fix(vip): preserve safe HTML in VIP copy
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
7a0445fc4a
commit
573040381d
15 files changed
+155
-118
No files matched your search
@@ -69,9 +69,9 @@ function vipProductFields() {
|
|||||||
function vipLevelTabFields(level) {
|
function vipLevelTabFields(level) {
|
||||||
const dep = { key: `pay_user_vip_${level}_s`, value: true };
|
const dep = { key: `pay_user_vip_${level}_s`, value: true };
|
||||||
return [
|
return [
|
||||||
{ key: `pay_user_vip_${level}_name`, label: '会员显示名称', type: 'input', default: level === 1 ? '黄金会员' : '钻石会员', dependsOn: dep, help: '会员名称(例如“黄金会员”、“超级会员”)' },
|
{ key: `pay_user_vip_${level}_name`, label: '会员显示名称', type: 'input', sanitize_type: 'html', default: level === 1 ? '黄金会员' : '钻石会员', dependsOn: dep, help: '支持HTML,请注意控制长度' },
|
||||||
{ key: `pay_user_vip_${level}_desc`, label: '一句话简介', type: 'input', default: '尊享专属特权', dependsOn: dep },
|
{ key: `pay_user_vip_${level}_desc`, label: '一句话简介', type: 'input', sanitize_type: 'html', default: '尊享专属特权', dependsOn: dep, help: '支持HTML' },
|
||||||
{ key: `pay_user_vip_${level}_equity`, label: '会员权益 HTML', type: 'textarea', sanitize_type: 'html', default: VIP_EQUITY_DEFAULT, rows: 4, dependsOn: dep, help: '建议使用 <li> 列表项' },
|
{ key: `pay_user_vip_${level}_equity`, label: '会员权益 HTML', type: 'textarea', sanitize_type: 'html', default: VIP_EQUITY_DEFAULT, rows: 4, dependsOn: dep, help: '支持安全HTML,建议使用 <li> 列表项' },
|
||||||
{ key: `vip_${level}_product_s`, label: '购买会员', type: 'switch', default: true, dependsOn: dep, help: '关闭后则不能付费购买会员,但可通过积分兑换会员,请确保已开启积分以及积分兑换会员功能' },
|
{ key: `vip_${level}_product_s`, label: '购买会员', type: 'switch', default: true, dependsOn: dep, help: '关闭后则不能付费购买会员,但可通过积分兑换会员,请确保已开启积分以及积分兑换会员功能' },
|
||||||
{
|
{
|
||||||
key: `vip_${level}_product`,
|
key: `vip_${level}_product`,
|
||||||
@@ -290,9 +290,10 @@ export const vipSchema = {
|
|||||||
key: 'vip_renew_desc',
|
key: 'vip_renew_desc',
|
||||||
label: '续费介绍',
|
label: '续费介绍',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
sanitize_type: 'html',
|
||||||
default: '立即续费会员,畅享VIP权益',
|
default: '立即续费会员,畅享VIP权益',
|
||||||
dependsOn: { key: 'vip_renew', value: true },
|
dependsOn: { key: 'vip_renew', value: true },
|
||||||
help: '续费会员的一句话简介',
|
help: '续费会员的一句话简介,支持HTML',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'vip_renew_price_type',
|
key: 'vip_renew_price_type',
|
||||||
@@ -371,7 +372,7 @@ export const vipSchema = {
|
|||||||
title: '会员升级',
|
title: '会员升级',
|
||||||
fields: [
|
fields: [
|
||||||
{ key: 'vip_upgrade', label: '允许升级', type: 'switch', default: true },
|
{ key: 'vip_upgrade', label: '允许升级', type: 'switch', default: true },
|
||||||
{ key: 'vip_upgrade_desc', label: '升级介绍', type: 'input', default: '升级VIP会员,享更多会员权益', dependsOn: { key: 'vip_upgrade', value: true }, help: '升级会员的一句话简介' },
|
{ key: 'vip_upgrade_desc', label: '升级介绍', type: 'input', sanitize_type: 'html', default: '升级VIP会员,享更多会员权益', dependsOn: { key: 'vip_upgrade', value: true }, help: '升级会员的一句话简介,支持HTML' },
|
||||||
{
|
{
|
||||||
key: 'vip_upgrade_product',
|
key: 'vip_upgrade_product',
|
||||||
label: '升级价格',
|
label: '升级价格',
|
||||||
@@ -478,9 +479,10 @@ export const vipSchema = {
|
|||||||
key: 'pay_user_vip_desc',
|
key: 'pay_user_vip_desc',
|
||||||
label: '会员介绍',
|
label: '会员介绍',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
sanitize_type: 'html',
|
||||||
default: '开通VIP会员,享受会员专属折扣以及多项特权',
|
default: '开通VIP会员,享受会员专属折扣以及多项特权',
|
||||||
dependsOn: { any: [{ key: 'pay_user_vip_1_s', value: true }, { key: 'pay_user_vip_2_s', value: true }] },
|
dependsOn: { any: [{ key: 'pay_user_vip_1_s', value: true }, { key: 'pay_user_vip_2_s', value: true }] },
|
||||||
help: '显示在开通界面顶部一句话简介,可以为会员权益简介或者活动介绍',
|
help: '显示在开通界面顶部一句话简介,可以为会员权益简介或者活动介绍,支持HTML',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'pay_user_vip_more',
|
key: 'pay_user_vip_more',
|
||||||
|
|||||||
+9
-13
@@ -17,8 +17,7 @@
|
|||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 服务端已按 post 级 HTML 净化的富文本字段(more / 卡密说明)走 innerHTML;
|
// Server-side schema sanitization limits these configured rich-text fields to post-safe HTML.
|
||||||
// node() 的 textContent 会把标签当字面文本显示。
|
|
||||||
function htmlNode(tag, className, value) {
|
function htmlNode(tag, className, value) {
|
||||||
var el = document.createElement(tag);
|
var el = document.createElement(tag);
|
||||||
if (className) el.className = className;
|
if (className) el.className = className;
|
||||||
@@ -70,7 +69,7 @@
|
|||||||
if (levelInfo && levelInfo.img_icon) {
|
if (levelInfo && levelInfo.img_icon) {
|
||||||
var img = node("img", "img-icon");
|
var img = node("img", "img-icon");
|
||||||
img.src = text(levelInfo.img_icon);
|
img.src = text(levelInfo.img_icon);
|
||||||
img.alt = text(levelInfo.name || "");
|
img.alt = text(levelInfo.name_text || levelInfo.name || "");
|
||||||
img.decoding = "async";
|
img.decoding = "async";
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
@@ -125,7 +124,7 @@
|
|||||||
// (右上 tag + 价格行 + 时长 badge,无标题行)。
|
// (右上 tag + 价格行 + 时长 badge,无标题行)。
|
||||||
var product = node("div", "zib-widget vip-product relative text-center product-box");
|
var product = node("div", "zib-widget vip-product relative text-center product-box");
|
||||||
// 标签颜色取后台「标签颜色」配置,未配置时回退参考主题默认的 jb-yellow。
|
// 标签颜色取后台「标签颜色」配置,未配置时回退参考主题默认的 jb-yellow。
|
||||||
if (item.discount_tag) append(product, node("div", "abs-right vip-tag badg " + (item.tag_class || "jb-yellow"), item.discount_tag));
|
if (item.discount_tag) append(product, htmlNode("div", "abs-right vip-tag badg " + (item.tag_class || "jb-yellow"), item.discount_tag));
|
||||||
append(product, priceRow(mark, item.price_label || "", item.show_price_label || ""));
|
append(product, priceRow(mark, item.price_label || "", item.show_price_label || ""));
|
||||||
if (item.days_label) append(product, node("div", "muted-2-color c-blue em09 p2-10 badg radius", item.days_label));
|
if (item.days_label) append(product, node("div", "muted-2-color c-blue em09 p2-10 badg radius", item.days_label));
|
||||||
append(label, product);
|
append(label, product);
|
||||||
@@ -408,7 +407,7 @@
|
|||||||
} else if (member.state === "expired") {
|
} else if (member.state === "expired") {
|
||||||
append(userInfo, node("div", "muted-2-color em09", "已过期"));
|
append(userInfo, node("div", "muted-2-color em09", "已过期"));
|
||||||
} else if (catalog.desc) {
|
} else if (catalog.desc) {
|
||||||
append(userInfo, node("div", "muted-2-color em09", catalog.desc));
|
append(userInfo, htmlNode("div", "muted-2-color em09", catalog.desc));
|
||||||
}
|
}
|
||||||
append(userBox, userInfo);
|
append(userBox, userInfo);
|
||||||
append(modal, userBox);
|
append(modal, userBox);
|
||||||
@@ -444,8 +443,8 @@
|
|||||||
append(miniIcon, vipIcon(levelInfo));
|
append(miniIcon, vipIcon(levelInfo));
|
||||||
append(miniIconbox, miniIcon);
|
append(miniIconbox, miniIcon);
|
||||||
var miniInfo = node("div", "v-info");
|
var miniInfo = node("div", "v-info");
|
||||||
append(miniInfo, node("div", "v-name", actionLabel(action) + (levelInfo.name || group.name || "VIP" + level)));
|
append(miniInfo, htmlNode("div", "v-name", actionLabel(action) + (levelInfo.name || group.name || "VIP" + level)));
|
||||||
append(miniInfo, node("div", "v-desc em09", levelInfo.desc || ""));
|
append(miniInfo, htmlNode("div", "v-desc em09", levelInfo.desc || ""));
|
||||||
append(miniIconbox, miniInfo);
|
append(miniIconbox, miniInfo);
|
||||||
append(miniCard, miniIconbox);
|
append(miniCard, miniIconbox);
|
||||||
append(tab, miniCard);
|
append(tab, miniCard);
|
||||||
@@ -471,15 +470,12 @@
|
|||||||
append(vicon, vipIcon(levelInfo));
|
append(vicon, vipIcon(levelInfo));
|
||||||
append(iconbox, vicon);
|
append(iconbox, vicon);
|
||||||
var vinfo = node("div", "v-info");
|
var vinfo = node("div", "v-info");
|
||||||
append(vinfo, node("div", "v-name", levelInfo.name || group.name || "VIP" + level));
|
append(vinfo, htmlNode("div", "v-name", levelInfo.name || group.name || "VIP" + level));
|
||||||
append(vinfo, node("div", "v-desc em09", levelInfo.desc || ""));
|
append(vinfo, htmlNode("div", "v-desc em09", levelInfo.desc || ""));
|
||||||
append(iconbox, vinfo);
|
append(iconbox, vinfo);
|
||||||
append(info, iconbox);
|
append(info, iconbox);
|
||||||
if (levelInfo.equity) {
|
if (levelInfo.equity) {
|
||||||
var equity = document.createElement("ul");
|
append(info, htmlNode("div", "", levelInfo.equity));
|
||||||
equity.className = "v-equity";
|
|
||||||
equity.innerHTML = text(levelInfo.equity);
|
|
||||||
append(info, equity);
|
|
||||||
}
|
}
|
||||||
var infoBa = node("div", "abs-center vip-baicon");
|
var infoBa = node("div", "abs-center vip-baicon");
|
||||||
append(infoBa, vipIcon(levelInfo));
|
append(infoBa, vipIcon(levelInfo));
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ if (!function_exists('aksrt_header_payvip_bar')) :
|
|||||||
$level = function_exists('aksrt_get_user_vip_level') ? (int) aksrt_get_user_vip_level($user_id) : 0;
|
$level = function_exists('aksrt_get_user_vip_level') ? (int) aksrt_get_user_vip_level($user_id) : 0;
|
||||||
if ($level > 0) {
|
if ($level > 0) {
|
||||||
$levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : [];
|
$levels = is_array($vip['levels'] ?? null) ? $vip['levels'] : [];
|
||||||
$name = (string) ($levels[$level]['name'] ?? ($level === 2 ? __('二级会员', 'aksrt') : __('一级会员', 'aksrt')));
|
$name = (string) ($levels[$level]['name_html'] ?? $levels[$level]['name'] ?? ($level === 2 ? __('二级会员', 'aksrt') : __('一级会员', 'aksrt')));
|
||||||
// 到期徽章语义对齐参考主题 zib_get_user_vip_exp_date_text():只有真的拿到
|
// 到期徽章语义对齐参考主题 zib_get_user_vip_exp_date_text():只有真的拿到
|
||||||
// 到期时间才渲染这枚徽章,已过期显示「会员已过期」否则显示裸日期;显式永久
|
// 到期时间才渲染这枚徽章,已过期显示「会员已过期」否则显示裸日期;显式永久
|
||||||
// 会员显示「永久会员」;拿不到到期数据时参考主题返回 false,整枚徽章不渲染。
|
// 会员显示「永久会员」;拿不到到期数据时参考主题返回 false,整枚徽章不渲染。
|
||||||
@@ -219,7 +219,7 @@ if (!function_exists('aksrt_header_payvip_bar')) :
|
|||||||
$html = '<div class="mt10 em09" style="padding:2px;">';
|
$html = '<div class="mt10 em09" style="padding:2px;">';
|
||||||
$html .= '<span class="radius4 payvip-icon btn-block text-center">';
|
$html .= '<span class="radius4 payvip-icon btn-block text-center">';
|
||||||
$html .= function_exists('aksrt_get_vip_icon') ? aksrt_get_vip_icon($user_id, 'em12 mr6', false) : '';
|
$html .= function_exists('aksrt_get_vip_icon') ? aksrt_get_vip_icon($user_id, 'em12 mr6', false) : '';
|
||||||
$html .= '<span>' . esc_html($name) . '</span>';
|
$html .= '<span>' . wp_kses_post($name) . '</span>';
|
||||||
if ($exp_label !== '') {
|
if ($exp_label !== '') {
|
||||||
$html .= '<span class="ml10 badg jb-yellow vip-expdate-tag">'
|
$html .= '<span class="ml10 badg jb-yellow vip-expdate-tag">'
|
||||||
. '<i class="fa fas fa-clock mr3" aria-hidden="true"></i>'
|
. '<i class="fa fas fa-clock mr3" aria-hidden="true"></i>'
|
||||||
|
|||||||
@@ -1309,6 +1309,7 @@ if (!function_exists('aksrt_get_posts_vip_price')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$level_name = (string) ($level_settings['name'] ?? sprintf(__('VIP%d', 'aksrt'), $level));
|
$level_name = (string) ($level_settings['name'] ?? sprintf(__('VIP%d', 'aksrt'), $level));
|
||||||
|
$level_name_html = (string) ($level_settings['name_html'] ?? $level_name);
|
||||||
$price = $vip_minor === 0
|
$price = $vip_minor === 0
|
||||||
? esc_html__('免费', 'aksrt')
|
? esc_html__('免费', 'aksrt')
|
||||||
: $mark . esc_html(aksrt_format_price($value));
|
: $mark . esc_html(aksrt_format_price($value));
|
||||||
@@ -1319,7 +1320,7 @@ if (!function_exists('aksrt_get_posts_vip_price')) {
|
|||||||
? ' data-toggle="tooltip" title="' . esc_attr($title) . '"'
|
? ' data-toggle="tooltip" title="' . esc_attr($title) . '"'
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
$items[] = '<span class="but vip-price' . $action_class . '" vip-level="' . esc_attr((string) $level) . '" data-vip-level="' . esc_attr((string) $level) . '"' . $title_attr . '>' . $icon . esc_html($level_name) . '<span class="em12 ml3 vip-price-text">' . $price . '</span></span>';
|
$items[] = '<span class="but vip-price' . $action_class . '" vip-level="' . esc_attr((string) $level) . '" data-vip-level="' . esc_attr((string) $level) . '"' . $title_attr . '>' . $icon . wp_kses_post($level_name_html) . '<span class="em12 ml3 vip-price-text">' . $price . '</span></span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$html = implode('', $items);
|
$html = implode('', $items);
|
||||||
@@ -1384,10 +1385,13 @@ if (!function_exists('aksrt_get_paywall_html')) {
|
|||||||
$vip_action = $vip_level > 0
|
$vip_action = $vip_level > 0
|
||||||
? sprintf(__('升级%s', 'aksrt'), $level_name)
|
? sprintf(__('升级%s', 'aksrt'), $level_name)
|
||||||
: __('开通会员', 'aksrt');
|
: __('开通会员', 'aksrt');
|
||||||
|
$vip_action_html = $vip_level > 0
|
||||||
|
? __('升级', 'aksrt') . wp_kses_post((string) ($level_settings['name_html'] ?? $level_name))
|
||||||
|
: esc_html__('开通会员', 'aksrt');
|
||||||
$action_class = is_user_logged_in() ? 'pay-vip' : 'signin-loader';
|
$action_class = is_user_logged_in() ? 'pay-vip' : 'signin-loader';
|
||||||
$icon = aksrt_get_pay_vip_icon_html($level_settings);
|
$icon = aksrt_get_pay_vip_icon_html($level_settings);
|
||||||
$button = '<div class="badg c-yellow em09 mb6"><i class="fa fas fa-fw fa-info-circle mr6" aria-hidden="true"></i>' . esc_html(sprintf(__('您暂无购买权限,请先%s', 'aksrt'), $vip_action)) . '</div>';
|
$button = '<div class="badg c-yellow em09 mb6"><i class="fa fas fa-fw fa-info-circle mr6" aria-hidden="true"></i>' . esc_html(sprintf(__('您暂无购买权限,请先%s', 'aksrt'), $vip_action)) . '</div>';
|
||||||
$button .= '<a href="javascript:;" vip-level="' . esc_attr((string) $pay_limit) . '" data-vip-level="' . esc_attr((string) $pay_limit) . '" class="but btn-block jb-vip' . esc_attr((string) $pay_limit) . ' ' . esc_attr($action_class) . ' padding-lg">' . $icon . esc_html($vip_action) . '</a>';
|
$button .= '<a href="javascript:;" vip-level="' . esc_attr((string) $pay_limit) . '" data-vip-level="' . esc_attr((string) $pay_limit) . '" class="but btn-block jb-vip' . esc_attr((string) $pay_limit) . ' ' . esc_attr($action_class) . ' padding-lg">' . $icon . $vip_action_html . '</a>';
|
||||||
} elseif (!is_user_logged_in()) {
|
} elseif (!is_user_logged_in()) {
|
||||||
if ($amount_minor > 0 && $modo === '0' && aksrt_allow_guest_content_pay($post)) {
|
if ($amount_minor > 0 && $modo === '0' && aksrt_allow_guest_content_pay($post)) {
|
||||||
$button = '<button type="button" class="but jb-red padding-lg cashier-link' . $block_class . '" data-aksrt-pay-content="' . esc_attr((string) $post->ID) . '"><i class="fa fas fa-shopping-cart" aria-hidden="true"></i> ' . esc_html__('立即购买', 'aksrt') . '</button>';
|
$button = '<button type="button" class="but jb-red padding-lg cashier-link' . $block_class . '" data-aksrt-pay-content="' . esc_attr((string) $post->ID) . '"><i class="fa fas fa-shopping-cart" aria-hidden="true"></i> ' . esc_html__('立即购买', 'aksrt') . '</button>';
|
||||||
|
|||||||
@@ -481,7 +481,7 @@ endif;
|
|||||||
* or Chinese keyword heuristics — that mangles intentional HTML.
|
* or Chinese keyword heuristics — that mangles intentional HTML.
|
||||||
*
|
*
|
||||||
* Rules:
|
* Rules:
|
||||||
* - Sanitize with a small allowlist.
|
* - Sanitize with WordPress post-safe HTML.
|
||||||
* - If markup already has <li>, wrap the items in <ul class="v-equity …">.
|
* - If markup already has <li>, wrap the items in <ul class="v-equity …">.
|
||||||
* - Otherwise output the sanitized HTML inside <div class="v-equity …">.
|
* - Otherwise output the sanitized HTML inside <div class="v-equity …">.
|
||||||
* - Plain multi-line text: each non-empty line becomes one <li> (only \n split).
|
* - Plain multi-line text: each non-empty line becomes one <li> (only \n split).
|
||||||
@@ -497,21 +497,7 @@ if (!function_exists('aksrt_format_vip_equity_html')) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$allowed = [
|
$clean = trim(wp_kses_post($raw));
|
||||||
'li' => ['class' => true],
|
|
||||||
'ul' => ['class' => true],
|
|
||||||
'ol' => ['class' => true],
|
|
||||||
'strong' => [],
|
|
||||||
'b' => [],
|
|
||||||
'em' => [],
|
|
||||||
'i' => ['class' => true],
|
|
||||||
'span' => ['class' => true],
|
|
||||||
'br' => [],
|
|
||||||
'p' => ['class' => true],
|
|
||||||
'div' => ['class' => true],
|
|
||||||
'a' => ['href' => true, 'target' => true, 'rel' => true, 'class' => true],
|
|
||||||
];
|
|
||||||
$clean = trim(wp_kses($raw, $allowed));
|
|
||||||
if ($clean === '') {
|
if ($clean === '') {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ if ($status === '') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($vip_level > 0) {
|
if ($vip_level > 0) {
|
||||||
$title = (string) ($vip_levels[$vip_level]['name'] ?? ('VIP' . $vip_level));
|
$title = (string) ($vip_levels[$vip_level]['name_html'] ?? $vip_levels[$vip_level]['name'] ?? ('VIP' . $vip_level));
|
||||||
if ($expires <= 0 && ($vip_state['source'] ?? 'none') !== 'order') {
|
if ($expires <= 0 && ($vip_state['source'] ?? 'none') !== 'order') {
|
||||||
$status = __('永久会员', 'aksrt');
|
$status = __('永久会员', 'aksrt');
|
||||||
} else {
|
} else {
|
||||||
@@ -41,7 +41,7 @@ if ($vip_level > 0) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} elseif ($expired_level > 0) {
|
} elseif ($expired_level > 0) {
|
||||||
$title = (string) ($vip_levels[$expired_level]['name'] ?? ('VIP' . $expired_level));
|
$title = (string) ($vip_levels[$expired_level]['name_html'] ?? $vip_levels[$expired_level]['name'] ?? ('VIP' . $expired_level));
|
||||||
$status = __('已过期', 'aksrt');
|
$status = __('已过期', 'aksrt');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,8 +55,8 @@ $sidebar_arrow = aksrt_icon_markup('zibsvg-right', 'uc-sidebar-vip__arrow');
|
|||||||
<a class="uc-sidebar-vip" href="<?php echo esc_url($vip_url); ?>" data-tab="vip">
|
<a class="uc-sidebar-vip" href="<?php echo esc_url($vip_url); ?>" data-tab="vip">
|
||||||
<span class="uc-sidebar-vip__icon"><?php echo $sidebar_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
|
<span class="uc-sidebar-vip__icon"><?php echo $sidebar_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
|
||||||
<span class="uc-sidebar-vip__content">
|
<span class="uc-sidebar-vip__content">
|
||||||
<strong><?php echo esc_html($title); ?></strong>
|
<strong><?php echo wp_kses_post($title); ?></strong>
|
||||||
<small><?php echo esc_html($status); ?></small>
|
<small><?php echo wp_kses_post($status); ?></small>
|
||||||
</span>
|
</span>
|
||||||
<?php echo $sidebar_arrow; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
<?php echo $sidebar_arrow; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||||
</a>
|
</a>
|
||||||
@@ -46,7 +46,7 @@ $btn_type = 'pay';
|
|||||||
|
|
||||||
if ($level > 0) {
|
if ($level > 0) {
|
||||||
$vip_level_number = $level;
|
$vip_level_number = $level;
|
||||||
$vip_level_name = (string) ($levels[$level]['name'] ?? ('VIP' . $level));
|
$vip_level_name = (string) ($levels[$level]['name_html'] ?? $levels[$level]['name'] ?? ('VIP' . $level));
|
||||||
$level_desc = (string) ($levels[$level]['desc'] ?? '');
|
$level_desc = (string) ($levels[$level]['desc'] ?? '');
|
||||||
$vip_description = $level_desc !== '' ? $level_desc : __('尊享专属特权', 'aksrt');
|
$vip_description = $level_desc !== '' ? $level_desc : __('尊享专属特权', 'aksrt');
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ if ($level > 0) {
|
|||||||
$vip_state_label = __('生效中', 'aksrt');
|
$vip_state_label = __('生效中', 'aksrt');
|
||||||
$vip_expiry_text = sprintf(__('到期时间:%s', 'aksrt'), wp_date('Y年m月d日', $expires));
|
$vip_expiry_text = sprintf(__('到期时间:%s', 'aksrt'), wp_date('Y年m月d日', $expires));
|
||||||
if ($base_level > 0 && $base_level < $level) {
|
if ($base_level > 0 && $base_level < $level) {
|
||||||
$base_name = (string) ($levels[$base_level]['name'] ?? ('VIP' . $base_level));
|
$base_name = wp_strip_all_tags((string) ($levels[$base_level]['name'] ?? ('VIP' . $base_level)));
|
||||||
$base_expiry_text = $base_expires > 0
|
$base_expiry_text = $base_expires > 0
|
||||||
? wp_date('Y年m月d日', $base_expires)
|
? wp_date('Y年m月d日', $base_expires)
|
||||||
: __('永久', 'aksrt');
|
: __('永久', 'aksrt');
|
||||||
@@ -75,7 +75,7 @@ if ($level > 0) {
|
|||||||
$vip_state = 'expired';
|
$vip_state = 'expired';
|
||||||
$vip_state_label = __('已过期', 'aksrt');
|
$vip_state_label = __('已过期', 'aksrt');
|
||||||
$vip_level_number = $expired_level;
|
$vip_level_number = $expired_level;
|
||||||
$vip_level_name = (string) ($levels[$expired_level]['name'] ?? ('VIP' . $expired_level));
|
$vip_level_name = (string) ($levels[$expired_level]['name_html'] ?? $levels[$expired_level]['name'] ?? ('VIP' . $expired_level));
|
||||||
$expired_desc = (string) ($levels[$expired_level]['desc'] ?? '');
|
$expired_desc = (string) ($levels[$expired_level]['desc'] ?? '');
|
||||||
$vip_description = $expired_desc !== ''
|
$vip_description = $expired_desc !== ''
|
||||||
? $expired_desc
|
? $expired_desc
|
||||||
@@ -97,9 +97,9 @@ $status_html = '<div class="uc-vip-status" role="status">'
|
|||||||
. '<div class="uc-vip-status__main uc-vip-status__main--level-' . (int) $vip_level_number . '">'
|
. '<div class="uc-vip-status__main uc-vip-status__main--level-' . (int) $vip_level_number . '">'
|
||||||
// 未开通(level 0)不输出图标位:名称前不放通用证书图标。
|
// 未开通(level 0)不输出图标位:名称前不放通用证书图标。
|
||||||
. ($vip_level_number > 0 ? '<span class="uc-vip-status__icon" aria-hidden="true">' . $status_icon . '</span>' : '')
|
. ($vip_level_number > 0 ? '<span class="uc-vip-status__icon" aria-hidden="true">' . $status_icon . '</span>' : '')
|
||||||
. '<span class="uc-vip-status__level">' . esc_html($vip_level_name) . '</span>'
|
. '<span class="uc-vip-status__level">' . wp_kses_post($vip_level_name) . '</span>'
|
||||||
. '</div>'
|
. '</div>'
|
||||||
. '<div class="uc-vip-status__desc">' . esc_html($vip_description) . '</div>'
|
. '<div class="uc-vip-status__desc">' . wp_kses_post($vip_description) . '</div>'
|
||||||
. ($vip_expiry_text !== '' ? '<div class="uc-vip-status__expiry"><i class="fa fas fa-clock" aria-hidden="true"></i><span>' . esc_html($vip_expiry_text) . '</span></div>' : '')
|
. ($vip_expiry_text !== '' ? '<div class="uc-vip-status__expiry"><i class="fa fas fa-clock" aria-hidden="true"></i><span>' . esc_html($vip_expiry_text) . '</span></div>' : '')
|
||||||
. '</div>';
|
. '</div>';
|
||||||
|
|
||||||
@@ -109,14 +109,14 @@ foreach ([1, 2] as $lv) {
|
|||||||
if (empty($levels[$lv]['enabled'])) {
|
if (empty($levels[$lv]['enabled'])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$lv_name = (string) ($levels[$lv]['name'] ?? ('VIP' . $lv));
|
$lv_name = (string) ($levels[$lv]['name_html'] ?? $levels[$lv]['name'] ?? ('VIP' . $lv));
|
||||||
$lv_desc = (string) ($levels[$lv]['desc'] ?? '');
|
$lv_desc = (string) ($levels[$lv]['desc'] ?? '');
|
||||||
// Preserve admin equity HTML as-is (sanitize/wrap only; never re-split into items).
|
// Preserve admin equity HTML as-is (sanitize/wrap only; never re-split into items).
|
||||||
$equity_raw = (string) ($levels[$lv]['equity'] ?? '');
|
$equity_raw = (string) ($levels[$lv]['equity'] ?? '');
|
||||||
$equity = function_exists('aksrt_format_vip_equity_html')
|
$equity = function_exists('aksrt_format_vip_equity_html')
|
||||||
? aksrt_format_vip_equity_html($equity_raw)
|
? aksrt_format_vip_equity_html($equity_raw)
|
||||||
: '';
|
: '';
|
||||||
$cta = sprintf(__('开通%s', 'aksrt'), $lv_name);
|
$cta = __('开通', 'aksrt') . wp_kses_post($lv_name);
|
||||||
$vip_attr = $lv;
|
$vip_attr = $lv;
|
||||||
// 参考主题 zibpay_get_vip_card:续费/升级档位卡展示各自的一句话简介。
|
// 参考主题 zibpay_get_vip_card:续费/升级档位卡展示各自的一句话简介。
|
||||||
$card_action = 'pay';
|
$card_action = 'pay';
|
||||||
@@ -128,10 +128,10 @@ foreach ([1, 2] as $lv) {
|
|||||||
if (str_starts_with($btn_type, 'renew_')) {
|
if (str_starts_with($btn_type, 'renew_')) {
|
||||||
$renew_lv = (int) substr($btn_type, 6);
|
$renew_lv = (int) substr($btn_type, 6);
|
||||||
if ($lv === $renew_lv) {
|
if ($lv === $renew_lv) {
|
||||||
$cta = sprintf(__('续费%s', 'aksrt'), $lv_name);
|
$cta = __('续费', 'aksrt') . wp_kses_post($lv_name);
|
||||||
$card_action = 'renew';
|
$card_action = 'renew';
|
||||||
} elseif ($lv > $renew_lv) {
|
} elseif ($lv > $renew_lv) {
|
||||||
$cta = sprintf(__('升级%s', 'aksrt'), $lv_name);
|
$cta = __('升级', 'aksrt') . wp_kses_post($lv_name);
|
||||||
$card_action = 'upgrade';
|
$card_action = 'upgrade';
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
@@ -140,13 +140,13 @@ foreach ([1, 2] as $lv) {
|
|||||||
if ($lv <= $level) {
|
if ($lv <= $level) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$cta = sprintf(__('升级%s', 'aksrt'), $lv_name);
|
$cta = __('升级', 'aksrt') . wp_kses_post($lv_name);
|
||||||
$card_action = 'upgrade';
|
$card_action = 'upgrade';
|
||||||
$vip_attr = 2;
|
$vip_attr = 2;
|
||||||
} elseif ($level > 0 && $lv < $level) {
|
} elseif ($level > 0 && $lv < $level) {
|
||||||
continue;
|
continue;
|
||||||
} elseif ($level > 0 && $lv === $level) {
|
} elseif ($level > 0 && $lv === $level) {
|
||||||
$cta = sprintf(__('续费%s', 'aksrt'), $lv_name);
|
$cta = __('续费', 'aksrt') . wp_kses_post($lv_name);
|
||||||
$card_action = 'renew';
|
$card_action = 'renew';
|
||||||
}
|
}
|
||||||
if ($card_action === 'renew' && $renew_desc !== '') {
|
if ($card_action === 'renew' && $renew_desc !== '') {
|
||||||
@@ -158,14 +158,14 @@ foreach ([1, 2] as $lv) {
|
|||||||
$cards .= '<div class="aksrt-panel text-center pay-vip-info flex1 level-' . $lv . ' uc-vip-level-card">';
|
$cards .= '<div class="aksrt-panel text-center pay-vip-info flex1 level-' . $lv . ' uc-vip-level-card">';
|
||||||
$cards .= '<div class="relative uc-vip-level-card__inner">';
|
$cards .= '<div class="relative uc-vip-level-card__inner">';
|
||||||
$cards .= '<div class="v-iconbox mb10">';
|
$cards .= '<div class="v-iconbox mb10">';
|
||||||
$cards .= '<div class="v-name font-bold"><span class="v-icon">' . $lv_icon . '</span>' . esc_html($lv_name) . '</div>';
|
$cards .= '<div class="v-name font-bold"><span class="v-icon">' . $lv_icon . '</span>' . wp_kses_post($lv_name) . '</div>';
|
||||||
if ($card_desc !== '') {
|
if ($card_desc !== '') {
|
||||||
$cards .= '<div class="v-desc em09 muted-2-color">' . esc_html($card_desc) . '</div>';
|
$cards .= '<div class="v-desc em09 muted-2-color">' . wp_kses_post($card_desc) . '</div>';
|
||||||
}
|
}
|
||||||
$cards .= '</div>';
|
$cards .= '</div>';
|
||||||
$cards .= $equity;
|
$cards .= $equity;
|
||||||
$cards .= '<a class="padding-lg but jb-vip' . $lv . ' radius pay-vip uc-vip-cta" href="javascript:;" vip-level="' . (int) $vip_attr . '">'
|
$cards .= '<a class="padding-lg but jb-vip' . $lv . ' radius pay-vip uc-vip-cta" href="javascript:;" vip-level="' . (int) $vip_attr . '">'
|
||||||
. '<span>' . esc_html($cta) . '</span>'
|
. '<span>' . $cta . '</span>'
|
||||||
. ' <i class="fa fas fa-chevron-circle-right" aria-hidden="true"></i></a>';
|
. ' <i class="fa fas fa-chevron-circle-right" aria-hidden="true"></i></a>';
|
||||||
$cards .= '</div></div>';
|
$cards .= '</div></div>';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ class SchemaPayment {
|
|||||||
['key' => 'vip2', 'title' => '二级会员', 'titleKey' => 'pay_user_vip_2_name', 'titleFallback' => '二级会员', 'fields' => self::vip_level_tab_fields(2)],
|
['key' => 'vip2', 'title' => '二级会员', 'titleKey' => 'pay_user_vip_2_name', 'titleFallback' => '二级会员', 'fields' => self::vip_level_tab_fields(2)],
|
||||||
['key' => 'renew', 'title' => '会员续费', 'fields' => [
|
['key' => 'renew', 'title' => '会员续费', 'fields' => [
|
||||||
['key' => 'vip_renew', 'label' => '允许续费', 'type' => 'switch', 'default' => true, 'help' => '关闭后则不能续费,只能会员到期之后再购买。永久会员不需要续费'],
|
['key' => 'vip_renew', 'label' => '允许续费', 'type' => 'switch', 'default' => true, 'help' => '关闭后则不能续费,只能会员到期之后再购买。永久会员不需要续费'],
|
||||||
['key' => 'vip_renew_desc', 'label' => '续费介绍', 'type' => 'input', 'default' => '立即续费会员,畅享VIP权益', 'dependsOn' => ['key' => 'vip_renew', 'value' => true], 'help' => '续费会员的一句话简介'],
|
['key' => 'vip_renew_desc', 'label' => '续费介绍', 'type' => 'input', 'sanitize_type' => 'html', 'default' => '立即续费会员,畅享VIP权益', 'dependsOn' => ['key' => 'vip_renew', 'value' => true], 'help' => '续费会员的一句话简介,支持HTML'],
|
||||||
['key' => 'vip_renew_price_type', 'label' => '续费价格类型', 'type' => 'select', 'default' => 'discount', 'dependsOn' => ['key' => 'vip_renew', 'value' => true], 'options' => [
|
['key' => 'vip_renew_price_type', 'label' => '续费价格类型', 'type' => 'select', 'default' => 'discount', 'dependsOn' => ['key' => 'vip_renew', 'value' => true], 'options' => [
|
||||||
['label' => '相同价格', 'value' => 'same'],
|
['label' => '相同价格', 'value' => 'same'],
|
||||||
['label' => '折扣价格', 'value' => 'discount'],
|
['label' => '折扣价格', 'value' => 'discount'],
|
||||||
@@ -247,7 +247,7 @@ class SchemaPayment {
|
|||||||
]],
|
]],
|
||||||
['key' => 'upgrade', 'title' => '会员升级', 'fields' => [
|
['key' => 'upgrade', 'title' => '会员升级', 'fields' => [
|
||||||
['key' => 'vip_upgrade', 'label' => '允许升级', 'type' => 'switch', 'default' => true],
|
['key' => 'vip_upgrade', 'label' => '允许升级', 'type' => 'switch', 'default' => true],
|
||||||
['key' => 'vip_upgrade_desc', 'label' => '升级介绍', 'type' => 'input', 'default' => '升级VIP会员,享更多会员权益', 'dependsOn' => ['key' => 'vip_upgrade', 'value' => true], 'help' => '升级会员的一句话简介'],
|
['key' => 'vip_upgrade_desc', 'label' => '升级介绍', 'type' => 'input', 'sanitize_type' => 'html', 'default' => '升级VIP会员,享更多会员权益', 'dependsOn' => ['key' => 'vip_upgrade', 'value' => true], 'help' => '升级会员的一句话简介,支持HTML'],
|
||||||
[
|
[
|
||||||
'key' => 'vip_upgrade_product', 'label' => '升级价格', 'type' => 'accordion', 'default' => [
|
'key' => 'vip_upgrade_product', 'label' => '升级价格', 'type' => 'accordion', 'default' => [
|
||||||
'month_to_month_s' => true, 'unit_price' => '10.00', 'unit_tag' => '<i class="fa fas fa-fw fa-bolt"></i> 站长推荐', 'unit_tag_class' => 'jb-yellow',
|
'month_to_month_s' => true, 'unit_price' => '10.00', 'unit_tag' => '<i class="fa fas fa-fw fa-bolt"></i> 站长推荐', 'unit_tag_class' => 'jb-yellow',
|
||||||
@@ -311,7 +311,7 @@ class SchemaPayment {
|
|||||||
['key' => 'pay_vip_points_exchange_desc', 'label' => '兑换说明', 'type' => 'textarea', 'sanitize_type' => 'html', 'default' => '', 'rows' => 2, 'dependsOn' => ['key' => 'pay_vip_points_exchange_s', 'value' => true], 'help' => '用户使用积分兑换时显示的内容,建议为提醒事项、兑换协议等,支持HTML代码'],
|
['key' => 'pay_vip_points_exchange_desc', 'label' => '兑换说明', 'type' => 'textarea', 'sanitize_type' => 'html', 'default' => '', 'rows' => 2, 'dependsOn' => ['key' => 'pay_vip_points_exchange_s', 'value' => true], 'help' => '用户使用积分兑换时显示的内容,建议为提醒事项、兑换协议等,支持HTML代码'],
|
||||||
]],
|
]],
|
||||||
['key' => 'modal-copy', 'title' => '界面文案', 'fields' => [
|
['key' => 'modal-copy', 'title' => '界面文案', 'fields' => [
|
||||||
['key' => 'pay_user_vip_desc', 'label' => '会员介绍', 'type' => 'input', 'default' => '开通VIP会员,享受会员专属折扣以及多项特权', 'dependsOn' => ['any' => [['key' => 'pay_user_vip_1_s', 'value' => true], ['key' => 'pay_user_vip_2_s', 'value' => true]]]],
|
['key' => 'pay_user_vip_desc', 'label' => '会员介绍', 'type' => 'input', 'sanitize_type' => 'html', 'default' => '开通VIP会员,享受会员专属折扣以及多项特权', 'dependsOn' => ['any' => [['key' => 'pay_user_vip_1_s', 'value' => true], ['key' => 'pay_user_vip_2_s', 'value' => true]]], 'help' => '显示在开通界面顶部的一句话简介,支持HTML'],
|
||||||
['key' => 'pay_user_vip_more', 'label' => '开通会员更多内容', 'type' => 'textarea', 'sanitize_type' => 'html', 'default' => '<div>购买后不支持退款</div><div>VIP权益仅适用于本站</div><div>欢迎与站长联系</div>', 'rows' => 4, 'dependsOn' => ['any' => [['key' => 'pay_user_vip_1_s', 'value' => true], ['key' => 'pay_user_vip_2_s', 'value' => true]]]],
|
['key' => 'pay_user_vip_more', 'label' => '开通会员更多内容', 'type' => 'textarea', 'sanitize_type' => 'html', 'default' => '<div>购买后不支持退款</div><div>VIP权益仅适用于本站</div><div>欢迎与站长联系</div>', 'rows' => 4, 'dependsOn' => ['any' => [['key' => 'pay_user_vip_1_s', 'value' => true], ['key' => 'pay_user_vip_2_s', 'value' => true]]]],
|
||||||
]],
|
]],
|
||||||
],
|
],
|
||||||
@@ -684,9 +684,9 @@ class SchemaPayment {
|
|||||||
$dep = ['key' => 'pay_user_vip_' . $level . '_s', 'value' => true];
|
$dep = ['key' => 'pay_user_vip_' . $level . '_s', 'value' => true];
|
||||||
$equity = '<li>全站资源折扣购买</li><li>部分内容免费阅读</li><li>一对一技术指导</li><li>VIP用户专属QQ群</li>';
|
$equity = '<li>全站资源折扣购买</li><li>部分内容免费阅读</li><li>一对一技术指导</li><li>VIP用户专属QQ群</li>';
|
||||||
return [
|
return [
|
||||||
['key' => 'pay_user_vip_' . $level . '_name', 'label' => '会员显示名称', 'type' => 'input', 'default' => $level === 1 ? '黄金会员' : '钻石会员', 'dependsOn' => $dep, 'help' => '会员名称(例如“黄金会员”、“超级会员”)'],
|
['key' => 'pay_user_vip_' . $level . '_name', 'label' => '会员显示名称', 'type' => 'input', 'sanitize_type' => 'html', 'default' => $level === 1 ? '黄金会员' : '钻石会员', 'dependsOn' => $dep, 'help' => '会员名称,支持HTML'],
|
||||||
['key' => 'pay_user_vip_' . $level . '_desc', 'label' => '一句话简介', 'type' => 'input', 'default' => '尊享专属特权', 'dependsOn' => $dep],
|
['key' => 'pay_user_vip_' . $level . '_desc', 'label' => '一句话简介', 'type' => 'input', 'sanitize_type' => 'html', 'default' => '尊享专属特权', 'dependsOn' => $dep, 'help' => '支持HTML'],
|
||||||
['key' => 'pay_user_vip_' . $level . '_equity', 'label' => '会员权益 HTML', 'type' => 'textarea', 'sanitize_type' => 'html', 'default' => $equity, 'rows' => 4, 'dependsOn' => $dep, 'help' => '建议使用 <li> 列表项'],
|
['key' => 'pay_user_vip_' . $level . '_equity', 'label' => '会员权益 HTML', 'type' => 'textarea', 'sanitize_type' => 'html', 'default' => $equity, 'rows' => 4, 'dependsOn' => $dep, 'help' => '支持安全HTML,建议使用 <li> 列表项'],
|
||||||
['key' => 'vip_' . $level . '_product_s', 'label' => '购买会员', 'type' => 'switch', 'default' => true, 'dependsOn' => $dep, 'help' => '关闭后则不能付费购买会员,但可通过积分兑换会员,请确保已开启积分以及积分兑换会员功能'],
|
['key' => 'vip_' . $level . '_product_s', 'label' => '购买会员', 'type' => 'switch', 'default' => true, 'dependsOn' => $dep, 'help' => '关闭后则不能付费购买会员,但可通过积分兑换会员,请确保已开启积分以及积分兑换会员功能'],
|
||||||
[
|
[
|
||||||
'key' => 'vip_' . $level . '_product', 'label' => '会员商品', 'type' => 'group', 'max' => 8,
|
'key' => 'vip_' . $level . '_product', 'label' => '会员商品', 'type' => 'group', 'max' => 8,
|
||||||
|
|||||||
@@ -38,8 +38,9 @@ trait VipServiceModal {
|
|||||||
: wp_kses_post($equity);
|
: wp_kses_post($equity);
|
||||||
$level_view[] = [
|
$level_view[] = [
|
||||||
'level' => $level,
|
'level' => $level,
|
||||||
'name' => (string) ($levels[$level]['name'] ?? ('VIP' . $level)),
|
'name' => wp_kses_post((string) ($levels[$level]['name_html'] ?? $levels[$level]['name'] ?? ('VIP' . $level))),
|
||||||
'desc' => (string) ($levels[$level]['desc'] ?? ''),
|
'name_text' => (string) ($levels[$level]['name'] ?? ('VIP' . $level)),
|
||||||
|
'desc' => wp_kses_post((string) ($levels[$level]['desc'] ?? '')),
|
||||||
'equity' => $equity,
|
'equity' => $equity,
|
||||||
'img_icon' => esc_url_raw((string) ($levels[$level]['img_icon'] ?? '')),
|
'img_icon' => esc_url_raw((string) ($levels[$level]['img_icon'] ?? '')),
|
||||||
];
|
];
|
||||||
@@ -221,10 +222,10 @@ trait VipServiceModal {
|
|||||||
$duration = ($is_permanent || $pkg_time <= 0)
|
$duration = ($is_permanent || $pkg_time <= 0)
|
||||||
? esc_html__('永久', 'aksrt')
|
? esc_html__('永久', 'aksrt')
|
||||||
: (string) $pkg_time . '<span class="px12">' . esc_html($pkg_unit === 'month' ? __('个月', 'aksrt') : __('天', 'aksrt')) . '</span>';
|
: (string) $pkg_time . '<span class="px12">' . esc_html($pkg_unit === 'month' ? __('个月', 'aksrt') : __('天', 'aksrt')) . '</span>';
|
||||||
$level_name = (string) ($levels[$level]['name'] ?? ('VIP' . $level));
|
$level_name = (string) ($levels[$level]['name_html'] ?? $levels[$level]['name'] ?? ('VIP' . $level));
|
||||||
|
|
||||||
$cards .= '<div class="vip-card vip-theme' . $level . ($index === 0 ? ' active' : '') . ' " data-for="product_id" data-value="' . $package_id . '">'
|
$cards .= '<div class="vip-card vip-theme' . $level . ($index === 0 ? ' active' : '') . ' " data-for="product_id" data-value="' . $package_id . '">'
|
||||||
. '<span class=" ">' . $this->points_exchange_card_icon($levels[$level] ?? []) . esc_html($level_name) . '</span>'
|
. '<span class=" ">' . $this->points_exchange_card_icon($levels[$level] ?? []) . wp_kses_post($level_name) . '</span>'
|
||||||
. '<div class="flex jsb ac hh em16 mt10">'
|
. '<div class="flex jsb ac hh em16 mt10">'
|
||||||
. '<span class="shrink0 mr10">' . $duration . '</span>'
|
. '<span class="shrink0 mr10">' . $duration . '</span>'
|
||||||
. '<span class="shrink0">' . $points . '<span class="px12">' . esc_html($currency) . '</span></span>'
|
. '<span class="shrink0">' . $points . '<span class="px12">' . esc_html($currency) . '</span></span>'
|
||||||
@@ -292,14 +293,14 @@ trait VipServiceModal {
|
|||||||
* @param array<string,mixed> $level_settings 等级设置(含 name / img_icon)。
|
* @param array<string,mixed> $level_settings 等级设置(含 name / img_icon)。
|
||||||
*/
|
*/
|
||||||
private function points_exchange_card_icon(array $level_settings): string {
|
private function points_exchange_card_icon(array $level_settings): string {
|
||||||
$name = (string) ($level_settings['name'] ?? '');
|
$name_text = (string) ($level_settings['name'] ?? '');
|
||||||
$icon_url = (string) ($level_settings['img_icon'] ?? '');
|
$icon_url = (string) ($level_settings['img_icon'] ?? '');
|
||||||
if ($icon_url === '') {
|
if ($icon_url === '') {
|
||||||
return '<i class="fa fas fa-certificate mr3" aria-hidden="true"></i>';
|
return '<i class="fa fas fa-certificate mr3" aria-hidden="true"></i>';
|
||||||
}
|
}
|
||||||
|
|
||||||
return '<img' . aksrt_get_lazy_attr('lazy_other', $icon_url, 'img-icon mr3')
|
return '<img' . aksrt_get_lazy_attr('lazy_other', $icon_url, 'img-icon mr3')
|
||||||
. ' title="' . esc_attr($name) . '" alt="' . esc_attr($name) . '">';
|
. ' title="' . esc_attr($name_text) . '" alt="' . esc_attr($name_text) . '">';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -330,26 +331,27 @@ trait VipServiceModal {
|
|||||||
if (empty($levels[$level]['enabled'])) {
|
if (empty($levels[$level]['enabled'])) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
$name = (string) ($levels[$level]['name'] ?? ('VIP' . $level));
|
$name = wp_kses_post((string) ($levels[$level]['name_html'] ?? $levels[$level]['name'] ?? ('VIP' . $level)));
|
||||||
|
$name_text = (string) ($levels[$level]['name'] ?? ('VIP' . $level));
|
||||||
$equity = (string) ($levels[$level]['equity'] ?? '');
|
$equity = (string) ($levels[$level]['equity'] ?? '');
|
||||||
$cls = $user_id > 0 ? ' pay-vip' : ' signin-loader';
|
$cls = $user_id > 0 ? ' pay-vip' : ' signin-loader';
|
||||||
|
|
||||||
$equity_html = $equity !== ''
|
$equity_html = $equity !== '' && function_exists('aksrt_format_vip_equity_html')
|
||||||
? '<ul class="mb10 relative">' . wp_kses_post($equity) . '</ul>'
|
? aksrt_format_vip_equity_html($equity, 'mb10 relative')
|
||||||
: '';
|
: ($equity !== '' ? '<div class="v-equity mb10 relative">' . wp_kses_post($equity) . '</div>' : '');
|
||||||
|
|
||||||
$icon_url = esc_url((string) ($levels[$level]['img_icon'] ?? ''));
|
$icon_url = esc_url((string) ($levels[$level]['img_icon'] ?? ''));
|
||||||
$icon = $icon_url !== ''
|
$icon = $icon_url !== ''
|
||||||
? '<img' . aksrt_get_lazy_attr('lazy_other', $icon_url, 'img-icon') . ' title="' . esc_attr($name) . '" alt="' . esc_attr($name) . '" decoding="async">'
|
? '<img' . aksrt_get_lazy_attr('lazy_other', $icon_url, 'img-icon') . ' title="' . esc_attr($name_text) . '" alt="' . esc_attr($name_text) . '" decoding="async">'
|
||||||
: '<i class="fa fas fa-gem" aria-hidden="true"></i>';
|
: '<i class="fa fas fa-gem" aria-hidden="true"></i>';
|
||||||
$html = '<div class="vip-card pointer level-' . $level . ' vip-theme' . $level . $cls . '" vip-level="' . $level . '">';
|
$html = '<div class="vip-card pointer level-' . $level . ' vip-theme' . $level . $cls . '" vip-level="' . $level . '">';
|
||||||
$html .= '<div class="abs-center vip-baicon" aria-hidden="true">' . $icon . '</div>';
|
$html .= '<div class="abs-center vip-baicon" aria-hidden="true">' . $icon . '</div>';
|
||||||
$html .= '<div class="vip-img abs-right">' . $icon . '</div>';
|
$html .= '<div class="vip-img abs-right">' . $icon . '</div>';
|
||||||
$html .= '<div class="relative">';
|
$html .= '<div class="relative">';
|
||||||
$html .= '<div class="vip-name mb10"><span class="mr6">' . $icon . '</span>' . esc_html($name) . '</div>';
|
$html .= '<div class="vip-name mb10"><span class="mr6">' . $icon . '</span>' . wp_kses_post($name) . '</div>';
|
||||||
$html .= $equity_html;
|
$html .= $equity_html;
|
||||||
$html .= '<a class="but jb-blue radius payvip-button" href="javascript:;">'
|
$html .= '<a class="but jb-blue radius payvip-button" href="javascript:;">'
|
||||||
. esc_html(sprintf(__('开通%s', 'aksrt'), $name))
|
. wp_kses_post(sprintf(__('开通%s', 'aksrt'), $name))
|
||||||
. '<span class="ml6"><i class="fa fas fa-chevron-circle-right" aria-hidden="true"></i></span></a>';
|
. '<span class="ml6"><i class="fa fas fa-chevron-circle-right" aria-hidden="true"></i></span></a>';
|
||||||
$html .= '</div></div>';
|
$html .= '</div></div>';
|
||||||
return $html;
|
return $html;
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ trait VipServicePurchase {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$name = (string) ($levels[$lv]['name'] ?? ('VIP' . $lv));
|
$name = trim(wp_strip_all_tags((string) ($levels[$lv]['name'] ?? ('VIP' . $lv))));
|
||||||
$items = [];
|
$items = [];
|
||||||
// 自定义续费商品时按续费商品组计价,否则按会员商品组计价。
|
// 自定义续费商品时按续费商品组计价,否则按会员商品组计价。
|
||||||
$level_action = 'pay';
|
$level_action = 'pay';
|
||||||
|
|||||||
@@ -170,13 +170,17 @@ class PaymentRuntime {
|
|||||||
$vip2_enabled = !empty($settings['pay_user_vip_2_s']);
|
$vip2_enabled = !empty($settings['pay_user_vip_2_s']);
|
||||||
$enabled = $vip1_enabled || $vip2_enabled;
|
$enabled = $vip1_enabled || $vip2_enabled;
|
||||||
|
|
||||||
$name1 = trim((string) ($settings['pay_user_vip_1_name'] ?? ''));
|
$name1_html = trim(wp_kses_post((string) ($settings['pay_user_vip_1_name'] ?? '')));
|
||||||
$name2 = trim((string) ($settings['pay_user_vip_2_name'] ?? ''));
|
$name2_html = trim(wp_kses_post((string) ($settings['pay_user_vip_2_name'] ?? '')));
|
||||||
|
$name1 = sanitize_text_field(wp_strip_all_tags($name1_html));
|
||||||
|
$name2 = sanitize_text_field(wp_strip_all_tags($name2_html));
|
||||||
if ($name1 === '') {
|
if ($name1 === '') {
|
||||||
$name1 = __('黄金会员', 'aksrt');
|
$name1 = __('黄金会员', 'aksrt');
|
||||||
|
$name1_html = $name1;
|
||||||
}
|
}
|
||||||
if ($name2 === '') {
|
if ($name2 === '') {
|
||||||
$name2 = __('钻石会员', 'aksrt');
|
$name2 = __('钻石会员', 'aksrt');
|
||||||
|
$name2_html = $name2;
|
||||||
}
|
}
|
||||||
|
|
||||||
$badge = $vip2_enabled ? $name2 : ($vip1_enabled ? $name1 : __('VIP', 'aksrt'));
|
$badge = $vip2_enabled ? $name2 : ($vip1_enabled ? $name1 : __('VIP', 'aksrt'));
|
||||||
@@ -186,20 +190,8 @@ class PaymentRuntime {
|
|||||||
$renew_price_type = 'discount';
|
$renew_price_type = 'discount';
|
||||||
}
|
}
|
||||||
|
|
||||||
$desc = sanitize_text_field((string) ($settings['pay_user_vip_desc'] ?? __('开通VIP会员,享受会员专属折扣以及多项特权', 'aksrt')));
|
$desc = wp_kses_post((string) ($settings['pay_user_vip_desc'] ?? __('开通VIP会员,享受会员专属折扣以及多项特权', 'aksrt')));
|
||||||
$more = (string) ($settings['pay_user_vip_more'] ?? '<div>购买后不支持退款</div><div>VIP权益仅适用于本站</div><div>欢迎与站长联系</div>');
|
$more = (string) ($settings['pay_user_vip_more'] ?? '<div>购买后不支持退款</div><div>VIP权益仅适用于本站</div><div>欢迎与站长联系</div>');
|
||||||
$allowed_equity = [
|
|
||||||
'li' => [],
|
|
||||||
'ul' => [],
|
|
||||||
'ol' => [],
|
|
||||||
'strong' => [],
|
|
||||||
'b' => [],
|
|
||||||
'em' => [],
|
|
||||||
'i' => ['class' => true],
|
|
||||||
'span' => ['class' => true],
|
|
||||||
'br' => [],
|
|
||||||
'a' => ['href' => true, 'target' => true, 'rel' => true, 'class' => true],
|
|
||||||
];
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'enabled' => $enabled,
|
'enabled' => $enabled,
|
||||||
@@ -215,8 +207,9 @@ class PaymentRuntime {
|
|||||||
1 => [
|
1 => [
|
||||||
'enabled' => $vip1_enabled,
|
'enabled' => $vip1_enabled,
|
||||||
'name' => $name1,
|
'name' => $name1,
|
||||||
'desc' => sanitize_text_field((string) ($settings['pay_user_vip_1_desc'] ?? __('尊享专属特权', 'aksrt'))),
|
'name_html' => $name1_html,
|
||||||
'equity' => wp_kses((string) (($settings['pay_user_vip_1_equity'] ?? '') !== '' ? $settings['pay_user_vip_1_equity'] : '<li>全站资源折扣购买</li><li>部分内容免费阅读</li><li>一对一技术指导</li><li>VIP用户专属QQ群</li>'), $allowed_equity),
|
'desc' => wp_kses_post((string) ($settings['pay_user_vip_1_desc'] ?? __('尊享专属特权', 'aksrt'))),
|
||||||
|
'equity' => wp_kses_post((string) (($settings['pay_user_vip_1_equity'] ?? '') !== '' ? $settings['pay_user_vip_1_equity'] : '<li>全站资源折扣购买</li><li>部分内容免费阅读</li><li>一对一技术指导</li><li>VIP用户专属QQ群</li>')),
|
||||||
'img_icon' => self::get_vip_icon_url(1, (string) ($settings['vip_1_img_icon'] ?? '')),
|
'img_icon' => self::get_vip_icon_url(1, (string) ($settings['vip_1_img_icon'] ?? '')),
|
||||||
'product_s' => !isset($settings['vip_1_product_s']) || !empty($settings['vip_1_product_s']),
|
'product_s' => !isset($settings['vip_1_product_s']) || !empty($settings['vip_1_product_s']),
|
||||||
'products' => self::normalize_vip_product_items($settings['vip_1_product'] ?? null, self::vip_product_defaults()),
|
'products' => self::normalize_vip_product_items($settings['vip_1_product'] ?? null, self::vip_product_defaults()),
|
||||||
@@ -226,8 +219,9 @@ class PaymentRuntime {
|
|||||||
2 => [
|
2 => [
|
||||||
'enabled' => $vip2_enabled,
|
'enabled' => $vip2_enabled,
|
||||||
'name' => $name2,
|
'name' => $name2,
|
||||||
'desc' => sanitize_text_field((string) ($settings['pay_user_vip_2_desc'] ?? __('尊享专属特权', 'aksrt'))),
|
'name_html' => $name2_html,
|
||||||
'equity' => wp_kses((string) (($settings['pay_user_vip_2_equity'] ?? '') !== '' ? $settings['pay_user_vip_2_equity'] : '<li>全站资源折扣购买</li><li>部分内容免费阅读</li><li>一对一技术指导</li><li>VIP用户专属QQ群</li>'), $allowed_equity),
|
'desc' => wp_kses_post((string) ($settings['pay_user_vip_2_desc'] ?? __('尊享专属特权', 'aksrt'))),
|
||||||
|
'equity' => wp_kses_post((string) (($settings['pay_user_vip_2_equity'] ?? '') !== '' ? $settings['pay_user_vip_2_equity'] : '<li>全站资源折扣购买</li><li>部分内容免费阅读</li><li>一对一技术指导</li><li>VIP用户专属QQ群</li>')),
|
||||||
'img_icon' => self::get_vip_icon_url(2, (string) ($settings['vip_2_img_icon'] ?? '')),
|
'img_icon' => self::get_vip_icon_url(2, (string) ($settings['vip_2_img_icon'] ?? '')),
|
||||||
'product_s' => !isset($settings['vip_2_product_s']) || !empty($settings['vip_2_product_s']),
|
'product_s' => !isset($settings['vip_2_product_s']) || !empty($settings['vip_2_product_s']),
|
||||||
'products' => self::normalize_vip_product_items($settings['vip_2_product'] ?? null, self::vip_product_defaults()),
|
'products' => self::normalize_vip_product_items($settings['vip_2_product'] ?? null, self::vip_product_defaults()),
|
||||||
@@ -240,7 +234,7 @@ class PaymentRuntime {
|
|||||||
],
|
],
|
||||||
'renew' => [
|
'renew' => [
|
||||||
'enabled' => !isset($settings['vip_renew']) || !empty($settings['vip_renew']),
|
'enabled' => !isset($settings['vip_renew']) || !empty($settings['vip_renew']),
|
||||||
'desc' => sanitize_text_field((string) ($settings['vip_renew_desc'] ?? '立即续费会员,畅享VIP权益')),
|
'desc' => wp_kses_post((string) ($settings['vip_renew_desc'] ?? '立即续费会员,畅享VIP权益')),
|
||||||
'price_type' => $renew_price_type,
|
'price_type' => $renew_price_type,
|
||||||
'discount' => max(0.1, min(9.9, (float) ($settings['vip_renew_discount'] ?? 8))),
|
'discount' => max(0.1, min(9.9, (float) ($settings['vip_renew_discount'] ?? 8))),
|
||||||
'reduce' => self::money_decimal($settings['vip_renew_reduce'] ?? '20.00', '20.00', 50000),
|
'reduce' => self::money_decimal($settings['vip_renew_reduce'] ?? '20.00', '20.00', 50000),
|
||||||
@@ -834,7 +828,7 @@ class PaymentRuntime {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
'enabled' => !isset($settings['vip_upgrade']) || !empty($settings['vip_upgrade']),
|
'enabled' => !isset($settings['vip_upgrade']) || !empty($settings['vip_upgrade']),
|
||||||
'desc' => sanitize_text_field((string) ($settings['vip_upgrade_desc'] ?? '升级VIP会员,享更多会员权益')),
|
'desc' => wp_kses_post((string) ($settings['vip_upgrade_desc'] ?? '升级VIP会员,享更多会员权益')),
|
||||||
'month_to_month_s' => $switch($raw['month_to_month_s'] ?? null, true),
|
'month_to_month_s' => $switch($raw['month_to_month_s'] ?? null, true),
|
||||||
'unit_price' => $money($raw['unit_price'] ?? null, '10.00'),
|
'unit_price' => $money($raw['unit_price'] ?? null, '10.00'),
|
||||||
'unit_tag' => $tag($raw['unit_tag'] ?? null, '<i class="fa fas fa-fw fa-bolt"></i> 站长推荐'),
|
'unit_tag' => $tag($raw['unit_tag'] ?? null, '<i class="fa fas fa-fw fa-bolt"></i> 站长推荐'),
|
||||||
|
|||||||
@@ -156,9 +156,8 @@ test('header VIP hover cards match the legacy markup and style contract', () =>
|
|||||||
assert.match(vipCardRenderer, /<div class=\"abs-center vip-baicon\"[^>]*>/)
|
assert.match(vipCardRenderer, /<div class=\"abs-center vip-baicon\"[^>]*>/)
|
||||||
assert.match(vipCardRenderer, /<div class=\"vip-img abs-right\">/)
|
assert.match(vipCardRenderer, /<div class=\"vip-img abs-right\">/)
|
||||||
assert.match(vipCardRenderer, /<div class=\"vip-name mb10\"><span class=\"mr6\">/)
|
assert.match(vipCardRenderer, /<div class=\"vip-name mb10\"><span class=\"mr6\">/)
|
||||||
assert.match(vipCardRenderer, /<ul class=\"mb10 relative\">/)
|
assert.match(vipCardRenderer, /aksrt_format_vip_equity_html\(\$equity, 'mb10 relative'\)/)
|
||||||
assert.doesNotMatch(vipCardRenderer, /vip-name__(?:icon|label)/)
|
assert.doesNotMatch(vipCardRenderer, /vip-name__(?:icon|label)/)
|
||||||
assert.doesNotMatch(vipCardRenderer, /aksrt_format_vip_equity_html\(\$equity, 'mb10 relative'\)/)
|
|
||||||
|
|
||||||
assert.doesNotMatch(vipModalCss, /Header hover VIP cards/)
|
assert.doesNotMatch(vipModalCss, /Header hover VIP cards/)
|
||||||
assert.doesNotMatch(vipModalCss, /\.navbar-vip-hover\.hover-show/)
|
assert.doesNotMatch(vipModalCss, /\.navbar-vip-hover\.hover-show/)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ test('弹窗文案字段更名为会员介绍/开通会员更多内容并改为
|
|||||||
// 会员介绍为单行 input(zibll text「一句话简介」);开通会员更多内容为 rows 4 的 HTML textarea。
|
// 会员介绍为单行 input(zibll text「一句话简介」);开通会员更多内容为 rows 4 的 HTML textarea。
|
||||||
assert.match(
|
assert.match(
|
||||||
vipSchemaJs,
|
vipSchemaJs,
|
||||||
/key: 'pay_user_vip_desc',\s*\n\s*label: '会员介绍',\s*\n\s*type: 'input',\s*\n\s*default: '开通VIP会员,享受会员专属折扣以及多项特权'/
|
/key: 'pay_user_vip_desc',\s*\n\s*label: '会员介绍',\s*\n\s*type: 'input',\s*\n\s*sanitize_type: 'html',\s*\n\s*default: '开通VIP会员,享受会员专属折扣以及多项特权'/
|
||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
vipSchemaJs,
|
vipSchemaJs,
|
||||||
@@ -60,7 +60,7 @@ test('弹窗文案字段更名为会员介绍/开通会员更多内容并改为
|
|||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
schemaPhp,
|
schemaPhp,
|
||||||
/'key' => 'pay_user_vip_desc', 'label' => '会员介绍', 'type' => 'input', 'default' => '开通VIP会员,享受会员专属折扣以及多项特权'/
|
/'key' => 'pay_user_vip_desc', 'label' => '会员介绍', 'type' => 'input', 'sanitize_type' => 'html', 'default' => '开通VIP会员,享受会员专属折扣以及多项特权'/
|
||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
schemaPhp,
|
schemaPhp,
|
||||||
@@ -141,7 +141,7 @@ test('文案默认值三处一致并对齐 zibll', () => {
|
|||||||
assert.ok(schemaPhp.includes(`'default' => '${DESC_DEFAULT}'`));
|
assert.ok(schemaPhp.includes(`'default' => '${DESC_DEFAULT}'`));
|
||||||
assert.ok(schemaPhp.includes(`'default' => '${MORE_DEFAULT}'`));
|
assert.ok(schemaPhp.includes(`'default' => '${MORE_DEFAULT}'`));
|
||||||
// PaymentRuntime 运行时回退(get_all 不合并 schema 默认值,回退即实际默认值)。
|
// PaymentRuntime 运行时回退(get_all 不合并 schema 默认值,回退即实际默认值)。
|
||||||
assert.ok(paymentRuntime.includes(`$desc = sanitize_text_field((string) ($settings['pay_user_vip_desc'] ?? __('${DESC_DEFAULT}', 'aksrt')))`));
|
assert.ok(paymentRuntime.includes(`$desc = wp_kses_post((string) ($settings['pay_user_vip_desc'] ?? __('${DESC_DEFAULT}', 'aksrt')))`));
|
||||||
assert.ok(paymentRuntime.includes(`$more = (string) ($settings['pay_user_vip_more'] ?? '${MORE_DEFAULT}')`));
|
assert.ok(paymentRuntime.includes(`$more = (string) ($settings['pay_user_vip_more'] ?? '${MORE_DEFAULT}')`));
|
||||||
assert.ok(paymentRuntime.includes("$name1 = __('黄金会员', 'aksrt');"));
|
assert.ok(paymentRuntime.includes("$name1 = __('黄金会员', 'aksrt');"));
|
||||||
assert.ok(paymentRuntime.includes("$name2 = __('钻石会员', 'aksrt');"));
|
assert.ok(paymentRuntime.includes("$name2 = __('钻石会员', 'aksrt');"));
|
||||||
@@ -154,19 +154,41 @@ test('文案默认值三处一致并对齐 zibll', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('权益与卡密说明字段保存时保留 HTML', () => {
|
test('会员等级名称、简介、续费和升级介绍都声明 HTML 净化', () => {
|
||||||
// 权益字段已并入「会员购买选项」的等级 Tab,标签统一为「会员权益 HTML」。
|
const levelFieldsJs = slice(vipSchemaJs, 'function vipLevelTabFields', 'export const vipSchema');
|
||||||
assert.ok(vipSchemaJs.includes("key: `pay_user_vip_${level}_equity`, label: '会员权益 HTML', type: 'textarea', sanitize_type: 'html'"));
|
const levelFieldsPhp = slice(schemaPhp, 'private static function vip_level_tab_fields', 'private static function vip_product_defaults');
|
||||||
assert.ok(schemaPhp.includes("['key' => 'pay_user_vip_' . $level . '_equity', 'label' => '会员权益 HTML', 'type' => 'textarea', 'sanitize_type' => 'html'"));
|
assert.match(levelFieldsJs, /pay_user_vip_\$\{level\}_name[^\n]*sanitize_type: 'html'/);
|
||||||
assert.ok(rechargeJs.includes("key: 'pay_vip_pass_charge_desc', label: '卡密兑换会员说明', type: 'textarea', sanitize_type: 'html'"));
|
assert.match(levelFieldsJs, /pay_user_vip_\$\{level\}_desc[^\n]*sanitize_type: 'html'/);
|
||||||
assert.ok(schemaPhp.includes("'key' => 'pay_vip_pass_charge_desc', 'label' => '卡密兑换会员说明', 'type' => 'textarea', 'sanitize_type' => 'html'"));
|
assert.match(levelFieldsPhp, /pay_user_vip_' \. \$level \. '_name'[^\n]*'sanitize_type' => 'html'/);
|
||||||
|
assert.match(levelFieldsPhp, /pay_user_vip_' \. \$level \. '_desc'[^\n]*'sanitize_type' => 'html'/);
|
||||||
|
assert.match(vipSchemaJs, /key: 'vip_renew_desc',[\s\S]*?sanitize_type: 'html'/);
|
||||||
|
assert.match(schemaPhp, /'key' => 'vip_renew_desc'[^\n]*'sanitize_type' => 'html'/);
|
||||||
|
assert.match(vipSchemaJs, /key: 'vip_upgrade_desc',[^\n]*sanitize_type: 'html'/);
|
||||||
|
assert.match(schemaPhp, /'key' => 'vip_upgrade_desc'[^\n]*'sanitize_type' => 'html'/);
|
||||||
|
assert.match(vipSchemaJs, /key: 'pay_user_vip_desc',[\s\S]*?sanitize_type: 'html'/);
|
||||||
|
assert.match(schemaPhp, /'key' => 'pay_user_vip_desc'[^\n]*'sanitize_type' => 'html'/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('会员介绍运行时按单行文本净化', () => {
|
test('权益和促销标签 HTML 通过安全净化及 HTML 渲染保留', () => {
|
||||||
// 只收敛 VIP 设置函数:充值等其它设置的 $desc 与本文无关。
|
|
||||||
const vipSettings = slice(paymentRuntime, 'public static function get_payment_vip_settings', 'public static function get_payment_revenue_settings');
|
const vipSettings = slice(paymentRuntime, 'public static function get_payment_vip_settings', 'public static function get_payment_revenue_settings');
|
||||||
assert.ok(vipSettings.includes("$desc = sanitize_text_field((string) ($settings['pay_user_vip_desc']"));
|
assert.match(vipSettings, /'equity'\s*=> wp_kses_post\(/);
|
||||||
assert.ok(!vipSettings.includes('$desc = sanitize_textarea_field'));
|
assert.doesNotMatch(vipSettings, /\$allowed_equity/);
|
||||||
|
const equityHelper = read('inc', 'helpers', 'aksrt-vip.php');
|
||||||
|
assert.match(equityHelper, /\$clean = trim\(wp_kses_post\(\$raw\)\)/);
|
||||||
|
assert.match(payVipJs, /if \(item\.discount_tag\) append\(product, htmlNode\(/);
|
||||||
|
assert.match(payVipJs, /append\(info, htmlNode\("div", "", levelInfo\.equity\)\)/);
|
||||||
|
assert.match(payVipJs, /htmlNode\("div", "v-name", levelInfo\.name \|\| group\.name/);
|
||||||
|
assert.match(payVipJs, /htmlNode\("div", "v-desc em09", levelInfo\.desc/);
|
||||||
|
assert.match(vipTabPhp, /wp_kses_post\(\$card_desc\)/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('会员介绍按 post 级 HTML 安全净化并在弹窗与用户中心渲染', () => {
|
||||||
|
const vipSettings = slice(paymentRuntime, 'public static function get_payment_vip_settings', 'public static function get_payment_revenue_settings');
|
||||||
|
assert.match(vipSettings, /\$desc = wp_kses_post\(\(string\) \(\$settings\['pay_user_vip_desc'\]/);
|
||||||
|
assert.match(vipSettings, /'desc'\s+=> wp_kses_post\(\(string\) \(\$settings\['pay_user_vip_1_desc'\]/);
|
||||||
|
assert.match(vipSettings, /'desc'\s+=> wp_kses_post\(\(string\) \(\$settings\['pay_user_vip_2_desc'\]/);
|
||||||
|
assert.match(vipTabPhp, /wp_kses_post\(\$vip_description\)/);
|
||||||
|
assert.match(payVipJs, /htmlNode\("div", "muted-2-color em09", catalog\.desc\)/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('开通会员更多内容只在弹窗内渲染,不裸露在用户中心页面', () => {
|
test('开通会员更多内容只在弹窗内渲染,不裸露在用户中心页面', () => {
|
||||||
|
|||||||
@@ -307,9 +307,21 @@ test('积分兑换明细.level 枚举净化后仍能通过校验,非法值依
|
|||||||
assert.strictEqual(sanitize.invalid_unit.valid, false);
|
assert.strictEqual(sanitize.invalid_unit.valid, false);
|
||||||
assert.deepStrictEqual(sanitize.invalid_unit.errors, ['积分兑换明细[0].unit has an invalid value']);
|
assert.deepStrictEqual(sanitize.invalid_unit.errors, ['积分兑换明细[0].unit has an invalid value']);
|
||||||
|
|
||||||
|
// VIP's HTML-capable fields pass the schema save sanitizer intact.
|
||||||
|
assert.strictEqual(sanitize.vip_html.valid, true);
|
||||||
|
assert.strictEqual(sanitize.vip_html.fields.pay_user_vip_1_name, '<b>会员</b>');
|
||||||
|
assert.strictEqual(sanitize.vip_html.fields.pay_user_vip_1_desc, '<em>简介</em>');
|
||||||
|
assert.strictEqual(sanitize.vip_html.fields.pay_user_vip_1_equity, '<p>权益</p>');
|
||||||
|
assert.strictEqual(sanitize.vip_html.fields.pay_user_vip_desc, '<span>会员介绍</span>');
|
||||||
|
assert.strictEqual(sanitize.vip_html.fields.vip_renew_desc, '<strong>续费</strong>');
|
||||||
|
assert.strictEqual(sanitize.vip_html.fields.vip_upgrade_desc, '<i>升级</i>');
|
||||||
|
assert.strictEqual(sanitize.vip_html.fields.vip_1_product[0].tag, '<b>推荐</b>');
|
||||||
|
|
||||||
// 字符串型枚举(palette)不受影响。
|
// 字符串型枚举(palette)不受影响。
|
||||||
assert.strictEqual(sanitize.invalid_tag_class.valid, false);
|
assert.strictEqual(sanitize.invalid_tag_class.valid, false);
|
||||||
assert.deepStrictEqual(sanitize.invalid_tag_class.errors, ['会员商品[0].tag_class has an invalid value']);
|
assert.deepStrictEqual(sanitize.invalid_tag_class.errors, ['会员商品[0].tag_class has an invalid value']);
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('运行时归一化:默认值、价格净化、零价与非法行剔除', () => {
|
test('运行时归一化:默认值、价格净化、零价与非法行剔除', () => {
|
||||||
@@ -345,7 +357,12 @@ test('运行时归一化:默认值、价格净化、零价与非法行剔除',
|
|||||||
// 自定义续费价格类型与折扣。
|
// 自定义续费价格类型与折扣。
|
||||||
assert.strictEqual(custom.renew.price_type, 'customize');
|
assert.strictEqual(custom.renew.price_type, 'customize');
|
||||||
assert.strictEqual(custom.renew.discount, 7.5);
|
assert.strictEqual(custom.renew.discount, 7.5);
|
||||||
assert.strictEqual(custom.renew.desc, '续费有礼');
|
assert.strictEqual(custom.renew.desc, '<b>续费有礼</b>');
|
||||||
|
assert.strictEqual(custom.upgrade.desc, '<i>升级王者</i>');
|
||||||
|
assert.strictEqual(custom.levels['1'].name, '青铜会员');
|
||||||
|
assert.strictEqual(custom.levels['1'].name_html, '<strong>青铜会员</strong>');
|
||||||
|
assert.strictEqual(custom.levels['1'].desc, '<em>专属权益</em>');
|
||||||
|
assert.strictEqual(custom.levels['1'].equity, '<p>一对一支持</p>');
|
||||||
assert.strictEqual(custom.levels['1'].renew_products[0].tag_class, 'b-red');
|
assert.strictEqual(custom.levels['1'].renew_products[0].tag_class, 'b-red');
|
||||||
// 升级三块参数透传。
|
// 升级三块参数透传。
|
||||||
assert.strictEqual(custom.upgrade.unit_price, '12.50');
|
assert.strictEqual(custom.upgrade.unit_price, '12.50');
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ namespace {
|
|||||||
function sanitize_text_field($value): string { return trim(strip_tags((string) $value)); }
|
function sanitize_text_field($value): string { return trim(strip_tags((string) $value)); }
|
||||||
function sanitize_textarea_field($value): string { return trim((string) $value); }
|
function sanitize_textarea_field($value): string { return trim((string) $value); }
|
||||||
function wp_kses_post($value): string { return (string) $value; }
|
function wp_kses_post($value): string { return (string) $value; }
|
||||||
|
function wp_strip_all_tags($value): string { return trim(strip_tags((string) $value)); }
|
||||||
function wp_kses($value, $allowed = null): string { return (string) $value; }
|
function wp_kses($value, $allowed = null): string { return (string) $value; }
|
||||||
function trailingslashit($value): string { return rtrim((string) $value, '/\\') . '/'; }
|
function trailingslashit($value): string { return rtrim((string) $value, '/\\') . '/'; }
|
||||||
function get_template_directory_uri(): string { return 'https://example.test/wp-content/themes/aksrt'; }
|
function get_template_directory_uri(): string { return 'https://example.test/wp-content/themes/aksrt'; }
|
||||||
@@ -115,15 +116,17 @@ namespace {
|
|||||||
Settings::$snapshot = [
|
Settings::$snapshot = [
|
||||||
'pay_user_vip_1_s' => true,
|
'pay_user_vip_1_s' => true,
|
||||||
'pay_user_vip_2_s' => true,
|
'pay_user_vip_2_s' => true,
|
||||||
'pay_user_vip_1_name' => '青铜会员',
|
'pay_user_vip_1_name' => '<strong>青铜会员</strong>',
|
||||||
'pay_user_vip_2_name' => '王者会员',
|
'pay_user_vip_2_name' => '<span>王者会员</span>',
|
||||||
|
'pay_user_vip_1_desc' => '<em>专属权益</em>',
|
||||||
|
'pay_user_vip_1_equity' => '<p>一对一支持</p>',
|
||||||
'vip_renew' => true,
|
'vip_renew' => true,
|
||||||
'vip_renew_desc' => '续费有礼',
|
'vip_renew_desc' => '<b>续费有礼</b>',
|
||||||
'vip_renew_price_type' => 'customize',
|
'vip_renew_price_type' => 'customize',
|
||||||
'vip_renew_discount' => 7.5,
|
'vip_renew_discount' => 7.5,
|
||||||
'vip_renew_reduce' => '15.00',
|
'vip_renew_reduce' => '15.00',
|
||||||
'vip_upgrade' => true,
|
'vip_upgrade' => true,
|
||||||
'vip_upgrade_desc' => '升级王者',
|
'vip_upgrade_desc' => '<i>升级王者</i>',
|
||||||
'vip_upgrade_product' => [
|
'vip_upgrade_product' => [
|
||||||
'month_to_month_s' => true,
|
'month_to_month_s' => true,
|
||||||
'unit_price' => '12.50',
|
'unit_price' => '12.50',
|
||||||
@@ -179,6 +182,17 @@ namespace {
|
|||||||
$sanitize = (static function (): array {
|
$sanitize = (static function (): array {
|
||||||
$provider = \AKSRT\Admin\SchemaProvider::get_instance();
|
$provider = \AKSRT\Admin\SchemaProvider::get_instance();
|
||||||
$cases = [
|
$cases = [
|
||||||
|
'vip_html' => [
|
||||||
|
'pay_user_vip_1_name' => '<b>会员</b>',
|
||||||
|
'pay_user_vip_1_desc' => '<em>简介</em>',
|
||||||
|
'pay_user_vip_1_equity' => '<p>权益</p>',
|
||||||
|
'pay_user_vip_desc' => '<span>会员介绍</span>',
|
||||||
|
'vip_renew_desc' => '<strong>续费</strong>',
|
||||||
|
'vip_upgrade_desc' => '<i>升级</i>',
|
||||||
|
'vip_1_product' => [
|
||||||
|
['price' => '88.80', 'show_price' => '99.00', 'tag' => '<b>推荐</b>', 'tag_class' => 'jb-yellow', 'time' => 1, 'unit' => 'month'],
|
||||||
|
],
|
||||||
|
],
|
||||||
'default_rows' => [
|
'default_rows' => [
|
||||||
'pay_vip_points_exchange_s' => true,
|
'pay_vip_points_exchange_s' => true,
|
||||||
'pay_vip_points_exchange_product' => [
|
'pay_vip_points_exchange_product' => [
|
||||||
@@ -215,6 +229,7 @@ namespace {
|
|||||||
$clean = $provider->sanitize_payload('payment', $payload, 'vip');
|
$clean = $provider->sanitize_payload('payment', $payload, 'vip');
|
||||||
$result = $provider->validate('payment', $clean, 'vip');
|
$result = $provider->validate('payment', $clean, 'vip');
|
||||||
$out[$name] = [
|
$out[$name] = [
|
||||||
|
'fields' => $clean,
|
||||||
'points_rows' => $clean['pay_vip_points_exchange_product'] ?? null,
|
'points_rows' => $clean['pay_vip_points_exchange_product'] ?? null,
|
||||||
'valid' => $result['valid'],
|
'valid' => $result['valid'],
|
||||||
'errors' => $result['errors'],
|
'errors' => $result['errors'],
|
||||||
|
|||||||
Reference in new issue
Block a user