PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.0
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.0
1.7.0 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 All 50 releases
← All changes | app/Services/Renderer/ProductRenderer.php +75 -42 1.6.2 → 1.7.0 View file →
@@ -3,9 +3,8 @@
3 3 namespace FluentCart\App\Services\Renderer;
4 4
5 5 use FluentCart\App\App;
6 6 use FluentCart\App\Services\FrontendView;
7 -use FluentCart\App\Vite;
8 7 use FluentCart\Api\StoreSettings;
9 8 use FluentCart\Api\ModuleSettings;
10 9 use FluentCart\App\Helpers\Helper;
11 10 use FluentCart\App\Models\Product;
@@ -13,8 +12,9 @@
13 12 use FluentCart\App\Http\Routes\WebRoutes;
14 13 use FluentCart\App\Models\ProductVariation;
15 14 use FluentCart\Framework\Support\Collection;
16 15 use FluentCart\App\Modules\Templating\AssetLoader;
16 +use FluentCart\App\Modules\FluentPlayer\ProductVideoRenderer;
17 17
18 18 class ProductRenderer
19 19 {
20 20 protected $product;
@@ -38,8 +38,10 @@
38 38 protected $defaultGalleryImageId = 0;
39 39
40 40 protected $galleryActiveSet = false;
41 41
42 + protected $productVideoRenderer = null;
43 +
42 44 protected $paymentTypes = [];
43 45
44 46 protected $variantsByPaymentTypes = [];
45 47
@@ -370,15 +372,15 @@
370 372 public function renderGalleryThumb()
371 373 {
372 374 $thumbnails = [];
373 375
374 - $featuredMedia = $this->product->thumbnail ?? Vite::getAssetUrl('images/placeholder.svg');
376 + $featuredMedia = $this->product->thumbnail ?? Helper::getProductPlaceholderUrl();
375 377
376 378 // thumbnail can be an empty string (not null), so ?? above doesn't catch
377 379 // it — fall back to the placeholder so $featuredMedia is always a usable
378 380 // image URL for both the main <img> and data-default-image-url.
379 381 if (!$featuredMedia || !\is_string($featuredMedia)) {
380 - $featuredMedia = Vite::getAssetUrl('images/placeholder.svg');
382 + $featuredMedia = Helper::getProductPlaceholderUrl();
381 383 }
382 384
383 385 $galleryImage = get_post_meta($this->product->ID, 'fluent-products-gallery-image', true);
384 386
@@ -432,11 +434,16 @@
432 434 if (empty($this->defaultImageUrl)) {
433 435 $this->defaultImageUrl = $featuredMedia;
434 436 }
435 437
438 + $videoRenderer = $this->getProductVideoRenderer();
439 + $videoRenderer->showFirstByDefault(!$this->hasGalleryImages());
440 + $defaultVideoId = $videoRenderer->getDefaultMediaId();
441 +
436 442 ?>
437 - <div class="fct-product-gallery-thumb" role="region"
438 - aria-label="<?php echo esc_attr($this->product->post_title . ' gallery'); ?>">
443 + <div class="fct-product-gallery-thumb<?php echo $defaultVideoId ? ' is-video-active' : ''; ?>" role="region"
444 + aria-label="<?php echo esc_attr($this->product->post_title . ' gallery'); ?>"
445 + <?php echo $defaultVideoId ? 'data-fct-video-default="' . esc_attr((string) $defaultVideoId) . '"' : ''; ?>>
439 446 <img
440 447 src="<?php echo esc_url($this->defaultImageUrl ?? '') ?>"
441 448 alt="<?php echo esc_attr($this->defaultImageAlt) ?>"
442 449 data-fluent-cart-single-product-page-product-thumbnail
@@ -441,21 +448,54 @@
441 448 alt="<?php echo esc_attr($this->defaultImageAlt) ?>"
442 449 data-fluent-cart-single-product-page-product-thumbnail
443 450 data-default-image-url="<?php echo esc_url($featuredMedia) ?>"
444 451 />
452 + <?php $this->getProductVideoRenderer()->renderInlinePlayers(); ?>
445 453 </div>
446 454 <?php
447 455 }
448 456
457 + /**
458 + * Whether any gallery / variant image exists to show in the main area.
459 + * Only meaningful after renderGalleryThumb() has built $this->images.
460 + */
461 + protected function hasGalleryImages(): bool
462 + {
463 + foreach ($this->images as $image) {
464 + foreach ((array) Arr::get($image, 'media', []) as $item) {
465 + if (!empty(Arr::get($item, 'url'))) {
466 + return true;
467 + }
468 + }
469 + }
470 +
471 + return false;
472 + }
473 +
474 + public function getProductVideoRenderer(): ProductVideoRenderer
475 + {
476 + if ($this->productVideoRenderer === null) {
477 + $this->productVideoRenderer = new ProductVideoRenderer($this->product);
478 + }
479 +
480 + return $this->productVideoRenderer;
481 + }
482 +
449 483 public function renderGalleryThumbControls($maxThumbnails = null)
450 484 {
451 485 $totalThumbImages = Arr::pluck($this->images, 'media.*.url');
452 486
453 - if(count($totalThumbImages) == 1 && is_countable($totalThumbImages[0]) && count($totalThumbImages[0]) == 1){
487 + // A lone image needs no strip, but a video thumb still has to be reachable.
488 + if(count($totalThumbImages) == 1 && is_countable($totalThumbImages[0]) && count($totalThumbImages[0]) == 1 && !$this->getProductVideoRenderer()->isAvailable()){
454 489
455 490 return '';
456 491 }
457 492
493 + // A lone video without images is already showing in the main area.
494 + if (!$this->hasGalleryImages() && count($this->getProductVideoRenderer()->getVideos()) === 1) {
495 + return '';
496 + }
497 +
458 498 // Build variantTermMap and variantFirstMediaMap.
459 499 // For advanced variations, Pro builds both maps via the gallery_variation_data
460 500 // filter — it has access to AttributeGroup types (color/image) that free cannot
461 501 // query directly. For all other product types, free builds variantFirstMediaMap
@@ -548,9 +588,16 @@
548 588 $isAdvVariation = !empty($this->variantTermMap);
549 589 $countedMediaKeys = [];
550 590 $count = 0;
551 591 $totalImages = 0;
592 + $videoRenderer = $this->getProductVideoRenderer();
552 593
594 + // The gallery opens on a video the admin put in front of every image,
595 + // so no image thumb takes the selected slot in that case.
596 + if ($videoRenderer->getDefaultMediaId()) {
597 + $this->galleryActiveSet = true;
598 + }
599 +
553 600 // Count unique images to render. For advanced variations, deduplicate by WP media ID
554 601 // when available, or by URL for externally imported images (media ID = 0).
555 602 foreach ($this->images as $imageId => $image) {
556 603 if (empty($image['media']) || !is_array($image['media'])) {
@@ -592,16 +639,21 @@
592 639 }
593 640 $renderedMediaKeys[$mediaDedupeKey] = true;
594 641 }
595 642 if ($maxThumbnails !== null && $count >= (int) $maxThumbnails) {
643 + $videoRenderer->renderThumbControls(!$this->galleryActiveSet);
596 644 $this->renderGallerySeeMoreButton($totalImages - (int) $maxThumbnails);
597 645 return;
598 646 }
647 + // Videos the admin dragged in front of this image come first.
648 + $videoRenderer->renderThumbControlsBefore($count, !$this->galleryActiveSet);
599 649 $termId = (int) ($this->variantTermMap[(int) $imageId] ?? 0);
600 650 $this->renderGalleryThumbControlButton($item, $imageId, $termId, $mediaId);
601 651 $count++;
602 652 }
603 653 }
654 +
655 + $videoRenderer->renderThumbControls(!$this->galleryActiveSet);
604 656 }
605 657
606 658 public function renderGallerySeeMoreButton($remainingCount)
607 659 {
@@ -1003,41 +1055,26 @@
1003 1055 'current_price' => $itemPrice,
1004 1056 'scope' => 'price_range'
1005 1057 ]));
1006 1058 ?>
1007 - <?php
1059 + <div class="fct-price-range fct-product-prices">
1008 1060
1009 - if ($comparePrice) {
1010 - $aria_label = sprintf(
1011 - /* translators: 1: Original price, 2: Current item price */
1012 - __('Original Price: %1$s, Price: %2$s', 'fluent-cart'),
1013 - Helper::toDecimal($comparePrice),
1014 - Helper::toDecimal($itemPrice)
1015 - );
1016 - } else {
1017 - $aria_label = sprintf(
1018 - /* translators: 1: Current item price */
1019 - __('Price: %1$s', 'fluent-cart'),
1020 - Helper::toDecimal($itemPrice)
1021 - );
1022 - }
1023 -
1024 - ?>
1025 - <div class="fct-price-range fct-product-prices" role="term"
1026 - aria-label="<?php echo esc_attr($aria_label); ?>">
1027 -
1028 1061 <?php if ($comparePrice): ?>
1029 1062 <span class="fct-compare-price">
1030 - <del aria-label="<?php echo esc_attr(__('Original price', 'fluent-cart')); ?>"><?php echo esc_html(Helper::toDecimal($comparePrice)); ?></del>
1063 + <span class="fct-sr-only"><?php echo esc_html__('Original price:', 'fluent-cart'); ?></span>
1064 + <del><?php echo esc_html(Helper::toDecimal($comparePrice)); ?></del>
1031 1065 </span>
1032 1066 <?php endif; ?>
1033 - <span class="fct-item-price" aria-label="<?php echo esc_attr(__('Current price', 'fluent-cart')); ?>">
1067 + <span class="fct-item-price">
1068 + <span class="fct-sr-only"><?php echo $comparePrice ? esc_html__('Sale price:', 'fluent-cart') : esc_html__('Price:', 'fluent-cart'); ?></span>
1034 1069 <?php echo esc_html(Helper::toDecimal($itemPrice)); ?>
1035 1070 <?php do_action('fluent_cart/product/after_price', RenderContext::decorate([
1036 1071 'product' => $this->product,
1072 + 'variant' => $first_price,
1037 1073 'current_price' => $itemPrice,
1038 1074 'scope' => 'price_range'
1039 1075 ])); ?>
1076 + <?php RenderHelper::renderPriceSuffix($this->product, $first_price, 'price_range'); ?>
1040 1077 </span>
1041 1078 </div>
1042 1079 <?php
1043 1080 do_action('fluent_cart/product/single/after_price_block', RenderContext::decorate([
@@ -1055,21 +1092,15 @@
1055 1092 'current_price' => $min_price,
1056 1093 'scope' => 'price_range'
1057 1094 ]));
1058 1095 ?>
1059 - <?php
1060 - $aria_label = sprintf(
1061 - /* translators: 1: Minimum price, 2: Maximum price */
1062 - __('Price range: %1$s - %2$s', 'fluent-cart'),
1063 - Helper::toDecimal($min_price),
1064 - Helper::toDecimal($max_price)
1065 - );
1066 - ?>
1067 - <div class="fct-product-prices fct-price-range" role="term" aria-label="<?php echo esc_attr($aria_label); ?>">
1096 + <div class="fct-product-prices fct-price-range">
1068 1097
1069 1098 <?php if ($max_price && $max_price != $min_price && $max_price > $min_price): ?>
1099 + <span class="fct-sr-only"><?php echo esc_html__('Price range:', 'fluent-cart'); ?></span>
1070 1100 <span class="fct-min-price"><?php echo esc_html(Helper::toDecimal($min_price)); ?></span>
1071 1101 <span class="fct-price-separator" aria-hidden="true">-</span>
1102 + <span class="fct-sr-only"><?php echo esc_html__('to', 'fluent-cart'); ?></span>
1072 1103 <?php endif; ?>
1073 1104 <span class="fct-max-price">
1074 1105 <?php echo esc_html(Helper::toDecimal($max_price)); ?>
1075 1106 </span>
@@ -1244,8 +1275,9 @@
1244 1275 return;
1245 1276 } ?>
1246 1277
1247 1278 <span class="fct-compare-price">
1279 + <span class="fct-sr-only"><?php echo esc_html__('Original price:', 'fluent-cart'); ?></span>
1248 1280 <del><?php echo esc_html(Helper::toDecimal($variant->compare_price)); ?></del>
1249 1281 </span>
1250 1282 <?php
1251 1283 }
@@ -1916,9 +1948,9 @@
1916 1948 }
1917 1949 ?>
1918 1950 <?php if ($this->viewType === 'both' || $this->viewType === 'text'): ?>
1919 1951 <div class="fct-product-variant-text">
1920 - <div class="fct-product-variant-title" aria-label="<?php echo esc_attr(__('Variant title', 'fluent-cart')); ?>"><?php echo esc_html($variant->variation_title); ?></div>
1952 + <div class="fct-product-variant-title"><?php echo esc_html($variant->variation_title); ?></div>
1921 1953 <?php if (!$this->shouldRenderPriceInPriceSection() && $paymentType === 'subscription'): ?>
1922 1954 <?php $this->renderSubscriptionInfo($variant); ?>
1923 1955 <?php endif; ?>
1924 1956 </div>
@@ -1928,14 +1960,15 @@
1928 1960 <?php if (!$this->shouldRenderPriceInPriceSection()): ?>
1929 1961 <div class="fct-product-variant-price">
1930 1962 <?php if ($comparePrice): ?>
1931 1963 <div class="fct-product-variant-compare-price">
1932 - <del aria-label="<?php echo esc_attr(__('Original price', 'fluent-cart')); ?>">
1964 + <span class="fct-sr-only"><?php echo esc_html__('Original price:', 'fluent-cart'); ?></span>
1965 + <del>
1933 1966 <span><?php echo esc_html(Helper::toDecimal($comparePrice)); ?></span></del>
1934 1967 </div>
1935 1968 <?php endif; ?>
1936 - <div class="fct-product-variant-item-price"
1937 - aria-label="<?php echo esc_attr(__('Current price', 'fluent-cart')); ?>">
1969 + <div class="fct-product-variant-item-price">
1970 + <span class="fct-sr-only"><?php echo $comparePrice ? esc_html__('Sale price:', 'fluent-cart') : esc_html__('Price:', 'fluent-cart'); ?></span>
1938 1971 <span><?php echo esc_html(Helper::toDecimal($itemPrice)); ?></span>
1939 1972 </div>
1940 1973 </div>
1941 1974 <?php endif; ?>
@@ -1955,9 +1988,9 @@
1955 1988 public function renderVariantImage($variant)
1956 1989 {
1957 1990 $image = $variant->thumbnail;
1958 1991 if (!$image) {
1959 - $image = Vite::getAssetUrl('images/placeholder.svg');
1992 + $image = Helper::getProductPlaceholderUrl();
1960 1993 }
1961 1994 ?>
1962 1995 <div class="fct-product-variant-image">
1963 1996 <img role="img" alt="<?php echo esc_attr($variant->variation_title); ?>"