PluginProbe
UiCore Elements – Free widgets and templates for Elementor / 1.2.3
UiCore Elements – Free widgets and templates for Elementor v1.2.3
1.3.17 1.3.16 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 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.2.0 1.2.1 1.2.2 1.2.3 1.2.4 All 41 releases
uicore-elements / includes / widgets / advanced-product-grid.php

advanced-product-grid.php in UiCore Elements – Free widgets and templates for Elementor 1.2.3, at includes/widgets/advanced-product-grid.php

490 lines 16.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace UiCoreElements;
4
5 use UiCoreElements\Controls\Product_Filter;
6 use uicoreElements\Utils\Product_Trait;
7
8 defined('ABSPATH') || exit();
9
10 /**
11 * Advanced Product Grid Widget. Extends APG.
12 *
13 * @author Lucas Marini Falbo <lucas@uicore.co>
14 * @since 1.0.11
15 */
16
17 class AdvancedProductGrid extends AdvancedPostGrid
18 {
19 private $_query;
20
21 use Product_Trait;
22
23 public function get_name()
24 {
25 return 'uicore-advanced-product-grid';
26 }
27 public function get_categories()
28 {
29 return ['uicore', 'uicore-woo'];
30 }
31 public function get_title()
32 {
33 return __('Advanced Product Grid', 'uicore-elements');
34 }
35 public function get_icon()
36 {
37 return 'eicon-gallery-grid ui-e-widget';
38 }
39 public function get_keywords()
40 {
41 return [ 'woocommerce', 'product', 'shop', 'store', 'post', 'grid' ];
42 }
43 public function get_styles()
44 {
45 // get parent get_styles
46 $styles = parent::get_styles();
47
48 // remove parent AdvancedPostGrid widget main style. OBS: the IDE complains because get_styles doc
49 // says it returns obj, but we're extending a widget that returns the array, so the parent
50 // is not widget base, but the AdvancedPostGrid widget
51 if ( ($key = array_search('advanced-post-grid', $styles)) !== false) {
52 unset($styles[$key]);
53 }
54
55 // Add the product grid style
56 $styles[] = 'advanced-product-grid';
57
58 return $styles;
59 }
60 function get_query()
61 {
62 return $this->_query;
63 }
64
65 protected function register_controls()
66 {
67 // Fallback
68 if( !class_exists('WooCommerce') ) {
69 $this->start_controls_section(
70 'section_fallback',
71 [
72 'label' => esc_html__('Content', 'uicore-elements'),
73 ]
74 );
75 $this->add_control(
76 'woocommerce_warning',
77 [
78 'type' => \Elementor\Controls_Manager::ALERT,
79 'alert_type' => 'warning',
80 'content' => esc_html__('Please enable WooCommerce to use this widget.', 'uicore-elements'),
81 ]
82 );
83 $this->end_controls_section();
84 return;
85 }
86
87 parent::register_controls();
88
89 // Remove query source and meta controls from parent
90 $this->remove_control('posts-filter_post_type');
91 $this->remove_control('top_meta');
92 $this->remove_control('before_title_meta');
93 $this->remove_control('after_title_meta');
94 $this->remove_control('bottom_meta');
95
96 // Register query control with updated options
97 $this->start_injection( [
98 'of' => 'section_post_grid_def',
99 'at' => 'after',
100 ] );
101 $this->add_group_control(
102 Product_Filter::get_type(),
103 [
104 'name' => 'product-filter',
105 'label' => esc_html__('Products', 'uicore-elements'),
106 'description' => esc_html__('Current Query Settings > Reading', 'uicore-elements'),
107 ]
108 );
109 $this->end_injection();
110
111 // Register meta controls with updated options
112 $this->start_injection( [
113 'of' => 'section_extra_item_content',
114 'at' => 'after',
115 ] );
116 $this->TRAIT_register_post_meta_controls(false, true);
117 $this->end_injection();
118
119 // Register new button placement control
120 $this->start_injection( [
121 'of' => 'section_button_style',
122 'at' => 'after',
123 ] );
124 $this->add_control(
125 'button_position',
126 [
127 'label' => esc_html__('Placement', 'uicore-elements'),
128 'type' => \Elementor\Controls_Manager::SELECT,
129 'options' => [
130 '' => 'Default',
131 'ui-e-button-placement-image' => 'On Image',
132 ],
133 'default' => 'ui-e-button-placement-image',
134 'render_type' => 'template',
135 'prefix_class' => ''
136 ]
137 );
138 $this->end_injection();
139
140 // Register new Swatches and Sale Badge controls
141 $this->start_injection( [
142 'of' => 'show_button',
143 'at' => 'after',
144 ] );
145 $this->add_control(
146 'show_swatches',
147 [
148 'label' => esc_html__('Swatches', 'uicore-elements'),
149 'type' => \Elementor\Controls_Manager::SWITCHER,
150 'label_on' => esc_html__( 'Show', 'uicore-elements' ),
151 'label_off' => esc_html__( 'Hide', 'uicore-elements' ),
152 'description' => esc_html__('Will only work if you have Uicore Framework plugin active at least on version 6.0.1', 'uicore-elements'),
153 'default' => 'yes',
154 ]
155 );
156 $this->add_control(
157 'show_sale_badge',
158 [
159 'label' => esc_html__('Sale Badge', 'uicore-elements'),
160 'type' => \Elementor\Controls_Manager::SWITCHER,
161 'label_on' => esc_html__( 'Show', 'uicore-elements' ),
162 'label_off' => esc_html__( 'Hide', 'uicore-elements' ),
163 'default' => 'yes',
164 'prefix_class' => 'ui-e-show-sale-badge-',
165 ]
166 );
167 $this->add_control(
168 'badge_warning',
169 [
170 'type' => \Elementor\Controls_Manager::RAW_HTML,
171 'raw' => __("If you can't see the Sale Badge after setting 'Show', you problably didn't set any Meta option as 'Product Sale', or you don't have products on sale.", 'uicore-elements'),
172 'content_classes' => 'elementor-control-field-description',
173 'condition' => [
174 'show_sale_badge' => 'yes',
175 ],
176 ]
177 );
178 $this->end_injection();
179
180 // Register new Hide out of stock products control
181 $this->start_injection( [
182 'of' => 'sticky',
183 'at' => 'before',
184 ] );
185 $this->add_control(
186 'hide_out_of_stock',
187 [
188 'label' => esc_html__('Hide out of stock', 'uicore-elements'),
189 'type' => \Elementor\Controls_Manager::SWITCHER,
190 'condition' => [
191 'product-filter_post_type!' => 'related',
192 ],
193 ]
194 );
195 $this->end_injection();
196
197 //
198 $this->start_injection( [
199 'of' => 'text',
200 'at' => 'after',
201 ] );
202 $this->add_control(
203 'variations_text',
204 [
205 'label' => esc_html__('Select Options Text', 'uicore-elements'),
206 'label_block' => true,
207 'default' => '',
208 'description' => esc_html__('Variable product purchase text.', 'uicore-elements'),
209 'placeholder' => esc_html__('Select Options', 'uicore-elements'),
210 ]
211 );
212 $this->add_control(
213 'unavailable_text',
214 [
215 'label' => esc_html__('Unavailable Text', 'uicore-elements'),
216 'label_block' => true,
217 'default' => '',
218 'description' => esc_html__('Out of stock or not available purchase text.', 'uicore-elements'),
219 'placeholder' => esc_html__('Read more', 'uicore-elements'),
220 ]
221 );
222 $this->end_injection();
223
224 // Update controls that uses 'posts_filter' to 'product_filter'
225 $this->update_control(
226 'item_limit',
227 [
228 'condition' => [
229 'product-filter_post_type!' => 'current',
230 ],
231 ]
232 );
233
234 // Update filters options
235 $this->update_control(
236 'filters_taxonomies',
237 [
238 'options' => Helper::get_taxonomies(true, true),
239 'default' => ['product_cat'],
240 ]
241 );
242
243 // Update Image animation options and default value
244 $this->update_control(
245 'anim_image',
246 [
247 'options' => [
248 '' => __('None', 'uicore-elements'),
249 'ui-e-img-anim-zoom' => __('Zoom', 'uicore-elements'),
250 'ui-e-img-anim-change' => __('Change image', 'uicore-elements'),
251 ],
252 'default' => 'ui-e-img-anim-change',
253 'render_type' => 'template', // `change image` adds a new html element
254 ]
255 );
256
257 // Update top meta style options so sale badge (default top meta) looks better by default
258 $this->update_control(
259 'top_meta_color',
260 [
261 'default' => '#FFF',
262 ]
263 );
264 $this->update_control(
265 'top_meta_background',
266 [
267 'default' => '#000',
268 ]
269 );
270 $this->update_control(
271 'top_meta_padding',
272 [
273 'default' => [
274 'top' => 5,
275 'right' => 5,
276 'bottom' => 5,
277 'left' => 5,
278 'unit' => 'px'
279 ],
280 ]
281 );
282
283 // Update some UI controls labels and default values
284 $this->update_control(
285 'excerpt',
286 [
287 'label' => esc_html__('Product Summary', 'uicore-elements'),
288 'default' => ''
289 ]
290 );
291 $this->update_control(
292 'excerpt_trim',
293 [ 'label' => esc_html__('Summary Length (words)', 'uicore-elements') ]
294 );
295 $this->update_control(
296 'show_button',
297 [
298 'label' => esc_html__('Add to Cart Button', 'uicore-elements'),
299 'default' => 'yes'
300 ]
301 );
302 $this->update_control(
303 'text',
304 [
305 'label' => esc_html__('Add to Cart Text', 'uicore-elements'),
306 'label_block' => true,
307 'default' => '',
308 'placeholder' => esc_html__('Add to cart', 'uicore-elements'),
309 ]
310 );
311 $this->update_control(
312 'text_color',
313 [ 'label' => esc_html__('Summary Color', 'uicore-elements') ]
314 );
315 $this->update_control(
316 'text_typography',
317 [ 'label' => esc_html__('Summary Typography', 'uicore-elements') ]
318 );
319 $this->update_control(
320 'text_gap',
321 [ 'label' => esc_html__('Summary Top Space', 'uicore-elements') ]
322 );
323
324 // Update button controls
325 $this->update_control(
326 'border_radius', // button border-radius
327 [
328 'default' => [ 'top' => 0, 'right' => 0, 'bottom' => 0, 'left' => 0 ]
329 ]
330 );
331 $this->update_control(
332 'text_padding', // button padding
333 [
334 'default' => [ 'top' => 10, 'right' => 10, 'bottom' => 10, 'left' => 10, 'unit' => 'px' ]
335 ]
336 );
337 $this->update_control(
338 'button_align',
339 [
340 'condition' => [
341 'button_position!' => 'ui-e-button-placement-image',
342 ],
343 // since product might have ajax add to cart, wich has flex display so the cart added icon do not break button layout,
344 // we justify the flex content to center so justified text align button keeps text centralized. See: https://uicore.atlassian.net/browse/ELM-429
345 'selectors' => [
346 '{{WRAPPER}} .ui-e-readmore' => 'align-self: {{VALUE}}; justify-content: center;',
347 ]
348 ]
349 );
350 $this->update_control(
351 'button_gap',
352 [
353 'condition' => [
354 'button_position!' => 'ui-e-button-placement-image',
355 ]
356 ]
357 );
358 }
359
360 /**
361 * Renders the widget content using AJAX.
362 *
363 * This method retrieves the widget settings; set up the query, and renders each post item.
364 * If no posts are found, it returns false. After rendering, it resets the query and returns the output.
365 *
366 * @param array|false $current_query The current query args, or false if the widget isn't set to use the current query.
367 *
368 * @return string|false The rendered widget content or false if no posts are found.
369 */
370 public function render_ajax($current_query)
371 {
372 // Get settings and post type
373 $settings = $this->get_settings();
374
375 $this->TRAIT_query_products( $settings, $current_query );
376 $wc_query = $this->get_query();
377
378 $products = wc_get_products($wc_query);
379
380 // No products found
381 if ( empty($products) ) {
382 return false;
383 }
384
385 \ob_start();
386 foreach ($products as $index => $product) {
387 $post_object = get_post($product->get_id());
388 setup_postdata($post_object);
389
390 $this->TRAIT_render_product($product, $index, false, true, true);
391
392 wp_reset_postdata();
393 }
394 $markup = \ob_get_clean();
395
396 return [
397 'markup' => $markup,
398 'total_pages' => $wc_query['total_pages']
399 ];
400 }
401
402 protected function render()
403 {
404 if( !class_exists('WooCommerce') ) {
405 if ( $this->is_edit_mode() ) {
406 echo '<p>' . esc_html__('Please enable WooCommerce to use this widget.', 'uicore-elements') . '</p>';
407 }
408 return;
409 }
410
411 global $wp_query;
412 $settings = $this->get_settings();
413
414 // Related products, on save page action at editor, triggers an fata error upon post object handle.
415 // Prevent it for now by disabling the widget on edit mode. TODO: remove it after fixing the issue
416 if ( 'related' === $settings['product-filter_post_type'] && $this->is_edit_mode() ) {
417 echo '<p>' . esc_html__('This widget is set to display related products. To see the results, please preview the page, or customize your widget before setting "current" query.', 'uicore-elements') . '</p>';
418 return;
419 }
420
421 // Build query
422 $products = $this->TRAIT_query_products( $settings, $wp_query->query );
423 $wc_query = $this->get_query();
424
425 $this->TRAIT_set_meta_font_size_to_svg($settings); // SVG icon experiment font-size adjustment
426
427 // Store widget settings in a transient
428 $ID = strval($this->get_ID());
429 set_transient('ui_elements_widgetdata_' . $ID, $settings, \MONTH_IN_SECONDS);
430
431 // Get the quantity of items and creates a loop control
432 $items = $settings['item_limit']['size'];
433 $loops = 0;
434
435 $this->TRAIT_render_filters($settings, true);
436
437 // No posts found
438 if ( empty($products) ) {
439 echo '<p style="text-align:center">' . esc_html__('No products found.', 'uicore-elements') . '</p>';
440
441 } else {
442 ?>
443 <div class="ui-e-adv-grid">
444 <?php
445 foreach ($products as $index => $product) {
446
447 // sticky posts disregards posts per page, so ending the loop if $items == $loop forces the query respects the users item limit
448 if ($settings['sticky'] && $items == $loops) {
449 break;
450 }
451
452 // check is is related post type
453 if ( 'related' === $settings['product-filter_post_type'] ) {
454 $product = wc_get_product($product);
455 }
456
457 $post_object = get_post($product->get_id());
458 setup_postdata($post_object);
459
460 $this->TRAIT_render_product($product, $index, false, true);
461
462 wp_reset_postdata(); // Reset post data after rendering each product
463 $loops++;
464 }
465 ?>
466 </div>
467 <?php
468
469 $this->TRAIT_render_pagination($settings, true);
470
471 // Add some data, to help rest api, to the js
472 if( $this->is_option('pagination', 'yes') && $this->is_option('pagination_type', 'load_more') ) {
473
474 echo '<script>';
475
476 // Add total pages to js variable
477 echo 'window.ui_total_pages_' . esc_html($ID) . ' = ' . esc_html( $wc_query['total_pages'] ) . ';';
478
479 // Pass current query to js variable
480 if( $settings['product-filter_post_type'] == 'current' ) {
481 echo 'window.ui_query_' . esc_html($ID) . ' = ' . \json_encode($wc_query) . ';';
482 }
483
484 echo '</script>';
485 }
486 }
487 }
488 }
489 \Elementor\Plugin::instance()->widgets_manager->register(new AdvancedProductGrid());
490