Plugin System
Plugin System
Understanding Plugins
What are Plugins?
Plugins are self-contained extensions that:
- Extend Functionality - Add new features to your forum
- Modify Behavior - Change how things work
- Integrate Services - Connect with external APIs
- Customize Experience - Tailor forum to your needs
Plugin Architecture
Plugins follow a structured architecture:
plugins/
└── my-plugin/
├── plugin.json # Plugin metadata
├── MyPlugin.php # Main plugin class
├── assets/ # CSS, JS, images
│ ├── css/
│ ├── js/
│ └── img/
├── views/ # Template files
│ └── admin.php
└── README.md # DocumentationInstalling Plugins
Method 1: From the Admin Panel (Recommended, since 5.2.2)
.zip archive — no FTP or shell access required.- Download the plugin
.zipfrom the Flatboard Resource Center or from the plugin author - Go to Admin → Plugins
- Click "Install a plugin"
- Select the
.ziparchive and confirm — the archive is validated, extracted, and the plugin appears in the list automatically
The server validates the archive integrity, MIME type, size, and the presence of a valid plugin.json before extracting.
Method 2: Manual Installation (FTP / SSH)
Plugin packages are distributed as .zip archives. The manual workflow is: download → extract locally → upload the folder → activate.
Step 1: Download and Extract
- Download the plugin
.zipfrom the Flatboard Resource Center or from the plugin author - Verify that the plugin is compatible with your Flatboard 5 version
- Extract the archive on your local machine — you should get a folder (e.g.
my-plugin/) withplugin.jsonat its root
.zip file itself to the server. FTP transfers files individually — you must extract the archive first, then upload the resulting folder.Step 2: Upload the Plugin Folder
Via FTP
Connect to your server with an FTP client (FileZilla, Cyberduck, etc.) and upload the extracted plugin folder into the plugins/ directory of your Flatboard installation:
your-flatboard/
└── plugins/
└── my-plugin/ ← upload this folder (not the .zip)
├── plugin.json
└── ...Via SSH
If you have shell access, you can extract the archive directly on the server:
cd /path/to/your-flatboard/plugins/
unzip my-plugin.zip # extracts to my-plugin/
chmod 755 my-plugin/ -R # adjust permissions if neededStep 3: Activate the Plugin
Once the plugin folder is in place:
- Go to Admin → Plugins
- Find the plugin in the list — it will appear as inactive
- Click Activate
Step 4: Configure (if applicable)
If the plugin has settings, click the settings icon next to the plugin name, fill in the options, and save.
Plugins from Flatboard 3 and 4 are NOT compatible with Flatboard 5 due to the complete architecture rewrite. You'll need to:
- Wait for plugin developers to release Flatboard 5 versions
- Contact plugin developers for updates
- Consider alternative plugins if updates aren't available
Creating a Plugin
Step 1: Create Plugin Structure
mkdir -p plugins/my-plugin/{assets/{css,js,img},views}Step 2: Create plugin.json
{
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"description": "A custom plugin for Flatboard 5",
"author": "Your Name",
"license": "GPL3",
"requires": {
"flatboard": ">=5.0.0"
},
"class": "App\\Plugins\\MyPlugin\\MyPluginPlugin"
}Step 3: Create Plugin Class
plugins/my-plugin/MyPluginPlugin.php:
<?php
namespace App\Plugins\MyPlugin;
use App\Core\Plugin;
use App\Helpers\PluginAssetHelper;
class MyPluginPlugin
{
public function boot()
{
// Register plugin routes (preferred hook for plugins)
Plugin::hook('router.plugins.register', [$this, 'registerRoutes']);
// Inject CSS into <head>
Plugin::hook('view.header.styles', [$this, 'addStyles']);
}
public function addStyles(array &$styles): void
{
$styles[] = PluginAssetHelper::loadCss('my-plugin', 'assets/css/style.css');
}
public function registerRoutes($router): void
{
$router->get('/my-plugin', function() {
return 'Hello from my plugin!';
});
}
}Step 4: Register Hooks
Flatboard 5 provides many hooks:
app.routes.register- Register custom routesview.header.styles- Add CSS styles to headerview.footer.content- Add content to footerview.post.avatar- Override post avatar renderingview.user.avatar- Override user avatar rendering anywhere on the forumdiscussion.created- When discussion is createdpost.created- When post is createduser.registered- When user registersuser.updated- When user is updatednotification.types.register- Register custom notification typesvisitor.page_info- Provide page metadata for presence indicator
See the complete hook list below for all available hooks.
Plugin Hooks
Available Hooks
Complete reference of all 96 hooks available in Flatboard 5. All data arguments marked by ref are passed by PHP reference — modifying them changes the actual value used by the framework.
Application
| Hook | Trigger point | Data |
|---|---|---|
app.routes.register | Application bootstrap | Router instance |
router.plugins.register | Router dispatch | Router instance |
router.not_found | Unmatched HTML route (404) | ['url' => string, 'redirect' => ?string] by ref — set redirect to issue a 301 |
routes.register | PluginHelper::registerRoute() — called when a plugin registers a route | $routes array by ref — internal hook used by the helper to append the route |
security.csp.directives | CSP header generation | CSP directives array by ref |
View — Layout
| Hook | Trigger point | Data |
|---|---|---|
assets.styles | PluginHelper::addStyle() — called when a plugin registers a stylesheet | Styles array by ref — internal hook used by the helper to append the asset |
assets.scripts | PluginHelper::addScript() — called when a plugin registers a script | Scripts array by ref — internal hook used by the helper to append the asset |
view.header.styles | <head> (frontend + backend) | Styles array by ref |
view.footer.scripts | </body> (frontend + backend) | Scripts array by ref |
view.footer.content | Footer (frontend) | HTML content array by ref |
view.footer.links | Footer links (frontend) | Links array by ref |
view.footer.bottom | Inside <footer> just before closing tag (frontend) | HTML string by ref — use to append content at the very bottom of the footer (e.g. legal links) |
view.navbar.items | Main navigation bar | Nav items array by ref |
view.main.before | Before main content (frontend) | HTML string by ref |
view.main.after | After main content (frontend) | HTML string by ref |
view.admin.sidebar.items | Admin sidebar | Sidebar items array by ref |
view.admin.main.before | Before admin main content | HTML string by ref |
view.admin.main.after | After admin main content | HTML string by ref |
view.theme.content.filter | Footer custom content + hero custom content (Premium theme) | Content string — return value replaces the original content |
View — Banner
| Hook | Trigger point | Data |
|---|---|---|
view.banner.data | Banner component render | Banner data array by ref |
view.banner.content | Banner component render | HTML string by ref, banner data |
View — SEO
| Hook | Trigger point | Data |
|---|---|---|
seo.meta.before | <head> meta tags | SEO data array by ref |
seo.meta.after | After default meta tags | Additional meta HTML string by ref |
View — Auth Forms
| Hook | Trigger point | Data |
|---|---|---|
view.login.form | Login page + login modal | Form fields array by ref |
view.login.validation | Login form submission | Validation error by ref |
view.user.register.form | Registration page + modal | Form fields array by ref |
view.user.register.validation | Registration form submission | Validation error by ref |
View — Discussions & Replies
| Hook | Trigger point | Data |
|---|---|---|
view.discussions.toolbar | Discussion list toolbar (all themes) | HTML string by ref — append extra toolbar buttons |
view.discussion.badges | Discussion item card — badge row | Badges array by ref, discussion data — append extra status badges |
view.discussion.create.form | New discussion form | Form fields array by ref |
view.discussion.create.validation | Discussion creation submission | Validation error by ref |
view.discussion.edit.form | Discussion edit form | Form fields array by ref, form context array |
view.discussion.show.before_content | Discussion view (before first post) | Event data array by ref |
view.discussion.show.after_content | Discussion view (after first post) | Event data array by ref |
view.reply.create.form | Reply form | Form fields array by ref |
view.reply.create.validation | Reply submission | Validation error by ref |
view.sidebar.navigation | Discussion list sidebar | Sidebar items array by ref |
view.post.user_data | Post thread component — user data setup | ['post' => array, 'user_data' => array] by ref — modify user_data to override author display data (name, avatar, etc.) for virtual users or bots |
view.post.avatar | Post thread component — avatar render | ['user_id', 'username', 'avatar', 'size', 'html'] by ref — set html to override the default <img> output |
view.user.avatar | components/avatar.php and discussion-item list (author + last-post author) | ['user_id', 'username', 'avatar', 'size', 'html'] by ref — set html to override the rendered avatar for any user across the forum |
view.post.user.stats | Post thread component — below the post count badge | ['user_id' => string, 'html' => string] by ref — append to html to inject extra badges/stats next to the post counter (all themes) |
view.post.dropdown.items | Post action dropdown menu (first post and replies) | Hook data array by ref (postId, discussionId, canEdit, html) — append extra dropdown items |
view.post.edit.form | Post / reply edit form | Hook data array by ref (postId, discussionId, content) |
view.profile.stats | User profile page — statistics card | ['user_id' => string, 'html' => string] by ref — append to html to inject extra rows below the discussions/replies counters |
Content Events
| Hook | Trigger point | Data |
|---|---|---|
discussion.created | After discussion saved | Enriched event data array |
discussion.created.legacy | After discussion saved | Created discussion (legacy format) |
discussion.updated | After discussion edited (DiscussionController::update()) | Updated discussion data |
discussion.deleted | After discussion deleted | Discussion data |
categories.list.filter | DiscussionController::index() and ::forums() — after categories sorted | Categories array by ref — remove or reorder categories before rendering |
discussions.index.filter | DiscussionController::index() — after discussions loaded | Discussions array by ref — remove discussions from the all-discussions feed before rendering |
best_answer.set | Best answer selected | ['discussion_id', 'post_id', 'user_id'] |
post.created | After reply saved | Created post data |
post.updated | After reply edited | Updated post data |
post.deleted | After reply deleted | Post data |
category.created | After category saved | Created category data |
category.updated | After category updated | Updated category data |
category.deleted | After category deleted | Category data |
tag.created | After tag saved | Tag data |
User Events
| Hook | Trigger point | Data |
|---|---|---|
user.registered | After registration | Created user data |
user.updated | After user profile saved | Updated user data |
user.deleted | After user account deleted | User data |
ban.created | After ban applied | Updated user data |
ban.removed | After ban lifted | Updated user data |
user.profile.tabs | Profile page render | Tabs array by ref, user array, isOwnProfile bool |
user.profile.tab.contents | Profile page render | Tab contents array by ref, user array, isOwnProfile bool |
user.settings.notifications | Notifications settings form | Options array by ref, preferences array |
user.settings.notifications.save | Notifications settings save | Preferences array by ref, request data, user |
user.settings.preferences.save | General preferences save | Preferences array by ref, request data, user |
Moderation
| Hook | Trigger point | Data |
|---|---|---|
report.created | After content report saved | Created report data |
Search
| Hook | Trigger point | Data |
|---|---|---|
search.results | Search service (all types) | Results array by ref, query, type, keywords, filters |
search.before.display | Before HTML search results | Results array by ref, query, type, filters |
search.ajax.results | Before AJAX search results | Results array by ref, query, type, filters |
Notifications
| Hook | Trigger point | Data |
|---|---|---|
notification.types.register | NotificationService::validateNotificationType() — called at boot time | $allowedTypes array by ref — append custom type strings to extend the core whitelist |
notification.before.create | Before notification written | Notification data array by ref, type string |
notification.after.create | After notification written | Notification ID, notification data |
notification.email.body | Before notification email sent | Email body string by ref, notification data |
Markdown & Editor
| Hook | Trigger point | Data |
|---|---|---|
markdown.pre_process | Before markdown parsing (MarkdownHelper) | ['raw' => string, 'context' => string] by ref — modify raw to alter the source before parsing |
markdown.parse | Markdown rendering (all contexts) | Result array by ref, raw markdown string |
markdown.post_process | After markdown parsing (MarkdownHelper) | ['html' => string, 'context' => string] by ref — modify html to post-process the rendered output |
markdown.editor.config | Editor component init | Config array by ref, $editorId |
component.markdown-editor.before | Before editor textarea | HTML string by ref, $editorId |
component.markdown-editor.attributes | Editor textarea tag | Attributes array by ref, $editorId, config |
markdown.editor.init | Editor JS init script | Script string by ref, $editorId, config |
markdown.editor.getValue | Editor getValue script | Script string by ref, $editorId |
markdown.editor.setValue | Editor setValue script | Script string by ref, $editorId, value path |
component.markdown-editor.after | After editor textarea | HTML string by ref, $editorId |
Presence & Visitor Tracking
| Hook | Trigger point | Data |
|---|---|---|
visitor.page_info | Visitor::getPageInfo() — unknown URLs only | ['page' => string, 'info' => array] by ref — set info['type'], info['title'], info['url'] |
visitor.before_track | VisitorTrackingMiddleware before write | ['ip_address', 'user_agent', 'page', 'user_id'] by ref |
presence.anonymous_visitors | PresenceService::getActiveAnonymousVisitorsDetailed() | Visitors array by ref |
presence.bots | PresenceService::getActiveBotsDetailed() | Bots array by ref |
presence.users | PresenceService::getActiveUsersDetailed() | Users array by ref |
Admin
| Hook | Trigger point | Data |
|---|---|---|
admin.dashboard.widgets | Admin dashboard render | Widgets array by ref |
Cron
| Hook | Trigger point | Data |
|---|---|---|
cron.register | PluginHelper::registerCronTask() — called when a plugin registers a scheduled task | $tasks array by ref — internal hook used by the helper to append the task definition |
Theme
| Hook | Trigger point | Data |
|---|---|---|
theme.setting.value | ThemeHelper::getThemeSetting() — after reading a theme setting | Value by ref, setting key, field type — allows plugins to override theme setting values |
themeswitcher.themes.expand | Theme Switcher — build available themes list | Themes array by ref — append sub-theme definitions (e.g. Bootswatch variants) |
themeswitcher.theme.validate | Theme Switcher — validate theme selection | ['themeId', 'subThemeId', 'valid'] by ref — set valid to override validation |
themeswitcher.theme.activate | Theme Switcher — apply theme switch | ['themeId', 'subThemeId'] by ref — run side effects on theme activation |
Plugin Views
| Hook | Trigger point | Data |
|---|---|---|
plugin.view.index | Plugin index view rendered | ['pluginId', 'viewName', 'viewVars'] by ref |
plugin.view.{viewName} | Named plugin view rendered | ['pluginId', 'viewName', 'viewVars'] by ref |
plugin.view.index.permissions | Before plugin index view | Permissions array by ref, pluginId, 'index' |
plugin.view.{viewName}.permissions | Before named plugin view | Permissions array by ref, pluginId, viewName |
plugin.view.{viewName}.vars | Before named plugin view | ViewVars array by ref, pluginId, viewName |
plugin.view.admin.vars | Before plugin admin view | ViewVars array by ref, pluginId, 'admin' |
plugin.view.privacy.vars | Before plugin privacy view | ViewVars array by ref, pluginId, 'privacy' |
plugin.view.cookies.vars | Before plugin cookies view | ViewVars array by ref, pluginId, 'cookies' |
Webhooks
| Hook | Trigger point | Data |
|---|---|---|
webhook.received | Inbound webhook payload | Webhook data array |
Using Hooks
// Register a hook
Plugin::hook('discussion.created', function($discussion) {
// Do something when discussion is created
Logger::info("New discussion: " . $discussion['title']);
});
// Register with priority
Plugin::hook('post.created', [$this, 'handlePostCreated'], 10);
// Extend the notification type whitelist (by reference)
Plugin::hook('notification.types.register', function(array &$types): void {
$types[] = 'my_custom_type';
$types[] = 'reputation_badge';
});
// Override avatar rendering for a virtual user (by reference)
Plugin::hook('view.user.avatar', function(array &$data): void {
if ($data['user_id'] === 'my-bot') {
$data['html'] = '<span class="avatar-bot"><i class="fas fa-robot"></i></span>';
}
});Plugin Configuration
Settings Storage
Plugin settings are stored in the "plugin" section of plugin.json and accessed via Plugin::getData(). Do not use Config::get/set for plugin-specific settings.
use App\Core\Plugin;
// Read a setting (with optional default)
$value = Plugin::getData('my-plugin', 'setting', 'default');
// Dot-notation is supported for nested keys
$host = Plugin::getData('my-plugin', 'smtp.host', '');
// Write a setting (in-memory only)
Plugin::setData('my-plugin', 'setting', 'value');
// Persist all settings to plugin.json
Plugin::saveData('my-plugin', ['setting' => 'value', 'another' => 'data']);Admin Interface
Create admin settings page:
- Create view file:
views/admin.php - Register admin route
- Handle form submission
- Save settings to config
Best Practices
Plugin Development
- Follow Structure - Use standard plugin structure
- Namespace Properly - Use proper namespaces
- Document Code - Add comments and documentation
- Handle Errors - Implement error handling
- Test Thoroughly - Test before release
Security
- Validate Input - Always validate user input
- Sanitize Output - Sanitize all output
- Check Permissions - Verify user permissions
- Use CSRF Protection - Protect forms with CSRF tokens
Performance
- Lazy Load - Load assets only when needed
- Cache When Possible - Cache expensive operations
- Minimize Queries - Optimize database/file access
- Use Hooks Efficiently - Don't overload hooks
Managing Plugins
Activating/Deactivating
- Activate - Enable plugin functionality
- Deactivate - Disable without uninstalling
Uninstalling a Plugin
Uninstalling permanently removes a plugin and all its data from your forum.
Using the Admin Panel
- Navigate to Admin → Plugins
- Deactivate the plugin first — the Uninstall button is blocked for active plugins
- Click the Uninstall button (trash icon, red/danger style) next to the plugin
- Confirm in the confirmation dialog that appears
- The server will:
- Run the plugin's
uninstall()hook (if defined), thendeactivate()hook - Remove all plugin permissions
- Clear asset and translation caches
- Remove the plugin's entry from
plugins.enabled - Recursively delete the plugin directory from
plugins/
- Run the plugin's
stockage/ (JSON or SQLite) is not automatically removed — consult the plugin's documentation for manual cleanup steps if needed.Always create a backup before uninstalling a plugin you may want to restore later.
Restrictions
- Core plugins (marked
cantDisable = "1"inplugin.json) cannot be uninstalled — the button is hidden - Active plugins cannot be uninstalled — deactivate the plugin first
Implementing the Uninstall Hook
Plugins can run cleanup logic when uninstalled:
public function uninstall(): void
{
// Clean up plugin-specific data in stockage/, custom tables, etc.
// Called before the plugin directory is deleted
}
public function deactivate(): void
{
// Called when plugin is deactivated (also called during uninstall)
}Updating Plugins
- Backup - Backup your forum first
- Download - Get new plugin version
- Replace Files - Replace plugin files
- Clear Cache - Clear forum cache
- Test - Test plugin functionality
Plugin Dependencies
Some plugins require other plugins:
{
"requires": {
"flatboard": ">=5.0.0",
"plugins": {
"another-plugin": ">=1.0.0"
}
}
}Pro Plugins
FlatSEO (v1.2.1)
A complete SEO toolkit for Flatboard, fully admin-driven — no code editing required.
Meta & Social
- Configurable title format with custom separators
- Open Graph (Facebook, LinkedIn) and Twitter/X Cards meta tags
- Per-page image overrides for social sharing previews
Structured Data (JSON-LD)
Automatically generated JSON-LD schemas:
| Schema type | Applied to |
|---|---|
WebSite + SearchAction | All pages |
Organization | Forum home |
BreadcrumbList | All pages |
DiscussionForumPosting | Discussion pages |
QAPage | Discussions with a best answer |
ProfilePage | User profile pages |
Sitemap & Crawling
- XML sitemap with per-URL
priorityandchangefreqsettings - Image sitemap support
- Live cache invalidation on content changes
- Full robots.txt editor in the admin panel
- Global noindex controls for search pages, user profiles, and tag pages
Redirects & Verification
- 301/302 redirect manager — rule-based, no
.htaccessediting - Search engine verification codes: Google, Bing, Yandex, Pinterest
Analytics Injection
- Google Analytics 4 and Google Tag Manager script injection (admin-configurable, no template editing)
SEO Audit
- Per-discussion SEO scoring with actionable recommendations
- Auto-translated breadcrumbs for all 5 supported languages
TUI Editor (v1.3.1)
A modern Markdown/WYSIWYG editor powered by Toast UI Editor, replacing the default editor globally.
Editing Modes
- Markdown mode — raw Markdown with live preview
- WYSIWYG mode — rich text editing, switchable at any time
- Preview layout: side-by-side or tabbed
Features
| Feature | Details |
|---|---|
| Image upload | Drag-and-drop or browse |
| Video embeds | YouTube and TikTok direct URL paste |
| Table editor | Visual table creation and editing |
| Syntax highlighting | CodeMirror-powered fenced code blocks |
| Emoji picker | Built-in emoji panel |
| Autosave | Configurable interval (seconds) |
| Spell checker | Browser spell-check toggle |
| Fullscreen mode | Distraction-free writing |
| Toolbar | Per-context configuration (new discussion, reply, admin) |
Private Messaging (v1.1.1)
A full-featured direct messaging system between registered members.
Mailbox
- Inbox, Sent, and Drafts views
- Read receipts — know when your message has been read
- Typing indicators — real-time "is typing…" indicator
- Full-text message search
Composer
- Emoji picker
- Optional file attachments
- Configurable maximum message length (up to 50,000 characters)
Notifications
- Email notification on new message (respects per-user preferences)
- In-forum notification badge
Administration
Configurable from Admin → Plugins → Private Messaging → Settings:
| Setting | Default | Range |
|---|---|---|
| Max message length | 5 000 chars | up to 50 000 |
| Max messages per user (inbox) | 100 | configurable |
| Max recipients per message | 1 | configurable |
| Pagination (messages per page) | 20 | configurable |
| Auto-delete old messages | Off | 1–365 days |
Additional options:
- User block list — members can block specific users
- Welcome/rules message — Markdown-formatted message shown to new users
- Date display — relative ("2 hours ago") or absolute
FlatHome (v1.0.2)
A full-featured homepage, CMS, and blog system for Flatboard. FlatHome transforms your forum into a complete web platform with a landing page, custom CMS pages, a blog with comments, and a flexible navigation builder.
CMS Pages
- Unlimited custom pages with Markdown or WYSIWYG editor
- Auto-generated URL slugs (editable)
- Page view counter
- PHP template system — assign custom PHP templates (e.g.
home,contact) to any page - Page groups — group pages into dropdown menus in the navigation bar
- Multi-language menu labels — per-language labels for nav items
- Discussion-linked pages — a page can mirror a forum discussion
- Homepage flag — designate any page as the site homepage (single enforcement)
Homepage Modes
| Mode | Description |
|---|---|
page | A CMS page is the site root |
forum | The discussion list is the site root |
blog | The blog archive is the site root |
Blog
- Blog posts are forum discussions from a designated category
- Full article view with hero banner, author info, tags, and read-time estimate
- Markdown comments — logged-in members can post comments via AJAX
- Blog sidebar — recent posts, popular posts, tags, categories
- Blog category can be hidden from the main forum list
- Shortcodes in page content:
{remote_version},{remote_codename},{remote_release_date}
Landing Page Template
The built-in home template (modeles/home.php) renders a modern landing page with:
- Hero section — headline, lead text from page content, two CTA buttons, live discussion preview
- Features section — four key capability chips
- Join section — documentation, bug report, and community cards
- Blog preview — latest blog articles
Navigation Builder
The Navigation order panel in the admin allows drag-and-drop reordering of all nav items (forum, pages, page groups, blog). Items can be dragged into a group to create a dropdown.
Contact Page Template
The built-in contact template provides a ready-made contact form. The recipient email is resolved from plugin settings or the forum configuration. All labels are translated in 5 languages.
Admin Panel
Accessible at Admin → FlatHome — manage pages, page groups, blog settings, navigation order, and plugin configuration from a single interface.
Forum Monitoring (v1.1.1)
Real-time health monitoring and anomaly detection for your forum.
Activity Chart
Visualizes forum activity over a configurable time window:
- Available windows: 7, 14, 30, 90, 180, 365 days
- Plots discussions, posts, and member registrations over time
Leaderboards
- Top users — ranked by post count (up to 100 entries)
- Top discussions — ranked by reply count (up to 100 entries)
Anomaly Detection
- Automatically flags unusual activity patterns (traffic spikes, burst posting)
- Configurable spam score threshold (scale 1–10)
- Email alerts sent to administrators when anomalies are detected
Admin Integration
- Admin dashboard widget — compact health summary on the main dashboard
- Dedicated section at Admin → Forum Monitoring
Flat Moderation Extend (v1.0.1)
Advanced moderation tools that go beyond the core moderation features.
Pre-moderation Queue
Hold content from new users for review before publication:
- Configurable post-count threshold — users below this number of approved posts have their content held automatically
- Held discussions and replies are invisible to other members until approved or rejected
- All administrators and moderators receive an instant notification with a direct link to the moderation queue when new content is held
- Accessible at Admin → Moderation Queue
Shadow Banning
Silently hide content from a disruptive user without alerting them:
- The shadow-banned user sees their own posts and discussions as normal
- Other members do not see the shadow-banned user's content
- Applied per-user from the admin panel
Discussion Merging
Merge two discussions into one:
- Replies from the merged discussion are moved to the target discussion
- Automatic 301-style redirects from the old discussion ID to the new one
- Prevents broken links and consolidates duplicate threads
Bulk Actions
Perform moderation actions on multiple discussions at once:
- From the admin panel: pin, lock, solve, move, hide, or delete multiple discussions
- From the frontend discussion list: moderators can perform bulk actions without leaving the forum
Configuration (Admin → Plugins → Flat Moderation Extend → Settings):
| Setting | Default | Description |
|---|---|---|
| Pre-moderation enabled | On | Enable/disable the pre-moderation queue |
| Post-count threshold | 5 | Users with fewer approved posts are subject to pre-moderation |
| Shadow ban enabled | On | Enable/disable the shadow ban feature |
Storage Migrator (v1.1.1)
Zero-downtime migration between the two storage backends — JSON flat-file and SQLite.
Overview
| Details | |
|---|---|
| Direction | JSON → SQLite and SQLite → JSON (bidirectional) |
| Downtime | None — forum remains accessible during migration |
| Safety | Original data is preserved until migration is confirmed |
| Rollback | Fully rollback-friendly at any step |
How It Works
- Go to Admin → Storage Migrator
- Select the target storage (JSON or SQLite)
- The migrator copies all data (discussions, posts, users, categories…) to the new backend atomically
- Verify the result — the old data remains intact
- Confirm the switch — the forum now uses the new backend
pdo_sqlite PHP extension. The migrator will warn you if the extension is missing before starting.Community Plugins
EasyMDE (v2.3.2)
A modern Markdown editor powered by EasyMDE, replacing the default editor globally.
Editing Features
| Feature | Details |
|---|---|
| Syntax highlighting | CodeMirror-powered fenced code blocks |
| Image upload | Browse or paste images directly into the editor |
| Video embeds | YouTube and TikTok direct URL paste |
| Emoji picker | Built-in emoji panel |
| Autosave | Configurable interval (milliseconds) |
| Spell checker | Browser spell-check toggle |
| Side-by-side preview | Live Markdown preview panel |
| Fullscreen mode | Distraction-free writing |
| Toolbar | Per-context configuration (new discussion, reply, edit, PM, admin pages) |
Configuration (Admin → Plugins → EasyMDE → Settings)
Key options:
| Setting | Default | Description |
|---|---|---|
| Syntax highlighting | On | Highlight code in fenced blocks |
| Spell checker | On | Enable browser spell checking |
| Autofocus | On | Focus the editor on page load |
| Line numbers | Off | Show line numbers in editor |
| Line wrapping | On | Wrap long lines |
| Tab size | 2 | Number of spaces per tab |
| Autosave | Off | Save draft to browser storage periodically |
| Autosave delay | 10 000 ms | Interval between autosaves |
| Toolbar per context | (see below) | Customizable toolbar for each editor context |
Toolbar contexts: discussion-create, reply, post-edit, discussion-edit, settings, pm, page-edit, default.
Logger (v1.1.5)
A forum activity logger with optional webhook delivery. Every selected event is recorded and can be forwarded in real time to an external URL.
Tracked Events
All events can be toggled individually:
| Category | Events |
|---|---|
| Discussions | created, updated, deleted |
| Posts | created, updated, deleted |
| Users | registered, updated, deleted |
| Categories | created, updated, deleted |
| Tags | created, updated, deleted |
| Reports | created |
| Bans | created, removed |
Webhook Delivery
Set a webhook URL in the plugin settings to receive a POST request for each enabled event. The payload contains the full event data.
Configuration (Admin → Plugins → Logger → Settings):
| Setting | Description |
|---|---|
| Webhook URL | Target URL for event delivery (leave empty to disable) |
| Enabled events | Toggle each event type individually |
Troubleshooting
Plugin Not Loading
Check:
- Plugin structure is correct
plugin.jsonis valid- Plugin class exists and is correct
- Check error logs
Plugin Conflicts
Solution:
- Deactivate conflicting plugins
- Check hook priorities
- Review plugin code
- Contact plugin developers
Plugin Errors
Solution:
- Check error logs
- Enable debug mode (temporarily)
- Review plugin code
- Check Flatboard 5 compatibility
Resources
- Developer Guide - Advanced development
- API Documentation - API integration
- Theme Guide - Theme integration
- Troubleshooting - Common issues
Last updated: March 17, 2026