PluginProbe
UiCore Elements – Free widgets and templates for Elementor / 1.3.17
UiCore Elements – Free widgets and templates for Elementor v1.3.17
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
← All changes | includes/utils/meta-component.php +533 -237 1.0.61.3.17 View file →
@@ -1,5 +1,6 @@
1 1 <?php
2 +
2 3 namespace UiCoreElements\Utils;
3 4
4 5 use Elementor\Controls_Manager;
5 6 use Elementor\Group_Control_Typography;
@@ -4,101 +5,194 @@
4 5 use Elementor\Controls_Manager;
5 6 use Elementor\Group_Control_Typography;
6 7 use UiCoreElements\Helper;
7 8
8 -if ( ! defined( 'ABSPATH' ) ) {
9 - exit; // Exit if accessed directly
9 +if (! defined('ABSPATH')) {
10 + exit; // Exit if accessed directly
10 11 }
11 12
12 -trait Meta_Trait {
13 +trait Meta_Trait
14 +{
13 15
14 - function get_meta_content_controls()
16 + function get_meta_content_controls($product_metas = false)
15 17 {
16 18
19 + // Meta options
17 20 $options = [
18 - 'none' => __( 'None', 'uicore-elements' ),
19 - 'author' => __( 'Author', 'uicore-elements' ),
20 - 'date' => __( 'Posted Date', 'uicore-elements' ),
21 - 'updated date' => __( 'Updated Date', 'uicore-elements' ),
22 - 'comment' => __( 'Comments Count', 'uicore-elements' ),
23 - 'reading time' => __( 'Reading Time', 'uicore-elements' ),
24 - 'category' => __( 'Category', 'uicore-elements' ),
25 - 'custom meta' => __( 'Custom Meta', 'uicore-elements' ),
26 - 'custom taxonomy' => __( 'Custom Taxonomy', 'uicore-elements' ),
27 - 'custom html' => __( 'Custom HTML', 'uicore-elements' ),
21 + 'none' => __('None', 'uicore-elements'),
28 22 ];
23 + $generic_posts = [
24 + 'author' => __('Author', 'uicore-elements'),
25 + 'date' => __('Posted Date', 'uicore-elements'),
26 + 'updated date' => __('Updated Date', 'uicore-elements'),
27 + 'comment' => __('Comments Count', 'uicore-elements'),
28 + 'reading time' => __('Reading Time', 'uicore-elements'),
29 + 'category' => __('Category', 'uicore-elements'),
30 + 'tag' => __('Tag', 'uicore-elements'),
31 + 'excerpt' => __('Excerpt', 'uicore-elements'),
32 + ];
33 + $products = [
34 + 'product price' => __('Product Price', 'uicore-elements'),
35 + 'product rating' => __('Product Rating', 'uicore-elements'),
36 + 'product stock' => __('Product Stock', 'uicore-elements'),
37 + 'product category' => __('Product Category', 'uicore-elements'),
38 + 'product tag' => __('Product Tag', 'uicore-elements'),
39 + 'product attribute' => __('Product Attribute', 'uicore-elements'),
40 + 'product sale' => __('Product Sale', 'uicore-elements'),
41 + ];
42 + $custom = [
43 + 'custom meta' => __('Custom Meta', 'uicore-elements'),
44 + 'custom taxonomy' => __('Custom Taxonomy', 'uicore-elements'),
45 + 'custom html' => __('Custom HTML', 'uicore-elements'),
46 + ];
47 + $portfolio = [
48 + 'portfolio category' => __('Portfolio Category', 'uicore-elements'),
49 + ];
29 50
30 - //add woocomerce meta to options if woocommerce is instaled
31 - $options['product price'] = __( 'Product Price', 'uicore-elements' );
32 - $options['product rating'] = __( 'Product Rating', 'uicore-elements' );
33 - // $options['product stock'] = __( 'Product Stock', 'uicore-elements' );
34 - $options['product category'] = __( 'Product Category', 'uicore-elements' );
35 - $options['product tag'] = __( 'Product Tag', 'uicore-elements' );
36 - $options['product attribute'] = __( 'Product Attribute', 'uicore-elements' );
51 + $options = $product_metas ?
52 + array_merge($options, $products, $custom) : // specific to product widgets
53 + array_merge($options, $generic_posts, $products, $portfolio, $custom); // generic posts widgets
37 54
38 - $options['portfolio category'] = __( 'Portfolio Category', 'uicore-elements' );
39 55
40 56 $repeater = new \Elementor\Repeater();
41 - $repeater->add_control('type',[
42 - 'label' => __( 'Meta', 'uicore-elements' ),
57 + $repeater->add_control(
58 + 'type',
59 + [
60 + 'label' => __('Meta', 'uicore-elements'),
43 61 'type' => Controls_Manager::SELECT,
44 62 'default' => '',
45 63 'options' => $options
46 64 ]
47 65 );
48 - $repeater->add_control('type_custom',[
49 - 'label' => __( 'Custom Field Name', 'uicore-elements' ),
50 - 'type' => Controls_Manager::TEXT,
51 - 'condition' => [
52 - 'type' => ['custom meta','custom taxonomy']
53 - ]
66 + $repeater->add_control(
67 + 'type_custom',
68 + [
69 + 'label' => __('Custom Field Name', 'uicore-elements'),
70 + 'type' => Controls_Manager::TEXT,
71 + 'condition' => [
72 + 'type' => ['custom meta', 'custom taxonomy']
54 73 ]
55 - );
56 - $repeater->add_control('html_custom',[
57 - 'label' => __( 'Custom HTML', 'uicore-elements' ),
58 - 'type' => Controls_Manager::TEXTAREA,
59 - 'condition' => [
60 - 'type' => ['custom html']
61 - ]
74 + ]
75 + );
76 + $repeater->add_control(
77 + 'html_custom',
78 + [
79 + 'label' => __('Custom HTML', 'uicore-elements'),
80 + 'type' => Controls_Manager::TEXTAREA,
81 + 'condition' => [
82 + 'type' => ['custom html']
62 83 ]
63 - );
64 - $repeater->add_control('date_format',[
65 - 'label' => esc_html__( 'Date Format', 'uicore-elements' ),
66 - 'type' => Controls_Manager::SELECT,
67 - 'default' => 'default',
68 - 'options' => [
69 - 'default' => esc_html__( 'Default', 'uicore-elements' ),
70 - 'F j, Y' => gmdate( 'F j, Y' ),
71 - 'Y-m-d' => gmdate( 'Y-m-d' ),
72 - 'm/d/Y' => gmdate( 'm/d/Y' ),
73 - 'd/m/Y' => gmdate( 'd/m/Y' ),
74 - 'custom' => esc_html__( 'Custom', 'uicore-elements' ),
75 - ],
76 - 'condition' => [
77 - 'type' => ['date','updated date']
78 - ]
84 + ]
85 + );
86 + $repeater->add_control(
87 + 'date_format',
88 + [
89 + 'label' => esc_html__('Date Format', 'uicore-elements'),
90 + 'type' => Controls_Manager::SELECT,
91 + 'default' => 'default',
92 + 'options' => [
93 + 'default' => esc_html__('Default', 'uicore-elements'),
94 + 'F j, Y' => gmdate('F j, Y'),
95 + 'Y-m-d' => gmdate('Y-m-d'),
96 + 'm/d/Y' => gmdate('m/d/Y'),
97 + 'd/m/Y' => gmdate('d/m/Y'),
98 + 'custom' => esc_html__('Custom', 'uicore-elements'),
99 + ],
100 + 'condition' => [
101 + 'type' => ['date', 'updated date']
79 102 ]
80 - );
81 - $repeater->add_control('custom_format',[
82 - 'label' => esc_html__( 'Custom Format', 'uicore-elements' ),
83 - 'default' => get_option( 'date_format' ) . ' ' . get_option( 'time_format' ),
84 - 'description' => sprintf( '<a href="https://wordpress.org/documentation/article/customize-date-and-time-format/" target="_blank">%s</a>', esc_html__( 'Documentation on date and time formatting', 'uicore-elements' ) ),
85 - 'condition' => [
86 - 'date_format' => 'custom',
87 - 'type' => ['date','updated date']
88 - ],
103 + ]
104 + );
105 + $repeater->add_control(
106 + 'custom_format',
107 + [
108 + 'label' => esc_html__('Custom Format', 'uicore-elements'),
109 + 'default' => get_option('date_format') . ' ' . get_option('time_format'),
110 + 'description' => sprintf('<a href="https://wordpress.org/documentation/article/customize-date-and-time-format/" target="_blank">%s</a>', esc_html__('Documentation on date and time formatting', 'uicore-elements')),
111 + 'condition' => [
112 + 'date_format' => 'custom',
113 + 'type' => ['date', 'updated date']
114 + ],
115 + ]
116 + );
117 + $repeater->add_control(
118 + 'stock_type',
119 + [
120 + 'label' => esc_html__('Stock Data', 'uicore-elements'),
121 + 'type' => Controls_Manager::SELECT,
122 + 'default' => 'qty',
123 + 'options' => [
124 + 'qty' => esc_html__('Quantity', 'uicore-elements'),
125 + 'in' => esc_html__('In/Out of stock', 'uicore-elements'),
126 + ],
127 + 'condition' => [
128 + 'type' => ['product stock']
89 129 ]
90 - );
91 - $repeater->add_control('before',[
92 - 'label' => __( 'Text Before', 'uicore-elements' ),
130 + ]
131 + );
132 + $repeater->add_control(
133 + 'in_stock_text',
134 + [
135 + 'label' => esc_html__('In Stock Text', 'uicore-elements'),
93 136 'type' => Controls_Manager::TEXT,
137 + 'default' => esc_html__('In Stock', 'uicore-elements'),
94 138 'condition' => [
139 + 'type' => ['product stock'],
140 + 'stock_type' => 'in',
141 + ]
142 + ]
143 + );
144 + $repeater->add_control(
145 + 'out_of_stock_text',
146 + [
147 + 'label' => esc_html__('Out of Stock Text', 'uicore-elements'),
148 + 'type' => Controls_Manager::TEXT,
149 + 'default' => esc_html__('Out of Stock', 'uicore-elements'),
150 + 'condition' => [
151 + 'type' => ['product stock'],
152 + ]
153 + ]
154 + );
155 + $repeater->add_control(
156 + 'in_stock_color',
157 + [
158 + 'label' => __('In Stock Color', 'uicore-elements'),
159 + 'type' => Controls_Manager::COLOR,
160 + 'selectors' => [
161 + '{{WRAPPER}} .in-stock' => 'color: {{VALUE}}',
162 + ],
163 + 'condition' => [
164 + 'type' => ['product stock'],
165 + ]
166 + ]
167 + );
168 + $repeater->add_control(
169 + 'out_of_stock_color',
170 + [
171 + 'label' => __('Out of Stock Color', 'uicore-elements'),
172 + 'type' => Controls_Manager::COLOR,
173 + 'selectors' => [
174 + '{{WRAPPER}} .out-of-stock' => 'color: {{VALUE}}',
175 + ],
176 + 'condition' => [
177 + 'type' => ['product stock'],
178 + ]
179 + ]
180 + );
181 + $repeater->add_control(
182 + 'before',
183 + [
184 + 'label' => __('Text Before', 'uicore-elements'),
185 + 'type' => Controls_Manager::TEXT,
186 + 'condition' => [
95 187 'type!' => 'none',
96 188 ]
97 189 ]
98 190 );
99 - $repeater->add_control('after',[
100 - 'label' => __( 'Text After', 'uicore-elements' ),
191 + $repeater->add_control(
192 + 'after',
193 + [
194 + 'label' => __('Text After', 'uicore-elements'),
101 195 'type' => Controls_Manager::TEXT,
102 196 'condition' => [
103 197 'type!' => 'none',
104 198 ]
@@ -103,16 +197,18 @@
103 197 'type!' => 'none',
104 198 ]
105 199 ]
106 200 );
107 - $repeater->add_control('autor_display',[
108 - 'label' => __( 'Display Type', 'uicore-elements' ),
201 + $repeater->add_control(
202 + 'autor_display',
203 + [
204 + 'label' => __('Display Type', 'uicore-elements'),
109 205 'type' => Controls_Manager::SELECT,
110 206 'default' => 'name',
111 207 'options' => [
112 - 'name' => __( 'Name', 'uicore-elements' ),
113 - 'full' => __( 'Avatar & Name', 'uicore-elements' ),
114 - 'avatar' => __( 'Avatar', 'uicore-elements' ),
208 + 'name' => __('Name', 'uicore-elements'),
209 + 'full' => __('Avatar & Name', 'uicore-elements'),
210 + 'avatar' => __('Avatar', 'uicore-elements'),
115 211 ],
116 212 'condition' => [
117 213 'type' => 'author',
118 214 ],
@@ -117,15 +213,16 @@
117 213 'type' => 'author',
118 214 ],
119 215 ]
120 216 );
121 - $repeater->add_control('icon',[
122 - 'label' => __( 'Icon', 'uicore-elements' ),
217 + $repeater->add_control(
218 + 'icon',
219 + [
220 + 'label' => __('Icon', 'uicore-elements'),
123 221 'type' => Controls_Manager::ICONS,
124 222 'condition' => [
125 223 'autor_display' => 'name',
126 224 'type!' => 'none',
127 -
128 225 ],
129 226 ]
130 227 );
131 228 return $repeater->get_controls();
@@ -133,128 +230,131 @@
133 230
134 231 function get_meta_style_controls($position = 'tb-meta')
135 232 {
136 233 $this->add_group_control(
137 - Group_Control_Typography::get_type(),
138 - [
139 - 'name' => $position.'_meta_typography',
140 - 'selector' => '{{WRAPPER}} .ui-e-'.$position,
234 + Group_Control_Typography::get_type(),
235 + [
236 + 'name' => $position . '_meta_typography',
237 + 'selector' => '{{WRAPPER}} .ui-e-' . $position,
141 238 'separator' => 'before',
142 - ]
143 - );
239 + ]
240 + );
144 241 $this->add_control(
145 - $position.'_meta_color',
146 - [
147 - 'label' => __( 'Text Color', 'uicore-elements' ),
148 - 'type' => Controls_Manager::COLOR,
149 - 'selectors' => [
150 - '{{WRAPPER}} .ui-e-'.$position => 'color: {{VALUE}}',
151 -
152 - ],
153 - ]
154 - );
242 + $position . '_meta_color',
243 + [
244 + 'label' => __('Text Color', 'uicore-elements'),
245 + 'type' => Controls_Manager::COLOR,
246 + 'selectors' => [
247 + '{{WRAPPER}} .ui-e-' . $position => 'color: {{VALUE}}',
248 + '{{WRAPPER}} .ui-e-' . $position . ' svg' => 'fill: {{VALUE}}',
249 + ],
250 + ]
251 + );
155 252 $this->add_control(
156 - $position.'_link_color',
157 - [
158 - 'label' => __( 'Link Color', 'uicore-elements' ),
159 - 'type' => Controls_Manager::COLOR,
160 - 'selectors' => [
161 - '{{WRAPPER}} .ui-e-'.$position.' .ui-e-meta-item a' => 'color: {{VALUE}}',
162 -
163 - ],
164 - ]
165 - );
253 + $position . '_link_color',
254 + [
255 + 'label' => __('Link Color', 'uicore-elements'),
256 + 'type' => Controls_Manager::COLOR,
257 + 'selectors' => [
258 + '{{WRAPPER}} .ui-e-' . $position . ' .ui-e-meta-item a' => 'color: {{VALUE}}',
259 + '{{WRAPPER}} .ui-e-' . $position . ' .ui-e-meta-item svg' => 'fill: {{VALUE}}',
260 + ],
261 + ]
262 + );
166 263 $this->add_control(
167 - $position.'_linkh_color',
168 - [
169 - 'label' => __( 'Link Hover Color', 'uicore-elements' ),
170 - 'type' => Controls_Manager::COLOR,
171 - 'selectors' => [
172 - '{{WRAPPER}} .ui-e-'.$position.' .ui-e-meta-item a:hover' => 'color: {{VALUE}}',
173 -
174 - ],
175 - ]
176 - );
264 + $position . '_linkh_color',
265 + [
266 + 'label' => __('Link Hover Color', 'uicore-elements'),
267 + 'type' => Controls_Manager::COLOR,
268 + 'selectors' => [
269 + '{{WRAPPER}} .ui-e-' . $position . ' .ui-e-meta-item a:hover' => 'color: {{VALUE}}',
270 + ],
271 + ]
272 + );
177 273 $this->add_control(
178 - $position.'_meta_background',
179 - [
180 - 'label' => __( 'Background Color', 'uicore-elements' ),
181 - 'type' => Controls_Manager::COLOR,
182 - 'selectors' => [
183 - '{{WRAPPER}} .ui-e-'.$position.' .ui-e-meta-item' => 'background-color: {{VALUE}}',
274 + $position . '_meta_background',
275 + [
276 + 'label' => __('Background Color', 'uicore-elements'),
277 + 'type' => Controls_Manager::COLOR,
278 + 'selectors' => [
279 + '{{WRAPPER}} .ui-e-' . $position . ' .ui-e-meta-item' => 'background-color: {{VALUE}}',
184 280
185 - ],
186 - ]
187 - );
281 + ],
282 + ]
283 + );
188 284 $this->add_control(
189 - $position.'_meta_radius',
190 - [
191 - 'label' => esc_html__('Border Radius', 'uicore-elements'),
192 - 'type' => Controls_Manager::DIMENSIONS,
193 - 'selectors' => [
194 - '{{WRAPPER}} .ui-e-'.$position.' .ui-e-meta-item' => 'border-radius: {{TOP}}px {{RIGHT}}px {{BOTTOM}}px {{LEFT}}px;'
195 - ],
196 - ]
197 - );
285 + $position . '_meta_radius',
286 + [
287 + 'label' => esc_html__('Border Radius', 'uicore-elements'),
288 + 'type' => Controls_Manager::DIMENSIONS,
289 + 'selectors' => [
290 + '{{WRAPPER}} .ui-e-' . $position . ' .ui-e-meta-item' => 'border-radius: {{TOP}}px {{RIGHT}}px {{BOTTOM}}px {{LEFT}}px;'
291 + ],
292 + ]
293 + );
198 294 $this->add_group_control(
199 - \Elementor\Group_Control_Box_Shadow::get_type(),
200 - [
201 - 'name' => $position.'_meta_shadow',
202 - 'label' => esc_html__( 'Box Shadow', 'uicore-elements' ),
203 - 'selector' => '{{WRAPPER}} .ui-e-'.$position.' .ui-e-meta-item',
204 - ]
205 - );
206 - $this->add_responsive_control(
207 - $position.'_meta_padding',
208 - [
209 - 'label' => esc_html__('Padding', 'uicore-elements'),
210 - 'type' => Controls_Manager::DIMENSIONS,
211 - 'size_units' => [ 'px', 'em', '%' ],
212 - 'selectors' => [
213 - '{{WRAPPER}} .ui-e-'.$position.' .ui-e-meta-item' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};'
214 - ]
215 - ]
216 - );
217 - if($position === 'top'){
295 + \Elementor\Group_Control_Box_Shadow::get_type(),
296 + [
297 + 'name' => $position . '_meta_shadow',
298 + 'label' => esc_html__('Box Shadow', 'uicore-elements'),
299 + 'selector' => '{{WRAPPER}} .ui-e-' . $position . ' .ui-e-meta-item',
300 + ]
301 + );
302 + $this->add_responsive_control(
303 + $position . '_meta_padding',
304 + [
305 + 'label' => esc_html__('Padding', 'uicore-elements'),
306 + 'type' => Controls_Manager::DIMENSIONS,
307 + 'size_units' => ['px', 'em', '%'],
308 + 'selectors' => [
309 + '{{WRAPPER}} .ui-e-' . $position . ' .ui-e-meta-item' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};'
310 + ]
311 + ]
312 + );
313 + if ($position === 'top') {
218 314 $this->add_responsive_control(
219 - $position.'_meta_margin',
315 + $position . '_meta_margin',
220 316 [
221 317 'label' => esc_html__('Margin', 'uicore-elements'),
222 318 'type' => Controls_Manager::DIMENSIONS,
223 - 'size_units' => [ 'px', 'em', '%' ],
319 + 'size_units' => ['px', 'em', '%'],
224 320 'selectors' => [
225 - '{{WRAPPER}} .ui-e-'.$position => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};'
321 + '{{WRAPPER}} .ui-e-' . $position => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};'
226 322 ]
227 323 ]
228 324 );
229 - }else{
230 - $this->add_control($position.'_meta_margin',[
231 - 'label' => __( 'Meta Top Space', 'uicore-elements' ),
232 - 'type' => Controls_Manager::SLIDER,
233 - 'size_units' => [ 'em' ],
234 - 'separator' => 'after',
235 - 'range' => [
236 - 'px' => [
237 - 'min' => 0,
238 - 'max' => 3,
239 - 'step' => 0.1,
325 + } else {
326 + $this->add_control(
327 + $position . '_meta_margin',
328 + [
329 + 'label' => __('Meta Top Space', 'uicore-elements'),
330 + 'type' => Controls_Manager::SLIDER,
331 + 'size_units' => ['em'],
332 + 'separator' => 'after',
333 + 'range' => [
334 + 'px' => [
335 + 'min' => 0,
336 + 'max' => 3,
337 + 'step' => 0.1,
338 + ],
240 339 ],
241 - ],
242 - 'default' => [
243 - 'unit' => 'em',
244 - 'size' => 1.2,
245 - ],
246 - 'selectors' => [
247 - '{{WRAPPER}} .ui-e-'.$position => 'margin-top: {{SIZE}}em;',
248 - ],
249 - ]
250 - );
340 + 'default' => [
341 + 'unit' => 'em',
342 + 'size' => 1.2,
343 + ],
344 + 'selectors' => [
345 + '{{WRAPPER}} .ui-e-' . $position => 'margin-top: {{SIZE}}em;',
346 + ],
347 + ]
348 + );
251 349 }
252 350
253 - $this->add_responsive_control($position.'_meta_gap',[
254 - 'label' => __( 'Items Gap', 'uicore-elements' ),
351 + $this->add_responsive_control(
352 + $position . '_meta_gap',
353 + [
354 + 'label' => __('Items Gap', 'uicore-elements'),
255 355 'type' => Controls_Manager::SLIDER,
256 - 'size_units' => [ 'px' ],
356 + 'size_units' => ['px'],
257 357 'range' => [
258 358 'px' => [
259 359 'min' => 0,
260 360 'max' => 30,
@@ -264,59 +364,67 @@
264 364 'unit' => 'px',
265 365 'size' => 8,
266 366 ],
267 367 'selectors' => [
268 - '{{WRAPPER}} .ui-e-'.$position.' ' => 'gap: {{SIZE}}px;',
368 + '{{WRAPPER}} .ui-e-' . $position . ' ' => 'gap: {{SIZE}}px;',
269 369 ],
270 370 ]
271 371 );
272 - $this->add_control($position.'_meta_separator',[
273 - 'label' => __( 'Separator', 'uicore-elements' ),
372 + $this->add_control(
373 + $position . '_meta_separator',
374 + [
375 + 'label' => __('Separator', 'uicore-elements'),
274 376 'type' => Controls_Manager::TEXT,
275 377 ]
276 378 );
277 - if($position === 'top'){
278 - $this->add_control($position.'_meta_placement',[
279 - 'label' => __( 'Items placement', 'uicore-elements' ),
280 - 'type' => Controls_Manager::SELECT,
281 - 'default' => '',
282 - 'options' => [
283 - 'start left' => __( 'Top Left', 'uicore-elements' ),
284 - 'start right' => __( 'Top Right', 'uicore-elements' ),
285 - 'end left' => __( 'Bottom Left', 'uicore-elements' ),
286 - 'end right' => __( 'Bottom Right', 'uicore-elements' ),
287 - ],
288 - 'selectors' => [
289 - '{{WRAPPER}} .ui-e-post-top-meta' => 'place-content: {{VALUE}};',
290 - ],
379 + if ($position === 'top') {
380 + $this->add_control(
381 + $position . '_meta_placement',
382 + [
383 + 'label' => __('Items placement', 'uicore-elements'),
384 + 'type' => Controls_Manager::SELECT,
385 + 'default' => '',
386 + 'options' => [
387 + 'start left' => __('Top Left', 'uicore-elements'),
388 + 'start right' => __('Top Right', 'uicore-elements'),
389 + 'end left' => __('Bottom Left', 'uicore-elements'),
390 + 'end right' => __('Bottom Right', 'uicore-elements'),
391 + ],
392 + 'selectors' => [
393 + '{{WRAPPER}} .ui-e-post-top-meta' => 'place-content: {{VALUE}};',
394 + ],
291 395 ]
292 396 );
293 397 }
294 398 }
295 399
296 - function get_meta_the_author($mode){
400 + function get_meta_the_author($mode)
401 + {
297 402 global $post;
298 403 $author_id = $post->post_author;
404 + $author_name = get_the_author_meta('display_name', $author_id);
299 405
300 406 // name, full, avatar
301 - if($mode === 'avatar'){
302 - $display = '<img class="ui-e-meta-avatar" src="' . esc_url( get_avatar_url($author_id, array('size' => 100)) ) . '" />';
303 - }elseif($mode === 'full'){
304 - $display = '<img class="ui-e-meta-avatar" src="' . esc_url( get_avatar_url($author_id, array('size' => 100)) ) . '" /> '.get_the_author_meta('display_name', $author_id);
305 - }else{
306 - $display = esc_html(\get_the_author_meta('display_name', $author_id));
407 + if ($mode === 'avatar') {
408 + $display = '<img class="ui-e-meta-avatar" src="' . esc_url(get_avatar_url($author_id, array('size' => 100))) . '" />';
409 + } elseif ($mode === 'full') {
410 + $display = '<img class="ui-e-meta-avatar" src="' . esc_url(get_avatar_url($author_id, array('size' => 100))) . '" /> ' . esc_html($author_name);
411 + } else {
412 + $display = esc_html($author_name);
307 413 }
308 414 $link = sprintf(
415 + /* translators: 1: Author archive url, 2: Author meta title, 3: Author display name */
309 416 '<a href="%1$s" title="%2$s" rel="author">%3$s</a>',
310 - esc_url( get_author_posts_url( $author_id) ),
417 + esc_url(get_author_posts_url($author_id)),
311 418 /* translators: %s: Author's display name. */
312 - esc_attr( sprintf( __( 'View %s&#8217;s posts', 'uicore-elements' ), esc_html($display) ) ),
419 + esc_attr(sprintf(__('View %s&#8217;s posts', 'uicore-elements'), esc_html($author_name))),
313 420 $display
314 421 );
315 422 return $link;
316 423 }
317 424
318 - function get_woo_meta($type) {
425 + function get_woo_meta($type, $meta = null)
426 + {
319 427 global $product;
320 428
321 429 if (empty($product)) {
322 430 return 'No product found.';
@@ -325,58 +433,246 @@
325 433 switch ($type) {
326 434 case 'product price':
327 435 return $product->get_price_html();
328 436 case 'product rating':
329 - return $product->get_rating_html();
437 + return wc_get_rating_html($product->get_average_rating());
330 438 case 'product category':
331 439 return get_the_term_list($product->get_id(), 'product_cat', '', ', ', '');
332 440 case 'product tag':
333 441 return get_the_term_list($product->get_id(), 'product_tag', '', ', ', '');
442 + case 'product stock':
443 + return $this->get_product_stock_for_widget_meta($product, $meta);
334 444 case 'product attribute':
335 - return wc_display_product_attributes($product);
445 + return $this->print_product_attributes($product);
446 + case 'product sale':
447 + return $product->is_on_sale() ? esc_html__('Sale!', 'uicore-elements') : '';
336 448 default:
337 449 return 'Invalid meta type.';
338 450 }
339 451 }
340 452
453 + /**
454 + * Get the product stock quantity for Advanced Product widget meta. If the product is variable, return the highest stock quantity among its variations.
455 + * TODO: this function should be part of the product TRAIT, but product trait uses this meta trait, so we'd have a circular dependency.
456 + *
457 + * @param WC_Product $product The WooCommerce product object.
458 + * @param array $meta The meta settings array.
459 + */
460 + function get_product_stock_for_widget_meta($product, $meta)
461 + {
341 462
342 - function display_meta($meta){
463 + // In/out of stock return
464 + if (isset($meta['stock_type']) && $meta['stock_type'] === 'in') {
343 465
344 - if($meta['type'] === 'none')
466 + $in_stock = false;
467 +
468 + if ($product->is_type('variable')) {
469 + foreach ($product->get_children() as $child_id) {
470 + $variation = wc_get_product($child_id);
471 + if ($variation && $variation->is_in_stock()) {
472 + $in_stock = true;
473 + break;
474 + }
475 + }
476 + } else {
477 + $in_stock = $product->is_in_stock();
478 + }
479 +
480 + return $in_stock
481 + ? '<span class="in-stock">' . esc_html($meta['in_stock_text'] ?? __('In stock', 'uicore-elements')) . '</span>'
482 + : '<span class="out-of-stock">' . esc_html($meta['out_of_stock_text'] ?? __('Out of stock', 'uicore-elements')) . '</span>';
483 + }
484 +
485 + // Quantity return
486 + if ($product->is_type('variable')) {
487 +
488 + $qty = 0;
489 +
490 + // Find the maximum stock quantity among variations
491 + foreach ($product->get_available_variations() as $variation) {
492 + $obj = wc_get_product($variation['variation_id']);
493 +
494 + if ($obj->managing_stock()) {
495 + $stock = $obj->get_stock_quantity();
496 + if ($stock > $qty) {
497 + $qty = $stock;
498 + }
499 + }
500 + }
501 +
502 + return $qty > 0
503 + ? '<span class="in-stock">' . esc_html($qty) . '</span>'
504 + : '<span class="out-of-stock">' . esc_html($meta['out_of_stock_text'] ?? __('Out of stock', 'uicore-elements')) . '</span>';
505 + }
506 +
507 + if (!$product->managing_stock()) {
508 + return '<span class="in-stock">' . esc_html($meta['in_stock_text'] ?? __('In stock', 'uicore-elements')) . '</span>';
509 + }
510 +
511 + return $product->get_stock_quantity() !== null
512 + ? $product->get_stock_quantity()
513 + : '';
514 + }
515 +
516 + /**
517 + * Gets and prints a list of product attributes. We use a custom function for it because
518 + * `wc_display_product_attributes` uses a table layout wich is not suitable for our widget.
519 + *
520 + * @param WC_Product $product The WooCommerce product object.
521 + * @return string|false The HTML string of product attributes or false if none are visible.
522 + * @since 1.3.13
523 + */
524 + function print_product_attributes($product)
525 + {
526 +
527 + $attributes = $product->get_attributes();
528 +
529 + if (empty($attributes)) {
530 + return false;
531 + }
532 +
533 + $default = true; // Flag to check if we're actually displaying any attribute
534 + $content = '<ul class="ui-e-product-attributes">';
535 +
536 + foreach ($attributes as $attribute) {
537 + if (! $attribute->get_visible()) {
538 + continue;
539 + }
540 +
541 + // We only want to display default attributes here that are not using framework swatches
542 + $swatch = $this->get_swatch_type($attribute->get_name());
543 + if ($swatch && in_array($swatch, ['color', 'label', 'button', 'image'])) {
544 + continue;
545 + }
546 +
547 + $default = false; // Safe to output html
548 + $name = wc_attribute_label($attribute->get_name());
549 + $value = '';
550 +
551 + if ($attribute->is_taxonomy()) {
552 + $taxonomy = $attribute->get_name();
553 + $terms = wc_get_product_terms(
554 + $product->get_id(),
555 + $taxonomy,
556 + ['fields' => 'all']
557 + );
558 +
559 + $term_links = [];
560 + $taxonomy_obj = get_taxonomy($taxonomy);
561 + $has_archive = $taxonomy_obj && $taxonomy_obj->public && $taxonomy_obj->rewrite && !empty($taxonomy_obj->rewrite['slug']);
562 +
563 + foreach ($terms as $term) {
564 + if ($has_archive && get_term_link($term)) {
565 + $term_links[] = '<a href="' . esc_url(get_term_link($term)) . '">' . esc_html($term->name) . '</a>';
566 + } else {
567 + $term_links[] = esc_html($term->name);
568 + }
569 + }
570 + $value = implode(', ', $term_links);
571 + } else {
572 + $value = implode(', ', $attribute->get_options());
573 + }
574 +
575 + if (empty($value)) {
576 + continue;
577 + }
578 +
579 + $content .= '<li>';
580 + $content .= '<strong>' . esc_html($name) . ':</strong> ';
581 + $content .= $value;
582 + $content .= '</li>';
583 + }
584 +
585 + if ($default) {
586 + return false;
587 + }
588 +
589 + $content .= '</ul>';
590 + return $content;
591 + }
592 +
593 + /**
594 + * Get the attribute swatch type from Uicore Framework plugin.
595 + *
596 + * @return string The swatch type.
597 + * @since 1.3.15
598 + */
599 + function get_swatch_type($attribute_slug)
600 + {
601 + if (!class_exists('Uicore\WooCommerce\Swatches') && !function_exists('get_attribute_swatch_type')) {
602 + return false;
603 + }
604 +
605 + return \Uicore\WooCommerce\Swatches::get_attribute_swatch_type($attribute_slug);
606 + }
607 +
608 + function display_meta($meta, $product_id = null)
609 + {
610 +
611 + if ($meta['type'] === 'none')
345 612 return;
346 613
347 - \Elementor\Icons_Manager::render_icon( $meta['icon'], [ 'aria-hidden' => 'true', 'class'=>'ui-e-meta-icon' ],'span' );
614 + $content = '';
615 + $wrapper = '<div class="ui-e-meta-item">';
616 + $type = $meta['type'];
617 + $prefix = $meta['before'] ? '<span>' . esc_html($meta['before']) . '</span>' : '';
618 + $suffix = $meta['after'] ? '<span class="ui-e-meta-after">' . esc_html($meta['after']) . '</span>' : '';
348 619
349 - if($meta['before'])
350 - echo '<span>'.esc_html($meta['before']).'</span>';
620 + ob_start();
621 + \Elementor\Icons_Manager::render_icon($meta['icon'], ['aria-hidden' => 'true', 'class' => 'ui-e-meta-icon'], 'span');
622 + $icon = ob_get_clean();
351 623
352 - $type = $meta['type'];
353 - if($type === 'author'){
354 - echo $this->get_meta_the_author($meta['autor_display']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
355 - }elseif($type === 'date'){
356 - echo Helper::format_date(get_the_date('U'), $meta['date_format'], $meta['custom_format']); // 'U' param returns the date in timestamp, necessary for format_date()
357 - }elseif($type === 'updated date'){
358 - echo Helper::format_date(get_the_modified_date('U'), $meta['date_format'], $meta['custom_format']); // 'U' param returns the date in timestamp, necessary for format_date()
359 - }elseif($type === 'category'){
360 - echo Helper::get_taxonomy('category');
361 - }elseif($type === 'portfolio category'){
362 - echo Helper::get_taxonomy('portfolio_category');
363 - }elseif($type === 'comment'){
364 - echo esc_html(get_comments_number());
365 - }elseif($type === 'custom meta'){
366 - echo esc_html(get_post_meta( get_the_ID(), $meta['type_custom'], true ));
367 - }elseif($type === 'custom taxonomy'){
368 - echo Helper::get_taxonomy($meta['type_custom']);
369 - }elseif($type === 'custom html'){
370 - echo wp_kses_post($meta['html_custom']);
371 - }elseif($type === 'reading time'){
372 - echo esc_html(Helper::get_reading_time());
373 - }else if(strpos($type, 'product') === 0){
374 - echo wp_kses_post($this->get_woo_meta($type));
375 - }else{
376 - echo \esc_html($type);
624 + // Build content
625 + switch ($type) {
626 + case 'author':
627 + $content .= $this->get_meta_the_author($meta['autor_display']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
628 + break;
629 + case 'date':
630 + $content .= Helper::format_date(get_the_date('U'), $meta['date_format'], $meta['custom_format']); // 'U' param returns the date in timestamp, necessary for format_date()
631 + break;
632 + case 'updated date':
633 + $content .= Helper::format_date(get_the_modified_date('U'), $meta['date_format'], $meta['custom_format']); // 'U' param returns the date in timestamp, necessary for format_date()
634 + break;
635 + case 'category':
636 + $content .= Helper::get_taxonomy('category');
637 + break;
638 + case 'tag':
639 + $content .= Helper::get_taxonomy('post_tag');
640 + break;
641 + case 'portfolio category':
642 + $content .= Helper::get_taxonomy('portfolio_category');
643 + break;
644 + case 'comment':
645 + $content .= esc_html(get_comments_number());
646 + break;
647 + case 'custom meta':
648 + $content .= Helper::get_custom_meta($meta['type_custom'], $product_id);
649 + break;
650 + case 'custom taxonomy':
651 + $content .= Helper::get_taxonomy($meta['type_custom']);
652 + break;
653 + case 'custom html':
654 + $content .= wp_kses_post($meta['html_custom']);
655 + break;
656 + case 'reading time':
657 + $content .= esc_html(Helper::get_reading_time());
658 + break;
659 + case 'excerpt':
660 + $content .= esc_html(get_the_excerpt());
661 + break;
662 + default:
663 + if (strpos($type, 'product') === 0) {
664 + $data = $this->get_woo_meta($type, $meta);
665 + if ($data === false) return;
666 + $content .= wp_kses_post($data);
667 + } else {
668 + echo \esc_html($type);
669 + }
670 + break;
377 671 }
378 672
379 - if($meta['after'])
380 - echo '<span class="ui-e-meta-after">'.esc_html($meta['after']).'</span>';
673 + // Only output if there's content
674 + if (!empty($content)) {
675 + echo $wrapper . Helper::esc_svg($icon) . $prefix . $content . $suffix . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
676 + }
381 677 }
382 -}
678 +}