✏️ 正在编辑: schedule-settings.php
路径:
/home/h359620/public_html/wp-content/themes/datis/inc/includes/schedules/schedule-settings.php
提示:
您可以编辑任何文件(包括二进制文件),但请注意不当修改可能导致文件损坏。
<?php if (!defined('ABSPATH')) exit; if (!class_exists('Datis_Consultation_Settings')) { final class Datis_Consultation_Settings { private static $instance = null; /** * Options key in wp_options table. * IMPORTANT: Do not change this value (keeps backward compatibility). */ public const OPT_KEY = 'datis_consultation_settings'; /** * Admin page slug. */ private const PAGE_SLUG = 'datis-consultation-settings'; public static function get_instance(): self { return self::$instance ?? (self::$instance = new self()); } private function __construct() { // Create options page under Settings add_action('admin_menu', [$this, 'register_options_page']); // Register settings + sanitization add_action('admin_init', [$this, 'register_settings']); // Admin assets (repeater + sortable) only for our options page add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']); } /* ============================================================ * Admin page + Settings API * ============================================================ */ public function register_options_page(): void { add_options_page( esc_html__('Consultation Settings', 'datis'), esc_html__('Consultation', 'datis'), 'manage_options', self::PAGE_SLUG, [$this, 'render_page'] ); } public function register_settings(): void { register_setting( 'datis_consultation_settings_group', self::OPT_KEY, [ 'type' => 'array', 'sanitize_callback' => [$this, 'sanitize_options'], 'default' => [], ] ); add_settings_section( 'datis_consultation_main_section', esc_html__('Consultation Settings', 'datis'), function () { echo '<p class="description">' . esc_html__('Configure consultation booking calendar behavior.', 'datis') . '</p>'; }, self::PAGE_SLUG ); // Default calendar select add_settings_field( 'default_calendar', esc_html__('Default Calendar (fallback)', 'datis'), [$this, 'field_default_calendar'], self::PAGE_SLUG, 'datis_consultation_main_section' ); // Allow user override checkbox add_settings_field( 'allow_user_calendar_override', esc_html__('Allow user to change calendar in booking form', 'datis'), [$this, 'field_allow_user_override'], self::PAGE_SLUG, 'datis_consultation_main_section' ); // Calendar map repeater add_settings_field( 'calendar_map', esc_html__('Calendar per language (WPML/Polylang)', 'datis'), [$this, 'field_calendar_map'], self::PAGE_SLUG, 'datis_consultation_main_section' ); } public function render_page(): void { if (!current_user_can('manage_options')) return; echo '<div class="wrap">'; echo '<h1>' . esc_html__('Consultation Settings', 'datis') . '</h1>'; echo '<form method="post" action="options.php">'; settings_fields('datis_consultation_settings_group'); do_settings_sections(self::PAGE_SLUG); submit_button(); echo '</form>'; echo '</div>'; } /* ============================================================ * Fields * ============================================================ */ private function get_opts(): array { $opts = get_option(self::OPT_KEY, []); return is_array($opts) ? $opts : []; } public function field_default_calendar(): void { $opts = $this->get_opts(); $val = isset($opts['default_calendar']) ? (string)$opts['default_calendar'] : 'gregorian'; $val = in_array($val, ['gregorian', 'jalali'], true) ? $val : 'gregorian'; $name = self::OPT_KEY . '[default_calendar]'; ?> <select name="<?php echo esc_attr($name); ?>"> <option value="gregorian" <?php selected($val, 'gregorian'); ?>> <?php echo esc_html__('Gregorian (Miladi)', 'datis'); ?> </option> <option value="jalali" <?php selected($val, 'jalali'); ?>> <?php echo esc_html__('Jalali (Shamsi)', 'datis'); ?> </option> </select> <p class="description"> <?php echo esc_html__('Used when no language rule matches.', 'datis'); ?> </p> <?php } public function field_allow_user_override(): void { $opts = $this->get_opts(); $val = !empty($opts['allow_user_calendar_override']) ? '1' : '0'; $name = self::OPT_KEY . '[allow_user_calendar_override]'; ?> <label> <input type="checkbox" name="<?php echo esc_attr($name); ?>" value="1" <?php checked($val, '1'); ?> /> <?php echo esc_html__('If enabled, user can switch between Jalali/Gregorian in the booking widget.', 'datis'); ?> </label> <?php } public function field_calendar_map(): void { $opts = $this->get_opts(); $rows = $opts['calendar_map'] ?? []; if (!is_array($rows)) $rows = []; // Normalize rows for display $normalized = []; foreach ($rows as $r) { if (!is_array($r)) continue; $lang = isset($r['lang']) ? (string)$r['lang'] : ''; $cal = isset($r['calendar']) ? (string)$r['calendar'] : 'gregorian'; $cal = in_array($cal, ['gregorian', 'jalali'], true) ? $cal : 'gregorian'; $normalized[] = [ 'lang' => $lang, 'calendar' => $cal, ]; } $base_name = self::OPT_KEY . '[calendar_map]'; ?> <p class="description"> <?php echo esc_html__('Example: fa => jalali, en/ar => gregorian. Drag rows to reorder.', 'datis'); ?> </p> <style> .datis-cs-table { width: 100%; max-width: 860px; border-collapse: collapse; } .datis-cs-table th, .datis-cs-table td { border: 1px solid #dcdcde; padding: 10px; vertical-align: top; } .datis-cs-table th { background: #f6f7f7; text-align: right; } .datis-cs-row { background: #fff; } .datis-cs-handle { cursor: move; user-select: none; font-weight: 700; color: #646970; } .datis-cs-actions { display:flex; gap:10px; align-items:center; } .datis-cs-remove { color:#b32d2e; text-decoration:none; } .datis-cs-add { margin-top: 10px; } .datis-cs-small { font-size: 12px; color:#646970; margin-top: 6px; } </style> <table class="datis-cs-table" id="datis-cs-calendar-map"> <thead> <tr> <th style="width:46px;"><?php echo esc_html__('#', 'datis'); ?></th> <th style="width:260px;"><?php echo esc_html__('Language Code', 'datis'); ?></th> <th><?php echo esc_html__('Calendar', 'datis'); ?></th> <th style="width:140px;"><?php echo esc_html__('Actions', 'datis'); ?></th> </tr> </thead> <tbody> <?php if (empty($normalized)) : ?> <?php /* Show one empty row by default for UX (still optional). */ ?> <?php $normalized = [['lang' => '', 'calendar' => 'gregorian']]; ?> <?php endif; ?> <?php foreach ($normalized as $i => $row) : ?> <tr class="datis-cs-row" data-index="<?php echo esc_attr((int)$i); ?>"> <td class="datis-cs-handle"><?php echo esc_html((string)($i + 1)); ?></td> <td> <input type="text" class="regular-text datis-cs-lang" name="<?php echo esc_attr($base_name . '[' . (int)$i . '][lang]'); ?>" value="<?php echo esc_attr($row['lang']); ?>" placeholder="fa / en / ar" /> <div class="datis-cs-small"><?php echo esc_html__('Lowercase language slug/code.', 'datis'); ?></div> </td> <td> <select name="<?php echo esc_attr($base_name . '[' . (int)$i . '][calendar]'); ?>"> <option value="gregorian" <?php selected($row['calendar'], 'gregorian'); ?>> <?php echo esc_html__('Gregorian (Miladi)', 'datis'); ?> </option> <option value="jalali" <?php selected($row['calendar'], 'jalali'); ?>> <?php echo esc_html__('Jalali (Shamsi)', 'datis'); ?> </option> </select> </td> <td> <div class="datis-cs-actions"> <a href="#" class="datis-cs-remove"><?php echo esc_html__('Remove', 'datis'); ?></a> </div> </td> </tr> <?php endforeach; ?> </tbody> </table> <button type="button" class="button datis-cs-add" id="datis-cs-add-row"> <?php echo esc_html__('Add Language Rule', 'datis'); ?> </button> <script type="text/template" id="tmpl-datis-cs-calendar-row"> <tr class="datis-cs-row" data-index="{{index}}"> <td class="datis-cs-handle">{{num}}</td> <td> <input type="text" class="regular-text datis-cs-lang" name="<?php echo esc_attr($base_name); ?>[{{index}}][lang]" value="" placeholder="fa / en / ar" /> <div class="datis-cs-small"><?php echo esc_html__('Lowercase language slug/code.', 'datis'); ?></div> </td> <td> <select name="<?php echo esc_attr($base_name); ?>[{{index}}][calendar]"> <option value="gregorian" selected><?php echo esc_html__('Gregorian (Miladi)', 'datis'); ?></option> <option value="jalali"><?php echo esc_html__('Jalali (Shamsi)', 'datis'); ?></option> </select> </td> <td> <div class="datis-cs-actions"> <a href="#" class="datis-cs-remove"><?php echo esc_html__('Remove', 'datis'); ?></a> </div> </td> </tr> </script> <?php } /* ============================================================ * Sanitization * ============================================================ */ public function sanitize_options($input): array { $out = []; // default_calendar $fallback = isset($input['default_calendar']) ? sanitize_key((string)$input['default_calendar']) : 'gregorian'; $out['default_calendar'] = in_array($fallback, ['gregorian', 'jalali'], true) ? $fallback : 'gregorian'; // allow_user_calendar_override $out['allow_user_calendar_override'] = !empty($input['allow_user_calendar_override']) ? 1 : 0; // calendar_map (group/repeater) $out['calendar_map'] = []; if (isset($input['calendar_map']) && is_array($input['calendar_map'])) { foreach ($input['calendar_map'] as $row) { if (!is_array($row)) continue; // Keep the same sanitization behavior as the original CMB2 callback $lang = isset($row['lang']) ? strtolower(trim((string)$row['lang'])) : ''; $lang = preg_replace('/[^a-z\-]/', '', $lang); $cal = isset($row['calendar']) ? sanitize_key((string)$row['calendar']) : $out['default_calendar']; $cal = in_array($cal, ['gregorian', 'jalali'], true) ? $cal : $out['default_calendar']; // Skip empty rows if ($lang === '') continue; $out['calendar_map'][] = [ 'lang' => $lang, 'calendar' => $cal, ]; } } return $out; } /* ============================================================ * Admin assets (sortable + repeater JS) * ============================================================ */ public function enqueue_admin_assets(string $hook): void { // Only load on our settings page if ($hook !== 'settings_page_' . self::PAGE_SLUG) return; wp_enqueue_script('jquery'); wp_enqueue_script('jquery-ui-sortable'); // Inline JS for repeater add/remove/sort/reindex add_action('admin_footer', function () { $screen = function_exists('get_current_screen') ? get_current_screen() : null; if (!$screen || ($screen->base ?? '') !== ('settings_page_' . self::PAGE_SLUG)) return; ?> <script> (function($){ "use strict"; function reindexRows() { var $tbody = $("#datis-cs-calendar-map tbody"); $tbody.find("tr.datis-cs-row").each(function(i){ var $tr = $(this); $tr.attr("data-index", i); $tr.find(".datis-cs-handle").text(String(i + 1)); // Update input/select names with the new index $tr.find("input, select, textarea").each(function(){ var $el = $(this); var name = ($el.attr("name") || ""); if (!name) return; // Replace [<number>] after [calendar_map] // Example: datis_consultation_settings[calendar_map][0][lang] name = name.replace(/\[calendar_map\]\[\d+\]/, "[calendar_map][" + i + "]"); $el.attr("name", name); }); }); } function addRow() { var $tbody = $("#datis-cs-calendar-map tbody"); var index = $tbody.find("tr.datis-cs-row").length; var tmpl = $("#tmpl-datis-cs-calendar-row").html(); if (!tmpl) return; var html = tmpl .replaceAll("{{index}}", String(index)) .replaceAll("{{num}}", String(index + 1)); $tbody.append($(html)); reindexRows(); } $(function(){ // Add row $("#datis-cs-add-row").on("click", function(e){ e.preventDefault(); addRow(); }); // Remove row $(document).on("click", ".datis-cs-remove", function(e){ e.preventDefault(); $(this).closest("tr.datis-cs-row").remove(); reindexRows(); }); // Sortable rows (drag handle is the first cell) $("#datis-cs-calendar-map tbody").sortable({ handle: ".datis-cs-handle", helper: function(e, tr){ var $originals = tr.children(); var $helper = tr.clone(); $helper.children().each(function(index){ $(this).width($originals.eq(index).width()); }); return $helper; }, stop: function(){ reindexRows(); } }); // Initial reindex (safe) reindexRows(); }); })(jQuery); </script> <?php }, 99); } /* ============================================================ * Public helpers (unchanged behavior) * ============================================================ */ public static function detect_language_code(): string { // WPML if (has_filter('wpml_current_language')) { $code = apply_filters('wpml_current_language', null); if (is_string($code) && $code !== '') return strtolower($code); } // Polylang if (function_exists('pll_current_language')) { $code = pll_current_language('slug'); if (is_string($code) && $code !== '') return strtolower($code); } // Fallback $loc = function_exists('determine_locale') ? determine_locale() : get_locale(); $loc = strtolower((string)$loc); return strstr($loc, '_', true) ?: (substr($loc, 0, 2) ?: 'en'); } public static function get_calendar_mode_for_language(?string $lang = null): string { $opts = get_option(self::OPT_KEY, []); if (!is_array($opts)) $opts = []; $fallback = $opts['default_calendar'] ?? 'gregorian'; $fallback = in_array($fallback, ['gregorian', 'jalali'], true) ? $fallback : 'gregorian'; $map = $opts['calendar_map'] ?? []; $lang = $lang ? strtolower($lang) : self::detect_language_code(); if (is_array($map)) { foreach ($map as $row) { if (!is_array($row)) continue; $row_lang = strtolower(trim($row['lang'] ?? '')); if ($row_lang && $row_lang === $lang) { $cal = $row['calendar'] ?? $fallback; return in_array($cal, ['gregorian', 'jalali'], true) ? $cal : $fallback; } } } return $fallback; } public static function allow_user_override(): bool { $opts = get_option(self::OPT_KEY, []); if (!is_array($opts)) $opts = []; return !empty($opts['allow_user_calendar_override']); } } Datis_Consultation_Settings::get_instance(); }
💾 保存文件
← 返回文件管理器