PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.87
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.87
51.1.87 51.1.86 51.1.84 51.1.85 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 All 41 releases
← All changes | includes/wishlist/Wishlist_Renderer.php +114 -6 51.1.76 → 51.1.87 View file →
@@ -53,10 +53,14 @@
53 53 if ($product_id <= 0) {
54 54 return '';
55 55 }
56 56
57 - $wishlist_id = $args['wishlist_id'] ? sanitize_title($args['wishlist_id']) : $this->service->get_active_wishlist_id();
57 + if (function_exists('wc_get_product') && !wc_get_product($product_id)) {
58 + return '';
59 + }
58 60
61 + $wishlist_id = !empty($args['wishlist_id']) ? sanitize_title((string) $args['wishlist_id']) : $this->service->get_active_wishlist_id();
62 +
59 63 $in_list = $this->service->has_item($product_id, $variation_id, $wishlist_id);
60 64 $label_default = $args['label_default'] ?? Wishlist_Settings::get('button_add_text');
61 65 $label_added = $args['label_added'] ?? Wishlist_Settings::get('button_added_text');
62 66 $label = $in_list ? $label_added : $label_default;
@@ -64,23 +68,127 @@
64 68
65 69 $display_mode = $args['display_mode'] ?? Wishlist_Settings::get('button_display_mode', 'icon_text');
66 70 $show_icon = in_array($display_mode, ['icon', 'icon_text'], true);
67 71 $show_label = $display_mode === 'icon_text';
68 - $icon_class = $args['icon_class'] ?? Wishlist_Settings::get('icon_choice', 'eicon-heart');
72 + $icon_class = (string) ($args['icon_class'] ?? Wishlist_Settings::get('icon_choice', 'fas fa-heart'));
69 73
70 74 $classes = [
71 75 'king-addons-wishlist-button',
72 76 $state_class,
73 - sanitize_html_class($args['class']),
77 + sanitize_html_class((string) $args['class']),
74 78 ];
75 79
76 - $icon_html = $show_icon ? '<span class="king-addons-wishlist-button__icon" aria-hidden="true"><i class="' . esc_attr($icon_class) . '"></i></span>' : '';
80 + $icon_html = $show_icon ? $this->render_icon_html($icon_class) : '';
77 81 $label_html = $show_label ? '<span class="king-addons-wishlist-button__label">' . esc_html($label) . '</span>' : '';
78 82
79 - return '<button type="button" class="' . esc_attr(implode(' ', array_filter($classes))) . '" data-product-id="' . esc_attr($product_id) . '" data-variation-id="' . esc_attr($variation_id) . '" data-wishlist-id="' . esc_attr($wishlist_id) . '" data-state="' . esc_attr($in_list ? 'added' : 'default') . '" aria-pressed="' . ($in_list ? 'true' : 'false') . '">' . $icon_html . $label_html . '</button>';
83 + return '<button type="button" class="' . esc_attr(implode(' ', array_filter($classes))) . '" data-product-id="' . esc_attr((string) $product_id) . '" data-variation-id="' . esc_attr((string) $variation_id) . '" data-wishlist-id="' . esc_attr((string) $wishlist_id) . '" data-state="' . esc_attr($in_list ? 'added' : 'default') . '" data-display-mode="' . esc_attr((string) $display_mode) . '" data-label-default="' . esc_attr((string) $label_default) . '" data-label-added="' . esc_attr((string) $label_added) . '" aria-pressed="' . ($in_list ? 'true' : 'false') . '" aria-label="' . esc_attr((string) $label) . '">' . $icon_html . $label_html . '</button>';
80 84 }
81 85
82 86 /**
87 + * Built-in heart SVG used by the button and floating icon.
88 + *
89 + * @param string $class Extra SVG class.
90 + * @return string SVG markup.
91 + */
92 + public static function heart_svg(string $class = 'king-addons-wishlist-button__heart'): string
93 + {
94 + return '<svg class="' . esc_attr($class) . '" viewBox="0 0 24 24" width="16" height="16" focusable="false" aria-hidden="true">'
95 + . '<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78L12 21.23l8.84-8.84a5.5 5.5 0 0 0 0-7.78z"/>'
96 + . '</svg>';
97 + }
98 +
99 + /**
100 + * Render the button icon: built-in SVG heart, or a custom font class.
101 + *
102 + * @param string $icon_class Icon class from settings/widget.
103 + * @return string Icon markup.
104 + */
105 + private function render_icon_html(string $icon_class): string
106 + {
107 + if ($this->uses_builtin_heart($icon_class)) {
108 + return '<span class="king-addons-wishlist-button__icon" aria-hidden="true">' . self::heart_svg() . '</span>';
109 + }
110 +
111 + $classes = preg_split('/\s+/', trim($icon_class), -1, PREG_SPLIT_NO_EMPTY);
112 + $safe = implode(' ', array_map('sanitize_html_class', is_array($classes) ? $classes : []));
113 + if ($safe === '') {
114 + return $this->render_icon_html('fas fa-heart');
115 + }
116 +
117 + return '<span class="king-addons-wishlist-button__icon" aria-hidden="true"><i class="' . esc_attr($safe) . '"></i></span>';
118 + }
119 +
120 + /**
121 + * Default heart classes use the built-in SVG instead of a webfont.
122 + *
123 + * @param string $icon_class Icon class from settings/widget.
124 + * @return bool Whether to render the SVG heart.
125 + */
126 + private function uses_builtin_heart(string $icon_class): bool
127 + {
128 + $normalized = strtolower(trim(preg_replace('/\s+/', ' ', $icon_class) ?? $icon_class));
129 +
130 + return in_array($normalized, [
131 + '',
132 + 'fas fa-heart',
133 + 'fa fa-heart',
134 + 'far fa-heart',
135 + 'fa-solid fa-heart',
136 + 'fa-regular fa-heart',
137 + 'eicon-heart',
138 + 'eicon-heart-o',
139 + ], true);
140 + }
141 +
142 + /**
143 + * Sanitize renderer HTML for echo contexts that still want kses.
144 + *
145 + * @param string $html Button or similar markup.
146 + * @return string Sanitized HTML.
147 + */
148 + public static function kses(string $html): string
149 + {
150 + $allowed = [
151 + 'button' => [
152 + 'type' => true,
153 + 'class' => true,
154 + 'disabled' => true,
155 + 'aria-pressed' => true,
156 + 'aria-label' => true,
157 + ],
158 + 'span' => [
159 + 'class' => true,
160 + 'aria-hidden' => true,
161 + ],
162 + 'i' => [
163 + 'class' => true,
164 + 'aria-hidden' => true,
165 + ],
166 + 'svg' => [
167 + 'class' => true,
168 + 'viewbox' => true,
169 + 'width' => true,
170 + 'height' => true,
171 + 'focusable' => true,
172 + 'aria-hidden' => true,
173 + ],
174 + 'path' => [
175 + 'd' => true,
176 + ],
177 + ];
178 +
179 + foreach ($allowed as $tag => $attrs) {
180 + $allowed[$tag] = array_merge($attrs, [
181 + 'class' => true,
182 + 'aria-hidden' => true,
183 + 'data-*' => true,
184 + ]);
185 + }
186 +
187 + return wp_kses($html, $allowed);
188 + }
189 +
190 + /**
83 191 * Render wishlist counter element.
84 192 *
85 193 * @param array<string, mixed> $args Rendering arguments.
86 194 * @return string Rendered HTML.
@@ -90,9 +198,9 @@
90 198 if (!Wishlist_Settings::is_enabled()) {
91 199 return '';
92 200 }
93 201
94 - $wishlist_id = $args['wishlist_id'] ? sanitize_title($args['wishlist_id']) : $this->service->get_active_wishlist_id();
202 + $wishlist_id = !empty($args['wishlist_id']) ? sanitize_title((string) $args['wishlist_id']) : $this->service->get_active_wishlist_id();
95 203 $count = $this->service->get_count($wishlist_id);
96 204
97 205 $classes = [
98 206 'king-addons-wishlist-counter',