get_param('domain'); $schema_provider = SchemaProvider::get_instance(); $schema = $schema_provider->get_schema($domain); if (!$schema) { return $this->error('未知的设置域: ' . esc_html($domain), 404); } return $this->success(['schema' => $schema]); } public function rest_get_schema_section(\WP_REST_Request $request): \WP_REST_Response { return $this->get_section_settings($request, (string) $request->get_param('domain')); } public function rest_save_schema_section(\WP_REST_Request $request): \WP_REST_Response { return $this->save_section_settings($request, (string) $request->get_param('domain')); } public function list_setting_backups(\WP_REST_Request $request): \WP_REST_Response { $backups = get_option('aksrt_settings_backups', []); if (!is_array($backups)) { $backups = []; } usort($backups, static function ($a, $b) { return strcmp((string) ($b['created_at'] ?? ''), (string) ($a['created_at'] ?? '')); }); return $this->success([ 'data' => [ 'items' => array_values($backups), 'total' => count($backups), ], ]); } public function create_setting_backup(\WP_REST_Request $request): \WP_REST_Response { $params = $this->get_json_params($request); $type = sanitize_key((string) ($params['type'] ?? 'manual')); $label = sanitize_text_field((string) ($params['label'] ?? '')); $allowed_types = ['manual', 'scheduled', 'reset_all', 'reset_domain', 'update_theme']; if (!in_array($type, $allowed_types, true)) { $type = 'manual'; } $backup = $this->build_settings_backup($type, $label); $this->persist_setting_backup($backup); return $this->success([ 'message' => __('设置快照已创建', 'aksrt'), 'data' => $backup, ]); } public function restore_setting_backup(\WP_REST_Request $request): \WP_REST_Response { $id = (int) $request->get_param('id'); $backups = get_option('aksrt_settings_backups', []); if (!is_array($backups)) { $backups = []; } $target = null; foreach ($backups as $backup) { if ((int) ($backup['id'] ?? 0) === $id) { $target = $backup; break; } } if (!$target || !is_array($target['payload'] ?? null)) { return $this->error(__('备份快照不存在', 'aksrt'), 404); } $payload = $target['payload']; $domains = $payload['domains'] ?? []; $standalone = $payload['standalone'] ?? []; $provider = SchemaProvider::get_instance(); foreach ($domains as $domain => $domainPayload) { $data = $domainPayload['data'] ?? $domainPayload; if (is_array($data)) { $provider->write_settings((string) $domain, $data); } } foreach ($standalone as $key => $value) { if (is_string($key) && preg_match('/^aksrt_/', $key)) { update_option($key, $value); } } \AKSRT\Settings\Settings::flush_settings_cache(); return $this->success([ 'message' => __('设置已从快照恢复', 'aksrt'), 'data' => [ 'id' => $id, ], ]); } public function delete_setting_backup(\WP_REST_Request $request): \WP_REST_Response { $id = (int) $request->get_param('id'); $backups = get_option('aksrt_settings_backups', []); if (!is_array($backups)) { $backups = []; } $filtered = []; $found = false; foreach ($backups as $backup) { if ((int) ($backup['id'] ?? 0) === $id) { $found = true; continue; } $filtered[] = $backup; } if (!$found) { return $this->error(__('备份快照不存在', 'aksrt'), 404); } update_option('aksrt_settings_backups', array_values($filtered)); return $this->success([ 'message' => __('备份快照已删除', 'aksrt'), ]); } /* ------------------------------------------------------------------ */ /* Widget Template Handlers */ /* ------------------------------------------------------------------ */ /** * GET /widgets/export — Export current widget configuration. */ public function export_widgets(\WP_REST_Request $request): \WP_REST_Response { $sidebars_widgets = get_option('sidebars_widgets', []); $widget_options = $this->collect_widget_options(); return $this->success([ 'data' => [ 'version' => AKSRT_VERSION ?? '3.4.0', 'exported' => gmdate('Y-m-d H:i:s'), 'sidebars_widgets' => $sidebars_widgets, 'widget_options' => $widget_options, ], ]); } /** * POST /widgets/import — Import widget configuration. */ public function import_widgets(\WP_REST_Request $request): \WP_REST_Response { $params = $request->get_json_params() ?? []; $sidebars = $params['sidebars_widgets'] ?? null; $options = $params['widget_options'] ?? null; if (!$sidebars || !is_array($sidebars)) { return $this->error('缺少 sidebars_widgets 数据'); } // Validate sidebar IDs exist $valid_sidebars = array_keys(get_registered_sidebars()); $valid_sidebars[] = 'wp_inactive_widgets'; $imported = 0; foreach ($sidebars as $sidebar_id => $widget_ids) { if (!in_array($sidebar_id, $valid_sidebars, true) && $sidebar_id !== 'array_version') { continue; } $imported++; } // Import widget options if provided if ($options && is_array($options)) { foreach ($options as $option_key => $option_value) { if (preg_match('/^widget_aksrt_/', $option_key)) { update_option($option_key, $option_value); } } } // Import sidebar assignments update_option('sidebars_widgets', $sidebars); return $this->success(['message' => "已导入 {$imported} 个侧栏配置。"]); } /** * GET /widgets/templates — List saved widget templates. */ public function list_widget_templates(\WP_REST_Request $request): \WP_REST_Response { $templates = get_option('aksrt_widget_templates', []); return $this->success(['data' => ['templates' => $templates]]); } /** * POST /widgets/templates — Save current layout as a named template. */ public function save_widget_template(\WP_REST_Request $request): \WP_REST_Response { $params = $request->get_json_params() ?? []; $name = sanitize_text_field((string) ($params['name'] ?? '')); if (!$name) { return $this->error('请输入模板名称'); } $templates = get_option('aksrt_widget_templates', []); $template = [ 'id' => count($templates) + 1, 'name' => $name, 'created_at' => gmdate('Y-m-d H:i:s'), 'sidebars_widgets' => get_option('sidebars_widgets', []), 'widget_options' => $this->collect_widget_options(), ]; $templates[] = $template; update_option('aksrt_widget_templates', $templates); return $this->success(['message' => '模板已保存', 'data' => $template]); } /** * POST /widgets/templates/{id}/apply — Apply a saved template. */ public function apply_widget_template(\WP_REST_Request $request): \WP_REST_Response { $id = (int) $request->get_param('id'); $templates = get_option('aksrt_widget_templates', []); $template = null; foreach ($templates as $t) { if ((int) ($t['id'] ?? 0) === $id) { $template = $t; break; } } if (!$template) { return $this->error('模板不存在', 404); } // Apply widget options if (!empty($template['widget_options']) && is_array($template['widget_options'])) { foreach ($template['widget_options'] as $key => $value) { update_option($key, $value); } } // Apply sidebar assignments if (!empty($template['sidebars_widgets'])) { update_option('sidebars_widgets', $template['sidebars_widgets']); } return $this->success(['message' => '模板已应用']); } /** * DELETE /widgets/templates/{id} — Delete a saved template. */ public function delete_widget_template(\WP_REST_Request $request): \WP_REST_Response { $id = (int) $request->get_param('id'); $templates = get_option('aksrt_widget_templates', []); $found = false; $filtered = []; foreach ($templates as $t) { if ((int) ($t['id'] ?? 0) === $id) { $found = true; continue; } $filtered[] = $t; } if (!$found) { return $this->error('模板不存在', 404); } update_option('aksrt_widget_templates', $filtered); return $this->success(['message' => '模板已删除']); } /** * Collect all widget options from the database. */ private function collect_widget_options(): array { global $wpdb; $rows = $wpdb->get_results( "SELECT option_name, option_value FROM {$wpdb->options} WHERE option_name LIKE 'widget_aksrt_%' ORDER BY option_name", ARRAY_A ); $options = []; if ($rows) { foreach ($rows as $row) { $value = maybe_unserialize($row['option_value']); $options[$row['option_name']] = $value; } } return $options; } private function persist_setting_backup(array $backup): void { $backups = get_option('aksrt_settings_backups', []); if (!is_array($backups)) { $backups = []; } array_unshift($backups, $backup); update_option('aksrt_settings_backups', $this->trim_setting_backups($backups)); } private function create_backup_record(string $type, string $label = ''): array { return $this->build_settings_backup($type, $label); } public function register_backup_schedules(array $schedules): array { if (!isset($schedules[self::MONTHLY_SCHEDULE])) { $schedules[self::MONTHLY_SCHEDULE] = [ 'interval' => MONTH_IN_SECONDS, 'display' => __('每月', 'aksrt'), ]; } return $schedules; } public function sync_backup_schedule(): void { $settings = $this->get_backup_schedule_settings(); $next = wp_next_scheduled(self::SCHEDULED_BACKUP_HOOK); if (!$settings['enabled']) { if ($next) { wp_unschedule_event($next, self::SCHEDULED_BACKUP_HOOK); } return; } $schedule = $settings['schedule']; if ($next && wp_get_schedule(self::SCHEDULED_BACKUP_HOOK) !== $schedule) { wp_unschedule_event($next, self::SCHEDULED_BACKUP_HOOK); $next = false; } if (!$next) { wp_schedule_event(time() + HOUR_IN_SECONDS, $schedule, self::SCHEDULED_BACKUP_HOOK); } } public function clear_backup_schedule(): void { $next = wp_next_scheduled(self::SCHEDULED_BACKUP_HOOK); while ($next) { wp_unschedule_event($next, self::SCHEDULED_BACKUP_HOOK); $next = wp_next_scheduled(self::SCHEDULED_BACKUP_HOOK); } } public function run_scheduled_backup(): void { $settings = $this->get_backup_schedule_settings(); if (!$settings['enabled']) { return; } $backup = $this->create_backup_record('scheduled'); $this->persist_setting_backup($backup); } public function handle_theme_upgrade_snapshot($response, array $hook_extra) { if (($hook_extra['type'] ?? '') !== 'theme') { return $response; } $themes = $hook_extra['themes'] ?? []; $stylesheet = wp_get_theme()->get_stylesheet(); if (!in_array($stylesheet, (array) $themes, true)) { return $response; } $backup = $this->create_backup_record('update_theme'); $this->persist_setting_backup($backup); return $response; } }