Changeset 3408023
- Timestamp:
- 12/02/2025 01:09:03 PM (3 months ago)
- Location:
- reventor-calendar-appointment-booking/trunk
- Files:
-
- 6 edited
-
assets/js/frontend.js (modified) (1 diff)
-
includes/class-frontend.php (modified) (5 diffs)
-
includes/email-functions.php (modified) (2 diffs)
-
includes/functions.php (modified) (1 diff)
-
readme.txt (modified) (2 diffs)
-
reventor-calendar-appointment-booking.php (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
reventor-calendar-appointment-booking/trunk/assets/js/frontend.js
r3407895 r3408023 920 920 const startingDayOfWeek = firstDay.getDay(); 921 921 922 // Create header 923 const monthNames = [ 924 'January', 'February', 'March', 'April', 'May', 'June', 925 'July', 'August', 'September', 'October', 'November', 'December' 926 ]; 927 928 const weekdayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; 922 // Create header - use localized names from PHP 923 const monthNames = reventorcab_frontend.locale.month_names; 924 const weekdayNames = reventorcab_frontend.locale.weekday_names; 929 925 930 926 let html = ` -
reventor-calendar-appointment-booking/trunk/includes/class-frontend.php
r3407895 r3408023 25 25 wp_enqueue_style('reventorcab-frontend-style', REVENTORCAB_PLUGIN_URL . 'assets/css/frontend.css', ['dashicons'], REVENTORCAB_VERSION); 26 26 wp_enqueue_script('reventorcab-frontend-script', REVENTORCAB_PLUGIN_URL . 'assets/js/frontend.js', ['jquery'], REVENTORCAB_VERSION, true); 27 27 28 28 $theme_color = get_option('reventorcab_theme_color', '#007cba'); 29 29 30 30 // Add inline CSS to apply theme color to success message 31 31 $custom_css = " … … 41 41 "; 42 42 wp_add_inline_style('reventorcab-frontend-style', wp_strip_all_tags($custom_css)); 43 43 44 44 // Get available dates for calendar picker 45 45 $available_dates = reventorcab_get_available_dates(); 46 46 47 // Detect browser language for JavaScript strings and calendar localization 48 $use_german = false; 49 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { 50 $accept_lang = sanitize_text_field(wp_unslash($_SERVER['HTTP_ACCEPT_LANGUAGE'])); 51 $languages = explode(',', $accept_lang); 52 53 foreach ($languages as $lang) { 54 $lang = trim(explode(';', $lang)[0]); 55 if (strpos($lang, 'de') === 0) { 56 $use_german = true; 57 break; 58 } 59 } 60 } 61 62 // Get localized strings based on detected language 63 $strings = $this->get_localized_strings($use_german); 64 47 65 wp_localize_script('reventorcab-frontend-script', 'reventorcab_frontend', [ 48 66 'ajax_url' => admin_url('admin-ajax.php'), … … 57 75 'time_format' => get_option('reventorcab_time_format', '24h') 58 76 ], 59 'strings' => [ 60 'loading' => __('Loading...', 'reventor-calendar-appointment-booking'), 61 'no_slots' => __('No available time slots for this date.', 'reventor-calendar-appointment-booking'), 62 'booking_success' => __('Appointment booked successfully!', 'reventor-calendar-appointment-booking'), 63 'booking_error' => __('Error booking appointment. Please try again.', 'reventor-calendar-appointment-booking'), 64 'required_fields' => __('Please fill in all required fields.', 'reventor-calendar-appointment-booking'), 65 'invalid_email' => __('Please enter a valid email address.', 'reventor-calendar-appointment-booking'), 66 'syncing_calendar' => __('Syncing with calendar...', 'reventor-calendar-appointment-booking'), 67 'outside_working_hours' => __('This date is outside working hours.', 'reventor-calendar-appointment-booking'), 68 'minimum_advance_required' => __('Please select a date that meets the minimum advance time requirement.', 'reventor-calendar-appointment-booking') 77 'strings' => $strings, 78 'locale' => [ 79 'language' => $use_german ? 'de' : 'en', 80 'month_names' => $use_german ? $this->get_german_month_names() : $this->get_english_month_names(), 81 'weekday_names' => $use_german ? $this->get_german_weekday_names() : $this->get_english_weekday_names() 69 82 ] 70 83 ]); 71 84 72 85 // Add dynamic CSS for theme color 73 86 wp_add_inline_style('reventorcab-frontend-style', $this->get_dynamic_css($theme_color)); 74 87 } 88 } 89 90 /** 91 * Get localized strings based on detected language 92 */ 93 private function get_localized_strings($use_german) { 94 if ($use_german) { 95 // Load German translations 96 $po_file = REVENTORCAB_PLUGIN_PATH . 'languages/easy-calendar-appointment-booking-de_DE.po'; 97 if (file_exists($po_file)) { 98 $german_translations = $this->parse_po_file($po_file); 99 100 return [ 101 'loading' => $german_translations['Loading...'] ?? 'Lade...', 102 'no_slots' => $german_translations['No available time slots for this date.'] ?? 'Keine verfügbaren Zeitslots für dieses Datum.', 103 'booking_success' => $german_translations['Appointment booked successfully!'] ?? 'Termin erfolgreich gebucht!', 104 'booking_error' => $german_translations['Error booking appointment. Please try again.'] ?? 'Fehler beim Buchen des Termins. Bitte versuchen Sie es erneut.', 105 'required_fields' => $german_translations['Please fill in all required fields.'] ?? 'Bitte füllen Sie alle Pflichtfelder aus.', 106 'invalid_email' => $german_translations['Please enter a valid email address.'] ?? 'Bitte geben Sie eine gültige E-Mail-Adresse ein.', 107 'syncing_calendar' => $german_translations['Syncing with calendar...'] ?? 'Synchronisiere mit Kalender...', 108 'outside_working_hours' => $german_translations['This date is outside working hours.'] ?? 'Dieses Datum liegt außerhalb der Arbeitszeiten.', 109 'minimum_advance_required' => $german_translations['Please select a date that meets the minimum advance time requirement.'] ?? 'Bitte wählen Sie ein Datum, das die Mindestvorlaufzeit erfüllt.' 110 ]; 111 } 112 } 113 114 // Default English strings 115 return [ 116 'loading' => 'Loading...', 117 'no_slots' => 'No available time slots for this date.', 118 'booking_success' => 'Appointment booked successfully!', 119 'booking_error' => 'Error booking appointment. Please try again.', 120 'required_fields' => 'Please fill in all required fields.', 121 'invalid_email' => 'Please enter a valid email address.', 122 'syncing_calendar' => 'Syncing with calendar...', 123 'outside_working_hours' => 'This date is outside working hours.', 124 'minimum_advance_required' => 'Please select a date that meets the minimum advance time requirement.' 125 ]; 126 } 127 128 /** 129 * Get English month names 130 */ 131 private function get_english_month_names() { 132 return [ 133 'January', 'February', 'March', 'April', 'May', 'June', 134 'July', 'August', 'September', 'October', 'November', 'December' 135 ]; 136 } 137 138 /** 139 * Get German month names 140 */ 141 private function get_german_month_names() { 142 return [ 143 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 144 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' 145 ]; 146 } 147 148 /** 149 * Get English weekday names 150 */ 151 private function get_english_weekday_names() { 152 return ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; 153 } 154 155 /** 156 * Get German weekday names 157 */ 158 private function get_german_weekday_names() { 159 return ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']; 160 } 161 162 /** 163 * Parse a .po file and return translations array 164 */ 165 private function parse_po_file($po_file) { 166 $translations = []; 167 $content = file_get_contents($po_file); 168 169 if ($content === false) { 170 return $translations; 171 } 172 173 // Simple PO file parser 174 $lines = explode("\n", $content); 175 $current_msgid = ''; 176 $current_msgstr = ''; 177 $in_msgid = false; 178 $in_msgstr = false; 179 180 foreach ($lines as $line) { 181 $line = trim($line); 182 183 if (strpos($line, 'msgid "') === 0) { 184 // Save previous translation if exists 185 if (!empty($current_msgid) && !empty($current_msgstr)) { 186 $translations[$current_msgid] = $current_msgstr; 187 } 188 189 $current_msgid = substr($line, 7, -1); // Remove msgid " and " 190 $current_msgstr = ''; 191 $in_msgid = true; 192 $in_msgstr = false; 193 } elseif (strpos($line, 'msgstr "') === 0) { 194 $current_msgstr = substr($line, 8, -1); // Remove msgstr " and " 195 $in_msgid = false; 196 $in_msgstr = true; 197 } elseif (strpos($line, '"') === 0 && $in_msgstr) { 198 // Continuation of msgstr 199 $current_msgstr .= substr($line, 1, -1); 200 } elseif (strpos($line, '"') === 0 && $in_msgid) { 201 // Continuation of msgid 202 $current_msgid .= substr($line, 1, -1); 203 } 204 } 205 206 // Save last translation 207 if (!empty($current_msgid) && !empty($current_msgstr)) { 208 $translations[$current_msgid] = $current_msgstr; 209 } 210 211 return $translations; 75 212 } 76 213 … … 846 983 $jitsi_url = 'https://meet.jit.si/' . $jitsi_room_id; 847 984 985 // Detect browser language 986 $browser_language = 'en'; 987 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { 988 $accept_lang = sanitize_text_field(wp_unslash($_SERVER['HTTP_ACCEPT_LANGUAGE'])); 989 $languages = explode(',', $accept_lang); 990 $browser_language = trim(explode(';', $languages[0])[0]); 991 } 992 848 993 // Prepare appointment data for CalDAV and action hook 849 994 $appointment_data = [ … … 858 1003 'jitsi_url' => $jitsi_url, 859 1004 'user_timezone' => $user_timezone, 860 'user_timezone_offset' => $user_timezone_offset 1005 'user_timezone_offset' => $user_timezone_offset, 1006 'browser_language' => $browser_language 861 1007 ]; 862 1008 -
reventor-calendar-appointment-booking/trunk/includes/email-functions.php
r3352327 r3408023 32 32 */ 33 33 function reventorcab_send_appointment_confirmation_email($appointment_id, $appointment_data, $is_update = false) { 34 34 35 // Detect browser language from appointment data or default to site language 36 $use_german = false; 37 if (isset($appointment_data['browser_language']) && strpos($appointment_data['browser_language'], 'de') === 0) { 38 $use_german = true; 39 } 40 41 // Set up translations if German is detected 42 if ($use_german) { 43 $po_file = REVENTORCAB_PLUGIN_PATH . 'languages/easy-calendar-appointment-booking-de_DE.po'; 44 if (file_exists($po_file)) { 45 $german_translations = reventorcab_parse_po_file($po_file); 46 add_filter('gettext', function($translated_text, $text, $domain) use ($german_translations) { 47 if ($domain === 'reventor-calendar-appointment-booking' && isset($german_translations[$text])) { 48 return $german_translations[$text]; 49 } 50 return $translated_text; 51 }, 10, 3); 52 } 53 } 54 35 55 // Get recipient email 36 56 $to = $appointment_data['email']; 37 57 38 58 // Get site info 39 59 $site_name = get_bloginfo('name'); 40 60 $admin_email = get_option('admin_email'); 41 61 42 62 // Get user timezone for proper formatting 43 63 $user_timezone = isset($appointment_data['user_timezone']) ? $appointment_data['user_timezone'] : null; 44 64 45 65 // Format date and time for display in user's timezone 46 66 $formatted_date = reventorcab_format_date($appointment_data['appointment_date'], null, $user_timezone); 47 67 $formatted_time = reventorcab_format_time($appointment_data['appointment_time'], null, $user_timezone, $appointment_data['appointment_date']); 48 68 49 69 // Use the Jitsi Meet URL that was already generated and passed in appointment data 50 70 // No need to generate a new one here - it should be consistent across all components 51 71 52 72 // Set email subject and heading based on whether this is a new booking or an update 53 73 if ($is_update) { … … 285 305 } 286 306 307 /** 308 * Parse a .po file and return translations array 309 */ 310 function reventorcab_parse_po_file($po_file) { 311 $translations = []; 312 $content = file_get_contents($po_file); 313 314 if ($content === false) { 315 return $translations; 316 } 317 318 // Simple PO file parser 319 $lines = explode("\n", $content); 320 $current_msgid = ''; 321 $current_msgstr = ''; 322 $in_msgid = false; 323 $in_msgstr = false; 324 325 foreach ($lines as $line) { 326 $line = trim($line); 327 328 if (strpos($line, 'msgid "') === 0) { 329 // Save previous translation if exists 330 if (!empty($current_msgid) && !empty($current_msgstr)) { 331 $translations[$current_msgid] = $current_msgstr; 332 } 333 334 $current_msgid = substr($line, 7, -1); // Remove msgid " and " 335 $current_msgstr = ''; 336 $in_msgid = true; 337 $in_msgstr = false; 338 } elseif (strpos($line, 'msgstr "') === 0) { 339 $current_msgstr = substr($line, 8, -1); // Remove msgstr " and " 340 $in_msgid = false; 341 $in_msgstr = true; 342 } elseif (strpos($line, '"') === 0 && $in_msgstr) { 343 // Continuation of msgstr 344 $current_msgstr .= substr($line, 1, -1); 345 } elseif (strpos($line, '"') === 0 && $in_msgid) { 346 // Continuation of msgid 347 $current_msgid .= substr($line, 1, -1); 348 } 349 } 350 351 // Save last translation 352 if (!empty($current_msgid) && !empty($current_msgstr)) { 353 $translations[$current_msgid] = $current_msgstr; 354 } 355 356 return $translations; 357 } 358 287 359 // VTIMEZONE generation functions removed - now using UTC timestamps for CalDAV compatibility -
reventor-calendar-appointment-booking/trunk/includes/functions.php
r3407895 r3408023 98 98 try { 99 99 $tz = new DateTimeZone($timezone); 100 100 101 101 // If we have a date, combine it with time for proper timezone conversion 102 102 if ($date) { 103 // Parse in UTC first to avoid server timezone dependency 104 $datetime = new DateTime($date . ' ' . $time, new DateTimeZone('UTC')); 105 $datetime->setTimezone($tz); 103 // Parse directly in the target timezone to avoid double conversion 104 $datetime = new DateTime($date . ' ' . $time, $tz); 106 105 } else { 107 // For time-only, assume today's date in UTC first 108 $datetime = new DateTime('today ' . $time, new DateTimeZone('UTC')); 109 $datetime->setTimezone($tz); 106 // For time-only, assume today's date in target timezone 107 $datetime = new DateTime('today ' . $time, $tz); 110 108 } 111 109 -
reventor-calendar-appointment-booking/trunk/readme.txt
r3407895 r3408023 3 3 Tags: appointments, booking, calendar, scheduling 4 4 Requires at least: 6.3 5 Tested up to: 6. 86 Stable tag: 1.1. 05 Tested up to: 6.9 6 Stable tag: 1.1.1 7 7 Requires PHP: 8.1 8 8 License: GPL v2 or later … … 42 42 == Changelog == 43 43 44 = 1.1.1 = 45 * Fixed a language translation 46 * Tested the plugin for WordPress 6.9 47 44 48 = 1.1.0 = 45 49 * Added automatic saving in admin settings -
reventor-calendar-appointment-booking/trunk/reventor-calendar-appointment-booking.php
r3407895 r3408023 4 4 * Description: A REVENTOR calendar appointment booking system with CalDAV integration for seamless scheduling and calendar synchronization. 5 5 * Plugin URI: https://wordpress.org/plugins/reventor-calendar-appointment-booking/ 6 * Version: 1.1. 06 * Version: 1.1.1 7 7 * Author: REVENTOR 8 8 * Author URI: https://reventor.eu … … 40 40 define('REVENTORCAB_PLUGIN_URL', plugin_dir_url(__FILE__)); 41 41 define('REVENTORCAB_PLUGIN_PATH', plugin_dir_path(__FILE__)); 42 define('REVENTORCAB_VERSION', '1.1. 0');42 define('REVENTORCAB_VERSION', '1.1.1'); 43 43 44 44 // Main plugin class … … 106 106 'type' => 'default' 107 107 ), $atts); 108 108 109 // Detect browser language and set up translation filter 110 $this->setup_booking_form_translations(); 111 109 112 ob_start(); 110 113 include REVENTORCAB_PLUGIN_PATH . 'templates/booking-form.php'; 111 114 return ob_get_clean(); 115 } 116 117 /** 118 * Set up translations for booking form based on browser language 119 */ 120 private function setup_booking_form_translations() { 121 static $translations_loaded = false; 122 123 if ($translations_loaded) { 124 return; // Already set up 125 } 126 127 if (defined('WP_DEBUG') && WP_DEBUG) { 128 error_log('REVENTORCAB: setup_booking_form_translations called'); 129 } 130 131 // Detect browser language 132 $use_german = false; 133 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { 134 $accept_lang = sanitize_text_field(wp_unslash($_SERVER['HTTP_ACCEPT_LANGUAGE'])); 135 if (defined('WP_DEBUG') && WP_DEBUG) { 136 error_log('REVENTORCAB: Accept-Language: ' . $accept_lang); 137 } 138 139 $languages = explode(',', $accept_lang); 140 141 foreach ($languages as $lang) { 142 $lang = trim(explode(';', $lang)[0]); // Remove quality values 143 144 if (defined('WP_DEBUG') && WP_DEBUG) { 145 error_log('REVENTORCAB: Checking language: ' . $lang); 146 } 147 148 // Check for German variants 149 if (strpos($lang, 'de') === 0) { 150 $use_german = true; 151 if (defined('WP_DEBUG') && WP_DEBUG) { 152 error_log('REVENTORCAB: German detected, will use German translations'); 153 } 154 break; 155 } 156 } 157 } 158 159 if ($use_german) { 160 // Load German translations from the .po file 161 $po_file = REVENTORCAB_PLUGIN_PATH . 'languages/easy-calendar-appointment-booking-de_DE.po'; 162 if (file_exists($po_file)) { 163 if (defined('WP_DEBUG') && WP_DEBUG) { 164 error_log('REVENTORCAB: Loading German translations from: ' . $po_file); 165 } 166 167 // Parse the .po file and create a translation array 168 $german_translations = $this->parse_po_file($po_file); 169 170 if (!empty($german_translations)) { 171 // Add gettext filter to intercept translations 172 add_filter('gettext', function($translated_text, $text, $domain) use ($german_translations) { 173 if ($domain === 'reventor-calendar-appointment-booking' && isset($german_translations[$text])) { 174 if (defined('WP_DEBUG') && WP_DEBUG) { 175 error_log('REVENTORCAB: Translating "' . $text . '" to "' . $german_translations[$text] . '"'); 176 } 177 return $german_translations[$text]; 178 } 179 return $translated_text; 180 }, 10, 3); 181 } 182 } else { 183 if (defined('WP_DEBUG') && WP_DEBUG) { 184 error_log('REVENTORCAB: German PO file not found: ' . $po_file); 185 } 186 } 187 } 188 189 $translations_loaded = true; 190 } 191 192 /** 193 * Parse a .po file and return translations array 194 */ 195 private function parse_po_file($po_file) { 196 $translations = []; 197 $content = file_get_contents($po_file); 198 199 if ($content === false) { 200 return $translations; 201 } 202 203 // Simple PO file parser 204 $lines = explode("\n", $content); 205 $current_msgid = ''; 206 $current_msgstr = ''; 207 $in_msgid = false; 208 $in_msgstr = false; 209 210 foreach ($lines as $line) { 211 $line = trim($line); 212 213 if (strpos($line, 'msgid "') === 0) { 214 // Save previous translation if exists 215 if (!empty($current_msgid) && !empty($current_msgstr)) { 216 $translations[$current_msgid] = $current_msgstr; 217 } 218 219 $current_msgid = substr($line, 7, -1); // Remove msgid " and " 220 $current_msgstr = ''; 221 $in_msgid = true; 222 $in_msgstr = false; 223 } elseif (strpos($line, 'msgstr "') === 0) { 224 $current_msgstr = substr($line, 8, -1); // Remove msgstr " and " 225 $in_msgid = false; 226 $in_msgstr = true; 227 } elseif (strpos($line, '"') === 0 && $in_msgstr) { 228 // Continuation of msgstr 229 $current_msgstr .= substr($line, 1, -1); 230 } elseif (strpos($line, '"') === 0 && $in_msgid) { 231 // Continuation of msgid 232 $current_msgid .= substr($line, 1, -1); 233 } 234 } 235 236 // Save last translation 237 if (!empty($current_msgid) && !empty($current_msgstr)) { 238 $translations[$current_msgid] = $current_msgstr; 239 } 240 241 return $translations; 112 242 } 113 243 … … 151 281 152 282 public function load_textdomain() { 153 // WordPress automatically loads translations for plugins hosted on WordPress.org 154 // since WordPress 4.6 using just-in-time loading. No manual load_plugin_textdomain() call needed. 155 // The Text Domain header in the plugin file is sufficient for WordPress to locate translations. 156 } 283 // Load default text domain for admin and other areas 284 load_plugin_textdomain( 285 'reventor-calendar-appointment-booking', 286 false, 287 dirname(plugin_basename(__FILE__)) . '/languages/' 288 ); 289 } 290 157 291 158 292 public function activation_notice() {
Note: See TracChangeset
for help on using the changeset viewer.