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/widgets/Woo_Cart_Table/Woo_Cart_Table.php +89 -26 51.1.44 → 51.1.87 View file →
@@ -47,9 +47,9 @@
47 47 * @return string
48 48 */
49 49 public function get_icon(): string
50 50 {
51 - return 'eicon-cart';
51 + return 'king-addons-icon king-addons-woo-cart-table';
52 52 }
53 53
54 54 /**
55 55 * Categories.
@@ -111,9 +111,10 @@
111 111 'heading_empty',
112 112 [
113 113 'label' => esc_html__('Empty title', 'king-addons'),
114 114 'type' => Controls_Manager::TEXT,
115 - 'default' => esc_html__('Your cart is empty', 'king-addons'),
115 + 'dynamic' => ['active' => true],
116 + 'default' => __('Your cart is empty', 'king-addons'),
116 117 ]
117 118 );
118 119
119 120 $this->add_control(
@@ -120,9 +121,10 @@
120 121 'message_empty',
121 122 [
122 123 'label' => esc_html__('Empty message', 'king-addons'),
123 124 'type' => Controls_Manager::TEXTAREA,
124 - 'default' => esc_html__('Looks like you have not added anything to your cart yet.', 'king-addons'),
125 + 'dynamic' => ['active' => true],
126 + 'default' => __('Looks like you have not added anything to your cart yet.', 'king-addons'),
125 127 ]
126 128 );
127 129
128 130 $this->add_control(
@@ -129,9 +131,10 @@
129 131 'primary_btn_text',
130 132 [
131 133 'label' => esc_html__('Primary button text', 'king-addons'),
132 134 'type' => Controls_Manager::TEXT,
133 - 'default' => esc_html__('Return to shop', 'king-addons'),
135 + 'dynamic' => ['active' => true],
136 + 'default' => __('Return to shop', 'king-addons'),
134 137 ]
135 138 );
136 139
137 140 $this->add_control(
@@ -138,8 +141,9 @@
138 141 'primary_btn_url',
139 142 [
140 143 'label' => esc_html__('Primary button URL', 'king-addons'),
141 144 'type' => Controls_Manager::URL,
145 + 'dynamic' => ['active' => true],
142 146 'placeholder' => 'https://',
143 147 ]
144 148 );
145 149
@@ -147,9 +151,10 @@
147 151 'secondary_btn_text',
148 152 [
149 153 'label' => sprintf(__('Secondary button text %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
150 154 'type' => Controls_Manager::TEXT,
151 - 'default' => esc_html__('View offers', 'king-addons'),
155 + 'dynamic' => ['active' => true],
156 + 'default' => __('View offers', 'king-addons'),
152 157 ]
153 158 );
154 159
155 160 $this->add_control(
@@ -156,8 +161,9 @@
156 161 'secondary_btn_url',
157 162 [
158 163 'label' => sprintf(__('Secondary button URL %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
159 164 'type' => Controls_Manager::URL,
165 + 'dynamic' => ['active' => true],
160 166 'placeholder' => 'https://',
161 167 ]
162 168 );
163 169
@@ -215,19 +221,23 @@
215 221 * @return void
216 222 */
217 223 protected function render(): void
218 224 {
219 - if (!function_exists('is_cart') || !is_cart()) {
225 + $settings = $this->get_settings_for_display();
226 +
227 + if (!$this->should_render()) {
220 228 $this->render_missing_cart_notice();
221 229 return;
222 230 }
223 231
224 - if (!WC()->cart || WC()->cart->is_empty()) {
225 - $this->render_empty_cart($settings ?? []);
232 + if (!function_exists('WC') || !WC()->cart) {
226 233 return;
227 234 }
228 235
229 - $settings = $this->get_settings_for_display();
236 + if (WC()->cart->is_empty()) {
237 + $this->render_empty_cart($settings);
238 + return;
239 + }
230 240 $can_pro = king_addons_can_use_pro();
231 241 $include_collaterals = (!empty($settings['show_collaterals']) && $can_pro);
232 242
233 243 $had_totals = has_action('woocommerce_cart_collaterals', 'woocommerce_cart_totals');
@@ -243,9 +253,21 @@
243 253 }
244 254
245 255 $ajax_nonce = wp_create_nonce('ka_cart');
246 256
247 - echo '<div class="ka-cart-table" data-ka-cart="1" data-ajax-url="' . esc_url(admin_url('admin-ajax.php')) . '" data-nonce="' . esc_attr($ajax_nonce) . '">';
257 + // The empty-cart block after an AJAX removal used to be a hardcoded
258 + // minimal version, so emptying the cart in place looked nothing like
259 + // loading an empty cart. Hand the configured copy to the script.
260 + $shop_id = wc_get_page_id('shop');
261 + $shop_url = $shop_id > 0 ? get_permalink($shop_id) : home_url('/');
262 + $empty_attrs = ' data-empty-title="' . esc_attr((string) ($settings['heading_empty'] ?? '')) . '"'
263 + . ' data-empty-message="' . esc_attr((string) ($settings['message_empty'] ?? '')) . '"'
264 + . ' data-empty-primary-text="' . esc_attr((string) ($settings['primary_btn_text'] ?? '')) . '"'
265 + . ' data-empty-primary-url="' . esc_url(!empty($settings['primary_btn_url']['url']) ? $settings['primary_btn_url']['url'] : $shop_url) . '"'
266 + . ' data-empty-secondary-text="' . esc_attr($can_pro ? (string) ($settings['secondary_btn_text'] ?? '') : '') . '"'
267 + . ' data-empty-secondary-url="' . esc_url(($can_pro && !empty($settings['secondary_btn_url']['url'])) ? $settings['secondary_btn_url']['url'] : '') . '"';
268 +
269 + echo '<div class="ka-cart-table" data-ka-cart="1" data-ajax-url="' . esc_url(admin_url('admin-ajax.php')) . '" data-nonce="' . esc_attr($ajax_nonce) . '"' . $empty_attrs . '>';
248 270 woocommerce_output_all_notices();
249 271 wc_get_template('cart/cart.php');
250 272 echo '</div>';
251 273
@@ -276,11 +298,11 @@
276 298 $shop_id = wc_get_page_id('shop');
277 299 $shop_url = $shop_id > 0 ? get_permalink($shop_id) : home_url('/');
278 300 $can_pro = king_addons_can_use_pro();
279 301
280 - $heading = $settings['heading_empty'] ?? esc_html__('Your cart is empty', 'king-addons');
281 - $message = $settings['message_empty'] ?? esc_html__('Looks like you have not added anything to your cart yet.', 'king-addons');
282 - $primary_text = $settings['primary_btn_text'] ?? esc_html__('Return to shop', 'king-addons');
302 + $heading = $settings['heading_empty'] ?? __('Your cart is empty', 'king-addons');
303 + $message = $settings['message_empty'] ?? __('Looks like you have not added anything to your cart yet.', 'king-addons');
304 + $primary_text = $settings['primary_btn_text'] ?? __('Return to shop', 'king-addons');
283 305 $primary_url = !empty($settings['primary_btn_url']['url']) ? $settings['primary_btn_url']['url'] : $shop_url;
284 306 $secondary_text = (!empty($settings['secondary_btn_text']) && $can_pro) ? $settings['secondary_btn_text'] : '';
285 307 $secondary_url = (!empty($settings['secondary_btn_url']['url']) && $can_pro) ? $settings['secondary_btn_url']['url'] : '';
286 308
@@ -380,12 +402,15 @@
380 402 private static function build_fragments(): array
381 403 {
382 404 $empty = WC()->cart->is_empty();
383 405
406 + // Capture notices before cart HTML. Previously render_cart_html() printed
407 + // them first via woocommerce_output_all_notices(), so ajax fragments
408 + // returned an empty notices string and the coupon widget stayed silent.
409 + $notices = function_exists('wc_print_notices') ? wc_print_notices(true) : '';
384 410 $cart_html = $empty ? self::render_empty_cart_html() : self::render_cart_html();
385 411 $totals_html = self::render_totals_html($empty);
386 412 $cross_html = self::render_cross_sells_html($empty);
387 - $notices = function_exists('wc_print_notices') ? wc_print_notices(true) : '';
388 413
389 414 return [
390 415 'cart_html' => $cart_html,
391 416 'totals_html' => $totals_html,
@@ -404,11 +429,25 @@
404 429 */
405 430 private static function render_cart_html(): string
406 431 {
407 432 ob_start();
408 - woocommerce_output_all_notices();
433 + $had_totals = has_action('woocommerce_cart_collaterals', 'woocommerce_cart_totals');
434 + $had_cross = has_action('woocommerce_cart_collaterals', 'woocommerce_cross_sell_display');
435 + if ($had_totals) {
436 + remove_action('woocommerce_cart_collaterals', 'woocommerce_cart_totals', 10);
437 + }
438 + if ($had_cross) {
439 + remove_action('woocommerce_cart_collaterals', 'woocommerce_cross_sell_display', 10);
440 + }
409 441 wc_get_template('cart/cart.php');
410 - return (string) ob_get_clean();
442 + if ($had_totals) {
443 + add_action('woocommerce_cart_collaterals', 'woocommerce_cart_totals', 10);
444 + }
445 + if ($had_cross) {
446 + add_action('woocommerce_cart_collaterals', 'woocommerce_cross_sell_display', 10);
447 + }
448 + $html = (string) ob_get_clean();
449 + return (string) preg_replace('#<div class="cart-collaterals">\s*</div>#', '', $html);
411 450 }
412 451
413 452 /**
414 453 * Render totals HTML.
@@ -453,25 +492,49 @@
453 492 {
454 493 $shop_id = wc_get_page_id('shop');
455 494 $shop_url = $shop_id > 0 ? get_permalink($shop_id) : home_url('/');
456 495
496 + // The copy comes from the widget, passed along by the script, so the
497 + // markup matches what a full page load of an empty cart produces.
498 + // phpcs:disable WordPress.Security.NonceVerification.Missing -- verified by the caller.
499 + $heading = isset($_POST['empty_title']) ? sanitize_text_field(wp_unslash($_POST['empty_title'])) : __('Your cart is empty', 'king-addons');
500 + $message = isset($_POST['empty_message']) ? sanitize_text_field(wp_unslash($_POST['empty_message'])) : '';
501 + $primary_text = isset($_POST['empty_primary_text']) ? sanitize_text_field(wp_unslash($_POST['empty_primary_text'])) : __('Return to shop', 'king-addons');
502 + $primary_url = isset($_POST['empty_primary_url']) ? esc_url_raw(wp_unslash($_POST['empty_primary_url'])) : '';
503 + $secondary_text = isset($_POST['empty_secondary_text']) ? sanitize_text_field(wp_unslash($_POST['empty_secondary_text'])) : '';
504 + $secondary_url = isset($_POST['empty_secondary_url']) ? esc_url_raw(wp_unslash($_POST['empty_secondary_url'])) : '';
505 + // phpcs:enable WordPress.Security.NonceVerification.Missing
506 +
507 + if ('' === $primary_url) {
508 + $primary_url = $shop_url;
509 + }
510 +
457 511 ob_start();
458 512 echo '<div class="ka-cart-empty">';
459 - echo '<p class="ka-cart-empty__text">' . esc_html__('Your cart is empty.', 'king-addons') . '</p>';
460 - echo '<a class="ka-cart-empty__link" href="' . esc_url($shop_url) . '">' . esc_html__('Return to shop', 'king-addons') . '</a>';
513 + echo '<div class="ka-cart-empty__icon" aria-hidden="true">🛒</div>';
514 + if ($heading) {
515 + echo '<h3 class="ka-cart-empty__title">' . esc_html($heading) . '</h3>';
516 + }
517 + if ($message) {
518 + echo '<p class="ka-cart-empty__text">' . esc_html($message) . '</p>';
519 + }
520 + echo '<div class="ka-cart-empty__actions">';
521 + if ($primary_text && $primary_url) {
522 + echo '<a class="ka-cart-empty__btn ka-cart-empty__btn--primary" href="' . esc_url($primary_url) . '">' . esc_html($primary_text) . '</a>';
523 + }
524 + if ($secondary_text && $secondary_url) {
525 + echo '<a class="ka-cart-empty__btn ka-cart-empty__btn--ghost" href="' . esc_url($secondary_url) . '">' . esc_html($secondary_text) . '</a>';
526 + }
461 527 echo '</div>';
528 + do_action('king_addons/cart/empty_after');
529 + echo '</div>';
462 530 return (string) ob_get_clean();
463 531 }
464 532 }
465 533
466 -// Register AJAX handlers only if WooCommerce is active
467 -if (class_exists('WooCommerce')) {
468 - add_action('wp_ajax_ka_cart_update', [Woo_Cart_Table::class, 'ajax_update']);
469 - add_action('wp_ajax_nopriv_ka_cart_update', [Woo_Cart_Table::class, 'ajax_update']);
470 - add_action('wp_ajax_ka_cart_coupon', [Woo_Cart_Table::class, 'ajax_coupon']);
471 - add_action('wp_ajax_nopriv_ka_cart_coupon', [Woo_Cart_Table::class, 'ajax_coupon']);
472 -}
473 -
534 +// Registration lives in Woo_Builder: admin-ajax.php never loads Elementor
535 +// widget files, so hooking from here meant the quantity, remove and coupon
536 +// requests all came back as "0".
474 537
475 538
476 539
477 540