fix(vip): render 开通会员更多内容 as HTML in the modal

Three layers were dropping the markup: the save path was already relaxed to wp_kses_post via schema sanitize_type html, but the runtime output re-filtered more through the narrow equity whitelist (h/img/hr/table stripped), and pay-vip.js inserted it with node() whose textContent showed the tags as literal text. The card-pass desc had the same output-side strip despite its save-side fix.

- PaymentRuntime: more and pay_vip_pass_charge_desc now output via wp_kses_post, matching the save-time level (repo precedent: revenue note, after-sale desc)

- pay-vip.js: new htmlNode() helper (innerHTML) for server-sanitized rich text; more and cardPass.desc use it, plain-text fields keep node()

Verification: 3 new contract tests cover output-level kses and innerHTML rendering; node --test tests/**/*.test.cjs 640/640 pass; php -l and node --check clean.
This commit is contained in:
kate522 committed 2026-10-18 14:53:42 +08:00
1 parent 817ac9c647
commit 2e2558e5ce
3 files changed
+33 -4

No files matched your search

+11 -2
View File
@@ -17,6 +17,15 @@
return el;
}
// 服务端已按 post 级 HTML 净化的富文本字段(more / 卡密说明)走 innerHTML;
// node() 的 textContent 会把标签当字面文本显示。
function htmlNode(tag, className, value) {
var el = document.createElement(tag);
if (className) el.className = className;
if (value !== undefined && value !== null) el.innerHTML = text(value);
return el;
}
function imageNode(src, className) {
if (!src) return null;
var image = document.createElement("img");
@@ -179,7 +188,7 @@
box.setAttribute("data-condition", "==");
box.setAttribute("data-value", "card_pass");
if (defaultMethod !== "card_pass") box.style.display = "none";
if (cardPass.desc) append(box, node("div", "muted-box muted-2-color padding-10 mb10 em09", cardPass.desc));
if (cardPass.desc) append(box, htmlNode("div", "muted-box muted-2-color padding-10 mb10 em09", cardPass.desc));
append(box, node("div", "muted-2-color em09 mb6", cardPass.only_password ? "请输入卡密" : "请输入卡号和密码"));
if (!cardPass.only_password) {
var cardInput = node("input", "form-control mb6");
@@ -492,7 +501,7 @@
+ '<i class="fa fas fa-angle-right em12 ml10" aria-hidden="true"></i>';
append(products, pointsLink);
}
if (catalog.more) append(products, node("div", "muted-2-color mb10 em09 muted-box padding-10 mini-radius", catalog.more));
if (catalog.more) append(products, htmlNode("div", "muted-2-color mb10 em09 muted-box padding-10 mini-radius", catalog.more));
renderMethods(products, methods, cardPass, wallet);
var actionName = actionKey(action);
var actionAllowed = allowed[actionName] === true;