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

460 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' => esc_html__('', 'uicore-elements'),
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 // TODO: check related render method
354
355 \ob_start();
356 foreach ($products as $product) {
357 $post_object = get_post($product->get_id());
358 setup_postdata($post_object);
359
360 $this->TRAIT_render_product($product, false, true, true);
361
362 wp_reset_postdata();
363 }
364 $markup = \ob_get_clean();
365
366 return [
367 'markup' => $markup,
368 'total_pages' => $wc_query['total_pages']
369 ];
370 }
371
372 protected function render()
373 {
374 if( !class_exists('WooCommerce') ) {
375 if ( $this->is_edit_mode() ) {
376 echo '<p>' . __('Please enable WooCommerce to use this widget.', 'uicore-elements') . '</p>';
377 }
378 return;
379 }
380
381 global $wp_query;
382 $settings = $this->get_settings();
383
384 // Related products, on save page action at editor, triggers an fata error upon post object handle.
385 // Prevent it for now by disabling the widget on edit mode. TODO: remove it after fixing the issue
386 if ( 'related' === $settings['product-filter_post_type'] && $this->is_edit_mode() ) {
387 echo '<p>' . __('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>';
388 return;
389 }
390
391 // Build query
392 $products = $this->TRAIT_query_products( $settings, $wp_query->query );
393 $wc_query = $this->get_query();
394
395 $this->TRAIT_set_meta_font_size_to_svg($settings); // SVG icon experiment font-size adjustment
396
397 // Store widget settings in a transient
398 $ID = strval($this->get_ID());
399 set_transient('ui_elements_widgetdata_' . $ID, $settings, \MONTH_IN_SECONDS);
400
401 // Get the quantity of items and creates a loop control
402 $items = $settings['item_limit']['size'];
403 $loops = 0;
404
405 $this->TRAIT_render_filters($settings, true);
406
407 // No posts found
408 if ( empty($products) ) {
409 echo '<p style="text-align:center">' . __('No products found.', 'uicore-elements') . '</p>';
410
411 } else {
412 ?>
413 <div class="ui-e-adv-grid">
414 <?php
415 foreach ($products as $product) {
416
417 // sticky posts disregards posts per page, so ending the loop if $items == $loop forces the query respects the users item limit
418 if ($settings['sticky'] && $items == $loops) {
419 break;
420 }
421
422 // check is is related post type
423 if ( 'related' === $settings['product-filter_post_type'] ) {
424 $product = wc_get_product($product);
425 }
426
427 $post_object = get_post($product->get_id());
428 setup_postdata($post_object);
429
430 $this->TRAIT_render_product($product, false, true);
431
432 wp_reset_postdata(); // Reset post data after rendering each product
433 $loops++;
434 }
435 ?>
436 </div>
437 <?php
438
439 $this->TRAIT_render_pagination($settings, true);
440
441 // Add some data, to help rest api, to the js
442 if( $settings['pagination'] == 'yes' && $settings['pagination_type'] == 'load_more' ) {
443
444 echo '<script>';
445
446 // Add total pages to js variable
447 echo 'window.ui_total_pages_' . esc_html($ID) . ' = ' . esc_html( $wc_query['total_pages'] ) . ';';
448
449 // Pass current query to js variable
450 if( $settings['product-filter_post_type'] == 'current' ) {
451 echo 'window.ui_query_' . esc_html($ID) . ' = ' . \json_encode($wc_query) . ';';
452 }
453
454 echo '</script>';
455 }
456 }
457 }
458 }
459 \Elementor\Plugin::instance()->widgets_manager->register(new AdvancedProductGrid());
460