fix(vip): preserve equity HTML instead of re-splitting into list items
This commit is contained in:
@@ -288,6 +288,14 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Non-list equity HTML in hover cards */
|
||||||
|
.sub-vip-card .v-equity > p {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
}
|
||||||
|
.sub-vip-card .v-equity > p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.sub-vip-card .payvip-button {
|
.sub-vip-card .payvip-button {
|
||||||
font-size: 1em !important;
|
font-size: 1em !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
|
|||||||
@@ -1233,7 +1233,7 @@ body.uc-modal-open {
|
|||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CRITICAL: equity must stack as list, never one jammed line */
|
/* CRITICAL: equity is admin HTML; keep author structure, only force vertical flow for li */
|
||||||
.uc-vip-tab .uc-vip-level-card .v-equity,
|
.uc-vip-tab .uc-vip-level-card .v-equity,
|
||||||
.uc-vip-tab .pay-vip-info .v-equity {
|
.uc-vip-tab .pay-vip-info .v-equity {
|
||||||
display: block !important;
|
display: block !important;
|
||||||
@@ -1276,6 +1276,15 @@ body.uc-modal-open {
|
|||||||
background: currentColor;
|
background: currentColor;
|
||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
}
|
}
|
||||||
|
/* Non-list equity HTML (p/br/div) */
|
||||||
|
.uc-vip-tab .uc-vip-level-card .v-equity > p,
|
||||||
|
.uc-vip-tab .pay-vip-info .v-equity > p {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
}
|
||||||
|
.uc-vip-tab .uc-vip-level-card .v-equity > p:last-child,
|
||||||
|
.uc-vip-tab .pay-vip-info .v-equity > p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.uc-vip-cta {
|
.uc-vip-cta {
|
||||||
display: inline-flex !important;
|
display: inline-flex !important;
|
||||||
|
|||||||
@@ -415,3 +415,80 @@ if (!function_exists('aksrt_get_vip_purchase_url')) {
|
|||||||
return home_url('/');
|
return home_url('/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format VIP equity HTML for front-end display.
|
||||||
|
*
|
||||||
|
* Admin field is free-form HTML (typically one or more <li>). Do NOT
|
||||||
|
* re-parse / re-split the content into synthetic list items by punctuation
|
||||||
|
* or Chinese keyword heuristics — that mangles intentional HTML.
|
||||||
|
*
|
||||||
|
* Rules:
|
||||||
|
* - Sanitize with a small allowlist.
|
||||||
|
* - If markup already has <li>, wrap the items in <ul 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).
|
||||||
|
* - Empty input → empty string.
|
||||||
|
*
|
||||||
|
* @param string $raw Equity HTML/plain from settings.
|
||||||
|
* @param string $class Extra classes on the wrapper (e.g. "mb10 relative").
|
||||||
|
*/
|
||||||
|
if (!function_exists('aksrt_format_vip_equity_html')) {
|
||||||
|
function aksrt_format_vip_equity_html(string $raw, string $class = ''): string {
|
||||||
|
$raw = trim($raw);
|
||||||
|
if ($raw === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowed = [
|
||||||
|
'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 === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$class = trim('v-equity ' . $class);
|
||||||
|
$class_attr = esc_attr($class);
|
||||||
|
|
||||||
|
// Already list items — preserve them, only re-wrap.
|
||||||
|
if (preg_match('/<li[\s>]/i', $clean)) {
|
||||||
|
$inner = preg_replace('#</?(ul|ol)[^>]*>#i', '', $clean) ?? $clean;
|
||||||
|
$inner = trim($inner);
|
||||||
|
if ($inner === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return '<ul class="' . $class_attr . '">' . $inner . '</ul>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other HTML (p/br/a/…) — keep as-is, no list fabrication.
|
||||||
|
if (preg_match('/<[^>]+>/', $clean)) {
|
||||||
|
return '<div class="' . $class_attr . '">' . $clean . '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plain text only: split by newlines (author-authored line breaks), never
|
||||||
|
// by commas / Chinese keywords.
|
||||||
|
$lines = preg_split('/\R+/u', $clean) ?: [];
|
||||||
|
$lines = array_values(array_filter(array_map('trim', $lines), static fn($s) => $s !== ''));
|
||||||
|
if ($lines === []) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$html = '<ul class="' . $class_attr . '">';
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$html .= '<li>' . esc_html($line) . '</li>';
|
||||||
|
}
|
||||||
|
$html .= '</ul>';
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,72 +18,6 @@ if (empty($vip_settings['enabled'])) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize equity HTML/plain text into a proper <ul> of <li>.
|
|
||||||
*/
|
|
||||||
$normalize_equity = static function (string $raw): string {
|
|
||||||
$raw = trim($raw);
|
|
||||||
if ($raw === '') {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
$allowed = [
|
|
||||||
'li' => ['class' => true],
|
|
||||||
'ul' => ['class' => true],
|
|
||||||
'ol' => ['class' => true],
|
|
||||||
'strong' => [],
|
|
||||||
'b' => [],
|
|
||||||
'em' => [],
|
|
||||||
'i' => ['class' => true],
|
|
||||||
'span' => ['class' => true],
|
|
||||||
'br' => [],
|
|
||||||
];
|
|
||||||
$clean = wp_kses($raw, $allowed);
|
|
||||||
|
|
||||||
// Already has list items.
|
|
||||||
if (preg_match('/<li[\s>]/i', $clean)) {
|
|
||||||
// Strip wrapping ul/ol; we re-wrap.
|
|
||||||
$inner = preg_replace('#</?(ul|ol)[^>]*>#i', '', $clean) ?? $clean;
|
|
||||||
return '<ul class="v-equity">' . $inner . '</ul>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plain text: split by newlines / Chinese separators / commas.
|
|
||||||
$plain = trim(wp_strip_all_tags($clean));
|
|
||||||
if ($plain === '') {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
$parts = preg_split('/[\r\n]+|[;;,,||]+/u', $plain) ?: [];
|
|
||||||
$items = [];
|
|
||||||
foreach ($parts as $part) {
|
|
||||||
$part = trim((string) $part);
|
|
||||||
// Also split jammed Chinese phrases if still one long blob with known tokens.
|
|
||||||
if ($part === '') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$items[] = $part;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Heuristic: if still a single long string without separators, try common benefit chunks.
|
|
||||||
if (count($items) === 1 && mb_strlen($items[0]) > 16) {
|
|
||||||
$blob = $items[0];
|
|
||||||
$chunks = preg_split('/(?=(?:免费|会员|更高|专属|付费|下载|身份|权益|折扣|无限))/u', $blob, -1, PREG_SPLIT_NO_EMPTY) ?: [];
|
|
||||||
$chunks = array_values(array_filter(array_map('trim', $chunks)));
|
|
||||||
if (count($chunks) > 1) {
|
|
||||||
$items = $chunks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($items === []) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$html = '<ul class="v-equity">';
|
|
||||||
foreach ($items as $item) {
|
|
||||||
$html .= '<li>' . esc_html($item) . '</li>';
|
|
||||||
}
|
|
||||||
$html .= '</ul>';
|
|
||||||
return $html;
|
|
||||||
};
|
|
||||||
|
|
||||||
$user_id = (int) ($user_id ?? get_current_user_id());
|
$user_id = (int) ($user_id ?? get_current_user_id());
|
||||||
$level = function_exists('aksrt_get_user_vip_level') ? aksrt_get_user_vip_level($user_id) : 0;
|
$level = function_exists('aksrt_get_user_vip_level') ? aksrt_get_user_vip_level($user_id) : 0;
|
||||||
$expires = (int) get_user_meta($user_id, 'aksrt_vip_expires', true);
|
$expires = (int) get_user_meta($user_id, 'aksrt_vip_expires', true);
|
||||||
@@ -142,14 +76,11 @@ foreach ([1, 2] as $lv) {
|
|||||||
}
|
}
|
||||||
$lv_name = (string) ($levels[$lv]['name'] ?? ('VIP' . $lv));
|
$lv_name = (string) ($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).
|
||||||
$equity_raw = (string) ($levels[$lv]['equity'] ?? '');
|
$equity_raw = (string) ($levels[$lv]['equity'] ?? '');
|
||||||
if ($equity_raw === '') {
|
$equity = function_exists('aksrt_format_vip_equity_html')
|
||||||
// Fallback defaults when option empty / stripped.
|
? aksrt_format_vip_equity_html($equity_raw)
|
||||||
$equity_raw = $lv === 1
|
: '';
|
||||||
? "免费下载部分资源\n会员专属身份标识\n付费内容会员价"
|
|
||||||
: "更高免费下载额度\n更高等级身份标识\n更多付费内容会员价";
|
|
||||||
}
|
|
||||||
$equity = $normalize_equity($equity_raw);
|
|
||||||
$cta = sprintf(__('开通%s', 'aksrt'), $lv_name);
|
$cta = sprintf(__('开通%s', 'aksrt'), $lv_name);
|
||||||
$vip_attr = $lv;
|
$vip_attr = $lv;
|
||||||
if (str_starts_with($btn_type, 'renew_')) {
|
if (str_starts_with($btn_type, 'renew_')) {
|
||||||
|
|||||||
@@ -57,35 +57,14 @@ trait VipServiceModal {
|
|||||||
$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 = '';
|
// Preserve admin equity HTML as-is (sanitize/wrap only; never re-split).
|
||||||
$equity_raw = trim(wp_kses_post($equity));
|
$equity_html = function_exists('aksrt_format_vip_equity_html')
|
||||||
if ($equity_raw !== '') {
|
? aksrt_format_vip_equity_html($equity, 'mb10 relative')
|
||||||
if (preg_match('/<li[\s>]/i', $equity_raw)) {
|
: '';
|
||||||
$inner = preg_replace('#</?(ul|ol)[^>]*>#i', '', $equity_raw) ?? $equity_raw;
|
|
||||||
$equity_html = '<ul class="mb10 relative v-equity">' . $inner . '</ul>';
|
|
||||||
} else {
|
|
||||||
$parts = preg_split('/[\r\n]+|[;;,,||]+/u', wp_strip_all_tags($equity_raw)) ?: [];
|
|
||||||
$parts = array_values(array_filter(array_map('trim', $parts)));
|
|
||||||
if (count($parts) === 1 && mb_strlen($parts[0]) > 16) {
|
|
||||||
$chunks = preg_split('/(?=(?:免费|会员|更高|专属|付费|下载|身份|权益|折扣|无限))/u', $parts[0], -1, PREG_SPLIT_NO_EMPTY) ?: [];
|
|
||||||
$chunks = array_values(array_filter(array_map('trim', $chunks)));
|
|
||||||
if (count($chunks) > 1) {
|
|
||||||
$parts = $chunks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($parts) {
|
|
||||||
$equity_html = '<ul class="mb10 relative v-equity">';
|
|
||||||
foreach ($parts as $p) {
|
|
||||||
$equity_html .= '<li>' . esc_html($p) . '</li>';
|
|
||||||
}
|
|
||||||
$equity_html .= '</ul>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$icon = '<i class="fas fa-gem" aria-hidden="true"></i>';
|
$icon = '<i class="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">' . $icon . '</div>';
|
$html .= '<div class="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>' . esc_html($name) . '</div>';
|
||||||
@@ -152,7 +131,9 @@ trait VipServiceModal {
|
|||||||
$panes .= '<div class="muted-2-color em09 mb6">' . esc_html($lv_desc) . '</div>';
|
$panes .= '<div class="muted-2-color em09 mb6">' . esc_html($lv_desc) . '</div>';
|
||||||
}
|
}
|
||||||
if ($equity !== '') {
|
if ($equity !== '') {
|
||||||
$panes .= '<ul class="v-equity">' . $equity . '</ul>';
|
$panes .= function_exists('aksrt_format_vip_equity_html')
|
||||||
|
? aksrt_format_vip_equity_html($equity)
|
||||||
|
: '<div class="v-equity">' . wp_kses_post($equity) . '</div>';
|
||||||
}
|
}
|
||||||
$panes .= '</div><div class="pay-vip-products">';
|
$panes .= '</div><div class="pay-vip-products">';
|
||||||
$panes .= $products;
|
$panes .= $products;
|
||||||
@@ -230,7 +211,9 @@ trait VipServiceModal {
|
|||||||
$panes .= '<div class="pay-vip-info level-' . $lv . '">';
|
$panes .= '<div class="pay-vip-info level-' . $lv . '">';
|
||||||
$panes .= '<div class="v-name font-bold">' . esc_html($lv_name) . '</div>';
|
$panes .= '<div class="v-name font-bold">' . esc_html($lv_name) . '</div>';
|
||||||
if ($equity !== '') {
|
if ($equity !== '') {
|
||||||
$panes .= '<ul class="v-equity">' . $equity . '</ul>';
|
$panes .= function_exists('aksrt_format_vip_equity_html')
|
||||||
|
? aksrt_format_vip_equity_html($equity)
|
||||||
|
: '<div class="v-equity">' . wp_kses_post($equity) . '</div>';
|
||||||
}
|
}
|
||||||
$panes .= '</div><div class="pay-vip-products">';
|
$panes .= '</div><div class="pay-vip-products">';
|
||||||
$panes .= $products;
|
$panes .= $products;
|
||||||
|
|||||||
Reference in New Issue
Block a user