4,'online_booking_cutoff_mode'=>'none','online_booking_cutoff_hours'=>null,'booking_contact_phone'=>'','confirmation_email_intro'=>[],'confirmation_email_outro'=>[]]; public function __construct(private readonly SettingsRepository $settings) {} /** Stored overrides remain separate from the effective values edited by the client. */ public function response(): array { $data=$this->get();$metadata=[]; foreach(['confirmation_email_intro','confirmation_email_outro'] as $key){ $custom=$data[$key];$effective=[];$defaults=[];$isCustom=[];$resolved=[]; foreach(ConfirmationEmailDefaults::LOCALES as $locale){ $defaults[$locale]=ConfirmationEmailDefaults::text($key,$locale); $isCustom[$locale]=isset($custom[$locale])&&trim($custom[$locale])!==''; $effective[$locale]=$isCustom[$locale]?$custom[$locale]:$defaults[$locale]; $siteLocale=get_option('WPLANG')?:(defined('WPLANG')&&WPLANG?WPLANG:'en_US'); $resolved[$locale]=$this->resolveEmailText($key,$locale,$siteLocale); } $metadata[$key]=['custom'=>(object)$custom,'defaults'=>(object)$defaults,'is_custom'=>(object)$isCustom,'resolved'=>(object)$resolved]; $data[$key]=(object)$effective; } $data['confirmation_email_metadata']=$metadata;return $data; } public function get(): array { $data=[]; foreach(self::DEFAULTS as $key=>$default)$data[$key]=get_option('abece_booking_'.$key,$default); $data['max_daily_customer_bookings']=(int)$data['max_daily_customer_bookings'];$data['online_booking_cutoff_hours']=$data['online_booking_cutoff_hours']===null||$data['online_booking_cutoff_hours']===''?null:(int)$data['online_booking_cutoff_hours'];return $data; } public function save(array $input): array { BookingValidator::fields($input,[...array_keys(self::DEFAULTS),'reset_confirmation_email_locales']); $reset=array_key_exists('reset_confirmation_email_locales',$input)?$input['reset_confirmation_email_locales']:[];unset($input['reset_confirmation_email_locales']); if(!is_array($reset)||!array_is_list($reset)||array_filter($reset,static fn($locale)=>!is_string($locale)||!in_array($locale,ConfirmationEmailDefaults::LOCALES,true)))CatalogValidator::invalid('reset_confirmation_email_locales',__('Select a supported email language.','abece-booking')); $stored=$this->get();$data=array_replace($stored,$input); $data['max_daily_customer_bookings']=CatalogValidator::integer($data['max_daily_customer_bookings'],'max_daily_customer_bookings',1,1000); $data['online_booking_cutoff_mode']=CatalogValidator::choice($data['online_booking_cutoff_mode'],'online_booking_cutoff_mode',['none','hours_before','previous_day']); $data['online_booking_cutoff_hours']=CatalogValidator::integer($data['online_booking_cutoff_hours'],'online_booking_cutoff_hours',1,8760,true); if($data['online_booking_cutoff_mode']==='hours_before'&&$data['online_booking_cutoff_hours']===null)CatalogValidator::invalid('online_booking_cutoff_hours',__('Enter the number of hours before the booking starts.','abece-booking')); if(!is_string($data['booking_contact_phone']))CatalogValidator::invalid('booking_contact_phone',__('A text value is required.','abece-booking')); $data['booking_contact_phone']=sanitize_text_field($data['booking_contact_phone']);if(mb_strlen($data['booking_contact_phone'])>60||($data['booking_contact_phone']!==''&&!preg_match('/^[+0-9().\s-]+$/uD',$data['booking_contact_phone'])))CatalogValidator::invalid('booking_contact_phone',__('Enter a valid contact phone number.','abece-booking')); foreach(['confirmation_email_intro','confirmation_email_outro'] as $key){ if(!is_array($data[$key])||($data[$key]!==[]&&array_is_list($data[$key])))CatalogValidator::invalid($key,__('Provide email texts by locale.','abece-booking')); foreach($data[$key] as $locale=>$text){if(!is_string($locale)||($locale!=='uk'&&!preg_match('/^[a-z]{2,3}_[A-Z]{2}$/D',$locale))||!is_string($text)||mb_strlen($text)>5000)CatalogValidator::invalid($key,__('Use valid locales and texts of at most 5000 characters.','abece-booking')); // Preserve untouched existing text byte-for-byte, including surrounding line breaks. if(!array_key_exists($locale,$stored[$key])||$text!==$stored[$key][$locale])$data[$key][$locale]=sanitize_textarea_field($text); } } foreach($reset as $locale)foreach(['confirmation_email_intro','confirmation_email_outro'] as $key)unset($data[$key][$locale]); $this->settings->save($data);return $this->get(); } public function emailText(string $key,string $userLocale,string $siteLocale): string { return $this->resolveEmailText($key,$userLocale,$siteLocale)['value']; } /** Actual notification value, including the unchanged site-custom fallback. */ public function resolveEmailText(string $key,string $userLocale,string $siteLocale): array { $texts=$this->get()[$key];$requested=\ABeCe\Booking\Support\LocaleContext::language($userLocale);$site=\ABeCe\Booking\Support\LocaleContext::language($siteLocale); $custom=function(array $locales)use($texts):?array{foreach(array_unique(array_filter($locales)) as $locale){$text=(string)($texts[$locale]??'');if(trim($text)!=='')return ['value'=>$text,'is_custom'=>true,'locale'=>$locale];}return null;}; $value=$custom([$userLocale,$requested]);if($value)return $value; // Preserve en_US/en_GB same-language site-custom fallback without injecting a foreign language. if($requested&&$site&&explode('_',$requested)[0]===explode('_',$site)[0]){$value=$custom([$siteLocale,$site]);if($value)return $value;} if($requested)return ['value'=>ConfirmationEmailDefaults::text($key,$requested),'is_custom'=>false,'locale'=>$requested]; $value=$custom([$siteLocale,$site]);if($value)return $value; $locale=$site??'en_US';return ['value'=>ConfirmationEmailDefaults::text($key,$locale),'is_custom'=>false,'locale'=>$locale]; } }