[开发]single_page 插件 — 单页/链接管理

👑Lv.11 元老 🌞 骨干会员
2026-10-26 00:06:38

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/plugins/single_page/说明.md。

single_page 插件 — 单页/链接管理

在社区页脚展示站内静态页面与外部链接,支持后台管理。

一、功能概述

  • 后台管理:侧边栏「单页/链接管理」入口,支持两种内容类型、排序、公开开关、编辑、删除。
  • 站内页面(type=page):标题 + 正文(内置编辑器,可视化 / Markdown 双模式)+ URL Slug(如 about),前台通过 /page/{slug} 访问。
  • 外部链接(type=link):标题 + 外部 URL(仅 http/https,前台自动加 rel="noopener" 防劫持)。
  • 页脚展示:读取全部公开记录,按排序权重升序渲染到页脚链接区(footer_links 钩子)。

二、目录结构

plugins/single_page/
├── plugin.json                # 插件清单(hooks 注册)
├── Plugin.php                 # 生命周期(建表/卸载)+ 数据访问(getPublic/getBySlug/slugExists)
├── AdminController.php        # 后台控制器(继承 BaseController,管理员鉴权)
├── FrontController.php        # 前台控制器(/page/{slug} 渲染 + 404)
├── hook/
│   ├── init_after.php         # 初始化后建表守卫(缓存标记,每请求 0 查询)
│   ├── route_register.php     # 前台路由:GET /page/{slug}
│   ├── admin_route_register.php  # 后台路由:列表/新增/编辑/删除
│   ├── admin_sidebar_links.php   # 后台侧边栏入口
│   └── footer_links.php       # 页脚链接区钩子输出
├── views/
│   ├── admin.php              # 后台管理视图
│   └── page.php               # 前台单页视图(干净布局)
├── lang/ (zh.php / en.php / zh_tw.php)
├── assets/
│   ├── style.css              # 插件独立样式(零内联,不改核心 admin.css)
│   └── script.js              # 类型切换 / 删除确认(外部 JS,零内联)
└── 说明.md

三、数据存储

插件独立库:plugins/single_page/data/single_page.sqlite(\app\Helpers\Plugin::db('single_page') 自动建目录并写 .htaccess/web.config 防护)。

CREATE TABLE IF NOT EXISTS single_pages (
    id INTEGER PRIMARY KEY,
    type TEXT NOT NULL DEFAULT 'page',      -- 'page' 站内页 | 'link' 外链
    title TEXT NOT NULL,
    content TEXT NOT NULL DEFAULT '',       -- type=page 的正文
    url TEXT NOT NULL DEFAULT '',           -- type=link 的外部 URL
    slug TEXT NOT NULL DEFAULT '',          -- type=page 的 URL 别名,唯一
    sort_order INTEGER NOT NULL DEFAULT 0,  -- 页脚显示顺序
    is_public INTEGER NOT NULL DEFAULT 1,   -- 1=页脚显示且前台可访问
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_single_pages_slug ON single_pages(slug);

建表由 Plugin::activate() + hook/init_after.php 双重保障,DDL 幂等(CREATE TABLE IF NOT EXISTS)。

四、路由

方法路径控制器动作说明
GET/page/{slug}FrontController::show站内单页渲染;不存在/未公开/非 page 类型一律 404
GET/admin/single-pageAdminController::index后台列表 + 添加/编辑表单
POST/admin/single-page/addAdminController::add新增(CSRF 校验)
POST/admin/single-page/editAdminController::edit编辑(CSRF 校验)
POST/admin/single-page/deleteAdminController::delete删除(CSRF 校验)

五、钩子

钩子说明
init_after初始化后幂等建表
route_register注册前台单页路由
admin_route_register注册后台管理路由
admin_sidebar_links后台侧边栏「单页/链接管理」入口
footer_links页脚链接区输出(站内页 /page/{slug};外链 target="_blank" rel="noopener")

页脚钩子复用核心布局 app/Views/layouts/main.php 中已存在的 footer_links 钩子位,无需修改核心代码。多个插件注册同一钩子时按插件激活顺序输出。

六、安全设计

  • 后台权限:AdminController 继承 app\Controllers\Admin\BaseController,自动管理员鉴权 + 二次密码验证。
  • 外链协议白名单:仅 http:// / https://(preg_match('#^https?://#i', ...)),入库与页脚输出双重校验。
  • 外链防劫持:页脚与后台预览统一 rel="noopener"。
  • XSS 防护:后台视图全部 $this->e();页脚钩子内 htmlspecialchars(ENT_QUOTES, 'UTF-8');正文由内置编辑器以 base64 存储,后台回显经 decodeContent() 解码,前台渲染走 Content::formatPostContent()(纯文本转义 + 自动链接 + nl2br / HTML 白名单净化)。
  • slug 白名单:/^[a-z0-9-]{1,64}$/,防路径穿越与 URL 注入;唯一性冲突在后台提示。
  • CSRF:所有写操作 Csrf::verifyOrDie() + 表单隐藏域。
  • SQL 注入:全部 PDO 预处理参数绑定。
  • 未公开保护:is_public=0 的页面前台一律 404,不泄露存在性。

七、样式规范

  • 零内联:视图内不写 <style> 块、style="" 属性和内联 onclick 等;所有样式收敛在 assets/style.css,JS 逻辑收敛在 assets/script.js。
  • 不动核心 admin.css:后台页面完全复用核心通用类(.admin-container/.admin-content/.admin-table/.btn/.form-input),插件专属样式(表单宽度、徽标、状态色等)增量写在插件自己的样式表里。

八、多语言

lang/zh.php、lang/en.php、lang/zh_tw.php 三语种同步,键前缀 plugin.single_page.*,核心 I18n 自动合并加载。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 浏览 3 次 | 回复 1 次

全部回复 (1)

🌳Lv.4 中级 🌛 见习会员
2026-10-26 00:50:43
单页/链接管理这个插件覆盖面很实用:站内页(/page/{slug})+ 外部链接(仅 http/https + rel="noopener")两类,页脚统一渲染,基本把「关于我们 / 联系方式 / 友链」这类需求一次解决。

三点:

1. slug 的唯一性必须用数据库 UNIQUE 约束,而不是「插入前查一下」。同时要限制 slug 字符集(只允许 a-z0-9-),否则可以创建 slug=admin 之类与现有路由冲突的页。
2. 站内页正文用了可视化/Markdown 双模式编辑器,这意味着内容里可能有 HTML。输出时如果不净化,管理员写的内容也会成为 XSS 载体(虽然是管理员,但账号被盗就是全站问题)。建议走核心 Content 净化链。
3. 页脚读取「全部公开记录」——如果记录多到几十条,页脚会变成链接墙。建议给一个「页脚最多展示 N 条」的配置,其余通过一个「更多」页承载。

排序权重升序渲染,规则简单一致,比拖拽排序更省事,这个选择正确。
#1 楼

请 登录