Public hitokoto v1 rejects c=hitokoto (and ignores batch count). Whitelist categories a–l, multi-fetch for carousel, and share full markup via aksrt_render_yiyan_block so single-post shells actually render quotes.
154 lines
6.0 KiB
PHP
154 lines
6.0 KiB
PHP
<?php
|
||
/**
|
||
* AKSRT Yiyan Widget — 一言轮播组件
|
||
*
|
||
* Fetches random quotes from hitokoto.cn API and displays with auto-rotation.
|
||
* Category values map to Hitokoto `c` letters a–l; empty = mixed random.
|
||
*
|
||
* @package AKSRT
|
||
*/
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
class AKSRT_Widget_Yiyan extends WP_Widget {
|
||
|
||
/**
|
||
* Hitokoto category codes → labels (API `c` param).
|
||
*
|
||
* @return array<string,string>
|
||
*/
|
||
private static function type_labels(): array {
|
||
return [
|
||
'' => __('随机', 'aksrt'),
|
||
'a' => __('动画', 'aksrt'),
|
||
'b' => __('漫画', 'aksrt'),
|
||
'c' => __('游戏', 'aksrt'),
|
||
'd' => __('文学', 'aksrt'),
|
||
'e' => __('原创', 'aksrt'),
|
||
'f' => __('来自网络', 'aksrt'),
|
||
'g' => __('其他', 'aksrt'),
|
||
'h' => __('影视', 'aksrt'),
|
||
'i' => __('诗词', 'aksrt'),
|
||
'j' => __('网易云', 'aksrt'),
|
||
'k' => __('哲学', 'aksrt'),
|
||
'l' => __('抖机灵', 'aksrt'),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Sanitize stored type to a valid Hitokoto category letter or empty.
|
||
*/
|
||
private static function sanitize_type(string $type): string {
|
||
$type = strtolower(trim($type));
|
||
return preg_match('/^[a-l]$/', $type) ? $type : '';
|
||
}
|
||
|
||
public function __construct() {
|
||
parent::__construct(
|
||
'aksrt_yiyan',
|
||
__('一言轮播', 'aksrt'),
|
||
['description' => __('显示随机一言/每日语录,支持自动轮播。', 'aksrt')]
|
||
);
|
||
}
|
||
|
||
public function widget($args, $instance): void {
|
||
$title = !empty($instance['title']) ? $instance['title'] : '';
|
||
$count = !empty($instance['count']) ? max(1, min(10, (int) $instance['count'])) : 5;
|
||
$interval = !empty($instance['interval']) ? max(3, min(30, (int) $instance['interval'])) : 5;
|
||
$type = self::sanitize_type((string) ($instance['type'] ?? ''));
|
||
$icon = !empty($instance['icon']) ? $instance['icon'] : 'fas fa-quote-left';
|
||
|
||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||
|
||
if ($title) {
|
||
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||
}
|
||
|
||
if (function_exists('aksrt_render_yiyan_block')) {
|
||
aksrt_render_yiyan_block([
|
||
'class' => 'aksrt-yiyan',
|
||
'type' => $type,
|
||
'count' => $count,
|
||
'interval' => $interval,
|
||
'icon' => $icon,
|
||
]);
|
||
} else {
|
||
// Fallback if single-render helpers are not loaded.
|
||
?>
|
||
<div class="aksrt-yiyan"
|
||
data-aksrt-yiyan
|
||
data-count="<?php echo esc_attr((string) $count); ?>"
|
||
data-interval="<?php echo esc_attr((string) $interval); ?>"
|
||
<?php echo $type !== '' ? ' data-type="' . esc_attr($type) . '"' : ''; ?>>
|
||
<div class="aksrt-yiyan__content">
|
||
<i class="<?php echo esc_attr($icon); ?> aksrt-yiyan__icon" aria-hidden="true"></i>
|
||
<p class="aksrt-yiyan__text"><?php esc_html_e('加载中…', 'aksrt'); ?></p>
|
||
<p class="aksrt-yiyan__from"></p>
|
||
</div>
|
||
<div class="aksrt-yiyan__dots" aria-hidden="true"></div>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||
}
|
||
|
||
public function form($instance): void {
|
||
$title = !empty($instance['title']) ? $instance['title'] : '一言';
|
||
$count = !empty($instance['count']) ? (int) $instance['count'] : 5;
|
||
$interval = !empty($instance['interval']) ? (int) $instance['interval'] : 5;
|
||
$type = self::sanitize_type((string) ($instance['type'] ?? ''));
|
||
$icon = !empty($instance['icon']) ? $instance['icon'] : 'fas fa-quote-left';
|
||
$labels = self::type_labels();
|
||
// Curated subset for the form (full map still used for sanitization).
|
||
$form_types = ['', 'a', 'd', 'i', 'k', 'h', 'j'];
|
||
?>
|
||
<p>
|
||
<label for="<?php echo esc_attr($this->get_field_id('title')); ?>"><?php esc_html_e('标题', 'aksrt'); ?></label>
|
||
<input class="widefat" id="<?php echo esc_attr($this->get_field_id('title')); ?>"
|
||
name="<?php echo esc_attr($this->get_field_name('title')); ?>" type="text"
|
||
value="<?php echo esc_attr($title); ?>">
|
||
</p>
|
||
<p>
|
||
<label for="<?php echo esc_attr($this->get_field_id('count')); ?>"><?php esc_html_e('轮播条数', 'aksrt'); ?></label>
|
||
<input id="<?php echo esc_attr($this->get_field_id('count')); ?>"
|
||
name="<?php echo esc_attr($this->get_field_name('count')); ?>" type="number"
|
||
min="1" max="10" value="<?php echo esc_attr((string) $count); ?>" style="width:80px;">
|
||
</p>
|
||
<p>
|
||
<label for="<?php echo esc_attr($this->get_field_id('interval')); ?>"><?php esc_html_e('切换间隔(秒)', 'aksrt'); ?></label>
|
||
<input id="<?php echo esc_attr($this->get_field_id('interval')); ?>"
|
||
name="<?php echo esc_attr($this->get_field_name('interval')); ?>" type="number"
|
||
min="3" max="30" value="<?php echo esc_attr((string) $interval); ?>" style="width:80px;">
|
||
</p>
|
||
<p>
|
||
<label for="<?php echo esc_attr($this->get_field_id('type')); ?>"><?php esc_html_e('分类', 'aksrt'); ?></label>
|
||
<select id="<?php echo esc_attr($this->get_field_id('type')); ?>"
|
||
name="<?php echo esc_attr($this->get_field_name('type')); ?>">
|
||
<?php foreach ($form_types as $code) : ?>
|
||
<option value="<?php echo esc_attr($code); ?>" <?php selected($type, $code); ?>>
|
||
<?php echo esc_html($labels[$code] ?? $code); ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</p>
|
||
<p>
|
||
<label for="<?php echo esc_attr($this->get_field_id('icon')); ?>"><?php esc_html_e('图标', 'aksrt'); ?></label>
|
||
<input class="widefat" id="<?php echo esc_attr($this->get_field_id('icon')); ?>"
|
||
name="<?php echo esc_attr($this->get_field_name('icon')); ?>" type="text"
|
||
value="<?php echo esc_attr($icon); ?>">
|
||
</p>
|
||
<?php
|
||
}
|
||
|
||
public function update($new_instance, $old_instance): array {
|
||
$instance = [];
|
||
$instance['title'] = sanitize_text_field($new_instance['title'] ?? '');
|
||
$instance['count'] = max(1, min(10, absint($new_instance['count'] ?? 5)));
|
||
$instance['interval'] = max(3, min(30, absint($new_instance['interval'] ?? 5)));
|
||
$instance['type'] = self::sanitize_type((string) ($new_instance['type'] ?? ''));
|
||
$instance['icon'] = sanitize_text_field($new_instance['icon'] ?? 'fas fa-quote-left');
|
||
return $instance;
|
||
}
|
||
}
|