feat(widgets): complete phase 3 atomic integration
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
namespace AKSRT\WidgetSystem;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/** Import/export contract for schema widgets. */
|
||||
final class WidgetTransfer {
|
||||
public const FORMAT = 'aksrt.widgets.v1';
|
||||
|
||||
/** @return array<string, array<string,mixed>> */
|
||||
public static function templates(): array {
|
||||
return [
|
||||
'article_home' => [
|
||||
'label' => __('文章首页', 'aksrt'),
|
||||
'description' => __('公告、幻灯片、多栏目文章与热榜侧栏。', 'aksrt'),
|
||||
'sidebars' => [
|
||||
'home_top_content' => [
|
||||
['widget' => 'notice', 'settings' => ['title' => '站点公告']],
|
||||
['widget' => 'slider', 'settings' => ['title' => '精选内容']],
|
||||
['widget' => 'tab_posts', 'settings' => ['title' => '热门栏目']],
|
||||
],
|
||||
'home_sidebar' => [['widget' => 'hot_posts', 'settings' => ['title' => '热门文章']]],
|
||||
],
|
||||
],
|
||||
'article_list' => [
|
||||
'label' => __('文章列表', 'aksrt'),
|
||||
'description' => __('标题、分类卡片与可分页文章列表。', 'aksrt'),
|
||||
'sidebars' => [
|
||||
'home_top_content' => [
|
||||
['widget' => 'text_title', 'settings' => ['heading' => '最新知识内容', 'description' => '持续更新的课程、文章与资料']],
|
||||
['widget' => 'term_cards', 'settings' => ['title' => '内容分类']],
|
||||
['widget' => 'posts', 'settings' => ['title' => '最新发布', 'paginate' => 'number']],
|
||||
],
|
||||
],
|
||||
],
|
||||
'links_navigation' => [
|
||||
'label' => __('链接导航', 'aksrt'),
|
||||
'description' => __('搜索、分类入口与链接列表。', 'aksrt'),
|
||||
'sidebars' => [
|
||||
'home_top_content' => [
|
||||
['widget' => 'icon_cover_cards', 'settings' => ['title' => '常用入口']],
|
||||
['widget' => 'links', 'settings' => ['title' => '链接导航']],
|
||||
],
|
||||
'home_sidebar' => [['widget' => 'search', 'settings' => ['title' => '快速查找']]],
|
||||
],
|
||||
],
|
||||
'article_sidebar' => [
|
||||
'label' => __('文章侧栏', 'aksrt'),
|
||||
'description' => __('目录、作者、付费授权与热榜文章。', 'aksrt'),
|
||||
'sidebars' => [
|
||||
'single_sidebar' => [
|
||||
['widget' => 'posts_toc', 'settings' => ['title' => '文章目录']],
|
||||
['widget' => 'author_card', 'settings' => ['title' => '作者']],
|
||||
['widget' => 'post_pay', 'settings' => []],
|
||||
['widget' => 'hot_posts', 'settings' => ['title' => '热门文章']],
|
||||
],
|
||||
],
|
||||
],
|
||||
'knowledge_landing' => [
|
||||
'label' => __('知识付费落地页', 'aksrt'),
|
||||
'description' => __('亮点、方案、评价与常见问题。', 'aksrt'),
|
||||
'sidebars' => [
|
||||
'home_top_content' => [
|
||||
['widget' => 'feature_intro', 'settings' => ['title' => '系统化学习路径']],
|
||||
['widget' => 'highlights', 'settings' => ['title' => '内容亮点']],
|
||||
['widget' => 'pricing_plans', 'settings' => ['title' => '选择学习方案']],
|
||||
['widget' => 'reviews', 'settings' => ['title' => '学员评价']],
|
||||
['widget' => 'faq', 'settings' => ['title' => '常见问题']],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public static function template_payload(string $template): array {
|
||||
$templates = self::templates();
|
||||
if (!isset($templates[$template])) {
|
||||
throw new \InvalidArgumentException(__('未知的小工具模板。', 'aksrt'));
|
||||
}
|
||||
return [
|
||||
'format' => self::FORMAT,
|
||||
'template' => $template,
|
||||
'sidebars' => $templates[$template]['sidebars'],
|
||||
];
|
||||
}
|
||||
|
||||
public static function export(string $sidebar = ''): array {
|
||||
$assignments = wp_get_sidebars_widgets();
|
||||
$sidebars = [];
|
||||
foreach ($assignments as $sidebar_id => $widget_ids) {
|
||||
if ($sidebar !== '' && $sidebar_id !== $sidebar) {
|
||||
continue;
|
||||
}
|
||||
if (!is_array($widget_ids)) {
|
||||
continue;
|
||||
}
|
||||
$items = [];
|
||||
foreach ($widget_ids as $widget_id) {
|
||||
$item = self::export_widget((string) $widget_id);
|
||||
if ($item !== null) {
|
||||
$items[] = $item;
|
||||
}
|
||||
}
|
||||
if ($items || $sidebar !== '') {
|
||||
$sidebars[$sidebar_id] = $items;
|
||||
}
|
||||
}
|
||||
return [
|
||||
'format' => self::FORMAT,
|
||||
'exported_at' => gmdate('c'),
|
||||
'sidebars' => $sidebars,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array{sidebars:int,widgets:int} */
|
||||
public static function import(array $payload, string $mode = 'append', string $target_sidebar = ''): array {
|
||||
if (($payload['format'] ?? '') !== self::FORMAT || !isset($payload['sidebars']) || !is_array($payload['sidebars'])) {
|
||||
throw new \InvalidArgumentException(__('仅接受 aksrt.widgets.v1 格式。', 'aksrt'));
|
||||
}
|
||||
$mode = $mode === 'replace' ? 'replace' : 'append';
|
||||
$registered = array_keys($GLOBALS['wp_registered_sidebars'] ?? []);
|
||||
$assignments = wp_get_sidebars_widgets();
|
||||
$sources = array_slice($payload['sidebars'], 0, 100, true);
|
||||
if ($target_sidebar !== '') {
|
||||
$combined = [];
|
||||
foreach ($sources as $items) {
|
||||
if (is_array($items)) {
|
||||
$combined = array_merge($combined, $items);
|
||||
}
|
||||
}
|
||||
$sources = [$target_sidebar => $combined];
|
||||
}
|
||||
$changed = 0;
|
||||
$created = 0;
|
||||
|
||||
foreach ($sources as $source_sidebar => $items) {
|
||||
$sidebar_id = $target_sidebar !== '' ? sanitize_key($target_sidebar) : sanitize_key((string) $source_sidebar);
|
||||
if (!in_array($sidebar_id, $registered, true) || !is_array($items)) {
|
||||
continue;
|
||||
}
|
||||
$current = isset($assignments[$sidebar_id]) && is_array($assignments[$sidebar_id]) ? $assignments[$sidebar_id] : [];
|
||||
if ($mode === 'replace') {
|
||||
$current = [];
|
||||
}
|
||||
foreach (array_slice($items, 0, 100) as $item) {
|
||||
if (!is_array($item)) {
|
||||
continue;
|
||||
}
|
||||
$widget_id = self::create_widget($item);
|
||||
if ($widget_id !== '') {
|
||||
$current[] = $widget_id;
|
||||
$created++;
|
||||
}
|
||||
}
|
||||
$assignments[$sidebar_id] = array_values(array_unique($current));
|
||||
$changed++;
|
||||
}
|
||||
wp_set_sidebars_widgets($assignments);
|
||||
return ['sidebars' => $changed, 'widgets' => $created];
|
||||
}
|
||||
|
||||
public static function preview(string $template): string {
|
||||
$payload = self::template_payload($template);
|
||||
$html = '';
|
||||
foreach ($payload['sidebars'] as $sidebar_id => $items) {
|
||||
$html .= '<section class="aksrt-widget-template-preview__area"><h4>' . esc_html((string) $sidebar_id) . '</h4>';
|
||||
foreach ($items as $item) {
|
||||
$definition = WidgetRegistry::get((string) ($item['widget'] ?? ''));
|
||||
if ($definition === null) {
|
||||
continue;
|
||||
}
|
||||
$settings = $definition->normalize((array) ($item['settings'] ?? []));
|
||||
$html .= '<article class="aksrt-widget-template-preview__module aksrt-widget aksrt-widget--' . esc_attr($definition->slug()) . '"><strong>' . esc_html($definition->name()) . '</strong><div class="aksrt-widget__content">';
|
||||
$html .= RendererRegistry::render($definition->renderer(), $settings, ['widget_id' => 'preview-0']);
|
||||
$html .= '</div></article>';
|
||||
}
|
||||
$html .= '</section>';
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
private static function export_widget(string $widget_id): ?array {
|
||||
if (!preg_match('/^(aksrt_widget_[a-z0-9_]+)-([1-9][0-9]*)$/', $widget_id, $matches)) {
|
||||
return null;
|
||||
}
|
||||
$definition = WidgetRegistry::get_by_id_base($matches[1]);
|
||||
if ($definition === null) {
|
||||
return null;
|
||||
}
|
||||
$settings = get_option('widget_' . $definition->id_base(), []);
|
||||
$number = (int) $matches[2];
|
||||
if (!is_array($settings) || !isset($settings[$number]) || !is_array($settings[$number])) {
|
||||
return null;
|
||||
}
|
||||
return ['widget' => $definition->slug(), 'settings' => $definition->normalize($settings[$number])];
|
||||
}
|
||||
|
||||
private static function create_widget(array $item): string {
|
||||
$definition = WidgetRegistry::get(sanitize_key((string) ($item['widget'] ?? '')));
|
||||
if ($definition === null) {
|
||||
return '';
|
||||
}
|
||||
$option_name = 'widget_' . $definition->id_base();
|
||||
$settings = get_option($option_name, []);
|
||||
$settings = is_array($settings) ? $settings : [];
|
||||
$numbers = array_filter(array_map('intval', array_keys($settings)), static fn(int $number): bool => $number > 0);
|
||||
$number = $numbers ? max($numbers) + 1 : 1;
|
||||
$settings[$number] = $definition->normalize((array) ($item['settings'] ?? []));
|
||||
$settings['_multiwidget'] = 1;
|
||||
update_option($option_name, $settings);
|
||||
return $definition->id_base() . '-' . $number;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user