Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions INTEGRATION_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Discourse integration notes

## Rendering model

BBCode is registered through Discourse's Markdown plugin pipeline, using the
same bundled BBob parser for server cooking and browser previews. A markdown-it
core rule runs before normalization and detects whether the source needs custom
BBCode processing. The `bbcode_enabled` setting and Markdown feature overrides
control whether that rule is registered, including restricted cooks such as chat.

Posts without custom BBCode retain native Markdown paragraphs, headings, lists,
tables, whitespace, and code blocks. Discourse's `[b]`, `[i]`, `[u]`, and `[s]`
tags also stay native on that path. BBCode shown only inside native Markdown
code examples does not enable layout processing.

For a post containing custom BBCode, BBob processes the source before Markdown.
That whole post retains the plugin's legacy whitespace conventions: paragraph
wrappers are suppressed and indented layout markup is not treated as a native
indented code block. These differences apply to surrounding Markdown too; this
implementation does not isolate processing to individual BBCode spans. Core
inline tags nested in those posts use the BBob preset handlers.

A renderer extension restores content hoisted by the parser, including code
examples and custom styles. Restored code text is escaped, so HTML inside
`[code]` or `[plain]` appears as source. Allowed HTML outside code examples still
uses Discourse's normal rendering and sanitization. The integration does not
replace Discourse's engine initialization or bypass its sanitizer.

## Imported source

- Use native Markdown lists and tables. This change adds no XenForo `[list]`,
`[*]`, `[table]`, `[tr]`, `[td]`, `[th]`, or `[tf]` handlers. Convert unsupported
imported syntax while preserving its contents; inspect complex tables individually.
- Markdown headings remain active inside layout tags such as `[div]` and
`[nobr]`. Escape a line's leading hash as `\# label` when it is literal text.
Rebaking cannot determine the author's original intent.
- This change adds no automatic repair of missing or misnested closing tags.
Malformed source can lose content during parsing; correct it before relying
on a rebake to reproduce the original layout.

Use the composer's Markdown mode for BBCode. This change does not add a rich-text
BBCode editor. External fonts, images, and optional Font Awesome kits still need
available resources and their normal configuration.

## Existing safeguards and behavior

The current CSS containment rules, cross-process parser reset, spoiler fix, and
rehosting of external image URLs used in BBCode CSS are retained. Initializers
for fonts, icons, and BBCode highlighting respect the plugin's enabled setting.

Containment and Discourse's sanitizer serve different purposes; this integration
does not establish a sandbox for arbitrary user HTML, CSS, or scripts. BBScript
is optional and remains disabled by default. Expanding that interpreter or
introducing unrestricted HTML requires a separate security review.

## Deployment and rebaking

1. After changing `bbcode-src`, rebuild and include the generated bundle and
source map:

```sh
pnpm install --frozen-lockfile
pnpm build
```

2. Deploy the full plugin. Restart Discourse web processes and Sidekiq so both
use the new renderer. Restart the frontend development build when adding or
removing plugin modules.
3. Correct unsupported or malformed imported source, then compare representative
posts in the composer preview and after saving.
4. Rebuild saved posts on the destination server from its Discourse environment:

```sh
RAILS_ENV=production bundle exec rake posts:rebake
```

