PluginProbe
HTML Forms – Simple WordPress Forms Plugin / 1.7.0
HTML Forms – Simple WordPress Forms Plugin v1.7.0
1.7.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 All 67 releases
html-forms / src / admin / class-admin.php

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

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