PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.3.8
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.3.8
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 2.3.2 2.3.3 All 194 releases
convertkit / includes / blocks / class-convertkit-block-form-builder.php

class-convertkit-block-form-builder.php in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 3.3.8, at includes/blocks/class-convertkit-block-form-builder.php

833 lines 25.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Kit Form Builder Block class.
4 *
5 * @package ConvertKit
6 * @author ConvertKit
7 */
8
9 /**
10 * Kit Form Builder Block for Gutenberg.
11 *
12 * @package ConvertKit
13 * @author ConvertKit
14 */
15 class ConvertKit_Block_Form_Builder extends ConvertKit_Block {
16
17 /**
18 * Holds the subscriber that was created
19 * when the form was submitted.
20 *
21 * @since 3.0.0
22 *
23 * @var bool|int
24 */
25 public $subscriber_id = false;
26
27 /**
28 * Constructor
29 *
30 * @since 3.0.0
31 */
32 public function __construct() {
33
34 // Subscribe if the form was submitted.
35 add_action( 'init', array( $this, 'maybe_subscribe' ) );
36
37 // Register this as a Gutenberg block in the Kit Plugin.
38 add_filter( 'convertkit_blocks', array( $this, 'register' ) );
39
40 // Enqueue styles for this Gutenberg Block in the editor view.
41 add_action( 'convertkit_gutenberg_enqueue_styles', array( $this, 'enqueue_styles_editor' ) );
42
43 // Enqueue scripts and styles for this Gutenberg Block in the editor and frontend views.
44 add_action( 'convertkit_gutenberg_enqueue_styles_editor_and_frontend', array( $this, 'enqueue_styles' ) );
45
46 // Replace <a> with <button type="submit"> for the core/button element within the form builder.
47 add_filter( 'render_block_core/button', array( $this, 'render_form_button' ), 10, 2 );
48
49 }
50
51 /**
52 * Checks if the request is a Native Form subscribe request with an email address.
53 * If so, subscribes the email address to the Kit account.
54 *
55 * @since 3.0.0
56 */
57 public function maybe_subscribe() {
58
59 // Bail if no nonce was specified.
60 if ( ! array_key_exists( '_wpnonce', $_REQUEST ) ) {
61 return;
62 }
63
64 // Bail if the nonce failed validation.
65 if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'convertkit_block_form_builder' ) ) {
66 return;
67 }
68
69 // Bail if the expected email, resource ID or Post ID are missing.
70 if ( ! array_key_exists( 'convertkit', $_REQUEST ) ) {
71 return;
72 }
73 if ( ! array_key_exists( 'email', $_REQUEST['convertkit'] ) ) {
74 return;
75 }
76 if ( ! array_key_exists( 'post_id', $_REQUEST['convertkit'] ) ) {
77 return;
78 }
79
80 // Check spam protection.
81 $spam_protection = new ConvertKit_Spam_Protection();
82
83 // Bail if spam protection failed.
84 if ( is_wp_error( $spam_protection->verify( 'convertkit_form_builder' ) ) ) {
85 return;
86 }
87
88 // Sanitize form data.
89 $form_data = map_deep( wp_unslash( $_REQUEST['convertkit'] ), 'sanitize_text_field' );
90
91 // Build custom fields, if any were specified.
92 $custom_fields = array();
93 if ( array_key_exists( 'custom_fields', $form_data ) ) {
94 $custom_fields = $form_data['custom_fields'];
95 }
96
97 // Get Form, Tag and Sequence IDs, if any were specified.
98 $form_id = array_key_exists( 'form_id', $form_data ) ? $form_data['form_id'] : false;
99 $tag_id = array_key_exists( 'tag_id', $form_data ) ? $form_data['tag_id'] : false;
100 $sequence_id = array_key_exists( 'sequence_id', $form_data ) ? $form_data['sequence_id'] : false;
101
102 // Initialize classes that will be used.
103 $settings = new ConvertKit_Settings();
104 $entries = new ConvertKit_Form_Entries();
105
106 // If the Plugin Access Token has not been configured, we can't add a subscriber.
107 if ( ! $settings->has_access_and_refresh_token() ) {
108 // Store entry and return.
109 if ( $form_data['store_entries'] ) {
110 $entries->upsert(
111 array(
112 'post_id' => $form_data['post_id'],
113 'email' => $form_data['email'],
114 'first_name' => $form_data['first_name'],
115 'custom_fields' => $custom_fields,
116 'form_id' => $form_id,
117 'tag_id' => $tag_id,
118 'sequence_id' => $sequence_id,
119 'api_result' => 'error',
120 'api_error' => __( 'Plugin Access Token not configured', 'convertkit' ),
121 )
122 );
123 }
124 return;
125 }
126
127 // Initialize the API.
128 $api = new ConvertKit_API_V4(
129 CONVERTKIT_OAUTH_CLIENT_ID,
130 CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI,
131 $settings->get_access_token(),
132 $settings->get_refresh_token(),
133 $settings->debug_enabled(),
134 'block_form_builder'
135 );
136
137 // Determine the subscriber state.
138 // If a Form is specified, mark the subscriber as inactive, so the form's double optin is honored.
139 // If a Tag or Sequence is specified, mark the subscriber as active, as there's no double optin for tags or sequences.
140 $subscriber_state = $form_id !== false ? 'inactive' : 'active';
141
142 // Create subscriber.
143 $result = $api->create_subscriber(
144 sanitize_email( $form_data['email'] ),
145 array_key_exists( 'first_name', $form_data ) ? $form_data['first_name'] : '',
146 $subscriber_state,
147 $custom_fields
148 );
149
150 // Bail if an error occurred.
151 if ( is_wp_error( $result ) ) {
152 // Store entry and return.
153 if ( $form_data['store_entries'] ) {
154 $entries->upsert(
155 array(
156 'post_id' => $form_data['post_id'],
157 'email' => $form_data['email'],
158 'first_name' => $form_data['first_name'],
159 'custom_fields' => $custom_fields,
160 'form_id' => $form_id,
161 'tag_id' => $tag_id,
162 'sequence_id' => $sequence_id,
163 'api_result' => 'error',
164 'api_error' => $result->get_error_message(),
165 )
166 );
167 }
168 return;
169 }
170
171 // Store entry.
172 if ( $form_data['store_entries'] ) {
173 $entries->upsert(
174 array(
175 'post_id' => $form_data['post_id'],
176 'email' => $form_data['email'],
177 'first_name' => $form_data['first_name'],
178 'custom_fields' => $custom_fields,
179 'form_id' => $form_id,
180 'tag_id' => $tag_id,
181 'sequence_id' => $sequence_id,
182 'api_result' => 'success',
183 )
184 );
185 }
186
187 // Store the subscriber ID in a cookie.
188 $subscriber = new ConvertKit_Subscriber();
189 $subscriber->set( $result['subscriber']['id'] );
190
191 // If a form was specified, add the subscriber to the form.
192 if ( $form_id ) {
193 // For Legacy Forms, a different endpoint is used.
194 $forms = new ConvertKit_Resource_Forms();
195 if ( $forms->is_legacy( $form_id ) ) {
196 $result = $api->add_subscriber_to_legacy_form(
197 $form_id,
198 $result['subscriber']['id']
199 );
200 } else {
201 $result = $api->add_subscriber_to_form(
202 $form_id,
203 $result['subscriber']['id'],
204 get_permalink( absint( $form_data['post_id'] ) )
205 );
206 }
207
208 if ( $form_data['store_entries'] ) {
209 $entries->upsert(
210 array(
211 'post_id' => $form_data['post_id'],
212 'email' => $form_data['email'],
213 'first_name' => $form_data['first_name'],
214 'custom_fields' => $custom_fields,
215 'form_id' => $form_id,
216 'tag_id' => $tag_id,
217 'sequence_id' => $sequence_id,
218 'api_result' => is_wp_error( $result ) ? 'error' : 'success',
219 'api_error' => is_wp_error( $result ) ? $result->get_error_message() : '',
220 )
221 );
222 }
223 }
224
225 // If a tag was specified, add the subscriber to the tag.
226 if ( $tag_id ) {
227 $result = $api->tag_subscriber( $tag_id, $result['subscriber']['id'] );
228
229 if ( $form_data['store_entries'] ) {
230 $entries->upsert(
231 array(
232 'post_id' => $form_data['post_id'],
233 'email' => $form_data['email'],
234 'first_name' => $form_data['first_name'],
235 'custom_fields' => $custom_fields,
236 'form_id' => $form_id,
237 'tag_id' => $tag_id,
238 'sequence_id' => $sequence_id,
239 'api_result' => is_wp_error( $result ) ? 'error' : 'success',
240 'api_error' => is_wp_error( $result ) ? $result->get_error_message() : '',
241 )
242 );
243 }
244 }
245
246 // If a sequence was specified, add the subscriber to the sequence.
247 if ( $sequence_id ) {
248 $result = $api->add_subscriber_to_sequence( $sequence_id, $result['subscriber']['id'] );
249
250 if ( $form_data['store_entries'] ) {
251 $entries->upsert(
252 array(
253 'post_id' => $form_data['post_id'],
254 'email' => $form_data['email'],
255 'first_name' => $form_data['first_name'],
256 'custom_fields' => $custom_fields,
257 'form_id' => $form_id,
258 'tag_id' => $tag_id,
259 'sequence_id' => $sequence_id,
260 'api_result' => is_wp_error( $result ) ? 'error' : 'success',
261 'api_error' => is_wp_error( $result ) ? $result->get_error_message() : '',
262 )
263 );
264 }
265 }
266
267 // Get the redirect URL, based on whether the form is configured to redirect
268 // or not.
269 if ( array_key_exists( 'redirect', $form_data ) && wp_http_validate_url( sanitize_url( $form_data['redirect'] ) ) ) {
270 // Redirect to the URL specified in the form.
271 $redirect = sanitize_url( $form_data['redirect'] );
272 } else {
273 // Redirect to the Post the form was displayed on, to show a success message.
274 $redirect = get_permalink( absint( $form_data['post_id'] ) );
275 }
276
277 // Redirect.
278 wp_redirect( $redirect ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
279 exit();
280
281 }
282
283 /**
284 * Enqueues styles for this Gutenberg Block in the editor view.
285 *
286 * @since 3.0.0
287 */
288 public function enqueue_styles_editor() {
289
290 wp_enqueue_style( 'convertkit-gutenberg', CONVERTKIT_PLUGIN_URL . 'resources/backend/css/gutenberg.css', array( 'wp-edit-blocks' ), CONVERTKIT_PLUGIN_VERSION );
291
292 }
293
294 /**
295 * Enqueues styles for this Gutenberg Block in the editor and frontend views.
296 *
297 * @since 2.3.3
298 */
299 public function enqueue_styles() {
300
301 convertkit_enqueue_frontend_css();
302
303 }
304
305 /**
306 * Returns this block's programmatic name, excluding the convertkit- prefix.
307 *
308 * @since 3.0.0
309 *
310 * @return string
311 */
312 public function get_name() {
313
314 /**
315 * This will register as:
316 * - a Gutenberg block, with the name convertkit/form-builder.
317 */
318 return 'form-builder';
319
320 }
321
322 /**
323 * Returns this block's title.
324 *
325 * @since 3.1.1
326 */
327 public function get_title() {
328
329 return __( 'Kit Form Builder', 'convertkit' );
330
331 }
332
333 /**
334 * Returns this block's icon.
335 *
336 * @since 3.1.1
337 */
338 public function get_icon() {
339
340 return 'resources/backend/images/block-icon-form-builder.svg';
341
342 }
343
344 /**
345 * Returns this block's Title, Icon, Categories, Keywords and properties.
346 *
347 * @since 3.0.0
348 *
349 * @return array
350 */
351 public function get_overview() {
352
353 $convertkit_forms = new ConvertKit_Resource_Forms( 'block_edit' );
354 $settings = new ConvertKit_Settings();
355
356 return array(
357 'title' => $this->get_title(),
358 'description' => __( 'Build a subscription form with Kit.', 'convertkit' ),
359 'icon' => $this->get_icon(),
360 'category' => 'convertkit',
361 'keywords' => array(
362 __( 'ConvertKit', 'convertkit' ),
363 __( 'Kit', 'convertkit' ),
364 __( 'Form Builder', 'convertkit' ),
365 ),
366
367 // Function to call when rendering.
368 'render_callback' => array( $this, 'render' ),
369
370 // Gutenberg: Block Icon in Editor.
371 'gutenberg_icon' => convertkit_get_file_contents( CONVERTKIT_PLUGIN_PATH . '/resources/backend/images/block-icon-form-builder.svg' ),
372
373 // Gutenberg: Example image showing how this block looks when choosing it in Gutenberg.
374 'gutenberg_example_image' => CONVERTKIT_PLUGIN_URL . 'resources/backend/images/block-example-form-builder.png',
375
376 // Gutenberg: Inner blocks to use as a starting template when creating a new block.
377 'gutenberg_template' => array(
378 'convertkit/form-builder-field-name' => array(
379 'label' => 'First name',
380 ),
381 'convertkit/form-builder-field-email' => array(
382 'label' => 'Email address',
383 'lock' => array(
384 'move' => false,
385 'remove' => true,
386 ),
387 ),
388 'core/button' => array(
389 'label' => 'Submit button',
390 'text' => 'Subscribe',
391 'variant' => 'primary',
392 'className' => 'convertkit-form-builder-submit-button',
393 'lock' => array(
394 'move' => true,
395 'remove' => true,
396 ),
397 ),
398 ),
399
400 // Help descriptions, displayed when no Access Token / resources exist and this block/shortcode is added.
401 'no_access_token' => array(
402 'notice' => __( 'Not connected to Kit.', 'convertkit' ),
403 'link' => convertkit_get_setup_wizard_plugin_link(),
404 'link_text' => __( 'Click here to connect your Kit account.', 'convertkit' ),
405 'instruction_text' => __( 'Connect your Kit account at Settings > Kit, and then refresh this page to configure this block.', 'convertkit' ),
406 ),
407
408 'has_access_token' => $settings->has_access_and_refresh_token(),
409
410 // This block works without resources, so we don't need to check if resources exist.
411 'has_resources' => true,
412 );
413
414 }
415
416 /**
417 * Returns this block's Attributes
418 *
419 * @since 3.0.0
420 *
421 * @return array
422 */
423 public function get_attributes() {
424
425 return array(
426 // Block attributes.
427 'redirect' => array(
428 'type' => 'string',
429 'default' => $this->get_default_value( 'redirect' ),
430 ),
431 'store_entries' => array(
432 'type' => 'boolean',
433 'default' => $this->get_default_value( 'store_entries' ),
434 ),
435 'display_form_if_subscribed' => array(
436 'type' => 'boolean',
437 'default' => $this->get_default_value( 'display_form_if_subscribed' ),
438 ),
439 'text_if_subscribed' => array(
440 'type' => 'string',
441 'default' => $this->get_default_value( 'text_if_subscribed' ),
442 ),
443 'form_id' => array(
444 'type' => 'string',
445 'default' => $this->get_default_value( 'form_id' ),
446 ),
447 'tag_id' => array(
448 'type' => 'string',
449 'default' => $this->get_default_value( 'tag_id' ),
450 ),
451 'sequence_id' => array(
452 'type' => 'string',
453 'default' => $this->get_default_value( 'sequence_id' ),
454 ),
455
456 // get_supports() style, color and typography attributes.
457 'align' => array(
458 'type' => 'string',
459 ),
460 'style' => array(
461 'type' => 'object',
462 ),
463 'backgroundColor' => array(
464 'type' => 'string',
465 ),
466 'textColor' => array(
467 'type' => 'string',
468 ),
469 'fontSize' => array(
470 'type' => 'string',
471 ),
472
473 // Always required for Gutenberg.
474 'is_gutenberg_example' => array(
475 'type' => 'boolean',
476 'default' => false,
477 ),
478 );
479
480 }
481
482 /**
483 * Returns this block's supported built-in Attributes.
484 *
485 * @since 3.0.0
486 *
487 * @return array Supports
488 */
489 public function get_supports() {
490
491 return array(
492 'align' => true,
493 'className' => true,
494 'color' => array(
495 'link' => true,
496 'background' => true,
497 'text' => true,
498 ),
499 'typography' => array(
500 'fontSize' => true,
501 'lineHeight' => true,
502 ),
503 'spacing' => array(
504 'margin' => true,
505 'padding' => true,
506 ),
507 );
508
509 }
510
511 /**
512 * Returns this block's Fields
513 *
514 * @since 3.0.0
515 *
516 * @return bool|array
517 */
518 public function get_fields() {
519
520 // Get Kit Forms. Non-legacy forms populate the sidebar dropdown;
521 // legacy forms are exposed separately as a fallback so the sidebar can
522 // keep displaying a previously-saved legacy form as the current
523 // selection without offering other legacy forms as new choices.
524 $forms = new ConvertKit_Resource_Forms( 'block_form_builder' );
525 $forms_options = array();
526 $forms_legacy_options = array();
527 if ( $forms->exist() ) {
528 foreach ( $forms->get() as $form ) {
529 $label = sprintf(
530 '%s [%s]',
531 sanitize_text_field( $form['name'] ),
532 // Legacy forms don't include a `format` key, so define them as inline.
533 ( ! empty( $form['format'] ) ? sanitize_text_field( $form['format'] ) : 'inline' )
534 );
535
536 if ( ! empty( $form['format'] ) ) {
537 $forms_options[ $form['id'] ] = $label;
538 } else {
539 $forms_legacy_options[ $form['id'] ] = $label;
540 }
541 }
542 }
543
544 // Get Kit Tags.
545 $tags = new ConvertKit_Resource_Tags( 'block_form_builder' );
546 $tags_options = array();
547 if ( $tags->exist() ) {
548 foreach ( $tags->get() as $tag ) {
549 $tags_options[ $tag['id'] ] = sanitize_text_field( $tag['name'] );
550 }
551 }
552
553 // Get Kit Sequences.
554 $sequences = new ConvertKit_Resource_Sequences( 'block_form_builder' );
555 $sequences_options = array();
556 if ( $sequences->exist() ) {
557 foreach ( $sequences->get() as $sequence ) {
558 $sequences_options[ $sequence['id'] ] = sanitize_text_field( $sequence['name'] );
559 }
560 }
561
562 return array(
563 'redirect' => array(
564 'label' => __( 'Redirect', 'convertkit' ),
565 'type' => 'url',
566 'description' => __( 'The URL to redirect to after the visitor subscribes. If not specified, the visitor will remain on the current page.', 'convertkit' ),
567 ),
568 'store_entries' => array(
569 'label' => __( 'Store form submissions', 'convertkit' ),
570 'type' => 'toggle',
571 'description' => __( 'If enabled, stores copies of form submissions in the WordPress database. Submissions are always sent to Kit.', 'convertkit' ),
572 ),
573 'display_form_if_subscribed' => array(
574 'label' => __( 'Display form', 'convertkit' ),
575 'type' => 'toggle',
576 'description' => __( 'If enabled, displays the form if the visitor is already subscribed.', 'convertkit' ),
577 ),
578 'text_if_subscribed' => array(
579 'label' => __( 'Text', 'convertkit' ),
580 'type' => 'text',
581 'description' => __( 'The text to display if the visitor is already subscribed.', 'convertkit' ),
582 'display_if' => array(
583 'key' => 'display_form_if_subscribed',
584 'value' => 0,
585 ),
586 ),
587 'form_id' => array(
588 'label' => __( 'Form', 'convertkit' ),
589 'type' => 'select',
590 'description' => __( 'The Kit form to add the subscriber to. Useful if you want to send an incentive email.', 'convertkit' ),
591 'values' => $forms_options,
592 'legacy_values' => $forms_legacy_options,
593 ),
594 'tag_id' => array(
595 'label' => __( 'Tag', 'convertkit' ),
596 'type' => 'select',
597 'description' => __( 'The Kit tag to add the subscriber to.', 'convertkit' ),
598 'values' => $tags_options,
599 ),
600 'sequence_id' => array(
601 'label' => __( 'Sequence', 'convertkit' ),
602 'type' => 'select',
603 'description' => __( 'The Kit sequence to add the subscriber to.', 'convertkit' ),
604 'values' => $sequences_options,
605 ),
606 );
607
608 }
609
610 /**
611 * Returns this block's UI panels / sections.
612 *
613 * @since 3.0.0
614 *
615 * @return bool|array
616 */
617 public function get_panels() {
618
619 return array(
620 'general' => array(
621 'label' => __( 'General', 'convertkit' ),
622 'fields' => array(
623 'form_id',
624 'tag_id',
625 'sequence_id',
626 'redirect',
627 'store_entries',
628 'display_form_if_subscribed',
629 'text_if_subscribed',
630 ),
631 ),
632 );
633
634 }
635
636 /**
637 * Returns this block's Default Values
638 *
639 * @since 3.0.0
640 *
641 * @return array
642 */
643 public function get_default_values() {
644
645 return array(
646 'form_id' => '',
647 'tag_id' => '',
648 'sequence_id' => '',
649 'redirect' => '',
650 'store_entries' => true,
651 'display_form_if_subscribed' => true,
652 'text_if_subscribed' => __( 'Thanks for subscribing!', 'convertkit' ),
653
654 // Built-in Gutenberg block attributes.
655 'align' => 'center',
656 'style' => '',
657 'backgroundColor' => '',
658 'textColor' => '',
659 );
660
661 }
662
663 /**
664 * Returns the block's output, based on the supplied configuration attributes.
665 *
666 * @since 3.0.0
667 *
668 * @param array $atts Block Attributes.
669 * @param string $content Inner blocks content.
670 * @return string
671 */
672 public function render( $atts, $content ) {
673
674 global $post;
675
676 // Get Post ID.
677 $post_id = is_a( $post, 'WP_Post' ) ? $post->ID : 0;
678
679 // Parse attributes, defining fallback defaults if required
680 // and moving some attributes (such as Gutenberg's styles), if defined.
681 $atts = $this->sanitize_and_declare_atts( $atts );
682
683 // Check if subscriber is already subscribed, and whether the form should be displayed.
684 $subscriber = new ConvertKit_Subscriber();
685 $this->subscriber_id = $subscriber->get_subscriber_id();
686 $display_form = $this->subscriber_id && ! $atts['display_form_if_subscribed'] ? false : true;
687
688 // If the form should not be displayed, return the subscribed text.
689 if ( ! $display_form ) {
690 $html = '<div class="' . implode( ' ', map_deep( $this->get_css_classes(), 'sanitize_html_class' ) ) . '" style="' . implode( ';', map_deep( $this->get_css_styles( $atts ), 'esc_attr' ) ) . '">';
691 $html .= esc_html( $atts['text_if_subscribed'] );
692 $html .= '</div>';
693 return $html;
694 }
695
696 // Add the <form> element and hidden fields immediate inside the block's container.
697 $html = $this->add_form_to_block_content( $content, $atts, $post_id );
698
699 /**
700 * Filter the block's content immediately before it is output.
701 *
702 * @since 3.0.0
703 *
704 * @param string $html ConvertKit Native Form HTML.
705 * @param array $atts Block Attributes.
706 */
707 $html = apply_filters( 'convertkit_block_form_builder_render', $html, $atts );
708
709 return $html;
710
711 }
712
713 /**
714 * Replace <a> with <button type="submit"> for the core/button element within the form builder
715 * that has the class convertkit-form-builder-submit-button, as the block editor doesn't
716 * have a core <button> element, and registering our own just for this block would be overkill.
717 *
718 * @since 3.0.0
719 *
720 * @param string $block_content Block content.
721 * @param array $block Block attributes.
722 * @return string
723 */
724 public function render_form_button( $block_content, $block ) {
725
726 if ( ! isset( $block['attrs']['className'] ) ) {
727 return $block_content;
728 }
729
730 if ( strpos( $block['attrs']['className'], 'convertkit-form-builder-submit-button' ) === false ) {
731 return $block_content;
732 }
733
734 // Change link to button.
735 $block_content = preg_replace(
736 '/<a([^>]*)>(.*?)<\/a>/',
737 '<button type="submit"$1>$2</button>',
738 $block_content
739 );
740
741 // Return the button if no spam protection provider is active.
742 $spam_protection = new ConvertKit_Spam_Protection();
743 $provider = $spam_protection->get_active_provider();
744 if ( ! $provider ) {
745 return $block_content;
746 }
747
748 // Enqueue the spam protection provider's JS.
749 $provider->enqueue_scripts();
750
751 // Parse the button's DOM.
752 $parser = new ConvertKit_HTML_Parser( $block_content );
753 $button = $parser->xpath->query( '//button' )->item( 0 );
754
755 // Attach the spam protection provider's attributes/elements to the form/button as necessary.
756 // $button is narrowed from DOMNode to DOMElement by the //button xpath expression above.
757 $provider->attach_to_form_button_dom( $parser, $button, 'convertkit_form_builder' ); // @phpstan-ignore-line
758
759 // Return button HTML.
760 return $parser->get_body_html();
761
762 }
763
764 /**
765 * Wraps the block's content within a <form> element, and adds hidden fields.
766 *
767 * @since 3.0.0
768 *
769 * @param string $content Block content.
770 * @param array $atts Block attributes.
771 * @param int $post_id Post ID.
772 * @return string
773 */
774 private function add_form_to_block_content( $content, $atts, $post_id ) {
775
776 // Load the content into the parser.
777 $parser = new ConvertKit_HTML_Parser( $content );
778
779 // Get block container.
780 $block_container = $parser->xpath->query( '//div[contains(@class, "wp-block-convertkit-form-builder")]' )->item( 0 );
781
782 // If no block container was found, return the original content.
783 // This shouldn't happen, as the block editor supplies the container, but it's a safeguard.
784 if ( ! $block_container ) {
785 return $content;
786 }
787
788 // Create form element.
789 $form = $parser->html->createElement( 'form' );
790 $form->setAttribute( 'action', esc_url( get_permalink( $post_id ) ) );
791 $form->setAttribute( 'method', 'post' );
792
793 // Move form builder div contents into form.
794 while ( $block_container->hasChildNodes() ) {
795 $form->appendChild( $block_container->firstChild ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
796 }
797
798 // Add subscribed message if required.
799 if ( $this->subscriber_id ) {
800 $subscribed_message = $parser->html->createElement( 'div' );
801 $subscribed_message->setAttribute( 'class', 'convertkit-form-builder-subscribed-message' );
802 $subscribed_message->appendChild( $parser->html->createTextNode( $atts['text_if_subscribed'] ) );
803 $form->insertBefore( $subscribed_message, $form->firstChild ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
804 }
805
806 // Add hidden fields.
807 $fields = array(
808 'convertkit[post_id]' => absint( $post_id ),
809 'convertkit[store_entries]' => $atts['store_entries'] ? '1' : '0',
810 'convertkit[redirect]' => esc_url( $atts['redirect'] ),
811 'convertkit[form_id]' => absint( $atts['form_id'] ),
812 'convertkit[tag_id]' => absint( $atts['tag_id'] ),
813 'convertkit[sequence_id]' => absint( $atts['sequence_id'] ),
814 '_wpnonce' => wp_create_nonce( 'convertkit_block_form_builder' ),
815 );
816 foreach ( $fields as $name => $value ) {
817 $hidden = $parser->html->createElement( 'input' );
818 $hidden->setAttribute( 'type', 'hidden' );
819 $hidden->setAttribute( 'name', $name );
820 $hidden->setAttribute( 'value', $value );
821 $form->appendChild( $hidden );
822 }
823
824 // Replace div contents with form.
825 $block_container->appendChild( $form );
826
827 // Return modified content.
828 return $parser->get_body_html();
829
830 }
831
832 }
833