2026-02-28 15:06:33 +01:00
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* Product Archive / Shop Page (Bootstrap 5 Layout)
|
|
|
|
|
*
|
|
|
|
|
* Renders the WooCommerce product archive content with a Bootstrap 5 layout
|
|
|
|
|
* featuring a responsive sidebar (offcanvas on mobile) and product card grid.
|
|
|
|
|
*
|
|
|
|
|
* This file is NOT included directly by WordPress. Instead, it is captured via
|
|
|
|
|
* output buffering by wc_bootstrap_render_product_archive() and injected into
|
|
|
|
|
* the parent theme's page shell (pages/page.html.twig). Therefore it does NOT
|
|
|
|
|
* call get_header()/get_footer() or render the wrapper hooks.
|
|
|
|
|
*
|
|
|
|
|
* @package WcBootstrap
|
|
|
|
|
* @since 0.1.0
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
defined( 'ABSPATH' ) || exit;
|
|
|
|
|
|
Fix 10 known bugs: catalog, single product, and account pages (v0.1.5)
Catalog: page title via woocommerce_page_title(), breadcrumbs, category
template rename (underscore), 3-column grid, single chevron on sort.
Single product: variable form data attributes + disabled CSS class fix
(WC JS only toggles CSS classes, not HTML disabled attribute), dark mode
select specificity (0,5,1) to beat WC's (0,4,3) background shorthand,
gallery main image in thumbnail strip with empty URL guard, related/
upsells setup_postdata for correct global $product, grouped product
loop logic rewrite.
Account: downloads via wc_get_customer_available_downloads().
New: product-gallery.js, sanitize_title filter, wc_setup_product_data()
and wp_reset_postdata() Twig functions, product-thumbnails.html.twig
suppressor. Removed obsolete PLAN.md and SETUP.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 03:33:31 +01:00
|
|
|
// Render breadcrumbs. The woocommerce_before_main_content hook is not fired here
|
|
|
|
|
// (to avoid the content wrapper), so breadcrumbs must be called directly.
|
|
|
|
|
// TemplateOverride will intercept global/breadcrumb.php and render the Bootstrap
|
|
|
|
|
// breadcrumb Twig template.
|
|
|
|
|
woocommerce_breadcrumb();
|
|
|
|
|
|
2026-02-28 15:06:33 +01:00
|
|
|
// Fire structured data hook (normally on woocommerce_before_main_content at priority 30).
|
|
|
|
|
do_action( 'woocommerce_shop_loop_header' );
|
|
|
|
|
|
|
|
|
|
$has_sidebar = is_active_sidebar( 'shop-sidebar' );
|
|
|
|
|
|
|
|
|
|
if ( woocommerce_product_loop() ) {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook: woocommerce_before_shop_loop.
|
|
|
|
|
*
|
|
|
|
|
* @hooked woocommerce_output_all_notices - 10
|
|
|
|
|
* @hooked woocommerce_result_count - 20
|
|
|
|
|
* @hooked woocommerce_catalog_ordering - 30
|
|
|
|
|
*/
|
|
|
|
|
do_action( 'woocommerce_before_shop_loop' );
|
|
|
|
|
|
|
|
|
|
?>
|
|
|
|
|
<div class="row g-4">
|
|
|
|
|
<?php if ( $has_sidebar ) : ?>
|
|
|
|
|
<aside class="col-lg-3 mb-4 mb-lg-0">
|
|
|
|
|
<button class="btn btn-outline-secondary w-100 d-lg-none mb-3"
|
|
|
|
|
type="button"
|
|
|
|
|
data-bs-toggle="offcanvas"
|
|
|
|
|
data-bs-target="#shopSidebar"
|
|
|
|
|
aria-controls="shopSidebar">
|
|
|
|
|
<i class="bi bi-funnel me-1" aria-hidden="true"></i>
|
|
|
|
|
<?php esc_html_e( 'Filters', 'wc-bootstrap' ); ?>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<div class="offcanvas-lg offcanvas-start"
|
|
|
|
|
id="shopSidebar"
|
|
|
|
|
tabindex="-1"
|
|
|
|
|
aria-labelledby="shopSidebarLabel">
|
|
|
|
|
<div class="offcanvas-header d-lg-none">
|
|
|
|
|
<h5 class="offcanvas-title" id="shopSidebarLabel">
|
|
|
|
|
<?php esc_html_e( 'Filters', 'wc-bootstrap' ); ?>
|
|
|
|
|
</h5>
|
|
|
|
|
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="<?php esc_attr_e( 'Close', 'wc-bootstrap' ); ?>"></button>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="offcanvas-body p-0">
|
|
|
|
|
<?php dynamic_sidebar( 'shop-sidebar' ); ?>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</aside>
|
|
|
|
|
<div class="col-lg-9">
|
|
|
|
|
<?php else : ?>
|
|
|
|
|
<div class="col-12">
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
|
|
<?php
|
|
|
|
|
woocommerce_product_loop_start();
|
|
|
|
|
|
|
|
|
|
if ( wc_get_loop_prop( 'total' ) ) {
|
|
|
|
|
while ( have_posts() ) {
|
|
|
|
|
the_post();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook: woocommerce_shop_loop.
|
|
|
|
|
*/
|
|
|
|
|
do_action( 'woocommerce_shop_loop' );
|
|
|
|
|
|
|
|
|
|
wc_get_template_part( 'content', 'product' );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
woocommerce_product_loop_end();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook: woocommerce_after_shop_loop.
|
|
|
|
|
*
|
|
|
|
|
* @hooked woocommerce_pagination - 10
|
|
|
|
|
*/
|
|
|
|
|
do_action( 'woocommerce_after_shop_loop' );
|
|
|
|
|
?>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
/**
|
|
|
|
|
* Hook: woocommerce_no_products_found.
|
|
|
|
|
*
|
|
|
|
|
* @hooked wc_no_products_found - 10
|
|
|
|
|
*/
|
|
|
|
|
do_action( 'woocommerce_no_products_found' );
|
|
|
|
|
}
|