使用文档

五段内容:装程序、填授权码、后台功能、做模板、做插件。照抄能跑,不用猜。

1. 安装程序

DTR自动收录导航 是纯 PHP 程序,不用 Composer、不用框架,上传后浏览器点一遍就装好。

1. 把整包上传到网站根目录(wwwroot 这一层),index.php 必须在根目录里
2. 浏览器打开 https://你的域名/install/ ,填数据库信息与管理员账号
3. 装完自动写入 config/install.lock,重复访问安装页会被拦住
4. config 与 storage 目录需要 PHP 可写(755 即可)

宝塔伪静态

宝塔面板 → 网站 → 对应站点 → 设置 → 「伪静态」,把下面三行整段粘进去保存。 只填这三行,不要自己写 server { } 外壳,面板会自动套:

location ~ ^/(app|config|database|storage(?!/uploads/))/ { deny all; }
location ~ ^/(templates/[^/]+/(views/|.*\.html$)|public/[^/]+/(data/|views/|[^/]+\.(php|json|txt|md|html)$)|storage/uploads/.*\.php$)|\.(sql|log|lock)$|/\.(htaccess|user\.ini)$ { deny all; }
if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s=$1 last; }

第一行挡程序目录(storage/uploads 图片目录除外),第二行挡模板内部文件、插件程序与数据、备份文件, 第三行才是伪静态。顺序不要调换;Apache / IIS 用包里的 .htaccess 与 web.config,不用管这里。

2. 填写授权码

程序装好后默认未激活,后台除了授权页以外会被锁定。授权服务器与通信密钥已经内置,站长只需要填一枚授权码。

  1. 打开授权管理登录主程序后台,左侧菜单点「授权管理」。页面会显示一串「本站指纹」(8 位),申领授权码时把「域名 + 这串指纹」一起发给老李。
  2. 粘贴授权码授权码一栏粘贴老李发的那串,形如 DTR-XXXXXXXX-XXXXXXXX-XXXXXXXX,第一段就是本站指纹,注意大小写与横线。
  3. 保存并校验点「保存并校验」。状态显示「授权有效」、方案与到期时间即为开通,不需要填授权服务器、密钥或绑定域名。
  4. 域名自动识别程序按当前访问域名自动校验;换域名或换服务器联系老李重新换绑,换绑会给出一枚新码,替换后点「立即联网检测」即可,不用重装。
  5. 离线宽限在线校验成功后结果缓存 12 小时;授权服务器暂时连不上时,站点在默认 7 天宽限期内照常运行,恢复后自动重验。

授权状态、到期时间、绑定域名、本站指纹、上次校验时间都在「授权管理」页显示,随时可以点「立即联网检测」强制刷新。

常见校验提示

提示怎么处理
授权码不存在授权码抄错(大小写、横线丢了),或这枚码已被删除。
授权码与当前域名不匹配这串码是发给别的域名的(码里的指纹段和本站指纹不一样)。联系老李按本站域名重新换绑取一枚新码,替换后点「立即联网检测」。
该授权码已被停用授权被临时停用或已吊销,请联系老李恢复。
授权已到期续费时从原到期日顺延,续费后点一次「立即联网检测」。
无法连接授权服务器网络或授权服务器暂时不可达,站点进入离线宽限;长时间不恢复再联系老李。

不要为了跳过校验去改授权文件:改坏之后换绑、续费都会失效,而且无法自动恢复。授权问题直接找老李处理最快。

3. 后台功能地图

后台是左侧菜单加多标签页,常用的收录、分类、文章、插件都在一个地方,不用记路径。

站点库      添加/编辑站点、网址别名、推荐位、上下架、批量操作
收录申请    待审列表、一键通过自动入站、驳回
分类与导航  站点分类、导航菜单、公告、友情链接
文章频道    文章分类、文章列表、富文本编辑
会员中心    注册用户、重置密码、禁用、删除
模板管理    模板启用与单独配置
插件管理    插件开关、设置、数据管理
站点设置    站点信息、SEO、截图 API、真实 IP 头、关于页、代码位
授权管理    只填授权码,保存并联网检测

