PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.8
Booking for Appointments and Events Calendar – Amelia v2.4.8
2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / src / Infrastructure / WP / InstallActions / ActivationSettingsHook.php
ameliabooking / src / Infrastructure / WP / InstallActions Last commit date
DB 5 days ago ActivationDatabaseHook.php 7 months ago ActivationMultisite.php 1 year ago ActivationNewSiteMultisite.php 2 months ago ActivationRolesHook.php 5 days ago ActivationSettingsHook.php 5 days ago AutoUpdateHook.php 1 month ago DeleteDatabaseHook.php 1 year ago DeletionMultisite.php 1 year ago
ActivationSettingsHook.php
2833 lines
1 <?php
2
3 /**
4 * Settings hook for activation
5 */
6
7 namespace AmeliaBooking\Infrastructure\WP\InstallActions;
8
9 use AmeliaBooking\Domain\Entity\Entities;
10 use AmeliaBooking\Domain\Services\Settings\SettingsService;
11 use AmeliaBooking\Domain\ValueObjects\String\Token;
12 use AmeliaBooking\Infrastructure\Licence\Licence;
13 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\EventsTagsTable;
14 use AmeliaBooking\Infrastructure\WP\SettingsService\SettingsStorage;
15 use AmeliaBooking\Infrastructure\WP\PermissionsService\PermissionsChecker;
16 use Exception;
17
18 /**
19 * Class ActivationSettingsHook
20 *
21 * @package AmeliaBooking\Infrastructure\WP\InstallActions
22 */
23 class ActivationSettingsHook
24 {
25 /**
26 * Initialize the plugin
27 *
28 * @throws Exception
29 */
30 public static function init()
31 {
32 self::initDBSettings();
33
34 self::initGeneralSettings();
35
36 self::initCompanySettings();
37
38 self::initNotificationsSettings();
39
40 self::initDaysOffSettings();
41
42 self::initWeekScheduleSettings();
43
44 self::initGoogleCalendarSettings();
45
46 self::initOutlookCalendarSettings();
47
48 self::initPaymentsSettings();
49
50 self::initActivationSettings();
51
52 self::initLabelsSettings();
53
54 self::initRolesSettings();
55
56 self::initAppointmentsSettings();
57
58 self::initWebHooksSettings();
59
60 self::initZoomSettings();
61
62 self::initApiKeySettings();
63
64 self::initLessonSpaceSettings();
65
66 self::initIcsSettings();
67
68 self::initFacebookPixelSettings();
69
70 self::initGoogleAnalyticsSettings();
71
72 self::initGoogleTagSettings();
73
74 self::initMailchimpSettings();
75
76 self::initIvySettings();
77
78 self::initAppleCalendarSettings();
79
80 self::initPageColumnSettings();
81
82 self::initSocialLoginSettings();
83
84 self::initWhiteLabelSettings();
85
86 self::initFeaturesIntegrationsSettings();
87
88 self::initLoggingSettings();
89 }
90
91 /**
92 * @param string $category
93 * @param array $settings
94 * @param bool $replace
95 */
96 public static function initSettings($category, $settings, $replace = false)
97 {
98 $settingsService = new SettingsService(new SettingsStorage());
99
100 if (!$settingsService->getCategorySettings($category)) {
101 $settingsService->setCategorySettings(
102 $category,
103 []
104 );
105 }
106
107 foreach ($settings as $key => $value) {
108 if ($replace || null === $settingsService->getSetting($category, $key)) {
109 $settingsService->setSetting(
110 $category,
111 $key,
112 $value
113 );
114 }
115 }
116 }
117
118 /**
119 * Init General Settings
120 *
121 * @param array $savedSettings
122 *
123 * @return array
124 */
125 public static function getDefaultGeneralSettings($savedSettings)
126 {
127 return [
128 'timeSlotLength' => 1800,
129 'serviceDurationAsSlot' => false,
130 'bufferTimeInSlot' => true,
131 'defaultAppointmentStatus' => 'approved',
132 'defaultEventStatus' => 'approved',
133 'minimumTimeRequirementPriorToBooking' => 0,
134 'minimumTimeRequirementPriorToCanceling' => 0,
135 'minimumTimeRequirementPriorToRescheduling' =>
136 isset($savedSettings['minimumTimeRequirementPriorToCanceling']) &&
137 !isset($savedSettings['minimumTimeRequirementPriorToRescheduling']) ?
138 $savedSettings['minimumTimeRequirementPriorToCanceling'] : 0,
139 'numberOfDaysAvailableForBooking' => SettingsService::NUMBER_OF_DAYS_AVAILABLE_FOR_BOOKING,
140 'backendSlotsDaysInFuture' => SettingsService::NUMBER_OF_DAYS_AVAILABLE_FOR_BOOKING,
141 'backendSlotsDaysInPast' => SettingsService::NUMBER_OF_DAYS_AVAILABLE_FOR_BOOKING,
142 'phoneDefaultCountryCode' => 'auto',
143 'ipLocateApiKey' => '',
144 'requiredPhoneNumberField' => false,
145 'requiredEmailField' => true,
146 'itemsPerPage' => 12,
147 'appointmentsPerPage' => 100,
148 'eventsPerPage' => 100,
149 'servicesPerPage' => 100,
150 'customersFilterLimit' => 100,
151 'eventsFilterLimit' => 1000,
152 'calendarEmployeesPreselected' => 0,
153 'gMapApiKey' => '',
154 'addToCalendar' => true,
155 'defaultPageOnBackend' => 'Dashboard',
156 'showClientTimeZone' => false,
157 'redirectUrlAfterAppointment' => '',
158 'customFieldsUploadsPath' => '',
159 'customFieldsAllowedExtensions' => [
160 '.jpg' => 'image/jpeg',
161 '.jpeg' => 'image/jpeg',
162 '.png' => 'image/png',
163 '.mp3' => 'audio/mpeg',
164 '.mpeg' => 'video/mpeg',
165 '.mp4' => 'video/mp4',
166 '.txt' => 'text/plain',
167 '.csv' => 'text/plain',
168 '.xls' => 'application/vnd.ms-excel',
169 '.pdf' => 'application/pdf',
170 '.doc' => 'application/msword',
171 '.docx' => 'application/msword'
172 ],
173 'customFieldsBackendValidation' => false,
174 'runInstantPostBookingActions' => false,
175 'sortingPackages' => 'nameAsc',
176 'sortingServices' => 'nameAsc',
177 'calendarLocaleSubstitutes' => [],
178 'googleRecaptcha' => [
179 'invisible' => true,
180 'siteKey' => '',
181 'secret' => '',
182 ],
183 'usedLanguages' => [],
184 ];
185 }
186
187 /**
188 * Get General Settings
189 */
190 private static function initGeneralSettings()
191 {
192 $settingsService = new SettingsService(new SettingsStorage());
193
194 $savedSettings = $settingsService->getCategorySettings('general');
195
196 $settings = self::getDefaultGeneralSettings($savedSettings);
197
198 $settings['backLink'] = self::getBackLinkSetting();
199
200 self::initSettings('general', $settings);
201
202 self::setNewSettingsToExistingSettings(
203 'general',
204 [
205 ['backLink', 'url'],
206 ],
207 $settings
208 );
209 }
210
211 /**
212 * Init DB Settings
213 */
214 private static function initDBSettings()
215 {
216 self::initSettings('db', [
217 'wpTablesPrefix' => '',
218 ]);
219 }
220
221 /**
222 * Init Company Settings
223 */
224 private static function initCompanySettings()
225 {
226
227 $settings = [
228 'pictureFullPath' => '',
229 'pictureThumbPath' => '',
230 'name' => '',
231 'address' => '',
232 'addressComponents' => [],
233 'countryCode' => '',
234 'phone' => '',
235 'vat' => '',
236 'countryPhoneIso' => '',
237 'website' => '',
238 'translations' => '',
239 ];
240
241 self::initSettings('company', $settings);
242 }
243
244 /**
245 * Get Notification Settings
246 *
247 * @return array
248 */
249 public static function getDefaultNotificationsSettings()
250 {
251 return [
252 'mailService' => '',
253 'smtpHost' => '',
254 'smtpPort' => '',
255 'smtpSecure' => 'ssl',
256 'smtpUsername' => '',
257 'smtpPassword' => '',
258 'mailgunApiKey' => '',
259 'mailgunDomain' => '',
260 'mailgunEndpoint' => '',
261 'senderName' => '',
262 'replyTo' => '',
263 'senderEmail' => '',
264 'sendAllCF' => true,
265 'smsAlphaSenderId' => 'Amelia',
266 'smsSignedIn' => false,
267 'smsApiToken' => '',
268 'bccEmail' => '',
269 'bccSms' => '',
270 'emptyPackageEmployees' => '',
271 'smsBalanceEmail' => ['enabled' => false, 'minimum' => 0, 'email' => ''],
272 'cancelSuccessUrl' => '',
273 'cancelErrorUrl' => '',
274 'approveSuccessUrl' => '',
275 'approveErrorUrl' => '',
276 'rejectSuccessUrl' => '',
277 'rejectErrorUrl' => '',
278 'breakReplacement' => '<br>',
279 'pendingReminder' => false,
280 'sendInvoice' => false,
281 'invoiceFormat' => 'pdf',
282 'whatsAppPhoneID' => '',
283 'whatsAppAccessToken' => '',
284 'whatsAppBusinessID' => '',
285 'whatsAppLanguage' => '',
286 'whatsAppReplyEnabled' => false,
287 'whatsAppReplyMsg' => 'Dear %customer_full_name%,
288 This message does not have an option for responding. If you need additional information about your booking, please contact us at %company_phone%',
289 'whatsAppReplyToken' => (new Token(null, 20))->getValue(),
290 ];
291 }
292
293 /**
294 * Resolve SMS alpha sender ID from a plugin display name.
295 *
296 * @param string $pluginName
297 *
298 * @return string
299 */
300 public static function deriveSmsAlphaSenderIdFromPluginName($pluginName)
301 {
302 $defaultSenderId = 'Amelia';
303
304 // Alpha sender IDs must be 3-11 alphanumeric characters with at least one letter.
305 $senderId = preg_replace('/[^A-Za-z0-9]/', '', (string) $pluginName);
306 $senderId = substr((string) $senderId, 0, 11);
307
308 if (strlen($senderId) < 3 || !preg_match('/[A-Za-z]/', $senderId)) {
309 return $defaultSenderId;
310 }
311
312 return $senderId;
313 }
314
315 /**
316 * Sync smsAlphaSenderId when white label is toggled or its plugin name changes.
317 * Does nothing on plugin activation/deactivation.
318 *
319 * @param array $settingsFields
320 *
321 * @return void
322 */
323 public static function syncSmsAlphaSenderIdOnWhiteLabelChange($settingsFields = [])
324 {
325 if (
326 !is_array($settingsFields) ||
327 (
328 !array_key_exists('whiteLabel', $settingsFields) &&
329 !array_key_exists('featuresIntegrations', $settingsFields)
330 )
331 ) {
332 return;
333 }
334
335 self::syncSmsAlphaSenderIdFromWhiteLabel();
336 }
337
338 /**
339 * Apply white-label plugin name to smsAlphaSenderId when appropriate.
340 *
341 * @return void
342 */
343 public static function syncSmsAlphaSenderIdFromWhiteLabel()
344 {
345 $settingsService = new SettingsService(new SettingsStorage());
346 $whiteLabel = $settingsService->getCategorySettings('whiteLabel');
347 $pluginName = !empty($whiteLabel['pluginName']) ? trim($whiteLabel['pluginName']) : '';
348
349 if ($pluginName === '') {
350 return;
351 }
352
353 $derivedSenderId = self::deriveSmsAlphaSenderIdFromPluginName($pluginName);
354 $currentSenderId = $settingsService->getSetting('notifications', 'smsAlphaSenderId');
355
356 if ($settingsService->isFeatureEnabled('whiteLabel')) {
357 if (
358 ($currentSenderId === null || $currentSenderId === '' || $currentSenderId === 'Amelia') &&
359 $derivedSenderId !== 'Amelia'
360 ) {
361 $settingsService->setSetting('notifications', 'smsAlphaSenderId', $derivedSenderId);
362 }
363
364 return;
365 }
366
367 // White label disabled: restore Amelia only if still using the branded sender.
368 if ($currentSenderId === $derivedSenderId) {
369 $settingsService->setSetting('notifications', 'smsAlphaSenderId', 'Amelia');
370 }
371 }
372
373 /**
374 * Init Notification Settings
375 */
376 private static function initNotificationsSettings()
377 {
378 $settings = self::getDefaultNotificationsSettings();
379
380 self::initSettings('notifications', $settings);
381 }
382
383 /**
384 * Init Days Off Settings
385 */
386 private static function initDaysOffSettings()
387 {
388 self::initSettings('daysOff', []);
389 }
390
391 /**
392 * Init Work Schedule Settings
393 */
394 private static function initWeekScheduleSettings()
395 {
396 self::initSettings(
397 'weekSchedule',
398 [
399 [
400 'day' => 'Monday',
401 'time' => ['09:00', '17:00'],
402 'breaks' => [],
403 'periods' => []
404 ],
405 [
406 'day' => 'Tuesday',
407 'time' => ['09:00', '17:00'],
408 'breaks' => [],
409 'periods' => []
410 ],
411 [
412 'day' => 'Wednesday',
413 'time' => ['09:00', '17:00'],
414 'breaks' => [],
415 'periods' => []
416 ],
417 [
418 'day' => 'Thursday',
419 'time' => ['09:00', '17:00'],
420 'breaks' => [],
421 'periods' => []
422 ],
423 [
424 'day' => 'Friday',
425 'time' => ['09:00', '17:00'],
426 'breaks' => [],
427 'periods' => []
428 ],
429 [
430 'day' => 'Saturday',
431 'time' => [],
432 'breaks' => [],
433 'periods' => []
434 ],
435 [
436 'day' => 'Sunday',
437 'time' => [],
438 'breaks' => [],
439 'periods' => []
440 ]
441 ]
442 );
443 }
444
445 /**
446 * Init Google Calendar Settings
447 */
448 private static function initGoogleCalendarSettings()
449 {
450 $settingsService = new SettingsService(new SettingsStorage());
451
452 $savedSettings = $settingsService->getCategorySettings('googleCalendar');
453
454 $settings = [
455 'clientID' => '',
456 'clientSecret' => '',
457 'redirectURI' => AMELIA_SITE_URL . '/wp-admin/admin.php?page=wpamelia-employees',
458 'showAttendees' => false,
459 'insertPendingAppointments' => false,
460 'addAttendees' => false,
461 'sendEventInvitationEmail' => false,
462 'removeGoogleCalendarBusySlots' => false,
463 'maximumNumberOfEventsReturned' => 50,
464 'eventTitle' => '%service_name%',
465 'eventDescription' => '',
466 'includeBufferTimeGoogleCalendar' => false,
467 'status' => 'tentative',
468 'enableGoogleMeet' => false,
469 'title' => [
470 'appointment' => $savedSettings && !empty($savedSettings['eventTitle']) ?
471 $savedSettings['eventTitle'] : '%service_name%',
472 'event' => '%event_name%'
473 ],
474 'description' => [
475 'appointment' => $savedSettings && !empty($savedSettings['eventDescription']) ?
476 $savedSettings['eventDescription'] : '',
477 'event' => ''
478 ],
479 'accessToken' => '',
480 'googleAccountData' => null
481 ];
482
483 self::initSettings('googleCalendar', $settings);
484 }
485
486 /**
487 * Init Outlook Calendar Settings
488 */
489 private static function initOutlookCalendarSettings()
490 {
491 $settingsService = new SettingsService(new SettingsStorage());
492
493 $savedSettings = $settingsService->getCategorySettings('outlookCalendar');
494
495 $settings = [
496 'clientID' => '',
497 'clientSecret' => '',
498 'redirectURI' => AMELIA_SITE_URL . '/wp-admin/',
499 'insertPendingAppointments' => false,
500 'addAttendees' => false,
501 'sendEventInvitationEmail' => false,
502 'removeOutlookCalendarBusySlots' => false,
503 'maximumNumberOfEventsReturned' => 50,
504 'ignoreAmeliaEvents' => false,
505 'eventTitle' => '%service_name%',
506 'eventDescription' => '',
507 'includeBufferTimeOutlookCalendar' => false,
508 'enableMicrosoftTeams' => false,
509 'title' => [
510 'appointment' => $savedSettings && !empty($savedSettings['eventTitle']) ?
511 $savedSettings['eventTitle'] : '%service_name%',
512 'event' => '%event_name%'
513 ],
514 'description' => [
515 'appointment' => $savedSettings && !empty($savedSettings['eventDescription']) ?
516 $savedSettings['eventDescription'] : '',
517 'event' => ''
518 ],
519 'mailEnabled' => false,
520 'token' => null,
521 'accessToken' => '',
522 'outlookAccountData' => null
523 ];
524
525 self::initSettings('outlookCalendar', $settings);
526 }
527
528 /**
529 * Init Apple Calendar Settings
530 */
531 private static function initAppleCalendarSettings()
532 {
533 $settings = [
534 'clientID' => '',
535 'clientSecret' => '',
536 'redirectURI' => AMELIA_SITE_URL . '/wp-admin/admin.php?page=wpamelia-employees',
537 'insertPendingAppointments' => false,
538 'addAttendees' => false,
539 'removeAppleCalendarBusySlots' => false,
540 'eventTitle' => '%service_name%',
541 'eventDescription' => '',
542 'includeBufferTimeAppleCalendar' => false,
543 'title' => [
544 'appointment' => '%service_name%',
545 'event' => '%event_name%'
546 ],
547 'description' => [
548 'appointment' => '',
549 'event' => ''
550 ],
551 ];
552
553 self::initSettings('appleCalendar', $settings);
554 }
555
556 /**
557 * Init Zoom Settings
558 */
559 private static function initZoomSettings()
560 {
561 $settings = [
562 'enabled' => true,
563 'apiKey' => '',
564 'apiSecret' => '',
565 'meetingTitle' => '%reservation_name%',
566 'meetingAgenda' => '%reservation_description%',
567 'pendingAppointmentsMeetings' => false,
568 's2sEnabled' => true,
569 'accountId' => '',
570 'clientId' => '',
571 'clientSecret' => '',
572 'accessToken' => '',
573 ];
574
575 self::initSettings('zoom', $settings);
576 }
577
578
579 /**
580 * Init Api Key Settings
581 */
582 private static function initApiKeySettings()
583 {
584 $settings = [
585 'apiKeys' => []
586 ];
587
588 self::initSettings('apiKeys', $settings);
589 }
590
591 /**
592 * Init Lesson Space Settings
593 */
594 private static function initLessonSpaceSettings()
595 {
596 $settings = [
597 'apiKey' => '',
598 'spaceNameAppointments' => '%reservation_name%',
599 'spaceNameEvents' => '%reservation_name%',
600 'pendingAppointments' => false,
601 'companyId' => ''
602 ];
603
604 self::initSettings('lessonSpace', $settings);
605 }
606
607 /**
608 * Init FacebookPixel Settings
609 */
610 private static function initFacebookPixelSettings()
611 {
612 $settings = [
613 'id' => '',
614 'tracking' => [
615 'appointment' => [],
616 'event' => [],
617 'package' => [],
618 ],
619 ];
620
621 self::initSettings('facebookPixel', $settings);
622 }
623
624 /**
625 * Init Google Analytics Settings
626 */
627 private static function initGoogleAnalyticsSettings()
628 {
629 $settings = [
630 'id' => '',
631 'tracking' => [
632 'appointment' => [],
633 'event' => [],
634 'package' => [],
635 ],
636 ];
637
638 self::initSettings('googleAnalytics', $settings);
639 }
640
641 /**
642 * Init GoogleTag Settings
643 */
644 private static function initGoogleTagSettings()
645 {
646 $settings = [
647 'id' => '',
648 'tracking' => [
649 'appointment' => [],
650 'event' => [],
651 'package' => [],
652 ],
653 ];
654
655 self::initSettings('googleTag', $settings);
656 }
657
658
659 /**
660 * Init Mailchimp Settings
661 */
662 private static function initMailchimpSettings()
663 {
664 $settings = [
665 'accessToken' => null,
666 'server' => null,
667 'list' => null,
668 'checkedByDefault' => false
669 ];
670
671 self::initSettings('mailchimp', $settings);
672 }
673
674 /**
675 * Init Logging Settings
676 */
677 private static function initLoggingSettings()
678 {
679 $settings = [
680 'enabled' => true,
681 'minLevel' => 'debug',
682 'retentionDays' => 30,
683 ];
684
685 self::initSettings('logging', $settings);
686 }
687
688 /**
689 * Init Ivy Settings
690 */
691 private static function initIvySettings()
692 {
693 $settings = [];
694
695 self::initSettings('ivy', $settings);
696 }
697
698 /**
699 * Init Page Column Settings
700 */
701 private static function initPageColumnSettings()
702 {
703 $settings = self::getDefaultPageColumnSettings();
704
705 self::initSettings('pageColumnSettings', $settings);
706
707 // Handle updates to existing page column settings
708 self::updatePageColumnSettings($settings);
709 }
710
711 /**
712 * Get Default Page Column Settings
713 *
714 * @return array
715 */
716 public static function getDefaultPageColumnSettings()
717 {
718 return [
719 "customersPage" => [
720 [
721 "prop" => "id",
722 "visible" => true,
723 "width" => 80,
724 "label" => "id",
725 "sortable" => false
726 ],
727 [
728 "prop" => "name",
729 "visible" => true,
730 "width" => 320,
731 "label" => "name",
732 "sortable" => true,
733 "required" => true
734 ],
735 [
736 "prop" => "phone",
737 "visible" => true,
738 "width" => 160,
739 "label" => "phone",
740 "sortable" => false
741 ],
742 [
743 "prop" => "email",
744 "visible" => true,
745 "width" => 240,
746 "label" => "email",
747 "sortable" => false
748 ],
749 [
750 "prop" => "note",
751 "visible" => true,
752 "width" => 80,
753 "label" => "note",
754 "sortable" => false
755 ],
756 [
757 "prop" => "lastBooking",
758 "visible" => true,
759 "width" => 240,
760 "label" => "last_booking",
761 "sortable" => true
762 ],
763 [
764 "prop" => "totalBookings",
765 "visible" => true,
766 "width" => 160,
767 "label" => "total_bookings",
768 "sortable" => true
769 ],
770 [
771 "prop" => "wordPressUser",
772 "visible" => true,
773 "width" => 240,
774 "label" => "wp_user",
775 "sortable" => false
776 ]
777 ],
778
779 "employeesPage" => [
780 [
781 "prop" => "id",
782 "visible" => true,
783 "width" => 80,
784 "label" => "id",
785 "sortable" => false
786 ],
787 [
788 "prop" => "name",
789 "visible" => true,
790 "width" => 320,
791 "label" => "name",
792 "sortable" => true,
793 "required" => true
794 ],
795 [
796 "prop" => "status",
797 "visible" => true,
798 "width" => 160,
799 "label" => "visibility",
800 "sortable" => false
801 ],
802 [
803 "prop" => "availability",
804 "visible" => true,
805 "width" => 160,
806 "label" => "red_availability",
807 "sortable" => false
808 ],
809 [
810 "prop" => "phone",
811 "visible" => true,
812 "width" => 160,
813 "label" => "phone",
814 "sortable" => false
815 ],
816 [
817 "prop" => "email",
818 "visible" => true,
819 "width" => 240,
820 "label" => "email",
821 "sortable" => false
822 ],
823 [
824 "prop" => "actions",
825 "visible" => true,
826 "width" => 240,
827 "label" => "red_actions",
828 "sortable" => false
829 ],
830 ],
831
832 "locationsPage" => [
833 [
834 "prop" => "id",
835 "visible" => true,
836 "width" => 80,
837 "label" => "id",
838 "sortable" => false
839 ],
840 [
841 "prop" => "name",
842 "visible" => true,
843 "width" => 320,
844 "label" => "name",
845 "sortable" => true,
846 "required" => true
847 ],
848 [
849 "prop" => "address",
850 "visible" => true,
851 "width" => 320,
852 "label" => "address",
853 "sortable" => false
854 ],
855 [
856 "prop" => "phone",
857 "visible" => true,
858 "width" => 160,
859 "label" => "phone",
860 "sortable" => false
861 ],
862 [
863 "prop" => "status",
864 "visible" => true,
865 "width" => 160,
866 "label" => "visibility",
867 "sortable" => false
868 ]
869 ],
870
871 "bookingsAppointmentsPage" => [
872 [
873 "prop" => "id",
874 "visible" => true,
875 "width" => 80,
876 "label" => "id",
877 "sortable" => true
878 ],
879 [
880 "prop" => "date",
881 "visible" => true,
882 "width" => 160,
883 "label" => "date",
884 "sortable" => false,
885 "required" => true
886 ],
887 [
888 "prop" => "time",
889 "visible" => true,
890 "width" => 160,
891 "label" => "time",
892 "sortable" => false,
893 "required" => true
894 ],
895 [
896 "prop" => "customer",
897 "visible" => true,
898 "width" => 320,
899 "label" => "customer",
900 "sortable" => true,
901 "required" => true
902 ],
903 [
904 "prop" => "service",
905 "visible" => true,
906 "width" => 240,
907 "label" => "service",
908 "sortable" => true,
909 "required" => true
910 ],
911 [
912 "prop" => "type",
913 "visible" => true,
914 "width" => 160,
915 "label" => "type",
916 "sortable" => false
917 ],
918 [
919 "prop" => "status",
920 "visible" => true,
921 "width" => 200,
922 "label" => "status",
923 "sortable" => false
924 ],
925 [
926 "prop" => "employee",
927 "visible" => true,
928 "width" => 320,
929 "label" => "employee",
930 "sortable" => false
931 ],
932 [
933 "prop" => "location",
934 "visible" => true,
935 "width" => 240,
936 "label" => "location",
937 "sortable" => false
938 ],
939 [
940 "prop" => "duration",
941 "visible" => false,
942 "width" => 160,
943 "label" => "duration",
944 "sortable" => false
945 ],
946 [
947 "prop" => "customerEmail",
948 "visible" => false,
949 "width" => 240,
950 "label" => "customer_email",
951 "sortable" => false
952 ],
953 [
954 "prop" => "customerPhone",
955 "visible" => false,
956 "width" => 240,
957 "label" => "customer_phone",
958 "sortable" => false
959 ],
960 [
961 "prop" => "booked",
962 "visible" => false,
963 "width" => 80,
964 "label" => "booked",
965 "sortable" => false
966 ],
967 [
968 "prop" => "note",
969 "visible" => false,
970 "width" => 80,
971 "label" => "note",
972 "sortable" => false
973 ],
974 [
975 "prop" => "paidPrice",
976 "visible" => false,
977 "width" => 160,
978 "label" => "paid",
979 "sortable" => false
980 ],
981 [
982 "prop" => "payment",
983 "visible" => false,
984 "width" => 160,
985 "label" => "payment",
986 "sortable" => false
987 ],
988 [
989 "prop" => "totalPrice",
990 "visible" => false,
991 "width" => 160,
992 "label" => "total_price",
993 "sortable" => false
994 ],
995 [
996 "prop" => "hostLink",
997 "visible" => false,
998 "width" => 160,
999 "label" => "red_host_link",
1000 "sortable" => false
1001 ],
1002 [
1003 "prop" => "joinLink",
1004 "visible" => false,
1005 "width" => 200,
1006 "label" => "red_join_link",
1007 "sortable" => false
1008 ],
1009 [
1010 "prop" => "created",
1011 "visible" => false,
1012 "width" => 160,
1013 "label" => "created_on",
1014 "sortable" => true,
1015 "required" => false
1016 ],
1017 [
1018 "prop" => "bookingSource",
1019 "visible" => false,
1020 "width" => 64,
1021 "label" => "red_booking_source",
1022 "sortable" => false,
1023 "showLabel" => false
1024 ],
1025 ],
1026
1027 "bookingsPackagesPage" => [
1028 [
1029 "prop" => "id",
1030 "visible" => true,
1031 "width" => 80,
1032 "label" => "id",
1033 "sortable" => true
1034 ],
1035 [
1036 "prop" => "date",
1037 "visible" => true,
1038 "width" => 160,
1039 "label" => "package_date_purchased",
1040 "sortable" => true
1041 ],
1042 [
1043 "prop" => "customer",
1044 "visible" => true,
1045 "width" => 320,
1046 "label" => "customer",
1047 "sortable" => true,
1048 "required" => true
1049 ],
1050 [
1051 "prop" => "package",
1052 "visible" => true,
1053 "width" => 320,
1054 "label" => "package",
1055 "sortable" => true,
1056 "required" => true
1057 ],
1058 [
1059 "prop" => "status",
1060 "visible" => true,
1061 "width" => 200,
1062 "label" => "status",
1063 "sortable" => false
1064 ],
1065 [
1066 "prop" => "appointments",
1067 "visible" => true,
1068 "width" => 160,
1069 "label" => "appointments",
1070 "sortable" => false
1071 ],
1072 [
1073 "prop" => "employees",
1074 "visible" => true,
1075 "width" => 320,
1076 "label" => "employees",
1077 "sortable" => false
1078 ],
1079 [
1080 "prop" => "expirationDate",
1081 "visible" => false,
1082 "width" => 240,
1083 "label" => "expiration_date",
1084 "sortable" => false
1085 ],
1086 [
1087 "prop" => "price",
1088 "visible" => false,
1089 "width" => 160,
1090 "label" => "price",
1091 "sortable" => false
1092 ],
1093 [
1094 "prop" => "paymentStatus",
1095 "visible" => false,
1096 "width" => 200,
1097 "label" => "payment_status",
1098 "sortable" => false
1099 ]
1100 ],
1101
1102 "bookingsEventsPage" => [
1103 [
1104 "prop" => "code",
1105 "visible" => true,
1106 "width" => 120,
1107 "label" => "code",
1108 "sortable" => false
1109 ],
1110 [
1111 "prop" => "date",
1112 "visible" => true,
1113 "width" => 160,
1114 "label" => "date",
1115 "sortable" => false
1116 ],
1117 [
1118 "prop" => "time",
1119 "visible" => true,
1120 "width" => 160,
1121 "label" => "time",
1122 "sortable" => false
1123 ],
1124 [
1125 "prop" => "attendee",
1126 "visible" => true,
1127 "width" => 240,
1128 "label" => "attendee",
1129 "sortable" => true,
1130 "required" => true
1131 ],
1132 [
1133 "prop" => "event",
1134 "visible" => true,
1135 "width" => 320,
1136 "label" => "event",
1137 "sortable" => true,
1138 "required" => true
1139 ],
1140 [
1141 "prop" => "status",
1142 "visible" => true,
1143 "width" => 200,
1144 "label" => "status",
1145 "sortable" => false
1146 ],
1147 [
1148 "prop" => "spots",
1149 "visible" => true,
1150 "width" => 80,
1151 "label" => "red_booked",
1152 "sortable" => false
1153 ],
1154 [
1155 "prop" => "organizer",
1156 "visible" => false,
1157 "width" => 240,
1158 "label" => "event_organizer",
1159 "sortable" => false
1160 ],
1161 [
1162 "prop" => "staff",
1163 "visible" => false,
1164 "width" => 240,
1165 "label" => "event_staff",
1166 "sortable" => false
1167 ],
1168 [
1169 "prop" => "price",
1170 "visible" => false,
1171 "width" => 160,
1172 "label" => "price",
1173 "sortable" => false
1174 ],
1175 [
1176 "prop" => "paymentStatus",
1177 "visible" => false,
1178 "width" => 200,
1179 "label" => "payment_status",
1180 "sortable" => false
1181 ],
1182 [
1183 "prop" => "created",
1184 "visible" => false,
1185 "width" => 160,
1186 "label" => "created_on",
1187 "sortable" => true,
1188 "required" => false
1189 ],
1190 ],
1191
1192 "eventsPage" => [
1193 [
1194 "prop" => "id",
1195 "visible" => true,
1196 "width" => 80,
1197 "label" => "id",
1198 "sortable" => true
1199 ],
1200 [
1201 "prop" => "dateTime",
1202 "visible" => true,
1203 "width" => 240,
1204 "label" => "date_time",
1205 "sortable" => true,
1206 "required" => true
1207 ],
1208 [
1209 "prop" => "name",
1210 "visible" => true,
1211 "width" => 320,
1212 "label" => "name",
1213 "sortable" => true,
1214 "required" => true
1215 ],
1216 [
1217 "prop" => "status",
1218 "visible" => true,
1219 "width" => 200,
1220 "label" => "status",
1221 "sortable" => false
1222 ],
1223 [
1224 "prop" => "spots",
1225 "visible" => true,
1226 "width" => 80,
1227 "label" => "red_booked",
1228 "sortable" => false
1229 ],
1230 [
1231 "prop" => "organizer",
1232 "visible" => true,
1233 "width" => 240,
1234 "label" => "event_organizer",
1235 "sortable" => false
1236 ],
1237 [
1238 "prop" => "staff",
1239 "visible" => true,
1240 "width" => 240,
1241 "label" => "event_staff",
1242 "sortable" => false
1243 ],
1244 [
1245 "prop" => "recurring",
1246 "visible" => true,
1247 "width" => 120,
1248 "label" => "recurring",
1249 "sortable" => false
1250 ],
1251 [
1252 "prop" => "waitingList",
1253 "visible" => true,
1254 "width" => 200,
1255 "label" => "waiting_list",
1256 "sortable" => false
1257 ],
1258 [
1259 "prop" => "bookingOpens",
1260 "visible" => true,
1261 "width" => 240,
1262 "label" => "red_booking_opens",
1263 "sortable" => true
1264 ],
1265 [
1266 "prop" => "bookingCloses",
1267 "visible" => true,
1268 "width" => 240,
1269 "label" => "red_booking_closes",
1270 "sortable" => true
1271 ],
1272 [
1273 "prop" => "visibility",
1274 "visible" => true,
1275 "width" => 160,
1276 "label" => "visibility",
1277 "sortable" => false
1278 ],
1279 ],
1280
1281 "catalogServicesPage" => [
1282 [
1283 "prop" => "id",
1284 "visible" => true,
1285 "width" => 80,
1286 "label" => "id",
1287 "sortable" => true
1288 ],
1289 [
1290 "prop" => "service",
1291 "visible" => true,
1292 "width" => 320,
1293 "label" => "service",
1294 "sortable" => true,
1295 "required" => true
1296 ],
1297 [
1298 "prop" => "duration",
1299 "visible" => true,
1300 "width" => 160,
1301 "label" => "duration",
1302 "sortable" => true
1303 ],
1304 [
1305 "prop" => "price",
1306 "visible" => true,
1307 "width" => 160,
1308 "label" => "price",
1309 "sortable" => true
1310 ],
1311 [
1312 "prop" => "employees",
1313 "visible" => true,
1314 "width" => 320,
1315 "label" => "employees",
1316 "sortable" => false
1317 ],
1318 [
1319 "prop" => "visibility",
1320 "visible" => true,
1321 "width" => 160,
1322 "label" => "visibility",
1323 "sortable" => false
1324 ],
1325 ],
1326
1327 "catalogPackagesPage" => [
1328 [
1329 "prop" => "id",
1330 "visible" => true,
1331 "width" => 80,
1332 "label" => "id",
1333 "sortable" => true
1334 ],
1335 [
1336 "prop" => "name",
1337 "visible" => true,
1338 "width" => 320,
1339 "label" => "name",
1340 "sortable" => true,
1341 "required" => true
1342 ],
1343 [
1344 "prop" => "services",
1345 "visible" => true,
1346 "width" => 200,
1347 "label" => "services",
1348 "sortable" => true
1349 ],
1350 [
1351 "prop" => "price",
1352 "visible" => true,
1353 "width" => 160,
1354 "label" => "price",
1355 "sortable" => true
1356 ],
1357 [
1358 "prop" => "duration",
1359 "visible" => true,
1360 "width" => 160,
1361 "label" => "duration",
1362 "sortable" => false
1363 ],
1364 [
1365 "prop" => "employees",
1366 "visible" => true,
1367 "width" => 320,
1368 "label" => "employees",
1369 "sortable" => false
1370 ],
1371 [
1372 "prop" => "status",
1373 "visible" => true,
1374 "width" => 160,
1375 "label" => "visibility",
1376 "sortable" => false
1377 ]
1378 ],
1379
1380 "catalogResourcesPage" => [
1381 [
1382 "prop" => "id",
1383 "visible" => true,
1384 "width" => 80,
1385 "label" => "id",
1386 "sortable" => true
1387 ],
1388 [
1389 "prop" => "name",
1390 "visible" => true,
1391 "width" => 320,
1392 "label" => "name",
1393 "sortable" => true,
1394 "required" => true
1395 ],
1396 [
1397 "prop" => "quantity",
1398 "visible" => true,
1399 "width" => 160,
1400 "label" => "quantity",
1401 "sortable" => true
1402 ],
1403 [
1404 "prop" => "services",
1405 "visible" => true,
1406 "width" => 320,
1407 "label" => "services",
1408 "sortable" => false
1409 ],
1410 [
1411 "prop" => "locations",
1412 "visible" => true,
1413 "width" => 320,
1414 "label" => "locations",
1415 "sortable" => false
1416 ],
1417 [
1418 "prop" => "employees",
1419 "visible" => true,
1420 "width" => 320,
1421 "label" => "employees",
1422 "sortable" => false
1423 ],
1424 [
1425 "prop" => "type",
1426 "visible" => true,
1427 "width" => 200,
1428 "label" => "type",
1429 "sortable" => false
1430 ],
1431 [
1432 "prop" => "status",
1433 "visible" => true,
1434 "width" => 160,
1435 "label" => "visibility",
1436 "sortable" => false
1437 ]
1438 ],
1439
1440 "financePaymentsPage" => [
1441 [
1442 "prop" => "id",
1443 "visible" => true,
1444 "width" => 80,
1445 "label" => "id",
1446 "sortable" => true
1447 ],
1448 [
1449 "prop" => "dateTime",
1450 "visible" => true,
1451 "width" => 160,
1452 "label" => "payment_date",
1453 "sortable" => true
1454 ],
1455 [
1456 "prop" => "customer",
1457 "visible" => true,
1458 "width" => 240,
1459 "label" => "customer",
1460 "sortable" => false,
1461 "required" => true
1462 ],
1463 [
1464 "prop" => "employees",
1465 "visible" => true,
1466 "width" => 240,
1467 "label" => "employees",
1468 "sortable" => false
1469 ],
1470 [
1471 "prop" => "booking",
1472 "visible" => true,
1473 "width" => 320,
1474 "label" => "booking",
1475 "sortable" => false
1476 ],
1477 [
1478 "prop" => "status",
1479 "visible" => true,
1480 "width" => 160,
1481 "label" => "status",
1482 "sortable" => true
1483 ],
1484 [
1485 "prop" => "amount",
1486 "visible" => true,
1487 "width" => 160,
1488 "label" => "amount",
1489 "sortable" => true,
1490 "required" => true
1491 ],
1492 [
1493 "prop" => "payment_method",
1494 "visible" => true,
1495 "width" => 160,
1496 "label" => "payment_method",
1497 "sortable" => false
1498 ],
1499 [
1500 "prop" => "location",
1501 "visible" => true,
1502 "width" => 320,
1503 "label" => "location",
1504 "sortable" => false
1505 ],
1506 ],
1507 "financeCouponsPage" => [
1508 [
1509 "prop" => "code",
1510 "visible" => true,
1511 "width" => 120,
1512 "label" => "code",
1513 "sortable" => false,
1514 "required" => true
1515 ],
1516 [
1517 "prop" => "discount",
1518 "visible" => true,
1519 "width" => 120,
1520 "label" => "discount_amount",
1521 "sortable" => true,
1522 "required" => true
1523 ],
1524 [
1525 "prop" => "deduction",
1526 "visible" => true,
1527 "width" => 120,
1528 "label" => "deduction",
1529 "sortable" => true
1530 ],
1531 [
1532 "prop" => "status",
1533 "visible" => true,
1534 "width" => 160,
1535 "label" => "visibility",
1536 "sortable" => false
1537 ],
1538 [
1539 "prop" => "start_date",
1540 "visible" => true,
1541 "width" => 240,
1542 "label" => "start_date",
1543 "sortable" => false
1544 ],
1545 [
1546 "prop" => "expiration_date",
1547 "visible" => true,
1548 "width" => 240,
1549 "label" => "expiration_date",
1550 "sortable" => false
1551 ],
1552 [
1553 "prop" => "service",
1554 "visible" => true,
1555 "width" => 320,
1556 "label" => "services",
1557 "sortable" => false
1558 ],
1559 [
1560 "prop" => "event",
1561 "visible" => true,
1562 "width" => 320,
1563 "label" => "events",
1564 "sortable" => false
1565 ],
1566 [
1567 "prop" => "package",
1568 "visible" => true,
1569 "width" => 320,
1570 "label" => "packages",
1571 "sortable" => false
1572 ],
1573 [
1574 "prop" => "usage_limit",
1575 "visible" => false,
1576 "width" => 120,
1577 "label" => "usage_limit",
1578 "sortable" => false
1579 ],
1580 [
1581 "prop" => "limit_per_customer",
1582 "visible" => false,
1583 "width" => 120,
1584 "label" => "red_limit_per_customer",
1585 "sortable" => false
1586 ],
1587 [
1588 "prop" => "times_used",
1589 "visible" => false,
1590 "width" => 120,
1591 "label" => "times_used",
1592 "sortable" => true
1593 ],
1594 ],
1595 "financeTaxesPage" => [
1596 [
1597 "prop" => "name",
1598 "visible" => true,
1599 "width" => 240,
1600 "label" => "name",
1601 "sortable" => true,
1602 "required" => true
1603 ],
1604 [
1605 "prop" => "type",
1606 "visible" => true,
1607 "width" => 120,
1608 "label" => "type",
1609 "sortable" => true
1610 ],
1611 [
1612 "prop" => "rate",
1613 "visible" => true,
1614 "width" => 80,
1615 "label" => "rate",
1616 "required" => true
1617 ],
1618 [
1619 "prop" => "status",
1620 "visible" => true,
1621 "width" => 160,
1622 "label" => "visibility",
1623 "sortable" => false
1624 ],
1625 [
1626 "prop" => "service",
1627 "visible" => true,
1628 "width" => 320,
1629 "label" => "services",
1630 "sortable" => false
1631 ],
1632 [
1633 "prop" => "event",
1634 "visible" => true,
1635 "width" => 320,
1636 "label" => "events",
1637 "sortable" => false
1638 ],
1639 [
1640 "prop" => "package",
1641 "visible" => true,
1642 "width" => 320,
1643 "label" => "packages",
1644 "sortable" => false
1645 ],
1646 [
1647 "prop" => "extra",
1648 "visible" => true,
1649 "width" => 320,
1650 "label" => "extras",
1651 "sortable" => false
1652 ],
1653
1654 ],
1655 "financeInvoicesPage" => [
1656 [
1657 "prop" => "invoiceNumber",
1658 "visible" => true,
1659 "width" => 160,
1660 "label" => "red_invoice_number",
1661 "sortable" => true,
1662 "required" => true
1663 ],
1664 [
1665 "prop" => "customer",
1666 "visible" => true,
1667 "width" => 240,
1668 "label" => "customer",
1669 "sortable" => false,
1670 "required" => true
1671 ],
1672 [
1673 "prop" => "dateTime",
1674 "visible" => true,
1675 "width" => 160,
1676 "label" => "red_invoice_date",
1677 "sortable" => true
1678 ],
1679 [
1680 "prop" => "employees",
1681 "visible" => true,
1682 "width" => 240,
1683 "label" => "employees",
1684 "sortable" => false
1685 ],
1686 [
1687 "prop" => "booking",
1688 "visible" => true,
1689 "width" => 320,
1690 "label" => "booking",
1691 "sortable" => false
1692 ],
1693 [
1694 "prop" => "status",
1695 "visible" => true,
1696 "width" => 160,
1697 "label" => "status",
1698 "sortable" => true
1699 ],
1700 [
1701 "prop" => "amount",
1702 "visible" => true,
1703 "width" => 160,
1704 "label" => "total",
1705 "sortable" => false
1706 ],
1707 ],
1708 ];
1709 }
1710
1711 /**
1712 * Update Page Column Settings for existing installations
1713 *
1714 * @param array $defaultSettings
1715 */
1716 private static function updatePageColumnSettings($defaultSettings)
1717 {
1718 $settingsService = new SettingsService(new SettingsStorage());
1719 $savedSettings = $settingsService->getCategorySettings('pageColumnSettings');
1720
1721 // If no saved settings exist, nothing to update
1722 if (!$savedSettings) {
1723 return;
1724 }
1725
1726 $needsUpdate = false;
1727 $updatedSettings = $savedSettings;
1728
1729 foreach ($defaultSettings as $pageKey => $defaultColumns) {
1730 // If page doesn't exist in saved settings, add it
1731 if (!isset($savedSettings[$pageKey])) {
1732 $updatedSettings[$pageKey] = $defaultColumns;
1733 $needsUpdate = true;
1734 continue;
1735 }
1736
1737 // Create a map of existing columns by prop for easy lookup, preserving user customizations
1738 $existingColumnsByProp = [];
1739 foreach ($savedSettings[$pageKey] as $column) {
1740 if (isset($column['prop'])) {
1741 $existingColumnsByProp[$column['prop']] = $column;
1742 }
1743 }
1744
1745 // Rebuild the columns array in the default order, preserving user customizations
1746 $mergedColumns = [];
1747 $addedColumns = [];
1748
1749 // First, add columns in the default order
1750 foreach ($defaultColumns as $defaultColumn) {
1751 if (isset($existingColumnsByProp[$defaultColumn['prop']])) {
1752 // Use existing column but merge missing properties from defaults
1753 $existingColumn = $existingColumnsByProp[$defaultColumn['prop']];
1754
1755 // Check if any properties from default are missing or different in existing column
1756 $columnNeedsUpdate = false;
1757 foreach ($defaultColumn as $key => $value) {
1758 if (!isset($existingColumn[$key])) {
1759 // Add missing property
1760 $existingColumn[$key] = $value;
1761 $columnNeedsUpdate = true;
1762 } elseif ($key === 'label' && $existingColumn[$key] !== $value) {
1763 // Update label if it has changed in defaults
1764 $existingColumn[$key] = $value;
1765 $columnNeedsUpdate = true;
1766 } elseif ($key === 'sortable' && $existingColumn[$key] !== $value) {
1767 // Update sortable if it has changed in defaults
1768 $existingColumn[$key] = $value;
1769 $columnNeedsUpdate = true;
1770 }
1771 }
1772
1773 if ($columnNeedsUpdate) {
1774 $needsUpdate = true;
1775 }
1776
1777 $mergedColumns[] = $existingColumn;
1778 $addedColumns[] = $defaultColumn['prop'];
1779 } else {
1780 // Add new column from defaults
1781 $mergedColumns[] = $defaultColumn;
1782 $addedColumns[] = $defaultColumn['prop'];
1783 $needsUpdate = true;
1784 }
1785 }
1786
1787 // Then, add any existing columns that are not in defaults (user-added columns)
1788 foreach ($existingColumnsByProp as $prop => $column) {
1789 if (!in_array($prop, $addedColumns)) {
1790 $mergedColumns[] = $column;
1791 }
1792 }
1793
1794 // Check if the order or content changed
1795 if ($mergedColumns !== $savedSettings[$pageKey]) {
1796 $updatedSettings[$pageKey] = $mergedColumns;
1797 $needsUpdate = true;
1798 }
1799 }
1800
1801 // Save updated settings if changes were made
1802 if ($needsUpdate) {
1803 $settingsService->setCategorySettings('pageColumnSettings', $updatedSettings);
1804 }
1805 }
1806
1807
1808 /**
1809 * Init Ics Settings
1810 */
1811 private static function initIcsSettings()
1812 {
1813 $settingsService = new SettingsService(new SettingsStorage());
1814
1815 $savedSettings = $settingsService->getCategorySettings('general');
1816
1817 $settings = [
1818 'sendIcsAttachment' => isset($savedSettings['sendIcsAttachment']) ? $savedSettings['sendIcsAttachment'] : false,
1819 'sendIcsAttachmentPending' => false,
1820 'description' => [
1821 'appointment' => '',
1822 'event' => '',
1823 'translations' => [
1824 'appointment' => null,
1825 'event' => null,
1826 ],
1827 ],
1828 ];
1829
1830 self::initSettings('ics', $settings);
1831 }
1832
1833 private static function isSquareTestMode(): bool
1834 {
1835 if (!defined('AMELIA_DEV')) {
1836 return false;
1837 }
1838
1839 return filter_var(
1840 constant('AMELIA_DEV'),
1841 FILTER_VALIDATE_BOOLEAN,
1842 FILTER_NULL_ON_FAILURE
1843 ) === true;
1844 }
1845
1846 /**
1847 * Get Payments Settings
1848 *
1849 * @param array $savedSettings
1850 *
1851 * @return array
1852 */
1853 public static function getDefaultPaymentsSettings($savedSettings)
1854 {
1855 return [
1856 'currency' => 'USD',
1857 'symbol' => '$',
1858 'priceSymbolPosition' => 'before',
1859 'priceNumberOfDecimals' => 2,
1860 'priceSeparator' => 1,
1861 'hideCurrencySymbolFrontend' => false,
1862 'defaultPaymentMethod' => 'onSite',
1863 'onSite' => true,
1864 'couponsCaseInsensitive' => false,
1865 'paymentLinks' => [
1866 'enabled' => false,
1867 'changeBookingStatus' => false,
1868 'redirectUrl' => AMELIA_SITE_URL
1869 ],
1870 'taxes' => [
1871 'excluded' => true,
1872 ],
1873 'payPal' => [
1874 'enabled' => false,
1875 'sandboxMode' => false,
1876 'liveApiClientId' => '',
1877 'liveApiSecret' => '',
1878 'testApiClientId' => '',
1879 'testApiSecret' => '',
1880 'description' => [
1881 'enabled' => false,
1882 'appointment' => '',
1883 'package' => '',
1884 'event' => '',
1885 'cart' => '',
1886 ],
1887 ],
1888 'stripe' => [
1889 'enabled' => false,
1890 'testMode' => false,
1891 'livePublishableKey' => '',
1892 'liveSecretKey' => '',
1893 'testPublishableKey' => '',
1894 'testSecretKey' => '',
1895 'address' => false,
1896 'description' => [
1897 'enabled' => false,
1898 'appointment' => '',
1899 'package' => '',
1900 'event' => '',
1901 'cart' => '',
1902 ],
1903 'metaData' => [
1904 'enabled' => false,
1905 'appointment' => null,
1906 'package' => null,
1907 'event' => null,
1908 'cart' => '',
1909 ],
1910 'manualCapture' => false,
1911 'returnUrl' => '',
1912 'connect' => [
1913 'enabled' => false,
1914 'method' => 'transfer',
1915 'amount' => 0,
1916 'type' => 'percentage',
1917 'capabilities' => ['card_payments', 'transfers'],
1918 ],
1919 ],
1920 'wc' => [
1921 'enabled' => false,
1922 'productId' => '',
1923 'onSiteIfFree' => false,
1924 'page' => 'cart',
1925 'dashboard' => true,
1926 'checkoutData' => [
1927 'appointment' => '',
1928 'package' => '',
1929 'event' => '',
1930 'cart' => '',
1931 'translations' => [
1932 'appointment' => null,
1933 'event' => null,
1934 'package' => null,
1935 'cart' => '',
1936 ],
1937 ],
1938 'skipCheckoutGetValueProcessing' => isset($savedSettings['wc']['skipCheckoutGetValueProcessing']) ?
1939 $savedSettings['wc']['skipCheckoutGetValueProcessing'] : true,
1940 'skipGetItemDataProcessing' => !isset($savedSettings['wc']),
1941 'redirectPage' => 1,
1942 'bookMultiple' => false,
1943 'rules' =>
1944 isset($savedSettings['wc']['rules']) ? $savedSettings['wc']['rules'] : [
1945 'appointment' => [
1946 [
1947 'order' => 'pending',
1948 'booking' => 'pending',
1949 'payment' => 'pending',
1950 'update' => false,
1951 ],
1952 [
1953 'order' => 'on-hold',
1954 'booking' => 'default',
1955 'payment' => 'paid',
1956 'update' => true,
1957 ],
1958 [
1959 'order' => 'processing',
1960 'booking' => 'default',
1961 'payment' => 'paid',
1962 'update' => true,
1963 ],
1964 [
1965 'order' => 'completed',
1966 'booking' => 'default',
1967 'payment' => 'paid',
1968 'update' => true,
1969 ],
1970 [
1971 'order' => 'cancelled',
1972 'booking' => 'canceled',
1973 'payment' => 'pending',
1974 'update' => true,
1975 ],
1976 [
1977 'order' => 'failed',
1978 'booking' => 'canceled',
1979 'payment' => 'pending',
1980 'update' => true,
1981 ],
1982 ],
1983 'package' => [
1984 [
1985 'order' => 'on-hold',
1986 'booking' => 'approved',
1987 'payment' => 'paid',
1988 'update' => true,
1989 ],
1990 [
1991 'order' => 'processing',
1992 'booking' => 'approved',
1993 'payment' => 'paid',
1994 'update' => true,
1995 ],
1996 [
1997 'order' => 'completed',
1998 'booking' => 'approved',
1999 'payment' => 'paid',
2000 'update' => true,
2001 ],
2002 [
2003 'order' => 'cancelled',
2004 'booking' => 'canceled',
2005 'payment' => 'pending',
2006 'update' => true,
2007 ],
2008 [
2009 'order' => 'failed',
2010 'booking' => 'canceled',
2011 'payment' => 'pending',
2012 'update' => true,
2013 ],
2014 ],
2015 'event' => [
2016 [
2017 'order' => 'pending',
2018 'booking' => 'pending',
2019 'payment' => 'pending',
2020 'update' => false,
2021 ],
2022 [
2023 'order' => 'on-hold',
2024 'booking' => 'approved',
2025 'payment' => 'paid',
2026 'update' => true,
2027 ],
2028 [
2029 'order' => 'processing',
2030 'booking' => 'approved',
2031 'payment' => 'paid',
2032 'update' => true,
2033 ],
2034 [
2035 'order' => 'completed',
2036 'booking' => 'approved',
2037 'payment' => 'paid',
2038 'update' => true,
2039 ],
2040 [
2041 'order' => 'cancelled',
2042 'booking' => 'canceled',
2043 'payment' => 'pending',
2044 'update' => true,
2045 ],
2046 [
2047 'order' => 'failed',
2048 'booking' => 'canceled',
2049 'payment' => 'pending',
2050 'update' => true,
2051 ],
2052 ],
2053 ],
2054 ],
2055 'mollie' => [
2056 'enabled' => false,
2057 'testMode' => false,
2058 'liveApiKey' => '',
2059 'testApiKey' => '',
2060 'description' => [
2061 'enabled' => false,
2062 'appointment' => '',
2063 'package' => '',
2064 'event' => '',
2065 'cart' => '',
2066 ],
2067 'metaData' => [
2068 'enabled' => false,
2069 'appointment' => null,
2070 'package' => null,
2071 'event' => null,
2072 'cart' => '',
2073 ],
2074 'method' => [],
2075 'cancelBooking' => false
2076 ],
2077 'razorpay' => [
2078 'enabled' => false,
2079 'testMode' => false,
2080 'liveKeyId' => '',
2081 'liveKeySecret' => '',
2082 'testKeyId' => '',
2083 'testKeySecret' => '',
2084 'description' => [
2085 'enabled' => false,
2086 'appointment' => '',
2087 'package' => '',
2088 'event' => '',
2089 'cart' => '',
2090 ],
2091 'name' => [
2092 'enabled' => false,
2093 'appointment' => '',
2094 'package' => '',
2095 'event' => '',
2096 'cart' => '',
2097 ],
2098 'metaData' => [
2099 'enabled' => false,
2100 'appointment' => null,
2101 'package' => null,
2102 'event' => null,
2103 'cart' => '',
2104 ],
2105 ],
2106 'square' => [
2107 'enabled' => false,
2108 'locationId' => '',
2109 'accessToken' => '',
2110 'testMode' => self::isSquareTestMode(),
2111 'clientLiveId' => 'sq0idp-TtDyGP_2RfKYpFzrDqs0lw',
2112 'clientTestId' => 'sandbox-sq0idb-Wxnxasx1NMG_ZyvM--JV4Q',
2113 'countryCode' => '',
2114 'description' => [
2115 'enabled' => false,
2116 'appointment' => '',
2117 'package' => '',
2118 'event' => '',
2119 'cart' => ''
2120 ],
2121 'metaData' => [
2122 'enabled' => false,
2123 'appointment' => null,
2124 'package' => null,
2125 'event' => null,
2126 'cart' => null
2127 ],
2128 ],
2129 'barion' => [
2130 'enabled' => false,
2131 'sandboxMode' => false,
2132 'livePOSKey' => '',
2133 'sandboxPOSKey' => '',
2134 'payeeEmail' => '',
2135 'description' => [
2136 'enabled' => false,
2137 'appointment' => '',
2138 'package' => '',
2139 'event' => '',
2140 'cart' => '',
2141 ],
2142 'metaData' => [
2143 'enabled' => false,
2144 'appointment' => null,
2145 'package' => null,
2146 'event' => null,
2147 'cart' => null
2148 ],
2149 ],
2150 ];
2151 }
2152
2153 /**
2154 * Init Payments Settings
2155 */
2156 private static function initPaymentsSettings()
2157 {
2158 $settingsService = new SettingsService(new SettingsStorage());
2159
2160 $savedSettings = $settingsService->getCategorySettings('payments');
2161
2162 $settings = self::getDefaultPaymentsSettings($savedSettings);
2163
2164 self::initSettings('payments', $settings);
2165
2166 self::setNewSettingsToExistingSettings(
2167 'payments',
2168 [
2169 ['stripe', 'connect'],
2170 ['stripe', 'connect', 'capabilities'],
2171 ['stripe', 'description'],
2172 ['stripe', 'description', 'package'],
2173 ['stripe', 'description', 'cart'],
2174 ['stripe', 'metaData'],
2175 ['stripe', 'metaData', 'package'],
2176 ['stripe', 'metaData', 'cart'],
2177 ['stripe', 'manualCapture'],
2178 ['stripe', 'returnUrl'],
2179 ['stripe', 'address'],
2180 ['payPal', 'description'],
2181 ['payPal', 'description', 'package'],
2182 ['payPal', 'description', 'cart'],
2183 ['wc', 'onSiteIfFree'],
2184 ['wc', 'page'],
2185 ['wc', 'dashboard'],
2186 ['wc', 'skipCheckoutGetValueProcessing'],
2187 ['wc', 'skipGetItemDataProcessing'],
2188 ['wc', 'rules'],
2189 ['wc', 'redirectPage'],
2190 ['wc', 'bookMultiple'],
2191 ['wc', 'checkoutData'],
2192 ['wc', 'checkoutData', 'package'],
2193 ['wc', 'checkoutData', 'cart'],
2194 ['wc', 'checkoutData', 'translations'],
2195 ['wc', 'checkoutData', 'translations', 'appointment'],
2196 ['wc', 'checkoutData', 'translations', 'event'],
2197 ['wc', 'checkoutData', 'translations', 'package'],
2198 ['wc', 'checkoutData', 'translations', 'cart'],
2199 ['razorpay', 'name'],
2200 ['razorpay', 'description', 'cart'],
2201 ['razorpay', 'metaData', 'cart'],
2202 ['razorpay', 'name', 'cart'],
2203 ['mollie', 'description', 'cart'],
2204 ['mollie', 'metaData', 'cart'],
2205 ['mollie', 'cancelBooking'],
2206 ['square', 'enabled'],
2207 ['square', 'description', 'cart'],
2208 ['square', 'metaData', 'cart'],
2209 ['square', 'locationId'],
2210 ['square', 'accessToken'],
2211 ['square', 'testMode'],
2212 ['square', 'clientLiveId'],
2213 ['square', 'clientTestId'],
2214 ['square', 'countryCode'],
2215 ['barion', 'metaData'],
2216 ],
2217 $settings
2218 );
2219
2220 self::syncSquareTestModeForDev();
2221 }
2222
2223 /**
2224 * Ensure Square sandbox mode is enabled on dev environments for existing installations.
2225 */
2226 private static function syncSquareTestModeForDev(): void
2227 {
2228 if (!self::isSquareTestMode()) {
2229 return;
2230 }
2231
2232 $settingsService = new SettingsService(new SettingsStorage());
2233 $savedSettings = $settingsService->getCategorySettings('payments');
2234
2235 if (empty($savedSettings['square']) || !empty($savedSettings['square']['testMode'])) {
2236 return;
2237 }
2238
2239 $savedSettings['square']['testMode'] = true;
2240 self::initSettings('payments', $savedSettings, true);
2241 }
2242
2243 /**
2244 * Init Purchase Code Settings
2245 */
2246 private static function initActivationSettings()
2247 {
2248 $settingsService = new SettingsService(new SettingsStorage());
2249
2250 $savedSettings = $settingsService->getCategorySettings('activation');
2251
2252 $isNewInstallation = empty($savedSettings);
2253
2254 $settings = [
2255 'showActivationSettings' => true,
2256 'active' => false,
2257 'purchaseCodeStore' => '',
2258 'envatoTokenEmail' => '',
2259 'licenseActivatorUserId' => null,
2260 'version' => '',
2261 'deleteTables' => false,
2262 'showAmeliaPromoCustomizePopup' => true,
2263 'showAmeliaSurvey' => true,
2264 'showWelcomePage' => true,
2265 'stash' => false,
2266 'responseErrorAsConflict' => $savedSettings ? false : true,
2267 'hideTipsAndSuggestions' => false,
2268 'hideUnavailableFeatures' => false,
2269 'licence' => '',
2270 'disableUrlParams' => $savedSettings ? false : true,
2271 'enableThriveItems' => false,
2272 'customUrl' => [
2273 'enabled' => false,
2274 'pluginPath' => '/wp-content/plugins/ameliabooking/',
2275 'ajaxPath' => '/wp-admin/admin-ajax.php',
2276 'uploadsPath' => '',
2277 ],
2278 'v3RelativePath' => false,
2279 'v3AsyncLoading' => false,
2280 'premiumBannerVisibility' => true,
2281 'dismissibleBannerVisibility' => true,
2282 'cronKey' => (new Token(null, 32))->getValue(),
2283 ];
2284
2285 $mergedSettings = array_replace($settings, (array)$savedSettings);
2286
2287 $mergedSettings['showAmeliaPromoCustomizePopup'] = true;
2288
2289 $mergedSettings['isNewInstallation'] = $isNewInstallation;
2290
2291 self::initSettings('activation', $mergedSettings, true);
2292 }
2293
2294 /**
2295 * Init Labels Settings
2296 */
2297 private static function initLabelsSettings()
2298 {
2299 $settings = [
2300 'enabled' => true,
2301 'employee' => 'employee',
2302 'employees' => 'employees',
2303 'service' => 'service',
2304 'services' => 'services'
2305 ];
2306
2307 self::initSettings('labels', $settings);
2308 }
2309
2310 /**
2311 * Init Roles Settings
2312 *
2313 * @return array
2314 */
2315 public static function getDefaultRolesSettings()
2316 {
2317 $permissionChecker = new PermissionsChecker();
2318
2319 return [
2320 'allowConfigureSchedule' => false,
2321 'allowConfigureDaysOff' => false,
2322 'allowConfigureSpecialDays' => false,
2323 'allowConfigureServices' => false,
2324 'allowWriteAppointments' => false,
2325 'allowWriteCustomers' => false,
2326 'allowReadAllCustomers' => $permissionChecker->hasCapability(
2327 Entities::PROVIDER,
2328 'amelia_read_others_customers'
2329 ),
2330 'automaticallyCreateCustomer' => false,
2331 'inspectCustomerInfo' => false,
2332 'allowCustomerReschedule' => false,
2333 'allowCustomerCancelPackages' => true,
2334 'allowCustomerDeleteProfile' => false,
2335 'allowWriteEvents' => false,
2336 'allowAdminBookAtAnyTime' => false,
2337 'allowAdminBookOverApp' => false,
2338 'adminServiceDurationAsSlot' => false,
2339 'enabledHttpAuthorization' => true,
2340 'customerCabinet' => [
2341 'headerJwtSecret' => (new Token(null, 20))->getValue(),
2342 'urlJwtSecret' => (new Token(null, 20))->getValue(),
2343 'tokenValidTime' => 2592000,
2344 'pageUrl' => '',
2345 'loginEnabled' => true,
2346 'filterDate' => false,
2347 'translations' => [],
2348 'googleRecaptcha' => false,
2349 ],
2350 'providerCabinet' => [
2351 'headerJwtSecret' => (new Token(null, 20))->getValue(),
2352 'urlJwtSecret' => (new Token(null, 20))->getValue(),
2353 'tokenValidTime' => 2592000,
2354 'pageUrl' => '',
2355 'loginEnabled' => true,
2356 'filterDate' => false,
2357 'googleRecaptcha' => false,
2358 ],
2359 'urlAttachment' => [
2360 'enabled' => true,
2361 'headerJwtSecret' => (new Token(null, 20))->getValue(),
2362 'urlJwtSecret' => (new Token(null, 20))->getValue(),
2363 'tokenValidTime' => 2592000,
2364 'pageUrl' => '',
2365 'loginEnabled' => true,
2366 'filterDate' => false,
2367 ],
2368 'limitPerCustomerService' => [
2369 'enabled' => false,
2370 'numberOfApp' => 1,
2371 'timeFrame' => 'day',
2372 'period' => 1,
2373 'from' => 'bookingDate'
2374 ],
2375 'limitPerCustomerPackage' => [
2376 'enabled' => false,
2377 'numberOfApp' => 1,
2378 'timeFrame' => 'day',
2379 'period' => 1,
2380 ],
2381 'limitPerCustomerEvent' => [
2382 'enabled' => false,
2383 'numberOfApp' => 1,
2384 'timeFrame' => 'day',
2385 'period' => 1,
2386 'from' => 'bookingDate'
2387 ],
2388 'limitPerEmployee' => [
2389 'enabled' => false,
2390 'numberOfApp' => 1,
2391 'timeFrame' => 'day',
2392 'period' => 1,
2393 ],
2394 'providerBadges' => [
2395 'counter' => 3,
2396 'badges' => [
2397 [
2398 'id' => 1,
2399 'content' => 'Most Popular',
2400 'color' => '#316bff'
2401 ],
2402 [
2403 'id' => 2,
2404 'content' => 'Top Performer',
2405 'color' => '#06a192'
2406 ],
2407 [
2408 'id' => 3,
2409 'content' => 'Exclusive',
2410 'color' => '#facc15'
2411 ],
2412 ]
2413 ],
2414 ];
2415 }
2416
2417 /**
2418 * Init Roles Settings
2419 */
2420 private static function initRolesSettings()
2421 {
2422 $settings = self::getDefaultRolesSettings();
2423
2424 self::initSettings('roles', $settings);
2425
2426 self::setNewSettingsToExistingSettings(
2427 'roles',
2428 [
2429 ['customerCabinet', 'filterDate'],
2430 ['customerCabinet', 'translations'],
2431 ['customerCabinet', 'headerJwtSecret'],
2432 ['customerCabinet', 'urlJwtSecret'],
2433 ['customerCabinet', 'googleRecaptcha'],
2434 ['providerCabinet', 'headerJwtSecret'],
2435 ['providerCabinet', 'urlJwtSecret'],
2436 ['providerCabinet', 'googleRecaptcha'],
2437 ['urlAttachment', 'headerJwtSecret'],
2438 ['urlAttachment', 'urlJwtSecret'],
2439 ],
2440 $settings
2441 );
2442 }
2443
2444 /**
2445 * Get Appointments Settings
2446 *
2447 * @return array
2448 */
2449 public static function getDefaultAppointmentsSettings()
2450 {
2451 return [
2452 'isGloballyBusySlot' => false,
2453 'bookMultipleTimes' => false,
2454 'allowBookingIfPending' => true,
2455 'allowBookingIfNotMin' => true,
2456 'openedBookingAfterMin' => false,
2457 'cartPlaceholders' => '<!-- Content --><p>DateTime: %appointment_date_time%</p>',
2458 'cartPlaceholdersSms' => 'DateTime: %appointment_date_time%',
2459 'cartPlaceholdersCustomer' => '<!-- Content --><p>DateTime: %appointment_date_time%</p>',
2460 'cartPlaceholdersCustomerSms' => 'DateTime: %appointment_date_time%',
2461 'recurringPlaceholders' => 'DateTime: %appointment_date_time%',
2462 'recurringPlaceholdersSms' => 'DateTime: %appointment_date_time%',
2463 'recurringPlaceholdersCustomer' => 'DateTime: %appointment_date_time%',
2464 'recurringPlaceholdersCustomerSms' => 'DateTime: %appointment_date_time%',
2465 'packagePlaceholders' => 'DateTime: %appointment_date_time%',
2466 'packagePlaceholdersSms' => 'DateTime: %appointment_date_time%',
2467 'packagePlaceholdersCustomer' => 'DateTime: %appointment_date_time%',
2468 'packagePlaceholdersCustomerSms' => 'DateTime: %appointment_date_time%',
2469 'groupAppointmentPlaceholder' => 'Name: %customer_full_name%',
2470 'groupEventPlaceholder' => 'Name: %customer_full_name%',
2471 'groupAppointmentPlaceholderSms' => 'Name: %customer_full_name%',
2472 'groupEventPlaceholderSms' => 'Name: %customer_full_name%',
2473 'translations' => [
2474 'cartPlaceholdersCustomer' => null,
2475 'cartPlaceholdersCustomerSms' => null,
2476 'recurringPlaceholdersCustomer' => null,
2477 'recurringPlaceholdersCustomerSms' => null,
2478 'packagePlaceholdersCustomer' => null,
2479 'packagePlaceholdersCustomerSms' => null,
2480 'groupAppointmentPlaceholder' => 'Name: %customer_full_name%',
2481 'groupEventPlaceholder' => 'Name: %customer_full_name%',
2482 'groupAppointmentPlaceholderSms' => 'Name: %customer_full_name%',
2483 'groupEventPlaceholderSms' => 'Name: %customer_full_name%',
2484 ],
2485 'waitingListEvents' => [
2486 'addingMethod' => 'Manually'
2487 ],
2488 'qrCodeEvents' => [
2489 'enabled' => false,
2490 ],
2491 'waitingListAppointments' => [
2492 'enabled' => false,
2493 'redirectUrlDenied' => ''
2494 ],
2495 'pastDaysEvents' => 0,
2496 'employeeSelection' => 'random',
2497 'bringingAnyoneLogic' => 'additional',
2498 'automaticallyCreateEventCustomPost' => false,
2499 ];
2500 }
2501
2502 /**
2503 * Init Appointments Settings
2504 */
2505 private static function initAppointmentsSettings()
2506 {
2507 $settings = self::getDefaultAppointmentsSettings();
2508
2509 self::initSettings('appointments', $settings);
2510
2511 self::setNewSettingsToExistingSettings(
2512 'appointments',
2513 [
2514 ['translations', 'cartPlaceholdersCustomer'],
2515 ['translations', 'cartPlaceholdersCustomerSms'],
2516 ],
2517 $settings
2518 );
2519 }
2520
2521 /**
2522 * Init Web Hooks Settings
2523 */
2524 private static function initWebHooksSettings()
2525 {
2526 $settings = [];
2527
2528 self::initSettings('webHooks', $settings);
2529 }
2530
2531 /**
2532 * get Back Link Setting
2533 */
2534 private static function getBackLinkSetting()
2535 {
2536 $settingsService = new SettingsService(new SettingsStorage());
2537
2538 $backLinksLabels = [
2539 'Generated with Amelia - WordPress Booking Plugin',
2540 'Powered by Amelia - WordPress Booking Plugin',
2541 'Booking by Amelia - WordPress Booking Plugin',
2542 'Powered by Amelia - Appointment and Events Booking Plugin',
2543 'Powered by Amelia - Appointment and Event Booking Plugin',
2544 'Powered by Amelia - WordPress Booking Plugin',
2545 'Generated with Amelia - Appointment and Event Booking Plugin',
2546 'Booking Enabled by Amelia - Appointment and Event Booking Plugin',
2547 ];
2548
2549 $backLinksUrls = [
2550 'https://wpamelia.com/?utm_source=lite&utm_medium=websites&utm_campaign=powerdby',
2551 'https://wpamelia.com/demos/?utm_source=lite&utm_medium=website&utm_campaign=powerdby#Features-list',
2552 'https://wpamelia.com/pricing/?utm_source=lite&utm_medium=website&utm_campaign=powerdby',
2553 'https://wpamelia.com/documentation/?utm_source=lite&utm_medium=website&utm_campaign=powerdby',
2554 ];
2555
2556 return [
2557 'enabled' => $settingsService->getCategorySettings('general') === null,
2558 'label' => $backLinksLabels[rand(0, 7)],
2559 'url' => $backLinksUrls[rand(0, 3)],
2560 ];
2561 }
2562
2563 /**
2564 * Add new settings ti global parent settings
2565 *
2566 * @param string $category
2567 * @param array $pathsKeys
2568 * @param array $initSettings
2569 */
2570 private static function setNewSettingsToExistingSettings($category, $pathsKeys, $initSettings)
2571 {
2572 $settingsService = new SettingsService(new SettingsStorage());
2573
2574 $savedSettings = $settingsService->getCategorySettings($category);
2575
2576 $activationSettings = $settingsService->getCategorySettings('activation');
2577 $savedVersion = !empty($activationSettings['version']) ? $activationSettings['version'] : '0.0.0';
2578
2579 $setSettings = false;
2580
2581 foreach ($pathsKeys as $keys) {
2582 $current = &$savedSettings;
2583
2584 $currentInit = &$initSettings;
2585
2586 foreach ((array)$keys as $key) {
2587 if (!isset($current[$key])) {
2588 $current[$key] = !empty($currentInit[$key]) ? $currentInit[$key] : null;
2589
2590 $setSettings = true;
2591
2592 continue 2;
2593 }
2594
2595 // If the saved value is a JSON-encoded string but the new schema expects
2596 // a nested array (i.e. there are more keys to traverse), decode it so
2597 // that subsequent keys can be accessed without a PHP 8 TypeError.
2598 // This fix is only applied when upgrading from versions before 9.2
2599 if (version_compare($savedVersion, '9.2', '<') && is_string($current[$key])) {
2600 $decoded = json_decode($current[$key], true);
2601 if (is_array($decoded)) {
2602 $current[$key] = $decoded;
2603 $setSettings = true;
2604 } else {
2605 // Scalar string with no deeper path possible — skip this path.
2606 continue 2;
2607 }
2608 } elseif (!is_array($current[$key])) {
2609 // Boolean / int leaf where a nested array is expected — skip.
2610 continue 2;
2611 }
2612
2613 $current = &$current[$key];
2614
2615 $currentInit = &$currentInit[$key];
2616 }
2617 }
2618
2619 if ($setSettings) {
2620 self::initSettings($category, $savedSettings, true);
2621 }
2622 }
2623
2624 /**
2625 * Init Social Login Settings
2626 */
2627 private static function initSocialLoginSettings()
2628 {
2629 $settings = [
2630 'facebookAppId' => '',
2631 'facebookAppSecret' => '',
2632 ];
2633
2634 self::initSettings('socialLogin', $settings);
2635 }
2636
2637 /**
2638 * Init Features and Integrations Settings
2639 */
2640 private static function initFeaturesIntegrationsSettings()
2641 {
2642 $settingsService = new SettingsService(new SettingsStorage());
2643
2644 $saved = $settingsService->getAllSettingsCategorized();
2645
2646 $old = empty($saved['activation']['isNewInstallation']);
2647
2648 $starterAndUp = Licence::getLicence() === 'Developer' ||
2649 Licence::getLicence() === 'Pro' ||
2650 Licence::getLicence() === 'Basic' ||
2651 Licence::getLicence() === 'Starter';
2652
2653 $basicAndUp = Licence::getLicence() === 'Developer' || Licence::getLicence() === 'Pro' || Licence::getLicence() === 'Basic';
2654
2655 $proAndUp = Licence::getLicence() === 'Developer' || Licence::getLicence() === 'Pro';
2656
2657 $settings = [
2658 'googleCalendar' => [
2659 'enabled' => $old &&
2660 $basicAndUp &&
2661 !empty($saved['googleCalendar']['calendarEnabled']),
2662 ],
2663 'appleCalendar' => [
2664 'enabled' => $old &&
2665 $basicAndUp &&
2666 !empty($saved['appleCalendar']['clientID']) &&
2667 !empty($saved['appleCalendar']['clientSecret']),
2668 ],
2669 'outlookCalendar' => [
2670 'enabled' => $old &&
2671 $basicAndUp &&
2672 !empty($saved['outlookCalendar']['calendarEnabled']),
2673 ],
2674 'zoom' => [
2675 'enabled' => $old &&
2676 $basicAndUp &&
2677 !empty($saved['zoom']['accountId']) &&
2678 !empty($saved['zoom']['clientId']) &&
2679 !empty($saved['zoom']['clientSecret']),
2680 ],
2681 'webhooks' => [
2682 'enabled' => $basicAndUp,
2683 ],
2684 'facebookPixel' => [
2685 'enabled' => $old && $starterAndUp && !empty($saved['facebookPixel']['id']),
2686 ],
2687 'googleAnalytics' => [
2688 'enabled' => $old && $starterAndUp && !empty($saved['googleAnalytics']['id']),
2689 ],
2690 'lessonSpace' => [
2691 'enabled' => $old && $starterAndUp && !empty($saved['lessonSpace']['apiKey']),
2692 ],
2693 'recaptcha' => [
2694 'enabled' => $old &&
2695 $starterAndUp &&
2696 !empty($saved['general']['googleRecaptcha']['enabled']) &&
2697 !empty($saved['general']['googleRecaptcha']['siteKey']) &&
2698 !empty($saved['general']['googleRecaptcha']['secret']),
2699 ],
2700 'mollie' => [
2701 'enabled' => $old && $basicAndUp && !empty($saved['payments']['mollie']['enabled']),
2702 ],
2703 'wc' => [
2704 'enabled' => $old && $basicAndUp && !empty($saved['payments']['wc']['enabled']),
2705 ],
2706 'payPal' => [
2707 'enabled' => $old && $basicAndUp && !empty($saved['payments']['payPal']['enabled']),
2708 ],
2709 'stripe' => [
2710 'enabled' => $old && $basicAndUp && !empty($saved['payments']['stripe']['enabled']),
2711 ],
2712 'razorpay' => [
2713 'enabled' => $old && $basicAndUp && !empty($saved['payments']['razorpay']['enabled']),
2714 ],
2715 'square' => [
2716 'enabled' => $old && !empty($saved['payments']['square']['enabled']),
2717 ],
2718 'barion' => [
2719 'enabled' => $old && $basicAndUp && !empty($saved['payments']['barion']['enabled']),
2720 ],
2721 'packages' => [
2722 'enabled' => $proAndUp,
2723 ],
2724 'resources' => [
2725 'enabled' => $proAndUp,
2726 ],
2727 'customFields' => [
2728 'enabled' => $basicAndUp,
2729 ],
2730 'coupons' => [
2731 'enabled' => $old && $starterAndUp && !empty($saved['payments']['coupons']),
2732 ],
2733 'customNotifications' => [
2734 'enabled' => $basicAndUp,
2735 ],
2736 'tax' => [
2737 'enabled' => $basicAndUp && (!$old || !empty($saved['payments']['taxes']['enabled'])),
2738 ],
2739 'invoices' => [
2740 'enabled' => $basicAndUp,
2741 ],
2742 'whatsapp' => [
2743 'enabled' => $old &&
2744 $proAndUp &&
2745 !empty($saved['notifications']['whatsAppEnabled']) &&
2746 !empty($saved['notifications']['whatsAppPhoneID']) &&
2747 !empty($saved['notifications']['whatsAppAccessToken']) &&
2748 !empty($saved['notifications']['whatsAppBusinessID']),
2749 ],
2750 'recurringEvents' => [
2751 'enabled' => $basicAndUp,
2752 ],
2753 'tickets' => [
2754 'enabled' => $basicAndUp,
2755 ],
2756 'waitingList' => [
2757 'enabled' => $proAndUp && (!$old || !empty($saved['appointments']['waitingListEvents']['enabled'])),
2758 ],
2759 'waitingListAppointments' => [
2760 'enabled' => $proAndUp && (!$old || !empty($saved['appointments']['waitingListAppointments']['enabled'])),
2761 ],
2762 'customPricing' => [
2763 'enabled' => $basicAndUp,
2764 ],
2765 'recurringAppointments' => [
2766 'enabled' => $basicAndUp,
2767 ],
2768 'extras' => [
2769 'enabled' => $starterAndUp,
2770 ],
2771 'cart' => [
2772 'enabled' => $old && $proAndUp && !empty($saved['payments']['cart']),
2773 ],
2774 'timezones' => [
2775 'enabled' => $basicAndUp,
2776 ],
2777 'depositPayment' => [
2778 'enabled' => $basicAndUp,
2779 ],
2780 'noShowTag' => [
2781 'enabled' => $basicAndUp && (!$old || !empty($saved['roles']['enableNoShowTag'])),
2782 ],
2783 'apis' => [
2784 'enabled' => Licence::getLicence() === 'Developer',
2785 ],
2786 'whiteLabel' => [
2787 'enabled' => false,
2788 ],
2789 'buddyboss' => [
2790 'enabled' => $basicAndUp && $old,
2791 ],
2792 'employeeBadge' => [
2793 'enabled' => $basicAndUp,
2794 ],
2795 'mailchimp' => [
2796 'enabled' => $old && $basicAndUp && !empty($saved['mailchimp']['accessToken']),
2797 ],
2798 'googleSocialLogin' => [
2799 'enabled' => $old && $basicAndUp && !empty($saved['socialLogin']['enableGoogleLogin']),
2800 ],
2801 'facebookSocialLogin' => [
2802 'enabled' => $old && $basicAndUp && !empty($saved['socialLogin']['enableFacebookLogin']),
2803 ],
2804 'eTickets' => [
2805 'enabled' => $proAndUp && (!$old || !empty($saved['appointments']['qrCodeEvents']['enabled'])),
2806 ],
2807 'eventTags' => [
2808 'enabled' => $starterAndUp && (!$old || EventsTagsTable::hasTags()),
2809 ],
2810 'ivy' => [
2811 'enabled' => false,
2812 ],
2813 ];
2814
2815 self::initSettings('featuresIntegrations', $settings);
2816 }
2817
2818 /**
2819 * Init White Label Settings
2820 */
2821 private static function initWhiteLabelSettings()
2822 {
2823 $settings = [
2824 'pluginName' => '',
2825 'hideExternalLinks' => false,
2826 'pictureFullPath' => '',
2827 'pictureThumbPath' => '',
2828 ];
2829
2830 self::initSettings('whiteLabel', $settings);
2831 }
2832 }
2833