PluginProbe
UiCore Elements – Free widgets and templates for Elementor / 1.2.0
UiCore Elements – Free widgets and templates for Elementor v1.2.0
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.0, at includes/widgets/advanced-product-grid.php

458 lines 15.4 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 // Update controls that uses 'posts_filter' to 'product_filter'
199 $this->update_control(
200 'item_limit',
201 [
202 'condition' => [
203 'product-filter_post_type!' => 'current',
204 ],
205 ]
206 );
207
208 // Update filters options
209 $this->update_control(
210 'filters_taxonomies',
211 [
212 'options' => Helper::get_taxonomies(true, true),
213 'default' => ['product_cat'],
214 ]
215 );
216
217 // Update Image animation options and default value
218 $this->update_control(
219 'anim_image',
220 [
221 'options' => [
222 '' => __('None', 'uicore-elements'),
223 'ui-e-img-anim-zoom' => __('Zoom', 'uicore-elements'),
224 'ui-e-img-anim-change' => __('Change image', 'uicore-elements'),
225 ],
226 'default' => 'ui-e-img-anim-change',
227 'render_type' => 'template', // `change image` adds a new html element
228 ]
229 );
230
231 // Update top meta style options so sale badge (default top meta) looks better by default
232 $this->update_control(
233 'top_meta_color',
234 [
235 'default' => '#FFF',
236 ]
237 );
238 $this->update_control(
239 'top_meta_background',
240 [
241 'default' => '#000',
242 ]
243 );
244 $this->update_control(
245 'top_meta_padding',
246 [
247 'default' => [
248 'top' => 5,
249 'right' => 5,
250 'bottom' => 5,
251 'left' => 5,
252 'unit' => 'px'
253 ],
254 ]
255 );
256
257 // Update some UI controls labels and default values
258 $this->update_control(
259 'excerpt',
260 [
261 'label' => esc_html__('Product Summary', 'uicore-elements'),
262 'default' => ''
263 ]
264 );
265 $this->update_control(
266 'excerpt_trim',
267 [ 'label' => esc_html__('Summary Length (words)', 'uicore-elements') ]
268 );
269 $this->update_control(
270 'show_button',
271 [
272 'label' => esc_html__('Add to Cart Button', 'uicore-elements'),
273 'default' => 'yes'
274 ]
275 );
276 $this->update_control(
277 'text',
278 [
279 'label' => esc_html__('Custom Text', 'uicore-elements'),
280 'default' => '',
281 'description' => esc_html__('If left blank, the default add to cart button text will be used.', 'uicore-elements')
282 ]
283 );
284 $this->update_control(
285 'text_color',
286 [ 'label' => esc_html__('Summary Color', 'uicore-elements') ]
287 );
288 $this->update_control(
289 'text_typography',
290 [ 'label' => esc_html__('Summary Typography', 'uicore-elements') ]
291 );
292 $this->update_control(
293 'text_gap',
294 [ 'label' => esc_html__('Summary Top Space', 'uicore-elements') ]
295 );
296
297 // Update button controls
298 $this->update_control(
299 'border_radius', // button border-radius
300 [
301 'default' => [ 'top' => 0, 'right' => 0, 'bottom' => 0, 'left' => 0 ]
302 ]
303 );
304 $this->update_control(
305 'text_padding', // button padding
306 [
307 'default' => [ 'top' => 10, 'right' => 10, 'bottom' => 10, 'left' => 10, 'unit' => 'px' ]
308 ]
309 );
310 $this->update_control(
311 'button_align',
312 [
313 'condition' => [
314 'button_position!' => 'ui-e-button-placement-image',
315 ]
316 ]
317 );
318 $this->update_control(
319 'button_gap',
320 [
321 'condition' => [
322 'button_position!' => 'ui-e-button-placement-image',
323 ]
324 ]
325 );
326 }
327
328 /**
329 * Renders the widget content using AJAX.
330 *
331 * This method retrieves the widget settings; set up the query, and renders each post item.
332 * If no posts are found, it returns false. After rendering, it resets the query and returns the output.
333 *
334 * @param array|false $current_query The current query args, or false if the widget isn't set to use the current query.
335 *
336 * @return string|false The rendered widget content or false if no posts are found.
337 */
338 public function render_ajax($current_query)
339 {
340 // Get settings and post type
341 $settings = $this->get_settings();
342
343 $this->TRAIT_query_products( $settings, $current_query );
344 $wc_query = $this->get_query();
345
346 $products = wc_get_products($wc_query);
347
348 // No products found
349 if ( empty($products) ) {
350 return false;
351 }
352
353 \ob_start();
354 foreach ($products as $product) {
355 $post_object = get_post($product->get_id());
356 setup_postdata($post_object);
357
358 $this->TRAIT_render_product($product, false, true, true);
359
360 wp_reset_postdata();
361 }
362 $markup = \ob_get_clean();
363
364 return [
365 'markup' => $markup,
366 'total_pages' => $wc_query['total_pages']
367 ];
368 }
369
370 protected function render()
371 {
372 if( !class_exists('WooCommerce') ) {
373 if ( $this->is_edit_mode() ) {
374 echo '<p>' . esc_html__('Please enable WooCommerce to use this widget.', 'uicore-elements') . '</p>';
375 }
376 return;
377 }
378
379 global $wp_query;
380 $settings = $this->get_settings();
381
382 // Related products, on save page action at editor, triggers an fata error upon post object handle.
383 // Prevent it for now by disabling the widget on edit mode. TODO: remove it after fixing the issue
384 if ( 'related' === $settings['product-filter_post_type'] && $this->is_edit_mode() ) {
385 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>';
386 return;
387 }
388
389 // Build query
390 $products = $this->TRAIT_query_products( $settings, $wp_query->query );
391 $wc_query = $this->get_query();
392
393 $this->TRAIT_set_meta_font_size_to_svg($settings); // SVG icon experiment font-size adjustment
394
395 // Store widget settings in a transient
396 $ID = strval($this->get_ID());
397 set_transient('ui_elements_widgetdata_' . $ID, $settings, \MONTH_IN_SECONDS);
398
399 // Get the quantity of items and creates a loop control
400 $items = $settings['item_limit']['size'];
401 $loops = 0;
402
403 $this->TRAIT_render_filters($settings, true);
404
405 // No posts found
406 if ( empty($products) ) {
407 echo '<p style="text-align:center">' . esc_html__('No products found.', 'uicore-elements') . '</p>';
408
409 } else {
410 ?>
411 <div class="ui-e-adv-grid">
412 <?php
413 foreach ($products as $product) {
414
415 // sticky posts disregards posts per page, so ending the loop if $items == $loop forces the query respects the users item limit
416 if ($settings['sticky'] && $items == $loops) {
417 break;
418 }
419
420 // check is is related post type
421 if ( 'related' === $settings['product-filter_post_type'] ) {
422 $product = wc_get_product($product);
423 }
424
425 $post_object = get_post($product->get_id());
426 setup_postdata($post_object);
427
428 $this->TRAIT_render_product($product, false, true);
429
430 wp_reset_postdata(); // Reset post data after rendering each product
431 $loops++;
432 }
433 ?>
434 </div>
435 <?php
436
437 $this->TRAIT_render_pagination($settings, true);
438
439 // Add some data, to help rest api, to the js
440 if( $settings['pagination'] == 'yes' && $settings['pagination_type'] == 'load_more' ) {
441
442 echo '<script>';
443
444 // Add total pages to js variable
445 echo 'window.ui_total_pages_' . esc_html($ID) . ' = ' . esc_html( $wc_query['total_pages'] ) . ';';
446
447 // Pass current query to js variable
448 if( $settings['product-filter_post_type'] == 'current' ) {
449 echo 'window.ui_query_' . esc_html($ID) . ' = ' . \json_encode($wc_query) . ';';
450 }
451
452 echo '</script>';
453 }
454 }
455 }
456 }
457 \Elementor\Plugin::instance()->widgets_manager->register(new AdvancedProductGrid());
458