PluginProbe
UiCore Elements – Free widgets and templates for Elementor / 1.0.14
UiCore Elements – Free widgets and templates for Elementor v1.0.14
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.14, 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 $styles = array_diff($styles, ['advanced-post-grid']);
52
53 // Add the product grid style
54 $styles[] = 'advanced-product-grid';
55
56 return $styles;
57 }
58 function get_query()
59 {
60 return $this->_query;
61 }
62
63 protected function register_controls()
64 {
65 // Fallback
66 if( !class_exists('WooCommerce') ) {
67 $this->start_controls_section(
68 'section_fallback',
69 [
70 'label' => esc_html__('Content', 'uicore-elements'),
71 ]
72 );
73 $this->add_control(
74 'woocommerce_warning',
75 [
76 'type' => \Elementor\Controls_Manager::ALERT,
77 'alert_type' => 'warning',
78 'content' => esc_html__('Please enable WooCommerce to use this widget.', 'uicore-elements'),
79 ]
80 );
81 $this->end_controls_section();
82 return;
83 }
84
85 parent::register_controls();
86
87 // Remove query source and meta controls from parent
88 $this->remove_control('posts-filter_post_type');
89 $this->remove_control('top_meta');
90 $this->remove_control('before_title_meta');
91 $this->remove_control('after_title_meta');
92 $this->remove_control('bottom_meta');
93
94 // Register query control with updated options
95 $this->start_injection( [
96 'of' => 'section_post_grid_def',
97 'at' => 'after',
98 ] );
99 $this->add_group_control(
100 Product_Filter::get_type(),
101 [
102 'name' => 'product-filter',
103 'label' => esc_html__('Products', 'uicore-elements'),
104 'description' => esc_html__('Current Query Settings > Reading', 'uicore-elements'),
105 ]
106 );
107 $this->end_injection();
108
109 // Register meta controls with updated options
110 $this->start_injection( [
111 'of' => 'section_extra_item_content',
112 'at' => 'after',
113 ] );
114 $this->TRAIT_register_post_meta_controls(false, true);
115 $this->end_injection();
116
117 // Register new button placement control
118 $this->start_injection( [
119 'of' => 'section_button_style',
120 'at' => 'after',
121 ] );
122 $this->add_control(
123 'button_position',
124 [
125 'label' => esc_html__('Placement', 'uicore-elements'),
126 'type' => \Elementor\Controls_Manager::SELECT,
127 'options' => [
128 '' => 'Default',
129 'ui-e-button-placement-image' => 'On Image',
130 ],
131 'default' => 'ui-e-button-placement-image',
132 'render_type' => 'template',
133 'prefix_class' => ''
134 ]
135 );
136 $this->end_injection();
137
138 // Register new Swatches and Sale Badge controls
139 $this->start_injection( [
140 'of' => 'show_button',
141 'at' => 'after',
142 ] );
143 $this->add_control(
144 'show_swatches',
145 [
146 'label' => esc_html__('Swatches', 'uicore-elements'),
147 'type' => \Elementor\Controls_Manager::SWITCHER,
148 'label_on' => esc_html__( 'Show', 'uicore-elements' ),
149 'label_off' => esc_html__( 'Hide', 'uicore-elements' ),
150 'description' => esc_html__('Will only work if you have Uicore Framework plugin active at least on version 6.0.1', 'uicore-elements'),
151 'default' => 'yes',
152 ]
153 );
154 $this->add_control(
155 'show_sale_badge',
156 [
157 'label' => esc_html__('Sale Badge', 'uicore-elements'),
158 'type' => \Elementor\Controls_Manager::SWITCHER,
159 'label_on' => esc_html__( 'Show', 'uicore-elements' ),
160 'label_off' => esc_html__( 'Hide', 'uicore-elements' ),
161 'default' => 'yes',
162 'prefix_class' => 'ui-e-show-sale-badge-',
163 ]
164 );
165 $this->add_control(
166 'badge_warning',
167 [
168 'type' => \Elementor\Controls_Manager::RAW_HTML,
169 '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'),
170 'content_classes' => 'elementor-control-field-description',
171 'condition' => [
172 'show_sale_badge' => 'yes',
173 ],
174 ]
175 );
176 $this->end_injection();
177
178 // Register new Hide out of stock products control
179 $this->start_injection( [
180 'of' => 'sticky',
181 'at' => 'before',
182 ] );
183 $this->add_control(
184 'hide_out_of_stock',
185 [
186 'label' => esc_html__('Hide out of stock', 'uicore-elements'),
187 'type' => \Elementor\Controls_Manager::SWITCHER,
188 'condition' => [
189 'product-filter_post_type!' => 'related',
190 ],
191 ]
192 );
193 $this->end_injection();
194
195
196 // Update controls that uses 'posts_filter' to 'product_filter'
197 $this->update_control(
198 'item_limit',
199 [
200 'condition' => [
201 'product-filter_post_type!' => 'current',
202 ],
203 ]
204 );
205
206 // Update filters options
207 $this->update_control(
208 'filters_taxonomies',
209 [
210 'options' => Helper::get_taxonomies(true, true),
211 'default' => ['product_cat'],
212 ]
213 );
214
215 // Update Image animation options and default value
216 $this->update_control(
217 'anim_image',
218 [
219 'options' => [
220 '' => __('None', 'uicore-elements'),
221 'ui-e-img-anim-zoom' => __('Zoom', 'uicore-elements'),
222 'ui-e-img-anim-change' => __('Change image', 'uicore-elements'),
223 ],
224 'default' => 'ui-e-img-anim-change',
225 'render_type' => 'template', // `change image` adds a new html element
226 ]
227 );
228
229 // Update top meta style options so sale badge (default top meta) looks better by default
230 $this->update_control(
231 'top_meta_color',
232 [
233 'default' => '#FFF',
234 ]
235 );
236 $this->update_control(
237 'top_meta_background',
238 [
239 'default' => '#000',
240 ]
241 );
242 $this->update_control(
243 'top_meta_padding',
244 [
245 'default' => [
246 'top' => 5,
247 'right' => 5,
248 'bottom' => 5,
249 'left' => 5,
250 'unit' => 'px'
251 ],
252 ]
253 );
254
255 // Update some UI controls labels and default values
256 $this->update_control(
257 'excerpt',
258 [
259 'label' => esc_html__('Product Summary', 'uicore-elements'),
260 'default' => ''
261 ]
262 );
263 $this->update_control(
264 'excerpt_trim',
265 [ 'label' => esc_html__('Summary Length (words)', 'uicore-elements') ]
266 );
267 $this->update_control(
268 'show_button',
269 [
270 'label' => esc_html__('Add to Cart Button', 'uicore-elements'),
271 'default' => 'yes'
272 ]
273 );
274 $this->update_control(
275 'text',
276 [
277 'label' => esc_html__('Custom Text', 'uicore-elements'),
278 'default' => esc_html__('', 'uicore-elements'),
279 'description' => esc_html__('If left blank, the default add to cart button text will be used.', 'uicore-elements')
280 ]
281 );
282 $this->update_control(
283 'text_color',
284 [ 'label' => esc_html__('Summary Color', 'uicore-elements') ]
285 );
286 $this->update_control(
287 'text_typography',
288 [ 'label' => esc_html__('Summary Typography', 'uicore-elements') ]
289 );
290 $this->update_control(
291 'text_gap',
292 [ 'label' => esc_html__('Summary Top Space', 'uicore-elements') ]
293 );
294
295 // Update button controls
296 $this->update_control(
297 'border_radius', // button border-radius
298 [
299 'default' => [ 'top' => 0, 'right' => 0, 'bottom' => 0, 'left' => 0 ]
300 ]
301 );
302 $this->update_control(
303 'text_padding', // button padding
304 [
305 'default' => [ 'top' => 10, 'right' => 10, 'bottom' => 10, 'left' => 10, 'unit' => 'px' ]
306 ]
307 );
308 $this->update_control(
309 'button_align',
310 [
311 'condition' => [
312 'button_position!' => 'ui-e-button-placement-image',
313 ]
314 ]
315 );
316 $this->update_control(
317 'button_gap',
318 [
319 'condition' => [
320 'button_position!' => 'ui-e-button-placement-image',
321 ]
322 ]
323 );
324 }
325
326 /**
327 * Renders the widget content using AJAX.
328 *
329 * This method retrieves the widget settings; set up the query, and renders each post item.
330 * If no posts are found, it returns false. After rendering, it resets the query and returns the output.
331 *
332 * @param array|false $current_query The current query args, or false if the widget isn't set to use the current query.
333 *
334 * @return string|false The rendered widget content or false if no posts are found.
335 */
336 public function render_ajax($current_query)
337 {
338 // Get settings and post type
339 $settings = $this->get_settings();
340
341 $this->TRAIT_query_products( $settings, $current_query );
342 $wc_query = $this->get_query();
343
344 $products = wc_get_products($wc_query);
345
346 // No products found
347 if ( empty($products) ) {
348 return false;
349 }
350
351 // TODO: check related render method
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>' . __('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>' . __('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">' . __('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