Flatboard 5.3.0 - Changelog

Fred Fred ·19 March 2026 à 21:51·28 min read·299· 0 comment

🚀 Changelog — Flatboard 5.3.0 — Lighthouse

Release date: March 19, 2026


Highlights

  • FlatHome joins Flatboard Pro — FlatHome replaces EasyPages in the Pro plugin pack. It is a full CMS, homepage, and blog system that transforms any Flatboard forum into a complete web platform. EasyPages users can migrate their pages to FlatHome's richer page model.

Added

  • FlatHome — forum_enabled toggle — pure CMS mode — A new forum_enabled setting lets admins disable the forum entirely so Flatboard runs as a pure CMS. When disabled: the /forum route alias is no longer registered, the forum is removed from the navigation bar, and the "Forum" nav label row is hidden in the admin quick-settings card. Two toggle switches (Blog / Forum) are now shown at the top of the "Navigation labels" admin card; the corresponding label row hides dynamically when its feature is disabled, and the entire card collapses when both are off. The blog_enabled and forum_enabled values are saved via the existing quick-settings AJAX endpoint.
    Files changed: plugins/FlatHome/FlatHomePlugin.php, plugins/FlatHome/FlatHomeService.php, plugins/FlatHome/FlatHomePageController.php, plugins/FlatHome/views/admin/pages.php, plugins/FlatHome/plugin.json, plugins/FlatHome/langs/{en,fr,de,pt,zh}.json.

  • FlatHome — Generic home.php landing page template — The home.php template has been rewritten as a simple, site-agnostic landing page suitable for any Flatboard installation. It shows the site name and lead content (CMS page body, or a default message pointing to the admin when empty), CTA buttons for the forum and/or blog when those features are enabled, a dynamic accent band using var(--bs-primary), a recent blog posts section (when blog_enabled is on and posts exist), and a recent forum discussions section (when forum_enabled is on and discussions exist). Both activity sections are skipped when their respective feature is disabled or returns no data. The number of items shown in each section is controlled by template_recent_blog (default 3) and template_recent_forum (default 5). The previous Flatboard.org-specific layout (version pill, trust badges, browser mockup, feature chips, join cards) has been removed.
    Files changed: plugins/FlatHome/modeles/home.php.

  • discussions.index.filter hook — New hook fired in DiscussionController::index() after discussions are loaded, allowing plugins to remove discussions from the all-discussions feed before rendering. Used by FlatHome to hide blog discussions from the forum index when Hide blog category from forum is enabled — fixes the bug where the blog category and its discussions remained visible in SQLite mode.
    Files changed: app/Controllers/Discussion/DiscussionController.php, plugins/FlatHome/FlatHomePlugin.php.


Fixed

  • FlatHome — Users link missing from navbar — The CSS rule that hid the core "Users" link also matched FlatHome's re-injected link (same href). Fixed by adding :not([data-fh]) to the hide selector and a data-fh="1" attribute to FlatHome's own link, so only the core duplicate is suppressed.
    Files changed: plugins/FlatHome/FlatHomePlugin.php.

  • FlatHome — Blog category hidden from forum in JSON but not SQLite — In SQLite mode, getAllDiscussionsSorted() returns all discussions including blog ones, making the blog category label visible on discussion cards even when Hide blog category from forum was enabled. The new discussions.index.filter hook resolves this by filtering blog discussions from the forum feed at controller level, regardless of storage backend. ID comparisons now use explicit (string) casts for type safety.
    Files changed: app/Controllers/Discussion/DiscussionController.php, plugins/FlatHome/FlatHomePlugin.php.

  • FlatHome — Spurious "Comment published!" toast on blog article visit — The inline #flathome-reply-success element (class="alert alert-success") was always present in the DOM with display:none, causing the toast.js fallback scanner (which reads all .alert.alert-success elements regardless of visibility) to fire a success toast on every page load. Fixed by adding data-toast="none" to both the success and error inline alert divs, which are explicitly excluded by the scanner.
    Files changed: plugins/FlatHome/views/blog/article.php.

  • FlatHome — Share URL input empty on blog articles — The article share URL field appeared empty when the server-side UrlHelper::full() call produced no output (e.g. misconfigured site_url). The JavaScript IIFE now falls back to window.location.href when the input value is empty, and also updates all social share links in the same section to use the corrected URL.
    Files changed: plugins/FlatHome/views/blog/article.php.

  • FlatHome — Doubled icon live preview in Page groups form — The icon picker JS sets preview.innerHTML = '<i class="..."></i>' on the element referenced by previewId. Because the group icon preview was an <i> element itself, this nested a new <i> inside it, rendering two icons. Fixed by changing the preview element to a <span> container (consistent with the core admin pattern) and updating updateGroupIconPreview() to use innerHTML instead of className.
    Files changed: plugins/FlatHome/views/admin/pages.php.