几个容易忽略的设置:站点设置里的「网址后缀」对全站地址生效(默认 .html);「截图 API 前缀」用于站点详情页大图; 「真实访客 IP 头」只有套了 CDN 或反向代理才填,直接部署请留空。改完设置记得保存,再回前台刷新确认。

插件管理里每个插件都有「开关 / 设置 / 管理」三个按钮。自带的 TDK 获取插件就在这里开关, 抓取超时、是否覆盖已填内容、限流间隔都在它的「设置」里改,改完点保存即生效。

4. 开发新模板

一套模板就是 templates/ 下的一个文件夹,纯 PHP 写页面,没有模板标签、没有编译缓存。 做好后丢进目录,后台「模板管理」自动列出来,点「启用」就切换;删掉整个文件夹就能干净移除,不留残余。

4.1 目录结构

templates/你的模板名/
 ├─ Dtr.php            模板信息(后台模板列表读取)
 ├─ Config.php         模板配置默认值(后台改过的值存数据库)
 ├─ Form.html          后台「模板配置」表单
 ├─ pic.png            模板预览图
 ├─ views/             前台视图
 │   ├─ layout.php     公共外壳(有它就不用每页写头尾)
 │   ├─ index.php  sort.php  site.php  search.php  pager.php
 │   ├─ nav.php  ranking.php  ranking_body.php  404.php
 │   ├─ apply.php  daishen.php  about.php  tongji.php
 │   ├─ wz_all.php  wz_show.php  wz_sort.php
 │   └─ user.php  login.php  register.php
 └─ public/pc/         本模板自带的 css / js / img

最快上手方式:把 templates/default 整个复制一份改名,先改 Dtr.php 的 name 与 title、再换 public/pc/ 里的配色,前台就能跑;之后逐步替换页面。 缺哪个视图文件,引擎会自动回退到 default 的同名文件,所以可以只做首页、其余页面先沿用默认。

4.2 模板信息 Dtr.php

// Dtr.php —— 键名固定,值随便改;name 必须等于目录名
return [
    'title'   => '我的模板',
    'name'    => 'mytpl',
    'author'  => '张三',
    'version' => '1.0.0',
    'pic'     => 'pic.png',
    'link'    => 'https://wpa.qq.com/msgrd?v=3&uin=1735737677&site=qq&menu=yes',
    'set'     => '1',               // 1 = 允许后台「模板配置」
    'desc'    => '一句话介绍这套模板。',
];

后台模板列表靠这个文件识别模板:没有 Dtr.php 的目录不会出现在列表里,name 必须和目录名一致。

4.3 模板配置 Config.php + Form.html

// Config.php —— 键名自定义,视图里用 tplcfg('键名') 取
return [
    'hero_title'   => 'DTR 自动收录导航',
    'hero_desc'    => '专注优质站点收录,自动抓取、人工审核、快速上线',
    'theme_main'   => '#2fadf8',
    'foot_link'    => "首页,/\n关于,/about.html",   // 多行值必须用双引号
    // 开关型字段列在这里:未勾选存 0,勾选存 1
    '_switch'      => 'hero_open,show_tui,show_new,show_hot',
];
<!-- Form.html:字段名必须和 Config.php 的键一一对应 -->
<input class="input" name="hero_title" value="<?php echo e(tplcfg('hero_title')); ?>">

<label class="switch">
  <input type="checkbox" name="hero_open" value="1" <?php echo tplcfg('hero_open') == '1' ? 'checked' : ''; ?>>
  <i></i><span>显示首页横幅</span>
</label>

默认值写在 Config.php,站长在后台「模板配置」改过的值存进数据库,不会覆盖你的文件; 点「恢复默认」就删掉数据库里那份,重新读你的文件。视图里一律用 tplcfg('键名') 取值。

