PluginProbe
HTML Forms – Simple WordPress Forms Plugin / 1.4.0
HTML Forms – Simple WordPress Forms Plugin v1.4.0
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.4.0, at src/admin/class-admin.php

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