For imported posts, follow the
[importer's verified rebake instructions](https://github.com/RpNation/discourse_xf_importer#readme),
using a fresh run ID after renderer changes. A previously completed checkpoint
does not establish that posts have been rebuilt with the new renderer.
5. Check failed post IDs and Sidekiq retry/dead jobs, allow background processing
to finish, and inspect representative layouts. Command completion does not
prove that every imported design renders faithfully.

Run full imports and site-wide rebakes on the migration servers, not on the
local development computer.

## Focused validation

Inside a Discourse checkout with this plugin installed and assets built:

```sh
LOAD_PLUGINS=1 bin/rspec plugins/bbcode/spec/lib/pretty_text_spec.rb
bin/qunit --target bbcode --filter BBCode
```

Check native Markdown and code examples, mixed BBCode layouts, enabled/disabled
settings, sanitization, and preview/save parity. Run lint for changed files and
review desktop and mobile layouts. Synthetic tests and browser emulation do not
establish that all historical designs or Safari/iOS render correctly; retain a
visual review of representative imported posts after source conversion.
40 changes: 34 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,18 @@

RpNation's Official BBCode Implementation for Discourse

See: [https://www.rpnation.com]
See [RpNation](https://www.rpnation.com).

The goal of this repo and plugin is to provide users with the BBCode suite that they have grown accustomed to when it comes to using our site before our migration to Discourse and make sure that old posts rebake correctly. Above in the chart is marked our status on each BBCode which will hopefully co-exist in tandom even with the markdown/htlm versions provided in the box experience by the Discourse Software.
This plugin adds RpNation's custom BBCode to Discourse while using native
Discourse formatting where it already exists. Use Markdown for lists and tables,
and Discourse's own inline bold, italic, underline, and strikethrough tags.
Custom layout tags remain available for designs that need them.

Imported XenForo posts may need source conversion before rebaking: this plugin
does not add XenForo list/table syntax or automatically repair malformed tags.
Markdown headings also work inside layout tags; write `\# label` when the hash
should be literal. See the [integration notes](INTEGRATION_NOTES.md) for migration
and rendering boundaries.

## Features/Planned

Expand Down Expand Up @@ -52,7 +61,8 @@ The goal of this repo and plugin is to provide users with the BBCode suite that
- [x] Sides
- [x] Tabs
- [x] Accordions
- [x] ~~Tables~~ now using markdown tables
- [x] Native Discourse Markdown tables
- [x] Native Discourse numbered and bulleted lists
- [x] Center Block
- [x] Background
- [x] Border
Expand Down Expand Up @@ -105,6 +115,24 @@ For more, see the [Discourse Docker Guide](https://meta.discourse.org/docs?topic

## Architecture

The architecture of this project is for all BBCode Parser related code to be contained in `/bbcode-src`, which would then be minified into a module and added to the appropriate location in `/assets/javascripts` to be used by the discourse plugin proper. This is to work around the weird way discourse requires libraries to be loaded in. There will be a Rollup config and github action setup to automate minifying and moving the module.

Honestly, if anyone has a better solution, please send help.
Parser and tag implementations live in `bbcode-src`. Run
`pnpm install --frozen-lockfile` and `pnpm build` to regenerate the checked-in
parser bundle and source map after source changes. Install the full plugin in
Discourse's `plugins/bbcode` directory.

The same parser bundle serves server cooking and browser previews. A registered
Markdown plugin uses a markdown-it core rule to detect custom BBCode and process
it through BBob. Ordinary Markdown, core inline formatting, and Markdown code
examples containing BBCode keep native behavior when no custom BBCode occurs
outside those examples.

Processing remains per post: a post containing custom BBCode uses the plugin's
legacy whitespace and paragraph behavior throughout the post, including its
surrounding Markdown. This is not isolation of each BBCode span. Rendered output
still passes through Discourse's sanitizer.

Deploying a parser change does not refresh saved cooked HTML. Restart Discourse
web processes and Sidekiq, then follow the
[deployment and rebake instructions](INTEGRATION_NOTES.md#deployment-and-rebaking)
on the destination server. Existing CSS containment, parser resets, and CSS
image rehosting remain in place. BBScript remains optional and disabled by default.
2 changes: 1 addition & 1 deletion assets/bundled/bbcode-parser.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/bundled/bbcode-parser.min.js.map

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const postsMissingFontAwesome = [];

export default apiInitializer((api) => {
const siteSettings = api.container.lookup("service:site-settings");
if (!siteSettings.fontawesome_kit_url) {
if (!siteSettings.bbcode_enabled || !siteSettings.fontawesome_kit_url) {
return;
}
window.FontAwesomeConfig = {
Expand Down
8 changes: 6 additions & 2 deletions assets/javascripts/discourse/api-initializers/google-font.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { apiInitializer } from "discourse/lib/api";
function addGoogleFont(post) {
// Cleans up post if we're editing
const priorLinks = post.querySelectorAll("link[data-rendered-gfont]");
priorLinks.forEach((oldLink) => post.removeChild(oldLink));
priorLinks.forEach((oldLink) => oldLink.remove());

const elements = post.querySelectorAll("[data-font]");
if (!elements.length) {
Expand All @@ -22,7 +22,7 @@ function addGoogleFont(post) {
const data = e.getAttribute("data-font");
if (
!gFonts.includes(data) &&
data.startsWith("https://fonts.googleapis.com")
data.startsWith("https://fonts.googleapis.com/css2?")
) {
frag.appendChild(linkBuilder(data));
gFonts.push(data);
Expand All @@ -46,6 +46,10 @@ function linkBuilder(data) {
}

export default apiInitializer((api) => {
if (!api.container.lookup("service:site-settings").bbcode_enabled) {
return;
}

api.decorateCookedElement((elem) => addGoogleFont(elem), {
id: "add google font",
});
Expand Down
4 changes: 4 additions & 0 deletions assets/javascripts/discourse/api-initializers/highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ function markdownHighlight(hljs) {
}

export default apiInitializer((api) => {
if (!api.container.lookup("service:site-settings").bbcode_enabled) {
return;
}

api.registerHighlightJSLanguage("bbcode", bbcodeHighlight);
api.registerHighlightJSLanguage("markdown-bbcode", markdownHighlight);
});
15 changes: 0 additions & 15 deletions assets/javascripts/discourse/initializers/bbcode-parser-init.js

This file was deleted.

Loading
Loading