Move ~94 root class shells and root *-traits into domain directories (core, auth, user, credits, content, payment/gateways, forum, shop, sms, ai, messages, extensions, widgets, settings, admin). Update bootstrap require paths, co-locate traits with owners, fix forum/user relative requires, drop duplicate shop controller requires, and wire home-modules load. Path-only; class names and init order unchanged.
294 lines
12 KiB
PHP
294 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* AKSRT Term Meta
|
|
*
|
|
* 为分类(category)和标签(post_tag)添加局部配置字段:
|
|
* - 头图/封面 URL
|
|
* - 自定义简介
|
|
* - 列表布局模式覆盖
|
|
* - 侧栏开关
|
|
* - 推荐位开关
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
class AKSRT_Term_Meta {
|
|
|
|
private static $instance = null;
|
|
|
|
/** Supported taxonomies */
|
|
private $taxonomies = ['category', 'post_tag'];
|
|
|
|
/** Meta key for storing term-level settings */
|
|
const META_KEY = AKSRT_Domain_Model::TERM_META_KEY;
|
|
|
|
public static function get_instance(): self {
|
|
if (null === self::$instance) self::$instance = new self();
|
|
return self::$instance;
|
|
}
|
|
|
|
public function init(): void {
|
|
// Register meta fields
|
|
foreach ($this->taxonomies as $tax) {
|
|
add_action("{$tax}_add_form_fields", [$this, 'render_add_fields']);
|
|
add_action("{$tax}_edit_form_fields", [$this, 'render_edit_fields']);
|
|
add_action("created_{$tax}", [$this, 'save_term_meta']);
|
|
add_action("edited_{$tax}", [$this, 'save_term_meta']);
|
|
}
|
|
|
|
add_action('pre_get_posts', [$this, 'filter_archive_query']);
|
|
}
|
|
|
|
/**
|
|
* Get term meta with defaults.
|
|
*/
|
|
public static function get_meta(int $term_id): array {
|
|
$defaults = AKSRT_Domain_Model::default_term_meta();
|
|
$saved = get_term_meta($term_id, self::META_KEY, true);
|
|
if (!is_array($saved)) $saved = [];
|
|
return array_merge($defaults, $saved);
|
|
}
|
|
|
|
/**
|
|
* Get a single term meta value.
|
|
*/
|
|
public static function get_meta_value(int $term_id, string $key, $default = '') {
|
|
$meta = self::get_meta($term_id);
|
|
return $meta[$key] ?? $default;
|
|
}
|
|
|
|
/**
|
|
* Resolve global page-display settings with term-level overrides.
|
|
*
|
|
* @param WP_Term $term Archive term.
|
|
* @return array<string,mixed>
|
|
*/
|
|
public static function resolve_archive_settings(WP_Term $term): array {
|
|
$taxonomy = $term->taxonomy === 'post_tag' ? 'post_tag' : 'category';
|
|
$global = $taxonomy === 'post_tag' && class_exists('AKSRT_Settings')
|
|
? AKSRT_Settings::get_tag_page_settings()
|
|
: (class_exists('AKSRT_Settings') ? AKSRT_Settings::get_category_page_settings() : [
|
|
'header_style' => 'cover',
|
|
'cover_enabled' => true,
|
|
'desc_enabled' => true,
|
|
'list_mode' => 'grid',
|
|
'filter_bar' => true,
|
|
'sidebar' => true,
|
|
'per_page' => 12,
|
|
]);
|
|
|
|
$saved = get_term_meta($term->term_id, self::META_KEY, true);
|
|
if (!is_array($saved)) {
|
|
$saved = [];
|
|
}
|
|
|
|
// Empty term layout_mode means follow global post-list settings (Zibll).
|
|
// Do not fall back to page-display list_mode=grid which forced card everywhere.
|
|
$layout_preset = isset($saved['layout_mode']) ? sanitize_key((string) $saved['layout_mode']) : '';
|
|
if ($layout_preset === '') {
|
|
$resolved_layout = ['mode' => '', 'card_style' => ''];
|
|
$layout_mode = '';
|
|
} else {
|
|
$layout_mode = $layout_preset;
|
|
$layout_map = [
|
|
'grid' => ['mode' => 'card', 'card_style' => 'thumb'],
|
|
'card' => ['mode' => 'card', 'card_style' => 'thumb'],
|
|
'list' => ['mode' => 'list', 'card_style' => 'thumb'],
|
|
'compact' => ['mode' => 'list', 'card_style' => 'compact'],
|
|
'mini' => ['mode' => 'mini', 'card_style' => 'thumb'],
|
|
'grid-cms' => ['mode' => 'grid-cms', 'card_style' => 'thumb'],
|
|
];
|
|
$resolved_layout = $layout_map[$layout_mode] ?? ['mode' => 'list', 'card_style' => 'thumb'];
|
|
}
|
|
|
|
$header_style = (string) ($global['header_style'] ?? 'simple');
|
|
if (!in_array($header_style, ['cover', 'simple', 'none'], true)) {
|
|
$header_style = 'simple';
|
|
}
|
|
|
|
if (array_key_exists('show_header', $saved)) {
|
|
$header_style = !empty($saved['show_header'])
|
|
? ($header_style === 'none' ? 'simple' : $header_style)
|
|
: 'none';
|
|
}
|
|
|
|
$sidebar_enabled = (bool) ($global['sidebar'] ?? true);
|
|
if (($saved['show_sidebar'] ?? '') === '1') {
|
|
$sidebar_enabled = true;
|
|
} elseif (($saved['show_sidebar'] ?? '') === '0') {
|
|
$sidebar_enabled = false;
|
|
}
|
|
|
|
$cover_url = isset($saved['cover_url']) ? esc_url_raw((string) $saved['cover_url']) : '';
|
|
if ($cover_url === '' && $taxonomy === 'category' && !empty($global['cover_enabled']) && class_exists('AKSRT_Settings')) {
|
|
$cover_url = AKSRT_Settings::get_category_cover_url($term->term_id);
|
|
}
|
|
|
|
$description = isset($saved['description']) ? sanitize_textarea_field((string) $saved['description']) : '';
|
|
if ($description === '') {
|
|
$description = (string) $term->description;
|
|
}
|
|
|
|
return [
|
|
'taxonomy' => $taxonomy,
|
|
'layout_mode' => $resolved_layout['mode'],
|
|
'card_style' => $resolved_layout['card_style'],
|
|
'layout_preset' => $layout_mode,
|
|
'header_style' => $header_style,
|
|
'show_sidebar' => $sidebar_enabled,
|
|
'show_cover' => $header_style === 'cover' && !empty($global['cover_enabled']) && $cover_url !== '',
|
|
'show_desc' => $taxonomy === 'post_tag' ? $description !== '' : (!empty($global['desc_enabled']) && $description !== ''),
|
|
'filter_bar' => $taxonomy === 'category' && !empty($global['filter_bar']),
|
|
'per_page' => max(4, min(48, (int) ($global['per_page'] ?? 12))),
|
|
'cover_url' => $cover_url,
|
|
'description' => $description,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Apply archive per-page settings to the main query.
|
|
*/
|
|
public function filter_archive_query(WP_Query $query): void {
|
|
if (is_admin() || !$query->is_main_query()) {
|
|
return;
|
|
}
|
|
|
|
$term = null;
|
|
if ($query->is_category()) {
|
|
$cat_id = absint($query->get('cat'));
|
|
if ($cat_id > 0) {
|
|
$term = get_term($cat_id, 'category');
|
|
} else {
|
|
$slug = sanitize_title((string) $query->get('category_name'));
|
|
if ($slug !== '') {
|
|
$term = get_term_by('slug', $slug, 'category');
|
|
}
|
|
}
|
|
} elseif ($query->is_tag()) {
|
|
$tag_id = absint($query->get('tag_id'));
|
|
if ($tag_id > 0) {
|
|
$term = get_term($tag_id, 'post_tag');
|
|
} else {
|
|
$slug = sanitize_title((string) $query->get('tag'));
|
|
if ($slug !== '') {
|
|
$term = get_term_by('slug', $slug, 'post_tag');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$term instanceof WP_Term) {
|
|
return;
|
|
}
|
|
|
|
$settings = self::resolve_archive_settings($term);
|
|
if (!empty($settings['per_page'])) {
|
|
$query->set('posts_per_page', (int) $settings['per_page']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render fields for "Add new term" form.
|
|
*/
|
|
public function render_add_fields(string $taxonomy): void {
|
|
?>
|
|
<div class="form-field">
|
|
<label for="aksrt_term_cover_url"><?php esc_html_e('封面图 URL', 'aksrt'); ?></label>
|
|
<input type="url" name="aksrt_term_meta[cover_url]" id="aksrt_term_cover_url" value="" placeholder="https://...">
|
|
<p class="description"><?php esc_html_e('分类/标签页顶部封面图,留空则不显示。', 'aksrt'); ?></p>
|
|
</div>
|
|
<div class="form-field">
|
|
<label for="aksrt_term_layout"><?php esc_html_e('列表布局', 'aksrt'); ?></label>
|
|
<select name="aksrt_term_meta[layout_mode]" id="aksrt_term_layout">
|
|
<option value=""><?php esc_html_e('跟随全局设置', 'aksrt'); ?></option>
|
|
<option value="card"><?php esc_html_e('卡片模式', 'aksrt'); ?></option>
|
|
<option value="list"><?php esc_html_e('列表模式', 'aksrt'); ?></option>
|
|
<option value="mini"><?php esc_html_e('迷你模式', 'aksrt'); ?></option>
|
|
</select>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render fields for "Edit term" form.
|
|
*/
|
|
public function render_edit_fields(WP_Term $term): void {
|
|
$meta = self::get_meta($term->term_id);
|
|
?>
|
|
<tr class="form-field">
|
|
<th scope="row"><label for="aksrt_term_cover_url"><?php esc_html_e('封面图 URL', 'aksrt'); ?></label></th>
|
|
<td>
|
|
<input type="url" name="aksrt_term_meta[cover_url]" id="aksrt_term_cover_url" value="<?php echo esc_url($meta['cover_url']); ?>" class="regular-text" placeholder="https://...">
|
|
<p class="description"><?php esc_html_e('分类/标签页顶部封面图,留空则不显示。', 'aksrt'); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr class="form-field">
|
|
<th scope="row"><label for="aksrt_term_desc"><?php esc_html_e('自定义简介', 'aksrt'); ?></label></th>
|
|
<td>
|
|
<textarea name="aksrt_term_meta[description]" id="aksrt_term_desc" rows="3" class="large-text"><?php echo esc_textarea($meta['description']); ?></textarea>
|
|
<p class="description"><?php esc_html_e('覆盖默认的分类/标签描述。', 'aksrt'); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr class="form-field">
|
|
<th scope="row"><label for="aksrt_term_layout"><?php esc_html_e('列表布局', 'aksrt'); ?></label></th>
|
|
<td>
|
|
<select name="aksrt_term_meta[layout_mode]" id="aksrt_term_layout">
|
|
<option value="" <?php selected($meta['layout_mode'], ''); ?>><?php esc_html_e('跟随全局设置', 'aksrt'); ?></option>
|
|
<option value="card" <?php selected($meta['layout_mode'], 'card'); ?>><?php esc_html_e('卡片模式', 'aksrt'); ?></option>
|
|
<option value="list" <?php selected($meta['layout_mode'], 'list'); ?>><?php esc_html_e('列表模式', 'aksrt'); ?></option>
|
|
<option value="mini" <?php selected($meta['layout_mode'], 'mini'); ?>><?php esc_html_e('迷你模式', 'aksrt'); ?></option>
|
|
</select>
|
|
</td>
|
|
</tr>
|
|
<tr class="form-field">
|
|
<th scope="row"><?php esc_html_e('显示设置', 'aksrt'); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="aksrt_term_meta[show_header]" value="1" <?php checked(!empty($meta['show_header'])); ?>>
|
|
<?php esc_html_e('显示页面头部(标题、描述)', 'aksrt'); ?>
|
|
</label><br>
|
|
<label>
|
|
<input type="checkbox" name="aksrt_term_meta[show_sidebar]" value="1" <?php checked($meta['show_sidebar'] === '1'); ?>>
|
|
<?php esc_html_e('强制显示侧栏', 'aksrt'); ?>
|
|
</label><br>
|
|
<label>
|
|
<input type="checkbox" name="aksrt_term_meta[show_featured]" value="1" <?php checked(!empty($meta['show_featured'])); ?>>
|
|
<?php esc_html_e('显示推荐文章位', 'aksrt'); ?>
|
|
</label>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Save term meta from form submission.
|
|
*/
|
|
public function save_term_meta(int $term_id): void {
|
|
if (!isset($_POST['aksrt_term_meta']) || !is_array($_POST['aksrt_term_meta'])) {
|
|
return;
|
|
}
|
|
if (!current_user_can('manage_categories')) {
|
|
return;
|
|
}
|
|
|
|
$raw = $_POST['aksrt_term_meta'];
|
|
$sanitized = [
|
|
'cover_url' => isset($raw['cover_url']) ? esc_url_raw($raw['cover_url']) : '',
|
|
'description' => isset($raw['description']) ? sanitize_textarea_field($raw['description']) : '',
|
|
'layout_mode' => isset($raw['layout_mode']) ? sanitize_key($raw['layout_mode']) : '',
|
|
'show_sidebar' => isset($raw['show_sidebar']) ? '1' : '',
|
|
'show_header' => isset($raw['show_header']) ? true : false,
|
|
'show_featured' => isset($raw['show_featured']) ? true : false,
|
|
];
|
|
|
|
// Only allow valid layout modes
|
|
$valid_modes = ['', 'card', 'list', 'mini', 'grid-cms'];
|
|
if (!in_array($sanitized['layout_mode'], $valid_modes, true)) {
|
|
$sanitized['layout_mode'] = '';
|
|
}
|
|
|
|
update_term_meta($term_id, self::META_KEY, $sanitized);
|
|
}
|
|
}
|