🚀 Changelog — Flatboard 5.2.8

Release date: March 18, 2026


Added

  • FlatHome — home landing page template (modeles/home.php) — Initial version of the home PHP template for CMS pages. The template displays the page's CMS content as a hero lead section with CTA buttons and recent forum activity. Three shortcodes become available in any page content: {remote_version}, {remote_codename}, {remote_release_date}. (The template was substantially revised in 5.3.0 into a generic, site-agnostic layout.)
    Files changed: plugins/FlatHome/FlatHomeService.php, plugins/FlatHome/modeles/home.php (new).

  • FlatHome — Template badge in pages list — When a CMS page has an explicit PHP template assigned, its name is shown as a yellow <i class="fas fa-palette"> badge in the pages list alongside the "Homepage" and "Draft" badges.
    Files changed: plugins/FlatHome/views/admin/pages.php.

  • FlatHome — Explicit template selector in page form — The CMS page create/edit form now shows a "PHP Template" card in the sidebar when template files exist in plugins/FlatHome/modeles/. Admins can select a template from a dropdown (or leave it on "Default"). The selected template is stored in the page data as a template field and takes priority over slug-based auto-detection in both the page view and the early banner-hiding hook.
    Files changed: plugins/FlatHome/FlatHomePlugin.php, plugins/FlatHome/FlatHomePageController.php, plugins/FlatHome/views/admin/page_form.php, plugins/FlatHome/views/page.php, plugins/FlatHome/langs/{en,fr,de,pt,zh}.json.

  • FlatHome — Blog hidden from nav when it is the homepage — When homepage_type is set to blog, the "Blog" link is no longer added to the navigation bar (same behaviour as the Forum link when homepage_type = forum). A dedicated nav link for the active homepage is redundant since / already points there.
    Files changed: plugins/FlatHome/FlatHomeService.php.

  • FlatHome — Contact page template (modeles/contact.php) — A ready-to-use contact form template is included. It is activated by creating a CMS page with the slug contact. The recipient email is resolved in order: plugin setting contact_email, then mail.from_address / site_email / admin_email from the forum configuration. The form posts to /flathome/contact and displays flash messages on success or error. All labels are translated in 5 languages.
    Files changed: plugins/FlatHome/FlatHomePlugin.php, plugins/FlatHome/FlatHomePageController.php, plugins/FlatHome/FlatHomeService.php, plugins/FlatHome/modeles/contact.php (new), plugins/FlatHome/langs/{en,fr,de,pt,zh}.json.

  • FlatHome — PHP templates for CMS pages (modeles/) — FlatHome now supports PHP template files placed in plugins/FlatHome/modeles/{slug}.php. When a CMS page's slug matches a file in this folder, the template is used instead of the default page view. If no template is found, the standard view is used as fallback. The banner is always hidden for CMS homepage pages (regardless of whether a template is active), and also hidden for any page with an active template. The number of discussions and articles displayed is configurable via the new template_recent_forum (default 5) and template_recent_blog (default 3) plugin settings.
    Files changed: plugins/FlatHome/FlatHomePlugin.php, plugins/FlatHome/FlatHomeService.php, plugins/FlatHome/views/page.php, plugins/FlatHome/langs/{en,fr,de,pt,zh}.json.

  • FlatHome — Multilingual labels for page groups — The group form in the "Page groups" card now shows one label input per available language. Labels are stored as {"fr":"...","en":"..."} objects; the nav builder resolves the label using the current visitor language. Existing groups with plain-string labels continue to work without migration.
    Files changed: plugins/FlatHome/FlatHomePageController.php, plugins/FlatHome/FlatHomeService.php, plugins/FlatHome/views/admin/pages.php, plugins/FlatHome/langs/{en,fr,de,pt,zh}.json.

  • FlatHome — Date in banner for discussion-type CMS pages — Pages of type discussion now display the discussion's last-updated date below the page title in the banner. The $pageDate variable is added to the view.banner.data hook data and rendered in the page-type banner block.
    Files changed: plugins/FlatHome/FlatHomePlugin.php, themes/premium/views/components/banner.php.

  • FlatHome — Multi-language menu labels for CMS pages — The menu.label field in FlatHome CMS pages now supports per-language values (one input per available language in the page form). The nav builder resolves the label using the visitor's current language, with en as fallback. Existing pages with a plain string label continue to work without migration.
    Files changed: plugins/FlatHome/FlatHomeService.php, plugins/FlatHome/FlatHomePageController.php, plugins/FlatHome/views/admin/page_form.php, plugins/FlatHome/langs/{en,fr,de,pt,zh}.json.

  • FlatHome — Discussion link on discussion-type pages — Pages of type discussion now display a "View discussion" button in the page footer, linking directly to the corresponding discussion thread.
    Files changed: plugins/FlatHome/FlatHomePageController.php, plugins/FlatHome/views/page.php, plugins/FlatHome/langs/{en,fr,de,pt,zh}.json.

  • FlatHome — Last-updated date on editor-type pages — Pages of type editor now show the updated_at date in the page footer as a last-updated indicator.
    Files changed: plugins/FlatHome/views/page.php, plugins/FlatHome/langs/{en,fr,de,pt,zh}.json.

  • categories.list.filter hook — New hook fired in DiscussionController::index() and DiscussionController::forums() after categories are sorted, allowing plugins to remove or reorder categories before rendering. Used by FlatHome to hide the designated blog category from the forum list.
    Files changed: app/Controllers/Discussion/DiscussionController.php.


