核心
[核心] bbs_plugin 表新增 author_name/author_homepage 字段,存储从远程 manifest.json 同步的作者昵称和 appcenter 地址
[核心] install.sql 新装时包含作者字段;UpgradeService upgradePluginTable() 为存量站点 ALTER TABLE 补字段
[核心] model/plugin.func.php: plugin_db_init 初始化作者字段,plugin_read_by_dir_with_db 读取作者字段,新增 plugin_db_set_author 写入函数
[核心] OfficialPluginService: install/upgrade/forceRefresh 时同步作者信息到 db(manifest 已在内存,无额外远程调用);新增 syncInstalledPluginsAuthorInfo 公开方法
[核心] admin/route/plugin.php: 查看插件市场列表时用已拉取的 manifest 同步已安装插件作者信息(无额外远程调用)
[核心] admin/view/htm/plugin_list.htm: 本地插件列表展示作者昵称+appcenter 链接,author_name 为空时 fallback 到 conf.json author
[核心] 修复本地插件安装/升级(FTP上传、压缩包上传)不同步作者信息:admin/route/plugin.php 的 4 个入口(install/upgrade/upload_install/upload_upgrade)在 plugin_install() 后调用 OfficialPluginService::syncInstalledPluginsAuthorInfo(false),优先读 6h 缓存无远程延迟,manifest 无此插件则保持空值
[核心] 本地插件列表(plugin_list.htm)作者位置调整:从 brief 上方移到底部,与插件市场布局一致(左下角作者+appcenter链接,右下角操作按钮,justify-content-between)
[核心] 后台"其他"菜单新增"数据库升级"Tab(admin/?upgrade.htm),用于执行数据库结构升级(补齐 bbs_plugin 表 author_name/author_homepage 字段等)。在线升级页新增"升级数据库"按钮跳转。upgrade.htm 标题改为"数据库结构升级",新增蓝色提示框说明使用场景+红色警告提醒备份
[核心] 移除 plugin_list.htm 和 admin/route/plugin.php 中的临时调试代码
[核心] 3 语言包新增 admin_db_upgrade 系列键
[核心] index.php + api/v1/index.php 新增插件 Service 类兜底加载:model.inc.php 的 model_inc_file hook 注入可能因 tmp 缓存陈旧而丢失(tmp/model.inc.php 未重新编译但 tmp/model_xxx.func.php 重新编译注入了 Service 调用),两者不一致会导致 Class not found fatal 崩溃触发插件自动禁用。在 model.inc.php 加载后用 class_exists 守卫 + include_once 兜底加载已启用插件 model/ 目录的所有 Service 类,已加载的类不会重复 include
[核心] route/notice.php announcements 接口加 60s 缓存(CacheHelper::remember):此接口被 footer.inc.htm 全站 fetch,QPS≈页面浏览量,宝塔 PHP-FPM worker 偶发饿死触发 500。publish/delete 写入后主动清缓存,无延迟
[核心] 删除 xn_send_mail_async() 函数:伪异步(register_shutdown_function)且失败时错误被静默吞掉,0 调用方,违反 YAGNI。所有邮件场景统一用同步 xn_send_mail()
xnx_login_alert(登录提醒邮件链路)
[xnx_login_alert] 登录提醒插件加入调试日志,追踪 hook 触发与邮件发送流程各节点(uid/email/全局开关/用户开关/频率/SMTP配置/异步发送调用),版本 1.0.3 → 1.0.4,排查"每次登录都没有提醒"问题
[xnx_login_alert] 修复登录不发送邮件:hook user_login_post_end.php 误用 $user 变量,实际 route/user.php 登录路由中用户变量为 $_user,导致 hook 触发时 user_empty=1 uid=0 跳过邮件发送。改为优先取 $_user,conf.json 版本 1.0.4 → 1.0.5
[xnx_login_alert] 邮件发送改用同步 xn_send_mail 替代 xn_send_mail_async:原 async 通过 register_shutdown_function 注册,失败时错误被静默吞掉无法判断邮件是否发出。改同步后可拿到返回值并记录发送耗时与失败原因到 xnx_login_alert_error 日志,登录响应多等 1-2 秒可接受。conf.json 版本 1.0.5 → 1.0.6
[文档] 同步更新 docs/plugindev/04-api-cheatsheet.md、docs/xiunox-plugin-dev/references/api-cheatsheet.md、docs/xiunox-plugin-dev/references/ai-rules.md、docs/xiunox-mechanism/XIUNOX_Email.md、plugin/xnx_login_alert/README.md:移除 xn_send_mail_async 相关内容,统一推荐同步 xn_send_mail
[xnx_login_alert] 修复邮件模板保存后变乱码:① setting.php 的 subject/body 用 param() 默认开启 htmlspecialchars 转义,导致
被存成 <p>,显示时 esc_html 再转义一次变成 <p>,邮件 HTML 实体不渲染。改为 param(…, FALSE) 关闭转义。② 表单 action 用 $_SERVER[“REQUEST_URI”] 会带 preview=1 参数,保存后下次预览重复拼接成 preview=1&preview=1。改为 admin_plugin_setting_url() 干净 URL。conf.json 版本 1.0.6 → 1.0.7
[xnx_login_alert] 邮件发送改为后台异步(用户感知即时):原同步 xn_send_mail 阻塞登录响应 1-3 秒(SMTP 连接+握手+发送),免费 SMTP 更久。改为 register_shutdown_function + fastcgi_finish_request:前置检查(开关/频率/SMTP 配置)同步执行,邮件发送在 message() exit 后后台执行,响应立即返回浏览器。失败时仍写 bbs_email_log 和 xn_log 便于排查。conf.json 版本 1.0.7 → 1.0.8
[xnx_login_alert] 邮件发送日志增加 subject/body 内容预览(前 500 字符):排查"SMTP 返回 ok 但 QQ 邮箱收不到"问题,疑似 body 仍是修复前的双重转义乱码被 QQ 反垃圾丢弃。conf.json 版本 1.0.8 → 1.0.9
[xnx_login_alert] 修复 QQ 邮箱收不到邮件:{site_url} 因 $conf[“siteurl”] 未配置被替换成空字符串,导致 空链接,QQ 邮箱反垃圾系统直接丢弃(不退信不进垃圾箱)。改用 http_url_path() 动态生成站点 URL。conf.json 版本 1.0.9 → 1.0.10
[xnx_login_alert] 修复邮件延迟问题:默认模板主题"登录提醒"含敏感词触发 QQ 邮箱反垃圾灰度投递;body 去掉钓鱼特征词(“若非本人操作建议立即修改密码”)改温和措辞(“如本次访问是您本人操作可忽略”);"登录"改"访问"淡化敏感度;版本 1.0.10 → 1.0.11。注意:已自定义模板的用户需手动去后台去掉 URL 两侧反引号
https://xiunox.org/(非法字符触发伪装链接判定)[xnx_login_alert] 修复安全设置页点击保存后整个页面被嵌套进 form 的 bug:表单 hx-post 用相对 URL ?my-login-alert-save.htm 被浏览器附加到当前路径 /my-security.html,xiuno 路由把 my-security 当作 action 导致 hook 拦截失败,整个页面被 htmx 替换进 form 位置。改为用 url() 生成绝对路径;卡片 HTML 抽到 LoginAlertService::renderCard() 统一渲染(hook 注入与 htmx 响应共用同一份模板);UI 规范化(h6→h3 card-title、去掉 fw-medium、按钮加 px-3 与 ti-check 图标);加 HX-Trigger 触发 Toast 反馈;显示上次提醒时间/IP;版本 1.0.11 → 1.0.12
[xnx_login_alert] 修复 my_security_end.htm 缺少 PHP 关闭标签 ?> 导致 my-security.html ParseError 502:hook 文件 echo renderCard() 后未关闭 PHP 块,编译缓存把后续 当作 PHP 代码解析。版本 1.0.12 → 1.0.13
xnx_appcenter
[xnx_appcenter] 上传新版本安装包后版本号和更新日志必填校验(前后端双层),不影响审核驳回修改/单纯编辑;详情页右侧"添加新版本"路由统一改为强制回审 + 三字段必填,与编辑页"上传新版本"语义对齐
[xnx_appcenter] 版本更新审核通过后自动发"更新帖"到 forum_fid 配置版块(与首帖机制对齐),标题加"更新"后缀,内容含更新日志+下载链接(回复可见)+开发者/适用版本;version 表新增 thread_tid 字段记录每个版本帖 tid 避免重复发帖;首帖 tid 仍保留在 app.thread_tid
[xnx_appcenter] 编辑页新增上传/删除安装包时动态显示版本号+更新日志必填红星;详情页表单字段加红星提示与回审说明;三语语言包同步新增 5 个键(changelog_required/version_download_required/add_version_reaudit_hint/update_thread_changelog_label/update_thread_no_changelog)
[xnx_appcenter] 版本号 1.14.16 → 1.15.0,install.php + upgrade.php 同步加 version.thread_tid 字段(幂等)
[xnx_appcenter] 精简 appcenter_guide.html 文档(630 行 → 478 行):CSS 删除 4 套重复 SVG mask 改用 Unicode 字符;原 10 章合并为 8 章,删除冗余"文档定位"章,原"代码规范"与"踩坑速查"两章合并消除重复条目;表格字段合并精简,版本号 1.15.0 → 1.15.1
[xnx_appcenter] 修复插件市场作者链接访问报错:setting.php 导出 manifest.json 时 $SITE_URL 域名从 xiuno.xcxgy.cn 改为 xiunox.org,author_homepage 格式 appcenter-developer.htm?uid={id}(路由 appcenter_developer 无路径参数占位符,旧格式 appcenter-developer-{id}.htm 会误匹配 appcenter_detail 路由报"应用不存在")。需重新导出 manifest.json 推送到 GitHub 生效
[xnx_appcenter] 插件详情页上传新版本成功后延迟 1.5s 自动刷新页面:后端通过 HX-Trigger 触发 appcenterVersionAdded 事件,前端监听后 reload。修复提交新版本后应用状态徽章/版本号未更新的问题。conf.json 版本 1.15.1 → 1.15.2
设置项保存失效修复(xnx_lottery / xnx_attach_access / xnx_hidden)
[xnx_lottery] 后台3个Tab改为独立页面(?tab=basic/sidebar/thread),每次只加载当前页所需数据,提升性能;修复侧边栏奖品添加/编辑时 consume_credits_type/consume_credits_amount/sidebar_prize_credits_type/sidebar_prize_credits_amount 4个参数未接收导致抽奖消耗配置和积分奖品数据未保存的问题,版本 1.3.2 → 1.3.3
[Bug修复] xnx_lottery v1.3.3 → v1.3.4:setting.php 的 save_settings 分支缺失 6 个字段的接收与保存(prize_credits_types、prize_credits_min、prize_credits_max、winner_count_min、winner_count_max、winners_show_sources),导致用户修改这些设置后保存仍回退默认值;同时删除 5 个历史残留字段(create_credits_type、create_credits_amount、sidebar_credits_type、sidebar_credits_amount、sidebar_guarantee)。
[Bug修复] xnx_attach_access v1.2.2 → v1.2.3:setting.php 接收并保存 forum_whitelist,但 setting.htm 漏渲染版块白名单 checkbox,导致每次保存设置时 forum_whitelist 被清空。补齐版块白名单卡片 UI。
[Bug修复] xnx_hidden v1.1.2 → v1.1.3:同 xnx_attach_access 问题,setting.htm 漏渲染 forum_whitelist checkbox,补齐版块白名单卡片 UI。
[排查] 全量排查 40 个插件的 setting.php 与 setting.htm 字段对应关系,确认其他插件无"模板有字段但后端未接收"或"后端接收但模板缺失"的问题(xnx_comment_visibility/xnx_private_reply/xnx_reward 用 $_POST 直接接收数组字段,属正常;xnx_verify 的 type_filter 是 GET 筛选字段;xnx_appcenter 的 import_json 是文件上传字段)。
前端与安全
[修复] 头像上传取消裁剪后再次选择同一文件无反应:file input change 回调中未重置 value,浏览器规则下相同 value 不触发 change 事件
[修复] 发帖页浏览器后退 bfcache 失效导致自定义字段/抽奖/悬赏/投票 Tab 消失且必填字段被绕过:select change 时 history.replaceState 更新 URL 到 /thread-create-{fid}.htm,pageshow 事件监听 bfcache 恢复时重新调用 refresh() 同步 Tab 显隐;不走 bfcache 时 PHP 端根据 URL 中 fid 渲染正确初始状态(修改文件:view/htm/post.htm)
[修复] 发帖页 replaceState URL 拼接错误:url() 函数已根据 url_rewrite_on 自动追加后缀(.htm/.html),参数中又带 .htm 导致双重拼接成 thread-create-13.htm.html,改为传纯路由名 thread-create-FID
[修复] 版块积分规则覆盖保存后不生效:admin/route/credits_rule.php 误读模板中不存在的 enabled_xxx 字段(param 默认返回 0),导致版块规则 enabled 恒存 0,getRule 检测到 enabled=0 直接返回禁用且不回退全局;改为硬编码 enabled=1(勾选 override 即启用此版块规则)。历史脏数据需执行 SQL:UPDATE bbs_credits_rule_forum SET enabled=1 WHERE enabled=0;
[xnx_comment_visibility] 修复 hook 文件 thread_quick_reply_message_before.htm 注释中包含
<!--{hook thread_quick_reply_message_before.htm}-->字符串导致编译器多趟循环重复拼接 hook 内容(10 趟=拼接 10 次),<?php endif; ?>重复出现触发 ParseError: unexpected token “endif”,帖子详情页 502。将注释改为[本 hook 注入位置:xxx]纯文字描述,版本 1.0.0 → 1.0.1[安全] 优化 /my-security.html 修改邮箱/密码流程:①修改邮箱必须验证当前登录密码(防止会话被劫持后改绑邮箱,再通过"忘记密码"接管账号)+ 新邮箱验证码,成功后跳转刷新更新显示邮箱;②修改密码保持旧密码验证,成功后发送通知邮件到当前邮箱(防撞库后静默改密),并跳转登录页重新登录(改密后 token 已清除);③route/my.php 与 api/v1/my.php 两端逻辑同步;④新增 current_password 语言键(三语)与 password_change_notify 邮件模板(conf/email_templates.conf.php)
[安全] /my-security.html 修改邮箱/密码表单增加提交防重机制(my_security.htm 独立页 + my.common.template.htm Tab 页同步):submit capture 阶段同步拦截快速双击(htmx:before:request 是异步的无法拦截两次 submit),请求期间禁用按钮并显示 loading;htmx:response:error 网络错误立即恢复、30s 超时兜底恢复按钮、htmx:after:request 统一恢复(成功/失败必触发);超时/网络错误 toast 提示(request_timeout/network_request_failed 语言键已存在)
[优化] 无密码账户(OAuth 绑定用户)边界处理:user_change_password 普通模式跳过旧密码验证(无密码即首次设置密码,安全由已登录会话保证,管理员重置模式不受影响);route/my.php 与 api/v1/my.php 改邮箱的密码验证改为仅对有密码用户执行(无密码用户由新邮箱验证码+旧邮箱通知兜底);my_security.htm 与 my.common.template.htm 的"当前密码/旧密码"输入框仅对有密码用户渲染
[安全] 改邮箱成功后发送通知邮件到旧邮箱(email_change_notify 模板),防撞库/会话劫持后静默改绑,发送失败不影响改绑结果仅记日志;Web 端与 API 端同步
[安全] 注册页邮件验证码防滥用:未填写用户名/密码时禁止发送邮件验证码(route/user.php send_code 的 user_create 分支新增 username/password 非空校验,防止绕过前端直接构造请求;bbs.js sendVerifyCode 同步校验并携带字段;footer.inc.htm 新增 please_input_username/please_input_password 两个 bbs_lang 键),修复仅填邮箱即可获取验证码被恶意轰炸的问题
UI 与前台
[UI] /my-avatar.html 头像设置页优化:上传/预设子 Tab 由 nav-pills 改为 nav-tabs 扁平样式(避免用户误把「上传头像」当按钮直接触发上传);当前头像展示移入「上传头像」Tab 表单上方,无上传权限时置于「预设头像」Tab 内,仅头像审核中保留页面顶部展示
[xnx_project_cost] 实现前台展示页:路由表注册与前台路由分发 hook、独立展示页(route/project_cost.php + view/htm/project_cost.htm,三栏骨架 + 概览/支出明细/捐助记录双分页)、PC 左侧栏入口卡与移动端首页入口卡 hook;conf.json version 1.0.1(后台任务已递增,本次未改)
语言包
[语言包] 三语 bbs_common.php 追加 10 个通知相关语言键(no_announcement、notify_publish_、notify_menu_)
xnx_ai_reply
[xnx_ai_reply] install.php 三处 @unlink 改用 xn_unlink(is_file 守卫),避免首次安装时编译缓存文件不存在触发 WARNING 日志污染;版本 1.0.2 → 1.0.3
[xnx_ai_reply] AiReplyService.executeByTid 删除 14 条流程跟踪/业务跳过调试日志(execute_start/step/ok/skip/fallback_ok 等),仅保留 7 条真正错误日志(api_failed/publish_failed/markReplied_failed/no_fallback),避免生产环境日志被正常流程污染;版本 1.0.2 → 1.0.4(含上一条 install.php unlink 修复)


