PluginProbe
UiCore Elements – Free widgets and templates for Elementor / 1.3.18
UiCore Elements – Free widgets and templates for Elementor v1.3.18
1.3.18 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 All 42 releases
← All changes | includes/utils/meta-component.php +678 -372 1.0.0 → 1.3.18 View file →
@@ -1,372 +1,678 @@
1 -<?php
2 -namespace UiCoreElements\Utils;
3 -
4 -use Elementor\Controls_Manager;
5 -use Elementor\Group_Control_Typography;
6 -use UiCoreElements\Helper;
7 -
8 -if ( ! defined( 'ABSPATH' ) ) {
9 - exit; // Exit if accessed directly
10 -}
11 -
12 -trait Meta_Trait {
13 -
14 - function get_meta_content_controls()
15 - {
16 -
17 - $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 - ];
28 -
29 - //add woocomerce meta to options if woocommerce is instaled
30 - $options['product price'] = __( 'Product Price', 'uicore-elements' );
31 - $options['product rating'] = __( 'Product Rating', 'uicore-elements' );
32 - // $options['product stock'] = __( 'Product Stock', 'uicore-elements' );
33 - $options['product category'] = __( 'Product Category', 'uicore-elements' );
34 - $options['product tag'] = __( 'Product Tag', 'uicore-elements' );
35 - $options['product attribute'] = __( 'Product Attribute', 'uicore-elements' );
36 -
37 - $options['portfolio category'] = __( 'Portfolio Category', 'uicore-elements' );
38 -
39 - $repeater = new \Elementor\Repeater();
40 - $repeater->add_control('type',[
41 - 'label' => __( 'Meta', 'uicore-elements' ),
42 - 'type' => Controls_Manager::SELECT,
43 - 'default' => '',
44 - 'options' => $options
45 - ]
46 - );
47 - $repeater->add_control('type_custom',[
48 - 'label' => __( 'Custom Field Name', 'uicore-elements' ),
49 - 'type' => Controls_Manager::TEXT,
50 - 'condition' => [
51 - 'type' => ['custom meta','custom taxonomy']
52 - ]
53 - ]
54 - );
55 - $repeater->add_control('date_format',[
56 - 'label' => esc_html__( 'Date Format', 'uicore-elements' ),
57 - 'type' => Controls_Manager::SELECT,
58 - 'default' => 'default',
59 - 'options' => [
60 - 'default' => esc_html__( 'Default', 'uicore-elements' ),
61 - '' => esc_html__( 'None', 'uicore-elements' ),
62 - 'F j, Y' => gmdate( 'F j, Y' ),
63 - 'Y-m-d' => gmdate( 'Y-m-d' ),
64 - 'm/d/Y' => gmdate( 'm/d/Y' ),
65 - 'd/m/Y' => gmdate( 'd/m/Y' ),
66 - 'custom' => esc_html__( 'Custom', 'uicore-elements' ),
67 - ],
68 - 'condition' => [
69 - 'type' => ['date','updated date']
70 - ]
71 - ]
72 - );
73 - $repeater->add_control('custom_format',[
74 - 'label' => esc_html__( 'Custom Format', 'uicore-elements' ),
75 - 'default' => get_option( 'date_format' ) . ' ' . get_option( 'time_format' ),
76 - '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' ) ),
77 - 'condition' => [
78 - 'date_format' => 'custom',
79 - 'type' => ['date','updated date']
80 - ],
81 - ]
82 - );
83 - $repeater->add_control('before',[
84 - 'label' => __( 'Text Before', 'uicore-elements' ),
85 - 'type' => Controls_Manager::TEXT,
86 - 'condition' => [
87 - 'type!' => 'none',
88 - ]
89 - ]
90 - );
91 - $repeater->add_control('after',[
92 - 'label' => __( 'Text After', 'uicore-elements' ),
93 - 'type' => Controls_Manager::TEXT,
94 - 'condition' => [
95 - 'type!' => 'none',
96 - ]
97 - ]
98 - );
99 - $repeater->add_control('autor_display',[
100 - 'label' => __( 'Display Type', 'uicore-elements' ),
101 - 'type' => Controls_Manager::SELECT,
102 - 'default' => 'name',
103 - 'options' => [
104 - 'name' => __( 'Name', 'uicore-elements' ),
105 - 'full' => __( 'Avatar & Name', 'uicore-elements' ),
106 - 'avatar' => __( 'Avatar', 'uicore-elements' ),
107 - ],
108 - 'condition' => [
109 - 'type' => 'author',
110 - ],
111 - ]
112 - );
113 - $repeater->add_control('icon',[
114 - 'label' => __( 'Icon', 'uicore-elements' ),
115 - 'type' => Controls_Manager::ICONS,
116 - 'condition' => [
117 - 'autor_display' => 'name',
118 - 'type!' => 'none',
119 -
120 - ],
121 - ]
122 - );
123 - return $repeater->get_controls();
124 - }
125 -
126 - function get_meta_style_controls($position = 'tb-meta')
127 - {
128 - $this->add_group_control(
129 - Group_Control_Typography::get_type(),
130 - [
131 - 'name' => $position.'_meta_typography',
132 - 'selector' => '{{WRAPPER}} .ui-e-'.$position,
133 - 'separator' => 'before',
134 - ]
135 - );
136 - $this->add_control(
137 - $position.'_meta_color',
138 - [
139 - 'label' => __( 'Text Color', 'uicore-elements' ),
140 - 'type' => Controls_Manager::COLOR,
141 - 'selectors' => [
142 - '{{WRAPPER}} .ui-e-'.$position => 'color: {{VALUE}}',
143 -
144 - ],
145 - ]
146 - );
147 - $this->add_control(
148 - $position.'_link_color',
149 - [
150 - 'label' => __( 'Link Color', 'uicore-elements' ),
151 - 'type' => Controls_Manager::COLOR,
152 - 'selectors' => [
153 - '{{WRAPPER}} .ui-e-'.$position.' .ui-e-meta-item a' => 'color: {{VALUE}}',
154 -
155 - ],
156 - ]
157 - );
158 - $this->add_control(
159 - $position.'_linkh_color',
160 - [
161 - 'label' => __( 'Link Hover Color', 'uicore-elements' ),
162 - 'type' => Controls_Manager::COLOR,
163 - 'selectors' => [
164 - '{{WRAPPER}} .ui-e-'.$position.' .ui-e-meta-item a:hover' => 'color: {{VALUE}}',
165 -
166 - ],
167 - ]
168 - );
169 - $this->add_control(
170 - $position.'_meta_background',
171 - [
172 - 'label' => __( 'Background Color', 'uicore-elements' ),
173 - 'type' => Controls_Manager::COLOR,
174 - 'selectors' => [
175 - '{{WRAPPER}} .ui-e-'.$position.' .ui-e-meta-item' => 'background-color: {{VALUE}}',
176 -
177 - ],
178 - ]
179 - );
180 - $this->add_control(
181 - $position.'_meta_radius',
182 - [
183 - 'label' => esc_html__('Border Radius', 'uicore-elements'),
184 - 'type' => Controls_Manager::DIMENSIONS,
185 - 'selectors' => [
186 - '{{WRAPPER}} .ui-e-'.$position.' .ui-e-meta-item' => 'border-radius: {{TOP}}px {{RIGHT}}px {{BOTTOM}}px {{LEFT}}px;'
187 - ],
188 - ]
189 - );
190 - $this->add_group_control(
191 - \Elementor\Group_Control_Box_Shadow::get_type(),
192 - [
193 - 'name' => $position.'_meta_shadow',
194 - 'label' => esc_html__( 'Box Shadow', 'uicore-elements' ),
195 - 'selector' => '{{WRAPPER}} .ui-e-'.$position.' .ui-e-meta-item',
196 - ]
197 - );
198 - $this->add_responsive_control(
199 - $position.'_meta_padding',
200 - [
201 - 'label' => esc_html__('Padding', 'uicore-elements'),
202 - 'type' => Controls_Manager::DIMENSIONS,
203 - 'size_units' => [ 'px', 'em', '%' ],
204 - 'selectors' => [
205 - '{{WRAPPER}} .ui-e-'.$position.' .ui-e-meta-item' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};'
206 - ]
207 - ]
208 - );
209 - if($position === 'top'){
210 - $this->add_responsive_control(
211 - $position.'_meta_margin',
212 - [
213 - 'label' => esc_html__('Margin', 'uicore-elements'),
214 - 'type' => Controls_Manager::DIMENSIONS,
215 - 'size_units' => [ 'px', 'em', '%' ],
216 - 'selectors' => [
217 - '{{WRAPPER}} .ui-e-'.$position => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};'
218 - ]
219 - ]
220 - );
221 - }else{
222 - $this->add_control($position.'_meta_margin',[
223 - 'label' => __( 'Meta Top Space', 'uicore-elements' ),
224 - 'type' => Controls_Manager::SLIDER,
225 - 'size_units' => [ 'em' ],
226 - 'separator' => 'after',
227 - 'range' => [
228 - 'px' => [
229 - 'min' => 0,
230 - 'max' => 3,
231 - 'step' => 0.1,
232 - ],
233 - ],
234 - 'default' => [
235 - 'unit' => 'em',
236 - 'size' => 1.2,
237 - ],
238 - 'selectors' => [
239 - '{{WRAPPER}} .ui-e-'.$position => 'margin-top: {{SIZE}}em;',
240 - ],
241 - ]
242 - );
243 - }
244 -
245 - $this->add_responsive_control($position.'_meta_gap',[
246 - 'label' => __( 'Items Gap', 'uicore-elements' ),
247 - 'type' => Controls_Manager::SLIDER,
248 - 'size_units' => [ 'px' ],
249 - 'range' => [
250 - 'px' => [
251 - 'min' => 0,
252 - 'max' => 30,
253 - ],
254 - ],
255 - 'default' => [
256 - 'unit' => 'px',
257 - 'size' => 8,
258 - ],
259 - 'selectors' => [
260 - '{{WRAPPER}} .ui-e-'.$position.' ' => 'gap: {{SIZE}}px;',
261 - ],
262 - ]
263 - );
264 - $this->add_control($position.'_meta_separator',[
265 - 'label' => __( 'Separator', 'uicore-elements' ),
266 - 'type' => Controls_Manager::TEXT,
267 - ]
268 - );
269 - if($position === 'top'){
270 - $this->add_control($position.'_meta_placement',[
271 - 'label' => __( 'Items placement', 'uicore-elements' ),
272 - 'type' => Controls_Manager::SELECT,
273 - 'default' => '',
274 - 'options' => [
275 - 'start left' => __( 'Top Left', 'uicore-elements' ),
276 - 'start right' => __( 'Top Right', 'uicore-elements' ),
277 - 'end left' => __( 'Bottom Left', 'uicore-elements' ),
278 - 'end right' => __( 'Bottom Right', 'uicore-elements' ),
279 - ],
280 - 'selectors' => [
281 - '{{WRAPPER}} .ui-e-post-top-meta' => 'place-content: {{VALUE}};',
282 - ],
283 - ]
284 - );
285 - }
286 - }
287 -
288 - function get_meta_the_author($mode){
289 - global $post;
290 - $author_id = $post->post_author;
291 -
292 - // name, full, avatar
293 - if($mode === 'avatar'){
294 - $display = '<img class="ui-e-meta-avatar" src="' . esc_url( get_avatar_url($author_id, array('size' => 100)) ) . '" />';
295 - }elseif($mode === 'full'){
296 - $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);
297 - }else{
298 - $display = esc_html(\get_the_author_meta('display_name', $author_id));
299 - }
300 - $link = sprintf(
301 - '<a href="%1$s" title="%2$s" rel="author">%3$s</a>',
302 - esc_url( get_author_posts_url( $author_id) ),
303 - /* translators: %s: Author's display name. */
304 - esc_attr( sprintf( __( 'View %s&#8217;s posts', 'uicore-elements' ), esc_html($display) ) ),
305 - $display
306 - );
307 - return $link;
308 - }
309 -
310 - function get_woo_meta($type) {
311 - global $product;
312 -
313 - if (empty($product)) {
314 - return 'No product found.';
315 - }
316 -
317 - switch ($type) {
318 - case 'product price':
319 - return $product->get_price_html();
320 - case 'product rating':
321 - return $product->get_rating_html();
322 - case 'product category':
323 - return get_the_term_list($product->get_id(), 'product_cat', '', ', ', '');
324 - case 'product tag':
325 - return get_the_term_list($product->get_id(), 'product_tag', '', ', ', '');
326 - case 'product attribute':
327 - return wc_display_product_attributes($product);
328 - default:
329 - return 'Invalid meta type.';
330 - }
331 - }
332 -
333 -
334 - function display_meta($meta){
335 -
336 - if($meta['type'] === 'none')
337 - return;
338 -
339 - \Elementor\Icons_Manager::render_icon( $meta['icon'], [ 'aria-hidden' => 'true', 'class'=>'ui-e-meta-icon' ],'span' );
340 -
341 - if($meta['before'])
342 - echo '<span>'.esc_html($meta['before']).'</span>';
343 -
344 - $type = $meta['type'];
345 - if($type === 'author'){
346 - echo $this->get_meta_the_author($meta['autor_display']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
347 - }elseif($type === 'date'){
348 - echo Helper::format_date(get_the_date(), $meta['date_format'], $meta['custom_format']);
349 - }elseif($type === 'updated date'){
350 - echo Helper::format_date(get_the_modified_date(), $meta['date_format'], $meta['custom_format']);
351 - }elseif($type === 'category'){
352 - echo Helper::get_taxonomy('category');
353 - }elseif($type === 'portfolio category'){
354 - echo Helper::get_taxonomy('portfolio_category');
355 - }elseif($type === 'comment'){
356 - echo esc_html(get_comments_number());
357 - }elseif($type === 'custom meta'){
358 - echo esc_html(get_post_meta( get_the_ID(), $meta['type_custom'], true ));
359 - }elseif($type === 'custom taxonomy'){
360 - echo Helper::get_taxonomy($meta['type_custom']);
361 - }elseif($type === 'reading time'){
362 - echo esc_html(Helper::get_reading_time());
363 - }else if(strpos($type, 'product') === 0){
364 - echo wp_kses_post($this->get_woo_meta($type));
365 - }else{
366 - echo \esc_html($type);
367 - }
368 -
369 - if($meta['after'])
370 - echo '<span class="ui-e-meta-after">'.esc_html($meta['after']).'</span>';
371 - }
372 -}
1 +<?php
2 +
3 +namespace UiCoreElements\Utils;
4 +
5 +use Elementor\Controls_Manager;
6 +use Elementor\Group_Control_Typography;
7 +use UiCoreElements\Helper;
8 +
9 +if (! defined('ABSPATH')) {
10 + exit; // Exit if accessed directly
11 +}
12 +
13 +trait Meta_Trait
14 +{
15 +
16 + function get_meta_content_controls($product_metas = false)
17 + {
18 +
19 + // Meta options
20 + $options = [
21 + 'none' => __('None', 'uicore-elements'),
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 + ];
50 +
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
54 +
55 +
56 + $repeater = new \Elementor\Repeater();
57 + $repeater->add_control(
58 + 'type',
59 + [
60 + 'label' => __('Meta', 'uicore-elements'),
61 + 'type' => Controls_Manager::SELECT,
62 + 'default' => '',
63 + 'options' => $options
64 + ]
65 + );
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']
73 + ]
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']
83 + ]
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']
102 + ]
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']
129 + ]
130 + ]
131 + );
132 + $repeater->add_control(
133 + 'in_stock_text',
134 + [
135 + 'label' => esc_html__('In Stock Text', 'uicore-elements'),
136 + 'type' => Controls_Manager::TEXT,
137 + 'default' => esc_html__('In Stock', 'uicore-elements'),
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' => [
187 + 'type!' => 'none',
188 + ]
189 + ]
190 + );
191 + $repeater->add_control(
192 + 'after',
193 + [
194 + 'label' => __('Text After', 'uicore-elements'),
195 + 'type' => Controls_Manager::TEXT,
196 + 'condition' => [
197 + 'type!' => 'none',
198 + ]
199 + ]
200 + );
201 + $repeater->add_control(
202 + 'autor_display',
203 + [
204 + 'label' => __('Display Type', 'uicore-elements'),
205 + 'type' => Controls_Manager::SELECT,
206 + 'default' => 'name',
207 + 'options' => [
208 + 'name' => __('Name', 'uicore-elements'),
209 + 'full' => __('Avatar & Name', 'uicore-elements'),
210 + 'avatar' => __('Avatar', 'uicore-elements'),
211 + ],
212 + 'condition' => [
213 + 'type' => 'author',
214 + ],
215 + ]
216 + );
217 + $repeater->add_control(
218 + 'icon',
219 + [
220 + 'label' => __('Icon', 'uicore-elements'),
221 + 'type' => Controls_Manager::ICONS,
222 + 'condition' => [
223 + 'autor_display' => 'name',
224 + 'type!' => 'none',
225 + ],
226 + ]
227 + );
228 + return $repeater->get_controls();
229 + }
230 +
231 + function get_meta_style_controls($position = 'tb-meta')
232 + {
233 + $this->add_group_control(
234 + Group_Control_Typography::get_type(),
235 + [
236 + 'name' => $position . '_meta_typography',
237 + 'selector' => '{{WRAPPER}} .ui-e-' . $position,
238 + 'separator' => 'before',
239 + ]
240 + );
241 + $this->add_control(
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 + );
252 + $this->add_control(
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 + );
263 + $this->add_control(
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 + );
273 + $this->add_control(
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}}',
280 +
281 + ],
282 + ]
283 + );
284 + $this->add_control(
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 + );
294 + $this->add_group_control(
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') {
314 + $this->add_responsive_control(
315 + $position . '_meta_margin',
316 + [
317 + 'label' => esc_html__('Margin', 'uicore-elements'),
318 + 'type' => Controls_Manager::DIMENSIONS,
319 + 'size_units' => ['px', 'em', '%'],
320 + 'selectors' => [
321 + '{{WRAPPER}} .ui-e-' . $position => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};'
322 + ]
323 + ]
324 + );
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 + ],
339 + ],
340 + 'default' => [
341 + 'unit' => 'em',
342 + 'size' => 1.2,
343 + ],
344 + 'selectors' => [
345 + '{{WRAPPER}} .ui-e-' . $position => 'margin-top: {{SIZE}}em;',
346 + ],
347 + ]
348 + );
349 + }
350 +
351 + $this->add_responsive_control(
352 + $position . '_meta_gap',
353 + [
354 + 'label' => __('Items Gap', 'uicore-elements'),
355 + 'type' => Controls_Manager::SLIDER,
356 + 'size_units' => ['px'],
357 + 'range' => [
358 + 'px' => [
359 + 'min' => 0,
360 + 'max' => 30,
361 + ],
362 + ],
363 + 'default' => [
364 + 'unit' => 'px',
365 + 'size' => 8,
366 + ],
367 + 'selectors' => [
368 + '{{WRAPPER}} .ui-e-' . $position . ' ' => 'gap: {{SIZE}}px;',
369 + ],
370 + ]
371 + );
372 + $this->add_control(
373 + $position . '_meta_separator',
374 + [
375 + 'label' => __('Separator', 'uicore-elements'),
376 + 'type' => Controls_Manager::TEXT,
377 + ]
378 + );
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 + ],
395 + ]
396 + );
397 + }
398 + }
399 +
400 + function get_meta_the_author($mode)
401 + {
402 + global $post;
403 + $author_id = $post->post_author;
404 + $author_name = get_the_author_meta('display_name', $author_id);
405 +
406 + // name, full, avatar
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);
413 + }
414 + $link = sprintf(
415 + /* translators: 1: Author archive url, 2: Author meta title, 3: Author display name */
416 + '<a href="%1$s" title="%2$s" rel="author">%3$s</a>',
417 + esc_url(get_author_posts_url($author_id)),
418 + /* translators: %s: Author's display name. */
419 + esc_attr(sprintf(__('View %s&#8217;s posts', 'uicore-elements'), esc_html($author_name))),
420 + $display
421 + );
422 + return $link;
423 + }
424 +
425 + function get_woo_meta($type, $meta = null)
426 + {
427 + global $product;
428 +
429 + if (empty($product)) {
430 + return 'No product found.';
431 + }
432 +
433 + switch ($type) {
434 + case 'product price':
435 + return $product->get_price_html();
436 + case 'product rating':
437 + return wc_get_rating_html($product->get_average_rating());
438 + case 'product category':
439 + return get_the_term_list($product->get_id(), 'product_cat', '', ', ', '');
440 + case 'product tag':
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);
444 + case 'product attribute':
445 + return $this->print_product_attributes($product);
446 + case 'product sale':
447 + return $product->is_on_sale() ? esc_html__('Sale!', 'uicore-elements') : '';
448 + default:
449 + return 'Invalid meta type.';
450 + }
451 + }
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 + {
462 +
463 + // In/out of stock return
464 + if (isset($meta['stock_type']) && $meta['stock_type'] === 'in') {
465 +
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')
612 + return;
613 +
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>' : '';
619 +
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();
623 +
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;
671 + }
672 +
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 + }
677 + }
678 +}