Skip to content

[Open Source] Discourse Reply-to-View / Login-to-View Plugin (discourse-reply-to-view) — Unlock Hidden Content by Replying

Web
5
2026-09-17
[Open Source] Discourse Reply-to-View / Login-to-View Plugin (discourse-reply-to-view) — Unlock Hidden Content by Replying

Foreword

While tinkering with my own forum (bbs.eeclub.top) recently, I needed a "reply to view" feature: members must reply to a topic before they can see hidden resource links, download addresses, and other content within the post. On one hand, this encourages forum activity; on the other hand, it prevents content from being outright freeloaded.

Discourse only ships with a [hide] tag (which hides content from anonymous users), which is nowhere near enough for a "reply to unlock" requirement. I searched around and found no existing plugin, so I just wrote one myself, naming it discourse-reply-to-view. The whole project was built with AI assistance (GLM) — I handled requirements, testing, and acceptance, while the frontend, backend, and tests came to over 2,000 lines of code. It was a remarkably smooth development experience.

AI large model API platform recommendations and overview: https://blog.zeruns.com/archives/947.html

Plugin Overview

discourse-reply-to-view is a Discourse content protection plugin that provides three BBCode tags:

Tag Function
[reply]content[/reply] Reply to view: only visible after replying to this topic
[login]content[/login] Login to view: visible once registered/logged in
[reply=N]content[/reply] Reply count: visible after posting at least N replies in this topic (optional toggle)

Markdown nests normally inside the tags — code blocks, images, links, and lists all work fine. Writing both tags inline on one line ([reply]xxx[/reply]) is also supported.

Key Features

  • Server-side permission checks: Hidden content visibility is determined 100% on the server. The cooked field in the database stores no hidden original text. Search indexes, notification emails, daily digests, Onebox previews, topic list excerpts, RSS, and raw export endpoints are all protected against leaks. This is not fake hiding with frontend CSS.
  • Three unlock modes: any_reply (any single reply unlocks everything, default), exact_post (must reply to the specific post containing the content), and count mode [reply=N].
  • Trust level exemption: You can configure users at or above a certain trust level to skip replying and view directly (off by default).
  • Usage permission control: You can restrict which trust levels are allowed to use the hide tags.
  • Editor integration: Two insert buttons are added to the "+" menu, available on both desktop and mobile — one click wraps the selected text.
  • Author WYSIWYG: When authors view their own posts, hidden content is shown with a dashed border and a hint bar, making it easy to verify tag placement.
  • Auto-unlock after replying: After a user replies successfully, the page refreshes locally — no need to manually reload the whole page.
  • Anti-misalignment injection: Each hidden block carries a fingerprint check. If the cooking and serialization stages don't match, the entire post degrades to a placeholder — it would rather hide than misalign.
  • Full language support: All 49 languages supported by Discourse are built in, with both frontend copy and admin settings translated (Simplified Chinese, English, Japanese, Korean, etc.).
  • Multilingual content leak prevention: Sites with content localization (automatic post translation) are also handled, preventing translated versions from bypassing permissions and leaking hidden content.

Usage

Insert via the "+" menu in the editor, or just write the BBCode by hand:

[reply]
Content here is only visible after replying, e.g. download links
[/reply]

[login]
Content here is visible once logged in
[/login]

[reply=5]
Content visible only after posting 5 replies (requires enabling count syntax in admin settings)
[/reply]

Frontend behavior: users who don't yet meet the condition see a placeholder box (reply-to-view uses a blue theme, login-to-view uses green). Clicking the button jumps straight to the reply box or login page. Once the condition is met, the full content displays automatically.

Security Design

Security is what I care about most with this plugin — after all, "fake hiding" (hiding on the frontend while all the data is still there) is essentially useless. The security architecture is designed like this:

  1. Zero original text at the storage layer: During Markdown rendering, the original text inside the tags is discarded; the database only stores a fingerprinted placeholder container <div class="rtv-block">.
  2. Injection at serialization time: The original text is only injected dynamically at API serialization based on the current user's permissions. If you haven't unlocked it, you can't get it — it's not even in the HTML source.
  3. All exit points sealed: The easily-overlooked holes — raw export, search index, email notifications, revision history diffs, and localized translation versions — are all plugged.
  4. No cache leak window: Permission checks are computed in real time and take effect immediately after replying, so there's no risk of a low-privilege user hitting a high-privilege cached response.

Installation

For Discourse deployed via official Docker, edit containers/app.yml and add a line under hooksafter_code:

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/zeruns/discourse-reply-to-view.git

Then rebuild the container (frontend assets need recompiling, so you must rebuild, not restart):

cd /var/discourse
./launcher rebuild app

After installation, you can adjust the settings in Admin → Settings → Plugins:

  • enable_rtv: master switch for the plugin
  • reply_to_view_mode: reply unlock mode (any reply / exact post)
  • reply_to_view_allow_count: whether to enable [reply=N] count syntax
  • min_trust_level_to_bypass: trust level exemption (0 = no exemption)
  • min_trust_level_to_use: minimum trust level allowed to use the hide tags

Compatibility

Developed and verified on the latest Discourse (master branch, 2026-09 build). It depends on the new markdown-it BBCode pipeline, so Discourse 3.4 or above is recommended. For testing, 79 RSpec cases cover the permission matrix and each leak surface, all green across consecutive randomized-order runs.

If you find it useful, head over to GitHub and drop a Star to show support~

Summary

This project was also an experiment in "AI-led development with human acceptance": I set the requirements, boundary conditions, and security scenarios, while AI generated the code, test cases, and documentation. Along the way, it even caught several leak surfaces I hadn't thought of myself (raw export endpoint, revision history diff, translated versions) — AI really is more reliable than humans at enumerating these kinds of edge cases. If you're into running forums, give it a try. Questions are welcome — open an issue on GitHub, or leave a comment below.

  • 0

Comment