PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.7
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.7
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.7, at classes/models/FrmForm.php

1,185 lines 33.6 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 int|bool 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 int|boolean 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 }
215
216 if ( ! empty( $new_values ) ) {
217 FrmField::update( $field['id'], $new_values );
218 }
219 }
220
221 /**
222 * @return int|boolean
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 ( isset( $values['new_status'] ) && ! 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 = isset( $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 unset( $update_options['custom_html'] ); // don't check for POST html
355 $update_options = apply_filters( 'frm_field_options_to_update', $update_options );
356
357 foreach ( $update_options as $opt => $default ) {
358 $field->field_options[ $opt ] = isset( $values['field_options'][ $opt . '_' . $field_id ] ) ? $values['field_options'][ $opt . '_' . $field_id ] : $default;
359 self::sanitize_field_opt( $opt, $field->field_options[ $opt ] );
360 }
361
362 $field->field_options = apply_filters( 'frm_update_field_options', $field->field_options, $field, $values );
363
364 $new_field = array(
365 'field_options' => $field->field_options,
366 'default_value' => isset( $values[ 'default_value_' . $field_id ] ) ? FrmAppHelper::maybe_json_encode( $values[ 'default_value_' . $field_id ] ) : '',
367 );
368
369 if ( ! FrmAppHelper::allow_unfiltered_html() && isset( $values['field_options'][ 'options_' . $field_id ] ) && is_array( $values['field_options'][ 'options_' . $field_id ] ) ) {
370 foreach ( $values['field_options'][ 'options_' . $field_id ] as $option_key => $option ) {
371 if ( is_array( $option ) ) {
372 foreach ( $option as $key => $item ) {
373 $values['field_options'][ 'options_' . $field_id ][ $option_key ][ $key ] = FrmAppHelper::kses( $item, 'all' );
374 }
375 }
376 }
377 }
378
379 self::prepare_field_update_values( $field, $values, $new_field );
380 self::maybe_update_max_option( $field, $values, $new_field );
381
382 FrmField::update( $field_id, $new_field );
383
384 FrmField::delete_form_transient( $field->form_id );
385 }
386 self::clear_form_cache();
387
388 return $values;
389 }
390
391 /**
392 * Resets the 'max' option of a field when changing paragraph field type to other field types like text, email etc.
393 *
394 * @since 6.7
395 *
396 * @param array $field
397 * @param array $values
398 * @param array $new_field
399 * @return void
400 */
401 private static function maybe_update_max_option( $field, $values, &$new_field ) {
402 if ( $field->type === 'textarea' &&
403 ! empty( $values['field_options'][ 'type_' . $field->id ] ) &&
404 in_array( $values['field_options'][ 'type_' . $field->id ], array( 'text', 'email', 'url', 'password', 'phone' ), true ) ) {
405
406 $new_field['field_options']['max'] = '';
407
408 /**
409 * Update posted field setting so that new 'max' option is displayed after form is saved and page reloads.
410 * FrmFieldsHelper::fill_default_field_opts populates field options by calling self::get_posted_field_setting.
411 */
412 $_POST['field_options'][ 'max_' . $field->id ] = '';
413 }
414 }
415
416 /**
417 * @param string $opt
418 * @param mixed $value
419 * @return void
420 */
421 private static function sanitize_field_opt( $opt, &$value ) {
422 if ( ! is_string( $value ) ) {
423 return;
424 }
425
426 /**
427 * Allow the option to turn off sanitization for a field. This way a custom rule can be used instead.
428 * Make sure to add custom sanitization using the frm_update_field_options filter as the data will no longer be sanitized.
429 *
430 * @since 6.0
431 *
432 * @param bool $should_sanitize
433 * @param string $opt
434 */
435 $should_sanitize = apply_filters( 'frm_should_sanitize_field_opt_string', true, $opt );
436
437 if ( ! $should_sanitize ) {
438 return;
439 }
440
441 if ( $opt === 'calc' ) {
442 $value = self::sanitize_calc( $value );
443 } else {
444 $value = FrmAppHelper::kses( $value, 'all' );
445 }
446
447 $value = trim( $value );
448 }
449
450 /**
451 * @param string $value
452 * @return string
453 */
454 private static function sanitize_calc( $value ) {
455 if ( false !== strpos( $value, '<' ) ) {
456 $value = self::normalize_calc_spaces( $value );
457 }
458 $allow = array( '<= ', ' >=' ); // Allow <= and >=
459 $temp = array( '< = ', ' > =' );
460 $value = str_replace( $allow, $temp, $value );
461 $value = strip_tags( $value );
462 $value = str_replace( $temp, $allow, $value );
463 return $value;
464 }
465
466 /**
467 * Format a comparison like 5<10 to 5 < 10. Also works on 5< 10, 5 <10, 5<=10 variations.
468 * This is to avoid an issue with unspaced calculations being recognized as HTML that gets removed when strip_tags is called.
469 *
470 * @param string $calc
471 * @return string
472 */
473 private static function normalize_calc_spaces( $calc ) {
474 // Check for a pattern with 5 parts
475 // $1 \d the first comparison digit.
476 // $2 a space (optional).
477 // $3 an equals sign (optional) that follows the < operator for <= comparisons.
478 // $4 another space (optional).
479 // $5 \d the second comparison digit.
480 return preg_replace( '/(\d)( ){0,1}<(=){0,1}( ){0,1}(\d)/', '$1 <$3 $5', $calc );
481 }
482
483 /**
484 * Updating the settings page
485 */
486 private static function get_settings_page_html( $values, &$field ) {
487 if ( isset( $values['field_options'][ 'custom_html_' . $field->id ] ) ) {
488 $prev_opts = array();
489 $fallback_html = isset( $field->field_options['custom_html'] ) ? $field->field_options['custom_html'] : FrmFieldsHelper::get_default_html( $field->type );
490
491 $field->field_options['custom_html'] = isset( $values['field_options'][ 'custom_html_' . $field->id ] ) ? $values['field_options'][ 'custom_html_' . $field->id ] : $fallback_html;
492 } elseif ( $field->type == 'hidden' || $field->type == 'user_id' ) {
493 $prev_opts = $field->field_options;
494 }
495
496 if ( isset( $prev_opts ) ) {
497 $field->field_options = apply_filters( 'frm_update_form_field_options', $field->field_options, $field, $values );
498 if ( $prev_opts != $field->field_options ) {
499 FrmField::update( $field->id, array( 'field_options' => $field->field_options ) );
500 }
501 }
502 }
503
504 private static function prepare_field_update_values( $field, $values, &$new_field ) {
505 $field_cols = array(
506 'field_order' => 0,
507 'field_key' => '',
508 'required' => false,
509 'type' => '',
510 'description' => '',
511 'options' => '',
512 'name' => '',
513 );
514 foreach ( $field_cols as $col => $default ) {
515 $default = ( $default === '' ) ? $field->{$col} : $default;
516
517 $new_field[ $col ] = isset( $values['field_options'][ $col . '_' . $field->id ] ) ? $values['field_options'][ $col . '_' . $field->id ] : $default;
518 }
519
520 // Don't save the template option.
521 if ( is_array( $new_field['options'] ) && isset( $new_field['options']['000'] ) ) {
522 unset( $new_field['options']['000'] );
523 }
524 }
525
526 /**
527 * Get a list of all form settings that should be translated
528 * on a multilingual site.
529 *
530 * @since 3.06.01
531 * @param object $form - The form object
532 */
533 public static function translatable_strings( $form ) {
534 $strings = array(
535 'name',
536 'description',
537 'submit_value',
538 'submit_msg',
539 'success_msg',
540 'invalid_msg',
541 'failed_msg',
542 'login_msg',
543 'admin_permission',
544 );
545
546 return apply_filters( 'frm_form_strings', $strings, $form );
547 }
548
549 /**
550 * @param string $status
551 *
552 * @return int|boolean
553 */
554 public static function set_status( $id, $status ) {
555 if ( 'trash' == $status ) {
556 return self::trash( $id );
557 }
558
559 $statuses = array( 'published', 'draft', 'trash' );
560 if ( ! in_array( $status, $statuses ) ) {
561 return false;
562 }
563
564 global $wpdb;
565
566 if ( is_array( $id ) ) {
567 $where = array(
568 'id' => $id,
569 'parent_form_id' => $id,
570 'or' => 1,
571 );
572 FrmDb::get_where_clause_and_values( $where );
573 array_unshift( $where['values'], $status );
574
575 $query_results = $wpdb->query( $wpdb->prepare( 'UPDATE ' . $wpdb->prefix . 'frm_forms SET status = %s ' . $where['where'], $where['values'] ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
576 } else {
577 $query_results = $wpdb->update( $wpdb->prefix . 'frm_forms', array( 'status' => $status ), array( 'id' => $id ) );
578 $wpdb->update( $wpdb->prefix . 'frm_forms', array( 'status' => $status ), array( 'parent_form_id' => $id ) );
579 }
580
581 if ( $query_results ) {
582 self::clear_form_cache();
583 }
584
585 return $query_results;
586 }
587
588 /**
589 * @return int|boolean
590 */
591 public static function trash( $id ) {
592 if ( ! EMPTY_TRASH_DAYS ) {
593 return self::destroy( $id );
594 }
595
596 $form = self::getOne( $id );
597 if ( ! $form ) {
598 return false;
599 }
600
601 $options = $form->options;
602 $options['trash_time'] = time();
603
604 global $wpdb;
605 $query_results = $wpdb->update(
606 $wpdb->prefix . 'frm_forms',
607 array(
608 'status' => 'trash',
609 'options' => serialize( $options ),
610 ),
611 array(
612 'id' => $id,
613 )
614 );
615
616 $wpdb->update(
617 $wpdb->prefix . 'frm_forms',
618 array(
619 'status' => 'trash',
620 'options' => serialize( $options ),
621 ),
622 array(
623 'parent_form_id' => $id,
624 )
625 );
626
627 if ( $query_results ) {
628 self::clear_form_cache();
629 }
630
631 return $query_results;
632 }
633
634 /**
635 * @return int|boolean
636 */
637 public static function destroy( $id ) {
638 global $wpdb;
639
640 $form = self::getOne( $id );
641 if ( ! $form ) {
642 return false;
643 }
644 $id = $form->id;
645
646 // Disconnect the entries from this form
647 $entries = FrmDb::get_col( $wpdb->prefix . 'frm_items', array( 'form_id' => $id ) );
648 foreach ( $entries as $entry_id ) {
649 FrmEntry::destroy( $entry_id );
650 unset( $entry_id );
651 }
652
653 // Disconnect the fields from this form
654 $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 ) );
655
656 $query_results = $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_forms WHERE id=%d OR parent_form_id=%d', $id, $id ) );
657 if ( $query_results ) {
658 // Delete all form actions linked to this form
659 $action_control = FrmFormActionsController::get_form_actions( 'email' );
660 $action_control->destroy( $id, 'all' );
661
662 // Clear form caching
663 self::clear_form_cache();
664
665 do_action( 'frm_destroy_form', $id );
666 do_action( 'frm_destroy_form_' . $id );
667 }
668
669 return $query_results;
670 }
671
672 /**
673 * Delete trashed forms based on how long they have been trashed
674 *
675 * @return int The number of forms deleted
676 */
677 public static function scheduled_delete( $delete_timestamp = '' ) {
678 global $wpdb;
679
680 $trash_forms = FrmDb::get_results( $wpdb->prefix . 'frm_forms', array( 'status' => 'trash' ), 'id, options' );
681
682 if ( ! $trash_forms ) {
683 return 0;
684 }
685
686 if ( empty( $delete_timestamp ) ) {
687 $delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS );
688 }
689
690 $count = 0;
691 foreach ( $trash_forms as $form ) {
692 FrmAppHelper::unserialize_or_decode( $form->options );
693 if ( ! isset( $form->options['trash_time'] ) || $form->options['trash_time'] < $delete_timestamp ) {
694 self::destroy( $form->id );
695 $count ++;
696 }
697
698 unset( $form );
699 }
700
701 return $count;
702 }
703
704 /**
705 * @return string form name
706 */
707 public static function getName( $id ) {
708 $form = FrmDb::check_cache( $id, 'frm_form' );
709 if ( $form ) {
710 $r = stripslashes( $form->name );
711
712 return $r;
713 }
714
715 $query_key = is_numeric( $id ) ? 'id' : 'form_key';
716 $r = FrmDb::get_var( 'frm_forms', array( $query_key => $id ), 'name' );
717
718 // An empty form name can result in a null value.
719 $r = is_null( $r ) ? '' : stripslashes( $r );
720
721 return $r;
722 }
723
724 /**
725 * @since 3.0
726 *
727 * @param string $key
728 *
729 * @return int form id
730 */
731 public static function get_id_by_key( $key ) {
732 return (int) FrmDb::get_var( 'frm_forms', array( 'form_key' => sanitize_title( $key ) ) );
733 }
734
735 /**
736 * @since 3.0
737 *
738 * @param int $id
739 *
740 * @return string form key
741 */
742 public static function get_key_by_id( $id ) {
743 $id = (int) $id;
744 $cache = FrmDb::check_cache( $id, 'frm_form' );
745 if ( $cache ) {
746 return $cache->form_key;
747 }
748
749 $key = FrmDb::get_var( 'frm_forms', array( 'id' => $id ), 'form_key' );
750
751 return $key;
752 }
753
754 /**
755 * If $form is numeric, get the form object
756 *
757 * @param object|int $form
758 *
759 * @since 2.0.9
760 */
761 public static function maybe_get_form( &$form ) {
762 if ( ! is_object( $form ) && ! is_array( $form ) && ! empty( $form ) ) {
763 $form = self::getOne( $form );
764 }
765 }
766
767 /**
768 * @return object form
769 */
770 public static function getOne( $id, $blog_id = false ) {
771 global $wpdb;
772
773 if ( $blog_id && is_multisite() ) {
774 global $wpmuBaseTablePrefix;
775 $prefix = $wpmuBaseTablePrefix ? $wpmuBaseTablePrefix . $blog_id . '_' : $wpdb->get_blog_prefix( $blog_id );
776
777 $table_name = $prefix . 'frm_forms';
778 } else {
779 $table_name = $wpdb->prefix . 'frm_forms';
780 $cache = wp_cache_get( $id, 'frm_form' );
781 if ( $cache ) {
782 if ( isset( $cache->options ) ) {
783 FrmAppHelper::unserialize_or_decode( $cache->options );
784 }
785
786 return apply_filters( 'frm_form_object', wp_unslash( $cache ) );
787 }
788 }
789
790 if ( is_numeric( $id ) ) {
791 $where = array( 'id' => $id );
792 } else {
793 $where = array( 'form_key' => $id );
794 }
795
796 $results = FrmDb::get_row( $table_name, $where );
797
798 if ( isset( $results->options ) ) {
799 FrmDb::set_cache( $results->id, $results, 'frm_form' );
800 FrmAppHelper::unserialize_or_decode( $results->options );
801 }
802
803 return apply_filters( 'frm_form_object', wp_unslash( $results ) );
804 }
805
806 /**
807 * @return object|array of objects
808 */
809 public static function getAll( $where = array(), $order_by = '', $limit = '' ) {
810 if ( is_array( $where ) && ! empty( $where ) ) {
811 if ( isset( $where['is_template'] ) && $where['is_template'] && ! isset( $where['status'] ) && ! isset( $where['status !'] ) ) {
812 // don't get trashed templates
813 $where['status'] = array( null, '', 'published' );
814 }
815
816 $results = FrmDb::get_results( 'frm_forms', $where, '*', compact( 'order_by', 'limit' ) );
817 } else {
818 global $wpdb;
819
820 // the query has already been prepared if this is not an array
821 $query = 'SELECT * FROM ' . $wpdb->prefix . 'frm_forms' . FrmDb::prepend_and_or_where( ' WHERE ', $where ) . FrmDb::esc_order( $order_by ) . FrmDb::esc_limit( $limit );
822 $results = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
823 }
824
825 if ( $results ) {
826 foreach ( $results as $result ) {
827 FrmDb::set_cache( $result->id, $result, 'frm_form' );
828 FrmAppHelper::unserialize_or_decode( $result->options );
829 }
830 }
831
832 if ( $limit == ' LIMIT 1' || $limit == 1 ) {
833 // return the first form object if we are only getting one form
834 $results = reset( $results );
835 }
836
837 return wp_unslash( $results );
838 }
839
840 /**
841 * Get all published forms
842 *
843 * @since 2.0
844 *
845 * @param array $query
846 * @param int $limit
847 * @param string $inc_children
848 * @return array|object of forms A single form object would be passed if $limit was set to 1.
849 */
850 public static function get_published_forms( $query = array(), $limit = 999, $inc_children = 'exclude' ) {
851 $query['is_template'] = 0;
852 $query['status'] = array( null, '', 'published' );
853 if ( $inc_children == 'exclude' ) {
854 $query['parent_form_id'] = array( null, 0 );
855 }
856
857 $forms = self::getAll( $query, 'name', $limit );
858
859 return $forms;
860 }
861
862 /**
863 * @return object count of forms
864 */
865 public static function get_count() {
866 global $wpdb;
867
868 $cache_key = 'frm_form_counts';
869
870 $counts = wp_cache_get( $cache_key, 'frm_form' );
871 if ( false !== $counts ) {
872 return $counts;
873 }
874
875 $results = (array) FrmDb::get_results(
876 'frm_forms',
877 array(
878 'or' => 1,
879 'parent_form_id' => null,
880 'parent_form_id <' => 0,
881 ),
882 'status, is_template'
883 );
884
885 $statuses = array( 'published', 'draft', 'template', 'trash' );
886 $counts = array_fill_keys( $statuses, 0 );
887
888 foreach ( $results as $row ) {
889 if ( 'trash' != $row->status ) {
890 if ( $row->is_template ) {
891 $counts['template'] ++;
892 } else {
893 $counts['published'] ++;
894 }
895 } else {
896 $counts['trash'] ++;
897 }
898
899 if ( 'draft' == $row->status ) {
900 $counts['draft'] ++;
901 }
902
903 unset( $row );
904 }
905
906 $counts = (object) $counts;
907 FrmDb::set_cache( $cache_key, $counts, 'frm_form' );
908
909 return $counts;
910 }
911
912 /**
913 * Clear form caching
914 * Called when a form is created, updated, duplicated, or deleted
915 * or when the form status is changed
916 *
917 * @since 2.0.4
918 */
919 public static function clear_form_cache() {
920 FrmDb::cache_delete_group( 'frm_form' );
921 }
922
923 /**
924 * @return array of errors
925 */
926 public static function validate( $values ) {
927 $errors = array();
928
929 return apply_filters( 'frm_validate_form', $errors, $values );
930 }
931
932 public static function get_params( $form = null ) {
933 global $frm_vars;
934
935 if ( ! $form ) {
936 $form = self::getAll( array(), 'name', 1 );
937 } else {
938 self::maybe_get_form( $form );
939 }
940
941 if ( isset( $frm_vars['form_params'] ) && is_array( $frm_vars['form_params'] ) && isset( $frm_vars['form_params'][ $form->id ] ) ) {
942 return $frm_vars['form_params'][ $form->id ];
943 }
944
945 $action_var = isset( $_REQUEST['frm_action'] ) ? 'frm_action' : 'action'; // phpcs:ignore WordPress.Security.NonceVerification.Missing
946 $action = apply_filters( 'frm_show_new_entry_page', FrmAppHelper::get_param( $action_var, 'new', 'get', 'sanitize_title' ), $form );
947
948 $default_values = array(
949 'id' => '',
950 'form_name' => '',
951 'paged' => 1,
952 'form' => $form->id,
953 'form_id' => $form->id,
954 'field_id' => '',
955 'search' => '',
956 'sort' => '',
957 'sdir' => '',
958 'action' => $action,
959 );
960
961 $values = array();
962 $values['posted_form_id'] = FrmAppHelper::get_param( 'form_id', '', 'get', 'absint' );
963 if ( ! $values['posted_form_id'] ) {
964 $values['posted_form_id'] = FrmAppHelper::get_param( 'form', '', 'get', 'absint' );
965 }
966
967 if ( $form->id == $values['posted_form_id'] ) {
968 //if there are two forms on the same page, make sure not to submit both
969 foreach ( $default_values as $var => $default ) {
970 if ( $var == 'action' ) {
971 $values[ $var ] = FrmAppHelper::get_param( $action_var, $default, 'get', 'sanitize_title' );
972 } else {
973 $values[ $var ] = FrmAppHelper::get_param( $var, $default, 'get', 'sanitize_text_field' );
974 }
975 unset( $var, $default );
976 }
977 } else {
978 foreach ( $default_values as $var => $default ) {
979 $values[ $var ] = $default;
980 unset( $var, $default );
981 }
982 }
983
984 if ( in_array( $values['action'], array( 'create', 'update' ) ) &&
985 ( ! $_POST || ( ! isset( $_POST['action'] ) && ! isset( $_POST['frm_action'] ) ) ) // phpcs:ignore WordPress.Security.NonceVerification.Missing
986 ) {
987 $values['action'] = 'new';
988 }
989
990 return $values;
991 }
992
993 public static function list_page_params() {
994 $values = array();
995 $defaults = array(
996 'template' => 0,
997 'id' => '',
998 'paged' => 1,
999 'form' => '',
1000 'search' => '',
1001 'sort' => '',
1002 'sdir' => '',
1003 );
1004 foreach ( $defaults as $var => $default ) {
1005 $values[ $var ] = FrmAppHelper::get_param( $var, $default, 'get', 'sanitize_text_field' );
1006 }
1007
1008 return $values;
1009 }
1010
1011 public static function get_admin_params( $form = null ) {
1012 $form_id = $form;
1013 if ( $form === null ) {
1014 $form_id = self::get_current_form_id();
1015 } elseif ( $form && is_object( $form ) ) {
1016 $form_id = $form->id;
1017 }
1018
1019 $values = array();
1020 $defaults = array(
1021 'id' => '',
1022 'form_name' => '',
1023 'paged' => 1,
1024 'form' => $form_id,
1025 'field_id' => '',
1026 'search' => '',
1027 'sort' => '',
1028 'sdir' => '',
1029 'fid' => '',
1030 'keep_post' => '',
1031 );
1032 foreach ( $defaults as $var => $default ) {
1033 $values[ $var ] = FrmAppHelper::get_param( $var, $default, 'get', 'sanitize_text_field' );
1034 }
1035
1036 return $values;
1037 }
1038
1039 public static function get_current_form_id( $default_form = 'none' ) {
1040 if ( 'first' == $default_form ) {
1041 $form = self::get_current_form();
1042 } else {
1043 $form = self::maybe_get_current_form();
1044 }
1045 $form_id = $form ? $form->id : 0;
1046
1047 return $form_id;
1048 }
1049
1050 public static function maybe_get_current_form( $form_id = 0 ) {
1051 global $frm_vars;
1052
1053 if ( isset( $frm_vars['current_form'] ) && $frm_vars['current_form'] && ( ! $form_id || $form_id == $frm_vars['current_form']->id ) ) {
1054 return $frm_vars['current_form'];
1055 }
1056
1057 $form_id = FrmAppHelper::get_param( 'form', $form_id, 'get', 'absint' );
1058 if ( $form_id ) {
1059 $form_id = self::set_current_form( $form_id );
1060 }
1061
1062 return $form_id;
1063 }
1064
1065 public static function get_current_form( $form_id = 0 ) {
1066 $form = self::maybe_get_current_form( $form_id );
1067 if ( is_numeric( $form ) ) {
1068 $form = self::set_current_form( $form );
1069 }
1070
1071 return $form;
1072 }
1073
1074 public static function set_current_form( $form_id ) {
1075 global $frm_vars;
1076
1077 $query = array();
1078 if ( $form_id ) {
1079 $query['id'] = $form_id;
1080 }
1081
1082 $frm_vars['current_form'] = self::get_published_forms( $query, 1 );
1083
1084 return $frm_vars['current_form'];
1085 }
1086
1087 public static function is_form_loaded( $form, $this_load, $global_load ) {
1088 global $frm_vars;
1089 $small_form = new stdClass();
1090 foreach ( array( 'id', 'form_key', 'name' ) as $var ) {
1091 $small_form->{$var} = $form->{$var};
1092 unset( $var );
1093 }
1094
1095 $frm_vars['forms_loaded'][] = $small_form;
1096
1097 if ( $this_load && empty( $global_load ) ) {
1098 $global_load = true;
1099 $frm_vars['load_css'] = true;
1100 }
1101
1102 return ( ( ! isset( $frm_vars['css_loaded'] ) || ! $frm_vars['css_loaded'] ) && $global_load );
1103 }
1104
1105 /**
1106 * @since 4.06.03
1107 *
1108 * @param object $form
1109 *
1110 * @return bool
1111 */
1112 public static function &is_visible_to_user( $form ) {
1113 if ( $form->logged_in && isset( $form->options['logged_in_role'] ) ) {
1114 $visible = FrmAppHelper::user_has_permission( $form->options['logged_in_role'] );
1115 } else {
1116 $visible = true;
1117 }
1118
1119 return $visible;
1120 }
1121
1122 public static function show_submit( $form ) {
1123 $show = ( ! $form->is_template && $form->status == 'published' && ! FrmAppHelper::is_admin() );
1124 $show = apply_filters( 'frm_show_submit_button', $show, $form );
1125
1126 return $show;
1127 }
1128
1129 /**
1130 * @since 2.3
1131 */
1132 public static function get_option( $atts ) {
1133 $form = $atts['form'];
1134 $default = isset( $atts['default'] ) ? $atts['default'] : '';
1135
1136 return isset( $form->options[ $atts['option'] ] ) ? $form->options[ $atts['option'] ] : $default;
1137 }
1138
1139 /**
1140 * Get the link to edit this form.
1141 *
1142 * @since 4.0
1143 * @param int $form_id The id of the form.
1144 */
1145 public static function get_edit_link( $form_id ) {
1146 return admin_url( 'admin.php?page=formidable&frm_action=edit&id=' . $form_id );
1147 }
1148
1149 /**
1150 * Check if the "Submit this form with AJAX" setting is toggled on.
1151 *
1152 * @since 6.2
1153 *
1154 * @param stdClass $form
1155 * @return bool
1156 */
1157 public static function is_ajax_on( $form ) {
1158 return ! empty( $form->options['ajax_submit'] );
1159 }
1160
1161 /**
1162 * @deprecated 2.03.05 This is still referenced in a few add ons (API, locations).
1163 * @codeCoverageIgnore
1164 *
1165 * @param string $key
1166 * @return int form id
1167 */
1168 public static function getIdByKey( $key ) {
1169 _deprecated_function( __FUNCTION__, '2.03.05', 'FrmForm::get_id_by_key' );
1170 return self::get_id_by_key( $key );
1171 }
1172
1173 /**
1174 * @deprecated 2.03.05 This is still referenced in the API add on as of v1.13.
1175 * @codeCoverageIgnore
1176 *
1177 * @param string|int $id
1178 * @return string
1179 */
1180 public static function getKeyById( $id ) {
1181 _deprecated_function( __FUNCTION__, '2.03.05', 'FrmForm::get_key_by_id' );
1182 return self::get_key_by_id( $id );
1183 }
1184 }
1185