两个最容易踩的坑:① 多行默认值(比如页脚链接)必须用双引号写 "a\nb",单引号里的 '\n' 是两个字符; ② 勾选类字段要在数组里写一行 '_switch' => 'a,b,c' 把开关字段名列出来,没列出的字段按普通文本保存。

4.4 视图里能用的函数与变量

<?php /* views/ 里能直接用的函数 */ ?>
<?= e(tplcfg('hero_title')) ?>                   模板配置(后台改过就用改后的)
<?= e(dtr('name')) ?>                            站点名称
<?= tpl_url('css/style.css') ?>                  本模板静态资源 → templates/当前模板/public/pc/css/style.css
<?= url('index') ?>                              首页地址(自动带伪静态后缀与子目录)
<?= url('sort', ['alias' => 'gongju']) ?>        分类地址 → /list/gongju.html
<?= e(cut($site['description'], 80)) ?>          截断输出
<?= ad(1) ?>                                     广告位 1(后台「系统设置 → 广告位」里贴代码)

<?php /* 控制器传什么变量,视图里就有什么 */ ?>
index.php:$sorts / $recommend / $latest / $topView / $recent / $notice / $links / $articles
sort.php:$sort / $sites / $total / $pages / $cur
site.php:$site / $sort / $domain / $related
wz_show.php:$article / $sortwz

数据由控制器传进来,模板不做查询;静态资源一定要用 tpl_url() 输出,这样才会指向本模板自己的目录, 写死 /public/... 会串到插件目录去。所有输出用户内容的地方都要用 e() 转义。

4.5 给插件留钩子

插件靠模板里的钩子把入口挂进页面(比如插件在页脚挂一个入口链接)。这几个钩子建议照着 default 全部保留, 新模板少写一个,对应插件在前台就没有入口:

<?php \DTR\Core\Hooks::fire('front_head', ['page' => $page]); ?>         <!-- </head> 前:插件插 css/js -->
<?php \DTR\Core\Hooks::fire('front_nav_extra', ['navs' => $navs]); ?>   导航条末尾
<?php \DTR\Core\Hooks::fire('front_footer_extra'); ?>                   页脚链接区(插件入口挂这里)
<?php \DTR\Core\Hooks::fire('front_body_end', ['page' => $page]); ?>    </body> 前:统计脚本等

# 内容插槽(模板里留位置,插件或广告往里放内容)
front_slot_head   front_slot_home   front_slot_side   front_slot_detail

5. 开发新插件

一个插件就是 public/ 下的一个文件夹。放进目录后台「插件管理」自动列出, 列表上有「开启 / 关闭」和「设置」按钮。删掉整个文件夹即卸载,不需要安装、不需要卸载动作; 只有自己在 plugin.json 里声明过数据表的插件,删除时才会多一个「删数据表」勾选。

5.1 目录结构

public/你的插件目录/
 ├─ plugin.json     插件信息
 ├─ 入口文件.php     插件主类,继承 \DTR\Core\Plugin
 ├─ views/          插件前台页面(renderFront 会套当前模板头尾)
 ├─ public/         插件自带 css / js / img(别引用模板资源)
 ├─ data/           插件自己的数据(示例插件用 json 存这里)
 └─ config.json     开关与设置,后台开启/关闭与保存时由框架读写

5.2 插件信息 plugin.json

// plugin.json
{
    "name": "demo",
    "title": "示例插件",
    "version": "1.0.0",
    "author": "老李",
    "desc": "演示用:后台一键开关与设置,前台一个提交页。",
    "entry": "demo.php",
    "class": "DTR\\Plugins\\Demo",
    "has_config": 1,
    "tables": ["demo_log"]
}
// entry = 入口文件名;class = 该文件里的完整类名
// has_config = 1 时后台插件列表出现「设置」按钮
// tables = 这个插件自己建的数据表(不写 dtr_ 前缀),表名必须以插件目录名开头

