feat(admin): rebuild page display settings
This commit is contained in:
@@ -152,7 +152,7 @@ abstract class ControllerBase {
|
||||
return $this->error('无效的设置载荷');
|
||||
}
|
||||
|
||||
$sanitized = $this->sanitize_fields($payload, $provider->get_allowed_fields($domain, $section));
|
||||
$sanitized = $provider->sanitize_payload($domain, $payload, $section);
|
||||
$validation = $provider->validate($domain, $sanitized, $section);
|
||||
if (!$validation['valid']) {
|
||||
return $this->error(implode('; ', $validation['errors']), 422);
|
||||
|
||||
+61
-162
@@ -10,7 +10,6 @@ defined('ABSPATH') || exit;
|
||||
class GlobalController extends ControllerBase {
|
||||
use GlobalControllerPart1;
|
||||
use GlobalControllerPart2;
|
||||
use HomepageSettingsRest;
|
||||
|
||||
|
||||
private static $instance = null;
|
||||
@@ -36,13 +35,6 @@ class GlobalController extends ControllerBase {
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
|
||||
// Footer
|
||||
register_rest_route(self::API_NAMESPACE, '/settings/footer', [
|
||||
'methods' => ['GET', 'POST'],
|
||||
'callback' => [$this, 'rest_handle_footer_settings'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
|
||||
// Footer Tabbar
|
||||
register_rest_route(self::API_NAMESPACE, '/settings/footer-tabbar', [
|
||||
'methods' => ['GET', 'POST'],
|
||||
@@ -91,13 +83,6 @@ class GlobalController extends ControllerBase {
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
|
||||
// Homepage settings
|
||||
register_rest_route(self::API_NAMESPACE, '/settings/homepage', [
|
||||
'methods' => ['GET', 'POST'],
|
||||
'callback' => [$this, 'rest_handle_homepage_settings'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
|
||||
// Category recommend
|
||||
register_rest_route(self::API_NAMESPACE, '/settings/category-recommend', [
|
||||
'methods' => ['GET', 'POST'],
|
||||
@@ -144,14 +129,11 @@ class GlobalController extends ControllerBase {
|
||||
'args' => [
|
||||
'archive_type' => ['type' => 'string', 'default' => 'category', 'sanitize_callback' => 'sanitize_key'],
|
||||
'term_id' => ['type' => 'integer', 'default' => 0, 'sanitize_callback' => 'absint'],
|
||||
'filter_cat' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'filter_tag' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'sort' => ['type' => 'string', 'default' => 'date', 'sanitize_callback' => 'sanitize_key'],
|
||||
'order' => ['type' => 'string', 'default' => 'DESC', 'sanitize_callback' => 'sanitize_key'],
|
||||
'page' => ['type' => 'integer', 'default' => 1, 'sanitize_callback' => 'absint'],
|
||||
'per_page' => ['type' => 'integer', 'default' => 10, 'sanitize_callback' => 'absint'],
|
||||
'custom_filters' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
],
|
||||
'filter_cat' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'filter_tag' => ['type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field'],
|
||||
'sort' => ['type' => 'string', 'default' => 'date', 'sanitize_callback' => 'sanitize_key'],
|
||||
'page' => ['type' => 'integer', 'default' => 1, 'sanitize_callback' => 'absint'],
|
||||
],
|
||||
]);
|
||||
|
||||
// Homepage categories (for admin tab editor)
|
||||
@@ -307,9 +289,62 @@ class GlobalController extends ControllerBase {
|
||||
]);
|
||||
}
|
||||
|
||||
public function rest_archive_posts(\WP_REST_Request $request): \WP_REST_Response {
|
||||
return $this->rest_homepage_tab_posts($request);
|
||||
}
|
||||
public function rest_archive_posts(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$archive_type = sanitize_key((string) ($request->get_param('archive_type') ?: 'category'));
|
||||
$archive_type = in_array($archive_type, ['category', 'tag'], true) ? $archive_type : 'category';
|
||||
$term_id = absint($request->get_param('term_id'));
|
||||
$page = max(1, absint($request->get_param('page')));
|
||||
$page_settings = $archive_type === 'tag'
|
||||
? \AKSRT\Settings\Settings::get_tag_page_settings()
|
||||
: \AKSRT\Settings\Settings::get_category_page_settings();
|
||||
$per_page = min(50, max(1, (int) ($page_settings['per_page'] ?? get_option('posts_per_page', 10))));
|
||||
$sort = sanitize_key((string) ($request->get_param('sort') ?: 'date'));
|
||||
$allowed_sorts = \AKSRT\Settings\Settings::homepage_allowed_sorts();
|
||||
if (!in_array($sort, $allowed_sorts, true)) {
|
||||
$sort = 'date';
|
||||
}
|
||||
|
||||
$args = [
|
||||
'post_type' => 'post',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => $per_page,
|
||||
'paged' => $page,
|
||||
'ignore_sticky_posts' => true,
|
||||
];
|
||||
$tax_query = [];
|
||||
if ($term_id > 0) {
|
||||
$tax_query[] = [
|
||||
'taxonomy' => $archive_type === 'tag' ? 'post_tag' : 'category',
|
||||
'field' => 'term_id',
|
||||
'terms' => [$term_id],
|
||||
];
|
||||
}
|
||||
$filter_cat = absint($request->get_param('filter_cat'));
|
||||
$filter_tag = absint($request->get_param('filter_tag'));
|
||||
if ($filter_cat > 0) {
|
||||
$tax_query[] = ['taxonomy' => 'category', 'field' => 'term_id', 'terms' => [$filter_cat]];
|
||||
}
|
||||
if ($filter_tag > 0) {
|
||||
$tax_query[] = ['taxonomy' => 'post_tag', 'field' => 'term_id', 'terms' => [$filter_tag]];
|
||||
}
|
||||
if ($tax_query !== []) {
|
||||
$args['tax_query'] = $tax_query;
|
||||
}
|
||||
if (function_exists('aksrt_homepage_apply_named_sort_args')) {
|
||||
$args = aksrt_homepage_apply_named_sort_args($args, $sort);
|
||||
}
|
||||
|
||||
$query = new \WP_Query($args);
|
||||
$render_config = aksrt_resolve_post_list_render_config(['context' => $archive_type]);
|
||||
$render_config['skip_wrapper'] = true;
|
||||
$html = aksrt_render_post_list($render_config, $query, false);
|
||||
|
||||
return $this->success([
|
||||
'html' => $html,
|
||||
'total' => (int) $query->found_posts,
|
||||
'has_more' => $page < (int) $query->max_num_pages,
|
||||
]);
|
||||
}
|
||||
|
||||
public function rest_homepage_categories(): \WP_REST_Response {
|
||||
$data = [];
|
||||
@@ -386,142 +421,6 @@ class GlobalController extends ControllerBase {
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('Order saved.', 'aksrt')]);
|
||||
}
|
||||
|
||||
public function rest_handle_footer_settings(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if ($request->get_method() === 'GET') {
|
||||
$footer = get_option('aksrt_footer_settings', []);
|
||||
$defaults = [
|
||||
'contacts' => [],
|
||||
'quick_links' => [],
|
||||
'more_links' => [],
|
||||
'hot_categories' => 5,
|
||||
'copyright_text' => '',
|
||||
'layout_template' => 'template_1',
|
||||
'footer_t1_m_s' => ['pc', 'mobile'],
|
||||
'footer_t1_img' => '',
|
||||
'footer_t1_img_dark' => '',
|
||||
'footer_t1_t' => '',
|
||||
'fcode_t1_code' => '',
|
||||
'footer_contact_m_s' => ['pc', 'mobile'],
|
||||
'footer_contact_wechat_img' => '',
|
||||
'footer_contact_qq' => '',
|
||||
'footer_contact_weibo' => '',
|
||||
'footer_contact_email' => '',
|
||||
'footer_contact_more' => '',
|
||||
'fcode_t2_code_1' => '',
|
||||
'fcode_t2_code_2' => '',
|
||||
'footer_mini_img_m_s' => ['pc', 'mobile'],
|
||||
'footer_mini_img' => [],
|
||||
'fcode_customize_code' => '',
|
||||
];
|
||||
$data = array_merge($defaults, $footer);
|
||||
return rest_ensure_response(['code' => 'success', 'data' => $data]);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$footer = [
|
||||
'contacts' => isset($params['contacts']) ? $this->sanitize_contacts($params['contacts']) : [],
|
||||
'quick_links' => isset($params['quick_links']) ? $this->sanitize_link_list($params['quick_links']) : [],
|
||||
'more_links' => isset($params['more_links']) ? $this->sanitize_link_list($params['more_links']) : [],
|
||||
'hot_categories' => (int) ($params['hot_categories'] ?? 5),
|
||||
'copyright_text' => sanitize_textarea_field($params['copyright_text'] ?? ''),
|
||||
'layout_template' => 'template_1',
|
||||
'footer_t1_m_s' => $this->sanitize_device_visibility($params['footer_t1_m_s'] ?? ['pc', 'mobile']),
|
||||
'footer_t1_img' => esc_url_raw($params['footer_t1_img'] ?? ''),
|
||||
'footer_t1_img_dark' => esc_url_raw($params['footer_t1_img_dark'] ?? ''),
|
||||
'footer_t1_t' => sanitize_text_field($params['footer_t1_t'] ?? ''),
|
||||
'fcode_t1_code' => wp_kses_post($params['fcode_t1_code'] ?? ''),
|
||||
'footer_contact_m_s' => $this->sanitize_device_visibility($params['footer_contact_m_s'] ?? ['pc', 'mobile']),
|
||||
'footer_contact_wechat_img' => esc_url_raw($params['footer_contact_wechat_img'] ?? ''),
|
||||
'footer_contact_qq' => sanitize_text_field($params['footer_contact_qq'] ?? ''),
|
||||
'footer_contact_weibo' => sanitize_text_field($params['footer_contact_weibo'] ?? ''),
|
||||
'footer_contact_email' => sanitize_email($params['footer_contact_email'] ?? ''),
|
||||
'footer_contact_more' => isset($params['footer_contact_more']) && is_array($params['footer_contact_more'])
|
||||
? $this->sanitize_contact_more($params['footer_contact_more']) : [],
|
||||
'fcode_t2_code_1' => wp_kses_post($params['fcode_t2_code_1'] ?? ''),
|
||||
'fcode_t2_code_2' => wp_kses_post($params['fcode_t2_code_2'] ?? ''),
|
||||
'footer_mini_img_m_s' => $this->sanitize_device_visibility($params['footer_mini_img_m_s'] ?? ['pc', 'mobile']),
|
||||
'footer_mini_img' => isset($params['footer_mini_img']) && is_array($params['footer_mini_img'])
|
||||
? $this->sanitize_mini_img_group($params['footer_mini_img']) : [],
|
||||
'fcode_customize_code' => wp_kses_post($params['fcode_customize_code'] ?? ''),
|
||||
];
|
||||
update_option('aksrt_footer_settings', $footer);
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('Footer settings saved.', 'aksrt')]);
|
||||
}
|
||||
|
||||
private function sanitize_contacts(array $contacts): array {
|
||||
$allowed_icons = ['github', 'x', 'twitter', 'email', 'wechat', 'qq', 'weibo',
|
||||
'telegram', 'discord', 'bilibili', 'linkedin', 'facebook',
|
||||
'youtube', 'instagram', 'rss', 'web', 'globe'];
|
||||
$clean = [];
|
||||
foreach ($contacts as $item) {
|
||||
$icon = sanitize_text_field($item['icon'] ?? '');
|
||||
if (!in_array($icon, $allowed_icons)) {
|
||||
continue;
|
||||
}
|
||||
$clean[] = [
|
||||
'icon' => $icon,
|
||||
'url' => esc_url_raw($item['url'] ?? ''),
|
||||
'title' => sanitize_text_field($item['title'] ?? ''),
|
||||
'enabled' => !empty($item['enabled']),
|
||||
];
|
||||
}
|
||||
return $clean;
|
||||
}
|
||||
|
||||
private function sanitize_link_list(array $links): array {
|
||||
$clean = [];
|
||||
foreach ($links as $item) {
|
||||
$clean[] = [
|
||||
'id' => isset($item['id']) ? sanitize_text_field($item['id']) : uniqid('link_'),
|
||||
'name' => sanitize_text_field($item['name'] ?? ''),
|
||||
'url' => esc_url_raw($item['url'] ?? '#'),
|
||||
'enabled' => !empty($item['enabled']),
|
||||
'sort_order' => (int) ($item['sort_order'] ?? 0),
|
||||
];
|
||||
}
|
||||
return $clean;
|
||||
}
|
||||
|
||||
private function sanitize_device_visibility($value): array {
|
||||
$arr = is_array($value) ? $value : [$value];
|
||||
$allowed = ['pc', 'mobile'];
|
||||
$clean = array_values(array_intersect(array_map('sanitize_key', $arr), $allowed));
|
||||
return $clean ?: ['pc', 'mobile'];
|
||||
}
|
||||
|
||||
private function sanitize_contact_more(array $items): array {
|
||||
$clean = [];
|
||||
foreach ($items as $item) {
|
||||
if (!is_array($item)) {
|
||||
continue;
|
||||
}
|
||||
$clean[] = [
|
||||
'desc' => sanitize_text_field($item['desc'] ?? ''),
|
||||
'icon' => sanitize_text_field($item['icon'] ?? ''),
|
||||
'link' => esc_url_raw($item['link'] ?? ''),
|
||||
'img' => esc_url_raw($item['img'] ?? ''),
|
||||
];
|
||||
}
|
||||
return $clean;
|
||||
}
|
||||
|
||||
private function sanitize_mini_img_group(array $items): array {
|
||||
$clean = [];
|
||||
foreach ($items as $item) {
|
||||
if (!is_array($item)) {
|
||||
continue;
|
||||
}
|
||||
if (count($clean) >= 4) {
|
||||
break;
|
||||
}
|
||||
$clean[] = [
|
||||
'text' => sanitize_text_field($item['text'] ?? ''),
|
||||
'img' => esc_url_raw($item['img'] ?? ''),
|
||||
];
|
||||
}
|
||||
return $clean;
|
||||
}
|
||||
|
||||
public function rest_handle_footer_tabbar(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if ($request->get_method() === 'GET') {
|
||||
$settings = class_exists('\AKSRT\Settings\Settings')
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait HomepageSettingsRest {
|
||||
|
||||
public function rest_handle_homepage_settings(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$option_key = 'aksrt_settings';
|
||||
|
||||
if ($request->get_method() === 'GET') {
|
||||
return rest_ensure_response([
|
||||
'code' => 'success',
|
||||
'data' => \AKSRT\Settings\HomeCms::get_homepage_fixed_settings(),
|
||||
]);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
if (empty($params)) {
|
||||
$params = $request->get_params();
|
||||
}
|
||||
if (!is_array($params)) {
|
||||
$params = [];
|
||||
}
|
||||
|
||||
$allowed_sorts = \AKSRT\Settings\HomeCms::homepage_allowed_sorts();
|
||||
$settings = [];
|
||||
|
||||
// --- Canonical Zibll main list contract ---
|
||||
if (array_key_exists('home_posts_list_s', $params)) {
|
||||
$settings['home_posts_list_s'] = !empty($params['home_posts_list_s']);
|
||||
}
|
||||
|
||||
if (array_key_exists('home_list1_orderby_s', $params)) {
|
||||
$settings['home_list1_orderby_s'] = !empty($params['home_list1_orderby_s']);
|
||||
}
|
||||
|
||||
if (isset($params['home_list1_orderby_option']) && is_array($params['home_list1_orderby_option'])) {
|
||||
$settings['home_list1_orderby_option'] = $this->sanitize_home_list1_orderby_option(
|
||||
$params['home_list1_orderby_option'],
|
||||
$allowed_sorts
|
||||
);
|
||||
}
|
||||
|
||||
if (array_key_exists('index_list_title', $params)) {
|
||||
$settings['index_list_title'] = sanitize_text_field((string) $params['index_list_title']);
|
||||
}
|
||||
|
||||
if (array_key_exists('home_exclude_posts', $params)) {
|
||||
$settings['home_exclude_posts'] = $this->sanitize_id_list($params['home_exclude_posts']);
|
||||
}
|
||||
|
||||
if (array_key_exists('home_exclude_cats', $params)) {
|
||||
$settings['home_exclude_cats'] = $this->sanitize_id_list($params['home_exclude_cats']);
|
||||
}
|
||||
|
||||
if (array_key_exists('home_lists', $params)) {
|
||||
$settings['home_lists'] = $this->sanitize_home_lists($params['home_lists'], $allowed_sorts);
|
||||
}
|
||||
|
||||
if (array_key_exists('homepage_sidebar_mini_enabled', $params)) {
|
||||
$settings['homepage_sidebar_mini_enabled'] = !empty($params['homepage_sidebar_mini_enabled']);
|
||||
}
|
||||
if (array_key_exists('homepage_sort_ajax_enabled', $params)) {
|
||||
$settings['homepage_sort_ajax_enabled'] = !empty($params['homepage_sort_ajax_enabled']);
|
||||
}
|
||||
if (array_key_exists('homepage_sticky_tabs_enabled', $params)) {
|
||||
$settings['homepage_sticky_tabs_enabled'] = !empty($params['homepage_sticky_tabs_enabled']);
|
||||
}
|
||||
if (array_key_exists('home_list_sort_dropdown', $params)) {
|
||||
$settings['home_list_sort_dropdown'] = !empty($params['home_list_sort_dropdown']);
|
||||
}
|
||||
if (isset($params['homepage_tab_posts_per_page'])) {
|
||||
$settings['homepage_tab_posts_per_page'] = max(1, min(20, (int) $params['homepage_tab_posts_per_page']));
|
||||
}
|
||||
if (isset($params['homepage_tab_limit'])) {
|
||||
$settings['homepage_tab_limit'] = max(1, min(12, (int) $params['homepage_tab_limit']));
|
||||
}
|
||||
if (array_key_exists('homepage_module_banner_enabled', $params)) {
|
||||
$settings['homepage_module_banner_enabled'] = !empty($params['homepage_module_banner_enabled']);
|
||||
}
|
||||
|
||||
$current = get_option($option_key, []);
|
||||
if (!is_array($current)) {
|
||||
$current = [];
|
||||
}
|
||||
|
||||
foreach ($settings as $key => $value) {
|
||||
$current[$key] = $value;
|
||||
}
|
||||
|
||||
// Canonical writes also remove superseded aliases from persisted settings.
|
||||
$superseded = [
|
||||
'home_posts_list_enabled',
|
||||
'home_list_sort_enabled',
|
||||
'home_list_sort_options',
|
||||
'home_list_title',
|
||||
'home_exclude_post_ids',
|
||||
'home_exclude_category_ids',
|
||||
'homepage_tabs',
|
||||
'homepage_default_sort',
|
||||
];
|
||||
foreach ($superseded as $dead_key) {
|
||||
unset($current[$dead_key]);
|
||||
}
|
||||
|
||||
update_option($option_key, $current);
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
}
|
||||
update_option('aksrt_homepage_schema_version', gmdate('Y-m-d'));
|
||||
delete_transient('aksrt_homepage_cached_images');
|
||||
do_action('aksrt_homepage_settings_changed');
|
||||
|
||||
return rest_ensure_response([
|
||||
'code' => 'success',
|
||||
'message' => __('首页设置已保存', 'aksrt'),
|
||||
'data' => \AKSRT\Settings\HomeCms::get_homepage_fixed_settings(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $raw
|
||||
* @return int[]
|
||||
*/
|
||||
private function sanitize_id_list($raw): array {
|
||||
if (is_string($raw)) {
|
||||
$parts = preg_split('/[\s,]+/', $raw) ?: [];
|
||||
$raw = $parts;
|
||||
}
|
||||
if (!is_array($raw)) {
|
||||
return [];
|
||||
}
|
||||
return array_values(array_unique(array_filter(array_map('absint', $raw), static fn($id) => $id > 0)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $option
|
||||
* @param string[] $allowed_sorts
|
||||
* @return array{orderby:string,options:string[]}
|
||||
*/
|
||||
private function sanitize_home_list1_orderby_option(array $option, array $allowed_sorts): array {
|
||||
$orderby = sanitize_key((string) ($option['orderby'] ?? $option['default'] ?? 'modified'));
|
||||
if (!in_array($orderby, $allowed_sorts, true)) {
|
||||
$orderby = 'modified';
|
||||
}
|
||||
|
||||
$options = [];
|
||||
$raw_options = $option['options'] ?? $option['orderby_option'] ?? [];
|
||||
if (!is_array($raw_options)) {
|
||||
$raw_options = [];
|
||||
}
|
||||
foreach ($raw_options as $key) {
|
||||
$key = sanitize_key((string) $key);
|
||||
if (in_array($key, $allowed_sorts, true) && !in_array($key, $options, true)) {
|
||||
$options[] = $key;
|
||||
}
|
||||
}
|
||||
if ($options === []) {
|
||||
$options = ['modified', 'date', 'views', 'likes', 'comments'];
|
||||
}
|
||||
if (!in_array($orderby, $options, true)) {
|
||||
array_unshift($options, $orderby);
|
||||
}
|
||||
|
||||
return [
|
||||
'orderby' => $orderby,
|
||||
'options' => array_values($options),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $raw_tabs
|
||||
* @param string[] $allowed_sorts
|
||||
* @return array<int, array{term_id:int,title:string,orderby:string,enabled:bool}>
|
||||
*/
|
||||
private function sanitize_home_lists($raw_tabs, array $allowed_sorts): array {
|
||||
if (!is_array($raw_tabs)) {
|
||||
return [];
|
||||
}
|
||||
$tabs = [];
|
||||
foreach ($raw_tabs as $tab) {
|
||||
if (!is_array($tab)) {
|
||||
continue;
|
||||
}
|
||||
$term_id = absint($tab['term_id'] ?? 0);
|
||||
if ($term_id <= 0) {
|
||||
continue;
|
||||
}
|
||||
$term = get_term($term_id);
|
||||
if (!$term || is_wp_error($term)) {
|
||||
continue;
|
||||
}
|
||||
$taxonomy = (string) $term->taxonomy;
|
||||
if ($taxonomy !== 'category') {
|
||||
continue;
|
||||
}
|
||||
$orderby = sanitize_key((string) ($tab['orderby'] ?? ''));
|
||||
if ($orderby !== '' && !in_array($orderby, $allowed_sorts, true)) {
|
||||
$orderby = 'date';
|
||||
}
|
||||
$tabs[] = [
|
||||
'term_id' => $term_id,
|
||||
'title' => sanitize_text_field((string) ($tab['title'] ?? '')),
|
||||
'orderby' => $orderby,
|
||||
'enabled' => !array_key_exists('enabled', $tab) || !empty($tab['enabled']),
|
||||
];
|
||||
}
|
||||
return $tabs;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Page/display schema option search.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
@@ -9,169 +11,62 @@ defined('ABSPATH') || exit;
|
||||
|
||||
class PageDisplayController extends ControllerBase {
|
||||
|
||||
private static $instance = null;
|
||||
private $admin;
|
||||
private static $instance = null;
|
||||
|
||||
public static function get_instance(): self {
|
||||
if (null === self::$instance) self::$instance = new self();
|
||||
return self::$instance;
|
||||
}
|
||||
public static function get_instance(): self {
|
||||
if (null === self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function set_admin(Admin $admin): void {
|
||||
$this->admin = $admin;
|
||||
}
|
||||
public function register_routes(): void {
|
||||
register_rest_route(self::API_NAMESPACE, '/settings/schema/page_display/search/(?P<source>posts|categories|tags)', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'search_options'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
'args' => [
|
||||
'query' => ['type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => ''],
|
||||
'include' => ['type' => 'array', 'items' => ['type' => 'integer'], 'default' => []],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function register_routes(): void {
|
||||
// Homepage content-stream banners.
|
||||
register_rest_route(self::API_NAMESPACE, '/banners', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'rest_get_banners'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
register_rest_route(self::API_NAMESPACE, '/banners', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'rest_create_banner'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
register_rest_route(self::API_NAMESPACE, '/banners/(?P<id>[\w-]+)', [
|
||||
'methods' => ['PUT', 'DELETE'],
|
||||
'callback' => [$this, 'rest_handle_banner_item'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
}
|
||||
public function search_options(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$source = sanitize_key((string) $request->get_param('source'));
|
||||
$query = sanitize_text_field((string) $request->get_param('query'));
|
||||
$include = array_values(array_filter(array_map('absint', (array) $request->get_param('include'))));
|
||||
|
||||
private function get_banner_kind_query(): array {
|
||||
return [
|
||||
[
|
||||
'key' => '_aksrt_kind',
|
||||
'value' => 'banner',
|
||||
],
|
||||
];
|
||||
}
|
||||
if ($source === 'posts') {
|
||||
$posts = get_posts([
|
||||
'post_type' => 'post',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => 20,
|
||||
's' => $query,
|
||||
'include' => $include,
|
||||
'orderby' => $include ? 'post__in' : 'relevance',
|
||||
]);
|
||||
$items = array_map(static function ($post): array {
|
||||
return ['label' => get_the_title($post) ?: ('#' . $post->ID), 'value' => (int) $post->ID];
|
||||
}, $posts);
|
||||
return $this->success(['data' => $items]);
|
||||
}
|
||||
|
||||
public function rest_get_banners(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$args = [
|
||||
'post_type' => 'aksrt_banner',
|
||||
'posts_per_page' => 100,
|
||||
'post_status' => 'any',
|
||||
'orderby' => 'menu_order',
|
||||
'order' => 'ASC',
|
||||
'meta_query' => $this->get_banner_kind_query(),
|
||||
];
|
||||
$posts = get_posts($args);
|
||||
$banners = array_map(function ($post) {
|
||||
$layers_json = get_post_meta($post->ID, '_layers', true);
|
||||
return [
|
||||
'id' => $post->ID,
|
||||
'title' => $post->post_title,
|
||||
'subtitle' => get_post_meta($post->ID, '_subtitle', true),
|
||||
'description' => get_post_meta($post->ID, '_description', true),
|
||||
'image_url' => get_post_meta($post->ID, '_image_url', true),
|
||||
'video_url' => get_post_meta($post->ID, '_video_url', true),
|
||||
'link_url' => get_post_meta($post->ID, '_link_url', true),
|
||||
'link_target' => get_post_meta($post->ID, '_link_target', true) ?: '_self',
|
||||
'text_align' => get_post_meta($post->ID, '_text_align', true) ?: 'left',
|
||||
'device_visibility' => get_post_meta($post->ID, '_device_visibility', true) ?: 'all',
|
||||
// Match frontend aksrt_get_banners(): missing _enabled means enabled.
|
||||
'enabled' => get_post_meta($post->ID, '_enabled', true) !== '0',
|
||||
'show_text' => get_post_meta($post->ID, '_show_text', true) !== '0',
|
||||
'show_meta' => get_post_meta($post->ID, '_show_meta', true) === '1',
|
||||
'sort_order' => $post->menu_order,
|
||||
'text_size_pc' => get_post_meta($post->ID, '_text_size_pc', true) ?: '',
|
||||
'text_size_m' => get_post_meta($post->ID, '_text_size_m', true) ?: '',
|
||||
'text_parallax' => get_post_meta($post->ID, '_text_parallax', true) === '1',
|
||||
'layers' => is_string($layers_json) && $layers_json !== '' ? (json_decode($layers_json, true) ?: []) : [],
|
||||
];
|
||||
}, $posts);
|
||||
return rest_ensure_response(['code' => 'success', 'data' => $banners]);
|
||||
}
|
||||
|
||||
public function rest_create_banner(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$params = $this->get_json_params($request);
|
||||
$title = sanitize_text_field($params['title'] ?? '');
|
||||
$post_id = wp_insert_post([
|
||||
// Title is optional in admin UI; keep a non-empty WP title for list readability.
|
||||
'post_title' => '' !== $title ? $title : 'Banner Slide',
|
||||
'post_type' => 'aksrt_banner',
|
||||
'post_status' => 'publish',
|
||||
'menu_order' => (int) ($params['sort_order'] ?? 0),
|
||||
]);
|
||||
|
||||
if (is_wp_error($post_id)) {
|
||||
return new \WP_REST_Response(['code' => 'error', 'message' => $post_id->get_error_message()], 400);
|
||||
}
|
||||
|
||||
update_post_meta($post_id, '_image_url', esc_url_raw($params['image_url'] ?? ''));
|
||||
update_post_meta($post_id, '_link_url', esc_url_raw($params['link_url'] ?? ''));
|
||||
update_post_meta($post_id, '_link_target', in_array($params['link_target'] ?? '', ['_self', '_blank'], true) ? $params['link_target'] : '_self');
|
||||
update_post_meta($post_id, '_subtitle', sanitize_text_field($params['subtitle'] ?? ''));
|
||||
update_post_meta($post_id, '_description', sanitize_textarea_field($params['description'] ?? ''));
|
||||
update_post_meta($post_id, '_video_url', esc_url_raw($params['video_url'] ?? ''));
|
||||
update_post_meta($post_id, '_text_align', in_array($params['text_align'] ?? '', ['left', 'center', 'right'], true) ? $params['text_align'] : 'left');
|
||||
update_post_meta($post_id, '_device_visibility', in_array($params['device_visibility'] ?? '', ['all', 'pc', 'mobile'], true) ? $params['device_visibility'] : 'all');
|
||||
update_post_meta($post_id, '_enabled', !array_key_exists('enabled', $params) || !empty($params['enabled']) ? '1' : '0');
|
||||
update_post_meta($post_id, '_show_text', !array_key_exists('show_text', $params) || !empty($params['show_text']) ? '1' : '0');
|
||||
update_post_meta($post_id, '_show_meta', !empty($params['show_meta']) ? '1' : '0');
|
||||
update_post_meta($post_id, '_aksrt_kind', 'banner');
|
||||
update_post_meta($post_id, '_text_size_pc', sanitize_text_field($params['text_size_pc'] ?? ''));
|
||||
update_post_meta($post_id, '_text_size_m', sanitize_text_field($params['text_size_m'] ?? ''));
|
||||
update_post_meta($post_id, '_text_parallax', !empty($params['text_parallax']) ? '1' : '0');
|
||||
if (isset($params['layers']) && is_array($params['layers'])) {
|
||||
update_post_meta($post_id, '_layers', wp_json_encode(array_values($params['layers'])));
|
||||
}
|
||||
|
||||
return rest_ensure_response(['code' => 'success', 'data' => ['id' => $post_id], 'message' => __('幻灯片已创建', 'aksrt')]);
|
||||
}
|
||||
|
||||
public function rest_handle_banner_item(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$id = absint($request['id']);
|
||||
if (!$id || get_post_type($id) !== 'aksrt_banner' || 'banner' !== get_post_meta($id, '_aksrt_kind', true)) {
|
||||
return new \WP_REST_Response(['code' => 'not_found', 'message' => __('幻灯片不存在', 'aksrt')], 404);
|
||||
}
|
||||
|
||||
if ($request->get_method() === 'DELETE') {
|
||||
wp_delete_post($id, true);
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('幻灯片已删除', 'aksrt')]);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
// Optional title: empty is allowed (UI shows “未命名”); do not 422.
|
||||
$title = sanitize_text_field($params['title'] ?? '');
|
||||
|
||||
wp_update_post([
|
||||
'ID' => $id,
|
||||
'post_title' => $title,
|
||||
]);
|
||||
|
||||
update_post_meta($id, '_aksrt_kind', 'banner');
|
||||
update_post_meta($id, '_subtitle', sanitize_text_field($params['subtitle'] ?? ''));
|
||||
update_post_meta($id, '_description', sanitize_textarea_field($params['description'] ?? ''));
|
||||
update_post_meta($id, '_image_url', esc_url_raw($params['image_url'] ?? ''));
|
||||
update_post_meta($id, '_video_url', esc_url_raw($params['video_url'] ?? ''));
|
||||
update_post_meta($id, '_link_url', esc_url_raw($params['link_url'] ?? ''));
|
||||
update_post_meta($id, '_link_target', in_array($params['link_target'] ?? '', ['_blank', '_self'], true) ? $params['link_target'] : '_self');
|
||||
update_post_meta($id, '_text_align', in_array($params['text_align'] ?? '', ['left', 'center', 'right'], true) ? $params['text_align'] : 'left');
|
||||
update_post_meta($id, '_device_visibility', in_array($params['device_visibility'] ?? '', ['all', 'pc', 'mobile'], true) ? $params['device_visibility'] : 'all');
|
||||
update_post_meta($id, '_enabled', !array_key_exists('enabled', $params) || !empty($params['enabled']) ? '1' : '0');
|
||||
update_post_meta($id, '_show_text', !array_key_exists('show_text', $params) || !empty($params['show_text']) ? '1' : '0');
|
||||
update_post_meta($id, '_show_meta', !empty($params['show_meta']) ? '1' : '0');
|
||||
update_post_meta($id, '_text_size_pc', sanitize_text_field($params['text_size_pc'] ?? ''));
|
||||
update_post_meta($id, '_text_size_m', sanitize_text_field($params['text_size_m'] ?? ''));
|
||||
update_post_meta($id, '_text_parallax', !empty($params['text_parallax']) ? '1' : '0');
|
||||
|
||||
if (isset($params['layers']) && is_array($params['layers'])) {
|
||||
update_post_meta($id, '_layers', wp_json_encode(array_values($params['layers'])));
|
||||
} else {
|
||||
delete_post_meta($id, '_layers');
|
||||
}
|
||||
|
||||
if (isset($params['sort_order'])) {
|
||||
wp_update_post([
|
||||
'ID' => $id,
|
||||
'menu_order' => (int) $params['sort_order'],
|
||||
]);
|
||||
}
|
||||
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('幻灯片已更新', 'aksrt')]);
|
||||
}
|
||||
$taxonomy = $source === 'categories' ? 'category' : 'post_tag';
|
||||
$terms = get_terms([
|
||||
'taxonomy' => $taxonomy,
|
||||
'hide_empty' => false,
|
||||
'number' => 20,
|
||||
'search' => $query,
|
||||
'include' => $include,
|
||||
'orderby' => $include ? 'include' : 'name',
|
||||
]);
|
||||
if (is_wp_error($terms)) {
|
||||
return $this->error($terms->get_error_message(), 400);
|
||||
}
|
||||
$items = array_map(static function ($term): array {
|
||||
return ['label' => $term->name, 'value' => (int) $term->term_id];
|
||||
}, $terms);
|
||||
return $this->success(['data' => $items]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,25 +331,16 @@ class GlobalSchema {
|
||||
['key' => '404_search_s', 'label' => '在 404 页面显示搜索功能', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'search_posts', 'label' => '搜索框热门文章', 'type' => 'switch', 'default' => true, 'help' => '在搜索面板中显示热门文章列表'],
|
||||
['key' => 'search_no_page', 'label' => '排除页面', 'type' => 'switch', 'default' => true, 'help' => '搜索结果中排除页面(仅显示文章)'],
|
||||
['key' => 'search_exclude_cats', 'label' => '排除分类', 'type' => 'taxonomy_select', 'taxonomy' => ['category', 'shop_cat'], 'multiple' => true, 'default' => [], 'help' => '搜索结果中排除的文章或商品分类'],
|
||||
['key' => 'search_exclude_cats', 'label' => '排除分类', 'type' => 'taxonomy_select', 'taxonomy' => 'category', 'multiple' => true, 'default' => [], 'help' => '搜索结果中排除的文章分类'],
|
||||
['key' => 'search_custom_key_s', 'label' => '自定义匹配范围', 'type' => 'switch', 'default' => true, 'help' => '启用后可配置搜索匹配字段范围(标题/内容/分类/标签等)'],
|
||||
['key' => 'search_custom_key', 'label' => '匹配字段', 'type' => 'checkbox', 'default' => ['title', 'content', 'excerpt', 'post_tag', 'forum_topic', 'shop_cat', 'shop_discount'], 'options' => [
|
||||
['key' => 'search_custom_key', 'label' => '匹配字段', 'type' => 'checkbox', 'default' => ['title', 'content', 'excerpt', 'category', 'post_tag'], 'options' => [
|
||||
['label' => '标题', 'value' => 'title'], ['label' => '内容', 'value' => 'content'], ['label' => '简介', 'value' => 'excerpt'],
|
||||
['label' => '分类', 'value' => 'category'], ['label' => '标签', 'value' => 'post_tag'], ['label' => '专题', 'value' => 'topics'],
|
||||
['label' => '分类', 'value' => 'category'], ['label' => '标签', 'value' => 'post_tag'],
|
||||
['label' => '评论', 'value' => 'coment'], ['label' => '作者昵称', 'value' => 'user'],
|
||||
['label' => '[帖子]话题', 'value' => 'forum_topic'], ['label' => '[帖子]标签', 'value' => 'forum_tag'],
|
||||
['label' => '[商品]分类', 'value' => 'shop_cat'], ['label' => '[商品]特色标签', 'value' => 'shop_tag'], ['label' => '[商品]优惠活动', 'value' => 'shop_discount'],
|
||||
]],
|
||||
['key' => 'search_facets', 'label' => '搜索筛选', 'type' => 'fieldset', 'sanitize_type' => 'array', 'default' => [], 'dependsOn' => ['key' => 'search_ms_s', 'value' => false], 'help' => '搜索结果中显示的筛选列表;启用 Meilisearch 后由搜索引擎生成关联筛选。', 'fields' => [
|
||||
['key' => 'category', 'label' => '[文章] 分类', 'type' => 'taxonomy_select', 'taxonomy' => 'category', 'multiple' => true, 'default' => []],
|
||||
['key' => 'topics', 'label' => '[文章] 专题', 'type' => 'taxonomy_select', 'taxonomy' => 'topics', 'multiple' => true, 'default' => []],
|
||||
['key' => 'post_tag', 'label' => '[文章] 标签', 'type' => 'taxonomy_select', 'taxonomy' => 'post_tag', 'multiple' => true, 'default' => []],
|
||||
['key' => 'plate_id', 'label' => '[论坛帖子] 板块 ID', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'forum_topic', 'label' => '[论坛帖子] 话题', 'type' => 'taxonomy_select', 'taxonomy' => 'forum_topic', 'multiple' => true, 'default' => []],
|
||||
['key' => 'forum_tag', 'label' => '[论坛帖子] 标签', 'type' => 'taxonomy_select', 'taxonomy' => 'forum_tag', 'multiple' => true, 'default' => []],
|
||||
['key' => 'shop_cat', 'label' => '[商品] 分类', 'type' => 'taxonomy_select', 'taxonomy' => 'shop_cat', 'multiple' => true, 'default' => []],
|
||||
['key' => 'shop_discount', 'label' => '[商品] 优惠活动', 'type' => 'taxonomy_select', 'taxonomy' => 'shop_discount', 'multiple' => true, 'default' => []],
|
||||
['key' => 'shop_tag', 'label' => '[商品] 特色标签', 'type' => 'taxonomy_select', 'taxonomy' => 'shop_tag', 'multiple' => true, 'default' => []],
|
||||
]],
|
||||
]],
|
||||
['key' => 'category-search', 'title' => '分类搜索', 'fields' => [
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Zibll V9.0 page/display settings contract.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
@@ -9,478 +11,204 @@ defined('ABSPATH') || exit;
|
||||
|
||||
class SchemaPageDisplay {
|
||||
|
||||
public static function build(): array {
|
||||
return [
|
||||
'domain' => 'page_display',
|
||||
'title' => '页面&显示',
|
||||
'sections' => [
|
||||
'header-nav' => [
|
||||
'key' => 'header-nav',
|
||||
'title' => '顶部导航栏',
|
||||
'version' => '1.1.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'groups' => [
|
||||
['key' => 'layout', 'title' => '导航栏布局', 'fields' => [
|
||||
['key' => 'nav_fixed', 'label' => '导航栏一直固定在顶部', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'nav_scroll_down_hide', 'label' => '下滚动隐藏,上滚动显示', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'nav_fixed', 'value' => true]],
|
||||
]],
|
||||
['key' => 'layout-type', 'title' => '布局类型', 'fields' => [
|
||||
['key' => 'header_layout', 'label' => '桌面端布局', 'type' => 'image_select', 'default' => '1', 'options' => self::header_layout_options()],
|
||||
['key' => 'mobile_header_layout', 'label' => '移动端布局', 'type' => 'image_select', 'default' => 'center', 'options' => self::mobile_header_layout_options()],
|
||||
['key' => 'mobile_navbar_align', 'label' => '移动端菜单方向', 'type' => 'radio', 'default' => 'left', 'options' => [
|
||||
['label' => '顶部(关闭手势)', 'value' => 'top'],
|
||||
['label' => '左侧滑出', 'value' => 'left'],
|
||||
['label' => '右侧滑出', 'value' => 'right'],
|
||||
]],
|
||||
]],
|
||||
['key' => 'components', 'title' => '导航组件', 'fields' => [
|
||||
['key' => 'nav_pay_vip', 'label' => '导航栏开通会员按钮', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'nav_user_pay_vip', 'label' => '用户框开通会员按钮', 'type' => 'switch', 'default' => true],
|
||||
]],
|
||||
['key' => 'newposts', 'title' => '发布按钮', 'fields' => [
|
||||
['key' => 'nav_newposts', 'label' => '启用发布按钮', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'nav_newposts_text', 'label' => '按钮文案', 'type' => 'input', 'default' => '<i class="fa fas fa-fw fa-pen"></i>发布', 'dependsOn' => ['key' => 'nav_newposts', 'value' => true]],
|
||||
['key' => 'nav_newposts_radius', 'label' => '按钮圆角(胶囊)', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'nav_newposts', 'value' => true]],
|
||||
['key' => 'nav_newposts_class', 'label' => '按钮颜色', 'type' => 'select', 'default' => 'jb-blue', 'dependsOn' => ['key' => 'nav_newposts', 'value' => true], 'options' => [
|
||||
['label' => '蓝色', 'value' => 'jb-blue'],
|
||||
['label' => '红色', 'value' => 'jb-red'],
|
||||
['label' => '绿色', 'value' => 'jb-green'],
|
||||
['label' => '黄色', 'value' => 'jb-yellow'],
|
||||
['label' => '紫色', 'value' => 'jb-purple'],
|
||||
['label' => '粉色', 'value' => 'jb-pink'],
|
||||
]],
|
||||
['key' => 'nav_newposts_btns', 'label' => '需显示的按钮', 'type' => 'checkbox', 'default' => ['post', 'bbs_topic', 'bbs_plate', 'bbs_posts'], 'dependsOn' => ['key' => 'nav_newposts', 'value' => true], 'options' => [
|
||||
['label' => '文章', 'value' => 'post'],
|
||||
['label' => '论坛话题', 'value' => 'bbs_topic'],
|
||||
['label' => '论坛版块', 'value' => 'bbs_plate'],
|
||||
['label' => '论坛帖子', 'value' => 'bbs_posts'],
|
||||
]],
|
||||
['key' => 'nav_user_newposts_btn', 'label' => '用户卡发布类型', 'type' => 'select', 'default' => 'post', 'options' => [
|
||||
['label' => '文章', 'value' => 'post'],
|
||||
]],
|
||||
['key' => 'nav_user_newposts_btn_text', 'label' => '用户卡发布文案', 'type' => 'input', 'default' => '发布文章'],
|
||||
]],
|
||||
['key' => 'color', 'title' => '颜色设置', 'fields' => [
|
||||
['key' => 'header_theme_custom', 'label' => '自定义导航栏颜色', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'header_theme_bg_custom', 'label' => '自定义背景色', 'type' => 'color', 'default' => ''],
|
||||
['key' => 'header_theme_bg', 'label' => '背景颜色(备选)', 'type' => 'color', 'default' => ''],
|
||||
['key' => 'header_theme_color_custom', 'label' => '自定义文字颜色', 'type' => 'color', 'default' => ''],
|
||||
]],
|
||||
],
|
||||
],
|
||||
'header-components' => [
|
||||
'key' => 'header-components',
|
||||
'title' => '顶部多功能组件',
|
||||
'description' => 'Zibll 顶部幻灯、搜索覆盖层与图标卡片的原始字段契约。',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'groups' => [
|
||||
['key' => 'visibility', 'title' => '显示规则', 'fields' => [
|
||||
['key' => 'header_slider_show', 'label' => '启用顶部多功能组件的页面', 'type' => 'checkbox', 'default' => [], 'options' => [
|
||||
['label' => '首页', 'value' => 'home'],
|
||||
['label' => '分类页', 'value' => 'category'],
|
||||
['label' => '标签页', 'value' => 'tag'],
|
||||
['label' => '用户页', 'value' => 'author'],
|
||||
['label' => '文章页', 'value' => 'single'],
|
||||
['label' => '搜索页', 'value' => 'search'],
|
||||
['label' => '其它页面', 'value' => 'page'],
|
||||
['label' => '专题页', 'value' => 'topics'],
|
||||
['label' => '论坛首页', 'value' => 'forum_home'],
|
||||
['label' => '论坛版块', 'value' => 'forum_plate'],
|
||||
['label' => '论坛帖子', 'value' => 'forum_post'],
|
||||
]],
|
||||
['key' => 'header_slider_show_type', 'label' => '显示规则', 'type' => 'radio', 'default' => '', 'dependsOn' => ['key' => 'header_slider_show', 'not' => []], 'options' => [
|
||||
['label' => '全部', 'value' => ''],
|
||||
['label' => '仅电脑', 'value' => 'only_pc'],
|
||||
['label' => '仅移动', 'value' => 'only_sm'],
|
||||
]],
|
||||
['key' => 'header_slider_nav_color', 'label' => '导航栏文字颜色', 'type' => 'color', 'default' => '#ffffff', 'dependsOn' => ['key' => 'header_slider_show', 'not' => []]],
|
||||
]],
|
||||
['key' => 'slides', 'title' => '幻灯片', 'fields' => [
|
||||
['key' => 'header_slider', 'label' => '幻灯片项目', 'type' => 'repeater', 'sanitize_type' => 'array', 'default' => self::header_slider_defaults(), 'addText' => '添加幻灯片', 'fields' => [
|
||||
['key' => 'background', 'label' => '背景图片', 'type' => 'media', 'default' => ''],
|
||||
['key' => 'background_video', 'label' => '视频背景', 'type' => 'media', 'default' => '', 'help' => 'PC 端视频优先;移动端仅显示背景图片。'],
|
||||
['key' => 'video', 'label' => '视频内容', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'hide', 'label' => '隐藏设备', 'type' => 'radio', 'default' => '', 'options' => [
|
||||
['label' => '不隐藏', 'value' => ''],
|
||||
['label' => 'PC 端不显示', 'value' => 'pc'],
|
||||
['label' => '移动端不显示', 'value' => 'm'],
|
||||
]],
|
||||
['key' => 'link', 'label' => '跳转链接', 'type' => 'json', 'sanitize_type' => 'array', 'default' => ['url' => '', 'target' => '', 'text' => '']],
|
||||
['key' => 'image_layer', 'label' => '叠加图层', 'type' => 'json', 'sanitize_type' => 'array', 'default' => [], 'help' => '每层支持 image、free_size、align、parallax、parallax_scale、parallax_opacity。'],
|
||||
['key' => 'text', 'label' => '叠加文案', 'type' => 'json', 'sanitize_type' => 'array', 'default' => ['title' => '', 'desc' => '', 'text_align' => 'left-bottom', 'text_size_pc' => 30, 'text_size_m' => 20, 'parallax' => 40]],
|
||||
]],
|
||||
['key' => 'header_slider_option', 'label' => '幻灯片参数', 'type' => 'json', 'sanitize_type' => 'array', 'default' => self::header_slider_option_defaults()],
|
||||
]],
|
||||
['key' => 'search', 'title' => '搜索组件', 'fields' => [
|
||||
['key' => 'header_slider_search_s', 'label' => '显示搜索组件', 'type' => 'checkbox', 'default' => ['pc_s', 'm_s'], 'options' => self::device_options()],
|
||||
['key' => 'header_slider_search_option', 'label' => '搜索组件参数', 'type' => 'json', 'sanitize_type' => 'array', 'default' => self::header_slider_search_defaults()],
|
||||
]],
|
||||
['key' => 'cards', 'title' => '图标卡片组件', 'fields' => [
|
||||
['key' => 'header_slider_card_s', 'label' => '显示图标卡片', 'type' => 'checkbox', 'default' => ['pc_s', 'm_s'], 'options' => self::device_options()],
|
||||
['key' => 'header_slider_card_option', 'label' => '图标卡片参数', 'type' => 'json', 'sanitize_type' => 'array', 'default' => self::header_slider_card_defaults()],
|
||||
['key' => 'header_slider_filter_blur', 'label' => '组件高斯模糊特效', 'type' => 'checkbox', 'default' => ['pc_s', 'm_s'], 'options' => self::device_options()],
|
||||
]],
|
||||
],
|
||||
],
|
||||
'category' => [
|
||||
'key' => 'category',
|
||||
'title' => '分类页面',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'groups' => [
|
||||
['key' => 'header', 'title' => '分类头图', 'fields' => [
|
||||
['key' => 'category_header_style', 'label' => '头部风格', 'type' => 'radio', 'default' => 'cover', 'options' => [
|
||||
['label' => '封面图', 'value' => 'cover'],
|
||||
['label' => '简洁标题', 'value' => 'simple'],
|
||||
['label' => '隐藏', 'value' => 'none'],
|
||||
]],
|
||||
['key' => 'category_cover_enabled', 'label' => '显示封面图', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'category_header_style', 'value' => 'cover']],
|
||||
['key' => 'category_desc_enabled', 'label' => '显示分类简介', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'category_default_cover', 'label' => '默认封面图', 'type' => 'media', 'default' => ''],
|
||||
]],
|
||||
['key' => 'list', 'title' => '列表设置', 'fields' => [
|
||||
['key' => 'category_list_mode', 'label' => '列表模式', 'type' => 'radio', 'default' => 'grid', 'options' => [
|
||||
['label' => '网格', 'value' => 'grid'],
|
||||
['label' => '列表', 'value' => 'list'],
|
||||
['label' => '紧凑', 'value' => 'compact'],
|
||||
]],
|
||||
['key' => 'category_per_page', 'label' => '每页文章数', 'type' => 'number', 'default' => 12, 'min' => 4, 'max' => 48],
|
||||
['key' => 'category_filter_bar', 'label' => '筛选条', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'category_sidebar', 'label' => '侧边栏', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'category_post_count', 'label' => '显示文章数', 'type' => 'switch', 'default' => true],
|
||||
]],
|
||||
['key' => 'orderby', 'title' => '排序设置', 'fields' => [
|
||||
['key' => 'category_orderby', 'label' => '默认排序', 'type' => 'select', 'default' => 'modified', 'options' => [
|
||||
['label' => '最近更新', 'value' => 'modified'],
|
||||
['label' => '发布时间', 'value' => 'date'],
|
||||
['label' => '浏览数', 'value' => 'views'],
|
||||
['label' => '评论数', 'value' => 'comments'],
|
||||
['label' => '点赞数', 'value' => 'likes'],
|
||||
]],
|
||||
]],
|
||||
],
|
||||
],
|
||||
'tag' => [
|
||||
'key' => 'tag',
|
||||
'title' => '标签页面',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'groups' => [
|
||||
['key' => 'header', 'title' => '标签头图', 'fields' => [
|
||||
['key' => 'tag_header_style', 'label' => '头部风格', 'type' => 'radio', 'default' => 'simple', 'options' => [
|
||||
['label' => '简洁标题', 'value' => 'simple'],
|
||||
['label' => '封面图', 'value' => 'cover'],
|
||||
['label' => '隐藏', 'value' => 'none'],
|
||||
]],
|
||||
['key' => 'tag_default_cover', 'label' => '默认封面图', 'type' => 'media', 'default' => ''],
|
||||
]],
|
||||
['key' => 'list', 'title' => '列表设置', 'fields' => [
|
||||
['key' => 'tag_list_mode', 'label' => '列表模式', 'type' => 'radio', 'default' => 'grid', 'options' => [
|
||||
['label' => '网格', 'value' => 'grid'],
|
||||
['label' => '列表', 'value' => 'list'],
|
||||
]],
|
||||
['key' => 'tag_per_page', 'label' => '每页文章数', 'type' => 'number', 'default' => 12, 'min' => 4, 'max' => 48],
|
||||
['key' => 'tag_sidebar', 'label' => '侧边栏', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'tag_post_count', 'label' => '显示文章数', 'type' => 'switch', 'default' => true],
|
||||
]],
|
||||
['key' => 'orderby', 'title' => '排序设置', 'fields' => [
|
||||
['key' => 'tag_orderby', 'label' => '默认排序', 'type' => 'select', 'default' => 'count', 'options' => [
|
||||
['label' => '文章数', 'value' => 'count'],
|
||||
['label' => '名称', 'value' => 'name'],
|
||||
['label' => '最近更新', 'value' => 'modified'],
|
||||
]],
|
||||
]],
|
||||
],
|
||||
],
|
||||
'user-home' => [
|
||||
'key' => 'user-home',
|
||||
'title' => '用户主页',
|
||||
'version' => '1.1.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'groups' => [
|
||||
['key' => 'layout', 'title' => '主页布局', 'fields' => [
|
||||
['key' => 'user_home_route_identifier', 'label' => '主页 URL 标识', 'type' => 'select', 'default' => 'nicename', 'options' => [['label' => '用户名 URL slug', 'value' => 'nicename'], ['label' => '数字用户 ID', 'value' => 'uid']]],
|
||||
['key' => 'user_home_cover', 'label' => '封面区域', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'user_home_sidebar', 'label' => '侧边栏(已弃用)', 'type' => 'switch', 'default' => false, 'help' => '公开主页对齐 Zibll 作者页为通栏布局;交易/快捷入口仅在用户中心展示。此开关保留兼容,运行时强制关闭。'],
|
||||
['key' => 'user_home_follow', 'label' => '关注按钮', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'user_home_posts_per_page', 'label' => '每页文章数', 'type' => 'number', 'default' => 10, 'min' => 5, 'max' => 50],
|
||||
['key' => 'user_home_paging_ajax', 'label' => 'AJAX 翻页', 'type' => 'switch', 'default' => true],
|
||||
]],
|
||||
['key' => 'tabs', 'title' => 'Tab 配置', 'fields' => [
|
||||
['key' => 'user_home_tabs', 'label' => '显示的 Tab', 'type' => 'input', 'default' => 'posts,favorites,comments,follow', 'help' => '逗号分隔;默认对齐 Zibll:posts,favorites,comments,follow(粉丝)。可选 credits'],
|
||||
['key' => 'user_home_tabs_order', 'label' => 'Tab 显示顺序', 'type' => 'input', 'default' => 'posts,favorites,comments,follow'],
|
||||
['key' => 'user_home_tabs_pc_hide', 'label' => 'PC 端隐藏', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'user_home_tabs_mobile_hide', 'label' => '移动端隐藏', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'author_main_tab_swiper', 'label' => '移动端滑动切换', 'type' => 'switch', 'default' => false],
|
||||
]],
|
||||
['key' => 'display', 'title' => '展示设置', 'fields' => [
|
||||
['key' => 'user_home_avatar', 'label' => '显示头像', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'user_home_name', 'label' => '显示用户名', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'user_home_desc', 'label' => '显示个人简介', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'user_home_stats', 'label' => '显示统计数据(已弃用)', 'type' => 'switch', 'default' => false, 'help' => '计数已并入 Tab 角标(对齐 Zibll),头图内不再重复展示文章/评论/收藏。'],
|
||||
['key' => 'user_home_social', 'label' => '显示社交链接', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'user_home_message', 'label' => '私信/消息按钮', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'user_home_private', 'label' => '留言按钮', 'type' => 'switch', 'default' => true],
|
||||
]],
|
||||
],
|
||||
],
|
||||
'other' => [
|
||||
'key' => 'other',
|
||||
'title' => '其它页面',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'groups' => [
|
||||
['key' => 'page-header', 'title' => '统一页头', 'fields' => [
|
||||
['key' => 'page_header_style', 'label' => '页面标题样式', 'type' => 'select', 'default' => '1', 'options' => [
|
||||
['label' => '不显示', 'value' => ''],
|
||||
['label' => '简单', 'value' => '1'],
|
||||
['label' => '卡片', 'value' => '2'],
|
||||
['label' => '封面', 'value' => '3'],
|
||||
]],
|
||||
['key' => 'page_header_cover_img', 'label' => '默认封面图', 'type' => 'media', 'default' => ''],
|
||||
]],
|
||||
['key' => 'page_404', 'title' => '404 页面', 'fields' => [
|
||||
['key' => 'page_404_message', 'label' => '404 提示文字', 'type' => 'input', 'default' => '页面未找到'],
|
||||
['key' => 'page_404_search', 'label' => '显示搜索框', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'page_404_popular', 'label' => '显示热门文章', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'page_404_back', 'label' => '显示返回首页', 'type' => 'switch', 'default' => true],
|
||||
]],
|
||||
['key' => 'search_results', 'title' => '搜索结果页', 'fields' => [
|
||||
['key' => 'page_search_sidebar', 'label' => '搜索结果侧边栏', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'page_search_per_page', 'label' => '每页结果数', 'type' => 'number', 'default' => 12, 'min' => 4, 'max' => 48],
|
||||
]],
|
||||
['key' => 'author', 'title' => '作者页面', 'fields' => [
|
||||
['key' => 'page_author_cover', 'label' => '显示封面', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'page_author_sidebar', 'label' => '显示侧边栏', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'page_author_posts_per_page', 'label' => '每页文章数', 'type' => 'number', 'default' => 12, 'min' => 4, 'max' => 48],
|
||||
]],
|
||||
],
|
||||
],
|
||||
'background' => [
|
||||
'key' => 'background',
|
||||
'title' => '全局背景',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'groups' => [
|
||||
['key' => 'light', 'title' => '亮色背景', 'fields' => [
|
||||
['key' => 'bg_color', 'label' => '背景颜色', 'type' => 'color', 'default' => ''],
|
||||
['key' => 'bg_image', 'label' => '背景图片', 'type' => 'media', 'default' => ''],
|
||||
['key' => 'bg_size', 'label' => '图片尺寸', 'type' => 'select', 'default' => 'cover', 'options' => [
|
||||
['label' => '覆盖', 'value' => 'cover'],
|
||||
['label' => '包含', 'value' => 'contain'],
|
||||
['label' => '原始大小', 'value' => 'auto'],
|
||||
['label' => '拉伸填充', 'value' => '100% 100%'],
|
||||
['label' => '100% 宽度(旧版兼容)', 'value' => '100%'],
|
||||
]],
|
||||
['key' => 'bg_position', 'label' => '图片位置', 'type' => 'select', 'default' => 'center center', 'options' => [
|
||||
['label' => '居中', 'value' => 'center center'],
|
||||
['label' => '顶部居中', 'value' => 'top center'],
|
||||
['label' => '底部居中', 'value' => 'bottom center'],
|
||||
]],
|
||||
['key' => 'bg_repeat', 'label' => '图片重复', 'type' => 'select', 'default' => 'no-repeat', 'options' => [
|
||||
['label' => '不重复', 'value' => 'no-repeat'],
|
||||
['label' => '平铺', 'value' => 'repeat'],
|
||||
['label' => '水平平铺', 'value' => 'repeat-x'],
|
||||
['label' => '垂直平铺', 'value' => 'repeat-y'],
|
||||
]],
|
||||
['key' => 'bg_attachment', 'label' => '固定方式', 'type' => 'select', 'default' => 'fixed', 'options' => [
|
||||
['label' => '固定', 'value' => 'fixed'],
|
||||
['label' => '随内容滚动', 'value' => 'scroll'],
|
||||
]],
|
||||
]],
|
||||
['key' => 'dark', 'title' => '暗色背景', 'fields' => [
|
||||
['key' => 'bg_dark_color', 'label' => '暗色背景颜色', 'type' => 'color', 'default' => ''],
|
||||
['key' => 'bg_dark_image', 'label' => '暗色背景图片', 'type' => 'media', 'default' => ''],
|
||||
]],
|
||||
['key' => 'overlay', 'title' => '遮罩设置', 'fields' => [
|
||||
['key' => 'bg_overlay', 'label' => '启用背景遮罩', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'bg_overlay_color', 'label' => '遮罩颜色', 'type' => 'input', 'default' => 'rgba(0, 0, 0, 0.3)'],
|
||||
]],
|
||||
['key' => 'page', 'title' => '页面背景', 'fields' => [
|
||||
['key' => 'page_bg_color', 'label' => '内容区背景色', 'type' => 'color', 'default' => ''],
|
||||
['key' => 'page_bg_image', 'label' => '内容区背景图', 'type' => 'media', 'default' => ''],
|
||||
]],
|
||||
],
|
||||
],
|
||||
'footer' => [
|
||||
'key' => 'footer',
|
||||
'title' => '底部页脚',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'groups' => [
|
||||
['key' => 'layout', 'title' => '页脚布局', 'fields' => [
|
||||
['key' => 'footer_columns', 'label' => '页脚列数', 'type' => 'number', 'default' => 3, 'min' => 1, 'max' => 4],
|
||||
['key' => 'footer_copyright', 'label' => '显示版权信息', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'footer_icp', 'label' => '显示ICP备案', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'footer_police', 'label' => '显示公安备案', 'type' => 'switch', 'default' => true],
|
||||
]],
|
||||
['key' => 'color', 'title' => '页脚颜色', 'fields' => [
|
||||
['key' => 'footer_theme_custom', 'label' => '自定义页脚颜色', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'footer_theme_bg_custom', 'label' => '自定义背景色', 'type' => 'color', 'default' => ''],
|
||||
['key' => 'footer_theme_bg', 'label' => '背景图片', 'type' => 'media', 'default' => ''],
|
||||
['key' => 'footer_theme_color_custom', 'label' => '自定义文字颜色', 'type' => 'color', 'default' => ''],
|
||||
]],
|
||||
['key' => 'contact', 'title' => '联系方式', 'fields' => [
|
||||
['key' => 'footer_contact_wechat_img', 'label' => '微信二维码', 'type' => 'media', 'default' => ''],
|
||||
['key' => 'footer_contact_qq', 'label' => 'QQ', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'footer_contact_weibo', 'label' => '微博', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'footer_contact_email', 'label' => '邮箱', 'type' => 'input', 'default' => ''],
|
||||
]],
|
||||
['key' => 'brand', 'title' => '品牌列(第一列)', 'fields' => [
|
||||
['key' => 'footer_t1_m_s', 'label' => '显示设备', 'type' => 'checkbox', 'default' => ['pc', 'mobile'], 'options' => [
|
||||
['label' => 'PC', 'value' => 'pc'],
|
||||
['label' => '移动端', 'value' => 'mobile'],
|
||||
]],
|
||||
['key' => 'footer_t1_img', 'label' => '页脚 Logo(日间)', 'type' => 'media', 'default' => ''],
|
||||
['key' => 'footer_t1_img_dark', 'label' => '页脚 Logo(夜间)', 'type' => 'media', 'default' => ''],
|
||||
['key' => 'footer_t1_t', 'label' => '品牌标题', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'fcode_t1_code', 'label' => '品牌列自定义代码', 'type' => 'textarea', 'default' => ''],
|
||||
]],
|
||||
['key' => 'contact_ext', 'title' => '联系方式扩展', 'fields' => [
|
||||
['key' => 'footer_contact_m_s', 'label' => '显示设备', 'type' => 'checkbox', 'default' => ['pc', 'mobile'], 'options' => [
|
||||
['label' => 'PC', 'value' => 'pc'],
|
||||
['label' => '移动端', 'value' => 'mobile'],
|
||||
]],
|
||||
['key' => 'footer_contact_more', 'label' => '更多联系项', 'type' => 'textarea', 'default' => '', 'help' => 'JSON 数组,每项含 desc/icon/link/img'],
|
||||
['key' => 'fcode_t2_code_1', 'label' => '链接 HTML(fcode_t2_code_1)', 'type' => 'textarea', 'default' => '', 'help' => '直接写 <a> 链接 HTML;为空时回退快速链接/友链/热门分类'],
|
||||
['key' => 'fcode_t2_code_2', 'label' => '链接下方自定义代码', 'type' => 'textarea', 'default' => '', 'help' => '备案号等 HTML'],
|
||||
]],
|
||||
['key' => 'mini_qr', 'title' => '二维码图片组(第三列)', 'fields' => [
|
||||
['key' => 'footer_mini_img_m_s', 'label' => '显示设备', 'type' => 'checkbox', 'default' => ['pc', 'mobile'], 'options' => [
|
||||
['label' => 'PC', 'value' => 'pc'],
|
||||
['label' => '移动端', 'value' => 'mobile'],
|
||||
]],
|
||||
['key' => 'footer_mini_img', 'label' => '二维码组', 'type' => 'textarea', 'default' => '', 'help' => 'JSON 数组,每项含 text/img(最多4个)'],
|
||||
]],
|
||||
['key' => 'custom', 'title' => '底部自定义', 'fields' => [
|
||||
['key' => 'fcode_customize_code', 'label' => '底部自定义代码', 'type' => 'textarea', 'default' => ''],
|
||||
]],
|
||||
],
|
||||
],
|
||||
'tabbar' => [
|
||||
'key' => 'tabbar',
|
||||
'title' => '底部Tab栏',
|
||||
'version' => '1.1.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => 'Tab栏设置', 'fields' => [
|
||||
['key' => 'footer_tabbar_s', 'label' => '启用底部Tab栏', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'footer_tabbar_scroll_hide', 'label' => '滚动时隐藏', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'footer_tabbar_scroll_down_hide', 'label' => '下滑时隐藏', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'footer_tabbar_max_items', 'label' => '最大Tab数', 'type' => 'number', 'default' => 5, 'min' => 3, 'max' => 7],
|
||||
['key' => 'footer_tabbar_icon_size', 'label' => '图标大小', 'type' => 'number', 'default' => 22, 'min' => 16, 'max' => 32],
|
||||
['key' => 'footer_tabbar_text_size', 'label' => '文字大小', 'type' => 'number', 'default' => 10, 'min' => 8, 'max' => 14],
|
||||
['key' => 'footer_tabbar_show_text', 'label' => '显示文字', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'footer_tabbar_active_color', 'label' => '选中颜色', 'type' => 'color', 'default' => '#f04494'],
|
||||
['key' => 'footer_tabbar_bg_color', 'label' => '背景颜色', 'type' => 'color', 'default' => ''],
|
||||
['key' => 'footer_tabbar_border', 'label' => '显示上边框', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'footer_tabbar_shadow', 'label' => '显示阴影', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'footer_tabbar_animation', 'label' => '切换动画', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'footer_tabbar_double_click_home', 'label' => '双击首页刷新', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'footer_tabbar_badge_dot', 'label' => '徽标圆点样式', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'footer_tabbar_single', 'label' => '文章页专属Tab', 'type' => 'select', 'default' => 'default', 'options' => [
|
||||
['label' => '默认按钮', 'value' => 'default'],
|
||||
['label' => '文章扩展按钮(评论/收藏/分享)', 'value' => 'extend'],
|
||||
], 'help' => '文章详情页底部显示的Tab按钮类型'],
|
||||
]],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/** @return list<array{label:string,value:string}> */
|
||||
private static function device_options(): array {
|
||||
public static function build(): array {
|
||||
return [
|
||||
['label' => '电脑端', 'value' => 'pc_s'],
|
||||
['label' => '移动端', 'value' => 'm_s'],
|
||||
];
|
||||
}
|
||||
|
||||
/** @return list<array<string,mixed>> */
|
||||
private static function header_slider_defaults(): array {
|
||||
$base = defined('AKSRT_URI') ? trailingslashit(AKSRT_URI) . 'assets/vendor/zibll/img/' : '';
|
||||
$slides = [];
|
||||
foreach (['slider-bg.jpg', 'user_t.jpg'] as $image) {
|
||||
$slides[] = [
|
||||
'background' => $base . $image,
|
||||
'link' => ['url' => 'https://www.zibll.com/', 'target' => '_blank'],
|
||||
'text' => ['desc' => '', 'title' => ''],
|
||||
'text_align' => 'left-bottom',
|
||||
'text_parallax' => 30,
|
||||
'text_size_m' => 20,
|
||||
'text_size_pc' => 30,
|
||||
];
|
||||
}
|
||||
return $slides;
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private static function header_slider_option_defaults(): array {
|
||||
return [
|
||||
'direction' => 'horizontal',
|
||||
'loop' => true,
|
||||
'button' => true,
|
||||
'pagination' => true,
|
||||
'effect' => 'slide',
|
||||
'scale_height' => false,
|
||||
'auto_height' => false,
|
||||
'pc_height' => 550,
|
||||
'm_height' => 280,
|
||||
'spacebetween' => 0,
|
||||
'speed' => 0,
|
||||
'autoplay' => true,
|
||||
'interval' => 6,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private static function header_slider_search_defaults(): array {
|
||||
return [
|
||||
'show_keywords' => true,
|
||||
'popular_limit' => 6,
|
||||
'show_input_cat' => false,
|
||||
'in_cat' => '',
|
||||
'more_cats' => [],
|
||||
'show_type' => false,
|
||||
'in_type' => 'post',
|
||||
'before_html' => '<div class="em16 font-bold mb10">更优雅的WordPress主题</div><div>这是导航栏幻灯片多功能组件,可在后台进行配置</div>',
|
||||
'after_html' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private static function header_slider_card_defaults(): array {
|
||||
return [
|
||||
'show_widget_bg' => false,
|
||||
'icon_radius4' => false,
|
||||
'cards' => [
|
||||
['icon' => 'zibsvg-vip_1', 'customize_icon' => '', 'icon_class' => 'c-yellow', 'link' => [], 'title' => '图标卡片', 'desc' => '这是一个图标卡片示例'],
|
||||
['icon' => 'zibsvg-vip_2', 'customize_icon' => '', 'icon_class' => 'c-blue-2', 'link' => [], 'title' => '原创作品', 'desc' => '这是一个图标卡片示例'],
|
||||
['icon' => 'zibsvg-hot', 'customize_icon' => '', 'icon_class' => 'jb-pink', 'link' => [], 'title' => '灵感来源<badge class="ml6 jb-yellow">NEW</badge>', 'desc' => '这是一个图标卡片示例'],
|
||||
['icon' => 'fa fas fa-ioxhost', 'customize_icon' => '', 'icon_class' => 'jb-cyan', 'link' => [], 'title' => '系统工具 <badge class="ml6 jb-blue">GO <span class="fa fas fa-angle-right em12"></span></badge>', 'desc' => '这是一个图标卡片示例'],
|
||||
'domain' => 'page_display',
|
||||
'title' => '页面&显示',
|
||||
'sections' => [
|
||||
'header-nav' => self::header_nav(),
|
||||
'header-components' => self::header_components(),
|
||||
'footer' => self::footer(),
|
||||
'homepage' => self::homepage(),
|
||||
'category' => self::category(),
|
||||
'tag' => self::tag(),
|
||||
'user-home' => self::user_home(),
|
||||
'other' => self::other(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/** @return list<array{label:string,value:string,image:string}> */
|
||||
private static function header_layout_options(): array {
|
||||
$base = defined('AKSRT_URI') ? trailingslashit(AKSRT_URI) . 'assets/images/header-layout/' : '';
|
||||
return [
|
||||
['label' => '布局 1(经典)', 'value' => '1', 'image' => $base . 'header_layout_1.png'],
|
||||
['label' => '布局 2(内联搜索)', 'value' => '2', 'image' => $base . 'header_layout_2.png'],
|
||||
['label' => '布局 3(紧凑)', 'value' => '3', 'image' => $base . 'header_layout_3.png'],
|
||||
];
|
||||
}
|
||||
private static function section(string $key, string $title, array $fields): array {
|
||||
return [
|
||||
'key' => $key,
|
||||
'title' => $title,
|
||||
'version' => '9.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'groups' => [['key' => $key, 'fields' => $fields]],
|
||||
];
|
||||
}
|
||||
|
||||
/** @return list<array{label:string,value:string,image:string}> */
|
||||
private static function mobile_header_layout_options(): array {
|
||||
$base = defined('AKSRT_URI') ? trailingslashit(AKSRT_URI) . 'assets/images/header-layout/' : '';
|
||||
return [
|
||||
['label' => '居中(含搜索按钮)', 'value' => 'center', 'image' => $base . 'mobile_header_layout_center.png'],
|
||||
['label' => '靠左', 'value' => 'left', 'image' => $base . 'mobile_header_layout_left.png'],
|
||||
];
|
||||
}
|
||||
private static function header_nav(): array {
|
||||
return self::section('header-nav', '顶部导航栏', [
|
||||
self::notice('添加或管理菜单后,可在此配置导航栏显示效果。'),
|
||||
self::field('header_theme_custom', '自定义导航栏颜色', 'switcher', false),
|
||||
self::field('header_theme_bg_custom', '导航栏:背景色', 'color', '', ['dependsOn' => self::enabled('header_theme_custom'), 'description' => '如需选择预置颜色,请先清空此颜色。']),
|
||||
self::field('header_theme_bg', '', 'palette', '', ['dependsOn' => ['all' => [self::enabled('header_theme_custom'), ['key' => 'header_theme_bg_custom', 'operator' => 'empty']]], 'options' => self::solid_palette()]),
|
||||
self::field('header_theme_color_custom', '导航栏:前景色', 'color', '', ['dependsOn' => self::enabled('header_theme_custom'), 'description' => '请注意背景色和前景色的搭配,以免文字看不清。']),
|
||||
self::field('header_layout', '电脑端导航布局', 'image_select', '1', ['options' => self::header_layout_options()]),
|
||||
self::field('mobile_header_layout', '移动端导航布局', 'image_select', 'center', ['options' => self::mobile_header_layout_options()]),
|
||||
self::field('mobile_navbar_align', '移动端菜单弹出方向', 'radio', 'left', ['inline' => true, 'options' => self::options(['top' => '顶部', 'left' => '左边', 'right' => '右边'])]),
|
||||
self::field('nav_fixed', '导航浮动', 'switcher', true, ['description' => '导航栏一直固定在顶部。']),
|
||||
self::field('nav_scroll_down_hide', '页面向下滚动时隐藏', 'switcher', true, ['dependsOn' => self::enabled('nav_fixed'), 'description' => '下滚动隐藏,上滚动显示。']),
|
||||
self::field('nav_newposts', 'Add发布按钮', 'switcher', true, ['description' => '顶部导航显示文章投稿按钮。']),
|
||||
self::field('nav_newposts_radius', '按钮圆角', 'switcher', true, ['dependsOn' => self::enabled('nav_newposts')]),
|
||||
self::field('nav_newposts_class', '按钮颜色', 'palette', 'jb-blue', ['dependsOn' => self::enabled('nav_newposts'), 'options' => self::zib_palette()]),
|
||||
self::field('nav_newposts_text', '按钮文案', 'textarea', '<i class="fa fa-fw fa-pencil"></i>发布', ['dependsOn' => self::enabled('nav_newposts'), 'sanitize_type' => 'html', 'rows' => 1]),
|
||||
self::field('nav_newposts_btns', '需显示的按钮', 'async_select', ['post'], ['dependsOn' => self::enabled('nav_newposts'), 'multiple' => true, 'sortable' => true, 'source' => 'publish-types', 'options' => self::options(['post' => '文章'])]),
|
||||
self::field('nav_user_newposts_btn', '导航栏用户卡片发布按钮', 'select', 'post', ['options' => self::options(['post' => '文章'])]),
|
||||
self::field('nav_user_newposts_btn_text', '按钮文字', 'text', '发布文章'),
|
||||
]);
|
||||
}
|
||||
|
||||
private static function header_components(): array {
|
||||
return self::section('header-components', '顶部多功能组件', [
|
||||
self::notice('顶部多功能组件显示在导航栏下方,支持视频、图片、幻灯片、搜索和图标卡片;各组件可按电脑端、移动端单独启用。'),
|
||||
self::field('header_slider_show', '启用顶部多功能组件', 'checkbox', [], ['options' => self::options(['home' => '首页', 'category' => '分类页', 'tag' => '标签页', 'author' => '用户页', 'single' => '文章页', 'search' => '搜索页', 'page' => '其它页面'])]),
|
||||
self::field('header_slider_show_type', '顶部多功能组件的显示规则', 'radio', '', ['dependsOn' => self::not_empty('header_slider_show'), 'inline' => true, 'options' => self::options(['' => '全部显示', 'only_pc' => '仅在PC端显示', 'only_sm' => '仅在移动端显示'])]),
|
||||
self::field('header_slider_nav_color', '导航栏文字颜色', 'color', '#ffffff', ['dependsOn' => self::not_empty('header_slider_show')]),
|
||||
self::field('header_slider', '背景内容', 'group', self::header_slider_defaults(), ['dependsOn' => self::not_empty('header_slider_show'), 'min' => 1, 'button_title' => '添加背景项目', 'fields' => self::slider_item_fields()]),
|
||||
self::field('header_slider_option', '背景显示设置', 'fieldset', self::slider_options_defaults(), ['dependsOn' => self::not_empty('header_slider_show'), 'fields' => self::slider_option_fields()]),
|
||||
self::field('header_slider_search_s', '叠加搜索组件', 'checkbox', ['pc_s', 'm_s'], ['options' => self::device_options()]),
|
||||
self::field('header_slider_search_option', '搜索组件设置', 'fieldset', self::search_defaults(), ['dependsOn' => self::not_empty('header_slider_search_s'), 'fields' => self::search_fields()]),
|
||||
self::field('header_slider_card_s', '叠加封面卡片组件', 'checkbox', ['pc_s', 'm_s'], ['options' => self::device_options()]),
|
||||
self::field('header_slider_card_option', '图标卡片设置', 'fieldset', self::card_defaults(), ['dependsOn' => self::not_empty('header_slider_card_s'), 'fields' => self::card_fields()]),
|
||||
self::field('header_slider_filter_blur', '组件高斯模糊特效', 'checkbox', ['pc_s', 'm_s'], ['options' => self::options(['pc_s' => 'PC端开启', 'm_s' => '移动端开启'])]),
|
||||
]);
|
||||
}
|
||||
|
||||
private static function footer(): array {
|
||||
$img = self::asset('assets/vendor/zibll/img/');
|
||||
return self::section('footer', '底部页脚', [
|
||||
self::field('footer_theme_custom', '自定义底部页脚颜色', 'switcher', false),
|
||||
self::field('footer_theme_bg_custom', '底部页脚:背景色', 'color', '', ['dependsOn' => self::enabled('footer_theme_custom')]),
|
||||
self::field('footer_theme_bg', '', 'palette', '', ['dependsOn' => ['all' => [self::enabled('footer_theme_custom'), ['key' => 'footer_theme_bg_custom', 'operator' => 'empty']]], 'options' => self::solid_palette()]),
|
||||
self::field('footer_theme_color_custom', '底部页脚:前景色', 'color', '', ['dependsOn' => self::enabled('footer_theme_custom')]),
|
||||
self::field('fcode_template', '页脚布局模板选择', 'image_select', 'template_1', ['options' => [['label' => '模板一', 'value' => 'template_1', 'image' => self::asset('assets/images/footer/fcode_template_1.png')]]]),
|
||||
self::field('footer_t1_m_s', '版块一移动端显示', 'switcher', false),
|
||||
self::field('footer_t1_img', '日间模式图片', 'upload', $img . 'logo.png'),
|
||||
self::field('footer_t1_img_dark', '夜间模式图片', 'upload', $img . 'logo_dark.png'),
|
||||
self::field('footer_t1_t', '首行文字', 'text', ''),
|
||||
self::field('fcode_t1_code', '更多内容', 'textarea', 'Zibll 子比主题专为博客、自媒体、资讯类的网站设计开发,简约优雅的设计风格,全面的前端用户功能,简单的模块化配置,欢迎您的体验', ['sanitize_type' => 'html', 'rows' => 3]),
|
||||
self::field('fcode_t2_code_1', '版块二第一行', 'textarea', '<a href="https://zibll.com">友链申请</a>' . "\n" . '<a href="https://zibll.com">免责声明</a>' . "\n" . '<a href="https://zibll.com">广告合作</a>' . "\n" . '<a href="https://zibll.com">关于我们</a>', ['sanitize_type' => 'html', 'rows' => 4]),
|
||||
self::field('fcode_t2_code_2', '版块二第二行', 'textarea', self::copyright_default(), ['sanitize_type' => 'html', 'rows' => 3]),
|
||||
self::field('footer_contact_m_s', '图标联系方式移动端显示', 'switcher', true),
|
||||
self::field('footer_contact_wechat_img', '微信二维码', 'upload', $img . 'qrcode.png'),
|
||||
self::field('footer_contact_qq', 'QQ号', 'text', '1234567788'),
|
||||
self::field('footer_contact_weibo', '微博链接', 'url', 'https://weibo.com/'),
|
||||
self::field('footer_contact_email', '邮箱', 'text', '1234567788@QQ.COM'),
|
||||
self::field('footer_contact_more', '更多图标联系方式', 'group', [], ['button_title' => '添加图标联系方式', 'fields' => [
|
||||
self::field('desc', '按钮名称', 'text', ''), self::field('icon', '按钮图标', 'icon', 'fa fa-heart'), self::field('link', '按钮链接', 'url', '', ['dependsOn' => ['key' => 'img', 'operator' => 'empty']]), self::field('img', '二维码图片', 'upload', ''),
|
||||
]]),
|
||||
self::field('footer_mini_img_m_s', '版块三移动端显示', 'switcher', true),
|
||||
self::field('footer_mini_img', '版块三大图片', 'group', [['image' => $img . 'qrcode.png', 'text' => '扫码加QQ群'], ['image' => $img . 'qrcode.png', 'text' => '扫码加微信']], ['max' => 4, 'button_title' => '添加图片', 'fields' => self::footer_image_fields()]),
|
||||
self::field('footer_mini_img_2', '版块三小图片', 'group', [], ['max' => 16, 'button_title' => '添加图片', 'fields' => self::footer_image_fields()]),
|
||||
self::field('fcode_customize_code', '页脚自定义HTML', 'textarea', '', ['sanitize_type' => 'html', 'rows' => 3]),
|
||||
]);
|
||||
}
|
||||
|
||||
private static function homepage(): array {
|
||||
return self::section('homepage', '首页配置', [
|
||||
self::notice('首页主文章模块。关闭则首页不显示主文章模块,但仍可通过模块添加。'),
|
||||
self::field('home_posts_list_s', '首页文章', 'switcher', true),
|
||||
self::field('home_list1_orderby_s', '栏目1设置', 'switcher', true, ['dependsOn' => self::enabled('home_posts_list_s')]),
|
||||
self::field('home_list1_orderby_option', '排序方式设置', 'fieldset', '', ['dependsOn' => ['all' => [self::enabled('home_posts_list_s'), self::enabled('home_list1_orderby_s')]], 'fields' => self::orderby_fields()]),
|
||||
self::field('index_list_title', '显示标题', 'textarea', '', ['dependsOn' => self::enabled('home_posts_list_s'), 'sanitize_type' => 'html', 'rows' => 1]),
|
||||
self::field('home_exclude_posts', '排除文章', 'async_select', '', ['dependsOn' => self::enabled('home_posts_list_s'), 'source' => 'posts', 'multiple' => true, 'sortable' => true]),
|
||||
self::field('home_exclude_cats', '排除分类', 'async_select', '', ['dependsOn' => self::enabled('home_posts_list_s'), 'source' => 'categories', 'multiple' => true, 'sortable' => true]),
|
||||
self::field('home_lists', '更多栏目', 'group', [], ['dependsOn' => self::enabled('home_posts_list_s'), 'button_title' => '添加栏目', 'fields' => [self::field('title', '自定义标题', 'textarea', '', ['sanitize_type' => 'html', 'rows' => 1]), self::field('term_id', '显示内容', 'async_select', '', ['source' => 'categories']), self::field('orderby', '排序方式设置', 'select', '', ['options' => self::post_order_options(true)])]]),
|
||||
]);
|
||||
}
|
||||
|
||||
private static function category(): array {
|
||||
return self::section('category', '分类页面', [
|
||||
self::field('page_cover_cat_s', '显示封面图', 'switcher', true),
|
||||
self::field('cat_post_count_s', '显示文章数量', 'switcher', true),
|
||||
self::field('cat_default_cover', '封面图', 'upload', self::asset('assets/vendor/zibll/img/user_t.jpg'), ['dependsOn' => self::enabled('page_cover_cat_s'), 'help' => '默认封面图,建议尺寸2000x800。']),
|
||||
self::field('cat_orderby_s', '排序方式按钮', 'switcher', true),
|
||||
self::field('cat_orderby_option', '排序方式设置', 'fieldset', [], ['dependsOn' => self::enabled('cat_orderby_s'), 'fields' => self::orderby_fields()]),
|
||||
self::notice('AJAX筛选菜单列表:显示分类、标签菜单,通过 AJAX 获取内容。'),
|
||||
self::field('ajax_list_cat_cat', '分类筛选', 'switcher', true),
|
||||
self::field('ajax_list_option_cat_cat', '分类筛选设置', 'fieldset', [], ['dependsOn' => self::enabled('ajax_list_cat_cat'), 'fields' => self::ajax_fields('categories')]),
|
||||
self::field('ajax_list_cat_tag', '标签筛选', 'switcher', false),
|
||||
self::field('ajax_list_option_cat_tag', '标签筛选设置', 'fieldset', [], ['dependsOn' => self::enabled('ajax_list_cat_tag'), 'fields' => self::ajax_fields('tags')]),
|
||||
]);
|
||||
}
|
||||
|
||||
private static function tag(): array {
|
||||
return self::section('tag', '标签页面', [
|
||||
self::field('tag_post_count_s', '显示文章数量', 'switcher', true),
|
||||
self::field('page_cover_tag_s', '显示封面图', 'switcher', true),
|
||||
self::field('tag_default_cover', '封面图', 'upload', self::asset('assets/vendor/zibll/img/user_t.jpg'), ['dependsOn' => self::enabled('page_cover_tag_s'), 'help' => '默认封面图,建议尺寸2000x800。']),
|
||||
self::field('tag_orderby_s', '排序方式按钮', 'switcher', true),
|
||||
self::field('tag_orderby_option', '排序方式设置', 'fieldset', [], ['dependsOn' => self::enabled('tag_orderby_s'), 'fields' => self::orderby_fields()]),
|
||||
self::notice('AJAX筛选菜单列表:显示分类、标签菜单,通过 AJAX 获取内容。'),
|
||||
self::field('ajax_list_tag_cat', '分类筛选', 'switcher', false),
|
||||
self::field('ajax_list_option_tag_cat', '分类筛选设置', 'fieldset', [], ['dependsOn' => self::enabled('ajax_list_tag_cat'), 'fields' => self::ajax_fields('categories')]),
|
||||
self::field('ajax_list_tag_tag', '标签筛选', 'switcher', true),
|
||||
self::field('ajax_list_option_tag_tag', '标签筛选设置', 'fieldset', [], ['dependsOn' => self::enabled('ajax_list_tag_tag'), 'fields' => self::ajax_fields('tags')]),
|
||||
]);
|
||||
}
|
||||
|
||||
private static function user_home(): array {
|
||||
$items = [];
|
||||
foreach (['post' => '文章', 'favorite' => '收藏', 'comment' => '评论', 'follow' => '粉丝'] as $key => $title) {
|
||||
$items[] = ['key' => $key, 'label' => $title, 'fields' => [['key' => 'pc_s', 'label' => 'PC端显示', 'type' => 'switcher', 'default' => true], ['key' => 'm_s', 'label' => '移动端显示', 'type' => 'switcher', 'default' => true]]];
|
||||
}
|
||||
return self::section('user-home', '用户主页', [
|
||||
self::field('author_main_tab_opt', '栏目配置', 'sortable', ['post' => ['pc_s' => true, 'm_s' => true], 'favorite' => ['pc_s' => true, 'm_s' => true], 'comment' => ['pc_s' => true, 'm_s' => true], 'follow' => ['pc_s' => true, 'm_s' => true]], ['items' => $items, 'description' => '用户个人主页栏目显示配置及排序。']),
|
||||
self::field('author_main_tab_swiper', '移动端滑动切换', 'switcher', false),
|
||||
]);
|
||||
}
|
||||
|
||||
private static function other(): array {
|
||||
return self::section('other', '其它页面', [
|
||||
self::field('page_header_style', '页面标题', 'radio', '', ['options' => self::options(['' => '不显示', '1' => '简单样式', '2' => '卡片样式', '3' => '封面图样式'])]),
|
||||
self::field('page_header_cover_img', '页面封面图', 'upload', self::asset('assets/vendor/zibll/img/user_t.jpg')),
|
||||
]);
|
||||
}
|
||||
|
||||
private static function slider_item_fields(): array {
|
||||
return [
|
||||
self::field('background', '图片背景', 'upload', ''), self::field('background_video', '视频背景', 'upload', '', ['media_type' => 'video']), self::field('hide', '显示规则', 'radio', '', ['options' => self::options(['' => '全部显示', 'pc' => 'PC端不显示', 'm' => '移动端不显示'])]), self::field('link', '跳转链接', 'link', []),
|
||||
self::field('image_layer', '叠加图层', 'group', [], ['button_title' => '添加图层', 'fields' => [self::field('image', '图层图片', 'upload', ''), self::field('free_size', '自由尺寸', 'switcher', false), self::field('align', '图层对齐', 'radio', 'center', ['dependsOn' => ['all' => [self::not_empty('image'), self::enabled('free_size')]], 'options' => self::options(['left' => '靠左显示', 'center' => '居中显示', 'right' => '靠右显示'])]), self::field('parallax', '视差滚动', 'number', 0, ['dependsOn' => self::not_empty('image'), 'min' => -200, 'max' => 200, 'step' => 5]), self::field('parallax_scale', '视差缩放', 'number', 100, ['dependsOn' => self::not_empty('image'), 'min' => 1, 'max' => 200, 'step' => 5]), self::field('parallax_opacity', '视差透明', 'number', 100, ['dependsOn' => ['all' => [self::not_empty('image'), self::not_empty('parallax')]], 'min' => 1, 'max' => 100, 'step' => 5])]]),
|
||||
self::field('text', '叠加文案', 'accordion', ['title' => '', 'desc' => '', 'text_align' => 'left-bottom', 'text_size_pc' => 30, 'text_size_m' => 20, 'parallax' => 40], ['fields' => [self::field('title', '幻灯片标题', 'textarea', '', ['sanitize_type' => 'html']), self::field('desc', '幻灯片简介', 'textarea', '', ['dependsOn' => self::not_empty('title'), 'sanitize_type' => 'html']), self::field('text_align', '显示位置', 'image_select', 'left-bottom', ['dependsOn' => self::not_empty('title'), 'options' => self::text_align_options()]), self::field('text_size_pc', 'PC端字体大小', 'number', 30, ['dependsOn' => self::not_empty('title'), 'min' => 12, 'max' => 50, 'step' => 1]), self::field('text_size_m', '移动端字体大小', 'number', 20, ['dependsOn' => self::not_empty('title'), 'min' => 12, 'max' => 50, 'step' => 1]), self::field('parallax', '文案视差滚动', 'number', 40, ['dependsOn' => self::not_empty('title'), 'min' => -200, 'max' => 200, 'step' => 10])]]),
|
||||
];
|
||||
}
|
||||
|
||||
private static function slider_option_fields(): array {
|
||||
return [self::field('direction', '幻灯片方向', 'radio', 'horizontal', ['options' => self::options(['horizontal' => '左右切换', 'vertical' => '上下切换'])]), self::field('loop', '循环切换', 'switcher', true), self::field('button', '显示翻页按钮', 'switcher', false), self::field('pagination', '显示指示器', 'switcher', false), self::field('effect', '切换动画', 'select', 'slide', ['options' => self::options(['slide' => '滑动', 'fade' => '淡出淡入', 'cube' => '3D方块', 'coverflow' => '3D滑入', 'flip' => '3D翻转'])]), self::field('scale_height', '按比例自动高度', 'switcher', true, ['dependsOn' => self::not_value('direction', 'vertical')]), self::field('scale', '长宽比例', 'number', 35, ['dependsOn' => ['all' => [self::not_value('direction', 'vertical'), self::enabled('scale_height')]], 'min' => 10, 'max' => 300, 'step' => 5]), self::field('auto_height', '自动高度', 'switcher', false, ['dependsOn' => ['all' => [self::not_value('direction', 'vertical'), self::disabled('scale_height')]]]), self::field('max_height', '最大高度', 'number', 500, ['dependsOn' => ['all' => [self::enabled('auto_height'), self::not_value('direction', 'vertical'), self::disabled('scale_height')]], 'min' => 120, 'max' => 800, 'step' => 20]), self::field('min_height', '最小高度', 'number', 180, ['dependsOn' => ['all' => [self::enabled('auto_height'), self::not_value('direction', 'vertical'), self::disabled('scale_height')]], 'min' => 100, 'max' => 500, 'step' => 20]), self::field('pc_height', '电脑端高度', 'number', 400, ['min' => 120, 'max' => 800, 'step' => 20]), self::field('m_height', '移动端高度', 'number', 200, ['min' => 100, 'max' => 500, 'step' => 20]), self::field('spacebetween', '幻灯片间距', 'number', 15, ['min' => 0, 'max' => 500, 'step' => 5]), self::field('speed', '切换速度', 'number', 0, ['min' => 0, 'max' => 3000, 'step' => 100]), self::field('autoplay', '自动播放', 'switcher', true), self::field('interval', '停顿时间', 'number', 4, ['dependsOn' => self::enabled('autoplay'), 'min' => 0, 'max' => 20, 'step' => 1])];
|
||||
}
|
||||
|
||||
private static function search_fields(): array {
|
||||
return [self::field('show_keywords', '热门搜索', 'switcher', true), self::field('popular_limit', '关键词最多显示', 'number', 6, ['dependsOn' => self::enabled('show_keywords'), 'min' => 2, 'max' => 50]), self::field('show_input_cat', '分类搜索', 'switcher', false), self::field('in_cat', '默认搜索的分类', 'async_select', '', ['dependsOn' => self::enabled('show_input_cat'), 'source' => 'categories']), self::field('more_cats', '允许用户选择更多分类', 'async_select', '', ['dependsOn' => self::enabled('show_input_cat'), 'source' => 'categories', 'multiple' => true, 'sortable' => true]), self::field('show_type', '显示类型选择', 'switcher', false), self::field('in_type', '默认搜索类型', 'radio', 'post', ['options' => self::options(['post' => '文章', 'user' => '用户'])]), self::field('before_html', '组件上方额外内容', 'textarea', '<div class="em16 font-bold mb10">更优雅的Wordpress主题</div>' . "\n" . '<div>这是导航栏幻灯片多功能组件,可在后台进行配置</div>', ['sanitize_type' => 'html']), self::field('after_html', '组件下方额外内容', 'textarea', '', ['sanitize_type' => 'html'])];
|
||||
}
|
||||
|
||||
private static function card_fields(): array {
|
||||
return [self::field('show_widget_bg', '模块背景', 'switcher', false), self::field('icon_radius4', '方形图标', 'switcher', false), self::field('cards', '添加卡片', 'group', [], ['button_title' => '添加内容', 'fields' => [self::field('title', '文案标题', 'textarea', '', ['sanitize_type' => 'html']), self::field('desc', '文案简介', 'textarea', '', ['sanitize_type' => 'html']), self::field('icon', '选择图标', 'icon', 'fa fa-magic'), self::field('customize_icon', '自定义图标', 'textarea', '', ['sanitize_type' => 'html']), self::field('icon_class', '图标颜色', 'palette', 'c-yellow', ['options' => self::zib_palette(true)]), self::field('link', '跳转链接', 'link', [])]])];
|
||||
}
|
||||
|
||||
private static function orderby_fields(): array { return [self::field('lists', '显示排序方式', 'select', ['modified', 'views', 'like', 'comment_count'], ['multiple' => true, 'sortable' => true, 'options' => self::post_order_options(false)]), self::field('dropdown', '更多排序方式', 'switcher', false)]; }
|
||||
private static function ajax_fields(string $source): array { return [self::field('lists', '按钮列表', 'async_select', [], ['source' => $source, 'multiple' => true, 'sortable' => true]), self::field('dropdown', '下拉列表', 'switcher', false), self::field('dropdown_lists', '下拉菜单列表', 'async_select', [], ['dependsOn' => self::enabled('dropdown'), 'source' => $source, 'multiple' => true, 'sortable' => true])]; }
|
||||
private static function footer_image_fields(): array { return [self::field('text', '显示文字', 'text', ''), self::field('image', '显示图片', 'upload', ''), self::field('link', '点击跳转', 'link', [])]; }
|
||||
private static function header_slider_defaults(): array { $img = self::asset('assets/vendor/zibll/img/'); return [['background' => $img . 'slider-bg.jpg', 'link' => ['url' => 'https://www.zibll.com/', 'target' => '_blank'], 'text' => ['title' => '', 'desc' => ''], 'text_align' => 'left-bottom', 'text_parallax' => 30, 'text_size_m' => 20, 'text_size_pc' => 30], ['background' => $img . 'user_t.jpg', 'link' => ['url' => 'https://www.zibll.com/', 'target' => '_blank'], 'text' => ['title' => '', 'desc' => ''], 'text_align' => 'left-bottom', 'text_parallax' => 30, 'text_size_m' => 20, 'text_size_pc' => 30]]; }
|
||||
private static function slider_options_defaults(): array { return ['direction' => 'horizontal', 'loop' => true, 'button' => true, 'pagination' => true, 'effect' => 'slide', 'scale_height' => false, 'auto_height' => false, 'pc_height' => 550, 'm_height' => 280, 'spacebetween' => 0, 'speed' => 0, 'autoplay' => true, 'interval' => 6]; }
|
||||
private static function search_defaults(): array { return ['show_keywords' => true, 'popular_limit' => 6, 'show_input_cat' => false, 'in_cat' => '', 'more_cats' => '', 'show_type' => false, 'in_type' => 'post', 'before_html' => '<div class="em16 font-bold mb10">更优雅的Wordpress主题</div>' . "\n" . '<div>这是导航栏幻灯片多功能组件,可在后台进行配置</div>', 'after_html' => '']; }
|
||||
private static function card_defaults(): array { return ['show_widget_bg' => false, 'icon_radius4' => false, 'cards' => [['icon' => 'zibsvg-vip_1', 'customize_icon' => '', 'icon_class' => 'c-yellow', 'link' => [], 'title' => '图标卡片', 'desc' => '这是一个图标卡片示例'], ['icon' => 'zibsvg-vip_2', 'customize_icon' => '', 'icon_class' => 'c-blue-2', 'link' => [], 'title' => '原创作品', 'desc' => '这是一个图标卡片示例'], ['icon' => 'zibsvg-hot', 'customize_icon' => '', 'icon_class' => 'jb-pink', 'link' => [], 'title' => '灵感来源<badge class="ml6 jb-yellow">NEW</badge>', 'desc' => '这是一个图标卡片示例'], ['icon' => 'fa fa-ioxhost', 'customize_icon' => '', 'icon_class' => 'jb-cyan', 'link' => [], 'title' => '系统工具 <badge class="ml6 jb-blue">GO <span class="fa fa-angle-right em12"></span></badge>', 'desc' => '这是一个图标卡片示例']]]; }
|
||||
|
||||
private static function field(string $key, string $label, string $type, $default = null, array $extra = []): array { return array_merge(['key' => $key, 'label' => $label, 'type' => $type, 'default' => $default], $extra); }
|
||||
private static function notice(string $content): array { return ['key' => '_notice_' . md5($content), 'type' => 'notice', 'content' => $content, 'tone' => 'warning']; }
|
||||
private static function enabled(string $key): array { return ['key' => $key, 'operator' => 'truthy']; }
|
||||
private static function disabled(string $key): array { return ['key' => $key, 'operator' => 'falsy']; }
|
||||
private static function not_empty(string $key): array { return ['key' => $key, 'operator' => 'not_empty']; }
|
||||
private static function not_value(string $key, string $value): array { return ['key' => $key, 'operator' => 'neq', 'value' => $value]; }
|
||||
private static function options(array $options): array { $out = []; foreach ($options as $value => $label) { $out[] = ['label' => $label, 'value' => (string) $value]; } return $out; }
|
||||
private static function device_options(): array { return self::options(['pc_s' => 'PC端显示', 'm_s' => '移动端显示']); }
|
||||
private static function solid_palette(): array { return self::options(['ff648f' => '#ff648f', 'c246f5' => '#c246f5', '469cf5' => '#469cf5', '27bf41' => '#27bf41', 'fd6b4e' => '#fd6b4e', '2d2422' => '#2d2422']); }
|
||||
private static function zib_palette(bool $transparent = false): array { $colors = $transparent ? ['transparent' => 'rgba(114,114,114,.1)'] : []; $colors += ['c-red' => 'rgba(255,84,115,.4)', 'c-yellow' => 'rgba(255,111,6,.4)', 'c-cyan' => 'rgba(8,196,193,.4)', 'c-blue' => 'rgba(41,151,247,.4)', 'c-blue-2' => 'rgba(77,130,249,.4)', 'c-green' => 'rgba(18,185,40,.4)', 'c-purple' => 'rgba(213,72,245,.4)', 'jb-red' => '#ff5473', 'jb-yellow' => '#ff8a00', 'jb-cyan' => '#08c4c1', 'jb-blue' => '#2997f7', 'jb-green' => '#12b928', 'jb-purple' => '#d548f5', 'jb-pink' => '#ff648f']; return self::options($colors); }
|
||||
private static function post_order_options(bool $with_default): array { $options = $with_default ? ['' => '保持默认'] : []; $options += ['modified' => '更新', 'date' => '发布', 'views' => '浏览', 'like' => '点赞', 'comment_count' => '评论', 'favorite' => '收藏', 'rand' => '随机']; return self::options($options); }
|
||||
private static function header_layout_options(): array { $base = self::asset('assets/images/header-layout/'); return [['label' => '布局一', 'value' => '1', 'image' => $base . 'header_layout_1.png'], ['label' => '布局二', 'value' => '2', 'image' => $base . 'header_layout_2.png'], ['label' => '布局三', 'value' => '3', 'image' => $base . 'header_layout_3.png']]; }
|
||||
private static function mobile_header_layout_options(): array { $base = self::asset('assets/images/header-layout/'); return [['label' => '居中', 'value' => 'center', 'image' => $base . 'mobile_header_layout_center.png'], ['label' => '居左', 'value' => 'left', 'image' => $base . 'mobile_header_layout_left.png']]; }
|
||||
private static function text_align_options(): array { $base = self::asset('assets/images/slider-align/'); return [['label' => '左下', 'value' => 'left-bottom', 'image' => $base . 'left-bottom.jpg'], ['label' => '左中', 'value' => 'left-conter', 'image' => $base . 'left-conter.jpg'], ['label' => '居中', 'value' => 'conter-conter', 'image' => $base . 'conter-conter.jpg'], ['label' => '中下', 'value' => 'conter-bottom', 'image' => $base . 'conter-bottom.jpg'], ['label' => '右中', 'value' => 'right-conter', 'image' => $base . 'right-conter.jpg'], ['label' => '右下', 'value' => 'right-bottom', 'image' => $base . 'right-bottom.jpg']]; }
|
||||
private static function asset(string $path): string { return trailingslashit(get_template_directory_uri()) . ltrim($path, '/'); }
|
||||
private static function copyright_default(): string { return sprintf('Copyright © %s · <a href="%s">%s</a> · 由<a target="_blank" href="https://www.zibll.com">zibll主题</a>强力驱动.', date('Y'), home_url(), get_bloginfo('title')); }
|
||||
}
|
||||
|
||||
@@ -123,6 +123,26 @@ class SchemaProvider {
|
||||
return $allowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a schema payload with the full nested field contract.
|
||||
*/
|
||||
public function sanitize_payload(string $domain, array $data, string $section = ''): array {
|
||||
$schema = $section !== '' ? $this->get_section_schema($domain, $section) : $this->get_schema($domain);
|
||||
if (!$schema) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$clean = [];
|
||||
foreach ($this->get_all_fields($schema) as $field) {
|
||||
$key = (string) $field['key'];
|
||||
if (array_key_exists($key, $data)) {
|
||||
$clean[$key] = $this->sanitize_field_value($data[$key], $field);
|
||||
}
|
||||
}
|
||||
|
||||
return $clean;
|
||||
}
|
||||
|
||||
public function validate(string $domain, array $data, string $section = ''): array {
|
||||
$schemas = [];
|
||||
|
||||
@@ -151,7 +171,7 @@ class SchemaProvider {
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = $field['type'] ?? 'input';
|
||||
$type = $field['type'] ?? 'input';
|
||||
if (($type === 'number' || $type === 'slider') && !is_numeric($value)) {
|
||||
$errors[] = sprintf('%s must be a number', $field['label'] ?? $key);
|
||||
continue;
|
||||
@@ -162,6 +182,25 @@ class SchemaProvider {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($type, ['fieldset', 'accordion', 'sortable', 'link'], true) && !is_array($value)) {
|
||||
$errors[] = sprintf('%s must be an object', $field['label'] ?? $key);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($type === 'group' && !is_array($value)) {
|
||||
$errors[] = sprintf('%s must be an array of items', $field['label'] ?? $key);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($type, ['group', 'repeater'], true) && is_array($value)) {
|
||||
if (isset($field['min']) && count($value) < (int) $field['min']) {
|
||||
$errors[] = sprintf('%s must contain at least %s items', $field['label'] ?? $key, $field['min']);
|
||||
}
|
||||
if (isset($field['max']) && count($value) > (int) $field['max']) {
|
||||
$errors[] = sprintf('%s must contain at most %s items', $field['label'] ?? $key, $field['max']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($type === 'repeater' && !is_array($value)) {
|
||||
$errors[] = sprintf('%s must be an array of items', $field['label'] ?? $key);
|
||||
continue;
|
||||
@@ -175,7 +214,7 @@ class SchemaProvider {
|
||||
$errors[] = sprintf('%s must be at most %s', $field['label'] ?? $key, $field['max']);
|
||||
}
|
||||
|
||||
if (in_array($type, ['select', 'radio'], true) && !empty($field['options'])) {
|
||||
if (in_array($type, ['select', 'radio', 'palette', 'image_select'], true) && !empty($field['options'])) {
|
||||
$valid_values = array_column($field['options'], 'value');
|
||||
// Multi-select: accept array values when schema.multiple is set
|
||||
// (legacy / SPA may still use type=select multiple).
|
||||
@@ -204,6 +243,8 @@ class SchemaProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$errors = array_merge($errors, $this->validate_nested_field($field, $value, (string) ($field['label'] ?? $key)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,14 +367,16 @@ class SchemaProvider {
|
||||
$stored = [];
|
||||
}
|
||||
|
||||
$allowed = $this->collect_allowed_fields($schema);
|
||||
foreach ($allowed as $key => $type) {
|
||||
$fields = [];
|
||||
foreach ($this->get_all_fields($schema) as $field) {
|
||||
$fields[(string) $field['key']] = $field;
|
||||
}
|
||||
foreach ($fields as $key => $field) {
|
||||
if (!array_key_exists($key, $data)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $this->sanitize_value($data[$key], $type);
|
||||
$stored[$key] = $value;
|
||||
$stored[$key] = $this->sanitize_field_value($data[$key], $field);
|
||||
}
|
||||
|
||||
// Captcha stores a single positive scene list.
|
||||
@@ -431,9 +474,161 @@ class SchemaProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private function sanitize_field_value($value, array $field) {
|
||||
$type = (string) ($field['type'] ?? 'text');
|
||||
$sanitize_type = (string) ($field['sanitize_type'] ?? '');
|
||||
if ($sanitize_type !== '') {
|
||||
return $this->sanitize_value($value, $sanitize_type);
|
||||
}
|
||||
|
||||
if (in_array($type, ['fieldset', 'accordion'], true)) {
|
||||
return $this->sanitize_object_fields($value, (array) ($field['fields'] ?? []));
|
||||
}
|
||||
|
||||
if (in_array($type, ['group', 'repeater'], true)) {
|
||||
if (!is_array($value)) {
|
||||
return [];
|
||||
}
|
||||
$items = [];
|
||||
$limit = isset($field['max']) ? max(0, (int) $field['max']) : 100;
|
||||
foreach (array_slice(array_values($value), 0, $limit ?: 100) as $item) {
|
||||
if (is_array($item)) {
|
||||
$items[] = $this->sanitize_object_fields($item, (array) ($field['fields'] ?? []));
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
if ($type === 'sortable') {
|
||||
if (!is_array($value)) {
|
||||
return [];
|
||||
}
|
||||
$items = [];
|
||||
foreach ((array) ($field['items'] ?? []) as $item_schema) {
|
||||
$item_key = sanitize_key((string) ($item_schema['key'] ?? ''));
|
||||
if ($item_key !== '' && array_key_exists($item_key, $value) && is_array($value[$item_key])) {
|
||||
$items[$item_key] = $this->sanitize_object_fields($value[$item_key], (array) ($item_schema['fields'] ?? []));
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
if ($type === 'link') {
|
||||
$value = is_array($value) ? $value : [];
|
||||
return [
|
||||
'url' => esc_url_raw((string) ($value['url'] ?? '')),
|
||||
'target' => in_array(($value['target'] ?? ''), ['', '_self', '_blank'], true) ? (string) ($value['target'] ?? '') : '',
|
||||
'text' => sanitize_text_field((string) ($value['text'] ?? '')),
|
||||
];
|
||||
}
|
||||
|
||||
if ($type === 'checkbox' || !empty($field['multiple'])) {
|
||||
if (!is_array($value)) {
|
||||
return [];
|
||||
}
|
||||
return array_values(array_map(static function ($item) {
|
||||
return is_numeric($item) ? (int) $item : sanitize_key((string) $item);
|
||||
}, $value));
|
||||
}
|
||||
|
||||
if (in_array($type, ['switch', 'switcher'], true)) {
|
||||
return !empty($value);
|
||||
}
|
||||
if (in_array($type, ['number', 'slider', 'spinner'], true)) {
|
||||
return is_float($value) ? (float) $value : (int) $value;
|
||||
}
|
||||
if (in_array($type, ['media', 'upload', 'url'], true)) {
|
||||
return esc_url_raw((string) $value);
|
||||
}
|
||||
|
||||
return sanitize_text_field((string) $value);
|
||||
}
|
||||
|
||||
private function sanitize_object_fields($value, array $fields): array {
|
||||
if (!is_array($value)) {
|
||||
return [];
|
||||
}
|
||||
$clean = [];
|
||||
foreach ($fields as $field) {
|
||||
if (!is_array($field) || empty($field['key'])) {
|
||||
continue;
|
||||
}
|
||||
$key = (string) $field['key'];
|
||||
if (array_key_exists($key, $value)) {
|
||||
$clean[$key] = $this->sanitize_field_value($value[$key], $field);
|
||||
}
|
||||
}
|
||||
return $clean;
|
||||
}
|
||||
|
||||
private function validate_nested_field(array $field, $value, string $path): array {
|
||||
$type = (string) ($field['type'] ?? 'text');
|
||||
$errors = [];
|
||||
if (in_array($type, ['fieldset', 'accordion'], true) && is_array($value)) {
|
||||
$errors = $this->validate_object_fields((array) ($field['fields'] ?? []), $value, $path);
|
||||
} elseif (in_array($type, ['group', 'repeater'], true) && is_array($value)) {
|
||||
foreach (array_values($value) as $index => $item) {
|
||||
if (is_array($item)) {
|
||||
$errors = array_merge($errors, $this->validate_object_fields((array) ($field['fields'] ?? []), $item, $path . '[' . $index . ']'));
|
||||
}
|
||||
}
|
||||
} elseif ($type === 'sortable' && is_array($value)) {
|
||||
$items = [];
|
||||
foreach ((array) ($field['items'] ?? []) as $item_schema) {
|
||||
if (is_array($item_schema) && !empty($item_schema['key'])) {
|
||||
$items[(string) $item_schema['key']] = $item_schema;
|
||||
}
|
||||
}
|
||||
foreach (array_keys($value) as $item_key) {
|
||||
if (!isset($items[$item_key])) {
|
||||
$errors[] = sprintf('%s has an invalid item', $path);
|
||||
} elseif (is_array($value[$item_key])) {
|
||||
$errors = array_merge($errors, $this->validate_object_fields((array) ($items[$item_key]['fields'] ?? []), $value[$item_key], $path . '.' . $item_key));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
private function validate_object_fields(array $fields, array $value, string $path): array {
|
||||
$errors = [];
|
||||
foreach ($fields as $field) {
|
||||
$key = (string) ($field['key'] ?? '');
|
||||
if ($key === '' || !array_key_exists($key, $value)) {
|
||||
continue;
|
||||
}
|
||||
$type = (string) ($field['type'] ?? 'text');
|
||||
if (in_array($type, ['select', 'radio', 'palette', 'image_select'], true) && !empty($field['options'])) {
|
||||
$allowed = array_column($field['options'], 'value');
|
||||
if (!empty($field['multiple']) && is_array($value[$key])) {
|
||||
foreach ($value[$key] as $item) {
|
||||
if (!in_array($item, $allowed, true)) {
|
||||
$errors[] = sprintf('%s.%s has an invalid value', $path, $key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} elseif (!in_array($value[$key], $allowed, true)) {
|
||||
$errors[] = sprintf('%s.%s has an invalid value', $path, $key);
|
||||
}
|
||||
}
|
||||
if (in_array($type, ['number', 'slider', 'spinner'], true)) {
|
||||
if (!is_numeric($value[$key])) {
|
||||
$errors[] = sprintf('%s.%s must be a number', $path, $key);
|
||||
} elseif (isset($field['min']) && $value[$key] < $field['min']) {
|
||||
$errors[] = sprintf('%s.%s must be at least %s', $path, $key, $field['min']);
|
||||
} elseif (isset($field['max']) && $value[$key] > $field['max']) {
|
||||
$errors[] = sprintf('%s.%s must be at most %s', $path, $key, $field['max']);
|
||||
}
|
||||
}
|
||||
$errors = array_merge($errors, $this->validate_nested_field($field, $value[$key], $path . '.' . $key));
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
private function infer_sanitize_type(string $field_type): string {
|
||||
$map = [
|
||||
'switch' => 'bool',
|
||||
'switcher' => 'bool',
|
||||
'input' => 'string',
|
||||
'text' => 'string',
|
||||
'textarea' => 'textarea',
|
||||
@@ -443,12 +638,19 @@ class SchemaProvider {
|
||||
'radio' => 'string',
|
||||
'checkbox' => 'array',
|
||||
'repeater' => 'array',
|
||||
'group' => 'array',
|
||||
'fieldset' => 'array',
|
||||
'accordion'=> 'array',
|
||||
'sortable' => 'array',
|
||||
'link' => 'array',
|
||||
'async_select' => 'array',
|
||||
'json' => 'array',
|
||||
// 自定义面板(验证码服务商等)按对象存储
|
||||
'custom' => 'array',
|
||||
'color' => 'string',
|
||||
'icon' => 'string',
|
||||
'media' => 'url',
|
||||
'upload' => 'url',
|
||||
'code' => 'code',
|
||||
];
|
||||
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Core;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class HomeModules {
|
||||
|
||||
const PREVIEW_QUERY_VAR = 'aksrt_preview';
|
||||
const PREVIEW_TRANSIENT_PREFIX = 'aksrt_home_preview_';
|
||||
|
||||
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_filter('query_vars', [$this, 'add_query_var']);
|
||||
add_action('rest_api_init', [$this, 'register_routes']);
|
||||
}
|
||||
|
||||
public function add_query_var(array $vars): array {
|
||||
$vars[] = self::PREVIEW_QUERY_VAR;
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function register_routes(): void {
|
||||
register_rest_route('aksrt/v1', '/preview/homepage', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'rest_create_preview'],
|
||||
'permission_callback' => function () {
|
||||
return current_user_can('manage_options');
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并校验模块列表(含预览/默认回退逻辑)。
|
||||
*/
|
||||
private static function resolve_modules(): array {
|
||||
$modules = [];
|
||||
|
||||
// 预览模式
|
||||
$preview_id = get_query_var(self::PREVIEW_QUERY_VAR);
|
||||
if ($preview_id && current_user_can('manage_options')) {
|
||||
$cached = get_transient(self::PREVIEW_TRANSIENT_PREFIX . sanitize_key((string) $preview_id));
|
||||
if (is_array($cached)) {
|
||||
$modules = $cached;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($modules) && class_exists('\AKSRT\Settings\Settings')) {
|
||||
$modules = \AKSRT\Settings\Settings::get_homepage_modules();
|
||||
}
|
||||
|
||||
// 校验:过滤无效条目,若全部无效则清空以触发默认模板回退
|
||||
if (!empty($modules)) {
|
||||
$allowed = self::allowed_types();
|
||||
$valid = array_filter($modules, function ($row) use ($allowed) {
|
||||
return is_array($row) && !empty($row['type']) && in_array($row['type'], $allowed, true);
|
||||
});
|
||||
$modules = empty($valid) ? [] : array_values($valid);
|
||||
}
|
||||
|
||||
if (empty($modules)) {
|
||||
$modules = self::get_default_template('resource_standard');
|
||||
}
|
||||
|
||||
return $modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染入口(首页 CMS 形态调用)。
|
||||
*
|
||||
* 优先级:
|
||||
* 1. ?aksrt_preview={id} 且管理员 → 读 transient 渲染
|
||||
* 2. option homepage_modules
|
||||
* 3. 若空 → 渲染默认模板(resource_standard)
|
||||
*/
|
||||
public static function render(): void {
|
||||
$modules = self::resolve_modules();
|
||||
|
||||
echo '<div class="home-modules">';
|
||||
foreach ($modules as $idx => $row) {
|
||||
if (!is_array($row) || empty($row['type'])) continue;
|
||||
$type = sanitize_key((string) $row['type']);
|
||||
$config = isset($row['config']) && is_array($row['config']) ? $row['config'] : [];
|
||||
|
||||
set_query_var('aksrt_module_config', $config);
|
||||
set_query_var('aksrt_module_index', $idx);
|
||||
|
||||
get_template_part('templates/parts/home-modules/module', $type);
|
||||
}
|
||||
// 清理
|
||||
set_query_var('aksrt_module_config', null);
|
||||
set_query_var('aksrt_module_index', null);
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 全屏模块类型(不受侧栏约束)。
|
||||
*/
|
||||
public static function fullbleed_types(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 分区渲染:仅渲染指定区域的模块。
|
||||
*
|
||||
* @param string $zone 'fullbleed' | 'body'
|
||||
*/
|
||||
public static function render_zone(string $zone = 'body'): void {
|
||||
$modules = self::resolve_modules();
|
||||
|
||||
$fullbleed = self::fullbleed_types();
|
||||
$filtered = array_filter($modules, function ($row) use ($fullbleed, $zone) {
|
||||
$type = $row['type'] ?? '';
|
||||
$is_fullbleed = in_array($type, $fullbleed, true);
|
||||
return $zone === 'fullbleed' ? $is_fullbleed : !$is_fullbleed;
|
||||
});
|
||||
|
||||
if (empty($filtered)) return;
|
||||
|
||||
$wrapper_class = $zone === 'fullbleed' ? 'home-modules home-modules--fullbleed' : 'home-modules';
|
||||
echo '<div class="' . esc_attr($wrapper_class) . '">';
|
||||
foreach ($filtered as $idx => $row) {
|
||||
if (!is_array($row) || empty($row['type'])) continue;
|
||||
$type = sanitize_key((string) $row['type']);
|
||||
$config = isset($row['config']) && is_array($row['config']) ? $row['config'] : [];
|
||||
|
||||
set_query_var('aksrt_module_config', $config);
|
||||
set_query_var('aksrt_module_index', $idx);
|
||||
|
||||
get_template_part('templates/parts/home-modules/module', $type);
|
||||
}
|
||||
set_query_var('aksrt_module_config', null);
|
||||
set_query_var('aksrt_module_index', null);
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建预览会话。
|
||||
*/
|
||||
public function rest_create_preview(\WP_REST_Request $request) {
|
||||
$params = $request->get_json_params();
|
||||
if (!is_array($params)) $params = $request->get_params();
|
||||
$modules = isset($params['modules']) && is_array($params['modules']) ? $params['modules'] : [];
|
||||
|
||||
$clean = [];
|
||||
$allowed_types = self::allowed_types();
|
||||
foreach ($modules as $row) {
|
||||
if (!is_array($row)) continue;
|
||||
$type = isset($row['type']) ? sanitize_key((string) $row['type']) : '';
|
||||
if (!in_array($type, $allowed_types, true)) continue;
|
||||
$clean[] = [
|
||||
'type' => $type,
|
||||
'config' => isset($row['config']) && is_array($row['config']) ? $row['config'] : [],
|
||||
];
|
||||
}
|
||||
|
||||
$id = wp_generate_uuid4();
|
||||
set_transient(self::PREVIEW_TRANSIENT_PREFIX . $id, $clean, 30 * MINUTE_IN_SECONDS);
|
||||
|
||||
return new \WP_REST_Response([
|
||||
'preview_id' => $id,
|
||||
'preview_url' => add_query_arg(self::PREVIEW_QUERY_VAR, $id, home_url('/')),
|
||||
'expires_in' => 30 * MINUTE_IN_SECONDS,
|
||||
], 200);
|
||||
}
|
||||
|
||||
public static function allowed_types(): array {
|
||||
return [
|
||||
'banner', 'category', 'tabs', 'ranking',
|
||||
'recent', 'tag_cloud', 'friend_links', 'html', 'hotspot',
|
||||
'category_recommend_cards',
|
||||
// Phase 5: 运营型模块
|
||||
'text_title', 'button_group', 'spacing', 'faq',
|
||||
'data_grid', 'highlight_cards', 'partner_marquee', 'product_showcase',
|
||||
// Widget-区等价模块(补齐 zibll widget-user / widget-more 能力)
|
||||
'user', 'more',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认模板(3 套预设)。
|
||||
*/
|
||||
public static function get_default_template(string $name): array {
|
||||
switch ($name) {
|
||||
case 'news':
|
||||
return [
|
||||
['type' => 'banner', 'config' => []],
|
||||
['type' => 'category', 'config' => ['count' => 8, 'columns' => 4]],
|
||||
['type' => 'category', 'config' => ['count' => 8, 'columns' => 4]],
|
||||
['type' => 'tag_cloud', 'config' => ['limit' => 30]],
|
||||
];
|
||||
|
||||
case 'mixed':
|
||||
return [
|
||||
['type' => 'recent', 'config' => ['count' => 6, 'layout' => 'card']],
|
||||
['type' => 'category', 'config' => ['count' => 6, 'columns' => 3]],
|
||||
['type' => 'category', 'config' => ['count' => 6, 'columns' => 3]],
|
||||
];
|
||||
|
||||
case 'resource_standard':
|
||||
default:
|
||||
return [
|
||||
['type' => 'tabs', 'config' => ['count' => 8]],
|
||||
['type' => 'recent', 'config' => ['count' => 8, 'layout' => 'card']],
|
||||
['type' => 'ranking', 'config' => ['metric' => 'downloads', 'count' => 6]],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,6 @@ trait ScriptsPart1b {
|
||||
$enqueue_module('layout');
|
||||
$enqueue_module('footer');
|
||||
$enqueue_module('floating-buttons');
|
||||
$enqueue_module('theme-background');
|
||||
$enqueue_module('enhancements');
|
||||
|
||||
// 4a-b. 全局补充样式(骨架屏、空状态、分页、加载指示器)
|
||||
@@ -681,24 +680,6 @@ trait ScriptsPart1b {
|
||||
);
|
||||
}
|
||||
|
||||
// Legacy freeform CMS home modules: only for non-fixed CMS archive previews.
|
||||
$is_cms_home = ($is_home_or_archive && $is_cms_mode) && !$is_fixed_homepage;
|
||||
// 管理员预览模式(?aksrt_preview=...):无论站点风格都允许加载模块系统资源
|
||||
$is_preview = is_user_logged_in() && current_user_can('manage_options') && isset($_GET['aksrt_preview']);
|
||||
|
||||
if ($is_cms_home || $is_preview) {
|
||||
$enqueue_module('home-modules');
|
||||
$enqueue_module('post-card'); // 模块内复用
|
||||
|
||||
wp_enqueue_script(
|
||||
'aksrt-cms-home-modules',
|
||||
$base_uri . '/assets/js/cms-home-modules.js',
|
||||
['loader_js'],
|
||||
$version,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if ($is_fixed_homepage) {
|
||||
$enqueue_module('post-card');
|
||||
$homepage_style_deps = ['_main', 'aksrt-layout', 'aksrt-post-card'];
|
||||
|
||||
@@ -69,7 +69,6 @@ class Setup {
|
||||
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'])) {
|
||||
|
||||
+43
-9
@@ -94,10 +94,10 @@ class TermMeta {
|
||||
$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\Settings')) {
|
||||
$cover_url = \AKSRT\Settings\Settings::get_category_cover_url($term->term_id);
|
||||
}
|
||||
$cover_url = isset($saved['cover_url']) ? esc_url_raw((string) $saved['cover_url']) : '';
|
||||
if ($cover_url === '' && !empty($global['cover_enabled'])) {
|
||||
$cover_url = esc_url_raw((string) ($global['default_cover'] ?? ''));
|
||||
}
|
||||
|
||||
$description = isset($saved['description']) ? sanitize_textarea_field((string) $saved['description']) : '';
|
||||
if ($description === '') {
|
||||
@@ -110,7 +110,15 @@ class TermMeta {
|
||||
'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']),
|
||||
'filter_bar' => !empty($global['filter_bar']),
|
||||
'post_count' => !empty($global['post_count']),
|
||||
'orderby_enabled' => !empty($global['orderby_enabled']),
|
||||
'orderby' => sanitize_key((string) ($global['orderby'] ?? 'date')),
|
||||
'orderby_option'=> is_array($global['orderby_option'] ?? null) ? $global['orderby_option'] : [],
|
||||
'ajax_category_enabled' => !empty($global['ajax_category_enabled']),
|
||||
'ajax_tag_enabled' => !empty($global['ajax_tag_enabled']),
|
||||
'ajax_category' => is_array($global['ajax_category'] ?? null) ? $global['ajax_category'] : [],
|
||||
'ajax_tag' => is_array($global['ajax_tag'] ?? null) ? $global['ajax_tag'] : [],
|
||||
'per_page' => max(4, min(48, (int) ($global['per_page'] ?? 12))),
|
||||
'cover_url' => $cover_url,
|
||||
'description' => $description,
|
||||
@@ -153,10 +161,36 @@ class TermMeta {
|
||||
}
|
||||
|
||||
$settings = self::resolve_archive_settings($term);
|
||||
if (!empty($settings['per_page'])) {
|
||||
$query->set('posts_per_page', (int) $settings['per_page']);
|
||||
}
|
||||
}
|
||||
if (!empty($settings['per_page'])) {
|
||||
$query->set('posts_per_page', (int) $settings['per_page']);
|
||||
}
|
||||
|
||||
$tax_query = is_array($query->get('tax_query')) ? $query->get('tax_query') : [];
|
||||
$filter_cat = isset($_GET['aksrt_filter_cat']) ? absint(wp_unslash($_GET['aksrt_filter_cat'])) : 0;
|
||||
$filter_tag = isset($_GET['aksrt_filter_tag']) ? absint(wp_unslash($_GET['aksrt_filter_tag'])) : 0;
|
||||
if ($filter_cat > 0) {
|
||||
$tax_query[] = ['taxonomy' => 'category', 'field' => 'term_id', 'terms' => [$filter_cat]];
|
||||
}
|
||||
if ($filter_tag > 0) {
|
||||
$tax_query[] = ['taxonomy' => 'post_tag', 'field' => 'term_id', 'terms' => [$filter_tag]];
|
||||
}
|
||||
if ($tax_query !== []) {
|
||||
$query->set('tax_query', $tax_query);
|
||||
}
|
||||
|
||||
$orderby = isset($_GET['orderby']) ? sanitize_key(wp_unslash((string) $_GET['orderby'])) : 'date';
|
||||
$allowed = class_exists('\AKSRT\Settings\Settings')
|
||||
? \AKSRT\Settings\Settings::homepage_allowed_sorts()
|
||||
: ['modified', 'date', 'views', 'like', 'comment_count', 'favorite', 'rand'];
|
||||
if (!in_array($orderby, $allowed, true)) {
|
||||
$orderby = 'date';
|
||||
}
|
||||
if (function_exists('aksrt_homepage_apply_named_sort_args')) {
|
||||
foreach (aksrt_homepage_apply_named_sort_args([], $orderby) as $key => $value) {
|
||||
$query->set($key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render fields for "Add new term" form.
|
||||
|
||||
@@ -50,7 +50,7 @@ trait DisplayPageGroups {
|
||||
* 获取顶部多功能组件设置
|
||||
*/
|
||||
public static function get_header_slider_settings(): array {
|
||||
$s = self::get_all();
|
||||
$s = self::read_domain('page_display', 'header-components');
|
||||
$allowed_page_types = [
|
||||
'home',
|
||||
'category',
|
||||
@@ -59,10 +59,6 @@ trait DisplayPageGroups {
|
||||
'single',
|
||||
'search',
|
||||
'page',
|
||||
'topics',
|
||||
'forum_home',
|
||||
'forum_plate',
|
||||
'forum_post',
|
||||
];
|
||||
$show_on = isset($s['header_slider_show']) ? (array) $s['header_slider_show'] : [];
|
||||
$show_on = array_values(array_intersect(
|
||||
@@ -218,7 +214,6 @@ trait DisplayPageGroups {
|
||||
'tag' => self::get_tag_page_settings(),
|
||||
'user_home' => self::get_user_home_settings(),
|
||||
'other' => self::get_other_page_settings(),
|
||||
'background' => self::get_global_background_settings(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -9,39 +9,6 @@ defined('ABSPATH') || exit;
|
||||
|
||||
class DisplayRuntime {
|
||||
|
||||
/** 获取Banner列表 */
|
||||
public static function get_banners(string $position = ''): array {
|
||||
$banners = get_option('aksrt_banners', []) ?: [];
|
||||
|
||||
if ($position) {
|
||||
$banners = array_filter($banners, fn($banner) => ($banner['position'] ?? '') === $position);
|
||||
}
|
||||
|
||||
usort($banners, fn($a, $b) => ($a['sort_order'] ?? 0) - ($b['sort_order'] ?? 0));
|
||||
|
||||
return array_filter($banners, fn($banner) => ($banner['status'] ?? 'enabled') === 'enabled');
|
||||
}
|
||||
|
||||
/** 保存Banner */
|
||||
public static function save_banner(array $data): int {
|
||||
$banners = get_option('aksrt_banners', []);
|
||||
if (!empty($data['id'])) {
|
||||
$banners[$data['id']] = $data;
|
||||
} else {
|
||||
$data['id'] = uniqid('banner_');
|
||||
$banners[] = $data;
|
||||
}
|
||||
update_option('aksrt_banners', $banners);
|
||||
return $data['id'];
|
||||
}
|
||||
|
||||
/** 删除Banner */
|
||||
public static function delete_banner(string $id): void {
|
||||
$banners = get_option('aksrt_banners', []);
|
||||
unset($banners[$id]);
|
||||
update_option('aksrt_banners', array_values($banners));
|
||||
}
|
||||
|
||||
/** 获取友链列表 */
|
||||
public static function get_friend_links(): array {
|
||||
$links = get_option('aksrt_friend_links', []) ?: [];
|
||||
@@ -56,83 +23,4 @@ class DisplayRuntime {
|
||||
update_option('aksrt_friend_links', $links);
|
||||
}
|
||||
|
||||
public static function homepage_module_banner_enabled(): bool {
|
||||
$settings = Settings::get_all();
|
||||
return !isset($settings['homepage_module_banner_enabled']) || !empty($settings['homepage_module_banner_enabled']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Global slider configuration shared by Hero and Banner.
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function get_slider_config(): array {
|
||||
$settings = Settings::get_all();
|
||||
$config = is_array($settings['header_slider_option'] ?? null) ? $settings['header_slider_option'] : [];
|
||||
|
||||
return [
|
||||
'effect' => in_array($config['effect'] ?? '', ['fade', 'slide', 'cube', 'coverflow', 'flip'], true) ? $config['effect'] : 'fade',
|
||||
'direction' => in_array($config['direction'] ?? '', ['horizontal', 'vertical'], true) ? $config['direction'] : 'horizontal',
|
||||
'loop' => !isset($config['loop']) || !empty($config['loop']),
|
||||
'autoplay_interval' => max(0, min(20000, (int) ($config['interval'] ?? 6) * 1000)),
|
||||
'pagination' => !isset($config['pagination']) || !empty($config['pagination']),
|
||||
'navigation' => !isset($config['navigation']) || !empty($config['navigation']),
|
||||
'button' => !isset($config['button']) || !empty($config['button']),
|
||||
'auto_height' => !empty($config['auto_height']),
|
||||
'pc_height' => isset($config['pc_height']) && $config['pc_height'] !== '' ? sanitize_text_field($config['pc_height']) : '',
|
||||
'mobile_height' => isset($config['m_height']) && $config['m_height'] !== '' ? sanitize_text_field($config['m_height']) : '',
|
||||
'max_height' => isset($config['max_height']) && $config['max_height'] !== '' ? sanitize_text_field($config['max_height']) : '',
|
||||
'min_height' => isset($config['min_height']) && $config['min_height'] !== '' ? sanitize_text_field($config['min_height']) : '',
|
||||
'scale_height' => !empty($config['scale_height']),
|
||||
'scale' => (int) ($config['scale'] ?? 40),
|
||||
'speed' => max(0, min(3000, (int) ($config['speed'] ?? 0))),
|
||||
'space_between' => max(0, min(500, (int) ($config['spacebetween'] ?? 0))),
|
||||
'nav_color' => sanitize_hex_color((string) ($settings['header_slider_nav_color'] ?? '')) ?: '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全局背景设置
|
||||
*/
|
||||
public static function get_global_background_settings(): array {
|
||||
$settings = Settings::get_all();
|
||||
|
||||
$valid_sizes = ['cover', 'contain', 'auto', '100%', '100% 100%'];
|
||||
$size = sanitize_text_field((string) ($settings['bg_size'] ?? '100%'));
|
||||
if (!in_array($size, $valid_sizes, true)) {
|
||||
$size = '100%';
|
||||
}
|
||||
|
||||
$valid_positions = ['center center', 'top center', 'bottom center'];
|
||||
$position = sanitize_text_field((string) ($settings['bg_position'] ?? 'center center'));
|
||||
if (!in_array($position, $valid_positions, true)) {
|
||||
$position = 'center center';
|
||||
}
|
||||
|
||||
$valid_repeats = ['no-repeat', 'repeat', 'repeat-x', 'repeat-y'];
|
||||
$repeat = sanitize_key((string) ($settings['bg_repeat'] ?? 'no-repeat'));
|
||||
if (!in_array($repeat, $valid_repeats, true)) {
|
||||
$repeat = 'no-repeat';
|
||||
}
|
||||
|
||||
$attachment = sanitize_key((string) ($settings['bg_attachment'] ?? 'fixed'));
|
||||
if (!in_array($attachment, ['fixed', 'scroll'], true)) {
|
||||
$attachment = 'fixed';
|
||||
}
|
||||
|
||||
return [
|
||||
'bg_color' => sanitize_hex_color((string) ($settings['bg_color'] ?? '')) ?: '',
|
||||
'bg_image' => esc_url_raw((string) ($settings['bg_image'] ?? '')),
|
||||
'bg_size' => $size,
|
||||
'bg_position' => $position,
|
||||
'bg_repeat' => $repeat,
|
||||
'bg_attachment' => $attachment,
|
||||
'bg_dark_color' => sanitize_hex_color((string) ($settings['bg_dark_color'] ?? '')) ?: '',
|
||||
'bg_dark_image' => esc_url_raw((string) ($settings['bg_dark_image'] ?? '')),
|
||||
'bg_overlay' => !empty($settings['bg_overlay']),
|
||||
'bg_overlay_color' => sanitize_text_field((string) ($settings['bg_overlay_color'] ?? 'rgba(0, 0, 0, 0.3)')),
|
||||
'page_bg_color' => sanitize_hex_color((string) ($settings['page_bg_color'] ?? '')) ?: '',
|
||||
'page_bg_image' => esc_url_raw((string) ($settings['page_bg_image'] ?? '')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,13 +27,6 @@ trait DomainGroups {
|
||||
Integration::update_wechat_settings($settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全局背景设置
|
||||
*/
|
||||
public static function get_global_background_settings(): array {
|
||||
return DisplayRuntime::get_global_background_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取页脚布局模板
|
||||
*/
|
||||
|
||||
+78
-81
@@ -14,16 +14,21 @@ trait FacadeBulk {
|
||||
* @return array{custom:bool,bg_custom:string,bg:string,color_custom:string}
|
||||
*/
|
||||
public static function get_header_theme_config(): array {
|
||||
$s = self::get_all();
|
||||
$s = self::read_domain('page_display', 'header-nav');
|
||||
|
||||
return [
|
||||
'custom' => !empty($s['header_theme_custom']),
|
||||
'bg_custom' => sanitize_hex_color((string) ($s['header_theme_bg_custom'] ?? '')) ?: '',
|
||||
'bg' => sanitize_hex_color((string) ($s['header_theme_bg'] ?? '')) ?: '',
|
||||
'bg' => self::normalize_palette_hex((string) ($s['header_theme_bg'] ?? '')),
|
||||
'color_custom' => sanitize_hex_color((string) ($s['header_theme_color_custom'] ?? '')) ?: '',
|
||||
];
|
||||
}
|
||||
|
||||
private static function normalize_palette_hex(string $value): string {
|
||||
$value = ltrim(trim($value), '#');
|
||||
return preg_match('/^[0-9a-f]{6}$/i', $value) ? '#' . strtolower($value) : '';
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@@ -31,36 +36,32 @@ trait FacadeBulk {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_category_page_settings(): array {
|
||||
$settings = self::get_all();
|
||||
$header_style = sanitize_key((string) ($settings['category_header_style'] ?? 'cover'));
|
||||
$list_mode = sanitize_key((string) ($settings['category_list_mode'] ?? 'grid'));
|
||||
$per_page = (int) ($settings['category_per_page'] ?? 12);
|
||||
$orderby = sanitize_key((string) ($settings['category_orderby'] ?? 'modified'));
|
||||
public static function get_category_page_settings(): array {
|
||||
$settings = self::read_domain('page_display', 'category');
|
||||
$orderby_option = self::normalize_page_orderby_option($settings['cat_orderby_option'] ?? []);
|
||||
$orderby = sanitize_key((string) (($orderby_option['lists'][0] ?? 'modified')));
|
||||
$ajax_category_enabled = !empty($settings['ajax_list_cat_cat']);
|
||||
$ajax_tag_enabled = !empty($settings['ajax_list_cat_tag']);
|
||||
$orderby_enabled = !empty($settings['cat_orderby_s']);
|
||||
$post_count = !empty($settings['cat_post_count_s']);
|
||||
|
||||
if (!in_array($header_style, ['cover', 'simple', 'none'], true)) {
|
||||
$header_style = 'cover';
|
||||
}
|
||||
|
||||
if (!in_array($list_mode, ['grid', 'list', 'compact'], true)) {
|
||||
$list_mode = 'grid';
|
||||
}
|
||||
|
||||
if (!in_array($orderby, ['modified', 'date', 'views', 'comments', 'likes'], true)) {
|
||||
$orderby = 'modified';
|
||||
}
|
||||
|
||||
return [
|
||||
'header_style' => $header_style,
|
||||
'cover_enabled' => !isset($settings['category_cover_enabled']) || !empty($settings['category_cover_enabled']),
|
||||
'desc_enabled' => !isset($settings['category_desc_enabled']) || !empty($settings['category_desc_enabled']),
|
||||
'default_cover' => esc_url_raw((string) ($settings['category_default_cover'] ?? '')),
|
||||
'list_mode' => $list_mode,
|
||||
'filter_bar' => !isset($settings['category_filter_bar']) || !empty($settings['category_filter_bar']),
|
||||
'sidebar' => !isset($settings['category_sidebar']) || !empty($settings['category_sidebar']),
|
||||
'per_page' => max(4, min(48, $per_page)),
|
||||
'post_count' => !empty($settings['category_post_count']),
|
||||
'orderby' => $orderby,
|
||||
return [
|
||||
'header_style' => !empty($settings['page_cover_cat_s']) ? 'cover' : 'simple',
|
||||
'cover_enabled' => !empty($settings['page_cover_cat_s']),
|
||||
'desc_enabled' => true,
|
||||
'default_cover' => esc_url_raw((string) ($settings['cat_default_cover'] ?? '')),
|
||||
'list_mode' => 'grid',
|
||||
'filter_bar' => $ajax_category_enabled || $ajax_tag_enabled || $orderby_enabled || $post_count,
|
||||
'sidebar' => true,
|
||||
'per_page' => max(1, (int) get_option('posts_per_page', 10)),
|
||||
'post_count' => $post_count,
|
||||
'orderby' => $orderby,
|
||||
'orderby_enabled'=> $orderby_enabled,
|
||||
'orderby_option' => $orderby_option,
|
||||
'ajax_category_enabled' => $ajax_category_enabled,
|
||||
'ajax_tag_enabled' => $ajax_tag_enabled,
|
||||
'ajax_category' => is_array($settings['ajax_list_option_cat_cat'] ?? null) ? $settings['ajax_list_option_cat_cat'] : [],
|
||||
'ajax_tag' => is_array($settings['ajax_list_option_cat_tag'] ?? null) ? $settings['ajax_list_option_cat_tag'] : [],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -71,68 +72,64 @@ trait FacadeBulk {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_tag_page_settings(): array {
|
||||
$settings = self::get_all();
|
||||
$header_style = sanitize_key((string) ($settings['tag_header_style'] ?? 'simple'));
|
||||
$list_mode = sanitize_key((string) ($settings['tag_list_mode'] ?? 'grid'));
|
||||
$per_page = (int) ($settings['tag_per_page'] ?? 12);
|
||||
$orderby = sanitize_key((string) ($settings['tag_orderby'] ?? 'count'));
|
||||
public static function get_tag_page_settings(): array {
|
||||
$settings = self::read_domain('page_display', 'tag');
|
||||
$orderby_option = self::normalize_page_orderby_option($settings['tag_orderby_option'] ?? []);
|
||||
$orderby = sanitize_key((string) (($orderby_option['lists'][0] ?? 'modified')));
|
||||
$ajax_category_enabled = !empty($settings['ajax_list_tag_cat']);
|
||||
$ajax_tag_enabled = !empty($settings['ajax_list_tag_tag']);
|
||||
$orderby_enabled = !empty($settings['tag_orderby_s']);
|
||||
$post_count = !empty($settings['tag_post_count_s']);
|
||||
|
||||
if (!in_array($header_style, ['cover', 'simple', 'none'], true)) {
|
||||
$header_style = 'simple';
|
||||
}
|
||||
|
||||
if (!in_array($list_mode, ['grid', 'list'], true)) {
|
||||
$list_mode = 'grid';
|
||||
}
|
||||
|
||||
if (!in_array($orderby, ['count', 'name', 'modified'], true)) {
|
||||
$orderby = 'count';
|
||||
}
|
||||
|
||||
return [
|
||||
'header_style' => $header_style,
|
||||
return [
|
||||
'header_style' => !empty($settings['page_cover_tag_s']) ? 'cover' : 'simple',
|
||||
'cover_enabled' => !empty($settings['page_cover_tag_s']),
|
||||
'default_cover' => esc_url_raw((string) ($settings['tag_default_cover'] ?? '')),
|
||||
'list_mode' => $list_mode,
|
||||
'sidebar' => !isset($settings['tag_sidebar']) || !empty($settings['tag_sidebar']),
|
||||
'per_page' => max(4, min(48, $per_page)),
|
||||
'post_count' => !empty($settings['tag_post_count']),
|
||||
'orderby' => $orderby,
|
||||
'list_mode' => 'grid',
|
||||
'sidebar' => true,
|
||||
'per_page' => max(1, (int) get_option('posts_per_page', 10)),
|
||||
'post_count' => $post_count,
|
||||
'filter_bar' => $ajax_category_enabled || $ajax_tag_enabled || $orderby_enabled || $post_count,
|
||||
'orderby' => $orderby,
|
||||
'orderby_enabled'=> $orderby_enabled,
|
||||
'orderby_option' => $orderby_option,
|
||||
'ajax_category_enabled' => $ajax_category_enabled,
|
||||
'ajax_tag_enabled' => $ajax_tag_enabled,
|
||||
'ajax_category' => is_array($settings['ajax_list_option_tag_cat'] ?? null) ? $settings['ajax_list_option_tag_cat'] : [],
|
||||
'ajax_tag' => is_array($settings['ajax_list_option_tag_tag'] ?? null) ? $settings['ajax_list_option_tag_tag'] : [],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Misc page display settings (404, search, author).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_other_page_settings(): array {
|
||||
$settings = self::get_all();
|
||||
$message = trim((string) ($settings['page_404_message'] ?? ''));
|
||||
$search_per_page = max(4, min(48, (int) ($settings['page_search_per_page'] ?? 12)));
|
||||
$author_per_page = max(4, min(48, (int) ($settings['page_author_posts_per_page'] ?? 12)));
|
||||
|
||||
$header_style = sanitize_key((string) ($settings['page_header_style'] ?? '1'));
|
||||
/** @return array{header_style:string,header_cover_img:string} */
|
||||
public static function get_other_page_settings(): array {
|
||||
$settings = self::read_domain('page_display', 'other');
|
||||
$header_style = sanitize_key((string) ($settings['page_header_style'] ?? ''));
|
||||
if (!in_array($header_style, ['', '1', '2', '3'], true)) {
|
||||
$header_style = '1';
|
||||
}
|
||||
$header_style = '';
|
||||
}
|
||||
|
||||
return [
|
||||
'header_style' => $header_style,
|
||||
'header_cover_img' => esc_url_raw((string) ($settings['page_header_cover_img'] ?? '')),
|
||||
'page_404_message' => $message !== '' ? $message : __('页面未找到', 'aksrt'),
|
||||
'page_404_search' => !isset($settings['page_404_search']) || !empty($settings['page_404_search']),
|
||||
'page_404_popular' => !isset($settings['page_404_popular']) || !empty($settings['page_404_popular']),
|
||||
'page_404_back' => !isset($settings['page_404_back']) || !empty($settings['page_404_back']),
|
||||
'page_search_sidebar' => !isset($settings['page_search_sidebar']) || !empty($settings['page_search_sidebar']),
|
||||
'page_search_per_page' => $search_per_page,
|
||||
'page_author_cover' => !isset($settings['page_author_cover']) || !empty($settings['page_author_cover']),
|
||||
'page_author_sidebar' => !isset($settings['page_author_sidebar']) || !empty($settings['page_author_sidebar']),
|
||||
'page_author_posts_per_page' => $author_per_page,
|
||||
'header_style' => $header_style,
|
||||
'header_cover_img' => esc_url_raw((string) ($settings['page_header_cover_img'] ?? '')),
|
||||
];
|
||||
}
|
||||
|
||||
/** @param mixed $value */
|
||||
private static function normalize_page_orderby_option($value): array {
|
||||
$option = is_array($value) ? $value : [];
|
||||
$allowed = self::homepage_allowed_sorts();
|
||||
$lists = is_array($option['lists'] ?? null) ? $option['lists'] : [];
|
||||
$lists = array_values(array_intersect(array_map('sanitize_key', $lists), $allowed));
|
||||
if ($lists === []) {
|
||||
$lists = ['modified', 'views', 'like', 'comment_count'];
|
||||
}
|
||||
return [
|
||||
'lists' => $lists,
|
||||
'dropdown' => !empty($option['dropdown']),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Share and poster settings.
|
||||
*
|
||||
|
||||
+69
-131
@@ -9,65 +9,19 @@ defined('ABSPATH') || exit;
|
||||
|
||||
class HomeCms {
|
||||
|
||||
public static function get_footer_settings(): array {
|
||||
$defaults = [
|
||||
'contacts' => [],
|
||||
'quick_links' => [],
|
||||
'more_links' => [],
|
||||
'hot_categories' => 5,
|
||||
'copyright_text' => '',
|
||||
'layout_template' => 'template_1',
|
||||
];
|
||||
$footer = get_option('aksrt_footer_settings', []);
|
||||
$merged = array_merge($defaults, is_array($footer) ? $footer : []);
|
||||
public static function get_footer_settings(): array {
|
||||
return Settings::read_domain('page_display', 'footer');
|
||||
}
|
||||
|
||||
$s = Settings::get_all();
|
||||
$bridge_keys = [
|
||||
'footer_columns', 'footer_copyright', 'footer_icp', 'footer_police',
|
||||
'footer_theme_custom', 'footer_theme_bg_custom', 'footer_theme_bg', 'footer_theme_color_custom',
|
||||
'footer_contact_wechat_img', 'footer_contact_qq', 'footer_contact_weibo', 'footer_contact_email',
|
||||
];
|
||||
foreach ($bridge_keys as $key) {
|
||||
if (isset($s[$key])) {
|
||||
$merged[$key] = $s[$key];
|
||||
}
|
||||
}
|
||||
return $merged;
|
||||
}
|
||||
|
||||
public static function get_footer_contacts(): array {
|
||||
$footer = self::get_footer_settings();
|
||||
return array_filter($footer['contacts'], fn($c) => !empty($c['enabled']));
|
||||
}
|
||||
|
||||
public static function get_quick_links(): array {
|
||||
$footer = self::get_footer_settings();
|
||||
$links = array_filter($footer['quick_links'], fn($l) => !empty($l['enabled']));
|
||||
usort($links, fn($a, $b) => ($a['sort_order'] ?? 0) <=> ($b['sort_order'] ?? 0));
|
||||
return array_values($links);
|
||||
}
|
||||
|
||||
public static function get_more_links(): array {
|
||||
$footer = self::get_footer_settings();
|
||||
$links = array_filter($footer['more_links'], fn($l) => !empty($l['enabled']));
|
||||
usort($links, fn($a, $b) => ($a['sort_order'] ?? 0) <=> ($b['sort_order'] ?? 0));
|
||||
return array_values($links);
|
||||
}
|
||||
|
||||
public static function get_hot_categories_count(): int {
|
||||
$footer = self::get_footer_settings();
|
||||
return max(1, min(20, (int) ($footer['hot_categories'] ?? 5)));
|
||||
}
|
||||
|
||||
public static function get_copyright_text(): string {
|
||||
$footer = self::get_footer_settings();
|
||||
return $footer['copyright_text'] ?: '';
|
||||
}
|
||||
public static function get_footer_template(): string {
|
||||
$template = sanitize_key((string) (self::get_footer_settings()['fcode_template'] ?? 'template_1'));
|
||||
return $template === 'template_1' ? $template : 'template_1';
|
||||
}
|
||||
|
||||
public static function get_footer_brand_settings(): array {
|
||||
$f = self::get_footer_settings();
|
||||
return [
|
||||
'm_s' => is_array($f['footer_t1_m_s'] ?? null) ? $f['footer_t1_m_s'] : ['pc', 'mobile'],
|
||||
'm_s' => !empty($f['footer_t1_m_s']) ? ['pc', 'mobile'] : ['pc'],
|
||||
'img' => (string) ($f['footer_t1_img'] ?? ''),
|
||||
'img_dark' => (string) ($f['footer_t1_img_dark'] ?? ''),
|
||||
'title' => (string) ($f['footer_t1_t'] ?? ''),
|
||||
@@ -78,7 +32,7 @@ class HomeCms {
|
||||
public static function get_footer_contact_extended_settings(): array {
|
||||
$f = self::get_footer_settings();
|
||||
return [
|
||||
'm_s' => is_array($f['footer_contact_m_s'] ?? null) ? $f['footer_contact_m_s'] : ['pc', 'mobile'],
|
||||
'm_s' => !empty($f['footer_contact_m_s']) ? ['pc', 'mobile'] : ['pc'],
|
||||
'wechat_img' => (string) ($f['footer_contact_wechat_img'] ?? ''),
|
||||
'qq' => (string) ($f['footer_contact_qq'] ?? ''),
|
||||
'weibo' => (string) ($f['footer_contact_weibo'] ?? ''),
|
||||
@@ -91,26 +45,10 @@ class HomeCms {
|
||||
|
||||
public static function get_footer_mini_img_settings(): array {
|
||||
$f = self::get_footer_settings();
|
||||
$raw_items = is_array($f['footer_mini_img'] ?? null) ? $f['footer_mini_img'] : [];
|
||||
$items = [];
|
||||
foreach ($raw_items as $item) {
|
||||
if (!is_array($item)) {
|
||||
continue;
|
||||
}
|
||||
$img = (string) ($item['img'] ?? $item['image'] ?? '');
|
||||
if ($img === '') {
|
||||
continue;
|
||||
}
|
||||
$items[] = [
|
||||
'img' => $img,
|
||||
'image' => $img,
|
||||
'text' => (string) ($item['text'] ?? $item['label'] ?? ''),
|
||||
'label' => (string) ($item['label'] ?? $item['text'] ?? ''),
|
||||
];
|
||||
}
|
||||
return [
|
||||
'm_s' => is_array($f['footer_mini_img_m_s'] ?? null) ? $f['footer_mini_img_m_s'] : ['pc', 'mobile'],
|
||||
'items' => $items,
|
||||
'm_s' => !empty($f['footer_mini_img_m_s']) ? ['pc', 'mobile'] : ['pc'],
|
||||
'items' => self::normalize_footer_images($f['footer_mini_img'] ?? []),
|
||||
'items_2' => self::normalize_footer_images($f['footer_mini_img_2'] ?? []),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -120,11 +58,11 @@ class HomeCms {
|
||||
}
|
||||
|
||||
public static function get_footer_theme_config(): array {
|
||||
$s = Settings::get_all();
|
||||
$s = self::get_footer_settings();
|
||||
return [
|
||||
'custom' => !empty($s['footer_theme_custom']),
|
||||
'bg_custom' => sanitize_hex_color((string) ($s['footer_theme_bg_custom'] ?? '')) ?: '',
|
||||
'bg' => sanitize_hex_color((string) ($s['footer_theme_bg'] ?? '')) ?: '',
|
||||
'bg' => self::normalize_palette_hex((string) ($s['footer_theme_bg'] ?? '')),
|
||||
'color_custom' => sanitize_hex_color((string) ($s['footer_theme_color_custom'] ?? '')) ?: '',
|
||||
];
|
||||
}
|
||||
@@ -149,7 +87,7 @@ class HomeCms {
|
||||
}
|
||||
|
||||
public static function homepage_allowed_sorts(): array {
|
||||
return ['modified', 'date', 'views', 'likes', 'comments', 'favorites', 'downloads', 'rand'];
|
||||
return ['modified', 'date', 'views', 'like', 'comment_count', 'favorite', 'rand'];
|
||||
}
|
||||
|
||||
public static function homepage_sort_labels(): array {
|
||||
@@ -157,16 +95,15 @@ class HomeCms {
|
||||
'modified' => __('更新', 'aksrt'),
|
||||
'date' => __('发布', 'aksrt'),
|
||||
'views' => __('浏览', 'aksrt'),
|
||||
'likes' => __('点赞', 'aksrt'),
|
||||
'comments' => __('评论', 'aksrt'),
|
||||
'favorites' => __('收藏', 'aksrt'),
|
||||
'downloads' => __('下载', 'aksrt'),
|
||||
'like' => __('点赞', 'aksrt'),
|
||||
'comment_count' => __('评论', 'aksrt'),
|
||||
'favorite' => __('收藏', 'aksrt'),
|
||||
'rand' => __('随机', 'aksrt'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function get_homepage_fixed_settings(): array {
|
||||
$s = Settings::get_all();
|
||||
$s = Settings::read_domain('page_display', 'homepage');
|
||||
$allowed_sorts = self::homepage_allowed_sorts();
|
||||
|
||||
// Canonical Zibll-aligned primary list contract.
|
||||
@@ -176,24 +113,21 @@ class HomeCms {
|
||||
$orderby_option = isset($s['home_list1_orderby_option']) && is_array($s['home_list1_orderby_option'])
|
||||
? $s['home_list1_orderby_option']
|
||||
: [];
|
||||
$default_sort = sanitize_key((string) ($orderby_option['orderby'] ?? 'date'));
|
||||
$sort_options_raw = $orderby_option['lists'] ?? [];
|
||||
if (!is_array($sort_options_raw)) {
|
||||
$sort_options_raw = [];
|
||||
}
|
||||
$sort_options = self::normalize_sort_options($sort_options_raw);
|
||||
$default_sort = sanitize_key((string) ($sort_options[0] ?? 'modified'));
|
||||
if (!in_array($default_sort, $allowed_sorts, true)) {
|
||||
$default_sort = 'date';
|
||||
$default_sort = 'modified';
|
||||
}
|
||||
$sort_options_raw = $orderby_option['options'] ?? [];
|
||||
if (!is_array($sort_options_raw)) {
|
||||
$sort_options_raw = [];
|
||||
}
|
||||
$sort_options = self::normalize_sort_options($sort_options_raw);
|
||||
if (!in_array($default_sort, $sort_options, true)) {
|
||||
array_unshift($sort_options, $default_sort);
|
||||
$sort_options = array_values(array_unique($sort_options));
|
||||
}
|
||||
|
||||
$list_title = sanitize_text_field((string) ($s['index_list_title'] ?? ''));
|
||||
if ($list_title === '') {
|
||||
$list_title = __('最新发布', 'aksrt');
|
||||
}
|
||||
$list_title = wp_kses_post((string) ($s['index_list_title'] ?? ''));
|
||||
|
||||
$exclude_posts = self::normalize_int_array($s['home_exclude_posts'] ?? []);
|
||||
$exclude_cats = self::normalize_int_array($s['home_exclude_cats'] ?? []);
|
||||
@@ -203,22 +137,11 @@ class HomeCms {
|
||||
// Canonical Zibll keys.
|
||||
'home_posts_list_s' => $main_list_enabled,
|
||||
'home_list1_orderby_s' => $sort_bar_enabled,
|
||||
'home_list1_orderby_option' => [
|
||||
'orderby' => $default_sort,
|
||||
'options' => $sort_options,
|
||||
],
|
||||
'home_list1_orderby_option' => ['lists' => $sort_options, 'dropdown' => !empty($orderby_option['dropdown'])],
|
||||
'index_list_title' => $list_title,
|
||||
'home_exclude_posts' => $exclude_posts,
|
||||
'home_exclude_cats' => $exclude_cats,
|
||||
'home_lists' => self::normalize_homepage_tabs($tabs_raw),
|
||||
|
||||
'homepage_sidebar_mini_enabled' => !isset($s['homepage_sidebar_mini_enabled']) || !empty($s['homepage_sidebar_mini_enabled']),
|
||||
'homepage_sort_ajax_enabled' => !isset($s['homepage_sort_ajax_enabled']) || !empty($s['homepage_sort_ajax_enabled']),
|
||||
'homepage_sticky_tabs_enabled' => !isset($s['homepage_sticky_tabs_enabled']) || !empty($s['homepage_sticky_tabs_enabled']),
|
||||
'home_list_sort_dropdown' => !empty($s['home_list_sort_dropdown']),
|
||||
'homepage_tab_posts_per_page' => max(1, min(20, (int) ($s['homepage_tab_posts_per_page'] ?? 6))),
|
||||
'homepage_tab_limit' => max(1, min(12, (int) ($s['homepage_tab_limit'] ?? 4))),
|
||||
'homepage_module_banner_enabled' => !isset($s['homepage_module_banner_enabled']) || !empty($s['homepage_module_banner_enabled']),
|
||||
'home_lists' => self::normalize_homepage_tabs($tabs_raw),
|
||||
];
|
||||
|
||||
return $settings;
|
||||
@@ -238,29 +161,26 @@ class HomeCms {
|
||||
return $settings['home_lists'];
|
||||
}
|
||||
|
||||
/** @return array<int, array{term_id:int,title:string,orderby:string,enabled:bool}> */
|
||||
/** @return array<int, array{term_id:int,title:string,orderby:string}> */
|
||||
public static function home_lists(): array {
|
||||
return self::homepage_tabs();
|
||||
}
|
||||
|
||||
public static function homepage_sidebar_mini_enabled(): bool {
|
||||
$settings = self::get_homepage_fixed_settings();
|
||||
return !empty($settings['homepage_sidebar_mini_enabled']);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function homepage_default_sort(): string {
|
||||
$settings = self::get_homepage_fixed_settings();
|
||||
return $settings['home_list1_orderby_option']['orderby'] ?? 'date';
|
||||
return $settings['home_list1_orderby_option']['lists'][0] ?? 'modified';
|
||||
}
|
||||
|
||||
public static function homepage_sort_ajax_enabled(): bool {
|
||||
$settings = self::get_homepage_fixed_settings();
|
||||
return !empty($settings['homepage_sort_ajax_enabled']);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function homepage_sticky_tabs_enabled(): bool {
|
||||
$settings = self::get_homepage_fixed_settings();
|
||||
return !empty($settings['homepage_sticky_tabs_enabled']);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function home_posts_list_enabled(): bool {
|
||||
@@ -278,14 +198,14 @@ class HomeCms {
|
||||
return !empty($settings['home_list1_orderby_s']);
|
||||
}
|
||||
|
||||
public static function home_list_sort_options(): array {
|
||||
$settings = self::get_homepage_fixed_settings();
|
||||
return $settings['home_list1_orderby_option']['options'] ?? ['modified', 'date', 'views', 'likes', 'comments'];
|
||||
public static function home_list_sort_options(): array {
|
||||
$settings = self::get_homepage_fixed_settings();
|
||||
return $settings['home_list1_orderby_option']['lists'] ?? ['modified', 'views', 'like', 'comment_count'];
|
||||
}
|
||||
|
||||
public static function home_list_sort_dropdown(): bool {
|
||||
$settings = self::get_homepage_fixed_settings();
|
||||
return !empty($settings['home_list_sort_dropdown']);
|
||||
return !empty($settings['home_list1_orderby_option']['dropdown']);
|
||||
}
|
||||
|
||||
public static function home_exclude_post_ids(): array {
|
||||
@@ -299,13 +219,7 @@ class HomeCms {
|
||||
}
|
||||
|
||||
public static function homepage_tab_posts_per_page(): int {
|
||||
$settings = self::get_homepage_fixed_settings();
|
||||
return (int) $settings['homepage_tab_posts_per_page'];
|
||||
}
|
||||
|
||||
public static function homepage_tab_limit(): int {
|
||||
$settings = self::get_homepage_fixed_settings();
|
||||
return (int) $settings['homepage_tab_limit'];
|
||||
return 6;
|
||||
}
|
||||
|
||||
public static function get_paid_content_settings(): array {
|
||||
@@ -361,8 +275,8 @@ class HomeCms {
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($result)) {
|
||||
$result = ['modified', 'date', 'views', 'likes', 'comments'];
|
||||
if (empty($result)) {
|
||||
$result = ['modified', 'views', 'like', 'comment_count'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -389,22 +303,46 @@ class HomeCms {
|
||||
continue;
|
||||
}
|
||||
|
||||
$orderby = sanitize_key($tab['orderby'] ?? 'date');
|
||||
$orderby = sanitize_key($tab['orderby'] ?? 'modified');
|
||||
if (!in_array($orderby, $allowed_orderby, true)) {
|
||||
$orderby = 'date';
|
||||
$orderby = 'modified';
|
||||
}
|
||||
|
||||
$tabs[] = [
|
||||
'term_id' => $term_id,
|
||||
'title' => sanitize_text_field($tab['title'] ?? ''),
|
||||
'title' => wp_kses_post((string) ($tab['title'] ?? '')),
|
||||
'orderby' => $orderby,
|
||||
'enabled' => !empty($tab['enabled']),
|
||||
];
|
||||
}
|
||||
|
||||
return $tabs;
|
||||
}
|
||||
|
||||
private static function normalize_footer_images($raw_items): array {
|
||||
if (!is_array($raw_items)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$items = [];
|
||||
foreach ($raw_items as $item) {
|
||||
if (!is_array($item) || empty($item['image'])) {
|
||||
continue;
|
||||
}
|
||||
$items[] = [
|
||||
'image' => (string) $item['image'],
|
||||
'text' => (string) ($item['text'] ?? ''),
|
||||
'link' => is_array($item['link'] ?? null) ? $item['link'] : [],
|
||||
];
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
private static function normalize_palette_hex(string $value): string {
|
||||
$value = ltrim(trim($value), '#');
|
||||
return preg_match('/^[0-9a-f]{6}$/i', $value) ? '#' . strtolower($value) : '';
|
||||
}
|
||||
|
||||
private static function parse_slug_list(string $raw, array $fallback = []): array {
|
||||
$items = preg_split('/[\s,]+/', $raw, -1, PREG_SPLIT_NO_EMPTY);
|
||||
$normalized = [];
|
||||
|
||||
@@ -48,7 +48,7 @@ trait HomepageReader {
|
||||
/**
|
||||
* Structured homepage tabs.
|
||||
*
|
||||
* @return array<int, array{term_id:int, title:string, orderby:string, enabled:bool}>
|
||||
* @return array<int, array{term_id:int, title:string, orderby:string}>
|
||||
*/
|
||||
public static function homepage_tabs(): array {
|
||||
return HomeCms::homepage_tabs();
|
||||
@@ -144,30 +144,6 @@ trait HomepageReader {
|
||||
return HomeCms::homepage_tab_posts_per_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum number of category tabs to display.
|
||||
*/
|
||||
public static function homepage_tab_limit(): int {
|
||||
return HomeCms::homepage_tab_limit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取顶部组件图标卡片配置
|
||||
* @return array
|
||||
*/
|
||||
public static function homepage_module_banner_enabled(): bool {
|
||||
return DisplayRuntime::homepage_module_banner_enabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Global slider configuration shared by Hero and Banner.
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function get_slider_config(): array {
|
||||
return DisplayRuntime::get_slider_config();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取页脚配色配置
|
||||
*/
|
||||
|
||||
@@ -38,13 +38,11 @@ class Settings {
|
||||
'aksrt_oauth',
|
||||
'aksrt_captcha_config',
|
||||
'aksrt_moderation_config',
|
||||
'aksrt_footer_settings',
|
||||
'aksrt_footer_tabbar',
|
||||
'aksrt_floating_buttons',
|
||||
'aksrt_credit_settings',
|
||||
'aksrt_notification_settings',
|
||||
'aksrt_friend_links',
|
||||
'aksrt_banners',
|
||||
'aksrt_category_recommend',
|
||||
'aksrt_identity_secrets',
|
||||
];
|
||||
@@ -719,27 +717,6 @@ class Settings {
|
||||
|
||||
|
||||
|
||||
/** 获取Banner列表 */
|
||||
public static function get_banners(string $position = ''): array {
|
||||
return DisplayRuntime::get_banners($position);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 保存Banner */
|
||||
public static function save_banner(array $data): int {
|
||||
return DisplayRuntime::save_banner($data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 删除Banner */
|
||||
public static function delete_banner(string $id): void {
|
||||
DisplayRuntime::delete_banner($id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 获取友链列表 */
|
||||
public static function get_friend_links(): array {
|
||||
return DisplayRuntime::get_friend_links();
|
||||
@@ -841,7 +818,7 @@ class Settings {
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 获取底部信息完整配置(统一 aksrt_footer_settings + aksrt_settings page-display footer 字段)
|
||||
* 获取页面&显示中的页脚配置。
|
||||
* @return array
|
||||
*/
|
||||
public static function get_footer_settings(): array {
|
||||
@@ -850,41 +827,6 @@ class Settings {
|
||||
|
||||
|
||||
|
||||
/** 获取联系图标列表(仅返回启用的) */
|
||||
public static function get_footer_contacts(): array {
|
||||
return HomeCms::get_footer_contacts();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 获取快速链接列表(仅返回启用的,按排序) */
|
||||
public static function get_quick_links(): array {
|
||||
return HomeCms::get_quick_links();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 获取更多链接列表(仅返回启用的,按排序) */
|
||||
public static function get_more_links(): array {
|
||||
return HomeCms::get_more_links();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 获取热门分类显示数量 */
|
||||
public static function get_hot_categories_count(): int {
|
||||
return HomeCms::get_hot_categories_count();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 获取版权文字 */
|
||||
public static function get_copyright_text(): string {
|
||||
return HomeCms::get_copyright_text();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取页脚品牌列(第一列)配置
|
||||
* @return array {m_s, img, img_dark, title, custom_code}
|
||||
|
||||
@@ -101,14 +101,14 @@ class ThemeSearch {
|
||||
}
|
||||
|
||||
public static function search_custom_key(): array {
|
||||
$raw = Settings::get_value('search_custom_key', ['title', 'content', 'excerpt', 'post_tag', 'forum_topic', 'shop_cat', 'shop_discount']);
|
||||
$raw = Settings::get_value('search_custom_key', ['title', 'content', 'excerpt', 'category', 'post_tag']);
|
||||
if (is_array($raw)) {
|
||||
$items = $raw;
|
||||
} else {
|
||||
$items = preg_split('/[,,\s]+/', (string) $raw) ?: [];
|
||||
}
|
||||
$items = array_map(static fn($k) => trim(sanitize_key((string) $k)), $items);
|
||||
$valid = ['title', 'content', 'excerpt', 'category', 'post_tag', 'topics', 'coment', 'user', 'forum_topic', 'forum_tag', 'shop_cat', 'shop_tag', 'shop_discount'];
|
||||
$valid = ['title', 'content', 'excerpt', 'category', 'post_tag', 'coment', 'user'];
|
||||
return array_values(array_intersect(array_filter(array_unique($items)), $valid));
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ class ThemeSearch {
|
||||
$terms = [(string) $query->get('s')];
|
||||
}
|
||||
$groups = [];
|
||||
$taxonomies = ['post_tag', 'category', 'topics', 'forum_tag', 'forum_topic', 'shop_tag', 'shop_cat', 'shop_discount'];
|
||||
$taxonomies = ['post_tag', 'category'];
|
||||
foreach ($terms as $term) {
|
||||
$like = (!empty($query->query_vars['exact']) ? '' : '%') . $wpdb->esc_like((string) $term) . (!empty($query->query_vars['exact']) ? '' : '%');
|
||||
$parts = [];
|
||||
@@ -212,7 +212,7 @@ class ThemeSearch {
|
||||
if (in_array('user', $keys, true)) {
|
||||
$join .= " LEFT JOIN {$wpdb->users} AS aksrt_search_users ON ({$wpdb->posts}.post_author = aksrt_search_users.ID)";
|
||||
}
|
||||
if (array_intersect(['post_tag', 'category', 'topics', 'forum_tag', 'forum_topic', 'shop_tag', 'shop_cat', 'shop_discount'], $keys)) {
|
||||
if (array_intersect(['post_tag', 'category'], $keys)) {
|
||||
$join .= " LEFT JOIN {$wpdb->term_relationships} AS aksrt_search_tr ON ({$wpdb->posts}.ID = aksrt_search_tr.object_id)";
|
||||
$join .= " LEFT JOIN {$wpdb->term_taxonomy} AS aksrt_search_tt ON (aksrt_search_tr.term_taxonomy_id = aksrt_search_tt.term_taxonomy_id)";
|
||||
$join .= " LEFT JOIN {$wpdb->terms} AS aksrt_search_terms ON (aksrt_search_tt.term_id = aksrt_search_terms.term_id)";
|
||||
|
||||
@@ -15,65 +15,53 @@ class UserRuntime {
|
||||
* @return array
|
||||
*/
|
||||
public static function get_user_home_settings(): array {
|
||||
$settings = Settings::get_all();
|
||||
$allowed_tabs = ['posts', 'comments', 'favorites', 'credits', 'follow'];
|
||||
|
||||
$parse_csv = function (string $key, string $fallback) use ($settings, $allowed_tabs): array {
|
||||
$raw = $settings[$key] ?? $fallback;
|
||||
$items = is_array($raw) ? $raw : preg_split('/[\s,]+/', (string) $raw, -1, PREG_SPLIT_NO_EMPTY);
|
||||
$clean = [];
|
||||
foreach ((array) $items as $v) {
|
||||
$v = sanitize_key((string) $v);
|
||||
if (in_array($v, $allowed_tabs, true) && !in_array($v, $clean, true)) {
|
||||
$clean[] = $v;
|
||||
}
|
||||
}
|
||||
return $clean;
|
||||
};
|
||||
|
||||
// Public profile tabs supported by this theme.
|
||||
$normalized_tabs = $parse_csv('user_home_tabs', 'posts,favorites,comments,follow');
|
||||
if (empty($normalized_tabs)) {
|
||||
$normalized_tabs = ['posts', 'favorites', 'comments', 'follow'];
|
||||
}
|
||||
|
||||
$raw_order = $parse_csv('user_home_tabs_order', '');
|
||||
$ordered = [];
|
||||
foreach ($raw_order as $tab) {
|
||||
if (in_array($tab, $normalized_tabs, true)) {
|
||||
$ordered[] = $tab;
|
||||
}
|
||||
}
|
||||
foreach ($normalized_tabs as $tab) {
|
||||
if (!in_array($tab, $ordered, true)) {
|
||||
$ordered[] = $tab;
|
||||
}
|
||||
}
|
||||
|
||||
$pc_hide = $parse_csv('user_home_tabs_pc_hide', '');
|
||||
$mobile_hide = $parse_csv('user_home_tabs_mobile_hide', '');
|
||||
$settings = Settings::read_domain('page_display', 'user-home');
|
||||
$tab_options = is_array($settings['author_main_tab_opt'] ?? null)
|
||||
? $settings['author_main_tab_opt']
|
||||
: [
|
||||
'post' => ['pc_s' => true, 'm_s' => true],
|
||||
'favorite' => ['pc_s' => true, 'm_s' => true],
|
||||
'comment' => ['pc_s' => true, 'm_s' => true],
|
||||
'follow' => ['pc_s' => true, 'm_s' => true],
|
||||
];
|
||||
$map = ['post' => 'posts', 'favorite' => 'favorites', 'comment' => 'comments', 'follow' => 'follow'];
|
||||
$ordered = [];
|
||||
$pc_hide = [];
|
||||
$mobile_hide = [];
|
||||
foreach ($tab_options as $key => $option) {
|
||||
$key = sanitize_key((string) $key);
|
||||
if (!isset($map[$key]) || !is_array($option)) {
|
||||
continue;
|
||||
}
|
||||
$tab = $map[$key];
|
||||
$ordered[] = $tab;
|
||||
if (empty($option['pc_s'])) {
|
||||
$pc_hide[] = $tab;
|
||||
}
|
||||
if (empty($option['m_s'])) {
|
||||
$mobile_hide[] = $tab;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'route_identifier' => (($settings['user_home_route_identifier'] ?? 'nicename') === 'uid') ? 'uid' : 'nicename',
|
||||
'tabs' => $normalized_tabs,
|
||||
'route_identifier' => 'nicename',
|
||||
'tabs' => $ordered,
|
||||
'tabs_order' => $ordered,
|
||||
'tabs_pc_hide' => $pc_hide,
|
||||
'tabs_mobile_hide' => $mobile_hide,
|
||||
// Public author page is full-width (Zibll author.php). Sidebar option kept as no-op reserved key.
|
||||
'sidebar' => false,
|
||||
'cover' => !isset($settings['user_home_cover']) || !empty($settings['user_home_cover']),
|
||||
'follow' => !isset($settings['user_home_follow']) || !empty($settings['user_home_follow']),
|
||||
'posts_per_page' => max(5, min(50, (int) ($settings['user_home_posts_per_page'] ?? 10))),
|
||||
'avatar' => !isset($settings['user_home_avatar']) || !empty($settings['user_home_avatar']),
|
||||
'name' => !isset($settings['user_home_name']) || !empty($settings['user_home_name']),
|
||||
'desc' => !isset($settings['user_home_desc']) || !empty($settings['user_home_desc']),
|
||||
// Body stats row removed; counts live in tabs like Zibll.
|
||||
'cover' => true,
|
||||
'follow' => true,
|
||||
'posts_per_page' => max(1, (int) get_option('posts_per_page', 10)),
|
||||
'avatar' => true,
|
||||
'name' => true,
|
||||
'desc' => true,
|
||||
'stats' => false,
|
||||
'social' => !isset($settings['user_home_social']) || !empty($settings['user_home_social']),
|
||||
'social' => true,
|
||||
'tab_swiper' => !empty($settings['author_main_tab_swiper']),
|
||||
'message' => !isset($settings['user_home_message']) || !empty($settings['user_home_message']),
|
||||
'private' => !isset($settings['user_home_private']) || !empty($settings['user_home_private']),
|
||||
'paging_ajax' => !isset($settings['user_home_paging_ajax']) || !empty($settings['user_home_paging_ajax']),
|
||||
'message' => true,
|
||||
'private' => true,
|
||||
'paging_ajax' => true,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user