PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 5.0
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v5.0
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 / controllers / FrmXMLController.php

FrmXMLController.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 5.0, at classes/controllers/FrmXMLController.php

643 lines 18.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 FrmXMLController {
7
8 public static function menu() {
9 add_submenu_page( 'formidable', 'Formidable | ' . __( 'Import/Export', 'formidable' ), __( 'Import/Export', 'formidable' ), 'frm_edit_forms', 'formidable-import', 'FrmXMLController::route' );
10 }
11
12 public static function add_default_templates() {
13 if ( FrmXMLHelper::check_if_libxml_disable_entity_loader_exists() ) {
14 // XML import is not enabled on your server
15 return;
16 }
17
18 $set_err = libxml_use_internal_errors( true );
19 $loader = FrmXMLHelper::maybe_libxml_disable_entity_loader( true );
20
21 $files = apply_filters( 'frm_default_templates_files', array() );
22
23 foreach ( (array) $files as $file ) {
24 FrmXMLHelper::import_xml( $file );
25 unset( $file );
26 }
27
28 unset( $files );
29
30 libxml_use_internal_errors( $set_err );
31 FrmXMLHelper::maybe_libxml_disable_entity_loader( $loader );
32 }
33
34 /**
35 * Use the template link to install the XML template
36 *
37 * @since 3.06
38 */
39 public static function install_template() {
40 FrmAppHelper::permission_check( 'frm_edit_forms' );
41 check_ajax_referer( 'frm_ajax', 'nonce' );
42
43 $url = FrmAppHelper::get_param( 'xml', '', 'post', 'esc_url_raw' );
44
45 $form = self::get_posted_form();
46 self::override_url( $form, $url );
47
48 $response = wp_remote_get( $url );
49 $body = wp_remote_retrieve_body( $response );
50 $xml = simplexml_load_string( $body );
51
52 if ( ! $xml ) {
53 $response = array(
54 'message' => __( 'There was an error reading the form template', 'formidable' ),
55 );
56 echo wp_json_encode( $response );
57 wp_die();
58 }
59
60 self::set_new_form_name( $xml );
61
62 $imported = FrmXMLHelper::import_xml_now( $xml );
63 if ( isset( $imported['form_status'] ) && ! empty( $imported['form_status'] ) ) {
64 // Get the last form id in case there are child forms.
65 end( $imported['form_status'] );
66 $form_id = key( $imported['form_status'] );
67 $response = array(
68 'id' => $form_id,
69 'redirect' => FrmForm::get_edit_link( $form_id ),
70 'success' => 1,
71 );
72 if ( ! empty( $imported['imported']['posts'] ) ) {
73 // Return the link to the last page created.
74 $pages = $imported['posts'];
75 }
76
77 if ( ! empty( $form ) ) {
78 // Create selected pages with the correct shortcodes.
79 $pages = self::create_pages_for_import( $form );
80 }
81
82 if ( isset( $pages ) && ! empty( $pages ) ) {
83 $post_id = end( $pages );
84 $response['redirect'] = get_permalink( $post_id );
85 }
86 } else {
87 if ( isset( $imported['error'] ) ) {
88 $message = $imported['error'];
89 } else {
90 $message = __( 'There was an error importing form', 'formidable' );
91 }
92 $response = array(
93 'message' => $message,
94 );
95
96 }
97
98 $response = apply_filters( 'frm_xml_response', $response, compact( 'form', 'imported' ) );
99
100 echo wp_json_encode( $response );
101 wp_die();
102 }
103
104 /**
105 * @since 4.06.02
106 */
107 private static function get_posted_form() {
108 $form = FrmAppHelper::get_param( 'form', '', 'post', 'wp_unslash' );
109 if ( empty( $form ) ) {
110 return $form;
111 }
112 $form = json_decode( $form, true );
113 return $form;
114 }
115
116 /**
117 * Get a different URL depending on the selection in the form.
118 *
119 * @since 4.06.02
120 */
121 private static function override_url( $form, &$url ) {
122 $selected_form = self::get_selected_in_form( $form, 'form' );
123 if ( empty( $selected_form ) ) {
124 return;
125 }
126
127 $selected_xml = isset( $form['xml'] ) && isset( $form['xml'][ $selected_form ] ) ? $form['xml'][ $selected_form ] : '';
128 if ( empty( $selected_xml ) || strpos( $selected_xml, 'http' ) !== 0 ) {
129 return;
130 }
131
132 $url = $selected_xml;
133 }
134
135 /**
136 * @since 4.06.02
137 */
138 private static function get_selected_in_form( $form, $value = 'form' ) {
139 if ( ! empty( $form ) && isset( $form[ $value ] ) && ! empty( $form[ $value ] ) ) {
140 return $form[ $value ];
141 }
142
143 return '';
144 }
145
146 /**
147 * @since 4.06.02
148 *
149 * @param array $form The posted form values.
150 *
151 * @return array The array of created pages.
152 */
153 private static function create_pages_for_import( $form ) {
154 if ( ! isset( $form['pages'] ) || empty( $form['pages'] ) ) {
155 return;
156 }
157
158 $form_key = self::get_selected_in_form( $form, 'form' );
159 $view_keys = self::get_selected_in_form( $form, 'view' );
160
161 $page_ids = array();
162 foreach ( (array) $form['pages'] as $for => $name ) {
163 if ( empty( $name ) ) {
164 // Don't create a page if no title is given.
165 continue;
166 }
167
168 if ( $for === 'view' ) {
169 $item_key = is_array( $view_keys ) ? $view_keys[ $form_key ] : $view_keys;
170 $shortcode = '[display-frm-data id=%1$s filter=limited]';
171 } elseif ( $for === 'form' ) {
172 $item_key = $form_key;
173 $shortcode = '[formidable id=%1$s]';
174 } else {
175 $item_key = self::get_selected_in_form( $form, 'form' );
176 $shortcode = '[' . esc_html( $for ) . ' id=%1$s]';
177 }
178
179 if ( empty( $item_key ) ) {
180 // Don't create it if the shortcode won't show anything.
181 continue;
182 }
183
184 $page_ids[ $for ] = wp_insert_post(
185 array(
186 'post_title' => $name,
187 'post_type' => 'page',
188 'post_content' => sprintf( $shortcode, $item_key ),
189 )
190 );
191 }
192
193 return $page_ids;
194 }
195
196 /**
197 * Change the name of the last form that is not a child.
198 * This will allow for lookup fields and embedded forms
199 * since we redirect to the last form.
200 *
201 * @since 3.06
202 *
203 * @param object $xml The values included in the XML.
204 */
205 private static function set_new_form_name( &$xml ) {
206 if ( ! isset( $xml->form ) ) {
207 return;
208 }
209
210 $name = FrmAppHelper::get_param( 'name', '', 'post', 'sanitize_text_field' );
211 $description = FrmAppHelper::get_param( 'desc', '', 'post', 'sanitize_textarea_field' );
212 if ( empty( $name ) && empty( $description ) ) {
213 return;
214 }
215
216 // Get the main form ID.
217 $set_name = 0;
218 foreach ( $xml->form as $form ) {
219 if ( ! isset( $form->parent_form_id ) || empty( $form->parent_form_id ) ) {
220 $set_name = $form->id;
221 }
222 }
223
224 foreach ( $xml->form as $form ) {
225 // Maybe set the form name if this isn't a child form.
226 if ( $set_name == $form->id ) {
227 $form->name = $name;
228 $form->description = $description;
229 }
230
231 // Use a unique key to prevent editing existing form.
232 $name = sanitize_title( $form->name );
233 $form->form_key = FrmAppHelper::get_unique_key( $name, 'frm_forms', 'form_key' );
234 }
235 }
236
237 public static function route() {
238 $action = isset( $_REQUEST['frm_action'] ) ? 'frm_action' : 'action';
239 $action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
240 FrmAppHelper::include_svg();
241
242 if ( 'import_xml' === $action ) {
243 return self::import_xml();
244 } elseif ( 'export_xml' === $action ) {
245 return self::export_xml();
246 } elseif ( apply_filters( 'frm_xml_route', true, $action ) ) {
247 return self::form();
248 }
249 }
250
251 public static function form( $errors = array(), $message = '' ) {
252 $where = array(
253 'status' => array( null, '', 'published' ),
254 );
255 $forms = FrmForm::getAll( $where, 'name' );
256
257 $export_types = array(
258 'forms' => __( 'Forms', 'formidable' ),
259 'items' => __( 'Entries', 'formidable' ),
260 );
261 $export_types = apply_filters( 'frm_xml_export_types', $export_types );
262
263 $export_format = array(
264 'xml' => array(
265 'name' => 'XML',
266 'support' => 'forms',
267 'count' => 'multiple',
268 ),
269 'csv' => array(
270 'name' => 'CSV',
271 'support' => 'items',
272 'count' => 'single',
273 ),
274 );
275 $export_format = apply_filters( 'frm_export_formats', $export_format );
276
277 include( FrmAppHelper::plugin_path() . '/classes/views/xml/import_form.php' );
278 }
279
280 public static function import_xml() {
281 $errors = array();
282 $message = '';
283
284 $permission_error = FrmAppHelper::permission_nonce_error( 'frm_edit_forms', 'import-xml', 'import-xml-nonce' );
285 if ( false !== $permission_error ) {
286 $errors[] = $permission_error;
287 self::form( $errors );
288
289 return;
290 }
291
292 $has_file = isset( $_FILES ) && isset( $_FILES['frm_import_file'] ) && ! empty( $_FILES['frm_import_file']['name'] ) && ! empty( $_FILES['frm_import_file']['size'] ) && (int) $_FILES['frm_import_file']['size'] > 0;
293 if ( ! $has_file ) {
294 $errors[] = __( 'Oops, you didn\'t select a file.', 'formidable' );
295 self::form( $errors );
296
297 return;
298 }
299
300 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
301 $file = isset( $_FILES['frm_import_file']['tmp_name'] ) ? sanitize_option( 'upload_path', $_FILES['frm_import_file']['tmp_name'] ) : '';
302
303 if ( ! is_uploaded_file( $file ) ) {
304 unset( $file );
305 $errors[] = __( 'The file does not exist, please try again.', 'formidable' );
306 self::form( $errors );
307
308 return;
309 }
310
311 //add_filter('upload_mimes', 'FrmXMLController::allow_mime');
312
313 $export_format = array(
314 'xml' => array(
315 'name' => 'XML',
316 'support' => 'forms',
317 'count' => 'multiple',
318 ),
319 );
320 $export_format = apply_filters( 'frm_export_formats', $export_format );
321
322 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
323 $file_type = sanitize_option( 'upload_path', $_FILES['frm_import_file']['name'] );
324 $file_type = strtolower( pathinfo( $file_type, PATHINFO_EXTENSION ) );
325 if ( 'xml' !== $file_type && isset( $export_format[ $file_type ] ) ) {
326 // allow other file types to be imported
327 do_action( 'frm_before_import_' . $file_type );
328
329 return;
330 }
331 unset( $file_type );
332
333 if ( FrmXMLHelper::check_if_libxml_disable_entity_loader_exists() ) {
334 $errors[] = __( 'XML import is not enabled on your server with the libxml_disable_entity_loader function.', 'formidable' );
335 self::form( $errors );
336
337 return;
338 }
339
340 $set_err = libxml_use_internal_errors( true );
341 $loader = FrmXMLHelper::maybe_libxml_disable_entity_loader( true );
342
343 $result = FrmXMLHelper::import_xml( $file );
344 FrmXMLHelper::parse_message( $result, $message, $errors );
345
346 unset( $file );
347
348 libxml_use_internal_errors( $set_err );
349 FrmXMLHelper::maybe_libxml_disable_entity_loader( $loader );
350
351 self::form( $errors, $message );
352 }
353
354 public static function export_xml() {
355 $error = FrmAppHelper::permission_nonce_error( 'frm_edit_forms', 'export-xml', 'export-xml-nonce' );
356 if ( ! empty( $error ) ) {
357 wp_die( esc_html( $error ) );
358 }
359
360 $ids = FrmAppHelper::get_post_param( 'frm_export_forms', array(), 'sanitize_text_field' );
361 $type = FrmAppHelper::get_post_param( 'type', array(), 'sanitize_text_field' );
362 $format = FrmAppHelper::get_post_param( 'format', 'xml', 'sanitize_title' );
363
364 if ( ! headers_sent() && ! $type ) {
365 wp_redirect( esc_url_raw( admin_url( 'admin.php?page=formidable-import' ) ) );
366 die();
367 }
368
369 if ( 'xml' === $format ) {
370 self::generate_xml( $type, compact( 'ids' ) );
371 } elseif ( 'csv' === $format ) {
372 self::generate_csv( compact( 'ids' ) );
373 } else {
374 do_action( 'frm_export_format_' . $format, compact( 'ids' ) );
375 }
376
377 wp_die();
378 }
379
380 public static function generate_xml( $type, $args = array() ) {
381 global $wpdb;
382
383 self::prepare_types_array( $type );
384
385 $tables = array(
386 'items' => $wpdb->prefix . 'frm_items',
387 'forms' => $wpdb->prefix . 'frm_forms',
388 'posts' => $wpdb->posts,
389 'styles' => $wpdb->posts,
390 'actions' => $wpdb->posts,
391 );
392
393 $defaults = array(
394 'ids' => false,
395 );
396 $args = wp_parse_args( $args, $defaults );
397
398 // Make sure ids are numeric.
399 if ( is_array( $args['ids'] ) && ! empty( $args['ids'] ) ) {
400 $args['ids'] = array_filter( $args['ids'], 'is_numeric' );
401 }
402
403 $records = array();
404
405 foreach ( $type as $tb_type ) {
406 $where = array();
407 $join = '';
408 $table = $tables[ $tb_type ];
409
410 $select = $table . '.id';
411 $query_vars = array();
412
413 switch ( $tb_type ) {
414 case 'forms':
415 //add forms
416 if ( $args['ids'] ) {
417 $where[] = array(
418 'or' => 1,
419 $table . '.id' => $args['ids'],
420 $table . '.parent_form_id' => $args['ids'],
421 );
422 } else {
423 $where[ $table . '.status !' ] = 'draft';
424 }
425 break;
426 case 'actions':
427 $select = $table . '.ID';
428 $where['post_type'] = FrmFormActionsController::$action_post_type;
429 if ( ! empty( $args['ids'] ) ) {
430 $where['menu_order'] = $args['ids'];
431 }
432 break;
433 case 'items':
434 // $join = "INNER JOIN {$wpdb->prefix}frm_item_metas im ON ($table.id = im.item_id)";
435 if ( $args['ids'] ) {
436 $where[ $table . '.form_id' ] = $args['ids'];
437 }
438 break;
439 case 'styles':
440 // Loop through all exported forms and get their selected style IDs.
441 $frm_style = new FrmStyle();
442 $default_style = $frm_style->get_default_style();
443 $form_ids = $args['ids'];
444 $style_ids = array();
445 foreach ( $form_ids as $form_id ) {
446 $form_data = FrmForm::getOne( $form_id );
447 // For forms that have not been updated while running 2.0, check if custom_style is set.
448 if ( isset( $form_data->options['custom_style'] ) ) {
449 if ( 1 === absint( $form_data->options['custom_style'] ) ) {
450 $style_ids[] = $default_style->ID;
451 } else {
452 $style_ids[] = $form_data->options['custom_style'];
453 }
454 }
455 unset( $form_id, $form_data );
456 }
457 $select = $table . '.ID';
458 $where['post_type'] = 'frm_styles';
459
460 // Only export selected styles.
461 if ( ! empty( $style_ids ) ) {
462 $where['ID'] = $style_ids;
463 }
464 break;
465 default:
466 $select = $table . '.ID';
467 $join = ' INNER JOIN ' . $wpdb->postmeta . ' pm ON (pm.post_id=' . $table . '.ID)';
468 $where['pm.meta_key'] = 'frm_form_id';
469
470 if ( empty( $args['ids'] ) ) {
471 $where['pm.meta_value >'] = 1;
472 } else {
473 $where['pm.meta_value'] = $args['ids'];
474 }
475 }
476
477 $records[ $tb_type ] = FrmDb::get_col( $table . $join, $where, $select );
478 unset( $tb_type );
479 }
480
481 $filename = self::get_file_name( $args, $type, $records );
482
483 header( 'Content-Description: File Transfer' );
484 header( 'Content-Disposition: attachment; filename=' . $filename );
485 header( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ), true );
486
487 echo '<?xml version="1.0" encoding="' . esc_attr( get_bloginfo( 'charset' ) ) . "\" ?>\n";
488 include( FrmAppHelper::plugin_path() . '/classes/views/xml/xml.php' );
489 }
490
491 private static function prepare_types_array( &$type ) {
492 $type = (array) $type;
493 if ( ! in_array( 'forms', $type ) && ( in_array( 'items', $type ) || in_array( 'posts', $type ) ) ) {
494 // make sure the form is included if there are entries
495 $type[] = 'forms';
496 }
497
498 if ( in_array( 'forms', $type ) ) {
499 // include actions with forms
500 $type[] = 'actions';
501 }
502 }
503
504 /**
505 * Use a generic file name if multiple items are exported.
506 * Use the nme of the form if only one form is exported.
507 *
508 * @since 3.06
509 *
510 * @return string
511 */
512 private static function get_file_name( $args, $type, $records ) {
513 $has_one_form = isset( $records['forms'] ) && ! empty( $records['forms'] ) && count( $args['ids'] ) === 1;
514 if ( $has_one_form ) {
515 // one form is being exported
516 $selected_form_id = reset( $args['ids'] );
517 $filename = 'form-' . $selected_form_id . '.xml';
518
519 foreach ( $records['forms'] as $form_id ) {
520 $filename = 'form-' . $form_id . '.xml';
521 if ( $selected_form_id === $form_id ) {
522 $form = FrmForm::getOne( $form_id );
523 $filename = sanitize_title( $form->name ) . '-form.xml';
524 break;
525 }
526 }
527 } else {
528 $sitename = sanitize_key( get_bloginfo( 'name' ) );
529
530 if ( ! empty( $sitename ) ) {
531 $sitename .= '.';
532 }
533 $filename = $sitename . 'formidable.' . gmdate( 'Y-m-d' ) . '.xml';
534 }
535
536 return $filename;
537 }
538
539 public static function generate_csv( $atts ) {
540 $form_ids = $atts['ids'];
541 if ( empty( $form_ids ) ) {
542 wp_die( esc_html__( 'Please select a form', 'formidable' ) );
543 }
544 self::csv( reset( $form_ids ) );
545 }
546
547 /**
548 * Export to CSV
549 *
550 * @since 2.0.19
551 */
552 public static function csv( $form_id = false, $search = '', $fid = '' ) {
553 FrmAppHelper::permission_check( 'frm_view_entries' );
554
555 if ( ! $form_id ) {
556 $form_id = FrmAppHelper::get_param( 'form', '', 'get', 'sanitize_text_field' );
557 $search = FrmAppHelper::get_param( ( isset( $_REQUEST['s'] ) ? 's' : 'search' ), '', 'get', 'sanitize_text_field' );
558 $fid = FrmAppHelper::get_param( 'fid', '', 'get', 'sanitize_text_field' );
559 }
560
561 set_time_limit( 0 ); //Remove time limit to execute this function
562 $mem_limit = str_replace( 'M', '', ini_get( 'memory_limit' ) );
563 if ( (int) $mem_limit < 256 ) {
564 wp_raise_memory_limit();
565 }
566
567 global $wpdb;
568
569 $form = FrmForm::getOne( $form_id );
570 $form_id = $form->id;
571
572 $form_cols = self::get_fields_for_csv_export( $form_id, $form );
573
574 $item_id = FrmAppHelper::get_param( 'item_id', 0, 'get', 'sanitize_text_field' );
575 if ( ! empty( $item_id ) ) {
576 $item_id = explode( ',', $item_id );
577 }
578
579 $query = array(
580 'form_id' => $form_id,
581 );
582
583 if ( $item_id ) {
584 $query['id'] = $item_id;
585 }
586
587 /**
588 * Allows the query to be changed for fetching the entry ids to include in the export
589 *
590 * $query is the array of options to be filtered. It includes form_id, and maybe id (array of entry ids),
591 * and the search query. This should return an array, but it can be handled as a string as well.
592 */
593 $query = apply_filters( 'frm_csv_where', $query, compact( 'form_id', 'search', 'fid', 'item_id' ) );
594
595 $entry_ids = FrmDb::get_col( $wpdb->prefix . 'frm_items it', $query );
596 unset( $query );
597
598 if ( empty( $entry_ids ) ) {
599 esc_html_e( 'There are no entries for that form.', 'formidable' );
600 } else {
601 FrmCSVExportHelper::generate_csv( compact( 'form', 'entry_ids', 'form_cols' ) );
602 }
603
604 wp_die();
605 }
606
607 /**
608 * Get the fields that should be included in the CSV export
609 *
610 * @since 2.0.19
611 *
612 * @param int $form_id
613 * @param object $form
614 *
615 * @return array $csv_fields
616 */
617 private static function get_fields_for_csv_export( $form_id, $form ) {
618 $csv_fields = FrmField::get_all_for_form( $form_id, '', 'include', 'include' );
619 $no_export_fields = FrmField::no_save_fields();
620 foreach ( $csv_fields as $k => $f ) {
621 if ( in_array( $f->type, $no_export_fields, true ) ) {
622 unset( $csv_fields[ $k ] );
623 }
624 }
625
626 return $csv_fields;
627 }
628
629 public static function allow_mime( $mimes ) {
630 if ( ! isset( $mimes['csv'] ) ) {
631 // allow csv files
632 $mimes['csv'] = 'text/csv';
633 }
634
635 if ( ! isset( $mimes['xml'] ) ) {
636 // allow xml
637 $mimes['xml'] = 'text/xml';
638 }
639
640 return $mimes;
641 }
642 }
643