Files
wordpress-theme-aksrt/inc/classes/class-aksrt-term-meta.php
T

289 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 = [];
}
$layout_mode = isset($saved['layout_mode']) && $saved['layout_mode'] !== ''
? sanitize_key((string) $saved['layout_mode'])
: (string) ($global['list_mode'] ?? 'grid');
$layout_map = [
'grid' => ['mode' => 'card', 'card_style' => 'standard'],
'card' => ['mode' => 'card', 'card_style' => 'standard'],
'list' => ['mode' => 'list', 'card_style' => 'standard'],
'compact' => ['mode' => 'list', 'card_style' => 'compact'],
'mini' => ['mode' => 'mini', 'card_style' => 'standard'],
'grid-cms' => ['mode' => 'grid-cms', 'card_style' => 'standard'],
];
$resolved_layout = $layout_map[$layout_mode] ?? ['mode' => 'card', 'card_style' => 'standard'];
$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);
}
}