PluginProbe
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder / 3.0.2
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder v3.0.2
3.1.4 3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.0.7 3.0.5 3.0.4 3.0.3 3.0.2 trunk 1.5.0 1.5.1 1.5.2 1.6.1 1.6.2 1.6.3 1.6.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 All 93 releases
ultimate-store-kit / templates / shiny-grid.php

shiny-grid.php in Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder 3.0.2, at templates/shiny-grid.php

529 lines 21.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Shiny Grid Template Class
5 *
6 * Handles rendering of product items in the Shiny Grid layout
7 */
8
9 namespace UltimateStoreKit\Templates;
10
11 use UltimateStoreKit\Traits\Global_Widget_Template;
12 use UltimateStoreKit\Classes\Utils;
13
14 class USK_Shiny_Grid_Template
15 {
16 use Global_Widget_Template;
17
18 /**
19 * Widget settings and name
20 */
21 protected $target_widget_settings = [];
22 protected $target_widget_name = '';
23
24 /**
25 * Constructor
26 */
27 public function __construct($settings = [], $widget_name = '')
28 {
29 $this->target_widget_settings = $settings;
30 $this->target_widget_name = $widget_name;
31 }
32
33 /**
34 * Render a single product item
35 */
36 public function render_shiny_grid_item($product, $settings)
37 {
38 if (!$product) {
39 return;
40 }
41
42 $product_id = $product->get_id();
43 $rating_count = $product->get_rating_count();
44 $average = $product->get_average_rating();
45 $categories = wc_get_product_category_list($product_id, ' ');
46
47 $show_rating = isset($settings['show_rating']) ? $settings['show_rating'] : true;
48 $classes = $this->target_widget_name === 'shiny-grid' ? 'usk-item' : 'usk-item swiper-slide';
49 $classes .= $show_rating ? ' usk-have-rating' : '';
50 ?>
51 <div class="<?php echo esc_attr($classes); ?>" data-product-id="<?php echo esc_attr($product_id); ?>">
52 <div class="usk-item-box">
53 <?php $this->render_product_image($product); ?>
54 <div class="usk-content">
55 <?php if (isset($settings['show_variation']) && $settings['show_variation'] === 'yes'): ?>
56 <?php $this->render_product_variation($product, $settings);
57 ?>
58 <?php endif; ?>
59 <div class="usk-content-inner">
60 <?php if ($categories && (isset($settings['show_category']) ? $settings['show_category'] : true)): ?>
61 <div class="usk-category"><?php echo wp_kses_post($categories); ?></div>
62 <?php endif; ?>
63
64 <?php if (isset($settings['show_title']) ? $settings['show_title'] : true) :
65 printf(
66 '<%1$s class="title"><a href="%2$s" class="usk-title" aria-label="%4$s">%3$s</a></%1$s>',
67 esc_attr( Utils::get_valid_html_tag( isset( $settings['title_tags'] ) ? $settings['title_tags'] : 'h3' ) ),
68 esc_url( $product->get_permalink() ),
69 esc_html( $product->get_title() ),
70 esc_attr( sprintf( 'View details for %s', $product->get_title() ) )
71 );
72 endif; ?>
73
74 <?php if (
75 (isset($settings['show_excerpt']) ? $settings['show_excerpt'] : true)
76 && isset($settings['layout_style']) && $settings['layout_style'] === 'list'
77 ): ?>
78 <div class="usk-desc">
79 <span class="desc">
80 <?php echo wp_kses_post(wp_trim_words($product->get_short_description(), $settings['excerpt_limit'], '')); ?>
81 </span>
82 </div>
83 <?php endif; ?>
84
85 <?php if (isset($settings['show_price']) ? $settings['show_price'] : true && $product->get_price_html()): ?>
86 <div class="usk-price">
87 <?php $this->print_price_output($product->get_price_html()); ?>
88 </div>
89 <?php endif; ?>
90
91 <?php if (isset($settings['show_rating']) ? $settings['show_rating'] : true): ?>
92 <div class="usk-rating">
93 <span><?php echo wp_kses_post($this->register_global_template_wc_rating($average, $rating_count)); ?></span>
94 </div>
95 <?php endif; ?>
96 </div>
97
98 </div>
99 </div>
100 </div>
101 <?php
102 }
103
104 /**
105 * Render add to cart button
106 */
107 public function __render_add_to_cart_button($product)
108 {
109 if (!$product) {
110 return;
111 }
112
113 // Check if product is variable and has default attributes
114 $has_default_attributes = false;
115 if ($product->is_type('variable')) {
116 $default_attributes = $product->get_default_attributes();
117 if (!empty($default_attributes)) {
118 $has_default_attributes = true;
119 }
120 }
121
122 // Set up button classes
123 $button_classes = [
124 'usk-button',
125 'product_type_' . $product->get_type(),
126 ];
127
128 // Add appropriate classes for purchasable products
129 if ($product->is_purchasable() && $product->is_in_stock() && (!$product->is_type('variable') || $has_default_attributes)) {
130 $button_classes[] = 'add_to_cart_button';
131
132 if ($product->supports('ajax_add_to_cart')) {
133 $button_classes[] = 'ajax_add_to_cart';
134 }
135 }
136
137 // Set up button attributes
138 $button_attributes = [
139 'data-product_id' => $product->get_id(),
140 'data-product_sku' => $product->get_sku(),
141 'aria-label' => $product->add_to_cart_description(),
142 // 'rel' => 'nofollow',
143 ];
144
145 // Add default attributes data for variable products
146 if ($product->is_type('variable') && $has_default_attributes) {
147 $button_attributes['data-default_attributes'] = htmlspecialchars(
148 \wp_json_encode($product->get_default_attributes()),
149 ENT_QUOTES,
150 'UTF-8'
151 );
152
153 $variation_id = $this->get_variation_id_from_attributes($product, $product->get_default_attributes());
154 if ($variation_id) {
155 $button_attributes['data-variation_id'] = $variation_id;
156
157 // Add each default attribute as separate data attributes for AJAX support
158 foreach ($default_attributes as $attribute_name => $attribute_value) {
159 $button_attributes['data-attribute_' . $attribute_name] = $attribute_value;
160 }
161
162 // If we have a default variation, use product_type_variation instead of variable
163 if (in_array('product_type_variable', $button_classes)) {
164 $key = array_search('product_type_variable', $button_classes);
165 if ($key !== false) {
166 $button_classes[$key] = 'product_type_variation';
167 }
168 }
169 }
170 }
171
172 // Create button args
173 $args = [
174 'quantity' => 1,
175 'class' => implode(' ', $button_classes),
176 'attributes' => $button_attributes,
177 ];
178
179 $args = \apply_filters('woocommerce_loop_add_to_cart_args', $args, $product);
180
181 // Clean up aria-label
182 if (isset($args['attributes']['aria-label'])) {
183 $args['attributes']['aria-label'] = \wp_strip_all_tags($args['attributes']['aria-label']);
184 }
185
186 // Determine button text
187 if ($product->is_type('variable')) {
188 $button_text = $has_default_attributes ? \__('Add to cart', 'ultimate-store-kit') : \__('Select options', 'ultimate-store-kit');
189 } else {
190 $button_text = $product->add_to_cart_text();
191 }
192
193 // Get the URL
194 if ($product->is_type('variable')) {
195 $url = 'javascript:void(0)';
196 } else {
197 $url = $product->add_to_cart_url();
198 }
199
200 // Make sure variable products use correct classes
201 if ($product->is_type('variable') && in_array('product_type_variable', $button_classes)) {
202 $key = array_search('product_type_variable', $button_classes);
203 if ($key !== false) {
204 $button_classes[$key] = 'product_type_variation';
205 }
206 }
207
208 // Output the button
209 echo \apply_filters(
210 'woocommerce_loop_add_to_cart_link',
211 sprintf(
212 '<a href="%s" data-quantity="%s" class="%s" %s>%s <i class="button-icon usk-icon-arrow-right-8"></i></a>',
213 \esc_url($url),
214 \esc_attr(isset($args['quantity']) ? $args['quantity'] : 1),
215 \esc_attr(isset($args['class']) ? $args['class'] : 'button'),
216 isset($args['attributes']) ? \wc_implode_html_attributes($args['attributes']) : '',
217 \esc_html($button_text)
218 ),
219 $product,
220 $args
221 );
222 }
223 public function render_add_to_cart_button($product, $settings)
224 {
225 if ($product) {
226 $defaults = [
227 'quantity' => 1,
228 'class' => implode(
229 ' ',
230 array_filter(
231 [
232 'usk-button',
233 'product_type_' . $product->get_type(),
234 $product->is_purchasable() && $product->is_in_stock() ? 'add_to_cart_button' : '',
235 $product->supports('ajax_add_to_cart') && $product->is_purchasable() && $product->is_in_stock() ? 'ajax_add_to_cart' : '',
236 ]
237 )
238 ),
239 'attributes' => [
240 'data-product_id' => $product->get_id(),
241 'data-product_sku' => $product->get_sku(),
242 'aria-label' => $product->add_to_cart_description(),
243 'rel' => 'nofollow',
244 ],
245 ];
246 $args = apply_filters('woocommerce_loop_add_to_cart_args', wp_parse_args($defaults), $product);
247 if (isset($args['attributes']['aria-label'])) {
248 $args['attributes']['aria-label'] = wp_strip_all_tags($args['attributes']['aria-label']);
249 }
250
251 if ( isset( $settings['show_cart'] ) ? $settings['show_cart'] : true ) {
252 echo wp_kses_post(apply_filters(
253 'woocommerce_loop_add_to_cart_link', // WPCS: XSS ok.
254 sprintf(
255 '<a href="%s" data-quantity="%s" class="%s" %s>%s <i class="button-icon usk-icon-arrow-right-8"></i></a>',
256 esc_url( $product->add_to_cart_url() ),
257 esc_attr( isset( $args['quantity'] ) ? $args['quantity'] : 1 ),
258 esc_attr( isset($args['class'] ) ? $args['class'] : 'button' ),
259 isset( $args['attributes'] ) ? wc_implode_html_attributes( $args['attributes'] ) : '',
260 esc_html( $product->add_to_cart_text() )
261 ),
262 $product,
263 $args
264 ));
265 }
266 }
267 ;
268 }
269
270 /**
271 * Get variation ID from product attributes
272 * Uses WooCommerce data store for reliable variation finding
273 */
274 private function get_variation_id_from_attributes($product, $attributes)
275 {
276 if (!$product || !$product->is_type('variable')) {
277 return null;
278 }
279
280 // Format attributes for WooCommerce
281 $formatted_attributes = [];
282 foreach ($attributes as $attribute_name => $attribute_value) {
283 $formatted_attributes['attribute_' . $attribute_name] = $attribute_value;
284 }
285
286 // Use WooCommerce's data store to find matching variation
287 $data_store = \WC_Data_Store::load('product');
288 $variation_id = $data_store->find_matching_product_variation($product, $formatted_attributes);
289
290 if ($variation_id) {
291 return $variation_id;
292 }
293
294 return null;
295 }
296
297 /**
298 * Render product image with hover effect and action buttons
299 */
300 public function render_product_image($product)
301 {
302 if (!$product) {
303 return;
304 }
305
306 $tooltip_position = 'left';
307 $settings = $this->target_widget_settings;
308 $image_size = isset($settings['image_size']) ? $settings['image_size'] : 'full';
309
310 // Get main product image
311 $product_image = wp_get_attachment_image_url(get_post_thumbnail_id(), $image_size);
312
313 // Get gallery image for hover effect
314 $gallery_image_link = $product_image; // Default to main image
315 $gallery_thumbs = $product->get_gallery_image_ids();
316
317 if ($gallery_thumbs && !empty($gallery_thumbs[0])) {
318 $gallery_image_link = wp_get_attachment_image_url($gallery_thumbs[0], $image_size);
319 }
320 ?>
321 <div class="usk-image">
322 <a href="<?php echo esc_url(get_permalink()); ?>">
323 <img
324 class="img"
325 src="<?php echo esc_url( $product_image ); ?>"
326 alt="<?php echo esc_html( get_the_title() ); ?>"
327 >
328 </a>
329 <?php $this->render_add_to_cart_button($product, $settings); ?>
330
331 <div class="usk-shoping">
332 <?php $this->register_global_template_add_to_wishlist($tooltip_position, $settings); ?>
333 <?php $this->register_global_template_add_to_compare($tooltip_position, $settings); ?>
334 <?php $this->register_global_template_quick_view($product->get_id(), $tooltip_position, $settings); ?>
335 </div>
336 <div class="usk-badge-label-wrapper">
337 <div class="usk-badge-label-content usk-flex usk-flex-column">
338 <?php $this->register_global_template_badge_label($settings); ?>
339 </div>
340
341 <!-- display product variation -->
342 </div>
343 </div>
344 <?php
345 }
346
347 /**
348 * Print price HTML with allowed tags
349 */
350 public function print_price_output($output)
351 {
352 $allowed_tags = [
353 'del' => ['aria-hidden' => []],
354 'span' => ['class' => []],
355 'bdi' => [],
356 'ins' => [],
357 ];
358
359 if (isset($output)) {
360 echo wp_kses($output, $allowed_tags);
361 }
362 }
363
364 /**
365 * Check if swatches support is available
366 */
367 private function has_swatches_support()
368 {
369 return class_exists('UltimateStoreKitPro\\VariationSwatches\\Swatches');
370 }
371
372 /**
373 * Render product variation options (colors, sizes)
374 * Displays variation swatches on product grid items
375 */
376 public function render_product_variation($product, $settings)
377 {
378 if (!$product || !$product->is_type('variable')) {
379 return;
380 }
381
382 $variations = $product->get_available_variations();
383 if (empty($variations)) {
384 return;
385 }
386
387 // Check if sequential mode is enabled
388 $sequential = \apply_filters('usk_sequential_variations', isset($settings['show_variation_sequential']) && $settings['show_variation_sequential'] === 'yes');
389
390 // If Pro version with swatches is active, use that functionality
391 if ($this->has_swatches_support() && function_exists('apply_filters')) {
392 $this->render_swatches_variation($product, $variations, $sequential);
393 return;
394 }
395
396 // Otherwise use the simple variation buttons
397 $this->render_simple_variations($product, $variations, $sequential);
398 }
399
400 /**
401 * Render simple variation buttons for the free version
402 */
403 private function render_simple_variations($product, $variations, $sequential = false)
404 {
405 $product_id = $product->get_id();
406 $attributes = $product->get_variation_attributes();
407
408 if (empty($attributes)) {
409 return;
410 }
411
412 // Add sequential data attribute if enabled
413 $sequential_attr = $sequential ? ' data-sequential="true"' : '';
414
415 echo '<div class="usk-variations-container" data-product-id="' . esc_attr($product_id) . '" data-variations-reset="true"' . $sequential_attr . '>';
416
417 foreach ($attributes as $attribute_name => $options) {
418 if (empty($options)) {
419 continue;
420 }
421
422 // Get attribute label
423 $attribute_label = wc_attribute_label($attribute_name);
424 $attribute_slug = sanitize_title($attribute_name);
425
426 echo '<div class="usk-variation-group">';
427 echo '<div class="usk-variation-options">';
428
429 foreach ($options as $option) {
430 $classes = 'usk-variation-button';
431 $option_name = apply_filters('woocommerce_variation_option_name', $option);
432
433 // Check if this is a color attribute
434 $is_color = (strpos(strtolower($attribute_slug), 'color') !== false ||
435 strpos(strtolower($attribute_label), 'color') !== false);
436
437 if ($is_color) {
438 // For color attributes, use background color and minimal text
439 // Translators: %s is the name of the color option (e.g., "Red", "Blue").
440 $aria_label = sprintf( __( 'Select %s', 'ultimate-store-kit' ), esc_attr( $option_name ) );
441 echo '<button type="button" class="' . esc_attr($classes . ' usk-color-button') . '"
442 data-attribute="' . esc_attr($attribute_slug) . '"
443 data-value="' . esc_attr($option) . '"
444 style="background-color: ' . esc_attr($option) . '"
445 aria-label="' . esc_attr($aria_label) . '"><span class="usk-tooltip-text">' . esc_html($option_name) . '</span>
446 </button>';
447 } else {
448 // For non-color attributes, display as regular text buttons
449 // Translators: %s is the name of the variation option (e.g., "Large", "Cotton").
450 $aria_label = sprintf( __( 'Select %s', 'ultimate-store-kit' ), esc_attr( $option_name ) );
451 echo '<button type="button" class="' . esc_attr($classes) . '"
452 data-attribute="' . esc_attr($attribute_slug) . '"
453 data-value="' . esc_attr($option) . '"
454 aria-label="' . esc_attr($aria_label) . '">' . esc_html($option_name) . '<span class="usk-tooltip-text">' . esc_html($option_name) . '</span>
455 </button>';
456 }
457 }
458
459 echo '</div>'; // Close .usk-variation-options
460 echo '</div>'; // Close .usk-variation-group
461 }
462
463 echo '</div>'; // Close .usk-variations-container
464 }
465
466 /**
467 * Render variation swatches using the Pro version's swatches functionality
468 */
469 private function render_swatches_variation($product, $variations, $sequential = false)
470 {
471 $product_id = $product->get_id();
472 $attributes = $product->get_variation_attributes();
473
474 if (empty($attributes)) {
475 return;
476 }
477
478 // Add sequential data attribute if enabled
479 $sequential_attr = $sequential ? ' data-sequential="true"' : '';
480
481 echo '<div class="usk-variations-container usk-pro-swatches" data-product-id="' . esc_attr($product_id) . '" data-variations-reset="true"' . $sequential_attr . '>';
482
483 // Loop through each product attribute
484 foreach ($attributes as $attribute_name => $options) {
485 if (empty($options)) {
486 continue;
487 }
488
489 echo '<div class="usk-variation-group">';
490
491 // Build the args for the swatches
492 $args = array(
493 'options' => $options,
494 'product' => $product,
495 'attribute' => $attribute_name,
496 'name' => 'attribute_' . sanitize_title($attribute_name),
497 'selected' => isset($_REQUEST['attribute_' . sanitize_title($attribute_name)])
498 ? wc_clean(wp_unslash($_REQUEST['attribute_' . sanitize_title($attribute_name)]))
499 : $product->get_variation_default_attribute($attribute_name)
500 );
501
502 // Create a placeholder for the dropdown - this will be replaced with swatches
503 $dropdown_html = '<select id="' . esc_attr($args['name']) . '" class="' . esc_attr($args['name']) . '" name="' . esc_attr($args['name']) . '" data-attribute_name="' . esc_attr($args['name']) . '" data-show_option_none="yes" style="display:none;">';
504 $dropdown_html .= '<option value="">' . esc_html__('Choose an option', 'ultimate-store-kit') . '</option>';
505
506 if (!empty($options)) {
507 foreach ($options as $option) {
508 $dropdown_html .= '<option value="' . esc_attr($option) . '" ' . selected($args['selected'], $option, false) . '>' . esc_html(apply_filters('woocommerce_variation_option_name', $option, null, $attribute_name, $product)) . '</option>';
509 }
510 }
511
512 $dropdown_html .= '</select>';
513
514 // Apply the filter to transform the dropdown to swatches
515 if (class_exists('UltimateStoreKitPro\\VariationSwatches\\Swatches')) {
516 $swatches = \UltimateStoreKitPro\VariationSwatches\Swatches::instance();
517 $swatches_html = $swatches->swatches_html($dropdown_html, $args);
518 echo $swatches_html;
519 } else {
520 echo $dropdown_html;
521 }
522
523 echo '</div>'; // Close .usk-variation-group
524 }
525
526 echo '</div>'; // Close .usk-variations-container
527 }
528 }
529