# 简单文档
> 面向 Emlog 博客的 Markdown 文档站插件:将 Markdown 文档**动态解析渲染**为 HTML 文档页,不生成任何静态 HTML 文件。
| 项目 | 说明 |
| --- | --- |
| 插件名称 | 简单文档(目录名 `lcbk_doc`) |
| 当前版本 | 1.0.3 |
| 作者 | LAO.CHEN |
| 适用平台 | Emlog(PHP 博客系统) |
| 插件发布页 | https://www.emlog.net/plugin/detail/1259 |
| 作者主页 | https://www.emlog.net/profiles/f065546a |
| 数据表前缀 | `DB_PREFIX . 'lcbk_doc'` 系列 |
## 插件简介
简单文档(`lcbk_doc`)让你可以直接在 Emlog 后台用 **Markdown 写文档**,前台自动渲染成带目录、可搜索、可挂附件的文档页面。核心设计目标是"简单":
- 文档以 Markdown 原文入库,访问时动态解析渲染,**全程不生成静态 HTML**,升级、改版零负担;
- 基于 H2 / H3 标题**自动生成两级目录(TOC)**,标题文字即页内锚点;
- 支持**栏目分类**浏览、**站内搜索**、**代码块复制与轻量着色**;
- 支持 zip / pdf / office 等**附件上传、下载与浏览器内预览**,并记录下载日志;
- 内置防 XSS 的 HTML 白名单净化、附件目录脚本防护、下载防盗链与断点续传。
安装启用后,前台会自动获得一个独立于博客正文的文档栏目入口,适合存放产品手册、开发文档、使用说明、帮助中心等。
## 功能特性
### 文档与渲染
- **Markdown 渲染引擎**:内置自包含的 Parsedown + ParsedownExtra(类名已重命名避免与主题或其他插件冲突),支持表格、任务列表、围栏代码块、脚注、删除线、自动链接等扩展语法。
- **纯动态渲染**:文档每次请求时实时解析,**不生成、不缓存静态 HTML 文件**,正文与目录以 JSON 文件缓存于插件 `cache` 目录,缓存 key 由文档 ID 与正文内容哈希决定,内容一改自然失效。
- **自动两级目录**:按 Markdown 的 H2 / H3 标题生成"章节 + 子项"目录;正文侧栏跟随滚动高亮(IntersectionObserver 实现,无依赖),移动端收进可开关的"目录"按钮。
- **中文标题锚点**:锚点优先取标题文字生成的 slug 且**保留中文**,例如 `## 安装` 会生成 `id="安装"`,正文可用 `[点击](#安装)` 直接页内跳转;纯符号 / 表情标题或重名标题自动回退 / 追加 `-1` `-2` 序号,保证 id 唯一。
- **自动摘要**:从正文首行提取纯文本摘要(自动跳过标题、列表、引用、代码块、图片等),用于首页卡片与 ``。
- **代码块增强**:每个围栏代码块自动添加语言标识条与"复制"按钮,并按语言族做**零依赖的轻量着色**(支持 PHP / JS / Python / Bash / SQL / C 系 / HTML / CSS 等),不支持的语言也有通用关键字高亮。
- **防 XSS 净化**:渲染结果先过 HTML 白名单净化——有 DOM 扩展时走 DOMDocument 逐标签白名单(仅放行安全属性与 `http/https/mailto/data:image/*` 协议),无 DOM 扩展时回退正则过滤,删 `script`、`on*` 事件与 `javascript:` 伪协议。
### 站点与浏览
- **栏目分类**:后台可新增 / 重命名 / 排序栏目,前台首页顶部出现"全部 / 各栏目"导航并可点击筛选;删除栏目不删文档,文档自动归入"未分类"。
- **站内搜索**:首页搜索框按文档标题与正文检索,结果、分页与栏目筛选互相保留。
- **首页卡片栅格**:每页 9 篇,卡片展示标题、自动摘要、更新时间、附件数与阅读量。
- **阅读统计**:文档详情自动累计阅读量。
- **上一篇 / 下一篇**:详情页底部按排序规则自动衔接,可关联阅读。
### 附件与下载日志
- **附件上传**:为文档挂多个附件,后端做扩展名白名单 + 危险类型黑名单双重校验,存放到独立的 `files` 目录并以随机名落盘。
- **浏览器内预览**:PDF、常见图片、音视频、纯文本 / Markdown 等类型提供"预览"入口(inline 输出,不计数);SVG 因可承载脚本**一律按下载处理**。
- **下载与统计**:强制附件方式输出并记录下载日志(附件名、文档、IP、UA、时间),单个附件累计下载数;后台"下载日志"标签可按文档筛选、一键清空。
- **断点续传**:附件输出完整支持 HTTP Range(`206 Partial Content`),大文件下载与视频拖动进度友好;`HEAD` 请求只回响应头不计数。
- **防盗链开关**:开启后仅放行来自本站页面、同源(含子域)或(可选)空 Referer 直连的请求,拦截跨站盗链。
### 后台与主题
- **明暗主题**:前台默认跟随系统 `prefers-color-scheme`,右上角可手动切换,选择保存在 localStorage;CSS 全部收敛于 `css/jc-doc.css`,样式为浅色 / 深色双套变量,未引入任何第三方前端库。
- **SEO 增强**:页面输出 `
`、`description`、`canonical`、Open Graph、Twitter Card,文档详情页附带 `Article` 类型的 JSON-LD 结构化数据。
- **绿色卸载**:插件删除回调会**清理全部数据表与配置**(附件物理文件随插件目录删除),不留残留数据。
## 安装与启用
1. 在 Emlog 后台 **插件 → 插件管理 → 上传插件**,上传后插件目录为 `content/plugins/lcbk_doc/`(或手动将本目录解压放置到该路径)。
2. 启用插件。启用回调会**自动创建 4 张数据表**并写入默认配置,无需手动建表。
3. 进入插件设置页进行站点信息配置,并开始新增文档。
> 从旧版本覆盖升级时,即使未重新启用插件也不会缺列:回调里带增量迁移(补 `log.doc_id` 索引、`doc.cat_id` 列),前台 / 后台每次请求开头还会做一次幂等的运行时结构补齐(`lcbk_doc_ensure_schema`),无权限 ALTER 时静默降级不报错。
## 前台访问地址
插件前台是一个独立页面,地址根据 Emlog 是否开启伪静态自动适配:
- **开启伪静态**(推荐,地址更友好):
`https://yourdomain/plugin/lcbk_doc`
- **未开启伪静态**:
`https://yourdomain/?plugin=lcbk_doc`
若前台 404,请检查 Emlog 后台 **系统 → 设置 → SEO** 中的伪静态配置。
### 路由规则
| 路由 | 伪静态地址 | 兼容参数地址 | 说明 |
| --- | --- | --- | --- |
| 文档首页 / 全部 | `/plugin/lcbk_doc` | `?plugin=lcbk_doc` | 首页、栏目导航、搜索、分页 |
| 栏目筛选 | `/plugin/lcbk_doc/cat/{id}` | `?plugin=lcbk_doc&cat={id}` | 按栏目过滤(搜索/分页保留) |
| 文档详情 | `/plugin/lcbk_doc/doc/{id}` | `?plugin=lcbk_doc&doc={id}` | 正文 + 目录 + 附件 |
| 附件下载 | `/plugin/lcbk_doc/download/{id}` | `?plugin=lcbk_doc&download={id}` | 强制下载并记日志 |
| 附件预览 | `/plugin/lcbk_doc/preview/{id}` | `?plugin=lcbk_doc&preview={id}` | 浏览器内联输出,不计数 |
| 站内搜索 | `/plugin/lcbk_doc?q=关键词` | `?plugin=lcbk_doc&q=关键词` | 标题 / 正文检索 |
## 后台使用指南
后台入口:`https://yourdomain/admin/plugin.php?plugin=lcbk_doc`(仅管理员可见),共 4 个标签页:
### 1. 基础设置(basic)
| 配置项 | 说明 |
| --- | --- |
| 站点名称 | 文档站品牌名,显示于页头与首页 Hero 区,长度 60 |
| 副标题 | 首页副标语,同时作为默认页面描述,长度 200 |
| 站点简介 | 首页 Hero 大段介绍,支持换行,长度 1000 |
| 显示目录 | 开关详情页基于 H2 / H3 的两级目录 |
| 允许上传类型 | 附件扩展名白名单,逗号 / 空格分隔,默认 `zip,rar,7z,pdf,doc,docx,xls,xlsx,ppt,pptx,txt,md,csv,apk,mp3,mp4` |
| 下载防盗链 | 开启后附件仅允许从本站页面访问 |
| 允许直接访问 | 开启后空 Referer(如直接粘贴链接、下载工具)也能下载 |
| 日志保留天数 | 下载日志自动保留时长,范围 0–3650(0 为不清理) |
### 2. 文档管理(docs)
- 左侧为文档列表(标题、栏目、更新时间、阅读 / 附件数、可见状态),支持排序(数字越小越靠前)与**前台可见**开关。
- 新增 / 编辑文档需填写:标题(必填)、排序、所属栏目、前台可见、简介,以及核心的 **Markdown 内容**。
- Markdown 编辑框提供**导入 `.md` 文件**与**在线草稿预览**两种辅助能力:导入即把本地 Markdown 文件内容读入编辑框;预览在编辑页实时渲染查看效果(正式效果以保存后的前台为准)。
- 删除文档会**级联删除其全部附件(含物理文件)、下载日志与渲染缓存**。
### 3. 栏目管理(cats)
新增 / 重命名 / 排序 / 删除栏目。建好栏目后回到"文档管理"为文档选择栏目,前台首页即出现栏目导航。
### 4. 下载日志(logs)
展示最近下载记录(时间、附件名、所属文档、IP、UA),可按文档筛选,支持一键清空。
## Markdown 写作约定
- **目录**:正文中直接使用 `##` 与 `###`,渲染后自动进入两级目录;`###` 归属其上方最近的 `##`。没有目录开关时侧栏自动隐藏。
- **页内跳转**:标题文字即锚点(保留中文),正文可写 `[点击跳转](#标题文字)`;也可用传统 `#anchor` 方式引用其它标题生成的中文锚点。
- **代码块**:用 ```加语言名(如 ` ```php `、` ```python `)即可获得语言标识、复制按钮与轻量着色。
- **HTML 混排**:支持在 Markdown 中嵌入原生 HTML,但仅白名单标签与属性会被保留,脚本类内容一律被剥离(有 DOM 扩展时;无 DOM 扩展时按纯文本转义,避免渲染异常)。
- **图片**:支持外链图片,也允许 `data:image/png;base64` 内嵌小图。
## 附件支持范围
- **上传上限**:插件硬上限 200MB,同时受 PHP `upload_max_filesize`、`post_max_size` 约束(取三者最小值);超大附件请先在后台调整服务器 PHP 配置。
- **预览类型**(浏览器内联):`pdf` `jpg` `jpeg` `png` `gif` `webp` `mp3` `mp4` `webm` `ogg` `txt` `md`,同时提供"预览"与"下载"两个入口。
- **仅下载类型**:`zip` `rar` `7z` `tar` `gz` `doc` `docx` `xls` `xlsx` `ppt` `pptx` `csv` `apk` `ipa` 等。
- **存储安全**:文件写入 `content/plugins/lcbk_doc/files/`,目录内 `.htaccess` 禁止列目录,并对 php / phtml / phar / cgi / py / sh 等脚本扩展名**拒绝执行**(Apache 2.2 / 2.4 均覆盖),上传落盘文件名随机化,杜绝可执行文件落地。
## 目录结构
```text
lcbk_doc/
├── lcbk_doc.php 插件主文件:版本与常量、加载各库
├── lcbk_doc_callback.php 启用/更新/删除回调:建表、迁移、种子配置、卸载清理
├── lcbk_doc_setting.php 后台设置页:4 个标签页 + POST 处理 + 附件上传
├── lcbk_doc_show.php 前台入口:路由分发(首页/文档/下载/预览)
├── css/
│ └── jc-doc.css 前台样式(浅色/深色双主题变量,无第三方依赖)
├── files/ 附件上传目录(含 .htaccess 防护)
├── cache/ 渲染结果 JSON 缓存目录(运行时自动创建)
└── lib/
├── Parsedown.php Markdown 解析器(内置副本)
├── ParsedownExtra.php Markdown Extra 扩展解析器
├── lcbk_doc_kit.php 公共工具:配置读写、URL 生成、路由解析、输入过滤、扩展名校验、Range 解析、防盗链
├── lcbk_doc_md.php Markdown 渲染入口:锚点/TOC 注入、HTML 净化、纯文本摘要、渲染缓存
├── lcbk_doc_store.php 数据访问层:栏目、文档、附件、下载日志的增删改查
└── lcbk_doc_view.php 前台页面渲染、代码块交互脚本、附件下载/预览流式输出
```
## 数据表结构
全部表使用 `utf8mb4_unicode_ci`,表名前缀为 Emlog 的 `DB_PREFIX`(默认 `emlog_`)。
### 栏目表 `{prefix}lcbk_doc_cat`(v1.0.2 新增)
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| id | INT UNSIGNED 自增 | 主键 |
| name | VARCHAR(80) | 栏目名 |
| sort_order | INT | 排序(小者靠前) |
### 文档表 `{prefix}lcbk_doc`
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| id | INT UNSIGNED 自增 | 主键 |
| title | VARCHAR(255) | 标题 |
| intro | VARCHAR(1000) | 简介(摘要) |
| md_content | LONGTEXT | Markdown 原文 |
| sort_order | INT | 排序(小者靠前) |
| cat_id | INT UNSIGNED | 所属栏目,0 为未分类(v1.0.2 新增,带索引) |
| hide | ENUM('n','y') | 前台可见性 |
| views | INT UNSIGNED | 阅读数 |
| addtime / updtime | INT UNSIGNED | 添加 / 更新时间戳 |
### 附件表 `{prefix}lcbk_doc_attach`
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| id | INT UNSIGNED 自增 | 主键 |
| doc_id | INT UNSIGNED | 所属文档(带索引) |
| name | VARCHAR(255) | 展示用原始文件名 |
| file | VARCHAR(255) | 落盘文件名 |
| ext | VARCHAR(20) | 扩展名(小写) |
| size | BIGINT UNSIGNED | 字节数 |
| downloads | INT UNSIGNED | 累计下载次数 |
| addtime | INT UNSIGNED | 上传时间戳 |
### 下载日志表 `{prefix}lcbk_doc_log`
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| id | BIGINT UNSIGNED 自增 | 主键 |
| doc_id | INT UNSIGNED | 所属文档(带索引,用于筛选与级联删除) |
| attach_id | INT UNSIGNED | 附件(带索引) |
| name | VARCHAR(255) | 附件名快照 |
| ip | VARCHAR(45) | 访客 IP(兼容 IPv6) |
| ua | VARCHAR(255) | User-Agent |
| addtime | INT UNSIGNED | 下载时间戳 |
## 安全设计摘要
- **双保险扩展名校验**:上传与输出环节同时校验白名单 + 危险脚本扩展名黑名单,即使后台白名单被改动,脚本文件也无法经插件下发。
- **HTML 白名单净化**:DOM 逐标签清理未知标签(剥壳留文本)、剥离 `on*` 事件属性、封禁 `javascript:` 伪协议;`class` 仅保留代码高亮所需的 `language-*`。
- **防注入**:所有 SQL 均经 `intval` / `Database::escape_string`;前台标题、内容、URL 一律 `htmlspecialchars` 输出。
- **附件目录防护**:`files/.htaccess` 禁目录列表 + 禁脚本执行 + 落盘随机名。
- **缓存安全**:渲染缓存 JSON 只做纯文本读取,绝不 include / eval;缓存 key 绑定内容哈希,正文变化自然失效,删除文档时清理对应文件。
- **下载输出安全**:清空输出缓冲后再流式输出二进制,避免页面污染;`Content-Disposition` 同时给出 ASCII 与 RFC 5987 编码文件名,兼容中文文件名下载。
## 常见问题
**Q1:前台访问提示 404?**
确认 Emlog 是否开启伪静态。开启后地址为 `/plugin/lcbk_doc`;未开启用 `?plugin=lcbk_doc`。仍不行请检查 `系统 → 设置 → SEO` 的伪静态规则是否生效。
**Q2:文档里有表格 / 复杂 HTML,为什么显示成纯文本或变样?**
Markdown 中嵌入的原生 HTML 需要 PHP 的 `DOMDocument` 扩展解析(白名单净化);缺 DOM 扩展时会按纯文本转义处理。建议装好 `dom` 扩展(同时强烈建议启用 `mbstring`),可获得完整渲染体验。
**Q3:大附件上传失败?**
先确认未超过 200MB 插件硬上限,再检查服务器 `upload_max_filesize` 与 `post_max_size`(需大于附件大小,必要时同步调大 `max_execution_time`)。
**Q4:下载的中文文件名乱码?**
已按标准做法同时输出 `filename` 与 `filename*=UTF-8''`,现代浏览器正常;个别老旧下载工具可能退化,可改后端附件名为 ASCII 后上传。
**Q5:会不会生成一堆静态文件拖慢站点?**
不会。插件是纯动态渲染,只在 `cache/` 写少量 JSON 渲染缓存(正文不变即命中,正文一改自动失效),不产生任何静态 HTML。
**Q6:删除插件会留残留数据吗?**
删除插件时回调会删除全部 4 张数据表与 Storage 配置;插件目录(含附件)由 Emlog 一并清理。建议删除前确认不再需要已有文档与附件。
## 更新日志
### v1.0.3
**新增**
- 后台文档列表分页(每页 30)
- 后台写操作加入 CSRF 校验
**修复**
- 断点续传/流媒体重复计数下载
- 上/下篇无法跨栏目串联
- 阅读数刷新重复累加
- 搜索 `%` `_` 被当通配符
**安全**
- 扩充禁止上传扩展名黑名单
- 收紧 `files/.htaccess` 脚本拦截
- 附件输出加 `nosniff` 头
### v1.0.2
- 新增文档分类/栏目
- 新增代码块,高亮/一键复制
- 新增站内搜索
- 优化文档「上一篇 / 下一篇」仅在当前栏目内相邻,栏目切换后导航不串栏目。
- 修复若干渲染细节
### v1.0.1
- 优化标题锚点
- 新增文档详情页底部上一篇 / 下一篇
- 新增返回顶部
- 优化返回文档列表移至页面顶部面包屑右侧
### 1.0.0
- 首发版本
## 许可说明
插件作者为 LAO.CHEN,通过 Emlog 官方插件市场分发。使用前请阅读 Emlog 插件分发页相关条款,本文件仅作使用说明。