Changed

  • FlatHome — Homepage page excluded from nav menu — A CMS page flagged as homepage (is_homepage: true) is no longer listed as a separate nav item when homepage_type is set to page, since it is already accessible via /.
    Files changed: plugins/FlatHome/FlatHomeService.php.

  • FlatHome — Remove page ordering from admin list — The drag-to-reorder handle has been removed from the CMS pages table; ordering is managed exclusively via the "Navigation order" panel.
    Files changed: plugins/FlatHome/views/admin/pages.php.

  • FlatHome — Toolbar preset visible for Quill editor only — The toolbar preset radio buttons in the page form are now shown only when the Quill (WYSIWYG) editor is active; EasyMDE preserves the saved preset via a hidden field.
    Files changed: plugins/FlatHome/views/admin/page_form.php.

  • FlatHome — Icon shown in CMS pages list — Each page row in the admin pages table now displays its configured menu icon before the title.
    Files changed: plugins/FlatHome/views/admin/pages.php.

  • FlatHome — Help text for page groups drag-and-drop — An info line in the "Navigation order" card explains that items can be dragged into a page group to create a dropdown menu.
    Files changed: plugins/FlatHome/views/admin/pages.php, plugins/FlatHome/langs/{en,fr,de,pt,zh}.json.

  • FlatHome — Single homepage enforcement — Setting is_homepage: true on a CMS page now automatically clears the flag on all other pages, preventing multiple simultaneous homepages.
    Files changed: plugins/FlatHome/FlatHomeService.php.

  • FlatHome — Wider content area for CMS pages — The page view uses col-xl-10 col-lg-11 instead of col-lg-9, giving more reading width on larger screens.
    Files changed: plugins/FlatHome/views/page.php.

Fixed

  • Full backup — bootstrap.php missing from archive — The complete archive (flatboard-complete-*.zip) omitted bootstrap.php from the root files list, making the restored site completely blank since public/index.php requires it as its first instruction. The file is now included.
    Files changed: app/Controllers/Admin/BackupController.php.

  • CLI backup — vendor/ missing and stockage/backups not excluded — The backup:create CLI command did not include the vendor/ directory, requiring a manual composer install after any CLI-based restore. Additionally, stockage/backups was not excluded, causing backups to embed themselves recursively. Both issues are corrected.
    Files changed: app/Cli/Commands/BackupCommand.php.


