WordPress 4.7
[autoinstalls/wordpress.git] / wp-admin / includes / template.php
index a0b248f922a3b6a0ae902646994bb5cfac928763..edf9a8641c3902bbdbc2e032827e8fde36dc7b6b 100644 (file)
  * @subpackage Administration
  */
 
+/** Walker_Category_Checklist class */
+require_once( ABSPATH . 'wp-admin/includes/class-walker-category-checklist.php' );
+
+/** WP_Internal_Pointers class */
+require_once( ABSPATH . 'wp-admin/includes/class-wp-internal-pointers.php' );
+
 //
 // Category Checklists
 //
 
-/**
- * Walker to output an unordered list of category checkbox input elements.
- *
- * @since 2.5.1
- *
- * @see Walker
- * @see wp_category_checklist()
- * @see wp_terms_checklist()
- */
-class Walker_Category_Checklist extends Walker {
-       public $tree_type = 'category';
-       public $db_fields = array ('parent' => 'parent', 'id' => 'term_id'); //TODO: decouple this
-
-       /**
-        * Starts the list before the elements are added.
-        *
-        * @see Walker:start_lvl()
-        *
-        * @since 2.5.1
-        *
-        * @param string $output Passed by reference. Used to append additional content.
-        * @param int    $depth  Depth of category. Used for tab indentation.
-        * @param array  $args   An array of arguments. @see wp_terms_checklist()
-        */
-       public function start_lvl( &$output, $depth = 0, $args = array() ) {
-               $indent = str_repeat("\t", $depth);
-               $output .= "$indent<ul class='children'>\n";
-       }
-
-       /**
-        * Ends the list of after the elements are added.
-        *
-        * @see Walker::end_lvl()
-        *
-        * @since 2.5.1
-        *
-        * @param string $output Passed by reference. Used to append additional content.
-        * @param int    $depth  Depth of category. Used for tab indentation.
-        * @param array  $args   An array of arguments. @see wp_terms_checklist()
-        */
-       public function end_lvl( &$output, $depth = 0, $args = array() ) {
-               $indent = str_repeat("\t", $depth);
-               $output .= "$indent</ul>\n";
-       }
-
-       /**
-        * Start the element output.
-        *
-        * @see Walker::start_el()
-        *
-        * @since 2.5.1
-        *
-        * @param string $output   Passed by reference. Used to append additional content.
-        * @param object $category The current term object.
-        * @param int    $depth    Depth of the term in reference to parents. Default 0.
-        * @param array  $args     An array of arguments. @see wp_terms_checklist()
-        * @param int    $id       ID of the current term.
-        */
-       public function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) {
-               if ( empty( $args['taxonomy'] ) ) {
-                       $taxonomy = 'category';
-               } else {
-                       $taxonomy = $args['taxonomy'];
-               }
-
-               if ( $taxonomy == 'category' ) {
-                       $name = 'post_category';
-               } else {
-                       $name = 'tax_input[' . $taxonomy . ']';
-               }
-
-               $args['popular_cats'] = empty( $args['popular_cats'] ) ? array() : $args['popular_cats'];
-               $class = in_array( $category->term_id, $args['popular_cats'] ) ? ' class="popular-category"' : '';
-
-               $args['selected_cats'] = empty( $args['selected_cats'] ) ? array() : $args['selected_cats'];
-
-               /** This filter is documented in wp-includes/category-template.php */
-               if ( ! empty( $args['list_only'] ) ) {
-                       $aria_cheched = 'false';
-                       $inner_class = 'category';
-
-                       if ( in_array( $category->term_id, $args['selected_cats'] ) ) {
-                               $inner_class .= ' selected';
-                               $aria_cheched = 'true';
-                       }
-
-                       $output .= "\n" . '<li' . $class . '>' .
-                               '<div class="' . $inner_class . '" data-term-id=' . $category->term_id .
-                               ' tabindex="0" role="checkbox" aria-checked="' . $aria_cheched . '">' .
-                               esc_html( apply_filters( 'the_category', $category->name ) ) . '</div>';
-               } else {
-                       $output .= "\n<li id='{$taxonomy}-{$category->term_id}'$class>" .
-                               '<label class="selectit"><input value="' . $category->term_id . '" type="checkbox" name="'.$name.'[]" id="in-'.$taxonomy.'-' . $category->term_id . '"' .
-                               checked( in_array( $category->term_id, $args['selected_cats'] ), true, false ) .
-                               disabled( empty( $args['disabled'] ), false, false ) . ' /> ' .
-                               esc_html( apply_filters( 'the_category', $category->name ) ) . '</label>';
-               }
-       }
-
-       /**
-        * Ends the element output, if needed.
-        *
-        * @see Walker::end_el()
-        *
-        * @since 2.5.1
-        *
-        * @param string $output   Passed by reference. Used to append additional content.
-        * @param object $category The current term object.
-        * @param int    $depth    Depth of the term in reference to parents. Default 0.
-        * @param array  $args     An array of arguments. @see wp_terms_checklist()
-        */
-       public function end_el( &$output, $category, $depth = 0, $args = array() ) {
-               $output .= "</li>\n";
-       }
-}
-
 /**
  * Output an unordered list of checkbox input elements labeled with category names.
  *
@@ -164,6 +54,7 @@ function wp_category_checklist( $post_id = 0, $descendants_and_self = 0, $select
  * Taxonomy-independent version of wp_category_checklist().
  *
  * @since 3.0.0
+ * @since 4.4.0 Introduced the `$echo` argument.
  *
  * @param int          $post_id Optional. Post ID. Default 0.
  * @param array|string $args {
@@ -179,6 +70,8 @@ function wp_category_checklist( $post_id = 0, $descendants_and_self = 0, $select
  *     @type string $taxonomy             Taxonomy to generate the checklist for. Default 'category'.
  *     @type bool   $checked_ontop        Whether to move checked items out of the hierarchy and to
  *                                        the top of the list. Default true.
+ *     @type bool   $echo                 Whether to echo the generated markup. False to return the markup instead
+ *                                        of echoing it. Default true.
  * }
  */
 function wp_terms_checklist( $post_id = 0, $args = array() ) {
@@ -188,11 +81,12 @@ function wp_terms_checklist( $post_id = 0, $args = array() ) {
                'popular_cats' => false,
                'walker' => null,
                'taxonomy' => 'category',
-               'checked_ontop' => true
+               'checked_ontop' => true,
+               'echo' => true,
        );
 
        /**
-        * Filter the taxonomy terms checklist arguments.
+        * Filters the taxonomy terms checklist arguments.
         *
         * @since 3.4.0
         *
@@ -251,12 +145,14 @@ function wp_terms_checklist( $post_id = 0, $args = array() ) {
                $categories = (array) get_terms( $taxonomy, array( 'get' => 'all' ) );
        }
 
+       $output = '';
+
        if ( $r['checked_ontop'] ) {
                // Post process $categories rather than adding an exclude to the get_terms() query to keep the query the same across all posts (for any query cache)
                $checked_categories = array();
                $keys = array_keys( $categories );
 
-               foreach( $keys as $k ) {
+               foreach ( $keys as $k ) {
                        if ( in_array( $categories[$k]->term_id, $args['selected_cats'] ) ) {
                                $checked_categories[] = $categories[$k];
                                unset( $categories[$k] );
@@ -264,10 +160,16 @@ function wp_terms_checklist( $post_id = 0, $args = array() ) {
                }
 
                // Put checked cats on top
-               echo call_user_func_array( array( $walker, 'walk' ), array( $checked_categories, 0, $args ) );
+               $output .= call_user_func_array( array( $walker, 'walk' ), array( $checked_categories, 0, $args ) );
        }
        // Then the rest of them
-       echo call_user_func_array( array( $walker, 'walk' ), array( $categories, 0, $args ) );
+       $output .= call_user_func_array( array( $walker, 'walk' ), array( $categories, 0, $args ) );
+
+       if ( $r['echo'] ) {
+               echo $output;
+       }
+
+       return $output;
 }
 
 /**
@@ -301,7 +203,7 @@ function wp_popular_terms_checklist( $taxonomy, $default = 0, $number = 10, $ech
        $popular_ids = array();
        foreach ( (array) $terms as $term ) {
                $popular_ids[] = $term->term_id;
-               if ( !$echo ) // hack for AJAX use
+               if ( !$echo ) // Hack for Ajax use.
                        continue;
                $id = "popular-$taxonomy-$term->term_id";
                $checked = in_array( $term->term_id, $checked_terms ) ? 'checked="checked"' : '';
@@ -323,7 +225,7 @@ function wp_popular_terms_checklist( $taxonomy, $default = 0, $number = 10, $ech
 }
 
 /**
- * {@internal Missing Short Description}}
+ * Outputs a link category checklist element.
  *
  * @since 2.5.1
  *
@@ -376,8 +278,9 @@ function get_inline_data($post) {
        /** This filter is documented in wp-admin/edit-tag-form.php */
        echo '
 <div class="hidden" id="inline_' . $post->ID . '">
-       <div class="post_title">' . $title . '</div>
-       <div class="post_name">' . apply_filters( 'editable_slug', $post->post_name ) . '</div>
+       <div class="post_title">' . $title . '</div>' .
+       /** This filter is documented in wp-admin/edit-tag-form.php */
+       '<div class="post_name">' . apply_filters( 'editable_slug', $post->post_name, $post ) . '</div>
        <div class="post_author">' . $post->post_author . '</div>
        <div class="comment_status">' . esc_html( $post->comment_status ) . '</div>
        <div class="ping_status">' . esc_html( $post->ping_status ) . '</div>
@@ -390,14 +293,15 @@ function get_inline_data($post) {
        <div class="ss">' . mysql2date( 's', $post->post_date, false ) . '</div>
        <div class="post_password">' . esc_html( $post->post_password ) . '</div>';
 
-       if ( $post_type_object->hierarchical )
+       if ( $post_type_object->hierarchical ) {
                echo '<div class="post_parent">' . $post->post_parent . '</div>';
+       }
 
-       if ( $post->post_type == 'page' )
-               echo '<div class="page_template">' . esc_html( get_post_meta( $post->ID, '_wp_page_template', true ) ) . '</div>';
+       echo '<div class="page_template">' . ( $post->page_template ? esc_html( $post->page_template ) : 'default' ) . '</div>';
 
-       if ( post_type_supports( $post->post_type, 'page-attributes' ) )
+       if ( post_type_supports( $post->post_type, 'page-attributes' ) ) {
                echo '<div class="menu_order">' . $post->menu_order . '</div>';
+       }
 
        $taxonomy_names = get_object_taxonomies( $post->post_type );
        foreach ( $taxonomy_names as $taxonomy_name) {
@@ -408,7 +312,7 @@ function get_inline_data($post) {
                        $terms = get_object_term_cache( $post->ID, $taxonomy_name );
                        if ( false === $terms ) {
                                $terms = wp_get_object_terms( $post->ID, $taxonomy_name );
-                               wp_cache_add( $post->ID, $terms, $taxonomy_name . '_relationships' );
+                               wp_cache_add( $post->ID, wp_list_pluck( $terms, 'term_id' ), $taxonomy_name . '_relationships' );
                        }
                        $term_ids = empty( $terms ) ? array() : wp_list_pluck( $terms, 'term_id' );
 
@@ -416,8 +320,13 @@ function get_inline_data($post) {
 
                } elseif ( $taxonomy->show_ui ) {
 
+                       $terms_to_edit = get_terms_to_edit( $post->ID, $taxonomy_name );
+                       if ( ! is_string( $terms_to_edit ) ) {
+                               $terms_to_edit = '';
+                       }
+
                        echo '<div class="tags_input" id="'.$taxonomy_name.'_'.$post->ID.'">'
-                               . esc_html( str_replace( ',', ', ', get_terms_to_edit( $post->ID, $taxonomy_name ) ) ) . '</div>';
+                               . esc_html( str_replace( ',', ', ', $terms_to_edit ) ) . '</div>';
 
                }
        }
@@ -432,7 +341,7 @@ function get_inline_data($post) {
 }
 
 /**
- * {@internal Missing Short Description}}
+ * Outputs the in-line comment reply-to form in the Comments list table.
  *
  * @since 2.7.0
  *
@@ -446,7 +355,7 @@ function get_inline_data($post) {
 function wp_comment_reply( $position = 1, $checkbox = false, $mode = 'single', $table_row = true ) {
        global $wp_list_table;
        /**
-        * Filter the in-line comment reply-to form output in the Comments
+        * Filters the in-line comment reply-to form output in the Comments
         * list table.
         *
         * Returning a non-empty value here will short-circuit display
@@ -482,16 +391,29 @@ function wp_comment_reply( $position = 1, $checkbox = false, $mode = 'single', $
 <?php else : ?>
 <div id="com-reply" style="display:none;"><div id="replyrow" style="display:none;">
 <?php endif; ?>
-       <div id="replyhead" style="display:none;"><h5><?php _e( 'Reply to Comment' ); ?></h5></div>
-       <div id="addhead" style="display:none;"><h5><?php _e('Add new Comment'); ?></h5></div>
+       <fieldset class="comment-reply">
+       <legend>
+               <span class="hidden" id="editlegend"><?php _e( 'Edit Comment' ); ?></span>
+               <span class="hidden" id="replyhead"><?php _e( 'Reply to Comment' ); ?></span>
+               <span class="hidden" id="addhead"><?php _e( 'Add new Comment' ); ?></span>
+       </legend>
+
+       <div id="replycontainer">
+       <label for="replycontent" class="screen-reader-text"><?php _e( 'Comment' ); ?></label>
+       <?php
+       $quicktags_settings = array( 'buttons' => 'strong,em,link,block,del,ins,img,ul,ol,li,code,close' );
+       wp_editor( '', 'replycontent', array( 'media_buttons' => false, 'tinymce' => false, 'quicktags' => $quicktags_settings ) );
+       ?>
+       </div>
+
        <div id="edithead" style="display:none;">
                <div class="inside">
-               <label for="author"><?php _e('Name') ?></label>
-               <input type="text" name="newcomment_author" size="50" value="" id="author" />
+               <label for="author-name"><?php _e( 'Name' ) ?></label>
+               <input type="text" name="newcomment_author" size="50" value="" id="author-name" />
                </div>
 
                <div class="inside">
-               <label for="author-email"><?php _e('E-mail') ?></label>
+               <label for="author-email"><?php _e('Email') ?></label>
                <input type="text" name="newcomment_author_email" size="50" value="" id="author-email" />
                </div>
 
@@ -499,25 +421,16 @@ function wp_comment_reply( $position = 1, $checkbox = false, $mode = 'single', $
                <label for="author-url"><?php _e('URL') ?></label>
                <input type="text" id="author-url" name="newcomment_author_url" class="code" size="103" value="" />
                </div>
-               <div style="clear:both;"></div>
-       </div>
-
-       <div id="replycontainer">
-       <?php
-       $quicktags_settings = array( 'buttons' => 'strong,em,link,block,del,ins,img,ul,ol,li,code,close' );
-       wp_editor( '', 'replycontent', array( 'media_buttons' => false, 'tinymce' => false, 'quicktags' => $quicktags_settings ) );
-       ?>
        </div>
 
        <p id="replysubmit" class="submit">
-       <a href="#comments-form" class="save button-primary alignright">
+       <a href="#comments-form" class="save button button-primary alignright">
        <span id="addbtn" style="display:none;"><?php _e('Add Comment'); ?></span>
        <span id="savebtn" style="display:none;"><?php _e('Update Comment'); ?></span>
        <span id="replybtn" style="display:none;"><?php _e('Submit Reply'); ?></span></a>
-       <a href="#comments-form" class="cancel button-secondary alignleft"><?php _e('Cancel'); ?></a>
+       <a href="#comments-form" class="cancel button alignleft"><?php _e('Cancel'); ?></a>
        <span class="waiting spinner"></span>
        <span class="error" style="display:none;"></span>
-       <br class="clear" />
        </p>
 
        <input type="hidden" name="action" id="action" value="" />
@@ -532,6 +445,7 @@ function wp_comment_reply( $position = 1, $checkbox = false, $mode = 'single', $
                if ( current_user_can( 'unfiltered_html' ) )
                        wp_nonce_field( 'unfiltered-html-comment', '_wp_unfiltered_html_comment', false );
        ?>
+       </fieldset>
 <?php if ( $table_row ) : ?>
 </td></tr></tbody></table>
 <?php else : ?>
@@ -558,7 +472,7 @@ function wp_comment_trashnotice() {
 }
 
 /**
- * {@internal Missing Short Description}}
+ * Outputs a post's public meta data in the Custom Fields meta box.
  *
  * @since 1.2.0
  *
@@ -601,7 +515,7 @@ function list_meta( $meta ) {
 }
 
 /**
- * {@internal Missing Short Description}}
+ * Outputs a single row of public meta data in the Custom Fields meta box.
  *
  * @since 2.5.0
  *
@@ -660,7 +574,7 @@ function _list_meta_row( $entry, &$count ) {
  *
  * @since 1.2.0
  *
- * @global wpdb $wpdb
+ * @global wpdb $wpdb WordPress database abstraction object.
  *
  * @param WP_Post $post Optional. The post being edited.
  */
@@ -669,21 +583,37 @@ function meta_form( $post = null ) {
        $post = get_post( $post );
 
        /**
-        * Filter the number of custom fields to retrieve for the drop-down
-        * in the Custom Fields meta box.
+        * Filters values for the meta key dropdown in the Custom Fields meta box.
+        *
+        * Returning a non-null value will effectively short-circuit and avoid a
+        * potentially expensive query against postmeta.
         *
-        * @since 2.1.0
+        * @since 4.4.0
         *
-        * @param int $limit Number of custom fields to retrieve. Default 30.
+        * @param array|null $keys Pre-defined meta keys to be used in place of a postmeta query. Default null.
+        * @param WP_Post    $post The current post object.
         */
-       $limit = apply_filters( 'postmeta_form_limit', 30 );
-       $sql = "SELECT DISTINCT meta_key
-               FROM $wpdb->postmeta
-               WHERE meta_key NOT BETWEEN '_' AND '_z'
-               HAVING meta_key NOT LIKE %s
-               ORDER BY meta_key
-               LIMIT %d";
-       $keys = $wpdb->get_col( $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', $limit ) );
+       $keys = apply_filters( 'postmeta_form_keys', null, $post );
+
+       if ( null === $keys ) {
+               /**
+                * Filters the number of custom fields to retrieve for the drop-down
+                * in the Custom Fields meta box.
+                *
+                * @since 2.1.0
+                *
+                * @param int $limit Number of custom fields to retrieve. Default 30.
+                */
+               $limit = apply_filters( 'postmeta_form_limit', 30 );
+               $sql = "SELECT DISTINCT meta_key
+                       FROM $wpdb->postmeta
+                       WHERE meta_key NOT BETWEEN '_' AND '_z'
+                       HAVING meta_key NOT LIKE %s
+                       ORDER BY meta_key
+                       LIMIT %d";
+               $keys = $wpdb->get_col( $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', $limit ) );
+       }
+
        if ( $keys ) {
                natcasesort( $keys );
                $meta_key_input_id = 'metakeyselect';
@@ -728,7 +658,7 @@ function meta_form( $post = null ) {
 
 <tr><td colspan="2">
 <div class="submit">
-<?php submit_button( __( 'Add Custom Field' ), 'secondary', 'addmeta', false, array( 'id' => 'newmeta-submit', 'data-wp-lists' => 'add:the-list:newmeta' ) ); ?>
+<?php submit_button( __( 'Add Custom Field' ), '', 'addmeta', false, array( 'id' => 'newmeta-submit', 'data-wp-lists' => 'add:the-list:newmeta' ) ); ?>
 </div>
 <?php wp_nonce_field( 'add-meta', '_ajax_nonce-add-meta', false ); ?>
 </td></tr>
@@ -742,9 +672,9 @@ function meta_form( $post = null ) {
  * Print out HTML form date elements for editing post or comment publish date.
  *
  * @since 0.71
+ * @since 4.4.0 Converted to use get_comment() instead of the global `$comment`.
  *
- * @global WP_Locale $wp_locale
- * @global object    $comment
+ * @global WP_Locale  $wp_locale
  *
  * @param int|bool $edit      Accepts 1|true for editing the date, 0|false for adding the date.
  * @param int|bool $for_post  Accepts 1|true for applying the date to a post, 0|false for a comment.
@@ -753,7 +683,7 @@ function meta_form( $post = null ) {
  *                            Default 0|false.
  */
 function touch_time( $edit = 1, $for_post = 1, $tab_index = 0, $multi = 0 ) {
-       global $wp_locale, $comment;
+       global $wp_locale;
        $post = get_post();
 
        if ( $for_post )
@@ -767,7 +697,7 @@ function touch_time( $edit = 1, $for_post = 1, $tab_index = 0, $multi = 0 ) {
        // 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 />';
 
        $time_adj = current_time('timestamp');
-       $post_date = ($for_post) ? $post->post_date : $comment->comment_date;
+       $post_date = ($for_post) ? $post->post_date : get_comment()->comment_date;
        $jj = ($edit) ? mysql2date( 'd', $post_date, false ) : gmdate( 'd', $time_adj );
        $mm = ($edit) ? mysql2date( 'm', $post_date, false ) : gmdate( 'm', $time_adj );
        $aa = ($edit) ? mysql2date( 'Y', $post_date, false ) : gmdate( 'Y', $time_adj );
@@ -832,11 +762,13 @@ function touch_time( $edit = 1, $for_post = 1, $tab_index = 0, $multi = 0 ) {
  * Print out option HTML elements for the page templates drop-down.
  *
  * @since 1.5.0
+ * @since 4.7.0 Added the `$post_type` parameter.
  *
- * @param string $default Optional. The template file name. Default empty.
+ * @param string $default   Optional. The template file name. Default empty.
+ * @param string $post_type Optional. Post type to get templates for. Default 'post'.
  */
-function page_template_dropdown( $default = '' ) {
-       $templates = get_page_templates( get_post() );
+function page_template_dropdown( $default = '', $post_type = 'page' ) {
+       $templates = get_page_templates( null, $post_type );
        ksort( $templates );
        foreach ( array_keys( $templates ) as $template ) {
                $selected = selected( $default, $templates[ $template ], false );
@@ -848,18 +780,20 @@ function page_template_dropdown( $default = '' ) {
  * Print out option HTML elements for the page parents drop-down.
  *
  * @since 1.5.0
+ * @since 4.4.0 `$post` argument was added.
  *
- * @global wpdb $wpdb
+ * @global wpdb $wpdb WordPress database abstraction object.
  *
- * @param int $default Optional. The default page ID to be pre-selected. Default 0.
- * @param int $parent  Optional. The parent page ID. Default 0.
- * @param int $level   Optional. Page depth level. Default 0.
+ * @param int         $default Optional. The default page ID to be pre-selected. Default 0.
+ * @param int         $parent  Optional. The parent page ID. Default 0.
+ * @param int         $level   Optional. Page depth level. Default 0.
+ * @param int|WP_Post $post    Post ID or WP_Post object.
  *
  * @return null|false Boolean False if page has no children, otherwise print out html elements
  */
-function parent_dropdown( $default = 0, $parent = 0, $level = 0 ) {
+function parent_dropdown( $default = 0, $parent = 0, $level = 0, $post = null ) {
        global $wpdb;
-       $post = get_post();
+       $post = get_post( $post );
        $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) );
 
        if ( $items ) {
@@ -912,7 +846,7 @@ function wp_dropdown_roles( $selected = '' ) {
 function wp_import_upload_form( $action ) {
 
        /**
-        * Filter the maximum allowed upload size for import files.
+        * Filters the maximum allowed upload size for import files.
         *
         * @since 2.3.0
         *
@@ -935,44 +869,56 @@ function wp_import_upload_form( $action ) {
 <input type="hidden" name="action" value="save" />
 <input type="hidden" name="max_file_size" value="<?php echo $bytes; ?>" />
 </p>
-<?php submit_button( __('Upload file and import'), 'button' ); ?>
+<?php submit_button( __('Upload file and import'), 'primary' ); ?>
 </form>
 <?php
        endif;
 }
 
 /**
- * Add a meta box to an edit form.
+ * Adds a meta box to one or more screens.
  *
  * @since 2.5.0
+ * @since 4.4.0 The `$screen` parameter now accepts an array of screen IDs.
  *
  * @global array $wp_meta_boxes
  *
- * @param string           $id            String for use in the 'id' attribute of tags.
- * @param string           $title         Title of the meta box.
- * @param callback         $callback      Function that fills the box with the desired content.
- *                                        The function should echo its output.
- * @param string|WP_Screen $screen        Optional. The screen on which to show the box (like a post
- *                                        type, 'link', or 'comment'). Default is the current screen.
- * @param string           $context       Optional. The context within the screen where the boxes
- *                                        should display. Available contexts vary from screen to
- *                                        screen. Post edit screen contexts include 'normal', 'side',
- *                                        and 'advanced'. Comments screen contexts include 'normal'
- *                                        and 'side'. Menus meta boxes (accordion sections) all use
- *                                        the 'side' context. Global default is 'advanced'.
- * @param string           $priority      Optional. The priority within the context where the boxes
- *                                        should show ('high', 'low'). Default 'default'.
- * @param array            $callback_args Optional. Data that should be set as the $args property
- *                                        of the box array (which is the second parameter passed
- *                                        to your callback). Default null.
+ * @param string                 $id            Meta box ID (used in the 'id' attribute for the meta box).
+ * @param string                 $title         Title of the meta box.
+ * @param callable               $callback      Function that fills the box with the desired content.
+ *                                              The function should echo its output.
+ * @param string|array|WP_Screen $screen        Optional. The screen or screens on which to show the box
+ *                                              (such as a post type, 'link', or 'comment'). Accepts a single
+ *                                              screen ID, WP_Screen object, or array of screen IDs. Default
+ *                                              is the current screen.
+ * @param string                 $context       Optional. The context within the screen where the boxes
+ *                                              should display. Available contexts vary from screen to
+ *                                              screen. Post edit screen contexts include 'normal', 'side',
+ *                                              and 'advanced'. Comments screen contexts include 'normal'
+ *                                              and 'side'. Menus meta boxes (accordion sections) all use
+ *                                              the 'side' context. Global default is 'advanced'.
+ * @param string                 $priority      Optional. The priority within the context where the boxes
+ *                                              should show ('high', 'low'). Default 'default'.
+ * @param array                  $callback_args Optional. Data that should be set as the $args property
+ *                                              of the box array (which is the second parameter passed
+ *                                              to your callback). Default null.
  */
 function add_meta_box( $id, $title, $callback, $screen = null, $context = 'advanced', $priority = 'default', $callback_args = null ) {
        global $wp_meta_boxes;
 
-       if ( empty( $screen ) )
+       if ( empty( $screen ) ) {
                $screen = get_current_screen();
-       elseif ( is_string( $screen ) )
+       } elseif ( is_string( $screen ) ) {
                $screen = convert_to_screen( $screen );
+       } elseif ( is_array( $screen ) ) {
+               foreach ( $screen as $single_screen ) {
+                       add_meta_box( $id, $title, $callback, $single_screen, $context, $priority, $callback_args );
+               }
+       }
+
+       if ( ! isset( $screen->id ) ) {
+               return;
+       }
 
        $page = $screen->id;
 
@@ -1083,9 +1029,21 @@ function do_meta_boxes( $screen, $context, $object ) {
                                        $i++;
                                        $hidden_class = in_array($box['id'], $hidden) ? ' hide-if-js' : '';
                                        echo '<div id="' . $box['id'] . '" class="postbox ' . postbox_classes($box['id'], $page) . $hidden_class . '" ' . '>' . "\n";
-                                       if ( 'dashboard_browser_nag' != $box['id'] )
-                                               echo '<div class="handlediv" title="' . esc_attr__('Click to toggle') . '"><br /></div>';
-                                       echo "<h3 class='hndle'><span>{$box['title']}</span></h3>\n";
+                                       if ( 'dashboard_browser_nag' != $box['id'] ) {
+                                               $widget_title = $box[ 'title' ];
+
+                                               if ( is_array( $box[ 'args' ] ) && isset( $box[ 'args' ][ '__widget_basename' ] ) ) {
+                                                       $widget_title = $box[ 'args' ][ '__widget_basename' ];
+                                                       // Do not pass this parameter to the user callback function.
+                                                       unset( $box[ 'args' ][ '__widget_basename' ] );
+                                               }
+
+                                               echo '<button type="button" class="handlediv button-link" aria-expanded="true">';
+                                               echo '<span class="screen-reader-text">' . sprintf( __( 'Toggle panel: %s' ), $widget_title ) . '</span>';
+                                               echo '<span class="toggle-indicator" aria-hidden="true"></span>';
+                                               echo '</button>';
+                                       }
+                                       echo "<h2 class='hndle'><span>{$box['title']}</span></h2>\n";
                                        echo '<div class="inside">' . "\n";
                                        call_user_func($box['callback'], $object, $box);
                                        echo "</div>\n";
@@ -1102,23 +1060,39 @@ function do_meta_boxes( $screen, $context, $object ) {
 }
 
 /**
- * Remove a meta box from an edit form.
+ * Removes a meta box from one or more screens.
  *
  * @since 2.6.0
+ * @since 4.4.0 The `$screen` parameter now accepts an array of screen IDs.
  *
  * @global array $wp_meta_boxes
  *
- * @param string        $id      String for use in the 'id' attribute of tags.
- * @param string|object $screen  The screen on which to show the box (post, page, link).
- * @param string        $context The context within the page where the boxes should show ('normal', 'advanced').
+ * @param string                 $id      Meta box ID (used in the 'id' attribute for the meta box).
+ * @param string|array|WP_Screen $screen  The screen or screens on which the meta box is shown (such as a
+ *                                        post type, 'link', or 'comment'). Accepts a single screen ID,
+ *                                        WP_Screen object, or array of screen IDs.
+ * @param string                 $context The context within the screen where the box is set to display.
+ *                                        Contexts vary from screen to screen. Post edit screen contexts
+ *                                        include 'normal', 'side', and 'advanced'. Comments screen contexts
+ *                                        include 'normal' and 'side'. Menus meta boxes (accordion sections)
+ *                                        all use the 'side' context.
  */
-function remove_meta_box($id, $screen, $context) {
+function remove_meta_box( $id, $screen, $context ) {
        global $wp_meta_boxes;
 
-       if ( empty( $screen ) )
+       if ( empty( $screen ) ) {
                $screen = get_current_screen();
-       elseif ( is_string( $screen ) )
+       } elseif ( is_string( $screen ) ) {
                $screen = convert_to_screen( $screen );
+       } elseif ( is_array( $screen ) ) {
+               foreach ( $screen as $single_screen ) {
+                       remove_meta_box( $id, $single_screen, $context );
+               }
+       }
+
+       if ( ! isset( $screen->id ) ) {
+               return;
+       }
 
        $page = $screen->id;
 
@@ -1136,7 +1110,7 @@ function remove_meta_box($id, $screen, $context) {
 /**
  * Meta Box Accordion Template Function
  *
- * Largely made up of abstracted code from {@link do_meta_boxes()}, this
+ * Largely made up of abstracted code from do_meta_boxes(), this
  * function serves to build meta boxes as list items for display as
  * a collapsible accordion.
  *
@@ -1187,7 +1161,7 @@ function do_accordion_sections( $screen, $context, $object ) {
                                        <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'] ); ?>">
                                                <h3 class="accordion-section-title hndle" tabindex="0">
                                                        <?php echo esc_html( $box['title'] ); ?>
-                                                       <span class="screen-reader-text"><?php _e( 'Press return or enter to expand' ); ?></span>
+                                                       <span class="screen-reader-text"><?php _e( 'Press return or enter to open this section' ); ?></span>
                                                </h3>
                                                <div class="accordion-section-content <?php postbox_classes( $box['id'], $page ); ?>">
                                                        <div class="inside">
@@ -1222,21 +1196,23 @@ function do_accordion_sections( $screen, $context, $object ) {
  *
  * @global $wp_settings_sections Storage array of all settings sections added to admin pages
  *
- * @param string $id       Slug-name to identify the section. Used in the 'id' attribute of tags.
- * @param string $title    Formatted title of the section. Shown as the heading for the section.
- * @param string $callback Function that echos out any content at the top of the section (between heading and fields).
- * @param string $page     The slug-name of the settings page on which to show the section. Built-in pages include 'general', 'reading', 'writing', 'discussion', 'media', etc. Create your own using add_options_page();
+ * @param string   $id       Slug-name to identify the section. Used in the 'id' attribute of tags.
+ * @param string   $title    Formatted title of the section. Shown as the heading for the section.
+ * @param callable $callback Function that echos out any content at the top of the section (between heading and fields).
+ * @param string   $page     The slug-name of the settings page on which to show the section. Built-in pages include
+ *                           'general', 'reading', 'writing', 'discussion', 'media', etc. Create your own using
+ *                           add_options_page();
  */
 function add_settings_section($id, $title, $callback, $page) {
        global $wp_settings_sections;
 
        if ( 'misc' == $page ) {
-               _deprecated_argument( __FUNCTION__, '3.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) );
+               _deprecated_argument( __FUNCTION__, '3.0.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) );
                $page = 'general';
        }
 
        if ( 'privacy' == $page ) {
-               _deprecated_argument( __FUNCTION__, '3.5', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) );
+               _deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) );
                $page = 'reading';
        }
 
@@ -1259,16 +1235,16 @@ function add_settings_section($id, $title, $callback, $page) {
  *
  * @global $wp_settings_fields Storage array of settings fields and info about their pages/sections
  *
- * @param string $id       Slug-name to identify the field. Used in the 'id' attribute of tags.
- * @param string $title    Formatted title of the field. Shown as the label for the field
- *                         during output.
- * @param string $callback Function that fills the field with the desired form inputs. The
- *                         function should echo its output.
- * @param string $page     The slug-name of the settings page on which to show the section
- *                         (general, reading, writing, ...).
- * @param string $section  Optional. The slug-name of the section of the settings page
- *                         in which to show the box. Default 'default'.
- * @param array  $args {
+ * @param string   $id       Slug-name to identify the field. Used in the 'id' attribute of tags.
+ * @param string   $title    Formatted title of the field. Shown as the label for the field
+ *                           during output.
+ * @param callable $callback Function that fills the field with the desired form inputs. The
+ *                           function should echo its output.
+ * @param string   $page     The slug-name of the settings page on which to show the section
+ *                           (general, reading, writing, ...).
+ * @param string   $section  Optional. The slug-name of the section of the settings page
+ *                           in which to show the box. Default 'default'.
+ * @param array    $args {
  *     Optional. Extra arguments used when outputting the field.
  *
  *     @type string $label_for When supplied, the setting title will be wrapped
@@ -1282,12 +1258,12 @@ function add_settings_field($id, $title, $callback, $page, $section = 'default',
        global $wp_settings_fields;
 
        if ( 'misc' == $page ) {
-               _deprecated_argument( __FUNCTION__, '3.0', __( 'The miscellaneous options group has been removed. Use another settings group.' ) );
+               _deprecated_argument( __FUNCTION__, '3.0.0', __( 'The miscellaneous options group has been removed. Use another settings group.' ) );
                $page = 'general';
        }
 
        if ( 'privacy' == $page ) {
-               _deprecated_argument( __FUNCTION__, '3.5', __( 'The privacy options group has been removed. Use another settings group.' ) );
+               _deprecated_argument( __FUNCTION__, '3.5.0', __( 'The privacy options group has been removed. Use another settings group.' ) );
                $page = 'reading';
        }
 
@@ -1305,7 +1281,7 @@ function add_settings_field($id, $title, $callback, $page, $section = 'default',
  * @global $wp_settings_fields Storage array of settings fields and info about their pages/sections
  * @since 2.7.0
  *
- * @param string $page The slug name of the page whos settings sections you want to output
+ * @param string $page The slug name of the page whose settings sections you want to output
  */
 function do_settings_sections( $page ) {
        global $wp_settings_sections, $wp_settings_fields;
@@ -1315,7 +1291,7 @@ function do_settings_sections( $page ) {
 
        foreach ( (array) $wp_settings_sections[$page] as $section ) {
                if ( $section['title'] )
-                       echo "<h3>{$section['title']}</h3>\n";
+                       echo "<h2>{$section['title']}</h2>\n";
 
                if ( $section['callback'] )
                        call_user_func( $section['callback'], $section );
@@ -1417,7 +1393,8 @@ function add_settings_error( $setting, $code, $message, $type = 'error' ) {
  *
  * Use the $sanitize argument to manually re-sanitize the option before returning errors.
  * This is useful if you have errors or notices you want to show even when the user
- * hasn't submitted data (i.e. when they first load an options page, or in admin_notices action hook)
+ * hasn't submitted data (i.e. when they first load an options page, or in the {@see 'admin_notices'}
+ * action hook).
  *
  * @since 3.0.0
  *
@@ -1462,16 +1439,16 @@ function get_settings_errors( $setting = '', $sanitize = false ) {
 }
 
 /**
- * Display settings errors registered by {@see add_settings_error()}.
+ * Display settings errors registered by add_settings_error().
  *
  * Part of the Settings API. Outputs a div for each error retrieved by
- * {@see get_settings_errors()}.
+ * get_settings_errors().
  *
  * This is called automatically after a settings page based on the
  * Settings API is submitted. Errors should be added during the validation
- * callback function for a setting defined in {@see register_setting()}
+ * callback function for a setting defined in register_setting().
  *
- * The $sanitize option is passed into {@see get_settings_errors()} and will
+ * The $sanitize option is passed into get_settings_errors() and will
  * re-run the setting sanitization
  * on its current value.
  *
@@ -1485,7 +1462,8 @@ function get_settings_errors( $setting = '', $sanitize = false ) {
  *
  * @param string $setting        Optional slug title of a specific setting who's errors you want.
  * @param bool   $sanitize       Whether to re-sanitize the setting value before returning errors.
- * @param bool   $hide_on_update If set to true errors will not be shown if the settings page has already been submitted.
+ * @param bool   $hide_on_update If set to true errors will not be shown if the settings page has
+ *                               already been submitted.
  */
 function settings_errors( $setting = '', $sanitize = false, $hide_on_update = false ) {
 
@@ -1509,7 +1487,7 @@ function settings_errors( $setting = '', $sanitize = false, $hide_on_update = fa
 }
 
 /**
- * {@internal Missing Short Description}}
+ * Outputs the modal window used for attaching media to posts or pages in the media-listing screen.
  *
  * @since 2.7.0
  *
@@ -1519,8 +1497,8 @@ function find_posts_div($found_action = '') {
 ?>
        <div id="find-posts" class="find-box" style="display: none;">
                <div id="find-posts-head" class="find-box-head">
-                       <?php _e( 'Find Posts or Pages' ); ?>
-                       <div id="find-posts-close"></div>
+                       <?php _e( 'Attach to existing content' ); ?>
+                       <button type="button" id="find-posts-close"><span class="screen-reader-text"><?php _e( 'Close media attachment panel' ); ?></button>
                </div>
                <div class="find-box-inside">
                        <div class="find-box-search">
@@ -1538,7 +1516,7 @@ function find_posts_div($found_action = '') {
                        <div id="find-posts-response"></div>
                </div>
                <div class="find-box-buttons">
-                       <?php submit_button( __( 'Select' ), 'button-primary alignright', 'find-posts-submit', false ); ?>
+                       <?php submit_button( __( 'Select' ), 'primary alignright', 'find-posts-submit', false ); ?>
                        <div class="clear"></div>
                </div>
        </div>
@@ -1546,10 +1524,9 @@ function find_posts_div($found_action = '') {
 }
 
 /**
- * Display the post password.
+ * Displays the post password.
  *
- * The password is passed through {@link esc_attr()} to ensure that it
- * is safe for placing in an html attribute.
+ * The password is passed through esc_attr() to ensure that it is safe for placing in an html attribute.
  *
  * @since 2.7.0
  */
@@ -1578,10 +1555,10 @@ function _draft_or_post_title( $post = 0 ) {
 }
 
 /**
- * Display the search query.
+ * Displays the search query.
  *
- * A simple wrapper to display the "s" parameter in a GET URI. This function
- * should only be used when {@link the_search_query()} cannot.
+ * A simple wrapper to display the "s" parameter in a `GET` URI. This function
+ * should only be used when the_search_query() cannot.
  *
  * @since 2.7.0
  */
@@ -1648,7 +1625,7 @@ do_action( "admin_head-$hook_suffix" );
 /** This action is documented in wp-admin/admin-header.php */
 do_action( 'admin_head' );
 
-$admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) );
+$admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_user_locale() ) ) );
 
 if ( is_rtl() )
        $admin_body_class .= ' rtl';
@@ -1685,11 +1662,19 @@ function iframe_footer() {
         * but run the hooks anyway since they output JavaScript
         * or other needed content.
         */
-        ?>
+
+       /**
+        * @global string $hook_suffix
+        */
+       global $hook_suffix;
+       ?>
        <div class="hidden">
 <?php
        /** This action is documented in wp-admin/admin-footer.php */
-       do_action( 'admin_footer', '' );
+       do_action( 'admin_footer', $hook_suffix );
+
+       /** This action is documented in wp-admin/admin-footer.php */
+       do_action( "admin_print_footer_scripts-$hook_suffix" );
 
        /** This action is documented in wp-admin/admin-footer.php */
        do_action( 'admin_print_footer_scripts' );
@@ -1719,8 +1704,7 @@ function _post_states($post) {
        if ( 'draft' == $post->post_status && 'draft' != $post_status )
                $post_states['draft'] = __('Draft');
        if ( 'pending' == $post->post_status && 'pending' != $post_status )
-               /* translators: post state */
-               $post_states['pending'] = _x('Pending', 'post state');
+               $post_states['pending'] = _x('Pending', 'post status');
        if ( is_sticky($post->ID) )
                $post_states['sticky'] = __('Sticky');
 
@@ -1728,28 +1712,30 @@ function _post_states($post) {
                $post_states['scheduled'] = __( 'Scheduled' );
        }
 
-       if ( get_option( 'page_on_front' ) == $post->ID ) {
-               $post_states['page_on_front'] = __( 'Front Page' );
-       }
+       if ( 'page' === get_option( 'show_on_front' ) ) {
+               if ( intval( get_option( 'page_on_front' ) ) === $post->ID ) {
+                       $post_states['page_on_front'] = __( 'Front Page' );
+               }
 
-       if ( get_option( 'page_for_posts' ) == $post->ID ) {
-               $post_states['page_for_posts'] = __( 'Posts Page' );
+               if ( intval( get_option( 'page_for_posts' ) ) === $post->ID ) {
+                       $post_states['page_for_posts'] = __( 'Posts Page' );
+               }
        }
 
        /**
-        * Filter the default post display states used in the posts list table.
+        * Filters the default post display states used in the posts list table.
         *
         * @since 2.8.0
         *
-        * @param array $post_states An array of post display states.
-        * @param int   $post        The post ID.
+        * @param array   $post_states An array of post display states.
+        * @param WP_Post $post        The current post object.
         */
        $post_states = apply_filters( 'display_post_states', $post_states, $post );
 
        if ( ! empty($post_states) ) {
                $state_count = count($post_states);
                $i = 0;
-               echo ' - ';
+               echo ' &mdash; ';
                foreach ( $post_states as $state ) {
                        ++$i;
                        ( $i == $state_count ) ? $sep = '' : $sep = ', ';
@@ -1769,34 +1755,63 @@ function _media_states( $post ) {
 
        if ( current_theme_supports( 'custom-header') ) {
                $meta_header = get_post_meta($post->ID, '_wp_attachment_is_custom_header', true );
-               if ( ! empty( $meta_header ) && $meta_header == $stylesheet )
-                       $media_states[] = __( 'Header Image' );
+
+               if ( is_random_header_image() ) {
+                       $header_images = wp_list_pluck( get_uploaded_header_images(), 'attachment_id' );
+
+                       if ( $meta_header == $stylesheet && in_array( $post->ID, $header_images ) ) {
+                               $media_states[] = __( 'Header Image' );
+                       }
+               } else {
+                       $header_image = get_header_image();
+
+                       // Display "Header Image" if the image was ever used as a header image
+                       if ( ! empty( $meta_header ) && $meta_header == $stylesheet && $header_image !== wp_get_attachment_url( $post->ID ) ) {
+                               $media_states[] = __( 'Header Image' );
+                       }
+
+                       // Display "Current Header Image" if the image is currently the header image
+                       if ( $header_image && $header_image == wp_get_attachment_url( $post->ID ) ) {
+                               $media_states[] = __( 'Current Header Image' );
+                       }
+               }
        }
 
        if ( current_theme_supports( 'custom-background') ) {
                $meta_background = get_post_meta($post->ID, '_wp_attachment_is_custom_background', true );
-               if ( ! empty( $meta_background ) && $meta_background == $stylesheet )
+
+               if ( ! empty( $meta_background ) && $meta_background == $stylesheet ) {
                        $media_states[] = __( 'Background Image' );
+
+                       $background_image = get_background_image();
+                       if ( $background_image && $background_image == wp_get_attachment_url( $post->ID ) ) {
+                               $media_states[] = __( 'Current Background Image' );
+                       }
+               }
        }
 
        if ( $post->ID == get_option( 'site_icon' ) ) {
                $media_states[] = __( 'Site Icon' );
        }
 
+       if ( $post->ID == get_theme_mod( 'site_logo' ) ) {
+               $media_states[] = __( 'Logo' );
+       }
+
        /**
-        * Filter the default media display states for items in the Media list table.
+        * Filters the default media display states for items in the Media list table.
         *
         * @since 3.2.0
         *
         * @param array $media_states An array of media states. Default 'Header Image',
-        *                            'Background Image', 'Site Icon'.
+        *                            'Background Image', 'Site Icon', 'Logo'.
         */
        $media_states = apply_filters( 'display_media_states', $media_states );
 
        if ( ! empty( $media_states ) ) {
                $state_count = count( $media_states );
                $i = 0;
-               echo ' - ';
+               echo ' &mdash; ';
                foreach ( $media_states as $state ) {
                        ++$i;
                        ( $i == $state_count ) ? $sep = '' : $sep = ', ';
@@ -1818,6 +1833,7 @@ function _media_states( $post ) {
 function compression_test() {
 ?>
        <script type="text/javascript">
+       var compressionNonce = <?php echo wp_json_encode( wp_create_nonce( 'update_can_compress_scripts' ) ); ?>;
        var testCompression = {
                get : function(test) {
                        var x;
@@ -1837,7 +1853,7 @@ function compression_test() {
                                        }
                                };
 
-                               x.open('GET', ajaxurl + '?action=wp-compression-test&test='+test+'&'+(new Date()).getTime(), true);
+                               x.open('GET', ajaxurl + '?action=wp-compression-test&test='+test+'&_ajax_nonce='+compressionNonce+'&'+(new Date()).getTime(), true);
                                x.send('');
                        }
                },
@@ -1926,10 +1942,8 @@ function get_submit_button( $text = '', $type = 'primary large', $name = 'submit
                        continue;
                $classes[] = in_array( $t, $button_shorthand ) ? 'button-' . $t : $t;
        }
-       $class = implode( ' ', array_unique( $classes ) );
-
-       if ( 'delete' === $type )
-               $class = 'button-secondary delete';
+       // Remove empty items, remove duplicate items, and finally build a string.
+       $class = implode( ' ', array_unique( array_filter( $classes ) ) );
 
        $text = $text ? $text : __( 'Save Changes' );
 
@@ -1998,159 +2012,6 @@ function _wp_admin_html_begin() {
 <?php
 }
 
-final class WP_Internal_Pointers {
-       /**
-        * Initializes the new feature pointers.
-        *
-        * @since 3.3.0
-        *
-        * All pointers can be disabled using the following:
-        *     remove_action( 'admin_enqueue_scripts', array( 'WP_Internal_Pointers', 'enqueue_scripts' ) );
-        *
-        * Individual pointers (e.g. wp390_widgets) can be disabled using the following:
-        *     remove_action( 'admin_print_footer_scripts', array( 'WP_Internal_Pointers', 'pointer_wp390_widgets' ) );
-        *
-        * @static
-        *
-        * @param string $hook_suffix The current admin page.
-        */
-       public static function enqueue_scripts( $hook_suffix ) {
-               /*
-                * Register feature pointers
-                *
-                * Format:
-                *     array(
-                *         hook_suffix => pointer callback
-                *     )
-                *
-                * Example:
-                *     array(
-                *         'themes.php' => 'wp390_widgets'
-                *     )
-                */
-               $registered_pointers = array(
-                       // None currently
-               );
-
-               // Check if screen related pointer is registered
-               if ( empty( $registered_pointers[ $hook_suffix ] ) )
-                       return;
-
-               $pointers = (array) $registered_pointers[ $hook_suffix ];
-
-               /*
-                * Specify required capabilities for feature pointers
-                *
-                * Format:
-                *     array(
-                *         pointer callback => Array of required capabilities
-                *     )
-                *
-                * Example:
-                *     array(
-                *         'wp390_widgets' => array( 'edit_theme_options' )
-                *     )
-                */
-               $caps_required = array(
-                       // None currently
-               );
-
-               // Get dismissed pointers
-               $dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
-
-               $got_pointers = false;
-               foreach ( array_diff( $pointers, $dismissed ) as $pointer ) {
-                       if ( isset( $caps_required[ $pointer ] ) ) {
-                               foreach ( $caps_required[ $pointer ] as $cap ) {
-                                       if ( ! current_user_can( $cap ) )
-                                               continue 2;
-                               }
-                       }
-
-                       // Bind pointer print function
-                       add_action( 'admin_print_footer_scripts', array( 'WP_Internal_Pointers', 'pointer_' . $pointer ) );
-                       $got_pointers = true;
-               }
-
-               if ( ! $got_pointers )
-                       return;
-
-               // Add pointers script and style to queue
-               wp_enqueue_style( 'wp-pointer' );
-               wp_enqueue_script( 'wp-pointer' );
-       }
-
-       /**
-        * Print the pointer JavaScript data.
-        *
-        * @since 3.3.0
-        *
-        * @static
-        *
-        * @param string $pointer_id The pointer ID.
-        * @param string $selector The HTML elements, on which the pointer should be attached.
-        * @param array  $args Arguments to be passed to the pointer JS (see wp-pointer.js).
-        */
-       private static function print_js( $pointer_id, $selector, $args ) {
-               if ( empty( $pointer_id ) || empty( $selector ) || empty( $args ) || empty( $args['content'] ) )
-                       return;
-
-               ?>
-               <script type="text/javascript">
-               (function($){
-                       var options = <?php echo wp_json_encode( $args ); ?>, setup;
-
-                       if ( ! options )
-                               return;
-
-                       options = $.extend( options, {
-                               close: function() {
-                                       $.post( ajaxurl, {
-                                               pointer: '<?php echo $pointer_id; ?>',
-                                               action: 'dismiss-wp-pointer'
-                                       });
-                               }
-                       });
-
-                       setup = function() {
-                               $('<?php echo $selector; ?>').first().pointer( options ).pointer('open');
-                       };
-
-                       if ( options.position && options.position.defer_loading )
-                               $(window).bind( 'load.wp-pointers', setup );
-                       else
-                               $(document).ready( setup );
-
-               })( jQuery );
-               </script>
-               <?php
-       }
-
-       public static function pointer_wp330_toolbar() {}
-       public static function pointer_wp330_media_uploader() {}
-       public static function pointer_wp330_saving_widgets() {}
-       public static function pointer_wp340_customize_current_theme_link() {}
-       public static function pointer_wp340_choose_image_from_library() {}
-       public static function pointer_wp350_media() {}
-       public static function pointer_wp360_revisions() {}
-       public static function pointer_wp360_locks() {}
-       public static function pointer_wp390_widgets() {}
-       public static function pointer_wp410_dfw() {}
-
-       /**
-        * Prevents new users from seeing existing 'new feature' pointers.
-        *
-        * @since 3.3.0
-        *
-        * @static
-        *
-        * @param int $user_id User ID.
-        */
-       public static function dismiss_pointers_for_new_users( $user_id ) {
-               add_user_meta( $user_id, 'dismissed_wp_pointers', '' );
-       }
-}
-
 /**
  * Convert a screen string to a screen object
  *
@@ -2161,7 +2022,7 @@ final class WP_Internal_Pointers {
  */
 function convert_to_screen( $hook_name ) {
        if ( ! class_exists( 'WP_Screen' ) ) {
-               _doing_it_wrong( 'convert_to_screen(), add_meta_box()', __( "Likely direct inclusion of wp-admin/includes/template.php in order to use add_meta_box(). This is very wrong. Hook the add_meta_box() call into the add_meta_boxes action instead." ), '3.3' );
+               _doing_it_wrong( 'convert_to_screen(), add_meta_box()', __( "Likely direct inclusion of wp-admin/includes/template.php in order to use add_meta_box(). This is very wrong. Hook the add_meta_box() call into the add_meta_boxes action instead." ), '3.3.0' );
                return (object) array( 'id' => '_invalid', 'base' => '_are_belong_to_us' );
        }
 
@@ -2176,14 +2037,13 @@ function convert_to_screen( $hook_name ) {
  */
 function _local_storage_notice() {
        ?>
-       <div id="local-storage-notice" class="hidden notice">
+       <div id="local-storage-notice" class="hidden notice is-dismissible">
        <p class="local-restore">
-               <?php _e('The backup of this post in your browser is different from the version below.'); ?>
-               <a class="restore-backup" href="#"><?php _e('Restore the backup.'); ?></a>
+               <?php _e( 'The backup of this post in your browser is different from the version below.' ); ?>
+               <button type="button" class="button restore-backup"><?php _e('Restore the backup'); ?></button>
        </p>
-       <p class="undo-restore hidden">
-               <?php _e('Post restored successfully.'); ?>
-               <a class="undo-restore-backup" href="#"><?php _e('Undo.'); ?></a>
+       <p class="help">
+               <?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.' ); ?>
        </p>
        </div>
        <?php
@@ -2197,6 +2057,8 @@ function _local_storage_notice() {
  * number of ratings may also be displayed by passing the $number parameter.
  *
  * @since 3.8.0
+ * @since 4.4.0 Introduced the `echo` parameter.
+ *
  * @param array $args {
  *     Optional. Array of star ratings arguments.
  *
@@ -2205,13 +2067,16 @@ function _local_storage_notice() {
  *     @type string $type   Format that the $rating is in. Valid values are 'rating' (default),
  *                          or, 'percent'. Default 'rating'.
  *     @type int    $number The number of ratings that makes up this rating. Default 0.
+ *     @type bool   $echo   Whether to echo the generated markup. False to return the markup instead
+ *                          of echoing it. Default true.
  * }
  */
 function wp_star_rating( $args = array() ) {
        $defaults = array(
                'rating' => 0,
-               'type' => 'rating',
+               'type'   => 'rating',
                'number' => 0,
+               'echo'   => true,
        );
        $r = wp_parse_args( $args, $defaults );
 
@@ -2237,12 +2102,18 @@ function wp_star_rating( $args = array() ) {
                $title = sprintf( __( '%s rating' ), number_format_i18n( $rating, 1 ) );
        }
 
-       echo '<div class="star-rating" title="' . esc_attr( $title ) . '">';
-       echo '<span class="screen-reader-text">' . $title . '</span>';
-       echo str_repeat( '<div class="star star-full"></div>', $full_stars );
-       echo str_repeat( '<div class="star star-half"></div>', $half_stars );
-       echo str_repeat( '<div class="star star-empty"></div>', $empty_stars);
-       echo '</div>';
+       $output = '<div class="star-rating">';
+       $output .= '<span class="screen-reader-text">' . $title . '</span>';
+       $output .= str_repeat( '<div class="star star-full" aria-hidden="true"></div>', $full_stars );
+       $output .= str_repeat( '<div class="star star-half" aria-hidden="true"></div>', $half_stars );
+       $output .= str_repeat( '<div class="star star-empty" aria-hidden="true"></div>', $empty_stars );
+       $output .= '</div>';
+
+       if ( $r['echo'] ) {
+               echo $output;
+       }
+
+       return $output;
 }
 
 /**