用户等级配置的「等级图标 URL」和 VIP 的「会员图标」在后台显示为空白输入框, 但前台实际渲染的是主题自带图标——后台既看不到当前效果,也无法判断该字段 是否已被配置。根因是 schema default 写成空串,而运行时(Level:: get_level_icon_url() / PaymentRuntime::get_vip_icon_url())在值为空时回退 到 assets/img 下的内置图标。 把 schema default 改为直接写入主题自带图标 URL,与运行时回退链保持同一路径、 同一 is_file 守卫:后台展示的就是前台真正使用的图标,清空则回到文本/FA 回退。 - SchemaUsers::default_level_options():icon_img_N 写入 user-level-N.png - SchemaPayment::vip_icon_default():vip_N_img_icon 写入 user/vip/vip-N.svg - level.js / vip.js 同步默认值,help 与 placeholder 文案改为描述默认图标 - 两个测试 harness 补齐 get_theme_file_uri/path stub(schema 现在需要它们) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
287 lines
11 KiB
PHP
287 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* Lightweight WordPress stubs for the page/display schema save contract.
|
|
*/
|
|
|
|
namespace {
|
|
define('ABSPATH', __DIR__);
|
|
$GLOBALS['jio_test_options'] = [];
|
|
$GLOBALS['jio_test_manage_options'] = false;
|
|
$GLOBALS['jio_visibility_query'] = '';
|
|
$GLOBALS['jio_visibility_page_config'] = [];
|
|
|
|
final class WP_Query {
|
|
/** @var array<string,mixed> */
|
|
private array $vars;
|
|
|
|
/** @param array<string,mixed> $vars */
|
|
public function __construct(array $vars = []) {
|
|
$this->vars = $vars;
|
|
}
|
|
|
|
/** @return mixed */
|
|
public function get(string $key) {
|
|
return $this->vars[$key] ?? null;
|
|
}
|
|
}
|
|
|
|
$GLOBALS['wp_query'] = new WP_Query();
|
|
|
|
function sanitize_key($value): string {
|
|
return preg_replace('/[^a-z0-9_\-]/', '', strtolower((string) $value));
|
|
}
|
|
|
|
function sanitize_text_field($value): string {
|
|
return trim(strip_tags((string) $value));
|
|
}
|
|
|
|
function sanitize_textarea_field($value): string {
|
|
return trim(strip_tags((string) $value));
|
|
}
|
|
|
|
function wp_kses_post($value): string {
|
|
$value = preg_replace('#<script\b[^>]*>.*?</script>#is', '', (string) $value);
|
|
return strip_tags($value, '<a><b><strong><em><i><span><div><p><br><badge>');
|
|
}
|
|
|
|
function esc_url_raw($value): string {
|
|
$value = trim((string) $value);
|
|
if ($value === '') {
|
|
return '';
|
|
}
|
|
$scheme = strtolower((string) parse_url($value, PHP_URL_SCHEME));
|
|
return in_array($scheme, ['http', 'https', 'mailto'], true) ? $value : '';
|
|
}
|
|
|
|
function wp_unslash($value) {
|
|
return $value;
|
|
}
|
|
|
|
function trailingslashit($value): string {
|
|
return rtrim((string) $value, '/\\') . '/';
|
|
}
|
|
|
|
function get_template_directory_uri(): string {
|
|
return 'https://example.test/wp-content/themes/jio';
|
|
}
|
|
|
|
function get_theme_file_uri(string $path = ''): string {
|
|
return 'https://example.test/wp-content/themes/jio/' . ltrim($path, '/');
|
|
}
|
|
|
|
function get_theme_file_path(string $path = ''): string {
|
|
return dirname(__DIR__, 2) . '/' . ltrim($path, '/');
|
|
}
|
|
|
|
function home_url($path = ''): string {
|
|
return 'https://example.test' . ($path ? '/' . ltrim((string) $path, '/') : '');
|
|
}
|
|
|
|
function get_bloginfo($show = ''): string {
|
|
return $show === 'title' ? 'Jio' : 'Jio';
|
|
}
|
|
|
|
function get_option($key, $default = false) {
|
|
return array_key_exists($key, $GLOBALS['jio_test_options']) ? $GLOBALS['jio_test_options'][$key] : $default;
|
|
}
|
|
|
|
function get_query_var(string $key) {
|
|
if ($key === 'jio_message_view' && $GLOBALS['jio_visibility_query'] === 'message') {
|
|
return 'unread';
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function update_option($key, $value): bool {
|
|
$GLOBALS['jio_test_options'][$key] = $value;
|
|
return true;
|
|
}
|
|
|
|
function current_user_can($capability): bool {
|
|
return $capability === 'manage_options' && !empty($GLOBALS['jio_test_manage_options']);
|
|
}
|
|
|
|
function is_admin(): bool { return false; }
|
|
function wp_is_mobile(): bool { return false; }
|
|
function is_home(): bool {
|
|
// Custom rewrites can leave WordPress's posts-page flag set even though
|
|
// the theme resolves the request to another front-end surface.
|
|
return in_array($GLOBALS['jio_visibility_query'], [
|
|
'home', 'page', 'message', 'user_home', 'user_center', 'user_home_page', 'user_center_page',
|
|
], true);
|
|
}
|
|
function is_front_page(): bool { return $GLOBALS['jio_visibility_query'] === 'home'; }
|
|
function is_category(): bool { return $GLOBALS['jio_visibility_query'] === 'category'; }
|
|
function is_tag(): bool { return $GLOBALS['jio_visibility_query'] === 'tag'; }
|
|
function is_author(): bool { return $GLOBALS['jio_visibility_query'] === 'author'; }
|
|
function is_singular(string $type = ''): bool { return $type === 'post' && $GLOBALS['jio_visibility_query'] === 'single'; }
|
|
function is_search(): bool { return $GLOBALS['jio_visibility_query'] === 'search'; }
|
|
function is_page(): bool {
|
|
return in_array($GLOBALS['jio_visibility_query'], ['page', 'user_home_page', 'user_center_page'], true);
|
|
}
|
|
function is_page_template(string $template): bool { return false; }
|
|
function get_queried_object_id(): int { return 42; }
|
|
|
|
/** @return mixed */
|
|
function get_post_meta(int $post_id, string $key, bool $single = false) {
|
|
return $GLOBALS['jio_visibility_page_config'];
|
|
}
|
|
|
|
function jio_is_user_home_page(): bool {
|
|
return in_array($GLOBALS['jio_visibility_query'], ['user_home', 'user_home_page'], true);
|
|
}
|
|
|
|
function jio_is_user_center_page(): bool {
|
|
return in_array($GLOBALS['jio_visibility_query'], ['user_center', 'user_center_page'], true);
|
|
}
|
|
}
|
|
|
|
namespace Jio\Settings {
|
|
class Settings {
|
|
/** @var array<string,mixed> */
|
|
public static array $headerSlider = [];
|
|
|
|
public static function flush_settings_cache(): void {}
|
|
|
|
/** @return array<string,mixed> */
|
|
public static function get_header_slider_settings(): array {
|
|
return self::$headerSlider;
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
require_once dirname(__DIR__, 2) . '/src/Ledger/AssetType.php';
|
|
require_once dirname(__DIR__, 2) . '/src/Ledger/AssetAmount.php';
|
|
require_once dirname(__DIR__, 2) . '/src/Admin/Schema/SchemaPageDisplay.php';
|
|
require_once dirname(__DIR__, 2) . '/src/Admin/Schema/SchemaPayment.php';
|
|
require_once dirname(__DIR__, 2) . '/src/Core/Color.php';
|
|
require_once dirname(__DIR__, 2) . '/src/Admin/SchemaProviderOAuthValidation.php';
|
|
require_once dirname(__DIR__, 2) . '/src/Admin/SchemaProviderOptionMatch.php';
|
|
require_once dirname(__DIR__, 2) . '/src/Admin/SchemaProvider.php';
|
|
require_once dirname(__DIR__, 2) . '/src/Admin/ControllerBase.php';
|
|
require_once dirname(__DIR__, 2) . '/inc/helpers/homepage/header-components.php';
|
|
|
|
$controller = new class extends \Jio\Admin\ControllerBase {
|
|
public function register_routes(): void {}
|
|
};
|
|
$provider = \Jio\Admin\SchemaProvider::get_instance();
|
|
|
|
$permission_denied = !$controller->check_permission();
|
|
$GLOBALS['jio_test_manage_options'] = true;
|
|
$permission_allowed = $controller->check_permission();
|
|
$unknown_section = $provider->get_section_schema('page_display', 'missing') === null;
|
|
$homepage_defaults = $provider->get_defaults('page_display', 'homepage');
|
|
$footer_defaults = $provider->get_defaults('page_display', 'footer');
|
|
|
|
$invalid = $provider->sanitize_payload('page_display', ['page_header_style' => '9'], 'other');
|
|
$invalid_validation = $provider->validate('page_display', $invalid, 'other');
|
|
|
|
$header_payload = [
|
|
'header_slider' => [[
|
|
'background' => 'javascript:alert(1)',
|
|
'unknown' => 'drop-me',
|
|
'link' => ['url' => 'https://example.test/slide', 'target' => '_blank', 'text' => '<b>Go</b>', 'unknown' => 'drop-me'],
|
|
'text' => ['title' => '<strong>Safe</strong><script>bad()</script>', 'desc' => '<em>Copy</em>', 'unknown' => 'drop-me'],
|
|
]],
|
|
'header_slider_card_option' => ['show_widget_bg' => true, 'cards' => []],
|
|
];
|
|
$header_clean = $provider->sanitize_payload('page_display', $header_payload, 'header-components');
|
|
|
|
$footer_payload = [
|
|
// Empty means no preset background; it must remain a valid optional palette value.
|
|
'footer_theme_bg' => '',
|
|
'fcode_t1_code' => '<strong>Allowed</strong><script>bad()</script>',
|
|
'footer_contact_weibo' => 'javascript:alert(1)',
|
|
'footer_mini_img' => [[
|
|
'text' => '<b>QR</b>',
|
|
'image' => 'https://example.test/qr.png',
|
|
'link' => ['url' => 'https://example.test/go', 'target' => '_blank', 'text' => 'Open', 'unknown' => 'drop-me'],
|
|
'unknown' => 'drop-me',
|
|
]],
|
|
'unknown_top_level' => 'drop-me',
|
|
];
|
|
$footer_clean = $provider->sanitize_payload('page_display', $footer_payload, 'footer');
|
|
$footer_validation = $provider->validate('page_display', $footer_clean, 'footer');
|
|
$provider->write_settings('page_display', $footer_clean, 'footer');
|
|
$footer_readback = $provider->read_settings('page_display', 'footer');
|
|
|
|
$vip_clean = $provider->sanitize_payload('payment', [
|
|
'nav_pay_vip' => false,
|
|
'nav_user_pay_vip' => true,
|
|
'unknown_vip_key' => true,
|
|
], 'vip');
|
|
$vip_validation = $provider->validate('payment', $vip_clean, 'vip');
|
|
$provider->write_settings('payment', $vip_clean, 'vip');
|
|
$vip_readback = $provider->read_settings('payment', 'vip');
|
|
|
|
$category_payload = [
|
|
'cat_orderby_option' => [
|
|
'lists' => ['modified', 'views', 'like', 'comment_count'],
|
|
'dropdown' => false,
|
|
],
|
|
];
|
|
$category_clean = $provider->sanitize_payload('page_display', $category_payload, 'category');
|
|
$category_validation = $provider->validate('page_display', $category_clean, 'category');
|
|
|
|
/**
|
|
* @param list<string> $show_on
|
|
* @param array<string,mixed> $page_config
|
|
*/
|
|
function jio_visibility_case(string $query, array $show_on, array $page_config = [], int $paged = 0): bool {
|
|
$GLOBALS['jio_visibility_query'] = $query;
|
|
$GLOBALS['jio_visibility_page_config'] = $page_config;
|
|
$GLOBALS['wp_query'] = new WP_Query(['paged' => $paged]);
|
|
\Jio\Settings\Settings::$headerSlider = [
|
|
'show_on_pages' => $show_on,
|
|
'show_type' => '',
|
|
];
|
|
return jio_header_slide_is_show();
|
|
}
|
|
|
|
$enabled_page_override = [
|
|
'header_slider_show' => true,
|
|
'header_slider_show_type' => '',
|
|
];
|
|
$visibility = [
|
|
'disabled' => jio_visibility_case('home', []),
|
|
'disabled_with_page_override' => jio_visibility_case('page', [], $enabled_page_override),
|
|
'page_enabled' => jio_visibility_case('page', ['page'], $enabled_page_override),
|
|
'public_user_home' => jio_visibility_case('user_home', ['user_home']),
|
|
'user_center' => jio_visibility_case('user_center', ['user_center']),
|
|
'legacy_author_public_home' => jio_visibility_case('user_home', ['author']),
|
|
'generic_page_excludes_home' => jio_visibility_case('user_home_page', ['page']),
|
|
'generic_page_excludes_center' => jio_visibility_case('user_center_page', ['page']),
|
|
'templated_public_user_home' => jio_visibility_case('user_home_page', ['user_home']),
|
|
'templated_user_center' => jio_visibility_case('user_center_page', ['user_center']),
|
|
'home_excludes_page' => jio_visibility_case('page', ['home']),
|
|
'message_uses_other_page' => jio_visibility_case('message', ['page']),
|
|
'message_excludes_home' => jio_visibility_case('message', ['home']),
|
|
'home_excludes_public_home' => jio_visibility_case('user_home', ['home']),
|
|
'home_excludes_user_center' => jio_visibility_case('user_center', ['home']),
|
|
'paged_archive_hidden' => jio_visibility_case('home', ['home'], [], 2),
|
|
];
|
|
|
|
echo json_encode([
|
|
'permission_denied' => $permission_denied,
|
|
'permission_allowed' => $permission_allowed,
|
|
'unknown_section' => $unknown_section,
|
|
'homepage_defaults' => $homepage_defaults,
|
|
'footer_defaults' => $footer_defaults,
|
|
'invalid_enum_rejected' => !$invalid_validation['valid'],
|
|
'nested_multiselect_valid' => $category_validation['valid'],
|
|
'nested' => $header_clean,
|
|
'header_card_bg' => $header_clean['header_slider_card_option']['show_widget_bg'] ?? null,
|
|
'footer_valid' => $footer_validation['valid'],
|
|
'footer_clean' => $footer_clean,
|
|
'footer_readback' => $footer_readback,
|
|
'vip_switches_valid' => $vip_validation['valid'],
|
|
'vip_switches_clean' => $vip_clean,
|
|
'vip_switches_readback' => [
|
|
'nav_pay_vip' => $vip_readback['nav_pay_vip'] ?? null,
|
|
'nav_user_pay_vip' => $vip_readback['nav_user_pay_vip'] ?? null,
|
|
],
|
|
'visibility' => $visibility,
|
|
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
|
}
|