PluginProbe
Gutenify – Visual Site Builder Blocks & Starter Templates / trunk
Gutenify – Visual Site Builder Blocks & Starter Templates vtrunk
1.9.0 1.8.0 1.7.0 1.6.6 1.6.5 1.6.4 trunk 0.0.1 0.0.2 0.0.3 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 All 74 releases
gutenify / core / dist / blocks / single-product-gallery / render.php

render.php in Gutenify – Visual Site Builder Blocks & Starter Templates trunk, at core/dist/blocks/single-product-gallery/render.php

183 lines 8.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Single Product Gallery render template.
4 *
5 * @package Gutenify
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 if ( ! class_exists( 'woocommerce' ) ) {
13 return;
14 }
15
16 // Only trust context/the current post as the product to show when it
17 // actually IS a product — otherwise a page/post that merely happens to
18 // contain this block (context always supplies *some* postId once
19 // do_blocks() runs on real page/post content, per usesContext) would
20 // silently defeat previewProductId, which exists precisely for that
21 // non-product-context case (both the editor's ServerSideRender preview
22 // and, previously incorrectly, the frontend).
23 $context_product_id = 0;
24
25 if ( isset( $block->context['postId'] ) && 'product' === get_post_type( absint( $block->context['postId'] ) ) ) {
26 $context_product_id = absint( $block->context['postId'] );
27 }
28
29 if ( ! $context_product_id && ! empty( $attributes['previewProductId'] ) ) {
30 $context_product_id = absint( $attributes['previewProductId'] );
31 }
32
33 if ( ! $context_product_id && 'product' === get_post_type() ) {
34 $context_product_id = get_the_ID();
35 }
36 $gallery_product = wc_get_product( $context_product_id );
37
38 if ( ! $gallery_product ) {
39 if ( is_admin() || wp_is_json_request() ) {
40 echo '<p class="gutenify--single-product-gallery__message">' . esc_html__( 'The gallery will appear when this block has a WooCommerce product context.', 'gutenify' ) . '</p>';
41 }
42 return;
43 }
44
45 $allowed_layouts = array( 'stack', 'grid-2', 'grid-3', 'feature-grid', 'feature-columns' );
46 $layout = isset( $attributes['layout'] ) && in_array( $attributes['layout'], $allowed_layouts, true ) ? $attributes['layout'] : 'grid-2';
47 // Same preset values as core's Image block "Aspect ratio" control —
48 // whitelisted rather than passed through raw since this becomes a CSS
49 // custom property value below.
50 $allowed_aspect_ratios = array( 'auto', '1', '4/3', '3/2', '16/9', '3/4', '2/3', '9/16' );
51 // Default is "Square" (1), not "Original" (auto) — a real product's own
52 // gallery photos are rarely all the same natural size/orientation, so
53 // "Original" produced a visibly broken grid out of the box (mismatched
54 // image heights, large empty gaps next to a landscape photo). A uniform
55 // square crop is the standard e-commerce gallery default for exactly
56 // this reason (WooCommerce's own recommended product image size is
57 // itself square).
58 $aspect_ratio = isset( $attributes['aspectRatio'] ) && in_array( $attributes['aspectRatio'], $allowed_aspect_ratios, true ) ? $attributes['aspectRatio'] : '1';
59 // Same three values as CSS's own `object-fit` keywords, matching core's
60 // Image block "Scale" control (Cover/Contain/Fill) 1:1 — no translation
61 // layer needed between the attribute and the CSS custom property below.
62 $allowed_scales = array( 'cover', 'contain', 'fill' );
63 $scale = isset( $attributes['scale'] ) && in_array( $attributes['scale'], $allowed_scales, true ) ? $attributes['scale'] : 'cover';
64 // `focalPoint` mirrors core's Image block attribute shape ({x, y}, each
65 // 0-1) — clamped here since it becomes a CSS percentage below.
66 $focal_point = isset( $attributes['focalPoint'] ) && is_array( $attributes['focalPoint'] ) ? $attributes['focalPoint'] : array();
67 $focal_point_x = isset( $focal_point['x'] ) ? min( 1, max( 0, (float) $focal_point['x'] ) ) : 0.5;
68 $focal_point_y = isset( $focal_point['y'] ) ? min( 1, max( 0, (float) $focal_point['y'] ) ) : 0.5;
69 $enable_lightbox = ! isset( $attributes['enableLightbox'] ) || ! empty( $attributes['enableLightbox'] );
70 $close_on_overlay_click = ! isset( $attributes['closeLightboxOnOverlayClick'] ) || ! empty( $attributes['closeLightboxOnOverlayClick'] );
71 $show_dots = ! isset( $attributes['showDots'] ) || ! empty( $attributes['showDots'] );
72 $show_sale_badge = ! isset( $attributes['showSaleBadge'] ) || ! empty( $attributes['showSaleBadge'] );
73 $allowed_badge_alignments = array( 'left', 'center', 'right' );
74 $sale_badge_align = isset( $attributes['saleBadgeAlign'] ) && in_array( $attributes['saleBadgeAlign'], $allowed_badge_alignments, true ) ? $attributes['saleBadgeAlign'] : 'right';
75 $image_ids = array_values(
76 array_filter(
77 array_unique(
78 array_merge(
79 array( absint( $gallery_product->get_image_id() ) ),
80 array_map( 'absint', $gallery_product->get_gallery_image_ids() )
81 )
82 )
83 )
84 );
85
86 if ( empty( $image_ids ) ) {
87 if ( is_admin() || wp_is_json_request() ) {
88 echo '<p class="gutenify--single-product-gallery__message">' . esc_html__( 'This product does not have gallery images yet.', 'gutenify' ) . '</p>';
89 }
90 return;
91 }
92
93 $sale_badge = $content;
94 if ( $show_sale_badge && '' === trim( $sale_badge ) && $gallery_product->is_on_sale() && class_exists( 'WP_Block' ) ) {
95 $sale_badge_block = new WP_Block(
96 array(
97 'blockName' => 'woocommerce/product-sale-badge',
98 'attrs' => array( 'align' => $sale_badge_align ),
99 ),
100 array( 'postId' => $gallery_product->get_id() )
101 );
102 $sale_badge = $sale_badge_block->render();
103 }
104
105 $wrapper_attributes = get_block_wrapper_attributes(
106 array(
107 'class' => 'gutenify--single-product-gallery wc-block-components-product-image is-layout-' . $layout,
108 'style' => sprintf(
109 '--single-product-gallery-aspect-ratio:%1$s;--single-product-gallery-scale:%2$s;--single-product-gallery-focal-x:%3$s%%;--single-product-gallery-focal-y:%4$s%%;',
110 esc_attr( $aspect_ratio ),
111 esc_attr( $scale ),
112 esc_attr( $focal_point_x * 100 ),
113 esc_attr( $focal_point_y * 100 )
114 ),
115 'data-ff-product-gallery' => '',
116 'data-ff-lightbox' => $enable_lightbox ? 'true' : 'false',
117 'data-ff-lightbox-overlay-close' => $close_on_overlay_click ? 'true' : 'false',
118 )
119 );
120 ?>
121 <div <?php echo $wrapper_attributes; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
122 <?php if ( $show_sale_badge ) : ?>
123 <?php echo $sale_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
124 <?php endif; ?>
125 <div class="gutenify--single-product-gallery__viewport">
126 <div class="gutenify--single-product-gallery__track" style="--single-product-gallery-secondary-count:<?php echo esc_attr( max( 1, count( $image_ids ) - 1 ) ); ?>">
127 <?php foreach ( $image_ids as $index => $image_id ) : ?>
128 <?php
129 $full_image_src = wp_get_attachment_image_src( $image_id, 'full' );
130 $full_image = $full_image_src ? array(
131 'src' => $full_image_src[0],
132 'width' => $full_image_src[1],
133 'height' => $full_image_src[2],
134 'alt' => get_post_meta( $image_id, '_wp_attachment_image_alt', true ),
135 ) : null;
136 ?>
137 <figure class="gutenify--single-product-gallery__item">
138 <?php if ( $enable_lightbox ) : ?>
139 <button
140 type="button"
141 class="gutenify--single-product-gallery__image-button"
142 data-ff-gallery-item="<?php echo esc_attr( $index ); ?>"
143 data-ff-image-src="<?php echo esc_url( $full_image ? $full_image['src'] : '' ); ?>"
144 data-ff-image-alt="<?php echo esc_attr( $full_image ? $full_image['alt'] : '' ); ?>"
145 data-ff-image-width="<?php echo esc_attr( $full_image ? $full_image['width'] : 0 ); ?>"
146 data-ff-image-height="<?php echo esc_attr( $full_image ? $full_image['height'] : 0 ); ?>"
147 aria-label="<?php echo esc_attr( sprintf( __( 'Open product image %1$d of %2$d', 'gutenify' ), $index + 1, count( $image_ids ) ) ); ?>"
148 >
149 <?php echo wp_get_attachment_image( $image_id, 'woocommerce_single', false, array( 'loading' => 0 === $index ? 'eager' : 'lazy' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
150 </button>
151 <?php else : ?>
152 <?php echo wp_get_attachment_image( $image_id, 'woocommerce_single', false, array( 'loading' => 0 === $index ? 'eager' : 'lazy' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
153 <?php endif; ?>
154 </figure>
155 <?php endforeach; ?>
156 </div>
157 </div>
158
159 <div class="gutenify--single-product-gallery__mobile-ui">
160 <div class="gutenify--single-product-gallery__counter" role="status" aria-live="polite">
161 <span class="screen-reader-text"><?php esc_html_e( 'Image', 'gutenify' ); ?></span>
162 <span data-ff-gallery-current>1</span>
163 <span aria-hidden="true">/</span>
164 <span class="screen-reader-text"><?php esc_html_e( 'of', 'gutenify' ); ?></span>
165 <span><?php echo esc_html( count( $image_ids ) ); ?></span>
166 </div>
167
168 <?php if ( $show_dots && count( $image_ids ) > 1 ) : ?>
169 <div class="gutenify--single-product-gallery__dots" role="group" aria-label="<?php esc_attr_e( 'Choose product image', 'gutenify' ); ?>">
170 <?php foreach ( $image_ids as $index => $image_id ) : // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable ?>
171 <button
172 type="button"
173 class="gutenify--single-product-gallery__dot<?php echo 0 === $index ? ' is-active' : ''; ?>"
174 data-ff-gallery-dot="<?php echo esc_attr( $index ); ?>"
175 aria-label="<?php echo esc_attr( sprintf( __( 'Show image %d', 'gutenify' ), $index + 1 ) ); ?>"
176 aria-pressed="<?php echo 0 === $index ? 'true' : 'false'; ?>"
177 ></button>
178 <?php endforeach; ?>
179 </div>
180 <?php endif; ?>
181 </div>
182 </div>
183