'$', 'EUR' => '€', 'GBP' => '£', 'JPY' => '¥', 'CAD' => 'C$', 'AUD' => 'A$', ]; $symbol = $symbols[$currency] ?? '$'; return apply_filters('bizdex_uplisting_currency_symbol', $symbol); } } if (!function_exists('bizdex_uplisting_get_default_phone_country')) { /** * Resolve a default ISO phone country code for booking forms. * * Prefers the property country, then store currency, then US. * * @param PropertyDetails|null $property Optional property model. */ function bizdex_uplisting_get_default_phone_country($property = null): string { $allowed = [ 'US', 'CA', 'GB', 'AU', 'NZ', 'IE', 'IN', 'MX', 'BR', 'DE', 'FR', 'ES', 'IT', 'NL', 'PT', 'AE', 'SG', 'PH', 'ZA', 'JP', ]; $country = ''; if ($property instanceof \Bizdex\Uplisting\Models\PropertyDetails) { $country = trim((string) $property->get_country()); } if ($country !== '') { if (preg_match('/^[A-Za-z]{2}$/', $country)) { $iso = strtoupper($country); if (in_array($iso, $allowed, true)) { return $iso; } } $name_map = [ 'united states' => 'US', 'united states of america' => 'US', 'usa' => 'US', 'canada' => 'CA', 'united kingdom' => 'GB', 'great britain' => 'GB', 'england' => 'GB', 'australia' => 'AU', 'new zealand' => 'NZ', 'ireland' => 'IE', 'india' => 'IN', 'mexico' => 'MX', 'brazil' => 'BR', 'germany' => 'DE', 'france' => 'FR', 'spain' => 'ES', 'italy' => 'IT', 'netherlands' => 'NL', 'portugal' => 'PT', 'united arab emirates' => 'AE', 'uae' => 'AE', 'singapore' => 'SG', 'philippines' => 'PH', 'south africa' => 'ZA', 'japan' => 'JP', ]; $key = strtolower($country); if (isset($name_map[$key]) && in_array($name_map[$key], $allowed, true)) { return $name_map[$key]; } } $currency = strtoupper((string) get_option(BIZDEX_UPLISTING_CURRENCY, 'USD')); $currency_map = [ 'USD' => 'US', 'CAD' => 'CA', 'GBP' => 'GB', 'AUD' => 'AU', 'NZD' => 'NZ', 'EUR' => 'IE', 'INR' => 'IN', 'MXN' => 'MX', 'BRL' => 'BR', 'AED' => 'AE', 'SGD' => 'SG', 'PHP' => 'PH', 'ZAR' => 'ZA', 'JPY' => 'JP', ]; $from_currency = $currency_map[$currency] ?? 'US'; /** * Filter the default phone country ISO code for booking forms. * * @param string $iso Two-letter country code. * @param PropertyDetails|null $property Property model when available. */ $filtered = apply_filters('bizdex_uplisting_default_phone_country', $from_currency, $property); $filtered = is_string($filtered) ? strtoupper($filtered) : 'US'; return in_array($filtered, $allowed, true) ? $filtered : 'US'; } } if (!function_exists('bizdex_uplisting_format_price')) { /** * Format a numeric price using plugin currency settings. * * @param mixed $price Raw price (string or number). * @param array $args Optional formatting overrides. */ function bizdex_uplisting_format_price($price, $args = []) { if (empty($price) && !is_numeric($price)) { return ''; } $numeric_price = floatval(preg_replace('/[^\d.]/', '', (string) $price)); $args = wp_parse_args($args, [ 'currency' => '', 'decimal_separator' => get_option(BIZDEX_UPLISTING_DECIMAL_SEPARATOR, '.'), 'thousand_separator' => get_option(BIZDEX_UPLISTING_THOUSAND_SEPARATOR, ','), 'decimals' => absint(get_option(BIZDEX_UPLISTING_DECIMALS, 2)), 'price_format' => bizdex_uplisting_sanitize_price_format((string) get_option(BIZDEX_UPLISTING_PRICE_FORMAT, '%1$s%2$s')), ]); $currency_symbol = $args['currency'] !== '' ? $args['currency'] : bizdex_uplisting_get_currency_symbol(); $formatted_price = number_format( $numeric_price, (int) $args['decimals'], (string) $args['decimal_separator'], (string) $args['thousand_separator'] ); return sprintf( bizdex_uplisting_sanitize_price_format((string) $args['price_format']), $currency_symbol, $formatted_price ); } } if (!function_exists('bizdex_uplisting_sanitize_price_format')) { /** * Allow only known sprintf patterns for currency + amount. */ function bizdex_uplisting_sanitize_price_format(string $format): string { $allowed = [ '%1$s%2$s', '%1$s %2$s', '%2$s%1$s', '%2$s %1$s', ]; return in_array($format, $allowed, true) ? $format : '%1$s%2$s'; } } if (!function_exists('bizdex_uplisting_sanitize_phone')) { /** * Normalize a phone number for `tel:` links (digits, optional leading +). */ function bizdex_uplisting_sanitize_phone(string $phone): string { $phone = trim(sanitize_text_field($phone)); if ($phone === '') { return ''; } $has_plus = str_starts_with($phone, '+'); $digits = preg_replace('/\D+/', '', $phone); if (!is_string($digits) || $digits === '') { return ''; } return $has_plus ? '+' . $digits : $digits; } } if (!function_exists('bizdex_uplisting_get_phone_href')) { /** * Build a `tel:` href from a display phone number. */ function bizdex_uplisting_get_phone_href(string $phone): string { $normalized = bizdex_uplisting_sanitize_phone($phone); return $normalized !== '' ? 'tel:' . $normalized : ''; } } if (!function_exists('bizdex_uplisting_format_phone_link')) { /** * Escaped phone anchor HTML. * * @param string $phone Display phone number. * @param array{class?: string} $args Optional link attributes. */ function bizdex_uplisting_format_phone_link(string $phone, array $args = []): string { $phone = sanitize_text_field($phone); $href = bizdex_uplisting_get_phone_href($phone); if ($phone === '' || $href === '') { return ''; } $class_attr = ''; if (!empty($args['class'])) { $class_attr = sprintf(' class="%s"', esc_attr((string) $args['class'])); } return sprintf( '%3$s', esc_url($href), $class_attr, esc_html($phone) ); } } if (!function_exists('bizdex_uplisting_format_date')) { /** * Format a date for display using WordPress locale settings. * * @param mixed $date Date string or timestamp. * @param string $format Optional PHP date format. */ function bizdex_uplisting_format_date($date, $format = '') { if (empty($date)) { return ''; } if ($format === '') { $format = get_option('date_format') ?: 'F j, Y'; } $timestamp = is_numeric($date) ? (int) $date : strtotime((string) $date); if (!$timestamp) { return is_scalar($date) ? (string) $date : ''; } return (string) wp_date($format, $timestamp); } } if (!function_exists('bizdex_uplisting_format_time')) { /** * Format a time for display using WordPress locale settings. * * @param mixed $time Time string or timestamp. * @param string $format Optional PHP time format. */ function bizdex_uplisting_format_time($time, $format = '') { if (empty($time)) { return ''; } if ($format === '') { $format = get_option('time_format') ?: 'g:i a'; } $timestamp = is_numeric($time) ? (int) $time : strtotime((string) $time); if (!$timestamp) { return is_scalar($time) ? (string) $time : ''; } return (string) wp_date($format, $timestamp); } } if (!function_exists('bizdex_uplisting_log')) { /** * Log a plugin message when WP_DEBUG is enabled. */ function bizdex_uplisting_log(string $message, string $context = 'advanced-connector-for-uplisting'): void { if (!defined('WP_DEBUG') || !WP_DEBUG) { return; } /** * Fires for plugin debug messages when WP_DEBUG is enabled. * * @param string $message Log message. * @param string $context Log context. */ do_action('bizdex_uplisting_log', $message, $context); } } /* |-------------------------------------------------------------------------- | Permalinks |-------------------------------------------------------------------------- */ if (!function_exists('bizdex_uplisting_sanitize_slug')) { /** * Sanitize a URL slug for rewrite rules. */ function bizdex_uplisting_sanitize_slug(string $slug, string $fallback = ''): string { $slug = sanitize_title($slug); if ($slug === '') { return $fallback !== '' ? $fallback : BIZDEX_UPLISTING_DEFAULT_PROPERTY_SLUG; } return $slug; } } if (!function_exists('bizdex_uplisting_get_property_slug')) { /** * Property archive and single URL slug. */ function bizdex_uplisting_get_property_slug(): string { $slug = (string) get_option(BIZDEX_UPLISTING_PROPERTY_REWRITE_SLUG, BIZDEX_UPLISTING_DEFAULT_PROPERTY_SLUG); return bizdex_uplisting_sanitize_slug($slug, BIZDEX_UPLISTING_DEFAULT_PROPERTY_SLUG); } } if (!function_exists('bizdex_uplisting_get_amenity_slug')) { /** * Amenity taxonomy archive slug. */ function bizdex_uplisting_get_amenity_slug(): string { $slug = (string) get_option(BIZDEX_UPLISTING_AMENITY_REWRITE_SLUG, BIZDEX_UPLISTING_DEFAULT_AMENITY_SLUG); return bizdex_uplisting_sanitize_slug($slug, BIZDEX_UPLISTING_DEFAULT_AMENITY_SLUG); } } if (!function_exists('bizdex_uplisting_get_property_archive_url')) { /** * Property archive URL using the configured slug. */ function bizdex_uplisting_get_property_archive_url(): string { return home_url('/' . bizdex_uplisting_get_property_slug() . '/'); } } if (!function_exists('bizdex_uplisting_get_amenity_archive_url')) { /** * Amenity taxonomy archive base URL. */ function bizdex_uplisting_get_amenity_archive_url(): string { return home_url('/' . bizdex_uplisting_get_amenity_slug() . '/'); } } if (!function_exists('bizdex_uplisting_schedule_rewrite_flush')) { /** * Schedule a rewrite flush on the next `init` (after CPT registration). */ function bizdex_uplisting_schedule_rewrite_flush(): void { update_option(BIZDEX_UPLISTING_FLUSH_FLAG, '1', false); } } if (!function_exists('bizdex_uplisting_maybe_flush_rewrite_rules')) { /** * Flush rewrite rules when scheduled (activation or permalink settings change). */ function bizdex_uplisting_maybe_flush_rewrite_rules(): void { if (!get_option(BIZDEX_UPLISTING_FLUSH_FLAG)) { return; } flush_rewrite_rules(false); delete_option(BIZDEX_UPLISTING_FLUSH_FLAG); update_option(BIZDEX_UPLISTING_REWRITE_VERSION, BIZDEX_UPLISTING_VERSION, false); } } if (!function_exists('bizdex_uplisting_register_permalink_hooks')) { /** * Hook rewrite flush after CPTs are registered. */ function bizdex_uplisting_register_permalink_hooks(): void { add_action('init', 'bizdex_uplisting_maybe_flush_rewrite_rules', 999); } } if (!function_exists('bizdex_uplisting_ensure_permalink_defaults')) { /** * Ensure default permalink options exist (activation / first run). */ function bizdex_uplisting_ensure_permalink_defaults(): void { if (get_option(BIZDEX_UPLISTING_PROPERTY_REWRITE_SLUG, '') === '') { update_option(BIZDEX_UPLISTING_PROPERTY_REWRITE_SLUG, BIZDEX_UPLISTING_DEFAULT_PROPERTY_SLUG, false); } if (get_option(BIZDEX_UPLISTING_AMENITY_REWRITE_SLUG, '') === '') { update_option(BIZDEX_UPLISTING_AMENITY_REWRITE_SLUG, BIZDEX_UPLISTING_DEFAULT_AMENITY_SLUG, false); } } } if (!function_exists('bizdex_uplisting_get_page_option_names')) { /** * Page ID options stored as integers. * * @return string[] */ function bizdex_uplisting_get_page_option_names(): array { return [ BIZDEX_UPLISTING_LISTING_PAGE, BIZDEX_UPLISTING_RESERVATION_PAGE, BIZDEX_UPLISTING_RESERVATION_SUCCESS_PAGE, ]; } } if (!function_exists('bizdex_uplisting_get_registered_option_names')) { /** * All options registered via the Settings API. * * @return string[] */ function bizdex_uplisting_get_registered_option_names(): array { $options = [ BIZDEX_UPLISTING_SHOW_GRID_VIEW, BIZDEX_UPLISTING_SHOW_LIST_VIEW, BIZDEX_UPLISTING_PROPERTIES_PER_PAGE, BIZDEX_UPLISTING_GALLERY_IMAGES_COUNT, BIZDEX_UPLISTING_ENABLE_CAROUSEL, BIZDEX_UPLISTING_ADMIN_EMAIL, BIZDEX_UPLISTING_BOOKING_FROM_NAME, BIZDEX_UPLISTING_BOOKING_FROM_EMAIL, BIZDEX_UPLISTING_CUSTOMIZER_FONT_SIZE, BIZDEX_UPLISTING_CUSTOMIZER_HEADING_FONT_SIZE, BIZDEX_UPLISTING_API_KEY, BIZDEX_UPLISTING_PAGE_TYPE, BIZDEX_UPLISTING_LISTING_PAGE, BIZDEX_UPLISTING_PROPERTY_REWRITE_SLUG, BIZDEX_UPLISTING_AMENITY_REWRITE_SLUG, BIZDEX_UPLISTING_RESERVATION_PAGE, BIZDEX_UPLISTING_RESERVATION_SUCCESS_PAGE, BIZDEX_UPLISTING_SHOW_MORE_DAY_RATES, BIZDEX_UPLISTING_CURRENCY, BIZDEX_UPLISTING_DECIMAL_SEPARATOR, BIZDEX_UPLISTING_THOUSAND_SEPARATOR, BIZDEX_UPLISTING_DECIMALS, BIZDEX_UPLISTING_PRICE_FORMAT, ]; if (class_exists(ShortcodeRegistry::class)) { foreach (ShortcodeRegistry::get_admin_listing_setting_keys() as $listing_key) { $listing_option = ShortcodeRegistry::get_listing_setting_option_name($listing_key); if (!in_array($listing_option, $options, true)) { $options[] = $listing_option; } } } return $options; } } if (!function_exists('bizdex_uplisting_migrate_legacy_options')) { /** * Copy `wp_uplisting_connector_*` options back to `bizdex_uplisting_*` if present. * * @since 1.0.0 */ function bizdex_uplisting_migrate_legacy_options(): void { if (get_option('bizdex_uplisting_prefix_restored') === '1') { return; } global $wpdb; $like = $wpdb->esc_like('wp_uplisting_connector_') . '%'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-time option-key migration. $names = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s", $like ) ); if (is_array($names)) { foreach ($names as $old_name) { if (!is_string($old_name) || $old_name === '') { continue; } $new_name = 'bizdex_uplisting_' . substr($old_name, strlen('wp_uplisting_connector_')); if ($new_name === $old_name || get_option($new_name, null) !== null) { continue; } $old_value = get_option($old_name, null); if ($old_value !== null) { update_option($new_name, $old_value); } } } update_option('bizdex_uplisting_prefix_restored', '1', false); } } if (!function_exists('bizdex_uplisting_clear_legacy_cron')) { /** * Remove cron events registered under the wp_uplisting_connector_* hook names. * * @since 1.0.0 */ function bizdex_uplisting_clear_legacy_cron(): void { wp_clear_scheduled_hook('wp_uplisting_connector_listing_price_sync'); wp_clear_scheduled_hook('wp_uplisting_connector_calendar_sync'); } } if (!function_exists('bizdex_uplisting_sanitize_int_list')) { /** * Sanitize a REST list of positive integers (array, CSV, or scalar). * * @param mixed $value Raw request value. * @return list */ function bizdex_uplisting_sanitize_int_list($value): array { return \Bizdex\Uplisting\Helpers\Request::positive_int_list($value); } } if (!function_exists('bizdex_uplisting_sanitize_json_payload')) { /** * Sanitize a REST JSON payload (JSON string or array of IDs). * * @param mixed $value Raw request value. * @return string */ function bizdex_uplisting_sanitize_json_payload($value): string { return \Bizdex\Uplisting\Api\RestEndpointHelper::sanitize_json_payload($value); } } if (!function_exists('bizdex_uplisting_authorize_public_request')) { /** * REST permission callback: require a valid WordPress REST nonce. * * Delegates to {@see \Bizdex\Uplisting\Api\RestEndpointHelper::authorize_public_request()}. * * @param WP_REST_Request $request Incoming REST request. * @return bool|WP_Error */ function bizdex_uplisting_authorize_public_request(WP_REST_Request $request) { return \Bizdex\Uplisting\Api\RestEndpointHelper::authorize_public_request($request); } }