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 / Woo_Cart_Table / Woo_Cart_Table.php

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

483 lines 13.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Woo Cart Table widget.
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
15 if (!defined('ABSPATH')) {
16 exit;
17 }
18
19 /**
20 * Renders the WooCommerce cart table.
21 */
22 class Woo_Cart_Table extends Abstract_Cart_Widget
23 {
24 /**
25 * Widget slug.
26 *
27 * @return string
28 */
29 public function get_name(): string
30 {
31 return 'woo_cart_table';
32 }
33
34 /**
35 * Widget title.
36 *
37 * @return string
38 */
39 public function get_title(): string
40 {
41 return esc_html__('Cart Table', 'king-addons');
42 }
43
44 /**
45 * Widget icon.
46 *
47 * @return string
48 */
49 public function get_icon(): string
50 {
51 return 'eicon-cart';
52 }
53
54 /**
55 * Categories.
56 *
57 * @return array<int, string>
58 */
59 public function get_categories(): array
60 {
61 return ['king-addons-woo-builder'];
62 }
63
64 /**
65 * Style dependencies.
66 *
67 * @return array<int, string>
68 */
69 public function get_style_depends(): array
70 {
71 return [KING_ADDONS_ASSETS_UNIQUE_KEY . '-woo-cart-table-style'];
72 }
73
74 /**
75 * Script dependencies.
76 *
77 * @return array<int, string>
78 */
79 public function get_script_depends(): array
80 {
81 return [
82 KING_ADDONS_ASSETS_UNIQUE_KEY . '-woo-cart-table-script',
83 ];
84 }
85
86 /**
87 * Register controls.
88 *
89 * @return void
90 */
91 protected function register_controls(): void
92 {
93 $this->start_controls_section(
94 'section_content',
95 [
96 'label' => esc_html__('Content', 'king-addons'),
97 'tab' => Controls_Manager::TAB_CONTENT,
98 ]
99 );
100
101 $this->add_control(
102 'show_collaterals',
103 [
104 'label' => sprintf(__('Include Totals & Cross-sells (Pro) %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
105 'type' => Controls_Manager::SWITCHER,
106 'return_value' => 'yes',
107 ]
108 );
109
110 $this->add_control(
111 'heading_empty',
112 [
113 'label' => esc_html__('Empty title', 'king-addons'),
114 'type' => Controls_Manager::TEXT,
115 'default' => esc_html__('Your cart is empty', 'king-addons'),
116 ]
117 );
118
119 $this->add_control(
120 'message_empty',
121 [
122 'label' => esc_html__('Empty message', 'king-addons'),
123 'type' => Controls_Manager::TEXTAREA,
124 'default' => esc_html__('Looks like you have not added anything to your cart yet.', 'king-addons'),
125 ]
126 );
127
128 $this->add_control(
129 'primary_btn_text',
130 [
131 'label' => esc_html__('Primary button text', 'king-addons'),
132 'type' => Controls_Manager::TEXT,
133 'default' => esc_html__('Return to shop', 'king-addons'),
134 ]
135 );
136
137 $this->add_control(
138 'primary_btn_url',
139 [
140 'label' => esc_html__('Primary button URL', 'king-addons'),
141 'type' => Controls_Manager::URL,
142 'placeholder' => 'https://',
143 ]
144 );
145
146 $this->add_control(
147 'secondary_btn_text',
148 [
149 'label' => sprintf(__('Secondary button text %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
150 'type' => Controls_Manager::TEXT,
151 'default' => esc_html__('View offers', 'king-addons'),
152 ]
153 );
154
155 $this->add_control(
156 'secondary_btn_url',
157 [
158 'label' => sprintf(__('Secondary button URL %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
159 'type' => Controls_Manager::URL,
160 'placeholder' => 'https://',
161 ]
162 );
163
164 $this->end_controls_section();
165
166 $this->start_controls_section(
167 'section_style_table',
168 [
169 'label' => esc_html__('Table', 'king-addons'),
170 'tab' => Controls_Manager::TAB_STYLE,
171 ]
172 );
173
174 $this->add_group_control(
175 Group_Control_Typography::get_type(),
176 [
177 'name' => 'table_typography',
178 'selector' => '{{WRAPPER}} .woocommerce-cart-form',
179 ]
180 );
181
182 $this->add_control(
183 'table_text_color',
184 [
185 'label' => esc_html__('Text Color', 'king-addons'),
186 'type' => Controls_Manager::COLOR,
187 'selectors' => [
188 '{{WRAPPER}} .woocommerce-cart-form' => 'color: {{VALUE}};',
189 ],
190 ]
191 );
192
193 $this->add_group_control(
194 Group_Control_Border::get_type(),
195 [
196 'name' => 'table_border',
197 'selector' => '{{WRAPPER}} .woocommerce-cart-form table',
198 ]
199 );
200
201 $this->add_group_control(
202 Group_Control_Box_Shadow::get_type(),
203 [
204 'name' => 'table_shadow',
205 'selector' => '{{WRAPPER}} .woocommerce-cart-form table',
206 ]
207 );
208
209 $this->end_controls_section();
210 }
211
212 /**
213 * Render widget output.
214 *
215 * @return void
216 */
217 protected function render(): void
218 {
219 $settings = $this->get_settings_for_display();
220
221 if (!$this->should_render()) {
222 $this->render_missing_cart_notice();
223 return;
224 }
225
226 if (!function_exists('WC') || !WC()->cart) {
227 return;
228 }
229
230 if (WC()->cart->is_empty()) {
231 $this->render_empty_cart($settings);
232 return;
233 }
234 $can_pro = king_addons_can_use_pro();
235 $include_collaterals = (!empty($settings['show_collaterals']) && $can_pro);
236
237 $had_totals = has_action('woocommerce_cart_collaterals', 'woocommerce_cart_totals');
238 $had_cross = has_action('woocommerce_cart_collaterals', 'woocommerce_cross_sell_display');
239
240 if (!$include_collaterals) {
241 if ($had_totals) {
242 remove_action('woocommerce_cart_collaterals', 'woocommerce_cart_totals', 10);
243 }
244 if ($had_cross) {
245 remove_action('woocommerce_cart_collaterals', 'woocommerce_cross_sell_display', 10);
246 }
247 }
248
249 $ajax_nonce = wp_create_nonce('ka_cart');
250
251 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) . '">';
252 woocommerce_output_all_notices();
253 wc_get_template('cart/cart.php');
254 echo '</div>';
255
256 if (!$include_collaterals) {
257 if ($had_totals) {
258 add_action('woocommerce_cart_collaterals', 'woocommerce_cart_totals', 10);
259 }
260 if ($had_cross) {
261 add_action('woocommerce_cart_collaterals', 'woocommerce_cross_sell_display', 10);
262 }
263 }
264 }
265
266 /**
267 * Render simple empty cart notice.
268 *
269 * @return void
270 */
271 /**
272 * Render empty cart block.
273 *
274 * @param array<string,mixed> $settings Widget settings.
275 *
276 * @return void
277 */
278 protected function render_empty_cart(array $settings = []): void
279 {
280 $shop_id = wc_get_page_id('shop');
281 $shop_url = $shop_id > 0 ? get_permalink($shop_id) : home_url('/');
282 $can_pro = king_addons_can_use_pro();
283
284 $heading = $settings['heading_empty'] ?? esc_html__('Your cart is empty', 'king-addons');
285 $message = $settings['message_empty'] ?? esc_html__('Looks like you have not added anything to your cart yet.', 'king-addons');
286 $primary_text = $settings['primary_btn_text'] ?? esc_html__('Return to shop', 'king-addons');
287 $primary_url = !empty($settings['primary_btn_url']['url']) ? $settings['primary_btn_url']['url'] : $shop_url;
288 $secondary_text = (!empty($settings['secondary_btn_text']) && $can_pro) ? $settings['secondary_btn_text'] : '';
289 $secondary_url = (!empty($settings['secondary_btn_url']['url']) && $can_pro) ? $settings['secondary_btn_url']['url'] : '';
290
291 echo '<div class="ka-cart-empty">';
292 echo '<div class="ka-cart-empty__icon" aria-hidden="true">🛒</div>';
293 if ($heading) {
294 echo '<h3 class="ka-cart-empty__title">' . esc_html($heading) . '</h3>';
295 }
296 if ($message) {
297 echo '<p class="ka-cart-empty__text">' . esc_html($message) . '</p>';
298 }
299 echo '<div class="ka-cart-empty__actions">';
300 if ($primary_text && $primary_url) {
301 echo '<a class="ka-cart-empty__btn ka-cart-empty__btn--primary" href="' . esc_url($primary_url) . '">' . esc_html($primary_text) . '</a>';
302 }
303 if ($secondary_text && $secondary_url) {
304 echo '<a class="ka-cart-empty__btn ka-cart-empty__btn--ghost" href="' . esc_url($secondary_url) . '">' . esc_html($secondary_text) . '</a>';
305 }
306 echo '</div>';
307 /**
308 * Hook to inject additional empty cart content (e.g., Elementor template).
309 */
310 do_action('king_addons/cart/empty_after');
311 echo '</div>';
312 }
313
314 /**
315 * AJAX: Update quantity or remove item.
316 *
317 * @return void
318 */
319 public static function ajax_update(): void
320 {
321 check_ajax_referer('ka_cart', 'nonce');
322
323 if (!WC()->cart) {
324 wp_send_json_error(['message' => esc_html__('Cart unavailable.', 'king-addons')]);
325 }
326
327 $op = sanitize_text_field($_POST['op'] ?? '');
328 $key = sanitize_text_field($_POST['cart_item_key'] ?? '');
329
330 if (!$key) {
331 wp_send_json_error(['message' => esc_html__('Missing cart item.', 'king-addons')]);
332 }
333
334 if ('remove' === $op) {
335 WC()->cart->remove_cart_item($key);
336 } elseif ('qty' === $op) {
337 $qty = isset($_POST['qty']) ? (float) wc_clean(wp_unslash($_POST['qty'])) : 0;
338 WC()->cart->set_quantity($key, $qty, true);
339 } else {
340 wp_send_json_error(['message' => esc_html__('Unknown operation.', 'king-addons')]);
341 }
342
343 WC()->cart->calculate_totals();
344
345 wp_send_json_success(self::build_fragments());
346 }
347
348 /**
349 * AJAX: Apply/remove coupon.
350 *
351 * @return void
352 */
353 public static function ajax_coupon(): void
354 {
355 check_ajax_referer('ka_cart', 'nonce');
356
357 if (!WC()->cart) {
358 wp_send_json_error(['message' => esc_html__('Cart unavailable.', 'king-addons')]);
359 }
360
361 $code = isset($_POST['coupon']) ? wc_format_coupon_code(wp_unslash($_POST['coupon'])) : '';
362 $mode = sanitize_text_field($_POST['mode'] ?? 'apply');
363
364 if (!$code) {
365 wp_send_json_error(['message' => esc_html__('Coupon is empty.', 'king-addons')]);
366 }
367
368 if ('remove' === $mode) {
369 WC()->cart->remove_coupon($code);
370 } else {
371 WC()->cart->apply_coupon($code);
372 }
373
374 WC()->cart->calculate_totals();
375
376 wp_send_json_success(self::build_fragments());
377 }
378
379 /**
380 * Build fragments for AJAX responses.
381 *
382 * @return array<string, mixed>
383 */
384 private static function build_fragments(): array
385 {
386 $empty = WC()->cart->is_empty();
387
388 $cart_html = $empty ? self::render_empty_cart_html() : self::render_cart_html();
389 $totals_html = self::render_totals_html($empty);
390 $cross_html = self::render_cross_sells_html($empty);
391 $notices = function_exists('wc_print_notices') ? wc_print_notices(true) : '';
392
393 return [
394 'cart_html' => $cart_html,
395 'totals_html' => $totals_html,
396 'cross_sells_html' => $cross_html,
397 'notices' => $notices,
398 'empty' => $empty,
399 'total' => WC()->cart->get_cart_contents_count(),
400 'cart_hash' => WC()->cart->get_cart_hash(),
401 ];
402 }
403
404 /**
405 * Render cart HTML.
406 *
407 * @return string
408 */
409 private static function render_cart_html(): string
410 {
411 ob_start();
412 woocommerce_output_all_notices();
413 wc_get_template('cart/cart.php');
414 return (string) ob_get_clean();
415 }
416
417 /**
418 * Render totals HTML.
419 *
420 * @param bool $empty Is cart empty.
421 *
422 * @return string
423 */
424 private static function render_totals_html(bool $empty): string
425 {
426 if ($empty) {
427 return '';
428 }
429
430 return Woo_Cart_Totals::render_totals_block_html();
431 }
432
433 /**
434 * Render cross-sells HTML.
435 *
436 * @param bool $empty Is cart empty.
437 *
438 * @return string
439 */
440 private static function render_cross_sells_html(bool $empty): string
441 {
442 if ($empty) {
443 return '';
444 }
445
446 ob_start();
447 woocommerce_cross_sell_display();
448 return (string) ob_get_clean();
449 }
450
451 /**
452 * Render empty cart markup for AJAX.
453 *
454 * @return string
455 */
456 private static function render_empty_cart_html(): string
457 {
458 $shop_id = wc_get_page_id('shop');
459 $shop_url = $shop_id > 0 ? get_permalink($shop_id) : home_url('/');
460
461 ob_start();
462 echo '<div class="ka-cart-empty">';
463 echo '<p class="ka-cart-empty__text">' . esc_html__('Your cart is empty.', 'king-addons') . '</p>';
464 echo '<a class="ka-cart-empty__link" href="' . esc_url($shop_url) . '">' . esc_html__('Return to shop', 'king-addons') . '</a>';
465 echo '</div>';
466 return (string) ob_get_clean();
467 }
468 }
469
470 // Register AJAX handlers only if WooCommerce is active
471 if (class_exists('WooCommerce')) {
472 add_action('wp_ajax_ka_cart_update', [Woo_Cart_Table::class, 'ajax_update']);
473 add_action('wp_ajax_nopriv_ka_cart_update', [Woo_Cart_Table::class, 'ajax_update']);
474 add_action('wp_ajax_ka_cart_coupon', [Woo_Cart_Table::class, 'ajax_coupon']);
475 add_action('wp_ajax_nopriv_ka_cart_coupon', [Woo_Cart_Table::class, 'ajax_coupon']);
476 }
477
478
479
480
481
482
483