174 lines
6.1 KiB
PHP
174 lines
6.1 KiB
PHP
<?php
|
|
/**
|
|
* AKSRT Admin Controller Base
|
|
*
|
|
* 所有后台业务域控制器的基类。
|
|
* 提供通用的权限检查、响应格式化、设置读写等基础设施。
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
abstract class AKSRT_Admin_Controller_Base {
|
|
|
|
/**
|
|
* REST API namespace (shared with AKSRT_Admin).
|
|
*/
|
|
const API_NAMESPACE = 'aksrt/v1';
|
|
|
|
/**
|
|
* Register REST routes for this domain.
|
|
* Subclasses must implement this method.
|
|
*/
|
|
abstract public function register_routes(): void;
|
|
|
|
/**
|
|
* Permission callback — requires manage_options capability.
|
|
*/
|
|
public function check_permission(): bool {
|
|
return current_user_can('manage_options');
|
|
}
|
|
|
|
/**
|
|
* Create a standard success response.
|
|
*/
|
|
protected function success($data = [], int $status = 200): WP_REST_Response {
|
|
return new WP_REST_Response(array_merge(['success' => true], $data), $status);
|
|
}
|
|
|
|
/**
|
|
* Create a standard error response.
|
|
*/
|
|
protected function error(string $message, int $status = 400): WP_REST_Response {
|
|
return new WP_REST_Response([
|
|
'success' => false,
|
|
'message' => $message,
|
|
], $status);
|
|
}
|
|
|
|
/**
|
|
* Read JSON request body safely.
|
|
*/
|
|
protected function get_json_params(WP_REST_Request $request): array {
|
|
$params = $request->get_json_params();
|
|
return is_array($params) ? $params : [];
|
|
}
|
|
|
|
/**
|
|
* Sanitize a settings array using a whitelist of allowed keys.
|
|
*
|
|
* @param array $input Raw input data.
|
|
* @param array $allowed Allowed keys with their types (string|int|bool|float|array).
|
|
* @return array Sanitized data.
|
|
*/
|
|
protected function sanitize_fields(array $input, array $allowed): array {
|
|
$out = [];
|
|
foreach ($allowed as $key => $type) {
|
|
if (!array_key_exists($key, $input)) continue;
|
|
$val = $input[$key];
|
|
switch ($type) {
|
|
case 'string':
|
|
$out[$key] = sanitize_text_field($val);
|
|
break;
|
|
case 'html':
|
|
$out[$key] = wp_kses_post($val);
|
|
break;
|
|
case 'code':
|
|
$out[$key] = is_string($val) ? wp_unslash($val) : '';
|
|
break;
|
|
case 'url':
|
|
$out[$key] = esc_url_raw($val);
|
|
break;
|
|
case 'int':
|
|
$out[$key] = (int) $val;
|
|
break;
|
|
case 'bool':
|
|
$out[$key] = !empty($val);
|
|
break;
|
|
case 'float':
|
|
$out[$key] = (float) $val;
|
|
break;
|
|
case 'array':
|
|
$out[$key] = is_array($val) ? $val : [];
|
|
break;
|
|
default:
|
|
$out[$key] = sanitize_text_field($val);
|
|
}
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
protected function register_section_routes(string $route_slug, string $domain): void {
|
|
register_rest_route(self::API_NAMESPACE, '/settings/' . $route_slug . '/(?P<section>[a-z0-9_-]+)', [
|
|
'methods' => 'GET',
|
|
'callback' => function (WP_REST_Request $request) use ($domain) {
|
|
return $this->get_section_settings($request, $domain);
|
|
},
|
|
'permission_callback' => [$this, 'check_permission'],
|
|
]);
|
|
|
|
register_rest_route(self::API_NAMESPACE, '/settings/' . $route_slug . '/(?P<section>[a-z0-9_-]+)', [
|
|
'methods' => 'POST',
|
|
'callback' => function (WP_REST_Request $request) use ($domain) {
|
|
return $this->save_section_settings($request, $domain);
|
|
},
|
|
'permission_callback' => [$this, 'check_permission'],
|
|
]);
|
|
}
|
|
|
|
protected function get_section_settings(WP_REST_Request $request, string $domain): WP_REST_Response {
|
|
$provider = AKSRT_Admin_Schema_Provider::get_instance();
|
|
$section = (string) $request->get_param('section');
|
|
$schema = $provider->get_section_schema($domain, $section);
|
|
|
|
if (!$schema) {
|
|
return $this->error('未知的设置分组: ' . esc_html($section), 404);
|
|
}
|
|
|
|
return $this->success([
|
|
'data' => $provider->read_settings($domain, $section),
|
|
'schema' => $schema,
|
|
'payload' => [
|
|
'domain' => $provider->normalize_domain($domain),
|
|
'section' => $provider->normalize_section($section),
|
|
'version' => $schema['version'] ?? '1.0.0',
|
|
],
|
|
]);
|
|
}
|
|
|
|
protected function save_section_settings(WP_REST_Request $request, string $domain): WP_REST_Response {
|
|
$provider = AKSRT_Admin_Schema_Provider::get_instance();
|
|
$section = (string) $request->get_param('section');
|
|
$schema = $provider->get_section_schema($domain, $section);
|
|
|
|
if (!$schema) {
|
|
return $this->error('未知的设置分组: ' . esc_html($section), 404);
|
|
}
|
|
|
|
$params = $this->get_json_params($request);
|
|
$payload = $params['data'] ?? $params;
|
|
if (!is_array($payload)) {
|
|
return $this->error('无效的设置载荷');
|
|
}
|
|
|
|
$sanitized = $this->sanitize_fields($payload, $provider->get_allowed_fields($domain, $section));
|
|
$validation = $provider->validate($domain, $sanitized, $section);
|
|
if (!$validation['valid']) {
|
|
return $this->error(implode('; ', $validation['errors']), 422);
|
|
}
|
|
|
|
$provider->write_settings($domain, $sanitized, $section);
|
|
|
|
return $this->success([
|
|
'message' => sprintf('%s/%s 设置已保存', $provider->normalize_domain($domain), $provider->normalize_section($section)),
|
|
'data' => $provider->read_settings($domain, $section),
|
|
'payload' => [
|
|
'domain' => $provider->normalize_domain($domain),
|
|
'section' => $provider->normalize_section($section),
|
|
'version' => $schema['version'] ?? '1.0.0',
|
|
],
|
|
]);
|
|
}
|
|
}
|