参考资料
https://www.yuque.com/a632079/nodebb/
环境搭建
本来是想参考 https://www.yuque.com/a632079/nodebb/development-plugin-demo 这个搭建一个开发环境,不过这个教程里面用的是Windows子系统,但是我的应用商店用不了,而且我也之前学Vue也配好了npm、yarn和nodejs的环境。
在Windows环境下的开发很麻烦,我自己的方法跟中文文档推荐的安装方法也不一样,安装好后这个教程的意思是在本地搭一个Nodebb,我这个网速根本下不了依赖包,所以折腾了半天只能从服务器上处理好依赖包再下载到本地来。其实如果你如果不完全是新手的话,可以不按这个教程来,手动安装好Node、Yarn、Mongodb或者Redis就可以了。如果你是新手的话,环境搭建这部分还是参考Nodebb中国的文档比较好:https://www.yuque.com/a632079/nodebb/development-plugin-demo
开始第一个插件
根据Nodebb中国的这篇教程安装好环境后,选一个开发用的目录,按住Shift+右键 => Gitbash here,然后输入:
git clone https://github.com/NodeBB-China/nodebb-plugin-quickstart.git
克隆好之后,有两个地方需要修改,当然先把克隆的文件夹名字改了。我改成了 nodebb-plugin-replace-slug ,然后进入目录,我们需要修改 package.json 和 plugin.json 这两个文件,package.json参考修改:
{ "name": "nodebb-plugin-replace-slug", "version": "0.2.5", "description": "NodeBB替换Slug", "main": "library.js", "repository": { "type": "git", "url": "https://github.com/BrokenPaper/nodebb-plugin-replace-slug" /* 以后改成自己的仓库 */ }, "keywords": [ "nodebb", "plugin", "quickstart", "shell" ], "husky": { "hooks": { "pre-commit": "lint-staged", "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" } }, "lint-staged": { "*.js": [ "eslint --fix", "git add" ] }, "author": { "name": "1013370209", "email": "1013370209@gmail.com" }, "license": "MIT", "bugs": { "url": "https://github.com/BrokenPaper/nodebb-plugin-replace-slug" /* 以后改成自己的仓库 */ }, "readmeFilename": "README.md", "nbbpm": { "compatibility": "^1.13.0" }, "devDependencies": { "@commitlint/cli": "^8.0.0", "@commitlint/config-angular": "^7.1.2", "eslint": "^6.2.2 ", "eslint-config-standard": "^11.0.0", "eslint-plugin-html": "^6.0.0", "eslint-plugin-import": "^2.18.0", "eslint-plugin-node": "^9.1.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.0", "husky": "^2.4.0", "lint-staged": "^8.2.0" }, "dependencies": { } }
plugin.json参考修改:
{ "id": "nodebb-plugin-replace-slug", "url": "https://github.com/BrokenPaper/nodebb-plugin-replace-slug", /* 以后改成自己的仓库 */ "library": "./library.js", "hooks": [ { "hook": "static:app.load", "method": "init" }, { "hook": "filter:admin.header.build", "method": "addAdminNavigation" } ], "staticDirs": { "static": "./static" }, "less": [ "static/style.less" ], "scripts": [ "static/lib/main.js" ], "acpScripts": [ "static/lib/admin.js" ], "templates": "static/templates" }
然后,我们就可以开始正式的开发了,我这个插件的功能是要替换文章和目录里面的中文Slug,因为对搜索引擎很不友好。我们进入插件的入口(个人称呼)/src/core.js 进行一些修改:
'use strict' // 系统函数库 // const user = require.main.require('./user') // const db = require.main.require('../src/database') // const meta = require.main.require('./meta') // const utils = require.main.require('../public/src/utils') // 常用模块 // const async = require.main.require('async') // const nconf = require.main.require('nconf') // const winston = require.main.require('winston') // const path = require.main.require('path') const { Controllers } = require('./controllers') const Core = {} Core.init = async (params) => { const router = params.router const hostMiddleware = params.middleware // const hostControllers = params.controllers; // 我们需要为每个视图创建路由。 一个 API 路由,以及它自身的路由。 方法可以参考下面的方案 // 使用 buildHeader 中间件, NodeBB会构建页面,并将你的模板嵌入进去 router.get( '/admin/plugins/replace-slug', hostMiddleware.admin.buildHeader, Controllers.renderAdminPage ) router.get('/api/admin/plugins/replace-slug', Controllers.renderAdminPage) } // 这里的目的是为了给后台管理员页面插件栏里面添加我们的入口 Core.addAdminNavigation = async (header) => { header.plugins.push({ route: '/plugins/replace-slug', icon: 'fa-tint', name: '替换Slug' }) return header } module.exports = Core module.exports.Core = Core
然后我们再进入controller.js这个文件进行一些小修改:
'use strict' // 系统函数库 // const user = require.main.require('./user') // const db = require.main.require('../src/database') // const meta = require.main.require('./meta') // const utils = require.main.require('../public/src/utils') // 常用模块 // const async = require.main.require('async') // const nconf = require.main.require('nconf') // const winston = require.main.require('winston') // const path = require.main.require('path') // 载入依赖模块 // const _ = require('lodash') // const callbackify = require('../callbackify') const Controllers = {} Controllers.renderAdminPage = (req, res, next) => { // 注意: NodeBB 目前也支持使用 Async 方法作为路由中间件。 /* 请确保你的路由地址能和模板路径能够对应。 例如, 如果你的站点地址为: myforum.com/some/complex/route/ 你的模板地址应该为: templates/some/complex/route.tpl 并且你应该这样来渲染它: res.render('some/complex/route'); */ res.render('admin/plugins/replace-slug', {}) /* 使用回调方式中的 next(err, data) 方法。 传递数据: return '数据' 传递错误: throw new Error('错误') */ } module.exports = Controllers module.exports.Controllers = Controllers
还要修改/static/templates/admin/plugins/quickstart/tpl的名字为replace-slug.tpl,并且修改这个文件的内容,我只修改了class的名字,因为我这个插件其实还不需要管理面板:
因为我们是要修改Slug,Slug是在发表或者编辑主题的时候生成的,所以通过官方的Hook列表找到需要Hook的地方,然后编辑plugin.json添加Hook:
{ "id": "nodebb-plugin--replace-slug", "url": "https://github.com/BrokenPaper/nodebb-plugin-replace-slug", "library": "./library.js", "hooks": [ { "hook": "static:app.load", "method": "init" }, { "hook": "filter:admin.header.build", "method": "addAdminNavigation" }, { "hook": "filter:topic.create", "method": "topicCreate" }, { "hook": "filter:topic.edit", "method": "topicEdit" } ], "staticDirs": { "static": "./static" }, "less": ["static/style.less"], "scripts": ["static/lib/main.js"], "acpScripts": ["static/lib/admin.js"], "templates": "static/templates" }
我们hook的方法名是topicCreate和topicEdit,所以需要创建这两个方法,修改core.js:
"use strict"; // 系统函数库 // const user = require.main.require('./user') // const db = require.main.require('../src/database') // const meta = require.main.require('./meta') // const utils = require.main.require('../public/src/utils') // 常用模块 // const async = require.main.require('async') // const nconf = require.main.require('nconf') // const winston = require.main.require('winston') // const path = require.main.require('path') const { Controllers } = require("./controllers"); const Core = {}; Core.init = async params => { const router = params.router; const hostMiddleware = params.middleware; // const hostControllers = params.controllers; // 我们需要为每个视图创建路由。 一个 API 路由,以及它自身的路由。 方法可以参考下面的方案 // 使用 buildHeader 中间件, NodeBB会构建页面,并将你的模板嵌入进去 router.get( "/admin/plugins/quickstart", hostMiddleware.admin.buildHeader, Controllers.renderAdminPage ); router.get("/api/admin/plugins/quickstart", Controllers.renderAdminPage); }; Core.addAdminNavigation = async header => { header.plugins.push({ route: "/plugins/quickstart", icon: "fa-tint", name: "快速开始" }); return header; }; function slugify(text) { var slug = text.toLowerCase(); if (slug.match(/\/(.*)/)) { slug = slug.replace(/\/(.*)/, "/post"); } return slug; } Core.topicEdit = (data, callback) => { if (data && data.topic && data.topic.slug) { data.topic.slug = slugify(data.topic.slug); } callback(null, data); }; Core.topicCreate = (data, callback) => { if (data && data.topic && data.topic.slug) { data.topic.slug = slugify(data.topic.slug); } callback(null, data); }; module.exports = Core; module.exports.Core = Core;
这样的话,这个插件其实已经是做好了,效果已经实现了,最后还需要修改一下/static/lib/admin.js这个文件,把里面的quickstart都换成我们的插件名字就可以实现保存设置和加载设置了,不过我们这个插件并没有用到设置相关的。