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_Checkout_Form / Woo_Checkout_Form.php

Woo_Checkout_Form.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_Checkout_Form/Woo_Checkout_Form.php

566 lines 19.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Woo Checkout Form 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 if (!defined('ABSPATH')) {
15 exit;
16 }
17
18 /**
19 * Renders the WooCommerce checkout form.
20 */
21 class Woo_Checkout_Form extends Abstract_Checkout_Widget
22 {
23 /**
24 * Last config cache for checkout fields filter.
25 *
26 * @var array<int,array<string,mixed>>
27 */
28 private static array $cached_config = [];
29
30 /**
31 * Last extra fields cache for checkout fields filter.
32 *
33 * @var array<int,array<string,mixed>>
34 */
35 private static array $cached_extra = [];
36
37 /**
38 * Widget slug.
39 *
40 * @return string
41 */
42 public function get_name(): string
43 {
44 return 'woo_checkout_form';
45 }
46
47 /**
48 * Widget title.
49 *
50 * @return string
51 */
52 public function get_title(): string
53 {
54 return esc_html__('Checkout Form', 'king-addons');
55 }
56
57 /**
58 * Widget icon.
59 *
60 * @return string
61 */
62 public function get_icon(): string
63 {
64 return 'eicon-checkout';
65 }
66
67 /**
68 * Categories.
69 *
70 * @return array<int, string>
71 */
72 public function get_categories(): array
73 {
74 return ['king-addons-woo-builder'];
75 }
76
77 /**
78 * Style dependencies.
79 *
80 * @return array<int, string>
81 */
82 public function get_style_depends(): array
83 {
84 return [KING_ADDONS_ASSETS_UNIQUE_KEY . '-woo-checkout-form-style'];
85 }
86
87 /**
88 * Register controls.
89 *
90 * @return void
91 */
92 protected function register_controls(): void
93 {
94 $this->start_controls_section(
95 'section_content',
96 [
97 'label' => esc_html__('Content', 'king-addons'),
98 'tab' => Controls_Manager::TAB_CONTENT,
99 ]
100 );
101
102 $this->add_control(
103 'show_login',
104 [
105 'label' => sprintf(__('Show Login Notice (Pro) %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
106 'type' => Controls_Manager::SWITCHER,
107 'return_value' => 'yes',
108 'default' => 'yes',
109 ]
110 );
111
112 $this->add_control(
113 'show_coupon',
114 [
115 'label' => sprintf(__('Show Coupon Form (Pro) %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
116 'type' => Controls_Manager::SWITCHER,
117 'return_value' => 'yes',
118 'default' => 'yes',
119 ]
120 );
121
122 $this->add_control(
123 'show_order_notes',
124 [
125 'label' => sprintf(__('Show Order Notes (Pro) %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
126 'type' => Controls_Manager::SWITCHER,
127 'return_value' => 'yes',
128 'default' => 'yes',
129 ]
130 );
131
132 $this->add_control(
133 'enable_field_customization',
134 [
135 'label' => sprintf(__('Customize fields (Pro) %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
136 'type' => Controls_Manager::SWITCHER,
137 'return_value' => 'yes',
138 ]
139 );
140
141 $this->add_control(
142 'fields_config',
143 [
144 'label' => esc_html__('Fields settings (label/placeholder/required/order/hide)', 'king-addons'),
145 'type' => Controls_Manager::REPEATER,
146 'condition' => [
147 'enable_field_customization' => 'yes',
148 ],
149 'fields' => [
150 [
151 'name' => 'field_key',
152 'label' => esc_html__('Field key', 'king-addons'),
153 'type' => Controls_Manager::TEXT,
154 'placeholder' => 'billing_phone',
155 ],
156 [
157 'name' => 'section',
158 'label' => esc_html__('Section', 'king-addons'),
159 'type' => Controls_Manager::SELECT,
160 'options' => [
161 'billing' => esc_html__('Billing', 'king-addons'),
162 'shipping' => esc_html__('Shipping', 'king-addons'),
163 'order' => esc_html__('Order notes', 'king-addons'),
164 ],
165 'default' => 'billing',
166 ],
167 [
168 'name' => 'label',
169 'label' => esc_html__('Label', 'king-addons'),
170 'type' => Controls_Manager::TEXT,
171 ],
172 [
173 'name' => 'help',
174 'label' => esc_html__('Help text (hint)', 'king-addons'),
175 'type' => Controls_Manager::TEXT,
176 ],
177 [
178 'name' => 'placeholder',
179 'label' => esc_html__('Placeholder', 'king-addons'),
180 'type' => Controls_Manager::TEXT,
181 ],
182 [
183 'name' => 'required',
184 'label' => esc_html__('Required', 'king-addons'),
185 'type' => Controls_Manager::SWITCHER,
186 'return_value' => 'yes',
187 ],
188 [
189 'name' => 'hide',
190 'label' => esc_html__('Hide field', 'king-addons'),
191 'type' => Controls_Manager::SWITCHER,
192 'return_value' => 'yes',
193 ],
194 [
195 'name' => 'priority',
196 'label' => esc_html__('Priority (order)', 'king-addons'),
197 'type' => Controls_Manager::NUMBER,
198 'default' => 20,
199 ],
200 [
201 'name' => 'visibility_mode',
202 'label' => esc_html__('Visibility rule', 'king-addons'),
203 'type' => Controls_Manager::SELECT,
204 'options' => [
205 'all' => esc_html__('Show everywhere', 'king-addons'),
206 'show' => esc_html__('Show only for countries', 'king-addons'),
207 'hide' => esc_html__('Hide for countries', 'king-addons'),
208 ],
209 'default' => 'all',
210 ],
211 [
212 'name' => 'countries',
213 'label' => esc_html__('Countries (Pro)', 'king-addons'),
214 'type' => Controls_Manager::SELECT2,
215 'multiple' => true,
216 'options' => (function_exists('WC') && WC()->countries) ? WC()->countries->get_countries() : [],
217 ],
218 ],
219 'default' => [],
220 'title_field' => '{{ field_key }}',
221 ]
222 );
223
224 $this->add_control(
225 'extra_fields',
226 [
227 'label' => esc_html__('Extra fields (Pro)', 'king-addons'),
228 'type' => Controls_Manager::REPEATER,
229 'condition' => [
230 'enable_field_customization' => 'yes',
231 ],
232 'fields' => [
233 [
234 'name' => 'field_key',
235 'label' => esc_html__('Field key', 'king-addons'),
236 'type' => Controls_Manager::TEXT,
237 'placeholder' => 'order_reference',
238 ],
239 [
240 'name' => 'section',
241 'label' => esc_html__('Section', 'king-addons'),
242 'type' => Controls_Manager::SELECT,
243 'options' => [
244 'billing' => esc_html__('Billing', 'king-addons'),
245 'shipping' => esc_html__('Shipping', 'king-addons'),
246 'order' => esc_html__('Order notes', 'king-addons'),
247 ],
248 'default' => 'order',
249 ],
250 [
251 'name' => 'label',
252 'label' => esc_html__('Label', 'king-addons'),
253 'type' => Controls_Manager::TEXT,
254 'default' => esc_html__('Extra field', 'king-addons'),
255 ],
256 [
257 'name' => 'placeholder',
258 'label' => esc_html__('Placeholder', 'king-addons'),
259 'type' => Controls_Manager::TEXT,
260 ],
261 [
262 'name' => 'type',
263 'label' => esc_html__('Type', 'king-addons'),
264 'type' => Controls_Manager::SELECT,
265 'options' => [
266 'text' => esc_html__('Text', 'king-addons'),
267 'textarea' => esc_html__('Textarea', 'king-addons'),
268 ],
269 'default' => 'text',
270 ],
271 [
272 'name' => 'required',
273 'label' => esc_html__('Required', 'king-addons'),
274 'type' => Controls_Manager::SWITCHER,
275 'return_value' => 'yes',
276 ],
277 [
278 'name' => 'priority',
279 'label' => esc_html__('Priority (order)', 'king-addons'),
280 'type' => Controls_Manager::NUMBER,
281 'default' => 80,
282 ],
283 ],
284 'default' => [],
285 'title_field' => '{{ field_key }}',
286 ]
287 );
288
289 $this->end_controls_section();
290
291 $this->start_controls_section(
292 'section_style',
293 [
294 'label' => esc_html__('Form', 'king-addons'),
295 'tab' => Controls_Manager::TAB_STYLE,
296 ]
297 );
298
299 $this->add_group_control(
300 Group_Control_Typography::get_type(),
301 [
302 'name' => 'form_typography',
303 'selector' => '{{WRAPPER}} .woocommerce',
304 ]
305 );
306
307 $this->add_control(
308 'text_color',
309 [
310 'label' => esc_html__('Text Color', 'king-addons'),
311 'type' => Controls_Manager::COLOR,
312 'selectors' => [
313 '{{WRAPPER}} .woocommerce' => 'color: {{VALUE}};',
314 ],
315 ]
316 );
317
318 $this->add_group_control(
319 Group_Control_Border::get_type(),
320 [
321 'name' => 'form_border',
322 'selector' => '{{WRAPPER}} .woocommerce form.checkout',
323 ]
324 );
325
326 $this->add_group_control(
327 Group_Control_Box_Shadow::get_type(),
328 [
329 'name' => 'form_shadow',
330 'selector' => '{{WRAPPER}} .woocommerce form.checkout',
331 ]
332 );
333
334 $this->add_control(
335 'form_padding',
336 [
337 'label' => esc_html__('Form Padding', 'king-addons'),
338 'type' => Controls_Manager::DIMENSIONS,
339 'size_units' => ['px', 'em', '%'],
340 'selectors' => [
341 '{{WRAPPER}} .woocommerce form.checkout' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
342 ],
343 ]
344 );
345
346 $this->end_controls_section();
347 }
348
349 /**
350 * Render widget output.
351 *
352 * @return void
353 */
354 protected function render(): void
355 {
356 if (!$this->should_render()) {
357 $this->render_missing_checkout_notice();
358 return;
359 }
360
361 if (!function_exists('WC')) {
362 return;
363 }
364
365 $settings = $this->get_settings_for_display();
366 $can_pro = king_addons_can_use_pro();
367
368 $show_login = !empty($settings['show_login']);
369 $show_coupon = !empty($settings['show_coupon']);
370 $show_notes = !empty($settings['show_order_notes']);
371 $field_customization = $can_pro && !empty($settings['enable_field_customization']);
372
373 $removed = [];
374
375 if (!$can_pro) {
376 $show_login = true;
377 $show_coupon = true;
378 $show_notes = true;
379 }
380
381 if (!$show_login) {
382 remove_action('woocommerce_before_checkout_form', 'woocommerce_checkout_login_form', 10);
383 $removed[] = 'login';
384 }
385
386 if (!$show_coupon) {
387 remove_action('woocommerce_before_checkout_form', 'woocommerce_checkout_coupon_form', 10);
388 $removed[] = 'coupon';
389 }
390
391 $notes_filter_added = false;
392 if (!$show_notes) {
393 add_filter('woocommerce_enable_order_notes_field', '__return_false', 9999);
394 $notes_filter_added = true;
395 }
396
397 $fields_filter_added = false;
398 if ($field_customization) {
399 $fields_filter_added = true;
400 self::$cached_config = $settings['fields_config'] ?? [];
401 self::$cached_extra = $settings['extra_fields'] ?? [];
402 add_filter('woocommerce_checkout_fields', [self::class, 'filter_checkout_fields'], 9999);
403 add_action('woocommerce_checkout_update_order_meta', [self::class, 'save_extra_fields'], 20, 2);
404 }
405
406 if (function_exists('woocommerce_checkout')) {
407 call_user_func('woocommerce_checkout');
408 }
409
410 if (in_array('login', $removed, true)) {
411 add_action('woocommerce_before_checkout_form', 'woocommerce_checkout_login_form', 10);
412 }
413 if (in_array('coupon', $removed, true)) {
414 add_action('woocommerce_before_checkout_form', 'woocommerce_checkout_coupon_form', 10);
415 }
416 if ($notes_filter_added) {
417 remove_filter('woocommerce_enable_order_notes_field', '__return_false', 9999);
418 }
419 if ($fields_filter_added) {
420 remove_filter('woocommerce_checkout_fields', [self::class, 'filter_checkout_fields'], 9999);
421 remove_action('woocommerce_checkout_update_order_meta', [self::class, 'save_extra_fields'], 20);
422 self::$cached_config = [];
423 self::$cached_extra = [];
424 }
425 }
426
427 /**
428 * Wrapper for WC checkout fields filter using cached config.
429 *
430 * @param array<string,array<string,array<string,mixed>>> $fields WC checkout fields.
431 *
432 * @return array<string,array<string,array<string,mixed>>>
433 */
434 public static function filter_checkout_fields(array $fields): array
435 {
436 return self::tune_checkout_fields($fields, self::$cached_config, self::$cached_extra);
437 }
438
439 /**
440 * Adjust checkout fields based on widget settings (Pro).
441 *
442 * @param array<string,array<string,array<string,mixed>>> $fields WC checkout fields.
443 * @param array<int,array<string,mixed>> $config Configured existing fields.
444 * @param array<int,array<string,mixed>> $extra Extra fields to inject.
445 *
446 * @return array<string,array<string,array<string,mixed>>>
447 */
448 public static function tune_checkout_fields(array $fields, array $config, array $extra): array
449 {
450 foreach ($config as $item) {
451 $section = $item['section'] ?? '';
452 $key = $item['field_key'] ?? '';
453 if (empty($section) || empty($key) || empty($fields[$section][$key])) {
454 continue;
455 }
456 // Country-based visibility.
457 $mode = $item['visibility_mode'] ?? 'all';
458 $countries = !empty($item['countries']) && is_array($item['countries']) ? array_filter(array_map('sanitize_text_field', $item['countries'])) : [];
459 if ('all' !== $mode && !empty($countries) && function_exists('WC')) {
460 $customer = WC()->customer;
461 $current_country = $customer ? $customer->get_shipping_country() : '';
462 if (empty($current_country) && $customer) {
463 $current_country = $customer->get_billing_country();
464 }
465 if ('show' === $mode && !in_array($current_country, $countries, true)) {
466 unset($fields[$section][$key]);
467 continue;
468 }
469 if ('hide' === $mode && in_array($current_country, $countries, true)) {
470 unset($fields[$section][$key]);
471 continue;
472 }
473 }
474 if (!empty($item['hide']) && 'yes' === $item['hide']) {
475 unset($fields[$section][$key]);
476 continue;
477 }
478 if (isset($item['label']) && $item['label'] !== '') {
479 $fields[$section][$key]['label'] = sanitize_text_field($item['label']);
480 }
481 if (isset($item['placeholder']) && $item['placeholder'] !== '') {
482 $fields[$section][$key]['placeholder'] = sanitize_text_field($item['placeholder']);
483 } elseif (empty($fields[$section][$key]['placeholder']) && !empty($fields[$section][$key]['label'])) {
484 $fields[$section][$key]['placeholder'] = $fields[$section][$key]['label'];
485 }
486 if (isset($item['help']) && $item['help'] !== '') {
487 $fields[$section][$key]['description'] = sanitize_text_field($item['help']);
488 }
489 if (isset($item['required'])) {
490 $fields[$section][$key]['required'] = ('yes' === $item['required']);
491 }
492 if (isset($item['priority']) && '' !== $item['priority']) {
493 $fields[$section][$key]['priority'] = (int) $item['priority'];
494 }
495 }
496
497 foreach ($extra as $item) {
498 $section = $item['section'] ?? 'order';
499 $key = $item['field_key'] ?? '';
500 if (empty($key)) {
501 continue;
502 }
503 $type = ('textarea' === ($item['type'] ?? 'text')) ? 'textarea' : 'text';
504 $label = isset($item['label']) ? sanitize_text_field($item['label']) : '';
505 $placeholder = isset($item['placeholder']) ? sanitize_text_field($item['placeholder']) : '';
506 $required = ('yes' === ($item['required'] ?? ''));
507 $priority = isset($item['priority']) ? (int) $item['priority'] : 80;
508
509 $fields[$section][$key] = [
510 'type' => $type,
511 'label' => $label,
512 'placeholder' => $placeholder,
513 'required' => $required,
514 'priority' => $priority,
515 ];
516 }
517
518 return $fields;
519 }
520
521 /**
522 * Save extra checkout fields to order meta.
523 *
524 * @param int $order_id Order ID.
525 * @param array $data Posted data.
526 *
527 * @return void
528 */
529 public static function save_extra_fields(int $order_id, array $data): void
530 {
531 $order = wc_get_order($order_id);
532 if (!$order) {
533 return;
534 }
535
536 $posted = $_POST ?? []; // phpcs:ignore WordPress.Security.NonceVerification.Missing
537
538 $types = [];
539 foreach (self::$cached_extra as $item) {
540 if (!empty($item['field_key'])) {
541 $types[$item['field_key']] = $item['type'] ?? 'text';
542 }
543 }
544
545 foreach ($types as $extra_key => $type) {
546 if (!isset($posted[$extra_key])) {
547 continue;
548 }
549 $raw = $posted[$extra_key];
550 if (is_array($raw)) {
551 $clean = implode(', ', array_map('sanitize_text_field', $raw));
552 } else {
553 $clean = ('textarea' === $type) ? sanitize_textarea_field((string) $raw) : sanitize_text_field((string) $raw);
554 }
555 $order->update_meta_data('_ka_' . sanitize_key((string) $extra_key), $clean);
556 }
557 $order->save();
558 }
559 }
560
561
562
563
564
565
566