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

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