/**
* 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 {@see '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 );
}
/**
* Removes an existing rewrite tag (like %postname%).
*
* @since 4.5.0
*
* @global WP_Rewrite $wp_rewrite WordPress rewrite component.
*
* @param string $tag Name of the rewrite tag.
*/
function remove_rewrite_tag( $tag ) {
global $wp_rewrite;
$wp_rewrite->remove_rewrite_tag( $tag );
}
/**
* Add permalink structure.
*
* @since 3.0.0
*
* @see WP_Rewrite::add_permastruct()
* @global WP_Rewrite $wp_rewrite WordPress rewrite component.
*
* @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;
// Back-compat 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 );
}
/**
* Removes a permalink structure.
*
* Can only be used to remove permastructs that were added using add_permastruct().
* Built-in permastructs cannot be removed.
*
* @since 4.5.0
*
* @see WP_Rewrite::remove_permastruct()
* @global WP_Rewrite $wp_rewrite WordPress rewrite component.
*
* @param string $name Name for permalink structure.
*/
function remove_permastruct( $name ) {
global $wp_rewrite;
$wp_rewrite->remove_permastruct( $name );
}
/**
* 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 );
}
/**
* Filters 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 ( $postname_index && '%year%' === $permastructs[ $postname_index - 1 ] && ( isset( $query_vars['monthnum'] ) || isset( $query_vars['day'] ) ) ) {
$compare = 'monthnum';
} elseif ( $postname_index && '%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, '' ) + 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;
/**
* Filters 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 );
$url_host = str_replace( 'www.', '', parse_url( $url, PHP_URL_HOST ) );
$home_url_host = str_replace( 'www.', '', parse_url( home_url(), PHP_URL_HOST ) );
// Bail early if the URL does not belong to this site.
if ( $url_host && $url_host !== $home_url_host ) {
return 0;
}
// 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;
}
}
// 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.
$scheme = parse_url( home_url(), PHP_URL_SCHEME );
$url = set_url_scheme( $url, $scheme );
// 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 );
}
if ( trim( $url, '/' ) === home_url() && 'page' == get_option( 'show_on_front' ) ) {
$page_on_front = get_option( 'page_on_front' );
if ( $page_on_front && get_post( $page_on_front ) instanceof WP_Post ) {
return (int) $page_on_front;
}
}
// 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;
}
// 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;
}
/**
* Link/Bookmark API
*
* @package WordPress
* @subpackage Bookmark
*/
/**
* Retrieve Bookmark data
*
* @since 2.1.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param int|stdClass $bookmark
* @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which correspond to
* an stdClass object, an associative array, or a numeric array, respectively. Default OBJECT.
* @param string $filter Optional, default is 'raw'.
* @return array|object|null Type returned depends on $output value.
*/
function get_bookmark( $bookmark, $output = OBJECT, $filter = 'raw' ) {
global $wpdb;
if ( empty( $bookmark ) ) {
if ( isset( $GLOBALS['link'] ) ) {
$_bookmark = & $GLOBALS['link'];
} else {
$_bookmark = null;
}
} elseif ( is_object( $bookmark ) ) {
wp_cache_add( $bookmark->link_id, $bookmark, 'bookmark' );
$_bookmark = $bookmark;
} else {
if ( isset( $GLOBALS['link'] ) && ( $GLOBALS['link']->link_id == $bookmark ) ) {
$_bookmark = & $GLOBALS['link'];
} elseif ( ! $_bookmark = wp_cache_get( $bookmark, 'bookmark' ) ) {
$_bookmark = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->links WHERE link_id = %d LIMIT 1", $bookmark ) );
if ( $_bookmark ) {
$_bookmark->link_category = array_unique( wp_get_object_terms( $_bookmark->link_id, 'link_category', array( 'fields' => 'ids' ) ) );
wp_cache_add( $_bookmark->link_id, $_bookmark, 'bookmark' );
}
}
}
if ( ! $_bookmark ) {
return $_bookmark;
}
$_bookmark = sanitize_bookmark( $_bookmark, $filter );
if ( $output == OBJECT ) {
return $_bookmark;
} elseif ( $output == ARRAY_A ) {
return get_object_vars( $_bookmark );
} elseif ( $output == ARRAY_N ) {
return array_values( get_object_vars( $_bookmark ) );
} else {
return $_bookmark;
}
}
/**
* Retrieve single bookmark data item or field.
*
* @since 2.3.0
*
* @param string $field The name of the data field to return
* @param int $bookmark The bookmark ID to get field
* @param string $context Optional. The context of how the field will be used.
* @return string|WP_Error
*/
function get_bookmark_field( $field, $bookmark, $context = 'display' ) {
$bookmark = (int) $bookmark;
$bookmark = get_bookmark( $bookmark );
if ( is_wp_error( $bookmark ) ) {
return $bookmark;
}
if ( ! is_object( $bookmark ) ) {
return '';
}
if ( ! isset( $bookmark->$field ) ) {
return '';
}
return sanitize_bookmark_field( $field, $bookmark->$field, $bookmark->link_id, $context );
}
/**
* Retrieves the list of bookmarks
*
* Attempts to retrieve from the cache first based on MD5 hash of arguments. If
* that fails, then the query will be built from the arguments and executed. The
* results will be stored to the cache.
*
* @since 2.1.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param string|array $args {
* Optional. String or array of arguments to retrieve bookmarks.
*
* @type string $orderby How to order the links by. Accepts post fields. Default 'name'.
* @type string $order Whether to order bookmarks in ascending or descending order.
* Accepts 'ASC' (ascending) or 'DESC' (descending). Default 'ASC'.
* @type int $limit Amount of bookmarks to display. Accepts 1+ or -1 for all.
* Default -1.
* @type string $category Comma-separated list of category ids to include links from.
* Default empty.
* @type string $category_name Category to retrieve links for by name. Default empty.
* @type int|bool $hide_invisible Whether to show or hide links marked as 'invisible'. Accepts
* 1|true or 0|false. Default 1|true.
* @type int|bool $show_updated Whether to display the time the bookmark was last updated.
* Accepts 1|true or 0|false. Default 0|false.
* @type string $include Comma-separated list of bookmark IDs to include. Default empty.
* @type string $exclude Comma-separated list of bookmark IDs to exclude. Default empty.
* }
* @return array List of bookmark row objects.
*/
function get_bookmarks( $args = '' ) {
global $wpdb;
$defaults = array(
'orderby' => 'name',
'order' => 'ASC',
'limit' => -1,
'category' => '',
'category_name' => '',
'hide_invisible' => 1,
'show_updated' => 0,
'include' => '',
'exclude' => '',
'search' => '',
);
$r = wp_parse_args( $args, $defaults );
$key = md5( serialize( $r ) );
$cache = false;
if ( 'rand' !== $r['orderby'] && $cache = wp_cache_get( 'get_bookmarks', 'bookmark' ) ) {
if ( is_array( $cache ) && isset( $cache[ $key ] ) ) {
$bookmarks = $cache[ $key ];
/**
* Filters the returned list of bookmarks.
*
* The first time the hook is evaluated in this file, it returns the cached
* bookmarks list. The second evaluation returns a cached bookmarks list if the
* link category is passed but does not exist. The third evaluation returns
* the full cached results.
*
* @since 2.1.0
*
* @see get_bookmarks()
*
* @param array $bookmarks List of the cached bookmarks.
* @param array $r An array of bookmark query arguments.
*/
return apply_filters( 'get_bookmarks', $bookmarks, $r );
}
}
if ( ! is_array( $cache ) ) {
$cache = array();
}
$inclusions = '';
if ( ! empty( $r['include'] ) ) {
$r['exclude'] = ''; //ignore exclude, category, and category_name params if using include
$r['category'] = '';
$r['category_name'] = '';
$inclinks = wp_parse_id_list( $r['include'] );
if ( count( $inclinks ) ) {
foreach ( $inclinks as $inclink ) {
if ( empty( $inclusions ) ) {
$inclusions = ' AND ( link_id = ' . $inclink . ' ';
} else {
$inclusions .= ' OR link_id = ' . $inclink . ' ';
}
}
}
}
if ( ! empty( $inclusions ) ) {
$inclusions .= ')';
}
$exclusions = '';
if ( ! empty( $r['exclude'] ) ) {
$exlinks = wp_parse_id_list( $r['exclude'] );
if ( count( $exlinks ) ) {
foreach ( $exlinks as $exlink ) {
if ( empty( $exclusions ) ) {
$exclusions = ' AND ( link_id <> ' . $exlink . ' ';
} else {
$exclusions .= ' AND link_id <> ' . $exlink . ' ';
}
}
}
}
if ( ! empty( $exclusions ) ) {
$exclusions .= ')';
}
if ( ! empty( $r['category_name'] ) ) {
if ( $r['category'] = get_term_by( 'name', $r['category_name'], 'link_category' ) ) {
$r['category'] = $r['category']->term_id;
} else {
$cache[ $key ] = array();
wp_cache_set( 'get_bookmarks', $cache, 'bookmark' );
/** This filter is documented in wp-includes/bookmark.php */
return apply_filters( 'get_bookmarks', array(), $r );
}
}
$search = '';
if ( ! empty( $r['search'] ) ) {
$like = '%' . $wpdb->esc_like( $r['search'] ) . '%';
$search = $wpdb->prepare( ' AND ( (link_url LIKE %s) OR (link_name LIKE %s) OR (link_description LIKE %s) ) ', $like, $like, $like );
}
$category_query = '';
$join = '';
if ( ! empty( $r['category'] ) ) {
$incategories = wp_parse_id_list( $r['category'] );
if ( count( $incategories ) ) {
foreach ( $incategories as $incat ) {
if ( empty( $category_query ) ) {
$category_query = ' AND ( tt.term_id = ' . $incat . ' ';
} else {
$category_query .= ' OR tt.term_id = ' . $incat . ' ';
}
}
}
}
if ( ! empty( $category_query ) ) {
$category_query .= ") AND taxonomy = 'link_category'";
$join = " INNER JOIN $wpdb->term_relationships AS tr ON ($wpdb->links.link_id = tr.object_id) INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_taxonomy_id = tr.term_taxonomy_id";
}
if ( $r['show_updated'] ) {
$recently_updated_test = ', IF (DATE_ADD(link_updated, INTERVAL 120 MINUTE) >= NOW(), 1,0) as recently_updated ';
} else {
$recently_updated_test = '';
}
$get_updated = ( $r['show_updated'] ) ? ', UNIX_TIMESTAMP(link_updated) AS link_updated_f ' : '';
$orderby = strtolower( $r['orderby'] );
$length = '';
switch ( $orderby ) {
case 'length':
$length = ', CHAR_LENGTH(link_name) AS length';
break;
case 'rand':
$orderby = 'rand()';
break;
case 'link_id':
$orderby = "$wpdb->links.link_id";
break;
default:
$orderparams = array();
$keys = array( 'link_id', 'link_name', 'link_url', 'link_visible', 'link_rating', 'link_owner', 'link_updated', 'link_notes', 'link_description' );
foreach ( explode( ',', $orderby ) as $ordparam ) {
$ordparam = trim( $ordparam );
if ( in_array( 'link_' . $ordparam, $keys ) ) {
$orderparams[] = 'link_' . $ordparam;
} elseif ( in_array( $ordparam, $keys ) ) {
$orderparams[] = $ordparam;
}
}
$orderby = implode( ',', $orderparams );
}
if ( empty( $orderby ) ) {
$orderby = 'link_name';
}
$order = strtoupper( $r['order'] );
if ( '' !== $order && ! in_array( $order, array( 'ASC', 'DESC' ) ) ) {
$order = 'ASC';
}
$visible = '';
if ( $r['hide_invisible'] ) {
$visible = "AND link_visible = 'Y'";
}
$query = "SELECT * $length $recently_updated_test $get_updated FROM $wpdb->links $join WHERE 1=1 $visible $category_query";
$query .= " $exclusions $inclusions $search";
$query .= " ORDER BY $orderby $order";
if ( $r['limit'] != -1 ) {
$query .= ' LIMIT ' . absint( $r['limit'] );
}
$results = $wpdb->get_results( $query );
if ( 'rand()' !== $orderby ) {
$cache[ $key ] = $results;
wp_cache_set( 'get_bookmarks', $cache, 'bookmark' );
}
/** This filter is documented in wp-includes/bookmark.php */
return apply_filters( 'get_bookmarks', $results, $r );
}
/**
* Sanitizes all bookmark fields
*
* @since 2.3.0
*
* @param stdClass|array $bookmark Bookmark row
* @param string $context Optional, default is 'display'. How to filter the
* fields
* @return stdClass|array Same type as $bookmark but with fields sanitized.
*/
function sanitize_bookmark( $bookmark, $context = 'display' ) {
$fields = array(
'link_id',
'link_url',
'link_name',
'link_image',
'link_target',
'link_category',
'link_description',
'link_visible',
'link_owner',
'link_rating',
'link_updated',
'link_rel',
'link_notes',
'link_rss',
);
if ( is_object( $bookmark ) ) {
$do_object = true;
$link_id = $bookmark->link_id;
} else {
$do_object = false;
$link_id = $bookmark['link_id'];
}
foreach ( $fields as $field ) {
if ( $do_object ) {
if ( isset( $bookmark->$field ) ) {
$bookmark->$field = sanitize_bookmark_field( $field, $bookmark->$field, $link_id, $context );
}
} else {
if ( isset( $bookmark[ $field ] ) ) {
$bookmark[ $field ] = sanitize_bookmark_field( $field, $bookmark[ $field ], $link_id, $context );
}
}
}
return $bookmark;
}
/**
* Sanitizes a bookmark field.
*
* Sanitizes the bookmark fields based on what the field name is. If the field
* has a strict value set, then it will be tested for that, else a more generic
* filtering is applied. After the more strict filter is applied, if the `$context`
* is 'raw' then the value is immediately return.
*
* Hooks exist for the more generic cases. With the 'edit' context, the {@see 'edit_$field'}
* filter will be called and passed the `$value` and `$bookmark_id` respectively.
*
* With the 'db' context, the {@see 'pre_$field'} filter is called and passed the value.
* The 'display' context is the final context and has the `$field` has the filter name
* and is passed the `$value`, `$bookmark_id`, and `$context`, respectively.
*
* @since 2.3.0
*
* @param string $field The bookmark field.
* @param mixed $value The bookmark field value.
* @param int $bookmark_id Bookmark ID.
* @param string $context How to filter the field value. Accepts 'raw', 'edit', 'attribute',
* 'js', 'db', or 'display'
* @return mixed The filtered value.
*/
function sanitize_bookmark_field( $field, $value, $bookmark_id, $context ) {
switch ( $field ) {
case 'link_id': // ints
case 'link_rating':
$value = (int) $value;
break;
case 'link_category': // array( ints )
$value = array_map( 'absint', (array) $value );
// We return here so that the categories aren't filtered.
// The 'link_category' filter is for the name of a link category, not an array of a link's link categories
return $value;
case 'link_visible': // bool stored as Y|N
$value = preg_replace( '/[^YNyn]/', '', $value );
break;
case 'link_target': // "enum"
$targets = array( '_top', '_blank' );
if ( ! in_array( $value, $targets ) ) {
$value = '';
}
break;
}
if ( 'raw' == $context ) {
return $value;
}
if ( 'edit' == $context ) {
/** This filter is documented in wp-includes/post.php */
$value = apply_filters( "edit_{$field}", $value, $bookmark_id );
if ( 'link_notes' == $field ) {
$value = esc_html( $value ); // textarea_escaped
} else {
$value = esc_attr( $value );
}
} elseif ( 'db' == $context ) {
/** This filter is documented in wp-includes/post.php */
$value = apply_filters( "pre_{$field}", $value );
} else {
/** This filter is documented in wp-includes/post.php */
$value = apply_filters( "{$field}", $value, $bookmark_id, $context );
if ( 'attribute' == $context ) {
$value = esc_attr( $value );
} elseif ( 'js' == $context ) {
$value = esc_js( $value );
}
}
return $value;
}
/**
* Deletes the bookmark cache.
*
* @since 2.7.0
*
* @param int $bookmark_id Bookmark ID.
*/
function clean_bookmark_cache( $bookmark_id ) {
wp_cache_delete( $bookmark_id, 'bookmark' );
wp_cache_delete( 'get_bookmarks', 'bookmark' );
clean_object_term_cache( $bookmark_id, 'link' );
}
/**
* Nav Menu API: Walker_Nav_Menu class
*
* @package WordPress
* @subpackage Nav_Menus
* @since 4.6.0
*/
/**
* Core class used to implement an HTML list of nav menu items.
*
* @since 3.0.0
*
* @see Walker
*/
class Walker_Nav_Menu extends Walker {
/**
* What the class handles.
*
* @since 3.0.0
* @var string
*
* @see Walker::$tree_type
*/
public $tree_type = array( 'post_type', 'taxonomy', 'custom' );
/**
* Database fields to use.
*
* @since 3.0.0
* @todo Decouple this.
* @var array
*
* @see Walker::$db_fields
*/
public $db_fields = array(
'parent' => 'menu_item_parent',
'id' => 'db_id',
);
/**
* Starts the list before the elements are added.
*
* @since 3.0.0
*
* @see Walker::start_lvl()
*
* @param string $output Used to append additional content (passed by reference).
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
*/
public function start_lvl( &$output, $depth = 0, $args = array() ) {
if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
$t = '';
$n = '';
} else {
$t = "\t";
$n = "\n";
}
$indent = str_repeat( $t, $depth );
// Default class.
$classes = array( 'sub-menu' );
/**
* Filters the CSS class(es) applied to a menu list element.
*
* @since 4.8.0
*
* @param string[] $classes Array of the CSS classes that are applied to the menu `` element.
* @param stdClass $args An object of `wp_nav_menu()` arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$class_names = join( ' ', apply_filters( 'nav_menu_submenu_css_class', $classes, $args, $depth ) );
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
$output .= "{$n}{$indent}{$n}";
}
/**
* Ends the list of after the elements are added.
*
* @since 3.0.0
*
* @see Walker::end_lvl()
*
* @param string $output Used to append additional content (passed by reference).
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
*/
public function end_lvl( &$output, $depth = 0, $args = array() ) {
if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
$t = '';
$n = '';
} else {
$t = "\t";
$n = "\n";
}
$indent = str_repeat( $t, $depth );
$output .= "$indent
{$n}";
}
/**
* Starts the element output.
*
* @since 3.0.0
* @since 4.4.0 The {@see 'nav_menu_item_args'} filter was added.
*
* @see Walker::start_el()
*
* @param string $output Used to append additional content (passed by reference).
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $id Current item ID.
*/
public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
$t = '';
$n = '';
} else {
$t = "\t";
$n = "\n";
}
$indent = ( $depth ) ? str_repeat( $t, $depth ) : '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'menu-item-' . $item->ID;
/**
* Filters the arguments for a single nav menu item.
*
* @since 4.4.0
*
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
*/
$args = apply_filters( 'nav_menu_item_args', $args, $item, $depth );
/**
* Filters the CSS classes applied to a menu item's list item element.
*
* @since 3.0.0
* @since 4.1.0 The `$depth` parameter was added.
*
* @param string[] $classes Array of the CSS classes that are applied to the menu item's `` element.
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) );
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
/**
* Filters the ID applied to a menu item's list item element.
*
* @since 3.0.1
* @since 4.1.0 The `$depth` parameter was added.
*
* @param string $menu_id The ID that is applied to the menu item's `` element.
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$id = apply_filters( 'nav_menu_item_id', 'menu-item-' . $item->ID, $item, $args, $depth );
$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '';
$atts = array();
$atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
$atts['target'] = ! empty( $item->target ) ? $item->target : '';
if ( '_blank' === $item->target && empty( $item->xfn ) ) {
$atts['rel'] = 'noopener noreferrer';
} else {
$atts['rel'] = $item->xfn;
}
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
$atts['aria-current'] = $item->current ? 'page' : '';
/**
* Filters the HTML attributes applied to a menu item's anchor element.
*
* @since 3.6.0
* @since 4.1.0 The `$depth` parameter was added.
*
* @param array $atts {
* The HTML attributes applied to the menu item's `` element, empty strings are ignored.
*
* @type string $title Title attribute.
* @type string $target Target attribute.
* @type string $rel The rel attribute.
* @type string $href The href attribute.
* @type string $aria_current The aria-current attribute.
* }
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );
$attributes = '';
foreach ( $atts as $attr => $value ) {
if ( ! empty( $value ) ) {
$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
$attributes .= ' ' . $attr . '="' . $value . '"';
}
}
/** This filter is documented in wp-includes/post-template.php */
$title = apply_filters( 'the_title', $item->title, $item->ID );
/**
* Filters a menu item's title.
*
* @since 4.4.0
*
* @param string $title The menu item's title.
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$title = apply_filters( 'nav_menu_item_title', $title, $item, $args, $depth );
$item_output = $args->before;
$item_output .= '';
$item_output .= $args->link_before . $title . $args->link_after;
$item_output .= '';
$item_output .= $args->after;
/**
* Filters a menu item's starting output.
*
* The menu item's starting output only includes `$args->before`, the opening ``,
* the menu item's title, the closing ``, and `$args->after`. Currently, there is
* no filter for modifying the opening and closing `` for a menu item.
*
* @since 3.0.0
*
* @param string $item_output The menu item's starting HTML output.
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
*/
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
/**
* Ends the element output, if needed.
*
* @since 3.0.0
*
* @see Walker::end_el()
*
* @param string $output Used to append additional content (passed by reference).
* @param WP_Post $item Page data object. Not used.
* @param int $depth Depth of page. Not Used.
* @param stdClass $args An object of wp_nav_menu() arguments.
*/
public function end_el( &$output, $item, $depth = 0, $args = array() ) {
if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
$t = '';
$n = '';
} else {
$t = "\t";
$n = "\n";
}
$output .= "{$n}";
}
} // Walker_Nav_Menu
/**
* REST API: WP_REST_Term_Meta_Fields class
*
* @package WordPress
* @subpackage REST_API
* @since 4.7.0
*/
/**
* Core class used to manage meta values for terms via the REST API.
*
* @since 4.7.0
*
* @see WP_REST_Meta_Fields
*/
class WP_REST_Term_Meta_Fields extends WP_REST_Meta_Fields {
/**
* Taxonomy to register fields for.
*
* @since 4.7.0
* @var string
*/
protected $taxonomy;
/**
* Constructor.
*
* @since 4.7.0
*
* @param string $taxonomy Taxonomy to register fields for.
*/
public function __construct( $taxonomy ) {
$this->taxonomy = $taxonomy;
}
/**
* Retrieves the object meta type.
*
* @since 4.7.0
*
* @return string The meta type.
*/
protected function get_meta_type() {
return 'term';
}
/**
* Retrieves the object meta subtype.
*
* @since 4.9.8
*
* @return string Subtype for the meta type, or empty string if no specific subtype.
*/
protected function get_meta_subtype() {
return $this->taxonomy;
}
/**
* Retrieves the type for register_rest_field().
*
* @since 4.7.0
*
* @return string The REST field type.
*/
public function get_rest_field_type() {
return 'post_tag' === $this->taxonomy ? 'tag' : $this->taxonomy;
}
}