posts ) && function_exists( 'update_post_author_caches' ) ) { update_post_author_caches( $result->posts ); } return $result; } } // ============================================================================= // SHORTCODE-BASED RELATIONSHIPS // ============================================================================= /** * Register relationships for posts with certain shortcodes * * @since 5.0.0 * * @param int $post_id The ID of the saved post. * @param WP_Post $post The saved post object. */ function fictioneer_update_shortcode_relationships( $post_id, $post ) { // Prevent multi-fire if ( fictioneer_multi_save_guard( $post_id ) ) { return; } // Setup $registry = fictioneer_get_relationship_registry(); // Look for blog shortcode if ( str_contains( $post->post_content, 'fictioneer_blog' ) ) { $registry['always'][ $post_id ] = 'shortcode'; } else { unset( $registry['always'][ $post_id ] ); } // Look for story data shortcode if ( str_contains( $post->post_content, 'fictioneer_story_data' ) || str_contains( $post->post_content, 'fictioneer_story_comments' ) || str_contains( $post->post_content, 'fictioneer_story_actions' ) || str_contains( $post->post_content, 'fictioneer_story_section' ) || str_contains( $post->post_content, 'fictioneer_subscribe_button' ) ) { $registry['always'][ $post_id ] = 'shortcode'; } else { unset( $registry['always'][ $post_id ] ); } // Look for article cards shortcode if ( str_contains( $post->post_content, 'fictioneer_article_cards' ) ) { $registry['always'][ $post_id ] = 'shortcode'; } else { unset( $registry['always'][ $post_id ] ); } // Look for showcase shortcode if ( str_contains( $post->post_content, 'fictioneer_showcase' ) ) { $registry['always'][ $post_id ] = 'shortcode'; } else { unset( $registry['always'][ $post_id ] ); } // Look for post-related shortcode if ( str_contains( $post->post_content, 'fictioneer_latest_post' ) ) { $registry['ref_posts'][ $post_id ] = 'shortcode'; } else { unset( $registry['ref_posts'][ $post_id ] ); } // Look for chapter-related shortcodes if ( str_contains( $post->post_content, 'fictioneer_chapter_list' ) || str_contains( $post->post_content, 'fictioneer_latest_chapter' ) || str_contains( $post->post_content, 'fictioneer_chapter_cards' ) ) { $registry['ref_chapters'][ $post_id ] = 'shortcode'; } else { unset( $registry['ref_chapters'][ $post_id ] ); } // Look for story-related shortcodes if ( str_contains( $post->post_content, 'fictioneer_latest_stor' ) || str_contains( $post->post_content, 'fictioneer_story_cards' ) || str_contains( $post->post_content, 'fictioneer_latest_update' ) || str_contains( $post->post_content, 'fictioneer_update_cards' ) ) { $registry['ref_stories'][ $post_id ] = 'shortcode'; } else { unset( $registry['ref_stories'][ $post_id ] ); } // Look for recommendation-related shortcodes if ( str_contains( $post->post_content, 'fictioneer_latest_recommendation' ) || str_contains( $post->post_content, 'fictioneer_recommendation_cards' ) ) { $registry['ref_recommendations'][ $post_id ] = 'shortcode'; } else { unset( $registry['ref_recommendations'][ $post_id ] ); } // Update database fictioneer_save_relationship_registry( $registry ); } if ( FICTIONEER_RELATIONSHIP_PURGE_ASSIST ) { add_action( 'save_post', 'fictioneer_update_shortcode_relationships', 10, 2 ); } // ============================================================================= // GET SHORTCODE DEFAULT ARGS // ============================================================================= /** * Returns sanitized arguments extracted from shortcode attributes * * Checks for 'count', 'offset', 'order', 'orderby', 'page', 'per_page', 'post_ids', * 'author', 'author_ids', 'exclude_author_ids', 'exclude_tag_ids', 'exclude_cat_ids', * 'rel', 'ignore_sticky', 'ignore_protected', 'class', and taxonomies with the * fictioneer_get_shortcode_taxonomies() helper functions. * * @since 5.7.3 * * @param array $attr Attributes passed to the shortcode. * @param int $def_count Default for the 'count' argument. * * @return array The extracted arguments. */ function fictioneer_get_default_shortcode_args( $attr, $def_count = -1 ) { //--- Sanitize attributes ---------------------------------------------------- $attr = is_array( $attr ) ? array_map( 'sanitize_text_field', $attr ) : sanitize_text_field( $attr ); //--- Extract arguments ------------------------------------------------------ $seamless_default = get_theme_mod( 'card_image_style', 'default' ) === 'seamless'; $thumbnail_default = get_theme_mod( 'card_image_style', 'default' ) !== 'none'; $args = array( 'type' => $attr['type'] ?? 'default', 'count' => max( -1, intval( $attr['count'] ?? $def_count ) ), 'offset' => max( 0, intval( $attr['offset'] ?? 0 ) ), 'order' => $attr['order'] ?? '', 'orderby' => $attr['orderby'] ?? '', 'page' => max( 1, get_query_var( 'page' ) ?: get_query_var( 'paged' ) ), 'posts_per_page' => absint( $attr['per_page'] ?? 0 ) ?: get_option( 'posts_per_page' ), 'post_ids' => fictioneer_explode_list( $attr['post_ids'] ?? '' ), 'author' => sanitize_title( $attr['author'] ?? '' ), 'author_ids' => fictioneer_explode_list( $attr['author_ids'] ?? '' ), 'excluded_authors' => fictioneer_explode_list( $attr['exclude_author_ids'] ?? '' ), 'excluded_tags' => fictioneer_explode_list( $attr['exclude_tag_ids'] ?? '' ), 'excluded_cats' => fictioneer_explode_list( $attr['exclude_cat_ids'] ?? '' ), 'taxonomies' => fictioneer_get_shortcode_taxonomies( $attr ), 'relation' => strtolower( $attr['rel'] ?? 'and' ) === 'or' ? 'OR' : 'AND', 'ignore_sticky' => filter_var( $attr['ignore_sticky'] ?? 0, FILTER_VALIDATE_BOOLEAN ), 'ignore_protected' => filter_var( $attr['ignore_protected'] ?? 0, FILTER_VALIDATE_BOOLEAN ), 'vertical' => filter_var( $attr['vertical'] ?? 0, FILTER_VALIDATE_BOOLEAN ), 'seamless' => filter_var( $attr['seamless'] ?? $seamless_default, FILTER_VALIDATE_BOOLEAN ), 'aspect_ratio' => sanitize_css_aspect_ratio( $attr['aspect_ratio'] ?? '' ), 'thumbnail' => filter_var( $attr['thumbnail'] ?? $thumbnail_default, FILTER_VALIDATE_BOOLEAN ), 'lightbox' => filter_var( $attr['lightbox'] ?? 1, FILTER_VALIDATE_BOOLEAN ), 'words' => filter_var( $attr['words'] ?? 1, FILTER_VALIDATE_BOOLEAN ), 'date' => filter_var( $attr['date'] ?? 1, FILTER_VALIDATE_BOOLEAN ), 'footer_chapters' => filter_var( $attr['footer_chapters'] ?? 1, FILTER_VALIDATE_BOOLEAN ), 'footer_words' => filter_var( $attr['footer_words'] ?? 1, FILTER_VALIDATE_BOOLEAN ), 'footer_date' => filter_var( $attr['footer_date'] ?? 1, FILTER_VALIDATE_BOOLEAN ), 'footer_comments' => filter_var( $attr['footer_comments'] ?? 1, FILTER_VALIDATE_BOOLEAN ), 'footer_status' => filter_var( $attr['footer_status'] ?? 1, FILTER_VALIDATE_BOOLEAN ), 'footer_rating' => filter_var( $attr['footer_rating'] ?? 1, FILTER_VALIDATE_BOOLEAN ), 'classes' => esc_attr( wp_strip_all_tags( $attr['classes'] ?? $attr['class'] ?? '' ) ), 'infobox' => filter_var( $attr['infobox'] ?? 1, FILTER_VALIDATE_BOOLEAN ), 'source' => filter_var( $attr['source'] ?? 1, FILTER_VALIDATE_BOOLEAN ) ); //--- Fixes ------------------------------------------------------------------ // Update count if limited to post IDs if ( ! empty( $args['post_ids'] ) ) { $args['count'] = count( $args['post_ids'] ); } //--- Finish ----------------------------------------------------------------- return $args; } // ============================================================================= // GET SHORTCODE TAXONOMIES // ============================================================================= /** * Extract taxonomies from shortcode attributes * * @since 5.2.0 * * @param array $attr Attributes of the shortcode. * * @return array Array of found taxonomies. */ function fictioneer_get_shortcode_taxonomies( $attr ) { // Setup $taxonomies = []; // Tags if ( ! empty( $attr['tags'] ) ) { $taxonomies['tags'] = fictioneer_explode_list( $attr['tags'] ); } // Categories if ( ! empty( $attr['categories'] ) ) { $taxonomies['categories'] = fictioneer_explode_list( $attr['categories'] ); } // Fandoms if ( ! empty( $attr['fandoms'] ) ) { $taxonomies['fandoms'] = fictioneer_explode_list( $attr['fandoms'] ); } // Characters if ( ! empty( $attr['characters'] ) ) { $taxonomies['characters'] = fictioneer_explode_list( $attr['characters'] ); } // Genres if ( ! empty( $attr['genres'] ) ) { $taxonomies['genres'] = fictioneer_explode_list( $attr['genres'] ); } // Return return $taxonomies; } // ============================================================================= // GET SHORTCODE TAX QUERY // ============================================================================= /** * Get shortcode Tax Query * * @since 5.2.0 * * @param array $args Arguments of the shortcode partial. * * @return array Tax Query. */ function fictioneer_get_shortcode_tax_query( $args ) { // Setup $tax_query = []; // Are there taxonomies? if ( ! empty( $args['taxonomies'] ) ) { // Relationship? if ( count( $args['taxonomies'] ) > 1 ) { $tax_query['relation'] = $args['relation']; } // Tags? if ( ! empty( $args['taxonomies']['tags'] ) ) { $tax_query[] = array( 'taxonomy' => 'post_tag', 'field' => 'name', 'terms' => $args['taxonomies']['tags'] ); } // Categories? if ( ! empty( $args['taxonomies']['categories'] ) ) { $tax_query[] = array( 'taxonomy' => 'category', 'field' => 'name', 'terms' => $args['taxonomies']['categories'] ); } // Fandoms? if ( ! empty( $args['taxonomies']['fandoms'] ) ) { $tax_query[] = array( 'taxonomy' => 'fcn_fandom', 'field' => 'name', 'terms' => $args['taxonomies']['fandoms'] ); } // Characters? if ( ! empty( $args['taxonomies']['characters'] ) ) { $tax_query[] = array( 'taxonomy' => 'fcn_character', 'field' => 'name', 'terms' => $args['taxonomies']['characters'] ); } // Genres? if ( ! empty( $args['taxonomies']['genres'] ) ) { $tax_query[] = array( 'taxonomy' => 'fcn_genre', 'field' => 'name', 'terms' => $args['taxonomies']['genres'] ); } } // Return return $tax_query; } // ============================================================================= // SHOWCASE SHORTCODE // ============================================================================= /** * Shortcode to display showcase * * @since 5.0.0 * * @param string $attr['for'] What the showcase is for. Allowed are chapters, * collections, recommendations, and stories. * @param string|null $attr['count'] Optional. Maximum number of items. Default 8. * @param string|null $attr['author'] Optional. Limit posts to a specific author. * @param string|null $attr['order'] Optional. Order direction. Default 'DESC'. * @param string|null $attr['orderby'] Optional. Order argument. Default 'date'. * @param string|null $attr['post_ids'] Optional. Limit posts to specific post IDs. * @param string|null $attr['ignore_protected'] Optional. Whether to ignore protected posts. Default false. * @param string|null $attr['author_ids'] Optional. Only include posts by these author IDs. * @param string|null $attr['exclude_author_ids'] Optional. Exclude posts with these author IDs. * @param string|null $attr['exclude_tag_ids'] Optional. Exclude posts with these tags. * @param string|null $attr['exclude_cat_ids'] Optional. Exclude posts with these categories. * @param string|null $attr['categories'] Optional. Limit posts to specific category names. * @param string|null $attr['tags'] Optional. Limit posts to specific tag names. * @param string|null $attr['fandoms'] Optional. Limit posts to specific fandom names. * @param string|null $attr['genres'] Optional. Limit posts to specific genre names. * @param string|null $attr['characters'] Optional. Limit posts to specific character names. * @param string|null $attr['rel'] Optional. Relationship between taxonomies. Default 'AND'. * @param string|null $attr['vertical'] Optional. Whether to show the vertical variant. * @param string|null $attr['seamless'] Optional. Whether to render the image seamless. Default false (Customizer). * @param string|null $attr['aspect_ratio'] Optional. Aspect ratio for the image. Only with vertical. * @param string|null $attr['lightbox'] Optional. Whether the thumbnail is opened in the lightbox. Default true. * @param string|null $attr['thumbnail'] Optional. Whether to show the thumbnail. Default true (Customizer). * @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace. * * @return string The captured shortcode HTML. */ function fictioneer_shortcode_showcase( $attr ) { // Abort if... if ( empty( $attr['for'] ) ) { return ''; } // Defaults $args = fictioneer_get_default_shortcode_args( $attr, 8 ); // Specifics $args['no_cap'] = filter_var( $attr['no_cap'] ?? 0, FILTER_VALIDATE_BOOLEAN ); switch ( $attr['for'] ) { case 'collections': $args['post_type'] = 'fcn_collection'; break; case 'chapters': $args['post_type'] = 'fcn_chapter'; break; case 'stories': $args['post_type'] = 'fcn_story'; break; case 'recommendations': $args['post_type'] = 'fcn_recommendation'; break; } // Abort if... if ( ! isset( $args['post_type'] ) ) { return ''; } // Transient? if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { $base = serialize( $args ) . serialize( $attr ); $type = $args['post_type']; $transient_key = "fictioneer_shortcode_showcase_{$type}_html_" . md5( $base ); $transient = get_transient( $transient_key ); if ( ! empty( $transient ) ) { return $transient; } } // Buffer ob_start(); get_template_part( 'partials/_showcase', null, $args ); $html = fictioneer_minify_html( ob_get_clean() ); if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION ); } // Return minified buffer return $html; } add_shortcode( 'fictioneer_showcase', 'fictioneer_shortcode_showcase' ); // ============================================================================= // LATEST CHAPTERS SHORTCODE // ============================================================================= /** * Shortcode to show latest chapters * * @since 3.0 * * @param string|null $attr['count'] Optional. Maximum number of items. Default 4. * @param string|null $attr['author'] Optional. Limit posts to a specific author. * @param string|null $attr['type'] Optional. Choose between 'default', 'simple', and 'compact'. * @param string|null $attr['order'] Optional. Order argument. Default 'DESC'. * @param string|null $attr['orderby'] Optional. Orderby argument. Default 'date'. * @param string|null $attr['spoiler'] Optional. Whether to show spoiler content. * @param string|null $attr['source'] Optional. Whether to show author and story. * @param string|null $attr['post_ids'] Optional. Limit posts to specific post IDs. * @param string|null $attr['ignore_protected'] Optional. Whether to ignore protected posts. Default false. * @param string|null $attr['author_ids'] Optional. Only include posts by these author IDs. * @param string|null $attr['exclude_author_ids'] Optional. Exclude posts with these author IDs. * @param string|null $attr['exclude_tag_ids'] Optional. Exclude posts with these tags. * @param string|null $attr['exclude_cat_ids'] Optional. Exclude posts with these categories. * @param string|null $attr['categories'] Optional. Limit posts to specific category names. * @param string|null $attr['tags'] Optional. Limit posts to specific tag names. * @param string|null $attr['fandoms'] Optional. Limit posts to specific fandom names. * @param string|null $attr['genres'] Optional. Limit posts to specific genre names. * @param string|null $attr['characters'] Optional. Limit posts to specific character names. * @param string|null $attr['rel'] Optional. Relationship between taxonomies. Default 'AND'. * @param string|null $attr['vertical'] Optional. Whether to show the vertical variant. * @param string|null $attr['seamless'] Optional. Whether to render the image seamless. Default false (Customizer). * @param string|null $attr['aspect_ratio'] Optional. Aspect ratio for the image. Only with vertical. * @param string|null $attr['lightbox'] Optional. Whether the thumbnail is opened in the lightbox. Default true. * @param string|null $attr['thumbnail'] Optional. Whether to show the thumbnail. Default true (Customizer). * @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace. * * @return string The captured shortcode HTML. */ function fictioneer_shortcode_latest_chapters( $attr ) { // Defaults $args = fictioneer_get_default_shortcode_args( $attr, 4 ); // Specifics $args['simple'] = false; $args['spoiler'] = filter_var( $attr['spoiler'] ?? 0, FILTER_VALIDATE_BOOLEAN ); // Type $type = sanitize_text_field( $attr['type'] ?? 'default' ); // Transient? if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { $base = serialize( $args ) . serialize( $attr ); $transient_key = "fictioneer_shortcode_latest_chapters_{$type}_html_" . md5( $base ); $transient = get_transient( $transient_key ); if ( ! empty( $transient ) ) { return $transient; } } // Buffer ob_start(); switch ( $type ) { case 'compact': get_template_part( 'partials/_latest-chapters-compact', null, $args ); break; default: $args['simple'] = $type == 'simple'; get_template_part( 'partials/_latest-chapters', null, $args ); } $html = fictioneer_minify_html( ob_get_clean() ); if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION ); } // Return minified buffer return $html; } add_shortcode( 'fictioneer_latest_chapters', 'fictioneer_shortcode_latest_chapters' ); add_shortcode( 'fictioneer_latest_chapter', 'fictioneer_shortcode_latest_chapters' ); // Alias add_shortcode( 'fictioneer_chapter_cards', 'fictioneer_shortcode_latest_chapters' ); // Alias // ============================================================================= // LATEST STORIES SHORTCODE // ============================================================================= /** * Shortcode to show latest stories * * @since 3.0 * * @param string|null $attr['count'] Optional. Maximum number of items. Default 4. * @param string|null $attr['author'] Optional. Limit posts to a specific author. * @param string|null $attr['type'] Optional. Choose between 'default' and 'compact'. * @param string|null $attr['order'] Optional. Order argument. Default 'DESC'. * @param string|null $attr['orderby'] Optional. Orderby argument. Default 'date'. * @param string|null $attr['post_ids'] Optional. Limit posts to specific post IDs. * @param string|null $attr['ignore_protected'] Optional. Whether to ignore protected posts. Default false. * @param string|null $attr['author_ids'] Optional. Only include posts by these author IDs. * @param string|null $attr['exclude_author_ids'] Optional. Exclude posts with these author IDs. * @param string|null $attr['exclude_tag_ids'] Optional. Exclude posts with these tags. * @param string|null $attr['exclude_cat_ids'] Optional. Exclude posts with these categories. * @param string|null $attr['categories'] Optional. Limit posts to specific category names. * @param string|null $attr['tags'] Optional. Limit posts to specific tag names. * @param string|null $attr['fandoms'] Optional. Limit posts to specific fandom names. * @param string|null $attr['genres'] Optional. Limit posts to specific genre names. * @param string|null $attr['characters'] Optional. Limit posts to specific character names. * @param string|null $attr['rel'] Optional. Relationship between taxonomies. Default 'AND'. * @param string|null $attr['vertical'] Optional. Whether to show the vertical variant. * @param string|null $attr['seamless'] Optional. Whether to render the image seamless. Default false (Customizer). * @param string|null $attr['aspect_ratio'] Optional. Aspect ratio for the image. Only with vertical. * @param string|null $attr['lightbox'] Optional. Whether the thumbnail is opened in the lightbox. Default true. * @param string|null $attr['thumbnail'] Optional. Whether to show the thumbnail. Default true (Customizer). * @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace. * * @return string The captured shortcode HTML. */ function fictioneer_shortcode_latest_stories( $attr ) { // Defaults $args = fictioneer_get_default_shortcode_args( $attr, 4 ); // Type $type = sanitize_text_field( $attr['type'] ?? 'default' ); // Transient? if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { $base = serialize( $args ) . serialize( $attr ); $transient_key = "fictioneer_shortcode_latest_stories_{$type}_html_" . md5( $base ); $transient = get_transient( $transient_key ); if ( ! empty( $transient ) ) { return $transient; } } // Buffer ob_start(); switch ( $type ) { case 'compact': get_template_part( 'partials/_latest-stories-compact', null, $args ); break; default: get_template_part( 'partials/_latest-stories', null, $args ); } $html = fictioneer_minify_html( ob_get_clean() ); if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION ); } // Return minified buffer return $html; } add_shortcode( 'fictioneer_latest_stories', 'fictioneer_shortcode_latest_stories' ); add_shortcode( 'fictioneer_latest_story', 'fictioneer_shortcode_latest_stories' ); // Alias add_shortcode( 'fictioneer_story_cards', 'fictioneer_shortcode_latest_stories' ); // Alias // ============================================================================= // LATEST UPDATES SHORTCODE // ============================================================================= /** * Shortcode to show latest story updates * * @since 4.3.0 * * @param string|null $attr['count'] Optional. Maximum number of items. Default 4. * @param string|null $attr['author'] Optional. Limit posts to a specific author. * @param string|null $attr['type'] Optional. Choose between 'default', 'simple', 'single', and 'compact'. * @param string|null $attr['single'] Optional. Whether to show only one chapter item. Default false. * @param string|null $attr['post_ids'] Optional. Limit posts to specific post IDs. * @param string|null $attr['ignore_protected'] Optional. Whether to ignore protected posts. Default false. * @param string|null $attr['exclude_tag_ids'] Optional. Exclude posts with these tags. * @param string|null $attr['author_ids'] Optional. Only include posts by these author IDs. * @param string|null $attr['exclude_author_ids'] Optional. Exclude posts with these author IDs. * @param string|null $attr['exclude_cat_ids'] Optional. Exclude posts with these categories. * @param string|null $attr['categories'] Optional. Limit posts to specific category names. * @param string|null $attr['tags'] Optional. Limit posts to specific tag names. * @param string|null $attr['fandoms'] Optional. Limit posts to specific fandom names. * @param string|null $attr['genres'] Optional. Limit posts to specific genre names. * @param string|null $attr['characters'] Optional. Limit posts to specific character names. * @param string|null $attr['rel'] Optional. Relationship between taxonomies. Default 'AND'. * @param string|null $attr['vertical'] Optional. Whether to show the vertical variant. * @param string|null $attr['seamless'] Optional. Whether to render the image seamless. Default false (Customizer). * @param string|null $attr['aspect_ratio'] Optional. Aspect ratio for the image. Only with vertical. * @param string|null $attr['lightbox'] Optional. Whether the thumbnail is opened in the lightbox. Default true. * @param string|null $attr['thumbnail'] Optional. Whether to show the thumbnail. Default true (Customizer). * @param string|null $attr['words'] Optional. Whether to show the word count of chapter items. Default true. * @param string|null $attr['date'] Optional. Whether to show the date of chapter items. Default true. * @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace. * * @return string The captured shortcode HTML. */ function fictioneer_shortcode_latest_story_updates( $attr ) { // Defaults $args = fictioneer_get_default_shortcode_args( $attr, 4 ); $args['single'] = filter_var( $attr['single'] ?? 0, FILTER_VALIDATE_BOOLEAN ); // Type $type = sanitize_text_field( $attr['type'] ?? 'default' ); // Transient? if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { $base = serialize( $args ) . serialize( $attr ); $transient_key = "fictioneer_shortcode_latest_updates_{$type}_html_" . md5( $base ); $transient = get_transient( $transient_key ); if ( ! empty( $transient ) ) { return $transient; } } // Buffer ob_start(); switch ( $type ) { case 'compact': get_template_part( 'partials/_latest-updates-compact', null, $args ); break; default: get_template_part( 'partials/_latest-updates', null, $args ); } $html = fictioneer_minify_html( ob_get_clean() ); if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION ); } // Return minified buffer return $html; } add_shortcode( 'fictioneer_latest_updates', 'fictioneer_shortcode_latest_story_updates' ); add_shortcode( 'fictioneer_latest_update', 'fictioneer_shortcode_latest_story_updates' ); // Alias add_shortcode( 'fictioneer_update_cards', 'fictioneer_shortcode_latest_story_updates' ); // Alias // ============================================================================= // LATEST RECOMMENDATIONS SHORTCODE // ============================================================================= /** * Shortcode to show latest recommendations * * @since 4.0.0 * * @param string|null $attr['count'] Optional. Maximum number of items. Default 4. * @param string|null $attr['author'] Optional. Limit posts to a specific author. * @param string|null $attr['type'] Optional. Choose between 'default' and 'compact'. * @param string|null $attr['order'] Optional. Order argument. Default 'DESC'. * @param string|null $attr['orderby'] Optional. Orderby argument. Default 'date'. * @param string|null $attr['post_ids'] Optional. Limit posts to specific post IDs. * @param string|null $attr['ignore_protected'] Optional. Whether to ignore protected posts. Default false. * @param string|null $attr['author_ids'] Optional. Only include posts by these author IDs. * @param string|null $attr['exclude_author_ids'] Optional. Exclude posts with these author IDs. * @param string|null $attr['exclude_tag_ids'] Optional. Exclude posts with these tags. * @param string|null $attr['exclude_cat_ids'] Optional. Exclude posts with these categories. * @param string|null $attr['categories'] Optional. Limit posts to specific category names. * @param string|null $attr['tags'] Optional. Limit posts to specific tag names. * @param string|null $attr['fandoms'] Optional. Limit posts to specific fandom names. * @param string|null $attr['genres'] Optional. Limit posts to specific genre names. * @param string|null $attr['characters'] Optional. Limit posts to specific character names. * @param string|null $attr['rel'] Optional. Relationship between taxonomies. Default 'AND'. * @param string|null $attr['vertical'] Optional. Whether to show the vertical variant. * @param string|null $attr['seamless'] Optional. Whether to render the image seamless. Default false (Customizer). * @param string|null $attr['aspect_ratio'] Optional. Aspect ratio for the image. Only with vertical. * @param string|null $attr['lightbox'] Optional. Whether the thumbnail is opened in the lightbox. Default true. * @param string|null $attr['thumbnail'] Optional. Whether to show the thumbnail. Default true (Customizer). * @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace. * * @return string The captured shortcode HTML. */ function fictioneer_shortcode_latest_recommendations( $attr ) { // Defaults $args = fictioneer_get_default_shortcode_args( $attr, 4 ); // Type $type = sanitize_text_field( $attr['type'] ?? 'default' ); // Transient? if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { $base = serialize( $args ) . serialize( $attr ); $transient_key = "fictioneer_shortcode_latest_recommendations_{$type}_html_" . md5( $base ); $transient = get_transient( $transient_key ); if ( ! empty( $transient ) ) { return $transient; } } // Buffer ob_start(); switch ( $type ) { case 'compact': get_template_part( 'partials/_latest-recommendations-compact', null, $args ); break; default: get_template_part( 'partials/_latest-recommendations', null, $args ); } $html = fictioneer_minify_html( ob_get_clean() ); if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION ); } // Return minified buffer return $html; } add_shortcode( 'fictioneer_latest_recommendations', 'fictioneer_shortcode_latest_recommendations' ); add_shortcode( 'fictioneer_latest_recommendation', 'fictioneer_shortcode_latest_recommendations' ); // Alias add_shortcode( 'fictioneer_recommendation_cards', 'fictioneer_shortcode_latest_recommendations' ); // Alias // ============================================================================= // LATEST POST SHORTCODE // ============================================================================= /** * Shortcode to show the latest post * * @since 4.0.0 * * @param string|null $attr['count'] Optional. Maximum number of items. Default 1. * @param string|null $attr['author'] Optional. Limit posts to a specific author. * @param string|null $attr['post_ids'] Optional. Limit posts to specific post IDs. * @param string|null $attr['ignore_protected'] Optional. Whether to ignore protected posts. Default false. * @param string|null $attr['author_ids'] Optional. Only include posts by these author IDs. * @param string|null $attr['exclude_author_ids'] Optional. Exclude posts with these author IDs. * @param string|null $attr['exclude_tag_ids'] Optional. Exclude posts with these tags. * @param string|null $attr['exclude_cat_ids'] Optional. Exclude posts with these categories. * @param string|null $attr['categories'] Optional. Limit posts to specific category names. * @param string|null $attr['tags'] Optional. Limit posts to specific tag names. * @param string|null $attr['rel'] Optional. Relationship between taxonomies. Default 'AND'. * @param string|null $attr['class'] Optional. Additional CSS classes, separated by whitespace. * * @return string The captured shortcode HTML. */ function fictioneer_shortcode_latest_posts( $attr ) { // Defaults $args = fictioneer_get_default_shortcode_args( $attr, 1 ); // Transient? if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { $base = serialize( $args ) . serialize( $attr ); $transient_key = "fictioneer_shortcode_latest_posts_html_" . md5( $base ); $transient = get_transient( $transient_key ); if ( ! empty( $transient ) ) { return $transient; } } // Buffer ob_start(); get_template_part( 'partials/_latest-posts', null, $args ); $html = fictioneer_minify_html( ob_get_clean() ); if ( FICTIONEER_SHORTCODE_TRANSIENTS_ENABLED ) { set_transient( $transient_key, $html, FICTIONEER_SHORTCODE_TRANSIENT_EXPIRATION ); } // Return minified buffer return $html; } add_shortcode( 'fictioneer_latest_posts', 'fictioneer_shortcode_latest_posts' ); add_shortcode( 'fictioneer_latest_post', 'fictioneer_shortcode_latest_posts' ); // Alias // ============================================================================= // BOOKMARKS SHORTCODE // ============================================================================= /** * Shortcode to show bookmarks * * @since 4.0.0 * * @param string|null $attr['count'] Optional. Maximum number of items. Default -1 (all). * @param string|null $attr['show_empty'] Optional. Whether to show the "no bookmarks" message. Default false. * @param string|null $attr['seamless'] Optional. Whether to render the image seamless. Default false (Customizer). * @param string|null $attr['thumbnail'] Optional. Whether to show the thumbnail. Default true (Customizer). * * @return string The captured shortcode HTML. */ function fictioneer_shortcode_bookmarks( $attr ) { // Sanitize attributes $attr = is_array( $attr ) ? array_map( 'sanitize_text_field', $attr ) : sanitize_text_field( $attr ); // Setup $seamless_default = get_theme_mod( 'card_image_style', 'default' ) === 'seamless'; $thumbnail_default = get_theme_mod( 'card_image_style', 'default' ) !== 'none'; // Buffer ob_start(); get_template_part( 'partials/_bookmarks', null, array( 'count' => max( -1, intval( $attr['count'] ?? -1 ) ), 'show_empty' => $attr['show_empty'] ?? false, 'seamless' => filter_var( $attr['seamless'] ?? $seamless_default, FILTER_VALIDATE_BOOLEAN ), 'thumbnail' => filter_var( $attr['thumbnail'] ?? $thumbnail_default, FILTER_VALIDATE_BOOLEAN ) )); // Return minified buffer return fictioneer_minify_html( ob_get_clean() ); } add_shortcode( 'fictioneer_bookmarks', 'fictioneer_shortcode_bookmarks' ); // ============================================================================= // COOKIES SHORTCODE // ============================================================================= /** * Shortcode to show cookie consent actions * * Renders buttons to handle your consent and stored cookies. * * @since 4.7.0 * * @return string The captured shortcode HTML. */ function fictioneer_shortcode_cookie_buttons( $attr ) { ob_start(); get_template_part( 'partials/_cookie-buttons' ); return fictioneer_minify_html( ob_get_clean() ); } add_shortcode( 'fictioneer_cookie_buttons', 'fictioneer_shortcode_cookie_buttons' ); // ============================================================================= // CHAPTER LIST SHORTCODE // ============================================================================= /** * Returns empty chapter list * * @since 5.9.4 * @see fictioneer_shortcode_chapter_list() * * @param string|null $attr['heading'] Optional. Show