PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.16.1.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.16.1.1
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / models / FrmForm.php

FrmForm.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.16.1.1, at classes/models/FrmForm.php

1,264 lines 35.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmForm {
7
8 /**
9 * @param array $values
10 * @return bool|int id on success or false on failure.
11 */
12 public static function create( $values ) {
13 global $wpdb;
14
15 $values = FrmAppHelper::maybe_filter_array( $values, array( 'name', 'description' ) );
16
17 $new_values = array(
18 'form_key' => FrmAppHelper::get_unique_key( $values['form_key'], $wpdb->prefix . 'frm_forms', 'form_key' ),
19 'name' => $values['name'],
20 'description' => $values['description'],
21 'status' => isset( $values['status'] ) ? $values['status'] : 'published',
22 'logged_in' => isset( $values['logged_in'] ) ? $values['logged_in'] : 0,
23 'is_template' => isset( $values['is_template'] ) ? (int) $values['is_template'] : 0,
24 'parent_form_id' => isset( $values['parent_form_id'] ) ? absint( $values['parent_form_id'] ) : 0,
25 'editable' => isset( $values['editable'] ) ? (int) $values['editable'] : 0,
26 'created_at' => isset( $values['created_at'] ) ? $values['created_at'] : current_time( 'mysql', 1 ),
27 );
28
29 $options = isset( $values['options'] ) ? (array) $values['options'] : array();
30 FrmFormsHelper::fill_form_options( $options, $values );
31
32 $options['before_html'] = isset( $values['options']['before_html'] ) ? $values['options']['before_html'] : FrmFormsHelper::get_default_html( 'before' );
33 $options['after_html'] = isset( $values['options']['after_html'] ) ? $values['options']['after_html'] : FrmFormsHelper::get_default_html( 'after' );
34 $options['submit_html'] = isset( $values['options']['submit_html'] ) ? $values['options']['submit_html'] : FrmFormsHelper::get_default_html( 'submit' );
35
36 /**
37 * Allows modifying form options before updating or creating.
38 *
39 * @since 5.4 Add the third param.
40 *
41 * @param array $options Form options.
42 * @param array $values Form data.
43 * @param bool $update Is form updating or creating. It's `true` if is updating.
44 */
45 $options = apply_filters( 'frm_form_options_before_update', $options, $values, false );
46 $options = self::maybe_filter_form_options( $options );
47 $new_values['options'] = serialize( $options );
48
49 $wpdb->insert( $wpdb->prefix . 'frm_forms', $new_values );
50
51 $id = $wpdb->insert_id;
52
53 // Clear form caching
54 self::clear_form_cache();
55
56 return $id;
57 }
58
59 /**
60 * @since 5.0.08
61 *
62 * @param array $options
63 * @return array
64 */
65 private static function maybe_filter_form_options( $options ) {
66 if ( ! FrmAppHelper::allow_unfiltered_html() && ! empty( $options['submit_html'] ) ) {
67 $options['submit_html'] = FrmAppHelper::kses_submit_button( $options['submit_html'] );
68 }
69 return FrmAppHelper::maybe_filter_array( $options, array( 'submit_value', 'success_msg', 'before_html', 'after_html' ) );
70 }
71
72 /**
73 * @return bool|int ID on success or false on failure
74 */
75 public static function duplicate( $id, $template = false, $copy_keys = false, $blog_id = false ) {
76 global $wpdb;
77
78 $values = self::getOne( $id, $blog_id );
79 if ( ! $values ) {
80 return false;
81 }
82
83 $new_key = $copy_keys ? $values->form_key : '';
84
85 $new_values = array(
86 'form_key' => FrmAppHelper::get_unique_key( $new_key, $wpdb->prefix . 'frm_forms', 'form_key' ),
87 'name' => $values->name,
88 'description' => $values->description,
89 'status' => $values->status ? $values->status : 'published',
90 'logged_in' => $values->logged_in ? $values->logged_in : 0,
91 'editable' => $values->editable ? $values->editable : 0,
92 'created_at' => current_time( 'mysql', 1 ),
93 'is_template' => $template ? 1 : 0,
94 );
95
96 if ( $blog_id ) {
97 $new_values['status'] = 'published';
98 $new_options = $values->options;
99 FrmAppHelper::unserialize_or_decode( $new_options );
100 $new_options['email_to'] = get_option( 'admin_email' );
101 $new_options['copy'] = false;
102 $new_values['options'] = $new_options;
103 } else {
104 $new_values['options'] = $values->options;
105 }
106
107 if ( is_array( $new_values['options'] ) ) {
108 $new_values['options'] = serialize( $new_values['options'] );
109 }
110
111 $query_results = $wpdb->insert( $wpdb->prefix . 'frm_forms', $new_values );
112
113 if ( $query_results ) {
114 // Clear form caching
115 self::clear_form_cache();
116
117 $form_id = $wpdb->insert_id;
118 FrmField::duplicate( $id, $form_id, $copy_keys, $blog_id );
119
120 // update form settings after fields are created
121 do_action( 'frm_after_duplicate_form', $form_id, $new_values, array( 'old_id' => $id ) );
122
123 return $form_id;
124 }
125
126 return false;
127 }
128
129 public static function after_duplicate( $form_id, $values ) {
130 $new_opts = $values['options'];
131 FrmAppHelper::unserialize_or_decode( $new_opts );
132 $values['options'] = $new_opts;
133
134 if ( isset( $new_opts['success_msg'] ) ) {
135 $new_opts['success_msg'] = FrmFieldsHelper::switch_field_ids( $new_opts['success_msg'] );
136 }
137
138 $new_opts = apply_filters( 'frm_after_duplicate_form_values', $new_opts, $form_id );
139
140 if ( $new_opts != $values['options'] ) {
141 global $wpdb;
142 $wpdb->update( $wpdb->prefix . 'frm_forms', array( 'options' => maybe_serialize( $new_opts ) ), array( 'id' => $form_id ) );
143 }
144
145 self::switch_field_ids_in_fields( $form_id );
146 }
147
148 /**
149 * Switches field ID in fields.
150 *
151 * @since 5.3
152 *
153 * @param int $form_id Form ID.
154 */
155 private static function switch_field_ids_in_fields( $form_id ) {
156 global $wpdb;
157
158 // Keys of fields that you want to check to replace field ID.
159 $keys = array( 'default_value', 'field_options' );
160 $sql_cols = 'fi.id';
161 foreach ( $keys as $key ) {
162 $sql_cols .= ',fi.' . $key;
163 }
164
165 $fields = FrmDb::get_results(
166 "{$wpdb->prefix}frm_fields AS fi LEFT OUTER JOIN {$wpdb->prefix}frm_forms AS fr ON fi.form_id = fr.id",
167 array(
168 'or' => 1,
169 'fi.form_id' => $form_id,
170 'fr.parent_form_id' => $form_id,
171 ),
172 $sql_cols
173 );
174
175 if ( ! $fields || ! is_array( $fields ) ) {
176 return;
177 }
178
179 foreach ( $fields as $field ) {
180 self::switch_field_ids_in_field( (array) $field );
181 }
182 }
183
184 /**
185 * Switches field ID in a field.
186 *
187 * @since 5.3
188 *
189 * @param array $field Field array.
190 */
191 private static function switch_field_ids_in_field( $field ) {
192 $new_values = array();
193 foreach ( $field as $key => $value ) {
194 if ( 'id' === $key || ! $value ) {
195 continue;
196 }
197
198 if ( ! is_string( $value ) && ! is_array( $value ) ) {
199 continue;
200 }
201
202 if ( 'field_options' === $key ) {
203 // Need to loop through field_options to prevent breaking serialized string when length changed.
204 FrmAppHelper::unserialize_or_decode( $value );
205 $new_val = FrmFieldsHelper::switch_field_ids( $value );
206 $new_val = serialize( $new_val );
207 } else {
208 $new_val = FrmFieldsHelper::switch_field_ids( $value );
209 }
210
211 if ( $new_val !== $value ) {
212 $new_values[ $key ] = $new_val;
213 }
214 }//end foreach
215
216 if ( ! empty( $new_values ) ) {
217 FrmField::update( $field['id'], $new_values );
218 }
219 }
220
221 /**
222 * @return bool|int
223 */
224 public static function update( $id, $values, $create_link = false ) {
225 global $wpdb;
226
227 $values = FrmAppHelper::maybe_filter_array( $values, array( 'name', 'description' ) );
228
229 if ( ! isset( $values['status'] ) && ( $create_link || isset( $values['options'] ) || isset( $values['item_meta'] ) || isset( $values['field_options'] ) ) ) {
230 $values['status'] = 'published';
231 }
232
233 if ( isset( $values['form_key'] ) ) {
234 $values['form_key'] = FrmAppHelper::get_unique_key( $values['form_key'], $wpdb->prefix . 'frm_forms', 'form_key', $id );
235 }
236
237 $form_fields = array( 'form_key', 'name', 'description', 'status', 'parent_form_id' );
238
239 $new_values = self::set_update_options( array(), $values, array( 'form_id' => $id ) );
240
241 foreach ( $values as $value_key => $value ) {
242 if ( $value_key && in_array( $value_key, $form_fields ) ) {
243 $new_values[ $value_key ] = $value;
244 }
245 }
246
247 if ( ! empty( $values['new_status'] ) ) {
248 $new_values['status'] = $values['new_status'];
249 }
250
251 if ( ! empty( $new_values ) ) {
252 $query_results = $wpdb->update( $wpdb->prefix . 'frm_forms', $new_values, array( 'id' => $id ) );
253 if ( $query_results ) {
254 self::clear_form_cache();
255 }
256 } else {
257 $query_results = true;
258 }
259 unset( $new_values );
260
261 $values = self::update_fields( $id, $values );
262
263 do_action( 'frm_update_form', $id, $values );
264 do_action( 'frm_update_form_' . $id, $values );
265
266 return $query_results;
267 }
268
269 /**
270 * @param array $new_values
271 * @param array $values
272 * @param array $args
273 * @return array
274 */
275 public static function set_update_options( $new_values, $values, $args = array() ) {
276 if ( ! isset( $values['options'] ) ) {
277 return $new_values;
278 }
279
280 $options = ! empty( $values['options'] ) ? (array) $values['options'] : array();
281 FrmFormsHelper::fill_form_options( $options, $values );
282
283 $options['custom_style'] = isset( $values['options']['custom_style'] ) ? $values['options']['custom_style'] : 0;
284 $options['before_html'] = isset( $values['options']['before_html'] ) ? $values['options']['before_html'] : FrmFormsHelper::get_default_html( 'before' );
285 $options['after_html'] = isset( $values['options']['after_html'] ) ? $values['options']['after_html'] : FrmFormsHelper::get_default_html( 'after' );
286 $options['submit_html'] = isset( $values['options']['submit_html'] ) && '' !== $values['options']['submit_html'] ? $values['options']['submit_html'] : FrmFormsHelper::get_default_html( 'submit' );
287
288 /**
289 * Allows modifying form options before updating or creating.
290 *
291 * @since 5.4 Added the third param.
292 *
293 * @param array $options Form options.
294 * @param array $values Form data.
295 * @param bool $update Is form updating or creating. It's `true` if is updating.
296 */
297 $options = apply_filters( 'frm_form_options_before_update', $options, $values, true );
298 $options = self::maybe_filter_form_options( $options );
299 $new_values['options'] = serialize( $options );
300
301 return $new_values;
302 }
303
304 /**
305 * @return array
306 */
307 public static function update_fields( $id, $values ) {
308
309 if ( ! isset( $values['item_meta'] ) && ! isset( $values['field_options'] ) ) {
310 return $values;
311 }
312
313 $all_fields = FrmField::get_all_for_form( $id );
314 if ( empty( $all_fields ) ) {
315 return $values;
316 }
317
318 if ( ! isset( $values['item_meta'] ) ) {
319 $values['item_meta'] = array();
320 }
321
322 $field_array = array();
323 $existing_keys = array_keys( $values['item_meta'] );
324 foreach ( $all_fields as $fid ) {
325 if ( ! in_array( $fid->id, $existing_keys ) && ( isset( $values['frm_fields_submitted'] ) && in_array( $fid->id, $values['frm_fields_submitted'] ) ) || isset( $values['options'] ) ) {
326 $values['item_meta'][ $fid->id ] = '';
327 }
328 $field_array[ $fid->id ] = $fid;
329 }
330 unset( $all_fields );
331
332 foreach ( $values['item_meta'] as $field_id => $default_value ) {
333 if ( isset( $field_array[ $field_id ] ) ) {
334 $field = $field_array[ $field_id ];
335 } else {
336 $field = FrmField::getOne( $field_id );
337 }
338
339 if ( ! $field ) {
340 continue;
341 }
342
343 $is_settings_page = ( isset( $values['options'] ) || isset( $values['field_options'][ 'custom_html_' . $field_id ] ) );
344 if ( $is_settings_page ) {
345 self::get_settings_page_html( $values, $field );
346
347 if ( ! defined( 'WP_IMPORTING' ) ) {
348 continue;
349 }
350 }
351
352 // Updating the form.
353 $update_options = FrmFieldsHelper::get_default_field_options_from_field( $field );
354 // Don't check for POST html.
355 unset( $update_options['custom_html'] );
356 $update_options = apply_filters( 'frm_field_options_to_update', $update_options );
357
358 foreach ( $update_options as $opt => $default ) {
359 $field->field_options[ $opt ] = isset( $values['field_options'][ $opt . '_' . $field_id ] ) ? $values['field_options'][ $opt . '_' . $field_id ] : $default;
360 self::sanitize_field_opt( $opt, $field->field_options[ $opt ] );
361 }
362
363 $field->field_options = apply_filters( 'frm_update_field_options', $field->field_options, $field, $values );
364
365 $new_field = array(
366 'field_options' => $field->field_options,
367 'default_value' => isset( $values[ 'default_value_' . $field_id ] ) ? FrmAppHelper::maybe_json_encode( $values[ 'default_value_' . $field_id ] ) : '',
368 );
369
370 if ( ! FrmAppHelper::allow_unfiltered_html() && isset( $values['field_options'][ 'options_' . $field_id ] ) && is_array( $values['field_options'][ 'options_' . $field_id ] ) ) {
371 foreach ( $values['field_options'][ 'options_' . $field_id ] as $option_key => $option ) {
372 if ( is_array( $option ) ) {
373 foreach ( $option as $key => $item ) {
374 $values['field_options'][ 'options_' . $field_id ][ $option_key ][ $key ] = FrmAppHelper::kses( $item, 'all' );
375 }
376 }
377 }
378 }
379
380 self::prepare_field_update_values( $field, $values, $new_field );
381 self::maybe_update_max_option( $field, $values, $new_field );
382
383 FrmField::update( $field_id, $new_field );
384
385 FrmField::delete_form_transient( $field->form_id );
386 }//end foreach
387 self::clear_form_cache();
388
389 return $values;
390 }
391
392 /**
393 * Resets the 'max' option of a field when changing paragraph field type to other field types like text, email etc.
394 *
395 * @since 6.7
396 *
397 * @param array $field
398 * @param array $values
399 * @param array $new_field
400 * @return void
401 */
402 private static function maybe_update_max_option( $field, $values, &$new_field ) {
403 if ( $field->type === 'textarea' &&
404 ! empty( $values['field_options'][ 'type_' . $field->id ] ) &&
405 in_array( $values['field_options'][ 'type_' . $field->id ], array( 'text', 'email', 'url', 'password', 'phone' ), true ) ) {
406
407 $new_field['field_options']['max'] = '';
408
409 /**
410 * Update posted field setting so that new 'max' option is displayed after form is saved and page reloads.
411 * FrmFieldsHelper::fill_default_field_opts populates field options by calling self::get_posted_field_setting.
412 */
413 $_POST['field_options'][ 'max_' . $field->id ] = '';
414 }
415 }
416
417 /**
418 * @param string $opt
419 * @param mixed $value
420 * @return void
421 */
422 private static function sanitize_field_opt( $opt, &$value ) {
423 if ( ! is_string( $value ) ) {
424 return;
425 }
426
427 /**
428 * Allow the option to turn off sanitization for a field. This way a custom rule can be used instead.
429 * Make sure to add custom sanitization using the frm_update_field_options filter as the data will no longer be sanitized.
430 *
431 * @since 6.0
432 *
433 * @param bool $should_sanitize
434 * @param string $opt
435 */
436 $should_sanitize = apply_filters( 'frm_should_sanitize_field_opt_string', true, $opt );
437
438 if ( ! $should_sanitize ) {
439 return;
440 }
441
442 if ( $opt === 'calc' ) {
443 $value = self::sanitize_calc( $value );
444 } else {
445 $value = FrmAppHelper::kses( $value, 'all' );
446 }
447
448 $value = trim( $value );
449 }
450
451 /**
452 * @param string $value
453 * @return string
454 */
455 private static function sanitize_calc( $value ) {
456 if ( false !== strpos( $value, '<' ) ) {
457 $value = self::normalize_calc_spaces( $value );
458 }
459 // Allow <= and >=.
460 $allow = array( '<= ', ' >=' );
461 $temp = array( '< = ', ' > =' );
462 $value = str_replace( $allow, $temp, $value );
463 $value = strip_tags( $value );
464 $value = str_replace( $temp, $allow, $value );
465 return $value;
466 }
467
468 /**
469 * Format a comparison like 5<10 to 5 < 10. Also works on 5< 10, 5 <10, 5<=10 variations.
470 * This is to avoid an issue with unspaced calculations being recognized as HTML that gets removed when strip_tags is called.
471 *
472 * @param string $calc
473 * @return string
474 */
475 private static function normalize_calc_spaces( $calc ) {
476 // Check for a pattern with 5 parts
477 // $1 \d the first comparison digit.
478 // $2 a space (optional).
479 // $3 an equals sign (optional) that follows the < operator for <= comparisons.
480 // $4 another space (optional).
481 // $5 \d the second comparison digit.
482 return preg_replace( '/(\d)( ){0,1}<(=){0,1}( ){0,1}(\d)/', '$1 <$3 $5', $calc );
483 }
484
485 /**
486 * Updating the settings page
487 */
488 private static function get_settings_page_html( $values, &$field ) {
489 if ( isset( $values['field_options'][ 'custom_html_' . $field->id ] ) ) {
490 $prev_opts = array();
491 $fallback_html = isset( $field->field_options['custom_html'] ) ? $field->field_options['custom_html'] : FrmFieldsHelper::get_default_html( $field->type );
492
493 $field->field_options['custom_html'] = isset( $values['field_options'][ 'custom_html_' . $field->id ] ) ? $values['field_options'][ 'custom_html_' . $field->id ] : $fallback_html;
494 } elseif ( $field->type === 'hidden' || $field->type === 'user_id' ) {
495 $prev_opts = $field->field_options;
496 }
497
498 if ( isset( $prev_opts ) ) {
499 $field->field_options = apply_filters( 'frm_update_form_field_options', $field->field_options, $field, $values );
500 if ( $prev_opts != $field->field_options ) {
501 FrmField::update( $field->id, array( 'field_options' => $field->field_options ) );
502 }
503 }
504 }
505
506 private static function prepare_field_update_values( $field, $values, &$new_field ) {
507 $field_cols = array(
508 'field_order' => 0,
509 'field_key' => '',
510 'required' => false,
511 'type' => '',
512 'description' => '',
513 'options' => '',
514 'name' => '',
515 );
516 foreach ( $field_cols as $col => $default ) {
517 $default = $default === '' ? $field->{$col} : $default;
518 $new_field[ $col ] = isset( $values['field_options'][ $col . '_' . $field->id ] ) ? $values['field_options'][ $col . '_' . $field->id ] : $default;
519 }
520
521 if ( $field->type === 'submit' && isset( $new_field['field_order'] ) && (int) $new_field['field_order'] === FrmSubmitHelper::DEFAULT_ORDER ) {
522 $new_field['field_order'] = $field->field_order;
523 }
524
525 // Don't save the template option.
526 if ( is_array( $new_field['options'] ) && isset( $new_field['options']['000'] ) ) {
527 unset( $new_field['options']['000'] );
528 }
529 }
530
531 /**
532 * Get a list of all form settings that should be translated
533 * on a multilingual site.
534 *
535 * @since 3.06.01
536 * @param object $form The form object.
537 */
538 public static function translatable_strings( $form ) {
539 $strings = array(
540 'name',
541 'description',
542 'submit_value',
543 'submit_msg',
544 'success_msg',
545 'invalid_msg',
546 'failed_msg',
547 'login_msg',
548 'admin_permission',
549 );
550
551 return apply_filters( 'frm_form_strings', $strings, $form );
552 }
553
554 /**
555 * @param int $id
556 * @param string $status
557 *
558 * @return bool|int
559 */
560 public static function set_status( $id, $status ) {
561 if ( 'trash' == $status ) {
562 return self::trash( $id );
563 }
564
565 $statuses = array( 'published', 'draft', 'trash' );
566 if ( ! in_array( $status, $statuses, true ) ) {
567 return false;
568 }
569
570 global $wpdb;
571
572 if ( is_array( $id ) ) {
573 $where = array(
574 'id' => $id,
575 'parent_form_id' => $id,
576 'or' => 1,
577 );
578 FrmDb::get_where_clause_and_values( $where );
579 array_unshift( $where['values'], $status );
580
581 $query_results = $wpdb->query( $wpdb->prepare( 'UPDATE ' . $wpdb->prefix . 'frm_forms SET status = %s ' . $where['where'], $where['values'] ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
582 } else {
583 $query_results = $wpdb->update( $wpdb->prefix . 'frm_forms', array( 'status' => $status ), array( 'id' => $id ) );
584 $wpdb->update( $wpdb->prefix . 'frm_forms', array( 'status' => $status ), array( 'parent_form_id' => $id ) );
585 }
586
587 if ( $query_results ) {
588 self::clear_form_cache();
589 }
590
591 return $query_results;
592 }
593
594 /**
595 * @return bool|int
596 */
597 public static function trash( $id ) {
598 if ( ! EMPTY_TRASH_DAYS ) {
599 return self::destroy( $id );
600 }
601
602 $form = self::getOne( $id );
603 if ( ! $form ) {
604 return false;
605 }
606
607 $options = $form->options;
608 $options['trash_time'] = time();
609
610 global $wpdb;
611 $query_results = $wpdb->update(
612 $wpdb->prefix . 'frm_forms',
613 array(
614 'status' => 'trash',
615 'options' => serialize( $options ),
616 ),
617 array(
618 'id' => $id,
619 )
620 );
621
622 $wpdb->update(
623 $wpdb->prefix . 'frm_forms',
624 array(
625 'status' => 'trash',
626 'options' => serialize( $options ),
627 ),
628 array(
629 'parent_form_id' => $id,
630 )
631 );
632
633 if ( $query_results ) {
634 self::clear_form_cache();
635 }
636
637 return $query_results;
638 }
639
640 /**
641 * @return bool|int
642 */
643 public static function destroy( $id ) {
644 global $wpdb;
645
646 $form = self::getOne( $id );
647 if ( ! $form ) {
648 return false;
649 }
650 $id = $form->id;
651
652 // Disconnect the entries from this form
653 $entries = FrmDb::get_col( $wpdb->prefix . 'frm_items', array( 'form_id' => $id ) );
654 foreach ( $entries as $entry_id ) {
655 FrmEntry::destroy( $entry_id );
656 unset( $entry_id );
657 }
658
659 // Disconnect the fields from this form
660 $wpdb->query( $wpdb->prepare( 'DELETE fi FROM ' . $wpdb->prefix . 'frm_fields AS fi LEFT JOIN ' . $wpdb->prefix . 'frm_forms fr ON (fi.form_id = fr.id) WHERE fi.form_id=%d OR parent_form_id=%d', $id, $id ) );
661
662 $query_results = $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_forms WHERE id=%d OR parent_form_id=%d', $id, $id ) );
663 if ( $query_results ) {
664 // Delete all form actions linked to this form
665 /**
666 * @var FrmFormAction
667 */
668 $action_control = FrmFormActionsController::get_form_actions( 'email' );
669 $action_control->destroy( $id, 'all' );
670
671 // Clear form caching
672 self::clear_form_cache();
673
674 do_action( 'frm_destroy_form', $id );
675 do_action( 'frm_destroy_form_' . $id );
676 }
677
678 return $query_results;
679 }
680
681 /**
682 * Delete trashed forms based on how long they have been trashed
683 *
684 * @return int The number of forms deleted
685 */
686 public static function scheduled_delete( $delete_timestamp = '' ) {
687 global $wpdb;
688
689 $trash_forms = FrmDb::get_results( $wpdb->prefix . 'frm_forms', array( 'status' => 'trash' ), 'id, parent_form_id, options' );
690
691 if ( ! $trash_forms ) {
692 return 0;
693 }
694
695 if ( empty( $delete_timestamp ) ) {
696 $delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS );
697 }
698
699 $count = 0;
700 foreach ( $trash_forms as $form ) {
701 FrmAppHelper::unserialize_or_decode( $form->options );
702 if ( ! isset( $form->options['trash_time'] ) || $form->options['trash_time'] < $delete_timestamp ) {
703 self::destroy( $form->id );
704 if ( empty( $form->parent_form_id ) ) {
705 ++$count;
706 }
707 }
708
709 unset( $form );
710 }
711
712 return $count;
713 }
714
715 /**
716 * @return string form name
717 */
718 public static function getName( $id ) {
719 $form = FrmDb::check_cache( $id, 'frm_form' );
720 if ( $form ) {
721 $r = stripslashes( $form->name );
722
723 return $r;
724 }
725
726 $query_key = is_numeric( $id ) ? 'id' : 'form_key';
727 $r = FrmDb::get_var( 'frm_forms', array( $query_key => $id ), 'name' );
728
729 // An empty form name can result in a null value.
730 $r = is_null( $r ) ? '' : stripslashes( $r );
731
732 return $r;
733 }
734
735 /**
736 * @since 3.0
737 *
738 * @param string $key
739 *
740 * @return int form id
741 */
742 public static function get_id_by_key( $key ) {
743 return (int) FrmDb::get_var( 'frm_forms', array( 'form_key' => sanitize_title( $key ) ) );
744 }
745
746 /**
747 * @since 3.0
748 *
749 * @param int $id
750 *
751 * @return string form key
752 */
753 public static function get_key_by_id( $id ) {
754 $id = (int) $id;
755 $cache = FrmDb::check_cache( $id, 'frm_form' );
756 if ( $cache ) {
757 return $cache->form_key;
758 }
759
760 $key = FrmDb::get_var( 'frm_forms', array( 'id' => $id ), 'form_key' );
761
762 return $key;
763 }
764
765 /**
766 * If $form is numeric, get the form object
767 *
768 * @since 2.0.9
769 * @param int|object $form
770 */
771 public static function maybe_get_form( &$form ) {
772 if ( ! is_object( $form ) && ! is_array( $form ) && ! empty( $form ) ) {
773 $form = self::getOne( $form );
774 }
775 }
776
777 /**
778 * @param int|string $id
779 * @param false|int $blog_id
780 * @return stdClass|null
781 */
782 public static function getOne( $id, $blog_id = false ) {
783 global $wpdb;
784
785 if ( $blog_id && is_multisite() ) {
786 global $wpmuBaseTablePrefix;
787 $prefix = $wpmuBaseTablePrefix ? $wpmuBaseTablePrefix . $blog_id . '_' : $wpdb->get_blog_prefix( $blog_id );
788
789 $table_name = $prefix . 'frm_forms';
790 } else {
791 $table_name = $wpdb->prefix . 'frm_forms';
792 $cache = wp_cache_get( $id, 'frm_form' );
793 if ( $cache ) {
794 if ( isset( $cache->options ) ) {
795 FrmAppHelper::unserialize_or_decode( $cache->options );
796 }
797 return self::prepare_form_row_data( $cache );
798 }
799 }
800
801 if ( is_numeric( $id ) ) {
802 $where = array( 'id' => $id );
803 } else {
804 $where = array( 'form_key' => $id );
805 }
806
807 $results = FrmDb::get_row( $table_name, $where );
808
809 if ( isset( $results->options ) ) {
810 FrmDb::set_cache( $results->id, $results, 'frm_form' );
811 FrmAppHelper::unserialize_or_decode( $results->options );
812 }
813
814 return self::prepare_form_row_data( $results );
815 }
816
817 /**
818 * Make sure that if $row is an object, that $row->options is an array and not a string.
819 *
820 * @since 6.8.3
821 *
822 * @param stdClass|null $row The database row for a target form.
823 * @return stdClass|null
824 */
825 private static function prepare_form_row_data( $row ) {
826 $row = wp_unslash( $row );
827 if ( ! is_object( $row ) ) {
828 return $row;
829 }
830
831 if ( ! is_array( $row->options ) ) {
832 $row->options = FrmFormsHelper::get_default_opts();
833 }
834
835 /**
836 * @since 4.03.02
837 *
838 * @param stdClass $row
839 */
840 return apply_filters( 'frm_form_object', $row );
841 }
842
843 /**
844 * @return array|object of objects
845 */
846 public static function getAll( $where = array(), $order_by = '', $limit = '' ) {
847 if ( is_array( $where ) && ! empty( $where ) ) {
848 if ( isset( $where['is_template'] ) && $where['is_template'] && ! isset( $where['status'] ) && ! isset( $where['status !'] ) ) {
849 // don't get trashed templates
850 $where['status'] = array( null, '', 'published' );
851 }
852
853 $results = FrmDb::get_results( 'frm_forms', $where, '*', compact( 'order_by', 'limit' ) );
854 } else {
855 global $wpdb;
856
857 // The query has already been prepared if this is not an array.
858 $query = 'SELECT * FROM ' . $wpdb->prefix . 'frm_forms' . FrmDb::prepend_and_or_where( ' WHERE ', $where ) . FrmDb::esc_order( $order_by ) . FrmDb::esc_limit( $limit );
859 $results = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
860 }
861
862 if ( $results ) {
863 foreach ( $results as $result ) {
864 FrmDb::set_cache( $result->id, $result, 'frm_form' );
865 FrmAppHelper::unserialize_or_decode( $result->options );
866 }
867 }
868
869 if ( $limit === ' LIMIT 1' || $limit == 1 ) {
870 // return the first form object if we are only getting one form
871 $results = reset( $results );
872 }
873
874 return wp_unslash( $results );
875 }
876
877 /**
878 * Get all published forms
879 *
880 * @since 2.0
881 *
882 * @param array $query
883 * @param int $limit
884 * @param string $inc_children
885 * @return array|object of forms A single form object would be passed if $limit was set to 1.
886 */
887 public static function get_published_forms( $query = array(), $limit = 999, $inc_children = 'exclude' ) {
888 $query['is_template'] = 0;
889 $query['status'] = array( null, '', 'published' );
890 if ( $inc_children === 'exclude' ) {
891 $query['parent_form_id'] = array( null, 0 );
892 }
893
894 $forms = self::getAll( $query, 'name', $limit );
895
896 return $forms;
897 }
898
899 /**
900 * @return object count of forms
901 */
902 public static function get_count() {
903 global $wpdb;
904
905 $cache_key = 'frm_form_counts';
906
907 $counts = wp_cache_get( $cache_key, 'frm_form' );
908 if ( false !== $counts ) {
909 return $counts;
910 }
911
912 $results = (array) FrmDb::get_results(
913 'frm_forms',
914 array(
915 'or' => 1,
916 'parent_form_id' => null,
917 'parent_form_id <' => 0,
918 ),
919 'status, is_template'
920 );
921
922 $statuses = array( 'published', 'draft', 'template', 'trash' );
923 $counts = array_fill_keys( $statuses, 0 );
924
925 foreach ( $results as $row ) {
926 if ( 'trash' != $row->status ) {
927 if ( $row->is_template ) {
928 ++$counts['template'];
929 } else {
930 ++$counts['published'];
931 }
932 } else {
933 ++$counts['trash'];
934 }
935
936 if ( 'draft' == $row->status ) {
937 ++$counts['draft'];
938 }
939
940 unset( $row );
941 }
942
943 $counts = (object) $counts;
944 FrmDb::set_cache( $cache_key, $counts, 'frm_form' );
945
946 return $counts;
947 }
948
949 /**
950 * Clear form caching
951 * Called when a form is created, updated, duplicated, or deleted
952 * or when the form status is changed
953 *
954 * @since 2.0.4
955 */
956 public static function clear_form_cache() {
957 FrmDb::cache_delete_group( 'frm_form' );
958 }
959
960 /**
961 * @return array of errors
962 */
963 public static function validate( $values ) {
964 $errors = array();
965
966 return apply_filters( 'frm_validate_form', $errors, $values );
967 }
968
969 public static function get_params( $form = null ) {
970 global $frm_vars;
971
972 if ( ! $form ) {
973 $form = self::getAll( array(), 'name', 1 );
974 } else {
975 self::maybe_get_form( $form );
976 }
977
978 if ( isset( $frm_vars['form_params'] ) && is_array( $frm_vars['form_params'] ) && isset( $frm_vars['form_params'][ $form->id ] ) ) {
979 return $frm_vars['form_params'][ $form->id ];
980 }
981
982 $action_var = isset( $_REQUEST['frm_action'] ) ? 'frm_action' : 'action'; // phpcs:ignore WordPress.Security.NonceVerification.Missing
983 $action = apply_filters( 'frm_show_new_entry_page', FrmAppHelper::get_param( $action_var, 'new', 'get', 'sanitize_title' ), $form );
984
985 $default_values = array(
986 'id' => '',
987 'form_name' => '',
988 'paged' => 1,
989 'form' => $form->id,
990 'form_id' => $form->id,
991 'field_id' => '',
992 'search' => '',
993 'sort' => '',
994 'sdir' => '',
995 'action' => $action,
996 );
997
998 $values = array();
999 $values['posted_form_id'] = FrmAppHelper::get_param( 'form_id', '', 'get', 'absint' );
1000 if ( ! $values['posted_form_id'] ) {
1001 $values['posted_form_id'] = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
1002 }
1003
1004 if ( $form->id == $values['posted_form_id'] ) {
1005 // If there are two forms on the same page, make sure not to submit both.
1006 foreach ( $default_values as $var => $default ) {
1007 if ( $var === 'action' ) {
1008 $values[ $var ] = FrmAppHelper::get_param( $action_var, $default, 'get', 'sanitize_title' );
1009 } else {
1010 $values[ $var ] = FrmAppHelper::get_param( $var, $default, 'get', 'sanitize_text_field' );
1011 }
1012 unset( $var, $default );
1013 }
1014 } else {
1015 foreach ( $default_values as $var => $default ) {
1016 $values[ $var ] = $default;
1017 unset( $var, $default );
1018 }
1019 }
1020
1021 if ( in_array( $values['action'], array( 'create', 'update' ) ) &&
1022 ( ! $_POST || ( ! isset( $_POST['action'] ) && ! isset( $_POST['frm_action'] ) ) ) // phpcs:ignore WordPress.Security.NonceVerification.Missing
1023 ) {
1024 $values['action'] = 'new';
1025 }
1026
1027 return $values;
1028 }
1029
1030 public static function list_page_params() {
1031 $values = array();
1032 $defaults = array(
1033 'template' => 0,
1034 'id' => '',
1035 'paged' => 1,
1036 'form' => '',
1037 'search' => '',
1038 'sort' => '',
1039 'sdir' => '',
1040 );
1041 foreach ( $defaults as $var => $default ) {
1042 $values[ $var ] = FrmAppHelper::get_param( $var, $default, 'get', 'sanitize_text_field' );
1043 }
1044
1045 return $values;
1046 }
1047
1048 public static function get_admin_params( $form = null ) {
1049 $form_id = $form;
1050 if ( $form === null ) {
1051 $form_id = self::get_current_form_id();
1052 } elseif ( $form && is_object( $form ) ) {
1053 $form_id = $form->id;
1054 }
1055
1056 $values = array();
1057 $defaults = array(
1058 'id' => '',
1059 'form_name' => '',
1060 'paged' => 1,
1061 'form' => $form_id,
1062 'field_id' => '',
1063 'search' => '',
1064 'sort' => '',
1065 'sdir' => '',
1066 'fid' => '',
1067 'keep_post' => '',
1068 );
1069 foreach ( $defaults as $var => $default ) {
1070 $values[ $var ] = FrmAppHelper::get_param( $var, $default, 'get', 'sanitize_text_field' );
1071 }
1072
1073 return $values;
1074 }
1075
1076 public static function get_current_form_id( $default_form = 'none' ) {
1077 if ( 'first' === $default_form ) {
1078 $form = self::get_current_form();
1079 } else {
1080 $form = self::maybe_get_current_form();
1081 }
1082 $form_id = $form ? $form->id : 0;
1083
1084 return $form_id;
1085 }
1086
1087 public static function maybe_get_current_form( $form_id = 0 ) {
1088 global $frm_vars;
1089
1090 if ( isset( $frm_vars['current_form'] ) && $frm_vars['current_form'] && ( ! $form_id || $form_id == $frm_vars['current_form']->id ) ) {
1091 return $frm_vars['current_form'];
1092 }
1093
1094 $form_id = FrmAppHelper::get_param( 'form', $form_id, 'get', 'absint' );
1095 if ( $form_id ) {
1096 $form_id = self::set_current_form( $form_id );
1097 }
1098
1099 return $form_id;
1100 }
1101
1102 public static function get_current_form( $form_id = 0 ) {
1103 $form = self::maybe_get_current_form( $form_id );
1104 if ( is_numeric( $form ) ) {
1105 $form = self::set_current_form( $form );
1106 }
1107
1108 return $form;
1109 }
1110
1111 public static function set_current_form( $form_id ) {
1112 global $frm_vars;
1113
1114 $query = array();
1115 if ( $form_id ) {
1116 $query['id'] = $form_id;
1117 }
1118
1119 $frm_vars['current_form'] = self::get_published_forms( $query, 1 );
1120
1121 return $frm_vars['current_form'];
1122 }
1123
1124 public static function is_form_loaded( $form, $this_load, $global_load ) {
1125 global $frm_vars;
1126 $small_form = new stdClass();
1127 foreach ( array( 'id', 'form_key', 'name' ) as $var ) {
1128 $small_form->{$var} = $form->{$var};
1129 unset( $var );
1130 }
1131
1132 $frm_vars['forms_loaded'][] = $small_form;
1133
1134 if ( $this_load && empty( $global_load ) ) {
1135 $global_load = true;
1136 $frm_vars['load_css'] = true;
1137 }
1138
1139 return ( ( ! isset( $frm_vars['css_loaded'] ) || ! $frm_vars['css_loaded'] ) && $global_load );
1140 }
1141
1142 /**
1143 * @since 4.06.03
1144 *
1145 * @param object $form
1146 *
1147 * @return bool
1148 */
1149 public static function &is_visible_to_user( $form ) {
1150 if ( $form->logged_in && isset( $form->options['logged_in_role'] ) ) {
1151 $visible = FrmAppHelper::user_has_permission( $form->options['logged_in_role'] );
1152 } else {
1153 $visible = true;
1154 }
1155
1156 return $visible;
1157 }
1158
1159 public static function show_submit( $form ) {
1160 $show = ( ! $form->is_template && $form->status === 'published' && ! FrmAppHelper::is_admin() );
1161 $show = apply_filters( 'frm_show_submit_button', $show, $form );
1162
1163 return $show;
1164 }
1165
1166 /**
1167 * @since 2.3
1168 */
1169 public static function get_option( $atts ) {
1170 $form = $atts['form'];
1171 $default = isset( $atts['default'] ) ? $atts['default'] : '';
1172
1173 return isset( $form->options[ $atts['option'] ] ) ? $form->options[ $atts['option'] ] : $default;
1174 }
1175
1176 /**
1177 * Get the link to edit this form.
1178 *
1179 * @since 4.0
1180 * @param int $form_id The id of the form.
1181 */
1182 public static function get_edit_link( $form_id ) {
1183 return admin_url( 'admin.php?page=formidable&frm_action=edit&id=' . $form_id );
1184 }
1185
1186 /**
1187 * Check if the "Submit this form with AJAX" setting is toggled on.
1188 *
1189 * @since 6.2
1190 *
1191 * @param stdClass $form
1192 * @return bool
1193 */
1194 public static function is_ajax_on( $form ) {
1195 return ! empty( $form->options['ajax_submit'] );
1196 }
1197
1198 /**
1199 * Get the latest form available.
1200 *
1201 * @since 6.8
1202 * @return object
1203 */
1204 public static function get_latest_form() {
1205
1206 $args = array(
1207 array(
1208 'or' => 1,
1209 'parent_form_id' => null,
1210 'parent_form_id <' => 1,
1211 ),
1212 'is_template' => 0,
1213 'status !' => 'trash',
1214 );
1215
1216 return self::getAll( $args, 'created_at desc', 1 );
1217 }
1218
1219 /**
1220 * Count and return total forms.
1221 *
1222 * @since 6.8
1223 * @return int
1224 */
1225 public static function get_forms_count() {
1226
1227 $args = array(
1228 array(
1229 'or' => 1,
1230 'parent_form_id' => null,
1231 'parent_form_id <' => 1,
1232 ),
1233 'is_template' => 0,
1234 'status !' => 'trash',
1235 );
1236
1237 return FrmDb::get_count( 'frm_forms', $args );
1238 }
1239
1240 /**
1241 * @deprecated 2.03.05 This is still referenced in a few add ons (API, locations).
1242 * @codeCoverageIgnore
1243 *
1244 * @param string $key
1245 * @return int form id
1246 */
1247 public static function getIdByKey( $key ) {
1248 _deprecated_function( __FUNCTION__, '2.03.05', 'FrmForm::get_id_by_key' );
1249 return self::get_id_by_key( $key );
1250 }
1251
1252 /**
1253 * @deprecated 2.03.05 This is still referenced in the API add on as of v1.13.
1254 * @codeCoverageIgnore
1255 *
1256 * @param int|string $id
1257 * @return string
1258 */
1259 public static function getKeyById( $id ) {
1260 _deprecated_function( __FUNCTION__, '2.03.05', 'FrmForm::get_key_by_id' );
1261 return self::get_key_by_id( $id );
1262 }
1263 }
1264