This document audits nodebb-plugin-internalnotes against NodeBB upstream documentation and the nodebb-plugin-quickstart template. References: development, quickstart, plugins, plugin.json, hooks, statics, libraries, i18n, style guide, widgets.
| Requirement | Status | Notes |
|---|---|---|
| id | ✅ | nodebb-plugin-internalnotes (unique, matches npm package name) |
| url | ✅ | Absolute URL to repo (https://github.com/BrutalBirdie/nodebb-plugin-internalnotes) |
| library | ✅ | ./library.js – entry point loaded when plugin is active |
| hooks | ✅ | All hooks have hook and method; optional priority omitted (default 10) |
| scss | ✅ | scss/internalnotes.scss |
| scripts | ✅ | public/lib/main.js (forum client) |
| acpScripts | ✅ | public/lib/acp-main.js (ACP client) |
| modules | ✅ | ../admin/plugins/internalnotes.js → ./public/lib/admin.js (ACP page module) |
| templates | ✅ | templates |
| languages | ✅ | languages |
| staticDirs | ⚪ | Not required; no public static assets |
| upgrades | ⚪ | Optional; add when introducing DB migrations |
Verdict: Compliant.
| Requirement | Status | Notes |
|---|---|---|
| name | ✅ | nodebb-plugin-internalnotes (must be prefixed nodebb-plugin- for npm and NodeBB) |
| main | ✅ | library.js |
| nbbpm.compatibility | ✅ | ^4.0.0 – required for nbbpm listing; targets NodeBB v4 |
| repository / bugs | ✅ | Present (BrutalBirdie repo) |
| keywords | ✅ | Include nodebb, plugin |
| author | ✅ | Present |
| devDependencies | ✅ | eslint for npm run lint |
| .npmignore | ✅ | Present (node_modules, .git, .DS_Store, etc.) so only publishable files are shipped |
Verdict: Compliant.
| Requirement | Status | Notes |
|---|---|---|
| Hook methods | ✅ | Each hook in plugin.json has a matching exported method |
| require.main.require | ✅ | NodeBB modules loaded via require.main.require('./src/...') |
| Async hooks | ✅ | init, addRoutes, filters use async/callbacks appropriately |
| formatApiResponse | ✅ | API routes use helpers.formatApiResponse() (and controllerHelpers once for 403) |
| Route helpers | ✅ | routeHelpers.setupAdminPageRoute, routeHelpers.setupApiRoute used correctly |
API route paths: Routes are registered under /internalnotes/.... The client and README use /plugins/internalnotes/.... In NodeBB 3, plugin API routes are typically mounted under /api/v3/plugins/<plugin-id>; the router passed to static:api.routes may be scoped per plugin, so /internalnotes/:tid can resolve to /api/v3/plugins/internalnotes/:tid. Ensure the router you receive is the plugin-scoped one; if the client calls api.get('/plugins/internalnotes/' + tid) and the request goes to /api/v3/plugins/internalnotes/:tid, the server must serve that path (either by registering /internalnotes/... on a router already mounted at .../plugins/internalnotes, or by registering /plugins/internalnotes/... on the main API router). No code change if your environment already matches; otherwise align server route prefix with client.
Verdict: Compliant; verify API base path in your NodeBB version.
| Requirement | Status | Notes |
|---|---|---|
| Forum script | ✅ | public/lib/main.js – loaded via scripts |
| ACP script | ✅ | public/lib/acp-main.js – loaded via acpScripts |
| Admin module | ✅ | modules maps ../admin/plugins/internalnotes.js to admin script; template admin/plugins/internalnotes triggers require('admin/plugins/internalnotes') and init() |
| ES5 / minification | Style guide recommends ES5 for client code for minification. main.js uses async/await and template literals (ES6+). NodeBB 3 build may transpile; if not, consider ES5 or confirm build supports ES6+ |
|
| Admin ES modules | ✅ | admin.js uses import { save, load } from 'settings' – ACP is typically built with module support |
Verdict: Compliant for structure; confirm client build/minification for ES6+ if you hit issues.
| Requirement | Status | Notes |
|---|---|---|
| Admin template | ✅ | templates/admin/plugins/internalnotes.tpl; controller uses res.render('admin/plugins/internalnotes', ...) |
| Naming | ✅ | Matches quickstart pattern admin/plugins/<name> |
Verdict: Compliant.
| Requirement | Status | Notes |
|---|---|---|
| Directory | ✅ | languages/en-GB/internalnotes.json |
| plugin.json | ✅ | "languages": "languages" |
| Usage | ✅ | Keys like [[internalnotes:menu.assigned]] in templates and server; client uses translator.translate('[[internalnotes:' + key + ']]', ...) |
| defaultLang | ⚪ | Optional; only needed if you want a non–en-GB fallback |
Verdict: Compliant.
| Hook | Purpose | Status |
|---|---|---|
| static:app.load | Init, page and admin routes | ✅ |
| static:api.routes | REST API routes (notes, assign, group search) | ✅ |
| filter:admin.header.build | ACP nav item | ✅ |
| filter:navigation.available | “Assigned” nav item | ✅ |
| filter:widgets.getWidgets | Register "Internal Notes & Assign Topic" widget | ✅ |
| filter:widget.render:internalnotes_sidebar | Render widget HTML (topic page, privileged only) | ✅ |
| filter:topic.get | Add notes/assignee to single topic | ✅ |
| filter:topics.get | Add notes/assignee to topic lists | ✅ |
| action:topics.purge | Clean up notes/assignee on topic purge | ✅ |
Verdict: All hooks used correctly; filters return data in the expected shape.
8. Widgets (docs)
The plugin provides an optional Internal Notes & Assign Topic widget for themes that use a different layout (e.g. when the right sidebar component is not present). Audited against the Writing Widgets documentation.
| Requirement | Status | Notes |
|---|---|---|
| Register widget | ✅ | Listens to filter:widgets.getWidgets with method defineWidgets; pushes { widget, name, description, content } into the array and returns it |
| Widget namespace | ✅ | widget: 'internalnotes_sidebar' – unique namespace for the render hook |
| Render hook | ✅ | Listens to filter:widget.render:internalnotes_sidebar with method renderInternalNotesWidget; hook name matches widget namespace |
| widget.html | ✅ | Render method assigns HTML to widget.html (buttons for Notes and Assign Topic) |
| Async return | ✅ | renderInternalNotesWidget is async and returns widget (documented pattern: callback or return widget) |
| widget.req | ✅ | Used for widget.req.uid and widget.req.path (topic-page and privilege checks) |
| widget.area | ✅ | Used for widget.area.template and widget.area.url when present (topic-page detection) |
| widget.data | ⚪ | Not used; content: '' in registration (no admin form) – valid for a widget with no options |
| Nomenclature | ✅ | Plugin is nodebb-plugin-internalnotes (main purpose is internal notes); widget-only packages would use nodebb-widget-xyz – correct here |
| Privilege / scope | ✅ | Widget renders empty HTML for non–topic pages and for users without canViewNotes; no sensitive data exposed |
Verdict: Compliant with the widget development docs. Primary UI for Internal Notes and Assign Topic is the widget (and any theme-specific sidebar placement); no filter:topic.thread_tools in current plugin.
- No
staticDirs– no public static assets. Sensitive data is not exposed via static routes. - API routes use
middleware.ensureLoggedInand customensurePrivileged(notes visibility).
Verdict: Compliant.
- Uses NodeBB
databaseAPI (getObject,setObject,sortedSetAdd, etc.). - Keys documented in README; no custom migrations yet (no
upgradesin plugin.json).
Verdict: Compliant; addupgradeswhen you introduce schema changes.
- Core follows Airbnb JS + ESLint; third-party plugins are encouraged but not required to follow.
- This plugin has
"lint": "eslint ."in package.json,eslintin devDependencies, andeslint.config.mjs;npm run lintpasses with no errors or warnings.
Verdict: Compliant.
| Area | Result |
|---|---|
| plugin.json | ✅ Compliant |
| package.json / nbbpm | ✅ Compliant (.npmignore added) |
| library.js | ✅ Compliant (verify API path) |
| Client / ACP | ✅ Compliant (confirm ES5/ES6 build) |
| Templates | ✅ Compliant |
| i18n | ✅ Compliant |
| Hooks | ✅ Compliant |
| Widgets | ✅ Compliant (widgets) |
| Security | ✅ Compliant |
| Database | ✅ Compliant |
| Style / lint | ✅ eslint.config.mjs added (optional; run npm install -D eslint for lint) |
Done: Plugin targets NodeBB v4 (nbbpm.compatibility: ^4.0.0). .npmignore and eslint.config.mjs are in place; npm run lint passes. For production, confirm in your NodeBB instance that API requests from the client hit the routes your plugin registers; adjust server route prefix if your NodeBB mounts plugin routes differently.