Dashboard
PHP:
8.4.22
OS:
Linux
User:
provencez
/
/
home
/
provencez
/
www
/
wp-content
/
plugins
/
flexa-block
/
build
/
blocks
/
post-grid
📤 Upload
📝 New File
📁 New Folder
Close
Editing: render.php
<?php /** * Post Grid block — server-side render. * * CSS is generated at save time by Post_Grid_CSS and printed inline on the front * end. This file runs a WP_Query built from the sanitized block attributes and * outputs a grid of post cards (featured image, meta, title, excerpt, read-more) * plus optional numbered pagination. Every dynamic value is escaped on output. * * @package Flexa\Block * * @var array $attributes Block attributes. * @var string $content Save content (unused — dynamic block). * @var WP_Block $block Block instance. */ if ( ! defined( 'ABSPATH' ) ) { exit; } // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- $attributes provided by WP block API. use Flexa\Block\HTML_Helpers; $block_id = $attributes['blockId'] ?? ''; $anchor = $attributes['anchor'] ?? ''; $html_tag = HTML_Helpers::get_html_tag( $attributes, 'section' ); $container_type = 'full-width' === ( $attributes['containerType'] ?? 'boxed' ) ? 'full-width' : 'boxed'; // --- Query attributes (sanitized). --- $post_type = sanitize_key( (string) ( $attributes['postType'] ?? 'post' ) ); if ( '' === $post_type || ! post_type_exists( $post_type ) ) { $post_type = 'post'; } $per_page = max( 1, min( 100, (int) ( $attributes['postsPerPage'] ?? 6 ) ) ); $base_offset = max( 0, (int) ( $attributes['offset'] ?? 0 ) ); $order_by = in_array( $attributes['orderBy'] ?? 'date', [ 'date', 'title', 'menu_order', 'rand', 'comment_count' ], true ) ? $attributes['orderBy'] : 'date'; $order = 'asc' === strtolower( (string) ( $attributes['order'] ?? 'desc' ) ) ? 'ASC' : 'DESC'; $taxonomy = sanitize_key( (string) ( $attributes['taxonomy'] ?? '' ) ); $terms_raw = (string) ( $attributes['terms'] ?? '' ); $exclude_cur = ! empty( $attributes['excludeCurrent'] ); // --- Element toggles. --- $show_image = false !== ( $attributes['showImage'] ?? true ); $show_title = false !== ( $attributes['showTitle'] ?? true ); $title_tag = in_array( $attributes['titleTag'] ?? 'h3', [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'div' ], true ) ? $attributes['titleTag'] : 'h3'; $show_meta = false !== ( $attributes['showMeta'] ?? true ); $show_author = false !== ( $attributes['showAuthor'] ?? true ); $show_avatar = ! empty( $attributes['showAvatar'] ); $avatar_size = max( 12, min( 96, (int) ( $attributes['avatarSize'] ?? 24 ) ) ); $show_date = false !== ( $attributes['showDate'] ?? true ); $show_comments = ! empty( $attributes['showComments'] ); $show_taxonomy = ! empty( $attributes['showTaxonomy'] ); $show_excerpt = false !== ( $attributes['showExcerpt'] ?? true ); $excerpt_length = max( 0, (int) ( $attributes['excerptLength'] ?? 20 ) ); $show_read_more = false !== ( $attributes['showReadMore'] ?? true ); $read_more_text = (string) ( $attributes['readMoreText'] ?? '' ); $read_more_text = '' !== trim( $read_more_text ) ? $read_more_text : __( 'Read more', 'flexa-block' ); $image_size = sanitize_key( (string) ( $attributes['imageSize'] ?? 'large' ) ); $image_size = '' !== $image_size ? $image_size : 'large'; // Inline aspect-ratio for the "no image" placeholder so it matches the chosen ratio. $image_ratio = trim( (string) ( $attributes['imageRatio'] ?? '' ) ); $ratio_style = ( '' !== $image_ratio && preg_match( '#^[0-9]+\s*/\s*[0-9]+$#', $image_ratio ) ) ? 'aspect-ratio:' . $image_ratio : ''; $pag_type = in_array( $attributes['paginationType'] ?? 'numbered', [ 'none', 'numbered', 'loadmore' ], true ) ? $attributes['paginationType'] : 'numbered'; $show_pagination = 'numbered' === $pag_type; $is_loadmore = 'loadmore' === $pag_type; $load_more_text = (string) ( $attributes['loadMoreText'] ?? '' ); $load_more_text = '' !== trim( $load_more_text ) ? $load_more_text : __( 'Load more', 'flexa-block' ); // Pagination previous/next labels (user text, default arrows). $prev_label = (string) ( $attributes['prevLabel'] ?? '' ); $prev_label = '' !== trim( $prev_label ) ? $prev_label : __( '‹', 'flexa-block' ); $next_label = (string) ( $attributes['nextLabel'] ?? '' ); $next_label = '' !== trim( $next_label ) ? $next_label : __( '›', 'flexa-block' ); // --- Pagination: a per-block query arg so multiple grids page independently. --- $page_key = '' !== $block_id ? 'pg_' . sanitize_key( $block_id ) : 'pg'; $paged = 1; if ( isset( $_GET[ $page_key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only paging, no state change. $paged = max( 1, absint( wp_unslash( $_GET[ $page_key ] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended } else { $qv = (int) get_query_var( 'paged' ); $paged = $qv > 1 ? $qv : max( 1, (int) get_query_var( 'page' ) ); } // --- Build WP_Query args. --- $args = [ 'post_type' => $post_type, 'post_status' => 'publish', 'orderby' => $order_by, 'order' => $order, 'posts_per_page' => $per_page, 'offset' => $base_offset + ( $show_pagination ? ( $paged - 1 ) * $per_page : 0 ), 'ignore_sticky_posts' => true, ]; if ( '' !== $taxonomy && taxonomy_exists( $taxonomy ) && '' !== trim( $terms_raw ) ) { $term_list = array_values( array_filter( array_map( 'trim', explode( ',', $terms_raw ) ) ) ); if ( ! empty( $term_list ) ) { $numeric = count( $term_list ) === count( array_filter( $term_list, 'is_numeric' ) ); $args['tax_query'] = [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query [ 'taxonomy' => $taxonomy, 'field' => $numeric ? 'term_id' : 'slug', 'terms' => $numeric ? array_map( 'absint', $term_list ) : $term_list, ], ]; } } if ( $exclude_cur ) { $current_id = (int) get_queried_object_id(); if ( $current_id > 0 ) { // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- single current post excluded from the grid; negligible cost. $args['post__not_in'] = [ $current_id ]; } } $query = new WP_Query( $args ); // --- Wrapper classes + attributes. --- $classes = [ 'flexa-post-grid', 'flexa-post-grid--' . $container_type ]; if ( '' !== $block_id ) { $classes[] = 'flexa-post-grid-' . sanitize_html_class( $block_id ); } $classes = HTML_Helpers::build_wrapper_classes( $classes, $attributes ); $wrapper_args = [ 'class' => implode( ' ', $classes ) ]; if ( $anchor ) { $wrapper_args['id'] = sanitize_html_class( $anchor ); } // Per-instance key: the view script uses it to scope AJAX paging to this grid. $wrapper_args['data-flexa-post-grid'] = $page_key; if ( $is_loadmore ) { // The view script fetches the next page's URL and appends its cards. $wrapper_args['data-flexa-loadmore'] = '1'; } $wrapper_attributes = get_block_wrapper_attributes( $wrapper_args ); $data_attrs = HTML_Helpers::build_data_attrs( $attributes ); // Lazy background marker on the styled inner element. $background = $attributes['background'] ?? []; $is_lazy_bg = ! empty( $background['lazyLoad'] ) && 'image' === ( $background['type'] ?? 'none' ) && '' !== ( $background['image']['url'] ?? '' ); $lazy_marker = $is_lazy_bg ? ' data-flexa-lazy-bg' : ''; // --- Build the cards. --- $cards_html = ''; if ( $query->have_posts() ) { while ( $query->have_posts() ) { $query->the_post(); $post_id = get_the_ID(); $permalink = get_permalink( $post_id ); // Featured image (WP-escaped markup), linked to the post — or a neutral // placeholder glyph when the post has no thumbnail, so cards stay uniform. $image_html = ''; if ( $show_image ) { if ( has_post_thumbnail( $post_id ) ) { $thumb = get_the_post_thumbnail( $post_id, $image_size, [ 'class' => 'flexa-post-grid__image', 'loading' => 'lazy' ] ); if ( '' !== $thumb ) { $image_html = '<a class="flexa-post-grid__image-link" href="' . esc_url( $permalink ) . '" tabindex="-1" aria-hidden="true">' . '<span class="flexa-post-grid__image-wrap">' . $thumb . '</span></a>'; } } if ( '' === $image_html ) { $image_html = '<span class="flexa-post-grid__image-wrap">' . HTML_Helpers::image_placeholder( '', $ratio_style ) . '</span>'; } } // Meta line. $meta_html = ''; if ( $show_meta ) { $parts = []; if ( $show_author ) { $author_inner = ''; if ( $show_avatar ) { $author_inner .= get_avatar( get_the_author_meta( 'ID' ), $avatar_size, '', get_the_author(), [ 'class' => 'flexa-post-grid__avatar' ] ); } $author_inner .= esc_html( get_the_author() ); $parts[] = '<span class="flexa-post-grid__author">' . $author_inner . '</span>'; } if ( $show_date ) { $parts[] = '<time class="flexa-post-grid__date" datetime="' . esc_attr( get_the_date( 'c', $post_id ) ) . '">' . esc_html( get_the_date( '', $post_id ) ) . '</time>'; } if ( $show_comments ) { $comments_number = (int) get_comments_number( $post_id ); /* translators: %d: number of comments on the post. */ $comments_label = sprintf( _n( '%d comment', '%d comments', $comments_number, 'flexa-block' ), $comments_number ); $parts[] = '<span class="flexa-post-grid__comments">' . esc_html( $comments_label ) . '</span>'; } if ( $show_taxonomy ) { $tax_for_terms = '' !== $taxonomy && taxonomy_exists( $taxonomy ) ? $taxonomy : ( 'post' === $post_type ? 'category' : '' ); if ( '' !== $tax_for_terms ) { $term_names = get_the_term_list( $post_id, $tax_for_terms, '', ', ' ); if ( ! is_wp_error( $term_names ) && ! empty( $term_names ) ) { $parts[] = '<span class="flexa-post-grid__terms">' . wp_kses( $term_names, [ 'a' => [ 'href' => true, 'rel' => true ] ] ) . '</span>'; } } } if ( ! empty( $parts ) ) { $meta_html = '<div class="flexa-post-grid__meta">' . implode( '<span class="flexa-post-grid__meta-sep" aria-hidden="true">·</span>', $parts ) . '</div>'; } } // Title (linked). $title_html = ''; if ( $show_title ) { $title_text = get_the_title( $post_id ); $title_html = '<' . $title_tag . ' class="flexa-post-grid__title"><a href="' . esc_url( $permalink ) . '">' . esc_html( $title_text ) . '</a></' . $title_tag . '>'; } // Excerpt (trimmed, formatting stripped to plain text). $excerpt_html = ''; if ( $show_excerpt && $excerpt_length > 0 ) { $raw = has_excerpt( $post_id ) ? get_the_excerpt( $post_id ) : wp_strip_all_tags( (string) get_post_field( 'post_content', $post_id ) ); $trimmed = wp_trim_words( $raw, $excerpt_length, '…' ); if ( '' !== trim( $trimmed ) ) { $excerpt_html = '<div class="flexa-post-grid__excerpt">' . esc_html( $trimmed ) . '</div>'; } } // Read-more button. $read_more_html = ''; if ( $show_read_more ) { $read_more_html = '<a class="flexa-post-grid__readmore wp-element-button" href="' . esc_url( $permalink ) . '">' . esc_html( $read_more_text ) . '</a>'; } $body_html = '<div class="flexa-post-grid__body">' . $meta_html . $title_html . $excerpt_html . $read_more_html . '</div>'; $cards_html .= '<article class="flexa-post-grid__item">' . $image_html . $body_html . '</article>'; } wp_reset_postdata(); } // --- Pagination. Numbered = server-rendered links (progressive: works with no // JS; the view script upgrades to AJAX). Load more = a button the view script // wires to fetch + append the next page. --- $pagination_html = ''; $total = max( 0, (int) $query->found_posts - $base_offset ); $total_pages = (int) ceil( $total / $per_page ); if ( $show_pagination && $total_pages > 1 ) { $links = paginate_links( [ 'base' => esc_url_raw( add_query_arg( $page_key, '%#%' ) ), 'format' => '', 'current' => $paged, 'total' => $total_pages, 'type' => 'plain', 'prev_text' => esc_html( $prev_label ), 'next_text' => esc_html( $next_label ), ] ); if ( $links ) { $pagination_html = '<nav class="flexa-pagination" aria-label="' . esc_attr__( 'Posts navigation', 'flexa-block' ) . '">' . $links . '</nav>'; } } elseif ( $is_loadmore && $total_pages > 1 ) { $next_url = esc_url( add_query_arg( $page_key, 2 ) ); $pagination_html = '<div class="flexa-pagination-loadmore" data-next="' . esc_attr( $next_url ) . '" data-page="1" data-total="' . esc_attr( (string) $total_pages ) . '" data-key="' . esc_attr( $page_key ) . '">' . '<button type="button" class="flexa-pagination-loadmore__btn wp-element-button">' . esc_html( $load_more_text ) . '</button></div>'; } // --- Body: the grid, or a graceful empty state. --- if ( '' !== $cards_html ) { $inner_html = '<div class="flexa-post-grid__grid">' . $cards_html . '</div>' . $pagination_html; } else { $inner_html = '<p class="flexa-post-grid__empty">' . esc_html__( 'No posts found.', 'flexa-block' ) . '</p>'; } printf( '<%1$s %2$s%3$s><div class="flexa-post-grid__inner"%4$s>%5$s</div></%1$s>', esc_html( $html_tag ), $wrapper_attributes, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- built via get_block_wrapper_attributes. $data_attrs, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- keys sanitized, values escaped in helper. $lazy_marker, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static literal. $inner_html // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- titles/dates esc_html'd, urls esc_url'd, thumbnail + paginate_links WP-escaped. );
Save
Cancel