fix(theme): align reference layouts and restore passing contracts
Align homepage and category discovery, modern widgets, admin controls, quotes, and comments with the reference theme. Remove superseded paths and add rendering and behavior coverage. Update stale asset and finance contracts; all 760 Node tests and 139 widget assertions pass.
This commit is contained in:
1 parent
b520d29368
commit
8aa116ec43
63 files changed
+1985
-2304
No files matched your search
@@ -31,14 +31,22 @@ final class AdminFormController {
|
||||
aksrt_icon_picker_assets(['wp-color-picker']);
|
||||
wp_enqueue_script('aksrt-widget-admin-core', AKSRT_URI . '/assets/widgets/admin-core.js', [], AKSRT_VERSION, true);
|
||||
wp_enqueue_script('aksrt-widget-admin', AKSRT_URI . '/assets/widgets/admin.js', ['jquery', 'jquery-ui-sortable', 'wp-color-picker', 'aksrt-widget-admin-core'], AKSRT_VERSION, true);
|
||||
$sidebars = [];
|
||||
foreach ((array) ($GLOBALS['wp_registered_sidebars'] ?? []) as $id => $sidebar) {
|
||||
$sidebars[(string) $id] = (string) ($sidebar['name'] ?? $id);
|
||||
}
|
||||
wp_localize_script('aksrt-widget-admin', 'aksrtWidgetAdmin', [
|
||||
'ajaxUrl' => admin_url('admin-ajax.php'),
|
||||
'action' => self::AJAX_ACTION,
|
||||
'nonce' => wp_create_nonce(self::NONCE_ACTION),
|
||||
'transferNonce' => wp_create_nonce('aksrt_widgets_transfer'),
|
||||
'sidebars' => $sidebars,
|
||||
'i18n' => [
|
||||
'loading' => __('正在加载模块配置…', 'aksrt'),
|
||||
'error' => __('加载失败,请重试。', 'aksrt'),
|
||||
'export' => __('导出', 'aksrt'),
|
||||
'import' => __('导入', 'aksrt'),
|
||||
'template' => __('应用模板', 'aksrt'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
@@ -60,6 +68,10 @@ final class AdminFormController {
|
||||
$widget->_set($number);
|
||||
$settings = $widget->get_settings();
|
||||
$instance = isset($settings[$number]) && is_array($settings[$number]) ? $settings[$number] : [];
|
||||
$instance = apply_filters('widget_form_callback', $instance, $widget);
|
||||
if (!is_array($instance)) {
|
||||
$instance = [];
|
||||
}
|
||||
ob_start();
|
||||
$widget->render_full_form($instance);
|
||||
$html = (string) ob_get_clean();
|
||||
|
||||
@@ -34,7 +34,6 @@ final class AssetManager {
|
||||
if (!self::$configured) {
|
||||
$config = [
|
||||
'restUrl' => esc_url_raw(rest_url('aksrt/v1/widgets/')),
|
||||
'quoteUrl' => esc_url_raw(rest_url('aksrt/v1/widgets/quote')),
|
||||
'i18n' => [
|
||||
'loading' => __('正在加载…', 'aksrt'),
|
||||
'error' => __('加载失败,请重试。', 'aksrt'),
|
||||
@@ -57,7 +56,9 @@ final class AssetManager {
|
||||
$js = $root . '/assets/widgets/frontend.js';
|
||||
wp_register_style(self::STYLE_HANDLE, $uri . '/assets/widgets/frontend.css', ['aksrt-public'], is_file($css) ? (string) filemtime($css) : null);
|
||||
wp_register_style('aksrt-pay-box', $uri . '/assets/css/frontend/pay-box.css', ['aksrt-public'], is_file($pay_css) ? (string) filemtime($pay_css) : null);
|
||||
wp_register_script(self::SCRIPT_HANDLE, $uri . '/assets/widgets/frontend.js', ['loader_js'], is_file($js) ? (string) filemtime($js) : null, true);
|
||||
$yiyan_js = $root . '/assets/js/yiyan.js';
|
||||
wp_register_script('aksrt-yiyan', $uri . '/assets/js/yiyan.js', ['loader_js'], is_file($yiyan_js) ? (string) filemtime($yiyan_js) : null, true);
|
||||
wp_register_script(self::SCRIPT_HANDLE, $uri . '/assets/widgets/frontend.js', ['loader_js', 'aksrt-yiyan'], is_file($js) ? (string) filemtime($js) : null, true);
|
||||
$third_party = $uri . '/assets/widgets/third-party/';
|
||||
wp_register_script('aksrt-widget-hls', $third_party . 'hls.min.js', [], 'reference-pinned', true);
|
||||
wp_register_script('aksrt-widget-flv', $third_party . 'flv.min.js', [], 'reference-pinned', true);
|
||||
|
||||
@@ -121,14 +121,37 @@ final class FormRenderer {
|
||||
$image = is_array($label) ? (string) ($label['image'] ?? $label['url'] ?? '') : (string) $label;
|
||||
$text = is_array($label) ? (string) ($label['label'] ?? $label['title'] ?? $key) : (string) $label;
|
||||
if ($type === 'image_select' && $text === $image) $text = (string) $key;
|
||||
$body = $type === 'image_select' && filter_var($image, FILTER_VALIDATE_URL)
|
||||
? '<img src="' . esc_url($image) . '" alt="' . esc_attr($text) . '"><small>' . esc_html($text) . '</small>'
|
||||
: esc_html($text);
|
||||
if ($type === 'image_select' && filter_var($image, FILTER_VALIDATE_URL)) {
|
||||
$body = '<img src="' . esc_url($image) . '" alt="' . esc_attr($text) . '"><small>' . esc_html($text) . '</small>';
|
||||
} elseif ($type === 'palette') {
|
||||
$body = '<span class="aksrt-widget-palette-choice"><i aria-hidden="true" style="background:' . esc_attr(self::palette_color((string) $key)) . '"></i><small>' . esc_html($text) . '</small></span>';
|
||||
} else {
|
||||
$body = esc_html($text);
|
||||
}
|
||||
echo '<label for="' . esc_attr($option_id) . '"><input type="radio" id="' . esc_attr($option_id) . '" name="' . esc_attr($name) . '" value="' . esc_attr((string) $key) . '" ' . checked($value, (string) $key, false) . '><span>' . $body . '</span></label>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
private static function palette_color(string $key): string {
|
||||
$colors = [
|
||||
'' => '#fff', 'c-red' => 'rgba(255,84,115,.4)', 'c-red-2' => 'rgba(194,41,46,.4)',
|
||||
'c-yellow' => 'rgba(255,111,6,.4)', 'c-yellow-2' => 'rgba(179,103,8,.4)', 'c-cyan' => 'rgba(8,196,193,.4)',
|
||||
'c-blue' => 'rgba(41,151,247,.4)', 'c-blue-2' => 'rgba(77,130,249,.4)', 'c-green' => 'rgba(18,185,40,.4)',
|
||||
'c-green-2' => 'rgba(72,135,24,.4)', 'c-purple' => 'rgba(213,72,245,.4)', 'c-purple-2' => 'rgba(154,72,245,.4)',
|
||||
'b-red' => '#f74b3d', 'b-yellow' => '#f3920a', 'b-cyan' => '#08c4c1', 'b-blue' => '#0a8cf3',
|
||||
'b-green' => '#1fd05a', 'b-purple' => '#c133f5', 'b-black' => '#121517',
|
||||
'jb-red' => 'linear-gradient(135deg,#ffbeb4 10%,#f61a1a 100%)', 'jb-pink' => 'linear-gradient(135deg,#ff5e7f 30%,#ff967e 100%)',
|
||||
'jb-yellow' => 'linear-gradient(135deg,#ffd6b2 10%,#ff651c 100%)', 'jb-cyan' => 'linear-gradient(140deg,#039ab3 10%,#58dbcf 90%)',
|
||||
'jb-blue' => 'linear-gradient(135deg,#b6e6ff 10%,#198aff 100%)', 'jb-green' => 'linear-gradient(135deg,#ccffcd 10%,#52bb51 100%)',
|
||||
'jb-purple' => 'linear-gradient(135deg,#fec2ff 10%,#d000de 100%)', 'jb-vip1' => 'linear-gradient(25deg,#eab869 10%,#fbecd4 60%,#ffe0ae 100%)',
|
||||
'jb-vip2' => 'linear-gradient(317deg,#4d4c4c 30%,#878787 70%,#5f5c5c 100%)',
|
||||
'focus-color' => 'linear-gradient(135deg,#74d5f6,#9866ff)', 'key-color' => '#333', 'muted-color' => '#777',
|
||||
'muted-2-color' => '#999', 'muted-3-color' => '#b1b1b1',
|
||||
];
|
||||
return $colors[$key] ?? '#dcdcde';
|
||||
}
|
||||
|
||||
private static function select(string $id, string $name, array $field, $value): void {
|
||||
$multiple = !empty($field['multiple']);
|
||||
echo '<select class="widefat" id="' . esc_attr($id) . '" name="' . esc_attr($name) . ($multiple ? '[]' : '') . '"' . ($multiple ? ' multiple' : '') . '>';
|
||||
@@ -242,7 +265,7 @@ final class FormRenderer {
|
||||
private static function group(SchemaWidget $widget, array $field, array $rows, string $key): void {
|
||||
$token = '__index_' . substr(md5($key), 0, 8) . '__';
|
||||
$button_title = (string) ($field['button_title'] ?? __('添加项目', 'aksrt'));
|
||||
echo '<div class="aksrt-widget-repeater" data-index-token="' . esc_attr($token) . '" data-max-items="' . esc_attr((string) ($field['max_items'] ?? 50)) . '"><div class="aksrt-widget-repeater__rows">';
|
||||
echo '<div class="aksrt-widget-repeater" data-index-token="' . esc_attr($token) . '" data-item-label="' . esc_attr((string) ($field['item_label'] ?? $field['label'] ?? __('项目', 'aksrt'))) . '" data-min-items="' . esc_attr((string) ($field['min_items'] ?? 0)) . '" data-max-items="' . esc_attr((string) ($field['max_items'] ?? 50)) . '"><div class="aksrt-widget-repeater__rows">';
|
||||
foreach ($rows as $index => $row) {
|
||||
self::group_row($widget, $field, is_array($row) ? $row : [], $key, (string) $index);
|
||||
}
|
||||
@@ -256,12 +279,12 @@ final class FormRenderer {
|
||||
$number = ctype_digit($index) ? ((int) $index + 1) : 0;
|
||||
$title = $prefix . $number;
|
||||
if (!empty($row['title'])) $title .= ':' . strip_tags((string) $row['title']);
|
||||
echo '<div class="aksrt-widget-repeater__row" data-row-index="' . esc_attr(ctype_digit($index) ? $index : '0') . '"><div class="aksrt-widget-repeater__bar"><span class="dashicons dashicons-move" aria-hidden="true"></span><strong>' . esc_html($title) . '</strong><button type="button" class="button-link-delete aksrt-widget-repeater__remove">' . esc_html__('删除', 'aksrt') . '</button></div><div class="aksrt-widget-repeater__fields">';
|
||||
echo '<details class="aksrt-widget-repeater__row" data-row-index="' . esc_attr(ctype_digit($index) ? $index : '0') . '"><summary class="aksrt-widget-repeater__bar"><span class="dashicons dashicons-move" aria-hidden="true"></span><strong class="aksrt-widget-repeater__title" data-title-prefix="' . esc_attr($prefix) . '">' . esc_html($title) . '</strong><button type="button" class="button-link-delete aksrt-widget-repeater__remove">' . esc_html__('删除', 'aksrt') . '</button></summary><div class="aksrt-widget-repeater__fields">';
|
||||
foreach ((array) ($field['fields'] ?? []) as $child) {
|
||||
$child_id = (string) ($child['id'] ?? '');
|
||||
self::field($widget, $child, $row[$child_id] ?? ($child['default'] ?? ''), $key . '[' . $index . ']');
|
||||
}
|
||||
echo '</div></div>';
|
||||
echo '</div></details>';
|
||||
}
|
||||
|
||||
private static function nested(SchemaWidget $widget, array $field, array $value, string $key): void {
|
||||
|
||||
@@ -33,6 +33,8 @@ final class PostRenderer {
|
||||
$mini_opt = (array) ($instance['mini_opt'] ?? []);
|
||||
$config = [
|
||||
'type' => $mode === 'card' ? 'card' : 'list',
|
||||
'is_mult_thumb' => 'disable',
|
||||
'is_no_thumb' => 'disable',
|
||||
'show_number' => in_array('show_number', $mini_opt, true),
|
||||
'show_thumb' => in_array('show_thumb', $mini_opt, true),
|
||||
'show_meta' => in_array('show_meta', $mini_opt, true),
|
||||
|
||||
@@ -56,7 +56,7 @@ final class ContentRenderer {
|
||||
// The loader must live inside the pager: the REST client in frontend.js
|
||||
// resolves it with container.querySelector('.post_ajax_loader'), so a
|
||||
// sibling node would never be found and the skeleton would stay shut.
|
||||
return '<div class="widget-ajaxpager"' . $attrs . ' aria-live="polite" aria-busy="false">' . $payload['html'] . $payload['paginate_html'] . $loader . '</div>';
|
||||
return '<div class="widget-ajaxpager"' . $attrs . ' data-loaded="1" aria-live="polite" aria-busy="false">' . $payload['html'] . $payload['paginate_html'] . $loader . '</div>';
|
||||
}
|
||||
|
||||
private static function placeholder(array $instance): string {
|
||||
@@ -88,7 +88,7 @@ final class ContentRenderer {
|
||||
if (!is_array($tab) || trim((string) ($tab['title'] ?? '')) === '') continue;
|
||||
$tab_id = 'aksrt-tab-' . $key . '-' . $visible_index;
|
||||
$selected = $visible_index === 0;
|
||||
$buttons .= '<li class="' . ($selected ? 'active' : '') . '"><a' . ($selected ? '' : ' data-ajax') . ' data-toggle="tab" href="#' . esc_attr($panel_id . '-' . $visible_index) . '" role="tab" id="' . esc_attr($tab_id) . '" aria-controls="' . esc_attr($panel_id . '-' . $visible_index) . '" aria-selected="' . ($selected ? 'true' : 'false') . '" data-segment="' . (int) $index . '">' . wp_kses_post((string) $tab['title']) . '</a></li>';
|
||||
$buttons .= '<li class="' . ($selected ? 'active' : '') . '"><a' . ($selected ? '' : ' data-ajax') . ' data-toggle="tab" href="#' . esc_attr($panel_id . '-' . $visible_index) . '" role="tab" id="' . esc_attr($tab_id) . '" aria-controls="' . esc_attr($panel_id . '-' . $visible_index) . '" aria-selected="' . ($selected ? 'true' : 'false') . '" tabindex="' . ($selected ? '0' : '-1') . '" data-segment="' . (int) $index . '">' . wp_kses_post((string) $tab['title']) . '</a></li>';
|
||||
if ($selected) {
|
||||
$body = self::ajaxpager($instance, 'tab_posts', $widget_id, 1, (string) $index);
|
||||
} else {
|
||||
@@ -178,6 +178,16 @@ final class ContentRenderer {
|
||||
$animation = (string) ($instance['obs_animation'] ?? '');
|
||||
$html = '';
|
||||
foreach ($term_ids as $key => $term_id) {
|
||||
$card = aksrt_widget_term_aggregation([
|
||||
'term_id' => (int) $term_id,
|
||||
'target_blank' => !empty($instance['target_blank']),
|
||||
'orderby' => (string) ($instance['orderby'] ?? 'modified'),
|
||||
'order' => (($instance['order'] ?? 'desc') === 'asc') ? 'ASC' : 'DESC',
|
||||
'count' => (int) ($instance['count'] ?? 4),
|
||||
]);
|
||||
if ($card === '') {
|
||||
continue;
|
||||
}
|
||||
$attr = '';
|
||||
$col_class = '';
|
||||
if ($animation !== '') {
|
||||
@@ -192,13 +202,7 @@ final class ContentRenderer {
|
||||
$col_class .= ' ' . $row_class;
|
||||
}
|
||||
$html .= '<div class="' . esc_attr(trim($col_class)) . '"' . $attr . '>';
|
||||
$html .= aksrt_widget_term_aggregation([
|
||||
'term_id' => (int) $term_id,
|
||||
'target_blank' => !empty($instance['target_blank']),
|
||||
'orderby' => (string) ($instance['orderby'] ?? 'modified'),
|
||||
'order' => (($instance['order'] ?? 'desc') === 'asc') ? 'ASC' : 'DESC',
|
||||
'count' => (int) ($instance['count'] ?? 4),
|
||||
]);
|
||||
$html .= $card;
|
||||
$html .= '</div>';
|
||||
}
|
||||
return '<div class="clearfix mb6">' . ($is_row ? '<div class="row gutters-5">' : '') . $html . ($is_row ? '</div>' : '') . '</div>';
|
||||
|
||||
@@ -34,9 +34,9 @@ final class GeneralRenderer {
|
||||
}
|
||||
|
||||
private static function render_quote(array $i): string {
|
||||
$quotes = RendererHelpers::lines((string) $i['quotes']);
|
||||
$text = $quotes ? $quotes[array_rand($quotes)] : __('保持好奇,持续创造。', 'aksrt');
|
||||
return '<figure class="aksrt-widget-quote" data-source="' . esc_attr((string) $i['source']) . '" data-interval="' . (int) $i['interval'] . '"><i class="fa fas fa-quote-left" aria-hidden="true"></i><blockquote>' . esc_html($text) . '</blockquote><button type="button" aria-label="' . esc_attr__('换一句', 'aksrt') . '"><i class="fa fas fa-sync-alt" aria-hidden="true"></i></button></figure>';
|
||||
return function_exists('aksrt_get_yiyan_markup')
|
||||
? aksrt_get_yiyan_markup(['class' => 'aksrt-widget-quote'])
|
||||
: '';
|
||||
}
|
||||
|
||||
private static function render_icon_cards(array $i): string {
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace AKSRT\WidgetSystem;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/** Public read-only endpoints for saved widget instances and quotes. */
|
||||
/** Public read-only endpoints for saved widget instances. */
|
||||
final class RestController extends \WP_REST_Controller {
|
||||
public function __construct() {
|
||||
$this->namespace = 'aksrt/v1';
|
||||
@@ -23,12 +23,6 @@ final class RestController extends \WP_REST_Controller {
|
||||
'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 {
|
||||
@@ -59,28 +53,6 @@ final class RestController extends \WP_REST_Controller {
|
||||
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);
|
||||
|
||||
@@ -7,9 +7,11 @@ defined('ABSPATH') || exit;
|
||||
/** Admin-only import, export, template application and preview. */
|
||||
final class TransferController {
|
||||
private const NONCE = 'aksrt_widgets_transfer';
|
||||
private static bool $controls_rendered = false;
|
||||
|
||||
public static function register_hooks(): void {
|
||||
add_action('admin_notices', [self::class, 'render_panel']);
|
||||
add_action('customize_controls_print_footer_scripts', [self::class, 'render_panel']);
|
||||
add_action('admin_post_aksrt_widgets_export', [self::class, 'handle_export']);
|
||||
add_action('admin_post_aksrt_widgets_import', [self::class, 'handle_import']);
|
||||
add_action('admin_post_aksrt_widgets_template', [self::class, 'handle_template']);
|
||||
@@ -17,54 +19,69 @@ final class TransferController {
|
||||
}
|
||||
|
||||
public static function render_panel(): void {
|
||||
$screen = function_exists('get_current_screen') ? get_current_screen() : null;
|
||||
if (!$screen || $screen->id !== 'widgets' || !current_user_can('edit_theme_options')) {
|
||||
if (self::$controls_rendered) {
|
||||
return;
|
||||
}
|
||||
$sidebars = $GLOBALS['wp_registered_sidebars'] ?? [];
|
||||
$screen = function_exists('get_current_screen') ? get_current_screen() : null;
|
||||
if (!$screen || !in_array($screen->id, ['widgets', 'customize'], true) || !current_user_can('edit_theme_options')) {
|
||||
return;
|
||||
}
|
||||
self::$controls_rendered = true;
|
||||
$templates = WidgetTransfer::templates();
|
||||
$notice = isset($_GET['aksrt_widgets_notice']) ? sanitize_key(wp_unslash($_GET['aksrt_widgets_notice'])) : '';
|
||||
if ($notice !== '') {
|
||||
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('小工具配置已更新。', 'aksrt') . '</p></div>';
|
||||
}
|
||||
?>
|
||||
<div class="aksrt-widget-transfer postbox">
|
||||
<div class="postbox-header"><h2><?php esc_html_e('AKSRT 小工具模板与导入导出', 'aksrt'); ?></h2></div>
|
||||
<div class="inside aksrt-widget-transfer__grid">
|
||||
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
|
||||
<h3><?php esc_html_e('导出', 'aksrt'); ?></h3>
|
||||
<input type="hidden" name="action" value="aksrt_widgets_export">
|
||||
<?php wp_nonce_field(self::NONCE); ?>
|
||||
<?php self::sidebar_select($sidebars, 'sidebar', true); ?>
|
||||
<?php submit_button(__('下载 aksrt.widgets.v1', 'aksrt'), 'secondary', 'submit', false); ?>
|
||||
</form>
|
||||
<form method="post" enctype="multipart/form-data" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
|
||||
<h3><?php esc_html_e('导入', 'aksrt'); ?></h3>
|
||||
<input type="hidden" name="action" value="aksrt_widgets_import">
|
||||
<?php wp_nonce_field(self::NONCE); ?>
|
||||
<input type="file" name="widgets_file" accept="application/json,.json" required>
|
||||
<?php self::mode_select(); ?>
|
||||
<?php self::sidebar_select($sidebars, 'target_sidebar', true, __('沿用文件中的区域', 'aksrt')); ?>
|
||||
<?php submit_button(__('导入配置', 'aksrt'), 'secondary', 'submit', false); ?>
|
||||
</form>
|
||||
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
|
||||
<h3><?php esc_html_e('内置模板', 'aksrt'); ?></h3>
|
||||
<input type="hidden" name="action" value="aksrt_widgets_template">
|
||||
<?php wp_nonce_field(self::NONCE); ?>
|
||||
<select name="template" class="aksrt-widget-template-select">
|
||||
<?php foreach ($templates as $key => $template) : ?>
|
||||
<option value="<?php echo esc_attr($key); ?>"><?php echo esc_html((string) $template['label']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php self::mode_select(); ?>
|
||||
<?php self::sidebar_select($sidebars, 'target_sidebar', true, __('使用模板默认区域', 'aksrt')); ?>
|
||||
<div class="aksrt-widget-transfer__actions">
|
||||
<button type="button" class="button aksrt-widget-template-preview"><?php esc_html_e('预览模块', 'aksrt'); ?></button>
|
||||
<?php submit_button(__('应用模板', 'aksrt'), 'primary', 'submit', false); ?>
|
||||
</div>
|
||||
</form>
|
||||
<div class="aksrt-widget-template-preview-output" hidden></div>
|
||||
</div>
|
||||
<div class="aksrt-widget-transfer-modal" id="aksrt-widget-transfer-modal" aria-hidden="true" hidden>
|
||||
<button type="button" class="aksrt-widget-transfer-modal__backdrop" data-aksrt-transfer-close aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>"></button>
|
||||
<section class="aksrt-widget-transfer-modal__dialog" role="dialog" aria-modal="true" aria-labelledby="aksrt-widget-transfer-title">
|
||||
<button type="button" class="aksrt-widget-transfer-modal__close" data-aksrt-transfer-close aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>">×</button>
|
||||
<h2 id="aksrt-widget-transfer-title"><?php esc_html_e('小工具区域管理', 'aksrt'); ?></h2>
|
||||
<p class="description aksrt-widget-transfer-modal__area"></p>
|
||||
<nav class="aksrt-widget-transfer-modal__tabs" aria-label="<?php esc_attr_e('小工具操作', 'aksrt'); ?>">
|
||||
<button type="button" class="button is-active" data-transfer-tab="export"><?php esc_html_e('导出', 'aksrt'); ?></button>
|
||||
<button type="button" class="button" data-transfer-tab="import"><?php esc_html_e('导入', 'aksrt'); ?></button>
|
||||
<button type="button" class="button" data-transfer-tab="template"><?php esc_html_e('内置模板', 'aksrt'); ?></button>
|
||||
</nav>
|
||||
<div class="aksrt-widget-transfer-modal__panel" data-transfer-panel="export">
|
||||
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
|
||||
<input type="hidden" name="action" value="aksrt_widgets_export">
|
||||
<?php wp_nonce_field(self::NONCE); ?>
|
||||
<input type="hidden" name="sidebar" value="">
|
||||
<p><?php esc_html_e('导出当前小工具区域的配置文件。', 'aksrt'); ?></p>
|
||||
<?php submit_button(__('下载配置', 'aksrt'), 'primary', 'submit', false); ?>
|
||||
</form>
|
||||
</div>
|
||||
<div class="aksrt-widget-transfer-modal__panel" data-transfer-panel="import" hidden>
|
||||
<form method="post" enctype="multipart/form-data" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
|
||||
<input type="hidden" name="action" value="aksrt_widgets_import">
|
||||
<?php wp_nonce_field(self::NONCE); ?>
|
||||
<input type="file" name="widgets_file" accept="application/json,.json" required>
|
||||
<?php self::mode_select(); ?>
|
||||
<input type="hidden" name="target_sidebar" value="">
|
||||
<?php submit_button(__('导入配置', 'aksrt'), 'primary', 'submit', false); ?>
|
||||
</form>
|
||||
</div>
|
||||
<div class="aksrt-widget-transfer-modal__panel" data-transfer-panel="template" hidden>
|
||||
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
|
||||
<input type="hidden" name="action" value="aksrt_widgets_template">
|
||||
<?php wp_nonce_field(self::NONCE); ?>
|
||||
<select name="template" class="aksrt-widget-template-select" aria-label="<?php esc_attr_e('选择内置模板', 'aksrt'); ?>">
|
||||
<?php foreach ($templates as $key => $template) : ?>
|
||||
<option value="<?php echo esc_attr($key); ?>"><?php echo esc_html((string) $template['label']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php self::mode_select(); ?>
|
||||
<input type="hidden" name="target_sidebar" value="">
|
||||
<div class="aksrt-widget-transfer__actions">
|
||||
<button type="button" class="button aksrt-widget-template-preview"><?php esc_html_e('预览模块', 'aksrt'); ?></button>
|
||||
<?php submit_button(__('应用模板', 'aksrt'), 'primary', 'submit', false); ?>
|
||||
</div>
|
||||
</form>
|
||||
<div class="aksrt-widget-template-preview-output" hidden></div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
@@ -136,17 +153,6 @@ final class TransferController {
|
||||
exit;
|
||||
}
|
||||
|
||||
private static function sidebar_select(array $sidebars, string $name, bool $allow_empty, string $empty_label = ''): void {
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('小工具区域', 'aksrt') . '</span><select name="' . esc_attr($name) . '">';
|
||||
if ($allow_empty) {
|
||||
echo '<option value="">' . esc_html($empty_label !== '' ? $empty_label : __('全部区域', 'aksrt')) . '</option>';
|
||||
}
|
||||
foreach ($sidebars as $id => $sidebar) {
|
||||
echo '<option value="' . esc_attr((string) $id) . '">' . esc_html((string) ($sidebar['name'] ?? $id)) . '</option>';
|
||||
}
|
||||
echo '</select></label>';
|
||||
}
|
||||
|
||||
private static function mode_select(): void {
|
||||
echo '<select name="mode"><option value="append">' . esc_html__('追加', 'aksrt') . '</option><option value="replace">' . esc_html__('替换区域', 'aksrt') . '</option></select>';
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ final class WidgetCatalog {
|
||||
'general' => [
|
||||
['search', '搜索框', '显示多种样式的站内搜索框', 'sidebar'],
|
||||
['posts_toc', '文章目录树', '显示当前文章的标题目录', 'sidebar'],
|
||||
['quote', '一言', '定时切换本地或在线句子', 'sidebar'],
|
||||
['quote', '一言', '显示并定时切换中英文一言', 'sidebar'],
|
||||
['icon_cards', '图标卡片(竖版)', '图标在文字上方的特色卡片', 'wide'],
|
||||
['video', '视频', '支持常规视频与流媒体格式', 'any'],
|
||||
['embed', '超级嵌入', '按比例嵌入外部在线内容', 'any'],
|
||||
|
||||
@@ -43,11 +43,7 @@ final class WidgetSchemas {
|
||||
['id' => 'in_affix', 'type' => 'switcher', 'label' => '侧栏随动(仅在侧边栏有效)', 'default' => false],
|
||||
];
|
||||
case 'quote':
|
||||
return [
|
||||
['id' => 'source', 'type' => 'select', 'label' => '内容来源', 'default' => 'local', 'options' => ['local' => '本地句子', 'api' => '在线一言']],
|
||||
['id' => 'quotes', 'type' => 'textarea', 'label' => '本地句子', 'description' => '每行一句', 'default' => "保持好奇,持续创造。\n知识因为分享而更有价值。"],
|
||||
['id' => 'interval', 'type' => 'spinner', 'label' => '自动更新间隔(秒)', 'default' => 30, 'min' => 5, 'max' => 300],
|
||||
];
|
||||
return [];
|
||||
case 'video':
|
||||
return [
|
||||
['id' => 'url', 'type' => 'upload', 'label' => '视频地址', 'default' => ''],
|
||||
|
||||
Reference in new issue
Block a user