5.3 主类写法(下面是一个可直接照抄的例子)

<?php
namespace DTR\Plugins;

use DTR\Core\Plugin;

class Demo extends Plugin
{
    /** 必须实现:挂动作钩子(插件开启时才会被调用) */
    public function register(): void
    {
        \DTR\Core\Hooks::listen('front_footer_extra', function () {
            if ($this->config('enabled', 0)) {
                echo '<a href="' . site_url('/demo.html') . '">示例页面</a>';
            }
        });
    }

    /** 前台路由:[正则, 回调],正则是不含域名的路径 */
    public function routes(): array
    {
        return [
            ['@^/demo\.html$@', function () { $this->renderFront('index'); }],
            ['@^/demo/submit$@', function () { /* 处理提交 */ }],
        ];
    }

    /** 后台「设置」弹窗表单(返回 HTML) */
    public function adminForm(): ?string
    {
        return '<label><input type="checkbox" name="enabled" value="1"> 开启</label>'
             . '<label><input type="checkbox" name="need_audit" value="1"> 提交需审核</label>';
    }

    /** 后台「设置」保存:字段全在 $post 里,自己写回 config.json */
    public function adminSave(array $post): void
    {
        $this->saveConfig([
            'enabled'    => !empty($post['enabled']) ? 1 : 0,
            'need_audit' => !empty($post['need_audit']) ? 1 : 0,
        ]);
    }
}

基类 \DTR\Core\Plugin 提供的可覆盖方法与工具:

方法说明
register()必须实现。在这里 Hooks::listen() 挂动作;插件关闭时框架不会调用它。
routes()返回 [[正则, 回调], ...],正则是去掉域名的路径,在所有内置路由之后、404 之前匹配。
renderFront($view, $data)渲染插件自己的 views/ 页面,自动套当前模板的页头页尾。
adminForm()返回 HTML,显示在后台「设置」弹窗里;has_config 为 1 才有这个按钮。
adminSave($post)「设置」弹窗保存时进来,自己决定把哪些字段写回 config.json。
adminAction($action)自定义后台管理页,访问 /admin/plugin/run/目录名/动作 时进来。
config('键', 默认值)读 config.json;开关字段固定叫 enabled,由后台的开启/关闭按钮维护。
saveConfig(['a' => 1])合并写回 config.json,框架也会用它记住开启状态。

5.4 三条硬规矩

① 自包含:css / js / 图片 / 数据全放自己目录内,用 $this->asset('css/x.css') 这类方法引用, 绝不引用模板目录里的资源,否则换模板就坏。
② 写操作带令牌:后台管理页里每个表单都要输出 csrf_field(),后台对所有 POST 统一校验,漏了会提示「表单已过期」。
③ 关闭也要留地址:插件关闭时前台访问它的地址,框架会给出「已关闭」的中文提示页而不是空白 404, 所以路由里不要再自己判断开关后 exit 空白。

数据建议存自己的 data/*.json(上面这个示例插件就是这么存的),需要建表时再走数据库; 插件不要写 install 脚本,第一次访问时懒初始化最稳。

要建表的插件请在 plugin.json 里用 tables 声明表名:表名不带数据库前缀 (建表 SQL 里写 {{demo_log}},框架自动补成 dtr_demo_log),而且必须以插件目录名开头, 不合规的声明会被直接忽略。声明过的插件在后台删除时,卡片上会出现「删数据表」勾选,默认不勾: 不勾只删目录、表和数据留在库里,勾了才 DROP TABLE;没声明 tables 的插件删除时只删目录。

模板和插件做出来想在自家站点试效果,直接把文件夹丢进 templates/ 或 public/ 就能看见, 不用改任何程序代码。另有问题见 常见问题, 或直接找老李(QQ 310212367,群 812890793)。

顶