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