Changed

  • Full backup — exclude stockage/cache, stockage/logs, and stockage/backups — The complete archive (flatboard-complete-*.zip) no longer includes these three directories. The cache contains server-specific data (absolute paths, compiled assets) that is invalid on a different server; logs are irrelevant noise from the source environment; and backups must not embed themselves recursively. All three are now silently skipped during archive creation.
    Files changed: app/Controllers/Admin/BackupController.php.

  • 2FA settings — OTP input boxes for enable and disable forms — The plain text inputs on the 2FA settings page are replaced by the same 6-box OTP UI used on the login page (grouped 3 + 3, keyboard navigation, paste support, shake/bounce animations, dark mode). A live TOTP progress bar and countdown badge are added to both forms. The enable form auto-submits 250 ms after the sixth digit is entered; the disable form requires an explicit button click and confirmation dialog since it is a destructive action.
    Files changed: app/Views/auth/2fa-settings.php.

  • StorageMigrator — smart quick switch with count-based sync detection — When switching storage engines (JSON ↔ SQLite), the plugin now checks whether the target storage already exists and contains data before launching a full migration. If the target file is present, entity counts (users, discussions, posts) are compared between source and target: an exact match triggers a near-instant switch (storage_type config update only); any discrepancy — or a comparison failure — falls back to the full migration, guaranteeing data integrity. This covers the common round-trip scenario (JSON→SQLite→JSON→SQLite) without redundant re-migration.
    Files changed: plugins/StorageMigrator/StorageMigratorController.php.

  • RSS/Atom feed links on forum category pages — Small RSS and Atom icon links are now shown on each category card in the forum list (/forums) and next to the category title in the forum view (/f/{slug}). Routes /feed/rss/category/{id} and /feed/atom/category/{id} are registered; they delegate to the existing RssController::rssCategory() and atomCategory() methods.
    Files changed: app/Core/App.php, themes/premium/views/categories/index.php, app/Views/categories/index.php, themes/premium/views/discussions/index.php, app/Views/discussions/index.php.

  • FlatSEO — forum-adjusted scoring algorithm — The analyzeSeo() scoring was calibrated for blog articles (300-word threshold, 1–3% density) which was inappropriate for forum topics. Content thresholds are now forum-aware: ≥150 words → good (+20), ≥50 → ok (+15), ≥20 → acceptable (+10). Title length range extended to 30–70 chars. Keyword density check now uses a relaxed range (0.5–4%) for long posts; for short posts (< 100 words) a simple presence check replaces density, downgrading failures from "bad" to "ok" so they no longer penalise naturally short replies. Average audit score improves from 53 to 61/100; Poor count drops to 0.
    Files changed: plugins/FlatSEO/FlatSEOService.php.


🚀 Changelog — Flatboard 5.2.7

Release date: March 17, 2026


Fixed

  • *Backup download — `auto_backup_beforeupdateblocked by filename regex** —BackupController::download()validated the filename against a regex that only acceptedbackup_andflatboard-complete-prefixes. Backups created automatically byUpdateControllerbefore applying a local update (auto_backup_before_update_YYYY-MM-DD_HH-MM-SS.zip) were rejected with{"error":"Invalid filename"}. The pattern now also accepts theauto_backup_beforeupdateprefix. *Files changed:*app/Controllers/Admin/BackupController.php`.

Added

  • 2FA — Flatboard logo in QR code (fully self-hosted) — The QR code on the 2FA setup page is now generated entirely client-side with no external service. A bundled qrcode.min.js (qrcodejs, MIT, 20 KB) generates the matrix at error-correction level H (30 %, required to keep the code scannable under a logo), and the favicon.svg Flatboard logo is superimposed at the centre via the HTML Canvas API with a white rounded background. The SVG proportions (270 × 401) are preserved. A graceful fallback displays the plain QR code if the SVG fails to load.
    Files changed: app/Views/auth/2fa-settings.php, themes/assets/js/qrcode.min.js (added).

🚀 Changelog — Flatboard 5.2.6

Release date: March 16, 2026


