PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.78
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.78
51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / widgets / Review_Schema / Review_Schema.php

Review_Schema.php in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.78, at includes/widgets/Review_Schema/Review_Schema.php

477 lines 13.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Review Schema Widget (Free).
4 *
5 * @package King_Addons
6 */
7
8 namespace King_Addons;
9
10 use Elementor\Controls_Manager;
11 use Elementor\Group_Control_Border;
12 use Elementor\Group_Control_Box_Shadow;
13 use Elementor\Group_Control_Typography;
14 use Elementor\Widget_Base;
15
16 if (!defined('ABSPATH')) {
17 exit;
18 }
19
20 /**
21 * Renders a review/rating block with JSON-LD schema.
22 */
23 class Review_Schema extends Widget_Base
24 {
25 /**
26 * Widget slug.
27 *
28 * @return string
29 */
30 public function get_name(): string
31 {
32 return 'king-addons-review-schema';
33 }
34
35 /**
36 * Widget title.
37 *
38 * @return string
39 */
40 public function get_title(): string
41 {
42 return esc_html__('Review Schema', 'king-addons');
43 }
44
45 /**
46 * Widget icon.
47 *
48 * @return string
49 */
50 public function get_icon(): string
51 {
52 return 'king-addons-icon king-addons-review-schema';
53 }
54
55 /**
56 * Style dependencies.
57 *
58 * @return array<int, string>
59 */
60 public function get_style_depends(): array
61 {
62 return [
63 KING_ADDONS_ASSETS_UNIQUE_KEY . '-review-schema-style',
64 ];
65 }
66
67 /**
68 * Script dependencies.
69 *
70 * @return array<int, string>
71 */
72 public function get_script_depends(): array
73 {
74 return [
75 KING_ADDONS_ASSETS_UNIQUE_KEY . '-review-schema-script',
76 ];
77 }
78
79 /**
80 * Categories.
81 *
82 * @return array<int, string>
83 */
84 public function get_categories(): array
85 {
86 return ['king-addons'];
87 }
88
89 /**
90 * Keywords.
91 *
92 * @return array<int, string>
93 */
94 public function get_keywords(): array
95 {
96 return ['review', 'rating', 'schema', 'seo'];
97 }
98
99 public function get_custom_help_url()
100 {
101 return 'mailto:bug@kingaddons.com?subject=Bug Report - King Addons&body=Please describe the issue';
102 }
103
104 /**
105 * Register controls.
106 *
107 * @return void
108 */
109 public function register_controls(): void
110 {
111 $this->register_content_controls();
112 $this->register_style_controls();
113 $this->register_pro_notice_controls();
114 }
115
116 /**
117 * Render widget output.
118 *
119 * @return void
120 */
121 public function render(): void
122 {
123 $settings = $this->get_settings_for_display();
124 $rating = (float) ($settings['kng_rating']['size'] ?? 0);
125 if ($rating <= 0) {
126 return;
127 }
128
129 $item_name = $settings['kng_item_name'] ?? '';
130 $author = $settings['kng_author'] ?? '';
131 $title = $settings['kng_title'] ?? '';
132 $body = $settings['kng_body'] ?? '';
133 $date = $settings['kng_date'] ?? '';
134 $stars_html = $this->render_stars($rating);
135
136 $schema = $this->build_schema($settings);
137 ?>
138 <div class="king-addons-review">
139 <div class="king-addons-review__header">
140 <div class="king-addons-review__item"><?php echo esc_html($item_name); ?></div>
141 <div class="king-addons-review__rating" aria-label="<?php echo esc_attr($rating); ?>">
142 <?php echo $stars_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
143 <span class="king-addons-review__rating-value"><?php echo esc_html(number_format($rating, 1)); ?>/5</span>
144 </div>
145 </div>
146 <?php if (!empty($title)) : ?>
147 <div class="king-addons-review__title"><?php echo esc_html($title); ?></div>
148 <?php endif; ?>
149 <?php if (!empty($body)) : ?>
150 <div class="king-addons-review__body"><?php echo wp_kses_post($body); ?></div>
151 <?php endif; ?>
152 <div class="king-addons-review__meta">
153 <?php if (!empty($author)) : ?>
154 <span class="king-addons-review__author"><?php echo esc_html($author); ?></span>
155 <?php endif; ?>
156 <?php if (!empty($date)) : ?>
157 <span class="king-addons-review__date"><?php echo esc_html(date_i18n(get_option('date_format'), strtotime((string) $date))); ?></span>
158 <?php endif; ?>
159 </div>
160 <script type="application/ld+json">
161 <?php echo wp_json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
162 </script>
163 </div>
164 <?php
165 }
166
167 /**
168 * Content controls.
169 *
170 * @return void
171 */
172 protected function register_content_controls(): void
173 {
174 $this->start_controls_section(
175 'kng_content_section',
176 [
177 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Review', 'king-addons'),
178 'tab' => Controls_Manager::TAB_CONTENT,
179 ]
180 );
181
182 $this->add_control(
183 'kng_item_name',
184 [
185 'label' => esc_html__('Item Name', 'king-addons'),
186 'type' => Controls_Manager::TEXT,
187 'default' => esc_html__('Product or Service', 'king-addons'),
188 ]
189 );
190
191 $this->add_control(
192 'kng_author',
193 [
194 'label' => esc_html__('Author', 'king-addons'),
195 'type' => Controls_Manager::TEXT,
196 'default' => esc_html__('John Doe', 'king-addons'),
197 ]
198 );
199
200 $this->add_control(
201 'kng_rating',
202 [
203 'label' => esc_html__('Rating', 'king-addons'),
204 'type' => Controls_Manager::SLIDER,
205 'size_units' => ['custom'],
206 'range' => [
207 'custom' => [
208 'min' => 0,
209 'max' => 5,
210 'step' => 0.1,
211 ],
212 ],
213 'default' => [
214 'size' => 4.5,
215 'unit' => 'custom',
216 ],
217 ]
218 );
219
220 $this->add_control(
221 'kng_title',
222 [
223 'label' => esc_html__('Review Title', 'king-addons'),
224 'type' => Controls_Manager::TEXT,
225 'default' => esc_html__('Great quality and support', 'king-addons'),
226 ]
227 );
228
229 $this->add_control(
230 'kng_body',
231 [
232 'label' => esc_html__('Review Body', 'king-addons'),
233 'type' => Controls_Manager::WYSIWYG,
234 'default' => esc_html__('The product exceeded expectations and the support team was responsive.', 'king-addons'),
235 ]
236 );
237
238 $this->add_control(
239 'kng_date',
240 [
241 'label' => esc_html__('Review Date', 'king-addons'),
242 'type' => Controls_Manager::DATE_TIME,
243 ]
244 );
245
246 $this->end_controls_section();
247 }
248
249 /**
250 * Style controls.
251 *
252 * @return void
253 */
254 protected function register_style_controls(): void
255 {
256 $this->start_controls_section(
257 'kng_style_section',
258 [
259 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Card', 'king-addons'),
260 'tab' => Controls_Manager::TAB_STYLE,
261 ]
262 );
263
264 $this->add_group_control(
265 Group_Control_Typography::get_type(),
266 [
267 'name' => 'kng_item_typo',
268 'selector' => '{{WRAPPER}} .king-addons-review__item',
269 ]
270 );
271
272 $this->add_control(
273 'kng_item_color',
274 [
275 'label' => esc_html__('Item Color', 'king-addons'),
276 'type' => Controls_Manager::COLOR,
277 'selectors' => [
278 '{{WRAPPER}} .king-addons-review__item' => 'color: {{VALUE}};',
279 ],
280 ]
281 );
282
283 $this->add_group_control(
284 Group_Control_Typography::get_type(),
285 [
286 'name' => 'kng_title_typo',
287 'selector' => '{{WRAPPER}} .king-addons-review__title',
288 ]
289 );
290
291 $this->add_control(
292 'kng_title_color',
293 [
294 'label' => esc_html__('Title Color', 'king-addons'),
295 'type' => Controls_Manager::COLOR,
296 'selectors' => [
297 '{{WRAPPER}} .king-addons-review__title' => 'color: {{VALUE}};',
298 ],
299 ]
300 );
301
302 $this->add_group_control(
303 Group_Control_Typography::get_type(),
304 [
305 'name' => 'kng_body_typo',
306 'selector' => '{{WRAPPER}} .king-addons-review__body',
307 ]
308 );
309
310 $this->add_control(
311 'kng_body_color',
312 [
313 'label' => esc_html__('Body Color', 'king-addons'),
314 'type' => Controls_Manager::COLOR,
315 'selectors' => [
316 '{{WRAPPER}} .king-addons-review__body' => 'color: {{VALUE}};',
317 ],
318 ]
319 );
320
321 $this->add_group_control(
322 Group_Control_Typography::get_type(),
323 [
324 'name' => 'kng_meta_typo',
325 'selector' => '{{WRAPPER}} .king-addons-review__meta',
326 ]
327 );
328
329 $this->add_control(
330 'kng_meta_color',
331 [
332 'label' => esc_html__('Meta Color', 'king-addons'),
333 'type' => Controls_Manager::COLOR,
334 'selectors' => [
335 '{{WRAPPER}} .king-addons-review__meta' => 'color: {{VALUE}};',
336 ],
337 ]
338 );
339
340 $this->add_group_control(
341 Group_Control_Border::get_type(),
342 [
343 'name' => 'kng_card_border',
344 'selector' => '{{WRAPPER}} .king-addons-review',
345 ]
346 );
347
348 $this->add_group_control(
349 Group_Control_Box_Shadow::get_type(),
350 [
351 'name' => 'kng_card_shadow',
352 'selector' => '{{WRAPPER}} .king-addons-review',
353 ]
354 );
355
356 $this->add_control(
357 'kng_card_radius',
358 [
359 'label' => esc_html__('Border Radius', 'king-addons'),
360 'type' => Controls_Manager::SLIDER,
361 'size_units' => ['px', '%'],
362 'range' => [
363 'px' => ['min' => 0, 'max' => 40],
364 '%' => ['min' => 0, 'max' => 100],
365 ],
366 'selectors' => [
367 '{{WRAPPER}} .king-addons-review' => 'border-radius: {{SIZE}}{{UNIT}};',
368 ],
369 ]
370 );
371
372 $this->add_responsive_control(
373 'kng_card_padding',
374 [
375 'label' => esc_html__('Padding', 'king-addons'),
376 'type' => Controls_Manager::DIMENSIONS,
377 'size_units' => ['px', 'em'],
378 'selectors' => [
379 '{{WRAPPER}} .king-addons-review' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
380 ],
381 ]
382 );
383
384 $this->end_controls_section();
385 }
386
387 /**
388 * Pro notice.
389 *
390 * @return void
391 */
392 public function register_pro_notice_controls(): void
393 {
394 if (!king_addons_freemius()->can_use_premium_code__premium_only()) {
395 Core::renderProFeaturesSection($this, '', Controls_Manager::RAW_HTML, 'review-schema', [
396 'Aggregate rating with count',
397 'Multiple reviews and layout options',
398 'Custom star icons and animations',
399 'Schema type selection (Product/LocalBusiness)',
400 ]);
401 }
402 }
403
404 /**
405 * Render stars.
406 *
407 * @param float $rating Rating.
408 *
409 * @return string
410 */
411 protected function render_stars(float $rating): string
412 {
413 $full = floor($rating);
414 $half = ($rating - $full) >= 0.5;
415 $html = '<span class="king-addons-review__stars" aria-hidden="true">';
416 for ($i = 1; $i <= 5; $i++) {
417 if ($i <= $full) {
418 $html .= '<span class="king-addons-review__star is-full">�
419 </span>';
420 } elseif ($half && $i === ($full + 1)) {
421 $html .= '<span class="king-addons-review__star is-half">�
422 </span>';
423 } else {
424 $html .= '<span class="king-addons-review__star is-empty">☆</span>';
425 }
426 }
427 $html .= '</span>';
428 return $html;
429 }
430
431 /**
432 * Build schema for single review.
433 *
434 * @param array<string, mixed> $settings Settings.
435 *
436 * @return array<string, mixed>
437 */
438 protected function build_schema(array $settings): array
439 {
440 $rating = (float) ($settings['kng_rating']['size'] ?? 0);
441 $author = $settings['kng_author'] ?? '';
442 $item_name = $settings['kng_item_name'] ?? '';
443 $title = $settings['kng_title'] ?? '';
444 $body = $settings['kng_body'] ?? '';
445 $date = !empty($settings['kng_date']) ? date_i18n('c', strtotime((string) $settings['kng_date'])) : '';
446
447 return [
448 '@context' => 'https://schema.org',
449 '@type' => 'Review',
450 'itemReviewed' => [
451 '@type' => 'Thing',
452 'name' => $item_name,
453 ],
454 'author' => [
455 '@type' => 'Person',
456 'name' => $author,
457 ],
458 'datePublished' => $date,
459 'name' => $title,
460 'reviewBody' => wp_strip_all_tags($body),
461 'reviewRating' => [
462 '@type' => 'Rating',
463 'ratingValue' => number_format($rating, 1, '.', ''),
464 'bestRating' => '5',
465 'worstRating' => '1',
466 ],
467 ];
468 }
469 }
470
471
472
473
474
475
476
477