fix: 修复 /links 页面 500(th:attr 受限求值)并校正友链申请回执 (v1.1.11)

问题:GET /links 抛 TemplateProcessingException ——
"Instantiation of new objects and access to static classes or parameters is forbidden"
定位到 links.html 第 126 行第 978 列:
  th:attr="value=${param.value != null and !#lists.isEmpty(param.value) ? param.value[0] : ''}"
Thymeleaf 对 th:attr 走受限表达式求值,禁止表达式对象的方法调用(#lists.isEmpty 等),
且校验发生在解析阶段,运行时短路(param.value 为 null)也救不了。同样的表达式
放在 th:with 里是合法的 —— 这正是本主题其他页面的既有写法。

修复:
- 全部取值逻辑移入 th:with(采用主题已验证写法 param?.x + x.length > 0 ? x[0] : null),
  输出改用单值属性 th:value,彻底不在 th:attr 里放复杂表达式
- 校正申请回执语义:插件实际回跳参数是 applied=success|error|disabled,
  另有 message / field / value(旧版误判为 applied=true)
  - 统一单条提示:有 message 显示 message,否则成功文案
  - applied=success 用强调色,error 用红色
  - applied=disabled 时隐藏表单,保留「未开放」提示
  - field/value 回填到对应输入框(displayName / url)
- 新增 .wi-links__apply-result--ok 样式
- 版本升至 1.1.11

同时沉淀校验:新增扫描脚本检查构建产物中 th:attr 是否含 #xxx. 方法调用 / new / T() / @bean,
全主题扫描结果为 0。
This commit is contained in:
2026-09-26 20:33:54 +08:00
parent d3ba189a7d
commit 28b1f90b26
3 changed files with 65 additions and 30 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
{
"version": "1.1.10",
"version": "1.1.11",
"type": "module",
"engines": {
"node": ">=22.12.0"
+63 -28
View File
@@ -87,42 +87,66 @@ import Layout from "../layouts/Layout.astro";
</div>
</th:block>
<!--
友链申请:表单 POST 到插件的 /links/apply/submit,插件以 303 回跳 /links,
并带上 applied(success / error / disabled)、message、field、value 四个查询参数。
注意(踩过坑):th:attr 的赋值里不能调用 #lists 等表达式对象的方法,
Thymeleaf 会对 th:attr 走受限求值并抛
"Instantiation of new objects and access to static classes or parameters is forbidden"。
取值逻辑一律先在 th:with 里算成变量,再用 th:value 之类的单值属性输出。
-->
<div
class="wi-links__apply"
th:if="${theme.config?.links?.links_apply_form_enabled != false}"
th:with="applied=${param.applied != null and !#lists.isEmpty(param.applied) and param.applied[0] == 'true'},
applyMsg=${param.message != null and !#lists.isEmpty(param.message) ? param.message[0] : null}"
th:with="appliedList=${param?.applied},
applied=${appliedList != null and appliedList.length > 0 ? appliedList[0] : null},
messageList=${param?.message},
applyMsg=${messageList != null and messageList.length > 0 ? messageList[0] : null},
fieldList=${param?.field},
applyField=${fieldList != null and fieldList.length > 0 ? fieldList[0] : null},
valueList=${param?.value},
applyValue=${valueList != null and valueList.length > 0 ? valueList[0] : null}"
>
<h2 class="wi-links__apply-title" th:text="'申请友链'">申请友链</h2>
<p
class="wi-links__apply-result"
th:if="${applied}"
th:text="'申请已提交,等待站长审核 🌙'"
th:if="${applied != null}"
th:classappend="${applied == 'success'} ? 'wi-links__apply-result--ok' : 'wi-links__apply-result--error'"
th:text="${applyMsg != null ? applyMsg : '申请已提交,等待站长审核 🌙'}"
role="status"
></p>
<p
class="wi-links__apply-result wi-links__apply-result--error"
th:if="${not applied and applyMsg != null}"
th:text="${applyMsg}"
role="alert"
></p>
<form
class="wi-links__apply-form"
method="post"
th:action="@{/links/apply/submit}"
>
<input type="hidden" name="_csrf" th:value="${csrfToken}" />
<div class="wi-links__apply-row">
<label class="wi-links__apply-field">
<span th:text="'站点名称 *'">站点名称 *</span>
<input type="text" name="displayName" required maxlength="50" placeholder="你的站点名称" th:attr="value=${param.value != null and !#lists.isEmpty(param.value) ? param.value[0] : ''}" />
</label>
<label class="wi-links__apply-field">
<span th:text="'站点链接 *'">站点链接 *</span>
<input type="url" name="url" required placeholder="https://" />
</label>
</div>
<th:block th:unless="${applied == 'disabled'}">
<form
class="wi-links__apply-form"
method="post"
th:action="@{/links/apply/submit}"
>
<input type="hidden" name="_csrf" th:value="${csrfToken}" />
<div class="wi-links__apply-row">
<label class="wi-links__apply-field">
<span th:text="'站点名称 *'">站点名称 *</span>
<input
type="text"
name="displayName"
required
maxlength="50"
placeholder="你的站点名称"
th:value="${applyField == 'displayName' ? applyValue : null}"
/>
</label>
<label class="wi-links__apply-field">
<span th:text="'站点链接 *'">站点链接 *</span>
<input
type="url"
name="url"
required
placeholder="https://"
th:value="${applyField == 'url' ? applyValue : null}"
/>
</label>
</div>
<div class="wi-links__apply-row">
<label class="wi-links__apply-field">
<span th:text="'图标地址'">图标地址</span>
@@ -164,8 +188,15 @@ import Layout from "../layouts/Layout.astro";
/>
</div>
</div>
<button class="wi-links__apply-submit" type="submit" th:text="'提交申请'">提交申请</button>
</form>
<button
class="wi-links__apply-submit"
type="submit"
th:text="'提交申请'"
>
提交申请
</button>
</form>
</th:block>
<p
class="wi-links__apply-unavailable-tip"
th:text="'友链申请暂未开放。'"
@@ -299,6 +330,10 @@ import Layout from "../layouts/Layout.astro";
margin-bottom: var(--space-md);
}
.wi-links__apply-result--ok {
color: var(--accent);
}
.wi-links__apply-result--error {
color: #e6162d;
}
+1 -1
View File
@@ -28,7 +28,7 @@ spec:
description: 微信读书书架(依赖微信读书插件)
screenshot:
file: weread.html
version: "1.1.10"
version: "1.1.11"
requires: ">=2.24.0"
license:
- name: "GPL-3.0"