fictioneer/includes/functions/_helpers-query.php

336 lines
11 KiB
PHP
Raw Normal View History

2023-01-21 01:31:34 +01:00
<?php
// =============================================================================
// ALLOWED ORDERBY
// =============================================================================
/**
* Returns list of allowed orderby parameters for WP_Query
*
* @since 5.7.0
* @since 5.9.4 - Extended list.
*
* @return array List of allowed orderby parameters.
*/
function fictioneer_allowed_orderby() {
2024-04-11 11:17:30 +02:00
$defaults = ['modified', 'date', 'title', 'rand', 'name', 'ID', 'comment_count', 'type', 'post__in', 'author', 'words'];
return apply_filters( 'fictioneer_filter_allowed_orderby', $defaults );
}
2023-01-21 01:31:34 +01:00
// =============================================================================
// GET CARD LIST
// =============================================================================
if ( ! function_exists( 'fictioneer_get_card_list' ) ) {
/**
* Returns the query and HTML list items for a post type
*
* @since 5.0.0
*
* @param string $type Either story, chapter, collection, recommendation, or post.
* @param array $query_args Optional. Query arguments merged with the defaults.
* @param string $empty Optional. What to show as empty result. Defaults to 'No results'.
* @param array $card_args Optional. Card partial arguments merged with the defaults.
*
* @return array|boolean The query results ('query') and the cards as list items ('html').
* False for impermissible parameters.
*/
function fictioneer_get_card_list( $type, $query_args = [], $empty = '', $card_args = [] ) {
// Setup
$html = '';
$empty = empty( $empty ) ? __( 'No results.', 'fictioneer' ) : $empty;
$query = false;
$allowed_types = ['fcn_story', 'fcn_chapter', 'fcn_collection', 'fcn_recommendation', 'post'];
2023-12-01 03:00:41 +01:00
$post_type = in_array( $type, ['story', 'chapter', 'collection', 'recommendation'] ) ? "fcn_{$type}" : $type;
$page = $query_args['paged'] ?? 1;
$is_empty = false;
2023-11-12 14:31:28 +01:00
$excluded = [];
// Validations
2023-08-20 22:04:11 +02:00
if ( ! in_array( $post_type, $allowed_types ) ) {
return false;
}
// Default query arguments
$the_query_args = array(
'post_type' => $post_type,
'post_status' => 'publish',
'orderby' => 'modified',
'order' => 'DESC',
2023-08-09 01:53:01 +02:00
'posts_per_page' => get_option( 'posts_per_page' ),
2023-11-12 01:14:34 +01:00
'no_found_rows' => $query_args['no_found_rows'] ?? false,
'update_post_meta_cache' => true,
'update_post_term_cache' => true
);
// Default card arguments
$the_card_args = array(
'cache' => fictioneer_caching_active( 'card_args' ) && ! fictioneer_private_caching_active()
);
// Merge with optional arguments
$the_query_args = array_merge( $the_query_args, $query_args );
$the_card_args = array_merge( $the_card_args, $card_args );
// Query (but not if 'post__in' is set and empty)
if ( ! ( isset( $the_query_args['post__in'] ) && empty( $the_query_args['post__in'] ) ) ) {
2023-11-12 14:31:28 +01:00
// Look for IDs to exclude if story or chapter...
if ( in_array( $post_type, ['fcn_story', 'fcn_chapter'] ) && FICTIONEER_QUERY_ID_ARRAY_LIMIT > 0 ) {
2023-11-12 14:31:28 +01:00
// Get complete set for filtering (required due to pagination)
$all_query = new WP_Query(
array_merge( $the_query_args, array( 'posts_per_page' => -1, 'no_found_rows' => true ) )
);
// Get excluded posts (faster than meta query)
if ( $post_type === 'fcn_story' ) {
// Story hidden?
foreach ( $all_query->posts as $candidate ) {
if ( get_post_meta( $candidate->ID, 'fictioneer_story_hidden', true ) ) {
2023-11-12 14:31:28 +01:00
$excluded[] = $candidate->ID;
}
}
} else {
// Chapter hidden or excluded?
foreach ( $all_query->posts as $candidate ) {
if (
get_post_meta( $candidate->ID, 'fictioneer_chapter_hidden', true ) ||
get_post_meta( $candidate->ID, 'fictioneer_chapter_no_chapter', true )
2023-11-12 14:31:28 +01:00
) {
$excluded[] = $candidate->ID;
}
}
2023-11-12 01:14:34 +01:00
}
}
if ( ! empty( $excluded ) && count( $excluded ) <= FICTIONEER_QUERY_ID_ARRAY_LIMIT ) {
2023-11-12 01:14:34 +01:00
$the_query_args['post__not_in'] = array_merge( $excluded, ( $the_query_args['post__not_in'] ?? [] ) );
}
2023-11-12 12:42:25 +01:00
// Query without excluded posts
$query = new WP_Query( $the_query_args );
// Prime author cache
2023-08-06 21:33:37 +02:00
if (
get_option( 'fictioneer_show_authors' ) &&
! empty( $query->posts ) &&
function_exists( 'update_post_author_caches' )
) {
update_post_author_caches( $query->posts );
}
}
2023-01-21 01:31:34 +01:00
// Buffer HTML output
ob_start();
// Loop results
if ( $query && $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
$card_post_id = get_the_ID();
switch ( $post_type ) {
case 'fcn_story':
if ( get_post_meta( $card_post_id, 'fictioneer_story_hidden', true ) ) {
get_template_part( 'partials/_card-hidden', null, $the_card_args );
} else {
get_template_part( 'partials/_card-story', null, $the_card_args );
}
break;
case 'fcn_chapter':
if (
get_post_meta( $card_post_id, 'fictioneer_chapter_hidden', true ) ||
get_post_meta( $card_post_id, 'fictioneer_chapter_no_chapter', true )
) {
get_template_part( 'partials/_card-hidden', null, $the_card_args );
} else {
get_template_part( 'partials/_card-chapter', null, $the_card_args );
}
break;
default:
get_template_part( 'partials/_card-' . str_replace( 'fcn_', '', $post_type ), null, $the_card_args );
}
}
wp_reset_postdata();
} elseif ( $empty ) {
$is_empty = true;
// Start HTML ---> ?>
<li class="no-results"><?php echo $page > 1 ? __( 'No more results.', 'fictioneer' ) : $empty; ?></li>
<?php // <--- End HTML
2023-01-21 01:31:34 +01:00
}
// Get buffered HTML
$html = ob_get_clean();
2023-01-21 01:31:34 +01:00
// Return results
return array( 'query' => $query, 'html' => $html, 'page' => $page, 'empty' => $is_empty );
}
2023-01-21 01:31:34 +01:00
}
// =============================================================================
// APPEND DATE QUERY
// =============================================================================
if ( ! function_exists( 'fictioneer_append_date_query' ) ) {
/**
2023-10-09 00:27:36 +02:00
* Appends date query to query arguments
*
* @since 5.4.0
*
* @param array $query_args Query arguments to modify.
* @param string|int $ago Optional. Time range in days or valid date string. Default null.
* @param string $orderby Optional. Current orderby. Default null.
*
* @return array Modified query arguments.
*/
function fictioneer_append_date_query( $query_args, $ago = null, $orderby = null ) {
// Ago?
if ( empty( $ago ) ) {
$ago = $_GET['ago'] ?? 0;
$ago = is_numeric( $ago ) ? absint( $ago ) : sanitize_text_field( $ago );
}
// Orderby?
if ( empty( $orderby ) ) {
$orderby = fictioneer_sanitize_query_var( $_GET['orderby'] ?? 0, fictioneer_allowed_orderby(), 'modified' );
}
// Validate ago argument
if ( ! is_numeric( $ago ) && strtotime( $ago ) === false ) {
$ago = 0;
}
// Build date query...
if ( is_numeric( $ago ) && $ago > 0 ) {
// ... for number as days
$query_args['date_query'] = array(
array(
'column' => $orderby === 'modified' ? 'post_modified' : 'post_date',
'after' => absint( $ago ) . ' days ago',
'inclusive' => true,
)
);
} elseif ( ! empty( $ago ) ) {
// ... for valid strtotime() string
$query_args['date_query'] = array(
array(
'column' => $orderby === 'modified' ? 'post_modified' : 'post_date',
'after' => sanitize_text_field( $ago ),
'inclusive' => true,
)
);
}
// Non-date related order?
if ( isset( $query_args['date_query'] ) && in_array( $orderby, ['title', 'rand'] ) ) {
// Second condition for modified date
$modified_arg = $query_args['date_query'][0];
$modified_arg['column'] = 'post_modified';
// Include both publish and modified dates
$query_args['date_query'] = array(
'relation' => 'OR',
$query_args['date_query'][0],
$modified_arg
);
}
// Return (maybe) modified query arguments
return $query_args;
}
}
// =============================================================================
// STICKY STORIES
// =============================================================================
2023-09-17 18:21:46 +02:00
/**
2023-10-09 00:27:36 +02:00
* Filters sticky stories to the top and accounts for missing meta fields
2023-09-17 18:21:46 +02:00
*
* @since 5.7.3
2024-02-06 11:37:29 +01:00
* @since 5.9.4 - Check orderby by components, extend allow list.
2023-09-17 18:21:46 +02:00
*
2023-10-09 00:27:36 +02:00
* @param array $clauses An associative array of WP_Query SQL clauses.
2023-09-17 18:21:46 +02:00
* @param WP_Query $wp_query The WP_Query instance.
*
2024-02-06 11:37:29 +01:00
* @return string The updated or unchanged SQL clauses.
2023-09-17 18:21:46 +02:00
*/
function fictioneer_clause_sticky_stories( $clauses, $wp_query ) {
global $wpdb;
// Setup
$vars = $wp_query->query_vars;
$allowed_queries = ['stories_list', 'latest_stories', 'latest_stories_compact', 'author_stories'];
2024-02-06 11:37:29 +01:00
$allowed_orderby = ['', 'date', 'modified', 'title', 'meta_value', 'name', 'ID', 'post__in'];
$given_orderby = $vars['orderby'] ?? [''];
$given_orderby = is_array( $given_orderby ) ? $given_orderby : explode( ' ', $vars['orderby'] );
2024-02-06 11:37:29 +01:00
// Return if query is not allowed
if (
2023-10-09 00:27:36 +02:00
! in_array( $vars['fictioneer_query_name'] ?? 0, $allowed_queries ) ||
2024-02-06 11:37:29 +01:00
! empty( array_diff( $given_orderby, $allowed_orderby ) )
) {
return $clauses;
}
// Update clauses to set missing meta key to 0
$clauses['join'] .= " LEFT JOIN $wpdb->postmeta AS m ON ($wpdb->posts.ID = m.post_id AND m.meta_key = 'fictioneer_story_sticky')";
$clauses['orderby'] = "COALESCE(m.meta_value+0, 0) DESC, " . $clauses['orderby'];
$clauses['groupby'] = "$wpdb->posts.ID";
// Pass to query
return $clauses;
}
if ( FICTIONEER_ENABLE_STICKY_CARDS ) {
add_filter( 'posts_clauses', 'fictioneer_clause_sticky_stories', 10, 2 );
}
// =============================================================================
// LIST META QUERIES
// =============================================================================
/**
* Adds 'fictioneer_chapter_hidden' to be saved falsy
*
* @since 5.9.4
*
* @param array $allowed Array of allowed falsy meta fields.
*
* @return array The updated array.
*/
function fictioneer_allow_falsy_chapter_hidden( $allowed ) {
$allowed[] = 'fictioneer_chapter_hidden';
return $allowed;
}
if ( get_option( 'fictioneer_disable_extended_chapter_list_meta_queries' ) ) {
add_filter( 'fictioneer_filter_falsy_meta_allow_list', 'fictioneer_allow_falsy_chapter_hidden' );
}
/**
* Adds 'fictioneer_story_hidden' to be saved falsy
*
* @since 5.9.4
*
* @param array $allowed Array of allowed falsy meta fields.
*
* @return array The updated array.
*/
function fictioneer_allow_falsy_story_hidden( $allowed ) {
$allowed[] = 'fictioneer_story_hidden';
return $allowed;
}
if ( get_option( 'fictioneer_disable_extended_story_list_meta_queries' ) ) {
add_filter( 'fictioneer_filter_falsy_meta_allow_list', 'fictioneer_allow_falsy_story_hidden' );
}