110 lines
4.9 KiB
PHP
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]);
|
|
}
|
|
}
|