In Discourse, a theme component can use the plugin API to inject client-side logic and modify the user interface. To prevent moderators from managing or clearing flags on their own posts, we can customize the reviewable item interface to hide or disable the action buttons if the current user matches the post creator.
Here is a complete, production-ready Discourse theme component to achieve this.
1. Component Structure
A Discourse theme component requires a specific structure. Create a folder named discourse-prevent-self-moderation with the following files:
discourse-prevent-self-moderation/
├── about.json
└── common/
└── head_tag.html
2. File Contents
about.json
This file defines the metadata for your theme component.
{
"name": "Prevent Self-Moderation",
"component": true,
"license_url": "https://github.com/discourse/discourse/blob/main/LICENSE.txt",
"about_url": null,
"authors": "AI Collaborator",
"version": "1.0.0",
"minimum_discourse_version": "3.1.0"
}
common/head_tag.html
This is where the core logic lives. We hook into Discourse’s reviewable-item API to check if the post’s author matches the current user viewing the review queue.
<script type="text/javascript-initializer" version="0.10.1" name="prevent-self-moderation">
import { withPluginApi } from "discourse/lib/plugin-api";
export default {
name: "prevent-self-moderation",
initialize() {
withPluginApi("0.10.1", (api) => {
// Target the reviewable items in the review queue
api.modifyClass("component:reviewable-item", {
pluginId: "prevent-self-moderation",
// Computed property to check if the current user owns the flagged post
get isOwnPostFlag() {
const currentUser = this.currentUser;
const targetCreatedBy = this.args.reviewable.get("target_created_by");
if (!currentUser || !targetCreatedBy) {
return false;
}
// Compare user IDs
return currentUser.id === targetCreatedBy.id;
},
// Override or adjust actions based on ownership
get availableActions() {
const actions = this._super(...arguments);
if (this.isOwnPostFlag) {
// Option A: Strip out all actions so they can't click anything
return [];
// Option B (Alternative): If you want them to see a warning instead,
// you could map through actions and set `disabled: true` on them.
}
return actions;
},
// Optional: Add a visual indicator or warning message
didInsertElement() {
this._super(...arguments);
if (this.isOwnPostFlag) {
// Add a CSS class to the reviewable item container for custom styling
this.element.classList.add("is-self-flagged");
// Prepend a warning message to the reviewable item card
const warning = document.createElement("div");
warning.className = "alert alert-error self-moderation-warning";
warning.style.margin = "10px 0";
warning.innerText = "Conflict of Interest: You cannot moderate your own flagged post.";
this.element.insertBefore(warning, this.element.firstChild);
}
}
});
});
},
};
</script>
3. Optional CSS (For styling the warning)
If you want to make the warning look integrated, you can add a common/common.scss file to your component:
.reviewable-item.is-self-flagged {
border: 2px solid var(--danger-medium);
background-color: var(--danger-low);
// Hide the action buttons area completely just in case
.reviewable-actions {
display: none !important;
}
}
(If you add this, make sure to add “scss”: [“common/common.scss”] to your about.json targets).
4. How to Install This Component
- Compress the files: Zip the folder containing about.json and common/.
- Upload to Discourse:
- Go to your Discourse Admin panel \rightarrow Customize \rightarrow Themes.
- Click Install \rightarrow Upload a theme or component.
- Upload your .zip file.
- Activate it: Add this component to your main active theme(s) under the “Components” section at the bottom of the theme page.
[!WARNING]
Important Note on Security: Because theme components run entirely in the user’s browser (client-side), a tech-savvy moderator could technically bypass this restriction by using the browser console or making a direct API call to resolve the flag.
For absolute, bulletproof security, this logic should ideally be handled via a server-side Discourse Plugin using a Ruby backend hook (e.g., overriding Reviewable policies). However, this theme component is highly effective for enforcing standard UI guardrails and preventing accidental self-moderation.