Make WordPress Core

Ticket #50019: template.php

File template.php, 88.5 KB (added by coolmann, 5 years ago)

edited template.php

Line 
1<?php
2/**
3 * Template WordPress Administration API.
4 *
5 * A Big Mess. Also some neat functions that are nicely written.
6 *
7 * @package WordPress
8 * @subpackage Administration
9 */
10
11/** Walker_Category_Checklist class */
12require_once ABSPATH . 'wp-admin/includes/class-walker-category-checklist.php';
13
14/** WP_Internal_Pointers class */
15require_once ABSPATH . 'wp-admin/includes/class-wp-internal-pointers.php';
16
17//
18// Category Checklists.
19//
20
21/**
22 * Output an unordered list of checkbox input elements labeled with category names.
23 *
24 * @since 2.5.1
25 *
26 * @see wp_terms_checklist()
27 *
28 * @param int    $post_id              Optional. Post to generate a categories checklist for. Default 0.
29 *                                     $selected_cats must not be an array. Default 0.
30 * @param int    $descendants_and_self Optional. ID of the category to output along with its descendants.
31 *                                     Default 0.
32 * @param int[]  $selected_cats        Optional. Array of category IDs to mark as checked. Default false.
33 * @param int[]  $popular_cats         Optional. Array of category IDs to receive the "popular-category" class.
34 *                                     Default false.
35 * @param Walker $walker               Optional. Walker object to use to build the output.
36 *                                     Default is a Walker_Category_Checklist instance.
37 * @param bool   $checked_ontop        Optional. Whether to move checked items out of the hierarchy and to
38 *                                     the top of the list. Default true.
39 */
40function wp_category_checklist( $post_id = 0, $descendants_and_self = 0, $selected_cats = false, $popular_cats = false, $walker = null, $checked_ontop = true ) {
41        wp_terms_checklist(
42                $post_id,
43                array(
44                        'taxonomy'             => 'category',
45                        'descendants_and_self' => $descendants_and_self,
46                        'selected_cats'        => $selected_cats,
47                        'popular_cats'         => $popular_cats,
48                        'walker'               => $walker,
49                        'checked_ontop'        => $checked_ontop,
50                )
51        );
52}
53
54/**
55 * Output an unordered list of checkbox input elements labelled with term names.
56 *
57 * Taxonomy-independent version of wp_category_checklist().
58 *
59 * @since 3.0.0
60 * @since 4.4.0 Introduced the `$echo` argument.
61 *
62 * @param int          $post_id Optional. Post ID. Default 0.
63 * @param array|string $args {
64 *     Optional. Array or string of arguments for generating a terms checklist. Default empty array.
65 *
66 *     @type int    $descendants_and_self ID of the category to output along with its descendants.
67 *                                        Default 0.
68 *     @type int[]  $selected_cats        Array of category IDs to mark as checked. Default false.
69 *     @type int[]  $popular_cats         Array of category IDs to receive the "popular-category" class.
70 *                                        Default false.
71 *     @type Walker $walker               Walker object to use to build the output.
72 *                                        Default is a Walker_Category_Checklist instance.
73 *     @type string $taxonomy             Taxonomy to generate the checklist for. Default 'category'.
74 *     @type bool   $checked_ontop        Whether to move checked items out of the hierarchy and to
75 *                                        the top of the list. Default true.
76 *     @type bool   $echo                 Whether to echo the generated markup. False to return the markup instead
77 *                                        of echoing it. Default true.
78 * }
79 * @return string HTML list of input elements.
80 */
81function wp_terms_checklist( $post_id = 0, $args = array() ) {
82        $defaults = array(
83                'descendants_and_self' => 0,
84                'selected_cats'        => false,
85                'popular_cats'         => false,
86                'walker'               => null,
87                'taxonomy'             => 'category',
88                'checked_ontop'        => true,
89                'echo'                 => true,
90        );
91
92        /**
93         * Filters the taxonomy terms checklist arguments.
94         *
95         * @since 3.4.0
96         *
97         * @see wp_terms_checklist()
98         *
99         * @param array $args    An array of arguments.
100         * @param int   $post_id The post ID.
101         */
102        $params = apply_filters( 'wp_terms_checklist_args', $args, $post_id );
103
104        $parsed_args = wp_parse_args( $params, $defaults );
105
106        if ( empty( $parsed_args['walker'] ) || ! ( $parsed_args['walker'] instanceof Walker ) ) {
107                $walker = new Walker_Category_Checklist;
108        } else {
109                $walker = $parsed_args['walker'];
110        }
111
112        $taxonomy             = $parsed_args['taxonomy'];
113        $descendants_and_self = (int) $parsed_args['descendants_and_self'];
114
115        $args = array( 'taxonomy' => $taxonomy );
116
117        $tax              = get_taxonomy( $taxonomy );
118        $args['disabled'] = ! current_user_can( $tax->cap->assign_terms );
119
120        $args['list_only'] = ! empty( $parsed_args['list_only'] );
121
122        if ( is_array( $parsed_args['selected_cats'] ) ) {
123                $args['selected_cats'] = $parsed_args['selected_cats'];
124        } elseif ( $post_id ) {
125                $args['selected_cats'] = wp_get_object_terms( $post_id, $taxonomy, array_merge( $args, array( 'fields' => 'ids' ) ) );
126        } else {
127                $args['selected_cats'] = array();
128        }
129
130        if ( is_array( $parsed_args['popular_cats'] ) ) {
131                $args['popular_cats'] = $parsed_args['popular_cats'];
132        } else {
133                $args['popular_cats'] = get_terms(
134                        array(
135                                'taxonomy'     => $taxonomy,
136                                'fields'       => 'ids',
137                                'orderby'      => 'count',
138                                'order'        => 'DESC',
139                                'number'       => 10,
140                                'hierarchical' => false,
141                        )
142                );
143        }
144
145        if ( $descendants_and_self ) {
146                $categories = (array) get_terms(
147                        array(
148                                'taxonomy'     => $taxonomy,
149                                'child_of'     => $descendants_and_self,
150                                'hierarchical' => 0,
151                                'hide_empty'   => 0,
152                        )
153                );
154                $self       = get_term( $descendants_and_self, $taxonomy );
155                array_unshift( $categories, $self );
156        } else {
157                $categories = (array) get_terms(
158                        array(
159                                'taxonomy' => $taxonomy,
160                                'get'      => 'all',
161                        )
162                );
163        }
164
165        $output = '';
166
167        if ( $parsed_args['checked_ontop'] ) {
168                // Post-process $categories rather than adding an exclude to the get_terms() query
169                // to keep the query the same across all posts (for any query cache).
170                $checked_categories = array();
171                $keys               = array_keys( $categories );
172
173                foreach ( $keys as $k ) {
174                        if ( in_array( $categories[ $k ]->term_id, $args['selected_cats'] ) ) {
175                                $checked_categories[] = $categories[ $k ];
176                                unset( $categories[ $k ] );
177                        }
178                }
179
180                // Put checked categories on top.
181                $output .= $walker->walk( $checked_categories, 0, $args );
182        }
183        // Then the rest of them.
184        $output .= $walker->walk( $categories, 0, $args );
185
186        if ( $parsed_args['echo'] ) {
187                echo $output;
188        }
189
190        return $output;
191}
192
193/**
194 * Retrieve a list of the most popular terms from the specified taxonomy.
195 *
196 * If the $echo argument is true then the elements for a list of checkbox
197 * `<input>` elements labelled with the names of the selected terms is output.
198 * If the $post_ID global isn't empty then the terms associated with that
199 * post will be marked as checked.
200 *
201 * @since 2.5.0
202 *
203 * @param string $taxonomy Taxonomy to retrieve terms from.
204 * @param int    $default  Not used.
205 * @param int    $number   Number of terms to retrieve. Defaults to 10.
206 * @param bool   $echo     Optionally output the list as well. Defaults to true.
207 * @return int[] Array of popular term IDs.
208 */
209function wp_popular_terms_checklist( $taxonomy, $default = 0, $number = 10, $echo = true ) {
210        $post = get_post();
211
212        if ( $post && $post->ID ) {
213                $checked_terms = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'ids' ) );
214        } else {
215                $checked_terms = array();
216        }
217
218        $terms = get_terms(
219                array(
220                        'taxonomy'     => $taxonomy,
221                        'orderby'      => 'count',
222                        'order'        => 'DESC',
223                        'number'       => $number,
224                        'hierarchical' => false,
225                )
226        );
227
228        $tax = get_taxonomy( $taxonomy );
229
230        $popular_ids = array();
231        foreach ( (array) $terms as $term ) {
232                $popular_ids[] = $term->term_id;
233                if ( ! $echo ) { // Hack for Ajax use.
234                        continue;
235                }
236                $id      = "popular-$taxonomy-$term->term_id";
237                $checked = in_array( $term->term_id, $checked_terms ) ? 'checked="checked"' : '';
238                ?>
239
240                <li id="<?php echo $id; ?>" class="popular-category">
241                        <label class="selectit">
242                                <input id="in-<?php echo $id; ?>" type="checkbox" <?php echo $checked; ?> value="<?php echo (int) $term->term_id; ?>" <?php disabled( ! current_user_can( $tax->cap->assign_terms ) ); ?> />
243                                <?php
244                                /** This filter is documented in wp-includes/category-template.php */
245                                echo esc_html( apply_filters( 'the_category', $term->name, '', '' ) );
246                                ?>
247                        </label>
248                </li>
249
250                <?php
251        }
252        return $popular_ids;
253}
254
255/**
256 * Outputs a link category checklist element.
257 *
258 * @since 2.5.1
259 *
260 * @param int $link_id
261 */
262function wp_link_category_checklist( $link_id = 0 ) {
263        $default = 1;
264
265        $checked_categories = array();
266
267        if ( $link_id ) {
268                $checked_categories = wp_get_link_cats( $link_id );
269                // No selected categories, strange.
270                if ( ! count( $checked_categories ) ) {
271                        $checked_categories[] = $default;
272                }
273        } else {
274                $checked_categories[] = $default;
275        }
276
277        $categories = get_terms(
278                array(
279                        'taxonomy'   => 'link_category',
280                        'orderby'    => 'name',
281                        'hide_empty' => 0,
282                )
283        );
284
285        if ( empty( $categories ) ) {
286                return;
287        }
288
289        foreach ( $categories as $category ) {
290                $cat_id = $category->term_id;
291
292                /** This filter is documented in wp-includes/category-template.php */
293                $name    = esc_html( apply_filters( 'the_category', $category->name, '', '' ) );
294                $checked = in_array( $cat_id, $checked_categories ) ? ' checked="checked"' : '';
295                echo '<li id="link-category-', $cat_id, '"><label for="in-link-category-', $cat_id, '" class="selectit"><input value="', $cat_id, '" type="checkbox" name="link_category[]" id="in-link-category-', $cat_id, '"', $checked, '/> ', $name, '</label></li>';
296        }
297}
298
299/**
300 * Adds hidden fields with the data for use in the inline editor for posts and pages.
301 *
302 * @since 2.7.0
303 *
304 * @param WP_Post $post Post object.
305 */
306function get_inline_data( $post ) {
307        $post_type_object = get_post_type_object( $post->post_type );
308        if ( ! current_user_can( 'edit_post', $post->ID ) ) {
309                return;
310        }
311
312        $title = esc_textarea( trim( $post->post_title ) );
313
314        echo '
315<div class="hidden" id="inline_' . $post->ID . '">
316        <div class="post_title">' . $title . '</div>' .
317        /** This filter is documented in wp-admin/edit-tag-form.php */
318        '<div class="post_name">' . apply_filters( 'editable_slug', $post->post_name, $post ) . '</div>
319        <div class="post_author">' . $post->post_author . '</div>
320        <div class="comment_status">' . esc_html( $post->comment_status ) . '</div>
321        <div class="ping_status">' . esc_html( $post->ping_status ) . '</div>
322        <div class="_status">' . esc_html( $post->post_status ) . '</div>
323        <div class="jj">' . mysql2date( 'd', $post->post_date, false ) . '</div>
324        <div class="mm">' . mysql2date( 'm', $post->post_date, false ) . '</div>
325        <div class="aa">' . mysql2date( 'Y', $post->post_date, false ) . '</div>
326        <div class="hh">' . mysql2date( 'H', $post->post_date, false ) . '</div>
327        <div class="mn">' . mysql2date( 'i', $post->post_date, false ) . '</div>
328        <div class="ss">' . mysql2date( 's', $post->post_date, false ) . '</div>
329        <div class="post_password">' . esc_html( $post->post_password ) . '</div>';
330
331        if ( $post_type_object->hierarchical ) {
332                echo '<div class="post_parent">' . $post->post_parent . '</div>';
333        }
334
335        echo '<div class="page_template">' . ( $post->page_template ? esc_html( $post->page_template ) : 'default' ) . '</div>';
336
337        if ( post_type_supports( $post->post_type, 'page-attributes' ) ) {
338                echo '<div class="menu_order">' . $post->menu_order . '</div>';
339        }
340
341        $taxonomy_names = get_object_taxonomies( $post->post_type );
342        foreach ( $taxonomy_names as $taxonomy_name ) {
343                $taxonomy = get_taxonomy( $taxonomy_name );
344
345                if ( $taxonomy->hierarchical && $taxonomy->show_ui ) {
346
347                        $terms = get_object_term_cache( $post->ID, $taxonomy_name );
348                        if ( false === $terms ) {
349                                $terms = wp_get_object_terms( $post->ID, $taxonomy_name );
350                                wp_cache_add( $post->ID, wp_list_pluck( $terms, 'term_id' ), $taxonomy_name . '_relationships' );
351                        }
352                        $term_ids = empty( $terms ) ? array() : wp_list_pluck( $terms, 'term_id' );
353
354                        echo '<div class="post_category" id="' . $taxonomy_name . '_' . $post->ID . '">' . implode( ',', $term_ids ) . '</div>';
355
356                } elseif ( $taxonomy->show_ui ) {
357
358                        $terms_to_edit = get_terms_to_edit( $post->ID, $taxonomy_name );
359                        if ( ! is_string( $terms_to_edit ) ) {
360                                $terms_to_edit = '';
361                        }
362
363                        echo '<div class="tags_input" id="' . $taxonomy_name . '_' . $post->ID . '">'
364                                . esc_html( str_replace( ',', ', ', $terms_to_edit ) ) . '</div>';
365
366                }
367        }
368
369        if ( ! $post_type_object->hierarchical ) {
370                echo '<div class="sticky">' . ( is_sticky( $post->ID ) ? 'sticky' : '' ) . '</div>';
371        }
372
373        if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
374                echo '<div class="post_format">' . esc_html( get_post_format( $post->ID ) ) . '</div>';
375        }
376
377        /**
378         * Fires after outputting the fields for the inline editor for posts and pages.
379         *
380         * @since 4.9.8
381         *
382         * @param WP_Post      $post             The current post object.
383         * @param WP_Post_Type $post_type_object The current post's post type object.
384         */
385        do_action( 'add_inline_data', $post, $post_type_object );
386
387        echo '</div>';
388}
389
390/**
391 * Outputs the in-line comment reply-to form in the Comments list table.
392 *
393 * @since 2.7.0
394 *
395 * @global WP_List_Table $wp_list_table
396 *
397 * @param int    $position
398 * @param bool   $checkbox
399 * @param string $mode
400 * @param bool   $table_row
401 */
402function wp_comment_reply( $position = 1, $checkbox = false, $mode = 'single', $table_row = true ) {
403        global $wp_list_table;
404        /**
405         * Filters the in-line comment reply-to form output in the Comments
406         * list table.
407         *
408         * Returning a non-empty value here will short-circuit display
409         * of the in-line comment-reply form in the Comments list table,
410         * echoing the returned value instead.
411         *
412         * @since 2.7.0
413         *
414         * @see wp_comment_reply()
415         *
416         * @param string $content The reply-to form content.
417         * @param array  $args    An array of default args.
418         */
419        $content = apply_filters(
420                'wp_comment_reply',
421                '',
422                array(
423                        'position' => $position,
424                        'checkbox' => $checkbox,
425                        'mode'     => $mode,
426                )
427        );
428
429        if ( ! empty( $content ) ) {
430                echo $content;
431                return;
432        }
433
434        if ( ! $wp_list_table ) {
435                if ( 'single' === $mode ) {
436                        $wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table' );
437                } else {
438                        $wp_list_table = _get_list_table( 'WP_Comments_List_Table' );
439                }
440        }
441
442        ?>
443<form method="get">
444        <?php if ( $table_row ) : ?>
445<table style="display:none;"><tbody id="com-reply"><tr id="replyrow" class="inline-edit-row" style="display:none;"><td colspan="<?php echo $wp_list_table->get_column_count(); ?>" class="colspanchange">
446<?php else : ?>
447<div id="com-reply" style="display:none;"><div id="replyrow" style="display:none;">
448<?php endif; ?>
449        <fieldset class="comment-reply">
450        <legend>
451                <span class="hidden" id="editlegend"><?php _e( 'Edit Comment' ); ?></span>
452                <span class="hidden" id="replyhead"><?php _e( 'Reply to Comment' ); ?></span>
453                <span class="hidden" id="addhead"><?php _e( 'Add new Comment' ); ?></span>
454        </legend>
455
456        <div id="replycontainer">
457        <label for="replycontent" class="screen-reader-text"><?php _e( 'Comment' ); ?></label>
458        <?php
459        $quicktags_settings = array( 'buttons' => 'strong,em,link,block,del,ins,img,ul,ol,li,code,close' );
460        wp_editor(
461                '',
462                'replycontent',
463                array(
464                        'media_buttons' => false,
465                        'tinymce'       => false,
466                        'quicktags'     => $quicktags_settings,
467                )
468        );
469        ?>
470        </div>
471
472        <div id="edithead" style="display:none;">
473                <div class="inside">
474                <label for="author-name"><?php _e( 'Name' ); ?></label>
475                <input type="text" name="newcomment_author" size="50" value="" id="author-name" />
476                </div>
477
478                <div class="inside">
479                <label for="author-email"><?php _e( 'Email' ); ?></label>
480                <input type="text" name="newcomment_author_email" size="50" value="" id="author-email" />
481                </div>
482
483                <div class="inside">
484                <label for="author-url"><?php _e( 'URL' ); ?></label>
485                <input type="text" id="author-url" name="newcomment_author_url" class="code" size="103" value="" />
486                </div>
487        </div>
488
489        <div id="replysubmit" class="submit">
490                <p class="reply-submit-buttons">
491                        <button type="button" class="save button button-primary">
492                                <span id="addbtn" style="display: none;"><?php _e( 'Add Comment' ); ?></span>
493                                <span id="savebtn" style="display: none;"><?php _e( 'Update Comment' ); ?></span>
494                                <span id="replybtn" style="display: none;"><?php _e( 'Submit Reply' ); ?></span>
495                        </button>
496                        <button type="button" class="cancel button"><?php _e( 'Cancel' ); ?></button>
497                        <span class="waiting spinner"></span>
498                </p>
499                <div class="notice notice-error notice-alt inline hidden">
500                        <p class="error"></p>
501                </div>
502        </div>
503
504        <input type="hidden" name="action" id="action" value="" />
505        <input type="hidden" name="comment_ID" id="comment_ID" value="" />
506        <input type="hidden" name="comment_post_ID" id="comment_post_ID" value="" />
507        <input type="hidden" name="status" id="status" value="" />
508        <input type="hidden" name="position" id="position" value="<?php echo $position; ?>" />
509        <input type="hidden" name="checkbox" id="checkbox" value="<?php echo $checkbox ? 1 : 0; ?>" />
510        <input type="hidden" name="mode" id="mode" value="<?php echo esc_attr( $mode ); ?>" />
511        <?php
512                wp_nonce_field( 'replyto-comment', '_ajax_nonce-replyto-comment', false );
513        if ( current_user_can( 'unfiltered_html' ) ) {
514                wp_nonce_field( 'unfiltered-html-comment', '_wp_unfiltered_html_comment', false );
515        }
516        ?>
517        </fieldset>
518        <?php if ( $table_row ) : ?>
519</td></tr></tbody></table>
520        <?php else : ?>
521</div></div>
522        <?php endif; ?>
523</form>
524        <?php
525}
526
527/**
528 * Output 'undo move to Trash' text for comments
529 *
530 * @since 2.9.0
531 */
532function wp_comment_trashnotice() {
533        ?>
534<div class="hidden" id="trash-undo-holder">
535        <div class="trash-undo-inside">
536                <?php
537                /* translators: %s: Comment author, filled by AJAX. */
538                printf( __( 'Comment by %s moved to the Trash.' ), '<strong></strong>' );
539                ?>
540                <span class="undo untrash"><a href="#"><?php _e( 'Undo' ); ?></a></span>
541        </div>
542</div>
543<div class="hidden" id="spam-undo-holder">
544        <div class="spam-undo-inside">
545                <?php
546                /* translators: %s: Comment author, filled by AJAX. */
547                printf( __( 'Comment by %s marked as spam.' ), '<strong></strong>' );
548                ?>
549                <span class="undo unspam"><a href="#"><?php _e( 'Undo' ); ?></a></span>
550        </div>
551</div>
552        <?php
553}
554
555/**
556 * Outputs a post's public meta data in the Custom Fields meta box.
557 *
558 * @since 1.2.0
559 *
560 * @param array $meta
561 */
562function list_meta( $meta ) {
563        // Exit if no meta.
564        if ( ! $meta ) {
565                echo '
566<table id="list-table" style="display: none;">
567        <thead>
568        <tr>
569                <th class="left">' . _x( 'Name', 'meta name' ) . '</th>
570                <th>' . __( 'Value' ) . '</th>
571        </tr>
572        </thead>
573        <tbody id="the-list" data-wp-lists="list:meta">
574        <tr><td></td></tr>
575        </tbody>
576</table>'; // TBODY needed for list-manipulation JS.
577                return;
578        }
579        $count = 0;
580        ?>
581<table id="list-table">
582        <thead>
583        <tr>
584                <th class="left"><?php _ex( 'Name', 'meta name' ); ?></th>
585                <th><?php _e( 'Value' ); ?></th>
586        </tr>
587        </thead>
588        <tbody id='the-list' data-wp-lists='list:meta'>
589        <?php
590        foreach ( $meta as $entry ) {
591                echo _list_meta_row( $entry, $count );
592        }
593        ?>
594        </tbody>
595</table>
596        <?php
597}
598
599/**
600 * Outputs a single row of public meta data in the Custom Fields meta box.
601 *
602 * @since 2.5.0
603 *
604 * @staticvar string $update_nonce
605 *
606 * @param array $entry
607 * @param int   $count
608 * @return string
609 */
610function _list_meta_row( $entry, &$count ) {
611        static $update_nonce = '';
612
613        if ( is_protected_meta( $entry['meta_key'], 'post' ) ) {
614                return '';
615        }
616
617        if ( ! $update_nonce ) {
618                $update_nonce = wp_create_nonce( 'add-meta' );
619        }
620
621        $r = '';
622        ++ $count;
623
624        if ( is_serialized( $entry['meta_value'] ) ) {
625                if ( is_serialized_string( $entry['meta_value'] ) ) {
626                        // This is a serialized string, so we should display it.
627                        $entry['meta_value'] = maybe_unserialize( $entry['meta_value'] );
628                } else {
629                        // This is a serialized array/object so we should NOT display it.
630                        --$count;
631                        return '';
632                }
633        }
634
635        $entry['meta_key']   = esc_attr( $entry['meta_key'] );
636        $entry['meta_value'] = esc_textarea( $entry['meta_value'] ); // Using a <textarea />.
637        $entry['meta_id']    = (int) $entry['meta_id'];
638
639        $delete_nonce = wp_create_nonce( 'delete-meta_' . $entry['meta_id'] );
640
641        $r .= "\n\t<tr id='meta-{$entry['meta_id']}'>";
642        $r .= "\n\t\t<td class='left'><label class='screen-reader-text' for='meta-{$entry['meta_id']}-key'>" . __( 'Key' ) . "</label><input name='meta[{$entry['meta_id']}][key]' id='meta-{$entry['meta_id']}-key' type='text' size='20' value='{$entry['meta_key']}' />";
643
644        $r .= "\n\t\t<div class='submit'>";
645        $r .= get_submit_button( __( 'Delete' ), 'deletemeta small', "deletemeta[{$entry['meta_id']}]", false, array( 'data-wp-lists' => "delete:the-list:meta-{$entry['meta_id']}::_ajax_nonce=$delete_nonce" ) );
646        $r .= "\n\t\t";
647        $r .= get_submit_button( __( 'Update' ), 'updatemeta small', "meta-{$entry['meta_id']}-submit", false, array( 'data-wp-lists' => "add:the-list:meta-{$entry['meta_id']}::_ajax_nonce-add-meta=$update_nonce" ) );
648        $r .= '</div>';
649        $r .= wp_nonce_field( 'change-meta', '_ajax_nonce', false, false );
650        $r .= '</td>';
651
652        $r .= "\n\t\t<td><label class='screen-reader-text' for='meta-{$entry['meta_id']}-value'>" . __( 'Value' ) . "</label><textarea name='meta[{$entry['meta_id']}][value]' id='meta-{$entry['meta_id']}-value' rows='2' cols='30'>{$entry['meta_value']}</textarea></td>\n\t</tr>";
653        return $r;
654}
655
656/**
657 * Prints the form in the Custom Fields meta box.
658 *
659 * @since 1.2.0
660 *
661 * @global wpdb $wpdb WordPress database abstraction object.
662 *
663 * @param WP_Post $post Optional. The post being edited.
664 */
665function meta_form( $post = null ) {
666        global $wpdb;
667        $post = get_post( $post );
668
669        /**
670         * Filters values for the meta key dropdown in the Custom Fields meta box.
671         *
672         * Returning a non-null value will effectively short-circuit and avoid a
673         * potentially expensive query against postmeta.
674         *
675         * @since 4.4.0
676         *
677         * @param array|null $keys Pre-defined meta keys to be used in place of a postmeta query. Default null.
678         * @param WP_Post    $post The current post object.
679         */
680        $keys = apply_filters( 'postmeta_form_keys', null, $post );
681
682        if ( null === $keys ) {
683                /**
684                 * Filters the number of custom fields to retrieve for the drop-down
685                 * in the Custom Fields meta box.
686                 *
687                 * @since 2.1.0
688                 *
689                 * @param int $limit Number of custom fields to retrieve. Default 30.
690                 */
691                $limit = apply_filters( 'postmeta_form_limit', 30 );
692                $sql   = "SELECT DISTINCT meta_key
693                        FROM $wpdb->postmeta
694                        WHERE meta_key NOT BETWEEN '_' AND '_z'
695                        HAVING meta_key NOT LIKE %s
696                        ORDER BY meta_key
697                        LIMIT %d";
698                $keys  = $wpdb->get_col( $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', $limit ) );
699        }
700
701        if ( $keys ) {
702                natcasesort( $keys );
703                $meta_key_input_id = 'metakeyselect';
704        } else {
705                $meta_key_input_id = 'metakeyinput';
706        }
707        ?>
708<p><strong><?php _e( 'Add New Custom Field:' ); ?></strong></p>
709<table id="newmeta">
710<thead>
711<tr>
712<th class="left"><label for="<?php echo $meta_key_input_id; ?>"><?php _ex( 'Name', 'meta name' ); ?></label></th>
713<th><label for="metavalue"><?php _e( 'Value' ); ?></label></th>
714</tr>
715</thead>
716
717<tbody>
718<tr>
719<td id="newmetaleft" class="left">
720        <?php if ( $keys ) { ?>
721<select id="metakeyselect" name="metakeyselect">
722<option value="#NONE#"><?php _e( '&mdash; Select &mdash;' ); ?></option>
723                <?php
724
725                foreach ( $keys as $key ) {
726                        if ( is_protected_meta( $key, 'post' ) || ! current_user_can( 'add_post_meta', $post->ID, $key ) ) {
727                                continue;
728                        }
729                        echo "\n<option value='" . esc_attr( $key ) . "'>" . esc_html( $key ) . '</option>';
730                }
731                ?>
732</select>
733<input class="hide-if-js" type="text" id="metakeyinput" name="metakeyinput" value="" />
734<a href="#postcustomstuff" class="hide-if-no-js" onclick="jQuery('#metakeyinput, #metakeyselect, #enternew, #cancelnew').toggle();return false;">
735<span id="enternew"><?php _e( 'Enter new' ); ?></span>
736<span id="cancelnew" class="hidden"><?php _e( 'Cancel' ); ?></span></a>
737<?php } else { ?>
738<input type="text" id="metakeyinput" name="metakeyinput" value="" />
739<?php } ?>
740</td>
741<td><textarea id="metavalue" name="metavalue" rows="2" cols="25"></textarea></td>
742</tr>
743
744<tr><td colspan="2">
745<div class="submit">
746        <?php
747        submit_button(
748                __( 'Add Custom Field' ),
749                '',
750                'addmeta',
751                false,
752                array(
753                        'id'            => 'newmeta-submit',
754                        'data-wp-lists' => 'add:the-list:newmeta',
755                )
756        );
757        ?>
758</div>
759        <?php wp_nonce_field( 'add-meta', '_ajax_nonce-add-meta', false ); ?>
760</td></tr>
761</tbody>
762</table>
763        <?php
764
765}
766
767/**
768 * Print out HTML form date elements for editing post or comment publish date.
769 *
770 * @since 0.71
771 * @since 4.4.0 Converted to use get_comment() instead of the global `$comment`.
772 *
773 * @global WP_Locale $wp_locale WordPress date and time locale object.
774 *
775 * @param int|bool $edit      Accepts 1|true for editing the date, 0|false for adding the date.
776 * @param int|bool $for_post  Accepts 1|true for applying the date to a post, 0|false for a comment.
777 * @param int      $tab_index The tabindex attribute to add. Default 0.
778 * @param int|bool $multi     Optional. Whether the additional fields and buttons should be added.
779 *                            Default 0|false.
780 */
781function touch_time( $edit = 1, $for_post = 1, $tab_index = 0, $multi = 0 ) {
782        global $wp_locale;
783        $post = get_post();
784
785        if ( $for_post ) {
786                $edit = ! ( in_array( $post->post_status, array( 'draft', 'pending' ) ) && ( ! $post->post_date_gmt || '0000-00-00 00:00:00' == $post->post_date_gmt ) );
787        }
788
789        $tab_index_attribute = '';
790        if ( (int) $tab_index > 0 ) {
791                $tab_index_attribute = " tabindex=\"$tab_index\"";
792        }
793
794        // @todo Remove this?
795        // echo '<label for="timestamp" style="display: block;"><input type="checkbox" class="checkbox" name="edit_date" value="1" id="timestamp"'.$tab_index_attribute.' /> '.__( 'Edit timestamp' ).'</label><br />';
796
797        $post_date = ( $for_post ) ? $post->post_date : get_comment()->comment_date;
798        $jj        = ( $edit ) ? mysql2date( 'd', $post_date, false ) : current_time( 'd' );
799        $mm        = ( $edit ) ? mysql2date( 'm', $post_date, false ) : current_time( 'm' );
800        $aa        = ( $edit ) ? mysql2date( 'Y', $post_date, false ) : current_time( 'Y' );
801        $hh        = ( $edit ) ? mysql2date( 'H', $post_date, false ) : current_time( 'H' );
802        $mn        = ( $edit ) ? mysql2date( 'i', $post_date, false ) : current_time( 'i' );
803        $ss        = ( $edit ) ? mysql2date( 's', $post_date, false ) : current_time( 's' );
804
805        $cur_jj = current_time( 'd' );
806        $cur_mm = current_time( 'm' );
807        $cur_aa = current_time( 'Y' );
808        $cur_hh = current_time( 'H' );
809        $cur_mn = current_time( 'i' );
810
811        $month = '<label><span class="screen-reader-text">' . __( 'Month' ) . '</span><select ' . ( $multi ? '' : 'id="mm" ' ) . 'name="mm"' . $tab_index_attribute . ">\n";
812        for ( $i = 1; $i < 13; $i = $i + 1 ) {
813                $monthnum  = zeroise( $i, 2 );
814                $monthtext = $wp_locale->get_month_abbrev( $wp_locale->get_month( $i ) );
815                $month    .= "\t\t\t" . '<option value="' . $monthnum . '" data-text="' . $monthtext . '" ' . selected( $monthnum, $mm, false ) . '>';
816                /* translators: 1: Month number (01, 02, etc.), 2: Month abbreviation. */
817                $month .= sprintf( __( '%1$s-%2$s' ), $monthnum, $monthtext ) . "</option>\n";
818        }
819        $month .= '</select></label>';
820
821        $day    = '<label><span class="screen-reader-text">' . __( 'Day' ) . '</span><input type="text" ' . ( $multi ? '' : 'id="jj" ' ) . 'name="jj" value="' . $jj . '" size="2" maxlength="2"' . $tab_index_attribute . ' autocomplete="off" /></label>';
822        $year   = '<label><span class="screen-reader-text">' . __( 'Year' ) . '</span><input type="text" ' . ( $multi ? '' : 'id="aa" ' ) . 'name="aa" value="' . $aa . '" size="4" maxlength="4"' . $tab_index_attribute . ' autocomplete="off" /></label>';
823        $hour   = '<label><span class="screen-reader-text">' . __( 'Hour' ) . '</span><input type="text" ' . ( $multi ? '' : 'id="hh" ' ) . 'name="hh" value="' . $hh . '" size="2" maxlength="2"' . $tab_index_attribute . ' autocomplete="off" /></label>';
824        $minute = '<label><span class="screen-reader-text">' . __( 'Minute' ) . '</span><input type="text" ' . ( $multi ? '' : 'id="mn" ' ) . 'name="mn" value="' . $mn . '" size="2" maxlength="2"' . $tab_index_attribute . ' autocomplete="off" /></label>';
825
826        echo '<div class="timestamp-wrap">';
827        /* translators: 1: Month, 2: Day, 3: Year, 4: Hour, 5: Minute. */
828        printf( __( '%1$s %2$s, %3$s at %4$s:%5$s' ), $month, $day, $year, $hour, $minute );
829
830        echo '</div><input type="hidden" id="ss" name="ss" value="' . $ss . '" />';
831
832        if ( $multi ) {
833                return;
834        }
835
836        echo "\n\n";
837        $map = array(
838                'mm' => array( $mm, $cur_mm ),
839                'jj' => array( $jj, $cur_jj ),
840                'aa' => array( $aa, $cur_aa ),
841                'hh' => array( $hh, $cur_hh ),
842                'mn' => array( $mn, $cur_mn ),
843        );
844        foreach ( $map as $timeunit => $value ) {
845                list( $unit, $curr ) = $value;
846
847                echo '<input type="hidden" id="hidden_' . $timeunit . '" name="hidden_' . $timeunit . '" value="' . $unit . '" />' . "\n";
848                $cur_timeunit = 'cur_' . $timeunit;
849                echo '<input type="hidden" id="' . $cur_timeunit . '" name="' . $cur_timeunit . '" value="' . $curr . '" />' . "\n";
850        }
851        ?>
852
853<p>
854<a href="#edit_timestamp" class="save-timestamp hide-if-no-js button"><?php _e( 'OK' ); ?></a>
855<a href="#edit_timestamp" class="cancel-timestamp hide-if-no-js button-cancel"><?php _e( 'Cancel' ); ?></a>
856</p>
857        <?php
858}
859
860/**
861 * Print out option HTML elements for the page templates drop-down.
862 *
863 * @since 1.5.0
864 * @since 4.7.0 Added the `$post_type` parameter.
865 *
866 * @param string $default   Optional. The template file name. Default empty.
867 * @param string $post_type Optional. Post type to get templates for. Default 'post'.
868 */
869function page_template_dropdown( $default = '', $post_type = 'page' ) {
870        $templates = get_page_templates( null, $post_type );
871        ksort( $templates );
872        foreach ( array_keys( $templates ) as $template ) {
873                $selected = selected( $default, $templates[ $template ], false );
874                echo "\n\t<option value='" . esc_attr( $templates[ $template ] ) . "' $selected>" . esc_html( $template ) . '</option>';
875        }
876}
877
878/**
879 * Print out option HTML elements for the page parents drop-down.
880 *
881 * @since 1.5.0
882 * @since 4.4.0 `$post` argument was added.
883 *
884 * @global wpdb $wpdb WordPress database abstraction object.
885 *
886 * @param int         $default Optional. The default page ID to be pre-selected. Default 0.
887 * @param int         $parent  Optional. The parent page ID. Default 0.
888 * @param int         $level   Optional. Page depth level. Default 0.
889 * @param int|WP_Post $post    Post ID or WP_Post object.
890 * @return void|false Void on success, false if the page has no children.
891 */
892function parent_dropdown( $default = 0, $parent = 0, $level = 0, $post = null ) {
893        global $wpdb;
894        $post  = get_post( $post );
895        $items = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_parent, post_title FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'page' ORDER BY menu_order", $parent ) );
896
897        if ( $items ) {
898                foreach ( $items as $item ) {
899                        // A page cannot be its own parent.
900                        if ( $post && $post->ID && $item->ID == $post->ID ) {
901                                continue;
902                        }
903
904                        $pad      = str_repeat( '&nbsp;', $level * 3 );
905                        $selected = selected( $default, $item->ID, false );
906
907                        echo "\n\t<option class='level-$level' value='$item->ID' $selected>$pad " . esc_html( $item->post_title ) . '</option>';
908                        parent_dropdown( $default, $item->ID, $level + 1 );
909                }
910        } else {
911                return false;
912        }
913}
914
915/**
916 * Print out option html elements for role selectors.
917 *
918 * @since 2.1.0
919 *
920 * @param string $selected Slug for the role that should be already selected.
921 */
922function wp_dropdown_roles( $selected = '' ) {
923        $r = '';
924
925        $editable_roles = array_reverse( get_editable_roles() );
926
927        foreach ( $editable_roles as $role => $details ) {
928                $name = translate_user_role( $details['name'] );
929                // Preselect specified role.
930                if ( $selected == $role ) {
931                        $r .= "\n\t<option selected='selected' value='" . esc_attr( $role ) . "'>$name</option>";
932                } else {
933                        $r .= "\n\t<option value='" . esc_attr( $role ) . "'>$name</option>";
934                }
935        }
936
937        echo $r;
938}
939
940/**
941 * Outputs the form used by the importers to accept the data to be imported
942 *
943 * @since 2.0.0
944 *
945 * @param string $action The action attribute for the form.
946 */
947function wp_import_upload_form( $action ) {
948
949        /**
950         * Filters the maximum allowed upload size for import files.
951         *
952         * @since 2.3.0
953         *
954         * @see wp_max_upload_size()
955         *
956         * @param int $max_upload_size Allowed upload size. Default 1 MB.
957         */
958        $bytes      = apply_filters( 'import_upload_size_limit', wp_max_upload_size() );
959        $size       = size_format( $bytes );
960        $upload_dir = wp_upload_dir();
961        if ( ! empty( $upload_dir['error'] ) ) :
962                ?>
963                <div class="error"><p><?php _e( 'Before you can upload your import file, you will need to fix the following error:' ); ?></p>
964                <p><strong><?php echo $upload_dir['error']; ?></strong></p></div>
965                                                                <?php
966        else :
967                ?>
968<form enctype="multipart/form-data" id="import-upload-form" method="post" class="wp-upload-form" action="<?php echo esc_url( wp_nonce_url( $action, 'import-upload' ) ); ?>">
969<p>
970                <?php
971                printf(
972                        '<label for="upload">%s</label> (%s)',
973                        __( 'Choose a file from your computer:' ),
974                        /* translators: %s: Maximum allowed file size. */
975                        sprintf( __( 'Maximum size: %s' ), $size )
976                );
977                ?>
978<input type="file" id="upload" name="import" size="25" />
979<input type="hidden" name="action" value="save" />
980<input type="hidden" name="max_file_size" value="<?php echo $bytes; ?>" />
981</p>
982                <?php submit_button( __( 'Upload file and import' ), 'primary' ); ?>
983</form>
984                <?php
985        endif;
986}
987
988/**
989 * Adds a meta box to one or more screens.
990 *
991 * @since 2.5.0
992 * @since 4.4.0 The `$screen` parameter now accepts an array of screen IDs.
993 *
994 * @global array $wp_meta_boxes
995 *
996 * @param string                 $id            Meta box ID (used in the 'id' attribute for the meta box).
997 * @param string                 $title         Title of the meta box.
998 * @param callable               $callback      Function that fills the box with the desired content.
999 *                                              The function should echo its output.
1000 * @param string|array|WP_Screen $screen        Optional. The screen or screens on which to show the box
1001 *                                              (such as a post type, 'link', or 'comment'). Accepts a single
1002 *                                              screen ID, WP_Screen object, or array of screen IDs. Default
1003 *                                              is the current screen.  If you have used add_menu_page() or
1004 *                                              add_submenu_page() to create a new screen (and hence screen_id),
1005 *                                              make sure your menu slug conforms to the limits of sanitize_key()
1006 *                                              otherwise the 'screen' menu may not correctly render on your page.
1007 * @param string                 $context       Optional. The context within the screen where the boxes
1008 *                                              should display. Available contexts vary from screen to
1009 *                                              screen. Post edit screen contexts include 'normal', 'side',
1010 *                                              and 'advanced'. Comments screen contexts include 'normal'
1011 *                                              and 'side'. Menus meta boxes (accordion sections) all use
1012 *                                              the 'side' context. Global default is 'advanced'.
1013 * @param string                 $priority      Optional. The priority within the context where the boxes
1014 *                                              should show ('high', 'low'). Default 'default'.
1015 * @param array                  $callback_args Optional. Data that should be set as the $args property
1016 *                                              of the box array (which is the second parameter passed
1017 *                                              to your callback). Default null.
1018 */
1019function add_meta_box( $id, $title, $callback, $screen = null, $context = 'advanced', $priority = 'default', $callback_args = null ) {
1020        global $wp_meta_boxes;
1021
1022        if ( empty( $screen ) ) {
1023                $screen = get_current_screen();
1024        } elseif ( is_string( $screen ) ) {
1025                $screen = convert_to_screen( $screen );
1026        } elseif ( is_array( $screen ) ) {
1027                foreach ( $screen as $single_screen ) {
1028                        add_meta_box( $id, $title, $callback, $single_screen, $context, $priority, $callback_args );
1029                }
1030        }
1031
1032        if ( ! isset( $screen->id ) ) {
1033                return;
1034        }
1035
1036        $page = $screen->id;
1037
1038        if ( ! isset( $wp_meta_boxes ) ) {
1039                $wp_meta_boxes = array();
1040        }
1041        if ( ! isset( $wp_meta_boxes[ $page ] ) ) {
1042                $wp_meta_boxes[ $page ] = array();
1043        }
1044        if ( ! isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
1045                $wp_meta_boxes[ $page ][ $context ] = array();
1046        }
1047
1048        foreach ( array_keys( $wp_meta_boxes[ $page ] ) as $a_context ) {
1049                foreach ( array( 'high', 'core', 'default', 'low' ) as $a_priority ) {
1050                        if ( ! isset( $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ] ) ) {
1051                                continue;
1052                        }
1053
1054                        // If a core box was previously added or removed by a plugin, don't add.
1055                        if ( 'core' == $priority ) {
1056                                // If core box previously deleted, don't add.
1057                                if ( false === $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ] ) {
1058                                        return;
1059                                }
1060
1061                                /*
1062                                 * If box was added with default priority, give it core priority to
1063                                 * maintain sort order.
1064                                 */
1065                                if ( 'default' == $a_priority ) {
1066                                        $wp_meta_boxes[ $page ][ $a_context ]['core'][ $id ] = $wp_meta_boxes[ $page ][ $a_context ]['default'][ $id ];
1067                                        unset( $wp_meta_boxes[ $page ][ $a_context ]['default'][ $id ] );
1068                                }
1069                                return;
1070                        }
1071                        // If no priority given and ID already present, use existing priority.
1072                        if ( empty( $priority ) ) {
1073                                $priority = $a_priority;
1074                                /*
1075                                * Else, if we're adding to the sorted priority, we don't know the title
1076                                * or callback. Grab them from the previously added context/priority.
1077                                */
1078                        } elseif ( 'sorted' == $priority && is_array( $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ] ) ) {
1079                                $title         = $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ]['title'];
1080                                $callback      = $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ]['callback'];
1081                                $callback_args = $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ]['args'];
1082                        }
1083                        // An ID can be in only one priority and one context.
1084                        if ( $priority != $a_priority || $context != $a_context ) {
1085                                unset( $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ] );
1086                        }
1087                }
1088        }
1089
1090        if ( empty( $priority ) ) {
1091                $priority = 'low';
1092        }
1093
1094        if ( ! isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) {
1095                $wp_meta_boxes[ $page ][ $context ][ $priority ] = array();
1096        }
1097
1098        $wp_meta_boxes[ $page ][ $context ][ $priority ][ $id ] = array(
1099                'id'       => $id,
1100                'title'    => $title,
1101                'callback' => $callback,
1102                'args'     => $callback_args,
1103        );
1104}
1105
1106
1107/**
1108 * Function that renders a "fake" meta box with an information message,
1109 * shown on the block editor, when an incompatible meta box is found.
1110 *
1111 * @since 5.0.0
1112 *
1113 * @param mixed $object The data object being rendered on this screen.
1114 * @param array $box    {
1115 *     Custom formats meta box arguments.
1116 *
1117 *     @type string   $id           Meta box 'id' attribute.
1118 *     @type string   $title        Meta box title.
1119 *     @type callable $old_callback The original callback for this meta box.
1120 *     @type array    $args         Extra meta box arguments.
1121 * }
1122 */
1123function do_block_editor_incompatible_meta_box( $object, $box ) {
1124        $plugin  = _get_plugin_from_callback( $box['old_callback'] );
1125        $plugins = get_plugins();
1126        echo '<p>';
1127        if ( $plugin ) {
1128                /* translators: %s: The name of the plugin that generated this meta box. */
1129                printf( __( "This meta box, from the %s plugin, isn't compatible with the block editor." ), "<strong>{$plugin['Name']}</strong>" );
1130        } else {
1131                _e( "This meta box isn't compatible with the block editor." );
1132        }
1133        echo '</p>';
1134
1135        if ( empty( $plugins['classic-editor/classic-editor.php'] ) ) {
1136                if ( current_user_can( 'install_plugins' ) ) {
1137                        echo '<p>';
1138                        printf(
1139                                /* translators: %s: A link to install the Classic Editor plugin. */
1140                                __( 'Please install the <a href="%s">Classic Editor plugin</a> to use this meta box.' ),
1141                                esc_url( wp_nonce_url( self_admin_url( 'plugin-install.php?tab=favorites&user=wordpressdotorg&save=0' ), 'save_wporg_username_' . get_current_user_id() ) )
1142                        );
1143                        echo '</p>';
1144                }
1145        } elseif ( is_plugin_inactive( 'classic-editor/classic-editor.php' ) ) {
1146                if ( current_user_can( 'activate_plugins' ) ) {
1147                        $activate_url = wp_nonce_url( self_admin_url( 'plugins.php?action=activate&plugin=classic-editor/classic-editor.php' ), 'activate-plugin_classic-editor/classic-editor.php' );
1148                        echo '<p>';
1149                        /* translators: %s: A link to activate the Classic Editor plugin. */
1150                        printf( __( 'Please activate the <a href="%s">Classic Editor plugin</a> to use this meta box.' ), esc_url( $activate_url ) );
1151                        echo '</p>';
1152                }
1153        } elseif ( $object instanceof WP_Post ) {
1154                $edit_url = add_query_arg(
1155                        array(
1156                                'classic-editor'         => '',
1157                                'classic-editor__forget' => '',
1158                        ),
1159                        get_edit_post_link( $object )
1160                );
1161                echo '<p>';
1162                /* translators: %s: A link to use the Classic Editor plugin. */
1163                printf( __( 'Please open the <a href="%s">classic editor</a> to use this meta box.' ), esc_url( $edit_url ) );
1164                echo '</p>';
1165        }
1166}
1167
1168/**
1169 * Internal helper function to find the plugin from a meta box callback.
1170 *
1171 * @since 5.0.0
1172 *
1173 * @access private
1174 *
1175 * @param callable $callback The callback function to check.
1176 * @return array|null The plugin that the callback belongs to, or null if it doesn't belong to a plugin.
1177 */
1178function _get_plugin_from_callback( $callback ) {
1179        try {
1180                if ( is_array( $callback ) ) {
1181                        $reflection = new ReflectionMethod( $callback[0], $callback[1] );
1182                } elseif ( is_string( $callback ) && false !== strpos( $callback, '::' ) ) {
1183                        $reflection = new ReflectionMethod( $callback );
1184                } else {
1185                        $reflection = new ReflectionFunction( $callback );
1186                }
1187        } catch ( ReflectionException $exception ) {
1188                // We could not properly reflect on the callable, so we abort here.
1189                return null;
1190        }
1191
1192        // Don't show an error if it's an internal PHP function.
1193        if ( ! $reflection->isInternal() ) {
1194
1195                // Only show errors if the meta box was registered by a plugin.
1196                $filename   = wp_normalize_path( $reflection->getFileName() );
1197                $plugin_dir = wp_normalize_path( WP_PLUGIN_DIR );
1198                if ( strpos( $filename, $plugin_dir ) === 0 ) {
1199                        $filename = str_replace( $plugin_dir, '', $filename );
1200                        $filename = preg_replace( '|^/([^/]*/).*$|', '\\1', $filename );
1201
1202                        $plugins = get_plugins();
1203                        foreach ( $plugins as $name => $plugin ) {
1204                                if ( strpos( $name, $filename ) === 0 ) {
1205                                        return $plugin;
1206                                }
1207                        }
1208                }
1209        }
1210
1211        return null;
1212}
1213
1214/**
1215 * Meta-Box template function.
1216 *
1217 * @since 2.5.0
1218 *
1219 * @global array $wp_meta_boxes
1220 *
1221 * @staticvar bool $already_sorted
1222 *
1223 * @param string|WP_Screen $screen  Screen identifier. If you have used add_menu_page() or
1224 *                                  add_submenu_page() to create a new screen (and hence screen_id)
1225 *                                  make sure your menu slug conforms to the limits of sanitize_key()
1226 *                                  otherwise the 'screen' menu may not correctly render on your page.
1227 * @param string           $context The screen context for which to display meta boxes.
1228 * @param mixed            $object  Gets passed to the first parameter of the meta box callback function.
1229 *                                  Often this is the object that's the focus of the current screen, for
1230 *                                  example a `WP_Post` or `WP_Comment` object.
1231 * @return int number of meta_boxes
1232 */
1233function do_meta_boxes( $screen, $context, $object ) {
1234        global $wp_meta_boxes;
1235        static $already_sorted = false;
1236
1237        if ( empty( $screen ) ) {
1238                $screen = get_current_screen();
1239        } elseif ( is_string( $screen ) ) {
1240                $screen = convert_to_screen( $screen );
1241        }
1242
1243        $page = $screen->id;
1244
1245        $hidden = get_hidden_meta_boxes( $screen );
1246
1247        printf( '<div id="%s-sortables" class="meta-box-sortables">', esc_attr( $context ) );
1248
1249        // Grab the ones the user has manually sorted.
1250        // Pull them out of their previous context/priority and into the one the user chose.
1251        $sorted = get_user_option( "meta-box-order_$page" );
1252        if ( ! $already_sorted && $sorted ) {
1253                foreach ( $sorted as $box_context => $ids ) {
1254                        foreach ( explode( ',', $ids ) as $id ) {
1255                                if ( $id && 'dashboard_browser_nag' !== $id ) {
1256                                        add_meta_box( $id, null, null, $screen, $box_context, 'sorted' );
1257                                }
1258                        }
1259                }
1260        }
1261
1262        $already_sorted = true;
1263
1264        $i = 0;
1265
1266        if ( isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
1267                foreach ( array( 'high', 'sorted', 'core', 'default', 'low' ) as $priority ) {
1268                        if ( isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) {
1269                                foreach ( (array) $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) {
1270                                        if ( false == $box || ! $box['title'] ) {
1271                                                continue;
1272                                        }
1273
1274                                        $block_compatible = true;
1275                                        if ( is_array( $box['args'] ) ) {
1276                                                // If a meta box is just here for back compat, don't show it in the block editor.
1277                                                if ( $screen->is_block_editor() && isset( $box['args']['__back_compat_meta_box'] ) && $box['args']['__back_compat_meta_box'] ) {
1278                                                        continue;
1279                                                }
1280
1281                                                if ( isset( $box['args']['__block_editor_compatible_meta_box'] ) ) {
1282                                                        $block_compatible = (bool) $box['args']['__block_editor_compatible_meta_box'];
1283                                                        unset( $box['args']['__block_editor_compatible_meta_box'] );
1284                                                }
1285
1286                                                // If the meta box is declared as incompatible with the block editor, override the callback function.
1287                                                if ( ! $block_compatible && $screen->is_block_editor() ) {
1288                                                        $box['old_callback'] = $box['callback'];
1289                                                        $box['callback']     = 'do_block_editor_incompatible_meta_box';
1290                                                }
1291
1292                                                if ( isset( $box['args']['__back_compat_meta_box'] ) ) {
1293                                                        $block_compatible = $block_compatible || (bool) $box['args']['__back_compat_meta_box'];
1294                                                        unset( $box['args']['__back_compat_meta_box'] );
1295                                                }
1296                                        }
1297
1298                                        $i++;
1299                                        // get_hidden_meta_boxes() doesn't apply in the block editor.
1300                                        $hidden_class = ( ! $screen->is_block_editor() && in_array( $box['id'], $hidden ) ) ? ' hide-if-js' : '';
1301                                        echo '<div id="' . $box['id'] . '" class="postbox ' . postbox_classes( $box['id'], $page ) . $hidden_class . '" ' . '>' . "\n";
1302                                        if ( 'dashboard_browser_nag' != $box['id'] ) {
1303                                                $widget_title = $box['title'];
1304
1305                                                if ( is_array( $box['args'] ) && isset( $box['args']['__widget_basename'] ) ) {
1306                                                        $widget_title = $box['args']['__widget_basename'];
1307                                                        // Do not pass this parameter to the user callback function.
1308                                                        unset( $box['args']['__widget_basename'] );
1309                                                }
1310
1311                                                echo '<button type="button" class="handlediv" aria-expanded="true">';
1312                                                echo '<span class="screen-reader-text">' . sprintf(
1313                                                        /* translators: %s: Meta box title. */
1314                                                        __( 'Toggle panel: %s' ),
1315                                                        $widget_title
1316                                                ) . '</span>';
1317                                                echo '<span class="toggle-indicator" aria-hidden="true"></span>';
1318                                                echo '</button>';
1319                                        }
1320                                        echo '<h2 class="hndle">';
1321                                        if ( 'dashboard_php_nag' === $box['id'] ) {
1322                                                echo '<span aria-hidden="true" class="dashicons dashicons-warning"></span>';
1323                                                echo '<span class="screen-reader-text">' . __( 'Warning:' ) . ' </span>';
1324                                        }
1325                                        echo "<span>{$box['title']}</span>";
1326                                        echo "</h2>\n";
1327                                        echo '<div class="inside">' . "\n";
1328
1329                                        if ( WP_DEBUG && ! $block_compatible && 'edit' === $screen->parent_base && ! $screen->is_block_editor() && ! isset( $_GET['meta-box-loader'] ) ) {
1330                                                $plugin = _get_plugin_from_callback( $box['callback'] );
1331                                                if ( $plugin ) {
1332                                                        ?>
1333                                                        <div class="error inline">
1334                                                                <p>
1335                                                                        <?php
1336                                                                                /* translators: %s: The name of the plugin that generated this meta box. */
1337                                                                                printf( __( "This meta box, from the %s plugin, isn't compatible with the block editor." ), "<strong>{$plugin['Name']}</strong>" );
1338                                                                        ?>
1339                                                                </p>
1340                                                        </div>
1341                                                        <?php
1342                                                }
1343                                        }
1344
1345                                        call_user_func( $box['callback'], $object, $box );
1346                                        echo "</div>\n";
1347                                        echo "</div>\n";
1348                                }
1349                        }
1350                }
1351        }
1352
1353        echo '</div>';
1354
1355        return $i;
1356
1357}
1358
1359/**
1360 * Removes a meta box from one or more screens.
1361 *
1362 * @since 2.6.0
1363 * @since 4.4.0 The `$screen` parameter now accepts an array of screen IDs.
1364 *
1365 * @global array $wp_meta_boxes
1366 *
1367 * @param string                 $id      Meta box ID (used in the 'id' attribute for the meta box).
1368 * @param string|array|WP_Screen $screen  The screen or screens on which the meta box is shown (such as a
1369 *                                        post type, 'link', or 'comment'). Accepts a single screen ID,
1370 *                                        WP_Screen object, or array of screen IDs.
1371 * @param string                 $context The context within the screen where the box is set to display.
1372 *                                        Contexts vary from screen to screen. Post edit screen contexts
1373 *                                        include 'normal', 'side', and 'advanced'. Comments screen contexts
1374 *                                        include 'normal' and 'side'. Menus meta boxes (accordion sections)
1375 *                                        all use the 'side' context.
1376 */
1377function remove_meta_box( $id, $screen, $context ) {
1378        global $wp_meta_boxes;
1379
1380        if ( empty( $screen ) ) {
1381                $screen = get_current_screen();
1382        } elseif ( is_string( $screen ) ) {
1383                $screen = convert_to_screen( $screen );
1384        } elseif ( is_array( $screen ) ) {
1385                foreach ( $screen as $single_screen ) {
1386                        remove_meta_box( $id, $single_screen, $context );
1387                }
1388        }
1389
1390        if ( ! isset( $screen->id ) ) {
1391                return;
1392        }
1393
1394        $page = $screen->id;
1395
1396        if ( ! isset( $wp_meta_boxes ) ) {
1397                $wp_meta_boxes = array();
1398        }
1399        if ( ! isset( $wp_meta_boxes[ $page ] ) ) {
1400                $wp_meta_boxes[ $page ] = array();
1401        }
1402        if ( ! isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
1403                $wp_meta_boxes[ $page ][ $context ] = array();
1404        }
1405
1406        foreach ( array( 'high', 'core', 'default', 'low' ) as $priority ) {
1407                $wp_meta_boxes[ $page ][ $context ][ $priority ][ $id ] = false;
1408        }
1409}
1410
1411/**
1412 * Meta Box Accordion Template Function.
1413 *
1414 * Largely made up of abstracted code from do_meta_boxes(), this
1415 * function serves to build meta boxes as list items for display as
1416 * a collapsible accordion.
1417 *
1418 * @since 3.6.0
1419 *
1420 * @uses global $wp_meta_boxes Used to retrieve registered meta boxes.
1421 *
1422 * @param string|object $screen  The screen identifier.
1423 * @param string        $context The meta box context.
1424 * @param mixed         $object  gets passed to the section callback function as first parameter.
1425 * @return int number of meta boxes as accordion sections.
1426 */
1427function do_accordion_sections( $screen, $context, $object ) {
1428        global $wp_meta_boxes;
1429
1430        wp_enqueue_script( 'accordion' );
1431
1432        if ( empty( $screen ) ) {
1433                $screen = get_current_screen();
1434        } elseif ( is_string( $screen ) ) {
1435                $screen = convert_to_screen( $screen );
1436        }
1437
1438        $page = $screen->id;
1439
1440        $hidden = get_hidden_meta_boxes( $screen );
1441        ?>
1442        <div id="side-sortables" class="accordion-container">
1443                <ul class="outer-border">
1444        <?php
1445        $i          = 0;
1446        $first_open = false;
1447
1448        if ( isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
1449                foreach ( array( 'high', 'core', 'default', 'low' ) as $priority ) {
1450                        if ( isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) {
1451                                foreach ( $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) {
1452                                        if ( false == $box || ! $box['title'] ) {
1453                                                continue;
1454                                        }
1455                                        $i++;
1456                                        $hidden_class = in_array( $box['id'], $hidden ) ? 'hide-if-js' : '';
1457
1458                                        $open_class = '';
1459                                        if ( ! $first_open && empty( $hidden_class ) ) {
1460                                                $first_open = true;
1461                                                $open_class = 'open';
1462                                        }
1463                                        ?>
1464                                        <li class="control-section accordion-section <?php echo $hidden_class; ?> <?php echo $open_class; ?> <?php echo esc_attr( $box['id'] ); ?>" id="<?php echo esc_attr( $box['id'] ); ?>">
1465                                                <h3 class="accordion-section-title hndle" tabindex="0">
1466                                                        <?php echo esc_html( $box['title'] ); ?>
1467                                                        <span class="screen-reader-text"><?php _e( 'Press return or enter to open this section' ); ?></span>
1468                                                </h3>
1469                                                <div class="accordion-section-content <?php postbox_classes( $box['id'], $page ); ?>">
1470                                                        <div class="inside">
1471                                                                <?php call_user_func( $box['callback'], $object, $box ); ?>
1472                                                        </div><!-- .inside -->
1473                                                </div><!-- .accordion-section-content -->
1474                                        </li><!-- .accordion-section -->
1475                                        <?php
1476                                }
1477                        }
1478                }
1479        }
1480        ?>
1481                </ul><!-- .outer-border -->
1482        </div><!-- .accordion-container -->
1483        <?php
1484        return $i;
1485}
1486
1487/**
1488 * Add a new section to a settings page.
1489 *
1490 * Part of the Settings API. Use this to define new settings sections for an admin page.
1491 * Show settings sections in your admin page callback function with do_settings_sections().
1492 * Add settings fields to your section with add_settings_field().
1493 *
1494 * The $callback argument should be the name of a function that echoes out any
1495 * content you want to show at the top of the settings section before the actual
1496 * fields. It can output nothing if you want.
1497 *
1498 * @since 2.7.0
1499 *
1500 * @global $wp_settings_sections Storage array of all settings sections added to admin pages.
1501 *
1502 * @param string   $id       Slug-name to identify the section. Used in the 'id' attribute of tags.
1503 * @param string   $title    Formatted title of the section. Shown as the heading for the section.
1504 * @param callable $callback Function that echos out any content at the top of the section (between heading and fields).
1505 * @param string   $page     The slug-name of the settings page on which to show the section. Built-in pages include
1506 *                           'general', 'reading', 'writing', 'discussion', 'media', etc. Create your own using
1507 *                           add_options_page();
1508 */
1509function add_settings_section( $id, $title, $callback, $page ) {
1510        global $wp_settings_sections;
1511
1512        if ( 'misc' == $page ) {
1513                _deprecated_argument(
1514                        __FUNCTION__,
1515                        '3.0.0',
1516                        sprintf(
1517                                /* translators: %s: misc */
1518                                __( 'The "%s" options group has been removed. Use another settings group.' ),
1519                                'misc'
1520                        )
1521                );
1522                $page = 'general';
1523        }
1524
1525        if ( 'privacy' == $page ) {
1526                _deprecated_argument(
1527                        __FUNCTION__,
1528                        '3.5.0',
1529                        sprintf(
1530                                /* translators: %s: privacy */
1531                                __( 'The "%s" options group has been removed. Use another settings group.' ),
1532                                'privacy'
1533                        )
1534                );
1535                $page = 'reading';
1536        }
1537
1538        $wp_settings_sections[ $page ][ $id ] = array(
1539                'id'       => $id,
1540                'title'    => $title,
1541                'callback' => $callback,
1542        );
1543}
1544
1545/**
1546 * Add a new field to a section of a settings page.
1547 *
1548 * Part of the Settings API. Use this to define a settings field that will show
1549 * as part of a settings section inside a settings page. The fields are shown using
1550 * do_settings_fields() in do_settings-sections()
1551 *
1552 * The $callback argument should be the name of a function that echoes out the
1553 * html input tags for this setting field. Use get_option() to retrieve existing
1554 * values to show.
1555 *
1556 * @since 2.7.0
1557 * @since 4.2.0 The `$class` argument was added.
1558 *
1559 * @global $wp_settings_fields Storage array of settings fields and info about their pages/sections.
1560 *
1561 * @param string   $id       Slug-name to identify the field. Used in the 'id' attribute of tags.
1562 * @param string   $title    Formatted title of the field. Shown as the label for the field
1563 *                           during output.
1564 * @param callable $callback Function that fills the field with the desired form inputs. The
1565 *                           function should echo its output.
1566 * @param string   $page     The slug-name of the settings page on which to show the section
1567 *                           (general, reading, writing, ...).
1568 * @param string   $section  Optional. The slug-name of the section of the settings page
1569 *                           in which to show the box. Default 'default'.
1570 * @param array    $args {
1571 *     Optional. Extra arguments used when outputting the field.
1572 *
1573 *     @type string $label_for When supplied, the setting title will be wrapped
1574 *                             in a `<label>` element, its `for` attribute populated
1575 *                             with this value.
1576 *     @type string $class     CSS Class to be added to the `<tr>` element when the
1577 *                             field is output.
1578 * }
1579 */
1580function add_settings_field( $id, $title, $callback, $page, $section = 'default', $args = array() ) {
1581        global $wp_settings_fields;
1582
1583        if ( 'misc' == $page ) {
1584                _deprecated_argument(
1585                        __FUNCTION__,
1586                        '3.0.0',
1587                        sprintf(
1588                                /* translators: %s: misc */
1589                                __( 'The "%s" options group has been removed. Use another settings group.' ),
1590                                'misc'
1591                        )
1592                );
1593                $page = 'general';
1594        }
1595
1596        if ( 'privacy' == $page ) {
1597                _deprecated_argument(
1598                        __FUNCTION__,
1599                        '3.5.0',
1600                        sprintf(
1601                                /* translators: %s: privacy */
1602                                __( 'The "%s" options group has been removed. Use another settings group.' ),
1603                                'privacy'
1604                        )
1605                );
1606                $page = 'reading';
1607        }
1608
1609        $wp_settings_fields[ $page ][ $section ][ $id ] = array(
1610                'id'       => $id,
1611                'title'    => $title,
1612                'callback' => $callback,
1613                'args'     => $args,
1614        );
1615}
1616
1617/**
1618 * Prints out all settings sections added to a particular settings page
1619 *
1620 * Part of the Settings API. Use this in a settings page callback function
1621 * to output all the sections and fields that were added to that $page with
1622 * add_settings_section() and add_settings_field()
1623 *
1624 * @global $wp_settings_sections Storage array of all settings sections added to admin pages.
1625 * @global $wp_settings_fields Storage array of settings fields and info about their pages/sections.
1626 * @since 2.7.0
1627 *
1628 * @param string $page The slug name of the page whose settings sections you want to output.
1629 */
1630function do_settings_sections( $page ) {
1631        global $wp_settings_sections, $wp_settings_fields;
1632
1633        if ( ! isset( $wp_settings_sections[ $page ] ) ) {
1634                return;
1635        }
1636
1637        foreach ( (array) $wp_settings_sections[ $page ] as $section ) {
1638                if ( $section['title'] ) {
1639                        echo "<h2>{$section['title']}</h2>\n";
1640                }
1641
1642                if ( $section['callback'] ) {
1643                        call_user_func( $section['callback'], $section );
1644                }
1645
1646                if ( ! isset( $wp_settings_fields ) || ! isset( $wp_settings_fields[ $page ] ) || ! isset( $wp_settings_fields[ $page ][ $section['id'] ] ) ) {
1647                        continue;
1648                }
1649                echo '<table class="form-table" role="presentation">';
1650                do_settings_fields( $page, $section['id'] );
1651                echo '</table>';
1652        }
1653}
1654
1655/**
1656 * Print out the settings fields for a particular settings section.
1657 *
1658 * Part of the Settings API. Use this in a settings page to output
1659 * a specific section. Should normally be called by do_settings_sections()
1660 * rather than directly.
1661 *
1662 * @global $wp_settings_fields Storage array of settings fields and their pages/sections.
1663 *
1664 * @since 2.7.0
1665 *
1666 * @param string $page Slug title of the admin page whose settings fields you want to show.
1667 * @param string $section Slug title of the settings section whose fields you want to show.
1668 */
1669function do_settings_fields( $page, $section ) {
1670        global $wp_settings_fields;
1671
1672        if ( ! isset( $wp_settings_fields[ $page ][ $section ] ) ) {
1673                return;
1674        }
1675
1676        foreach ( (array) $wp_settings_fields[ $page ][ $section ] as $field ) {
1677                $class = '';
1678
1679                if ( ! empty( $field['args']['class'] ) ) {
1680                        $class = ' class="' . esc_attr( $field['args']['class'] ) . '"';
1681                }
1682
1683                echo "<tr{$class}>";
1684
1685                if ( ! empty( $field['args']['label_for'] ) ) {
1686                        echo '<th scope="row"><label for="' . esc_attr( $field['args']['label_for'] ) . '">' . $field['title'] . '</label></th>';
1687                } else {
1688                        echo '<th scope="row">' . $field['title'] . '</th>';
1689                }
1690
1691                echo '<td>';
1692                call_user_func( $field['callback'], $field['args'] );
1693                echo '</td>';
1694                echo '</tr>';
1695        }
1696}
1697
1698/**
1699 * Register a settings error to be displayed to the user.
1700 *
1701 * Part of the Settings API. Use this to show messages to users about settings validation
1702 * problems, missing settings or anything else.
1703 *
1704 * Settings errors should be added inside the $sanitize_callback function defined in
1705 * register_setting() for a given setting to give feedback about the submission.
1706 *
1707 * By default messages will show immediately after the submission that generated the error.
1708 * Additional calls to settings_errors() can be used to show errors even when the settings
1709 * page is first accessed.
1710 *
1711 * @since 3.0.0
1712 * @since 5.3.0 Added `warning` and `info` as possible values for `$type`.
1713 *
1714 * @global array $wp_settings_errors Storage array of errors registered during this pageload
1715 *
1716 * @param string $setting Slug title of the setting to which this error applies.
1717 * @param string $code    Slug-name to identify the error. Used as part of 'id' attribute in HTML output.
1718 * @param string $message The formatted message text to display to the user (will be shown inside styled
1719 *                        `<div>` and `<p>` tags).
1720 * @param string $type    Optional. Message type, controls HTML class. Possible values include 'error',
1721 *                        'success', 'warning', 'info'. Default 'error'.
1722 */
1723function add_settings_error( $setting, $code, $message, $type = 'error' ) {
1724        global $wp_settings_errors;
1725
1726        $wp_settings_errors[] = array(
1727                'setting' => $setting,
1728                'code'    => $code,
1729                'message' => $message,
1730                'type'    => $type,
1731        );
1732}
1733
1734/**
1735 * Fetch settings errors registered by add_settings_error().
1736 *
1737 * Checks the $wp_settings_errors array for any errors declared during the current
1738 * pageload and returns them.
1739 *
1740 * If changes were just submitted ($_GET['settings-updated']) and settings errors were saved
1741 * to the 'settings_errors' transient then those errors will be returned instead. This
1742 * is used to pass errors back across pageloads.
1743 *
1744 * Use the $sanitize argument to manually re-sanitize the option before returning errors.
1745 * This is useful if you have errors or notices you want to show even when the user
1746 * hasn't submitted data (i.e. when they first load an options page, or in the {@see 'admin_notices'}
1747 * action hook).
1748 *
1749 * @since 3.0.0
1750 *
1751 * @global array $wp_settings_errors Storage array of errors registered during this pageload
1752 *
1753 * @param string $setting Optional slug title of a specific setting whose errors you want.
1754 * @param boolean $sanitize Whether to re-sanitize the setting value before returning errors.
1755 * @return array Array of settings errors.
1756 */
1757function get_settings_errors( $setting = '', $sanitize = false ) {
1758        global $wp_settings_errors;
1759
1760        /*
1761         * If $sanitize is true, manually re-run the sanitization for this option
1762         * This allows the $sanitize_callback from register_setting() to run, adding
1763         * any settings errors you want to show by default.
1764         */
1765        if ( $sanitize ) {
1766                sanitize_option( $setting, get_option( $setting ) );
1767        }
1768
1769        // If settings were passed back from options.php then use them.
1770        if ( isset( $_GET['settings-updated'] ) && $_GET['settings-updated'] && get_transient( 'settings_errors' ) ) {
1771                $wp_settings_errors = array_merge( (array) $wp_settings_errors, get_transient( 'settings_errors' ) );
1772                delete_transient( 'settings_errors' );
1773        }
1774
1775        // Check global in case errors have been added on this pageload.
1776        if ( empty( $wp_settings_errors ) ) {
1777                return array();
1778        }
1779
1780        // Filter the results to those of a specific setting if one was set.
1781        if ( $setting ) {
1782                $setting_errors = array();
1783                foreach ( (array) $wp_settings_errors as $key => $details ) {
1784                        if ( $setting == $details['setting'] ) {
1785                                $setting_errors[] = $wp_settings_errors[ $key ];
1786                        }
1787                }
1788                return $setting_errors;
1789        }
1790
1791        return $wp_settings_errors;
1792}
1793
1794/**
1795 * Display settings errors registered by add_settings_error().
1796 *
1797 * Part of the Settings API. Outputs a div for each error retrieved by
1798 * get_settings_errors().
1799 *
1800 * This is called automatically after a settings page based on the
1801 * Settings API is submitted. Errors should be added during the validation
1802 * callback function for a setting defined in register_setting().
1803 *
1804 * The $sanitize option is passed into get_settings_errors() and will
1805 * re-run the setting sanitization
1806 * on its current value.
1807 *
1808 * The $hide_on_update option will cause errors to only show when the settings
1809 * page is first loaded. if the user has already saved new values it will be
1810 * hidden to avoid repeating messages already shown in the default error
1811 * reporting after submission. This is useful to show general errors like
1812 * missing settings when the user arrives at the settings page.
1813 *
1814 * @since 3.0.0
1815 * @since 5.3.0 Legacy `error` and `updated` CSS classes are mapped to
1816 *              `notice-error` and `notice-success`.
1817 *
1818 * @param string $setting        Optional slug title of a specific setting whose errors you want.
1819 * @param bool   $sanitize       Whether to re-sanitize the setting value before returning errors.
1820 * @param bool   $hide_on_update If set to true errors will not be shown if the settings page has
1821 *                               already been submitted.
1822 */
1823function settings_errors( $setting = '', $sanitize = false, $hide_on_update = false ) {
1824
1825        if ( $hide_on_update && ! empty( $_GET['settings-updated'] ) ) {
1826                return;
1827        }
1828
1829        $settings_errors = get_settings_errors( $setting, $sanitize );
1830
1831        if ( empty( $settings_errors ) ) {
1832                return;
1833        }
1834
1835        $output = '';
1836        foreach ( $settings_errors as $key => $details ) {
1837                if ( 'updated' === $details['type'] ) {
1838                        $details['type'] = 'success';
1839                }
1840
1841                if ( in_array( $details['type'], array( 'error', 'success', 'warning', 'info' ) ) ) {
1842                        $details['type'] = 'notice-' . $details['type'];
1843                }
1844
1845                $css_id    = sprintf(
1846                        'setting-error-%s',
1847                        esc_attr( $details['code'] )
1848                );
1849                $css_class = sprintf(
1850                        'notice %s settings-error is-dismissible',
1851                        esc_attr( $details['type'] )
1852                );
1853
1854                $output .= "<div id='$css_id' class='$css_class'> \n";
1855                $output .= "<p><strong>{$details['message']}</strong></p>";
1856                $output .= "</div> \n";
1857        }
1858        echo $output;
1859}
1860
1861/**
1862 * Outputs the modal window used for attaching media to posts or pages in the media-listing screen.
1863 *
1864 * @since 2.7.0
1865 *
1866 * @param string $found_action
1867 */
1868function find_posts_div( $found_action = '' ) {
1869        ?>
1870        <div id="find-posts" class="find-box" style="display: none;">
1871                <div id="find-posts-head" class="find-box-head">
1872                        <?php _e( 'Attach to existing content' ); ?>
1873                        <button type="button" id="find-posts-close"><span class="screen-reader-text"><?php _e( 'Close media attachment panel' ); ?></span></button>
1874                </div>
1875                <div class="find-box-inside">
1876                        <div class="find-box-search">
1877                                <?php if ( $found_action ) { ?>
1878                                        <input type="hidden" name="found_action" value="<?php echo esc_attr( $found_action ); ?>" />
1879                                <?php } ?>
1880                                <input type="hidden" name="affected" id="affected" value="" />
1881                                <?php wp_nonce_field( 'find-posts', '_ajax_nonce', false ); ?>
1882                                <label class="screen-reader-text" for="find-posts-input"><?php _e( 'Search' ); ?></label>
1883                                <input type="text" id="find-posts-input" name="ps" value="" />
1884                                <span class="spinner"></span>
1885                                <input type="button" id="find-posts-search" value="<?php esc_attr_e( 'Search' ); ?>" class="button" />
1886                                <div class="clear"></div>
1887                        </div>
1888                        <div id="find-posts-response"></div>
1889                </div>
1890                <div class="find-box-buttons">
1891                        <?php submit_button( __( 'Select' ), 'primary alignright', 'find-posts-submit', false ); ?>
1892                        <div class="clear"></div>
1893                </div>
1894        </div>
1895        <?php
1896}
1897
1898/**
1899 * Displays the post password.
1900 *
1901 * The password is passed through esc_attr() to ensure that it is safe for placing in an html attribute.
1902 *
1903 * @since 2.7.0
1904 */
1905function the_post_password() {
1906        $post = get_post();
1907        if ( isset( $post->post_password ) ) {
1908                echo esc_attr( $post->post_password );
1909        }
1910}
1911
1912/**
1913 * Get the post title.
1914 *
1915 * The post title is fetched and if it is blank then a default string is
1916 * returned.
1917 *
1918 * @since 2.7.0
1919 *
1920 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
1921 * @return string The post title if set.
1922 */
1923function _draft_or_post_title( $post = 0 ) {
1924        $title = get_the_title( $post );
1925        if ( empty( $title ) ) {
1926                $title = __( '(no title)' );
1927        }
1928        return esc_html( $title );
1929}
1930
1931/**
1932 * Displays the search query.
1933 *
1934 * A simple wrapper to display the "s" parameter in a `GET` URI. This function
1935 * should only be used when the_search_query() cannot.
1936 *
1937 * @since 2.7.0
1938 */
1939function _admin_search_query() {
1940        echo isset( $_REQUEST['s'] ) ? esc_attr( wp_unslash( $_REQUEST['s'] ) ) : '';
1941}
1942
1943/**
1944 * Generic Iframe header for use with Thickbox
1945 *
1946 * @since 2.7.0
1947 *
1948 * @global string    $hook_suffix
1949 * @global string    $admin_body_class
1950 * @global WP_Locale $wp_locale        WordPress date and time locale object.
1951 *
1952 * @param string $title      Optional. Title of the Iframe page. Default empty.
1953 * @param bool   $deprecated Not used.
1954 */
1955function iframe_header( $title = '', $deprecated = false ) {
1956        show_admin_bar( false );
1957        global $hook_suffix, $admin_body_class, $wp_locale;
1958        $admin_body_class = preg_replace( '/[^a-z0-9_-]+/i', '-', $hook_suffix );
1959
1960        $current_screen = get_current_screen();
1961
1962        header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) );
1963        _wp_admin_html_begin();
1964        ?>
1965<title><?php bloginfo( 'name' ); ?> &rsaquo; <?php echo $title; ?> &#8212; <?php _e( 'WordPress' ); ?></title>
1966        <?php
1967        wp_enqueue_style( 'colors' );
1968        ?>
1969<script type="text/javascript">
1970addLoadEvent = function(func){if(typeof jQuery!="undefined")jQuery(document).ready(func);else if(typeof wpOnload!='function'){wpOnload=func;}else{var oldonload=wpOnload;wpOnload=function(){oldonload();func();}}};
1971function tb_close(){var win=window.dialogArguments||opener||parent||top;win.tb_remove();}
1972var ajaxurl = '<?php echo admin_url( 'admin-ajax.php', 'relative' ); ?>',
1973        pagenow = '<?php echo $current_screen->id; ?>',
1974        typenow = '<?php echo $current_screen->post_type; ?>',
1975        adminpage = '<?php echo $admin_body_class; ?>',
1976        thousandsSeparator = '<?php echo addslashes( $wp_locale->number_format['thousands_sep'] ); ?>',
1977        decimalPoint = '<?php echo addslashes( $wp_locale->number_format['decimal_point'] ); ?>',
1978        isRtl = <?php echo (int) is_rtl(); ?>;
1979</script>
1980        <?php
1981        /** This action is documented in wp-admin/admin-header.php */
1982        do_action( 'admin_enqueue_scripts', $hook_suffix );
1983
1984        /** This action is documented in wp-admin/admin-header.php */
1985        do_action( "admin_print_styles-{$hook_suffix}" );  // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
1986
1987        /** This action is documented in wp-admin/admin-header.php */
1988        do_action( 'admin_print_styles' );
1989
1990        /** This action is documented in wp-admin/admin-header.php */
1991        do_action( "admin_print_scripts-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
1992
1993        /** This action is documented in wp-admin/admin-header.php */
1994        do_action( 'admin_print_scripts' );
1995
1996        /** This action is documented in wp-admin/admin-header.php */
1997        do_action( "admin_head-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
1998
1999        /** This action is documented in wp-admin/admin-header.php */
2000        do_action( 'admin_head' );
2001
2002        $admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_user_locale() ) ) );
2003
2004        if ( is_rtl() ) {
2005                $admin_body_class .= ' rtl';
2006        }
2007
2008        ?>
2009</head>
2010        <?php
2011        /**
2012         * @global string $body_id
2013         */
2014        $admin_body_id = isset( $GLOBALS['body_id'] ) ? 'id="' . $GLOBALS['body_id'] . '" ' : '';
2015
2016        /** This filter is documented in wp-admin/admin-header.php */
2017        $admin_body_classes = apply_filters( 'admin_body_class', '' );
2018        $admin_body_classes = ltrim( $admin_body_classes . ' ' . $admin_body_class );
2019        ?>
2020<body <?php echo $admin_body_id; ?>class="wp-admin wp-core-ui no-js iframe <?php echo $admin_body_classes; ?>">
2021<script type="text/javascript">
2022(function(){
2023var c = document.body.className;
2024c = c.replace(/no-js/, 'js');
2025document.body.className = c;
2026})();
2027</script>
2028        <?php
2029}
2030
2031/**
2032 * Generic Iframe footer for use with Thickbox
2033 *
2034 * @since 2.7.0
2035 */
2036function iframe_footer() {
2037        /*
2038         * We're going to hide any footer output on iFrame pages,
2039         * but run the hooks anyway since they output JavaScript
2040         * or other needed content.
2041         */
2042
2043        /**
2044         * @global string $hook_suffix
2045         */
2046        global $hook_suffix;
2047        ?>
2048        <div class="hidden">
2049        <?php
2050        /** This action is documented in wp-admin/admin-footer.php */
2051        do_action( 'admin_footer', $hook_suffix );
2052
2053        /** This action is documented in wp-admin/admin-footer.php */
2054        do_action( "admin_print_footer_scripts-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
2055
2056        /** This action is documented in wp-admin/admin-footer.php */
2057        do_action( 'admin_print_footer_scripts' );
2058        ?>
2059        </div>
2060<script type="text/javascript">if(typeof wpOnload=="function")wpOnload();</script>
2061</body>
2062</html>
2063        <?php
2064}
2065
2066/**
2067 * Function to echo or return the post states as HTML.
2068 *
2069 * @since 2.7.0
2070 * @since 5.3.0 Added the `$echo` parameter and a return value.
2071 *
2072 * @see get_post_states()
2073 *
2074 * @param WP_Post $post The post to retrieve states for.
2075 * @param bool    $echo Optional. Whether to echo the post states as an HTML string. Default true.
2076 * @return string Post states string.
2077 */
2078function _post_states( $post, $echo = true ) {
2079        $post_states        = get_post_states( $post );
2080        $post_states_string = '';
2081
2082        if ( ! empty( $post_states ) ) {
2083                $state_count = count( $post_states );
2084                $i           = 0;
2085
2086                $post_states_string .= ' &mdash; ';
2087                foreach ( $post_states as $state ) {
2088                        ++$i;
2089                        ( $i == $state_count ) ? $sep = '' : $sep = ', ';
2090                        $post_states_string          .= "<span class='post-state'>$state$sep</span>";
2091                }
2092        }
2093
2094        if ( $echo ) {
2095                echo $post_states_string;
2096        }
2097
2098        return $post_states_string;
2099}
2100
2101/**
2102 * Retrieves an array of post states from a post.
2103 *
2104 * @since 5.3.0
2105 *
2106 * @param WP_Post $post The post to retrieve states for.
2107 * @return string[] Array of post state labels keyed by their state.
2108 */
2109function get_post_states( $post ) {
2110        $post_states = array();
2111        if ( isset( $_REQUEST['post_status'] ) ) {
2112                $post_status = $_REQUEST['post_status'];
2113        } else {
2114                $post_status = '';
2115        }
2116
2117        if ( ! empty( $post->post_password ) ) {
2118                $post_states['protected'] = _x( 'Password protected', 'post status' );
2119        }
2120
2121        if ( 'private' == $post->post_status && 'private' != $post_status ) {
2122                $post_states['private'] = _x( 'Private', 'post status' );
2123        }
2124
2125        if ( 'draft' === $post->post_status ) {
2126                if ( get_post_meta( $post->ID, '_customize_changeset_uuid', true ) ) {
2127                        $post_states[] = __( 'Customization Draft' );
2128                } elseif ( 'draft' !== $post_status ) {
2129                        $post_states['draft'] = _x( 'Draft', 'post status' );
2130                }
2131        } elseif ( 'trash' === $post->post_status && get_post_meta( $post->ID, '_customize_changeset_uuid', true ) ) {
2132                $post_states[] = _x( 'Customization Draft', 'post status' );
2133        }
2134
2135        if ( 'pending' == $post->post_status && 'pending' != $post_status ) {
2136                $post_states['pending'] = _x( 'Pending', 'post status' );
2137        }
2138
2139        if ( is_sticky( $post->ID ) ) {
2140                $post_states['sticky'] = _x( 'Sticky', 'post status' );
2141        }
2142
2143        if ( 'future' === $post->post_status ) {
2144                $post_states['scheduled'] = _x( 'Scheduled', 'post status' );
2145        }
2146
2147        if ( 'page' === get_option( 'show_on_front' ) ) {
2148                if ( intval( get_option( 'page_on_front' ) ) === $post->ID ) {
2149                        $post_states['page_on_front'] = _x( 'Front Page', 'page label' );
2150                }
2151
2152                if ( intval( get_option( 'page_for_posts' ) ) === $post->ID ) {
2153                        $post_states['page_for_posts'] = _x( 'Posts Page', 'page label' );
2154                }
2155        }
2156
2157        if ( intval( get_option( 'wp_page_for_privacy_policy' ) ) === $post->ID ) {
2158                $post_states['page_for_privacy_policy'] = _x( 'Privacy Policy Page', 'page label' );
2159        }
2160
2161        /**
2162         * Filters the default post display states used in the posts list table.
2163         *
2164         * @since 2.8.0
2165         * @since 3.6.0 Added the `$post` parameter.
2166         *
2167         * @param string[] $post_states An array of post display states.
2168         * @param WP_Post  $post        The current post object.
2169         */
2170        return apply_filters( 'display_post_states', $post_states, $post );
2171}
2172
2173/**
2174 * Function to echo the attachment media states as HTML.
2175 *
2176 * @since 3.2.0
2177 *
2178 * @param WP_Post $post The attachment post to retrieve states for.
2179 * @return string Media states string.
2180 */
2181function _media_states( $post ) {
2182        $media_states = array();
2183        $stylesheet   = get_option( 'stylesheet' );
2184
2185        if ( current_theme_supports( 'custom-header' ) ) {
2186                $meta_header = get_post_meta( $post->ID, '_wp_attachment_is_custom_header', true );
2187
2188                if ( is_random_header_image() ) {
2189                        $header_images = wp_list_pluck( get_uploaded_header_images(), 'attachment_id' );
2190
2191                        if ( $meta_header == $stylesheet && in_array( $post->ID, $header_images ) ) {
2192                                $media_states[] = __( 'Header Image' );
2193                        }
2194                } else {
2195                        $header_image = get_header_image();
2196
2197                        // Display "Header Image" if the image was ever used as a header image.
2198                        if ( ! empty( $meta_header ) && $meta_header === $stylesheet && wp_get_attachment_url( $post->ID ) !== $header_image ) {
2199                                $media_states[] = __( 'Header Image' );
2200                        }
2201
2202                        // Display "Current Header Image" if the image is currently the header image.
2203                        if ( $header_image && wp_get_attachment_url( $post->ID ) === $header_image ) {
2204                                $media_states[] = __( 'Current Header Image' );
2205                        }
2206                }
2207        }
2208
2209        if ( current_theme_supports( 'custom-background' ) ) {
2210                $meta_background = get_post_meta( $post->ID, '_wp_attachment_is_custom_background', true );
2211
2212                if ( ! empty( $meta_background ) && $meta_background === $stylesheet ) {
2213                        $media_states[] = __( 'Background Image' );
2214
2215                        $background_image = get_background_image();
2216                        if ( $background_image && wp_get_attachment_url( $post->ID ) === $background_image ) {
2217                                $media_states[] = __( 'Current Background Image' );
2218                        }
2219                }
2220        }
2221
2222        if ( get_option( 'site_icon' ) == $post->ID ) {
2223                $media_states[] = __( 'Site Icon' );
2224        }
2225
2226        if ( get_theme_mod( 'custom_logo' ) == $post->ID ) {
2227                $media_states[] = __( 'Logo' );
2228        }
2229
2230        /**
2231         * Filters the default media display states for items in the Media list table.
2232         *
2233         * @since 3.2.0
2234         * @since 4.8.0 Added the `$post` parameter.
2235         *
2236         * @param string[] $media_states An array of media states. Default 'Header Image',
2237         *                               'Background Image', 'Site Icon', 'Logo'.
2238         * @param WP_Post  $post         The current attachment object.
2239         */
2240        $media_states = apply_filters( 'display_media_states', $media_states, $post );
2241
2242        if ( ! empty( $media_states ) ) {
2243                $state_count = count( $media_states );
2244                $i           = 0;
2245                echo ' &mdash; ';
2246                foreach ( $media_states as $state ) {
2247                        ++$i;
2248                        ( $i == $state_count ) ? $sep = '' : $sep = ', ';
2249                        echo "<span class='post-state'>$state$sep</span>";
2250                }
2251        }
2252}
2253
2254/**
2255 * Test support for compressing JavaScript from PHP
2256 *
2257 * Outputs JavaScript that tests if compression from PHP works as expected
2258 * and sets an option with the result. Has no effect when the current user
2259 * is not an administrator. To run the test again the option 'can_compress_scripts'
2260 * has to be deleted.
2261 *
2262 * @since 2.8.0
2263 */
2264function compression_test() {
2265        ?>
2266        <script type="text/javascript">
2267        var compressionNonce = <?php echo wp_json_encode( wp_create_nonce( 'update_can_compress_scripts' ) ); ?>;
2268        var testCompression = {
2269                get : function(test) {
2270                        var x;
2271                        if ( window.XMLHttpRequest ) {
2272                                x = new XMLHttpRequest();
2273                        } else {
2274                                try{x=new ActiveXObject('Msxml2.XMLHTTP');}catch(e){try{x=new ActiveXObject('Microsoft.XMLHTTP');}catch(e){};}
2275                        }
2276
2277                        if (x) {
2278                                x.onreadystatechange = function() {
2279                                        var r, h;
2280                                        if ( x.readyState == 4 ) {
2281                                                r = x.responseText.substr(0, 18);
2282                                                h = x.getResponseHeader('Content-Encoding');
2283                                                testCompression.check(r, h, test);
2284                                        }
2285                                };
2286
2287                                x.open('GET', ajaxurl + '?action=wp-compression-test&test='+test+'&_ajax_nonce='+compressionNonce+'&'+(new Date()).getTime(), true);
2288                                x.send('');
2289                        }
2290                },
2291
2292                check : function(r, h, test) {
2293                        if ( ! r && ! test )
2294                                this.get(1);
2295
2296                        if ( 1 == test ) {
2297                                if ( h && ( h.match(/deflate/i) || h.match(/gzip/i) ) )
2298                                        this.get('no');
2299                                else
2300                                        this.get(2);
2301
2302                                return;
2303                        }
2304
2305                        if ( 2 == test ) {
2306                                if ( '"wpCompressionTest' == r )
2307                                        this.get('yes');
2308                                else
2309                                        this.get('no');
2310                        }
2311                }
2312        };
2313        testCompression.check();
2314        </script>
2315        <?php
2316}
2317
2318/**
2319 * Echoes a submit button, with provided text and appropriate class(es).
2320 *
2321 * @since 3.1.0
2322 *
2323 * @see get_submit_button()
2324 *
2325 * @param string       $text             The text of the button (defaults to 'Save Changes')
2326 * @param string       $type             Optional. The type and CSS class(es) of the button. Core values
2327 *                                       include 'primary', 'small', and 'large'. Default 'primary'.
2328 * @param string       $name             The HTML name of the submit button. Defaults to "submit". If no
2329 *                                       id attribute is given in $other_attributes below, $name will be
2330 *                                       used as the button's id.
2331 * @param bool         $wrap             True if the output button should be wrapped in a paragraph tag,
2332 *                                       false otherwise. Defaults to true.
2333 * @param array|string $other_attributes Other attributes that should be output with the button, mapping
2334 *                                       attributes to their values, such as setting tabindex to 1, etc.
2335 *                                       These key/value attribute pairs will be output as attribute="value",
2336 *                                       where attribute is the key. Other attributes can also be provided
2337 *                                       as a string such as 'tabindex="1"', though the array format is
2338 *                                       preferred. Default null.
2339 */
2340function submit_button( $text = null, $type = 'primary', $name = 'submit', $wrap = true, $other_attributes = null ) {
2341        echo get_submit_button( $text, $type, $name, $wrap, $other_attributes );
2342}
2343
2344/**
2345 * Returns a submit button, with provided text and appropriate class
2346 *
2347 * @since 3.1.0
2348 *
2349 * @param string       $text             Optional. The text of the button. Default 'Save Changes'.
2350 * @param string       $type             Optional. The type and CSS class(es) of the button. Core values
2351 *                                       include 'primary', 'small', and 'large'. Default 'primary large'.
2352 * @param string       $name             Optional. The HTML name of the submit button. Defaults to "submit".
2353 *                                       If no id attribute is given in $other_attributes below, `$name` will
2354 *                                       be used as the button's id. Default 'submit'.
2355 * @param bool         $wrap             Optional. True if the output button should be wrapped in a paragraph
2356 *                                       tag, false otherwise. Default true.
2357 * @param array|string $other_attributes Optional. Other attributes that should be output with the button,
2358 *                                       mapping attributes to their values, such as `array( 'tabindex' => '1' )`.
2359 *                                       These attributes will be output as `attribute="value"`, such as
2360 *                                       `tabindex="1"`. Other attributes can also be provided as a string such
2361 *                                       as `tabindex="1"`, though the array format is typically cleaner.
2362 *                                       Default empty.
2363 * @return string Submit button HTML.
2364 */
2365function get_submit_button( $text = '', $type = 'primary large', $name = 'submit', $wrap = true, $other_attributes = '' ) {
2366        if ( ! is_array( $type ) ) {
2367                $type = explode( ' ', $type );
2368        }
2369
2370        $button_shorthand = array( 'primary', 'small', 'large' );
2371        $classes          = array( 'button' );
2372        foreach ( $type as $t ) {
2373                if ( 'secondary' === $t || 'button-secondary' === $t ) {
2374                        continue;
2375                }
2376                $classes[] = in_array( $t, $button_shorthand ) ? 'button-' . $t : $t;
2377        }
2378        // Remove empty items, remove duplicate items, and finally build a string.
2379        $class = implode( ' ', array_unique( array_filter( $classes ) ) );
2380
2381        $text = $text ? $text : __( 'Save Changes' );
2382
2383        // Default the id attribute to $name unless an id was specifically provided in $other_attributes.
2384        $id = $name;
2385        if ( is_array( $other_attributes ) && isset( $other_attributes['id'] ) ) {
2386                $id = $other_attributes['id'];
2387                unset( $other_attributes['id'] );
2388        }
2389
2390        $attributes = '';
2391        if ( is_array( $other_attributes ) ) {
2392                foreach ( $other_attributes as $attribute => $value ) {
2393                        $attributes .= $attribute . '="' . esc_attr( $value ) . '" '; // Trailing space is important.
2394                }
2395        } elseif ( ! empty( $other_attributes ) ) { // Attributes provided as a string.
2396                $attributes = $other_attributes;
2397        }
2398
2399        // Don't output empty name and id attributes.
2400        $name_attr = $name ? ' name="' . esc_attr( $name ) . '"' : '';
2401        $id_attr   = $id ? ' id="' . esc_attr( $id ) . '"' : '';
2402
2403        $button  = '<input type="submit"' . $name_attr . $id_attr . ' class="' . esc_attr( $class );
2404        $button .= '" value="' . esc_attr( $text ) . '" ' . $attributes . ' />';
2405
2406        if ( $wrap ) {
2407                $button = '<p class="submit">' . $button . '</p>';
2408        }
2409
2410        return $button;
2411}
2412
2413/**
2414 * @global bool $is_IE
2415 */
2416function _wp_admin_html_begin() {
2417        global $is_IE;
2418
2419        $admin_html_class = ( is_admin_bar_showing() ) ? 'wp-toolbar' : '';
2420
2421        if ( $is_IE ) {
2422                header( 'X-UA-Compatible: IE=edge' );
2423        }
2424
2425        ?>
2426<!DOCTYPE html>
2427<!--[if IE 8]>
2428<html xmlns="http://www.w3.org/1999/xhtml" class="ie8 <?php echo $admin_html_class; ?>"
2429        <?php
2430        /**
2431         * Fires inside the HTML tag in the admin header.
2432         *
2433         * @since 2.2.0
2434         */
2435        do_action( 'admin_xml_ns' );
2436
2437        language_attributes();
2438        ?>
2439        >
2440<![endif]-->
2441<!--[if !(IE 8) ]><!-->
2442<html xmlns="http://www.w3.org/1999/xhtml" class="<?php echo $admin_html_class; ?>"
2443        <?php
2444        /** This action is documented in wp-admin/includes/template.php */
2445        do_action( 'admin_xml_ns' );
2446
2447        language_attributes();
2448        ?>
2449        >
2450<!--<![endif]-->
2451<head>
2452<meta http-equiv="Content-Type" content="<?php bloginfo( 'html_type' ); ?>; charset=<?php echo get_option( 'blog_charset' ); ?>" />
2453        <?php
2454}
2455
2456/**
2457 * Convert a screen string to a screen object
2458 *
2459 * @since 3.0.0
2460 *
2461 * @param string $hook_name The hook name (also known as the hook suffix) used to determine the screen.
2462 * @return WP_Screen Screen object.
2463 */
2464function convert_to_screen( $hook_name ) {
2465        if ( ! class_exists( 'WP_Screen' ) ) {
2466                _doing_it_wrong(
2467                        'convert_to_screen(), add_meta_box()',
2468                        sprintf(
2469                                /* translators: 1: wp-admin/includes/template.php, 2: add_meta_box(), 3: add_meta_boxes */
2470                                __( 'Likely direct inclusion of %1$s in order to use %2$s. This is very wrong. Hook the %2$s call into the %3$s action instead.' ),
2471                                '<code>wp-admin/includes/template.php</code>',
2472                                '<code>add_meta_box()</code>',
2473                                '<code>add_meta_boxes</code>'
2474                        ),
2475                        '3.3.0'
2476                );
2477                return (object) array(
2478                        'id'   => '_invalid',
2479                        'base' => '_are_belong_to_us',
2480                );
2481        }
2482
2483        return WP_Screen::get( $hook_name );
2484}
2485
2486/**
2487 * Output the HTML for restoring the post data from DOM storage
2488 *
2489 * @since 3.6.0
2490 * @access private
2491 */
2492function _local_storage_notice() {
2493        ?>
2494        <div id="local-storage-notice" class="hidden notice is-dismissible">
2495        <p class="local-restore">
2496                <?php _e( 'The backup of this post in your browser is different from the version below.' ); ?>
2497                <button type="button" class="button restore-backup"><?php _e( 'Restore the backup' ); ?></button>
2498        </p>
2499        <p class="help">
2500                <?php _e( 'This will replace the current editor content with the last backup version. You can use undo and redo in the editor to get the old content back or to return to the restored version.' ); ?>
2501        </p>
2502        </div>
2503        <?php
2504}
2505
2506/**
2507 * Output a HTML element with a star rating for a given rating.
2508 *
2509 * Outputs a HTML element with the star rating exposed on a 0..5 scale in
2510 * half star increments (ie. 1, 1.5, 2 stars). Optionally, if specified, the
2511 * number of ratings may also be displayed by passing the $number parameter.
2512 *
2513 * @since 3.8.0
2514 * @since 4.4.0 Introduced the `echo` parameter.
2515 *
2516 * @param array $args {
2517 *     Optional. Array of star ratings arguments.
2518 *
2519 *     @type int|float $rating The rating to display, expressed in either a 0.5 rating increment,
2520 *                             or percentage. Default 0.
2521 *     @type string    $type   Format that the $rating is in. Valid values are 'rating' (default),
2522 *                             or, 'percent'. Default 'rating'.
2523 *     @type int       $number The number of ratings that makes up this rating. Default 0.
2524 *     @type bool      $echo   Whether to echo the generated markup. False to return the markup instead
2525 *                             of echoing it. Default true.
2526 * }
2527 * @return string Star rating HTML.
2528 */
2529function wp_star_rating( $args = array() ) {
2530        $defaults    = array(
2531                'rating' => 0,
2532                'type'   => 'rating',
2533                'number' => 0,
2534                'echo'   => true,
2535        );
2536        $parsed_args = wp_parse_args( $args, $defaults );
2537
2538        // Non-English decimal places when the $rating is coming from a string.
2539        $rating = (float) str_replace( ',', '.', $parsed_args['rating'] );
2540
2541        // Convert percentage to star rating, 0..5 in .5 increments.
2542        if ( 'percent' === $parsed_args['type'] ) {
2543                $rating = round( $rating / 10, 0 ) / 2;
2544        }
2545
2546        // Calculate the number of each type of star needed.
2547        $full_stars  = floor( $rating );
2548        $half_stars  = ceil( $rating - $full_stars );
2549        $empty_stars = 5 - $full_stars - $half_stars;
2550
2551        if ( $parsed_args['number'] ) {
2552                /* translators: 1: The rating, 2: The number of ratings. */
2553                $format = _n( '%1$s rating based on %2$s rating', '%1$s rating based on %2$s ratings', $parsed_args['number'] );
2554                $title  = sprintf( $format, number_format_i18n( $rating, 1 ), number_format_i18n( $parsed_args['number'] ) );
2555        } else {
2556                /* translators: %s: The rating. */
2557                $title = sprintf( __( '%s rating' ), number_format_i18n( $rating, 1 ) );
2558        }
2559
2560        $output  = '<div class="star-rating">';
2561        $output .= '<span class="screen-reader-text">' . $title . '</span>';
2562        $output .= str_repeat( '<div class="star star-full" aria-hidden="true"></div>', $full_stars );
2563        $output .= str_repeat( '<div class="star star-half" aria-hidden="true"></div>', $half_stars );
2564        $output .= str_repeat( '<div class="star star-empty" aria-hidden="true"></div>', $empty_stars );
2565        $output .= '</div>';
2566
2567        if ( $parsed_args['echo'] ) {
2568                echo $output;
2569        }
2570
2571        return $output;
2572}
2573
2574/**
2575 * Output a notice when editing the page for posts (internal use only).
2576 *
2577 * @ignore
2578 * @since 4.2.0
2579 */
2580function _wp_posts_page_notice() {
2581        echo '<div class="notice notice-warning inline"><p>' . __( 'You are currently editing the page that shows your latest posts.' ) . '</p></div>';
2582}