Changed

  • Permissions — Full UX overhaul — The Admin → Permissions page has been completely redesigned for ergonomics and readability.

    • Matrix view : new display mode accessible via the Badges / Matrix switcher (state persisted in localStorage). In Matrix view each permission maps to a row and each group to a column; cells are clickable to toggle a permission directly without leaving the view. Badge view remains click-to-toggle as before.
    • Group filter bar : a contextual filter strip lists all forum groups. Clicking a group dims the other groups' badges in Badge view (opacity + pointer-events: none) and hides the other groups' columns in Matrix view — allowing a full audit of one group's permissions in a single pass.
    • Streamlined two-column table : the separate Description column is removed; the description now appears below the permission name in the same cell (.perm-name + .perm-desc). Sections are no longer wrapped in nested cards but in flat .perm-flat-section separators with an uppercase label, matching the reference screenshot style.
    • Active tabs visible in light mode : the Administration & Moderation, Discussions & Posts, etc. tabs now display a primary-colour underline and a white background when active — fix applied globally to all tabbed tables in the backend via backend.css (.card-header .nav-tabs .nav-link.active).
    • Zero gap between tabs and content : the card-header containing nav-tabs loses its border-bottom (:has(.nav-tabs)); the active tab shifts down one pixel (transform: translateY(1px)) to visually fuse with the filter bar or the content below.
    • File settings redesigned : the File settings tab now presents the three categories (Attachments, Images, Avatars) side by side in a three-column CSS grid. Allowed file types are represented by toggleable chips (colour-coded pills) instead of checkboxes. The Max size field is embedded at the bottom of each column in a clean input without native spinners.
    • Double-toggle fix : badge toggles and file-settings chips now listen to the change event (fired after the browser's native toggle) instead of click with manual checked manipulation — eliminating the bug that cancelled every click.
    • Dashboard counter animation : the four quick-stat cards (Users, Discussions, Posts, Pending reports) on /admin now animate their values on load via the shared stats.js (cubic ease-out, 600 ms) — the same asset already used by /admin/analytics.
    • Translations : keys permissions.filter.* (label, all_groups, hint) added to all five admin.json files (fr, en, pt, de, zh).

    Files changed: app/Views/admin/permissions.php, app/Views/admin/components/PermissionSection.php, themes/assets/js/admin/modules/permissions-management.js, themes/assets/css/admin/modules/permissions-management.css, themes/default/assets/css/backend.dev.css, themes/default/assets/css/backend.css, app/Views/admin/dashboard.php, languages/*/admin.json.


🚀 Changelog — Flatboard 5.2.5

Release date: March 14, 2026


Added

  • Hook router.not_found — New hook fired by Router::handleNotFound() for unmatched HTML routes (not API). Data: ['url' => string, 'redirect' => ?string] passed by ref — plugins set redirect to issue a 301 before the default 404 response. Documented in docs/8-plugins.md.
  • Maintenance: Permissions Diagnostic tool — New tool in Admin → Dashboard → Maintenance (debug mode). Two actions: Scan (GET ?dry_run=1) reports group type conflicts without modifying anything; Fix conflicts (POST) corrects the type field of every group whose stored type does not match its canonical name, purges group/permission caches, and resynchronises plugin permissions. Route GET|POST /admin/maintenance/permissions-diagnostic (POST is CSRF-protected). Translation key maintenance.permissions_diagnostic added to all 5 admin.json files. JS bindings added to dashboard-management.js.

Changed

  • nginx.conf: PHP 8.3 — FastCGI socket updated from php8.0-fpm.sock to php8.3-fpm.sock in both the HTTP and HTTPS server blocks.

Fixed

  • Plugin permissions lifecyclePermissionHelper::getPluginPermissionPrefixes() now includes plugin.{id}. as the first candidate prefix (the format used by all modern plugin permissions). Previously the fallback only tried {id}., which never matched the actual key format, so disabling or uninstalling a plugin whose plugin.json lacked a _permissions field would silently leave its permissions in permissions.json / SQLite. All packaged plugins now have _permissions pre-populated in their plugin.json. Six plugins had a double-prefix bug where registerPermissions() returned keys like {id}.{action} instead of {action}, causing addPluginPermissionPrefix() to produce plugin.{id}.{id}.{action} — corrected for polls, Flatbot, TranslationManager, FlatModerationExtend, Impersonate, and FlatLetter; permissions.json updated accordingly. Orphan permissions from two uninstalled plugins (flatboat, aichatbox) removed from permissions.json. On first permissions.init cycle after update, initDefaults() now detects stale double-prefix keys for each active plugin and renames them in-place (e.g. plugin.polls.polls.viewplugin.polls.polls_view), keeping group assignments intact — no manual reconfiguration required on existing forums. The legacy key mapping in initDefaults() is scheduled for removal in 5.3 once all existing forums have migrated.
  • App: duplicate route /d/{number_slug}/page/{page} — The pagination route was registered twice in loadRoutes(). The second (dead) registration has been removed.
  • App: redundant path-traversal checks in avatar/attachment routes — The realpath/file_exists/strpos security check in the /uploads/avatars/ and /uploads/attachments/ route handlers was performed twice. The second check (and its unreachable else branch) have been removed; a single check now guards file serving.
  • Permission::set(): DEFAULT_GROUP_IDS cache not invalidated on saveclearCategoryCache() (called by Permission::set() after every permission save) now also deletes the DEFAULT_GROUP_IDS cache key. Previously, saving permissions from the admin panel did not invalidate this cache, so GroupHelper::getGuestGroupId() could return a stale or incorrect group ID on the same request — causing canGuest() to check the wrong group's permissions and potentially allowing guests to bypass permission restrictions.
  • GroupHelper::getGuestGroupId(): unreliable heuristic replaced — The fallback that identified the guest group by counting its permissions (≤ 2=guest) could misidentify non-system groups (e.g. "Traducteurs" created by TranslationManager with few initial permissions) as the guest group. A name-based detection step (multilingual: invité, invite, guest, gast, invitado, convidado, 访客) is now inserted between the type field lookup and the permission-count heuristic. Groups created by plugins without a canonical guest name are no longer at risk of being assigned type=guest by the fallback.

🚀 Changelog — Flatboard 5.2.4

Release date: March 12, 2026


Security

  • stockage/ directory hardening — A single Deny from all .htaccess is now placed at the root of stockage/, covering all subdirectories via Apache inheritance — independently of the root mod_rewrite rules. App::ensureStorageHtaccess() is called once per 24 h (tied to the bootstrap cache) and automatically recreates the directory and its .htaccess if either is missing. Both events are recorded in the application log.

Added

  • NumberHelper::compact() — compact number display — New helper that formats large integers into readable compact notation (e.g. 1 4131.4K, 25 00025K, 1 800 0001.8M). Trailing decimal zeros are stripped automatically (25.0K25K). A new Compact number display toggle in Admin → Settings → General controls the feature globally (off by default); when disabled, raw formatted numbers are shown as before. Applied to view counts across all themes and relevant plugins: default theme discussion list and search, premium theme discussion list and search, ClassicForum theme discussion list, EasyPages frontend banner. Translation keys added for all 5 languages.
  • Dashboard: masonry widget layout — The Quick Actions, Recent Activity, and plugin-contributed widgets (e.g. Forum Monitoring) are now laid out using CSS multi-column (column-count: 2 on desktop, 1 on mobile). Each card fills the available vertical space top-to-bottom before starting a new column, eliminating the large blank gaps that appeared on the right side when a tall widget occupied one column and a shorter widget sat alone in the other. break-inside: avoid ensures no card is split across a column boundary.

Fixed

  • Router: compiled-routes cache returns empty routesloadCompiledRoutes() stored the full ['routes', 'routesByMethod', 'namedRoutes'] object in $this->compiledRoutes, but dispatch() indexed it directly by HTTP method (e.g. $compiledRoutes['POST']), always resolving to null — causing all requests to return 404 whenever the routes file cache was active. The loader now extracts $data['routesByMethod'] into $compiledRoutes.
  • SQLite: permissions UNIQUE constraint crash — The permissions.name column incorrectly carried a UNIQUE NOT NULL constraint, causing a fatal SQLSTATE[23000]: Integrity constraint violation on startup whenever two or more plugins registered permissions with the same display name. The constraint has been removed (only id needs to be unique); a schema migration recreates the table without it for existing databases. SCHEMA_VERSION bumped to 14.
  • Users page: visitors/bots pagination & tab order — The load-more button for the Anonymous visitors and Bots panels was silently targeting #regular-users-container (the members panel container) due to $paginationContainerId not being reset between panel pagination includes. Added id="visitors-container" and id="bots-container" to the respective grid rows, and now explicitly set $paginationContainerId before each panel's pagination include so the load-more button correctly appends the next page of visitors/bots into their own panel. Tab order changed to Members → Anonymous visitors → Active bots.
  • Members list: duplicate usersJsonStorage::createUser() and createUserWithId() now enforce username and email uniqueness before writing, throwing a RuntimeException(409) on conflict. Previously, neither method checked for existing entries, allowing concurrent registrations or storage migrations to silently create multiple user files with the same credentials. RegisterController already caught the 409 code for race conditions and now also covers this case. The missing /users/search route has been registered, resolving a double error toast (one from clonedResponse.json() failure, one from the fetch network catch) shown when filtering members by online/offline status.

🚀 Changelog — Flatboard 5.2.3

Release date: March 12, 2026


Added

  • view.footer.bottom hook — New hook triggered inside <footer> just before its closing tag, allowing plugins to append content at the bottom of the footer.

🚀 Changelog — Flatboard 5.2.2

Release date: March 10, 2026


Fixed

  • view.login.validation hook fallbackLoginController now falls back to the error string itself before the hardcoded 'Captcha invalide' message (... ?: $captchaError ?: 'Captcha invalide'). Previously, any plugin setting a custom error message via this hook would have it silently replaced by 'Captcha invalide'.

Changed

  • SortableJS updated to 1.15.7themes/assets/js/Sortable.min.js replaced.
  • PHPMailer updated to 7.0.2 — vendor directory restructured from vendor/PHPMailer/PHPMailer/ to vendor/PHPMailer/. EmailService::loadPHPMailer() now resolves vendor/PHPMailer/src/ as the primary lookup path (priority 2), with the previous nested structure and all legacy fallback paths retained for backwards compatibility. Language path updated accordingly (vendor/PHPMailer/language/).

Added

  • Plugin installation from archive(admin/plugins): New "Install a plugin" button opens a modal where the admin can upload a ZIP archive. The server validates the MIME type, extension, size (respects PHP upload_max_filesize / post_max_size), ZIP integrity, path-traversal safety, and the presence of a plugin.json inside a top-level directory. The archive is extracted directly into plugins/ from the PHP temp file (no permanent copy kept) and all caches are invalidated. The new route POST /admin/plugins/upload is CSRF-protected. Translation keys added for all 5 languages.
  • Theme installation from archive(admin/themes): Same mechanism for themes — new "Install a theme" button and modal, route POST /admin/themes/upload, validates theme.json inside a top-level directory (reserved names assets, cache, premium are rejected), invalidates theme asset cache after extraction. Translation keys added for all 5 languages.

Fixed

  • Network error toasts on page navigation: The global fetch interceptor now ignores request cancellations caused by page navigation (beforeunload) and explicit AbortController aborts (AbortError), preventing spurious "network error" toasts from firing when the user changes pages. The raw browser error message (untranslated, browser-locale-dependent) is no longer appended to the toast text — only the translated network_error key is shown.
  • Local update: plugin.json / theme.json preservation: The deployUpdateFiles() step previously overwrote plugin.json and theme.json with a plain copy(), erasing user data such as "active" state, plugin settings stored under the "plugin" key, and customised theme colours in "variables". These files are now merged instead of replaced: existing keys are preserved, new keys introduced by the archive are added, and nested associative arrays (e.g. form_config.fields) are deep-merged recursively. Indexed arrays (e.g. hook lists) are union-merged without duplicates. A direct copy fallback is used if either JSON file is unreadable or invalid.

🚀 Changelog — Flatboard 5.2.1

Release date: March 10, 2026


Added

  • Local update system: New step-based update workflow for offline/self-hosted deployments. The Archive Builder plugin now injects a manifest.json (type, software, edition, version, build_date, checksum, compatible_from) into every Community and Pro archive it generates. The Backup upload endpoint detects archives where manifest.type == "update" and manifest.software == "flatboard", validates the manifest (edition, semver version, checksum), and routes them to storage/updates/ instead of storage/backups/. The admin/updates page scans storage/updates/, displays a card for each valid archive that is newer than the installed version, and lets the admin apply it via a 5-step progress UI (verify → backup → extract → deploy → cleanup). Each step is executed over a separate CSRF-protected AJAX call with retry logic; a full automatic backup is created before any file is deployed; protected paths (stockage/, uploads/, .env, .htaccess, install.php) are never overwritten. Archives older than the current version are listed separately and can be deleted. Translation keys added for all 5 languages.
  • Plugin uninstall(Pro & Community): New "Uninstall" button (danger/trash) in the admin plugin list. Clicking it opens a Bootstrap confirmation modal (ConfirmModal), then calls POST /admin/plugins/uninstall. The server runs the plugin's uninstall() / deactivate() hook, removes its permissions, clears asset and translation caches, removes its entry from plugins.enabled, then deletes the plugin directory recursively. The button is hidden for core plugins (cantDisable = "1") and blocked server-side if the plugin is still active. Translation keys added for all 5 languages.

Fixed

  • User list tab counts: The "Active bots" and "Anonymous visitors" tab badges on /users now display the real totals instead of the item count of the current page. Previously, both badges were capped at the pagination limit (e.g. 20) regardless of the actual number of active bots or visitors.
  • admin/updates archive display: Archives whose version equals the installed version were not rendered at all — the <details> block for non-newer archives was only shown when newer archives were also present. Same-version archives now appear as standalone cards with a "Reinstall" button; truly older archives are listed directly when no newer/same-version card is shown.
  • admin/updates card colours: Update and reinstall cards now use Bootstrap 5.3 semantic CSS variables (--bs-success-bg-subtle, --bs-warning-bg-subtle, etc.) instead of hard-coded utility classes, so backgrounds adapt correctly to both light and dark themes.
  • admin/backups upload hint: The "Upload backup" button area now includes a one-line hint explaining that Flatboard update archives can also be uploaded there, with a direct link to admin/updates. The same hint is shown inside the upload modal. Translation keys added for all 5 languages.
  • Local update same-version reinstall: The admin/updates page now shows a "Reinstall" button (warning style) for archives whose version matches the currently installed version, in addition to the "Update" button (green) for newer archives. Previously, same-version archives were not displayed and could not be applied. The verify step now accepts archives with version ≥ current (previously required strictly greater). Archives strictly older than the current version remain listed for deletion only. Translation keys added for all 5 languages.
  • Backup upload size error: The "Fichier trop volumineux" error on backup upload now includes the effective PHP limit (upload_max_filesize / post_max_size) in the message. The upload modal also displays the limit in the file input hint. A client-side pre-check validates the file size against the PHP limit before submitting, preventing a failed upload round-trip. Translation keys added for all 5 languages.
  • Update check false positive: admin/updates no longer shows "You have the latest version" when update_check_url is not configured — it now displays an explicit "not configured" notice. hasUpdateAvailable() now caches the false result for 1 hour when the URL is absent (previously uncached, causing repeated unnecessary calls) and for 60 seconds when the remote request fails (previously uncached, causing a cURL request on every page load).
  • Discussion creation: Fixed duplicate AJAX submission caused by two concurrent submit handlers (DiscussionFormManager + inline FormValidator) both firing on the same form. The second request hit the flood-protection cooldown and incorrectly showed a "wait 30 seconds" error. Also fixed the browser "leave or stay" popup that appeared during redirect because isDirty was still true. The inline submit handler has been removed from the create view; DiscussionFormManager is now the sole handler and now also validates the category field and synchronises the Markdown editor value before submission.
  • EasyMDE(Community & Pro): Fixed editor being inaccessible on iOS/macOS Safari — tapping the editing area did not open the virtual keyboard. Added an explicit touchstart → focus() listener on the CodeMirror wrapper and set -webkit-user-select: text, user-select: text, and touch-action: manipulation on the editor area to prevent focus being blocked by inherited user-select: none from the toolbar and the 300 ms tap delay.
  • TUIEditor(Pro): Same iOS/macOS Safari fix applied to both the CodeMirror container (Markdown mode) and the ProseMirror container (WYSIWYG mode).
  • Installer username validation: install.php now accepts usernames starting with a digit (e.g. 314r). The server-side validation regex has been aligned with the native Flatboard pattern (/^[a-zA-Z0-9_-]+$/), replacing two redundant and overly strict checks that incorrectly required the username to begin with a letter.
  • Theme logo deletion: Clicking "Delete logo" in the theme configuration showed the confirmation modal but did nothing on confirm. The data-confirm attribute on the button caused the global capture-phase handler in confirm-modal.js to intercept and stop propagation before the module's own click handler could run; on confirm it attempted to call window.deleteLogo which is undefined. Fixed by removing the redundant data-confirm / data-confirm-title attributes — the JS module already handles confirmation internally via UIHelpers.confirm().

Share this article:

Fred

👨‍💻 Flatboard Founder 🔧 Flatboard Core Developer.
Full-Stack Web Developer
Expert in Portable and Interoperable Solutions (PHP/JSON)

Member since December 2025

0 comment

No comments yet. Be the first!

Log in to leave a comment.