the method that renders it. Nothing else can be called. */ const RENDERERS = array( 'posts' => 'w_posts', 'nav-menu' => 'w_nav_menu', 'site-logo' => 'w_site_logo', 'site-title' => 'w_site_title', 'breadcrumbs' => 'w_breadcrumbs', 'shortcode' => 'w_shortcode', 'sitemap' => 'w_sitemap', 'login' => 'w_login', 'post-title' => 'w_post_title', 'post-content' => 'w_post_content', 'post-excerpt' => 'w_post_excerpt', 'featured-image' => 'w_featured_image', 'post-meta' => 'w_post_meta', 'author-box' => 'w_author_box', 'post-nav' => 'w_post_nav', 'comments' => 'w_comments', 'archive-title' => 'w_archive_title', 'archive-desc' => 'w_archive_desc', 'terms' => 'w_terms', 'products' => 'w_products', 'product-related' => 'w_product_related', 'product-categories' => 'w_product_categories', 'product-title' => 'w_product_title', 'product-price' => 'w_product_price', 'product-atc' => 'w_product_atc', 'product-gallery' => 'w_product_gallery', 'product-rating' => 'w_product_rating', 'product-stock' => 'w_product_stock', 'product-meta' => 'w_product_meta', 'product-short' => 'w_product_short', 'product-tabs' => 'w_product_tabs', 'cart-icon' => 'w_cart_icon', 'wc-page' => 'w_wc_page', 'product-filter' => 'w_product_filter', 'news-ticker' => 'w_news_ticker', 'sidebar' => 'w_sidebar', 'terms-list' => 'w_terms_list', 'social-feed' => 'w_social_feed', 'google-reviews' => 'w_google_reviews', 'wc-notices' => 'w_wc_notices', ); private static $ctx_backup = null; /** Last loop render pieces (used by the AJAX endpoint). */ private static $parts = array(); // ------------------------------------------------------------------ // Context for editor previews // ------------------------------------------------------------------ public static function setup_context( $post_id, $preview = null ) { global $post, $wp_query; self::$ctx_backup = array( 'post' => $post, 'query' => $wp_query, ); $target = (int) $post_id; if ( $target && 'akram_template' === get_post_type( $target ) ) { $type = Post_Types::template_type( $target ); $chosen = $preview ? (int) $preview : (int) get_post_meta( $target, '_akram_preview', true ); if ( in_array( $type, array( 'archive', 'search', 'loop' ), true ) || ! $chosen ) { $q = new \WP_Query( array( 'post_type' => 'post', 'posts_per_page' => (int) get_option( 'posts_per_page', 10 ), 'post_status' => 'publish', ) ); if ( 'search' === $type ) { $q->set( 's', 'a' ); } $wp_query = $q; $GLOBALS['wp_the_query'] = $q; } if ( ! $chosen && in_array( $type, array( 'single', 'loop', 'header', 'footer', 'popup', 'section', 'component' ), true ) ) { $latest = get_posts( array( 'post_type' => 'post', 'posts_per_page' => 1, 'fields' => 'ids', ) ); $chosen = $latest ? (int) $latest[0] : 0; } $target = $chosen; } if ( $target ) { $post = get_post( $target ); if ( $post ) { setup_postdata( $post ); if ( function_exists( 'wc_get_product' ) && 'product' === $post->post_type ) { $GLOBALS['product'] = wc_get_product( $post->ID ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited, WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WooCommerce's own global product, needed by its template functions. } } } } public static function reset_context() { global $post, $wp_query; if ( self::$ctx_backup ) { $post = self::$ctx_backup['post']; $wp_query = self::$ctx_backup['query']; $GLOBALS['wp_the_query'] = $wp_query; if ( $post ) { setup_postdata( $post ); } } self::$ctx_backup = null; } // ------------------------------------------------------------------ public static function render( $type, array $s, $id, $post_id, $preview = false ) { $method = self::RENDERERS[ $type ] ?? ''; if ( '' === $method ) { return (string) apply_filters( 'akram_render_widget', '', $type, $s, $id, $post_id ); } try { $html = (string) self::$method( $s, $id, (int) $post_id, $preview ); } catch ( \Throwable $e ) { $html = $preview ? '
' . esc_html( $e->getMessage() ) . '
' : ''; } if ( $preview && '' === trim( $html ) ) { $html = '
' . esc_html( ucwords( str_replace( '-', ' ', $type ) ) ) . '' . esc_html__( 'Nothing to show in this preview context.', 'akram' ) . '
'; } return $html; } private static function v( $s, $k, $d = '' ) { if ( ! isset( $s[ $k ] ) || '' === $s[ $k ] || null === $s[ $k ] ) { return $d; } return $s[ $k ]; } private static function icon( $name ) { static $icons = array( 'chevron' => '', 'plus' => '', 'arrow' => '', 'calendar' => '', 'user' => '', 'folder' => '', 'comment' => '', 'clock' => '', 'tag' => '', 'cart' => '', 'x' => '', 'left' => '', 'right' => '', 'heart' => '', 'eye' => '', 'compare' => '', 'filter' => '', 'star' => '', ); return ''; } // ------------------------------------------------------------------ // Posts / Loop grid // ------------------------------------------------------------------ public static function loop_query_args( array $s, $page = 1, $term = '', $ctx = array() ) { $source = (string) self::v( $s, 'source', 'post' ); $count = max( 1, min( 100, (int) self::v( $s, 'count', 6 ) ) ); $args = array( 'posts_per_page' => $count, 'paged' => max( 1, (int) $page ), 'post_status' => 'publish', 'ignore_sticky_posts' => (bool) self::v( $s, 'sticky', true ), 'orderby' => sanitize_key( self::v( $s, 'orderby', 'date' ) ), 'order' => 'ASC' === self::v( $s, 'order', 'DESC' ) ? 'ASC' : 'DESC', ); if ( 'meta_value_num' === $args['orderby'] ) { $args['meta_key'] = sanitize_text_field( self::v( $s, 'metaKey', '' ) ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- bounded query on a builder meta key. } $offset = (int) self::v( $s, 'offset', 0 ); if ( $offset ) { $args['offset'] = $offset + ( $args['paged'] - 1 ) * $count; } $current = get_the_ID(); if ( 'current' === $source ) { global $wp_query; $qv = $ctx && is_array( $ctx ) ? $ctx : (array) $wp_query->query; $allowed = array( 'cat', 'category_name', 'tag', 'tag_id', 's', 'post_type', 'taxonomy', 'term', 'author', 'author_name', 'year', 'monthnum', 'day', 'product_cat', 'product_tag' ); foreach ( $allowed as $k ) { if ( isset( $qv[ $k ] ) && '' !== $qv[ $k ] ) { $args[ $k ] = is_array( $qv[ $k ] ) ? array_map( 'sanitize_text_field', $qv[ $k ] ) : sanitize_text_field( (string) $qv[ $k ] ); } } // The context can come from a visitor's request (AJAX pagination): only publicly viewable types are allowed. if ( ! empty( $args['post_type'] ) ) { $types = array_filter( (array) $args['post_type'], static function ( $t ) { return 'any' === $t || ( post_type_exists( $t ) && is_post_type_viewable( $t ) && 0 !== strpos( $t, 'akram_' ) ); } ); $args['post_type'] = $types ? array_values( $types ) : 'post'; } if ( ! empty( $args['taxonomy'] ) && ! ( taxonomy_exists( $args['taxonomy'] ) && is_taxonomy_viewable( $args['taxonomy'] ) ) ) { unset( $args['taxonomy'], $args['term'] ); } if ( empty( $args['post_type'] ) ) { $args['post_type'] = isset( $args['s'] ) ? 'any' : 'post'; } $args['posts_per_page'] = $count ?: (int) get_option( 'posts_per_page' ); } elseif ( 'related' === $source ) { $args['post_type'] = get_post_type( $current ) ?: 'post'; $terms = wp_get_post_terms( $current, 'post' === $args['post_type'] ? 'category' : ( 'product' === $args['post_type'] ? 'product_cat' : 'category' ), array( 'fields' => 'ids' ) ); if ( $terms && ! is_wp_error( $terms ) ) { $args['tax_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- bounded query on a builder meta key. array( 'taxonomy' => 'product' === $args['post_type'] ? 'product_cat' : 'category', 'terms' => $terms, ), ); } $args['post__not_in'] = array( $current ); // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- excludes only the post being viewed from its related posts. } elseif ( 'manual' === $source ) { $ids = array_filter( array_map( 'absint', is_array( $s['ids'] ?? null ) ? $s['ids'] : explode( ',', (string) self::v( $s, 'ids', '' ) ) ) ); $args['post_type'] = 'any'; $args['post__in'] = $ids ?: array( 0 ); $args['orderby'] = 'post__in'; } else { $args['post_type'] = post_type_exists( $source ) ? $source : 'post'; } $tax = array(); foreach ( array( 'terms' => 'IN', 'exTerms' => 'NOT IN', ) as $key => $op ) { $ids = array_filter( array_map( 'absint', is_array( $s[ $key ] ?? null ) ? $s[ $key ] : explode( ',', (string) ( $s[ $key ] ?? '' ) ) ) ); $by_tax = array(); foreach ( $ids as $tid ) { $t = get_term( $tid ); if ( $t && ! is_wp_error( $t ) ) { $by_tax[ $t->taxonomy ][] = $tid; } } foreach ( $by_tax as $taxonomy => $tids ) { $tax[] = array( 'taxonomy' => $taxonomy, 'terms' => $tids, 'operator' => $op, ); } } if ( $term ) { $t = is_numeric( $term ) ? get_term( (int) $term ) : null; if ( $t && ! is_wp_error( $t ) ) { $tax[] = array( 'taxonomy' => $t->taxonomy, 'terms' => array( $t->term_id ), ); } } if ( $tax ) { $args['tax_query'] = array_merge( array( 'relation' => 'AND' ), $tax, $args['tax_query'] ?? array() ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- bounded query on a builder meta key. } $author = trim( (string) self::v( $s, 'author', '' ) ); if ( $author ) { $args['author__in'] = array_map( 'absint', explode( ',', $author ) ); } if ( self::v( $s, 'exCurrent', true ) && $current && 'manual' !== $source ) { $args['post__not_in'] = array_unique( array_merge( $args['post__not_in'] ?? array(), array( $current ) ) ); // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- excludes only the post being viewed. } return apply_filters( 'akram_loop_query_args', $args, $s ); } private static function w_posts( array $s, $id, $post_id, $preview, $page = null, $term = '', $ctx = null ) { $page = $page ?: max( 1, isset( $_GET['nxp'] ) ? (int) $_GET['nxp'] : 1 ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only request value; it changes nothing and is sanitised. global $wp_query; $is_current = 'current' === self::v( $s, 'source', 'post' ); $ctx = null !== $ctx ? $ctx : ( $is_current ? array_intersect_key( (array) $wp_query->query, array_flip( array( 'cat', 'category_name', 'tag', 's', 'post_type', 'taxonomy', 'term', 'author', 'author_name', 'year', 'monthnum', 'day', 'product_cat', 'product_tag' ) ) ) : array() ); $q = new \WP_Query( self::loop_query_args( $s, $page, $term, $ctx ) ); $items = self::loop_items( $q, $s ); $layout = (string) self::v( $s, 'layout', 'grid' ); $pager = (string) self::v( $s, 'pager', 'none' ); $tpl = (int) self::v( $s, 'tpl', 0 ); if ( ! $q->have_posts() ) { return '

' . esc_html( self::v( $s, 'empty', __( 'Nothing found.', 'akram' ) ) ) . '

'; } $filter = ''; $ftax = sanitize_key( (string) self::v( $s, 'filter', '' ) ); if ( $ftax && taxonomy_exists( $ftax ) ) { $terms = get_terms( array( 'taxonomy' => $ftax, 'hide_empty' => true, 'number' => 20, ) ); if ( $terms && ! is_wp_error( $terms ) ) { $filter = ''; } } if ( 'carousel' === $layout ) { $cfg = array( 'auto' => (float) self::v( $s, 'auto', 0 ), 'loop' => (bool) self::v( $s, 'loop', true ), 'pause' => true, 'center' => (bool) self::v( $s, 'center', false ), 'fx' => 'slide', ); $body = '
' . preg_replace( '/
' . ( self::v( $s, 'arrows', true ) ? '' . '' : '' ) . ( 'none' !== self::v( $s, 'dots', 'dots' ) ? '
' : '' ) . '
'; Frontend::add_module( 'slider' ); } else { $body = '
' . $items . '
'; } $pg = self::pager( $q, $pager, $page, $s ); self::$parts = array( 'items' => 'carousel' === $layout ? preg_replace( '/
(int) $q->max_num_pages, 'pager' => $pg, ); $data = array( 'post' => (int) $post_id, 'el' => $id, 'pager' => $pager, 'max' => (int) $q->max_num_pages, 'page' => $page, 'ctx' => $ctx, ); wp_reset_postdata(); return '
' . $filter . $body . $pg . '
'; } private static function pager( \WP_Query $q, $type, $page, $s ) { if ( 'none' === $type || $q->max_num_pages < 2 ) { return ''; } if ( 'more' === $type || 'infinite' === $type ) { return 'more' === $type ? '
' : ''; } $base = remove_query_arg( 'nxp' ); if ( 'prevnext' === $type ) { $out = ''; } $out = ''; } private static function loop_items( \WP_Query $q, array $s ) { $tpl = (int) self::v( $s, 'tpl', 0 ); $html = ''; $meta = (array) self::v( $s, 'meta', array( 'date', 'read' ) ); $size = sanitize_key( self::v( $s, 'imgSize', 'medium_large' ) ); $tag = in_array( self::v( $s, 'titleTag', 'h3' ), array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'span' ), true ) ? self::v( $s, 'titleTag', 'h3' ) : 'h3'; $words = (int) self::v( $s, 'excerpt', 18 ); while ( $q->have_posts() ) { $q->the_post(); $pid = get_the_ID(); if ( $tpl ) { $html .= '
' . Frontend::render_post( $tpl ) . '
'; continue; } $link = get_permalink(); $title = get_the_title(); $img = ''; if ( self::v( $s, 'showImg', true ) && has_post_thumbnail() ) { $badge = ''; if ( self::v( $s, 'showBadge', true ) ) { $cats = get_the_terms( $pid, 'product' === get_post_type() ? 'product_cat' : 'category' ); if ( $cats && ! is_wp_error( $cats ) ) { $badge = '' . esc_html( $cats[0]->name ) . ''; } } $img = ''; } $m = array(); foreach ( $meta as $key ) { switch ( $key ) { case 'author': $m[] = '' . esc_html( get_the_author() ) . ''; break; case 'date': $m[] = ''; break; case 'cats': $c = get_the_category_list( ', ' ); if ( $c ) { $m[] = '' . wp_kses_post( $c ) . ''; } break; case 'comments': /* translators: %d: comment count */ $m[] = '' . esc_html( sprintf( _n( '%d comment', '%d comments', get_comments_number(), 'akram' ), get_comments_number() ) ) . ''; break; case 'read': $m[] = '' . esc_html( Dynamic::value( 'reading_time', array() ) ) . ''; break; case 'price': if ( function_exists( 'wc_get_product' ) && 'product' === get_post_type() ) { $p = wc_get_product( $pid ); $m[] = '' . wp_kses_post( $p ? $p->get_price_html() : '' ) . ''; } break; } } $excerpt = $words > 0 ? '

' . esc_html( wp_trim_words( get_the_excerpt(), $words ) ) . '

' : ''; $more = self::v( $s, 'more', '' ) ? '' . esc_html( self::v( $s, 'more' ) ) . self::icon( 'arrow' ) . '' : ''; $html .= ''; } return $html; } /** Public AJAX endpoint for pagination / load more / filters. */ public static function rest_loop( \WP_REST_Request $r ) { $p = $r->get_json_params(); $post = (int) ( $p['post'] ?? 0 ); $el = sanitize_key( (string) ( $p['el'] ?? '' ) ); $status = get_post_status( $post ); if ( ! $post || ! $el || ( 'publish' !== $status && ! current_user_can( 'edit_post', $post ) ) ) { return new \WP_Error( 'akram_missing', 'Not found', array( 'status' => 404 ) ); } $out = Document::get_out( $post ); $d = $out['dyn'][ $el ] ?? null; if ( ! $d || ! in_array( $d['t'], array( 'posts', 'products' ), true ) ) { return new \WP_Error( 'akram_missing', 'Not found', array( 'status' => 404 ) ); } $s = (array) $d['s']; $page = max( 1, (int) ( $p['page'] ?? 1 ) ); $term = (string) ( $p['term'] ?? '' ); $ctx = is_array( $p['ctx'] ?? null ) ? $p['ctx'] : array(); self::$parts = array( 'items' => '', 'max' => 1, 'pager' => '', ); if ( 'products' === $d['t'] ) { self::w_products( $s, $el, $post, false, $page, $term ); } else { self::w_posts( $s, $el, $post, false, $page, $term, $ctx ); } return self::$parts; } // ------------------------------------------------------------------ // Navigation & site // ------------------------------------------------------------------ private static function w_nav_menu( array $s, $id, $post_id, $preview ) { $menu = (int) self::v( $s, 'menu', 0 ); if ( ! $menu ) { $menus = wp_get_nav_menus(); $locs = get_nav_menu_locations(); $menu = $locs ? (int) reset( $locs ) : ( $menus ? (int) $menus[0]->term_id : 0 ); } $args = array( 'container' => false, 'menu_class' => 'nx-menu', 'echo' => false, 'fallback_cb' => '__return_empty_string', 'walker' => new Nav_Walker( self::v( $s, 'indicator', 'chevron' ) ), 'depth' => 4, ); if ( $menu ) { $args['menu'] = $menu; } $list = wp_nav_menu( $args ); if ( ! $list ) { $pages = wp_list_pages( array( 'echo' => false, 'title_li' => '', 'depth' => 2, 'number' => 6, ) ); $list = '
    ' . $pages . '
'; } $label = $menu ? wp_get_nav_menu_object( $menu ) : null; $bp = self::v( $s, 'bp', 't' ); $logo = ''; if ( self::v( $s, 'mLogo', true ) ) { $lid = (int) get_theme_mod( 'custom_logo' ); $logo = $lid ? wp_get_attachment_image( $lid, 'medium', false, array( 'loading' => 'lazy' ) ) : '' . esc_html( get_bloginfo( 'name' ) ) . ''; } $cta = self::v( $s, 'mCta', '' ) ? '' . esc_html( self::v( $s, 'mCta' ) ) . '' : ''; $cls = 'nx-nav is-' . esc_attr( self::v( $s, 'layout', 'horizontal' ) ) . ' j-' . esc_attr( self::v( $s, 'just', 'start' ) ) . ' p-' . esc_attr( self::v( $s, 'pointer', 'underline' ) ) . ' dd-' . esc_attr( self::v( $s, 'dropAnim', 'fade-up' ) ) . ' m-' . esc_attr( self::v( $s, 'mStyle', 'offcanvas' ) ) . ( 'none' !== $bp ? ' bp-coll' : '' ); return ''; } private static function w_site_logo( array $s ) { $img = $s['image'] ?? null; $stuck = $s['stuck'] ?? null; $html = ''; if ( ! empty( $img['url'] ) ) { $html = '' . esc_attr( get_bloginfo( 'name' ) )
				. ''; } else { $lid = (int) get_theme_mod( 'custom_logo' ); if ( $lid ) { $html = wp_get_attachment_image( $lid, 'full', false, array( 'alt' => get_bloginfo( 'name' ), 'fetchpriority' => 'high', 'loading' => 'eager', ) ); } elseif ( 'title' === self::v( $s, 'fallback', 'title' ) ) { $html = '' . esc_html( get_bloginfo( 'name' ) ) . ''; } } if ( ! empty( $stuck['url'] ) ) { $html .= ''; } $cls = 'nx-logo' . ( ! empty( $stuck['url'] ) ? ' has-stuck' : '' ); return self::v( $s, 'link', true ) ? '' . $html . '' : '' . $html . ''; } private static function w_site_title( array $s ) { $tag = in_array( self::v( $s, 'tag', 'div' ), array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'span' ), true ) ? self::v( $s, 'tag', 'div' ) : 'div'; $text = esc_html( get_bloginfo( self::v( $s, 'tagline', false ) ? 'description' : 'name' ) ); return '<' . $tag . ' class="nx-site-title">' . ( self::v( $s, 'link', true ) ? '' . $text . '' : $text ) . ''; } private static function w_breadcrumbs( array $s ) { $sep = array( 'chevron' => '›', 'slash' => '/', 'arrow' => '→', 'dot' => '•', )[ self::v( $s, 'sep', 'chevron' ) ] ?? '›'; $items = array( array( self::v( $s, 'home', __( 'Home', 'akram' ) ), home_url( '/' ) ) ); if ( is_singular() ) { $p = get_queried_object(); if ( $p && 'page' !== $p->post_type ) { $pto = get_post_type_object( $p->post_type ); if ( 'post' === $p->post_type ) { $blog = (int) get_option( 'page_for_posts' ); if ( $blog ) { $items[] = array( get_the_title( $blog ), get_permalink( $blog ) ); } $cats = get_the_category( $p->ID ); if ( $cats ) { foreach ( array_reverse( get_ancestors( $cats[0]->term_id, 'category' ) ) as $a ) { $items[] = array( get_cat_name( $a ), get_category_link( $a ) ); } $items[] = array( $cats[0]->name, get_category_link( $cats[0] ) ); } } elseif ( $pto && $pto->has_archive ) { $items[] = array( $pto->labels->name, get_post_type_archive_link( $p->post_type ) ); } } if ( $p ) { foreach ( array_reverse( get_post_ancestors( $p ) ) as $a ) { $items[] = array( get_the_title( $a ), get_permalink( $a ) ); } $items[] = array( get_the_title( $p ), '' ); } } elseif ( is_category() || is_tag() || is_tax() ) { $t = get_queried_object(); foreach ( array_reverse( get_ancestors( $t->term_id, $t->taxonomy ) ) as $a ) { $at = get_term( $a, $t->taxonomy ); $items[] = array( $at->name, get_term_link( $at ) ); } $items[] = array( $t->name, '' ); } elseif ( is_search() ) { /* translators: %s: search query */ $items[] = array( sprintf( __( 'Search: %s', 'akram' ), get_search_query() ), '' ); } elseif ( is_404() ) { $items[] = array( __( 'Page not found', 'akram' ), '' ); } elseif ( is_post_type_archive() ) { $items[] = array( post_type_archive_title( '', false ), '' ); } elseif ( is_author() ) { $items[] = array( get_the_author_meta( 'display_name', get_queried_object_id() ), '' ); } elseif ( is_archive() ) { $items[] = array( wp_strip_all_tags( get_the_archive_title() ), '' ); } elseif ( is_home() && ! is_front_page() ) { $items[] = array( get_the_title( (int) get_option( 'page_for_posts' ) ), '' ); } if ( ! self::v( $s, 'current', true ) && count( $items ) > 1 ) { array_pop( $items ); } $html = ''; if ( self::v( $s, 'schema', true ) ) { $html .= wp_get_inline_script_tag( wp_json_encode( array( '@context' => 'https://schema.org', '@type' => 'BreadcrumbList', 'itemListElement' => $ld, ), JSON_HEX_TAG | JSON_HEX_AMP | JSON_UNESCAPED_SLASHES ), array( 'type' => 'application/ld+json' ) ); } return $html; } private static function w_shortcode( array $s ) { return do_shortcode( (string) self::v( $s, 'code', '' ) ); } private static function w_sitemap( array $s ) { $out = '
'; foreach ( array_filter( array_map( 'trim', explode( ',', (string) self::v( $s, 'types', 'page,post' ) ) ) ) as $type ) { if ( ! post_type_exists( $type ) ) { continue; } $out .= '

' . esc_html( get_post_type_object( $type )->labels->name ) . '

    '; if ( is_post_type_hierarchical( $type ) ) { $out .= wp_list_pages( array( 'post_type' => $type, 'title_li' => '', 'echo' => false, ) ); } else { foreach ( get_posts( array( 'post_type' => $type, 'posts_per_page' => 100, ) ) as $p ) { $out .= '
  • ' . esc_html( get_the_title( $p ) ) . '
  • '; } } $out .= '
'; } return $out . '
'; } private static function w_login( array $s ) { if ( is_user_logged_in() ) { $u = wp_get_current_user(); return ''; } $form = wp_login_form( array( 'echo' => false, 'redirect' => self::v( $s, 'redirect', '' ) ? esc_url_raw( self::v( $s, 'redirect' ) ) : ( is_singular() ? get_permalink() : home_url( '/' ) ), 'remember' => (bool) self::v( $s, 'remember', true ), ) ); $links = array(); if ( self::v( $s, 'lost', true ) ) { $links[] = '' . esc_html__( 'Lost your password?', 'akram' ) . ''; } if ( self::v( $s, 'register', false ) && get_option( 'users_can_register' ) ) { $links[] = '' . esc_html__( 'Create an account', 'akram' ) . ''; } return ''; } // ------------------------------------------------------------------ // Single post parts // ------------------------------------------------------------------ private static function w_post_title( array $s ) { $tag = in_array( self::v( $s, 'tag', 'h1' ), array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'span' ), true ) ? self::v( $s, 'tag', 'h1' ) : 'h1'; $title = esc_html( wp_strip_all_tags( get_the_title() ) ); return '<' . $tag . ' class="nx-h">' . ( self::v( $s, 'link', false ) ? '' . $title . '' : $title ) . ''; } private static function w_post_content( array $s, $id, $post_id ) { $pid = get_the_ID(); if ( ! $pid ) { return ''; } if ( in_array( $pid, Frontend::current_stack(), true ) ) { return ''; } $content = get_the_content( null, false, $pid ); return '
' . apply_filters( 'the_content', $content ) . '
'; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- reviewed. } private static function w_post_excerpt( array $s ) { return '

' . esc_html( wp_trim_words( get_the_excerpt(), (int) self::v( $s, 'len', 40 ) ) ) . '

'; } private static function w_featured_image( array $s ) { $tid = get_post_thumbnail_id(); if ( ! $tid ) { $fb = $s['fallback'] ?? null; return ! empty( $fb['url'] ) ? '
' : ''; } $cap = self::v( $s, 'caption', false ) ? wp_get_attachment_caption( $tid ) : ''; return '
' . wp_get_attachment_image( $tid, sanitize_key( self::v( $s, 'size', 'large' ) ), false, array( 'loading' => 'eager', 'fetchpriority' => 'high', ) ) . ( $cap ? '
' . esc_html( $cap ) . '
' : '' ) . '
'; } private static function w_post_meta( array $s ) { $items = (array) self::v( $s, 'items', array( 'author', 'date', 'cats', 'read' ) ); $ic = self::v( $s, 'icons', false ); $out = array(); foreach ( $items as $k ) { switch ( $k ) { case 'avatar': $out[] = get_avatar( get_the_author_meta( 'ID' ), 28 ); break; case 'author': $out[] = ( $ic ? self::icon( 'user' ) : '' ) . '' . esc_html( get_the_author() ) . ''; break; case 'date': $out[] = ( $ic ? self::icon( 'calendar' ) : '' ) . ''; break; case 'modified': /* translators: %s: date */ $out[] = ( $ic ? self::icon( 'clock' ) : '' ) . esc_html( sprintf( __( 'Updated %s', 'akram' ), get_the_modified_date() ) ); break; case 'cats': $c = get_the_category_list( ', ' ); if ( $c ) { $out[] = ( $ic ? self::icon( 'folder' ) : '' ) . wp_kses_post( $c ); } break; case 'tags': $t = get_the_tag_list( '', ', ' ); if ( $t && ! is_wp_error( $t ) ) { $out[] = ( $ic ? self::icon( 'tag' ) : '' ) . wp_kses_post( $t ); } break; case 'comments': $out[] = ( $ic ? self::icon( 'comment' ) : '' ) . esc_html( sprintf( /* translators: %d: comment count */ _n( '%d comment', '%d comments', get_comments_number(), 'akram' ), get_comments_number() ) ); break; case 'read': $out[] = ( $ic ? self::icon( 'clock' ) : '' ) . esc_html( Dynamic::value( 'reading_time', array() ) ); break; } } return '
' . implode( '', array_map( static function ( $x ) { return '' . $x . ''; }, $out ) ) . '
'; } private static function w_author_box( array $s ) { $a = (int) get_post_field( 'post_author', get_the_ID() ); if ( ! $a ) { return ''; } $name = esc_html( get_the_author_meta( 'display_name', $a ) ); if ( self::v( $s, 'link', true ) ) { $name = '' . $name . ''; } return '
' . ( self::v( $s, 'avatar', true ) ? get_avatar( $a, 144 ) : '' ) . '

' . $name . '

' . ( self::v( $s, 'bio', true ) ? '

' . esc_html( get_the_author_meta( 'description', $a ) ) . '

' : '' ) . '
'; } private static function w_post_nav( array $s ) { $same = (bool) self::v( $s, 'sameTerm', false ); $prev = get_previous_post( $same ); $next = get_next_post( $same ); $mk = static function ( $p, $label, $cls ) use ( $s ) { if ( ! $p ) { return ''; } $thumb = self::v( $s, 'thumbs', false ) ? get_the_post_thumbnail( $p, 'thumbnail' ) : ''; $text = ( self::v( $s, 'labels', true ) ? '' . esc_html( $label ) . '' : '' ) . ( self::v( $s, 'titles', true ) ? '' . esc_html( get_the_title( $p ) ) . '' : '' ); return '' . ( 'is-prev' === $cls ? $thumb . '' . $text . '' : '' . $text . '' . $thumb ) . ''; }; return ''; } private static function w_comments( array $s, $id, $post_id, $preview ) { if ( $preview ) { return '
' . esc_html__( 'Comments', 'akram' ) . '' . esc_html__( 'The comment list and form render on the live page.', 'akram' ) . '
'; } if ( ! comments_open() && ! get_comments_number() ) { return ''; } ob_start(); comments_template(); return ob_get_clean(); } private static function w_archive_title( array $s ) { $tag = in_array( self::v( $s, 'tag', 'h1' ), array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'span' ), true ) ? self::v( $s, 'tag', 'h1' ) : 'h1'; if ( ! self::v( $s, 'prefix', false ) ) { add_filter( 'get_the_archive_title_prefix', '__return_empty_string', 99 ); } $title = is_search() ? sprintf( /* translators: %s: query */ __( 'Results for “%s”', 'akram' ), get_search_query() ) : ( is_home() ? get_the_title( (int) get_option( 'page_for_posts' ) ) : get_the_archive_title() ); remove_filter( 'get_the_archive_title_prefix', '__return_empty_string', 99 ); return '<' . $tag . ' class="nx-h">' . wp_kses_post( $title ?: __( 'Blog', 'akram' ) ) . ''; } private static function w_archive_desc() { return '
' . wp_kses_post( get_the_archive_description() ) . '
'; } private static function w_terms( array $s ) { $tax = sanitize_key( self::v( $s, 'tax', 'category' ) ); $terms = get_the_terms( get_the_ID(), $tax ); if ( ! $terms || is_wp_error( $terms ) ) { return ''; } $links = array_map( static function ( $t ) { return ''; }, $terms ); return '
' . implode( 'text' === self::v( $s, 'style', 'pills' ) ? ', ' : '', $links ) . '
'; } // ------------------------------------------------------------------ // WooCommerce // ------------------------------------------------------------------ private static function product() { if ( ! function_exists( 'wc_get_product' ) ) { return null; } global $product; $p = wc_get_product( get_the_ID() ); if ( $p ) { $product = $p; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited, WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WooCommerce's own global product, needed by its template functions. } return $p; } private static function w_products( array $s, $id, $post_id, $preview = false, $page = null, $term = '' ) { if ( ! function_exists( 'wc_get_product' ) ) { return $preview ? '
' . esc_html__( 'WooCommerce is not active.', 'akram' ) . '
' : ''; } $page = $page ?: max( 1, isset( $_GET['nxp'] ) ? (int) $_GET['nxp'] : 1 ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only request value; it changes nothing and is sanitised. $count = max( 1, (int) self::v( $s, 'count', 8 ) ); $args = array( 'post_type' => 'product', 'post_status' => 'publish', 'posts_per_page' => $count, 'paged' => $page, ); $ob = self::v( $s, 'orderby', 'date' ); $order = 'ASC' === self::v( $s, 'order', 'DESC' ) ? 'ASC' : 'DESC'; $ordering = WC()->query->get_catalog_ordering_args( 'popularity' === $ob ? 'popularity' : ( 'rating' === $ob ? 'rating' : ( 'price' === $ob ? 'price' : $ob ) ), $order ); $args = array_merge( $args, array( 'orderby' => $ordering['orderby'], 'order' => $ordering['order'], ) ); if ( ! empty( $ordering['meta_key'] ) ) { $args['meta_key'] = $ordering['meta_key']; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- bounded query on a builder meta key. } $tax = array( 'relation' => 'AND' ); $meta = array(); switch ( self::v( $s, 'query', 'recent' ) ) { case 'featured': $tax[] = array( 'taxonomy' => 'product_visibility', 'field' => 'name', 'terms' => 'featured', ); break; case 'sale': $args['post__in'] = array_merge( array( 0 ), wc_get_product_ids_on_sale() ); break; case 'best': $args['meta_key'] = 'total_sales'; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- bounded query on a builder meta key. $args['orderby'] = 'meta_value_num'; break; case 'rated': $args['meta_key'] = '_wc_average_rating'; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- bounded query on a builder meta key. $args['orderby'] = 'meta_value_num'; break; case 'related': $args['post__in'] = array_merge( array( 0 ), wc_get_related_products( get_the_ID(), $count ) ); break; case 'upsell': $p = self::product(); $args['post__in'] = array_merge( array( 0 ), $p ? $p->get_upsell_ids() : array() ); break; case 'cross': $args['post__in'] = array_merge( array( 0 ), WC()->cart ? WC()->cart->get_cross_sells() : array() ); break; case 'manual': $args['post__in'] = array_merge( array( 0 ), array_map( 'absint', explode( ',', (string) self::v( $s, 'ids', '' ) ) ) ); $args['orderby'] = 'post__in'; break; case 'current': global $wp_query; foreach ( array( 'product_cat', 'product_tag', 's' ) as $k ) { if ( ! empty( $wp_query->query[ $k ] ) ) { $args[ $k ] = sanitize_text_field( $wp_query->query[ $k ] ); } } break; } $cats = array_filter( array_map( 'trim', explode( ',', (string) self::v( $s, 'cats', '' ) ) ) ); if ( $cats ) { $tax[] = array( 'taxonomy' => 'product_cat', 'field' => 'slug', 'terms' => $cats, ); } if ( ! in_array( self::v( $s, 'query', 'recent' ), array( 'related', 'upsell', 'cross', 'manual' ), true ) && ! self::v( $s, 'ignoreFilters', false ) ) { self::apply_product_filters( $args, $tax, $meta ); } if ( $term ) { $tax[] = array( 'taxonomy' => 'product_cat', 'terms' => array( (int) $term ), ); } if ( self::v( $s, 'hideOos', false ) ) { $tax[] = array( 'taxonomy' => 'product_visibility', 'field' => 'name', 'terms' => 'outofstock', 'operator' => 'NOT IN', ); } $tax[] = array( 'taxonomy' => 'product_visibility', 'field' => 'name', 'terms' => array( 'exclude-from-catalog' ), 'operator' => 'NOT IN', ); $args['tax_query'] = $tax; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- bounded query on a builder meta key. $q = new \WP_Query( $args ); $show = (array) self::v( $s, 'show', array( 'cat', 'rating', 'price', 'atc', 'sale' ) ); $items = ''; while ( $q->have_posts() ) { $q->the_post(); $p = wc_get_product( get_the_ID() ); if ( ! $p ) { continue; } $GLOBALS['product'] = $p; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited, WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WooCommerce's own global product, needed by its template functions. $img = $p->get_image( 'woocommerce_thumbnail', array( 'loading' => 'lazy' ) ); $gal = $p->get_gallery_image_ids(); $alt = self::v( $s, 'swap', true ) && $gal ? wp_get_attachment_image( $gal[0], 'woocommerce_thumbnail', false, array( 'class' => 'is-alt', 'loading' => 'lazy', 'alt' => '', ) ) : ''; $sale = in_array( 'sale', $show, true ) && $p->is_on_sale() ? '' . esc_html( Dynamic::value( 'woo_sale_badge', array() ) ?: __( 'Sale', 'akram' ) ) . '' : ''; $cat = ''; if ( in_array( 'cat', $show, true ) ) { $terms = get_the_terms( $p->get_id(), 'product_cat' ); $cat = $terms && ! is_wp_error( $terms ) ? '' . esc_html( $terms[0]->name ) . '' : ''; } $atc = ''; if ( in_array( 'atc', $show, true ) ) { ob_start(); woocommerce_template_loop_add_to_cart(); $atc = '
' . ob_get_clean() . '
'; } $acts = ''; foreach ( array( 'wish' => array( 'heart', __( 'Add to wishlist', 'akram' ) ), 'quick' => array( 'eye', __( 'Quick view', 'akram' ) ), 'compare' => array( 'compare', __( 'Compare', 'akram' ) ), ) as $k => $a ) { if ( in_array( $k, $show, true ) ) { $acts .= ''; } } if ( $acts ) { Frontend::add_module( 'shop' ); $acts = '
' . $acts . '
'; } $items .= '
  • ' . $acts . '' . $sale . $img . $alt . '' . $cat . '

    ' . esc_html( $p->get_name() ) . '

    ' . ( in_array( 'rating', $show, true ) ? wc_get_rating_html( (float) $p->get_average_rating() ) : '' ) . ( in_array( 'price', $show, true ) ? '' . $p->get_price_html() . '' : '' ) . $atc . '
  • '; } wp_reset_postdata(); if ( ! $items ) { return $preview ? '
    ' . esc_html__( 'No products match this query yet.', 'akram' ) . '
    ' : ''; } $sorting = ''; if ( self::v( $s, 'sorting', false ) && ! $preview ) { ob_start(); woocommerce_result_count(); woocommerce_catalog_ordering(); $sorting = '
    ' . ob_get_clean() . '
    '; } $pager = self::pager( $q, self::v( $s, 'pager', 'none' ), $page, $s ); self::$parts = array( 'items' => $items, 'max' => (int) $q->max_num_pages, 'pager' => $pager, ); $data = array( 'post' => (int) $post_id, 'el' => $id, 'pager' => self::v( $s, 'pager', 'none' ), 'max' => (int) $q->max_num_pages, 'page' => $page, ); return '
    ' . $sorting . '
      ' . $items . '
    ' . $pager . '
    '; } private static function w_product_related( array $s, $id, $post_id, $preview ) { $s['query'] = 'related'; return self::w_products( $s, $id, $post_id, $preview ); } private static function w_product_categories( array $s ) { $parent = self::v( $s, 'parent', 'top' ); $args = array( 'taxonomy' => 'product_cat', 'hide_empty' => (bool) self::v( $s, 'hideEmpty', true ), 'number' => (int) self::v( $s, 'count', 8 ), ); if ( 'top' === $parent ) { $args['parent'] = 0; } elseif ( 'current' === $parent && is_tax( 'product_cat' ) ) { $args['parent'] = get_queried_object_id(); } $terms = get_terms( $args ); if ( ! $terms || is_wp_error( $terms ) ) { return ''; } $out = ''; } /** Output of a WooCommerce product part; $cb is a closure from the widget renderers below. */ private static function woo_part( \Closure $cb ) { $p = self::product(); if ( ! $p ) { return ''; } ob_start(); $cb( $p ); return '
    ' . ob_get_clean() . '
    '; } private static function w_product_title( array $s ) { $p = self::product(); $tag = in_array( self::v( $s, 'tag', 'h1' ), array( 'h1', 'h2', 'h3', 'div' ), true ) ? self::v( $s, 'tag', 'h1' ) : 'h1'; return $p ? '<' . $tag . ' class="nx-h product_title">' . esc_html( $p->get_name() ) . '' : ''; } private static function w_product_price() { return self::woo_part( static function ( $p ) { echo '

    ' . wp_kses_post( $p->get_price_html() ) . '

    '; } ); } private static function w_product_atc( array $s ) { $pid = (int) self::v( $s, 'product', 0 ); if ( $pid && function_exists( 'wc_get_product' ) ) { $p = wc_get_product( $pid ); if ( ! $p ) { return ''; } $text = self::v( $s, 'text', '' ) ?: $p->add_to_cart_text(); return ''; } return self::woo_part( static function () { echo '
    '; woocommerce_template_single_add_to_cart(); echo '
    '; } ); } private static function w_product_gallery() { return self::woo_part( static function () { wp_enqueue_script( 'wc-single-product' ); woocommerce_show_product_images(); } ); } private static function w_product_rating( array $s ) { return self::woo_part( static function ( $p ) use ( $s ) { echo wp_kses_post( wc_get_rating_html( (float) $p->get_average_rating(), $p->get_rating_count() ) ); if ( self::v( $s, 'count', true ) && $p->get_review_count() ) { echo ' ' . esc_html( sprintf( /* translators: %d: reviews */ _n( '(%d review)', '(%d reviews)', $p->get_review_count(), 'akram' ), $p->get_review_count() ) ) . ''; } } ); } private static function w_product_stock() { return self::woo_part( static function ( $p ) { echo wp_kses_post( wc_get_stock_html( $p ) ?: '

    ' . esc_html__( 'In stock', 'akram' ) . '

    ' ); } ); } private static function w_product_meta( array $s ) { return self::woo_part( static function ( $p ) use ( $s ) { $items = (array) self::v( $s, 'items', array( 'sku', 'cats', 'tags' ) ); echo '
    '; if ( in_array( 'sku', $items, true ) && $p->get_sku() ) { echo '' . esc_html__( 'SKU:', 'akram' ) . ' ' . esc_html( $p->get_sku() ) . ' '; } if ( in_array( 'cats', $items, true ) ) { echo wp_kses_post( wc_get_product_category_list( $p->get_id(), ', ', '' . esc_html__( 'Category:', 'akram' ) . ' ', ' ' ) ); } if ( in_array( 'tags', $items, true ) ) { echo wp_kses_post( wc_get_product_tag_list( $p->get_id(), ', ', '' . esc_html__( 'Tags:', 'akram' ) . ' ', '' ) ); } echo '
    '; } ); } private static function w_product_short() { return self::woo_part( static function ( $p ) { echo '
    ' . wp_kses_post( apply_filters( 'woocommerce_short_description', $p->get_short_description() ) ) . '
    '; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- core or third-party hook. } ); } private static function w_product_tabs() { return self::woo_part( static function () { woocommerce_output_product_data_tabs(); } ); } private static function w_cart_icon( array $s ) { if ( ! function_exists( 'WC' ) || ! WC()->cart ) { return '' . self::icon( 'cart' ) . '0'; } $n = WC()->cart->get_cart_contents_count(); return '' . self::icon( 'cart' ) . '' . (int) $n . '' . ( self::v( $s, 'total', true ) ? '' . wp_kses_post( WC()->cart->get_cart_total() ) . '' : '' ) . ''; } private static function w_wc_page( array $s, $id, $post_id, $preview ) { $map = array( 'cart' => 'woocommerce_cart', 'checkout' => 'woocommerce_checkout', 'account' => 'woocommerce_my_account', 'tracking' => 'woocommerce_order_tracking', ); $sc = $map[ self::v( $s, 'page', 'cart' ) ] ?? 'woocommerce_cart'; if ( $preview && 'woocommerce_checkout' === $sc ) { return '
    ' . esc_html__( 'Checkout', 'akram' ) . '' . esc_html__( 'The checkout form renders on the live page.', 'akram' ) . '
    '; } return '
    ' . do_shortcode( '[' . $sc . ']' ) . '
    '; } /** Apply Product Filter widget parameters (same names WooCommerce uses) to a product query. */ private static function apply_product_filters( array &$args, array &$tax, array &$meta ) { // phpcs:disable WordPress.Security.NonceVerification.Recommended if ( ! empty( $_GET['product_cat'] ) ) { $tax[] = array( 'taxonomy' => 'product_cat', 'field' => 'slug', 'terms' => array_map( 'sanitize_title', explode( ',', sanitize_text_field( wp_unslash( $_GET['product_cat'] ) ) ) ), ); } foreach ( $_GET as $key => $val ) { if ( 0 !== strpos( (string) $key, 'filter_' ) || ! is_string( $val ) || '' === $val ) { continue; } $taxonomy = 'pa_' . sanitize_title( substr( $key, 7 ) ); if ( taxonomy_exists( $taxonomy ) ) { $tax[] = array( 'taxonomy' => $taxonomy, 'field' => 'slug', 'terms' => array_map( 'sanitize_title', explode( ',', sanitize_text_field( wp_unslash( $val ) ) ) ), 'operator' => 'IN', ); } } $min = isset( $_GET['min_price'] ) && '' !== $_GET['min_price'] ? (float) $_GET['min_price'] : null; $max = isset( $_GET['max_price'] ) && '' !== $_GET['max_price'] ? (float) $_GET['max_price'] : null; if ( null !== $min || null !== $max ) { $meta[] = array( 'key' => '_price', 'value' => array( null === $min ? 0 : $min, null === $max ? PHP_INT_MAX : $max ), 'compare' => 'BETWEEN', 'type' => 'DECIMAL(10,2)', ); $args['meta_query'] = array_merge( (array) ( $args['meta_query'] ?? array() ), $meta ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- bounded query on a builder meta key. } if ( ! empty( $_GET['rating_filter'] ) ) { $ratings = array_filter( array_map( 'absint', explode( ',', sanitize_text_field( wp_unslash( $_GET['rating_filter'] ) ) ) ) ); if ( $ratings ) { $tax[] = array( 'taxonomy' => 'product_visibility', 'field' => 'name', 'terms' => array_map( static function ( $r ) { return 'rated-' . min( 5, $r ); }, $ratings ), ); } } if ( ! empty( $_GET['nx_sale'] ) ) { $args['post__in'] = array_merge( array( 0 ), isset( $args['post__in'] ) ? array_intersect( $args['post__in'], wc_get_product_ids_on_sale() ) : wc_get_product_ids_on_sale() ); } if ( ! empty( $_GET['nx_stock'] ) ) { $tax[] = array( 'taxonomy' => 'product_visibility', 'field' => 'name', 'terms' => 'outofstock', 'operator' => 'NOT IN', ); } if ( ! empty( $_GET['orderby'] ) && in_array( $_GET['orderby'], array( 'price', 'price-desc', 'popularity', 'rating', 'date' ), true ) ) { $ob = sanitize_key( $_GET['orderby'] ); $ordering = WC()->query->get_catalog_ordering_args( 'price-desc' === $ob ? 'price' : $ob, 'price-desc' === $ob ? 'DESC' : ( 'price' === $ob ? 'ASC' : 'DESC' ) ); $args = array_merge( $args, array( 'orderby' => $ordering['orderby'], 'order' => $ordering['order'], ) ); if ( ! empty( $ordering['meta_key'] ) ) { $args['meta_key'] = $ordering['meta_key']; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- bounded query on a builder meta key. } } // phpcs:enable } private static function w_product_filter( array $s, $id, $post_id, $preview ) { if ( ! function_exists( 'wc_get_product' ) ) { return $preview ? '
    ' . esc_html__( 'WooCommerce is not active.', 'akram' ) . '
    ' : ''; } // phpcs:disable WordPress.Security.NonceVerification.Recommended $get = static function ( $k ) { return isset( $_GET[ $k ] ) ? sanitize_text_field( wp_unslash( $_GET[ $k ] ) ) : ''; }; $parts = (array) self::v( $s, 'parts', array( 'cats', 'price', 'attrs', 'rating', 'sale', 'stock', 'sort' ) ); $open = self::v( $s, 'collapsed', false ) ? '' : ' open'; $groups = ''; if ( in_array( 'cats', $parts, true ) ) { $sel = array_filter( explode( ',', $get( 'product_cat' ) ) ); $terms = get_terms( array( 'taxonomy' => 'product_cat', 'hide_empty' => true, 'number' => 40, ) ); $list = ''; foreach ( is_array( $terms ) ? $terms : array() as $t ) { if ( 'uncategorized' === $t->slug ) { continue; } $list .= ''; } if ( $list ) { $groups .= '
    ' . esc_html__( 'Categories', 'akram' ) . self::icon( 'chevron' ) . '
    ' . $list . '
    '; } } if ( in_array( 'price', $parts, true ) ) { global $wpdb; // The store's price range in one query (no WordPress API aggregates meta); it takes no user input. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $range = $wpdb->get_row( "SELECT MIN(CAST(meta_value AS DECIMAL(10,2))) AS lo, MAX(CAST(meta_value AS DECIMAL(10,2))) AS hi FROM {$wpdb->postmeta} WHERE meta_key = '_price' AND meta_value <> ''" ); $lo = $range ? floor( (float) $range->lo ) : 0; $hi = $range ? ceil( (float) $range->hi ) : 1000; $cur = get_woocommerce_currency_symbol(); $groups .= '
    ' . esc_html__( 'Price', 'akram' ) . self::icon( 'chevron' ) . '
    –
    '; } if ( in_array( 'attrs', $parts, true ) && function_exists( 'wc_get_attribute_taxonomies' ) ) { foreach ( wc_get_attribute_taxonomies() as $a ) { $tx = wc_attribute_taxonomy_name( $a->attribute_name ); $terms = get_terms( array( 'taxonomy' => $tx, 'hide_empty' => true, ) ); if ( ! $terms || is_wp_error( $terms ) ) { continue; } $key = 'filter_' . $a->attribute_name; $sel = array_filter( explode( ',', $get( $key ) ) ); $color = in_array( strtolower( $a->attribute_name ), array( 'color', 'colour' ), true ); $list = ''; foreach ( $terms as $t ) { $sw = $color ? '' : ''; $list .= ''; } $groups .= '
    ' . esc_html( $a->attribute_label ) . self::icon( 'chevron' ) . '
    ' . $list . '
    '; } } if ( in_array( 'rating', $parts, true ) ) { $sel = array_filter( explode( ',', $get( 'rating_filter' ) ) ); $list = ''; for ( $r = 5; $r >= 1; $r-- ) { $list .= ''; } $groups .= '
    ' . esc_html__( 'Rating', 'akram' ) . self::icon( 'chevron' ) . '
    ' . $list . '
    '; } $toggles = ''; if ( in_array( 'sale', $parts, true ) ) { $toggles .= ''; } if ( in_array( 'stock', $parts, true ) ) { $toggles .= ''; } $sort = ''; if ( in_array( 'sort', $parts, true ) ) { $opts = array( '' => __( 'Default sorting', 'akram' ), 'popularity' => __( 'Popularity', 'akram' ), 'rating' => __( 'Average rating', 'akram' ), 'date' => __( 'Latest', 'akram' ), 'price' => __( 'Price: low to high', 'akram' ), 'price-desc' => __( 'Price: high to low', 'akram' ), ); $sort = ''; } // phpcs:enable $cfg = array( 'ajax' => (bool) self::v( $s, 'ajax', true ), 'auto' => (bool) self::v( $s, 'auto', true ), ); return '
    ' . self::icon( 'filter' ) . '' . esc_html( self::v( $s, 'title', __( 'Filter products', 'akram' ) ) ) . '
    ' . $sort . $toggles . $groups . ( $cfg['auto'] ? '' : '' ) . '
    '; } private static function w_news_ticker( array $s ) { $items = array(); if ( 'posts' === self::v( $s, 'source', 'posts' ) ) { $q = new \WP_Query( array( 'post_type' => post_type_exists( (string) self::v( $s, 'postType', 'post' ) ) ? self::v( $s, 'postType', 'post' ) : 'post', 'posts_per_page' => max( 1, min( 30, (int) self::v( $s, 'count', 8 ) ) ), 'post_status' => 'publish', 'ignore_sticky_posts' => true, 'no_found_rows' => true, ) ); foreach ( $q->posts as $p ) { $items[] = '' . esc_html( get_the_title( $p ) ) . '' . ( self::v( $s, 'date', true ) ? ' ' : '' ); } } else { foreach ( preg_split( '/\r?\n/', (string) self::v( $s, 'lines', '' ) ) as $line ) { $line = trim( $line ); if ( '' === $line ) { continue; } $parts = array_map( 'trim', explode( '|', $line, 2 ) ); $items[] = ! empty( $parts[1] ) ? '' . esc_html( $parts[0] ) . '' : '' . esc_html( $parts[0] ) . ''; } } if ( ! $items ) { return ''; } $fx = self::v( $s, 'fx', 'marquee' ); $label = self::v( $s, 'label', __( 'Breaking', 'akram' ) ); $list = '
  • ' . implode( '
  • ', $items ) . '
  • '; if ( 'marquee' === $fx ) { $track = '
      ' . $list . '
    '; } else { Frontend::add_module( 'ticker' ); $track = '
      ' . $list . '
    '; } return '
    ' . ( $label ? '' . esc_html( $label ) . '' : '' ) . '
    ' . $track . '
    '; } private static function w_sidebar( array $s ) { $id = sanitize_key( self::v( $s, 'area', 'sidebar-1' ) ); if ( ! is_active_sidebar( $id ) ) { return ''; } ob_start(); dynamic_sidebar( $id ); return '
    ' . ob_get_clean() . '
    '; } private static function w_terms_list( array $s ) { $tax = sanitize_key( self::v( $s, 'tax', 'category' ) ); if ( ! taxonomy_exists( $tax ) ) { return ''; } $terms = get_terms( array( 'taxonomy' => $tax, 'hide_empty' => (bool) self::v( $s, 'hideEmpty', true ), 'number' => max( 1, (int) self::v( $s, 'count', 20 ) ), 'orderby' => 'cloud' === self::v( $s, 'style', 'list' ) || 'count' === self::v( $s, 'orderby', 'name' ) ? 'count' : 'name', 'order' => 'count' === self::v( $s, 'orderby', 'name' ) ? 'DESC' : 'ASC', 'parent' => self::v( $s, 'top', false ) ? 0 : '', ) ); if ( ! $terms || is_wp_error( $terms ) ) { return ''; } $style = self::v( $s, 'style', 'list' ); $max = max( 1, max( wp_list_pluck( $terms, 'count' ) ) ); $cur = is_tax() || is_category() || is_tag() ? get_queried_object_id() : 0; $out = ''; foreach ( $terms as $t ) { $size = 'cloud' === $style ? ' style="font-size:' . round( 0.85 + ( $t->count / $max ) * 0.9, 2 ) . 'em"' : ''; $out .= '
  • term_id ? ' aria-current="page"' : '' ) . '>' . esc_html( $t->name ) . '' . ( self::v( $s, 'counts', true ) && 'cloud' !== $style ? '' . (int) $t->count . '' : '' ) . '
  • '; } return '
      ' . $out . '
    '; } private static function w_social_feed( array $s, $id, $post_id, $preview ) { $source = (string) self::v( $s, 'source', 'rss' ); $items = Feeds::items( $source, 'youtube' === $source ? self::v( $s, 'channel', '' ) : self::v( $s, 'url', '' ), (int) self::v( $s, 'count', 6 ) ); if ( is_wp_error( $items ) ) { return $preview ? '
    ' . esc_html__( 'Social feed', 'akram' ) . '' . esc_html( $items->get_error_message() ) . '
    ' : ''; } $show = (array) self::v( $s, 'show', array( 'image', 'title', 'date' ) ); $blank = self::v( $s, 'newTab', true ) ? ' target="_blank" rel="noopener"' : ''; $out = ''; foreach ( $items as $it ) { $play = 'youtube' === $source ? '' : ''; $img = in_array( 'image', $show, true ) && $it['image'] ? '' . $play . '' : ''; $title = in_array( 'title', $show, true ) && $it['title'] ? '' . esc_html( $it['title'] ) . '' : ''; $text = in_array( 'text', $show, true ) && $it['text'] ? '' . esc_html( $it['text'] ) . '' : ''; $date = in_array( 'date', $show, true ) && $it['date'] ? '' : ''; $out .= '
  • ' . $img . ( $title || $text || $date ? '' . $title . $text . $date . '' : '' ) . '
  • '; } return '
      ' . $out . '
    '; } private static function w_google_reviews( array $s, $id, $post_id, $preview ) { $d = Feeds::google_reviews( self::v( $s, 'place', '' ) ); if ( is_wp_error( $d ) ) { return $preview ? '
    ' . esc_html__( 'Google reviews', 'akram' ) . '' . esc_html( $d->get_error_message() ) . '
    ' : ''; } $min = (int) self::v( $s, 'min', 4 ); $star = static function ( $n ) { $o = ''; for ( $i = 1; $i <= 5; $i++ ) { $o .= '★'; } $label = sprintf( /* translators: %s: rating */ __( '%s out of 5 stars', 'akram' ), number_format_i18n( $n, 1 ) ); return '' . $o . ''; }; $cards = ''; foreach ( array_slice( $d['reviews'], 0, (int) self::v( $s, 'count', 5 ) ) as $r ) { if ( $r['rating'] < $min || '' === trim( $r['text'] ) ) { continue; } $cards .= '
    ' . ( $r['photo'] ? '' : '' ) . '' . esc_html( $r['author'] ) . '' . esc_html( $r['time'] ) . '
    ' . $star( $r['rating'] ) . '
    ' . esc_html( wp_trim_words( $r['text'], (int) self::v( $s, 'words', 45 ) ) ) . '
    '; } $head = self::v( $s, 'summary', true ) ? '
    ' . esc_html( number_format_i18n( $d['rating'], 1 ) ) . '' . $star( $d['rating'] ) . '' . esc_html( sprintf( /* translators: %s: count */ _n( '%s review on Google', '%s reviews on Google', $d['total'], 'akram' ), number_format_i18n( $d['total'] ) ) ) . '' . ( $d['url'] ? '' . esc_html__( 'Write a review', 'akram' ) . '' : '' ) . '
    ' : ''; $schema = ''; if ( self::v( $s, 'schema', false ) && $d['total'] ) { $schema = wp_get_inline_script_tag( wp_json_encode( array( '@context' => 'https://schema.org', '@type' => 'LocalBusiness', 'name' => $d['name'], 'aggregateRating' => array( '@type' => 'AggregateRating', 'ratingValue' => $d['rating'], 'reviewCount' => $d['total'], ), ), JSON_HEX_TAG | JSON_HEX_AMP | JSON_UNESCAPED_SLASHES ), array( 'type' => 'application/ld+json' ) ); } return '
    ' . $head . '
    ' . $cards . '
    ' . $schema . '
    '; } private static function w_wc_notices() { if ( ! function_exists( 'wc_print_notices' ) ) { return ''; } ob_start(); wc_print_notices(); return '
    ' . ob_get_clean() . '
    '; } } /** * Menu walker: submenu indicators and mega-menu panels. */ class Nav_Walker extends \Walker_Nav_Menu { private $indicator; public function __construct( $indicator = 'chevron' ) { $this->indicator = $indicator; } public function start_el( &$output, $item, $depth = 0, $args = null, $id = 0 ) { $mega = (int) get_post_meta( $item->ID, '_akram_mega', true ); if ( $mega && 0 === $depth ) { $item->classes[] = 'nx-mega'; } parent::start_el( $output, $item, $depth, $args, $id ); $has = in_array( 'menu-item-has-children', (array) $item->classes, true ) || ( $mega && 0 === $depth ); if ( $has && 'none' !== $this->indicator ) { $icon = 'plus' === $this->indicator ? '' : ''; $output = preg_replace( '/<\/a>$/', '', $output ); } if ( $mega && 0 === $depth ) { $output .= '
    ' . Runtime::template( $mega ) . '
    '; } } }