<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[35718] trunk/src: Simplify the include graph after work to split out classes.</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/35718">35718</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/35718","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>nacin</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2015-11-20 07:23:04 +0000 (Fri, 20 Nov 2015)</dd>
</dl>
<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Simplify the include graph after work to split out classes.
see <a href="https://core.trac.wordpress.org/ticket/33413">#33413</a>. More details there.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadminincludesadminphp">trunk/src/wp-admin/includes/admin.php</a></li>
<li><a href="#trunksrcwpadminincludesupdatecorephp">trunk/src/wp-admin/includes/update-core.php</a></li>
<li><a href="#trunksrcwpincludesclasswpcustomizemanagerphp">trunk/src/wp-includes/class-wp-customize-manager.php</a></li>
<li><a href="#trunksrcwpsettingsphp">trunk/src/wp-settings.php</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunksrcwpadminincludestemplatephp">trunk/src/wp-admin/includes/template.php</a></li>
<li><a href="#trunksrcwpincludescapabilitiesphp">trunk/src/wp-includes/capabilities.php</a></li>
<li><a href="#trunksrcwpincludescategoryphp">trunk/src/wp-includes/category.php</a></li>
<li><a href="#trunksrcwpincludescommentphp">trunk/src/wp-includes/comment.php</a></li>
<li><a href="#trunksrcwpincludesembedphp">trunk/src/wp-includes/embed.php</a></li>
<li><a href="#trunksrcwpincludeshttpphp">trunk/src/wp-includes/http.php</a></li>
<li><a href="#trunksrcwpincludesmetaphp">trunk/src/wp-includes/meta.php</a></li>
<li><a href="#trunksrcwpincludespostphp">trunk/src/wp-includes/post.php</a></li>
<li><a href="#trunksrcwpincludesrestapiphp">trunk/src/wp-includes/rest-api.php</a></li>
<li><a href="#trunksrcwpincludesrewritephp">trunk/src/wp-includes/rewrite.php</a></li>
<li><a href="#trunksrcwpincludestaxonomyphp">trunk/src/wp-includes/taxonomy.php</a></li>
<li><a href="#trunksrcwpincludesuserphp">trunk/src/wp-includes/user.php</a></li>
<li><a href="#trunksrcwpincludeswidgetsphp">trunk/src/wp-includes/widgets.php</a></li>
</ul>
<h3>Removed Paths</h3>
<ul>
<li><a href="#trunksrcwpadminincludestemplatefunctionsphp">trunk/src/wp-admin/includes/template-functions.php</a></li>
<li><a href="#trunksrcwpadminincludestemplatephp">trunk/src/wp-admin/includes/template.php</a></li>
<li><a href="#trunksrcwpincludescapabilitiesfunctionsphp">trunk/src/wp-includes/capabilities-functions.php</a></li>
<li><a href="#trunksrcwpincludescapabilitiesphp">trunk/src/wp-includes/capabilities.php</a></li>
<li><a href="#trunksrcwpincludescategoryfunctionsphp">trunk/src/wp-includes/category-functions.php</a></li>
<li><a href="#trunksrcwpincludescategoryphp">trunk/src/wp-includes/category.php</a></li>
<li><a href="#trunksrcwpincludescommentfunctionsphp">trunk/src/wp-includes/comment-functions.php</a></li>
<li><a href="#trunksrcwpincludescommentphp">trunk/src/wp-includes/comment.php</a></li>
<li><a href="#trunksrcwpincludesembedfunctionsphp">trunk/src/wp-includes/embed-functions.php</a></li>
<li><a href="#trunksrcwpincludeshttpfunctionsphp">trunk/src/wp-includes/http-functions.php</a></li>
<li><a href="#trunksrcwpincludeshttpphp">trunk/src/wp-includes/http.php</a></li>
<li><a href="#trunksrcwpincludesmetafunctionsphp">trunk/src/wp-includes/meta-functions.php</a></li>
<li><a href="#trunksrcwpincludesmetaphp">trunk/src/wp-includes/meta.php</a></li>
<li><a href="#trunksrcwpincludespostfunctionsphp">trunk/src/wp-includes/post-functions.php</a></li>
<li><a href="#trunksrcwpincludespostphp">trunk/src/wp-includes/post.php</a></li>
<li><a href="#trunksrcwpincludesrestapirestfunctionsphp">trunk/src/wp-includes/rest-api/rest-functions.php</a></li>
<li><a href="#trunksrcwpincludesrestapiphp">trunk/src/wp-includes/rest-api.php</a></li>
<li><a href="#trunksrcwpincludesrewriteconstantsphp">trunk/src/wp-includes/rewrite-constants.php</a></li>
<li><a href="#trunksrcwpincludesrewritefunctionsphp">trunk/src/wp-includes/rewrite-functions.php</a></li>
<li><a href="#trunksrcwpincludesrewritephp">trunk/src/wp-includes/rewrite.php</a></li>
<li><a href="#trunksrcwpincludestaxonomyfunctionsphp">trunk/src/wp-includes/taxonomy-functions.php</a></li>
<li><a href="#trunksrcwpincludestaxonomyphp">trunk/src/wp-includes/taxonomy.php</a></li>
<li><a href="#trunksrcwpincludesuserfunctionsphp">trunk/src/wp-includes/user-functions.php</a></li>
<li><a href="#trunksrcwpincludesuserphp">trunk/src/wp-includes/user.php</a></li>
<li><a href="#trunksrcwpincludeswidgetfunctionsphp">trunk/src/wp-includes/widget-functions.php</a></li>
<li><a href="#trunksrcwpincludeswidgetsphp">trunk/src/wp-includes/widgets.php</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadminincludesadminphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/admin.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/admin.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-admin/includes/admin.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -58,6 +58,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /** WordPress Template Administration API */
</span><span class="cx" style="display: block; padding: 0 10px"> require_once(ABSPATH . 'wp-admin/includes/template.php');
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require_once(ABSPATH . 'wp-admin/includes/class-walker-category-checklist.php');
+require_once(ABSPATH . 'wp-admin/includes/class-wp-internal-pointers.php');
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /** WordPress List Table Administration API and base class */
</span><span class="cx" style="display: block; padding: 0 10px"> require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
</span></span></pre></div>
<a id="trunksrcwpadminincludestemplatefunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-admin/includes/template-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/template-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-admin/includes/template-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,2074 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Administration API: Top-level admin template functionality
- *
- * A Big Mess. Also some neat functions that are nicely written.
- *
- * @package WordPress
- * @subpackage Administration
- * @since 4.4.0
- */
-
-//
-// Category Checklists
-//
-
-/**
- * Output an unordered list of checkbox input elements labeled with category names.
- *
- * @since 2.5.1
- *
- * @see wp_terms_checklist()
- *
- * @param int $post_id Optional. Post to generate a categories checklist for. Default 0.
- * $selected_cats must not be an array. Default 0.
- * @param int $descendants_and_self Optional. ID of the category to output along with its descendants.
- * Default 0.
- * @param array $selected_cats Optional. List of categories to mark as checked. Default false.
- * @param array $popular_cats Optional. List of categories to receive the "popular-category" class.
- * Default false.
- * @param object $walker Optional. Walker object to use to build the output.
- * Default is a Walker_Category_Checklist instance.
- * @param bool $checked_ontop Optional. Whether to move checked items out of the hierarchy and to
- * the top of the list. Default true.
- */
-function wp_category_checklist( $post_id = 0, $descendants_and_self = 0, $selected_cats = false, $popular_cats = false, $walker = null, $checked_ontop = true ) {
- wp_terms_checklist( $post_id, array(
- 'taxonomy' => 'category',
- 'descendants_and_self' => $descendants_and_self,
- 'selected_cats' => $selected_cats,
- 'popular_cats' => $popular_cats,
- 'walker' => $walker,
- 'checked_ontop' => $checked_ontop
- ) );
-}
-
-/**
- * Output an unordered list of checkbox input elements labelled with term names.
- *
- * Taxonomy-independent version of wp_category_checklist().
- *
- * @since 3.0.0
- * @since 4.4.0 Introduced the `$echo` argument.
- *
- * @param int $post_id Optional. Post ID. Default 0.
- * @param array|string $args {
- * Optional. Array or string of arguments for generating a terms checklist. Default empty array.
- *
- * @type int $descendants_and_self ID of the category to output along with its descendants.
- * Default 0.
- * @type array $selected_cats List of categories to mark as checked. Default false.
- * @type array $popular_cats List of categories to receive the "popular-category" class.
- * Default false.
- * @type object $walker Walker object to use to build the output.
- * Default is a Walker_Category_Checklist instance.
- * @type string $taxonomy Taxonomy to generate the checklist for. Default 'category'.
- * @type bool $checked_ontop Whether to move checked items out of the hierarchy and to
- * the top of the list. Default true.
- * @type bool $echo Whether to echo the generated markup. False to return the markup instead
- * of echoing it. Default true.
- * }
- */
-function wp_terms_checklist( $post_id = 0, $args = array() ) {
- $defaults = array(
- 'descendants_and_self' => 0,
- 'selected_cats' => false,
- 'popular_cats' => false,
- 'walker' => null,
- 'taxonomy' => 'category',
- 'checked_ontop' => true,
- 'echo' => true,
- );
-
- /**
- * Filter the taxonomy terms checklist arguments.
- *
- * @since 3.4.0
- *
- * @see wp_terms_checklist()
- *
- * @param array $args An array of arguments.
- * @param int $post_id The post ID.
- */
- $params = apply_filters( 'wp_terms_checklist_args', $args, $post_id );
-
- $r = wp_parse_args( $params, $defaults );
-
- if ( empty( $r['walker'] ) || ! ( $r['walker'] instanceof Walker ) ) {
- $walker = new Walker_Category_Checklist;
- } else {
- $walker = $r['walker'];
- }
-
- $taxonomy = $r['taxonomy'];
- $descendants_and_self = (int) $r['descendants_and_self'];
-
- $args = array( 'taxonomy' => $taxonomy );
-
- $tax = get_taxonomy( $taxonomy );
- $args['disabled'] = ! current_user_can( $tax->cap->assign_terms );
-
- $args['list_only'] = ! empty( $r['list_only'] );
-
- if ( is_array( $r['selected_cats'] ) ) {
- $args['selected_cats'] = $r['selected_cats'];
- } elseif ( $post_id ) {
- $args['selected_cats'] = wp_get_object_terms( $post_id, $taxonomy, array_merge( $args, array( 'fields' => 'ids' ) ) );
- } else {
- $args['selected_cats'] = array();
- }
- if ( is_array( $r['popular_cats'] ) ) {
- $args['popular_cats'] = $r['popular_cats'];
- } else {
- $args['popular_cats'] = get_terms( $taxonomy, array(
- 'fields' => 'ids',
- 'orderby' => 'count',
- 'order' => 'DESC',
- 'number' => 10,
- 'hierarchical' => false
- ) );
- }
- if ( $descendants_and_self ) {
- $categories = (array) get_terms( $taxonomy, array(
- 'child_of' => $descendants_and_self,
- 'hierarchical' => 0,
- 'hide_empty' => 0
- ) );
- $self = get_term( $descendants_and_self, $taxonomy );
- array_unshift( $categories, $self );
- } else {
- $categories = (array) get_terms( $taxonomy, array( 'get' => 'all' ) );
- }
-
- $output = '';
-
- if ( $r['checked_ontop'] ) {
- // Post process $categories rather than adding an exclude to the get_terms() query to keep the query the same across all posts (for any query cache)
- $checked_categories = array();
- $keys = array_keys( $categories );
-
- foreach ( $keys as $k ) {
- if ( in_array( $categories[$k]->term_id, $args['selected_cats'] ) ) {
- $checked_categories[] = $categories[$k];
- unset( $categories[$k] );
- }
- }
-
- // Put checked cats on top
- $output .= call_user_func_array( array( $walker, 'walk' ), array( $checked_categories, 0, $args ) );
- }
- // Then the rest of them
- $output .= call_user_func_array( array( $walker, 'walk' ), array( $categories, 0, $args ) );
-
- if ( $r['echo'] ) {
- echo $output;
- }
-
- return $output;
-}
-
-/**
- * Retrieve a list of the most popular terms from the specified taxonomy.
- *
- * If the $echo argument is true then the elements for a list of checkbox
- * `<input>` elements labelled with the names of the selected terms is output.
- * If the $post_ID global isn't empty then the terms associated with that
- * post will be marked as checked.
- *
- * @since 2.5.0
- *
- * @param string $taxonomy Taxonomy to retrieve terms from.
- * @param int $default Not used.
- * @param int $number Number of terms to retrieve. Defaults to 10.
- * @param bool $echo Optionally output the list as well. Defaults to true.
- * @return array List of popular term IDs.
- */
-function wp_popular_terms_checklist( $taxonomy, $default = 0, $number = 10, $echo = true ) {
- $post = get_post();
-
- if ( $post && $post->ID )
- $checked_terms = wp_get_object_terms($post->ID, $taxonomy, array('fields'=>'ids'));
- else
- $checked_terms = array();
-
- $terms = get_terms( $taxonomy, array( 'orderby' => 'count', 'order' => 'DESC', 'number' => $number, 'hierarchical' => false ) );
-
- $tax = get_taxonomy($taxonomy);
-
- $popular_ids = array();
- foreach ( (array) $terms as $term ) {
- $popular_ids[] = $term->term_id;
- if ( !$echo ) // hack for AJAX use
- continue;
- $id = "popular-$taxonomy-$term->term_id";
- $checked = in_array( $term->term_id, $checked_terms ) ? 'checked="checked"' : '';
- ?>
-
- <li id="<?php echo $id; ?>" class="popular-category">
- <label class="selectit">
- <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 ) ); ?> />
- <?php
- /** This filter is documented in wp-includes/category-template.php */
- echo esc_html( apply_filters( 'the_category', $term->name ) );
- ?>
- </label>
- </li>
-
- <?php
- }
- return $popular_ids;
-}
-
-/**
- * Outputs a link category checklist element.
- *
- * @since 2.5.1
- *
- * @param int $link_id
- */
-function wp_link_category_checklist( $link_id = 0 ) {
- $default = 1;
-
- $checked_categories = array();
-
- if ( $link_id ) {
- $checked_categories = wp_get_link_cats( $link_id );
- // No selected categories, strange
- if ( ! count( $checked_categories ) ) {
- $checked_categories[] = $default;
- }
- } else {
- $checked_categories[] = $default;
- }
-
- $categories = get_terms( 'link_category', array( 'orderby' => 'name', 'hide_empty' => 0 ) );
-
- if ( empty( $categories ) )
- return;
-
- foreach ( $categories as $category ) {
- $cat_id = $category->term_id;
-
- /** This filter is documented in wp-includes/category-template.php */
- $name = esc_html( apply_filters( 'the_category', $category->name ) );
- $checked = in_array( $cat_id, $checked_categories ) ? ' checked="checked"' : '';
- 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>";
- }
-}
-
-/**
- * Adds hidden fields with the data for use in the inline editor for posts and pages.
- *
- * @since 2.7.0
- *
- * @param WP_Post $post Post object.
- */
-function get_inline_data($post) {
- $post_type_object = get_post_type_object($post->post_type);
- if ( ! current_user_can( 'edit_post', $post->ID ) )
- return;
-
- $title = esc_textarea( trim( $post->post_title ) );
-
- /** This filter is documented in wp-admin/edit-tag-form.php */
- echo '
-<div class="hidden" id="inline_' . $post->ID . '">
- <div class="post_title">' . $title . '</div>' .
- /** This filter is documented in wp-admin/edit-tag-form.php */
- '<div class="post_name">' . apply_filters( 'editable_slug', $post->post_name, $post ) . '</div>
- <div class="post_author">' . $post->post_author . '</div>
- <div class="comment_status">' . esc_html( $post->comment_status ) . '</div>
- <div class="ping_status">' . esc_html( $post->ping_status ) . '</div>
- <div class="_status">' . esc_html( $post->post_status ) . '</div>
- <div class="jj">' . mysql2date( 'd', $post->post_date, false ) . '</div>
- <div class="mm">' . mysql2date( 'm', $post->post_date, false ) . '</div>
- <div class="aa">' . mysql2date( 'Y', $post->post_date, false ) . '</div>
- <div class="hh">' . mysql2date( 'H', $post->post_date, false ) . '</div>
- <div class="mn">' . mysql2date( 'i', $post->post_date, false ) . '</div>
- <div class="ss">' . mysql2date( 's', $post->post_date, false ) . '</div>
- <div class="post_password">' . esc_html( $post->post_password ) . '</div>';
-
- if ( $post_type_object->hierarchical )
- echo '<div class="post_parent">' . $post->post_parent . '</div>';
-
- if ( $post->post_type == 'page' )
- echo '<div class="page_template">' . esc_html( get_post_meta( $post->ID, '_wp_page_template', true ) ) . '</div>';
-
- if ( post_type_supports( $post->post_type, 'page-attributes' ) )
- echo '<div class="menu_order">' . $post->menu_order . '</div>';
-
- $taxonomy_names = get_object_taxonomies( $post->post_type );
- foreach ( $taxonomy_names as $taxonomy_name) {
- $taxonomy = get_taxonomy( $taxonomy_name );
-
- if ( $taxonomy->hierarchical && $taxonomy->show_ui ) {
-
- $terms = get_object_term_cache( $post->ID, $taxonomy_name );
- if ( false === $terms ) {
- $terms = wp_get_object_terms( $post->ID, $taxonomy_name );
- wp_cache_add( $post->ID, $terms, $taxonomy_name . '_relationships' );
- }
- $term_ids = empty( $terms ) ? array() : wp_list_pluck( $terms, 'term_id' );
-
- echo '<div class="post_category" id="' . $taxonomy_name . '_' . $post->ID . '">' . implode( ',', $term_ids ) . '</div>';
-
- } elseif ( $taxonomy->show_ui ) {
-
- $terms_to_edit = get_terms_to_edit( $post->ID, $taxonomy_name );
- if ( ! is_string( $terms_to_edit ) ) {
- $terms_to_edit = '';
- }
-
- echo '<div class="tags_input" id="'.$taxonomy_name.'_'.$post->ID.'">'
- . esc_html( str_replace( ',', ', ', $terms_to_edit ) ) . '</div>';
-
- }
- }
-
- if ( !$post_type_object->hierarchical )
- echo '<div class="sticky">' . (is_sticky($post->ID) ? 'sticky' : '') . '</div>';
-
- if ( post_type_supports( $post->post_type, 'post-formats' ) )
- echo '<div class="post_format">' . esc_html( get_post_format( $post->ID ) ) . '</div>';
-
- echo '</div>';
-}
-
-/**
- * Outputs the in-line comment reply-to form in the Comments list table.
- *
- * @since 2.7.0
- *
- * @global WP_List_Table $wp_list_table
- *
- * @param int $position
- * @param bool $checkbox
- * @param string $mode
- * @param bool $table_row
- */
-function wp_comment_reply( $position = 1, $checkbox = false, $mode = 'single', $table_row = true ) {
- global $wp_list_table;
- /**
- * Filter the in-line comment reply-to form output in the Comments
- * list table.
- *
- * Returning a non-empty value here will short-circuit display
- * of the in-line comment-reply form in the Comments list table,
- * echoing the returned value instead.
- *
- * @since 2.7.0
- *
- * @see wp_comment_reply()
- *
- * @param string $content The reply-to form content.
- * @param array $args An array of default args.
- */
- $content = apply_filters( 'wp_comment_reply', '', array( 'position' => $position, 'checkbox' => $checkbox, 'mode' => $mode ) );
-
- if ( ! empty($content) ) {
- echo $content;
- return;
- }
-
- if ( ! $wp_list_table ) {
- if ( $mode == 'single' ) {
- $wp_list_table = _get_list_table('WP_Post_Comments_List_Table');
- } else {
- $wp_list_table = _get_list_table('WP_Comments_List_Table');
- }
- }
-
-?>
-<form method="get">
-<?php if ( $table_row ) : ?>
-<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">
-<?php else : ?>
-<div id="com-reply" style="display:none;"><div id="replyrow" style="display:none;">
-<?php endif; ?>
- <fieldset class="comment-reply">
- <legend>
- <span class="hidden" id="editlegend"><?php _e( 'Edit Comment' ); ?></span>
- <span class="hidden" id="replyhead"><?php _e( 'Reply to Comment' ); ?></span>
- <span class="hidden" id="addhead"><?php _e( 'Add new Comment' ); ?></span>
- </legend>
-
- <div id="replycontainer">
- <label for="replycontent" class="screen-reader-text"><?php _e( 'Comment' ); ?></label>
- <?php
- $quicktags_settings = array( 'buttons' => 'strong,em,link,block,del,ins,img,ul,ol,li,code,close' );
- wp_editor( '', 'replycontent', array( 'media_buttons' => false, 'tinymce' => false, 'quicktags' => $quicktags_settings ) );
- ?>
- </div>
-
- <div id="edithead" style="display:none;">
- <div class="inside">
- <label for="author-name"><?php _e( 'Name' ) ?></label>
- <input type="text" name="newcomment_author" size="50" value="" id="author-name" />
- </div>
-
- <div class="inside">
- <label for="author-email"><?php _e('Email') ?></label>
- <input type="text" name="newcomment_author_email" size="50" value="" id="author-email" />
- </div>
-
- <div class="inside">
- <label for="author-url"><?php _e('URL') ?></label>
- <input type="text" id="author-url" name="newcomment_author_url" class="code" size="103" value="" />
- </div>
- </div>
-
- <p id="replysubmit" class="submit">
- <a href="#comments-form" class="save button-primary alignright">
- <span id="addbtn" style="display:none;"><?php _e('Add Comment'); ?></span>
- <span id="savebtn" style="display:none;"><?php _e('Update Comment'); ?></span>
- <span id="replybtn" style="display:none;"><?php _e('Submit Reply'); ?></span></a>
- <a href="#comments-form" class="cancel button-secondary alignleft"><?php _e('Cancel'); ?></a>
- <span class="waiting spinner"></span>
- <span class="error" style="display:none;"></span>
- </p>
-
- <input type="hidden" name="action" id="action" value="" />
- <input type="hidden" name="comment_ID" id="comment_ID" value="" />
- <input type="hidden" name="comment_post_ID" id="comment_post_ID" value="" />
- <input type="hidden" name="status" id="status" value="" />
- <input type="hidden" name="position" id="position" value="<?php echo $position; ?>" />
- <input type="hidden" name="checkbox" id="checkbox" value="<?php echo $checkbox ? 1 : 0; ?>" />
- <input type="hidden" name="mode" id="mode" value="<?php echo esc_attr($mode); ?>" />
- <?php
- wp_nonce_field( 'replyto-comment', '_ajax_nonce-replyto-comment', false );
- if ( current_user_can( 'unfiltered_html' ) )
- wp_nonce_field( 'unfiltered-html-comment', '_wp_unfiltered_html_comment', false );
- ?>
- </fieldset>
-<?php if ( $table_row ) : ?>
-</td></tr></tbody></table>
-<?php else : ?>
-</div></div>
-<?php endif; ?>
-</form>
-<?php
-}
-
-/**
- * Output 'undo move to trash' text for comments
- *
- * @since 2.9.0
- */
-function wp_comment_trashnotice() {
-?>
-<div class="hidden" id="trash-undo-holder">
- <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>
-</div>
-<div class="hidden" id="spam-undo-holder">
- <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>
-</div>
-<?php
-}
-
-/**
- * Outputs a post's public meta data in the Custom Fields meta box.
- *
- * @since 1.2.0
- *
- * @param array $meta
- */
-function list_meta( $meta ) {
- // Exit if no meta
- if ( ! $meta ) {
- echo '
-<table id="list-table" style="display: none;">
- <thead>
- <tr>
- <th class="left">' . _x( 'Name', 'meta name' ) . '</th>
- <th>' . __( 'Value' ) . '</th>
- </tr>
- </thead>
- <tbody id="the-list" data-wp-lists="list:meta">
- <tr><td></td></tr>
- </tbody>
-</table>'; //TBODY needed for list-manipulation JS
- return;
- }
- $count = 0;
-?>
-<table id="list-table">
- <thead>
- <tr>
- <th class="left"><?php _ex( 'Name', 'meta name' ) ?></th>
- <th><?php _e( 'Value' ) ?></th>
- </tr>
- </thead>
- <tbody id='the-list' data-wp-lists='list:meta'>
-<?php
- foreach ( $meta as $entry )
- echo _list_meta_row( $entry, $count );
-?>
- </tbody>
-</table>
-<?php
-}
-
-/**
- * Outputs a single row of public meta data in the Custom Fields meta box.
- *
- * @since 2.5.0
- *
- * @staticvar string $update_nonce
- *
- * @param array $entry
- * @param int $count
- * @return string
- */
-function _list_meta_row( $entry, &$count ) {
- static $update_nonce = '';
-
- if ( is_protected_meta( $entry['meta_key'], 'post' ) )
- return '';
-
- if ( ! $update_nonce )
- $update_nonce = wp_create_nonce( 'add-meta' );
-
- $r = '';
- ++ $count;
-
- if ( is_serialized( $entry['meta_value'] ) ) {
- if ( is_serialized_string( $entry['meta_value'] ) ) {
- // This is a serialized string, so we should display it.
- $entry['meta_value'] = maybe_unserialize( $entry['meta_value'] );
- } else {
- // This is a serialized array/object so we should NOT display it.
- --$count;
- return '';
- }
- }
-
- $entry['meta_key'] = esc_attr($entry['meta_key']);
- $entry['meta_value'] = esc_textarea( $entry['meta_value'] ); // using a <textarea />
- $entry['meta_id'] = (int) $entry['meta_id'];
-
- $delete_nonce = wp_create_nonce( 'delete-meta_' . $entry['meta_id'] );
-
- $r .= "\n\t<tr id='meta-{$entry['meta_id']}'>";
- $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']}' />";
-
- $r .= "\n\t\t<div class='submit'>";
- $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" ) );
- $r .= "\n\t\t";
- $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" ) );
- $r .= "</div>";
- $r .= wp_nonce_field( 'change-meta', '_ajax_nonce', false, false );
- $r .= "</td>";
-
- $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>";
- return $r;
-}
-
-/**
- * Prints the form in the Custom Fields meta box.
- *
- * @since 1.2.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param WP_Post $post Optional. The post being edited.
- */
-function meta_form( $post = null ) {
- global $wpdb;
- $post = get_post( $post );
-
- /**
- * Filter values for the meta key dropdown in the Custom Fields meta box.
- *
- * Returning a non-null value will effectively short-circuit and avoid a
- * potentially expensive query against postmeta.
- *
- * @since 4.4.0
- *
- * @param array|null $keys Pre-defined meta keys to be used in place of a postmeta query. Default null.
- */
- $keys = apply_filters( 'postmeta_form_keys', null );
-
- if ( null === $keys ) {
- /**
- * Filter the number of custom fields to retrieve for the drop-down
- * in the Custom Fields meta box.
- *
- * @since 2.1.0
- *
- * @param int $limit Number of custom fields to retrieve. Default 30.
- */
- $limit = apply_filters( 'postmeta_form_limit', 30 );
- $sql = "SELECT DISTINCT meta_key
- FROM $wpdb->postmeta
- WHERE meta_key NOT BETWEEN '_' AND '_z'
- HAVING meta_key NOT LIKE %s
- ORDER BY meta_key
- LIMIT %d";
- $keys = $wpdb->get_col( $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', $limit ) );
- }
-
- if ( $keys ) {
- natcasesort( $keys );
- $meta_key_input_id = 'metakeyselect';
- } else {
- $meta_key_input_id = 'metakeyinput';
- }
-?>
-<p><strong><?php _e( 'Add New Custom Field:' ) ?></strong></p>
-<table id="newmeta">
-<thead>
-<tr>
-<th class="left"><label for="<?php echo $meta_key_input_id; ?>"><?php _ex( 'Name', 'meta name' ) ?></label></th>
-<th><label for="metavalue"><?php _e( 'Value' ) ?></label></th>
-</tr>
-</thead>
-
-<tbody>
-<tr>
-<td id="newmetaleft" class="left">
-<?php if ( $keys ) { ?>
-<select id="metakeyselect" name="metakeyselect">
-<option value="#NONE#"><?php _e( '— Select —' ); ?></option>
-<?php
-
- foreach ( $keys as $key ) {
- if ( is_protected_meta( $key, 'post' ) || ! current_user_can( 'add_post_meta', $post->ID, $key ) )
- continue;
- echo "\n<option value='" . esc_attr($key) . "'>" . esc_html($key) . "</option>";
- }
-?>
-</select>
-<input class="hide-if-js" type="text" id="metakeyinput" name="metakeyinput" value="" />
-<a href="#postcustomstuff" class="hide-if-no-js" onclick="jQuery('#metakeyinput, #metakeyselect, #enternew, #cancelnew').toggle();return false;">
-<span id="enternew"><?php _e('Enter new'); ?></span>
-<span id="cancelnew" class="hidden"><?php _e('Cancel'); ?></span></a>
-<?php } else { ?>
-<input type="text" id="metakeyinput" name="metakeyinput" value="" />
-<?php } ?>
-</td>
-<td><textarea id="metavalue" name="metavalue" rows="2" cols="25"></textarea></td>
-</tr>
-
-<tr><td colspan="2">
-<div class="submit">
-<?php submit_button( __( 'Add Custom Field' ), 'secondary', 'addmeta', false, array( 'id' => 'newmeta-submit', 'data-wp-lists' => 'add:the-list:newmeta' ) ); ?>
-</div>
-<?php wp_nonce_field( 'add-meta', '_ajax_nonce-add-meta', false ); ?>
-</td></tr>
-</tbody>
-</table>
-<?php
-
-}
-
-/**
- * Print out HTML form date elements for editing post or comment publish date.
- *
- * @since 0.71
- * @since 4.4.0 Converted to use get_comment() instead of the global `$comment`.
- *
- * @global WP_Locale $wp_locale
- *
- * @param int|bool $edit Accepts 1|true for editing the date, 0|false for adding the date.
- * @param int|bool $for_post Accepts 1|true for applying the date to a post, 0|false for a comment.
- * @param int $tab_index The tabindex attribute to add. Default 0.
- * @param int|bool $multi Optional. Whether the additional fields and buttons should be added.
- * Default 0|false.
- */
-function touch_time( $edit = 1, $for_post = 1, $tab_index = 0, $multi = 0 ) {
- global $wp_locale;
- $post = get_post();
-
- if ( $for_post )
- $edit = ! ( in_array($post->post_status, array('draft', 'pending') ) && (!$post->post_date_gmt || '0000-00-00 00:00:00' == $post->post_date_gmt ) );
-
- $tab_index_attribute = '';
- if ( (int) $tab_index > 0 )
- $tab_index_attribute = " tabindex=\"$tab_index\"";
-
- // todo: Remove this?
- // echo '<label for="timestamp" style="display: block;"><input type="checkbox" class="checkbox" name="edit_date" value="1" id="timestamp"'.$tab_index_attribute.' /> '.__( 'Edit timestamp' ).'</label><br />';
-
- $time_adj = current_time('timestamp');
- $post_date = ($for_post) ? $post->post_date : get_comment()->comment_date;
- $jj = ($edit) ? mysql2date( 'd', $post_date, false ) : gmdate( 'd', $time_adj );
- $mm = ($edit) ? mysql2date( 'm', $post_date, false ) : gmdate( 'm', $time_adj );
- $aa = ($edit) ? mysql2date( 'Y', $post_date, false ) : gmdate( 'Y', $time_adj );
- $hh = ($edit) ? mysql2date( 'H', $post_date, false ) : gmdate( 'H', $time_adj );
- $mn = ($edit) ? mysql2date( 'i', $post_date, false ) : gmdate( 'i', $time_adj );
- $ss = ($edit) ? mysql2date( 's', $post_date, false ) : gmdate( 's', $time_adj );
-
- $cur_jj = gmdate( 'd', $time_adj );
- $cur_mm = gmdate( 'm', $time_adj );
- $cur_aa = gmdate( 'Y', $time_adj );
- $cur_hh = gmdate( 'H', $time_adj );
- $cur_mn = gmdate( 'i', $time_adj );
-
- $month = '<label><span class="screen-reader-text">' . __( 'Month' ) . '</span><select ' . ( $multi ? '' : 'id="mm" ' ) . 'name="mm"' . $tab_index_attribute . ">\n";
- for ( $i = 1; $i < 13; $i = $i +1 ) {
- $monthnum = zeroise($i, 2);
- $monthtext = $wp_locale->get_month_abbrev( $wp_locale->get_month( $i ) );
- $month .= "\t\t\t" . '<option value="' . $monthnum . '" data-text="' . $monthtext . '" ' . selected( $monthnum, $mm, false ) . '>';
- /* translators: 1: month number (01, 02, etc.), 2: month abbreviation */
- $month .= sprintf( __( '%1$s-%2$s' ), $monthnum, $monthtext ) . "</option>\n";
- }
- $month .= '</select></label>';
-
- $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>';
- $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>';
- $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>';
- $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>';
-
- echo '<div class="timestamp-wrap">';
- /* translators: 1: month, 2: day, 3: year, 4: hour, 5: minute */
- printf( __( '%1$s %2$s, %3$s @ %4$s:%5$s' ), $month, $day, $year, $hour, $minute );
-
- echo '</div><input type="hidden" id="ss" name="ss" value="' . $ss . '" />';
-
- if ( $multi ) return;
-
- echo "\n\n";
- $map = array(
- 'mm' => array( $mm, $cur_mm ),
- 'jj' => array( $jj, $cur_jj ),
- 'aa' => array( $aa, $cur_aa ),
- 'hh' => array( $hh, $cur_hh ),
- 'mn' => array( $mn, $cur_mn ),
- );
- foreach ( $map as $timeunit => $value ) {
- list( $unit, $curr ) = $value;
-
- echo '<input type="hidden" id="hidden_' . $timeunit . '" name="hidden_' . $timeunit . '" value="' . $unit . '" />' . "\n";
- $cur_timeunit = 'cur_' . $timeunit;
- echo '<input type="hidden" id="' . $cur_timeunit . '" name="' . $cur_timeunit . '" value="' . $curr . '" />' . "\n";
- }
-?>
-
-<p>
-<a href="#edit_timestamp" class="save-timestamp hide-if-no-js button"><?php _e('OK'); ?></a>
-<a href="#edit_timestamp" class="cancel-timestamp hide-if-no-js button-cancel"><?php _e('Cancel'); ?></a>
-</p>
-<?php
-}
-
-/**
- * Print out option HTML elements for the page templates drop-down.
- *
- * @since 1.5.0
- *
- * @param string $default Optional. The template file name. Default empty.
- */
-function page_template_dropdown( $default = '' ) {
- $templates = get_page_templates( get_post() );
- ksort( $templates );
- foreach ( array_keys( $templates ) as $template ) {
- $selected = selected( $default, $templates[ $template ], false );
- echo "\n\t<option value='" . $templates[ $template ] . "' $selected>$template</option>";
- }
-}
-
-/**
- * Print out option HTML elements for the page parents drop-down.
- *
- * @since 1.5.0
- * @since 4.4.0 `$post` argument was added.
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $default Optional. The default page ID to be pre-selected. Default 0.
- * @param int $parent Optional. The parent page ID. Default 0.
- * @param int $level Optional. Page depth level. Default 0.
- * @param int|WP_Post $post Post ID or WP_Post object.
- *
- * @return null|false Boolean False if page has no children, otherwise print out html elements
- */
-function parent_dropdown( $default = 0, $parent = 0, $level = 0, $post = null ) {
- global $wpdb;
- $post = get_post( $post );
- $items = $wpdb->get_results( $wpdb->prepare("SELECT ID, post_parent, post_title FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'page' ORDER BY menu_order", $parent) );
-
- if ( $items ) {
- foreach ( $items as $item ) {
- // A page cannot be its own parent.
- if ( $post && $post->ID && $item->ID == $post->ID )
- continue;
-
- $pad = str_repeat( ' ', $level * 3 );
- $selected = selected( $default, $item->ID, false );
-
- echo "\n\t<option class='level-$level' value='$item->ID' $selected>$pad " . esc_html($item->post_title) . "</option>";
- parent_dropdown( $default, $item->ID, $level +1 );
- }
- } else {
- return false;
- }
-}
-
-/**
- * Print out option html elements for role selectors.
- *
- * @since 2.1.0
- *
- * @param string $selected Slug for the role that should be already selected.
- */
-function wp_dropdown_roles( $selected = '' ) {
- $p = '';
- $r = '';
-
- $editable_roles = array_reverse( get_editable_roles() );
-
- foreach ( $editable_roles as $role => $details ) {
- $name = translate_user_role($details['name'] );
- if ( $selected == $role ) // preselect specified role
- $p = "\n\t<option selected='selected' value='" . esc_attr($role) . "'>$name</option>";
- else
- $r .= "\n\t<option value='" . esc_attr($role) . "'>$name</option>";
- }
- echo $p . $r;
-}
-
-/**
- * Outputs the form used by the importers to accept the data to be imported
- *
- * @since 2.0.0
- *
- * @param string $action The action attribute for the form.
- */
-function wp_import_upload_form( $action ) {
-
- /**
- * Filter the maximum allowed upload size for import files.
- *
- * @since 2.3.0
- *
- * @see wp_max_upload_size()
- *
- * @param int $max_upload_size Allowed upload size. Default 1 MB.
- */
- $bytes = apply_filters( 'import_upload_size_limit', wp_max_upload_size() );
- $size = size_format( $bytes );
- $upload_dir = wp_upload_dir();
- if ( ! empty( $upload_dir['error'] ) ) :
- ?><div class="error"><p><?php _e('Before you can upload your import file, you will need to fix the following error:'); ?></p>
- <p><strong><?php echo $upload_dir['error']; ?></strong></p></div><?php
- else :
-?>
-<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' ) ); ?>">
-<p>
-<label for="upload"><?php _e( 'Choose a file from your computer:' ); ?></label> (<?php printf( __('Maximum size: %s' ), $size ); ?>)
-<input type="file" id="upload" name="import" size="25" />
-<input type="hidden" name="action" value="save" />
-<input type="hidden" name="max_file_size" value="<?php echo $bytes; ?>" />
-</p>
-<?php submit_button( __('Upload file and import'), 'primary' ); ?>
-</form>
-<?php
- endif;
-}
-
-/**
- * Adds a meta box to one or more screens.
- *
- * @since 2.5.0
- * @since 4.4.0 The `$screen` parameter now accepts an array of screen IDs.
- *
- * @global array $wp_meta_boxes
- *
- * @param string $id Meta box ID (used in the 'id' attribute for the meta box).
- * @param string $title Title of the meta box.
- * @param callable $callback Function that fills the box with the desired content.
- * The function should echo its output.
- * @param string|array|WP_Screen $screen Optional. The screen or screens on which to show the box
- * (such as a post type, 'link', or 'comment'). Accepts a single
- * screen ID, WP_Screen object, or array of screen IDs. Default
- * is the current screen.
- * @param string $context Optional. The context within the screen where the boxes
- * should display. Available contexts vary from screen to
- * screen. Post edit screen contexts include 'normal', 'side',
- * and 'advanced'. Comments screen contexts include 'normal'
- * and 'side'. Menus meta boxes (accordion sections) all use
- * the 'side' context. Global default is 'advanced'.
- * @param string $priority Optional. The priority within the context where the boxes
- * should show ('high', 'low'). Default 'default'.
- * @param array $callback_args Optional. Data that should be set as the $args property
- * of the box array (which is the second parameter passed
- * to your callback). Default null.
- */
-function add_meta_box( $id, $title, $callback, $screen = null, $context = 'advanced', $priority = 'default', $callback_args = null ) {
- global $wp_meta_boxes;
-
- if ( empty( $screen ) ) {
- $screen = get_current_screen();
- } elseif ( is_string( $screen ) ) {
- $screen = convert_to_screen( $screen );
- } elseif ( is_array( $screen ) ) {
- foreach ( $screen as $single_screen ) {
- add_meta_box( $id, $title, $callback, $single_screen, $context, $priority, $callback_args );
- }
- }
-
- if ( ! isset( $screen->id ) ) {
- return;
- }
-
- $page = $screen->id;
-
- if ( !isset($wp_meta_boxes) )
- $wp_meta_boxes = array();
- if ( !isset($wp_meta_boxes[$page]) )
- $wp_meta_boxes[$page] = array();
- if ( !isset($wp_meta_boxes[$page][$context]) )
- $wp_meta_boxes[$page][$context] = array();
-
- foreach ( array_keys($wp_meta_boxes[$page]) as $a_context ) {
- foreach ( array('high', 'core', 'default', 'low') as $a_priority ) {
- if ( !isset($wp_meta_boxes[$page][$a_context][$a_priority][$id]) )
- continue;
-
- // If a core box was previously added or removed by a plugin, don't add.
- if ( 'core' == $priority ) {
- // If core box previously deleted, don't add
- if ( false === $wp_meta_boxes[$page][$a_context][$a_priority][$id] )
- return;
-
- /*
- * If box was added with default priority, give it core priority to
- * maintain sort order.
- */
- if ( 'default' == $a_priority ) {
- $wp_meta_boxes[$page][$a_context]['core'][$id] = $wp_meta_boxes[$page][$a_context]['default'][$id];
- unset($wp_meta_boxes[$page][$a_context]['default'][$id]);
- }
- return;
- }
- // If no priority given and id already present, use existing priority.
- if ( empty($priority) ) {
- $priority = $a_priority;
- /*
- * Else, if we're adding to the sorted priority, we don't know the title
- * or callback. Grab them from the previously added context/priority.
- */
- } elseif ( 'sorted' == $priority ) {
- $title = $wp_meta_boxes[$page][$a_context][$a_priority][$id]['title'];
- $callback = $wp_meta_boxes[$page][$a_context][$a_priority][$id]['callback'];
- $callback_args = $wp_meta_boxes[$page][$a_context][$a_priority][$id]['args'];
- }
- // An id can be in only one priority and one context.
- if ( $priority != $a_priority || $context != $a_context )
- unset($wp_meta_boxes[$page][$a_context][$a_priority][$id]);
- }
- }
-
- if ( empty($priority) )
- $priority = 'low';
-
- if ( !isset($wp_meta_boxes[$page][$context][$priority]) )
- $wp_meta_boxes[$page][$context][$priority] = array();
-
- $wp_meta_boxes[$page][$context][$priority][$id] = array('id' => $id, 'title' => $title, 'callback' => $callback, 'args' => $callback_args);
-}
-
-/**
- * Meta-Box template function
- *
- * @since 2.5.0
- *
- * @global array $wp_meta_boxes
- *
- * @staticvar bool $already_sorted
- * @param string|WP_Screen $screen Screen identifier
- * @param string $context box context
- * @param mixed $object gets passed to the box callback function as first parameter
- * @return int number of meta_boxes
- */
-function do_meta_boxes( $screen, $context, $object ) {
- global $wp_meta_boxes;
- static $already_sorted = false;
-
- if ( empty( $screen ) )
- $screen = get_current_screen();
- elseif ( is_string( $screen ) )
- $screen = convert_to_screen( $screen );
-
- $page = $screen->id;
-
- $hidden = get_hidden_meta_boxes( $screen );
-
- printf('<div id="%s-sortables" class="meta-box-sortables">', htmlspecialchars($context));
-
- // Grab the ones the user has manually sorted. Pull them out of their previous context/priority and into the one the user chose
- if ( ! $already_sorted && $sorted = get_user_option( "meta-box-order_$page" ) ) {
- foreach ( $sorted as $box_context => $ids ) {
- foreach ( explode( ',', $ids ) as $id ) {
- if ( $id && 'dashboard_browser_nag' !== $id ) {
- add_meta_box( $id, null, null, $screen, $box_context, 'sorted' );
- }
- }
- }
- }
-
- $already_sorted = true;
-
- $i = 0;
-
- if ( isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
- foreach ( array( 'high', 'sorted', 'core', 'default', 'low' ) as $priority ) {
- if ( isset( $wp_meta_boxes[ $page ][ $context ][ $priority ]) ) {
- foreach ( (array) $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) {
- if ( false == $box || ! $box['title'] )
- continue;
- $i++;
- $hidden_class = in_array($box['id'], $hidden) ? ' hide-if-js' : '';
- echo '<div id="' . $box['id'] . '" class="postbox ' . postbox_classes($box['id'], $page) . $hidden_class . '" ' . '>' . "\n";
- if ( 'dashboard_browser_nag' != $box['id'] ) {
- echo '<button type="button" class="handlediv button-link" aria-expanded="true">';
- echo '<span class="screen-reader-text">' . sprintf( __( 'Toggle panel: %s' ), $box['title'] ) . '</span>';
- echo '<span class="toggle-indicator" aria-hidden="true"></span>';
- echo '</button>';
- }
- echo "<h2 class='hndle'><span>{$box['title']}</span></h2>\n";
- echo '<div class="inside">' . "\n";
- call_user_func($box['callback'], $object, $box);
- echo "</div>\n";
- echo "</div>\n";
- }
- }
- }
- }
-
- echo "</div>";
-
- return $i;
-
-}
-
-/**
- * Removes a meta box from one or more screens.
- *
- * @since 2.6.0
- * @since 4.4.0 The `$screen` parameter now accepts an array of screen IDs.
- *
- * @global array $wp_meta_boxes
- *
- * @param string $id Meta box ID (used in the 'id' attribute for the meta box).
- * @param string|array|WP_Screen $screen The screen or screens on which the meta box is shown (such as a
- * post type, 'link', or 'comment'). Accepts a single screen ID,
- * WP_Screen object, or array of screen IDs.
- * @param string $context Optional. The context within the screen where the boxes
- * should display. Available contexts vary from screen to
- * screen. Post edit screen contexts include 'normal', 'side',
- * and 'advanced'. Comments screen contexts include 'normal'
- * and 'side'. Menus meta boxes (accordion sections) all use
- * the 'side' context. Global default is 'advanced'.
- */
-function remove_meta_box( $id, $screen, $context ) {
- global $wp_meta_boxes;
-
- if ( empty( $screen ) ) {
- $screen = get_current_screen();
- } elseif ( is_string( $screen ) ) {
- $screen = convert_to_screen( $screen );
- } elseif ( is_array( $screen ) ) {
- foreach ( $screen as $single_screen ) {
- remove_meta_box( $id, $single_screen, $context );
- }
- }
-
- if ( ! isset( $screen->id ) ) {
- return;
- }
-
- $page = $screen->id;
-
- if ( !isset($wp_meta_boxes) )
- $wp_meta_boxes = array();
- if ( !isset($wp_meta_boxes[$page]) )
- $wp_meta_boxes[$page] = array();
- if ( !isset($wp_meta_boxes[$page][$context]) )
- $wp_meta_boxes[$page][$context] = array();
-
- foreach ( array('high', 'core', 'default', 'low') as $priority )
- $wp_meta_boxes[$page][$context][$priority][$id] = false;
-}
-
-/**
- * Meta Box Accordion Template Function
- *
- * Largely made up of abstracted code from {@link do_meta_boxes()}, this
- * function serves to build meta boxes as list items for display as
- * a collapsible accordion.
- *
- * @since 3.6.0
- *
- * @uses global $wp_meta_boxes Used to retrieve registered meta boxes.
- *
- * @param string|object $screen The screen identifier.
- * @param string $context The meta box context.
- * @param mixed $object gets passed to the section callback function as first parameter.
- * @return int number of meta boxes as accordion sections.
- */
-function do_accordion_sections( $screen, $context, $object ) {
- global $wp_meta_boxes;
-
- wp_enqueue_script( 'accordion' );
-
- if ( empty( $screen ) )
- $screen = get_current_screen();
- elseif ( is_string( $screen ) )
- $screen = convert_to_screen( $screen );
-
- $page = $screen->id;
-
- $hidden = get_hidden_meta_boxes( $screen );
- ?>
- <div id="side-sortables" class="accordion-container">
- <ul class="outer-border">
- <?php
- $i = 0;
- $first_open = false;
-
- if ( isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
- foreach ( array( 'high', 'core', 'default', 'low' ) as $priority ) {
- if ( isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) {
- foreach ( $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) {
- if ( false == $box || ! $box['title'] )
- continue;
- $i++;
- $hidden_class = in_array( $box['id'], $hidden ) ? 'hide-if-js' : '';
-
- $open_class = '';
- if ( ! $first_open && empty( $hidden_class ) ) {
- $first_open = true;
- $open_class = 'open';
- }
- ?>
- <li class="control-section accordion-section <?php echo $hidden_class; ?> <?php echo $open_class; ?> <?php echo esc_attr( $box['id'] ); ?>" id="<?php echo esc_attr( $box['id'] ); ?>">
- <h3 class="accordion-section-title hndle" tabindex="0">
- <?php echo esc_html( $box['title'] ); ?>
- <span class="screen-reader-text"><?php _e( 'Press return or enter to open this section' ); ?></span>
- </h3>
- <div class="accordion-section-content <?php postbox_classes( $box['id'], $page ); ?>">
- <div class="inside">
- <?php call_user_func( $box['callback'], $object, $box ); ?>
- </div><!-- .inside -->
- </div><!-- .accordion-section-content -->
- </li><!-- .accordion-section -->
- <?php
- }
- }
- }
- }
- ?>
- </ul><!-- .outer-border -->
- </div><!-- .accordion-container -->
- <?php
- return $i;
-}
-
-/**
- * Add a new section to a settings page.
- *
- * Part of the Settings API. Use this to define new settings sections for an admin page.
- * Show settings sections in your admin page callback function with do_settings_sections().
- * Add settings fields to your section with add_settings_field()
- *
- * The $callback argument should be the name of a function that echoes out any
- * content you want to show at the top of the settings section before the actual
- * fields. It can output nothing if you want.
- *
- * @since 2.7.0
- *
- * @global $wp_settings_sections Storage array of all settings sections added to admin pages
- *
- * @param string $id Slug-name to identify the section. Used in the 'id' attribute of tags.
- * @param string $title Formatted title of the section. Shown as the heading for the section.
- * @param string $callback Function that echos out any content at the top of the section (between heading and fields).
- * @param string $page The slug-name of the settings page on which to show the section. Built-in pages include 'general', 'reading', 'writing', 'discussion', 'media', etc. Create your own using add_options_page();
- */
-function add_settings_section($id, $title, $callback, $page) {
- global $wp_settings_sections;
-
- if ( 'misc' == $page ) {
- _deprecated_argument( __FUNCTION__, '3.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) );
- $page = 'general';
- }
-
- if ( 'privacy' == $page ) {
- _deprecated_argument( __FUNCTION__, '3.5', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) );
- $page = 'reading';
- }
-
- $wp_settings_sections[$page][$id] = array('id' => $id, 'title' => $title, 'callback' => $callback);
-}
-
-/**
- * Add a new field to a section of a settings page
- *
- * Part of the Settings API. Use this to define a settings field that will show
- * as part of a settings section inside a settings page. The fields are shown using
- * do_settings_fields() in do_settings-sections()
- *
- * The $callback argument should be the name of a function that echoes out the
- * html input tags for this setting field. Use get_option() to retrieve existing
- * values to show.
- *
- * @since 2.7.0
- * @since 4.2.0 The `$class` argument was added.
- *
- * @global $wp_settings_fields Storage array of settings fields and info about their pages/sections
- *
- * @param string $id Slug-name to identify the field. Used in the 'id' attribute of tags.
- * @param string $title Formatted title of the field. Shown as the label for the field
- * during output.
- * @param string $callback Function that fills the field with the desired form inputs. The
- * function should echo its output.
- * @param string $page The slug-name of the settings page on which to show the section
- * (general, reading, writing, ...).
- * @param string $section Optional. The slug-name of the section of the settings page
- * in which to show the box. Default 'default'.
- * @param array $args {
- * Optional. Extra arguments used when outputting the field.
- *
- * @type string $label_for When supplied, the setting title will be wrapped
- * in a `<label>` element, its `for` attribute populated
- * with this value.
- * @type string $class CSS Class to be added to the `<tr>` element when the
- * field is output.
- * }
- */
-function add_settings_field($id, $title, $callback, $page, $section = 'default', $args = array()) {
- global $wp_settings_fields;
-
- if ( 'misc' == $page ) {
- _deprecated_argument( __FUNCTION__, '3.0', __( 'The miscellaneous options group has been removed. Use another settings group.' ) );
- $page = 'general';
- }
-
- if ( 'privacy' == $page ) {
- _deprecated_argument( __FUNCTION__, '3.5', __( 'The privacy options group has been removed. Use another settings group.' ) );
- $page = 'reading';
- }
-
- $wp_settings_fields[$page][$section][$id] = array('id' => $id, 'title' => $title, 'callback' => $callback, 'args' => $args);
-}
-
-/**
- * Prints out all settings sections added to a particular settings page
- *
- * Part of the Settings API. Use this in a settings page callback function
- * to output all the sections and fields that were added to that $page with
- * add_settings_section() and add_settings_field()
- *
- * @global $wp_settings_sections Storage array of all settings sections added to admin pages
- * @global $wp_settings_fields Storage array of settings fields and info about their pages/sections
- * @since 2.7.0
- *
- * @param string $page The slug name of the page whose settings sections you want to output
- */
-function do_settings_sections( $page ) {
- global $wp_settings_sections, $wp_settings_fields;
-
- if ( ! isset( $wp_settings_sections[$page] ) )
- return;
-
- foreach ( (array) $wp_settings_sections[$page] as $section ) {
- if ( $section['title'] )
- echo "<h2>{$section['title']}</h2>\n";
-
- if ( $section['callback'] )
- call_user_func( $section['callback'], $section );
-
- if ( ! isset( $wp_settings_fields ) || !isset( $wp_settings_fields[$page] ) || !isset( $wp_settings_fields[$page][$section['id']] ) )
- continue;
- echo '<table class="form-table">';
- do_settings_fields( $page, $section['id'] );
- echo '</table>';
- }
-}
-
-/**
- * Print out the settings fields for a particular settings section
- *
- * Part of the Settings API. Use this in a settings page to output
- * a specific section. Should normally be called by do_settings_sections()
- * rather than directly.
- *
- * @global $wp_settings_fields Storage array of settings fields and their pages/sections
- *
- * @since 2.7.0
- *
- * @param string $page Slug title of the admin page who's settings fields you want to show.
- * @param string $section Slug title of the settings section who's fields you want to show.
- */
-function do_settings_fields($page, $section) {
- global $wp_settings_fields;
-
- if ( ! isset( $wp_settings_fields[$page][$section] ) )
- return;
-
- foreach ( (array) $wp_settings_fields[$page][$section] as $field ) {
- $class = '';
-
- if ( ! empty( $field['args']['class'] ) ) {
- $class = ' class="' . esc_attr( $field['args']['class'] ) . '"';
- }
-
- echo "<tr{$class}>";
-
- if ( ! empty( $field['args']['label_for'] ) ) {
- echo '<th scope="row"><label for="' . esc_attr( $field['args']['label_for'] ) . '">' . $field['title'] . '</label></th>';
- } else {
- echo '<th scope="row">' . $field['title'] . '</th>';
- }
-
- echo '<td>';
- call_user_func($field['callback'], $field['args']);
- echo '</td>';
- echo '</tr>';
- }
-}
-
-/**
- * Register a settings error to be displayed to the user
- *
- * Part of the Settings API. Use this to show messages to users about settings validation
- * problems, missing settings or anything else.
- *
- * Settings errors should be added inside the $sanitize_callback function defined in
- * register_setting() for a given setting to give feedback about the submission.
- *
- * By default messages will show immediately after the submission that generated the error.
- * Additional calls to settings_errors() can be used to show errors even when the settings
- * page is first accessed.
- *
- * @since 3.0.0
- *
- * @global array $wp_settings_errors Storage array of errors registered during this pageload
- *
- * @param string $setting Slug title of the setting to which this error applies
- * @param string $code Slug-name to identify the error. Used as part of 'id' attribute in HTML output.
- * @param string $message The formatted message text to display to the user (will be shown inside styled
- * `<div>` and `<p>` tags).
- * @param string $type Optional. Message type, controls HTML class. Accepts 'error' or 'updated'.
- * Default 'error'.
- */
-function add_settings_error( $setting, $code, $message, $type = 'error' ) {
- global $wp_settings_errors;
-
- $wp_settings_errors[] = array(
- 'setting' => $setting,
- 'code' => $code,
- 'message' => $message,
- 'type' => $type
- );
-}
-
-/**
- * Fetch settings errors registered by add_settings_error()
- *
- * Checks the $wp_settings_errors array for any errors declared during the current
- * pageload and returns them.
- *
- * If changes were just submitted ($_GET['settings-updated']) and settings errors were saved
- * to the 'settings_errors' transient then those errors will be returned instead. This
- * is used to pass errors back across pageloads.
- *
- * Use the $sanitize argument to manually re-sanitize the option before returning errors.
- * This is useful if you have errors or notices you want to show even when the user
- * hasn't submitted data (i.e. when they first load an options page, or in admin_notices action hook)
- *
- * @since 3.0.0
- *
- * @global array $wp_settings_errors Storage array of errors registered during this pageload
- *
- * @param string $setting Optional slug title of a specific setting who's errors you want.
- * @param boolean $sanitize Whether to re-sanitize the setting value before returning errors.
- * @return array Array of settings errors
- */
-function get_settings_errors( $setting = '', $sanitize = false ) {
- global $wp_settings_errors;
-
- /*
- * If $sanitize is true, manually re-run the sanitization for this option
- * This allows the $sanitize_callback from register_setting() to run, adding
- * any settings errors you want to show by default.
- */
- if ( $sanitize )
- sanitize_option( $setting, get_option( $setting ) );
-
- // If settings were passed back from options.php then use them.
- if ( isset( $_GET['settings-updated'] ) && $_GET['settings-updated'] && get_transient( 'settings_errors' ) ) {
- $wp_settings_errors = array_merge( (array) $wp_settings_errors, get_transient( 'settings_errors' ) );
- delete_transient( 'settings_errors' );
- }
-
- // Check global in case errors have been added on this pageload.
- if ( ! count( $wp_settings_errors ) )
- return array();
-
- // Filter the results to those of a specific setting if one was set.
- if ( $setting ) {
- $setting_errors = array();
- foreach ( (array) $wp_settings_errors as $key => $details ) {
- if ( $setting == $details['setting'] )
- $setting_errors[] = $wp_settings_errors[$key];
- }
- return $setting_errors;
- }
-
- return $wp_settings_errors;
-}
-
-/**
- * Display settings errors registered by {@see add_settings_error()}.
- *
- * Part of the Settings API. Outputs a div for each error retrieved by
- * {@see get_settings_errors()}.
- *
- * This is called automatically after a settings page based on the
- * Settings API is submitted. Errors should be added during the validation
- * callback function for a setting defined in {@see register_setting()}
- *
- * The $sanitize option is passed into {@see get_settings_errors()} and will
- * re-run the setting sanitization
- * on its current value.
- *
- * The $hide_on_update option will cause errors to only show when the settings
- * page is first loaded. if the user has already saved new values it will be
- * hidden to avoid repeating messages already shown in the default error
- * reporting after submission. This is useful to show general errors like
- * missing settings when the user arrives at the settings page.
- *
- * @since 3.0.0
- *
- * @param string $setting Optional slug title of a specific setting who's errors you want.
- * @param bool $sanitize Whether to re-sanitize the setting value before returning errors.
- * @param bool $hide_on_update If set to true errors will not be shown if the settings page has already been submitted.
- */
-function settings_errors( $setting = '', $sanitize = false, $hide_on_update = false ) {
-
- if ( $hide_on_update && ! empty( $_GET['settings-updated'] ) )
- return;
-
- $settings_errors = get_settings_errors( $setting, $sanitize );
-
- if ( empty( $settings_errors ) )
- return;
-
- $output = '';
- foreach ( $settings_errors as $key => $details ) {
- $css_id = 'setting-error-' . $details['code'];
- $css_class = $details['type'] . ' settings-error notice is-dismissible';
- $output .= "<div id='$css_id' class='$css_class'> \n";
- $output .= "<p><strong>{$details['message']}</strong></p>";
- $output .= "</div> \n";
- }
- echo $output;
-}
-
-/**
- * Outputs the modal window used for attaching media to posts or pages in the media-listing screen.
- *
- * @since 2.7.0
- *
- * @param string $found_action
- */
-function find_posts_div($found_action = '') {
-?>
- <div id="find-posts" class="find-box" style="display: none;">
- <div id="find-posts-head" class="find-box-head">
- <?php _e( 'Find Posts or Pages' ); ?>
- <div id="find-posts-close"></div>
- </div>
- <div class="find-box-inside">
- <div class="find-box-search">
- <?php if ( $found_action ) { ?>
- <input type="hidden" name="found_action" value="<?php echo esc_attr($found_action); ?>" />
- <?php } ?>
- <input type="hidden" name="affected" id="affected" value="" />
- <?php wp_nonce_field( 'find-posts', '_ajax_nonce', false ); ?>
- <label class="screen-reader-text" for="find-posts-input"><?php _e( 'Search' ); ?></label>
- <input type="text" id="find-posts-input" name="ps" value="" />
- <span class="spinner"></span>
- <input type="button" id="find-posts-search" value="<?php esc_attr_e( 'Search' ); ?>" class="button" />
- <div class="clear"></div>
- </div>
- <div id="find-posts-response"></div>
- </div>
- <div class="find-box-buttons">
- <?php submit_button( __( 'Select' ), 'button-primary alignright', 'find-posts-submit', false ); ?>
- <div class="clear"></div>
- </div>
- </div>
-<?php
-}
-
-/**
- * Display the post password.
- *
- * The password is passed through {@link esc_attr()} to ensure that it
- * is safe for placing in an html attribute.
- *
- * @since 2.7.0
- */
-function the_post_password() {
- $post = get_post();
- if ( isset( $post->post_password ) )
- echo esc_attr( $post->post_password );
-}
-
-/**
- * Get the post title.
- *
- * The post title is fetched and if it is blank then a default string is
- * returned.
- *
- * @since 2.7.0
- *
- * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
- * @return string The post title if set.
- */
-function _draft_or_post_title( $post = 0 ) {
- $title = get_the_title( $post );
- if ( empty( $title ) )
- $title = __( '(no title)' );
- return esc_html( $title );
-}
-
-/**
- * Display the search query.
- *
- * A simple wrapper to display the "s" parameter in a GET URI. This function
- * should only be used when {@link the_search_query()} cannot.
- *
- * @since 2.7.0
- */
-function _admin_search_query() {
- echo isset($_REQUEST['s']) ? esc_attr( wp_unslash( $_REQUEST['s'] ) ) : '';
-}
-
-/**
- * Generic Iframe header for use with Thickbox
- *
- * @since 2.7.0
- *
- * @global string $hook_suffix
- * @global string $admin_body_class
- * @global WP_Locale $wp_locale
- *
- * @param string $title Optional. Title of the Iframe page. Default empty.
- * @param bool $deprecated Not used.
- */
-function iframe_header( $title = '', $deprecated = false ) {
- show_admin_bar( false );
- global $hook_suffix, $admin_body_class, $wp_locale;
- $admin_body_class = preg_replace('/[^a-z0-9_-]+/i', '-', $hook_suffix);
-
- $current_screen = get_current_screen();
-
- @header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) );
- _wp_admin_html_begin();
-?>
-<title><?php bloginfo('name') ?> › <?php echo $title ?> — <?php _e('WordPress'); ?></title>
-<?php
-wp_enqueue_style( 'colors' );
-?>
-<script type="text/javascript">
-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();}}};
-function tb_close(){var win=window.dialogArguments||opener||parent||top;win.tb_remove();}
-var ajaxurl = '<?php echo admin_url( 'admin-ajax.php', 'relative' ); ?>',
- pagenow = '<?php echo $current_screen->id; ?>',
- typenow = '<?php echo $current_screen->post_type; ?>',
- adminpage = '<?php echo $admin_body_class; ?>',
- thousandsSeparator = '<?php echo addslashes( $wp_locale->number_format['thousands_sep'] ); ?>',
- decimalPoint = '<?php echo addslashes( $wp_locale->number_format['decimal_point'] ); ?>',
- isRtl = <?php echo (int) is_rtl(); ?>;
-</script>
-<?php
-/** This action is documented in wp-admin/admin-header.php */
-do_action( 'admin_enqueue_scripts', $hook_suffix );
-
-/** This action is documented in wp-admin/admin-header.php */
-do_action( "admin_print_styles-$hook_suffix" );
-
-/** This action is documented in wp-admin/admin-header.php */
-do_action( 'admin_print_styles' );
-
-/** This action is documented in wp-admin/admin-header.php */
-do_action( "admin_print_scripts-$hook_suffix" );
-
-/** This action is documented in wp-admin/admin-header.php */
-do_action( 'admin_print_scripts' );
-
-/** This action is documented in wp-admin/admin-header.php */
-do_action( "admin_head-$hook_suffix" );
-
-/** This action is documented in wp-admin/admin-header.php */
-do_action( 'admin_head' );
-
-$admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) );
-
-if ( is_rtl() )
- $admin_body_class .= ' rtl';
-
-?>
-</head>
-<?php
-/** This filter is documented in wp-admin/admin-header.php */
-$admin_body_classes = apply_filters( 'admin_body_class', '' );
-?>
-<body<?php
-/**
- * @global string $body_id
- */
-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; ?>">
-<script type="text/javascript">
-(function(){
-var c = document.body.className;
-c = c.replace(/no-js/, 'js');
-document.body.className = c;
-})();
-</script>
-<?php
-}
-
-/**
- * Generic Iframe footer for use with Thickbox
- *
- * @since 2.7.0
- */
-function iframe_footer() {
- /*
- * We're going to hide any footer output on iFrame pages,
- * but run the hooks anyway since they output JavaScript
- * or other needed content.
- */
- ?>
- <div class="hidden">
-<?php
- /** This action is documented in wp-admin/admin-footer.php */
- do_action( 'admin_footer', '' );
-
- /** This action is documented in wp-admin/admin-footer.php */
- do_action( 'admin_print_footer_scripts' );
-?>
- </div>
-<script type="text/javascript">if(typeof wpOnload=="function")wpOnload();</script>
-</body>
-</html>
-<?php
-}
-
-/**
- *
- * @param WP_Post $post
- */
-function _post_states($post) {
- $post_states = array();
- if ( isset( $_REQUEST['post_status'] ) )
- $post_status = $_REQUEST['post_status'];
- else
- $post_status = '';
-
- if ( !empty($post->post_password) )
- $post_states['protected'] = __('Password protected');
- if ( 'private' == $post->post_status && 'private' != $post_status )
- $post_states['private'] = __('Private');
- if ( 'draft' == $post->post_status && 'draft' != $post_status )
- $post_states['draft'] = __('Draft');
- if ( 'pending' == $post->post_status && 'pending' != $post_status )
- /* translators: post state */
- $post_states['pending'] = _x('Pending', 'post state');
- if ( is_sticky($post->ID) )
- $post_states['sticky'] = __('Sticky');
-
- if ( 'future' === $post->post_status ) {
- $post_states['scheduled'] = __( 'Scheduled' );
- }
-
- if ( 'page' === get_option( 'show_on_front' ) ) {
- if ( intval( get_option( 'page_on_front' ) ) === $post->ID ) {
- $post_states['page_on_front'] = __( 'Front Page' );
- }
-
- if ( intval( get_option( 'page_for_posts' ) ) === $post->ID ) {
- $post_states['page_for_posts'] = __( 'Posts Page' );
- }
- }
-
- /**
- * Filter the default post display states used in the posts list table.
- *
- * @since 2.8.0
- *
- * @param array $post_states An array of post display states.
- * @param WP_Post $post The current post object.
- */
- $post_states = apply_filters( 'display_post_states', $post_states, $post );
-
- if ( ! empty($post_states) ) {
- $state_count = count($post_states);
- $i = 0;
- echo ' — ';
- foreach ( $post_states as $state ) {
- ++$i;
- ( $i == $state_count ) ? $sep = '' : $sep = ', ';
- echo "<span class='post-state'>$state$sep</span>";
- }
- }
-
-}
-
-/**
- *
- * @param WP_Post $post
- */
-function _media_states( $post ) {
- $media_states = array();
- $stylesheet = get_option('stylesheet');
-
- if ( current_theme_supports( 'custom-header') ) {
- $meta_header = get_post_meta($post->ID, '_wp_attachment_is_custom_header', true );
- if ( ! empty( $meta_header ) && $meta_header == $stylesheet )
- $media_states[] = __( 'Header Image' );
- }
-
- if ( current_theme_supports( 'custom-background') ) {
- $meta_background = get_post_meta($post->ID, '_wp_attachment_is_custom_background', true );
- if ( ! empty( $meta_background ) && $meta_background == $stylesheet )
- $media_states[] = __( 'Background Image' );
- }
-
- if ( $post->ID == get_option( 'site_icon' ) ) {
- $media_states[] = __( 'Site Icon' );
- }
-
- /**
- * Filter the default media display states for items in the Media list table.
- *
- * @since 3.2.0
- *
- * @param array $media_states An array of media states. Default 'Header Image',
- * 'Background Image', 'Site Icon'.
- */
- $media_states = apply_filters( 'display_media_states', $media_states );
-
- if ( ! empty( $media_states ) ) {
- $state_count = count( $media_states );
- $i = 0;
- echo ' — ';
- foreach ( $media_states as $state ) {
- ++$i;
- ( $i == $state_count ) ? $sep = '' : $sep = ', ';
- echo "<span class='post-state'>$state$sep</span>";
- }
- }
-}
-
-/**
- * Test support for compressing JavaScript from PHP
- *
- * Outputs JavaScript that tests if compression from PHP works as expected
- * and sets an option with the result. Has no effect when the current user
- * is not an administrator. To run the test again the option 'can_compress_scripts'
- * has to be deleted.
- *
- * @since 2.8.0
- */
-function compression_test() {
-?>
- <script type="text/javascript">
- var testCompression = {
- get : function(test) {
- var x;
- if ( window.XMLHttpRequest ) {
- x = new XMLHttpRequest();
- } else {
- try{x=new ActiveXObject('Msxml2.XMLHTTP');}catch(e){try{x=new ActiveXObject('Microsoft.XMLHTTP');}catch(e){};}
- }
-
- if (x) {
- x.onreadystatechange = function() {
- var r, h;
- if ( x.readyState == 4 ) {
- r = x.responseText.substr(0, 18);
- h = x.getResponseHeader('Content-Encoding');
- testCompression.check(r, h, test);
- }
- };
-
- x.open('GET', ajaxurl + '?action=wp-compression-test&test='+test+'&'+(new Date()).getTime(), true);
- x.send('');
- }
- },
-
- check : function(r, h, test) {
- if ( ! r && ! test )
- this.get(1);
-
- if ( 1 == test ) {
- if ( h && ( h.match(/deflate/i) || h.match(/gzip/i) ) )
- this.get('no');
- else
- this.get(2);
-
- return;
- }
-
- if ( 2 == test ) {
- if ( '"wpCompressionTest' == r )
- this.get('yes');
- else
- this.get('no');
- }
- }
- };
- testCompression.check();
- </script>
-<?php
-}
-
-/**
- * Echoes a submit button, with provided text and appropriate class(es).
- *
- * @since 3.1.0
- *
- * @see get_submit_button()
- *
- * @param string $text The text of the button (defaults to 'Save Changes')
- * @param string $type Optional. The type and CSS class(es) of the button. Core values
- * include 'primary', 'secondary', 'delete'. Default 'primary'
- * @param string $name The HTML name of the submit button. Defaults to "submit". If no
- * id attribute is given in $other_attributes below, $name will be
- * used as the button's id.
- * @param bool $wrap True if the output button should be wrapped in a paragraph tag,
- * false otherwise. Defaults to true
- * @param array|string $other_attributes Other attributes that should be output with the button, mapping
- * attributes to their values, such as setting tabindex to 1, etc.
- * These key/value attribute pairs will be output as attribute="value",
- * where attribute is the key. Other attributes can also be provided
- * as a string such as 'tabindex="1"', though the array format is
- * preferred. Default null.
- */
-function submit_button( $text = null, $type = 'primary', $name = 'submit', $wrap = true, $other_attributes = null ) {
- echo get_submit_button( $text, $type, $name, $wrap, $other_attributes );
-}
-
-/**
- * Returns a submit button, with provided text and appropriate class
- *
- * @since 3.1.0
- *
- * @param string $text Optional. The text of the button. Default 'Save Changes'.
- * @param string $type Optional. The type of button. Accepts 'primary', 'secondary',
- * or 'delete'. Default 'primary large'.
- * @param string $name Optional. The HTML name of the submit button. Defaults to "submit".
- * If no id attribute is given in $other_attributes below, `$name` will
- * be used as the button's id. Default 'submit'.
- * @param bool $wrap Optional. True if the output button should be wrapped in a paragraph
- * tag, false otherwise. Default true.
- * @param array|string $other_attributes Optional. Other attributes that should be output with the button,
- * mapping attributes to their values, such as `array( 'tabindex' => '1' )`.
- * These attributes will be output as `attribute="value"`, such as
- * `tabindex="1"`. Other attributes can also be provided as a string such
- * as `tabindex="1"`, though the array format is typically cleaner.
- * Default empty.
- * @return string Submit button HTML.
- */
-function get_submit_button( $text = '', $type = 'primary large', $name = 'submit', $wrap = true, $other_attributes = '' ) {
- if ( ! is_array( $type ) )
- $type = explode( ' ', $type );
-
- $button_shorthand = array( 'primary', 'small', 'large' );
- $classes = array( 'button' );
- foreach ( $type as $t ) {
- if ( 'secondary' === $t || 'button-secondary' === $t )
- continue;
- $classes[] = in_array( $t, $button_shorthand ) ? 'button-' . $t : $t;
- }
- $class = implode( ' ', array_unique( $classes ) );
-
- if ( 'delete' === $type )
- $class = 'button-secondary delete';
-
- $text = $text ? $text : __( 'Save Changes' );
-
- // Default the id attribute to $name unless an id was specifically provided in $other_attributes
- $id = $name;
- if ( is_array( $other_attributes ) && isset( $other_attributes['id'] ) ) {
- $id = $other_attributes['id'];
- unset( $other_attributes['id'] );
- }
-
- $attributes = '';
- if ( is_array( $other_attributes ) ) {
- foreach ( $other_attributes as $attribute => $value ) {
- $attributes .= $attribute . '="' . esc_attr( $value ) . '" '; // Trailing space is important
- }
- } elseif ( ! empty( $other_attributes ) ) { // Attributes provided as a string
- $attributes = $other_attributes;
- }
-
- // Don't output empty name and id attributes.
- $name_attr = $name ? ' name="' . esc_attr( $name ) . '"' : '';
- $id_attr = $id ? ' id="' . esc_attr( $id ) . '"' : '';
-
- $button = '<input type="submit"' . $name_attr . $id_attr . ' class="' . esc_attr( $class );
- $button .= '" value="' . esc_attr( $text ) . '" ' . $attributes . ' />';
-
- if ( $wrap ) {
- $button = '<p class="submit">' . $button . '</p>';
- }
-
- return $button;
-}
-
-/**
- *
- * @global bool $is_IE
- */
-function _wp_admin_html_begin() {
- global $is_IE;
-
- $admin_html_class = ( is_admin_bar_showing() ) ? 'wp-toolbar' : '';
-
- if ( $is_IE )
- @header('X-UA-Compatible: IE=edge');
-
-?>
-<!DOCTYPE html>
-<!--[if IE 8]>
-<html xmlns="http://www.w3.org/1999/xhtml" class="ie8 <?php echo $admin_html_class; ?>" <?php
- /**
- * Fires inside the HTML tag in the admin header.
- *
- * @since 2.2.0
- */
- do_action( 'admin_xml_ns' );
-?> <?php language_attributes(); ?>>
-<![endif]-->
-<!--[if !(IE 8) ]><!-->
-<html xmlns="http://www.w3.org/1999/xhtml" class="<?php echo $admin_html_class; ?>" <?php
- /** This action is documented in wp-admin/includes/template-functions.php */
- do_action( 'admin_xml_ns' );
-?> <?php language_attributes(); ?>>
-<!--<![endif]-->
-<head>
-<meta http-equiv="Content-Type" content="<?php bloginfo('html_type'); ?>; charset=<?php echo get_option('blog_charset'); ?>" />
-<?php
-}
-
-/**
- * Convert a screen string to a screen object
- *
- * @since 3.0.0
- *
- * @param string $hook_name The hook name (also known as the hook suffix) used to determine the screen.
- * @return WP_Screen Screen object.
- */
-function convert_to_screen( $hook_name ) {
- if ( ! class_exists( 'WP_Screen', false ) ) {
- _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' );
- return (object) array( 'id' => '_invalid', 'base' => '_are_belong_to_us' );
- }
-
- return WP_Screen::get( $hook_name );
-}
-
-/**
- * Output the HTML for restoring the post data from DOM storage
- *
- * @since 3.6.0
- * @access private
- */
-function _local_storage_notice() {
- ?>
- <div id="local-storage-notice" class="hidden notice">
- <p class="local-restore">
- <?php _e('The backup of this post in your browser is different from the version below.'); ?>
- <a class="restore-backup" href="#"><?php _e('Restore the backup.'); ?></a>
- </p>
- <p class="undo-restore hidden">
- <?php _e('Post restored successfully.'); ?>
- <a class="undo-restore-backup" href="#"><?php _e('Undo.'); ?></a>
- </p>
- </div>
- <?php
-}
-
-/**
- * Output a HTML element with a star rating for a given rating.
- *
- * Outputs a HTML element with the star rating exposed on a 0..5 scale in
- * half star increments (ie. 1, 1.5, 2 stars). Optionally, if specified, the
- * number of ratings may also be displayed by passing the $number parameter.
- *
- * @since 3.8.0
- * @since 4.4.0 Introduced the `echo` parameter.
- *
- * @param array $args {
- * Optional. Array of star ratings arguments.
- *
- * @type int $rating The rating to display, expressed in either a 0.5 rating increment,
- * or percentage. Default 0.
- * @type string $type Format that the $rating is in. Valid values are 'rating' (default),
- * or, 'percent'. Default 'rating'.
- * @type int $number The number of ratings that makes up this rating. Default 0.
- * @type bool $echo Whether to echo the generated markup. False to return the markup instead
- * of echoing it. Default true.
- * }
- */
-function wp_star_rating( $args = array() ) {
- $defaults = array(
- 'rating' => 0,
- 'type' => 'rating',
- 'number' => 0,
- 'echo' => true,
- );
- $r = wp_parse_args( $args, $defaults );
-
- // Non-english decimal places when the $rating is coming from a string
- $rating = str_replace( ',', '.', $r['rating'] );
-
- // Convert Percentage to star rating, 0..5 in .5 increments
- if ( 'percent' == $r['type'] ) {
- $rating = round( $rating / 10, 0 ) / 2;
- }
-
- // Calculate the number of each type of star needed
- $full_stars = floor( $rating );
- $half_stars = ceil( $rating - $full_stars );
- $empty_stars = 5 - $full_stars - $half_stars;
-
- if ( $r['number'] ) {
- /* translators: 1: The rating, 2: The number of ratings */
- $format = _n( '%1$s rating based on %2$s rating', '%1$s rating based on %2$s ratings', $r['number'] );
- $title = sprintf( $format, number_format_i18n( $rating, 1 ), number_format_i18n( $r['number'] ) );
- } else {
- /* translators: 1: The rating */
- $title = sprintf( __( '%s rating' ), number_format_i18n( $rating, 1 ) );
- }
-
- $output = '<div class="star-rating" title="' . esc_attr( $title ) . '">';
- $output .= '<span class="screen-reader-text">' . $title . '</span>';
- $output .= str_repeat( '<div class="star star-full"></div>', $full_stars );
- $output .= str_repeat( '<div class="star star-half"></div>', $half_stars );
- $output .= str_repeat( '<div class="star star-empty"></div>', $empty_stars );
- $output .= '</div>';
-
- if ( $r['echo'] ) {
- echo $output;
- }
-
- return $output;
-}
-
-/**
- * Output a notice when editing the page for posts (internal use only).
- *
- * @ignore
- * @since 4.2.0
- */
-function _wp_posts_page_notice() {
- echo '<div class="notice notice-warning inline"><p>' . __( 'You are currently editing the page that shows your latest posts.' ) . '</p></div>';
-}
</del></span></pre></div>
<a id="trunksrcwpadminincludestemplatephp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-admin/includes/template.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/template.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-admin/includes/template.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,18 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Template WordPress Administration API.
- *
- * A Big Mess. Also some neat functions that are nicely written.
- *
- * @package WordPress
- * @subpackage Administration
- */
-
-/** Walker_Category_Checklist class */
-require_once( ABSPATH . 'wp-admin/includes/class-walker-category-checklist.php' );
-
-/** WP_Internal_Pointers class */
-require_once( ABSPATH . 'wp-admin/includes/class-wp-internal-pointers.php' );
-
-/** Admin template functionality */
-require_once( ABSPATH . 'wp-admin/includes/template-functions.php' );
</del></span></pre></div>
<a id="trunksrcwpadminincludestemplatephpfromrev35717trunksrcwpadminincludestemplatefunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-admin/includes/template.php (from rev 35717, trunk/src/wp-admin/includes/template-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/template.php (rev 0)
+++ trunk/src/wp-admin/includes/template.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,2073 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Template WordPress Administration API.
+ *
+ * A Big Mess. Also some neat functions that are nicely written.
+ *
+ * @package WordPress
+ * @subpackage Administration
+ */
+
+//
+// Category Checklists
+//
+
+/**
+ * Output an unordered list of checkbox input elements labeled with category names.
+ *
+ * @since 2.5.1
+ *
+ * @see wp_terms_checklist()
+ *
+ * @param int $post_id Optional. Post to generate a categories checklist for. Default 0.
+ * $selected_cats must not be an array. Default 0.
+ * @param int $descendants_and_self Optional. ID of the category to output along with its descendants.
+ * Default 0.
+ * @param array $selected_cats Optional. List of categories to mark as checked. Default false.
+ * @param array $popular_cats Optional. List of categories to receive the "popular-category" class.
+ * Default false.
+ * @param object $walker Optional. Walker object to use to build the output.
+ * Default is a Walker_Category_Checklist instance.
+ * @param bool $checked_ontop Optional. Whether to move checked items out of the hierarchy and to
+ * the top of the list. Default true.
+ */
+function wp_category_checklist( $post_id = 0, $descendants_and_self = 0, $selected_cats = false, $popular_cats = false, $walker = null, $checked_ontop = true ) {
+ wp_terms_checklist( $post_id, array(
+ 'taxonomy' => 'category',
+ 'descendants_and_self' => $descendants_and_self,
+ 'selected_cats' => $selected_cats,
+ 'popular_cats' => $popular_cats,
+ 'walker' => $walker,
+ 'checked_ontop' => $checked_ontop
+ ) );
+}
+
+/**
+ * Output an unordered list of checkbox input elements labelled with term names.
+ *
+ * Taxonomy-independent version of wp_category_checklist().
+ *
+ * @since 3.0.0
+ * @since 4.4.0 Introduced the `$echo` argument.
+ *
+ * @param int $post_id Optional. Post ID. Default 0.
+ * @param array|string $args {
+ * Optional. Array or string of arguments for generating a terms checklist. Default empty array.
+ *
+ * @type int $descendants_and_self ID of the category to output along with its descendants.
+ * Default 0.
+ * @type array $selected_cats List of categories to mark as checked. Default false.
+ * @type array $popular_cats List of categories to receive the "popular-category" class.
+ * Default false.
+ * @type object $walker Walker object to use to build the output.
+ * Default is a Walker_Category_Checklist instance.
+ * @type string $taxonomy Taxonomy to generate the checklist for. Default 'category'.
+ * @type bool $checked_ontop Whether to move checked items out of the hierarchy and to
+ * the top of the list. Default true.
+ * @type bool $echo Whether to echo the generated markup. False to return the markup instead
+ * of echoing it. Default true.
+ * }
+ */
+function wp_terms_checklist( $post_id = 0, $args = array() ) {
+ $defaults = array(
+ 'descendants_and_self' => 0,
+ 'selected_cats' => false,
+ 'popular_cats' => false,
+ 'walker' => null,
+ 'taxonomy' => 'category',
+ 'checked_ontop' => true,
+ 'echo' => true,
+ );
+
+ /**
+ * Filter the taxonomy terms checklist arguments.
+ *
+ * @since 3.4.0
+ *
+ * @see wp_terms_checklist()
+ *
+ * @param array $args An array of arguments.
+ * @param int $post_id The post ID.
+ */
+ $params = apply_filters( 'wp_terms_checklist_args', $args, $post_id );
+
+ $r = wp_parse_args( $params, $defaults );
+
+ if ( empty( $r['walker'] ) || ! ( $r['walker'] instanceof Walker ) ) {
+ $walker = new Walker_Category_Checklist;
+ } else {
+ $walker = $r['walker'];
+ }
+
+ $taxonomy = $r['taxonomy'];
+ $descendants_and_self = (int) $r['descendants_and_self'];
+
+ $args = array( 'taxonomy' => $taxonomy );
+
+ $tax = get_taxonomy( $taxonomy );
+ $args['disabled'] = ! current_user_can( $tax->cap->assign_terms );
+
+ $args['list_only'] = ! empty( $r['list_only'] );
+
+ if ( is_array( $r['selected_cats'] ) ) {
+ $args['selected_cats'] = $r['selected_cats'];
+ } elseif ( $post_id ) {
+ $args['selected_cats'] = wp_get_object_terms( $post_id, $taxonomy, array_merge( $args, array( 'fields' => 'ids' ) ) );
+ } else {
+ $args['selected_cats'] = array();
+ }
+ if ( is_array( $r['popular_cats'] ) ) {
+ $args['popular_cats'] = $r['popular_cats'];
+ } else {
+ $args['popular_cats'] = get_terms( $taxonomy, array(
+ 'fields' => 'ids',
+ 'orderby' => 'count',
+ 'order' => 'DESC',
+ 'number' => 10,
+ 'hierarchical' => false
+ ) );
+ }
+ if ( $descendants_and_self ) {
+ $categories = (array) get_terms( $taxonomy, array(
+ 'child_of' => $descendants_and_self,
+ 'hierarchical' => 0,
+ 'hide_empty' => 0
+ ) );
+ $self = get_term( $descendants_and_self, $taxonomy );
+ array_unshift( $categories, $self );
+ } else {
+ $categories = (array) get_terms( $taxonomy, array( 'get' => 'all' ) );
+ }
+
+ $output = '';
+
+ if ( $r['checked_ontop'] ) {
+ // Post process $categories rather than adding an exclude to the get_terms() query to keep the query the same across all posts (for any query cache)
+ $checked_categories = array();
+ $keys = array_keys( $categories );
+
+ foreach ( $keys as $k ) {
+ if ( in_array( $categories[$k]->term_id, $args['selected_cats'] ) ) {
+ $checked_categories[] = $categories[$k];
+ unset( $categories[$k] );
+ }
+ }
+
+ // Put checked cats on top
+ $output .= call_user_func_array( array( $walker, 'walk' ), array( $checked_categories, 0, $args ) );
+ }
+ // Then the rest of them
+ $output .= call_user_func_array( array( $walker, 'walk' ), array( $categories, 0, $args ) );
+
+ if ( $r['echo'] ) {
+ echo $output;
+ }
+
+ return $output;
+}
+
+/**
+ * Retrieve a list of the most popular terms from the specified taxonomy.
+ *
+ * If the $echo argument is true then the elements for a list of checkbox
+ * `<input>` elements labelled with the names of the selected terms is output.
+ * If the $post_ID global isn't empty then the terms associated with that
+ * post will be marked as checked.
+ *
+ * @since 2.5.0
+ *
+ * @param string $taxonomy Taxonomy to retrieve terms from.
+ * @param int $default Not used.
+ * @param int $number Number of terms to retrieve. Defaults to 10.
+ * @param bool $echo Optionally output the list as well. Defaults to true.
+ * @return array List of popular term IDs.
+ */
+function wp_popular_terms_checklist( $taxonomy, $default = 0, $number = 10, $echo = true ) {
+ $post = get_post();
+
+ if ( $post && $post->ID )
+ $checked_terms = wp_get_object_terms($post->ID, $taxonomy, array('fields'=>'ids'));
+ else
+ $checked_terms = array();
+
+ $terms = get_terms( $taxonomy, array( 'orderby' => 'count', 'order' => 'DESC', 'number' => $number, 'hierarchical' => false ) );
+
+ $tax = get_taxonomy($taxonomy);
+
+ $popular_ids = array();
+ foreach ( (array) $terms as $term ) {
+ $popular_ids[] = $term->term_id;
+ if ( !$echo ) // hack for AJAX use
+ continue;
+ $id = "popular-$taxonomy-$term->term_id";
+ $checked = in_array( $term->term_id, $checked_terms ) ? 'checked="checked"' : '';
+ ?>
+
+ <li id="<?php echo $id; ?>" class="popular-category">
+ <label class="selectit">
+ <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 ) ); ?> />
+ <?php
+ /** This filter is documented in wp-includes/category-template.php */
+ echo esc_html( apply_filters( 'the_category', $term->name ) );
+ ?>
+ </label>
+ </li>
+
+ <?php
+ }
+ return $popular_ids;
+}
+
+/**
+ * Outputs a link category checklist element.
+ *
+ * @since 2.5.1
+ *
+ * @param int $link_id
+ */
+function wp_link_category_checklist( $link_id = 0 ) {
+ $default = 1;
+
+ $checked_categories = array();
+
+ if ( $link_id ) {
+ $checked_categories = wp_get_link_cats( $link_id );
+ // No selected categories, strange
+ if ( ! count( $checked_categories ) ) {
+ $checked_categories[] = $default;
+ }
+ } else {
+ $checked_categories[] = $default;
+ }
+
+ $categories = get_terms( 'link_category', array( 'orderby' => 'name', 'hide_empty' => 0 ) );
+
+ if ( empty( $categories ) )
+ return;
+
+ foreach ( $categories as $category ) {
+ $cat_id = $category->term_id;
+
+ /** This filter is documented in wp-includes/category-template.php */
+ $name = esc_html( apply_filters( 'the_category', $category->name ) );
+ $checked = in_array( $cat_id, $checked_categories ) ? ' checked="checked"' : '';
+ 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>";
+ }
+}
+
+/**
+ * Adds hidden fields with the data for use in the inline editor for posts and pages.
+ *
+ * @since 2.7.0
+ *
+ * @param WP_Post $post Post object.
+ */
+function get_inline_data($post) {
+ $post_type_object = get_post_type_object($post->post_type);
+ if ( ! current_user_can( 'edit_post', $post->ID ) )
+ return;
+
+ $title = esc_textarea( trim( $post->post_title ) );
+
+ /** This filter is documented in wp-admin/edit-tag-form.php */
+ echo '
+<div class="hidden" id="inline_' . $post->ID . '">
+ <div class="post_title">' . $title . '</div>' .
+ /** This filter is documented in wp-admin/edit-tag-form.php */
+ '<div class="post_name">' . apply_filters( 'editable_slug', $post->post_name, $post ) . '</div>
+ <div class="post_author">' . $post->post_author . '</div>
+ <div class="comment_status">' . esc_html( $post->comment_status ) . '</div>
+ <div class="ping_status">' . esc_html( $post->ping_status ) . '</div>
+ <div class="_status">' . esc_html( $post->post_status ) . '</div>
+ <div class="jj">' . mysql2date( 'd', $post->post_date, false ) . '</div>
+ <div class="mm">' . mysql2date( 'm', $post->post_date, false ) . '</div>
+ <div class="aa">' . mysql2date( 'Y', $post->post_date, false ) . '</div>
+ <div class="hh">' . mysql2date( 'H', $post->post_date, false ) . '</div>
+ <div class="mn">' . mysql2date( 'i', $post->post_date, false ) . '</div>
+ <div class="ss">' . mysql2date( 's', $post->post_date, false ) . '</div>
+ <div class="post_password">' . esc_html( $post->post_password ) . '</div>';
+
+ if ( $post_type_object->hierarchical )
+ echo '<div class="post_parent">' . $post->post_parent . '</div>';
+
+ if ( $post->post_type == 'page' )
+ echo '<div class="page_template">' . esc_html( get_post_meta( $post->ID, '_wp_page_template', true ) ) . '</div>';
+
+ if ( post_type_supports( $post->post_type, 'page-attributes' ) )
+ echo '<div class="menu_order">' . $post->menu_order . '</div>';
+
+ $taxonomy_names = get_object_taxonomies( $post->post_type );
+ foreach ( $taxonomy_names as $taxonomy_name) {
+ $taxonomy = get_taxonomy( $taxonomy_name );
+
+ if ( $taxonomy->hierarchical && $taxonomy->show_ui ) {
+
+ $terms = get_object_term_cache( $post->ID, $taxonomy_name );
+ if ( false === $terms ) {
+ $terms = wp_get_object_terms( $post->ID, $taxonomy_name );
+ wp_cache_add( $post->ID, $terms, $taxonomy_name . '_relationships' );
+ }
+ $term_ids = empty( $terms ) ? array() : wp_list_pluck( $terms, 'term_id' );
+
+ echo '<div class="post_category" id="' . $taxonomy_name . '_' . $post->ID . '">' . implode( ',', $term_ids ) . '</div>';
+
+ } elseif ( $taxonomy->show_ui ) {
+
+ $terms_to_edit = get_terms_to_edit( $post->ID, $taxonomy_name );
+ if ( ! is_string( $terms_to_edit ) ) {
+ $terms_to_edit = '';
+ }
+
+ echo '<div class="tags_input" id="'.$taxonomy_name.'_'.$post->ID.'">'
+ . esc_html( str_replace( ',', ', ', $terms_to_edit ) ) . '</div>';
+
+ }
+ }
+
+ if ( !$post_type_object->hierarchical )
+ echo '<div class="sticky">' . (is_sticky($post->ID) ? 'sticky' : '') . '</div>';
+
+ if ( post_type_supports( $post->post_type, 'post-formats' ) )
+ echo '<div class="post_format">' . esc_html( get_post_format( $post->ID ) ) . '</div>';
+
+ echo '</div>';
+}
+
+/**
+ * Outputs the in-line comment reply-to form in the Comments list table.
+ *
+ * @since 2.7.0
+ *
+ * @global WP_List_Table $wp_list_table
+ *
+ * @param int $position
+ * @param bool $checkbox
+ * @param string $mode
+ * @param bool $table_row
+ */
+function wp_comment_reply( $position = 1, $checkbox = false, $mode = 'single', $table_row = true ) {
+ global $wp_list_table;
+ /**
+ * Filter the in-line comment reply-to form output in the Comments
+ * list table.
+ *
+ * Returning a non-empty value here will short-circuit display
+ * of the in-line comment-reply form in the Comments list table,
+ * echoing the returned value instead.
+ *
+ * @since 2.7.0
+ *
+ * @see wp_comment_reply()
+ *
+ * @param string $content The reply-to form content.
+ * @param array $args An array of default args.
+ */
+ $content = apply_filters( 'wp_comment_reply', '', array( 'position' => $position, 'checkbox' => $checkbox, 'mode' => $mode ) );
+
+ if ( ! empty($content) ) {
+ echo $content;
+ return;
+ }
+
+ if ( ! $wp_list_table ) {
+ if ( $mode == 'single' ) {
+ $wp_list_table = _get_list_table('WP_Post_Comments_List_Table');
+ } else {
+ $wp_list_table = _get_list_table('WP_Comments_List_Table');
+ }
+ }
+
+?>
+<form method="get">
+<?php if ( $table_row ) : ?>
+<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">
+<?php else : ?>
+<div id="com-reply" style="display:none;"><div id="replyrow" style="display:none;">
+<?php endif; ?>
+ <fieldset class="comment-reply">
+ <legend>
+ <span class="hidden" id="editlegend"><?php _e( 'Edit Comment' ); ?></span>
+ <span class="hidden" id="replyhead"><?php _e( 'Reply to Comment' ); ?></span>
+ <span class="hidden" id="addhead"><?php _e( 'Add new Comment' ); ?></span>
+ </legend>
+
+ <div id="replycontainer">
+ <label for="replycontent" class="screen-reader-text"><?php _e( 'Comment' ); ?></label>
+ <?php
+ $quicktags_settings = array( 'buttons' => 'strong,em,link,block,del,ins,img,ul,ol,li,code,close' );
+ wp_editor( '', 'replycontent', array( 'media_buttons' => false, 'tinymce' => false, 'quicktags' => $quicktags_settings ) );
+ ?>
+ </div>
+
+ <div id="edithead" style="display:none;">
+ <div class="inside">
+ <label for="author-name"><?php _e( 'Name' ) ?></label>
+ <input type="text" name="newcomment_author" size="50" value="" id="author-name" />
+ </div>
+
+ <div class="inside">
+ <label for="author-email"><?php _e('Email') ?></label>
+ <input type="text" name="newcomment_author_email" size="50" value="" id="author-email" />
+ </div>
+
+ <div class="inside">
+ <label for="author-url"><?php _e('URL') ?></label>
+ <input type="text" id="author-url" name="newcomment_author_url" class="code" size="103" value="" />
+ </div>
+ </div>
+
+ <p id="replysubmit" class="submit">
+ <a href="#comments-form" class="save button-primary alignright">
+ <span id="addbtn" style="display:none;"><?php _e('Add Comment'); ?></span>
+ <span id="savebtn" style="display:none;"><?php _e('Update Comment'); ?></span>
+ <span id="replybtn" style="display:none;"><?php _e('Submit Reply'); ?></span></a>
+ <a href="#comments-form" class="cancel button-secondary alignleft"><?php _e('Cancel'); ?></a>
+ <span class="waiting spinner"></span>
+ <span class="error" style="display:none;"></span>
+ </p>
+
+ <input type="hidden" name="action" id="action" value="" />
+ <input type="hidden" name="comment_ID" id="comment_ID" value="" />
+ <input type="hidden" name="comment_post_ID" id="comment_post_ID" value="" />
+ <input type="hidden" name="status" id="status" value="" />
+ <input type="hidden" name="position" id="position" value="<?php echo $position; ?>" />
+ <input type="hidden" name="checkbox" id="checkbox" value="<?php echo $checkbox ? 1 : 0; ?>" />
+ <input type="hidden" name="mode" id="mode" value="<?php echo esc_attr($mode); ?>" />
+ <?php
+ wp_nonce_field( 'replyto-comment', '_ajax_nonce-replyto-comment', false );
+ if ( current_user_can( 'unfiltered_html' ) )
+ wp_nonce_field( 'unfiltered-html-comment', '_wp_unfiltered_html_comment', false );
+ ?>
+ </fieldset>
+<?php if ( $table_row ) : ?>
+</td></tr></tbody></table>
+<?php else : ?>
+</div></div>
+<?php endif; ?>
+</form>
+<?php
+}
+
+/**
+ * Output 'undo move to trash' text for comments
+ *
+ * @since 2.9.0
+ */
+function wp_comment_trashnotice() {
+?>
+<div class="hidden" id="trash-undo-holder">
+ <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>
+</div>
+<div class="hidden" id="spam-undo-holder">
+ <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>
+</div>
+<?php
+}
+
+/**
+ * Outputs a post's public meta data in the Custom Fields meta box.
+ *
+ * @since 1.2.0
+ *
+ * @param array $meta
+ */
+function list_meta( $meta ) {
+ // Exit if no meta
+ if ( ! $meta ) {
+ echo '
+<table id="list-table" style="display: none;">
+ <thead>
+ <tr>
+ <th class="left">' . _x( 'Name', 'meta name' ) . '</th>
+ <th>' . __( 'Value' ) . '</th>
+ </tr>
+ </thead>
+ <tbody id="the-list" data-wp-lists="list:meta">
+ <tr><td></td></tr>
+ </tbody>
+</table>'; //TBODY needed for list-manipulation JS
+ return;
+ }
+ $count = 0;
+?>
+<table id="list-table">
+ <thead>
+ <tr>
+ <th class="left"><?php _ex( 'Name', 'meta name' ) ?></th>
+ <th><?php _e( 'Value' ) ?></th>
+ </tr>
+ </thead>
+ <tbody id='the-list' data-wp-lists='list:meta'>
+<?php
+ foreach ( $meta as $entry )
+ echo _list_meta_row( $entry, $count );
+?>
+ </tbody>
+</table>
+<?php
+}
+
+/**
+ * Outputs a single row of public meta data in the Custom Fields meta box.
+ *
+ * @since 2.5.0
+ *
+ * @staticvar string $update_nonce
+ *
+ * @param array $entry
+ * @param int $count
+ * @return string
+ */
+function _list_meta_row( $entry, &$count ) {
+ static $update_nonce = '';
+
+ if ( is_protected_meta( $entry['meta_key'], 'post' ) )
+ return '';
+
+ if ( ! $update_nonce )
+ $update_nonce = wp_create_nonce( 'add-meta' );
+
+ $r = '';
+ ++ $count;
+
+ if ( is_serialized( $entry['meta_value'] ) ) {
+ if ( is_serialized_string( $entry['meta_value'] ) ) {
+ // This is a serialized string, so we should display it.
+ $entry['meta_value'] = maybe_unserialize( $entry['meta_value'] );
+ } else {
+ // This is a serialized array/object so we should NOT display it.
+ --$count;
+ return '';
+ }
+ }
+
+ $entry['meta_key'] = esc_attr($entry['meta_key']);
+ $entry['meta_value'] = esc_textarea( $entry['meta_value'] ); // using a <textarea />
+ $entry['meta_id'] = (int) $entry['meta_id'];
+
+ $delete_nonce = wp_create_nonce( 'delete-meta_' . $entry['meta_id'] );
+
+ $r .= "\n\t<tr id='meta-{$entry['meta_id']}'>";
+ $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']}' />";
+
+ $r .= "\n\t\t<div class='submit'>";
+ $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" ) );
+ $r .= "\n\t\t";
+ $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" ) );
+ $r .= "</div>";
+ $r .= wp_nonce_field( 'change-meta', '_ajax_nonce', false, false );
+ $r .= "</td>";
+
+ $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>";
+ return $r;
+}
+
+/**
+ * Prints the form in the Custom Fields meta box.
+ *
+ * @since 1.2.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param WP_Post $post Optional. The post being edited.
+ */
+function meta_form( $post = null ) {
+ global $wpdb;
+ $post = get_post( $post );
+
+ /**
+ * Filter values for the meta key dropdown in the Custom Fields meta box.
+ *
+ * Returning a non-null value will effectively short-circuit and avoid a
+ * potentially expensive query against postmeta.
+ *
+ * @since 4.4.0
+ *
+ * @param array|null $keys Pre-defined meta keys to be used in place of a postmeta query. Default null.
+ */
+ $keys = apply_filters( 'postmeta_form_keys', null );
+
+ if ( null === $keys ) {
+ /**
+ * Filter the number of custom fields to retrieve for the drop-down
+ * in the Custom Fields meta box.
+ *
+ * @since 2.1.0
+ *
+ * @param int $limit Number of custom fields to retrieve. Default 30.
+ */
+ $limit = apply_filters( 'postmeta_form_limit', 30 );
+ $sql = "SELECT DISTINCT meta_key
+ FROM $wpdb->postmeta
+ WHERE meta_key NOT BETWEEN '_' AND '_z'
+ HAVING meta_key NOT LIKE %s
+ ORDER BY meta_key
+ LIMIT %d";
+ $keys = $wpdb->get_col( $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', $limit ) );
+ }
+
+ if ( $keys ) {
+ natcasesort( $keys );
+ $meta_key_input_id = 'metakeyselect';
+ } else {
+ $meta_key_input_id = 'metakeyinput';
+ }
+?>
+<p><strong><?php _e( 'Add New Custom Field:' ) ?></strong></p>
+<table id="newmeta">
+<thead>
+<tr>
+<th class="left"><label for="<?php echo $meta_key_input_id; ?>"><?php _ex( 'Name', 'meta name' ) ?></label></th>
+<th><label for="metavalue"><?php _e( 'Value' ) ?></label></th>
+</tr>
+</thead>
+
+<tbody>
+<tr>
+<td id="newmetaleft" class="left">
+<?php if ( $keys ) { ?>
+<select id="metakeyselect" name="metakeyselect">
+<option value="#NONE#"><?php _e( '— Select —' ); ?></option>
+<?php
+
+ foreach ( $keys as $key ) {
+ if ( is_protected_meta( $key, 'post' ) || ! current_user_can( 'add_post_meta', $post->ID, $key ) )
+ continue;
+ echo "\n<option value='" . esc_attr($key) . "'>" . esc_html($key) . "</option>";
+ }
+?>
+</select>
+<input class="hide-if-js" type="text" id="metakeyinput" name="metakeyinput" value="" />
+<a href="#postcustomstuff" class="hide-if-no-js" onclick="jQuery('#metakeyinput, #metakeyselect, #enternew, #cancelnew').toggle();return false;">
+<span id="enternew"><?php _e('Enter new'); ?></span>
+<span id="cancelnew" class="hidden"><?php _e('Cancel'); ?></span></a>
+<?php } else { ?>
+<input type="text" id="metakeyinput" name="metakeyinput" value="" />
+<?php } ?>
+</td>
+<td><textarea id="metavalue" name="metavalue" rows="2" cols="25"></textarea></td>
+</tr>
+
+<tr><td colspan="2">
+<div class="submit">
+<?php submit_button( __( 'Add Custom Field' ), 'secondary', 'addmeta', false, array( 'id' => 'newmeta-submit', 'data-wp-lists' => 'add:the-list:newmeta' ) ); ?>
+</div>
+<?php wp_nonce_field( 'add-meta', '_ajax_nonce-add-meta', false ); ?>
+</td></tr>
+</tbody>
+</table>
+<?php
+
+}
+
+/**
+ * Print out HTML form date elements for editing post or comment publish date.
+ *
+ * @since 0.71
+ * @since 4.4.0 Converted to use get_comment() instead of the global `$comment`.
+ *
+ * @global WP_Locale $wp_locale
+ *
+ * @param int|bool $edit Accepts 1|true for editing the date, 0|false for adding the date.
+ * @param int|bool $for_post Accepts 1|true for applying the date to a post, 0|false for a comment.
+ * @param int $tab_index The tabindex attribute to add. Default 0.
+ * @param int|bool $multi Optional. Whether the additional fields and buttons should be added.
+ * Default 0|false.
+ */
+function touch_time( $edit = 1, $for_post = 1, $tab_index = 0, $multi = 0 ) {
+ global $wp_locale;
+ $post = get_post();
+
+ if ( $for_post )
+ $edit = ! ( in_array($post->post_status, array('draft', 'pending') ) && (!$post->post_date_gmt || '0000-00-00 00:00:00' == $post->post_date_gmt ) );
+
+ $tab_index_attribute = '';
+ if ( (int) $tab_index > 0 )
+ $tab_index_attribute = " tabindex=\"$tab_index\"";
+
+ // todo: Remove this?
+ // echo '<label for="timestamp" style="display: block;"><input type="checkbox" class="checkbox" name="edit_date" value="1" id="timestamp"'.$tab_index_attribute.' /> '.__( 'Edit timestamp' ).'</label><br />';
+
+ $time_adj = current_time('timestamp');
+ $post_date = ($for_post) ? $post->post_date : get_comment()->comment_date;
+ $jj = ($edit) ? mysql2date( 'd', $post_date, false ) : gmdate( 'd', $time_adj );
+ $mm = ($edit) ? mysql2date( 'm', $post_date, false ) : gmdate( 'm', $time_adj );
+ $aa = ($edit) ? mysql2date( 'Y', $post_date, false ) : gmdate( 'Y', $time_adj );
+ $hh = ($edit) ? mysql2date( 'H', $post_date, false ) : gmdate( 'H', $time_adj );
+ $mn = ($edit) ? mysql2date( 'i', $post_date, false ) : gmdate( 'i', $time_adj );
+ $ss = ($edit) ? mysql2date( 's', $post_date, false ) : gmdate( 's', $time_adj );
+
+ $cur_jj = gmdate( 'd', $time_adj );
+ $cur_mm = gmdate( 'm', $time_adj );
+ $cur_aa = gmdate( 'Y', $time_adj );
+ $cur_hh = gmdate( 'H', $time_adj );
+ $cur_mn = gmdate( 'i', $time_adj );
+
+ $month = '<label><span class="screen-reader-text">' . __( 'Month' ) . '</span><select ' . ( $multi ? '' : 'id="mm" ' ) . 'name="mm"' . $tab_index_attribute . ">\n";
+ for ( $i = 1; $i < 13; $i = $i +1 ) {
+ $monthnum = zeroise($i, 2);
+ $monthtext = $wp_locale->get_month_abbrev( $wp_locale->get_month( $i ) );
+ $month .= "\t\t\t" . '<option value="' . $monthnum . '" data-text="' . $monthtext . '" ' . selected( $monthnum, $mm, false ) . '>';
+ /* translators: 1: month number (01, 02, etc.), 2: month abbreviation */
+ $month .= sprintf( __( '%1$s-%2$s' ), $monthnum, $monthtext ) . "</option>\n";
+ }
+ $month .= '</select></label>';
+
+ $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>';
+ $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>';
+ $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>';
+ $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>';
+
+ echo '<div class="timestamp-wrap">';
+ /* translators: 1: month, 2: day, 3: year, 4: hour, 5: minute */
+ printf( __( '%1$s %2$s, %3$s @ %4$s:%5$s' ), $month, $day, $year, $hour, $minute );
+
+ echo '</div><input type="hidden" id="ss" name="ss" value="' . $ss . '" />';
+
+ if ( $multi ) return;
+
+ echo "\n\n";
+ $map = array(
+ 'mm' => array( $mm, $cur_mm ),
+ 'jj' => array( $jj, $cur_jj ),
+ 'aa' => array( $aa, $cur_aa ),
+ 'hh' => array( $hh, $cur_hh ),
+ 'mn' => array( $mn, $cur_mn ),
+ );
+ foreach ( $map as $timeunit => $value ) {
+ list( $unit, $curr ) = $value;
+
+ echo '<input type="hidden" id="hidden_' . $timeunit . '" name="hidden_' . $timeunit . '" value="' . $unit . '" />' . "\n";
+ $cur_timeunit = 'cur_' . $timeunit;
+ echo '<input type="hidden" id="' . $cur_timeunit . '" name="' . $cur_timeunit . '" value="' . $curr . '" />' . "\n";
+ }
+?>
+
+<p>
+<a href="#edit_timestamp" class="save-timestamp hide-if-no-js button"><?php _e('OK'); ?></a>
+<a href="#edit_timestamp" class="cancel-timestamp hide-if-no-js button-cancel"><?php _e('Cancel'); ?></a>
+</p>
+<?php
+}
+
+/**
+ * Print out option HTML elements for the page templates drop-down.
+ *
+ * @since 1.5.0
+ *
+ * @param string $default Optional. The template file name. Default empty.
+ */
+function page_template_dropdown( $default = '' ) {
+ $templates = get_page_templates( get_post() );
+ ksort( $templates );
+ foreach ( array_keys( $templates ) as $template ) {
+ $selected = selected( $default, $templates[ $template ], false );
+ echo "\n\t<option value='" . $templates[ $template ] . "' $selected>$template</option>";
+ }
+}
+
+/**
+ * Print out option HTML elements for the page parents drop-down.
+ *
+ * @since 1.5.0
+ * @since 4.4.0 `$post` argument was added.
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $default Optional. The default page ID to be pre-selected. Default 0.
+ * @param int $parent Optional. The parent page ID. Default 0.
+ * @param int $level Optional. Page depth level. Default 0.
+ * @param int|WP_Post $post Post ID or WP_Post object.
+ *
+ * @return null|false Boolean False if page has no children, otherwise print out html elements
+ */
+function parent_dropdown( $default = 0, $parent = 0, $level = 0, $post = null ) {
+ global $wpdb;
+ $post = get_post( $post );
+ $items = $wpdb->get_results( $wpdb->prepare("SELECT ID, post_parent, post_title FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'page' ORDER BY menu_order", $parent) );
+
+ if ( $items ) {
+ foreach ( $items as $item ) {
+ // A page cannot be its own parent.
+ if ( $post && $post->ID && $item->ID == $post->ID )
+ continue;
+
+ $pad = str_repeat( ' ', $level * 3 );
+ $selected = selected( $default, $item->ID, false );
+
+ echo "\n\t<option class='level-$level' value='$item->ID' $selected>$pad " . esc_html($item->post_title) . "</option>";
+ parent_dropdown( $default, $item->ID, $level +1 );
+ }
+ } else {
+ return false;
+ }
+}
+
+/**
+ * Print out option html elements for role selectors.
+ *
+ * @since 2.1.0
+ *
+ * @param string $selected Slug for the role that should be already selected.
+ */
+function wp_dropdown_roles( $selected = '' ) {
+ $p = '';
+ $r = '';
+
+ $editable_roles = array_reverse( get_editable_roles() );
+
+ foreach ( $editable_roles as $role => $details ) {
+ $name = translate_user_role($details['name'] );
+ if ( $selected == $role ) // preselect specified role
+ $p = "\n\t<option selected='selected' value='" . esc_attr($role) . "'>$name</option>";
+ else
+ $r .= "\n\t<option value='" . esc_attr($role) . "'>$name</option>";
+ }
+ echo $p . $r;
+}
+
+/**
+ * Outputs the form used by the importers to accept the data to be imported
+ *
+ * @since 2.0.0
+ *
+ * @param string $action The action attribute for the form.
+ */
+function wp_import_upload_form( $action ) {
+
+ /**
+ * Filter the maximum allowed upload size for import files.
+ *
+ * @since 2.3.0
+ *
+ * @see wp_max_upload_size()
+ *
+ * @param int $max_upload_size Allowed upload size. Default 1 MB.
+ */
+ $bytes = apply_filters( 'import_upload_size_limit', wp_max_upload_size() );
+ $size = size_format( $bytes );
+ $upload_dir = wp_upload_dir();
+ if ( ! empty( $upload_dir['error'] ) ) :
+ ?><div class="error"><p><?php _e('Before you can upload your import file, you will need to fix the following error:'); ?></p>
+ <p><strong><?php echo $upload_dir['error']; ?></strong></p></div><?php
+ else :
+?>
+<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' ) ); ?>">
+<p>
+<label for="upload"><?php _e( 'Choose a file from your computer:' ); ?></label> (<?php printf( __('Maximum size: %s' ), $size ); ?>)
+<input type="file" id="upload" name="import" size="25" />
+<input type="hidden" name="action" value="save" />
+<input type="hidden" name="max_file_size" value="<?php echo $bytes; ?>" />
+</p>
+<?php submit_button( __('Upload file and import'), 'primary' ); ?>
+</form>
+<?php
+ endif;
+}
+
+/**
+ * Adds a meta box to one or more screens.
+ *
+ * @since 2.5.0
+ * @since 4.4.0 The `$screen` parameter now accepts an array of screen IDs.
+ *
+ * @global array $wp_meta_boxes
+ *
+ * @param string $id Meta box ID (used in the 'id' attribute for the meta box).
+ * @param string $title Title of the meta box.
+ * @param callable $callback Function that fills the box with the desired content.
+ * The function should echo its output.
+ * @param string|array|WP_Screen $screen Optional. The screen or screens on which to show the box
+ * (such as a post type, 'link', or 'comment'). Accepts a single
+ * screen ID, WP_Screen object, or array of screen IDs. Default
+ * is the current screen.
+ * @param string $context Optional. The context within the screen where the boxes
+ * should display. Available contexts vary from screen to
+ * screen. Post edit screen contexts include 'normal', 'side',
+ * and 'advanced'. Comments screen contexts include 'normal'
+ * and 'side'. Menus meta boxes (accordion sections) all use
+ * the 'side' context. Global default is 'advanced'.
+ * @param string $priority Optional. The priority within the context where the boxes
+ * should show ('high', 'low'). Default 'default'.
+ * @param array $callback_args Optional. Data that should be set as the $args property
+ * of the box array (which is the second parameter passed
+ * to your callback). Default null.
+ */
+function add_meta_box( $id, $title, $callback, $screen = null, $context = 'advanced', $priority = 'default', $callback_args = null ) {
+ global $wp_meta_boxes;
+
+ if ( empty( $screen ) ) {
+ $screen = get_current_screen();
+ } elseif ( is_string( $screen ) ) {
+ $screen = convert_to_screen( $screen );
+ } elseif ( is_array( $screen ) ) {
+ foreach ( $screen as $single_screen ) {
+ add_meta_box( $id, $title, $callback, $single_screen, $context, $priority, $callback_args );
+ }
+ }
+
+ if ( ! isset( $screen->id ) ) {
+ return;
+ }
+
+ $page = $screen->id;
+
+ if ( !isset($wp_meta_boxes) )
+ $wp_meta_boxes = array();
+ if ( !isset($wp_meta_boxes[$page]) )
+ $wp_meta_boxes[$page] = array();
+ if ( !isset($wp_meta_boxes[$page][$context]) )
+ $wp_meta_boxes[$page][$context] = array();
+
+ foreach ( array_keys($wp_meta_boxes[$page]) as $a_context ) {
+ foreach ( array('high', 'core', 'default', 'low') as $a_priority ) {
+ if ( !isset($wp_meta_boxes[$page][$a_context][$a_priority][$id]) )
+ continue;
+
+ // If a core box was previously added or removed by a plugin, don't add.
+ if ( 'core' == $priority ) {
+ // If core box previously deleted, don't add
+ if ( false === $wp_meta_boxes[$page][$a_context][$a_priority][$id] )
+ return;
+
+ /*
+ * If box was added with default priority, give it core priority to
+ * maintain sort order.
+ */
+ if ( 'default' == $a_priority ) {
+ $wp_meta_boxes[$page][$a_context]['core'][$id] = $wp_meta_boxes[$page][$a_context]['default'][$id];
+ unset($wp_meta_boxes[$page][$a_context]['default'][$id]);
+ }
+ return;
+ }
+ // If no priority given and id already present, use existing priority.
+ if ( empty($priority) ) {
+ $priority = $a_priority;
+ /*
+ * Else, if we're adding to the sorted priority, we don't know the title
+ * or callback. Grab them from the previously added context/priority.
+ */
+ } elseif ( 'sorted' == $priority ) {
+ $title = $wp_meta_boxes[$page][$a_context][$a_priority][$id]['title'];
+ $callback = $wp_meta_boxes[$page][$a_context][$a_priority][$id]['callback'];
+ $callback_args = $wp_meta_boxes[$page][$a_context][$a_priority][$id]['args'];
+ }
+ // An id can be in only one priority and one context.
+ if ( $priority != $a_priority || $context != $a_context )
+ unset($wp_meta_boxes[$page][$a_context][$a_priority][$id]);
+ }
+ }
+
+ if ( empty($priority) )
+ $priority = 'low';
+
+ if ( !isset($wp_meta_boxes[$page][$context][$priority]) )
+ $wp_meta_boxes[$page][$context][$priority] = array();
+
+ $wp_meta_boxes[$page][$context][$priority][$id] = array('id' => $id, 'title' => $title, 'callback' => $callback, 'args' => $callback_args);
+}
+
+/**
+ * Meta-Box template function
+ *
+ * @since 2.5.0
+ *
+ * @global array $wp_meta_boxes
+ *
+ * @staticvar bool $already_sorted
+ * @param string|WP_Screen $screen Screen identifier
+ * @param string $context box context
+ * @param mixed $object gets passed to the box callback function as first parameter
+ * @return int number of meta_boxes
+ */
+function do_meta_boxes( $screen, $context, $object ) {
+ global $wp_meta_boxes;
+ static $already_sorted = false;
+
+ if ( empty( $screen ) )
+ $screen = get_current_screen();
+ elseif ( is_string( $screen ) )
+ $screen = convert_to_screen( $screen );
+
+ $page = $screen->id;
+
+ $hidden = get_hidden_meta_boxes( $screen );
+
+ printf('<div id="%s-sortables" class="meta-box-sortables">', htmlspecialchars($context));
+
+ // Grab the ones the user has manually sorted. Pull them out of their previous context/priority and into the one the user chose
+ if ( ! $already_sorted && $sorted = get_user_option( "meta-box-order_$page" ) ) {
+ foreach ( $sorted as $box_context => $ids ) {
+ foreach ( explode( ',', $ids ) as $id ) {
+ if ( $id && 'dashboard_browser_nag' !== $id ) {
+ add_meta_box( $id, null, null, $screen, $box_context, 'sorted' );
+ }
+ }
+ }
+ }
+
+ $already_sorted = true;
+
+ $i = 0;
+
+ if ( isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
+ foreach ( array( 'high', 'sorted', 'core', 'default', 'low' ) as $priority ) {
+ if ( isset( $wp_meta_boxes[ $page ][ $context ][ $priority ]) ) {
+ foreach ( (array) $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) {
+ if ( false == $box || ! $box['title'] )
+ continue;
+ $i++;
+ $hidden_class = in_array($box['id'], $hidden) ? ' hide-if-js' : '';
+ echo '<div id="' . $box['id'] . '" class="postbox ' . postbox_classes($box['id'], $page) . $hidden_class . '" ' . '>' . "\n";
+ if ( 'dashboard_browser_nag' != $box['id'] ) {
+ echo '<button type="button" class="handlediv button-link" aria-expanded="true">';
+ echo '<span class="screen-reader-text">' . sprintf( __( 'Toggle panel: %s' ), $box['title'] ) . '</span>';
+ echo '<span class="toggle-indicator" aria-hidden="true"></span>';
+ echo '</button>';
+ }
+ echo "<h2 class='hndle'><span>{$box['title']}</span></h2>\n";
+ echo '<div class="inside">' . "\n";
+ call_user_func($box['callback'], $object, $box);
+ echo "</div>\n";
+ echo "</div>\n";
+ }
+ }
+ }
+ }
+
+ echo "</div>";
+
+ return $i;
+
+}
+
+/**
+ * Removes a meta box from one or more screens.
+ *
+ * @since 2.6.0
+ * @since 4.4.0 The `$screen` parameter now accepts an array of screen IDs.
+ *
+ * @global array $wp_meta_boxes
+ *
+ * @param string $id Meta box ID (used in the 'id' attribute for the meta box).
+ * @param string|array|WP_Screen $screen The screen or screens on which the meta box is shown (such as a
+ * post type, 'link', or 'comment'). Accepts a single screen ID,
+ * WP_Screen object, or array of screen IDs.
+ * @param string $context Optional. The context within the screen where the boxes
+ * should display. Available contexts vary from screen to
+ * screen. Post edit screen contexts include 'normal', 'side',
+ * and 'advanced'. Comments screen contexts include 'normal'
+ * and 'side'. Menus meta boxes (accordion sections) all use
+ * the 'side' context. Global default is 'advanced'.
+ */
+function remove_meta_box( $id, $screen, $context ) {
+ global $wp_meta_boxes;
+
+ if ( empty( $screen ) ) {
+ $screen = get_current_screen();
+ } elseif ( is_string( $screen ) ) {
+ $screen = convert_to_screen( $screen );
+ } elseif ( is_array( $screen ) ) {
+ foreach ( $screen as $single_screen ) {
+ remove_meta_box( $id, $single_screen, $context );
+ }
+ }
+
+ if ( ! isset( $screen->id ) ) {
+ return;
+ }
+
+ $page = $screen->id;
+
+ if ( !isset($wp_meta_boxes) )
+ $wp_meta_boxes = array();
+ if ( !isset($wp_meta_boxes[$page]) )
+ $wp_meta_boxes[$page] = array();
+ if ( !isset($wp_meta_boxes[$page][$context]) )
+ $wp_meta_boxes[$page][$context] = array();
+
+ foreach ( array('high', 'core', 'default', 'low') as $priority )
+ $wp_meta_boxes[$page][$context][$priority][$id] = false;
+}
+
+/**
+ * Meta Box Accordion Template Function
+ *
+ * Largely made up of abstracted code from {@link do_meta_boxes()}, this
+ * function serves to build meta boxes as list items for display as
+ * a collapsible accordion.
+ *
+ * @since 3.6.0
+ *
+ * @uses global $wp_meta_boxes Used to retrieve registered meta boxes.
+ *
+ * @param string|object $screen The screen identifier.
+ * @param string $context The meta box context.
+ * @param mixed $object gets passed to the section callback function as first parameter.
+ * @return int number of meta boxes as accordion sections.
+ */
+function do_accordion_sections( $screen, $context, $object ) {
+ global $wp_meta_boxes;
+
+ wp_enqueue_script( 'accordion' );
+
+ if ( empty( $screen ) )
+ $screen = get_current_screen();
+ elseif ( is_string( $screen ) )
+ $screen = convert_to_screen( $screen );
+
+ $page = $screen->id;
+
+ $hidden = get_hidden_meta_boxes( $screen );
+ ?>
+ <div id="side-sortables" class="accordion-container">
+ <ul class="outer-border">
+ <?php
+ $i = 0;
+ $first_open = false;
+
+ if ( isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
+ foreach ( array( 'high', 'core', 'default', 'low' ) as $priority ) {
+ if ( isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) {
+ foreach ( $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) {
+ if ( false == $box || ! $box['title'] )
+ continue;
+ $i++;
+ $hidden_class = in_array( $box['id'], $hidden ) ? 'hide-if-js' : '';
+
+ $open_class = '';
+ if ( ! $first_open && empty( $hidden_class ) ) {
+ $first_open = true;
+ $open_class = 'open';
+ }
+ ?>
+ <li class="control-section accordion-section <?php echo $hidden_class; ?> <?php echo $open_class; ?> <?php echo esc_attr( $box['id'] ); ?>" id="<?php echo esc_attr( $box['id'] ); ?>">
+ <h3 class="accordion-section-title hndle" tabindex="0">
+ <?php echo esc_html( $box['title'] ); ?>
+ <span class="screen-reader-text"><?php _e( 'Press return or enter to open this section' ); ?></span>
+ </h3>
+ <div class="accordion-section-content <?php postbox_classes( $box['id'], $page ); ?>">
+ <div class="inside">
+ <?php call_user_func( $box['callback'], $object, $box ); ?>
+ </div><!-- .inside -->
+ </div><!-- .accordion-section-content -->
+ </li><!-- .accordion-section -->
+ <?php
+ }
+ }
+ }
+ }
+ ?>
+ </ul><!-- .outer-border -->
+ </div><!-- .accordion-container -->
+ <?php
+ return $i;
+}
+
+/**
+ * Add a new section to a settings page.
+ *
+ * Part of the Settings API. Use this to define new settings sections for an admin page.
+ * Show settings sections in your admin page callback function with do_settings_sections().
+ * Add settings fields to your section with add_settings_field()
+ *
+ * The $callback argument should be the name of a function that echoes out any
+ * content you want to show at the top of the settings section before the actual
+ * fields. It can output nothing if you want.
+ *
+ * @since 2.7.0
+ *
+ * @global $wp_settings_sections Storage array of all settings sections added to admin pages
+ *
+ * @param string $id Slug-name to identify the section. Used in the 'id' attribute of tags.
+ * @param string $title Formatted title of the section. Shown as the heading for the section.
+ * @param string $callback Function that echos out any content at the top of the section (between heading and fields).
+ * @param string $page The slug-name of the settings page on which to show the section. Built-in pages include 'general', 'reading', 'writing', 'discussion', 'media', etc. Create your own using add_options_page();
+ */
+function add_settings_section($id, $title, $callback, $page) {
+ global $wp_settings_sections;
+
+ if ( 'misc' == $page ) {
+ _deprecated_argument( __FUNCTION__, '3.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) );
+ $page = 'general';
+ }
+
+ if ( 'privacy' == $page ) {
+ _deprecated_argument( __FUNCTION__, '3.5', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) );
+ $page = 'reading';
+ }
+
+ $wp_settings_sections[$page][$id] = array('id' => $id, 'title' => $title, 'callback' => $callback);
+}
+
+/**
+ * Add a new field to a section of a settings page
+ *
+ * Part of the Settings API. Use this to define a settings field that will show
+ * as part of a settings section inside a settings page. The fields are shown using
+ * do_settings_fields() in do_settings-sections()
+ *
+ * The $callback argument should be the name of a function that echoes out the
+ * html input tags for this setting field. Use get_option() to retrieve existing
+ * values to show.
+ *
+ * @since 2.7.0
+ * @since 4.2.0 The `$class` argument was added.
+ *
+ * @global $wp_settings_fields Storage array of settings fields and info about their pages/sections
+ *
+ * @param string $id Slug-name to identify the field. Used in the 'id' attribute of tags.
+ * @param string $title Formatted title of the field. Shown as the label for the field
+ * during output.
+ * @param string $callback Function that fills the field with the desired form inputs. The
+ * function should echo its output.
+ * @param string $page The slug-name of the settings page on which to show the section
+ * (general, reading, writing, ...).
+ * @param string $section Optional. The slug-name of the section of the settings page
+ * in which to show the box. Default 'default'.
+ * @param array $args {
+ * Optional. Extra arguments used when outputting the field.
+ *
+ * @type string $label_for When supplied, the setting title will be wrapped
+ * in a `<label>` element, its `for` attribute populated
+ * with this value.
+ * @type string $class CSS Class to be added to the `<tr>` element when the
+ * field is output.
+ * }
+ */
+function add_settings_field($id, $title, $callback, $page, $section = 'default', $args = array()) {
+ global $wp_settings_fields;
+
+ if ( 'misc' == $page ) {
+ _deprecated_argument( __FUNCTION__, '3.0', __( 'The miscellaneous options group has been removed. Use another settings group.' ) );
+ $page = 'general';
+ }
+
+ if ( 'privacy' == $page ) {
+ _deprecated_argument( __FUNCTION__, '3.5', __( 'The privacy options group has been removed. Use another settings group.' ) );
+ $page = 'reading';
+ }
+
+ $wp_settings_fields[$page][$section][$id] = array('id' => $id, 'title' => $title, 'callback' => $callback, 'args' => $args);
+}
+
+/**
+ * Prints out all settings sections added to a particular settings page
+ *
+ * Part of the Settings API. Use this in a settings page callback function
+ * to output all the sections and fields that were added to that $page with
+ * add_settings_section() and add_settings_field()
+ *
+ * @global $wp_settings_sections Storage array of all settings sections added to admin pages
+ * @global $wp_settings_fields Storage array of settings fields and info about their pages/sections
+ * @since 2.7.0
+ *
+ * @param string $page The slug name of the page whose settings sections you want to output
+ */
+function do_settings_sections( $page ) {
+ global $wp_settings_sections, $wp_settings_fields;
+
+ if ( ! isset( $wp_settings_sections[$page] ) )
+ return;
+
+ foreach ( (array) $wp_settings_sections[$page] as $section ) {
+ if ( $section['title'] )
+ echo "<h2>{$section['title']}</h2>\n";
+
+ if ( $section['callback'] )
+ call_user_func( $section['callback'], $section );
+
+ if ( ! isset( $wp_settings_fields ) || !isset( $wp_settings_fields[$page] ) || !isset( $wp_settings_fields[$page][$section['id']] ) )
+ continue;
+ echo '<table class="form-table">';
+ do_settings_fields( $page, $section['id'] );
+ echo '</table>';
+ }
+}
+
+/**
+ * Print out the settings fields for a particular settings section
+ *
+ * Part of the Settings API. Use this in a settings page to output
+ * a specific section. Should normally be called by do_settings_sections()
+ * rather than directly.
+ *
+ * @global $wp_settings_fields Storage array of settings fields and their pages/sections
+ *
+ * @since 2.7.0
+ *
+ * @param string $page Slug title of the admin page who's settings fields you want to show.
+ * @param string $section Slug title of the settings section who's fields you want to show.
+ */
+function do_settings_fields($page, $section) {
+ global $wp_settings_fields;
+
+ if ( ! isset( $wp_settings_fields[$page][$section] ) )
+ return;
+
+ foreach ( (array) $wp_settings_fields[$page][$section] as $field ) {
+ $class = '';
+
+ if ( ! empty( $field['args']['class'] ) ) {
+ $class = ' class="' . esc_attr( $field['args']['class'] ) . '"';
+ }
+
+ echo "<tr{$class}>";
+
+ if ( ! empty( $field['args']['label_for'] ) ) {
+ echo '<th scope="row"><label for="' . esc_attr( $field['args']['label_for'] ) . '">' . $field['title'] . '</label></th>';
+ } else {
+ echo '<th scope="row">' . $field['title'] . '</th>';
+ }
+
+ echo '<td>';
+ call_user_func($field['callback'], $field['args']);
+ echo '</td>';
+ echo '</tr>';
+ }
+}
+
+/**
+ * Register a settings error to be displayed to the user
+ *
+ * Part of the Settings API. Use this to show messages to users about settings validation
+ * problems, missing settings or anything else.
+ *
+ * Settings errors should be added inside the $sanitize_callback function defined in
+ * register_setting() for a given setting to give feedback about the submission.
+ *
+ * By default messages will show immediately after the submission that generated the error.
+ * Additional calls to settings_errors() can be used to show errors even when the settings
+ * page is first accessed.
+ *
+ * @since 3.0.0
+ *
+ * @global array $wp_settings_errors Storage array of errors registered during this pageload
+ *
+ * @param string $setting Slug title of the setting to which this error applies
+ * @param string $code Slug-name to identify the error. Used as part of 'id' attribute in HTML output.
+ * @param string $message The formatted message text to display to the user (will be shown inside styled
+ * `<div>` and `<p>` tags).
+ * @param string $type Optional. Message type, controls HTML class. Accepts 'error' or 'updated'.
+ * Default 'error'.
+ */
+function add_settings_error( $setting, $code, $message, $type = 'error' ) {
+ global $wp_settings_errors;
+
+ $wp_settings_errors[] = array(
+ 'setting' => $setting,
+ 'code' => $code,
+ 'message' => $message,
+ 'type' => $type
+ );
+}
+
+/**
+ * Fetch settings errors registered by add_settings_error()
+ *
+ * Checks the $wp_settings_errors array for any errors declared during the current
+ * pageload and returns them.
+ *
+ * If changes were just submitted ($_GET['settings-updated']) and settings errors were saved
+ * to the 'settings_errors' transient then those errors will be returned instead. This
+ * is used to pass errors back across pageloads.
+ *
+ * Use the $sanitize argument to manually re-sanitize the option before returning errors.
+ * This is useful if you have errors or notices you want to show even when the user
+ * hasn't submitted data (i.e. when they first load an options page, or in admin_notices action hook)
+ *
+ * @since 3.0.0
+ *
+ * @global array $wp_settings_errors Storage array of errors registered during this pageload
+ *
+ * @param string $setting Optional slug title of a specific setting who's errors you want.
+ * @param boolean $sanitize Whether to re-sanitize the setting value before returning errors.
+ * @return array Array of settings errors
+ */
+function get_settings_errors( $setting = '', $sanitize = false ) {
+ global $wp_settings_errors;
+
+ /*
+ * If $sanitize is true, manually re-run the sanitization for this option
+ * This allows the $sanitize_callback from register_setting() to run, adding
+ * any settings errors you want to show by default.
+ */
+ if ( $sanitize )
+ sanitize_option( $setting, get_option( $setting ) );
+
+ // If settings were passed back from options.php then use them.
+ if ( isset( $_GET['settings-updated'] ) && $_GET['settings-updated'] && get_transient( 'settings_errors' ) ) {
+ $wp_settings_errors = array_merge( (array) $wp_settings_errors, get_transient( 'settings_errors' ) );
+ delete_transient( 'settings_errors' );
+ }
+
+ // Check global in case errors have been added on this pageload.
+ if ( ! count( $wp_settings_errors ) )
+ return array();
+
+ // Filter the results to those of a specific setting if one was set.
+ if ( $setting ) {
+ $setting_errors = array();
+ foreach ( (array) $wp_settings_errors as $key => $details ) {
+ if ( $setting == $details['setting'] )
+ $setting_errors[] = $wp_settings_errors[$key];
+ }
+ return $setting_errors;
+ }
+
+ return $wp_settings_errors;
+}
+
+/**
+ * Display settings errors registered by {@see add_settings_error()}.
+ *
+ * Part of the Settings API. Outputs a div for each error retrieved by
+ * {@see get_settings_errors()}.
+ *
+ * This is called automatically after a settings page based on the
+ * Settings API is submitted. Errors should be added during the validation
+ * callback function for a setting defined in {@see register_setting()}
+ *
+ * The $sanitize option is passed into {@see get_settings_errors()} and will
+ * re-run the setting sanitization
+ * on its current value.
+ *
+ * The $hide_on_update option will cause errors to only show when the settings
+ * page is first loaded. if the user has already saved new values it will be
+ * hidden to avoid repeating messages already shown in the default error
+ * reporting after submission. This is useful to show general errors like
+ * missing settings when the user arrives at the settings page.
+ *
+ * @since 3.0.0
+ *
+ * @param string $setting Optional slug title of a specific setting who's errors you want.
+ * @param bool $sanitize Whether to re-sanitize the setting value before returning errors.
+ * @param bool $hide_on_update If set to true errors will not be shown if the settings page has already been submitted.
+ */
+function settings_errors( $setting = '', $sanitize = false, $hide_on_update = false ) {
+
+ if ( $hide_on_update && ! empty( $_GET['settings-updated'] ) )
+ return;
+
+ $settings_errors = get_settings_errors( $setting, $sanitize );
+
+ if ( empty( $settings_errors ) )
+ return;
+
+ $output = '';
+ foreach ( $settings_errors as $key => $details ) {
+ $css_id = 'setting-error-' . $details['code'];
+ $css_class = $details['type'] . ' settings-error notice is-dismissible';
+ $output .= "<div id='$css_id' class='$css_class'> \n";
+ $output .= "<p><strong>{$details['message']}</strong></p>";
+ $output .= "</div> \n";
+ }
+ echo $output;
+}
+
+/**
+ * Outputs the modal window used for attaching media to posts or pages in the media-listing screen.
+ *
+ * @since 2.7.0
+ *
+ * @param string $found_action
+ */
+function find_posts_div($found_action = '') {
+?>
+ <div id="find-posts" class="find-box" style="display: none;">
+ <div id="find-posts-head" class="find-box-head">
+ <?php _e( 'Find Posts or Pages' ); ?>
+ <div id="find-posts-close"></div>
+ </div>
+ <div class="find-box-inside">
+ <div class="find-box-search">
+ <?php if ( $found_action ) { ?>
+ <input type="hidden" name="found_action" value="<?php echo esc_attr($found_action); ?>" />
+ <?php } ?>
+ <input type="hidden" name="affected" id="affected" value="" />
+ <?php wp_nonce_field( 'find-posts', '_ajax_nonce', false ); ?>
+ <label class="screen-reader-text" for="find-posts-input"><?php _e( 'Search' ); ?></label>
+ <input type="text" id="find-posts-input" name="ps" value="" />
+ <span class="spinner"></span>
+ <input type="button" id="find-posts-search" value="<?php esc_attr_e( 'Search' ); ?>" class="button" />
+ <div class="clear"></div>
+ </div>
+ <div id="find-posts-response"></div>
+ </div>
+ <div class="find-box-buttons">
+ <?php submit_button( __( 'Select' ), 'button-primary alignright', 'find-posts-submit', false ); ?>
+ <div class="clear"></div>
+ </div>
+ </div>
+<?php
+}
+
+/**
+ * Display the post password.
+ *
+ * The password is passed through {@link esc_attr()} to ensure that it
+ * is safe for placing in an html attribute.
+ *
+ * @since 2.7.0
+ */
+function the_post_password() {
+ $post = get_post();
+ if ( isset( $post->post_password ) )
+ echo esc_attr( $post->post_password );
+}
+
+/**
+ * Get the post title.
+ *
+ * The post title is fetched and if it is blank then a default string is
+ * returned.
+ *
+ * @since 2.7.0
+ *
+ * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
+ * @return string The post title if set.
+ */
+function _draft_or_post_title( $post = 0 ) {
+ $title = get_the_title( $post );
+ if ( empty( $title ) )
+ $title = __( '(no title)' );
+ return esc_html( $title );
+}
+
+/**
+ * Display the search query.
+ *
+ * A simple wrapper to display the "s" parameter in a GET URI. This function
+ * should only be used when {@link the_search_query()} cannot.
+ *
+ * @since 2.7.0
+ */
+function _admin_search_query() {
+ echo isset($_REQUEST['s']) ? esc_attr( wp_unslash( $_REQUEST['s'] ) ) : '';
+}
+
+/**
+ * Generic Iframe header for use with Thickbox
+ *
+ * @since 2.7.0
+ *
+ * @global string $hook_suffix
+ * @global string $admin_body_class
+ * @global WP_Locale $wp_locale
+ *
+ * @param string $title Optional. Title of the Iframe page. Default empty.
+ * @param bool $deprecated Not used.
+ */
+function iframe_header( $title = '', $deprecated = false ) {
+ show_admin_bar( false );
+ global $hook_suffix, $admin_body_class, $wp_locale;
+ $admin_body_class = preg_replace('/[^a-z0-9_-]+/i', '-', $hook_suffix);
+
+ $current_screen = get_current_screen();
+
+ @header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) );
+ _wp_admin_html_begin();
+?>
+<title><?php bloginfo('name') ?> › <?php echo $title ?> — <?php _e('WordPress'); ?></title>
+<?php
+wp_enqueue_style( 'colors' );
+?>
+<script type="text/javascript">
+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();}}};
+function tb_close(){var win=window.dialogArguments||opener||parent||top;win.tb_remove();}
+var ajaxurl = '<?php echo admin_url( 'admin-ajax.php', 'relative' ); ?>',
+ pagenow = '<?php echo $current_screen->id; ?>',
+ typenow = '<?php echo $current_screen->post_type; ?>',
+ adminpage = '<?php echo $admin_body_class; ?>',
+ thousandsSeparator = '<?php echo addslashes( $wp_locale->number_format['thousands_sep'] ); ?>',
+ decimalPoint = '<?php echo addslashes( $wp_locale->number_format['decimal_point'] ); ?>',
+ isRtl = <?php echo (int) is_rtl(); ?>;
+</script>
+<?php
+/** This action is documented in wp-admin/admin-header.php */
+do_action( 'admin_enqueue_scripts', $hook_suffix );
+
+/** This action is documented in wp-admin/admin-header.php */
+do_action( "admin_print_styles-$hook_suffix" );
+
+/** This action is documented in wp-admin/admin-header.php */
+do_action( 'admin_print_styles' );
+
+/** This action is documented in wp-admin/admin-header.php */
+do_action( "admin_print_scripts-$hook_suffix" );
+
+/** This action is documented in wp-admin/admin-header.php */
+do_action( 'admin_print_scripts' );
+
+/** This action is documented in wp-admin/admin-header.php */
+do_action( "admin_head-$hook_suffix" );
+
+/** This action is documented in wp-admin/admin-header.php */
+do_action( 'admin_head' );
+
+$admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) );
+
+if ( is_rtl() )
+ $admin_body_class .= ' rtl';
+
+?>
+</head>
+<?php
+/** This filter is documented in wp-admin/admin-header.php */
+$admin_body_classes = apply_filters( 'admin_body_class', '' );
+?>
+<body<?php
+/**
+ * @global string $body_id
+ */
+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; ?>">
+<script type="text/javascript">
+(function(){
+var c = document.body.className;
+c = c.replace(/no-js/, 'js');
+document.body.className = c;
+})();
+</script>
+<?php
+}
+
+/**
+ * Generic Iframe footer for use with Thickbox
+ *
+ * @since 2.7.0
+ */
+function iframe_footer() {
+ /*
+ * We're going to hide any footer output on iFrame pages,
+ * but run the hooks anyway since they output JavaScript
+ * or other needed content.
+ */
+ ?>
+ <div class="hidden">
+<?php
+ /** This action is documented in wp-admin/admin-footer.php */
+ do_action( 'admin_footer', '' );
+
+ /** This action is documented in wp-admin/admin-footer.php */
+ do_action( 'admin_print_footer_scripts' );
+?>
+ </div>
+<script type="text/javascript">if(typeof wpOnload=="function")wpOnload();</script>
+</body>
+</html>
+<?php
+}
+
+/**
+ *
+ * @param WP_Post $post
+ */
+function _post_states($post) {
+ $post_states = array();
+ if ( isset( $_REQUEST['post_status'] ) )
+ $post_status = $_REQUEST['post_status'];
+ else
+ $post_status = '';
+
+ if ( !empty($post->post_password) )
+ $post_states['protected'] = __('Password protected');
+ if ( 'private' == $post->post_status && 'private' != $post_status )
+ $post_states['private'] = __('Private');
+ if ( 'draft' == $post->post_status && 'draft' != $post_status )
+ $post_states['draft'] = __('Draft');
+ if ( 'pending' == $post->post_status && 'pending' != $post_status )
+ /* translators: post state */
+ $post_states['pending'] = _x('Pending', 'post state');
+ if ( is_sticky($post->ID) )
+ $post_states['sticky'] = __('Sticky');
+
+ if ( 'future' === $post->post_status ) {
+ $post_states['scheduled'] = __( 'Scheduled' );
+ }
+
+ if ( 'page' === get_option( 'show_on_front' ) ) {
+ if ( intval( get_option( 'page_on_front' ) ) === $post->ID ) {
+ $post_states['page_on_front'] = __( 'Front Page' );
+ }
+
+ if ( intval( get_option( 'page_for_posts' ) ) === $post->ID ) {
+ $post_states['page_for_posts'] = __( 'Posts Page' );
+ }
+ }
+
+ /**
+ * Filter the default post display states used in the posts list table.
+ *
+ * @since 2.8.0
+ *
+ * @param array $post_states An array of post display states.
+ * @param WP_Post $post The current post object.
+ */
+ $post_states = apply_filters( 'display_post_states', $post_states, $post );
+
+ if ( ! empty($post_states) ) {
+ $state_count = count($post_states);
+ $i = 0;
+ echo ' — ';
+ foreach ( $post_states as $state ) {
+ ++$i;
+ ( $i == $state_count ) ? $sep = '' : $sep = ', ';
+ echo "<span class='post-state'>$state$sep</span>";
+ }
+ }
+
+}
+
+/**
+ *
+ * @param WP_Post $post
+ */
+function _media_states( $post ) {
+ $media_states = array();
+ $stylesheet = get_option('stylesheet');
+
+ if ( current_theme_supports( 'custom-header') ) {
+ $meta_header = get_post_meta($post->ID, '_wp_attachment_is_custom_header', true );
+ if ( ! empty( $meta_header ) && $meta_header == $stylesheet )
+ $media_states[] = __( 'Header Image' );
+ }
+
+ if ( current_theme_supports( 'custom-background') ) {
+ $meta_background = get_post_meta($post->ID, '_wp_attachment_is_custom_background', true );
+ if ( ! empty( $meta_background ) && $meta_background == $stylesheet )
+ $media_states[] = __( 'Background Image' );
+ }
+
+ if ( $post->ID == get_option( 'site_icon' ) ) {
+ $media_states[] = __( 'Site Icon' );
+ }
+
+ /**
+ * Filter the default media display states for items in the Media list table.
+ *
+ * @since 3.2.0
+ *
+ * @param array $media_states An array of media states. Default 'Header Image',
+ * 'Background Image', 'Site Icon'.
+ */
+ $media_states = apply_filters( 'display_media_states', $media_states );
+
+ if ( ! empty( $media_states ) ) {
+ $state_count = count( $media_states );
+ $i = 0;
+ echo ' — ';
+ foreach ( $media_states as $state ) {
+ ++$i;
+ ( $i == $state_count ) ? $sep = '' : $sep = ', ';
+ echo "<span class='post-state'>$state$sep</span>";
+ }
+ }
+}
+
+/**
+ * Test support for compressing JavaScript from PHP
+ *
+ * Outputs JavaScript that tests if compression from PHP works as expected
+ * and sets an option with the result. Has no effect when the current user
+ * is not an administrator. To run the test again the option 'can_compress_scripts'
+ * has to be deleted.
+ *
+ * @since 2.8.0
+ */
+function compression_test() {
+?>
+ <script type="text/javascript">
+ var testCompression = {
+ get : function(test) {
+ var x;
+ if ( window.XMLHttpRequest ) {
+ x = new XMLHttpRequest();
+ } else {
+ try{x=new ActiveXObject('Msxml2.XMLHTTP');}catch(e){try{x=new ActiveXObject('Microsoft.XMLHTTP');}catch(e){};}
+ }
+
+ if (x) {
+ x.onreadystatechange = function() {
+ var r, h;
+ if ( x.readyState == 4 ) {
+ r = x.responseText.substr(0, 18);
+ h = x.getResponseHeader('Content-Encoding');
+ testCompression.check(r, h, test);
+ }
+ };
+
+ x.open('GET', ajaxurl + '?action=wp-compression-test&test='+test+'&'+(new Date()).getTime(), true);
+ x.send('');
+ }
+ },
+
+ check : function(r, h, test) {
+ if ( ! r && ! test )
+ this.get(1);
+
+ if ( 1 == test ) {
+ if ( h && ( h.match(/deflate/i) || h.match(/gzip/i) ) )
+ this.get('no');
+ else
+ this.get(2);
+
+ return;
+ }
+
+ if ( 2 == test ) {
+ if ( '"wpCompressionTest' == r )
+ this.get('yes');
+ else
+ this.get('no');
+ }
+ }
+ };
+ testCompression.check();
+ </script>
+<?php
+}
+
+/**
+ * Echoes a submit button, with provided text and appropriate class(es).
+ *
+ * @since 3.1.0
+ *
+ * @see get_submit_button()
+ *
+ * @param string $text The text of the button (defaults to 'Save Changes')
+ * @param string $type Optional. The type and CSS class(es) of the button. Core values
+ * include 'primary', 'secondary', 'delete'. Default 'primary'
+ * @param string $name The HTML name of the submit button. Defaults to "submit". If no
+ * id attribute is given in $other_attributes below, $name will be
+ * used as the button's id.
+ * @param bool $wrap True if the output button should be wrapped in a paragraph tag,
+ * false otherwise. Defaults to true
+ * @param array|string $other_attributes Other attributes that should be output with the button, mapping
+ * attributes to their values, such as setting tabindex to 1, etc.
+ * These key/value attribute pairs will be output as attribute="value",
+ * where attribute is the key. Other attributes can also be provided
+ * as a string such as 'tabindex="1"', though the array format is
+ * preferred. Default null.
+ */
+function submit_button( $text = null, $type = 'primary', $name = 'submit', $wrap = true, $other_attributes = null ) {
+ echo get_submit_button( $text, $type, $name, $wrap, $other_attributes );
+}
+
+/**
+ * Returns a submit button, with provided text and appropriate class
+ *
+ * @since 3.1.0
+ *
+ * @param string $text Optional. The text of the button. Default 'Save Changes'.
+ * @param string $type Optional. The type of button. Accepts 'primary', 'secondary',
+ * or 'delete'. Default 'primary large'.
+ * @param string $name Optional. The HTML name of the submit button. Defaults to "submit".
+ * If no id attribute is given in $other_attributes below, `$name` will
+ * be used as the button's id. Default 'submit'.
+ * @param bool $wrap Optional. True if the output button should be wrapped in a paragraph
+ * tag, false otherwise. Default true.
+ * @param array|string $other_attributes Optional. Other attributes that should be output with the button,
+ * mapping attributes to their values, such as `array( 'tabindex' => '1' )`.
+ * These attributes will be output as `attribute="value"`, such as
+ * `tabindex="1"`. Other attributes can also be provided as a string such
+ * as `tabindex="1"`, though the array format is typically cleaner.
+ * Default empty.
+ * @return string Submit button HTML.
+ */
+function get_submit_button( $text = '', $type = 'primary large', $name = 'submit', $wrap = true, $other_attributes = '' ) {
+ if ( ! is_array( $type ) )
+ $type = explode( ' ', $type );
+
+ $button_shorthand = array( 'primary', 'small', 'large' );
+ $classes = array( 'button' );
+ foreach ( $type as $t ) {
+ if ( 'secondary' === $t || 'button-secondary' === $t )
+ continue;
+ $classes[] = in_array( $t, $button_shorthand ) ? 'button-' . $t : $t;
+ }
+ $class = implode( ' ', array_unique( $classes ) );
+
+ if ( 'delete' === $type )
+ $class = 'button-secondary delete';
+
+ $text = $text ? $text : __( 'Save Changes' );
+
+ // Default the id attribute to $name unless an id was specifically provided in $other_attributes
+ $id = $name;
+ if ( is_array( $other_attributes ) && isset( $other_attributes['id'] ) ) {
+ $id = $other_attributes['id'];
+ unset( $other_attributes['id'] );
+ }
+
+ $attributes = '';
+ if ( is_array( $other_attributes ) ) {
+ foreach ( $other_attributes as $attribute => $value ) {
+ $attributes .= $attribute . '="' . esc_attr( $value ) . '" '; // Trailing space is important
+ }
+ } elseif ( ! empty( $other_attributes ) ) { // Attributes provided as a string
+ $attributes = $other_attributes;
+ }
+
+ // Don't output empty name and id attributes.
+ $name_attr = $name ? ' name="' . esc_attr( $name ) . '"' : '';
+ $id_attr = $id ? ' id="' . esc_attr( $id ) . '"' : '';
+
+ $button = '<input type="submit"' . $name_attr . $id_attr . ' class="' . esc_attr( $class );
+ $button .= '" value="' . esc_attr( $text ) . '" ' . $attributes . ' />';
+
+ if ( $wrap ) {
+ $button = '<p class="submit">' . $button . '</p>';
+ }
+
+ return $button;
+}
+
+/**
+ *
+ * @global bool $is_IE
+ */
+function _wp_admin_html_begin() {
+ global $is_IE;
+
+ $admin_html_class = ( is_admin_bar_showing() ) ? 'wp-toolbar' : '';
+
+ if ( $is_IE )
+ @header('X-UA-Compatible: IE=edge');
+
+?>
+<!DOCTYPE html>
+<!--[if IE 8]>
+<html xmlns="http://www.w3.org/1999/xhtml" class="ie8 <?php echo $admin_html_class; ?>" <?php
+ /**
+ * Fires inside the HTML tag in the admin header.
+ *
+ * @since 2.2.0
+ */
+ do_action( 'admin_xml_ns' );
+?> <?php language_attributes(); ?>>
+<![endif]-->
+<!--[if !(IE 8) ]><!-->
+<html xmlns="http://www.w3.org/1999/xhtml" class="<?php echo $admin_html_class; ?>" <?php
+ /** This action is documented in wp-admin/includes/template-functions.php */
+ do_action( 'admin_xml_ns' );
+?> <?php language_attributes(); ?>>
+<!--<![endif]-->
+<head>
+<meta http-equiv="Content-Type" content="<?php bloginfo('html_type'); ?>; charset=<?php echo get_option('blog_charset'); ?>" />
+<?php
+}
+
+/**
+ * Convert a screen string to a screen object
+ *
+ * @since 3.0.0
+ *
+ * @param string $hook_name The hook name (also known as the hook suffix) used to determine the screen.
+ * @return WP_Screen Screen object.
+ */
+function convert_to_screen( $hook_name ) {
+ if ( ! class_exists( 'WP_Screen', false ) ) {
+ _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' );
+ return (object) array( 'id' => '_invalid', 'base' => '_are_belong_to_us' );
+ }
+
+ return WP_Screen::get( $hook_name );
+}
+
+/**
+ * Output the HTML for restoring the post data from DOM storage
+ *
+ * @since 3.6.0
+ * @access private
+ */
+function _local_storage_notice() {
+ ?>
+ <div id="local-storage-notice" class="hidden notice">
+ <p class="local-restore">
+ <?php _e('The backup of this post in your browser is different from the version below.'); ?>
+ <a class="restore-backup" href="#"><?php _e('Restore the backup.'); ?></a>
+ </p>
+ <p class="undo-restore hidden">
+ <?php _e('Post restored successfully.'); ?>
+ <a class="undo-restore-backup" href="#"><?php _e('Undo.'); ?></a>
+ </p>
+ </div>
+ <?php
+}
+
+/**
+ * Output a HTML element with a star rating for a given rating.
+ *
+ * Outputs a HTML element with the star rating exposed on a 0..5 scale in
+ * half star increments (ie. 1, 1.5, 2 stars). Optionally, if specified, the
+ * number of ratings may also be displayed by passing the $number parameter.
+ *
+ * @since 3.8.0
+ * @since 4.4.0 Introduced the `echo` parameter.
+ *
+ * @param array $args {
+ * Optional. Array of star ratings arguments.
+ *
+ * @type int $rating The rating to display, expressed in either a 0.5 rating increment,
+ * or percentage. Default 0.
+ * @type string $type Format that the $rating is in. Valid values are 'rating' (default),
+ * or, 'percent'. Default 'rating'.
+ * @type int $number The number of ratings that makes up this rating. Default 0.
+ * @type bool $echo Whether to echo the generated markup. False to return the markup instead
+ * of echoing it. Default true.
+ * }
+ */
+function wp_star_rating( $args = array() ) {
+ $defaults = array(
+ 'rating' => 0,
+ 'type' => 'rating',
+ 'number' => 0,
+ 'echo' => true,
+ );
+ $r = wp_parse_args( $args, $defaults );
+
+ // Non-english decimal places when the $rating is coming from a string
+ $rating = str_replace( ',', '.', $r['rating'] );
+
+ // Convert Percentage to star rating, 0..5 in .5 increments
+ if ( 'percent' == $r['type'] ) {
+ $rating = round( $rating / 10, 0 ) / 2;
+ }
+
+ // Calculate the number of each type of star needed
+ $full_stars = floor( $rating );
+ $half_stars = ceil( $rating - $full_stars );
+ $empty_stars = 5 - $full_stars - $half_stars;
+
+ if ( $r['number'] ) {
+ /* translators: 1: The rating, 2: The number of ratings */
+ $format = _n( '%1$s rating based on %2$s rating', '%1$s rating based on %2$s ratings', $r['number'] );
+ $title = sprintf( $format, number_format_i18n( $rating, 1 ), number_format_i18n( $r['number'] ) );
+ } else {
+ /* translators: 1: The rating */
+ $title = sprintf( __( '%s rating' ), number_format_i18n( $rating, 1 ) );
+ }
+
+ $output = '<div class="star-rating" title="' . esc_attr( $title ) . '">';
+ $output .= '<span class="screen-reader-text">' . $title . '</span>';
+ $output .= str_repeat( '<div class="star star-full"></div>', $full_stars );
+ $output .= str_repeat( '<div class="star star-half"></div>', $half_stars );
+ $output .= str_repeat( '<div class="star star-empty"></div>', $empty_stars );
+ $output .= '</div>';
+
+ if ( $r['echo'] ) {
+ echo $output;
+ }
+
+ return $output;
+}
+
+/**
+ * Output a notice when editing the page for posts (internal use only).
+ *
+ * @ignore
+ * @since 4.2.0
+ */
+function _wp_posts_page_notice() {
+ echo '<div class="notice notice-warning inline"><p>' . __( 'You are currently editing the page that shows your latest posts.' ) . '</p></div>';
+}
</ins></span></pre></div>
<a id="trunksrcwpadminincludesupdatecorephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/update-core.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/update-core.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-admin/includes/update-core.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -43,6 +43,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 'wp-admin/link-categories.php',
</span><span class="cx" style="display: block; padding: 0 10px"> 'wp-admin/list-manipulation.js',
</span><span class="cx" style="display: block; padding: 0 10px"> 'wp-admin/list-manipulation.php',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+'wp-includes/comment-functions.php',
</ins><span class="cx" style="display: block; padding: 0 10px"> 'wp-includes/feed-functions.php',
</span><span class="cx" style="display: block; padding: 0 10px"> 'wp-includes/functions-compat.php',
</span><span class="cx" style="display: block; padding: 0 10px"> 'wp-includes/functions-formatting.php',
</span></span></pre></div>
<a id="trunksrcwpincludescapabilitiesfunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/capabilities-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/capabilities-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/capabilities-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,612 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * User API: Top-level role and capabilities functionality
- *
- * @package WordPress
- * @subpackage Users
- * @since 4.4.0
- */
-
-/**
- * Map meta capabilities to primitive capabilities.
- *
- * This does not actually compare whether the user ID has the actual capability,
- * just what the capability or capabilities are. Meta capability list value can
- * be 'delete_user', 'edit_user', 'remove_user', 'promote_user', 'delete_post',
- * 'delete_page', 'edit_post', 'edit_page', 'read_post', or 'read_page'.
- *
- * @since 2.0.0
- *
- * @param string $cap Capability name.
- * @param int $user_id User ID.
- * @param int $object_id Optional. ID of the specific object to check against if `$cap` is a "meta" cap.
- * "Meta" capabilities, e.g. 'edit_post', 'edit_user', etc., are capabilities used
- * by map_meta_cap() to map to other "primitive" capabilities, e.g. 'edit_posts',
- * 'edit_others_posts', etc. The parameter is accessed via func_get_args().
- * @return array Actual capabilities for meta capability.
- */
-function map_meta_cap( $cap, $user_id ) {
- $args = array_slice( func_get_args(), 2 );
- $caps = array();
-
- switch ( $cap ) {
- case 'remove_user':
- $caps[] = 'remove_users';
- break;
- case 'promote_user':
- case 'add_users':
- $caps[] = 'promote_users';
- break;
- case 'edit_user':
- case 'edit_users':
- // Allow user to edit itself
- if ( 'edit_user' == $cap && isset( $args[0] ) && $user_id == $args[0] )
- break;
-
- // In multisite the user must have manage_network_users caps. If editing a super admin, the user must be a super admin.
- if ( is_multisite() && ( ( ! is_super_admin( $user_id ) && 'edit_user' === $cap && is_super_admin( $args[0] ) ) || ! user_can( $user_id, 'manage_network_users' ) ) ) {
- $caps[] = 'do_not_allow';
- } else {
- $caps[] = 'edit_users'; // edit_user maps to edit_users.
- }
- break;
- case 'delete_post':
- case 'delete_page':
- $post = get_post( $args[0] );
- if ( ! $post ) {
- $caps[] = 'do_not_allow';
- break;
- }
-
- if ( 'revision' == $post->post_type ) {
- $post = get_post( $post->post_parent );
- if ( ! $post ) {
- $caps[] = 'do_not_allow';
- break;
- }
- }
-
- $post_type = get_post_type_object( $post->post_type );
- if ( ! $post_type ) {
- /* translators: 1: post type, 2: capability name */
- _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
- $caps[] = 'edit_others_posts';
- break;
- }
-
- if ( ! $post_type->map_meta_cap ) {
- $caps[] = $post_type->cap->$cap;
- // Prior to 3.1 we would re-call map_meta_cap here.
- if ( 'delete_post' == $cap )
- $cap = $post_type->cap->$cap;
- break;
- }
-
- // If the post author is set and the user is the author...
- if ( $post->post_author && $user_id == $post->post_author ) {
- // If the post is published...
- if ( 'publish' == $post->post_status ) {
- $caps[] = $post_type->cap->delete_published_posts;
- } elseif ( 'trash' == $post->post_status ) {
- if ( 'publish' == get_post_meta( $post->ID, '_wp_trash_meta_status', true ) ) {
- $caps[] = $post_type->cap->delete_published_posts;
- }
- } else {
- // If the post is draft...
- $caps[] = $post_type->cap->delete_posts;
- }
- } else {
- // The user is trying to edit someone else's post.
- $caps[] = $post_type->cap->delete_others_posts;
- // The post is published, extra cap required.
- if ( 'publish' == $post->post_status ) {
- $caps[] = $post_type->cap->delete_published_posts;
- } elseif ( 'private' == $post->post_status ) {
- $caps[] = $post_type->cap->delete_private_posts;
- }
- }
- break;
- // edit_post breaks down to edit_posts, edit_published_posts, or
- // edit_others_posts
- case 'edit_post':
- case 'edit_page':
- $post = get_post( $args[0] );
- if ( ! $post ) {
- $caps[] = 'do_not_allow';
- break;
- }
-
- if ( 'revision' == $post->post_type ) {
- $post = get_post( $post->post_parent );
- if ( ! $post ) {
- $caps[] = 'do_not_allow';
- break;
- }
- }
-
- $post_type = get_post_type_object( $post->post_type );
- if ( ! $post_type ) {
- /* translators: 1: post type, 2: capability name */
- _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
- $caps[] = 'edit_others_posts';
- break;
- }
-
- if ( ! $post_type->map_meta_cap ) {
- $caps[] = $post_type->cap->$cap;
- // Prior to 3.1 we would re-call map_meta_cap here.
- if ( 'edit_post' == $cap )
- $cap = $post_type->cap->$cap;
- break;
- }
-
- // If the post author is set and the user is the author...
- if ( $post->post_author && $user_id == $post->post_author ) {
- // If the post is published...
- if ( 'publish' == $post->post_status ) {
- $caps[] = $post_type->cap->edit_published_posts;
- } elseif ( 'trash' == $post->post_status ) {
- if ( 'publish' == get_post_meta( $post->ID, '_wp_trash_meta_status', true ) ) {
- $caps[] = $post_type->cap->edit_published_posts;
- }
- } else {
- // If the post is draft...
- $caps[] = $post_type->cap->edit_posts;
- }
- } else {
- // The user is trying to edit someone else's post.
- $caps[] = $post_type->cap->edit_others_posts;
- // The post is published, extra cap required.
- if ( 'publish' == $post->post_status ) {
- $caps[] = $post_type->cap->edit_published_posts;
- } elseif ( 'private' == $post->post_status ) {
- $caps[] = $post_type->cap->edit_private_posts;
- }
- }
- break;
- case 'read_post':
- case 'read_page':
- $post = get_post( $args[0] );
- if ( ! $post ) {
- $caps[] = 'do_not_allow';
- break;
- }
-
- if ( 'revision' == $post->post_type ) {
- $post = get_post( $post->post_parent );
- if ( ! $post ) {
- $caps[] = 'do_not_allow';
- break;
- }
- }
-
- $post_type = get_post_type_object( $post->post_type );
- if ( ! $post_type ) {
- /* translators: 1: post type, 2: capability name */
- _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
- $caps[] = 'edit_others_posts';
- break;
- }
-
- if ( ! $post_type->map_meta_cap ) {
- $caps[] = $post_type->cap->$cap;
- // Prior to 3.1 we would re-call map_meta_cap here.
- if ( 'read_post' == $cap )
- $cap = $post_type->cap->$cap;
- break;
- }
-
- $status_obj = get_post_status_object( $post->post_status );
- if ( $status_obj->public ) {
- $caps[] = $post_type->cap->read;
- break;
- }
-
- if ( $post->post_author && $user_id == $post->post_author ) {
- $caps[] = $post_type->cap->read;
- } elseif ( $status_obj->private ) {
- $caps[] = $post_type->cap->read_private_posts;
- } else {
- $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
- }
- break;
- case 'publish_post':
- $post = get_post( $args[0] );
- if ( ! $post ) {
- $caps[] = 'do_not_allow';
- break;
- }
-
- $post_type = get_post_type_object( $post->post_type );
- if ( ! $post_type ) {
- /* translators: 1: post type, 2: capability name */
- _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
- $caps[] = 'edit_others_posts';
- break;
- }
-
- $caps[] = $post_type->cap->publish_posts;
- break;
- case 'edit_post_meta':
- case 'delete_post_meta':
- case 'add_post_meta':
- $post = get_post( $args[0] );
- if ( ! $post ) {
- $caps[] = 'do_not_allow';
- break;
- }
-
- $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
-
- $meta_key = isset( $args[ 1 ] ) ? $args[ 1 ] : false;
-
- if ( $meta_key && has_filter( "auth_post_meta_{$meta_key}" ) ) {
- /**
- * Filter whether the user is allowed to add post meta to a post.
- *
- * The dynamic portion of the hook name, `$meta_key`, refers to the
- * meta key passed to {@see map_meta_cap()}.
- *
- * @since 3.3.0
- *
- * @param bool $allowed Whether the user can add the post meta. Default false.
- * @param string $meta_key The meta key.
- * @param int $post_id Post ID.
- * @param int $user_id User ID.
- * @param string $cap Capability name.
- * @param array $caps User capabilities.
- */
- $allowed = apply_filters( "auth_post_meta_{$meta_key}", false, $meta_key, $post->ID, $user_id, $cap, $caps );
- if ( ! $allowed )
- $caps[] = $cap;
- } elseif ( $meta_key && is_protected_meta( $meta_key, 'post' ) ) {
- $caps[] = $cap;
- }
- break;
- case 'edit_comment':
- $comment = get_comment( $args[0] );
- if ( ! $comment ) {
- $caps[] = 'do_not_allow';
- break;
- }
-
- $post = get_post( $comment->comment_post_ID );
-
- /*
- * If the post doesn't exist, we have an orphaned comment.
- * Fall back to the edit_posts capability, instead.
- */
- if ( $post ) {
- $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
- } else {
- $caps = map_meta_cap( 'edit_posts', $user_id );
- }
- break;
- case 'unfiltered_upload':
- if ( defined('ALLOW_UNFILTERED_UPLOADS') && ALLOW_UNFILTERED_UPLOADS && ( !is_multisite() || is_super_admin( $user_id ) ) )
- $caps[] = $cap;
- else
- $caps[] = 'do_not_allow';
- break;
- case 'unfiltered_html' :
- // Disallow unfiltered_html for all users, even admins and super admins.
- if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML )
- $caps[] = 'do_not_allow';
- elseif ( is_multisite() && ! is_super_admin( $user_id ) )
- $caps[] = 'do_not_allow';
- else
- $caps[] = $cap;
- break;
- case 'edit_files':
- case 'edit_plugins':
- case 'edit_themes':
- // Disallow the file editors.
- if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT )
- $caps[] = 'do_not_allow';
- elseif ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
- $caps[] = 'do_not_allow';
- elseif ( is_multisite() && ! is_super_admin( $user_id ) )
- $caps[] = 'do_not_allow';
- else
- $caps[] = $cap;
- break;
- case 'update_plugins':
- case 'delete_plugins':
- case 'install_plugins':
- case 'upload_plugins':
- case 'update_themes':
- case 'delete_themes':
- case 'install_themes':
- case 'upload_themes':
- case 'update_core':
- // Disallow anything that creates, deletes, or updates core, plugin, or theme files.
- // Files in uploads are excepted.
- if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) {
- $caps[] = 'do_not_allow';
- } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
- $caps[] = 'do_not_allow';
- } elseif ( 'upload_themes' === $cap ) {
- $caps[] = 'install_themes';
- } elseif ( 'upload_plugins' === $cap ) {
- $caps[] = 'install_plugins';
- } else {
- $caps[] = $cap;
- }
- break;
- case 'activate_plugins':
- $caps[] = $cap;
- if ( is_multisite() ) {
- // update_, install_, and delete_ are handled above with is_super_admin().
- $menu_perms = get_site_option( 'menu_items', array() );
- if ( empty( $menu_perms['plugins'] ) )
- $caps[] = 'manage_network_plugins';
- }
- break;
- case 'delete_user':
- case 'delete_users':
- // If multisite only super admins can delete users.
- if ( is_multisite() && ! is_super_admin( $user_id ) )
- $caps[] = 'do_not_allow';
- else
- $caps[] = 'delete_users'; // delete_user maps to delete_users.
- break;
- case 'create_users':
- if ( !is_multisite() )
- $caps[] = $cap;
- elseif ( is_super_admin( $user_id ) || get_site_option( 'add_new_users' ) )
- $caps[] = $cap;
- else
- $caps[] = 'do_not_allow';
- break;
- case 'manage_links' :
- if ( get_option( 'link_manager_enabled' ) )
- $caps[] = $cap;
- else
- $caps[] = 'do_not_allow';
- break;
- case 'customize' :
- $caps[] = 'edit_theme_options';
- break;
- case 'delete_site':
- $caps[] = 'manage_options';
- break;
- default:
- // Handle meta capabilities for custom post types.
- $post_type_meta_caps = _post_type_meta_capabilities();
- if ( isset( $post_type_meta_caps[ $cap ] ) ) {
- $args = array_merge( array( $post_type_meta_caps[ $cap ], $user_id ), $args );
- return call_user_func_array( 'map_meta_cap', $args );
- }
-
- // If no meta caps match, return the original cap.
- $caps[] = $cap;
- }
-
- /**
- * Filter a user's capabilities depending on specific context and/or privilege.
- *
- * @since 2.8.0
- *
- * @param array $caps Returns the user's actual capabilities.
- * @param string $cap Capability name.
- * @param int $user_id The user ID.
- * @param array $args Adds the context to the cap. Typically the object ID.
- */
- return apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args );
-}
-
-/**
- * Whether the current user has a specific capability.
- *
- * While checking against particular roles in place of a capability is supported
- * in part, this practice is discouraged as it may produce unreliable results.
- *
- * @since 2.0.0
- *
- * @see WP_User::has_cap()
- * @see map_meta_cap()
- *
- * @param string $capability Capability name.
- * @param int $object_id Optional. ID of the specific object to check against if `$capability` is a "meta" cap.
- * "Meta" capabilities, e.g. 'edit_post', 'edit_user', etc., are capabilities used
- * by map_meta_cap() to map to other "primitive" capabilities, e.g. 'edit_posts',
- * 'edit_others_posts', etc. Accessed via func_get_args() and passed to WP_User::has_cap(),
- * then map_meta_cap().
- * @return bool Whether the current user has the given capability. If `$capability` is a meta cap and `$object_id` is
- * passed, whether the current user has the given meta capability for the given object.
- */
-function current_user_can( $capability ) {
- $current_user = wp_get_current_user();
-
- if ( empty( $current_user ) )
- return false;
-
- $args = array_slice( func_get_args(), 1 );
- $args = array_merge( array( $capability ), $args );
-
- return call_user_func_array( array( $current_user, 'has_cap' ), $args );
-}
-
-/**
- * Whether current user has a capability or role for a given blog.
- *
- * @since 3.0.0
- *
- * @param int $blog_id Blog ID
- * @param string $capability Capability or role name.
- * @return bool
- */
-function current_user_can_for_blog( $blog_id, $capability ) {
- $switched = is_multisite() ? switch_to_blog( $blog_id ) : false;
-
- $current_user = wp_get_current_user();
-
- if ( empty( $current_user ) ) {
- if ( $switched ) {
- restore_current_blog();
- }
- return false;
- }
-
- $args = array_slice( func_get_args(), 2 );
- $args = array_merge( array( $capability ), $args );
-
- $can = call_user_func_array( array( $current_user, 'has_cap' ), $args );
-
- if ( $switched ) {
- restore_current_blog();
- }
-
- return $can;
-}
-
-/**
- * Whether author of supplied post has capability or role.
- *
- * @since 2.9.0
- *
- * @param int|object $post Post ID or post object.
- * @param string $capability Capability or role name.
- * @return bool
- */
-function author_can( $post, $capability ) {
- if ( !$post = get_post($post) )
- return false;
-
- $author = get_userdata( $post->post_author );
-
- if ( ! $author )
- return false;
-
- $args = array_slice( func_get_args(), 2 );
- $args = array_merge( array( $capability ), $args );
-
- return call_user_func_array( array( $author, 'has_cap' ), $args );
-}
-
-/**
- * Whether a particular user has capability or role.
- *
- * @since 3.1.0
- *
- * @param int|object $user User ID or object.
- * @param string $capability Capability or role name.
- * @return bool
- */
-function user_can( $user, $capability ) {
- if ( ! is_object( $user ) )
- $user = get_userdata( $user );
-
- if ( ! $user || ! $user->exists() )
- return false;
-
- $args = array_slice( func_get_args(), 2 );
- $args = array_merge( array( $capability ), $args );
-
- return call_user_func_array( array( $user, 'has_cap' ), $args );
-}
-
-/**
- * Retrieves the global WP_Roles instance and instantiates it if necessary.
- *
- * @since 4.3.0
- *
- * @global WP_Roles $wp_roles WP_Roles global instance.
- *
- * @return WP_Roles WP_Roles global instance if not already instantiated.
- */
-function wp_roles() {
- global $wp_roles;
-
- if ( ! isset( $wp_roles ) ) {
- $wp_roles = new WP_Roles();
- }
- return $wp_roles;
-}
-
-/**
- * Retrieve role object.
- *
- * @since 2.0.0
- *
- * @param string $role Role name.
- * @return WP_Role|null WP_Role object if found, null if the role does not exist.
- */
-function get_role( $role ) {
- return wp_roles()->get_role( $role );
-}
-
-/**
- * Add role, if it does not exist.
- *
- * @since 2.0.0
- *
- * @param string $role Role name.
- * @param string $display_name Display name for role.
- * @param array $capabilities List of capabilities, e.g. array( 'edit_posts' => true, 'delete_posts' => false );
- * @return WP_Role|null WP_Role object if role is added, null if already exists.
- */
-function add_role( $role, $display_name, $capabilities = array() ) {
- if ( empty( $role ) ) {
- return;
- }
- return wp_roles()->add_role( $role, $display_name, $capabilities );
-}
-
-/**
- * Remove role, if it exists.
- *
- * @since 2.0.0
- *
- * @param string $role Role name.
- */
-function remove_role( $role ) {
- wp_roles()->remove_role( $role );
-}
-
-/**
- * Retrieve a list of super admins.
- *
- * @since 3.0.0
- *
- * @global array $super_admins
- *
- * @return array List of super admin logins
- */
-function get_super_admins() {
- global $super_admins;
-
- if ( isset($super_admins) )
- return $super_admins;
- else
- return get_site_option( 'site_admins', array('admin') );
-}
-
-/**
- * Determine if user is a site admin.
- *
- * @since 3.0.0
- *
- * @param int $user_id (Optional) The ID of a user. Defaults to the current user.
- * @return bool True if the user is a site admin.
- */
-function is_super_admin( $user_id = false ) {
- if ( ! $user_id || $user_id == get_current_user_id() )
- $user = wp_get_current_user();
- else
- $user = get_userdata( $user_id );
-
- if ( ! $user || ! $user->exists() )
- return false;
-
- if ( is_multisite() ) {
- $super_admins = get_super_admins();
- if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins ) )
- return true;
- } else {
- if ( $user->has_cap('delete_users') )
- return true;
- }
-
- return false;
-}
</del></span></pre></div>
<a id="trunksrcwpincludescapabilitiesphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/capabilities.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/capabilities.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/capabilities.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,20 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Core User Role & Capabilities API
- *
- * @package WordPress
- * @subpackage Users
- * @since 2.0.0
- */
-
-/** WP_Roles class */
-require_once( ABSPATH . WPINC . '/class-wp-roles.php' );
-
-/** WP_Role class */
-require_once( ABSPATH . WPINC . '/class-wp-role.php' );
-
-/** WP_User class */
-require_once( ABSPATH . WPINC . '/class-wp-user.php' );
-
-/** Core capabilities functionality */
-require_once( ABSPATH . WPINC . '/capabilities-functions.php' );
</del></span></pre></div>
<a id="trunksrcwpincludescapabilitiesphpfromrev35717trunksrcwpincludescapabilitiesfunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/capabilities.php (from rev 35717, trunk/src/wp-includes/capabilities-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/capabilities.php (rev 0)
+++ trunk/src/wp-includes/capabilities.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,611 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Core User Role & Capabilities API
+ *
+ * @package WordPress
+ * @subpackage Users
+ */
+
+/**
+ * Map meta capabilities to primitive capabilities.
+ *
+ * This does not actually compare whether the user ID has the actual capability,
+ * just what the capability or capabilities are. Meta capability list value can
+ * be 'delete_user', 'edit_user', 'remove_user', 'promote_user', 'delete_post',
+ * 'delete_page', 'edit_post', 'edit_page', 'read_post', or 'read_page'.
+ *
+ * @since 2.0.0
+ *
+ * @param string $cap Capability name.
+ * @param int $user_id User ID.
+ * @param int $object_id Optional. ID of the specific object to check against if `$cap` is a "meta" cap.
+ * "Meta" capabilities, e.g. 'edit_post', 'edit_user', etc., are capabilities used
+ * by map_meta_cap() to map to other "primitive" capabilities, e.g. 'edit_posts',
+ * 'edit_others_posts', etc. The parameter is accessed via func_get_args().
+ * @return array Actual capabilities for meta capability.
+ */
+function map_meta_cap( $cap, $user_id ) {
+ $args = array_slice( func_get_args(), 2 );
+ $caps = array();
+
+ switch ( $cap ) {
+ case 'remove_user':
+ $caps[] = 'remove_users';
+ break;
+ case 'promote_user':
+ case 'add_users':
+ $caps[] = 'promote_users';
+ break;
+ case 'edit_user':
+ case 'edit_users':
+ // Allow user to edit itself
+ if ( 'edit_user' == $cap && isset( $args[0] ) && $user_id == $args[0] )
+ break;
+
+ // In multisite the user must have manage_network_users caps. If editing a super admin, the user must be a super admin.
+ if ( is_multisite() && ( ( ! is_super_admin( $user_id ) && 'edit_user' === $cap && is_super_admin( $args[0] ) ) || ! user_can( $user_id, 'manage_network_users' ) ) ) {
+ $caps[] = 'do_not_allow';
+ } else {
+ $caps[] = 'edit_users'; // edit_user maps to edit_users.
+ }
+ break;
+ case 'delete_post':
+ case 'delete_page':
+ $post = get_post( $args[0] );
+ if ( ! $post ) {
+ $caps[] = 'do_not_allow';
+ break;
+ }
+
+ if ( 'revision' == $post->post_type ) {
+ $post = get_post( $post->post_parent );
+ if ( ! $post ) {
+ $caps[] = 'do_not_allow';
+ break;
+ }
+ }
+
+ $post_type = get_post_type_object( $post->post_type );
+ if ( ! $post_type ) {
+ /* translators: 1: post type, 2: capability name */
+ _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
+ $caps[] = 'edit_others_posts';
+ break;
+ }
+
+ if ( ! $post_type->map_meta_cap ) {
+ $caps[] = $post_type->cap->$cap;
+ // Prior to 3.1 we would re-call map_meta_cap here.
+ if ( 'delete_post' == $cap )
+ $cap = $post_type->cap->$cap;
+ break;
+ }
+
+ // If the post author is set and the user is the author...
+ if ( $post->post_author && $user_id == $post->post_author ) {
+ // If the post is published...
+ if ( 'publish' == $post->post_status ) {
+ $caps[] = $post_type->cap->delete_published_posts;
+ } elseif ( 'trash' == $post->post_status ) {
+ if ( 'publish' == get_post_meta( $post->ID, '_wp_trash_meta_status', true ) ) {
+ $caps[] = $post_type->cap->delete_published_posts;
+ }
+ } else {
+ // If the post is draft...
+ $caps[] = $post_type->cap->delete_posts;
+ }
+ } else {
+ // The user is trying to edit someone else's post.
+ $caps[] = $post_type->cap->delete_others_posts;
+ // The post is published, extra cap required.
+ if ( 'publish' == $post->post_status ) {
+ $caps[] = $post_type->cap->delete_published_posts;
+ } elseif ( 'private' == $post->post_status ) {
+ $caps[] = $post_type->cap->delete_private_posts;
+ }
+ }
+ break;
+ // edit_post breaks down to edit_posts, edit_published_posts, or
+ // edit_others_posts
+ case 'edit_post':
+ case 'edit_page':
+ $post = get_post( $args[0] );
+ if ( ! $post ) {
+ $caps[] = 'do_not_allow';
+ break;
+ }
+
+ if ( 'revision' == $post->post_type ) {
+ $post = get_post( $post->post_parent );
+ if ( ! $post ) {
+ $caps[] = 'do_not_allow';
+ break;
+ }
+ }
+
+ $post_type = get_post_type_object( $post->post_type );
+ if ( ! $post_type ) {
+ /* translators: 1: post type, 2: capability name */
+ _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
+ $caps[] = 'edit_others_posts';
+ break;
+ }
+
+ if ( ! $post_type->map_meta_cap ) {
+ $caps[] = $post_type->cap->$cap;
+ // Prior to 3.1 we would re-call map_meta_cap here.
+ if ( 'edit_post' == $cap )
+ $cap = $post_type->cap->$cap;
+ break;
+ }
+
+ // If the post author is set and the user is the author...
+ if ( $post->post_author && $user_id == $post->post_author ) {
+ // If the post is published...
+ if ( 'publish' == $post->post_status ) {
+ $caps[] = $post_type->cap->edit_published_posts;
+ } elseif ( 'trash' == $post->post_status ) {
+ if ( 'publish' == get_post_meta( $post->ID, '_wp_trash_meta_status', true ) ) {
+ $caps[] = $post_type->cap->edit_published_posts;
+ }
+ } else {
+ // If the post is draft...
+ $caps[] = $post_type->cap->edit_posts;
+ }
+ } else {
+ // The user is trying to edit someone else's post.
+ $caps[] = $post_type->cap->edit_others_posts;
+ // The post is published, extra cap required.
+ if ( 'publish' == $post->post_status ) {
+ $caps[] = $post_type->cap->edit_published_posts;
+ } elseif ( 'private' == $post->post_status ) {
+ $caps[] = $post_type->cap->edit_private_posts;
+ }
+ }
+ break;
+ case 'read_post':
+ case 'read_page':
+ $post = get_post( $args[0] );
+ if ( ! $post ) {
+ $caps[] = 'do_not_allow';
+ break;
+ }
+
+ if ( 'revision' == $post->post_type ) {
+ $post = get_post( $post->post_parent );
+ if ( ! $post ) {
+ $caps[] = 'do_not_allow';
+ break;
+ }
+ }
+
+ $post_type = get_post_type_object( $post->post_type );
+ if ( ! $post_type ) {
+ /* translators: 1: post type, 2: capability name */
+ _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
+ $caps[] = 'edit_others_posts';
+ break;
+ }
+
+ if ( ! $post_type->map_meta_cap ) {
+ $caps[] = $post_type->cap->$cap;
+ // Prior to 3.1 we would re-call map_meta_cap here.
+ if ( 'read_post' == $cap )
+ $cap = $post_type->cap->$cap;
+ break;
+ }
+
+ $status_obj = get_post_status_object( $post->post_status );
+ if ( $status_obj->public ) {
+ $caps[] = $post_type->cap->read;
+ break;
+ }
+
+ if ( $post->post_author && $user_id == $post->post_author ) {
+ $caps[] = $post_type->cap->read;
+ } elseif ( $status_obj->private ) {
+ $caps[] = $post_type->cap->read_private_posts;
+ } else {
+ $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
+ }
+ break;
+ case 'publish_post':
+ $post = get_post( $args[0] );
+ if ( ! $post ) {
+ $caps[] = 'do_not_allow';
+ break;
+ }
+
+ $post_type = get_post_type_object( $post->post_type );
+ if ( ! $post_type ) {
+ /* translators: 1: post type, 2: capability name */
+ _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
+ $caps[] = 'edit_others_posts';
+ break;
+ }
+
+ $caps[] = $post_type->cap->publish_posts;
+ break;
+ case 'edit_post_meta':
+ case 'delete_post_meta':
+ case 'add_post_meta':
+ $post = get_post( $args[0] );
+ if ( ! $post ) {
+ $caps[] = 'do_not_allow';
+ break;
+ }
+
+ $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
+
+ $meta_key = isset( $args[ 1 ] ) ? $args[ 1 ] : false;
+
+ if ( $meta_key && has_filter( "auth_post_meta_{$meta_key}" ) ) {
+ /**
+ * Filter whether the user is allowed to add post meta to a post.
+ *
+ * The dynamic portion of the hook name, `$meta_key`, refers to the
+ * meta key passed to {@see map_meta_cap()}.
+ *
+ * @since 3.3.0
+ *
+ * @param bool $allowed Whether the user can add the post meta. Default false.
+ * @param string $meta_key The meta key.
+ * @param int $post_id Post ID.
+ * @param int $user_id User ID.
+ * @param string $cap Capability name.
+ * @param array $caps User capabilities.
+ */
+ $allowed = apply_filters( "auth_post_meta_{$meta_key}", false, $meta_key, $post->ID, $user_id, $cap, $caps );
+ if ( ! $allowed )
+ $caps[] = $cap;
+ } elseif ( $meta_key && is_protected_meta( $meta_key, 'post' ) ) {
+ $caps[] = $cap;
+ }
+ break;
+ case 'edit_comment':
+ $comment = get_comment( $args[0] );
+ if ( ! $comment ) {
+ $caps[] = 'do_not_allow';
+ break;
+ }
+
+ $post = get_post( $comment->comment_post_ID );
+
+ /*
+ * If the post doesn't exist, we have an orphaned comment.
+ * Fall back to the edit_posts capability, instead.
+ */
+ if ( $post ) {
+ $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
+ } else {
+ $caps = map_meta_cap( 'edit_posts', $user_id );
+ }
+ break;
+ case 'unfiltered_upload':
+ if ( defined('ALLOW_UNFILTERED_UPLOADS') && ALLOW_UNFILTERED_UPLOADS && ( !is_multisite() || is_super_admin( $user_id ) ) )
+ $caps[] = $cap;
+ else
+ $caps[] = 'do_not_allow';
+ break;
+ case 'unfiltered_html' :
+ // Disallow unfiltered_html for all users, even admins and super admins.
+ if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML )
+ $caps[] = 'do_not_allow';
+ elseif ( is_multisite() && ! is_super_admin( $user_id ) )
+ $caps[] = 'do_not_allow';
+ else
+ $caps[] = $cap;
+ break;
+ case 'edit_files':
+ case 'edit_plugins':
+ case 'edit_themes':
+ // Disallow the file editors.
+ if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT )
+ $caps[] = 'do_not_allow';
+ elseif ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
+ $caps[] = 'do_not_allow';
+ elseif ( is_multisite() && ! is_super_admin( $user_id ) )
+ $caps[] = 'do_not_allow';
+ else
+ $caps[] = $cap;
+ break;
+ case 'update_plugins':
+ case 'delete_plugins':
+ case 'install_plugins':
+ case 'upload_plugins':
+ case 'update_themes':
+ case 'delete_themes':
+ case 'install_themes':
+ case 'upload_themes':
+ case 'update_core':
+ // Disallow anything that creates, deletes, or updates core, plugin, or theme files.
+ // Files in uploads are excepted.
+ if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) {
+ $caps[] = 'do_not_allow';
+ } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
+ $caps[] = 'do_not_allow';
+ } elseif ( 'upload_themes' === $cap ) {
+ $caps[] = 'install_themes';
+ } elseif ( 'upload_plugins' === $cap ) {
+ $caps[] = 'install_plugins';
+ } else {
+ $caps[] = $cap;
+ }
+ break;
+ case 'activate_plugins':
+ $caps[] = $cap;
+ if ( is_multisite() ) {
+ // update_, install_, and delete_ are handled above with is_super_admin().
+ $menu_perms = get_site_option( 'menu_items', array() );
+ if ( empty( $menu_perms['plugins'] ) )
+ $caps[] = 'manage_network_plugins';
+ }
+ break;
+ case 'delete_user':
+ case 'delete_users':
+ // If multisite only super admins can delete users.
+ if ( is_multisite() && ! is_super_admin( $user_id ) )
+ $caps[] = 'do_not_allow';
+ else
+ $caps[] = 'delete_users'; // delete_user maps to delete_users.
+ break;
+ case 'create_users':
+ if ( !is_multisite() )
+ $caps[] = $cap;
+ elseif ( is_super_admin( $user_id ) || get_site_option( 'add_new_users' ) )
+ $caps[] = $cap;
+ else
+ $caps[] = 'do_not_allow';
+ break;
+ case 'manage_links' :
+ if ( get_option( 'link_manager_enabled' ) )
+ $caps[] = $cap;
+ else
+ $caps[] = 'do_not_allow';
+ break;
+ case 'customize' :
+ $caps[] = 'edit_theme_options';
+ break;
+ case 'delete_site':
+ $caps[] = 'manage_options';
+ break;
+ default:
+ // Handle meta capabilities for custom post types.
+ $post_type_meta_caps = _post_type_meta_capabilities();
+ if ( isset( $post_type_meta_caps[ $cap ] ) ) {
+ $args = array_merge( array( $post_type_meta_caps[ $cap ], $user_id ), $args );
+ return call_user_func_array( 'map_meta_cap', $args );
+ }
+
+ // If no meta caps match, return the original cap.
+ $caps[] = $cap;
+ }
+
+ /**
+ * Filter a user's capabilities depending on specific context and/or privilege.
+ *
+ * @since 2.8.0
+ *
+ * @param array $caps Returns the user's actual capabilities.
+ * @param string $cap Capability name.
+ * @param int $user_id The user ID.
+ * @param array $args Adds the context to the cap. Typically the object ID.
+ */
+ return apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args );
+}
+
+/**
+ * Whether the current user has a specific capability.
+ *
+ * While checking against particular roles in place of a capability is supported
+ * in part, this practice is discouraged as it may produce unreliable results.
+ *
+ * @since 2.0.0
+ *
+ * @see WP_User::has_cap()
+ * @see map_meta_cap()
+ *
+ * @param string $capability Capability name.
+ * @param int $object_id Optional. ID of the specific object to check against if `$capability` is a "meta" cap.
+ * "Meta" capabilities, e.g. 'edit_post', 'edit_user', etc., are capabilities used
+ * by map_meta_cap() to map to other "primitive" capabilities, e.g. 'edit_posts',
+ * 'edit_others_posts', etc. Accessed via func_get_args() and passed to WP_User::has_cap(),
+ * then map_meta_cap().
+ * @return bool Whether the current user has the given capability. If `$capability` is a meta cap and `$object_id` is
+ * passed, whether the current user has the given meta capability for the given object.
+ */
+function current_user_can( $capability ) {
+ $current_user = wp_get_current_user();
+
+ if ( empty( $current_user ) )
+ return false;
+
+ $args = array_slice( func_get_args(), 1 );
+ $args = array_merge( array( $capability ), $args );
+
+ return call_user_func_array( array( $current_user, 'has_cap' ), $args );
+}
+
+/**
+ * Whether current user has a capability or role for a given blog.
+ *
+ * @since 3.0.0
+ *
+ * @param int $blog_id Blog ID
+ * @param string $capability Capability or role name.
+ * @return bool
+ */
+function current_user_can_for_blog( $blog_id, $capability ) {
+ $switched = is_multisite() ? switch_to_blog( $blog_id ) : false;
+
+ $current_user = wp_get_current_user();
+
+ if ( empty( $current_user ) ) {
+ if ( $switched ) {
+ restore_current_blog();
+ }
+ return false;
+ }
+
+ $args = array_slice( func_get_args(), 2 );
+ $args = array_merge( array( $capability ), $args );
+
+ $can = call_user_func_array( array( $current_user, 'has_cap' ), $args );
+
+ if ( $switched ) {
+ restore_current_blog();
+ }
+
+ return $can;
+}
+
+/**
+ * Whether author of supplied post has capability or role.
+ *
+ * @since 2.9.0
+ *
+ * @param int|object $post Post ID or post object.
+ * @param string $capability Capability or role name.
+ * @return bool
+ */
+function author_can( $post, $capability ) {
+ if ( !$post = get_post($post) )
+ return false;
+
+ $author = get_userdata( $post->post_author );
+
+ if ( ! $author )
+ return false;
+
+ $args = array_slice( func_get_args(), 2 );
+ $args = array_merge( array( $capability ), $args );
+
+ return call_user_func_array( array( $author, 'has_cap' ), $args );
+}
+
+/**
+ * Whether a particular user has capability or role.
+ *
+ * @since 3.1.0
+ *
+ * @param int|object $user User ID or object.
+ * @param string $capability Capability or role name.
+ * @return bool
+ */
+function user_can( $user, $capability ) {
+ if ( ! is_object( $user ) )
+ $user = get_userdata( $user );
+
+ if ( ! $user || ! $user->exists() )
+ return false;
+
+ $args = array_slice( func_get_args(), 2 );
+ $args = array_merge( array( $capability ), $args );
+
+ return call_user_func_array( array( $user, 'has_cap' ), $args );
+}
+
+/**
+ * Retrieves the global WP_Roles instance and instantiates it if necessary.
+ *
+ * @since 4.3.0
+ *
+ * @global WP_Roles $wp_roles WP_Roles global instance.
+ *
+ * @return WP_Roles WP_Roles global instance if not already instantiated.
+ */
+function wp_roles() {
+ global $wp_roles;
+
+ if ( ! isset( $wp_roles ) ) {
+ $wp_roles = new WP_Roles();
+ }
+ return $wp_roles;
+}
+
+/**
+ * Retrieve role object.
+ *
+ * @since 2.0.0
+ *
+ * @param string $role Role name.
+ * @return WP_Role|null WP_Role object if found, null if the role does not exist.
+ */
+function get_role( $role ) {
+ return wp_roles()->get_role( $role );
+}
+
+/**
+ * Add role, if it does not exist.
+ *
+ * @since 2.0.0
+ *
+ * @param string $role Role name.
+ * @param string $display_name Display name for role.
+ * @param array $capabilities List of capabilities, e.g. array( 'edit_posts' => true, 'delete_posts' => false );
+ * @return WP_Role|null WP_Role object if role is added, null if already exists.
+ */
+function add_role( $role, $display_name, $capabilities = array() ) {
+ if ( empty( $role ) ) {
+ return;
+ }
+ return wp_roles()->add_role( $role, $display_name, $capabilities );
+}
+
+/**
+ * Remove role, if it exists.
+ *
+ * @since 2.0.0
+ *
+ * @param string $role Role name.
+ */
+function remove_role( $role ) {
+ wp_roles()->remove_role( $role );
+}
+
+/**
+ * Retrieve a list of super admins.
+ *
+ * @since 3.0.0
+ *
+ * @global array $super_admins
+ *
+ * @return array List of super admin logins
+ */
+function get_super_admins() {
+ global $super_admins;
+
+ if ( isset($super_admins) )
+ return $super_admins;
+ else
+ return get_site_option( 'site_admins', array('admin') );
+}
+
+/**
+ * Determine if user is a site admin.
+ *
+ * @since 3.0.0
+ *
+ * @param int $user_id (Optional) The ID of a user. Defaults to the current user.
+ * @return bool True if the user is a site admin.
+ */
+function is_super_admin( $user_id = false ) {
+ if ( ! $user_id || $user_id == get_current_user_id() )
+ $user = wp_get_current_user();
+ else
+ $user = get_userdata( $user_id );
+
+ if ( ! $user || ! $user->exists() )
+ return false;
+
+ if ( is_multisite() ) {
+ $super_admins = get_super_admins();
+ if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins ) )
+ return true;
+ } else {
+ if ( $user->has_cap('delete_users') )
+ return true;
+ }
+
+ return false;
+}
</ins></span></pre></div>
<a id="trunksrcwpincludescategoryfunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/category-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/category-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/category-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,349 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Taxonomy API: Top-level core category-specific functionality
- *
- * @package WordPress
- * @subpackage Taxonomy
- * @since 4.4.0
- */
-
-/**
- * Retrieve list of category objects.
- *
- * If you change the type to 'link' in the arguments, then the link categories
- * will be returned instead. Also all categories will be updated to be backwards
- * compatible with pre-2.3 plugins and themes.
- *
- * @since 2.1.0
- * @see get_terms() Type of arguments that can be changed.
- * @link https://codex.wordpress.org/Function_Reference/get_categories
- *
- * @param string|array $args Optional. Change the defaults retrieving categories.
- * @return array List of categories.
- */
-function get_categories( $args = '' ) {
- $defaults = array( 'taxonomy' => 'category' );
- $args = wp_parse_args( $args, $defaults );
-
- $taxonomy = $args['taxonomy'];
-
- /**
- * Filter the taxonomy used to retrieve terms when calling {@see get_categories()}.
- *
- * @since 2.7.0
- *
- * @param string $taxonomy Taxonomy to retrieve terms from.
- * @param array $args An array of arguments. See {@see get_terms()}.
- */
- $taxonomy = apply_filters( 'get_categories_taxonomy', $taxonomy, $args );
-
- // Back compat
- if ( isset($args['type']) && 'link' == $args['type'] ) {
- /* translators: 1: "type => link", 2: "taxonomy => link_category" alternative */
- _deprecated_argument( __FUNCTION__, '3.0',
- sprintf( __( '%1$s is deprecated. Use %2$s instead.' ),
- '<code>type => link</code>',
- '<code>taxonomy => link_category</code>'
- )
- );
- $taxonomy = $args['taxonomy'] = 'link_category';
- }
-
- $categories = (array) get_terms( $taxonomy, $args );
-
- foreach ( array_keys( $categories ) as $k )
- _make_cat_compat( $categories[$k] );
-
- return $categories;
-}
-
-/**
- * Retrieves category data given a category ID or category object.
- *
- * If you pass the $category parameter an object, which is assumed to be the
- * category row object retrieved the database. It will cache the category data.
- *
- * If you pass $category an integer of the category ID, then that category will
- * be retrieved from the database, if it isn't already cached, and pass it back.
- *
- * If you look at get_term(), then both types will be passed through several
- * filters and finally sanitized based on the $filter parameter value.
- *
- * The category will converted to maintain backwards compatibility.
- *
- * @since 1.5.1
- *
- * @param int|object $category Category ID or Category row object
- * @param string $output Optional. Constant OBJECT, ARRAY_A, or ARRAY_N
- * @param string $filter Optional. Default is raw or no WordPress defined filter will applied.
- * @return object|array|WP_Error|null Category data in type defined by $output parameter.
- * WP_Error if $category is empty, null if it does not exist.
- */
-function get_category( $category, $output = OBJECT, $filter = 'raw' ) {
- $category = get_term( $category, 'category', $output, $filter );
-
- if ( is_wp_error( $category ) )
- return $category;
-
- _make_cat_compat( $category );
-
- return $category;
-}
-
-/**
- * Retrieve category based on URL containing the category slug.
- *
- * Breaks the $category_path parameter up to get the category slug.
- *
- * Tries to find the child path and will return it. If it doesn't find a
- * match, then it will return the first category matching slug, if $full_match,
- * is set to false. If it does not, then it will return null.
- *
- * It is also possible that it will return a WP_Error object on failure. Check
- * for it when using this function.
- *
- * @since 2.1.0
- *
- * @param string $category_path URL containing category slugs.
- * @param bool $full_match Optional. Whether full path should be matched.
- * @param string $output Optional. Constant OBJECT, ARRAY_A, or ARRAY_N
- * @return object|array|WP_Error|void Type is based on $output value.
- */
-function get_category_by_path( $category_path, $full_match = true, $output = OBJECT ) {
- $category_path = rawurlencode( urldecode( $category_path ) );
- $category_path = str_replace( '%2F', '/', $category_path );
- $category_path = str_replace( '%20', ' ', $category_path );
- $category_paths = '/' . trim( $category_path, '/' );
- $leaf_path = sanitize_title( basename( $category_paths ) );
- $category_paths = explode( '/', $category_paths );
- $full_path = '';
- foreach ( (array) $category_paths as $pathdir ) {
- $full_path .= ( $pathdir != '' ? '/' : '' ) . sanitize_title( $pathdir );
- }
- $categories = get_terms( 'category', array('get' => 'all', 'slug' => $leaf_path) );
-
- if ( empty( $categories ) ) {
- return;
- }
-
- foreach ( $categories as $category ) {
- $path = '/' . $leaf_path;
- $curcategory = $category;
- while ( ( $curcategory->parent != 0 ) && ( $curcategory->parent != $curcategory->term_id ) ) {
- $curcategory = get_term( $curcategory->parent, 'category' );
- if ( is_wp_error( $curcategory ) ) {
- return $curcategory;
- }
- $path = '/' . $curcategory->slug . $path;
- }
-
- if ( $path == $full_path ) {
- $category = get_term( $category->term_id, 'category', $output );
- _make_cat_compat( $category );
- return $category;
- }
- }
-
- // If full matching is not required, return the first cat that matches the leaf.
- if ( ! $full_match ) {
- $category = get_term( reset( $categories )->term_id, 'category', $output );
- _make_cat_compat( $category );
- return $category;
- }
-}
-
-/**
- * Retrieve category object by category slug.
- *
- * @since 2.3.0
- *
- * @param string $slug The category slug.
- * @return object Category data object
- */
-function get_category_by_slug( $slug ) {
- $category = get_term_by( 'slug', $slug, 'category' );
- if ( $category )
- _make_cat_compat( $category );
-
- return $category;
-}
-
-/**
- * Retrieve the ID of a category from its name.
- *
- * @since 1.0.0
- *
- * @param string $cat_name Category name.
- * @return int 0, if failure and ID of category on success.
- */
-function get_cat_ID( $cat_name ) {
- $cat = get_term_by( 'name', $cat_name, 'category' );
- if ( $cat )
- return $cat->term_id;
- return 0;
-}
-
-/**
- * Retrieve the name of a category from its ID.
- *
- * @since 1.0.0
- *
- * @param int $cat_id Category ID
- * @return string Category name, or an empty string if category doesn't exist.
- */
-function get_cat_name( $cat_id ) {
- $cat_id = (int) $cat_id;
- $category = get_term( $cat_id, 'category' );
- if ( ! $category || is_wp_error( $category ) )
- return '';
- return $category->name;
-}
-
-/**
- * Check if a category is an ancestor of another category.
- *
- * You can use either an id or the category object for both parameters. If you
- * use an integer the category will be retrieved.
- *
- * @since 2.1.0
- *
- * @param int|object $cat1 ID or object to check if this is the parent category.
- * @param int|object $cat2 The child category.
- * @return bool Whether $cat2 is child of $cat1
- */
-function cat_is_ancestor_of( $cat1, $cat2 ) {
- return term_is_ancestor_of( $cat1, $cat2, 'category' );
-}
-
-/**
- * Sanitizes category data based on context.
- *
- * @since 2.3.0
- *
- * @param object|array $category Category data
- * @param string $context Optional. Default is 'display'.
- * @return object|array Same type as $category with sanitized data for safe use.
- */
-function sanitize_category( $category, $context = 'display' ) {
- return sanitize_term( $category, 'category', $context );
-}
-
-/**
- * Sanitizes data in single category key field.
- *
- * @since 2.3.0
- *
- * @param string $field Category key to sanitize
- * @param mixed $value Category value to sanitize
- * @param int $cat_id Category ID
- * @param string $context What filter to use, 'raw', 'display', etc.
- * @return mixed Same type as $value after $value has been sanitized.
- */
-function sanitize_category_field( $field, $value, $cat_id, $context ) {
- return sanitize_term_field( $field, $value, $cat_id, 'category', $context );
-}
-
-/* Tags */
-
-/**
- * Retrieves all post tags.
- *
- * @since 2.3.0
- * @see get_terms() For list of arguments to pass.
- *
- * @param string|array $args Tag arguments to use when retrieving tags.
- * @return array List of tags.
- */
-function get_tags( $args = '' ) {
- $tags = get_terms( 'post_tag', $args );
-
- if ( empty( $tags ) ) {
- $return = array();
- return $return;
- }
-
- /**
- * Filter the array of term objects returned for the 'post_tag' taxonomy.
- *
- * @since 2.3.0
- *
- * @param array $tags Array of 'post_tag' term objects.
- * @param array $args An array of arguments. @see get_terms()
- */
- $tags = apply_filters( 'get_tags', $tags, $args );
- return $tags;
-}
-
-/**
- * Retrieve post tag by tag ID or tag object.
- *
- * If you pass the $tag parameter an object, which is assumed to be the tag row
- * object retrieved the database. It will cache the tag data.
- *
- * If you pass $tag an integer of the tag ID, then that tag will
- * be retrieved from the database, if it isn't already cached, and pass it back.
- *
- * If you look at get_term(), then both types will be passed through several
- * filters and finally sanitized based on the $filter parameter value.
- *
- * @since 2.3.0
- *
- * @param int|object $tag
- * @param string $output Optional. Constant OBJECT, ARRAY_A, or ARRAY_N
- * @param string $filter Optional. Default is raw or no WordPress defined filter will applied.
- * @return object|array|WP_Error|null Tag data in type defined by $output parameter. WP_Error if $tag is empty, null if it does not exist.
- */
-function get_tag( $tag, $output = OBJECT, $filter = 'raw' ) {
- return get_term( $tag, 'post_tag', $output, $filter );
-}
-
-/* Cache */
-
-/**
- * Remove the category cache data based on ID.
- *
- * @since 2.1.0
- *
- * @param int $id Category ID
- */
-function clean_category_cache( $id ) {
- clean_term_cache( $id, 'category' );
-}
-
-/**
- * Update category structure to old pre 2.3 from new taxonomy structure.
- *
- * This function was added for the taxonomy support to update the new category
- * structure with the old category one. This will maintain compatibility with
- * plugins and themes which depend on the old key or property names.
- *
- * The parameter should only be passed a variable and not create the array or
- * object inline to the parameter. The reason for this is that parameter is
- * passed by reference and PHP will fail unless it has the variable.
- *
- * There is no return value, because everything is updated on the variable you
- * pass to it. This is one of the features with using pass by reference in PHP.
- *
- * @since 2.3.0
- * @since 4.4.0 The `$category` parameter now also accepts a WP_Term object.
- * @access private
- *
- * @param array|object|WP_Term $category Category Row object or array
- */
-function _make_cat_compat( &$category ) {
- if ( is_object( $category ) && ! is_wp_error( $category ) ) {
- $category->cat_ID = $category->term_id;
- $category->category_count = $category->count;
- $category->category_description = $category->description;
- $category->cat_name = $category->name;
- $category->category_nicename = $category->slug;
- $category->category_parent = $category->parent;
- } elseif ( is_array( $category ) && isset( $category['term_id'] ) ) {
- $category['cat_ID'] = &$category['term_id'];
- $category['category_count'] = &$category['count'];
- $category['category_description'] = &$category['description'];
- $category['cat_name'] = &$category['name'];
- $category['category_nicename'] = &$category['slug'];
- $category['category_parent'] = &$category['parent'];
- }
-}
</del></span></pre></div>
<a id="trunksrcwpincludescategoryphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/category.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/category.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/category.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,17 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Taxonomy API: Core category-specific functionality
- *
- * @package WordPress
- * @subpackage Taxonomy
- * @since 2.1.0
- */
-
-/** Core category functionality */
-require_once( ABSPATH . WPINC . '/category-functions.php' );
-
-/** Walker_Category class */
-require_once( ABSPATH . WPINC . '/class-walker-category.php' );
-
-/** Walker_CategoryDropdown class */
-require_once( ABSPATH . WPINC . '/class-walker-category-dropdown.php' );
</del><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="trunksrcwpincludescategoryphpfromrev35712trunksrcwpincludescategoryfunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/category.php (from rev 35712, trunk/src/wp-includes/category-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/category.php (rev 0)
+++ trunk/src/wp-includes/category.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,348 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Taxonomy API: Core category-specific functionality
+ *
+ * @package WordPress
+ * @subpackage Taxonomy
+ */
+
+/**
+ * Retrieve list of category objects.
+ *
+ * If you change the type to 'link' in the arguments, then the link categories
+ * will be returned instead. Also all categories will be updated to be backwards
+ * compatible with pre-2.3 plugins and themes.
+ *
+ * @since 2.1.0
+ * @see get_terms() Type of arguments that can be changed.
+ * @link https://codex.wordpress.org/Function_Reference/get_categories
+ *
+ * @param string|array $args Optional. Change the defaults retrieving categories.
+ * @return array List of categories.
+ */
+function get_categories( $args = '' ) {
+ $defaults = array( 'taxonomy' => 'category' );
+ $args = wp_parse_args( $args, $defaults );
+
+ $taxonomy = $args['taxonomy'];
+
+ /**
+ * Filter the taxonomy used to retrieve terms when calling {@see get_categories()}.
+ *
+ * @since 2.7.0
+ *
+ * @param string $taxonomy Taxonomy to retrieve terms from.
+ * @param array $args An array of arguments. See {@see get_terms()}.
+ */
+ $taxonomy = apply_filters( 'get_categories_taxonomy', $taxonomy, $args );
+
+ // Back compat
+ if ( isset($args['type']) && 'link' == $args['type'] ) {
+ /* translators: 1: "type => link", 2: "taxonomy => link_category" alternative */
+ _deprecated_argument( __FUNCTION__, '3.0',
+ sprintf( __( '%1$s is deprecated. Use %2$s instead.' ),
+ '<code>type => link</code>',
+ '<code>taxonomy => link_category</code>'
+ )
+ );
+ $taxonomy = $args['taxonomy'] = 'link_category';
+ }
+
+ $categories = (array) get_terms( $taxonomy, $args );
+
+ foreach ( array_keys( $categories ) as $k )
+ _make_cat_compat( $categories[$k] );
+
+ return $categories;
+}
+
+/**
+ * Retrieves category data given a category ID or category object.
+ *
+ * If you pass the $category parameter an object, which is assumed to be the
+ * category row object retrieved the database. It will cache the category data.
+ *
+ * If you pass $category an integer of the category ID, then that category will
+ * be retrieved from the database, if it isn't already cached, and pass it back.
+ *
+ * If you look at get_term(), then both types will be passed through several
+ * filters and finally sanitized based on the $filter parameter value.
+ *
+ * The category will converted to maintain backwards compatibility.
+ *
+ * @since 1.5.1
+ *
+ * @param int|object $category Category ID or Category row object
+ * @param string $output Optional. Constant OBJECT, ARRAY_A, or ARRAY_N
+ * @param string $filter Optional. Default is raw or no WordPress defined filter will applied.
+ * @return object|array|WP_Error|null Category data in type defined by $output parameter.
+ * WP_Error if $category is empty, null if it does not exist.
+ */
+function get_category( $category, $output = OBJECT, $filter = 'raw' ) {
+ $category = get_term( $category, 'category', $output, $filter );
+
+ if ( is_wp_error( $category ) )
+ return $category;
+
+ _make_cat_compat( $category );
+
+ return $category;
+}
+
+/**
+ * Retrieve category based on URL containing the category slug.
+ *
+ * Breaks the $category_path parameter up to get the category slug.
+ *
+ * Tries to find the child path and will return it. If it doesn't find a
+ * match, then it will return the first category matching slug, if $full_match,
+ * is set to false. If it does not, then it will return null.
+ *
+ * It is also possible that it will return a WP_Error object on failure. Check
+ * for it when using this function.
+ *
+ * @since 2.1.0
+ *
+ * @param string $category_path URL containing category slugs.
+ * @param bool $full_match Optional. Whether full path should be matched.
+ * @param string $output Optional. Constant OBJECT, ARRAY_A, or ARRAY_N
+ * @return object|array|WP_Error|void Type is based on $output value.
+ */
+function get_category_by_path( $category_path, $full_match = true, $output = OBJECT ) {
+ $category_path = rawurlencode( urldecode( $category_path ) );
+ $category_path = str_replace( '%2F', '/', $category_path );
+ $category_path = str_replace( '%20', ' ', $category_path );
+ $category_paths = '/' . trim( $category_path, '/' );
+ $leaf_path = sanitize_title( basename( $category_paths ) );
+ $category_paths = explode( '/', $category_paths );
+ $full_path = '';
+ foreach ( (array) $category_paths as $pathdir ) {
+ $full_path .= ( $pathdir != '' ? '/' : '' ) . sanitize_title( $pathdir );
+ }
+ $categories = get_terms( 'category', array('get' => 'all', 'slug' => $leaf_path) );
+
+ if ( empty( $categories ) ) {
+ return;
+ }
+
+ foreach ( $categories as $category ) {
+ $path = '/' . $leaf_path;
+ $curcategory = $category;
+ while ( ( $curcategory->parent != 0 ) && ( $curcategory->parent != $curcategory->term_id ) ) {
+ $curcategory = get_term( $curcategory->parent, 'category' );
+ if ( is_wp_error( $curcategory ) ) {
+ return $curcategory;
+ }
+ $path = '/' . $curcategory->slug . $path;
+ }
+
+ if ( $path == $full_path ) {
+ $category = get_term( $category->term_id, 'category', $output );
+ _make_cat_compat( $category );
+ return $category;
+ }
+ }
+
+ // If full matching is not required, return the first cat that matches the leaf.
+ if ( ! $full_match ) {
+ $category = get_term( reset( $categories )->term_id, 'category', $output );
+ _make_cat_compat( $category );
+ return $category;
+ }
+}
+
+/**
+ * Retrieve category object by category slug.
+ *
+ * @since 2.3.0
+ *
+ * @param string $slug The category slug.
+ * @return object Category data object
+ */
+function get_category_by_slug( $slug ) {
+ $category = get_term_by( 'slug', $slug, 'category' );
+ if ( $category )
+ _make_cat_compat( $category );
+
+ return $category;
+}
+
+/**
+ * Retrieve the ID of a category from its name.
+ *
+ * @since 1.0.0
+ *
+ * @param string $cat_name Category name.
+ * @return int 0, if failure and ID of category on success.
+ */
+function get_cat_ID( $cat_name ) {
+ $cat = get_term_by( 'name', $cat_name, 'category' );
+ if ( $cat )
+ return $cat->term_id;
+ return 0;
+}
+
+/**
+ * Retrieve the name of a category from its ID.
+ *
+ * @since 1.0.0
+ *
+ * @param int $cat_id Category ID
+ * @return string Category name, or an empty string if category doesn't exist.
+ */
+function get_cat_name( $cat_id ) {
+ $cat_id = (int) $cat_id;
+ $category = get_term( $cat_id, 'category' );
+ if ( ! $category || is_wp_error( $category ) )
+ return '';
+ return $category->name;
+}
+
+/**
+ * Check if a category is an ancestor of another category.
+ *
+ * You can use either an id or the category object for both parameters. If you
+ * use an integer the category will be retrieved.
+ *
+ * @since 2.1.0
+ *
+ * @param int|object $cat1 ID or object to check if this is the parent category.
+ * @param int|object $cat2 The child category.
+ * @return bool Whether $cat2 is child of $cat1
+ */
+function cat_is_ancestor_of( $cat1, $cat2 ) {
+ return term_is_ancestor_of( $cat1, $cat2, 'category' );
+}
+
+/**
+ * Sanitizes category data based on context.
+ *
+ * @since 2.3.0
+ *
+ * @param object|array $category Category data
+ * @param string $context Optional. Default is 'display'.
+ * @return object|array Same type as $category with sanitized data for safe use.
+ */
+function sanitize_category( $category, $context = 'display' ) {
+ return sanitize_term( $category, 'category', $context );
+}
+
+/**
+ * Sanitizes data in single category key field.
+ *
+ * @since 2.3.0
+ *
+ * @param string $field Category key to sanitize
+ * @param mixed $value Category value to sanitize
+ * @param int $cat_id Category ID
+ * @param string $context What filter to use, 'raw', 'display', etc.
+ * @return mixed Same type as $value after $value has been sanitized.
+ */
+function sanitize_category_field( $field, $value, $cat_id, $context ) {
+ return sanitize_term_field( $field, $value, $cat_id, 'category', $context );
+}
+
+/* Tags */
+
+/**
+ * Retrieves all post tags.
+ *
+ * @since 2.3.0
+ * @see get_terms() For list of arguments to pass.
+ *
+ * @param string|array $args Tag arguments to use when retrieving tags.
+ * @return array List of tags.
+ */
+function get_tags( $args = '' ) {
+ $tags = get_terms( 'post_tag', $args );
+
+ if ( empty( $tags ) ) {
+ $return = array();
+ return $return;
+ }
+
+ /**
+ * Filter the array of term objects returned for the 'post_tag' taxonomy.
+ *
+ * @since 2.3.0
+ *
+ * @param array $tags Array of 'post_tag' term objects.
+ * @param array $args An array of arguments. @see get_terms()
+ */
+ $tags = apply_filters( 'get_tags', $tags, $args );
+ return $tags;
+}
+
+/**
+ * Retrieve post tag by tag ID or tag object.
+ *
+ * If you pass the $tag parameter an object, which is assumed to be the tag row
+ * object retrieved the database. It will cache the tag data.
+ *
+ * If you pass $tag an integer of the tag ID, then that tag will
+ * be retrieved from the database, if it isn't already cached, and pass it back.
+ *
+ * If you look at get_term(), then both types will be passed through several
+ * filters and finally sanitized based on the $filter parameter value.
+ *
+ * @since 2.3.0
+ *
+ * @param int|object $tag
+ * @param string $output Optional. Constant OBJECT, ARRAY_A, or ARRAY_N
+ * @param string $filter Optional. Default is raw or no WordPress defined filter will applied.
+ * @return object|array|WP_Error|null Tag data in type defined by $output parameter. WP_Error if $tag is empty, null if it does not exist.
+ */
+function get_tag( $tag, $output = OBJECT, $filter = 'raw' ) {
+ return get_term( $tag, 'post_tag', $output, $filter );
+}
+
+/* Cache */
+
+/**
+ * Remove the category cache data based on ID.
+ *
+ * @since 2.1.0
+ *
+ * @param int $id Category ID
+ */
+function clean_category_cache( $id ) {
+ clean_term_cache( $id, 'category' );
+}
+
+/**
+ * Update category structure to old pre 2.3 from new taxonomy structure.
+ *
+ * This function was added for the taxonomy support to update the new category
+ * structure with the old category one. This will maintain compatibility with
+ * plugins and themes which depend on the old key or property names.
+ *
+ * The parameter should only be passed a variable and not create the array or
+ * object inline to the parameter. The reason for this is that parameter is
+ * passed by reference and PHP will fail unless it has the variable.
+ *
+ * There is no return value, because everything is updated on the variable you
+ * pass to it. This is one of the features with using pass by reference in PHP.
+ *
+ * @since 2.3.0
+ * @since 4.4.0 The `$category` parameter now also accepts a WP_Term object.
+ * @access private
+ *
+ * @param array|object|WP_Term $category Category Row object or array
+ */
+function _make_cat_compat( &$category ) {
+ if ( is_object( $category ) && ! is_wp_error( $category ) ) {
+ $category->cat_ID = $category->term_id;
+ $category->category_count = $category->count;
+ $category->category_description = $category->description;
+ $category->cat_name = $category->name;
+ $category->category_nicename = $category->slug;
+ $category->category_parent = $category->parent;
+ } elseif ( is_array( $category ) && isset( $category['term_id'] ) ) {
+ $category['cat_ID'] = &$category['term_id'];
+ $category['category_count'] = &$category['count'];
+ $category['category_description'] = &$category['description'];
+ $category['cat_name'] = &$category['name'];
+ $category['category_nicename'] = &$category['slug'];
+ $category['category_parent'] = &$category['parent'];
+ }
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizemanagerphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-customize-manager.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-customize-manager.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/class-wp-customize-manager.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -192,6 +192,37 @@
</span><span class="cx" style="display: block; padding: 0 10px"> require_once( ABSPATH . WPINC . '/class-wp-customize-section.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require_once( ABSPATH . WPINC . '/class-wp-customize-control.php' );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-color-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-media-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-upload-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-image-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-cropped-image-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-site-icon-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-theme-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-widget-area-customize-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-widget-form-customize-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-location-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-name-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-auto-add-control.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-control.php' );
+
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php' );
+
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-section.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-section.php' );
+
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-setting.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-setting.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-setting.php' );
+ require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-setting.php' );
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px"> * Filter the core Customizer components to load.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span></span></pre></div>
<a id="trunksrcwpincludescommentfunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/comment-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/comment-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/comment-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,2797 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Comment API: Top-level comments functionality
- *
- * @package WordPress
- * @subpackage Comments
- * @since 4.4.0
- */
-
-/**
- * Check whether a comment passes internal checks to be allowed to add.
- *
- * If manual comment moderation is set in the administration, then all checks,
- * regardless of their type and whitelist, will fail and the function will
- * return false.
- *
- * If the number of links exceeds the amount in the administration, then the
- * check fails. If any of the parameter contents match the blacklist of words,
- * then the check fails.
- *
- * If the comment author was approved before, then the comment is automatically
- * whitelisted.
- *
- * If all checks pass, the function will return true.
- *
- * @since 1.2.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $author Comment author name.
- * @param string $email Comment author email.
- * @param string $url Comment author URL.
- * @param string $comment Content of the comment.
- * @param string $user_ip Comment author IP address.
- * @param string $user_agent Comment author User-Agent.
- * @param string $comment_type Comment type, either user-submitted comment,
- * trackback, or pingback.
- * @return bool If all checks pass, true, otherwise false.
- */
-function check_comment($author, $email, $url, $comment, $user_ip, $user_agent, $comment_type) {
- global $wpdb;
-
- // If manual moderation is enabled, skip all checks and return false.
- if ( 1 == get_option('comment_moderation') )
- return false;
-
- /** This filter is documented in wp-includes/comment-template.php */
- $comment = apply_filters( 'comment_text', $comment );
-
- // Check for the number of external links if a max allowed number is set.
- if ( $max_links = get_option( 'comment_max_links' ) ) {
- $num_links = preg_match_all( '/<a [^>]*href/i', $comment, $out );
-
- /**
- * Filter the maximum number of links allowed in a comment.
- *
- * @since 3.0.0
- *
- * @param int $num_links The number of links allowed.
- * @param string $url Comment author's URL. Included in allowed links total.
- */
- $num_links = apply_filters( 'comment_max_links_url', $num_links, $url );
-
- /*
- * If the number of links in the comment exceeds the allowed amount,
- * fail the check by returning false.
- */
- if ( $num_links >= $max_links )
- return false;
- }
-
- $mod_keys = trim(get_option('moderation_keys'));
-
- // If moderation 'keys' (keywords) are set, process them.
- if ( !empty($mod_keys) ) {
- $words = explode("\n", $mod_keys );
-
- foreach ( (array) $words as $word) {
- $word = trim($word);
-
- // Skip empty lines.
- if ( empty($word) )
- continue;
-
- /*
- * Do some escaping magic so that '#' (number of) characters in the spam
- * words don't break things:
- */
- $word = preg_quote($word, '#');
-
- /*
- * Check the comment fields for moderation keywords. If any are found,
- * fail the check for the given field by returning false.
- */
- $pattern = "#$word#i";
- if ( preg_match($pattern, $author) ) return false;
- if ( preg_match($pattern, $email) ) return false;
- if ( preg_match($pattern, $url) ) return false;
- if ( preg_match($pattern, $comment) ) return false;
- if ( preg_match($pattern, $user_ip) ) return false;
- if ( preg_match($pattern, $user_agent) ) return false;
- }
- }
-
- /*
- * Check if the option to approve comments by previously-approved authors is enabled.
- *
- * If it is enabled, check whether the comment author has a previously-approved comment,
- * as well as whether there are any moderation keywords (if set) present in the author
- * email address. If both checks pass, return true. Otherwise, return false.
- */
- if ( 1 == get_option('comment_whitelist')) {
- if ( 'trackback' != $comment_type && 'pingback' != $comment_type && $author != '' && $email != '' ) {
- // expected_slashed ($author, $email)
- $ok_to_comment = $wpdb->get_var("SELECT comment_approved FROM $wpdb->comments WHERE comment_author = '$author' AND comment_author_email = '$email' and comment_approved = '1' LIMIT 1");
- if ( ( 1 == $ok_to_comment ) &&
- ( empty($mod_keys) || false === strpos( $email, $mod_keys) ) )
- return true;
- else
- return false;
- } else {
- return false;
- }
- }
- return true;
-}
-
-/**
- * Retrieve the approved comments for post $post_id.
- *
- * @since 2.0.0
- * @since 4.1.0 Refactored to leverage {@see WP_Comment_Query} over a direct query.
- *
- * @param int $post_id The ID of the post.
- * @param array $args Optional. See {@see WP_Comment_Query::query()} for information
- * on accepted arguments.
- * @return int|array $comments The approved comments, or number of comments if `$count`
- * argument is true.
- */
-function get_approved_comments( $post_id, $args = array() ) {
- if ( ! $post_id ) {
- return array();
- }
-
- $defaults = array(
- 'status' => 1,
- 'post_id' => $post_id,
- 'order' => 'ASC',
- );
- $r = wp_parse_args( $args, $defaults );
-
- $query = new WP_Comment_Query;
- return $query->query( $r );
-}
-
-/**
- * Retrieves comment data given a comment ID or comment object.
- *
- * If an object is passed then the comment data will be cached and then returned
- * after being passed through a filter. If the comment is empty, then the global
- * comment variable will be used, if it is set.
- *
- * @since 2.0.0
- *
- * @global WP_Comment $comment
- *
- * @param WP_Comment|string|int $comment Comment to retrieve.
- * @param string $output Optional. OBJECT or ARRAY_A or ARRAY_N constants.
- * @return WP_Comment|array|null Depends on $output value.
- */
-function get_comment( &$comment = null, $output = OBJECT ) {
- if ( empty( $comment ) && isset( $GLOBALS['comment'] ) ) {
- $comment = $GLOBALS['comment'];
- }
-
- if ( $comment instanceof WP_Comment ) {
- $_comment = $comment;
- } elseif ( is_object( $comment ) ) {
- $_comment = new WP_Comment( $comment );
- } else {
- $_comment = WP_Comment::get_instance( $comment );
- }
-
- if ( ! $_comment ) {
- return null;
- }
-
- /**
- * Fires after a comment is retrieved.
- *
- * @since 2.3.0
- *
- * @param mixed $_comment Comment data.
- */
- $_comment = apply_filters( 'get_comment', $_comment );
-
- if ( $output == OBJECT ) {
- return $_comment;
- } elseif ( $output == ARRAY_A ) {
- return $_comment->to_array();
- } elseif ( $output == ARRAY_N ) {
- return array_values( $_comment->to_array() );
- }
- return $_comment;
-}
-
-/**
- * Retrieve a list of comments.
- *
- * The comment list can be for the blog as a whole or for an individual post.
- *
- * @since 2.7.0
- *
- * @param string|array $args Optional. Array or string of arguments. See {@see WP_Comment_Query::parse_query()}
- * for information on accepted arguments. Default empty.
- * @return int|array List of comments or number of found comments if `$count` argument is true.
- */
-function get_comments( $args = '' ) {
- $query = new WP_Comment_Query;
- return $query->query( $args );
-}
-
-/**
- * Retrieve all of the WordPress supported comment statuses.
- *
- * Comments have a limited set of valid status values, this provides the comment
- * status values and descriptions.
- *
- * @since 2.7.0
- *
- * @return array List of comment statuses.
- */
-function get_comment_statuses() {
- $status = array(
- 'hold' => __('Unapproved'),
- /* translators: comment status */
- 'approve' => _x('Approved', 'adjective'),
- /* translators: comment status */
- 'spam' => _x('Spam', 'adjective'),
- /* translators: comment status */
- 'trash' => _x('Trash', 'adjective'),
- );
-
- return $status;
-}
-
-/**
- * Gets the default comment status for a post type.
- *
- * @since 4.3.0
- *
- * @param string $post_type Optional. Post type. Default 'post'.
- * @param string $comment_type Optional. Comment type. Default 'comment'.
- * @return string Expected return value is 'open' or 'closed'.
- */
-function get_default_comment_status( $post_type = 'post', $comment_type = 'comment' ) {
- switch ( $comment_type ) {
- case 'pingback' :
- case 'trackback' :
- $supports = 'trackbacks';
- $option = 'ping';
- break;
- default :
- $supports = 'comments';
- $option = 'comment';
- }
-
- // Set the status.
- if ( 'page' === $post_type ) {
- $status = 'closed';
- } elseif ( post_type_supports( $post_type, $supports ) ) {
- $status = get_option( "default_{$option}_status" );
- } else {
- $status = 'closed';
- }
-
- /**
- * Filter the default comment status for the given post type.
- *
- * @since 4.3.0
- *
- * @param string $status Default status for the given post type,
- * either 'open' or 'closed'.
- * @param string $post_type Post type. Default is `post`.
- * @param string $comment_type Type of comment. Default is `comment`.
- */
- return apply_filters( 'get_default_comment_status' , $status, $post_type, $comment_type );
-}
-
-/**
- * The date the last comment was modified.
- *
- * @since 1.5.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- * @staticvar array $cache_lastcommentmodified
- *
- * @param string $timezone Which timezone to use in reference to 'gmt', 'blog',
- * or 'server' locations.
- * @return string Last comment modified date.
- */
-function get_lastcommentmodified($timezone = 'server') {
- global $wpdb;
- static $cache_lastcommentmodified = array();
-
- if ( isset($cache_lastcommentmodified[$timezone]) )
- return $cache_lastcommentmodified[$timezone];
-
- $add_seconds_server = date('Z');
-
- switch ( strtolower($timezone)) {
- case 'gmt':
- $lastcommentmodified = $wpdb->get_var("SELECT comment_date_gmt FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1");
- break;
- case 'blog':
- $lastcommentmodified = $wpdb->get_var("SELECT comment_date FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1");
- break;
- case 'server':
- $lastcommentmodified = $wpdb->get_var($wpdb->prepare("SELECT DATE_ADD(comment_date_gmt, INTERVAL %s SECOND) FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1", $add_seconds_server));
- break;
- }
-
- $cache_lastcommentmodified[$timezone] = $lastcommentmodified;
-
- return $lastcommentmodified;
-}
-
-/**
- * The amount of comments in a post or total comments.
- *
- * A lot like {@link wp_count_comments()}, in that they both return comment
- * stats (albeit with different types). The {@link wp_count_comments()} actual
- * caches, but this function does not.
- *
- * @since 2.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $post_id Optional. Comment amount in post if > 0, else total comments blog wide.
- * @return array The amount of spam, approved, awaiting moderation, and total comments.
- */
-function get_comment_count( $post_id = 0 ) {
- global $wpdb;
-
- $post_id = (int) $post_id;
-
- $where = '';
- if ( $post_id > 0 ) {
- $where = $wpdb->prepare("WHERE comment_post_ID = %d", $post_id);
- }
-
- $totals = (array) $wpdb->get_results("
- SELECT comment_approved, COUNT( * ) AS total
- FROM {$wpdb->comments}
- {$where}
- GROUP BY comment_approved
- ", ARRAY_A);
-
- $comment_count = array(
- 'approved' => 0,
- 'awaiting_moderation' => 0,
- 'spam' => 0,
- 'trash' => 0,
- 'post-trashed' => 0,
- 'total_comments' => 0,
- 'all' => 0,
- );
-
- foreach ( $totals as $row ) {
- switch ( $row['comment_approved'] ) {
- case 'trash':
- $comment_count['trash'] = $row['total'];
- break;
- case 'post-trashed':
- $comment_count['post-trashed'] = $row['total'];
- break;
- case 'spam':
- $comment_count['spam'] = $row['total'];
- $comment_count['total_comments'] += $row['total'];
- break;
- case '1':
- $comment_count['approved'] = $row['total'];
- $comment_count['total_comments'] += $row['total'];
- $comment_count['all'] += $row['total'];
- break;
- case '0':
- $comment_count['awaiting_moderation'] = $row['total'];
- $comment_count['total_comments'] += $row['total'];
- $comment_count['all'] += $row['total'];
- break;
- default:
- break;
- }
- }
-
- return $comment_count;
-}
-
-//
-// Comment meta functions
-//
-
-/**
- * Add meta data field to a comment.
- *
- * @since 2.9.0
- * @link https://codex.wordpress.org/Function_Reference/add_comment_meta
- *
- * @param int $comment_id Comment ID.
- * @param string $meta_key Metadata name.
- * @param mixed $meta_value Metadata value.
- * @param bool $unique Optional, default is false. Whether the same key should not be added.
- * @return int|bool Meta ID on success, false on failure.
- */
-function add_comment_meta($comment_id, $meta_key, $meta_value, $unique = false) {
- return add_metadata('comment', $comment_id, $meta_key, $meta_value, $unique);
-}
-
-/**
- * Remove metadata matching criteria from a comment.
- *
- * You can match based on the key, or key and value. Removing based on key and
- * value, will keep from removing duplicate metadata with the same key. It also
- * allows removing all metadata matching key, if needed.
- *
- * @since 2.9.0
- * @link https://codex.wordpress.org/Function_Reference/delete_comment_meta
- *
- * @param int $comment_id comment ID
- * @param string $meta_key Metadata name.
- * @param mixed $meta_value Optional. Metadata value.
- * @return bool True on success, false on failure.
- */
-function delete_comment_meta($comment_id, $meta_key, $meta_value = '') {
- return delete_metadata('comment', $comment_id, $meta_key, $meta_value);
-}
-
-/**
- * Retrieve comment meta field for a comment.
- *
- * @since 2.9.0
- * @link https://codex.wordpress.org/Function_Reference/get_comment_meta
- *
- * @param int $comment_id Comment ID.
- * @param string $key Optional. The meta key to retrieve. By default, returns data for all keys.
- * @param bool $single Whether to return a single value.
- * @return mixed Will be an array if $single is false. Will be value of meta data field if $single
- * is true.
- */
-function get_comment_meta($comment_id, $key = '', $single = false) {
- return get_metadata('comment', $comment_id, $key, $single);
-}
-
-/**
- * Update comment meta field based on comment ID.
- *
- * Use the $prev_value parameter to differentiate between meta fields with the
- * same key and comment ID.
- *
- * If the meta field for the comment does not exist, it will be added.
- *
- * @since 2.9.0
- * @link https://codex.wordpress.org/Function_Reference/update_comment_meta
- *
- * @param int $comment_id Comment ID.
- * @param string $meta_key Metadata key.
- * @param mixed $meta_value Metadata value.
- * @param mixed $prev_value Optional. Previous value to check before removing.
- * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
- */
-function update_comment_meta($comment_id, $meta_key, $meta_value, $prev_value = '') {
- return update_metadata('comment', $comment_id, $meta_key, $meta_value, $prev_value);
-}
-
-/**
- * Sets the cookies used to store an unauthenticated commentator's identity. Typically used
- * to recall previous comments by this commentator that are still held in moderation.
- *
- * @param WP_Comment $comment Comment object.
- * @param object $user Comment author's object.
- *
- * @since 3.4.0
- */
-function wp_set_comment_cookies($comment, $user) {
- if ( $user->exists() )
- return;
-
- /**
- * Filter the lifetime of the comment cookie in seconds.
- *
- * @since 2.8.0
- *
- * @param int $seconds Comment cookie lifetime. Default 30000000.
- */
- $comment_cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
- $secure = ( 'https' === parse_url( home_url(), PHP_URL_SCHEME ) );
- setcookie( 'comment_author_' . COOKIEHASH, $comment->comment_author, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
- setcookie( 'comment_author_email_' . COOKIEHASH, $comment->comment_author_email, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
- setcookie( 'comment_author_url_' . COOKIEHASH, esc_url($comment->comment_author_url), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
-}
-
-/**
- * Sanitizes the cookies sent to the user already.
- *
- * Will only do anything if the cookies have already been created for the user.
- * Mostly used after cookies had been sent to use elsewhere.
- *
- * @since 2.0.4
- */
-function sanitize_comment_cookies() {
- if ( isset( $_COOKIE['comment_author_' . COOKIEHASH] ) ) {
- /**
- * Filter the comment author's name cookie before it is set.
- *
- * When this filter hook is evaluated in wp_filter_comment(),
- * the comment author's name string is passed.
- *
- * @since 1.5.0
- *
- * @param string $author_cookie The comment author name cookie.
- */
- $comment_author = apply_filters( 'pre_comment_author_name', $_COOKIE['comment_author_' . COOKIEHASH] );
- $comment_author = wp_unslash($comment_author);
- $comment_author = esc_attr($comment_author);
- $_COOKIE['comment_author_' . COOKIEHASH] = $comment_author;
- }
-
- if ( isset( $_COOKIE['comment_author_email_' . COOKIEHASH] ) ) {
- /**
- * Filter the comment author's email cookie before it is set.
- *
- * When this filter hook is evaluated in wp_filter_comment(),
- * the comment author's email string is passed.
- *
- * @since 1.5.0
- *
- * @param string $author_email_cookie The comment author email cookie.
- */
- $comment_author_email = apply_filters( 'pre_comment_author_email', $_COOKIE['comment_author_email_' . COOKIEHASH] );
- $comment_author_email = wp_unslash($comment_author_email);
- $comment_author_email = esc_attr($comment_author_email);
- $_COOKIE['comment_author_email_'.COOKIEHASH] = $comment_author_email;
- }
-
- if ( isset( $_COOKIE['comment_author_url_' . COOKIEHASH] ) ) {
- /**
- * Filter the comment author's URL cookie before it is set.
- *
- * When this filter hook is evaluated in wp_filter_comment(),
- * the comment author's URL string is passed.
- *
- * @since 1.5.0
- *
- * @param string $author_url_cookie The comment author URL cookie.
- */
- $comment_author_url = apply_filters( 'pre_comment_author_url', $_COOKIE['comment_author_url_' . COOKIEHASH] );
- $comment_author_url = wp_unslash($comment_author_url);
- $_COOKIE['comment_author_url_'.COOKIEHASH] = $comment_author_url;
- }
-}
-
-/**
- * Validates whether this comment is allowed to be made.
- *
- * @since 2.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array $commentdata Contains information on the comment
- * @return int|string Signifies the approval status (0|1|'spam')
- */
-function wp_allow_comment( $commentdata ) {
- global $wpdb;
-
- // Simple duplicate check
- // expected_slashed ($comment_post_ID, $comment_author, $comment_author_email, $comment_content)
- $dupe = $wpdb->prepare(
- "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_parent = %s AND comment_approved != 'trash' AND ( comment_author = %s ",
- wp_unslash( $commentdata['comment_post_ID'] ),
- wp_unslash( $commentdata['comment_parent'] ),
- wp_unslash( $commentdata['comment_author'] )
- );
- if ( $commentdata['comment_author_email'] ) {
- $dupe .= $wpdb->prepare(
- "OR comment_author_email = %s ",
- wp_unslash( $commentdata['comment_author_email'] )
- );
- }
- $dupe .= $wpdb->prepare(
- ") AND comment_content = %s LIMIT 1",
- wp_unslash( $commentdata['comment_content'] )
- );
-
- $dupe_id = $wpdb->get_var( $dupe );
-
- /**
- * Filters the ID, if any, of the duplicate comment found when creating a new comment.
- *
- * Return an empty value from this filter to allow what WP considers a duplicate comment.
- *
- * @since 4.4.0
- *
- * @param int $dupe_id ID of the comment identified as a duplicate.
- * @param array $commentdata Data for the comment being created.
- */
- $dupe_id = apply_filters( 'duplicate_comment_id', $dupe_id, $commentdata );
-
- if ( $dupe_id ) {
- /**
- * Fires immediately after a duplicate comment is detected.
- *
- * @since 3.0.0
- *
- * @param array $commentdata Comment data.
- */
- do_action( 'comment_duplicate_trigger', $commentdata );
- if ( defined( 'DOING_AJAX' ) ) {
- die( __('Duplicate comment detected; it looks as though you’ve already said that!') );
- }
- wp_die( __( 'Duplicate comment detected; it looks as though you’ve already said that!' ), 409 );
- }
-
- /**
- * Fires immediately before a comment is marked approved.
- *
- * Allows checking for comment flooding.
- *
- * @since 2.3.0
- *
- * @param string $comment_author_IP Comment author's IP address.
- * @param string $comment_author_email Comment author's email.
- * @param string $comment_date_gmt GMT date the comment was posted.
- */
- do_action(
- 'check_comment_flood',
- $commentdata['comment_author_IP'],
- $commentdata['comment_author_email'],
- $commentdata['comment_date_gmt']
- );
-
- if ( ! empty( $commentdata['user_id'] ) ) {
- $user = get_userdata( $commentdata['user_id'] );
- $post_author = $wpdb->get_var( $wpdb->prepare(
- "SELECT post_author FROM $wpdb->posts WHERE ID = %d LIMIT 1",
- $commentdata['comment_post_ID']
- ) );
- }
-
- if ( isset( $user ) && ( $commentdata['user_id'] == $post_author || $user->has_cap( 'moderate_comments' ) ) ) {
- // The author and the admins get respect.
- $approved = 1;
- } else {
- // Everyone else's comments will be checked.
- if ( check_comment(
- $commentdata['comment_author'],
- $commentdata['comment_author_email'],
- $commentdata['comment_author_url'],
- $commentdata['comment_content'],
- $commentdata['comment_author_IP'],
- $commentdata['comment_agent'],
- $commentdata['comment_type']
- ) ) {
- $approved = 1;
- } else {
- $approved = 0;
- }
-
- if ( wp_blacklist_check(
- $commentdata['comment_author'],
- $commentdata['comment_author_email'],
- $commentdata['comment_author_url'],
- $commentdata['comment_content'],
- $commentdata['comment_author_IP'],
- $commentdata['comment_agent']
- ) ) {
- $approved = EMPTY_TRASH_DAYS ? 'trash' : 'spam';
- }
- }
-
- /**
- * Filter a comment's approval status before it is set.
- *
- * @since 2.1.0
- *
- * @param bool|string $approved The approval status. Accepts 1, 0, or 'spam'.
- * @param array $commentdata Comment data.
- */
- $approved = apply_filters( 'pre_comment_approved', $approved, $commentdata );
- return $approved;
-}
-
-/**
- * Check whether comment flooding is occurring.
- *
- * Won't run, if current user can manage options, so to not block
- * administrators.
- *
- * @since 2.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $ip Comment IP.
- * @param string $email Comment author email address.
- * @param string $date MySQL time string.
- */
-function check_comment_flood_db( $ip, $email, $date ) {
- global $wpdb;
- // don't throttle admins or moderators
- if ( current_user_can( 'manage_options' ) || current_user_can( 'moderate_comments' ) ) {
- return;
- }
- $hour_ago = gmdate( 'Y-m-d H:i:s', time() - HOUR_IN_SECONDS );
-
- if ( is_user_logged_in() ) {
- $user = get_current_user_id();
- $check_column = '`user_id`';
- } else {
- $user = $ip;
- $check_column = '`comment_author_IP`';
- }
-
- $sql = $wpdb->prepare(
- "SELECT `comment_date_gmt` FROM `$wpdb->comments` WHERE `comment_date_gmt` >= %s AND ( $check_column = %s OR `comment_author_email` = %s ) ORDER BY `comment_date_gmt` DESC LIMIT 1",
- $hour_ago,
- $user,
- $email
- );
- $lasttime = $wpdb->get_var( $sql );
- if ( $lasttime ) {
- $time_lastcomment = mysql2date('U', $lasttime, false);
- $time_newcomment = mysql2date('U', $date, false);
- /**
- * Filter the comment flood status.
- *
- * @since 2.1.0
- *
- * @param bool $bool Whether a comment flood is occurring. Default false.
- * @param int $time_lastcomment Timestamp of when the last comment was posted.
- * @param int $time_newcomment Timestamp of when the new comment was posted.
- */
- $flood_die = apply_filters( 'comment_flood_filter', false, $time_lastcomment, $time_newcomment );
- if ( $flood_die ) {
- /**
- * Fires before the comment flood message is triggered.
- *
- * @since 1.5.0
- *
- * @param int $time_lastcomment Timestamp of when the last comment was posted.
- * @param int $time_newcomment Timestamp of when the new comment was posted.
- */
- do_action( 'comment_flood_trigger', $time_lastcomment, $time_newcomment );
-
- if ( defined('DOING_AJAX') )
- die( __('You are posting comments too quickly. Slow down.') );
-
- wp_die( __( 'You are posting comments too quickly. Slow down.' ), 429 );
- }
- }
-}
-
-/**
- * Separates an array of comments into an array keyed by comment_type.
- *
- * @since 2.7.0
- *
- * @param array $comments Array of comments
- * @return array Array of comments keyed by comment_type.
- */
-function separate_comments(&$comments) {
- $comments_by_type = array('comment' => array(), 'trackback' => array(), 'pingback' => array(), 'pings' => array());
- $count = count($comments);
- for ( $i = 0; $i < $count; $i++ ) {
- $type = $comments[$i]->comment_type;
- if ( empty($type) )
- $type = 'comment';
- $comments_by_type[$type][] = &$comments[$i];
- if ( 'trackback' == $type || 'pingback' == $type )
- $comments_by_type['pings'][] = &$comments[$i];
- }
-
- return $comments_by_type;
-}
-
-/**
- * Calculate the total number of comment pages.
- *
- * @since 2.7.0
- *
- * @uses Walker_Comment
- *
- * @global WP_Query $wp_query
- *
- * @param array $comments Optional array of WP_Comment objects. Defaults to $wp_query->comments
- * @param int $per_page Optional comments per page.
- * @param bool $threaded Optional control over flat or threaded comments.
- * @return int Number of comment pages.
- */
-function get_comment_pages_count( $comments = null, $per_page = null, $threaded = null ) {
- global $wp_query;
-
- if ( null === $comments && null === $per_page && null === $threaded && !empty($wp_query->max_num_comment_pages) )
- return $wp_query->max_num_comment_pages;
-
- if ( ( ! $comments || ! is_array( $comments ) ) && ! empty( $wp_query->comments ) )
- $comments = $wp_query->comments;
-
- if ( empty($comments) )
- return 0;
-
- if ( ! get_option( 'page_comments' ) ) {
- return 1;
- }
-
- if ( !isset($per_page) )
- $per_page = (int) get_query_var('comments_per_page');
- if ( 0 === $per_page )
- $per_page = (int) get_option('comments_per_page');
- if ( 0 === $per_page )
- return 1;
-
- if ( !isset($threaded) )
- $threaded = get_option('thread_comments');
-
- if ( $threaded ) {
- $walker = new Walker_Comment;
- $count = ceil( $walker->get_number_of_root_elements( $comments ) / $per_page );
- } else {
- $count = ceil( count( $comments ) / $per_page );
- }
-
- return $count;
-}
-
-/**
- * Calculate what page number a comment will appear on for comment paging.
- *
- * @since 2.7.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $comment_ID Comment ID.
- * @param array $args {
- * Array of optional arguments.
- * @type string $type Limit paginated comments to those matching a given type. Accepts 'comment',
- * 'trackback', 'pingback', 'pings' (trackbacks and pingbacks), or 'all'.
- * Default is 'all'.
- * @type int $per_page Per-page count to use when calculating pagination. Defaults to the value of the
- * 'comments_per_page' option.
- * @type int|string $max_depth If greater than 1, comment page will be determined for the top-level parent of
- * `$comment_ID`. Defaults to the value of the 'thread_comments_depth' option.
- * } *
- * @return int|null Comment page number or null on error.
- */
-function get_page_of_comment( $comment_ID, $args = array() ) {
- global $wpdb;
-
- $page = null;
-
- if ( !$comment = get_comment( $comment_ID ) )
- return;
-
- $defaults = array( 'type' => 'all', 'page' => '', 'per_page' => '', 'max_depth' => '' );
- $args = wp_parse_args( $args, $defaults );
- $original_args = $args;
-
- // Order of precedence: 1. `$args['per_page']`, 2. 'comments_per_page' query_var, 3. 'comments_per_page' option.
- if ( get_option( 'page_comments' ) ) {
- if ( '' === $args['per_page'] ) {
- $args['per_page'] = get_query_var( 'comments_per_page' );
- }
-
- if ( '' === $args['per_page'] ) {
- $args['per_page'] = get_option( 'comments_per_page' );
- }
- }
-
- if ( empty($args['per_page']) ) {
- $args['per_page'] = 0;
- $args['page'] = 0;
- }
-
- if ( $args['per_page'] < 1 ) {
- $page = 1;
- }
-
- if ( null === $page ) {
- if ( '' === $args['max_depth'] ) {
- if ( get_option('thread_comments') )
- $args['max_depth'] = get_option('thread_comments_depth');
- else
- $args['max_depth'] = -1;
- }
-
- // Find this comment's top level parent if threading is enabled
- if ( $args['max_depth'] > 1 && 0 != $comment->comment_parent )
- return get_page_of_comment( $comment->comment_parent, $args );
-
- $comment_args = array(
- 'type' => $args['type'],
- 'post_id' => $comment->comment_post_ID,
- 'fields' => 'ids',
- 'count' => true,
- 'status' => 'approve',
- 'parent' => 0,
- 'date_query' => array(
- array(
- 'column' => "$wpdb->comments.comment_date_gmt",
- 'before' => $comment->comment_date_gmt,
- )
- ),
- );
-
- $comment_query = new WP_Comment_Query();
- $older_comment_count = $comment_query->query( $comment_args );
-
- // No older comments? Then it's page #1.
- if ( 0 == $older_comment_count ) {
- $page = 1;
-
- // Divide comments older than this one by comments per page to get this comment's page number
- } else {
- $page = ceil( ( $older_comment_count + 1 ) / $args['per_page'] );
- }
- }
-
- /**
- * Filters the calculated page on which a comment appears.
- *
- * @since 4.4.0
- *
- * @param int $page Comment page.
- * @param array $args {
- * Arguments used to calculate pagination. These include arguments auto-detected by the function,
- * based on query vars, system settings, etc. For pristine arguments passed to the function,
- * see `$original_args`.
- *
- * @type string $type Type of comments to count.
- * @type int $page Calculated current page.
- * @type int $per_page Calculated number of comments per page.
- * @type int $max_depth Maximum comment threading depth allowed.
- * }
- * @param array $original_args {
- * Array of arguments passed to the function. Some or all of these may not be set.
- *
- * @type string $type Type of comments to count.
- * @type int $page Current comment page.
- * @type int $per_page Number of comments per page.
- * @type int $max_depth Maximum comment threading depth allowed.
- * }
- */
- return apply_filters( 'get_page_of_comment', (int) $page, $args, $original_args );
-}
-
-/**
- * Does comment contain blacklisted characters or words.
- *
- * @since 1.5.0
- *
- * @param string $author The author of the comment
- * @param string $email The email of the comment
- * @param string $url The url used in the comment
- * @param string $comment The comment content
- * @param string $user_ip The comment author IP address
- * @param string $user_agent The author's browser user agent
- * @return bool True if comment contains blacklisted content, false if comment does not
- */
-function wp_blacklist_check($author, $email, $url, $comment, $user_ip, $user_agent) {
- /**
- * Fires before the comment is tested for blacklisted characters or words.
- *
- * @since 1.5.0
- *
- * @param string $author Comment author.
- * @param string $email Comment author's email.
- * @param string $url Comment author's URL.
- * @param string $comment Comment content.
- * @param string $user_ip Comment author's IP address.
- * @param string $user_agent Comment author's browser user agent.
- */
- do_action( 'wp_blacklist_check', $author, $email, $url, $comment, $user_ip, $user_agent );
-
- $mod_keys = trim( get_option('blacklist_keys') );
- if ( '' == $mod_keys )
- return false; // If moderation keys are empty
- $words = explode("\n", $mod_keys );
-
- foreach ( (array) $words as $word ) {
- $word = trim($word);
-
- // Skip empty lines
- if ( empty($word) ) { continue; }
-
- // Do some escaping magic so that '#' chars in the
- // spam words don't break things:
- $word = preg_quote($word, '#');
-
- $pattern = "#$word#i";
- if (
- preg_match($pattern, $author)
- || preg_match($pattern, $email)
- || preg_match($pattern, $url)
- || preg_match($pattern, $comment)
- || preg_match($pattern, $user_ip)
- || preg_match($pattern, $user_agent)
- )
- return true;
- }
- return false;
-}
-
-/**
- * Retrieve total comments for blog or single post.
- *
- * The properties of the returned object contain the 'moderated', 'approved',
- * and spam comments for either the entire blog or single post. Those properties
- * contain the amount of comments that match the status. The 'total_comments'
- * property contains the integer of total comments.
- *
- * The comment stats are cached and then retrieved, if they already exist in the
- * cache.
- *
- * @since 2.5.0
- *
- * @param int $post_id Optional. Post ID.
- * @return object|array Comment stats.
- */
-function wp_count_comments( $post_id = 0 ) {
- $post_id = (int) $post_id;
-
- /**
- * Filter the comments count for a given post.
- *
- * @since 2.7.0
- *
- * @param array $count An empty array.
- * @param int $post_id The post ID.
- */
- $filtered = apply_filters( 'wp_count_comments', array(), $post_id );
- if ( ! empty( $filtered ) ) {
- return $filtered;
- }
-
- $count = wp_cache_get( "comments-{$post_id}", 'counts' );
- if ( false !== $count ) {
- return $count;
- }
-
- $stats = get_comment_count( $post_id );
- $stats['moderated'] = $stats['awaiting_moderation'];
- unset( $stats['awaiting_moderation'] );
-
- $stats_object = (object) $stats;
- wp_cache_set( "comments-{$post_id}", $stats_object, 'counts' );
-
- return $stats_object;
-}
-
-/**
- * Trashes or deletes a comment.
- *
- * The comment is moved to trash instead of permanently deleted unless trash is
- * disabled, item is already in the trash, or $force_delete is true.
- *
- * The post comment count will be updated if the comment was approved and has a
- * post ID available.
- *
- * @since 2.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
- * @param bool $force_delete Whether to bypass trash and force deletion. Default is false.
- * @return bool True on success, false on failure.
- */
-function wp_delete_comment($comment_id, $force_delete = false) {
- global $wpdb;
- if (!$comment = get_comment($comment_id))
- return false;
-
- if ( !$force_delete && EMPTY_TRASH_DAYS && !in_array( wp_get_comment_status( $comment ), array( 'trash', 'spam' ) ) )
- return wp_trash_comment($comment_id);
-
- /**
- * Fires immediately before a comment is deleted from the database.
- *
- * @since 1.2.0
- *
- * @param int $comment_id The comment ID.
- */
- do_action( 'delete_comment', $comment->comment_ID );
-
- // Move children up a level.
- $children = $wpdb->get_col( $wpdb->prepare("SELECT comment_ID FROM $wpdb->comments WHERE comment_parent = %d", $comment->comment_ID) );
- if ( !empty($children) ) {
- $wpdb->update($wpdb->comments, array('comment_parent' => $comment->comment_parent), array('comment_parent' => $comment->comment_ID));
- clean_comment_cache($children);
- }
-
- // Delete metadata
- $meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->commentmeta WHERE comment_id = %d", $comment->comment_ID ) );
- foreach ( $meta_ids as $mid )
- delete_metadata_by_mid( 'comment', $mid );
-
- if ( ! $wpdb->delete( $wpdb->comments, array( 'comment_ID' => $comment->comment_ID ) ) )
- return false;
-
- /**
- * Fires immediately after a comment is deleted from the database.
- *
- * @since 2.9.0
- *
- * @param int $comment_id The comment ID.
- */
- do_action( 'deleted_comment', $comment->comment_ID );
-
- $post_id = $comment->comment_post_ID;
- if ( $post_id && $comment->comment_approved == 1 )
- wp_update_comment_count($post_id);
-
- clean_comment_cache( $comment->comment_ID );
-
- /** This action is documented in wp-includes/comment-functions.php */
- do_action( 'wp_set_comment_status', $comment->comment_ID, 'delete' );
-
- wp_transition_comment_status('delete', $comment->comment_approved, $comment);
- return true;
-}
-
-/**
- * Moves a comment to the Trash
- *
- * If trash is disabled, comment is permanently deleted.
- *
- * @since 2.9.0
- *
- * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
- * @return bool True on success, false on failure.
- */
-function wp_trash_comment($comment_id) {
- if ( !EMPTY_TRASH_DAYS )
- return wp_delete_comment($comment_id, true);
-
- if ( !$comment = get_comment($comment_id) )
- return false;
-
- /**
- * Fires immediately before a comment is sent to the Trash.
- *
- * @since 2.9.0
- *
- * @param int $comment_id The comment ID.
- */
- do_action( 'trash_comment', $comment->comment_ID );
-
- if ( wp_set_comment_status( $comment, 'trash' ) ) {
- delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_status' );
- delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_time' );
- add_comment_meta( $comment->comment_ID, '_wp_trash_meta_status', $comment->comment_approved );
- add_comment_meta( $comment->comment_ID, '_wp_trash_meta_time', time() );
-
- /**
- * Fires immediately after a comment is sent to Trash.
- *
- * @since 2.9.0
- *
- * @param int $comment_id The comment ID.
- */
- do_action( 'trashed_comment', $comment->comment_ID );
- return true;
- }
-
- return false;
-}
-
-/**
- * Removes a comment from the Trash
- *
- * @since 2.9.0
- *
- * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
- * @return bool True on success, false on failure.
- */
-function wp_untrash_comment($comment_id) {
- $comment = get_comment( $comment_id );
- if ( ! $comment ) {
- return false;
- }
-
- /**
- * Fires immediately before a comment is restored from the Trash.
- *
- * @since 2.9.0
- *
- * @param int $comment_id The comment ID.
- */
- do_action( 'untrash_comment', $comment->comment_ID );
-
- $status = (string) get_comment_meta( $comment->comment_ID, '_wp_trash_meta_status', true );
- if ( empty($status) )
- $status = '0';
-
- if ( wp_set_comment_status( $comment, $status ) ) {
- delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_time' );
- delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_status' );
- /**
- * Fires immediately after a comment is restored from the Trash.
- *
- * @since 2.9.0
- *
- * @param int $comment_id The comment ID.
- */
- do_action( 'untrashed_comment', $comment->comment_ID );
- return true;
- }
-
- return false;
-}
-
-/**
- * Marks a comment as Spam
- *
- * @since 2.9.0
- *
- * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
- * @return bool True on success, false on failure.
- */
-function wp_spam_comment( $comment_id ) {
- $comment = get_comment( $comment_id );
- if ( ! $comment ) {
- return false;
- }
-
- /**
- * Fires immediately before a comment is marked as Spam.
- *
- * @since 2.9.0
- *
- * @param int $comment_id The comment ID.
- */
- do_action( 'spam_comment', $comment->comment_ID );
-
- if ( wp_set_comment_status( $comment, 'spam' ) ) {
- delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_status' );
- delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_time' );
- add_comment_meta( $comment->comment_ID, '_wp_trash_meta_status', $comment->comment_approved );
- add_comment_meta( $comment->comment_ID, '_wp_trash_meta_time', time() );
- /**
- * Fires immediately after a comment is marked as Spam.
- *
- * @since 2.9.0
- *
- * @param int $comment_id The comment ID.
- */
- do_action( 'spammed_comment', $comment->comment_ID );
- return true;
- }
-
- return false;
-}
-
-/**
- * Removes a comment from the Spam
- *
- * @since 2.9.0
- *
- * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
- * @return bool True on success, false on failure.
- */
-function wp_unspam_comment( $comment_id ) {
- $comment = get_comment( $comment_id );
- if ( ! $comment ) {
- return false;
- }
-
- /**
- * Fires immediately before a comment is unmarked as Spam.
- *
- * @since 2.9.0
- *
- * @param int $comment_id The comment ID.
- */
- do_action( 'unspam_comment', $comment->comment_ID );
-
- $status = (string) get_comment_meta( $comment->comment_ID, '_wp_trash_meta_status', true );
- if ( empty($status) )
- $status = '0';
-
- if ( wp_set_comment_status( $comment, $status ) ) {
- delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_status' );
- delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_time' );
- /**
- * Fires immediately after a comment is unmarked as Spam.
- *
- * @since 2.9.0
- *
- * @param int $comment_id The comment ID.
- */
- do_action( 'unspammed_comment', $comment->comment_ID );
- return true;
- }
-
- return false;
-}
-
-/**
- * The status of a comment by ID.
- *
- * @since 1.0.0
- *
- * @param int|WP_Comment $comment_id Comment ID or WP_Comment object
- * @return false|string Status might be 'trash', 'approved', 'unapproved', 'spam'. False on failure.
- */
-function wp_get_comment_status($comment_id) {
- $comment = get_comment($comment_id);
- if ( !$comment )
- return false;
-
- $approved = $comment->comment_approved;
-
- if ( $approved == null )
- return false;
- elseif ( $approved == '1' )
- return 'approved';
- elseif ( $approved == '0' )
- return 'unapproved';
- elseif ( $approved == 'spam' )
- return 'spam';
- elseif ( $approved == 'trash' )
- return 'trash';
- else
- return false;
-}
-
-/**
- * Call hooks for when a comment status transition occurs.
- *
- * Calls hooks for comment status transitions. If the new comment status is not the same
- * as the previous comment status, then two hooks will be ran, the first is
- * 'transition_comment_status' with new status, old status, and comment data. The
- * next action called is 'comment_OLDSTATUS_to_NEWSTATUS' the NEWSTATUS is the
- * $new_status parameter and the OLDSTATUS is $old_status parameter; it has the
- * comment data.
- *
- * The final action will run whether or not the comment statuses are the same. The
- * action is named 'comment_NEWSTATUS_COMMENTTYPE', NEWSTATUS is from the $new_status
- * parameter and COMMENTTYPE is comment_type comment data.
- *
- * @since 2.7.0
- *
- * @param string $new_status New comment status.
- * @param string $old_status Previous comment status.
- * @param object $comment Comment data.
- */
-function wp_transition_comment_status($new_status, $old_status, $comment) {
- /*
- * Translate raw statuses to human readable formats for the hooks.
- * This is not a complete list of comment status, it's only the ones
- * that need to be renamed
- */
- $comment_statuses = array(
- 0 => 'unapproved',
- 'hold' => 'unapproved', // wp_set_comment_status() uses "hold"
- 1 => 'approved',
- 'approve' => 'approved', // wp_set_comment_status() uses "approve"
- );
- if ( isset($comment_statuses[$new_status]) ) $new_status = $comment_statuses[$new_status];
- if ( isset($comment_statuses[$old_status]) ) $old_status = $comment_statuses[$old_status];
-
- // Call the hooks
- if ( $new_status != $old_status ) {
- /**
- * Fires when the comment status is in transition.
- *
- * @since 2.7.0
- *
- * @param int|string $new_status The new comment status.
- * @param int|string $old_status The old comment status.
- * @param object $comment The comment data.
- */
- do_action( 'transition_comment_status', $new_status, $old_status, $comment );
- /**
- * Fires when the comment status is in transition from one specific status to another.
- *
- * The dynamic portions of the hook name, `$old_status`, and `$new_status`,
- * refer to the old and new comment statuses, respectively.
- *
- * @since 2.7.0
- *
- * @param WP_Comment $comment Comment object.
- */
- do_action( "comment_{$old_status}_to_{$new_status}", $comment );
- }
- /**
- * Fires when the status of a specific comment type is in transition.
- *
- * The dynamic portions of the hook name, `$new_status`, and `$comment->comment_type`,
- * refer to the new comment status, and the type of comment, respectively.
- *
- * Typical comment types include an empty string (standard comment), 'pingback',
- * or 'trackback'.
- *
- * @since 2.7.0
- *
- * @param int $comment_ID The comment ID.
- * @param WP_Comment $comment Comment object.
- */
- do_action( "comment_{$new_status}_{$comment->comment_type}", $comment->comment_ID, $comment );
-}
-
-/**
- * Get current commenter's name, email, and URL.
- *
- * Expects cookies content to already be sanitized. User of this function might
- * wish to recheck the returned array for validity.
- *
- * @see sanitize_comment_cookies() Use to sanitize cookies
- *
- * @since 2.0.4
- *
- * @return array Comment author, email, url respectively.
- */
-function wp_get_current_commenter() {
- // Cookies should already be sanitized.
-
- $comment_author = '';
- if ( isset($_COOKIE['comment_author_'.COOKIEHASH]) )
- $comment_author = $_COOKIE['comment_author_'.COOKIEHASH];
-
- $comment_author_email = '';
- if ( isset($_COOKIE['comment_author_email_'.COOKIEHASH]) )
- $comment_author_email = $_COOKIE['comment_author_email_'.COOKIEHASH];
-
- $comment_author_url = '';
- if ( isset($_COOKIE['comment_author_url_'.COOKIEHASH]) )
- $comment_author_url = $_COOKIE['comment_author_url_'.COOKIEHASH];
-
- /**
- * Filter the current commenter's name, email, and URL.
- *
- * @since 3.1.0
- *
- * @param array $comment_author_data {
- * An array of current commenter variables.
- *
- * @type string $comment_author The name of the author of the comment. Default empty.
- * @type string $comment_author_email The email address of the `$comment_author`. Default empty.
- * @type string $comment_author_url The URL address of the `$comment_author`. Default empty.
- * }
- */
- return apply_filters( 'wp_get_current_commenter', compact('comment_author', 'comment_author_email', 'comment_author_url') );
-}
-
-/**
- * Inserts a comment into the database.
- *
- * @since 2.0.0
- * @since 4.4.0 Introduced `$comment_meta` argument.
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array $commentdata {
- * Array of arguments for inserting a new comment.
- *
- * @type string $comment_agent The HTTP user agent of the `$comment_author` when
- * the comment was submitted. Default empty.
- * @type int|string $comment_approved Whether the comment has been approved. Default 1.
- * @type string $comment_author The name of the author of the comment. Default empty.
- * @type string $comment_author_email The email address of the `$comment_author`. Default empty.
- * @type string $comment_author_IP The IP address of the `$comment_author`. Default empty.
- * @type string $comment_author_url The URL address of the `$comment_author`. Default empty.
- * @type string $comment_content The content of the comment. Default empty.
- * @type string $comment_date The date the comment was submitted. To set the date
- * manually, `$comment_date_gmt` must also be specified.
- * Default is the current time.
- * @type string $comment_date_gmt The date the comment was submitted in the GMT timezone.
- * Default is `$comment_date` in the site's GMT timezone.
- * @type int $comment_karma The karma of the comment. Default 0.
- * @type int $comment_parent ID of this comment's parent, if any. Default 0.
- * @type int $comment_post_ID ID of the post that relates to the comment, if any.
- * Default empty.
- * @type string $comment_type Comment type. Default empty.
- * @type array $comment_meta Optional. Array of key/value pairs to be stored in commentmeta for the
- * new comment.
- * @type int $user_id ID of the user who submitted the comment. Default 0.
- * }
- * @return int|false The new comment's ID on success, false on failure.
- */
-function wp_insert_comment( $commentdata ) {
- global $wpdb;
- $data = wp_unslash( $commentdata );
-
- $comment_author = ! isset( $data['comment_author'] ) ? '' : $data['comment_author'];
- $comment_author_email = ! isset( $data['comment_author_email'] ) ? '' : $data['comment_author_email'];
- $comment_author_url = ! isset( $data['comment_author_url'] ) ? '' : $data['comment_author_url'];
- $comment_author_IP = ! isset( $data['comment_author_IP'] ) ? '' : $data['comment_author_IP'];
-
- $comment_date = ! isset( $data['comment_date'] ) ? current_time( 'mysql' ) : $data['comment_date'];
- $comment_date_gmt = ! isset( $data['comment_date_gmt'] ) ? get_gmt_from_date( $comment_date ) : $data['comment_date_gmt'];
-
- $comment_post_ID = ! isset( $data['comment_post_ID'] ) ? '' : $data['comment_post_ID'];
- $comment_content = ! isset( $data['comment_content'] ) ? '' : $data['comment_content'];
- $comment_karma = ! isset( $data['comment_karma'] ) ? 0 : $data['comment_karma'];
- $comment_approved = ! isset( $data['comment_approved'] ) ? 1 : $data['comment_approved'];
- $comment_agent = ! isset( $data['comment_agent'] ) ? '' : $data['comment_agent'];
- $comment_type = ! isset( $data['comment_type'] ) ? '' : $data['comment_type'];
- $comment_parent = ! isset( $data['comment_parent'] ) ? 0 : $data['comment_parent'];
-
- $user_id = ! isset( $data['user_id'] ) ? 0 : $data['user_id'];
-
- $compacted = compact( 'comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_date', 'comment_date_gmt', 'comment_content', 'comment_karma', 'comment_approved', 'comment_agent', 'comment_type', 'comment_parent', 'user_id' );
- if ( ! $wpdb->insert( $wpdb->comments, $compacted ) ) {
- return false;
- }
-
- $id = (int) $wpdb->insert_id;
-
- if ( $comment_approved == 1 ) {
- wp_update_comment_count( $comment_post_ID );
- }
- $comment = get_comment( $id );
-
- // If metadata is provided, store it.
- if ( isset( $commentdata['comment_meta'] ) && is_array( $commentdata['comment_meta'] ) ) {
- foreach ( $commentdata['comment_meta'] as $meta_key => $meta_value ) {
- add_comment_meta( $comment->comment_ID, $meta_key, $meta_value, true );
- }
- }
-
- /**
- * Fires immediately after a comment is inserted into the database.
- *
- * @since 2.8.0
- *
- * @param int $id The comment ID.
- * @param WP_Comment $comment Comment object.
- */
- do_action( 'wp_insert_comment', $id, $comment );
-
- wp_cache_set( 'last_changed', microtime(), 'comment' );
-
- return $id;
-}
-
-/**
- * Filters and sanitizes comment data.
- *
- * Sets the comment data 'filtered' field to true when finished. This can be
- * checked as to whether the comment should be filtered and to keep from
- * filtering the same comment more than once.
- *
- * @since 2.0.0
- *
- * @param array $commentdata Contains information on the comment.
- * @return array Parsed comment information.
- */
-function wp_filter_comment($commentdata) {
- if ( isset( $commentdata['user_ID'] ) ) {
- /**
- * Filter the comment author's user id before it is set.
- *
- * The first time this filter is evaluated, 'user_ID' is checked
- * (for back-compat), followed by the standard 'user_id' value.
- *
- * @since 1.5.0
- *
- * @param int $user_ID The comment author's user ID.
- */
- $commentdata['user_id'] = apply_filters( 'pre_user_id', $commentdata['user_ID'] );
- } elseif ( isset( $commentdata['user_id'] ) ) {
- /** This filter is documented in wp-includes/comment-functions.php */
- $commentdata['user_id'] = apply_filters( 'pre_user_id', $commentdata['user_id'] );
- }
-
- /**
- * Filter the comment author's browser user agent before it is set.
- *
- * @since 1.5.0
- *
- * @param int $comment_agent The comment author's browser user agent.
- */
- $commentdata['comment_agent'] = apply_filters( 'pre_comment_user_agent', ( isset( $commentdata['comment_agent'] ) ? $commentdata['comment_agent'] : '' ) );
- /** This filter is documented in wp-includes/comment-functions.php */
- $commentdata['comment_author'] = apply_filters( 'pre_comment_author_name', $commentdata['comment_author'] );
- /**
- * Filter the comment content before it is set.
- *
- * @since 1.5.0
- *
- * @param int $comment_content The comment content.
- */
- $commentdata['comment_content'] = apply_filters( 'pre_comment_content', $commentdata['comment_content'] );
- /**
- * Filter the comment author's IP before it is set.
- *
- * @since 1.5.0
- *
- * @param int $comment_author_ip The comment author's IP.
- */
- $commentdata['comment_author_IP'] = apply_filters( 'pre_comment_user_ip', $commentdata['comment_author_IP'] );
- /** This filter is documented in wp-includes/comment-functions.php */
- $commentdata['comment_author_url'] = apply_filters( 'pre_comment_author_url', $commentdata['comment_author_url'] );
- /** This filter is documented in wp-includes/comment-functions.php */
- $commentdata['comment_author_email'] = apply_filters( 'pre_comment_author_email', $commentdata['comment_author_email'] );
- $commentdata['filtered'] = true;
- return $commentdata;
-}
-
-/**
- * Whether a comment should be blocked because of comment flood.
- *
- * @since 2.1.0
- *
- * @param bool $block Whether plugin has already blocked comment.
- * @param int $time_lastcomment Timestamp for last comment.
- * @param int $time_newcomment Timestamp for new comment.
- * @return bool Whether comment should be blocked.
- */
-function wp_throttle_comment_flood($block, $time_lastcomment, $time_newcomment) {
- if ( $block ) // a plugin has already blocked... we'll let that decision stand
- return $block;
- if ( ($time_newcomment - $time_lastcomment) < 15 )
- return true;
- return false;
-}
-
-/**
- * Adds a new comment to the database.
- *
- * Filters new comment to ensure that the fields are sanitized and valid before
- * inserting comment into database. Calls 'comment_post' action with comment ID
- * and whether comment is approved by WordPress. Also has 'preprocess_comment'
- * filter for processing the comment data before the function handles it.
- *
- * We use REMOTE_ADDR here directly. If you are behind a proxy, you should ensure
- * that it is properly set, such as in wp-config.php, for your environment.
- * See {@link https://core.trac.wordpress.org/ticket/9235}
- *
- * @since 1.5.0
- * @since 4.3.0 'comment_agent' and 'comment_author_IP' can be set via `$commentdata`.
- *
- * @see wp_insert_comment()
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array $commentdata {
- * Comment data.
- *
- * @type string $comment_author The name of the comment author.
- * @type string $comment_author_email The comment author email address.
- * @type string $comment_author_url The comment author URL.
- * @type string $comment_content The content of the comment.
- * @type string $comment_date The date the comment was submitted. Default is the current time.
- * @type string $comment_date_gmt The date the comment was submitted in the GMT timezone.
- * Default is `$comment_date` in the GMT timezone.
- * @type int $comment_parent The ID of this comment's parent, if any. Default 0.
- * @type int $comment_post_ID The ID of the post that relates to the comment.
- * @type int $user_id The ID of the user who submitted the comment. Default 0.
- * @type int $user_ID Kept for backward-compatibility. Use `$user_id` instead.
- * @type string $comment_agent Comment author user agent. Default is the value of 'HTTP_USER_AGENT'
- * in the `$_SERVER` superglobal sent in the original request.
- * @type string $comment_author_IP Comment author IP address in IPv4 format. Default is the value of
- * 'REMOTE_ADDR' in the `$_SERVER` superglobal sent in the original request.
- * }
- * @return int|false The ID of the comment on success, false on failure.
- */
-function wp_new_comment( $commentdata ) {
- global $wpdb;
-
- if ( isset( $commentdata['user_ID'] ) ) {
- $commentdata['user_id'] = $commentdata['user_ID'] = (int) $commentdata['user_ID'];
- }
-
- $prefiltered_user_id = ( isset( $commentdata['user_id'] ) ) ? (int) $commentdata['user_id'] : 0;
-
- /**
- * Filter a comment's data before it is sanitized and inserted into the database.
- *
- * @since 1.5.0
- *
- * @param array $commentdata Comment data.
- */
- $commentdata = apply_filters( 'preprocess_comment', $commentdata );
-
- $commentdata['comment_post_ID'] = (int) $commentdata['comment_post_ID'];
- if ( isset( $commentdata['user_ID'] ) && $prefiltered_user_id !== (int) $commentdata['user_ID'] ) {
- $commentdata['user_id'] = $commentdata['user_ID'] = (int) $commentdata['user_ID'];
- } elseif ( isset( $commentdata['user_id'] ) ) {
- $commentdata['user_id'] = (int) $commentdata['user_id'];
- }
-
- $commentdata['comment_parent'] = isset($commentdata['comment_parent']) ? absint($commentdata['comment_parent']) : 0;
- $parent_status = ( 0 < $commentdata['comment_parent'] ) ? wp_get_comment_status($commentdata['comment_parent']) : '';
- $commentdata['comment_parent'] = ( 'approved' == $parent_status || 'unapproved' == $parent_status ) ? $commentdata['comment_parent'] : 0;
-
- if ( ! isset( $commentdata['comment_author_IP'] ) ) {
- $commentdata['comment_author_IP'] = $_SERVER['REMOTE_ADDR'];
- }
- $commentdata['comment_author_IP'] = preg_replace( '/[^0-9a-fA-F:., ]/', '', $commentdata['comment_author_IP'] );
-
- if ( ! isset( $commentdata['comment_agent'] ) ) {
- $commentdata['comment_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT']: '';
- }
- $commentdata['comment_agent'] = substr( $commentdata['comment_agent'], 0, 254 );
-
- if ( empty( $commentdata['comment_date'] ) ) {
- $commentdata['comment_date'] = current_time('mysql');
- }
-
- if ( empty( $commentdata['comment_date_gmt'] ) ) {
- $commentdata['comment_date_gmt'] = current_time( 'mysql', 1 );
- }
-
- $commentdata = wp_filter_comment($commentdata);
-
- $commentdata['comment_approved'] = wp_allow_comment($commentdata);
-
- $comment_ID = wp_insert_comment($commentdata);
- if ( ! $comment_ID ) {
- $fields = array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content' );
-
- foreach ( $fields as $field ) {
- if ( isset( $commentdata[ $field ] ) ) {
- $commentdata[ $field ] = $wpdb->strip_invalid_text_for_column( $wpdb->comments, $field, $commentdata[ $field ] );
- }
- }
-
- $commentdata = wp_filter_comment( $commentdata );
-
- $commentdata['comment_approved'] = wp_allow_comment( $commentdata );
-
- $comment_ID = wp_insert_comment( $commentdata );
- if ( ! $comment_ID ) {
- return false;
- }
- }
-
- /**
- * Fires immediately after a comment is inserted into the database.
- *
- * @since 1.2.0
- *
- * @param int $comment_ID The comment ID.
- * @param int|string $comment_approved 1 if the comment is approved, 0 if not, 'spam' if spam.
- */
- do_action( 'comment_post', $comment_ID, $commentdata['comment_approved'] );
-
- return $comment_ID;
-}
-
-/**
- * Send a comment moderation notification to the comment moderator.
- *
- * @since 4.4.0
- *
- * @param int $comment_ID ID of the comment.
- * @return bool True on success, false on failure.
- */
-function wp_new_comment_notify_moderator( $comment_ID ) {
- $comment = get_comment( $comment_ID );
-
- // Only send notifications for pending comments.
- $maybe_notify = ( '0' == $comment->comment_approved );
-
- /** This filter is documented in wp-includes/comment-functions.php */
- $maybe_notify = apply_filters( 'notify_moderator', $maybe_notify, $comment_ID );
-
- if ( ! $maybe_notify ) {
- return false;
- }
-
- return wp_notify_moderator( $comment_ID );
-}
-
-/**
- * Send a notification of a new comment to the post author.
- *
- * @since 4.4.0
- *
- * Uses the {@see 'notify_post_author'} filter to determine whether the post author
- * should be notified when a new comment is added, overriding site setting.
- *
- * @param int $comment_ID Comment ID.
- * @return bool True on success, false on failure.
- */
-function wp_new_comment_notify_postauthor( $comment_ID ) {
- $comment = get_comment( $comment_ID );
-
- $maybe_notify = get_option( 'comments_notify' );
-
- /**
- * Filter whether to send the post author new comment notification emails,
- * overriding the site setting.
- *
- * @since 4.4.0
- *
- * @param bool $maybe_notify Whether to notify the post author about the new comment.
- * @param int $comment_ID The ID of the comment for the notification.
- */
- $maybe_notify = apply_filters( 'notify_post_author', $maybe_notify, $comment_ID );
-
- /*
- * wp_notify_postauthor() checks if notifying the author of their own comment.
- * By default, it won't, but filters can override this.
- */
- if ( ! $maybe_notify ) {
- return false;
- }
-
- // Only send notifications for approved comments.
- if ( ! isset( $comment->comment_approved ) || 'spam' === $comment->comment_approved || ! $comment->comment_approved ) {
- return false;
- }
-
- return wp_notify_postauthor( $comment_ID );
-}
-
-/**
- * Sets the status of a comment.
- *
- * The 'wp_set_comment_status' action is called after the comment is handled.
- * If the comment status is not in the list, then false is returned.
- *
- * @since 1.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
- * @param string $comment_status New comment status, either 'hold', 'approve', 'spam', or 'trash'.
- * @param bool $wp_error Whether to return a WP_Error object if there is a failure. Default is false.
- * @return bool|WP_Error True on success, false or WP_Error on failure.
- */
-function wp_set_comment_status($comment_id, $comment_status, $wp_error = false) {
- global $wpdb;
-
- switch ( $comment_status ) {
- case 'hold':
- case '0':
- $status = '0';
- break;
- case 'approve':
- case '1':
- $status = '1';
- add_action( 'wp_set_comment_status', 'wp_new_comment_notify_postauthor' );
- break;
- case 'spam':
- $status = 'spam';
- break;
- case 'trash':
- $status = 'trash';
- break;
- default:
- return false;
- }
-
- $comment_old = clone get_comment($comment_id);
-
- if ( !$wpdb->update( $wpdb->comments, array('comment_approved' => $status), array( 'comment_ID' => $comment_old->comment_ID ) ) ) {
- if ( $wp_error )
- return new WP_Error('db_update_error', __('Could not update comment status'), $wpdb->last_error);
- else
- return false;
- }
-
- clean_comment_cache( $comment_old->comment_ID );
-
- $comment = get_comment( $comment_old->comment_ID );
-
- /**
- * Fires immediately before transitioning a comment's status from one to another
- * in the database.
- *
- * @since 1.5.0
- *
- * @param int $comment_id Comment ID.
- * @param string|bool $comment_status Current comment status. Possible values include
- * 'hold', 'approve', 'spam', 'trash', or false.
- */
- do_action( 'wp_set_comment_status', $comment->comment_ID, $comment_status );
-
- wp_transition_comment_status($comment_status, $comment_old->comment_approved, $comment);
-
- wp_update_comment_count($comment->comment_post_ID);
-
- return true;
-}
-
-/**
- * Updates an existing comment in the database.
- *
- * Filters the comment and makes sure certain fields are valid before updating.
- *
- * @since 2.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array $commentarr Contains information on the comment.
- * @return int Comment was updated if value is 1, or was not updated if value is 0.
- */
-function wp_update_comment($commentarr) {
- global $wpdb;
-
- // First, get all of the original fields
- $comment = get_comment($commentarr['comment_ID'], ARRAY_A);
- if ( empty( $comment ) ) {
- return 0;
- }
-
- // Make sure that the comment post ID is valid (if specified).
- if ( isset( $commentarr['comment_post_ID'] ) && ! get_post( $commentarr['comment_post_ID'] ) ) {
- return 0;
- }
-
- // Escape data pulled from DB.
- $comment = wp_slash($comment);
-
- $old_status = $comment['comment_approved'];
-
- // Merge old and new fields with new fields overwriting old ones.
- $commentarr = array_merge($comment, $commentarr);
-
- $commentarr = wp_filter_comment( $commentarr );
-
- // Now extract the merged array.
- $data = wp_unslash( $commentarr );
-
- /**
- * Filter the comment content before it is updated in the database.
- *
- * @since 1.5.0
- *
- * @param string $comment_content The comment data.
- */
- $data['comment_content'] = apply_filters( 'comment_save_pre', $data['comment_content'] );
-
- $data['comment_date_gmt'] = get_gmt_from_date( $data['comment_date'] );
-
- if ( ! isset( $data['comment_approved'] ) ) {
- $data['comment_approved'] = 1;
- } elseif ( 'hold' == $data['comment_approved'] ) {
- $data['comment_approved'] = 0;
- } elseif ( 'approve' == $data['comment_approved'] ) {
- $data['comment_approved'] = 1;
- }
-
- $comment_ID = $data['comment_ID'];
- $comment_post_ID = $data['comment_post_ID'];
- $keys = array( 'comment_post_ID', 'comment_content', 'comment_author', 'comment_author_email', 'comment_approved', 'comment_karma', 'comment_author_url', 'comment_date', 'comment_date_gmt', 'comment_type', 'comment_parent', 'user_id' );
- $data = wp_array_slice_assoc( $data, $keys );
- $rval = $wpdb->update( $wpdb->comments, $data, compact( 'comment_ID' ) );
-
- clean_comment_cache( $comment_ID );
- wp_update_comment_count( $comment_post_ID );
- /**
- * Fires immediately after a comment is updated in the database.
- *
- * The hook also fires immediately before comment status transition hooks are fired.
- *
- * @since 1.2.0
- *
- * @param int $comment_ID The comment ID.
- */
- do_action( 'edit_comment', $comment_ID );
- $comment = get_comment($comment_ID);
- wp_transition_comment_status($comment->comment_approved, $old_status, $comment);
- return $rval;
-}
-
-/**
- * Whether to defer comment counting.
- *
- * When setting $defer to true, all post comment counts will not be updated
- * until $defer is set to false. When $defer is set to false, then all
- * previously deferred updated post comment counts will then be automatically
- * updated without having to call wp_update_comment_count() after.
- *
- * @since 2.5.0
- * @staticvar bool $_defer
- *
- * @param bool $defer
- * @return bool
- */
-function wp_defer_comment_counting($defer=null) {
- static $_defer = false;
-
- if ( is_bool($defer) ) {
- $_defer = $defer;
- // flush any deferred counts
- if ( !$defer )
- wp_update_comment_count( null, true );
- }
-
- return $_defer;
-}
-
-/**
- * Updates the comment count for post(s).
- *
- * When $do_deferred is false (is by default) and the comments have been set to
- * be deferred, the post_id will be added to a queue, which will be updated at a
- * later date and only updated once per post ID.
- *
- * If the comments have not be set up to be deferred, then the post will be
- * updated. When $do_deferred is set to true, then all previous deferred post
- * IDs will be updated along with the current $post_id.
- *
- * @since 2.1.0
- * @see wp_update_comment_count_now() For what could cause a false return value
- *
- * @staticvar array $_deferred
- *
- * @param int $post_id Post ID
- * @param bool $do_deferred Whether to process previously deferred post comment counts
- * @return bool|void True on success, false on failure
- */
-function wp_update_comment_count($post_id, $do_deferred=false) {
- static $_deferred = array();
-
- if ( $do_deferred ) {
- $_deferred = array_unique($_deferred);
- foreach ( $_deferred as $i => $_post_id ) {
- wp_update_comment_count_now($_post_id);
- unset( $_deferred[$i] ); /** @todo Move this outside of the foreach and reset $_deferred to an array instead */
- }
- }
-
- if ( wp_defer_comment_counting() ) {
- $_deferred[] = $post_id;
- return true;
- }
- elseif ( $post_id ) {
- return wp_update_comment_count_now($post_id);
- }
-
-}
-
-/**
- * Updates the comment count for the post.
- *
- * @since 2.5.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $post_id Post ID
- * @return bool True on success, false on '0' $post_id or if post with ID does not exist.
- */
-function wp_update_comment_count_now($post_id) {
- global $wpdb;
- $post_id = (int) $post_id;
- if ( !$post_id )
- return false;
-
- wp_cache_delete( 'comments-0', 'counts' );
- wp_cache_delete( "comments-{$post_id}", 'counts' );
-
- if ( !$post = get_post($post_id) )
- return false;
-
- $old = (int) $post->comment_count;
- $new = (int) $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1'", $post_id) );
- $wpdb->update( $wpdb->posts, array('comment_count' => $new), array('ID' => $post_id) );
-
- clean_post_cache( $post );
-
- /**
- * Fires immediately after a post's comment count is updated in the database.
- *
- * @since 2.3.0
- *
- * @param int $post_id Post ID.
- * @param int $new The new comment count.
- * @param int $old The old comment count.
- */
- do_action( 'wp_update_comment_count', $post_id, $new, $old );
- /** This action is documented in wp-includes/post-functions.php */
- do_action( 'edit_post', $post_id, $post );
-
- return true;
-}
-
-//
-// Ping and trackback functions.
-//
-
-/**
- * Finds a pingback server URI based on the given URL.
- *
- * Checks the HTML for the rel="pingback" link and x-pingback headers. It does
- * a check for the x-pingback headers first and returns that, if available. The
- * check for the rel="pingback" has more overhead than just the header.
- *
- * @since 1.5.0
- *
- * @param string $url URL to ping.
- * @param int $deprecated Not Used.
- * @return false|string False on failure, string containing URI on success.
- */
-function discover_pingback_server_uri( $url, $deprecated = '' ) {
- if ( !empty( $deprecated ) )
- _deprecated_argument( __FUNCTION__, '2.7' );
-
- $pingback_str_dquote = 'rel="pingback"';
- $pingback_str_squote = 'rel=\'pingback\'';
-
- /** @todo Should use Filter Extension or custom preg_match instead. */
- $parsed_url = parse_url($url);
-
- if ( ! isset( $parsed_url['host'] ) ) // Not an URL. This should never happen.
- return false;
-
- //Do not search for a pingback server on our own uploads
- $uploads_dir = wp_upload_dir();
- if ( 0 === strpos($url, $uploads_dir['baseurl']) )
- return false;
-
- $response = wp_safe_remote_head( $url, array( 'timeout' => 2, 'httpversion' => '1.0' ) );
-
- if ( is_wp_error( $response ) )
- return false;
-
- if ( wp_remote_retrieve_header( $response, 'x-pingback' ) )
- return wp_remote_retrieve_header( $response, 'x-pingback' );
-
- // Not an (x)html, sgml, or xml page, no use going further.
- if ( preg_match('#(image|audio|video|model)/#is', wp_remote_retrieve_header( $response, 'content-type' )) )
- return false;
-
- // Now do a GET since we're going to look in the html headers (and we're sure it's not a binary file)
- $response = wp_safe_remote_get( $url, array( 'timeout' => 2, 'httpversion' => '1.0' ) );
-
- if ( is_wp_error( $response ) )
- return false;
-
- $contents = wp_remote_retrieve_body( $response );
-
- $pingback_link_offset_dquote = strpos($contents, $pingback_str_dquote);
- $pingback_link_offset_squote = strpos($contents, $pingback_str_squote);
- if ( $pingback_link_offset_dquote || $pingback_link_offset_squote ) {
- $quote = ($pingback_link_offset_dquote) ? '"' : '\'';
- $pingback_link_offset = ($quote=='"') ? $pingback_link_offset_dquote : $pingback_link_offset_squote;
- $pingback_href_pos = @strpos($contents, 'href=', $pingback_link_offset);
- $pingback_href_start = $pingback_href_pos+6;
- $pingback_href_end = @strpos($contents, $quote, $pingback_href_start);
- $pingback_server_url_len = $pingback_href_end - $pingback_href_start;
- $pingback_server_url = substr($contents, $pingback_href_start, $pingback_server_url_len);
-
- // We may find rel="pingback" but an incomplete pingback URL
- if ( $pingback_server_url_len > 0 ) { // We got it!
- return $pingback_server_url;
- }
- }
-
- return false;
-}
-
-/**
- * Perform all pingbacks, enclosures, trackbacks, and send to pingback services.
- *
- * @since 2.1.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- */
-function do_all_pings() {
- global $wpdb;
-
- // Do pingbacks
- while ($ping = $wpdb->get_row("SELECT ID, post_content, meta_id FROM {$wpdb->posts}, {$wpdb->postmeta} WHERE {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_pingme' LIMIT 1")) {
- delete_metadata_by_mid( 'post', $ping->meta_id );
- pingback( $ping->post_content, $ping->ID );
- }
-
- // Do Enclosures
- while ($enclosure = $wpdb->get_row("SELECT ID, post_content, meta_id FROM {$wpdb->posts}, {$wpdb->postmeta} WHERE {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_encloseme' LIMIT 1")) {
- delete_metadata_by_mid( 'post', $enclosure->meta_id );
- do_enclose( $enclosure->post_content, $enclosure->ID );
- }
-
- // Do Trackbacks
- $trackbacks = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE to_ping <> '' AND post_status = 'publish'");
- if ( is_array($trackbacks) )
- foreach ( $trackbacks as $trackback )
- do_trackbacks($trackback);
-
- //Do Update Services/Generic Pings
- generic_ping();
-}
-
-/**
- * Perform trackbacks.
- *
- * @since 1.5.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $post_id Post ID to do trackbacks on.
- */
-function do_trackbacks($post_id) {
- global $wpdb;
-
- $post = get_post( $post_id );
- $to_ping = get_to_ping($post_id);
- $pinged = get_pung($post_id);
- if ( empty($to_ping) ) {
- $wpdb->update($wpdb->posts, array('to_ping' => ''), array('ID' => $post_id) );
- return;
- }
-
- if ( empty($post->post_excerpt) ) {
- /** This filter is documented in wp-includes/post-template.php */
- $excerpt = apply_filters( 'the_content', $post->post_content, $post->ID );
- } else {
- /** This filter is documented in wp-includes/post-template.php */
- $excerpt = apply_filters( 'the_excerpt', $post->post_excerpt );
- }
-
- $excerpt = str_replace(']]>', ']]>', $excerpt);
- $excerpt = wp_html_excerpt($excerpt, 252, '…');
-
- /** This filter is documented in wp-includes/post-template.php */
- $post_title = apply_filters( 'the_title', $post->post_title, $post->ID );
- $post_title = strip_tags($post_title);
-
- if ( $to_ping ) {
- foreach ( (array) $to_ping as $tb_ping ) {
- $tb_ping = trim($tb_ping);
- if ( !in_array($tb_ping, $pinged) ) {
- trackback($tb_ping, $post_title, $excerpt, $post_id);
- $pinged[] = $tb_ping;
- } else {
- $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, '')) WHERE ID = %d", $tb_ping, $post_id) );
- }
- }
- }
-}
-
-/**
- * Sends pings to all of the ping site services.
- *
- * @since 1.2.0
- *
- * @param int $post_id Post ID.
- * @return int Same as Post ID from parameter
- */
-function generic_ping( $post_id = 0 ) {
- $services = get_option('ping_sites');
-
- $services = explode("\n", $services);
- foreach ( (array) $services as $service ) {
- $service = trim($service);
- if ( '' != $service )
- weblog_ping($service);
- }
-
- return $post_id;
-}
-
-/**
- * Pings back the links found in a post.
- *
- * @since 0.71
- *
- * @global string $wp_version
- *
- * @param string $content Post content to check for links.
- * @param int $post_ID Post ID.
- */
-function pingback($content, $post_ID) {
- global $wp_version;
- include_once(ABSPATH . WPINC . '/class-IXR.php');
- include_once(ABSPATH . WPINC . '/class-wp-http-ixr-client.php');
-
- // original code by Mort (http://mort.mine.nu:8080)
- $post_links = array();
-
- $pung = get_pung($post_ID);
-
- // Step 1
- // Parsing the post, external links (if any) are stored in the $post_links array
- $post_links_temp = wp_extract_urls( $content );
-
- // Step 2.
- // Walking thru the links array
- // first we get rid of links pointing to sites, not to specific files
- // Example:
- // http://dummy-weblog.org
- // http://dummy-weblog.org/
- // http://dummy-weblog.org/post.php
- // We don't wanna ping first and second types, even if they have a valid <link/>
-
- foreach ( (array) $post_links_temp as $link_test ) :
- if ( !in_array($link_test, $pung) && (url_to_postid($link_test) != $post_ID) // If we haven't pung it already and it isn't a link to itself
- && !is_local_attachment($link_test) ) : // Also, let's never ping local attachments.
- if ( $test = @parse_url($link_test) ) {
- if ( isset($test['query']) )
- $post_links[] = $link_test;
- elseif ( isset( $test['path'] ) && ( $test['path'] != '/' ) && ( $test['path'] != '' ) )
- $post_links[] = $link_test;
- }
- endif;
- endforeach;
-
- $post_links = array_unique( $post_links );
- /**
- * Fires just before pinging back links found in a post.
- *
- * @since 2.0.0
- *
- * @param array &$post_links An array of post links to be checked, passed by reference.
- * @param array &$pung Whether a link has already been pinged, passed by reference.
- * @param int $post_ID The post ID.
- */
- do_action_ref_array( 'pre_ping', array( &$post_links, &$pung, $post_ID ) );
-
- foreach ( (array) $post_links as $pagelinkedto ) {
- $pingback_server_url = discover_pingback_server_uri( $pagelinkedto );
-
- if ( $pingback_server_url ) {
- @ set_time_limit( 60 );
- // Now, the RPC call
- $pagelinkedfrom = get_permalink($post_ID);
-
- // using a timeout of 3 seconds should be enough to cover slow servers
- $client = new WP_HTTP_IXR_Client($pingback_server_url);
- $client->timeout = 3;
- /**
- * Filter the user agent sent when pinging-back a URL.
- *
- * @since 2.9.0
- *
- * @param string $concat_useragent The user agent concatenated with ' -- WordPress/'
- * and the WordPress version.
- * @param string $useragent The useragent.
- * @param string $pingback_server_url The server URL being linked to.
- * @param string $pagelinkedto URL of page linked to.
- * @param string $pagelinkedfrom URL of page linked from.
- */
- $client->useragent = apply_filters( 'pingback_useragent', $client->useragent . ' -- WordPress/' . $wp_version, $client->useragent, $pingback_server_url, $pagelinkedto, $pagelinkedfrom );
- // when set to true, this outputs debug messages by itself
- $client->debug = false;
-
- if ( $client->query('pingback.ping', $pagelinkedfrom, $pagelinkedto) || ( isset($client->error->code) && 48 == $client->error->code ) ) // Already registered
- add_ping( $post_ID, $pagelinkedto );
- }
- }
-}
-
-/**
- * Check whether blog is public before returning sites.
- *
- * @since 2.1.0
- *
- * @param mixed $sites Will return if blog is public, will not return if not public.
- * @return mixed Empty string if blog is not public, returns $sites, if site is public.
- */
-function privacy_ping_filter($sites) {
- if ( '0' != get_option('blog_public') )
- return $sites;
- else
- return '';
-}
-
-/**
- * Send a Trackback.
- *
- * Updates database when sending trackback to prevent duplicates.
- *
- * @since 0.71
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $trackback_url URL to send trackbacks.
- * @param string $title Title of post.
- * @param string $excerpt Excerpt of post.
- * @param int $ID Post ID.
- * @return int|false|void Database query from update.
- */
-function trackback($trackback_url, $title, $excerpt, $ID) {
- global $wpdb;
-
- if ( empty($trackback_url) )
- return;
-
- $options = array();
- $options['timeout'] = 10;
- $options['body'] = array(
- 'title' => $title,
- 'url' => get_permalink($ID),
- 'blog_name' => get_option('blogname'),
- 'excerpt' => $excerpt
- );
-
- $response = wp_safe_remote_post( $trackback_url, $options );
-
- if ( is_wp_error( $response ) )
- return;
-
- $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET pinged = CONCAT(pinged, '\n', %s) WHERE ID = %d", $trackback_url, $ID) );
- return $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, '')) WHERE ID = %d", $trackback_url, $ID) );
-}
-
-/**
- * Send a pingback.
- *
- * @since 1.2.0
- *
- * @global string $wp_version
- *
- * @param string $server Host of blog to connect to.
- * @param string $path Path to send the ping.
- */
-function weblog_ping($server = '', $path = '') {
- global $wp_version;
- include_once(ABSPATH . WPINC . '/class-IXR.php');
- include_once(ABSPATH . WPINC . '/class-wp-http-ixr-client.php');
-
- // using a timeout of 3 seconds should be enough to cover slow servers
- $client = new WP_HTTP_IXR_Client($server, ((!strlen(trim($path)) || ('/' == $path)) ? false : $path));
- $client->timeout = 3;
- $client->useragent .= ' -- WordPress/'.$wp_version;
-
- // when set to true, this outputs debug messages by itself
- $client->debug = false;
- $home = trailingslashit( home_url() );
- if ( !$client->query('weblogUpdates.extendedPing', get_option('blogname'), $home, get_bloginfo('rss2_url') ) ) // then try a normal ping
- $client->query('weblogUpdates.ping', get_option('blogname'), $home);
-}
-
-/**
- * Default filter attached to pingback_ping_source_uri to validate the pingback's Source URI
- *
- * @since 3.5.1
- * @see wp_http_validate_url()
- *
- * @param string $source_uri
- * @return string
- */
-function pingback_ping_source_uri( $source_uri ) {
- return (string) wp_http_validate_url( $source_uri );
-}
-
-/**
- * Default filter attached to xmlrpc_pingback_error.
- *
- * Returns a generic pingback error code unless the error code is 48,
- * which reports that the pingback is already registered.
- *
- * @since 3.5.1
- * @link http://www.hixie.ch/specs/pingback/pingback#TOC3
- *
- * @param IXR_Error $ixr_error
- * @return IXR_Error
- */
-function xmlrpc_pingback_error( $ixr_error ) {
- if ( $ixr_error->code === 48 )
- return $ixr_error;
- return new IXR_Error( 0, '' );
-}
-
-//
-// Cache
-//
-
-/**
- * Removes comment ID from the comment cache.
- *
- * @since 2.3.0
- *
- * @param int|array $ids Comment ID or array of comment IDs to remove from cache
- */
-function clean_comment_cache($ids) {
- foreach ( (array) $ids as $id ) {
- wp_cache_delete( $id, 'comment' );
- }
-
- wp_cache_set( 'last_changed', microtime(), 'comment' );
-}
-
-/**
- * Updates the comment cache of given comments.
- *
- * Will add the comments in $comments to the cache. If comment ID already exists
- * in the comment cache then it will not be updated. The comment is added to the
- * cache using the comment group with the key using the ID of the comments.
- *
- * @since 2.3.0
- * @since 4.4.0 Introduced the `$update_meta_cache` parameter.
- *
- * @param array $comments Array of comment row objects
- * @param bool $update_meta_cache Whether to update commentmeta cache. Default true.
- */
-function update_comment_cache( $comments, $update_meta_cache = true ) {
- foreach ( (array) $comments as $comment )
- wp_cache_add($comment->comment_ID, $comment, 'comment');
-
- if ( $update_meta_cache ) {
- // Avoid `wp_list_pluck()` in case `$comments` is passed by reference.
- $comment_ids = array();
- foreach ( $comments as $comment ) {
- $comment_ids[] = $comment->comment_ID;
- }
- update_meta_cache( 'comment', $comment_ids );
- }
-}
-
-/**
- * Adds any comments from the given IDs to the cache that do not already exist in cache.
- *
- * @since 4.4.0
- * @access private
- *
- * @see update_comment_cache()
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array $comment_ids Array of comment IDs.
- * @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
- */
-function _prime_comment_caches( $comment_ids, $update_meta_cache = true ) {
- global $wpdb;
-
- $non_cached_ids = _get_non_cached_ids( $comment_ids, 'comment' );
- if ( !empty( $non_cached_ids ) ) {
- $fresh_comments = $wpdb->get_results( sprintf( "SELECT $wpdb->comments.* FROM $wpdb->comments WHERE comment_ID IN (%s)", join( ",", array_map( 'intval', $non_cached_ids ) ) ) );
-
- update_comment_cache( $fresh_comments, $update_meta_cache );
- }
-}
-
-//
-// Internal
-//
-
-/**
- * Close comments on old posts on the fly, without any extra DB queries. Hooked to the_posts.
- *
- * @access private
- * @since 2.7.0
- *
- * @param WP_Post $posts Post data object.
- * @param WP_Query $query Query object.
- * @return array
- */
-function _close_comments_for_old_posts( $posts, $query ) {
- if ( empty( $posts ) || ! $query->is_singular() || ! get_option( 'close_comments_for_old_posts' ) )
- return $posts;
-
- /**
- * Filter the list of post types to automatically close comments for.
- *
- * @since 3.2.0
- *
- * @param array $post_types An array of registered post types. Default array with 'post'.
- */
- $post_types = apply_filters( 'close_comments_for_post_types', array( 'post' ) );
- if ( ! in_array( $posts[0]->post_type, $post_types ) )
- return $posts;
-
- $days_old = (int) get_option( 'close_comments_days_old' );
- if ( ! $days_old )
- return $posts;
-
- if ( time() - strtotime( $posts[0]->post_date_gmt ) > ( $days_old * DAY_IN_SECONDS ) ) {
- $posts[0]->comment_status = 'closed';
- $posts[0]->ping_status = 'closed';
- }
-
- return $posts;
-}
-
-/**
- * Close comments on an old post. Hooked to comments_open and pings_open.
- *
- * @access private
- * @since 2.7.0
- *
- * @param bool $open Comments open or closed
- * @param int $post_id Post ID
- * @return bool $open
- */
-function _close_comments_for_old_post( $open, $post_id ) {
- if ( ! $open )
- return $open;
-
- if ( !get_option('close_comments_for_old_posts') )
- return $open;
-
- $days_old = (int) get_option('close_comments_days_old');
- if ( !$days_old )
- return $open;
-
- $post = get_post($post_id);
-
- /** This filter is documented in wp-includes/comment-functions.php */
- $post_types = apply_filters( 'close_comments_for_post_types', array( 'post' ) );
- if ( ! in_array( $post->post_type, $post_types ) )
- return $open;
-
- // Undated drafts should not show up as comments closed.
- if ( '0000-00-00 00:00:00' === $post->post_date_gmt ) {
- return $open;
- }
-
- if ( time() - strtotime( $post->post_date_gmt ) > ( $days_old * DAY_IN_SECONDS ) )
- return false;
-
- return $open;
-}
-
-/**
- * Handles the submission of a comment, usually posted to wp-comments-post.php via a comment form.
- *
- * This function expects unslashed data, as opposed to functions such as `wp_new_comment()` which
- * expect slashed data.
- *
- * @since 4.4.0
- *
- * @param array $comment_data {
- * Comment data.
- *
- * @type string|int $comment_post_ID The ID of the post that relates to the comment.
- * @type string $author The name of the comment author.
- * @type string $email The comment author email address.
- * @type string $url The comment author URL.
- * @type string $comment The content of the comment.
- * @type string|int $comment_parent The ID of this comment's parent, if any. Default 0.
- * @type string $_wp_unfiltered_html_comment The nonce value for allowing unfiltered HTML.
- * }
- * @return WP_Comment|WP_Error A WP_Comment object on success, a WP_Error object on failure.
- */
-function wp_handle_comment_submission( $comment_data ) {
-
- $comment_post_ID = $comment_parent = 0;
- $comment_author = $comment_author_email = $comment_author_url = $comment_content = $_wp_unfiltered_html_comment = null;
-
- if ( isset( $comment_data['comment_post_ID'] ) ) {
- $comment_post_ID = (int) $comment_data['comment_post_ID'];
- }
- if ( isset( $comment_data['author'] ) && is_string( $comment_data['author'] ) ) {
- $comment_author = trim( strip_tags( $comment_data['author'] ) );
- }
- if ( isset( $comment_data['email'] ) && is_string( $comment_data['email'] ) ) {
- $comment_author_email = trim( $comment_data['email'] );
- }
- if ( isset( $comment_data['url'] ) && is_string( $comment_data['url'] ) ) {
- $comment_author_url = trim( $comment_data['url'] );
- }
- if ( isset( $comment_data['comment'] ) && is_string( $comment_data['comment'] ) ) {
- $comment_content = trim( $comment_data['comment'] );
- }
- if ( isset( $comment_data['comment_parent'] ) ) {
- $comment_parent = absint( $comment_data['comment_parent'] );
- }
- if ( isset( $comment_data['_wp_unfiltered_html_comment'] ) && is_string( $comment_data['_wp_unfiltered_html_comment'] ) ) {
- $_wp_unfiltered_html_comment = trim( $comment_data['_wp_unfiltered_html_comment'] );
- }
-
- $post = get_post( $comment_post_ID );
-
- if ( empty( $post->comment_status ) ) {
-
- /**
- * Fires when a comment is attempted on a post that does not exist.
- *
- * @since 1.5.0
- *
- * @param int $comment_post_ID Post ID.
- */
- do_action( 'comment_id_not_found', $comment_post_ID );
-
- return new WP_Error( 'comment_id_not_found' );
-
- }
-
- // get_post_status() will get the parent status for attachments.
- $status = get_post_status( $post );
-
- $status_obj = get_post_status_object( $status );
-
- if ( ! comments_open( $comment_post_ID ) ) {
-
- /**
- * Fires when a comment is attempted on a post that has comments closed.
- *
- * @since 1.5.0
- *
- * @param int $comment_post_ID Post ID.
- */
- do_action( 'comment_closed', $comment_post_ID );
-
- return new WP_Error( 'comment_closed', __( 'Sorry, comments are closed for this item.' ), 403 );
-
- } elseif ( 'trash' == $status ) {
-
- /**
- * Fires when a comment is attempted on a trashed post.
- *
- * @since 2.9.0
- *
- * @param int $comment_post_ID Post ID.
- */
- do_action( 'comment_on_trash', $comment_post_ID );
-
- return new WP_Error( 'comment_on_trash' );
-
- } elseif ( ! $status_obj->public && ! $status_obj->private ) {
-
- /**
- * Fires when a comment is attempted on a post in draft mode.
- *
- * @since 1.5.1
- *
- * @param int $comment_post_ID Post ID.
- */
- do_action( 'comment_on_draft', $comment_post_ID );
-
- return new WP_Error( 'comment_on_draft' );
-
- } elseif ( post_password_required( $comment_post_ID ) ) {
-
- /**
- * Fires when a comment is attempted on a password-protected post.
- *
- * @since 2.9.0
- *
- * @param int $comment_post_ID Post ID.
- */
- do_action( 'comment_on_password_protected', $comment_post_ID );
-
- return new WP_Error( 'comment_on_password_protected' );
-
- } else {
-
- /**
- * Fires before a comment is posted.
- *
- * @since 2.8.0
- *
- * @param int $comment_post_ID Post ID.
- */
- do_action( 'pre_comment_on_post', $comment_post_ID );
-
- }
-
- // If the user is logged in
- $user = wp_get_current_user();
- if ( $user->exists() ) {
- if ( empty( $user->display_name ) ) {
- $user->display_name=$user->user_login;
- }
- $comment_author = $user->display_name;
- $comment_author_email = $user->user_email;
- $comment_author_url = $user->user_url;
- $user_id = $user->ID;
- if ( current_user_can( 'unfiltered_html' ) ) {
- if ( ! isset( $comment_data['_wp_unfiltered_html_comment'] )
- || ! wp_verify_nonce( $comment_data['_wp_unfiltered_html_comment'], 'unfiltered-html-comment_' . $comment_post_ID )
- ) {
- kses_remove_filters(); // start with a clean slate
- kses_init_filters(); // set up the filters
- }
- }
- } else {
- if ( get_option( 'comment_registration' ) || 'private' == $status ) {
- return new WP_Error( 'not_logged_in', __( 'Sorry, you must be logged in to post a comment.' ), 403 );
- }
- }
-
- $comment_type = '';
-
- if ( get_option( 'require_name_email' ) && ! $user->exists() ) {
- if ( 6 > strlen( $comment_author_email ) || '' == $comment_author ) {
- return new WP_Error( 'require_name_email', __( '<strong>ERROR</strong>: please fill the required fields (name, email).' ), 200 );
- } elseif ( ! is_email( $comment_author_email ) ) {
- return new WP_Error( 'require_valid_email', __( '<strong>ERROR</strong>: please enter a valid email address.' ), 200 );
- }
- }
-
- if ( '' == $comment_content ) {
- return new WP_Error( 'require_valid_comment', __( '<strong>ERROR</strong>: please type a comment.' ), 200 );
- }
-
- $commentdata = compact(
- 'comment_post_ID',
- 'comment_author',
- 'comment_author_email',
- 'comment_author_url',
- 'comment_content',
- 'comment_type',
- 'comment_parent',
- 'user_id'
- );
-
- $comment_id = wp_new_comment( wp_slash( $commentdata ) );
- if ( ! $comment_id ) {
- return new WP_Error( 'comment_save_error', __( '<strong>ERROR</strong>: The comment could not be saved. Please try again later.' ), 500 );
- }
-
- return get_comment( $comment_id );
-
-}
</del></span></pre></div>
<a id="trunksrcwpincludescommentphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/comment.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/comment.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/comment.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,20 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Core Comment API
- *
- * @package WordPress
- * @subpackage Comment
- * @since 1.5.0
- */
-
-/** WP_Comment class */
-require_once( ABSPATH . WPINC . '/class-wp-comment.php' );
-
-/** WP_Comment_Query class */
-require_once( ABSPATH . WPINC . '/class-wp-comment-query.php' );
-
-/** Walker_Comment class */
-require_once( ABSPATH . WPINC . '/class-walker-comment.php' );
-
-/** Core comments functionality */
-require_once( ABSPATH . WPINC . '/comment-functions.php' );
</del></span></pre></div>
<a id="trunksrcwpincludescommentphpfromrev35712trunksrcwpincludescommentfunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/comment.php (from rev 35712, trunk/src/wp-includes/comment-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/comment.php (rev 0)
+++ trunk/src/wp-includes/comment.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,2796 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Core Comment API
+ *
+ * @package WordPress
+ * @subpackage Comment
+ */
+
+/**
+ * Check whether a comment passes internal checks to be allowed to add.
+ *
+ * If manual comment moderation is set in the administration, then all checks,
+ * regardless of their type and whitelist, will fail and the function will
+ * return false.
+ *
+ * If the number of links exceeds the amount in the administration, then the
+ * check fails. If any of the parameter contents match the blacklist of words,
+ * then the check fails.
+ *
+ * If the comment author was approved before, then the comment is automatically
+ * whitelisted.
+ *
+ * If all checks pass, the function will return true.
+ *
+ * @since 1.2.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $author Comment author name.
+ * @param string $email Comment author email.
+ * @param string $url Comment author URL.
+ * @param string $comment Content of the comment.
+ * @param string $user_ip Comment author IP address.
+ * @param string $user_agent Comment author User-Agent.
+ * @param string $comment_type Comment type, either user-submitted comment,
+ * trackback, or pingback.
+ * @return bool If all checks pass, true, otherwise false.
+ */
+function check_comment($author, $email, $url, $comment, $user_ip, $user_agent, $comment_type) {
+ global $wpdb;
+
+ // If manual moderation is enabled, skip all checks and return false.
+ if ( 1 == get_option('comment_moderation') )
+ return false;
+
+ /** This filter is documented in wp-includes/comment-template.php */
+ $comment = apply_filters( 'comment_text', $comment );
+
+ // Check for the number of external links if a max allowed number is set.
+ if ( $max_links = get_option( 'comment_max_links' ) ) {
+ $num_links = preg_match_all( '/<a [^>]*href/i', $comment, $out );
+
+ /**
+ * Filter the maximum number of links allowed in a comment.
+ *
+ * @since 3.0.0
+ *
+ * @param int $num_links The number of links allowed.
+ * @param string $url Comment author's URL. Included in allowed links total.
+ */
+ $num_links = apply_filters( 'comment_max_links_url', $num_links, $url );
+
+ /*
+ * If the number of links in the comment exceeds the allowed amount,
+ * fail the check by returning false.
+ */
+ if ( $num_links >= $max_links )
+ return false;
+ }
+
+ $mod_keys = trim(get_option('moderation_keys'));
+
+ // If moderation 'keys' (keywords) are set, process them.
+ if ( !empty($mod_keys) ) {
+ $words = explode("\n", $mod_keys );
+
+ foreach ( (array) $words as $word) {
+ $word = trim($word);
+
+ // Skip empty lines.
+ if ( empty($word) )
+ continue;
+
+ /*
+ * Do some escaping magic so that '#' (number of) characters in the spam
+ * words don't break things:
+ */
+ $word = preg_quote($word, '#');
+
+ /*
+ * Check the comment fields for moderation keywords. If any are found,
+ * fail the check for the given field by returning false.
+ */
+ $pattern = "#$word#i";
+ if ( preg_match($pattern, $author) ) return false;
+ if ( preg_match($pattern, $email) ) return false;
+ if ( preg_match($pattern, $url) ) return false;
+ if ( preg_match($pattern, $comment) ) return false;
+ if ( preg_match($pattern, $user_ip) ) return false;
+ if ( preg_match($pattern, $user_agent) ) return false;
+ }
+ }
+
+ /*
+ * Check if the option to approve comments by previously-approved authors is enabled.
+ *
+ * If it is enabled, check whether the comment author has a previously-approved comment,
+ * as well as whether there are any moderation keywords (if set) present in the author
+ * email address. If both checks pass, return true. Otherwise, return false.
+ */
+ if ( 1 == get_option('comment_whitelist')) {
+ if ( 'trackback' != $comment_type && 'pingback' != $comment_type && $author != '' && $email != '' ) {
+ // expected_slashed ($author, $email)
+ $ok_to_comment = $wpdb->get_var("SELECT comment_approved FROM $wpdb->comments WHERE comment_author = '$author' AND comment_author_email = '$email' and comment_approved = '1' LIMIT 1");
+ if ( ( 1 == $ok_to_comment ) &&
+ ( empty($mod_keys) || false === strpos( $email, $mod_keys) ) )
+ return true;
+ else
+ return false;
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Retrieve the approved comments for post $post_id.
+ *
+ * @since 2.0.0
+ * @since 4.1.0 Refactored to leverage {@see WP_Comment_Query} over a direct query.
+ *
+ * @param int $post_id The ID of the post.
+ * @param array $args Optional. See {@see WP_Comment_Query::query()} for information
+ * on accepted arguments.
+ * @return int|array $comments The approved comments, or number of comments if `$count`
+ * argument is true.
+ */
+function get_approved_comments( $post_id, $args = array() ) {
+ if ( ! $post_id ) {
+ return array();
+ }
+
+ $defaults = array(
+ 'status' => 1,
+ 'post_id' => $post_id,
+ 'order' => 'ASC',
+ );
+ $r = wp_parse_args( $args, $defaults );
+
+ $query = new WP_Comment_Query;
+ return $query->query( $r );
+}
+
+/**
+ * Retrieves comment data given a comment ID or comment object.
+ *
+ * If an object is passed then the comment data will be cached and then returned
+ * after being passed through a filter. If the comment is empty, then the global
+ * comment variable will be used, if it is set.
+ *
+ * @since 2.0.0
+ *
+ * @global WP_Comment $comment
+ *
+ * @param WP_Comment|string|int $comment Comment to retrieve.
+ * @param string $output Optional. OBJECT or ARRAY_A or ARRAY_N constants.
+ * @return WP_Comment|array|null Depends on $output value.
+ */
+function get_comment( &$comment = null, $output = OBJECT ) {
+ if ( empty( $comment ) && isset( $GLOBALS['comment'] ) ) {
+ $comment = $GLOBALS['comment'];
+ }
+
+ if ( $comment instanceof WP_Comment ) {
+ $_comment = $comment;
+ } elseif ( is_object( $comment ) ) {
+ $_comment = new WP_Comment( $comment );
+ } else {
+ $_comment = WP_Comment::get_instance( $comment );
+ }
+
+ if ( ! $_comment ) {
+ return null;
+ }
+
+ /**
+ * Fires after a comment is retrieved.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $_comment Comment data.
+ */
+ $_comment = apply_filters( 'get_comment', $_comment );
+
+ if ( $output == OBJECT ) {
+ return $_comment;
+ } elseif ( $output == ARRAY_A ) {
+ return $_comment->to_array();
+ } elseif ( $output == ARRAY_N ) {
+ return array_values( $_comment->to_array() );
+ }
+ return $_comment;
+}
+
+/**
+ * Retrieve a list of comments.
+ *
+ * The comment list can be for the blog as a whole or for an individual post.
+ *
+ * @since 2.7.0
+ *
+ * @param string|array $args Optional. Array or string of arguments. See {@see WP_Comment_Query::parse_query()}
+ * for information on accepted arguments. Default empty.
+ * @return int|array List of comments or number of found comments if `$count` argument is true.
+ */
+function get_comments( $args = '' ) {
+ $query = new WP_Comment_Query;
+ return $query->query( $args );
+}
+
+/**
+ * Retrieve all of the WordPress supported comment statuses.
+ *
+ * Comments have a limited set of valid status values, this provides the comment
+ * status values and descriptions.
+ *
+ * @since 2.7.0
+ *
+ * @return array List of comment statuses.
+ */
+function get_comment_statuses() {
+ $status = array(
+ 'hold' => __('Unapproved'),
+ /* translators: comment status */
+ 'approve' => _x('Approved', 'adjective'),
+ /* translators: comment status */
+ 'spam' => _x('Spam', 'adjective'),
+ /* translators: comment status */
+ 'trash' => _x('Trash', 'adjective'),
+ );
+
+ return $status;
+}
+
+/**
+ * Gets the default comment status for a post type.
+ *
+ * @since 4.3.0
+ *
+ * @param string $post_type Optional. Post type. Default 'post'.
+ * @param string $comment_type Optional. Comment type. Default 'comment'.
+ * @return string Expected return value is 'open' or 'closed'.
+ */
+function get_default_comment_status( $post_type = 'post', $comment_type = 'comment' ) {
+ switch ( $comment_type ) {
+ case 'pingback' :
+ case 'trackback' :
+ $supports = 'trackbacks';
+ $option = 'ping';
+ break;
+ default :
+ $supports = 'comments';
+ $option = 'comment';
+ }
+
+ // Set the status.
+ if ( 'page' === $post_type ) {
+ $status = 'closed';
+ } elseif ( post_type_supports( $post_type, $supports ) ) {
+ $status = get_option( "default_{$option}_status" );
+ } else {
+ $status = 'closed';
+ }
+
+ /**
+ * Filter the default comment status for the given post type.
+ *
+ * @since 4.3.0
+ *
+ * @param string $status Default status for the given post type,
+ * either 'open' or 'closed'.
+ * @param string $post_type Post type. Default is `post`.
+ * @param string $comment_type Type of comment. Default is `comment`.
+ */
+ return apply_filters( 'get_default_comment_status' , $status, $post_type, $comment_type );
+}
+
+/**
+ * The date the last comment was modified.
+ *
+ * @since 1.5.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ * @staticvar array $cache_lastcommentmodified
+ *
+ * @param string $timezone Which timezone to use in reference to 'gmt', 'blog',
+ * or 'server' locations.
+ * @return string Last comment modified date.
+ */
+function get_lastcommentmodified($timezone = 'server') {
+ global $wpdb;
+ static $cache_lastcommentmodified = array();
+
+ if ( isset($cache_lastcommentmodified[$timezone]) )
+ return $cache_lastcommentmodified[$timezone];
+
+ $add_seconds_server = date('Z');
+
+ switch ( strtolower($timezone)) {
+ case 'gmt':
+ $lastcommentmodified = $wpdb->get_var("SELECT comment_date_gmt FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1");
+ break;
+ case 'blog':
+ $lastcommentmodified = $wpdb->get_var("SELECT comment_date FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1");
+ break;
+ case 'server':
+ $lastcommentmodified = $wpdb->get_var($wpdb->prepare("SELECT DATE_ADD(comment_date_gmt, INTERVAL %s SECOND) FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1", $add_seconds_server));
+ break;
+ }
+
+ $cache_lastcommentmodified[$timezone] = $lastcommentmodified;
+
+ return $lastcommentmodified;
+}
+
+/**
+ * The amount of comments in a post or total comments.
+ *
+ * A lot like {@link wp_count_comments()}, in that they both return comment
+ * stats (albeit with different types). The {@link wp_count_comments()} actual
+ * caches, but this function does not.
+ *
+ * @since 2.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $post_id Optional. Comment amount in post if > 0, else total comments blog wide.
+ * @return array The amount of spam, approved, awaiting moderation, and total comments.
+ */
+function get_comment_count( $post_id = 0 ) {
+ global $wpdb;
+
+ $post_id = (int) $post_id;
+
+ $where = '';
+ if ( $post_id > 0 ) {
+ $where = $wpdb->prepare("WHERE comment_post_ID = %d", $post_id);
+ }
+
+ $totals = (array) $wpdb->get_results("
+ SELECT comment_approved, COUNT( * ) AS total
+ FROM {$wpdb->comments}
+ {$where}
+ GROUP BY comment_approved
+ ", ARRAY_A);
+
+ $comment_count = array(
+ 'approved' => 0,
+ 'awaiting_moderation' => 0,
+ 'spam' => 0,
+ 'trash' => 0,
+ 'post-trashed' => 0,
+ 'total_comments' => 0,
+ 'all' => 0,
+ );
+
+ foreach ( $totals as $row ) {
+ switch ( $row['comment_approved'] ) {
+ case 'trash':
+ $comment_count['trash'] = $row['total'];
+ break;
+ case 'post-trashed':
+ $comment_count['post-trashed'] = $row['total'];
+ break;
+ case 'spam':
+ $comment_count['spam'] = $row['total'];
+ $comment_count['total_comments'] += $row['total'];
+ break;
+ case '1':
+ $comment_count['approved'] = $row['total'];
+ $comment_count['total_comments'] += $row['total'];
+ $comment_count['all'] += $row['total'];
+ break;
+ case '0':
+ $comment_count['awaiting_moderation'] = $row['total'];
+ $comment_count['total_comments'] += $row['total'];
+ $comment_count['all'] += $row['total'];
+ break;
+ default:
+ break;
+ }
+ }
+
+ return $comment_count;
+}
+
+//
+// Comment meta functions
+//
+
+/**
+ * Add meta data field to a comment.
+ *
+ * @since 2.9.0
+ * @link https://codex.wordpress.org/Function_Reference/add_comment_meta
+ *
+ * @param int $comment_id Comment ID.
+ * @param string $meta_key Metadata name.
+ * @param mixed $meta_value Metadata value.
+ * @param bool $unique Optional, default is false. Whether the same key should not be added.
+ * @return int|bool Meta ID on success, false on failure.
+ */
+function add_comment_meta($comment_id, $meta_key, $meta_value, $unique = false) {
+ return add_metadata('comment', $comment_id, $meta_key, $meta_value, $unique);
+}
+
+/**
+ * Remove metadata matching criteria from a comment.
+ *
+ * You can match based on the key, or key and value. Removing based on key and
+ * value, will keep from removing duplicate metadata with the same key. It also
+ * allows removing all metadata matching key, if needed.
+ *
+ * @since 2.9.0
+ * @link https://codex.wordpress.org/Function_Reference/delete_comment_meta
+ *
+ * @param int $comment_id comment ID
+ * @param string $meta_key Metadata name.
+ * @param mixed $meta_value Optional. Metadata value.
+ * @return bool True on success, false on failure.
+ */
+function delete_comment_meta($comment_id, $meta_key, $meta_value = '') {
+ return delete_metadata('comment', $comment_id, $meta_key, $meta_value);
+}
+
+/**
+ * Retrieve comment meta field for a comment.
+ *
+ * @since 2.9.0
+ * @link https://codex.wordpress.org/Function_Reference/get_comment_meta
+ *
+ * @param int $comment_id Comment ID.
+ * @param string $key Optional. The meta key to retrieve. By default, returns data for all keys.
+ * @param bool $single Whether to return a single value.
+ * @return mixed Will be an array if $single is false. Will be value of meta data field if $single
+ * is true.
+ */
+function get_comment_meta($comment_id, $key = '', $single = false) {
+ return get_metadata('comment', $comment_id, $key, $single);
+}
+
+/**
+ * Update comment meta field based on comment ID.
+ *
+ * Use the $prev_value parameter to differentiate between meta fields with the
+ * same key and comment ID.
+ *
+ * If the meta field for the comment does not exist, it will be added.
+ *
+ * @since 2.9.0
+ * @link https://codex.wordpress.org/Function_Reference/update_comment_meta
+ *
+ * @param int $comment_id Comment ID.
+ * @param string $meta_key Metadata key.
+ * @param mixed $meta_value Metadata value.
+ * @param mixed $prev_value Optional. Previous value to check before removing.
+ * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
+ */
+function update_comment_meta($comment_id, $meta_key, $meta_value, $prev_value = '') {
+ return update_metadata('comment', $comment_id, $meta_key, $meta_value, $prev_value);
+}
+
+/**
+ * Sets the cookies used to store an unauthenticated commentator's identity. Typically used
+ * to recall previous comments by this commentator that are still held in moderation.
+ *
+ * @param WP_Comment $comment Comment object.
+ * @param object $user Comment author's object.
+ *
+ * @since 3.4.0
+ */
+function wp_set_comment_cookies($comment, $user) {
+ if ( $user->exists() )
+ return;
+
+ /**
+ * Filter the lifetime of the comment cookie in seconds.
+ *
+ * @since 2.8.0
+ *
+ * @param int $seconds Comment cookie lifetime. Default 30000000.
+ */
+ $comment_cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
+ $secure = ( 'https' === parse_url( home_url(), PHP_URL_SCHEME ) );
+ setcookie( 'comment_author_' . COOKIEHASH, $comment->comment_author, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
+ setcookie( 'comment_author_email_' . COOKIEHASH, $comment->comment_author_email, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
+ setcookie( 'comment_author_url_' . COOKIEHASH, esc_url($comment->comment_author_url), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
+}
+
+/**
+ * Sanitizes the cookies sent to the user already.
+ *
+ * Will only do anything if the cookies have already been created for the user.
+ * Mostly used after cookies had been sent to use elsewhere.
+ *
+ * @since 2.0.4
+ */
+function sanitize_comment_cookies() {
+ if ( isset( $_COOKIE['comment_author_' . COOKIEHASH] ) ) {
+ /**
+ * Filter the comment author's name cookie before it is set.
+ *
+ * When this filter hook is evaluated in wp_filter_comment(),
+ * the comment author's name string is passed.
+ *
+ * @since 1.5.0
+ *
+ * @param string $author_cookie The comment author name cookie.
+ */
+ $comment_author = apply_filters( 'pre_comment_author_name', $_COOKIE['comment_author_' . COOKIEHASH] );
+ $comment_author = wp_unslash($comment_author);
+ $comment_author = esc_attr($comment_author);
+ $_COOKIE['comment_author_' . COOKIEHASH] = $comment_author;
+ }
+
+ if ( isset( $_COOKIE['comment_author_email_' . COOKIEHASH] ) ) {
+ /**
+ * Filter the comment author's email cookie before it is set.
+ *
+ * When this filter hook is evaluated in wp_filter_comment(),
+ * the comment author's email string is passed.
+ *
+ * @since 1.5.0
+ *
+ * @param string $author_email_cookie The comment author email cookie.
+ */
+ $comment_author_email = apply_filters( 'pre_comment_author_email', $_COOKIE['comment_author_email_' . COOKIEHASH] );
+ $comment_author_email = wp_unslash($comment_author_email);
+ $comment_author_email = esc_attr($comment_author_email);
+ $_COOKIE['comment_author_email_'.COOKIEHASH] = $comment_author_email;
+ }
+
+ if ( isset( $_COOKIE['comment_author_url_' . COOKIEHASH] ) ) {
+ /**
+ * Filter the comment author's URL cookie before it is set.
+ *
+ * When this filter hook is evaluated in wp_filter_comment(),
+ * the comment author's URL string is passed.
+ *
+ * @since 1.5.0
+ *
+ * @param string $author_url_cookie The comment author URL cookie.
+ */
+ $comment_author_url = apply_filters( 'pre_comment_author_url', $_COOKIE['comment_author_url_' . COOKIEHASH] );
+ $comment_author_url = wp_unslash($comment_author_url);
+ $_COOKIE['comment_author_url_'.COOKIEHASH] = $comment_author_url;
+ }
+}
+
+/**
+ * Validates whether this comment is allowed to be made.
+ *
+ * @since 2.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array $commentdata Contains information on the comment
+ * @return int|string Signifies the approval status (0|1|'spam')
+ */
+function wp_allow_comment( $commentdata ) {
+ global $wpdb;
+
+ // Simple duplicate check
+ // expected_slashed ($comment_post_ID, $comment_author, $comment_author_email, $comment_content)
+ $dupe = $wpdb->prepare(
+ "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_parent = %s AND comment_approved != 'trash' AND ( comment_author = %s ",
+ wp_unslash( $commentdata['comment_post_ID'] ),
+ wp_unslash( $commentdata['comment_parent'] ),
+ wp_unslash( $commentdata['comment_author'] )
+ );
+ if ( $commentdata['comment_author_email'] ) {
+ $dupe .= $wpdb->prepare(
+ "OR comment_author_email = %s ",
+ wp_unslash( $commentdata['comment_author_email'] )
+ );
+ }
+ $dupe .= $wpdb->prepare(
+ ") AND comment_content = %s LIMIT 1",
+ wp_unslash( $commentdata['comment_content'] )
+ );
+
+ $dupe_id = $wpdb->get_var( $dupe );
+
+ /**
+ * Filters the ID, if any, of the duplicate comment found when creating a new comment.
+ *
+ * Return an empty value from this filter to allow what WP considers a duplicate comment.
+ *
+ * @since 4.4.0
+ *
+ * @param int $dupe_id ID of the comment identified as a duplicate.
+ * @param array $commentdata Data for the comment being created.
+ */
+ $dupe_id = apply_filters( 'duplicate_comment_id', $dupe_id, $commentdata );
+
+ if ( $dupe_id ) {
+ /**
+ * Fires immediately after a duplicate comment is detected.
+ *
+ * @since 3.0.0
+ *
+ * @param array $commentdata Comment data.
+ */
+ do_action( 'comment_duplicate_trigger', $commentdata );
+ if ( defined( 'DOING_AJAX' ) ) {
+ die( __('Duplicate comment detected; it looks as though you’ve already said that!') );
+ }
+ wp_die( __( 'Duplicate comment detected; it looks as though you’ve already said that!' ), 409 );
+ }
+
+ /**
+ * Fires immediately before a comment is marked approved.
+ *
+ * Allows checking for comment flooding.
+ *
+ * @since 2.3.0
+ *
+ * @param string $comment_author_IP Comment author's IP address.
+ * @param string $comment_author_email Comment author's email.
+ * @param string $comment_date_gmt GMT date the comment was posted.
+ */
+ do_action(
+ 'check_comment_flood',
+ $commentdata['comment_author_IP'],
+ $commentdata['comment_author_email'],
+ $commentdata['comment_date_gmt']
+ );
+
+ if ( ! empty( $commentdata['user_id'] ) ) {
+ $user = get_userdata( $commentdata['user_id'] );
+ $post_author = $wpdb->get_var( $wpdb->prepare(
+ "SELECT post_author FROM $wpdb->posts WHERE ID = %d LIMIT 1",
+ $commentdata['comment_post_ID']
+ ) );
+ }
+
+ if ( isset( $user ) && ( $commentdata['user_id'] == $post_author || $user->has_cap( 'moderate_comments' ) ) ) {
+ // The author and the admins get respect.
+ $approved = 1;
+ } else {
+ // Everyone else's comments will be checked.
+ if ( check_comment(
+ $commentdata['comment_author'],
+ $commentdata['comment_author_email'],
+ $commentdata['comment_author_url'],
+ $commentdata['comment_content'],
+ $commentdata['comment_author_IP'],
+ $commentdata['comment_agent'],
+ $commentdata['comment_type']
+ ) ) {
+ $approved = 1;
+ } else {
+ $approved = 0;
+ }
+
+ if ( wp_blacklist_check(
+ $commentdata['comment_author'],
+ $commentdata['comment_author_email'],
+ $commentdata['comment_author_url'],
+ $commentdata['comment_content'],
+ $commentdata['comment_author_IP'],
+ $commentdata['comment_agent']
+ ) ) {
+ $approved = EMPTY_TRASH_DAYS ? 'trash' : 'spam';
+ }
+ }
+
+ /**
+ * Filter a comment's approval status before it is set.
+ *
+ * @since 2.1.0
+ *
+ * @param bool|string $approved The approval status. Accepts 1, 0, or 'spam'.
+ * @param array $commentdata Comment data.
+ */
+ $approved = apply_filters( 'pre_comment_approved', $approved, $commentdata );
+ return $approved;
+}
+
+/**
+ * Check whether comment flooding is occurring.
+ *
+ * Won't run, if current user can manage options, so to not block
+ * administrators.
+ *
+ * @since 2.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $ip Comment IP.
+ * @param string $email Comment author email address.
+ * @param string $date MySQL time string.
+ */
+function check_comment_flood_db( $ip, $email, $date ) {
+ global $wpdb;
+ // don't throttle admins or moderators
+ if ( current_user_can( 'manage_options' ) || current_user_can( 'moderate_comments' ) ) {
+ return;
+ }
+ $hour_ago = gmdate( 'Y-m-d H:i:s', time() - HOUR_IN_SECONDS );
+
+ if ( is_user_logged_in() ) {
+ $user = get_current_user_id();
+ $check_column = '`user_id`';
+ } else {
+ $user = $ip;
+ $check_column = '`comment_author_IP`';
+ }
+
+ $sql = $wpdb->prepare(
+ "SELECT `comment_date_gmt` FROM `$wpdb->comments` WHERE `comment_date_gmt` >= %s AND ( $check_column = %s OR `comment_author_email` = %s ) ORDER BY `comment_date_gmt` DESC LIMIT 1",
+ $hour_ago,
+ $user,
+ $email
+ );
+ $lasttime = $wpdb->get_var( $sql );
+ if ( $lasttime ) {
+ $time_lastcomment = mysql2date('U', $lasttime, false);
+ $time_newcomment = mysql2date('U', $date, false);
+ /**
+ * Filter the comment flood status.
+ *
+ * @since 2.1.0
+ *
+ * @param bool $bool Whether a comment flood is occurring. Default false.
+ * @param int $time_lastcomment Timestamp of when the last comment was posted.
+ * @param int $time_newcomment Timestamp of when the new comment was posted.
+ */
+ $flood_die = apply_filters( 'comment_flood_filter', false, $time_lastcomment, $time_newcomment );
+ if ( $flood_die ) {
+ /**
+ * Fires before the comment flood message is triggered.
+ *
+ * @since 1.5.0
+ *
+ * @param int $time_lastcomment Timestamp of when the last comment was posted.
+ * @param int $time_newcomment Timestamp of when the new comment was posted.
+ */
+ do_action( 'comment_flood_trigger', $time_lastcomment, $time_newcomment );
+
+ if ( defined('DOING_AJAX') )
+ die( __('You are posting comments too quickly. Slow down.') );
+
+ wp_die( __( 'You are posting comments too quickly. Slow down.' ), 429 );
+ }
+ }
+}
+
+/**
+ * Separates an array of comments into an array keyed by comment_type.
+ *
+ * @since 2.7.0
+ *
+ * @param array $comments Array of comments
+ * @return array Array of comments keyed by comment_type.
+ */
+function separate_comments(&$comments) {
+ $comments_by_type = array('comment' => array(), 'trackback' => array(), 'pingback' => array(), 'pings' => array());
+ $count = count($comments);
+ for ( $i = 0; $i < $count; $i++ ) {
+ $type = $comments[$i]->comment_type;
+ if ( empty($type) )
+ $type = 'comment';
+ $comments_by_type[$type][] = &$comments[$i];
+ if ( 'trackback' == $type || 'pingback' == $type )
+ $comments_by_type['pings'][] = &$comments[$i];
+ }
+
+ return $comments_by_type;
+}
+
+/**
+ * Calculate the total number of comment pages.
+ *
+ * @since 2.7.0
+ *
+ * @uses Walker_Comment
+ *
+ * @global WP_Query $wp_query
+ *
+ * @param array $comments Optional array of WP_Comment objects. Defaults to $wp_query->comments
+ * @param int $per_page Optional comments per page.
+ * @param bool $threaded Optional control over flat or threaded comments.
+ * @return int Number of comment pages.
+ */
+function get_comment_pages_count( $comments = null, $per_page = null, $threaded = null ) {
+ global $wp_query;
+
+ if ( null === $comments && null === $per_page && null === $threaded && !empty($wp_query->max_num_comment_pages) )
+ return $wp_query->max_num_comment_pages;
+
+ if ( ( ! $comments || ! is_array( $comments ) ) && ! empty( $wp_query->comments ) )
+ $comments = $wp_query->comments;
+
+ if ( empty($comments) )
+ return 0;
+
+ if ( ! get_option( 'page_comments' ) ) {
+ return 1;
+ }
+
+ if ( !isset($per_page) )
+ $per_page = (int) get_query_var('comments_per_page');
+ if ( 0 === $per_page )
+ $per_page = (int) get_option('comments_per_page');
+ if ( 0 === $per_page )
+ return 1;
+
+ if ( !isset($threaded) )
+ $threaded = get_option('thread_comments');
+
+ if ( $threaded ) {
+ $walker = new Walker_Comment;
+ $count = ceil( $walker->get_number_of_root_elements( $comments ) / $per_page );
+ } else {
+ $count = ceil( count( $comments ) / $per_page );
+ }
+
+ return $count;
+}
+
+/**
+ * Calculate what page number a comment will appear on for comment paging.
+ *
+ * @since 2.7.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $comment_ID Comment ID.
+ * @param array $args {
+ * Array of optional arguments.
+ * @type string $type Limit paginated comments to those matching a given type. Accepts 'comment',
+ * 'trackback', 'pingback', 'pings' (trackbacks and pingbacks), or 'all'.
+ * Default is 'all'.
+ * @type int $per_page Per-page count to use when calculating pagination. Defaults to the value of the
+ * 'comments_per_page' option.
+ * @type int|string $max_depth If greater than 1, comment page will be determined for the top-level parent of
+ * `$comment_ID`. Defaults to the value of the 'thread_comments_depth' option.
+ * } *
+ * @return int|null Comment page number or null on error.
+ */
+function get_page_of_comment( $comment_ID, $args = array() ) {
+ global $wpdb;
+
+ $page = null;
+
+ if ( !$comment = get_comment( $comment_ID ) )
+ return;
+
+ $defaults = array( 'type' => 'all', 'page' => '', 'per_page' => '', 'max_depth' => '' );
+ $args = wp_parse_args( $args, $defaults );
+ $original_args = $args;
+
+ // Order of precedence: 1. `$args['per_page']`, 2. 'comments_per_page' query_var, 3. 'comments_per_page' option.
+ if ( get_option( 'page_comments' ) ) {
+ if ( '' === $args['per_page'] ) {
+ $args['per_page'] = get_query_var( 'comments_per_page' );
+ }
+
+ if ( '' === $args['per_page'] ) {
+ $args['per_page'] = get_option( 'comments_per_page' );
+ }
+ }
+
+ if ( empty($args['per_page']) ) {
+ $args['per_page'] = 0;
+ $args['page'] = 0;
+ }
+
+ if ( $args['per_page'] < 1 ) {
+ $page = 1;
+ }
+
+ if ( null === $page ) {
+ if ( '' === $args['max_depth'] ) {
+ if ( get_option('thread_comments') )
+ $args['max_depth'] = get_option('thread_comments_depth');
+ else
+ $args['max_depth'] = -1;
+ }
+
+ // Find this comment's top level parent if threading is enabled
+ if ( $args['max_depth'] > 1 && 0 != $comment->comment_parent )
+ return get_page_of_comment( $comment->comment_parent, $args );
+
+ $comment_args = array(
+ 'type' => $args['type'],
+ 'post_id' => $comment->comment_post_ID,
+ 'fields' => 'ids',
+ 'count' => true,
+ 'status' => 'approve',
+ 'parent' => 0,
+ 'date_query' => array(
+ array(
+ 'column' => "$wpdb->comments.comment_date_gmt",
+ 'before' => $comment->comment_date_gmt,
+ )
+ ),
+ );
+
+ $comment_query = new WP_Comment_Query();
+ $older_comment_count = $comment_query->query( $comment_args );
+
+ // No older comments? Then it's page #1.
+ if ( 0 == $older_comment_count ) {
+ $page = 1;
+
+ // Divide comments older than this one by comments per page to get this comment's page number
+ } else {
+ $page = ceil( ( $older_comment_count + 1 ) / $args['per_page'] );
+ }
+ }
+
+ /**
+ * Filters the calculated page on which a comment appears.
+ *
+ * @since 4.4.0
+ *
+ * @param int $page Comment page.
+ * @param array $args {
+ * Arguments used to calculate pagination. These include arguments auto-detected by the function,
+ * based on query vars, system settings, etc. For pristine arguments passed to the function,
+ * see `$original_args`.
+ *
+ * @type string $type Type of comments to count.
+ * @type int $page Calculated current page.
+ * @type int $per_page Calculated number of comments per page.
+ * @type int $max_depth Maximum comment threading depth allowed.
+ * }
+ * @param array $original_args {
+ * Array of arguments passed to the function. Some or all of these may not be set.
+ *
+ * @type string $type Type of comments to count.
+ * @type int $page Current comment page.
+ * @type int $per_page Number of comments per page.
+ * @type int $max_depth Maximum comment threading depth allowed.
+ * }
+ */
+ return apply_filters( 'get_page_of_comment', (int) $page, $args, $original_args );
+}
+
+/**
+ * Does comment contain blacklisted characters or words.
+ *
+ * @since 1.5.0
+ *
+ * @param string $author The author of the comment
+ * @param string $email The email of the comment
+ * @param string $url The url used in the comment
+ * @param string $comment The comment content
+ * @param string $user_ip The comment author IP address
+ * @param string $user_agent The author's browser user agent
+ * @return bool True if comment contains blacklisted content, false if comment does not
+ */
+function wp_blacklist_check($author, $email, $url, $comment, $user_ip, $user_agent) {
+ /**
+ * Fires before the comment is tested for blacklisted characters or words.
+ *
+ * @since 1.5.0
+ *
+ * @param string $author Comment author.
+ * @param string $email Comment author's email.
+ * @param string $url Comment author's URL.
+ * @param string $comment Comment content.
+ * @param string $user_ip Comment author's IP address.
+ * @param string $user_agent Comment author's browser user agent.
+ */
+ do_action( 'wp_blacklist_check', $author, $email, $url, $comment, $user_ip, $user_agent );
+
+ $mod_keys = trim( get_option('blacklist_keys') );
+ if ( '' == $mod_keys )
+ return false; // If moderation keys are empty
+ $words = explode("\n", $mod_keys );
+
+ foreach ( (array) $words as $word ) {
+ $word = trim($word);
+
+ // Skip empty lines
+ if ( empty($word) ) { continue; }
+
+ // Do some escaping magic so that '#' chars in the
+ // spam words don't break things:
+ $word = preg_quote($word, '#');
+
+ $pattern = "#$word#i";
+ if (
+ preg_match($pattern, $author)
+ || preg_match($pattern, $email)
+ || preg_match($pattern, $url)
+ || preg_match($pattern, $comment)
+ || preg_match($pattern, $user_ip)
+ || preg_match($pattern, $user_agent)
+ )
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Retrieve total comments for blog or single post.
+ *
+ * The properties of the returned object contain the 'moderated', 'approved',
+ * and spam comments for either the entire blog or single post. Those properties
+ * contain the amount of comments that match the status. The 'total_comments'
+ * property contains the integer of total comments.
+ *
+ * The comment stats are cached and then retrieved, if they already exist in the
+ * cache.
+ *
+ * @since 2.5.0
+ *
+ * @param int $post_id Optional. Post ID.
+ * @return object|array Comment stats.
+ */
+function wp_count_comments( $post_id = 0 ) {
+ $post_id = (int) $post_id;
+
+ /**
+ * Filter the comments count for a given post.
+ *
+ * @since 2.7.0
+ *
+ * @param array $count An empty array.
+ * @param int $post_id The post ID.
+ */
+ $filtered = apply_filters( 'wp_count_comments', array(), $post_id );
+ if ( ! empty( $filtered ) ) {
+ return $filtered;
+ }
+
+ $count = wp_cache_get( "comments-{$post_id}", 'counts' );
+ if ( false !== $count ) {
+ return $count;
+ }
+
+ $stats = get_comment_count( $post_id );
+ $stats['moderated'] = $stats['awaiting_moderation'];
+ unset( $stats['awaiting_moderation'] );
+
+ $stats_object = (object) $stats;
+ wp_cache_set( "comments-{$post_id}", $stats_object, 'counts' );
+
+ return $stats_object;
+}
+
+/**
+ * Trashes or deletes a comment.
+ *
+ * The comment is moved to trash instead of permanently deleted unless trash is
+ * disabled, item is already in the trash, or $force_delete is true.
+ *
+ * The post comment count will be updated if the comment was approved and has a
+ * post ID available.
+ *
+ * @since 2.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
+ * @param bool $force_delete Whether to bypass trash and force deletion. Default is false.
+ * @return bool True on success, false on failure.
+ */
+function wp_delete_comment($comment_id, $force_delete = false) {
+ global $wpdb;
+ if (!$comment = get_comment($comment_id))
+ return false;
+
+ if ( !$force_delete && EMPTY_TRASH_DAYS && !in_array( wp_get_comment_status( $comment ), array( 'trash', 'spam' ) ) )
+ return wp_trash_comment($comment_id);
+
+ /**
+ * Fires immediately before a comment is deleted from the database.
+ *
+ * @since 1.2.0
+ *
+ * @param int $comment_id The comment ID.
+ */
+ do_action( 'delete_comment', $comment->comment_ID );
+
+ // Move children up a level.
+ $children = $wpdb->get_col( $wpdb->prepare("SELECT comment_ID FROM $wpdb->comments WHERE comment_parent = %d", $comment->comment_ID) );
+ if ( !empty($children) ) {
+ $wpdb->update($wpdb->comments, array('comment_parent' => $comment->comment_parent), array('comment_parent' => $comment->comment_ID));
+ clean_comment_cache($children);
+ }
+
+ // Delete metadata
+ $meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->commentmeta WHERE comment_id = %d", $comment->comment_ID ) );
+ foreach ( $meta_ids as $mid )
+ delete_metadata_by_mid( 'comment', $mid );
+
+ if ( ! $wpdb->delete( $wpdb->comments, array( 'comment_ID' => $comment->comment_ID ) ) )
+ return false;
+
+ /**
+ * Fires immediately after a comment is deleted from the database.
+ *
+ * @since 2.9.0
+ *
+ * @param int $comment_id The comment ID.
+ */
+ do_action( 'deleted_comment', $comment->comment_ID );
+
+ $post_id = $comment->comment_post_ID;
+ if ( $post_id && $comment->comment_approved == 1 )
+ wp_update_comment_count($post_id);
+
+ clean_comment_cache( $comment->comment_ID );
+
+ /** This action is documented in wp-includes/comment-functions.php */
+ do_action( 'wp_set_comment_status', $comment->comment_ID, 'delete' );
+
+ wp_transition_comment_status('delete', $comment->comment_approved, $comment);
+ return true;
+}
+
+/**
+ * Moves a comment to the Trash
+ *
+ * If trash is disabled, comment is permanently deleted.
+ *
+ * @since 2.9.0
+ *
+ * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
+ * @return bool True on success, false on failure.
+ */
+function wp_trash_comment($comment_id) {
+ if ( !EMPTY_TRASH_DAYS )
+ return wp_delete_comment($comment_id, true);
+
+ if ( !$comment = get_comment($comment_id) )
+ return false;
+
+ /**
+ * Fires immediately before a comment is sent to the Trash.
+ *
+ * @since 2.9.0
+ *
+ * @param int $comment_id The comment ID.
+ */
+ do_action( 'trash_comment', $comment->comment_ID );
+
+ if ( wp_set_comment_status( $comment, 'trash' ) ) {
+ delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_status' );
+ delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_time' );
+ add_comment_meta( $comment->comment_ID, '_wp_trash_meta_status', $comment->comment_approved );
+ add_comment_meta( $comment->comment_ID, '_wp_trash_meta_time', time() );
+
+ /**
+ * Fires immediately after a comment is sent to Trash.
+ *
+ * @since 2.9.0
+ *
+ * @param int $comment_id The comment ID.
+ */
+ do_action( 'trashed_comment', $comment->comment_ID );
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Removes a comment from the Trash
+ *
+ * @since 2.9.0
+ *
+ * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
+ * @return bool True on success, false on failure.
+ */
+function wp_untrash_comment($comment_id) {
+ $comment = get_comment( $comment_id );
+ if ( ! $comment ) {
+ return false;
+ }
+
+ /**
+ * Fires immediately before a comment is restored from the Trash.
+ *
+ * @since 2.9.0
+ *
+ * @param int $comment_id The comment ID.
+ */
+ do_action( 'untrash_comment', $comment->comment_ID );
+
+ $status = (string) get_comment_meta( $comment->comment_ID, '_wp_trash_meta_status', true );
+ if ( empty($status) )
+ $status = '0';
+
+ if ( wp_set_comment_status( $comment, $status ) ) {
+ delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_time' );
+ delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_status' );
+ /**
+ * Fires immediately after a comment is restored from the Trash.
+ *
+ * @since 2.9.0
+ *
+ * @param int $comment_id The comment ID.
+ */
+ do_action( 'untrashed_comment', $comment->comment_ID );
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Marks a comment as Spam
+ *
+ * @since 2.9.0
+ *
+ * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
+ * @return bool True on success, false on failure.
+ */
+function wp_spam_comment( $comment_id ) {
+ $comment = get_comment( $comment_id );
+ if ( ! $comment ) {
+ return false;
+ }
+
+ /**
+ * Fires immediately before a comment is marked as Spam.
+ *
+ * @since 2.9.0
+ *
+ * @param int $comment_id The comment ID.
+ */
+ do_action( 'spam_comment', $comment->comment_ID );
+
+ if ( wp_set_comment_status( $comment, 'spam' ) ) {
+ delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_status' );
+ delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_time' );
+ add_comment_meta( $comment->comment_ID, '_wp_trash_meta_status', $comment->comment_approved );
+ add_comment_meta( $comment->comment_ID, '_wp_trash_meta_time', time() );
+ /**
+ * Fires immediately after a comment is marked as Spam.
+ *
+ * @since 2.9.0
+ *
+ * @param int $comment_id The comment ID.
+ */
+ do_action( 'spammed_comment', $comment->comment_ID );
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Removes a comment from the Spam
+ *
+ * @since 2.9.0
+ *
+ * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
+ * @return bool True on success, false on failure.
+ */
+function wp_unspam_comment( $comment_id ) {
+ $comment = get_comment( $comment_id );
+ if ( ! $comment ) {
+ return false;
+ }
+
+ /**
+ * Fires immediately before a comment is unmarked as Spam.
+ *
+ * @since 2.9.0
+ *
+ * @param int $comment_id The comment ID.
+ */
+ do_action( 'unspam_comment', $comment->comment_ID );
+
+ $status = (string) get_comment_meta( $comment->comment_ID, '_wp_trash_meta_status', true );
+ if ( empty($status) )
+ $status = '0';
+
+ if ( wp_set_comment_status( $comment, $status ) ) {
+ delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_status' );
+ delete_comment_meta( $comment->comment_ID, '_wp_trash_meta_time' );
+ /**
+ * Fires immediately after a comment is unmarked as Spam.
+ *
+ * @since 2.9.0
+ *
+ * @param int $comment_id The comment ID.
+ */
+ do_action( 'unspammed_comment', $comment->comment_ID );
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * The status of a comment by ID.
+ *
+ * @since 1.0.0
+ *
+ * @param int|WP_Comment $comment_id Comment ID or WP_Comment object
+ * @return false|string Status might be 'trash', 'approved', 'unapproved', 'spam'. False on failure.
+ */
+function wp_get_comment_status($comment_id) {
+ $comment = get_comment($comment_id);
+ if ( !$comment )
+ return false;
+
+ $approved = $comment->comment_approved;
+
+ if ( $approved == null )
+ return false;
+ elseif ( $approved == '1' )
+ return 'approved';
+ elseif ( $approved == '0' )
+ return 'unapproved';
+ elseif ( $approved == 'spam' )
+ return 'spam';
+ elseif ( $approved == 'trash' )
+ return 'trash';
+ else
+ return false;
+}
+
+/**
+ * Call hooks for when a comment status transition occurs.
+ *
+ * Calls hooks for comment status transitions. If the new comment status is not the same
+ * as the previous comment status, then two hooks will be ran, the first is
+ * 'transition_comment_status' with new status, old status, and comment data. The
+ * next action called is 'comment_OLDSTATUS_to_NEWSTATUS' the NEWSTATUS is the
+ * $new_status parameter and the OLDSTATUS is $old_status parameter; it has the
+ * comment data.
+ *
+ * The final action will run whether or not the comment statuses are the same. The
+ * action is named 'comment_NEWSTATUS_COMMENTTYPE', NEWSTATUS is from the $new_status
+ * parameter and COMMENTTYPE is comment_type comment data.
+ *
+ * @since 2.7.0
+ *
+ * @param string $new_status New comment status.
+ * @param string $old_status Previous comment status.
+ * @param object $comment Comment data.
+ */
+function wp_transition_comment_status($new_status, $old_status, $comment) {
+ /*
+ * Translate raw statuses to human readable formats for the hooks.
+ * This is not a complete list of comment status, it's only the ones
+ * that need to be renamed
+ */
+ $comment_statuses = array(
+ 0 => 'unapproved',
+ 'hold' => 'unapproved', // wp_set_comment_status() uses "hold"
+ 1 => 'approved',
+ 'approve' => 'approved', // wp_set_comment_status() uses "approve"
+ );
+ if ( isset($comment_statuses[$new_status]) ) $new_status = $comment_statuses[$new_status];
+ if ( isset($comment_statuses[$old_status]) ) $old_status = $comment_statuses[$old_status];
+
+ // Call the hooks
+ if ( $new_status != $old_status ) {
+ /**
+ * Fires when the comment status is in transition.
+ *
+ * @since 2.7.0
+ *
+ * @param int|string $new_status The new comment status.
+ * @param int|string $old_status The old comment status.
+ * @param object $comment The comment data.
+ */
+ do_action( 'transition_comment_status', $new_status, $old_status, $comment );
+ /**
+ * Fires when the comment status is in transition from one specific status to another.
+ *
+ * The dynamic portions of the hook name, `$old_status`, and `$new_status`,
+ * refer to the old and new comment statuses, respectively.
+ *
+ * @since 2.7.0
+ *
+ * @param WP_Comment $comment Comment object.
+ */
+ do_action( "comment_{$old_status}_to_{$new_status}", $comment );
+ }
+ /**
+ * Fires when the status of a specific comment type is in transition.
+ *
+ * The dynamic portions of the hook name, `$new_status`, and `$comment->comment_type`,
+ * refer to the new comment status, and the type of comment, respectively.
+ *
+ * Typical comment types include an empty string (standard comment), 'pingback',
+ * or 'trackback'.
+ *
+ * @since 2.7.0
+ *
+ * @param int $comment_ID The comment ID.
+ * @param WP_Comment $comment Comment object.
+ */
+ do_action( "comment_{$new_status}_{$comment->comment_type}", $comment->comment_ID, $comment );
+}
+
+/**
+ * Get current commenter's name, email, and URL.
+ *
+ * Expects cookies content to already be sanitized. User of this function might
+ * wish to recheck the returned array for validity.
+ *
+ * @see sanitize_comment_cookies() Use to sanitize cookies
+ *
+ * @since 2.0.4
+ *
+ * @return array Comment author, email, url respectively.
+ */
+function wp_get_current_commenter() {
+ // Cookies should already be sanitized.
+
+ $comment_author = '';
+ if ( isset($_COOKIE['comment_author_'.COOKIEHASH]) )
+ $comment_author = $_COOKIE['comment_author_'.COOKIEHASH];
+
+ $comment_author_email = '';
+ if ( isset($_COOKIE['comment_author_email_'.COOKIEHASH]) )
+ $comment_author_email = $_COOKIE['comment_author_email_'.COOKIEHASH];
+
+ $comment_author_url = '';
+ if ( isset($_COOKIE['comment_author_url_'.COOKIEHASH]) )
+ $comment_author_url = $_COOKIE['comment_author_url_'.COOKIEHASH];
+
+ /**
+ * Filter the current commenter's name, email, and URL.
+ *
+ * @since 3.1.0
+ *
+ * @param array $comment_author_data {
+ * An array of current commenter variables.
+ *
+ * @type string $comment_author The name of the author of the comment. Default empty.
+ * @type string $comment_author_email The email address of the `$comment_author`. Default empty.
+ * @type string $comment_author_url The URL address of the `$comment_author`. Default empty.
+ * }
+ */
+ return apply_filters( 'wp_get_current_commenter', compact('comment_author', 'comment_author_email', 'comment_author_url') );
+}
+
+/**
+ * Inserts a comment into the database.
+ *
+ * @since 2.0.0
+ * @since 4.4.0 Introduced `$comment_meta` argument.
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array $commentdata {
+ * Array of arguments for inserting a new comment.
+ *
+ * @type string $comment_agent The HTTP user agent of the `$comment_author` when
+ * the comment was submitted. Default empty.
+ * @type int|string $comment_approved Whether the comment has been approved. Default 1.
+ * @type string $comment_author The name of the author of the comment. Default empty.
+ * @type string $comment_author_email The email address of the `$comment_author`. Default empty.
+ * @type string $comment_author_IP The IP address of the `$comment_author`. Default empty.
+ * @type string $comment_author_url The URL address of the `$comment_author`. Default empty.
+ * @type string $comment_content The content of the comment. Default empty.
+ * @type string $comment_date The date the comment was submitted. To set the date
+ * manually, `$comment_date_gmt` must also be specified.
+ * Default is the current time.
+ * @type string $comment_date_gmt The date the comment was submitted in the GMT timezone.
+ * Default is `$comment_date` in the site's GMT timezone.
+ * @type int $comment_karma The karma of the comment. Default 0.
+ * @type int $comment_parent ID of this comment's parent, if any. Default 0.
+ * @type int $comment_post_ID ID of the post that relates to the comment, if any.
+ * Default empty.
+ * @type string $comment_type Comment type. Default empty.
+ * @type array $comment_meta Optional. Array of key/value pairs to be stored in commentmeta for the
+ * new comment.
+ * @type int $user_id ID of the user who submitted the comment. Default 0.
+ * }
+ * @return int|false The new comment's ID on success, false on failure.
+ */
+function wp_insert_comment( $commentdata ) {
+ global $wpdb;
+ $data = wp_unslash( $commentdata );
+
+ $comment_author = ! isset( $data['comment_author'] ) ? '' : $data['comment_author'];
+ $comment_author_email = ! isset( $data['comment_author_email'] ) ? '' : $data['comment_author_email'];
+ $comment_author_url = ! isset( $data['comment_author_url'] ) ? '' : $data['comment_author_url'];
+ $comment_author_IP = ! isset( $data['comment_author_IP'] ) ? '' : $data['comment_author_IP'];
+
+ $comment_date = ! isset( $data['comment_date'] ) ? current_time( 'mysql' ) : $data['comment_date'];
+ $comment_date_gmt = ! isset( $data['comment_date_gmt'] ) ? get_gmt_from_date( $comment_date ) : $data['comment_date_gmt'];
+
+ $comment_post_ID = ! isset( $data['comment_post_ID'] ) ? '' : $data['comment_post_ID'];
+ $comment_content = ! isset( $data['comment_content'] ) ? '' : $data['comment_content'];
+ $comment_karma = ! isset( $data['comment_karma'] ) ? 0 : $data['comment_karma'];
+ $comment_approved = ! isset( $data['comment_approved'] ) ? 1 : $data['comment_approved'];
+ $comment_agent = ! isset( $data['comment_agent'] ) ? '' : $data['comment_agent'];
+ $comment_type = ! isset( $data['comment_type'] ) ? '' : $data['comment_type'];
+ $comment_parent = ! isset( $data['comment_parent'] ) ? 0 : $data['comment_parent'];
+
+ $user_id = ! isset( $data['user_id'] ) ? 0 : $data['user_id'];
+
+ $compacted = compact( 'comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_date', 'comment_date_gmt', 'comment_content', 'comment_karma', 'comment_approved', 'comment_agent', 'comment_type', 'comment_parent', 'user_id' );
+ if ( ! $wpdb->insert( $wpdb->comments, $compacted ) ) {
+ return false;
+ }
+
+ $id = (int) $wpdb->insert_id;
+
+ if ( $comment_approved == 1 ) {
+ wp_update_comment_count( $comment_post_ID );
+ }
+ $comment = get_comment( $id );
+
+ // If metadata is provided, store it.
+ if ( isset( $commentdata['comment_meta'] ) && is_array( $commentdata['comment_meta'] ) ) {
+ foreach ( $commentdata['comment_meta'] as $meta_key => $meta_value ) {
+ add_comment_meta( $comment->comment_ID, $meta_key, $meta_value, true );
+ }
+ }
+
+ /**
+ * Fires immediately after a comment is inserted into the database.
+ *
+ * @since 2.8.0
+ *
+ * @param int $id The comment ID.
+ * @param WP_Comment $comment Comment object.
+ */
+ do_action( 'wp_insert_comment', $id, $comment );
+
+ wp_cache_set( 'last_changed', microtime(), 'comment' );
+
+ return $id;
+}
+
+/**
+ * Filters and sanitizes comment data.
+ *
+ * Sets the comment data 'filtered' field to true when finished. This can be
+ * checked as to whether the comment should be filtered and to keep from
+ * filtering the same comment more than once.
+ *
+ * @since 2.0.0
+ *
+ * @param array $commentdata Contains information on the comment.
+ * @return array Parsed comment information.
+ */
+function wp_filter_comment($commentdata) {
+ if ( isset( $commentdata['user_ID'] ) ) {
+ /**
+ * Filter the comment author's user id before it is set.
+ *
+ * The first time this filter is evaluated, 'user_ID' is checked
+ * (for back-compat), followed by the standard 'user_id' value.
+ *
+ * @since 1.5.0
+ *
+ * @param int $user_ID The comment author's user ID.
+ */
+ $commentdata['user_id'] = apply_filters( 'pre_user_id', $commentdata['user_ID'] );
+ } elseif ( isset( $commentdata['user_id'] ) ) {
+ /** This filter is documented in wp-includes/comment-functions.php */
+ $commentdata['user_id'] = apply_filters( 'pre_user_id', $commentdata['user_id'] );
+ }
+
+ /**
+ * Filter the comment author's browser user agent before it is set.
+ *
+ * @since 1.5.0
+ *
+ * @param int $comment_agent The comment author's browser user agent.
+ */
+ $commentdata['comment_agent'] = apply_filters( 'pre_comment_user_agent', ( isset( $commentdata['comment_agent'] ) ? $commentdata['comment_agent'] : '' ) );
+ /** This filter is documented in wp-includes/comment-functions.php */
+ $commentdata['comment_author'] = apply_filters( 'pre_comment_author_name', $commentdata['comment_author'] );
+ /**
+ * Filter the comment content before it is set.
+ *
+ * @since 1.5.0
+ *
+ * @param int $comment_content The comment content.
+ */
+ $commentdata['comment_content'] = apply_filters( 'pre_comment_content', $commentdata['comment_content'] );
+ /**
+ * Filter the comment author's IP before it is set.
+ *
+ * @since 1.5.0
+ *
+ * @param int $comment_author_ip The comment author's IP.
+ */
+ $commentdata['comment_author_IP'] = apply_filters( 'pre_comment_user_ip', $commentdata['comment_author_IP'] );
+ /** This filter is documented in wp-includes/comment-functions.php */
+ $commentdata['comment_author_url'] = apply_filters( 'pre_comment_author_url', $commentdata['comment_author_url'] );
+ /** This filter is documented in wp-includes/comment-functions.php */
+ $commentdata['comment_author_email'] = apply_filters( 'pre_comment_author_email', $commentdata['comment_author_email'] );
+ $commentdata['filtered'] = true;
+ return $commentdata;
+}
+
+/**
+ * Whether a comment should be blocked because of comment flood.
+ *
+ * @since 2.1.0
+ *
+ * @param bool $block Whether plugin has already blocked comment.
+ * @param int $time_lastcomment Timestamp for last comment.
+ * @param int $time_newcomment Timestamp for new comment.
+ * @return bool Whether comment should be blocked.
+ */
+function wp_throttle_comment_flood($block, $time_lastcomment, $time_newcomment) {
+ if ( $block ) // a plugin has already blocked... we'll let that decision stand
+ return $block;
+ if ( ($time_newcomment - $time_lastcomment) < 15 )
+ return true;
+ return false;
+}
+
+/**
+ * Adds a new comment to the database.
+ *
+ * Filters new comment to ensure that the fields are sanitized and valid before
+ * inserting comment into database. Calls 'comment_post' action with comment ID
+ * and whether comment is approved by WordPress. Also has 'preprocess_comment'
+ * filter for processing the comment data before the function handles it.
+ *
+ * We use REMOTE_ADDR here directly. If you are behind a proxy, you should ensure
+ * that it is properly set, such as in wp-config.php, for your environment.
+ * See {@link https://core.trac.wordpress.org/ticket/9235}
+ *
+ * @since 1.5.0
+ * @since 4.3.0 'comment_agent' and 'comment_author_IP' can be set via `$commentdata`.
+ *
+ * @see wp_insert_comment()
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array $commentdata {
+ * Comment data.
+ *
+ * @type string $comment_author The name of the comment author.
+ * @type string $comment_author_email The comment author email address.
+ * @type string $comment_author_url The comment author URL.
+ * @type string $comment_content The content of the comment.
+ * @type string $comment_date The date the comment was submitted. Default is the current time.
+ * @type string $comment_date_gmt The date the comment was submitted in the GMT timezone.
+ * Default is `$comment_date` in the GMT timezone.
+ * @type int $comment_parent The ID of this comment's parent, if any. Default 0.
+ * @type int $comment_post_ID The ID of the post that relates to the comment.
+ * @type int $user_id The ID of the user who submitted the comment. Default 0.
+ * @type int $user_ID Kept for backward-compatibility. Use `$user_id` instead.
+ * @type string $comment_agent Comment author user agent. Default is the value of 'HTTP_USER_AGENT'
+ * in the `$_SERVER` superglobal sent in the original request.
+ * @type string $comment_author_IP Comment author IP address in IPv4 format. Default is the value of
+ * 'REMOTE_ADDR' in the `$_SERVER` superglobal sent in the original request.
+ * }
+ * @return int|false The ID of the comment on success, false on failure.
+ */
+function wp_new_comment( $commentdata ) {
+ global $wpdb;
+
+ if ( isset( $commentdata['user_ID'] ) ) {
+ $commentdata['user_id'] = $commentdata['user_ID'] = (int) $commentdata['user_ID'];
+ }
+
+ $prefiltered_user_id = ( isset( $commentdata['user_id'] ) ) ? (int) $commentdata['user_id'] : 0;
+
+ /**
+ * Filter a comment's data before it is sanitized and inserted into the database.
+ *
+ * @since 1.5.0
+ *
+ * @param array $commentdata Comment data.
+ */
+ $commentdata = apply_filters( 'preprocess_comment', $commentdata );
+
+ $commentdata['comment_post_ID'] = (int) $commentdata['comment_post_ID'];
+ if ( isset( $commentdata['user_ID'] ) && $prefiltered_user_id !== (int) $commentdata['user_ID'] ) {
+ $commentdata['user_id'] = $commentdata['user_ID'] = (int) $commentdata['user_ID'];
+ } elseif ( isset( $commentdata['user_id'] ) ) {
+ $commentdata['user_id'] = (int) $commentdata['user_id'];
+ }
+
+ $commentdata['comment_parent'] = isset($commentdata['comment_parent']) ? absint($commentdata['comment_parent']) : 0;
+ $parent_status = ( 0 < $commentdata['comment_parent'] ) ? wp_get_comment_status($commentdata['comment_parent']) : '';
+ $commentdata['comment_parent'] = ( 'approved' == $parent_status || 'unapproved' == $parent_status ) ? $commentdata['comment_parent'] : 0;
+
+ if ( ! isset( $commentdata['comment_author_IP'] ) ) {
+ $commentdata['comment_author_IP'] = $_SERVER['REMOTE_ADDR'];
+ }
+ $commentdata['comment_author_IP'] = preg_replace( '/[^0-9a-fA-F:., ]/', '', $commentdata['comment_author_IP'] );
+
+ if ( ! isset( $commentdata['comment_agent'] ) ) {
+ $commentdata['comment_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT']: '';
+ }
+ $commentdata['comment_agent'] = substr( $commentdata['comment_agent'], 0, 254 );
+
+ if ( empty( $commentdata['comment_date'] ) ) {
+ $commentdata['comment_date'] = current_time('mysql');
+ }
+
+ if ( empty( $commentdata['comment_date_gmt'] ) ) {
+ $commentdata['comment_date_gmt'] = current_time( 'mysql', 1 );
+ }
+
+ $commentdata = wp_filter_comment($commentdata);
+
+ $commentdata['comment_approved'] = wp_allow_comment($commentdata);
+
+ $comment_ID = wp_insert_comment($commentdata);
+ if ( ! $comment_ID ) {
+ $fields = array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content' );
+
+ foreach ( $fields as $field ) {
+ if ( isset( $commentdata[ $field ] ) ) {
+ $commentdata[ $field ] = $wpdb->strip_invalid_text_for_column( $wpdb->comments, $field, $commentdata[ $field ] );
+ }
+ }
+
+ $commentdata = wp_filter_comment( $commentdata );
+
+ $commentdata['comment_approved'] = wp_allow_comment( $commentdata );
+
+ $comment_ID = wp_insert_comment( $commentdata );
+ if ( ! $comment_ID ) {
+ return false;
+ }
+ }
+
+ /**
+ * Fires immediately after a comment is inserted into the database.
+ *
+ * @since 1.2.0
+ *
+ * @param int $comment_ID The comment ID.
+ * @param int|string $comment_approved 1 if the comment is approved, 0 if not, 'spam' if spam.
+ */
+ do_action( 'comment_post', $comment_ID, $commentdata['comment_approved'] );
+
+ return $comment_ID;
+}
+
+/**
+ * Send a comment moderation notification to the comment moderator.
+ *
+ * @since 4.4.0
+ *
+ * @param int $comment_ID ID of the comment.
+ * @return bool True on success, false on failure.
+ */
+function wp_new_comment_notify_moderator( $comment_ID ) {
+ $comment = get_comment( $comment_ID );
+
+ // Only send notifications for pending comments.
+ $maybe_notify = ( '0' == $comment->comment_approved );
+
+ /** This filter is documented in wp-includes/comment-functions.php */
+ $maybe_notify = apply_filters( 'notify_moderator', $maybe_notify, $comment_ID );
+
+ if ( ! $maybe_notify ) {
+ return false;
+ }
+
+ return wp_notify_moderator( $comment_ID );
+}
+
+/**
+ * Send a notification of a new comment to the post author.
+ *
+ * @since 4.4.0
+ *
+ * Uses the {@see 'notify_post_author'} filter to determine whether the post author
+ * should be notified when a new comment is added, overriding site setting.
+ *
+ * @param int $comment_ID Comment ID.
+ * @return bool True on success, false on failure.
+ */
+function wp_new_comment_notify_postauthor( $comment_ID ) {
+ $comment = get_comment( $comment_ID );
+
+ $maybe_notify = get_option( 'comments_notify' );
+
+ /**
+ * Filter whether to send the post author new comment notification emails,
+ * overriding the site setting.
+ *
+ * @since 4.4.0
+ *
+ * @param bool $maybe_notify Whether to notify the post author about the new comment.
+ * @param int $comment_ID The ID of the comment for the notification.
+ */
+ $maybe_notify = apply_filters( 'notify_post_author', $maybe_notify, $comment_ID );
+
+ /*
+ * wp_notify_postauthor() checks if notifying the author of their own comment.
+ * By default, it won't, but filters can override this.
+ */
+ if ( ! $maybe_notify ) {
+ return false;
+ }
+
+ // Only send notifications for approved comments.
+ if ( ! isset( $comment->comment_approved ) || 'spam' === $comment->comment_approved || ! $comment->comment_approved ) {
+ return false;
+ }
+
+ return wp_notify_postauthor( $comment_ID );
+}
+
+/**
+ * Sets the status of a comment.
+ *
+ * The 'wp_set_comment_status' action is called after the comment is handled.
+ * If the comment status is not in the list, then false is returned.
+ *
+ * @since 1.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
+ * @param string $comment_status New comment status, either 'hold', 'approve', 'spam', or 'trash'.
+ * @param bool $wp_error Whether to return a WP_Error object if there is a failure. Default is false.
+ * @return bool|WP_Error True on success, false or WP_Error on failure.
+ */
+function wp_set_comment_status($comment_id, $comment_status, $wp_error = false) {
+ global $wpdb;
+
+ switch ( $comment_status ) {
+ case 'hold':
+ case '0':
+ $status = '0';
+ break;
+ case 'approve':
+ case '1':
+ $status = '1';
+ add_action( 'wp_set_comment_status', 'wp_new_comment_notify_postauthor' );
+ break;
+ case 'spam':
+ $status = 'spam';
+ break;
+ case 'trash':
+ $status = 'trash';
+ break;
+ default:
+ return false;
+ }
+
+ $comment_old = clone get_comment($comment_id);
+
+ if ( !$wpdb->update( $wpdb->comments, array('comment_approved' => $status), array( 'comment_ID' => $comment_old->comment_ID ) ) ) {
+ if ( $wp_error )
+ return new WP_Error('db_update_error', __('Could not update comment status'), $wpdb->last_error);
+ else
+ return false;
+ }
+
+ clean_comment_cache( $comment_old->comment_ID );
+
+ $comment = get_comment( $comment_old->comment_ID );
+
+ /**
+ * Fires immediately before transitioning a comment's status from one to another
+ * in the database.
+ *
+ * @since 1.5.0
+ *
+ * @param int $comment_id Comment ID.
+ * @param string|bool $comment_status Current comment status. Possible values include
+ * 'hold', 'approve', 'spam', 'trash', or false.
+ */
+ do_action( 'wp_set_comment_status', $comment->comment_ID, $comment_status );
+
+ wp_transition_comment_status($comment_status, $comment_old->comment_approved, $comment);
+
+ wp_update_comment_count($comment->comment_post_ID);
+
+ return true;
+}
+
+/**
+ * Updates an existing comment in the database.
+ *
+ * Filters the comment and makes sure certain fields are valid before updating.
+ *
+ * @since 2.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array $commentarr Contains information on the comment.
+ * @return int Comment was updated if value is 1, or was not updated if value is 0.
+ */
+function wp_update_comment($commentarr) {
+ global $wpdb;
+
+ // First, get all of the original fields
+ $comment = get_comment($commentarr['comment_ID'], ARRAY_A);
+ if ( empty( $comment ) ) {
+ return 0;
+ }
+
+ // Make sure that the comment post ID is valid (if specified).
+ if ( isset( $commentarr['comment_post_ID'] ) && ! get_post( $commentarr['comment_post_ID'] ) ) {
+ return 0;
+ }
+
+ // Escape data pulled from DB.
+ $comment = wp_slash($comment);
+
+ $old_status = $comment['comment_approved'];
+
+ // Merge old and new fields with new fields overwriting old ones.
+ $commentarr = array_merge($comment, $commentarr);
+
+ $commentarr = wp_filter_comment( $commentarr );
+
+ // Now extract the merged array.
+ $data = wp_unslash( $commentarr );
+
+ /**
+ * Filter the comment content before it is updated in the database.
+ *
+ * @since 1.5.0
+ *
+ * @param string $comment_content The comment data.
+ */
+ $data['comment_content'] = apply_filters( 'comment_save_pre', $data['comment_content'] );
+
+ $data['comment_date_gmt'] = get_gmt_from_date( $data['comment_date'] );
+
+ if ( ! isset( $data['comment_approved'] ) ) {
+ $data['comment_approved'] = 1;
+ } elseif ( 'hold' == $data['comment_approved'] ) {
+ $data['comment_approved'] = 0;
+ } elseif ( 'approve' == $data['comment_approved'] ) {
+ $data['comment_approved'] = 1;
+ }
+
+ $comment_ID = $data['comment_ID'];
+ $comment_post_ID = $data['comment_post_ID'];
+ $keys = array( 'comment_post_ID', 'comment_content', 'comment_author', 'comment_author_email', 'comment_approved', 'comment_karma', 'comment_author_url', 'comment_date', 'comment_date_gmt', 'comment_type', 'comment_parent', 'user_id' );
+ $data = wp_array_slice_assoc( $data, $keys );
+ $rval = $wpdb->update( $wpdb->comments, $data, compact( 'comment_ID' ) );
+
+ clean_comment_cache( $comment_ID );
+ wp_update_comment_count( $comment_post_ID );
+ /**
+ * Fires immediately after a comment is updated in the database.
+ *
+ * The hook also fires immediately before comment status transition hooks are fired.
+ *
+ * @since 1.2.0
+ *
+ * @param int $comment_ID The comment ID.
+ */
+ do_action( 'edit_comment', $comment_ID );
+ $comment = get_comment($comment_ID);
+ wp_transition_comment_status($comment->comment_approved, $old_status, $comment);
+ return $rval;
+}
+
+/**
+ * Whether to defer comment counting.
+ *
+ * When setting $defer to true, all post comment counts will not be updated
+ * until $defer is set to false. When $defer is set to false, then all
+ * previously deferred updated post comment counts will then be automatically
+ * updated without having to call wp_update_comment_count() after.
+ *
+ * @since 2.5.0
+ * @staticvar bool $_defer
+ *
+ * @param bool $defer
+ * @return bool
+ */
+function wp_defer_comment_counting($defer=null) {
+ static $_defer = false;
+
+ if ( is_bool($defer) ) {
+ $_defer = $defer;
+ // flush any deferred counts
+ if ( !$defer )
+ wp_update_comment_count( null, true );
+ }
+
+ return $_defer;
+}
+
+/**
+ * Updates the comment count for post(s).
+ *
+ * When $do_deferred is false (is by default) and the comments have been set to
+ * be deferred, the post_id will be added to a queue, which will be updated at a
+ * later date and only updated once per post ID.
+ *
+ * If the comments have not be set up to be deferred, then the post will be
+ * updated. When $do_deferred is set to true, then all previous deferred post
+ * IDs will be updated along with the current $post_id.
+ *
+ * @since 2.1.0
+ * @see wp_update_comment_count_now() For what could cause a false return value
+ *
+ * @staticvar array $_deferred
+ *
+ * @param int $post_id Post ID
+ * @param bool $do_deferred Whether to process previously deferred post comment counts
+ * @return bool|void True on success, false on failure
+ */
+function wp_update_comment_count($post_id, $do_deferred=false) {
+ static $_deferred = array();
+
+ if ( $do_deferred ) {
+ $_deferred = array_unique($_deferred);
+ foreach ( $_deferred as $i => $_post_id ) {
+ wp_update_comment_count_now($_post_id);
+ unset( $_deferred[$i] ); /** @todo Move this outside of the foreach and reset $_deferred to an array instead */
+ }
+ }
+
+ if ( wp_defer_comment_counting() ) {
+ $_deferred[] = $post_id;
+ return true;
+ }
+ elseif ( $post_id ) {
+ return wp_update_comment_count_now($post_id);
+ }
+
+}
+
+/**
+ * Updates the comment count for the post.
+ *
+ * @since 2.5.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $post_id Post ID
+ * @return bool True on success, false on '0' $post_id or if post with ID does not exist.
+ */
+function wp_update_comment_count_now($post_id) {
+ global $wpdb;
+ $post_id = (int) $post_id;
+ if ( !$post_id )
+ return false;
+
+ wp_cache_delete( 'comments-0', 'counts' );
+ wp_cache_delete( "comments-{$post_id}", 'counts' );
+
+ if ( !$post = get_post($post_id) )
+ return false;
+
+ $old = (int) $post->comment_count;
+ $new = (int) $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1'", $post_id) );
+ $wpdb->update( $wpdb->posts, array('comment_count' => $new), array('ID' => $post_id) );
+
+ clean_post_cache( $post );
+
+ /**
+ * Fires immediately after a post's comment count is updated in the database.
+ *
+ * @since 2.3.0
+ *
+ * @param int $post_id Post ID.
+ * @param int $new The new comment count.
+ * @param int $old The old comment count.
+ */
+ do_action( 'wp_update_comment_count', $post_id, $new, $old );
+ /** This action is documented in wp-includes/post-functions.php */
+ do_action( 'edit_post', $post_id, $post );
+
+ return true;
+}
+
+//
+// Ping and trackback functions.
+//
+
+/**
+ * Finds a pingback server URI based on the given URL.
+ *
+ * Checks the HTML for the rel="pingback" link and x-pingback headers. It does
+ * a check for the x-pingback headers first and returns that, if available. The
+ * check for the rel="pingback" has more overhead than just the header.
+ *
+ * @since 1.5.0
+ *
+ * @param string $url URL to ping.
+ * @param int $deprecated Not Used.
+ * @return false|string False on failure, string containing URI on success.
+ */
+function discover_pingback_server_uri( $url, $deprecated = '' ) {
+ if ( !empty( $deprecated ) )
+ _deprecated_argument( __FUNCTION__, '2.7' );
+
+ $pingback_str_dquote = 'rel="pingback"';
+ $pingback_str_squote = 'rel=\'pingback\'';
+
+ /** @todo Should use Filter Extension or custom preg_match instead. */
+ $parsed_url = parse_url($url);
+
+ if ( ! isset( $parsed_url['host'] ) ) // Not an URL. This should never happen.
+ return false;
+
+ //Do not search for a pingback server on our own uploads
+ $uploads_dir = wp_upload_dir();
+ if ( 0 === strpos($url, $uploads_dir['baseurl']) )
+ return false;
+
+ $response = wp_safe_remote_head( $url, array( 'timeout' => 2, 'httpversion' => '1.0' ) );
+
+ if ( is_wp_error( $response ) )
+ return false;
+
+ if ( wp_remote_retrieve_header( $response, 'x-pingback' ) )
+ return wp_remote_retrieve_header( $response, 'x-pingback' );
+
+ // Not an (x)html, sgml, or xml page, no use going further.
+ if ( preg_match('#(image|audio|video|model)/#is', wp_remote_retrieve_header( $response, 'content-type' )) )
+ return false;
+
+ // Now do a GET since we're going to look in the html headers (and we're sure it's not a binary file)
+ $response = wp_safe_remote_get( $url, array( 'timeout' => 2, 'httpversion' => '1.0' ) );
+
+ if ( is_wp_error( $response ) )
+ return false;
+
+ $contents = wp_remote_retrieve_body( $response );
+
+ $pingback_link_offset_dquote = strpos($contents, $pingback_str_dquote);
+ $pingback_link_offset_squote = strpos($contents, $pingback_str_squote);
+ if ( $pingback_link_offset_dquote || $pingback_link_offset_squote ) {
+ $quote = ($pingback_link_offset_dquote) ? '"' : '\'';
+ $pingback_link_offset = ($quote=='"') ? $pingback_link_offset_dquote : $pingback_link_offset_squote;
+ $pingback_href_pos = @strpos($contents, 'href=', $pingback_link_offset);
+ $pingback_href_start = $pingback_href_pos+6;
+ $pingback_href_end = @strpos($contents, $quote, $pingback_href_start);
+ $pingback_server_url_len = $pingback_href_end - $pingback_href_start;
+ $pingback_server_url = substr($contents, $pingback_href_start, $pingback_server_url_len);
+
+ // We may find rel="pingback" but an incomplete pingback URL
+ if ( $pingback_server_url_len > 0 ) { // We got it!
+ return $pingback_server_url;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Perform all pingbacks, enclosures, trackbacks, and send to pingback services.
+ *
+ * @since 2.1.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ */
+function do_all_pings() {
+ global $wpdb;
+
+ // Do pingbacks
+ while ($ping = $wpdb->get_row("SELECT ID, post_content, meta_id FROM {$wpdb->posts}, {$wpdb->postmeta} WHERE {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_pingme' LIMIT 1")) {
+ delete_metadata_by_mid( 'post', $ping->meta_id );
+ pingback( $ping->post_content, $ping->ID );
+ }
+
+ // Do Enclosures
+ while ($enclosure = $wpdb->get_row("SELECT ID, post_content, meta_id FROM {$wpdb->posts}, {$wpdb->postmeta} WHERE {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_encloseme' LIMIT 1")) {
+ delete_metadata_by_mid( 'post', $enclosure->meta_id );
+ do_enclose( $enclosure->post_content, $enclosure->ID );
+ }
+
+ // Do Trackbacks
+ $trackbacks = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE to_ping <> '' AND post_status = 'publish'");
+ if ( is_array($trackbacks) )
+ foreach ( $trackbacks as $trackback )
+ do_trackbacks($trackback);
+
+ //Do Update Services/Generic Pings
+ generic_ping();
+}
+
+/**
+ * Perform trackbacks.
+ *
+ * @since 1.5.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $post_id Post ID to do trackbacks on.
+ */
+function do_trackbacks($post_id) {
+ global $wpdb;
+
+ $post = get_post( $post_id );
+ $to_ping = get_to_ping($post_id);
+ $pinged = get_pung($post_id);
+ if ( empty($to_ping) ) {
+ $wpdb->update($wpdb->posts, array('to_ping' => ''), array('ID' => $post_id) );
+ return;
+ }
+
+ if ( empty($post->post_excerpt) ) {
+ /** This filter is documented in wp-includes/post-template.php */
+ $excerpt = apply_filters( 'the_content', $post->post_content, $post->ID );
+ } else {
+ /** This filter is documented in wp-includes/post-template.php */
+ $excerpt = apply_filters( 'the_excerpt', $post->post_excerpt );
+ }
+
+ $excerpt = str_replace(']]>', ']]>', $excerpt);
+ $excerpt = wp_html_excerpt($excerpt, 252, '…');
+
+ /** This filter is documented in wp-includes/post-template.php */
+ $post_title = apply_filters( 'the_title', $post->post_title, $post->ID );
+ $post_title = strip_tags($post_title);
+
+ if ( $to_ping ) {
+ foreach ( (array) $to_ping as $tb_ping ) {
+ $tb_ping = trim($tb_ping);
+ if ( !in_array($tb_ping, $pinged) ) {
+ trackback($tb_ping, $post_title, $excerpt, $post_id);
+ $pinged[] = $tb_ping;
+ } else {
+ $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, '')) WHERE ID = %d", $tb_ping, $post_id) );
+ }
+ }
+ }
+}
+
+/**
+ * Sends pings to all of the ping site services.
+ *
+ * @since 1.2.0
+ *
+ * @param int $post_id Post ID.
+ * @return int Same as Post ID from parameter
+ */
+function generic_ping( $post_id = 0 ) {
+ $services = get_option('ping_sites');
+
+ $services = explode("\n", $services);
+ foreach ( (array) $services as $service ) {
+ $service = trim($service);
+ if ( '' != $service )
+ weblog_ping($service);
+ }
+
+ return $post_id;
+}
+
+/**
+ * Pings back the links found in a post.
+ *
+ * @since 0.71
+ *
+ * @global string $wp_version
+ *
+ * @param string $content Post content to check for links.
+ * @param int $post_ID Post ID.
+ */
+function pingback($content, $post_ID) {
+ global $wp_version;
+ include_once(ABSPATH . WPINC . '/class-IXR.php');
+ include_once(ABSPATH . WPINC . '/class-wp-http-ixr-client.php');
+
+ // original code by Mort (http://mort.mine.nu:8080)
+ $post_links = array();
+
+ $pung = get_pung($post_ID);
+
+ // Step 1
+ // Parsing the post, external links (if any) are stored in the $post_links array
+ $post_links_temp = wp_extract_urls( $content );
+
+ // Step 2.
+ // Walking thru the links array
+ // first we get rid of links pointing to sites, not to specific files
+ // Example:
+ // http://dummy-weblog.org
+ // http://dummy-weblog.org/
+ // http://dummy-weblog.org/post.php
+ // We don't wanna ping first and second types, even if they have a valid <link/>
+
+ foreach ( (array) $post_links_temp as $link_test ) :
+ if ( !in_array($link_test, $pung) && (url_to_postid($link_test) != $post_ID) // If we haven't pung it already and it isn't a link to itself
+ && !is_local_attachment($link_test) ) : // Also, let's never ping local attachments.
+ if ( $test = @parse_url($link_test) ) {
+ if ( isset($test['query']) )
+ $post_links[] = $link_test;
+ elseif ( isset( $test['path'] ) && ( $test['path'] != '/' ) && ( $test['path'] != '' ) )
+ $post_links[] = $link_test;
+ }
+ endif;
+ endforeach;
+
+ $post_links = array_unique( $post_links );
+ /**
+ * Fires just before pinging back links found in a post.
+ *
+ * @since 2.0.0
+ *
+ * @param array &$post_links An array of post links to be checked, passed by reference.
+ * @param array &$pung Whether a link has already been pinged, passed by reference.
+ * @param int $post_ID The post ID.
+ */
+ do_action_ref_array( 'pre_ping', array( &$post_links, &$pung, $post_ID ) );
+
+ foreach ( (array) $post_links as $pagelinkedto ) {
+ $pingback_server_url = discover_pingback_server_uri( $pagelinkedto );
+
+ if ( $pingback_server_url ) {
+ @ set_time_limit( 60 );
+ // Now, the RPC call
+ $pagelinkedfrom = get_permalink($post_ID);
+
+ // using a timeout of 3 seconds should be enough to cover slow servers
+ $client = new WP_HTTP_IXR_Client($pingback_server_url);
+ $client->timeout = 3;
+ /**
+ * Filter the user agent sent when pinging-back a URL.
+ *
+ * @since 2.9.0
+ *
+ * @param string $concat_useragent The user agent concatenated with ' -- WordPress/'
+ * and the WordPress version.
+ * @param string $useragent The useragent.
+ * @param string $pingback_server_url The server URL being linked to.
+ * @param string $pagelinkedto URL of page linked to.
+ * @param string $pagelinkedfrom URL of page linked from.
+ */
+ $client->useragent = apply_filters( 'pingback_useragent', $client->useragent . ' -- WordPress/' . $wp_version, $client->useragent, $pingback_server_url, $pagelinkedto, $pagelinkedfrom );
+ // when set to true, this outputs debug messages by itself
+ $client->debug = false;
+
+ if ( $client->query('pingback.ping', $pagelinkedfrom, $pagelinkedto) || ( isset($client->error->code) && 48 == $client->error->code ) ) // Already registered
+ add_ping( $post_ID, $pagelinkedto );
+ }
+ }
+}
+
+/**
+ * Check whether blog is public before returning sites.
+ *
+ * @since 2.1.0
+ *
+ * @param mixed $sites Will return if blog is public, will not return if not public.
+ * @return mixed Empty string if blog is not public, returns $sites, if site is public.
+ */
+function privacy_ping_filter($sites) {
+ if ( '0' != get_option('blog_public') )
+ return $sites;
+ else
+ return '';
+}
+
+/**
+ * Send a Trackback.
+ *
+ * Updates database when sending trackback to prevent duplicates.
+ *
+ * @since 0.71
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $trackback_url URL to send trackbacks.
+ * @param string $title Title of post.
+ * @param string $excerpt Excerpt of post.
+ * @param int $ID Post ID.
+ * @return int|false|void Database query from update.
+ */
+function trackback($trackback_url, $title, $excerpt, $ID) {
+ global $wpdb;
+
+ if ( empty($trackback_url) )
+ return;
+
+ $options = array();
+ $options['timeout'] = 10;
+ $options['body'] = array(
+ 'title' => $title,
+ 'url' => get_permalink($ID),
+ 'blog_name' => get_option('blogname'),
+ 'excerpt' => $excerpt
+ );
+
+ $response = wp_safe_remote_post( $trackback_url, $options );
+
+ if ( is_wp_error( $response ) )
+ return;
+
+ $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET pinged = CONCAT(pinged, '\n', %s) WHERE ID = %d", $trackback_url, $ID) );
+ return $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, '')) WHERE ID = %d", $trackback_url, $ID) );
+}
+
+/**
+ * Send a pingback.
+ *
+ * @since 1.2.0
+ *
+ * @global string $wp_version
+ *
+ * @param string $server Host of blog to connect to.
+ * @param string $path Path to send the ping.
+ */
+function weblog_ping($server = '', $path = '') {
+ global $wp_version;
+ include_once(ABSPATH . WPINC . '/class-IXR.php');
+ include_once(ABSPATH . WPINC . '/class-wp-http-ixr-client.php');
+
+ // using a timeout of 3 seconds should be enough to cover slow servers
+ $client = new WP_HTTP_IXR_Client($server, ((!strlen(trim($path)) || ('/' == $path)) ? false : $path));
+ $client->timeout = 3;
+ $client->useragent .= ' -- WordPress/'.$wp_version;
+
+ // when set to true, this outputs debug messages by itself
+ $client->debug = false;
+ $home = trailingslashit( home_url() );
+ if ( !$client->query('weblogUpdates.extendedPing', get_option('blogname'), $home, get_bloginfo('rss2_url') ) ) // then try a normal ping
+ $client->query('weblogUpdates.ping', get_option('blogname'), $home);
+}
+
+/**
+ * Default filter attached to pingback_ping_source_uri to validate the pingback's Source URI
+ *
+ * @since 3.5.1
+ * @see wp_http_validate_url()
+ *
+ * @param string $source_uri
+ * @return string
+ */
+function pingback_ping_source_uri( $source_uri ) {
+ return (string) wp_http_validate_url( $source_uri );
+}
+
+/**
+ * Default filter attached to xmlrpc_pingback_error.
+ *
+ * Returns a generic pingback error code unless the error code is 48,
+ * which reports that the pingback is already registered.
+ *
+ * @since 3.5.1
+ * @link http://www.hixie.ch/specs/pingback/pingback#TOC3
+ *
+ * @param IXR_Error $ixr_error
+ * @return IXR_Error
+ */
+function xmlrpc_pingback_error( $ixr_error ) {
+ if ( $ixr_error->code === 48 )
+ return $ixr_error;
+ return new IXR_Error( 0, '' );
+}
+
+//
+// Cache
+//
+
+/**
+ * Removes comment ID from the comment cache.
+ *
+ * @since 2.3.0
+ *
+ * @param int|array $ids Comment ID or array of comment IDs to remove from cache
+ */
+function clean_comment_cache($ids) {
+ foreach ( (array) $ids as $id ) {
+ wp_cache_delete( $id, 'comment' );
+ }
+
+ wp_cache_set( 'last_changed', microtime(), 'comment' );
+}
+
+/**
+ * Updates the comment cache of given comments.
+ *
+ * Will add the comments in $comments to the cache. If comment ID already exists
+ * in the comment cache then it will not be updated. The comment is added to the
+ * cache using the comment group with the key using the ID of the comments.
+ *
+ * @since 2.3.0
+ * @since 4.4.0 Introduced the `$update_meta_cache` parameter.
+ *
+ * @param array $comments Array of comment row objects
+ * @param bool $update_meta_cache Whether to update commentmeta cache. Default true.
+ */
+function update_comment_cache( $comments, $update_meta_cache = true ) {
+ foreach ( (array) $comments as $comment )
+ wp_cache_add($comment->comment_ID, $comment, 'comment');
+
+ if ( $update_meta_cache ) {
+ // Avoid `wp_list_pluck()` in case `$comments` is passed by reference.
+ $comment_ids = array();
+ foreach ( $comments as $comment ) {
+ $comment_ids[] = $comment->comment_ID;
+ }
+ update_meta_cache( 'comment', $comment_ids );
+ }
+}
+
+/**
+ * Adds any comments from the given IDs to the cache that do not already exist in cache.
+ *
+ * @since 4.4.0
+ * @access private
+ *
+ * @see update_comment_cache()
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array $comment_ids Array of comment IDs.
+ * @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
+ */
+function _prime_comment_caches( $comment_ids, $update_meta_cache = true ) {
+ global $wpdb;
+
+ $non_cached_ids = _get_non_cached_ids( $comment_ids, 'comment' );
+ if ( !empty( $non_cached_ids ) ) {
+ $fresh_comments = $wpdb->get_results( sprintf( "SELECT $wpdb->comments.* FROM $wpdb->comments WHERE comment_ID IN (%s)", join( ",", array_map( 'intval', $non_cached_ids ) ) ) );
+
+ update_comment_cache( $fresh_comments, $update_meta_cache );
+ }
+}
+
+//
+// Internal
+//
+
+/**
+ * Close comments on old posts on the fly, without any extra DB queries. Hooked to the_posts.
+ *
+ * @access private
+ * @since 2.7.0
+ *
+ * @param WP_Post $posts Post data object.
+ * @param WP_Query $query Query object.
+ * @return array
+ */
+function _close_comments_for_old_posts( $posts, $query ) {
+ if ( empty( $posts ) || ! $query->is_singular() || ! get_option( 'close_comments_for_old_posts' ) )
+ return $posts;
+
+ /**
+ * Filter the list of post types to automatically close comments for.
+ *
+ * @since 3.2.0
+ *
+ * @param array $post_types An array of registered post types. Default array with 'post'.
+ */
+ $post_types = apply_filters( 'close_comments_for_post_types', array( 'post' ) );
+ if ( ! in_array( $posts[0]->post_type, $post_types ) )
+ return $posts;
+
+ $days_old = (int) get_option( 'close_comments_days_old' );
+ if ( ! $days_old )
+ return $posts;
+
+ if ( time() - strtotime( $posts[0]->post_date_gmt ) > ( $days_old * DAY_IN_SECONDS ) ) {
+ $posts[0]->comment_status = 'closed';
+ $posts[0]->ping_status = 'closed';
+ }
+
+ return $posts;
+}
+
+/**
+ * Close comments on an old post. Hooked to comments_open and pings_open.
+ *
+ * @access private
+ * @since 2.7.0
+ *
+ * @param bool $open Comments open or closed
+ * @param int $post_id Post ID
+ * @return bool $open
+ */
+function _close_comments_for_old_post( $open, $post_id ) {
+ if ( ! $open )
+ return $open;
+
+ if ( !get_option('close_comments_for_old_posts') )
+ return $open;
+
+ $days_old = (int) get_option('close_comments_days_old');
+ if ( !$days_old )
+ return $open;
+
+ $post = get_post($post_id);
+
+ /** This filter is documented in wp-includes/comment-functions.php */
+ $post_types = apply_filters( 'close_comments_for_post_types', array( 'post' ) );
+ if ( ! in_array( $post->post_type, $post_types ) )
+ return $open;
+
+ // Undated drafts should not show up as comments closed.
+ if ( '0000-00-00 00:00:00' === $post->post_date_gmt ) {
+ return $open;
+ }
+
+ if ( time() - strtotime( $post->post_date_gmt ) > ( $days_old * DAY_IN_SECONDS ) )
+ return false;
+
+ return $open;
+}
+
+/**
+ * Handles the submission of a comment, usually posted to wp-comments-post.php via a comment form.
+ *
+ * This function expects unslashed data, as opposed to functions such as `wp_new_comment()` which
+ * expect slashed data.
+ *
+ * @since 4.4.0
+ *
+ * @param array $comment_data {
+ * Comment data.
+ *
+ * @type string|int $comment_post_ID The ID of the post that relates to the comment.
+ * @type string $author The name of the comment author.
+ * @type string $email The comment author email address.
+ * @type string $url The comment author URL.
+ * @type string $comment The content of the comment.
+ * @type string|int $comment_parent The ID of this comment's parent, if any. Default 0.
+ * @type string $_wp_unfiltered_html_comment The nonce value for allowing unfiltered HTML.
+ * }
+ * @return WP_Comment|WP_Error A WP_Comment object on success, a WP_Error object on failure.
+ */
+function wp_handle_comment_submission( $comment_data ) {
+
+ $comment_post_ID = $comment_parent = 0;
+ $comment_author = $comment_author_email = $comment_author_url = $comment_content = $_wp_unfiltered_html_comment = null;
+
+ if ( isset( $comment_data['comment_post_ID'] ) ) {
+ $comment_post_ID = (int) $comment_data['comment_post_ID'];
+ }
+ if ( isset( $comment_data['author'] ) && is_string( $comment_data['author'] ) ) {
+ $comment_author = trim( strip_tags( $comment_data['author'] ) );
+ }
+ if ( isset( $comment_data['email'] ) && is_string( $comment_data['email'] ) ) {
+ $comment_author_email = trim( $comment_data['email'] );
+ }
+ if ( isset( $comment_data['url'] ) && is_string( $comment_data['url'] ) ) {
+ $comment_author_url = trim( $comment_data['url'] );
+ }
+ if ( isset( $comment_data['comment'] ) && is_string( $comment_data['comment'] ) ) {
+ $comment_content = trim( $comment_data['comment'] );
+ }
+ if ( isset( $comment_data['comment_parent'] ) ) {
+ $comment_parent = absint( $comment_data['comment_parent'] );
+ }
+ if ( isset( $comment_data['_wp_unfiltered_html_comment'] ) && is_string( $comment_data['_wp_unfiltered_html_comment'] ) ) {
+ $_wp_unfiltered_html_comment = trim( $comment_data['_wp_unfiltered_html_comment'] );
+ }
+
+ $post = get_post( $comment_post_ID );
+
+ if ( empty( $post->comment_status ) ) {
+
+ /**
+ * Fires when a comment is attempted on a post that does not exist.
+ *
+ * @since 1.5.0
+ *
+ * @param int $comment_post_ID Post ID.
+ */
+ do_action( 'comment_id_not_found', $comment_post_ID );
+
+ return new WP_Error( 'comment_id_not_found' );
+
+ }
+
+ // get_post_status() will get the parent status for attachments.
+ $status = get_post_status( $post );
+
+ $status_obj = get_post_status_object( $status );
+
+ if ( ! comments_open( $comment_post_ID ) ) {
+
+ /**
+ * Fires when a comment is attempted on a post that has comments closed.
+ *
+ * @since 1.5.0
+ *
+ * @param int $comment_post_ID Post ID.
+ */
+ do_action( 'comment_closed', $comment_post_ID );
+
+ return new WP_Error( 'comment_closed', __( 'Sorry, comments are closed for this item.' ), 403 );
+
+ } elseif ( 'trash' == $status ) {
+
+ /**
+ * Fires when a comment is attempted on a trashed post.
+ *
+ * @since 2.9.0
+ *
+ * @param int $comment_post_ID Post ID.
+ */
+ do_action( 'comment_on_trash', $comment_post_ID );
+
+ return new WP_Error( 'comment_on_trash' );
+
+ } elseif ( ! $status_obj->public && ! $status_obj->private ) {
+
+ /**
+ * Fires when a comment is attempted on a post in draft mode.
+ *
+ * @since 1.5.1
+ *
+ * @param int $comment_post_ID Post ID.
+ */
+ do_action( 'comment_on_draft', $comment_post_ID );
+
+ return new WP_Error( 'comment_on_draft' );
+
+ } elseif ( post_password_required( $comment_post_ID ) ) {
+
+ /**
+ * Fires when a comment is attempted on a password-protected post.
+ *
+ * @since 2.9.0
+ *
+ * @param int $comment_post_ID Post ID.
+ */
+ do_action( 'comment_on_password_protected', $comment_post_ID );
+
+ return new WP_Error( 'comment_on_password_protected' );
+
+ } else {
+
+ /**
+ * Fires before a comment is posted.
+ *
+ * @since 2.8.0
+ *
+ * @param int $comment_post_ID Post ID.
+ */
+ do_action( 'pre_comment_on_post', $comment_post_ID );
+
+ }
+
+ // If the user is logged in
+ $user = wp_get_current_user();
+ if ( $user->exists() ) {
+ if ( empty( $user->display_name ) ) {
+ $user->display_name=$user->user_login;
+ }
+ $comment_author = $user->display_name;
+ $comment_author_email = $user->user_email;
+ $comment_author_url = $user->user_url;
+ $user_id = $user->ID;
+ if ( current_user_can( 'unfiltered_html' ) ) {
+ if ( ! isset( $comment_data['_wp_unfiltered_html_comment'] )
+ || ! wp_verify_nonce( $comment_data['_wp_unfiltered_html_comment'], 'unfiltered-html-comment_' . $comment_post_ID )
+ ) {
+ kses_remove_filters(); // start with a clean slate
+ kses_init_filters(); // set up the filters
+ }
+ }
+ } else {
+ if ( get_option( 'comment_registration' ) || 'private' == $status ) {
+ return new WP_Error( 'not_logged_in', __( 'Sorry, you must be logged in to post a comment.' ), 403 );
+ }
+ }
+
+ $comment_type = '';
+
+ if ( get_option( 'require_name_email' ) && ! $user->exists() ) {
+ if ( 6 > strlen( $comment_author_email ) || '' == $comment_author ) {
+ return new WP_Error( 'require_name_email', __( '<strong>ERROR</strong>: please fill the required fields (name, email).' ), 200 );
+ } elseif ( ! is_email( $comment_author_email ) ) {
+ return new WP_Error( 'require_valid_email', __( '<strong>ERROR</strong>: please enter a valid email address.' ), 200 );
+ }
+ }
+
+ if ( '' == $comment_content ) {
+ return new WP_Error( 'require_valid_comment', __( '<strong>ERROR</strong>: please type a comment.' ), 200 );
+ }
+
+ $commentdata = compact(
+ 'comment_post_ID',
+ 'comment_author',
+ 'comment_author_email',
+ 'comment_author_url',
+ 'comment_content',
+ 'comment_type',
+ 'comment_parent',
+ 'user_id'
+ );
+
+ $comment_id = wp_new_comment( wp_slash( $commentdata ) );
+ if ( ! $comment_id ) {
+ return new WP_Error( 'comment_save_error', __( '<strong>ERROR</strong>: The comment could not be saved. Please try again later.' ), 500 );
+ }
+
+ return get_comment( $comment_id );
+
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesembedfunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/embed-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/embed-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/embed-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,1047 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * oEmbed API: Top-level oEmbed functionality
- *
- * @package WordPress
- * @subpackage oEmbed
- * @since 4.4.0
- */
-
-/**
- * Registers an embed handler.
- *
- * Should probably only be used for sites that do not support oEmbed.
- *
- * @since 2.9.0
- *
- * @global WP_Embed $wp_embed
- *
- * @param string $id An internal ID/name for the handler. Needs to be unique.
- * @param string $regex The regex that will be used to see if this handler should be used for a URL.
- * @param callable $callback The callback function that will be called if the regex is matched.
- * @param int $priority Optional. Used to specify the order in which the registered handlers will
- * be tested. Default 10.
- */
-function wp_embed_register_handler( $id, $regex, $callback, $priority = 10 ) {
- global $wp_embed;
- $wp_embed->register_handler( $id, $regex, $callback, $priority );
-}
-
-/**
- * Unregisters a previously-registered embed handler.
- *
- * @since 2.9.0
- *
- * @global WP_Embed $wp_embed
- *
- * @param string $id The handler ID that should be removed.
- * @param int $priority Optional. The priority of the handler to be removed. Default 10.
- */
-function wp_embed_unregister_handler( $id, $priority = 10 ) {
- global $wp_embed;
- $wp_embed->unregister_handler( $id, $priority );
-}
-
-/**
- * Creates default array of embed parameters.
- *
- * The width defaults to the content width as specified by the theme. If the
- * theme does not specify a content width, then 500px is used.
- *
- * The default height is 1.5 times the width, or 1000px, whichever is smaller.
- *
- * The 'embed_defaults' filter can be used to adjust either of these values.
- *
- * @since 2.9.0
- *
- * @global int $content_width
- *
- * @param string $url Optional. The URL that should be embedded. Default empty.
- *
- * @return array Default embed parameters.
- */
-function wp_embed_defaults( $url = '' ) {
- if ( ! empty( $GLOBALS['content_width'] ) )
- $width = (int) $GLOBALS['content_width'];
-
- if ( empty( $width ) )
- $width = 500;
-
- $height = min( ceil( $width * 1.5 ), 1000 );
-
- /**
- * Filter the default array of embed dimensions.
- *
- * @since 2.9.0
- *
- * @param array $size An array of embed width and height values
- * in pixels (in that order).
- * @param string $url The URL that should be embedded.
- */
- return apply_filters( 'embed_defaults', compact( 'width', 'height' ), $url );
-}
-
-/**
- * Attempts to fetch the embed HTML for a provided URL using oEmbed.
- *
- * @since 2.9.0
- *
- * @see WP_oEmbed
- *
- * @param string $url The URL that should be embedded.
- * @param array $args Optional. Additional arguments and parameters for retrieving embed HTML.
- * Default empty.
- * @return false|string False on failure or the embed HTML on success.
- */
-function wp_oembed_get( $url, $args = '' ) {
- require_once( ABSPATH . WPINC . '/class-oembed.php' );
- $oembed = _wp_oembed_get_object();
- return $oembed->get_html( $url, $args );
-}
-
-/**
- * Adds a URL format and oEmbed provider URL pair.
- *
- * @since 2.9.0
- *
- * @see WP_oEmbed
- *
- * @param string $format The format of URL that this provider can handle. You can use asterisks
- * as wildcards.
- * @param string $provider The URL to the oEmbed provider.
- * @param boolean $regex Optional. Whether the `$format` parameter is in a RegEx format. Default false.
- */
-function wp_oembed_add_provider( $format, $provider, $regex = false ) {
- require_once( ABSPATH . WPINC . '/class-oembed.php' );
-
- if ( did_action( 'plugins_loaded' ) ) {
- $oembed = _wp_oembed_get_object();
- $oembed->providers[$format] = array( $provider, $regex );
- } else {
- WP_oEmbed::_add_provider_early( $format, $provider, $regex );
- }
-}
-
-/**
- * Removes an oEmbed provider.
- *
- * @since 3.5.0
- *
- * @see WP_oEmbed
- *
- * @param string $format The URL format for the oEmbed provider to remove.
- * @return bool Was the provider removed successfully?
- */
-function wp_oembed_remove_provider( $format ) {
- require_once( ABSPATH . WPINC . '/class-oembed.php' );
-
- if ( did_action( 'plugins_loaded' ) ) {
- $oembed = _wp_oembed_get_object();
-
- if ( isset( $oembed->providers[ $format ] ) ) {
- unset( $oembed->providers[ $format ] );
- return true;
- }
- } else {
- WP_oEmbed::_remove_provider_early( $format );
- }
-
- return false;
-}
-
-/**
- * Determines if default embed handlers should be loaded.
- *
- * Checks to make sure that the embeds library hasn't already been loaded. If
- * it hasn't, then it will load the embeds library.
- *
- * @since 2.9.0
- *
- * @see wp_embed_register_handler()
- */
-function wp_maybe_load_embeds() {
- /**
- * Filter whether to load the default embed handlers.
- *
- * Returning a falsey value will prevent loading the default embed handlers.
- *
- * @since 2.9.0
- *
- * @param bool $maybe_load_embeds Whether to load the embeds library. Default true.
- */
- if ( ! apply_filters( 'load_default_embeds', true ) ) {
- return;
- }
-
- wp_embed_register_handler( 'youtube_embed_url', '#https?://(www.)?youtube\.com/(?:v|embed)/([^/]+)#i', 'wp_embed_handler_youtube' );
-
- wp_embed_register_handler( 'googlevideo', '#http://video\.google\.([A-Za-z.]{2,5})/videoplay\?docid=([\d-]+)(.*?)#i', 'wp_embed_handler_googlevideo' );
-
- /**
- * Filter the audio embed handler callback.
- *
- * @since 3.6.0
- *
- * @param callable $handler Audio embed handler callback function.
- */
- wp_embed_register_handler( 'audio', '#^https?://.+?\.(' . join( '|', wp_get_audio_extensions() ) . ')$#i', apply_filters( 'wp_audio_embed_handler', 'wp_embed_handler_audio' ), 9999 );
-
- /**
- * Filter the video embed handler callback.
- *
- * @since 3.6.0
- *
- * @param callable $handler Video embed handler callback function.
- */
- wp_embed_register_handler( 'video', '#^https?://.+?\.(' . join( '|', wp_get_video_extensions() ) . ')$#i', apply_filters( 'wp_video_embed_handler', 'wp_embed_handler_video' ), 9999 );
-}
-
-/**
- * The Google Video embed handler callback.
- *
- * Google Video does not support oEmbed.
- *
- * @see WP_Embed::register_handler()
- * @see WP_Embed::shortcode()
- *
- * @param array $matches The RegEx matches from the provided regex when calling wp_embed_register_handler().
- * @param array $attr Embed attributes.
- * @param string $url The original URL that was matched by the regex.
- * @param array $rawattr The original unmodified attributes.
- * @return string The embed HTML.
- */
-function wp_embed_handler_googlevideo( $matches, $attr, $url, $rawattr ) {
- // If the user supplied a fixed width AND height, use it
- if ( !empty($rawattr['width']) && !empty($rawattr['height']) ) {
- $width = (int) $rawattr['width'];
- $height = (int) $rawattr['height'];
- } else {
- list( $width, $height ) = wp_expand_dimensions( 425, 344, $attr['width'], $attr['height'] );
- }
-
- /**
- * Filter the Google Video embed output.
- *
- * @since 2.9.0
- *
- * @param string $html Google Video HTML embed markup.
- * @param array $matches The RegEx matches from the provided regex.
- * @param array $attr An array of embed attributes.
- * @param string $url The original URL that was matched by the regex.
- * @param array $rawattr The original unmodified attributes.
- */
- return apply_filters( 'embed_googlevideo', '<embed type="application/x-shockwave-flash" src="http://video.google.com/googleplayer.swf?docid=' . esc_attr($matches[2]) . '&hl=en&fs=true" style="width:' . esc_attr($width) . 'px;height:' . esc_attr($height) . 'px" allowFullScreen="true" allowScriptAccess="always" />', $matches, $attr, $url, $rawattr );
-}
-
-/**
- * YouTube iframe embed handler callback.
- *
- * Catches YouTube iframe embed URLs that are not parsable by oEmbed but can be translated into a URL that is.
- *
- * @since 4.0.0
- *
- * @global WP_Embed $wp_embed
- *
- * @param array $matches The RegEx matches from the provided regex when calling
- * wp_embed_register_handler().
- * @param array $attr Embed attributes.
- * @param string $url The original URL that was matched by the regex.
- * @param array $rawattr The original unmodified attributes.
- * @return string The embed HTML.
- */
-function wp_embed_handler_youtube( $matches, $attr, $url, $rawattr ) {
- global $wp_embed;
- $embed = $wp_embed->autoembed( "https://youtube.com/watch?v={$matches[2]}" );
-
- /**
- * Filter the YoutTube embed output.
- *
- * @since 4.0.0
- *
- * @see wp_embed_handler_youtube()
- *
- * @param string $embed YouTube embed output.
- * @param array $attr An array of embed attributes.
- * @param string $url The original URL that was matched by the regex.
- * @param array $rawattr The original unmodified attributes.
- */
- return apply_filters( 'wp_embed_handler_youtube', $embed, $attr, $url, $rawattr );
-}
-
-/**
- * Audio embed handler callback.
- *
- * @since 3.6.0
- *
- * @param array $matches The RegEx matches from the provided regex when calling wp_embed_register_handler().
- * @param array $attr Embed attributes.
- * @param string $url The original URL that was matched by the regex.
- * @param array $rawattr The original unmodified attributes.
- * @return string The embed HTML.
- */
-function wp_embed_handler_audio( $matches, $attr, $url, $rawattr ) {
- $audio = sprintf( '[audio src="%s" /]', esc_url( $url ) );
-
- /**
- * Filter the audio embed output.
- *
- * @since 3.6.0
- *
- * @param string $audio Audio embed output.
- * @param array $attr An array of embed attributes.
- * @param string $url The original URL that was matched by the regex.
- * @param array $rawattr The original unmodified attributes.
- */
- return apply_filters( 'wp_embed_handler_audio', $audio, $attr, $url, $rawattr );
-}
-
-/**
- * Video embed handler callback.
- *
- * @since 3.6.0
- *
- * @param array $matches The RegEx matches from the provided regex when calling wp_embed_register_handler().
- * @param array $attr Embed attributes.
- * @param string $url The original URL that was matched by the regex.
- * @param array $rawattr The original unmodified attributes.
- * @return string The embed HTML.
- */
-function wp_embed_handler_video( $matches, $attr, $url, $rawattr ) {
- $dimensions = '';
- if ( ! empty( $rawattr['width'] ) && ! empty( $rawattr['height'] ) ) {
- $dimensions .= sprintf( 'width="%d" ', (int) $rawattr['width'] );
- $dimensions .= sprintf( 'height="%d" ', (int) $rawattr['height'] );
- }
- $video = sprintf( '[video %s src="%s" /]', $dimensions, esc_url( $url ) );
-
- /**
- * Filter the video embed output.
- *
- * @since 3.6.0
- *
- * @param string $video Video embed output.
- * @param array $attr An array of embed attributes.
- * @param string $url The original URL that was matched by the regex.
- * @param array $rawattr The original unmodified attributes.
- */
- return apply_filters( 'wp_embed_handler_video', $video, $attr, $url, $rawattr );
-}
-
-/**
- * Registers the oEmbed REST API route.
- *
- * @since 4.4.0
- */
-function wp_oembed_register_route() {
- $controller = new WP_oEmbed_Controller();
- $controller->register_routes();
-}
-
-/**
- * Adds oEmbed discovery links in the website <head>.
- *
- * @since 4.4.0
- */
-function wp_oembed_add_discovery_links() {
- $output = '';
-
- if ( is_singular() ) {
- $output .= '<link rel="alternate" type="application/json+oembed" href="' . esc_url( get_oembed_endpoint_url( get_permalink() ) ) . '" />' . "\n";
-
- if ( class_exists( 'SimpleXMLElement' ) ) {
- $output .= '<link rel="alternate" type="text/xml+oembed" href="' . esc_url( get_oembed_endpoint_url( get_permalink(), 'xml' ) ) . '" />' . "\n";
- }
- }
-
- /**
- * Filter the oEmbed discovery links HTML.
- *
- * @since 4.4.0
- *
- * @param string $output HTML of the discovery links.
- */
- echo apply_filters( 'oembed_discovery_links', $output );
-}
-
-/**
- * Adds the necessary JavaScript to communicate with the embedded iframes.
- *
- * @since 4.4.0
- */
-function wp_oembed_add_host_js() {
- wp_enqueue_script( 'wp-embed' );
-}
-
-/**
- * Retrieves the URL to embed a specific post in an iframe.
- *
- * @since 4.4.0
- *
- * @param int|WP_Post $post Optional. Post ID or object. Defaults to the current post.
- * @return string|false The post embed URL on success, false if the post doesn't exist.
- */
-function get_post_embed_url( $post = null ) {
- $post = get_post( $post );
-
- if ( ! $post ) {
- return false;
- }
-
- if ( get_option( 'permalink_structure' ) ) {
- $embed_url = trailingslashit( get_permalink( $post ) ) . user_trailingslashit( 'embed' );
- } else {
- $embed_url = add_query_arg( array( 'embed' => 'true' ), get_permalink( $post ) );
- }
-
- /**
- * Filter the URL to embed a specific post.
- *
- * @since 4.4.0
- *
- * @param string $embed_url The post embed URL.
- * @param WP_Post $post The corresponding post object.
- */
- return esc_url_raw( apply_filters( 'post_embed_url', $embed_url, $post ) );
-}
-
-/**
- * Retrieves the oEmbed endpoint URL for a given permalink.
- *
- * Pass an empty string as the first argument to get the endpoint base URL.
- *
- * @since 4.4.0
- *
- * @param string $permalink Optional. The permalink used for the `url` query arg. Default empty.
- * @param string $format Optional. The requested response format. Default 'json'.
- * @return string The oEmbed endpoint URL.
- */
-function get_oembed_endpoint_url( $permalink = '', $format = 'json' ) {
- $url = rest_url( 'oembed/1.0/embed' );
-
- if ( 'json' === $format ) {
- $format = false;
- }
-
- if ( '' !== $permalink ) {
- $url = add_query_arg( array(
- 'url' => urlencode( $permalink ),
- 'format' => $format,
- ), $url );
- }
-
- /**
- * Filter the oEmbed endpoint URL.
- *
- * @since 4.4.0
- *
- * @param string $url The URL to the oEmbed endpoint.
- * @param string $permalink The permalink used for the `url` query arg.
- * @param string $format The requested response format.
- */
- return apply_filters( 'oembed_endpoint_url', $url, $permalink, $format );
-}
-
-/**
- * Retrieves the embed code for a specific post.
- *
- * @since 4.4.0
- *
- * @param int $width The width for the response.
- * @param int $height The height for the response.
- * @param int|WP_Post $post Optional. Post ID or object. Default is global `$post`.
- * @return string|false Embed code on success, false if post doesn't exist.
- */
-function get_post_embed_html( $width, $height, $post = null ) {
- $post = get_post( $post );
-
- if ( ! $post ) {
- return false;
- }
-
- $embed_url = get_post_embed_url( $post );
-
- $output = '<blockquote class="wp-embedded-content"><a href="' . esc_url( get_permalink( $post ) ) . '">' . get_the_title( $post ) . "</a></blockquote>\n";
-
- $output .= "<script type='text/javascript'>\n";
- $output .= "<!--//--><![CDATA[//><!--\n";
- if ( SCRIPT_DEBUG ) {
- $output .= file_get_contents( ABSPATH . WPINC . '/js/wp-embed.js' );
- } else {
- /*
- * If you're looking at a src version of this file, you'll see an "include"
- * statement below. This is used by the `grunt build` process to directly
- * include a minified version of wp-embed.js, instead of using the
- * file_get_contents() method from above.
- *
- * If you're looking at a build version of this file, you'll see a string of
- * minified JavaScript. If you need to debug it, please turn on SCRIPT_DEBUG
- * and edit wp-embed.js directly.
- */
- $output .=<<<JS
- include "js/wp-embed.min.js"
-JS;
- }
- $output .= "\n//--><!]]>";
- $output .= "\n</script>";
-
- $output .= sprintf(
- '<iframe sandbox="allow-scripts" security="restricted" src="%1$s" width="%2$d" height="%3$d" title="%4$s" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" class="wp-embedded-content"></iframe>',
- esc_url( $embed_url ),
- absint( $width ),
- absint( $height ),
- esc_attr__( 'Embedded WordPress Post' )
- );
-
- /**
- * Filter the embed HTML output for a given post.
- *
- * @since 4.4.0
- *
- * @param string $output The default HTML.
- * @param WP_Post $post Current post object.
- * @param int $width Width of the response.
- * @param int $height Height of the response.
- */
- return apply_filters( 'embed_html', $output, $post, $width, $height );
-}
-
-/**
- * Retrieves the oEmbed response data for a given post.
- *
- * @since 4.4.0
- *
- * @param WP_Post|int $post Post object or ID.
- * @param int $width The requested width.
- * @return array|false Response data on success, false if post doesn't exist.
- */
-function get_oembed_response_data( $post, $width ) {
- $post = get_post( $post );
-
- if ( ! $post ) {
- return false;
- }
-
- if ( 'publish' !== get_post_status( $post ) ) {
- return false;
- }
-
- /**
- * Filter the allowed minimum and maximum widths for the oEmbed response.
- *
- * @since 4.4.0
- *
- * @param array $min_max_width {
- * Minimum and maximum widths for the oEmbed response.
- *
- * @type int $min Minimum width. Default 200.
- * @type int $max Maximum width. Default 600.
- * }
- */
- $min_max_width = apply_filters( 'oembed_min_max_width', array(
- 'min' => 200,
- 'max' => 600
- ) );
-
- $width = min( max( $min_max_width['min'], $width ), $min_max_width['max'] );
- $height = max( ceil( $width / 16 * 9 ), 200 );
-
- $data = array(
- 'version' => '1.0',
- 'provider_name' => get_bloginfo( 'name' ),
- 'provider_url' => get_home_url(),
- 'author_name' => get_bloginfo( 'name' ),
- 'author_url' => get_home_url(),
- 'title' => $post->post_title,
- 'type' => 'link',
- );
-
- $author = get_userdata( $post->post_author );
-
- if ( $author ) {
- $data['author_name'] = $author->display_name;
- $data['author_url'] = get_author_posts_url( $author->ID );
- }
-
- /**
- * Filter the oEmbed response data.
- *
- * @since 4.4.0
- *
- * @param array $data The response data.
- * @param WP_Post $post The post object.
- * @param int $width The requested width.
- * @param int $height The calculated height.
- */
- return apply_filters( 'oembed_response_data', $data, $post, $width, $height );
-}
-
-/**
- * Filters the oEmbed response data to return an iframe embed code.
- *
- * @since 4.4.0
- *
- * @param array $data The response data.
- * @param WP_Post $post The post object.
- * @param int $width The requested width.
- * @param int $height The calculated height.
- * @return array The modified response data.
- */
-function get_oembed_response_data_rich( $data, $post, $width, $height ) {
- $data['width'] = absint( $width );
- $data['height'] = absint( $height );
- $data['type'] = 'rich';
- $data['html'] = get_post_embed_html( $width, $height, $post );
-
- // Add post thumbnail to response if available.
- $thumbnail_id = false;
-
- if ( has_post_thumbnail( $post->ID ) ) {
- $thumbnail_id = get_post_thumbnail_id( $post->ID );
- }
-
- if ( 'attachment' === get_post_type( $post ) ) {
- if ( wp_attachment_is_image( $post ) ) {
- $thumbnail_id = $post->ID;
- } else if ( wp_attachment_is( 'video', $post ) ) {
- $thumbnail_id = get_post_thumbnail_id( $post );
- $data['type'] = 'video';
- }
- }
-
- if ( $thumbnail_id ) {
- list( $thumbnail_url, $thumbnail_width, $thumbnail_height ) = wp_get_attachment_image_src( $thumbnail_id, array( $width, 99999 ) );
- $data['thumbnail_url'] = $thumbnail_url;
- $data['thumbnail_width'] = $thumbnail_width;
- $data['thumbnail_height'] = $thumbnail_height;
- }
-
- return $data;
-}
-
-/**
- * Ensures that the specified format is either 'json' or 'xml'.
- *
- * @since 4.4.0
- *
- * @param string $format The oEmbed response format. Accepts 'json' or 'xml'.
- * @return string The format, either 'xml' or 'json'. Default 'json'.
- */
-function wp_oembed_ensure_format( $format ) {
- if ( ! in_array( $format, array( 'json', 'xml' ), true ) ) {
- return 'json';
- }
-
- return $format;
-}
-
-/**
- * Hooks into the REST API output to print XML instead of JSON.
- *
- * This is only done for the oEmbed API endpoint,
- * which supports both formats.
- *
- * @access private
- * @since 4.4.0
- *
- * @param bool $served Whether the request has already been served.
- * @param WP_HTTP_ResponseInterface $result Result to send to the client. Usually a WP_REST_Response.
- * @param WP_REST_Request $request Request used to generate the response.
- * @param WP_REST_Server $server Server instance.
- * @return true
- */
-function _oembed_rest_pre_serve_request( $served, $result, $request, $server ) {
- $params = $request->get_params();
-
- if ( '/oembed/1.0/embed' !== $request->get_route() || 'GET' !== $request->get_method() ) {
- return $served;
- }
-
- if ( ! isset( $params['format'] ) || 'xml' !== $params['format'] ) {
- return $served;
- }
-
- // Embed links inside the request.
- $data = $server->response_to_data( $result, false );
-
- if ( ! class_exists( 'SimpleXMLElement' ) ) {
- status_header( 501 );
- die( get_status_header_desc( 501 ) );
- }
-
- $result = _oembed_create_xml( $data );
-
- // Bail if there's no XML.
- if ( ! $result ) {
- status_header( 501 );
- return get_status_header_desc( 501 );
- }
-
- if ( ! headers_sent() ) {
- $server->send_header( 'Content-Type', 'text/xml; charset=' . get_option( 'blog_charset' ) );
- }
-
- echo $result;
-
- return true;
-}
-
-/**
- * Creates an XML string from a given array.
- *
- * @since 4.4.0
- * @access private
- *
- * @param array $data The original oEmbed response data.
- * @param SimpleXMLElement $node Optional. XML node to append the result to recursively.
- * @return string|false XML string on success, false on error.
- */
-function _oembed_create_xml( $data, $node = null ) {
- if ( ! is_array( $data ) || empty( $data ) ) {
- return false;
- }
-
- if ( null === $node ) {
- $node = new SimpleXMLElement( '<oembed></oembed>' );
- }
-
- foreach ( $data as $key => $value ) {
- if ( is_numeric( $key ) ) {
- $key = 'oembed';
- }
-
- if ( is_array( $value ) ) {
- $item = $node->addChild( $key );
- _oembed_create_xml( $value, $item );
- } else {
- $node->addChild( $key, esc_html( $value ) );
- }
- }
-
- return $node->asXML();
-}
-
-/**
- * Filters the given oEmbed HTML.
- *
- * If the `$url` isn't on the trusted providers list,
- * we need to filter the HTML heavily for security.
- *
- * Only filters 'rich' and 'html' response types.
- *
- * @since 4.4.0
- *
- * @param string $result The oEmbed HTML result.
- * @param object $data A data object result from an oEmbed provider.
- * @param string $url The URL of the content to be embedded.
- * @return string The filtered and sanitized oEmbed result.
- */
-function wp_filter_oembed_result( $result, $data, $url ) {
- if ( false === $result || ! in_array( $data->type, array( 'rich', 'video' ) ) ) {
- return $result;
- }
-
- require_once( ABSPATH . WPINC . '/class-oembed.php' );
- $wp_oembed = _wp_oembed_get_object();
-
- // Don't modify the HTML for trusted providers.
- if ( false !== $wp_oembed->get_provider( $url, array( 'discover' => false ) ) ) {
- return $result;
- }
-
- $allowed_html = array(
- 'a' => array(
- 'href' => true,
- ),
- 'blockquote' => array(),
- 'iframe' => array(
- 'src' => true,
- 'width' => true,
- 'height' => true,
- 'frameborder' => true,
- 'marginwidth' => true,
- 'marginheight' => true,
- 'scrolling' => true,
- 'title' => true,
- ),
- );
-
- $html = wp_kses( $result, $allowed_html );
-
- preg_match( '|(<blockquote>.*?</blockquote>)?.*(<iframe.*?></iframe>)|ms', $html, $content );
- // We require at least the iframe to exist.
- if ( empty( $content[2] ) ) {
- return false;
- }
- $html = $content[1] . $content[2];
-
- if ( ! empty( $content[1] ) ) {
- // We have a blockquote to fall back on. Hide the iframe by default.
- $html = str_replace( '<iframe', '<iframe style="display:none;"', $html );
- $html = str_replace( '<blockquote', '<blockquote class="wp-embedded-content"', $html );
- }
-
- $html = str_replace( '<iframe', '<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted"', $html );
-
- preg_match( '/ src=[\'"]([^\'"]*)[\'"]/', $html, $results );
-
- if ( ! empty( $results ) ) {
- $secret = wp_generate_password( 10, false );
-
- $url = esc_url( "{$results[1]}#?secret=$secret" );
-
- $html = str_replace( $results[0], " src=\"$url\" data-secret=\"$secret\"", $html );
- $html = str_replace( '<blockquote', "<blockquote data-secret=\"$secret\"", $html );
- }
-
- return $html;
-}
-
-/**
- * Filters the string in the 'more' link displayed after a trimmed excerpt.
- *
- * Replaces '[...]' (appended to automatically generated excerpts) with an
- * ellipsis and a "Continue reading" link in the embed template.
- *
- * @since 4.4.0
- *
- * @param string $more_string Default 'more' string.
- * @return string 'Continue reading' link prepended with an ellipsis.
- */
-function wp_embed_excerpt_more( $more_string ) {
- if ( ! is_embed() ) {
- return $more_string;
- }
-
- $link = sprintf( '<a href="%1$s" class="wp-embed-more" target="_top">%2$s</a>',
- esc_url( get_permalink() ),
- /* translators: %s: Name of current post */
- sprintf( __( 'Continue reading %s' ), '<span class="screen-reader-text">' . get_the_title() . '</span>' )
- );
- return ' … ' . $link;
-}
-
-/**
- * Displays the post excerpt for the embed template.
- *
- * Intended to be used in 'The Loop'.
- *
- * @since 4.4.0
- */
-function the_excerpt_embed() {
- $output = get_the_excerpt();
-
- /**
- * Filter the post excerpt for the embed template.
- *
- * @since 4.4.0
- *
- * @param string $output The current post excerpt.
- */
- echo apply_filters( 'the_excerpt_embed', $output );
-}
-
-/**
- * Filters the post excerpt for the embed template.
- *
- * Shows players for video and audio attachments.
- *
- * @since 4.4.0
- *
- * @param string $content The current post excerpt.
- * @return string The modified post excerpt.
- */
-function wp_embed_excerpt_attachment( $content ) {
- if ( is_attachment() ) {
- return prepend_attachment( '' );
- }
-
- return $content;
-}
-
-/**
- * Enqueue embed iframe default CSS and JS & fire do_action('enqueue_embed_scripts')
- *
- * Enqueue PNG fallback CSS for embed iframe for legacy versions of IE.
- *
- * Allows plugins to queue scripts for the embed iframe end using wp_enqueue_script().
- * Runs first in oembed_head().
- *
- * @since 4.4.0
- */
-function enqueue_embed_scripts() {
- wp_enqueue_style( 'open-sans' );
- wp_enqueue_style( 'wp-embed-template-ie' );
-
- /**
- * Fires when scripts and styles are enqueued for the embed iframe.
- *
- * @since 4.4.0
- */
- do_action( 'enqueue_embed_scripts' );
-}
-
-/**
- * Prints the CSS in the embed iframe header.
- *
- * @since 4.4.0
- */
-function print_embed_styles() {
- ?>
- <style type="text/css">
- <?php
- if ( SCRIPT_DEBUG ) {
- readfile( ABSPATH . WPINC . "/css/wp-embed-template.css" );
- } else {
- /*
- * If you're looking at a src version of this file, you'll see an "include"
- * statement below. This is used by the `grunt build` process to directly
- * include a minified version of wp-oembed-embed.css, instead of using the
- * readfile() method from above.
- *
- * If you're looking at a build version of this file, you'll see a string of
- * minified CSS. If you need to debug it, please turn on SCRIPT_DEBUG
- * and edit wp-embed-template.css directly.
- */
- ?>
- include "css/wp-embed-template.min.css"
- <?php
- }
- ?>
- </style>
- <?php
-}
-
-/**
- * Prints the JavaScript in the embed iframe header.
- *
- * @since 4.4.0
- */
-function print_embed_scripts() {
- ?>
- <script type="text/javascript">
- <?php
- if ( SCRIPT_DEBUG ) {
- readfile( ABSPATH . WPINC . "/js/wp-embed-template.js" );
- } else {
- /*
- * If you're looking at a src version of this file, you'll see an "include"
- * statement below. This is used by the `grunt build` process to directly
- * include a minified version of wp-embed-template.js, instead of using the
- * readfile() method from above.
- *
- * If you're looking at a build version of this file, you'll see a string of
- * minified JavaScript. If you need to debug it, please turn on SCRIPT_DEBUG
- * and edit wp-embed-template.js directly.
- */
- ?>
- include "js/wp-embed-template.min.js"
- <?php
- }
- ?>
- </script>
- <?php
-}
-
-/**
- * Prepare the oembed HTML to be displayed in an RSS feed.
- *
- * @since 4.4.0
- * @access private
- *
- * @param string $content The content to filter.
- * @return string The filtered content.
- */
-function _oembed_filter_feed_content( $content ) {
- return str_replace( '<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" style="display:none;"', '<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted"', $content );
-}
-
-/**
- * Prints the necessary markup for the embed comments button.
- *
- * @since 4.4.0
- */
-function print_embed_comments_button() {
- if ( is_404() || ! ( get_comments_number() || comments_open() ) ) {
- return;
- }
- ?>
- <div class="wp-embed-comments">
- <a href="<?php comments_link(); ?>" target="_top">
- <span class="dashicons dashicons-admin-comments"></span>
- <?php
- printf(
- _n(
- '%s <span class="screen-reader-text">Comment</span>',
- '%s <span class="screen-reader-text">Comments</span>',
- get_comments_number()
- ),
- number_format_i18n( get_comments_number() )
- );
- ?>
- </a>
- </div>
- <?php
-}
-
-/**
- * Prints the necessary markup for the embed sharing button.
- *
- * @since 4.4.0
- */
-function print_embed_sharing_button() {
- if ( is_404() ) {
- return;
- }
- ?>
- <div class="wp-embed-share">
- <button type="button" class="wp-embed-share-dialog-open" aria-label="<?php esc_attr_e( 'Open sharing dialog' ); ?>">
- <span class="dashicons dashicons-share"></span>
- </button>
- </div>
- <?php
-}
-
-/**
- * Prints the necessary markup for the embed sharing dialog.
- *
- * @since 4.4.0
- */
-function print_embed_sharing_dialog() {
- if ( is_404() ) {
- return;
- }
- ?>
- <div class="wp-embed-share-dialog hidden" role="dialog" aria-label="<?php esc_attr_e( 'Sharing options' ); ?>">
- <div class="wp-embed-share-dialog-content">
- <div class="wp-embed-share-dialog-text">
- <ul class="wp-embed-share-tabs" role="tablist">
- <li class="wp-embed-share-tab-button wp-embed-share-tab-button-wordpress" role="presentation">
- <button type="button" role="tab" aria-controls="wp-embed-share-tab-wordpress" aria-selected="true" tabindex="0"><?php esc_html_e( 'WordPress Embed' ); ?></button>
- </li>
- <li class="wp-embed-share-tab-button wp-embed-share-tab-button-html" role="presentation">
- <button type="button" role="tab" aria-controls="wp-embed-share-tab-html" aria-selected="false" tabindex="-1"><?php esc_html_e( 'HTML Embed' ); ?></button>
- </li>
- </ul>
- <div id="wp-embed-share-tab-wordpress" class="wp-embed-share-tab" role="tabpanel" aria-hidden="false">
- <input type="text" value="<?php the_permalink(); ?>" class="wp-embed-share-input" aria-describedby="wp-embed-share-description-wordpress" tabindex="0" readonly/>
-
- <p class="wp-embed-share-description" id="wp-embed-share-description-wordpress">
- <?php _e( 'Copy and paste this URL into your WordPress site to embed' ); ?>
- </p>
- </div>
- <div id="wp-embed-share-tab-html" class="wp-embed-share-tab" role="tabpanel" aria-hidden="true">
- <textarea class="wp-embed-share-input" aria-describedby="wp-embed-share-description-html" tabindex="0" readonly><?php echo esc_textarea( get_post_embed_html( 600, 400 ) ); ?></textarea>
-
- <p class="wp-embed-share-description" id="wp-embed-share-description-html">
- <?php _e( 'Copy and paste this code into your site to embed' ); ?>
- </p>
- </div>
- </div>
-
- <button type="button" class="wp-embed-share-dialog-close" aria-label="<?php esc_attr_e( 'Close sharing dialog' ); ?>">
- <span class="dashicons dashicons-no"></span>
- </button>
- </div>
- </div>
- <?php
-}
</del></span></pre></div>
<a id="trunksrcwpincludesembedphpfromrev35712trunksrcwpincludesembedfunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/embed.php (from rev 35712, trunk/src/wp-includes/embed-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/embed.php (rev 0)
+++ trunk/src/wp-includes/embed.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,1047 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * oEmbed API: Top-level oEmbed functionality
+ *
+ * @package WordPress
+ * @subpackage oEmbed
+ * @since 4.4.0
+ */
+
+/**
+ * Registers an embed handler.
+ *
+ * Should probably only be used for sites that do not support oEmbed.
+ *
+ * @since 2.9.0
+ *
+ * @global WP_Embed $wp_embed
+ *
+ * @param string $id An internal ID/name for the handler. Needs to be unique.
+ * @param string $regex The regex that will be used to see if this handler should be used for a URL.
+ * @param callable $callback The callback function that will be called if the regex is matched.
+ * @param int $priority Optional. Used to specify the order in which the registered handlers will
+ * be tested. Default 10.
+ */
+function wp_embed_register_handler( $id, $regex, $callback, $priority = 10 ) {
+ global $wp_embed;
+ $wp_embed->register_handler( $id, $regex, $callback, $priority );
+}
+
+/**
+ * Unregisters a previously-registered embed handler.
+ *
+ * @since 2.9.0
+ *
+ * @global WP_Embed $wp_embed
+ *
+ * @param string $id The handler ID that should be removed.
+ * @param int $priority Optional. The priority of the handler to be removed. Default 10.
+ */
+function wp_embed_unregister_handler( $id, $priority = 10 ) {
+ global $wp_embed;
+ $wp_embed->unregister_handler( $id, $priority );
+}
+
+/**
+ * Creates default array of embed parameters.
+ *
+ * The width defaults to the content width as specified by the theme. If the
+ * theme does not specify a content width, then 500px is used.
+ *
+ * The default height is 1.5 times the width, or 1000px, whichever is smaller.
+ *
+ * The 'embed_defaults' filter can be used to adjust either of these values.
+ *
+ * @since 2.9.0
+ *
+ * @global int $content_width
+ *
+ * @param string $url Optional. The URL that should be embedded. Default empty.
+ *
+ * @return array Default embed parameters.
+ */
+function wp_embed_defaults( $url = '' ) {
+ if ( ! empty( $GLOBALS['content_width'] ) )
+ $width = (int) $GLOBALS['content_width'];
+
+ if ( empty( $width ) )
+ $width = 500;
+
+ $height = min( ceil( $width * 1.5 ), 1000 );
+
+ /**
+ * Filter the default array of embed dimensions.
+ *
+ * @since 2.9.0
+ *
+ * @param array $size An array of embed width and height values
+ * in pixels (in that order).
+ * @param string $url The URL that should be embedded.
+ */
+ return apply_filters( 'embed_defaults', compact( 'width', 'height' ), $url );
+}
+
+/**
+ * Attempts to fetch the embed HTML for a provided URL using oEmbed.
+ *
+ * @since 2.9.0
+ *
+ * @see WP_oEmbed
+ *
+ * @param string $url The URL that should be embedded.
+ * @param array $args Optional. Additional arguments and parameters for retrieving embed HTML.
+ * Default empty.
+ * @return false|string False on failure or the embed HTML on success.
+ */
+function wp_oembed_get( $url, $args = '' ) {
+ require_once( ABSPATH . WPINC . '/class-oembed.php' );
+ $oembed = _wp_oembed_get_object();
+ return $oembed->get_html( $url, $args );
+}
+
+/**
+ * Adds a URL format and oEmbed provider URL pair.
+ *
+ * @since 2.9.0
+ *
+ * @see WP_oEmbed
+ *
+ * @param string $format The format of URL that this provider can handle. You can use asterisks
+ * as wildcards.
+ * @param string $provider The URL to the oEmbed provider.
+ * @param boolean $regex Optional. Whether the `$format` parameter is in a RegEx format. Default false.
+ */
+function wp_oembed_add_provider( $format, $provider, $regex = false ) {
+ require_once( ABSPATH . WPINC . '/class-oembed.php' );
+
+ if ( did_action( 'plugins_loaded' ) ) {
+ $oembed = _wp_oembed_get_object();
+ $oembed->providers[$format] = array( $provider, $regex );
+ } else {
+ WP_oEmbed::_add_provider_early( $format, $provider, $regex );
+ }
+}
+
+/**
+ * Removes an oEmbed provider.
+ *
+ * @since 3.5.0
+ *
+ * @see WP_oEmbed
+ *
+ * @param string $format The URL format for the oEmbed provider to remove.
+ * @return bool Was the provider removed successfully?
+ */
+function wp_oembed_remove_provider( $format ) {
+ require_once( ABSPATH . WPINC . '/class-oembed.php' );
+
+ if ( did_action( 'plugins_loaded' ) ) {
+ $oembed = _wp_oembed_get_object();
+
+ if ( isset( $oembed->providers[ $format ] ) ) {
+ unset( $oembed->providers[ $format ] );
+ return true;
+ }
+ } else {
+ WP_oEmbed::_remove_provider_early( $format );
+ }
+
+ return false;
+}
+
+/**
+ * Determines if default embed handlers should be loaded.
+ *
+ * Checks to make sure that the embeds library hasn't already been loaded. If
+ * it hasn't, then it will load the embeds library.
+ *
+ * @since 2.9.0
+ *
+ * @see wp_embed_register_handler()
+ */
+function wp_maybe_load_embeds() {
+ /**
+ * Filter whether to load the default embed handlers.
+ *
+ * Returning a falsey value will prevent loading the default embed handlers.
+ *
+ * @since 2.9.0
+ *
+ * @param bool $maybe_load_embeds Whether to load the embeds library. Default true.
+ */
+ if ( ! apply_filters( 'load_default_embeds', true ) ) {
+ return;
+ }
+
+ wp_embed_register_handler( 'youtube_embed_url', '#https?://(www.)?youtube\.com/(?:v|embed)/([^/]+)#i', 'wp_embed_handler_youtube' );
+
+ wp_embed_register_handler( 'googlevideo', '#http://video\.google\.([A-Za-z.]{2,5})/videoplay\?docid=([\d-]+)(.*?)#i', 'wp_embed_handler_googlevideo' );
+
+ /**
+ * Filter the audio embed handler callback.
+ *
+ * @since 3.6.0
+ *
+ * @param callable $handler Audio embed handler callback function.
+ */
+ wp_embed_register_handler( 'audio', '#^https?://.+?\.(' . join( '|', wp_get_audio_extensions() ) . ')$#i', apply_filters( 'wp_audio_embed_handler', 'wp_embed_handler_audio' ), 9999 );
+
+ /**
+ * Filter the video embed handler callback.
+ *
+ * @since 3.6.0
+ *
+ * @param callable $handler Video embed handler callback function.
+ */
+ wp_embed_register_handler( 'video', '#^https?://.+?\.(' . join( '|', wp_get_video_extensions() ) . ')$#i', apply_filters( 'wp_video_embed_handler', 'wp_embed_handler_video' ), 9999 );
+}
+
+/**
+ * The Google Video embed handler callback.
+ *
+ * Google Video does not support oEmbed.
+ *
+ * @see WP_Embed::register_handler()
+ * @see WP_Embed::shortcode()
+ *
+ * @param array $matches The RegEx matches from the provided regex when calling wp_embed_register_handler().
+ * @param array $attr Embed attributes.
+ * @param string $url The original URL that was matched by the regex.
+ * @param array $rawattr The original unmodified attributes.
+ * @return string The embed HTML.
+ */
+function wp_embed_handler_googlevideo( $matches, $attr, $url, $rawattr ) {
+ // If the user supplied a fixed width AND height, use it
+ if ( !empty($rawattr['width']) && !empty($rawattr['height']) ) {
+ $width = (int) $rawattr['width'];
+ $height = (int) $rawattr['height'];
+ } else {
+ list( $width, $height ) = wp_expand_dimensions( 425, 344, $attr['width'], $attr['height'] );
+ }
+
+ /**
+ * Filter the Google Video embed output.
+ *
+ * @since 2.9.0
+ *
+ * @param string $html Google Video HTML embed markup.
+ * @param array $matches The RegEx matches from the provided regex.
+ * @param array $attr An array of embed attributes.
+ * @param string $url The original URL that was matched by the regex.
+ * @param array $rawattr The original unmodified attributes.
+ */
+ return apply_filters( 'embed_googlevideo', '<embed type="application/x-shockwave-flash" src="http://video.google.com/googleplayer.swf?docid=' . esc_attr($matches[2]) . '&hl=en&fs=true" style="width:' . esc_attr($width) . 'px;height:' . esc_attr($height) . 'px" allowFullScreen="true" allowScriptAccess="always" />', $matches, $attr, $url, $rawattr );
+}
+
+/**
+ * YouTube iframe embed handler callback.
+ *
+ * Catches YouTube iframe embed URLs that are not parsable by oEmbed but can be translated into a URL that is.
+ *
+ * @since 4.0.0
+ *
+ * @global WP_Embed $wp_embed
+ *
+ * @param array $matches The RegEx matches from the provided regex when calling
+ * wp_embed_register_handler().
+ * @param array $attr Embed attributes.
+ * @param string $url The original URL that was matched by the regex.
+ * @param array $rawattr The original unmodified attributes.
+ * @return string The embed HTML.
+ */
+function wp_embed_handler_youtube( $matches, $attr, $url, $rawattr ) {
+ global $wp_embed;
+ $embed = $wp_embed->autoembed( "https://youtube.com/watch?v={$matches[2]}" );
+
+ /**
+ * Filter the YoutTube embed output.
+ *
+ * @since 4.0.0
+ *
+ * @see wp_embed_handler_youtube()
+ *
+ * @param string $embed YouTube embed output.
+ * @param array $attr An array of embed attributes.
+ * @param string $url The original URL that was matched by the regex.
+ * @param array $rawattr The original unmodified attributes.
+ */
+ return apply_filters( 'wp_embed_handler_youtube', $embed, $attr, $url, $rawattr );
+}
+
+/**
+ * Audio embed handler callback.
+ *
+ * @since 3.6.0
+ *
+ * @param array $matches The RegEx matches from the provided regex when calling wp_embed_register_handler().
+ * @param array $attr Embed attributes.
+ * @param string $url The original URL that was matched by the regex.
+ * @param array $rawattr The original unmodified attributes.
+ * @return string The embed HTML.
+ */
+function wp_embed_handler_audio( $matches, $attr, $url, $rawattr ) {
+ $audio = sprintf( '[audio src="%s" /]', esc_url( $url ) );
+
+ /**
+ * Filter the audio embed output.
+ *
+ * @since 3.6.0
+ *
+ * @param string $audio Audio embed output.
+ * @param array $attr An array of embed attributes.
+ * @param string $url The original URL that was matched by the regex.
+ * @param array $rawattr The original unmodified attributes.
+ */
+ return apply_filters( 'wp_embed_handler_audio', $audio, $attr, $url, $rawattr );
+}
+
+/**
+ * Video embed handler callback.
+ *
+ * @since 3.6.0
+ *
+ * @param array $matches The RegEx matches from the provided regex when calling wp_embed_register_handler().
+ * @param array $attr Embed attributes.
+ * @param string $url The original URL that was matched by the regex.
+ * @param array $rawattr The original unmodified attributes.
+ * @return string The embed HTML.
+ */
+function wp_embed_handler_video( $matches, $attr, $url, $rawattr ) {
+ $dimensions = '';
+ if ( ! empty( $rawattr['width'] ) && ! empty( $rawattr['height'] ) ) {
+ $dimensions .= sprintf( 'width="%d" ', (int) $rawattr['width'] );
+ $dimensions .= sprintf( 'height="%d" ', (int) $rawattr['height'] );
+ }
+ $video = sprintf( '[video %s src="%s" /]', $dimensions, esc_url( $url ) );
+
+ /**
+ * Filter the video embed output.
+ *
+ * @since 3.6.0
+ *
+ * @param string $video Video embed output.
+ * @param array $attr An array of embed attributes.
+ * @param string $url The original URL that was matched by the regex.
+ * @param array $rawattr The original unmodified attributes.
+ */
+ return apply_filters( 'wp_embed_handler_video', $video, $attr, $url, $rawattr );
+}
+
+/**
+ * Registers the oEmbed REST API route.
+ *
+ * @since 4.4.0
+ */
+function wp_oembed_register_route() {
+ $controller = new WP_oEmbed_Controller();
+ $controller->register_routes();
+}
+
+/**
+ * Adds oEmbed discovery links in the website <head>.
+ *
+ * @since 4.4.0
+ */
+function wp_oembed_add_discovery_links() {
+ $output = '';
+
+ if ( is_singular() ) {
+ $output .= '<link rel="alternate" type="application/json+oembed" href="' . esc_url( get_oembed_endpoint_url( get_permalink() ) ) . '" />' . "\n";
+
+ if ( class_exists( 'SimpleXMLElement' ) ) {
+ $output .= '<link rel="alternate" type="text/xml+oembed" href="' . esc_url( get_oembed_endpoint_url( get_permalink(), 'xml' ) ) . '" />' . "\n";
+ }
+ }
+
+ /**
+ * Filter the oEmbed discovery links HTML.
+ *
+ * @since 4.4.0
+ *
+ * @param string $output HTML of the discovery links.
+ */
+ echo apply_filters( 'oembed_discovery_links', $output );
+}
+
+/**
+ * Adds the necessary JavaScript to communicate with the embedded iframes.
+ *
+ * @since 4.4.0
+ */
+function wp_oembed_add_host_js() {
+ wp_enqueue_script( 'wp-embed' );
+}
+
+/**
+ * Retrieves the URL to embed a specific post in an iframe.
+ *
+ * @since 4.4.0
+ *
+ * @param int|WP_Post $post Optional. Post ID or object. Defaults to the current post.
+ * @return string|false The post embed URL on success, false if the post doesn't exist.
+ */
+function get_post_embed_url( $post = null ) {
+ $post = get_post( $post );
+
+ if ( ! $post ) {
+ return false;
+ }
+
+ if ( get_option( 'permalink_structure' ) ) {
+ $embed_url = trailingslashit( get_permalink( $post ) ) . user_trailingslashit( 'embed' );
+ } else {
+ $embed_url = add_query_arg( array( 'embed' => 'true' ), get_permalink( $post ) );
+ }
+
+ /**
+ * Filter the URL to embed a specific post.
+ *
+ * @since 4.4.0
+ *
+ * @param string $embed_url The post embed URL.
+ * @param WP_Post $post The corresponding post object.
+ */
+ return esc_url_raw( apply_filters( 'post_embed_url', $embed_url, $post ) );
+}
+
+/**
+ * Retrieves the oEmbed endpoint URL for a given permalink.
+ *
+ * Pass an empty string as the first argument to get the endpoint base URL.
+ *
+ * @since 4.4.0
+ *
+ * @param string $permalink Optional. The permalink used for the `url` query arg. Default empty.
+ * @param string $format Optional. The requested response format. Default 'json'.
+ * @return string The oEmbed endpoint URL.
+ */
+function get_oembed_endpoint_url( $permalink = '', $format = 'json' ) {
+ $url = rest_url( 'oembed/1.0/embed' );
+
+ if ( 'json' === $format ) {
+ $format = false;
+ }
+
+ if ( '' !== $permalink ) {
+ $url = add_query_arg( array(
+ 'url' => urlencode( $permalink ),
+ 'format' => $format,
+ ), $url );
+ }
+
+ /**
+ * Filter the oEmbed endpoint URL.
+ *
+ * @since 4.4.0
+ *
+ * @param string $url The URL to the oEmbed endpoint.
+ * @param string $permalink The permalink used for the `url` query arg.
+ * @param string $format The requested response format.
+ */
+ return apply_filters( 'oembed_endpoint_url', $url, $permalink, $format );
+}
+
+/**
+ * Retrieves the embed code for a specific post.
+ *
+ * @since 4.4.0
+ *
+ * @param int $width The width for the response.
+ * @param int $height The height for the response.
+ * @param int|WP_Post $post Optional. Post ID or object. Default is global `$post`.
+ * @return string|false Embed code on success, false if post doesn't exist.
+ */
+function get_post_embed_html( $width, $height, $post = null ) {
+ $post = get_post( $post );
+
+ if ( ! $post ) {
+ return false;
+ }
+
+ $embed_url = get_post_embed_url( $post );
+
+ $output = '<blockquote class="wp-embedded-content"><a href="' . esc_url( get_permalink( $post ) ) . '">' . get_the_title( $post ) . "</a></blockquote>\n";
+
+ $output .= "<script type='text/javascript'>\n";
+ $output .= "<!--//--><![CDATA[//><!--\n";
+ if ( SCRIPT_DEBUG ) {
+ $output .= file_get_contents( ABSPATH . WPINC . '/js/wp-embed.js' );
+ } else {
+ /*
+ * If you're looking at a src version of this file, you'll see an "include"
+ * statement below. This is used by the `grunt build` process to directly
+ * include a minified version of wp-embed.js, instead of using the
+ * file_get_contents() method from above.
+ *
+ * If you're looking at a build version of this file, you'll see a string of
+ * minified JavaScript. If you need to debug it, please turn on SCRIPT_DEBUG
+ * and edit wp-embed.js directly.
+ */
+ $output .=<<<JS
+ include "js/wp-embed.min.js"
+JS;
+ }
+ $output .= "\n//--><!]]>";
+ $output .= "\n</script>";
+
+ $output .= sprintf(
+ '<iframe sandbox="allow-scripts" security="restricted" src="%1$s" width="%2$d" height="%3$d" title="%4$s" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" class="wp-embedded-content"></iframe>',
+ esc_url( $embed_url ),
+ absint( $width ),
+ absint( $height ),
+ esc_attr__( 'Embedded WordPress Post' )
+ );
+
+ /**
+ * Filter the embed HTML output for a given post.
+ *
+ * @since 4.4.0
+ *
+ * @param string $output The default HTML.
+ * @param WP_Post $post Current post object.
+ * @param int $width Width of the response.
+ * @param int $height Height of the response.
+ */
+ return apply_filters( 'embed_html', $output, $post, $width, $height );
+}
+
+/**
+ * Retrieves the oEmbed response data for a given post.
+ *
+ * @since 4.4.0
+ *
+ * @param WP_Post|int $post Post object or ID.
+ * @param int $width The requested width.
+ * @return array|false Response data on success, false if post doesn't exist.
+ */
+function get_oembed_response_data( $post, $width ) {
+ $post = get_post( $post );
+
+ if ( ! $post ) {
+ return false;
+ }
+
+ if ( 'publish' !== get_post_status( $post ) ) {
+ return false;
+ }
+
+ /**
+ * Filter the allowed minimum and maximum widths for the oEmbed response.
+ *
+ * @since 4.4.0
+ *
+ * @param array $min_max_width {
+ * Minimum and maximum widths for the oEmbed response.
+ *
+ * @type int $min Minimum width. Default 200.
+ * @type int $max Maximum width. Default 600.
+ * }
+ */
+ $min_max_width = apply_filters( 'oembed_min_max_width', array(
+ 'min' => 200,
+ 'max' => 600
+ ) );
+
+ $width = min( max( $min_max_width['min'], $width ), $min_max_width['max'] );
+ $height = max( ceil( $width / 16 * 9 ), 200 );
+
+ $data = array(
+ 'version' => '1.0',
+ 'provider_name' => get_bloginfo( 'name' ),
+ 'provider_url' => get_home_url(),
+ 'author_name' => get_bloginfo( 'name' ),
+ 'author_url' => get_home_url(),
+ 'title' => $post->post_title,
+ 'type' => 'link',
+ );
+
+ $author = get_userdata( $post->post_author );
+
+ if ( $author ) {
+ $data['author_name'] = $author->display_name;
+ $data['author_url'] = get_author_posts_url( $author->ID );
+ }
+
+ /**
+ * Filter the oEmbed response data.
+ *
+ * @since 4.4.0
+ *
+ * @param array $data The response data.
+ * @param WP_Post $post The post object.
+ * @param int $width The requested width.
+ * @param int $height The calculated height.
+ */
+ return apply_filters( 'oembed_response_data', $data, $post, $width, $height );
+}
+
+/**
+ * Filters the oEmbed response data to return an iframe embed code.
+ *
+ * @since 4.4.0
+ *
+ * @param array $data The response data.
+ * @param WP_Post $post The post object.
+ * @param int $width The requested width.
+ * @param int $height The calculated height.
+ * @return array The modified response data.
+ */
+function get_oembed_response_data_rich( $data, $post, $width, $height ) {
+ $data['width'] = absint( $width );
+ $data['height'] = absint( $height );
+ $data['type'] = 'rich';
+ $data['html'] = get_post_embed_html( $width, $height, $post );
+
+ // Add post thumbnail to response if available.
+ $thumbnail_id = false;
+
+ if ( has_post_thumbnail( $post->ID ) ) {
+ $thumbnail_id = get_post_thumbnail_id( $post->ID );
+ }
+
+ if ( 'attachment' === get_post_type( $post ) ) {
+ if ( wp_attachment_is_image( $post ) ) {
+ $thumbnail_id = $post->ID;
+ } else if ( wp_attachment_is( 'video', $post ) ) {
+ $thumbnail_id = get_post_thumbnail_id( $post );
+ $data['type'] = 'video';
+ }
+ }
+
+ if ( $thumbnail_id ) {
+ list( $thumbnail_url, $thumbnail_width, $thumbnail_height ) = wp_get_attachment_image_src( $thumbnail_id, array( $width, 99999 ) );
+ $data['thumbnail_url'] = $thumbnail_url;
+ $data['thumbnail_width'] = $thumbnail_width;
+ $data['thumbnail_height'] = $thumbnail_height;
+ }
+
+ return $data;
+}
+
+/**
+ * Ensures that the specified format is either 'json' or 'xml'.
+ *
+ * @since 4.4.0
+ *
+ * @param string $format The oEmbed response format. Accepts 'json' or 'xml'.
+ * @return string The format, either 'xml' or 'json'. Default 'json'.
+ */
+function wp_oembed_ensure_format( $format ) {
+ if ( ! in_array( $format, array( 'json', 'xml' ), true ) ) {
+ return 'json';
+ }
+
+ return $format;
+}
+
+/**
+ * Hooks into the REST API output to print XML instead of JSON.
+ *
+ * This is only done for the oEmbed API endpoint,
+ * which supports both formats.
+ *
+ * @access private
+ * @since 4.4.0
+ *
+ * @param bool $served Whether the request has already been served.
+ * @param WP_HTTP_ResponseInterface $result Result to send to the client. Usually a WP_REST_Response.
+ * @param WP_REST_Request $request Request used to generate the response.
+ * @param WP_REST_Server $server Server instance.
+ * @return true
+ */
+function _oembed_rest_pre_serve_request( $served, $result, $request, $server ) {
+ $params = $request->get_params();
+
+ if ( '/oembed/1.0/embed' !== $request->get_route() || 'GET' !== $request->get_method() ) {
+ return $served;
+ }
+
+ if ( ! isset( $params['format'] ) || 'xml' !== $params['format'] ) {
+ return $served;
+ }
+
+ // Embed links inside the request.
+ $data = $server->response_to_data( $result, false );
+
+ if ( ! class_exists( 'SimpleXMLElement' ) ) {
+ status_header( 501 );
+ die( get_status_header_desc( 501 ) );
+ }
+
+ $result = _oembed_create_xml( $data );
+
+ // Bail if there's no XML.
+ if ( ! $result ) {
+ status_header( 501 );
+ return get_status_header_desc( 501 );
+ }
+
+ if ( ! headers_sent() ) {
+ $server->send_header( 'Content-Type', 'text/xml; charset=' . get_option( 'blog_charset' ) );
+ }
+
+ echo $result;
+
+ return true;
+}
+
+/**
+ * Creates an XML string from a given array.
+ *
+ * @since 4.4.0
+ * @access private
+ *
+ * @param array $data The original oEmbed response data.
+ * @param SimpleXMLElement $node Optional. XML node to append the result to recursively.
+ * @return string|false XML string on success, false on error.
+ */
+function _oembed_create_xml( $data, $node = null ) {
+ if ( ! is_array( $data ) || empty( $data ) ) {
+ return false;
+ }
+
+ if ( null === $node ) {
+ $node = new SimpleXMLElement( '<oembed></oembed>' );
+ }
+
+ foreach ( $data as $key => $value ) {
+ if ( is_numeric( $key ) ) {
+ $key = 'oembed';
+ }
+
+ if ( is_array( $value ) ) {
+ $item = $node->addChild( $key );
+ _oembed_create_xml( $value, $item );
+ } else {
+ $node->addChild( $key, esc_html( $value ) );
+ }
+ }
+
+ return $node->asXML();
+}
+
+/**
+ * Filters the given oEmbed HTML.
+ *
+ * If the `$url` isn't on the trusted providers list,
+ * we need to filter the HTML heavily for security.
+ *
+ * Only filters 'rich' and 'html' response types.
+ *
+ * @since 4.4.0
+ *
+ * @param string $result The oEmbed HTML result.
+ * @param object $data A data object result from an oEmbed provider.
+ * @param string $url The URL of the content to be embedded.
+ * @return string The filtered and sanitized oEmbed result.
+ */
+function wp_filter_oembed_result( $result, $data, $url ) {
+ if ( false === $result || ! in_array( $data->type, array( 'rich', 'video' ) ) ) {
+ return $result;
+ }
+
+ require_once( ABSPATH . WPINC . '/class-oembed.php' );
+ $wp_oembed = _wp_oembed_get_object();
+
+ // Don't modify the HTML for trusted providers.
+ if ( false !== $wp_oembed->get_provider( $url, array( 'discover' => false ) ) ) {
+ return $result;
+ }
+
+ $allowed_html = array(
+ 'a' => array(
+ 'href' => true,
+ ),
+ 'blockquote' => array(),
+ 'iframe' => array(
+ 'src' => true,
+ 'width' => true,
+ 'height' => true,
+ 'frameborder' => true,
+ 'marginwidth' => true,
+ 'marginheight' => true,
+ 'scrolling' => true,
+ 'title' => true,
+ ),
+ );
+
+ $html = wp_kses( $result, $allowed_html );
+
+ preg_match( '|(<blockquote>.*?</blockquote>)?.*(<iframe.*?></iframe>)|ms', $html, $content );
+ // We require at least the iframe to exist.
+ if ( empty( $content[2] ) ) {
+ return false;
+ }
+ $html = $content[1] . $content[2];
+
+ if ( ! empty( $content[1] ) ) {
+ // We have a blockquote to fall back on. Hide the iframe by default.
+ $html = str_replace( '<iframe', '<iframe style="display:none;"', $html );
+ $html = str_replace( '<blockquote', '<blockquote class="wp-embedded-content"', $html );
+ }
+
+ $html = str_replace( '<iframe', '<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted"', $html );
+
+ preg_match( '/ src=[\'"]([^\'"]*)[\'"]/', $html, $results );
+
+ if ( ! empty( $results ) ) {
+ $secret = wp_generate_password( 10, false );
+
+ $url = esc_url( "{$results[1]}#?secret=$secret" );
+
+ $html = str_replace( $results[0], " src=\"$url\" data-secret=\"$secret\"", $html );
+ $html = str_replace( '<blockquote', "<blockquote data-secret=\"$secret\"", $html );
+ }
+
+ return $html;
+}
+
+/**
+ * Filters the string in the 'more' link displayed after a trimmed excerpt.
+ *
+ * Replaces '[...]' (appended to automatically generated excerpts) with an
+ * ellipsis and a "Continue reading" link in the embed template.
+ *
+ * @since 4.4.0
+ *
+ * @param string $more_string Default 'more' string.
+ * @return string 'Continue reading' link prepended with an ellipsis.
+ */
+function wp_embed_excerpt_more( $more_string ) {
+ if ( ! is_embed() ) {
+ return $more_string;
+ }
+
+ $link = sprintf( '<a href="%1$s" class="wp-embed-more" target="_top">%2$s</a>',
+ esc_url( get_permalink() ),
+ /* translators: %s: Name of current post */
+ sprintf( __( 'Continue reading %s' ), '<span class="screen-reader-text">' . get_the_title() . '</span>' )
+ );
+ return ' … ' . $link;
+}
+
+/**
+ * Displays the post excerpt for the embed template.
+ *
+ * Intended to be used in 'The Loop'.
+ *
+ * @since 4.4.0
+ */
+function the_excerpt_embed() {
+ $output = get_the_excerpt();
+
+ /**
+ * Filter the post excerpt for the embed template.
+ *
+ * @since 4.4.0
+ *
+ * @param string $output The current post excerpt.
+ */
+ echo apply_filters( 'the_excerpt_embed', $output );
+}
+
+/**
+ * Filters the post excerpt for the embed template.
+ *
+ * Shows players for video and audio attachments.
+ *
+ * @since 4.4.0
+ *
+ * @param string $content The current post excerpt.
+ * @return string The modified post excerpt.
+ */
+function wp_embed_excerpt_attachment( $content ) {
+ if ( is_attachment() ) {
+ return prepend_attachment( '' );
+ }
+
+ return $content;
+}
+
+/**
+ * Enqueue embed iframe default CSS and JS & fire do_action('enqueue_embed_scripts')
+ *
+ * Enqueue PNG fallback CSS for embed iframe for legacy versions of IE.
+ *
+ * Allows plugins to queue scripts for the embed iframe end using wp_enqueue_script().
+ * Runs first in oembed_head().
+ *
+ * @since 4.4.0
+ */
+function enqueue_embed_scripts() {
+ wp_enqueue_style( 'open-sans' );
+ wp_enqueue_style( 'wp-embed-template-ie' );
+
+ /**
+ * Fires when scripts and styles are enqueued for the embed iframe.
+ *
+ * @since 4.4.0
+ */
+ do_action( 'enqueue_embed_scripts' );
+}
+
+/**
+ * Prints the CSS in the embed iframe header.
+ *
+ * @since 4.4.0
+ */
+function print_embed_styles() {
+ ?>
+ <style type="text/css">
+ <?php
+ if ( SCRIPT_DEBUG ) {
+ readfile( ABSPATH . WPINC . "/css/wp-embed-template.css" );
+ } else {
+ /*
+ * If you're looking at a src version of this file, you'll see an "include"
+ * statement below. This is used by the `grunt build` process to directly
+ * include a minified version of wp-oembed-embed.css, instead of using the
+ * readfile() method from above.
+ *
+ * If you're looking at a build version of this file, you'll see a string of
+ * minified CSS. If you need to debug it, please turn on SCRIPT_DEBUG
+ * and edit wp-embed-template.css directly.
+ */
+ ?>
+ include "css/wp-embed-template.min.css"
+ <?php
+ }
+ ?>
+ </style>
+ <?php
+}
+
+/**
+ * Prints the JavaScript in the embed iframe header.
+ *
+ * @since 4.4.0
+ */
+function print_embed_scripts() {
+ ?>
+ <script type="text/javascript">
+ <?php
+ if ( SCRIPT_DEBUG ) {
+ readfile( ABSPATH . WPINC . "/js/wp-embed-template.js" );
+ } else {
+ /*
+ * If you're looking at a src version of this file, you'll see an "include"
+ * statement below. This is used by the `grunt build` process to directly
+ * include a minified version of wp-embed-template.js, instead of using the
+ * readfile() method from above.
+ *
+ * If you're looking at a build version of this file, you'll see a string of
+ * minified JavaScript. If you need to debug it, please turn on SCRIPT_DEBUG
+ * and edit wp-embed-template.js directly.
+ */
+ ?>
+ include "js/wp-embed-template.min.js"
+ <?php
+ }
+ ?>
+ </script>
+ <?php
+}
+
+/**
+ * Prepare the oembed HTML to be displayed in an RSS feed.
+ *
+ * @since 4.4.0
+ * @access private
+ *
+ * @param string $content The content to filter.
+ * @return string The filtered content.
+ */
+function _oembed_filter_feed_content( $content ) {
+ return str_replace( '<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" style="display:none;"', '<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted"', $content );
+}
+
+/**
+ * Prints the necessary markup for the embed comments button.
+ *
+ * @since 4.4.0
+ */
+function print_embed_comments_button() {
+ if ( is_404() || ! ( get_comments_number() || comments_open() ) ) {
+ return;
+ }
+ ?>
+ <div class="wp-embed-comments">
+ <a href="<?php comments_link(); ?>" target="_top">
+ <span class="dashicons dashicons-admin-comments"></span>
+ <?php
+ printf(
+ _n(
+ '%s <span class="screen-reader-text">Comment</span>',
+ '%s <span class="screen-reader-text">Comments</span>',
+ get_comments_number()
+ ),
+ number_format_i18n( get_comments_number() )
+ );
+ ?>
+ </a>
+ </div>
+ <?php
+}
+
+/**
+ * Prints the necessary markup for the embed sharing button.
+ *
+ * @since 4.4.0
+ */
+function print_embed_sharing_button() {
+ if ( is_404() ) {
+ return;
+ }
+ ?>
+ <div class="wp-embed-share">
+ <button type="button" class="wp-embed-share-dialog-open" aria-label="<?php esc_attr_e( 'Open sharing dialog' ); ?>">
+ <span class="dashicons dashicons-share"></span>
+ </button>
+ </div>
+ <?php
+}
+
+/**
+ * Prints the necessary markup for the embed sharing dialog.
+ *
+ * @since 4.4.0
+ */
+function print_embed_sharing_dialog() {
+ if ( is_404() ) {
+ return;
+ }
+ ?>
+ <div class="wp-embed-share-dialog hidden" role="dialog" aria-label="<?php esc_attr_e( 'Sharing options' ); ?>">
+ <div class="wp-embed-share-dialog-content">
+ <div class="wp-embed-share-dialog-text">
+ <ul class="wp-embed-share-tabs" role="tablist">
+ <li class="wp-embed-share-tab-button wp-embed-share-tab-button-wordpress" role="presentation">
+ <button type="button" role="tab" aria-controls="wp-embed-share-tab-wordpress" aria-selected="true" tabindex="0"><?php esc_html_e( 'WordPress Embed' ); ?></button>
+ </li>
+ <li class="wp-embed-share-tab-button wp-embed-share-tab-button-html" role="presentation">
+ <button type="button" role="tab" aria-controls="wp-embed-share-tab-html" aria-selected="false" tabindex="-1"><?php esc_html_e( 'HTML Embed' ); ?></button>
+ </li>
+ </ul>
+ <div id="wp-embed-share-tab-wordpress" class="wp-embed-share-tab" role="tabpanel" aria-hidden="false">
+ <input type="text" value="<?php the_permalink(); ?>" class="wp-embed-share-input" aria-describedby="wp-embed-share-description-wordpress" tabindex="0" readonly/>
+
+ <p class="wp-embed-share-description" id="wp-embed-share-description-wordpress">
+ <?php _e( 'Copy and paste this URL into your WordPress site to embed' ); ?>
+ </p>
+ </div>
+ <div id="wp-embed-share-tab-html" class="wp-embed-share-tab" role="tabpanel" aria-hidden="true">
+ <textarea class="wp-embed-share-input" aria-describedby="wp-embed-share-description-html" tabindex="0" readonly><?php echo esc_textarea( get_post_embed_html( 600, 400 ) ); ?></textarea>
+
+ <p class="wp-embed-share-description" id="wp-embed-share-description-html">
+ <?php _e( 'Copy and paste this code into your site to embed' ); ?>
+ </p>
+ </div>
+ </div>
+
+ <button type="button" class="wp-embed-share-dialog-close" aria-label="<?php esc_attr_e( 'Close sharing dialog' ); ?>">
+ <span class="dashicons dashicons-no"></span>
+ </button>
+ </div>
+ </div>
+ <?php
+}
</ins></span></pre></div>
<a id="trunksrcwpincludeshttpfunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/http-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/http-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/http-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,660 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * HTTP API: Top-level HTTP request API functionality
- *
- * @package WordPress
- * @subpackage HTTP
- * @since 4.4.0
- */
-
-/**
- * Returns the initialized WP_Http Object
- *
- * @since 2.7.0
- * @access private
- *
- * @staticvar WP_Http $http
- *
- * @return WP_Http HTTP Transport object.
- */
-function _wp_http_get_object() {
- static $http = null;
-
- if ( is_null( $http ) ) {
- $http = new WP_Http();
- }
- return $http;
-}
-
-/**
- * Retrieve the raw response from a safe HTTP request.
- *
- * This function is ideal when the HTTP request is being made to an arbitrary
- * URL. The URL is validated to avoid redirection and request forgery attacks.
- *
- * @since 3.6.0
- *
- * @see wp_remote_request() For more information on the response array format.
- * @see WP_Http::request() For default arguments information.
- *
- * @param string $url Site URL to retrieve.
- * @param array $args Optional. Request arguments. Default empty array.
- * @return WP_Error|array The response or WP_Error on failure.
- */
-function wp_safe_remote_request( $url, $args = array() ) {
- $args['reject_unsafe_urls'] = true;
- $http = _wp_http_get_object();
- return $http->request( $url, $args );
-}
-
-/**
- * Retrieve the raw response from a safe HTTP request using the GET method.
- *
- * This function is ideal when the HTTP request is being made to an arbitrary
- * URL. The URL is validated to avoid redirection and request forgery attacks.
- *
- * @since 3.6.0
- *
- * @see wp_remote_request() For more information on the response array format.
- * @see WP_Http::request() For default arguments information.
- *
- * @param string $url Site URL to retrieve.
- * @param array $args Optional. Request arguments. Default empty array.
- * @return WP_Error|array The response or WP_Error on failure.
- */
-function wp_safe_remote_get( $url, $args = array() ) {
- $args['reject_unsafe_urls'] = true;
- $http = _wp_http_get_object();
- return $http->get( $url, $args );
-}
-
-/**
- * Retrieve the raw response from a safe HTTP request using the POST method.
- *
- * This function is ideal when the HTTP request is being made to an arbitrary
- * URL. The URL is validated to avoid redirection and request forgery attacks.
- *
- * @since 3.6.0
- *
- * @see wp_remote_request() For more information on the response array format.
- * @see WP_Http::request() For default arguments information.
- *
- * @param string $url Site URL to retrieve.
- * @param array $args Optional. Request arguments. Default empty array.
- * @return WP_Error|array The response or WP_Error on failure.
- */
-function wp_safe_remote_post( $url, $args = array() ) {
- $args['reject_unsafe_urls'] = true;
- $http = _wp_http_get_object();
- return $http->post( $url, $args );
-}
-
-/**
- * Retrieve the raw response from a safe HTTP request using the HEAD method.
- *
- * This function is ideal when the HTTP request is being made to an arbitrary
- * URL. The URL is validated to avoid redirection and request forgery attacks.
- *
- * @since 3.6.0
- *
- * @see wp_remote_request() For more information on the response array format.
- * @see WP_Http::request() For default arguments information.
- *
- * @param string $url Site URL to retrieve.
- * @param array $args Optional. Request arguments. Default empty array.
- * @return WP_Error|array The response or WP_Error on failure.
- */
-function wp_safe_remote_head( $url, $args = array() ) {
- $args['reject_unsafe_urls'] = true;
- $http = _wp_http_get_object();
- return $http->head( $url, $args );
-}
-
-/**
- * Retrieve the raw response from the HTTP request.
- *
- * The array structure is a little complex:
- *
- * $res = array(
- * 'headers' => array(),
- * 'response' => array(
- * 'code' => int,
- * 'message' => string
- * )
- * );
- *
- * All of the headers in $res['headers'] are with the name as the key and the
- * value as the value. So to get the User-Agent, you would do the following.
- *
- * $user_agent = $res['headers']['user-agent'];
- *
- * The body is the raw response content and can be retrieved from $res['body'].
- *
- * This function is called first to make the request and there are other API
- * functions to abstract out the above convoluted setup.
- *
- * Request method defaults for helper functions:
- * - Default 'GET' for wp_remote_get()
- * - Default 'POST' for wp_remote_post()
- * - Default 'HEAD' for wp_remote_head()
- *
- * @since 2.7.0
- *
- * @see WP_Http::request() For additional information on default arguments.
- *
- * @param string $url Site URL to retrieve.
- * @param array $args Optional. Request arguments. Default empty array.
- * @return WP_Error|array The response or WP_Error on failure.
- */
-function wp_remote_request($url, $args = array()) {
- $http = _wp_http_get_object();
- return $http->request( $url, $args );
-}
-
-/**
- * Retrieve the raw response from the HTTP request using the GET method.
- *
- * @since 2.7.0
- *
- * @see wp_remote_request() For more information on the response array format.
- * @see WP_Http::request() For default arguments information.
- *
- * @param string $url Site URL to retrieve.
- * @param array $args Optional. Request arguments. Default empty array.
- * @return WP_Error|array The response or WP_Error on failure.
- */
-function wp_remote_get($url, $args = array()) {
- $http = _wp_http_get_object();
- return $http->get( $url, $args );
-}
-
-/**
- * Retrieve the raw response from the HTTP request using the POST method.
- *
- * @since 2.7.0
- *
- * @see wp_remote_request() For more information on the response array format.
- * @see WP_Http::request() For default arguments information.
- *
- * @param string $url Site URL to retrieve.
- * @param array $args Optional. Request arguments. Default empty array.
- * @return WP_Error|array The response or WP_Error on failure.
- */
-function wp_remote_post($url, $args = array()) {
- $http = _wp_http_get_object();
- return $http->post( $url, $args );
-}
-
-/**
- * Retrieve the raw response from the HTTP request using the HEAD method.
- *
- * @since 2.7.0
- *
- * @see wp_remote_request() For more information on the response array format.
- * @see WP_Http::request() For default arguments information.
- *
- * @param string $url Site URL to retrieve.
- * @param array $args Optional. Request arguments. Default empty array.
- * @return WP_Error|array The response or WP_Error on failure.
- */
-function wp_remote_head($url, $args = array()) {
- $http = _wp_http_get_object();
- return $http->head( $url, $args );
-}
-
-/**
- * Retrieve only the headers from the raw response.
- *
- * @since 2.7.0
- *
- * @param array $response HTTP response.
- * @return array The headers of the response. Empty array if incorrect parameter given.
- */
-function wp_remote_retrieve_headers( $response ) {
- if ( is_wp_error($response) || ! isset($response['headers']) || ! is_array($response['headers']))
- return array();
-
- return $response['headers'];
-}
-
-/**
- * Retrieve a single header by name from the raw response.
- *
- * @since 2.7.0
- *
- * @param array $response
- * @param string $header Header name to retrieve value from.
- * @return string The header value. Empty string on if incorrect parameter given, or if the header doesn't exist.
- */
-function wp_remote_retrieve_header( $response, $header ) {
- if ( is_wp_error($response) || ! isset($response['headers']) || ! is_array($response['headers']))
- return '';
-
- if ( array_key_exists($header, $response['headers']) )
- return $response['headers'][$header];
-
- return '';
-}
-
-/**
- * Retrieve only the response code from the raw response.
- *
- * Will return an empty array if incorrect parameter value is given.
- *
- * @since 2.7.0
- *
- * @param array $response HTTP response.
- * @return int|string The response code as an integer. Empty string on incorrect parameter given.
- */
-function wp_remote_retrieve_response_code( $response ) {
- if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
- return '';
-
- return $response['response']['code'];
-}
-
-/**
- * Retrieve only the response message from the raw response.
- *
- * Will return an empty array if incorrect parameter value is given.
- *
- * @since 2.7.0
- *
- * @param array $response HTTP response.
- * @return string The response message. Empty string on incorrect parameter given.
- */
-function wp_remote_retrieve_response_message( $response ) {
- if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
- return '';
-
- return $response['response']['message'];
-}
-
-/**
- * Retrieve only the body from the raw response.
- *
- * @since 2.7.0
- *
- * @param array $response HTTP response.
- * @return string The body of the response. Empty string if no body or incorrect parameter given.
- */
-function wp_remote_retrieve_body( $response ) {
- if ( is_wp_error($response) || ! isset($response['body']) )
- return '';
-
- return $response['body'];
-}
-
-/**
- * Retrieve only the body from the raw response.
- *
- * @since 4.4.0
- *
- * @param array $response HTTP response.
- * @return array An array of `WP_Http_Cookie` objects from the response. Empty array if there are none, or the response is a WP_Error.
- */
-function wp_remote_retrieve_cookies( $response ) {
- if ( is_wp_error( $response ) || empty( $response['cookies'] ) ) {
- return array();
- }
-
- return $response['cookies'];
-}
-
-/**
- * Retrieve a single cookie by name from the raw response.
- *
- * @since 4.4.0
- *
- * @param array $response HTTP response.
- * @param string $name The name of the cookie to retrieve.
- * @return WP_Http_Cookie|string The `WP_Http_Cookie` object. Empty string if the cookie isn't present in the response.
- */
-function wp_remote_retrieve_cookie( $response, $name ) {
- $cookies = wp_remote_retrieve_cookies( $response );
-
- if ( empty( $cookies ) ) {
- return '';
- }
-
- foreach ( $cookies as $cookie ) {
- if ( $cookie->name === $name ) {
- return $cookie;
- }
- }
-
- return '';
-}
-
-/**
- * Retrieve a single cookie's value by name from the raw response.
- *
- * @since 4.4.0
- *
- * @param array $response HTTP response.
- * @param string $name The name of the cookie to retrieve.
- * @return string The value of the cookie. Empty string if the cookie isn't present in the response.
- */
-function wp_remote_retrieve_cookie_value( $response, $name ) {
- $cookie = wp_remote_retrieve_cookie( $response, $name );
-
- if ( ! is_a( $cookie, 'WP_Http_Cookie' ) ) {
- return '';
- }
-
- return $cookie->value;
-}
-
-/**
- * Determines if there is an HTTP Transport that can process this request.
- *
- * @since 3.2.0
- *
- * @param array $capabilities Array of capabilities to test or a wp_remote_request() $args array.
- * @param string $url Optional. If given, will check if the URL requires SSL and adds
- * that requirement to the capabilities array.
- *
- * @return bool
- */
-function wp_http_supports( $capabilities = array(), $url = null ) {
- $http = _wp_http_get_object();
-
- $capabilities = wp_parse_args( $capabilities );
-
- $count = count( $capabilities );
-
- // If we have a numeric $capabilities array, spoof a wp_remote_request() associative $args array
- if ( $count && count( array_filter( array_keys( $capabilities ), 'is_numeric' ) ) == $count ) {
- $capabilities = array_combine( array_values( $capabilities ), array_fill( 0, $count, true ) );
- }
-
- if ( $url && !isset( $capabilities['ssl'] ) ) {
- $scheme = parse_url( $url, PHP_URL_SCHEME );
- if ( 'https' == $scheme || 'ssl' == $scheme ) {
- $capabilities['ssl'] = true;
- }
- }
-
- return (bool) $http->_get_first_available_transport( $capabilities );
-}
-
-/**
- * Get the HTTP Origin of the current request.
- *
- * @since 3.4.0
- *
- * @return string URL of the origin. Empty string if no origin.
- */
-function get_http_origin() {
- $origin = '';
- if ( ! empty ( $_SERVER[ 'HTTP_ORIGIN' ] ) )
- $origin = $_SERVER[ 'HTTP_ORIGIN' ];
-
- /**
- * Change the origin of an HTTP request.
- *
- * @since 3.4.0
- *
- * @param string $origin The original origin for the request.
- */
- return apply_filters( 'http_origin', $origin );
-}
-
-/**
- * Retrieve list of allowed HTTP origins.
- *
- * @since 3.4.0
- *
- * @return array Array of origin URLs.
- */
-function get_allowed_http_origins() {
- $admin_origin = parse_url( admin_url() );
- $home_origin = parse_url( home_url() );
-
- // @todo preserve port?
- $allowed_origins = array_unique( array(
- 'http://' . $admin_origin[ 'host' ],
- 'https://' . $admin_origin[ 'host' ],
- 'http://' . $home_origin[ 'host' ],
- 'https://' . $home_origin[ 'host' ],
- ) );
-
- /**
- * Change the origin types allowed for HTTP requests.
- *
- * @since 3.4.0
- *
- * @param array $allowed_origins {
- * Default allowed HTTP origins.
- * @type string Non-secure URL for admin origin.
- * @type string Secure URL for admin origin.
- * @type string Non-secure URL for home origin.
- * @type string Secure URL for home origin.
- * }
- */
- return apply_filters( 'allowed_http_origins' , $allowed_origins );
-}
-
-/**
- * Determines if the HTTP origin is an authorized one.
- *
- * @since 3.4.0
- *
- * @param null|string $origin Origin URL. If not provided, the value of get_http_origin() is used.
- * @return string True if the origin is allowed. False otherwise.
- */
-function is_allowed_http_origin( $origin = null ) {
- $origin_arg = $origin;
-
- if ( null === $origin )
- $origin = get_http_origin();
-
- if ( $origin && ! in_array( $origin, get_allowed_http_origins() ) )
- $origin = '';
-
- /**
- * Change the allowed HTTP origin result.
- *
- * @since 3.4.0
- *
- * @param string $origin Result of check for allowed origin.
- * @param string $origin_arg Original origin string passed into is_allowed_http_origin function.
- */
- return apply_filters( 'allowed_http_origin', $origin, $origin_arg );
-}
-
-/**
- * Send Access-Control-Allow-Origin and related headers if the current request
- * is from an allowed origin.
- *
- * If the request is an OPTIONS request, the script exits with either access
- * control headers sent, or a 403 response if the origin is not allowed. For
- * other request methods, you will receive a return value.
- *
- * @since 3.4.0
- *
- * @return string|false Returns the origin URL if headers are sent. Returns false
- * if headers are not sent.
- */
-function send_origin_headers() {
- $origin = get_http_origin();
-
- if ( is_allowed_http_origin( $origin ) ) {
- @header( 'Access-Control-Allow-Origin: ' . $origin );
- @header( 'Access-Control-Allow-Credentials: true' );
- if ( 'OPTIONS' === $_SERVER['REQUEST_METHOD'] )
- exit;
- return $origin;
- }
-
- if ( 'OPTIONS' === $_SERVER['REQUEST_METHOD'] ) {
- status_header( 403 );
- exit;
- }
-
- return false;
-}
-
-/**
- * Validate a URL for safe use in the HTTP API.
- *
- * @since 3.5.2
- *
- * @param string $url
- * @return false|string URL or false on failure.
- */
-function wp_http_validate_url( $url ) {
- $original_url = $url;
- $url = wp_kses_bad_protocol( $url, array( 'http', 'https' ) );
- if ( ! $url || strtolower( $url ) !== strtolower( $original_url ) )
- return false;
-
- $parsed_url = @parse_url( $url );
- if ( ! $parsed_url || empty( $parsed_url['host'] ) )
- return false;
-
- if ( isset( $parsed_url['user'] ) || isset( $parsed_url['pass'] ) )
- return false;
-
- if ( false !== strpbrk( $parsed_url['host'], ':#?[]' ) )
- return false;
-
- $parsed_home = @parse_url( get_option( 'home' ) );
-
- $same_host = strtolower( $parsed_home['host'] ) === strtolower( $parsed_url['host'] );
-
- if ( ! $same_host ) {
- $host = trim( $parsed_url['host'], '.' );
- if ( preg_match( '#^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $host ) ) {
- $ip = $host;
- } else {
- $ip = gethostbyname( $host );
- if ( $ip === $host ) // Error condition for gethostbyname()
- $ip = false;
- }
- if ( $ip ) {
- $parts = array_map( 'intval', explode( '.', $ip ) );
- if ( 127 === $parts[0] || 10 === $parts[0]
- || ( 172 === $parts[0] && 16 <= $parts[1] && 31 >= $parts[1] )
- || ( 192 === $parts[0] && 168 === $parts[1] )
- ) {
- // If host appears local, reject unless specifically allowed.
- /**
- * Check if HTTP request is external or not.
- *
- * Allows to change and allow external requests for the HTTP request.
- *
- * @since 3.6.0
- *
- * @param bool false Whether HTTP request is external or not.
- * @param string $host IP of the requested host.
- * @param string $url URL of the requested host.
- */
- if ( ! apply_filters( 'http_request_host_is_external', false, $host, $url ) )
- return false;
- }
- }
- }
-
- if ( empty( $parsed_url['port'] ) )
- return $url;
-
- $port = $parsed_url['port'];
- if ( 80 === $port || 443 === $port || 8080 === $port )
- return $url;
-
- if ( $parsed_home && $same_host && isset( $parsed_home['port'] ) && $parsed_home['port'] === $port )
- return $url;
-
- return false;
-}
-
-/**
- * Whitelists allowed redirect hosts for safe HTTP requests as well.
- *
- * Attached to the http_request_host_is_external filter.
- *
- * @since 3.6.0
- *
- * @param bool $is_external
- * @param string $host
- * @return bool
- */
-function allowed_http_request_hosts( $is_external, $host ) {
- if ( ! $is_external && wp_validate_redirect( 'http://' . $host ) )
- $is_external = true;
- return $is_external;
-}
-
-/**
- * Whitelists any domain in a multisite installation for safe HTTP requests.
- *
- * Attached to the http_request_host_is_external filter.
- *
- * @since 3.6.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- * @staticvar array $queried
- *
- * @param bool $is_external
- * @param string $host
- * @return bool
- */
-function ms_allowed_http_request_hosts( $is_external, $host ) {
- global $wpdb;
- static $queried = array();
- if ( $is_external )
- return $is_external;
- if ( $host === get_current_site()->domain )
- return true;
- if ( isset( $queried[ $host ] ) )
- return $queried[ $host ];
- $queried[ $host ] = (bool) $wpdb->get_var( $wpdb->prepare( "SELECT domain FROM $wpdb->blogs WHERE domain = %s LIMIT 1", $host ) );
- return $queried[ $host ];
-}
-
-/**
- * A wrapper for PHP's parse_url() function that handles edgecases in < PHP 5.4.7
- *
- * PHP 5.4.7 expanded parse_url()'s ability to handle non-absolute url's, including
- * schemeless and relative url's with :// in the path, this works around those
- * limitations providing a standard output on PHP 5.2~5.4+.
- *
- * Error suppression is used as prior to PHP 5.3.3, an E_WARNING would be generated
- * when URL parsing failed.
- *
- * @since 4.4.0
- *
- * @param string $url The URL to parse.
- * @return bool|array False on failure; Array of URL components on success;
- * See parse_url()'s return values.
- */
-function wp_parse_url( $url ) {
- $parts = @parse_url( $url );
- if ( ! $parts ) {
- // < PHP 5.4.7 compat, trouble with relative paths including a scheme break in the path
- if ( '/' == $url[0] && false !== strpos( $url, '://' ) ) {
- // Since we know it's a relative path, prefix with a scheme/host placeholder and try again
- if ( ! $parts = @parse_url( 'placeholder://placeholder' . $url ) ) {
- return $parts;
- }
- // Remove the placeholder values
- unset( $parts['scheme'], $parts['host'] );
- } else {
- return $parts;
- }
- }
-
- // < PHP 5.4.7 compat, doesn't detect schemeless URL's host field
- if ( '//' == substr( $url, 0, 2 ) && ! isset( $parts['host'] ) ) {
- $path_parts = explode( '/', substr( $parts['path'], 2 ), 2 );
- $parts['host'] = $path_parts[0];
- if ( isset( $path_parts[1] ) ) {
- $parts['path'] = '/' . $path_parts[1];
- } else {
- unset( $parts['path'] );
- }
- }
-
- return $parts;
-}
</del></span></pre></div>
<a id="trunksrcwpincludeshttpphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/http.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/http.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/http.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,35 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Core HTTP Request API
- *
- * Standardizes the HTTP requests for WordPress. Handles cookies, gzip encoding and decoding, chunk
- * decoding, if HTTP 1.1 and various other difficult HTTP protocol implementations.
- *
- * @package WordPress
- * @subpackage HTTP
- * @since 2.7.0
- */
-
-/** Core HTTP API functionality */
-require_once( ABSPATH . WPINC . '/http-functions.php' );
-
-/** WP_Http class */
-require_once( ABSPATH . WPINC . '/class-http.php' );
-
-/** WP_Http_Streams class */
-require_once( ABSPATH . WPINC . '/class-wp-http-streams.php' );
-
-/** WP_Http_Curl transport class */
-require_once( ABSPATH . WPINC . '/class-wp-http-curl.php' );
-
-/** WP_HTTP_Proxy transport class */
-require_once( ABSPATH . WPINC . '/class-wp-http-proxy.php' );
-
-/** WP_Http_Cookie class */
-require_once( ABSPATH . WPINC . '/class-wp-http-cookie.php' );
-
-/** WP_Http_Encoding class */
-require_once( ABSPATH . WPINC . '/class-wp-http-encoding.php' );
-
-/** WP_HTTP_Response class */
-require_once( ABSPATH . WPINC . '/class-wp-http-response.php' );
</del></span></pre></div>
<a id="trunksrcwpincludeshttpphpfromrev35712trunksrcwpincludeshttpfunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/http.php (from rev 35712, trunk/src/wp-includes/http-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/http.php (rev 0)
+++ trunk/src/wp-includes/http.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,662 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Core HTTP Request API
+ *
+ * Standardizes the HTTP requests for WordPress. Handles cookies, gzip encoding and decoding, chunk
+ * decoding, if HTTP 1.1 and various other difficult HTTP protocol implementations.
+ *
+ * @package WordPress
+ * @subpackage HTTP
+ */
+
+/**
+ * Returns the initialized WP_Http Object
+ *
+ * @since 2.7.0
+ * @access private
+ *
+ * @staticvar WP_Http $http
+ *
+ * @return WP_Http HTTP Transport object.
+ */
+function _wp_http_get_object() {
+ static $http = null;
+
+ if ( is_null( $http ) ) {
+ $http = new WP_Http();
+ }
+ return $http;
+}
+
+/**
+ * Retrieve the raw response from a safe HTTP request.
+ *
+ * This function is ideal when the HTTP request is being made to an arbitrary
+ * URL. The URL is validated to avoid redirection and request forgery attacks.
+ *
+ * @since 3.6.0
+ *
+ * @see wp_remote_request() For more information on the response array format.
+ * @see WP_Http::request() For default arguments information.
+ *
+ * @param string $url Site URL to retrieve.
+ * @param array $args Optional. Request arguments. Default empty array.
+ * @return WP_Error|array The response or WP_Error on failure.
+ */
+function wp_safe_remote_request( $url, $args = array() ) {
+ $args['reject_unsafe_urls'] = true;
+ $http = _wp_http_get_object();
+ return $http->request( $url, $args );
+}
+
+/**
+ * Retrieve the raw response from a safe HTTP request using the GET method.
+ *
+ * This function is ideal when the HTTP request is being made to an arbitrary
+ * URL. The URL is validated to avoid redirection and request forgery attacks.
+ *
+ * @since 3.6.0
+ *
+ * @see wp_remote_request() For more information on the response array format.
+ * @see WP_Http::request() For default arguments information.
+ *
+ * @param string $url Site URL to retrieve.
+ * @param array $args Optional. Request arguments. Default empty array.
+ * @return WP_Error|array The response or WP_Error on failure.
+ */
+function wp_safe_remote_get( $url, $args = array() ) {
+ $args['reject_unsafe_urls'] = true;
+ $http = _wp_http_get_object();
+ return $http->get( $url, $args );
+}
+
+/**
+ * Retrieve the raw response from a safe HTTP request using the POST method.
+ *
+ * This function is ideal when the HTTP request is being made to an arbitrary
+ * URL. The URL is validated to avoid redirection and request forgery attacks.
+ *
+ * @since 3.6.0
+ *
+ * @see wp_remote_request() For more information on the response array format.
+ * @see WP_Http::request() For default arguments information.
+ *
+ * @param string $url Site URL to retrieve.
+ * @param array $args Optional. Request arguments. Default empty array.
+ * @return WP_Error|array The response or WP_Error on failure.
+ */
+function wp_safe_remote_post( $url, $args = array() ) {
+ $args['reject_unsafe_urls'] = true;
+ $http = _wp_http_get_object();
+ return $http->post( $url, $args );
+}
+
+/**
+ * Retrieve the raw response from a safe HTTP request using the HEAD method.
+ *
+ * This function is ideal when the HTTP request is being made to an arbitrary
+ * URL. The URL is validated to avoid redirection and request forgery attacks.
+ *
+ * @since 3.6.0
+ *
+ * @see wp_remote_request() For more information on the response array format.
+ * @see WP_Http::request() For default arguments information.
+ *
+ * @param string $url Site URL to retrieve.
+ * @param array $args Optional. Request arguments. Default empty array.
+ * @return WP_Error|array The response or WP_Error on failure.
+ */
+function wp_safe_remote_head( $url, $args = array() ) {
+ $args['reject_unsafe_urls'] = true;
+ $http = _wp_http_get_object();
+ return $http->head( $url, $args );
+}
+
+/**
+ * Retrieve the raw response from the HTTP request.
+ *
+ * The array structure is a little complex:
+ *
+ * $res = array(
+ * 'headers' => array(),
+ * 'response' => array(
+ * 'code' => int,
+ * 'message' => string
+ * )
+ * );
+ *
+ * All of the headers in $res['headers'] are with the name as the key and the
+ * value as the value. So to get the User-Agent, you would do the following.
+ *
+ * $user_agent = $res['headers']['user-agent'];
+ *
+ * The body is the raw response content and can be retrieved from $res['body'].
+ *
+ * This function is called first to make the request and there are other API
+ * functions to abstract out the above convoluted setup.
+ *
+ * Request method defaults for helper functions:
+ * - Default 'GET' for wp_remote_get()
+ * - Default 'POST' for wp_remote_post()
+ * - Default 'HEAD' for wp_remote_head()
+ *
+ * @since 2.7.0
+ *
+ * @see WP_Http::request() For additional information on default arguments.
+ *
+ * @param string $url Site URL to retrieve.
+ * @param array $args Optional. Request arguments. Default empty array.
+ * @return WP_Error|array The response or WP_Error on failure.
+ */
+function wp_remote_request($url, $args = array()) {
+ $http = _wp_http_get_object();
+ return $http->request( $url, $args );
+}
+
+/**
+ * Retrieve the raw response from the HTTP request using the GET method.
+ *
+ * @since 2.7.0
+ *
+ * @see wp_remote_request() For more information on the response array format.
+ * @see WP_Http::request() For default arguments information.
+ *
+ * @param string $url Site URL to retrieve.
+ * @param array $args Optional. Request arguments. Default empty array.
+ * @return WP_Error|array The response or WP_Error on failure.
+ */
+function wp_remote_get($url, $args = array()) {
+ $http = _wp_http_get_object();
+ return $http->get( $url, $args );
+}
+
+/**
+ * Retrieve the raw response from the HTTP request using the POST method.
+ *
+ * @since 2.7.0
+ *
+ * @see wp_remote_request() For more information on the response array format.
+ * @see WP_Http::request() For default arguments information.
+ *
+ * @param string $url Site URL to retrieve.
+ * @param array $args Optional. Request arguments. Default empty array.
+ * @return WP_Error|array The response or WP_Error on failure.
+ */
+function wp_remote_post($url, $args = array()) {
+ $http = _wp_http_get_object();
+ return $http->post( $url, $args );
+}
+
+/**
+ * Retrieve the raw response from the HTTP request using the HEAD method.
+ *
+ * @since 2.7.0
+ *
+ * @see wp_remote_request() For more information on the response array format.
+ * @see WP_Http::request() For default arguments information.
+ *
+ * @param string $url Site URL to retrieve.
+ * @param array $args Optional. Request arguments. Default empty array.
+ * @return WP_Error|array The response or WP_Error on failure.
+ */
+function wp_remote_head($url, $args = array()) {
+ $http = _wp_http_get_object();
+ return $http->head( $url, $args );
+}
+
+/**
+ * Retrieve only the headers from the raw response.
+ *
+ * @since 2.7.0
+ *
+ * @param array $response HTTP response.
+ * @return array The headers of the response. Empty array if incorrect parameter given.
+ */
+function wp_remote_retrieve_headers( $response ) {
+ if ( is_wp_error($response) || ! isset($response['headers']) || ! is_array($response['headers']))
+ return array();
+
+ return $response['headers'];
+}
+
+/**
+ * Retrieve a single header by name from the raw response.
+ *
+ * @since 2.7.0
+ *
+ * @param array $response
+ * @param string $header Header name to retrieve value from.
+ * @return string The header value. Empty string on if incorrect parameter given, or if the header doesn't exist.
+ */
+function wp_remote_retrieve_header( $response, $header ) {
+ if ( is_wp_error($response) || ! isset($response['headers']) || ! is_array($response['headers']))
+ return '';
+
+ if ( array_key_exists($header, $response['headers']) )
+ return $response['headers'][$header];
+
+ return '';
+}
+
+/**
+ * Retrieve only the response code from the raw response.
+ *
+ * Will return an empty array if incorrect parameter value is given.
+ *
+ * @since 2.7.0
+ *
+ * @param array $response HTTP response.
+ * @return int|string The response code as an integer. Empty string on incorrect parameter given.
+ */
+function wp_remote_retrieve_response_code( $response ) {
+ if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
+ return '';
+
+ return $response['response']['code'];
+}
+
+/**
+ * Retrieve only the response message from the raw response.
+ *
+ * Will return an empty array if incorrect parameter value is given.
+ *
+ * @since 2.7.0
+ *
+ * @param array $response HTTP response.
+ * @return string The response message. Empty string on incorrect parameter given.
+ */
+function wp_remote_retrieve_response_message( $response ) {
+ if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
+ return '';
+
+ return $response['response']['message'];
+}
+
+/**
+ * Retrieve only the body from the raw response.
+ *
+ * @since 2.7.0
+ *
+ * @param array $response HTTP response.
+ * @return string The body of the response. Empty string if no body or incorrect parameter given.
+ */
+function wp_remote_retrieve_body( $response ) {
+ if ( is_wp_error($response) || ! isset($response['body']) )
+ return '';
+
+ return $response['body'];
+}
+
+/**
+ * Retrieve only the body from the raw response.
+ *
+ * @since 4.4.0
+ *
+ * @param array $response HTTP response.
+ * @return array An array of `WP_Http_Cookie` objects from the response. Empty array if there are none, or the response is a WP_Error.
+ */
+function wp_remote_retrieve_cookies( $response ) {
+ if ( is_wp_error( $response ) || empty( $response['cookies'] ) ) {
+ return array();
+ }
+
+ return $response['cookies'];
+}
+
+/**
+ * Retrieve a single cookie by name from the raw response.
+ *
+ * @since 4.4.0
+ *
+ * @param array $response HTTP response.
+ * @param string $name The name of the cookie to retrieve.
+ * @return WP_Http_Cookie|string The `WP_Http_Cookie` object. Empty string if the cookie isn't present in the response.
+ */
+function wp_remote_retrieve_cookie( $response, $name ) {
+ $cookies = wp_remote_retrieve_cookies( $response );
+
+ if ( empty( $cookies ) ) {
+ return '';
+ }
+
+ foreach ( $cookies as $cookie ) {
+ if ( $cookie->name === $name ) {
+ return $cookie;
+ }
+ }
+
+ return '';
+}
+
+/**
+ * Retrieve a single cookie's value by name from the raw response.
+ *
+ * @since 4.4.0
+ *
+ * @param array $response HTTP response.
+ * @param string $name The name of the cookie to retrieve.
+ * @return string The value of the cookie. Empty string if the cookie isn't present in the response.
+ */
+function wp_remote_retrieve_cookie_value( $response, $name ) {
+ $cookie = wp_remote_retrieve_cookie( $response, $name );
+
+ if ( ! is_a( $cookie, 'WP_Http_Cookie' ) ) {
+ return '';
+ }
+
+ return $cookie->value;
+}
+
+/**
+ * Determines if there is an HTTP Transport that can process this request.
+ *
+ * @since 3.2.0
+ *
+ * @param array $capabilities Array of capabilities to test or a wp_remote_request() $args array.
+ * @param string $url Optional. If given, will check if the URL requires SSL and adds
+ * that requirement to the capabilities array.
+ *
+ * @return bool
+ */
+function wp_http_supports( $capabilities = array(), $url = null ) {
+ $http = _wp_http_get_object();
+
+ $capabilities = wp_parse_args( $capabilities );
+
+ $count = count( $capabilities );
+
+ // If we have a numeric $capabilities array, spoof a wp_remote_request() associative $args array
+ if ( $count && count( array_filter( array_keys( $capabilities ), 'is_numeric' ) ) == $count ) {
+ $capabilities = array_combine( array_values( $capabilities ), array_fill( 0, $count, true ) );
+ }
+
+ if ( $url && !isset( $capabilities['ssl'] ) ) {
+ $scheme = parse_url( $url, PHP_URL_SCHEME );
+ if ( 'https' == $scheme || 'ssl' == $scheme ) {
+ $capabilities['ssl'] = true;
+ }
+ }
+
+ return (bool) $http->_get_first_available_transport( $capabilities );
+}
+
+/**
+ * Get the HTTP Origin of the current request.
+ *
+ * @since 3.4.0
+ *
+ * @return string URL of the origin. Empty string if no origin.
+ */
+function get_http_origin() {
+ $origin = '';
+ if ( ! empty ( $_SERVER[ 'HTTP_ORIGIN' ] ) )
+ $origin = $_SERVER[ 'HTTP_ORIGIN' ];
+
+ /**
+ * Change the origin of an HTTP request.
+ *
+ * @since 3.4.0
+ *
+ * @param string $origin The original origin for the request.
+ */
+ return apply_filters( 'http_origin', $origin );
+}
+
+/**
+ * Retrieve list of allowed HTTP origins.
+ *
+ * @since 3.4.0
+ *
+ * @return array Array of origin URLs.
+ */
+function get_allowed_http_origins() {
+ $admin_origin = parse_url( admin_url() );
+ $home_origin = parse_url( home_url() );
+
+ // @todo preserve port?
+ $allowed_origins = array_unique( array(
+ 'http://' . $admin_origin[ 'host' ],
+ 'https://' . $admin_origin[ 'host' ],
+ 'http://' . $home_origin[ 'host' ],
+ 'https://' . $home_origin[ 'host' ],
+ ) );
+
+ /**
+ * Change the origin types allowed for HTTP requests.
+ *
+ * @since 3.4.0
+ *
+ * @param array $allowed_origins {
+ * Default allowed HTTP origins.
+ * @type string Non-secure URL for admin origin.
+ * @type string Secure URL for admin origin.
+ * @type string Non-secure URL for home origin.
+ * @type string Secure URL for home origin.
+ * }
+ */
+ return apply_filters( 'allowed_http_origins' , $allowed_origins );
+}
+
+/**
+ * Determines if the HTTP origin is an authorized one.
+ *
+ * @since 3.4.0
+ *
+ * @param null|string $origin Origin URL. If not provided, the value of get_http_origin() is used.
+ * @return string True if the origin is allowed. False otherwise.
+ */
+function is_allowed_http_origin( $origin = null ) {
+ $origin_arg = $origin;
+
+ if ( null === $origin )
+ $origin = get_http_origin();
+
+ if ( $origin && ! in_array( $origin, get_allowed_http_origins() ) )
+ $origin = '';
+
+ /**
+ * Change the allowed HTTP origin result.
+ *
+ * @since 3.4.0
+ *
+ * @param string $origin Result of check for allowed origin.
+ * @param string $origin_arg Original origin string passed into is_allowed_http_origin function.
+ */
+ return apply_filters( 'allowed_http_origin', $origin, $origin_arg );
+}
+
+/**
+ * Send Access-Control-Allow-Origin and related headers if the current request
+ * is from an allowed origin.
+ *
+ * If the request is an OPTIONS request, the script exits with either access
+ * control headers sent, or a 403 response if the origin is not allowed. For
+ * other request methods, you will receive a return value.
+ *
+ * @since 3.4.0
+ *
+ * @return string|false Returns the origin URL if headers are sent. Returns false
+ * if headers are not sent.
+ */
+function send_origin_headers() {
+ $origin = get_http_origin();
+
+ if ( is_allowed_http_origin( $origin ) ) {
+ @header( 'Access-Control-Allow-Origin: ' . $origin );
+ @header( 'Access-Control-Allow-Credentials: true' );
+ if ( 'OPTIONS' === $_SERVER['REQUEST_METHOD'] )
+ exit;
+ return $origin;
+ }
+
+ if ( 'OPTIONS' === $_SERVER['REQUEST_METHOD'] ) {
+ status_header( 403 );
+ exit;
+ }
+
+ return false;
+}
+
+/**
+ * Validate a URL for safe use in the HTTP API.
+ *
+ * @since 3.5.2
+ *
+ * @param string $url
+ * @return false|string URL or false on failure.
+ */
+function wp_http_validate_url( $url ) {
+ $original_url = $url;
+ $url = wp_kses_bad_protocol( $url, array( 'http', 'https' ) );
+ if ( ! $url || strtolower( $url ) !== strtolower( $original_url ) )
+ return false;
+
+ $parsed_url = @parse_url( $url );
+ if ( ! $parsed_url || empty( $parsed_url['host'] ) )
+ return false;
+
+ if ( isset( $parsed_url['user'] ) || isset( $parsed_url['pass'] ) )
+ return false;
+
+ if ( false !== strpbrk( $parsed_url['host'], ':#?[]' ) )
+ return false;
+
+ $parsed_home = @parse_url( get_option( 'home' ) );
+
+ $same_host = strtolower( $parsed_home['host'] ) === strtolower( $parsed_url['host'] );
+
+ if ( ! $same_host ) {
+ $host = trim( $parsed_url['host'], '.' );
+ if ( preg_match( '#^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $host ) ) {
+ $ip = $host;
+ } else {
+ $ip = gethostbyname( $host );
+ if ( $ip === $host ) // Error condition for gethostbyname()
+ $ip = false;
+ }
+ if ( $ip ) {
+ $parts = array_map( 'intval', explode( '.', $ip ) );
+ if ( 127 === $parts[0] || 10 === $parts[0]
+ || ( 172 === $parts[0] && 16 <= $parts[1] && 31 >= $parts[1] )
+ || ( 192 === $parts[0] && 168 === $parts[1] )
+ ) {
+ // If host appears local, reject unless specifically allowed.
+ /**
+ * Check if HTTP request is external or not.
+ *
+ * Allows to change and allow external requests for the HTTP request.
+ *
+ * @since 3.6.0
+ *
+ * @param bool false Whether HTTP request is external or not.
+ * @param string $host IP of the requested host.
+ * @param string $url URL of the requested host.
+ */
+ if ( ! apply_filters( 'http_request_host_is_external', false, $host, $url ) )
+ return false;
+ }
+ }
+ }
+
+ if ( empty( $parsed_url['port'] ) )
+ return $url;
+
+ $port = $parsed_url['port'];
+ if ( 80 === $port || 443 === $port || 8080 === $port )
+ return $url;
+
+ if ( $parsed_home && $same_host && isset( $parsed_home['port'] ) && $parsed_home['port'] === $port )
+ return $url;
+
+ return false;
+}
+
+/**
+ * Whitelists allowed redirect hosts for safe HTTP requests as well.
+ *
+ * Attached to the http_request_host_is_external filter.
+ *
+ * @since 3.6.0
+ *
+ * @param bool $is_external
+ * @param string $host
+ * @return bool
+ */
+function allowed_http_request_hosts( $is_external, $host ) {
+ if ( ! $is_external && wp_validate_redirect( 'http://' . $host ) )
+ $is_external = true;
+ return $is_external;
+}
+
+/**
+ * Whitelists any domain in a multisite installation for safe HTTP requests.
+ *
+ * Attached to the http_request_host_is_external filter.
+ *
+ * @since 3.6.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ * @staticvar array $queried
+ *
+ * @param bool $is_external
+ * @param string $host
+ * @return bool
+ */
+function ms_allowed_http_request_hosts( $is_external, $host ) {
+ global $wpdb;
+ static $queried = array();
+ if ( $is_external )
+ return $is_external;
+ if ( $host === get_current_site()->domain )
+ return true;
+ if ( isset( $queried[ $host ] ) )
+ return $queried[ $host ];
+ $queried[ $host ] = (bool) $wpdb->get_var( $wpdb->prepare( "SELECT domain FROM $wpdb->blogs WHERE domain = %s LIMIT 1", $host ) );
+ return $queried[ $host ];
+}
+
+/**
+ * A wrapper for PHP's parse_url() function that handles edgecases in < PHP 5.4.7
+ *
+ * PHP 5.4.7 expanded parse_url()'s ability to handle non-absolute url's, including
+ * schemeless and relative url's with :// in the path, this works around those
+ * limitations providing a standard output on PHP 5.2~5.4+.
+ *
+ * Error suppression is used as prior to PHP 5.3.3, an E_WARNING would be generated
+ * when URL parsing failed.
+ *
+ * @since 4.4.0
+ *
+ * @param string $url The URL to parse.
+ * @return bool|array False on failure; Array of URL components on success;
+ * See parse_url()'s return values.
+ */
+function wp_parse_url( $url ) {
+ $parts = @parse_url( $url );
+ if ( ! $parts ) {
+ // < PHP 5.4.7 compat, trouble with relative paths including a scheme break in the path
+ if ( '/' == $url[0] && false !== strpos( $url, '://' ) ) {
+ // Since we know it's a relative path, prefix with a scheme/host placeholder and try again
+ if ( ! $parts = @parse_url( 'placeholder://placeholder' . $url ) ) {
+ return $parts;
+ }
+ // Remove the placeholder values
+ unset( $parts['scheme'], $parts['host'] );
+ } else {
+ return $parts;
+ }
+ }
+
+ // < PHP 5.4.7 compat, doesn't detect schemeless URL's host field
+ if ( '//' == substr( $url, 0, 2 ) && ! isset( $parts['host'] ) ) {
+ $path_parts = explode( '/', substr( $parts['path'], 2 ), 2 );
+ $parts['host'] = $path_parts[0];
+ if ( isset( $path_parts[1] ) ) {
+ $parts['path'] = '/' . $path_parts[1];
+ } else {
+ unset( $parts['path'] );
+ }
+ }
+
+ return $parts;
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesmetafunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/meta-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/meta-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/meta-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,968 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Meta API: Top-level metadata functionality
- *
- * Functions for retrieving and manipulating metadata of various WordPress object types. Metadata
- * for an object is a represented by a simple key-value pair. Objects may contain multiple
- * metadata entries that share the same key and differ only in their value.
- *
- * @package WordPress
- * @subpackage Meta
- * @since 4.4.0
- */
-
-/**
- * Add metadata for the specified object.
- *
- * @since 2.9.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
- * @param int $object_id ID of the object metadata is for
- * @param string $meta_key Metadata key
- * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
- * @param bool $unique Optional, default is false.
- * Whether the specified metadata key should be unique for the object.
- * If true, and the object already has a value for the specified metadata key,
- * no change will be made.
- * @return int|false The meta ID on success, false on failure.
- */
-function add_metadata($meta_type, $object_id, $meta_key, $meta_value, $unique = false) {
- global $wpdb;
-
- if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
- return false;
- }
-
- $object_id = absint( $object_id );
- if ( ! $object_id ) {
- return false;
- }
-
- $table = _get_meta_table( $meta_type );
- if ( ! $table ) {
- return false;
- }
-
- $column = sanitize_key($meta_type . '_id');
-
- // expected_slashed ($meta_key)
- $meta_key = wp_unslash($meta_key);
- $meta_value = wp_unslash($meta_value);
- $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
-
- /**
- * Filter whether to add metadata of a specific type.
- *
- * The dynamic portion of the hook, `$meta_type`, refers to the meta
- * object type (comment, post, or user). Returning a non-null value
- * will effectively short-circuit the function.
- *
- * @since 3.1.0
- *
- * @param null|bool $check Whether to allow adding metadata for the given type.
- * @param int $object_id Object ID.
- * @param string $meta_key Meta key.
- * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
- * @param bool $unique Whether the specified meta key should be unique
- * for the object. Optional. Default false.
- */
- $check = apply_filters( "add_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $unique );
- if ( null !== $check )
- return $check;
-
- if ( $unique && $wpdb->get_var( $wpdb->prepare(
- "SELECT COUNT(*) FROM $table WHERE meta_key = %s AND $column = %d",
- $meta_key, $object_id ) ) )
- return false;
-
- $_meta_value = $meta_value;
- $meta_value = maybe_serialize( $meta_value );
-
- /**
- * Fires immediately before meta of a specific type is added.
- *
- * The dynamic portion of the hook, `$meta_type`, refers to the meta
- * object type (comment, post, or user).
- *
- * @since 3.1.0
- *
- * @param int $object_id Object ID.
- * @param string $meta_key Meta key.
- * @param mixed $meta_value Meta value.
- */
- do_action( "add_{$meta_type}_meta", $object_id, $meta_key, $_meta_value );
-
- $result = $wpdb->insert( $table, array(
- $column => $object_id,
- 'meta_key' => $meta_key,
- 'meta_value' => $meta_value
- ) );
-
- if ( ! $result )
- return false;
-
- $mid = (int) $wpdb->insert_id;
-
- wp_cache_delete($object_id, $meta_type . '_meta');
-
- /**
- * Fires immediately after meta of a specific type is added.
- *
- * The dynamic portion of the hook, `$meta_type`, refers to the meta
- * object type (comment, post, or user).
- *
- * @since 2.9.0
- *
- * @param int $mid The meta ID after successful update.
- * @param int $object_id Object ID.
- * @param string $meta_key Meta key.
- * @param mixed $meta_value Meta value.
- */
- do_action( "added_{$meta_type}_meta", $mid, $object_id, $meta_key, $_meta_value );
-
- return $mid;
-}
-
-/**
- * Update metadata for the specified object. If no value already exists for the specified object
- * ID and metadata key, the metadata will be added.
- *
- * @since 2.9.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
- * @param int $object_id ID of the object metadata is for
- * @param string $meta_key Metadata key
- * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
- * @param mixed $prev_value Optional. If specified, only update existing metadata entries with
- * the specified value. Otherwise, update all entries.
- * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
- */
-function update_metadata($meta_type, $object_id, $meta_key, $meta_value, $prev_value = '') {
- global $wpdb;
-
- if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
- return false;
- }
-
- $object_id = absint( $object_id );
- if ( ! $object_id ) {
- return false;
- }
-
- $table = _get_meta_table( $meta_type );
- if ( ! $table ) {
- return false;
- }
-
- $column = sanitize_key($meta_type . '_id');
- $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
-
- // expected_slashed ($meta_key)
- $meta_key = wp_unslash($meta_key);
- $passed_value = $meta_value;
- $meta_value = wp_unslash($meta_value);
- $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
-
- /**
- * Filter whether to update metadata of a specific type.
- *
- * The dynamic portion of the hook, `$meta_type`, refers to the meta
- * object type (comment, post, or user). Returning a non-null value
- * will effectively short-circuit the function.
- *
- * @since 3.1.0
- *
- * @param null|bool $check Whether to allow updating metadata for the given type.
- * @param int $object_id Object ID.
- * @param string $meta_key Meta key.
- * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
- * @param mixed $prev_value Optional. If specified, only update existing
- * metadata entries with the specified value.
- * Otherwise, update all entries.
- */
- $check = apply_filters( "update_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $prev_value );
- if ( null !== $check )
- return (bool) $check;
-
- // Compare existing value to new value if no prev value given and the key exists only once.
- if ( empty($prev_value) ) {
- $old_value = get_metadata($meta_type, $object_id, $meta_key);
- if ( count($old_value) == 1 ) {
- if ( $old_value[0] === $meta_value )
- return false;
- }
- }
-
- $meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s AND $column = %d", $meta_key, $object_id ) );
- if ( empty( $meta_ids ) ) {
- return add_metadata($meta_type, $object_id, $meta_key, $passed_value);
- }
-
- $_meta_value = $meta_value;
- $meta_value = maybe_serialize( $meta_value );
-
- $data = compact( 'meta_value' );
- $where = array( $column => $object_id, 'meta_key' => $meta_key );
-
- if ( !empty( $prev_value ) ) {
- $prev_value = maybe_serialize($prev_value);
- $where['meta_value'] = $prev_value;
- }
-
- foreach ( $meta_ids as $meta_id ) {
- /**
- * Fires immediately before updating metadata of a specific type.
- *
- * The dynamic portion of the hook, `$meta_type`, refers to the meta
- * object type (comment, post, or user).
- *
- * @since 2.9.0
- *
- * @param int $meta_id ID of the metadata entry to update.
- * @param int $object_id Object ID.
- * @param string $meta_key Meta key.
- * @param mixed $meta_value Meta value.
- */
- do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
- }
-
- if ( 'post' == $meta_type ) {
- foreach ( $meta_ids as $meta_id ) {
- /**
- * Fires immediately before updating a post's metadata.
- *
- * @since 2.9.0
- *
- * @param int $meta_id ID of metadata entry to update.
- * @param int $object_id Object ID.
- * @param string $meta_key Meta key.
- * @param mixed $meta_value Meta value.
- */
- do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
- }
- }
-
- $result = $wpdb->update( $table, $data, $where );
- if ( ! $result )
- return false;
-
- wp_cache_delete($object_id, $meta_type . '_meta');
-
- foreach ( $meta_ids as $meta_id ) {
- /**
- * Fires immediately after updating metadata of a specific type.
- *
- * The dynamic portion of the hook, `$meta_type`, refers to the meta
- * object type (comment, post, or user).
- *
- * @since 2.9.0
- *
- * @param int $meta_id ID of updated metadata entry.
- * @param int $object_id Object ID.
- * @param string $meta_key Meta key.
- * @param mixed $meta_value Meta value.
- */
- do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
- }
-
- if ( 'post' == $meta_type ) {
- foreach ( $meta_ids as $meta_id ) {
- /**
- * Fires immediately after updating a post's metadata.
- *
- * @since 2.9.0
- *
- * @param int $meta_id ID of updated metadata entry.
- * @param int $object_id Object ID.
- * @param string $meta_key Meta key.
- * @param mixed $meta_value Meta value.
- */
- do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
- }
- }
-
- return true;
-}
-
-/**
- * Delete metadata for the specified object.
- *
- * @since 2.9.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
- * @param int $object_id ID of the object metadata is for
- * @param string $meta_key Metadata key
- * @param mixed $meta_value Optional. Metadata value. Must be serializable if non-scalar. If specified, only delete
- * metadata entries with this value. Otherwise, delete all entries with the specified meta_key.
- * Pass `null, `false`, or an empty string to skip this check. (For backward compatibility,
- * it is not possible to pass an empty string to delete those entries with an empty string
- * for a value.)
- * @param bool $delete_all Optional, default is false. If true, delete matching metadata entries for all objects,
- * ignoring the specified object_id. Otherwise, only delete matching metadata entries for
- * the specified object_id.
- * @return bool True on successful delete, false on failure.
- */
-function delete_metadata($meta_type, $object_id, $meta_key, $meta_value = '', $delete_all = false) {
- global $wpdb;
-
- if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) && ! $delete_all ) {
- return false;
- }
-
- $object_id = absint( $object_id );
- if ( ! $object_id && ! $delete_all ) {
- return false;
- }
-
- $table = _get_meta_table( $meta_type );
- if ( ! $table ) {
- return false;
- }
-
- $type_column = sanitize_key($meta_type . '_id');
- $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
- // expected_slashed ($meta_key)
- $meta_key = wp_unslash($meta_key);
- $meta_value = wp_unslash($meta_value);
-
- /**
- * Filter whether to delete metadata of a specific type.
- *
- * The dynamic portion of the hook, `$meta_type`, refers to the meta
- * object type (comment, post, or user). Returning a non-null value
- * will effectively short-circuit the function.
- *
- * @since 3.1.0
- *
- * @param null|bool $delete Whether to allow metadata deletion of the given type.
- * @param int $object_id Object ID.
- * @param string $meta_key Meta key.
- * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
- * @param bool $delete_all Whether to delete the matching metadata entries
- * for all objects, ignoring the specified $object_id.
- * Default false.
- */
- $check = apply_filters( "delete_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $delete_all );
- if ( null !== $check )
- return (bool) $check;
-
- $_meta_value = $meta_value;
- $meta_value = maybe_serialize( $meta_value );
-
- $query = $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s", $meta_key );
-
- if ( !$delete_all )
- $query .= $wpdb->prepare(" AND $type_column = %d", $object_id );
-
- if ( '' !== $meta_value && null !== $meta_value && false !== $meta_value )
- $query .= $wpdb->prepare(" AND meta_value = %s", $meta_value );
-
- $meta_ids = $wpdb->get_col( $query );
- if ( !count( $meta_ids ) )
- return false;
-
- if ( $delete_all )
- $object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s", $meta_key ) );
-
- /**
- * Fires immediately before deleting metadata of a specific type.
- *
- * The dynamic portion of the hook, `$meta_type`, refers to the meta
- * object type (comment, post, or user).
- *
- * @since 3.1.0
- *
- * @param array $meta_ids An array of metadata entry IDs to delete.
- * @param int $object_id Object ID.
- * @param string $meta_key Meta key.
- * @param mixed $meta_value Meta value.
- */
- do_action( "delete_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
-
- // Old-style action.
- if ( 'post' == $meta_type ) {
- /**
- * Fires immediately before deleting metadata for a post.
- *
- * @since 2.9.0
- *
- * @param array $meta_ids An array of post metadata entry IDs to delete.
- */
- do_action( 'delete_postmeta', $meta_ids );
- }
-
- $query = "DELETE FROM $table WHERE $id_column IN( " . implode( ',', $meta_ids ) . " )";
-
- $count = $wpdb->query($query);
-
- if ( !$count )
- return false;
-
- if ( $delete_all ) {
- foreach ( (array) $object_ids as $o_id ) {
- wp_cache_delete($o_id, $meta_type . '_meta');
- }
- } else {
- wp_cache_delete($object_id, $meta_type . '_meta');
- }
-
- /**
- * Fires immediately after deleting metadata of a specific type.
- *
- * The dynamic portion of the hook name, `$meta_type`, refers to the meta
- * object type (comment, post, or user).
- *
- * @since 2.9.0
- *
- * @param array $meta_ids An array of deleted metadata entry IDs.
- * @param int $object_id Object ID.
- * @param string $meta_key Meta key.
- * @param mixed $meta_value Meta value.
- */
- do_action( "deleted_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
-
- // Old-style action.
- if ( 'post' == $meta_type ) {
- /**
- * Fires immediately after deleting metadata for a post.
- *
- * @since 2.9.0
- *
- * @param array $meta_ids An array of deleted post metadata entry IDs.
- */
- do_action( 'deleted_postmeta', $meta_ids );
- }
-
- return true;
-}
-
-/**
- * Retrieve metadata for the specified object.
- *
- * @since 2.9.0
- *
- * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
- * @param int $object_id ID of the object metadata is for
- * @param string $meta_key Optional. Metadata key. If not specified, retrieve all metadata for
- * the specified object.
- * @param bool $single Optional, default is false.
- * If true, return only the first value of the specified meta_key.
- * This parameter has no effect if meta_key is not specified.
- * @return mixed Single metadata value, or array of values
- */
-function get_metadata($meta_type, $object_id, $meta_key = '', $single = false) {
- if ( ! $meta_type || ! is_numeric( $object_id ) ) {
- return false;
- }
-
- $object_id = absint( $object_id );
- if ( ! $object_id ) {
- return false;
- }
-
- /**
- * Filter whether to retrieve metadata of a specific type.
- *
- * The dynamic portion of the hook, `$meta_type`, refers to the meta
- * object type (comment, post, or user). Returning a non-null value
- * will effectively short-circuit the function.
- *
- * @since 3.1.0
- *
- * @param null|array|string $value The value get_metadata() should return - a single metadata value,
- * or an array of values.
- * @param int $object_id Object ID.
- * @param string $meta_key Meta key.
- * @param bool $single Whether to return only the first value of the specified $meta_key.
- */
- $check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, $single );
- if ( null !== $check ) {
- if ( $single && is_array( $check ) )
- return $check[0];
- else
- return $check;
- }
-
- $meta_cache = wp_cache_get($object_id, $meta_type . '_meta');
-
- if ( !$meta_cache ) {
- $meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
- $meta_cache = $meta_cache[$object_id];
- }
-
- if ( ! $meta_key ) {
- return $meta_cache;
- }
-
- if ( isset($meta_cache[$meta_key]) ) {
- if ( $single )
- return maybe_unserialize( $meta_cache[$meta_key][0] );
- else
- return array_map('maybe_unserialize', $meta_cache[$meta_key]);
- }
-
- if ($single)
- return '';
- else
- return array();
-}
-
-/**
- * Determine if a meta key is set for a given object
- *
- * @since 3.3.0
- *
- * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
- * @param int $object_id ID of the object metadata is for
- * @param string $meta_key Metadata key.
- * @return bool True of the key is set, false if not.
- */
-function metadata_exists( $meta_type, $object_id, $meta_key ) {
- if ( ! $meta_type || ! is_numeric( $object_id ) ) {
- return false;
- }
-
- $object_id = absint( $object_id );
- if ( ! $object_id ) {
- return false;
- }
-
- /** This filter is documented in wp-includes/meta-functions.php */
- $check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, true );
- if ( null !== $check )
- return (bool) $check;
-
- $meta_cache = wp_cache_get( $object_id, $meta_type . '_meta' );
-
- if ( !$meta_cache ) {
- $meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
- $meta_cache = $meta_cache[$object_id];
- }
-
- if ( isset( $meta_cache[ $meta_key ] ) )
- return true;
-
- return false;
-}
-
-/**
- * Get meta data by meta ID
- *
- * @since 3.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $meta_type Type of object metadata is for (e.g., comment, post, term, or user).
- * @param int $meta_id ID for a specific meta row
- * @return object|false Meta object or false.
- */
-function get_metadata_by_mid( $meta_type, $meta_id ) {
- global $wpdb;
-
- if ( ! $meta_type || ! is_numeric( $meta_id ) ) {
- return false;
- }
-
- $meta_id = absint( $meta_id );
- if ( ! $meta_id ) {
- return false;
- }
-
- $table = _get_meta_table( $meta_type );
- if ( ! $table ) {
- return false;
- }
-
- $id_column = ( 'user' == $meta_type ) ? 'umeta_id' : 'meta_id';
-
- $meta = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE $id_column = %d", $meta_id ) );
-
- if ( empty( $meta ) )
- return false;
-
- if ( isset( $meta->meta_value ) )
- $meta->meta_value = maybe_unserialize( $meta->meta_value );
-
- return $meta;
-}
-
-/**
- * Update meta data by meta ID
- *
- * @since 3.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
- * @param int $meta_id ID for a specific meta row
- * @param string $meta_value Metadata value
- * @param string $meta_key Optional, you can provide a meta key to update it
- * @return bool True on successful update, false on failure.
- */
-function update_metadata_by_mid( $meta_type, $meta_id, $meta_value, $meta_key = false ) {
- global $wpdb;
-
- // Make sure everything is valid.
- if ( ! $meta_type || ! is_numeric( $meta_id ) ) {
- return false;
- }
-
- $meta_id = absint( $meta_id );
- if ( ! $meta_id ) {
- return false;
- }
-
- $table = _get_meta_table( $meta_type );
- if ( ! $table ) {
- return false;
- }
-
- $column = sanitize_key($meta_type . '_id');
- $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
-
- // Fetch the meta and go on if it's found.
- if ( $meta = get_metadata_by_mid( $meta_type, $meta_id ) ) {
- $original_key = $meta->meta_key;
- $object_id = $meta->{$column};
-
- // If a new meta_key (last parameter) was specified, change the meta key,
- // otherwise use the original key in the update statement.
- if ( false === $meta_key ) {
- $meta_key = $original_key;
- } elseif ( ! is_string( $meta_key ) ) {
- return false;
- }
-
- // Sanitize the meta
- $_meta_value = $meta_value;
- $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
- $meta_value = maybe_serialize( $meta_value );
-
- // Format the data query arguments.
- $data = array(
- 'meta_key' => $meta_key,
- 'meta_value' => $meta_value
- );
-
- // Format the where query arguments.
- $where = array();
- $where[$id_column] = $meta_id;
-
- /** This action is documented in wp-includes/meta-functions.php */
- do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
-
- if ( 'post' == $meta_type ) {
- /** This action is documented in wp-includes/meta-functions.php */
- do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
- }
-
- // Run the update query, all fields in $data are %s, $where is a %d.
- $result = $wpdb->update( $table, $data, $where, '%s', '%d' );
- if ( ! $result )
- return false;
-
- // Clear the caches.
- wp_cache_delete($object_id, $meta_type . '_meta');
-
- /** This action is documented in wp-includes/meta-functions.php */
- do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
-
- if ( 'post' == $meta_type ) {
- /** This action is documented in wp-includes/meta-functions.php */
- do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
- }
-
- return true;
- }
-
- // And if the meta was not found.
- return false;
-}
-
-/**
- * Delete meta data by meta ID
- *
- * @since 3.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $meta_type Type of object metadata is for (e.g., comment, post, term, or user).
- * @param int $meta_id ID for a specific meta row
- * @return bool True on successful delete, false on failure.
- */
-function delete_metadata_by_mid( $meta_type, $meta_id ) {
- global $wpdb;
-
- // Make sure everything is valid.
- if ( ! $meta_type || ! is_numeric( $meta_id ) ) {
- return false;
- }
-
- $meta_id = absint( $meta_id );
- if ( ! $meta_id ) {
- return false;
- }
-
- $table = _get_meta_table( $meta_type );
- if ( ! $table ) {
- return false;
- }
-
- // object and id columns
- $column = sanitize_key($meta_type . '_id');
- $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
-
- // Fetch the meta and go on if it's found.
- if ( $meta = get_metadata_by_mid( $meta_type, $meta_id ) ) {
- $object_id = $meta->{$column};
-
- /** This action is documented in wp-includes/meta-functions.php */
- do_action( "delete_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
-
- // Old-style action.
- if ( 'post' == $meta_type || 'comment' == $meta_type ) {
- /**
- * Fires immediately before deleting post or comment metadata of a specific type.
- *
- * The dynamic portion of the hook, `$meta_type`, refers to the meta
- * object type (post or comment).
- *
- * @since 3.4.0
- *
- * @param int $meta_id ID of the metadata entry to delete.
- */
- do_action( "delete_{$meta_type}meta", $meta_id );
- }
-
- // Run the query, will return true if deleted, false otherwise
- $result = (bool) $wpdb->delete( $table, array( $id_column => $meta_id ) );
-
- // Clear the caches.
- wp_cache_delete($object_id, $meta_type . '_meta');
-
- /** This action is documented in wp-includes/meta-functions.php */
- do_action( "deleted_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
-
- // Old-style action.
- if ( 'post' == $meta_type || 'comment' == $meta_type ) {
- /**
- * Fires immediately after deleting post or comment metadata of a specific type.
- *
- * The dynamic portion of the hook, `$meta_type`, refers to the meta
- * object type (post or comment).
- *
- * @since 3.4.0
- *
- * @param int $meta_ids Deleted metadata entry ID.
- */
- do_action( "deleted_{$meta_type}meta", $meta_id );
- }
-
- return $result;
-
- }
-
- // Meta id was not found.
- return false;
-}
-
-/**
- * Update the metadata cache for the specified objects.
- *
- * @since 2.9.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
- * @param int|array $object_ids Array or comma delimited list of object IDs to update cache for
- * @return array|false Metadata cache for the specified objects, or false on failure.
- */
-function update_meta_cache($meta_type, $object_ids) {
- global $wpdb;
-
- if ( ! $meta_type || ! $object_ids ) {
- return false;
- }
-
- $table = _get_meta_table( $meta_type );
- if ( ! $table ) {
- return false;
- }
-
- $column = sanitize_key($meta_type . '_id');
-
- if ( !is_array($object_ids) ) {
- $object_ids = preg_replace('|[^0-9,]|', '', $object_ids);
- $object_ids = explode(',', $object_ids);
- }
-
- $object_ids = array_map('intval', $object_ids);
-
- $cache_key = $meta_type . '_meta';
- $ids = array();
- $cache = array();
- foreach ( $object_ids as $id ) {
- $cached_object = wp_cache_get( $id, $cache_key );
- if ( false === $cached_object )
- $ids[] = $id;
- else
- $cache[$id] = $cached_object;
- }
-
- if ( empty( $ids ) )
- return $cache;
-
- // Get meta info
- $id_list = join( ',', $ids );
- $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
- $meta_list = $wpdb->get_results( "SELECT $column, meta_key, meta_value FROM $table WHERE $column IN ($id_list) ORDER BY $id_column ASC", ARRAY_A );
-
- if ( !empty($meta_list) ) {
- foreach ( $meta_list as $metarow) {
- $mpid = intval($metarow[$column]);
- $mkey = $metarow['meta_key'];
- $mval = $metarow['meta_value'];
-
- // Force subkeys to be array type:
- if ( !isset($cache[$mpid]) || !is_array($cache[$mpid]) )
- $cache[$mpid] = array();
- if ( !isset($cache[$mpid][$mkey]) || !is_array($cache[$mpid][$mkey]) )
- $cache[$mpid][$mkey] = array();
-
- // Add a value to the current pid/key:
- $cache[$mpid][$mkey][] = $mval;
- }
- }
-
- foreach ( $ids as $id ) {
- if ( ! isset($cache[$id]) )
- $cache[$id] = array();
- wp_cache_add( $id, $cache[$id], $cache_key );
- }
-
- return $cache;
-}
-
-/**
- * Given a meta query, generates SQL clauses to be appended to a main query.
- *
- * @since 3.2.0
- *
- * @see WP_Meta_Query
- *
- * @param array $meta_query A meta query.
- * @param string $type Type of meta.
- * @param string $primary_table Primary database table name.
- * @param string $primary_id_column Primary ID column name.
- * @param object $context Optional. The main query object
- * @return array Associative array of `JOIN` and `WHERE` SQL.
- */
-function get_meta_sql( $meta_query, $type, $primary_table, $primary_id_column, $context = null ) {
- $meta_query_obj = new WP_Meta_Query( $meta_query );
- return $meta_query_obj->get_sql( $type, $primary_table, $primary_id_column, $context );
-}
-
-/**
- * Retrieve the name of the metadata table for the specified object type.
- *
- * @since 2.9.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $type Type of object to get metadata table for (e.g., comment, post, or user)
- * @return string|false Metadata table name, or false if no metadata table exists
- */
-function _get_meta_table($type) {
- global $wpdb;
-
- $table_name = $type . 'meta';
-
- if ( empty($wpdb->$table_name) )
- return false;
-
- return $wpdb->$table_name;
-}
-
-/**
- * Determine whether a meta key is protected.
- *
- * @since 3.1.3
- *
- * @param string $meta_key Meta key
- * @param string|null $meta_type
- * @return bool True if the key is protected, false otherwise.
- */
-function is_protected_meta( $meta_key, $meta_type = null ) {
- $protected = ( '_' == $meta_key[0] );
-
- /**
- * Filter whether a meta key is protected.
- *
- * @since 3.2.0
- *
- * @param bool $protected Whether the key is protected. Default false.
- * @param string $meta_key Meta key.
- * @param string $meta_type Meta type.
- */
- return apply_filters( 'is_protected_meta', $protected, $meta_key, $meta_type );
-}
-
-/**
- * Sanitize meta value.
- *
- * @since 3.1.3
- *
- * @param string $meta_key Meta key
- * @param mixed $meta_value Meta value to sanitize
- * @param string $meta_type Type of meta
- * @return mixed Sanitized $meta_value
- */
-function sanitize_meta( $meta_key, $meta_value, $meta_type ) {
-
- /**
- * Filter the sanitization of a specific meta key of a specific meta type.
- *
- * The dynamic portions of the hook name, `$meta_type`, and `$meta_key`,
- * refer to the metadata object type (comment, post, or user) and the meta
- * key value,
- * respectively.
- *
- * @since 3.3.0
- *
- * @param mixed $meta_value Meta value to sanitize.
- * @param string $meta_key Meta key.
- * @param string $meta_type Meta type.
- */
- return apply_filters( "sanitize_{$meta_type}_meta_{$meta_key}", $meta_value, $meta_key, $meta_type );
-}
-
-/**
- * Register meta key
- *
- * @since 3.3.0
- *
- * @param string $meta_type Type of meta
- * @param string $meta_key Meta key
- * @param string|array $sanitize_callback A function or method to call when sanitizing the value of $meta_key.
- * @param string|array $auth_callback Optional. A function or method to call when performing edit_post_meta, add_post_meta, and delete_post_meta capability checks.
- */
-function register_meta( $meta_type, $meta_key, $sanitize_callback, $auth_callback = null ) {
- if ( is_callable( $sanitize_callback ) )
- add_filter( "sanitize_{$meta_type}_meta_{$meta_key}", $sanitize_callback, 10, 3 );
-
- if ( empty( $auth_callback ) ) {
- if ( is_protected_meta( $meta_key, $meta_type ) )
- $auth_callback = '__return_false';
- else
- $auth_callback = '__return_true';
- }
-
- if ( is_callable( $auth_callback ) )
- add_filter( "auth_{$meta_type}_meta_{$meta_key}", $auth_callback, 10, 6 );
-}
</del></span></pre></div>
<a id="trunksrcwpincludesmetaphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/meta.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/meta.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/meta.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,18 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Core Metadata API
- *
- * Functions for retrieving and manipulating metadata of various WordPress object types. Metadata
- * for an object is a represented by a simple key-value pair. Objects may contain multiple
- * metadata entries that share the same key and differ only in their value.
- *
- * @package WordPress
- * @subpackage Meta
- * @since 2.9.0
- */
-
-/** Core metdata functionality */
-require_once( ABSPATH . WPINC . '/meta-functions.php' );
-
-/** WP_Meta_Query class */
-require_once( ABSPATH . WPINC . '/class-wp-meta-query.php' );
</del></span></pre></div>
<a id="trunksrcwpincludesmetaphpfromrev35712trunksrcwpincludesmetafunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/meta.php (from rev 35712, trunk/src/wp-includes/meta-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/meta.php (rev 0)
+++ trunk/src/wp-includes/meta.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,967 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Core Metadata API
+ *
+ * Functions for retrieving and manipulating metadata of various WordPress object types. Metadata
+ * for an object is a represented by a simple key-value pair. Objects may contain multiple
+ * metadata entries that share the same key and differ only in their value.
+ *
+ * @package WordPress
+ * @subpackage Meta
+ */
+
+/**
+ * Add metadata for the specified object.
+ *
+ * @since 2.9.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
+ * @param int $object_id ID of the object metadata is for
+ * @param string $meta_key Metadata key
+ * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
+ * @param bool $unique Optional, default is false.
+ * Whether the specified metadata key should be unique for the object.
+ * If true, and the object already has a value for the specified metadata key,
+ * no change will be made.
+ * @return int|false The meta ID on success, false on failure.
+ */
+function add_metadata($meta_type, $object_id, $meta_key, $meta_value, $unique = false) {
+ global $wpdb;
+
+ if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
+ return false;
+ }
+
+ $object_id = absint( $object_id );
+ if ( ! $object_id ) {
+ return false;
+ }
+
+ $table = _get_meta_table( $meta_type );
+ if ( ! $table ) {
+ return false;
+ }
+
+ $column = sanitize_key($meta_type . '_id');
+
+ // expected_slashed ($meta_key)
+ $meta_key = wp_unslash($meta_key);
+ $meta_value = wp_unslash($meta_value);
+ $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
+
+ /**
+ * Filter whether to add metadata of a specific type.
+ *
+ * The dynamic portion of the hook, `$meta_type`, refers to the meta
+ * object type (comment, post, or user). Returning a non-null value
+ * will effectively short-circuit the function.
+ *
+ * @since 3.1.0
+ *
+ * @param null|bool $check Whether to allow adding metadata for the given type.
+ * @param int $object_id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
+ * @param bool $unique Whether the specified meta key should be unique
+ * for the object. Optional. Default false.
+ */
+ $check = apply_filters( "add_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $unique );
+ if ( null !== $check )
+ return $check;
+
+ if ( $unique && $wpdb->get_var( $wpdb->prepare(
+ "SELECT COUNT(*) FROM $table WHERE meta_key = %s AND $column = %d",
+ $meta_key, $object_id ) ) )
+ return false;
+
+ $_meta_value = $meta_value;
+ $meta_value = maybe_serialize( $meta_value );
+
+ /**
+ * Fires immediately before meta of a specific type is added.
+ *
+ * The dynamic portion of the hook, `$meta_type`, refers to the meta
+ * object type (comment, post, or user).
+ *
+ * @since 3.1.0
+ *
+ * @param int $object_id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value.
+ */
+ do_action( "add_{$meta_type}_meta", $object_id, $meta_key, $_meta_value );
+
+ $result = $wpdb->insert( $table, array(
+ $column => $object_id,
+ 'meta_key' => $meta_key,
+ 'meta_value' => $meta_value
+ ) );
+
+ if ( ! $result )
+ return false;
+
+ $mid = (int) $wpdb->insert_id;
+
+ wp_cache_delete($object_id, $meta_type . '_meta');
+
+ /**
+ * Fires immediately after meta of a specific type is added.
+ *
+ * The dynamic portion of the hook, `$meta_type`, refers to the meta
+ * object type (comment, post, or user).
+ *
+ * @since 2.9.0
+ *
+ * @param int $mid The meta ID after successful update.
+ * @param int $object_id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value.
+ */
+ do_action( "added_{$meta_type}_meta", $mid, $object_id, $meta_key, $_meta_value );
+
+ return $mid;
+}
+
+/**
+ * Update metadata for the specified object. If no value already exists for the specified object
+ * ID and metadata key, the metadata will be added.
+ *
+ * @since 2.9.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
+ * @param int $object_id ID of the object metadata is for
+ * @param string $meta_key Metadata key
+ * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
+ * @param mixed $prev_value Optional. If specified, only update existing metadata entries with
+ * the specified value. Otherwise, update all entries.
+ * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
+ */
+function update_metadata($meta_type, $object_id, $meta_key, $meta_value, $prev_value = '') {
+ global $wpdb;
+
+ if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
+ return false;
+ }
+
+ $object_id = absint( $object_id );
+ if ( ! $object_id ) {
+ return false;
+ }
+
+ $table = _get_meta_table( $meta_type );
+ if ( ! $table ) {
+ return false;
+ }
+
+ $column = sanitize_key($meta_type . '_id');
+ $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
+
+ // expected_slashed ($meta_key)
+ $meta_key = wp_unslash($meta_key);
+ $passed_value = $meta_value;
+ $meta_value = wp_unslash($meta_value);
+ $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
+
+ /**
+ * Filter whether to update metadata of a specific type.
+ *
+ * The dynamic portion of the hook, `$meta_type`, refers to the meta
+ * object type (comment, post, or user). Returning a non-null value
+ * will effectively short-circuit the function.
+ *
+ * @since 3.1.0
+ *
+ * @param null|bool $check Whether to allow updating metadata for the given type.
+ * @param int $object_id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
+ * @param mixed $prev_value Optional. If specified, only update existing
+ * metadata entries with the specified value.
+ * Otherwise, update all entries.
+ */
+ $check = apply_filters( "update_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $prev_value );
+ if ( null !== $check )
+ return (bool) $check;
+
+ // Compare existing value to new value if no prev value given and the key exists only once.
+ if ( empty($prev_value) ) {
+ $old_value = get_metadata($meta_type, $object_id, $meta_key);
+ if ( count($old_value) == 1 ) {
+ if ( $old_value[0] === $meta_value )
+ return false;
+ }
+ }
+
+ $meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s AND $column = %d", $meta_key, $object_id ) );
+ if ( empty( $meta_ids ) ) {
+ return add_metadata($meta_type, $object_id, $meta_key, $passed_value);
+ }
+
+ $_meta_value = $meta_value;
+ $meta_value = maybe_serialize( $meta_value );
+
+ $data = compact( 'meta_value' );
+ $where = array( $column => $object_id, 'meta_key' => $meta_key );
+
+ if ( !empty( $prev_value ) ) {
+ $prev_value = maybe_serialize($prev_value);
+ $where['meta_value'] = $prev_value;
+ }
+
+ foreach ( $meta_ids as $meta_id ) {
+ /**
+ * Fires immediately before updating metadata of a specific type.
+ *
+ * The dynamic portion of the hook, `$meta_type`, refers to the meta
+ * object type (comment, post, or user).
+ *
+ * @since 2.9.0
+ *
+ * @param int $meta_id ID of the metadata entry to update.
+ * @param int $object_id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value.
+ */
+ do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
+ }
+
+ if ( 'post' == $meta_type ) {
+ foreach ( $meta_ids as $meta_id ) {
+ /**
+ * Fires immediately before updating a post's metadata.
+ *
+ * @since 2.9.0
+ *
+ * @param int $meta_id ID of metadata entry to update.
+ * @param int $object_id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value.
+ */
+ do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
+ }
+ }
+
+ $result = $wpdb->update( $table, $data, $where );
+ if ( ! $result )
+ return false;
+
+ wp_cache_delete($object_id, $meta_type . '_meta');
+
+ foreach ( $meta_ids as $meta_id ) {
+ /**
+ * Fires immediately after updating metadata of a specific type.
+ *
+ * The dynamic portion of the hook, `$meta_type`, refers to the meta
+ * object type (comment, post, or user).
+ *
+ * @since 2.9.0
+ *
+ * @param int $meta_id ID of updated metadata entry.
+ * @param int $object_id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value.
+ */
+ do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
+ }
+
+ if ( 'post' == $meta_type ) {
+ foreach ( $meta_ids as $meta_id ) {
+ /**
+ * Fires immediately after updating a post's metadata.
+ *
+ * @since 2.9.0
+ *
+ * @param int $meta_id ID of updated metadata entry.
+ * @param int $object_id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value.
+ */
+ do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Delete metadata for the specified object.
+ *
+ * @since 2.9.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
+ * @param int $object_id ID of the object metadata is for
+ * @param string $meta_key Metadata key
+ * @param mixed $meta_value Optional. Metadata value. Must be serializable if non-scalar. If specified, only delete
+ * metadata entries with this value. Otherwise, delete all entries with the specified meta_key.
+ * Pass `null, `false`, or an empty string to skip this check. (For backward compatibility,
+ * it is not possible to pass an empty string to delete those entries with an empty string
+ * for a value.)
+ * @param bool $delete_all Optional, default is false. If true, delete matching metadata entries for all objects,
+ * ignoring the specified object_id. Otherwise, only delete matching metadata entries for
+ * the specified object_id.
+ * @return bool True on successful delete, false on failure.
+ */
+function delete_metadata($meta_type, $object_id, $meta_key, $meta_value = '', $delete_all = false) {
+ global $wpdb;
+
+ if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) && ! $delete_all ) {
+ return false;
+ }
+
+ $object_id = absint( $object_id );
+ if ( ! $object_id && ! $delete_all ) {
+ return false;
+ }
+
+ $table = _get_meta_table( $meta_type );
+ if ( ! $table ) {
+ return false;
+ }
+
+ $type_column = sanitize_key($meta_type . '_id');
+ $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
+ // expected_slashed ($meta_key)
+ $meta_key = wp_unslash($meta_key);
+ $meta_value = wp_unslash($meta_value);
+
+ /**
+ * Filter whether to delete metadata of a specific type.
+ *
+ * The dynamic portion of the hook, `$meta_type`, refers to the meta
+ * object type (comment, post, or user). Returning a non-null value
+ * will effectively short-circuit the function.
+ *
+ * @since 3.1.0
+ *
+ * @param null|bool $delete Whether to allow metadata deletion of the given type.
+ * @param int $object_id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
+ * @param bool $delete_all Whether to delete the matching metadata entries
+ * for all objects, ignoring the specified $object_id.
+ * Default false.
+ */
+ $check = apply_filters( "delete_{$meta_type}_metadata", null, $object_id, $meta_key, $meta_value, $delete_all );
+ if ( null !== $check )
+ return (bool) $check;
+
+ $_meta_value = $meta_value;
+ $meta_value = maybe_serialize( $meta_value );
+
+ $query = $wpdb->prepare( "SELECT $id_column FROM $table WHERE meta_key = %s", $meta_key );
+
+ if ( !$delete_all )
+ $query .= $wpdb->prepare(" AND $type_column = %d", $object_id );
+
+ if ( '' !== $meta_value && null !== $meta_value && false !== $meta_value )
+ $query .= $wpdb->prepare(" AND meta_value = %s", $meta_value );
+
+ $meta_ids = $wpdb->get_col( $query );
+ if ( !count( $meta_ids ) )
+ return false;
+
+ if ( $delete_all )
+ $object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s", $meta_key ) );
+
+ /**
+ * Fires immediately before deleting metadata of a specific type.
+ *
+ * The dynamic portion of the hook, `$meta_type`, refers to the meta
+ * object type (comment, post, or user).
+ *
+ * @since 3.1.0
+ *
+ * @param array $meta_ids An array of metadata entry IDs to delete.
+ * @param int $object_id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value.
+ */
+ do_action( "delete_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
+
+ // Old-style action.
+ if ( 'post' == $meta_type ) {
+ /**
+ * Fires immediately before deleting metadata for a post.
+ *
+ * @since 2.9.0
+ *
+ * @param array $meta_ids An array of post metadata entry IDs to delete.
+ */
+ do_action( 'delete_postmeta', $meta_ids );
+ }
+
+ $query = "DELETE FROM $table WHERE $id_column IN( " . implode( ',', $meta_ids ) . " )";
+
+ $count = $wpdb->query($query);
+
+ if ( !$count )
+ return false;
+
+ if ( $delete_all ) {
+ foreach ( (array) $object_ids as $o_id ) {
+ wp_cache_delete($o_id, $meta_type . '_meta');
+ }
+ } else {
+ wp_cache_delete($object_id, $meta_type . '_meta');
+ }
+
+ /**
+ * Fires immediately after deleting metadata of a specific type.
+ *
+ * The dynamic portion of the hook name, `$meta_type`, refers to the meta
+ * object type (comment, post, or user).
+ *
+ * @since 2.9.0
+ *
+ * @param array $meta_ids An array of deleted metadata entry IDs.
+ * @param int $object_id Object ID.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value.
+ */
+ do_action( "deleted_{$meta_type}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
+
+ // Old-style action.
+ if ( 'post' == $meta_type ) {
+ /**
+ * Fires immediately after deleting metadata for a post.
+ *
+ * @since 2.9.0
+ *
+ * @param array $meta_ids An array of deleted post metadata entry IDs.
+ */
+ do_action( 'deleted_postmeta', $meta_ids );
+ }
+
+ return true;
+}
+
+/**
+ * Retrieve metadata for the specified object.
+ *
+ * @since 2.9.0
+ *
+ * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
+ * @param int $object_id ID of the object metadata is for
+ * @param string $meta_key Optional. Metadata key. If not specified, retrieve all metadata for
+ * the specified object.
+ * @param bool $single Optional, default is false.
+ * If true, return only the first value of the specified meta_key.
+ * This parameter has no effect if meta_key is not specified.
+ * @return mixed Single metadata value, or array of values
+ */
+function get_metadata($meta_type, $object_id, $meta_key = '', $single = false) {
+ if ( ! $meta_type || ! is_numeric( $object_id ) ) {
+ return false;
+ }
+
+ $object_id = absint( $object_id );
+ if ( ! $object_id ) {
+ return false;
+ }
+
+ /**
+ * Filter whether to retrieve metadata of a specific type.
+ *
+ * The dynamic portion of the hook, `$meta_type`, refers to the meta
+ * object type (comment, post, or user). Returning a non-null value
+ * will effectively short-circuit the function.
+ *
+ * @since 3.1.0
+ *
+ * @param null|array|string $value The value get_metadata() should return - a single metadata value,
+ * or an array of values.
+ * @param int $object_id Object ID.
+ * @param string $meta_key Meta key.
+ * @param bool $single Whether to return only the first value of the specified $meta_key.
+ */
+ $check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, $single );
+ if ( null !== $check ) {
+ if ( $single && is_array( $check ) )
+ return $check[0];
+ else
+ return $check;
+ }
+
+ $meta_cache = wp_cache_get($object_id, $meta_type . '_meta');
+
+ if ( !$meta_cache ) {
+ $meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
+ $meta_cache = $meta_cache[$object_id];
+ }
+
+ if ( ! $meta_key ) {
+ return $meta_cache;
+ }
+
+ if ( isset($meta_cache[$meta_key]) ) {
+ if ( $single )
+ return maybe_unserialize( $meta_cache[$meta_key][0] );
+ else
+ return array_map('maybe_unserialize', $meta_cache[$meta_key]);
+ }
+
+ if ($single)
+ return '';
+ else
+ return array();
+}
+
+/**
+ * Determine if a meta key is set for a given object
+ *
+ * @since 3.3.0
+ *
+ * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
+ * @param int $object_id ID of the object metadata is for
+ * @param string $meta_key Metadata key.
+ * @return bool True of the key is set, false if not.
+ */
+function metadata_exists( $meta_type, $object_id, $meta_key ) {
+ if ( ! $meta_type || ! is_numeric( $object_id ) ) {
+ return false;
+ }
+
+ $object_id = absint( $object_id );
+ if ( ! $object_id ) {
+ return false;
+ }
+
+ /** This filter is documented in wp-includes/meta-functions.php */
+ $check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, true );
+ if ( null !== $check )
+ return (bool) $check;
+
+ $meta_cache = wp_cache_get( $object_id, $meta_type . '_meta' );
+
+ if ( !$meta_cache ) {
+ $meta_cache = update_meta_cache( $meta_type, array( $object_id ) );
+ $meta_cache = $meta_cache[$object_id];
+ }
+
+ if ( isset( $meta_cache[ $meta_key ] ) )
+ return true;
+
+ return false;
+}
+
+/**
+ * Get meta data by meta ID
+ *
+ * @since 3.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $meta_type Type of object metadata is for (e.g., comment, post, term, or user).
+ * @param int $meta_id ID for a specific meta row
+ * @return object|false Meta object or false.
+ */
+function get_metadata_by_mid( $meta_type, $meta_id ) {
+ global $wpdb;
+
+ if ( ! $meta_type || ! is_numeric( $meta_id ) ) {
+ return false;
+ }
+
+ $meta_id = absint( $meta_id );
+ if ( ! $meta_id ) {
+ return false;
+ }
+
+ $table = _get_meta_table( $meta_type );
+ if ( ! $table ) {
+ return false;
+ }
+
+ $id_column = ( 'user' == $meta_type ) ? 'umeta_id' : 'meta_id';
+
+ $meta = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE $id_column = %d", $meta_id ) );
+
+ if ( empty( $meta ) )
+ return false;
+
+ if ( isset( $meta->meta_value ) )
+ $meta->meta_value = maybe_unserialize( $meta->meta_value );
+
+ return $meta;
+}
+
+/**
+ * Update meta data by meta ID
+ *
+ * @since 3.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
+ * @param int $meta_id ID for a specific meta row
+ * @param string $meta_value Metadata value
+ * @param string $meta_key Optional, you can provide a meta key to update it
+ * @return bool True on successful update, false on failure.
+ */
+function update_metadata_by_mid( $meta_type, $meta_id, $meta_value, $meta_key = false ) {
+ global $wpdb;
+
+ // Make sure everything is valid.
+ if ( ! $meta_type || ! is_numeric( $meta_id ) ) {
+ return false;
+ }
+
+ $meta_id = absint( $meta_id );
+ if ( ! $meta_id ) {
+ return false;
+ }
+
+ $table = _get_meta_table( $meta_type );
+ if ( ! $table ) {
+ return false;
+ }
+
+ $column = sanitize_key($meta_type . '_id');
+ $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
+
+ // Fetch the meta and go on if it's found.
+ if ( $meta = get_metadata_by_mid( $meta_type, $meta_id ) ) {
+ $original_key = $meta->meta_key;
+ $object_id = $meta->{$column};
+
+ // If a new meta_key (last parameter) was specified, change the meta key,
+ // otherwise use the original key in the update statement.
+ if ( false === $meta_key ) {
+ $meta_key = $original_key;
+ } elseif ( ! is_string( $meta_key ) ) {
+ return false;
+ }
+
+ // Sanitize the meta
+ $_meta_value = $meta_value;
+ $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
+ $meta_value = maybe_serialize( $meta_value );
+
+ // Format the data query arguments.
+ $data = array(
+ 'meta_key' => $meta_key,
+ 'meta_value' => $meta_value
+ );
+
+ // Format the where query arguments.
+ $where = array();
+ $where[$id_column] = $meta_id;
+
+ /** This action is documented in wp-includes/meta-functions.php */
+ do_action( "update_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
+
+ if ( 'post' == $meta_type ) {
+ /** This action is documented in wp-includes/meta-functions.php */
+ do_action( 'update_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
+ }
+
+ // Run the update query, all fields in $data are %s, $where is a %d.
+ $result = $wpdb->update( $table, $data, $where, '%s', '%d' );
+ if ( ! $result )
+ return false;
+
+ // Clear the caches.
+ wp_cache_delete($object_id, $meta_type . '_meta');
+
+ /** This action is documented in wp-includes/meta-functions.php */
+ do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
+
+ if ( 'post' == $meta_type ) {
+ /** This action is documented in wp-includes/meta-functions.php */
+ do_action( 'updated_postmeta', $meta_id, $object_id, $meta_key, $meta_value );
+ }
+
+ return true;
+ }
+
+ // And if the meta was not found.
+ return false;
+}
+
+/**
+ * Delete meta data by meta ID
+ *
+ * @since 3.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $meta_type Type of object metadata is for (e.g., comment, post, term, or user).
+ * @param int $meta_id ID for a specific meta row
+ * @return bool True on successful delete, false on failure.
+ */
+function delete_metadata_by_mid( $meta_type, $meta_id ) {
+ global $wpdb;
+
+ // Make sure everything is valid.
+ if ( ! $meta_type || ! is_numeric( $meta_id ) ) {
+ return false;
+ }
+
+ $meta_id = absint( $meta_id );
+ if ( ! $meta_id ) {
+ return false;
+ }
+
+ $table = _get_meta_table( $meta_type );
+ if ( ! $table ) {
+ return false;
+ }
+
+ // object and id columns
+ $column = sanitize_key($meta_type . '_id');
+ $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
+
+ // Fetch the meta and go on if it's found.
+ if ( $meta = get_metadata_by_mid( $meta_type, $meta_id ) ) {
+ $object_id = $meta->{$column};
+
+ /** This action is documented in wp-includes/meta-functions.php */
+ do_action( "delete_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
+
+ // Old-style action.
+ if ( 'post' == $meta_type || 'comment' == $meta_type ) {
+ /**
+ * Fires immediately before deleting post or comment metadata of a specific type.
+ *
+ * The dynamic portion of the hook, `$meta_type`, refers to the meta
+ * object type (post or comment).
+ *
+ * @since 3.4.0
+ *
+ * @param int $meta_id ID of the metadata entry to delete.
+ */
+ do_action( "delete_{$meta_type}meta", $meta_id );
+ }
+
+ // Run the query, will return true if deleted, false otherwise
+ $result = (bool) $wpdb->delete( $table, array( $id_column => $meta_id ) );
+
+ // Clear the caches.
+ wp_cache_delete($object_id, $meta_type . '_meta');
+
+ /** This action is documented in wp-includes/meta-functions.php */
+ do_action( "deleted_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
+
+ // Old-style action.
+ if ( 'post' == $meta_type || 'comment' == $meta_type ) {
+ /**
+ * Fires immediately after deleting post or comment metadata of a specific type.
+ *
+ * The dynamic portion of the hook, `$meta_type`, refers to the meta
+ * object type (post or comment).
+ *
+ * @since 3.4.0
+ *
+ * @param int $meta_ids Deleted metadata entry ID.
+ */
+ do_action( "deleted_{$meta_type}meta", $meta_id );
+ }
+
+ return $result;
+
+ }
+
+ // Meta id was not found.
+ return false;
+}
+
+/**
+ * Update the metadata cache for the specified objects.
+ *
+ * @since 2.9.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
+ * @param int|array $object_ids Array or comma delimited list of object IDs to update cache for
+ * @return array|false Metadata cache for the specified objects, or false on failure.
+ */
+function update_meta_cache($meta_type, $object_ids) {
+ global $wpdb;
+
+ if ( ! $meta_type || ! $object_ids ) {
+ return false;
+ }
+
+ $table = _get_meta_table( $meta_type );
+ if ( ! $table ) {
+ return false;
+ }
+
+ $column = sanitize_key($meta_type . '_id');
+
+ if ( !is_array($object_ids) ) {
+ $object_ids = preg_replace('|[^0-9,]|', '', $object_ids);
+ $object_ids = explode(',', $object_ids);
+ }
+
+ $object_ids = array_map('intval', $object_ids);
+
+ $cache_key = $meta_type . '_meta';
+ $ids = array();
+ $cache = array();
+ foreach ( $object_ids as $id ) {
+ $cached_object = wp_cache_get( $id, $cache_key );
+ if ( false === $cached_object )
+ $ids[] = $id;
+ else
+ $cache[$id] = $cached_object;
+ }
+
+ if ( empty( $ids ) )
+ return $cache;
+
+ // Get meta info
+ $id_list = join( ',', $ids );
+ $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
+ $meta_list = $wpdb->get_results( "SELECT $column, meta_key, meta_value FROM $table WHERE $column IN ($id_list) ORDER BY $id_column ASC", ARRAY_A );
+
+ if ( !empty($meta_list) ) {
+ foreach ( $meta_list as $metarow) {
+ $mpid = intval($metarow[$column]);
+ $mkey = $metarow['meta_key'];
+ $mval = $metarow['meta_value'];
+
+ // Force subkeys to be array type:
+ if ( !isset($cache[$mpid]) || !is_array($cache[$mpid]) )
+ $cache[$mpid] = array();
+ if ( !isset($cache[$mpid][$mkey]) || !is_array($cache[$mpid][$mkey]) )
+ $cache[$mpid][$mkey] = array();
+
+ // Add a value to the current pid/key:
+ $cache[$mpid][$mkey][] = $mval;
+ }
+ }
+
+ foreach ( $ids as $id ) {
+ if ( ! isset($cache[$id]) )
+ $cache[$id] = array();
+ wp_cache_add( $id, $cache[$id], $cache_key );
+ }
+
+ return $cache;
+}
+
+/**
+ * Given a meta query, generates SQL clauses to be appended to a main query.
+ *
+ * @since 3.2.0
+ *
+ * @see WP_Meta_Query
+ *
+ * @param array $meta_query A meta query.
+ * @param string $type Type of meta.
+ * @param string $primary_table Primary database table name.
+ * @param string $primary_id_column Primary ID column name.
+ * @param object $context Optional. The main query object
+ * @return array Associative array of `JOIN` and `WHERE` SQL.
+ */
+function get_meta_sql( $meta_query, $type, $primary_table, $primary_id_column, $context = null ) {
+ $meta_query_obj = new WP_Meta_Query( $meta_query );
+ return $meta_query_obj->get_sql( $type, $primary_table, $primary_id_column, $context );
+}
+
+/**
+ * Retrieve the name of the metadata table for the specified object type.
+ *
+ * @since 2.9.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $type Type of object to get metadata table for (e.g., comment, post, or user)
+ * @return string|false Metadata table name, or false if no metadata table exists
+ */
+function _get_meta_table($type) {
+ global $wpdb;
+
+ $table_name = $type . 'meta';
+
+ if ( empty($wpdb->$table_name) )
+ return false;
+
+ return $wpdb->$table_name;
+}
+
+/**
+ * Determine whether a meta key is protected.
+ *
+ * @since 3.1.3
+ *
+ * @param string $meta_key Meta key
+ * @param string|null $meta_type
+ * @return bool True if the key is protected, false otherwise.
+ */
+function is_protected_meta( $meta_key, $meta_type = null ) {
+ $protected = ( '_' == $meta_key[0] );
+
+ /**
+ * Filter whether a meta key is protected.
+ *
+ * @since 3.2.0
+ *
+ * @param bool $protected Whether the key is protected. Default false.
+ * @param string $meta_key Meta key.
+ * @param string $meta_type Meta type.
+ */
+ return apply_filters( 'is_protected_meta', $protected, $meta_key, $meta_type );
+}
+
+/**
+ * Sanitize meta value.
+ *
+ * @since 3.1.3
+ *
+ * @param string $meta_key Meta key
+ * @param mixed $meta_value Meta value to sanitize
+ * @param string $meta_type Type of meta
+ * @return mixed Sanitized $meta_value
+ */
+function sanitize_meta( $meta_key, $meta_value, $meta_type ) {
+
+ /**
+ * Filter the sanitization of a specific meta key of a specific meta type.
+ *
+ * The dynamic portions of the hook name, `$meta_type`, and `$meta_key`,
+ * refer to the metadata object type (comment, post, or user) and the meta
+ * key value,
+ * respectively.
+ *
+ * @since 3.3.0
+ *
+ * @param mixed $meta_value Meta value to sanitize.
+ * @param string $meta_key Meta key.
+ * @param string $meta_type Meta type.
+ */
+ return apply_filters( "sanitize_{$meta_type}_meta_{$meta_key}", $meta_value, $meta_key, $meta_type );
+}
+
+/**
+ * Register meta key
+ *
+ * @since 3.3.0
+ *
+ * @param string $meta_type Type of meta
+ * @param string $meta_key Meta key
+ * @param string|array $sanitize_callback A function or method to call when sanitizing the value of $meta_key.
+ * @param string|array $auth_callback Optional. A function or method to call when performing edit_post_meta, add_post_meta, and delete_post_meta capability checks.
+ */
+function register_meta( $meta_type, $meta_key, $sanitize_callback, $auth_callback = null ) {
+ if ( is_callable( $sanitize_callback ) )
+ add_filter( "sanitize_{$meta_type}_meta_{$meta_key}", $sanitize_callback, 10, 3 );
+
+ if ( empty( $auth_callback ) ) {
+ if ( is_protected_meta( $meta_key, $meta_type ) )
+ $auth_callback = '__return_false';
+ else
+ $auth_callback = '__return_true';
+ }
+
+ if ( is_callable( $auth_callback ) )
+ add_filter( "auth_{$meta_type}_meta_{$meta_key}", $auth_callback, 10, 6 );
+}
</ins></span></pre></div>
<a id="trunksrcwpincludespostfunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/post-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/post-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/post-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,5891 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Post API: Top-level post functionality
- *
- * @package WordPress
- * @subpackage Post
- * @since 4.4.0
- */
-
-//
-// Post Type Registration
-//
-
-/**
- * Creates the initial post types when 'init' action is fired.
- *
- * @since 2.9.0
- */
-function create_initial_post_types() {
- register_post_type( 'post', array(
- 'labels' => array(
- 'name_admin_bar' => _x( 'Post', 'add new on admin bar' ),
- ),
- 'public' => true,
- '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
- '_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */
- 'capability_type' => 'post',
- 'map_meta_cap' => true,
- 'menu_position' => 5,
- 'hierarchical' => false,
- 'rewrite' => false,
- 'query_var' => false,
- 'delete_with_user' => true,
- 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions', 'post-formats' ),
- ) );
-
- register_post_type( 'page', array(
- 'labels' => array(
- 'name_admin_bar' => _x( 'Page', 'add new on admin bar' ),
- ),
- 'public' => true,
- 'publicly_queryable' => false,
- '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
- '_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */
- 'capability_type' => 'page',
- 'map_meta_cap' => true,
- 'menu_position' => 20,
- 'hierarchical' => true,
- 'rewrite' => false,
- 'query_var' => false,
- 'delete_with_user' => true,
- 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'page-attributes', 'custom-fields', 'comments', 'revisions' ),
- ) );
-
- register_post_type( 'attachment', array(
- 'labels' => array(
- 'name' => _x('Media', 'post type general name'),
- 'name_admin_bar' => _x( 'Media', 'add new from admin bar' ),
- 'add_new' => _x( 'Add New', 'add new media' ),
- 'edit_item' => __( 'Edit Media' ),
- 'view_item' => __( 'View Attachment Page' ),
- ),
- 'public' => true,
- 'show_ui' => true,
- '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
- '_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */
- 'capability_type' => 'post',
- 'capabilities' => array(
- 'create_posts' => 'upload_files',
- ),
- 'map_meta_cap' => true,
- 'hierarchical' => false,
- 'rewrite' => false,
- 'query_var' => false,
- 'show_in_nav_menus' => false,
- 'delete_with_user' => true,
- 'supports' => array( 'title', 'author', 'comments' ),
- ) );
- add_post_type_support( 'attachment:audio', 'thumbnail' );
- add_post_type_support( 'attachment:video', 'thumbnail' );
-
- register_post_type( 'revision', array(
- 'labels' => array(
- 'name' => __( 'Revisions' ),
- 'singular_name' => __( 'Revision' ),
- ),
- 'public' => false,
- '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
- '_edit_link' => 'revision.php?revision=%d', /* internal use only. don't use this when registering your own post type. */
- 'capability_type' => 'post',
- 'map_meta_cap' => true,
- 'hierarchical' => false,
- 'rewrite' => false,
- 'query_var' => false,
- 'can_export' => false,
- 'delete_with_user' => true,
- 'supports' => array( 'author' ),
- ) );
-
- register_post_type( 'nav_menu_item', array(
- 'labels' => array(
- 'name' => __( 'Navigation Menu Items' ),
- 'singular_name' => __( 'Navigation Menu Item' ),
- ),
- 'public' => false,
- '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
- 'hierarchical' => false,
- 'rewrite' => false,
- 'delete_with_user' => false,
- 'query_var' => false,
- ) );
-
- register_post_status( 'publish', array(
- 'label' => _x( 'Published', 'post' ),
- 'public' => true,
- '_builtin' => true, /* internal use only. */
- 'label_count' => _n_noop( 'Published <span class="count">(%s)</span>', 'Published <span class="count">(%s)</span>' ),
- ) );
-
- register_post_status( 'future', array(
- 'label' => _x( 'Scheduled', 'post' ),
- 'protected' => true,
- '_builtin' => true, /* internal use only. */
- 'label_count' => _n_noop('Scheduled <span class="count">(%s)</span>', 'Scheduled <span class="count">(%s)</span>' ),
- ) );
-
- register_post_status( 'draft', array(
- 'label' => _x( 'Draft', 'post' ),
- 'protected' => true,
- '_builtin' => true, /* internal use only. */
- 'label_count' => _n_noop( 'Draft <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>' ),
- ) );
-
- register_post_status( 'pending', array(
- 'label' => _x( 'Pending', 'post' ),
- 'protected' => true,
- '_builtin' => true, /* internal use only. */
- 'label_count' => _n_noop( 'Pending <span class="count">(%s)</span>', 'Pending <span class="count">(%s)</span>' ),
- ) );
-
- register_post_status( 'private', array(
- 'label' => _x( 'Private', 'post' ),
- 'private' => true,
- '_builtin' => true, /* internal use only. */
- 'label_count' => _n_noop( 'Private <span class="count">(%s)</span>', 'Private <span class="count">(%s)</span>' ),
- ) );
-
- register_post_status( 'trash', array(
- 'label' => _x( 'Trash', 'post' ),
- 'internal' => true,
- '_builtin' => true, /* internal use only. */
- 'label_count' => _n_noop( 'Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>' ),
- 'show_in_admin_status_list' => true,
- ) );
-
- register_post_status( 'auto-draft', array(
- 'label' => 'auto-draft',
- 'internal' => true,
- '_builtin' => true, /* internal use only. */
- ) );
-
- register_post_status( 'inherit', array(
- 'label' => 'inherit',
- 'internal' => true,
- '_builtin' => true, /* internal use only. */
- 'exclude_from_search' => false,
- ) );
-}
-
-/**
- * Retrieve attached file path based on attachment ID.
- *
- * By default the path will go through the 'get_attached_file' filter, but
- * passing a true to the $unfiltered argument of get_attached_file() will
- * return the file path unfiltered.
- *
- * The function works by getting the single post meta name, named
- * '_wp_attached_file' and returning it. This is a convenience function to
- * prevent looking up the meta name and provide a mechanism for sending the
- * attached filename through a filter.
- *
- * @since 2.0.0
- *
- * @param int $attachment_id Attachment ID.
- * @param bool $unfiltered Optional. Whether to apply filters. Default false.
- * @return string|false The file path to where the attached file should be, false otherwise.
- */
-function get_attached_file( $attachment_id, $unfiltered = false ) {
- $file = get_post_meta( $attachment_id, '_wp_attached_file', true );
- // If the file is relative, prepend upload dir.
- if ( $file && 0 !== strpos($file, '/') && !preg_match('|^.:\\\|', $file) && ( ($uploads = wp_upload_dir()) && false === $uploads['error'] ) )
- $file = $uploads['basedir'] . "/$file";
- if ( $unfiltered )
- return $file;
-
- /**
- * Filter the attached file based on the given ID.
- *
- * @since 2.1.0
- *
- * @param string $file Path to attached file.
- * @param int $attachment_id Attachment ID.
- */
- return apply_filters( 'get_attached_file', $file, $attachment_id );
-}
-
-/**
- * Update attachment file path based on attachment ID.
- *
- * Used to update the file path of the attachment, which uses post meta name
- * '_wp_attached_file' to store the path of the attachment.
- *
- * @since 2.1.0
- *
- * @param int $attachment_id Attachment ID.
- * @param string $file File path for the attachment.
- * @return bool True on success, false on failure.
- */
-function update_attached_file( $attachment_id, $file ) {
- if ( !get_post( $attachment_id ) )
- return false;
-
- /**
- * Filter the path to the attached file to update.
- *
- * @since 2.1.0
- *
- * @param string $file Path to the attached file to update.
- * @param int $attachment_id Attachment ID.
- */
- $file = apply_filters( 'update_attached_file', $file, $attachment_id );
-
- if ( $file = _wp_relative_upload_path( $file ) )
- return update_post_meta( $attachment_id, '_wp_attached_file', $file );
- else
- return delete_post_meta( $attachment_id, '_wp_attached_file' );
-}
-
-/**
- * Return relative path to an uploaded file.
- *
- * The path is relative to the current upload dir.
- *
- * @since 2.9.0
- *
- * @param string $path Full path to the file.
- * @return string Relative path on success, unchanged path on failure.
- */
-function _wp_relative_upload_path( $path ) {
- $new_path = $path;
-
- $uploads = wp_upload_dir();
- if ( 0 === strpos( $new_path, $uploads['basedir'] ) ) {
- $new_path = str_replace( $uploads['basedir'], '', $new_path );
- $new_path = ltrim( $new_path, '/' );
- }
-
- /**
- * Filter the relative path to an uploaded file.
- *
- * @since 2.9.0
- *
- * @param string $new_path Relative path to the file.
- * @param string $path Full path to the file.
- */
- return apply_filters( '_wp_relative_upload_path', $new_path, $path );
-}
-
-/**
- * Retrieve all children of the post parent ID.
- *
- * Normally, without any enhancements, the children would apply to pages. In the
- * context of the inner workings of WordPress, pages, posts, and attachments
- * share the same table, so therefore the functionality could apply to any one
- * of them. It is then noted that while this function does not work on posts, it
- * does not mean that it won't work on posts. It is recommended that you know
- * what context you wish to retrieve the children of.
- *
- * Attachments may also be made the child of a post, so if that is an accurate
- * statement (which needs to be verified), it would then be possible to get
- * all of the attachments for a post. Attachments have since changed since
- * version 2.5, so this is most likely inaccurate, but serves generally as an
- * example of what is possible.
- *
- * The arguments listed as defaults are for this function and also of the
- * {@link get_posts()} function. The arguments are combined with the
- * get_children defaults and are then passed to the {@link get_posts()}
- * function, which accepts additional arguments. You can replace the defaults in
- * this function, listed below and the additional arguments listed in the
- * {@link get_posts()} function.
- *
- * The 'post_parent' is the most important argument and important attention
- * needs to be paid to the $args parameter. If you pass either an object or an
- * integer (number), then just the 'post_parent' is grabbed and everything else
- * is lost. If you don't specify any arguments, then it is assumed that you are
- * in The Loop and the post parent will be grabbed for from the current post.
- *
- * The 'post_parent' argument is the ID to get the children. The 'numberposts'
- * is the amount of posts to retrieve that has a default of '-1', which is
- * used to get all of the posts. Giving a number higher than 0 will only
- * retrieve that amount of posts.
- *
- * The 'post_type' and 'post_status' arguments can be used to choose what
- * criteria of posts to retrieve. The 'post_type' can be anything, but WordPress
- * post types are 'post', 'pages', and 'attachments'. The 'post_status'
- * argument will accept any post status within the write administration panels.
- *
- * @since 2.0.0
- *
- * @see get_posts()
- * @todo Check validity of description.
- *
- * @global WP_Post $post
- *
- * @param mixed $args Optional. User defined arguments for replacing the defaults. Default empty.
- * @param string $output Optional. Constant for return type. Accepts OBJECT, ARRAY_A, ARRAY_N.
- * Default OBJECT.
- * @return array Array of children, where the type of each element is determined by $output parameter.
- * Empty array on failure.
- */
-function get_children( $args = '', $output = OBJECT ) {
- $kids = array();
- if ( empty( $args ) ) {
- if ( isset( $GLOBALS['post'] ) ) {
- $args = array('post_parent' => (int) $GLOBALS['post']->post_parent );
- } else {
- return $kids;
- }
- } elseif ( is_object( $args ) ) {
- $args = array('post_parent' => (int) $args->post_parent );
- } elseif ( is_numeric( $args ) ) {
- $args = array('post_parent' => (int) $args);
- }
-
- $defaults = array(
- 'numberposts' => -1, 'post_type' => 'any',
- 'post_status' => 'any', 'post_parent' => 0,
- );
-
- $r = wp_parse_args( $args, $defaults );
-
- $children = get_posts( $r );
-
- if ( ! $children )
- return $kids;
-
- if ( ! empty( $r['fields'] ) )
- return $children;
-
- update_post_cache($children);
-
- foreach ( $children as $key => $child )
- $kids[$child->ID] = $children[$key];
-
- if ( $output == OBJECT ) {
- return $kids;
- } elseif ( $output == ARRAY_A ) {
- $weeuns = array();
- foreach ( (array) $kids as $kid ) {
- $weeuns[$kid->ID] = get_object_vars($kids[$kid->ID]);
- }
- return $weeuns;
- } elseif ( $output == ARRAY_N ) {
- $babes = array();
- foreach ( (array) $kids as $kid ) {
- $babes[$kid->ID] = array_values(get_object_vars($kids[$kid->ID]));
- }
- return $babes;
- } else {
- return $kids;
- }
-}
-
-/**
- * Get extended entry info (<!--more-->).
- *
- * There should not be any space after the second dash and before the word
- * 'more'. There can be text or space(s) after the word 'more', but won't be
- * referenced.
- *
- * The returned array has 'main', 'extended', and 'more_text' keys. Main has the text before
- * the `<!--more-->`. The 'extended' key has the content after the
- * `<!--more-->` comment. The 'more_text' key has the custom "Read More" text.
- *
- * @since 1.0.0
- *
- * @param string $post Post content.
- * @return array Post before ('main'), after ('extended'), and custom read more ('more_text').
- */
-function get_extended( $post ) {
- //Match the new style more links.
- if ( preg_match('/<!--more(.*?)?-->/', $post, $matches) ) {
- list($main, $extended) = explode($matches[0], $post, 2);
- $more_text = $matches[1];
- } else {
- $main = $post;
- $extended = '';
- $more_text = '';
- }
-
- // leading and trailing whitespace.
- $main = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $main);
- $extended = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $extended);
- $more_text = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $more_text);
-
- return array( 'main' => $main, 'extended' => $extended, 'more_text' => $more_text );
-}
-
-/**
- * Retrieves post data given a post ID or post object.
- *
- * See {@link sanitize_post()} for optional $filter values. Also, the parameter
- * $post, must be given as a variable, since it is passed by reference.
- *
- * @since 1.5.1
- *
- * @global WP_Post $post
- *
- * @param int|WP_Post|null $post Optional. Post ID or post object. Defaults to global $post.
- * @param string $output Optional, default is Object. Accepts OBJECT, ARRAY_A, or ARRAY_N.
- * Default OBJECT.
- * @param string $filter Optional. Type of filter to apply. Accepts 'raw', 'edit', 'db',
- * or 'display'. Default 'raw'.
- * @return WP_Post|array|null Type corresponding to $output on success or null on failure.
- * When $output is OBJECT, a `WP_Post` instance is returned.
- */
-function get_post( $post = null, $output = OBJECT, $filter = 'raw' ) {
- if ( empty( $post ) && isset( $GLOBALS['post'] ) )
- $post = $GLOBALS['post'];
-
- if ( $post instanceof WP_Post ) {
- $_post = $post;
- } elseif ( is_object( $post ) ) {
- if ( empty( $post->filter ) ) {
- $_post = sanitize_post( $post, 'raw' );
- $_post = new WP_Post( $_post );
- } elseif ( 'raw' == $post->filter ) {
- $_post = new WP_Post( $post );
- } else {
- $_post = WP_Post::get_instance( $post->ID );
- }
- } else {
- $_post = WP_Post::get_instance( $post );
- }
-
- if ( ! $_post )
- return null;
-
- $_post = $_post->filter( $filter );
-
- if ( $output == ARRAY_A )
- return $_post->to_array();
- elseif ( $output == ARRAY_N )
- return array_values( $_post->to_array() );
-
- return $_post;
-}
-
-/**
- * Retrieve ancestors of a post.
- *
- * @since 2.5.0
- *
- * @param int|WP_Post $post Post ID or post object.
- * @return array Ancestor IDs or empty array if none are found.
- */
-function get_post_ancestors( $post ) {
- $post = get_post( $post );
-
- if ( ! $post || empty( $post->post_parent ) || $post->post_parent == $post->ID )
- return array();
-
- $ancestors = array();
-
- $id = $ancestors[] = $post->post_parent;
-
- while ( $ancestor = get_post( $id ) ) {
- // Loop detection: If the ancestor has been seen before, break.
- if ( empty( $ancestor->post_parent ) || ( $ancestor->post_parent == $post->ID ) || in_array( $ancestor->post_parent, $ancestors ) )
- break;
-
- $id = $ancestors[] = $ancestor->post_parent;
- }
-
- return $ancestors;
-}
-
-/**
- * Retrieve data from a post field based on Post ID.
- *
- * Examples of the post field will be, 'post_type', 'post_status', 'post_content',
- * etc and based off of the post object property or key names.
- *
- * The context values are based off of the taxonomy filter functions and
- * supported values are found within those functions.
- *
- * @since 2.3.0
- *
- * @see sanitize_post_field()
- *
- * @param string $field Post field name.
- * @param int|WP_Post $post Post ID or post object.
- * @param string $context Optional. How to filter the field. Accepts 'raw', 'edit', 'db',
- * or 'display'. Default 'display'.
- * @return string The value of the post field on success, empty string on failure.
- */
-function get_post_field( $field, $post, $context = 'display' ) {
- $post = get_post( $post );
-
- if ( !$post )
- return '';
-
- if ( !isset($post->$field) )
- return '';
-
- return sanitize_post_field($field, $post->$field, $post->ID, $context);
-}
-
-/**
- * Retrieve the mime type of an attachment based on the ID.
- *
- * This function can be used with any post type, but it makes more sense with
- * attachments.
- *
- * @since 2.0.0
- *
- * @param int|WP_Post $ID Optional. Post ID or post object. Default empty.
- * @return string|false The mime type on success, false on failure.
- */
-function get_post_mime_type( $ID = '' ) {
- $post = get_post($ID);
-
- if ( is_object($post) )
- return $post->post_mime_type;
-
- return false;
-}
-
-/**
- * Retrieve the post status based on the Post ID.
- *
- * If the post ID is of an attachment, then the parent post status will be given
- * instead.
- *
- * @since 2.0.0
- *
- * @param int|WP_Post $ID Optional. Post ID or post object. Default empty.
- * @return string|false Post status on success, false on failure.
- */
-function get_post_status( $ID = '' ) {
- $post = get_post($ID);
-
- if ( !is_object($post) )
- return false;
-
- if ( 'attachment' == $post->post_type ) {
- if ( 'private' == $post->post_status )
- return 'private';
-
- // Unattached attachments are assumed to be published.
- if ( ( 'inherit' == $post->post_status ) && ( 0 == $post->post_parent) )
- return 'publish';
-
- // Inherit status from the parent.
- if ( $post->post_parent && ( $post->ID != $post->post_parent ) ) {
- $parent_post_status = get_post_status( $post->post_parent );
- if ( 'trash' == $parent_post_status ) {
- return get_post_meta( $post->post_parent, '_wp_trash_meta_status', true );
- } else {
- return $parent_post_status;
- }
- }
-
- }
-
- /**
- * Filter the post status.
- *
- * @since 4.4.0
- *
- * @param string $post_status The post status.
- * @param WP_Post $post The post object.
- */
- return apply_filters( 'get_post_status', $post->post_status, $post );
-}
-
-/**
- * Retrieve all of the WordPress supported post statuses.
- *
- * Posts have a limited set of valid status values, this provides the
- * post_status values and descriptions.
- *
- * @since 2.5.0
- *
- * @return array List of post statuses.
- */
-function get_post_statuses() {
- $status = array(
- 'draft' => __( 'Draft' ),
- 'pending' => __( 'Pending Review' ),
- 'private' => __( 'Private' ),
- 'publish' => __( 'Published' )
- );
-
- return $status;
-}
-
-/**
- * Retrieve all of the WordPress support page statuses.
- *
- * Pages have a limited set of valid status values, this provides the
- * post_status values and descriptions.
- *
- * @since 2.5.0
- *
- * @return array List of page statuses.
- */
-function get_page_statuses() {
- $status = array(
- 'draft' => __( 'Draft' ),
- 'private' => __( 'Private' ),
- 'publish' => __( 'Published' )
- );
-
- return $status;
-}
-
-/**
- * Register a post status. Do not use before init.
- *
- * A simple function for creating or modifying a post status based on the
- * parameters given. The function will accept an array (second optional
- * parameter), along with a string for the post status name.
- *
- * Arguments prefixed with an _underscore shouldn't be used by plugins and themes.
- *
- * @since 3.0.0
- * @global array $wp_post_statuses Inserts new post status object into the list
- *
- * @param string $post_status Name of the post status.
- * @param array|string $args {
- * Optional. Array or string of post status arguments.
- *
- * @type bool|string $label A descriptive name for the post status marked
- * for translation. Defaults to value of $post_status.
- * @type bool|array $label_count Descriptive text to use for nooped plurals.
- * Default array of $label, twice
- * @type bool $exclude_from_search Whether to exclude posts with this post status
- * from search results. Default is value of $internal.
- * @type bool $_builtin Whether the status is built-in. Core-use only.
- * Default false.
- * @type bool $public Whether posts of this status should be shown
- * in the front end of the site. Default false.
- * @type bool $internal Whether the status is for internal use only.
- * Default false.
- * @type bool $protected Whether posts with this status should be protected.
- * Default false.
- * @type bool $private Whether posts with this status should be private.
- * Default false.
- * @type bool $publicly_queryable Whether posts with this status should be publicly-
- * queryable. Default is value of $public.
- * @type bool $show_in_admin_all_list Whether to include posts in the edit listing for
- * their post type. Default is value of $internal.
- * @type bool $show_in_admin_status_list Show in the list of statuses with post counts at
- * the top of the edit listings,
- * e.g. All (12) | Published (9) | My Custom Status (2)
- * Default is value of $internal.
- * }
- * @return object
- */
-function register_post_status( $post_status, $args = array() ) {
- global $wp_post_statuses;
-
- if (!is_array($wp_post_statuses))
- $wp_post_statuses = array();
-
- // Args prefixed with an underscore are reserved for internal use.
- $defaults = array(
- 'label' => false,
- 'label_count' => false,
- 'exclude_from_search' => null,
- '_builtin' => false,
- 'public' => null,
- 'internal' => null,
- 'protected' => null,
- 'private' => null,
- 'publicly_queryable' => null,
- 'show_in_admin_status_list' => null,
- 'show_in_admin_all_list' => null,
- );
- $args = wp_parse_args($args, $defaults);
- $args = (object) $args;
-
- $post_status = sanitize_key($post_status);
- $args->name = $post_status;
-
- // Set various defaults.
- if ( null === $args->public && null === $args->internal && null === $args->protected && null === $args->private )
- $args->internal = true;
-
- if ( null === $args->public )
- $args->public = false;
-
- if ( null === $args->private )
- $args->private = false;
-
- if ( null === $args->protected )
- $args->protected = false;
-
- if ( null === $args->internal )
- $args->internal = false;
-
- if ( null === $args->publicly_queryable )
- $args->publicly_queryable = $args->public;
-
- if ( null === $args->exclude_from_search )
- $args->exclude_from_search = $args->internal;
-
- if ( null === $args->show_in_admin_all_list )
- $args->show_in_admin_all_list = !$args->internal;
-
- if ( null === $args->show_in_admin_status_list )
- $args->show_in_admin_status_list = !$args->internal;
-
- if ( false === $args->label )
- $args->label = $post_status;
-
- if ( false === $args->label_count )
- $args->label_count = array( $args->label, $args->label );
-
- $wp_post_statuses[$post_status] = $args;
-
- return $args;
-}
-
-/**
- * Retrieve a post status object by name.
- *
- * @since 3.0.0
- *
- * @global array $wp_post_statuses List of post statuses.
- *
- * @see register_post_status()
- *
- * @param string $post_status The name of a registered post status.
- * @return object|null A post status object.
- */
-function get_post_status_object( $post_status ) {
- global $wp_post_statuses;
-
- if ( empty($wp_post_statuses[$post_status]) )
- return null;
-
- return $wp_post_statuses[$post_status];
-}
-
-/**
- * Get a list of post statuses.
- *
- * @since 3.0.0
- *
- * @global array $wp_post_statuses List of post statuses.
- *
- * @see register_post_status()
- *
- * @param array|string $args Optional. Array or string of post status arguments to compare against
- * properties of the global `$wp_post_statuses objects`. Default empty array.
- * @param string $output Optional. The type of output to return, either 'names' or 'objects'. Default 'names'.
- * @param string $operator Optional. The logical operation to perform. 'or' means only one element
- * from the array needs to match; 'and' means all elements must match.
- * Default 'and'.
- * @return array A list of post status names or objects.
- */
-function get_post_stati( $args = array(), $output = 'names', $operator = 'and' ) {
- global $wp_post_statuses;
-
- $field = ('names' == $output) ? 'name' : false;
-
- return wp_filter_object_list($wp_post_statuses, $args, $operator, $field);
-}
-
-/**
- * Whether the post type is hierarchical.
- *
- * A false return value might also mean that the post type does not exist.
- *
- * @since 3.0.0
- *
- * @see get_post_type_object()
- *
- * @param string $post_type Post type name
- * @return bool Whether post type is hierarchical.
- */
-function is_post_type_hierarchical( $post_type ) {
- if ( ! post_type_exists( $post_type ) )
- return false;
-
- $post_type = get_post_type_object( $post_type );
- return $post_type->hierarchical;
-}
-
-/**
- * Check if a post type is registered.
- *
- * @since 3.0.0
- *
- * @see get_post_type_object()
- *
- * @param string $post_type Post type name.
- * @return bool Whether post type is registered.
- */
-function post_type_exists( $post_type ) {
- return (bool) get_post_type_object( $post_type );
-}
-
-/**
- * Retrieve the post type of the current post or of a given post.
- *
- * @since 2.1.0
- *
- * @param int|WP_Post|null $post Optional. Post ID or post object. Default is global $post.
- * @return string|false Post type on success, false on failure.
- */
-function get_post_type( $post = null ) {
- if ( $post = get_post( $post ) )
- return $post->post_type;
-
- return false;
-}
-
-/**
- * Retrieve a post type object by name.
- *
- * @since 3.0.0
- *
- * @global array $wp_post_types List of post types.
- *
- * @see register_post_type()
- *
- * @param string $post_type The name of a registered post type.
- * @return object|null A post type object.
- */
-function get_post_type_object( $post_type ) {
- global $wp_post_types;
-
- if ( ! is_scalar( $post_type ) || empty( $wp_post_types[ $post_type ] ) ) {
- return null;
- }
-
- return $wp_post_types[ $post_type ];
-}
-
-/**
- * Get a list of all registered post type objects.
- *
- * @since 2.9.0
- *
- * @global array $wp_post_types List of post types.
- *
- * @see register_post_type() for accepted arguments.
- *
- * @param array|string $args Optional. An array of key => value arguments to match against
- * the post type objects. Default empty array.
- * @param string $output Optional. The type of output to return. Accepts post type 'names'
- * or 'objects'. Default 'names'.
- * @param string $operator Optional. The logical operation to perform. 'or' means only one
- * element from the array needs to match; 'and' means all elements
- * must match. Accepts 'or' or 'and'. Default 'and'.
- * @return array A list of post type names or objects.
- */
-function get_post_types( $args = array(), $output = 'names', $operator = 'and' ) {
- global $wp_post_types;
-
- $field = ('names' == $output) ? 'name' : false;
-
- return wp_filter_object_list($wp_post_types, $args, $operator, $field);
-}
-
-/**
- * Register a post type. Do not use before init.
- *
- * A function for creating or modifying a post type based on the
- * parameters given. The function will accept an array (second optional
- * parameter), along with a string for the post type name.
- *
- * @since 2.9.0
- * @since 3.0.0 The `show_ui` argument is now enforced on the new post screen.
- * @since 4.4.0 The `show_ui` argument is now enforced on the post type listing screen and post editing screen.
- *
- * @global array $wp_post_types List of post types.
- * @global WP_Rewrite $wp_rewrite Used for default feeds.
- * @global WP $wp Used to add query vars.
- *
- * @param string $post_type Post type key, must not exceed 20 characters.
- * @param array|string $args {
- * Array or string of arguments for registering a post type.
- *
- * @type string $label Name of the post type shown in the menu. Usually plural.
- * Default is value of $labels['name'].
- * @type array $labels An array of labels for this post type. If not set, post
- * labels are inherited for non-hierarchical types and page
- * labels for hierarchical ones. {@see get_post_type_labels()}.
- * @type string $description A short descriptive summary of what the post type is.
- * Default empty.
- * @type bool $public Whether a post type is intended for use publicly either via
- * the admin interface or by front-end users. While the default
- * settings of $exclude_from_search, $publicly_queryable, $show_ui,
- * and $show_in_nav_menus are inherited from public, each does not
- * rely on this relationship and controls a very specific intention.
- * Default false.
- * @type bool $hierarchical Whether the post type is hierarchical (e.g. page). Default false.
- * @type bool $exclude_from_search Whether to exclude posts with this post type from front end search
- * results. Default is the opposite value of $public.
- * @type bool $publicly_queryable Whether queries can be performed on the front end for the post type
- * as part of {@see parse_request()}. Endpoints would include:
- * * ?post_type={post_type_key}
- * * ?{post_type_key}={single_post_slug}
- * * ?{post_type_query_var}={single_post_slug}
- * If not set, the default is inherited from $public.
- * @type bool $show_ui Whether to generate and allow a UI for managing this post type in the
- * admin. Default is value of $public.
- * @type bool $show_in_menu Where to show the post type in the admin menu. To work, $show_ui
- * must be true. If true, the post type is shown in its own top level
- * menu. If false, no menu is shown. If a string of an existing top
- * level menu (eg. 'tools.php' or 'edit.php?post_type=page'), the post
- * type will be placed as a sub-menu of that.
- * Default is value of $show_ui.
- * @type bool $show_in_nav_menus Makes this post type available for selection in navigation menus.
- * Default is value $public.
- * @type bool $show_in_admin_bar Makes this post type available via the admin bar. Default is value
- * of $show_in_menu.
- * @type int $menu_position The position in the menu order the post type should appear. To work,
- * $show_in_menu must be true. Default null (at the bottom).
- * @type string $menu_icon The url to the icon to be used for this menu. Pass a base64-encoded
- * SVG using a data URI, which will be colored to match the color scheme
- * -- this should begin with 'data:image/svg+xml;base64,'. Pass the name
- * of a Dashicons helper class to use a font icon, e.g.
- * 'dashicons-chart-pie'. Pass 'none' to leave div.wp-menu-image empty
- * so an icon can be added via CSS. Defaults to use the posts icon.
- * @type string $capability_type The string to use to build the read, edit, and delete capabilities.
- * May be passed as an array to allow for alternative plurals when using
- * this argument as a base to construct the capabilities, e.g.
- * array('story', 'stories'). Default 'post'.
- * @type array $capabilities Array of capabilities for this post type. $capability_type is used
- * as a base to construct capabilities by default.
- * {@see get_post_type_capabilities()}.
- * @type bool $map_meta_cap Whether to use the internal default meta capability handling.
- * Default false.
- * @type array $supports An alias for calling {@see add_post_type_support()} directly.
- * Defaults to array containing 'title' & 'editor'.
- * @type callable $register_meta_box_cb Provide a callback function that sets up the meta boxes for the
- * edit form. Do remove_meta_box() and add_meta_box() calls in the
- * callback. Default null.
- * @type array $taxonomies An array of taxonomy identifiers that will be registered for the
- * post type. Taxonomies can be registered later with
- * {@see register_taxonomy()} or {@see register_taxonomy_for_object_type()}.
- * Default empty array.
- * @type bool|string $has_archive Whether there should be post type archives, or if a string, the
- * archive slug to use. Will generate the proper rewrite rules if
- * $rewrite is enabled. Default false.
- * @type bool|array $rewrite {
- * Triggers the handling of rewrites for this post type. To prevent rewrite, set to false.
- * Defaults to true, using $post_type as slug. To specify rewrite rules, an array can be
- * passed with any of these keys:
- *
- * @type string $slug Customize the permastruct slug. Defaults to $post_type key.
- * @type bool $with_front Whether the permastruct should be prepended with WP_Rewrite::$front.
- * Default true.
- * @type bool $feeds Whether the feed permastruct should be built for this post type.
- * Default is value of $has_archive.
- * @type bool $pages Whether the permastruct should provide for pagination. Default true.
- * @type const $ep_mask Endpoint mask to assign. If not specified and permalink_epmask is set,
- * inherits from $permalink_epmask. If not specified and permalink_epmask
- * is not set, defaults to EP_PERMALINK.
- * }
- * @type string|bool $query_var Sets the query_var key for this post type. Defaults to $post_type
- * key. If false, a post type cannot be loaded at
- * ?{query_var}={post_slug}. If specified as a string, the query
- * ?{query_var_string}={post_slug} will be valid.
- * @type bool $can_export Whether to allow this post type to be exported. Default true.
- * @type bool $delete_with_user Whether to delete posts of this type when deleting a user. If true,
- * posts of this type belonging to the user will be moved to trash
- * when then user is deleted. If false, posts of this type belonging
- * to the user will *not* be trashed or deleted. If not set (the default),
- * posts are trashed if post_type_supports('author'). Otherwise posts
- * are not trashed or deleted. Default null.
- * @type bool $_builtin FOR INTERNAL USE ONLY! True if this post type is a native or
- * "built-in" post_type. Default false.
- * @type string $_edit_link FOR INTERNAL USE ONLY! URL segment to use for edit link of
- * this post type. Default 'post.php?post=%d'.
- * }
- * @return object|WP_Error The registered post type object, or an error object.
- */
-function register_post_type( $post_type, $args = array() ) {
- global $wp_post_types, $wp_rewrite, $wp;
-
- if ( ! is_array( $wp_post_types ) ) {
- $wp_post_types = array();
- }
-
- // Sanitize post type name
- $post_type = sanitize_key( $post_type );
- $args = wp_parse_args( $args );
-
- /**
- * Filter the arguments for registering a post type.
- *
- * @since 4.4.0
- *
- * @param array $args Array of arguments for registering a post type.
- * @param string $post_type Post type key.
- */
- $args = apply_filters( 'register_post_type_args', $args, $post_type );
-
- $has_edit_link = ! empty( $args['_edit_link'] );
-
- // Args prefixed with an underscore are reserved for internal use.
- $defaults = array(
- 'labels' => array(),
- 'description' => '',
- 'public' => false,
- 'hierarchical' => false,
- 'exclude_from_search' => null,
- 'publicly_queryable' => null,
- 'show_ui' => null,
- 'show_in_menu' => null,
- 'show_in_nav_menus' => null,
- 'show_in_admin_bar' => null,
- 'menu_position' => null,
- 'menu_icon' => null,
- 'capability_type' => 'post',
- 'capabilities' => array(),
- 'map_meta_cap' => null,
- 'supports' => array(),
- 'register_meta_box_cb' => null,
- 'taxonomies' => array(),
- 'has_archive' => false,
- 'rewrite' => true,
- 'query_var' => true,
- 'can_export' => true,
- 'delete_with_user' => null,
- '_builtin' => false,
- '_edit_link' => 'post.php?post=%d',
- );
- $args = array_merge( $defaults, $args );
- $args = (object) $args;
-
- $args->name = $post_type;
-
- if ( empty( $post_type ) || strlen( $post_type ) > 20 ) {
- _doing_it_wrong( __FUNCTION__, __( 'Post type names must be between 1 and 20 characters in length.' ), '4.2' );
- return new WP_Error( 'post_type_length_invalid', __( 'Post type names must be between 1 and 20 characters in length.' ) );
- }
-
- // If not set, default to the setting for public.
- if ( null === $args->publicly_queryable )
- $args->publicly_queryable = $args->public;
-
- // If not set, default to the setting for public.
- if ( null === $args->show_ui )
- $args->show_ui = $args->public;
-
- // If not set, default to the setting for show_ui.
- if ( null === $args->show_in_menu || ! $args->show_ui )
- $args->show_in_menu = $args->show_ui;
-
- // If not set, default to the whether the full UI is shown.
- if ( null === $args->show_in_admin_bar )
- $args->show_in_admin_bar = (bool) $args->show_in_menu;
-
- // If not set, default to the setting for public.
- if ( null === $args->show_in_nav_menus )
- $args->show_in_nav_menus = $args->public;
-
- // If not set, default to true if not public, false if public.
- if ( null === $args->exclude_from_search )
- $args->exclude_from_search = !$args->public;
-
- // Back compat with quirky handling in version 3.0. #14122.
- if ( empty( $args->capabilities ) && null === $args->map_meta_cap && in_array( $args->capability_type, array( 'post', 'page' ) ) )
- $args->map_meta_cap = true;
-
- // If not set, default to false.
- if ( null === $args->map_meta_cap )
- $args->map_meta_cap = false;
-
- // If there's no specified edit link and no UI, remove the edit link.
- if ( ! $args->show_ui && ! $has_edit_link ) {
- $args->_edit_link = '';
- }
-
- $args->cap = get_post_type_capabilities( $args );
- unset( $args->capabilities );
-
- if ( is_array( $args->capability_type ) )
- $args->capability_type = $args->capability_type[0];
-
- if ( ! empty( $args->supports ) ) {
- add_post_type_support( $post_type, $args->supports );
- unset( $args->supports );
- } elseif ( false !== $args->supports ) {
- // Add default features
- add_post_type_support( $post_type, array( 'title', 'editor' ) );
- }
-
- if ( false !== $args->query_var ) {
- if ( true === $args->query_var )
- $args->query_var = $post_type;
- else
- $args->query_var = sanitize_title_with_dashes( $args->query_var );
-
- if ( $wp && is_post_type_viewable( $args ) ) {
- $wp->add_query_var( $args->query_var );
- }
- }
-
- if ( false !== $args->rewrite && ( is_admin() || '' != get_option( 'permalink_structure' ) ) ) {
- if ( ! is_array( $args->rewrite ) )
- $args->rewrite = array();
- if ( empty( $args->rewrite['slug'] ) )
- $args->rewrite['slug'] = $post_type;
- if ( ! isset( $args->rewrite['with_front'] ) )
- $args->rewrite['with_front'] = true;
- if ( ! isset( $args->rewrite['pages'] ) )
- $args->rewrite['pages'] = true;
- if ( ! isset( $args->rewrite['feeds'] ) || ! $args->has_archive )
- $args->rewrite['feeds'] = (bool) $args->has_archive;
- if ( ! isset( $args->rewrite['ep_mask'] ) ) {
- if ( isset( $args->permalink_epmask ) )
- $args->rewrite['ep_mask'] = $args->permalink_epmask;
- else
- $args->rewrite['ep_mask'] = EP_PERMALINK;
- }
-
- if ( $args->hierarchical )
- add_rewrite_tag( "%$post_type%", '(.+?)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&pagename=" );
- else
- add_rewrite_tag( "%$post_type%", '([^/]+)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name=" );
-
- if ( $args->has_archive ) {
- $archive_slug = $args->has_archive === true ? $args->rewrite['slug'] : $args->has_archive;
- if ( $args->rewrite['with_front'] )
- $archive_slug = substr( $wp_rewrite->front, 1 ) . $archive_slug;
- else
- $archive_slug = $wp_rewrite->root . $archive_slug;
-
- add_rewrite_rule( "{$archive_slug}/?$", "index.php?post_type=$post_type", 'top' );
- if ( $args->rewrite['feeds'] && $wp_rewrite->feeds ) {
- $feeds = '(' . trim( implode( '|', $wp_rewrite->feeds ) ) . ')';
- add_rewrite_rule( "{$archive_slug}/feed/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
- add_rewrite_rule( "{$archive_slug}/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
- }
- if ( $args->rewrite['pages'] )
- add_rewrite_rule( "{$archive_slug}/{$wp_rewrite->pagination_base}/([0-9]{1,})/?$", "index.php?post_type=$post_type" . '&paged=$matches[1]', 'top' );
- }
-
- $permastruct_args = $args->rewrite;
- $permastruct_args['feed'] = $permastruct_args['feeds'];
- add_permastruct( $post_type, "{$args->rewrite['slug']}/%$post_type%", $permastruct_args );
- }
-
- // Register the post type meta box if a custom callback was specified.
- if ( $args->register_meta_box_cb )
- add_action( 'add_meta_boxes_' . $post_type, $args->register_meta_box_cb, 10, 1 );
-
- $args->labels = get_post_type_labels( $args );
- $args->label = $args->labels->name;
-
- $wp_post_types[ $post_type ] = $args;
-
- add_action( 'future_' . $post_type, '_future_post_hook', 5, 2 );
-
- foreach ( $args->taxonomies as $taxonomy ) {
- register_taxonomy_for_object_type( $taxonomy, $post_type );
- }
-
- /**
- * Fires after a post type is registered.
- *
- * @since 3.3.0
- *
- * @param string $post_type Post type.
- * @param object $args Arguments used to register the post type.
- */
- do_action( 'registered_post_type', $post_type, $args );
-
- return $args;
-}
-
-/**
- * Build an object with all post type capabilities out of a post type object
- *
- * Post type capabilities use the 'capability_type' argument as a base, if the
- * capability is not set in the 'capabilities' argument array or if the
- * 'capabilities' argument is not supplied.
- *
- * The capability_type argument can optionally be registered as an array, with
- * the first value being singular and the second plural, e.g. array('story, 'stories')
- * Otherwise, an 's' will be added to the value for the plural form. After
- * registration, capability_type will always be a string of the singular value.
- *
- * By default, seven keys are accepted as part of the capabilities array:
- *
- * - edit_post, read_post, and delete_post are meta capabilities, which are then
- * generally mapped to corresponding primitive capabilities depending on the
- * context, which would be the post being edited/read/deleted and the user or
- * role being checked. Thus these capabilities would generally not be granted
- * directly to users or roles.
- *
- * - edit_posts - Controls whether objects of this post type can be edited.
- * - edit_others_posts - Controls whether objects of this type owned by other users
- * can be edited. If the post type does not support an author, then this will
- * behave like edit_posts.
- * - publish_posts - Controls publishing objects of this post type.
- * - read_private_posts - Controls whether private objects can be read.
- *
- * These four primitive capabilities are checked in core in various locations.
- * There are also seven other primitive capabilities which are not referenced
- * directly in core, except in map_meta_cap(), which takes the three aforementioned
- * meta capabilities and translates them into one or more primitive capabilities
- * that must then be checked against the user or role, depending on the context.
- *
- * - read - Controls whether objects of this post type can be read.
- * - delete_posts - Controls whether objects of this post type can be deleted.
- * - delete_private_posts - Controls whether private objects can be deleted.
- * - delete_published_posts - Controls whether published objects can be deleted.
- * - delete_others_posts - Controls whether objects owned by other users can be
- * can be deleted. If the post type does not support an author, then this will
- * behave like delete_posts.
- * - edit_private_posts - Controls whether private objects can be edited.
- * - edit_published_posts - Controls whether published objects can be edited.
- *
- * These additional capabilities are only used in map_meta_cap(). Thus, they are
- * only assigned by default if the post type is registered with the 'map_meta_cap'
- * argument set to true (default is false).
- *
- * @since 3.0.0
- *
- * @see register_post_type()
- * @see map_meta_cap()
- *
- * @param object $args Post type registration arguments.
- * @return object object with all the capabilities as member variables.
- */
-function get_post_type_capabilities( $args ) {
- if ( ! is_array( $args->capability_type ) )
- $args->capability_type = array( $args->capability_type, $args->capability_type . 's' );
-
- // Singular base for meta capabilities, plural base for primitive capabilities.
- list( $singular_base, $plural_base ) = $args->capability_type;
-
- $default_capabilities = array(
- // Meta capabilities
- 'edit_post' => 'edit_' . $singular_base,
- 'read_post' => 'read_' . $singular_base,
- 'delete_post' => 'delete_' . $singular_base,
- // Primitive capabilities used outside of map_meta_cap():
- 'edit_posts' => 'edit_' . $plural_base,
- 'edit_others_posts' => 'edit_others_' . $plural_base,
- 'publish_posts' => 'publish_' . $plural_base,
- 'read_private_posts' => 'read_private_' . $plural_base,
- );
-
- // Primitive capabilities used within map_meta_cap():
- if ( $args->map_meta_cap ) {
- $default_capabilities_for_mapping = array(
- 'read' => 'read',
- 'delete_posts' => 'delete_' . $plural_base,
- 'delete_private_posts' => 'delete_private_' . $plural_base,
- 'delete_published_posts' => 'delete_published_' . $plural_base,
- 'delete_others_posts' => 'delete_others_' . $plural_base,
- 'edit_private_posts' => 'edit_private_' . $plural_base,
- 'edit_published_posts' => 'edit_published_' . $plural_base,
- );
- $default_capabilities = array_merge( $default_capabilities, $default_capabilities_for_mapping );
- }
-
- $capabilities = array_merge( $default_capabilities, $args->capabilities );
-
- // Post creation capability simply maps to edit_posts by default:
- if ( ! isset( $capabilities['create_posts'] ) )
- $capabilities['create_posts'] = $capabilities['edit_posts'];
-
- // Remember meta capabilities for future reference.
- if ( $args->map_meta_cap )
- _post_type_meta_capabilities( $capabilities );
-
- return (object) $capabilities;
-}
-
-/**
- * Store or return a list of post type meta caps for map_meta_cap().
- *
- * @since 3.1.0
- * @access private
- *
- * @staticvar array $meta_caps
- *
- * @param array|void $capabilities Post type meta capabilities.
- */
-function _post_type_meta_capabilities( $capabilities = null ) {
- static $meta_caps = array();
- if ( null === $capabilities )
- return $meta_caps;
- foreach ( $capabilities as $core => $custom ) {
- if ( in_array( $core, array( 'read_post', 'delete_post', 'edit_post' ) ) )
- $meta_caps[ $custom ] = $core;
- }
-}
-
-/**
- * Build an object with all post type labels out of a post type object
- *
- * Accepted keys of the label array in the post type object:
- *
- * - name - general name for the post type, usually plural. The same and overridden
- * by $post_type_object->label. Default is Posts/Pages
- * - singular_name - name for one object of this post type. Default is Post/Page
- * - add_new - Default is Add New for both hierarchical and non-hierarchical types.
- * When internationalizing this string, please use a gettext context
- * {@link https://codex.wordpress.org/I18n_for_WordPress_Developers#Disambiguation_by_context}
- * matching your post type. Example: `_x( 'Add New', 'product' );`.
- * - add_new_item - Default is Add New Post/Add New Page.
- * - edit_item - Default is Edit Post/Edit Page.
- * - new_item - Default is New Post/New Page.
- * - view_item - Default is View Post/View Page.
- * - search_items - Default is Search Posts/Search Pages.
- * - not_found - Default is No posts found/No pages found.
- * - not_found_in_trash - Default is No posts found in Trash/No pages found in Trash.
- * - parent_item_colon - This string isn't used on non-hierarchical types. In hierarchical
- * ones the default is 'Parent Page:'.
- * - all_items - String for the submenu. Default is All Posts/All Pages.
- * - archives - String for use with archives in nav menus. Default is Post Archives/Page Archives.
- * - insert_into_item - String for the media frame button. Default is Insert into post/Insert into page.
- * - uploaded_to_this_item - String for the media frame filter. Default is Uploaded to this post/Uploaded to this page.
- * - featured_image - Default is Featured Image.
- * - set_featured_image - Default is Set featured image.
- * - remove_featured_image - Default is Remove featured image.
- * - use_featured_image - Default is Use as featured image.
- * - menu_name - Default is the same as `name`.
- * - filter_items_list - String for the table views hidden heading.
- * - items_list_navigation - String for the table pagination hidden heading.
- * - items_list - String for the table hidden heading.
- *
- * Above, the first default value is for non-hierarchical post types (like posts)
- * and the second one is for hierarchical post types (like pages).
- *
- * @since 3.0.0
- * @since 4.3.0 Added the `featured_image`, `set_featured_image`, `remove_featured_image`,
- * and `use_featured_image` labels.
- * @since 4.4.0 Added the `insert_into_item`, `uploaded_to_this_item`, `filter_items_list`,
- * `items_list_navigation`, and `items_list` labels.
- *
- * @access private
- *
- * @param object $post_type_object Post type object.
- * @return object object with all the labels as member variables.
- */
-function get_post_type_labels( $post_type_object ) {
- $nohier_vs_hier_defaults = array(
- 'name' => array( _x('Posts', 'post type general name'), _x('Pages', 'post type general name') ),
- 'singular_name' => array( _x('Post', 'post type singular name'), _x('Page', 'post type singular name') ),
- 'add_new' => array( _x('Add New', 'post'), _x('Add New', 'page') ),
- 'add_new_item' => array( __('Add New Post'), __('Add New Page') ),
- 'edit_item' => array( __('Edit Post'), __('Edit Page') ),
- 'new_item' => array( __('New Post'), __('New Page') ),
- 'view_item' => array( __('View Post'), __('View Page') ),
- 'search_items' => array( __('Search Posts'), __('Search Pages') ),
- 'not_found' => array( __('No posts found.'), __('No pages found.') ),
- 'not_found_in_trash' => array( __('No posts found in Trash.'), __('No pages found in Trash.') ),
- 'parent_item_colon' => array( null, __('Parent Page:') ),
- 'all_items' => array( __( 'All Posts' ), __( 'All Pages' ) ),
- 'archives' => array( __( 'Post Archives' ), __( 'Page Archives' ) ),
- 'insert_into_item' => array( __( 'Insert into post' ), __( 'Insert into page' ) ),
- 'uploaded_to_this_item' => array( __( 'Uploaded to this post' ), __( 'Uploaded to this page' ) ),
- 'featured_image' => array( __( 'Featured Image' ), __( 'Featured Image' ) ),
- 'set_featured_image' => array( __( 'Set featured image' ), __( 'Set featured image' ) ),
- 'remove_featured_image' => array( __( 'Remove featured image' ), __( 'Remove featured image' ) ),
- 'use_featured_image' => array( __( 'Use as featured image' ), __( 'Use as featured image' ) ),
- 'filter_items_list' => array( __( 'Filter posts list' ), __( 'Filter pages list' ) ),
- 'items_list_navigation' => array( __( 'Posts list navigation' ), __( 'Pages list navigation' ) ),
- 'items_list' => array( __( 'Posts list' ), __( 'Pages list' ) ),
- );
- $nohier_vs_hier_defaults['menu_name'] = $nohier_vs_hier_defaults['name'];
-
- $labels = _get_custom_object_labels( $post_type_object, $nohier_vs_hier_defaults );
-
- $post_type = $post_type_object->name;
-
- $default_labels = clone $labels;
-
- /**
- * Filter the labels of a specific post type.
- *
- * The dynamic portion of the hook name, `$post_type`, refers to
- * the post type slug.
- *
- * @since 3.5.0
- *
- * @see get_post_type_labels() for the full list of labels.
- *
- * @param object $labels Object with labels for the post type as member variables.
- */
- $labels = apply_filters( "post_type_labels_{$post_type}", $labels );
-
- // Ensure that the filtered labels contain all required default values.
- $labels = (object) array_merge( (array) $default_labels, (array) $labels );
-
- return $labels;
-}
-
-/**
- * Build an object with custom-something object (post type, taxonomy) labels
- * out of a custom-something object
- *
- * @since 3.0.0
- * @access private
- *
- * @param object $object A custom-something object.
- * @param array $nohier_vs_hier_defaults Hierarchical vs non-hierarchical default labels.
- * @return object Object containing labels for the given custom-something object.
- */
-function _get_custom_object_labels( $object, $nohier_vs_hier_defaults ) {
- $object->labels = (array) $object->labels;
-
- if ( isset( $object->label ) && empty( $object->labels['name'] ) )
- $object->labels['name'] = $object->label;
-
- if ( !isset( $object->labels['singular_name'] ) && isset( $object->labels['name'] ) )
- $object->labels['singular_name'] = $object->labels['name'];
-
- if ( ! isset( $object->labels['name_admin_bar'] ) )
- $object->labels['name_admin_bar'] = isset( $object->labels['singular_name'] ) ? $object->labels['singular_name'] : $object->name;
-
- if ( !isset( $object->labels['menu_name'] ) && isset( $object->labels['name'] ) )
- $object->labels['menu_name'] = $object->labels['name'];
-
- if ( !isset( $object->labels['all_items'] ) && isset( $object->labels['menu_name'] ) )
- $object->labels['all_items'] = $object->labels['menu_name'];
-
- if ( !isset( $object->labels['archives'] ) && isset( $object->labels['all_items'] ) ) {
- $object->labels['archives'] = $object->labels['all_items'];
- }
-
- $defaults = array();
- foreach ( $nohier_vs_hier_defaults as $key => $value ) {
- $defaults[$key] = $object->hierarchical ? $value[1] : $value[0];
- }
- $labels = array_merge( $defaults, $object->labels );
- $object->labels = (object) $object->labels;
-
- return (object) $labels;
-}
-
-/**
- * Add submenus for post types.
- *
- * @access private
- * @since 3.1.0
- */
-function _add_post_type_submenus() {
- foreach ( get_post_types( array( 'show_ui' => true ) ) as $ptype ) {
- $ptype_obj = get_post_type_object( $ptype );
- // Sub-menus only.
- if ( ! $ptype_obj->show_in_menu || $ptype_obj->show_in_menu === true )
- continue;
- add_submenu_page( $ptype_obj->show_in_menu, $ptype_obj->labels->name, $ptype_obj->labels->all_items, $ptype_obj->cap->edit_posts, "edit.php?post_type=$ptype" );
- }
-}
-
-/**
- * Register support of certain features for a post type.
- *
- * All core features are directly associated with a functional area of the edit
- * screen, such as the editor or a meta box. Features include: 'title', 'editor',
- * 'comments', 'revisions', 'trackbacks', 'author', 'excerpt', 'page-attributes',
- * 'thumbnail', 'custom-fields', and 'post-formats'.
- *
- * Additionally, the 'revisions' feature dictates whether the post type will
- * store revisions, and the 'comments' feature dictates whether the comments
- * count will show on the edit screen.
- *
- * @since 3.0.0
- *
- * @global array $_wp_post_type_features
- *
- * @param string $post_type The post type for which to add the feature.
- * @param string|array $feature The feature being added, accepts an array of
- * feature strings or a single string.
- */
-function add_post_type_support( $post_type, $feature ) {
- global $_wp_post_type_features;
-
- $features = (array) $feature;
- foreach ($features as $feature) {
- if ( func_num_args() == 2 )
- $_wp_post_type_features[$post_type][$feature] = true;
- else
- $_wp_post_type_features[$post_type][$feature] = array_slice( func_get_args(), 2 );
- }
-}
-
-/**
- * Remove support for a feature from a post type.
- *
- * @since 3.0.0
- *
- * @global array $_wp_post_type_features
- *
- * @param string $post_type The post type for which to remove the feature.
- * @param string $feature The feature being removed.
- */
-function remove_post_type_support( $post_type, $feature ) {
- global $_wp_post_type_features;
-
- unset( $_wp_post_type_features[ $post_type ][ $feature ] );
-}
-
-/**
- * Get all the post type features
- *
- * @since 3.4.0
- *
- * @global array $_wp_post_type_features
- *
- * @param string $post_type The post type.
- * @return array Post type supports list.
- */
-function get_all_post_type_supports( $post_type ) {
- global $_wp_post_type_features;
-
- if ( isset( $_wp_post_type_features[$post_type] ) )
- return $_wp_post_type_features[$post_type];
-
- return array();
-}
-
-/**
- * Check a post type's support for a given feature.
- *
- * @since 3.0.0
- *
- * @global array $_wp_post_type_features
- *
- * @param string $post_type The post type being checked.
- * @param string $feature The feature being checked.
- * @return bool Whether the post type supports the given feature.
- */
-function post_type_supports( $post_type, $feature ) {
- global $_wp_post_type_features;
-
- return ( isset( $_wp_post_type_features[$post_type][$feature] ) );
-}
-
-/**
- * Update the post type for the post ID.
- *
- * The page or post cache will be cleaned for the post ID.
- *
- * @since 2.5.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $post_id Optional. Post ID to change post type. Default 0.
- * @param string $post_type Optional. Post type. Accepts 'post' or 'page' to
- * name a few. Default 'post'.
- * @return int|false Amount of rows changed. Should be 1 for success and 0 for failure.
- */
-function set_post_type( $post_id = 0, $post_type = 'post' ) {
- global $wpdb;
-
- $post_type = sanitize_post_field('post_type', $post_type, $post_id, 'db');
- $return = $wpdb->update( $wpdb->posts, array('post_type' => $post_type), array('ID' => $post_id) );
-
- clean_post_cache( $post_id );
-
- return $return;
-}
-
-/**
- * Determines whether a post type is considered "viewable".
- *
- * For built-in post types such as posts and pages, the 'public' value will be evaluated.
- * For all others, the 'publicly_queryable' value will be used.
- *
- * @since 4.4.0
- *
- * @param object $post_type_object Post type object.
- * @return bool Whether the post type should be considered viewable.
- */
-function is_post_type_viewable( $post_type_object ) {
- return $post_type_object->publicly_queryable || ( $post_type_object->_builtin && $post_type_object->public );
-}
-
-/**
- * Retrieve list of latest posts or posts matching criteria.
- *
- * The defaults are as follows:
- *
- * @since 1.2.0
- *
- * @see WP_Query::parse_query()
- *
- * @param array $args {
- * Optional. Arguments to retrieve posts. See WP_Query::parse_query() for all
- * available arguments.
- *
- * @type int $numberposts Total number of posts to retrieve. Is an alias of $posts_per_page
- * in WP_Query. Accepts -1 for all. Default 5.
- * @type int|string $category Category ID or comma-separated list of IDs (this or any children).
- * Is an alias of $cat in WP_Query. Default 0.
- * @type array $include An array of post IDs to retrieve, sticky posts will be included.
- * Is an alias of $post__in in WP_Query. Default empty array.
- * @type array $exclude An array of post IDs not to retrieve. Default empty array.
- * @type bool $suppress_filters Whether to suppress filters. Default true.
- * }
- * @return array List of posts.
- */
-function get_posts( $args = null ) {
- $defaults = array(
- 'numberposts' => 5,
- 'category' => 0, 'orderby' => 'date',
- 'order' => 'DESC', 'include' => array(),
- 'exclude' => array(), 'meta_key' => '',
- 'meta_value' =>'', 'post_type' => 'post',
- 'suppress_filters' => true
- );
-
- $r = wp_parse_args( $args, $defaults );
- if ( empty( $r['post_status'] ) )
- $r['post_status'] = ( 'attachment' == $r['post_type'] ) ? 'inherit' : 'publish';
- if ( ! empty($r['numberposts']) && empty($r['posts_per_page']) )
- $r['posts_per_page'] = $r['numberposts'];
- if ( ! empty($r['category']) )
- $r['cat'] = $r['category'];
- if ( ! empty($r['include']) ) {
- $incposts = wp_parse_id_list( $r['include'] );
- $r['posts_per_page'] = count($incposts); // only the number of posts included
- $r['post__in'] = $incposts;
- } elseif ( ! empty($r['exclude']) )
- $r['post__not_in'] = wp_parse_id_list( $r['exclude'] );
-
- $r['ignore_sticky_posts'] = true;
- $r['no_found_rows'] = true;
-
- $get_posts = new WP_Query;
- return $get_posts->query($r);
-
-}
-
-//
-// Post meta functions
-//
-
-/**
- * Add meta data field to a post.
- *
- * Post meta data is called "Custom Fields" on the Administration Screen.
- *
- * @since 1.5.0
- *
- * @param int $post_id Post ID.
- * @param string $meta_key Metadata name.
- * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
- * @param bool $unique Optional. Whether the same key should not be added.
- * Default false.
- * @return int|false Meta ID on success, false on failure.
- */
-function add_post_meta( $post_id, $meta_key, $meta_value, $unique = false ) {
- // Make sure meta is added to the post, not a revision.
- if ( $the_post = wp_is_post_revision($post_id) )
- $post_id = $the_post;
-
- return add_metadata('post', $post_id, $meta_key, $meta_value, $unique);
-}
-
-/**
- * Remove metadata matching criteria from a post.
- *
- * You can match based on the key, or key and value. Removing based on key and
- * value, will keep from removing duplicate metadata with the same key. It also
- * allows removing all metadata matching key, if needed.
- *
- * @since 1.5.0
- *
- * @param int $post_id Post ID.
- * @param string $meta_key Metadata name.
- * @param mixed $meta_value Optional. Metadata value. Must be serializable if
- * non-scalar. Default empty.
- * @return bool True on success, false on failure.
- */
-function delete_post_meta( $post_id, $meta_key, $meta_value = '' ) {
- // Make sure meta is added to the post, not a revision.
- if ( $the_post = wp_is_post_revision($post_id) )
- $post_id = $the_post;
-
- return delete_metadata('post', $post_id, $meta_key, $meta_value);
-}
-
-/**
- * Retrieve post meta field for a post.
- *
- * @since 1.5.0
- *
- * @param int $post_id Post ID.
- * @param string $key Optional. The meta key to retrieve. By default, returns
- * data for all keys. Default empty.
- * @param bool $single Optional. Whether to return a single value. Default false.
- * @return mixed Will be an array if $single is false. Will be value of meta data
- * field if $single is true.
- */
-function get_post_meta( $post_id, $key = '', $single = false ) {
- return get_metadata('post', $post_id, $key, $single);
-}
-
-/**
- * Update post meta field based on post ID.
- *
- * Use the $prev_value parameter to differentiate between meta fields with the
- * same key and post ID.
- *
- * If the meta field for the post does not exist, it will be added.
- *
- * @since 1.5.0
- *
- * @param int $post_id Post ID.
- * @param string $meta_key Metadata key.
- * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
- * @param mixed $prev_value Optional. Previous value to check before removing.
- * Default empty.
- * @return int|bool Meta ID if the key didn't exist, true on successful update,
- * false on failure.
- */
-function update_post_meta( $post_id, $meta_key, $meta_value, $prev_value = '' ) {
- // Make sure meta is added to the post, not a revision.
- if ( $the_post = wp_is_post_revision($post_id) )
- $post_id = $the_post;
-
- return update_metadata('post', $post_id, $meta_key, $meta_value, $prev_value);
-}
-
-/**
- * Delete everything from post meta matching meta key.
- *
- * @since 2.3.0
- *
- * @param string $post_meta_key Key to search for when deleting.
- * @return bool Whether the post meta key was deleted from the database.
- */
-function delete_post_meta_by_key( $post_meta_key ) {
- return delete_metadata( 'post', null, $post_meta_key, '', true );
-}
-
-/**
- * Retrieve post meta fields, based on post ID.
- *
- * The post meta fields are retrieved from the cache where possible,
- * so the function is optimized to be called more than once.
- *
- * @since 1.2.0
- *
- * @param int $post_id Optional. Post ID. Default is ID of the global $post.
- * @return array Post meta for the given post.
- */
-function get_post_custom( $post_id = 0 ) {
- $post_id = absint( $post_id );
- if ( ! $post_id )
- $post_id = get_the_ID();
-
- return get_post_meta( $post_id );
-}
-
-/**
- * Retrieve meta field names for a post.
- *
- * If there are no meta fields, then nothing (null) will be returned.
- *
- * @since 1.2.0
- *
- * @param int $post_id Optional. Post ID. Default is ID of the global $post.
- * @return array|void Array of the keys, if retrieved.
- */
-function get_post_custom_keys( $post_id = 0 ) {
- $custom = get_post_custom( $post_id );
-
- if ( !is_array($custom) )
- return;
-
- if ( $keys = array_keys($custom) )
- return $keys;
-}
-
-/**
- * Retrieve values for a custom post field.
- *
- * The parameters must not be considered optional. All of the post meta fields
- * will be retrieved and only the meta field key values returned.
- *
- * @since 1.2.0
- *
- * @param string $key Optional. Meta field key. Default empty.
- * @param int $post_id Optional. Post ID. Default is ID of the global $post.
- * @return array|null Meta field values.
- */
-function get_post_custom_values( $key = '', $post_id = 0 ) {
- if ( !$key )
- return null;
-
- $custom = get_post_custom($post_id);
-
- return isset($custom[$key]) ? $custom[$key] : null;
-}
-
-/**
- * Check if post is sticky.
- *
- * Sticky posts should remain at the top of The Loop. If the post ID is not
- * given, then The Loop ID for the current post will be used.
- *
- * @since 2.7.0
- *
- * @param int $post_id Optional. Post ID. Default is ID of the global $post.
- * @return bool Whether post is sticky.
- */
-function is_sticky( $post_id = 0 ) {
- $post_id = absint( $post_id );
-
- if ( ! $post_id )
- $post_id = get_the_ID();
-
- $stickies = get_option( 'sticky_posts' );
-
- if ( ! is_array( $stickies ) )
- return false;
-
- if ( in_array( $post_id, $stickies ) )
- return true;
-
- return false;
-}
-
-/**
- * Sanitize every post field.
- *
- * If the context is 'raw', then the post object or array will get minimal
- * sanitization of the integer fields.
- *
- * @since 2.3.0
- *
- * @see sanitize_post_field()
- *
- * @param object|WP_Post|array $post The Post Object or Array
- * @param string $context Optional. How to sanitize post fields.
- * Accepts 'raw', 'edit', 'db', or 'display'.
- * Default 'display'.
- * @return object|WP_Post|array The now sanitized Post Object or Array (will be the
- * same type as $post).
- */
-function sanitize_post( $post, $context = 'display' ) {
- if ( is_object($post) ) {
- // Check if post already filtered for this context.
- if ( isset($post->filter) && $context == $post->filter )
- return $post;
- if ( !isset($post->ID) )
- $post->ID = 0;
- foreach ( array_keys(get_object_vars($post)) as $field )
- $post->$field = sanitize_post_field($field, $post->$field, $post->ID, $context);
- $post->filter = $context;
- } elseif ( is_array( $post ) ) {
- // Check if post already filtered for this context.
- if ( isset($post['filter']) && $context == $post['filter'] )
- return $post;
- if ( !isset($post['ID']) )
- $post['ID'] = 0;
- foreach ( array_keys($post) as $field )
- $post[$field] = sanitize_post_field($field, $post[$field], $post['ID'], $context);
- $post['filter'] = $context;
- }
- return $post;
-}
-
-/**
- * Sanitize post field based on context.
- *
- * Possible context values are: 'raw', 'edit', 'db', 'display', 'attribute' and
- * 'js'. The 'display' context is used by default. 'attribute' and 'js' contexts
- * are treated like 'display' when calling filters.
- *
- * @since 2.3.0
- * @since 4.4.0 Like `sanitize_post()`, `$context` defaults to 'display'.
- *
- * @param string $field The Post Object field name.
- * @param mixed $value The Post Object value.
- * @param int $post_id Post ID.
- * @param string $context Optional. How to sanitize post fields. Looks for 'raw', 'edit',
- * 'db', 'display', 'attribute' and 'js'. Default 'display'.
- * @return mixed Sanitized value.
- */
-function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) {
- $int_fields = array('ID', 'post_parent', 'menu_order');
- if ( in_array($field, $int_fields) )
- $value = (int) $value;
-
- // Fields which contain arrays of integers.
- $array_int_fields = array( 'ancestors' );
- if ( in_array($field, $array_int_fields) ) {
- $value = array_map( 'absint', $value);
- return $value;
- }
-
- if ( 'raw' == $context )
- return $value;
-
- $prefixed = false;
- if ( false !== strpos($field, 'post_') ) {
- $prefixed = true;
- $field_no_prefix = str_replace('post_', '', $field);
- }
-
- if ( 'edit' == $context ) {
- $format_to_edit = array('post_content', 'post_excerpt', 'post_title', 'post_password');
-
- if ( $prefixed ) {
-
- /**
- * Filter the value of a specific post field to edit.
- *
- * The dynamic portion of the hook name, `$field`, refers to the post
- * field name.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the post field.
- * @param int $post_id Post ID.
- */
- $value = apply_filters( "edit_{$field}", $value, $post_id );
-
- /**
- * Filter the value of a specific post field to edit.
- *
- * The dynamic portion of the hook name, `$field_no_prefix`, refers to
- * the post field name.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the post field.
- * @param int $post_id Post ID.
- */
- $value = apply_filters( "{$field_no_prefix}_edit_pre", $value, $post_id );
- } else {
- $value = apply_filters( "edit_post_{$field}", $value, $post_id );
- }
-
- if ( in_array($field, $format_to_edit) ) {
- if ( 'post_content' == $field )
- $value = format_to_edit($value, user_can_richedit());
- else
- $value = format_to_edit($value);
- } else {
- $value = esc_attr($value);
- }
- } elseif ( 'db' == $context ) {
- if ( $prefixed ) {
-
- /**
- * Filter the value of a specific post field before saving.
- *
- * The dynamic portion of the hook name, `$field`, refers to the post
- * field name.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the post field.
- */
- $value = apply_filters( "pre_{$field}", $value );
-
- /**
- * Filter the value of a specific field before saving.
- *
- * The dynamic portion of the hook name, `$field_no_prefix`, refers
- * to the post field name.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the post field.
- */
- $value = apply_filters( "{$field_no_prefix}_save_pre", $value );
- } else {
- $value = apply_filters( "pre_post_{$field}", $value );
-
- /**
- * Filter the value of a specific post field before saving.
- *
- * The dynamic portion of the hook name, `$field`, refers to the post
- * field name.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the post field.
- */
- $value = apply_filters( "{$field}_pre", $value );
- }
- } else {
-
- // Use display filters by default.
- if ( $prefixed ) {
-
- /**
- * Filter the value of a specific post field for display.
- *
- * The dynamic portion of the hook name, `$field`, refers to the post
- * field name.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the prefixed post field.
- * @param int $post_id Post ID.
- * @param string $context Context for how to sanitize the field. Possible
- * values include 'raw', 'edit', 'db', 'display',
- * 'attribute' and 'js'.
- */
- $value = apply_filters( $field, $value, $post_id, $context );
- } else {
- $value = apply_filters( "post_{$field}", $value, $post_id, $context );
- }
- }
-
- if ( 'attribute' == $context )
- $value = esc_attr($value);
- elseif ( 'js' == $context )
- $value = esc_js($value);
-
- return $value;
-}
-
-/**
- * Make a post sticky.
- *
- * Sticky posts should be displayed at the top of the front page.
- *
- * @since 2.7.0
- *
- * @param int $post_id Post ID.
- */
-function stick_post( $post_id ) {
- $stickies = get_option('sticky_posts');
-
- if ( !is_array($stickies) )
- $stickies = array($post_id);
-
- if ( ! in_array($post_id, $stickies) )
- $stickies[] = $post_id;
-
- update_option('sticky_posts', $stickies);
-}
-
-/**
- * Un-stick a post.
- *
- * Sticky posts should be displayed at the top of the front page.
- *
- * @since 2.7.0
- *
- * @param int $post_id Post ID.
- */
-function unstick_post( $post_id ) {
- $stickies = get_option('sticky_posts');
-
- if ( !is_array($stickies) )
- return;
-
- if ( ! in_array($post_id, $stickies) )
- return;
-
- $offset = array_search($post_id, $stickies);
- if ( false === $offset )
- return;
-
- array_splice($stickies, $offset, 1);
-
- update_option('sticky_posts', $stickies);
-}
-
-/**
- * Return the cache key for wp_count_posts() based on the passed arguments.
- *
- * @since 3.9.0
- *
- * @param string $type Optional. Post type to retrieve count Default 'post'.
- * @param string $perm Optional. 'readable' or empty. Default empty.
- * @return string The cache key.
- */
-function _count_posts_cache_key( $type = 'post', $perm = '' ) {
- $cache_key = 'posts-' . $type;
- if ( 'readable' == $perm && is_user_logged_in() ) {
- $post_type_object = get_post_type_object( $type );
- if ( $post_type_object && ! current_user_can( $post_type_object->cap->read_private_posts ) ) {
- $cache_key .= '_' . $perm . '_' . get_current_user_id();
- }
- }
- return $cache_key;
-}
-
-/**
- * Count number of posts of a post type and if user has permissions to view.
- *
- * This function provides an efficient method of finding the amount of post's
- * type a blog has. Another method is to count the amount of items in
- * get_posts(), but that method has a lot of overhead with doing so. Therefore,
- * when developing for 2.5+, use this function instead.
- *
- * The $perm parameter checks for 'readable' value and if the user can read
- * private posts, it will display that for the user that is signed in.
- *
- * @since 2.5.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $type Optional. Post type to retrieve count. Default 'post'.
- * @param string $perm Optional. 'readable' or empty. Default empty.
- * @return object Number of posts for each status.
- */
-function wp_count_posts( $type = 'post', $perm = '' ) {
- global $wpdb;
-
- if ( ! post_type_exists( $type ) )
- return new stdClass;
-
- $cache_key = _count_posts_cache_key( $type, $perm );
-
- $counts = wp_cache_get( $cache_key, 'counts' );
- if ( false !== $counts ) {
- /** This filter is documented in wp-includes/post-functions.php */
- return apply_filters( 'wp_count_posts', $counts, $type, $perm );
- }
-
- $query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s";
- if ( 'readable' == $perm && is_user_logged_in() ) {
- $post_type_object = get_post_type_object($type);
- if ( ! current_user_can( $post_type_object->cap->read_private_posts ) ) {
- $query .= $wpdb->prepare( " AND (post_status != 'private' OR ( post_author = %d AND post_status = 'private' ))",
- get_current_user_id()
- );
- }
- }
- $query .= ' GROUP BY post_status';
-
- $results = (array) $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A );
- $counts = array_fill_keys( get_post_stati(), 0 );
-
- foreach ( $results as $row ) {
- $counts[ $row['post_status'] ] = $row['num_posts'];
- }
-
- $counts = (object) $counts;
- wp_cache_set( $cache_key, $counts, 'counts' );
-
- /**
- * Modify returned post counts by status for the current post type.
- *
- * @since 3.7.0
- *
- * @param object $counts An object containing the current post_type's post
- * counts by status.
- * @param string $type Post type.
- * @param string $perm The permission to determine if the posts are 'readable'
- * by the current user.
- */
- return apply_filters( 'wp_count_posts', $counts, $type, $perm );
-}
-
-/**
- * Count number of attachments for the mime type(s).
- *
- * If you set the optional mime_type parameter, then an array will still be
- * returned, but will only have the item you are looking for. It does not give
- * you the number of attachments that are children of a post. You can get that
- * by counting the number of children that post has.
- *
- * @since 2.5.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string|array $mime_type Optional. Array or comma-separated list of
- * MIME patterns. Default empty.
- * @return object An object containing the attachment counts by mime type.
- */
-function wp_count_attachments( $mime_type = '' ) {
- global $wpdb;
-
- $and = wp_post_mime_type_where( $mime_type );
- $count = $wpdb->get_results( "SELECT post_mime_type, COUNT( * ) AS num_posts FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status != 'trash' $and GROUP BY post_mime_type", ARRAY_A );
-
- $counts = array();
- foreach ( (array) $count as $row ) {
- $counts[ $row['post_mime_type'] ] = $row['num_posts'];
- }
- $counts['trash'] = $wpdb->get_var( "SELECT COUNT( * ) FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status = 'trash' $and");
-
- /**
- * Modify returned attachment counts by mime type.
- *
- * @since 3.7.0
- *
- * @param object $counts An object containing the attachment counts by
- * mime type.
- * @param string $mime_type The mime type pattern used to filter the attachments
- * counted.
- */
- return apply_filters( 'wp_count_attachments', (object) $counts, $mime_type );
-}
-
-/**
- * Get default post mime types.
- *
- * @since 2.9.0
- *
- * @return array List of post mime types.
- */
-function get_post_mime_types() {
- $post_mime_types = array( // array( adj, noun )
- 'image' => array(__('Images'), __('Manage Images'), _n_noop('Image <span class="count">(%s)</span>', 'Images <span class="count">(%s)</span>')),
- 'audio' => array(__('Audio'), __('Manage Audio'), _n_noop('Audio <span class="count">(%s)</span>', 'Audio <span class="count">(%s)</span>')),
- 'video' => array(__('Video'), __('Manage Video'), _n_noop('Video <span class="count">(%s)</span>', 'Video <span class="count">(%s)</span>')),
- );
-
- /**
- * Filter the default list of post mime types.
- *
- * @since 2.5.0
- *
- * @param array $post_mime_types Default list of post mime types.
- */
- return apply_filters( 'post_mime_types', $post_mime_types );
-}
-
-/**
- * Check a MIME-Type against a list.
- *
- * If the wildcard_mime_types parameter is a string, it must be comma separated
- * list. If the real_mime_types is a string, it is also comma separated to
- * create the list.
- *
- * @since 2.5.0
- *
- * @param string|array $wildcard_mime_types Mime types, e.g. audio/mpeg or image (same as image/*)
- * or flash (same as *flash*).
- * @param string|array $real_mime_types Real post mime type values.
- * @return array array(wildcard=>array(real types)).
- */
-function wp_match_mime_types( $wildcard_mime_types, $real_mime_types ) {
- $matches = array();
- if ( is_string( $wildcard_mime_types ) ) {
- $wildcard_mime_types = array_map( 'trim', explode( ',', $wildcard_mime_types ) );
- }
- if ( is_string( $real_mime_types ) ) {
- $real_mime_types = array_map( 'trim', explode( ',', $real_mime_types ) );
- }
-
- $patternses = array();
- $wild = '[-._a-z0-9]*';
-
- foreach ( (array) $wildcard_mime_types as $type ) {
- $mimes = array_map( 'trim', explode( ',', $type ) );
- foreach ( $mimes as $mime ) {
- $regex = str_replace( '__wildcard__', $wild, preg_quote( str_replace( '*', '__wildcard__', $mime ) ) );
- $patternses[][$type] = "^$regex$";
- if ( false === strpos( $mime, '/' ) ) {
- $patternses[][$type] = "^$regex/";
- $patternses[][$type] = $regex;
- }
- }
- }
- asort( $patternses );
-
- foreach ( $patternses as $patterns ) {
- foreach ( $patterns as $type => $pattern ) {
- foreach ( (array) $real_mime_types as $real ) {
- if ( preg_match( "#$pattern#", $real ) && ( empty( $matches[$type] ) || false === array_search( $real, $matches[$type] ) ) ) {
- $matches[$type][] = $real;
- }
- }
- }
- }
- return $matches;
-}
-
-/**
- * Convert MIME types into SQL.
- *
- * @since 2.5.0
- *
- * @param string|array $post_mime_types List of mime types or comma separated string
- * of mime types.
- * @param string $table_alias Optional. Specify a table alias, if needed.
- * Default empty.
- * @return string The SQL AND clause for mime searching.
- */
-function wp_post_mime_type_where( $post_mime_types, $table_alias = '' ) {
- $where = '';
- $wildcards = array('', '%', '%/%');
- if ( is_string($post_mime_types) )
- $post_mime_types = array_map('trim', explode(',', $post_mime_types));
-
- $wheres = array();
-
- foreach ( (array) $post_mime_types as $mime_type ) {
- $mime_type = preg_replace('/\s/', '', $mime_type);
- $slashpos = strpos($mime_type, '/');
- if ( false !== $slashpos ) {
- $mime_group = preg_replace('/[^-*.a-zA-Z0-9]/', '', substr($mime_type, 0, $slashpos));
- $mime_subgroup = preg_replace('/[^-*.+a-zA-Z0-9]/', '', substr($mime_type, $slashpos + 1));
- if ( empty($mime_subgroup) )
- $mime_subgroup = '*';
- else
- $mime_subgroup = str_replace('/', '', $mime_subgroup);
- $mime_pattern = "$mime_group/$mime_subgroup";
- } else {
- $mime_pattern = preg_replace('/[^-*.a-zA-Z0-9]/', '', $mime_type);
- if ( false === strpos($mime_pattern, '*') )
- $mime_pattern .= '/*';
- }
-
- $mime_pattern = preg_replace('/\*+/', '%', $mime_pattern);
-
- if ( in_array( $mime_type, $wildcards ) )
- return '';
-
- if ( false !== strpos($mime_pattern, '%') )
- $wheres[] = empty($table_alias) ? "post_mime_type LIKE '$mime_pattern'" : "$table_alias.post_mime_type LIKE '$mime_pattern'";
- else
- $wheres[] = empty($table_alias) ? "post_mime_type = '$mime_pattern'" : "$table_alias.post_mime_type = '$mime_pattern'";
- }
- if ( !empty($wheres) )
- $where = ' AND (' . join(' OR ', $wheres) . ') ';
- return $where;
-}
-
-/**
- * Trash or delete a post or page.
- *
- * When the post and page is permanently deleted, everything that is tied to
- * it is deleted also. This includes comments, post meta fields, and terms
- * associated with the post.
- *
- * The post or page is moved to trash instead of permanently deleted unless
- * trash is disabled, item is already in the trash, or $force_delete is true.
- *
- * @since 1.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- * @see wp_delete_attachment()
- * @see wp_trash_post()
- *
- * @param int $postid Optional. Post ID. Default 0.
- * @param bool $force_delete Optional. Whether to bypass trash and force deletion.
- * Default false.
- * @return array|false|WP_Post False on failure.
- */
-function wp_delete_post( $postid = 0, $force_delete = false ) {
- global $wpdb;
-
- if ( !$post = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d", $postid)) )
- return $post;
-
- if ( !$force_delete && ( $post->post_type == 'post' || $post->post_type == 'page') && get_post_status( $postid ) != 'trash' && EMPTY_TRASH_DAYS )
- return wp_trash_post( $postid );
-
- if ( $post->post_type == 'attachment' )
- return wp_delete_attachment( $postid, $force_delete );
-
- /**
- * Filter whether a post deletion should take place.
- *
- * @since 4.4.0
- *
- * @param bool $delete Whether to go forward with deletion.
- * @param WP_Post $post Post object.
- * @param bool $force_delete Whether to bypass the trash.
- */
- $check = apply_filters( 'pre_delete_post', null, $post, $force_delete );
- if ( null !== $check ) {
- return $check;
- }
-
- /**
- * Fires before a post is deleted, at the start of wp_delete_post().
- *
- * @since 3.2.0
- *
- * @see wp_delete_post()
- *
- * @param int $postid Post ID.
- */
- do_action( 'before_delete_post', $postid );
-
- delete_post_meta($postid,'_wp_trash_meta_status');
- delete_post_meta($postid,'_wp_trash_meta_time');
-
- wp_delete_object_term_relationships($postid, get_object_taxonomies($post->post_type));
-
- $parent_data = array( 'post_parent' => $post->post_parent );
- $parent_where = array( 'post_parent' => $postid );
-
- if ( is_post_type_hierarchical( $post->post_type ) ) {
- // Point children of this page to its parent, also clean the cache of affected children.
- $children_query = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_parent = %d AND post_type = %s", $postid, $post->post_type );
- $children = $wpdb->get_results( $children_query );
-
- $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => $post->post_type ) );
- }
-
- // Do raw query. wp_get_post_revisions() is filtered.
- $revision_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'revision'", $postid ) );
- // Use wp_delete_post (via wp_delete_post_revision) again. Ensures any meta/misplaced data gets cleaned up.
- foreach ( $revision_ids as $revision_id )
- wp_delete_post_revision( $revision_id );
-
- // Point all attachments to this post up one level.
- $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => 'attachment' ) );
-
- wp_defer_comment_counting( true );
-
- $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $postid ));
- foreach ( $comment_ids as $comment_id ) {
- wp_delete_comment( $comment_id, true );
- }
-
- wp_defer_comment_counting( false );
-
- $post_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d ", $postid ));
- foreach ( $post_meta_ids as $mid )
- delete_metadata_by_mid( 'post', $mid );
-
- /**
- * Fires immediately before a post is deleted from the database.
- *
- * @since 1.2.0
- *
- * @param int $postid Post ID.
- */
- do_action( 'delete_post', $postid );
- $result = $wpdb->delete( $wpdb->posts, array( 'ID' => $postid ) );
- if ( ! $result ) {
- return false;
- }
-
- /**
- * Fires immediately after a post is deleted from the database.
- *
- * @since 2.2.0
- *
- * @param int $postid Post ID.
- */
- do_action( 'deleted_post', $postid );
-
- clean_post_cache( $post );
-
- if ( is_post_type_hierarchical( $post->post_type ) && $children ) {
- foreach ( $children as $child )
- clean_post_cache( $child );
- }
-
- wp_clear_scheduled_hook('publish_future_post', array( $postid ) );
-
- /**
- * Fires after a post is deleted, at the conclusion of wp_delete_post().
- *
- * @since 3.2.0
- *
- * @see wp_delete_post()
- *
- * @param int $postid Post ID.
- */
- do_action( 'after_delete_post', $postid );
-
- return $post;
-}
-
-/**
- * Reset the page_on_front, show_on_front, and page_for_post settings when
- * a linked page is deleted or trashed.
- *
- * Also ensures the post is no longer sticky.
- *
- * @since 3.7.0
- * @access private
- *
- * @param int $post_id Post ID.
- */
-function _reset_front_page_settings_for_post( $post_id ) {
- $post = get_post( $post_id );
- if ( 'page' == $post->post_type ) {
- /*
- * If the page is defined in option page_on_front or post_for_posts,
- * adjust the corresponding options.
- */
- if ( get_option( 'page_on_front' ) == $post->ID ) {
- update_option( 'show_on_front', 'posts' );
- update_option( 'page_on_front', 0 );
- }
- if ( get_option( 'page_for_posts' ) == $post->ID ) {
- delete_option( 'page_for_posts', 0 );
- }
- }
- unstick_post( $post->ID );
-}
-
-/**
- * Move a post or page to the Trash
- *
- * If trash is disabled, the post or page is permanently deleted.
- *
- * @since 2.9.0
- *
- * @see wp_delete_post()
- *
- * @param int $post_id Optional. Post ID. Default is ID of the global $post
- * if EMPTY_TRASH_DAYS equals true.
- * @return false|array|WP_Post|null Post data array, otherwise false.
- */
-function wp_trash_post( $post_id = 0 ) {
- if ( !EMPTY_TRASH_DAYS )
- return wp_delete_post($post_id, true);
-
- if ( !$post = get_post($post_id, ARRAY_A) )
- return $post;
-
- if ( $post['post_status'] == 'trash' )
- return false;
-
- /**
- * Fires before a post is sent to the trash.
- *
- * @since 3.3.0
- *
- * @param int $post_id Post ID.
- */
- do_action( 'wp_trash_post', $post_id );
-
- add_post_meta($post_id,'_wp_trash_meta_status', $post['post_status']);
- add_post_meta($post_id,'_wp_trash_meta_time', time());
-
- $post['post_status'] = 'trash';
- wp_insert_post( wp_slash( $post ) );
-
- wp_trash_post_comments($post_id);
-
- /**
- * Fires after a post is sent to the trash.
- *
- * @since 2.9.0
- *
- * @param int $post_id Post ID.
- */
- do_action( 'trashed_post', $post_id );
-
- return $post;
-}
-
-/**
- * Restore a post or page from the Trash.
- *
- * @since 2.9.0
- *
- * @param int $post_id Optional. Post ID. Default is ID of the global $post.
- * @return WP_Post|false WP_Post object. False on failure.
- */
-function wp_untrash_post( $post_id = 0 ) {
- if ( !$post = get_post($post_id, ARRAY_A) )
- return $post;
-
- if ( $post['post_status'] != 'trash' )
- return false;
-
- /**
- * Fires before a post is restored from the trash.
- *
- * @since 2.9.0
- *
- * @param int $post_id Post ID.
- */
- do_action( 'untrash_post', $post_id );
-
- $post_status = get_post_meta($post_id, '_wp_trash_meta_status', true);
-
- $post['post_status'] = $post_status;
-
- delete_post_meta($post_id, '_wp_trash_meta_status');
- delete_post_meta($post_id, '_wp_trash_meta_time');
-
- wp_insert_post( wp_slash( $post ) );
-
- wp_untrash_post_comments($post_id);
-
- /**
- * Fires after a post is restored from the trash.
- *
- * @since 2.9.0
- *
- * @param int $post_id Post ID.
- */
- do_action( 'untrashed_post', $post_id );
-
- return $post;
-}
-
-/**
- * Moves comments for a post to the trash.
- *
- * @since 2.9.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int|WP_Post|null $post Optional. Post ID or post object. Defaults to global $post.
- * @return mixed|void False on failure.
- */
-function wp_trash_post_comments( $post = null ) {
- global $wpdb;
-
- $post = get_post($post);
- if ( empty($post) )
- return;
-
- $post_id = $post->ID;
-
- /**
- * Fires before comments are sent to the trash.
- *
- * @since 2.9.0
- *
- * @param int $post_id Post ID.
- */
- do_action( 'trash_post_comments', $post_id );
-
- $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_ID, comment_approved FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id) );
- if ( empty($comments) )
- return;
-
- // Cache current status for each comment.
- $statuses = array();
- foreach ( $comments as $comment )
- $statuses[$comment->comment_ID] = $comment->comment_approved;
- add_post_meta($post_id, '_wp_trash_meta_comments_status', $statuses);
-
- // Set status for all comments to post-trashed.
- $result = $wpdb->update($wpdb->comments, array('comment_approved' => 'post-trashed'), array('comment_post_ID' => $post_id));
-
- clean_comment_cache( array_keys($statuses) );
-
- /**
- * Fires after comments are sent to the trash.
- *
- * @since 2.9.0
- *
- * @param int $post_id Post ID.
- * @param array $statuses Array of comment statuses.
- */
- do_action( 'trashed_post_comments', $post_id, $statuses );
-
- return $result;
-}
-
-/**
- * Restore comments for a post from the trash.
- *
- * @since 2.9.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int|WP_Post|null $post Optional. Post ID or post object. Defaults to global $post.
- * @return true|void
- */
-function wp_untrash_post_comments( $post = null ) {
- global $wpdb;
-
- $post = get_post($post);
- if ( empty($post) )
- return;
-
- $post_id = $post->ID;
-
- $statuses = get_post_meta($post_id, '_wp_trash_meta_comments_status', true);
-
- if ( empty($statuses) )
- return true;
-
- /**
- * Fires before comments are restored for a post from the trash.
- *
- * @since 2.9.0
- *
- * @param int $post_id Post ID.
- */
- do_action( 'untrash_post_comments', $post_id );
-
- // Restore each comment to its original status.
- $group_by_status = array();
- foreach ( $statuses as $comment_id => $comment_status )
- $group_by_status[$comment_status][] = $comment_id;
-
- foreach ( $group_by_status as $status => $comments ) {
- // Sanity check. This shouldn't happen.
- if ( 'post-trashed' == $status ) {
- $status = '0';
- }
- $comments_in = implode( ', ', array_map( 'intval', $comments ) );
- $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->comments SET comment_approved = %s WHERE comment_ID IN ($comments_in)", $status ) );
- }
-
- clean_comment_cache( array_keys($statuses) );
-
- delete_post_meta($post_id, '_wp_trash_meta_comments_status');
-
- /**
- * Fires after comments are restored for a post from the trash.
- *
- * @since 2.9.0
- *
- * @param int $post_id Post ID.
- */
- do_action( 'untrashed_post_comments', $post_id );
-}
-
-/**
- * Retrieve the list of categories for a post.
- *
- * Compatibility layer for themes and plugins. Also an easy layer of abstraction
- * away from the complexity of the taxonomy layer.
- *
- * @since 2.1.0
- *
- * @see wp_get_object_terms()
- *
- * @param int $post_id Optional. The Post ID. Does not default to the ID of the
- * global $post. Default 0.
- * @param array $args Optional. Category arguments. Default empty.
- * @return array List of categories.
- */
-function wp_get_post_categories( $post_id = 0, $args = array() ) {
- $post_id = (int) $post_id;
-
- $defaults = array('fields' => 'ids');
- $args = wp_parse_args( $args, $defaults );
-
- $cats = wp_get_object_terms($post_id, 'category', $args);
- return $cats;
-}
-
-/**
- * Retrieve the tags for a post.
- *
- * There is only one default for this function, called 'fields' and by default
- * is set to 'all'. There are other defaults that can be overridden in
- * {@link wp_get_object_terms()}.
- *
- * @since 2.3.0
- *
- * @param int $post_id Optional. The Post ID. Does not default to the ID of the
- * global $post. Default 0.
- * @param array $args Optional. Overwrite the defaults
- * @return array List of post tags.
- */
-function wp_get_post_tags( $post_id = 0, $args = array() ) {
- return wp_get_post_terms( $post_id, 'post_tag', $args);
-}
-
-/**
- * Retrieve the terms for a post.
- *
- * There is only one default for this function, called 'fields' and by default
- * is set to 'all'. There are other defaults that can be overridden in
- * {@link wp_get_object_terms()}.
- *
- * @since 2.8.0
- *
- * @param int $post_id Optional. The Post ID. Does not default to the ID of the
- * global $post. Default 0.
- * @param string $taxonomy Optional. The taxonomy for which to retrieve terms. Default 'post_tag'.
- * @param array $args Optional. {@link wp_get_object_terms()} arguments. Default empty array.
- * @return array|WP_Error List of post terms or empty array if no terms were found. WP_Error object
- * if `$taxonomy` doesn't exist.
- */
-function wp_get_post_terms( $post_id = 0, $taxonomy = 'post_tag', $args = array() ) {
- $post_id = (int) $post_id;
-
- $defaults = array('fields' => 'all');
- $args = wp_parse_args( $args, $defaults );
-
- $tags = wp_get_object_terms($post_id, $taxonomy, $args);
-
- return $tags;
-}
-
-/**
- * Retrieve a number of recent posts.
- *
- * @since 1.0.0
- *
- * @see get_posts()
- *
- * @param array $args Optional. Arguments to retrieve posts. Default empty array.
- * @param string $output Optional. Type of output. Accepts ARRAY_A or ''. Default ARRAY_A.
- * @return array|false Associative array if $output equals ARRAY_A, array or false if no results.
- */
-function wp_get_recent_posts( $args = array(), $output = ARRAY_A ) {
-
- if ( is_numeric( $args ) ) {
- _deprecated_argument( __FUNCTION__, '3.1', __( 'Passing an integer number of posts is deprecated. Pass an array of arguments instead.' ) );
- $args = array( 'numberposts' => absint( $args ) );
- }
-
- // Set default arguments.
- $defaults = array(
- 'numberposts' => 10, 'offset' => 0,
- 'category' => 0, 'orderby' => 'post_date',
- 'order' => 'DESC', 'include' => '',
- 'exclude' => '', 'meta_key' => '',
- 'meta_value' =>'', 'post_type' => 'post', 'post_status' => 'draft, publish, future, pending, private',
- 'suppress_filters' => true
- );
-
- $r = wp_parse_args( $args, $defaults );
-
- $results = get_posts( $r );
-
- // Backward compatibility. Prior to 3.1 expected posts to be returned in array.
- if ( ARRAY_A == $output ){
- foreach ( $results as $key => $result ) {
- $results[$key] = get_object_vars( $result );
- }
- return $results ? $results : array();
- }
-
- return $results ? $results : false;
-
-}
-
-/**
- * Insert or update a post.
- *
- * If the $postarr parameter has 'ID' set to a value, then post will be updated.
- *
- * You can set the post date manually, by setting the values for 'post_date'
- * and 'post_date_gmt' keys. You can close the comments or open the comments by
- * setting the value for 'comment_status' key.
- *
- * @since 1.0.0
- * @since 4.2.0 Support was added for encoding emoji in the post title, content, and excerpt.
- * @since 4.4.0 A 'meta_input' array can now be passed to `$postarr` to add post meta data.
- *
- * @see sanitize_post()
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array $postarr {
- * An array of elements that make up a post to update or insert.
- *
- * @type int $ID The post ID. If equal to something other than 0,
- * the post with that ID will be updated. Default 0.
- * @type int $post_author The ID of the user who added the post. Default is
- * the current user ID.
- * @type string $post_date The date of the post. Default is the current time.
- * @type string $post_date_gmt The date of the post in the GMT timezone. Default is
- * the value of `$post_date`.
- * @type mixed $post_content The post content. Default empty.
- * @type string $post_content_filtered The filtered post content. Default empty.
- * @type string $post_title The post title. Default empty.
- * @type string $post_excerpt The post excerpt. Default empty.
- * @type string $post_status The post status. Default 'draft'.
- * @type string $post_type The post type. Default 'post'.
- * @type string $comment_status Whether the post can accept comments. Accepts 'open' or 'closed'.
- * Default is the value of 'default_comment_status' option.
- * @type string $ping_status Whether the post can accept pings. Accepts 'open' or 'closed'.
- * Default is the value of 'default_ping_status' option.
- * @type string $post_password The password to access the post. Default empty.
- * @type string $post_name The post name. Default is the sanitized post title.
- * @type string $to_ping Space or carriage return-separated list of URLs to ping.
- * Default empty.
- * @type string $pinged Space or carriage return-separated list of URLs that have
- * been pinged. Default empty.
- * @type string $post_modified The date when the post was last modified. Default is
- * the current time.
- * @type string $post_modified_gmt The date when the post was last modified in the GMT
- * timezone. Default is the current time.
- * @type int $post_parent Set this for the post it belongs to, if any. Default 0.
- * @type int $menu_order The order the post should be displayed in. Default 0.
- * @type string $post_mime_type The mime type of the post. Default empty.
- * @type string $guid Global Unique ID for referencing the post. Default empty.
- * @type array $tax_input Array of taxonomy terms keyed by their taxonomy name. Default empty.
- * @type array $meta_input Array of post meta values keyed by their post meta key. Default empty.
- * }
- * @param bool $wp_error Optional. Whether to allow return of WP_Error on failure. Default false.
- * @return int|WP_Error The post ID on success. The value 0 or WP_Error on failure.
- */
-function wp_insert_post( $postarr, $wp_error = false ) {
- global $wpdb;
-
- $user_id = get_current_user_id();
-
- $defaults = array(
- 'post_author' => $user_id,
- 'post_content' => '',
- 'post_content_filtered' => '',
- 'post_title' => '',
- 'post_excerpt' => '',
- 'post_status' => 'draft',
- 'post_type' => 'post',
- 'comment_status' => '',
- 'ping_status' => '',
- 'post_password' => '',
- 'to_ping' => '',
- 'pinged' => '',
- 'post_parent' => 0,
- 'menu_order' => 0,
- 'guid' => '',
- 'import_id' => 0,
- 'context' => '',
- );
-
- $postarr = wp_parse_args($postarr, $defaults);
-
- unset( $postarr[ 'filter' ] );
-
- $postarr = sanitize_post($postarr, 'db');
-
- // Are we updating or creating?
- $post_ID = 0;
- $update = false;
- $guid = $postarr['guid'];
-
- if ( ! empty( $postarr['ID'] ) ) {
- $update = true;
-
- // Get the post ID and GUID.
- $post_ID = $postarr['ID'];
- $post_before = get_post( $post_ID );
- if ( is_null( $post_before ) ) {
- if ( $wp_error ) {
- return new WP_Error( 'invalid_post', __( 'Invalid post ID.' ) );
- }
- return 0;
- }
-
- $guid = get_post_field( 'guid', $post_ID );
- $previous_status = get_post_field('post_status', $post_ID );
- } else {
- $previous_status = 'new';
- }
-
- $post_type = empty( $postarr['post_type'] ) ? 'post' : $postarr['post_type'];
-
- $post_title = $postarr['post_title'];
- $post_content = $postarr['post_content'];
- $post_excerpt = $postarr['post_excerpt'];
- if ( isset( $postarr['post_name'] ) ) {
- $post_name = $postarr['post_name'];
- }
-
- $maybe_empty = 'attachment' !== $post_type
- && ! $post_content && ! $post_title && ! $post_excerpt
- && post_type_supports( $post_type, 'editor' )
- && post_type_supports( $post_type, 'title' )
- && post_type_supports( $post_type, 'excerpt' );
-
- /**
- * Filter whether the post should be considered "empty".
- *
- * The post is considered "empty" if both:
- * 1. The post type supports the title, editor, and excerpt fields
- * 2. The title, editor, and excerpt fields are all empty
- *
- * Returning a truthy value to the filter will effectively short-circuit
- * the new post being inserted, returning 0. If $wp_error is true, a WP_Error
- * will be returned instead.
- *
- * @since 3.3.0
- *
- * @param bool $maybe_empty Whether the post should be considered "empty".
- * @param array $postarr Array of post data.
- */
- if ( apply_filters( 'wp_insert_post_empty_content', $maybe_empty, $postarr ) ) {
- if ( $wp_error ) {
- return new WP_Error( 'empty_content', __( 'Content, title, and excerpt are empty.' ) );
- } else {
- return 0;
- }
- }
-
- $post_status = empty( $postarr['post_status'] ) ? 'draft' : $postarr['post_status'];
- if ( 'attachment' === $post_type && ! in_array( $post_status, array( 'inherit', 'private', 'trash' ) ) ) {
- $post_status = 'inherit';
- }
-
- if ( ! empty( $postarr['post_category'] ) ) {
- // Filter out empty terms.
- $post_category = array_filter( $postarr['post_category'] );
- }
-
- // Make sure we set a valid category.
- if ( empty( $post_category ) || 0 == count( $post_category ) || ! is_array( $post_category ) ) {
- // 'post' requires at least one category.
- if ( 'post' == $post_type && 'auto-draft' != $post_status ) {
- $post_category = array( get_option('default_category') );
- } else {
- $post_category = array();
- }
- }
-
- // Don't allow contributors to set the post slug for pending review posts.
- if ( 'pending' == $post_status && !current_user_can( 'publish_posts' ) ) {
- $post_name = '';
- }
-
- /*
- * Create a valid post name. Drafts and pending posts are allowed to have
- * an empty post name.
- */
- if ( empty($post_name) ) {
- if ( !in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) ) {
- $post_name = sanitize_title($post_title);
- } else {
- $post_name = '';
- }
- } else {
- // On updates, we need to check to see if it's using the old, fixed sanitization context.
- $check_name = sanitize_title( $post_name, '', 'old-save' );
- if ( $update && strtolower( urlencode( $post_name ) ) == $check_name && get_post_field( 'post_name', $post_ID ) == $check_name ) {
- $post_name = $check_name;
- } else { // new post, or slug has changed.
- $post_name = sanitize_title($post_name);
- }
- }
-
- /*
- * If the post date is empty (due to having been new or a draft) and status
- * is not 'draft' or 'pending', set date to now.
- */
- if ( empty( $postarr['post_date'] ) || '0000-00-00 00:00:00' == $postarr['post_date'] ) {
- if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' == $postarr['post_date_gmt'] ) {
- $post_date = current_time( 'mysql' );
- } else {
- $post_date = get_date_from_gmt( $postarr['post_date_gmt'] );
- }
- } else {
- $post_date = $postarr['post_date'];
- }
-
- // Validate the date.
- $mm = substr( $post_date, 5, 2 );
- $jj = substr( $post_date, 8, 2 );
- $aa = substr( $post_date, 0, 4 );
- $valid_date = wp_checkdate( $mm, $jj, $aa, $post_date );
- if ( ! $valid_date ) {
- if ( $wp_error ) {
- return new WP_Error( 'invalid_date', __( 'Whoops, the provided date is invalid.' ) );
- } else {
- return 0;
- }
- }
-
- if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' == $postarr['post_date_gmt'] ) {
- if ( ! in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) ) {
- $post_date_gmt = get_gmt_from_date( $post_date );
- } else {
- $post_date_gmt = '0000-00-00 00:00:00';
- }
- } else {
- $post_date_gmt = $postarr['post_date_gmt'];
- }
-
- if ( $update || '0000-00-00 00:00:00' == $post_date ) {
- $post_modified = current_time( 'mysql' );
- $post_modified_gmt = current_time( 'mysql', 1 );
- } else {
- $post_modified = $post_date;
- $post_modified_gmt = $post_date_gmt;
- }
-
- if ( 'attachment' !== $post_type ) {
- if ( 'publish' == $post_status ) {
- $now = gmdate('Y-m-d H:i:59');
- if ( mysql2date('U', $post_date_gmt, false) > mysql2date('U', $now, false) ) {
- $post_status = 'future';
- }
- } elseif ( 'future' == $post_status ) {
- $now = gmdate('Y-m-d H:i:59');
- if ( mysql2date('U', $post_date_gmt, false) <= mysql2date('U', $now, false) ) {
- $post_status = 'publish';
- }
- }
- }
-
- // Comment status.
- if ( empty( $postarr['comment_status'] ) ) {
- if ( $update ) {
- $comment_status = 'closed';
- } else {
- $comment_status = get_default_comment_status( $post_type );
- }
- } else {
- $comment_status = $postarr['comment_status'];
- }
-
- // These variables are needed by compact() later.
- $post_content_filtered = $postarr['post_content_filtered'];
- $post_author = isset( $postarr['post_author'] ) ? $postarr['post_author'] : $user_id;
- $ping_status = empty( $postarr['ping_status'] ) ? get_default_comment_status( $post_type, 'pingback' ) : $postarr['ping_status'];
- $to_ping = isset( $postarr['to_ping'] ) ? sanitize_trackback_urls( $postarr['to_ping'] ) : '';
- $pinged = isset( $postarr['pinged'] ) ? $postarr['pinged'] : '';
- $import_id = isset( $postarr['import_id'] ) ? $postarr['import_id'] : 0;
-
- /*
- * The 'wp_insert_post_parent' filter expects all variables to be present.
- * Previously, these variables would have already been extracted
- */
- if ( isset( $postarr['menu_order'] ) ) {
- $menu_order = (int) $postarr['menu_order'];
- } else {
- $menu_order = 0;
- }
-
- $post_password = isset( $postarr['post_password'] ) ? $postarr['post_password'] : '';
- if ( 'private' == $post_status ) {
- $post_password = '';
- }
-
- if ( isset( $postarr['post_parent'] ) ) {
- $post_parent = (int) $postarr['post_parent'];
- } else {
- $post_parent = 0;
- }
-
- /**
- * Filter the post parent -- used to check for and prevent hierarchy loops.
- *
- * @since 3.1.0
- *
- * @param int $post_parent Post parent ID.
- * @param int $post_ID Post ID.
- * @param array $new_postarr Array of parsed post data.
- * @param array $postarr Array of sanitized, but otherwise unmodified post data.
- */
- $post_parent = apply_filters( 'wp_insert_post_parent', $post_parent, $post_ID, compact( array_keys( $postarr ) ), $postarr );
-
- $post_name = wp_unique_post_slug( $post_name, $post_ID, $post_status, $post_type, $post_parent );
-
- // Don't unslash.
- $post_mime_type = isset( $postarr['post_mime_type'] ) ? $postarr['post_mime_type'] : '';
-
- // Expected_slashed (everything!).
- $data = compact( 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_content_filtered', 'post_title', 'post_excerpt', 'post_status', 'post_type', 'comment_status', 'ping_status', 'post_password', 'post_name', 'to_ping', 'pinged', 'post_modified', 'post_modified_gmt', 'post_parent', 'menu_order', 'post_mime_type', 'guid' );
-
- $emoji_fields = array( 'post_title', 'post_content', 'post_excerpt' );
-
- foreach ( $emoji_fields as $emoji_field ) {
- if ( isset( $data[ $emoji_field ] ) ) {
- $charset = $wpdb->get_col_charset( $wpdb->posts, $emoji_field );
- if ( 'utf8' === $charset ) {
- $data[ $emoji_field ] = wp_encode_emoji( $data[ $emoji_field ] );
- }
- }
- }
-
- if ( 'attachment' === $post_type ) {
- /**
- * Filter attachment post data before it is updated in or added to the database.
- *
- * @since 3.9.0
- *
- * @param array $data An array of sanitized attachment post data.
- * @param array $postarr An array of unsanitized attachment post data.
- */
- $data = apply_filters( 'wp_insert_attachment_data', $data, $postarr );
- } else {
- /**
- * Filter slashed post data just before it is inserted into the database.
- *
- * @since 2.7.0
- *
- * @param array $data An array of slashed post data.
- * @param array $postarr An array of sanitized, but otherwise unmodified post data.
- */
- $data = apply_filters( 'wp_insert_post_data', $data, $postarr );
- }
- $data = wp_unslash( $data );
- $where = array( 'ID' => $post_ID );
-
- if ( $update ) {
- /**
- * Fires immediately before an existing post is updated in the database.
- *
- * @since 2.5.0
- *
- * @param int $post_ID Post ID.
- * @param array $data Array of unslashed post data.
- */
- do_action( 'pre_post_update', $post_ID, $data );
- if ( false === $wpdb->update( $wpdb->posts, $data, $where ) ) {
- if ( $wp_error ) {
- return new WP_Error('db_update_error', __('Could not update post in the database'), $wpdb->last_error);
- } else {
- return 0;
- }
- }
- } else {
- // If there is a suggested ID, use it if not already present.
- if ( ! empty( $import_id ) ) {
- $import_id = (int) $import_id;
- if ( ! $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE ID = %d", $import_id) ) ) {
- $data['ID'] = $import_id;
- }
- }
- if ( false === $wpdb->insert( $wpdb->posts, $data ) ) {
- if ( $wp_error ) {
- return new WP_Error('db_insert_error', __('Could not insert post into the database'), $wpdb->last_error);
- } else {
- return 0;
- }
- }
- $post_ID = (int) $wpdb->insert_id;
-
- // Use the newly generated $post_ID.
- $where = array( 'ID' => $post_ID );
- }
-
- if ( empty( $data['post_name'] ) && ! in_array( $data['post_status'], array( 'draft', 'pending', 'auto-draft' ) ) ) {
- $data['post_name'] = wp_unique_post_slug( sanitize_title( $data['post_title'], $post_ID ), $post_ID, $data['post_status'], $post_type, $post_parent );
- $wpdb->update( $wpdb->posts, array( 'post_name' => $data['post_name'] ), $where );
- clean_post_cache( $post_ID );
- }
-
- if ( is_object_in_taxonomy( $post_type, 'category' ) ) {
- wp_set_post_categories( $post_ID, $post_category );
- }
-
- if ( isset( $postarr['tags_input'] ) && is_object_in_taxonomy( $post_type, 'post_tag' ) ) {
- wp_set_post_tags( $post_ID, $postarr['tags_input'] );
- }
-
- // New-style support for all custom taxonomies.
- if ( ! empty( $postarr['tax_input'] ) ) {
- foreach ( $postarr['tax_input'] as $taxonomy => $tags ) {
- $taxonomy_obj = get_taxonomy($taxonomy);
- if ( ! $taxonomy_obj ) {
- /* translators: %s: taxonomy name */
- _doing_it_wrong( __FUNCTION__, sprintf( __( 'Invalid taxonomy: %s.' ), $taxonomy ), '4.4.0' );
- continue;
- }
-
- // array = hierarchical, string = non-hierarchical.
- if ( is_array( $tags ) ) {
- $tags = array_filter($tags);
- }
- if ( current_user_can( $taxonomy_obj->cap->assign_terms ) ) {
- wp_set_post_terms( $post_ID, $tags, $taxonomy );
- }
- }
- }
-
- if ( ! empty( $postarr['meta_input'] ) ) {
- foreach ( $postarr['meta_input'] as $field => $value ) {
- update_post_meta( $post_ID, $field, $value );
- }
- }
-
- $current_guid = get_post_field( 'guid', $post_ID );
-
- // Set GUID.
- if ( ! $update && '' == $current_guid ) {
- $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post_ID ) ), $where );
- }
-
- if ( 'attachment' === $postarr['post_type'] ) {
- if ( ! empty( $postarr['file'] ) ) {
- update_attached_file( $post_ID, $postarr['file'] );
- }
-
- if ( ! empty( $postarr['context'] ) ) {
- add_post_meta( $post_ID, '_wp_attachment_context', $postarr['context'], true );
- }
- }
-
- clean_post_cache( $post_ID );
-
- $post = get_post( $post_ID );
-
- if ( ! empty( $postarr['page_template'] ) && 'page' == $data['post_type'] ) {
- $post->page_template = $postarr['page_template'];
- $page_templates = wp_get_theme()->get_page_templates( $post );
- if ( 'default' != $postarr['page_template'] && ! isset( $page_templates[ $postarr['page_template'] ] ) ) {
- if ( $wp_error ) {
- return new WP_Error('invalid_page_template', __('The page template is invalid.'));
- }
- update_post_meta( $post_ID, '_wp_page_template', 'default' );
- } else {
- update_post_meta( $post_ID, '_wp_page_template', $postarr['page_template'] );
- }
- }
-
- if ( 'attachment' !== $postarr['post_type'] ) {
- wp_transition_post_status( $data['post_status'], $previous_status, $post );
- } else {
- if ( $update ) {
- /**
- * Fires once an existing attachment has been updated.
- *
- * @since 2.0.0
- *
- * @param int $post_ID Attachment ID.
- */
- do_action( 'edit_attachment', $post_ID );
- $post_after = get_post( $post_ID );
-
- /**
- * Fires once an existing attachment has been updated.
- *
- * @since 4.4.0
- *
- * @param int $post_ID Post ID.
- * @param WP_Post $post_after Post object following the update.
- * @param WP_Post $post_before Post object before the update.
- */
- do_action( 'attachment_updated', $post_ID, $post_after, $post_before );
- } else {
-
- /**
- * Fires once an attachment has been added.
- *
- * @since 2.0.0
- *
- * @param int $post_ID Attachment ID.
- */
- do_action( 'add_attachment', $post_ID );
- }
-
- return $post_ID;
- }
-
- if ( $update ) {
- /**
- * Fires once an existing post has been updated.
- *
- * @since 1.2.0
- *
- * @param int $post_ID Post ID.
- * @param WP_Post $post Post object.
- */
- do_action( 'edit_post', $post_ID, $post );
- $post_after = get_post($post_ID);
-
- /**
- * Fires once an existing post has been updated.
- *
- * @since 3.0.0
- *
- * @param int $post_ID Post ID.
- * @param WP_Post $post_after Post object following the update.
- * @param WP_Post $post_before Post object before the update.
- */
- do_action( 'post_updated', $post_ID, $post_after, $post_before);
- }
-
- /**
- * Fires once a post has been saved.
- *
- * The dynamic portion of the hook name, `$post->post_type`, refers to
- * the post type slug.
- *
- * @since 3.7.0
- *
- * @param int $post_ID Post ID.
- * @param WP_Post $post Post object.
- * @param bool $update Whether this is an existing post being updated or not.
- */
- do_action( "save_post_{$post->post_type}", $post_ID, $post, $update );
-
- /**
- * Fires once a post has been saved.
- *
- * @since 1.5.0
- *
- * @param int $post_ID Post ID.
- * @param WP_Post $post Post object.
- * @param bool $update Whether this is an existing post being updated or not.
- */
- do_action( 'save_post', $post_ID, $post, $update );
-
- /**
- * Fires once a post has been saved.
- *
- * @since 2.0.0
- *
- * @param int $post_ID Post ID.
- * @param WP_Post $post Post object.
- * @param bool $update Whether this is an existing post being updated or not.
- */
- do_action( 'wp_insert_post', $post_ID, $post, $update );
-
- return $post_ID;
-}
-
-/**
- * Update a post with new post data.
- *
- * The date does not have to be set for drafts. You can set the date and it will
- * not be overridden.
- *
- * @since 1.0.0
- *
- * @param array|object $postarr Optional. Post data. Arrays are expected to be escaped,
- * objects are not. Default array.
- * @param bool $wp_error Optional. Allow return of WP_Error on failure. Default false.
- * @return int|WP_Error The value 0 or WP_Error on failure. The post ID on success.
- */
-function wp_update_post( $postarr = array(), $wp_error = false ) {
- if ( is_object($postarr) ) {
- // Non-escaped post was passed.
- $postarr = get_object_vars($postarr);
- $postarr = wp_slash($postarr);
- }
-
- // First, get all of the original fields.
- $post = get_post($postarr['ID'], ARRAY_A);
-
- if ( is_null( $post ) ) {
- if ( $wp_error )
- return new WP_Error( 'invalid_post', __( 'Invalid post ID.' ) );
- return 0;
- }
-
- // Escape data pulled from DB.
- $post = wp_slash($post);
-
- // Passed post category list overwrites existing category list if not empty.
- if ( isset($postarr['post_category']) && is_array($postarr['post_category'])
- && 0 != count($postarr['post_category']) )
- $post_cats = $postarr['post_category'];
- else
- $post_cats = $post['post_category'];
-
- // Drafts shouldn't be assigned a date unless explicitly done so by the user.
- if ( isset( $post['post_status'] ) && in_array($post['post_status'], array('draft', 'pending', 'auto-draft')) && empty($postarr['edit_date']) &&
- ('0000-00-00 00:00:00' == $post['post_date_gmt']) )
- $clear_date = true;
- else
- $clear_date = false;
-
- // Merge old and new fields with new fields overwriting old ones.
- $postarr = array_merge($post, $postarr);
- $postarr['post_category'] = $post_cats;
- if ( $clear_date ) {
- $postarr['post_date'] = current_time('mysql');
- $postarr['post_date_gmt'] = '';
- }
-
- if ($postarr['post_type'] == 'attachment')
- return wp_insert_attachment($postarr);
-
- return wp_insert_post( $postarr, $wp_error );
-}
-
-/**
- * Publish a post by transitioning the post status.
- *
- * @since 2.1.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int|WP_Post $post Post ID or post object.
- */
-function wp_publish_post( $post ) {
- global $wpdb;
-
- if ( ! $post = get_post( $post ) )
- return;
-
- if ( 'publish' == $post->post_status )
- return;
-
- $wpdb->update( $wpdb->posts, array( 'post_status' => 'publish' ), array( 'ID' => $post->ID ) );
-
- clean_post_cache( $post->ID );
-
- $old_status = $post->post_status;
- $post->post_status = 'publish';
- wp_transition_post_status( 'publish', $old_status, $post );
-
- /** This action is documented in wp-includes/post-functions.php */
- do_action( 'edit_post', $post->ID, $post );
-
- /** This action is documented in wp-includes/post-functions.php */
- do_action( "save_post_{$post->post_type}", $post->ID, $post, true );
-
- /** This action is documented in wp-includes/post-functions.php */
- do_action( 'save_post', $post->ID, $post, true );
-
- /** This action is documented in wp-includes/post-functions.php */
- do_action( 'wp_insert_post', $post->ID, $post, true );
-}
-
-/**
- * Publish future post and make sure post ID has future post status.
- *
- * Invoked by cron 'publish_future_post' event. This safeguard prevents cron
- * from publishing drafts, etc.
- *
- * @since 2.5.0
- *
- * @param int|WP_Post $post_id Post ID or post object.
- */
-function check_and_publish_future_post( $post_id ) {
- $post = get_post($post_id);
-
- if ( empty($post) )
- return;
-
- if ( 'future' != $post->post_status )
- return;
-
- $time = strtotime( $post->post_date_gmt . ' GMT' );
-
- // Uh oh, someone jumped the gun!
- if ( $time > time() ) {
- wp_clear_scheduled_hook( 'publish_future_post', array( $post_id ) ); // clear anything else in the system
- wp_schedule_single_event( $time, 'publish_future_post', array( $post_id ) );
- return;
- }
-
- // wp_publish_post(_ returns no meaningful value.
- wp_publish_post( $post_id );
-}
-
-/**
- * Computes a unique slug for the post, when given the desired slug and some post details.
- *
- * @since 2.8.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- * @global WP_Rewrite $wp_rewrite
- *
- * @param string $slug The desired slug (post_name).
- * @param int $post_ID Post ID.
- * @param string $post_status No uniqueness checks are made if the post is still draft or pending.
- * @param string $post_type Post type.
- * @param int $post_parent Post parent ID.
- * @return string Unique slug for the post, based on $post_name (with a -1, -2, etc. suffix)
- */
-function wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_parent ) {
- if ( in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) || ( 'inherit' == $post_status && 'revision' == $post_type ) )
- return $slug;
-
- global $wpdb, $wp_rewrite;
-
- $original_slug = $slug;
-
- $feeds = $wp_rewrite->feeds;
- if ( ! is_array( $feeds ) )
- $feeds = array();
-
- if ( 'attachment' == $post_type ) {
- // Attachment slugs must be unique across all types.
- $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND ID != %d LIMIT 1";
- $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_ID ) );
-
- /**
- * Filter whether the post slug would make a bad attachment slug.
- *
- * @since 3.1.0
- *
- * @param bool $bad_slug Whether the slug would be bad as an attachment slug.
- * @param string $slug The post slug.
- */
- if ( $post_name_check || in_array( $slug, $feeds ) || apply_filters( 'wp_unique_post_slug_is_bad_attachment_slug', false, $slug ) ) {
- $suffix = 2;
- do {
- $alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
- $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_ID ) );
- $suffix++;
- } while ( $post_name_check );
- $slug = $alt_post_name;
- }
- } elseif ( is_post_type_hierarchical( $post_type ) ) {
- if ( 'nav_menu_item' == $post_type )
- return $slug;
-
- /*
- * Page slugs must be unique within their own trees. Pages are in a separate
- * namespace than posts so page slugs are allowed to overlap post slugs.
- */
- $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1";
- $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID, $post_parent ) );
-
- /**
- * Filter whether the post slug would make a bad hierarchical post slug.
- *
- * @since 3.1.0
- *
- * @param bool $bad_slug Whether the post slug would be bad in a hierarchical post context.
- * @param string $slug The post slug.
- * @param string $post_type Post type.
- * @param int $post_parent Post parent ID.
- */
- if ( $post_name_check || in_array( $slug, $feeds ) || preg_match( "@^($wp_rewrite->pagination_base)?\d+$@", $slug ) || apply_filters( 'wp_unique_post_slug_is_bad_hierarchical_slug', false, $slug, $post_type, $post_parent ) ) {
- $suffix = 2;
- do {
- $alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
- $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_ID, $post_parent ) );
- $suffix++;
- } while ( $post_name_check );
- $slug = $alt_post_name;
- }
- } else {
- // Post slugs must be unique across all posts.
- $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1";
- $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID ) );
-
- // Prevent new post slugs that could result in URLs that conflict with date archives.
- $post = get_post( $post_ID );
- $conflicts_with_date_archive = false;
- if ( 'post' === $post_type && ( ! $post || $post->post_name !== $slug ) && preg_match( '/^[0-9]+$/', $slug ) && $slug_num = intval( $slug ) ) {
- $permastructs = array_values( array_filter( explode( '/', get_option( 'permalink_structure' ) ) ) );
- $postname_index = array_search( '%postname%', $permastructs );
-
- /*
- * Potential date clashes are as follows:
- *
- * - Any integer in the first permastruct position could be a year.
- * - An integer between 1 and 12 that follows 'year' conflicts with 'monthnum'.
- * - An integer between 1 and 31 that follows 'monthnum' conflicts with 'day'.
- */
- if ( 0 === $postname_index ||
- ( $postname_index && '%year%' === $permastructs[ $postname_index - 1 ] && 13 > $slug_num ) ||
- ( $postname_index && '%monthnum%' === $permastructs[ $postname_index - 1 ] && 32 > $slug_num )
- ) {
- $conflicts_with_date_archive = true;
- }
- }
-
- /**
- * Filter whether the post slug would be bad as a flat slug.
- *
- * @since 3.1.0
- *
- * @param bool $bad_slug Whether the post slug would be bad as a flat slug.
- * @param string $slug The post slug.
- * @param string $post_type Post type.
- */
- if ( $post_name_check || in_array( $slug, $feeds ) || $conflicts_with_date_archive || apply_filters( 'wp_unique_post_slug_is_bad_flat_slug', false, $slug, $post_type ) ) {
- $suffix = 2;
- do {
- $alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
- $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_ID ) );
- $suffix++;
- } while ( $post_name_check );
- $slug = $alt_post_name;
- }
- }
-
- /**
- * Filter the unique post slug.
- *
- * @since 3.3.0
- *
- * @param string $slug The post slug.
- * @param int $post_ID Post ID.
- * @param string $post_status The post status.
- * @param string $post_type Post type.
- * @param int $post_parent Post parent ID
- * @param string $original_slug The original post slug.
- */
- return apply_filters( 'wp_unique_post_slug', $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug );
-}
-
-/**
- * Truncate a post slug.
- *
- * @since 3.6.0
- * @access private
- *
- * @see utf8_uri_encode()
- *
- * @param string $slug The slug to truncate.
- * @param int $length Optional. Max length of the slug. Default 200 (characters).
- * @return string The truncated slug.
- */
-function _truncate_post_slug( $slug, $length = 200 ) {
- if ( strlen( $slug ) > $length ) {
- $decoded_slug = urldecode( $slug );
- if ( $decoded_slug === $slug )
- $slug = substr( $slug, 0, $length );
- else
- $slug = utf8_uri_encode( $decoded_slug, $length );
- }
-
- return rtrim( $slug, '-' );
-}
-
-/**
- * Add tags to a post.
- *
- * @see wp_set_post_tags()
- *
- * @since 2.3.0
- *
- * @param int $post_id Optional. The Post ID. Does not default to the ID of the global $post.
- * @param string|array $tags Optional. An array of tags to set for the post, or a string of tags
- * separated by commas. Default empty.
- * @return array|false|WP_Error Array of affected term IDs. WP_Error or false on failure.
- */
-function wp_add_post_tags( $post_id = 0, $tags = '' ) {
- return wp_set_post_tags($post_id, $tags, true);
-}
-
-/**
- * Set the tags for a post.
- *
- * @since 2.3.0
- *
- * @see wp_set_object_terms()
- *
- * @param int $post_id Optional. The Post ID. Does not default to the ID of the global $post.
- * @param string|array $tags Optional. An array of tags to set for the post, or a string of tags
- * separated by commas. Default empty.
- * @param bool $append Optional. If true, don't delete existing tags, just add on. If false,
- * replace the tags with the new tags. Default false.
- * @return array|false|WP_Error Array of affected term IDs. WP_Error or false on failure.
- */
-function wp_set_post_tags( $post_id = 0, $tags = '', $append = false ) {
- return wp_set_post_terms( $post_id, $tags, 'post_tag', $append);
-}
-
-/**
- * Set the terms for a post.
- *
- * @since 2.8.0
- *
- * @see wp_set_object_terms()
- *
- * @param int $post_id Optional. The Post ID. Does not default to the ID of the global $post.
- * @param string|array $tags Optional. An array of terms to set for the post, or a string of terms
- * separated by commas. Default empty.
- * @param string $taxonomy Optional. Taxonomy name. Default 'post_tag'.
- * @param bool $append Optional. If true, don't delete existing terms, just add on. If false,
- * replace the terms with the new terms. Default false.
- * @return array|false|WP_Error Array of affected term IDs. WP_Error or false on failure.
- */
-function wp_set_post_terms( $post_id = 0, $tags = '', $taxonomy = 'post_tag', $append = false ) {
- $post_id = (int) $post_id;
-
- if ( !$post_id )
- return false;
-
- if ( empty($tags) )
- $tags = array();
-
- if ( ! is_array( $tags ) ) {
- $comma = _x( ',', 'tag delimiter' );
- if ( ',' !== $comma )
- $tags = str_replace( $comma, ',', $tags );
- $tags = explode( ',', trim( $tags, " \n\t\r\0\x0B," ) );
- }
-
- /*
- * Hierarchical taxonomies must always pass IDs rather than names so that
- * children with the same names but different parents aren't confused.
- */
- if ( is_taxonomy_hierarchical( $taxonomy ) ) {
- $tags = array_unique( array_map( 'intval', $tags ) );
- }
-
- return wp_set_object_terms( $post_id, $tags, $taxonomy, $append );
-}
-
-/**
- * Set categories for a post.
- *
- * If the post categories parameter is not set, then the default category is
- * going used.
- *
- * @since 2.1.0
- *
- * @param int $post_ID Optional. The Post ID. Does not default to the ID
- * of the global $post. Default 0.
- * @param array|int $post_categories Optional. List of categories or ID of category.
- * Default empty array.
- * @param bool $append If true, don't delete existing categories, just add on.
- * If false, replace the categories with the new categories.
- * @return array|bool|WP_Error
- */
-function wp_set_post_categories( $post_ID = 0, $post_categories = array(), $append = false ) {
- $post_ID = (int) $post_ID;
- $post_type = get_post_type( $post_ID );
- $post_status = get_post_status( $post_ID );
- // If $post_categories isn't already an array, make it one:
- $post_categories = (array) $post_categories;
- if ( empty( $post_categories ) ) {
- if ( 'post' == $post_type && 'auto-draft' != $post_status ) {
- $post_categories = array( get_option('default_category') );
- $append = false;
- } else {
- $post_categories = array();
- }
- } elseif ( 1 == count( $post_categories ) && '' == reset( $post_categories ) ) {
- return true;
- }
-
- return wp_set_post_terms( $post_ID, $post_categories, 'category', $append );
-}
-
-/**
- * Fires actions related to the transitioning of a post's status.
- *
- * When a post is saved, the post status is "transitioned" from one status to another,
- * though this does not always mean the status has actually changed before and after
- * the save. This function fires a number of action hooks related to that transition:
- * the generic 'transition_post_status' action, as well as the dynamic hooks
- * `"{$old_status}_to_{$new_status}"` and `"{$new_status}_{$post->post_type}"`. Note
- * that the function does not transition the post object in the database.
- *
- * For instance: When publishing a post for the first time, the post status may transition
- * from 'draft' – or some other status – to 'publish'. However, if a post is already
- * published and is simply being updated, the "old" and "new" statuses may both be 'publish'
- * before and after the transition.
- *
- * @since 2.3.0
- *
- * @param string $new_status Transition to this post status.
- * @param string $old_status Previous post status.
- * @param WP_Post $post Post data.
- */
-function wp_transition_post_status( $new_status, $old_status, $post ) {
- /**
- * Fires when a post is transitioned from one status to another.
- *
- * @since 2.3.0
- *
- * @param string $new_status New post status.
- * @param string $old_status Old post status.
- * @param WP_Post $post Post object.
- */
- do_action( 'transition_post_status', $new_status, $old_status, $post );
-
- /**
- * Fires when a post is transitioned from one status to another.
- *
- * The dynamic portions of the hook name, `$new_status` and `$old status`,
- * refer to the old and new post statuses, respectively.
- *
- * @since 2.3.0
- *
- * @param WP_Post $post Post object.
- */
- do_action( "{$old_status}_to_{$new_status}", $post );
-
- /**
- * Fires when a post is transitioned from one status to another.
- *
- * The dynamic portions of the hook name, `$new_status` and `$post->post_type`,
- * refer to the new post status and post type, respectively.
- *
- * Please note: When this action is hooked using a particular post status (like
- * 'publish', as `publish_{$post->post_type}`), it will fire both when a post is
- * first transitioned to that status from something else, as well as upon
- * subsequent post updates (old and new status are both the same).
- *
- * Therefore, if you are looking to only fire a callback when a post is first
- * transitioned to a status, use the {@see 'transition_post_status'} hook instead.
- *
- * @since 2.3.0
- *
- * @param int $post_id Post ID.
- * @param WP_Post $post Post object.
- */
- do_action( "{$new_status}_{$post->post_type}", $post->ID, $post );
-}
-
-//
-// Comment, trackback, and pingback functions.
-//
-
-/**
- * Add a URL to those already pinged.
- *
- * @since 1.5.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $post_id Post ID.
- * @param string $uri Ping URI.
- * @return int|false How many rows were updated.
- */
-function add_ping( $post_id, $uri ) {
- global $wpdb;
- $pung = $wpdb->get_var( $wpdb->prepare( "SELECT pinged FROM $wpdb->posts WHERE ID = %d", $post_id ));
- $pung = trim($pung);
- $pung = preg_split('/\s/', $pung);
- $pung[] = $uri;
- $new = implode("\n", $pung);
-
- /**
- * Filter the new ping URL to add for the given post.
- *
- * @since 2.0.0
- *
- * @param string $new New ping URL to add.
- */
- $new = apply_filters( 'add_ping', $new );
-
- // expected_slashed ($new).
- $new = wp_unslash($new);
- return $wpdb->update( $wpdb->posts, array( 'pinged' => $new ), array( 'ID' => $post_id ) );
-}
-
-/**
- * Retrieve enclosures already enclosed for a post.
- *
- * @since 1.5.0
- *
- * @param int $post_id Post ID.
- * @return array List of enclosures.
- */
-function get_enclosed( $post_id ) {
- $custom_fields = get_post_custom( $post_id );
- $pung = array();
- if ( !is_array( $custom_fields ) )
- return $pung;
-
- foreach ( $custom_fields as $key => $val ) {
- if ( 'enclosure' != $key || !is_array( $val ) )
- continue;
- foreach ( $val as $enc ) {
- $enclosure = explode( "\n", $enc );
- $pung[] = trim( $enclosure[ 0 ] );
- }
- }
-
- /**
- * Filter the list of enclosures already enclosed for the given post.
- *
- * @since 2.0.0
- *
- * @param array $pung Array of enclosures for the given post.
- * @param int $post_id Post ID.
- */
- return apply_filters( 'get_enclosed', $pung, $post_id );
-}
-
-/**
- * Retrieve URLs already pinged for a post.
- *
- * @since 1.5.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $post_id Post ID.
- * @return array
- */
-function get_pung( $post_id ) {
- global $wpdb;
- $pung = $wpdb->get_var( $wpdb->prepare( "SELECT pinged FROM $wpdb->posts WHERE ID = %d", $post_id ));
- $pung = trim($pung);
- $pung = preg_split('/\s/', $pung);
-
- /**
- * Filter the list of already-pinged URLs for the given post.
- *
- * @since 2.0.0
- *
- * @param array $pung Array of URLs already pinged for the given post.
- */
- return apply_filters( 'get_pung', $pung );
-}
-
-/**
- * Retrieve URLs that need to be pinged.
- *
- * @since 1.5.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $post_id Post ID
- * @return array
- */
-function get_to_ping( $post_id ) {
- global $wpdb;
- $to_ping = $wpdb->get_var( $wpdb->prepare( "SELECT to_ping FROM $wpdb->posts WHERE ID = %d", $post_id ));
- $to_ping = sanitize_trackback_urls( $to_ping );
- $to_ping = preg_split('/\s/', $to_ping, -1, PREG_SPLIT_NO_EMPTY);
-
- /**
- * Filter the list of URLs yet to ping for the given post.
- *
- * @since 2.0.0
- *
- * @param array $to_ping List of URLs yet to ping.
- */
- return apply_filters( 'get_to_ping', $to_ping );
-}
-
-/**
- * Do trackbacks for a list of URLs.
- *
- * @since 1.0.0
- *
- * @param string $tb_list Comma separated list of URLs.
- * @param int $post_id Post ID.
- */
-function trackback_url_list( $tb_list, $post_id ) {
- if ( ! empty( $tb_list ) ) {
- // Get post data.
- $postdata = get_post( $post_id, ARRAY_A );
-
- // Form an excerpt.
- $excerpt = strip_tags( $postdata['post_excerpt'] ? $postdata['post_excerpt'] : $postdata['post_content'] );
-
- if ( strlen( $excerpt ) > 255 ) {
- $excerpt = substr( $excerpt, 0, 252 ) . '…';
- }
-
- $trackback_urls = explode( ',', $tb_list );
- foreach ( (array) $trackback_urls as $tb_url ) {
- $tb_url = trim( $tb_url );
- trackback( $tb_url, wp_unslash( $postdata['post_title'] ), $excerpt, $post_id );
- }
- }
-}
-
-//
-// Page functions
-//
-
-/**
- * Get a list of page IDs.
- *
- * @since 2.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @return array List of page IDs.
- */
-function get_all_page_ids() {
- global $wpdb;
-
- $page_ids = wp_cache_get('all_page_ids', 'posts');
- if ( ! is_array( $page_ids ) ) {
- $page_ids = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE post_type = 'page'");
- wp_cache_add('all_page_ids', $page_ids, 'posts');
- }
-
- return $page_ids;
-}
-
-/**
- * Retrieves page data given a page ID or page object.
- *
- * Use get_post() instead of get_page().
- *
- * @since 1.5.1
- * @deprecated 3.5.0 Use get_post()
- *
- * @param mixed $page Page object or page ID. Passed by reference.
- * @param string $output Optional. What to output. Accepts OBJECT, ARRAY_A, or ARRAY_N.
- * Default OBJECT.
- * @param string $filter Optional. How the return value should be filtered. Accepts 'raw',
- * 'edit', 'db', 'display'. Default 'raw'.
- * @return WP_Post|array|null WP_Post on success or null on failure.
- */
-function get_page( $page, $output = OBJECT, $filter = 'raw') {
- return get_post( $page, $output, $filter );
-}
-
-/**
- * Retrieves a page given its path.
- *
- * @since 2.1.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $page_path Page path.
- * @param string $output Optional. Output type. Accepts OBJECT, ARRAY_N, or ARRAY_A.
- * Default OBJECT.
- * @param string|array $post_type Optional. Post type or array of post types. Default 'page'.
- * @return WP_Post|array|void WP_Post on success.
- */
-function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) {
- global $wpdb;
-
- $page_path = rawurlencode(urldecode($page_path));
- $page_path = str_replace('%2F', '/', $page_path);
- $page_path = str_replace('%20', ' ', $page_path);
- $parts = explode( '/', trim( $page_path, '/' ) );
- $parts = esc_sql( $parts );
- $parts = array_map( 'sanitize_title_for_query', $parts );
-
- $in_string = "'" . implode( "','", $parts ) . "'";
-
- if ( is_array( $post_type ) ) {
- $post_types = $post_type;
- } else {
- $post_types = array( $post_type, 'attachment' );
- }
-
- $post_types = esc_sql( $post_types );
- $post_type_in_string = "'" . implode( "','", $post_types ) . "'";
- $sql = "
- SELECT ID, post_name, post_parent, post_type
- FROM $wpdb->posts
- WHERE post_name IN ($in_string)
- AND post_type IN ($post_type_in_string)
- ";
-
- $pages = $wpdb->get_results( $sql, OBJECT_K );
-
- $revparts = array_reverse( $parts );
-
- $foundid = 0;
- foreach ( (array) $pages as $page ) {
- if ( $page->post_name == $revparts[0] ) {
- $count = 0;
- $p = $page;
- while ( $p->post_parent != 0 && isset( $pages[ $p->post_parent ] ) ) {
- $count++;
- $parent = $pages[ $p->post_parent ];
- if ( ! isset( $revparts[ $count ] ) || $parent->post_name != $revparts[ $count ] )
- break;
- $p = $parent;
- }
-
- if ( $p->post_parent == 0 && $count+1 == count( $revparts ) && $p->post_name == $revparts[ $count ] ) {
- $foundid = $page->ID;
- if ( $page->post_type == $post_type )
- break;
- }
- }
- }
-
- if ( $foundid ) {
- return get_post( $foundid, $output );
- }
-}
-
-/**
- * Retrieve a page given its title.
- *
- * @since 2.1.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $page_title Page title
- * @param string $output Optional. Output type. OBJECT, ARRAY_N, or ARRAY_A.
- * Default OBJECT.
- * @param string|array $post_type Optional. Post type or array of post types. Default 'page'.
- * @return WP_Post|array|void WP_Post on success or null on failure
- */
-function get_page_by_title( $page_title, $output = OBJECT, $post_type = 'page' ) {
- global $wpdb;
-
- if ( is_array( $post_type ) ) {
- $post_type = esc_sql( $post_type );
- $post_type_in_string = "'" . implode( "','", $post_type ) . "'";
- $sql = $wpdb->prepare( "
- SELECT ID
- FROM $wpdb->posts
- WHERE post_title = %s
- AND post_type IN ($post_type_in_string)
- ", $page_title );
- } else {
- $sql = $wpdb->prepare( "
- SELECT ID
- FROM $wpdb->posts
- WHERE post_title = %s
- AND post_type = %s
- ", $page_title, $post_type );
- }
-
- $page = $wpdb->get_var( $sql );
-
- if ( $page ) {
- return get_post( $page, $output );
- }
-}
-
-/**
- * Identify descendants of a given page ID in a list of page objects.
- *
- * Descendants are identified from the `$pages` array passed to the function. No database queries are performed.
- *
- * @since 1.5.1
- *
- * @param int $page_id Page ID.
- * @param array $pages List of page objects from which descendants should be identified.
- * @return array List of page children.
- */
-function get_page_children( $page_id, $pages ) {
- // Build a hash of ID -> children.
- $children = array();
- foreach ( (array) $pages as $page ) {
- $children[ intval( $page->post_parent ) ][] = $page;
- }
-
- $page_list = array();
-
- // Start the search by looking at immediate children.
- if ( isset( $children[ $page_id ] ) ) {
- // Always start at the end of the stack in order to preserve original `$pages` order.
- $to_look = array_reverse( $children[ $page_id ] );
-
- while ( $to_look ) {
- $p = array_pop( $to_look );
- $page_list[] = $p;
- if ( isset( $children[ $p->ID ] ) ) {
- foreach ( array_reverse( $children[ $p->ID ] ) as $child ) {
- // Append to the `$to_look` stack to descend the tree.
- $to_look[] = $child;
- }
- }
- }
- }
-
- return $page_list;
-}
-
-/**
- * Order the pages with children under parents in a flat list.
- *
- * It uses auxiliary structure to hold parent-children relationships and
- * runs in O(N) complexity
- *
- * @since 2.0.0
- *
- * @param array $pages Posts array, passed by reference.
- * @param int $page_id Optional. Parent page ID. Default 0.
- * @return array A list arranged by hierarchy. Children immediately follow their parents.
- */
-function get_page_hierarchy( &$pages, $page_id = 0 ) {
- if ( empty( $pages ) ) {
- return array();
- }
-
- $children = array();
- foreach ( (array) $pages as $p ) {
- $parent_id = intval( $p->post_parent );
- $children[ $parent_id ][] = $p;
- }
-
- $result = array();
- _page_traverse_name( $page_id, $children, $result );
-
- return $result;
-}
-
-/**
- * Traverse and return all the nested children post names of a root page.
- *
- * $children contains parent-children relations
- *
- * @since 2.9.0
- *
- * @see _page_traverse_name()
- *
- * @param int $page_id Page ID.
- * @param array &$children Parent-children relations, passed by reference.
- * @param array &$result Result, passed by reference.
- */
-function _page_traverse_name( $page_id, &$children, &$result ){
- if ( isset( $children[ $page_id ] ) ){
- foreach ( (array)$children[ $page_id ] as $child ) {
- $result[ $child->ID ] = $child->post_name;
- _page_traverse_name( $child->ID, $children, $result );
- }
- }
-}
-
-/**
- * Build URI for a page.
- *
- * Sub pages will be in the "directory" under the parent page post name.
- *
- * @since 1.5.0
- *
- * @param WP_Post|object|int $page Page object or page ID.
- * @return string|false Page URI, false on error.
- */
-function get_page_uri( $page ) {
- $page = get_post( $page );
-
- if ( ! $page )
- return false;
-
- $uri = $page->post_name;
-
- foreach ( $page->ancestors as $parent ) {
- $parent = get_post( $parent );
- if ( 'publish' === $parent->post_status ) {
- $uri = $parent->post_name . '/' . $uri;
- }
- }
-
- /**
- * Filter the URI for a page.
- *
- * @since 4.4.0
- *
- * @param string $uri Page URI.
- * @param WP_Post $page Page object.
- */
- return apply_filters( 'get_page_uri', $uri, $page );
-}
-
-/**
- * Retrieve a list of pages.
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @since 1.5.0
- *
- * @param array|string $args {
- * Optional. Array or string of arguments to retrieve pages.
- *
- * @type int $child_of Page ID to return child and grandchild pages of. Note: The value
- * of `$hierarchical` has no bearing on whether `$child_of` returns
- * hierarchical results. Default 0, or no restriction.
- * @type string $sort_order How to sort retrieved pages. Accepts 'ASC', 'DESC'. Default 'ASC'.
- * @type string $sort_column What columns to sort pages by, comma-separated. Accepts 'post_author',
- * 'post_date', 'post_title', 'post_name', 'post_modified', 'menu_order',
- * 'post_modified_gmt', 'post_parent', 'ID', 'rand', 'comment_count'.
- * 'post_' can be omitted for any values that start with it.
- * Default 'post_title'.
- * @type bool $hierarchical Whether to return pages hierarchically. If false in conjunction with
- * `$child_of` also being false, both arguments will be disregarded.
- * Default true.
- * @type array $exclude Array of page IDs to exclude. Default empty array.
- * @type array $include Array of page IDs to include. Cannot be used with `$child_of`,
- * `$parent`, `$exclude`, `$meta_key`, `$meta_value`, or `$hierarchical`.
- * Default empty array.
- * @type string $meta_key Only include pages with this meta key. Default empty.
- * @type string $meta_value Only include pages with this meta value. Requires `$meta_key`.
- * Default empty.
- * @type string $authors A comma-separated list of author IDs. Default empty.
- * @type int $parent Page ID to return direct children of. Default -1, or no restriction.
- * @type string|array $exclude_tree Comma-separated string or array of page IDs to exclude.
- * Default empty array.
- * @type int $number The number of pages to return. Default 0, or all pages.
- * @type int $offset The number of pages to skip before returning. Requires `$number`.
- * Default 0.
- * @type string $post_type The post type to query. Default 'page'.
- * @type string $post_status A comma-separated list of post status types to include.
- * Default 'publish'.
- * }
- * @return array|false List of pages matching defaults or `$args`.
- */
-function get_pages( $args = array() ) {
- global $wpdb;
-
- $defaults = array(
- 'child_of' => 0, 'sort_order' => 'ASC',
- 'sort_column' => 'post_title', 'hierarchical' => 1,
- 'exclude' => array(), 'include' => array(),
- 'meta_key' => '', 'meta_value' => '',
- 'authors' => '', 'parent' => -1, 'exclude_tree' => array(),
- 'number' => '', 'offset' => 0,
- 'post_type' => 'page', 'post_status' => 'publish',
- );
-
- $r = wp_parse_args( $args, $defaults );
-
- $number = (int) $r['number'];
- $offset = (int) $r['offset'];
- $child_of = (int) $r['child_of'];
- $hierarchical = $r['hierarchical'];
- $exclude = $r['exclude'];
- $meta_key = $r['meta_key'];
- $meta_value = $r['meta_value'];
- $parent = $r['parent'];
- $post_status = $r['post_status'];
-
- // Make sure the post type is hierarchical.
- $hierarchical_post_types = get_post_types( array( 'hierarchical' => true ) );
- if ( ! in_array( $r['post_type'], $hierarchical_post_types ) ) {
- return false;
- }
-
- if ( $parent > 0 && ! $child_of ) {
- $hierarchical = false;
- }
-
- // Make sure we have a valid post status.
- if ( ! is_array( $post_status ) ) {
- $post_status = explode( ',', $post_status );
- }
- if ( array_diff( $post_status, get_post_stati() ) ) {
- return false;
- }
-
- // $args can be whatever, only use the args defined in defaults to compute the key.
- $key = md5( serialize( wp_array_slice_assoc( $r, array_keys( $defaults ) ) ) );
- $last_changed = wp_cache_get( 'last_changed', 'posts' );
- if ( ! $last_changed ) {
- $last_changed = microtime();
- wp_cache_set( 'last_changed', $last_changed, 'posts' );
- }
-
- $cache_key = "get_pages:$key:$last_changed";
- if ( $cache = wp_cache_get( $cache_key, 'posts' ) ) {
- // Convert to WP_Post instances.
- $pages = array_map( 'get_post', $cache );
- /** This filter is documented in wp-includes/post-functions.php */
- $pages = apply_filters( 'get_pages', $pages, $r );
- return $pages;
- }
-
- $inclusions = '';
- if ( ! empty( $r['include'] ) ) {
- $child_of = 0; //ignore child_of, parent, exclude, meta_key, and meta_value params if using include
- $parent = -1;
- $exclude = '';
- $meta_key = '';
- $meta_value = '';
- $hierarchical = false;
- $incpages = wp_parse_id_list( $r['include'] );
- if ( ! empty( $incpages ) ) {
- $inclusions = ' AND ID IN (' . implode( ',', $incpages ) . ')';
- }
- }
-
- $exclusions = '';
- if ( ! empty( $exclude ) ) {
- $expages = wp_parse_id_list( $exclude );
- if ( ! empty( $expages ) ) {
- $exclusions = ' AND ID NOT IN (' . implode( ',', $expages ) . ')';
- }
- }
-
- $author_query = '';
- if ( ! empty( $r['authors'] ) ) {
- $post_authors = preg_split( '/[\s,]+/', $r['authors'] );
-
- if ( ! empty( $post_authors ) ) {
- foreach ( $post_authors as $post_author ) {
- //Do we have an author id or an author login?
- if ( 0 == intval($post_author) ) {
- $post_author = get_user_by('login', $post_author);
- if ( empty( $post_author ) ) {
- continue;
- }
- if ( empty( $post_author->ID ) ) {
- continue;
- }
- $post_author = $post_author->ID;
- }
-
- if ( '' == $author_query ) {
- $author_query = $wpdb->prepare(' post_author = %d ', $post_author);
- } else {
- $author_query .= $wpdb->prepare(' OR post_author = %d ', $post_author);
- }
- }
- if ( '' != $author_query ) {
- $author_query = " AND ($author_query)";
- }
- }
- }
-
- $join = '';
- $where = "$exclusions $inclusions ";
- if ( '' !== $meta_key || '' !== $meta_value ) {
- $join = " LEFT JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id )";
-
- // meta_key and meta_value might be slashed
- $meta_key = wp_unslash($meta_key);
- $meta_value = wp_unslash($meta_value);
- if ( '' !== $meta_key ) {
- $where .= $wpdb->prepare(" AND $wpdb->postmeta.meta_key = %s", $meta_key);
- }
- if ( '' !== $meta_value ) {
- $where .= $wpdb->prepare(" AND $wpdb->postmeta.meta_value = %s", $meta_value);
- }
-
- }
-
- if ( is_array( $parent ) ) {
- $post_parent__in = implode( ',', array_map( 'absint', (array) $parent ) );
- if ( ! empty( $post_parent__in ) ) {
- $where .= " AND post_parent IN ($post_parent__in)";
- }
- } elseif ( $parent >= 0 ) {
- $where .= $wpdb->prepare(' AND post_parent = %d ', $parent);
- }
-
- if ( 1 == count( $post_status ) ) {
- $where_post_type = $wpdb->prepare( "post_type = %s AND post_status = %s", $r['post_type'], reset( $post_status ) );
- } else {
- $post_status = implode( "', '", $post_status );
- $where_post_type = $wpdb->prepare( "post_type = %s AND post_status IN ('$post_status')", $r['post_type'] );
- }
-
- $orderby_array = array();
- $allowed_keys = array( 'author', 'post_author', 'date', 'post_date', 'title', 'post_title', 'name', 'post_name', 'modified',
- 'post_modified', 'modified_gmt', 'post_modified_gmt', 'menu_order', 'parent', 'post_parent',
- 'ID', 'rand', 'comment_count' );
-
- foreach ( explode( ',', $r['sort_column'] ) as $orderby ) {
- $orderby = trim( $orderby );
- if ( ! in_array( $orderby, $allowed_keys ) ) {
- continue;
- }
-
- switch ( $orderby ) {
- case 'menu_order':
- break;
- case 'ID':
- $orderby = "$wpdb->posts.ID";
- break;
- case 'rand':
- $orderby = 'RAND()';
- break;
- case 'comment_count':
- $orderby = "$wpdb->posts.comment_count";
- break;
- default:
- if ( 0 === strpos( $orderby, 'post_' ) ) {
- $orderby = "$wpdb->posts." . $orderby;
- } else {
- $orderby = "$wpdb->posts.post_" . $orderby;
- }
- }
-
- $orderby_array[] = $orderby;
-
- }
- $sort_column = ! empty( $orderby_array ) ? implode( ',', $orderby_array ) : "$wpdb->posts.post_title";
-
- $sort_order = strtoupper( $r['sort_order'] );
- if ( '' !== $sort_order && ! in_array( $sort_order, array( 'ASC', 'DESC' ) ) ) {
- $sort_order = 'ASC';
- }
-
- $query = "SELECT * FROM $wpdb->posts $join WHERE ($where_post_type) $where ";
- $query .= $author_query;
- $query .= " ORDER BY " . $sort_column . " " . $sort_order ;
-
- if ( ! empty( $number ) ) {
- $query .= ' LIMIT ' . $offset . ',' . $number;
- }
-
- $pages = $wpdb->get_results($query);
-
- if ( empty($pages) ) {
- /** This filter is documented in wp-includes/post-functions.php */
- $pages = apply_filters( 'get_pages', array(), $r );
- return $pages;
- }
-
- // Sanitize before caching so it'll only get done once.
- $num_pages = count($pages);
- for ($i = 0; $i < $num_pages; $i++) {
- $pages[$i] = sanitize_post($pages[$i], 'raw');
- }
-
- // Update cache.
- update_post_cache( $pages );
-
- if ( $child_of || $hierarchical ) {
- $pages = get_page_children($child_of, $pages);
- }
-
- if ( ! empty( $r['exclude_tree'] ) ) {
- $exclude = wp_parse_id_list( $r['exclude_tree'] );
- foreach ( $exclude as $id ) {
- $children = get_page_children( $id, $pages );
- foreach ( $children as $child ) {
- $exclude[] = $child->ID;
- }
- }
-
- $num_pages = count( $pages );
- for ( $i = 0; $i < $num_pages; $i++ ) {
- if ( in_array( $pages[$i]->ID, $exclude ) ) {
- unset( $pages[$i] );
- }
- }
- }
-
- $page_structure = array();
- foreach ( $pages as $page ) {
- $page_structure[] = $page->ID;
- }
-
- wp_cache_set( $cache_key, $page_structure, 'posts' );
-
- // Convert to WP_Post instances
- $pages = array_map( 'get_post', $pages );
-
- /**
- * Filter the retrieved list of pages.
- *
- * @since 2.1.0
- *
- * @param array $pages List of pages to retrieve.
- * @param array $r Array of get_pages() arguments.
- */
- return apply_filters( 'get_pages', $pages, $r );
-}
-
-//
-// Attachment functions
-//
-
-/**
- * Check if the attachment URI is local one and is really an attachment.
- *
- * @since 2.0.0
- *
- * @param string $url URL to check
- * @return bool True on success, false on failure.
- */
-function is_local_attachment($url) {
- if (strpos($url, home_url()) === false)
- return false;
- if (strpos($url, home_url('/?attachment_id=')) !== false)
- return true;
- if ( $id = url_to_postid($url) ) {
- $post = get_post($id);
- if ( 'attachment' == $post->post_type )
- return true;
- }
- return false;
-}
-
-/**
- * Insert an attachment.
- *
- * If you set the 'ID' in the $args parameter, it will mean that you are
- * updating and attempt to update the attachment. You can also set the
- * attachment name or title by setting the key 'post_name' or 'post_title'.
- *
- * You can set the dates for the attachment manually by setting the 'post_date'
- * and 'post_date_gmt' keys' values.
- *
- * By default, the comments will use the default settings for whether the
- * comments are allowed. You can close them manually or keep them open by
- * setting the value for the 'comment_status' key.
- *
- * @since 2.0.0
- *
- * @see wp_insert_post()
- *
- * @param string|array $args Arguments for inserting an attachment.
- * @param string $file Optional. Filename.
- * @param int $parent Optional. Parent post ID.
- * @return int Attachment ID.
- */
-function wp_insert_attachment( $args, $file = false, $parent = 0 ) {
- $defaults = array(
- 'file' => $file,
- 'post_parent' => 0
- );
-
- $data = wp_parse_args( $args, $defaults );
-
- if ( ! empty( $parent ) ) {
- $data['post_parent'] = $parent;
- }
-
- $data['post_type'] = 'attachment';
-
- return wp_insert_post( $data );
-}
-
-/**
- * Trash or delete an attachment.
- *
- * When an attachment is permanently deleted, the file will also be removed.
- * Deletion removes all post meta fields, taxonomy, comments, etc. associated
- * with the attachment (except the main post).
- *
- * The attachment is moved to the trash instead of permanently deleted unless trash
- * for media is disabled, item is already in the trash, or $force_delete is true.
- *
- * @since 2.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $post_id Attachment ID.
- * @param bool $force_delete Optional. Whether to bypass trash and force deletion.
- * Default false.
- * @return mixed False on failure. Post data on success.
- */
-function wp_delete_attachment( $post_id, $force_delete = false ) {
- global $wpdb;
-
- if ( !$post = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d", $post_id) ) )
- return $post;
-
- if ( 'attachment' != $post->post_type )
- return false;
-
- if ( !$force_delete && EMPTY_TRASH_DAYS && MEDIA_TRASH && 'trash' != $post->post_status )
- return wp_trash_post( $post_id );
-
- delete_post_meta($post_id, '_wp_trash_meta_status');
- delete_post_meta($post_id, '_wp_trash_meta_time');
-
- $meta = wp_get_attachment_metadata( $post_id );
- $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true );
- $file = get_attached_file( $post_id );
-
- if ( is_multisite() )
- delete_transient( 'dirsize_cache' );
-
- /**
- * Fires before an attachment is deleted, at the start of wp_delete_attachment().
- *
- * @since 2.0.0
- *
- * @param int $post_id Attachment ID.
- */
- do_action( 'delete_attachment', $post_id );
-
- wp_delete_object_term_relationships($post_id, array('category', 'post_tag'));
- wp_delete_object_term_relationships($post_id, get_object_taxonomies($post->post_type));
-
- // Delete all for any posts.
- delete_metadata( 'post', null, '_thumbnail_id', $post_id, true );
-
- wp_defer_comment_counting( true );
-
- $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id ));
- foreach ( $comment_ids as $comment_id ) {
- wp_delete_comment( $comment_id, true );
- }
-
- wp_defer_comment_counting( false );
-
- $post_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d ", $post_id ));
- foreach ( $post_meta_ids as $mid )
- delete_metadata_by_mid( 'post', $mid );
-
- /** This action is documented in wp-includes/post-functions.php */
- do_action( 'delete_post', $post_id );
- $result = $wpdb->delete( $wpdb->posts, array( 'ID' => $post_id ) );
- if ( ! $result ) {
- return false;
- }
- /** This action is documented in wp-includes/post-functions.php */
- do_action( 'deleted_post', $post_id );
-
- $uploadpath = wp_upload_dir();
-
- if ( ! empty($meta['thumb']) ) {
- // Don't delete the thumb if another attachment uses it.
- if (! $wpdb->get_row( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attachment_metadata' AND meta_value LIKE %s AND post_id <> %d", '%' . $wpdb->esc_like( $meta['thumb'] ) . '%', $post_id)) ) {
- $thumbfile = str_replace(basename($file), $meta['thumb'], $file);
- /** This filter is documented in wp-includes/functions.php */
- $thumbfile = apply_filters( 'wp_delete_file', $thumbfile );
- @ unlink( path_join($uploadpath['basedir'], $thumbfile) );
- }
- }
-
- // Remove intermediate and backup images if there are any.
- if ( isset( $meta['sizes'] ) && is_array( $meta['sizes'] ) ) {
- foreach ( $meta['sizes'] as $size => $sizeinfo ) {
- $intermediate_file = str_replace( basename( $file ), $sizeinfo['file'], $file );
- /** This filter is documented in wp-includes/functions.php */
- $intermediate_file = apply_filters( 'wp_delete_file', $intermediate_file );
- @ unlink( path_join( $uploadpath['basedir'], $intermediate_file ) );
- }
- }
-
- if ( is_array($backup_sizes) ) {
- foreach ( $backup_sizes as $size ) {
- $del_file = path_join( dirname($meta['file']), $size['file'] );
- /** This filter is documented in wp-includes/functions.php */
- $del_file = apply_filters( 'wp_delete_file', $del_file );
- @ unlink( path_join($uploadpath['basedir'], $del_file) );
- }
- }
-
- wp_delete_file( $file );
-
- clean_post_cache( $post );
-
- return $post;
-}
-
-/**
- * Retrieve attachment meta field for attachment ID.
- *
- * @since 2.1.0
- *
- * @param int $post_id Attachment ID. Default 0.
- * @param bool $unfiltered Optional. If true, filters are not run. Default false.
- * @return mixed Attachment meta field. False on failure.
- */
-function wp_get_attachment_metadata( $post_id = 0, $unfiltered = false ) {
- $post_id = (int) $post_id;
- if ( !$post = get_post( $post_id ) )
- return false;
-
- $data = get_post_meta( $post->ID, '_wp_attachment_metadata', true );
-
- if ( $unfiltered )
- return $data;
-
- /**
- * Filter the attachment meta data.
- *
- * @since 2.1.0
- *
- * @param array|bool $data Array of meta data for the given attachment, or false
- * if the object does not exist.
- * @param int $post_id Attachment ID.
- */
- return apply_filters( 'wp_get_attachment_metadata', $data, $post->ID );
-}
-
-/**
- * Update metadata for an attachment.
- *
- * @since 2.1.0
- *
- * @param int $post_id Attachment ID.
- * @param array $data Attachment data.
- * @return int|bool False if $post is invalid.
- */
-function wp_update_attachment_metadata( $post_id, $data ) {
- $post_id = (int) $post_id;
- if ( !$post = get_post( $post_id ) )
- return false;
-
- /**
- * Filter the updated attachment meta data.
- *
- * @since 2.1.0
- *
- * @param array $data Array of updated attachment meta data.
- * @param int $post_id Attachment ID.
- */
- if ( $data = apply_filters( 'wp_update_attachment_metadata', $data, $post->ID ) )
- return update_post_meta( $post->ID, '_wp_attachment_metadata', $data );
- else
- return delete_post_meta( $post->ID, '_wp_attachment_metadata' );
-}
-
-/**
- * Retrieve the URL for an attachment.
- *
- * @since 2.1.0
- *
- * @global string $pagenow
- *
- * @param int $post_id Optional. Attachment ID. Default 0.
- * @return string|false Attachment URL, otherwise false.
- */
-function wp_get_attachment_url( $post_id = 0 ) {
- $post_id = (int) $post_id;
- if ( !$post = get_post( $post_id ) )
- return false;
-
- if ( 'attachment' != $post->post_type )
- return false;
-
- $url = '';
- // Get attached file.
- if ( $file = get_post_meta( $post->ID, '_wp_attached_file', true) ) {
- // Get upload directory.
- if ( ($uploads = wp_upload_dir()) && false === $uploads['error'] ) {
- // Check that the upload base exists in the file location.
- if ( 0 === strpos( $file, $uploads['basedir'] ) ) {
- // Replace file location with url location.
- $url = str_replace($uploads['basedir'], $uploads['baseurl'], $file);
- } elseif ( false !== strpos($file, 'wp-content/uploads') ) {
- $url = $uploads['baseurl'] . substr( $file, strpos($file, 'wp-content/uploads') + 18 );
- } else {
- // It's a newly-uploaded file, therefore $file is relative to the basedir.
- $url = $uploads['baseurl'] . "/$file";
- }
- }
- }
-
- /*
- * If any of the above options failed, Fallback on the GUID as used pre-2.7,
- * not recommended to rely upon this.
- */
- if ( empty($url) ) {
- $url = get_the_guid( $post->ID );
- }
-
- // On SSL front-end, URLs should be HTTPS.
- if ( is_ssl() && ! is_admin() && 'wp-login.php' !== $GLOBALS['pagenow'] ) {
- $url = set_url_scheme( $url );
- }
-
- /**
- * Filter the attachment URL.
- *
- * @since 2.1.0
- *
- * @param string $url URL for the given attachment.
- * @param int $post_id Attachment ID.
- */
- $url = apply_filters( 'wp_get_attachment_url', $url, $post->ID );
-
- if ( empty( $url ) )
- return false;
-
- return $url;
-}
-
-/**
- * Retrieve thumbnail for an attachment.
- *
- * @since 2.1.0
- *
- * @param int $post_id Optional. Attachment ID. Default 0.
- * @return string|false False on failure. Thumbnail file path on success.
- */
-function wp_get_attachment_thumb_file( $post_id = 0 ) {
- $post_id = (int) $post_id;
- if ( !$post = get_post( $post_id ) )
- return false;
- if ( !is_array( $imagedata = wp_get_attachment_metadata( $post->ID ) ) )
- return false;
-
- $file = get_attached_file( $post->ID );
-
- if ( !empty($imagedata['thumb']) && ($thumbfile = str_replace(basename($file), $imagedata['thumb'], $file)) && file_exists($thumbfile) ) {
- /**
- * Filter the attachment thumbnail file path.
- *
- * @since 2.1.0
- *
- * @param string $thumbfile File path to the attachment thumbnail.
- * @param int $post_id Attachment ID.
- */
- return apply_filters( 'wp_get_attachment_thumb_file', $thumbfile, $post->ID );
- }
- return false;
-}
-
-/**
- * Retrieve URL for an attachment thumbnail.
- *
- * @since 2.1.0
- *
- * @param int $post_id Optional. Attachment ID. Default 0.
- * @return string|false False on failure. Thumbnail URL on success.
- */
-function wp_get_attachment_thumb_url( $post_id = 0 ) {
- $post_id = (int) $post_id;
- if ( !$post = get_post( $post_id ) )
- return false;
- if ( !$url = wp_get_attachment_url( $post->ID ) )
- return false;
-
- $sized = image_downsize( $post_id, 'thumbnail' );
- if ( $sized )
- return $sized[0];
-
- if ( !$thumb = wp_get_attachment_thumb_file( $post->ID ) )
- return false;
-
- $url = str_replace(basename($url), basename($thumb), $url);
-
- /**
- * Filter the attachment thumbnail URL.
- *
- * @since 2.1.0
- *
- * @param string $url URL for the attachment thumbnail.
- * @param int $post_id Attachment ID.
- */
- return apply_filters( 'wp_get_attachment_thumb_url', $url, $post->ID );
-}
-
-/**
- * Verifies an attachment is of a given type.
- *
- * @since 4.2.0
- *
- * @param string $type Attachment type. Accepts 'image', 'audio', or 'video'.
- * @param int|WP_Post $post_id Optional. Attachment ID. Default 0.
- * @return bool True if one of the accepted types, false otherwise.
- */
-function wp_attachment_is( $type, $post_id = 0 ) {
- if ( ! $post = get_post( $post_id ) ) {
- return false;
- }
-
- if ( ! $file = get_attached_file( $post->ID ) ) {
- return false;
- }
-
- if ( 0 === strpos( $post->post_mime_type, $type . '/' ) ) {
- return true;
- }
-
- $check = wp_check_filetype( $file );
- if ( empty( $check['ext'] ) ) {
- return false;
- }
-
- $ext = $check['ext'];
-
- if ( 'import' !== $post->post_mime_type ) {
- return $type === $ext;
- }
-
- switch ( $type ) {
- case 'image':
- $image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png' );
- return in_array( $ext, $image_exts );
-
- case 'audio':
- return in_array( $ext, wp_get_audio_extensions() );
-
- case 'video':
- return in_array( $ext, wp_get_video_extensions() );
-
- default:
- return $type === $ext;
- }
-}
-
-/**
- * Checks if the attachment is an image.
- *
- * @since 2.1.0
- * @since 4.2.0 Modified into wrapper for wp_attachment_is() and
- * allowed WP_Post object to be passed.
- *
- * @param int|WP_Post $post Optional. Attachment ID. Default 0.
- * @return bool Whether the attachment is an image.
- */
-function wp_attachment_is_image( $post = 0 ) {
- return wp_attachment_is( 'image', $post );
-}
-
-/**
- * Retrieve the icon for a MIME type.
- *
- * @since 2.1.0
- *
- * @param string|int $mime MIME type or attachment ID.
- * @return string|false Icon, false otherwise.
- */
-function wp_mime_type_icon( $mime = 0 ) {
- if ( !is_numeric($mime) )
- $icon = wp_cache_get("mime_type_icon_$mime");
-
- $post_id = 0;
- if ( empty($icon) ) {
- $post_mimes = array();
- if ( is_numeric($mime) ) {
- $mime = (int) $mime;
- if ( $post = get_post( $mime ) ) {
- $post_id = (int) $post->ID;
- $file = get_attached_file( $post_id );
- $ext = preg_replace('/^.+?\.([^.]+)$/', '$1', $file);
- if ( !empty($ext) ) {
- $post_mimes[] = $ext;
- if ( $ext_type = wp_ext2type( $ext ) )
- $post_mimes[] = $ext_type;
- }
- $mime = $post->post_mime_type;
- } else {
- $mime = 0;
- }
- } else {
- $post_mimes[] = $mime;
- }
-
- $icon_files = wp_cache_get('icon_files');
-
- if ( !is_array($icon_files) ) {
- /**
- * Filter the icon directory path.
- *
- * @since 2.0.0
- *
- * @param string $path Icon directory absolute path.
- */
- $icon_dir = apply_filters( 'icon_dir', ABSPATH . WPINC . '/images/media' );
-
- /**
- * Filter the icon directory URI.
- *
- * @since 2.0.0
- *
- * @param string $uri Icon directory URI.
- */
- $icon_dir_uri = apply_filters( 'icon_dir_uri', includes_url( 'images/media' ) );
-
- /**
- * Filter the list of icon directory URIs.
- *
- * @since 2.5.0
- *
- * @param array $uris List of icon directory URIs.
- */
- $dirs = apply_filters( 'icon_dirs', array( $icon_dir => $icon_dir_uri ) );
- $icon_files = array();
- while ( $dirs ) {
- $keys = array_keys( $dirs );
- $dir = array_shift( $keys );
- $uri = array_shift($dirs);
- if ( $dh = opendir($dir) ) {
- while ( false !== $file = readdir($dh) ) {
- $file = basename($file);
- if ( substr($file, 0, 1) == '.' )
- continue;
- if ( !in_array(strtolower(substr($file, -4)), array('.png', '.gif', '.jpg') ) ) {
- if ( is_dir("$dir/$file") )
- $dirs["$dir/$file"] = "$uri/$file";
- continue;
- }
- $icon_files["$dir/$file"] = "$uri/$file";
- }
- closedir($dh);
- }
- }
- wp_cache_add( 'icon_files', $icon_files, 'default', 600 );
- }
-
- $types = array();
- // Icon basename - extension = MIME wildcard.
- foreach ( $icon_files as $file => $uri )
- $types[ preg_replace('/^([^.]*).*$/', '$1', basename($file)) ] =& $icon_files[$file];
-
- if ( ! empty($mime) ) {
- $post_mimes[] = substr($mime, 0, strpos($mime, '/'));
- $post_mimes[] = substr($mime, strpos($mime, '/') + 1);
- $post_mimes[] = str_replace('/', '_', $mime);
- }
-
- $matches = wp_match_mime_types(array_keys($types), $post_mimes);
- $matches['default'] = array('default');
-
- foreach ( $matches as $match => $wilds ) {
- foreach ( $wilds as $wild ) {
- if ( ! isset( $types[ $wild ] ) ) {
- continue;
- }
-
- $icon = $types[ $wild ];
- if ( ! is_numeric( $mime ) ) {
- wp_cache_add( "mime_type_icon_$mime", $icon );
- }
- break 2;
- }
- }
- }
-
- /**
- * Filter the mime type icon.
- *
- * @since 2.1.0
- *
- * @param string $icon Path to the mime type icon.
- * @param string $mime Mime type.
- * @param int $post_id Attachment ID. Will equal 0 if the function passed
- * the mime type.
- */
- return apply_filters( 'wp_mime_type_icon', $icon, $mime, $post_id );
-}
-
-/**
- * Check for changed slugs for published post objects and save the old slug.
- *
- * The function is used when a post object of any type is updated,
- * by comparing the current and previous post objects.
- *
- * If the slug was changed and not already part of the old slugs then it will be
- * added to the post meta field ('_wp_old_slug') for storing old slugs for that
- * post.
- *
- * The most logically usage of this function is redirecting changed post objects, so
- * that those that linked to an changed post will be redirected to the new post.
- *
- * @since 2.1.0
- *
- * @param int $post_id Post ID.
- * @param WP_Post $post The Post Object
- * @param WP_Post $post_before The Previous Post Object
- */
-function wp_check_for_changed_slugs( $post_id, $post, $post_before ) {
- // Don't bother if it hasn't changed.
- if ( $post->post_name == $post_before->post_name ) {
- return;
- }
-
- // We're only concerned with published, non-hierarchical objects.
- if ( ! ( 'publish' === $post->post_status || ( 'attachment' === get_post_type( $post ) && 'inherit' === $post->post_status ) ) || is_post_type_hierarchical( $post->post_type ) ) {
- return;
- }
-
- $old_slugs = (array) get_post_meta( $post_id, '_wp_old_slug' );
-
- // If we haven't added this old slug before, add it now.
- if ( ! empty( $post_before->post_name ) && ! in_array( $post_before->post_name, $old_slugs ) ) {
- add_post_meta( $post_id, '_wp_old_slug', $post_before->post_name );
- }
-
- // If the new slug was used previously, delete it from the list.
- if ( in_array( $post->post_name, $old_slugs ) ) {
- delete_post_meta( $post_id, '_wp_old_slug', $post->post_name );
- }
-}
-
-/**
- * Retrieve the private post SQL based on capability.
- *
- * This function provides a standardized way to appropriately select on the
- * post_status of a post type. The function will return a piece of SQL code
- * that can be added to a WHERE clause; this SQL is constructed to allow all
- * published posts, and all private posts to which the user has access.
- *
- * @since 2.2.0
- * @since 4.3.0 Added the ability to pass an array to `$post_type`.
- *
- * @param string|array $post_type Single post type or an array of post types. Currently only supports 'post' or 'page'.
- * @return string SQL code that can be added to a where clause.
- */
-function get_private_posts_cap_sql( $post_type ) {
- return get_posts_by_author_sql( $post_type, false );
-}
-
-/**
- * Retrieve the post SQL based on capability, author, and type.
- *
- * @since 3.0.0
- * @since 4.3.0 Introduced the ability to pass an array of post types to `$post_type`.
- *
- * @see get_private_posts_cap_sql()
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array|string $post_type Single post type or an array of post types.
- * @param bool $full Optional. Returns a full WHERE statement instead of just
- * an 'andalso' term. Default true.
- * @param int $post_author Optional. Query posts having a single author ID. Default null.
- * @param bool $public_only Optional. Only return public posts. Skips cap checks for
- * $current_user. Default false.
- * @return string SQL WHERE code that can be added to a query.
- */
-function get_posts_by_author_sql( $post_type, $full = true, $post_author = null, $public_only = false ) {
- global $wpdb;
-
- if ( is_array( $post_type ) ) {
- $post_types = $post_type;
- } else {
- $post_types = array( $post_type );
- }
-
- $post_type_clauses = array();
- foreach ( $post_types as $post_type ) {
- $post_type_obj = get_post_type_object( $post_type );
- if ( ! $post_type_obj ) {
- continue;
- }
-
- /**
- * Filter the capability to read private posts for a custom post type
- * when generating SQL for getting posts by author.
- *
- * @since 2.2.0
- * @deprecated 3.2.0 The hook transitioned from "somewhat useless" to "totally useless".
- *
- * @param string $cap Capability.
- */
- if ( ! $cap = apply_filters( 'pub_priv_sql_capability', '' ) ) {
- $cap = current_user_can( $post_type_obj->cap->read_private_posts );
- }
-
- // Only need to check the cap if $public_only is false.
- $post_status_sql = "post_status = 'publish'";
- if ( false === $public_only ) {
- if ( $cap ) {
- // Does the user have the capability to view private posts? Guess so.
- $post_status_sql .= " OR post_status = 'private'";
- } elseif ( is_user_logged_in() ) {
- // Users can view their own private posts.
- $id = get_current_user_id();
- if ( null === $post_author || ! $full ) {
- $post_status_sql .= " OR post_status = 'private' AND post_author = $id";
- } elseif ( $id == (int) $post_author ) {
- $post_status_sql .= " OR post_status = 'private'";
- } // else none
- } // else none
- }
-
- $post_type_clauses[] = "( post_type = '" . $post_type . "' AND ( $post_status_sql ) )";
- }
-
- if ( empty( $post_type_clauses ) ) {
- return $full ? 'WHERE 1 = 0' : '1 = 0';
- }
-
- $sql = '( '. implode( ' OR ', $post_type_clauses ) . ' )';
-
- if ( null !== $post_author ) {
- $sql .= $wpdb->prepare( ' AND post_author = %d', $post_author );
- }
-
- if ( $full ) {
- $sql = 'WHERE ' . $sql;
- }
-
- return $sql;
-}
-
-/**
- * Retrieve the date that the last post was published.
- *
- * The server timezone is the default and is the difference between GMT and
- * server time. The 'blog' value is the date when the last post was posted. The
- * 'gmt' is when the last post was posted in GMT formatted date.
- *
- * @since 0.71
- * @since 4.4.0 The `$post_type` argument was added.
- *
- * @param string $timezone Optional. The timezone for the timestamp. Accepts 'server', 'blog', or 'gmt'.
- * 'server' uses the server's internal timezone.
- * 'blog' uses the `post_modified` field, which proxies to the timezone set for the site.
- * 'gmt' uses the `post_modified_gmt` field.
- * Default 'server'.
- * @param string $post_type Optional. The post type to check. Default 'any'.
- * @return string The date of the last post.
- */
-function get_lastpostdate( $timezone = 'server', $post_type = 'any' ) {
- /**
- * Filter the date the last post was published.
- *
- * @since 2.3.0
- *
- * @param string $date Date the last post was published.
- * @param string $timezone Location to use for getting the post published date.
- * See {@see get_lastpostdate()} for accepted `$timezone` values.
- */
- return apply_filters( 'get_lastpostdate', _get_last_post_time( $timezone, 'date', $post_type ), $timezone );
-}
-
-/**
- * Get the timestamp of the last time any post was modified.
- *
- * The server timezone is the default and is the difference between GMT and
- * server time. The 'blog' value is just when the last post was modified. The
- * 'gmt' is when the last post was modified in GMT time.
- *
- * @since 1.2.0
- * @since 4.4.0 The `$post_type` argument was added.
- *
- * @param string $timezone Optional. The timezone for the timestamp. See {@see get_lastpostdate()}
- * for information on accepted values.
- * Default 'server'.
- * @param string $post_type Optional. The post type to check. Default 'any'.
- * @return string The timestamp.
- */
-function get_lastpostmodified( $timezone = 'server', $post_type = 'any' ) {
- /**
- * Pre-filter the return value of get_lastpostmodified() before the query is run.
- *
- * @since 4.4.0
- *
- * @param string $lastpostmodified Date the last post was modified.
- * Returning anything other than false will short-circuit the function.
- * @param string $timezone Location to use for getting the post modified date.
- * See {@see get_lastpostdate()} for accepted `$timezone` values.
- * @param string $post_type The post type to check.
- */
- $lastpostmodified = apply_filters( 'pre_get_lastpostmodified', false, $timezone, $post_type );
- if ( false !== $lastpostmodified ) {
- return $lastpostmodified;
- }
-
- $lastpostmodified = _get_last_post_time( $timezone, 'modified', $post_type );
-
- $lastpostdate = get_lastpostdate($timezone);
- if ( $lastpostdate > $lastpostmodified ) {
- $lastpostmodified = $lastpostdate;
- }
-
- /**
- * Filter the date the last post was modified.
- *
- * @since 2.3.0
- *
- * @param string $lastpostmodified Date the last post was modified.
- * @param string $timezone Location to use for getting the post modified date.
- * See {@see get_lastpostdate()} for accepted `$timezone` values.
- */
- return apply_filters( 'get_lastpostmodified', $lastpostmodified, $timezone );
-}
-
-/**
- * Get the timestamp of the last time any post was modified or published.
- *
- * @since 3.1.0
- * @since 4.4.0 The `$post_type` argument was added.
- * @access private
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $timezone The timezone for the timestamp. See get_lastpostdate().
- * for information on accepted values.
- * @param string $field Post field to check. Accepts 'date' or 'modified'.
- * @param string $post_type Optional. The post type to check. Default 'any'.
- * @return string|false The timestamp.
- */
-function _get_last_post_time( $timezone, $field, $post_type = 'any' ) {
- global $wpdb;
-
- if ( ! in_array( $field, array( 'date', 'modified' ) ) ) {
- return false;
- }
-
- $timezone = strtolower( $timezone );
-
- $key = "lastpost{$field}:$timezone";
- if ( 'any' !== $post_type ) {
- $key .= ':' . sanitize_key( $post_type );
- }
-
- $date = wp_cache_get( $key, 'timeinfo' );
-
- if ( ! $date ) {
- if ( 'any' === $post_type ) {
- $post_types = get_post_types( array( 'public' => true ) );
- array_walk( $post_types, array( $wpdb, 'escape_by_ref' ) );
- $post_types = "'" . implode( "', '", $post_types ) . "'";
- } else {
- $post_types = "'" . sanitize_key( $post_type ) . "'";
- }
-
- switch ( $timezone ) {
- case 'gmt':
- $date = $wpdb->get_var("SELECT post_{$field}_gmt FROM $wpdb->posts WHERE post_status = 'publish' AND post_type IN ({$post_types}) ORDER BY post_{$field}_gmt DESC LIMIT 1");
- break;
- case 'blog':
- $date = $wpdb->get_var("SELECT post_{$field} FROM $wpdb->posts WHERE post_status = 'publish' AND post_type IN ({$post_types}) ORDER BY post_{$field}_gmt DESC LIMIT 1");
- break;
- case 'server':
- $add_seconds_server = date( 'Z' );
- $date = $wpdb->get_var("SELECT DATE_ADD(post_{$field}_gmt, INTERVAL '$add_seconds_server' SECOND) FROM $wpdb->posts WHERE post_status = 'publish' AND post_type IN ({$post_types}) ORDER BY post_{$field}_gmt DESC LIMIT 1");
- break;
- }
-
- if ( $date ) {
- wp_cache_set( $key, $date, 'timeinfo' );
- }
- }
-
- return $date;
-}
-
-/**
- * Updates posts in cache.
- *
- * @since 1.5.1
- *
- * @param array $posts Array of post objects, passed by reference.
- */
-function update_post_cache( &$posts ) {
- if ( ! $posts )
- return;
-
- foreach ( $posts as $post )
- wp_cache_add( $post->ID, $post, 'posts' );
-}
-
-/**
- * Will clean the post in the cache.
- *
- * Cleaning means delete from the cache of the post. Will call to clean the term
- * object cache associated with the post ID.
- *
- * This function not run if $_wp_suspend_cache_invalidation is not empty. See
- * wp_suspend_cache_invalidation().
- *
- * @since 2.0.0
- *
- * @global bool $_wp_suspend_cache_invalidation
- *
- * @param int|WP_Post $post Post ID or post object to remove from the cache.
- */
-function clean_post_cache( $post ) {
- global $_wp_suspend_cache_invalidation;
-
- if ( ! empty( $_wp_suspend_cache_invalidation ) )
- return;
-
- $post = get_post( $post );
- if ( empty( $post ) )
- return;
-
- wp_cache_delete( $post->ID, 'posts' );
- wp_cache_delete( $post->ID, 'post_meta' );
-
- clean_object_term_cache( $post->ID, $post->post_type );
-
- wp_cache_delete( 'wp_get_archives', 'general' );
-
- /**
- * Fires immediately after the given post's cache is cleaned.
- *
- * @since 2.5.0
- *
- * @param int $post_id Post ID.
- * @param WP_Post $post Post object.
- */
- do_action( 'clean_post_cache', $post->ID, $post );
-
- if ( 'page' == $post->post_type ) {
- wp_cache_delete( 'all_page_ids', 'posts' );
-
- /**
- * Fires immediately after the given page's cache is cleaned.
- *
- * @since 2.5.0
- *
- * @param int $post_id Post ID.
- */
- do_action( 'clean_page_cache', $post->ID );
- }
-
- wp_cache_set( 'last_changed', microtime(), 'posts' );
-}
-
-/**
- * Call major cache updating functions for list of Post objects.
- *
- * @since 1.5.0
- *
- * @param array $posts Array of Post objects
- * @param string $post_type Optional. Post type. Default 'post'.
- * @param bool $update_term_cache Optional. Whether to update the term cache. Default true.
- * @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
- */
-function update_post_caches( &$posts, $post_type = 'post', $update_term_cache = true, $update_meta_cache = true ) {
- // No point in doing all this work if we didn't match any posts.
- if ( !$posts )
- return;
-
- update_post_cache($posts);
-
- $post_ids = array();
- foreach ( $posts as $post )
- $post_ids[] = $post->ID;
-
- if ( ! $post_type )
- $post_type = 'any';
-
- if ( $update_term_cache ) {
- if ( is_array($post_type) ) {
- $ptypes = $post_type;
- } elseif ( 'any' == $post_type ) {
- $ptypes = array();
- // Just use the post_types in the supplied posts.
- foreach ( $posts as $post ) {
- $ptypes[] = $post->post_type;
- }
- $ptypes = array_unique($ptypes);
- } else {
- $ptypes = array($post_type);
- }
-
- if ( ! empty($ptypes) )
- update_object_term_cache($post_ids, $ptypes);
- }
-
- if ( $update_meta_cache )
- update_postmeta_cache($post_ids);
-}
-
-/**
- * Updates metadata cache for list of post IDs.
- *
- * Performs SQL query to retrieve the metadata for the post IDs and updates the
- * metadata cache for the posts. Therefore, the functions, which call this
- * function, do not need to perform SQL queries on their own.
- *
- * @since 2.1.0
- *
- * @param array $post_ids List of post IDs.
- * @return array|false Returns false if there is nothing to update or an array
- * of metadata.
- */
-function update_postmeta_cache( $post_ids ) {
- return update_meta_cache('post', $post_ids);
-}
-
-/**
- * Will clean the attachment in the cache.
- *
- * Cleaning means delete from the cache. Optionally will clean the term
- * object cache associated with the attachment ID.
- *
- * This function will not run if $_wp_suspend_cache_invalidation is not empty.
- *
- * @since 3.0.0
- *
- * @global bool $_wp_suspend_cache_invalidation
- *
- * @param int $id The attachment ID in the cache to clean.
- * @param bool $clean_terms Optional. Whether to clean terms cache. Default false.
- */
-function clean_attachment_cache( $id, $clean_terms = false ) {
- global $_wp_suspend_cache_invalidation;
-
- if ( !empty($_wp_suspend_cache_invalidation) )
- return;
-
- $id = (int) $id;
-
- wp_cache_delete($id, 'posts');
- wp_cache_delete($id, 'post_meta');
-
- if ( $clean_terms )
- clean_object_term_cache($id, 'attachment');
-
- /**
- * Fires after the given attachment's cache is cleaned.
- *
- * @since 3.0.0
- *
- * @param int $id Attachment ID.
- */
- do_action( 'clean_attachment_cache', $id );
-}
-
-//
-// Hooks
-//
-
-/**
- * Hook for managing future post transitions to published.
- *
- * @since 2.3.0
- * @access private
- *
- * @see wp_clear_scheduled_hook()
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $new_status New post status.
- * @param string $old_status Previous post status.
- * @param WP_Post $post Post object.
- */
-function _transition_post_status( $new_status, $old_status, $post ) {
- global $wpdb;
-
- if ( $old_status != 'publish' && $new_status == 'publish' ) {
- // Reset GUID if transitioning to publish and it is empty.
- if ( '' == get_the_guid($post->ID) )
- $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post->ID ) ), array( 'ID' => $post->ID ) );
-
- /**
- * Fires when a post's status is transitioned from private to published.
- *
- * @since 1.5.0
- * @deprecated 2.3.0 Use 'private_to_publish' instead.
- *
- * @param int $post_id Post ID.
- */
- do_action('private_to_published', $post->ID);
- }
-
- // If published posts changed clear the lastpostmodified cache.
- if ( 'publish' == $new_status || 'publish' == $old_status) {
- foreach ( array( 'server', 'gmt', 'blog' ) as $timezone ) {
- wp_cache_delete( "lastpostmodified:$timezone", 'timeinfo' );
- wp_cache_delete( "lastpostdate:$timezone", 'timeinfo' );
- wp_cache_delete( "lastpostdate:$timezone:{$post->post_type}", 'timeinfo' );
- }
- }
-
- if ( $new_status !== $old_status ) {
- wp_cache_delete( _count_posts_cache_key( $post->post_type ), 'counts' );
- wp_cache_delete( _count_posts_cache_key( $post->post_type, 'readable' ), 'counts' );
- }
-
- // Always clears the hook in case the post status bounced from future to draft.
- wp_clear_scheduled_hook('publish_future_post', array( $post->ID ) );
-}
-
-/**
- * Hook used to schedule publication for a post marked for the future.
- *
- * The $post properties used and must exist are 'ID' and 'post_date_gmt'.
- *
- * @since 2.3.0
- * @access private
- *
- * @param int $deprecated Not used. Can be set to null. Never implemented. Not marked
- * as deprecated with _deprecated_argument() as it conflicts with
- * wp_transition_post_status() and the default filter for
- * {@see _future_post_hook()}.
- * @param WP_Post $post Post object.
- */
-function _future_post_hook( $deprecated, $post ) {
- wp_clear_scheduled_hook( 'publish_future_post', array( $post->ID ) );
- wp_schedule_single_event( strtotime( get_gmt_from_date( $post->post_date ) . ' GMT') , 'publish_future_post', array( $post->ID ) );
-}
-
-/**
- * Hook to schedule pings and enclosures when a post is published.
- *
- * Uses XMLRPC_REQUEST and WP_IMPORTING constants.
- *
- * @since 2.3.0
- * @access private
- *
- * @param int $post_id The ID in the database table of the post being published.
- */
-function _publish_post_hook( $post_id ) {
- if ( defined( 'XMLRPC_REQUEST' ) ) {
- /**
- * Fires when _publish_post_hook() is called during an XML-RPC request.
- *
- * @since 2.1.0
- *
- * @param int $post_id Post ID.
- */
- do_action( 'xmlrpc_publish_post', $post_id );
- }
-
- if ( defined('WP_IMPORTING') )
- return;
-
- if ( get_option('default_pingback_flag') )
- add_post_meta( $post_id, '_pingme', '1' );
- add_post_meta( $post_id, '_encloseme', '1' );
-
- wp_schedule_single_event(time(), 'do_pings');
-}
-
-/**
- * Return the post's parent's post_ID
- *
- * @since 3.1.0
- *
- * @param int $post_ID
- *
- * @return int|false Post parent ID, otherwise false.
- */
-function wp_get_post_parent_id( $post_ID ) {
- $post = get_post( $post_ID );
- if ( !$post || is_wp_error( $post ) )
- return false;
- return (int) $post->post_parent;
-}
-
-/**
- * Check the given subset of the post hierarchy for hierarchy loops.
- *
- * Prevents loops from forming and breaks those that it finds. Attached
- * to the 'wp_insert_post_parent' filter.
- *
- * @since 3.1.0
- *
- * @see wp_find_hierarchy_loop()
- *
- * @param int $post_parent ID of the parent for the post we're checking.
- * @param int $post_ID ID of the post we're checking.
- * @return int The new post_parent for the post, 0 otherwise.
- */
-function wp_check_post_hierarchy_for_loops( $post_parent, $post_ID ) {
- // Nothing fancy here - bail.
- if ( !$post_parent )
- return 0;
-
- // New post can't cause a loop.
- if ( empty( $post_ID ) )
- return $post_parent;
-
- // Can't be its own parent.
- if ( $post_parent == $post_ID )
- return 0;
-
- // Now look for larger loops.
- if ( !$loop = wp_find_hierarchy_loop( 'wp_get_post_parent_id', $post_ID, $post_parent ) )
- return $post_parent; // No loop
-
- // Setting $post_parent to the given value causes a loop.
- if ( isset( $loop[$post_ID] ) )
- return 0;
-
- // There's a loop, but it doesn't contain $post_ID. Break the loop.
- foreach ( array_keys( $loop ) as $loop_member )
- wp_update_post( array( 'ID' => $loop_member, 'post_parent' => 0 ) );
-
- return $post_parent;
-}
-
-/**
- * Set a post thumbnail.
- *
- * @since 3.1.0
- *
- * @param int|WP_Post $post Post ID or post object where thumbnail should be attached.
- * @param int $thumbnail_id Thumbnail to attach.
- * @return int|bool True on success, false on failure.
- */
-function set_post_thumbnail( $post, $thumbnail_id ) {
- $post = get_post( $post );
- $thumbnail_id = absint( $thumbnail_id );
- if ( $post && $thumbnail_id && get_post( $thumbnail_id ) ) {
- if ( wp_get_attachment_image( $thumbnail_id, 'thumbnail' ) )
- return update_post_meta( $post->ID, '_thumbnail_id', $thumbnail_id );
- else
- return delete_post_meta( $post->ID, '_thumbnail_id' );
- }
- return false;
-}
-
-/**
- * Remove a post thumbnail.
- *
- * @since 3.3.0
- *
- * @param int|WP_Post $post Post ID or post object where thumbnail should be removed from.
- * @return bool True on success, false on failure.
- */
-function delete_post_thumbnail( $post ) {
- $post = get_post( $post );
- if ( $post )
- return delete_post_meta( $post->ID, '_thumbnail_id' );
- return false;
-}
-
-/**
- * Delete auto-drafts for new posts that are > 7 days old.
- *
- * @since 3.4.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- */
-function wp_delete_auto_drafts() {
- global $wpdb;
-
- // Cleanup old auto-drafts more than 7 days old.
- $old_posts = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_status = 'auto-draft' AND DATE_SUB( NOW(), INTERVAL 7 DAY ) > post_date" );
- foreach ( (array) $old_posts as $delete ) {
- // Force delete.
- wp_delete_post( $delete, true );
- }
-}
-
-/**
- * Update the custom taxonomies' term counts when a post's status is changed.
- *
- * For example, default posts term counts (for custom taxonomies) don't include
- * private / draft posts.
- *
- * @since 3.3.0
- * @access private
- *
- * @param string $new_status New post status.
- * @param string $old_status Old post status.
- * @param WP_Post $post Post object.
- */
-function _update_term_count_on_transition_post_status( $new_status, $old_status, $post ) {
- // Update counts for the post's terms.
- foreach ( (array) get_object_taxonomies( $post->post_type ) as $taxonomy ) {
- $tt_ids = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'tt_ids' ) );
- wp_update_term_count( $tt_ids, $taxonomy );
- }
-}
-
-/**
- * Adds any posts from the given ids to the cache that do not already exist in cache
- *
- * @since 3.4.0
- * @access private
- *
- * @see update_post_caches()
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array $ids ID list.
- * @param bool $update_term_cache Optional. Whether to update the term cache. Default true.
- * @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
- */
-function _prime_post_caches( $ids, $update_term_cache = true, $update_meta_cache = true ) {
- global $wpdb;
-
- $non_cached_ids = _get_non_cached_ids( $ids, 'posts' );
- if ( !empty( $non_cached_ids ) ) {
- $fresh_posts = $wpdb->get_results( sprintf( "SELECT $wpdb->posts.* FROM $wpdb->posts WHERE ID IN (%s)", join( ",", $non_cached_ids ) ) );
-
- update_post_caches( $fresh_posts, 'any', $update_term_cache, $update_meta_cache );
- }
-}
</del></span></pre></div>
<a id="trunksrcwpincludespostphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/post.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/post.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/post.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,20 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Core Post API
- *
- * @package WordPress
- * @subpackage Post
- * @since 1.5.0
- */
-
-/** Core posts functionality */
-require_once( ABSPATH . WPINC . '/post-functions.php' );
-
-/** Walker_Page class */
-require_once( ABSPATH . WPINC . '/class-walker-page.php' );
-
-/** Walker_PageDropdown class */
-require_once( ABSPATH . WPINC . '/class-walker-page-dropdown.php' );
-
-/** WP_Post class */
-require_once( ABSPATH . WPINC . '/class-wp-post.php' );
</del></span></pre></div>
<a id="trunksrcwpincludespostphpfromrev35712trunksrcwpincludespostfunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/post.php (from rev 35712, trunk/src/wp-includes/post-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/post.php (rev 0)
+++ trunk/src/wp-includes/post.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,5890 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Core Post API
+ *
+ * @package WordPress
+ * @subpackage Post
+ */
+
+//
+// Post Type Registration
+//
+
+/**
+ * Creates the initial post types when 'init' action is fired.
+ *
+ * @since 2.9.0
+ */
+function create_initial_post_types() {
+ register_post_type( 'post', array(
+ 'labels' => array(
+ 'name_admin_bar' => _x( 'Post', 'add new on admin bar' ),
+ ),
+ 'public' => true,
+ '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
+ '_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */
+ 'capability_type' => 'post',
+ 'map_meta_cap' => true,
+ 'menu_position' => 5,
+ 'hierarchical' => false,
+ 'rewrite' => false,
+ 'query_var' => false,
+ 'delete_with_user' => true,
+ 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions', 'post-formats' ),
+ ) );
+
+ register_post_type( 'page', array(
+ 'labels' => array(
+ 'name_admin_bar' => _x( 'Page', 'add new on admin bar' ),
+ ),
+ 'public' => true,
+ 'publicly_queryable' => false,
+ '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
+ '_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */
+ 'capability_type' => 'page',
+ 'map_meta_cap' => true,
+ 'menu_position' => 20,
+ 'hierarchical' => true,
+ 'rewrite' => false,
+ 'query_var' => false,
+ 'delete_with_user' => true,
+ 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'page-attributes', 'custom-fields', 'comments', 'revisions' ),
+ ) );
+
+ register_post_type( 'attachment', array(
+ 'labels' => array(
+ 'name' => _x('Media', 'post type general name'),
+ 'name_admin_bar' => _x( 'Media', 'add new from admin bar' ),
+ 'add_new' => _x( 'Add New', 'add new media' ),
+ 'edit_item' => __( 'Edit Media' ),
+ 'view_item' => __( 'View Attachment Page' ),
+ ),
+ 'public' => true,
+ 'show_ui' => true,
+ '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
+ '_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */
+ 'capability_type' => 'post',
+ 'capabilities' => array(
+ 'create_posts' => 'upload_files',
+ ),
+ 'map_meta_cap' => true,
+ 'hierarchical' => false,
+ 'rewrite' => false,
+ 'query_var' => false,
+ 'show_in_nav_menus' => false,
+ 'delete_with_user' => true,
+ 'supports' => array( 'title', 'author', 'comments' ),
+ ) );
+ add_post_type_support( 'attachment:audio', 'thumbnail' );
+ add_post_type_support( 'attachment:video', 'thumbnail' );
+
+ register_post_type( 'revision', array(
+ 'labels' => array(
+ 'name' => __( 'Revisions' ),
+ 'singular_name' => __( 'Revision' ),
+ ),
+ 'public' => false,
+ '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
+ '_edit_link' => 'revision.php?revision=%d', /* internal use only. don't use this when registering your own post type. */
+ 'capability_type' => 'post',
+ 'map_meta_cap' => true,
+ 'hierarchical' => false,
+ 'rewrite' => false,
+ 'query_var' => false,
+ 'can_export' => false,
+ 'delete_with_user' => true,
+ 'supports' => array( 'author' ),
+ ) );
+
+ register_post_type( 'nav_menu_item', array(
+ 'labels' => array(
+ 'name' => __( 'Navigation Menu Items' ),
+ 'singular_name' => __( 'Navigation Menu Item' ),
+ ),
+ 'public' => false,
+ '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
+ 'hierarchical' => false,
+ 'rewrite' => false,
+ 'delete_with_user' => false,
+ 'query_var' => false,
+ ) );
+
+ register_post_status( 'publish', array(
+ 'label' => _x( 'Published', 'post' ),
+ 'public' => true,
+ '_builtin' => true, /* internal use only. */
+ 'label_count' => _n_noop( 'Published <span class="count">(%s)</span>', 'Published <span class="count">(%s)</span>' ),
+ ) );
+
+ register_post_status( 'future', array(
+ 'label' => _x( 'Scheduled', 'post' ),
+ 'protected' => true,
+ '_builtin' => true, /* internal use only. */
+ 'label_count' => _n_noop('Scheduled <span class="count">(%s)</span>', 'Scheduled <span class="count">(%s)</span>' ),
+ ) );
+
+ register_post_status( 'draft', array(
+ 'label' => _x( 'Draft', 'post' ),
+ 'protected' => true,
+ '_builtin' => true, /* internal use only. */
+ 'label_count' => _n_noop( 'Draft <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>' ),
+ ) );
+
+ register_post_status( 'pending', array(
+ 'label' => _x( 'Pending', 'post' ),
+ 'protected' => true,
+ '_builtin' => true, /* internal use only. */
+ 'label_count' => _n_noop( 'Pending <span class="count">(%s)</span>', 'Pending <span class="count">(%s)</span>' ),
+ ) );
+
+ register_post_status( 'private', array(
+ 'label' => _x( 'Private', 'post' ),
+ 'private' => true,
+ '_builtin' => true, /* internal use only. */
+ 'label_count' => _n_noop( 'Private <span class="count">(%s)</span>', 'Private <span class="count">(%s)</span>' ),
+ ) );
+
+ register_post_status( 'trash', array(
+ 'label' => _x( 'Trash', 'post' ),
+ 'internal' => true,
+ '_builtin' => true, /* internal use only. */
+ 'label_count' => _n_noop( 'Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>' ),
+ 'show_in_admin_status_list' => true,
+ ) );
+
+ register_post_status( 'auto-draft', array(
+ 'label' => 'auto-draft',
+ 'internal' => true,
+ '_builtin' => true, /* internal use only. */
+ ) );
+
+ register_post_status( 'inherit', array(
+ 'label' => 'inherit',
+ 'internal' => true,
+ '_builtin' => true, /* internal use only. */
+ 'exclude_from_search' => false,
+ ) );
+}
+
+/**
+ * Retrieve attached file path based on attachment ID.
+ *
+ * By default the path will go through the 'get_attached_file' filter, but
+ * passing a true to the $unfiltered argument of get_attached_file() will
+ * return the file path unfiltered.
+ *
+ * The function works by getting the single post meta name, named
+ * '_wp_attached_file' and returning it. This is a convenience function to
+ * prevent looking up the meta name and provide a mechanism for sending the
+ * attached filename through a filter.
+ *
+ * @since 2.0.0
+ *
+ * @param int $attachment_id Attachment ID.
+ * @param bool $unfiltered Optional. Whether to apply filters. Default false.
+ * @return string|false The file path to where the attached file should be, false otherwise.
+ */
+function get_attached_file( $attachment_id, $unfiltered = false ) {
+ $file = get_post_meta( $attachment_id, '_wp_attached_file', true );
+ // If the file is relative, prepend upload dir.
+ if ( $file && 0 !== strpos($file, '/') && !preg_match('|^.:\\\|', $file) && ( ($uploads = wp_upload_dir()) && false === $uploads['error'] ) )
+ $file = $uploads['basedir'] . "/$file";
+ if ( $unfiltered )
+ return $file;
+
+ /**
+ * Filter the attached file based on the given ID.
+ *
+ * @since 2.1.0
+ *
+ * @param string $file Path to attached file.
+ * @param int $attachment_id Attachment ID.
+ */
+ return apply_filters( 'get_attached_file', $file, $attachment_id );
+}
+
+/**
+ * Update attachment file path based on attachment ID.
+ *
+ * Used to update the file path of the attachment, which uses post meta name
+ * '_wp_attached_file' to store the path of the attachment.
+ *
+ * @since 2.1.0
+ *
+ * @param int $attachment_id Attachment ID.
+ * @param string $file File path for the attachment.
+ * @return bool True on success, false on failure.
+ */
+function update_attached_file( $attachment_id, $file ) {
+ if ( !get_post( $attachment_id ) )
+ return false;
+
+ /**
+ * Filter the path to the attached file to update.
+ *
+ * @since 2.1.0
+ *
+ * @param string $file Path to the attached file to update.
+ * @param int $attachment_id Attachment ID.
+ */
+ $file = apply_filters( 'update_attached_file', $file, $attachment_id );
+
+ if ( $file = _wp_relative_upload_path( $file ) )
+ return update_post_meta( $attachment_id, '_wp_attached_file', $file );
+ else
+ return delete_post_meta( $attachment_id, '_wp_attached_file' );
+}
+
+/**
+ * Return relative path to an uploaded file.
+ *
+ * The path is relative to the current upload dir.
+ *
+ * @since 2.9.0
+ *
+ * @param string $path Full path to the file.
+ * @return string Relative path on success, unchanged path on failure.
+ */
+function _wp_relative_upload_path( $path ) {
+ $new_path = $path;
+
+ $uploads = wp_upload_dir();
+ if ( 0 === strpos( $new_path, $uploads['basedir'] ) ) {
+ $new_path = str_replace( $uploads['basedir'], '', $new_path );
+ $new_path = ltrim( $new_path, '/' );
+ }
+
+ /**
+ * Filter the relative path to an uploaded file.
+ *
+ * @since 2.9.0
+ *
+ * @param string $new_path Relative path to the file.
+ * @param string $path Full path to the file.
+ */
+ return apply_filters( '_wp_relative_upload_path', $new_path, $path );
+}
+
+/**
+ * Retrieve all children of the post parent ID.
+ *
+ * Normally, without any enhancements, the children would apply to pages. In the
+ * context of the inner workings of WordPress, pages, posts, and attachments
+ * share the same table, so therefore the functionality could apply to any one
+ * of them. It is then noted that while this function does not work on posts, it
+ * does not mean that it won't work on posts. It is recommended that you know
+ * what context you wish to retrieve the children of.
+ *
+ * Attachments may also be made the child of a post, so if that is an accurate
+ * statement (which needs to be verified), it would then be possible to get
+ * all of the attachments for a post. Attachments have since changed since
+ * version 2.5, so this is most likely inaccurate, but serves generally as an
+ * example of what is possible.
+ *
+ * The arguments listed as defaults are for this function and also of the
+ * {@link get_posts()} function. The arguments are combined with the
+ * get_children defaults and are then passed to the {@link get_posts()}
+ * function, which accepts additional arguments. You can replace the defaults in
+ * this function, listed below and the additional arguments listed in the
+ * {@link get_posts()} function.
+ *
+ * The 'post_parent' is the most important argument and important attention
+ * needs to be paid to the $args parameter. If you pass either an object or an
+ * integer (number), then just the 'post_parent' is grabbed and everything else
+ * is lost. If you don't specify any arguments, then it is assumed that you are
+ * in The Loop and the post parent will be grabbed for from the current post.
+ *
+ * The 'post_parent' argument is the ID to get the children. The 'numberposts'
+ * is the amount of posts to retrieve that has a default of '-1', which is
+ * used to get all of the posts. Giving a number higher than 0 will only
+ * retrieve that amount of posts.
+ *
+ * The 'post_type' and 'post_status' arguments can be used to choose what
+ * criteria of posts to retrieve. The 'post_type' can be anything, but WordPress
+ * post types are 'post', 'pages', and 'attachments'. The 'post_status'
+ * argument will accept any post status within the write administration panels.
+ *
+ * @since 2.0.0
+ *
+ * @see get_posts()
+ * @todo Check validity of description.
+ *
+ * @global WP_Post $post
+ *
+ * @param mixed $args Optional. User defined arguments for replacing the defaults. Default empty.
+ * @param string $output Optional. Constant for return type. Accepts OBJECT, ARRAY_A, ARRAY_N.
+ * Default OBJECT.
+ * @return array Array of children, where the type of each element is determined by $output parameter.
+ * Empty array on failure.
+ */
+function get_children( $args = '', $output = OBJECT ) {
+ $kids = array();
+ if ( empty( $args ) ) {
+ if ( isset( $GLOBALS['post'] ) ) {
+ $args = array('post_parent' => (int) $GLOBALS['post']->post_parent );
+ } else {
+ return $kids;
+ }
+ } elseif ( is_object( $args ) ) {
+ $args = array('post_parent' => (int) $args->post_parent );
+ } elseif ( is_numeric( $args ) ) {
+ $args = array('post_parent' => (int) $args);
+ }
+
+ $defaults = array(
+ 'numberposts' => -1, 'post_type' => 'any',
+ 'post_status' => 'any', 'post_parent' => 0,
+ );
+
+ $r = wp_parse_args( $args, $defaults );
+
+ $children = get_posts( $r );
+
+ if ( ! $children )
+ return $kids;
+
+ if ( ! empty( $r['fields'] ) )
+ return $children;
+
+ update_post_cache($children);
+
+ foreach ( $children as $key => $child )
+ $kids[$child->ID] = $children[$key];
+
+ if ( $output == OBJECT ) {
+ return $kids;
+ } elseif ( $output == ARRAY_A ) {
+ $weeuns = array();
+ foreach ( (array) $kids as $kid ) {
+ $weeuns[$kid->ID] = get_object_vars($kids[$kid->ID]);
+ }
+ return $weeuns;
+ } elseif ( $output == ARRAY_N ) {
+ $babes = array();
+ foreach ( (array) $kids as $kid ) {
+ $babes[$kid->ID] = array_values(get_object_vars($kids[$kid->ID]));
+ }
+ return $babes;
+ } else {
+ return $kids;
+ }
+}
+
+/**
+ * Get extended entry info (<!--more-->).
+ *
+ * There should not be any space after the second dash and before the word
+ * 'more'. There can be text or space(s) after the word 'more', but won't be
+ * referenced.
+ *
+ * The returned array has 'main', 'extended', and 'more_text' keys. Main has the text before
+ * the `<!--more-->`. The 'extended' key has the content after the
+ * `<!--more-->` comment. The 'more_text' key has the custom "Read More" text.
+ *
+ * @since 1.0.0
+ *
+ * @param string $post Post content.
+ * @return array Post before ('main'), after ('extended'), and custom read more ('more_text').
+ */
+function get_extended( $post ) {
+ //Match the new style more links.
+ if ( preg_match('/<!--more(.*?)?-->/', $post, $matches) ) {
+ list($main, $extended) = explode($matches[0], $post, 2);
+ $more_text = $matches[1];
+ } else {
+ $main = $post;
+ $extended = '';
+ $more_text = '';
+ }
+
+ // leading and trailing whitespace.
+ $main = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $main);
+ $extended = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $extended);
+ $more_text = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $more_text);
+
+ return array( 'main' => $main, 'extended' => $extended, 'more_text' => $more_text );
+}
+
+/**
+ * Retrieves post data given a post ID or post object.
+ *
+ * See {@link sanitize_post()} for optional $filter values. Also, the parameter
+ * $post, must be given as a variable, since it is passed by reference.
+ *
+ * @since 1.5.1
+ *
+ * @global WP_Post $post
+ *
+ * @param int|WP_Post|null $post Optional. Post ID or post object. Defaults to global $post.
+ * @param string $output Optional, default is Object. Accepts OBJECT, ARRAY_A, or ARRAY_N.
+ * Default OBJECT.
+ * @param string $filter Optional. Type of filter to apply. Accepts 'raw', 'edit', 'db',
+ * or 'display'. Default 'raw'.
+ * @return WP_Post|array|null Type corresponding to $output on success or null on failure.
+ * When $output is OBJECT, a `WP_Post` instance is returned.
+ */
+function get_post( $post = null, $output = OBJECT, $filter = 'raw' ) {
+ if ( empty( $post ) && isset( $GLOBALS['post'] ) )
+ $post = $GLOBALS['post'];
+
+ if ( $post instanceof WP_Post ) {
+ $_post = $post;
+ } elseif ( is_object( $post ) ) {
+ if ( empty( $post->filter ) ) {
+ $_post = sanitize_post( $post, 'raw' );
+ $_post = new WP_Post( $_post );
+ } elseif ( 'raw' == $post->filter ) {
+ $_post = new WP_Post( $post );
+ } else {
+ $_post = WP_Post::get_instance( $post->ID );
+ }
+ } else {
+ $_post = WP_Post::get_instance( $post );
+ }
+
+ if ( ! $_post )
+ return null;
+
+ $_post = $_post->filter( $filter );
+
+ if ( $output == ARRAY_A )
+ return $_post->to_array();
+ elseif ( $output == ARRAY_N )
+ return array_values( $_post->to_array() );
+
+ return $_post;
+}
+
+/**
+ * Retrieve ancestors of a post.
+ *
+ * @since 2.5.0
+ *
+ * @param int|WP_Post $post Post ID or post object.
+ * @return array Ancestor IDs or empty array if none are found.
+ */
+function get_post_ancestors( $post ) {
+ $post = get_post( $post );
+
+ if ( ! $post || empty( $post->post_parent ) || $post->post_parent == $post->ID )
+ return array();
+
+ $ancestors = array();
+
+ $id = $ancestors[] = $post->post_parent;
+
+ while ( $ancestor = get_post( $id ) ) {
+ // Loop detection: If the ancestor has been seen before, break.
+ if ( empty( $ancestor->post_parent ) || ( $ancestor->post_parent == $post->ID ) || in_array( $ancestor->post_parent, $ancestors ) )
+ break;
+
+ $id = $ancestors[] = $ancestor->post_parent;
+ }
+
+ return $ancestors;
+}
+
+/**
+ * Retrieve data from a post field based on Post ID.
+ *
+ * Examples of the post field will be, 'post_type', 'post_status', 'post_content',
+ * etc and based off of the post object property or key names.
+ *
+ * The context values are based off of the taxonomy filter functions and
+ * supported values are found within those functions.
+ *
+ * @since 2.3.0
+ *
+ * @see sanitize_post_field()
+ *
+ * @param string $field Post field name.
+ * @param int|WP_Post $post Post ID or post object.
+ * @param string $context Optional. How to filter the field. Accepts 'raw', 'edit', 'db',
+ * or 'display'. Default 'display'.
+ * @return string The value of the post field on success, empty string on failure.
+ */
+function get_post_field( $field, $post, $context = 'display' ) {
+ $post = get_post( $post );
+
+ if ( !$post )
+ return '';
+
+ if ( !isset($post->$field) )
+ return '';
+
+ return sanitize_post_field($field, $post->$field, $post->ID, $context);
+}
+
+/**
+ * Retrieve the mime type of an attachment based on the ID.
+ *
+ * This function can be used with any post type, but it makes more sense with
+ * attachments.
+ *
+ * @since 2.0.0
+ *
+ * @param int|WP_Post $ID Optional. Post ID or post object. Default empty.
+ * @return string|false The mime type on success, false on failure.
+ */
+function get_post_mime_type( $ID = '' ) {
+ $post = get_post($ID);
+
+ if ( is_object($post) )
+ return $post->post_mime_type;
+
+ return false;
+}
+
+/**
+ * Retrieve the post status based on the Post ID.
+ *
+ * If the post ID is of an attachment, then the parent post status will be given
+ * instead.
+ *
+ * @since 2.0.0
+ *
+ * @param int|WP_Post $ID Optional. Post ID or post object. Default empty.
+ * @return string|false Post status on success, false on failure.
+ */
+function get_post_status( $ID = '' ) {
+ $post = get_post($ID);
+
+ if ( !is_object($post) )
+ return false;
+
+ if ( 'attachment' == $post->post_type ) {
+ if ( 'private' == $post->post_status )
+ return 'private';
+
+ // Unattached attachments are assumed to be published.
+ if ( ( 'inherit' == $post->post_status ) && ( 0 == $post->post_parent) )
+ return 'publish';
+
+ // Inherit status from the parent.
+ if ( $post->post_parent && ( $post->ID != $post->post_parent ) ) {
+ $parent_post_status = get_post_status( $post->post_parent );
+ if ( 'trash' == $parent_post_status ) {
+ return get_post_meta( $post->post_parent, '_wp_trash_meta_status', true );
+ } else {
+ return $parent_post_status;
+ }
+ }
+
+ }
+
+ /**
+ * Filter the post status.
+ *
+ * @since 4.4.0
+ *
+ * @param string $post_status The post status.
+ * @param WP_Post $post The post object.
+ */
+ return apply_filters( 'get_post_status', $post->post_status, $post );
+}
+
+/**
+ * Retrieve all of the WordPress supported post statuses.
+ *
+ * Posts have a limited set of valid status values, this provides the
+ * post_status values and descriptions.
+ *
+ * @since 2.5.0
+ *
+ * @return array List of post statuses.
+ */
+function get_post_statuses() {
+ $status = array(
+ 'draft' => __( 'Draft' ),
+ 'pending' => __( 'Pending Review' ),
+ 'private' => __( 'Private' ),
+ 'publish' => __( 'Published' )
+ );
+
+ return $status;
+}
+
+/**
+ * Retrieve all of the WordPress support page statuses.
+ *
+ * Pages have a limited set of valid status values, this provides the
+ * post_status values and descriptions.
+ *
+ * @since 2.5.0
+ *
+ * @return array List of page statuses.
+ */
+function get_page_statuses() {
+ $status = array(
+ 'draft' => __( 'Draft' ),
+ 'private' => __( 'Private' ),
+ 'publish' => __( 'Published' )
+ );
+
+ return $status;
+}
+
+/**
+ * Register a post status. Do not use before init.
+ *
+ * A simple function for creating or modifying a post status based on the
+ * parameters given. The function will accept an array (second optional
+ * parameter), along with a string for the post status name.
+ *
+ * Arguments prefixed with an _underscore shouldn't be used by plugins and themes.
+ *
+ * @since 3.0.0
+ * @global array $wp_post_statuses Inserts new post status object into the list
+ *
+ * @param string $post_status Name of the post status.
+ * @param array|string $args {
+ * Optional. Array or string of post status arguments.
+ *
+ * @type bool|string $label A descriptive name for the post status marked
+ * for translation. Defaults to value of $post_status.
+ * @type bool|array $label_count Descriptive text to use for nooped plurals.
+ * Default array of $label, twice
+ * @type bool $exclude_from_search Whether to exclude posts with this post status
+ * from search results. Default is value of $internal.
+ * @type bool $_builtin Whether the status is built-in. Core-use only.
+ * Default false.
+ * @type bool $public Whether posts of this status should be shown
+ * in the front end of the site. Default false.
+ * @type bool $internal Whether the status is for internal use only.
+ * Default false.
+ * @type bool $protected Whether posts with this status should be protected.
+ * Default false.
+ * @type bool $private Whether posts with this status should be private.
+ * Default false.
+ * @type bool $publicly_queryable Whether posts with this status should be publicly-
+ * queryable. Default is value of $public.
+ * @type bool $show_in_admin_all_list Whether to include posts in the edit listing for
+ * their post type. Default is value of $internal.
+ * @type bool $show_in_admin_status_list Show in the list of statuses with post counts at
+ * the top of the edit listings,
+ * e.g. All (12) | Published (9) | My Custom Status (2)
+ * Default is value of $internal.
+ * }
+ * @return object
+ */
+function register_post_status( $post_status, $args = array() ) {
+ global $wp_post_statuses;
+
+ if (!is_array($wp_post_statuses))
+ $wp_post_statuses = array();
+
+ // Args prefixed with an underscore are reserved for internal use.
+ $defaults = array(
+ 'label' => false,
+ 'label_count' => false,
+ 'exclude_from_search' => null,
+ '_builtin' => false,
+ 'public' => null,
+ 'internal' => null,
+ 'protected' => null,
+ 'private' => null,
+ 'publicly_queryable' => null,
+ 'show_in_admin_status_list' => null,
+ 'show_in_admin_all_list' => null,
+ );
+ $args = wp_parse_args($args, $defaults);
+ $args = (object) $args;
+
+ $post_status = sanitize_key($post_status);
+ $args->name = $post_status;
+
+ // Set various defaults.
+ if ( null === $args->public && null === $args->internal && null === $args->protected && null === $args->private )
+ $args->internal = true;
+
+ if ( null === $args->public )
+ $args->public = false;
+
+ if ( null === $args->private )
+ $args->private = false;
+
+ if ( null === $args->protected )
+ $args->protected = false;
+
+ if ( null === $args->internal )
+ $args->internal = false;
+
+ if ( null === $args->publicly_queryable )
+ $args->publicly_queryable = $args->public;
+
+ if ( null === $args->exclude_from_search )
+ $args->exclude_from_search = $args->internal;
+
+ if ( null === $args->show_in_admin_all_list )
+ $args->show_in_admin_all_list = !$args->internal;
+
+ if ( null === $args->show_in_admin_status_list )
+ $args->show_in_admin_status_list = !$args->internal;
+
+ if ( false === $args->label )
+ $args->label = $post_status;
+
+ if ( false === $args->label_count )
+ $args->label_count = array( $args->label, $args->label );
+
+ $wp_post_statuses[$post_status] = $args;
+
+ return $args;
+}
+
+/**
+ * Retrieve a post status object by name.
+ *
+ * @since 3.0.0
+ *
+ * @global array $wp_post_statuses List of post statuses.
+ *
+ * @see register_post_status()
+ *
+ * @param string $post_status The name of a registered post status.
+ * @return object|null A post status object.
+ */
+function get_post_status_object( $post_status ) {
+ global $wp_post_statuses;
+
+ if ( empty($wp_post_statuses[$post_status]) )
+ return null;
+
+ return $wp_post_statuses[$post_status];
+}
+
+/**
+ * Get a list of post statuses.
+ *
+ * @since 3.0.0
+ *
+ * @global array $wp_post_statuses List of post statuses.
+ *
+ * @see register_post_status()
+ *
+ * @param array|string $args Optional. Array or string of post status arguments to compare against
+ * properties of the global `$wp_post_statuses objects`. Default empty array.
+ * @param string $output Optional. The type of output to return, either 'names' or 'objects'. Default 'names'.
+ * @param string $operator Optional. The logical operation to perform. 'or' means only one element
+ * from the array needs to match; 'and' means all elements must match.
+ * Default 'and'.
+ * @return array A list of post status names or objects.
+ */
+function get_post_stati( $args = array(), $output = 'names', $operator = 'and' ) {
+ global $wp_post_statuses;
+
+ $field = ('names' == $output) ? 'name' : false;
+
+ return wp_filter_object_list($wp_post_statuses, $args, $operator, $field);
+}
+
+/**
+ * Whether the post type is hierarchical.
+ *
+ * A false return value might also mean that the post type does not exist.
+ *
+ * @since 3.0.0
+ *
+ * @see get_post_type_object()
+ *
+ * @param string $post_type Post type name
+ * @return bool Whether post type is hierarchical.
+ */
+function is_post_type_hierarchical( $post_type ) {
+ if ( ! post_type_exists( $post_type ) )
+ return false;
+
+ $post_type = get_post_type_object( $post_type );
+ return $post_type->hierarchical;
+}
+
+/**
+ * Check if a post type is registered.
+ *
+ * @since 3.0.0
+ *
+ * @see get_post_type_object()
+ *
+ * @param string $post_type Post type name.
+ * @return bool Whether post type is registered.
+ */
+function post_type_exists( $post_type ) {
+ return (bool) get_post_type_object( $post_type );
+}
+
+/**
+ * Retrieve the post type of the current post or of a given post.
+ *
+ * @since 2.1.0
+ *
+ * @param int|WP_Post|null $post Optional. Post ID or post object. Default is global $post.
+ * @return string|false Post type on success, false on failure.
+ */
+function get_post_type( $post = null ) {
+ if ( $post = get_post( $post ) )
+ return $post->post_type;
+
+ return false;
+}
+
+/**
+ * Retrieve a post type object by name.
+ *
+ * @since 3.0.0
+ *
+ * @global array $wp_post_types List of post types.
+ *
+ * @see register_post_type()
+ *
+ * @param string $post_type The name of a registered post type.
+ * @return object|null A post type object.
+ */
+function get_post_type_object( $post_type ) {
+ global $wp_post_types;
+
+ if ( ! is_scalar( $post_type ) || empty( $wp_post_types[ $post_type ] ) ) {
+ return null;
+ }
+
+ return $wp_post_types[ $post_type ];
+}
+
+/**
+ * Get a list of all registered post type objects.
+ *
+ * @since 2.9.0
+ *
+ * @global array $wp_post_types List of post types.
+ *
+ * @see register_post_type() for accepted arguments.
+ *
+ * @param array|string $args Optional. An array of key => value arguments to match against
+ * the post type objects. Default empty array.
+ * @param string $output Optional. The type of output to return. Accepts post type 'names'
+ * or 'objects'. Default 'names'.
+ * @param string $operator Optional. The logical operation to perform. 'or' means only one
+ * element from the array needs to match; 'and' means all elements
+ * must match. Accepts 'or' or 'and'. Default 'and'.
+ * @return array A list of post type names or objects.
+ */
+function get_post_types( $args = array(), $output = 'names', $operator = 'and' ) {
+ global $wp_post_types;
+
+ $field = ('names' == $output) ? 'name' : false;
+
+ return wp_filter_object_list($wp_post_types, $args, $operator, $field);
+}
+
+/**
+ * Register a post type. Do not use before init.
+ *
+ * A function for creating or modifying a post type based on the
+ * parameters given. The function will accept an array (second optional
+ * parameter), along with a string for the post type name.
+ *
+ * @since 2.9.0
+ * @since 3.0.0 The `show_ui` argument is now enforced on the new post screen.
+ * @since 4.4.0 The `show_ui` argument is now enforced on the post type listing screen and post editing screen.
+ *
+ * @global array $wp_post_types List of post types.
+ * @global WP_Rewrite $wp_rewrite Used for default feeds.
+ * @global WP $wp Used to add query vars.
+ *
+ * @param string $post_type Post type key, must not exceed 20 characters.
+ * @param array|string $args {
+ * Array or string of arguments for registering a post type.
+ *
+ * @type string $label Name of the post type shown in the menu. Usually plural.
+ * Default is value of $labels['name'].
+ * @type array $labels An array of labels for this post type. If not set, post
+ * labels are inherited for non-hierarchical types and page
+ * labels for hierarchical ones. {@see get_post_type_labels()}.
+ * @type string $description A short descriptive summary of what the post type is.
+ * Default empty.
+ * @type bool $public Whether a post type is intended for use publicly either via
+ * the admin interface or by front-end users. While the default
+ * settings of $exclude_from_search, $publicly_queryable, $show_ui,
+ * and $show_in_nav_menus are inherited from public, each does not
+ * rely on this relationship and controls a very specific intention.
+ * Default false.
+ * @type bool $hierarchical Whether the post type is hierarchical (e.g. page). Default false.
+ * @type bool $exclude_from_search Whether to exclude posts with this post type from front end search
+ * results. Default is the opposite value of $public.
+ * @type bool $publicly_queryable Whether queries can be performed on the front end for the post type
+ * as part of {@see parse_request()}. Endpoints would include:
+ * * ?post_type={post_type_key}
+ * * ?{post_type_key}={single_post_slug}
+ * * ?{post_type_query_var}={single_post_slug}
+ * If not set, the default is inherited from $public.
+ * @type bool $show_ui Whether to generate and allow a UI for managing this post type in the
+ * admin. Default is value of $public.
+ * @type bool $show_in_menu Where to show the post type in the admin menu. To work, $show_ui
+ * must be true. If true, the post type is shown in its own top level
+ * menu. If false, no menu is shown. If a string of an existing top
+ * level menu (eg. 'tools.php' or 'edit.php?post_type=page'), the post
+ * type will be placed as a sub-menu of that.
+ * Default is value of $show_ui.
+ * @type bool $show_in_nav_menus Makes this post type available for selection in navigation menus.
+ * Default is value $public.
+ * @type bool $show_in_admin_bar Makes this post type available via the admin bar. Default is value
+ * of $show_in_menu.
+ * @type int $menu_position The position in the menu order the post type should appear. To work,
+ * $show_in_menu must be true. Default null (at the bottom).
+ * @type string $menu_icon The url to the icon to be used for this menu. Pass a base64-encoded
+ * SVG using a data URI, which will be colored to match the color scheme
+ * -- this should begin with 'data:image/svg+xml;base64,'. Pass the name
+ * of a Dashicons helper class to use a font icon, e.g.
+ * 'dashicons-chart-pie'. Pass 'none' to leave div.wp-menu-image empty
+ * so an icon can be added via CSS. Defaults to use the posts icon.
+ * @type string $capability_type The string to use to build the read, edit, and delete capabilities.
+ * May be passed as an array to allow for alternative plurals when using
+ * this argument as a base to construct the capabilities, e.g.
+ * array('story', 'stories'). Default 'post'.
+ * @type array $capabilities Array of capabilities for this post type. $capability_type is used
+ * as a base to construct capabilities by default.
+ * {@see get_post_type_capabilities()}.
+ * @type bool $map_meta_cap Whether to use the internal default meta capability handling.
+ * Default false.
+ * @type array $supports An alias for calling {@see add_post_type_support()} directly.
+ * Defaults to array containing 'title' & 'editor'.
+ * @type callable $register_meta_box_cb Provide a callback function that sets up the meta boxes for the
+ * edit form. Do remove_meta_box() and add_meta_box() calls in the
+ * callback. Default null.
+ * @type array $taxonomies An array of taxonomy identifiers that will be registered for the
+ * post type. Taxonomies can be registered later with
+ * {@see register_taxonomy()} or {@see register_taxonomy_for_object_type()}.
+ * Default empty array.
+ * @type bool|string $has_archive Whether there should be post type archives, or if a string, the
+ * archive slug to use. Will generate the proper rewrite rules if
+ * $rewrite is enabled. Default false.
+ * @type bool|array $rewrite {
+ * Triggers the handling of rewrites for this post type. To prevent rewrite, set to false.
+ * Defaults to true, using $post_type as slug. To specify rewrite rules, an array can be
+ * passed with any of these keys:
+ *
+ * @type string $slug Customize the permastruct slug. Defaults to $post_type key.
+ * @type bool $with_front Whether the permastruct should be prepended with WP_Rewrite::$front.
+ * Default true.
+ * @type bool $feeds Whether the feed permastruct should be built for this post type.
+ * Default is value of $has_archive.
+ * @type bool $pages Whether the permastruct should provide for pagination. Default true.
+ * @type const $ep_mask Endpoint mask to assign. If not specified and permalink_epmask is set,
+ * inherits from $permalink_epmask. If not specified and permalink_epmask
+ * is not set, defaults to EP_PERMALINK.
+ * }
+ * @type string|bool $query_var Sets the query_var key for this post type. Defaults to $post_type
+ * key. If false, a post type cannot be loaded at
+ * ?{query_var}={post_slug}. If specified as a string, the query
+ * ?{query_var_string}={post_slug} will be valid.
+ * @type bool $can_export Whether to allow this post type to be exported. Default true.
+ * @type bool $delete_with_user Whether to delete posts of this type when deleting a user. If true,
+ * posts of this type belonging to the user will be moved to trash
+ * when then user is deleted. If false, posts of this type belonging
+ * to the user will *not* be trashed or deleted. If not set (the default),
+ * posts are trashed if post_type_supports('author'). Otherwise posts
+ * are not trashed or deleted. Default null.
+ * @type bool $_builtin FOR INTERNAL USE ONLY! True if this post type is a native or
+ * "built-in" post_type. Default false.
+ * @type string $_edit_link FOR INTERNAL USE ONLY! URL segment to use for edit link of
+ * this post type. Default 'post.php?post=%d'.
+ * }
+ * @return object|WP_Error The registered post type object, or an error object.
+ */
+function register_post_type( $post_type, $args = array() ) {
+ global $wp_post_types, $wp_rewrite, $wp;
+
+ if ( ! is_array( $wp_post_types ) ) {
+ $wp_post_types = array();
+ }
+
+ // Sanitize post type name
+ $post_type = sanitize_key( $post_type );
+ $args = wp_parse_args( $args );
+
+ /**
+ * Filter the arguments for registering a post type.
+ *
+ * @since 4.4.0
+ *
+ * @param array $args Array of arguments for registering a post type.
+ * @param string $post_type Post type key.
+ */
+ $args = apply_filters( 'register_post_type_args', $args, $post_type );
+
+ $has_edit_link = ! empty( $args['_edit_link'] );
+
+ // Args prefixed with an underscore are reserved for internal use.
+ $defaults = array(
+ 'labels' => array(),
+ 'description' => '',
+ 'public' => false,
+ 'hierarchical' => false,
+ 'exclude_from_search' => null,
+ 'publicly_queryable' => null,
+ 'show_ui' => null,
+ 'show_in_menu' => null,
+ 'show_in_nav_menus' => null,
+ 'show_in_admin_bar' => null,
+ 'menu_position' => null,
+ 'menu_icon' => null,
+ 'capability_type' => 'post',
+ 'capabilities' => array(),
+ 'map_meta_cap' => null,
+ 'supports' => array(),
+ 'register_meta_box_cb' => null,
+ 'taxonomies' => array(),
+ 'has_archive' => false,
+ 'rewrite' => true,
+ 'query_var' => true,
+ 'can_export' => true,
+ 'delete_with_user' => null,
+ '_builtin' => false,
+ '_edit_link' => 'post.php?post=%d',
+ );
+ $args = array_merge( $defaults, $args );
+ $args = (object) $args;
+
+ $args->name = $post_type;
+
+ if ( empty( $post_type ) || strlen( $post_type ) > 20 ) {
+ _doing_it_wrong( __FUNCTION__, __( 'Post type names must be between 1 and 20 characters in length.' ), '4.2' );
+ return new WP_Error( 'post_type_length_invalid', __( 'Post type names must be between 1 and 20 characters in length.' ) );
+ }
+
+ // If not set, default to the setting for public.
+ if ( null === $args->publicly_queryable )
+ $args->publicly_queryable = $args->public;
+
+ // If not set, default to the setting for public.
+ if ( null === $args->show_ui )
+ $args->show_ui = $args->public;
+
+ // If not set, default to the setting for show_ui.
+ if ( null === $args->show_in_menu || ! $args->show_ui )
+ $args->show_in_menu = $args->show_ui;
+
+ // If not set, default to the whether the full UI is shown.
+ if ( null === $args->show_in_admin_bar )
+ $args->show_in_admin_bar = (bool) $args->show_in_menu;
+
+ // If not set, default to the setting for public.
+ if ( null === $args->show_in_nav_menus )
+ $args->show_in_nav_menus = $args->public;
+
+ // If not set, default to true if not public, false if public.
+ if ( null === $args->exclude_from_search )
+ $args->exclude_from_search = !$args->public;
+
+ // Back compat with quirky handling in version 3.0. #14122.
+ if ( empty( $args->capabilities ) && null === $args->map_meta_cap && in_array( $args->capability_type, array( 'post', 'page' ) ) )
+ $args->map_meta_cap = true;
+
+ // If not set, default to false.
+ if ( null === $args->map_meta_cap )
+ $args->map_meta_cap = false;
+
+ // If there's no specified edit link and no UI, remove the edit link.
+ if ( ! $args->show_ui && ! $has_edit_link ) {
+ $args->_edit_link = '';
+ }
+
+ $args->cap = get_post_type_capabilities( $args );
+ unset( $args->capabilities );
+
+ if ( is_array( $args->capability_type ) )
+ $args->capability_type = $args->capability_type[0];
+
+ if ( ! empty( $args->supports ) ) {
+ add_post_type_support( $post_type, $args->supports );
+ unset( $args->supports );
+ } elseif ( false !== $args->supports ) {
+ // Add default features
+ add_post_type_support( $post_type, array( 'title', 'editor' ) );
+ }
+
+ if ( false !== $args->query_var ) {
+ if ( true === $args->query_var )
+ $args->query_var = $post_type;
+ else
+ $args->query_var = sanitize_title_with_dashes( $args->query_var );
+
+ if ( $wp && is_post_type_viewable( $args ) ) {
+ $wp->add_query_var( $args->query_var );
+ }
+ }
+
+ if ( false !== $args->rewrite && ( is_admin() || '' != get_option( 'permalink_structure' ) ) ) {
+ if ( ! is_array( $args->rewrite ) )
+ $args->rewrite = array();
+ if ( empty( $args->rewrite['slug'] ) )
+ $args->rewrite['slug'] = $post_type;
+ if ( ! isset( $args->rewrite['with_front'] ) )
+ $args->rewrite['with_front'] = true;
+ if ( ! isset( $args->rewrite['pages'] ) )
+ $args->rewrite['pages'] = true;
+ if ( ! isset( $args->rewrite['feeds'] ) || ! $args->has_archive )
+ $args->rewrite['feeds'] = (bool) $args->has_archive;
+ if ( ! isset( $args->rewrite['ep_mask'] ) ) {
+ if ( isset( $args->permalink_epmask ) )
+ $args->rewrite['ep_mask'] = $args->permalink_epmask;
+ else
+ $args->rewrite['ep_mask'] = EP_PERMALINK;
+ }
+
+ if ( $args->hierarchical )
+ add_rewrite_tag( "%$post_type%", '(.+?)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&pagename=" );
+ else
+ add_rewrite_tag( "%$post_type%", '([^/]+)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name=" );
+
+ if ( $args->has_archive ) {
+ $archive_slug = $args->has_archive === true ? $args->rewrite['slug'] : $args->has_archive;
+ if ( $args->rewrite['with_front'] )
+ $archive_slug = substr( $wp_rewrite->front, 1 ) . $archive_slug;
+ else
+ $archive_slug = $wp_rewrite->root . $archive_slug;
+
+ add_rewrite_rule( "{$archive_slug}/?$", "index.php?post_type=$post_type", 'top' );
+ if ( $args->rewrite['feeds'] && $wp_rewrite->feeds ) {
+ $feeds = '(' . trim( implode( '|', $wp_rewrite->feeds ) ) . ')';
+ add_rewrite_rule( "{$archive_slug}/feed/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
+ add_rewrite_rule( "{$archive_slug}/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
+ }
+ if ( $args->rewrite['pages'] )
+ add_rewrite_rule( "{$archive_slug}/{$wp_rewrite->pagination_base}/([0-9]{1,})/?$", "index.php?post_type=$post_type" . '&paged=$matches[1]', 'top' );
+ }
+
+ $permastruct_args = $args->rewrite;
+ $permastruct_args['feed'] = $permastruct_args['feeds'];
+ add_permastruct( $post_type, "{$args->rewrite['slug']}/%$post_type%", $permastruct_args );
+ }
+
+ // Register the post type meta box if a custom callback was specified.
+ if ( $args->register_meta_box_cb )
+ add_action( 'add_meta_boxes_' . $post_type, $args->register_meta_box_cb, 10, 1 );
+
+ $args->labels = get_post_type_labels( $args );
+ $args->label = $args->labels->name;
+
+ $wp_post_types[ $post_type ] = $args;
+
+ add_action( 'future_' . $post_type, '_future_post_hook', 5, 2 );
+
+ foreach ( $args->taxonomies as $taxonomy ) {
+ register_taxonomy_for_object_type( $taxonomy, $post_type );
+ }
+
+ /**
+ * Fires after a post type is registered.
+ *
+ * @since 3.3.0
+ *
+ * @param string $post_type Post type.
+ * @param object $args Arguments used to register the post type.
+ */
+ do_action( 'registered_post_type', $post_type, $args );
+
+ return $args;
+}
+
+/**
+ * Build an object with all post type capabilities out of a post type object
+ *
+ * Post type capabilities use the 'capability_type' argument as a base, if the
+ * capability is not set in the 'capabilities' argument array or if the
+ * 'capabilities' argument is not supplied.
+ *
+ * The capability_type argument can optionally be registered as an array, with
+ * the first value being singular and the second plural, e.g. array('story, 'stories')
+ * Otherwise, an 's' will be added to the value for the plural form. After
+ * registration, capability_type will always be a string of the singular value.
+ *
+ * By default, seven keys are accepted as part of the capabilities array:
+ *
+ * - edit_post, read_post, and delete_post are meta capabilities, which are then
+ * generally mapped to corresponding primitive capabilities depending on the
+ * context, which would be the post being edited/read/deleted and the user or
+ * role being checked. Thus these capabilities would generally not be granted
+ * directly to users or roles.
+ *
+ * - edit_posts - Controls whether objects of this post type can be edited.
+ * - edit_others_posts - Controls whether objects of this type owned by other users
+ * can be edited. If the post type does not support an author, then this will
+ * behave like edit_posts.
+ * - publish_posts - Controls publishing objects of this post type.
+ * - read_private_posts - Controls whether private objects can be read.
+ *
+ * These four primitive capabilities are checked in core in various locations.
+ * There are also seven other primitive capabilities which are not referenced
+ * directly in core, except in map_meta_cap(), which takes the three aforementioned
+ * meta capabilities and translates them into one or more primitive capabilities
+ * that must then be checked against the user or role, depending on the context.
+ *
+ * - read - Controls whether objects of this post type can be read.
+ * - delete_posts - Controls whether objects of this post type can be deleted.
+ * - delete_private_posts - Controls whether private objects can be deleted.
+ * - delete_published_posts - Controls whether published objects can be deleted.
+ * - delete_others_posts - Controls whether objects owned by other users can be
+ * can be deleted. If the post type does not support an author, then this will
+ * behave like delete_posts.
+ * - edit_private_posts - Controls whether private objects can be edited.
+ * - edit_published_posts - Controls whether published objects can be edited.
+ *
+ * These additional capabilities are only used in map_meta_cap(). Thus, they are
+ * only assigned by default if the post type is registered with the 'map_meta_cap'
+ * argument set to true (default is false).
+ *
+ * @since 3.0.0
+ *
+ * @see register_post_type()
+ * @see map_meta_cap()
+ *
+ * @param object $args Post type registration arguments.
+ * @return object object with all the capabilities as member variables.
+ */
+function get_post_type_capabilities( $args ) {
+ if ( ! is_array( $args->capability_type ) )
+ $args->capability_type = array( $args->capability_type, $args->capability_type . 's' );
+
+ // Singular base for meta capabilities, plural base for primitive capabilities.
+ list( $singular_base, $plural_base ) = $args->capability_type;
+
+ $default_capabilities = array(
+ // Meta capabilities
+ 'edit_post' => 'edit_' . $singular_base,
+ 'read_post' => 'read_' . $singular_base,
+ 'delete_post' => 'delete_' . $singular_base,
+ // Primitive capabilities used outside of map_meta_cap():
+ 'edit_posts' => 'edit_' . $plural_base,
+ 'edit_others_posts' => 'edit_others_' . $plural_base,
+ 'publish_posts' => 'publish_' . $plural_base,
+ 'read_private_posts' => 'read_private_' . $plural_base,
+ );
+
+ // Primitive capabilities used within map_meta_cap():
+ if ( $args->map_meta_cap ) {
+ $default_capabilities_for_mapping = array(
+ 'read' => 'read',
+ 'delete_posts' => 'delete_' . $plural_base,
+ 'delete_private_posts' => 'delete_private_' . $plural_base,
+ 'delete_published_posts' => 'delete_published_' . $plural_base,
+ 'delete_others_posts' => 'delete_others_' . $plural_base,
+ 'edit_private_posts' => 'edit_private_' . $plural_base,
+ 'edit_published_posts' => 'edit_published_' . $plural_base,
+ );
+ $default_capabilities = array_merge( $default_capabilities, $default_capabilities_for_mapping );
+ }
+
+ $capabilities = array_merge( $default_capabilities, $args->capabilities );
+
+ // Post creation capability simply maps to edit_posts by default:
+ if ( ! isset( $capabilities['create_posts'] ) )
+ $capabilities['create_posts'] = $capabilities['edit_posts'];
+
+ // Remember meta capabilities for future reference.
+ if ( $args->map_meta_cap )
+ _post_type_meta_capabilities( $capabilities );
+
+ return (object) $capabilities;
+}
+
+/**
+ * Store or return a list of post type meta caps for map_meta_cap().
+ *
+ * @since 3.1.0
+ * @access private
+ *
+ * @staticvar array $meta_caps
+ *
+ * @param array|void $capabilities Post type meta capabilities.
+ */
+function _post_type_meta_capabilities( $capabilities = null ) {
+ static $meta_caps = array();
+ if ( null === $capabilities )
+ return $meta_caps;
+ foreach ( $capabilities as $core => $custom ) {
+ if ( in_array( $core, array( 'read_post', 'delete_post', 'edit_post' ) ) )
+ $meta_caps[ $custom ] = $core;
+ }
+}
+
+/**
+ * Build an object with all post type labels out of a post type object
+ *
+ * Accepted keys of the label array in the post type object:
+ *
+ * - name - general name for the post type, usually plural. The same and overridden
+ * by $post_type_object->label. Default is Posts/Pages
+ * - singular_name - name for one object of this post type. Default is Post/Page
+ * - add_new - Default is Add New for both hierarchical and non-hierarchical types.
+ * When internationalizing this string, please use a gettext context
+ * {@link https://codex.wordpress.org/I18n_for_WordPress_Developers#Disambiguation_by_context}
+ * matching your post type. Example: `_x( 'Add New', 'product' );`.
+ * - add_new_item - Default is Add New Post/Add New Page.
+ * - edit_item - Default is Edit Post/Edit Page.
+ * - new_item - Default is New Post/New Page.
+ * - view_item - Default is View Post/View Page.
+ * - search_items - Default is Search Posts/Search Pages.
+ * - not_found - Default is No posts found/No pages found.
+ * - not_found_in_trash - Default is No posts found in Trash/No pages found in Trash.
+ * - parent_item_colon - This string isn't used on non-hierarchical types. In hierarchical
+ * ones the default is 'Parent Page:'.
+ * - all_items - String for the submenu. Default is All Posts/All Pages.
+ * - archives - String for use with archives in nav menus. Default is Post Archives/Page Archives.
+ * - insert_into_item - String for the media frame button. Default is Insert into post/Insert into page.
+ * - uploaded_to_this_item - String for the media frame filter. Default is Uploaded to this post/Uploaded to this page.
+ * - featured_image - Default is Featured Image.
+ * - set_featured_image - Default is Set featured image.
+ * - remove_featured_image - Default is Remove featured image.
+ * - use_featured_image - Default is Use as featured image.
+ * - menu_name - Default is the same as `name`.
+ * - filter_items_list - String for the table views hidden heading.
+ * - items_list_navigation - String for the table pagination hidden heading.
+ * - items_list - String for the table hidden heading.
+ *
+ * Above, the first default value is for non-hierarchical post types (like posts)
+ * and the second one is for hierarchical post types (like pages).
+ *
+ * @since 3.0.0
+ * @since 4.3.0 Added the `featured_image`, `set_featured_image`, `remove_featured_image`,
+ * and `use_featured_image` labels.
+ * @since 4.4.0 Added the `insert_into_item`, `uploaded_to_this_item`, `filter_items_list`,
+ * `items_list_navigation`, and `items_list` labels.
+ *
+ * @access private
+ *
+ * @param object $post_type_object Post type object.
+ * @return object object with all the labels as member variables.
+ */
+function get_post_type_labels( $post_type_object ) {
+ $nohier_vs_hier_defaults = array(
+ 'name' => array( _x('Posts', 'post type general name'), _x('Pages', 'post type general name') ),
+ 'singular_name' => array( _x('Post', 'post type singular name'), _x('Page', 'post type singular name') ),
+ 'add_new' => array( _x('Add New', 'post'), _x('Add New', 'page') ),
+ 'add_new_item' => array( __('Add New Post'), __('Add New Page') ),
+ 'edit_item' => array( __('Edit Post'), __('Edit Page') ),
+ 'new_item' => array( __('New Post'), __('New Page') ),
+ 'view_item' => array( __('View Post'), __('View Page') ),
+ 'search_items' => array( __('Search Posts'), __('Search Pages') ),
+ 'not_found' => array( __('No posts found.'), __('No pages found.') ),
+ 'not_found_in_trash' => array( __('No posts found in Trash.'), __('No pages found in Trash.') ),
+ 'parent_item_colon' => array( null, __('Parent Page:') ),
+ 'all_items' => array( __( 'All Posts' ), __( 'All Pages' ) ),
+ 'archives' => array( __( 'Post Archives' ), __( 'Page Archives' ) ),
+ 'insert_into_item' => array( __( 'Insert into post' ), __( 'Insert into page' ) ),
+ 'uploaded_to_this_item' => array( __( 'Uploaded to this post' ), __( 'Uploaded to this page' ) ),
+ 'featured_image' => array( __( 'Featured Image' ), __( 'Featured Image' ) ),
+ 'set_featured_image' => array( __( 'Set featured image' ), __( 'Set featured image' ) ),
+ 'remove_featured_image' => array( __( 'Remove featured image' ), __( 'Remove featured image' ) ),
+ 'use_featured_image' => array( __( 'Use as featured image' ), __( 'Use as featured image' ) ),
+ 'filter_items_list' => array( __( 'Filter posts list' ), __( 'Filter pages list' ) ),
+ 'items_list_navigation' => array( __( 'Posts list navigation' ), __( 'Pages list navigation' ) ),
+ 'items_list' => array( __( 'Posts list' ), __( 'Pages list' ) ),
+ );
+ $nohier_vs_hier_defaults['menu_name'] = $nohier_vs_hier_defaults['name'];
+
+ $labels = _get_custom_object_labels( $post_type_object, $nohier_vs_hier_defaults );
+
+ $post_type = $post_type_object->name;
+
+ $default_labels = clone $labels;
+
+ /**
+ * Filter the labels of a specific post type.
+ *
+ * The dynamic portion of the hook name, `$post_type`, refers to
+ * the post type slug.
+ *
+ * @since 3.5.0
+ *
+ * @see get_post_type_labels() for the full list of labels.
+ *
+ * @param object $labels Object with labels for the post type as member variables.
+ */
+ $labels = apply_filters( "post_type_labels_{$post_type}", $labels );
+
+ // Ensure that the filtered labels contain all required default values.
+ $labels = (object) array_merge( (array) $default_labels, (array) $labels );
+
+ return $labels;
+}
+
+/**
+ * Build an object with custom-something object (post type, taxonomy) labels
+ * out of a custom-something object
+ *
+ * @since 3.0.0
+ * @access private
+ *
+ * @param object $object A custom-something object.
+ * @param array $nohier_vs_hier_defaults Hierarchical vs non-hierarchical default labels.
+ * @return object Object containing labels for the given custom-something object.
+ */
+function _get_custom_object_labels( $object, $nohier_vs_hier_defaults ) {
+ $object->labels = (array) $object->labels;
+
+ if ( isset( $object->label ) && empty( $object->labels['name'] ) )
+ $object->labels['name'] = $object->label;
+
+ if ( !isset( $object->labels['singular_name'] ) && isset( $object->labels['name'] ) )
+ $object->labels['singular_name'] = $object->labels['name'];
+
+ if ( ! isset( $object->labels['name_admin_bar'] ) )
+ $object->labels['name_admin_bar'] = isset( $object->labels['singular_name'] ) ? $object->labels['singular_name'] : $object->name;
+
+ if ( !isset( $object->labels['menu_name'] ) && isset( $object->labels['name'] ) )
+ $object->labels['menu_name'] = $object->labels['name'];
+
+ if ( !isset( $object->labels['all_items'] ) && isset( $object->labels['menu_name'] ) )
+ $object->labels['all_items'] = $object->labels['menu_name'];
+
+ if ( !isset( $object->labels['archives'] ) && isset( $object->labels['all_items'] ) ) {
+ $object->labels['archives'] = $object->labels['all_items'];
+ }
+
+ $defaults = array();
+ foreach ( $nohier_vs_hier_defaults as $key => $value ) {
+ $defaults[$key] = $object->hierarchical ? $value[1] : $value[0];
+ }
+ $labels = array_merge( $defaults, $object->labels );
+ $object->labels = (object) $object->labels;
+
+ return (object) $labels;
+}
+
+/**
+ * Add submenus for post types.
+ *
+ * @access private
+ * @since 3.1.0
+ */
+function _add_post_type_submenus() {
+ foreach ( get_post_types( array( 'show_ui' => true ) ) as $ptype ) {
+ $ptype_obj = get_post_type_object( $ptype );
+ // Sub-menus only.
+ if ( ! $ptype_obj->show_in_menu || $ptype_obj->show_in_menu === true )
+ continue;
+ add_submenu_page( $ptype_obj->show_in_menu, $ptype_obj->labels->name, $ptype_obj->labels->all_items, $ptype_obj->cap->edit_posts, "edit.php?post_type=$ptype" );
+ }
+}
+
+/**
+ * Register support of certain features for a post type.
+ *
+ * All core features are directly associated with a functional area of the edit
+ * screen, such as the editor or a meta box. Features include: 'title', 'editor',
+ * 'comments', 'revisions', 'trackbacks', 'author', 'excerpt', 'page-attributes',
+ * 'thumbnail', 'custom-fields', and 'post-formats'.
+ *
+ * Additionally, the 'revisions' feature dictates whether the post type will
+ * store revisions, and the 'comments' feature dictates whether the comments
+ * count will show on the edit screen.
+ *
+ * @since 3.0.0
+ *
+ * @global array $_wp_post_type_features
+ *
+ * @param string $post_type The post type for which to add the feature.
+ * @param string|array $feature The feature being added, accepts an array of
+ * feature strings or a single string.
+ */
+function add_post_type_support( $post_type, $feature ) {
+ global $_wp_post_type_features;
+
+ $features = (array) $feature;
+ foreach ($features as $feature) {
+ if ( func_num_args() == 2 )
+ $_wp_post_type_features[$post_type][$feature] = true;
+ else
+ $_wp_post_type_features[$post_type][$feature] = array_slice( func_get_args(), 2 );
+ }
+}
+
+/**
+ * Remove support for a feature from a post type.
+ *
+ * @since 3.0.0
+ *
+ * @global array $_wp_post_type_features
+ *
+ * @param string $post_type The post type for which to remove the feature.
+ * @param string $feature The feature being removed.
+ */
+function remove_post_type_support( $post_type, $feature ) {
+ global $_wp_post_type_features;
+
+ unset( $_wp_post_type_features[ $post_type ][ $feature ] );
+}
+
+/**
+ * Get all the post type features
+ *
+ * @since 3.4.0
+ *
+ * @global array $_wp_post_type_features
+ *
+ * @param string $post_type The post type.
+ * @return array Post type supports list.
+ */
+function get_all_post_type_supports( $post_type ) {
+ global $_wp_post_type_features;
+
+ if ( isset( $_wp_post_type_features[$post_type] ) )
+ return $_wp_post_type_features[$post_type];
+
+ return array();
+}
+
+/**
+ * Check a post type's support for a given feature.
+ *
+ * @since 3.0.0
+ *
+ * @global array $_wp_post_type_features
+ *
+ * @param string $post_type The post type being checked.
+ * @param string $feature The feature being checked.
+ * @return bool Whether the post type supports the given feature.
+ */
+function post_type_supports( $post_type, $feature ) {
+ global $_wp_post_type_features;
+
+ return ( isset( $_wp_post_type_features[$post_type][$feature] ) );
+}
+
+/**
+ * Update the post type for the post ID.
+ *
+ * The page or post cache will be cleaned for the post ID.
+ *
+ * @since 2.5.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $post_id Optional. Post ID to change post type. Default 0.
+ * @param string $post_type Optional. Post type. Accepts 'post' or 'page' to
+ * name a few. Default 'post'.
+ * @return int|false Amount of rows changed. Should be 1 for success and 0 for failure.
+ */
+function set_post_type( $post_id = 0, $post_type = 'post' ) {
+ global $wpdb;
+
+ $post_type = sanitize_post_field('post_type', $post_type, $post_id, 'db');
+ $return = $wpdb->update( $wpdb->posts, array('post_type' => $post_type), array('ID' => $post_id) );
+
+ clean_post_cache( $post_id );
+
+ return $return;
+}
+
+/**
+ * Determines whether a post type is considered "viewable".
+ *
+ * For built-in post types such as posts and pages, the 'public' value will be evaluated.
+ * For all others, the 'publicly_queryable' value will be used.
+ *
+ * @since 4.4.0
+ *
+ * @param object $post_type_object Post type object.
+ * @return bool Whether the post type should be considered viewable.
+ */
+function is_post_type_viewable( $post_type_object ) {
+ return $post_type_object->publicly_queryable || ( $post_type_object->_builtin && $post_type_object->public );
+}
+
+/**
+ * Retrieve list of latest posts or posts matching criteria.
+ *
+ * The defaults are as follows:
+ *
+ * @since 1.2.0
+ *
+ * @see WP_Query::parse_query()
+ *
+ * @param array $args {
+ * Optional. Arguments to retrieve posts. See WP_Query::parse_query() for all
+ * available arguments.
+ *
+ * @type int $numberposts Total number of posts to retrieve. Is an alias of $posts_per_page
+ * in WP_Query. Accepts -1 for all. Default 5.
+ * @type int|string $category Category ID or comma-separated list of IDs (this or any children).
+ * Is an alias of $cat in WP_Query. Default 0.
+ * @type array $include An array of post IDs to retrieve, sticky posts will be included.
+ * Is an alias of $post__in in WP_Query. Default empty array.
+ * @type array $exclude An array of post IDs not to retrieve. Default empty array.
+ * @type bool $suppress_filters Whether to suppress filters. Default true.
+ * }
+ * @return array List of posts.
+ */
+function get_posts( $args = null ) {
+ $defaults = array(
+ 'numberposts' => 5,
+ 'category' => 0, 'orderby' => 'date',
+ 'order' => 'DESC', 'include' => array(),
+ 'exclude' => array(), 'meta_key' => '',
+ 'meta_value' =>'', 'post_type' => 'post',
+ 'suppress_filters' => true
+ );
+
+ $r = wp_parse_args( $args, $defaults );
+ if ( empty( $r['post_status'] ) )
+ $r['post_status'] = ( 'attachment' == $r['post_type'] ) ? 'inherit' : 'publish';
+ if ( ! empty($r['numberposts']) && empty($r['posts_per_page']) )
+ $r['posts_per_page'] = $r['numberposts'];
+ if ( ! empty($r['category']) )
+ $r['cat'] = $r['category'];
+ if ( ! empty($r['include']) ) {
+ $incposts = wp_parse_id_list( $r['include'] );
+ $r['posts_per_page'] = count($incposts); // only the number of posts included
+ $r['post__in'] = $incposts;
+ } elseif ( ! empty($r['exclude']) )
+ $r['post__not_in'] = wp_parse_id_list( $r['exclude'] );
+
+ $r['ignore_sticky_posts'] = true;
+ $r['no_found_rows'] = true;
+
+ $get_posts = new WP_Query;
+ return $get_posts->query($r);
+
+}
+
+//
+// Post meta functions
+//
+
+/**
+ * Add meta data field to a post.
+ *
+ * Post meta data is called "Custom Fields" on the Administration Screen.
+ *
+ * @since 1.5.0
+ *
+ * @param int $post_id Post ID.
+ * @param string $meta_key Metadata name.
+ * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
+ * @param bool $unique Optional. Whether the same key should not be added.
+ * Default false.
+ * @return int|false Meta ID on success, false on failure.
+ */
+function add_post_meta( $post_id, $meta_key, $meta_value, $unique = false ) {
+ // Make sure meta is added to the post, not a revision.
+ if ( $the_post = wp_is_post_revision($post_id) )
+ $post_id = $the_post;
+
+ return add_metadata('post', $post_id, $meta_key, $meta_value, $unique);
+}
+
+/**
+ * Remove metadata matching criteria from a post.
+ *
+ * You can match based on the key, or key and value. Removing based on key and
+ * value, will keep from removing duplicate metadata with the same key. It also
+ * allows removing all metadata matching key, if needed.
+ *
+ * @since 1.5.0
+ *
+ * @param int $post_id Post ID.
+ * @param string $meta_key Metadata name.
+ * @param mixed $meta_value Optional. Metadata value. Must be serializable if
+ * non-scalar. Default empty.
+ * @return bool True on success, false on failure.
+ */
+function delete_post_meta( $post_id, $meta_key, $meta_value = '' ) {
+ // Make sure meta is added to the post, not a revision.
+ if ( $the_post = wp_is_post_revision($post_id) )
+ $post_id = $the_post;
+
+ return delete_metadata('post', $post_id, $meta_key, $meta_value);
+}
+
+/**
+ * Retrieve post meta field for a post.
+ *
+ * @since 1.5.0
+ *
+ * @param int $post_id Post ID.
+ * @param string $key Optional. The meta key to retrieve. By default, returns
+ * data for all keys. Default empty.
+ * @param bool $single Optional. Whether to return a single value. Default false.
+ * @return mixed Will be an array if $single is false. Will be value of meta data
+ * field if $single is true.
+ */
+function get_post_meta( $post_id, $key = '', $single = false ) {
+ return get_metadata('post', $post_id, $key, $single);
+}
+
+/**
+ * Update post meta field based on post ID.
+ *
+ * Use the $prev_value parameter to differentiate between meta fields with the
+ * same key and post ID.
+ *
+ * If the meta field for the post does not exist, it will be added.
+ *
+ * @since 1.5.0
+ *
+ * @param int $post_id Post ID.
+ * @param string $meta_key Metadata key.
+ * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
+ * @param mixed $prev_value Optional. Previous value to check before removing.
+ * Default empty.
+ * @return int|bool Meta ID if the key didn't exist, true on successful update,
+ * false on failure.
+ */
+function update_post_meta( $post_id, $meta_key, $meta_value, $prev_value = '' ) {
+ // Make sure meta is added to the post, not a revision.
+ if ( $the_post = wp_is_post_revision($post_id) )
+ $post_id = $the_post;
+
+ return update_metadata('post', $post_id, $meta_key, $meta_value, $prev_value);
+}
+
+/**
+ * Delete everything from post meta matching meta key.
+ *
+ * @since 2.3.0
+ *
+ * @param string $post_meta_key Key to search for when deleting.
+ * @return bool Whether the post meta key was deleted from the database.
+ */
+function delete_post_meta_by_key( $post_meta_key ) {
+ return delete_metadata( 'post', null, $post_meta_key, '', true );
+}
+
+/**
+ * Retrieve post meta fields, based on post ID.
+ *
+ * The post meta fields are retrieved from the cache where possible,
+ * so the function is optimized to be called more than once.
+ *
+ * @since 1.2.0
+ *
+ * @param int $post_id Optional. Post ID. Default is ID of the global $post.
+ * @return array Post meta for the given post.
+ */
+function get_post_custom( $post_id = 0 ) {
+ $post_id = absint( $post_id );
+ if ( ! $post_id )
+ $post_id = get_the_ID();
+
+ return get_post_meta( $post_id );
+}
+
+/**
+ * Retrieve meta field names for a post.
+ *
+ * If there are no meta fields, then nothing (null) will be returned.
+ *
+ * @since 1.2.0
+ *
+ * @param int $post_id Optional. Post ID. Default is ID of the global $post.
+ * @return array|void Array of the keys, if retrieved.
+ */
+function get_post_custom_keys( $post_id = 0 ) {
+ $custom = get_post_custom( $post_id );
+
+ if ( !is_array($custom) )
+ return;
+
+ if ( $keys = array_keys($custom) )
+ return $keys;
+}
+
+/**
+ * Retrieve values for a custom post field.
+ *
+ * The parameters must not be considered optional. All of the post meta fields
+ * will be retrieved and only the meta field key values returned.
+ *
+ * @since 1.2.0
+ *
+ * @param string $key Optional. Meta field key. Default empty.
+ * @param int $post_id Optional. Post ID. Default is ID of the global $post.
+ * @return array|null Meta field values.
+ */
+function get_post_custom_values( $key = '', $post_id = 0 ) {
+ if ( !$key )
+ return null;
+
+ $custom = get_post_custom($post_id);
+
+ return isset($custom[$key]) ? $custom[$key] : null;
+}
+
+/**
+ * Check if post is sticky.
+ *
+ * Sticky posts should remain at the top of The Loop. If the post ID is not
+ * given, then The Loop ID for the current post will be used.
+ *
+ * @since 2.7.0
+ *
+ * @param int $post_id Optional. Post ID. Default is ID of the global $post.
+ * @return bool Whether post is sticky.
+ */
+function is_sticky( $post_id = 0 ) {
+ $post_id = absint( $post_id );
+
+ if ( ! $post_id )
+ $post_id = get_the_ID();
+
+ $stickies = get_option( 'sticky_posts' );
+
+ if ( ! is_array( $stickies ) )
+ return false;
+
+ if ( in_array( $post_id, $stickies ) )
+ return true;
+
+ return false;
+}
+
+/**
+ * Sanitize every post field.
+ *
+ * If the context is 'raw', then the post object or array will get minimal
+ * sanitization of the integer fields.
+ *
+ * @since 2.3.0
+ *
+ * @see sanitize_post_field()
+ *
+ * @param object|WP_Post|array $post The Post Object or Array
+ * @param string $context Optional. How to sanitize post fields.
+ * Accepts 'raw', 'edit', 'db', or 'display'.
+ * Default 'display'.
+ * @return object|WP_Post|array The now sanitized Post Object or Array (will be the
+ * same type as $post).
+ */
+function sanitize_post( $post, $context = 'display' ) {
+ if ( is_object($post) ) {
+ // Check if post already filtered for this context.
+ if ( isset($post->filter) && $context == $post->filter )
+ return $post;
+ if ( !isset($post->ID) )
+ $post->ID = 0;
+ foreach ( array_keys(get_object_vars($post)) as $field )
+ $post->$field = sanitize_post_field($field, $post->$field, $post->ID, $context);
+ $post->filter = $context;
+ } elseif ( is_array( $post ) ) {
+ // Check if post already filtered for this context.
+ if ( isset($post['filter']) && $context == $post['filter'] )
+ return $post;
+ if ( !isset($post['ID']) )
+ $post['ID'] = 0;
+ foreach ( array_keys($post) as $field )
+ $post[$field] = sanitize_post_field($field, $post[$field], $post['ID'], $context);
+ $post['filter'] = $context;
+ }
+ return $post;
+}
+
+/**
+ * Sanitize post field based on context.
+ *
+ * Possible context values are: 'raw', 'edit', 'db', 'display', 'attribute' and
+ * 'js'. The 'display' context is used by default. 'attribute' and 'js' contexts
+ * are treated like 'display' when calling filters.
+ *
+ * @since 2.3.0
+ * @since 4.4.0 Like `sanitize_post()`, `$context` defaults to 'display'.
+ *
+ * @param string $field The Post Object field name.
+ * @param mixed $value The Post Object value.
+ * @param int $post_id Post ID.
+ * @param string $context Optional. How to sanitize post fields. Looks for 'raw', 'edit',
+ * 'db', 'display', 'attribute' and 'js'. Default 'display'.
+ * @return mixed Sanitized value.
+ */
+function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) {
+ $int_fields = array('ID', 'post_parent', 'menu_order');
+ if ( in_array($field, $int_fields) )
+ $value = (int) $value;
+
+ // Fields which contain arrays of integers.
+ $array_int_fields = array( 'ancestors' );
+ if ( in_array($field, $array_int_fields) ) {
+ $value = array_map( 'absint', $value);
+ return $value;
+ }
+
+ if ( 'raw' == $context )
+ return $value;
+
+ $prefixed = false;
+ if ( false !== strpos($field, 'post_') ) {
+ $prefixed = true;
+ $field_no_prefix = str_replace('post_', '', $field);
+ }
+
+ if ( 'edit' == $context ) {
+ $format_to_edit = array('post_content', 'post_excerpt', 'post_title', 'post_password');
+
+ if ( $prefixed ) {
+
+ /**
+ * Filter the value of a specific post field to edit.
+ *
+ * The dynamic portion of the hook name, `$field`, refers to the post
+ * field name.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the post field.
+ * @param int $post_id Post ID.
+ */
+ $value = apply_filters( "edit_{$field}", $value, $post_id );
+
+ /**
+ * Filter the value of a specific post field to edit.
+ *
+ * The dynamic portion of the hook name, `$field_no_prefix`, refers to
+ * the post field name.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the post field.
+ * @param int $post_id Post ID.
+ */
+ $value = apply_filters( "{$field_no_prefix}_edit_pre", $value, $post_id );
+ } else {
+ $value = apply_filters( "edit_post_{$field}", $value, $post_id );
+ }
+
+ if ( in_array($field, $format_to_edit) ) {
+ if ( 'post_content' == $field )
+ $value = format_to_edit($value, user_can_richedit());
+ else
+ $value = format_to_edit($value);
+ } else {
+ $value = esc_attr($value);
+ }
+ } elseif ( 'db' == $context ) {
+ if ( $prefixed ) {
+
+ /**
+ * Filter the value of a specific post field before saving.
+ *
+ * The dynamic portion of the hook name, `$field`, refers to the post
+ * field name.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the post field.
+ */
+ $value = apply_filters( "pre_{$field}", $value );
+
+ /**
+ * Filter the value of a specific field before saving.
+ *
+ * The dynamic portion of the hook name, `$field_no_prefix`, refers
+ * to the post field name.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the post field.
+ */
+ $value = apply_filters( "{$field_no_prefix}_save_pre", $value );
+ } else {
+ $value = apply_filters( "pre_post_{$field}", $value );
+
+ /**
+ * Filter the value of a specific post field before saving.
+ *
+ * The dynamic portion of the hook name, `$field`, refers to the post
+ * field name.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the post field.
+ */
+ $value = apply_filters( "{$field}_pre", $value );
+ }
+ } else {
+
+ // Use display filters by default.
+ if ( $prefixed ) {
+
+ /**
+ * Filter the value of a specific post field for display.
+ *
+ * The dynamic portion of the hook name, `$field`, refers to the post
+ * field name.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the prefixed post field.
+ * @param int $post_id Post ID.
+ * @param string $context Context for how to sanitize the field. Possible
+ * values include 'raw', 'edit', 'db', 'display',
+ * 'attribute' and 'js'.
+ */
+ $value = apply_filters( $field, $value, $post_id, $context );
+ } else {
+ $value = apply_filters( "post_{$field}", $value, $post_id, $context );
+ }
+ }
+
+ if ( 'attribute' == $context )
+ $value = esc_attr($value);
+ elseif ( 'js' == $context )
+ $value = esc_js($value);
+
+ return $value;
+}
+
+/**
+ * Make a post sticky.
+ *
+ * Sticky posts should be displayed at the top of the front page.
+ *
+ * @since 2.7.0
+ *
+ * @param int $post_id Post ID.
+ */
+function stick_post( $post_id ) {
+ $stickies = get_option('sticky_posts');
+
+ if ( !is_array($stickies) )
+ $stickies = array($post_id);
+
+ if ( ! in_array($post_id, $stickies) )
+ $stickies[] = $post_id;
+
+ update_option('sticky_posts', $stickies);
+}
+
+/**
+ * Un-stick a post.
+ *
+ * Sticky posts should be displayed at the top of the front page.
+ *
+ * @since 2.7.0
+ *
+ * @param int $post_id Post ID.
+ */
+function unstick_post( $post_id ) {
+ $stickies = get_option('sticky_posts');
+
+ if ( !is_array($stickies) )
+ return;
+
+ if ( ! in_array($post_id, $stickies) )
+ return;
+
+ $offset = array_search($post_id, $stickies);
+ if ( false === $offset )
+ return;
+
+ array_splice($stickies, $offset, 1);
+
+ update_option('sticky_posts', $stickies);
+}
+
+/**
+ * Return the cache key for wp_count_posts() based on the passed arguments.
+ *
+ * @since 3.9.0
+ *
+ * @param string $type Optional. Post type to retrieve count Default 'post'.
+ * @param string $perm Optional. 'readable' or empty. Default empty.
+ * @return string The cache key.
+ */
+function _count_posts_cache_key( $type = 'post', $perm = '' ) {
+ $cache_key = 'posts-' . $type;
+ if ( 'readable' == $perm && is_user_logged_in() ) {
+ $post_type_object = get_post_type_object( $type );
+ if ( $post_type_object && ! current_user_can( $post_type_object->cap->read_private_posts ) ) {
+ $cache_key .= '_' . $perm . '_' . get_current_user_id();
+ }
+ }
+ return $cache_key;
+}
+
+/**
+ * Count number of posts of a post type and if user has permissions to view.
+ *
+ * This function provides an efficient method of finding the amount of post's
+ * type a blog has. Another method is to count the amount of items in
+ * get_posts(), but that method has a lot of overhead with doing so. Therefore,
+ * when developing for 2.5+, use this function instead.
+ *
+ * The $perm parameter checks for 'readable' value and if the user can read
+ * private posts, it will display that for the user that is signed in.
+ *
+ * @since 2.5.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $type Optional. Post type to retrieve count. Default 'post'.
+ * @param string $perm Optional. 'readable' or empty. Default empty.
+ * @return object Number of posts for each status.
+ */
+function wp_count_posts( $type = 'post', $perm = '' ) {
+ global $wpdb;
+
+ if ( ! post_type_exists( $type ) )
+ return new stdClass;
+
+ $cache_key = _count_posts_cache_key( $type, $perm );
+
+ $counts = wp_cache_get( $cache_key, 'counts' );
+ if ( false !== $counts ) {
+ /** This filter is documented in wp-includes/post-functions.php */
+ return apply_filters( 'wp_count_posts', $counts, $type, $perm );
+ }
+
+ $query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s";
+ if ( 'readable' == $perm && is_user_logged_in() ) {
+ $post_type_object = get_post_type_object($type);
+ if ( ! current_user_can( $post_type_object->cap->read_private_posts ) ) {
+ $query .= $wpdb->prepare( " AND (post_status != 'private' OR ( post_author = %d AND post_status = 'private' ))",
+ get_current_user_id()
+ );
+ }
+ }
+ $query .= ' GROUP BY post_status';
+
+ $results = (array) $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A );
+ $counts = array_fill_keys( get_post_stati(), 0 );
+
+ foreach ( $results as $row ) {
+ $counts[ $row['post_status'] ] = $row['num_posts'];
+ }
+
+ $counts = (object) $counts;
+ wp_cache_set( $cache_key, $counts, 'counts' );
+
+ /**
+ * Modify returned post counts by status for the current post type.
+ *
+ * @since 3.7.0
+ *
+ * @param object $counts An object containing the current post_type's post
+ * counts by status.
+ * @param string $type Post type.
+ * @param string $perm The permission to determine if the posts are 'readable'
+ * by the current user.
+ */
+ return apply_filters( 'wp_count_posts', $counts, $type, $perm );
+}
+
+/**
+ * Count number of attachments for the mime type(s).
+ *
+ * If you set the optional mime_type parameter, then an array will still be
+ * returned, but will only have the item you are looking for. It does not give
+ * you the number of attachments that are children of a post. You can get that
+ * by counting the number of children that post has.
+ *
+ * @since 2.5.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string|array $mime_type Optional. Array or comma-separated list of
+ * MIME patterns. Default empty.
+ * @return object An object containing the attachment counts by mime type.
+ */
+function wp_count_attachments( $mime_type = '' ) {
+ global $wpdb;
+
+ $and = wp_post_mime_type_where( $mime_type );
+ $count = $wpdb->get_results( "SELECT post_mime_type, COUNT( * ) AS num_posts FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status != 'trash' $and GROUP BY post_mime_type", ARRAY_A );
+
+ $counts = array();
+ foreach ( (array) $count as $row ) {
+ $counts[ $row['post_mime_type'] ] = $row['num_posts'];
+ }
+ $counts['trash'] = $wpdb->get_var( "SELECT COUNT( * ) FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status = 'trash' $and");
+
+ /**
+ * Modify returned attachment counts by mime type.
+ *
+ * @since 3.7.0
+ *
+ * @param object $counts An object containing the attachment counts by
+ * mime type.
+ * @param string $mime_type The mime type pattern used to filter the attachments
+ * counted.
+ */
+ return apply_filters( 'wp_count_attachments', (object) $counts, $mime_type );
+}
+
+/**
+ * Get default post mime types.
+ *
+ * @since 2.9.0
+ *
+ * @return array List of post mime types.
+ */
+function get_post_mime_types() {
+ $post_mime_types = array( // array( adj, noun )
+ 'image' => array(__('Images'), __('Manage Images'), _n_noop('Image <span class="count">(%s)</span>', 'Images <span class="count">(%s)</span>')),
+ 'audio' => array(__('Audio'), __('Manage Audio'), _n_noop('Audio <span class="count">(%s)</span>', 'Audio <span class="count">(%s)</span>')),
+ 'video' => array(__('Video'), __('Manage Video'), _n_noop('Video <span class="count">(%s)</span>', 'Video <span class="count">(%s)</span>')),
+ );
+
+ /**
+ * Filter the default list of post mime types.
+ *
+ * @since 2.5.0
+ *
+ * @param array $post_mime_types Default list of post mime types.
+ */
+ return apply_filters( 'post_mime_types', $post_mime_types );
+}
+
+/**
+ * Check a MIME-Type against a list.
+ *
+ * If the wildcard_mime_types parameter is a string, it must be comma separated
+ * list. If the real_mime_types is a string, it is also comma separated to
+ * create the list.
+ *
+ * @since 2.5.0
+ *
+ * @param string|array $wildcard_mime_types Mime types, e.g. audio/mpeg or image (same as image/*)
+ * or flash (same as *flash*).
+ * @param string|array $real_mime_types Real post mime type values.
+ * @return array array(wildcard=>array(real types)).
+ */
+function wp_match_mime_types( $wildcard_mime_types, $real_mime_types ) {
+ $matches = array();
+ if ( is_string( $wildcard_mime_types ) ) {
+ $wildcard_mime_types = array_map( 'trim', explode( ',', $wildcard_mime_types ) );
+ }
+ if ( is_string( $real_mime_types ) ) {
+ $real_mime_types = array_map( 'trim', explode( ',', $real_mime_types ) );
+ }
+
+ $patternses = array();
+ $wild = '[-._a-z0-9]*';
+
+ foreach ( (array) $wildcard_mime_types as $type ) {
+ $mimes = array_map( 'trim', explode( ',', $type ) );
+ foreach ( $mimes as $mime ) {
+ $regex = str_replace( '__wildcard__', $wild, preg_quote( str_replace( '*', '__wildcard__', $mime ) ) );
+ $patternses[][$type] = "^$regex$";
+ if ( false === strpos( $mime, '/' ) ) {
+ $patternses[][$type] = "^$regex/";
+ $patternses[][$type] = $regex;
+ }
+ }
+ }
+ asort( $patternses );
+
+ foreach ( $patternses as $patterns ) {
+ foreach ( $patterns as $type => $pattern ) {
+ foreach ( (array) $real_mime_types as $real ) {
+ if ( preg_match( "#$pattern#", $real ) && ( empty( $matches[$type] ) || false === array_search( $real, $matches[$type] ) ) ) {
+ $matches[$type][] = $real;
+ }
+ }
+ }
+ }
+ return $matches;
+}
+
+/**
+ * Convert MIME types into SQL.
+ *
+ * @since 2.5.0
+ *
+ * @param string|array $post_mime_types List of mime types or comma separated string
+ * of mime types.
+ * @param string $table_alias Optional. Specify a table alias, if needed.
+ * Default empty.
+ * @return string The SQL AND clause for mime searching.
+ */
+function wp_post_mime_type_where( $post_mime_types, $table_alias = '' ) {
+ $where = '';
+ $wildcards = array('', '%', '%/%');
+ if ( is_string($post_mime_types) )
+ $post_mime_types = array_map('trim', explode(',', $post_mime_types));
+
+ $wheres = array();
+
+ foreach ( (array) $post_mime_types as $mime_type ) {
+ $mime_type = preg_replace('/\s/', '', $mime_type);
+ $slashpos = strpos($mime_type, '/');
+ if ( false !== $slashpos ) {
+ $mime_group = preg_replace('/[^-*.a-zA-Z0-9]/', '', substr($mime_type, 0, $slashpos));
+ $mime_subgroup = preg_replace('/[^-*.+a-zA-Z0-9]/', '', substr($mime_type, $slashpos + 1));
+ if ( empty($mime_subgroup) )
+ $mime_subgroup = '*';
+ else
+ $mime_subgroup = str_replace('/', '', $mime_subgroup);
+ $mime_pattern = "$mime_group/$mime_subgroup";
+ } else {
+ $mime_pattern = preg_replace('/[^-*.a-zA-Z0-9]/', '', $mime_type);
+ if ( false === strpos($mime_pattern, '*') )
+ $mime_pattern .= '/*';
+ }
+
+ $mime_pattern = preg_replace('/\*+/', '%', $mime_pattern);
+
+ if ( in_array( $mime_type, $wildcards ) )
+ return '';
+
+ if ( false !== strpos($mime_pattern, '%') )
+ $wheres[] = empty($table_alias) ? "post_mime_type LIKE '$mime_pattern'" : "$table_alias.post_mime_type LIKE '$mime_pattern'";
+ else
+ $wheres[] = empty($table_alias) ? "post_mime_type = '$mime_pattern'" : "$table_alias.post_mime_type = '$mime_pattern'";
+ }
+ if ( !empty($wheres) )
+ $where = ' AND (' . join(' OR ', $wheres) . ') ';
+ return $where;
+}
+
+/**
+ * Trash or delete a post or page.
+ *
+ * When the post and page is permanently deleted, everything that is tied to
+ * it is deleted also. This includes comments, post meta fields, and terms
+ * associated with the post.
+ *
+ * The post or page is moved to trash instead of permanently deleted unless
+ * trash is disabled, item is already in the trash, or $force_delete is true.
+ *
+ * @since 1.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ * @see wp_delete_attachment()
+ * @see wp_trash_post()
+ *
+ * @param int $postid Optional. Post ID. Default 0.
+ * @param bool $force_delete Optional. Whether to bypass trash and force deletion.
+ * Default false.
+ * @return array|false|WP_Post False on failure.
+ */
+function wp_delete_post( $postid = 0, $force_delete = false ) {
+ global $wpdb;
+
+ if ( !$post = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d", $postid)) )
+ return $post;
+
+ if ( !$force_delete && ( $post->post_type == 'post' || $post->post_type == 'page') && get_post_status( $postid ) != 'trash' && EMPTY_TRASH_DAYS )
+ return wp_trash_post( $postid );
+
+ if ( $post->post_type == 'attachment' )
+ return wp_delete_attachment( $postid, $force_delete );
+
+ /**
+ * Filter whether a post deletion should take place.
+ *
+ * @since 4.4.0
+ *
+ * @param bool $delete Whether to go forward with deletion.
+ * @param WP_Post $post Post object.
+ * @param bool $force_delete Whether to bypass the trash.
+ */
+ $check = apply_filters( 'pre_delete_post', null, $post, $force_delete );
+ if ( null !== $check ) {
+ return $check;
+ }
+
+ /**
+ * Fires before a post is deleted, at the start of wp_delete_post().
+ *
+ * @since 3.2.0
+ *
+ * @see wp_delete_post()
+ *
+ * @param int $postid Post ID.
+ */
+ do_action( 'before_delete_post', $postid );
+
+ delete_post_meta($postid,'_wp_trash_meta_status');
+ delete_post_meta($postid,'_wp_trash_meta_time');
+
+ wp_delete_object_term_relationships($postid, get_object_taxonomies($post->post_type));
+
+ $parent_data = array( 'post_parent' => $post->post_parent );
+ $parent_where = array( 'post_parent' => $postid );
+
+ if ( is_post_type_hierarchical( $post->post_type ) ) {
+ // Point children of this page to its parent, also clean the cache of affected children.
+ $children_query = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_parent = %d AND post_type = %s", $postid, $post->post_type );
+ $children = $wpdb->get_results( $children_query );
+
+ $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => $post->post_type ) );
+ }
+
+ // Do raw query. wp_get_post_revisions() is filtered.
+ $revision_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'revision'", $postid ) );
+ // Use wp_delete_post (via wp_delete_post_revision) again. Ensures any meta/misplaced data gets cleaned up.
+ foreach ( $revision_ids as $revision_id )
+ wp_delete_post_revision( $revision_id );
+
+ // Point all attachments to this post up one level.
+ $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => 'attachment' ) );
+
+ wp_defer_comment_counting( true );
+
+ $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $postid ));
+ foreach ( $comment_ids as $comment_id ) {
+ wp_delete_comment( $comment_id, true );
+ }
+
+ wp_defer_comment_counting( false );
+
+ $post_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d ", $postid ));
+ foreach ( $post_meta_ids as $mid )
+ delete_metadata_by_mid( 'post', $mid );
+
+ /**
+ * Fires immediately before a post is deleted from the database.
+ *
+ * @since 1.2.0
+ *
+ * @param int $postid Post ID.
+ */
+ do_action( 'delete_post', $postid );
+ $result = $wpdb->delete( $wpdb->posts, array( 'ID' => $postid ) );
+ if ( ! $result ) {
+ return false;
+ }
+
+ /**
+ * Fires immediately after a post is deleted from the database.
+ *
+ * @since 2.2.0
+ *
+ * @param int $postid Post ID.
+ */
+ do_action( 'deleted_post', $postid );
+
+ clean_post_cache( $post );
+
+ if ( is_post_type_hierarchical( $post->post_type ) && $children ) {
+ foreach ( $children as $child )
+ clean_post_cache( $child );
+ }
+
+ wp_clear_scheduled_hook('publish_future_post', array( $postid ) );
+
+ /**
+ * Fires after a post is deleted, at the conclusion of wp_delete_post().
+ *
+ * @since 3.2.0
+ *
+ * @see wp_delete_post()
+ *
+ * @param int $postid Post ID.
+ */
+ do_action( 'after_delete_post', $postid );
+
+ return $post;
+}
+
+/**
+ * Reset the page_on_front, show_on_front, and page_for_post settings when
+ * a linked page is deleted or trashed.
+ *
+ * Also ensures the post is no longer sticky.
+ *
+ * @since 3.7.0
+ * @access private
+ *
+ * @param int $post_id Post ID.
+ */
+function _reset_front_page_settings_for_post( $post_id ) {
+ $post = get_post( $post_id );
+ if ( 'page' == $post->post_type ) {
+ /*
+ * If the page is defined in option page_on_front or post_for_posts,
+ * adjust the corresponding options.
+ */
+ if ( get_option( 'page_on_front' ) == $post->ID ) {
+ update_option( 'show_on_front', 'posts' );
+ update_option( 'page_on_front', 0 );
+ }
+ if ( get_option( 'page_for_posts' ) == $post->ID ) {
+ delete_option( 'page_for_posts', 0 );
+ }
+ }
+ unstick_post( $post->ID );
+}
+
+/**
+ * Move a post or page to the Trash
+ *
+ * If trash is disabled, the post or page is permanently deleted.
+ *
+ * @since 2.9.0
+ *
+ * @see wp_delete_post()
+ *
+ * @param int $post_id Optional. Post ID. Default is ID of the global $post
+ * if EMPTY_TRASH_DAYS equals true.
+ * @return false|array|WP_Post|null Post data array, otherwise false.
+ */
+function wp_trash_post( $post_id = 0 ) {
+ if ( !EMPTY_TRASH_DAYS )
+ return wp_delete_post($post_id, true);
+
+ if ( !$post = get_post($post_id, ARRAY_A) )
+ return $post;
+
+ if ( $post['post_status'] == 'trash' )
+ return false;
+
+ /**
+ * Fires before a post is sent to the trash.
+ *
+ * @since 3.3.0
+ *
+ * @param int $post_id Post ID.
+ */
+ do_action( 'wp_trash_post', $post_id );
+
+ add_post_meta($post_id,'_wp_trash_meta_status', $post['post_status']);
+ add_post_meta($post_id,'_wp_trash_meta_time', time());
+
+ $post['post_status'] = 'trash';
+ wp_insert_post( wp_slash( $post ) );
+
+ wp_trash_post_comments($post_id);
+
+ /**
+ * Fires after a post is sent to the trash.
+ *
+ * @since 2.9.0
+ *
+ * @param int $post_id Post ID.
+ */
+ do_action( 'trashed_post', $post_id );
+
+ return $post;
+}
+
+/**
+ * Restore a post or page from the Trash.
+ *
+ * @since 2.9.0
+ *
+ * @param int $post_id Optional. Post ID. Default is ID of the global $post.
+ * @return WP_Post|false WP_Post object. False on failure.
+ */
+function wp_untrash_post( $post_id = 0 ) {
+ if ( !$post = get_post($post_id, ARRAY_A) )
+ return $post;
+
+ if ( $post['post_status'] != 'trash' )
+ return false;
+
+ /**
+ * Fires before a post is restored from the trash.
+ *
+ * @since 2.9.0
+ *
+ * @param int $post_id Post ID.
+ */
+ do_action( 'untrash_post', $post_id );
+
+ $post_status = get_post_meta($post_id, '_wp_trash_meta_status', true);
+
+ $post['post_status'] = $post_status;
+
+ delete_post_meta($post_id, '_wp_trash_meta_status');
+ delete_post_meta($post_id, '_wp_trash_meta_time');
+
+ wp_insert_post( wp_slash( $post ) );
+
+ wp_untrash_post_comments($post_id);
+
+ /**
+ * Fires after a post is restored from the trash.
+ *
+ * @since 2.9.0
+ *
+ * @param int $post_id Post ID.
+ */
+ do_action( 'untrashed_post', $post_id );
+
+ return $post;
+}
+
+/**
+ * Moves comments for a post to the trash.
+ *
+ * @since 2.9.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int|WP_Post|null $post Optional. Post ID or post object. Defaults to global $post.
+ * @return mixed|void False on failure.
+ */
+function wp_trash_post_comments( $post = null ) {
+ global $wpdb;
+
+ $post = get_post($post);
+ if ( empty($post) )
+ return;
+
+ $post_id = $post->ID;
+
+ /**
+ * Fires before comments are sent to the trash.
+ *
+ * @since 2.9.0
+ *
+ * @param int $post_id Post ID.
+ */
+ do_action( 'trash_post_comments', $post_id );
+
+ $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_ID, comment_approved FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id) );
+ if ( empty($comments) )
+ return;
+
+ // Cache current status for each comment.
+ $statuses = array();
+ foreach ( $comments as $comment )
+ $statuses[$comment->comment_ID] = $comment->comment_approved;
+ add_post_meta($post_id, '_wp_trash_meta_comments_status', $statuses);
+
+ // Set status for all comments to post-trashed.
+ $result = $wpdb->update($wpdb->comments, array('comment_approved' => 'post-trashed'), array('comment_post_ID' => $post_id));
+
+ clean_comment_cache( array_keys($statuses) );
+
+ /**
+ * Fires after comments are sent to the trash.
+ *
+ * @since 2.9.0
+ *
+ * @param int $post_id Post ID.
+ * @param array $statuses Array of comment statuses.
+ */
+ do_action( 'trashed_post_comments', $post_id, $statuses );
+
+ return $result;
+}
+
+/**
+ * Restore comments for a post from the trash.
+ *
+ * @since 2.9.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int|WP_Post|null $post Optional. Post ID or post object. Defaults to global $post.
+ * @return true|void
+ */
+function wp_untrash_post_comments( $post = null ) {
+ global $wpdb;
+
+ $post = get_post($post);
+ if ( empty($post) )
+ return;
+
+ $post_id = $post->ID;
+
+ $statuses = get_post_meta($post_id, '_wp_trash_meta_comments_status', true);
+
+ if ( empty($statuses) )
+ return true;
+
+ /**
+ * Fires before comments are restored for a post from the trash.
+ *
+ * @since 2.9.0
+ *
+ * @param int $post_id Post ID.
+ */
+ do_action( 'untrash_post_comments', $post_id );
+
+ // Restore each comment to its original status.
+ $group_by_status = array();
+ foreach ( $statuses as $comment_id => $comment_status )
+ $group_by_status[$comment_status][] = $comment_id;
+
+ foreach ( $group_by_status as $status => $comments ) {
+ // Sanity check. This shouldn't happen.
+ if ( 'post-trashed' == $status ) {
+ $status = '0';
+ }
+ $comments_in = implode( ', ', array_map( 'intval', $comments ) );
+ $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->comments SET comment_approved = %s WHERE comment_ID IN ($comments_in)", $status ) );
+ }
+
+ clean_comment_cache( array_keys($statuses) );
+
+ delete_post_meta($post_id, '_wp_trash_meta_comments_status');
+
+ /**
+ * Fires after comments are restored for a post from the trash.
+ *
+ * @since 2.9.0
+ *
+ * @param int $post_id Post ID.
+ */
+ do_action( 'untrashed_post_comments', $post_id );
+}
+
+/**
+ * Retrieve the list of categories for a post.
+ *
+ * Compatibility layer for themes and plugins. Also an easy layer of abstraction
+ * away from the complexity of the taxonomy layer.
+ *
+ * @since 2.1.0
+ *
+ * @see wp_get_object_terms()
+ *
+ * @param int $post_id Optional. The Post ID. Does not default to the ID of the
+ * global $post. Default 0.
+ * @param array $args Optional. Category arguments. Default empty.
+ * @return array List of categories.
+ */
+function wp_get_post_categories( $post_id = 0, $args = array() ) {
+ $post_id = (int) $post_id;
+
+ $defaults = array('fields' => 'ids');
+ $args = wp_parse_args( $args, $defaults );
+
+ $cats = wp_get_object_terms($post_id, 'category', $args);
+ return $cats;
+}
+
+/**
+ * Retrieve the tags for a post.
+ *
+ * There is only one default for this function, called 'fields' and by default
+ * is set to 'all'. There are other defaults that can be overridden in
+ * {@link wp_get_object_terms()}.
+ *
+ * @since 2.3.0
+ *
+ * @param int $post_id Optional. The Post ID. Does not default to the ID of the
+ * global $post. Default 0.
+ * @param array $args Optional. Overwrite the defaults
+ * @return array List of post tags.
+ */
+function wp_get_post_tags( $post_id = 0, $args = array() ) {
+ return wp_get_post_terms( $post_id, 'post_tag', $args);
+}
+
+/**
+ * Retrieve the terms for a post.
+ *
+ * There is only one default for this function, called 'fields' and by default
+ * is set to 'all'. There are other defaults that can be overridden in
+ * {@link wp_get_object_terms()}.
+ *
+ * @since 2.8.0
+ *
+ * @param int $post_id Optional. The Post ID. Does not default to the ID of the
+ * global $post. Default 0.
+ * @param string $taxonomy Optional. The taxonomy for which to retrieve terms. Default 'post_tag'.
+ * @param array $args Optional. {@link wp_get_object_terms()} arguments. Default empty array.
+ * @return array|WP_Error List of post terms or empty array if no terms were found. WP_Error object
+ * if `$taxonomy` doesn't exist.
+ */
+function wp_get_post_terms( $post_id = 0, $taxonomy = 'post_tag', $args = array() ) {
+ $post_id = (int) $post_id;
+
+ $defaults = array('fields' => 'all');
+ $args = wp_parse_args( $args, $defaults );
+
+ $tags = wp_get_object_terms($post_id, $taxonomy, $args);
+
+ return $tags;
+}
+
+/**
+ * Retrieve a number of recent posts.
+ *
+ * @since 1.0.0
+ *
+ * @see get_posts()
+ *
+ * @param array $args Optional. Arguments to retrieve posts. Default empty array.
+ * @param string $output Optional. Type of output. Accepts ARRAY_A or ''. Default ARRAY_A.
+ * @return array|false Associative array if $output equals ARRAY_A, array or false if no results.
+ */
+function wp_get_recent_posts( $args = array(), $output = ARRAY_A ) {
+
+ if ( is_numeric( $args ) ) {
+ _deprecated_argument( __FUNCTION__, '3.1', __( 'Passing an integer number of posts is deprecated. Pass an array of arguments instead.' ) );
+ $args = array( 'numberposts' => absint( $args ) );
+ }
+
+ // Set default arguments.
+ $defaults = array(
+ 'numberposts' => 10, 'offset' => 0,
+ 'category' => 0, 'orderby' => 'post_date',
+ 'order' => 'DESC', 'include' => '',
+ 'exclude' => '', 'meta_key' => '',
+ 'meta_value' =>'', 'post_type' => 'post', 'post_status' => 'draft, publish, future, pending, private',
+ 'suppress_filters' => true
+ );
+
+ $r = wp_parse_args( $args, $defaults );
+
+ $results = get_posts( $r );
+
+ // Backward compatibility. Prior to 3.1 expected posts to be returned in array.
+ if ( ARRAY_A == $output ){
+ foreach ( $results as $key => $result ) {
+ $results[$key] = get_object_vars( $result );
+ }
+ return $results ? $results : array();
+ }
+
+ return $results ? $results : false;
+
+}
+
+/**
+ * Insert or update a post.
+ *
+ * If the $postarr parameter has 'ID' set to a value, then post will be updated.
+ *
+ * You can set the post date manually, by setting the values for 'post_date'
+ * and 'post_date_gmt' keys. You can close the comments or open the comments by
+ * setting the value for 'comment_status' key.
+ *
+ * @since 1.0.0
+ * @since 4.2.0 Support was added for encoding emoji in the post title, content, and excerpt.
+ * @since 4.4.0 A 'meta_input' array can now be passed to `$postarr` to add post meta data.
+ *
+ * @see sanitize_post()
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array $postarr {
+ * An array of elements that make up a post to update or insert.
+ *
+ * @type int $ID The post ID. If equal to something other than 0,
+ * the post with that ID will be updated. Default 0.
+ * @type int $post_author The ID of the user who added the post. Default is
+ * the current user ID.
+ * @type string $post_date The date of the post. Default is the current time.
+ * @type string $post_date_gmt The date of the post in the GMT timezone. Default is
+ * the value of `$post_date`.
+ * @type mixed $post_content The post content. Default empty.
+ * @type string $post_content_filtered The filtered post content. Default empty.
+ * @type string $post_title The post title. Default empty.
+ * @type string $post_excerpt The post excerpt. Default empty.
+ * @type string $post_status The post status. Default 'draft'.
+ * @type string $post_type The post type. Default 'post'.
+ * @type string $comment_status Whether the post can accept comments. Accepts 'open' or 'closed'.
+ * Default is the value of 'default_comment_status' option.
+ * @type string $ping_status Whether the post can accept pings. Accepts 'open' or 'closed'.
+ * Default is the value of 'default_ping_status' option.
+ * @type string $post_password The password to access the post. Default empty.
+ * @type string $post_name The post name. Default is the sanitized post title.
+ * @type string $to_ping Space or carriage return-separated list of URLs to ping.
+ * Default empty.
+ * @type string $pinged Space or carriage return-separated list of URLs that have
+ * been pinged. Default empty.
+ * @type string $post_modified The date when the post was last modified. Default is
+ * the current time.
+ * @type string $post_modified_gmt The date when the post was last modified in the GMT
+ * timezone. Default is the current time.
+ * @type int $post_parent Set this for the post it belongs to, if any. Default 0.
+ * @type int $menu_order The order the post should be displayed in. Default 0.
+ * @type string $post_mime_type The mime type of the post. Default empty.
+ * @type string $guid Global Unique ID for referencing the post. Default empty.
+ * @type array $tax_input Array of taxonomy terms keyed by their taxonomy name. Default empty.
+ * @type array $meta_input Array of post meta values keyed by their post meta key. Default empty.
+ * }
+ * @param bool $wp_error Optional. Whether to allow return of WP_Error on failure. Default false.
+ * @return int|WP_Error The post ID on success. The value 0 or WP_Error on failure.
+ */
+function wp_insert_post( $postarr, $wp_error = false ) {
+ global $wpdb;
+
+ $user_id = get_current_user_id();
+
+ $defaults = array(
+ 'post_author' => $user_id,
+ 'post_content' => '',
+ 'post_content_filtered' => '',
+ 'post_title' => '',
+ 'post_excerpt' => '',
+ 'post_status' => 'draft',
+ 'post_type' => 'post',
+ 'comment_status' => '',
+ 'ping_status' => '',
+ 'post_password' => '',
+ 'to_ping' => '',
+ 'pinged' => '',
+ 'post_parent' => 0,
+ 'menu_order' => 0,
+ 'guid' => '',
+ 'import_id' => 0,
+ 'context' => '',
+ );
+
+ $postarr = wp_parse_args($postarr, $defaults);
+
+ unset( $postarr[ 'filter' ] );
+
+ $postarr = sanitize_post($postarr, 'db');
+
+ // Are we updating or creating?
+ $post_ID = 0;
+ $update = false;
+ $guid = $postarr['guid'];
+
+ if ( ! empty( $postarr['ID'] ) ) {
+ $update = true;
+
+ // Get the post ID and GUID.
+ $post_ID = $postarr['ID'];
+ $post_before = get_post( $post_ID );
+ if ( is_null( $post_before ) ) {
+ if ( $wp_error ) {
+ return new WP_Error( 'invalid_post', __( 'Invalid post ID.' ) );
+ }
+ return 0;
+ }
+
+ $guid = get_post_field( 'guid', $post_ID );
+ $previous_status = get_post_field('post_status', $post_ID );
+ } else {
+ $previous_status = 'new';
+ }
+
+ $post_type = empty( $postarr['post_type'] ) ? 'post' : $postarr['post_type'];
+
+ $post_title = $postarr['post_title'];
+ $post_content = $postarr['post_content'];
+ $post_excerpt = $postarr['post_excerpt'];
+ if ( isset( $postarr['post_name'] ) ) {
+ $post_name = $postarr['post_name'];
+ }
+
+ $maybe_empty = 'attachment' !== $post_type
+ && ! $post_content && ! $post_title && ! $post_excerpt
+ && post_type_supports( $post_type, 'editor' )
+ && post_type_supports( $post_type, 'title' )
+ && post_type_supports( $post_type, 'excerpt' );
+
+ /**
+ * Filter whether the post should be considered "empty".
+ *
+ * The post is considered "empty" if both:
+ * 1. The post type supports the title, editor, and excerpt fields
+ * 2. The title, editor, and excerpt fields are all empty
+ *
+ * Returning a truthy value to the filter will effectively short-circuit
+ * the new post being inserted, returning 0. If $wp_error is true, a WP_Error
+ * will be returned instead.
+ *
+ * @since 3.3.0
+ *
+ * @param bool $maybe_empty Whether the post should be considered "empty".
+ * @param array $postarr Array of post data.
+ */
+ if ( apply_filters( 'wp_insert_post_empty_content', $maybe_empty, $postarr ) ) {
+ if ( $wp_error ) {
+ return new WP_Error( 'empty_content', __( 'Content, title, and excerpt are empty.' ) );
+ } else {
+ return 0;
+ }
+ }
+
+ $post_status = empty( $postarr['post_status'] ) ? 'draft' : $postarr['post_status'];
+ if ( 'attachment' === $post_type && ! in_array( $post_status, array( 'inherit', 'private', 'trash' ) ) ) {
+ $post_status = 'inherit';
+ }
+
+ if ( ! empty( $postarr['post_category'] ) ) {
+ // Filter out empty terms.
+ $post_category = array_filter( $postarr['post_category'] );
+ }
+
+ // Make sure we set a valid category.
+ if ( empty( $post_category ) || 0 == count( $post_category ) || ! is_array( $post_category ) ) {
+ // 'post' requires at least one category.
+ if ( 'post' == $post_type && 'auto-draft' != $post_status ) {
+ $post_category = array( get_option('default_category') );
+ } else {
+ $post_category = array();
+ }
+ }
+
+ // Don't allow contributors to set the post slug for pending review posts.
+ if ( 'pending' == $post_status && !current_user_can( 'publish_posts' ) ) {
+ $post_name = '';
+ }
+
+ /*
+ * Create a valid post name. Drafts and pending posts are allowed to have
+ * an empty post name.
+ */
+ if ( empty($post_name) ) {
+ if ( !in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) ) {
+ $post_name = sanitize_title($post_title);
+ } else {
+ $post_name = '';
+ }
+ } else {
+ // On updates, we need to check to see if it's using the old, fixed sanitization context.
+ $check_name = sanitize_title( $post_name, '', 'old-save' );
+ if ( $update && strtolower( urlencode( $post_name ) ) == $check_name && get_post_field( 'post_name', $post_ID ) == $check_name ) {
+ $post_name = $check_name;
+ } else { // new post, or slug has changed.
+ $post_name = sanitize_title($post_name);
+ }
+ }
+
+ /*
+ * If the post date is empty (due to having been new or a draft) and status
+ * is not 'draft' or 'pending', set date to now.
+ */
+ if ( empty( $postarr['post_date'] ) || '0000-00-00 00:00:00' == $postarr['post_date'] ) {
+ if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' == $postarr['post_date_gmt'] ) {
+ $post_date = current_time( 'mysql' );
+ } else {
+ $post_date = get_date_from_gmt( $postarr['post_date_gmt'] );
+ }
+ } else {
+ $post_date = $postarr['post_date'];
+ }
+
+ // Validate the date.
+ $mm = substr( $post_date, 5, 2 );
+ $jj = substr( $post_date, 8, 2 );
+ $aa = substr( $post_date, 0, 4 );
+ $valid_date = wp_checkdate( $mm, $jj, $aa, $post_date );
+ if ( ! $valid_date ) {
+ if ( $wp_error ) {
+ return new WP_Error( 'invalid_date', __( 'Whoops, the provided date is invalid.' ) );
+ } else {
+ return 0;
+ }
+ }
+
+ if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' == $postarr['post_date_gmt'] ) {
+ if ( ! in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) ) {
+ $post_date_gmt = get_gmt_from_date( $post_date );
+ } else {
+ $post_date_gmt = '0000-00-00 00:00:00';
+ }
+ } else {
+ $post_date_gmt = $postarr['post_date_gmt'];
+ }
+
+ if ( $update || '0000-00-00 00:00:00' == $post_date ) {
+ $post_modified = current_time( 'mysql' );
+ $post_modified_gmt = current_time( 'mysql', 1 );
+ } else {
+ $post_modified = $post_date;
+ $post_modified_gmt = $post_date_gmt;
+ }
+
+ if ( 'attachment' !== $post_type ) {
+ if ( 'publish' == $post_status ) {
+ $now = gmdate('Y-m-d H:i:59');
+ if ( mysql2date('U', $post_date_gmt, false) > mysql2date('U', $now, false) ) {
+ $post_status = 'future';
+ }
+ } elseif ( 'future' == $post_status ) {
+ $now = gmdate('Y-m-d H:i:59');
+ if ( mysql2date('U', $post_date_gmt, false) <= mysql2date('U', $now, false) ) {
+ $post_status = 'publish';
+ }
+ }
+ }
+
+ // Comment status.
+ if ( empty( $postarr['comment_status'] ) ) {
+ if ( $update ) {
+ $comment_status = 'closed';
+ } else {
+ $comment_status = get_default_comment_status( $post_type );
+ }
+ } else {
+ $comment_status = $postarr['comment_status'];
+ }
+
+ // These variables are needed by compact() later.
+ $post_content_filtered = $postarr['post_content_filtered'];
+ $post_author = isset( $postarr['post_author'] ) ? $postarr['post_author'] : $user_id;
+ $ping_status = empty( $postarr['ping_status'] ) ? get_default_comment_status( $post_type, 'pingback' ) : $postarr['ping_status'];
+ $to_ping = isset( $postarr['to_ping'] ) ? sanitize_trackback_urls( $postarr['to_ping'] ) : '';
+ $pinged = isset( $postarr['pinged'] ) ? $postarr['pinged'] : '';
+ $import_id = isset( $postarr['import_id'] ) ? $postarr['import_id'] : 0;
+
+ /*
+ * The 'wp_insert_post_parent' filter expects all variables to be present.
+ * Previously, these variables would have already been extracted
+ */
+ if ( isset( $postarr['menu_order'] ) ) {
+ $menu_order = (int) $postarr['menu_order'];
+ } else {
+ $menu_order = 0;
+ }
+
+ $post_password = isset( $postarr['post_password'] ) ? $postarr['post_password'] : '';
+ if ( 'private' == $post_status ) {
+ $post_password = '';
+ }
+
+ if ( isset( $postarr['post_parent'] ) ) {
+ $post_parent = (int) $postarr['post_parent'];
+ } else {
+ $post_parent = 0;
+ }
+
+ /**
+ * Filter the post parent -- used to check for and prevent hierarchy loops.
+ *
+ * @since 3.1.0
+ *
+ * @param int $post_parent Post parent ID.
+ * @param int $post_ID Post ID.
+ * @param array $new_postarr Array of parsed post data.
+ * @param array $postarr Array of sanitized, but otherwise unmodified post data.
+ */
+ $post_parent = apply_filters( 'wp_insert_post_parent', $post_parent, $post_ID, compact( array_keys( $postarr ) ), $postarr );
+
+ $post_name = wp_unique_post_slug( $post_name, $post_ID, $post_status, $post_type, $post_parent );
+
+ // Don't unslash.
+ $post_mime_type = isset( $postarr['post_mime_type'] ) ? $postarr['post_mime_type'] : '';
+
+ // Expected_slashed (everything!).
+ $data = compact( 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_content_filtered', 'post_title', 'post_excerpt', 'post_status', 'post_type', 'comment_status', 'ping_status', 'post_password', 'post_name', 'to_ping', 'pinged', 'post_modified', 'post_modified_gmt', 'post_parent', 'menu_order', 'post_mime_type', 'guid' );
+
+ $emoji_fields = array( 'post_title', 'post_content', 'post_excerpt' );
+
+ foreach ( $emoji_fields as $emoji_field ) {
+ if ( isset( $data[ $emoji_field ] ) ) {
+ $charset = $wpdb->get_col_charset( $wpdb->posts, $emoji_field );
+ if ( 'utf8' === $charset ) {
+ $data[ $emoji_field ] = wp_encode_emoji( $data[ $emoji_field ] );
+ }
+ }
+ }
+
+ if ( 'attachment' === $post_type ) {
+ /**
+ * Filter attachment post data before it is updated in or added to the database.
+ *
+ * @since 3.9.0
+ *
+ * @param array $data An array of sanitized attachment post data.
+ * @param array $postarr An array of unsanitized attachment post data.
+ */
+ $data = apply_filters( 'wp_insert_attachment_data', $data, $postarr );
+ } else {
+ /**
+ * Filter slashed post data just before it is inserted into the database.
+ *
+ * @since 2.7.0
+ *
+ * @param array $data An array of slashed post data.
+ * @param array $postarr An array of sanitized, but otherwise unmodified post data.
+ */
+ $data = apply_filters( 'wp_insert_post_data', $data, $postarr );
+ }
+ $data = wp_unslash( $data );
+ $where = array( 'ID' => $post_ID );
+
+ if ( $update ) {
+ /**
+ * Fires immediately before an existing post is updated in the database.
+ *
+ * @since 2.5.0
+ *
+ * @param int $post_ID Post ID.
+ * @param array $data Array of unslashed post data.
+ */
+ do_action( 'pre_post_update', $post_ID, $data );
+ if ( false === $wpdb->update( $wpdb->posts, $data, $where ) ) {
+ if ( $wp_error ) {
+ return new WP_Error('db_update_error', __('Could not update post in the database'), $wpdb->last_error);
+ } else {
+ return 0;
+ }
+ }
+ } else {
+ // If there is a suggested ID, use it if not already present.
+ if ( ! empty( $import_id ) ) {
+ $import_id = (int) $import_id;
+ if ( ! $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE ID = %d", $import_id) ) ) {
+ $data['ID'] = $import_id;
+ }
+ }
+ if ( false === $wpdb->insert( $wpdb->posts, $data ) ) {
+ if ( $wp_error ) {
+ return new WP_Error('db_insert_error', __('Could not insert post into the database'), $wpdb->last_error);
+ } else {
+ return 0;
+ }
+ }
+ $post_ID = (int) $wpdb->insert_id;
+
+ // Use the newly generated $post_ID.
+ $where = array( 'ID' => $post_ID );
+ }
+
+ if ( empty( $data['post_name'] ) && ! in_array( $data['post_status'], array( 'draft', 'pending', 'auto-draft' ) ) ) {
+ $data['post_name'] = wp_unique_post_slug( sanitize_title( $data['post_title'], $post_ID ), $post_ID, $data['post_status'], $post_type, $post_parent );
+ $wpdb->update( $wpdb->posts, array( 'post_name' => $data['post_name'] ), $where );
+ clean_post_cache( $post_ID );
+ }
+
+ if ( is_object_in_taxonomy( $post_type, 'category' ) ) {
+ wp_set_post_categories( $post_ID, $post_category );
+ }
+
+ if ( isset( $postarr['tags_input'] ) && is_object_in_taxonomy( $post_type, 'post_tag' ) ) {
+ wp_set_post_tags( $post_ID, $postarr['tags_input'] );
+ }
+
+ // New-style support for all custom taxonomies.
+ if ( ! empty( $postarr['tax_input'] ) ) {
+ foreach ( $postarr['tax_input'] as $taxonomy => $tags ) {
+ $taxonomy_obj = get_taxonomy($taxonomy);
+ if ( ! $taxonomy_obj ) {
+ /* translators: %s: taxonomy name */
+ _doing_it_wrong( __FUNCTION__, sprintf( __( 'Invalid taxonomy: %s.' ), $taxonomy ), '4.4.0' );
+ continue;
+ }
+
+ // array = hierarchical, string = non-hierarchical.
+ if ( is_array( $tags ) ) {
+ $tags = array_filter($tags);
+ }
+ if ( current_user_can( $taxonomy_obj->cap->assign_terms ) ) {
+ wp_set_post_terms( $post_ID, $tags, $taxonomy );
+ }
+ }
+ }
+
+ if ( ! empty( $postarr['meta_input'] ) ) {
+ foreach ( $postarr['meta_input'] as $field => $value ) {
+ update_post_meta( $post_ID, $field, $value );
+ }
+ }
+
+ $current_guid = get_post_field( 'guid', $post_ID );
+
+ // Set GUID.
+ if ( ! $update && '' == $current_guid ) {
+ $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post_ID ) ), $where );
+ }
+
+ if ( 'attachment' === $postarr['post_type'] ) {
+ if ( ! empty( $postarr['file'] ) ) {
+ update_attached_file( $post_ID, $postarr['file'] );
+ }
+
+ if ( ! empty( $postarr['context'] ) ) {
+ add_post_meta( $post_ID, '_wp_attachment_context', $postarr['context'], true );
+ }
+ }
+
+ clean_post_cache( $post_ID );
+
+ $post = get_post( $post_ID );
+
+ if ( ! empty( $postarr['page_template'] ) && 'page' == $data['post_type'] ) {
+ $post->page_template = $postarr['page_template'];
+ $page_templates = wp_get_theme()->get_page_templates( $post );
+ if ( 'default' != $postarr['page_template'] && ! isset( $page_templates[ $postarr['page_template'] ] ) ) {
+ if ( $wp_error ) {
+ return new WP_Error('invalid_page_template', __('The page template is invalid.'));
+ }
+ update_post_meta( $post_ID, '_wp_page_template', 'default' );
+ } else {
+ update_post_meta( $post_ID, '_wp_page_template', $postarr['page_template'] );
+ }
+ }
+
+ if ( 'attachment' !== $postarr['post_type'] ) {
+ wp_transition_post_status( $data['post_status'], $previous_status, $post );
+ } else {
+ if ( $update ) {
+ /**
+ * Fires once an existing attachment has been updated.
+ *
+ * @since 2.0.0
+ *
+ * @param int $post_ID Attachment ID.
+ */
+ do_action( 'edit_attachment', $post_ID );
+ $post_after = get_post( $post_ID );
+
+ /**
+ * Fires once an existing attachment has been updated.
+ *
+ * @since 4.4.0
+ *
+ * @param int $post_ID Post ID.
+ * @param WP_Post $post_after Post object following the update.
+ * @param WP_Post $post_before Post object before the update.
+ */
+ do_action( 'attachment_updated', $post_ID, $post_after, $post_before );
+ } else {
+
+ /**
+ * Fires once an attachment has been added.
+ *
+ * @since 2.0.0
+ *
+ * @param int $post_ID Attachment ID.
+ */
+ do_action( 'add_attachment', $post_ID );
+ }
+
+ return $post_ID;
+ }
+
+ if ( $update ) {
+ /**
+ * Fires once an existing post has been updated.
+ *
+ * @since 1.2.0
+ *
+ * @param int $post_ID Post ID.
+ * @param WP_Post $post Post object.
+ */
+ do_action( 'edit_post', $post_ID, $post );
+ $post_after = get_post($post_ID);
+
+ /**
+ * Fires once an existing post has been updated.
+ *
+ * @since 3.0.0
+ *
+ * @param int $post_ID Post ID.
+ * @param WP_Post $post_after Post object following the update.
+ * @param WP_Post $post_before Post object before the update.
+ */
+ do_action( 'post_updated', $post_ID, $post_after, $post_before);
+ }
+
+ /**
+ * Fires once a post has been saved.
+ *
+ * The dynamic portion of the hook name, `$post->post_type`, refers to
+ * the post type slug.
+ *
+ * @since 3.7.0
+ *
+ * @param int $post_ID Post ID.
+ * @param WP_Post $post Post object.
+ * @param bool $update Whether this is an existing post being updated or not.
+ */
+ do_action( "save_post_{$post->post_type}", $post_ID, $post, $update );
+
+ /**
+ * Fires once a post has been saved.
+ *
+ * @since 1.5.0
+ *
+ * @param int $post_ID Post ID.
+ * @param WP_Post $post Post object.
+ * @param bool $update Whether this is an existing post being updated or not.
+ */
+ do_action( 'save_post', $post_ID, $post, $update );
+
+ /**
+ * Fires once a post has been saved.
+ *
+ * @since 2.0.0
+ *
+ * @param int $post_ID Post ID.
+ * @param WP_Post $post Post object.
+ * @param bool $update Whether this is an existing post being updated or not.
+ */
+ do_action( 'wp_insert_post', $post_ID, $post, $update );
+
+ return $post_ID;
+}
+
+/**
+ * Update a post with new post data.
+ *
+ * The date does not have to be set for drafts. You can set the date and it will
+ * not be overridden.
+ *
+ * @since 1.0.0
+ *
+ * @param array|object $postarr Optional. Post data. Arrays are expected to be escaped,
+ * objects are not. Default array.
+ * @param bool $wp_error Optional. Allow return of WP_Error on failure. Default false.
+ * @return int|WP_Error The value 0 or WP_Error on failure. The post ID on success.
+ */
+function wp_update_post( $postarr = array(), $wp_error = false ) {
+ if ( is_object($postarr) ) {
+ // Non-escaped post was passed.
+ $postarr = get_object_vars($postarr);
+ $postarr = wp_slash($postarr);
+ }
+
+ // First, get all of the original fields.
+ $post = get_post($postarr['ID'], ARRAY_A);
+
+ if ( is_null( $post ) ) {
+ if ( $wp_error )
+ return new WP_Error( 'invalid_post', __( 'Invalid post ID.' ) );
+ return 0;
+ }
+
+ // Escape data pulled from DB.
+ $post = wp_slash($post);
+
+ // Passed post category list overwrites existing category list if not empty.
+ if ( isset($postarr['post_category']) && is_array($postarr['post_category'])
+ && 0 != count($postarr['post_category']) )
+ $post_cats = $postarr['post_category'];
+ else
+ $post_cats = $post['post_category'];
+
+ // Drafts shouldn't be assigned a date unless explicitly done so by the user.
+ if ( isset( $post['post_status'] ) && in_array($post['post_status'], array('draft', 'pending', 'auto-draft')) && empty($postarr['edit_date']) &&
+ ('0000-00-00 00:00:00' == $post['post_date_gmt']) )
+ $clear_date = true;
+ else
+ $clear_date = false;
+
+ // Merge old and new fields with new fields overwriting old ones.
+ $postarr = array_merge($post, $postarr);
+ $postarr['post_category'] = $post_cats;
+ if ( $clear_date ) {
+ $postarr['post_date'] = current_time('mysql');
+ $postarr['post_date_gmt'] = '';
+ }
+
+ if ($postarr['post_type'] == 'attachment')
+ return wp_insert_attachment($postarr);
+
+ return wp_insert_post( $postarr, $wp_error );
+}
+
+/**
+ * Publish a post by transitioning the post status.
+ *
+ * @since 2.1.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int|WP_Post $post Post ID or post object.
+ */
+function wp_publish_post( $post ) {
+ global $wpdb;
+
+ if ( ! $post = get_post( $post ) )
+ return;
+
+ if ( 'publish' == $post->post_status )
+ return;
+
+ $wpdb->update( $wpdb->posts, array( 'post_status' => 'publish' ), array( 'ID' => $post->ID ) );
+
+ clean_post_cache( $post->ID );
+
+ $old_status = $post->post_status;
+ $post->post_status = 'publish';
+ wp_transition_post_status( 'publish', $old_status, $post );
+
+ /** This action is documented in wp-includes/post-functions.php */
+ do_action( 'edit_post', $post->ID, $post );
+
+ /** This action is documented in wp-includes/post-functions.php */
+ do_action( "save_post_{$post->post_type}", $post->ID, $post, true );
+
+ /** This action is documented in wp-includes/post-functions.php */
+ do_action( 'save_post', $post->ID, $post, true );
+
+ /** This action is documented in wp-includes/post-functions.php */
+ do_action( 'wp_insert_post', $post->ID, $post, true );
+}
+
+/**
+ * Publish future post and make sure post ID has future post status.
+ *
+ * Invoked by cron 'publish_future_post' event. This safeguard prevents cron
+ * from publishing drafts, etc.
+ *
+ * @since 2.5.0
+ *
+ * @param int|WP_Post $post_id Post ID or post object.
+ */
+function check_and_publish_future_post( $post_id ) {
+ $post = get_post($post_id);
+
+ if ( empty($post) )
+ return;
+
+ if ( 'future' != $post->post_status )
+ return;
+
+ $time = strtotime( $post->post_date_gmt . ' GMT' );
+
+ // Uh oh, someone jumped the gun!
+ if ( $time > time() ) {
+ wp_clear_scheduled_hook( 'publish_future_post', array( $post_id ) ); // clear anything else in the system
+ wp_schedule_single_event( $time, 'publish_future_post', array( $post_id ) );
+ return;
+ }
+
+ // wp_publish_post(_ returns no meaningful value.
+ wp_publish_post( $post_id );
+}
+
+/**
+ * Computes a unique slug for the post, when given the desired slug and some post details.
+ *
+ * @since 2.8.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ * @global WP_Rewrite $wp_rewrite
+ *
+ * @param string $slug The desired slug (post_name).
+ * @param int $post_ID Post ID.
+ * @param string $post_status No uniqueness checks are made if the post is still draft or pending.
+ * @param string $post_type Post type.
+ * @param int $post_parent Post parent ID.
+ * @return string Unique slug for the post, based on $post_name (with a -1, -2, etc. suffix)
+ */
+function wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_parent ) {
+ if ( in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) || ( 'inherit' == $post_status && 'revision' == $post_type ) )
+ return $slug;
+
+ global $wpdb, $wp_rewrite;
+
+ $original_slug = $slug;
+
+ $feeds = $wp_rewrite->feeds;
+ if ( ! is_array( $feeds ) )
+ $feeds = array();
+
+ if ( 'attachment' == $post_type ) {
+ // Attachment slugs must be unique across all types.
+ $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND ID != %d LIMIT 1";
+ $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_ID ) );
+
+ /**
+ * Filter whether the post slug would make a bad attachment slug.
+ *
+ * @since 3.1.0
+ *
+ * @param bool $bad_slug Whether the slug would be bad as an attachment slug.
+ * @param string $slug The post slug.
+ */
+ if ( $post_name_check || in_array( $slug, $feeds ) || apply_filters( 'wp_unique_post_slug_is_bad_attachment_slug', false, $slug ) ) {
+ $suffix = 2;
+ do {
+ $alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
+ $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_ID ) );
+ $suffix++;
+ } while ( $post_name_check );
+ $slug = $alt_post_name;
+ }
+ } elseif ( is_post_type_hierarchical( $post_type ) ) {
+ if ( 'nav_menu_item' == $post_type )
+ return $slug;
+
+ /*
+ * Page slugs must be unique within their own trees. Pages are in a separate
+ * namespace than posts so page slugs are allowed to overlap post slugs.
+ */
+ $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1";
+ $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID, $post_parent ) );
+
+ /**
+ * Filter whether the post slug would make a bad hierarchical post slug.
+ *
+ * @since 3.1.0
+ *
+ * @param bool $bad_slug Whether the post slug would be bad in a hierarchical post context.
+ * @param string $slug The post slug.
+ * @param string $post_type Post type.
+ * @param int $post_parent Post parent ID.
+ */
+ if ( $post_name_check || in_array( $slug, $feeds ) || preg_match( "@^($wp_rewrite->pagination_base)?\d+$@", $slug ) || apply_filters( 'wp_unique_post_slug_is_bad_hierarchical_slug', false, $slug, $post_type, $post_parent ) ) {
+ $suffix = 2;
+ do {
+ $alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
+ $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_ID, $post_parent ) );
+ $suffix++;
+ } while ( $post_name_check );
+ $slug = $alt_post_name;
+ }
+ } else {
+ // Post slugs must be unique across all posts.
+ $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1";
+ $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID ) );
+
+ // Prevent new post slugs that could result in URLs that conflict with date archives.
+ $post = get_post( $post_ID );
+ $conflicts_with_date_archive = false;
+ if ( 'post' === $post_type && ( ! $post || $post->post_name !== $slug ) && preg_match( '/^[0-9]+$/', $slug ) && $slug_num = intval( $slug ) ) {
+ $permastructs = array_values( array_filter( explode( '/', get_option( 'permalink_structure' ) ) ) );
+ $postname_index = array_search( '%postname%', $permastructs );
+
+ /*
+ * Potential date clashes are as follows:
+ *
+ * - Any integer in the first permastruct position could be a year.
+ * - An integer between 1 and 12 that follows 'year' conflicts with 'monthnum'.
+ * - An integer between 1 and 31 that follows 'monthnum' conflicts with 'day'.
+ */
+ if ( 0 === $postname_index ||
+ ( $postname_index && '%year%' === $permastructs[ $postname_index - 1 ] && 13 > $slug_num ) ||
+ ( $postname_index && '%monthnum%' === $permastructs[ $postname_index - 1 ] && 32 > $slug_num )
+ ) {
+ $conflicts_with_date_archive = true;
+ }
+ }
+
+ /**
+ * Filter whether the post slug would be bad as a flat slug.
+ *
+ * @since 3.1.0
+ *
+ * @param bool $bad_slug Whether the post slug would be bad as a flat slug.
+ * @param string $slug The post slug.
+ * @param string $post_type Post type.
+ */
+ if ( $post_name_check || in_array( $slug, $feeds ) || $conflicts_with_date_archive || apply_filters( 'wp_unique_post_slug_is_bad_flat_slug', false, $slug, $post_type ) ) {
+ $suffix = 2;
+ do {
+ $alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
+ $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_ID ) );
+ $suffix++;
+ } while ( $post_name_check );
+ $slug = $alt_post_name;
+ }
+ }
+
+ /**
+ * Filter the unique post slug.
+ *
+ * @since 3.3.0
+ *
+ * @param string $slug The post slug.
+ * @param int $post_ID Post ID.
+ * @param string $post_status The post status.
+ * @param string $post_type Post type.
+ * @param int $post_parent Post parent ID
+ * @param string $original_slug The original post slug.
+ */
+ return apply_filters( 'wp_unique_post_slug', $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug );
+}
+
+/**
+ * Truncate a post slug.
+ *
+ * @since 3.6.0
+ * @access private
+ *
+ * @see utf8_uri_encode()
+ *
+ * @param string $slug The slug to truncate.
+ * @param int $length Optional. Max length of the slug. Default 200 (characters).
+ * @return string The truncated slug.
+ */
+function _truncate_post_slug( $slug, $length = 200 ) {
+ if ( strlen( $slug ) > $length ) {
+ $decoded_slug = urldecode( $slug );
+ if ( $decoded_slug === $slug )
+ $slug = substr( $slug, 0, $length );
+ else
+ $slug = utf8_uri_encode( $decoded_slug, $length );
+ }
+
+ return rtrim( $slug, '-' );
+}
+
+/**
+ * Add tags to a post.
+ *
+ * @see wp_set_post_tags()
+ *
+ * @since 2.3.0
+ *
+ * @param int $post_id Optional. The Post ID. Does not default to the ID of the global $post.
+ * @param string|array $tags Optional. An array of tags to set for the post, or a string of tags
+ * separated by commas. Default empty.
+ * @return array|false|WP_Error Array of affected term IDs. WP_Error or false on failure.
+ */
+function wp_add_post_tags( $post_id = 0, $tags = '' ) {
+ return wp_set_post_tags($post_id, $tags, true);
+}
+
+/**
+ * Set the tags for a post.
+ *
+ * @since 2.3.0
+ *
+ * @see wp_set_object_terms()
+ *
+ * @param int $post_id Optional. The Post ID. Does not default to the ID of the global $post.
+ * @param string|array $tags Optional. An array of tags to set for the post, or a string of tags
+ * separated by commas. Default empty.
+ * @param bool $append Optional. If true, don't delete existing tags, just add on. If false,
+ * replace the tags with the new tags. Default false.
+ * @return array|false|WP_Error Array of affected term IDs. WP_Error or false on failure.
+ */
+function wp_set_post_tags( $post_id = 0, $tags = '', $append = false ) {
+ return wp_set_post_terms( $post_id, $tags, 'post_tag', $append);
+}
+
+/**
+ * Set the terms for a post.
+ *
+ * @since 2.8.0
+ *
+ * @see wp_set_object_terms()
+ *
+ * @param int $post_id Optional. The Post ID. Does not default to the ID of the global $post.
+ * @param string|array $tags Optional. An array of terms to set for the post, or a string of terms
+ * separated by commas. Default empty.
+ * @param string $taxonomy Optional. Taxonomy name. Default 'post_tag'.
+ * @param bool $append Optional. If true, don't delete existing terms, just add on. If false,
+ * replace the terms with the new terms. Default false.
+ * @return array|false|WP_Error Array of affected term IDs. WP_Error or false on failure.
+ */
+function wp_set_post_terms( $post_id = 0, $tags = '', $taxonomy = 'post_tag', $append = false ) {
+ $post_id = (int) $post_id;
+
+ if ( !$post_id )
+ return false;
+
+ if ( empty($tags) )
+ $tags = array();
+
+ if ( ! is_array( $tags ) ) {
+ $comma = _x( ',', 'tag delimiter' );
+ if ( ',' !== $comma )
+ $tags = str_replace( $comma, ',', $tags );
+ $tags = explode( ',', trim( $tags, " \n\t\r\0\x0B," ) );
+ }
+
+ /*
+ * Hierarchical taxonomies must always pass IDs rather than names so that
+ * children with the same names but different parents aren't confused.
+ */
+ if ( is_taxonomy_hierarchical( $taxonomy ) ) {
+ $tags = array_unique( array_map( 'intval', $tags ) );
+ }
+
+ return wp_set_object_terms( $post_id, $tags, $taxonomy, $append );
+}
+
+/**
+ * Set categories for a post.
+ *
+ * If the post categories parameter is not set, then the default category is
+ * going used.
+ *
+ * @since 2.1.0
+ *
+ * @param int $post_ID Optional. The Post ID. Does not default to the ID
+ * of the global $post. Default 0.
+ * @param array|int $post_categories Optional. List of categories or ID of category.
+ * Default empty array.
+ * @param bool $append If true, don't delete existing categories, just add on.
+ * If false, replace the categories with the new categories.
+ * @return array|bool|WP_Error
+ */
+function wp_set_post_categories( $post_ID = 0, $post_categories = array(), $append = false ) {
+ $post_ID = (int) $post_ID;
+ $post_type = get_post_type( $post_ID );
+ $post_status = get_post_status( $post_ID );
+ // If $post_categories isn't already an array, make it one:
+ $post_categories = (array) $post_categories;
+ if ( empty( $post_categories ) ) {
+ if ( 'post' == $post_type && 'auto-draft' != $post_status ) {
+ $post_categories = array( get_option('default_category') );
+ $append = false;
+ } else {
+ $post_categories = array();
+ }
+ } elseif ( 1 == count( $post_categories ) && '' == reset( $post_categories ) ) {
+ return true;
+ }
+
+ return wp_set_post_terms( $post_ID, $post_categories, 'category', $append );
+}
+
+/**
+ * Fires actions related to the transitioning of a post's status.
+ *
+ * When a post is saved, the post status is "transitioned" from one status to another,
+ * though this does not always mean the status has actually changed before and after
+ * the save. This function fires a number of action hooks related to that transition:
+ * the generic 'transition_post_status' action, as well as the dynamic hooks
+ * `"{$old_status}_to_{$new_status}"` and `"{$new_status}_{$post->post_type}"`. Note
+ * that the function does not transition the post object in the database.
+ *
+ * For instance: When publishing a post for the first time, the post status may transition
+ * from 'draft' – or some other status – to 'publish'. However, if a post is already
+ * published and is simply being updated, the "old" and "new" statuses may both be 'publish'
+ * before and after the transition.
+ *
+ * @since 2.3.0
+ *
+ * @param string $new_status Transition to this post status.
+ * @param string $old_status Previous post status.
+ * @param WP_Post $post Post data.
+ */
+function wp_transition_post_status( $new_status, $old_status, $post ) {
+ /**
+ * Fires when a post is transitioned from one status to another.
+ *
+ * @since 2.3.0
+ *
+ * @param string $new_status New post status.
+ * @param string $old_status Old post status.
+ * @param WP_Post $post Post object.
+ */
+ do_action( 'transition_post_status', $new_status, $old_status, $post );
+
+ /**
+ * Fires when a post is transitioned from one status to another.
+ *
+ * The dynamic portions of the hook name, `$new_status` and `$old status`,
+ * refer to the old and new post statuses, respectively.
+ *
+ * @since 2.3.0
+ *
+ * @param WP_Post $post Post object.
+ */
+ do_action( "{$old_status}_to_{$new_status}", $post );
+
+ /**
+ * Fires when a post is transitioned from one status to another.
+ *
+ * The dynamic portions of the hook name, `$new_status` and `$post->post_type`,
+ * refer to the new post status and post type, respectively.
+ *
+ * Please note: When this action is hooked using a particular post status (like
+ * 'publish', as `publish_{$post->post_type}`), it will fire both when a post is
+ * first transitioned to that status from something else, as well as upon
+ * subsequent post updates (old and new status are both the same).
+ *
+ * Therefore, if you are looking to only fire a callback when a post is first
+ * transitioned to a status, use the {@see 'transition_post_status'} hook instead.
+ *
+ * @since 2.3.0
+ *
+ * @param int $post_id Post ID.
+ * @param WP_Post $post Post object.
+ */
+ do_action( "{$new_status}_{$post->post_type}", $post->ID, $post );
+}
+
+//
+// Comment, trackback, and pingback functions.
+//
+
+/**
+ * Add a URL to those already pinged.
+ *
+ * @since 1.5.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $post_id Post ID.
+ * @param string $uri Ping URI.
+ * @return int|false How many rows were updated.
+ */
+function add_ping( $post_id, $uri ) {
+ global $wpdb;
+ $pung = $wpdb->get_var( $wpdb->prepare( "SELECT pinged FROM $wpdb->posts WHERE ID = %d", $post_id ));
+ $pung = trim($pung);
+ $pung = preg_split('/\s/', $pung);
+ $pung[] = $uri;
+ $new = implode("\n", $pung);
+
+ /**
+ * Filter the new ping URL to add for the given post.
+ *
+ * @since 2.0.0
+ *
+ * @param string $new New ping URL to add.
+ */
+ $new = apply_filters( 'add_ping', $new );
+
+ // expected_slashed ($new).
+ $new = wp_unslash($new);
+ return $wpdb->update( $wpdb->posts, array( 'pinged' => $new ), array( 'ID' => $post_id ) );
+}
+
+/**
+ * Retrieve enclosures already enclosed for a post.
+ *
+ * @since 1.5.0
+ *
+ * @param int $post_id Post ID.
+ * @return array List of enclosures.
+ */
+function get_enclosed( $post_id ) {
+ $custom_fields = get_post_custom( $post_id );
+ $pung = array();
+ if ( !is_array( $custom_fields ) )
+ return $pung;
+
+ foreach ( $custom_fields as $key => $val ) {
+ if ( 'enclosure' != $key || !is_array( $val ) )
+ continue;
+ foreach ( $val as $enc ) {
+ $enclosure = explode( "\n", $enc );
+ $pung[] = trim( $enclosure[ 0 ] );
+ }
+ }
+
+ /**
+ * Filter the list of enclosures already enclosed for the given post.
+ *
+ * @since 2.0.0
+ *
+ * @param array $pung Array of enclosures for the given post.
+ * @param int $post_id Post ID.
+ */
+ return apply_filters( 'get_enclosed', $pung, $post_id );
+}
+
+/**
+ * Retrieve URLs already pinged for a post.
+ *
+ * @since 1.5.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $post_id Post ID.
+ * @return array
+ */
+function get_pung( $post_id ) {
+ global $wpdb;
+ $pung = $wpdb->get_var( $wpdb->prepare( "SELECT pinged FROM $wpdb->posts WHERE ID = %d", $post_id ));
+ $pung = trim($pung);
+ $pung = preg_split('/\s/', $pung);
+
+ /**
+ * Filter the list of already-pinged URLs for the given post.
+ *
+ * @since 2.0.0
+ *
+ * @param array $pung Array of URLs already pinged for the given post.
+ */
+ return apply_filters( 'get_pung', $pung );
+}
+
+/**
+ * Retrieve URLs that need to be pinged.
+ *
+ * @since 1.5.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $post_id Post ID
+ * @return array
+ */
+function get_to_ping( $post_id ) {
+ global $wpdb;
+ $to_ping = $wpdb->get_var( $wpdb->prepare( "SELECT to_ping FROM $wpdb->posts WHERE ID = %d", $post_id ));
+ $to_ping = sanitize_trackback_urls( $to_ping );
+ $to_ping = preg_split('/\s/', $to_ping, -1, PREG_SPLIT_NO_EMPTY);
+
+ /**
+ * Filter the list of URLs yet to ping for the given post.
+ *
+ * @since 2.0.0
+ *
+ * @param array $to_ping List of URLs yet to ping.
+ */
+ return apply_filters( 'get_to_ping', $to_ping );
+}
+
+/**
+ * Do trackbacks for a list of URLs.
+ *
+ * @since 1.0.0
+ *
+ * @param string $tb_list Comma separated list of URLs.
+ * @param int $post_id Post ID.
+ */
+function trackback_url_list( $tb_list, $post_id ) {
+ if ( ! empty( $tb_list ) ) {
+ // Get post data.
+ $postdata = get_post( $post_id, ARRAY_A );
+
+ // Form an excerpt.
+ $excerpt = strip_tags( $postdata['post_excerpt'] ? $postdata['post_excerpt'] : $postdata['post_content'] );
+
+ if ( strlen( $excerpt ) > 255 ) {
+ $excerpt = substr( $excerpt, 0, 252 ) . '…';
+ }
+
+ $trackback_urls = explode( ',', $tb_list );
+ foreach ( (array) $trackback_urls as $tb_url ) {
+ $tb_url = trim( $tb_url );
+ trackback( $tb_url, wp_unslash( $postdata['post_title'] ), $excerpt, $post_id );
+ }
+ }
+}
+
+//
+// Page functions
+//
+
+/**
+ * Get a list of page IDs.
+ *
+ * @since 2.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @return array List of page IDs.
+ */
+function get_all_page_ids() {
+ global $wpdb;
+
+ $page_ids = wp_cache_get('all_page_ids', 'posts');
+ if ( ! is_array( $page_ids ) ) {
+ $page_ids = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE post_type = 'page'");
+ wp_cache_add('all_page_ids', $page_ids, 'posts');
+ }
+
+ return $page_ids;
+}
+
+/**
+ * Retrieves page data given a page ID or page object.
+ *
+ * Use get_post() instead of get_page().
+ *
+ * @since 1.5.1
+ * @deprecated 3.5.0 Use get_post()
+ *
+ * @param mixed $page Page object or page ID. Passed by reference.
+ * @param string $output Optional. What to output. Accepts OBJECT, ARRAY_A, or ARRAY_N.
+ * Default OBJECT.
+ * @param string $filter Optional. How the return value should be filtered. Accepts 'raw',
+ * 'edit', 'db', 'display'. Default 'raw'.
+ * @return WP_Post|array|null WP_Post on success or null on failure.
+ */
+function get_page( $page, $output = OBJECT, $filter = 'raw') {
+ return get_post( $page, $output, $filter );
+}
+
+/**
+ * Retrieves a page given its path.
+ *
+ * @since 2.1.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $page_path Page path.
+ * @param string $output Optional. Output type. Accepts OBJECT, ARRAY_N, or ARRAY_A.
+ * Default OBJECT.
+ * @param string|array $post_type Optional. Post type or array of post types. Default 'page'.
+ * @return WP_Post|array|void WP_Post on success.
+ */
+function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) {
+ global $wpdb;
+
+ $page_path = rawurlencode(urldecode($page_path));
+ $page_path = str_replace('%2F', '/', $page_path);
+ $page_path = str_replace('%20', ' ', $page_path);
+ $parts = explode( '/', trim( $page_path, '/' ) );
+ $parts = esc_sql( $parts );
+ $parts = array_map( 'sanitize_title_for_query', $parts );
+
+ $in_string = "'" . implode( "','", $parts ) . "'";
+
+ if ( is_array( $post_type ) ) {
+ $post_types = $post_type;
+ } else {
+ $post_types = array( $post_type, 'attachment' );
+ }
+
+ $post_types = esc_sql( $post_types );
+ $post_type_in_string = "'" . implode( "','", $post_types ) . "'";
+ $sql = "
+ SELECT ID, post_name, post_parent, post_type
+ FROM $wpdb->posts
+ WHERE post_name IN ($in_string)
+ AND post_type IN ($post_type_in_string)
+ ";
+
+ $pages = $wpdb->get_results( $sql, OBJECT_K );
+
+ $revparts = array_reverse( $parts );
+
+ $foundid = 0;
+ foreach ( (array) $pages as $page ) {
+ if ( $page->post_name == $revparts[0] ) {
+ $count = 0;
+ $p = $page;
+ while ( $p->post_parent != 0 && isset( $pages[ $p->post_parent ] ) ) {
+ $count++;
+ $parent = $pages[ $p->post_parent ];
+ if ( ! isset( $revparts[ $count ] ) || $parent->post_name != $revparts[ $count ] )
+ break;
+ $p = $parent;
+ }
+
+ if ( $p->post_parent == 0 && $count+1 == count( $revparts ) && $p->post_name == $revparts[ $count ] ) {
+ $foundid = $page->ID;
+ if ( $page->post_type == $post_type )
+ break;
+ }
+ }
+ }
+
+ if ( $foundid ) {
+ return get_post( $foundid, $output );
+ }
+}
+
+/**
+ * Retrieve a page given its title.
+ *
+ * @since 2.1.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $page_title Page title
+ * @param string $output Optional. Output type. OBJECT, ARRAY_N, or ARRAY_A.
+ * Default OBJECT.
+ * @param string|array $post_type Optional. Post type or array of post types. Default 'page'.
+ * @return WP_Post|array|void WP_Post on success or null on failure
+ */
+function get_page_by_title( $page_title, $output = OBJECT, $post_type = 'page' ) {
+ global $wpdb;
+
+ if ( is_array( $post_type ) ) {
+ $post_type = esc_sql( $post_type );
+ $post_type_in_string = "'" . implode( "','", $post_type ) . "'";
+ $sql = $wpdb->prepare( "
+ SELECT ID
+ FROM $wpdb->posts
+ WHERE post_title = %s
+ AND post_type IN ($post_type_in_string)
+ ", $page_title );
+ } else {
+ $sql = $wpdb->prepare( "
+ SELECT ID
+ FROM $wpdb->posts
+ WHERE post_title = %s
+ AND post_type = %s
+ ", $page_title, $post_type );
+ }
+
+ $page = $wpdb->get_var( $sql );
+
+ if ( $page ) {
+ return get_post( $page, $output );
+ }
+}
+
+/**
+ * Identify descendants of a given page ID in a list of page objects.
+ *
+ * Descendants are identified from the `$pages` array passed to the function. No database queries are performed.
+ *
+ * @since 1.5.1
+ *
+ * @param int $page_id Page ID.
+ * @param array $pages List of page objects from which descendants should be identified.
+ * @return array List of page children.
+ */
+function get_page_children( $page_id, $pages ) {
+ // Build a hash of ID -> children.
+ $children = array();
+ foreach ( (array) $pages as $page ) {
+ $children[ intval( $page->post_parent ) ][] = $page;
+ }
+
+ $page_list = array();
+
+ // Start the search by looking at immediate children.
+ if ( isset( $children[ $page_id ] ) ) {
+ // Always start at the end of the stack in order to preserve original `$pages` order.
+ $to_look = array_reverse( $children[ $page_id ] );
+
+ while ( $to_look ) {
+ $p = array_pop( $to_look );
+ $page_list[] = $p;
+ if ( isset( $children[ $p->ID ] ) ) {
+ foreach ( array_reverse( $children[ $p->ID ] ) as $child ) {
+ // Append to the `$to_look` stack to descend the tree.
+ $to_look[] = $child;
+ }
+ }
+ }
+ }
+
+ return $page_list;
+}
+
+/**
+ * Order the pages with children under parents in a flat list.
+ *
+ * It uses auxiliary structure to hold parent-children relationships and
+ * runs in O(N) complexity
+ *
+ * @since 2.0.0
+ *
+ * @param array $pages Posts array, passed by reference.
+ * @param int $page_id Optional. Parent page ID. Default 0.
+ * @return array A list arranged by hierarchy. Children immediately follow their parents.
+ */
+function get_page_hierarchy( &$pages, $page_id = 0 ) {
+ if ( empty( $pages ) ) {
+ return array();
+ }
+
+ $children = array();
+ foreach ( (array) $pages as $p ) {
+ $parent_id = intval( $p->post_parent );
+ $children[ $parent_id ][] = $p;
+ }
+
+ $result = array();
+ _page_traverse_name( $page_id, $children, $result );
+
+ return $result;
+}
+
+/**
+ * Traverse and return all the nested children post names of a root page.
+ *
+ * $children contains parent-children relations
+ *
+ * @since 2.9.0
+ *
+ * @see _page_traverse_name()
+ *
+ * @param int $page_id Page ID.
+ * @param array &$children Parent-children relations, passed by reference.
+ * @param array &$result Result, passed by reference.
+ */
+function _page_traverse_name( $page_id, &$children, &$result ){
+ if ( isset( $children[ $page_id ] ) ){
+ foreach ( (array)$children[ $page_id ] as $child ) {
+ $result[ $child->ID ] = $child->post_name;
+ _page_traverse_name( $child->ID, $children, $result );
+ }
+ }
+}
+
+/**
+ * Build URI for a page.
+ *
+ * Sub pages will be in the "directory" under the parent page post name.
+ *
+ * @since 1.5.0
+ *
+ * @param WP_Post|object|int $page Page object or page ID.
+ * @return string|false Page URI, false on error.
+ */
+function get_page_uri( $page ) {
+ $page = get_post( $page );
+
+ if ( ! $page )
+ return false;
+
+ $uri = $page->post_name;
+
+ foreach ( $page->ancestors as $parent ) {
+ $parent = get_post( $parent );
+ if ( 'publish' === $parent->post_status ) {
+ $uri = $parent->post_name . '/' . $uri;
+ }
+ }
+
+ /**
+ * Filter the URI for a page.
+ *
+ * @since 4.4.0
+ *
+ * @param string $uri Page URI.
+ * @param WP_Post $page Page object.
+ */
+ return apply_filters( 'get_page_uri', $uri, $page );
+}
+
+/**
+ * Retrieve a list of pages.
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @since 1.5.0
+ *
+ * @param array|string $args {
+ * Optional. Array or string of arguments to retrieve pages.
+ *
+ * @type int $child_of Page ID to return child and grandchild pages of. Note: The value
+ * of `$hierarchical` has no bearing on whether `$child_of` returns
+ * hierarchical results. Default 0, or no restriction.
+ * @type string $sort_order How to sort retrieved pages. Accepts 'ASC', 'DESC'. Default 'ASC'.
+ * @type string $sort_column What columns to sort pages by, comma-separated. Accepts 'post_author',
+ * 'post_date', 'post_title', 'post_name', 'post_modified', 'menu_order',
+ * 'post_modified_gmt', 'post_parent', 'ID', 'rand', 'comment_count'.
+ * 'post_' can be omitted for any values that start with it.
+ * Default 'post_title'.
+ * @type bool $hierarchical Whether to return pages hierarchically. If false in conjunction with
+ * `$child_of` also being false, both arguments will be disregarded.
+ * Default true.
+ * @type array $exclude Array of page IDs to exclude. Default empty array.
+ * @type array $include Array of page IDs to include. Cannot be used with `$child_of`,
+ * `$parent`, `$exclude`, `$meta_key`, `$meta_value`, or `$hierarchical`.
+ * Default empty array.
+ * @type string $meta_key Only include pages with this meta key. Default empty.
+ * @type string $meta_value Only include pages with this meta value. Requires `$meta_key`.
+ * Default empty.
+ * @type string $authors A comma-separated list of author IDs. Default empty.
+ * @type int $parent Page ID to return direct children of. Default -1, or no restriction.
+ * @type string|array $exclude_tree Comma-separated string or array of page IDs to exclude.
+ * Default empty array.
+ * @type int $number The number of pages to return. Default 0, or all pages.
+ * @type int $offset The number of pages to skip before returning. Requires `$number`.
+ * Default 0.
+ * @type string $post_type The post type to query. Default 'page'.
+ * @type string $post_status A comma-separated list of post status types to include.
+ * Default 'publish'.
+ * }
+ * @return array|false List of pages matching defaults or `$args`.
+ */
+function get_pages( $args = array() ) {
+ global $wpdb;
+
+ $defaults = array(
+ 'child_of' => 0, 'sort_order' => 'ASC',
+ 'sort_column' => 'post_title', 'hierarchical' => 1,
+ 'exclude' => array(), 'include' => array(),
+ 'meta_key' => '', 'meta_value' => '',
+ 'authors' => '', 'parent' => -1, 'exclude_tree' => array(),
+ 'number' => '', 'offset' => 0,
+ 'post_type' => 'page', 'post_status' => 'publish',
+ );
+
+ $r = wp_parse_args( $args, $defaults );
+
+ $number = (int) $r['number'];
+ $offset = (int) $r['offset'];
+ $child_of = (int) $r['child_of'];
+ $hierarchical = $r['hierarchical'];
+ $exclude = $r['exclude'];
+ $meta_key = $r['meta_key'];
+ $meta_value = $r['meta_value'];
+ $parent = $r['parent'];
+ $post_status = $r['post_status'];
+
+ // Make sure the post type is hierarchical.
+ $hierarchical_post_types = get_post_types( array( 'hierarchical' => true ) );
+ if ( ! in_array( $r['post_type'], $hierarchical_post_types ) ) {
+ return false;
+ }
+
+ if ( $parent > 0 && ! $child_of ) {
+ $hierarchical = false;
+ }
+
+ // Make sure we have a valid post status.
+ if ( ! is_array( $post_status ) ) {
+ $post_status = explode( ',', $post_status );
+ }
+ if ( array_diff( $post_status, get_post_stati() ) ) {
+ return false;
+ }
+
+ // $args can be whatever, only use the args defined in defaults to compute the key.
+ $key = md5( serialize( wp_array_slice_assoc( $r, array_keys( $defaults ) ) ) );
+ $last_changed = wp_cache_get( 'last_changed', 'posts' );
+ if ( ! $last_changed ) {
+ $last_changed = microtime();
+ wp_cache_set( 'last_changed', $last_changed, 'posts' );
+ }
+
+ $cache_key = "get_pages:$key:$last_changed";
+ if ( $cache = wp_cache_get( $cache_key, 'posts' ) ) {
+ // Convert to WP_Post instances.
+ $pages = array_map( 'get_post', $cache );
+ /** This filter is documented in wp-includes/post-functions.php */
+ $pages = apply_filters( 'get_pages', $pages, $r );
+ return $pages;
+ }
+
+ $inclusions = '';
+ if ( ! empty( $r['include'] ) ) {
+ $child_of = 0; //ignore child_of, parent, exclude, meta_key, and meta_value params if using include
+ $parent = -1;
+ $exclude = '';
+ $meta_key = '';
+ $meta_value = '';
+ $hierarchical = false;
+ $incpages = wp_parse_id_list( $r['include'] );
+ if ( ! empty( $incpages ) ) {
+ $inclusions = ' AND ID IN (' . implode( ',', $incpages ) . ')';
+ }
+ }
+
+ $exclusions = '';
+ if ( ! empty( $exclude ) ) {
+ $expages = wp_parse_id_list( $exclude );
+ if ( ! empty( $expages ) ) {
+ $exclusions = ' AND ID NOT IN (' . implode( ',', $expages ) . ')';
+ }
+ }
+
+ $author_query = '';
+ if ( ! empty( $r['authors'] ) ) {
+ $post_authors = preg_split( '/[\s,]+/', $r['authors'] );
+
+ if ( ! empty( $post_authors ) ) {
+ foreach ( $post_authors as $post_author ) {
+ //Do we have an author id or an author login?
+ if ( 0 == intval($post_author) ) {
+ $post_author = get_user_by('login', $post_author);
+ if ( empty( $post_author ) ) {
+ continue;
+ }
+ if ( empty( $post_author->ID ) ) {
+ continue;
+ }
+ $post_author = $post_author->ID;
+ }
+
+ if ( '' == $author_query ) {
+ $author_query = $wpdb->prepare(' post_author = %d ', $post_author);
+ } else {
+ $author_query .= $wpdb->prepare(' OR post_author = %d ', $post_author);
+ }
+ }
+ if ( '' != $author_query ) {
+ $author_query = " AND ($author_query)";
+ }
+ }
+ }
+
+ $join = '';
+ $where = "$exclusions $inclusions ";
+ if ( '' !== $meta_key || '' !== $meta_value ) {
+ $join = " LEFT JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id )";
+
+ // meta_key and meta_value might be slashed
+ $meta_key = wp_unslash($meta_key);
+ $meta_value = wp_unslash($meta_value);
+ if ( '' !== $meta_key ) {
+ $where .= $wpdb->prepare(" AND $wpdb->postmeta.meta_key = %s", $meta_key);
+ }
+ if ( '' !== $meta_value ) {
+ $where .= $wpdb->prepare(" AND $wpdb->postmeta.meta_value = %s", $meta_value);
+ }
+
+ }
+
+ if ( is_array( $parent ) ) {
+ $post_parent__in = implode( ',', array_map( 'absint', (array) $parent ) );
+ if ( ! empty( $post_parent__in ) ) {
+ $where .= " AND post_parent IN ($post_parent__in)";
+ }
+ } elseif ( $parent >= 0 ) {
+ $where .= $wpdb->prepare(' AND post_parent = %d ', $parent);
+ }
+
+ if ( 1 == count( $post_status ) ) {
+ $where_post_type = $wpdb->prepare( "post_type = %s AND post_status = %s", $r['post_type'], reset( $post_status ) );
+ } else {
+ $post_status = implode( "', '", $post_status );
+ $where_post_type = $wpdb->prepare( "post_type = %s AND post_status IN ('$post_status')", $r['post_type'] );
+ }
+
+ $orderby_array = array();
+ $allowed_keys = array( 'author', 'post_author', 'date', 'post_date', 'title', 'post_title', 'name', 'post_name', 'modified',
+ 'post_modified', 'modified_gmt', 'post_modified_gmt', 'menu_order', 'parent', 'post_parent',
+ 'ID', 'rand', 'comment_count' );
+
+ foreach ( explode( ',', $r['sort_column'] ) as $orderby ) {
+ $orderby = trim( $orderby );
+ if ( ! in_array( $orderby, $allowed_keys ) ) {
+ continue;
+ }
+
+ switch ( $orderby ) {
+ case 'menu_order':
+ break;
+ case 'ID':
+ $orderby = "$wpdb->posts.ID";
+ break;
+ case 'rand':
+ $orderby = 'RAND()';
+ break;
+ case 'comment_count':
+ $orderby = "$wpdb->posts.comment_count";
+ break;
+ default:
+ if ( 0 === strpos( $orderby, 'post_' ) ) {
+ $orderby = "$wpdb->posts." . $orderby;
+ } else {
+ $orderby = "$wpdb->posts.post_" . $orderby;
+ }
+ }
+
+ $orderby_array[] = $orderby;
+
+ }
+ $sort_column = ! empty( $orderby_array ) ? implode( ',', $orderby_array ) : "$wpdb->posts.post_title";
+
+ $sort_order = strtoupper( $r['sort_order'] );
+ if ( '' !== $sort_order && ! in_array( $sort_order, array( 'ASC', 'DESC' ) ) ) {
+ $sort_order = 'ASC';
+ }
+
+ $query = "SELECT * FROM $wpdb->posts $join WHERE ($where_post_type) $where ";
+ $query .= $author_query;
+ $query .= " ORDER BY " . $sort_column . " " . $sort_order ;
+
+ if ( ! empty( $number ) ) {
+ $query .= ' LIMIT ' . $offset . ',' . $number;
+ }
+
+ $pages = $wpdb->get_results($query);
+
+ if ( empty($pages) ) {
+ /** This filter is documented in wp-includes/post-functions.php */
+ $pages = apply_filters( 'get_pages', array(), $r );
+ return $pages;
+ }
+
+ // Sanitize before caching so it'll only get done once.
+ $num_pages = count($pages);
+ for ($i = 0; $i < $num_pages; $i++) {
+ $pages[$i] = sanitize_post($pages[$i], 'raw');
+ }
+
+ // Update cache.
+ update_post_cache( $pages );
+
+ if ( $child_of || $hierarchical ) {
+ $pages = get_page_children($child_of, $pages);
+ }
+
+ if ( ! empty( $r['exclude_tree'] ) ) {
+ $exclude = wp_parse_id_list( $r['exclude_tree'] );
+ foreach ( $exclude as $id ) {
+ $children = get_page_children( $id, $pages );
+ foreach ( $children as $child ) {
+ $exclude[] = $child->ID;
+ }
+ }
+
+ $num_pages = count( $pages );
+ for ( $i = 0; $i < $num_pages; $i++ ) {
+ if ( in_array( $pages[$i]->ID, $exclude ) ) {
+ unset( $pages[$i] );
+ }
+ }
+ }
+
+ $page_structure = array();
+ foreach ( $pages as $page ) {
+ $page_structure[] = $page->ID;
+ }
+
+ wp_cache_set( $cache_key, $page_structure, 'posts' );
+
+ // Convert to WP_Post instances
+ $pages = array_map( 'get_post', $pages );
+
+ /**
+ * Filter the retrieved list of pages.
+ *
+ * @since 2.1.0
+ *
+ * @param array $pages List of pages to retrieve.
+ * @param array $r Array of get_pages() arguments.
+ */
+ return apply_filters( 'get_pages', $pages, $r );
+}
+
+//
+// Attachment functions
+//
+
+/**
+ * Check if the attachment URI is local one and is really an attachment.
+ *
+ * @since 2.0.0
+ *
+ * @param string $url URL to check
+ * @return bool True on success, false on failure.
+ */
+function is_local_attachment($url) {
+ if (strpos($url, home_url()) === false)
+ return false;
+ if (strpos($url, home_url('/?attachment_id=')) !== false)
+ return true;
+ if ( $id = url_to_postid($url) ) {
+ $post = get_post($id);
+ if ( 'attachment' == $post->post_type )
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Insert an attachment.
+ *
+ * If you set the 'ID' in the $args parameter, it will mean that you are
+ * updating and attempt to update the attachment. You can also set the
+ * attachment name or title by setting the key 'post_name' or 'post_title'.
+ *
+ * You can set the dates for the attachment manually by setting the 'post_date'
+ * and 'post_date_gmt' keys' values.
+ *
+ * By default, the comments will use the default settings for whether the
+ * comments are allowed. You can close them manually or keep them open by
+ * setting the value for the 'comment_status' key.
+ *
+ * @since 2.0.0
+ *
+ * @see wp_insert_post()
+ *
+ * @param string|array $args Arguments for inserting an attachment.
+ * @param string $file Optional. Filename.
+ * @param int $parent Optional. Parent post ID.
+ * @return int Attachment ID.
+ */
+function wp_insert_attachment( $args, $file = false, $parent = 0 ) {
+ $defaults = array(
+ 'file' => $file,
+ 'post_parent' => 0
+ );
+
+ $data = wp_parse_args( $args, $defaults );
+
+ if ( ! empty( $parent ) ) {
+ $data['post_parent'] = $parent;
+ }
+
+ $data['post_type'] = 'attachment';
+
+ return wp_insert_post( $data );
+}
+
+/**
+ * Trash or delete an attachment.
+ *
+ * When an attachment is permanently deleted, the file will also be removed.
+ * Deletion removes all post meta fields, taxonomy, comments, etc. associated
+ * with the attachment (except the main post).
+ *
+ * The attachment is moved to the trash instead of permanently deleted unless trash
+ * for media is disabled, item is already in the trash, or $force_delete is true.
+ *
+ * @since 2.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $post_id Attachment ID.
+ * @param bool $force_delete Optional. Whether to bypass trash and force deletion.
+ * Default false.
+ * @return mixed False on failure. Post data on success.
+ */
+function wp_delete_attachment( $post_id, $force_delete = false ) {
+ global $wpdb;
+
+ if ( !$post = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d", $post_id) ) )
+ return $post;
+
+ if ( 'attachment' != $post->post_type )
+ return false;
+
+ if ( !$force_delete && EMPTY_TRASH_DAYS && MEDIA_TRASH && 'trash' != $post->post_status )
+ return wp_trash_post( $post_id );
+
+ delete_post_meta($post_id, '_wp_trash_meta_status');
+ delete_post_meta($post_id, '_wp_trash_meta_time');
+
+ $meta = wp_get_attachment_metadata( $post_id );
+ $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true );
+ $file = get_attached_file( $post_id );
+
+ if ( is_multisite() )
+ delete_transient( 'dirsize_cache' );
+
+ /**
+ * Fires before an attachment is deleted, at the start of wp_delete_attachment().
+ *
+ * @since 2.0.0
+ *
+ * @param int $post_id Attachment ID.
+ */
+ do_action( 'delete_attachment', $post_id );
+
+ wp_delete_object_term_relationships($post_id, array('category', 'post_tag'));
+ wp_delete_object_term_relationships($post_id, get_object_taxonomies($post->post_type));
+
+ // Delete all for any posts.
+ delete_metadata( 'post', null, '_thumbnail_id', $post_id, true );
+
+ wp_defer_comment_counting( true );
+
+ $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id ));
+ foreach ( $comment_ids as $comment_id ) {
+ wp_delete_comment( $comment_id, true );
+ }
+
+ wp_defer_comment_counting( false );
+
+ $post_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d ", $post_id ));
+ foreach ( $post_meta_ids as $mid )
+ delete_metadata_by_mid( 'post', $mid );
+
+ /** This action is documented in wp-includes/post-functions.php */
+ do_action( 'delete_post', $post_id );
+ $result = $wpdb->delete( $wpdb->posts, array( 'ID' => $post_id ) );
+ if ( ! $result ) {
+ return false;
+ }
+ /** This action is documented in wp-includes/post-functions.php */
+ do_action( 'deleted_post', $post_id );
+
+ $uploadpath = wp_upload_dir();
+
+ if ( ! empty($meta['thumb']) ) {
+ // Don't delete the thumb if another attachment uses it.
+ if (! $wpdb->get_row( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attachment_metadata' AND meta_value LIKE %s AND post_id <> %d", '%' . $wpdb->esc_like( $meta['thumb'] ) . '%', $post_id)) ) {
+ $thumbfile = str_replace(basename($file), $meta['thumb'], $file);
+ /** This filter is documented in wp-includes/functions.php */
+ $thumbfile = apply_filters( 'wp_delete_file', $thumbfile );
+ @ unlink( path_join($uploadpath['basedir'], $thumbfile) );
+ }
+ }
+
+ // Remove intermediate and backup images if there are any.
+ if ( isset( $meta['sizes'] ) && is_array( $meta['sizes'] ) ) {
+ foreach ( $meta['sizes'] as $size => $sizeinfo ) {
+ $intermediate_file = str_replace( basename( $file ), $sizeinfo['file'], $file );
+ /** This filter is documented in wp-includes/functions.php */
+ $intermediate_file = apply_filters( 'wp_delete_file', $intermediate_file );
+ @ unlink( path_join( $uploadpath['basedir'], $intermediate_file ) );
+ }
+ }
+
+ if ( is_array($backup_sizes) ) {
+ foreach ( $backup_sizes as $size ) {
+ $del_file = path_join( dirname($meta['file']), $size['file'] );
+ /** This filter is documented in wp-includes/functions.php */
+ $del_file = apply_filters( 'wp_delete_file', $del_file );
+ @ unlink( path_join($uploadpath['basedir'], $del_file) );
+ }
+ }
+
+ wp_delete_file( $file );
+
+ clean_post_cache( $post );
+
+ return $post;
+}
+
+/**
+ * Retrieve attachment meta field for attachment ID.
+ *
+ * @since 2.1.0
+ *
+ * @param int $post_id Attachment ID. Default 0.
+ * @param bool $unfiltered Optional. If true, filters are not run. Default false.
+ * @return mixed Attachment meta field. False on failure.
+ */
+function wp_get_attachment_metadata( $post_id = 0, $unfiltered = false ) {
+ $post_id = (int) $post_id;
+ if ( !$post = get_post( $post_id ) )
+ return false;
+
+ $data = get_post_meta( $post->ID, '_wp_attachment_metadata', true );
+
+ if ( $unfiltered )
+ return $data;
+
+ /**
+ * Filter the attachment meta data.
+ *
+ * @since 2.1.0
+ *
+ * @param array|bool $data Array of meta data for the given attachment, or false
+ * if the object does not exist.
+ * @param int $post_id Attachment ID.
+ */
+ return apply_filters( 'wp_get_attachment_metadata', $data, $post->ID );
+}
+
+/**
+ * Update metadata for an attachment.
+ *
+ * @since 2.1.0
+ *
+ * @param int $post_id Attachment ID.
+ * @param array $data Attachment data.
+ * @return int|bool False if $post is invalid.
+ */
+function wp_update_attachment_metadata( $post_id, $data ) {
+ $post_id = (int) $post_id;
+ if ( !$post = get_post( $post_id ) )
+ return false;
+
+ /**
+ * Filter the updated attachment meta data.
+ *
+ * @since 2.1.0
+ *
+ * @param array $data Array of updated attachment meta data.
+ * @param int $post_id Attachment ID.
+ */
+ if ( $data = apply_filters( 'wp_update_attachment_metadata', $data, $post->ID ) )
+ return update_post_meta( $post->ID, '_wp_attachment_metadata', $data );
+ else
+ return delete_post_meta( $post->ID, '_wp_attachment_metadata' );
+}
+
+/**
+ * Retrieve the URL for an attachment.
+ *
+ * @since 2.1.0
+ *
+ * @global string $pagenow
+ *
+ * @param int $post_id Optional. Attachment ID. Default 0.
+ * @return string|false Attachment URL, otherwise false.
+ */
+function wp_get_attachment_url( $post_id = 0 ) {
+ $post_id = (int) $post_id;
+ if ( !$post = get_post( $post_id ) )
+ return false;
+
+ if ( 'attachment' != $post->post_type )
+ return false;
+
+ $url = '';
+ // Get attached file.
+ if ( $file = get_post_meta( $post->ID, '_wp_attached_file', true) ) {
+ // Get upload directory.
+ if ( ($uploads = wp_upload_dir()) && false === $uploads['error'] ) {
+ // Check that the upload base exists in the file location.
+ if ( 0 === strpos( $file, $uploads['basedir'] ) ) {
+ // Replace file location with url location.
+ $url = str_replace($uploads['basedir'], $uploads['baseurl'], $file);
+ } elseif ( false !== strpos($file, 'wp-content/uploads') ) {
+ $url = $uploads['baseurl'] . substr( $file, strpos($file, 'wp-content/uploads') + 18 );
+ } else {
+ // It's a newly-uploaded file, therefore $file is relative to the basedir.
+ $url = $uploads['baseurl'] . "/$file";
+ }
+ }
+ }
+
+ /*
+ * If any of the above options failed, Fallback on the GUID as used pre-2.7,
+ * not recommended to rely upon this.
+ */
+ if ( empty($url) ) {
+ $url = get_the_guid( $post->ID );
+ }
+
+ // On SSL front-end, URLs should be HTTPS.
+ if ( is_ssl() && ! is_admin() && 'wp-login.php' !== $GLOBALS['pagenow'] ) {
+ $url = set_url_scheme( $url );
+ }
+
+ /**
+ * Filter the attachment URL.
+ *
+ * @since 2.1.0
+ *
+ * @param string $url URL for the given attachment.
+ * @param int $post_id Attachment ID.
+ */
+ $url = apply_filters( 'wp_get_attachment_url', $url, $post->ID );
+
+ if ( empty( $url ) )
+ return false;
+
+ return $url;
+}
+
+/**
+ * Retrieve thumbnail for an attachment.
+ *
+ * @since 2.1.0
+ *
+ * @param int $post_id Optional. Attachment ID. Default 0.
+ * @return string|false False on failure. Thumbnail file path on success.
+ */
+function wp_get_attachment_thumb_file( $post_id = 0 ) {
+ $post_id = (int) $post_id;
+ if ( !$post = get_post( $post_id ) )
+ return false;
+ if ( !is_array( $imagedata = wp_get_attachment_metadata( $post->ID ) ) )
+ return false;
+
+ $file = get_attached_file( $post->ID );
+
+ if ( !empty($imagedata['thumb']) && ($thumbfile = str_replace(basename($file), $imagedata['thumb'], $file)) && file_exists($thumbfile) ) {
+ /**
+ * Filter the attachment thumbnail file path.
+ *
+ * @since 2.1.0
+ *
+ * @param string $thumbfile File path to the attachment thumbnail.
+ * @param int $post_id Attachment ID.
+ */
+ return apply_filters( 'wp_get_attachment_thumb_file', $thumbfile, $post->ID );
+ }
+ return false;
+}
+
+/**
+ * Retrieve URL for an attachment thumbnail.
+ *
+ * @since 2.1.0
+ *
+ * @param int $post_id Optional. Attachment ID. Default 0.
+ * @return string|false False on failure. Thumbnail URL on success.
+ */
+function wp_get_attachment_thumb_url( $post_id = 0 ) {
+ $post_id = (int) $post_id;
+ if ( !$post = get_post( $post_id ) )
+ return false;
+ if ( !$url = wp_get_attachment_url( $post->ID ) )
+ return false;
+
+ $sized = image_downsize( $post_id, 'thumbnail' );
+ if ( $sized )
+ return $sized[0];
+
+ if ( !$thumb = wp_get_attachment_thumb_file( $post->ID ) )
+ return false;
+
+ $url = str_replace(basename($url), basename($thumb), $url);
+
+ /**
+ * Filter the attachment thumbnail URL.
+ *
+ * @since 2.1.0
+ *
+ * @param string $url URL for the attachment thumbnail.
+ * @param int $post_id Attachment ID.
+ */
+ return apply_filters( 'wp_get_attachment_thumb_url', $url, $post->ID );
+}
+
+/**
+ * Verifies an attachment is of a given type.
+ *
+ * @since 4.2.0
+ *
+ * @param string $type Attachment type. Accepts 'image', 'audio', or 'video'.
+ * @param int|WP_Post $post_id Optional. Attachment ID. Default 0.
+ * @return bool True if one of the accepted types, false otherwise.
+ */
+function wp_attachment_is( $type, $post_id = 0 ) {
+ if ( ! $post = get_post( $post_id ) ) {
+ return false;
+ }
+
+ if ( ! $file = get_attached_file( $post->ID ) ) {
+ return false;
+ }
+
+ if ( 0 === strpos( $post->post_mime_type, $type . '/' ) ) {
+ return true;
+ }
+
+ $check = wp_check_filetype( $file );
+ if ( empty( $check['ext'] ) ) {
+ return false;
+ }
+
+ $ext = $check['ext'];
+
+ if ( 'import' !== $post->post_mime_type ) {
+ return $type === $ext;
+ }
+
+ switch ( $type ) {
+ case 'image':
+ $image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png' );
+ return in_array( $ext, $image_exts );
+
+ case 'audio':
+ return in_array( $ext, wp_get_audio_extensions() );
+
+ case 'video':
+ return in_array( $ext, wp_get_video_extensions() );
+
+ default:
+ return $type === $ext;
+ }
+}
+
+/**
+ * Checks if the attachment is an image.
+ *
+ * @since 2.1.0
+ * @since 4.2.0 Modified into wrapper for wp_attachment_is() and
+ * allowed WP_Post object to be passed.
+ *
+ * @param int|WP_Post $post Optional. Attachment ID. Default 0.
+ * @return bool Whether the attachment is an image.
+ */
+function wp_attachment_is_image( $post = 0 ) {
+ return wp_attachment_is( 'image', $post );
+}
+
+/**
+ * Retrieve the icon for a MIME type.
+ *
+ * @since 2.1.0
+ *
+ * @param string|int $mime MIME type or attachment ID.
+ * @return string|false Icon, false otherwise.
+ */
+function wp_mime_type_icon( $mime = 0 ) {
+ if ( !is_numeric($mime) )
+ $icon = wp_cache_get("mime_type_icon_$mime");
+
+ $post_id = 0;
+ if ( empty($icon) ) {
+ $post_mimes = array();
+ if ( is_numeric($mime) ) {
+ $mime = (int) $mime;
+ if ( $post = get_post( $mime ) ) {
+ $post_id = (int) $post->ID;
+ $file = get_attached_file( $post_id );
+ $ext = preg_replace('/^.+?\.([^.]+)$/', '$1', $file);
+ if ( !empty($ext) ) {
+ $post_mimes[] = $ext;
+ if ( $ext_type = wp_ext2type( $ext ) )
+ $post_mimes[] = $ext_type;
+ }
+ $mime = $post->post_mime_type;
+ } else {
+ $mime = 0;
+ }
+ } else {
+ $post_mimes[] = $mime;
+ }
+
+ $icon_files = wp_cache_get('icon_files');
+
+ if ( !is_array($icon_files) ) {
+ /**
+ * Filter the icon directory path.
+ *
+ * @since 2.0.0
+ *
+ * @param string $path Icon directory absolute path.
+ */
+ $icon_dir = apply_filters( 'icon_dir', ABSPATH . WPINC . '/images/media' );
+
+ /**
+ * Filter the icon directory URI.
+ *
+ * @since 2.0.0
+ *
+ * @param string $uri Icon directory URI.
+ */
+ $icon_dir_uri = apply_filters( 'icon_dir_uri', includes_url( 'images/media' ) );
+
+ /**
+ * Filter the list of icon directory URIs.
+ *
+ * @since 2.5.0
+ *
+ * @param array $uris List of icon directory URIs.
+ */
+ $dirs = apply_filters( 'icon_dirs', array( $icon_dir => $icon_dir_uri ) );
+ $icon_files = array();
+ while ( $dirs ) {
+ $keys = array_keys( $dirs );
+ $dir = array_shift( $keys );
+ $uri = array_shift($dirs);
+ if ( $dh = opendir($dir) ) {
+ while ( false !== $file = readdir($dh) ) {
+ $file = basename($file);
+ if ( substr($file, 0, 1) == '.' )
+ continue;
+ if ( !in_array(strtolower(substr($file, -4)), array('.png', '.gif', '.jpg') ) ) {
+ if ( is_dir("$dir/$file") )
+ $dirs["$dir/$file"] = "$uri/$file";
+ continue;
+ }
+ $icon_files["$dir/$file"] = "$uri/$file";
+ }
+ closedir($dh);
+ }
+ }
+ wp_cache_add( 'icon_files', $icon_files, 'default', 600 );
+ }
+
+ $types = array();
+ // Icon basename - extension = MIME wildcard.
+ foreach ( $icon_files as $file => $uri )
+ $types[ preg_replace('/^([^.]*).*$/', '$1', basename($file)) ] =& $icon_files[$file];
+
+ if ( ! empty($mime) ) {
+ $post_mimes[] = substr($mime, 0, strpos($mime, '/'));
+ $post_mimes[] = substr($mime, strpos($mime, '/') + 1);
+ $post_mimes[] = str_replace('/', '_', $mime);
+ }
+
+ $matches = wp_match_mime_types(array_keys($types), $post_mimes);
+ $matches['default'] = array('default');
+
+ foreach ( $matches as $match => $wilds ) {
+ foreach ( $wilds as $wild ) {
+ if ( ! isset( $types[ $wild ] ) ) {
+ continue;
+ }
+
+ $icon = $types[ $wild ];
+ if ( ! is_numeric( $mime ) ) {
+ wp_cache_add( "mime_type_icon_$mime", $icon );
+ }
+ break 2;
+ }
+ }
+ }
+
+ /**
+ * Filter the mime type icon.
+ *
+ * @since 2.1.0
+ *
+ * @param string $icon Path to the mime type icon.
+ * @param string $mime Mime type.
+ * @param int $post_id Attachment ID. Will equal 0 if the function passed
+ * the mime type.
+ */
+ return apply_filters( 'wp_mime_type_icon', $icon, $mime, $post_id );
+}
+
+/**
+ * Check for changed slugs for published post objects and save the old slug.
+ *
+ * The function is used when a post object of any type is updated,
+ * by comparing the current and previous post objects.
+ *
+ * If the slug was changed and not already part of the old slugs then it will be
+ * added to the post meta field ('_wp_old_slug') for storing old slugs for that
+ * post.
+ *
+ * The most logically usage of this function is redirecting changed post objects, so
+ * that those that linked to an changed post will be redirected to the new post.
+ *
+ * @since 2.1.0
+ *
+ * @param int $post_id Post ID.
+ * @param WP_Post $post The Post Object
+ * @param WP_Post $post_before The Previous Post Object
+ */
+function wp_check_for_changed_slugs( $post_id, $post, $post_before ) {
+ // Don't bother if it hasn't changed.
+ if ( $post->post_name == $post_before->post_name ) {
+ return;
+ }
+
+ // We're only concerned with published, non-hierarchical objects.
+ if ( ! ( 'publish' === $post->post_status || ( 'attachment' === get_post_type( $post ) && 'inherit' === $post->post_status ) ) || is_post_type_hierarchical( $post->post_type ) ) {
+ return;
+ }
+
+ $old_slugs = (array) get_post_meta( $post_id, '_wp_old_slug' );
+
+ // If we haven't added this old slug before, add it now.
+ if ( ! empty( $post_before->post_name ) && ! in_array( $post_before->post_name, $old_slugs ) ) {
+ add_post_meta( $post_id, '_wp_old_slug', $post_before->post_name );
+ }
+
+ // If the new slug was used previously, delete it from the list.
+ if ( in_array( $post->post_name, $old_slugs ) ) {
+ delete_post_meta( $post_id, '_wp_old_slug', $post->post_name );
+ }
+}
+
+/**
+ * Retrieve the private post SQL based on capability.
+ *
+ * This function provides a standardized way to appropriately select on the
+ * post_status of a post type. The function will return a piece of SQL code
+ * that can be added to a WHERE clause; this SQL is constructed to allow all
+ * published posts, and all private posts to which the user has access.
+ *
+ * @since 2.2.0
+ * @since 4.3.0 Added the ability to pass an array to `$post_type`.
+ *
+ * @param string|array $post_type Single post type or an array of post types. Currently only supports 'post' or 'page'.
+ * @return string SQL code that can be added to a where clause.
+ */
+function get_private_posts_cap_sql( $post_type ) {
+ return get_posts_by_author_sql( $post_type, false );
+}
+
+/**
+ * Retrieve the post SQL based on capability, author, and type.
+ *
+ * @since 3.0.0
+ * @since 4.3.0 Introduced the ability to pass an array of post types to `$post_type`.
+ *
+ * @see get_private_posts_cap_sql()
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array|string $post_type Single post type or an array of post types.
+ * @param bool $full Optional. Returns a full WHERE statement instead of just
+ * an 'andalso' term. Default true.
+ * @param int $post_author Optional. Query posts having a single author ID. Default null.
+ * @param bool $public_only Optional. Only return public posts. Skips cap checks for
+ * $current_user. Default false.
+ * @return string SQL WHERE code that can be added to a query.
+ */
+function get_posts_by_author_sql( $post_type, $full = true, $post_author = null, $public_only = false ) {
+ global $wpdb;
+
+ if ( is_array( $post_type ) ) {
+ $post_types = $post_type;
+ } else {
+ $post_types = array( $post_type );
+ }
+
+ $post_type_clauses = array();
+ foreach ( $post_types as $post_type ) {
+ $post_type_obj = get_post_type_object( $post_type );
+ if ( ! $post_type_obj ) {
+ continue;
+ }
+
+ /**
+ * Filter the capability to read private posts for a custom post type
+ * when generating SQL for getting posts by author.
+ *
+ * @since 2.2.0
+ * @deprecated 3.2.0 The hook transitioned from "somewhat useless" to "totally useless".
+ *
+ * @param string $cap Capability.
+ */
+ if ( ! $cap = apply_filters( 'pub_priv_sql_capability', '' ) ) {
+ $cap = current_user_can( $post_type_obj->cap->read_private_posts );
+ }
+
+ // Only need to check the cap if $public_only is false.
+ $post_status_sql = "post_status = 'publish'";
+ if ( false === $public_only ) {
+ if ( $cap ) {
+ // Does the user have the capability to view private posts? Guess so.
+ $post_status_sql .= " OR post_status = 'private'";
+ } elseif ( is_user_logged_in() ) {
+ // Users can view their own private posts.
+ $id = get_current_user_id();
+ if ( null === $post_author || ! $full ) {
+ $post_status_sql .= " OR post_status = 'private' AND post_author = $id";
+ } elseif ( $id == (int) $post_author ) {
+ $post_status_sql .= " OR post_status = 'private'";
+ } // else none
+ } // else none
+ }
+
+ $post_type_clauses[] = "( post_type = '" . $post_type . "' AND ( $post_status_sql ) )";
+ }
+
+ if ( empty( $post_type_clauses ) ) {
+ return $full ? 'WHERE 1 = 0' : '1 = 0';
+ }
+
+ $sql = '( '. implode( ' OR ', $post_type_clauses ) . ' )';
+
+ if ( null !== $post_author ) {
+ $sql .= $wpdb->prepare( ' AND post_author = %d', $post_author );
+ }
+
+ if ( $full ) {
+ $sql = 'WHERE ' . $sql;
+ }
+
+ return $sql;
+}
+
+/**
+ * Retrieve the date that the last post was published.
+ *
+ * The server timezone is the default and is the difference between GMT and
+ * server time. The 'blog' value is the date when the last post was posted. The
+ * 'gmt' is when the last post was posted in GMT formatted date.
+ *
+ * @since 0.71
+ * @since 4.4.0 The `$post_type` argument was added.
+ *
+ * @param string $timezone Optional. The timezone for the timestamp. Accepts 'server', 'blog', or 'gmt'.
+ * 'server' uses the server's internal timezone.
+ * 'blog' uses the `post_modified` field, which proxies to the timezone set for the site.
+ * 'gmt' uses the `post_modified_gmt` field.
+ * Default 'server'.
+ * @param string $post_type Optional. The post type to check. Default 'any'.
+ * @return string The date of the last post.
+ */
+function get_lastpostdate( $timezone = 'server', $post_type = 'any' ) {
+ /**
+ * Filter the date the last post was published.
+ *
+ * @since 2.3.0
+ *
+ * @param string $date Date the last post was published.
+ * @param string $timezone Location to use for getting the post published date.
+ * See {@see get_lastpostdate()} for accepted `$timezone` values.
+ */
+ return apply_filters( 'get_lastpostdate', _get_last_post_time( $timezone, 'date', $post_type ), $timezone );
+}
+
+/**
+ * Get the timestamp of the last time any post was modified.
+ *
+ * The server timezone is the default and is the difference between GMT and
+ * server time. The 'blog' value is just when the last post was modified. The
+ * 'gmt' is when the last post was modified in GMT time.
+ *
+ * @since 1.2.0
+ * @since 4.4.0 The `$post_type` argument was added.
+ *
+ * @param string $timezone Optional. The timezone for the timestamp. See {@see get_lastpostdate()}
+ * for information on accepted values.
+ * Default 'server'.
+ * @param string $post_type Optional. The post type to check. Default 'any'.
+ * @return string The timestamp.
+ */
+function get_lastpostmodified( $timezone = 'server', $post_type = 'any' ) {
+ /**
+ * Pre-filter the return value of get_lastpostmodified() before the query is run.
+ *
+ * @since 4.4.0
+ *
+ * @param string $lastpostmodified Date the last post was modified.
+ * Returning anything other than false will short-circuit the function.
+ * @param string $timezone Location to use for getting the post modified date.
+ * See {@see get_lastpostdate()} for accepted `$timezone` values.
+ * @param string $post_type The post type to check.
+ */
+ $lastpostmodified = apply_filters( 'pre_get_lastpostmodified', false, $timezone, $post_type );
+ if ( false !== $lastpostmodified ) {
+ return $lastpostmodified;
+ }
+
+ $lastpostmodified = _get_last_post_time( $timezone, 'modified', $post_type );
+
+ $lastpostdate = get_lastpostdate($timezone);
+ if ( $lastpostdate > $lastpostmodified ) {
+ $lastpostmodified = $lastpostdate;
+ }
+
+ /**
+ * Filter the date the last post was modified.
+ *
+ * @since 2.3.0
+ *
+ * @param string $lastpostmodified Date the last post was modified.
+ * @param string $timezone Location to use for getting the post modified date.
+ * See {@see get_lastpostdate()} for accepted `$timezone` values.
+ */
+ return apply_filters( 'get_lastpostmodified', $lastpostmodified, $timezone );
+}
+
+/**
+ * Get the timestamp of the last time any post was modified or published.
+ *
+ * @since 3.1.0
+ * @since 4.4.0 The `$post_type` argument was added.
+ * @access private
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $timezone The timezone for the timestamp. See get_lastpostdate().
+ * for information on accepted values.
+ * @param string $field Post field to check. Accepts 'date' or 'modified'.
+ * @param string $post_type Optional. The post type to check. Default 'any'.
+ * @return string|false The timestamp.
+ */
+function _get_last_post_time( $timezone, $field, $post_type = 'any' ) {
+ global $wpdb;
+
+ if ( ! in_array( $field, array( 'date', 'modified' ) ) ) {
+ return false;
+ }
+
+ $timezone = strtolower( $timezone );
+
+ $key = "lastpost{$field}:$timezone";
+ if ( 'any' !== $post_type ) {
+ $key .= ':' . sanitize_key( $post_type );
+ }
+
+ $date = wp_cache_get( $key, 'timeinfo' );
+
+ if ( ! $date ) {
+ if ( 'any' === $post_type ) {
+ $post_types = get_post_types( array( 'public' => true ) );
+ array_walk( $post_types, array( $wpdb, 'escape_by_ref' ) );
+ $post_types = "'" . implode( "', '", $post_types ) . "'";
+ } else {
+ $post_types = "'" . sanitize_key( $post_type ) . "'";
+ }
+
+ switch ( $timezone ) {
+ case 'gmt':
+ $date = $wpdb->get_var("SELECT post_{$field}_gmt FROM $wpdb->posts WHERE post_status = 'publish' AND post_type IN ({$post_types}) ORDER BY post_{$field}_gmt DESC LIMIT 1");
+ break;
+ case 'blog':
+ $date = $wpdb->get_var("SELECT post_{$field} FROM $wpdb->posts WHERE post_status = 'publish' AND post_type IN ({$post_types}) ORDER BY post_{$field}_gmt DESC LIMIT 1");
+ break;
+ case 'server':
+ $add_seconds_server = date( 'Z' );
+ $date = $wpdb->get_var("SELECT DATE_ADD(post_{$field}_gmt, INTERVAL '$add_seconds_server' SECOND) FROM $wpdb->posts WHERE post_status = 'publish' AND post_type IN ({$post_types}) ORDER BY post_{$field}_gmt DESC LIMIT 1");
+ break;
+ }
+
+ if ( $date ) {
+ wp_cache_set( $key, $date, 'timeinfo' );
+ }
+ }
+
+ return $date;
+}
+
+/**
+ * Updates posts in cache.
+ *
+ * @since 1.5.1
+ *
+ * @param array $posts Array of post objects, passed by reference.
+ */
+function update_post_cache( &$posts ) {
+ if ( ! $posts )
+ return;
+
+ foreach ( $posts as $post )
+ wp_cache_add( $post->ID, $post, 'posts' );
+}
+
+/**
+ * Will clean the post in the cache.
+ *
+ * Cleaning means delete from the cache of the post. Will call to clean the term
+ * object cache associated with the post ID.
+ *
+ * This function not run if $_wp_suspend_cache_invalidation is not empty. See
+ * wp_suspend_cache_invalidation().
+ *
+ * @since 2.0.0
+ *
+ * @global bool $_wp_suspend_cache_invalidation
+ *
+ * @param int|WP_Post $post Post ID or post object to remove from the cache.
+ */
+function clean_post_cache( $post ) {
+ global $_wp_suspend_cache_invalidation;
+
+ if ( ! empty( $_wp_suspend_cache_invalidation ) )
+ return;
+
+ $post = get_post( $post );
+ if ( empty( $post ) )
+ return;
+
+ wp_cache_delete( $post->ID, 'posts' );
+ wp_cache_delete( $post->ID, 'post_meta' );
+
+ clean_object_term_cache( $post->ID, $post->post_type );
+
+ wp_cache_delete( 'wp_get_archives', 'general' );
+
+ /**
+ * Fires immediately after the given post's cache is cleaned.
+ *
+ * @since 2.5.0
+ *
+ * @param int $post_id Post ID.
+ * @param WP_Post $post Post object.
+ */
+ do_action( 'clean_post_cache', $post->ID, $post );
+
+ if ( 'page' == $post->post_type ) {
+ wp_cache_delete( 'all_page_ids', 'posts' );
+
+ /**
+ * Fires immediately after the given page's cache is cleaned.
+ *
+ * @since 2.5.0
+ *
+ * @param int $post_id Post ID.
+ */
+ do_action( 'clean_page_cache', $post->ID );
+ }
+
+ wp_cache_set( 'last_changed', microtime(), 'posts' );
+}
+
+/**
+ * Call major cache updating functions for list of Post objects.
+ *
+ * @since 1.5.0
+ *
+ * @param array $posts Array of Post objects
+ * @param string $post_type Optional. Post type. Default 'post'.
+ * @param bool $update_term_cache Optional. Whether to update the term cache. Default true.
+ * @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
+ */
+function update_post_caches( &$posts, $post_type = 'post', $update_term_cache = true, $update_meta_cache = true ) {
+ // No point in doing all this work if we didn't match any posts.
+ if ( !$posts )
+ return;
+
+ update_post_cache($posts);
+
+ $post_ids = array();
+ foreach ( $posts as $post )
+ $post_ids[] = $post->ID;
+
+ if ( ! $post_type )
+ $post_type = 'any';
+
+ if ( $update_term_cache ) {
+ if ( is_array($post_type) ) {
+ $ptypes = $post_type;
+ } elseif ( 'any' == $post_type ) {
+ $ptypes = array();
+ // Just use the post_types in the supplied posts.
+ foreach ( $posts as $post ) {
+ $ptypes[] = $post->post_type;
+ }
+ $ptypes = array_unique($ptypes);
+ } else {
+ $ptypes = array($post_type);
+ }
+
+ if ( ! empty($ptypes) )
+ update_object_term_cache($post_ids, $ptypes);
+ }
+
+ if ( $update_meta_cache )
+ update_postmeta_cache($post_ids);
+}
+
+/**
+ * Updates metadata cache for list of post IDs.
+ *
+ * Performs SQL query to retrieve the metadata for the post IDs and updates the
+ * metadata cache for the posts. Therefore, the functions, which call this
+ * function, do not need to perform SQL queries on their own.
+ *
+ * @since 2.1.0
+ *
+ * @param array $post_ids List of post IDs.
+ * @return array|false Returns false if there is nothing to update or an array
+ * of metadata.
+ */
+function update_postmeta_cache( $post_ids ) {
+ return update_meta_cache('post', $post_ids);
+}
+
+/**
+ * Will clean the attachment in the cache.
+ *
+ * Cleaning means delete from the cache. Optionally will clean the term
+ * object cache associated with the attachment ID.
+ *
+ * This function will not run if $_wp_suspend_cache_invalidation is not empty.
+ *
+ * @since 3.0.0
+ *
+ * @global bool $_wp_suspend_cache_invalidation
+ *
+ * @param int $id The attachment ID in the cache to clean.
+ * @param bool $clean_terms Optional. Whether to clean terms cache. Default false.
+ */
+function clean_attachment_cache( $id, $clean_terms = false ) {
+ global $_wp_suspend_cache_invalidation;
+
+ if ( !empty($_wp_suspend_cache_invalidation) )
+ return;
+
+ $id = (int) $id;
+
+ wp_cache_delete($id, 'posts');
+ wp_cache_delete($id, 'post_meta');
+
+ if ( $clean_terms )
+ clean_object_term_cache($id, 'attachment');
+
+ /**
+ * Fires after the given attachment's cache is cleaned.
+ *
+ * @since 3.0.0
+ *
+ * @param int $id Attachment ID.
+ */
+ do_action( 'clean_attachment_cache', $id );
+}
+
+//
+// Hooks
+//
+
+/**
+ * Hook for managing future post transitions to published.
+ *
+ * @since 2.3.0
+ * @access private
+ *
+ * @see wp_clear_scheduled_hook()
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $new_status New post status.
+ * @param string $old_status Previous post status.
+ * @param WP_Post $post Post object.
+ */
+function _transition_post_status( $new_status, $old_status, $post ) {
+ global $wpdb;
+
+ if ( $old_status != 'publish' && $new_status == 'publish' ) {
+ // Reset GUID if transitioning to publish and it is empty.
+ if ( '' == get_the_guid($post->ID) )
+ $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post->ID ) ), array( 'ID' => $post->ID ) );
+
+ /**
+ * Fires when a post's status is transitioned from private to published.
+ *
+ * @since 1.5.0
+ * @deprecated 2.3.0 Use 'private_to_publish' instead.
+ *
+ * @param int $post_id Post ID.
+ */
+ do_action('private_to_published', $post->ID);
+ }
+
+ // If published posts changed clear the lastpostmodified cache.
+ if ( 'publish' == $new_status || 'publish' == $old_status) {
+ foreach ( array( 'server', 'gmt', 'blog' ) as $timezone ) {
+ wp_cache_delete( "lastpostmodified:$timezone", 'timeinfo' );
+ wp_cache_delete( "lastpostdate:$timezone", 'timeinfo' );
+ wp_cache_delete( "lastpostdate:$timezone:{$post->post_type}", 'timeinfo' );
+ }
+ }
+
+ if ( $new_status !== $old_status ) {
+ wp_cache_delete( _count_posts_cache_key( $post->post_type ), 'counts' );
+ wp_cache_delete( _count_posts_cache_key( $post->post_type, 'readable' ), 'counts' );
+ }
+
+ // Always clears the hook in case the post status bounced from future to draft.
+ wp_clear_scheduled_hook('publish_future_post', array( $post->ID ) );
+}
+
+/**
+ * Hook used to schedule publication for a post marked for the future.
+ *
+ * The $post properties used and must exist are 'ID' and 'post_date_gmt'.
+ *
+ * @since 2.3.0
+ * @access private
+ *
+ * @param int $deprecated Not used. Can be set to null. Never implemented. Not marked
+ * as deprecated with _deprecated_argument() as it conflicts with
+ * wp_transition_post_status() and the default filter for
+ * {@see _future_post_hook()}.
+ * @param WP_Post $post Post object.
+ */
+function _future_post_hook( $deprecated, $post ) {
+ wp_clear_scheduled_hook( 'publish_future_post', array( $post->ID ) );
+ wp_schedule_single_event( strtotime( get_gmt_from_date( $post->post_date ) . ' GMT') , 'publish_future_post', array( $post->ID ) );
+}
+
+/**
+ * Hook to schedule pings and enclosures when a post is published.
+ *
+ * Uses XMLRPC_REQUEST and WP_IMPORTING constants.
+ *
+ * @since 2.3.0
+ * @access private
+ *
+ * @param int $post_id The ID in the database table of the post being published.
+ */
+function _publish_post_hook( $post_id ) {
+ if ( defined( 'XMLRPC_REQUEST' ) ) {
+ /**
+ * Fires when _publish_post_hook() is called during an XML-RPC request.
+ *
+ * @since 2.1.0
+ *
+ * @param int $post_id Post ID.
+ */
+ do_action( 'xmlrpc_publish_post', $post_id );
+ }
+
+ if ( defined('WP_IMPORTING') )
+ return;
+
+ if ( get_option('default_pingback_flag') )
+ add_post_meta( $post_id, '_pingme', '1' );
+ add_post_meta( $post_id, '_encloseme', '1' );
+
+ wp_schedule_single_event(time(), 'do_pings');
+}
+
+/**
+ * Return the post's parent's post_ID
+ *
+ * @since 3.1.0
+ *
+ * @param int $post_ID
+ *
+ * @return int|false Post parent ID, otherwise false.
+ */
+function wp_get_post_parent_id( $post_ID ) {
+ $post = get_post( $post_ID );
+ if ( !$post || is_wp_error( $post ) )
+ return false;
+ return (int) $post->post_parent;
+}
+
+/**
+ * Check the given subset of the post hierarchy for hierarchy loops.
+ *
+ * Prevents loops from forming and breaks those that it finds. Attached
+ * to the 'wp_insert_post_parent' filter.
+ *
+ * @since 3.1.0
+ *
+ * @see wp_find_hierarchy_loop()
+ *
+ * @param int $post_parent ID of the parent for the post we're checking.
+ * @param int $post_ID ID of the post we're checking.
+ * @return int The new post_parent for the post, 0 otherwise.
+ */
+function wp_check_post_hierarchy_for_loops( $post_parent, $post_ID ) {
+ // Nothing fancy here - bail.
+ if ( !$post_parent )
+ return 0;
+
+ // New post can't cause a loop.
+ if ( empty( $post_ID ) )
+ return $post_parent;
+
+ // Can't be its own parent.
+ if ( $post_parent == $post_ID )
+ return 0;
+
+ // Now look for larger loops.
+ if ( !$loop = wp_find_hierarchy_loop( 'wp_get_post_parent_id', $post_ID, $post_parent ) )
+ return $post_parent; // No loop
+
+ // Setting $post_parent to the given value causes a loop.
+ if ( isset( $loop[$post_ID] ) )
+ return 0;
+
+ // There's a loop, but it doesn't contain $post_ID. Break the loop.
+ foreach ( array_keys( $loop ) as $loop_member )
+ wp_update_post( array( 'ID' => $loop_member, 'post_parent' => 0 ) );
+
+ return $post_parent;
+}
+
+/**
+ * Set a post thumbnail.
+ *
+ * @since 3.1.0
+ *
+ * @param int|WP_Post $post Post ID or post object where thumbnail should be attached.
+ * @param int $thumbnail_id Thumbnail to attach.
+ * @return int|bool True on success, false on failure.
+ */
+function set_post_thumbnail( $post, $thumbnail_id ) {
+ $post = get_post( $post );
+ $thumbnail_id = absint( $thumbnail_id );
+ if ( $post && $thumbnail_id && get_post( $thumbnail_id ) ) {
+ if ( wp_get_attachment_image( $thumbnail_id, 'thumbnail' ) )
+ return update_post_meta( $post->ID, '_thumbnail_id', $thumbnail_id );
+ else
+ return delete_post_meta( $post->ID, '_thumbnail_id' );
+ }
+ return false;
+}
+
+/**
+ * Remove a post thumbnail.
+ *
+ * @since 3.3.0
+ *
+ * @param int|WP_Post $post Post ID or post object where thumbnail should be removed from.
+ * @return bool True on success, false on failure.
+ */
+function delete_post_thumbnail( $post ) {
+ $post = get_post( $post );
+ if ( $post )
+ return delete_post_meta( $post->ID, '_thumbnail_id' );
+ return false;
+}
+
+/**
+ * Delete auto-drafts for new posts that are > 7 days old.
+ *
+ * @since 3.4.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ */
+function wp_delete_auto_drafts() {
+ global $wpdb;
+
+ // Cleanup old auto-drafts more than 7 days old.
+ $old_posts = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_status = 'auto-draft' AND DATE_SUB( NOW(), INTERVAL 7 DAY ) > post_date" );
+ foreach ( (array) $old_posts as $delete ) {
+ // Force delete.
+ wp_delete_post( $delete, true );
+ }
+}
+
+/**
+ * Update the custom taxonomies' term counts when a post's status is changed.
+ *
+ * For example, default posts term counts (for custom taxonomies) don't include
+ * private / draft posts.
+ *
+ * @since 3.3.0
+ * @access private
+ *
+ * @param string $new_status New post status.
+ * @param string $old_status Old post status.
+ * @param WP_Post $post Post object.
+ */
+function _update_term_count_on_transition_post_status( $new_status, $old_status, $post ) {
+ // Update counts for the post's terms.
+ foreach ( (array) get_object_taxonomies( $post->post_type ) as $taxonomy ) {
+ $tt_ids = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'tt_ids' ) );
+ wp_update_term_count( $tt_ids, $taxonomy );
+ }
+}
+
+/**
+ * Adds any posts from the given ids to the cache that do not already exist in cache
+ *
+ * @since 3.4.0
+ * @access private
+ *
+ * @see update_post_caches()
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array $ids ID list.
+ * @param bool $update_term_cache Optional. Whether to update the term cache. Default true.
+ * @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
+ */
+function _prime_post_caches( $ids, $update_term_cache = true, $update_meta_cache = true ) {
+ global $wpdb;
+
+ $non_cached_ids = _get_non_cached_ids( $ids, 'posts' );
+ if ( !empty( $non_cached_ids ) ) {
+ $fresh_posts = $wpdb->get_results( sprintf( "SELECT $wpdb->posts.* FROM $wpdb->posts WHERE ID IN (%s)", join( ",", $non_cached_ids ) ) );
+
+ update_post_caches( $fresh_posts, 'any', $update_term_cache, $update_meta_cache );
+ }
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesrestapirestfunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/rest-api/rest-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rest-api/rest-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/rest-api/rest-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,643 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * REST API functions.
- *
- * @package WordPress
- * @subpackage REST_API
- * @since 4.4.0
- */
-
-/**
- * Registers a REST API route.
- *
- * @since 4.4.0
- *
- * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
- *
- * @param string $namespace The first URL segment after core prefix. Should be unique to your package/plugin.
- * @param string $route The base URL for route you are adding.
- * @param array $args Optional. Either an array of options for the endpoint, or an array of arrays for
- * multiple methods. Default empty array.
- * @param bool $override Optional. If the route already exists, should we override it? True overrides,
- * false merges (with newer overriding if duplicate keys exist). Default false.
- * @return bool True on success, false on error.
- */
-function register_rest_route( $namespace, $route, $args = array(), $override = false ) {
- /** @var WP_REST_Server $wp_rest_server */
- global $wp_rest_server;
-
- if ( empty( $namespace ) ) {
- /*
- * Non-namespaced routes are not allowed, with the exception of the main
- * and namespace indexes. If you really need to register a
- * non-namespaced route, call `WP_REST_Server::register_route` directly.
- */
- _doing_it_wrong( 'register_rest_route', 'Routes must be namespaced with plugin or theme name and version.', '4.4.0' );
- return false;
- } else if ( empty( $route ) ) {
- _doing_it_wrong( 'register_rest_route', 'Route must be specified.', '4.4.0' );
- return false;
- }
-
- if ( isset( $args['callback'] ) ) {
- // Upgrade a single set to multiple.
- $args = array( $args );
- }
-
- $defaults = array(
- 'methods' => 'GET',
- 'callback' => null,
- 'args' => array(),
- );
- foreach ( $args as $key => &$arg_group ) {
- if ( ! is_numeric( $arg_group ) ) {
- // Route option, skip here.
- continue;
- }
-
- $arg_group = array_merge( $defaults, $arg_group );
- }
-
- $full_route = '/' . trim( $namespace, '/' ) . '/' . trim( $route, '/' );
- $wp_rest_server->register_route( $namespace, $full_route, $args, $override );
- return true;
-}
-
-/**
- * Registers rewrite rules for the API.
- *
- * @since 4.4.0
- *
- * @see rest_api_register_rewrites()
- * @global WP $wp Current WordPress environment instance.
- */
-function rest_api_init() {
- rest_api_register_rewrites();
-
- global $wp;
- $wp->add_query_var( 'rest_route' );
-}
-
-/**
- * Adds REST rewrite rules.
- *
- * @since 4.4.0
- *
- * @see add_rewrite_rule()
- */
-function rest_api_register_rewrites() {
- add_rewrite_rule( '^' . rest_get_url_prefix() . '/?$','index.php?rest_route=/','top' );
- add_rewrite_rule( '^' . rest_get_url_prefix() . '/(.*)?','index.php?rest_route=/$matches[1]','top' );
-}
-
-/**
- * Registers the default REST API filters.
- *
- * Attached to the {@see 'rest_api_init'} action
- * to make testing and disabling these filters easier.
- *
- * @since 4.4.0
- */
-function rest_api_default_filters() {
- // Deprecated reporting.
- add_action( 'deprecated_function_run', 'rest_handle_deprecated_function', 10, 3 );
- add_filter( 'deprecated_function_trigger_error', '__return_false' );
- add_action( 'deprecated_argument_run', 'rest_handle_deprecated_argument', 10, 3 );
- add_filter( 'deprecated_argument_trigger_error', '__return_false' );
-
- // Default serving.
- add_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
- add_filter( 'rest_post_dispatch', 'rest_send_allow_header', 10, 3 );
-
- add_filter( 'rest_pre_dispatch', 'rest_handle_options_request', 10, 3 );
-}
-
-/**
- * Loads the REST API.
- *
- * @since 4.4.0
- *
- * @global WP $wp Current WordPress environment instance.
- * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
- */
-function rest_api_loaded() {
- if ( empty( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
- return;
- }
-
- /**
- * Whether this is a REST Request.
- *
- * @since 4.4.0
- * @var bool
- */
- define( 'REST_REQUEST', true );
-
- /** @var WP_REST_Server $wp_rest_server */
- global $wp_rest_server;
-
- /**
- * Filter the REST Server Class.
- *
- * This filter allows you to adjust the server class used by the API, using a
- * different class to handle requests.
- *
- * @since 4.4.0
- *
- * @param string $class_name The name of the server class. Default 'WP_REST_Server'.
- */
- $wp_rest_server_class = apply_filters( 'wp_rest_server_class', 'WP_REST_Server' );
- $wp_rest_server = new $wp_rest_server_class;
-
- /**
- * Fires when preparing to serve an API request.
- *
- * Endpoint objects should be created and register their hooks on this action rather
- * than another action to ensure they're only loaded when needed.
- *
- * @since 4.4.0
- *
- * @param WP_REST_Server $wp_rest_server Server object.
- */
- do_action( 'rest_api_init', $wp_rest_server );
-
- // Fire off the request.
- $wp_rest_server->serve_request( $GLOBALS['wp']->query_vars['rest_route'] );
-
- // We're done.
- die();
-}
-
-/**
- * Retrieves the URL prefix for any API resource.
- *
- * @since 4.4.0
- *
- * @return string Prefix.
- */
-function rest_get_url_prefix() {
- /**
- * Filter the REST URL prefix.
- *
- * @since 4.4.0
- *
- * @param string $prefix URL prefix. Default 'wp-json'.
- */
- return apply_filters( 'rest_url_prefix', 'wp-json' );
-}
-
-/**
- * Retrieves the URL to a REST endpoint on a site.
- *
- * Note: The returned URL is NOT escaped.
- *
- * @since 4.4.0
- *
- * @todo Check if this is even necessary
- *
- * @param int $blog_id Optional. Blog ID. Default of null returns URL for current blog.
- * @param string $path Optional. REST route. Default '/'.
- * @param string $scheme Optional. Sanitization scheme. Default 'rest'.
- * @return string Full URL to the endpoint.
- */
-function get_rest_url( $blog_id = null, $path = '/', $scheme = 'rest' ) {
- if ( empty( $path ) ) {
- $path = '/';
- }
-
- if ( is_multisite() && get_blog_option( $blog_id, 'permalink_structure' ) || get_option( 'permalink_structure' ) ) {
- $url = get_home_url( $blog_id, rest_get_url_prefix(), $scheme );
- $url .= '/' . ltrim( $path, '/' );
- } else {
- $url = trailingslashit( get_home_url( $blog_id, '', $scheme ) );
-
- $path = '/' . ltrim( $path, '/' );
-
- $url = add_query_arg( 'rest_route', $path, $url );
- }
-
- if ( is_ssl() ) {
- // If the current host is the same as the REST URL host, force the REST URL scheme to HTTPS.
- if ( $_SERVER['SERVER_NAME'] === parse_url( get_home_url( $blog_id ), PHP_URL_HOST ) ) {
- $url = set_url_scheme( $url, 'https' );
- }
- }
-
- /**
- * Filter the REST URL.
- *
- * Use this filter to adjust the url returned by the `get_rest_url` function.
- *
- * @since 4.4.0
- *
- * @param string $url REST URL.
- * @param string $path REST route.
- * @param int $blog_id Blog ID.
- * @param string $scheme Sanitization scheme.
- */
- return apply_filters( 'rest_url', $url, $path, $blog_id, $scheme );
-}
-
-/**
- * Retrieves the URL to a REST endpoint.
- *
- * Note: The returned URL is NOT escaped.
- *
- * @since 4.4.0
- *
- * @param string $path Optional. REST route. Default empty.
- * @param string $scheme Optional. Sanitization scheme. Default 'json'.
- * @return string Full URL to the endpoint.
- */
-function rest_url( $path = '', $scheme = 'json' ) {
- return get_rest_url( null, $path, $scheme );
-}
-
-/**
- * Do a REST request.
- *
- * Used primarily to route internal requests through WP_REST_Server.
- *
- * @since 4.4.0
- *
- * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
- *
- * @param WP_REST_Request|string $request Request.
- * @return WP_REST_Response REST response.
- */
-function rest_do_request( $request ) {
- global $wp_rest_server;
- $request = rest_ensure_request( $request );
- return $wp_rest_server->dispatch( $request );
-}
-
-/**
- * Ensures request arguments are a request object (for consistency).
- *
- * @since 4.4.0
- *
- * @param array|WP_REST_Request $request Request to check.
- * @return WP_REST_Request REST request instance.
- */
-function rest_ensure_request( $request ) {
- if ( $request instanceof WP_REST_Request ) {
- return $request;
- }
-
- return new WP_REST_Request( 'GET', '', $request );
-}
-
-/**
- * Ensures a REST response is a response object (for consistency).
- *
- * This implements WP_HTTP_Response, allowing usage of `set_status`/`header`/etc
- * without needing to double-check the object. Will also allow WP_Error to indicate error
- * responses, so users should immediately check for this value.
- *
- * @since 4.4.0
- *
- * @param WP_Error|WP_HTTP_Response|mixed $response Response to check.
- * @return mixed WP_Error if response generated an error, WP_HTTP_Response if response
- * is a already an instance, otherwise returns a new WP_REST_Response instance.
- */
-function rest_ensure_response( $response ) {
- if ( is_wp_error( $response ) ) {
- return $response;
- }
-
- if ( $response instanceof WP_HTTP_Response ) {
- return $response;
- }
-
- return new WP_REST_Response( $response );
-}
-
-/**
- * Handles _deprecated_function() errors.
- *
- * @since 4.4.0
- *
- * @param string $function Function name.
- * @param string $replacement Replacement function name.
- * @param string $version Version.
- */
-function rest_handle_deprecated_function( $function, $replacement, $version ) {
- if ( ! empty( $replacement ) ) {
- /* translators: 1: function name, 2: WordPress version number, 3: new function name */
- $string = sprintf( __( '%1$s (since %2$s; use %3$s instead)' ), $function, $version, $replacement );
- } else {
- /* translators: 1: function name, 2: WordPress version number */
- $string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
- }
-
- header( sprintf( 'X-WP-DeprecatedFunction: %s', $string ) );
-}
-
-/**
- * Handles _deprecated_argument() errors.
- *
- * @since 4.4.0
- *
- * @param string $function Function name.
- * @param string $replacement Replacement function name.
- * @param string $version Version.
- */
-function rest_handle_deprecated_argument( $function, $replacement, $version ) {
- if ( ! empty( $replacement ) ) {
- /* translators: 1: function name, 2: WordPress version number, 3: new argument name */
- $string = sprintf( __( '%1$s (since %2$s; %3$s)' ), $function, $version, $replacement );
- } else {
- /* translators: 1: function name, 2: WordPress version number */
- $string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
- }
-
- header( sprintf( 'X-WP-DeprecatedParam: %s', $string ) );
-}
-
-/**
- * Sends Cross-Origin Resource Sharing headers with API requests.
- *
- * @since 4.4.0
- *
- * @param mixed $value Response data.
- * @return mixed Response data.
- */
-function rest_send_cors_headers( $value ) {
- $origin = get_http_origin();
-
- if ( $origin ) {
- header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
- header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE' );
- header( 'Access-Control-Allow-Credentials: true' );
- }
-
- return $value;
-}
-
-/**
- * Handles OPTIONS requests for the server.
- *
- * This is handled outside of the server code, as it doesn't obey normal route
- * mapping.
- *
- * @since 4.4.0
- *
- * @param mixed $response Current response, either response or `null` to indicate pass-through.
- * @param WP_REST_Server $handler ResponseHandler instance (usually WP_REST_Server).
- * @param WP_REST_Request $request The request that was used to make current response.
- * @return WP_REST_Response Modified response, either response or `null` to indicate pass-through.
- */
-function rest_handle_options_request( $response, $handler, $request ) {
- if ( ! empty( $response ) || $request->get_method() !== 'OPTIONS' ) {
- return $response;
- }
-
- $response = new WP_REST_Response();
- $data = array();
-
- $accept = array();
-
- foreach ( $handler->get_routes() as $route => $endpoints ) {
- $match = preg_match( '@^' . $route . '$@i', $request->get_route(), $args );
-
- if ( ! $match ) {
- continue;
- }
-
- $data = $handler->get_data_for_route( $route, $endpoints, 'help' );
- $accept = array_merge( $accept, $data['methods'] );
- break;
- }
- $response->header( 'Accept', implode( ', ', $accept ) );
-
- $response->set_data( $data );
- return $response;
-}
-
-/**
- * Sends the "Allow" header to state all methods that can be sent to the current route.
- *
- * @since 4.4.0
- *
- * @param WP_REST_Response $response Current response being served.
- * @param WP_REST_Server $server ResponseHandler instance (usually WP_REST_Server).
- * @param WP_REST_Request $request The request that was used to make current response.
- * @return WP_REST_Response Response to be served, with "Allow" header if route has allowed methods.
- */
-function rest_send_allow_header( $response, $server, $request ) {
- $matched_route = $response->get_matched_route();
-
- if ( ! $matched_route ) {
- return $response;
- }
-
- $routes = $server->get_routes();
-
- $allowed_methods = array();
-
- // Get the allowed methods across the routes.
- foreach ( $routes[ $matched_route ] as $_handler ) {
- foreach ( $_handler['methods'] as $handler_method => $value ) {
-
- if ( ! empty( $_handler['permission_callback'] ) ) {
-
- $permission = call_user_func( $_handler['permission_callback'], $request );
-
- $allowed_methods[ $handler_method ] = true === $permission;
- } else {
- $allowed_methods[ $handler_method ] = true;
- }
- }
- }
-
- // Strip out all the methods that are not allowed (false values).
- $allowed_methods = array_filter( $allowed_methods );
-
- if ( $allowed_methods ) {
- $response->header( 'Allow', implode( ', ', array_map( 'strtoupper', array_keys( $allowed_methods ) ) ) );
- }
-
- return $response;
-}
-
-/**
- * Adds the REST API URL to the WP RSD endpoint.
- *
- * @since 4.4.0
- *
- * @see get_rest_url()
- */
-function rest_output_rsd() {
- $api_root = get_rest_url();
-
- if ( empty( $api_root ) ) {
- return;
- }
- ?>
- <api name="WP-API" blogID="1" preferred="false" apiLink="<?php echo esc_url( $api_root ); ?>" />
- <?php
-}
-
-/**
- * Outputs the REST API link tag into page header.
- *
- * @since 4.4.0
- *
- * @see get_rest_url()
- */
-function rest_output_link_wp_head() {
- $api_root = get_rest_url();
-
- if ( empty( $api_root ) ) {
- return;
- }
-
- echo "<link rel='https://api.w.org/' href='" . esc_url( $api_root ) . "' />\n";
-}
-
-/**
- * Sends a Link header for the REST API.
- *
- * @since 4.4.0
- */
-function rest_output_link_header() {
- if ( headers_sent() ) {
- return;
- }
-
- $api_root = get_rest_url();
-
- if ( empty( $api_root ) ) {
- return;
- }
-
- header( 'Link: <' . esc_url_raw( $api_root ) . '>; rel="https://api.w.org/"', false );
-}
-
-/**
- * Checks for errors when using cookie-based authentication.
- *
- * WordPress' built-in cookie authentication is always active
- * for logged in users. However, the API has to check nonces
- * for each request to ensure users are not vulnerable to CSRF.
- *
- * @since 4.4.0
- *
- * @global mixed $wp_rest_auth_cookie
- *
- * @param WP_Error|mixed $result Error from another authentication handler, null if we should handle it,
- * or another value if not.
- * @return WP_Error|mixed|bool WP_Error if the cookie is invalid, the $result, otherwise true.
- */
-function rest_cookie_check_errors( $result ) {
- if ( ! empty( $result ) ) {
- return $result;
- }
-
- global $wp_rest_auth_cookie;
-
- /*
- * Is cookie authentication being used? (If we get an auth
- * error, but we're still logged in, another authentication
- * must have been used).
- */
- if ( true !== $wp_rest_auth_cookie && is_user_logged_in() ) {
- return $result;
- }
-
- // Determine if there is a nonce.
- $nonce = null;
-
- if ( isset( $_REQUEST['_wpnonce'] ) ) {
- $nonce = $_REQUEST['_wpnonce'];
- } elseif ( isset( $_SERVER['HTTP_X_WP_NONCE'] ) ) {
- $nonce = $_SERVER['HTTP_X_WP_NONCE'];
- }
-
- if ( null === $nonce ) {
- // No nonce at all, so act as if it's an unauthenticated request.
- wp_set_current_user( 0 );
- return true;
- }
-
- // Check the nonce.
- $result = wp_verify_nonce( $nonce, 'wp_rest' );
-
- if ( ! $result ) {
- return new WP_Error( 'rest_cookie_invalid_nonce', __( 'Cookie nonce is invalid' ), array( 'status' => 403 ) );
- }
-
- return true;
-}
-
-/**
- * Collects cookie authentication status.
- *
- * Collects errors from wp_validate_auth_cookie for use by rest_cookie_check_errors.
- *
- * @since 4.4.0
- *
- * @see current_action()
- * @global mixed $wp_rest_auth_cookie
- */
-function rest_cookie_collect_status() {
- global $wp_rest_auth_cookie;
-
- $status_type = current_action();
-
- if ( 'auth_cookie_valid' !== $status_type ) {
- $wp_rest_auth_cookie = substr( $status_type, 12 );
- return;
- }
-
- $wp_rest_auth_cookie = true;
-}
-
-/**
- * Parses an RFC3339 timestamp into a DateTime.
- *
- * @since 4.4.0
- *
- * @param string $date RFC3339 timestamp.
- * @param bool $force_utc Optional. Whether to force UTC timezone instead of using
- * the timestamp's timezone. Default false.
- * @return DateTime DateTime instance.
- */
-function rest_parse_date( $date, $force_utc = false ) {
- if ( $force_utc ) {
- $date = preg_replace( '/[+-]\d+:?\d+$/', '+00:00', $date );
- }
-
- $regex = '#^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}(?::\d{2})?)?$#';
-
- if ( ! preg_match( $regex, $date, $matches ) ) {
- return false;
- }
-
- return strtotime( $date );
-}
-
-/**
- * Retrieves a local date with its GMT equivalent, in MySQL datetime format.
- *
- * @since 4.4.0
- *
- * @see rest_parse_date()
- *
- * @param string $date RFC3339 timestamp.
- * @param bool $force_utc Whether a UTC timestamp should be forced. Default false.
- * @return array|null Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s),
- * null on failure.
- */
-function rest_get_date_with_gmt( $date, $force_utc = false ) {
- $date = rest_parse_date( $date, $force_utc );
-
- if ( empty( $date ) ) {
- return null;
- }
-
- $utc = date( 'Y-m-d H:i:s', $date );
- $local = get_date_from_gmt( $utc );
-
- return array( $local, $utc );
-}
</del></span></pre></div>
<a id="trunksrcwpincludesrestapiphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/rest-api.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rest-api.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/rest-api.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,26 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * REST API functions.
- *
- * @package WordPress
- * @subpackage REST_API
- */
-
-/**
- * Version number for our API.
- *
- * @var string
- */
-define( 'REST_API_VERSION', '2.0' );
-
-/** WP_REST_Server class */
-require_once( ABSPATH . WPINC . '/rest-api/class-wp-rest-server.php' );
-
-/** WP_REST_Response class */
-require_once( ABSPATH . WPINC . '/rest-api/class-wp-rest-response.php' );
-
-/** WP_REST_Request class */
-require_once( ABSPATH . WPINC . '/rest-api/class-wp-rest-request.php' );
-
-/** REST functions */
-require_once( ABSPATH . WPINC . '/rest-api/rest-functions.php' );
</del></span></pre></div>
<a id="trunksrcwpincludesrestapiphpfromrev35712trunksrcwpincludesrestapirestfunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/rest-api.php (from rev 35712, trunk/src/wp-includes/rest-api/rest-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rest-api.php (rev 0)
+++ trunk/src/wp-includes/rest-api.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,650 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * REST API functions.
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 4.4.0
+ */
+
+/**
+ * Version number for our API.
+ *
+ * @var string
+ */
+define( 'REST_API_VERSION', '2.0' );
+
+/**
+ * Registers a REST API route.
+ *
+ * @since 4.4.0
+ *
+ * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
+ *
+ * @param string $namespace The first URL segment after core prefix. Should be unique to your package/plugin.
+ * @param string $route The base URL for route you are adding.
+ * @param array $args Optional. Either an array of options for the endpoint, or an array of arrays for
+ * multiple methods. Default empty array.
+ * @param bool $override Optional. If the route already exists, should we override it? True overrides,
+ * false merges (with newer overriding if duplicate keys exist). Default false.
+ * @return bool True on success, false on error.
+ */
+function register_rest_route( $namespace, $route, $args = array(), $override = false ) {
+ /** @var WP_REST_Server $wp_rest_server */
+ global $wp_rest_server;
+
+ if ( empty( $namespace ) ) {
+ /*
+ * Non-namespaced routes are not allowed, with the exception of the main
+ * and namespace indexes. If you really need to register a
+ * non-namespaced route, call `WP_REST_Server::register_route` directly.
+ */
+ _doing_it_wrong( 'register_rest_route', 'Routes must be namespaced with plugin or theme name and version.', '4.4.0' );
+ return false;
+ } else if ( empty( $route ) ) {
+ _doing_it_wrong( 'register_rest_route', 'Route must be specified.', '4.4.0' );
+ return false;
+ }
+
+ if ( isset( $args['callback'] ) ) {
+ // Upgrade a single set to multiple.
+ $args = array( $args );
+ }
+
+ $defaults = array(
+ 'methods' => 'GET',
+ 'callback' => null,
+ 'args' => array(),
+ );
+ foreach ( $args as $key => &$arg_group ) {
+ if ( ! is_numeric( $arg_group ) ) {
+ // Route option, skip here.
+ continue;
+ }
+
+ $arg_group = array_merge( $defaults, $arg_group );
+ }
+
+ $full_route = '/' . trim( $namespace, '/' ) . '/' . trim( $route, '/' );
+ $wp_rest_server->register_route( $namespace, $full_route, $args, $override );
+ return true;
+}
+
+/**
+ * Registers rewrite rules for the API.
+ *
+ * @since 4.4.0
+ *
+ * @see rest_api_register_rewrites()
+ * @global WP $wp Current WordPress environment instance.
+ */
+function rest_api_init() {
+ rest_api_register_rewrites();
+
+ global $wp;
+ $wp->add_query_var( 'rest_route' );
+}
+
+/**
+ * Adds REST rewrite rules.
+ *
+ * @since 4.4.0
+ *
+ * @see add_rewrite_rule()
+ */
+function rest_api_register_rewrites() {
+ add_rewrite_rule( '^' . rest_get_url_prefix() . '/?$','index.php?rest_route=/','top' );
+ add_rewrite_rule( '^' . rest_get_url_prefix() . '/(.*)?','index.php?rest_route=/$matches[1]','top' );
+}
+
+/**
+ * Registers the default REST API filters.
+ *
+ * Attached to the {@see 'rest_api_init'} action
+ * to make testing and disabling these filters easier.
+ *
+ * @since 4.4.0
+ */
+function rest_api_default_filters() {
+ // Deprecated reporting.
+ add_action( 'deprecated_function_run', 'rest_handle_deprecated_function', 10, 3 );
+ add_filter( 'deprecated_function_trigger_error', '__return_false' );
+ add_action( 'deprecated_argument_run', 'rest_handle_deprecated_argument', 10, 3 );
+ add_filter( 'deprecated_argument_trigger_error', '__return_false' );
+
+ // Default serving.
+ add_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
+ add_filter( 'rest_post_dispatch', 'rest_send_allow_header', 10, 3 );
+
+ add_filter( 'rest_pre_dispatch', 'rest_handle_options_request', 10, 3 );
+}
+
+/**
+ * Loads the REST API.
+ *
+ * @since 4.4.0
+ *
+ * @global WP $wp Current WordPress environment instance.
+ * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
+ */
+function rest_api_loaded() {
+ if ( empty( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
+ return;
+ }
+
+ /**
+ * Whether this is a REST Request.
+ *
+ * @since 4.4.0
+ * @var bool
+ */
+ define( 'REST_REQUEST', true );
+
+ /** @var WP_REST_Server $wp_rest_server */
+ global $wp_rest_server;
+
+ /**
+ * Filter the REST Server Class.
+ *
+ * This filter allows you to adjust the server class used by the API, using a
+ * different class to handle requests.
+ *
+ * @since 4.4.0
+ *
+ * @param string $class_name The name of the server class. Default 'WP_REST_Server'.
+ */
+ $wp_rest_server_class = apply_filters( 'wp_rest_server_class', 'WP_REST_Server' );
+ $wp_rest_server = new $wp_rest_server_class;
+
+ /**
+ * Fires when preparing to serve an API request.
+ *
+ * Endpoint objects should be created and register their hooks on this action rather
+ * than another action to ensure they're only loaded when needed.
+ *
+ * @since 4.4.0
+ *
+ * @param WP_REST_Server $wp_rest_server Server object.
+ */
+ do_action( 'rest_api_init', $wp_rest_server );
+
+ // Fire off the request.
+ $wp_rest_server->serve_request( $GLOBALS['wp']->query_vars['rest_route'] );
+
+ // We're done.
+ die();
+}
+
+/**
+ * Retrieves the URL prefix for any API resource.
+ *
+ * @since 4.4.0
+ *
+ * @return string Prefix.
+ */
+function rest_get_url_prefix() {
+ /**
+ * Filter the REST URL prefix.
+ *
+ * @since 4.4.0
+ *
+ * @param string $prefix URL prefix. Default 'wp-json'.
+ */
+ return apply_filters( 'rest_url_prefix', 'wp-json' );
+}
+
+/**
+ * Retrieves the URL to a REST endpoint on a site.
+ *
+ * Note: The returned URL is NOT escaped.
+ *
+ * @since 4.4.0
+ *
+ * @todo Check if this is even necessary
+ *
+ * @param int $blog_id Optional. Blog ID. Default of null returns URL for current blog.
+ * @param string $path Optional. REST route. Default '/'.
+ * @param string $scheme Optional. Sanitization scheme. Default 'rest'.
+ * @return string Full URL to the endpoint.
+ */
+function get_rest_url( $blog_id = null, $path = '/', $scheme = 'rest' ) {
+ if ( empty( $path ) ) {
+ $path = '/';
+ }
+
+ if ( is_multisite() && get_blog_option( $blog_id, 'permalink_structure' ) || get_option( 'permalink_structure' ) ) {
+ $url = get_home_url( $blog_id, rest_get_url_prefix(), $scheme );
+ $url .= '/' . ltrim( $path, '/' );
+ } else {
+ $url = trailingslashit( get_home_url( $blog_id, '', $scheme ) );
+
+ $path = '/' . ltrim( $path, '/' );
+
+ $url = add_query_arg( 'rest_route', $path, $url );
+ }
+
+ if ( is_ssl() ) {
+ // If the current host is the same as the REST URL host, force the REST URL scheme to HTTPS.
+ if ( $_SERVER['SERVER_NAME'] === parse_url( get_home_url( $blog_id ), PHP_URL_HOST ) ) {
+ $url = set_url_scheme( $url, 'https' );
+ }
+ }
+
+ /**
+ * Filter the REST URL.
+ *
+ * Use this filter to adjust the url returned by the `get_rest_url` function.
+ *
+ * @since 4.4.0
+ *
+ * @param string $url REST URL.
+ * @param string $path REST route.
+ * @param int $blog_id Blog ID.
+ * @param string $scheme Sanitization scheme.
+ */
+ return apply_filters( 'rest_url', $url, $path, $blog_id, $scheme );
+}
+
+/**
+ * Retrieves the URL to a REST endpoint.
+ *
+ * Note: The returned URL is NOT escaped.
+ *
+ * @since 4.4.0
+ *
+ * @param string $path Optional. REST route. Default empty.
+ * @param string $scheme Optional. Sanitization scheme. Default 'json'.
+ * @return string Full URL to the endpoint.
+ */
+function rest_url( $path = '', $scheme = 'json' ) {
+ return get_rest_url( null, $path, $scheme );
+}
+
+/**
+ * Do a REST request.
+ *
+ * Used primarily to route internal requests through WP_REST_Server.
+ *
+ * @since 4.4.0
+ *
+ * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
+ *
+ * @param WP_REST_Request|string $request Request.
+ * @return WP_REST_Response REST response.
+ */
+function rest_do_request( $request ) {
+ global $wp_rest_server;
+ $request = rest_ensure_request( $request );
+ return $wp_rest_server->dispatch( $request );
+}
+
+/**
+ * Ensures request arguments are a request object (for consistency).
+ *
+ * @since 4.4.0
+ *
+ * @param array|WP_REST_Request $request Request to check.
+ * @return WP_REST_Request REST request instance.
+ */
+function rest_ensure_request( $request ) {
+ if ( $request instanceof WP_REST_Request ) {
+ return $request;
+ }
+
+ return new WP_REST_Request( 'GET', '', $request );
+}
+
+/**
+ * Ensures a REST response is a response object (for consistency).
+ *
+ * This implements WP_HTTP_Response, allowing usage of `set_status`/`header`/etc
+ * without needing to double-check the object. Will also allow WP_Error to indicate error
+ * responses, so users should immediately check for this value.
+ *
+ * @since 4.4.0
+ *
+ * @param WP_Error|WP_HTTP_Response|mixed $response Response to check.
+ * @return mixed WP_Error if response generated an error, WP_HTTP_Response if response
+ * is a already an instance, otherwise returns a new WP_REST_Response instance.
+ */
+function rest_ensure_response( $response ) {
+ if ( is_wp_error( $response ) ) {
+ return $response;
+ }
+
+ if ( $response instanceof WP_HTTP_Response ) {
+ return $response;
+ }
+
+ return new WP_REST_Response( $response );
+}
+
+/**
+ * Handles _deprecated_function() errors.
+ *
+ * @since 4.4.0
+ *
+ * @param string $function Function name.
+ * @param string $replacement Replacement function name.
+ * @param string $version Version.
+ */
+function rest_handle_deprecated_function( $function, $replacement, $version ) {
+ if ( ! empty( $replacement ) ) {
+ /* translators: 1: function name, 2: WordPress version number, 3: new function name */
+ $string = sprintf( __( '%1$s (since %2$s; use %3$s instead)' ), $function, $version, $replacement );
+ } else {
+ /* translators: 1: function name, 2: WordPress version number */
+ $string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
+ }
+
+ header( sprintf( 'X-WP-DeprecatedFunction: %s', $string ) );
+}
+
+/**
+ * Handles _deprecated_argument() errors.
+ *
+ * @since 4.4.0
+ *
+ * @param string $function Function name.
+ * @param string $replacement Replacement function name.
+ * @param string $version Version.
+ */
+function rest_handle_deprecated_argument( $function, $replacement, $version ) {
+ if ( ! empty( $replacement ) ) {
+ /* translators: 1: function name, 2: WordPress version number, 3: new argument name */
+ $string = sprintf( __( '%1$s (since %2$s; %3$s)' ), $function, $version, $replacement );
+ } else {
+ /* translators: 1: function name, 2: WordPress version number */
+ $string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
+ }
+
+ header( sprintf( 'X-WP-DeprecatedParam: %s', $string ) );
+}
+
+/**
+ * Sends Cross-Origin Resource Sharing headers with API requests.
+ *
+ * @since 4.4.0
+ *
+ * @param mixed $value Response data.
+ * @return mixed Response data.
+ */
+function rest_send_cors_headers( $value ) {
+ $origin = get_http_origin();
+
+ if ( $origin ) {
+ header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
+ header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE' );
+ header( 'Access-Control-Allow-Credentials: true' );
+ }
+
+ return $value;
+}
+
+/**
+ * Handles OPTIONS requests for the server.
+ *
+ * This is handled outside of the server code, as it doesn't obey normal route
+ * mapping.
+ *
+ * @since 4.4.0
+ *
+ * @param mixed $response Current response, either response or `null` to indicate pass-through.
+ * @param WP_REST_Server $handler ResponseHandler instance (usually WP_REST_Server).
+ * @param WP_REST_Request $request The request that was used to make current response.
+ * @return WP_REST_Response Modified response, either response or `null` to indicate pass-through.
+ */
+function rest_handle_options_request( $response, $handler, $request ) {
+ if ( ! empty( $response ) || $request->get_method() !== 'OPTIONS' ) {
+ return $response;
+ }
+
+ $response = new WP_REST_Response();
+ $data = array();
+
+ $accept = array();
+
+ foreach ( $handler->get_routes() as $route => $endpoints ) {
+ $match = preg_match( '@^' . $route . '$@i', $request->get_route(), $args );
+
+ if ( ! $match ) {
+ continue;
+ }
+
+ $data = $handler->get_data_for_route( $route, $endpoints, 'help' );
+ $accept = array_merge( $accept, $data['methods'] );
+ break;
+ }
+ $response->header( 'Accept', implode( ', ', $accept ) );
+
+ $response->set_data( $data );
+ return $response;
+}
+
+/**
+ * Sends the "Allow" header to state all methods that can be sent to the current route.
+ *
+ * @since 4.4.0
+ *
+ * @param WP_REST_Response $response Current response being served.
+ * @param WP_REST_Server $server ResponseHandler instance (usually WP_REST_Server).
+ * @param WP_REST_Request $request The request that was used to make current response.
+ * @return WP_REST_Response Response to be served, with "Allow" header if route has allowed methods.
+ */
+function rest_send_allow_header( $response, $server, $request ) {
+ $matched_route = $response->get_matched_route();
+
+ if ( ! $matched_route ) {
+ return $response;
+ }
+
+ $routes = $server->get_routes();
+
+ $allowed_methods = array();
+
+ // Get the allowed methods across the routes.
+ foreach ( $routes[ $matched_route ] as $_handler ) {
+ foreach ( $_handler['methods'] as $handler_method => $value ) {
+
+ if ( ! empty( $_handler['permission_callback'] ) ) {
+
+ $permission = call_user_func( $_handler['permission_callback'], $request );
+
+ $allowed_methods[ $handler_method ] = true === $permission;
+ } else {
+ $allowed_methods[ $handler_method ] = true;
+ }
+ }
+ }
+
+ // Strip out all the methods that are not allowed (false values).
+ $allowed_methods = array_filter( $allowed_methods );
+
+ if ( $allowed_methods ) {
+ $response->header( 'Allow', implode( ', ', array_map( 'strtoupper', array_keys( $allowed_methods ) ) ) );
+ }
+
+ return $response;
+}
+
+/**
+ * Adds the REST API URL to the WP RSD endpoint.
+ *
+ * @since 4.4.0
+ *
+ * @see get_rest_url()
+ */
+function rest_output_rsd() {
+ $api_root = get_rest_url();
+
+ if ( empty( $api_root ) ) {
+ return;
+ }
+ ?>
+ <api name="WP-API" blogID="1" preferred="false" apiLink="<?php echo esc_url( $api_root ); ?>" />
+ <?php
+}
+
+/**
+ * Outputs the REST API link tag into page header.
+ *
+ * @since 4.4.0
+ *
+ * @see get_rest_url()
+ */
+function rest_output_link_wp_head() {
+ $api_root = get_rest_url();
+
+ if ( empty( $api_root ) ) {
+ return;
+ }
+
+ echo "<link rel='https://api.w.org/' href='" . esc_url( $api_root ) . "' />\n";
+}
+
+/**
+ * Sends a Link header for the REST API.
+ *
+ * @since 4.4.0
+ */
+function rest_output_link_header() {
+ if ( headers_sent() ) {
+ return;
+ }
+
+ $api_root = get_rest_url();
+
+ if ( empty( $api_root ) ) {
+ return;
+ }
+
+ header( 'Link: <' . esc_url_raw( $api_root ) . '>; rel="https://api.w.org/"', false );
+}
+
+/**
+ * Checks for errors when using cookie-based authentication.
+ *
+ * WordPress' built-in cookie authentication is always active
+ * for logged in users. However, the API has to check nonces
+ * for each request to ensure users are not vulnerable to CSRF.
+ *
+ * @since 4.4.0
+ *
+ * @global mixed $wp_rest_auth_cookie
+ *
+ * @param WP_Error|mixed $result Error from another authentication handler, null if we should handle it,
+ * or another value if not.
+ * @return WP_Error|mixed|bool WP_Error if the cookie is invalid, the $result, otherwise true.
+ */
+function rest_cookie_check_errors( $result ) {
+ if ( ! empty( $result ) ) {
+ return $result;
+ }
+
+ global $wp_rest_auth_cookie;
+
+ /*
+ * Is cookie authentication being used? (If we get an auth
+ * error, but we're still logged in, another authentication
+ * must have been used).
+ */
+ if ( true !== $wp_rest_auth_cookie && is_user_logged_in() ) {
+ return $result;
+ }
+
+ // Determine if there is a nonce.
+ $nonce = null;
+
+ if ( isset( $_REQUEST['_wpnonce'] ) ) {
+ $nonce = $_REQUEST['_wpnonce'];
+ } elseif ( isset( $_SERVER['HTTP_X_WP_NONCE'] ) ) {
+ $nonce = $_SERVER['HTTP_X_WP_NONCE'];
+ }
+
+ if ( null === $nonce ) {
+ // No nonce at all, so act as if it's an unauthenticated request.
+ wp_set_current_user( 0 );
+ return true;
+ }
+
+ // Check the nonce.
+ $result = wp_verify_nonce( $nonce, 'wp_rest' );
+
+ if ( ! $result ) {
+ return new WP_Error( 'rest_cookie_invalid_nonce', __( 'Cookie nonce is invalid' ), array( 'status' => 403 ) );
+ }
+
+ return true;
+}
+
+/**
+ * Collects cookie authentication status.
+ *
+ * Collects errors from wp_validate_auth_cookie for use by rest_cookie_check_errors.
+ *
+ * @since 4.4.0
+ *
+ * @see current_action()
+ * @global mixed $wp_rest_auth_cookie
+ */
+function rest_cookie_collect_status() {
+ global $wp_rest_auth_cookie;
+
+ $status_type = current_action();
+
+ if ( 'auth_cookie_valid' !== $status_type ) {
+ $wp_rest_auth_cookie = substr( $status_type, 12 );
+ return;
+ }
+
+ $wp_rest_auth_cookie = true;
+}
+
+/**
+ * Parses an RFC3339 timestamp into a DateTime.
+ *
+ * @since 4.4.0
+ *
+ * @param string $date RFC3339 timestamp.
+ * @param bool $force_utc Optional. Whether to force UTC timezone instead of using
+ * the timestamp's timezone. Default false.
+ * @return DateTime DateTime instance.
+ */
+function rest_parse_date( $date, $force_utc = false ) {
+ if ( $force_utc ) {
+ $date = preg_replace( '/[+-]\d+:?\d+$/', '+00:00', $date );
+ }
+
+ $regex = '#^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}(?::\d{2})?)?$#';
+
+ if ( ! preg_match( $regex, $date, $matches ) ) {
+ return false;
+ }
+
+ return strtotime( $date );
+}
+
+/**
+ * Retrieves a local date with its GMT equivalent, in MySQL datetime format.
+ *
+ * @since 4.4.0
+ *
+ * @see rest_parse_date()
+ *
+ * @param string $date RFC3339 timestamp.
+ * @param bool $force_utc Whether a UTC timestamp should be forced. Default false.
+ * @return array|null Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s),
+ * null on failure.
+ */
+function rest_get_date_with_gmt( $date, $force_utc = false ) {
+ $date = rest_parse_date( $date, $force_utc );
+
+ if ( empty( $date ) ) {
+ return null;
+ }
+
+ $utc = date( 'Y-m-d H:i:s', $date );
+ $local = get_date_from_gmt( $utc );
+
+ return array( $local, $utc );
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesrewriteconstantsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/rewrite-constants.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rewrite-constants.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/rewrite-constants.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,120 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Rewrite API: Constants
- *
- * @package WordPress
- * @subpackage Rewrite
- * @since 4.4.0
- */
-
-/**
- * Endpoint Mask for default, which is nothing.
- *
- * @since 2.1.0
- */
-define('EP_NONE', 0);
-
-/**
- * Endpoint Mask for Permalink.
- *
- * @since 2.1.0
- */
-define('EP_PERMALINK', 1);
-
-/**
- * Endpoint Mask for Attachment.
- *
- * @since 2.1.0
- */
-define('EP_ATTACHMENT', 2);
-
-/**
- * Endpoint Mask for date.
- *
- * @since 2.1.0
- */
-define('EP_DATE', 4);
-
-/**
- * Endpoint Mask for year
- *
- * @since 2.1.0
- */
-define('EP_YEAR', 8);
-
-/**
- * Endpoint Mask for month.
- *
- * @since 2.1.0
- */
-define('EP_MONTH', 16);
-
-/**
- * Endpoint Mask for day.
- *
- * @since 2.1.0
- */
-define('EP_DAY', 32);
-
-/**
- * Endpoint Mask for root.
- *
- * @since 2.1.0
- */
-define('EP_ROOT', 64);
-
-/**
- * Endpoint Mask for comments.
- *
- * @since 2.1.0
- */
-define('EP_COMMENTS', 128);
-
-/**
- * Endpoint Mask for searches.
- *
- * @since 2.1.0
- */
-define('EP_SEARCH', 256);
-
-/**
- * Endpoint Mask for categories.
- *
- * @since 2.1.0
- */
-define('EP_CATEGORIES', 512);
-
-/**
- * Endpoint Mask for tags.
- *
- * @since 2.3.0
- */
-define('EP_TAGS', 1024);
-
-/**
- * Endpoint Mask for authors.
- *
- * @since 2.1.0
- */
-define('EP_AUTHORS', 2048);
-
-/**
- * Endpoint Mask for pages.
- *
- * @since 2.1.0
- */
-define('EP_PAGES', 4096);
-
-/**
- * Endpoint Mask for all archive views.
- *
- * @since 3.7.0
- */
-define( 'EP_ALL_ARCHIVES', EP_DATE | EP_YEAR | EP_MONTH | EP_DAY | EP_CATEGORIES | EP_TAGS | EP_AUTHORS );
-
-/**
- * Endpoint Mask for everything.
- *
- * @since 2.1.0
- */
-define( 'EP_ALL', EP_PERMALINK | EP_ATTACHMENT | EP_ROOT | EP_COMMENTS | EP_SEARCH | EP_PAGES | EP_ALL_ARCHIVES );
</del></span></pre></div>
<a id="trunksrcwpincludesrewritefunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/rewrite-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rewrite-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/rewrite-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,445 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * WordPress Rewrite API
- *
- * @package WordPress
- * @subpackage Rewrite
- */
-
-/**
- * Adds a rewrite rule that transforms a URL structure to a set of query vars.
- *
- * Any value in the $after parameter that isn't 'bottom' will result in the rule
- * being placed at the top of the rewrite rules.
- *
- * @since 2.1.0
- * @since 4.4.0 Array support was added to the `$query` parameter.
- *
- * @global WP_Rewrite $wp_rewrite WordPress Rewrite Component.
- *
- * @param string $regex Regular expression to match request against.
- * @param string|array $query The corresponding query vars for this rewrite rule.
- * @param string $after Optional. Priority of the new rule. Accepts 'top'
- * or 'bottom'. Default 'bottom'.
- */
-function add_rewrite_rule( $regex, $query, $after = 'bottom' ) {
- global $wp_rewrite;
-
- $wp_rewrite->add_rule( $regex, $query, $after );
-}
-
-/**
- * Add a new rewrite tag (like %postname%).
- *
- * The $query parameter is optional. If it is omitted you must ensure that
- * you call this on, or before, the 'init' hook. This is because $query defaults
- * to "$tag=", and for this to work a new query var has to be added.
- *
- * @since 2.1.0
- *
- * @global WP_Rewrite $wp_rewrite
- * @global WP $wp
- *
- * @param string $tag Name of the new rewrite tag.
- * @param string $regex Regular expression to substitute the tag for in rewrite rules.
- * @param string $query Optional. String to append to the rewritten query. Must end in '='. Default empty.
- */
-function add_rewrite_tag( $tag, $regex, $query = '' ) {
- // validate the tag's name
- if ( strlen( $tag ) < 3 || $tag[0] != '%' || $tag[ strlen($tag) - 1 ] != '%' )
- return;
-
- global $wp_rewrite, $wp;
-
- if ( empty( $query ) ) {
- $qv = trim( $tag, '%' );
- $wp->add_query_var( $qv );
- $query = $qv . '=';
- }
-
- $wp_rewrite->add_rewrite_tag( $tag, $regex, $query );
-}
-
-/**
- * Add permalink structure.
- *
- * @since 3.0.0
- *
- * @see WP_Rewrite::add_permastruct()
- * @global WP_Rewrite $wp_rewrite
- *
- * @param string $name Name for permalink structure.
- * @param string $struct Permalink structure.
- * @param array $args Optional. Arguments for building the rules from the permalink structure,
- * see WP_Rewrite::add_permastruct() for full details. Default empty array.
- */
-function add_permastruct( $name, $struct, $args = array() ) {
- global $wp_rewrite;
-
- // backwards compatibility for the old parameters: $with_front and $ep_mask
- if ( ! is_array( $args ) )
- $args = array( 'with_front' => $args );
- if ( func_num_args() == 4 )
- $args['ep_mask'] = func_get_arg( 3 );
-
- $wp_rewrite->add_permastruct( $name, $struct, $args );
-}
-
-/**
- * Add a new feed type like /atom1/.
- *
- * @since 2.1.0
- *
- * @global WP_Rewrite $wp_rewrite
- *
- * @param string $feedname Feed name.
- * @param callable $function Callback to run on feed display.
- * @return string Feed action name.
- */
-function add_feed( $feedname, $function ) {
- global $wp_rewrite;
-
- if ( ! in_array( $feedname, $wp_rewrite->feeds ) ) {
- $wp_rewrite->feeds[] = $feedname;
- }
-
- $hook = 'do_feed_' . $feedname;
-
- // Remove default function hook
- remove_action( $hook, $hook );
-
- add_action( $hook, $function, 10, 2 );
-
- return $hook;
-}
-
-/**
- * Remove rewrite rules and then recreate rewrite rules.
- *
- * @since 3.0.0
- *
- * @global WP_Rewrite $wp_rewrite
- *
- * @param bool $hard Whether to update .htaccess (hard flush) or just update
- * rewrite_rules transient (soft flush). Default is true (hard).
- */
-function flush_rewrite_rules( $hard = true ) {
- global $wp_rewrite;
- $wp_rewrite->flush_rules( $hard );
-}
-
-/**
- * Add an endpoint, like /trackback/.
- *
- * Adding an endpoint creates extra rewrite rules for each of the matching
- * places specified by the provided bitmask. For example:
- *
- * add_rewrite_endpoint( 'json', EP_PERMALINK | EP_PAGES );
- *
- * will add a new rewrite rule ending with "json(/(.*))?/?$" for every permastruct
- * that describes a permalink (post) or page. This is rewritten to "json=$match"
- * where $match is the part of the URL matched by the endpoint regex (e.g. "foo" in
- * "[permalink]/json/foo/").
- *
- * A new query var with the same name as the endpoint will also be created.
- *
- * When specifying $places ensure that you are using the EP_* constants (or a
- * combination of them using the bitwise OR operator) as their values are not
- * guaranteed to remain static (especially `EP_ALL`).
- *
- * Be sure to flush the rewrite rules - see flush_rewrite_rules() - when your plugin gets
- * activated and deactivated.
- *
- * @since 2.1.0
- * @since 4.3.0 Added support for skipping query var registration by passing `false` to `$query_var`.
- *
- * @global WP_Rewrite $wp_rewrite
- *
- * @param string $name Name of the endpoint.
- * @param int $places Endpoint mask describing the places the endpoint should be added.
- * @param string|bool $query_var Name of the corresponding query variable. Pass `false` to skip registering a query_var
- * for this endpoint. Defaults to the value of `$name`.
- */
-function add_rewrite_endpoint( $name, $places, $query_var = true ) {
- global $wp_rewrite;
- $wp_rewrite->add_endpoint( $name, $places, $query_var );
-}
-
-/**
- * Filter the URL base for taxonomies.
- *
- * To remove any manually prepended /index.php/.
- *
- * @access private
- * @since 2.6.0
- *
- * @param string $base The taxonomy base that we're going to filter
- * @return string
- */
-function _wp_filter_taxonomy_base( $base ) {
- if ( !empty( $base ) ) {
- $base = preg_replace( '|^/index\.php/|', '', $base );
- $base = trim( $base, '/' );
- }
- return $base;
-}
-
-
-/**
- * Resolve numeric slugs that collide with date permalinks.
- *
- * Permalinks of posts with numeric slugs can sometimes look to WP_Query::parse_query()
- * like a date archive, as when your permalink structure is `/%year%/%postname%/` and
- * a post with post_name '05' has the URL `/2015/05/`.
- *
- * This function detects conflicts of this type and resolves them in favor of the
- * post permalink.
- *
- * Note that, since 4.3.0, wp_unique_post_slug() prevents the creation of post slugs
- * that would result in a date archive conflict. The resolution performed in this
- * function is primarily for legacy content, as well as cases when the admin has changed
- * the site's permalink structure in a way that introduces URL conflicts.
- *
- * @since 4.3.0
- *
- * @param array $query_vars Optional. Query variables for setting up the loop, as determined in
- * WP::parse_request(). Default empty array.
- * @return array Returns the original array of query vars, with date/post conflicts resolved.
- */
-function wp_resolve_numeric_slug_conflicts( $query_vars = array() ) {
- if ( ! isset( $query_vars['year'] ) && ! isset( $query_vars['monthnum'] ) && ! isset( $query_vars['day'] ) ) {
- return $query_vars;
- }
-
- // Identify the 'postname' position in the permastruct array.
- $permastructs = array_values( array_filter( explode( '/', get_option( 'permalink_structure' ) ) ) );
- $postname_index = array_search( '%postname%', $permastructs );
-
- if ( false === $postname_index ) {
- return $query_vars;
- }
-
- /*
- * A numeric slug could be confused with a year, month, or day, depending on position. To account for
- * the possibility of post pagination (eg 2015/2 for the second page of a post called '2015'), our
- * `is_*` checks are generous: check for year-slug clashes when `is_year` *or* `is_month`, and check
- * for month-slug clashes when `is_month` *or* `is_day`.
- */
- $compare = '';
- if ( 0 === $postname_index && ( isset( $query_vars['year'] ) || isset( $query_vars['monthnum'] ) ) ) {
- $compare = 'year';
- } elseif ( '%year%' === $permastructs[ $postname_index - 1 ] && ( isset( $query_vars['monthnum'] ) || isset( $query_vars['day'] ) ) ) {
- $compare = 'monthnum';
- } elseif ( '%monthnum%' === $permastructs[ $postname_index - 1 ] && isset( $query_vars['day'] ) ) {
- $compare = 'day';
- }
-
- if ( ! $compare ) {
- return $query_vars;
- }
-
- // This is the potentially clashing slug.
- $value = $query_vars[ $compare ];
-
- $post = get_page_by_path( $value, OBJECT, 'post' );
- if ( ! ( $post instanceof WP_Post ) ) {
- return $query_vars;
- }
-
- // If the date of the post doesn't match the date specified in the URL, resolve to the date archive.
- if ( preg_match( '/^([0-9]{4})\-([0-9]{2})/', $post->post_date, $matches ) && isset( $query_vars['year'] ) && ( 'monthnum' === $compare || 'day' === $compare ) ) {
- // $matches[1] is the year the post was published.
- if ( intval( $query_vars['year'] ) !== intval( $matches[1] ) ) {
- return $query_vars;
- }
-
- // $matches[2] is the month the post was published.
- if ( 'day' === $compare && isset( $query_vars['monthnum'] ) && intval( $query_vars['monthnum'] ) !== intval( $matches[2] ) ) {
- return $query_vars;
- }
- }
-
- /*
- * If the located post contains nextpage pagination, then the URL chunk following postname may be
- * intended as the page number. Verify that it's a valid page before resolving to it.
- */
- $maybe_page = '';
- if ( 'year' === $compare && isset( $query_vars['monthnum'] ) ) {
- $maybe_page = $query_vars['monthnum'];
- } elseif ( 'monthnum' === $compare && isset( $query_vars['day'] ) ) {
- $maybe_page = $query_vars['day'];
- }
- // Bug found in #11694 - 'page' was returning '/4'
- $maybe_page = (int) trim( $maybe_page, '/' );
-
- $post_page_count = substr_count( $post->post_content, '<!--nextpage-->' ) + 1;
-
- // If the post doesn't have multiple pages, but a 'page' candidate is found, resolve to the date archive.
- if ( 1 === $post_page_count && $maybe_page ) {
- return $query_vars;
- }
-
- // If the post has multiple pages and the 'page' number isn't valid, resolve to the date archive.
- if ( $post_page_count > 1 && $maybe_page > $post_page_count ) {
- return $query_vars;
- }
-
- // If we've gotten to this point, we have a slug/date clash. First, adjust for nextpage.
- if ( '' !== $maybe_page ) {
- $query_vars['page'] = intval( $maybe_page );
- }
-
- // Next, unset autodetected date-related query vars.
- unset( $query_vars['year'] );
- unset( $query_vars['monthnum'] );
- unset( $query_vars['day'] );
-
- // Then, set the identified post.
- $query_vars['name'] = $post->post_name;
-
- // Finally, return the modified query vars.
- return $query_vars;
-}
-
-/**
- * Examine a url and try to determine the post ID it represents.
- *
- * Checks are supposedly from the hosted site blog.
- *
- * @since 1.0.0
- *
- * @global WP_Rewrite $wp_rewrite
- * @global WP $wp
- *
- * @param string $url Permalink to check.
- * @return int Post ID, or 0 on failure.
- */
-function url_to_postid( $url ) {
- global $wp_rewrite;
-
- /**
- * Filter the URL to derive the post ID from.
- *
- * @since 2.2.0
- *
- * @param string $url The URL to derive the post ID from.
- */
- $url = apply_filters( 'url_to_postid', $url );
-
- // First, check to see if there is a 'p=N' or 'page_id=N' to match against
- if ( preg_match('#[?&](p|page_id|attachment_id)=(\d+)#', $url, $values) ) {
- $id = absint($values[2]);
- if ( $id )
- return $id;
- }
-
- // Check to see if we are using rewrite rules
- $rewrite = $wp_rewrite->wp_rewrite_rules();
-
- // Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options
- if ( empty($rewrite) )
- return 0;
-
- // Get rid of the #anchor
- $url_split = explode('#', $url);
- $url = $url_split[0];
-
- // Get rid of URL ?query=string
- $url_split = explode('?', $url);
- $url = $url_split[0];
-
- // Set the correct URL scheme.
- $url = set_url_scheme( $url );
-
- // Add 'www.' if it is absent and should be there
- if ( false !== strpos(home_url(), '://www.') && false === strpos($url, '://www.') )
- $url = str_replace('://', '://www.', $url);
-
- // Strip 'www.' if it is present and shouldn't be
- if ( false === strpos(home_url(), '://www.') )
- $url = str_replace('://www.', '://', $url);
-
- // Strip 'index.php/' if we're not using path info permalinks
- if ( !$wp_rewrite->using_index_permalinks() )
- $url = str_replace( $wp_rewrite->index . '/', '', $url );
-
- if ( false !== strpos( trailingslashit( $url ), home_url( '/' ) ) ) {
- // Chop off http://domain.com/[path]
- $url = str_replace(home_url(), '', $url);
- } else {
- // Chop off /path/to/blog
- $home_path = parse_url( home_url( '/' ) );
- $home_path = isset( $home_path['path'] ) ? $home_path['path'] : '' ;
- $url = preg_replace( sprintf( '#^%s#', preg_quote( $home_path ) ), '', trailingslashit( $url ) );
- }
-
- // Trim leading and lagging slashes
- $url = trim($url, '/');
-
- $request = $url;
- $post_type_query_vars = array();
-
- foreach ( get_post_types( array() , 'objects' ) as $post_type => $t ) {
- if ( ! empty( $t->query_var ) )
- $post_type_query_vars[ $t->query_var ] = $post_type;
- }
-
- // Look for matches.
- $request_match = $request;
- foreach ( (array)$rewrite as $match => $query) {
-
- // If the requesting file is the anchor of the match, prepend it
- // to the path info.
- if ( !empty($url) && ($url != $request) && (strpos($match, $url) === 0) )
- $request_match = $url . '/' . $request;
-
- if ( preg_match("#^$match#", $request_match, $matches) ) {
-
- if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) ) {
- // This is a verbose page match, let's check to be sure about it.
- $page = get_page_by_path( $matches[ $varmatch[1] ] );
- if ( ! $page ) {
- continue;
- }
-
- $post_status_obj = get_post_status_object( $page->post_status );
- if ( ! $post_status_obj->public && ! $post_status_obj->protected
- && ! $post_status_obj->private && $post_status_obj->exclude_from_search ) {
- continue;
- }
- }
-
- // Got a match.
- // Trim the query of everything up to the '?'.
- $query = preg_replace("!^.+\?!", '', $query);
-
- // Substitute the substring matches into the query.
- $query = addslashes(WP_MatchesMapRegex::apply($query, $matches));
-
- // Filter out non-public query vars
- global $wp;
- parse_str( $query, $query_vars );
- $query = array();
- foreach ( (array) $query_vars as $key => $value ) {
- if ( in_array( $key, $wp->public_query_vars ) ){
- $query[$key] = $value;
- if ( isset( $post_type_query_vars[$key] ) ) {
- $query['post_type'] = $post_type_query_vars[$key];
- $query['name'] = $value;
- }
- }
- }
-
- // Resolve conflicts between posts with numeric slugs and date archive queries.
- $query = wp_resolve_numeric_slug_conflicts( $query );
-
- // Do the query
- $query = new WP_Query( $query );
- if ( ! empty( $query->posts ) && $query->is_singular )
- return $query->post->ID;
- else
- return 0;
- }
- }
- return 0;
-}
</del></span></pre></div>
<a id="trunksrcwpincludesrewritephp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/rewrite.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rewrite.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/rewrite.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,11 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * WordPress Rewrite API
- *
- * @package WordPress
- * @subpackage Rewrite
- */
-
-require_once( ABSPATH . WPINC . '/rewrite-constants.php' );
-require_once( ABSPATH . WPINC . '/rewrite-functions.php' );
-require_once( ABSPATH . WPINC . '/class-wp-rewrite.php' );
</del><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="trunksrcwpincludesrewritephpfromrev35712trunksrcwpincludesrewritefunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/rewrite.php (from rev 35712, trunk/src/wp-includes/rewrite-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rewrite.php (rev 0)
+++ trunk/src/wp-includes/rewrite.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,557 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * WordPress Rewrite API
+ *
+ * @package WordPress
+ * @subpackage Rewrite
+ */
+
+/**
+ * Endpoint Mask for default, which is nothing.
+ *
+ * @since 2.1.0
+ */
+define('EP_NONE', 0);
+
+/**
+ * Endpoint Mask for Permalink.
+ *
+ * @since 2.1.0
+ */
+define('EP_PERMALINK', 1);
+
+/**
+ * Endpoint Mask for Attachment.
+ *
+ * @since 2.1.0
+ */
+define('EP_ATTACHMENT', 2);
+
+/**
+ * Endpoint Mask for date.
+ *
+ * @since 2.1.0
+ */
+define('EP_DATE', 4);
+
+/**
+ * Endpoint Mask for year
+ *
+ * @since 2.1.0
+ */
+define('EP_YEAR', 8);
+
+/**
+ * Endpoint Mask for month.
+ *
+ * @since 2.1.0
+ */
+define('EP_MONTH', 16);
+
+/**
+ * Endpoint Mask for day.
+ *
+ * @since 2.1.0
+ */
+define('EP_DAY', 32);
+
+/**
+ * Endpoint Mask for root.
+ *
+ * @since 2.1.0
+ */
+define('EP_ROOT', 64);
+
+/**
+ * Endpoint Mask for comments.
+ *
+ * @since 2.1.0
+ */
+define('EP_COMMENTS', 128);
+
+/**
+ * Endpoint Mask for searches.
+ *
+ * @since 2.1.0
+ */
+define('EP_SEARCH', 256);
+
+/**
+ * Endpoint Mask for categories.
+ *
+ * @since 2.1.0
+ */
+define('EP_CATEGORIES', 512);
+
+/**
+ * Endpoint Mask for tags.
+ *
+ * @since 2.3.0
+ */
+define('EP_TAGS', 1024);
+
+/**
+ * Endpoint Mask for authors.
+ *
+ * @since 2.1.0
+ */
+define('EP_AUTHORS', 2048);
+
+/**
+ * Endpoint Mask for pages.
+ *
+ * @since 2.1.0
+ */
+define('EP_PAGES', 4096);
+
+/**
+ * Endpoint Mask for all archive views.
+ *
+ * @since 3.7.0
+ */
+define( 'EP_ALL_ARCHIVES', EP_DATE | EP_YEAR | EP_MONTH | EP_DAY | EP_CATEGORIES | EP_TAGS | EP_AUTHORS );
+
+/**
+ * Endpoint Mask for everything.
+ *
+ * @since 2.1.0
+ */
+define( 'EP_ALL', EP_PERMALINK | EP_ATTACHMENT | EP_ROOT | EP_COMMENTS | EP_SEARCH | EP_PAGES | EP_ALL_ARCHIVES );
+
+/**
+ * Adds a rewrite rule that transforms a URL structure to a set of query vars.
+ *
+ * Any value in the $after parameter that isn't 'bottom' will result in the rule
+ * being placed at the top of the rewrite rules.
+ *
+ * @since 2.1.0
+ * @since 4.4.0 Array support was added to the `$query` parameter.
+ *
+ * @global WP_Rewrite $wp_rewrite WordPress Rewrite Component.
+ *
+ * @param string $regex Regular expression to match request against.
+ * @param string|array $query The corresponding query vars for this rewrite rule.
+ * @param string $after Optional. Priority of the new rule. Accepts 'top'
+ * or 'bottom'. Default 'bottom'.
+ */
+function add_rewrite_rule( $regex, $query, $after = 'bottom' ) {
+ global $wp_rewrite;
+
+ $wp_rewrite->add_rule( $regex, $query, $after );
+}
+
+/**
+ * Add a new rewrite tag (like %postname%).
+ *
+ * The $query parameter is optional. If it is omitted you must ensure that
+ * you call this on, or before, the 'init' hook. This is because $query defaults
+ * to "$tag=", and for this to work a new query var has to be added.
+ *
+ * @since 2.1.0
+ *
+ * @global WP_Rewrite $wp_rewrite
+ * @global WP $wp
+ *
+ * @param string $tag Name of the new rewrite tag.
+ * @param string $regex Regular expression to substitute the tag for in rewrite rules.
+ * @param string $query Optional. String to append to the rewritten query. Must end in '='. Default empty.
+ */
+function add_rewrite_tag( $tag, $regex, $query = '' ) {
+ // validate the tag's name
+ if ( strlen( $tag ) < 3 || $tag[0] != '%' || $tag[ strlen($tag) - 1 ] != '%' )
+ return;
+
+ global $wp_rewrite, $wp;
+
+ if ( empty( $query ) ) {
+ $qv = trim( $tag, '%' );
+ $wp->add_query_var( $qv );
+ $query = $qv . '=';
+ }
+
+ $wp_rewrite->add_rewrite_tag( $tag, $regex, $query );
+}
+
+/**
+ * Add permalink structure.
+ *
+ * @since 3.0.0
+ *
+ * @see WP_Rewrite::add_permastruct()
+ * @global WP_Rewrite $wp_rewrite
+ *
+ * @param string $name Name for permalink structure.
+ * @param string $struct Permalink structure.
+ * @param array $args Optional. Arguments for building the rules from the permalink structure,
+ * see WP_Rewrite::add_permastruct() for full details. Default empty array.
+ */
+function add_permastruct( $name, $struct, $args = array() ) {
+ global $wp_rewrite;
+
+ // backwards compatibility for the old parameters: $with_front and $ep_mask
+ if ( ! is_array( $args ) )
+ $args = array( 'with_front' => $args );
+ if ( func_num_args() == 4 )
+ $args['ep_mask'] = func_get_arg( 3 );
+
+ $wp_rewrite->add_permastruct( $name, $struct, $args );
+}
+
+/**
+ * Add a new feed type like /atom1/.
+ *
+ * @since 2.1.0
+ *
+ * @global WP_Rewrite $wp_rewrite
+ *
+ * @param string $feedname Feed name.
+ * @param callable $function Callback to run on feed display.
+ * @return string Feed action name.
+ */
+function add_feed( $feedname, $function ) {
+ global $wp_rewrite;
+
+ if ( ! in_array( $feedname, $wp_rewrite->feeds ) ) {
+ $wp_rewrite->feeds[] = $feedname;
+ }
+
+ $hook = 'do_feed_' . $feedname;
+
+ // Remove default function hook
+ remove_action( $hook, $hook );
+
+ add_action( $hook, $function, 10, 2 );
+
+ return $hook;
+}
+
+/**
+ * Remove rewrite rules and then recreate rewrite rules.
+ *
+ * @since 3.0.0
+ *
+ * @global WP_Rewrite $wp_rewrite
+ *
+ * @param bool $hard Whether to update .htaccess (hard flush) or just update
+ * rewrite_rules transient (soft flush). Default is true (hard).
+ */
+function flush_rewrite_rules( $hard = true ) {
+ global $wp_rewrite;
+ $wp_rewrite->flush_rules( $hard );
+}
+
+/**
+ * Add an endpoint, like /trackback/.
+ *
+ * Adding an endpoint creates extra rewrite rules for each of the matching
+ * places specified by the provided bitmask. For example:
+ *
+ * add_rewrite_endpoint( 'json', EP_PERMALINK | EP_PAGES );
+ *
+ * will add a new rewrite rule ending with "json(/(.*))?/?$" for every permastruct
+ * that describes a permalink (post) or page. This is rewritten to "json=$match"
+ * where $match is the part of the URL matched by the endpoint regex (e.g. "foo" in
+ * "[permalink]/json/foo/").
+ *
+ * A new query var with the same name as the endpoint will also be created.
+ *
+ * When specifying $places ensure that you are using the EP_* constants (or a
+ * combination of them using the bitwise OR operator) as their values are not
+ * guaranteed to remain static (especially `EP_ALL`).
+ *
+ * Be sure to flush the rewrite rules - see flush_rewrite_rules() - when your plugin gets
+ * activated and deactivated.
+ *
+ * @since 2.1.0
+ * @since 4.3.0 Added support for skipping query var registration by passing `false` to `$query_var`.
+ *
+ * @global WP_Rewrite $wp_rewrite
+ *
+ * @param string $name Name of the endpoint.
+ * @param int $places Endpoint mask describing the places the endpoint should be added.
+ * @param string|bool $query_var Name of the corresponding query variable. Pass `false` to skip registering a query_var
+ * for this endpoint. Defaults to the value of `$name`.
+ */
+function add_rewrite_endpoint( $name, $places, $query_var = true ) {
+ global $wp_rewrite;
+ $wp_rewrite->add_endpoint( $name, $places, $query_var );
+}
+
+/**
+ * Filter the URL base for taxonomies.
+ *
+ * To remove any manually prepended /index.php/.
+ *
+ * @access private
+ * @since 2.6.0
+ *
+ * @param string $base The taxonomy base that we're going to filter
+ * @return string
+ */
+function _wp_filter_taxonomy_base( $base ) {
+ if ( !empty( $base ) ) {
+ $base = preg_replace( '|^/index\.php/|', '', $base );
+ $base = trim( $base, '/' );
+ }
+ return $base;
+}
+
+
+/**
+ * Resolve numeric slugs that collide with date permalinks.
+ *
+ * Permalinks of posts with numeric slugs can sometimes look to WP_Query::parse_query()
+ * like a date archive, as when your permalink structure is `/%year%/%postname%/` and
+ * a post with post_name '05' has the URL `/2015/05/`.
+ *
+ * This function detects conflicts of this type and resolves them in favor of the
+ * post permalink.
+ *
+ * Note that, since 4.3.0, wp_unique_post_slug() prevents the creation of post slugs
+ * that would result in a date archive conflict. The resolution performed in this
+ * function is primarily for legacy content, as well as cases when the admin has changed
+ * the site's permalink structure in a way that introduces URL conflicts.
+ *
+ * @since 4.3.0
+ *
+ * @param array $query_vars Optional. Query variables for setting up the loop, as determined in
+ * WP::parse_request(). Default empty array.
+ * @return array Returns the original array of query vars, with date/post conflicts resolved.
+ */
+function wp_resolve_numeric_slug_conflicts( $query_vars = array() ) {
+ if ( ! isset( $query_vars['year'] ) && ! isset( $query_vars['monthnum'] ) && ! isset( $query_vars['day'] ) ) {
+ return $query_vars;
+ }
+
+ // Identify the 'postname' position in the permastruct array.
+ $permastructs = array_values( array_filter( explode( '/', get_option( 'permalink_structure' ) ) ) );
+ $postname_index = array_search( '%postname%', $permastructs );
+
+ if ( false === $postname_index ) {
+ return $query_vars;
+ }
+
+ /*
+ * A numeric slug could be confused with a year, month, or day, depending on position. To account for
+ * the possibility of post pagination (eg 2015/2 for the second page of a post called '2015'), our
+ * `is_*` checks are generous: check for year-slug clashes when `is_year` *or* `is_month`, and check
+ * for month-slug clashes when `is_month` *or* `is_day`.
+ */
+ $compare = '';
+ if ( 0 === $postname_index && ( isset( $query_vars['year'] ) || isset( $query_vars['monthnum'] ) ) ) {
+ $compare = 'year';
+ } elseif ( '%year%' === $permastructs[ $postname_index - 1 ] && ( isset( $query_vars['monthnum'] ) || isset( $query_vars['day'] ) ) ) {
+ $compare = 'monthnum';
+ } elseif ( '%monthnum%' === $permastructs[ $postname_index - 1 ] && isset( $query_vars['day'] ) ) {
+ $compare = 'day';
+ }
+
+ if ( ! $compare ) {
+ return $query_vars;
+ }
+
+ // This is the potentially clashing slug.
+ $value = $query_vars[ $compare ];
+
+ $post = get_page_by_path( $value, OBJECT, 'post' );
+ if ( ! ( $post instanceof WP_Post ) ) {
+ return $query_vars;
+ }
+
+ // If the date of the post doesn't match the date specified in the URL, resolve to the date archive.
+ if ( preg_match( '/^([0-9]{4})\-([0-9]{2})/', $post->post_date, $matches ) && isset( $query_vars['year'] ) && ( 'monthnum' === $compare || 'day' === $compare ) ) {
+ // $matches[1] is the year the post was published.
+ if ( intval( $query_vars['year'] ) !== intval( $matches[1] ) ) {
+ return $query_vars;
+ }
+
+ // $matches[2] is the month the post was published.
+ if ( 'day' === $compare && isset( $query_vars['monthnum'] ) && intval( $query_vars['monthnum'] ) !== intval( $matches[2] ) ) {
+ return $query_vars;
+ }
+ }
+
+ /*
+ * If the located post contains nextpage pagination, then the URL chunk following postname may be
+ * intended as the page number. Verify that it's a valid page before resolving to it.
+ */
+ $maybe_page = '';
+ if ( 'year' === $compare && isset( $query_vars['monthnum'] ) ) {
+ $maybe_page = $query_vars['monthnum'];
+ } elseif ( 'monthnum' === $compare && isset( $query_vars['day'] ) ) {
+ $maybe_page = $query_vars['day'];
+ }
+ // Bug found in #11694 - 'page' was returning '/4'
+ $maybe_page = (int) trim( $maybe_page, '/' );
+
+ $post_page_count = substr_count( $post->post_content, '<!--nextpage-->' ) + 1;
+
+ // If the post doesn't have multiple pages, but a 'page' candidate is found, resolve to the date archive.
+ if ( 1 === $post_page_count && $maybe_page ) {
+ return $query_vars;
+ }
+
+ // If the post has multiple pages and the 'page' number isn't valid, resolve to the date archive.
+ if ( $post_page_count > 1 && $maybe_page > $post_page_count ) {
+ return $query_vars;
+ }
+
+ // If we've gotten to this point, we have a slug/date clash. First, adjust for nextpage.
+ if ( '' !== $maybe_page ) {
+ $query_vars['page'] = intval( $maybe_page );
+ }
+
+ // Next, unset autodetected date-related query vars.
+ unset( $query_vars['year'] );
+ unset( $query_vars['monthnum'] );
+ unset( $query_vars['day'] );
+
+ // Then, set the identified post.
+ $query_vars['name'] = $post->post_name;
+
+ // Finally, return the modified query vars.
+ return $query_vars;
+}
+
+/**
+ * Examine a url and try to determine the post ID it represents.
+ *
+ * Checks are supposedly from the hosted site blog.
+ *
+ * @since 1.0.0
+ *
+ * @global WP_Rewrite $wp_rewrite
+ * @global WP $wp
+ *
+ * @param string $url Permalink to check.
+ * @return int Post ID, or 0 on failure.
+ */
+function url_to_postid( $url ) {
+ global $wp_rewrite;
+
+ /**
+ * Filter the URL to derive the post ID from.
+ *
+ * @since 2.2.0
+ *
+ * @param string $url The URL to derive the post ID from.
+ */
+ $url = apply_filters( 'url_to_postid', $url );
+
+ // First, check to see if there is a 'p=N' or 'page_id=N' to match against
+ if ( preg_match('#[?&](p|page_id|attachment_id)=(\d+)#', $url, $values) ) {
+ $id = absint($values[2]);
+ if ( $id )
+ return $id;
+ }
+
+ // Check to see if we are using rewrite rules
+ $rewrite = $wp_rewrite->wp_rewrite_rules();
+
+ // Not using rewrite rules, and 'p=N' and 'page_id=N' methods failed, so we're out of options
+ if ( empty($rewrite) )
+ return 0;
+
+ // Get rid of the #anchor
+ $url_split = explode('#', $url);
+ $url = $url_split[0];
+
+ // Get rid of URL ?query=string
+ $url_split = explode('?', $url);
+ $url = $url_split[0];
+
+ // Set the correct URL scheme.
+ $url = set_url_scheme( $url );
+
+ // Add 'www.' if it is absent and should be there
+ if ( false !== strpos(home_url(), '://www.') && false === strpos($url, '://www.') )
+ $url = str_replace('://', '://www.', $url);
+
+ // Strip 'www.' if it is present and shouldn't be
+ if ( false === strpos(home_url(), '://www.') )
+ $url = str_replace('://www.', '://', $url);
+
+ // Strip 'index.php/' if we're not using path info permalinks
+ if ( !$wp_rewrite->using_index_permalinks() )
+ $url = str_replace( $wp_rewrite->index . '/', '', $url );
+
+ if ( false !== strpos( trailingslashit( $url ), home_url( '/' ) ) ) {
+ // Chop off http://domain.com/[path]
+ $url = str_replace(home_url(), '', $url);
+ } else {
+ // Chop off /path/to/blog
+ $home_path = parse_url( home_url( '/' ) );
+ $home_path = isset( $home_path['path'] ) ? $home_path['path'] : '' ;
+ $url = preg_replace( sprintf( '#^%s#', preg_quote( $home_path ) ), '', trailingslashit( $url ) );
+ }
+
+ // Trim leading and lagging slashes
+ $url = trim($url, '/');
+
+ $request = $url;
+ $post_type_query_vars = array();
+
+ foreach ( get_post_types( array() , 'objects' ) as $post_type => $t ) {
+ if ( ! empty( $t->query_var ) )
+ $post_type_query_vars[ $t->query_var ] = $post_type;
+ }
+
+ // Look for matches.
+ $request_match = $request;
+ foreach ( (array)$rewrite as $match => $query) {
+
+ // If the requesting file is the anchor of the match, prepend it
+ // to the path info.
+ if ( !empty($url) && ($url != $request) && (strpos($match, $url) === 0) )
+ $request_match = $url . '/' . $request;
+
+ if ( preg_match("#^$match#", $request_match, $matches) ) {
+
+ if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) ) {
+ // This is a verbose page match, let's check to be sure about it.
+ $page = get_page_by_path( $matches[ $varmatch[1] ] );
+ if ( ! $page ) {
+ continue;
+ }
+
+ $post_status_obj = get_post_status_object( $page->post_status );
+ if ( ! $post_status_obj->public && ! $post_status_obj->protected
+ && ! $post_status_obj->private && $post_status_obj->exclude_from_search ) {
+ continue;
+ }
+ }
+
+ // Got a match.
+ // Trim the query of everything up to the '?'.
+ $query = preg_replace("!^.+\?!", '', $query);
+
+ // Substitute the substring matches into the query.
+ $query = addslashes(WP_MatchesMapRegex::apply($query, $matches));
+
+ // Filter out non-public query vars
+ global $wp;
+ parse_str( $query, $query_vars );
+ $query = array();
+ foreach ( (array) $query_vars as $key => $value ) {
+ if ( in_array( $key, $wp->public_query_vars ) ){
+ $query[$key] = $value;
+ if ( isset( $post_type_query_vars[$key] ) ) {
+ $query['post_type'] = $post_type_query_vars[$key];
+ $query['name'] = $value;
+ }
+ }
+ }
+
+ // Resolve conflicts between posts with numeric slugs and date archive queries.
+ $query = wp_resolve_numeric_slug_conflicts( $query );
+
+ // Do the query
+ $query = new WP_Query( $query );
+ if ( ! empty( $query->posts ) && $query->is_singular )
+ return $query->post->ID;
+ else
+ return 0;
+ }
+ }
+ return 0;
+}
</ins></span></pre></div>
<a id="trunksrcwpincludestaxonomyfunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/taxonomy-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/taxonomy-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/taxonomy-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,4692 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Taxonomy API: Top-level taxonomy functionality
- *
- * @package WordPress
- * @subpackage Taxonomy
- * @since 4.4.0
- */
-
-//
-// Taxonomy Registration
-//
-
-/**
- * Creates the initial taxonomies.
- *
- * This function fires twice: in wp-settings.php before plugins are loaded (for
- * backwards compatibility reasons), and again on the {@see 'init'} action. We must
- * avoid registering rewrite rules before the {@see 'init'} action.
- *
- * @since 2.8.0
- *
- * @global WP_Rewrite $wp_rewrite The WordPress rewrite class.
- */
-function create_initial_taxonomies() {
- global $wp_rewrite;
-
- if ( ! did_action( 'init' ) ) {
- $rewrite = array( 'category' => false, 'post_tag' => false, 'post_format' => false );
- } else {
-
- /**
- * Filter the post formats rewrite base.
- *
- * @since 3.1.0
- *
- * @param string $context Context of the rewrite base. Default 'type'.
- */
- $post_format_base = apply_filters( 'post_format_rewrite_base', 'type' );
- $rewrite = array(
- 'category' => array(
- 'hierarchical' => true,
- 'slug' => get_option('category_base') ? get_option('category_base') : 'category',
- 'with_front' => ! get_option('category_base') || $wp_rewrite->using_index_permalinks(),
- 'ep_mask' => EP_CATEGORIES,
- ),
- 'post_tag' => array(
- 'hierarchical' => false,
- 'slug' => get_option('tag_base') ? get_option('tag_base') : 'tag',
- 'with_front' => ! get_option('tag_base') || $wp_rewrite->using_index_permalinks(),
- 'ep_mask' => EP_TAGS,
- ),
- 'post_format' => $post_format_base ? array( 'slug' => $post_format_base ) : false,
- );
- }
-
- register_taxonomy( 'category', 'post', array(
- 'hierarchical' => true,
- 'query_var' => 'category_name',
- 'rewrite' => $rewrite['category'],
- 'public' => true,
- 'show_ui' => true,
- 'show_admin_column' => true,
- '_builtin' => true,
- ) );
-
- register_taxonomy( 'post_tag', 'post', array(
- 'hierarchical' => false,
- 'query_var' => 'tag',
- 'rewrite' => $rewrite['post_tag'],
- 'public' => true,
- 'show_ui' => true,
- 'show_admin_column' => true,
- '_builtin' => true,
- ) );
-
- register_taxonomy( 'nav_menu', 'nav_menu_item', array(
- 'public' => false,
- 'hierarchical' => false,
- 'labels' => array(
- 'name' => __( 'Navigation Menus' ),
- 'singular_name' => __( 'Navigation Menu' ),
- ),
- 'query_var' => false,
- 'rewrite' => false,
- 'show_ui' => false,
- '_builtin' => true,
- 'show_in_nav_menus' => false,
- ) );
-
- register_taxonomy( 'link_category', 'link', array(
- 'hierarchical' => false,
- 'labels' => array(
- 'name' => __( 'Link Categories' ),
- 'singular_name' => __( 'Link Category' ),
- 'search_items' => __( 'Search Link Categories' ),
- 'popular_items' => null,
- 'all_items' => __( 'All Link Categories' ),
- 'edit_item' => __( 'Edit Link Category' ),
- 'update_item' => __( 'Update Link Category' ),
- 'add_new_item' => __( 'Add New Link Category' ),
- 'new_item_name' => __( 'New Link Category Name' ),
- 'separate_items_with_commas' => null,
- 'add_or_remove_items' => null,
- 'choose_from_most_used' => null,
- ),
- 'capabilities' => array(
- 'manage_terms' => 'manage_links',
- 'edit_terms' => 'manage_links',
- 'delete_terms' => 'manage_links',
- 'assign_terms' => 'manage_links',
- ),
- 'query_var' => false,
- 'rewrite' => false,
- 'public' => false,
- 'show_ui' => true,
- '_builtin' => true,
- ) );
-
- register_taxonomy( 'post_format', 'post', array(
- 'public' => true,
- 'hierarchical' => false,
- 'labels' => array(
- 'name' => _x( 'Format', 'post format' ),
- 'singular_name' => _x( 'Format', 'post format' ),
- ),
- 'query_var' => true,
- 'rewrite' => $rewrite['post_format'],
- 'show_ui' => false,
- '_builtin' => true,
- 'show_in_nav_menus' => current_theme_supports( 'post-formats' ),
- ) );
-}
-
-/**
- * Retrieves a list of registered taxonomy names or objects.
- *
- * @since 3.0.0
- *
- * @global array $wp_taxonomies The registered taxonomies.
- *
- * @param array $args Optional. An array of `key => value` arguments to match against the taxonomy objects.
- * Default empty array.
- * @param string $output Optional. The type of output to return in the array. Accepts either taxonomy 'names'
- * or 'objects'. Default 'names'.
- * @param string $operator Optional. The logical operation to perform. Accepts 'and' or 'or'. 'or' means only
- * one element from the array needs to match; 'and' means all elements must match.
- * Default 'and'.
- * @return array A list of taxonomy names or objects.
- */
-function get_taxonomies( $args = array(), $output = 'names', $operator = 'and' ) {
- global $wp_taxonomies;
-
- $field = ('names' == $output) ? 'name' : false;
-
- return wp_filter_object_list($wp_taxonomies, $args, $operator, $field);
-}
-
-/**
- * Return all of the taxonomy names that are of $object_type.
- *
- * It appears that this function can be used to find all of the names inside of
- * $wp_taxonomies global variable.
- *
- * `<?php $taxonomies = get_object_taxonomies('post'); ?>` Should
- * result in `Array( 'category', 'post_tag' )`
- *
- * @since 2.3.0
- *
- * @global array $wp_taxonomies The registered taxonomies.
- *
- * @param array|string|WP_Post $object Name of the type of taxonomy object, or an object (row from posts)
- * @param string $output Optional. The type of output to return in the array. Accepts either
- * taxonomy 'names' or 'objects'. Default 'names'.
- * @return array The names of all taxonomy of $object_type.
- */
-function get_object_taxonomies( $object, $output = 'names' ) {
- global $wp_taxonomies;
-
- if ( is_object($object) ) {
- if ( $object->post_type == 'attachment' )
- return get_attachment_taxonomies($object);
- $object = $object->post_type;
- }
-
- $object = (array) $object;
-
- $taxonomies = array();
- foreach ( (array) $wp_taxonomies as $tax_name => $tax_obj ) {
- if ( array_intersect($object, (array) $tax_obj->object_type) ) {
- if ( 'names' == $output )
- $taxonomies[] = $tax_name;
- else
- $taxonomies[ $tax_name ] = $tax_obj;
- }
- }
-
- return $taxonomies;
-}
-
-/**
- * Retrieves the taxonomy object of $taxonomy.
- *
- * The get_taxonomy function will first check that the parameter string given
- * is a taxonomy object and if it is, it will return it.
- *
- * @since 2.3.0
- *
- * @global array $wp_taxonomies The registered taxonomies.
- *
- * @param string $taxonomy Name of taxonomy object to return.
- * @return object|false The Taxonomy Object or false if $taxonomy doesn't exist.
- */
-function get_taxonomy( $taxonomy ) {
- global $wp_taxonomies;
-
- if ( ! taxonomy_exists( $taxonomy ) )
- return false;
-
- return $wp_taxonomies[$taxonomy];
-}
-
-/**
- * Checks that the taxonomy name exists.
- *
- * Formerly is_taxonomy(), introduced in 2.3.0.
- *
- * @since 3.0.0
- *
- * @global array $wp_taxonomies The registered taxonomies.
- *
- * @param string $taxonomy Name of taxonomy object.
- * @return bool Whether the taxonomy exists.
- */
-function taxonomy_exists( $taxonomy ) {
- global $wp_taxonomies;
-
- return isset( $wp_taxonomies[$taxonomy] );
-}
-
-/**
- * Whether the taxonomy object is hierarchical.
- *
- * Checks to make sure that the taxonomy is an object first. Then Gets the
- * object, and finally returns the hierarchical value in the object.
- *
- * A false return value might also mean that the taxonomy does not exist.
- *
- * @since 2.3.0
- *
- * @param string $taxonomy Name of taxonomy object.
- * @return bool Whether the taxonomy is hierarchical.
- */
-function is_taxonomy_hierarchical($taxonomy) {
- if ( ! taxonomy_exists($taxonomy) )
- return false;
-
- $taxonomy = get_taxonomy($taxonomy);
- return $taxonomy->hierarchical;
-}
-
-/**
- * Creates or modifies a taxonomy object.
- *
- * Note: Do not use before the {@see 'init'} hook.
- *
- * A simple function for creating or modifying a taxonomy object based on the
- * parameters given. The function will accept an array (third optional
- * parameter), along with strings for the taxonomy name and another string for
- * the object type.
- *
- * @since 2.3.0
- * @since 4.2.0 Introduced `show_in_quick_edit` argument.
- * @since 4.4.0 The `show_ui` argument is now enforced on the term editing screen.
- * @since 4.4.0 The `public` argument now controls whether the taxonomy can be queried on the front-end.
- *
- * @global array $wp_taxonomies Registered taxonomies.
- * @global WP $wp WP instance.
- *
- * @param string $taxonomy Taxonomy key, must not exceed 32 characters.
- * @param array|string $object_type Name of the object type for the taxonomy object.
- * @param array|string $args {
- * Optional. Array or query string of arguments for registering a taxonomy.
- *
- * @type string $label Name of the taxonomy shown in the menu. Usually plural. If not set,
- * `$labels['name']` will be used.
- * @type array $labels An array of labels for this taxonomy. By default, Tag labels are used for
- * non-hierarchical taxonmies, and Category labels are used for hierarchical
- * taxonomies. See accepted values in get_taxonomy_labels().
- * Default empty array.
- * @type string $description A short descriptive summary of what the taxonomy is for. Default empty.
- * @type bool $public Whether the taxonomy is publicly queryable. Default true.
- * @type bool $hierarchical Whether the taxonomy is hierarchical. Default false.
- * @type bool $show_ui Whether to generate and allow a UI for managing terms in this taxonomy in
- * the admin. If not set, the default is inherited from `$public`
- * (default true).
- * @type bool $show_in_menu Whether to show the taxonomy in the admin menu. If true, the taxonomy is
- * shown as a submenu of the object type menu. If false, no menu is shown.
- * `$show_ui` must be true. If not set, default is inherited from `$show_ui`
- * (default true).
- * @type bool $show_in_nav_menus Makes this taxonomy available for selection in navigation menus. If not
- * set, the default is inherited from `$public` (default true).
- * @type bool $show_tagcloud Whether to list the taxonomy in the Tag Cloud Widget controls. If not set,
- * the default is inherited from `$show_ui` (default true).
- * @type bool $show_in_quick_edit Whether to show the taxonomy in the quick/bulk edit panel. It not set,
- * the default is inherited from `$show_ui` (default true).
- * @type bool $show_admin_column Whether to display a column for the taxonomy on its post type listing
- * screens. Default false.
- * @type bool|callable $meta_box_cb Provide a callback function for the meta box display. If not set,
- * post_categories_meta_box() is used for hierarchical taxonomies, and
- * post_tags_meta_box() is used for non-hierarchical. If false, no meta
- * box is shown.
- * @type array $capabilities {
- * Array of capabilities for this taxonomy.
- *
- * @type string $manage_terms Default 'manage_categories'.
- * @type string $edit_terms Default 'manage_categories'.
- * @type string $delete_terms Default 'manage_categories'.
- * @type string $assign_terms Default 'edit_posts'.
- * }
- * @type bool|array $rewrite {
- * Triggers the handling of rewrites for this taxonomy. Default true, using $taxonomy as slug. To prevent
- * rewrite, set to false. To specify rewrite rules, an array can be passed with any of these keys:
- *
- * @type string $slug Customize the permastruct slug. Default `$taxonomy` key.
- * @type bool $with_front Should the permastruct be prepended with WP_Rewrite::$front. Default true.
- * @type bool $hierarchical Either hierarchical rewrite tag or not. Default false.
- * @type int $ep_mask Assign an endpoint mask. Default `EP_NONE`.
- * }
- * @type string $query_var Sets the query var key for this taxonomy. Default `$taxonomy` key. If
- * false, a taxonomy cannot be loaded at `?{query_var}={term_slug}`. If a
- * string, the query `?{query_var}={term_slug}` will be valid.
- * @type callable $update_count_callback Works much like a hook, in that it will be called when the count is
- * updated. Default _update_post_term_count() for taxonomies attached
- * to post types, which confirms that the objects are published before
- * counting them. Default _update_generic_term_count() for taxonomies
- * attached to other object types, such as users.
- * @type bool $_builtin This taxonomy is a "built-in" taxonomy. INTERNAL USE ONLY!
- * Default false.
- * }
- * @return WP_Error|void WP_Error, if errors.
- */
-function register_taxonomy( $taxonomy, $object_type, $args = array() ) {
- global $wp_taxonomies, $wp;
-
- if ( ! is_array( $wp_taxonomies ) )
- $wp_taxonomies = array();
-
- $args = wp_parse_args( $args );
-
- /**
- * Filter the arguments for registering a taxonomy.
- *
- * @since 4.4.0
- *
- * @param array $args Array of arguments for registering a taxonomy.
- * @param array $object_type Array of names of object types for the taxonomy.
- * @param string $taxonomy Taxonomy key.
- */
- $args = apply_filters( 'register_taxonomy_args', $args, $taxonomy, (array) $object_type );
-
- $defaults = array(
- 'labels' => array(),
- 'description' => '',
- 'public' => true,
- 'hierarchical' => false,
- 'show_ui' => null,
- 'show_in_menu' => null,
- 'show_in_nav_menus' => null,
- 'show_tagcloud' => null,
- 'show_in_quick_edit' => null,
- 'show_admin_column' => false,
- 'meta_box_cb' => null,
- 'capabilities' => array(),
- 'rewrite' => true,
- 'query_var' => $taxonomy,
- 'update_count_callback' => '',
- '_builtin' => false,
- );
- $args = array_merge( $defaults, $args );
-
- if ( empty( $taxonomy ) || strlen( $taxonomy ) > 32 ) {
- _doing_it_wrong( __FUNCTION__, __( 'Taxonomy names must be between 1 and 32 characters in length.' ), '4.2' );
- return new WP_Error( 'taxonomy_length_invalid', __( 'Taxonomy names must be between 1 and 32 characters in length.' ) );
- }
-
- // Non-public taxonomies should not register query vars, except in the admin.
- if ( false !== $args['query_var'] && ( is_admin() || false !== $args['public'] ) && ! empty( $wp ) ) {
- if ( true === $args['query_var'] )
- $args['query_var'] = $taxonomy;
- else
- $args['query_var'] = sanitize_title_with_dashes( $args['query_var'] );
- $wp->add_query_var( $args['query_var'] );
- }
-
- if ( false !== $args['rewrite'] && ( is_admin() || '' != get_option( 'permalink_structure' ) ) ) {
- $args['rewrite'] = wp_parse_args( $args['rewrite'], array(
- 'with_front' => true,
- 'hierarchical' => false,
- 'ep_mask' => EP_NONE,
- ) );
-
- if ( empty( $args['rewrite']['slug'] ) )
- $args['rewrite']['slug'] = sanitize_title_with_dashes( $taxonomy );
-
- if ( $args['hierarchical'] && $args['rewrite']['hierarchical'] )
- $tag = '(.+?)';
- else
- $tag = '([^/]+)';
-
- add_rewrite_tag( "%$taxonomy%", $tag, $args['query_var'] ? "{$args['query_var']}=" : "taxonomy=$taxonomy&term=" );
- add_permastruct( $taxonomy, "{$args['rewrite']['slug']}/%$taxonomy%", $args['rewrite'] );
- }
-
- // If not set, default to the setting for public.
- if ( null === $args['show_ui'] )
- $args['show_ui'] = $args['public'];
-
- // If not set, default to the setting for show_ui.
- if ( null === $args['show_in_menu' ] || ! $args['show_ui'] )
- $args['show_in_menu' ] = $args['show_ui'];
-
- // If not set, default to the setting for public.
- if ( null === $args['show_in_nav_menus'] )
- $args['show_in_nav_menus'] = $args['public'];
-
- // If not set, default to the setting for show_ui.
- if ( null === $args['show_tagcloud'] )
- $args['show_tagcloud'] = $args['show_ui'];
-
- // If not set, default to the setting for show_ui.
- if ( null === $args['show_in_quick_edit'] ) {
- $args['show_in_quick_edit'] = $args['show_ui'];
- }
-
- $default_caps = array(
- 'manage_terms' => 'manage_categories',
- 'edit_terms' => 'manage_categories',
- 'delete_terms' => 'manage_categories',
- 'assign_terms' => 'edit_posts',
- );
- $args['cap'] = (object) array_merge( $default_caps, $args['capabilities'] );
- unset( $args['capabilities'] );
-
- $args['name'] = $taxonomy;
- $args['object_type'] = array_unique( (array) $object_type );
-
- $args['labels'] = get_taxonomy_labels( (object) $args );
- $args['label'] = $args['labels']->name;
-
- // If not set, use the default meta box
- if ( null === $args['meta_box_cb'] ) {
- if ( $args['hierarchical'] )
- $args['meta_box_cb'] = 'post_categories_meta_box';
- else
- $args['meta_box_cb'] = 'post_tags_meta_box';
- }
-
- $wp_taxonomies[ $taxonomy ] = (object) $args;
-
- // register callback handling for metabox
- add_filter( 'wp_ajax_add-' . $taxonomy, '_wp_ajax_add_hierarchical_term' );
-
- /**
- * Fires after a taxonomy is registered.
- *
- * @since 3.3.0
- *
- * @param string $taxonomy Taxonomy slug.
- * @param array|string $object_type Object type or array of object types.
- * @param array $args Array of taxonomy registration arguments.
- */
- do_action( 'registered_taxonomy', $taxonomy, $object_type, $args );
-}
-
-/**
- * Builds an object with all taxonomy labels out of a taxonomy object
- *
- * Accepted keys of the label array in the taxonomy object:
- *
- * - name - general name for the taxonomy, usually plural. The same as and overridden by $tax->label. Default is Tags/Categories
- * - singular_name - name for one object of this taxonomy. Default is Tag/Category
- * - search_items - Default is Search Tags/Search Categories
- * - popular_items - This string isn't used on hierarchical taxonomies. Default is Popular Tags
- * - all_items - Default is All Tags/All Categories
- * - parent_item - This string isn't used on non-hierarchical taxonomies. In hierarchical ones the default is Parent Category
- * - parent_item_colon - The same as `parent_item`, but with colon `:` in the end
- * - edit_item - Default is Edit Tag/Edit Category
- * - view_item - Default is View Tag/View Category
- * - update_item - Default is Update Tag/Update Category
- * - add_new_item - Default is Add New Tag/Add New Category
- * - new_item_name - Default is New Tag Name/New Category Name
- * - separate_items_with_commas - This string isn't used on hierarchical taxonomies. Default is "Separate tags with commas", used in the meta box.
- * - add_or_remove_items - This string isn't used on hierarchical taxonomies. Default is "Add or remove tags", used in the meta box when JavaScript is disabled.
- * - choose_from_most_used - This string isn't used on hierarchical taxonomies. Default is "Choose from the most used tags", used in the meta box.
- * - not_found - Default is "No tags found"/"No categories found", used in the meta box and taxonomy list table.
- * - no_terms - Default is "No tags"/"No categories", used in the posts and media list tables.
- * - items_list_navigation - String for the table pagination hidden heading.
- * - items_list - String for the table hidden heading.
- *
- * Above, the first default value is for non-hierarchical taxonomies (like tags) and the second one is for hierarchical taxonomies (like categories).
- *
- * @todo Better documentation for the labels array.
- *
- * @since 3.0.0
- * @since 4.3.0 Added the `no_terms` label.
- * @since 4.4.0 Added the `items_list_navigation` and `items_list` labels.
- *
- * @param object $tax Taxonomy object.
- * @return object object with all the labels as member variables.
- */
-function get_taxonomy_labels( $tax ) {
- $tax->labels = (array) $tax->labels;
-
- if ( isset( $tax->helps ) && empty( $tax->labels['separate_items_with_commas'] ) )
- $tax->labels['separate_items_with_commas'] = $tax->helps;
-
- if ( isset( $tax->no_tagcloud ) && empty( $tax->labels['not_found'] ) )
- $tax->labels['not_found'] = $tax->no_tagcloud;
-
- $nohier_vs_hier_defaults = array(
- 'name' => array( _x( 'Tags', 'taxonomy general name' ), _x( 'Categories', 'taxonomy general name' ) ),
- 'singular_name' => array( _x( 'Tag', 'taxonomy singular name' ), _x( 'Category', 'taxonomy singular name' ) ),
- 'search_items' => array( __( 'Search Tags' ), __( 'Search Categories' ) ),
- 'popular_items' => array( __( 'Popular Tags' ), null ),
- 'all_items' => array( __( 'All Tags' ), __( 'All Categories' ) ),
- 'parent_item' => array( null, __( 'Parent Category' ) ),
- 'parent_item_colon' => array( null, __( 'Parent Category:' ) ),
- 'edit_item' => array( __( 'Edit Tag' ), __( 'Edit Category' ) ),
- 'view_item' => array( __( 'View Tag' ), __( 'View Category' ) ),
- 'update_item' => array( __( 'Update Tag' ), __( 'Update Category' ) ),
- 'add_new_item' => array( __( 'Add New Tag' ), __( 'Add New Category' ) ),
- 'new_item_name' => array( __( 'New Tag Name' ), __( 'New Category Name' ) ),
- 'separate_items_with_commas' => array( __( 'Separate tags with commas' ), null ),
- 'add_or_remove_items' => array( __( 'Add or remove tags' ), null ),
- 'choose_from_most_used' => array( __( 'Choose from the most used tags' ), null ),
- 'not_found' => array( __( 'No tags found.' ), __( 'No categories found.' ) ),
- 'no_terms' => array( __( 'No tags' ), __( 'No categories' ) ),
- 'items_list_navigation' => array( __( 'Tags list navigation' ), __( 'Categories list navigation' ) ),
- 'items_list' => array( __( 'Tags list' ), __( 'Categories list' ) ),
- );
- $nohier_vs_hier_defaults['menu_name'] = $nohier_vs_hier_defaults['name'];
-
- $labels = _get_custom_object_labels( $tax, $nohier_vs_hier_defaults );
-
- $taxonomy = $tax->name;
-
- $default_labels = clone $labels;
-
- /**
- * Filter the labels of a specific taxonomy.
- *
- * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug.
- *
- * @since 4.4.0
- *
- * @see get_taxonomy_labels() for the full list of taxonomy labels.
- *
- * @param object $labels Object with labels for the taxonomy as member variables.
- */
- $labels = apply_filters( "taxonomy_labels_{$taxonomy}", $labels );
-
- // Ensure that the filtered labels contain all required default values.
- $labels = (object) array_merge( (array) $default_labels, (array) $labels );
-
- return $labels;
-}
-
-/**
- * Add an already registered taxonomy to an object type.
- *
- * @since 3.0.0
- *
- * @global array $wp_taxonomies The registered taxonomies.
- *
- * @param string $taxonomy Name of taxonomy object.
- * @param string $object_type Name of the object type.
- * @return bool True if successful, false if not.
- */
-function register_taxonomy_for_object_type( $taxonomy, $object_type) {
- global $wp_taxonomies;
-
- if ( !isset($wp_taxonomies[$taxonomy]) )
- return false;
-
- if ( ! get_post_type_object($object_type) )
- return false;
-
- if ( ! in_array( $object_type, $wp_taxonomies[$taxonomy]->object_type ) )
- $wp_taxonomies[$taxonomy]->object_type[] = $object_type;
-
- // Filter out empties.
- $wp_taxonomies[ $taxonomy ]->object_type = array_filter( $wp_taxonomies[ $taxonomy ]->object_type );
-
- return true;
-}
-
-/**
- * Remove an already registered taxonomy from an object type.
- *
- * @since 3.7.0
- *
- * @global array $wp_taxonomies The registered taxonomies.
- *
- * @param string $taxonomy Name of taxonomy object.
- * @param string $object_type Name of the object type.
- * @return bool True if successful, false if not.
- */
-function unregister_taxonomy_for_object_type( $taxonomy, $object_type ) {
- global $wp_taxonomies;
-
- if ( ! isset( $wp_taxonomies[ $taxonomy ] ) )
- return false;
-
- if ( ! get_post_type_object( $object_type ) )
- return false;
-
- $key = array_search( $object_type, $wp_taxonomies[ $taxonomy ]->object_type, true );
- if ( false === $key )
- return false;
-
- unset( $wp_taxonomies[ $taxonomy ]->object_type[ $key ] );
- return true;
-}
-
-//
-// Term API
-//
-
-/**
- * Retrieve object_ids of valid taxonomy and term.
- *
- * The strings of $taxonomies must exist before this function will continue. On
- * failure of finding a valid taxonomy, it will return an WP_Error class, kind
- * of like Exceptions in PHP 5, except you can't catch them. Even so, you can
- * still test for the WP_Error class and get the error message.
- *
- * The $terms aren't checked the same as $taxonomies, but still need to exist
- * for $object_ids to be returned.
- *
- * It is possible to change the order that object_ids is returned by either
- * using PHP sort family functions or using the database by using $args with
- * either ASC or DESC array. The value should be in the key named 'order'.
- *
- * @since 2.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int|array $term_ids Term id or array of term ids of terms that will be used.
- * @param string|array $taxonomies String of taxonomy name or Array of string values of taxonomy names.
- * @param array|string $args Change the order of the object_ids, either ASC or DESC.
- * @return WP_Error|array If the taxonomy does not exist, then WP_Error will be returned. On success.
- * the array can be empty meaning that there are no $object_ids found or it will return the $object_ids found.
- */
-function get_objects_in_term( $term_ids, $taxonomies, $args = array() ) {
- global $wpdb;
-
- if ( ! is_array( $term_ids ) ) {
- $term_ids = array( $term_ids );
- }
- if ( ! is_array( $taxonomies ) ) {
- $taxonomies = array( $taxonomies );
- }
- foreach ( (array) $taxonomies as $taxonomy ) {
- if ( ! taxonomy_exists( $taxonomy ) ) {
- return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
- }
- }
-
- $defaults = array( 'order' => 'ASC' );
- $args = wp_parse_args( $args, $defaults );
-
- $order = ( 'desc' == strtolower( $args['order'] ) ) ? 'DESC' : 'ASC';
-
- $term_ids = array_map('intval', $term_ids );
-
- $taxonomies = "'" . implode( "', '", $taxonomies ) . "'";
- $term_ids = "'" . implode( "', '", $term_ids ) . "'";
-
- $object_ids = $wpdb->get_col("SELECT tr.object_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tt.term_id IN ($term_ids) ORDER BY tr.object_id $order");
-
- if ( ! $object_ids ){
- return array();
- }
- return $object_ids;
-}
-
-/**
- * Given a taxonomy query, generates SQL to be appended to a main query.
- *
- * @since 3.1.0
- *
- * @see WP_Tax_Query
- *
- * @param array $tax_query A compact tax query
- * @param string $primary_table
- * @param string $primary_id_column
- * @return array
- */
-function get_tax_sql( $tax_query, $primary_table, $primary_id_column ) {
- $tax_query_obj = new WP_Tax_Query( $tax_query );
- return $tax_query_obj->get_sql( $primary_table, $primary_id_column );
-}
-
-/**
- * Get all Term data from database by Term ID.
- *
- * The usage of the get_term function is to apply filters to a term object. It
- * is possible to get a term object from the database before applying the
- * filters.
- *
- * $term ID must be part of $taxonomy, to get from the database. Failure, might
- * be able to be captured by the hooks. Failure would be the same value as $wpdb
- * returns for the get_row method.
- *
- * There are two hooks, one is specifically for each term, named 'get_term', and
- * the second is for the taxonomy name, 'term_$taxonomy'. Both hooks gets the
- * term object, and the taxonomy name as parameters. Both hooks are expected to
- * return a Term object.
- *
- * {@see 'get_term'} hook - Takes two parameters the term Object and the taxonomy name.
- * Must return term object. Used in get_term() as a catch-all filter for every
- * $term.
- *
- * {@see 'get_$taxonomy'} hook - Takes two parameters the term Object and the taxonomy
- * name. Must return term object. $taxonomy will be the taxonomy name, so for
- * example, if 'category', it would be 'get_category' as the filter name. Useful
- * for custom taxonomies or plugging into default taxonomies.
- *
- * @todo Better formatting for DocBlock
- *
- * @since 2.3.0
- * @since 4.4.0 Converted to return a WP_Term object if `$output` is `OBJECT`.
- * The `$taxonomy` parameter was made optional.
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- * @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param.
- *
- * @param int|WP_Term|object $term If integer, term data will be fetched from the database, or from the cache if
- * available. If stdClass object (as in the results of a database query), will apply
- * filters and return a `WP_Term` object corresponding to the `$term` data. If `WP_Term`,
- * will return `$term`.
- * @param string $taxonomy Optional. Taxonomy name that $term is part of.
- * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N
- * @param string $filter Optional, default is raw or no WordPress defined filter will applied.
- * @return mixed Type corresponding to `$output` on success or null on failure. When `$output` is `OBJECT`,
- * a WP_Term instance is returned. If taxonomy does not exist then WP_Error will be returned.
- */
-function get_term( $term, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) {
- if ( empty( $term ) ) {
- return new WP_Error( 'invalid_term', __( 'Empty Term' ) );
- }
-
- if ( $taxonomy && ! taxonomy_exists( $taxonomy ) ) {
- return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
- }
-
- if ( $term instanceof WP_Term ) {
- $_term = $term;
- } elseif ( is_object( $term ) ) {
- if ( empty( $term->filter ) || 'raw' === $term->filter ) {
- $_term = sanitize_term( $term, $taxonomy, 'raw' );
- $_term = new WP_Term( $_term );
- } else {
- $_term = WP_Term::get_instance( $term->term_id );
- }
- } else {
- $_term = WP_Term::get_instance( $term, $taxonomy );
- }
-
- if ( is_wp_error( $_term ) ) {
- return $_term;
- } elseif ( ! $_term ) {
- return null;
- }
-
- /**
- * Filter a term.
- *
- * @since 2.3.0
- * @since 4.4.0 `$_term` can now also be a WP_Term object.
- *
- * @param int|WP_Term $_term Term object or ID.
- * @param string $taxonomy The taxonomy slug.
- */
- $_term = apply_filters( 'get_term', $_term, $taxonomy );
-
- /**
- * Filter a taxonomy.
- *
- * The dynamic portion of the filter name, `$taxonomy`, refers
- * to the taxonomy slug.
- *
- * @since 2.3.0
- * @since 4.4.0 `$_term` can now also be a WP_Term object.
- *
- * @param int|WP_Term $_term Term object or ID.
- * @param string $taxonomy The taxonomy slug.
- */
- $_term = apply_filters( "get_$taxonomy", $_term, $taxonomy );
-
- // Sanitize term, according to the specified filter.
- $_term->filter( $filter );
-
- if ( $output == ARRAY_A ) {
- return $_term->to_array();
- } elseif ( $output == ARRAY_N ) {
- return array_values( $_term->to_array() );
- }
-
- return $_term;
-}
-
-/**
- * Get all Term data from database by Term field and data.
- *
- * Warning: $value is not escaped for 'name' $field. You must do it yourself, if
- * required.
- *
- * The default $field is 'id', therefore it is possible to also use null for
- * field, but not recommended that you do so.
- *
- * If $value does not exist, the return value will be false. If $taxonomy exists
- * and $field and $value combinations exist, the Term will be returned.
- *
- * @todo Better formatting for DocBlock.
- *
- * @since 2.3.0
- * @since 4.4.0 `$taxonomy` is optional if `$field` is 'term_taxonomy_id'. Converted to return
- * a WP_Term object if `$output` is `OBJECT`.
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- * @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param.
- *
- * @param string $field Either 'slug', 'name', 'id' (term_id), or 'term_taxonomy_id'
- * @param string|int $value Search for this term value
- * @param string $taxonomy Taxonomy name. Optional, if `$field` is 'term_taxonomy_id'.
- * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N
- * @param string $filter Optional, default is raw or no WordPress defined filter will applied.
- * @return WP_Term|bool WP_Term instance on success. Will return false if `$taxonomy` does not exist
- * or `$term` was not found.
- */
-function get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) {
- global $wpdb;
-
- // 'term_taxonomy_id' lookups don't require taxonomy checks.
- if ( 'term_taxonomy_id' !== $field && ! taxonomy_exists( $taxonomy ) ) {
- return false;
- }
-
- $tax_clause = $wpdb->prepare( "AND tt.taxonomy = %s", $taxonomy );
-
- if ( 'slug' == $field ) {
- $_field = 't.slug';
- $value = sanitize_title($value);
- if ( empty($value) )
- return false;
- } elseif ( 'name' == $field ) {
- // Assume already escaped
- $value = wp_unslash($value);
- $_field = 't.name';
- } elseif ( 'term_taxonomy_id' == $field ) {
- $value = (int) $value;
- $_field = 'tt.term_taxonomy_id';
-
- // No `taxonomy` clause when searching by 'term_taxonomy_id'.
- $tax_clause = '';
- } else {
- $term = get_term( (int) $value, $taxonomy, $output, $filter );
- if ( is_wp_error( $term ) || is_null( $term ) ) {
- $term = false;
- }
- return $term;
- }
-
- $term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE $_field = %s $tax_clause LIMIT 1", $value ) );
- if ( ! $term )
- return false;
-
- // In the case of 'term_taxonomy_id', override the provided `$taxonomy` with whatever we find in the db.
- if ( 'term_taxonomy_id' === $field ) {
- $taxonomy = $term->taxonomy;
- }
-
- wp_cache_add( $term->term_id, $term, 'terms' );
-
- return get_term( $term, $taxonomy, $output, $filter );
-}
-
-/**
- * Merge all term children into a single array of their IDs.
- *
- * This recursive function will merge all of the children of $term into the same
- * array of term IDs. Only useful for taxonomies which are hierarchical.
- *
- * Will return an empty array if $term does not exist in $taxonomy.
- *
- * @since 2.3.0
- *
- * @param string $term_id ID of Term to get children.
- * @param string $taxonomy Taxonomy Name.
- * @return array|WP_Error List of Term IDs. WP_Error returned if `$taxonomy` does not exist.
- */
-function get_term_children( $term_id, $taxonomy ) {
- if ( ! taxonomy_exists($taxonomy) )
- return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
-
- $term_id = intval( $term_id );
-
- $terms = _get_term_hierarchy($taxonomy);
-
- if ( ! isset($terms[$term_id]) )
- return array();
-
- $children = $terms[$term_id];
-
- foreach ( (array) $terms[$term_id] as $child ) {
- if ( $term_id == $child ) {
- continue;
- }
-
- if ( isset($terms[$child]) )
- $children = array_merge($children, get_term_children($child, $taxonomy));
- }
-
- return $children;
-}
-
-/**
- * Get sanitized Term field.
- *
- * The function is for contextual reasons and for simplicity of usage.
- *
- * @since 2.3.0
- * @since 4.4.0 The `$taxonomy` parameter was made optional. `$term` can also now accept a WP_Term object.
- *
- * @see sanitize_term_field()
- *
- * @param string $field Term field to fetch.
- * @param int|WP_Term $term Term ID or object.
- * @param string $taxonomy Optional. Taxonomy Name. Default empty.
- * @param string $context Optional, default is display. Look at sanitize_term_field() for available options.
- * @return string|int|null|WP_Error Will return an empty string if $term is not an object or if $field is not set in $term.
- */
-function get_term_field( $field, $term, $taxonomy = '', $context = 'display' ) {
- $term = get_term( $term, $taxonomy );
- if ( is_wp_error($term) )
- return $term;
-
- if ( !is_object($term) )
- return '';
-
- if ( !isset($term->$field) )
- return '';
-
- return sanitize_term_field( $field, $term->$field, $term->term_id, $term->taxonomy, $context );
-}
-
-/**
- * Sanitizes Term for editing.
- *
- * Return value is sanitize_term() and usage is for sanitizing the term for
- * editing. Function is for contextual and simplicity.
- *
- * @since 2.3.0
- *
- * @param int|object $id Term ID or object.
- * @param string $taxonomy Taxonomy name.
- * @return string|int|null|WP_Error Will return empty string if $term is not an object.
- */
-function get_term_to_edit( $id, $taxonomy ) {
- $term = get_term( $id, $taxonomy );
-
- if ( is_wp_error($term) )
- return $term;
-
- if ( !is_object($term) )
- return '';
-
- return sanitize_term($term, $taxonomy, 'edit');
-}
-
-/**
- * Retrieve the terms in a given taxonomy or list of taxonomies.
- *
- * You can fully inject any customizations to the query before it is sent, as
- * well as control the output with a filter.
- *
- * The {@see 'get_terms'} filter will be called when the cache has the term and will
- * pass the found term along with the array of $taxonomies and array of $args.
- * This filter is also called before the array of terms is passed and will pass
- * the array of terms, along with the $taxonomies and $args.
- *
- * The {@see 'list_terms_exclusions'} filter passes the compiled exclusions along with
- * the $args.
- *
- * The {@see 'get_terms_orderby'} filter passes the `ORDER BY` clause for the query
- * along with the $args array.
- *
- * @since 2.3.0
- * @since 4.2.0 Introduced 'name' and 'childless' parameters.
- * @since 4.4.0 Introduced the ability to pass 'term_id' as an alias of 'id' for the `orderby` parameter.
- * Introduced the 'meta_query' and 'update_term_meta_cache' parameters. Converted to return
- * a list of WP_Term objects.
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- * @global array $wp_filter
- *
- * @param string|array $taxonomies Taxonomy name or list of Taxonomy names.
- * @param array|string $args {
- * Optional. Array or string of arguments to get terms.
- *
- * @type string $orderby Field(s) to order terms by. Accepts term fields ('name', 'slug',
- * 'term_group', 'term_id', 'id', 'description'), 'count' for term
- * taxonomy count, 'include' to match the 'order' of the $include param,
- * or 'none' to skip ORDER BY. Defaults to 'name'.
- * @type string $order Whether to order terms in ascending or descending order.
- * Accepts 'ASC' (ascending) or 'DESC' (descending).
- * Default 'ASC'.
- * @type bool|int $hide_empty Whether to hide terms not assigned to any posts. Accepts
- * 1|true or 0|false. Default 1|true.
- * @type array|string $include Array or comma/space-separated string of term ids to include.
- * Default empty array.
- * @type array|string $exclude Array or comma/space-separated string of term ids to exclude.
- * If $include is non-empty, $exclude is ignored.
- * Default empty array.
- * @type array|string $exclude_tree Array or comma/space-separated string of term ids to exclude
- * along with all of their descendant terms. If $include is
- * non-empty, $exclude_tree is ignored. Default empty array.
- * @type int|string $number Maximum number of terms to return. Accepts ''|0 (all) or any
- * positive number. Default ''|0 (all).
- * @type int $offset The number by which to offset the terms query. Default empty.
- * @type string $fields Term fields to query for. Accepts 'all' (returns an array of complete
- * term objects), 'ids' (returns an array of ids), 'id=>parent' (returns
- * an associative array with ids as keys, parent term IDs as values),
- * 'names' (returns an array of term names), 'count' (returns the number
- * of matching terms), 'id=>name' (returns an associative array with ids
- * as keys, term names as values), or 'id=>slug' (returns an associative
- * array with ids as keys, term slugs as values). Default 'all'.
- * @type string|array $name Optional. Name or array of names to return term(s) for. Default empty.
- * @type string|array $slug Optional. Slug or array of slugs to return term(s) for. Default empty.
- * @type bool $hierarchical Whether to include terms that have non-empty descendants (even
- * if $hide_empty is set to true). Default true.
- * @type string $search Search criteria to match terms. Will be SQL-formatted with
- * wildcards before and after. Default empty.
- * @type string $name__like Retrieve terms with criteria by which a term is LIKE $name__like.
- * Default empty.
- * @type string $description__like Retrieve terms where the description is LIKE $description__like.
- * Default empty.
- * @type bool $pad_counts Whether to pad the quantity of a term's children in the quantity
- * of each term's "count" object variable. Default false.
- * @type string $get Whether to return terms regardless of ancestry or whether the terms
- * are empty. Accepts 'all' or empty (disabled). Default empty.
- * @type int $child_of Term ID to retrieve child terms of. If multiple taxonomies
- * are passed, $child_of is ignored. Default 0.
- * @type int|string $parent Parent term ID to retrieve direct-child terms of. Default empty.
- * @type bool $childless True to limit results to terms that have no children. This parameter
- * has no effect on non-hierarchical taxonomies. Default false.
- * @type string $cache_domain Unique cache key to be produced when this query is stored in an
- * object cache. Default is 'core'.
- * @type bool $update_term_meta_cache Whether to prime meta caches for matched terms. Default true.
- * @type array $meta_query Meta query clauses to limit retrieved terms by.
- * See `WP_Meta_Query`. Default empty.
- * }
- * @return array|int|WP_Error List of WP_Term instances and their children. Will return WP_Error, if any of $taxonomies
- * do not exist.
- */
-function get_terms( $taxonomies, $args = '' ) {
- global $wpdb;
- $empty_array = array();
-
- $single_taxonomy = ! is_array( $taxonomies ) || 1 === count( $taxonomies );
- if ( ! is_array( $taxonomies ) ) {
- $taxonomies = array( $taxonomies );
- }
-
- foreach ( $taxonomies as $taxonomy ) {
- if ( ! taxonomy_exists($taxonomy) ) {
- return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
- }
- }
-
- $defaults = array(
- 'orderby' => 'name',
- 'order' => 'ASC',
- 'hide_empty' => true,
- 'include' => array(),
- 'exclude' => array(),
- 'exclude_tree' => array(),
- 'number' => '',
- 'offset' => '',
- 'fields' => 'all',
- 'name' => '',
- 'slug' => '',
- 'hierarchical' => true,
- 'search' => '',
- 'name__like' => '',
- 'description__like' => '',
- 'pad_counts' => false,
- 'get' => '',
- 'child_of' => 0,
- 'parent' => '',
- 'childless' => false,
- 'cache_domain' => 'core',
- 'update_term_meta_cache' => true,
- 'meta_query' => ''
- );
-
- /**
- * Filter the terms query default arguments.
- *
- * Use 'get_terms_args' to filter the passed arguments.
- *
- * @since 4.4.0
- *
- * @param array $defaults An array of default get_terms() arguments.
- * @param array $taxonomies An array of taxonomies.
- */
- $args = wp_parse_args( $args, apply_filters( 'get_terms_defaults', $defaults, $taxonomies ) );
-
- $args['number'] = absint( $args['number'] );
- $args['offset'] = absint( $args['offset'] );
-
- // Save queries by not crawling the tree in the case of multiple taxes or a flat tax.
- $has_hierarchical_tax = false;
- foreach ( $taxonomies as $_tax ) {
- if ( is_taxonomy_hierarchical( $_tax ) ) {
- $has_hierarchical_tax = true;
- }
- }
-
- if ( ! $has_hierarchical_tax ) {
- $args['hierarchical'] = false;
- $args['pad_counts'] = false;
- }
-
- // 'parent' overrides 'child_of'.
- if ( 0 < intval( $args['parent'] ) ) {
- $args['child_of'] = false;
- }
-
- if ( 'all' == $args['get'] ) {
- $args['childless'] = false;
- $args['child_of'] = 0;
- $args['hide_empty'] = 0;
- $args['hierarchical'] = false;
- $args['pad_counts'] = false;
- }
-
- /**
- * Filter the terms query arguments.
- *
- * @since 3.1.0
- *
- * @param array $args An array of get_terms() arguments.
- * @param array $taxonomies An array of taxonomies.
- */
- $args = apply_filters( 'get_terms_args', $args, $taxonomies );
-
- // Avoid the query if the queried parent/child_of term has no descendants.
- $child_of = $args['child_of'];
- $parent = $args['parent'];
-
- if ( $child_of ) {
- $_parent = $child_of;
- } elseif ( $parent ) {
- $_parent = $parent;
- } else {
- $_parent = false;
- }
-
- if ( $_parent ) {
- $in_hierarchy = false;
- foreach ( $taxonomies as $_tax ) {
- $hierarchy = _get_term_hierarchy( $_tax );
-
- if ( isset( $hierarchy[ $_parent ] ) ) {
- $in_hierarchy = true;
- }
- }
-
- if ( ! $in_hierarchy ) {
- return $empty_array;
- }
- }
-
- $_orderby = strtolower( $args['orderby'] );
- if ( 'count' == $_orderby ) {
- $orderby = 'tt.count';
- } elseif ( 'name' == $_orderby ) {
- $orderby = 't.name';
- } elseif ( 'slug' == $_orderby ) {
- $orderby = 't.slug';
- } elseif ( 'include' == $_orderby && ! empty( $args['include'] ) ) {
- $include = implode( ',', array_map( 'absint', $args['include'] ) );
- $orderby = "FIELD( t.term_id, $include )";
- } elseif ( 'term_group' == $_orderby ) {
- $orderby = 't.term_group';
- } elseif ( 'description' == $_orderby ) {
- $orderby = 'tt.description';
- } elseif ( 'none' == $_orderby ) {
- $orderby = '';
- } elseif ( empty( $_orderby ) || 'id' == $_orderby || 'term_id' === $_orderby ) {
- $orderby = 't.term_id';
- } else {
- $orderby = 't.name';
- }
-
- /**
- * Filter the ORDERBY clause of the terms query.
- *
- * @since 2.8.0
- *
- * @param string $orderby `ORDERBY` clause of the terms query.
- * @param array $args An array of terms query arguments.
- * @param array $taxonomies An array of taxonomies.
- */
- $orderby = apply_filters( 'get_terms_orderby', $orderby, $args, $taxonomies );
-
- $order = strtoupper( $args['order'] );
- if ( ! empty( $orderby ) ) {
- $orderby = "ORDER BY $orderby";
- } else {
- $order = '';
- }
-
- if ( '' !== $order && ! in_array( $order, array( 'ASC', 'DESC' ) ) ) {
- $order = 'ASC';
- }
-
- $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
-
- $exclude = $args['exclude'];
- $exclude_tree = $args['exclude_tree'];
- $include = $args['include'];
-
- $inclusions = '';
- if ( ! empty( $include ) ) {
- $exclude = '';
- $exclude_tree = '';
- $inclusions = implode( ',', wp_parse_id_list( $include ) );
- }
-
- if ( ! empty( $inclusions ) ) {
- $inclusions = ' AND t.term_id IN ( ' . $inclusions . ' )';
- $where .= $inclusions;
- }
-
- $exclusions = array();
- if ( ! empty( $exclude_tree ) ) {
- $exclude_tree = wp_parse_id_list( $exclude_tree );
- $excluded_children = $exclude_tree;
- foreach ( $exclude_tree as $extrunk ) {
- $excluded_children = array_merge(
- $excluded_children,
- (array) get_terms( $taxonomies[0], array( 'child_of' => intval( $extrunk ), 'fields' => 'ids', 'hide_empty' => 0 ) )
- );
- }
- $exclusions = array_merge( $excluded_children, $exclusions );
- }
-
- if ( ! empty( $exclude ) ) {
- $exclusions = array_merge( wp_parse_id_list( $exclude ), $exclusions );
- }
-
- // 'childless' terms are those without an entry in the flattened term hierarchy.
- $childless = (bool) $args['childless'];
- if ( $childless ) {
- foreach ( $taxonomies as $_tax ) {
- $term_hierarchy = _get_term_hierarchy( $_tax );
- $exclusions = array_merge( array_keys( $term_hierarchy ), $exclusions );
- }
- }
-
- if ( ! empty( $exclusions ) ) {
- $exclusions = ' AND t.term_id NOT IN (' . implode( ',', array_map( 'intval', $exclusions ) ) . ')';
- } else {
- $exclusions = '';
- }
-
- /**
- * Filter the terms to exclude from the terms query.
- *
- * @since 2.3.0
- *
- * @param string $exclusions `NOT IN` clause of the terms query.
- * @param array $args An array of terms query arguments.
- * @param array $taxonomies An array of taxonomies.
- */
- $exclusions = apply_filters( 'list_terms_exclusions', $exclusions, $args, $taxonomies );
-
- if ( ! empty( $exclusions ) ) {
- $where .= $exclusions;
- }
-
- if ( ! empty( $args['name'] ) ) {
- $names = (array) $args['name'];
- foreach ( $names as &$_name ) {
- $_name = sanitize_term_field( 'name', $_name, 0, reset( $taxonomies ), 'db' );
- }
-
- $where .= " AND t.name IN ('" . implode( "', '", array_map( 'esc_sql', $names ) ) . "')";
- }
-
- if ( ! empty( $args['slug'] ) ) {
- if ( is_array( $args['slug'] ) ) {
- $slug = array_map( 'sanitize_title', $args['slug'] );
- $where .= " AND t.slug IN ('" . implode( "', '", $slug ) . "')";
- } else {
- $slug = sanitize_title( $args['slug'] );
- $where .= " AND t.slug = '$slug'";
- }
- }
-
- if ( ! empty( $args['name__like'] ) ) {
- $where .= $wpdb->prepare( " AND t.name LIKE %s", '%' . $wpdb->esc_like( $args['name__like'] ) . '%' );
- }
-
- if ( ! empty( $args['description__like'] ) ) {
- $where .= $wpdb->prepare( " AND tt.description LIKE %s", '%' . $wpdb->esc_like( $args['description__like'] ) . '%' );
- }
-
- if ( '' !== $parent ) {
- $parent = (int) $parent;
- $where .= " AND tt.parent = '$parent'";
- }
-
- $hierarchical = $args['hierarchical'];
- if ( 'count' == $args['fields'] ) {
- $hierarchical = false;
- }
- if ( $args['hide_empty'] && !$hierarchical ) {
- $where .= ' AND tt.count > 0';
- }
-
- $number = $args['number'];
- $offset = $args['offset'];
-
- // Don't limit the query results when we have to descend the family tree.
- if ( $number && ! $hierarchical && ! $child_of && '' === $parent ) {
- if ( $offset ) {
- $limits = 'LIMIT ' . $offset . ',' . $number;
- } else {
- $limits = 'LIMIT ' . $number;
- }
- } else {
- $limits = '';
- }
-
- if ( ! empty( $args['search'] ) ) {
- $like = '%' . $wpdb->esc_like( $args['search'] ) . '%';
- $where .= $wpdb->prepare( ' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', $like, $like );
- }
-
- // Meta query support.
- $join = '';
- if ( ! empty( $args['meta_query'] ) ) {
- $mquery = new WP_Meta_Query( $args['meta_query'] );
- $mq_sql = $mquery->get_sql( 'term', 't', 'term_id' );
-
- $join .= $mq_sql['join'];
- $where .= $mq_sql['where'];
- }
-
- $selects = array();
- switch ( $args['fields'] ) {
- case 'all':
- $selects = array( 't.*', 'tt.*' );
- break;
- case 'ids':
- case 'id=>parent':
- $selects = array( 't.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy' );
- break;
- case 'names':
- $selects = array( 't.term_id', 'tt.parent', 'tt.count', 't.name', 'tt.taxonomy' );
- break;
- case 'count':
- $orderby = '';
- $order = '';
- $selects = array( 'COUNT(*)' );
- break;
- case 'id=>name':
- $selects = array( 't.term_id', 't.name', 'tt.count', 'tt.taxonomy' );
- break;
- case 'id=>slug':
- $selects = array( 't.term_id', 't.slug', 'tt.count', 'tt.taxonomy' );
- break;
- }
-
- $_fields = $args['fields'];
-
- /**
- * Filter the fields to select in the terms query.
- *
- * Field lists modified using this filter will only modify the term fields returned
- * by the function when the `$fields` parameter set to 'count' or 'all'. In all other
- * cases, the term fields in the results array will be determined by the `$fields`
- * parameter alone.
- *
- * Use of this filter can result in unpredictable behavior, and is not recommended.
- *
- * @since 2.8.0
- *
- * @param array $selects An array of fields to select for the terms query.
- * @param array $args An array of term query arguments.
- * @param array $taxonomies An array of taxonomies.
- */
- $fields = implode( ', ', apply_filters( 'get_terms_fields', $selects, $args, $taxonomies ) );
-
- $join .= " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
-
- $pieces = array( 'fields', 'join', 'where', 'orderby', 'order', 'limits' );
-
- /**
- * Filter the terms query SQL clauses.
- *
- * @since 3.1.0
- *
- * @param array $pieces Terms query SQL clauses.
- * @param array $taxonomies An array of taxonomies.
- * @param array $args An array of terms query arguments.
- */
- $clauses = apply_filters( 'terms_clauses', compact( $pieces ), $taxonomies, $args );
-
- $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
- $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
- $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
- $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
- $order = isset( $clauses[ 'order' ] ) ? $clauses[ 'order' ] : '';
- $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
-
- $query = "SELECT $fields FROM $wpdb->terms AS t $join WHERE $where $orderby $order $limits";
-
- // $args can be anything. Only use the args defined in defaults to compute the key.
- $key = md5( serialize( wp_array_slice_assoc( $args, array_keys( $defaults ) ) ) . serialize( $taxonomies ) . $query );
- $last_changed = wp_cache_get( 'last_changed', 'terms' );
- if ( ! $last_changed ) {
- $last_changed = microtime();
- wp_cache_set( 'last_changed', $last_changed, 'terms' );
- }
- $cache_key = "get_terms:$key:$last_changed";
- $cache = wp_cache_get( $cache_key, 'terms' );
- if ( false !== $cache ) {
- if ( 'all' === $_fields ) {
- $cache = array_map( 'get_term', $cache );
- }
-
- /**
- * Filter the given taxonomy's terms cache.
- *
- * @since 2.3.0
- *
- * @param array $cache Cached array of terms for the given taxonomy.
- * @param array $taxonomies An array of taxonomies.
- * @param array $args An array of get_terms() arguments.
- */
- return apply_filters( 'get_terms', $cache, $taxonomies, $args );
- }
-
- if ( 'count' == $_fields ) {
- return $wpdb->get_var( $query );
- }
-
- $terms = $wpdb->get_results($query);
- if ( 'all' == $_fields ) {
- update_term_cache( $terms );
- }
-
- // Prime termmeta cache.
- if ( $args['update_term_meta_cache'] ) {
- $term_ids = wp_list_pluck( $terms, 'term_id' );
- update_termmeta_cache( $term_ids );
- }
-
- if ( empty($terms) ) {
- wp_cache_add( $cache_key, array(), 'terms', DAY_IN_SECONDS );
-
- /** This filter is documented in wp-includes/taxonomy-functions.php */
- return apply_filters( 'get_terms', array(), $taxonomies, $args );
- }
-
- if ( $child_of ) {
- foreach ( $taxonomies as $_tax ) {
- $children = _get_term_hierarchy( $_tax );
- if ( ! empty( $children ) ) {
- $terms = _get_term_children( $child_of, $terms, $_tax );
- }
- }
- }
-
- // Update term counts to include children.
- if ( $args['pad_counts'] && 'all' == $_fields ) {
- foreach ( $taxonomies as $_tax ) {
- _pad_term_counts( $terms, $_tax );
- }
- }
-
- // Make sure we show empty categories that have children.
- if ( $hierarchical && $args['hide_empty'] && is_array( $terms ) ) {
- foreach ( $terms as $k => $term ) {
- if ( ! $term->count ) {
- $children = get_term_children( $term->term_id, $term->taxonomy );
- if ( is_array( $children ) ) {
- foreach ( $children as $child_id ) {
- $child = get_term( $child_id, $term->taxonomy );
- if ( $child->count ) {
- continue 2;
- }
- }
- }
-
- // It really is empty.
- unset($terms[$k]);
- }
- }
- }
-
- $_terms = array();
- if ( 'id=>parent' == $_fields ) {
- foreach ( $terms as $term ) {
- $_terms[ $term->term_id ] = $term->parent;
- }
- } elseif ( 'ids' == $_fields ) {
- foreach ( $terms as $term ) {
- $_terms[] = $term->term_id;
- }
- } elseif ( 'names' == $_fields ) {
- foreach ( $terms as $term ) {
- $_terms[] = $term->name;
- }
- } elseif ( 'id=>name' == $_fields ) {
- foreach ( $terms as $term ) {
- $_terms[ $term->term_id ] = $term->name;
- }
- } elseif ( 'id=>slug' == $_fields ) {
- foreach ( $terms as $term ) {
- $_terms[ $term->term_id ] = $term->slug;
- }
- }
-
- if ( ! empty( $_terms ) ) {
- $terms = $_terms;
- }
-
- if ( $number && is_array( $terms ) && count( $terms ) > $number ) {
- $terms = array_slice( $terms, $offset, $number );
- }
-
- wp_cache_add( $cache_key, $terms, 'terms', DAY_IN_SECONDS );
-
- if ( 'all' === $_fields ) {
- $terms = array_map( 'get_term', $terms );
- }
-
- /** This filter is documented in wp-includes/taxonomy-functions.php */
- return apply_filters( 'get_terms', $terms, $taxonomies, $args );
-}
-
-/**
- * Adds metadata to a term.
- *
- * @since 4.4.0
- *
- * @param int $term_id Term ID.
- * @param string $meta_key Metadata name.
- * @param mixed $meta_value Metadata value.
- * @param bool $unique Optional. Whether to bail if an entry with the same key is found for the term.
- * Default false.
- * @return int|WP_Error|bool Meta ID on success. WP_Error when term_id is ambiguous between taxonomies.
- * False on failure.
- */
-function add_term_meta( $term_id, $meta_key, $meta_value, $unique = false ) {
- // Bail if term meta table is not installed.
- if ( get_option( 'db_version' ) < 34370 ) {
- return false;
- }
-
- if ( wp_term_is_shared( $term_id ) ) {
- return new WP_Error( 'ambiguous_term_id', __( 'Term meta cannot be added to terms that are shared between taxonomies.'), $term_id );
- }
-
- $added = add_metadata( 'term', $term_id, $meta_key, $meta_value, $unique );
-
- // Bust term query cache.
- if ( $added ) {
- wp_cache_set( 'last_changed', microtime(), 'terms' );
- }
-
- return $added;
-}
-
-/**
- * Removes metadata matching criteria from a term.
- *
- * @since 4.4.0
- *
- * @param int $term_id Term ID.
- * @param string $meta_key Metadata name.
- * @param mixed $meta_value Optional. Metadata value. If provided, rows will only be removed that match the value.
- * @return bool True on success, false on failure.
- */
-function delete_term_meta( $term_id, $meta_key, $meta_value = '' ) {
- // Bail if term meta table is not installed.
- if ( get_option( 'db_version' ) < 34370 ) {
- return false;
- }
-
- $deleted = delete_metadata( 'term', $term_id, $meta_key, $meta_value );
-
- // Bust term query cache.
- if ( $deleted ) {
- wp_cache_set( 'last_changed', microtime(), 'terms' );
- }
-
- return $deleted;
-}
-
-/**
- * Retrieves metadata for a term.
- *
- * @since 4.4.0
- *
- * @param int $term_id Term ID.
- * @param string $key Optional. The meta key to retrieve. If no key is provided, fetches all metadata for the term.
- * @param bool $single Whether to return a single value. If false, an array of all values matching the
- * `$term_id`/`$key` pair will be returned. Default: false.
- * @return mixed If `$single` is false, an array of metadata values. If `$single` is true, a single metadata value.
- */
-function get_term_meta( $term_id, $key = '', $single = false ) {
- // Bail if term meta table is not installed.
- if ( get_option( 'db_version' ) < 34370 ) {
- return false;
- }
-
- return get_metadata( 'term', $term_id, $key, $single );
-}
-
-/**
- * Updates term metadata.
- *
- * Use the `$prev_value` parameter to differentiate between meta fields with the same key and term ID.
- *
- * If the meta field for the term does not exist, it will be added.
- *
- * @since 4.4.0
- *
- * @param int $term_id Term ID.
- * @param string $meta_key Metadata key.
- * @param mixed $meta_value Metadata value.
- * @param mixed $prev_value Optional. Previous value to check before removing.
- * @return int|WP_Error|bool Meta ID if the key didn't previously exist. True on successful update.
- * WP_Error when term_id is ambiguous between taxonomies. False on failure.
- */
-function update_term_meta( $term_id, $meta_key, $meta_value, $prev_value = '' ) {
- // Bail if term meta table is not installed.
- if ( get_option( 'db_version' ) < 34370 ) {
- return false;
- }
-
- if ( wp_term_is_shared( $term_id ) ) {
- return new WP_Error( 'ambiguous_term_id', __( 'Term meta cannot be added to terms that are shared between taxonomies.'), $term_id );
- }
-
- $updated = update_metadata( 'term', $term_id, $meta_key, $meta_value, $prev_value );
-
- // Bust term query cache.
- if ( $updated ) {
- wp_cache_set( 'last_changed', microtime(), 'terms' );
- }
-
- return $updated;
-}
-
-/**
- * Updates metadata cache for list of term IDs.
- *
- * Performs SQL query to retrieve all metadata for the terms matching `$term_ids` and stores them in the cache.
- * Subsequent calls to `get_term_meta()` will not need to query the database.
- *
- * @since 4.4.0
- *
- * @param array $term_ids List of term IDs.
- * @return array|false Returns false if there is nothing to update. Returns an array of metadata on success.
- */
-function update_termmeta_cache( $term_ids ) {
- // Bail if term meta table is not installed.
- if ( get_option( 'db_version' ) < 34370 ) {
- return;
- }
-
- return update_meta_cache( 'term', $term_ids );
-}
-
-/**
- * Check if Term exists.
- *
- * Formerly is_term(), introduced in 2.3.0.
- *
- * @since 3.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int|string $term The term to check
- * @param string $taxonomy The taxonomy name to use
- * @param int $parent Optional. ID of parent term under which to confine the exists search.
- * @return mixed Returns null if the term does not exist. Returns the term ID
- * if no taxonomy is specified and the term ID exists. Returns
- * an array of the term ID and the term taxonomy ID the taxonomy
- * is specified and the pairing exists.
- */
-function term_exists( $term, $taxonomy = '', $parent = null ) {
- global $wpdb;
-
- $select = "SELECT term_id FROM $wpdb->terms as t WHERE ";
- $tax_select = "SELECT tt.term_id, tt.term_taxonomy_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_id = t.term_id WHERE ";
-
- if ( is_int($term) ) {
- if ( 0 == $term )
- return 0;
- $where = 't.term_id = %d';
- if ( !empty($taxonomy) )
- return $wpdb->get_row( $wpdb->prepare( $tax_select . $where . " AND tt.taxonomy = %s", $term, $taxonomy ), ARRAY_A );
- else
- return $wpdb->get_var( $wpdb->prepare( $select . $where, $term ) );
- }
-
- $term = trim( wp_unslash( $term ) );
- $slug = sanitize_title( $term );
-
- $where = 't.slug = %s';
- $else_where = 't.name = %s';
- $where_fields = array($slug);
- $else_where_fields = array($term);
- $orderby = 'ORDER BY t.term_id ASC';
- $limit = 'LIMIT 1';
- if ( !empty($taxonomy) ) {
- if ( is_numeric( $parent ) ) {
- $parent = (int) $parent;
- $where_fields[] = $parent;
- $else_where_fields[] = $parent;
- $where .= ' AND tt.parent = %d';
- $else_where .= ' AND tt.parent = %d';
- }
-
- $where_fields[] = $taxonomy;
- $else_where_fields[] = $taxonomy;
-
- if ( $result = $wpdb->get_row( $wpdb->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_id = t.term_id WHERE $where AND tt.taxonomy = %s $orderby $limit", $where_fields), ARRAY_A) )
- return $result;
-
- return $wpdb->get_row( $wpdb->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_id = t.term_id WHERE $else_where AND tt.taxonomy = %s $orderby $limit", $else_where_fields), ARRAY_A);
- }
-
- if ( $result = $wpdb->get_var( $wpdb->prepare("SELECT term_id FROM $wpdb->terms as t WHERE $where $orderby $limit", $where_fields) ) )
- return $result;
-
- return $wpdb->get_var( $wpdb->prepare("SELECT term_id FROM $wpdb->terms as t WHERE $else_where $orderby $limit", $else_where_fields) );
-}
-
-/**
- * Check if a term is an ancestor of another term.
- *
- * You can use either an id or the term object for both parameters.
- *
- * @since 3.4.0
- *
- * @param int|object $term1 ID or object to check if this is the parent term.
- * @param int|object $term2 The child term.
- * @param string $taxonomy Taxonomy name that $term1 and `$term2` belong to.
- * @return bool Whether `$term2` is a child of `$term1`.
- */
-function term_is_ancestor_of( $term1, $term2, $taxonomy ) {
- if ( ! isset( $term1->term_id ) )
- $term1 = get_term( $term1, $taxonomy );
- if ( ! isset( $term2->parent ) )
- $term2 = get_term( $term2, $taxonomy );
-
- if ( empty( $term1->term_id ) || empty( $term2->parent ) )
- return false;
- if ( $term2->parent == $term1->term_id )
- return true;
-
- return term_is_ancestor_of( $term1, get_term( $term2->parent, $taxonomy ), $taxonomy );
-}
-
-/**
- * Sanitize Term all fields.
- *
- * Relies on sanitize_term_field() to sanitize the term. The difference is that
- * this function will sanitize <strong>all</strong> fields. The context is based
- * on sanitize_term_field().
- *
- * The $term is expected to be either an array or an object.
- *
- * @since 2.3.0
- *
- * @param array|object $term The term to check.
- * @param string $taxonomy The taxonomy name to use.
- * @param string $context Optional. Context in which to sanitize the term. Accepts 'edit', 'db',
- * 'display', 'attribute', or 'js'. Default 'display'.
- * @return array|object Term with all fields sanitized.
- */
-function sanitize_term($term, $taxonomy, $context = 'display') {
- $fields = array( 'term_id', 'name', 'description', 'slug', 'count', 'parent', 'term_group', 'term_taxonomy_id', 'object_id' );
-
- $do_object = is_object( $term );
-
- $term_id = $do_object ? $term->term_id : (isset($term['term_id']) ? $term['term_id'] : 0);
-
- foreach ( (array) $fields as $field ) {
- if ( $do_object ) {
- if ( isset($term->$field) )
- $term->$field = sanitize_term_field($field, $term->$field, $term_id, $taxonomy, $context);
- } else {
- if ( isset($term[$field]) )
- $term[$field] = sanitize_term_field($field, $term[$field], $term_id, $taxonomy, $context);
- }
- }
-
- if ( $do_object )
- $term->filter = $context;
- else
- $term['filter'] = $context;
-
- return $term;
-}
-
-/**
- * Cleanse the field value in the term based on the context.
- *
- * Passing a term field value through the function should be assumed to have
- * cleansed the value for whatever context the term field is going to be used.
- *
- * If no context or an unsupported context is given, then default filters will
- * be applied.
- *
- * There are enough filters for each context to support a custom filtering
- * without creating your own filter function. Simply create a function that
- * hooks into the filter you need.
- *
- * @since 2.3.0
- *
- * @param string $field Term field to sanitize.
- * @param string $value Search for this term value.
- * @param int $term_id Term ID.
- * @param string $taxonomy Taxonomy Name.
- * @param string $context Context in which to sanitize the term field. Accepts 'edit', 'db', 'display',
- * 'attribute', or 'js'.
- * @return mixed Sanitized field.
- */
-function sanitize_term_field($field, $value, $term_id, $taxonomy, $context) {
- $int_fields = array( 'parent', 'term_id', 'count', 'term_group', 'term_taxonomy_id', 'object_id' );
- if ( in_array( $field, $int_fields ) ) {
- $value = (int) $value;
- if ( $value < 0 )
- $value = 0;
- }
-
- if ( 'raw' == $context )
- return $value;
-
- if ( 'edit' == $context ) {
-
- /**
- * Filter a term field to edit before it is sanitized.
- *
- * The dynamic portion of the filter name, `$field`, refers to the term field.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the term field.
- * @param int $term_id Term ID.
- * @param string $taxonomy Taxonomy slug.
- */
- $value = apply_filters( "edit_term_{$field}", $value, $term_id, $taxonomy );
-
- /**
- * Filter the taxonomy field to edit before it is sanitized.
- *
- * The dynamic portions of the filter name, `$taxonomy` and `$field`, refer
- * to the taxonomy slug and taxonomy field, respectively.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the taxonomy field to edit.
- * @param int $term_id Term ID.
- */
- $value = apply_filters( "edit_{$taxonomy}_{$field}", $value, $term_id );
-
- if ( 'description' == $field )
- $value = esc_html($value); // textarea_escaped
- else
- $value = esc_attr($value);
- } elseif ( 'db' == $context ) {
-
- /**
- * Filter a term field value before it is sanitized.
- *
- * The dynamic portion of the filter name, `$field`, refers to the term field.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the term field.
- * @param string $taxonomy Taxonomy slug.
- */
- $value = apply_filters( "pre_term_{$field}", $value, $taxonomy );
-
- /**
- * Filter a taxonomy field before it is sanitized.
- *
- * The dynamic portions of the filter name, `$taxonomy` and `$field`, refer
- * to the taxonomy slug and field name, respectively.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the taxonomy field.
- */
- $value = apply_filters( "pre_{$taxonomy}_{$field}", $value );
-
- // Back compat filters
- if ( 'slug' == $field ) {
- /**
- * Filter the category nicename before it is sanitized.
- *
- * Use the pre_{$taxonomy}_{$field} hook instead.
- *
- * @since 2.0.3
- *
- * @param string $value The category nicename.
- */
- $value = apply_filters( 'pre_category_nicename', $value );
- }
-
- } elseif ( 'rss' == $context ) {
-
- /**
- * Filter the term field for use in RSS.
- *
- * The dynamic portion of the filter name, `$field`, refers to the term field.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the term field.
- * @param string $taxonomy Taxonomy slug.
- */
- $value = apply_filters( "term_{$field}_rss", $value, $taxonomy );
-
- /**
- * Filter the taxonomy field for use in RSS.
- *
- * The dynamic portions of the hook name, `$taxonomy`, and `$field`, refer
- * to the taxonomy slug and field name, respectively.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the taxonomy field.
- */
- $value = apply_filters( "{$taxonomy}_{$field}_rss", $value );
- } else {
- // Use display filters by default.
-
- /**
- * Filter the term field sanitized for display.
- *
- * The dynamic portion of the filter name, `$field`, refers to the term field name.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the term field.
- * @param int $term_id Term ID.
- * @param string $taxonomy Taxonomy slug.
- * @param string $context Context to retrieve the term field value.
- */
- $value = apply_filters( "term_{$field}", $value, $term_id, $taxonomy, $context );
-
- /**
- * Filter the taxonomy field sanitized for display.
- *
- * The dynamic portions of the filter name, `$taxonomy`, and `$field`, refer
- * to the taxonomy slug and taxonomy field, respectively.
- *
- * @since 2.3.0
- *
- * @param mixed $value Value of the taxonomy field.
- * @param int $term_id Term ID.
- * @param string $context Context to retrieve the taxonomy field value.
- */
- $value = apply_filters( "{$taxonomy}_{$field}", $value, $term_id, $context );
- }
-
- if ( 'attribute' == $context ) {
- $value = esc_attr($value);
- } elseif ( 'js' == $context ) {
- $value = esc_js($value);
- }
- return $value;
-}
-
-/**
- * Count how many terms are in Taxonomy.
- *
- * Default $args is 'hide_empty' which can be 'hide_empty=true' or array('hide_empty' => true).
- *
- * @todo Document $args as a hash notation.
- *
- * @since 2.3.0
- *
- * @param string $taxonomy Taxonomy name
- * @param array|string $args Overwrite defaults. See get_terms()
- * @return array|int|WP_Error How many terms are in $taxonomy. WP_Error if $taxonomy does not exist.
- */
-function wp_count_terms( $taxonomy, $args = array() ) {
- $defaults = array('hide_empty' => false);
- $args = wp_parse_args($args, $defaults);
-
- // backwards compatibility
- if ( isset($args['ignore_empty']) ) {
- $args['hide_empty'] = $args['ignore_empty'];
- unset($args['ignore_empty']);
- }
-
- $args['fields'] = 'count';
-
- return get_terms($taxonomy, $args);
-}
-
-/**
- * Will unlink the object from the taxonomy or taxonomies.
- *
- * Will remove all relationships between the object and any terms in
- * a particular taxonomy or taxonomies. Does not remove the term or
- * taxonomy itself.
- *
- * @since 2.3.0
- *
- * @param int $object_id The term Object Id that refers to the term.
- * @param string|array $taxonomies List of Taxonomy Names or single Taxonomy name.
- */
-function wp_delete_object_term_relationships( $object_id, $taxonomies ) {
- $object_id = (int) $object_id;
-
- if ( !is_array($taxonomies) )
- $taxonomies = array($taxonomies);
-
- foreach ( (array) $taxonomies as $taxonomy ) {
- $term_ids = wp_get_object_terms( $object_id, $taxonomy, array( 'fields' => 'ids' ) );
- $term_ids = array_map( 'intval', $term_ids );
- wp_remove_object_terms( $object_id, $term_ids, $taxonomy );
- }
-}
-
-/**
- * Removes a term from the database.
- *
- * If the term is a parent of other terms, then the children will be updated to
- * that term's parent.
- *
- * Metadata associated with the term will be deleted.
- *
- * The `$args` 'default' will only override the terms found, if there is only one
- * term found. Any other and the found terms are used.
- *
- * The $args 'force_default' will force the term supplied as default to be
- * assigned even if the object was not going to be termless
- *
- * @todo Document $args as a hash notation.
- *
- * @since 2.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $term Term ID.
- * @param string $taxonomy Taxonomy Name.
- * @param array|string $args Optional. Change 'default' term id and override found term ids.
- * @return bool|int|WP_Error Returns false if not term; true if completes delete action.
- */
-function wp_delete_term( $term, $taxonomy, $args = array() ) {
- global $wpdb;
-
- $term = (int) $term;
-
- if ( ! $ids = term_exists($term, $taxonomy) )
- return false;
- if ( is_wp_error( $ids ) )
- return $ids;
-
- $tt_id = $ids['term_taxonomy_id'];
-
- $defaults = array();
-
- if ( 'category' == $taxonomy ) {
- $defaults['default'] = get_option( 'default_category' );
- if ( $defaults['default'] == $term )
- return 0; // Don't delete the default category
- }
-
- $args = wp_parse_args($args, $defaults);
-
- if ( isset( $args['default'] ) ) {
- $default = (int) $args['default'];
- if ( ! term_exists( $default, $taxonomy ) ) {
- unset( $default );
- }
- }
-
- if ( isset( $args['force_default'] ) ) {
- $force_default = $args['force_default'];
- }
-
- /**
- * Fires when deleting a term, before any modifications are made to posts or terms.
- *
- * @since 4.1.0
- *
- * @param int $term Term ID.
- * @param string $taxonomy Taxonomy Name.
- */
- do_action( 'pre_delete_term', $term, $taxonomy );
-
- // Update children to point to new parent
- if ( is_taxonomy_hierarchical($taxonomy) ) {
- $term_obj = get_term($term, $taxonomy);
- if ( is_wp_error( $term_obj ) )
- return $term_obj;
- $parent = $term_obj->parent;
-
- $edit_ids = $wpdb->get_results( "SELECT term_id, term_taxonomy_id FROM $wpdb->term_taxonomy WHERE `parent` = " . (int)$term_obj->term_id );
- $edit_tt_ids = wp_list_pluck( $edit_ids, 'term_taxonomy_id' );
-
- /**
- * Fires immediately before a term to delete's children are reassigned a parent.
- *
- * @since 2.9.0
- *
- * @param array $edit_tt_ids An array of term taxonomy IDs for the given term.
- */
- do_action( 'edit_term_taxonomies', $edit_tt_ids );
-
- $wpdb->update( $wpdb->term_taxonomy, compact( 'parent' ), array( 'parent' => $term_obj->term_id) + compact( 'taxonomy' ) );
-
- // Clean the cache for all child terms.
- $edit_term_ids = wp_list_pluck( $edit_ids, 'term_id' );
- clean_term_cache( $edit_term_ids, $taxonomy );
-
- /**
- * Fires immediately after a term to delete's children are reassigned a parent.
- *
- * @since 2.9.0
- *
- * @param array $edit_tt_ids An array of term taxonomy IDs for the given term.
- */
- do_action( 'edited_term_taxonomies', $edit_tt_ids );
- }
-
- // Get the term before deleting it or its term relationships so we can pass to actions below.
- $deleted_term = get_term( $term, $taxonomy );
-
- $objects = $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $tt_id ) );
-
- foreach ( (array) $objects as $object ) {
- $terms = wp_get_object_terms($object, $taxonomy, array('fields' => 'ids', 'orderby' => 'none'));
- if ( 1 == count($terms) && isset($default) ) {
- $terms = array($default);
- } else {
- $terms = array_diff($terms, array($term));
- if (isset($default) && isset($force_default) && $force_default)
- $terms = array_merge($terms, array($default));
- }
- $terms = array_map('intval', $terms);
- wp_set_object_terms($object, $terms, $taxonomy);
- }
-
- // Clean the relationship caches for all object types using this term.
- $tax_object = get_taxonomy( $taxonomy );
- foreach ( $tax_object->object_type as $object_type )
- clean_object_term_cache( $objects, $object_type );
-
- $term_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->termmeta WHERE term_id = %d ", $term ) );
- foreach ( $term_meta_ids as $mid ) {
- delete_metadata_by_mid( 'term', $mid );
- }
-
- /**
- * Fires immediately before a term taxonomy ID is deleted.
- *
- * @since 2.9.0
- *
- * @param int $tt_id Term taxonomy ID.
- */
- do_action( 'delete_term_taxonomy', $tt_id );
- $wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) );
-
- /**
- * Fires immediately after a term taxonomy ID is deleted.
- *
- * @since 2.9.0
- *
- * @param int $tt_id Term taxonomy ID.
- */
- do_action( 'deleted_term_taxonomy', $tt_id );
-
- // Delete the term if no taxonomies use it.
- if ( !$wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy WHERE term_id = %d", $term) ) )
- $wpdb->delete( $wpdb->terms, array( 'term_id' => $term ) );
-
- clean_term_cache($term, $taxonomy);
-
- /**
- * Fires after a term is deleted from the database and the cache is cleaned.
- *
- * @since 2.5.0
- *
- * @param int $term Term ID.
- * @param int $tt_id Term taxonomy ID.
- * @param string $taxonomy Taxonomy slug.
- * @param mixed $deleted_term Copy of the already-deleted term, in the form specified
- * by the parent function. WP_Error otherwise.
- */
- do_action( 'delete_term', $term, $tt_id, $taxonomy, $deleted_term );
-
- /**
- * Fires after a term in a specific taxonomy is deleted.
- *
- * The dynamic portion of the hook name, `$taxonomy`, refers to the specific
- * taxonomy the term belonged to.
- *
- * @since 2.3.0
- *
- * @param int $term Term ID.
- * @param int $tt_id Term taxonomy ID.
- * @param mixed $deleted_term Copy of the already-deleted term, in the form specified
- * by the parent function. WP_Error otherwise.
- */
- do_action( "delete_$taxonomy", $term, $tt_id, $deleted_term );
-
- return true;
-}
-
-/**
- * Deletes one existing category.
- *
- * @since 2.0.0
- *
- * @param int $cat_ID
- * @return bool|int|WP_Error Returns true if completes delete action; false if term doesn't exist;
- * Zero on attempted deletion of default Category; WP_Error object is also a possibility.
- */
-function wp_delete_category( $cat_ID ) {
- return wp_delete_term( $cat_ID, 'category' );
-}
-
-/**
- * Retrieves the terms associated with the given object(s), in the supplied taxonomies.
- *
- * @since 2.3.0
- * @since 4.2.0 Added support for 'taxonomy', 'parent', and 'term_taxonomy_id' values of `$orderby`.
- * Introduced `$parent` argument.
- * @since 4.4.0 Introduced `$meta_query` and `$update_term_meta_cache` arguments. When `$fields` is 'all' or
- * 'all_with_object_id', an array of `WP_Term` objects will be returned.
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int|array $object_ids The ID(s) of the object(s) to retrieve.
- * @param string|array $taxonomies The taxonomies to retrieve terms from.
- * @param array|string $args {
- * Array of arguments.
- * @type string $orderby Field by which results should be sorted. Accepts 'name', 'count', 'slug',
- * 'term_group', 'term_order', 'taxonomy', 'parent', or 'term_taxonomy_id'.
- * Default 'name'.
- * @type string $order Sort order. Accepts 'ASC' or 'DESC'. Default 'ASC'.
- * @type string $fields Fields to return for matched terms. Accepts 'all', 'ids', 'names', and
- * 'all_with_object_id'. Note that 'all' or 'all_with_object_id' will result
- * in an array of term objects being returned, 'ids' will return an array of
- * integers, and 'names' an array of strings.
- * @type int $parent Optional. Limit results to the direct children of a given term ID.
- * @type bool $update_term_meta_cache Whether to prime termmeta cache for matched terms. Only applies when
- * `$fields` is 'all', 'all_with_object_id', or 'term_id'. Default true.
- * @type array $meta_query Meta query clauses to limit retrieved terms by. See `WP_Meta_Query`.
- * Default empty.
- * }
- * @return array|WP_Error The requested term data or empty array if no terms found.
- * WP_Error if any of the $taxonomies don't exist.
- */
-function wp_get_object_terms($object_ids, $taxonomies, $args = array()) {
- global $wpdb;
-
- if ( empty( $object_ids ) || empty( $taxonomies ) )
- return array();
-
- if ( !is_array($taxonomies) )
- $taxonomies = array($taxonomies);
-
- foreach ( $taxonomies as $taxonomy ) {
- if ( ! taxonomy_exists($taxonomy) )
- return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
- }
-
- if ( !is_array($object_ids) )
- $object_ids = array($object_ids);
- $object_ids = array_map('intval', $object_ids);
-
- $defaults = array(
- 'orderby' => 'name',
- 'order' => 'ASC',
- 'fields' => 'all',
- 'parent' => '',
- 'update_term_meta_cache' => true,
- 'meta_query' => '',
- );
- $args = wp_parse_args( $args, $defaults );
-
- $terms = array();
- if ( count($taxonomies) > 1 ) {
- foreach ( $taxonomies as $index => $taxonomy ) {
- $t = get_taxonomy($taxonomy);
- if ( isset($t->args) && is_array($t->args) && $args != array_merge($args, $t->args) ) {
- unset($taxonomies[$index]);
- $terms = array_merge($terms, wp_get_object_terms($object_ids, $taxonomy, array_merge($args, $t->args)));
- }
- }
- } else {
- $t = get_taxonomy($taxonomies[0]);
- if ( isset($t->args) && is_array($t->args) )
- $args = array_merge($args, $t->args);
- }
-
- $orderby = $args['orderby'];
- $order = $args['order'];
- $fields = $args['fields'];
-
- if ( in_array( $orderby, array( 'term_id', 'name', 'slug', 'term_group' ) ) ) {
- $orderby = "t.$orderby";
- } elseif ( in_array( $orderby, array( 'count', 'parent', 'taxonomy', 'term_taxonomy_id' ) ) ) {
- $orderby = "tt.$orderby";
- } elseif ( 'term_order' === $orderby ) {
- $orderby = 'tr.term_order';
- } elseif ( 'none' === $orderby ) {
- $orderby = '';
- $order = '';
- } else {
- $orderby = 't.term_id';
- }
-
- // tt_ids queries can only be none or tr.term_taxonomy_id
- if ( ('tt_ids' == $fields) && !empty($orderby) )
- $orderby = 'tr.term_taxonomy_id';
-
- if ( !empty($orderby) )
- $orderby = "ORDER BY $orderby";
-
- $order = strtoupper( $order );
- if ( '' !== $order && ! in_array( $order, array( 'ASC', 'DESC' ) ) )
- $order = 'ASC';
-
- $taxonomy_array = $taxonomies;
- $object_id_array = $object_ids;
- $taxonomies = "'" . implode("', '", $taxonomies) . "'";
- $object_ids = implode(', ', $object_ids);
-
- $select_this = '';
- if ( 'all' == $fields ) {
- $select_this = 't.*, tt.*';
- } elseif ( 'ids' == $fields ) {
- $select_this = 't.term_id';
- } elseif ( 'names' == $fields ) {
- $select_this = 't.name';
- } elseif ( 'slugs' == $fields ) {
- $select_this = 't.slug';
- } elseif ( 'all_with_object_id' == $fields ) {
- $select_this = 't.*, tt.*, tr.object_id';
- }
-
- $where = array(
- "tt.taxonomy IN ($taxonomies)",
- "tr.object_id IN ($object_ids)",
- );
-
- if ( '' !== $args['parent'] ) {
- $where[] = $wpdb->prepare( 'tt.parent = %d', $args['parent'] );
- }
-
- // Meta query support.
- $meta_query_join = '';
- if ( ! empty( $args['meta_query'] ) ) {
- $mquery = new WP_Meta_Query( $args['meta_query'] );
- $mq_sql = $mquery->get_sql( 'term', 't', 'term_id' );
-
- $meta_query_join .= $mq_sql['join'];
-
- // Strip leading AND.
- $where[] = preg_replace( '/^\s*AND/', '', $mq_sql['where'] );
- }
-
- $where = implode( ' AND ', $where );
-
- $query = "SELECT $select_this FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id INNER JOIN $wpdb->term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id $meta_query_join WHERE $where $orderby $order";
-
- $objects = false;
- if ( 'all' == $fields || 'all_with_object_id' == $fields ) {
- $_terms = $wpdb->get_results( $query );
- $object_id_index = array();
- foreach ( $_terms as $key => $term ) {
- $term = sanitize_term( $term, $taxonomy, 'raw' );
- $_terms[ $key ] = $term;
-
- if ( isset( $term->object_id ) ) {
- $object_id_index[ $key ] = $term->object_id;
- }
- }
-
- update_term_cache( $_terms );
- $_terms = array_map( 'get_term', $_terms );
-
- // Re-add the object_id data, which is lost when fetching terms from cache.
- if ( 'all_with_object_id' === $fields ) {
- foreach ( $_terms as $key => $_term ) {
- if ( isset( $object_id_index[ $key ] ) ) {
- $_term->object_id = $object_id_index[ $key ];
- }
- }
- }
-
- $terms = array_merge( $terms, $_terms );
- $objects = true;
-
- } elseif ( 'ids' == $fields || 'names' == $fields || 'slugs' == $fields ) {
- $_terms = $wpdb->get_col( $query );
- $_field = ( 'ids' == $fields ) ? 'term_id' : 'name';
- foreach ( $_terms as $key => $term ) {
- $_terms[$key] = sanitize_term_field( $_field, $term, $term, $taxonomy, 'raw' );
- }
- $terms = array_merge( $terms, $_terms );
- } elseif ( 'tt_ids' == $fields ) {
- $terms = $wpdb->get_col("SELECT tr.term_taxonomy_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tr.object_id IN ($object_ids) AND tt.taxonomy IN ($taxonomies) $orderby $order");
- foreach ( $terms as $key => $tt_id ) {
- $terms[$key] = sanitize_term_field( 'term_taxonomy_id', $tt_id, 0, $taxonomy, 'raw' ); // 0 should be the term id, however is not needed when using raw context.
- }
- }
-
- // Update termmeta cache, if necessary.
- if ( $args['update_term_meta_cache'] && ( 'all' === $fields || 'all_with_object_ids' === $fields || 'term_id' === $fields ) ) {
- if ( 'term_id' === $fields ) {
- $term_ids = $fields;
- } else {
- $term_ids = wp_list_pluck( $terms, 'term_id' );
- }
-
- update_termmeta_cache( $term_ids );
- }
-
- if ( ! $terms ) {
- $terms = array();
- } elseif ( $objects && 'all_with_object_id' !== $fields ) {
- $_tt_ids = array();
- $_terms = array();
- foreach ( $terms as $term ) {
- if ( in_array( $term->term_taxonomy_id, $_tt_ids ) ) {
- continue;
- }
-
- $_tt_ids[] = $term->term_taxonomy_id;
- $_terms[] = $term;
- }
- $terms = $_terms;
- } elseif ( ! $objects ) {
- $terms = array_values( array_unique( $terms ) );
- }
-
- /**
- * Filter the terms for a given object or objects.
- *
- * @since 4.2.0
- *
- * @param array $terms An array of terms for the given object or objects.
- * @param array $object_id_array Array of object IDs for which `$terms` were retrieved.
- * @param array $taxonomy_array Array of taxonomies from which `$terms` were retrieved.
- * @param array $args An array of arguments for retrieving terms for the given
- * object(s). See wp_get_object_terms() for details.
- */
- $terms = apply_filters( 'get_object_terms', $terms, $object_id_array, $taxonomy_array, $args );
-
- /**
- * Filter the terms for a given object or objects.
- *
- * The `$taxonomies` parameter passed to this filter is formatted as a SQL fragment. The
- * {@see 'get_object_terms'} filter is recommended as an alternative.
- *
- * @since 2.8.0
- *
- * @param array $terms An array of terms for the given object or objects.
- * @param int|array $object_ids Object ID or array of IDs.
- * @param string $taxonomies SQL-formatted (comma-separated and quoted) list of taxonomy names.
- * @param array $args An array of arguments for retrieving terms for the given object(s).
- * See {@see wp_get_object_terms()} for details.
- */
- return apply_filters( 'wp_get_object_terms', $terms, $object_ids, $taxonomies, $args );
-}
-
-/**
- * Add a new term to the database.
- *
- * A non-existent term is inserted in the following sequence:
- * 1. The term is added to the term table, then related to the taxonomy.
- * 2. If everything is correct, several actions are fired.
- * 3. The 'term_id_filter' is evaluated.
- * 4. The term cache is cleaned.
- * 5. Several more actions are fired.
- * 6. An array is returned containing the term_id and term_taxonomy_id.
- *
- * If the 'slug' argument is not empty, then it is checked to see if the term
- * is invalid. If it is not a valid, existing term, it is added and the term_id
- * is given.
- *
- * If the taxonomy is hierarchical, and the 'parent' argument is not empty,
- * the term is inserted and the term_id will be given.
- *
- * Error handling:
- * If $taxonomy does not exist or $term is empty,
- * a WP_Error object will be returned.
- *
- * If the term already exists on the same hierarchical level,
- * or the term slug and name are not unique, a WP_Error object will be returned.
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @since 2.3.0
- *
- * @param string $term The term to add or update.
- * @param string $taxonomy The taxonomy to which to add the term.
- * @param array|string $args {
- * Optional. Array or string of arguments for inserting a term.
- *
- * @type string $alias_of Slug of the term to make this term an alias of.
- * Default empty string. Accepts a term slug.
- * @type string $description The term description. Default empty string.
- * @type int $parent The id of the parent term. Default 0.
- * @type string $slug The term slug to use. Default empty string.
- * }
- * @return array|WP_Error An array containing the `term_id` and `term_taxonomy_id`,
- * {@see WP_Error} otherwise.
- */
-function wp_insert_term( $term, $taxonomy, $args = array() ) {
- global $wpdb;
-
- if ( ! taxonomy_exists($taxonomy) ) {
- return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
- }
- /**
- * Filter a term before it is sanitized and inserted into the database.
- *
- * @since 3.0.0
- *
- * @param string $term The term to add or update.
- * @param string $taxonomy Taxonomy slug.
- */
- $term = apply_filters( 'pre_insert_term', $term, $taxonomy );
- if ( is_wp_error( $term ) ) {
- return $term;
- }
- if ( is_int($term) && 0 == $term ) {
- return new WP_Error('invalid_term_id', __('Invalid term ID'));
- }
- if ( '' == trim($term) ) {
- return new WP_Error('empty_term_name', __('A name is required for this term'));
- }
- $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => '');
- $args = wp_parse_args( $args, $defaults );
-
- if ( $args['parent'] > 0 && ! term_exists( (int) $args['parent'] ) ) {
- return new WP_Error( 'missing_parent', __( 'Parent term does not exist.' ) );
- }
- $args['name'] = $term;
- $args['taxonomy'] = $taxonomy;
- $args = sanitize_term($args, $taxonomy, 'db');
-
- // expected_slashed ($name)
- $name = wp_unslash( $args['name'] );
- $description = wp_unslash( $args['description'] );
- $parent = (int) $args['parent'];
-
- $slug_provided = ! empty( $args['slug'] );
- if ( ! $slug_provided ) {
- $slug = sanitize_title( $name );
- } else {
- $slug = $args['slug'];
- }
-
- $term_group = 0;
- if ( $args['alias_of'] ) {
- $alias = get_term_by( 'slug', $args['alias_of'], $taxonomy );
- if ( ! empty( $alias->term_group ) ) {
- // The alias we want is already in a group, so let's use that one.
- $term_group = $alias->term_group;
- } elseif ( ! empty( $alias->term_id ) ) {
- /*
- * The alias is not in a group, so we create a new one
- * and add the alias to it.
- */
- $term_group = $wpdb->get_var("SELECT MAX(term_group) FROM $wpdb->terms") + 1;
-
- wp_update_term( $alias->term_id, $taxonomy, array(
- 'term_group' => $term_group,
- ) );
- }
- }
-
- /*
- * Prevent the creation of terms with duplicate names at the same level of a taxonomy hierarchy,
- * unless a unique slug has been explicitly provided.
- */
- $name_matches = get_terms( $taxonomy, array(
- 'name' => $name,
- 'hide_empty' => false,
- ) );
-
- /*
- * The `name` match in `get_terms()` doesn't differentiate accented characters,
- * so we do a stricter comparison here.
- */
- $name_match = null;
- if ( $name_matches ) {
- foreach ( $name_matches as $_match ) {
- if ( strtolower( $name ) === strtolower( $_match->name ) ) {
- $name_match = $_match;
- break;
- }
- }
- }
-
- if ( $name_match ) {
- $slug_match = get_term_by( 'slug', $slug, $taxonomy );
- if ( ! $slug_provided || $name_match->slug === $slug || $slug_match ) {
- if ( is_taxonomy_hierarchical( $taxonomy ) ) {
- $siblings = get_terms( $taxonomy, array( 'get' => 'all', 'parent' => $parent ) );
-
- $existing_term = null;
- if ( $name_match->slug === $slug && in_array( $name, wp_list_pluck( $siblings, 'name' ) ) ) {
- $existing_term = $name_match;
- } elseif ( $slug_match && in_array( $slug, wp_list_pluck( $siblings, 'slug' ) ) ) {
- $existing_term = $slug_match;
- }
-
- if ( $existing_term ) {
- return new WP_Error( 'term_exists', __( 'A term with the name provided already exists with this parent.' ), $existing_term->term_id );
- }
- } else {
- return new WP_Error( 'term_exists', __( 'A term with the name provided already exists in this taxonomy.' ), $name_match->term_id );
- }
- }
- }
-
- $slug = wp_unique_term_slug( $slug, (object) $args );
-
- if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
- return new WP_Error( 'db_insert_error', __( 'Could not insert term into the database' ), $wpdb->last_error );
- }
-
- $term_id = (int) $wpdb->insert_id;
-
- // Seems unreachable, However, Is used in the case that a term name is provided, which sanitizes to an empty string.
- if ( empty($slug) ) {
- $slug = sanitize_title($slug, $term_id);
-
- /** This action is documented in wp-includes/taxonomy-functions.php */
- do_action( 'edit_terms', $term_id, $taxonomy );
- $wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) );
-
- /** This action is documented in wp-includes/taxonomy-functions.php */
- do_action( 'edited_terms', $term_id, $taxonomy );
- }
-
- $tt_id = $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id ) );
-
- if ( !empty($tt_id) ) {
- return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
- }
- $wpdb->insert( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent') + array( 'count' => 0 ) );
- $tt_id = (int) $wpdb->insert_id;
-
- /*
- * Sanity check: if we just created a term with the same parent + taxonomy + slug but a higher term_id than
- * an existing term, then we have unwittingly created a duplicate term. Delete the dupe, and use the term_id
- * and term_taxonomy_id of the older term instead. Then return out of the function so that the "create" hooks
- * are not fired.
- */
- $duplicate_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.term_id, tt.term_taxonomy_id FROM $wpdb->terms t INNER JOIN $wpdb->term_taxonomy tt ON ( tt.term_id = t.term_id ) WHERE t.slug = %s AND tt.parent = %d AND tt.taxonomy = %s AND t.term_id < %d AND tt.term_taxonomy_id != %d", $slug, $parent, $taxonomy, $term_id, $tt_id ) );
- if ( $duplicate_term ) {
- $wpdb->delete( $wpdb->terms, array( 'term_id' => $term_id ) );
- $wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) );
-
- $term_id = (int) $duplicate_term->term_id;
- $tt_id = (int) $duplicate_term->term_taxonomy_id;
-
- clean_term_cache( $term_id, $taxonomy );
- return array( 'term_id' => $term_id, 'term_taxonomy_id' => $tt_id );
- }
-
- /**
- * Fires immediately after a new term is created, before the term cache is cleaned.
- *
- * @since 2.3.0
- *
- * @param int $term_id Term ID.
- * @param int $tt_id Term taxonomy ID.
- * @param string $taxonomy Taxonomy slug.
- */
- do_action( "create_term", $term_id, $tt_id, $taxonomy );
-
- /**
- * Fires after a new term is created for a specific taxonomy.
- *
- * The dynamic portion of the hook name, `$taxonomy`, refers
- * to the slug of the taxonomy the term was created for.
- *
- * @since 2.3.0
- *
- * @param int $term_id Term ID.
- * @param int $tt_id Term taxonomy ID.
- */
- do_action( "create_$taxonomy", $term_id, $tt_id );
-
- /**
- * Filter the term ID after a new term is created.
- *
- * @since 2.3.0
- *
- * @param int $term_id Term ID.
- * @param int $tt_id Taxonomy term ID.
- */
- $term_id = apply_filters( 'term_id_filter', $term_id, $tt_id );
-
- clean_term_cache($term_id, $taxonomy);
-
- /**
- * Fires after a new term is created, and after the term cache has been cleaned.
- *
- * @since 2.3.0
- *
- * @param int $term_id Term ID.
- * @param int $tt_id Term taxonomy ID.
- * @param string $taxonomy Taxonomy slug.
- */
- do_action( 'created_term', $term_id, $tt_id, $taxonomy );
-
- /**
- * Fires after a new term in a specific taxonomy is created, and after the term
- * cache has been cleaned.
- *
- * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug.
- *
- * @since 2.3.0
- *
- * @param int $term_id Term ID.
- * @param int $tt_id Term taxonomy ID.
- */
- do_action( "created_$taxonomy", $term_id, $tt_id );
-
- return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
-}
-
-/**
- * Create Term and Taxonomy Relationships.
- *
- * Relates an object (post, link etc) to a term and taxonomy type. Creates the
- * term and taxonomy relationship if it doesn't already exist. Creates a term if
- * it doesn't exist (using the slug).
- *
- * A relationship means that the term is grouped in or belongs to the taxonomy.
- * A term has no meaning until it is given context by defining which taxonomy it
- * exists under.
- *
- * @since 2.3.0
- *
- * @global wpdb $wpdb The WordPress database abstraction object.
- *
- * @param int $object_id The object to relate to.
- * @param array|int|string $terms A single term slug, single term id, or array of either term slugs or ids.
- * Will replace all existing related terms in this taxonomy.
- * @param string $taxonomy The context in which to relate the term to the object.
- * @param bool $append Optional. If false will delete difference of terms. Default false.
- * @return array|WP_Error Affected Term IDs.
- */
-function wp_set_object_terms( $object_id, $terms, $taxonomy, $append = false ) {
- global $wpdb;
-
- $object_id = (int) $object_id;
-
- if ( ! taxonomy_exists($taxonomy) )
- return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
-
- if ( !is_array($terms) )
- $terms = array($terms);
-
- if ( ! $append )
- $old_tt_ids = wp_get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids', 'orderby' => 'none'));
- else
- $old_tt_ids = array();
-
- $tt_ids = array();
- $term_ids = array();
- $new_tt_ids = array();
-
- foreach ( (array) $terms as $term) {
- if ( !strlen(trim($term)) )
- continue;
-
- if ( !$term_info = term_exists($term, $taxonomy) ) {
- // Skip if a non-existent term ID is passed.
- if ( is_int($term) )
- continue;
- $term_info = wp_insert_term($term, $taxonomy);
- }
- if ( is_wp_error($term_info) )
- return $term_info;
- $term_ids[] = $term_info['term_id'];
- $tt_id = $term_info['term_taxonomy_id'];
- $tt_ids[] = $tt_id;
-
- if ( $wpdb->get_var( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id = %d", $object_id, $tt_id ) ) )
- continue;
-
- /**
- * Fires immediately before an object-term relationship is added.
- *
- * @since 2.9.0
- *
- * @param int $object_id Object ID.
- * @param int $tt_id Term taxonomy ID.
- */
- do_action( 'add_term_relationship', $object_id, $tt_id );
- $wpdb->insert( $wpdb->term_relationships, array( 'object_id' => $object_id, 'term_taxonomy_id' => $tt_id ) );
-
- /**
- * Fires immediately after an object-term relationship is added.
- *
- * @since 2.9.0
- *
- * @param int $object_id Object ID.
- * @param int $tt_id Term taxonomy ID.
- */
- do_action( 'added_term_relationship', $object_id, $tt_id );
- $new_tt_ids[] = $tt_id;
- }
-
- if ( $new_tt_ids )
- wp_update_term_count( $new_tt_ids, $taxonomy );
-
- if ( ! $append ) {
- $delete_tt_ids = array_diff( $old_tt_ids, $tt_ids );
-
- if ( $delete_tt_ids ) {
- $in_delete_tt_ids = "'" . implode( "', '", $delete_tt_ids ) . "'";
- $delete_term_ids = $wpdb->get_col( $wpdb->prepare( "SELECT tt.term_id FROM $wpdb->term_taxonomy AS tt WHERE tt.taxonomy = %s AND tt.term_taxonomy_id IN ($in_delete_tt_ids)", $taxonomy ) );
- $delete_term_ids = array_map( 'intval', $delete_term_ids );
-
- $remove = wp_remove_object_terms( $object_id, $delete_term_ids, $taxonomy );
- if ( is_wp_error( $remove ) ) {
- return $remove;
- }
- }
- }
-
- $t = get_taxonomy($taxonomy);
- if ( ! $append && isset($t->sort) && $t->sort ) {
- $values = array();
- $term_order = 0;
- $final_tt_ids = wp_get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids'));
- foreach ( $tt_ids as $tt_id )
- if ( in_array($tt_id, $final_tt_ids) )
- $values[] = $wpdb->prepare( "(%d, %d, %d)", $object_id, $tt_id, ++$term_order);
- if ( $values )
- if ( false === $wpdb->query( "INSERT INTO $wpdb->term_relationships (object_id, term_taxonomy_id, term_order) VALUES " . join( ',', $values ) . " ON DUPLICATE KEY UPDATE term_order = VALUES(term_order)" ) )
- return new WP_Error( 'db_insert_error', __( 'Could not insert term relationship into the database' ), $wpdb->last_error );
- }
-
- wp_cache_delete( $object_id, $taxonomy . '_relationships' );
-
- /**
- * Fires after an object's terms have been set.
- *
- * @since 2.8.0
- *
- * @param int $object_id Object ID.
- * @param array $terms An array of object terms.
- * @param array $tt_ids An array of term taxonomy IDs.
- * @param string $taxonomy Taxonomy slug.
- * @param bool $append Whether to append new terms to the old terms.
- * @param array $old_tt_ids Old array of term taxonomy IDs.
- */
- do_action( 'set_object_terms', $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids );
- return $tt_ids;
-}
-
-/**
- * Add term(s) associated with a given object.
- *
- * @since 3.6.0
- *
- * @param int $object_id The ID of the object to which the terms will be added.
- * @param array|int|string $terms The slug(s) or ID(s) of the term(s) to add.
- * @param array|string $taxonomy Taxonomy name.
- * @return array|WP_Error Affected Term IDs
- */
-function wp_add_object_terms( $object_id, $terms, $taxonomy ) {
- return wp_set_object_terms( $object_id, $terms, $taxonomy, true );
-}
-
-/**
- * Remove term(s) associated with a given object.
- *
- * @since 3.6.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $object_id The ID of the object from which the terms will be removed.
- * @param array|int|string $terms The slug(s) or ID(s) of the term(s) to remove.
- * @param array|string $taxonomy Taxonomy name.
- * @return bool|WP_Error True on success, false or WP_Error on failure.
- */
-function wp_remove_object_terms( $object_id, $terms, $taxonomy ) {
- global $wpdb;
-
- $object_id = (int) $object_id;
-
- if ( ! taxonomy_exists( $taxonomy ) ) {
- return new WP_Error( 'invalid_taxonomy', __( 'Invalid Taxonomy' ) );
- }
-
- if ( ! is_array( $terms ) ) {
- $terms = array( $terms );
- }
-
- $tt_ids = array();
-
- foreach ( (array) $terms as $term ) {
- if ( ! strlen( trim( $term ) ) ) {
- continue;
- }
-
- if ( ! $term_info = term_exists( $term, $taxonomy ) ) {
- // Skip if a non-existent term ID is passed.
- if ( is_int( $term ) ) {
- continue;
- }
- }
-
- if ( is_wp_error( $term_info ) ) {
- return $term_info;
- }
-
- $tt_ids[] = $term_info['term_taxonomy_id'];
- }
-
- if ( $tt_ids ) {
- $in_tt_ids = "'" . implode( "', '", $tt_ids ) . "'";
-
- /**
- * Fires immediately before an object-term relationship is deleted.
- *
- * @since 2.9.0
- *
- * @param int $object_id Object ID.
- * @param array $tt_ids An array of term taxonomy IDs.
- */
- do_action( 'delete_term_relationships', $object_id, $tt_ids );
- $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
-
- wp_cache_delete( $object_id, $taxonomy . '_relationships' );
-
- /**
- * Fires immediately after an object-term relationship is deleted.
- *
- * @since 2.9.0
- *
- * @param int $object_id Object ID.
- * @param array $tt_ids An array of term taxonomy IDs.
- */
- do_action( 'deleted_term_relationships', $object_id, $tt_ids );
-
- wp_update_term_count( $tt_ids, $taxonomy );
-
- return (bool) $deleted;
- }
-
- return false;
-}
-
-/**
- * Will make slug unique, if it isn't already.
- *
- * The `$slug` has to be unique global to every taxonomy, meaning that one
- * taxonomy term can't have a matching slug with another taxonomy term. Each
- * slug has to be globally unique for every taxonomy.
- *
- * The way this works is that if the taxonomy that the term belongs to is
- * hierarchical and has a parent, it will append that parent to the $slug.
- *
- * If that still doesn't return an unique slug, then it try to append a number
- * until it finds a number that is truly unique.
- *
- * The only purpose for `$term` is for appending a parent, if one exists.
- *
- * @since 2.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $slug The string that will be tried for a unique slug.
- * @param object $term The term object that the `$slug` will belong to.
- * @return string Will return a true unique slug.
- */
-function wp_unique_term_slug( $slug, $term ) {
- global $wpdb;
-
- $needs_suffix = true;
- $original_slug = $slug;
-
- // As of 4.1, duplicate slugs are allowed as long as they're in different taxonomies.
- if ( ! term_exists( $slug ) || get_option( 'db_version' ) >= 30133 && ! get_term_by( 'slug', $slug, $term->taxonomy ) ) {
- $needs_suffix = false;
- }
-
- /*
- * If the taxonomy supports hierarchy and the term has a parent, make the slug unique
- * by incorporating parent slugs.
- */
- $parent_suffix = '';
- if ( $needs_suffix && is_taxonomy_hierarchical( $term->taxonomy ) && ! empty( $term->parent ) ) {
- $the_parent = $term->parent;
- while ( ! empty($the_parent) ) {
- $parent_term = get_term($the_parent, $term->taxonomy);
- if ( is_wp_error($parent_term) || empty($parent_term) )
- break;
- $parent_suffix .= '-' . $parent_term->slug;
- if ( ! term_exists( $slug . $parent_suffix ) ) {
- break;
- }
-
- if ( empty($parent_term->parent) )
- break;
- $the_parent = $parent_term->parent;
- }
- }
-
- // If we didn't get a unique slug, try appending a number to make it unique.
-
- /**
- * Filter whether the proposed unique term slug is bad.
- *
- * @since 4.3.0
- *
- * @param bool $needs_suffix Whether the slug needs to be made unique with a suffix.
- * @param string $slug The slug.
- * @param object $term Term object.
- */
- if ( apply_filters( 'wp_unique_term_slug_is_bad_slug', $needs_suffix, $slug, $term ) ) {
- if ( $parent_suffix ) {
- $slug .= $parent_suffix;
- } else {
- if ( ! empty( $term->term_id ) )
- $query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s AND term_id != %d", $slug, $term->term_id );
- else
- $query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $slug );
-
- if ( $wpdb->get_var( $query ) ) {
- $num = 2;
- do {
- $alt_slug = $slug . "-$num";
- $num++;
- $slug_check = $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $alt_slug ) );
- } while ( $slug_check );
- $slug = $alt_slug;
- }
- }
- }
-
- /**
- * Filter the unique term slug.
- *
- * @since 4.3.0
- *
- * @param string $slug Unique term slug.
- * @param object $term Term object.
- * @param string $original_slug Slug originally passed to the function for testing.
- */
- return apply_filters( 'wp_unique_term_slug', $slug, $term, $original_slug );
-}
-
-/**
- * Update term based on arguments provided.
- *
- * The $args will indiscriminately override all values with the same field name.
- * Care must be taken to not override important information need to update or
- * update will fail (or perhaps create a new term, neither would be acceptable).
- *
- * Defaults will set 'alias_of', 'description', 'parent', and 'slug' if not
- * defined in $args already.
- *
- * 'alias_of' will create a term group, if it doesn't already exist, and update
- * it for the $term.
- *
- * If the 'slug' argument in $args is missing, then the 'name' in $args will be
- * used. It should also be noted that if you set 'slug' and it isn't unique then
- * a WP_Error will be passed back. If you don't pass any slug, then a unique one
- * will be created for you.
- *
- * For what can be overrode in `$args`, check the term scheme can contain and stay
- * away from the term keys.
- *
- * @since 2.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $term_id The ID of the term
- * @param string $taxonomy The context in which to relate the term to the object.
- * @param array|string $args Optional. Array of get_terms() arguments. Default empty array.
- * @return array|WP_Error Returns Term ID and Taxonomy Term ID
- */
-function wp_update_term( $term_id, $taxonomy, $args = array() ) {
- global $wpdb;
-
- if ( ! taxonomy_exists( $taxonomy ) ) {
- return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
- }
-
- $term_id = (int) $term_id;
-
- // First, get all of the original args
- $term = get_term( $term_id, $taxonomy );
-
- if ( is_wp_error( $term ) ) {
- return $term;
- }
-
- if ( ! $term ) {
- return new WP_Error( 'invalid_term', __( 'Empty Term' ) );
- }
-
- $term = (array) $term->data;
-
- // Escape data pulled from DB.
- $term = wp_slash( $term );
-
- // Merge old and new args with new args overwriting old ones.
- $args = array_merge($term, $args);
-
- $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => '');
- $args = wp_parse_args($args, $defaults);
- $args = sanitize_term($args, $taxonomy, 'db');
- $parsed_args = $args;
-
- // expected_slashed ($name)
- $name = wp_unslash( $args['name'] );
- $description = wp_unslash( $args['description'] );
-
- $parsed_args['name'] = $name;
- $parsed_args['description'] = $description;
-
- if ( '' == trim($name) )
- return new WP_Error('empty_term_name', __('A name is required for this term'));
-
- if ( $parsed_args['parent'] > 0 && ! term_exists( (int) $parsed_args['parent'] ) ) {
- return new WP_Error( 'missing_parent', __( 'Parent term does not exist.' ) );
- }
-
- $empty_slug = false;
- if ( empty( $args['slug'] ) ) {
- $empty_slug = true;
- $slug = sanitize_title($name);
- } else {
- $slug = $args['slug'];
- }
-
- $parsed_args['slug'] = $slug;
-
- $term_group = isset( $parsed_args['term_group'] ) ? $parsed_args['term_group'] : 0;
- if ( $args['alias_of'] ) {
- $alias = get_term_by( 'slug', $args['alias_of'], $taxonomy );
- if ( ! empty( $alias->term_group ) ) {
- // The alias we want is already in a group, so let's use that one.
- $term_group = $alias->term_group;
- } elseif ( ! empty( $alias->term_id ) ) {
- /*
- * The alias is not in a group, so we create a new one
- * and add the alias to it.
- */
- $term_group = $wpdb->get_var("SELECT MAX(term_group) FROM $wpdb->terms") + 1;
-
- wp_update_term( $alias->term_id, $taxonomy, array(
- 'term_group' => $term_group,
- ) );
- }
-
- $parsed_args['term_group'] = $term_group;
- }
-
- /**
- * Filter the term parent.
- *
- * Hook to this filter to see if it will cause a hierarchy loop.
- *
- * @since 3.1.0
- *
- * @param int $parent ID of the parent term.
- * @param int $term_id Term ID.
- * @param string $taxonomy Taxonomy slug.
- * @param array $parsed_args An array of potentially altered update arguments for the given term.
- * @param array $args An array of update arguments for the given term.
- */
- $parent = apply_filters( 'wp_update_term_parent', $args['parent'], $term_id, $taxonomy, $parsed_args, $args );
-
- // Check for duplicate slug
- $duplicate = get_term_by( 'slug', $slug, $taxonomy );
- if ( $duplicate && $duplicate->term_id != $term_id ) {
- // If an empty slug was passed or the parent changed, reset the slug to something unique.
- // Otherwise, bail.
- if ( $empty_slug || ( $parent != $term['parent']) )
- $slug = wp_unique_term_slug($slug, (object) $args);
- else
- return new WP_Error('duplicate_term_slug', sprintf(__('The slug “%s” is already in use by another term'), $slug));
- }
-
- $tt_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) );
-
- // Check whether this is a shared term that needs splitting.
- $_term_id = _split_shared_term( $term_id, $tt_id );
- if ( ! is_wp_error( $_term_id ) ) {
- $term_id = $_term_id;
- }
-
- /**
- * Fires immediately before the given terms are edited.
- *
- * @since 2.9.0
- *
- * @param int $term_id Term ID.
- * @param string $taxonomy Taxonomy slug.
- */
- do_action( 'edit_terms', $term_id, $taxonomy );
- $wpdb->update($wpdb->terms, compact( 'name', 'slug', 'term_group' ), compact( 'term_id' ) );
- if ( empty($slug) ) {
- $slug = sanitize_title($name, $term_id);
- $wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) );
- }
-
- /**
- * Fires immediately after the given terms are edited.
- *
- * @since 2.9.0
- *
- * @param int $term_id Term ID
- * @param string $taxonomy Taxonomy slug.
- */
- do_action( 'edited_terms', $term_id, $taxonomy );
-
- /**
- * Fires immediate before a term-taxonomy relationship is updated.
- *
- * @since 2.9.0
- *
- * @param int $tt_id Term taxonomy ID.
- * @param string $taxonomy Taxonomy slug.
- */
- do_action( 'edit_term_taxonomy', $tt_id, $taxonomy );
-
- $wpdb->update( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent' ), array( 'term_taxonomy_id' => $tt_id ) );
-
- /**
- * Fires immediately after a term-taxonomy relationship is updated.
- *
- * @since 2.9.0
- *
- * @param int $tt_id Term taxonomy ID.
- * @param string $taxonomy Taxonomy slug.
- */
- do_action( 'edited_term_taxonomy', $tt_id, $taxonomy );
-
- // Clean the relationship caches for all object types using this term.
- $objects = $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $tt_id ) );
- $tax_object = get_taxonomy( $taxonomy );
- foreach ( $tax_object->object_type as $object_type ) {
- clean_object_term_cache( $objects, $object_type );
- }
-
- /**
- * Fires after a term has been updated, but before the term cache has been cleaned.
- *
- * @since 2.3.0
- *
- * @param int $term_id Term ID.
- * @param int $tt_id Term taxonomy ID.
- * @param string $taxonomy Taxonomy slug.
- */
- do_action( "edit_term", $term_id, $tt_id, $taxonomy );
-
- /**
- * Fires after a term in a specific taxonomy has been updated, but before the term
- * cache has been cleaned.
- *
- * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug.
- *
- * @since 2.3.0
- *
- * @param int $term_id Term ID.
- * @param int $tt_id Term taxonomy ID.
- */
- do_action( "edit_$taxonomy", $term_id, $tt_id );
-
- /** This filter is documented in wp-includes/taxonomy-functions.php */
- $term_id = apply_filters( 'term_id_filter', $term_id, $tt_id );
-
- clean_term_cache($term_id, $taxonomy);
-
- /**
- * Fires after a term has been updated, and the term cache has been cleaned.
- *
- * @since 2.3.0
- *
- * @param int $term_id Term ID.
- * @param int $tt_id Term taxonomy ID.
- * @param string $taxonomy Taxonomy slug.
- */
- do_action( "edited_term", $term_id, $tt_id, $taxonomy );
-
- /**
- * Fires after a term for a specific taxonomy has been updated, and the term
- * cache has been cleaned.
- *
- * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug.
- *
- * @since 2.3.0
- *
- * @param int $term_id Term ID.
- * @param int $tt_id Term taxonomy ID.
- */
- do_action( "edited_$taxonomy", $term_id, $tt_id );
-
- return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
-}
-
-/**
- * Enable or disable term counting.
- *
- * @since 2.5.0
- *
- * @staticvar bool $_defer
- *
- * @param bool $defer Optional. Enable if true, disable if false.
- * @return bool Whether term counting is enabled or disabled.
- */
-function wp_defer_term_counting($defer=null) {
- static $_defer = false;
-
- if ( is_bool($defer) ) {
- $_defer = $defer;
- // flush any deferred counts
- if ( !$defer )
- wp_update_term_count( null, null, true );
- }
-
- return $_defer;
-}
-
-/**
- * Updates the amount of terms in taxonomy.
- *
- * If there is a taxonomy callback applied, then it will be called for updating
- * the count.
- *
- * The default action is to count what the amount of terms have the relationship
- * of term ID. Once that is done, then update the database.
- *
- * @since 2.3.0
- *
- * @staticvar array $_deferred
- *
- * @param int|array $terms The term_taxonomy_id of the terms.
- * @param string $taxonomy The context of the term.
- * @return bool If no terms will return false, and if successful will return true.
- */
-function wp_update_term_count( $terms, $taxonomy, $do_deferred=false ) {
- static $_deferred = array();
-
- if ( $do_deferred ) {
- foreach ( (array) array_keys($_deferred) as $tax ) {
- wp_update_term_count_now( $_deferred[$tax], $tax );
- unset( $_deferred[$tax] );
- }
- }
-
- if ( empty($terms) )
- return false;
-
- if ( !is_array($terms) )
- $terms = array($terms);
-
- if ( wp_defer_term_counting() ) {
- if ( !isset($_deferred[$taxonomy]) )
- $_deferred[$taxonomy] = array();
- $_deferred[$taxonomy] = array_unique( array_merge($_deferred[$taxonomy], $terms) );
- return true;
- }
-
- return wp_update_term_count_now( $terms, $taxonomy );
-}
-
-/**
- * Perform term count update immediately.
- *
- * @since 2.5.0
- *
- * @param array $terms The term_taxonomy_id of terms to update.
- * @param string $taxonomy The context of the term.
- * @return true Always true when complete.
- */
-function wp_update_term_count_now( $terms, $taxonomy ) {
- $terms = array_map('intval', $terms);
-
- $taxonomy = get_taxonomy($taxonomy);
- if ( !empty($taxonomy->update_count_callback) ) {
- call_user_func($taxonomy->update_count_callback, $terms, $taxonomy);
- } else {
- $object_types = (array) $taxonomy->object_type;
- foreach ( $object_types as &$object_type ) {
- if ( 0 === strpos( $object_type, 'attachment:' ) )
- list( $object_type ) = explode( ':', $object_type );
- }
-
- if ( $object_types == array_filter( $object_types, 'post_type_exists' ) ) {
- // Only post types are attached to this taxonomy
- _update_post_term_count( $terms, $taxonomy );
- } else {
- // Default count updater
- _update_generic_term_count( $terms, $taxonomy );
- }
- }
-
- clean_term_cache($terms, '', false);
-
- return true;
-}
-
-//
-// Cache
-//
-
-/**
- * Removes the taxonomy relationship to terms from the cache.
- *
- * Will remove the entire taxonomy relationship containing term `$object_id`. The
- * term IDs have to exist within the taxonomy `$object_type` for the deletion to
- * take place.
- *
- * @since 2.3.0
- *
- * @see get_object_taxonomies() for more on $object_type.
- *
- * @param int|array $object_ids Single or list of term object ID(s).
- * @param array|string $object_type The taxonomy object type.
- */
-function clean_object_term_cache($object_ids, $object_type) {
- if ( !is_array($object_ids) )
- $object_ids = array($object_ids);
-
- $taxonomies = get_object_taxonomies( $object_type );
-
- foreach ( $object_ids as $id ) {
- foreach ( $taxonomies as $taxonomy ) {
- wp_cache_delete($id, "{$taxonomy}_relationships");
- }
- }
-
- /**
- * Fires after the object term cache has been cleaned.
- *
- * @since 2.5.0
- *
- * @param array $object_ids An array of object IDs.
- * @param string $objet_type Object type.
- */
- do_action( 'clean_object_term_cache', $object_ids, $object_type );
-}
-
-/**
- * Will remove all of the term ids from the cache.
- *
- * @since 2.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- * @global bool $_wp_suspend_cache_invalidation
- *
- * @param int|array $ids Single or list of Term IDs.
- * @param string $taxonomy Optional. Can be empty and will assume `tt_ids`, else will use for context.
- * Default empty.
- * @param bool $clean_taxonomy Optional. Whether to clean taxonomy wide caches (true), or just individual
- * term object caches (false). Default true.
- */
-function clean_term_cache($ids, $taxonomy = '', $clean_taxonomy = true) {
- global $wpdb, $_wp_suspend_cache_invalidation;
-
- if ( ! empty( $_wp_suspend_cache_invalidation ) ) {
- return;
- }
-
- if ( !is_array($ids) )
- $ids = array($ids);
-
- $taxonomies = array();
- // If no taxonomy, assume tt_ids.
- if ( empty($taxonomy) ) {
- $tt_ids = array_map('intval', $ids);
- $tt_ids = implode(', ', $tt_ids);
- $terms = $wpdb->get_results("SELECT term_id, taxonomy FROM $wpdb->term_taxonomy WHERE term_taxonomy_id IN ($tt_ids)");
- $ids = array();
- foreach ( (array) $terms as $term ) {
- $taxonomies[] = $term->taxonomy;
- $ids[] = $term->term_id;
- wp_cache_delete( $term->term_id, 'terms' );
- }
- $taxonomies = array_unique($taxonomies);
- } else {
- $taxonomies = array($taxonomy);
- foreach ( $taxonomies as $taxonomy ) {
- foreach ( $ids as $id ) {
- wp_cache_delete( $id, 'terms' );
- }
- }
- }
-
- foreach ( $taxonomies as $taxonomy ) {
- if ( $clean_taxonomy ) {
- wp_cache_delete('all_ids', $taxonomy);
- wp_cache_delete('get', $taxonomy);
- delete_option("{$taxonomy}_children");
- // Regenerate {$taxonomy}_children
- _get_term_hierarchy($taxonomy);
- }
-
- /**
- * Fires once after each taxonomy's term cache has been cleaned.
- *
- * @since 2.5.0
- *
- * @param array $ids An array of term IDs.
- * @param string $taxonomy Taxonomy slug.
- */
- do_action( 'clean_term_cache', $ids, $taxonomy );
- }
-
- wp_cache_set( 'last_changed', microtime(), 'terms' );
-}
-
-/**
- * Retrieves the taxonomy relationship to the term object id.
- *
- * @since 2.3.0
- *
- * @param int $id Term object ID.
- * @param string $taxonomy Taxonomy name.
- * @return bool|mixed Empty array if $terms found, but not `$taxonomy`. False if nothing is in cache
- * for `$taxonomy` and `$id`.
- */
-function get_object_term_cache( $id, $taxonomy ) {
- return wp_cache_get( $id, "{$taxonomy}_relationships" );
-}
-
-/**
- * Updates the cache for the given term object ID(s).
- *
- * Note: Due to performance concerns, great care should be taken to only update
- * term caches when necessary. Processing time can increase exponentially depending
- * on both the number of passed term IDs and the number of taxonomies those terms
- * belong to.
- *
- * Caches will only be updated for terms not already cached.
- *
- * @since 2.3.0
- *
- * @param string|array $object_ids Comma-separated list or array of term object IDs.
- * @param array|string $object_type The taxonomy object type.
- * @return void|false False if all of the terms in `$object_ids` are already cached.
- */
-function update_object_term_cache($object_ids, $object_type) {
- if ( empty($object_ids) )
- return;
-
- if ( !is_array($object_ids) )
- $object_ids = explode(',', $object_ids);
-
- $object_ids = array_map('intval', $object_ids);
-
- $taxonomies = get_object_taxonomies($object_type);
-
- $ids = array();
- foreach ( (array) $object_ids as $id ) {
- foreach ( $taxonomies as $taxonomy ) {
- if ( false === wp_cache_get($id, "{$taxonomy}_relationships") ) {
- $ids[] = $id;
- break;
- }
- }
- }
-
- if ( empty( $ids ) )
- return false;
-
- $terms = wp_get_object_terms( $ids, $taxonomies, array(
- 'fields' => 'all_with_object_id',
- 'orderby' => 'none',
- 'update_term_meta_cache' => false,
- ) );
-
- $object_terms = array();
- foreach ( (array) $terms as $term )
- $object_terms[$term->object_id][$term->taxonomy][] = $term;
-
- foreach ( $ids as $id ) {
- foreach ( $taxonomies as $taxonomy ) {
- if ( ! isset($object_terms[$id][$taxonomy]) ) {
- if ( !isset($object_terms[$id]) )
- $object_terms[$id] = array();
- $object_terms[$id][$taxonomy] = array();
- }
- }
- }
-
- foreach ( $object_terms as $id => $value ) {
- foreach ( $value as $taxonomy => $terms ) {
- wp_cache_add( $id, $terms, "{$taxonomy}_relationships" );
- }
- }
-}
-
-/**
- * Updates Terms to Taxonomy in cache.
- *
- * @since 2.3.0
- *
- * @param array $terms List of term objects to change.
- * @param string $taxonomy Optional. Update Term to this taxonomy in cache. Default empty.
- */
-function update_term_cache( $terms, $taxonomy = '' ) {
- foreach ( (array) $terms as $term ) {
- // Create a copy in case the array was passed by reference.
- $_term = $term;
-
- // Object ID should not be cached.
- unset( $_term->object_id );
-
- wp_cache_add( $term->term_id, $_term, 'terms' );
- }
-}
-
-//
-// Private
-//
-
-/**
- * Retrieves children of taxonomy as Term IDs.
- *
- * @ignore
- * @since 2.3.0
- *
- * @param string $taxonomy Taxonomy name.
- * @return array Empty if $taxonomy isn't hierarchical or returns children as Term IDs.
- */
-function _get_term_hierarchy( $taxonomy ) {
- if ( !is_taxonomy_hierarchical($taxonomy) )
- return array();
- $children = get_option("{$taxonomy}_children");
-
- if ( is_array($children) )
- return $children;
- $children = array();
- $terms = get_terms($taxonomy, array('get' => 'all', 'orderby' => 'id', 'fields' => 'id=>parent'));
- foreach ( $terms as $term_id => $parent ) {
- if ( $parent > 0 )
- $children[$parent][] = $term_id;
- }
- update_option("{$taxonomy}_children", $children);
-
- return $children;
-}
-
-/**
- * Get the subset of $terms that are descendants of $term_id.
- *
- * If `$terms` is an array of objects, then _get_term_children() returns an array of objects.
- * If `$terms` is an array of IDs, then _get_term_children() returns an array of IDs.
- *
- * @access private
- * @since 2.3.0
- *
- * @param int $term_id The ancestor term: all returned terms should be descendants of `$term_id`.
- * @param array $terms The set of terms - either an array of term objects or term IDs - from which those that
- * are descendants of $term_id will be chosen.
- * @param string $taxonomy The taxonomy which determines the hierarchy of the terms.
- * @param array $ancestors Optional. Term ancestors that have already been identified. Passed by reference, to keep
- * track of found terms when recursing the hierarchy. The array of located ancestors is used
- * to prevent infinite recursion loops. For performance, `term_ids` are used as array keys,
- * with 1 as value. Default empty array.
- * @return array|WP_Error The subset of $terms that are descendants of $term_id.
- */
-function _get_term_children( $term_id, $terms, $taxonomy, &$ancestors = array() ) {
- $empty_array = array();
- if ( empty($terms) )
- return $empty_array;
-
- $term_list = array();
- $has_children = _get_term_hierarchy($taxonomy);
-
- if ( ( 0 != $term_id ) && ! isset($has_children[$term_id]) )
- return $empty_array;
-
- // Include the term itself in the ancestors array, so we can properly detect when a loop has occurred.
- if ( empty( $ancestors ) ) {
- $ancestors[ $term_id ] = 1;
- }
-
- foreach ( (array) $terms as $term ) {
- $use_id = false;
- if ( !is_object($term) ) {
- $term = get_term($term, $taxonomy);
- if ( is_wp_error( $term ) )
- return $term;
- $use_id = true;
- }
-
- // Don't recurse if we've already identified the term as a child - this indicates a loop.
- if ( isset( $ancestors[ $term->term_id ] ) ) {
- continue;
- }
-
- if ( $term->parent == $term_id ) {
- if ( $use_id )
- $term_list[] = $term->term_id;
- else
- $term_list[] = $term;
-
- if ( !isset($has_children[$term->term_id]) )
- continue;
-
- $ancestors[ $term->term_id ] = 1;
-
- if ( $children = _get_term_children( $term->term_id, $terms, $taxonomy, $ancestors) )
- $term_list = array_merge($term_list, $children);
- }
- }
-
- return $term_list;
-}
-
-/**
- * Add count of children to parent count.
- *
- * Recalculates term counts by including items from child terms. Assumes all
- * relevant children are already in the $terms argument.
- *
- * @access private
- * @since 2.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array $terms List of term objects, passed by reference.
- * @param string $taxonomy Term context.
- */
-function _pad_term_counts( &$terms, $taxonomy ) {
- global $wpdb;
-
- // This function only works for hierarchical taxonomies like post categories.
- if ( !is_taxonomy_hierarchical( $taxonomy ) )
- return;
-
- $term_hier = _get_term_hierarchy($taxonomy);
-
- if ( empty($term_hier) )
- return;
-
- $term_items = array();
- $terms_by_id = array();
- $term_ids = array();
-
- foreach ( (array) $terms as $key => $term ) {
- $terms_by_id[$term->term_id] = & $terms[$key];
- $term_ids[$term->term_taxonomy_id] = $term->term_id;
- }
-
- // Get the object and term ids and stick them in a lookup table.
- $tax_obj = get_taxonomy($taxonomy);
- $object_types = esc_sql($tax_obj->object_type);
- $results = $wpdb->get_results("SELECT object_id, term_taxonomy_id FROM $wpdb->term_relationships INNER JOIN $wpdb->posts ON object_id = ID WHERE term_taxonomy_id IN (" . implode(',', array_keys($term_ids)) . ") AND post_type IN ('" . implode("', '", $object_types) . "') AND post_status = 'publish'");
- foreach ( $results as $row ) {
- $id = $term_ids[$row->term_taxonomy_id];
- $term_items[$id][$row->object_id] = isset($term_items[$id][$row->object_id]) ? ++$term_items[$id][$row->object_id] : 1;
- }
-
- // Touch every ancestor's lookup row for each post in each term.
- foreach ( $term_ids as $term_id ) {
- $child = $term_id;
- $ancestors = array();
- while ( !empty( $terms_by_id[$child] ) && $parent = $terms_by_id[$child]->parent ) {
- $ancestors[] = $child;
- if ( !empty( $term_items[$term_id] ) )
- foreach ( $term_items[$term_id] as $item_id => $touches ) {
- $term_items[$parent][$item_id] = isset($term_items[$parent][$item_id]) ? ++$term_items[$parent][$item_id]: 1;
- }
- $child = $parent;
-
- if ( in_array( $parent, $ancestors ) ) {
- break;
- }
- }
- }
-
- // Transfer the touched cells.
- foreach ( (array) $term_items as $id => $items )
- if ( isset($terms_by_id[$id]) )
- $terms_by_id[$id]->count = count($items);
-}
-
-//
-// Default callbacks
-//
-
-/**
- * Will update term count based on object types of the current taxonomy.
- *
- * Private function for the default callback for post_tag and category
- * taxonomies.
- *
- * @access private
- * @since 2.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array $terms List of Term taxonomy IDs.
- * @param object $taxonomy Current taxonomy object of terms.
- */
-function _update_post_term_count( $terms, $taxonomy ) {
- global $wpdb;
-
- $object_types = (array) $taxonomy->object_type;
-
- foreach ( $object_types as &$object_type )
- list( $object_type ) = explode( ':', $object_type );
-
- $object_types = array_unique( $object_types );
-
- if ( false !== ( $check_attachments = array_search( 'attachment', $object_types ) ) ) {
- unset( $object_types[ $check_attachments ] );
- $check_attachments = true;
- }
-
- if ( $object_types )
- $object_types = esc_sql( array_filter( $object_types, 'post_type_exists' ) );
-
- foreach ( (array) $terms as $term ) {
- $count = 0;
-
- // Attachments can be 'inherit' status, we need to base count off the parent's status if so.
- if ( $check_attachments )
- $count += (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts p1 WHERE p1.ID = $wpdb->term_relationships.object_id AND ( post_status = 'publish' OR ( post_status = 'inherit' AND post_parent > 0 AND ( SELECT post_status FROM $wpdb->posts WHERE ID = p1.post_parent ) = 'publish' ) ) AND post_type = 'attachment' AND term_taxonomy_id = %d", $term ) );
-
- if ( $object_types )
- $count += (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type IN ('" . implode("', '", $object_types ) . "') AND term_taxonomy_id = %d", $term ) );
-
- /** This action is documented in wp-includes/taxonomy-functions.php */
- do_action( 'edit_term_taxonomy', $term, $taxonomy->name );
- $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
-
- /** This action is documented in wp-includes/taxonomy-functions.php */
- do_action( 'edited_term_taxonomy', $term, $taxonomy->name );
- }
-}
-
-/**
- * Will update term count based on number of objects.
- *
- * Default callback for the 'link_category' taxonomy.
- *
- * @since 3.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array $terms List of term taxonomy IDs.
- * @param object $taxonomy Current taxonomy object of terms.
- */
-function _update_generic_term_count( $terms, $taxonomy ) {
- global $wpdb;
-
- foreach ( (array) $terms as $term ) {
- $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $term ) );
-
- /** This action is documented in wp-includes/taxonomy-functions.php */
- do_action( 'edit_term_taxonomy', $term, $taxonomy->name );
- $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
-
- /** This action is documented in wp-includes/taxonomy-functions.php */
- do_action( 'edited_term_taxonomy', $term, $taxonomy->name );
- }
-}
-
-/**
- * Create a new term for a term_taxonomy item that currently shares its term
- * with another term_taxonomy.
- *
- * @ignore
- * @since 4.2.0
- * @since 4.3.0 Introduced `$record` parameter. Also, `$term_id` and
- * `$term_taxonomy_id` can now accept objects.
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int|object $term_id ID of the shared term, or the shared term object.
- * @param int|object $term_taxonomy_id ID of the term_taxonomy item to receive a new term, or the term_taxonomy object
- * (corresponding to a row from the term_taxonomy table).
- * @param bool $record Whether to record data about the split term in the options table. The recording
- * process has the potential to be resource-intensive, so during batch operations
- * it can be beneficial to skip inline recording and do it just once, after the
- * batch is processed. Only set this to `false` if you know what you are doing.
- * Default: true.
- * @return int|WP_Error When the current term does not need to be split (or cannot be split on the current
- * database schema), `$term_id` is returned. When the term is successfully split, the
- * new term_id is returned. A WP_Error is returned for miscellaneous errors.
- */
-function _split_shared_term( $term_id, $term_taxonomy_id, $record = true ) {
- global $wpdb;
-
- if ( is_object( $term_id ) ) {
- $shared_term = $term_id;
- $term_id = intval( $shared_term->term_id );
- }
-
- if ( is_object( $term_taxonomy_id ) ) {
- $term_taxonomy = $term_taxonomy_id;
- $term_taxonomy_id = intval( $term_taxonomy->term_taxonomy_id );
- }
-
- // If there are no shared term_taxonomy rows, there's nothing to do here.
- $shared_tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy tt WHERE tt.term_id = %d AND tt.term_taxonomy_id != %d", $term_id, $term_taxonomy_id ) );
-
- if ( ! $shared_tt_count ) {
- return $term_id;
- }
-
- /*
- * Verify that the term_taxonomy_id passed to the function is actually associated with the term_id.
- * If there's a mismatch, it may mean that the term is already split. Return the actual term_id from the db.
- */
- $check_term_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $term_taxonomy_id ) );
- if ( $check_term_id != $term_id ) {
- return $check_term_id;
- }
-
- // Pull up data about the currently shared slug, which we'll use to populate the new one.
- if ( empty( $shared_term ) ) {
- $shared_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.* FROM $wpdb->terms t WHERE t.term_id = %d", $term_id ) );
- }
-
- $new_term_data = array(
- 'name' => $shared_term->name,
- 'slug' => $shared_term->slug,
- 'term_group' => $shared_term->term_group,
- );
-
- if ( false === $wpdb->insert( $wpdb->terms, $new_term_data ) ) {
- return new WP_Error( 'db_insert_error', __( 'Could not split shared term.' ), $wpdb->last_error );
- }
-
- $new_term_id = (int) $wpdb->insert_id;
-
- // Update the existing term_taxonomy to point to the newly created term.
- $wpdb->update( $wpdb->term_taxonomy,
- array( 'term_id' => $new_term_id ),
- array( 'term_taxonomy_id' => $term_taxonomy_id )
- );
-
- // Reassign child terms to the new parent.
- if ( empty( $term_taxonomy ) ) {
- $term_taxonomy = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $term_taxonomy_id ) );
- }
-
- $children_tt_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE parent = %d AND taxonomy = %s", $term_id, $term_taxonomy->taxonomy ) );
- if ( ! empty( $children_tt_ids ) ) {
- foreach ( $children_tt_ids as $child_tt_id ) {
- $wpdb->update( $wpdb->term_taxonomy,
- array( 'parent' => $new_term_id ),
- array( 'term_taxonomy_id' => $child_tt_id )
- );
- clean_term_cache( $term_id, $term_taxonomy->taxonomy );
- }
- } else {
- // If the term has no children, we must force its taxonomy cache to be rebuilt separately.
- clean_term_cache( $new_term_id, $term_taxonomy->taxonomy );
- }
-
- // Clean the cache for term taxonomies formerly shared with the current term.
- $shared_term_taxonomies = $wpdb->get_row( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) );
- if ( $shared_term_taxonomies ) {
- foreach ( $shared_term_taxonomies as $shared_term_taxonomy ) {
- clean_term_cache( $term_id, $shared_term_taxonomy );
- }
- }
-
- // Keep a record of term_ids that have been split, keyed by old term_id. See {@see wp_get_split_term()}.
- if ( $record ) {
- $split_term_data = get_option( '_split_terms', array() );
- if ( ! isset( $split_term_data[ $term_id ] ) ) {
- $split_term_data[ $term_id ] = array();
- }
-
- $split_term_data[ $term_id ][ $term_taxonomy->taxonomy ] = $new_term_id;
- update_option( '_split_terms', $split_term_data );
- }
-
- // If we've just split the final shared term, set the "finished" flag.
- $shared_terms_exist = $wpdb->get_results(
- "SELECT tt.term_id, t.*, count(*) as term_tt_count FROM {$wpdb->term_taxonomy} tt
- LEFT JOIN {$wpdb->terms} t ON t.term_id = tt.term_id
- GROUP BY t.term_id
- HAVING term_tt_count > 1
- LIMIT 1"
- );
- if ( ! $shared_terms_exist ) {
- update_option( 'finished_splitting_shared_terms', true );
- }
-
- /**
- * Fires after a previously shared taxonomy term is split into two separate terms.
- *
- * @since 4.2.0
- *
- * @param int $term_id ID of the formerly shared term.
- * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
- * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
- * @param string $taxonomy Taxonomy for the split term.
- */
- do_action( 'split_shared_term', $term_id, $new_term_id, $term_taxonomy_id, $term_taxonomy->taxonomy );
-
- return $new_term_id;
-}
-
-/**
- * Splits a batch of shared taxonomy terms.
- *
- * @since 4.3.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- */
-function _wp_batch_split_terms() {
- global $wpdb;
-
- $lock_name = 'term_split.lock';
-
- // Try to lock.
- $lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", $lock_name, time() ) );
-
- if ( ! $lock_result ) {
- $lock_result = get_option( $lock_name );
-
- // Bail if we were unable to create a lock, or if the existing lock is still valid.
- if ( ! $lock_result || ( $lock_result > ( time() - HOUR_IN_SECONDS ) ) ) {
- wp_schedule_single_event( time() + ( 5 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' );
- return;
- }
- }
-
- // Update the lock, as by this point we've definitely got a lock, just need to fire the actions.
- update_option( $lock_name, time() );
-
- // Get a list of shared terms (those with more than one associated row in term_taxonomy).
- $shared_terms = $wpdb->get_results(
- "SELECT tt.term_id, t.*, count(*) as term_tt_count FROM {$wpdb->term_taxonomy} tt
- LEFT JOIN {$wpdb->terms} t ON t.term_id = tt.term_id
- GROUP BY t.term_id
- HAVING term_tt_count > 1
- LIMIT 10"
- );
-
- // No more terms, we're done here.
- if ( ! $shared_terms ) {
- update_option( 'finished_splitting_shared_terms', true );
- delete_option( $lock_name );
- return;
- }
-
- // Shared terms found? We'll need to run this script again.
- wp_schedule_single_event( time() + ( 2 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' );
-
- // Rekey shared term array for faster lookups.
- $_shared_terms = array();
- foreach ( $shared_terms as $shared_term ) {
- $term_id = intval( $shared_term->term_id );
- $_shared_terms[ $term_id ] = $shared_term;
- }
- $shared_terms = $_shared_terms;
-
- // Get term taxonomy data for all shared terms.
- $shared_term_ids = implode( ',', array_keys( $shared_terms ) );
- $shared_tts = $wpdb->get_results( "SELECT * FROM {$wpdb->term_taxonomy} WHERE `term_id` IN ({$shared_term_ids})" );
-
- // Split term data recording is slow, so we do it just once, outside the loop.
- $split_term_data = get_option( '_split_terms', array() );
- $skipped_first_term = $taxonomies = array();
- foreach ( $shared_tts as $shared_tt ) {
- $term_id = intval( $shared_tt->term_id );
-
- // Don't split the first tt belonging to a given term_id.
- if ( ! isset( $skipped_first_term[ $term_id ] ) ) {
- $skipped_first_term[ $term_id ] = 1;
- continue;
- }
-
- if ( ! isset( $split_term_data[ $term_id ] ) ) {
- $split_term_data[ $term_id ] = array();
- }
-
- // Keep track of taxonomies whose hierarchies need flushing.
- if ( ! isset( $taxonomies[ $shared_tt->taxonomy ] ) ) {
- $taxonomies[ $shared_tt->taxonomy ] = 1;
- }
-
- // Split the term.
- $split_term_data[ $term_id ][ $shared_tt->taxonomy ] = _split_shared_term( $shared_terms[ $term_id ], $shared_tt, false );
- }
-
- // Rebuild the cached hierarchy for each affected taxonomy.
- foreach ( array_keys( $taxonomies ) as $tax ) {
- delete_option( "{$tax}_children" );
- _get_term_hierarchy( $tax );
- }
-
- update_option( '_split_terms', $split_term_data );
-
- delete_option( $lock_name );
-}
-
-/**
- * In order to avoid the _wp_batch_split_terms() job being accidentally removed,
- * check that it's still scheduled while we haven't finished splitting terms.
- *
- * @ignore
- * @since 4.3.0
- */
-function _wp_check_for_scheduled_split_terms() {
- if ( ! get_option( 'finished_splitting_shared_terms' ) && ! wp_next_scheduled( 'wp_split_shared_term_batch' ) ) {
- wp_schedule_single_event( time() + MINUTE_IN_SECONDS, 'wp_split_shared_term_batch' );
- }
-}
-
-/**
- * Check default categories when a term gets split to see if any of them need to be updated.
- *
- * @ignore
- * @since 4.2.0
- *
- * @param int $term_id ID of the formerly shared term.
- * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
- * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
- * @param string $taxonomy Taxonomy for the split term.
- */
-function _wp_check_split_default_terms( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
- if ( 'category' != $taxonomy ) {
- return;
- }
-
- foreach ( array( 'default_category', 'default_link_category', 'default_email_category' ) as $option ) {
- if ( $term_id == get_option( $option, -1 ) ) {
- update_option( $option, $new_term_id );
- }
- }
-}
-
-/**
- * Check menu items when a term gets split to see if any of them need to be updated.
- *
- * @ignore
- * @since 4.2.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $term_id ID of the formerly shared term.
- * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
- * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
- * @param string $taxonomy Taxonomy for the split term.
- */
-function _wp_check_split_terms_in_menus( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
- global $wpdb;
- $post_ids = $wpdb->get_col( $wpdb->prepare(
- "SELECT m1.post_id
- FROM {$wpdb->postmeta} AS m1
- INNER JOIN {$wpdb->postmeta} AS m2 ON ( m2.post_id = m1.post_id )
- INNER JOIN {$wpdb->postmeta} AS m3 ON ( m3.post_id = m1.post_id )
- WHERE ( m1.meta_key = '_menu_item_type' AND m1.meta_value = 'taxonomy' )
- AND ( m2.meta_key = '_menu_item_object' AND m2.meta_value = '%s' )
- AND ( m3.meta_key = '_menu_item_object_id' AND m3.meta_value = %d )",
- $taxonomy,
- $term_id
- ) );
-
- if ( $post_ids ) {
- foreach ( $post_ids as $post_id ) {
- update_post_meta( $post_id, '_menu_item_object_id', $new_term_id, $term_id );
- }
- }
-}
-
-/**
- * If the term being split is a nav_menu, change associations.
- *
- * @ignore
- * @since 4.3.0
- *
- * @param int $term_id ID of the formerly shared term.
- * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
- * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
- * @param string $taxonomy Taxonomy for the split term.
- */
-function _wp_check_split_nav_menu_terms( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
- if ( 'nav_menu' !== $taxonomy ) {
- return;
- }
-
- // Update menu locations.
- $locations = get_nav_menu_locations();
- foreach ( $locations as $location => $menu_id ) {
- if ( $term_id == $menu_id ) {
- $locations[ $location ] = $new_term_id;
- }
- }
- set_theme_mod( 'nav_menu_locations', $locations );
-}
-
-/**
- * Get data about terms that previously shared a single term_id, but have since been split.
- *
- * @since 4.2.0
- *
- * @param int $old_term_id Term ID. This is the old, pre-split term ID.
- * @return array Array of new term IDs, keyed by taxonomy.
- */
-function wp_get_split_terms( $old_term_id ) {
- $split_terms = get_option( '_split_terms', array() );
-
- $terms = array();
- if ( isset( $split_terms[ $old_term_id ] ) ) {
- $terms = $split_terms[ $old_term_id ];
- }
-
- return $terms;
-}
-
-/**
- * Get the new term ID corresponding to a previously split term.
- *
- * @since 4.2.0
- *
- * @param int $old_term_id Term ID. This is the old, pre-split term ID.
- * @param string $taxonomy Taxonomy that the term belongs to.
- * @return int|false If a previously split term is found corresponding to the old term_id and taxonomy,
- * the new term_id will be returned. If no previously split term is found matching
- * the parameters, returns false.
- */
-function wp_get_split_term( $old_term_id, $taxonomy ) {
- $split_terms = wp_get_split_terms( $old_term_id );
-
- $term_id = false;
- if ( isset( $split_terms[ $taxonomy ] ) ) {
- $term_id = (int) $split_terms[ $taxonomy ];
- }
-
- return $term_id;
-}
-
-/**
- * Determine whether a term is shared between multiple taxonomies.
- *
- * Shared taxonomy terms began to be split in 4.3, but failed cron tasks or other delays in upgrade routines may cause
- * shared terms to remain.
- *
- * @since 4.4.0
- *
- * @param int $term_id
- * @return bool
- */
-function wp_term_is_shared( $term_id ) {
- global $wpdb;
-
- if ( get_option( 'finished_splitting_shared_terms' ) ) {
- return false;
- }
-
- $tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) );
-
- return $tt_count > 1;
-}
-
-/**
- * Generate a permalink for a taxonomy term archive.
- *
- * @since 2.5.0
- *
- * @global WP_Rewrite $wp_rewrite
- *
- * @param object|int|string $term The term object, ID, or slug whose link will be retrieved.
- * @param string $taxonomy Optional. Taxonomy. Default empty.
- * @return string|WP_Error HTML link to taxonomy term archive on success, WP_Error if term does not exist.
- */
-function get_term_link( $term, $taxonomy = '' ) {
- global $wp_rewrite;
-
- if ( !is_object($term) ) {
- if ( is_int( $term ) ) {
- $term = get_term( $term, $taxonomy );
- } else {
- $term = get_term_by( 'slug', $term, $taxonomy );
- }
- }
-
- if ( !is_object($term) )
- $term = new WP_Error('invalid_term', __('Empty Term'));
-
- if ( is_wp_error( $term ) )
- return $term;
-
- $taxonomy = $term->taxonomy;
-
- $termlink = $wp_rewrite->get_extra_permastruct($taxonomy);
-
- $slug = $term->slug;
- $t = get_taxonomy($taxonomy);
-
- if ( empty($termlink) ) {
- if ( 'category' == $taxonomy )
- $termlink = '?cat=' . $term->term_id;
- elseif ( $t->query_var )
- $termlink = "?$t->query_var=$slug";
- else
- $termlink = "?taxonomy=$taxonomy&term=$slug";
- $termlink = home_url($termlink);
- } else {
- if ( $t->rewrite['hierarchical'] ) {
- $hierarchical_slugs = array();
- $ancestors = get_ancestors( $term->term_id, $taxonomy, 'taxonomy' );
- foreach ( (array)$ancestors as $ancestor ) {
- $ancestor_term = get_term($ancestor, $taxonomy);
- $hierarchical_slugs[] = $ancestor_term->slug;
- }
- $hierarchical_slugs = array_reverse($hierarchical_slugs);
- $hierarchical_slugs[] = $slug;
- $termlink = str_replace("%$taxonomy%", implode('/', $hierarchical_slugs), $termlink);
- } else {
- $termlink = str_replace("%$taxonomy%", $slug, $termlink);
- }
- $termlink = home_url( user_trailingslashit($termlink, 'category') );
- }
- // Back Compat filters.
- if ( 'post_tag' == $taxonomy ) {
-
- /**
- * Filter the tag link.
- *
- * @since 2.3.0
- * @deprecated 2.5.0 Use 'term_link' instead.
- *
- * @param string $termlink Tag link URL.
- * @param int $term_id Term ID.
- */
- $termlink = apply_filters( 'tag_link', $termlink, $term->term_id );
- } elseif ( 'category' == $taxonomy ) {
-
- /**
- * Filter the category link.
- *
- * @since 1.5.0
- * @deprecated 2.5.0 Use 'term_link' instead.
- *
- * @param string $termlink Category link URL.
- * @param int $term_id Term ID.
- */
- $termlink = apply_filters( 'category_link', $termlink, $term->term_id );
- }
-
- /**
- * Filter the term link.
- *
- * @since 2.5.0
- *
- * @param string $termlink Term link URL.
- * @param object $term Term object.
- * @param string $taxonomy Taxonomy slug.
- */
- return apply_filters( 'term_link', $termlink, $term, $taxonomy );
-}
-
-/**
- * Display the taxonomies of a post with available options.
- *
- * This function can be used within the loop to display the taxonomies for a
- * post without specifying the Post ID. You can also use it outside the Loop to
- * display the taxonomies for a specific post.
- *
- * @since 2.5.0
- *
- * @param array $args {
- * Arguments about which post to use and how to format the output. Shares all of the arguments
- * supported by get_the_taxonomies(), in addition to the following.
- *
- * @type int|WP_Post $post Post ID or object to get taxonomies of. Default current post.
- * @type string $before Displays before the taxonomies. Default empty string.
- * @type string $sep Separates each taxonomy. Default is a space.
- * @type string $after Displays after the taxonomies. Default empty string.
- * }
- * @param array $args See {@link get_the_taxonomies()} for a description of arguments and their defaults.
- */
-function the_taxonomies( $args = array() ) {
- $defaults = array(
- 'post' => 0,
- 'before' => '',
- 'sep' => ' ',
- 'after' => '',
- );
-
- $r = wp_parse_args( $args, $defaults );
-
- echo $r['before'] . join( $r['sep'], get_the_taxonomies( $r['post'], $r ) ) . $r['after'];
-}
-
-/**
- * Retrieve all taxonomies associated with a post.
- *
- * This function can be used within the loop. It will also return an array of
- * the taxonomies with links to the taxonomy and name.
- *
- * @since 2.5.0
- *
- * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
- * @param array $args {
- * Optional. Arguments about how to format the list of taxonomies. Default empty array.
- *
- * @type string $template Template for displaying a taxonomy label and list of terms.
- * Default is "Label: Terms."
- * @type string $term_template Template for displaying a single term in the list. Default is the term name
- * linked to its archive.
- * }
- * @return array List of taxonomies.
- */
-function get_the_taxonomies( $post = 0, $args = array() ) {
- $post = get_post( $post );
-
- $args = wp_parse_args( $args, array(
- /* translators: %s: taxonomy label, %l: list of terms formatted as per $term_template */
- 'template' => __( '%s: %l.' ),
- 'term_template' => '<a href="%1$s">%2$s</a>',
- ) );
-
- $taxonomies = array();
-
- if ( ! $post ) {
- return $taxonomies;
- }
-
- foreach ( get_object_taxonomies( $post ) as $taxonomy ) {
- $t = (array) get_taxonomy( $taxonomy );
- if ( empty( $t['label'] ) ) {
- $t['label'] = $taxonomy;
- }
- if ( empty( $t['args'] ) ) {
- $t['args'] = array();
- }
- if ( empty( $t['template'] ) ) {
- $t['template'] = $args['template'];
- }
- if ( empty( $t['term_template'] ) ) {
- $t['term_template'] = $args['term_template'];
- }
-
- $terms = get_object_term_cache( $post->ID, $taxonomy );
- if ( false === $terms ) {
- $terms = wp_get_object_terms( $post->ID, $taxonomy, $t['args'] );
- }
- $links = array();
-
- foreach ( $terms as $term ) {
- $links[] = wp_sprintf( $t['term_template'], esc_attr( get_term_link( $term ) ), $term->name );
- }
- if ( $links ) {
- $taxonomies[$taxonomy] = wp_sprintf( $t['template'], $t['label'], $links, $terms );
- }
- }
- return $taxonomies;
-}
-
-/**
- * Retrieve all taxonomies of a post with just the names.
- *
- * @since 2.5.0
- *
- * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
- * @return array
- */
-function get_post_taxonomies( $post = 0 ) {
- $post = get_post( $post );
-
- return get_object_taxonomies($post);
-}
-
-/**
- * Determine if the given object is associated with any of the given terms.
- *
- * The given terms are checked against the object's terms' term_ids, names and slugs.
- * Terms given as integers will only be checked against the object's terms' term_ids.
- * If no terms are given, determines if object is associated with any terms in the given taxonomy.
- *
- * @since 2.7.0
- *
- * @param int $object_id ID of the object (post ID, link ID, ...).
- * @param string $taxonomy Single taxonomy name.
- * @param int|string|array $terms Optional. Term term_id, name, slug or array of said. Default null.
- * @return bool|WP_Error WP_Error on input error.
- */
-function is_object_in_term( $object_id, $taxonomy, $terms = null ) {
- if ( !$object_id = (int) $object_id )
- return new WP_Error( 'invalid_object', __( 'Invalid object ID' ) );
-
- $object_terms = get_object_term_cache( $object_id, $taxonomy );
- if ( false === $object_terms ) {
- $object_terms = wp_get_object_terms( $object_id, $taxonomy, array( 'update_term_meta_cache' => false ) );
- wp_cache_set( $object_id, $object_terms, "{$taxonomy}_relationships" );
- }
-
- if ( is_wp_error( $object_terms ) )
- return $object_terms;
- if ( empty( $object_terms ) )
- return false;
- if ( empty( $terms ) )
- return ( !empty( $object_terms ) );
-
- $terms = (array) $terms;
-
- if ( $ints = array_filter( $terms, 'is_int' ) )
- $strs = array_diff( $terms, $ints );
- else
- $strs =& $terms;
-
- foreach ( $object_terms as $object_term ) {
- // If term is an int, check against term_ids only.
- if ( $ints && in_array( $object_term->term_id, $ints ) ) {
- return true;
- }
-
- if ( $strs ) {
- // Only check numeric strings against term_id, to avoid false matches due to type juggling.
- $numeric_strs = array_map( 'intval', array_filter( $strs, 'is_numeric' ) );
- if ( in_array( $object_term->term_id, $numeric_strs, true ) ) {
- return true;
- }
-
- if ( in_array( $object_term->name, $strs ) ) return true;
- if ( in_array( $object_term->slug, $strs ) ) return true;
- }
- }
-
- return false;
-}
-
-/**
- * Determine if the given object type is associated with the given taxonomy.
- *
- * @since 3.0.0
- *
- * @param string $object_type Object type string.
- * @param string $taxonomy Single taxonomy name.
- * @return bool True if object is associated with the taxonomy, otherwise false.
- */
-function is_object_in_taxonomy( $object_type, $taxonomy ) {
- $taxonomies = get_object_taxonomies( $object_type );
- if ( empty( $taxonomies ) ) {
- return false;
- }
- return in_array( $taxonomy, $taxonomies );
-}
-
-/**
- * Get an array of ancestor IDs for a given object.
- *
- * @since 3.1.0
- * @since 4.1.0 Introduced the `$resource_type` argument.
- *
- * @param int $object_id Optional. The ID of the object. Default 0.
- * @param string $object_type Optional. The type of object for which we'll be retrieving
- * ancestors. Accepts a post type or a taxonomy name. Default empty.
- * @param string $resource_type Optional. Type of resource $object_type is. Accepts 'post_type'
- * or 'taxonomy'. Default empty.
- * @return array An array of ancestors from lowest to highest in the hierarchy.
- */
-function get_ancestors( $object_id = 0, $object_type = '', $resource_type = '' ) {
- $object_id = (int) $object_id;
-
- $ancestors = array();
-
- if ( empty( $object_id ) ) {
-
- /** This filter is documented in wp-includes/taxonomy-functions.php */
- return apply_filters( 'get_ancestors', $ancestors, $object_id, $object_type, $resource_type );
- }
-
- if ( ! $resource_type ) {
- if ( is_taxonomy_hierarchical( $object_type ) ) {
- $resource_type = 'taxonomy';
- } elseif ( post_type_exists( $object_type ) ) {
- $resource_type = 'post_type';
- }
- }
-
- if ( 'taxonomy' === $resource_type ) {
- $term = get_term($object_id, $object_type);
- while ( ! is_wp_error($term) && ! empty( $term->parent ) && ! in_array( $term->parent, $ancestors ) ) {
- $ancestors[] = (int) $term->parent;
- $term = get_term($term->parent, $object_type);
- }
- } elseif ( 'post_type' === $resource_type ) {
- $ancestors = get_post_ancestors($object_id);
- }
-
- /**
- * Filter a given object's ancestors.
- *
- * @since 3.1.0
- * @since 4.1.1 Introduced the `$resource_type` parameter.
- *
- * @param array $ancestors An array of object ancestors.
- * @param int $object_id Object ID.
- * @param string $object_type Type of object.
- * @param string $resource_type Type of resource $object_type is.
- */
- return apply_filters( 'get_ancestors', $ancestors, $object_id, $object_type, $resource_type );
-}
-
-/**
- * Returns the term's parent's term_ID.
- *
- * @since 3.1.0
- *
- * @param int $term_id Term ID.
- * @param string $taxonomy Taxonomy name.
- * @return int|false False on error.
- */
-function wp_get_term_taxonomy_parent_id( $term_id, $taxonomy ) {
- $term = get_term( $term_id, $taxonomy );
- if ( ! $term || is_wp_error( $term ) ) {
- return false;
- }
- return (int) $term->parent;
-}
-
-/**
- * Checks the given subset of the term hierarchy for hierarchy loops.
- * Prevents loops from forming and breaks those that it finds.
- *
- * Attached to the {@see 'wp_update_term_parent'} filter.
- *
- * @since 3.1.0
- *
- * @param int $parent `term_id` of the parent for the term we're checking.
- * @param int $term_id The term we're checking.
- * @param string $taxonomy The taxonomy of the term we're checking.
- *
- * @return int The new parent for the term.
- */
-function wp_check_term_hierarchy_for_loops( $parent, $term_id, $taxonomy ) {
- // Nothing fancy here - bail
- if ( !$parent )
- return 0;
-
- // Can't be its own parent.
- if ( $parent == $term_id )
- return 0;
-
- // Now look for larger loops.
- if ( !$loop = wp_find_hierarchy_loop( 'wp_get_term_taxonomy_parent_id', $term_id, $parent, array( $taxonomy ) ) )
- return $parent; // No loop
-
- // Setting $parent to the given value causes a loop.
- if ( isset( $loop[$term_id] ) )
- return 0;
-
- // There's a loop, but it doesn't contain $term_id. Break the loop.
- foreach ( array_keys( $loop ) as $loop_member )
- wp_update_term( $loop_member, $taxonomy, array( 'parent' => 0 ) );
-
- return $parent;
-}
</del></span></pre></div>
<a id="trunksrcwpincludestaxonomyphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/taxonomy.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/taxonomy.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/taxonomy.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,17 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Core Taxonomy API
- *
- * @package WordPress
- * @subpackage Taxonomy
- * @since 2.3.0
- */
-
-/** Core taxonomy functionality */
-require_once( ABSPATH . WPINC . '/taxonomy-functions.php' );
-
-/** WP_Term class */
-require_once( ABSPATH . WPINC . '/class-wp-term.php' );
-
-/** WP_Tax_Query class */
-require_once( ABSPATH . WPINC . '/class-wp-tax-query.php' );
</del></span></pre></div>
<a id="trunksrcwpincludestaxonomyphpfromrev35712trunksrcwpincludestaxonomyfunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/taxonomy.php (from rev 35712, trunk/src/wp-includes/taxonomy-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/taxonomy.php (rev 0)
+++ trunk/src/wp-includes/taxonomy.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,4691 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Core Taxonomy API
+ *
+ * @package WordPress
+ * @subpackage Taxonomy
+ */
+
+//
+// Taxonomy Registration
+//
+
+/**
+ * Creates the initial taxonomies.
+ *
+ * This function fires twice: in wp-settings.php before plugins are loaded (for
+ * backwards compatibility reasons), and again on the {@see 'init'} action. We must
+ * avoid registering rewrite rules before the {@see 'init'} action.
+ *
+ * @since 2.8.0
+ *
+ * @global WP_Rewrite $wp_rewrite The WordPress rewrite class.
+ */
+function create_initial_taxonomies() {
+ global $wp_rewrite;
+
+ if ( ! did_action( 'init' ) ) {
+ $rewrite = array( 'category' => false, 'post_tag' => false, 'post_format' => false );
+ } else {
+
+ /**
+ * Filter the post formats rewrite base.
+ *
+ * @since 3.1.0
+ *
+ * @param string $context Context of the rewrite base. Default 'type'.
+ */
+ $post_format_base = apply_filters( 'post_format_rewrite_base', 'type' );
+ $rewrite = array(
+ 'category' => array(
+ 'hierarchical' => true,
+ 'slug' => get_option('category_base') ? get_option('category_base') : 'category',
+ 'with_front' => ! get_option('category_base') || $wp_rewrite->using_index_permalinks(),
+ 'ep_mask' => EP_CATEGORIES,
+ ),
+ 'post_tag' => array(
+ 'hierarchical' => false,
+ 'slug' => get_option('tag_base') ? get_option('tag_base') : 'tag',
+ 'with_front' => ! get_option('tag_base') || $wp_rewrite->using_index_permalinks(),
+ 'ep_mask' => EP_TAGS,
+ ),
+ 'post_format' => $post_format_base ? array( 'slug' => $post_format_base ) : false,
+ );
+ }
+
+ register_taxonomy( 'category', 'post', array(
+ 'hierarchical' => true,
+ 'query_var' => 'category_name',
+ 'rewrite' => $rewrite['category'],
+ 'public' => true,
+ 'show_ui' => true,
+ 'show_admin_column' => true,
+ '_builtin' => true,
+ ) );
+
+ register_taxonomy( 'post_tag', 'post', array(
+ 'hierarchical' => false,
+ 'query_var' => 'tag',
+ 'rewrite' => $rewrite['post_tag'],
+ 'public' => true,
+ 'show_ui' => true,
+ 'show_admin_column' => true,
+ '_builtin' => true,
+ ) );
+
+ register_taxonomy( 'nav_menu', 'nav_menu_item', array(
+ 'public' => false,
+ 'hierarchical' => false,
+ 'labels' => array(
+ 'name' => __( 'Navigation Menus' ),
+ 'singular_name' => __( 'Navigation Menu' ),
+ ),
+ 'query_var' => false,
+ 'rewrite' => false,
+ 'show_ui' => false,
+ '_builtin' => true,
+ 'show_in_nav_menus' => false,
+ ) );
+
+ register_taxonomy( 'link_category', 'link', array(
+ 'hierarchical' => false,
+ 'labels' => array(
+ 'name' => __( 'Link Categories' ),
+ 'singular_name' => __( 'Link Category' ),
+ 'search_items' => __( 'Search Link Categories' ),
+ 'popular_items' => null,
+ 'all_items' => __( 'All Link Categories' ),
+ 'edit_item' => __( 'Edit Link Category' ),
+ 'update_item' => __( 'Update Link Category' ),
+ 'add_new_item' => __( 'Add New Link Category' ),
+ 'new_item_name' => __( 'New Link Category Name' ),
+ 'separate_items_with_commas' => null,
+ 'add_or_remove_items' => null,
+ 'choose_from_most_used' => null,
+ ),
+ 'capabilities' => array(
+ 'manage_terms' => 'manage_links',
+ 'edit_terms' => 'manage_links',
+ 'delete_terms' => 'manage_links',
+ 'assign_terms' => 'manage_links',
+ ),
+ 'query_var' => false,
+ 'rewrite' => false,
+ 'public' => false,
+ 'show_ui' => true,
+ '_builtin' => true,
+ ) );
+
+ register_taxonomy( 'post_format', 'post', array(
+ 'public' => true,
+ 'hierarchical' => false,
+ 'labels' => array(
+ 'name' => _x( 'Format', 'post format' ),
+ 'singular_name' => _x( 'Format', 'post format' ),
+ ),
+ 'query_var' => true,
+ 'rewrite' => $rewrite['post_format'],
+ 'show_ui' => false,
+ '_builtin' => true,
+ 'show_in_nav_menus' => current_theme_supports( 'post-formats' ),
+ ) );
+}
+
+/**
+ * Retrieves a list of registered taxonomy names or objects.
+ *
+ * @since 3.0.0
+ *
+ * @global array $wp_taxonomies The registered taxonomies.
+ *
+ * @param array $args Optional. An array of `key => value` arguments to match against the taxonomy objects.
+ * Default empty array.
+ * @param string $output Optional. The type of output to return in the array. Accepts either taxonomy 'names'
+ * or 'objects'. Default 'names'.
+ * @param string $operator Optional. The logical operation to perform. Accepts 'and' or 'or'. 'or' means only
+ * one element from the array needs to match; 'and' means all elements must match.
+ * Default 'and'.
+ * @return array A list of taxonomy names or objects.
+ */
+function get_taxonomies( $args = array(), $output = 'names', $operator = 'and' ) {
+ global $wp_taxonomies;
+
+ $field = ('names' == $output) ? 'name' : false;
+
+ return wp_filter_object_list($wp_taxonomies, $args, $operator, $field);
+}
+
+/**
+ * Return all of the taxonomy names that are of $object_type.
+ *
+ * It appears that this function can be used to find all of the names inside of
+ * $wp_taxonomies global variable.
+ *
+ * `<?php $taxonomies = get_object_taxonomies('post'); ?>` Should
+ * result in `Array( 'category', 'post_tag' )`
+ *
+ * @since 2.3.0
+ *
+ * @global array $wp_taxonomies The registered taxonomies.
+ *
+ * @param array|string|WP_Post $object Name of the type of taxonomy object, or an object (row from posts)
+ * @param string $output Optional. The type of output to return in the array. Accepts either
+ * taxonomy 'names' or 'objects'. Default 'names'.
+ * @return array The names of all taxonomy of $object_type.
+ */
+function get_object_taxonomies( $object, $output = 'names' ) {
+ global $wp_taxonomies;
+
+ if ( is_object($object) ) {
+ if ( $object->post_type == 'attachment' )
+ return get_attachment_taxonomies($object);
+ $object = $object->post_type;
+ }
+
+ $object = (array) $object;
+
+ $taxonomies = array();
+ foreach ( (array) $wp_taxonomies as $tax_name => $tax_obj ) {
+ if ( array_intersect($object, (array) $tax_obj->object_type) ) {
+ if ( 'names' == $output )
+ $taxonomies[] = $tax_name;
+ else
+ $taxonomies[ $tax_name ] = $tax_obj;
+ }
+ }
+
+ return $taxonomies;
+}
+
+/**
+ * Retrieves the taxonomy object of $taxonomy.
+ *
+ * The get_taxonomy function will first check that the parameter string given
+ * is a taxonomy object and if it is, it will return it.
+ *
+ * @since 2.3.0
+ *
+ * @global array $wp_taxonomies The registered taxonomies.
+ *
+ * @param string $taxonomy Name of taxonomy object to return.
+ * @return object|false The Taxonomy Object or false if $taxonomy doesn't exist.
+ */
+function get_taxonomy( $taxonomy ) {
+ global $wp_taxonomies;
+
+ if ( ! taxonomy_exists( $taxonomy ) )
+ return false;
+
+ return $wp_taxonomies[$taxonomy];
+}
+
+/**
+ * Checks that the taxonomy name exists.
+ *
+ * Formerly is_taxonomy(), introduced in 2.3.0.
+ *
+ * @since 3.0.0
+ *
+ * @global array $wp_taxonomies The registered taxonomies.
+ *
+ * @param string $taxonomy Name of taxonomy object.
+ * @return bool Whether the taxonomy exists.
+ */
+function taxonomy_exists( $taxonomy ) {
+ global $wp_taxonomies;
+
+ return isset( $wp_taxonomies[$taxonomy] );
+}
+
+/**
+ * Whether the taxonomy object is hierarchical.
+ *
+ * Checks to make sure that the taxonomy is an object first. Then Gets the
+ * object, and finally returns the hierarchical value in the object.
+ *
+ * A false return value might also mean that the taxonomy does not exist.
+ *
+ * @since 2.3.0
+ *
+ * @param string $taxonomy Name of taxonomy object.
+ * @return bool Whether the taxonomy is hierarchical.
+ */
+function is_taxonomy_hierarchical($taxonomy) {
+ if ( ! taxonomy_exists($taxonomy) )
+ return false;
+
+ $taxonomy = get_taxonomy($taxonomy);
+ return $taxonomy->hierarchical;
+}
+
+/**
+ * Creates or modifies a taxonomy object.
+ *
+ * Note: Do not use before the {@see 'init'} hook.
+ *
+ * A simple function for creating or modifying a taxonomy object based on the
+ * parameters given. The function will accept an array (third optional
+ * parameter), along with strings for the taxonomy name and another string for
+ * the object type.
+ *
+ * @since 2.3.0
+ * @since 4.2.0 Introduced `show_in_quick_edit` argument.
+ * @since 4.4.0 The `show_ui` argument is now enforced on the term editing screen.
+ * @since 4.4.0 The `public` argument now controls whether the taxonomy can be queried on the front-end.
+ *
+ * @global array $wp_taxonomies Registered taxonomies.
+ * @global WP $wp WP instance.
+ *
+ * @param string $taxonomy Taxonomy key, must not exceed 32 characters.
+ * @param array|string $object_type Name of the object type for the taxonomy object.
+ * @param array|string $args {
+ * Optional. Array or query string of arguments for registering a taxonomy.
+ *
+ * @type string $label Name of the taxonomy shown in the menu. Usually plural. If not set,
+ * `$labels['name']` will be used.
+ * @type array $labels An array of labels for this taxonomy. By default, Tag labels are used for
+ * non-hierarchical taxonmies, and Category labels are used for hierarchical
+ * taxonomies. See accepted values in get_taxonomy_labels().
+ * Default empty array.
+ * @type string $description A short descriptive summary of what the taxonomy is for. Default empty.
+ * @type bool $public Whether the taxonomy is publicly queryable. Default true.
+ * @type bool $hierarchical Whether the taxonomy is hierarchical. Default false.
+ * @type bool $show_ui Whether to generate and allow a UI for managing terms in this taxonomy in
+ * the admin. If not set, the default is inherited from `$public`
+ * (default true).
+ * @type bool $show_in_menu Whether to show the taxonomy in the admin menu. If true, the taxonomy is
+ * shown as a submenu of the object type menu. If false, no menu is shown.
+ * `$show_ui` must be true. If not set, default is inherited from `$show_ui`
+ * (default true).
+ * @type bool $show_in_nav_menus Makes this taxonomy available for selection in navigation menus. If not
+ * set, the default is inherited from `$public` (default true).
+ * @type bool $show_tagcloud Whether to list the taxonomy in the Tag Cloud Widget controls. If not set,
+ * the default is inherited from `$show_ui` (default true).
+ * @type bool $show_in_quick_edit Whether to show the taxonomy in the quick/bulk edit panel. It not set,
+ * the default is inherited from `$show_ui` (default true).
+ * @type bool $show_admin_column Whether to display a column for the taxonomy on its post type listing
+ * screens. Default false.
+ * @type bool|callable $meta_box_cb Provide a callback function for the meta box display. If not set,
+ * post_categories_meta_box() is used for hierarchical taxonomies, and
+ * post_tags_meta_box() is used for non-hierarchical. If false, no meta
+ * box is shown.
+ * @type array $capabilities {
+ * Array of capabilities for this taxonomy.
+ *
+ * @type string $manage_terms Default 'manage_categories'.
+ * @type string $edit_terms Default 'manage_categories'.
+ * @type string $delete_terms Default 'manage_categories'.
+ * @type string $assign_terms Default 'edit_posts'.
+ * }
+ * @type bool|array $rewrite {
+ * Triggers the handling of rewrites for this taxonomy. Default true, using $taxonomy as slug. To prevent
+ * rewrite, set to false. To specify rewrite rules, an array can be passed with any of these keys:
+ *
+ * @type string $slug Customize the permastruct slug. Default `$taxonomy` key.
+ * @type bool $with_front Should the permastruct be prepended with WP_Rewrite::$front. Default true.
+ * @type bool $hierarchical Either hierarchical rewrite tag or not. Default false.
+ * @type int $ep_mask Assign an endpoint mask. Default `EP_NONE`.
+ * }
+ * @type string $query_var Sets the query var key for this taxonomy. Default `$taxonomy` key. If
+ * false, a taxonomy cannot be loaded at `?{query_var}={term_slug}`. If a
+ * string, the query `?{query_var}={term_slug}` will be valid.
+ * @type callable $update_count_callback Works much like a hook, in that it will be called when the count is
+ * updated. Default _update_post_term_count() for taxonomies attached
+ * to post types, which confirms that the objects are published before
+ * counting them. Default _update_generic_term_count() for taxonomies
+ * attached to other object types, such as users.
+ * @type bool $_builtin This taxonomy is a "built-in" taxonomy. INTERNAL USE ONLY!
+ * Default false.
+ * }
+ * @return WP_Error|void WP_Error, if errors.
+ */
+function register_taxonomy( $taxonomy, $object_type, $args = array() ) {
+ global $wp_taxonomies, $wp;
+
+ if ( ! is_array( $wp_taxonomies ) )
+ $wp_taxonomies = array();
+
+ $args = wp_parse_args( $args );
+
+ /**
+ * Filter the arguments for registering a taxonomy.
+ *
+ * @since 4.4.0
+ *
+ * @param array $args Array of arguments for registering a taxonomy.
+ * @param array $object_type Array of names of object types for the taxonomy.
+ * @param string $taxonomy Taxonomy key.
+ */
+ $args = apply_filters( 'register_taxonomy_args', $args, $taxonomy, (array) $object_type );
+
+ $defaults = array(
+ 'labels' => array(),
+ 'description' => '',
+ 'public' => true,
+ 'hierarchical' => false,
+ 'show_ui' => null,
+ 'show_in_menu' => null,
+ 'show_in_nav_menus' => null,
+ 'show_tagcloud' => null,
+ 'show_in_quick_edit' => null,
+ 'show_admin_column' => false,
+ 'meta_box_cb' => null,
+ 'capabilities' => array(),
+ 'rewrite' => true,
+ 'query_var' => $taxonomy,
+ 'update_count_callback' => '',
+ '_builtin' => false,
+ );
+ $args = array_merge( $defaults, $args );
+
+ if ( empty( $taxonomy ) || strlen( $taxonomy ) > 32 ) {
+ _doing_it_wrong( __FUNCTION__, __( 'Taxonomy names must be between 1 and 32 characters in length.' ), '4.2' );
+ return new WP_Error( 'taxonomy_length_invalid', __( 'Taxonomy names must be between 1 and 32 characters in length.' ) );
+ }
+
+ // Non-public taxonomies should not register query vars, except in the admin.
+ if ( false !== $args['query_var'] && ( is_admin() || false !== $args['public'] ) && ! empty( $wp ) ) {
+ if ( true === $args['query_var'] )
+ $args['query_var'] = $taxonomy;
+ else
+ $args['query_var'] = sanitize_title_with_dashes( $args['query_var'] );
+ $wp->add_query_var( $args['query_var'] );
+ }
+
+ if ( false !== $args['rewrite'] && ( is_admin() || '' != get_option( 'permalink_structure' ) ) ) {
+ $args['rewrite'] = wp_parse_args( $args['rewrite'], array(
+ 'with_front' => true,
+ 'hierarchical' => false,
+ 'ep_mask' => EP_NONE,
+ ) );
+
+ if ( empty( $args['rewrite']['slug'] ) )
+ $args['rewrite']['slug'] = sanitize_title_with_dashes( $taxonomy );
+
+ if ( $args['hierarchical'] && $args['rewrite']['hierarchical'] )
+ $tag = '(.+?)';
+ else
+ $tag = '([^/]+)';
+
+ add_rewrite_tag( "%$taxonomy%", $tag, $args['query_var'] ? "{$args['query_var']}=" : "taxonomy=$taxonomy&term=" );
+ add_permastruct( $taxonomy, "{$args['rewrite']['slug']}/%$taxonomy%", $args['rewrite'] );
+ }
+
+ // If not set, default to the setting for public.
+ if ( null === $args['show_ui'] )
+ $args['show_ui'] = $args['public'];
+
+ // If not set, default to the setting for show_ui.
+ if ( null === $args['show_in_menu' ] || ! $args['show_ui'] )
+ $args['show_in_menu' ] = $args['show_ui'];
+
+ // If not set, default to the setting for public.
+ if ( null === $args['show_in_nav_menus'] )
+ $args['show_in_nav_menus'] = $args['public'];
+
+ // If not set, default to the setting for show_ui.
+ if ( null === $args['show_tagcloud'] )
+ $args['show_tagcloud'] = $args['show_ui'];
+
+ // If not set, default to the setting for show_ui.
+ if ( null === $args['show_in_quick_edit'] ) {
+ $args['show_in_quick_edit'] = $args['show_ui'];
+ }
+
+ $default_caps = array(
+ 'manage_terms' => 'manage_categories',
+ 'edit_terms' => 'manage_categories',
+ 'delete_terms' => 'manage_categories',
+ 'assign_terms' => 'edit_posts',
+ );
+ $args['cap'] = (object) array_merge( $default_caps, $args['capabilities'] );
+ unset( $args['capabilities'] );
+
+ $args['name'] = $taxonomy;
+ $args['object_type'] = array_unique( (array) $object_type );
+
+ $args['labels'] = get_taxonomy_labels( (object) $args );
+ $args['label'] = $args['labels']->name;
+
+ // If not set, use the default meta box
+ if ( null === $args['meta_box_cb'] ) {
+ if ( $args['hierarchical'] )
+ $args['meta_box_cb'] = 'post_categories_meta_box';
+ else
+ $args['meta_box_cb'] = 'post_tags_meta_box';
+ }
+
+ $wp_taxonomies[ $taxonomy ] = (object) $args;
+
+ // register callback handling for metabox
+ add_filter( 'wp_ajax_add-' . $taxonomy, '_wp_ajax_add_hierarchical_term' );
+
+ /**
+ * Fires after a taxonomy is registered.
+ *
+ * @since 3.3.0
+ *
+ * @param string $taxonomy Taxonomy slug.
+ * @param array|string $object_type Object type or array of object types.
+ * @param array $args Array of taxonomy registration arguments.
+ */
+ do_action( 'registered_taxonomy', $taxonomy, $object_type, $args );
+}
+
+/**
+ * Builds an object with all taxonomy labels out of a taxonomy object
+ *
+ * Accepted keys of the label array in the taxonomy object:
+ *
+ * - name - general name for the taxonomy, usually plural. The same as and overridden by $tax->label. Default is Tags/Categories
+ * - singular_name - name for one object of this taxonomy. Default is Tag/Category
+ * - search_items - Default is Search Tags/Search Categories
+ * - popular_items - This string isn't used on hierarchical taxonomies. Default is Popular Tags
+ * - all_items - Default is All Tags/All Categories
+ * - parent_item - This string isn't used on non-hierarchical taxonomies. In hierarchical ones the default is Parent Category
+ * - parent_item_colon - The same as `parent_item`, but with colon `:` in the end
+ * - edit_item - Default is Edit Tag/Edit Category
+ * - view_item - Default is View Tag/View Category
+ * - update_item - Default is Update Tag/Update Category
+ * - add_new_item - Default is Add New Tag/Add New Category
+ * - new_item_name - Default is New Tag Name/New Category Name
+ * - separate_items_with_commas - This string isn't used on hierarchical taxonomies. Default is "Separate tags with commas", used in the meta box.
+ * - add_or_remove_items - This string isn't used on hierarchical taxonomies. Default is "Add or remove tags", used in the meta box when JavaScript is disabled.
+ * - choose_from_most_used - This string isn't used on hierarchical taxonomies. Default is "Choose from the most used tags", used in the meta box.
+ * - not_found - Default is "No tags found"/"No categories found", used in the meta box and taxonomy list table.
+ * - no_terms - Default is "No tags"/"No categories", used in the posts and media list tables.
+ * - items_list_navigation - String for the table pagination hidden heading.
+ * - items_list - String for the table hidden heading.
+ *
+ * Above, the first default value is for non-hierarchical taxonomies (like tags) and the second one is for hierarchical taxonomies (like categories).
+ *
+ * @todo Better documentation for the labels array.
+ *
+ * @since 3.0.0
+ * @since 4.3.0 Added the `no_terms` label.
+ * @since 4.4.0 Added the `items_list_navigation` and `items_list` labels.
+ *
+ * @param object $tax Taxonomy object.
+ * @return object object with all the labels as member variables.
+ */
+function get_taxonomy_labels( $tax ) {
+ $tax->labels = (array) $tax->labels;
+
+ if ( isset( $tax->helps ) && empty( $tax->labels['separate_items_with_commas'] ) )
+ $tax->labels['separate_items_with_commas'] = $tax->helps;
+
+ if ( isset( $tax->no_tagcloud ) && empty( $tax->labels['not_found'] ) )
+ $tax->labels['not_found'] = $tax->no_tagcloud;
+
+ $nohier_vs_hier_defaults = array(
+ 'name' => array( _x( 'Tags', 'taxonomy general name' ), _x( 'Categories', 'taxonomy general name' ) ),
+ 'singular_name' => array( _x( 'Tag', 'taxonomy singular name' ), _x( 'Category', 'taxonomy singular name' ) ),
+ 'search_items' => array( __( 'Search Tags' ), __( 'Search Categories' ) ),
+ 'popular_items' => array( __( 'Popular Tags' ), null ),
+ 'all_items' => array( __( 'All Tags' ), __( 'All Categories' ) ),
+ 'parent_item' => array( null, __( 'Parent Category' ) ),
+ 'parent_item_colon' => array( null, __( 'Parent Category:' ) ),
+ 'edit_item' => array( __( 'Edit Tag' ), __( 'Edit Category' ) ),
+ 'view_item' => array( __( 'View Tag' ), __( 'View Category' ) ),
+ 'update_item' => array( __( 'Update Tag' ), __( 'Update Category' ) ),
+ 'add_new_item' => array( __( 'Add New Tag' ), __( 'Add New Category' ) ),
+ 'new_item_name' => array( __( 'New Tag Name' ), __( 'New Category Name' ) ),
+ 'separate_items_with_commas' => array( __( 'Separate tags with commas' ), null ),
+ 'add_or_remove_items' => array( __( 'Add or remove tags' ), null ),
+ 'choose_from_most_used' => array( __( 'Choose from the most used tags' ), null ),
+ 'not_found' => array( __( 'No tags found.' ), __( 'No categories found.' ) ),
+ 'no_terms' => array( __( 'No tags' ), __( 'No categories' ) ),
+ 'items_list_navigation' => array( __( 'Tags list navigation' ), __( 'Categories list navigation' ) ),
+ 'items_list' => array( __( 'Tags list' ), __( 'Categories list' ) ),
+ );
+ $nohier_vs_hier_defaults['menu_name'] = $nohier_vs_hier_defaults['name'];
+
+ $labels = _get_custom_object_labels( $tax, $nohier_vs_hier_defaults );
+
+ $taxonomy = $tax->name;
+
+ $default_labels = clone $labels;
+
+ /**
+ * Filter the labels of a specific taxonomy.
+ *
+ * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug.
+ *
+ * @since 4.4.0
+ *
+ * @see get_taxonomy_labels() for the full list of taxonomy labels.
+ *
+ * @param object $labels Object with labels for the taxonomy as member variables.
+ */
+ $labels = apply_filters( "taxonomy_labels_{$taxonomy}", $labels );
+
+ // Ensure that the filtered labels contain all required default values.
+ $labels = (object) array_merge( (array) $default_labels, (array) $labels );
+
+ return $labels;
+}
+
+/**
+ * Add an already registered taxonomy to an object type.
+ *
+ * @since 3.0.0
+ *
+ * @global array $wp_taxonomies The registered taxonomies.
+ *
+ * @param string $taxonomy Name of taxonomy object.
+ * @param string $object_type Name of the object type.
+ * @return bool True if successful, false if not.
+ */
+function register_taxonomy_for_object_type( $taxonomy, $object_type) {
+ global $wp_taxonomies;
+
+ if ( !isset($wp_taxonomies[$taxonomy]) )
+ return false;
+
+ if ( ! get_post_type_object($object_type) )
+ return false;
+
+ if ( ! in_array( $object_type, $wp_taxonomies[$taxonomy]->object_type ) )
+ $wp_taxonomies[$taxonomy]->object_type[] = $object_type;
+
+ // Filter out empties.
+ $wp_taxonomies[ $taxonomy ]->object_type = array_filter( $wp_taxonomies[ $taxonomy ]->object_type );
+
+ return true;
+}
+
+/**
+ * Remove an already registered taxonomy from an object type.
+ *
+ * @since 3.7.0
+ *
+ * @global array $wp_taxonomies The registered taxonomies.
+ *
+ * @param string $taxonomy Name of taxonomy object.
+ * @param string $object_type Name of the object type.
+ * @return bool True if successful, false if not.
+ */
+function unregister_taxonomy_for_object_type( $taxonomy, $object_type ) {
+ global $wp_taxonomies;
+
+ if ( ! isset( $wp_taxonomies[ $taxonomy ] ) )
+ return false;
+
+ if ( ! get_post_type_object( $object_type ) )
+ return false;
+
+ $key = array_search( $object_type, $wp_taxonomies[ $taxonomy ]->object_type, true );
+ if ( false === $key )
+ return false;
+
+ unset( $wp_taxonomies[ $taxonomy ]->object_type[ $key ] );
+ return true;
+}
+
+//
+// Term API
+//
+
+/**
+ * Retrieve object_ids of valid taxonomy and term.
+ *
+ * The strings of $taxonomies must exist before this function will continue. On
+ * failure of finding a valid taxonomy, it will return an WP_Error class, kind
+ * of like Exceptions in PHP 5, except you can't catch them. Even so, you can
+ * still test for the WP_Error class and get the error message.
+ *
+ * The $terms aren't checked the same as $taxonomies, but still need to exist
+ * for $object_ids to be returned.
+ *
+ * It is possible to change the order that object_ids is returned by either
+ * using PHP sort family functions or using the database by using $args with
+ * either ASC or DESC array. The value should be in the key named 'order'.
+ *
+ * @since 2.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int|array $term_ids Term id or array of term ids of terms that will be used.
+ * @param string|array $taxonomies String of taxonomy name or Array of string values of taxonomy names.
+ * @param array|string $args Change the order of the object_ids, either ASC or DESC.
+ * @return WP_Error|array If the taxonomy does not exist, then WP_Error will be returned. On success.
+ * the array can be empty meaning that there are no $object_ids found or it will return the $object_ids found.
+ */
+function get_objects_in_term( $term_ids, $taxonomies, $args = array() ) {
+ global $wpdb;
+
+ if ( ! is_array( $term_ids ) ) {
+ $term_ids = array( $term_ids );
+ }
+ if ( ! is_array( $taxonomies ) ) {
+ $taxonomies = array( $taxonomies );
+ }
+ foreach ( (array) $taxonomies as $taxonomy ) {
+ if ( ! taxonomy_exists( $taxonomy ) ) {
+ return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
+ }
+ }
+
+ $defaults = array( 'order' => 'ASC' );
+ $args = wp_parse_args( $args, $defaults );
+
+ $order = ( 'desc' == strtolower( $args['order'] ) ) ? 'DESC' : 'ASC';
+
+ $term_ids = array_map('intval', $term_ids );
+
+ $taxonomies = "'" . implode( "', '", $taxonomies ) . "'";
+ $term_ids = "'" . implode( "', '", $term_ids ) . "'";
+
+ $object_ids = $wpdb->get_col("SELECT tr.object_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tt.term_id IN ($term_ids) ORDER BY tr.object_id $order");
+
+ if ( ! $object_ids ){
+ return array();
+ }
+ return $object_ids;
+}
+
+/**
+ * Given a taxonomy query, generates SQL to be appended to a main query.
+ *
+ * @since 3.1.0
+ *
+ * @see WP_Tax_Query
+ *
+ * @param array $tax_query A compact tax query
+ * @param string $primary_table
+ * @param string $primary_id_column
+ * @return array
+ */
+function get_tax_sql( $tax_query, $primary_table, $primary_id_column ) {
+ $tax_query_obj = new WP_Tax_Query( $tax_query );
+ return $tax_query_obj->get_sql( $primary_table, $primary_id_column );
+}
+
+/**
+ * Get all Term data from database by Term ID.
+ *
+ * The usage of the get_term function is to apply filters to a term object. It
+ * is possible to get a term object from the database before applying the
+ * filters.
+ *
+ * $term ID must be part of $taxonomy, to get from the database. Failure, might
+ * be able to be captured by the hooks. Failure would be the same value as $wpdb
+ * returns for the get_row method.
+ *
+ * There are two hooks, one is specifically for each term, named 'get_term', and
+ * the second is for the taxonomy name, 'term_$taxonomy'. Both hooks gets the
+ * term object, and the taxonomy name as parameters. Both hooks are expected to
+ * return a Term object.
+ *
+ * {@see 'get_term'} hook - Takes two parameters the term Object and the taxonomy name.
+ * Must return term object. Used in get_term() as a catch-all filter for every
+ * $term.
+ *
+ * {@see 'get_$taxonomy'} hook - Takes two parameters the term Object and the taxonomy
+ * name. Must return term object. $taxonomy will be the taxonomy name, so for
+ * example, if 'category', it would be 'get_category' as the filter name. Useful
+ * for custom taxonomies or plugging into default taxonomies.
+ *
+ * @todo Better formatting for DocBlock
+ *
+ * @since 2.3.0
+ * @since 4.4.0 Converted to return a WP_Term object if `$output` is `OBJECT`.
+ * The `$taxonomy` parameter was made optional.
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ * @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param.
+ *
+ * @param int|WP_Term|object $term If integer, term data will be fetched from the database, or from the cache if
+ * available. If stdClass object (as in the results of a database query), will apply
+ * filters and return a `WP_Term` object corresponding to the `$term` data. If `WP_Term`,
+ * will return `$term`.
+ * @param string $taxonomy Optional. Taxonomy name that $term is part of.
+ * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N
+ * @param string $filter Optional, default is raw or no WordPress defined filter will applied.
+ * @return mixed Type corresponding to `$output` on success or null on failure. When `$output` is `OBJECT`,
+ * a WP_Term instance is returned. If taxonomy does not exist then WP_Error will be returned.
+ */
+function get_term( $term, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) {
+ if ( empty( $term ) ) {
+ return new WP_Error( 'invalid_term', __( 'Empty Term' ) );
+ }
+
+ if ( $taxonomy && ! taxonomy_exists( $taxonomy ) ) {
+ return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
+ }
+
+ if ( $term instanceof WP_Term ) {
+ $_term = $term;
+ } elseif ( is_object( $term ) ) {
+ if ( empty( $term->filter ) || 'raw' === $term->filter ) {
+ $_term = sanitize_term( $term, $taxonomy, 'raw' );
+ $_term = new WP_Term( $_term );
+ } else {
+ $_term = WP_Term::get_instance( $term->term_id );
+ }
+ } else {
+ $_term = WP_Term::get_instance( $term, $taxonomy );
+ }
+
+ if ( is_wp_error( $_term ) ) {
+ return $_term;
+ } elseif ( ! $_term ) {
+ return null;
+ }
+
+ /**
+ * Filter a term.
+ *
+ * @since 2.3.0
+ * @since 4.4.0 `$_term` can now also be a WP_Term object.
+ *
+ * @param int|WP_Term $_term Term object or ID.
+ * @param string $taxonomy The taxonomy slug.
+ */
+ $_term = apply_filters( 'get_term', $_term, $taxonomy );
+
+ /**
+ * Filter a taxonomy.
+ *
+ * The dynamic portion of the filter name, `$taxonomy`, refers
+ * to the taxonomy slug.
+ *
+ * @since 2.3.0
+ * @since 4.4.0 `$_term` can now also be a WP_Term object.
+ *
+ * @param int|WP_Term $_term Term object or ID.
+ * @param string $taxonomy The taxonomy slug.
+ */
+ $_term = apply_filters( "get_$taxonomy", $_term, $taxonomy );
+
+ // Sanitize term, according to the specified filter.
+ $_term->filter( $filter );
+
+ if ( $output == ARRAY_A ) {
+ return $_term->to_array();
+ } elseif ( $output == ARRAY_N ) {
+ return array_values( $_term->to_array() );
+ }
+
+ return $_term;
+}
+
+/**
+ * Get all Term data from database by Term field and data.
+ *
+ * Warning: $value is not escaped for 'name' $field. You must do it yourself, if
+ * required.
+ *
+ * The default $field is 'id', therefore it is possible to also use null for
+ * field, but not recommended that you do so.
+ *
+ * If $value does not exist, the return value will be false. If $taxonomy exists
+ * and $field and $value combinations exist, the Term will be returned.
+ *
+ * @todo Better formatting for DocBlock.
+ *
+ * @since 2.3.0
+ * @since 4.4.0 `$taxonomy` is optional if `$field` is 'term_taxonomy_id'. Converted to return
+ * a WP_Term object if `$output` is `OBJECT`.
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ * @see sanitize_term_field() The $context param lists the available values for get_term_by() $filter param.
+ *
+ * @param string $field Either 'slug', 'name', 'id' (term_id), or 'term_taxonomy_id'
+ * @param string|int $value Search for this term value
+ * @param string $taxonomy Taxonomy name. Optional, if `$field` is 'term_taxonomy_id'.
+ * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N
+ * @param string $filter Optional, default is raw or no WordPress defined filter will applied.
+ * @return WP_Term|bool WP_Term instance on success. Will return false if `$taxonomy` does not exist
+ * or `$term` was not found.
+ */
+function get_term_by( $field, $value, $taxonomy = '', $output = OBJECT, $filter = 'raw' ) {
+ global $wpdb;
+
+ // 'term_taxonomy_id' lookups don't require taxonomy checks.
+ if ( 'term_taxonomy_id' !== $field && ! taxonomy_exists( $taxonomy ) ) {
+ return false;
+ }
+
+ $tax_clause = $wpdb->prepare( "AND tt.taxonomy = %s", $taxonomy );
+
+ if ( 'slug' == $field ) {
+ $_field = 't.slug';
+ $value = sanitize_title($value);
+ if ( empty($value) )
+ return false;
+ } elseif ( 'name' == $field ) {
+ // Assume already escaped
+ $value = wp_unslash($value);
+ $_field = 't.name';
+ } elseif ( 'term_taxonomy_id' == $field ) {
+ $value = (int) $value;
+ $_field = 'tt.term_taxonomy_id';
+
+ // No `taxonomy` clause when searching by 'term_taxonomy_id'.
+ $tax_clause = '';
+ } else {
+ $term = get_term( (int) $value, $taxonomy, $output, $filter );
+ if ( is_wp_error( $term ) || is_null( $term ) ) {
+ $term = false;
+ }
+ return $term;
+ }
+
+ $term = $wpdb->get_row( $wpdb->prepare( "SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE $_field = %s $tax_clause LIMIT 1", $value ) );
+ if ( ! $term )
+ return false;
+
+ // In the case of 'term_taxonomy_id', override the provided `$taxonomy` with whatever we find in the db.
+ if ( 'term_taxonomy_id' === $field ) {
+ $taxonomy = $term->taxonomy;
+ }
+
+ wp_cache_add( $term->term_id, $term, 'terms' );
+
+ return get_term( $term, $taxonomy, $output, $filter );
+}
+
+/**
+ * Merge all term children into a single array of their IDs.
+ *
+ * This recursive function will merge all of the children of $term into the same
+ * array of term IDs. Only useful for taxonomies which are hierarchical.
+ *
+ * Will return an empty array if $term does not exist in $taxonomy.
+ *
+ * @since 2.3.0
+ *
+ * @param string $term_id ID of Term to get children.
+ * @param string $taxonomy Taxonomy Name.
+ * @return array|WP_Error List of Term IDs. WP_Error returned if `$taxonomy` does not exist.
+ */
+function get_term_children( $term_id, $taxonomy ) {
+ if ( ! taxonomy_exists($taxonomy) )
+ return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
+
+ $term_id = intval( $term_id );
+
+ $terms = _get_term_hierarchy($taxonomy);
+
+ if ( ! isset($terms[$term_id]) )
+ return array();
+
+ $children = $terms[$term_id];
+
+ foreach ( (array) $terms[$term_id] as $child ) {
+ if ( $term_id == $child ) {
+ continue;
+ }
+
+ if ( isset($terms[$child]) )
+ $children = array_merge($children, get_term_children($child, $taxonomy));
+ }
+
+ return $children;
+}
+
+/**
+ * Get sanitized Term field.
+ *
+ * The function is for contextual reasons and for simplicity of usage.
+ *
+ * @since 2.3.0
+ * @since 4.4.0 The `$taxonomy` parameter was made optional. `$term` can also now accept a WP_Term object.
+ *
+ * @see sanitize_term_field()
+ *
+ * @param string $field Term field to fetch.
+ * @param int|WP_Term $term Term ID or object.
+ * @param string $taxonomy Optional. Taxonomy Name. Default empty.
+ * @param string $context Optional, default is display. Look at sanitize_term_field() for available options.
+ * @return string|int|null|WP_Error Will return an empty string if $term is not an object or if $field is not set in $term.
+ */
+function get_term_field( $field, $term, $taxonomy = '', $context = 'display' ) {
+ $term = get_term( $term, $taxonomy );
+ if ( is_wp_error($term) )
+ return $term;
+
+ if ( !is_object($term) )
+ return '';
+
+ if ( !isset($term->$field) )
+ return '';
+
+ return sanitize_term_field( $field, $term->$field, $term->term_id, $term->taxonomy, $context );
+}
+
+/**
+ * Sanitizes Term for editing.
+ *
+ * Return value is sanitize_term() and usage is for sanitizing the term for
+ * editing. Function is for contextual and simplicity.
+ *
+ * @since 2.3.0
+ *
+ * @param int|object $id Term ID or object.
+ * @param string $taxonomy Taxonomy name.
+ * @return string|int|null|WP_Error Will return empty string if $term is not an object.
+ */
+function get_term_to_edit( $id, $taxonomy ) {
+ $term = get_term( $id, $taxonomy );
+
+ if ( is_wp_error($term) )
+ return $term;
+
+ if ( !is_object($term) )
+ return '';
+
+ return sanitize_term($term, $taxonomy, 'edit');
+}
+
+/**
+ * Retrieve the terms in a given taxonomy or list of taxonomies.
+ *
+ * You can fully inject any customizations to the query before it is sent, as
+ * well as control the output with a filter.
+ *
+ * The {@see 'get_terms'} filter will be called when the cache has the term and will
+ * pass the found term along with the array of $taxonomies and array of $args.
+ * This filter is also called before the array of terms is passed and will pass
+ * the array of terms, along with the $taxonomies and $args.
+ *
+ * The {@see 'list_terms_exclusions'} filter passes the compiled exclusions along with
+ * the $args.
+ *
+ * The {@see 'get_terms_orderby'} filter passes the `ORDER BY` clause for the query
+ * along with the $args array.
+ *
+ * @since 2.3.0
+ * @since 4.2.0 Introduced 'name' and 'childless' parameters.
+ * @since 4.4.0 Introduced the ability to pass 'term_id' as an alias of 'id' for the `orderby` parameter.
+ * Introduced the 'meta_query' and 'update_term_meta_cache' parameters. Converted to return
+ * a list of WP_Term objects.
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ * @global array $wp_filter
+ *
+ * @param string|array $taxonomies Taxonomy name or list of Taxonomy names.
+ * @param array|string $args {
+ * Optional. Array or string of arguments to get terms.
+ *
+ * @type string $orderby Field(s) to order terms by. Accepts term fields ('name', 'slug',
+ * 'term_group', 'term_id', 'id', 'description'), 'count' for term
+ * taxonomy count, 'include' to match the 'order' of the $include param,
+ * or 'none' to skip ORDER BY. Defaults to 'name'.
+ * @type string $order Whether to order terms in ascending or descending order.
+ * Accepts 'ASC' (ascending) or 'DESC' (descending).
+ * Default 'ASC'.
+ * @type bool|int $hide_empty Whether to hide terms not assigned to any posts. Accepts
+ * 1|true or 0|false. Default 1|true.
+ * @type array|string $include Array or comma/space-separated string of term ids to include.
+ * Default empty array.
+ * @type array|string $exclude Array or comma/space-separated string of term ids to exclude.
+ * If $include is non-empty, $exclude is ignored.
+ * Default empty array.
+ * @type array|string $exclude_tree Array or comma/space-separated string of term ids to exclude
+ * along with all of their descendant terms. If $include is
+ * non-empty, $exclude_tree is ignored. Default empty array.
+ * @type int|string $number Maximum number of terms to return. Accepts ''|0 (all) or any
+ * positive number. Default ''|0 (all).
+ * @type int $offset The number by which to offset the terms query. Default empty.
+ * @type string $fields Term fields to query for. Accepts 'all' (returns an array of complete
+ * term objects), 'ids' (returns an array of ids), 'id=>parent' (returns
+ * an associative array with ids as keys, parent term IDs as values),
+ * 'names' (returns an array of term names), 'count' (returns the number
+ * of matching terms), 'id=>name' (returns an associative array with ids
+ * as keys, term names as values), or 'id=>slug' (returns an associative
+ * array with ids as keys, term slugs as values). Default 'all'.
+ * @type string|array $name Optional. Name or array of names to return term(s) for. Default empty.
+ * @type string|array $slug Optional. Slug or array of slugs to return term(s) for. Default empty.
+ * @type bool $hierarchical Whether to include terms that have non-empty descendants (even
+ * if $hide_empty is set to true). Default true.
+ * @type string $search Search criteria to match terms. Will be SQL-formatted with
+ * wildcards before and after. Default empty.
+ * @type string $name__like Retrieve terms with criteria by which a term is LIKE $name__like.
+ * Default empty.
+ * @type string $description__like Retrieve terms where the description is LIKE $description__like.
+ * Default empty.
+ * @type bool $pad_counts Whether to pad the quantity of a term's children in the quantity
+ * of each term's "count" object variable. Default false.
+ * @type string $get Whether to return terms regardless of ancestry or whether the terms
+ * are empty. Accepts 'all' or empty (disabled). Default empty.
+ * @type int $child_of Term ID to retrieve child terms of. If multiple taxonomies
+ * are passed, $child_of is ignored. Default 0.
+ * @type int|string $parent Parent term ID to retrieve direct-child terms of. Default empty.
+ * @type bool $childless True to limit results to terms that have no children. This parameter
+ * has no effect on non-hierarchical taxonomies. Default false.
+ * @type string $cache_domain Unique cache key to be produced when this query is stored in an
+ * object cache. Default is 'core'.
+ * @type bool $update_term_meta_cache Whether to prime meta caches for matched terms. Default true.
+ * @type array $meta_query Meta query clauses to limit retrieved terms by.
+ * See `WP_Meta_Query`. Default empty.
+ * }
+ * @return array|int|WP_Error List of WP_Term instances and their children. Will return WP_Error, if any of $taxonomies
+ * do not exist.
+ */
+function get_terms( $taxonomies, $args = '' ) {
+ global $wpdb;
+ $empty_array = array();
+
+ $single_taxonomy = ! is_array( $taxonomies ) || 1 === count( $taxonomies );
+ if ( ! is_array( $taxonomies ) ) {
+ $taxonomies = array( $taxonomies );
+ }
+
+ foreach ( $taxonomies as $taxonomy ) {
+ if ( ! taxonomy_exists($taxonomy) ) {
+ return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
+ }
+ }
+
+ $defaults = array(
+ 'orderby' => 'name',
+ 'order' => 'ASC',
+ 'hide_empty' => true,
+ 'include' => array(),
+ 'exclude' => array(),
+ 'exclude_tree' => array(),
+ 'number' => '',
+ 'offset' => '',
+ 'fields' => 'all',
+ 'name' => '',
+ 'slug' => '',
+ 'hierarchical' => true,
+ 'search' => '',
+ 'name__like' => '',
+ 'description__like' => '',
+ 'pad_counts' => false,
+ 'get' => '',
+ 'child_of' => 0,
+ 'parent' => '',
+ 'childless' => false,
+ 'cache_domain' => 'core',
+ 'update_term_meta_cache' => true,
+ 'meta_query' => ''
+ );
+
+ /**
+ * Filter the terms query default arguments.
+ *
+ * Use 'get_terms_args' to filter the passed arguments.
+ *
+ * @since 4.4.0
+ *
+ * @param array $defaults An array of default get_terms() arguments.
+ * @param array $taxonomies An array of taxonomies.
+ */
+ $args = wp_parse_args( $args, apply_filters( 'get_terms_defaults', $defaults, $taxonomies ) );
+
+ $args['number'] = absint( $args['number'] );
+ $args['offset'] = absint( $args['offset'] );
+
+ // Save queries by not crawling the tree in the case of multiple taxes or a flat tax.
+ $has_hierarchical_tax = false;
+ foreach ( $taxonomies as $_tax ) {
+ if ( is_taxonomy_hierarchical( $_tax ) ) {
+ $has_hierarchical_tax = true;
+ }
+ }
+
+ if ( ! $has_hierarchical_tax ) {
+ $args['hierarchical'] = false;
+ $args['pad_counts'] = false;
+ }
+
+ // 'parent' overrides 'child_of'.
+ if ( 0 < intval( $args['parent'] ) ) {
+ $args['child_of'] = false;
+ }
+
+ if ( 'all' == $args['get'] ) {
+ $args['childless'] = false;
+ $args['child_of'] = 0;
+ $args['hide_empty'] = 0;
+ $args['hierarchical'] = false;
+ $args['pad_counts'] = false;
+ }
+
+ /**
+ * Filter the terms query arguments.
+ *
+ * @since 3.1.0
+ *
+ * @param array $args An array of get_terms() arguments.
+ * @param array $taxonomies An array of taxonomies.
+ */
+ $args = apply_filters( 'get_terms_args', $args, $taxonomies );
+
+ // Avoid the query if the queried parent/child_of term has no descendants.
+ $child_of = $args['child_of'];
+ $parent = $args['parent'];
+
+ if ( $child_of ) {
+ $_parent = $child_of;
+ } elseif ( $parent ) {
+ $_parent = $parent;
+ } else {
+ $_parent = false;
+ }
+
+ if ( $_parent ) {
+ $in_hierarchy = false;
+ foreach ( $taxonomies as $_tax ) {
+ $hierarchy = _get_term_hierarchy( $_tax );
+
+ if ( isset( $hierarchy[ $_parent ] ) ) {
+ $in_hierarchy = true;
+ }
+ }
+
+ if ( ! $in_hierarchy ) {
+ return $empty_array;
+ }
+ }
+
+ $_orderby = strtolower( $args['orderby'] );
+ if ( 'count' == $_orderby ) {
+ $orderby = 'tt.count';
+ } elseif ( 'name' == $_orderby ) {
+ $orderby = 't.name';
+ } elseif ( 'slug' == $_orderby ) {
+ $orderby = 't.slug';
+ } elseif ( 'include' == $_orderby && ! empty( $args['include'] ) ) {
+ $include = implode( ',', array_map( 'absint', $args['include'] ) );
+ $orderby = "FIELD( t.term_id, $include )";
+ } elseif ( 'term_group' == $_orderby ) {
+ $orderby = 't.term_group';
+ } elseif ( 'description' == $_orderby ) {
+ $orderby = 'tt.description';
+ } elseif ( 'none' == $_orderby ) {
+ $orderby = '';
+ } elseif ( empty( $_orderby ) || 'id' == $_orderby || 'term_id' === $_orderby ) {
+ $orderby = 't.term_id';
+ } else {
+ $orderby = 't.name';
+ }
+
+ /**
+ * Filter the ORDERBY clause of the terms query.
+ *
+ * @since 2.8.0
+ *
+ * @param string $orderby `ORDERBY` clause of the terms query.
+ * @param array $args An array of terms query arguments.
+ * @param array $taxonomies An array of taxonomies.
+ */
+ $orderby = apply_filters( 'get_terms_orderby', $orderby, $args, $taxonomies );
+
+ $order = strtoupper( $args['order'] );
+ if ( ! empty( $orderby ) ) {
+ $orderby = "ORDER BY $orderby";
+ } else {
+ $order = '';
+ }
+
+ if ( '' !== $order && ! in_array( $order, array( 'ASC', 'DESC' ) ) ) {
+ $order = 'ASC';
+ }
+
+ $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')";
+
+ $exclude = $args['exclude'];
+ $exclude_tree = $args['exclude_tree'];
+ $include = $args['include'];
+
+ $inclusions = '';
+ if ( ! empty( $include ) ) {
+ $exclude = '';
+ $exclude_tree = '';
+ $inclusions = implode( ',', wp_parse_id_list( $include ) );
+ }
+
+ if ( ! empty( $inclusions ) ) {
+ $inclusions = ' AND t.term_id IN ( ' . $inclusions . ' )';
+ $where .= $inclusions;
+ }
+
+ $exclusions = array();
+ if ( ! empty( $exclude_tree ) ) {
+ $exclude_tree = wp_parse_id_list( $exclude_tree );
+ $excluded_children = $exclude_tree;
+ foreach ( $exclude_tree as $extrunk ) {
+ $excluded_children = array_merge(
+ $excluded_children,
+ (array) get_terms( $taxonomies[0], array( 'child_of' => intval( $extrunk ), 'fields' => 'ids', 'hide_empty' => 0 ) )
+ );
+ }
+ $exclusions = array_merge( $excluded_children, $exclusions );
+ }
+
+ if ( ! empty( $exclude ) ) {
+ $exclusions = array_merge( wp_parse_id_list( $exclude ), $exclusions );
+ }
+
+ // 'childless' terms are those without an entry in the flattened term hierarchy.
+ $childless = (bool) $args['childless'];
+ if ( $childless ) {
+ foreach ( $taxonomies as $_tax ) {
+ $term_hierarchy = _get_term_hierarchy( $_tax );
+ $exclusions = array_merge( array_keys( $term_hierarchy ), $exclusions );
+ }
+ }
+
+ if ( ! empty( $exclusions ) ) {
+ $exclusions = ' AND t.term_id NOT IN (' . implode( ',', array_map( 'intval', $exclusions ) ) . ')';
+ } else {
+ $exclusions = '';
+ }
+
+ /**
+ * Filter the terms to exclude from the terms query.
+ *
+ * @since 2.3.0
+ *
+ * @param string $exclusions `NOT IN` clause of the terms query.
+ * @param array $args An array of terms query arguments.
+ * @param array $taxonomies An array of taxonomies.
+ */
+ $exclusions = apply_filters( 'list_terms_exclusions', $exclusions, $args, $taxonomies );
+
+ if ( ! empty( $exclusions ) ) {
+ $where .= $exclusions;
+ }
+
+ if ( ! empty( $args['name'] ) ) {
+ $names = (array) $args['name'];
+ foreach ( $names as &$_name ) {
+ $_name = sanitize_term_field( 'name', $_name, 0, reset( $taxonomies ), 'db' );
+ }
+
+ $where .= " AND t.name IN ('" . implode( "', '", array_map( 'esc_sql', $names ) ) . "')";
+ }
+
+ if ( ! empty( $args['slug'] ) ) {
+ if ( is_array( $args['slug'] ) ) {
+ $slug = array_map( 'sanitize_title', $args['slug'] );
+ $where .= " AND t.slug IN ('" . implode( "', '", $slug ) . "')";
+ } else {
+ $slug = sanitize_title( $args['slug'] );
+ $where .= " AND t.slug = '$slug'";
+ }
+ }
+
+ if ( ! empty( $args['name__like'] ) ) {
+ $where .= $wpdb->prepare( " AND t.name LIKE %s", '%' . $wpdb->esc_like( $args['name__like'] ) . '%' );
+ }
+
+ if ( ! empty( $args['description__like'] ) ) {
+ $where .= $wpdb->prepare( " AND tt.description LIKE %s", '%' . $wpdb->esc_like( $args['description__like'] ) . '%' );
+ }
+
+ if ( '' !== $parent ) {
+ $parent = (int) $parent;
+ $where .= " AND tt.parent = '$parent'";
+ }
+
+ $hierarchical = $args['hierarchical'];
+ if ( 'count' == $args['fields'] ) {
+ $hierarchical = false;
+ }
+ if ( $args['hide_empty'] && !$hierarchical ) {
+ $where .= ' AND tt.count > 0';
+ }
+
+ $number = $args['number'];
+ $offset = $args['offset'];
+
+ // Don't limit the query results when we have to descend the family tree.
+ if ( $number && ! $hierarchical && ! $child_of && '' === $parent ) {
+ if ( $offset ) {
+ $limits = 'LIMIT ' . $offset . ',' . $number;
+ } else {
+ $limits = 'LIMIT ' . $number;
+ }
+ } else {
+ $limits = '';
+ }
+
+ if ( ! empty( $args['search'] ) ) {
+ $like = '%' . $wpdb->esc_like( $args['search'] ) . '%';
+ $where .= $wpdb->prepare( ' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', $like, $like );
+ }
+
+ // Meta query support.
+ $join = '';
+ if ( ! empty( $args['meta_query'] ) ) {
+ $mquery = new WP_Meta_Query( $args['meta_query'] );
+ $mq_sql = $mquery->get_sql( 'term', 't', 'term_id' );
+
+ $join .= $mq_sql['join'];
+ $where .= $mq_sql['where'];
+ }
+
+ $selects = array();
+ switch ( $args['fields'] ) {
+ case 'all':
+ $selects = array( 't.*', 'tt.*' );
+ break;
+ case 'ids':
+ case 'id=>parent':
+ $selects = array( 't.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy' );
+ break;
+ case 'names':
+ $selects = array( 't.term_id', 'tt.parent', 'tt.count', 't.name', 'tt.taxonomy' );
+ break;
+ case 'count':
+ $orderby = '';
+ $order = '';
+ $selects = array( 'COUNT(*)' );
+ break;
+ case 'id=>name':
+ $selects = array( 't.term_id', 't.name', 'tt.count', 'tt.taxonomy' );
+ break;
+ case 'id=>slug':
+ $selects = array( 't.term_id', 't.slug', 'tt.count', 'tt.taxonomy' );
+ break;
+ }
+
+ $_fields = $args['fields'];
+
+ /**
+ * Filter the fields to select in the terms query.
+ *
+ * Field lists modified using this filter will only modify the term fields returned
+ * by the function when the `$fields` parameter set to 'count' or 'all'. In all other
+ * cases, the term fields in the results array will be determined by the `$fields`
+ * parameter alone.
+ *
+ * Use of this filter can result in unpredictable behavior, and is not recommended.
+ *
+ * @since 2.8.0
+ *
+ * @param array $selects An array of fields to select for the terms query.
+ * @param array $args An array of term query arguments.
+ * @param array $taxonomies An array of taxonomies.
+ */
+ $fields = implode( ', ', apply_filters( 'get_terms_fields', $selects, $args, $taxonomies ) );
+
+ $join .= " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
+
+ $pieces = array( 'fields', 'join', 'where', 'orderby', 'order', 'limits' );
+
+ /**
+ * Filter the terms query SQL clauses.
+ *
+ * @since 3.1.0
+ *
+ * @param array $pieces Terms query SQL clauses.
+ * @param array $taxonomies An array of taxonomies.
+ * @param array $args An array of terms query arguments.
+ */
+ $clauses = apply_filters( 'terms_clauses', compact( $pieces ), $taxonomies, $args );
+
+ $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
+ $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
+ $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
+ $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
+ $order = isset( $clauses[ 'order' ] ) ? $clauses[ 'order' ] : '';
+ $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
+
+ $query = "SELECT $fields FROM $wpdb->terms AS t $join WHERE $where $orderby $order $limits";
+
+ // $args can be anything. Only use the args defined in defaults to compute the key.
+ $key = md5( serialize( wp_array_slice_assoc( $args, array_keys( $defaults ) ) ) . serialize( $taxonomies ) . $query );
+ $last_changed = wp_cache_get( 'last_changed', 'terms' );
+ if ( ! $last_changed ) {
+ $last_changed = microtime();
+ wp_cache_set( 'last_changed', $last_changed, 'terms' );
+ }
+ $cache_key = "get_terms:$key:$last_changed";
+ $cache = wp_cache_get( $cache_key, 'terms' );
+ if ( false !== $cache ) {
+ if ( 'all' === $_fields ) {
+ $cache = array_map( 'get_term', $cache );
+ }
+
+ /**
+ * Filter the given taxonomy's terms cache.
+ *
+ * @since 2.3.0
+ *
+ * @param array $cache Cached array of terms for the given taxonomy.
+ * @param array $taxonomies An array of taxonomies.
+ * @param array $args An array of get_terms() arguments.
+ */
+ return apply_filters( 'get_terms', $cache, $taxonomies, $args );
+ }
+
+ if ( 'count' == $_fields ) {
+ return $wpdb->get_var( $query );
+ }
+
+ $terms = $wpdb->get_results($query);
+ if ( 'all' == $_fields ) {
+ update_term_cache( $terms );
+ }
+
+ // Prime termmeta cache.
+ if ( $args['update_term_meta_cache'] ) {
+ $term_ids = wp_list_pluck( $terms, 'term_id' );
+ update_termmeta_cache( $term_ids );
+ }
+
+ if ( empty($terms) ) {
+ wp_cache_add( $cache_key, array(), 'terms', DAY_IN_SECONDS );
+
+ /** This filter is documented in wp-includes/taxonomy-functions.php */
+ return apply_filters( 'get_terms', array(), $taxonomies, $args );
+ }
+
+ if ( $child_of ) {
+ foreach ( $taxonomies as $_tax ) {
+ $children = _get_term_hierarchy( $_tax );
+ if ( ! empty( $children ) ) {
+ $terms = _get_term_children( $child_of, $terms, $_tax );
+ }
+ }
+ }
+
+ // Update term counts to include children.
+ if ( $args['pad_counts'] && 'all' == $_fields ) {
+ foreach ( $taxonomies as $_tax ) {
+ _pad_term_counts( $terms, $_tax );
+ }
+ }
+
+ // Make sure we show empty categories that have children.
+ if ( $hierarchical && $args['hide_empty'] && is_array( $terms ) ) {
+ foreach ( $terms as $k => $term ) {
+ if ( ! $term->count ) {
+ $children = get_term_children( $term->term_id, $term->taxonomy );
+ if ( is_array( $children ) ) {
+ foreach ( $children as $child_id ) {
+ $child = get_term( $child_id, $term->taxonomy );
+ if ( $child->count ) {
+ continue 2;
+ }
+ }
+ }
+
+ // It really is empty.
+ unset($terms[$k]);
+ }
+ }
+ }
+
+ $_terms = array();
+ if ( 'id=>parent' == $_fields ) {
+ foreach ( $terms as $term ) {
+ $_terms[ $term->term_id ] = $term->parent;
+ }
+ } elseif ( 'ids' == $_fields ) {
+ foreach ( $terms as $term ) {
+ $_terms[] = $term->term_id;
+ }
+ } elseif ( 'names' == $_fields ) {
+ foreach ( $terms as $term ) {
+ $_terms[] = $term->name;
+ }
+ } elseif ( 'id=>name' == $_fields ) {
+ foreach ( $terms as $term ) {
+ $_terms[ $term->term_id ] = $term->name;
+ }
+ } elseif ( 'id=>slug' == $_fields ) {
+ foreach ( $terms as $term ) {
+ $_terms[ $term->term_id ] = $term->slug;
+ }
+ }
+
+ if ( ! empty( $_terms ) ) {
+ $terms = $_terms;
+ }
+
+ if ( $number && is_array( $terms ) && count( $terms ) > $number ) {
+ $terms = array_slice( $terms, $offset, $number );
+ }
+
+ wp_cache_add( $cache_key, $terms, 'terms', DAY_IN_SECONDS );
+
+ if ( 'all' === $_fields ) {
+ $terms = array_map( 'get_term', $terms );
+ }
+
+ /** This filter is documented in wp-includes/taxonomy-functions.php */
+ return apply_filters( 'get_terms', $terms, $taxonomies, $args );
+}
+
+/**
+ * Adds metadata to a term.
+ *
+ * @since 4.4.0
+ *
+ * @param int $term_id Term ID.
+ * @param string $meta_key Metadata name.
+ * @param mixed $meta_value Metadata value.
+ * @param bool $unique Optional. Whether to bail if an entry with the same key is found for the term.
+ * Default false.
+ * @return int|WP_Error|bool Meta ID on success. WP_Error when term_id is ambiguous between taxonomies.
+ * False on failure.
+ */
+function add_term_meta( $term_id, $meta_key, $meta_value, $unique = false ) {
+ // Bail if term meta table is not installed.
+ if ( get_option( 'db_version' ) < 34370 ) {
+ return false;
+ }
+
+ if ( wp_term_is_shared( $term_id ) ) {
+ return new WP_Error( 'ambiguous_term_id', __( 'Term meta cannot be added to terms that are shared between taxonomies.'), $term_id );
+ }
+
+ $added = add_metadata( 'term', $term_id, $meta_key, $meta_value, $unique );
+
+ // Bust term query cache.
+ if ( $added ) {
+ wp_cache_set( 'last_changed', microtime(), 'terms' );
+ }
+
+ return $added;
+}
+
+/**
+ * Removes metadata matching criteria from a term.
+ *
+ * @since 4.4.0
+ *
+ * @param int $term_id Term ID.
+ * @param string $meta_key Metadata name.
+ * @param mixed $meta_value Optional. Metadata value. If provided, rows will only be removed that match the value.
+ * @return bool True on success, false on failure.
+ */
+function delete_term_meta( $term_id, $meta_key, $meta_value = '' ) {
+ // Bail if term meta table is not installed.
+ if ( get_option( 'db_version' ) < 34370 ) {
+ return false;
+ }
+
+ $deleted = delete_metadata( 'term', $term_id, $meta_key, $meta_value );
+
+ // Bust term query cache.
+ if ( $deleted ) {
+ wp_cache_set( 'last_changed', microtime(), 'terms' );
+ }
+
+ return $deleted;
+}
+
+/**
+ * Retrieves metadata for a term.
+ *
+ * @since 4.4.0
+ *
+ * @param int $term_id Term ID.
+ * @param string $key Optional. The meta key to retrieve. If no key is provided, fetches all metadata for the term.
+ * @param bool $single Whether to return a single value. If false, an array of all values matching the
+ * `$term_id`/`$key` pair will be returned. Default: false.
+ * @return mixed If `$single` is false, an array of metadata values. If `$single` is true, a single metadata value.
+ */
+function get_term_meta( $term_id, $key = '', $single = false ) {
+ // Bail if term meta table is not installed.
+ if ( get_option( 'db_version' ) < 34370 ) {
+ return false;
+ }
+
+ return get_metadata( 'term', $term_id, $key, $single );
+}
+
+/**
+ * Updates term metadata.
+ *
+ * Use the `$prev_value` parameter to differentiate between meta fields with the same key and term ID.
+ *
+ * If the meta field for the term does not exist, it will be added.
+ *
+ * @since 4.4.0
+ *
+ * @param int $term_id Term ID.
+ * @param string $meta_key Metadata key.
+ * @param mixed $meta_value Metadata value.
+ * @param mixed $prev_value Optional. Previous value to check before removing.
+ * @return int|WP_Error|bool Meta ID if the key didn't previously exist. True on successful update.
+ * WP_Error when term_id is ambiguous between taxonomies. False on failure.
+ */
+function update_term_meta( $term_id, $meta_key, $meta_value, $prev_value = '' ) {
+ // Bail if term meta table is not installed.
+ if ( get_option( 'db_version' ) < 34370 ) {
+ return false;
+ }
+
+ if ( wp_term_is_shared( $term_id ) ) {
+ return new WP_Error( 'ambiguous_term_id', __( 'Term meta cannot be added to terms that are shared between taxonomies.'), $term_id );
+ }
+
+ $updated = update_metadata( 'term', $term_id, $meta_key, $meta_value, $prev_value );
+
+ // Bust term query cache.
+ if ( $updated ) {
+ wp_cache_set( 'last_changed', microtime(), 'terms' );
+ }
+
+ return $updated;
+}
+
+/**
+ * Updates metadata cache for list of term IDs.
+ *
+ * Performs SQL query to retrieve all metadata for the terms matching `$term_ids` and stores them in the cache.
+ * Subsequent calls to `get_term_meta()` will not need to query the database.
+ *
+ * @since 4.4.0
+ *
+ * @param array $term_ids List of term IDs.
+ * @return array|false Returns false if there is nothing to update. Returns an array of metadata on success.
+ */
+function update_termmeta_cache( $term_ids ) {
+ // Bail if term meta table is not installed.
+ if ( get_option( 'db_version' ) < 34370 ) {
+ return;
+ }
+
+ return update_meta_cache( 'term', $term_ids );
+}
+
+/**
+ * Check if Term exists.
+ *
+ * Formerly is_term(), introduced in 2.3.0.
+ *
+ * @since 3.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int|string $term The term to check
+ * @param string $taxonomy The taxonomy name to use
+ * @param int $parent Optional. ID of parent term under which to confine the exists search.
+ * @return mixed Returns null if the term does not exist. Returns the term ID
+ * if no taxonomy is specified and the term ID exists. Returns
+ * an array of the term ID and the term taxonomy ID the taxonomy
+ * is specified and the pairing exists.
+ */
+function term_exists( $term, $taxonomy = '', $parent = null ) {
+ global $wpdb;
+
+ $select = "SELECT term_id FROM $wpdb->terms as t WHERE ";
+ $tax_select = "SELECT tt.term_id, tt.term_taxonomy_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_id = t.term_id WHERE ";
+
+ if ( is_int($term) ) {
+ if ( 0 == $term )
+ return 0;
+ $where = 't.term_id = %d';
+ if ( !empty($taxonomy) )
+ return $wpdb->get_row( $wpdb->prepare( $tax_select . $where . " AND tt.taxonomy = %s", $term, $taxonomy ), ARRAY_A );
+ else
+ return $wpdb->get_var( $wpdb->prepare( $select . $where, $term ) );
+ }
+
+ $term = trim( wp_unslash( $term ) );
+ $slug = sanitize_title( $term );
+
+ $where = 't.slug = %s';
+ $else_where = 't.name = %s';
+ $where_fields = array($slug);
+ $else_where_fields = array($term);
+ $orderby = 'ORDER BY t.term_id ASC';
+ $limit = 'LIMIT 1';
+ if ( !empty($taxonomy) ) {
+ if ( is_numeric( $parent ) ) {
+ $parent = (int) $parent;
+ $where_fields[] = $parent;
+ $else_where_fields[] = $parent;
+ $where .= ' AND tt.parent = %d';
+ $else_where .= ' AND tt.parent = %d';
+ }
+
+ $where_fields[] = $taxonomy;
+ $else_where_fields[] = $taxonomy;
+
+ if ( $result = $wpdb->get_row( $wpdb->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_id = t.term_id WHERE $where AND tt.taxonomy = %s $orderby $limit", $where_fields), ARRAY_A) )
+ return $result;
+
+ return $wpdb->get_row( $wpdb->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_id = t.term_id WHERE $else_where AND tt.taxonomy = %s $orderby $limit", $else_where_fields), ARRAY_A);
+ }
+
+ if ( $result = $wpdb->get_var( $wpdb->prepare("SELECT term_id FROM $wpdb->terms as t WHERE $where $orderby $limit", $where_fields) ) )
+ return $result;
+
+ return $wpdb->get_var( $wpdb->prepare("SELECT term_id FROM $wpdb->terms as t WHERE $else_where $orderby $limit", $else_where_fields) );
+}
+
+/**
+ * Check if a term is an ancestor of another term.
+ *
+ * You can use either an id or the term object for both parameters.
+ *
+ * @since 3.4.0
+ *
+ * @param int|object $term1 ID or object to check if this is the parent term.
+ * @param int|object $term2 The child term.
+ * @param string $taxonomy Taxonomy name that $term1 and `$term2` belong to.
+ * @return bool Whether `$term2` is a child of `$term1`.
+ */
+function term_is_ancestor_of( $term1, $term2, $taxonomy ) {
+ if ( ! isset( $term1->term_id ) )
+ $term1 = get_term( $term1, $taxonomy );
+ if ( ! isset( $term2->parent ) )
+ $term2 = get_term( $term2, $taxonomy );
+
+ if ( empty( $term1->term_id ) || empty( $term2->parent ) )
+ return false;
+ if ( $term2->parent == $term1->term_id )
+ return true;
+
+ return term_is_ancestor_of( $term1, get_term( $term2->parent, $taxonomy ), $taxonomy );
+}
+
+/**
+ * Sanitize Term all fields.
+ *
+ * Relies on sanitize_term_field() to sanitize the term. The difference is that
+ * this function will sanitize <strong>all</strong> fields. The context is based
+ * on sanitize_term_field().
+ *
+ * The $term is expected to be either an array or an object.
+ *
+ * @since 2.3.0
+ *
+ * @param array|object $term The term to check.
+ * @param string $taxonomy The taxonomy name to use.
+ * @param string $context Optional. Context in which to sanitize the term. Accepts 'edit', 'db',
+ * 'display', 'attribute', or 'js'. Default 'display'.
+ * @return array|object Term with all fields sanitized.
+ */
+function sanitize_term($term, $taxonomy, $context = 'display') {
+ $fields = array( 'term_id', 'name', 'description', 'slug', 'count', 'parent', 'term_group', 'term_taxonomy_id', 'object_id' );
+
+ $do_object = is_object( $term );
+
+ $term_id = $do_object ? $term->term_id : (isset($term['term_id']) ? $term['term_id'] : 0);
+
+ foreach ( (array) $fields as $field ) {
+ if ( $do_object ) {
+ if ( isset($term->$field) )
+ $term->$field = sanitize_term_field($field, $term->$field, $term_id, $taxonomy, $context);
+ } else {
+ if ( isset($term[$field]) )
+ $term[$field] = sanitize_term_field($field, $term[$field], $term_id, $taxonomy, $context);
+ }
+ }
+
+ if ( $do_object )
+ $term->filter = $context;
+ else
+ $term['filter'] = $context;
+
+ return $term;
+}
+
+/**
+ * Cleanse the field value in the term based on the context.
+ *
+ * Passing a term field value through the function should be assumed to have
+ * cleansed the value for whatever context the term field is going to be used.
+ *
+ * If no context or an unsupported context is given, then default filters will
+ * be applied.
+ *
+ * There are enough filters for each context to support a custom filtering
+ * without creating your own filter function. Simply create a function that
+ * hooks into the filter you need.
+ *
+ * @since 2.3.0
+ *
+ * @param string $field Term field to sanitize.
+ * @param string $value Search for this term value.
+ * @param int $term_id Term ID.
+ * @param string $taxonomy Taxonomy Name.
+ * @param string $context Context in which to sanitize the term field. Accepts 'edit', 'db', 'display',
+ * 'attribute', or 'js'.
+ * @return mixed Sanitized field.
+ */
+function sanitize_term_field($field, $value, $term_id, $taxonomy, $context) {
+ $int_fields = array( 'parent', 'term_id', 'count', 'term_group', 'term_taxonomy_id', 'object_id' );
+ if ( in_array( $field, $int_fields ) ) {
+ $value = (int) $value;
+ if ( $value < 0 )
+ $value = 0;
+ }
+
+ if ( 'raw' == $context )
+ return $value;
+
+ if ( 'edit' == $context ) {
+
+ /**
+ * Filter a term field to edit before it is sanitized.
+ *
+ * The dynamic portion of the filter name, `$field`, refers to the term field.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the term field.
+ * @param int $term_id Term ID.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ $value = apply_filters( "edit_term_{$field}", $value, $term_id, $taxonomy );
+
+ /**
+ * Filter the taxonomy field to edit before it is sanitized.
+ *
+ * The dynamic portions of the filter name, `$taxonomy` and `$field`, refer
+ * to the taxonomy slug and taxonomy field, respectively.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the taxonomy field to edit.
+ * @param int $term_id Term ID.
+ */
+ $value = apply_filters( "edit_{$taxonomy}_{$field}", $value, $term_id );
+
+ if ( 'description' == $field )
+ $value = esc_html($value); // textarea_escaped
+ else
+ $value = esc_attr($value);
+ } elseif ( 'db' == $context ) {
+
+ /**
+ * Filter a term field value before it is sanitized.
+ *
+ * The dynamic portion of the filter name, `$field`, refers to the term field.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the term field.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ $value = apply_filters( "pre_term_{$field}", $value, $taxonomy );
+
+ /**
+ * Filter a taxonomy field before it is sanitized.
+ *
+ * The dynamic portions of the filter name, `$taxonomy` and `$field`, refer
+ * to the taxonomy slug and field name, respectively.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the taxonomy field.
+ */
+ $value = apply_filters( "pre_{$taxonomy}_{$field}", $value );
+
+ // Back compat filters
+ if ( 'slug' == $field ) {
+ /**
+ * Filter the category nicename before it is sanitized.
+ *
+ * Use the pre_{$taxonomy}_{$field} hook instead.
+ *
+ * @since 2.0.3
+ *
+ * @param string $value The category nicename.
+ */
+ $value = apply_filters( 'pre_category_nicename', $value );
+ }
+
+ } elseif ( 'rss' == $context ) {
+
+ /**
+ * Filter the term field for use in RSS.
+ *
+ * The dynamic portion of the filter name, `$field`, refers to the term field.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the term field.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ $value = apply_filters( "term_{$field}_rss", $value, $taxonomy );
+
+ /**
+ * Filter the taxonomy field for use in RSS.
+ *
+ * The dynamic portions of the hook name, `$taxonomy`, and `$field`, refer
+ * to the taxonomy slug and field name, respectively.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the taxonomy field.
+ */
+ $value = apply_filters( "{$taxonomy}_{$field}_rss", $value );
+ } else {
+ // Use display filters by default.
+
+ /**
+ * Filter the term field sanitized for display.
+ *
+ * The dynamic portion of the filter name, `$field`, refers to the term field name.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the term field.
+ * @param int $term_id Term ID.
+ * @param string $taxonomy Taxonomy slug.
+ * @param string $context Context to retrieve the term field value.
+ */
+ $value = apply_filters( "term_{$field}", $value, $term_id, $taxonomy, $context );
+
+ /**
+ * Filter the taxonomy field sanitized for display.
+ *
+ * The dynamic portions of the filter name, `$taxonomy`, and `$field`, refer
+ * to the taxonomy slug and taxonomy field, respectively.
+ *
+ * @since 2.3.0
+ *
+ * @param mixed $value Value of the taxonomy field.
+ * @param int $term_id Term ID.
+ * @param string $context Context to retrieve the taxonomy field value.
+ */
+ $value = apply_filters( "{$taxonomy}_{$field}", $value, $term_id, $context );
+ }
+
+ if ( 'attribute' == $context ) {
+ $value = esc_attr($value);
+ } elseif ( 'js' == $context ) {
+ $value = esc_js($value);
+ }
+ return $value;
+}
+
+/**
+ * Count how many terms are in Taxonomy.
+ *
+ * Default $args is 'hide_empty' which can be 'hide_empty=true' or array('hide_empty' => true).
+ *
+ * @todo Document $args as a hash notation.
+ *
+ * @since 2.3.0
+ *
+ * @param string $taxonomy Taxonomy name
+ * @param array|string $args Overwrite defaults. See get_terms()
+ * @return array|int|WP_Error How many terms are in $taxonomy. WP_Error if $taxonomy does not exist.
+ */
+function wp_count_terms( $taxonomy, $args = array() ) {
+ $defaults = array('hide_empty' => false);
+ $args = wp_parse_args($args, $defaults);
+
+ // backwards compatibility
+ if ( isset($args['ignore_empty']) ) {
+ $args['hide_empty'] = $args['ignore_empty'];
+ unset($args['ignore_empty']);
+ }
+
+ $args['fields'] = 'count';
+
+ return get_terms($taxonomy, $args);
+}
+
+/**
+ * Will unlink the object from the taxonomy or taxonomies.
+ *
+ * Will remove all relationships between the object and any terms in
+ * a particular taxonomy or taxonomies. Does not remove the term or
+ * taxonomy itself.
+ *
+ * @since 2.3.0
+ *
+ * @param int $object_id The term Object Id that refers to the term.
+ * @param string|array $taxonomies List of Taxonomy Names or single Taxonomy name.
+ */
+function wp_delete_object_term_relationships( $object_id, $taxonomies ) {
+ $object_id = (int) $object_id;
+
+ if ( !is_array($taxonomies) )
+ $taxonomies = array($taxonomies);
+
+ foreach ( (array) $taxonomies as $taxonomy ) {
+ $term_ids = wp_get_object_terms( $object_id, $taxonomy, array( 'fields' => 'ids' ) );
+ $term_ids = array_map( 'intval', $term_ids );
+ wp_remove_object_terms( $object_id, $term_ids, $taxonomy );
+ }
+}
+
+/**
+ * Removes a term from the database.
+ *
+ * If the term is a parent of other terms, then the children will be updated to
+ * that term's parent.
+ *
+ * Metadata associated with the term will be deleted.
+ *
+ * The `$args` 'default' will only override the terms found, if there is only one
+ * term found. Any other and the found terms are used.
+ *
+ * The $args 'force_default' will force the term supplied as default to be
+ * assigned even if the object was not going to be termless
+ *
+ * @todo Document $args as a hash notation.
+ *
+ * @since 2.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $term Term ID.
+ * @param string $taxonomy Taxonomy Name.
+ * @param array|string $args Optional. Change 'default' term id and override found term ids.
+ * @return bool|int|WP_Error Returns false if not term; true if completes delete action.
+ */
+function wp_delete_term( $term, $taxonomy, $args = array() ) {
+ global $wpdb;
+
+ $term = (int) $term;
+
+ if ( ! $ids = term_exists($term, $taxonomy) )
+ return false;
+ if ( is_wp_error( $ids ) )
+ return $ids;
+
+ $tt_id = $ids['term_taxonomy_id'];
+
+ $defaults = array();
+
+ if ( 'category' == $taxonomy ) {
+ $defaults['default'] = get_option( 'default_category' );
+ if ( $defaults['default'] == $term )
+ return 0; // Don't delete the default category
+ }
+
+ $args = wp_parse_args($args, $defaults);
+
+ if ( isset( $args['default'] ) ) {
+ $default = (int) $args['default'];
+ if ( ! term_exists( $default, $taxonomy ) ) {
+ unset( $default );
+ }
+ }
+
+ if ( isset( $args['force_default'] ) ) {
+ $force_default = $args['force_default'];
+ }
+
+ /**
+ * Fires when deleting a term, before any modifications are made to posts or terms.
+ *
+ * @since 4.1.0
+ *
+ * @param int $term Term ID.
+ * @param string $taxonomy Taxonomy Name.
+ */
+ do_action( 'pre_delete_term', $term, $taxonomy );
+
+ // Update children to point to new parent
+ if ( is_taxonomy_hierarchical($taxonomy) ) {
+ $term_obj = get_term($term, $taxonomy);
+ if ( is_wp_error( $term_obj ) )
+ return $term_obj;
+ $parent = $term_obj->parent;
+
+ $edit_ids = $wpdb->get_results( "SELECT term_id, term_taxonomy_id FROM $wpdb->term_taxonomy WHERE `parent` = " . (int)$term_obj->term_id );
+ $edit_tt_ids = wp_list_pluck( $edit_ids, 'term_taxonomy_id' );
+
+ /**
+ * Fires immediately before a term to delete's children are reassigned a parent.
+ *
+ * @since 2.9.0
+ *
+ * @param array $edit_tt_ids An array of term taxonomy IDs for the given term.
+ */
+ do_action( 'edit_term_taxonomies', $edit_tt_ids );
+
+ $wpdb->update( $wpdb->term_taxonomy, compact( 'parent' ), array( 'parent' => $term_obj->term_id) + compact( 'taxonomy' ) );
+
+ // Clean the cache for all child terms.
+ $edit_term_ids = wp_list_pluck( $edit_ids, 'term_id' );
+ clean_term_cache( $edit_term_ids, $taxonomy );
+
+ /**
+ * Fires immediately after a term to delete's children are reassigned a parent.
+ *
+ * @since 2.9.0
+ *
+ * @param array $edit_tt_ids An array of term taxonomy IDs for the given term.
+ */
+ do_action( 'edited_term_taxonomies', $edit_tt_ids );
+ }
+
+ // Get the term before deleting it or its term relationships so we can pass to actions below.
+ $deleted_term = get_term( $term, $taxonomy );
+
+ $objects = $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $tt_id ) );
+
+ foreach ( (array) $objects as $object ) {
+ $terms = wp_get_object_terms($object, $taxonomy, array('fields' => 'ids', 'orderby' => 'none'));
+ if ( 1 == count($terms) && isset($default) ) {
+ $terms = array($default);
+ } else {
+ $terms = array_diff($terms, array($term));
+ if (isset($default) && isset($force_default) && $force_default)
+ $terms = array_merge($terms, array($default));
+ }
+ $terms = array_map('intval', $terms);
+ wp_set_object_terms($object, $terms, $taxonomy);
+ }
+
+ // Clean the relationship caches for all object types using this term.
+ $tax_object = get_taxonomy( $taxonomy );
+ foreach ( $tax_object->object_type as $object_type )
+ clean_object_term_cache( $objects, $object_type );
+
+ $term_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->termmeta WHERE term_id = %d ", $term ) );
+ foreach ( $term_meta_ids as $mid ) {
+ delete_metadata_by_mid( 'term', $mid );
+ }
+
+ /**
+ * Fires immediately before a term taxonomy ID is deleted.
+ *
+ * @since 2.9.0
+ *
+ * @param int $tt_id Term taxonomy ID.
+ */
+ do_action( 'delete_term_taxonomy', $tt_id );
+ $wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) );
+
+ /**
+ * Fires immediately after a term taxonomy ID is deleted.
+ *
+ * @since 2.9.0
+ *
+ * @param int $tt_id Term taxonomy ID.
+ */
+ do_action( 'deleted_term_taxonomy', $tt_id );
+
+ // Delete the term if no taxonomies use it.
+ if ( !$wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy WHERE term_id = %d", $term) ) )
+ $wpdb->delete( $wpdb->terms, array( 'term_id' => $term ) );
+
+ clean_term_cache($term, $taxonomy);
+
+ /**
+ * Fires after a term is deleted from the database and the cache is cleaned.
+ *
+ * @since 2.5.0
+ *
+ * @param int $term Term ID.
+ * @param int $tt_id Term taxonomy ID.
+ * @param string $taxonomy Taxonomy slug.
+ * @param mixed $deleted_term Copy of the already-deleted term, in the form specified
+ * by the parent function. WP_Error otherwise.
+ */
+ do_action( 'delete_term', $term, $tt_id, $taxonomy, $deleted_term );
+
+ /**
+ * Fires after a term in a specific taxonomy is deleted.
+ *
+ * The dynamic portion of the hook name, `$taxonomy`, refers to the specific
+ * taxonomy the term belonged to.
+ *
+ * @since 2.3.0
+ *
+ * @param int $term Term ID.
+ * @param int $tt_id Term taxonomy ID.
+ * @param mixed $deleted_term Copy of the already-deleted term, in the form specified
+ * by the parent function. WP_Error otherwise.
+ */
+ do_action( "delete_$taxonomy", $term, $tt_id, $deleted_term );
+
+ return true;
+}
+
+/**
+ * Deletes one existing category.
+ *
+ * @since 2.0.0
+ *
+ * @param int $cat_ID
+ * @return bool|int|WP_Error Returns true if completes delete action; false if term doesn't exist;
+ * Zero on attempted deletion of default Category; WP_Error object is also a possibility.
+ */
+function wp_delete_category( $cat_ID ) {
+ return wp_delete_term( $cat_ID, 'category' );
+}
+
+/**
+ * Retrieves the terms associated with the given object(s), in the supplied taxonomies.
+ *
+ * @since 2.3.0
+ * @since 4.2.0 Added support for 'taxonomy', 'parent', and 'term_taxonomy_id' values of `$orderby`.
+ * Introduced `$parent` argument.
+ * @since 4.4.0 Introduced `$meta_query` and `$update_term_meta_cache` arguments. When `$fields` is 'all' or
+ * 'all_with_object_id', an array of `WP_Term` objects will be returned.
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int|array $object_ids The ID(s) of the object(s) to retrieve.
+ * @param string|array $taxonomies The taxonomies to retrieve terms from.
+ * @param array|string $args {
+ * Array of arguments.
+ * @type string $orderby Field by which results should be sorted. Accepts 'name', 'count', 'slug',
+ * 'term_group', 'term_order', 'taxonomy', 'parent', or 'term_taxonomy_id'.
+ * Default 'name'.
+ * @type string $order Sort order. Accepts 'ASC' or 'DESC'. Default 'ASC'.
+ * @type string $fields Fields to return for matched terms. Accepts 'all', 'ids', 'names', and
+ * 'all_with_object_id'. Note that 'all' or 'all_with_object_id' will result
+ * in an array of term objects being returned, 'ids' will return an array of
+ * integers, and 'names' an array of strings.
+ * @type int $parent Optional. Limit results to the direct children of a given term ID.
+ * @type bool $update_term_meta_cache Whether to prime termmeta cache for matched terms. Only applies when
+ * `$fields` is 'all', 'all_with_object_id', or 'term_id'. Default true.
+ * @type array $meta_query Meta query clauses to limit retrieved terms by. See `WP_Meta_Query`.
+ * Default empty.
+ * }
+ * @return array|WP_Error The requested term data or empty array if no terms found.
+ * WP_Error if any of the $taxonomies don't exist.
+ */
+function wp_get_object_terms($object_ids, $taxonomies, $args = array()) {
+ global $wpdb;
+
+ if ( empty( $object_ids ) || empty( $taxonomies ) )
+ return array();
+
+ if ( !is_array($taxonomies) )
+ $taxonomies = array($taxonomies);
+
+ foreach ( $taxonomies as $taxonomy ) {
+ if ( ! taxonomy_exists($taxonomy) )
+ return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
+ }
+
+ if ( !is_array($object_ids) )
+ $object_ids = array($object_ids);
+ $object_ids = array_map('intval', $object_ids);
+
+ $defaults = array(
+ 'orderby' => 'name',
+ 'order' => 'ASC',
+ 'fields' => 'all',
+ 'parent' => '',
+ 'update_term_meta_cache' => true,
+ 'meta_query' => '',
+ );
+ $args = wp_parse_args( $args, $defaults );
+
+ $terms = array();
+ if ( count($taxonomies) > 1 ) {
+ foreach ( $taxonomies as $index => $taxonomy ) {
+ $t = get_taxonomy($taxonomy);
+ if ( isset($t->args) && is_array($t->args) && $args != array_merge($args, $t->args) ) {
+ unset($taxonomies[$index]);
+ $terms = array_merge($terms, wp_get_object_terms($object_ids, $taxonomy, array_merge($args, $t->args)));
+ }
+ }
+ } else {
+ $t = get_taxonomy($taxonomies[0]);
+ if ( isset($t->args) && is_array($t->args) )
+ $args = array_merge($args, $t->args);
+ }
+
+ $orderby = $args['orderby'];
+ $order = $args['order'];
+ $fields = $args['fields'];
+
+ if ( in_array( $orderby, array( 'term_id', 'name', 'slug', 'term_group' ) ) ) {
+ $orderby = "t.$orderby";
+ } elseif ( in_array( $orderby, array( 'count', 'parent', 'taxonomy', 'term_taxonomy_id' ) ) ) {
+ $orderby = "tt.$orderby";
+ } elseif ( 'term_order' === $orderby ) {
+ $orderby = 'tr.term_order';
+ } elseif ( 'none' === $orderby ) {
+ $orderby = '';
+ $order = '';
+ } else {
+ $orderby = 't.term_id';
+ }
+
+ // tt_ids queries can only be none or tr.term_taxonomy_id
+ if ( ('tt_ids' == $fields) && !empty($orderby) )
+ $orderby = 'tr.term_taxonomy_id';
+
+ if ( !empty($orderby) )
+ $orderby = "ORDER BY $orderby";
+
+ $order = strtoupper( $order );
+ if ( '' !== $order && ! in_array( $order, array( 'ASC', 'DESC' ) ) )
+ $order = 'ASC';
+
+ $taxonomy_array = $taxonomies;
+ $object_id_array = $object_ids;
+ $taxonomies = "'" . implode("', '", $taxonomies) . "'";
+ $object_ids = implode(', ', $object_ids);
+
+ $select_this = '';
+ if ( 'all' == $fields ) {
+ $select_this = 't.*, tt.*';
+ } elseif ( 'ids' == $fields ) {
+ $select_this = 't.term_id';
+ } elseif ( 'names' == $fields ) {
+ $select_this = 't.name';
+ } elseif ( 'slugs' == $fields ) {
+ $select_this = 't.slug';
+ } elseif ( 'all_with_object_id' == $fields ) {
+ $select_this = 't.*, tt.*, tr.object_id';
+ }
+
+ $where = array(
+ "tt.taxonomy IN ($taxonomies)",
+ "tr.object_id IN ($object_ids)",
+ );
+
+ if ( '' !== $args['parent'] ) {
+ $where[] = $wpdb->prepare( 'tt.parent = %d', $args['parent'] );
+ }
+
+ // Meta query support.
+ $meta_query_join = '';
+ if ( ! empty( $args['meta_query'] ) ) {
+ $mquery = new WP_Meta_Query( $args['meta_query'] );
+ $mq_sql = $mquery->get_sql( 'term', 't', 'term_id' );
+
+ $meta_query_join .= $mq_sql['join'];
+
+ // Strip leading AND.
+ $where[] = preg_replace( '/^\s*AND/', '', $mq_sql['where'] );
+ }
+
+ $where = implode( ' AND ', $where );
+
+ $query = "SELECT $select_this FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id INNER JOIN $wpdb->term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id $meta_query_join WHERE $where $orderby $order";
+
+ $objects = false;
+ if ( 'all' == $fields || 'all_with_object_id' == $fields ) {
+ $_terms = $wpdb->get_results( $query );
+ $object_id_index = array();
+ foreach ( $_terms as $key => $term ) {
+ $term = sanitize_term( $term, $taxonomy, 'raw' );
+ $_terms[ $key ] = $term;
+
+ if ( isset( $term->object_id ) ) {
+ $object_id_index[ $key ] = $term->object_id;
+ }
+ }
+
+ update_term_cache( $_terms );
+ $_terms = array_map( 'get_term', $_terms );
+
+ // Re-add the object_id data, which is lost when fetching terms from cache.
+ if ( 'all_with_object_id' === $fields ) {
+ foreach ( $_terms as $key => $_term ) {
+ if ( isset( $object_id_index[ $key ] ) ) {
+ $_term->object_id = $object_id_index[ $key ];
+ }
+ }
+ }
+
+ $terms = array_merge( $terms, $_terms );
+ $objects = true;
+
+ } elseif ( 'ids' == $fields || 'names' == $fields || 'slugs' == $fields ) {
+ $_terms = $wpdb->get_col( $query );
+ $_field = ( 'ids' == $fields ) ? 'term_id' : 'name';
+ foreach ( $_terms as $key => $term ) {
+ $_terms[$key] = sanitize_term_field( $_field, $term, $term, $taxonomy, 'raw' );
+ }
+ $terms = array_merge( $terms, $_terms );
+ } elseif ( 'tt_ids' == $fields ) {
+ $terms = $wpdb->get_col("SELECT tr.term_taxonomy_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tr.object_id IN ($object_ids) AND tt.taxonomy IN ($taxonomies) $orderby $order");
+ foreach ( $terms as $key => $tt_id ) {
+ $terms[$key] = sanitize_term_field( 'term_taxonomy_id', $tt_id, 0, $taxonomy, 'raw' ); // 0 should be the term id, however is not needed when using raw context.
+ }
+ }
+
+ // Update termmeta cache, if necessary.
+ if ( $args['update_term_meta_cache'] && ( 'all' === $fields || 'all_with_object_ids' === $fields || 'term_id' === $fields ) ) {
+ if ( 'term_id' === $fields ) {
+ $term_ids = $fields;
+ } else {
+ $term_ids = wp_list_pluck( $terms, 'term_id' );
+ }
+
+ update_termmeta_cache( $term_ids );
+ }
+
+ if ( ! $terms ) {
+ $terms = array();
+ } elseif ( $objects && 'all_with_object_id' !== $fields ) {
+ $_tt_ids = array();
+ $_terms = array();
+ foreach ( $terms as $term ) {
+ if ( in_array( $term->term_taxonomy_id, $_tt_ids ) ) {
+ continue;
+ }
+
+ $_tt_ids[] = $term->term_taxonomy_id;
+ $_terms[] = $term;
+ }
+ $terms = $_terms;
+ } elseif ( ! $objects ) {
+ $terms = array_values( array_unique( $terms ) );
+ }
+
+ /**
+ * Filter the terms for a given object or objects.
+ *
+ * @since 4.2.0
+ *
+ * @param array $terms An array of terms for the given object or objects.
+ * @param array $object_id_array Array of object IDs for which `$terms` were retrieved.
+ * @param array $taxonomy_array Array of taxonomies from which `$terms` were retrieved.
+ * @param array $args An array of arguments for retrieving terms for the given
+ * object(s). See wp_get_object_terms() for details.
+ */
+ $terms = apply_filters( 'get_object_terms', $terms, $object_id_array, $taxonomy_array, $args );
+
+ /**
+ * Filter the terms for a given object or objects.
+ *
+ * The `$taxonomies` parameter passed to this filter is formatted as a SQL fragment. The
+ * {@see 'get_object_terms'} filter is recommended as an alternative.
+ *
+ * @since 2.8.0
+ *
+ * @param array $terms An array of terms for the given object or objects.
+ * @param int|array $object_ids Object ID or array of IDs.
+ * @param string $taxonomies SQL-formatted (comma-separated and quoted) list of taxonomy names.
+ * @param array $args An array of arguments for retrieving terms for the given object(s).
+ * See {@see wp_get_object_terms()} for details.
+ */
+ return apply_filters( 'wp_get_object_terms', $terms, $object_ids, $taxonomies, $args );
+}
+
+/**
+ * Add a new term to the database.
+ *
+ * A non-existent term is inserted in the following sequence:
+ * 1. The term is added to the term table, then related to the taxonomy.
+ * 2. If everything is correct, several actions are fired.
+ * 3. The 'term_id_filter' is evaluated.
+ * 4. The term cache is cleaned.
+ * 5. Several more actions are fired.
+ * 6. An array is returned containing the term_id and term_taxonomy_id.
+ *
+ * If the 'slug' argument is not empty, then it is checked to see if the term
+ * is invalid. If it is not a valid, existing term, it is added and the term_id
+ * is given.
+ *
+ * If the taxonomy is hierarchical, and the 'parent' argument is not empty,
+ * the term is inserted and the term_id will be given.
+ *
+ * Error handling:
+ * If $taxonomy does not exist or $term is empty,
+ * a WP_Error object will be returned.
+ *
+ * If the term already exists on the same hierarchical level,
+ * or the term slug and name are not unique, a WP_Error object will be returned.
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @since 2.3.0
+ *
+ * @param string $term The term to add or update.
+ * @param string $taxonomy The taxonomy to which to add the term.
+ * @param array|string $args {
+ * Optional. Array or string of arguments for inserting a term.
+ *
+ * @type string $alias_of Slug of the term to make this term an alias of.
+ * Default empty string. Accepts a term slug.
+ * @type string $description The term description. Default empty string.
+ * @type int $parent The id of the parent term. Default 0.
+ * @type string $slug The term slug to use. Default empty string.
+ * }
+ * @return array|WP_Error An array containing the `term_id` and `term_taxonomy_id`,
+ * {@see WP_Error} otherwise.
+ */
+function wp_insert_term( $term, $taxonomy, $args = array() ) {
+ global $wpdb;
+
+ if ( ! taxonomy_exists($taxonomy) ) {
+ return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
+ }
+ /**
+ * Filter a term before it is sanitized and inserted into the database.
+ *
+ * @since 3.0.0
+ *
+ * @param string $term The term to add or update.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ $term = apply_filters( 'pre_insert_term', $term, $taxonomy );
+ if ( is_wp_error( $term ) ) {
+ return $term;
+ }
+ if ( is_int($term) && 0 == $term ) {
+ return new WP_Error('invalid_term_id', __('Invalid term ID'));
+ }
+ if ( '' == trim($term) ) {
+ return new WP_Error('empty_term_name', __('A name is required for this term'));
+ }
+ $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => '');
+ $args = wp_parse_args( $args, $defaults );
+
+ if ( $args['parent'] > 0 && ! term_exists( (int) $args['parent'] ) ) {
+ return new WP_Error( 'missing_parent', __( 'Parent term does not exist.' ) );
+ }
+ $args['name'] = $term;
+ $args['taxonomy'] = $taxonomy;
+ $args = sanitize_term($args, $taxonomy, 'db');
+
+ // expected_slashed ($name)
+ $name = wp_unslash( $args['name'] );
+ $description = wp_unslash( $args['description'] );
+ $parent = (int) $args['parent'];
+
+ $slug_provided = ! empty( $args['slug'] );
+ if ( ! $slug_provided ) {
+ $slug = sanitize_title( $name );
+ } else {
+ $slug = $args['slug'];
+ }
+
+ $term_group = 0;
+ if ( $args['alias_of'] ) {
+ $alias = get_term_by( 'slug', $args['alias_of'], $taxonomy );
+ if ( ! empty( $alias->term_group ) ) {
+ // The alias we want is already in a group, so let's use that one.
+ $term_group = $alias->term_group;
+ } elseif ( ! empty( $alias->term_id ) ) {
+ /*
+ * The alias is not in a group, so we create a new one
+ * and add the alias to it.
+ */
+ $term_group = $wpdb->get_var("SELECT MAX(term_group) FROM $wpdb->terms") + 1;
+
+ wp_update_term( $alias->term_id, $taxonomy, array(
+ 'term_group' => $term_group,
+ ) );
+ }
+ }
+
+ /*
+ * Prevent the creation of terms with duplicate names at the same level of a taxonomy hierarchy,
+ * unless a unique slug has been explicitly provided.
+ */
+ $name_matches = get_terms( $taxonomy, array(
+ 'name' => $name,
+ 'hide_empty' => false,
+ ) );
+
+ /*
+ * The `name` match in `get_terms()` doesn't differentiate accented characters,
+ * so we do a stricter comparison here.
+ */
+ $name_match = null;
+ if ( $name_matches ) {
+ foreach ( $name_matches as $_match ) {
+ if ( strtolower( $name ) === strtolower( $_match->name ) ) {
+ $name_match = $_match;
+ break;
+ }
+ }
+ }
+
+ if ( $name_match ) {
+ $slug_match = get_term_by( 'slug', $slug, $taxonomy );
+ if ( ! $slug_provided || $name_match->slug === $slug || $slug_match ) {
+ if ( is_taxonomy_hierarchical( $taxonomy ) ) {
+ $siblings = get_terms( $taxonomy, array( 'get' => 'all', 'parent' => $parent ) );
+
+ $existing_term = null;
+ if ( $name_match->slug === $slug && in_array( $name, wp_list_pluck( $siblings, 'name' ) ) ) {
+ $existing_term = $name_match;
+ } elseif ( $slug_match && in_array( $slug, wp_list_pluck( $siblings, 'slug' ) ) ) {
+ $existing_term = $slug_match;
+ }
+
+ if ( $existing_term ) {
+ return new WP_Error( 'term_exists', __( 'A term with the name provided already exists with this parent.' ), $existing_term->term_id );
+ }
+ } else {
+ return new WP_Error( 'term_exists', __( 'A term with the name provided already exists in this taxonomy.' ), $name_match->term_id );
+ }
+ }
+ }
+
+ $slug = wp_unique_term_slug( $slug, (object) $args );
+
+ if ( false === $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ) ) {
+ return new WP_Error( 'db_insert_error', __( 'Could not insert term into the database' ), $wpdb->last_error );
+ }
+
+ $term_id = (int) $wpdb->insert_id;
+
+ // Seems unreachable, However, Is used in the case that a term name is provided, which sanitizes to an empty string.
+ if ( empty($slug) ) {
+ $slug = sanitize_title($slug, $term_id);
+
+ /** This action is documented in wp-includes/taxonomy-functions.php */
+ do_action( 'edit_terms', $term_id, $taxonomy );
+ $wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) );
+
+ /** This action is documented in wp-includes/taxonomy-functions.php */
+ do_action( 'edited_terms', $term_id, $taxonomy );
+ }
+
+ $tt_id = $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id ) );
+
+ if ( !empty($tt_id) ) {
+ return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
+ }
+ $wpdb->insert( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent') + array( 'count' => 0 ) );
+ $tt_id = (int) $wpdb->insert_id;
+
+ /*
+ * Sanity check: if we just created a term with the same parent + taxonomy + slug but a higher term_id than
+ * an existing term, then we have unwittingly created a duplicate term. Delete the dupe, and use the term_id
+ * and term_taxonomy_id of the older term instead. Then return out of the function so that the "create" hooks
+ * are not fired.
+ */
+ $duplicate_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.term_id, tt.term_taxonomy_id FROM $wpdb->terms t INNER JOIN $wpdb->term_taxonomy tt ON ( tt.term_id = t.term_id ) WHERE t.slug = %s AND tt.parent = %d AND tt.taxonomy = %s AND t.term_id < %d AND tt.term_taxonomy_id != %d", $slug, $parent, $taxonomy, $term_id, $tt_id ) );
+ if ( $duplicate_term ) {
+ $wpdb->delete( $wpdb->terms, array( 'term_id' => $term_id ) );
+ $wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) );
+
+ $term_id = (int) $duplicate_term->term_id;
+ $tt_id = (int) $duplicate_term->term_taxonomy_id;
+
+ clean_term_cache( $term_id, $taxonomy );
+ return array( 'term_id' => $term_id, 'term_taxonomy_id' => $tt_id );
+ }
+
+ /**
+ * Fires immediately after a new term is created, before the term cache is cleaned.
+ *
+ * @since 2.3.0
+ *
+ * @param int $term_id Term ID.
+ * @param int $tt_id Term taxonomy ID.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ do_action( "create_term", $term_id, $tt_id, $taxonomy );
+
+ /**
+ * Fires after a new term is created for a specific taxonomy.
+ *
+ * The dynamic portion of the hook name, `$taxonomy`, refers
+ * to the slug of the taxonomy the term was created for.
+ *
+ * @since 2.3.0
+ *
+ * @param int $term_id Term ID.
+ * @param int $tt_id Term taxonomy ID.
+ */
+ do_action( "create_$taxonomy", $term_id, $tt_id );
+
+ /**
+ * Filter the term ID after a new term is created.
+ *
+ * @since 2.3.0
+ *
+ * @param int $term_id Term ID.
+ * @param int $tt_id Taxonomy term ID.
+ */
+ $term_id = apply_filters( 'term_id_filter', $term_id, $tt_id );
+
+ clean_term_cache($term_id, $taxonomy);
+
+ /**
+ * Fires after a new term is created, and after the term cache has been cleaned.
+ *
+ * @since 2.3.0
+ *
+ * @param int $term_id Term ID.
+ * @param int $tt_id Term taxonomy ID.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ do_action( 'created_term', $term_id, $tt_id, $taxonomy );
+
+ /**
+ * Fires after a new term in a specific taxonomy is created, and after the term
+ * cache has been cleaned.
+ *
+ * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug.
+ *
+ * @since 2.3.0
+ *
+ * @param int $term_id Term ID.
+ * @param int $tt_id Term taxonomy ID.
+ */
+ do_action( "created_$taxonomy", $term_id, $tt_id );
+
+ return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
+}
+
+/**
+ * Create Term and Taxonomy Relationships.
+ *
+ * Relates an object (post, link etc) to a term and taxonomy type. Creates the
+ * term and taxonomy relationship if it doesn't already exist. Creates a term if
+ * it doesn't exist (using the slug).
+ *
+ * A relationship means that the term is grouped in or belongs to the taxonomy.
+ * A term has no meaning until it is given context by defining which taxonomy it
+ * exists under.
+ *
+ * @since 2.3.0
+ *
+ * @global wpdb $wpdb The WordPress database abstraction object.
+ *
+ * @param int $object_id The object to relate to.
+ * @param array|int|string $terms A single term slug, single term id, or array of either term slugs or ids.
+ * Will replace all existing related terms in this taxonomy.
+ * @param string $taxonomy The context in which to relate the term to the object.
+ * @param bool $append Optional. If false will delete difference of terms. Default false.
+ * @return array|WP_Error Affected Term IDs.
+ */
+function wp_set_object_terms( $object_id, $terms, $taxonomy, $append = false ) {
+ global $wpdb;
+
+ $object_id = (int) $object_id;
+
+ if ( ! taxonomy_exists($taxonomy) )
+ return new WP_Error('invalid_taxonomy', __('Invalid taxonomy'));
+
+ if ( !is_array($terms) )
+ $terms = array($terms);
+
+ if ( ! $append )
+ $old_tt_ids = wp_get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids', 'orderby' => 'none'));
+ else
+ $old_tt_ids = array();
+
+ $tt_ids = array();
+ $term_ids = array();
+ $new_tt_ids = array();
+
+ foreach ( (array) $terms as $term) {
+ if ( !strlen(trim($term)) )
+ continue;
+
+ if ( !$term_info = term_exists($term, $taxonomy) ) {
+ // Skip if a non-existent term ID is passed.
+ if ( is_int($term) )
+ continue;
+ $term_info = wp_insert_term($term, $taxonomy);
+ }
+ if ( is_wp_error($term_info) )
+ return $term_info;
+ $term_ids[] = $term_info['term_id'];
+ $tt_id = $term_info['term_taxonomy_id'];
+ $tt_ids[] = $tt_id;
+
+ if ( $wpdb->get_var( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id = %d", $object_id, $tt_id ) ) )
+ continue;
+
+ /**
+ * Fires immediately before an object-term relationship is added.
+ *
+ * @since 2.9.0
+ *
+ * @param int $object_id Object ID.
+ * @param int $tt_id Term taxonomy ID.
+ */
+ do_action( 'add_term_relationship', $object_id, $tt_id );
+ $wpdb->insert( $wpdb->term_relationships, array( 'object_id' => $object_id, 'term_taxonomy_id' => $tt_id ) );
+
+ /**
+ * Fires immediately after an object-term relationship is added.
+ *
+ * @since 2.9.0
+ *
+ * @param int $object_id Object ID.
+ * @param int $tt_id Term taxonomy ID.
+ */
+ do_action( 'added_term_relationship', $object_id, $tt_id );
+ $new_tt_ids[] = $tt_id;
+ }
+
+ if ( $new_tt_ids )
+ wp_update_term_count( $new_tt_ids, $taxonomy );
+
+ if ( ! $append ) {
+ $delete_tt_ids = array_diff( $old_tt_ids, $tt_ids );
+
+ if ( $delete_tt_ids ) {
+ $in_delete_tt_ids = "'" . implode( "', '", $delete_tt_ids ) . "'";
+ $delete_term_ids = $wpdb->get_col( $wpdb->prepare( "SELECT tt.term_id FROM $wpdb->term_taxonomy AS tt WHERE tt.taxonomy = %s AND tt.term_taxonomy_id IN ($in_delete_tt_ids)", $taxonomy ) );
+ $delete_term_ids = array_map( 'intval', $delete_term_ids );
+
+ $remove = wp_remove_object_terms( $object_id, $delete_term_ids, $taxonomy );
+ if ( is_wp_error( $remove ) ) {
+ return $remove;
+ }
+ }
+ }
+
+ $t = get_taxonomy($taxonomy);
+ if ( ! $append && isset($t->sort) && $t->sort ) {
+ $values = array();
+ $term_order = 0;
+ $final_tt_ids = wp_get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids'));
+ foreach ( $tt_ids as $tt_id )
+ if ( in_array($tt_id, $final_tt_ids) )
+ $values[] = $wpdb->prepare( "(%d, %d, %d)", $object_id, $tt_id, ++$term_order);
+ if ( $values )
+ if ( false === $wpdb->query( "INSERT INTO $wpdb->term_relationships (object_id, term_taxonomy_id, term_order) VALUES " . join( ',', $values ) . " ON DUPLICATE KEY UPDATE term_order = VALUES(term_order)" ) )
+ return new WP_Error( 'db_insert_error', __( 'Could not insert term relationship into the database' ), $wpdb->last_error );
+ }
+
+ wp_cache_delete( $object_id, $taxonomy . '_relationships' );
+
+ /**
+ * Fires after an object's terms have been set.
+ *
+ * @since 2.8.0
+ *
+ * @param int $object_id Object ID.
+ * @param array $terms An array of object terms.
+ * @param array $tt_ids An array of term taxonomy IDs.
+ * @param string $taxonomy Taxonomy slug.
+ * @param bool $append Whether to append new terms to the old terms.
+ * @param array $old_tt_ids Old array of term taxonomy IDs.
+ */
+ do_action( 'set_object_terms', $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids );
+ return $tt_ids;
+}
+
+/**
+ * Add term(s) associated with a given object.
+ *
+ * @since 3.6.0
+ *
+ * @param int $object_id The ID of the object to which the terms will be added.
+ * @param array|int|string $terms The slug(s) or ID(s) of the term(s) to add.
+ * @param array|string $taxonomy Taxonomy name.
+ * @return array|WP_Error Affected Term IDs
+ */
+function wp_add_object_terms( $object_id, $terms, $taxonomy ) {
+ return wp_set_object_terms( $object_id, $terms, $taxonomy, true );
+}
+
+/**
+ * Remove term(s) associated with a given object.
+ *
+ * @since 3.6.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $object_id The ID of the object from which the terms will be removed.
+ * @param array|int|string $terms The slug(s) or ID(s) of the term(s) to remove.
+ * @param array|string $taxonomy Taxonomy name.
+ * @return bool|WP_Error True on success, false or WP_Error on failure.
+ */
+function wp_remove_object_terms( $object_id, $terms, $taxonomy ) {
+ global $wpdb;
+
+ $object_id = (int) $object_id;
+
+ if ( ! taxonomy_exists( $taxonomy ) ) {
+ return new WP_Error( 'invalid_taxonomy', __( 'Invalid Taxonomy' ) );
+ }
+
+ if ( ! is_array( $terms ) ) {
+ $terms = array( $terms );
+ }
+
+ $tt_ids = array();
+
+ foreach ( (array) $terms as $term ) {
+ if ( ! strlen( trim( $term ) ) ) {
+ continue;
+ }
+
+ if ( ! $term_info = term_exists( $term, $taxonomy ) ) {
+ // Skip if a non-existent term ID is passed.
+ if ( is_int( $term ) ) {
+ continue;
+ }
+ }
+
+ if ( is_wp_error( $term_info ) ) {
+ return $term_info;
+ }
+
+ $tt_ids[] = $term_info['term_taxonomy_id'];
+ }
+
+ if ( $tt_ids ) {
+ $in_tt_ids = "'" . implode( "', '", $tt_ids ) . "'";
+
+ /**
+ * Fires immediately before an object-term relationship is deleted.
+ *
+ * @since 2.9.0
+ *
+ * @param int $object_id Object ID.
+ * @param array $tt_ids An array of term taxonomy IDs.
+ */
+ do_action( 'delete_term_relationships', $object_id, $tt_ids );
+ $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
+
+ wp_cache_delete( $object_id, $taxonomy . '_relationships' );
+
+ /**
+ * Fires immediately after an object-term relationship is deleted.
+ *
+ * @since 2.9.0
+ *
+ * @param int $object_id Object ID.
+ * @param array $tt_ids An array of term taxonomy IDs.
+ */
+ do_action( 'deleted_term_relationships', $object_id, $tt_ids );
+
+ wp_update_term_count( $tt_ids, $taxonomy );
+
+ return (bool) $deleted;
+ }
+
+ return false;
+}
+
+/**
+ * Will make slug unique, if it isn't already.
+ *
+ * The `$slug` has to be unique global to every taxonomy, meaning that one
+ * taxonomy term can't have a matching slug with another taxonomy term. Each
+ * slug has to be globally unique for every taxonomy.
+ *
+ * The way this works is that if the taxonomy that the term belongs to is
+ * hierarchical and has a parent, it will append that parent to the $slug.
+ *
+ * If that still doesn't return an unique slug, then it try to append a number
+ * until it finds a number that is truly unique.
+ *
+ * The only purpose for `$term` is for appending a parent, if one exists.
+ *
+ * @since 2.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $slug The string that will be tried for a unique slug.
+ * @param object $term The term object that the `$slug` will belong to.
+ * @return string Will return a true unique slug.
+ */
+function wp_unique_term_slug( $slug, $term ) {
+ global $wpdb;
+
+ $needs_suffix = true;
+ $original_slug = $slug;
+
+ // As of 4.1, duplicate slugs are allowed as long as they're in different taxonomies.
+ if ( ! term_exists( $slug ) || get_option( 'db_version' ) >= 30133 && ! get_term_by( 'slug', $slug, $term->taxonomy ) ) {
+ $needs_suffix = false;
+ }
+
+ /*
+ * If the taxonomy supports hierarchy and the term has a parent, make the slug unique
+ * by incorporating parent slugs.
+ */
+ $parent_suffix = '';
+ if ( $needs_suffix && is_taxonomy_hierarchical( $term->taxonomy ) && ! empty( $term->parent ) ) {
+ $the_parent = $term->parent;
+ while ( ! empty($the_parent) ) {
+ $parent_term = get_term($the_parent, $term->taxonomy);
+ if ( is_wp_error($parent_term) || empty($parent_term) )
+ break;
+ $parent_suffix .= '-' . $parent_term->slug;
+ if ( ! term_exists( $slug . $parent_suffix ) ) {
+ break;
+ }
+
+ if ( empty($parent_term->parent) )
+ break;
+ $the_parent = $parent_term->parent;
+ }
+ }
+
+ // If we didn't get a unique slug, try appending a number to make it unique.
+
+ /**
+ * Filter whether the proposed unique term slug is bad.
+ *
+ * @since 4.3.0
+ *
+ * @param bool $needs_suffix Whether the slug needs to be made unique with a suffix.
+ * @param string $slug The slug.
+ * @param object $term Term object.
+ */
+ if ( apply_filters( 'wp_unique_term_slug_is_bad_slug', $needs_suffix, $slug, $term ) ) {
+ if ( $parent_suffix ) {
+ $slug .= $parent_suffix;
+ } else {
+ if ( ! empty( $term->term_id ) )
+ $query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s AND term_id != %d", $slug, $term->term_id );
+ else
+ $query = $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $slug );
+
+ if ( $wpdb->get_var( $query ) ) {
+ $num = 2;
+ do {
+ $alt_slug = $slug . "-$num";
+ $num++;
+ $slug_check = $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $alt_slug ) );
+ } while ( $slug_check );
+ $slug = $alt_slug;
+ }
+ }
+ }
+
+ /**
+ * Filter the unique term slug.
+ *
+ * @since 4.3.0
+ *
+ * @param string $slug Unique term slug.
+ * @param object $term Term object.
+ * @param string $original_slug Slug originally passed to the function for testing.
+ */
+ return apply_filters( 'wp_unique_term_slug', $slug, $term, $original_slug );
+}
+
+/**
+ * Update term based on arguments provided.
+ *
+ * The $args will indiscriminately override all values with the same field name.
+ * Care must be taken to not override important information need to update or
+ * update will fail (or perhaps create a new term, neither would be acceptable).
+ *
+ * Defaults will set 'alias_of', 'description', 'parent', and 'slug' if not
+ * defined in $args already.
+ *
+ * 'alias_of' will create a term group, if it doesn't already exist, and update
+ * it for the $term.
+ *
+ * If the 'slug' argument in $args is missing, then the 'name' in $args will be
+ * used. It should also be noted that if you set 'slug' and it isn't unique then
+ * a WP_Error will be passed back. If you don't pass any slug, then a unique one
+ * will be created for you.
+ *
+ * For what can be overrode in `$args`, check the term scheme can contain and stay
+ * away from the term keys.
+ *
+ * @since 2.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $term_id The ID of the term
+ * @param string $taxonomy The context in which to relate the term to the object.
+ * @param array|string $args Optional. Array of get_terms() arguments. Default empty array.
+ * @return array|WP_Error Returns Term ID and Taxonomy Term ID
+ */
+function wp_update_term( $term_id, $taxonomy, $args = array() ) {
+ global $wpdb;
+
+ if ( ! taxonomy_exists( $taxonomy ) ) {
+ return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
+ }
+
+ $term_id = (int) $term_id;
+
+ // First, get all of the original args
+ $term = get_term( $term_id, $taxonomy );
+
+ if ( is_wp_error( $term ) ) {
+ return $term;
+ }
+
+ if ( ! $term ) {
+ return new WP_Error( 'invalid_term', __( 'Empty Term' ) );
+ }
+
+ $term = (array) $term->data;
+
+ // Escape data pulled from DB.
+ $term = wp_slash( $term );
+
+ // Merge old and new args with new args overwriting old ones.
+ $args = array_merge($term, $args);
+
+ $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => '');
+ $args = wp_parse_args($args, $defaults);
+ $args = sanitize_term($args, $taxonomy, 'db');
+ $parsed_args = $args;
+
+ // expected_slashed ($name)
+ $name = wp_unslash( $args['name'] );
+ $description = wp_unslash( $args['description'] );
+
+ $parsed_args['name'] = $name;
+ $parsed_args['description'] = $description;
+
+ if ( '' == trim($name) )
+ return new WP_Error('empty_term_name', __('A name is required for this term'));
+
+ if ( $parsed_args['parent'] > 0 && ! term_exists( (int) $parsed_args['parent'] ) ) {
+ return new WP_Error( 'missing_parent', __( 'Parent term does not exist.' ) );
+ }
+
+ $empty_slug = false;
+ if ( empty( $args['slug'] ) ) {
+ $empty_slug = true;
+ $slug = sanitize_title($name);
+ } else {
+ $slug = $args['slug'];
+ }
+
+ $parsed_args['slug'] = $slug;
+
+ $term_group = isset( $parsed_args['term_group'] ) ? $parsed_args['term_group'] : 0;
+ if ( $args['alias_of'] ) {
+ $alias = get_term_by( 'slug', $args['alias_of'], $taxonomy );
+ if ( ! empty( $alias->term_group ) ) {
+ // The alias we want is already in a group, so let's use that one.
+ $term_group = $alias->term_group;
+ } elseif ( ! empty( $alias->term_id ) ) {
+ /*
+ * The alias is not in a group, so we create a new one
+ * and add the alias to it.
+ */
+ $term_group = $wpdb->get_var("SELECT MAX(term_group) FROM $wpdb->terms") + 1;
+
+ wp_update_term( $alias->term_id, $taxonomy, array(
+ 'term_group' => $term_group,
+ ) );
+ }
+
+ $parsed_args['term_group'] = $term_group;
+ }
+
+ /**
+ * Filter the term parent.
+ *
+ * Hook to this filter to see if it will cause a hierarchy loop.
+ *
+ * @since 3.1.0
+ *
+ * @param int $parent ID of the parent term.
+ * @param int $term_id Term ID.
+ * @param string $taxonomy Taxonomy slug.
+ * @param array $parsed_args An array of potentially altered update arguments for the given term.
+ * @param array $args An array of update arguments for the given term.
+ */
+ $parent = apply_filters( 'wp_update_term_parent', $args['parent'], $term_id, $taxonomy, $parsed_args, $args );
+
+ // Check for duplicate slug
+ $duplicate = get_term_by( 'slug', $slug, $taxonomy );
+ if ( $duplicate && $duplicate->term_id != $term_id ) {
+ // If an empty slug was passed or the parent changed, reset the slug to something unique.
+ // Otherwise, bail.
+ if ( $empty_slug || ( $parent != $term['parent']) )
+ $slug = wp_unique_term_slug($slug, (object) $args);
+ else
+ return new WP_Error('duplicate_term_slug', sprintf(__('The slug “%s” is already in use by another term'), $slug));
+ }
+
+ $tt_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) );
+
+ // Check whether this is a shared term that needs splitting.
+ $_term_id = _split_shared_term( $term_id, $tt_id );
+ if ( ! is_wp_error( $_term_id ) ) {
+ $term_id = $_term_id;
+ }
+
+ /**
+ * Fires immediately before the given terms are edited.
+ *
+ * @since 2.9.0
+ *
+ * @param int $term_id Term ID.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ do_action( 'edit_terms', $term_id, $taxonomy );
+ $wpdb->update($wpdb->terms, compact( 'name', 'slug', 'term_group' ), compact( 'term_id' ) );
+ if ( empty($slug) ) {
+ $slug = sanitize_title($name, $term_id);
+ $wpdb->update( $wpdb->terms, compact( 'slug' ), compact( 'term_id' ) );
+ }
+
+ /**
+ * Fires immediately after the given terms are edited.
+ *
+ * @since 2.9.0
+ *
+ * @param int $term_id Term ID
+ * @param string $taxonomy Taxonomy slug.
+ */
+ do_action( 'edited_terms', $term_id, $taxonomy );
+
+ /**
+ * Fires immediate before a term-taxonomy relationship is updated.
+ *
+ * @since 2.9.0
+ *
+ * @param int $tt_id Term taxonomy ID.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ do_action( 'edit_term_taxonomy', $tt_id, $taxonomy );
+
+ $wpdb->update( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent' ), array( 'term_taxonomy_id' => $tt_id ) );
+
+ /**
+ * Fires immediately after a term-taxonomy relationship is updated.
+ *
+ * @since 2.9.0
+ *
+ * @param int $tt_id Term taxonomy ID.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ do_action( 'edited_term_taxonomy', $tt_id, $taxonomy );
+
+ // Clean the relationship caches for all object types using this term.
+ $objects = $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $tt_id ) );
+ $tax_object = get_taxonomy( $taxonomy );
+ foreach ( $tax_object->object_type as $object_type ) {
+ clean_object_term_cache( $objects, $object_type );
+ }
+
+ /**
+ * Fires after a term has been updated, but before the term cache has been cleaned.
+ *
+ * @since 2.3.0
+ *
+ * @param int $term_id Term ID.
+ * @param int $tt_id Term taxonomy ID.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ do_action( "edit_term", $term_id, $tt_id, $taxonomy );
+
+ /**
+ * Fires after a term in a specific taxonomy has been updated, but before the term
+ * cache has been cleaned.
+ *
+ * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug.
+ *
+ * @since 2.3.0
+ *
+ * @param int $term_id Term ID.
+ * @param int $tt_id Term taxonomy ID.
+ */
+ do_action( "edit_$taxonomy", $term_id, $tt_id );
+
+ /** This filter is documented in wp-includes/taxonomy-functions.php */
+ $term_id = apply_filters( 'term_id_filter', $term_id, $tt_id );
+
+ clean_term_cache($term_id, $taxonomy);
+
+ /**
+ * Fires after a term has been updated, and the term cache has been cleaned.
+ *
+ * @since 2.3.0
+ *
+ * @param int $term_id Term ID.
+ * @param int $tt_id Term taxonomy ID.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ do_action( "edited_term", $term_id, $tt_id, $taxonomy );
+
+ /**
+ * Fires after a term for a specific taxonomy has been updated, and the term
+ * cache has been cleaned.
+ *
+ * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug.
+ *
+ * @since 2.3.0
+ *
+ * @param int $term_id Term ID.
+ * @param int $tt_id Term taxonomy ID.
+ */
+ do_action( "edited_$taxonomy", $term_id, $tt_id );
+
+ return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
+}
+
+/**
+ * Enable or disable term counting.
+ *
+ * @since 2.5.0
+ *
+ * @staticvar bool $_defer
+ *
+ * @param bool $defer Optional. Enable if true, disable if false.
+ * @return bool Whether term counting is enabled or disabled.
+ */
+function wp_defer_term_counting($defer=null) {
+ static $_defer = false;
+
+ if ( is_bool($defer) ) {
+ $_defer = $defer;
+ // flush any deferred counts
+ if ( !$defer )
+ wp_update_term_count( null, null, true );
+ }
+
+ return $_defer;
+}
+
+/**
+ * Updates the amount of terms in taxonomy.
+ *
+ * If there is a taxonomy callback applied, then it will be called for updating
+ * the count.
+ *
+ * The default action is to count what the amount of terms have the relationship
+ * of term ID. Once that is done, then update the database.
+ *
+ * @since 2.3.0
+ *
+ * @staticvar array $_deferred
+ *
+ * @param int|array $terms The term_taxonomy_id of the terms.
+ * @param string $taxonomy The context of the term.
+ * @return bool If no terms will return false, and if successful will return true.
+ */
+function wp_update_term_count( $terms, $taxonomy, $do_deferred=false ) {
+ static $_deferred = array();
+
+ if ( $do_deferred ) {
+ foreach ( (array) array_keys($_deferred) as $tax ) {
+ wp_update_term_count_now( $_deferred[$tax], $tax );
+ unset( $_deferred[$tax] );
+ }
+ }
+
+ if ( empty($terms) )
+ return false;
+
+ if ( !is_array($terms) )
+ $terms = array($terms);
+
+ if ( wp_defer_term_counting() ) {
+ if ( !isset($_deferred[$taxonomy]) )
+ $_deferred[$taxonomy] = array();
+ $_deferred[$taxonomy] = array_unique( array_merge($_deferred[$taxonomy], $terms) );
+ return true;
+ }
+
+ return wp_update_term_count_now( $terms, $taxonomy );
+}
+
+/**
+ * Perform term count update immediately.
+ *
+ * @since 2.5.0
+ *
+ * @param array $terms The term_taxonomy_id of terms to update.
+ * @param string $taxonomy The context of the term.
+ * @return true Always true when complete.
+ */
+function wp_update_term_count_now( $terms, $taxonomy ) {
+ $terms = array_map('intval', $terms);
+
+ $taxonomy = get_taxonomy($taxonomy);
+ if ( !empty($taxonomy->update_count_callback) ) {
+ call_user_func($taxonomy->update_count_callback, $terms, $taxonomy);
+ } else {
+ $object_types = (array) $taxonomy->object_type;
+ foreach ( $object_types as &$object_type ) {
+ if ( 0 === strpos( $object_type, 'attachment:' ) )
+ list( $object_type ) = explode( ':', $object_type );
+ }
+
+ if ( $object_types == array_filter( $object_types, 'post_type_exists' ) ) {
+ // Only post types are attached to this taxonomy
+ _update_post_term_count( $terms, $taxonomy );
+ } else {
+ // Default count updater
+ _update_generic_term_count( $terms, $taxonomy );
+ }
+ }
+
+ clean_term_cache($terms, '', false);
+
+ return true;
+}
+
+//
+// Cache
+//
+
+/**
+ * Removes the taxonomy relationship to terms from the cache.
+ *
+ * Will remove the entire taxonomy relationship containing term `$object_id`. The
+ * term IDs have to exist within the taxonomy `$object_type` for the deletion to
+ * take place.
+ *
+ * @since 2.3.0
+ *
+ * @see get_object_taxonomies() for more on $object_type.
+ *
+ * @param int|array $object_ids Single or list of term object ID(s).
+ * @param array|string $object_type The taxonomy object type.
+ */
+function clean_object_term_cache($object_ids, $object_type) {
+ if ( !is_array($object_ids) )
+ $object_ids = array($object_ids);
+
+ $taxonomies = get_object_taxonomies( $object_type );
+
+ foreach ( $object_ids as $id ) {
+ foreach ( $taxonomies as $taxonomy ) {
+ wp_cache_delete($id, "{$taxonomy}_relationships");
+ }
+ }
+
+ /**
+ * Fires after the object term cache has been cleaned.
+ *
+ * @since 2.5.0
+ *
+ * @param array $object_ids An array of object IDs.
+ * @param string $objet_type Object type.
+ */
+ do_action( 'clean_object_term_cache', $object_ids, $object_type );
+}
+
+/**
+ * Will remove all of the term ids from the cache.
+ *
+ * @since 2.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ * @global bool $_wp_suspend_cache_invalidation
+ *
+ * @param int|array $ids Single or list of Term IDs.
+ * @param string $taxonomy Optional. Can be empty and will assume `tt_ids`, else will use for context.
+ * Default empty.
+ * @param bool $clean_taxonomy Optional. Whether to clean taxonomy wide caches (true), or just individual
+ * term object caches (false). Default true.
+ */
+function clean_term_cache($ids, $taxonomy = '', $clean_taxonomy = true) {
+ global $wpdb, $_wp_suspend_cache_invalidation;
+
+ if ( ! empty( $_wp_suspend_cache_invalidation ) ) {
+ return;
+ }
+
+ if ( !is_array($ids) )
+ $ids = array($ids);
+
+ $taxonomies = array();
+ // If no taxonomy, assume tt_ids.
+ if ( empty($taxonomy) ) {
+ $tt_ids = array_map('intval', $ids);
+ $tt_ids = implode(', ', $tt_ids);
+ $terms = $wpdb->get_results("SELECT term_id, taxonomy FROM $wpdb->term_taxonomy WHERE term_taxonomy_id IN ($tt_ids)");
+ $ids = array();
+ foreach ( (array) $terms as $term ) {
+ $taxonomies[] = $term->taxonomy;
+ $ids[] = $term->term_id;
+ wp_cache_delete( $term->term_id, 'terms' );
+ }
+ $taxonomies = array_unique($taxonomies);
+ } else {
+ $taxonomies = array($taxonomy);
+ foreach ( $taxonomies as $taxonomy ) {
+ foreach ( $ids as $id ) {
+ wp_cache_delete( $id, 'terms' );
+ }
+ }
+ }
+
+ foreach ( $taxonomies as $taxonomy ) {
+ if ( $clean_taxonomy ) {
+ wp_cache_delete('all_ids', $taxonomy);
+ wp_cache_delete('get', $taxonomy);
+ delete_option("{$taxonomy}_children");
+ // Regenerate {$taxonomy}_children
+ _get_term_hierarchy($taxonomy);
+ }
+
+ /**
+ * Fires once after each taxonomy's term cache has been cleaned.
+ *
+ * @since 2.5.0
+ *
+ * @param array $ids An array of term IDs.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ do_action( 'clean_term_cache', $ids, $taxonomy );
+ }
+
+ wp_cache_set( 'last_changed', microtime(), 'terms' );
+}
+
+/**
+ * Retrieves the taxonomy relationship to the term object id.
+ *
+ * @since 2.3.0
+ *
+ * @param int $id Term object ID.
+ * @param string $taxonomy Taxonomy name.
+ * @return bool|mixed Empty array if $terms found, but not `$taxonomy`. False if nothing is in cache
+ * for `$taxonomy` and `$id`.
+ */
+function get_object_term_cache( $id, $taxonomy ) {
+ return wp_cache_get( $id, "{$taxonomy}_relationships" );
+}
+
+/**
+ * Updates the cache for the given term object ID(s).
+ *
+ * Note: Due to performance concerns, great care should be taken to only update
+ * term caches when necessary. Processing time can increase exponentially depending
+ * on both the number of passed term IDs and the number of taxonomies those terms
+ * belong to.
+ *
+ * Caches will only be updated for terms not already cached.
+ *
+ * @since 2.3.0
+ *
+ * @param string|array $object_ids Comma-separated list or array of term object IDs.
+ * @param array|string $object_type The taxonomy object type.
+ * @return void|false False if all of the terms in `$object_ids` are already cached.
+ */
+function update_object_term_cache($object_ids, $object_type) {
+ if ( empty($object_ids) )
+ return;
+
+ if ( !is_array($object_ids) )
+ $object_ids = explode(',', $object_ids);
+
+ $object_ids = array_map('intval', $object_ids);
+
+ $taxonomies = get_object_taxonomies($object_type);
+
+ $ids = array();
+ foreach ( (array) $object_ids as $id ) {
+ foreach ( $taxonomies as $taxonomy ) {
+ if ( false === wp_cache_get($id, "{$taxonomy}_relationships") ) {
+ $ids[] = $id;
+ break;
+ }
+ }
+ }
+
+ if ( empty( $ids ) )
+ return false;
+
+ $terms = wp_get_object_terms( $ids, $taxonomies, array(
+ 'fields' => 'all_with_object_id',
+ 'orderby' => 'none',
+ 'update_term_meta_cache' => false,
+ ) );
+
+ $object_terms = array();
+ foreach ( (array) $terms as $term )
+ $object_terms[$term->object_id][$term->taxonomy][] = $term;
+
+ foreach ( $ids as $id ) {
+ foreach ( $taxonomies as $taxonomy ) {
+ if ( ! isset($object_terms[$id][$taxonomy]) ) {
+ if ( !isset($object_terms[$id]) )
+ $object_terms[$id] = array();
+ $object_terms[$id][$taxonomy] = array();
+ }
+ }
+ }
+
+ foreach ( $object_terms as $id => $value ) {
+ foreach ( $value as $taxonomy => $terms ) {
+ wp_cache_add( $id, $terms, "{$taxonomy}_relationships" );
+ }
+ }
+}
+
+/**
+ * Updates Terms to Taxonomy in cache.
+ *
+ * @since 2.3.0
+ *
+ * @param array $terms List of term objects to change.
+ * @param string $taxonomy Optional. Update Term to this taxonomy in cache. Default empty.
+ */
+function update_term_cache( $terms, $taxonomy = '' ) {
+ foreach ( (array) $terms as $term ) {
+ // Create a copy in case the array was passed by reference.
+ $_term = $term;
+
+ // Object ID should not be cached.
+ unset( $_term->object_id );
+
+ wp_cache_add( $term->term_id, $_term, 'terms' );
+ }
+}
+
+//
+// Private
+//
+
+/**
+ * Retrieves children of taxonomy as Term IDs.
+ *
+ * @ignore
+ * @since 2.3.0
+ *
+ * @param string $taxonomy Taxonomy name.
+ * @return array Empty if $taxonomy isn't hierarchical or returns children as Term IDs.
+ */
+function _get_term_hierarchy( $taxonomy ) {
+ if ( !is_taxonomy_hierarchical($taxonomy) )
+ return array();
+ $children = get_option("{$taxonomy}_children");
+
+ if ( is_array($children) )
+ return $children;
+ $children = array();
+ $terms = get_terms($taxonomy, array('get' => 'all', 'orderby' => 'id', 'fields' => 'id=>parent'));
+ foreach ( $terms as $term_id => $parent ) {
+ if ( $parent > 0 )
+ $children[$parent][] = $term_id;
+ }
+ update_option("{$taxonomy}_children", $children);
+
+ return $children;
+}
+
+/**
+ * Get the subset of $terms that are descendants of $term_id.
+ *
+ * If `$terms` is an array of objects, then _get_term_children() returns an array of objects.
+ * If `$terms` is an array of IDs, then _get_term_children() returns an array of IDs.
+ *
+ * @access private
+ * @since 2.3.0
+ *
+ * @param int $term_id The ancestor term: all returned terms should be descendants of `$term_id`.
+ * @param array $terms The set of terms - either an array of term objects or term IDs - from which those that
+ * are descendants of $term_id will be chosen.
+ * @param string $taxonomy The taxonomy which determines the hierarchy of the terms.
+ * @param array $ancestors Optional. Term ancestors that have already been identified. Passed by reference, to keep
+ * track of found terms when recursing the hierarchy. The array of located ancestors is used
+ * to prevent infinite recursion loops. For performance, `term_ids` are used as array keys,
+ * with 1 as value. Default empty array.
+ * @return array|WP_Error The subset of $terms that are descendants of $term_id.
+ */
+function _get_term_children( $term_id, $terms, $taxonomy, &$ancestors = array() ) {
+ $empty_array = array();
+ if ( empty($terms) )
+ return $empty_array;
+
+ $term_list = array();
+ $has_children = _get_term_hierarchy($taxonomy);
+
+ if ( ( 0 != $term_id ) && ! isset($has_children[$term_id]) )
+ return $empty_array;
+
+ // Include the term itself in the ancestors array, so we can properly detect when a loop has occurred.
+ if ( empty( $ancestors ) ) {
+ $ancestors[ $term_id ] = 1;
+ }
+
+ foreach ( (array) $terms as $term ) {
+ $use_id = false;
+ if ( !is_object($term) ) {
+ $term = get_term($term, $taxonomy);
+ if ( is_wp_error( $term ) )
+ return $term;
+ $use_id = true;
+ }
+
+ // Don't recurse if we've already identified the term as a child - this indicates a loop.
+ if ( isset( $ancestors[ $term->term_id ] ) ) {
+ continue;
+ }
+
+ if ( $term->parent == $term_id ) {
+ if ( $use_id )
+ $term_list[] = $term->term_id;
+ else
+ $term_list[] = $term;
+
+ if ( !isset($has_children[$term->term_id]) )
+ continue;
+
+ $ancestors[ $term->term_id ] = 1;
+
+ if ( $children = _get_term_children( $term->term_id, $terms, $taxonomy, $ancestors) )
+ $term_list = array_merge($term_list, $children);
+ }
+ }
+
+ return $term_list;
+}
+
+/**
+ * Add count of children to parent count.
+ *
+ * Recalculates term counts by including items from child terms. Assumes all
+ * relevant children are already in the $terms argument.
+ *
+ * @access private
+ * @since 2.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array $terms List of term objects, passed by reference.
+ * @param string $taxonomy Term context.
+ */
+function _pad_term_counts( &$terms, $taxonomy ) {
+ global $wpdb;
+
+ // This function only works for hierarchical taxonomies like post categories.
+ if ( !is_taxonomy_hierarchical( $taxonomy ) )
+ return;
+
+ $term_hier = _get_term_hierarchy($taxonomy);
+
+ if ( empty($term_hier) )
+ return;
+
+ $term_items = array();
+ $terms_by_id = array();
+ $term_ids = array();
+
+ foreach ( (array) $terms as $key => $term ) {
+ $terms_by_id[$term->term_id] = & $terms[$key];
+ $term_ids[$term->term_taxonomy_id] = $term->term_id;
+ }
+
+ // Get the object and term ids and stick them in a lookup table.
+ $tax_obj = get_taxonomy($taxonomy);
+ $object_types = esc_sql($tax_obj->object_type);
+ $results = $wpdb->get_results("SELECT object_id, term_taxonomy_id FROM $wpdb->term_relationships INNER JOIN $wpdb->posts ON object_id = ID WHERE term_taxonomy_id IN (" . implode(',', array_keys($term_ids)) . ") AND post_type IN ('" . implode("', '", $object_types) . "') AND post_status = 'publish'");
+ foreach ( $results as $row ) {
+ $id = $term_ids[$row->term_taxonomy_id];
+ $term_items[$id][$row->object_id] = isset($term_items[$id][$row->object_id]) ? ++$term_items[$id][$row->object_id] : 1;
+ }
+
+ // Touch every ancestor's lookup row for each post in each term.
+ foreach ( $term_ids as $term_id ) {
+ $child = $term_id;
+ $ancestors = array();
+ while ( !empty( $terms_by_id[$child] ) && $parent = $terms_by_id[$child]->parent ) {
+ $ancestors[] = $child;
+ if ( !empty( $term_items[$term_id] ) )
+ foreach ( $term_items[$term_id] as $item_id => $touches ) {
+ $term_items[$parent][$item_id] = isset($term_items[$parent][$item_id]) ? ++$term_items[$parent][$item_id]: 1;
+ }
+ $child = $parent;
+
+ if ( in_array( $parent, $ancestors ) ) {
+ break;
+ }
+ }
+ }
+
+ // Transfer the touched cells.
+ foreach ( (array) $term_items as $id => $items )
+ if ( isset($terms_by_id[$id]) )
+ $terms_by_id[$id]->count = count($items);
+}
+
+//
+// Default callbacks
+//
+
+/**
+ * Will update term count based on object types of the current taxonomy.
+ *
+ * Private function for the default callback for post_tag and category
+ * taxonomies.
+ *
+ * @access private
+ * @since 2.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array $terms List of Term taxonomy IDs.
+ * @param object $taxonomy Current taxonomy object of terms.
+ */
+function _update_post_term_count( $terms, $taxonomy ) {
+ global $wpdb;
+
+ $object_types = (array) $taxonomy->object_type;
+
+ foreach ( $object_types as &$object_type )
+ list( $object_type ) = explode( ':', $object_type );
+
+ $object_types = array_unique( $object_types );
+
+ if ( false !== ( $check_attachments = array_search( 'attachment', $object_types ) ) ) {
+ unset( $object_types[ $check_attachments ] );
+ $check_attachments = true;
+ }
+
+ if ( $object_types )
+ $object_types = esc_sql( array_filter( $object_types, 'post_type_exists' ) );
+
+ foreach ( (array) $terms as $term ) {
+ $count = 0;
+
+ // Attachments can be 'inherit' status, we need to base count off the parent's status if so.
+ if ( $check_attachments )
+ $count += (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts p1 WHERE p1.ID = $wpdb->term_relationships.object_id AND ( post_status = 'publish' OR ( post_status = 'inherit' AND post_parent > 0 AND ( SELECT post_status FROM $wpdb->posts WHERE ID = p1.post_parent ) = 'publish' ) ) AND post_type = 'attachment' AND term_taxonomy_id = %d", $term ) );
+
+ if ( $object_types )
+ $count += (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type IN ('" . implode("', '", $object_types ) . "') AND term_taxonomy_id = %d", $term ) );
+
+ /** This action is documented in wp-includes/taxonomy-functions.php */
+ do_action( 'edit_term_taxonomy', $term, $taxonomy->name );
+ $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
+
+ /** This action is documented in wp-includes/taxonomy-functions.php */
+ do_action( 'edited_term_taxonomy', $term, $taxonomy->name );
+ }
+}
+
+/**
+ * Will update term count based on number of objects.
+ *
+ * Default callback for the 'link_category' taxonomy.
+ *
+ * @since 3.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array $terms List of term taxonomy IDs.
+ * @param object $taxonomy Current taxonomy object of terms.
+ */
+function _update_generic_term_count( $terms, $taxonomy ) {
+ global $wpdb;
+
+ foreach ( (array) $terms as $term ) {
+ $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $term ) );
+
+ /** This action is documented in wp-includes/taxonomy-functions.php */
+ do_action( 'edit_term_taxonomy', $term, $taxonomy->name );
+ $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
+
+ /** This action is documented in wp-includes/taxonomy-functions.php */
+ do_action( 'edited_term_taxonomy', $term, $taxonomy->name );
+ }
+}
+
+/**
+ * Create a new term for a term_taxonomy item that currently shares its term
+ * with another term_taxonomy.
+ *
+ * @ignore
+ * @since 4.2.0
+ * @since 4.3.0 Introduced `$record` parameter. Also, `$term_id` and
+ * `$term_taxonomy_id` can now accept objects.
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int|object $term_id ID of the shared term, or the shared term object.
+ * @param int|object $term_taxonomy_id ID of the term_taxonomy item to receive a new term, or the term_taxonomy object
+ * (corresponding to a row from the term_taxonomy table).
+ * @param bool $record Whether to record data about the split term in the options table. The recording
+ * process has the potential to be resource-intensive, so during batch operations
+ * it can be beneficial to skip inline recording and do it just once, after the
+ * batch is processed. Only set this to `false` if you know what you are doing.
+ * Default: true.
+ * @return int|WP_Error When the current term does not need to be split (or cannot be split on the current
+ * database schema), `$term_id` is returned. When the term is successfully split, the
+ * new term_id is returned. A WP_Error is returned for miscellaneous errors.
+ */
+function _split_shared_term( $term_id, $term_taxonomy_id, $record = true ) {
+ global $wpdb;
+
+ if ( is_object( $term_id ) ) {
+ $shared_term = $term_id;
+ $term_id = intval( $shared_term->term_id );
+ }
+
+ if ( is_object( $term_taxonomy_id ) ) {
+ $term_taxonomy = $term_taxonomy_id;
+ $term_taxonomy_id = intval( $term_taxonomy->term_taxonomy_id );
+ }
+
+ // If there are no shared term_taxonomy rows, there's nothing to do here.
+ $shared_tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy tt WHERE tt.term_id = %d AND tt.term_taxonomy_id != %d", $term_id, $term_taxonomy_id ) );
+
+ if ( ! $shared_tt_count ) {
+ return $term_id;
+ }
+
+ /*
+ * Verify that the term_taxonomy_id passed to the function is actually associated with the term_id.
+ * If there's a mismatch, it may mean that the term is already split. Return the actual term_id from the db.
+ */
+ $check_term_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $term_taxonomy_id ) );
+ if ( $check_term_id != $term_id ) {
+ return $check_term_id;
+ }
+
+ // Pull up data about the currently shared slug, which we'll use to populate the new one.
+ if ( empty( $shared_term ) ) {
+ $shared_term = $wpdb->get_row( $wpdb->prepare( "SELECT t.* FROM $wpdb->terms t WHERE t.term_id = %d", $term_id ) );
+ }
+
+ $new_term_data = array(
+ 'name' => $shared_term->name,
+ 'slug' => $shared_term->slug,
+ 'term_group' => $shared_term->term_group,
+ );
+
+ if ( false === $wpdb->insert( $wpdb->terms, $new_term_data ) ) {
+ return new WP_Error( 'db_insert_error', __( 'Could not split shared term.' ), $wpdb->last_error );
+ }
+
+ $new_term_id = (int) $wpdb->insert_id;
+
+ // Update the existing term_taxonomy to point to the newly created term.
+ $wpdb->update( $wpdb->term_taxonomy,
+ array( 'term_id' => $new_term_id ),
+ array( 'term_taxonomy_id' => $term_taxonomy_id )
+ );
+
+ // Reassign child terms to the new parent.
+ if ( empty( $term_taxonomy ) ) {
+ $term_taxonomy = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $term_taxonomy_id ) );
+ }
+
+ $children_tt_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE parent = %d AND taxonomy = %s", $term_id, $term_taxonomy->taxonomy ) );
+ if ( ! empty( $children_tt_ids ) ) {
+ foreach ( $children_tt_ids as $child_tt_id ) {
+ $wpdb->update( $wpdb->term_taxonomy,
+ array( 'parent' => $new_term_id ),
+ array( 'term_taxonomy_id' => $child_tt_id )
+ );
+ clean_term_cache( $term_id, $term_taxonomy->taxonomy );
+ }
+ } else {
+ // If the term has no children, we must force its taxonomy cache to be rebuilt separately.
+ clean_term_cache( $new_term_id, $term_taxonomy->taxonomy );
+ }
+
+ // Clean the cache for term taxonomies formerly shared with the current term.
+ $shared_term_taxonomies = $wpdb->get_row( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) );
+ if ( $shared_term_taxonomies ) {
+ foreach ( $shared_term_taxonomies as $shared_term_taxonomy ) {
+ clean_term_cache( $term_id, $shared_term_taxonomy );
+ }
+ }
+
+ // Keep a record of term_ids that have been split, keyed by old term_id. See {@see wp_get_split_term()}.
+ if ( $record ) {
+ $split_term_data = get_option( '_split_terms', array() );
+ if ( ! isset( $split_term_data[ $term_id ] ) ) {
+ $split_term_data[ $term_id ] = array();
+ }
+
+ $split_term_data[ $term_id ][ $term_taxonomy->taxonomy ] = $new_term_id;
+ update_option( '_split_terms', $split_term_data );
+ }
+
+ // If we've just split the final shared term, set the "finished" flag.
+ $shared_terms_exist = $wpdb->get_results(
+ "SELECT tt.term_id, t.*, count(*) as term_tt_count FROM {$wpdb->term_taxonomy} tt
+ LEFT JOIN {$wpdb->terms} t ON t.term_id = tt.term_id
+ GROUP BY t.term_id
+ HAVING term_tt_count > 1
+ LIMIT 1"
+ );
+ if ( ! $shared_terms_exist ) {
+ update_option( 'finished_splitting_shared_terms', true );
+ }
+
+ /**
+ * Fires after a previously shared taxonomy term is split into two separate terms.
+ *
+ * @since 4.2.0
+ *
+ * @param int $term_id ID of the formerly shared term.
+ * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
+ * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
+ * @param string $taxonomy Taxonomy for the split term.
+ */
+ do_action( 'split_shared_term', $term_id, $new_term_id, $term_taxonomy_id, $term_taxonomy->taxonomy );
+
+ return $new_term_id;
+}
+
+/**
+ * Splits a batch of shared taxonomy terms.
+ *
+ * @since 4.3.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ */
+function _wp_batch_split_terms() {
+ global $wpdb;
+
+ $lock_name = 'term_split.lock';
+
+ // Try to lock.
+ $lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", $lock_name, time() ) );
+
+ if ( ! $lock_result ) {
+ $lock_result = get_option( $lock_name );
+
+ // Bail if we were unable to create a lock, or if the existing lock is still valid.
+ if ( ! $lock_result || ( $lock_result > ( time() - HOUR_IN_SECONDS ) ) ) {
+ wp_schedule_single_event( time() + ( 5 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' );
+ return;
+ }
+ }
+
+ // Update the lock, as by this point we've definitely got a lock, just need to fire the actions.
+ update_option( $lock_name, time() );
+
+ // Get a list of shared terms (those with more than one associated row in term_taxonomy).
+ $shared_terms = $wpdb->get_results(
+ "SELECT tt.term_id, t.*, count(*) as term_tt_count FROM {$wpdb->term_taxonomy} tt
+ LEFT JOIN {$wpdb->terms} t ON t.term_id = tt.term_id
+ GROUP BY t.term_id
+ HAVING term_tt_count > 1
+ LIMIT 10"
+ );
+
+ // No more terms, we're done here.
+ if ( ! $shared_terms ) {
+ update_option( 'finished_splitting_shared_terms', true );
+ delete_option( $lock_name );
+ return;
+ }
+
+ // Shared terms found? We'll need to run this script again.
+ wp_schedule_single_event( time() + ( 2 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' );
+
+ // Rekey shared term array for faster lookups.
+ $_shared_terms = array();
+ foreach ( $shared_terms as $shared_term ) {
+ $term_id = intval( $shared_term->term_id );
+ $_shared_terms[ $term_id ] = $shared_term;
+ }
+ $shared_terms = $_shared_terms;
+
+ // Get term taxonomy data for all shared terms.
+ $shared_term_ids = implode( ',', array_keys( $shared_terms ) );
+ $shared_tts = $wpdb->get_results( "SELECT * FROM {$wpdb->term_taxonomy} WHERE `term_id` IN ({$shared_term_ids})" );
+
+ // Split term data recording is slow, so we do it just once, outside the loop.
+ $split_term_data = get_option( '_split_terms', array() );
+ $skipped_first_term = $taxonomies = array();
+ foreach ( $shared_tts as $shared_tt ) {
+ $term_id = intval( $shared_tt->term_id );
+
+ // Don't split the first tt belonging to a given term_id.
+ if ( ! isset( $skipped_first_term[ $term_id ] ) ) {
+ $skipped_first_term[ $term_id ] = 1;
+ continue;
+ }
+
+ if ( ! isset( $split_term_data[ $term_id ] ) ) {
+ $split_term_data[ $term_id ] = array();
+ }
+
+ // Keep track of taxonomies whose hierarchies need flushing.
+ if ( ! isset( $taxonomies[ $shared_tt->taxonomy ] ) ) {
+ $taxonomies[ $shared_tt->taxonomy ] = 1;
+ }
+
+ // Split the term.
+ $split_term_data[ $term_id ][ $shared_tt->taxonomy ] = _split_shared_term( $shared_terms[ $term_id ], $shared_tt, false );
+ }
+
+ // Rebuild the cached hierarchy for each affected taxonomy.
+ foreach ( array_keys( $taxonomies ) as $tax ) {
+ delete_option( "{$tax}_children" );
+ _get_term_hierarchy( $tax );
+ }
+
+ update_option( '_split_terms', $split_term_data );
+
+ delete_option( $lock_name );
+}
+
+/**
+ * In order to avoid the _wp_batch_split_terms() job being accidentally removed,
+ * check that it's still scheduled while we haven't finished splitting terms.
+ *
+ * @ignore
+ * @since 4.3.0
+ */
+function _wp_check_for_scheduled_split_terms() {
+ if ( ! get_option( 'finished_splitting_shared_terms' ) && ! wp_next_scheduled( 'wp_split_shared_term_batch' ) ) {
+ wp_schedule_single_event( time() + MINUTE_IN_SECONDS, 'wp_split_shared_term_batch' );
+ }
+}
+
+/**
+ * Check default categories when a term gets split to see if any of them need to be updated.
+ *
+ * @ignore
+ * @since 4.2.0
+ *
+ * @param int $term_id ID of the formerly shared term.
+ * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
+ * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
+ * @param string $taxonomy Taxonomy for the split term.
+ */
+function _wp_check_split_default_terms( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
+ if ( 'category' != $taxonomy ) {
+ return;
+ }
+
+ foreach ( array( 'default_category', 'default_link_category', 'default_email_category' ) as $option ) {
+ if ( $term_id == get_option( $option, -1 ) ) {
+ update_option( $option, $new_term_id );
+ }
+ }
+}
+
+/**
+ * Check menu items when a term gets split to see if any of them need to be updated.
+ *
+ * @ignore
+ * @since 4.2.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $term_id ID of the formerly shared term.
+ * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
+ * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
+ * @param string $taxonomy Taxonomy for the split term.
+ */
+function _wp_check_split_terms_in_menus( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
+ global $wpdb;
+ $post_ids = $wpdb->get_col( $wpdb->prepare(
+ "SELECT m1.post_id
+ FROM {$wpdb->postmeta} AS m1
+ INNER JOIN {$wpdb->postmeta} AS m2 ON ( m2.post_id = m1.post_id )
+ INNER JOIN {$wpdb->postmeta} AS m3 ON ( m3.post_id = m1.post_id )
+ WHERE ( m1.meta_key = '_menu_item_type' AND m1.meta_value = 'taxonomy' )
+ AND ( m2.meta_key = '_menu_item_object' AND m2.meta_value = '%s' )
+ AND ( m3.meta_key = '_menu_item_object_id' AND m3.meta_value = %d )",
+ $taxonomy,
+ $term_id
+ ) );
+
+ if ( $post_ids ) {
+ foreach ( $post_ids as $post_id ) {
+ update_post_meta( $post_id, '_menu_item_object_id', $new_term_id, $term_id );
+ }
+ }
+}
+
+/**
+ * If the term being split is a nav_menu, change associations.
+ *
+ * @ignore
+ * @since 4.3.0
+ *
+ * @param int $term_id ID of the formerly shared term.
+ * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
+ * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
+ * @param string $taxonomy Taxonomy for the split term.
+ */
+function _wp_check_split_nav_menu_terms( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
+ if ( 'nav_menu' !== $taxonomy ) {
+ return;
+ }
+
+ // Update menu locations.
+ $locations = get_nav_menu_locations();
+ foreach ( $locations as $location => $menu_id ) {
+ if ( $term_id == $menu_id ) {
+ $locations[ $location ] = $new_term_id;
+ }
+ }
+ set_theme_mod( 'nav_menu_locations', $locations );
+}
+
+/**
+ * Get data about terms that previously shared a single term_id, but have since been split.
+ *
+ * @since 4.2.0
+ *
+ * @param int $old_term_id Term ID. This is the old, pre-split term ID.
+ * @return array Array of new term IDs, keyed by taxonomy.
+ */
+function wp_get_split_terms( $old_term_id ) {
+ $split_terms = get_option( '_split_terms', array() );
+
+ $terms = array();
+ if ( isset( $split_terms[ $old_term_id ] ) ) {
+ $terms = $split_terms[ $old_term_id ];
+ }
+
+ return $terms;
+}
+
+/**
+ * Get the new term ID corresponding to a previously split term.
+ *
+ * @since 4.2.0
+ *
+ * @param int $old_term_id Term ID. This is the old, pre-split term ID.
+ * @param string $taxonomy Taxonomy that the term belongs to.
+ * @return int|false If a previously split term is found corresponding to the old term_id and taxonomy,
+ * the new term_id will be returned. If no previously split term is found matching
+ * the parameters, returns false.
+ */
+function wp_get_split_term( $old_term_id, $taxonomy ) {
+ $split_terms = wp_get_split_terms( $old_term_id );
+
+ $term_id = false;
+ if ( isset( $split_terms[ $taxonomy ] ) ) {
+ $term_id = (int) $split_terms[ $taxonomy ];
+ }
+
+ return $term_id;
+}
+
+/**
+ * Determine whether a term is shared between multiple taxonomies.
+ *
+ * Shared taxonomy terms began to be split in 4.3, but failed cron tasks or other delays in upgrade routines may cause
+ * shared terms to remain.
+ *
+ * @since 4.4.0
+ *
+ * @param int $term_id
+ * @return bool
+ */
+function wp_term_is_shared( $term_id ) {
+ global $wpdb;
+
+ if ( get_option( 'finished_splitting_shared_terms' ) ) {
+ return false;
+ }
+
+ $tt_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) );
+
+ return $tt_count > 1;
+}
+
+/**
+ * Generate a permalink for a taxonomy term archive.
+ *
+ * @since 2.5.0
+ *
+ * @global WP_Rewrite $wp_rewrite
+ *
+ * @param object|int|string $term The term object, ID, or slug whose link will be retrieved.
+ * @param string $taxonomy Optional. Taxonomy. Default empty.
+ * @return string|WP_Error HTML link to taxonomy term archive on success, WP_Error if term does not exist.
+ */
+function get_term_link( $term, $taxonomy = '' ) {
+ global $wp_rewrite;
+
+ if ( !is_object($term) ) {
+ if ( is_int( $term ) ) {
+ $term = get_term( $term, $taxonomy );
+ } else {
+ $term = get_term_by( 'slug', $term, $taxonomy );
+ }
+ }
+
+ if ( !is_object($term) )
+ $term = new WP_Error('invalid_term', __('Empty Term'));
+
+ if ( is_wp_error( $term ) )
+ return $term;
+
+ $taxonomy = $term->taxonomy;
+
+ $termlink = $wp_rewrite->get_extra_permastruct($taxonomy);
+
+ $slug = $term->slug;
+ $t = get_taxonomy($taxonomy);
+
+ if ( empty($termlink) ) {
+ if ( 'category' == $taxonomy )
+ $termlink = '?cat=' . $term->term_id;
+ elseif ( $t->query_var )
+ $termlink = "?$t->query_var=$slug";
+ else
+ $termlink = "?taxonomy=$taxonomy&term=$slug";
+ $termlink = home_url($termlink);
+ } else {
+ if ( $t->rewrite['hierarchical'] ) {
+ $hierarchical_slugs = array();
+ $ancestors = get_ancestors( $term->term_id, $taxonomy, 'taxonomy' );
+ foreach ( (array)$ancestors as $ancestor ) {
+ $ancestor_term = get_term($ancestor, $taxonomy);
+ $hierarchical_slugs[] = $ancestor_term->slug;
+ }
+ $hierarchical_slugs = array_reverse($hierarchical_slugs);
+ $hierarchical_slugs[] = $slug;
+ $termlink = str_replace("%$taxonomy%", implode('/', $hierarchical_slugs), $termlink);
+ } else {
+ $termlink = str_replace("%$taxonomy%", $slug, $termlink);
+ }
+ $termlink = home_url( user_trailingslashit($termlink, 'category') );
+ }
+ // Back Compat filters.
+ if ( 'post_tag' == $taxonomy ) {
+
+ /**
+ * Filter the tag link.
+ *
+ * @since 2.3.0
+ * @deprecated 2.5.0 Use 'term_link' instead.
+ *
+ * @param string $termlink Tag link URL.
+ * @param int $term_id Term ID.
+ */
+ $termlink = apply_filters( 'tag_link', $termlink, $term->term_id );
+ } elseif ( 'category' == $taxonomy ) {
+
+ /**
+ * Filter the category link.
+ *
+ * @since 1.5.0
+ * @deprecated 2.5.0 Use 'term_link' instead.
+ *
+ * @param string $termlink Category link URL.
+ * @param int $term_id Term ID.
+ */
+ $termlink = apply_filters( 'category_link', $termlink, $term->term_id );
+ }
+
+ /**
+ * Filter the term link.
+ *
+ * @since 2.5.0
+ *
+ * @param string $termlink Term link URL.
+ * @param object $term Term object.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ return apply_filters( 'term_link', $termlink, $term, $taxonomy );
+}
+
+/**
+ * Display the taxonomies of a post with available options.
+ *
+ * This function can be used within the loop to display the taxonomies for a
+ * post without specifying the Post ID. You can also use it outside the Loop to
+ * display the taxonomies for a specific post.
+ *
+ * @since 2.5.0
+ *
+ * @param array $args {
+ * Arguments about which post to use and how to format the output. Shares all of the arguments
+ * supported by get_the_taxonomies(), in addition to the following.
+ *
+ * @type int|WP_Post $post Post ID or object to get taxonomies of. Default current post.
+ * @type string $before Displays before the taxonomies. Default empty string.
+ * @type string $sep Separates each taxonomy. Default is a space.
+ * @type string $after Displays after the taxonomies. Default empty string.
+ * }
+ * @param array $args See {@link get_the_taxonomies()} for a description of arguments and their defaults.
+ */
+function the_taxonomies( $args = array() ) {
+ $defaults = array(
+ 'post' => 0,
+ 'before' => '',
+ 'sep' => ' ',
+ 'after' => '',
+ );
+
+ $r = wp_parse_args( $args, $defaults );
+
+ echo $r['before'] . join( $r['sep'], get_the_taxonomies( $r['post'], $r ) ) . $r['after'];
+}
+
+/**
+ * Retrieve all taxonomies associated with a post.
+ *
+ * This function can be used within the loop. It will also return an array of
+ * the taxonomies with links to the taxonomy and name.
+ *
+ * @since 2.5.0
+ *
+ * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
+ * @param array $args {
+ * Optional. Arguments about how to format the list of taxonomies. Default empty array.
+ *
+ * @type string $template Template for displaying a taxonomy label and list of terms.
+ * Default is "Label: Terms."
+ * @type string $term_template Template for displaying a single term in the list. Default is the term name
+ * linked to its archive.
+ * }
+ * @return array List of taxonomies.
+ */
+function get_the_taxonomies( $post = 0, $args = array() ) {
+ $post = get_post( $post );
+
+ $args = wp_parse_args( $args, array(
+ /* translators: %s: taxonomy label, %l: list of terms formatted as per $term_template */
+ 'template' => __( '%s: %l.' ),
+ 'term_template' => '<a href="%1$s">%2$s</a>',
+ ) );
+
+ $taxonomies = array();
+
+ if ( ! $post ) {
+ return $taxonomies;
+ }
+
+ foreach ( get_object_taxonomies( $post ) as $taxonomy ) {
+ $t = (array) get_taxonomy( $taxonomy );
+ if ( empty( $t['label'] ) ) {
+ $t['label'] = $taxonomy;
+ }
+ if ( empty( $t['args'] ) ) {
+ $t['args'] = array();
+ }
+ if ( empty( $t['template'] ) ) {
+ $t['template'] = $args['template'];
+ }
+ if ( empty( $t['term_template'] ) ) {
+ $t['term_template'] = $args['term_template'];
+ }
+
+ $terms = get_object_term_cache( $post->ID, $taxonomy );
+ if ( false === $terms ) {
+ $terms = wp_get_object_terms( $post->ID, $taxonomy, $t['args'] );
+ }
+ $links = array();
+
+ foreach ( $terms as $term ) {
+ $links[] = wp_sprintf( $t['term_template'], esc_attr( get_term_link( $term ) ), $term->name );
+ }
+ if ( $links ) {
+ $taxonomies[$taxonomy] = wp_sprintf( $t['template'], $t['label'], $links, $terms );
+ }
+ }
+ return $taxonomies;
+}
+
+/**
+ * Retrieve all taxonomies of a post with just the names.
+ *
+ * @since 2.5.0
+ *
+ * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
+ * @return array
+ */
+function get_post_taxonomies( $post = 0 ) {
+ $post = get_post( $post );
+
+ return get_object_taxonomies($post);
+}
+
+/**
+ * Determine if the given object is associated with any of the given terms.
+ *
+ * The given terms are checked against the object's terms' term_ids, names and slugs.
+ * Terms given as integers will only be checked against the object's terms' term_ids.
+ * If no terms are given, determines if object is associated with any terms in the given taxonomy.
+ *
+ * @since 2.7.0
+ *
+ * @param int $object_id ID of the object (post ID, link ID, ...).
+ * @param string $taxonomy Single taxonomy name.
+ * @param int|string|array $terms Optional. Term term_id, name, slug or array of said. Default null.
+ * @return bool|WP_Error WP_Error on input error.
+ */
+function is_object_in_term( $object_id, $taxonomy, $terms = null ) {
+ if ( !$object_id = (int) $object_id )
+ return new WP_Error( 'invalid_object', __( 'Invalid object ID' ) );
+
+ $object_terms = get_object_term_cache( $object_id, $taxonomy );
+ if ( false === $object_terms ) {
+ $object_terms = wp_get_object_terms( $object_id, $taxonomy, array( 'update_term_meta_cache' => false ) );
+ wp_cache_set( $object_id, $object_terms, "{$taxonomy}_relationships" );
+ }
+
+ if ( is_wp_error( $object_terms ) )
+ return $object_terms;
+ if ( empty( $object_terms ) )
+ return false;
+ if ( empty( $terms ) )
+ return ( !empty( $object_terms ) );
+
+ $terms = (array) $terms;
+
+ if ( $ints = array_filter( $terms, 'is_int' ) )
+ $strs = array_diff( $terms, $ints );
+ else
+ $strs =& $terms;
+
+ foreach ( $object_terms as $object_term ) {
+ // If term is an int, check against term_ids only.
+ if ( $ints && in_array( $object_term->term_id, $ints ) ) {
+ return true;
+ }
+
+ if ( $strs ) {
+ // Only check numeric strings against term_id, to avoid false matches due to type juggling.
+ $numeric_strs = array_map( 'intval', array_filter( $strs, 'is_numeric' ) );
+ if ( in_array( $object_term->term_id, $numeric_strs, true ) ) {
+ return true;
+ }
+
+ if ( in_array( $object_term->name, $strs ) ) return true;
+ if ( in_array( $object_term->slug, $strs ) ) return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Determine if the given object type is associated with the given taxonomy.
+ *
+ * @since 3.0.0
+ *
+ * @param string $object_type Object type string.
+ * @param string $taxonomy Single taxonomy name.
+ * @return bool True if object is associated with the taxonomy, otherwise false.
+ */
+function is_object_in_taxonomy( $object_type, $taxonomy ) {
+ $taxonomies = get_object_taxonomies( $object_type );
+ if ( empty( $taxonomies ) ) {
+ return false;
+ }
+ return in_array( $taxonomy, $taxonomies );
+}
+
+/**
+ * Get an array of ancestor IDs for a given object.
+ *
+ * @since 3.1.0
+ * @since 4.1.0 Introduced the `$resource_type` argument.
+ *
+ * @param int $object_id Optional. The ID of the object. Default 0.
+ * @param string $object_type Optional. The type of object for which we'll be retrieving
+ * ancestors. Accepts a post type or a taxonomy name. Default empty.
+ * @param string $resource_type Optional. Type of resource $object_type is. Accepts 'post_type'
+ * or 'taxonomy'. Default empty.
+ * @return array An array of ancestors from lowest to highest in the hierarchy.
+ */
+function get_ancestors( $object_id = 0, $object_type = '', $resource_type = '' ) {
+ $object_id = (int) $object_id;
+
+ $ancestors = array();
+
+ if ( empty( $object_id ) ) {
+
+ /** This filter is documented in wp-includes/taxonomy-functions.php */
+ return apply_filters( 'get_ancestors', $ancestors, $object_id, $object_type, $resource_type );
+ }
+
+ if ( ! $resource_type ) {
+ if ( is_taxonomy_hierarchical( $object_type ) ) {
+ $resource_type = 'taxonomy';
+ } elseif ( post_type_exists( $object_type ) ) {
+ $resource_type = 'post_type';
+ }
+ }
+
+ if ( 'taxonomy' === $resource_type ) {
+ $term = get_term($object_id, $object_type);
+ while ( ! is_wp_error($term) && ! empty( $term->parent ) && ! in_array( $term->parent, $ancestors ) ) {
+ $ancestors[] = (int) $term->parent;
+ $term = get_term($term->parent, $object_type);
+ }
+ } elseif ( 'post_type' === $resource_type ) {
+ $ancestors = get_post_ancestors($object_id);
+ }
+
+ /**
+ * Filter a given object's ancestors.
+ *
+ * @since 3.1.0
+ * @since 4.1.1 Introduced the `$resource_type` parameter.
+ *
+ * @param array $ancestors An array of object ancestors.
+ * @param int $object_id Object ID.
+ * @param string $object_type Type of object.
+ * @param string $resource_type Type of resource $object_type is.
+ */
+ return apply_filters( 'get_ancestors', $ancestors, $object_id, $object_type, $resource_type );
+}
+
+/**
+ * Returns the term's parent's term_ID.
+ *
+ * @since 3.1.0
+ *
+ * @param int $term_id Term ID.
+ * @param string $taxonomy Taxonomy name.
+ * @return int|false False on error.
+ */
+function wp_get_term_taxonomy_parent_id( $term_id, $taxonomy ) {
+ $term = get_term( $term_id, $taxonomy );
+ if ( ! $term || is_wp_error( $term ) ) {
+ return false;
+ }
+ return (int) $term->parent;
+}
+
+/**
+ * Checks the given subset of the term hierarchy for hierarchy loops.
+ * Prevents loops from forming and breaks those that it finds.
+ *
+ * Attached to the {@see 'wp_update_term_parent'} filter.
+ *
+ * @since 3.1.0
+ *
+ * @param int $parent `term_id` of the parent for the term we're checking.
+ * @param int $term_id The term we're checking.
+ * @param string $taxonomy The taxonomy of the term we're checking.
+ *
+ * @return int The new parent for the term.
+ */
+function wp_check_term_hierarchy_for_loops( $parent, $term_id, $taxonomy ) {
+ // Nothing fancy here - bail
+ if ( !$parent )
+ return 0;
+
+ // Can't be its own parent.
+ if ( $parent == $term_id )
+ return 0;
+
+ // Now look for larger loops.
+ if ( !$loop = wp_find_hierarchy_loop( 'wp_get_term_taxonomy_parent_id', $term_id, $parent, array( $taxonomy ) ) )
+ return $parent; // No loop
+
+ // Setting $parent to the given value causes a loop.
+ if ( isset( $loop[$term_id] ) )
+ return 0;
+
+ // There's a loop, but it doesn't contain $term_id. Break the loop.
+ foreach ( array_keys( $loop ) as $loop_member )
+ wp_update_term( $loop_member, $taxonomy, array( 'parent' => 0 ) );
+
+ return $parent;
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesuserfunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/user-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/user-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/user-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,2294 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * User API: Top-level users functionality
- *
- * @package WordPress
- * @subpackage Users
- * @since 4.4.0
- */
-
-/**
- * Authenticate user with remember capability.
- *
- * The credentials is an array that has 'user_login', 'user_password', and
- * 'remember' indices. If the credentials is not given, then the log in form
- * will be assumed and used if set.
- *
- * The various authentication cookies will be set by this function and will be
- * set for a longer period depending on if the 'remember' credential is set to
- * true.
- *
- * @since 2.5.0
- *
- * @global string $auth_secure_cookie
- *
- * @param array $credentials Optional. User info in order to sign on.
- * @param string|bool $secure_cookie Optional. Whether to use secure cookie.
- * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
- */
-function wp_signon( $credentials = array(), $secure_cookie = '' ) {
- if ( empty($credentials) ) {
- if ( ! empty($_POST['log']) )
- $credentials['user_login'] = $_POST['log'];
- if ( ! empty($_POST['pwd']) )
- $credentials['user_password'] = $_POST['pwd'];
- if ( ! empty($_POST['rememberme']) )
- $credentials['remember'] = $_POST['rememberme'];
- }
-
- if ( !empty($credentials['remember']) )
- $credentials['remember'] = true;
- else
- $credentials['remember'] = false;
-
- /**
- * Fires before the user is authenticated.
- *
- * The variables passed to the callbacks are passed by reference,
- * and can be modified by callback functions.
- *
- * @since 1.5.1
- *
- * @todo Decide whether to deprecate the wp_authenticate action.
- *
- * @param string $user_login Username, passed by reference.
- * @param string $user_password User password, passed by reference.
- */
- do_action_ref_array( 'wp_authenticate', array( &$credentials['user_login'], &$credentials['user_password'] ) );
-
- if ( '' === $secure_cookie )
- $secure_cookie = is_ssl();
-
- /**
- * Filter whether to use a secure sign-on cookie.
- *
- * @since 3.1.0
- *
- * @param bool $secure_cookie Whether to use a secure sign-on cookie.
- * @param array $credentials {
- * Array of entered sign-on data.
- *
- * @type string $user_login Username.
- * @type string $user_password Password entered.
- * @type bool $remember Whether to 'remember' the user. Increases the time
- * that the cookie will be kept. Default false.
- * }
- */
- $secure_cookie = apply_filters( 'secure_signon_cookie', $secure_cookie, $credentials );
-
- global $auth_secure_cookie; // XXX ugly hack to pass this to wp_authenticate_cookie
- $auth_secure_cookie = $secure_cookie;
-
- add_filter('authenticate', 'wp_authenticate_cookie', 30, 3);
-
- $user = wp_authenticate($credentials['user_login'], $credentials['user_password']);
-
- if ( is_wp_error($user) ) {
- if ( $user->get_error_codes() == array('empty_username', 'empty_password') ) {
- $user = new WP_Error('', '');
- }
-
- return $user;
- }
-
- wp_set_auth_cookie($user->ID, $credentials['remember'], $secure_cookie);
- /**
- * Fires after the user has successfully logged in.
- *
- * @since 1.5.0
- *
- * @param string $user_login Username.
- * @param WP_User $user WP_User object of the logged-in user.
- */
- do_action( 'wp_login', $user->user_login, $user );
- return $user;
-}
-
-/**
- * Authenticate the user using the username and password.
- *
- * @since 2.8.0
- *
- * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null.
- * @param string $username Username for authentication.
- * @param string $password Password for authentication.
- * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
- */
-function wp_authenticate_username_password($user, $username, $password) {
- if ( $user instanceof WP_User ) {
- return $user;
- }
-
- if ( empty($username) || empty($password) ) {
- if ( is_wp_error( $user ) )
- return $user;
-
- $error = new WP_Error();
-
- if ( empty($username) )
- $error->add('empty_username', __('<strong>ERROR</strong>: The username field is empty.'));
-
- if ( empty($password) )
- $error->add('empty_password', __('<strong>ERROR</strong>: The password field is empty.'));
-
- return $error;
- }
-
- $user = get_user_by('login', $username);
-
- if ( !$user ) {
- return new WP_Error( 'invalid_username',
- __( '<strong>ERROR</strong>: Invalid username.' ) .
- ' <a href="' . wp_lostpassword_url() . '">' .
- __( 'Lost your password?' ) .
- '</a>'
- );
- }
-
- /**
- * Filter whether the given user can be authenticated with the provided $password.
- *
- * @since 2.5.0
- *
- * @param WP_User|WP_Error $user WP_User or WP_Error object if a previous
- * callback failed authentication.
- * @param string $password Password to check against the user.
- */
- $user = apply_filters( 'wp_authenticate_user', $user, $password );
- if ( is_wp_error($user) )
- return $user;
-
- if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
- return new WP_Error( 'incorrect_password',
- sprintf(
- /* translators: %s: user name */
- __( '<strong>ERROR</strong>: The password you entered for the username %s is incorrect.' ),
- '<strong>' . $username . '</strong>'
- ) .
- ' <a href="' . wp_lostpassword_url() . '">' .
- __( 'Lost your password?' ) .
- '</a>'
- );
- }
-
- return $user;
-}
-
-/**
- * Authenticate the user using the WordPress auth cookie.
- *
- * @since 2.8.0
- *
- * @global string $auth_secure_cookie
- *
- * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null.
- * @param string $username Username. If not empty, cancels the cookie authentication.
- * @param string $password Password. If not empty, cancels the cookie authentication.
- * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
- */
-function wp_authenticate_cookie($user, $username, $password) {
- if ( $user instanceof WP_User ) {
- return $user;
- }
-
- if ( empty($username) && empty($password) ) {
- $user_id = wp_validate_auth_cookie();
- if ( $user_id )
- return new WP_User($user_id);
-
- global $auth_secure_cookie;
-
- if ( $auth_secure_cookie )
- $auth_cookie = SECURE_AUTH_COOKIE;
- else
- $auth_cookie = AUTH_COOKIE;
-
- if ( !empty($_COOKIE[$auth_cookie]) )
- return new WP_Error('expired_session', __('Please log in again.'));
-
- // If the cookie is not set, be silent.
- }
-
- return $user;
-}
-
-/**
- * For Multisite blogs, check if the authenticated user has been marked as a
- * spammer, or if the user's primary blog has been marked as spam.
- *
- * @since 3.7.0
- *
- * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null.
- * @return WP_User|WP_Error WP_User on success, WP_Error if the user is considered a spammer.
- */
-function wp_authenticate_spam_check( $user ) {
- if ( $user instanceof WP_User && is_multisite() ) {
- /**
- * Filter whether the user has been marked as a spammer.
- *
- * @since 3.7.0
- *
- * @param bool $spammed Whether the user is considered a spammer.
- * @param WP_User $user User to check against.
- */
- $spammed = apply_filters( 'check_is_user_spammed', is_user_spammy(), $user );
-
- if ( $spammed )
- return new WP_Error( 'spammer_account', __( '<strong>ERROR</strong>: Your account has been marked as a spammer.' ) );
- }
- return $user;
-}
-
-/**
- * Validate the logged-in cookie.
- *
- * Checks the logged-in cookie if the previous auth cookie could not be
- * validated and parsed.
- *
- * This is a callback for the determine_current_user filter, rather than API.
- *
- * @since 3.9.0
- *
- * @param int|bool $user_id The user ID (or false) as received from the
- * determine_current_user filter.
- * @return int|false User ID if validated, false otherwise. If a user ID from
- * an earlier filter callback is received, that value is returned.
- */
-function wp_validate_logged_in_cookie( $user_id ) {
- if ( $user_id ) {
- return $user_id;
- }
-
- if ( is_blog_admin() || is_network_admin() || empty( $_COOKIE[LOGGED_IN_COOKIE] ) ) {
- return false;
- }
-
- return wp_validate_auth_cookie( $_COOKIE[LOGGED_IN_COOKIE], 'logged_in' );
-}
-
-/**
- * Number of posts user has written.
- *
- * @since 3.0.0
- * @since 4.1.0 Added `$post_type` argument.
- * @since 4.3.0 Added `$public_only` argument. Added the ability to pass an array
- * of post types to `$post_type`.
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $userid User ID.
- * @param array|string $post_type Optional. Single post type or array of post types to count the number of posts for. Default 'post'.
- * @param bool $public_only Optional. Whether to only return counts for public posts. Default false.
- * @return int Number of posts the user has written in this post type.
- */
-function count_user_posts( $userid, $post_type = 'post', $public_only = false ) {
- global $wpdb;
-
- $where = get_posts_by_author_sql( $post_type, true, $userid, $public_only );
-
- $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" );
-
- /**
- * Filter the number of posts a user has written.
- *
- * @since 2.7.0
- * @since 4.1.0 Added `$post_type` argument.
- * @since 4.3.1 Added `$public_only` argument.
- *
- * @param int $count The user's post count.
- * @param int $userid User ID.
- * @param string|array $post_type Single post type or array of post types to count the number of posts for.
- * @param bool $public_only Whether to limit counted posts to public posts.
- */
- return apply_filters( 'get_usernumposts', $count, $userid, $post_type, $public_only );
-}
-
-/**
- * Number of posts written by a list of users.
- *
- * @since 3.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array $users Array of user IDs.
- * @param string|array $post_type Optional. Single post type or array of post types to check. Defaults to 'post'.
- * @param bool $public_only Optional. Only return counts for public posts. Defaults to false.
- * @return array Amount of posts each user has written.
- */
-function count_many_users_posts( $users, $post_type = 'post', $public_only = false ) {
- global $wpdb;
-
- $count = array();
- if ( empty( $users ) || ! is_array( $users ) )
- return $count;
-
- $userlist = implode( ',', array_map( 'absint', $users ) );
- $where = get_posts_by_author_sql( $post_type, true, null, $public_only );
-
- $result = $wpdb->get_results( "SELECT post_author, COUNT(*) FROM $wpdb->posts $where AND post_author IN ($userlist) GROUP BY post_author", ARRAY_N );
- foreach ( $result as $row ) {
- $count[ $row[0] ] = $row[1];
- }
-
- foreach ( $users as $id ) {
- if ( ! isset( $count[ $id ] ) )
- $count[ $id ] = 0;
- }
-
- return $count;
-}
-
-//
-// User option functions
-//
-
-/**
- * Get the current user's ID
- *
- * @since MU
- *
- * @return int The current user's ID
- */
-function get_current_user_id() {
- if ( ! function_exists( 'wp_get_current_user' ) )
- return 0;
- $user = wp_get_current_user();
- return ( isset( $user->ID ) ? (int) $user->ID : 0 );
-}
-
-/**
- * Retrieve user option that can be either per Site or per Network.
- *
- * If the user ID is not given, then the current user will be used instead. If
- * the user ID is given, then the user data will be retrieved. The filter for
- * the result, will also pass the original option name and finally the user data
- * object as the third parameter.
- *
- * The option will first check for the per site name and then the per Network name.
- *
- * @since 2.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $option User option name.
- * @param int $user Optional. User ID.
- * @param string $deprecated Use get_option() to check for an option in the options table.
- * @return mixed User option value on success, false on failure.
- */
-function get_user_option( $option, $user = 0, $deprecated = '' ) {
- global $wpdb;
-
- if ( !empty( $deprecated ) )
- _deprecated_argument( __FUNCTION__, '3.0' );
-
- if ( empty( $user ) )
- $user = get_current_user_id();
-
- if ( ! $user = get_userdata( $user ) )
- return false;
-
- $prefix = $wpdb->get_blog_prefix();
- if ( $user->has_prop( $prefix . $option ) ) // Blog specific
- $result = $user->get( $prefix . $option );
- elseif ( $user->has_prop( $option ) ) // User specific and cross-blog
- $result = $user->get( $option );
- else
- $result = false;
-
- /**
- * Filter a specific user option value.
- *
- * The dynamic portion of the hook name, `$option`, refers to the user option name.
- *
- * @since 2.5.0
- *
- * @param mixed $result Value for the user's option.
- * @param string $option Name of the option being retrieved.
- * @param WP_User $user WP_User object of the user whose option is being retrieved.
- */
- return apply_filters( "get_user_option_{$option}", $result, $option, $user );
-}
-
-/**
- * Update user option with global blog capability.
- *
- * User options are just like user metadata except that they have support for
- * global blog options. If the 'global' parameter is false, which it is by default
- * it will prepend the WordPress table prefix to the option name.
- *
- * Deletes the user option if $newvalue is empty.
- *
- * @since 2.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $user_id User ID.
- * @param string $option_name User option name.
- * @param mixed $newvalue User option value.
- * @param bool $global Optional. Whether option name is global or blog specific.
- * Default false (blog specific).
- * @return int|bool User meta ID if the option didn't exist, true on successful update,
- * false on failure.
- */
-function update_user_option( $user_id, $option_name, $newvalue, $global = false ) {
- global $wpdb;
-
- if ( !$global )
- $option_name = $wpdb->get_blog_prefix() . $option_name;
-
- return update_user_meta( $user_id, $option_name, $newvalue );
-}
-
-/**
- * Delete user option with global blog capability.
- *
- * User options are just like user metadata except that they have support for
- * global blog options. If the 'global' parameter is false, which it is by default
- * it will prepend the WordPress table prefix to the option name.
- *
- * @since 3.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $user_id User ID
- * @param string $option_name User option name.
- * @param bool $global Optional. Whether option name is global or blog specific.
- * Default false (blog specific).
- * @return bool True on success, false on failure.
- */
-function delete_user_option( $user_id, $option_name, $global = false ) {
- global $wpdb;
-
- if ( !$global )
- $option_name = $wpdb->get_blog_prefix() . $option_name;
- return delete_user_meta( $user_id, $option_name );
-}
-
-/**
- * Retrieve list of users matching criteria.
- *
- * @since 3.1.0
- *
- * @see WP_User_Query
- *
- * @param array $args Optional. Arguments to retrieve users. See {@see WP_User_Query::prepare_query()}
- * for more information on accepted arguments.
- * @return array List of users.
- */
-function get_users( $args = array() ) {
-
- $args = wp_parse_args( $args );
- $args['count_total'] = false;
-
- $user_search = new WP_User_Query($args);
-
- return (array) $user_search->get_results();
-}
-
-/**
- * Get the blogs a user belongs to.
- *
- * @since 3.0.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param int $user_id User ID
- * @param bool $all Whether to retrieve all blogs, or only blogs that are not
- * marked as deleted, archived, or spam.
- * @return array A list of the user's blogs. An empty array if the user doesn't exist
- * or belongs to no blogs.
- */
-function get_blogs_of_user( $user_id, $all = false ) {
- global $wpdb;
-
- $user_id = (int) $user_id;
-
- // Logged out users can't have blogs
- if ( empty( $user_id ) )
- return array();
-
- $keys = get_user_meta( $user_id );
- if ( empty( $keys ) )
- return array();
-
- if ( ! is_multisite() ) {
- $blog_id = get_current_blog_id();
- $blogs = array( $blog_id => new stdClass );
- $blogs[ $blog_id ]->userblog_id = $blog_id;
- $blogs[ $blog_id ]->blogname = get_option('blogname');
- $blogs[ $blog_id ]->domain = '';
- $blogs[ $blog_id ]->path = '';
- $blogs[ $blog_id ]->site_id = 1;
- $blogs[ $blog_id ]->siteurl = get_option('siteurl');
- $blogs[ $blog_id ]->archived = 0;
- $blogs[ $blog_id ]->spam = 0;
- $blogs[ $blog_id ]->deleted = 0;
- return $blogs;
- }
-
- $blogs = array();
-
- if ( isset( $keys[ $wpdb->base_prefix . 'capabilities' ] ) && defined( 'MULTISITE' ) ) {
- $blog = get_blog_details( 1 );
- if ( $blog && isset( $blog->domain ) && ( $all || ( ! $blog->archived && ! $blog->spam && ! $blog->deleted ) ) ) {
- $blogs[ 1 ] = (object) array(
- 'userblog_id' => 1,
- 'blogname' => $blog->blogname,
- 'domain' => $blog->domain,
- 'path' => $blog->path,
- 'site_id' => $blog->site_id,
- 'siteurl' => $blog->siteurl,
- 'archived' => $blog->archived,
- 'mature' => $blog->mature,
- 'spam' => $blog->spam,
- 'deleted' => $blog->deleted,
- );
- }
- unset( $keys[ $wpdb->base_prefix . 'capabilities' ] );
- }
-
- $keys = array_keys( $keys );
-
- foreach ( $keys as $key ) {
- if ( 'capabilities' !== substr( $key, -12 ) )
- continue;
- if ( $wpdb->base_prefix && 0 !== strpos( $key, $wpdb->base_prefix ) )
- continue;
- $blog_id = str_replace( array( $wpdb->base_prefix, '_capabilities' ), '', $key );
- if ( ! is_numeric( $blog_id ) )
- continue;
-
- $blog_id = (int) $blog_id;
- $blog = get_blog_details( $blog_id );
- if ( $blog && isset( $blog->domain ) && ( $all || ( ! $blog->archived && ! $blog->spam && ! $blog->deleted ) ) ) {
- $blogs[ $blog_id ] = (object) array(
- 'userblog_id' => $blog_id,
- 'blogname' => $blog->blogname,
- 'domain' => $blog->domain,
- 'path' => $blog->path,
- 'site_id' => $blog->site_id,
- 'siteurl' => $blog->siteurl,
- 'archived' => $blog->archived,
- 'mature' => $blog->mature,
- 'spam' => $blog->spam,
- 'deleted' => $blog->deleted,
- );
- }
- }
-
- /**
- * Filter the list of blogs a user belongs to.
- *
- * @since MU
- *
- * @param array $blogs An array of blog objects belonging to the user.
- * @param int $user_id User ID.
- * @param bool $all Whether the returned blogs array should contain all blogs, including
- * those marked 'deleted', 'archived', or 'spam'. Default false.
- */
- return apply_filters( 'get_blogs_of_user', $blogs, $user_id, $all );
-}
-
-/**
- * Find out whether a user is a member of a given blog.
- *
- * @since MU 1.1
- *
- * @param int $user_id Optional. The unique ID of the user. Defaults to the current user.
- * @param int $blog_id Optional. ID of the blog to check. Defaults to the current site.
- * @return bool
- */
-function is_user_member_of_blog( $user_id = 0, $blog_id = 0 ) {
- global $wpdb;
-
- $user_id = (int) $user_id;
- $blog_id = (int) $blog_id;
-
- if ( empty( $user_id ) ) {
- $user_id = get_current_user_id();
- }
-
- // Technically not needed, but does save calls to get_blog_details and get_user_meta
- // in the event that the function is called when a user isn't logged in
- if ( empty( $user_id ) ) {
- return false;
- } else {
- $user = get_userdata( $user_id );
- if ( ! $user instanceof WP_User ) {
- return false;
- }
- }
-
- if ( ! is_multisite() ) {
- return true;
- }
-
- if ( empty( $blog_id ) ) {
- $blog_id = get_current_blog_id();
- }
-
- $blog = get_blog_details( $blog_id );
-
- if ( ! $blog || ! isset( $blog->domain ) || $blog->archived || $blog->spam || $blog->deleted ) {
- return false;
- }
-
- $keys = get_user_meta( $user_id );
- if ( empty( $keys ) ) {
- return false;
- }
-
- // no underscore before capabilities in $base_capabilities_key
- $base_capabilities_key = $wpdb->base_prefix . 'capabilities';
- $site_capabilities_key = $wpdb->base_prefix . $blog_id . '_capabilities';
-
- if ( isset( $keys[ $base_capabilities_key ] ) && $blog_id == 1 ) {
- return true;
- }
-
- if ( isset( $keys[ $site_capabilities_key ] ) ) {
- return true;
- }
-
- return false;
-}
-
-/**
- * Add meta data field to a user.
- *
- * Post meta data is called "Custom Fields" on the Administration Screens.
- *
- * @since 3.0.0
- * @link https://codex.wordpress.org/Function_Reference/add_user_meta
- *
- * @param int $user_id User ID.
- * @param string $meta_key Metadata name.
- * @param mixed $meta_value Metadata value.
- * @param bool $unique Optional, default is false. Whether the same key should not be added.
- * @return int|false Meta ID on success, false on failure.
- */
-function add_user_meta($user_id, $meta_key, $meta_value, $unique = false) {
- return add_metadata('user', $user_id, $meta_key, $meta_value, $unique);
-}
-
-/**
- * Remove metadata matching criteria from a user.
- *
- * You can match based on the key, or key and value. Removing based on key and
- * value, will keep from removing duplicate metadata with the same key. It also
- * allows removing all metadata matching key, if needed.
- *
- * @since 3.0.0
- * @link https://codex.wordpress.org/Function_Reference/delete_user_meta
- *
- * @param int $user_id User ID
- * @param string $meta_key Metadata name.
- * @param mixed $meta_value Optional. Metadata value.
- * @return bool True on success, false on failure.
- */
-function delete_user_meta($user_id, $meta_key, $meta_value = '') {
- return delete_metadata('user', $user_id, $meta_key, $meta_value);
-}
-
-/**
- * Retrieve user meta field for a user.
- *
- * @since 3.0.0
- * @link https://codex.wordpress.org/Function_Reference/get_user_meta
- *
- * @param int $user_id User ID.
- * @param string $key Optional. The meta key to retrieve. By default, returns data for all keys.
- * @param bool $single Whether to return a single value.
- * @return mixed Will be an array if $single is false. Will be value of meta data field if $single is true.
- */
-function get_user_meta($user_id, $key = '', $single = false) {
- return get_metadata('user', $user_id, $key, $single);
-}
-
-/**
- * Update user meta field based on user ID.
- *
- * Use the $prev_value parameter to differentiate between meta fields with the
- * same key and user ID.
- *
- * If the meta field for the user does not exist, it will be added.
- *
- * @since 3.0.0
- * @link https://codex.wordpress.org/Function_Reference/update_user_meta
- *
- * @param int $user_id User ID.
- * @param string $meta_key Metadata key.
- * @param mixed $meta_value Metadata value.
- * @param mixed $prev_value Optional. Previous value to check before removing.
- * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
- */
-function update_user_meta($user_id, $meta_key, $meta_value, $prev_value = '') {
- return update_metadata('user', $user_id, $meta_key, $meta_value, $prev_value);
-}
-
-/**
- * Count number of users who have each of the user roles.
- *
- * Assumes there are neither duplicated nor orphaned capabilities meta_values.
- * Assumes role names are unique phrases. Same assumption made by WP_User_Query::prepare_query()
- * Using $strategy = 'time' this is CPU-intensive and should handle around 10^7 users.
- * Using $strategy = 'memory' this is memory-intensive and should handle around 10^5 users, but see WP Bug #12257.
- *
- * @since 3.0.0
- * @since 4.4.0 The number of users with no role is now included in the `none` element.
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param string $strategy 'time' or 'memory'
- * @return array Includes a grand total and an array of counts indexed by role strings.
- */
-function count_users($strategy = 'time') {
- global $wpdb;
-
- // Initialize
- $id = get_current_blog_id();
- $blog_prefix = $wpdb->get_blog_prefix($id);
- $result = array();
-
- if ( 'time' == $strategy ) {
- $avail_roles = wp_roles()->get_names();
-
- // Build a CPU-intensive query that will return concise information.
- $select_count = array();
- foreach ( $avail_roles as $this_role => $name ) {
- $select_count[] = $wpdb->prepare( "COUNT(NULLIF(`meta_value` LIKE %s, false))", '%' . $wpdb->esc_like( '"' . $this_role . '"' ) . '%');
- }
- $select_count[] = "COUNT(NULLIF(`meta_value` = 'a:0:{}', false))";
- $select_count = implode(', ', $select_count);
-
- // Add the meta_value index to the selection list, then run the query.
- $row = $wpdb->get_row( "SELECT $select_count, COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'", ARRAY_N );
-
- // Run the previous loop again to associate results with role names.
- $col = 0;
- $role_counts = array();
- foreach ( $avail_roles as $this_role => $name ) {
- $count = (int) $row[$col++];
- if ($count > 0) {
- $role_counts[$this_role] = $count;
- }
- }
-
- $role_counts['none'] = (int) $row[$col++];
-
- // Get the meta_value index from the end of the result set.
- $total_users = (int) $row[$col];
-
- $result['total_users'] = $total_users;
- $result['avail_roles'] =& $role_counts;
- } else {
- $avail_roles = array(
- 'none' => 0,
- );
-
- $users_of_blog = $wpdb->get_col( "SELECT meta_value FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'" );
-
- foreach ( $users_of_blog as $caps_meta ) {
- $b_roles = maybe_unserialize($caps_meta);
- if ( ! is_array( $b_roles ) )
- continue;
- if ( empty( $b_roles ) ) {
- $avail_roles['none']++;
- }
- foreach ( $b_roles as $b_role => $val ) {
- if ( isset($avail_roles[$b_role]) ) {
- $avail_roles[$b_role]++;
- } else {
- $avail_roles[$b_role] = 1;
- }
- }
- }
-
- $result['total_users'] = count( $users_of_blog );
- $result['avail_roles'] =& $avail_roles;
- }
-
- if ( is_multisite() ) {
- $result['avail_roles']['none'] = 0;
- }
-
- return $result;
-}
-
-//
-// Private helper functions
-//
-
-/**
- * Set up global user vars.
- *
- * Used by wp_set_current_user() for back compat. Might be deprecated in the future.
- *
- * @since 2.0.4
- *
- * @global string $user_login The user username for logging in
- * @global object $userdata User data.
- * @global int $user_level The level of the user
- * @global int $user_ID The ID of the user
- * @global string $user_email The email address of the user
- * @global string $user_url The url in the user's profile
- * @global string $user_identity The display name of the user
- *
- * @param int $for_user_id Optional. User ID to set up global data.
- */
-function setup_userdata($for_user_id = '') {
- global $user_login, $userdata, $user_level, $user_ID, $user_email, $user_url, $user_identity;
-
- if ( '' == $for_user_id )
- $for_user_id = get_current_user_id();
- $user = get_userdata( $for_user_id );
-
- if ( ! $user ) {
- $user_ID = 0;
- $user_level = 0;
- $userdata = null;
- $user_login = $user_email = $user_url = $user_identity = '';
- return;
- }
-
- $user_ID = (int) $user->ID;
- $user_level = (int) $user->user_level;
- $userdata = $user;
- $user_login = $user->user_login;
- $user_email = $user->user_email;
- $user_url = $user->user_url;
- $user_identity = $user->display_name;
-}
-
-/**
- * Create dropdown HTML content of users.
- *
- * The content can either be displayed, which it is by default or retrieved by
- * setting the 'echo' argument. The 'include' and 'exclude' arguments do not
- * need to be used; all users will be displayed in that case. Only one can be
- * used, either 'include' or 'exclude', but not both.
- *
- * The available arguments are as follows:
- *
- * @since 2.3.0
- *
- * @global int $blog_id
- *
- * @param array|string $args {
- * Optional. Array or string of arguments to generate a drop-down of users.
- * {@see WP_User_Query::prepare_query() for additional available arguments.
- *
- * @type string $show_option_all Text to show as the drop-down default (all).
- * Default empty.
- * @type string $show_option_none Text to show as the drop-down default when no
- * users were found. Default empty.
- * @type int|string $option_none_value Value to use for $show_option_non when no users
- * were found. Default -1.
- * @type string $hide_if_only_one_author Whether to skip generating the drop-down
- * if only one user was found. Default empty.
- * @type string $orderby Field to order found users by. Accepts user fields.
- * Default 'display_name'.
- * @type string $order Whether to order users in ascending or descending
- * order. Accepts 'ASC' (ascending) or 'DESC' (descending).
- * Default 'ASC'.
- * @type array|string $include Array or comma-separated list of user IDs to include.
- * Default empty.
- * @type array|string $exclude Array or comma-separated list of user IDs to exclude.
- * Default empty.
- * @type bool|int $multi Whether to skip the ID attribute on the 'select' element.
- * Accepts 1|true or 0|false. Default 0|false.
- * @type string $show User table column to display. If the selected item is empty
- * then the 'user_login' will be displayed in parentheses.
- * Accepts user fields. Default 'display_name'.
- * @type int|bool $echo Whether to echo or return the drop-down. Accepts 1|true (echo)
- * or 0|false (return). Default 1|true.
- * @type int $selected Which user ID should be selected. Default 0.
- * @type bool $include_selected Whether to always include the selected user ID in the drop-
- * down. Default false.
- * @type string $name Name attribute of select element. Default 'user'.
- * @type string $id ID attribute of the select element. Default is the value of $name.
- * @type string $class Class attribute of the select element. Default empty.
- * @type int $blog_id ID of blog (Multisite only). Default is ID of the current blog.
- * @type string $who Which type of users to query. Accepts only an empty string or
- * 'authors'. Default empty.
- * }
- * @return string String of HTML content.
- */
-function wp_dropdown_users( $args = '' ) {
- $defaults = array(
- 'show_option_all' => '', 'show_option_none' => '', 'hide_if_only_one_author' => '',
- 'orderby' => 'display_name', 'order' => 'ASC',
- 'include' => '', 'exclude' => '', 'multi' => 0,
- 'show' => 'display_name', 'echo' => 1,
- 'selected' => 0, 'name' => 'user', 'class' => '', 'id' => '',
- 'blog_id' => $GLOBALS['blog_id'], 'who' => '', 'include_selected' => false,
- 'option_none_value' => -1
- );
-
- $defaults['selected'] = is_author() ? get_query_var( 'author' ) : 0;
-
- $r = wp_parse_args( $args, $defaults );
- $show = $r['show'];
- $show_option_all = $r['show_option_all'];
- $show_option_none = $r['show_option_none'];
- $option_none_value = $r['option_none_value'];
-
- $query_args = wp_array_slice_assoc( $r, array( 'blog_id', 'include', 'exclude', 'orderby', 'order', 'who' ) );
- $query_args['fields'] = array( 'ID', 'user_login', $show );
-
- /**
- * Filter the query arguments for the user drop-down.
- *
- * @since 4.4.0
- *
- * @param array $query_args The query arguments for wp_dropdown_users().
- * @param array $r The default arguments for wp_dropdown_users().
- */
- $query_args = apply_filters( 'wp_dropdown_users_args', $query_args, $r );
-
- $users = get_users( $query_args );
-
- $output = '';
- if ( ! empty( $users ) && ( empty( $r['hide_if_only_one_author'] ) || count( $users ) > 1 ) ) {
- $name = esc_attr( $r['name'] );
- if ( $r['multi'] && ! $r['id'] ) {
- $id = '';
- } else {
- $id = $r['id'] ? " id='" . esc_attr( $r['id'] ) . "'" : " id='$name'";
- }
- $output = "<select name='{$name}'{$id} class='" . $r['class'] . "'>\n";
-
- if ( $show_option_all ) {
- $output .= "\t<option value='0'>$show_option_all</option>\n";
- }
-
- if ( $show_option_none ) {
- $_selected = selected( $option_none_value, $r['selected'], false );
- $output .= "\t<option value='" . esc_attr( $option_none_value ) . "'$_selected>$show_option_none</option>\n";
- }
-
- $found_selected = false;
- foreach ( (array) $users as $user ) {
- $user->ID = (int) $user->ID;
- $_selected = selected( $user->ID, $r['selected'], false );
- if ( $_selected ) {
- $found_selected = true;
- }
- $display = ! empty( $user->$show ) ? $user->$show : '('. $user->user_login . ')';
- $output .= "\t<option value='$user->ID'$_selected>" . esc_html( $display ) . "</option>\n";
- }
-
- if ( $r['include_selected'] && ! $found_selected && ( $r['selected'] > 0 ) ) {
- $user = get_userdata( $r['selected'] );
- $_selected = selected( $user->ID, $r['selected'], false );
- $display = ! empty( $user->$show ) ? $user->$show : '('. $user->user_login . ')';
- $output .= "\t<option value='$user->ID'$_selected>" . esc_html( $display ) . "</option>\n";
- }
-
- $output .= "</select>";
- }
-
- /**
- * Filter the wp_dropdown_users() HTML output.
- *
- * @since 2.3.0
- *
- * @param string $output HTML output generated by wp_dropdown_users().
- */
- $html = apply_filters( 'wp_dropdown_users', $output );
-
- if ( $r['echo'] ) {
- echo $html;
- }
- return $html;
-}
-
-/**
- * Sanitize user field based on context.
- *
- * Possible context values are: 'raw', 'edit', 'db', 'display', 'attribute' and 'js'. The
- * 'display' context is used by default. 'attribute' and 'js' contexts are treated like 'display'
- * when calling filters.
- *
- * @since 2.3.0
- *
- * @param string $field The user Object field name.
- * @param mixed $value The user Object value.
- * @param int $user_id User ID.
- * @param string $context How to sanitize user fields. Looks for 'raw', 'edit', 'db', 'display',
- * 'attribute' and 'js'.
- * @return mixed Sanitized value.
- */
-function sanitize_user_field($field, $value, $user_id, $context) {
- $int_fields = array('ID');
- if ( in_array($field, $int_fields) )
- $value = (int) $value;
-
- if ( 'raw' == $context )
- return $value;
-
- if ( !is_string($value) && !is_numeric($value) )
- return $value;
-
- $prefixed = false !== strpos( $field, 'user_' );
-
- if ( 'edit' == $context ) {
- if ( $prefixed ) {
-
- /** This filter is documented in wp-includes/post-functions.php */
- $value = apply_filters( "edit_{$field}", $value, $user_id );
- } else {
-
- /**
- * Filter a user field value in the 'edit' context.
- *
- * The dynamic portion of the hook name, `$field`, refers to the prefixed user
- * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
- *
- * @since 2.9.0
- *
- * @param mixed $value Value of the prefixed user field.
- * @param int $user_id User ID.
- */
- $value = apply_filters( "edit_user_{$field}", $value, $user_id );
- }
-
- if ( 'description' == $field )
- $value = esc_html( $value ); // textarea_escaped?
- else
- $value = esc_attr($value);
- } elseif ( 'db' == $context ) {
- if ( $prefixed ) {
- /** This filter is documented in wp-includes/post-functions.php */
- $value = apply_filters( "pre_{$field}", $value );
- } else {
-
- /**
- * Filter the value of a user field in the 'db' context.
- *
- * The dynamic portion of the hook name, `$field`, refers to the prefixed user
- * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
- *
- * @since 2.9.0
- *
- * @param mixed $value Value of the prefixed user field.
- */
- $value = apply_filters( "pre_user_{$field}", $value );
- }
- } else {
- // Use display filters by default.
- if ( $prefixed ) {
-
- /** This filter is documented in wp-includes/post-functions.php */
- $value = apply_filters( $field, $value, $user_id, $context );
- } else {
-
- /**
- * Filter the value of a user field in a standard context.
- *
- * The dynamic portion of the hook name, `$field`, refers to the prefixed user
- * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
- *
- * @since 2.9.0
- *
- * @param mixed $value The user object value to sanitize.
- * @param int $user_id User ID.
- * @param string $context The context to filter within.
- */
- $value = apply_filters( "user_{$field}", $value, $user_id, $context );
- }
- }
-
- if ( 'user_url' == $field )
- $value = esc_url($value);
-
- if ( 'attribute' == $context ) {
- $value = esc_attr( $value );
- } elseif ( 'js' == $context ) {
- $value = esc_js( $value );
- }
- return $value;
-}
-
-/**
- * Update all user caches
- *
- * @since 3.0.0
- *
- * @param object|WP_User $user User object to be cached
- * @return bool|null Returns false on failure.
- */
-function update_user_caches( $user ) {
- if ( $user instanceof WP_User ) {
- if ( ! $user->exists() ) {
- return false;
- }
-
- $user = $user->data;
- }
-
- wp_cache_add($user->ID, $user, 'users');
- wp_cache_add($user->user_login, $user->ID, 'userlogins');
- wp_cache_add($user->user_email, $user->ID, 'useremail');
- wp_cache_add($user->user_nicename, $user->ID, 'userslugs');
-}
-
-/**
- * Clean all user caches
- *
- * @since 3.0.0
- * @since 4.4.0 'clean_user_cache' action was added.
- *
- * @param WP_User|int $user User object or ID to be cleaned from the cache
- */
-function clean_user_cache( $user ) {
- if ( is_numeric( $user ) )
- $user = new WP_User( $user );
-
- if ( ! $user->exists() )
- return;
-
- wp_cache_delete( $user->ID, 'users' );
- wp_cache_delete( $user->user_login, 'userlogins' );
- wp_cache_delete( $user->user_email, 'useremail' );
- wp_cache_delete( $user->user_nicename, 'userslugs' );
-
- /**
- * Fires immediately after the given user's cache is cleaned.
- *
- * @since 4.4.0
- *
- * @param int $user_id User ID.
- * @param WP_User $user User object.
- */
- do_action( 'clean_user_cache', $user->ID, $user );
-}
-
-/**
- * Checks whether the given username exists.
- *
- * @since 2.0.0
- *
- * @param string $username Username.
- * @return int|false The user's ID on success, and false on failure.
- */
-function username_exists( $username ) {
- if ( $user = get_user_by( 'login', $username ) ) {
- return $user->ID;
- }
- return false;
-}
-
-/**
- * Checks whether the given email exists.
- *
- * @since 2.1.0
- *
- * @param string $email Email.
- * @return int|false The user's ID on success, and false on failure.
- */
-function email_exists( $email ) {
- if ( $user = get_user_by( 'email', $email) ) {
- return $user->ID;
- }
- return false;
-}
-
-/**
- * Checks whether a username is valid.
- *
- * @since 2.0.1
- * @since 4.4.0 Empty sanitized usernames are now considered invalid
- *
- * @param string $username Username.
- * @return bool Whether username given is valid
- */
-function validate_username( $username ) {
- $sanitized = sanitize_user( $username, true );
- $valid = ( $sanitized == $username && ! empty( $sanitized ) );
-
- /**
- * Filter whether the provided username is valid or not.
- *
- * @since 2.0.1
- *
- * @param bool $valid Whether given username is valid.
- * @param string $username Username to check.
- */
- return apply_filters( 'validate_username', $valid, $username );
-}
-
-/**
- * Insert a user into the database.
- *
- * Most of the `$userdata` array fields have filters associated with the values. Exceptions are
- * 'ID', 'rich_editing', 'comment_shortcuts', 'admin_color', 'use_ssl',
- * 'user_registered', and 'role'. The filters have the prefix 'pre_user_' followed by the field
- * name. An example using 'description' would have the filter called, 'pre_user_description' that
- * can be hooked into.
- *
- * @since 2.0.0
- * @since 3.6.0 The `aim`, `jabber`, and `yim` fields were removed as default user contact
- * methods for new installs. See wp_get_user_contact_methods().
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- *
- * @param array|object|WP_User $userdata {
- * An array, object, or WP_User object of user data arguments.
- *
- * @type int $ID User ID. If supplied, the user will be updated.
- * @type string $user_pass The plain-text user password.
- * @type string $user_login The user's login username.
- * @type string $user_nicename The URL-friendly user name.
- * @type string $user_url The user URL.
- * @type string $user_email The user email address.
- * @type string $display_name The user's display name.
- * Default is the the user's username.
- * @type string $nickname The user's nickname.
- * Default is the the user's username.
- * @type string $first_name The user's first name. For new users, will be used
- * to build the first part of the user's display name
- * if `$display_name` is not specified.
- * @type string $last_name The user's last name. For new users, will be used
- * to build the second part of the user's display name
- * if `$display_name` is not specified.
- * @type string $description The user's biographical description.
- * @type string|bool $rich_editing Whether to enable the rich-editor for the user.
- * False if not empty.
- * @type string|bool $comment_shortcuts Whether to enable comment moderation keyboard
- * shortcuts for the user. Default false.
- * @type string $admin_color Admin color scheme for the user. Default 'fresh'.
- * @type bool $use_ssl Whether the user should always access the admin over
- * https. Default false.
- * @type string $user_registered Date the user registered. Format is 'Y-m-d H:i:s'.
- * @type string|bool $show_admin_bar_front Whether to display the Admin Bar for the user on the
- * site's frontend. Default true.
- * @type string $role User's role.
- * }
- * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not
- * be created.
- */
-function wp_insert_user( $userdata ) {
- global $wpdb;
-
- if ( $userdata instanceof stdClass ) {
- $userdata = get_object_vars( $userdata );
- } elseif ( $userdata instanceof WP_User ) {
- $userdata = $userdata->to_array();
- }
-
- // Are we updating or creating?
- if ( ! empty( $userdata['ID'] ) ) {
- $ID = (int) $userdata['ID'];
- $update = true;
- $old_user_data = get_userdata( $ID );
-
- if ( ! $old_user_data ) {
- return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
- }
-
- // hashed in wp_update_user(), plaintext if called directly
- $user_pass = ! empty( $userdata['user_pass'] ) ? $userdata['user_pass'] : $old_user_data->user_pass;
- } else {
- $update = false;
- // Hash the password
- $user_pass = wp_hash_password( $userdata['user_pass'] );
- }
-
- $sanitized_user_login = sanitize_user( $userdata['user_login'], true );
-
- /**
- * Filter a username after it has been sanitized.
- *
- * This filter is called before the user is created or updated.
- *
- * @since 2.0.3
- *
- * @param string $sanitized_user_login Username after it has been sanitized.
- */
- $pre_user_login = apply_filters( 'pre_user_login', $sanitized_user_login );
-
- //Remove any non-printable chars from the login string to see if we have ended up with an empty username
- $user_login = trim( $pre_user_login );
-
- // user_login must be between 0 and 60 characters.
- if ( empty( $user_login ) ) {
- return new WP_Error('empty_user_login', __('Cannot create a user with an empty login name.') );
- } elseif ( mb_strlen( $user_login ) > 60 ) {
- return new WP_Error( 'user_login_too_long', __( 'Username may not be longer than 60 characters.' ) );
- }
-
- if ( ! $update && username_exists( $user_login ) ) {
- return new WP_Error( 'existing_user_login', __( 'Sorry, that username already exists!' ) );
- }
-
- /**
- * Filter the list of blacklisted usernames.
- *
- * @since 4.4.0
- *
- * @param array $usernames Array of blacklisted usernames.
- */
- $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
-
- if ( in_array( strtolower( $user_login ), array_map( 'strtolower', $illegal_logins ) ) ) {
- return new WP_Error( 'illegal_user_login', __( 'Sorry, that username is not allowed.' ) );
- }
-
- /*
- * If a nicename is provided, remove unsafe user characters before using it.
- * Otherwise build a nicename from the user_login.
- */
- if ( ! empty( $userdata['user_nicename'] ) ) {
- $user_nicename = sanitize_user( $userdata['user_nicename'], true );
- if ( mb_strlen( $user_nicename ) > 50 ) {
- return new WP_Error( 'user_nicename_too_long', __( 'Nicename may not be longer than 50 characters.' ) );
- }
- } else {
- $user_nicename = mb_substr( $user_login, 0, 50 );
- }
-
- $user_nicename = sanitize_title( $user_nicename );
-
- // Store values to save in user meta.
- $meta = array();
-
- /**
- * Filter a user's nicename before the user is created or updated.
- *
- * @since 2.0.3
- *
- * @param string $user_nicename The user's nicename.
- */
- $user_nicename = apply_filters( 'pre_user_nicename', $user_nicename );
-
- $raw_user_url = empty( $userdata['user_url'] ) ? '' : $userdata['user_url'];
-
- /**
- * Filter a user's URL before the user is created or updated.
- *
- * @since 2.0.3
- *
- * @param string $raw_user_url The user's URL.
- */
- $user_url = apply_filters( 'pre_user_url', $raw_user_url );
-
- $raw_user_email = empty( $userdata['user_email'] ) ? '' : $userdata['user_email'];
-
- /**
- * Filter a user's email before the user is created or updated.
- *
- * @since 2.0.3
- *
- * @param string $raw_user_email The user's email.
- */
- $user_email = apply_filters( 'pre_user_email', $raw_user_email );
-
- /*
- * If there is no update, just check for `email_exists`. If there is an update,
- * check if current email and new email are the same, or not, and check `email_exists`
- * accordingly.
- */
- if ( ( ! $update || ( ! empty( $old_user_data ) && 0 !== strcasecmp( $user_email, $old_user_data->user_email ) ) )
- && ! defined( 'WP_IMPORTING' )
- && email_exists( $user_email )
- ) {
- return new WP_Error( 'existing_user_email', __( 'Sorry, that email address is already used!' ) );
- }
- $nickname = empty( $userdata['nickname'] ) ? $user_login : $userdata['nickname'];
-
- /**
- * Filter a user's nickname before the user is created or updated.
- *
- * @since 2.0.3
- *
- * @param string $nickname The user's nickname.
- */
- $meta['nickname'] = apply_filters( 'pre_user_nickname', $nickname );
-
- $first_name = empty( $userdata['first_name'] ) ? '' : $userdata['first_name'];
-
- /**
- * Filter a user's first name before the user is created or updated.
- *
- * @since 2.0.3
- *
- * @param string $first_name The user's first name.
- */
- $meta['first_name'] = apply_filters( 'pre_user_first_name', $first_name );
-
- $last_name = empty( $userdata['last_name'] ) ? '' : $userdata['last_name'];
-
- /**
- * Filter a user's last name before the user is created or updated.
- *
- * @since 2.0.3
- *
- * @param string $last_name The user's last name.
- */
- $meta['last_name'] = apply_filters( 'pre_user_last_name', $last_name );
-
- if ( empty( $userdata['display_name'] ) ) {
- if ( $update ) {
- $display_name = $user_login;
- } elseif ( $meta['first_name'] && $meta['last_name'] ) {
- /* translators: 1: first name, 2: last name */
- $display_name = sprintf( _x( '%1$s %2$s', 'Display name based on first name and last name' ), $meta['first_name'], $meta['last_name'] );
- } elseif ( $meta['first_name'] ) {
- $display_name = $meta['first_name'];
- } elseif ( $meta['last_name'] ) {
- $display_name = $meta['last_name'];
- } else {
- $display_name = $user_login;
- }
- } else {
- $display_name = $userdata['display_name'];
- }
-
- /**
- * Filter a user's display name before the user is created or updated.
- *
- * @since 2.0.3
- *
- * @param string $display_name The user's display name.
- */
- $display_name = apply_filters( 'pre_user_display_name', $display_name );
-
- $description = empty( $userdata['description'] ) ? '' : $userdata['description'];
-
- /**
- * Filter a user's description before the user is created or updated.
- *
- * @since 2.0.3
- *
- * @param string $description The user's description.
- */
- $meta['description'] = apply_filters( 'pre_user_description', $description );
-
- $meta['rich_editing'] = empty( $userdata['rich_editing'] ) ? 'true' : $userdata['rich_editing'];
-
- $meta['comment_shortcuts'] = empty( $userdata['comment_shortcuts'] ) || 'false' === $userdata['comment_shortcuts'] ? 'false' : 'true';
-
- $admin_color = empty( $userdata['admin_color'] ) ? 'fresh' : $userdata['admin_color'];
- $meta['admin_color'] = preg_replace( '|[^a-z0-9 _.\-@]|i', '', $admin_color );
-
- $meta['use_ssl'] = empty( $userdata['use_ssl'] ) ? 0 : $userdata['use_ssl'];
-
- $user_registered = empty( $userdata['user_registered'] ) ? gmdate( 'Y-m-d H:i:s' ) : $userdata['user_registered'];
-
- $meta['show_admin_bar_front'] = empty( $userdata['show_admin_bar_front'] ) ? 'true' : $userdata['show_admin_bar_front'];
-
- $user_nicename_check = $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1" , $user_nicename, $user_login));
-
- if ( $user_nicename_check ) {
- $suffix = 2;
- while ($user_nicename_check) {
- // user_nicename allows 50 chars. Subtract one for a hyphen, plus the length of the suffix.
- $base_length = 49 - mb_strlen( $suffix );
- $alt_user_nicename = mb_substr( $user_nicename, 0, $base_length ) . "-$suffix";
- $user_nicename_check = $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1" , $alt_user_nicename, $user_login));
- $suffix++;
- }
- $user_nicename = $alt_user_nicename;
- }
-
- $compacted = compact( 'user_pass', 'user_email', 'user_url', 'user_nicename', 'display_name', 'user_registered' );
- $data = wp_unslash( $compacted );
-
- if ( $update ) {
- if ( $user_email !== $old_user_data->user_email ) {
- $data['user_activation_key'] = '';
- }
- $wpdb->update( $wpdb->users, $data, compact( 'ID' ) );
- $user_id = (int) $ID;
- } else {
- $wpdb->insert( $wpdb->users, $data + compact( 'user_login' ) );
- $user_id = (int) $wpdb->insert_id;
- }
-
- $user = new WP_User( $user_id );
-
- /**
- * Filter a user's meta values and keys before the user is created or updated.
- *
- * Does not include contact methods. These are added using `wp_get_user_contact_methods( $user )`.
- *
- * @since 4.4.0
- *
- * @param array $meta {
- * Default meta values and keys for the user.
- *
- * @type string $nickname The user's nickname. Default is the the user's username.
- * @type string $first_name The user's first name.
- * @type string $last_name The user's last name.
- * @type string $description The user's description.
- * @type bool $rich_editing Whether to enable the rich-editor for the user. False if not empty.
- * @type bool $comment_shortcuts Whether to enable keyboard shortcuts for the user. Default false.
- * @type string $admin_color The color scheme for a user's admin screen. Default 'fresh'.
- * @type int|bool $use_ssl Whether to force SSL on the user's admin area. 0|false if SSL is
- * not forced.
- * @type bool $show_admin_bar_front Whether to show the admin bar on the front end for the user.
- * Default true.
- * }
- * @param WP_User $user User object.
- * @param bool $update Whether the user is being updated rather than created.
- */
- $meta = apply_filters( 'insert_user_meta', $meta, $user, $update );
-
- // Update user meta.
- foreach ( $meta as $key => $value ) {
- update_user_meta( $user_id, $key, $value );
- }
-
- foreach ( wp_get_user_contact_methods( $user ) as $key => $value ) {
- if ( isset( $userdata[ $key ] ) ) {
- update_user_meta( $user_id, $key, $userdata[ $key ] );
- }
- }
-
- if ( isset( $userdata['role'] ) ) {
- $user->set_role( $userdata['role'] );
- } elseif ( ! $update ) {
- $user->set_role(get_option('default_role'));
- }
- wp_cache_delete( $user_id, 'users' );
- wp_cache_delete( $user_login, 'userlogins' );
-
- if ( $update ) {
- /**
- * Fires immediately after an existing user is updated.
- *
- * @since 2.0.0
- *
- * @param int $user_id User ID.
- * @param object $old_user_data Object containing user's data prior to update.
- */
- do_action( 'profile_update', $user_id, $old_user_data );
- } else {
- /**
- * Fires immediately after a new user is registered.
- *
- * @since 1.5.0
- *
- * @param int $user_id User ID.
- */
- do_action( 'user_register', $user_id );
- }
-
- return $user_id;
-}
-
-/**
- * Update a user in the database.
- *
- * It is possible to update a user's password by specifying the 'user_pass'
- * value in the $userdata parameter array.
- *
- * If current user's password is being updated, then the cookies will be
- * cleared.
- *
- * @since 2.0.0
- *
- * @see wp_insert_user() For what fields can be set in $userdata.
- *
- * @param mixed $userdata An array of user data or a user object of type stdClass or WP_User.
- * @return int|WP_Error The updated user's ID or a WP_Error object if the user could not be updated.
- */
-function wp_update_user($userdata) {
- if ( $userdata instanceof stdClass ) {
- $userdata = get_object_vars( $userdata );
- } elseif ( $userdata instanceof WP_User ) {
- $userdata = $userdata->to_array();
- }
-
- $ID = isset( $userdata['ID'] ) ? (int) $userdata['ID'] : 0;
- if ( ! $ID ) {
- return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
- }
-
- // First, get all of the original fields
- $user_obj = get_userdata( $ID );
- if ( ! $user_obj ) {
- return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
- }
-
- $user = $user_obj->to_array();
-
- // Add additional custom fields
- foreach ( _get_additional_user_keys( $user_obj ) as $key ) {
- $user[ $key ] = get_user_meta( $ID, $key, true );
- }
-
- // Escape data pulled from DB.
- $user = add_magic_quotes( $user );
-
- if ( ! empty( $userdata['user_pass'] ) && $userdata['user_pass'] !== $user_obj->user_pass ) {
- // If password is changing, hash it now
- $plaintext_pass = $userdata['user_pass'];
- $userdata['user_pass'] = wp_hash_password( $userdata['user_pass'] );
-
- /**
- * Filter whether to send the password change email.
- *
- * @since 4.3.0
- *
- * @see wp_insert_user() For `$user` and `$userdata` fields.
- *
- * @param bool $send Whether to send the email.
- * @param array $user The original user array.
- * @param array $userdata The updated user array.
- *
- */
- $send_password_change_email = apply_filters( 'send_password_change_email', true, $user, $userdata );
- }
-
- if ( isset( $userdata['user_email'] ) && $user['user_email'] !== $userdata['user_email'] ) {
- /**
- * Filter whether to send the email change email.
- *
- * @since 4.3.0
- *
- * @see wp_insert_user() For `$user` and `$userdata` fields.
- *
- * @param bool $send Whether to send the email.
- * @param array $user The original user array.
- * @param array $userdata The updated user array.
- *
- */
- $send_email_change_email = apply_filters( 'send_email_change_email', true, $user, $userdata );
- }
-
- wp_cache_delete( $user['user_email'], 'useremail' );
-
- // Merge old and new fields with new fields overwriting old ones.
- $userdata = array_merge( $user, $userdata );
- $user_id = wp_insert_user( $userdata );
-
- if ( ! is_wp_error( $user_id ) ) {
-
- $blog_name = wp_specialchars_decode( get_option( 'blogname' ) );
-
- if ( ! empty( $send_password_change_email ) ) {
-
- /* translators: Do not translate USERNAME, ADMIN_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */
- $pass_change_text = __( 'Hi ###USERNAME###,
-
-This notice confirms that your password was changed on ###SITENAME###.
-
-If you did not change your password, please contact the Site Administrator at
-###ADMIN_EMAIL###
-
-This email has been sent to ###EMAIL###
-
-Regards,
-All at ###SITENAME###
-###SITEURL###' );
-
- $pass_change_email = array(
- 'to' => $user['user_email'],
- 'subject' => __( '[%s] Notice of Password Change' ),
- 'message' => $pass_change_text,
- 'headers' => '',
- );
-
- /**
- * Filter the contents of the email sent when the user's password is changed.
- *
- * @since 4.3.0
- *
- * @param array $pass_change_email {
- * Used to build wp_mail().
- * @type string $to The intended recipients. Add emails in a comma separated string.
- * @type string $subject The subject of the email.
- * @type string $message The content of the email.
- * The following strings have a special meaning and will get replaced dynamically:
- * - ###USERNAME### The current user's username.
- * - ###ADMIN_EMAIL### The admin email in case this was unexpected.
- * - ###EMAIL### The old email.
- * - ###SITENAME### The name of the site.
- * - ###SITEURL### The URL to the site.
- * @type string $headers Headers. Add headers in a newline (\r\n) separated string.
- * }
- * @param array $user The original user array.
- * @param array $userdata The updated user array.
- *
- */
- $pass_change_email = apply_filters( 'password_change_email', $pass_change_email, $user, $userdata );
-
- $pass_change_email['message'] = str_replace( '###USERNAME###', $user['user_login'], $pass_change_email['message'] );
- $pass_change_email['message'] = str_replace( '###ADMIN_EMAIL###', get_option( 'admin_email' ), $pass_change_email['message'] );
- $pass_change_email['message'] = str_replace( '###EMAIL###', $user['user_email'], $pass_change_email['message'] );
- $pass_change_email['message'] = str_replace( '###SITENAME###', get_option( 'blogname' ), $pass_change_email['message'] );
- $pass_change_email['message'] = str_replace( '###SITEURL###', home_url(), $pass_change_email['message'] );
-
- wp_mail( $pass_change_email['to'], sprintf( $pass_change_email['subject'], $blog_name ), $pass_change_email['message'], $pass_change_email['headers'] );
- }
-
- if ( ! empty( $send_email_change_email ) ) {
- /* translators: Do not translate USERNAME, ADMIN_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */
- $email_change_text = __( 'Hi ###USERNAME###,
-
-This notice confirms that your email was changed on ###SITENAME###.
-
-If you did not change your email, please contact the Site Administrator at
-###ADMIN_EMAIL###
-
-This email has been sent to ###EMAIL###
-
-Regards,
-All at ###SITENAME###
-###SITEURL###' );
-
- $email_change_email = array(
- 'to' => $user['user_email'],
- 'subject' => __( '[%s] Notice of Email Change' ),
- 'message' => $email_change_text,
- 'headers' => '',
- );
-
- /**
- * Filter the contents of the email sent when the user's email is changed.
- *
- * @since 4.3.0
- *
- * @param array $email_change_email {
- * Used to build wp_mail().
- * @type string $to The intended recipients.
- * @type string $subject The subject of the email.
- * @type string $message The content of the email.
- * The following strings have a special meaning and will get replaced dynamically:
- * - ###USERNAME### The current user's username.
- * - ###ADMIN_EMAIL### The admin email in case this was unexpected.
- * - ###EMAIL### The old email.
- * - ###SITENAME### The name of the site.
- * - ###SITEURL### The URL to the site.
- * @type string $headers Headers.
- * }
- * @param array $user The original user array.
- * @param array $userdata The updated user array.
- */
- $email_change_email = apply_filters( 'email_change_email', $email_change_email, $user, $userdata );
-
- $email_change_email['message'] = str_replace( '###USERNAME###', $user['user_login'], $email_change_email['message'] );
- $email_change_email['message'] = str_replace( '###ADMIN_EMAIL###', get_option( 'admin_email' ), $email_change_email['message'] );
- $email_change_email['message'] = str_replace( '###EMAIL###', $user['user_email'], $email_change_email['message'] );
- $email_change_email['message'] = str_replace( '###SITENAME###', get_option( 'blogname' ), $email_change_email['message'] );
- $email_change_email['message'] = str_replace( '###SITEURL###', home_url(), $email_change_email['message'] );
-
- wp_mail( $email_change_email['to'], sprintf( $email_change_email['subject'], $blog_name ), $email_change_email['message'], $email_change_email['headers'] );
- }
- }
-
- // Update the cookies if the password changed.
- $current_user = wp_get_current_user();
- if ( $current_user->ID == $ID ) {
- if ( isset($plaintext_pass) ) {
- wp_clear_auth_cookie();
-
- // Here we calculate the expiration length of the current auth cookie and compare it to the default expiration.
- // If it's greater than this, then we know the user checked 'Remember Me' when they logged in.
- $logged_in_cookie = wp_parse_auth_cookie( '', 'logged_in' );
- /** This filter is documented in wp-includes/pluggable.php */
- $default_cookie_life = apply_filters( 'auth_cookie_expiration', ( 2 * DAY_IN_SECONDS ), $ID, false );
- $remember = ( ( $logged_in_cookie['expiration'] - time() ) > $default_cookie_life );
-
- wp_set_auth_cookie( $ID, $remember );
- }
- }
-
- return $user_id;
-}
-
-/**
- * A simpler way of inserting a user into the database.
- *
- * Creates a new user with just the username, password, and email. For more
- * complex user creation use {@see wp_insert_user()} to specify more information.
- *
- * @since 2.0.0
- * @see wp_insert_user() More complete way to create a new user
- *
- * @param string $username The user's username.
- * @param string $password The user's password.
- * @param string $email Optional. The user's email. Default empty.
- * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not
- * be created.
- */
-function wp_create_user($username, $password, $email = '') {
- $user_login = wp_slash( $username );
- $user_email = wp_slash( $email );
- $user_pass = $password;
-
- $userdata = compact('user_login', 'user_email', 'user_pass');
- return wp_insert_user($userdata);
-}
-
-/**
- * Returns a list of meta keys to be (maybe) populated in wp_update_user().
- *
- * The list of keys returned via this function are dependent on the presence
- * of those keys in the user meta data to be set.
- *
- * @since 3.3.0
- * @access private
- *
- * @param WP_User $user WP_User instance.
- * @return array List of user keys to be populated in wp_update_user().
- */
-function _get_additional_user_keys( $user ) {
- $keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front' );
- return array_merge( $keys, array_keys( wp_get_user_contact_methods( $user ) ) );
-}
-
-/**
- * Set up the user contact methods.
- *
- * Default contact methods were removed in 3.6. A filter dictates contact methods.
- *
- * @since 3.7.0
- *
- * @param WP_User $user Optional. WP_User object.
- * @return array Array of contact methods and their labels.
- */
-function wp_get_user_contact_methods( $user = null ) {
- $methods = array();
- if ( get_site_option( 'initial_db_version' ) < 23588 ) {
- $methods = array(
- 'aim' => __( 'AIM' ),
- 'yim' => __( 'Yahoo IM' ),
- 'jabber' => __( 'Jabber / Google Talk' )
- );
- }
-
- /**
- * Filter the user contact methods.
- *
- * @since 2.9.0
- *
- * @param array $methods Array of contact methods and their labels.
- * @param WP_User $user WP_User object.
- */
- return apply_filters( 'user_contactmethods', $methods, $user );
-}
-
-/**
- * The old private function for setting up user contact methods.
- *
- * @since 2.9.0
- * @access private
- */
-function _wp_get_user_contactmethods( $user = null ) {
- return wp_get_user_contact_methods( $user );
-}
-
-/**
- * Gets the text suggesting how to create strong passwords.
- *
- * @since 4.1.0
- *
- * @return string The password hint text.
- */
-function wp_get_password_hint() {
- $hint = __( 'Hint: The password should be at least twelve characters long. To make it stronger, use upper and lower case letters, numbers, and symbols like ! " ? $ % ^ & ).' );
-
- /**
- * Filter the text describing the site's password complexity policy.
- *
- * @since 4.1.0
- *
- * @param string $hint The password hint text.
- */
- return apply_filters( 'password_hint', $hint );
-}
-
-/**
- * Creates, stores, then returns a password reset key for user.
- *
- * @since 4.4.0
- *
- * @global wpdb $wpdb WordPress database abstraction object.
- * @global PasswordHash $wp_hasher Portable PHP password hashing framework.
- *
- * @param WP_User $user User to retrieve password reset key for.
- *
- * @return string|WP_Error Password reset key on success. WP_Error on error.
- */
-function get_password_reset_key( $user ) {
- global $wpdb, $wp_hasher;
-
- /**
- * Fires before a new password is retrieved.
- *
- * @since 1.5.0
- * @deprecated 1.5.1 Misspelled. Use 'retrieve_password' hook instead.
- *
- * @param string $user_login The user login name.
- */
- do_action( 'retreive_password', $user->user_login );
-
- /**
- * Fires before a new password is retrieved.
- *
- * @since 1.5.1
- *
- * @param string $user_login The user login name.
- */
- do_action( 'retrieve_password', $user->user_login );
-
- /**
- * Filter whether to allow a password to be reset.
- *
- * @since 2.7.0
- *
- * @param bool true Whether to allow the password to be reset. Default true.
- * @param int $user_data->ID The ID of the user attempting to reset a password.
- */
- $allow = apply_filters( 'allow_password_reset', true, $user->ID );
-
- if ( ! $allow ) {
- return new WP_Error( 'no_password_reset', __( 'Password reset is not allowed for this user' ) );
- } elseif ( is_wp_error( $allow ) ) {
- return $allow;
- }
-
- // Generate something random for a password reset key.
- $key = wp_generate_password( 20, false );
-
- /**
- * Fires when a password reset key is generated.
- *
- * @since 2.5.0
- *
- * @param string $user_login The username for the user.
- * @param string $key The generated password reset key.
- */
- do_action( 'retrieve_password_key', $user->user_login, $key );
-
- // Now insert the key, hashed, into the DB.
- if ( empty( $wp_hasher ) ) {
- require_once ABSPATH . WPINC . '/class-phpass.php';
- $wp_hasher = new PasswordHash( 8, true );
- }
- $hashed = time() . ':' . $wp_hasher->HashPassword( $key );
- $key_saved = $wpdb->update( $wpdb->users, array( 'user_activation_key' => $hashed ), array( 'user_login' => $user->user_login ) );
- if ( false === $key_saved ) {
- return WP_Error( 'no_password_key_update', __( 'Could not save password reset key to database.' ) );
- }
-
- return $key;
-}
-
-/**
- * Retrieves a user row based on password reset key and login
- *
- * A key is considered 'expired' if it exactly matches the value of the
- * user_activation_key field, rather than being matched after going through the
- * hashing process. This field is now hashed; old values are no longer accepted
- * but have a different WP_Error code so good user feedback can be provided.
- *
- * @since 3.1.0
- *
- * @global wpdb $wpdb WordPress database object for queries.
- * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
- *
- * @param string $key Hash to validate sending user's password.
- * @param string $login The user login.
- * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
- */
-function check_password_reset_key($key, $login) {
- global $wpdb, $wp_hasher;
-
- $key = preg_replace('/[^a-z0-9]/i', '', $key);
-
- if ( empty( $key ) || !is_string( $key ) )
- return new WP_Error('invalid_key', __('Invalid key'));
-
- if ( empty($login) || !is_string($login) )
- return new WP_Error('invalid_key', __('Invalid key'));
-
- $row = $wpdb->get_row( $wpdb->prepare( "SELECT ID, user_activation_key FROM $wpdb->users WHERE user_login = %s", $login ) );
- if ( ! $row )
- return new WP_Error('invalid_key', __('Invalid key'));
-
- if ( empty( $wp_hasher ) ) {
- require_once ABSPATH . WPINC . '/class-phpass.php';
- $wp_hasher = new PasswordHash( 8, true );
- }
-
- /**
- * Filter the expiration time of password reset keys.
- *
- * @since 4.3.0
- *
- * @param int $expiration The expiration time in seconds.
- */
- $expiration_duration = apply_filters( 'password_reset_expiration', DAY_IN_SECONDS );
-
- if ( false !== strpos( $row->user_activation_key, ':' ) ) {
- list( $pass_request_time, $pass_key ) = explode( ':', $row->user_activation_key, 2 );
- $expiration_time = $pass_request_time + $expiration_duration;
- } else {
- $pass_key = $row->user_activation_key;
- $expiration_time = false;
- }
-
- $hash_is_correct = $wp_hasher->CheckPassword( $key, $pass_key );
-
- if ( $hash_is_correct && $expiration_time && time() < $expiration_time ) {
- return get_userdata( $row->ID );
- } elseif ( $hash_is_correct && $expiration_time ) {
- // Key has an expiration time that's passed
- return new WP_Error( 'expired_key', __( 'Invalid key' ) );
- }
-
- if ( hash_equals( $row->user_activation_key, $key ) || ( $hash_is_correct && ! $expiration_time ) ) {
- $return = new WP_Error( 'expired_key', __( 'Invalid key' ) );
- $user_id = $row->ID;
-
- /**
- * Filter the return value of check_password_reset_key() when an
- * old-style key is used.
- *
- * @since 3.7.0 Previously plain-text keys were stored in the database.
- * @since 4.3.0 Previously key hashes were stored without an expiration time.
- *
- * @param WP_Error $return A WP_Error object denoting an expired key.
- * Return a WP_User object to validate the key.
- * @param int $user_id The matched user ID.
- */
- return apply_filters( 'password_reset_key_expired', $return, $user_id );
- }
-
- return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
-}
-
-/**
- * Handles resetting the user's password.
- *
- * @since 2.5.0
- *
- * @param object $user The user
- * @param string $new_pass New password for the user in plaintext
- */
-function reset_password( $user, $new_pass ) {
- /**
- * Fires before the user's password is reset.
- *
- * @since 1.5.0
- *
- * @param object $user The user.
- * @param string $new_pass New user password.
- */
- do_action( 'password_reset', $user, $new_pass );
-
- wp_set_password( $new_pass, $user->ID );
- update_user_option( $user->ID, 'default_password_nag', false, true );
-
- /**
- * Fires after the user's password is reset.
- *
- * @since 4.4.0
- *
- * @param object $user The user.
- * @param string $new_pass New user password.
- */
- do_action( 'after_password_reset', $user, $new_pass );
-}
-
-/**
- * Handles registering a new user.
- *
- * @since 2.5.0
- *
- * @param string $user_login User's username for logging in
- * @param string $user_email User's email address to send password and add
- * @return int|WP_Error Either user's ID or error on failure.
- */
-function register_new_user( $user_login, $user_email ) {
- $errors = new WP_Error();
-
- $sanitized_user_login = sanitize_user( $user_login );
- /**
- * Filter the email address of a user being registered.
- *
- * @since 2.1.0
- *
- * @param string $user_email The email address of the new user.
- */
- $user_email = apply_filters( 'user_registration_email', $user_email );
-
- // Check the username
- if ( $sanitized_user_login == '' ) {
- $errors->add( 'empty_username', __( '<strong>ERROR</strong>: Please enter a username.' ) );
- } elseif ( ! validate_username( $user_login ) ) {
- $errors->add( 'invalid_username', __( '<strong>ERROR</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ) );
- $sanitized_user_login = '';
- } elseif ( username_exists( $sanitized_user_login ) ) {
- $errors->add( 'username_exists', __( '<strong>ERROR</strong>: This username is already registered. Please choose another one.' ) );
- }
-
- // Check the email address
- if ( $user_email == '' ) {
- $errors->add( 'empty_email', __( '<strong>ERROR</strong>: Please type your email address.' ) );
- } elseif ( ! is_email( $user_email ) ) {
- $errors->add( 'invalid_email', __( '<strong>ERROR</strong>: The email address isn’t correct.' ) );
- $user_email = '';
- } elseif ( email_exists( $user_email ) ) {
- $errors->add( 'email_exists', __( '<strong>ERROR</strong>: This email is already registered, please choose another one.' ) );
- }
-
- /**
- * Fires when submitting registration form data, before the user is created.
- *
- * @since 2.1.0
- *
- * @param string $sanitized_user_login The submitted username after being sanitized.
- * @param string $user_email The submitted email.
- * @param WP_Error $errors Contains any errors with submitted username and email,
- * e.g., an empty field, an invalid username or email,
- * or an existing username or email.
- */
- do_action( 'register_post', $sanitized_user_login, $user_email, $errors );
-
- /**
- * Filter the errors encountered when a new user is being registered.
- *
- * The filtered WP_Error object may, for example, contain errors for an invalid
- * or existing username or email address. A WP_Error object should always returned,
- * but may or may not contain errors.
- *
- * If any errors are present in $errors, this will abort the user's registration.
- *
- * @since 2.1.0
- *
- * @param WP_Error $errors A WP_Error object containing any errors encountered
- * during registration.
- * @param string $sanitized_user_login User's username after it has been sanitized.
- * @param string $user_email User's email.
- */
- $errors = apply_filters( 'registration_errors', $errors, $sanitized_user_login, $user_email );
-
- if ( $errors->get_error_code() )
- return $errors;
-
- $user_pass = wp_generate_password( 12, false );
- $user_id = wp_create_user( $sanitized_user_login, $user_pass, $user_email );
- if ( ! $user_id || is_wp_error( $user_id ) ) {
- $errors->add( 'registerfail', sprintf( __( '<strong>ERROR</strong>: Couldn’t register you… please contact the <a href="mailto:%s">webmaster</a> !' ), get_option( 'admin_email' ) ) );
- return $errors;
- }
-
- update_user_option( $user_id, 'default_password_nag', true, true ); //Set up the Password change nag.
-
- /**
- * Fires after a new user registration has been recorded.
- *
- * @since 4.4.0
- *
- * @param int $user_id ID of the newly registered user.
- */
- do_action( 'register_new_user', $user_id );
-
- return $user_id;
-}
-
-/**
- * Initiate email notifications related to the creation of new users.
- *
- * Notifications are sent both to the site admin and to the newly created user.
- *
- * @since 4.4.0
- *
- * @param int $user_id ID of the newly created user.
- */
-function wp_send_new_user_notifications( $user_id ) {
- wp_new_user_notification( $user_id, null, 'both' );
-}
-
-/**
- * Retrieve the current session token from the logged_in cookie.
- *
- * @since 4.0.0
- *
- * @return string Token.
- */
-function wp_get_session_token() {
- $cookie = wp_parse_auth_cookie( '', 'logged_in' );
- return ! empty( $cookie['token'] ) ? $cookie['token'] : '';
-}
-
-/**
- * Retrieve a list of sessions for the current user.
- *
- * @since 4.0.0
- * @return array Array of sessions.
- */
-function wp_get_all_sessions() {
- $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
- return $manager->get_all();
-}
-
-/**
- * Remove the current session token from the database.
- *
- * @since 4.0.0
- */
-function wp_destroy_current_session() {
- $token = wp_get_session_token();
- if ( $token ) {
- $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
- $manager->destroy( $token );
- }
-}
-
-/**
- * Remove all but the current session token for the current user for the database.
- *
- * @since 4.0.0
- */
-function wp_destroy_other_sessions() {
- $token = wp_get_session_token();
- if ( $token ) {
- $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
- $manager->destroy_others( $token );
- }
-}
-
-/**
- * Remove all session tokens for the current user from the database.
- *
- * @since 4.0.0
- */
-function wp_destroy_all_sessions() {
- $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
- $manager->destroy_all();
-}
-
-/**
- * Get the user IDs of all users with no role on this site.
- *
- * This function returns an empty array when used on Multisite.
- *
- * @since 4.4.0
- *
- * @return array Array of user IDs.
- */
-function wp_get_users_with_no_role() {
- global $wpdb;
-
- if ( is_multisite() ) {
- return array();
- }
-
- $prefix = $wpdb->get_blog_prefix();
- $regex = implode( '|', wp_roles()->get_names() );
- $regex = preg_replace( '/[^a-zA-Z_\|-]/', '', $regex );
- $users = $wpdb->get_col( $wpdb->prepare( "
- SELECT user_id
- FROM $wpdb->usermeta
- WHERE meta_key = '{$prefix}capabilities'
- AND meta_value NOT REGEXP %s
- ", $regex ) );
-
- return $users;
-}
</del></span></pre></div>
<a id="trunksrcwpincludesuserphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/user.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/user.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/user.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,14 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Core User API
- *
- * @package WordPress
- * @subpackage Users
- * @since 2.1.0
- */
-
-/** Core users functionality */
-require_once( ABSPATH . WPINC . '/user-functions.php' );
-
-/** WP_User_Query class */
-require_once( ABSPATH . WPINC . '/class-wp-user-query.php' );
</del></span></pre></div>
<a id="trunksrcwpincludesuserphpfromrev35712trunksrcwpincludesuserfunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/user.php (from rev 35712, trunk/src/wp-includes/user-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/user.php (rev 0)
+++ trunk/src/wp-includes/user.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,2293 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Core User API
+ *
+ * @package WordPress
+ * @subpackage Users
+ */
+
+/**
+ * Authenticate user with remember capability.
+ *
+ * The credentials is an array that has 'user_login', 'user_password', and
+ * 'remember' indices. If the credentials is not given, then the log in form
+ * will be assumed and used if set.
+ *
+ * The various authentication cookies will be set by this function and will be
+ * set for a longer period depending on if the 'remember' credential is set to
+ * true.
+ *
+ * @since 2.5.0
+ *
+ * @global string $auth_secure_cookie
+ *
+ * @param array $credentials Optional. User info in order to sign on.
+ * @param string|bool $secure_cookie Optional. Whether to use secure cookie.
+ * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
+ */
+function wp_signon( $credentials = array(), $secure_cookie = '' ) {
+ if ( empty($credentials) ) {
+ if ( ! empty($_POST['log']) )
+ $credentials['user_login'] = $_POST['log'];
+ if ( ! empty($_POST['pwd']) )
+ $credentials['user_password'] = $_POST['pwd'];
+ if ( ! empty($_POST['rememberme']) )
+ $credentials['remember'] = $_POST['rememberme'];
+ }
+
+ if ( !empty($credentials['remember']) )
+ $credentials['remember'] = true;
+ else
+ $credentials['remember'] = false;
+
+ /**
+ * Fires before the user is authenticated.
+ *
+ * The variables passed to the callbacks are passed by reference,
+ * and can be modified by callback functions.
+ *
+ * @since 1.5.1
+ *
+ * @todo Decide whether to deprecate the wp_authenticate action.
+ *
+ * @param string $user_login Username, passed by reference.
+ * @param string $user_password User password, passed by reference.
+ */
+ do_action_ref_array( 'wp_authenticate', array( &$credentials['user_login'], &$credentials['user_password'] ) );
+
+ if ( '' === $secure_cookie )
+ $secure_cookie = is_ssl();
+
+ /**
+ * Filter whether to use a secure sign-on cookie.
+ *
+ * @since 3.1.0
+ *
+ * @param bool $secure_cookie Whether to use a secure sign-on cookie.
+ * @param array $credentials {
+ * Array of entered sign-on data.
+ *
+ * @type string $user_login Username.
+ * @type string $user_password Password entered.
+ * @type bool $remember Whether to 'remember' the user. Increases the time
+ * that the cookie will be kept. Default false.
+ * }
+ */
+ $secure_cookie = apply_filters( 'secure_signon_cookie', $secure_cookie, $credentials );
+
+ global $auth_secure_cookie; // XXX ugly hack to pass this to wp_authenticate_cookie
+ $auth_secure_cookie = $secure_cookie;
+
+ add_filter('authenticate', 'wp_authenticate_cookie', 30, 3);
+
+ $user = wp_authenticate($credentials['user_login'], $credentials['user_password']);
+
+ if ( is_wp_error($user) ) {
+ if ( $user->get_error_codes() == array('empty_username', 'empty_password') ) {
+ $user = new WP_Error('', '');
+ }
+
+ return $user;
+ }
+
+ wp_set_auth_cookie($user->ID, $credentials['remember'], $secure_cookie);
+ /**
+ * Fires after the user has successfully logged in.
+ *
+ * @since 1.5.0
+ *
+ * @param string $user_login Username.
+ * @param WP_User $user WP_User object of the logged-in user.
+ */
+ do_action( 'wp_login', $user->user_login, $user );
+ return $user;
+}
+
+/**
+ * Authenticate the user using the username and password.
+ *
+ * @since 2.8.0
+ *
+ * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null.
+ * @param string $username Username for authentication.
+ * @param string $password Password for authentication.
+ * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
+ */
+function wp_authenticate_username_password($user, $username, $password) {
+ if ( $user instanceof WP_User ) {
+ return $user;
+ }
+
+ if ( empty($username) || empty($password) ) {
+ if ( is_wp_error( $user ) )
+ return $user;
+
+ $error = new WP_Error();
+
+ if ( empty($username) )
+ $error->add('empty_username', __('<strong>ERROR</strong>: The username field is empty.'));
+
+ if ( empty($password) )
+ $error->add('empty_password', __('<strong>ERROR</strong>: The password field is empty.'));
+
+ return $error;
+ }
+
+ $user = get_user_by('login', $username);
+
+ if ( !$user ) {
+ return new WP_Error( 'invalid_username',
+ __( '<strong>ERROR</strong>: Invalid username.' ) .
+ ' <a href="' . wp_lostpassword_url() . '">' .
+ __( 'Lost your password?' ) .
+ '</a>'
+ );
+ }
+
+ /**
+ * Filter whether the given user can be authenticated with the provided $password.
+ *
+ * @since 2.5.0
+ *
+ * @param WP_User|WP_Error $user WP_User or WP_Error object if a previous
+ * callback failed authentication.
+ * @param string $password Password to check against the user.
+ */
+ $user = apply_filters( 'wp_authenticate_user', $user, $password );
+ if ( is_wp_error($user) )
+ return $user;
+
+ if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
+ return new WP_Error( 'incorrect_password',
+ sprintf(
+ /* translators: %s: user name */
+ __( '<strong>ERROR</strong>: The password you entered for the username %s is incorrect.' ),
+ '<strong>' . $username . '</strong>'
+ ) .
+ ' <a href="' . wp_lostpassword_url() . '">' .
+ __( 'Lost your password?' ) .
+ '</a>'
+ );
+ }
+
+ return $user;
+}
+
+/**
+ * Authenticate the user using the WordPress auth cookie.
+ *
+ * @since 2.8.0
+ *
+ * @global string $auth_secure_cookie
+ *
+ * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null.
+ * @param string $username Username. If not empty, cancels the cookie authentication.
+ * @param string $password Password. If not empty, cancels the cookie authentication.
+ * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
+ */
+function wp_authenticate_cookie($user, $username, $password) {
+ if ( $user instanceof WP_User ) {
+ return $user;
+ }
+
+ if ( empty($username) && empty($password) ) {
+ $user_id = wp_validate_auth_cookie();
+ if ( $user_id )
+ return new WP_User($user_id);
+
+ global $auth_secure_cookie;
+
+ if ( $auth_secure_cookie )
+ $auth_cookie = SECURE_AUTH_COOKIE;
+ else
+ $auth_cookie = AUTH_COOKIE;
+
+ if ( !empty($_COOKIE[$auth_cookie]) )
+ return new WP_Error('expired_session', __('Please log in again.'));
+
+ // If the cookie is not set, be silent.
+ }
+
+ return $user;
+}
+
+/**
+ * For Multisite blogs, check if the authenticated user has been marked as a
+ * spammer, or if the user's primary blog has been marked as spam.
+ *
+ * @since 3.7.0
+ *
+ * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null.
+ * @return WP_User|WP_Error WP_User on success, WP_Error if the user is considered a spammer.
+ */
+function wp_authenticate_spam_check( $user ) {
+ if ( $user instanceof WP_User && is_multisite() ) {
+ /**
+ * Filter whether the user has been marked as a spammer.
+ *
+ * @since 3.7.0
+ *
+ * @param bool $spammed Whether the user is considered a spammer.
+ * @param WP_User $user User to check against.
+ */
+ $spammed = apply_filters( 'check_is_user_spammed', is_user_spammy(), $user );
+
+ if ( $spammed )
+ return new WP_Error( 'spammer_account', __( '<strong>ERROR</strong>: Your account has been marked as a spammer.' ) );
+ }
+ return $user;
+}
+
+/**
+ * Validate the logged-in cookie.
+ *
+ * Checks the logged-in cookie if the previous auth cookie could not be
+ * validated and parsed.
+ *
+ * This is a callback for the determine_current_user filter, rather than API.
+ *
+ * @since 3.9.0
+ *
+ * @param int|bool $user_id The user ID (or false) as received from the
+ * determine_current_user filter.
+ * @return int|false User ID if validated, false otherwise. If a user ID from
+ * an earlier filter callback is received, that value is returned.
+ */
+function wp_validate_logged_in_cookie( $user_id ) {
+ if ( $user_id ) {
+ return $user_id;
+ }
+
+ if ( is_blog_admin() || is_network_admin() || empty( $_COOKIE[LOGGED_IN_COOKIE] ) ) {
+ return false;
+ }
+
+ return wp_validate_auth_cookie( $_COOKIE[LOGGED_IN_COOKIE], 'logged_in' );
+}
+
+/**
+ * Number of posts user has written.
+ *
+ * @since 3.0.0
+ * @since 4.1.0 Added `$post_type` argument.
+ * @since 4.3.0 Added `$public_only` argument. Added the ability to pass an array
+ * of post types to `$post_type`.
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $userid User ID.
+ * @param array|string $post_type Optional. Single post type or array of post types to count the number of posts for. Default 'post'.
+ * @param bool $public_only Optional. Whether to only return counts for public posts. Default false.
+ * @return int Number of posts the user has written in this post type.
+ */
+function count_user_posts( $userid, $post_type = 'post', $public_only = false ) {
+ global $wpdb;
+
+ $where = get_posts_by_author_sql( $post_type, true, $userid, $public_only );
+
+ $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" );
+
+ /**
+ * Filter the number of posts a user has written.
+ *
+ * @since 2.7.0
+ * @since 4.1.0 Added `$post_type` argument.
+ * @since 4.3.1 Added `$public_only` argument.
+ *
+ * @param int $count The user's post count.
+ * @param int $userid User ID.
+ * @param string|array $post_type Single post type or array of post types to count the number of posts for.
+ * @param bool $public_only Whether to limit counted posts to public posts.
+ */
+ return apply_filters( 'get_usernumposts', $count, $userid, $post_type, $public_only );
+}
+
+/**
+ * Number of posts written by a list of users.
+ *
+ * @since 3.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array $users Array of user IDs.
+ * @param string|array $post_type Optional. Single post type or array of post types to check. Defaults to 'post'.
+ * @param bool $public_only Optional. Only return counts for public posts. Defaults to false.
+ * @return array Amount of posts each user has written.
+ */
+function count_many_users_posts( $users, $post_type = 'post', $public_only = false ) {
+ global $wpdb;
+
+ $count = array();
+ if ( empty( $users ) || ! is_array( $users ) )
+ return $count;
+
+ $userlist = implode( ',', array_map( 'absint', $users ) );
+ $where = get_posts_by_author_sql( $post_type, true, null, $public_only );
+
+ $result = $wpdb->get_results( "SELECT post_author, COUNT(*) FROM $wpdb->posts $where AND post_author IN ($userlist) GROUP BY post_author", ARRAY_N );
+ foreach ( $result as $row ) {
+ $count[ $row[0] ] = $row[1];
+ }
+
+ foreach ( $users as $id ) {
+ if ( ! isset( $count[ $id ] ) )
+ $count[ $id ] = 0;
+ }
+
+ return $count;
+}
+
+//
+// User option functions
+//
+
+/**
+ * Get the current user's ID
+ *
+ * @since MU
+ *
+ * @return int The current user's ID
+ */
+function get_current_user_id() {
+ if ( ! function_exists( 'wp_get_current_user' ) )
+ return 0;
+ $user = wp_get_current_user();
+ return ( isset( $user->ID ) ? (int) $user->ID : 0 );
+}
+
+/**
+ * Retrieve user option that can be either per Site or per Network.
+ *
+ * If the user ID is not given, then the current user will be used instead. If
+ * the user ID is given, then the user data will be retrieved. The filter for
+ * the result, will also pass the original option name and finally the user data
+ * object as the third parameter.
+ *
+ * The option will first check for the per site name and then the per Network name.
+ *
+ * @since 2.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $option User option name.
+ * @param int $user Optional. User ID.
+ * @param string $deprecated Use get_option() to check for an option in the options table.
+ * @return mixed User option value on success, false on failure.
+ */
+function get_user_option( $option, $user = 0, $deprecated = '' ) {
+ global $wpdb;
+
+ if ( !empty( $deprecated ) )
+ _deprecated_argument( __FUNCTION__, '3.0' );
+
+ if ( empty( $user ) )
+ $user = get_current_user_id();
+
+ if ( ! $user = get_userdata( $user ) )
+ return false;
+
+ $prefix = $wpdb->get_blog_prefix();
+ if ( $user->has_prop( $prefix . $option ) ) // Blog specific
+ $result = $user->get( $prefix . $option );
+ elseif ( $user->has_prop( $option ) ) // User specific and cross-blog
+ $result = $user->get( $option );
+ else
+ $result = false;
+
+ /**
+ * Filter a specific user option value.
+ *
+ * The dynamic portion of the hook name, `$option`, refers to the user option name.
+ *
+ * @since 2.5.0
+ *
+ * @param mixed $result Value for the user's option.
+ * @param string $option Name of the option being retrieved.
+ * @param WP_User $user WP_User object of the user whose option is being retrieved.
+ */
+ return apply_filters( "get_user_option_{$option}", $result, $option, $user );
+}
+
+/**
+ * Update user option with global blog capability.
+ *
+ * User options are just like user metadata except that they have support for
+ * global blog options. If the 'global' parameter is false, which it is by default
+ * it will prepend the WordPress table prefix to the option name.
+ *
+ * Deletes the user option if $newvalue is empty.
+ *
+ * @since 2.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $user_id User ID.
+ * @param string $option_name User option name.
+ * @param mixed $newvalue User option value.
+ * @param bool $global Optional. Whether option name is global or blog specific.
+ * Default false (blog specific).
+ * @return int|bool User meta ID if the option didn't exist, true on successful update,
+ * false on failure.
+ */
+function update_user_option( $user_id, $option_name, $newvalue, $global = false ) {
+ global $wpdb;
+
+ if ( !$global )
+ $option_name = $wpdb->get_blog_prefix() . $option_name;
+
+ return update_user_meta( $user_id, $option_name, $newvalue );
+}
+
+/**
+ * Delete user option with global blog capability.
+ *
+ * User options are just like user metadata except that they have support for
+ * global blog options. If the 'global' parameter is false, which it is by default
+ * it will prepend the WordPress table prefix to the option name.
+ *
+ * @since 3.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $user_id User ID
+ * @param string $option_name User option name.
+ * @param bool $global Optional. Whether option name is global or blog specific.
+ * Default false (blog specific).
+ * @return bool True on success, false on failure.
+ */
+function delete_user_option( $user_id, $option_name, $global = false ) {
+ global $wpdb;
+
+ if ( !$global )
+ $option_name = $wpdb->get_blog_prefix() . $option_name;
+ return delete_user_meta( $user_id, $option_name );
+}
+
+/**
+ * Retrieve list of users matching criteria.
+ *
+ * @since 3.1.0
+ *
+ * @see WP_User_Query
+ *
+ * @param array $args Optional. Arguments to retrieve users. See {@see WP_User_Query::prepare_query()}
+ * for more information on accepted arguments.
+ * @return array List of users.
+ */
+function get_users( $args = array() ) {
+
+ $args = wp_parse_args( $args );
+ $args['count_total'] = false;
+
+ $user_search = new WP_User_Query($args);
+
+ return (array) $user_search->get_results();
+}
+
+/**
+ * Get the blogs a user belongs to.
+ *
+ * @since 3.0.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int $user_id User ID
+ * @param bool $all Whether to retrieve all blogs, or only blogs that are not
+ * marked as deleted, archived, or spam.
+ * @return array A list of the user's blogs. An empty array if the user doesn't exist
+ * or belongs to no blogs.
+ */
+function get_blogs_of_user( $user_id, $all = false ) {
+ global $wpdb;
+
+ $user_id = (int) $user_id;
+
+ // Logged out users can't have blogs
+ if ( empty( $user_id ) )
+ return array();
+
+ $keys = get_user_meta( $user_id );
+ if ( empty( $keys ) )
+ return array();
+
+ if ( ! is_multisite() ) {
+ $blog_id = get_current_blog_id();
+ $blogs = array( $blog_id => new stdClass );
+ $blogs[ $blog_id ]->userblog_id = $blog_id;
+ $blogs[ $blog_id ]->blogname = get_option('blogname');
+ $blogs[ $blog_id ]->domain = '';
+ $blogs[ $blog_id ]->path = '';
+ $blogs[ $blog_id ]->site_id = 1;
+ $blogs[ $blog_id ]->siteurl = get_option('siteurl');
+ $blogs[ $blog_id ]->archived = 0;
+ $blogs[ $blog_id ]->spam = 0;
+ $blogs[ $blog_id ]->deleted = 0;
+ return $blogs;
+ }
+
+ $blogs = array();
+
+ if ( isset( $keys[ $wpdb->base_prefix . 'capabilities' ] ) && defined( 'MULTISITE' ) ) {
+ $blog = get_blog_details( 1 );
+ if ( $blog && isset( $blog->domain ) && ( $all || ( ! $blog->archived && ! $blog->spam && ! $blog->deleted ) ) ) {
+ $blogs[ 1 ] = (object) array(
+ 'userblog_id' => 1,
+ 'blogname' => $blog->blogname,
+ 'domain' => $blog->domain,
+ 'path' => $blog->path,
+ 'site_id' => $blog->site_id,
+ 'siteurl' => $blog->siteurl,
+ 'archived' => $blog->archived,
+ 'mature' => $blog->mature,
+ 'spam' => $blog->spam,
+ 'deleted' => $blog->deleted,
+ );
+ }
+ unset( $keys[ $wpdb->base_prefix . 'capabilities' ] );
+ }
+
+ $keys = array_keys( $keys );
+
+ foreach ( $keys as $key ) {
+ if ( 'capabilities' !== substr( $key, -12 ) )
+ continue;
+ if ( $wpdb->base_prefix && 0 !== strpos( $key, $wpdb->base_prefix ) )
+ continue;
+ $blog_id = str_replace( array( $wpdb->base_prefix, '_capabilities' ), '', $key );
+ if ( ! is_numeric( $blog_id ) )
+ continue;
+
+ $blog_id = (int) $blog_id;
+ $blog = get_blog_details( $blog_id );
+ if ( $blog && isset( $blog->domain ) && ( $all || ( ! $blog->archived && ! $blog->spam && ! $blog->deleted ) ) ) {
+ $blogs[ $blog_id ] = (object) array(
+ 'userblog_id' => $blog_id,
+ 'blogname' => $blog->blogname,
+ 'domain' => $blog->domain,
+ 'path' => $blog->path,
+ 'site_id' => $blog->site_id,
+ 'siteurl' => $blog->siteurl,
+ 'archived' => $blog->archived,
+ 'mature' => $blog->mature,
+ 'spam' => $blog->spam,
+ 'deleted' => $blog->deleted,
+ );
+ }
+ }
+
+ /**
+ * Filter the list of blogs a user belongs to.
+ *
+ * @since MU
+ *
+ * @param array $blogs An array of blog objects belonging to the user.
+ * @param int $user_id User ID.
+ * @param bool $all Whether the returned blogs array should contain all blogs, including
+ * those marked 'deleted', 'archived', or 'spam'. Default false.
+ */
+ return apply_filters( 'get_blogs_of_user', $blogs, $user_id, $all );
+}
+
+/**
+ * Find out whether a user is a member of a given blog.
+ *
+ * @since MU 1.1
+ *
+ * @param int $user_id Optional. The unique ID of the user. Defaults to the current user.
+ * @param int $blog_id Optional. ID of the blog to check. Defaults to the current site.
+ * @return bool
+ */
+function is_user_member_of_blog( $user_id = 0, $blog_id = 0 ) {
+ global $wpdb;
+
+ $user_id = (int) $user_id;
+ $blog_id = (int) $blog_id;
+
+ if ( empty( $user_id ) ) {
+ $user_id = get_current_user_id();
+ }
+
+ // Technically not needed, but does save calls to get_blog_details and get_user_meta
+ // in the event that the function is called when a user isn't logged in
+ if ( empty( $user_id ) ) {
+ return false;
+ } else {
+ $user = get_userdata( $user_id );
+ if ( ! $user instanceof WP_User ) {
+ return false;
+ }
+ }
+
+ if ( ! is_multisite() ) {
+ return true;
+ }
+
+ if ( empty( $blog_id ) ) {
+ $blog_id = get_current_blog_id();
+ }
+
+ $blog = get_blog_details( $blog_id );
+
+ if ( ! $blog || ! isset( $blog->domain ) || $blog->archived || $blog->spam || $blog->deleted ) {
+ return false;
+ }
+
+ $keys = get_user_meta( $user_id );
+ if ( empty( $keys ) ) {
+ return false;
+ }
+
+ // no underscore before capabilities in $base_capabilities_key
+ $base_capabilities_key = $wpdb->base_prefix . 'capabilities';
+ $site_capabilities_key = $wpdb->base_prefix . $blog_id . '_capabilities';
+
+ if ( isset( $keys[ $base_capabilities_key ] ) && $blog_id == 1 ) {
+ return true;
+ }
+
+ if ( isset( $keys[ $site_capabilities_key ] ) ) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Add meta data field to a user.
+ *
+ * Post meta data is called "Custom Fields" on the Administration Screens.
+ *
+ * @since 3.0.0
+ * @link https://codex.wordpress.org/Function_Reference/add_user_meta
+ *
+ * @param int $user_id User ID.
+ * @param string $meta_key Metadata name.
+ * @param mixed $meta_value Metadata value.
+ * @param bool $unique Optional, default is false. Whether the same key should not be added.
+ * @return int|false Meta ID on success, false on failure.
+ */
+function add_user_meta($user_id, $meta_key, $meta_value, $unique = false) {
+ return add_metadata('user', $user_id, $meta_key, $meta_value, $unique);
+}
+
+/**
+ * Remove metadata matching criteria from a user.
+ *
+ * You can match based on the key, or key and value. Removing based on key and
+ * value, will keep from removing duplicate metadata with the same key. It also
+ * allows removing all metadata matching key, if needed.
+ *
+ * @since 3.0.0
+ * @link https://codex.wordpress.org/Function_Reference/delete_user_meta
+ *
+ * @param int $user_id User ID
+ * @param string $meta_key Metadata name.
+ * @param mixed $meta_value Optional. Metadata value.
+ * @return bool True on success, false on failure.
+ */
+function delete_user_meta($user_id, $meta_key, $meta_value = '') {
+ return delete_metadata('user', $user_id, $meta_key, $meta_value);
+}
+
+/**
+ * Retrieve user meta field for a user.
+ *
+ * @since 3.0.0
+ * @link https://codex.wordpress.org/Function_Reference/get_user_meta
+ *
+ * @param int $user_id User ID.
+ * @param string $key Optional. The meta key to retrieve. By default, returns data for all keys.
+ * @param bool $single Whether to return a single value.
+ * @return mixed Will be an array if $single is false. Will be value of meta data field if $single is true.
+ */
+function get_user_meta($user_id, $key = '', $single = false) {
+ return get_metadata('user', $user_id, $key, $single);
+}
+
+/**
+ * Update user meta field based on user ID.
+ *
+ * Use the $prev_value parameter to differentiate between meta fields with the
+ * same key and user ID.
+ *
+ * If the meta field for the user does not exist, it will be added.
+ *
+ * @since 3.0.0
+ * @link https://codex.wordpress.org/Function_Reference/update_user_meta
+ *
+ * @param int $user_id User ID.
+ * @param string $meta_key Metadata key.
+ * @param mixed $meta_value Metadata value.
+ * @param mixed $prev_value Optional. Previous value to check before removing.
+ * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
+ */
+function update_user_meta($user_id, $meta_key, $meta_value, $prev_value = '') {
+ return update_metadata('user', $user_id, $meta_key, $meta_value, $prev_value);
+}
+
+/**
+ * Count number of users who have each of the user roles.
+ *
+ * Assumes there are neither duplicated nor orphaned capabilities meta_values.
+ * Assumes role names are unique phrases. Same assumption made by WP_User_Query::prepare_query()
+ * Using $strategy = 'time' this is CPU-intensive and should handle around 10^7 users.
+ * Using $strategy = 'memory' this is memory-intensive and should handle around 10^5 users, but see WP Bug #12257.
+ *
+ * @since 3.0.0
+ * @since 4.4.0 The number of users with no role is now included in the `none` element.
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $strategy 'time' or 'memory'
+ * @return array Includes a grand total and an array of counts indexed by role strings.
+ */
+function count_users($strategy = 'time') {
+ global $wpdb;
+
+ // Initialize
+ $id = get_current_blog_id();
+ $blog_prefix = $wpdb->get_blog_prefix($id);
+ $result = array();
+
+ if ( 'time' == $strategy ) {
+ $avail_roles = wp_roles()->get_names();
+
+ // Build a CPU-intensive query that will return concise information.
+ $select_count = array();
+ foreach ( $avail_roles as $this_role => $name ) {
+ $select_count[] = $wpdb->prepare( "COUNT(NULLIF(`meta_value` LIKE %s, false))", '%' . $wpdb->esc_like( '"' . $this_role . '"' ) . '%');
+ }
+ $select_count[] = "COUNT(NULLIF(`meta_value` = 'a:0:{}', false))";
+ $select_count = implode(', ', $select_count);
+
+ // Add the meta_value index to the selection list, then run the query.
+ $row = $wpdb->get_row( "SELECT $select_count, COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'", ARRAY_N );
+
+ // Run the previous loop again to associate results with role names.
+ $col = 0;
+ $role_counts = array();
+ foreach ( $avail_roles as $this_role => $name ) {
+ $count = (int) $row[$col++];
+ if ($count > 0) {
+ $role_counts[$this_role] = $count;
+ }
+ }
+
+ $role_counts['none'] = (int) $row[$col++];
+
+ // Get the meta_value index from the end of the result set.
+ $total_users = (int) $row[$col];
+
+ $result['total_users'] = $total_users;
+ $result['avail_roles'] =& $role_counts;
+ } else {
+ $avail_roles = array(
+ 'none' => 0,
+ );
+
+ $users_of_blog = $wpdb->get_col( "SELECT meta_value FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'" );
+
+ foreach ( $users_of_blog as $caps_meta ) {
+ $b_roles = maybe_unserialize($caps_meta);
+ if ( ! is_array( $b_roles ) )
+ continue;
+ if ( empty( $b_roles ) ) {
+ $avail_roles['none']++;
+ }
+ foreach ( $b_roles as $b_role => $val ) {
+ if ( isset($avail_roles[$b_role]) ) {
+ $avail_roles[$b_role]++;
+ } else {
+ $avail_roles[$b_role] = 1;
+ }
+ }
+ }
+
+ $result['total_users'] = count( $users_of_blog );
+ $result['avail_roles'] =& $avail_roles;
+ }
+
+ if ( is_multisite() ) {
+ $result['avail_roles']['none'] = 0;
+ }
+
+ return $result;
+}
+
+//
+// Private helper functions
+//
+
+/**
+ * Set up global user vars.
+ *
+ * Used by wp_set_current_user() for back compat. Might be deprecated in the future.
+ *
+ * @since 2.0.4
+ *
+ * @global string $user_login The user username for logging in
+ * @global object $userdata User data.
+ * @global int $user_level The level of the user
+ * @global int $user_ID The ID of the user
+ * @global string $user_email The email address of the user
+ * @global string $user_url The url in the user's profile
+ * @global string $user_identity The display name of the user
+ *
+ * @param int $for_user_id Optional. User ID to set up global data.
+ */
+function setup_userdata($for_user_id = '') {
+ global $user_login, $userdata, $user_level, $user_ID, $user_email, $user_url, $user_identity;
+
+ if ( '' == $for_user_id )
+ $for_user_id = get_current_user_id();
+ $user = get_userdata( $for_user_id );
+
+ if ( ! $user ) {
+ $user_ID = 0;
+ $user_level = 0;
+ $userdata = null;
+ $user_login = $user_email = $user_url = $user_identity = '';
+ return;
+ }
+
+ $user_ID = (int) $user->ID;
+ $user_level = (int) $user->user_level;
+ $userdata = $user;
+ $user_login = $user->user_login;
+ $user_email = $user->user_email;
+ $user_url = $user->user_url;
+ $user_identity = $user->display_name;
+}
+
+/**
+ * Create dropdown HTML content of users.
+ *
+ * The content can either be displayed, which it is by default or retrieved by
+ * setting the 'echo' argument. The 'include' and 'exclude' arguments do not
+ * need to be used; all users will be displayed in that case. Only one can be
+ * used, either 'include' or 'exclude', but not both.
+ *
+ * The available arguments are as follows:
+ *
+ * @since 2.3.0
+ *
+ * @global int $blog_id
+ *
+ * @param array|string $args {
+ * Optional. Array or string of arguments to generate a drop-down of users.
+ * {@see WP_User_Query::prepare_query() for additional available arguments.
+ *
+ * @type string $show_option_all Text to show as the drop-down default (all).
+ * Default empty.
+ * @type string $show_option_none Text to show as the drop-down default when no
+ * users were found. Default empty.
+ * @type int|string $option_none_value Value to use for $show_option_non when no users
+ * were found. Default -1.
+ * @type string $hide_if_only_one_author Whether to skip generating the drop-down
+ * if only one user was found. Default empty.
+ * @type string $orderby Field to order found users by. Accepts user fields.
+ * Default 'display_name'.
+ * @type string $order Whether to order users in ascending or descending
+ * order. Accepts 'ASC' (ascending) or 'DESC' (descending).
+ * Default 'ASC'.
+ * @type array|string $include Array or comma-separated list of user IDs to include.
+ * Default empty.
+ * @type array|string $exclude Array or comma-separated list of user IDs to exclude.
+ * Default empty.
+ * @type bool|int $multi Whether to skip the ID attribute on the 'select' element.
+ * Accepts 1|true or 0|false. Default 0|false.
+ * @type string $show User table column to display. If the selected item is empty
+ * then the 'user_login' will be displayed in parentheses.
+ * Accepts user fields. Default 'display_name'.
+ * @type int|bool $echo Whether to echo or return the drop-down. Accepts 1|true (echo)
+ * or 0|false (return). Default 1|true.
+ * @type int $selected Which user ID should be selected. Default 0.
+ * @type bool $include_selected Whether to always include the selected user ID in the drop-
+ * down. Default false.
+ * @type string $name Name attribute of select element. Default 'user'.
+ * @type string $id ID attribute of the select element. Default is the value of $name.
+ * @type string $class Class attribute of the select element. Default empty.
+ * @type int $blog_id ID of blog (Multisite only). Default is ID of the current blog.
+ * @type string $who Which type of users to query. Accepts only an empty string or
+ * 'authors'. Default empty.
+ * }
+ * @return string String of HTML content.
+ */
+function wp_dropdown_users( $args = '' ) {
+ $defaults = array(
+ 'show_option_all' => '', 'show_option_none' => '', 'hide_if_only_one_author' => '',
+ 'orderby' => 'display_name', 'order' => 'ASC',
+ 'include' => '', 'exclude' => '', 'multi' => 0,
+ 'show' => 'display_name', 'echo' => 1,
+ 'selected' => 0, 'name' => 'user', 'class' => '', 'id' => '',
+ 'blog_id' => $GLOBALS['blog_id'], 'who' => '', 'include_selected' => false,
+ 'option_none_value' => -1
+ );
+
+ $defaults['selected'] = is_author() ? get_query_var( 'author' ) : 0;
+
+ $r = wp_parse_args( $args, $defaults );
+ $show = $r['show'];
+ $show_option_all = $r['show_option_all'];
+ $show_option_none = $r['show_option_none'];
+ $option_none_value = $r['option_none_value'];
+
+ $query_args = wp_array_slice_assoc( $r, array( 'blog_id', 'include', 'exclude', 'orderby', 'order', 'who' ) );
+ $query_args['fields'] = array( 'ID', 'user_login', $show );
+
+ /**
+ * Filter the query arguments for the user drop-down.
+ *
+ * @since 4.4.0
+ *
+ * @param array $query_args The query arguments for wp_dropdown_users().
+ * @param array $r The default arguments for wp_dropdown_users().
+ */
+ $query_args = apply_filters( 'wp_dropdown_users_args', $query_args, $r );
+
+ $users = get_users( $query_args );
+
+ $output = '';
+ if ( ! empty( $users ) && ( empty( $r['hide_if_only_one_author'] ) || count( $users ) > 1 ) ) {
+ $name = esc_attr( $r['name'] );
+ if ( $r['multi'] && ! $r['id'] ) {
+ $id = '';
+ } else {
+ $id = $r['id'] ? " id='" . esc_attr( $r['id'] ) . "'" : " id='$name'";
+ }
+ $output = "<select name='{$name}'{$id} class='" . $r['class'] . "'>\n";
+
+ if ( $show_option_all ) {
+ $output .= "\t<option value='0'>$show_option_all</option>\n";
+ }
+
+ if ( $show_option_none ) {
+ $_selected = selected( $option_none_value, $r['selected'], false );
+ $output .= "\t<option value='" . esc_attr( $option_none_value ) . "'$_selected>$show_option_none</option>\n";
+ }
+
+ $found_selected = false;
+ foreach ( (array) $users as $user ) {
+ $user->ID = (int) $user->ID;
+ $_selected = selected( $user->ID, $r['selected'], false );
+ if ( $_selected ) {
+ $found_selected = true;
+ }
+ $display = ! empty( $user->$show ) ? $user->$show : '('. $user->user_login . ')';
+ $output .= "\t<option value='$user->ID'$_selected>" . esc_html( $display ) . "</option>\n";
+ }
+
+ if ( $r['include_selected'] && ! $found_selected && ( $r['selected'] > 0 ) ) {
+ $user = get_userdata( $r['selected'] );
+ $_selected = selected( $user->ID, $r['selected'], false );
+ $display = ! empty( $user->$show ) ? $user->$show : '('. $user->user_login . ')';
+ $output .= "\t<option value='$user->ID'$_selected>" . esc_html( $display ) . "</option>\n";
+ }
+
+ $output .= "</select>";
+ }
+
+ /**
+ * Filter the wp_dropdown_users() HTML output.
+ *
+ * @since 2.3.0
+ *
+ * @param string $output HTML output generated by wp_dropdown_users().
+ */
+ $html = apply_filters( 'wp_dropdown_users', $output );
+
+ if ( $r['echo'] ) {
+ echo $html;
+ }
+ return $html;
+}
+
+/**
+ * Sanitize user field based on context.
+ *
+ * Possible context values are: 'raw', 'edit', 'db', 'display', 'attribute' and 'js'. The
+ * 'display' context is used by default. 'attribute' and 'js' contexts are treated like 'display'
+ * when calling filters.
+ *
+ * @since 2.3.0
+ *
+ * @param string $field The user Object field name.
+ * @param mixed $value The user Object value.
+ * @param int $user_id User ID.
+ * @param string $context How to sanitize user fields. Looks for 'raw', 'edit', 'db', 'display',
+ * 'attribute' and 'js'.
+ * @return mixed Sanitized value.
+ */
+function sanitize_user_field($field, $value, $user_id, $context) {
+ $int_fields = array('ID');
+ if ( in_array($field, $int_fields) )
+ $value = (int) $value;
+
+ if ( 'raw' == $context )
+ return $value;
+
+ if ( !is_string($value) && !is_numeric($value) )
+ return $value;
+
+ $prefixed = false !== strpos( $field, 'user_' );
+
+ if ( 'edit' == $context ) {
+ if ( $prefixed ) {
+
+ /** This filter is documented in wp-includes/post-functions.php */
+ $value = apply_filters( "edit_{$field}", $value, $user_id );
+ } else {
+
+ /**
+ * Filter a user field value in the 'edit' context.
+ *
+ * The dynamic portion of the hook name, `$field`, refers to the prefixed user
+ * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
+ *
+ * @since 2.9.0
+ *
+ * @param mixed $value Value of the prefixed user field.
+ * @param int $user_id User ID.
+ */
+ $value = apply_filters( "edit_user_{$field}", $value, $user_id );
+ }
+
+ if ( 'description' == $field )
+ $value = esc_html( $value ); // textarea_escaped?
+ else
+ $value = esc_attr($value);
+ } elseif ( 'db' == $context ) {
+ if ( $prefixed ) {
+ /** This filter is documented in wp-includes/post-functions.php */
+ $value = apply_filters( "pre_{$field}", $value );
+ } else {
+
+ /**
+ * Filter the value of a user field in the 'db' context.
+ *
+ * The dynamic portion of the hook name, `$field`, refers to the prefixed user
+ * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
+ *
+ * @since 2.9.0
+ *
+ * @param mixed $value Value of the prefixed user field.
+ */
+ $value = apply_filters( "pre_user_{$field}", $value );
+ }
+ } else {
+ // Use display filters by default.
+ if ( $prefixed ) {
+
+ /** This filter is documented in wp-includes/post-functions.php */
+ $value = apply_filters( $field, $value, $user_id, $context );
+ } else {
+
+ /**
+ * Filter the value of a user field in a standard context.
+ *
+ * The dynamic portion of the hook name, `$field`, refers to the prefixed user
+ * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
+ *
+ * @since 2.9.0
+ *
+ * @param mixed $value The user object value to sanitize.
+ * @param int $user_id User ID.
+ * @param string $context The context to filter within.
+ */
+ $value = apply_filters( "user_{$field}", $value, $user_id, $context );
+ }
+ }
+
+ if ( 'user_url' == $field )
+ $value = esc_url($value);
+
+ if ( 'attribute' == $context ) {
+ $value = esc_attr( $value );
+ } elseif ( 'js' == $context ) {
+ $value = esc_js( $value );
+ }
+ return $value;
+}
+
+/**
+ * Update all user caches
+ *
+ * @since 3.0.0
+ *
+ * @param object|WP_User $user User object to be cached
+ * @return bool|null Returns false on failure.
+ */
+function update_user_caches( $user ) {
+ if ( $user instanceof WP_User ) {
+ if ( ! $user->exists() ) {
+ return false;
+ }
+
+ $user = $user->data;
+ }
+
+ wp_cache_add($user->ID, $user, 'users');
+ wp_cache_add($user->user_login, $user->ID, 'userlogins');
+ wp_cache_add($user->user_email, $user->ID, 'useremail');
+ wp_cache_add($user->user_nicename, $user->ID, 'userslugs');
+}
+
+/**
+ * Clean all user caches
+ *
+ * @since 3.0.0
+ * @since 4.4.0 'clean_user_cache' action was added.
+ *
+ * @param WP_User|int $user User object or ID to be cleaned from the cache
+ */
+function clean_user_cache( $user ) {
+ if ( is_numeric( $user ) )
+ $user = new WP_User( $user );
+
+ if ( ! $user->exists() )
+ return;
+
+ wp_cache_delete( $user->ID, 'users' );
+ wp_cache_delete( $user->user_login, 'userlogins' );
+ wp_cache_delete( $user->user_email, 'useremail' );
+ wp_cache_delete( $user->user_nicename, 'userslugs' );
+
+ /**
+ * Fires immediately after the given user's cache is cleaned.
+ *
+ * @since 4.4.0
+ *
+ * @param int $user_id User ID.
+ * @param WP_User $user User object.
+ */
+ do_action( 'clean_user_cache', $user->ID, $user );
+}
+
+/**
+ * Checks whether the given username exists.
+ *
+ * @since 2.0.0
+ *
+ * @param string $username Username.
+ * @return int|false The user's ID on success, and false on failure.
+ */
+function username_exists( $username ) {
+ if ( $user = get_user_by( 'login', $username ) ) {
+ return $user->ID;
+ }
+ return false;
+}
+
+/**
+ * Checks whether the given email exists.
+ *
+ * @since 2.1.0
+ *
+ * @param string $email Email.
+ * @return int|false The user's ID on success, and false on failure.
+ */
+function email_exists( $email ) {
+ if ( $user = get_user_by( 'email', $email) ) {
+ return $user->ID;
+ }
+ return false;
+}
+
+/**
+ * Checks whether a username is valid.
+ *
+ * @since 2.0.1
+ * @since 4.4.0 Empty sanitized usernames are now considered invalid
+ *
+ * @param string $username Username.
+ * @return bool Whether username given is valid
+ */
+function validate_username( $username ) {
+ $sanitized = sanitize_user( $username, true );
+ $valid = ( $sanitized == $username && ! empty( $sanitized ) );
+
+ /**
+ * Filter whether the provided username is valid or not.
+ *
+ * @since 2.0.1
+ *
+ * @param bool $valid Whether given username is valid.
+ * @param string $username Username to check.
+ */
+ return apply_filters( 'validate_username', $valid, $username );
+}
+
+/**
+ * Insert a user into the database.
+ *
+ * Most of the `$userdata` array fields have filters associated with the values. Exceptions are
+ * 'ID', 'rich_editing', 'comment_shortcuts', 'admin_color', 'use_ssl',
+ * 'user_registered', and 'role'. The filters have the prefix 'pre_user_' followed by the field
+ * name. An example using 'description' would have the filter called, 'pre_user_description' that
+ * can be hooked into.
+ *
+ * @since 2.0.0
+ * @since 3.6.0 The `aim`, `jabber`, and `yim` fields were removed as default user contact
+ * methods for new installs. See wp_get_user_contact_methods().
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param array|object|WP_User $userdata {
+ * An array, object, or WP_User object of user data arguments.
+ *
+ * @type int $ID User ID. If supplied, the user will be updated.
+ * @type string $user_pass The plain-text user password.
+ * @type string $user_login The user's login username.
+ * @type string $user_nicename The URL-friendly user name.
+ * @type string $user_url The user URL.
+ * @type string $user_email The user email address.
+ * @type string $display_name The user's display name.
+ * Default is the the user's username.
+ * @type string $nickname The user's nickname.
+ * Default is the the user's username.
+ * @type string $first_name The user's first name. For new users, will be used
+ * to build the first part of the user's display name
+ * if `$display_name` is not specified.
+ * @type string $last_name The user's last name. For new users, will be used
+ * to build the second part of the user's display name
+ * if `$display_name` is not specified.
+ * @type string $description The user's biographical description.
+ * @type string|bool $rich_editing Whether to enable the rich-editor for the user.
+ * False if not empty.
+ * @type string|bool $comment_shortcuts Whether to enable comment moderation keyboard
+ * shortcuts for the user. Default false.
+ * @type string $admin_color Admin color scheme for the user. Default 'fresh'.
+ * @type bool $use_ssl Whether the user should always access the admin over
+ * https. Default false.
+ * @type string $user_registered Date the user registered. Format is 'Y-m-d H:i:s'.
+ * @type string|bool $show_admin_bar_front Whether to display the Admin Bar for the user on the
+ * site's frontend. Default true.
+ * @type string $role User's role.
+ * }
+ * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not
+ * be created.
+ */
+function wp_insert_user( $userdata ) {
+ global $wpdb;
+
+ if ( $userdata instanceof stdClass ) {
+ $userdata = get_object_vars( $userdata );
+ } elseif ( $userdata instanceof WP_User ) {
+ $userdata = $userdata->to_array();
+ }
+
+ // Are we updating or creating?
+ if ( ! empty( $userdata['ID'] ) ) {
+ $ID = (int) $userdata['ID'];
+ $update = true;
+ $old_user_data = get_userdata( $ID );
+
+ if ( ! $old_user_data ) {
+ return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
+ }
+
+ // hashed in wp_update_user(), plaintext if called directly
+ $user_pass = ! empty( $userdata['user_pass'] ) ? $userdata['user_pass'] : $old_user_data->user_pass;
+ } else {
+ $update = false;
+ // Hash the password
+ $user_pass = wp_hash_password( $userdata['user_pass'] );
+ }
+
+ $sanitized_user_login = sanitize_user( $userdata['user_login'], true );
+
+ /**
+ * Filter a username after it has been sanitized.
+ *
+ * This filter is called before the user is created or updated.
+ *
+ * @since 2.0.3
+ *
+ * @param string $sanitized_user_login Username after it has been sanitized.
+ */
+ $pre_user_login = apply_filters( 'pre_user_login', $sanitized_user_login );
+
+ //Remove any non-printable chars from the login string to see if we have ended up with an empty username
+ $user_login = trim( $pre_user_login );
+
+ // user_login must be between 0 and 60 characters.
+ if ( empty( $user_login ) ) {
+ return new WP_Error('empty_user_login', __('Cannot create a user with an empty login name.') );
+ } elseif ( mb_strlen( $user_login ) > 60 ) {
+ return new WP_Error( 'user_login_too_long', __( 'Username may not be longer than 60 characters.' ) );
+ }
+
+ if ( ! $update && username_exists( $user_login ) ) {
+ return new WP_Error( 'existing_user_login', __( 'Sorry, that username already exists!' ) );
+ }
+
+ /**
+ * Filter the list of blacklisted usernames.
+ *
+ * @since 4.4.0
+ *
+ * @param array $usernames Array of blacklisted usernames.
+ */
+ $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
+
+ if ( in_array( strtolower( $user_login ), array_map( 'strtolower', $illegal_logins ) ) ) {
+ return new WP_Error( 'illegal_user_login', __( 'Sorry, that username is not allowed.' ) );
+ }
+
+ /*
+ * If a nicename is provided, remove unsafe user characters before using it.
+ * Otherwise build a nicename from the user_login.
+ */
+ if ( ! empty( $userdata['user_nicename'] ) ) {
+ $user_nicename = sanitize_user( $userdata['user_nicename'], true );
+ if ( mb_strlen( $user_nicename ) > 50 ) {
+ return new WP_Error( 'user_nicename_too_long', __( 'Nicename may not be longer than 50 characters.' ) );
+ }
+ } else {
+ $user_nicename = mb_substr( $user_login, 0, 50 );
+ }
+
+ $user_nicename = sanitize_title( $user_nicename );
+
+ // Store values to save in user meta.
+ $meta = array();
+
+ /**
+ * Filter a user's nicename before the user is created or updated.
+ *
+ * @since 2.0.3
+ *
+ * @param string $user_nicename The user's nicename.
+ */
+ $user_nicename = apply_filters( 'pre_user_nicename', $user_nicename );
+
+ $raw_user_url = empty( $userdata['user_url'] ) ? '' : $userdata['user_url'];
+
+ /**
+ * Filter a user's URL before the user is created or updated.
+ *
+ * @since 2.0.3
+ *
+ * @param string $raw_user_url The user's URL.
+ */
+ $user_url = apply_filters( 'pre_user_url', $raw_user_url );
+
+ $raw_user_email = empty( $userdata['user_email'] ) ? '' : $userdata['user_email'];
+
+ /**
+ * Filter a user's email before the user is created or updated.
+ *
+ * @since 2.0.3
+ *
+ * @param string $raw_user_email The user's email.
+ */
+ $user_email = apply_filters( 'pre_user_email', $raw_user_email );
+
+ /*
+ * If there is no update, just check for `email_exists`. If there is an update,
+ * check if current email and new email are the same, or not, and check `email_exists`
+ * accordingly.
+ */
+ if ( ( ! $update || ( ! empty( $old_user_data ) && 0 !== strcasecmp( $user_email, $old_user_data->user_email ) ) )
+ && ! defined( 'WP_IMPORTING' )
+ && email_exists( $user_email )
+ ) {
+ return new WP_Error( 'existing_user_email', __( 'Sorry, that email address is already used!' ) );
+ }
+ $nickname = empty( $userdata['nickname'] ) ? $user_login : $userdata['nickname'];
+
+ /**
+ * Filter a user's nickname before the user is created or updated.
+ *
+ * @since 2.0.3
+ *
+ * @param string $nickname The user's nickname.
+ */
+ $meta['nickname'] = apply_filters( 'pre_user_nickname', $nickname );
+
+ $first_name = empty( $userdata['first_name'] ) ? '' : $userdata['first_name'];
+
+ /**
+ * Filter a user's first name before the user is created or updated.
+ *
+ * @since 2.0.3
+ *
+ * @param string $first_name The user's first name.
+ */
+ $meta['first_name'] = apply_filters( 'pre_user_first_name', $first_name );
+
+ $last_name = empty( $userdata['last_name'] ) ? '' : $userdata['last_name'];
+
+ /**
+ * Filter a user's last name before the user is created or updated.
+ *
+ * @since 2.0.3
+ *
+ * @param string $last_name The user's last name.
+ */
+ $meta['last_name'] = apply_filters( 'pre_user_last_name', $last_name );
+
+ if ( empty( $userdata['display_name'] ) ) {
+ if ( $update ) {
+ $display_name = $user_login;
+ } elseif ( $meta['first_name'] && $meta['last_name'] ) {
+ /* translators: 1: first name, 2: last name */
+ $display_name = sprintf( _x( '%1$s %2$s', 'Display name based on first name and last name' ), $meta['first_name'], $meta['last_name'] );
+ } elseif ( $meta['first_name'] ) {
+ $display_name = $meta['first_name'];
+ } elseif ( $meta['last_name'] ) {
+ $display_name = $meta['last_name'];
+ } else {
+ $display_name = $user_login;
+ }
+ } else {
+ $display_name = $userdata['display_name'];
+ }
+
+ /**
+ * Filter a user's display name before the user is created or updated.
+ *
+ * @since 2.0.3
+ *
+ * @param string $display_name The user's display name.
+ */
+ $display_name = apply_filters( 'pre_user_display_name', $display_name );
+
+ $description = empty( $userdata['description'] ) ? '' : $userdata['description'];
+
+ /**
+ * Filter a user's description before the user is created or updated.
+ *
+ * @since 2.0.3
+ *
+ * @param string $description The user's description.
+ */
+ $meta['description'] = apply_filters( 'pre_user_description', $description );
+
+ $meta['rich_editing'] = empty( $userdata['rich_editing'] ) ? 'true' : $userdata['rich_editing'];
+
+ $meta['comment_shortcuts'] = empty( $userdata['comment_shortcuts'] ) || 'false' === $userdata['comment_shortcuts'] ? 'false' : 'true';
+
+ $admin_color = empty( $userdata['admin_color'] ) ? 'fresh' : $userdata['admin_color'];
+ $meta['admin_color'] = preg_replace( '|[^a-z0-9 _.\-@]|i', '', $admin_color );
+
+ $meta['use_ssl'] = empty( $userdata['use_ssl'] ) ? 0 : $userdata['use_ssl'];
+
+ $user_registered = empty( $userdata['user_registered'] ) ? gmdate( 'Y-m-d H:i:s' ) : $userdata['user_registered'];
+
+ $meta['show_admin_bar_front'] = empty( $userdata['show_admin_bar_front'] ) ? 'true' : $userdata['show_admin_bar_front'];
+
+ $user_nicename_check = $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1" , $user_nicename, $user_login));
+
+ if ( $user_nicename_check ) {
+ $suffix = 2;
+ while ($user_nicename_check) {
+ // user_nicename allows 50 chars. Subtract one for a hyphen, plus the length of the suffix.
+ $base_length = 49 - mb_strlen( $suffix );
+ $alt_user_nicename = mb_substr( $user_nicename, 0, $base_length ) . "-$suffix";
+ $user_nicename_check = $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1" , $alt_user_nicename, $user_login));
+ $suffix++;
+ }
+ $user_nicename = $alt_user_nicename;
+ }
+
+ $compacted = compact( 'user_pass', 'user_email', 'user_url', 'user_nicename', 'display_name', 'user_registered' );
+ $data = wp_unslash( $compacted );
+
+ if ( $update ) {
+ if ( $user_email !== $old_user_data->user_email ) {
+ $data['user_activation_key'] = '';
+ }
+ $wpdb->update( $wpdb->users, $data, compact( 'ID' ) );
+ $user_id = (int) $ID;
+ } else {
+ $wpdb->insert( $wpdb->users, $data + compact( 'user_login' ) );
+ $user_id = (int) $wpdb->insert_id;
+ }
+
+ $user = new WP_User( $user_id );
+
+ /**
+ * Filter a user's meta values and keys before the user is created or updated.
+ *
+ * Does not include contact methods. These are added using `wp_get_user_contact_methods( $user )`.
+ *
+ * @since 4.4.0
+ *
+ * @param array $meta {
+ * Default meta values and keys for the user.
+ *
+ * @type string $nickname The user's nickname. Default is the the user's username.
+ * @type string $first_name The user's first name.
+ * @type string $last_name The user's last name.
+ * @type string $description The user's description.
+ * @type bool $rich_editing Whether to enable the rich-editor for the user. False if not empty.
+ * @type bool $comment_shortcuts Whether to enable keyboard shortcuts for the user. Default false.
+ * @type string $admin_color The color scheme for a user's admin screen. Default 'fresh'.
+ * @type int|bool $use_ssl Whether to force SSL on the user's admin area. 0|false if SSL is
+ * not forced.
+ * @type bool $show_admin_bar_front Whether to show the admin bar on the front end for the user.
+ * Default true.
+ * }
+ * @param WP_User $user User object.
+ * @param bool $update Whether the user is being updated rather than created.
+ */
+ $meta = apply_filters( 'insert_user_meta', $meta, $user, $update );
+
+ // Update user meta.
+ foreach ( $meta as $key => $value ) {
+ update_user_meta( $user_id, $key, $value );
+ }
+
+ foreach ( wp_get_user_contact_methods( $user ) as $key => $value ) {
+ if ( isset( $userdata[ $key ] ) ) {
+ update_user_meta( $user_id, $key, $userdata[ $key ] );
+ }
+ }
+
+ if ( isset( $userdata['role'] ) ) {
+ $user->set_role( $userdata['role'] );
+ } elseif ( ! $update ) {
+ $user->set_role(get_option('default_role'));
+ }
+ wp_cache_delete( $user_id, 'users' );
+ wp_cache_delete( $user_login, 'userlogins' );
+
+ if ( $update ) {
+ /**
+ * Fires immediately after an existing user is updated.
+ *
+ * @since 2.0.0
+ *
+ * @param int $user_id User ID.
+ * @param object $old_user_data Object containing user's data prior to update.
+ */
+ do_action( 'profile_update', $user_id, $old_user_data );
+ } else {
+ /**
+ * Fires immediately after a new user is registered.
+ *
+ * @since 1.5.0
+ *
+ * @param int $user_id User ID.
+ */
+ do_action( 'user_register', $user_id );
+ }
+
+ return $user_id;
+}
+
+/**
+ * Update a user in the database.
+ *
+ * It is possible to update a user's password by specifying the 'user_pass'
+ * value in the $userdata parameter array.
+ *
+ * If current user's password is being updated, then the cookies will be
+ * cleared.
+ *
+ * @since 2.0.0
+ *
+ * @see wp_insert_user() For what fields can be set in $userdata.
+ *
+ * @param mixed $userdata An array of user data or a user object of type stdClass or WP_User.
+ * @return int|WP_Error The updated user's ID or a WP_Error object if the user could not be updated.
+ */
+function wp_update_user($userdata) {
+ if ( $userdata instanceof stdClass ) {
+ $userdata = get_object_vars( $userdata );
+ } elseif ( $userdata instanceof WP_User ) {
+ $userdata = $userdata->to_array();
+ }
+
+ $ID = isset( $userdata['ID'] ) ? (int) $userdata['ID'] : 0;
+ if ( ! $ID ) {
+ return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
+ }
+
+ // First, get all of the original fields
+ $user_obj = get_userdata( $ID );
+ if ( ! $user_obj ) {
+ return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
+ }
+
+ $user = $user_obj->to_array();
+
+ // Add additional custom fields
+ foreach ( _get_additional_user_keys( $user_obj ) as $key ) {
+ $user[ $key ] = get_user_meta( $ID, $key, true );
+ }
+
+ // Escape data pulled from DB.
+ $user = add_magic_quotes( $user );
+
+ if ( ! empty( $userdata['user_pass'] ) && $userdata['user_pass'] !== $user_obj->user_pass ) {
+ // If password is changing, hash it now
+ $plaintext_pass = $userdata['user_pass'];
+ $userdata['user_pass'] = wp_hash_password( $userdata['user_pass'] );
+
+ /**
+ * Filter whether to send the password change email.
+ *
+ * @since 4.3.0
+ *
+ * @see wp_insert_user() For `$user` and `$userdata` fields.
+ *
+ * @param bool $send Whether to send the email.
+ * @param array $user The original user array.
+ * @param array $userdata The updated user array.
+ *
+ */
+ $send_password_change_email = apply_filters( 'send_password_change_email', true, $user, $userdata );
+ }
+
+ if ( isset( $userdata['user_email'] ) && $user['user_email'] !== $userdata['user_email'] ) {
+ /**
+ * Filter whether to send the email change email.
+ *
+ * @since 4.3.0
+ *
+ * @see wp_insert_user() For `$user` and `$userdata` fields.
+ *
+ * @param bool $send Whether to send the email.
+ * @param array $user The original user array.
+ * @param array $userdata The updated user array.
+ *
+ */
+ $send_email_change_email = apply_filters( 'send_email_change_email', true, $user, $userdata );
+ }
+
+ wp_cache_delete( $user['user_email'], 'useremail' );
+
+ // Merge old and new fields with new fields overwriting old ones.
+ $userdata = array_merge( $user, $userdata );
+ $user_id = wp_insert_user( $userdata );
+
+ if ( ! is_wp_error( $user_id ) ) {
+
+ $blog_name = wp_specialchars_decode( get_option( 'blogname' ) );
+
+ if ( ! empty( $send_password_change_email ) ) {
+
+ /* translators: Do not translate USERNAME, ADMIN_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */
+ $pass_change_text = __( 'Hi ###USERNAME###,
+
+This notice confirms that your password was changed on ###SITENAME###.
+
+If you did not change your password, please contact the Site Administrator at
+###ADMIN_EMAIL###
+
+This email has been sent to ###EMAIL###
+
+Regards,
+All at ###SITENAME###
+###SITEURL###' );
+
+ $pass_change_email = array(
+ 'to' => $user['user_email'],
+ 'subject' => __( '[%s] Notice of Password Change' ),
+ 'message' => $pass_change_text,
+ 'headers' => '',
+ );
+
+ /**
+ * Filter the contents of the email sent when the user's password is changed.
+ *
+ * @since 4.3.0
+ *
+ * @param array $pass_change_email {
+ * Used to build wp_mail().
+ * @type string $to The intended recipients. Add emails in a comma separated string.
+ * @type string $subject The subject of the email.
+ * @type string $message The content of the email.
+ * The following strings have a special meaning and will get replaced dynamically:
+ * - ###USERNAME### The current user's username.
+ * - ###ADMIN_EMAIL### The admin email in case this was unexpected.
+ * - ###EMAIL### The old email.
+ * - ###SITENAME### The name of the site.
+ * - ###SITEURL### The URL to the site.
+ * @type string $headers Headers. Add headers in a newline (\r\n) separated string.
+ * }
+ * @param array $user The original user array.
+ * @param array $userdata The updated user array.
+ *
+ */
+ $pass_change_email = apply_filters( 'password_change_email', $pass_change_email, $user, $userdata );
+
+ $pass_change_email['message'] = str_replace( '###USERNAME###', $user['user_login'], $pass_change_email['message'] );
+ $pass_change_email['message'] = str_replace( '###ADMIN_EMAIL###', get_option( 'admin_email' ), $pass_change_email['message'] );
+ $pass_change_email['message'] = str_replace( '###EMAIL###', $user['user_email'], $pass_change_email['message'] );
+ $pass_change_email['message'] = str_replace( '###SITENAME###', get_option( 'blogname' ), $pass_change_email['message'] );
+ $pass_change_email['message'] = str_replace( '###SITEURL###', home_url(), $pass_change_email['message'] );
+
+ wp_mail( $pass_change_email['to'], sprintf( $pass_change_email['subject'], $blog_name ), $pass_change_email['message'], $pass_change_email['headers'] );
+ }
+
+ if ( ! empty( $send_email_change_email ) ) {
+ /* translators: Do not translate USERNAME, ADMIN_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */
+ $email_change_text = __( 'Hi ###USERNAME###,
+
+This notice confirms that your email was changed on ###SITENAME###.
+
+If you did not change your email, please contact the Site Administrator at
+###ADMIN_EMAIL###
+
+This email has been sent to ###EMAIL###
+
+Regards,
+All at ###SITENAME###
+###SITEURL###' );
+
+ $email_change_email = array(
+ 'to' => $user['user_email'],
+ 'subject' => __( '[%s] Notice of Email Change' ),
+ 'message' => $email_change_text,
+ 'headers' => '',
+ );
+
+ /**
+ * Filter the contents of the email sent when the user's email is changed.
+ *
+ * @since 4.3.0
+ *
+ * @param array $email_change_email {
+ * Used to build wp_mail().
+ * @type string $to The intended recipients.
+ * @type string $subject The subject of the email.
+ * @type string $message The content of the email.
+ * The following strings have a special meaning and will get replaced dynamically:
+ * - ###USERNAME### The current user's username.
+ * - ###ADMIN_EMAIL### The admin email in case this was unexpected.
+ * - ###EMAIL### The old email.
+ * - ###SITENAME### The name of the site.
+ * - ###SITEURL### The URL to the site.
+ * @type string $headers Headers.
+ * }
+ * @param array $user The original user array.
+ * @param array $userdata The updated user array.
+ */
+ $email_change_email = apply_filters( 'email_change_email', $email_change_email, $user, $userdata );
+
+ $email_change_email['message'] = str_replace( '###USERNAME###', $user['user_login'], $email_change_email['message'] );
+ $email_change_email['message'] = str_replace( '###ADMIN_EMAIL###', get_option( 'admin_email' ), $email_change_email['message'] );
+ $email_change_email['message'] = str_replace( '###EMAIL###', $user['user_email'], $email_change_email['message'] );
+ $email_change_email['message'] = str_replace( '###SITENAME###', get_option( 'blogname' ), $email_change_email['message'] );
+ $email_change_email['message'] = str_replace( '###SITEURL###', home_url(), $email_change_email['message'] );
+
+ wp_mail( $email_change_email['to'], sprintf( $email_change_email['subject'], $blog_name ), $email_change_email['message'], $email_change_email['headers'] );
+ }
+ }
+
+ // Update the cookies if the password changed.
+ $current_user = wp_get_current_user();
+ if ( $current_user->ID == $ID ) {
+ if ( isset($plaintext_pass) ) {
+ wp_clear_auth_cookie();
+
+ // Here we calculate the expiration length of the current auth cookie and compare it to the default expiration.
+ // If it's greater than this, then we know the user checked 'Remember Me' when they logged in.
+ $logged_in_cookie = wp_parse_auth_cookie( '', 'logged_in' );
+ /** This filter is documented in wp-includes/pluggable.php */
+ $default_cookie_life = apply_filters( 'auth_cookie_expiration', ( 2 * DAY_IN_SECONDS ), $ID, false );
+ $remember = ( ( $logged_in_cookie['expiration'] - time() ) > $default_cookie_life );
+
+ wp_set_auth_cookie( $ID, $remember );
+ }
+ }
+
+ return $user_id;
+}
+
+/**
+ * A simpler way of inserting a user into the database.
+ *
+ * Creates a new user with just the username, password, and email. For more
+ * complex user creation use {@see wp_insert_user()} to specify more information.
+ *
+ * @since 2.0.0
+ * @see wp_insert_user() More complete way to create a new user
+ *
+ * @param string $username The user's username.
+ * @param string $password The user's password.
+ * @param string $email Optional. The user's email. Default empty.
+ * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not
+ * be created.
+ */
+function wp_create_user($username, $password, $email = '') {
+ $user_login = wp_slash( $username );
+ $user_email = wp_slash( $email );
+ $user_pass = $password;
+
+ $userdata = compact('user_login', 'user_email', 'user_pass');
+ return wp_insert_user($userdata);
+}
+
+/**
+ * Returns a list of meta keys to be (maybe) populated in wp_update_user().
+ *
+ * The list of keys returned via this function are dependent on the presence
+ * of those keys in the user meta data to be set.
+ *
+ * @since 3.3.0
+ * @access private
+ *
+ * @param WP_User $user WP_User instance.
+ * @return array List of user keys to be populated in wp_update_user().
+ */
+function _get_additional_user_keys( $user ) {
+ $keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front' );
+ return array_merge( $keys, array_keys( wp_get_user_contact_methods( $user ) ) );
+}
+
+/**
+ * Set up the user contact methods.
+ *
+ * Default contact methods were removed in 3.6. A filter dictates contact methods.
+ *
+ * @since 3.7.0
+ *
+ * @param WP_User $user Optional. WP_User object.
+ * @return array Array of contact methods and their labels.
+ */
+function wp_get_user_contact_methods( $user = null ) {
+ $methods = array();
+ if ( get_site_option( 'initial_db_version' ) < 23588 ) {
+ $methods = array(
+ 'aim' => __( 'AIM' ),
+ 'yim' => __( 'Yahoo IM' ),
+ 'jabber' => __( 'Jabber / Google Talk' )
+ );
+ }
+
+ /**
+ * Filter the user contact methods.
+ *
+ * @since 2.9.0
+ *
+ * @param array $methods Array of contact methods and their labels.
+ * @param WP_User $user WP_User object.
+ */
+ return apply_filters( 'user_contactmethods', $methods, $user );
+}
+
+/**
+ * The old private function for setting up user contact methods.
+ *
+ * @since 2.9.0
+ * @access private
+ */
+function _wp_get_user_contactmethods( $user = null ) {
+ return wp_get_user_contact_methods( $user );
+}
+
+/**
+ * Gets the text suggesting how to create strong passwords.
+ *
+ * @since 4.1.0
+ *
+ * @return string The password hint text.
+ */
+function wp_get_password_hint() {
+ $hint = __( 'Hint: The password should be at least twelve characters long. To make it stronger, use upper and lower case letters, numbers, and symbols like ! " ? $ % ^ & ).' );
+
+ /**
+ * Filter the text describing the site's password complexity policy.
+ *
+ * @since 4.1.0
+ *
+ * @param string $hint The password hint text.
+ */
+ return apply_filters( 'password_hint', $hint );
+}
+
+/**
+ * Creates, stores, then returns a password reset key for user.
+ *
+ * @since 4.4.0
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ * @global PasswordHash $wp_hasher Portable PHP password hashing framework.
+ *
+ * @param WP_User $user User to retrieve password reset key for.
+ *
+ * @return string|WP_Error Password reset key on success. WP_Error on error.
+ */
+function get_password_reset_key( $user ) {
+ global $wpdb, $wp_hasher;
+
+ /**
+ * Fires before a new password is retrieved.
+ *
+ * @since 1.5.0
+ * @deprecated 1.5.1 Misspelled. Use 'retrieve_password' hook instead.
+ *
+ * @param string $user_login The user login name.
+ */
+ do_action( 'retreive_password', $user->user_login );
+
+ /**
+ * Fires before a new password is retrieved.
+ *
+ * @since 1.5.1
+ *
+ * @param string $user_login The user login name.
+ */
+ do_action( 'retrieve_password', $user->user_login );
+
+ /**
+ * Filter whether to allow a password to be reset.
+ *
+ * @since 2.7.0
+ *
+ * @param bool true Whether to allow the password to be reset. Default true.
+ * @param int $user_data->ID The ID of the user attempting to reset a password.
+ */
+ $allow = apply_filters( 'allow_password_reset', true, $user->ID );
+
+ if ( ! $allow ) {
+ return new WP_Error( 'no_password_reset', __( 'Password reset is not allowed for this user' ) );
+ } elseif ( is_wp_error( $allow ) ) {
+ return $allow;
+ }
+
+ // Generate something random for a password reset key.
+ $key = wp_generate_password( 20, false );
+
+ /**
+ * Fires when a password reset key is generated.
+ *
+ * @since 2.5.0
+ *
+ * @param string $user_login The username for the user.
+ * @param string $key The generated password reset key.
+ */
+ do_action( 'retrieve_password_key', $user->user_login, $key );
+
+ // Now insert the key, hashed, into the DB.
+ if ( empty( $wp_hasher ) ) {
+ require_once ABSPATH . WPINC . '/class-phpass.php';
+ $wp_hasher = new PasswordHash( 8, true );
+ }
+ $hashed = time() . ':' . $wp_hasher->HashPassword( $key );
+ $key_saved = $wpdb->update( $wpdb->users, array( 'user_activation_key' => $hashed ), array( 'user_login' => $user->user_login ) );
+ if ( false === $key_saved ) {
+ return WP_Error( 'no_password_key_update', __( 'Could not save password reset key to database.' ) );
+ }
+
+ return $key;
+}
+
+/**
+ * Retrieves a user row based on password reset key and login
+ *
+ * A key is considered 'expired' if it exactly matches the value of the
+ * user_activation_key field, rather than being matched after going through the
+ * hashing process. This field is now hashed; old values are no longer accepted
+ * but have a different WP_Error code so good user feedback can be provided.
+ *
+ * @since 3.1.0
+ *
+ * @global wpdb $wpdb WordPress database object for queries.
+ * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
+ *
+ * @param string $key Hash to validate sending user's password.
+ * @param string $login The user login.
+ * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
+ */
+function check_password_reset_key($key, $login) {
+ global $wpdb, $wp_hasher;
+
+ $key = preg_replace('/[^a-z0-9]/i', '', $key);
+
+ if ( empty( $key ) || !is_string( $key ) )
+ return new WP_Error('invalid_key', __('Invalid key'));
+
+ if ( empty($login) || !is_string($login) )
+ return new WP_Error('invalid_key', __('Invalid key'));
+
+ $row = $wpdb->get_row( $wpdb->prepare( "SELECT ID, user_activation_key FROM $wpdb->users WHERE user_login = %s", $login ) );
+ if ( ! $row )
+ return new WP_Error('invalid_key', __('Invalid key'));
+
+ if ( empty( $wp_hasher ) ) {
+ require_once ABSPATH . WPINC . '/class-phpass.php';
+ $wp_hasher = new PasswordHash( 8, true );
+ }
+
+ /**
+ * Filter the expiration time of password reset keys.
+ *
+ * @since 4.3.0
+ *
+ * @param int $expiration The expiration time in seconds.
+ */
+ $expiration_duration = apply_filters( 'password_reset_expiration', DAY_IN_SECONDS );
+
+ if ( false !== strpos( $row->user_activation_key, ':' ) ) {
+ list( $pass_request_time, $pass_key ) = explode( ':', $row->user_activation_key, 2 );
+ $expiration_time = $pass_request_time + $expiration_duration;
+ } else {
+ $pass_key = $row->user_activation_key;
+ $expiration_time = false;
+ }
+
+ $hash_is_correct = $wp_hasher->CheckPassword( $key, $pass_key );
+
+ if ( $hash_is_correct && $expiration_time && time() < $expiration_time ) {
+ return get_userdata( $row->ID );
+ } elseif ( $hash_is_correct && $expiration_time ) {
+ // Key has an expiration time that's passed
+ return new WP_Error( 'expired_key', __( 'Invalid key' ) );
+ }
+
+ if ( hash_equals( $row->user_activation_key, $key ) || ( $hash_is_correct && ! $expiration_time ) ) {
+ $return = new WP_Error( 'expired_key', __( 'Invalid key' ) );
+ $user_id = $row->ID;
+
+ /**
+ * Filter the return value of check_password_reset_key() when an
+ * old-style key is used.
+ *
+ * @since 3.7.0 Previously plain-text keys were stored in the database.
+ * @since 4.3.0 Previously key hashes were stored without an expiration time.
+ *
+ * @param WP_Error $return A WP_Error object denoting an expired key.
+ * Return a WP_User object to validate the key.
+ * @param int $user_id The matched user ID.
+ */
+ return apply_filters( 'password_reset_key_expired', $return, $user_id );
+ }
+
+ return new WP_Error( 'invalid_key', __( 'Invalid key' ) );
+}
+
+/**
+ * Handles resetting the user's password.
+ *
+ * @since 2.5.0
+ *
+ * @param object $user The user
+ * @param string $new_pass New password for the user in plaintext
+ */
+function reset_password( $user, $new_pass ) {
+ /**
+ * Fires before the user's password is reset.
+ *
+ * @since 1.5.0
+ *
+ * @param object $user The user.
+ * @param string $new_pass New user password.
+ */
+ do_action( 'password_reset', $user, $new_pass );
+
+ wp_set_password( $new_pass, $user->ID );
+ update_user_option( $user->ID, 'default_password_nag', false, true );
+
+ /**
+ * Fires after the user's password is reset.
+ *
+ * @since 4.4.0
+ *
+ * @param object $user The user.
+ * @param string $new_pass New user password.
+ */
+ do_action( 'after_password_reset', $user, $new_pass );
+}
+
+/**
+ * Handles registering a new user.
+ *
+ * @since 2.5.0
+ *
+ * @param string $user_login User's username for logging in
+ * @param string $user_email User's email address to send password and add
+ * @return int|WP_Error Either user's ID or error on failure.
+ */
+function register_new_user( $user_login, $user_email ) {
+ $errors = new WP_Error();
+
+ $sanitized_user_login = sanitize_user( $user_login );
+ /**
+ * Filter the email address of a user being registered.
+ *
+ * @since 2.1.0
+ *
+ * @param string $user_email The email address of the new user.
+ */
+ $user_email = apply_filters( 'user_registration_email', $user_email );
+
+ // Check the username
+ if ( $sanitized_user_login == '' ) {
+ $errors->add( 'empty_username', __( '<strong>ERROR</strong>: Please enter a username.' ) );
+ } elseif ( ! validate_username( $user_login ) ) {
+ $errors->add( 'invalid_username', __( '<strong>ERROR</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ) );
+ $sanitized_user_login = '';
+ } elseif ( username_exists( $sanitized_user_login ) ) {
+ $errors->add( 'username_exists', __( '<strong>ERROR</strong>: This username is already registered. Please choose another one.' ) );
+ }
+
+ // Check the email address
+ if ( $user_email == '' ) {
+ $errors->add( 'empty_email', __( '<strong>ERROR</strong>: Please type your email address.' ) );
+ } elseif ( ! is_email( $user_email ) ) {
+ $errors->add( 'invalid_email', __( '<strong>ERROR</strong>: The email address isn’t correct.' ) );
+ $user_email = '';
+ } elseif ( email_exists( $user_email ) ) {
+ $errors->add( 'email_exists', __( '<strong>ERROR</strong>: This email is already registered, please choose another one.' ) );
+ }
+
+ /**
+ * Fires when submitting registration form data, before the user is created.
+ *
+ * @since 2.1.0
+ *
+ * @param string $sanitized_user_login The submitted username after being sanitized.
+ * @param string $user_email The submitted email.
+ * @param WP_Error $errors Contains any errors with submitted username and email,
+ * e.g., an empty field, an invalid username or email,
+ * or an existing username or email.
+ */
+ do_action( 'register_post', $sanitized_user_login, $user_email, $errors );
+
+ /**
+ * Filter the errors encountered when a new user is being registered.
+ *
+ * The filtered WP_Error object may, for example, contain errors for an invalid
+ * or existing username or email address. A WP_Error object should always returned,
+ * but may or may not contain errors.
+ *
+ * If any errors are present in $errors, this will abort the user's registration.
+ *
+ * @since 2.1.0
+ *
+ * @param WP_Error $errors A WP_Error object containing any errors encountered
+ * during registration.
+ * @param string $sanitized_user_login User's username after it has been sanitized.
+ * @param string $user_email User's email.
+ */
+ $errors = apply_filters( 'registration_errors', $errors, $sanitized_user_login, $user_email );
+
+ if ( $errors->get_error_code() )
+ return $errors;
+
+ $user_pass = wp_generate_password( 12, false );
+ $user_id = wp_create_user( $sanitized_user_login, $user_pass, $user_email );
+ if ( ! $user_id || is_wp_error( $user_id ) ) {
+ $errors->add( 'registerfail', sprintf( __( '<strong>ERROR</strong>: Couldn’t register you… please contact the <a href="mailto:%s">webmaster</a> !' ), get_option( 'admin_email' ) ) );
+ return $errors;
+ }
+
+ update_user_option( $user_id, 'default_password_nag', true, true ); //Set up the Password change nag.
+
+ /**
+ * Fires after a new user registration has been recorded.
+ *
+ * @since 4.4.0
+ *
+ * @param int $user_id ID of the newly registered user.
+ */
+ do_action( 'register_new_user', $user_id );
+
+ return $user_id;
+}
+
+/**
+ * Initiate email notifications related to the creation of new users.
+ *
+ * Notifications are sent both to the site admin and to the newly created user.
+ *
+ * @since 4.4.0
+ *
+ * @param int $user_id ID of the newly created user.
+ */
+function wp_send_new_user_notifications( $user_id ) {
+ wp_new_user_notification( $user_id, null, 'both' );
+}
+
+/**
+ * Retrieve the current session token from the logged_in cookie.
+ *
+ * @since 4.0.0
+ *
+ * @return string Token.
+ */
+function wp_get_session_token() {
+ $cookie = wp_parse_auth_cookie( '', 'logged_in' );
+ return ! empty( $cookie['token'] ) ? $cookie['token'] : '';
+}
+
+/**
+ * Retrieve a list of sessions for the current user.
+ *
+ * @since 4.0.0
+ * @return array Array of sessions.
+ */
+function wp_get_all_sessions() {
+ $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
+ return $manager->get_all();
+}
+
+/**
+ * Remove the current session token from the database.
+ *
+ * @since 4.0.0
+ */
+function wp_destroy_current_session() {
+ $token = wp_get_session_token();
+ if ( $token ) {
+ $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
+ $manager->destroy( $token );
+ }
+}
+
+/**
+ * Remove all but the current session token for the current user for the database.
+ *
+ * @since 4.0.0
+ */
+function wp_destroy_other_sessions() {
+ $token = wp_get_session_token();
+ if ( $token ) {
+ $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
+ $manager->destroy_others( $token );
+ }
+}
+
+/**
+ * Remove all session tokens for the current user from the database.
+ *
+ * @since 4.0.0
+ */
+function wp_destroy_all_sessions() {
+ $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
+ $manager->destroy_all();
+}
+
+/**
+ * Get the user IDs of all users with no role on this site.
+ *
+ * This function returns an empty array when used on Multisite.
+ *
+ * @since 4.4.0
+ *
+ * @return array Array of user IDs.
+ */
+function wp_get_users_with_no_role() {
+ global $wpdb;
+
+ if ( is_multisite() ) {
+ return array();
+ }
+
+ $prefix = $wpdb->get_blog_prefix();
+ $regex = implode( '|', wp_roles()->get_names() );
+ $regex = preg_replace( '/[^a-zA-Z_\|-]/', '', $regex );
+ $users = $wpdb->get_col( $wpdb->prepare( "
+ SELECT user_id
+ FROM $wpdb->usermeta
+ WHERE meta_key = '{$prefix}capabilities'
+ AND meta_value NOT REGEXP %s
+ ", $regex ) );
+
+ return $users;
+}
</ins></span></pre></div>
<a id="trunksrcwpincludeswidgetfunctionsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/widget-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/widget-functions.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/widget-functions.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,1381 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Widget API: Top-level widget functionality
- *
- * @package WordPress
- * @subpackage Widgets
- * @since 4.4.0
- */
-
-//
-// Template tags & API functions
-//
-
-/**
- * Register a widget
- *
- * Registers a WP_Widget widget
- *
- * @since 2.8.0
- *
- * @see WP_Widget
- *
- * @global WP_Widget_Factory $wp_widget_factory
- *
- * @param string $widget_class The name of a class that extends WP_Widget
- */
-function register_widget($widget_class) {
- global $wp_widget_factory;
-
- $wp_widget_factory->register($widget_class);
-}
-
-/**
- * Unregister a widget
- *
- * Unregisters a WP_Widget widget. Useful for unregistering default widgets.
- * Run within a function hooked to the widgets_init action.
- *
- * @since 2.8.0
- *
- * @see WP_Widget
- *
- * @global WP_Widget_Factory $wp_widget_factory
- *
- * @param string $widget_class The name of a class that extends WP_Widget
- */
-function unregister_widget($widget_class) {
- global $wp_widget_factory;
-
- $wp_widget_factory->unregister($widget_class);
-}
-
-/**
- * Creates multiple sidebars.
- *
- * If you wanted to quickly create multiple sidebars for a theme or internally.
- * This function will allow you to do so. If you don't pass the 'name' and/or
- * 'id' in `$args`, then they will be built for you.
- *
- * @since 2.2.0
- *
- * @see register_sidebar() The second parameter is documented by register_sidebar() and is the same here.
- *
- * @global array $wp_registered_sidebars
- *
- * @param int $number Optional. Number of sidebars to create. Default 1.
- * @param array|string $args {
- * Optional. Array or string of arguments for building a sidebar.
- *
- * @type string $id The base string of the unique identifier for each sidebar. If provided, and multiple
- * sidebars are being defined, the id will have "-2" appended, and so on.
- * Default 'sidebar-' followed by the number the sidebar creation is currently at.
- * @type string $name The name or title for the sidebars displayed in the admin dashboard. If registering
- * more than one sidebar, include '%d' in the string as a placeholder for the uniquely
- * assigned number for each sidebar.
- * Default 'Sidebar' for the first sidebar, otherwise 'Sidebar %d'.
- * }
- */
-function register_sidebars( $number = 1, $args = array() ) {
- global $wp_registered_sidebars;
- $number = (int) $number;
-
- if ( is_string($args) )
- parse_str($args, $args);
-
- for ( $i = 1; $i <= $number; $i++ ) {
- $_args = $args;
-
- if ( $number > 1 )
- $_args['name'] = isset($args['name']) ? sprintf($args['name'], $i) : sprintf(__('Sidebar %d'), $i);
- else
- $_args['name'] = isset($args['name']) ? $args['name'] : __('Sidebar');
-
- // Custom specified ID's are suffixed if they exist already.
- // Automatically generated sidebar names need to be suffixed regardless starting at -0
- if ( isset($args['id']) ) {
- $_args['id'] = $args['id'];
- $n = 2; // Start at -2 for conflicting custom ID's
- while ( is_registered_sidebar( $_args['id'] ) ) {
- $_args['id'] = $args['id'] . '-' . $n++;
- }
- } else {
- $n = count( $wp_registered_sidebars );
- do {
- $_args['id'] = 'sidebar-' . ++$n;
- } while ( is_registered_sidebar( $_args['id'] ) );
- }
- register_sidebar($_args);
- }
-}
-
-/**
- * Builds the definition for a single sidebar and returns the ID.
- *
- * Accepts either a string or an array and then parses that against a set
- * of default arguments for the new sidebar. WordPress will automatically
- * generate a sidebar ID and name based on the current number of registered
- * sidebars if those arguments are not included.
- *
- * When allowing for automatic generation of the name and ID parameters, keep
- * in mind that the incrementor for your sidebar can change over time depending
- * on what other plugins and themes are installed.
- *
- * If theme support for 'widgets' has not yet been added when this function is
- * called, it will be automatically enabled through the use of add_theme_support()
- *
- * @since 2.2.0
- *
- * @global array $wp_registered_sidebars Stores the new sidebar in this array by sidebar ID.
- *
- * @param array|string $args {
- * Optional. Array or string of arguments for the sidebar being registered.
- *
- * @type string $name The name or title of the sidebar displayed in the Widgets
- * interface. Default 'Sidebar $instance'.
- * @type string $id The unique identifier by which the sidebar will be called.
- * Default 'sidebar-$instance'.
- * @type string $description Description of the sidebar, displayed in the Widgets interface.
- * Default empty string.
- * @type string $class Extra CSS class to assign to the sidebar in the Widgets interface.
- * Default empty.
- * @type string $before_widget HTML content to prepend to each widget's HTML output when
- * assigned to this sidebar. Default is an opening list item element.
- * @type string $after_widget HTML content to append to each widget's HTML output when
- * assigned to this sidebar. Default is a closing list item element.
- * @type string $before_title HTML content to prepend to the sidebar title when displayed.
- * Default is an opening h2 element.
- * @type string $after_title HTML content to append to the sidebar title when displayed.
- * Default is a closing h2 element.
- * }
- * @return string Sidebar ID added to $wp_registered_sidebars global.
- */
-function register_sidebar($args = array()) {
- global $wp_registered_sidebars;
-
- $i = count($wp_registered_sidebars) + 1;
-
- $id_is_empty = empty( $args['id'] );
-
- $defaults = array(
- 'name' => sprintf(__('Sidebar %d'), $i ),
- 'id' => "sidebar-$i",
- 'description' => '',
- 'class' => '',
- 'before_widget' => '<li id="%1$s" class="widget %2$s">',
- 'after_widget' => "</li>\n",
- 'before_title' => '<h2 class="widgettitle">',
- 'after_title' => "</h2>\n",
- );
-
- $sidebar = wp_parse_args( $args, $defaults );
-
- if ( $id_is_empty ) {
- /* translators: 1: the id argument, 2: sidebar name, 3: recommended id value */
- _doing_it_wrong( __FUNCTION__, sprintf( __( 'No %1$s was set in the arguments array for the "%2$s" sidebar. Defaulting to "%3$s". Manually set the %1$s to "%3$s" to silence this notice and keep existing sidebar content.' ), '<code>id</code>', $sidebar['name'], $sidebar['id'] ), '4.2.0' );
- }
-
- $wp_registered_sidebars[$sidebar['id']] = $sidebar;
-
- add_theme_support('widgets');
-
- /**
- * Fires once a sidebar has been registered.
- *
- * @since 3.0.0
- *
- * @param array $sidebar Parsed arguments for the registered sidebar.
- */
- do_action( 'register_sidebar', $sidebar );
-
- return $sidebar['id'];
-}
-
-/**
- * Removes a sidebar from the list.
- *
- * @since 2.2.0
- *
- * @global array $wp_registered_sidebars Stores the new sidebar in this array by sidebar ID.
- *
- * @param string $name The ID of the sidebar when it was added.
- */
-function unregister_sidebar( $name ) {
- global $wp_registered_sidebars;
-
- unset( $wp_registered_sidebars[ $name ] );
-}
-
-/**
- * Checks if a sidebar is registered.
- *
- * @since 4.4.0
- *
- * @global array $wp_registered_sidebars Registered sidebars.
- *
- * @param string|int $sidebar_id The ID of the sidebar when it was registered.
- * @return bool True if the sidebar is registered, false otherwise.
- */
-function is_registered_sidebar( $sidebar_id ) {
- global $wp_registered_sidebars;
-
- return isset( $wp_registered_sidebars[ $sidebar_id ] );
-}
-
-/**
- * Register an instance of a widget.
- *
- * The default widget option is 'classname' that can be overridden.
- *
- * The function can also be used to un-register widgets when `$output_callback`
- * parameter is an empty string.
- *
- * @since 2.2.0
- *
- * @global array $wp_registered_widgets Uses stored registered widgets.
- * @global array $wp_register_widget_defaults Retrieves widget defaults.
- * @global array $wp_registered_widget_updates
- * @global array $_wp_deprecated_widgets_callbacks
- *
- * @param int|string $id Widget ID.
- * @param string $name Widget display title.
- * @param callable $output_callback Run when widget is called.
- * @param array $options {
- * Optional. An array of supplementary widget options for the instance.
- *
- * @type string $classname Class name for the widget's HTML container. Default is a shortened
- * version of the output callback name.
- * @type string $description Widget description for display in the widget administration
- * panel and/or theme.
- * }
- */
-function wp_register_sidebar_widget( $id, $name, $output_callback, $options = array() ) {
- global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates, $_wp_deprecated_widgets_callbacks;
-
- $id = strtolower($id);
-
- if ( empty($output_callback) ) {
- unset($wp_registered_widgets[$id]);
- return;
- }
-
- $id_base = _get_widget_id_base($id);
- if ( in_array($output_callback, $_wp_deprecated_widgets_callbacks, true) && !is_callable($output_callback) ) {
- unset( $wp_registered_widget_controls[ $id ] );
- unset( $wp_registered_widget_updates[ $id_base ] );
- return;
- }
-
- $defaults = array('classname' => $output_callback);
- $options = wp_parse_args($options, $defaults);
- $widget = array(
- 'name' => $name,
- 'id' => $id,
- 'callback' => $output_callback,
- 'params' => array_slice(func_get_args(), 4)
- );
- $widget = array_merge($widget, $options);
-
- if ( is_callable($output_callback) && ( !isset($wp_registered_widgets[$id]) || did_action( 'widgets_init' ) ) ) {
-
- /**
- * Fires once for each registered widget.
- *
- * @since 3.0.0
- *
- * @param array $widget An array of default widget arguments.
- */
- do_action( 'wp_register_sidebar_widget', $widget );
- $wp_registered_widgets[$id] = $widget;
- }
-}
-
-/**
- * Retrieve description for widget.
- *
- * When registering widgets, the options can also include 'description' that
- * describes the widget for display on the widget administration panel or
- * in the theme.
- *
- * @since 2.5.0
- *
- * @global array $wp_registered_widgets
- *
- * @param int|string $id Widget ID.
- * @return string|void Widget description, if available.
- */
-function wp_widget_description( $id ) {
- if ( !is_scalar($id) )
- return;
-
- global $wp_registered_widgets;
-
- if ( isset($wp_registered_widgets[$id]['description']) )
- return esc_html( $wp_registered_widgets[$id]['description'] );
-}
-
-/**
- * Retrieve description for a sidebar.
- *
- * When registering sidebars a 'description' parameter can be included that
- * describes the sidebar for display on the widget administration panel.
- *
- * @since 2.9.0
- *
- * @global array $wp_registered_sidebars
- *
- * @param string $id sidebar ID.
- * @return string|void Sidebar description, if available.
- */
-function wp_sidebar_description( $id ) {
- if ( !is_scalar($id) )
- return;
-
- global $wp_registered_sidebars;
-
- if ( isset($wp_registered_sidebars[$id]['description']) )
- return esc_html( $wp_registered_sidebars[$id]['description'] );
-}
-
-/**
- * Remove widget from sidebar.
- *
- * @since 2.2.0
- *
- * @param int|string $id Widget ID.
- */
-function wp_unregister_sidebar_widget($id) {
-
- /**
- * Fires just before a widget is removed from a sidebar.
- *
- * @since 3.0.0
- *
- * @param int $id The widget ID.
- */
- do_action( 'wp_unregister_sidebar_widget', $id );
-
- wp_register_sidebar_widget($id, '', '');
- wp_unregister_widget_control($id);
-}
-
-/**
- * Registers widget control callback for customizing options.
- *
- * The options contains the 'height', 'width', and 'id_base' keys. The 'height'
- * option is never used. The 'width' option is the width of the fully expanded
- * control form, but try hard to use the default width. The 'id_base' is for
- * multi-widgets (widgets which allow multiple instances such as the text
- * widget), an id_base must be provided. The widget id will end up looking like
- * `{$id_base}-{$unique_number}`.
- *
- * @since 2.2.0
- *
- * @todo Document `$options` as a hash notation, re: WP_Widget::__construct() cross-reference.
- * @todo `$params` parameter?
- *
- * @global array $wp_registered_widget_controls
- * @global array $wp_registered_widget_updates
- * @global array $wp_registered_widgets
- * @global array $_wp_deprecated_widgets_callbacks
- *
- * @param int|string $id Sidebar ID.
- * @param string $name Sidebar display name.
- * @param callable $control_callback Run when sidebar is displayed.
- * @param array|string $options Optional. Widget options. See description above. Default empty array.
- */
-function wp_register_widget_control( $id, $name, $control_callback, $options = array() ) {
- global $wp_registered_widget_controls, $wp_registered_widget_updates, $wp_registered_widgets, $_wp_deprecated_widgets_callbacks;
-
- $id = strtolower($id);
- $id_base = _get_widget_id_base($id);
-
- if ( empty($control_callback) ) {
- unset($wp_registered_widget_controls[$id]);
- unset($wp_registered_widget_updates[$id_base]);
- return;
- }
-
- if ( in_array($control_callback, $_wp_deprecated_widgets_callbacks, true) && !is_callable($control_callback) ) {
- unset( $wp_registered_widgets[ $id ] );
- return;
- }
-
- if ( isset($wp_registered_widget_controls[$id]) && !did_action( 'widgets_init' ) )
- return;
-
- $defaults = array('width' => 250, 'height' => 200 ); // height is never used
- $options = wp_parse_args($options, $defaults);
- $options['width'] = (int) $options['width'];
- $options['height'] = (int) $options['height'];
-
- $widget = array(
- 'name' => $name,
- 'id' => $id,
- 'callback' => $control_callback,
- 'params' => array_slice(func_get_args(), 4)
- );
- $widget = array_merge($widget, $options);
-
- $wp_registered_widget_controls[$id] = $widget;
-
- if ( isset($wp_registered_widget_updates[$id_base]) )
- return;
-
- if ( isset($widget['params'][0]['number']) )
- $widget['params'][0]['number'] = -1;
-
- unset($widget['width'], $widget['height'], $widget['name'], $widget['id']);
- $wp_registered_widget_updates[$id_base] = $widget;
-}
-
-/**
- * @global array $wp_registered_widget_updates
- *
- * @param string $id_base
- * @param callable $update_callback
- * @param array $options
- */
-function _register_widget_update_callback($id_base, $update_callback, $options = array()) {
- global $wp_registered_widget_updates;
-
- if ( isset($wp_registered_widget_updates[$id_base]) ) {
- if ( empty($update_callback) )
- unset($wp_registered_widget_updates[$id_base]);
- return;
- }
-
- $widget = array(
- 'callback' => $update_callback,
- 'params' => array_slice(func_get_args(), 3)
- );
-
- $widget = array_merge($widget, $options);
- $wp_registered_widget_updates[$id_base] = $widget;
-}
-
-/**
- *
- * @global array $wp_registered_widget_controls
- *
- * @param int|string $id
- * @param string $name
- * @param callable $form_callback
- * @param array $options
- */
-function _register_widget_form_callback($id, $name, $form_callback, $options = array()) {
- global $wp_registered_widget_controls;
-
- $id = strtolower($id);
-
- if ( empty($form_callback) ) {
- unset($wp_registered_widget_controls[$id]);
- return;
- }
-
- if ( isset($wp_registered_widget_controls[$id]) && !did_action( 'widgets_init' ) )
- return;
-
- $defaults = array('width' => 250, 'height' => 200 );
- $options = wp_parse_args($options, $defaults);
- $options['width'] = (int) $options['width'];
- $options['height'] = (int) $options['height'];
-
- $widget = array(
- 'name' => $name,
- 'id' => $id,
- 'callback' => $form_callback,
- 'params' => array_slice(func_get_args(), 4)
- );
- $widget = array_merge($widget, $options);
-
- $wp_registered_widget_controls[$id] = $widget;
-}
-
-/**
- * Remove control callback for widget.
- *
- * @since 2.2.0
- *
- * @param int|string $id Widget ID.
- */
-function wp_unregister_widget_control($id) {
- wp_register_widget_control( $id, '', '' );
-}
-
-/**
- * Display dynamic sidebar.
- *
- * By default this displays the default sidebar or 'sidebar-1'. If your theme specifies the 'id' or
- * 'name' parameter for its registered sidebars you can pass an id or name as the $index parameter.
- * Otherwise, you can pass in a numerical index to display the sidebar at that index.
- *
- * @since 2.2.0
- *
- * @global array $wp_registered_sidebars
- * @global array $wp_registered_widgets
- *
- * @param int|string $index Optional, default is 1. Index, name or ID of dynamic sidebar.
- * @return bool True, if widget sidebar was found and called. False if not found or not called.
- */
-function dynamic_sidebar( $index = 1 ) {
- global $wp_registered_sidebars, $wp_registered_widgets;
-
- if ( is_int( $index ) ) {
- $index = "sidebar-$index";
- } else {
- $sanitized_index = sanitize_title( $index );
- foreach ( (array) $wp_registered_sidebars as $key => $value ) {
- if ( sanitize_title( $value['name'] ) == $sanitized_index ) {
- $index = $key;
- break;
- }
- }
- }
-
- $sidebars_widgets = wp_get_sidebars_widgets();
- if ( empty( $wp_registered_sidebars[ $index ] ) || empty( $sidebars_widgets[ $index ] ) || ! is_array( $sidebars_widgets[ $index ] ) ) {
- /** This action is documented in wp-includes/widget-functions.php */
- do_action( 'dynamic_sidebar_before', $index, false );
- /** This action is documented in wp-includes/widget-functions.php */
- do_action( 'dynamic_sidebar_after', $index, false );
- /** This filter is documented in wp-includes/widget-functions.php */
- return apply_filters( 'dynamic_sidebar_has_widgets', false, $index );
- }
-
- /**
- * Fires before widgets are rendered in a dynamic sidebar.
- *
- * Note: The action also fires for empty sidebars, and on both the front-end
- * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
- *
- * @since 3.9.0
- *
- * @param int|string $index Index, name, or ID of the dynamic sidebar.
- * @param bool $has_widgets Whether the sidebar is populated with widgets.
- * Default true.
- */
- do_action( 'dynamic_sidebar_before', $index, true );
- $sidebar = $wp_registered_sidebars[$index];
-
- $did_one = false;
- foreach ( (array) $sidebars_widgets[$index] as $id ) {
-
- if ( !isset($wp_registered_widgets[$id]) ) continue;
-
- $params = array_merge(
- array( array_merge( $sidebar, array('widget_id' => $id, 'widget_name' => $wp_registered_widgets[$id]['name']) ) ),
- (array) $wp_registered_widgets[$id]['params']
- );
-
- // Substitute HTML id and class attributes into before_widget
- $classname_ = '';
- foreach ( (array) $wp_registered_widgets[$id]['classname'] as $cn ) {
- if ( is_string($cn) )
- $classname_ .= '_' . $cn;
- elseif ( is_object($cn) )
- $classname_ .= '_' . get_class($cn);
- }
- $classname_ = ltrim($classname_, '_');
- $params[0]['before_widget'] = sprintf($params[0]['before_widget'], $id, $classname_);
-
- /**
- * Filter the parameters passed to a widget's display callback.
- *
- * Note: The filter is evaluated on both the front-end and back-end,
- * including for the Inactive Widgets sidebar on the Widgets screen.
- *
- * @since 2.5.0
- *
- * @see register_sidebar()
- *
- * @param array $params {
- * @type array $args {
- * An array of widget display arguments.
- *
- * @type string $name Name of the sidebar the widget is assigned to.
- * @type string $id ID of the sidebar the widget is assigned to.
- * @type string $description The sidebar description.
- * @type string $class CSS class applied to the sidebar container.
- * @type string $before_widget HTML markup to prepend to each widget in the sidebar.
- * @type string $after_widget HTML markup to append to each widget in the sidebar.
- * @type string $before_title HTML markup to prepend to the widget title when displayed.
- * @type string $after_title HTML markup to append to the widget title when displayed.
- * @type string $widget_id ID of the widget.
- * @type string $widget_name Name of the widget.
- * }
- * @type array $widget_args {
- * An array of multi-widget arguments.
- *
- * @type int $number Number increment used for multiples of the same widget.
- * }
- * }
- */
- $params = apply_filters( 'dynamic_sidebar_params', $params );
-
- $callback = $wp_registered_widgets[$id]['callback'];
-
- /**
- * Fires before a widget's display callback is called.
- *
- * Note: The action fires on both the front-end and back-end, including
- * for widgets in the Inactive Widgets sidebar on the Widgets screen.
- *
- * The action is not fired for empty sidebars.
- *
- * @since 3.0.0
- *
- * @param array $widget_id {
- * An associative array of widget arguments.
- *
- * @type string $name Name of the widget.
- * @type string $id Widget ID.
- * @type array|callable $callback When the hook is fired on the front-end, $callback is an array
- * containing the widget object. Fired on the back-end, $callback
- * is 'wp_widget_control', see $_callback.
- * @type array $params An associative array of multi-widget arguments.
- * @type string $classname CSS class applied to the widget container.
- * @type string $description The widget description.
- * @type array $_callback When the hook is fired on the back-end, $_callback is populated
- * with an array containing the widget object, see $callback.
- * }
- */
- do_action( 'dynamic_sidebar', $wp_registered_widgets[ $id ] );
-
- if ( is_callable($callback) ) {
- call_user_func_array($callback, $params);
- $did_one = true;
- }
- }
-
- /**
- * Fires after widgets are rendered in a dynamic sidebar.
- *
- * Note: The action also fires for empty sidebars, and on both the front-end
- * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
- *
- * @since 3.9.0
- *
- * @param int|string $index Index, name, or ID of the dynamic sidebar.
- * @param bool $has_widgets Whether the sidebar is populated with widgets.
- * Default true.
- */
- do_action( 'dynamic_sidebar_after', $index, true );
-
- /**
- * Filter whether a sidebar has widgets.
- *
- * Note: The filter is also evaluated for empty sidebars, and on both the front-end
- * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
- *
- * @since 3.9.0
- *
- * @param bool $did_one Whether at least one widget was rendered in the sidebar.
- * Default false.
- * @param int|string $index Index, name, or ID of the dynamic sidebar.
- */
- return apply_filters( 'dynamic_sidebar_has_widgets', $did_one, $index );
-}
-
-/**
- * Whether widget is displayed on the front-end.
- *
- * Either $callback or $id_base can be used
- * $id_base is the first argument when extending WP_Widget class
- * Without the optional $widget_id parameter, returns the ID of the first sidebar
- * in which the first instance of the widget with the given callback or $id_base is found.
- * With the $widget_id parameter, returns the ID of the sidebar where
- * the widget with that callback/$id_base AND that ID is found.
- *
- * NOTE: $widget_id and $id_base are the same for single widgets. To be effective
- * this function has to run after widgets have initialized, at action 'init' or later.
- *
- * @since 2.2.0
- *
- * @global array $wp_registered_widgets
- *
- * @param string $callback Optional, Widget callback to check.
- * @param int $widget_id Optional, but needed for checking. Widget ID.
- * @param string $id_base Optional, the base ID of a widget created by extending WP_Widget.
- * @param bool $skip_inactive Optional, whether to check in 'wp_inactive_widgets'.
- * @return string|false False if widget is not active or id of sidebar in which the widget is active.
- */
-function is_active_widget($callback = false, $widget_id = false, $id_base = false, $skip_inactive = true) {
- global $wp_registered_widgets;
-
- $sidebars_widgets = wp_get_sidebars_widgets();
-
- if ( is_array($sidebars_widgets) ) {
- foreach ( $sidebars_widgets as $sidebar => $widgets ) {
- if ( $skip_inactive && ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) ) {
- continue;
- }
-
- if ( is_array($widgets) ) {
- foreach ( $widgets as $widget ) {
- if ( ( $callback && isset($wp_registered_widgets[$widget]['callback']) && $wp_registered_widgets[$widget]['callback'] == $callback ) || ( $id_base && _get_widget_id_base($widget) == $id_base ) ) {
- if ( !$widget_id || $widget_id == $wp_registered_widgets[$widget]['id'] )
- return $sidebar;
- }
- }
- }
- }
- }
- return false;
-}
-
-/**
- * Whether the dynamic sidebar is enabled and used by theme.
- *
- * @since 2.2.0
- *
- * @global array $wp_registered_widgets
- * @global array $wp_registered_sidebars
- *
- * @return bool True, if using widgets. False, if not using widgets.
- */
-function is_dynamic_sidebar() {
- global $wp_registered_widgets, $wp_registered_sidebars;
- $sidebars_widgets = get_option('sidebars_widgets');
- foreach ( (array) $wp_registered_sidebars as $index => $sidebar ) {
- if ( count($sidebars_widgets[$index]) ) {
- foreach ( (array) $sidebars_widgets[$index] as $widget )
- if ( array_key_exists($widget, $wp_registered_widgets) )
- return true;
- }
- }
- return false;
-}
-
-/**
- * Whether a sidebar is in use.
- *
- * @since 2.8.0
- *
- * @param string|int $index Sidebar name, id or number to check.
- * @return bool true if the sidebar is in use, false otherwise.
- */
-function is_active_sidebar( $index ) {
- $index = ( is_int($index) ) ? "sidebar-$index" : sanitize_title($index);
- $sidebars_widgets = wp_get_sidebars_widgets();
- $is_active_sidebar = ! empty( $sidebars_widgets[$index] );
-
- /**
- * Filter whether a dynamic sidebar is considered "active".
- *
- * @since 3.9.0
- *
- * @param bool $is_active_sidebar Whether or not the sidebar should be considered "active".
- * In other words, whether the sidebar contains any widgets.
- * @param int|string $index Index, name, or ID of the dynamic sidebar.
- */
- return apply_filters( 'is_active_sidebar', $is_active_sidebar, $index );
-}
-
-//
-// Internal Functions
-//
-
-/**
- * Retrieve full list of sidebars and their widget instance IDs.
- *
- * Will upgrade sidebar widget list, if needed. Will also save updated list, if
- * needed.
- *
- * @since 2.2.0
- * @access private
- *
- * @global array $_wp_sidebars_widgets
- * @global array $sidebars_widgets
- *
- * @param bool $deprecated Not used (argument deprecated).
- * @return array Upgraded list of widgets to version 3 array format when called from the admin.
- */
-function wp_get_sidebars_widgets( $deprecated = true ) {
- if ( $deprecated !== true )
- _deprecated_argument( __FUNCTION__, '2.8.1' );
-
- global $_wp_sidebars_widgets, $sidebars_widgets;
-
- // If loading from front page, consult $_wp_sidebars_widgets rather than options
- // to see if wp_convert_widget_settings() has made manipulations in memory.
- if ( !is_admin() ) {
- if ( empty($_wp_sidebars_widgets) )
- $_wp_sidebars_widgets = get_option('sidebars_widgets', array());
-
- $sidebars_widgets = $_wp_sidebars_widgets;
- } else {
- $sidebars_widgets = get_option('sidebars_widgets', array());
- }
-
- if ( is_array( $sidebars_widgets ) && isset($sidebars_widgets['array_version']) )
- unset($sidebars_widgets['array_version']);
-
- /**
- * Filter the list of sidebars and their widgets.
- *
- * @since 2.7.0
- *
- * @param array $sidebars_widgets An associative array of sidebars and their widgets.
- */
- return apply_filters( 'sidebars_widgets', $sidebars_widgets );
-}
-
-/**
- * Set the sidebar widget option to update sidebars.
- *
- * @since 2.2.0
- * @access private
- *
- * @param array $sidebars_widgets Sidebar widgets and their settings.
- */
-function wp_set_sidebars_widgets( $sidebars_widgets ) {
- if ( !isset( $sidebars_widgets['array_version'] ) )
- $sidebars_widgets['array_version'] = 3;
- update_option( 'sidebars_widgets', $sidebars_widgets );
-}
-
-/**
- * Retrieve default registered sidebars list.
- *
- * @since 2.2.0
- * @access private
- *
- * @global array $wp_registered_sidebars
- *
- * @return array
- */
-function wp_get_widget_defaults() {
- global $wp_registered_sidebars;
-
- $defaults = array();
-
- foreach ( (array) $wp_registered_sidebars as $index => $sidebar )
- $defaults[$index] = array();
-
- return $defaults;
-}
-
-/**
- * Convert the widget settings from single to multi-widget format.
- *
- * @since 2.8.0
- *
- * @global array $_wp_sidebars_widgets
- *
- * @param string $base_name
- * @param string $option_name
- * @param array $settings
- * @return array
- */
-function wp_convert_widget_settings($base_name, $option_name, $settings) {
- // This test may need expanding.
- $single = $changed = false;
- if ( empty($settings) ) {
- $single = true;
- } else {
- foreach ( array_keys($settings) as $number ) {
- if ( 'number' == $number )
- continue;
- if ( !is_numeric($number) ) {
- $single = true;
- break;
- }
- }
- }
-
- if ( $single ) {
- $settings = array( 2 => $settings );
-
- // If loading from the front page, update sidebar in memory but don't save to options
- if ( is_admin() ) {
- $sidebars_widgets = get_option('sidebars_widgets');
- } else {
- if ( empty($GLOBALS['_wp_sidebars_widgets']) )
- $GLOBALS['_wp_sidebars_widgets'] = get_option('sidebars_widgets', array());
- $sidebars_widgets = &$GLOBALS['_wp_sidebars_widgets'];
- }
-
- foreach ( (array) $sidebars_widgets as $index => $sidebar ) {
- if ( is_array($sidebar) ) {
- foreach ( $sidebar as $i => $name ) {
- if ( $base_name == $name ) {
- $sidebars_widgets[$index][$i] = "$name-2";
- $changed = true;
- break 2;
- }
- }
- }
- }
-
- if ( is_admin() && $changed )
- update_option('sidebars_widgets', $sidebars_widgets);
- }
-
- $settings['_multiwidget'] = 1;
- if ( is_admin() )
- update_option( $option_name, $settings );
-
- return $settings;
-}
-
-/**
- * Output an arbitrary widget as a template tag.
- *
- * @since 2.8.0
- *
- * @global WP_Widget_Factory $wp_widget_factory
- *
- * @param string $widget The widget's PHP class name (see class-wp-widget.php).
- * @param array $instance Optional. The widget's instance settings. Default empty array.
- * @param array $args {
- * Optional. Array of arguments to configure the display of the widget.
- *
- * @type string $before_widget HTML content that will be prepended to the widget's HTML output.
- * Default `<div class="widget %s">`, where `%s` is the widget's class name.
- * @type string $after_widget HTML content that will be appended to the widget's HTML output.
- * Default `</div>`.
- * @type string $before_title HTML content that will be prepended to the widget's title when displayed.
- * Default `<h2 class="widgettitle">`.
- * @type string $after_title HTML content that will be appended to the widget's title when displayed.
- * Default `</h2>`.
- * }
- */
-function the_widget( $widget, $instance = array(), $args = array() ) {
- global $wp_widget_factory;
-
- $widget_obj = $wp_widget_factory->widgets[$widget];
- if ( ! ( $widget_obj instanceof WP_Widget ) ) {
- return;
- }
-
- $default_args = array(
- 'before_widget' => '<div class="widget %s">',
- 'after_widget' => "</div>",
- 'before_title' => '<h2 class="widgettitle">',
- 'after_title' => '</h2>',
- );
- $args = wp_parse_args( $args, $default_args );
- $args['before_widget'] = sprintf( $args['before_widget'], $widget_obj->widget_options['classname'] );
-
- $instance = wp_parse_args($instance);
-
- /**
- * Fires before rendering the requested widget.
- *
- * @since 3.0.0
- *
- * @param string $widget The widget's class name.
- * @param array $instance The current widget instance's settings.
- * @param array $args An array of the widget's sidebar arguments.
- */
- do_action( 'the_widget', $widget, $instance, $args );
-
- $widget_obj->_set(-1);
- $widget_obj->widget($args, $instance);
-}
-
-/**
- * Private
- *
- * @return string
- */
-function _get_widget_id_base($id) {
- return preg_replace( '/-[0-9]+$/', '', $id );
-}
-
-/**
- * Handle sidebars config after theme change
- *
- * @access private
- * @since 3.3.0
- *
- * @global array $sidebars_widgets
- */
-function _wp_sidebars_changed() {
- global $sidebars_widgets;
-
- if ( ! is_array( $sidebars_widgets ) )
- $sidebars_widgets = wp_get_sidebars_widgets();
-
- retrieve_widgets(true);
-}
-
-/**
- * Look for "lost" widgets, this has to run at least on each theme change.
- *
- * @since 2.8.0
- *
- * @global array $wp_registered_sidebars
- * @global array $sidebars_widgets
- * @global array $wp_registered_widgets
- *
- * @param string|bool $theme_changed Whether the theme was changed as a boolean. A value
- * of 'customize' defers updates for the Customizer.
- * @return array|void
- */
-function retrieve_widgets( $theme_changed = false ) {
- global $wp_registered_sidebars, $sidebars_widgets, $wp_registered_widgets;
-
- $registered_sidebar_keys = array_keys( $wp_registered_sidebars );
- $orphaned = 0;
-
- $old_sidebars_widgets = get_theme_mod( 'sidebars_widgets' );
- if ( is_array( $old_sidebars_widgets ) ) {
- // time() that sidebars were stored is in $old_sidebars_widgets['time']
- $_sidebars_widgets = $old_sidebars_widgets['data'];
-
- if ( 'customize' !== $theme_changed ) {
- remove_theme_mod( 'sidebars_widgets' );
- }
-
- foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
- if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) {
- continue;
- }
-
- if ( !in_array( $sidebar, $registered_sidebar_keys ) ) {
- $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $widgets;
- unset( $_sidebars_widgets[$sidebar] );
- }
- }
- } else {
- if ( empty( $sidebars_widgets ) )
- return;
-
- unset( $sidebars_widgets['array_version'] );
-
- $old = array_keys($sidebars_widgets);
- sort($old);
- sort($registered_sidebar_keys);
-
- if ( $old == $registered_sidebar_keys )
- return;
-
- $_sidebars_widgets = array(
- 'wp_inactive_widgets' => !empty( $sidebars_widgets['wp_inactive_widgets'] ) ? $sidebars_widgets['wp_inactive_widgets'] : array()
- );
-
- unset( $sidebars_widgets['wp_inactive_widgets'] );
-
- foreach ( $wp_registered_sidebars as $id => $settings ) {
- if ( $theme_changed ) {
- $_sidebars_widgets[$id] = array_shift( $sidebars_widgets );
- } else {
- // no theme change, grab only sidebars that are currently registered
- if ( isset( $sidebars_widgets[$id] ) ) {
- $_sidebars_widgets[$id] = $sidebars_widgets[$id];
- unset( $sidebars_widgets[$id] );
- }
- }
- }
-
- foreach ( $sidebars_widgets as $val ) {
- if ( is_array($val) && ! empty( $val ) )
- $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $val;
- }
- }
-
- // discard invalid, theme-specific widgets from sidebars
- $shown_widgets = array();
-
- foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
- if ( !is_array($widgets) )
- continue;
-
- $_widgets = array();
- foreach ( $widgets as $widget ) {
- if ( isset($wp_registered_widgets[$widget]) )
- $_widgets[] = $widget;
- }
-
- $_sidebars_widgets[$sidebar] = $_widgets;
- $shown_widgets = array_merge($shown_widgets, $_widgets);
- }
-
- $sidebars_widgets = $_sidebars_widgets;
- unset($_sidebars_widgets, $_widgets);
-
- // find hidden/lost multi-widget instances
- $lost_widgets = array();
- foreach ( $wp_registered_widgets as $key => $val ) {
- if ( in_array($key, $shown_widgets, true) )
- continue;
-
- $number = preg_replace('/.+?-([0-9]+)$/', '$1', $key);
-
- if ( 2 > (int) $number )
- continue;
-
- $lost_widgets[] = $key;
- }
-
- $sidebars_widgets['wp_inactive_widgets'] = array_merge($lost_widgets, (array) $sidebars_widgets['wp_inactive_widgets']);
- if ( 'customize' !== $theme_changed ) {
- wp_set_sidebars_widgets( $sidebars_widgets );
- }
-
- return $sidebars_widgets;
-}
-
-/**
- * Display the RSS entries in a list.
- *
- * @since 2.5.0
- *
- * @param string|array|object $rss RSS url.
- * @param array $args Widget arguments.
- */
-function wp_widget_rss_output( $rss, $args = array() ) {
- if ( is_string( $rss ) ) {
- $rss = fetch_feed($rss);
- } elseif ( is_array($rss) && isset($rss['url']) ) {
- $args = $rss;
- $rss = fetch_feed($rss['url']);
- } elseif ( !is_object($rss) ) {
- return;
- }
-
- if ( is_wp_error($rss) ) {
- if ( is_admin() || current_user_can('manage_options') )
- echo '<p>' . sprintf( __('<strong>RSS Error</strong>: %s'), $rss->get_error_message() ) . '</p>';
- return;
- }
-
- $default_args = array( 'show_author' => 0, 'show_date' => 0, 'show_summary' => 0, 'items' => 0 );
- $args = wp_parse_args( $args, $default_args );
-
- $items = (int) $args['items'];
- if ( $items < 1 || 20 < $items )
- $items = 10;
- $show_summary = (int) $args['show_summary'];
- $show_author = (int) $args['show_author'];
- $show_date = (int) $args['show_date'];
-
- if ( !$rss->get_item_quantity() ) {
- echo '<ul><li>' . __( 'An error has occurred, which probably means the feed is down. Try again later.' ) . '</li></ul>';
- $rss->__destruct();
- unset($rss);
- return;
- }
-
- echo '<ul>';
- foreach ( $rss->get_items( 0, $items ) as $item ) {
- $link = $item->get_link();
- while ( stristr( $link, 'http' ) != $link ) {
- $link = substr( $link, 1 );
- }
- $link = esc_url( strip_tags( $link ) );
-
- $title = esc_html( trim( strip_tags( $item->get_title() ) ) );
- if ( empty( $title ) ) {
- $title = __( 'Untitled' );
- }
-
- $desc = @html_entity_decode( $item->get_description(), ENT_QUOTES, get_option( 'blog_charset' ) );
- $desc = esc_attr( wp_trim_words( $desc, 55, ' […]' ) );
-
- $summary = '';
- if ( $show_summary ) {
- $summary = $desc;
-
- // Change existing [...] to […].
- if ( '[...]' == substr( $summary, -5 ) ) {
- $summary = substr( $summary, 0, -5 ) . '[…]';
- }
-
- $summary = '<div class="rssSummary">' . esc_html( $summary ) . '</div>';
- }
-
- $date = '';
- if ( $show_date ) {
- $date = $item->get_date( 'U' );
-
- if ( $date ) {
- $date = ' <span class="rss-date">' . date_i18n( get_option( 'date_format' ), $date ) . '</span>';
- }
- }
-
- $author = '';
- if ( $show_author ) {
- $author = $item->get_author();
- if ( is_object($author) ) {
- $author = $author->get_name();
- $author = ' <cite>' . esc_html( strip_tags( $author ) ) . '</cite>';
- }
- }
-
- if ( $link == '' ) {
- echo "<li>$title{$date}{$summary}{$author}</li>";
- } elseif ( $show_summary ) {
- echo "<li><a class='rsswidget' href='$link'>$title</a>{$date}{$summary}{$author}</li>";
- } else {
- echo "<li><a class='rsswidget' href='$link'>$title</a>{$date}{$author}</li>";
- }
- }
- echo '</ul>';
- $rss->__destruct();
- unset($rss);
-}
-
-/**
- * Display RSS widget options form.
- *
- * The options for what fields are displayed for the RSS form are all booleans
- * and are as follows: 'url', 'title', 'items', 'show_summary', 'show_author',
- * 'show_date'.
- *
- * @since 2.5.0
- *
- * @param array|string $args Values for input fields.
- * @param array $inputs Override default display options.
- */
-function wp_widget_rss_form( $args, $inputs = null ) {
- $default_inputs = array( 'url' => true, 'title' => true, 'items' => true, 'show_summary' => true, 'show_author' => true, 'show_date' => true );
- $inputs = wp_parse_args( $inputs, $default_inputs );
-
- $args['title'] = isset( $args['title'] ) ? $args['title'] : '';
- $args['url'] = isset( $args['url'] ) ? $args['url'] : '';
- $args['items'] = isset( $args['items'] ) ? (int) $args['items'] : 0;
-
- if ( $args['items'] < 1 || 20 < $args['items'] ) {
- $args['items'] = 10;
- }
-
- $args['show_summary'] = isset( $args['show_summary'] ) ? (int) $args['show_summary'] : (int) $inputs['show_summary'];
- $args['show_author'] = isset( $args['show_author'] ) ? (int) $args['show_author'] : (int) $inputs['show_author'];
- $args['show_date'] = isset( $args['show_date'] ) ? (int) $args['show_date'] : (int) $inputs['show_date'];
-
- if ( ! empty( $args['error'] ) ) {
- echo '<p class="widget-error"><strong>' . sprintf( __( 'RSS Error: %s' ), $args['error'] ) . '</strong></p>';
- }
-
- $esc_number = esc_attr( $args['number'] );
- if ( $inputs['url'] ) :
-?>
- <p><label for="rss-url-<?php echo $esc_number; ?>"><?php _e( 'Enter the RSS feed URL here:' ); ?></label>
- <input class="widefat" id="rss-url-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][url]" type="text" value="<?php echo esc_url( $args['url'] ); ?>" /></p>
-<?php endif; if ( $inputs['title'] ) : ?>
- <p><label for="rss-title-<?php echo $esc_number; ?>"><?php _e( 'Give the feed a title (optional):' ); ?></label>
- <input class="widefat" id="rss-title-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][title]" type="text" value="<?php echo esc_attr( $args['title'] ); ?>" /></p>
-<?php endif; if ( $inputs['items'] ) : ?>
- <p><label for="rss-items-<?php echo $esc_number; ?>"><?php _e( 'How many items would you like to display?' ); ?></label>
- <select id="rss-items-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][items]">
- <?php
- for ( $i = 1; $i <= 20; ++$i ) {
- echo "<option value='$i' " . selected( $args['items'], $i, false ) . ">$i</option>";
- }
- ?>
- </select></p>
-<?php endif; if ( $inputs['show_summary'] ) : ?>
- <p><input id="rss-show-summary-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][show_summary]" type="checkbox" value="1" <?php checked( $args['show_summary'] ); ?> />
- <label for="rss-show-summary-<?php echo $esc_number; ?>"><?php _e( 'Display item content?' ); ?></label></p>
-<?php endif; if ( $inputs['show_author'] ) : ?>
- <p><input id="rss-show-author-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][show_author]" type="checkbox" value="1" <?php checked( $args['show_author'] ); ?> />
- <label for="rss-show-author-<?php echo $esc_number; ?>"><?php _e( 'Display item author if available?' ); ?></label></p>
-<?php endif; if ( $inputs['show_date'] ) : ?>
- <p><input id="rss-show-date-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][show_date]" type="checkbox" value="1" <?php checked( $args['show_date'] ); ?>/>
- <label for="rss-show-date-<?php echo $esc_number; ?>"><?php _e( 'Display item date?' ); ?></label></p>
-<?php
- endif;
- foreach ( array_keys($default_inputs) as $input ) :
- if ( 'hidden' === $inputs[$input] ) :
- $id = str_replace( '_', '-', $input );
-?>
- <input type="hidden" id="rss-<?php echo esc_attr( $id ); ?>-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][<?php echo esc_attr( $input ); ?>]" value="<?php echo esc_attr( $args[ $input ] ); ?>" />
-<?php
- endif;
- endforeach;
-}
-
-/**
- * Process RSS feed widget data and optionally retrieve feed items.
- *
- * The feed widget can not have more than 20 items or it will reset back to the
- * default, which is 10.
- *
- * The resulting array has the feed title, feed url, feed link (from channel),
- * feed items, error (if any), and whether to show summary, author, and date.
- * All respectively in the order of the array elements.
- *
- * @since 2.5.0
- *
- * @param array $widget_rss RSS widget feed data. Expects unescaped data.
- * @param bool $check_feed Optional, default is true. Whether to check feed for errors.
- * @return array
- */
-function wp_widget_rss_process( $widget_rss, $check_feed = true ) {
- $items = (int) $widget_rss['items'];
- if ( $items < 1 || 20 < $items )
- $items = 10;
- $url = esc_url_raw( strip_tags( $widget_rss['url'] ) );
- $title = isset( $widget_rss['title'] ) ? trim( strip_tags( $widget_rss['title'] ) ) : '';
- $show_summary = isset( $widget_rss['show_summary'] ) ? (int) $widget_rss['show_summary'] : 0;
- $show_author = isset( $widget_rss['show_author'] ) ? (int) $widget_rss['show_author'] :0;
- $show_date = isset( $widget_rss['show_date'] ) ? (int) $widget_rss['show_date'] : 0;
-
- if ( $check_feed ) {
- $rss = fetch_feed($url);
- $error = false;
- $link = '';
- if ( is_wp_error($rss) ) {
- $error = $rss->get_error_message();
- } else {
- $link = esc_url(strip_tags($rss->get_permalink()));
- while ( stristr($link, 'http') != $link )
- $link = substr($link, 1);
-
- $rss->__destruct();
- unset($rss);
- }
- }
-
- return compact( 'title', 'url', 'link', 'items', 'error', 'show_summary', 'show_author', 'show_date' );
-}
-
-/**
- * Register all of the default WordPress widgets on startup.
- *
- * Calls 'widgets_init' action after all of the WordPress widgets have been
- * registered.
- *
- * @since 2.2.0
- */
-function wp_widgets_init() {
- if ( !is_blog_installed() )
- return;
-
- register_widget('WP_Widget_Pages');
-
- register_widget('WP_Widget_Calendar');
-
- register_widget('WP_Widget_Archives');
-
- if ( get_option( 'link_manager_enabled' ) )
- register_widget('WP_Widget_Links');
-
- register_widget('WP_Widget_Meta');
-
- register_widget('WP_Widget_Search');
-
- register_widget('WP_Widget_Text');
-
- register_widget('WP_Widget_Categories');
-
- register_widget('WP_Widget_Recent_Posts');
-
- register_widget('WP_Widget_Recent_Comments');
-
- register_widget('WP_Widget_RSS');
-
- register_widget('WP_Widget_Tag_Cloud');
-
- register_widget('WP_Nav_Menu_Widget');
-
- /**
- * Fires after all default WordPress widgets have been registered.
- *
- * @since 2.2.0
- */
- do_action( 'widgets_init' );
-}
</del></span></pre></div>
<a id="trunksrcwpincludeswidgetsphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: trunk/src/wp-includes/widgets.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/widgets.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-includes/widgets.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,100 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-/**
- * Core Widgets API
- *
- * This API is used for creating dynamic sidebar without hardcoding functionality into
- * themes
- *
- * Includes both internal WordPress routines and theme-use routines.
- *
- * This functionality was found in a plugin before the WordPress 2.2 release, which
- * included it in the core from that point on.
- *
- * @link https://codex.wordpress.org/Plugins/WordPress_Widgets WordPress Widgets
- * @link https://codex.wordpress.org/Plugins/WordPress_Widgets_Api Widgets API
- *
- * @package WordPress
- * @subpackage Widgets
- * @since 2.2.0
- */
-
-//
-// Global Variables
-//
-
-/** @ignore */
-global $wp_registered_sidebars, $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
-
-/**
- * Stores the sidebars, since many themes can have more than one.
- *
- * @global array $wp_registered_sidebars
- * @since 2.2.0
- */
-$wp_registered_sidebars = array();
-
-/**
- * Stores the registered widgets.
- *
- * @global array $wp_registered_widgets
- * @since 2.2.0
- */
-$wp_registered_widgets = array();
-
-/**
- * Stores the registered widget control (options).
- *
- * @global array $wp_registered_widget_controls
- * @since 2.2.0
- */
-$wp_registered_widget_controls = array();
-/**
- * @global array $wp_registered_widget_updates
- */
-$wp_registered_widget_updates = array();
-
-/**
- * Private
- *
- * @global array $_wp_sidebars_widgets
- */
-$_wp_sidebars_widgets = array();
-
-/**
- * Private
- *
- * @global array $_wp_deprecated_widgets_callbacks
- */
-$GLOBALS['_wp_deprecated_widgets_callbacks'] = array(
- 'wp_widget_pages',
- 'wp_widget_pages_control',
- 'wp_widget_calendar',
- 'wp_widget_calendar_control',
- 'wp_widget_archives',
- 'wp_widget_archives_control',
- 'wp_widget_links',
- 'wp_widget_meta',
- 'wp_widget_meta_control',
- 'wp_widget_search',
- 'wp_widget_recent_entries',
- 'wp_widget_recent_entries_control',
- 'wp_widget_tag_cloud',
- 'wp_widget_tag_cloud_control',
- 'wp_widget_categories',
- 'wp_widget_categories_control',
- 'wp_widget_text',
- 'wp_widget_text_control',
- 'wp_widget_rss',
- 'wp_widget_rss_control',
- 'wp_widget_recent_comments',
- 'wp_widget_recent_comments_control'
-);
-
-/** WP_Widget class */
-require_once( ABSPATH . WPINC . '/class-wp-widget.php' );
-
-/** WP_Widget_Factory class */
-require_once( ABSPATH . WPINC . '/class-wp-widget-factory.php' );
-
-/** Core widgets functionality */
-require_once( ABSPATH . WPINC . '/widget-functions.php' );
</del></span></pre></div>
<a id="trunksrcwpincludeswidgetsphpfromrev35712trunksrcwpincludeswidgetfunctionsphp"></a>
<div class="copfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Copied: trunk/src/wp-includes/widgets.php (from rev 35712, trunk/src/wp-includes/widget-functions.php)</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/widgets.php (rev 0)
+++ trunk/src/wp-includes/widgets.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,1464 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Core Widgets API
+ *
+ * This API is used for creating dynamic sidebar without hardcoding functionality into
+ * themes
+ *
+ * Includes both internal WordPress routines and theme-use routines.
+ *
+ * This functionality was found in a plugin before the WordPress 2.2 release, which
+ * included it in the core from that point on.
+ *
+ * @link https://codex.wordpress.org/Plugins/WordPress_Widgets WordPress Widgets
+ * @link https://codex.wordpress.org/Plugins/WordPress_Widgets_Api Widgets API
+ *
+ * @package WordPress
+ * @subpackage Widgets
+ * @since 2.2.0
+ */
+
+//
+// Global Variables
+//
+
+/** @ignore */
+global $wp_registered_sidebars, $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
+
+/**
+ * Stores the sidebars, since many themes can have more than one.
+ *
+ * @global array $wp_registered_sidebars
+ * @since 2.2.0
+ */
+$wp_registered_sidebars = array();
+
+/**
+ * Stores the registered widgets.
+ *
+ * @global array $wp_registered_widgets
+ * @since 2.2.0
+ */
+$wp_registered_widgets = array();
+
+/**
+ * Stores the registered widget control (options).
+ *
+ * @global array $wp_registered_widget_controls
+ * @since 2.2.0
+ */
+$wp_registered_widget_controls = array();
+/**
+ * @global array $wp_registered_widget_updates
+ */
+$wp_registered_widget_updates = array();
+
+/**
+ * Private
+ *
+ * @global array $_wp_sidebars_widgets
+ */
+$_wp_sidebars_widgets = array();
+
+/**
+ * Private
+ *
+ * @global array $_wp_deprecated_widgets_callbacks
+ */
+$GLOBALS['_wp_deprecated_widgets_callbacks'] = array(
+ 'wp_widget_pages',
+ 'wp_widget_pages_control',
+ 'wp_widget_calendar',
+ 'wp_widget_calendar_control',
+ 'wp_widget_archives',
+ 'wp_widget_archives_control',
+ 'wp_widget_links',
+ 'wp_widget_meta',
+ 'wp_widget_meta_control',
+ 'wp_widget_search',
+ 'wp_widget_recent_entries',
+ 'wp_widget_recent_entries_control',
+ 'wp_widget_tag_cloud',
+ 'wp_widget_tag_cloud_control',
+ 'wp_widget_categories',
+ 'wp_widget_categories_control',
+ 'wp_widget_text',
+ 'wp_widget_text_control',
+ 'wp_widget_rss',
+ 'wp_widget_rss_control',
+ 'wp_widget_recent_comments',
+ 'wp_widget_recent_comments_control'
+);
+
+//
+// Template tags & API functions
+//
+
+/**
+ * Register a widget
+ *
+ * Registers a WP_Widget widget
+ *
+ * @since 2.8.0
+ *
+ * @see WP_Widget
+ *
+ * @global WP_Widget_Factory $wp_widget_factory
+ *
+ * @param string $widget_class The name of a class that extends WP_Widget
+ */
+function register_widget($widget_class) {
+ global $wp_widget_factory;
+
+ $wp_widget_factory->register($widget_class);
+}
+
+/**
+ * Unregister a widget
+ *
+ * Unregisters a WP_Widget widget. Useful for unregistering default widgets.
+ * Run within a function hooked to the widgets_init action.
+ *
+ * @since 2.8.0
+ *
+ * @see WP_Widget
+ *
+ * @global WP_Widget_Factory $wp_widget_factory
+ *
+ * @param string $widget_class The name of a class that extends WP_Widget
+ */
+function unregister_widget($widget_class) {
+ global $wp_widget_factory;
+
+ $wp_widget_factory->unregister($widget_class);
+}
+
+/**
+ * Creates multiple sidebars.
+ *
+ * If you wanted to quickly create multiple sidebars for a theme or internally.
+ * This function will allow you to do so. If you don't pass the 'name' and/or
+ * 'id' in `$args`, then they will be built for you.
+ *
+ * @since 2.2.0
+ *
+ * @see register_sidebar() The second parameter is documented by register_sidebar() and is the same here.
+ *
+ * @global array $wp_registered_sidebars
+ *
+ * @param int $number Optional. Number of sidebars to create. Default 1.
+ * @param array|string $args {
+ * Optional. Array or string of arguments for building a sidebar.
+ *
+ * @type string $id The base string of the unique identifier for each sidebar. If provided, and multiple
+ * sidebars are being defined, the id will have "-2" appended, and so on.
+ * Default 'sidebar-' followed by the number the sidebar creation is currently at.
+ * @type string $name The name or title for the sidebars displayed in the admin dashboard. If registering
+ * more than one sidebar, include '%d' in the string as a placeholder for the uniquely
+ * assigned number for each sidebar.
+ * Default 'Sidebar' for the first sidebar, otherwise 'Sidebar %d'.
+ * }
+ */
+function register_sidebars( $number = 1, $args = array() ) {
+ global $wp_registered_sidebars;
+ $number = (int) $number;
+
+ if ( is_string($args) )
+ parse_str($args, $args);
+
+ for ( $i = 1; $i <= $number; $i++ ) {
+ $_args = $args;
+
+ if ( $number > 1 )
+ $_args['name'] = isset($args['name']) ? sprintf($args['name'], $i) : sprintf(__('Sidebar %d'), $i);
+ else
+ $_args['name'] = isset($args['name']) ? $args['name'] : __('Sidebar');
+
+ // Custom specified ID's are suffixed if they exist already.
+ // Automatically generated sidebar names need to be suffixed regardless starting at -0
+ if ( isset($args['id']) ) {
+ $_args['id'] = $args['id'];
+ $n = 2; // Start at -2 for conflicting custom ID's
+ while ( is_registered_sidebar( $_args['id'] ) ) {
+ $_args['id'] = $args['id'] . '-' . $n++;
+ }
+ } else {
+ $n = count( $wp_registered_sidebars );
+ do {
+ $_args['id'] = 'sidebar-' . ++$n;
+ } while ( is_registered_sidebar( $_args['id'] ) );
+ }
+ register_sidebar($_args);
+ }
+}
+
+/**
+ * Builds the definition for a single sidebar and returns the ID.
+ *
+ * Accepts either a string or an array and then parses that against a set
+ * of default arguments for the new sidebar. WordPress will automatically
+ * generate a sidebar ID and name based on the current number of registered
+ * sidebars if those arguments are not included.
+ *
+ * When allowing for automatic generation of the name and ID parameters, keep
+ * in mind that the incrementor for your sidebar can change over time depending
+ * on what other plugins and themes are installed.
+ *
+ * If theme support for 'widgets' has not yet been added when this function is
+ * called, it will be automatically enabled through the use of add_theme_support()
+ *
+ * @since 2.2.0
+ *
+ * @global array $wp_registered_sidebars Stores the new sidebar in this array by sidebar ID.
+ *
+ * @param array|string $args {
+ * Optional. Array or string of arguments for the sidebar being registered.
+ *
+ * @type string $name The name or title of the sidebar displayed in the Widgets
+ * interface. Default 'Sidebar $instance'.
+ * @type string $id The unique identifier by which the sidebar will be called.
+ * Default 'sidebar-$instance'.
+ * @type string $description Description of the sidebar, displayed in the Widgets interface.
+ * Default empty string.
+ * @type string $class Extra CSS class to assign to the sidebar in the Widgets interface.
+ * Default empty.
+ * @type string $before_widget HTML content to prepend to each widget's HTML output when
+ * assigned to this sidebar. Default is an opening list item element.
+ * @type string $after_widget HTML content to append to each widget's HTML output when
+ * assigned to this sidebar. Default is a closing list item element.
+ * @type string $before_title HTML content to prepend to the sidebar title when displayed.
+ * Default is an opening h2 element.
+ * @type string $after_title HTML content to append to the sidebar title when displayed.
+ * Default is a closing h2 element.
+ * }
+ * @return string Sidebar ID added to $wp_registered_sidebars global.
+ */
+function register_sidebar($args = array()) {
+ global $wp_registered_sidebars;
+
+ $i = count($wp_registered_sidebars) + 1;
+
+ $id_is_empty = empty( $args['id'] );
+
+ $defaults = array(
+ 'name' => sprintf(__('Sidebar %d'), $i ),
+ 'id' => "sidebar-$i",
+ 'description' => '',
+ 'class' => '',
+ 'before_widget' => '<li id="%1$s" class="widget %2$s">',
+ 'after_widget' => "</li>\n",
+ 'before_title' => '<h2 class="widgettitle">',
+ 'after_title' => "</h2>\n",
+ );
+
+ $sidebar = wp_parse_args( $args, $defaults );
+
+ if ( $id_is_empty ) {
+ /* translators: 1: the id argument, 2: sidebar name, 3: recommended id value */
+ _doing_it_wrong( __FUNCTION__, sprintf( __( 'No %1$s was set in the arguments array for the "%2$s" sidebar. Defaulting to "%3$s". Manually set the %1$s to "%3$s" to silence this notice and keep existing sidebar content.' ), '<code>id</code>', $sidebar['name'], $sidebar['id'] ), '4.2.0' );
+ }
+
+ $wp_registered_sidebars[$sidebar['id']] = $sidebar;
+
+ add_theme_support('widgets');
+
+ /**
+ * Fires once a sidebar has been registered.
+ *
+ * @since 3.0.0
+ *
+ * @param array $sidebar Parsed arguments for the registered sidebar.
+ */
+ do_action( 'register_sidebar', $sidebar );
+
+ return $sidebar['id'];
+}
+
+/**
+ * Removes a sidebar from the list.
+ *
+ * @since 2.2.0
+ *
+ * @global array $wp_registered_sidebars Stores the new sidebar in this array by sidebar ID.
+ *
+ * @param string $name The ID of the sidebar when it was added.
+ */
+function unregister_sidebar( $name ) {
+ global $wp_registered_sidebars;
+
+ unset( $wp_registered_sidebars[ $name ] );
+}
+
+/**
+ * Checks if a sidebar is registered.
+ *
+ * @since 4.4.0
+ *
+ * @global array $wp_registered_sidebars Registered sidebars.
+ *
+ * @param string|int $sidebar_id The ID of the sidebar when it was registered.
+ * @return bool True if the sidebar is registered, false otherwise.
+ */
+function is_registered_sidebar( $sidebar_id ) {
+ global $wp_registered_sidebars;
+
+ return isset( $wp_registered_sidebars[ $sidebar_id ] );
+}
+
+/**
+ * Register an instance of a widget.
+ *
+ * The default widget option is 'classname' that can be overridden.
+ *
+ * The function can also be used to un-register widgets when `$output_callback`
+ * parameter is an empty string.
+ *
+ * @since 2.2.0
+ *
+ * @global array $wp_registered_widgets Uses stored registered widgets.
+ * @global array $wp_register_widget_defaults Retrieves widget defaults.
+ * @global array $wp_registered_widget_updates
+ * @global array $_wp_deprecated_widgets_callbacks
+ *
+ * @param int|string $id Widget ID.
+ * @param string $name Widget display title.
+ * @param callable $output_callback Run when widget is called.
+ * @param array $options {
+ * Optional. An array of supplementary widget options for the instance.
+ *
+ * @type string $classname Class name for the widget's HTML container. Default is a shortened
+ * version of the output callback name.
+ * @type string $description Widget description for display in the widget administration
+ * panel and/or theme.
+ * }
+ */
+function wp_register_sidebar_widget( $id, $name, $output_callback, $options = array() ) {
+ global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates, $_wp_deprecated_widgets_callbacks;
+
+ $id = strtolower($id);
+
+ if ( empty($output_callback) ) {
+ unset($wp_registered_widgets[$id]);
+ return;
+ }
+
+ $id_base = _get_widget_id_base($id);
+ if ( in_array($output_callback, $_wp_deprecated_widgets_callbacks, true) && !is_callable($output_callback) ) {
+ unset( $wp_registered_widget_controls[ $id ] );
+ unset( $wp_registered_widget_updates[ $id_base ] );
+ return;
+ }
+
+ $defaults = array('classname' => $output_callback);
+ $options = wp_parse_args($options, $defaults);
+ $widget = array(
+ 'name' => $name,
+ 'id' => $id,
+ 'callback' => $output_callback,
+ 'params' => array_slice(func_get_args(), 4)
+ );
+ $widget = array_merge($widget, $options);
+
+ if ( is_callable($output_callback) && ( !isset($wp_registered_widgets[$id]) || did_action( 'widgets_init' ) ) ) {
+
+ /**
+ * Fires once for each registered widget.
+ *
+ * @since 3.0.0
+ *
+ * @param array $widget An array of default widget arguments.
+ */
+ do_action( 'wp_register_sidebar_widget', $widget );
+ $wp_registered_widgets[$id] = $widget;
+ }
+}
+
+/**
+ * Retrieve description for widget.
+ *
+ * When registering widgets, the options can also include 'description' that
+ * describes the widget for display on the widget administration panel or
+ * in the theme.
+ *
+ * @since 2.5.0
+ *
+ * @global array $wp_registered_widgets
+ *
+ * @param int|string $id Widget ID.
+ * @return string|void Widget description, if available.
+ */
+function wp_widget_description( $id ) {
+ if ( !is_scalar($id) )
+ return;
+
+ global $wp_registered_widgets;
+
+ if ( isset($wp_registered_widgets[$id]['description']) )
+ return esc_html( $wp_registered_widgets[$id]['description'] );
+}
+
+/**
+ * Retrieve description for a sidebar.
+ *
+ * When registering sidebars a 'description' parameter can be included that
+ * describes the sidebar for display on the widget administration panel.
+ *
+ * @since 2.9.0
+ *
+ * @global array $wp_registered_sidebars
+ *
+ * @param string $id sidebar ID.
+ * @return string|void Sidebar description, if available.
+ */
+function wp_sidebar_description( $id ) {
+ if ( !is_scalar($id) )
+ return;
+
+ global $wp_registered_sidebars;
+
+ if ( isset($wp_registered_sidebars[$id]['description']) )
+ return esc_html( $wp_registered_sidebars[$id]['description'] );
+}
+
+/**
+ * Remove widget from sidebar.
+ *
+ * @since 2.2.0
+ *
+ * @param int|string $id Widget ID.
+ */
+function wp_unregister_sidebar_widget($id) {
+
+ /**
+ * Fires just before a widget is removed from a sidebar.
+ *
+ * @since 3.0.0
+ *
+ * @param int $id The widget ID.
+ */
+ do_action( 'wp_unregister_sidebar_widget', $id );
+
+ wp_register_sidebar_widget($id, '', '');
+ wp_unregister_widget_control($id);
+}
+
+/**
+ * Registers widget control callback for customizing options.
+ *
+ * The options contains the 'height', 'width', and 'id_base' keys. The 'height'
+ * option is never used. The 'width' option is the width of the fully expanded
+ * control form, but try hard to use the default width. The 'id_base' is for
+ * multi-widgets (widgets which allow multiple instances such as the text
+ * widget), an id_base must be provided. The widget id will end up looking like
+ * `{$id_base}-{$unique_number}`.
+ *
+ * @since 2.2.0
+ *
+ * @todo Document `$options` as a hash notation, re: WP_Widget::__construct() cross-reference.
+ * @todo `$params` parameter?
+ *
+ * @global array $wp_registered_widget_controls
+ * @global array $wp_registered_widget_updates
+ * @global array $wp_registered_widgets
+ * @global array $_wp_deprecated_widgets_callbacks
+ *
+ * @param int|string $id Sidebar ID.
+ * @param string $name Sidebar display name.
+ * @param callable $control_callback Run when sidebar is displayed.
+ * @param array|string $options Optional. Widget options. See description above. Default empty array.
+ */
+function wp_register_widget_control( $id, $name, $control_callback, $options = array() ) {
+ global $wp_registered_widget_controls, $wp_registered_widget_updates, $wp_registered_widgets, $_wp_deprecated_widgets_callbacks;
+
+ $id = strtolower($id);
+ $id_base = _get_widget_id_base($id);
+
+ if ( empty($control_callback) ) {
+ unset($wp_registered_widget_controls[$id]);
+ unset($wp_registered_widget_updates[$id_base]);
+ return;
+ }
+
+ if ( in_array($control_callback, $_wp_deprecated_widgets_callbacks, true) && !is_callable($control_callback) ) {
+ unset( $wp_registered_widgets[ $id ] );
+ return;
+ }
+
+ if ( isset($wp_registered_widget_controls[$id]) && !did_action( 'widgets_init' ) )
+ return;
+
+ $defaults = array('width' => 250, 'height' => 200 ); // height is never used
+ $options = wp_parse_args($options, $defaults);
+ $options['width'] = (int) $options['width'];
+ $options['height'] = (int) $options['height'];
+
+ $widget = array(
+ 'name' => $name,
+ 'id' => $id,
+ 'callback' => $control_callback,
+ 'params' => array_slice(func_get_args(), 4)
+ );
+ $widget = array_merge($widget, $options);
+
+ $wp_registered_widget_controls[$id] = $widget;
+
+ if ( isset($wp_registered_widget_updates[$id_base]) )
+ return;
+
+ if ( isset($widget['params'][0]['number']) )
+ $widget['params'][0]['number'] = -1;
+
+ unset($widget['width'], $widget['height'], $widget['name'], $widget['id']);
+ $wp_registered_widget_updates[$id_base] = $widget;
+}
+
+/**
+ * @global array $wp_registered_widget_updates
+ *
+ * @param string $id_base
+ * @param callable $update_callback
+ * @param array $options
+ */
+function _register_widget_update_callback($id_base, $update_callback, $options = array()) {
+ global $wp_registered_widget_updates;
+
+ if ( isset($wp_registered_widget_updates[$id_base]) ) {
+ if ( empty($update_callback) )
+ unset($wp_registered_widget_updates[$id_base]);
+ return;
+ }
+
+ $widget = array(
+ 'callback' => $update_callback,
+ 'params' => array_slice(func_get_args(), 3)
+ );
+
+ $widget = array_merge($widget, $options);
+ $wp_registered_widget_updates[$id_base] = $widget;
+}
+
+/**
+ *
+ * @global array $wp_registered_widget_controls
+ *
+ * @param int|string $id
+ * @param string $name
+ * @param callable $form_callback
+ * @param array $options
+ */
+function _register_widget_form_callback($id, $name, $form_callback, $options = array()) {
+ global $wp_registered_widget_controls;
+
+ $id = strtolower($id);
+
+ if ( empty($form_callback) ) {
+ unset($wp_registered_widget_controls[$id]);
+ return;
+ }
+
+ if ( isset($wp_registered_widget_controls[$id]) && !did_action( 'widgets_init' ) )
+ return;
+
+ $defaults = array('width' => 250, 'height' => 200 );
+ $options = wp_parse_args($options, $defaults);
+ $options['width'] = (int) $options['width'];
+ $options['height'] = (int) $options['height'];
+
+ $widget = array(
+ 'name' => $name,
+ 'id' => $id,
+ 'callback' => $form_callback,
+ 'params' => array_slice(func_get_args(), 4)
+ );
+ $widget = array_merge($widget, $options);
+
+ $wp_registered_widget_controls[$id] = $widget;
+}
+
+/**
+ * Remove control callback for widget.
+ *
+ * @since 2.2.0
+ *
+ * @param int|string $id Widget ID.
+ */
+function wp_unregister_widget_control($id) {
+ wp_register_widget_control( $id, '', '' );
+}
+
+/**
+ * Display dynamic sidebar.
+ *
+ * By default this displays the default sidebar or 'sidebar-1'. If your theme specifies the 'id' or
+ * 'name' parameter for its registered sidebars you can pass an id or name as the $index parameter.
+ * Otherwise, you can pass in a numerical index to display the sidebar at that index.
+ *
+ * @since 2.2.0
+ *
+ * @global array $wp_registered_sidebars
+ * @global array $wp_registered_widgets
+ *
+ * @param int|string $index Optional, default is 1. Index, name or ID of dynamic sidebar.
+ * @return bool True, if widget sidebar was found and called. False if not found or not called.
+ */
+function dynamic_sidebar( $index = 1 ) {
+ global $wp_registered_sidebars, $wp_registered_widgets;
+
+ if ( is_int( $index ) ) {
+ $index = "sidebar-$index";
+ } else {
+ $sanitized_index = sanitize_title( $index );
+ foreach ( (array) $wp_registered_sidebars as $key => $value ) {
+ if ( sanitize_title( $value['name'] ) == $sanitized_index ) {
+ $index = $key;
+ break;
+ }
+ }
+ }
+
+ $sidebars_widgets = wp_get_sidebars_widgets();
+ if ( empty( $wp_registered_sidebars[ $index ] ) || empty( $sidebars_widgets[ $index ] ) || ! is_array( $sidebars_widgets[ $index ] ) ) {
+ /** This action is documented in wp-includes/widget-functions.php */
+ do_action( 'dynamic_sidebar_before', $index, false );
+ /** This action is documented in wp-includes/widget-functions.php */
+ do_action( 'dynamic_sidebar_after', $index, false );
+ /** This filter is documented in wp-includes/widget-functions.php */
+ return apply_filters( 'dynamic_sidebar_has_widgets', false, $index );
+ }
+
+ /**
+ * Fires before widgets are rendered in a dynamic sidebar.
+ *
+ * Note: The action also fires for empty sidebars, and on both the front-end
+ * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
+ *
+ * @since 3.9.0
+ *
+ * @param int|string $index Index, name, or ID of the dynamic sidebar.
+ * @param bool $has_widgets Whether the sidebar is populated with widgets.
+ * Default true.
+ */
+ do_action( 'dynamic_sidebar_before', $index, true );
+ $sidebar = $wp_registered_sidebars[$index];
+
+ $did_one = false;
+ foreach ( (array) $sidebars_widgets[$index] as $id ) {
+
+ if ( !isset($wp_registered_widgets[$id]) ) continue;
+
+ $params = array_merge(
+ array( array_merge( $sidebar, array('widget_id' => $id, 'widget_name' => $wp_registered_widgets[$id]['name']) ) ),
+ (array) $wp_registered_widgets[$id]['params']
+ );
+
+ // Substitute HTML id and class attributes into before_widget
+ $classname_ = '';
+ foreach ( (array) $wp_registered_widgets[$id]['classname'] as $cn ) {
+ if ( is_string($cn) )
+ $classname_ .= '_' . $cn;
+ elseif ( is_object($cn) )
+ $classname_ .= '_' . get_class($cn);
+ }
+ $classname_ = ltrim($classname_, '_');
+ $params[0]['before_widget'] = sprintf($params[0]['before_widget'], $id, $classname_);
+
+ /**
+ * Filter the parameters passed to a widget's display callback.
+ *
+ * Note: The filter is evaluated on both the front-end and back-end,
+ * including for the Inactive Widgets sidebar on the Widgets screen.
+ *
+ * @since 2.5.0
+ *
+ * @see register_sidebar()
+ *
+ * @param array $params {
+ * @type array $args {
+ * An array of widget display arguments.
+ *
+ * @type string $name Name of the sidebar the widget is assigned to.
+ * @type string $id ID of the sidebar the widget is assigned to.
+ * @type string $description The sidebar description.
+ * @type string $class CSS class applied to the sidebar container.
+ * @type string $before_widget HTML markup to prepend to each widget in the sidebar.
+ * @type string $after_widget HTML markup to append to each widget in the sidebar.
+ * @type string $before_title HTML markup to prepend to the widget title when displayed.
+ * @type string $after_title HTML markup to append to the widget title when displayed.
+ * @type string $widget_id ID of the widget.
+ * @type string $widget_name Name of the widget.
+ * }
+ * @type array $widget_args {
+ * An array of multi-widget arguments.
+ *
+ * @type int $number Number increment used for multiples of the same widget.
+ * }
+ * }
+ */
+ $params = apply_filters( 'dynamic_sidebar_params', $params );
+
+ $callback = $wp_registered_widgets[$id]['callback'];
+
+ /**
+ * Fires before a widget's display callback is called.
+ *
+ * Note: The action fires on both the front-end and back-end, including
+ * for widgets in the Inactive Widgets sidebar on the Widgets screen.
+ *
+ * The action is not fired for empty sidebars.
+ *
+ * @since 3.0.0
+ *
+ * @param array $widget_id {
+ * An associative array of widget arguments.
+ *
+ * @type string $name Name of the widget.
+ * @type string $id Widget ID.
+ * @type array|callable $callback When the hook is fired on the front-end, $callback is an array
+ * containing the widget object. Fired on the back-end, $callback
+ * is 'wp_widget_control', see $_callback.
+ * @type array $params An associative array of multi-widget arguments.
+ * @type string $classname CSS class applied to the widget container.
+ * @type string $description The widget description.
+ * @type array $_callback When the hook is fired on the back-end, $_callback is populated
+ * with an array containing the widget object, see $callback.
+ * }
+ */
+ do_action( 'dynamic_sidebar', $wp_registered_widgets[ $id ] );
+
+ if ( is_callable($callback) ) {
+ call_user_func_array($callback, $params);
+ $did_one = true;
+ }
+ }
+
+ /**
+ * Fires after widgets are rendered in a dynamic sidebar.
+ *
+ * Note: The action also fires for empty sidebars, and on both the front-end
+ * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
+ *
+ * @since 3.9.0
+ *
+ * @param int|string $index Index, name, or ID of the dynamic sidebar.
+ * @param bool $has_widgets Whether the sidebar is populated with widgets.
+ * Default true.
+ */
+ do_action( 'dynamic_sidebar_after', $index, true );
+
+ /**
+ * Filter whether a sidebar has widgets.
+ *
+ * Note: The filter is also evaluated for empty sidebars, and on both the front-end
+ * and back-end, including the Inactive Widgets sidebar on the Widgets screen.
+ *
+ * @since 3.9.0
+ *
+ * @param bool $did_one Whether at least one widget was rendered in the sidebar.
+ * Default false.
+ * @param int|string $index Index, name, or ID of the dynamic sidebar.
+ */
+ return apply_filters( 'dynamic_sidebar_has_widgets', $did_one, $index );
+}
+
+/**
+ * Whether widget is displayed on the front-end.
+ *
+ * Either $callback or $id_base can be used
+ * $id_base is the first argument when extending WP_Widget class
+ * Without the optional $widget_id parameter, returns the ID of the first sidebar
+ * in which the first instance of the widget with the given callback or $id_base is found.
+ * With the $widget_id parameter, returns the ID of the sidebar where
+ * the widget with that callback/$id_base AND that ID is found.
+ *
+ * NOTE: $widget_id and $id_base are the same for single widgets. To be effective
+ * this function has to run after widgets have initialized, at action 'init' or later.
+ *
+ * @since 2.2.0
+ *
+ * @global array $wp_registered_widgets
+ *
+ * @param string $callback Optional, Widget callback to check.
+ * @param int $widget_id Optional, but needed for checking. Widget ID.
+ * @param string $id_base Optional, the base ID of a widget created by extending WP_Widget.
+ * @param bool $skip_inactive Optional, whether to check in 'wp_inactive_widgets'.
+ * @return string|false False if widget is not active or id of sidebar in which the widget is active.
+ */
+function is_active_widget($callback = false, $widget_id = false, $id_base = false, $skip_inactive = true) {
+ global $wp_registered_widgets;
+
+ $sidebars_widgets = wp_get_sidebars_widgets();
+
+ if ( is_array($sidebars_widgets) ) {
+ foreach ( $sidebars_widgets as $sidebar => $widgets ) {
+ if ( $skip_inactive && ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) ) {
+ continue;
+ }
+
+ if ( is_array($widgets) ) {
+ foreach ( $widgets as $widget ) {
+ if ( ( $callback && isset($wp_registered_widgets[$widget]['callback']) && $wp_registered_widgets[$widget]['callback'] == $callback ) || ( $id_base && _get_widget_id_base($widget) == $id_base ) ) {
+ if ( !$widget_id || $widget_id == $wp_registered_widgets[$widget]['id'] )
+ return $sidebar;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * Whether the dynamic sidebar is enabled and used by theme.
+ *
+ * @since 2.2.0
+ *
+ * @global array $wp_registered_widgets
+ * @global array $wp_registered_sidebars
+ *
+ * @return bool True, if using widgets. False, if not using widgets.
+ */
+function is_dynamic_sidebar() {
+ global $wp_registered_widgets, $wp_registered_sidebars;
+ $sidebars_widgets = get_option('sidebars_widgets');
+ foreach ( (array) $wp_registered_sidebars as $index => $sidebar ) {
+ if ( count($sidebars_widgets[$index]) ) {
+ foreach ( (array) $sidebars_widgets[$index] as $widget )
+ if ( array_key_exists($widget, $wp_registered_widgets) )
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Whether a sidebar is in use.
+ *
+ * @since 2.8.0
+ *
+ * @param string|int $index Sidebar name, id or number to check.
+ * @return bool true if the sidebar is in use, false otherwise.
+ */
+function is_active_sidebar( $index ) {
+ $index = ( is_int($index) ) ? "sidebar-$index" : sanitize_title($index);
+ $sidebars_widgets = wp_get_sidebars_widgets();
+ $is_active_sidebar = ! empty( $sidebars_widgets[$index] );
+
+ /**
+ * Filter whether a dynamic sidebar is considered "active".
+ *
+ * @since 3.9.0
+ *
+ * @param bool $is_active_sidebar Whether or not the sidebar should be considered "active".
+ * In other words, whether the sidebar contains any widgets.
+ * @param int|string $index Index, name, or ID of the dynamic sidebar.
+ */
+ return apply_filters( 'is_active_sidebar', $is_active_sidebar, $index );
+}
+
+//
+// Internal Functions
+//
+
+/**
+ * Retrieve full list of sidebars and their widget instance IDs.
+ *
+ * Will upgrade sidebar widget list, if needed. Will also save updated list, if
+ * needed.
+ *
+ * @since 2.2.0
+ * @access private
+ *
+ * @global array $_wp_sidebars_widgets
+ * @global array $sidebars_widgets
+ *
+ * @param bool $deprecated Not used (argument deprecated).
+ * @return array Upgraded list of widgets to version 3 array format when called from the admin.
+ */
+function wp_get_sidebars_widgets( $deprecated = true ) {
+ if ( $deprecated !== true )
+ _deprecated_argument( __FUNCTION__, '2.8.1' );
+
+ global $_wp_sidebars_widgets, $sidebars_widgets;
+
+ // If loading from front page, consult $_wp_sidebars_widgets rather than options
+ // to see if wp_convert_widget_settings() has made manipulations in memory.
+ if ( !is_admin() ) {
+ if ( empty($_wp_sidebars_widgets) )
+ $_wp_sidebars_widgets = get_option('sidebars_widgets', array());
+
+ $sidebars_widgets = $_wp_sidebars_widgets;
+ } else {
+ $sidebars_widgets = get_option('sidebars_widgets', array());
+ }
+
+ if ( is_array( $sidebars_widgets ) && isset($sidebars_widgets['array_version']) )
+ unset($sidebars_widgets['array_version']);
+
+ /**
+ * Filter the list of sidebars and their widgets.
+ *
+ * @since 2.7.0
+ *
+ * @param array $sidebars_widgets An associative array of sidebars and their widgets.
+ */
+ return apply_filters( 'sidebars_widgets', $sidebars_widgets );
+}
+
+/**
+ * Set the sidebar widget option to update sidebars.
+ *
+ * @since 2.2.0
+ * @access private
+ *
+ * @param array $sidebars_widgets Sidebar widgets and their settings.
+ */
+function wp_set_sidebars_widgets( $sidebars_widgets ) {
+ if ( !isset( $sidebars_widgets['array_version'] ) )
+ $sidebars_widgets['array_version'] = 3;
+ update_option( 'sidebars_widgets', $sidebars_widgets );
+}
+
+/**
+ * Retrieve default registered sidebars list.
+ *
+ * @since 2.2.0
+ * @access private
+ *
+ * @global array $wp_registered_sidebars
+ *
+ * @return array
+ */
+function wp_get_widget_defaults() {
+ global $wp_registered_sidebars;
+
+ $defaults = array();
+
+ foreach ( (array) $wp_registered_sidebars as $index => $sidebar )
+ $defaults[$index] = array();
+
+ return $defaults;
+}
+
+/**
+ * Convert the widget settings from single to multi-widget format.
+ *
+ * @since 2.8.0
+ *
+ * @global array $_wp_sidebars_widgets
+ *
+ * @param string $base_name
+ * @param string $option_name
+ * @param array $settings
+ * @return array
+ */
+function wp_convert_widget_settings($base_name, $option_name, $settings) {
+ // This test may need expanding.
+ $single = $changed = false;
+ if ( empty($settings) ) {
+ $single = true;
+ } else {
+ foreach ( array_keys($settings) as $number ) {
+ if ( 'number' == $number )
+ continue;
+ if ( !is_numeric($number) ) {
+ $single = true;
+ break;
+ }
+ }
+ }
+
+ if ( $single ) {
+ $settings = array( 2 => $settings );
+
+ // If loading from the front page, update sidebar in memory but don't save to options
+ if ( is_admin() ) {
+ $sidebars_widgets = get_option('sidebars_widgets');
+ } else {
+ if ( empty($GLOBALS['_wp_sidebars_widgets']) )
+ $GLOBALS['_wp_sidebars_widgets'] = get_option('sidebars_widgets', array());
+ $sidebars_widgets = &$GLOBALS['_wp_sidebars_widgets'];
+ }
+
+ foreach ( (array) $sidebars_widgets as $index => $sidebar ) {
+ if ( is_array($sidebar) ) {
+ foreach ( $sidebar as $i => $name ) {
+ if ( $base_name == $name ) {
+ $sidebars_widgets[$index][$i] = "$name-2";
+ $changed = true;
+ break 2;
+ }
+ }
+ }
+ }
+
+ if ( is_admin() && $changed )
+ update_option('sidebars_widgets', $sidebars_widgets);
+ }
+
+ $settings['_multiwidget'] = 1;
+ if ( is_admin() )
+ update_option( $option_name, $settings );
+
+ return $settings;
+}
+
+/**
+ * Output an arbitrary widget as a template tag.
+ *
+ * @since 2.8.0
+ *
+ * @global WP_Widget_Factory $wp_widget_factory
+ *
+ * @param string $widget The widget's PHP class name (see class-wp-widget.php).
+ * @param array $instance Optional. The widget's instance settings. Default empty array.
+ * @param array $args {
+ * Optional. Array of arguments to configure the display of the widget.
+ *
+ * @type string $before_widget HTML content that will be prepended to the widget's HTML output.
+ * Default `<div class="widget %s">`, where `%s` is the widget's class name.
+ * @type string $after_widget HTML content that will be appended to the widget's HTML output.
+ * Default `</div>`.
+ * @type string $before_title HTML content that will be prepended to the widget's title when displayed.
+ * Default `<h2 class="widgettitle">`.
+ * @type string $after_title HTML content that will be appended to the widget's title when displayed.
+ * Default `</h2>`.
+ * }
+ */
+function the_widget( $widget, $instance = array(), $args = array() ) {
+ global $wp_widget_factory;
+
+ $widget_obj = $wp_widget_factory->widgets[$widget];
+ if ( ! ( $widget_obj instanceof WP_Widget ) ) {
+ return;
+ }
+
+ $default_args = array(
+ 'before_widget' => '<div class="widget %s">',
+ 'after_widget' => "</div>",
+ 'before_title' => '<h2 class="widgettitle">',
+ 'after_title' => '</h2>',
+ );
+ $args = wp_parse_args( $args, $default_args );
+ $args['before_widget'] = sprintf( $args['before_widget'], $widget_obj->widget_options['classname'] );
+
+ $instance = wp_parse_args($instance);
+
+ /**
+ * Fires before rendering the requested widget.
+ *
+ * @since 3.0.0
+ *
+ * @param string $widget The widget's class name.
+ * @param array $instance The current widget instance's settings.
+ * @param array $args An array of the widget's sidebar arguments.
+ */
+ do_action( 'the_widget', $widget, $instance, $args );
+
+ $widget_obj->_set(-1);
+ $widget_obj->widget($args, $instance);
+}
+
+/**
+ * Private
+ *
+ * @return string
+ */
+function _get_widget_id_base($id) {
+ return preg_replace( '/-[0-9]+$/', '', $id );
+}
+
+/**
+ * Handle sidebars config after theme change
+ *
+ * @access private
+ * @since 3.3.0
+ *
+ * @global array $sidebars_widgets
+ */
+function _wp_sidebars_changed() {
+ global $sidebars_widgets;
+
+ if ( ! is_array( $sidebars_widgets ) )
+ $sidebars_widgets = wp_get_sidebars_widgets();
+
+ retrieve_widgets(true);
+}
+
+/**
+ * Look for "lost" widgets, this has to run at least on each theme change.
+ *
+ * @since 2.8.0
+ *
+ * @global array $wp_registered_sidebars
+ * @global array $sidebars_widgets
+ * @global array $wp_registered_widgets
+ *
+ * @param string|bool $theme_changed Whether the theme was changed as a boolean. A value
+ * of 'customize' defers updates for the Customizer.
+ * @return array|void
+ */
+function retrieve_widgets( $theme_changed = false ) {
+ global $wp_registered_sidebars, $sidebars_widgets, $wp_registered_widgets;
+
+ $registered_sidebar_keys = array_keys( $wp_registered_sidebars );
+ $orphaned = 0;
+
+ $old_sidebars_widgets = get_theme_mod( 'sidebars_widgets' );
+ if ( is_array( $old_sidebars_widgets ) ) {
+ // time() that sidebars were stored is in $old_sidebars_widgets['time']
+ $_sidebars_widgets = $old_sidebars_widgets['data'];
+
+ if ( 'customize' !== $theme_changed ) {
+ remove_theme_mod( 'sidebars_widgets' );
+ }
+
+ foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
+ if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) {
+ continue;
+ }
+
+ if ( !in_array( $sidebar, $registered_sidebar_keys ) ) {
+ $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $widgets;
+ unset( $_sidebars_widgets[$sidebar] );
+ }
+ }
+ } else {
+ if ( empty( $sidebars_widgets ) )
+ return;
+
+ unset( $sidebars_widgets['array_version'] );
+
+ $old = array_keys($sidebars_widgets);
+ sort($old);
+ sort($registered_sidebar_keys);
+
+ if ( $old == $registered_sidebar_keys )
+ return;
+
+ $_sidebars_widgets = array(
+ 'wp_inactive_widgets' => !empty( $sidebars_widgets['wp_inactive_widgets'] ) ? $sidebars_widgets['wp_inactive_widgets'] : array()
+ );
+
+ unset( $sidebars_widgets['wp_inactive_widgets'] );
+
+ foreach ( $wp_registered_sidebars as $id => $settings ) {
+ if ( $theme_changed ) {
+ $_sidebars_widgets[$id] = array_shift( $sidebars_widgets );
+ } else {
+ // no theme change, grab only sidebars that are currently registered
+ if ( isset( $sidebars_widgets[$id] ) ) {
+ $_sidebars_widgets[$id] = $sidebars_widgets[$id];
+ unset( $sidebars_widgets[$id] );
+ }
+ }
+ }
+
+ foreach ( $sidebars_widgets as $val ) {
+ if ( is_array($val) && ! empty( $val ) )
+ $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $val;
+ }
+ }
+
+ // discard invalid, theme-specific widgets from sidebars
+ $shown_widgets = array();
+
+ foreach ( $_sidebars_widgets as $sidebar => $widgets ) {
+ if ( !is_array($widgets) )
+ continue;
+
+ $_widgets = array();
+ foreach ( $widgets as $widget ) {
+ if ( isset($wp_registered_widgets[$widget]) )
+ $_widgets[] = $widget;
+ }
+
+ $_sidebars_widgets[$sidebar] = $_widgets;
+ $shown_widgets = array_merge($shown_widgets, $_widgets);
+ }
+
+ $sidebars_widgets = $_sidebars_widgets;
+ unset($_sidebars_widgets, $_widgets);
+
+ // find hidden/lost multi-widget instances
+ $lost_widgets = array();
+ foreach ( $wp_registered_widgets as $key => $val ) {
+ if ( in_array($key, $shown_widgets, true) )
+ continue;
+
+ $number = preg_replace('/.+?-([0-9]+)$/', '$1', $key);
+
+ if ( 2 > (int) $number )
+ continue;
+
+ $lost_widgets[] = $key;
+ }
+
+ $sidebars_widgets['wp_inactive_widgets'] = array_merge($lost_widgets, (array) $sidebars_widgets['wp_inactive_widgets']);
+ if ( 'customize' !== $theme_changed ) {
+ wp_set_sidebars_widgets( $sidebars_widgets );
+ }
+
+ return $sidebars_widgets;
+}
+
+/**
+ * Display the RSS entries in a list.
+ *
+ * @since 2.5.0
+ *
+ * @param string|array|object $rss RSS url.
+ * @param array $args Widget arguments.
+ */
+function wp_widget_rss_output( $rss, $args = array() ) {
+ if ( is_string( $rss ) ) {
+ $rss = fetch_feed($rss);
+ } elseif ( is_array($rss) && isset($rss['url']) ) {
+ $args = $rss;
+ $rss = fetch_feed($rss['url']);
+ } elseif ( !is_object($rss) ) {
+ return;
+ }
+
+ if ( is_wp_error($rss) ) {
+ if ( is_admin() || current_user_can('manage_options') )
+ echo '<p>' . sprintf( __('<strong>RSS Error</strong>: %s'), $rss->get_error_message() ) . '</p>';
+ return;
+ }
+
+ $default_args = array( 'show_author' => 0, 'show_date' => 0, 'show_summary' => 0, 'items' => 0 );
+ $args = wp_parse_args( $args, $default_args );
+
+ $items = (int) $args['items'];
+ if ( $items < 1 || 20 < $items )
+ $items = 10;
+ $show_summary = (int) $args['show_summary'];
+ $show_author = (int) $args['show_author'];
+ $show_date = (int) $args['show_date'];
+
+ if ( !$rss->get_item_quantity() ) {
+ echo '<ul><li>' . __( 'An error has occurred, which probably means the feed is down. Try again later.' ) . '</li></ul>';
+ $rss->__destruct();
+ unset($rss);
+ return;
+ }
+
+ echo '<ul>';
+ foreach ( $rss->get_items( 0, $items ) as $item ) {
+ $link = $item->get_link();
+ while ( stristr( $link, 'http' ) != $link ) {
+ $link = substr( $link, 1 );
+ }
+ $link = esc_url( strip_tags( $link ) );
+
+ $title = esc_html( trim( strip_tags( $item->get_title() ) ) );
+ if ( empty( $title ) ) {
+ $title = __( 'Untitled' );
+ }
+
+ $desc = @html_entity_decode( $item->get_description(), ENT_QUOTES, get_option( 'blog_charset' ) );
+ $desc = esc_attr( wp_trim_words( $desc, 55, ' […]' ) );
+
+ $summary = '';
+ if ( $show_summary ) {
+ $summary = $desc;
+
+ // Change existing [...] to […].
+ if ( '[...]' == substr( $summary, -5 ) ) {
+ $summary = substr( $summary, 0, -5 ) . '[…]';
+ }
+
+ $summary = '<div class="rssSummary">' . esc_html( $summary ) . '</div>';
+ }
+
+ $date = '';
+ if ( $show_date ) {
+ $date = $item->get_date( 'U' );
+
+ if ( $date ) {
+ $date = ' <span class="rss-date">' . date_i18n( get_option( 'date_format' ), $date ) . '</span>';
+ }
+ }
+
+ $author = '';
+ if ( $show_author ) {
+ $author = $item->get_author();
+ if ( is_object($author) ) {
+ $author = $author->get_name();
+ $author = ' <cite>' . esc_html( strip_tags( $author ) ) . '</cite>';
+ }
+ }
+
+ if ( $link == '' ) {
+ echo "<li>$title{$date}{$summary}{$author}</li>";
+ } elseif ( $show_summary ) {
+ echo "<li><a class='rsswidget' href='$link'>$title</a>{$date}{$summary}{$author}</li>";
+ } else {
+ echo "<li><a class='rsswidget' href='$link'>$title</a>{$date}{$author}</li>";
+ }
+ }
+ echo '</ul>';
+ $rss->__destruct();
+ unset($rss);
+}
+
+/**
+ * Display RSS widget options form.
+ *
+ * The options for what fields are displayed for the RSS form are all booleans
+ * and are as follows: 'url', 'title', 'items', 'show_summary', 'show_author',
+ * 'show_date'.
+ *
+ * @since 2.5.0
+ *
+ * @param array|string $args Values for input fields.
+ * @param array $inputs Override default display options.
+ */
+function wp_widget_rss_form( $args, $inputs = null ) {
+ $default_inputs = array( 'url' => true, 'title' => true, 'items' => true, 'show_summary' => true, 'show_author' => true, 'show_date' => true );
+ $inputs = wp_parse_args( $inputs, $default_inputs );
+
+ $args['title'] = isset( $args['title'] ) ? $args['title'] : '';
+ $args['url'] = isset( $args['url'] ) ? $args['url'] : '';
+ $args['items'] = isset( $args['items'] ) ? (int) $args['items'] : 0;
+
+ if ( $args['items'] < 1 || 20 < $args['items'] ) {
+ $args['items'] = 10;
+ }
+
+ $args['show_summary'] = isset( $args['show_summary'] ) ? (int) $args['show_summary'] : (int) $inputs['show_summary'];
+ $args['show_author'] = isset( $args['show_author'] ) ? (int) $args['show_author'] : (int) $inputs['show_author'];
+ $args['show_date'] = isset( $args['show_date'] ) ? (int) $args['show_date'] : (int) $inputs['show_date'];
+
+ if ( ! empty( $args['error'] ) ) {
+ echo '<p class="widget-error"><strong>' . sprintf( __( 'RSS Error: %s' ), $args['error'] ) . '</strong></p>';
+ }
+
+ $esc_number = esc_attr( $args['number'] );
+ if ( $inputs['url'] ) :
+?>
+ <p><label for="rss-url-<?php echo $esc_number; ?>"><?php _e( 'Enter the RSS feed URL here:' ); ?></label>
+ <input class="widefat" id="rss-url-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][url]" type="text" value="<?php echo esc_url( $args['url'] ); ?>" /></p>
+<?php endif; if ( $inputs['title'] ) : ?>
+ <p><label for="rss-title-<?php echo $esc_number; ?>"><?php _e( 'Give the feed a title (optional):' ); ?></label>
+ <input class="widefat" id="rss-title-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][title]" type="text" value="<?php echo esc_attr( $args['title'] ); ?>" /></p>
+<?php endif; if ( $inputs['items'] ) : ?>
+ <p><label for="rss-items-<?php echo $esc_number; ?>"><?php _e( 'How many items would you like to display?' ); ?></label>
+ <select id="rss-items-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][items]">
+ <?php
+ for ( $i = 1; $i <= 20; ++$i ) {
+ echo "<option value='$i' " . selected( $args['items'], $i, false ) . ">$i</option>";
+ }
+ ?>
+ </select></p>
+<?php endif; if ( $inputs['show_summary'] ) : ?>
+ <p><input id="rss-show-summary-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][show_summary]" type="checkbox" value="1" <?php checked( $args['show_summary'] ); ?> />
+ <label for="rss-show-summary-<?php echo $esc_number; ?>"><?php _e( 'Display item content?' ); ?></label></p>
+<?php endif; if ( $inputs['show_author'] ) : ?>
+ <p><input id="rss-show-author-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][show_author]" type="checkbox" value="1" <?php checked( $args['show_author'] ); ?> />
+ <label for="rss-show-author-<?php echo $esc_number; ?>"><?php _e( 'Display item author if available?' ); ?></label></p>
+<?php endif; if ( $inputs['show_date'] ) : ?>
+ <p><input id="rss-show-date-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][show_date]" type="checkbox" value="1" <?php checked( $args['show_date'] ); ?>/>
+ <label for="rss-show-date-<?php echo $esc_number; ?>"><?php _e( 'Display item date?' ); ?></label></p>
+<?php
+ endif;
+ foreach ( array_keys($default_inputs) as $input ) :
+ if ( 'hidden' === $inputs[$input] ) :
+ $id = str_replace( '_', '-', $input );
+?>
+ <input type="hidden" id="rss-<?php echo esc_attr( $id ); ?>-<?php echo $esc_number; ?>" name="widget-rss[<?php echo $esc_number; ?>][<?php echo esc_attr( $input ); ?>]" value="<?php echo esc_attr( $args[ $input ] ); ?>" />
+<?php
+ endif;
+ endforeach;
+}
+
+/**
+ * Process RSS feed widget data and optionally retrieve feed items.
+ *
+ * The feed widget can not have more than 20 items or it will reset back to the
+ * default, which is 10.
+ *
+ * The resulting array has the feed title, feed url, feed link (from channel),
+ * feed items, error (if any), and whether to show summary, author, and date.
+ * All respectively in the order of the array elements.
+ *
+ * @since 2.5.0
+ *
+ * @param array $widget_rss RSS widget feed data. Expects unescaped data.
+ * @param bool $check_feed Optional, default is true. Whether to check feed for errors.
+ * @return array
+ */
+function wp_widget_rss_process( $widget_rss, $check_feed = true ) {
+ $items = (int) $widget_rss['items'];
+ if ( $items < 1 || 20 < $items )
+ $items = 10;
+ $url = esc_url_raw( strip_tags( $widget_rss['url'] ) );
+ $title = isset( $widget_rss['title'] ) ? trim( strip_tags( $widget_rss['title'] ) ) : '';
+ $show_summary = isset( $widget_rss['show_summary'] ) ? (int) $widget_rss['show_summary'] : 0;
+ $show_author = isset( $widget_rss['show_author'] ) ? (int) $widget_rss['show_author'] :0;
+ $show_date = isset( $widget_rss['show_date'] ) ? (int) $widget_rss['show_date'] : 0;
+
+ if ( $check_feed ) {
+ $rss = fetch_feed($url);
+ $error = false;
+ $link = '';
+ if ( is_wp_error($rss) ) {
+ $error = $rss->get_error_message();
+ } else {
+ $link = esc_url(strip_tags($rss->get_permalink()));
+ while ( stristr($link, 'http') != $link )
+ $link = substr($link, 1);
+
+ $rss->__destruct();
+ unset($rss);
+ }
+ }
+
+ return compact( 'title', 'url', 'link', 'items', 'error', 'show_summary', 'show_author', 'show_date' );
+}
+
+/**
+ * Register all of the default WordPress widgets on startup.
+ *
+ * Calls 'widgets_init' action after all of the WordPress widgets have been
+ * registered.
+ *
+ * @since 2.2.0
+ */
+function wp_widgets_init() {
+ if ( !is_blog_installed() )
+ return;
+
+ register_widget('WP_Widget_Pages');
+
+ register_widget('WP_Widget_Calendar');
+
+ register_widget('WP_Widget_Archives');
+
+ if ( get_option( 'link_manager_enabled' ) )
+ register_widget('WP_Widget_Links');
+
+ register_widget('WP_Widget_Meta');
+
+ register_widget('WP_Widget_Search');
+
+ register_widget('WP_Widget_Text');
+
+ register_widget('WP_Widget_Categories');
+
+ register_widget('WP_Widget_Recent_Posts');
+
+ register_widget('WP_Widget_Recent_Comments');
+
+ register_widget('WP_Widget_RSS');
+
+ register_widget('WP_Widget_Tag_Cloud');
+
+ register_widget('WP_Nav_Menu_Widget');
+
+ /**
+ * Fires after all default WordPress widgets have been registered.
+ *
+ * @since 2.2.0
+ */
+ do_action( 'widgets_init' );
+}
</ins></span></pre></div>
<a id="trunksrcwpsettingsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-settings.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-settings.php 2015-11-20 06:15:34 UTC (rev 35717)
+++ trunk/src/wp-settings.php 2015-11-20 07:23:04 UTC (rev 35718)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -122,27 +122,41 @@
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/class-wp-ajax-response.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/formatting.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/capabilities.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/class-wp-roles.php' );
+require( ABSPATH . WPINC . '/class-wp-role.php' );
+require( ABSPATH . WPINC . '/class-wp-user.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/query.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/date.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/theme.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/class-wp-theme.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/template.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/user.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/class-wp-user-query.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/session.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/meta.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/class-wp-meta-query.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/general-template.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/link-template.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/author-template.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/post.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/class-walker-page.php' );
+require( ABSPATH . WPINC . '/class-walker-page-dropdown.php' );
+require( ABSPATH . WPINC . '/class-wp-post.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/post-template.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/revision.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/post-formats.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/post-thumbnail-template.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/category.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/class-walker-category.php' );
+require( ABSPATH . WPINC . '/class-walker-category-dropdown.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/category-template.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/comment.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/class-wp-comment.php' );
+require( ABSPATH . WPINC . '/class-wp-comment-query.php' );
+require( ABSPATH . WPINC . '/class-walker-comment.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/comment-template.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rewrite.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/class-wp-rewrite.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/feed.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/bookmark.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/bookmark-template.php' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -151,19 +165,33 @@
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/deprecated.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/script-loader.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/taxonomy.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/class-wp-term.php' );
+require( ABSPATH . WPINC . '/class-wp-tax-query.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/update.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/canonical.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/shortcodes.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/embed.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/class-wp-embed.php' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-require( ABSPATH . WPINC . '/embed-functions.php' );
</del><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/class-wp-oembed-controller.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/media.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/http.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/class-http.php' );
+require( ABSPATH . WPINC . '/class-wp-http-streams.php' );
+require( ABSPATH . WPINC . '/class-wp-http-curl.php' );
+require( ABSPATH . WPINC . '/class-wp-http-proxy.php' );
+require( ABSPATH . WPINC . '/class-wp-http-cookie.php' );
+require( ABSPATH . WPINC . '/class-wp-http-encoding.php' );
+require( ABSPATH . WPINC . '/class-wp-http-response.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/widgets.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/class-wp-widget.php' );
+require( ABSPATH . WPINC . '/class-wp-widget-factory.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/nav-menu.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/nav-menu-template.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/admin-bar.php' );
</span><span class="cx" style="display: block; padding: 0 10px"> require( ABSPATH . WPINC . '/rest-api.php' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require( ABSPATH . WPINC . '/rest-api/class-wp-rest-server.php' );
+require( ABSPATH . WPINC . '/rest-api/class-wp-rest-response.php' );
+require( ABSPATH . WPINC . '/rest-api/class-wp-rest-request.php' );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // Load multisite-specific files.
</span><span class="cx" style="display: block; padding: 0 10px"> if ( is_multisite() ) {
</span></span></pre>
</div>
</div>
</body>
</html>