} elseif ( in_array( $post_obj->post_name, $post, true ) ) {
return true;
} else {
foreach ( $post as $postpath ) {
if ( ! strpos( $postpath, '/' ) ) {
continue;
}
$postpath_obj = get_page_by_path( $postpath, OBJECT, $post_obj->post_type );
if ( $postpath_obj && ( $postpath_obj->ID == $post_obj->ID ) ) {
return true;
}
}
}
return false;
}
/**
* Determines whether the query is for an existing single post of any post type
* (post, attachment, page, custom post types).
*
* If the $post_types parameter is specified, this function will additionally
* check if the query is for one of the Posts Types specified.
*
* @since 3.1.0
*
* @see WP_Query::is_page()
* @see WP_Query::is_single()
*
* @param string|string[] $post_types Optional. Post type or array of post types
* to check against. Default empty.
* @return bool Whether the query is for an existing single post
* or any of the given post types.
*/
public function is_singular( $post_types = '' ) {
if ( empty( $post_types ) || ! $this->is_singular ) {
return (bool) $this->is_singular;
}
$post_obj = $this->get_queried_object();
if ( ! $post_obj ) {
return false;
}
return in_array( $post_obj->post_type, (array) $post_types, true );
}
/**
* Determines whether the query is for a specific time.
*
* @since 3.1.0
*
* @return bool Whether the query is for a specific time.
*/
public function is_time() {
return (bool) $this->is_time;
}
/**
* Determines whether the query is for a trackback endpoint call.
*
* @since 3.1.0
*
* @return bool Whether the query is for a trackback endpoint call.
*/
public function is_trackback() {
return (bool) $this->is_trackback;
}
/**
* Determines whether the query is for an existing year archive.
*
* @since 3.1.0
*
* @return bool Whether the query is for an existing year archive.
*/
public function is_year() {
return (bool) $this->is_year;
}
/**
* Determines whether the query is a 404 (returns no results).
*
* @since 3.1.0
*
* @return bool Whether the query is a 404 error.
*/
public function is_404() {
return (bool) $this->is_404;
}
/**
* Determines whether the query is for an embedded post.
*
* @since 4.4.0
*
* @return bool Whether the query is for an embedded post.
*/
public function is_embed() {
return (bool) $this->is_embed;
}
/**
* Determines whether the query is the main query.
*
* @since 3.3.0
*
* @global WP_Query $wp_the_query WordPress Query object.
*
* @return bool Whether the query is the main query.
*/
public function is_main_query() {
global $wp_the_query;
return $wp_the_query === $this;
}
/**
* Sets up global post data.
*
* @since 4.1.0
* @since 4.4.0 Added the ability to pass a post ID to `$post`.
*
* @global int $id
* @global WP_User $authordata
* @global string $currentday
* @global string $currentmonth
* @global int $page
* @global array $pages
* @global int $multipage
* @global int $more
* @global int $numpages
*
* @param WP_Post|object|int $post WP_Post instance or Post ID/object.
* @return true True when finished.
*/
public function setup_postdata( $post ) {
global $id, $authordata, $currentday, $currentmonth, $page, $pages, $multipage, $more, $numpages;
if ( ! ( $post instanceof WP_Post ) ) {
$post = get_post( $post );
}
if ( ! $post ) {
return;
}
$elements = $this->generate_postdata( $post );
if ( false === $elements ) {
return;
}
$id = $elements['id'];
$authordata = $elements['authordata'];
$currentday = $elements['currentday'];
$currentmonth = $elements['currentmonth'];
$page = $elements['page'];
$pages = $elements['pages'];
$multipage = $elements['multipage'];
$more = $elements['more'];
$numpages = $elements['numpages'];
/**
* Fires once the post data has been set up.
*
* @since 2.8.0
* @since 4.1.0 Introduced `$query` parameter.
*
* @param WP_Post $post The Post object (passed by reference).
* @param WP_Query $query The current Query object (passed by reference).
*/
do_action_ref_array( 'the_post', array( &$post, &$this ) );
return true;
}
/**
* Generates post data.
*
* @since 5.2.0
*
* @param WP_Post|object|int $post WP_Post instance or Post ID/object.
* @return array|false Elements of post or false on failure.
*/
public function generate_postdata( $post ) {
if ( ! ( $post instanceof WP_Post ) ) {
$post = get_post( $post );
}
if ( ! $post ) {
return false;
}
$id = (int) $post->ID;
$authordata = get_userdata( $post->post_author );
$currentday = false;
$currentmonth = false;
$post_date = $post->post_date;
if ( ! empty( $post_date ) && '0000-00-00 00:00:00' !== $post_date ) {
// Avoid using mysql2date for performance reasons.
$currentmonth = substr( $post_date, 5, 2 );
$day = substr( $post_date, 8, 2 );
$year = substr( $post_date, 2, 2 );
$currentday = sprintf( '%s.%s.%s', $day, $currentmonth, $year );
}
$numpages = 1;
$multipage = 0;
$page = $this->get( 'page' );
if ( ! $page ) {
$page = 1;
}
/*
* Force full post content when viewing the permalink for the $post,
* or when on an RSS feed. Otherwise respect the 'more' tag.
*/
if ( get_queried_object_id() === $post->ID && ( $this->is_page() || $this->is_single() ) ) {
$more = 1;
} elseif ( $this->is_feed() ) {
$more = 1;
} else {
$more = 0;
}
$content = $post->post_content;
if ( str_contains( $content, '' ) ) {
$content = str_replace( "\n\n", '', $content );
$content = str_replace( "\n", '', $content );
$content = str_replace( "\n", '', $content );
// Remove the nextpage block delimiters, to avoid invalid block structures in the split content.
$content = str_replace( '', '', $content );
$content = str_replace( '', '', $content );
// Ignore nextpage at the beginning of the content.
if ( str_starts_with( $content, '' ) ) {
$content = substr( $content, 15 );
}
$pages = explode( '', $content );
} else {
$pages = array( $post->post_content );
}
/**
* Filters the "pages" derived from splitting the post content.
*
* "Pages" are determined by splitting the post content based on the presence
* of `` tags.
*
* @since 4.4.0
*
* @param string[] $pages Array of "pages" from the post content split by `` tags.
* @param WP_Post $post Current post object.
*/
$pages = apply_filters( 'content_pagination', $pages, $post );
$numpages = count( $pages );
if ( $numpages > 1 ) {
if ( $page > 1 ) {
$more = 1;
}
$multipage = 1;
} else {
$multipage = 0;
}
$elements = compact( 'id', 'authordata', 'currentday', 'currentmonth', 'page', 'pages', 'multipage', 'more', 'numpages' );
return $elements;
}
/**
* Generates cache key.
*
* @since 6.1.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param array $args Query arguments.
* @param string $sql SQL statement.
* @return string Cache key.
*/
protected function generate_cache_key( array $args, $sql ) {
global $wpdb;
unset(
$args['cache_results'],
$args['fields'],
$args['lazy_load_term_meta'],
$args['update_post_meta_cache'],
$args['update_post_term_cache'],
$args['update_menu_item_cache'],
$args['suppress_filters']
);
if ( empty( $args['post_type'] ) ) {
if ( $this->is_attachment ) {
$args['post_type'] = 'attachment';
} elseif ( $this->is_page ) {
$args['post_type'] = 'page';
} else {
$args['post_type'] = 'post';
}
} elseif ( 'any' === $args['post_type'] ) {
$args['post_type'] = array_values( get_post_types( array( 'exclude_from_search' => false ) ) );
}
$args['post_type'] = (array) $args['post_type'];
// Sort post types to ensure same cache key generation.
sort( $args['post_type'] );
if ( isset( $args['post_status'] ) ) {
$args['post_status'] = (array) $args['post_status'];
// Sort post status to ensure same cache key generation.
sort( $args['post_status'] );
}
// Add a default orderby value of date to ensure same cache key generation.
if ( ! isset( $q['orderby'] ) ) {
$args['orderby'] = 'date';
}
$placeholder = $wpdb->placeholder_escape();
array_walk_recursive(
$args,
/*
* Replace wpdb placeholders with the string used in the database
* query to avoid unreachable cache keys. This is necessary because
* the placeholder is randomly generated in each request.
*
* $value is passed by reference to allow it to be modified.
* array_walk_recursive() does not return an array.
*/
static function ( &$value ) use ( $wpdb, $placeholder ) {
if ( is_string( $value ) && str_contains( $value, $placeholder ) ) {
$value = $wpdb->remove_placeholder_escape( $value );
}
}
);
ksort( $args );
// Replace wpdb placeholder in the SQL statement used by the cache key.
$sql = $wpdb->remove_placeholder_escape( $sql );
$key = md5( serialize( $args ) . $sql );
$last_changed = wp_cache_get_last_changed( 'posts' );
if ( ! empty( $this->tax_query->queries ) ) {
$last_changed .= wp_cache_get_last_changed( 'terms' );
}
return "wp_query:$key:$last_changed";
}
/**
* After looping through a nested query, this function
* restores the $post global to the current post in this query.
*
* @since 3.7.0
*
* @global WP_Post $post Global post object.
*/
public function reset_postdata() {
if ( ! empty( $this->post ) ) {
$GLOBALS['post'] = $this->post;
$this->setup_postdata( $this->post );
}
}
/**
* Lazyloads term meta for posts in the loop.
*
* @since 4.4.0
* @deprecated 4.5.0 See wp_queue_posts_for_term_meta_lazyload().
*
* @param mixed $check
* @param int $term_id
* @return mixed
*/
public function lazyload_term_meta( $check, $term_id ) {
_deprecated_function( __METHOD__, '4.5.0' );
return $check;
}
/**
* Lazyloads comment meta for comments in the loop.
*
* @since 4.4.0
* @deprecated 4.5.0 See wp_lazyload_comment_meta().
*
* @param mixed $check
* @param int $comment_id
* @return mixed
*/
public function lazyload_comment_meta( $check, $comment_id ) {
_deprecated_function( __METHOD__, '4.5.0' );
return $check;
}
}
uery_vars', $query, $block, $page );
}
/**
* Helper function that returns the proper pagination arrow HTML for
* `QueryPaginationNext` and `QueryPaginationPrevious` blocks based
* on the provided `paginationArrow` from `QueryPagination` context.
*
* It's used in QueryPaginationNext and QueryPaginationPrevious blocks.
*
* @since 5.9.0
*
* @param WP_Block $block Block instance.
* @param bool $is_next Flag for handling `next/previous` blocks.
* @return string|null The pagination arrow HTML or null if there is none.
*/
function get_query_pagination_arrow( $block, $is_next ) {
$arrow_map = array(
'none' => '',
'arrow' => array(
'next' => '→',
'previous' => '←',
),
'chevron' => array(
'next' => '»',
'previous' => '«',
),
);
if ( ! empty( $block->context['paginationArrow'] ) && array_key_exists( $block->context['paginationArrow'], $arrow_map ) && ! empty( $arrow_map[ $block->context['paginationArrow'] ] ) ) {
$pagination_type = $is_next ? 'next' : 'previous';
$arrow_attribute = $block->context['paginationArrow'];
$arrow = $arrow_map[ $block->context['paginationArrow'] ][ $pagination_type ];
$arrow_classes = "wp-block-query-pagination-$pagination_type-arrow is-arrow-$arrow_attribute";
return "$arrow";
}
return null;
}
/**
* Helper function that constructs a comment query vars array from the passed
* block properties.
*
* It's used with the Comment Query Loop inner blocks.
*
* @since 6.0.0
*
* @param WP_Block $block Block instance.
* @return array Returns the comment query parameters to use with the
* WP_Comment_Query constructor.
*/
function build_comment_query_vars_from_block( $block ) {
$comment_args = array(
'orderby' => 'comment_date_gmt',
'order' => 'ASC',
'status' => 'approve',
'no_found_rows' => false,
);
if ( is_user_logged_in() ) {
$comment_args['include_unapproved'] = array( get_current_user_id() );
} else {
$unapproved_email = wp_get_unapproved_comment_author_email();
if ( $unapproved_email ) {
$comment_args['include_unapproved'] = array( $unapproved_email );
}
}
if ( ! empty( $block->context['postId'] ) ) {
$comment_args['post_id'] = (int) $block->context['postId'];
}
if ( get_option( 'thread_comments' ) ) {
$comment_args['hierarchical'] = 'threaded';
} else {
$comment_args['hierarchical'] = false;
}
if ( get_option( 'page_comments' ) === '1' || get_option( 'page_comments' ) === true ) {
$per_page = get_option( 'comments_per_page' );
$default_page = get_option( 'default_comments_page' );
if ( $per_page > 0 ) {
$comment_args['number'] = $per_page;
$page = (int) get_query_var( 'cpage' );
if ( $page ) {
$comment_args['paged'] = $page;
} elseif ( 'oldest' === $default_page ) {
$comment_args['paged'] = 1;
} elseif ( 'newest' === $default_page ) {
$max_num_pages = (int) ( new WP_Comment_Query( $comment_args ) )->max_num_pages;
if ( 0 !== $max_num_pages ) {
$comment_args['paged'] = $max_num_pages;
}
}
// Set the `cpage` query var to ensure the previous and next pagination links are correct
// when inheriting the Discussion Settings.
if ( 0 === $page && isset( $comment_args['paged'] ) && $comment_args['paged'] > 0 ) {
set_query_var( 'cpage', $comment_args['paged'] );
}
}
}
return $comment_args;
}
/**
* Helper function that returns the proper pagination arrow HTML for
* `CommentsPaginationNext` and `CommentsPaginationPrevious` blocks based on the
* provided `paginationArrow` from `CommentsPagination` context.
*
* It's used in CommentsPaginationNext and CommentsPaginationPrevious blocks.
*
* @since 6.0.0
*
* @param WP_Block $block Block instance.
* @param string $pagination_type Optional. Type of the arrow we will be rendering.
* Accepts 'next' or 'previous'. Default 'next'.
* @return string|null The pagination arrow HTML or null if there is none.
*/
function get_comments_pagination_arrow( $block, $pagination_type = 'next' ) {
$arrow_map = array(
'none' => '',
'arrow' => array(
'next' => '→',
'previous' => '←',
),
'chevron' => array(
'next' => '»',
'previous' => '«',
),
);
if ( ! empty( $block->context['comments/paginationArrow'] ) && ! empty( $arrow_map[ $block->context['comments/paginationArrow'] ][ $pagination_type ] ) ) {
$arrow_attribute = $block->context['comments/paginationArrow'];
$arrow = $arrow_map[ $block->context['comments/paginationArrow'] ][ $pagination_type ];
$arrow_classes = "wp-block-comments-pagination-$pagination_type-arrow is-arrow-$arrow_attribute";
return "$arrow";
}
return null;
}
/**
* Strips all HTML from the content of footnotes, and sanitizes the ID.
*
* This function expects slashed data on the footnotes content.
*
* @access private
* @since 6.3.2
*
* @param string $footnotes JSON-encoded string of an array containing the content and ID of each footnote.
* @return string Filtered content without any HTML on the footnote content and with the sanitized ID.
*/
function _wp_filter_post_meta_footnotes( $footnotes ) {
$footnotes_decoded = json_decode( $footnotes, true );
if ( ! is_array( $footnotes_decoded ) ) {
return '';
}
$footnotes_sanitized = array();
foreach ( $footnotes_decoded as $footnote ) {
if ( ! empty( $footnote['content'] ) && ! empty( $footnote['id'] ) ) {
$footnotes_sanitized[] = array(
'id' => sanitize_key( $footnote['id'] ),
'content' => wp_unslash( wp_filter_post_kses( wp_slash( $footnote['content'] ) ) ),
);
}
}
return wp_json_encode( $footnotes_sanitized );
}
/**
* Adds the filters for footnotes meta field.
*
* @access private
* @since 6.3.2
*/
function _wp_footnotes_kses_init_filters() {
add_filter( 'sanitize_post_meta_footnotes', '_wp_filter_post_meta_footnotes' );
}
/**
* Removes the filters for footnotes meta field.
*
* @access private
* @since 6.3.2
*/
function _wp_footnotes_remove_filters() {
remove_filter( 'sanitize_post_meta_footnotes', '_wp_filter_post_meta_footnotes' );
}
/**
* Registers the filter of footnotes meta field if the user does not have `unfiltered_html` capability.
*
* @access private
* @since 6.3.2
*/
function _wp_footnotes_kses_init() {
_wp_footnotes_remove_filters();
if ( ! current_user_can( 'unfiltered_html' ) ) {
_wp_footnotes_kses_init_filters();
}
}
/**
* Initializes the filters for footnotes meta field when imported data should be filtered.
*
* This filter is the last one being executed on {@see 'force_filtered_html_on_import'}.
* If the input of the filter is true, it means we are in an import situation and should
* enable kses, independently of the user capabilities. So in that case we call
* _wp_footnotes_kses_init_filters().
*
* @access private
* @since 6.3.2
*
* @param string $arg Input argument of the filter.
* @return string Input argument of the filter.
*/
function _wp_footnotes_force_filtered_html_on_import_filter( $arg ) {
// If `force_filtered_html_on_import` is true, we need to init the global styles kses filters.
if ( $arg ) {
_wp_footnotes_kses_init_filters();
}
return $arg;
}