PluginProbe
HTML Forms – Simple WordPress Forms Plugin / 1.6.1
HTML Forms – Simple WordPress Forms Plugin v1.6.1
trunk 1.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.2.0 1.3.0 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 All 66 releases
html-forms / src / admin / class-admin.php

class-admin.php in HTML Forms – Simple WordPress Forms Plugin 1.6.1, at src/admin/class-admin.php

621 lines 18.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace HTML_Forms\Admin;
4
5 use HTML_Forms\Form;
6 use HTML_Forms\Submission;
7
8 class Admin {
9
10 /**
11 * @var string
12 */
13 private $plugin_file;
14
15 /**
16 * Admin constructor.
17 *
18 * @param string $plugin_file
19 */
20 public function __construct( $plugin_file ) {
21 $this->plugin_file = $plugin_file;
22 }
23
24 public function hook() {
25 add_action( 'admin_menu', array( $this, 'menu' ) );
26 add_action( 'init', array( $this, 'register_settings' ) );
27 add_action( 'admin_init', array( $this, 'run_migrations' ) );
28 add_action( 'admin_init', array( $this, 'listen' ) );
29 add_action( 'admin_print_styles', array( $this, 'assets' ) );
30 add_action( 'admin_head', array( $this, 'add_screen_options' ) );
31 add_action( 'hf_admin_action_create_form', array( $this, 'process_create_form' ) );
32 add_action( 'wp_ajax_hf_admin_action', array( $this, 'handle_admin_action' ) );
33 add_action( 'wp_ajax_hf_dismiss_recaptcha_notice', array( $this, 'dismiss_recaptcha_notice' ) );
34 add_action( 'hf_admin_action_save_form', array( $this, 'process_save_form' ) );
35 add_action( 'hf_admin_action_bulk_delete_submissions', array( $this, 'process_bulk_delete_submissions' ) );
36
37 add_action( 'hf_admin_output_form_tab_fields', array( $this, 'tab_fields' ) );
38 add_action( 'hf_admin_output_form_tab_messages', array( $this, 'tab_messages' ) );
39 add_action( 'hf_admin_output_form_tab_settings', array( $this, 'tab_settings' ) );
40 add_action( 'hf_admin_output_form_tab_actions', array( $this, 'tab_actions' ) );
41 add_action( 'hf_admin_output_form_tab_submissions', array( $this, 'tab_submissions_list' ) );
42 add_action( 'hf_admin_output_form_tab_submissions', array( $this, 'tab_submissions_detail' ) );
43 add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_gutenberg_assets' ) );
44 }
45
46 public function enqueue_gutenberg_assets() {
47 wp_enqueue_script(
48 'html-forms-block',
49 plugins_url( 'assets/js/gutenberg-block.js', $this->plugin_file ),
50 array(
51 'wp-blocks',
52 'wp-i18n',
53 'wp-element',
54 'wp-components',
55 'wp-block-editor',
56 )
57 );
58 $forms = hf_get_forms();
59 $data = array();
60 foreach ( $forms as $form ) {
61 $data[] = array(
62 'title' => $form->title,
63 'slug' => $form->slug,
64 'id' => $form->ID,
65 );
66 }
67 wp_localize_script( 'html-forms-block', 'html_forms', $data );
68 }
69
70 public function register_settings() {
71 // register settings
72 register_setting( 'hf_settings', 'hf_settings', array( $this, 'sanitize_settings' ) );
73 }
74
75 public function run_migrations() {
76 $version_from = get_option( 'hf_version', '0.0' );
77 $version_to = HTML_FORMS_VERSION;
78
79 if ( version_compare( $version_from, $version_to, '>=' ) ) {
80 return;
81 }
82
83 $migrations = new Migrations( $version_from, $version_to, dirname( $this->plugin_file ) . '/migrations' );
84 $migrations->run();
85 update_option( 'hf_version', HTML_FORMS_VERSION );
86 }
87
88 /**
89 * @param array $dirty
90 *
91 * @return array
92 */
93 public function sanitize_settings( $dirty ) {
94 if ( isset( $dirty['wrapper_tag'] ) ) {
95 $dirty['wrapper_tag'] = sanitize_text_field( $dirty['wrapper_tag'] );
96 }
97
98 return $dirty;
99 }
100
101 public function listen() {
102 if ( isset( $_GET['_hf_admin_action'] ) ) {
103 $action = (string) $_GET['_hf_admin_action'];
104 } elseif ( isset( $_POST['_hf_admin_action'] ) ) {
105 $action = (string) $_POST['_hf_admin_action'];
106 } else {
107 return;
108 }
109
110 // verify nonce
111 if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], '_hf_admin_action' ) ) {
112 wp_nonce_ays( $action );
113 exit;
114 }
115
116 // do nothing if logged in user is not of role administrator
117 if ( ! current_user_can( 'edit_forms' ) ) {
118 return;
119 }
120
121 /**
122 * Allows you to hook into requests containing `_hf_admin_action` => action name.
123 *
124 * The dynamic portion of the hook name, `$action`, refers to the action name.
125 *
126 * By the time this hook is fired, the user is already authorized. After processing all the registered hooks,
127 * the request is redirected back to the referring URL.
128 *
129 * @since 3.0
130 */
131 do_action( 'hf_admin_action_' . $action );
132
133 // redirect back to where we came from
134 $redirect_url = ! empty( $_REQUEST['_redirect_to'] ) ? $_REQUEST['_redirect_to'] : remove_query_arg( '_hf_admin_action' );
135 wp_safe_redirect( $redirect_url );
136 exit;
137 }
138
139 public function assets() {
140 if ( empty( $_GET['page'] ) || strpos( $_GET['page'], 'html-forms' ) !== 0 ) {
141 return;
142 }
143
144 $settings = hf_get_settings();
145
146 wp_enqueue_style( 'html-forms-admin', plugins_url( 'assets/css/admin.css', $this->plugin_file ), array(), HTML_FORMS_VERSION );
147 wp_enqueue_script( 'html-forms-admin', plugins_url( 'assets/js/admin.js', $this->plugin_file ), array(), HTML_FORMS_VERSION, true );
148 wp_localize_script(
149 'html-forms-admin',
150 'hf_options',
151 array(
152 'page' => $_GET['page'],
153 'view' => empty( $_GET['view'] ) ? '' : $_GET['view'],
154 'form_id' => empty( $_GET['form_id'] ) ? 0 : (int) $_GET['form_id'],
155 'wrapper_tag' => empty( $settings['wrapper_tag'] ) ? 'p' : $settings['wrapper_tag'],
156 )
157 );
158 }
159
160 public function menu() {
161 $capability = 'edit_forms';
162 $svg_icon = '<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="256.000000pt" height="256.000000pt" viewBox="0 0 256.000000 256.000000" preserveAspectRatio="xMidYMid meet"><g transform="translate(0.000000,256.000000) scale(0.100000,-0.100000)"
163 fill="#000000" stroke="none"><path d="M0 1280 l0 -1280 1280 0 1280 0 0 1280 0 1280 -1280 0 -1280 0 0 -1280z m2031 593 c8 -8 9 -34 4 -78 -6 -56 -9 -65 -23 -60 -43 16 -98 15 -132 -2 -50 -26 -72 -72 -78 -159 l-5 -74 92 0 91 0 0 -70 0 -70 -90 0 -90 0 0 -345 0 -345 -90 0 -90 0 0 345 0 345 -55 0 -55 0 0 70 0 70 55 0 55 0 0 38 c0 63 20 153 45 202 54 105 141 152 273 147 45 -2 87 -8 93 -14z m-1291 -288 l0 -235 230 0 230 0 0 235 0 235 90 0 90 0 0 -575 0 -575 -90 0 -90 0 0 260 0 260 -230 0 -230 0 0 -260 0 -260 -90 0 -90 0 0 575 0 575 90 0 90 0 0 -235z"/></g></svg>';
164 add_menu_page(
165 'HTML Forms',
166 'HTML Forms',
167 $capability,
168 'html-forms',
169 array(
170 $this,
171 'page_overview',
172 ),
173 'data:image/svg+xml;base64,' . base64_encode( $svg_icon ),
174 '99.88491'
175 );
176 add_submenu_page(
177 'html-forms',
178 __( 'Forms', 'html-forms' ),
179 __( 'All Forms', 'html-forms' ),
180 $capability,
181 'html-forms',
182 array(
183 $this,
184 'page_overview',
185 )
186 );
187 add_submenu_page(
188 'html-forms',
189 __( 'Add New Form', 'html-forms' ),
190 __( 'Add New', 'html-forms' ),
191 $capability,
192 'html-forms-add-form',
193 array(
194 $this,
195 'page_new_form',
196 )
197 );
198 add_submenu_page(
199 'html-forms',
200 __( 'Settings', 'html-forms' ),
201 __( 'Settings', 'html-forms' ),
202 $capability,
203 'html-forms-settings',
204 array(
205 $this,
206 'page_settings',
207 )
208 );
209
210 if ( ! defined( 'HF_PREMIUM_VERSION' ) ) {
211 add_submenu_page(
212 'html-forms',
213 'Premium',
214 '<span style="color: #ea6ea6;">Premium</span>',
215 $capability,
216 'html-forms-premium',
217 array(
218 $this,
219 'page_premium',
220 )
221 );
222 }
223 }
224
225 public function add_screen_options() {
226 // only run on the submissions overview page (not detail)
227 if ( empty( $_GET['page'] ) || $_GET['page'] !== 'html-forms' || empty( $_GET['view'] ) || $_GET['view'] !== 'edit' || empty( $_GET['form_id'] ) || ! empty( $_GET['submission_id'] ) ) {
228 return;
229 }
230
231 // don't run if form does not have submissions enabled
232 $form = hf_get_form( $_GET['form_id'] );
233 if ( ! $form->settings['save_submissions'] ) {
234 return;
235 }
236
237 // tell screen options to show columns option
238 $submissions = hf_get_form_submissions( $_GET['form_id'] );
239 $columns = $this->get_submission_columns( $submissions );
240 add_filter(
241 'manage_toplevel_page_html-forms_columns',
242 function ( $unused ) use ( $columns ) {
243 return $columns;
244 }
245 );
246 add_screen_option( 'layout_columns' );
247 }
248
249 public function page_overview() {
250 if ( ! empty( $_GET['view'] ) && $_GET['view'] === 'edit' ) {
251 $this->page_edit_form();
252
253 return;
254 }
255
256 $settings = hf_get_settings();
257
258 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
259 $table = new Table( $settings );
260
261 require dirname( $this->plugin_file ) . '/views/page-overview.php';
262 }
263
264 public function page_new_form() {
265 require dirname( $this->plugin_file ) . '/views/page-add-form.php';
266 }
267
268 public function page_settings() {
269 $settings = hf_get_settings();
270 $wrapper_tags = array ( 'p', 'div', 'span' );
271
272 require dirname( $this->plugin_file ) . '/views/page-global-settings.php';
273 }
274
275 public function page_premium() {
276 require dirname( $this->plugin_file ) . '/views/page-premium.php';
277 }
278
279 public function page_edit_form() {
280 $active_tab = ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'fields';
281 $form_id = (int) $_GET['form_id'];
282 $form = hf_get_form( $form_id );
283 $settings = hf_get_settings();
284 require dirname( $this->plugin_file ) . '/views/page-edit-form.php';
285 }
286
287 public function tab_fields( Form $form ) {
288 $form_preview_url = add_query_arg(
289 array(
290 'hf_preview_form' => $form->ID,
291 ),
292 site_url( '/', 'admin' )
293 );
294 require dirname( $this->plugin_file ) . '/views/tab-fields.php';
295 }
296
297 public function tab_messages( Form $form ) {
298 require dirname( $this->plugin_file ) . '/views/tab-messages.php';
299 }
300
301
302 public function tab_settings( Form $form ) {
303 require dirname( $this->plugin_file ) . '/views/tab-settings.php';
304 }
305
306
307 public function tab_actions( Form $form ) {
308 require dirname( $this->plugin_file ) . '/views/tab-actions.php';
309 }
310
311 public function get_submission_columns( array $submissions ) {
312 $columns = array();
313 foreach ( $submissions as $s ) {
314 if ( ! is_array( $s->data ) ) {
315 continue;
316 }
317
318 foreach ( $s->data as $field => $value ) {
319 if ( ! isset( $columns[ $field ] ) ) {
320 $columns[ $field ] = esc_html( ucfirst( strtolower( str_replace( '_', ' ', $field ) ) ) );
321 }
322 }
323 }
324
325 return $columns;
326 }
327
328 public function tab_submissions_list( Form $form ) {
329 if ( ! empty( $_GET['submission_id'] ) ) {
330 return;
331 }
332
333 $items_per_page = 500;
334 $total_items = hf_count_form_submissions( $form->ID );
335 $total_pages = max( 1, ceil( $total_items / $items_per_page ) );
336 $current_page = isset( $_GET['paged'] ) ? intval( $_GET['paged'] ) : 1;
337 $current_page = max( 1, $current_page );
338 $current_page = min( $total_pages, $current_page );
339 $submissions = hf_get_form_submissions(
340 $form->ID,
341 array(
342 'limit' => $items_per_page,
343 'offset' => ( $current_page - 1 ) * $items_per_page,
344 )
345 );
346 $columns = $this->get_submission_columns( $submissions );
347 $hidden_columns = get_hidden_columns( get_current_screen() );
348
349 require dirname( $this->plugin_file ) . '/views/tab-submissions-list.php';
350 }
351
352 public function tab_submissions_detail( Form $form ) {
353 if ( empty( $_GET['submission_id'] ) ) {
354 return;
355 }
356
357 $submission = hf_get_form_submission( (int) $_GET['submission_id'] );
358 do_action( 'hf_admin_form_submissions_detail', $submission );
359 require dirname( $this->plugin_file ) . '/views/tab-submissions-detail.php';
360 }
361
362 public function process_create_form() {
363 // Fix for MultiSite stripping KSES for roles other than administrator
364 remove_all_filters( 'content_save_pre' );
365
366 $data = $_POST['form'];
367 $form_title = sanitize_text_field( $data['title'] );
368 $form_id = wp_insert_post(
369 array(
370 'post_type' => 'html-form',
371 'post_status' => 'publish',
372 'post_title' => $form_title,
373 'post_content' => $this->get_default_form_content(),
374 )
375 );
376
377 wp_safe_redirect( admin_url( 'admin.php?page=html-forms&view=edit&form_id=' . $form_id ) );
378 exit;
379 }
380
381 public function process_save_form() {
382 $form_id = (int) $_POST['form_id'];
383 $form = hf_get_form( $form_id );
384 $data = $_POST['form'];
385
386 // Fix for MultiSite stripping KSES for roles other than administrator
387 remove_all_filters( 'content_save_pre' );
388
389 // run our own kses filter
390 if ( ! current_user_can( 'unfiltered_html' ) ) {
391 $data['markup'] = $this->kses( $data['markup'] );
392 }
393
394 // strip <form> tag from markup
395 $data['markup'] = preg_replace( '/<\/?form(.|\s)*?>/i', '', $data['markup'] );
396
397 $form_id = wp_insert_post(
398 array(
399 'ID' => $form_id,
400 'post_type' => 'html-form',
401 'post_status' => 'publish',
402 'post_title' => sanitize_text_field( $data['title'] ),
403 'post_content' => $data['markup'],
404 'post_name' => sanitize_title_with_dashes( $data['slug'] ),
405 )
406 );
407
408 if ( ! empty( $data['settings'] ) ) {
409 update_post_meta( $form_id, '_hf_settings', $data['settings'] );
410 }
411
412 // save form messages in individual meta keys
413 foreach ( $data['messages'] as $key => $message ) {
414 if ( current_user_can( 'unfiltered_html' ) ) {
415 update_post_meta( $form_id, 'hf_message_' . $key, $message );
416 } else {
417 update_post_meta( $form_id, 'hf_message_' . $key, wp_kses_post( $message ) );
418 }
419 }
420
421 $redirect_url_args = array(
422 'form_id' => $form_id,
423 'saved' => 1,
424 );
425 $redirect_url = add_query_arg( $redirect_url_args, admin_url( 'admin.php?page=html-forms&view=edit' ) );
426 wp_safe_redirect( $redirect_url );
427 exit;
428 }
429
430 /**
431 * Get URL for a tab on the current page.
432 *
433 * @param $tab
434 *
435 * @return string
436 * @since 3.0
437 * @internal
438 */
439 public function get_tab_url( $tab ) {
440 $tab_url = add_query_arg( array( 'tab' => $tab ), remove_query_arg( 'tab' ) );
441
442 $url_parts = parse_url($tab_url);
443 if ( isset( $url_parts['query'] ) ) {
444 parse_str( $url_parts['query'], $query_params );
445
446 if ( isset( $query_params['submission_id'] ) ) {
447 unset( $query_params['submission_id'] );
448 }
449
450 $new_query = http_build_query( $query_params );
451 $new_tab_url = $url_parts['path'];
452
453 if ( ! empty( $new_query ) ) {
454 $new_tab_url .= '?' . $new_query;
455 }
456
457 return $new_tab_url;
458 }
459
460 return $tab_url;
461 }
462
463 /**
464 * @return array
465 */
466 public function get_available_form_actions() {
467 $actions = array();
468
469 /**
470 * Filters the available form actions
471 *
472 * @param array $actions
473 */
474 $actions = apply_filters( 'hf_available_form_actions', $actions );
475
476 return $actions;
477 }
478
479 public function process_bulk_delete_submissions() {
480 global $wpdb;
481
482 if ( empty( $_POST['id'] ) ) {
483 return;
484 }
485
486 $args = array_map( 'intval', $_POST['id'] );
487 $table = $wpdb->prefix . 'hf_submissions';
488 $placeholders = rtrim( str_repeat( '%d,', count( $args ) ), ',' );
489 $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE id IN( {$placeholders} );", $args ) );
490
491 $args[] = '_hf_%%';
492 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE post_id IN ( {$placeholders} ) AND meta_key LIKE %s;", $args ) );
493 }
494
495 private function get_default_form_content() {
496 $settings = hf_get_settings();
497 $wrapper_tag = ( isset( $settings['wrapper_tag'] ) ) ? $settings['wrapper_tag'] : 'p';
498
499 $html = '';
500 $html .= sprintf( "<%1\$s>\n\t<label>%2\$s</label>\n\t<input type=\"text\" name=\"NAME\" placeholder=\"%2\$s\" required />\n</%1\$s>", $wrapper_tag, __( 'Your name', 'html-forms' ) ) . PHP_EOL;
501 $html .= sprintf( "<%1\$s>\n\t<label>%2\$s</label>\n\t<input type=\"email\" name=\"EMAIL\" placeholder=\"%2\$s\" required />\n</%1\$s>", $wrapper_tag, __( 'Your email', 'html-forms' ) ) . PHP_EOL;
502 $html .= sprintf( "<%1\$s>\n\t<label>%2\$s</label>\n\t<input type=\"text\" name=\"SUBJECT\" placeholder=\"%2\$s\" required />\n</%1\$s>", $wrapper_tag, __( 'Subject', 'html-forms' ) ) . PHP_EOL;
503 $html .= sprintf( "<%1\$s>\n\t<label>%2\$s</label>\n\t<textarea name=\"MESSAGE\" placeholder=\"%2\$s\" required></textarea>\n</%1\$s>", $wrapper_tag, __( 'Message', 'html-forms' ) ) . PHP_EOL;
504 $html .= sprintf( "<%1\$s>\n\t<input type=\"submit\" value=\"%2\$s\" />\n</%1\$s>", $wrapper_tag, __( 'Send', 'html-forms' ) );
505
506 return $html;
507 }
508
509 /**
510 * Filters string and strips out all HTML tags and attributes, except what's in our whitelist.
511 *
512 * @param string $string The string to apply KSES whitelist on
513 * @return string
514 */
515 private function kses( $string ) {
516 $always_allowed_attr = array_fill_keys(
517 array(
518 'aria-describedby',
519 'aria-details',
520 'aria-label',
521 'aria-labelledby',
522 'aria-hidden',
523 'aria-*',
524 'class',
525 'id',
526 'style',
527 'title',
528 'role',
529 'data-*',
530 'data-confirm',
531 'tabindex',
532 ),
533 true
534 );
535 $input_allowed_attr = array_merge(
536 $always_allowed_attr,
537 array_fill_keys(
538 array(
539 'type',
540 'required',
541 'placeholder',
542 'value',
543 'name',
544 'step',
545 'min',
546 'max',
547 'checked',
548 'width',
549 'autocomplete',
550 'autofocus',
551 'minlength',
552 'maxlength',
553 'size',
554 'pattern',
555 'disabled',
556 'readonly',
557 ),
558 true
559 )
560 );
561
562 $allowed = array(
563 'p' => $always_allowed_attr,
564 'label' => array_merge( $always_allowed_attr, array( 'for' => true ) ),
565 'input' => $input_allowed_attr,
566 'button' => $input_allowed_attr,
567 'fieldset' => $always_allowed_attr,
568 'legend' => $always_allowed_attr,
569 'ul' => $always_allowed_attr,
570 'ol' => $always_allowed_attr,
571 'li' => $always_allowed_attr,
572 'select' => array_merge( $input_allowed_attr, array( 'multiple' => true ) ),
573 'option' => array_merge( $input_allowed_attr, array( 'selected' => true ) ),
574 'optgroup' => array(
575 'disabled' => true,
576 'label' => true,
577 ),
578 'textarea' => array_merge(
579 $input_allowed_attr,
580 array(
581 'rows' => true,
582 'cols' => true,
583 )
584 ),
585 'div' => $always_allowed_attr,
586 'strong' => $always_allowed_attr,
587 'b' => $always_allowed_attr,
588 'i' => $always_allowed_attr,
589 'br' => array(),
590 'em' => $always_allowed_attr,
591 'span' => $always_allowed_attr,
592 'a' => array_merge( $always_allowed_attr, array( 'href' => true ) ),
593 'img' => array_merge(
594 $always_allowed_attr,
595 array(
596 'src' => true,
597 'alt' => true,
598 'width' => true,
599 'height' => true,
600 'srcset' => true,
601 'sizes' => true,
602 'referrerpolicy' => true,
603 'loading' => true,
604 'decoding' => true,
605 )
606 ),
607 'u' => $always_allowed_attr,
608 'table' => $always_allowed_attr,
609 'tr' => $always_allowed_attr,
610 'td' => $always_allowed_attr,
611 'th' => $always_allowed_attr,
612 'thead' => $always_allowed_attr,
613 'tbody' => $always_allowed_attr,
614 'picture' => $always_allowed_attr,
615 'video' => $always_allowed_attr,
616 );
617
618 return wp_kses( $string, $allowed );
619 }
620 }
621