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

384 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* AKSRT Theme Setup
*
* 主题初始化:注册菜单、侧边栏、图片尺寸、自定义文章类型等
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
class AKSRT_Setup {
use AKSRT_Setup_Part_1;
use AKSRT_Setup_Part_2;
private static $instance = null;
private function __construct() {}
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function init(): void {
add_action('after_setup_theme', [$this, 'load_textdomain'], 1);
add_action('after_setup_theme', [$this, 'setup_theme']);
add_action('widgets_init', [$this, 'register_sidebars']);
add_filter('dynamic_sidebar_params', [$this, 'filter_sidebar_widget_params']);
add_action('init', [$this, 'register_nav_menus'], 0);
add_action('init', [$this, 'register_post_types'], 1);
add_action('init', [$this, 'register_meta_fields'], 5);
add_action('init', [$this, 'register_shop_rewrite_rules'], 20);
add_filter('query_vars', [$this, 'register_shop_query_vars']);
add_action('template_redirect', [$this, 'shop_template_redirect']);
// Shop fulfillment hook (§6.5 R68)
if (class_exists('AKSRT_Shop')) {
add_action('aksrt_order_paid', [AKSRT_Shop::get_instance(), 'handle_order_paid'], 10, 2);
}
// Auto-confirm receipt cron (§6.5 R68)
if (!wp_next_scheduled('aksrt_auto_confirm_receipt')) {
wp_schedule_event(time(), 'hourly', 'aksrt_auto_confirm_receipt');
}
if (class_exists('AKSRT_Shop')) {
add_action('aksrt_auto_confirm_receipt', [AKSRT_Shop::get_instance(), 'run_auto_confirm_cron']);
}
// 文章浏览量统计(单次访问)
add_action('wp', 'aksrt_set_post_views');
// 隐藏默认WordPress管理菜单
add_action('admin_menu', [$this, 'hide_default_menus'], 999);
}
public function load_textdomain(): void {
load_theme_textdomain('aksrt', AKSRT_DIR . '/languages');
}
public function setup_theme(): void {
// HTML5 支持
add_theme_support('html5', [
'search-form', 'comment-form', 'comment-list',
'gallery', 'caption', 'style', 'script',
'navigation-widgets',
]);
add_theme_support('title-tag');
add_theme_support('post-thumbnails');
add_theme_support('custom-logo', [
'height' => 120,
'width' => 300,
'flex-height' => true,
'flex-width' => true,
'header-text' => true,
]);
add_theme_support('customize-selective-refresh-widgets');
add_theme_support('align-wide');
add_theme_support('wp-block-styles');
add_theme_support('responsive-embeds');
// 文章格式
add_theme_support('post-formats', ['aside', 'image', 'video', 'quote', 'link', 'gallery']);
// 图片尺寸
add_image_size('aksrt-cover', 1200, 675, true);
add_image_size('aksrt-card', 400, 260, true);
add_image_size('aksrt-thumb', 150, 150, true);
add_image_size('aksrt-banner', 1920, 500, true);
// 内容宽度(用于区块编辑器)
if (!isset($GLOBALS['content_width'])) {
$GLOBALS['content_width'] = 900;
}
}
public function register_nav_menus(): void {
register_nav_menus([
'primary' => __('主导航', 'aksrt'),
'mobile' => __('移动端菜单', 'aksrt'),
'footer' => __('页脚菜单', 'aksrt'),
'social' => __('社交链接', 'aksrt'),
'header_actions' => __('顶部右侧入口', 'aksrt'),
]);
}
/**
* 注册自定义文章类型
*
* v3.4: 移除 aksrt_resource CPT,资源元数据统一挂在 post 上,
* 通过 _aksrt_is_resource meta 标记单篇文章是否为“资源”。
*/
public function register_post_types(): void {
// Shop rewrite settings (safe fallback if settings not yet loaded)
$shop_rewrite = [];
if (class_exists('AKSRT_Settings')) {
$shop = AKSRT_Settings::get_shop_settings();
$shop_rewrite = $shop['rewrite'] ?? [];
}
$product_slug = sanitize_key((string) ($shop_rewrite['product_slug'] ?? 'product'));
$suffix_html = !empty($shop_rewrite['suffix_html']);
// 商品 CPT
register_post_type('aksrt_product', [
'labels' => [
'name' => __('商品', 'aksrt'),
'singular_name' => __('商品', 'aksrt'),
'add_new' => __('新增商品', 'aksrt'),
'add_new_item' => __('新增商品', 'aksrt'),
'edit_item' => __('编辑商品', 'aksrt'),
'new_item' => __('新商品', 'aksrt'),
'view_item' => __('查看商品', 'aksrt'),
'search_items' => __('搜索商品', 'aksrt'),
'not_found' => __('未找到商品', 'aksrt'),
'not_found_in_trash' => __('回收站中无商品', 'aksrt'),
'menu_name' => __('商品', 'aksrt'),
],
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => false,
'show_in_rest' => true,
'has_archive' => true,
'hierarchical' => false,
'supports' => ['title', 'editor', 'excerpt', 'thumbnail', 'author', 'revisions'],
'rewrite' => ['slug' => $product_slug, 'with_front' => false],
'capability_type' => 'post',
'map_meta_cap' => true,
]);
// 商品分类 taxonomy(独立于文章 category)
register_taxonomy('aksrt_product_cat', 'aksrt_product', [
'labels' => [
'name' => __('商品分类', 'aksrt'),
'singular_name' => __('商品分类', 'aksrt'),
'search_items' => __('搜索商品分类', 'aksrt'),
'all_items' => __('所有商品分类', 'aksrt'),
'parent_item' => __('父级分类', 'aksrt'),
'edit_item' => __('编辑分类', 'aksrt'),
'add_new_item' => __('新增分类', 'aksrt'),
'menu_name' => __('商品分类', 'aksrt'),
],
'public' => true,
'hierarchical' => true,
'show_ui' => false,
'show_in_rest' => true,
'show_admin_column' => false,
'rewrite' => ['slug' => 'product-cat', 'with_front' => false],
]);
// 商品标签 taxonomy(独立于文章 post_tag)
register_taxonomy('aksrt_product_tag', 'aksrt_product', [
'labels' => [
'name' => __('商品标签', 'aksrt'),
'singular_name' => __('商品标签', 'aksrt'),
'search_items' => __('搜索商品标签', 'aksrt'),
'all_items' => __('所有商品标签', 'aksrt'),
'edit_item' => __('编辑标签', 'aksrt'),
'add_new_item' => __('新增标签', 'aksrt'),
'menu_name' => __('商品标签', 'aksrt'),
],
'public' => true,
'hierarchical' => false,
'show_ui' => false,
'show_in_rest' => true,
'show_admin_column' => false,
'rewrite' => ['slug' => 'product-tag', 'with_front' => false,
],
]);
// 项目展示
register_post_type('project', [
'labels' => [
'name' => __('项目展示', 'aksrt'),
'singular_name' => __('项目', 'aksrt'),
'add_new' => __('新建项目', 'aksrt'),
'add_new_item' => __('新建项目', 'aksrt'),
'edit_item' => __('编辑项目', 'aksrt'),
'new_item' => __('新项目', 'aksrt'),
'view_item' => __('查看项目', 'aksrt'),
'search_items' => __('搜索项目', 'aksrt'),
'not_found' => __('未找到项目', 'aksrt'),
'not_found_in_trash' => __('回收站中无项目', 'aksrt'),
'menu_name' => __('项目展示', 'aksrt'),
],
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => false,
'show_in_nav_menus' => false,
'has_archive' => false,
'hierarchical' => false,
'menu_position' => 25,
'menu_icon' => 'dashicons-portfolio',
'supports' => ['title', 'editor', 'excerpt', 'thumbnail', 'author', 'revisions'],
'rewrite' => ['slug' => 'project'],
'capability_type' => 'post',
'map_meta_cap' => true,
]);
}
/**
* 注册资源 post meta + 分类 term meta(aksrt_flow)
*
* 全部 show_in_rest,便于 Block Editor + Vue SPA 读写。
*
* v3.4: 资源元数据全部挂在 post 上,配合 _aksrt_is_resource 开关使用。
*/
public function register_meta_fields(): void {
$post_edit_auth = static function () {
return current_user_can('edit_posts');
};
// Core switch: mark a post as resource.
register_post_meta('post', AKSRT_Domain_Model::RESOURCE_MARKER_KEY, [
'type' => 'boolean',
'description' => 'Whether this post is a resource post.',
'single' => true,
'default' => false,
'show_in_rest' => true,
'sanitize_callback' => static function ($v) { return (bool) $v; },
'auth_callback' => $post_edit_auth,
]);
register_post_meta('post', AKSRT_Domain_Model::PAY_META_KEY, [
'type' => 'object',
'description' => 'Canonical paid content payload.',
'single' => true,
'default' => AKSRT_Domain_Model::default_pay_meta(),
'show_in_rest' => [
'schema' => [
'type' => 'object',
'properties' => [
'pay_type' => [
'type' => 'string',
'enum' => array_keys(AKSRT_Domain_Model::pay_type_labels()),
],
'price' => ['type' => 'number'],
'original_price' => ['type' => 'number'],
'vip1_price' => ['type' => 'number'],
'vip2_price' => ['type' => 'number'],
'version' => ['type' => 'string'],
'size' => ['type' => 'string'],
'license' => ['type' => 'string'],
'compat' => ['type' => 'string'],
'demo_url' => ['type' => 'string', 'format' => 'uri'],
'video_url' => ['type' => 'string', 'format' => 'uri'],
'downloads' => [
'type' => 'array',
'items' => [
'type' => 'object',
'properties' => [
'label' => ['type' => 'string'],
'url' => ['type' => 'string', 'format' => 'uri'],
'type' => ['type' => 'string'],
'extract_code' => ['type' => 'string'],
],
],
],
'attributes' => [
'type' => 'array',
'items' => [
'type' => 'object',
'properties' => [
'key' => ['type' => 'string'],
'value' => ['type' => 'string'],
],
],
],
'gallery' => [
'type' => 'array',
'items' => ['type' => 'string', 'format' => 'uri'],
],
'require_login' => ['type' => 'boolean'],
'require_captcha' => ['type' => 'boolean'],
],
],
],
'sanitize_callback' => [AKSRT_Domain_Model::class, 'sanitize_pay_meta'],
'auth_callback' => $post_edit_auth,
]);
register_post_meta('post', AKSRT_Domain_Model::PAY_TYPE_KEY, [
'type' => 'string',
'description' => 'Query-friendly paid content type.',
'single' => true,
'default' => AKSRT_Domain_Model::PAY_TYPE_NO,
'show_in_rest' => true,
'sanitize_callback' => [AKSRT_Domain_Model::class, 'normalize_pay_type'],
'auth_callback' => $post_edit_auth,
]);
register_post_meta('post', AKSRT_Domain_Model::PAY_PRICE_KEY, [
'type' => 'number',
'description' => 'Query-friendly paid content price.',
'single' => true,
'default' => 0,
'show_in_rest' => true,
'sanitize_callback' => static function ($value) {
return max(0, (float) $value);
},
'auth_callback' => $post_edit_auth,
]);
foreach ([
AKSRT_Domain_Model::RESOURCE_DOWNLOAD_COUNT_KEY => 'Resource download count',
AKSRT_Domain_Model::RESOURCE_VIEW_COUNT_KEY => 'Resource view count',
AKSRT_Domain_Model::POST_VIEW_COUNT_KEY => 'Generic post view count',
] as $meta_key => $description) {
register_post_meta('post', $meta_key, [
'type' => 'integer',
'description' => $description,
'single' => true,
'default' => 0,
'show_in_rest' => true,
'sanitize_callback' => 'absint',
'auth_callback' => $post_edit_auth,
]);
}
register_term_meta('category', 'aksrt_flow', [
'type' => 'string',
'description' => 'Category flow mode: inherit/blog/cms',
'single' => true,
'default' => 'inherit',
'show_in_rest' => true,
'sanitize_callback' => function ($v) {
$v = is_string($v) ? $v : 'inherit';
return in_array($v, ['inherit', 'blog', 'cms'], true) ? $v : 'inherit';
},
'auth_callback' => function () {
return current_user_can('manage_categories');
},
]);
}
public static function sanitize_downloads_meta($value): array {
return AKSRT_Domain_Model::sanitize_downloads($value);
}
/**
* 为 Customizer 小工具实时预览补齐编辑态包裹。
*
* @param array $params Dynamic sidebar params.
* @return array
*/
public function filter_sidebar_widget_params(array $params): array {
if (!is_customize_preview() || empty($params[0]['before_widget']) || empty($params[0]['after_widget'])) {
return $params;
}
$params[0]['before_widget'] = '<div class="customize-preview-widget-box"><div class="customize-preview-widget-edit"></div>' . $params[0]['before_widget'];
$params[0]['after_widget'] = $params[0]['after_widget'] . '</div>';
return $params;
}
/**
* 注册侧边栏
*/
}
return AKSRT_Setup::get_instance();