PluginProbe
Happyforms – Form Builder for WordPress: Drag & Drop Contact Forms, Surveys, Payments & Multipurpose Forms / delete
Happyforms – Form Builder for WordPress: Drag & Drop Contact Forms, Surveys, Payments & Multipurpose Forms vdelete
1.26.15 trunk 1.0 1.10.0 1.11.0 1.11.1 1.12.0 1.12.1 1.12.10 1.12.11 1.12.12 1.12.2 1.12.3 1.12.4 1.12.5 1.12.6 1.12.7 1.12.8 1.12.9 1.13.0 1.13.1 1.13.10 1.13.11 1.13.12 1.13.2 All 194 releases
happyforms / core / classes / class-form-controller.php

class-form-controller.php in Happyforms – Form Builder for WordPress: Drag & Drop Contact Forms, Surveys, Payments & Multipurpose Forms delete, at core/classes/class-form-controller.php

907 lines 21.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class HappyForms_Form_Controller {
4
5 /**
6 * The singleton instance.
7 *
8 * @since 1.0
9 *
10 * @var HappyForms_Form_Controller
11 */
12 private static $instance;
13
14 /**
15 * The form post type slug.
16 *
17 * @since 1.0
18 *
19 * @var string
20 */
21 public $post_type = 'happyform';
22
23 /**
24 * The singleton constructor.
25 *
26 * @since 1.0
27 *
28 * @return HappyForms_Form_Controller
29 */
30 public static function instance() {
31 if ( is_null( self::$instance ) ) {
32 self::$instance = new self();
33 }
34
35 self::$instance->hook();
36
37 return self::$instance;
38 }
39
40 /**
41 * Register hooks.
42 *
43 * @since 1.0
44 *
45 * @return void
46 */
47 public function hook() {
48 add_action( 'init', array( $this, 'register_post_type' ) );
49 add_action( 'wp', array( $this, 'inject_new_form' ) );
50 add_filter( 'template_include', array( $this, 'single_template' ), 9999 );
51 add_action( 'delete_post', array( $this, 'delete_post' ) );
52
53 add_filter( 'happyforms_part_class', array( $this, 'part_error_class' ), 10, 3 );
54
55 add_filter( 'happyforms_get_form_attributes', array( $this, 'set_form_autocomplete_off' ), 10, 1);
56 add_filter( 'happyforms_part_attributes', array( $this, 'set_parts_autocomplete_off' ), 10, 1);
57 add_filter( 'happyforms_part_attributes', array( $this, 'set_parts_spellcheck_off' ), 10, 1);
58 }
59
60 /**
61 * Action: register the form custom post type.
62 *
63 * @hooked action init
64 *
65 * @since 1.0
66 *
67 * @return void
68 */
69 public function register_post_type() {
70 $labels = array(
71 'name' => __( 'Forms', 'happyforms' ),
72 'singular_name' => __( 'Form', 'happyforms' ),
73 'add_new' => __( 'Add New', 'happyforms' ),
74 'add_new_item' => __( 'Build form', 'happyforms' ),
75 'edit_item' => __( 'Edit form', 'happyforms' ),
76 'new_item' => __( 'Build form', 'happyforms' ),
77 'view_item' => __( 'View form', 'happyforms' ),
78 'view_items' => __( 'View forms', 'happyforms' ),
79 'search_items' => __( 'Search Forms', 'happyforms' ),
80 'not_found' => __( 'No forms found.', 'happyforms' ),
81 'not_found_in_trash' => __( 'No forms found in Trash.', 'happyforms' ),
82 'all_items' => __( 'All Forms', 'happyforms' ),
83 'menu_name' => __( 'All Forms', 'happyforms' ),
84 );
85
86 $args = array(
87 'labels' => $labels,
88 'public' => false,
89 'publicly_queryable' => is_customize_preview(),
90 'exclude_from_search' => true,
91 'show_ui' => true,
92 'show_in_menu' => false,
93 'query_var' => true,
94 'rewrite' => false,
95 'capability_type' => 'page',
96 'has_archive' => false,
97 'hierarchical' => false,
98 'can_export' => false,
99 'supports' => array( 'author', 'custom-fields' ),
100 );
101
102 $args = apply_filters( 'happyforms_happyform_post_type_args', $args );
103
104 register_post_type( $this->post_type, $args );
105
106 $tracking_status = happyforms_get_tracking()->get_status();
107
108 if ( 1 === intval( $tracking_status['status'] ) ) {
109 flush_rewrite_rules();
110 }
111 }
112
113 /**
114 * Action: inject a virtual HappyForms post object
115 * if we're previewing a new form.
116 *
117 * @since 1.3
118 *
119 * @hooked action template_redirect
120 *
121 * @return void
122 */
123 public function inject_new_form() {
124 global $wp_query;
125
126 if ( ! is_customize_preview() ) {
127 return;
128 }
129
130 if ( ! isset( $wp_query->query['p'] ) ||
131 ! isset( $wp_query->query['post_type'] ) ) {
132 return;
133 }
134
135 $queried_post_type = $wp_query->query['post_type'];
136 $queried_post_id = intval( $wp_query->query['p'] );
137
138 if ( $this->post_type !== $queried_post_type || 0 !== $queried_post_id ) {
139 return;
140 }
141
142 // See https://barn2.co.uk/create-fake-wordpress-post-fly/
143 $post = $this->create_virtual();
144 $this->inject_virtual_post( $post );
145 }
146
147 /**
148 * Filter: filter the template path used for
149 * the Customize screen preview and frontend rendering.
150 *
151 * @since 1.0
152 *
153 * @hooked filter single_template
154 *
155 * @param $single_template The original template path.
156 *
157 * @return string
158 */
159 public function single_template( $single_template ) {
160 global $post;
161
162 if ( ! $post ) {
163 return $single_template;
164 }
165
166 if ( $post->post_type == happyforms_get_form_controller()->post_type ) {
167 if ( is_customize_preview() ) {
168 $single_template = happyforms_get_core_folder() . '/templates/preview-form-edit.php';
169 } else {
170 $single_template = happyforms_get_core_folder() . '/templates/single-form.php';
171 }
172 }
173
174 return $single_template;
175 }
176
177 public function get_post_fields() {
178 $fields = array(
179 'ID' => array(
180 'default' => '0',
181 'sanitize' => 'intval',
182 ),
183 'post_title' => array(
184 'default' => '',
185 'sanitize' => 'sanitize_text_field',
186 ),
187 'post_status' => array(
188 'default' => 'publish',
189 'sanitize' => 'happyforms_sanitize_post_status',
190 ),
191 'post_type' => array(
192 'default' => $this->post_type,
193 'sanitize' => 'sanitize_text_field',
194 )
195 );
196
197 return $fields;
198 }
199
200 public function get_meta_fields() {
201 global $current_user;
202
203 $fields = array(
204 'layout' => array(
205 'default' => array(),
206 ),
207 'parts' => array(
208 'default' => array(),
209 ),
210 );
211
212 /**
213 * Filter fields stored as form post meta.
214 *
215 * @since 1.3
216 *
217 * @param array $fields Registered post meta fields.
218 *
219 * @return array
220 */
221 $fields = apply_filters( 'happyforms_meta_fields', $fields );
222
223 return $fields;
224 }
225
226 /**
227 * Get the defaults and sanitization configuration
228 * for the fields of the form post object.
229 *
230 * @since 1.0
231 *
232 * @param string $group An optional subset of fields
233 * to retrieve configuration for.
234 *
235 * @return array
236 */
237 public function get_fields( $group = '' ) {
238 $fields = array();
239
240 switch ( $group ) {
241 case 'post':
242 $fields = $this->get_post_fields();
243 break;
244 case 'meta':
245 $fields = $this->get_meta_fields();
246 break;
247 default:
248 $fields = array_merge(
249 $this->get_post_fields(),
250 $this->get_meta_fields()
251 );
252 break;
253 }
254
255 return $fields;
256 }
257
258 public function get_field( $field ) {
259 $fields = $this->get_fields();
260
261 if ( isset( $fields[$field] ) ) {
262 return $fields[$field];
263 }
264
265 return null;
266 }
267
268 public function get_defaults( $group = '' ) {
269 $defaults = wp_list_pluck( $this->get_fields( $group ), 'default' );
270
271 return $defaults;
272 }
273
274 public function get_default( $field ) {
275 $defaults = $this->get_defaults();
276
277 if ( isset( $defaults[$field] ) ) {
278 return $defaults[$field];
279 }
280
281 return null;
282 }
283
284 public function validate_field( &$value, $key ) {
285 $field = $this->get_field( $key );
286
287 if ( isset( $field['sanitize'] ) && is_callable( $field['sanitize'] ) ) {
288 $callback = $field['sanitize'];
289 $value = call_user_func( $callback, $value );
290 };
291 }
292
293 /**
294 * Validate the form data submitted from the Customize screen.
295 *
296 * @since 1.0
297 *
298 * @param array $post_data The raw input form data.
299 *
300 * @return array
301 */
302 public function validate_fields( $post_data = array() ) {
303 $defaults = $this->get_defaults();
304 $filtered = array_intersect_key( $post_data, $defaults );
305 $validated = wp_parse_args( $post_data, $filtered );
306 array_walk( $validated, array( $this, 'validate_field' ) );
307
308 return $validated;
309 }
310
311 /**
312 * Creates a virtual form post object.
313 *
314 * @since 1.3
315 *
316 * @return WP_Post
317 */
318 private function create_virtual() {
319 $post_id = 0;
320 $defaults = $this->get_defaults();
321
322 $post = new stdClass();
323 $post->ID = $post_id;
324 $post->post_author = 1;
325 $post->post_date = current_time( 'mysql' );
326 $post->post_date_gmt = current_time( 'mysql', 1 );
327 $post->post_title = $this->get_default( 'post_title' );
328 $post->post_content = '';
329 $post->post_status = 'publish';
330 $post->comment_status = 'closed';
331 $post->ping_status = 'closed';
332 $post->post_name = '';
333 $post->post_type = $this->post_type;
334 $post->filter = 'raw';
335
336 $wp_post = new WP_Post( $post );
337 wp_cache_add( $post_id, $wp_post, 'posts' );
338
339 return $wp_post;
340 }
341
342 /**
343 * Injects a virtual post object
344 * in the current query.
345 *
346 * @since 1.3
347 *
348 * @return WP_Post
349 */
350 private function inject_virtual_post( $post ) {
351 global $wp, $wp_query;
352
353 $wp_query->post = $post;
354 $wp_query->posts = array( $post );
355 $wp_query->queried_object = $post;
356 $wp_query->queried_object_id = 0;
357 $wp_query->found_posts = 1;
358 $wp_query->post_count = 1;
359 $wp_query->max_num_pages = 1;
360 $wp_query->is_page = false;
361 $wp_query->is_singular = true;
362 $wp_query->is_single = true;
363 $wp_query->is_attachment = false;
364 $wp_query->is_archive = false;
365 $wp_query->is_category = false;
366 $wp_query->is_tag = false;
367 $wp_query->is_tax = false;
368 $wp_query->is_author = false;
369 $wp_query->is_date = false;
370 $wp_query->is_year = false;
371 $wp_query->is_month = false;
372 $wp_query->is_day = false;
373 $wp_query->is_time = false;
374 $wp_query->is_search = false;
375 $wp_query->is_feed = false;
376 $wp_query->is_comment_feed = false;
377 $wp_query->is_trackback = false;
378 $wp_query->is_home = false;
379 $wp_query->is_embed = false;
380 $wp_query->is_404 = false;
381 $wp_query->is_paged = false;
382 $wp_query->is_admin = false;
383 $wp_query->is_preview = false;
384 $wp_query->is_robots = false;
385 $wp_query->is_posts_page = false;
386 $wp_query->is_post_type_archive = false;
387
388 $GLOBALS['wp_query'] = $wp_query;
389 $wp->register_globals();
390 }
391
392 /**
393 * Create a new form post object.
394 *
395 * @since 1.0
396 *
397 * @return int|string
398 */
399 public function create() {
400 $defaults = $this->get_defaults( 'post' );
401 $meta = $this->get_defaults( 'meta' );
402 $meta = happyforms_prefix_meta( $meta );
403 $data = array_merge( $defaults, $meta );
404 $data = apply_filters( 'happyforms_create_form_data', $data );
405 $defaults = array_intersect_key( $data, $defaults );
406 $meta = array_intersect_key( $data, $meta );
407 unset( $meta['_happyforms_parts'] );
408
409 $post_data = array_merge( $defaults, array(
410 'meta_input' => $meta
411 ) );
412
413 $result = wp_insert_post( wp_slash( $post_data ), true );
414
415 if ( is_wp_error( $result ) ) {
416 return $result;
417 }
418
419 $result = get_post( $result );
420
421 return $result;
422 }
423
424 /**
425 * Get a list of forms.
426 *
427 * @since 1.0
428 *
429 * @param array $post_ids A list of form IDs to fetch.
430 * @param boolean $only_id Whether or not to limit the
431 * results to the ID field.
432 *
433 * @return array
434 */
435 public function do_get( $post_ids = array(), $only_id = false ) {
436 $query_params = array(
437 'post_type' => happyforms_get_form_controller()->post_type,
438 'post_status' => array( 'publish', 'archive', 'trash' ),
439 'posts_per_page' => -1,
440 'orderby' => 'modified',
441 'order' => 'desc',
442 );
443
444 $query_params['post__in'] = is_array( $post_ids ) ? $post_ids : array( $post_ids );
445
446 if ( true === $only_id ) {
447 $query_params['fields'] = 'ids';
448 }
449
450 if ( 0 !== $post_ids ) {
451 $forms = get_posts( $query_params );
452 } else {
453 $forms = array( $this->create_virtual() );
454 }
455
456 if ( true === $only_id ) {
457 return $forms;
458 }
459
460 $form_entries = array();
461
462 foreach ( $forms as $form ) {
463 $form_entries[] = $this->to_array( $form );
464 }
465
466 if ( ! is_array( $post_ids ) ) {
467 if ( count( $form_entries ) > 0 ) {
468 return $form_entries[0];
469 } else {
470 return false;
471 }
472 }
473
474 return $form_entries;
475 }
476
477 public function get( $post_ids = array(), $only_id = false ) {
478 $args = md5( serialize( func_get_args() ) );
479 $key = "_happyforms_cache_forms_get_{$args}";
480 $found = false;
481 $result = happyforms_cache_get( $key, $found );
482
483 if ( false === $found ) {
484 $result = $this->do_get( $post_ids, $only_id );
485 happyforms_cache_set( $key, $result );
486 }
487
488 return $result;
489 }
490
491 /**
492 * Turn a form post object into an array.
493 *
494 * @param WP_Post $form The form post object.
495 *
496 * @return array
497 */
498 public function to_array( $form ) {
499 $form_array = $form->to_array();
500
501 $defaults = $this->get_defaults( 'meta' );
502 $meta = happyforms_unprefix_meta( get_post_meta( $form->ID ) );
503 $form_array = array_merge( $form_array, wp_parse_args( $meta, $defaults ) );
504 $form_array['layout'] = isset( $form_array['layout'] ) ? $form_array['layout'] : array();
505 $form_array['parts'] = array();
506
507 foreach ( $form_array['layout'] as $p => $part_id ) {
508 $part = $form_array[$part_id];
509 $part_class = happyforms_get_part_library()->get_part( $part['type'] );
510 if ( $part_class ) {
511 $form_array['parts'][] = wp_parse_args( $part, $part_class->get_customize_defaults() );
512 }
513 unset( $form_array[$part_id] );
514 }
515
516 $form_array = apply_filters( 'happyforms_get_form_data', $form_array );
517
518 return $form_array;
519 }
520
521 /**
522 * Update a form post object.
523 *
524 * @since 1.0
525 *
526 * @param array $form_data The raw input form data.
527 *
528 * @return array
529 */
530 public function update( $form_data = array() ) {
531
532 $validated_data = $this->validate_fields( $form_data );
533
534 if ( isset( $validated_data['ID'] ) && 0 === $validated_data['ID'] ) {
535 $form = $this->create();
536 $validated_data['ID'] = $form->ID;
537 }
538
539 $post_data = array_intersect_key( $validated_data, $this->get_defaults( 'post' ) );
540 $meta_data = array_intersect_key( $validated_data, $this->get_defaults( 'meta' ) );
541
542 $meta_data = apply_filters( 'happyforms_validate_meta_data', $meta_data );
543
544 $meta_data = happyforms_prefix_meta( $meta_data );
545
546 // Flatten data to make it filterable
547 $update_data = array_merge( $post_data, $meta_data );
548 $update_data = apply_filters( 'happyforms_update_form_data', $update_data );
549
550 // Rebuild update array format
551 $post_data = array_intersect_key( $update_data, $post_data);
552 $meta_data = array_intersect_key( $update_data, $meta_data);
553 unset( $meta_data['_happyforms_parts'] );
554
555 $update_data = array_merge( $post_data, array(
556 'meta_input' => $meta_data
557 ) );
558
559 // Double slash to preserve useful slashes in values
560 $update_data = wp_slash( $update_data );
561
562 $form_id = wp_update_post( $update_data, true );
563
564 if ( is_wp_error( $form_id ) ) {
565 return $form_id;
566 }
567
568 // Update parts
569 if ( isset( $form_data['parts'] ) ) {
570 $part_layout = array();
571 $parts_data = $form_data['parts'];
572 $library = happyforms_get_part_library();
573
574 foreach ( $parts_data as $part_data ) {
575 $validated_part = $library->validate_part( $part_data );
576 $validated_part = apply_filters( 'happyforms_validate_part', $validated_part );
577
578 if ( ! is_wp_error( $validated_part ) ) {
579 $part_id = $part_data['id'];
580 $part_layout[] = $part_id;
581 happyforms_update_meta( $form_id, $part_id, $validated_part );
582 }
583 }
584
585 happyforms_update_meta( $form_id, 'layout', $part_layout );
586 }
587
588 // Cleanup stale parts
589 $part_layout = happyforms_get_meta( $form_id, 'layout', true );
590 $form_meta = happyforms_unprefix_meta( get_post_meta( $form_id ) );
591 $form_meta = array_diff_key( $form_meta, $this->get_meta_fields() );
592 $stale_parts = array_diff_key( $form_meta, array_flip( $part_layout ) );
593 $part_library = happyforms_get_part_library();
594 $stale_parts = array_filter( $stale_parts, function( $stale_part ) use( $part_library ) {
595 if ( ! isset( $stale_part['type'] ) ) {
596 return false;
597 }
598
599 $part = $part_library->get_part( $stale_part['type'] );
600
601 return $part !== false;
602 } );
603
604 $stale_parts = happyforms_prefix_meta( $stale_parts );
605 $stale_parts = array_keys( $stale_parts );
606
607 foreach ( $stale_parts as $part_meta ) {
608 delete_post_meta( $form_id, $part_meta );
609 }
610
611 if ( ! empty( $stale_parts ) ) {
612 do_action( 'happyforms_stale_fields_deleted', $stale_parts, $form_id );
613 }
614
615 // Cleanup stale parts array meta
616 delete_post_meta( $form_id, '_happyforms_parts' );
617
618 $form = $this->to_array( get_post( $form_id ) );
619
620 do_action( 'happyforms_form_updated', $form );
621
622 return $form;
623 }
624
625 /**
626 * Duplicate a form post object.
627 *
628 * @since 1.0
629 *
630 * @param array $form The form data to be duplicated.
631 *
632 * @return bool|int The ID of the duplicated form object.
633 */
634 public function duplicate( $form ) {
635 $duplicate = array_intersect_key( $form->to_array(), array_flip( array(
636 'post_type', 'post_status',
637 ) ) );
638
639 $duplicate['post_title'] = trim( $form->post_title . __( ' Copy', 'happyforms' ) );
640 $duplicate_id = wp_insert_post( $duplicate );
641
642 if ( ! is_wp_error( $duplicate_id ) ) {
643 $form_meta = get_post_meta( $form->ID );
644
645 $form_meta = array_map( function( $value ) {
646 return $value[0];
647 }, $form_meta );
648
649 $form_meta = array_map( 'maybe_unserialize', $form_meta );
650
651 foreach ( $form_meta as $key => $value ) {
652 add_post_meta( $duplicate_id, $key, $value );
653 }
654
655 $result = $this->to_array( get_post( $duplicate_id ) );
656
657 do_action( 'happyforms_form_duplicated', $result );
658 }
659
660 return $duplicate_id;
661 }
662
663 /**
664 * Delete a form post object.
665 *
666 * @since 1.0
667 *
668 * @param int|string $form_id The ID of the form object.
669 *
670 * @return boolean|WP_Post
671 */
672 public function delete( $form_id ) {
673 $result = wp_delete_post( $form_id, true );
674
675 return $result;
676 }
677
678 /**
679 * Action: remove form messages when a form is removed.
680 *
681 * @since 1.0
682 *
683 * @hooked action delete_post
684 *
685 * @param int|string $post_id The ID of the form object.
686 *
687 * @return void
688 */
689 public function delete_post( $post_id ) {
690 $post = get_post( $post_id );
691
692 if ( $this->post_type !== $post->post_type ) {
693 return;
694 }
695
696 do_action( 'happyforms_form_deleted', $post_id );
697 }
698
699 public function get_latest() {
700 $forms = get_posts( "post_type={$this->post_type}&numberposts=1" );
701 $form_id = $forms[0]->ID;
702 $form = $this->get( $form_id );
703
704 return $form;
705 }
706
707 public function get_parts_by_type( $form, $type = '' ) {
708 $parts = array_filter( $form['parts'], function( $part ) use( $type ) {
709 return $part['type'] === $type;
710 } );
711
712 $parts = array_values( $parts );
713
714 return $parts;
715 }
716
717 public function get_parts_by_types( $form, $types = array() ) {
718 $parts = array_filter( $form['parts'], function( $part ) use( $types ) {
719 return in_array( $part['type'], $types );
720 } );
721
722 $parts = array_values( $parts );
723
724 return $parts;
725 }
726
727 /**
728 * Return the first part with the given type found in a form.
729 *
730 * @since 1.0
731 *
732 * @param array $form_data The data of the form the part belongs to.
733 * @param string $type The type of the part.
734 *
735 * @return boolean|array
736 */
737 public function get_first_part_by_type( $form_data, $type = '' ) {
738 $part = false;
739
740 foreach( $form_data['parts'] as $_part ) {
741 if ( $type === $_part['type'] ) {
742 $part = $_part;
743 break;
744 }
745 }
746
747 $part = apply_filters( 'happyforms_get_first_part_by_type_' . $type, $part, $form_data );
748
749 return $part;
750 }
751
752 public function get_part_by_id( $form_data, $part_id ) {
753 $part_ids = wp_list_pluck( $form_data['parts'], 'id' );
754 $parts = array_combine( $part_ids, $form_data['parts'] );
755
756 if ( isset( $parts[$part_id] ) ) {
757 return $parts[$part_id];
758 }
759
760 return false;
761 }
762
763 /**
764 * Get whether or not the given form data has spam prevention on.
765 *
766 * @since 1.0
767 *
768 * @param array $form_data The form data.
769 *
770 * @return int
771 */
772 public function has_honeypot_protection( $form_data ) {
773 $has_honeypot_protection = apply_filters( 'happyforms_use_honeypot', true, $form_data );
774
775 return $has_honeypot_protection;
776 }
777
778 public function has_hash_protection( $form_data ) {
779 $has_hash_protection = apply_filters( 'happyforms_use_hash_protection', true, $form_data );
780
781 return $has_hash_protection;
782 }
783
784 public function has_browser_protection( $form_data ) {
785 $has_browser_protection = apply_filters( 'happyforms_use_browser_protection', true, $form_data );
786
787 return $has_browser_protection;
788 }
789
790 /**
791 * Get form-wide submission notice definitions.
792 *
793 * @since 1.0
794 *
795 * @param array $form_data The form data.
796 *
797 * @return array
798 */
799 public function get_message_definitions( $form_data ) {
800 return array(
801 'form_error' => array(
802 'type' => 'error-submission',
803 'message' => html_entity_decode( $form_data['error_message'] ),
804 ),
805 'form_success' => array(
806 'type' => 'success',
807 'message' => html_entity_decode( $form_data['confirmation_message'] ),
808 ),
809 );
810 }
811
812 /**
813 * Get the HTML string of a rendered form.
814 *
815 * @since 1.0
816 *
817 * @param array $form The form data.
818 *
819 * @return string
820 */
821 public function render( $form = array(), $asset_mode = HappyForms_Form_Assets::MODE_NONE ) {
822 $html = '';
823
824 if ( empty( $form ) ) {
825 return $html;
826 }
827
828 if ( 'publish' !== $form['post_status'] && ! happyforms_is_preview() ) {
829 return $html;
830 }
831
832 // Prevent rendering in preview screen from 3rd party plugins.
833 if ( happyforms_is_preview() && doing_action() ) {
834 return $html;
835 }
836
837 ob_start();
838
839 happyforms_get_form_assets()->output( $form, $asset_mode );
840
841 $template_path = happyforms_get_core_folder() . '/templates/single-form.php';
842 $template_path = apply_filters( 'happyforms_form_template_path', $template_path, $form );
843 require( $template_path );
844 $html = ob_get_clean();
845
846 return $html;
847 }
848
849
850 public function get_default_steps( $form ) {
851 $steps = array(
852 1000 => 'submit',
853 );
854
855 return $steps;
856 }
857
858 public function part_error_class( $classes, $part, $form ) {
859 $part_name = happyforms_get_part_name( $part, $form );
860 $notices = happyforms_get_session()->get_messages( $part_name );
861
862 if( ! empty( $notices ) ) {
863 $classes[] = 'happyforms-part__validation-failed';
864 }
865
866 return $classes;
867 }
868
869 public function set_form_autocomplete_off( $attrs ) {
870 $attrs['autocomplete'] = 'off';
871
872 return $attrs;
873 }
874
875 public function set_parts_autocomplete_off( $attrs ) {
876 $attrs[] = 'autocomplete="off"';
877
878 return $attrs;
879 }
880
881 public function set_parts_spellcheck_off( $attrs ) {
882 $attrs[] = 'spellcheck="false"';
883
884 return $attrs;
885 }
886
887 }
888
889 if ( ! function_exists( 'happyforms_get_form_controller' ) ):
890 /**
891 * Get the HappyForms_Form_Controller class instance.
892 *
893 * @since 1.0
894 *
895 * @return HappyForms_Form_Controller
896 */
897 function happyforms_get_form_controller() {
898 return HappyForms_Form_Controller::instance();
899 }
900
901 endif;
902
903 /**
904 * Initialize the HappyForms_Form_Controller class immediately.
905 */
906 happyforms_get_form_controller();
907