PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 4.06.03
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v4.06.03
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 4.06.03, at classes/controllers/FrmXMLController.php

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