PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.4
1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk 1.2.0 All 47 releases
fluent-cart / api / StoreSettings.php

StoreSettings.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.4, at api/StoreSettings.php

1,797 lines 92.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\Api;
4
5 use FluentCart\App\App;
6 use FluentCart\App\Vite;
7 use FluentCart\App\CPT\Pages;
8 use FluentCart\App\Helpers\AddressHelper;
9 use FluentCart\App\Helpers\CurrenciesHelper;
10 use FluentCart\App\Services\OrderService;
11 use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager;
12 use FluentCart\App\Modules\StoreManagedRenewal\Services\RenewalService;
13 use FluentCart\App\Modules\Subscriptions\Services\SubscriptionManagementMode;
14 use FluentCart\Framework\Support\Arr;
15 use FluentCart\Framework\Support\ArrayableInterface;
16 use FluentCart\Framework\Support\Str;
17 use FluentCart\App\Services\Permission\PermissionManager;
18
19 class StoreSettings implements ArrayableInterface
20 {
21 const CACHE_KEY = 'store_settings';
22 const CACHE_GROUP = 'fluentcart';
23
24 /**
25 * @var string
26 *
27 * Store settings option name
28 */
29 protected string $optionKey = 'fluent_cart_store_settings';
30
31 /**
32 * @var array key value pair
33 *
34 * Store settings parsed from fields
35 */
36 protected array $storeSettings;
37
38 protected static $cachedStoreSettings = null;
39
40 public static function clearCache(): void
41 {
42 self::$cachedStoreSettings = null;
43 wp_cache_delete(self::CACHE_KEY, self::CACHE_GROUP);
44 }
45
46 public function __construct()
47 {
48 if (self::$cachedStoreSettings !== null) {
49 $this->storeSettings = self::$cachedStoreSettings;
50 return;
51 }
52 $defaultSettings = $this->getDefaultSettings();
53 $storeSettings = get_option($this->optionKey, []);
54 $settings = wp_parse_args($storeSettings, $defaultSettings);
55 $this->storeSettings = $settings;
56 self::$cachedStoreSettings = $this->storeSettings;
57 }
58
59 protected function getDefaultSettings(): array
60 {
61 $defaultSettings = [
62 'store_name' => get_bloginfo('name'),
63 'company_name' => '',
64 'legal_registration_id' => '',
65 'seller_vat_id' => '',
66 'seller_tax_id' => '',
67 'note_for_user_account_creation' => __('An user account will be created', 'fluent-cart'),
68 'checkout_button_text' => __('Checkout', 'fluent-cart'),
69 'view_cart_button_text' => __('View Cart', 'fluent-cart'),
70 'cart_button_text' => __('Add To Cart', 'fluent-cart'),
71 'popup_button_text' => __('View Product', 'fluent-cart'),
72 'out_of_stock_button_text' => __('Not Available', 'fluent-cart'),
73 'currency_position' => 'before',
74 // 'thousand_separator' => 'comma',
75 'decimal_separator' => 'dot',
76 'checkout_method_style' => 'logo',
77 'enable_modal_checkout' => 'no',
78 'require_logged_in' => 'no',
79 'show_cart_icon_in_nav' => 'no',
80 'show_cart_icon_in_body' => 'yes',
81 'additional_address_field' => 'yes',
82 'hide_coupon_field' => 'no',
83 'user_account_creation_mode' => 'all',
84 'checkout_page_id' => '',
85 'custom_payment_page_id' => '',
86 'registration_page_id' => '',
87 'login_page_id' => '',
88 'cart_page_id' => '',
89 'receipt_page_id' => '',
90 'shop_page_id' => '',
91 'customer_profile_page_id' => '',
92 'customer_profile_page_slug' => '',
93 'currency' => 'USD',
94 'store_address1' => '',
95 'store_address2' => '',
96 'store_city' => '',
97 'store_country' => '',
98 'store_postcode' => '',
99 'store_state' => '',
100 'show_relevant_product_in_single_page' => 'yes',
101 'show_relevant_product_in_modal' => '',
102 'order_mode' => 'test',
103 'subscription_mode_guard' => 'yes',
104 'variation_view' => 'both',
105 'variation_columns' => 'masonry',
106 'enable_early_payment_for_installment' => 'yes',
107 'subscription_management_mode' => 'gateway_managed',
108 'subscription_system_charge' => 'no',
109 'modules_settings' => [],
110 'min_receipt_number' => '1',
111 'inv_prefix' => 'INV-',
112 'weight_unit' => 'kg',
113 'dimension_unit' => 'cm'
114 ];
115
116 return apply_filters('fluent_cart/store_settings/values', $defaultSettings, []);
117 }
118
119 /**
120 * @return array
121 *
122 * Get all store settings fields
123 * @hook to use apply_filters("fluent_cart/store_setting_fields", $fields)
124 */
125 public function fields($params = []): array
126 {
127
128 $pages = Pages::getPages('');
129 $previewLinks = [
130 'shop_page_id' => $this->getShopPage(),
131 'customer_profile_page_id' => $this->getCustomerProfilePage(),
132 'cart_page_id' => $this->getCartPage(),
133 'checkout_page_id' => $this->getCheckoutPage(),
134 'receipt_page_id' => $this->getReceiptPage()
135 ];
136 $isProActive = App::isProActive();
137 $proFeatureIcon = Vite::getAssetUrl('images/crown.svg');
138
139 // Read-only schedule for store-managed subscription renewals. Pulled live
140 // from the same map the scheduler uses, so it stays accurate under the
141 // fluent_cart/renewal/advance_creation_days filter. No editable knob — the timing is
142 // deliberately built-in; developers tune it via that filter.
143 $invoiceScheduleMap = RenewalService::getAdvanceCreationDaysMap();
144 $invoiceScheduleLabels = [
145 'daily' => __('Daily', 'fluent-cart'),
146 'weekly' => __('Weekly', 'fluent-cart'),
147 'monthly' => __('Monthly', 'fluent-cart'),
148 'quarterly' => __('Quarterly', 'fluent-cart'),
149 'half_yearly' => __('Half-yearly', 'fluent-cart'),
150 'yearly' => __('Yearly', 'fluent-cart'),
151 ];
152 // Structured renewal-order schedule for the SubscriptionModeManager
153 // component (status card + guarded edit dialog).
154 $invoiceScheduleList = [];
155 foreach ($invoiceScheduleLabels as $invoiceScheduleKey => $invoiceScheduleLabel) {
156 if (!isset($invoiceScheduleMap[$invoiceScheduleKey])) {
157 continue;
158 }
159 $invoiceScheduleDays = (int) $invoiceScheduleMap[$invoiceScheduleKey];
160 $invoiceScheduleList[] = [
161 'label' => $invoiceScheduleLabel,
162 'when' => $invoiceScheduleDays <= 0
163 ? __('on the due date', 'fluent-cart')
164 /* translators: %d: number of days before the due date */
165 : sprintf(_n('%d day before due date', '%d days before due date', $invoiceScheduleDays, 'fluent-cart'), $invoiceScheduleDays),
166 ];
167 }
168
169 // Gateways declaring `system_subscription` — the only ones
170 // subscription_system_charge can ever auto-charge.
171 $systemChargeGateways = [];
172 foreach (GatewayManager::getInstance()->all() as $systemChargeGateway) {
173 if (!$systemChargeGateway->has('system_subscription') || $systemChargeGateway->isUpcoming()) {
174 continue;
175 }
176 $systemChargeGatewayMeta = $systemChargeGateway->getMeta();
177 $systemChargeGateways[] = [
178 'label' => Arr::get($systemChargeGatewayMeta, 'admin_title')
179 ?: Arr::get($systemChargeGatewayMeta, 'label')
180 ?: Arr::get($systemChargeGatewayMeta, 'title'),
181 'active' => $systemChargeGateway->isEnabled(),
182 ];
183 }
184
185 $fields = [
186 'setting_tabs' => [
187 'type' => 'section',
188 'disable_nesting' => true,
189 'default_tab' => 'store_setup',
190 'hide_tab_switch' => true,
191 'schema' => [
192 'store_setup' => [
193 'id' => '',
194 'title' => __('Store Setup', 'fluent-cart'),
195 'show_title' => false,
196 'type' => 'section',
197 'disable_nesting' => true,
198 'columns' => [
199 'default' => 1,
200 'md' => 1
201 ],
202 'schema' => [
203 'name_grid' => [
204 'type' => 'grid',
205 'columns' => [
206 'default' => 1,
207 'md' => 3
208 ],
209 'disable_nesting' => true,
210 'schema' => [
211 'label' => [
212 'type' => 'html',
213 'value' => '<span class="setting-label">' . __('Store Name', 'fluent-cart') . '</span>
214 <div class="form-note">' . __('Enter the public name of your online store.', 'fluent-cart') . '</div>'
215 ],
216 "store_name" => [
217 'wrapperClass' => 'col-span-2 flex items-center',
218 "label" => '',
219 "type" => "input",
220 "value" => "",
221 ],
222 ]
223 ],
224
225 'hr' => [
226 'type' => 'html',
227 'value' => '<hr class="settings-divider">'
228 ],
229
230
231 'logo_grid' => [
232 'type' => 'grid',
233 'columns' => [
234 'default' => 1,
235 'md' => 3
236 ],
237 'disable_nesting' => true,
238 'schema' => [
239 'label' => [
240 'type' => 'html',
241 'value' => '<span class="setting-label">' . __('Store Logo', 'fluent-cart') . '</span>
242 <div class="form-note">' . __("Upload your brand's logo. Recommended width: 512 pixels minimum.", 'fluent-cart') . '</div>'
243 ],
244 "store_logo" => [
245 "label" => false,
246 "type" => "media",
247 "value" => "",
248 'multiple' => false,
249 // if `condition_type` not specified then all condition type will be `and`
250
251 // 'conditions' => [
252 // [
253 // 'key' => 'store_name',
254 // 'operator' => '==',
255 // 'value' => [
256 // 'accessor' => 'order_mode'
257 // ]
258 // ],
259 // ],
260
261 ],
262 ]
263 ],
264
265 'hr2' => [
266 'type' => 'html',
267 'value' => '<hr class="settings-divider">'
268 ],
269
270 'mode_grid' => [
271 'type' => 'grid',
272 'columns' => [
273 'default' => 1,
274 'md' => 3
275 ],
276 'disable_nesting' => true,
277 'schema' => [
278 'label' => [
279 'type' => 'html',
280 'value' => '<span class="setting-label">' . __('Store Mode', 'fluent-cart') . '</span>
281 <div class="form-note">' . __("Select your store's operating mode: `Test` for setup, `Live` for real transactions.", 'fluent-cart') . '</div>'
282 ],
283 'order_mode' => [
284 'wrapperClass' => 'col-span-2 flex items-center',
285 "label" => '',
286 "type" => "radio",
287 "options" => [
288 [
289 "label" => __('Live', 'fluent-cart'),
290 "value" => 'live',
291 ],
292 [
293 "label" => __('Test', 'fluent-cart'),
294 "value" => 'test',
295 ],
296 ],
297 "value" => "live"
298 ],
299 ]
300 ],
301
302 'settings_hr' => [
303 'type' => 'html',
304 'value' => '<hr class="settings-divider">'
305 ],
306
307 'address_grid' => [
308 'type' => 'grid',
309 'columns' => [
310 'default' => 1,
311 'md' => 3
312 ],
313 'disable_nesting' => true,
314 'schema' => [
315 'label' => [
316 'type' => 'html',
317 'value' => '<span class="setting-label">' . __('Store Address', 'fluent-cart') . '</span>
318 <div class="form-note">' . __("Provide your physical business address details.", 'fluent-cart') . '</div>'
319 ],
320 'address_input_grid' => [
321 'wrapperClass' => 'fct-compact-form',
322 'type' => 'grid',
323 'disable_nesting' => true,
324 'class' => 'col-span-2',
325 'schema' => [
326 'store_address_component' => [
327 'type' => 'component',
328 'component' => 'StoreSettings/AddressComponent',
329 'wrapperClass' => 'col-span-full'
330 ],
331 'store_address1' => [
332 'type' => 'hidden',
333 ],
334 'store_address2' => [
335 'type' => 'hidden',
336 ],
337 'store_city' => [
338 'type' => 'hidden',
339 ],
340 'store_postcode' => [
341 'type' => 'hidden',
342 ],
343 'store_country' => [
344 'type' => 'hidden',
345 ],
346 'store_state' => [
347 'type' => 'hidden',
348 ],
349 ]
350 ],
351 ]
352 ],
353
354
355 'settings_hr_2' => [
356 'type' => 'html',
357 'value' => '<hr class="settings-divider">'
358 ],
359
360 'business_details_grid' => [
361 'type' => 'grid',
362 'id' => 'business_details',
363 'columns' => [
364 'default' => 1,
365 'md' => 3
366 ],
367 'disable_nesting' => true,
368 'schema' => [
369 'label' => [
370 'type' => 'html',
371 'value' => '<span class="setting-label">' . __('Business Details', 'fluent-cart') . '</span>
372 <div class="form-note">' . __('Add your legal business identity details used for store records and compliance.', 'fluent-cart') . '</div>'
373 ],
374 'business_details_fields' => [
375 'type' => 'grid',
376 'columns' => [
377 'default' => 1,
378 'md' => 2
379 ],
380 'disable_nesting' => true,
381 'wrapperClass' => 'col-span-2',
382 'schema' => [
383 'company_name' => [
384 'label' => __('Company Name', 'fluent-cart'),
385 'type' => 'input',
386 'value' => '',
387 ],
388 'legal_registration_id' => [
389 'label' => __('Legal Registration ID', 'fluent-cart'),
390 'type' => 'input',
391 'value' => '',
392 ],
393 'seller_vat_id' => [
394 'label' => __('Seller VAT ID', 'fluent-cart'),
395 'type' => 'input',
396 'value' => '',
397 ],
398 'seller_tax_id' => [
399 'label' => __('Seller Tax ID', 'fluent-cart'),
400 'type' => 'input',
401 'value' => '',
402 ],
403 ]
404 ],
405 ]
406 ],
407
408 'settings_hr_3' => [
409 'type' => 'html',
410 'value' => '<hr class="settings-divider">'
411 ],
412
413 'currency_grid' => [
414 'type' => 'grid',
415 'columns' => [
416 'default' => 1,
417 'md' => 3
418 ],
419 'disable_nesting' => true,
420 'schema' => [
421 'label' => [
422 'type' => 'html',
423 'value' => '<span class="setting-label">' . __('Checkout Currency', 'fluent-cart') . '</span>
424 <div class="form-note">' . __("Select the primary currency for your store.", 'fluent-cart') . '</div>'
425 ],
426 "currency" => [
427 'wrapperClass' => 'col-span-2 flex items-center',
428 "label" => '',
429 "type" => "select",
430 'filterable' => true,
431 "options" => CurrencySettings::getFormattedCurrencies(),
432 "value" => "USD"
433 ],
434 ]
435 ],
436
437 'hr3' => [
438 'type' => 'html',
439 'value' => '<hr class="settings-divider">'
440 ],
441
442 'decimal_separator_grid' => [
443 'type' => 'grid',
444 'columns' => [
445 'default' => 1,
446 'md' => 3
447 ],
448 'disable_nesting' => true,
449 'schema' => [
450 'label' => [
451 'type' => 'html',
452 'value' => '<span class="setting-label">' . __('Number Format', 'fluent-cart') . '</span>
453 <div class="form-note">' . __("Select the character used to separate thousands or decimals in prices.", 'fluent-cart') . '</div>'
454 ],
455 'decimal_separator' => [
456 'wrapperClass' => 'col-span-2 flex items-start flex-col',
457 "label" => '',
458 "type" => "radio",
459 "options" => [
460 [
461 "label" => __('Comma & Dot (eg 10,000.00)', 'fluent-cart'),
462 "value" => 'dot'
463 ],
464 [
465 "label" => __('Dot & Comma (eg 10.000,00)', 'fluent-cart'),
466 "value" => 'comma'
467 ],
468 ],
469 "value" => "dot"
470 ],
471 ]
472 ],
473
474 'hr4' => [
475 'type' => 'html',
476 'value' => '<hr class="settings-divider">'
477 ],
478
479 'currency_position_grid' => [
480 'type' => 'grid',
481 'columns' => [
482 'default' => 1,
483 'md' => 3
484 ],
485 'disable_nesting' => true,
486 'schema' => [
487 'label' => [
488 'type' => 'html',
489 'value' => '<span class="setting-label">' . __('Currency Formatting', 'fluent-cart') . '</span>
490 <div class="form-note">' . __("Select how the currency should be formatted.", 'fluent-cart') . '</div>'
491 ],
492 'currency_position' => [
493 'wrapperClass' => 'col-span-2 flex items-center',
494 "label" => '',
495 "type" => "select",
496 "options" => [
497 [
498 "label" => __('Symbol before (eg: $100)', 'fluent-cart'),
499 "value" => 'before',
500 ],
501 [
502 "label" => __('Symbol after (eg: 100$)', 'fluent-cart'),
503 "value" => 'after',
504 ],
505 [
506 "label" => __('ISO before (eg: USD 100)', 'fluent-cart'),
507 "value" => 'iso_before',
508 ],
509 [
510 "label" => __('ISO after (eg: 100 USD)', 'fluent-cart'),
511 "value" => 'iso_after',
512 ],
513 [
514 "label" => __('Symbol & ISO (eg: $100 USD)', 'fluent-cart'),
515 "value" => 'symbool_before_iso',
516 ],
517 [
518 "label" => __('ISO & Symbol (eg: USD 100$)', 'fluent-cart'),
519 "value" => 'symbool_after_iso',
520 ],
521 [
522 "label" => __('ISO-Symbol (eg: USD $100)', 'fluent-cart'),
523 "value" => 'symbool_and_iso',
524 ],
525 ],
526 "value" => "before"
527 ],
528 ]
529 ],
530
531 'hr5' => [
532 'type' => 'html',
533 'value' => '<hr class="settings-divider">'
534 ],
535
536
537 'checkout_style_grid' => [
538 'type' => 'grid',
539 'columns' => [
540 'default' => 1,
541 'md' => 3
542 ],
543 'disable_nesting' => true,
544 'schema' => [
545 'label' => [
546 'type' => 'html',
547 'value' => '<span class="setting-label">' . __('Payment View', 'fluent-cart') . '</span>
548 <div class="form-note">' . __("Select how payment options are visually presented on checkout.", 'fluent-cart') . '</div>'
549 ],
550 "checkout_method_style" => [
551 'wrapperClass' => 'col-span-2 flex items-center',
552 "type" => "component",
553 'component' => 'PaymentView',
554 'label' => false,
555 "options" => [
556 [
557 "label" => '',
558 "value" => 'logo',
559 ],
560 [
561 "label" => __('Label Selector', 'fluent-cart'),
562 "value" => 'radio',
563 ],
564 ],
565 "value" => "logo"
566 ],
567 ]
568 ],
569
570 // 'settings_hr_modal' => [
571 // 'type' => 'html',
572 // 'value' => '<hr class="settings-divider">'
573 // ],
574 //
575 // 'modal_checkout_grid' => [
576 // 'type' => 'grid',
577 // 'columns' => [
578 // 'default' => 1,
579 // 'md' => 3
580 // ],
581 // 'disable_nesting' => true,
582 // 'schema' => [
583 // 'label' => [
584 // 'type' => 'html',
585 // 'value' => '<span class="setting-label">' . __('Buy Now Button Behavior', 'fluent-cart') . '</span>
586 // <div class="form-note">' . __("Choose how the Buy Now button behaves. Modal checkout provides a seamless experience without leaving the product page.", 'fluent-cart') . '</div>'
587 // ],
588 // "enable_modal_checkout" => [
589 // 'wrapperClass' => 'col-span-2 flex items-center',
590 // "label" => '',
591 // "type" => "radio",
592 // "options" => [
593 // [
594 // "label" => __('Redirect to Checkout Page', 'fluent-cart'),
595 // "value" => 'no',
596 // ],
597 // [
598 // "label" => __('Open Checkout in Modal', 'fluent-cart'),
599 // "value" => 'yes',
600 // ],
601 // ],
602 // "value" => "no"
603 // ],
604 // ]
605 // ],
606 ],
607 ],
608 // 'button_setup' => [
609 // 'title' => __('Button Setup', 'fluent-cart'),
610 // 'type' => 'tab-pane',
611 // 'disable_nesting' => true,
612 // 'schema' => [
613 // 'button_setup_settings' => [
614 // 'title' => __('Button Setup', 'fluent-cart'),
615 // 'type' => 'section',
616 // 'disable_nesting' => true,
617 // 'columns' => [
618 // 'default' => 1,
619 // 'md' => 2
620 // ],
621 // 'schema' => [
622 // "checkout_button_text" => [
623 // "label" => __('Checkout Button Text', 'fluent-cart'),
624 // "type" => "input",
625 // "value" => __('Buy now', 'fluent-cart')
626 // ],
627 // "cart_button_text" => [
628 // "label" => __('Cart Button Text', 'fluent-cart'),
629 // "type" => "input",
630 // "value" => __('Add to cart', 'fluent-cart')
631 // ],
632 //
633 // "popup_button_text" => [
634 // "label" => __('Popup Button Text', 'fluent-cart'),
635 // "type" => "input",
636 // "value" => __('View Product', 'fluent-cart')
637 // ],
638 // "out_of_stock_button_text" => [
639 // "label" => __('Out of Stock Button Text', 'fluent-cart'),
640 // "type" => "input",
641 // "value" => __('Out of stock', 'fluent-cart')
642 // ],
643 // "view_cart_button_text" => [
644 // "label" => __('View Cart Button Text', 'fluent-cart'),
645 // "type" => "input",
646 // "value" => __('View Cart', 'fluent-cart')
647 // ],
648 //
649 // "note_for_user_account_creation" => [
650 // "label" => __('Note for User Account Creation,', 'fluent-cart'),
651 // "type" => "input",
652 // "value" => __('You have subscription product in your cart, an account will be created', 'fluent-cart')
653 // ],
654 // ],
655 // ]
656 // ]
657 // ],
658 'pages_setup' => [
659 'id' => '',
660 'title' => __('Pages Setup', 'fluent-cart'),
661 'show_title' => false,
662 'type' => 'section',
663 'disable_nesting' => true,
664 'columns' => [
665 'default' => 1,
666 'md' => 1
667 ],
668 'schema' => [
669
670 'shop_page_grid' => [
671 'type' => 'grid',
672 'columns' => [
673 'default' => 1,
674 'md' => 3,
675 ],
676 'disable_nesting' => true,
677 'schema' => [
678 'label' => [
679 'type' => 'html',
680 'value' => '<span class="setting-label">' . __('Select Shop Page', 'fluent-cart') . '</span>
681 <div class="form-note">' . __("Select the page that showcases all of your products.", 'fluent-cart') . '</div>'
682 ],
683 'shop_page_id' => [
684 'wrapperClass' => 'col-span-2',
685 'page_title' => __('Shop', 'fluent-cart'),
686 'type' => 'component',
687 'component' => 'StoreSettings/PageSelector',
688 'page_key' => 'shop_page_id',
689 'preview_link' => $previewLinks['shop_page_id'],
690 'options' => $pages,
691 'hide_note' => true,
692 'value' => '',
693 'note' => \FluentCart\App\Helpers\Helper::getShortcodeInstructionString(
694 '[fluent_cart_products]',
695 __('Products', 'fluent-cart')
696 ),
697 ],
698 ]
699 ],
700
701 'hr1' => [
702 'type' => 'html',
703 'value' => '<hr class="settings-divider">'
704 ],
705
706 'customer_profile_grid' => [
707 'type' => 'grid',
708 'columns' => [
709 'default' => 1,
710 'md' => 3
711 ],
712 'disable_nesting' => true,
713 'schema' => [
714 'label' => [
715 'type' => 'html',
716 'value' => '<span class="setting-label">' . __('Select Customer Profile Page', 'fluent-cart') . '</span>
717 <div class="form-note">' . __("Select the page where customers will manage their profile, orders, and downloads.", 'fluent-cart') . '</div>'
718 ],
719 'customer_profile_page_id' => [
720 'wrapperClass' => 'col-span-2',
721 'label' => false,
722 'page_title' => __('Account', 'fluent-cart'),
723 'type' => 'component',
724 'component' => 'StoreSettings/PageSelector',
725 'page_key' => 'customer_profile_page_id',
726 'preview_link' => $previewLinks['customer_profile_page_id'],
727 'hide_note' => true,
728 'options' => $pages,
729 'value' => '',
730 'note' => \FluentCart\App\Helpers\Helper::getShortcodeInstructionString(
731 '[fluent_cart_customer_profile]',
732 __('Account', 'fluent-cart')
733 ),
734 ],
735 ]
736 ],
737
738 'hr2' => [
739 'type' => 'html',
740 'value' => '<hr class="settings-divider">'
741 ],
742
743 'cart_page_grid' => [
744 'type' => 'grid',
745 'columns' => [
746 'default' => 1,
747 'md' => 3
748 ],
749 'disable_nesting' => true,
750 'schema' => [
751 'label' => [
752 'type' => 'html',
753 'value' => '<span class="setting-label">' . __('Select Cart Page', 'fluent-cart') . '</span>
754 <div class="form-note">' . __("Select the page that will display customer's current shopping cart.", 'fluent-cart') . '</div>'
755 ],
756 'cart_page_id' => [
757 'wrapperClass' => 'col-span-2',
758 'label' => false,
759 'page_title' => __('Cart', 'fluent-cart'),
760 'type' => 'component',
761 'component' => 'StoreSettings/PageSelector',
762 'page_key' => 'cart_page_id',
763 'preview_link' => $previewLinks['cart_page_id'],
764 'hide_note' => true,
765 'options' => $pages,
766 'value' => '',
767 'note' => \FluentCart\App\Helpers\Helper::getShortcodeInstructionString(
768 '[fluent_cart_cart]',
769 __('Cart', 'fluent-cart')
770 ),
771 ],
772 ]
773 ],
774
775 'hr3' => [
776 'type' => 'html',
777 'value' => '<hr class="settings-divider">'
778 ],
779
780 'receipt_page_grid' => [
781 'type' => 'grid',
782 'columns' => [
783 'default' => 1,
784 'md' => 3
785 ],
786 'disable_nesting' => true,
787 'schema' => [
788 'label' => [
789 'type' => 'html',
790 'value' => '<span class="setting-label">' . __('Select Receipt Page', 'fluent-cart') . '</span>
791 <div class="form-note">' . __("Select the page that will display order summary after a successful purchase.", 'fluent-cart') . '</div>'
792 ],
793 'receipt_page_id' => [
794 'wrapperClass' => 'col-span-2',
795 'label' => false,
796 'page_title' => __('Receipt', 'fluent-cart'),
797 'type' => 'component',
798 'component' => 'StoreSettings/PageSelector',
799 'page_key' => 'receipt_page_id',
800 'preview_link' => $previewLinks['receipt_page_id'],
801 'hide_note' => true,
802 'options' => $pages,
803 'value' => '',
804 'note' => \FluentCart\App\Helpers\Helper::getShortcodeInstructionString(
805 '[fluent_cart_receipt]',
806 __('Receipt', 'fluent-cart')
807 ),
808 ],
809 ]
810 ],
811
812 'hr4' => [
813 'type' => 'html',
814 'value' => '<hr class="settings-divider">'
815 ],
816
817 'checkout_page_grid' => [
818 'type' => 'grid',
819 'columns' => [
820 'default' => 1,
821 'md' => 3
822 ],
823 'disable_nesting' => true,
824 'schema' => [
825 'label' => [
826 'type' => 'html',
827 'value' => '<span class="setting-label">' . __('Select Checkout Page', 'fluent-cart') . '</span>
828 <div class="form-note">' . __("Select the page where customers will finalize their purchase.", 'fluent-cart') . '</div>'
829 ],
830 'checkout_page_id' => [
831 'wrapperClass' => 'col-span-2',
832 'label' => false,
833 'page_title' => __('Checkout', 'fluent-cart'),
834 'type' => 'component',
835 'component' => 'StoreSettings/PageSelector',
836 'page_key' => 'checkout_page_id',
837 'preview_link' => $previewLinks['checkout_page_id'],
838 'hide_note' => true,
839 'options' => $pages,
840 'value' => '',
841 'note' => \FluentCart\App\Helpers\Helper::getShortcodeInstructionString(
842 '[fluent_cart_checkout]',
843 __('Checkout', 'fluent-cart')
844 ),
845 ],
846 ]
847 ]
848 ]
849 ],
850 'single_product_setup' => [
851 'title' => __('Product Page', 'fluent-cart'),
852 'show_title' => false,
853 'type' => 'section',
854 'disable_nesting' => true,
855 'columns' => [
856 'default' => 1,
857 'md' => 1
858 ],
859 'schema' => [
860 'product_settings_grid' => [
861 'type' => 'grid',
862 'columns' => [
863 'default' => 1,
864 'md' => 3
865 ],
866 'disable_nesting' => true,
867 'schema' => [
868 'label' => [
869 'type' => 'html',
870 'value' => '<span class="setting-label">' . __('Single Product Setup', 'fluent-cart') . '</span>
871 <div class="form-note">' . __("Control the display of relevant information.", 'fluent-cart') . '</div>'
872 ],
873 'fields' => [
874 'type' => 'grid',
875 'columns' => [
876 'default' => 1,
877 'md' => 1
878 ],
879 'disable_nesting' => true,
880 'schema' => [
881 "show_relevant_product_in_single_page" => [
882 "label" => __('Show Relevant In Single Page', 'fluent-cart'),
883 "type" => "checkbox",
884 "value" => "yes"
885 ],
886 "show_relevant_product_in_modal" => [
887 "label" => __('Show Relevant In Product Modal', 'fluent-cart'),
888 "type" => "checkbox",
889 "value" => "no"
890 ],
891 ]
892 ]
893
894 ]
895 ],
896 'hr' => [
897 'type' => 'html',
898 'value' => '<hr class="settings-divider">'
899 ],
900
901 'image_zoom_grid' => [
902 'type' => 'grid',
903 'columns' => [
904 'default' => 1,
905 'md' => 3
906 ],
907 'disable_nesting' => true,
908 'schema' => [
909 'label' => [
910 'type' => 'html',
911 'value' => '<span class="setting-label">' . __('Image Zooming', 'fluent-cart') . '</span>
912 <div class="form-note">' . __("Enable Image zoom in single product.", 'fluent-cart') . '</div>'
913 ],
914 'fields' => [
915 'type' => 'grid',
916 'columns' => [
917 'default' => 1,
918 'md' => 1
919 ],
920 'disable_nesting' => true,
921 'schema' => [
922 "enable_image_zoom_in_single_product" => [
923 "label" => __('Enable Zoom in Single Product', 'fluent-cart'),
924 "type" => "checkbox",
925 "value" => "no"
926 ],
927 "enable_image_zoom_in_modal" => [
928 "label" => __('Enable Zoom in Modal', 'fluent-cart'),
929 "type" => "checkbox",
930 "value" => "no"
931 ],
932 ]
933 ]
934
935 ]
936 ],
937
938 'hr_image_zoom' => [
939 'type' => 'html',
940 'value' => '<hr class="settings-divider">'
941 ],
942
943 'variation_grid' => [
944 'type' => 'grid',
945 'columns' => [
946 'default' => 1,
947 'md' => 3
948 ],
949 'disable_nesting' => true,
950 'schema' => [
951 'label' => [
952 'type' => 'html',
953 'value' => '<span class="setting-label">' . __('Variation View', 'fluent-cart') . '</span>
954 <div class="form-note">' . __("Defines how product variations are visually presented to customers.", 'fluent-cart') . '</div>'
955 ],
956 "variation_view" => [
957 'wrapperClass' => 'col-span-2 flex items-center',
958 "label" => false,
959 "type" => "select",
960 "options" => [
961 [
962 "label" => __('Image', 'fluent-cart'),
963 "value" => 'image',
964 ],
965 [
966 "label" => __('Text', 'fluent-cart'),
967 "value" => 'text',
968 ],
969 [
970 "label" => __('Image with Text', 'fluent-cart'),
971 "value" => 'both',
972 ],
973 ],
974 "value" => ""
975 ],
976
977 ]
978 ],
979 'hr2' => [
980 'type' => 'html',
981 'value' => '<hr class="settings-divider">'
982 ],
983 'variation_column_grid' => [
984 'type' => 'grid',
985 'columns' => [
986 'default' => 1,
987 'md' => 3
988 ],
989 'disable_nesting' => true,
990 'schema' => [
991 'label' => [
992 'type' => 'html',
993 'value' => '<span class="setting-label">' . __('Variation Columns', 'fluent-cart') . '</span>
994 <div class="form-note">' . __("Set the column layout for how product variations are displayed within product sections.", 'fluent-cart') . '</div>'
995 ],
996 "variation_columns" => [
997 'wrapperClass' => 'col-span-2 flex items-center',
998 "label" => false,
999 "type" => "select",
1000 "options" => [
1001 [
1002 "label" => __('One Column', 'fluent-cart'),
1003 "value" => 'one',
1004 ],
1005 [
1006 "label" => __('Two Columns', 'fluent-cart'),
1007 "value" => 'two',
1008 ],
1009 [
1010 "label" => __('Three Columns', 'fluent-cart'),
1011 "value" => 'three',
1012 ],
1013 [
1014 "label" => __('Four Columns', 'fluent-cart'),
1015 "value" => 'four',
1016 ],
1017 [
1018 "label" => __('Masonry', 'fluent-cart'),
1019 "value" => 'masonry',
1020 ],
1021 ],
1022 "value" => ""
1023 ],
1024
1025 ]
1026 ],
1027
1028 'hr3' => [
1029 'type' => 'html',
1030 'value' => '<hr class="settings-divider">'
1031 ],
1032 'product_slug_grid' => [
1033 'type' => 'grid',
1034 'columns' => [
1035 'default' => 1,
1036 'md' => 3
1037 ],
1038 'disable_nesting' => true,
1039 'schema' => [
1040 'label' => [
1041 'type' => 'html',
1042 'value' => '<span class="setting-label">' . __('Product Slug', 'fluent-cart') . '</span>
1043 <div class="form-note">' . __("Set Product Slug.", 'fluent-cart') . '</div>'
1044 ],
1045 "product_slug" => [
1046 'wrapperClass' => 'col-span-2 flex items-center',
1047 "label" => false,
1048 "type" => "input",
1049 "value" => ""
1050 ],
1051
1052 ]
1053 ],
1054 ],
1055 ],
1056 'cart_and_checkout' => [
1057 'title' => __('Cart & checkout', 'fluent-cart'),
1058 'show_title' => false,
1059 'type' => 'section',
1060 'disable_nesting' => true,
1061 'columns' => [
1062 'default' => 1,
1063 'md' => 1
1064 ],
1065 'schema' => [
1066 "show_cart_icon_in_body" => [
1067 "label" => __('Cart Icon In Body', 'fluent-cart'),
1068 "type" => "checkbox",
1069 "value" => "yes",
1070 'note' => sprintf(
1071 /* translators: %1$s: Display instruction text, %2$s: "Use this" text, %3$s: Copy to clipboard tooltip, %4$s: CSS class name, %5$s: CSS class description text */
1072 "<div class='pl-6'><p>%1\$s</p><p>%2\$s <code class='copyable-content' title='%3\$s'>%4\$s</code> %5\$s</p></div>",
1073 __('Display the cart icon in the body of the website', 'fluent-cart'),
1074 __('Use this', 'fluent-cart'),
1075 __('Copy to clipboard', 'fluent-cart'),
1076 'fcart-cart-toggle-button',
1077 __('CSS class to display the cart link/icon anywhere else.', 'fluent-cart')
1078 )
1079 ],
1080 'hr2' => [
1081 'type' => 'html',
1082 'value' => '<hr class="settings-divider">'
1083 ],
1084 "require_logged_in" => [
1085 "label" => __('Require user to be logged in for checkout (Coming soon)', 'fluent-cart'),
1086 "type" => "checkbox",
1087 "value" => "no",
1088 'note' => "<div class='pl-6'>" . __('Enforce that customers must be logged into their account to complete a purchase.', 'fluent-cart') . "</div>",
1089 'wrapperClass' => 'disabled'
1090 ],
1091 'hr3' => [
1092 'type' => 'html',
1093 'value' => '<hr class="settings-divider">'
1094 ],
1095
1096 'user_account_creation_mode_grid' => [
1097 'type' => 'grid',
1098 'columns' => [
1099 'default' => 1,
1100 'md' => 3
1101 ],
1102 'disable_nesting' => true,
1103 'schema' => [
1104 'label' => [
1105 'type' => 'html',
1106 'value' => '<span class="setting-label">' . __('User Account Creation Mode', 'fluent-cart') . '</span>
1107 <div class="form-note">' . __("User account will be created regardless of the mode if the order has a subscription.", 'fluent-cart') . '</div>'
1108 ],
1109 'user_account_input_grid' => [
1110 'type' => 'grid',
1111 'disable_nesting' => true,
1112 'class' => 'col-span-2',
1113 'schema' => [
1114 'user_account_creation_mode' => [
1115 'wrapperClass' => 'col-span-2 flex items-center',
1116 "label" => '',
1117 "type" => "radio",
1118 "options" => [
1119 [
1120 "label" => __('Create User Account Automatically after payment
1121 ', 'fluent-cart'),
1122 "value" => 'all',
1123 'note' => "<div class='my-[2px] form-note'>" . __('User account will be created automatically after payment.', 'fluent-cart') . "</div>",
1124 'option_class' => 'mb-2.5'
1125 ],
1126 [
1127 "label" => __('Give checkbox to create account on checkout page', 'fluent-cart'),
1128 "value" => 'user_choice',
1129 'note' => "<div class='my-[2px] form-note'>" . __('User will have an option to create an account during checkout.', 'fluent-cart') . "</div>",
1130 'option_class' => 'mb-2.5'
1131 ],
1132 [
1133 "label" => __('No need to create account for onetime purchases', 'fluent-cart'),
1134 "value" => 'only_subscription',
1135 'note' => "<div class='my-[2px] form-note'>" . __('User account will not be created for onetime purchases.', 'fluent-cart') . "</div>",
1136 ],
1137 ],
1138 "value" => "all"
1139 ],
1140 ]
1141 ],
1142 ]
1143 ],
1144
1145
1146 // "allow_checkout_signup" => [
1147 // "label" => __('Allow customers to create account during one-time checkout', 'fluent-cart'),
1148 // "type" => "checkbox",
1149 // "value" => "no",
1150 // 'note' => "<div class='pl-6'>" . __('Provide an option for customers to create a user account during a single, non-recurring checkout process.', 'fluent-cart') . "</div>"
1151 // ],
1152 // "force_ssl" => [
1153 // "label" => __('Force page to SSL (https)', 'fluent-cart'),
1154 // "type" => "checkbox",
1155 // "value" => "no"
1156 // ],
1157 'hr5' => [
1158 'type' => 'html',
1159 'value' => '<hr class="settings-divider">'
1160 ],
1161 "hide_coupon_field" => [
1162 "label" => __('Hide coupon field on checkout', 'fluent-cart'),
1163 "type" => "checkbox",
1164 "value" => "no",
1165 'note' => "<div class='pl-6'>" . __('Hide the coupon code input field on the checkout page.', 'fluent-cart') . "</div>"
1166 ],
1167 'hr6' => [
1168 'type' => 'html',
1169 'value' => '<hr class="settings-divider">'
1170 ],
1171 'order_invoice_grid' => [
1172 'type' => 'grid',
1173 'columns' => [
1174 'default' => 1,
1175 'md' => 3
1176 ],
1177 'disable_nesting' => true,
1178 'schema' => [
1179 'label' => [
1180 'type' => 'html',
1181 'value' => '<span class="setting-label">' . __('Receipt Settings', 'fluent-cart') . '</span>
1182 <div class="form-note">' . __("Setup your receipt.", 'fluent-cart') . '</div>'
1183 ],
1184 'invoice_input_grid' => [
1185 'type' => 'grid',
1186 'disable_nesting' => true,
1187 'class' => 'col-span-2',
1188 'schema' => [
1189 "min_receipt_number" => [
1190 'wrapperClass' => 'col-span-2 flex flex-col',
1191 "label" => __('Minimum Receipt Number', 'fluent-cart'),
1192 "type" => "input",
1193 "value" => "",
1194 'note' => sprintf(
1195 "<div class=''>%s</div>",
1196 sprintf(
1197 /* translators: %s is the next receipt number */
1198 __('Next Receipt Number: %s', 'fluent-cart'),
1199 OrderService::getNextReceiptNumber()
1200 )
1201 ),
1202
1203 ],
1204 "inv_prefix" => [
1205 'wrapperClass' => 'col-span-2 flex flex-col',
1206 "label" => __('Receipt Prefix', 'fluent-cart'),
1207 "type" => "input",
1208 "value" => "",
1209 ],
1210 ]
1211 ],
1212 ]
1213 ],
1214 ]
1215 ],
1216 'subscriptions_setup' => [
1217 'title' => __('Subscriptions', 'fluent-cart'),
1218 'show_title' => false,
1219 'type' => 'section',
1220 'disable_nesting' => true,
1221 'columns' => [
1222 'default' => 1,
1223 'md' => 1
1224 ],
1225 'schema' => [
1226 'subscription_early_payment_grid' => [
1227 'type' => 'grid',
1228 'wrapperClass' => 'items-start mb-6',
1229 'columns' => [
1230 'default' => 1,
1231 'md' => 3
1232 ],
1233 'disable_nesting' => true,
1234 'schema' => [
1235 'label' => [
1236 'type' => 'html',
1237 'value' => '<span class="setting-label">' . __('Early Payment', 'fluent-cart') . (!$isProActive ? ' <img src="' . esc_url($proFeatureIcon) . '" alt="' . esc_attr__('Pro feature', 'fluent-cart') . '" class="pro-feature-icon" style="margin-left: 6px; width: 14px; height: 14px; display: inline-block; vertical-align: text-top;" />' : '') . '</span>
1238 <div class="form-note">' . __('Let customers pay remaining installments before their due date.', 'fluent-cart') . '</div>'
1239 ],
1240 'fields' => [
1241 'type' => 'grid',
1242 'columns' => [
1243 'default' => 1,
1244 'md' => 1
1245 ],
1246 'disable_nesting' => true,
1247 'class' => 'col-span-2',
1248 'schema' => [
1249 'enable_early_payment_for_installment' => [
1250 'label' => __('Enable early payment for installment', 'fluent-cart'),
1251 'type' => 'checkbox',
1252 'value' => 'yes',
1253 'disabled' => !$isProActive,
1254 'wrapperClass' => !$isProActive ? 'disabled' : '',
1255 'note' => !$isProActive
1256 ? "<div class='pl-6'>" . __('This is a FluentCart Pro feature.', 'fluent-cart') . "</div>"
1257 : "<div class='pl-6'>" . __('Allow customers to pay remaining installments early from subscription screens.', 'fluent-cart') . "</div>"
1258 ]
1259 ]
1260 ]
1261 ]
1262 ],
1263 'subscription_management_mode_grid' => [
1264 'type' => 'grid',
1265 'wrapperClass' => 'items-start',
1266 'columns' => [
1267 'default' => 1,
1268 'md' => 3
1269 ],
1270 'disable_nesting' => true,
1271 'schema' => [
1272 'label' => [
1273 'type' => 'html',
1274 'value' => sprintf(
1275 /* translators: 1: setting label, 2: setting description */
1276 '<span class="setting-label">%1$s</span>
1277 <div class="form-note">%2$s</div>',
1278 __('Renewal Billing', 'fluent-cart'),
1279 __('Choose how recurring subscription payments are collected.', 'fluent-cart')
1280 )
1281 ],
1282 'fields' => [
1283 'type' => 'grid',
1284 'columns' => [
1285 'default' => 1,
1286 'md' => 1
1287 ],
1288 'disable_nesting' => true,
1289 'class' => 'col-span-2',
1290 'schema' => [
1291 // Compact, guarded control: shows only the CURRENT mode with a
1292 // short how-it-works summary and a Change button. Editing goes
1293 // through a disclaimer confirm + dialog so the store-wide
1294 // billing decision can never be flipped by a stray click.
1295 'subscription_management_mode' => [
1296 'label' => false,
1297 'component' => 'StoreSettings/SubscriptionModeManager',
1298 'disable_nesting' => true,
1299 'value' => 'gateway_managed',
1300 // Read-only renewal-order schedule, built live from the
1301 // same map the scheduler uses (filterable).
1302 'schedule_items' => $invoiceScheduleList,
1303 // Which gateways auto-charge is actually possible on.
1304 'system_charge_gateways' => $systemChargeGateways,
1305 ],
1306 // Keep these in the form payload — the component above writes
1307 // them; without a schema entry the keys would drop out of the
1308 // saved values.
1309 'subscription_system_charge' => [
1310 'type' => 'hidden',
1311 'value' => 'no',
1312 ],
1313 'subscription_manual_fallback' => [
1314 'type' => 'hidden',
1315 'value' => 'no',
1316 ],
1317 ]
1318 ]
1319 ]
1320 ],
1321 'subscription_mode_guard_grid' => [
1322 'type' => 'grid',
1323 'wrapperClass' => 'items-start mt-6',
1324 'columns' => [
1325 'default' => 1,
1326 'md' => 3
1327 ],
1328 'disable_nesting' => true,
1329 'schema' => [
1330 'label' => [
1331 'type' => 'html',
1332 'value' => sprintf(
1333 /* translators: 1: setting label, 2: setting description */
1334 '<span class="setting-label">%1$s</span>
1335 <div class="form-note">%2$s</div>',
1336 __('Staging Protection', 'fluent-cart'),
1337 __('Prevent staging or test copies of your store from billing real customers.', 'fluent-cart')
1338 )
1339 ],
1340 'fields' => [
1341 'type' => 'grid',
1342 'columns' => [
1343 'default' => 1,
1344 'md' => 1
1345 ],
1346 'disable_nesting' => true,
1347 'class' => 'col-span-2',
1348 'schema' => [
1349 'subscription_mode_guard' => [
1350 'label' => __('Don\'t bill live subscriptions from this site while it is in test mode', 'fluent-cart'),
1351 'type' => 'checkbox',
1352 'value' => 'yes',
1353 'note' => sprintf(
1354 /* translators: 1: the setting's explanatory note text */
1355 "<div class='pl-6'>%1\$s</div>",
1356 __('In test mode, this site won\'t invoice, charge, or email live subscriptions — so a staging copy can never double-bill customers. Billing from this site resumes when it is live again.', 'fluent-cart')
1357 )
1358 ],
1359 ]
1360 ]
1361 ]
1362 ],
1363 ]
1364 ],
1365 ]
1366 ],
1367 ];
1368
1369
1370 // Only show weight/dimension unit fields to users with shipping-sensitive permission
1371 if (PermissionManager::hasPermission(['store/sensitive'])) {
1372 $storeSchema = &$fields['setting_tabs']['schema']['store_setup']['schema'];
1373 $storeSchema['weight_unit_grid'] = [
1374 'type' => 'grid',
1375 'columns' => ['default' => 1, 'md' => 3],
1376 'disable_nesting' => true,
1377 'schema' => [
1378 'label' => [
1379 'type' => 'html',
1380 'value' => '<span class="setting-label">' . __('Weight Unit', 'fluent-cart') . '</span>
1381 <div class="form-note">' . __('Unit used for product weight measurements.', 'fluent-cart') . '</div>'
1382 ],
1383 'weight_unit' => [
1384 'wrapperClass' => 'col-span-2 flex items-center',
1385 'label' => '',
1386 'type' => 'select',
1387 'options' => [
1388 ['label' => __('kg', 'fluent-cart'), 'value' => 'kg'],
1389 ['label' => __('g', 'fluent-cart'), 'value' => 'g'],
1390 ['label' => __('lbs', 'fluent-cart'), 'value' => 'lbs'],
1391 ['label' => __('oz', 'fluent-cart'), 'value' => 'oz'],
1392 ],
1393 'value' => 'kg'
1394 ],
1395 ]
1396 ];
1397 $storeSchema['dimension_unit_grid'] = [
1398 'type' => 'grid',
1399 'columns' => ['default' => 1, 'md' => 3],
1400 'disable_nesting' => true,
1401 'schema' => [
1402 'label' => [
1403 'type' => 'html',
1404 'value' => '<span class="setting-label">' . __('Dimension Unit', 'fluent-cart') . '</span>
1405 <div class="form-note">' . __('Unit used for product dimension measurements.', 'fluent-cart') . '</div>'
1406 ],
1407 'dimension_unit' => [
1408 'wrapperClass' => 'col-span-2 flex items-center',
1409 'label' => '',
1410 'type' => 'select',
1411 'options' => [
1412 ['label' => __('cm', 'fluent-cart'), 'value' => 'cm'],
1413 ['label' => __('mm', 'fluent-cart'), 'value' => 'mm'],
1414 ['label' => __('in', 'fluent-cart'), 'value' => 'in'],
1415 ['label' => __('m', 'fluent-cart'), 'value' => 'm'],
1416 ],
1417 'value' => 'cm'
1418 ],
1419 ]
1420 ];
1421 }
1422
1423 return apply_filters("fluent_cart/store_settings/fields", $fields, []);
1424 }
1425
1426 public function isModuleTabEnabled(): bool
1427 {
1428 $fields = $this->fields();
1429 return isset($fields['setting_tabs']['schema']['modules_tab']);
1430 }
1431
1432 /**
1433 * @param string|null $key like stripe or paypal
1434 * All store settings if key is not provided
1435 * @return mixed
1436 *
1437 */
1438
1439 public function get($key = null, $default = null)
1440 {
1441 if (empty($key)) {
1442 return $this->storeSettings;
1443 }
1444
1445 if (is_array($key)) {
1446 $data = Arr::only($this->storeSettings, $key);
1447 } else {
1448 $data = Arr::get($this->storeSettings, $key);
1449 }
1450
1451 return empty($data) ? $default : $data;
1452 }
1453
1454 public function moduleSettings(): array
1455 {
1456 $settings = $this->get('modules_settings', []);
1457 return Arr::wrap($settings);
1458 }
1459
1460 /**
1461 * @param array $settings like Stripe or PayPal settings array
1462 * Save store settings
1463 * @return array
1464 *
1465 */
1466 public function save(array $settings)
1467 {
1468 $prevSettings = get_option($this->optionKey, []);
1469 $prevSettings = wp_parse_args($prevSettings, $this->getDefaultSettings());
1470
1471 $settings = wp_parse_args($settings, $prevSettings);
1472
1473 $customerProfilePageId = Arr::get($settings, 'customer_profile_page_id', 0);
1474 if ($customerProfilePageId) {
1475 $settings['customer_profile_page_slug'] = $this->getCustomerDashboardPageSlug($customerProfilePageId, false);
1476 }
1477
1478 update_option($this->optionKey, $settings, true);
1479 $this->storeSettings = $settings;
1480 self::$cachedStoreSettings = $this->storeSettings;
1481 wp_cache_delete(self::CACHE_KEY, self::CACHE_GROUP);
1482
1483 $isSlugChanged = Arr::get($prevSettings, 'product_slug') !== Arr::get($settings, 'product_slug');
1484 $isAccountPageChanged = Arr::get($prevSettings, 'customer_profile_page_slug') !== Arr::get($settings, 'customer_profile_page_slug');
1485
1486 if ($isSlugChanged || $isAccountPageChanged) {
1487 flush_rewrite_rules();
1488 delete_option('rewrite_rules');
1489 }
1490
1491 return $this->storeSettings;
1492 }
1493
1494 public function set(string $key, $value)
1495 {
1496 return $this->save([
1497 $key => $value
1498 ]);
1499 }
1500
1501 public function getCurrency()
1502 {
1503 return $this->get('currency', 'USD');
1504 }
1505
1506 public function getCurrencySymbol()
1507 {
1508 $currency = $this->getCurrency();
1509 return CurrenciesHelper::getCurrencySign($currency);
1510 }
1511
1512 public function isCheckoutPage(): bool
1513 {
1514 global $post;
1515 $pageId = $this->getCheckoutPageId();
1516 return intval($pageId) === intval($post->ID);
1517 }
1518
1519 public function getCheckoutPageId()
1520 {
1521 return Arr::get($this->storeSettings, 'checkout_page_id');
1522 }
1523
1524 public function getPagesSettings(): array
1525 {
1526 return Arr::only(
1527 $this->storeSettings,
1528 [
1529 'checkout_page_id',
1530 'custom_payment_page_id',
1531 'registration_page_id',
1532 'login_page_id',
1533 'cart_page_id',
1534 'receipt_page_id',
1535 'shop_page_id',
1536 'customer_profile_page_id',
1537 'customer_profile_page_slug',
1538 ]
1539 );
1540 }
1541
1542 public function getCheckoutPage(): string
1543 {
1544 if ($pageID = $this->getCheckoutPageId()) {
1545 return $this->getPageLink($pageID);
1546 }
1547
1548 return '';
1549 }
1550
1551
1552 public function getCustomerProfilePageId()
1553 {
1554 return Arr::get($this->storeSettings, 'customer_profile_page_id');
1555 }
1556
1557 public function getCustomerProfilePage(): string
1558 {
1559 if ($pageId = $this->getCustomerProfilePageId()) {
1560 return $this->getPageLink($pageId);
1561 }
1562 return '';
1563 }
1564
1565
1566 public function getCartPageId()
1567 {
1568 return Arr::get($this->storeSettings, 'cart_page_id');
1569 }
1570
1571 public function getCartPage(): string
1572 {
1573 if ($pageId = $this->getCartPageId()) {
1574 if (Pages::isPage($pageId)) {
1575 return $this->getPageLink($pageId);
1576 }
1577 }
1578 return '';
1579 }
1580
1581 public function getShopPageId()
1582 {
1583 return Arr::get($this->storeSettings, 'shop_page_id');
1584 }
1585
1586 public function getShopPage(): string
1587 {
1588 if ($pageId = $this->getShopPageId()) {
1589 if (Pages::isPage($pageId)) {
1590 return $this->getPageLink($pageId);
1591 }
1592 }
1593
1594 return '';
1595 }
1596
1597 public function getReceiptPageId()
1598 {
1599 return Arr::get($this->storeSettings, 'receipt_page_id');
1600 }
1601
1602 public function getReceiptPage(): string
1603 {
1604 if ($pageId = $this->getReceiptPageId()) {
1605 if (Pages::isPage($pageId)) {
1606 return $this->getPageLink($pageId);
1607 }
1608 }
1609 return home_url();
1610 }
1611
1612
1613 public function getBuyButtonText(): string
1614 {
1615 return esc_html(Arr::get($this->storeSettings, 'checkout_button_text', __('Buy now', 'fluent-cart')));
1616 }
1617
1618 public function getViewCartButtonText(): string
1619 {
1620 return esc_html(Arr::get($this->storeSettings, 'view_cart_button_text', __('View Cart', 'fluent-cart')));
1621 }
1622
1623 /**
1624 * @return string
1625 *
1626 * Get cart button text
1627 */
1628 public function getCartButtonText(): string
1629 {
1630 return esc_html(Arr::get($this->storeSettings, 'cart_button_text', __('Add To Cart', 'fluent-cart')));
1631 }
1632
1633 public function toArray(): array
1634 {
1635 return $this->storeSettings;
1636 }
1637
1638 /**
1639 * @return string
1640 *
1641 * Get the base address1 for the store.
1642 */
1643 public function getBaseAddressLine1()
1644 {
1645 return Arr::get($this->storeSettings, 'store_address1');
1646 }
1647
1648 public function getFormattedFullAddress()
1649 {
1650 $addressParts = [
1651 trim(Arr::get($this->storeSettings, 'store_address1') ?? ''),
1652 trim(Arr::get($this->storeSettings, 'store_address2') ?? ''),
1653 trim(Arr::get($this->storeSettings, 'store_city') ?? ''),
1654 trim(AddressHelper::getStateNameByCode(
1655 Arr::get($this->storeSettings, 'store_state'),
1656 Arr::get($this->storeSettings, 'store_country')
1657 )),
1658 trim(Arr::get($this->storeSettings, 'store_postcode') ?? ''),
1659 trim(AddressHelper::getCountryNameByCode(Arr::get($this->storeSettings, 'store_country')))
1660 ];
1661
1662 // Filter out empty or null parts
1663 $addressParts = array_filter($addressParts, function ($part) {
1664 return $part !== '';
1665 });
1666
1667 // Join parts with comma and space
1668 $addressString = implode(', ', $addressParts);
1669 return $addressString;
1670 }
1671
1672 /**
1673 * @return string
1674 *
1675 * Get the base address2 for the store.
1676 */
1677 public function getBaseAddressLine2()
1678 {
1679 return Arr::get($this->storeSettings, 'store_address2');
1680 }
1681
1682 /**
1683 * @return string
1684 *
1685 * Get the base country for the store.
1686 */
1687 public function getBaseCountry()
1688 {
1689 return Arr::get($this->storeSettings, 'store_country');
1690 }
1691
1692 /**
1693 * @return string
1694 *
1695 * Get the base state for the store.
1696 */
1697 public function getBaseState()
1698 {
1699 return Arr::get($this->storeSettings, 'store_state');
1700 }
1701
1702 /**
1703 * @return string
1704 *
1705 * Get the base city for the store.
1706 */
1707 public function getBaseCity()
1708 {
1709 return Arr::get($this->storeSettings, 'store_city');
1710 }
1711
1712 /**
1713 * @return string
1714 *
1715 * Get the base postcode for the store.
1716 */
1717 public function getBasePostcode()
1718 {
1719 return Arr::get($this->storeSettings, 'store_postcode');
1720 }
1721
1722 public function getInvoicePrefix()
1723 {
1724 // @todo: make this dynamic from settings
1725 return $this->get('inv_prefix', '');
1726 }
1727
1728 public function getInvoiceSuffix(): string
1729 {
1730 return '';
1731 }
1732
1733 public function getCustomerDashboardPageSlug($pageId = null, $cached = true)
1734 {
1735 $slug = Arr::get($this->storeSettings, 'customer_profile_page_slug', '');
1736
1737 if ($cached && $slug) {
1738 return $slug;
1739 }
1740
1741 if (!$pageId) {
1742 $pageId = Arr::get($this->storeSettings, 'customer_profile_page_id');
1743 }
1744
1745 if (!$pageId || !get_post($pageId)) {
1746 return $slug;
1747 }
1748
1749 $url = get_page_link($pageId);
1750 $url = rtrim($url, '/');
1751
1752 if (!$url) {
1753 return $slug;
1754 }
1755
1756 return trim(str_replace(home_url('/'), '', $url), '/');
1757 }
1758
1759 /**
1760 * Get theme colors as CSS variable string.
1761 *
1762 * @return string
1763 */
1764 public function getThemeColors(): string
1765 {
1766 $themeSetup = $this->get('theme_setup');
1767 $colors = '';
1768
1769 if (!empty($themeSetup) && is_array($themeSetup)) {
1770 foreach ($themeSetup as $key => $value) {
1771 if (!empty($value)) {
1772 $safeKey = preg_replace('/[^a-zA-Z0-9\-_]/', '', $key);
1773 $safeValue = preg_replace('/[;\{\}]/', '', $value);
1774 $colors .= "$safeKey: $safeValue;";
1775 }
1776 }
1777 }
1778
1779 return $colors;
1780 }
1781
1782 public function getPageLink($pageId = null): string
1783 {
1784 if (!$pageId || !get_post($pageId)) {
1785 return '';
1786 }
1787
1788 $link = get_page_link($pageId);
1789
1790 if (!empty($link)) {
1791 return Str::endsWith($link, '/') ? $link : $link . '/';
1792 }
1793 return '';
1794 }
1795
1796 }
1797