215 lines
7.8 KiB
PHP
215 lines
7.8 KiB
PHP
<?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;
|
|
}
|
|
}
|