PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.49
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.49
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.49, at includes/widgets/Woo_Cart_Table/Woo_Cart_Table.php

480 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 if (!function_exists('is_cart') || !is_cart()) {
220 $this->render_missing_cart_notice();
221 return;
222 }
223
224 if (!WC()->cart || WC()->cart->is_empty()) {
225 $this->render_empty_cart($settings ?? []);
226 return;
227 }
228
229 $settings = $this->get_settings_for_display();
230 $can_pro = king_addons_can_use_pro();
231 $include_collaterals = (!empty($settings['show_collaterals']) && $can_pro);
232
233 $had_totals = has_action('woocommerce_cart_collaterals', 'woocommerce_cart_totals');
234 $had_cross = has_action('woocommerce_cart_collaterals', 'woocommerce_cross_sell_display');
235
236 if (!$include_collaterals) {
237 if ($had_totals) {
238 remove_action('woocommerce_cart_collaterals', 'woocommerce_cart_totals', 10);
239 }
240 if ($had_cross) {
241 remove_action('woocommerce_cart_collaterals', 'woocommerce_cross_sell_display', 10);
242 }
243 }
244
245 $ajax_nonce = wp_create_nonce('ka_cart');
246
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) . '">';
248 woocommerce_output_all_notices();
249 wc_get_template('cart/cart.php');
250 echo '</div>';
251
252 if (!$include_collaterals) {
253 if ($had_totals) {
254 add_action('woocommerce_cart_collaterals', 'woocommerce_cart_totals', 10);
255 }
256 if ($had_cross) {
257 add_action('woocommerce_cart_collaterals', 'woocommerce_cross_sell_display', 10);
258 }
259 }
260 }
261
262 /**
263 * Render simple empty cart notice.
264 *
265 * @return void
266 */
267 /**
268 * Render empty cart block.
269 *
270 * @param array<string,mixed> $settings Widget settings.
271 *
272 * @return void
273 */
274 protected function render_empty_cart(array $settings = []): void
275 {
276 $shop_id = wc_get_page_id('shop');
277 $shop_url = $shop_id > 0 ? get_permalink($shop_id) : home_url('/');
278 $can_pro = king_addons_can_use_pro();
279
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');
283 $primary_url = !empty($settings['primary_btn_url']['url']) ? $settings['primary_btn_url']['url'] : $shop_url;
284 $secondary_text = (!empty($settings['secondary_btn_text']) && $can_pro) ? $settings['secondary_btn_text'] : '';
285 $secondary_url = (!empty($settings['secondary_btn_url']['url']) && $can_pro) ? $settings['secondary_btn_url']['url'] : '';
286
287 echo '<div class="ka-cart-empty">';
288 echo '<div class="ka-cart-empty__icon" aria-hidden="true">🛒</div>';
289 if ($heading) {
290 echo '<h3 class="ka-cart-empty__title">' . esc_html($heading) . '</h3>';
291 }
292 if ($message) {
293 echo '<p class="ka-cart-empty__text">' . esc_html($message) . '</p>';
294 }
295 echo '<div class="ka-cart-empty__actions">';
296 if ($primary_text && $primary_url) {
297 echo '<a class="ka-cart-empty__btn ka-cart-empty__btn--primary" href="' . esc_url($primary_url) . '">' . esc_html($primary_text) . '</a>';
298 }
299 if ($secondary_text && $secondary_url) {
300 echo '<a class="ka-cart-empty__btn ka-cart-empty__btn--ghost" href="' . esc_url($secondary_url) . '">' . esc_html($secondary_text) . '</a>';
301 }
302 echo '</div>';
303 /**
304 * Hook to inject additional empty cart content (e.g., Elementor template).
305 */
306 do_action('king_addons/cart/empty_after');
307 echo '</div>';
308 }
309
310 /**
311 * AJAX: Update quantity or remove item.
312 *
313 * @return void
314 */
315 public static function ajax_update(): void
316 {
317 check_ajax_referer('ka_cart', 'nonce');
318
319 if (!WC()->cart) {
320 wp_send_json_error(['message' => esc_html__('Cart unavailable.', 'king-addons')]);
321 }
322
323 $op = sanitize_text_field($_POST['op'] ?? '');
324 $key = sanitize_text_field($_POST['cart_item_key'] ?? '');
325
326 if (!$key) {
327 wp_send_json_error(['message' => esc_html__('Missing cart item.', 'king-addons')]);
328 }
329
330 if ('remove' === $op) {
331 WC()->cart->remove_cart_item($key);
332 } elseif ('qty' === $op) {
333 $qty = isset($_POST['qty']) ? (float) wc_clean(wp_unslash($_POST['qty'])) : 0;
334 WC()->cart->set_quantity($key, $qty, true);
335 } else {
336 wp_send_json_error(['message' => esc_html__('Unknown operation.', 'king-addons')]);
337 }
338
339 WC()->cart->calculate_totals();
340
341 wp_send_json_success(self::build_fragments());
342 }
343
344 /**
345 * AJAX: Apply/remove coupon.
346 *
347 * @return void
348 */
349 public static function ajax_coupon(): void
350 {
351 check_ajax_referer('ka_cart', 'nonce');
352
353 if (!WC()->cart) {
354 wp_send_json_error(['message' => esc_html__('Cart unavailable.', 'king-addons')]);
355 }
356
357 $code = isset($_POST['coupon']) ? wc_format_coupon_code(wp_unslash($_POST['coupon'])) : '';
358 $mode = sanitize_text_field($_POST['mode'] ?? 'apply');
359
360 if (!$code) {
361 wp_send_json_error(['message' => esc_html__('Coupon is empty.', 'king-addons')]);
362 }
363
364 if ('remove' === $mode) {
365 WC()->cart->remove_coupon($code);
366 } else {
367 WC()->cart->apply_coupon($code);
368 }
369
370 WC()->cart->calculate_totals();
371
372 wp_send_json_success(self::build_fragments());
373 }
374
375 /**
376 * Build fragments for AJAX responses.
377 *
378 * @return array<string, mixed>
379 */
380 private static function build_fragments(): array
381 {
382 $empty = WC()->cart->is_empty();
383
384 $cart_html = $empty ? self::render_empty_cart_html() : self::render_cart_html();
385 $totals_html = self::render_totals_html($empty);
386 $cross_html = self::render_cross_sells_html($empty);
387 $notices = function_exists('wc_print_notices') ? wc_print_notices(true) : '';
388
389 return [
390 'cart_html' => $cart_html,
391 'totals_html' => $totals_html,
392 'cross_sells_html' => $cross_html,
393 'notices' => $notices,
394 'empty' => $empty,
395 'total' => WC()->cart->get_cart_contents_count(),
396 'cart_hash' => WC()->cart->get_cart_hash(),
397 ];
398 }
399
400 /**
401 * Render cart HTML.
402 *
403 * @return string
404 */
405 private static function render_cart_html(): string
406 {
407 ob_start();
408 woocommerce_output_all_notices();
409 wc_get_template('cart/cart.php');
410 return (string) ob_get_clean();
411 }
412
413 /**
414 * Render totals HTML.
415 *
416 * @param bool $empty Is cart empty.
417 *
418 * @return string
419 */
420 private static function render_totals_html(bool $empty): string
421 {
422 if ($empty) {
423 return '';
424 }
425
426 return Woo_Cart_Totals::render_totals_block_html();
427 }
428
429 /**
430 * Render cross-sells HTML.
431 *
432 * @param bool $empty Is cart empty.
433 *
434 * @return string
435 */
436 private static function render_cross_sells_html(bool $empty): string
437 {
438 if ($empty) {
439 return '';
440 }
441
442 ob_start();
443 woocommerce_cross_sell_display();
444 return (string) ob_get_clean();
445 }
446
447 /**
448 * Render empty cart markup for AJAX.
449 *
450 * @return string
451 */
452 private static function render_empty_cart_html(): string
453 {
454 $shop_id = wc_get_page_id('shop');
455 $shop_url = $shop_id > 0 ? get_permalink($shop_id) : home_url('/');
456
457 ob_start();
458 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>';
461 echo '</div>';
462 return (string) ob_get_clean();
463 }
464 }
465
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
474
475
476
477
478
479
480