Files
wordpress-theme-aksrt/src/WidgetSystem/RestController.php
T

110 lines
4.9 KiB
PHP

<?php
namespace AKSRT\WidgetSystem;
defined('ABSPATH') || exit;
/** Public read-only endpoints for saved widget instances and quotes. */
final class RestController extends \WP_REST_Controller {
public function __construct() {
$this->namespace = 'aksrt/v1';
$this->rest_base = 'widgets';
}
public function register_routes(): void {
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<widget>[a-z][a-z0-9_]*)/items', [
'methods' => \WP_REST_Server::READABLE,
'callback' => [$this, 'get_items'],
'permission_callback' => '__return_true',
'args' => [
'widget' => ['type' => 'string', 'required' => true, 'pattern' => '^[a-z][a-z0-9_]*$', 'validate_callback' => [$this, 'validate_widget']],
'instance' => ['type' => 'string', 'required' => true, 'pattern' => '^aksrt_widget_[a-z0-9_]+-[1-9][0-9]*$'],
'page' => ['type' => 'integer', 'default' => 1, 'minimum' => 1, 'maximum' => 1000],
'segment' => ['type' => 'string', 'default' => '', 'pattern' => '^[0-9]{0,2}$'],
],
]);
register_rest_route($this->namespace, '/' . $this->rest_base . '/quote', [
'methods' => \WP_REST_Server::READABLE,
'callback' => [$this, 'get_quote'],
'permission_callback' => '__return_true',
'args' => [],
]);
}
public function validate_widget($value): bool {
return is_string($value) && WidgetRegistry::get($value) !== null;
}
/**
* @param \WP_REST_Request $request Request object.
* @return \WP_REST_Response|\WP_Error
*/
public function get_items($request) {
$unknown = array_diff(array_keys($request->get_params()), ['widget', 'instance', 'page', 'segment']);
if ($unknown) {
return new \WP_Error('aksrt_widget_unknown_parameter', __('请求包含不支持的参数。', 'aksrt'), ['status' => 400, 'parameters' => array_values($unknown)]);
}
$slug = (string) $request->get_param('widget');
if (!RendererRegistry::supports_items($slug)) {
return new \WP_Error('aksrt_widget_items_unsupported', __('该小工具不提供动态列表。', 'aksrt'), ['status' => 404]);
}
$instance_id = (string) $request->get_param('instance');
$instance = $this->load_instance($slug, $instance_id);
if (is_wp_error($instance)) {
return $instance;
}
$page = max(1, (int) $request->get_param('page'));
$segment = (string) $request->get_param('segment');
$payload = PostRenderer::payload($instance, $slug, $page, $segment);
return rest_ensure_response(['success' => true, 'code' => 'ok'] + $payload);
}
/** @return \WP_REST_Response|\WP_Error */
public function get_quote(\WP_REST_Request $request) {
if ($request->get_params()) {
return new \WP_Error('aksrt_widget_unknown_parameter', __('一言接口不接受查询参数。', 'aksrt'), ['status' => 400]);
}
$cached = get_transient('aksrt_widget_quote');
if (is_array($cached) && !empty($cached['text'])) {
return rest_ensure_response($cached);
}
$fallbacks = [__('保持好奇,持续创造。', 'aksrt'), __('知识因为分享而更有价值。', 'aksrt'), __('把复杂的事,做得清楚而简单。', 'aksrt')];
$data = ['text' => $fallbacks[array_rand($fallbacks)], 'author' => 'AKSRT', 'source' => 'local'];
$response = wp_remote_get('https://v1.hitokoto.cn/?encode=json', ['timeout' => 3, 'redirection' => 2, 'user-agent' => 'AKSRT Widget/1.0']);
if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
$remote = json_decode((string) wp_remote_retrieve_body($response), true);
if (is_array($remote) && !empty($remote['hitokoto'])) {
$data = ['text' => sanitize_text_field((string) $remote['hitokoto']), 'author' => sanitize_text_field((string) ($remote['from_who'] ?? $remote['from'] ?? '')), 'source' => 'hitokoto'];
}
}
set_transient('aksrt_widget_quote', $data, 10 * MINUTE_IN_SECONDS);
return rest_ensure_response($data);
}
/** @return array<string,mixed>|\WP_Error */
private function load_instance(string $slug, string $instance_id) {
$definition = WidgetRegistry::get($slug);
if ($definition === null || !preg_match('/^' . preg_quote($definition->id_base(), '/') . '-([1-9][0-9]*)$/', $instance_id, $matches)) {
return new \WP_Error('aksrt_widget_invalid_instance', __('小工具实例无效。', 'aksrt'), ['status' => 400]);
}
$sidebars = wp_get_sidebars_widgets();
$registered = false;
foreach ($sidebars as $widgets) {
if (is_array($widgets) && in_array($instance_id, $widgets, true)) {
$registered = true;
break;
}
}
if (!$registered) {
return new \WP_Error('aksrt_widget_instance_not_registered', __('小工具实例未挂载。', 'aksrt'), ['status' => 404]);
}
$widget = new SchemaWidget($definition);
$settings = $widget->get_settings();
$number = (int) $matches[1];
if (!is_array($settings) || !isset($settings[$number]) || !is_array($settings[$number])) {
return new \WP_Error('aksrt_widget_instance_not_found', __('未找到小工具配置。', 'aksrt'), ['status' => 404]);
}
return $definition->normalize($settings[$number]);
}
}