Edited on Jan 02, 2026 By Fred .
Flatboard v5.0.0-rc.2
Release Date: December 23, 2025
🐛 Bug Fixes
Discussion Reply Counting
- Fixed incorrect reply counter initialization - New discussions now correctly start with 0 replies instead of 1
- Changed SQL logic in
createPost()fromreplies_count + CASE WHEN :is_first_post = 1 THEN 0 ELSE 1 ENDtoCASE WHEN :is_first_post = 1 THEN 0 ELSE replies_count + 1 END - Removed duplicate
$stmtUpdate->execute()call that was causing incorrect counts
- Changed SQL logic in
SQLite Storage Issues
- Fixed undefined property error - Corrected
$pdoto$this->dbinSqliteStorage::togglePostReaction() - Fixed notification delivery - System notifications now properly received when subscribing to discussions by adding explicit
\PDO::FETCH_ASSOCto:getUserSubscriptions()getSubscriptionsByDiscussion()getUserNotifications()
Display and Sorting
- Fixed pinned discussions ordering - Pinned posts now correctly appear first in category views by handling SQLite integer values (0/1) instead of strict boolean
true
Frontend and Resources
- Fixed image upload in ResourceManager - Added
X-Requested-With: XMLHttpRequestheader to fetch requests - Fixed missing method - Added
isRemotePath()method toFileStorageServicefor detecting remote URLs (http, https, ftp) - Fixed URL duplication -
UrlHelper::full()now detects existing subfolders insite_urland prevents duplication- Example:
http://localhost/Flatboard+/Flatboardnow correctly produceshttp://localhost/Flatboard/discussioninstead ofhttp://localhost/Flatboard/Flatboard/discussion - Works by extracting and comparing the path from
site_urlwithbaseUrlbefore construction
- Example:
Plugin System
- Fixed Cookie Consent Plugin translation - Updated
PluginControllerto prioritizeplugin_description_defaultkey from language files
🔄 Changes
Discussion Sorting Behavior
- Modified index page sorting - Latest messages now displayed without prioritizing pinned discussions (pinned priority only applies to category views)
- Added
$ignorePinnedparameter - New parameter ingetAllDiscussionsSorted()method allows bypassing pinned sorting when needed
Notification System
- Enhanced notification data -
NotificationService::notify()now accepts and storesdiscussion_idandpost_idfor improved diagnostics and data completeness
✨ New Features
Maintenance Dashboard
- Discussion reply rebuilder - Added "Rebuild Discussion Replies" button in Maintenance and Optimization section
- Recalculates reply counts for all discussions
- Supports both SQLite and JsonStorage
- New route:
/admin/maintenance/rebuild-discussion-replies - Includes progress tracking
Storage Layer
- Public recalculation method - Added
recalculateDiscussionReplies()to SQLite Storage for maintenance operations
Documentation
- Cookie Consent Plugin documentation - Added comprehensive
README.mdwith features, improvements, fixes, and usage instructions - Plugin metadata - Added
descriptionfield toplugin.jsonwith detailed French description
🚀 Performance Improvements
Tag Loading Optimization
Refactored tag loading across multiple controllers to eliminate N+1 query problem:
Affected Controllers:
CategoryControllerTagControllerApi\DiscussionApiControllerApi\TagApiController
New Batch Loading Pattern:
- Collect all discussion IDs
- Batch load all tag associations in a single query
- Collect all unique tag IDs
- Load full tag data in one query
- Enrich discussion objects with pre-loaded data
Performance Impact: Reduced from N+1 queries (1 + N queries per discussion) to just 3 total queries
Error Handling
- Enhanced ResourceManager logging - Improved error handling in
uploadImage()with detailed messages for allUPLOAD_ERR_*constants - Better translation fallbacks - Plugin description translation loading now prioritizes
plugin_description_defaultwith graceful fallback todescription
🔒 Username Validation
Reference: /d/6-username-with-unsupported-characters
Implementation Details
Server-Side Validation:
- Added regex validation in
RegisterController,ProfileController, andUserManagementController - Only allows letters, numbers, hyphens (-), and underscores (_)
Client-Side Validation:
- HTML5 pattern attribute:
pattern="[a-zA-Z0-9_-]+" - Applied to registration forms, user settings, and admin edit forms
- JavaScript validation updated with correct hyphen handling:
/^[a-zA-Z0-9_-]+$/
Translation:
- Added
username_invalid_formatmessage in French and English error files
Allowed Characters
✅ Permitted:
- Letters:
a-z,A-Z - Numbers:
0-9 - Hyphen:
- - Underscore:
_
❌ Prohibited:
- Spaces
- Diacritical marks (é, à, ñ, č, etc.)
- Special characters (@, #, !, etc.)
Examples
| Username | Valid | Reason |
|---|---|---|
John_Doe |
✅ | Alphanumeric with underscore |
user123 |
✅ | Alphanumeric only |
my-username |
✅ | Alphanumeric with hyphen |
jean-marie |
✅ | Letters with hyphen |
Patrik Čmejla |
❌ | Contains space and diacritic |
user@example |
❌ | Contains @ symbol |
📝 Technical Notes
SQLite Storage Changes
- Property access standardization:
$this->pdo→$this->db - Explicit
\PDO::FETCH_ASSOCadded for consistency - Removed duplicate method execution in post creation
Notification System Updates
- Enhanced method signature with optional
discussionIdandpostIdparameters - Updated
notifyNewReply()to pass contextual IDs - Improved data completeness for debugging and analytics
Maintenance System
- New maintenance routes and endpoints
- Progress tracking for long-running operations
- Bilingual UI support (French and English)
Edited on Dec 23, 2025 By Fred .
[5.0.0-rc.3] - 2025-12-24
Fixed
Authentication
- 2FA Authentication: Fixed 404 error when submitting 2FA verification code. The form action was pointing to
/login/2fa/verifyinstead of the correct route/login/2fa. Updated all 2FA views across all themes (default, premium).
Subdirectory Installation
- URL Generation: Fixed incorrect URL generation when Flatboard is installed in a subdirectory. All fetch requests now properly use
window.url()helper function to include the base path. - Post Creation: Fixed 404 errors when creating posts in discussions. POST requests to
/d/{number_slug}/postsnow correctly include the subdirectory path. - Discussion Actions: Fixed all discussion-related actions (subscribe, pin, lock, solve, best answer, delete) to work correctly in subdirectory installations.
- Image Uploads: Fixed image upload URLs to include subdirectory path. Upload endpoints now return URLs with the correct base path using
UrlHelper::to(). - API Requests: Fixed all API fetch requests to properly include subdirectory paths.
Added
Image Upload Support
- WebP Format Support: Added WebP image format support for easyMDE editor uploads. The plugin now reads allowed file types from configuration (
uploads.images) and dynamically sets theimageAcceptoption in easyMDE to allow WebP uploads when permitted by file permissions. - WebP MIME Type Normalization: Added normalization for WebP MIME types (
image/x-webp→image/webp) to handle variations across different systems. - Enhanced Upload Validation: Improved upload service validation with fallback checks - if MIME type doesn't match exactly but the extension is valid and corresponds to an allowed image type, the file is accepted.
- Improved Upload Error Logging: Enhanced error logging in
UploadController::uploadImage()to provide detailed error messages including MIME type, file size, and allowed types for better debugging.
SEO & Feeds
- RSS/ATOM Feed Links: Added RSS and ATOM feed links to the frontend footer across all themes with Font Awesome icons:
- RSS feed link with
fas fa-rssicon - ATOM feed link with
fas fa-rss-squareicon - Proper
rel="alternate"andtypeattributes for SEO - Links positioned between "Users" and "Settings" in the footer
- RSS feed link with
Changed
URL Helper
- url-helper.js Enhancement: Updated
themes/assets/js/url-helper.jsto usewindow.BASE_URLwhen available, ensuring proper subdirectory support even if the helper is loaded before translations.php.
Code Quality
- Consistent URL Usage: Replaced all hardcoded URLs (
/d/...,/api/...) withwindow.url()calls in JavaScript code across all discussion views and themes. - Upload URL Generation: Changed all upload controllers to use
UrlHelper::to()instead of hardcoded paths for consistent subdirectory support.
Technical Details
Files Modified
app/Views/auth/2fa.phpand all theme variantsplugins/easymde/EasyMDEPlugin.phpapp/Services/UploadService.phpapp/Controllers/UploadController.phpapp/Views/discussions/show.phpand all theme variantsthemes/assets/js/url-helper.jsapp/Views/layouts/frontend/footer.phpand all theme variants
Routes
- No new routes added (existing routes:
/feed/rss,/feed/atom,/login/2fa)
Flatboard 5.0.0-rc.4 Changelog
Release Date: December 25, 2025
Codename: MERRY CHRISTMAS
🎯 Overview
This release candidate focuses on major improvements to the translation system, URL handling for subfolder installations, two-factor authentication fixes, and various bug fixes and enhancements.
✨ Major Features & Improvements
🌐 Translation System Refactoring
-
Complete hierarchical translation structure
- Reorganized all translation files into a logical hierarchical structure
- Implemented consistent naming conventions (
module.section.element) - Grouped translations by feature/component for better maintainability
- Eliminated duplicate translations and centralized reusable texts
-
Translation key migration
- Updated all PHP files (247 files) to use the new hierarchical translation keys
- Automated migration scripts for key replacement across the codebase
- Fixed translation domain usage (removed redundant prefixes when domain is specified)
-
JavaScript translation fixes
- Fixed all JavaScript translation strings to use
json_encode()for proper escaping - Resolved syntax errors caused by unescaped apostrophes in translations
- Improved error handling in client-side translation calls
- Fixed all JavaScript translation strings to use
-
Complete translation coverage
- Added missing translations for admin panel, settings, permissions, and user management
- Fixed untranslated keys in audit logs, maintenance actions, and theme settings
- Completed English and German translations, added structure for other languages
🔗 URL & Base Path Improvements
-
Subfolder installation support
- Fixed all URL generation to respect base path when installed in subfolders
- Automatic base path detection (no manual configuration required)
- Support for root installation, simple subfolders (
/forum), and nested subfolders (/apps/forum)
-
Universal URL helpers
- Enhanced
UrlHelper::to(),UrlHelper::asset(),UrlHelper::theme(),UrlHelper::plugin() - New
UrlHelper::full()method for absolute URLs using configuredsite_url - Fixed
/index.php/appearing in URLs
- Enhanced
-
Asset loading fixes
- All CSS, JavaScript, and theme assets now correctly include base path
- Fixed plugin asset loading to respect subfolder installations
- Improved SEO meta tags (canonical, Open Graph, sitemap, RSS/Atom) with correct base paths
-
AJAX request handling
- Created universal
window.apiRequest()helper for API calls - Automatic base path resolution for all AJAX requests
- Silent error handling for expected 404/401 responses (notifications, presence)
- Improved JSON parsing with Content-Type validation
- Created universal
🔐 Two-Factor Authentication (2FA)
-
Activation fixes
- Fixed 2FA activation not working despite valid code
- Improved code validation and retrieval from FormData
- Added verification after activation to ensure proper database update
- Fixed cache invalidation issues
-
UI improvements
- Fixed JavaScript errors in 2FA settings page
- Changed form submission from GET to POST (security improvement)
- Hide activation encouragement message when 2FA is already enabled
- Improved error messages and user feedback
📁 File Serving
- Avatar serving route
- Added dedicated route for serving avatar images (
/uploads/avatars/) - Proper MIME type detection (jpg, jpeg, png, gif, webp, svg)
- Cache headers optimization (ETag, Last-Modified, Cache-Control)
- Security checks to prevent path traversal attacks
- Support for 304 Not Modified responses
- Added dedicated route for serving avatar images (
🐛 Bug Fixes
Translation System
- Fixed
errors.permission.deniednot being translated (removed redundanterrors.prefix) - Fixed
admin.panel.titleand other admin panel translations - Fixed missing translations in sidebar, permissions page, and file settings
- Fixed audit log translations for theme actions and user updates
- Fixed icon picker modal translations
- Fixed username pattern validation (escaped hyphen in regex)
URL & Routing
- Fixed search form not using
site_urlfor redirects - Fixed AJAX search results not detecting subfolder
- Fixed user profile discussion links not respecting base path
- Fixed footer links duplication (removed redundant "Settings" and "Users" links)
Theme Settings
- Fixed theme settings not saving (colors, checkboxes, selects)
- Fixed checkbox values not being retrieved correctly
- Fixed select default values not being preserved
- Improved form data handling in JavaScript
Installation
- Added automatic
site_urldetection during installation - Improved base path detection to include subfolders
- Added help text for site URL configuration
🔧 Technical Improvements
Code Quality
- Improved error handling in API requests
- Better logging for debugging 2FA activation
- Enhanced cache invalidation methods
- Added public
User::invalidateCache()method
Security
- Fixed XSS vulnerabilities in JavaScript translation strings
- Improved file path validation for uploaded files
- Enhanced CSRF protection in forms
Performance
- Optimized cache headers for static assets
- Improved ETag generation for better caching
- Reduced unnecessary API calls with silent error handling
📝 Documentation
- Updated translation key structure documentation
- Added migration guides for translation keys
- Improved inline code comments
🔄 Migration Notes
For Developers
-
Translation Keys: All translation keys have been migrated to hierarchical structure. Update any custom code using old flat keys.
-
URL Generation: Always use
UrlHelper::to(),UrlHelper::asset(), etc. instead of hardcoded paths. -
JavaScript Translations: Use
json_encode()when embedding translations in JavaScript to avoid syntax errors. -
2FA: The 2FA activation process has been improved. Test thoroughly if you have custom 2FA implementations.
For Administrators
-
Subfolder Installations: The base path is now automatically detected. No manual configuration needed.
-
Theme Settings: Theme settings (checkboxes, selects) should now save correctly. Verify your theme configurations.
-
Translations: All admin panel translations should now be complete. Report any missing translations.
📦 Files Changed
Core Files
app/Core/App.php- Added avatar serving route, improved routingapp/Helpers/UrlHelper.php- Enhanced base path detection and URL generationapp/Helpers/SeoHelper.php- Fixed SEO URLs to use base pathapp/Models/User.php- Added cache invalidation method
Controllers
app/Controllers/Auth/TwoFactorController.php- Fixed 2FA activationapp/Controllers/Admin/ThemeController.php- Fixed theme settings saving- All controllers - Fixed translation key usage
Views
app/Views/users/settings.php- Fixed 2FA message display, JavaScript translationsapp/Views/auth/2fa-settings.php- Fixed form submission, JavaScript errorsapp/Views/admin/*- Fixed all translation keys- All views - Fixed JavaScript translation escaping
Translation Files
languages/*/main.json- Complete hierarchical restructurelanguages/*/admin.json- Added missing translationslanguages/*/errors.json- Fixed key structurelanguages/*/auth.json- Added 2FA translations
JavaScript
themes/assets/js/api-helper.js- New universal API request helper- All JavaScript files - Fixed translation string escaping
🎉 Contributors
Thank you to all contributors who helped improve Flatboard 5!
📌 Known Issues
- None reported at this time
🔜 Next Steps
- Continue translation coverage for additional languages
- Further optimize asset loading and caching
- Enhance plugin system for better subfolder support
Changelog - RC5 - Complete Release Notes
Release Date: December 27, 2025
Version: 5.0.0-RC5
Compatibility: PHP 8.0+, SQLite/JSON Storage
Summary
This release includes comprehensive improvements to the Premium theme configuration system, API routing fixes, critical bug fixes for group management, significant backend UX improvements, theme optimization, and automatic theme mode improvements. All changes maintain backward compatibility and respect the core architecture constraints.
Part 1: API Routing Configuration
Summary
Fixed 404 errors for API endpoints (/api/presence/update and /api/notifications) by adding proper routing rules to redirect API requests to api.php instead of index.php.
Changes
1. .htaccess (Root directory)
Added: API routing rule before the general rewrite rule
- Location: Line 16-19
- Change: Added rule to redirect
/api/*requests topublic/api.php - Supports: Subdirectories and subdomains
# Redirect API requests to public/api.php (supports subdirectories)
RewriteCond %{THE_REQUEST} \s+/api/ [NC]
RewriteRule ^api/(.*)$ public/api.php?url=/api/$1 [QSA,L]2. public/.htaccess (Public directory)
Added: API routing rule before the general rewrite rule
- Location: Line 28-30
- Change: Added rule to redirect
/api/*requests toapi.php - Modified: Changed condition from
^/api/to/api/to support subdirectories (removed anchor^)
# Redirect API requests to api.php
# Supports subdirectories: /forum/api/... or /api/...
RewriteCond %{REQUEST_URI} /api/ [NC]
RewriteRule ^api/(.*)$ api.php?url=/api/$1 [QSA,L]3. nginx.conf
Added: API location block and FastCGI configuration
- Location 1: Line 67-70 (before static files section)
- Change: Added location block to route
/api/*requests topublic/api.php - Modified: Changed from
^/api/to/api/to support subdirectories (removed anchor^)
# API entry point (supports subdirectories)
location ~ /api/ {
try_files $uri /public/api.php?url=$uri&$args;
}- Location 2: Line 82-97 (before main PHP entry point)
- Change: Added FastCGI configuration block for
api.php
# API PHP entry point
location ~ ^/public/api\.php$ {
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
fastcgi_index api.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param QUERY_STRING $query_string;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
}- Location 3: Line 202-205 (HTTPS section, commented)
- Change: Added same API routing rules in the commented HTTPS configuration section for consistency
Technical Details
Why these changes were needed
- API endpoints were returning 404 errors because requests to
/api/*were being routed toindex.phpinstead ofapi.php - The
api.phpfile usesrunApi()method which callsdispatchApi()instead ofdispatch(), ensuring proper API response handling
Compatibility
These changes support:
- ✅ Root installation:
https://example.com/api/... - ✅ Subdirectory installation:
https://example.com/forum/api/... - ✅ Subdomain installation:
https://forum.example.com/api/... - ✅ Any combination of the above
Pattern Changes
- Before: Used anchored patterns
^/api/(only matches at start of path) - After: Use non-anchored patterns
/api/(matches anywhere in path) - Reason: Allows matching
/api/even when preceded by a subdirectory path like/forum/api/
Testing
After applying these changes, the following endpoints should work correctly:
GET /api/notificationsPOST /api/presence/update- All other
/api/*endpoints
Server Configuration Required
Apache
No additional configuration needed. Just ensure mod_rewrite is enabled.
Nginx
- Test configuration:
sudo nginx -t - Reload Nginx:
sudo systemctl reload nginx - Adjust
fastcgi_passpath if different (check your PHP-FPM socket location)
Part 2: Premium Theme Enhancements
Summary
Added comprehensive theme configuration system to the Premium theme, allowing administrators to customize layout, visual styles, and feature toggles without modifying core files. All changes are contained within the themes/premium directory.
Changes
1. Theme Configuration (themes/premium/theme.json)
Added: 15 new configurable parameters for theme customization
-
Layout Options:
layout_style: Choose between centered, wide, or compact layoutsnavbar_style: Glass, solid, or transparent navbar stylescard_style: Elevated, bordered, or minimal card designs
-
Visual Options:
animation_level: Control animation intensity (none, subtle, normal, high)border_radius: Adjust border radius multiplier (0.5x to 2x)font_size: Adjust font size multiplier (0.8x to 1.5x)spacing: Adjust spacing multiplier (0.8x to 1.5x)enable_gradients: Toggle gradient effectsenable_shadows: Toggle shadow effectsenable_parallax: Toggle parallax scrolling effects
-
Feature Toggles:
show_hero: Display hero section on homepage and forums pageshow_stats: Display statistics sectionshow_breadcrumbs: Display breadcrumb navigationshow_cta: Display call-to-action sectionsshow_avatars: Display user avatars in postsshow_signatures: Display user signatures in postsshow_share_buttons: Display social sharing buttonsenable_reactions: Enable reaction picker in postsshow_notifications_bell: Display notification bell in navbarenable_sticky_navbar: Make navbar sticky on scrollenable_back_to_top: Display back-to-top buttonenable_smooth_scroll: Enable smooth scrolling behaviorenable_lazy_loading: Enable lazy loading for imagesshow_user_presence: Display user presence indicatorsenable_tooltips: Enable Bootstrap and custom tooltipsenable_auto_refresh: Auto-refresh notifications
2. Theme Helper (app/Helpers/ThemeHelper.php)
Added: getThemeSetting($key, $default) method
- Purpose: Retrieve theme configuration values from
theme.json - Usage:
ThemeHelper::getThemeSetting('show_hero', true)
3. Header Template (themes/premium/views/layouts/frontend/header.php)
Modified: Applied theme settings to header and navigation
- Changes:
- Applied
show_notifications_bellto conditionally display notification bell - Applied
enable_sticky_navbarto addsticky-topclass to navbar - Applied
navbar_styleto add CSS classes (navbar-glass,navbar-solid,navbar-transparent) - Applied
enable_smooth_scrollto adddata-smooth-scrollattribute - Applied visual settings (
card_style,animation_level,border_radius,font_size,spacing,enable_gradients,enable_shadows,enable_parallax) by adding classes to<body>tag - Injected theme settings into JavaScript via
data-theme-settingsattribute - Fixed:
implode()error by initializing$bodyClassesbefore use - Fixed: Syntax error by removing extraneous
<?phptag - Fixed: Admin panel link visibility by improving
isAdmindetection logic
- Applied
4. Main Layout (themes/premium/views/layouts/frontend/main.php)
Modified: Applied layout style setting
- Change: Applied
layout_styleto add classes (theme-layout-centered,theme-layout-wide,theme-layout-compact) to<main>tag
5. Footer Template (themes/premium/views/layouts/frontend/footer.php)
Modified: Added back-to-top button and recent registrations section
- Changes:
- Implemented
enable_back_to_topto display scroll-to-top button - Integrated
enable_smooth_scrollfor smooth scrolling behavior - Added: "Recent Registrations" section displaying 5 latest registered users with avatars, usernames, and registration dates
- Fixed: Avatar display using
AvatarHelper::getUrl()for correct URL generation - Fixed: Translation key for "Recent Registrations" to display translated text
- Implemented
6. Banner Component (themes/premium/views/components/banner.php)
Modified: Applied breadcrumbs setting
- Change: Applied
show_breadcrumbsto conditionally display breadcrumbs
7. Categories Index (themes/premium/views/categories/index.php)
Modified: Applied hero, stats, and CTA settings
- Changes:
- Applied
show_hero,show_stats,show_ctato conditionally display sections - Moved "Statistics" section to appear after main content (category cards)
- Fixed:
getAllPosts()error by iterating through discussions to count posts
- Applied
8. Discussions Index (themes/premium/views/discussions/index.php)
Modified: Applied hero, stats, and CTA settings
- Changes:
- Applied
show_hero,show_stats,show_ctato conditionally display sections - Moved "Statistics" section to appear after main content (discussion list)
- Fixed:
getAllPosts()error by iterating through discussions to count posts
- Applied
9. Post Thread Component (themes/premium/views/components/post-thread.php)
Modified: Applied post display settings
- Changes:
- Applied
show_avatarsto conditionally display user avatars or placeholder with initials - Applied
show_signaturesto conditionally display user signatures - Applied
show_share_buttonsto conditionally display share buttons - Applied
enable_reactionsto conditionally display reaction picker
- Applied
10. Main JavaScript (themes/premium/assets/js/main.js)
Modified: Integrated theme settings for dynamic behavior
- Changes:
- Integrated
getThemeConfig()to read settings from injected JSON script - Applied
enable_tooltipsto initialize Bootstrap tooltips and custom Flatboard tooltips - Applied
enable_auto_refreshto controlsetIntervalfor notifications - Applied
enable_lazy_loadingto initialize lazy loading or removeloading="lazy"attributes - Applied
show_user_presenceto controlupdatePresence()calls - Applied
enable_smooth_scrollto setdocument.documentElement.style.scrollBehavior - Applied
enable_parallax,animation_level,border_radius,font_size,spacingby setting CSS custom properties or adding classes todocument.documentElement
- Integrated
11. Frontend CSS (themes/premium/assets/css/frontend.css)
Modified: Applied theme settings for dynamic styling
- Changes:
- Applied
card_stylewith specific CSS rules forelevated,bordered,minimalstyles - Applied
enable_shadowsandenable_gradientsto conditionally disable these effects - Applied
border_radius,font_size,spacing,animation_level,layout_style,enable_parallaxby setting CSS custom properties or adding classes
- Applied
12. Theme Colors Component (themes/premium/views/components/theme-colors.php)
Modified: Fixed function re-declaration error
- Change: Added
if (!function_exists('hexToRgb'))to prevent re-declaration of thehexToRgbfunction - Added: Dynamic CSS variables for
--theme-border-radius,--theme-font-size, and--theme-spacing-multiplierbased on theme settings
13. RSS Service (app/Services/RssService.php)
Modified: Added permission filtering for RSS feeds
- Change: Added filter to
getAllDiscussions()to only include discussions from categories visible to anonymous users usingCategory::canView($categoryId, null)
14. Sitemap Service (app/Services/SitemapService.php)
Verified: Confirmed correct permission filtering
- Status: Already correctly filters discussions, categories, and tags based on
Category::canView($categoryId, null)andTag::canView(null)
Architecture
- Configuration: Theme settings stored in
theme.json(JSON format) - Access: Settings retrieved via
ThemeHelper::getThemeSetting($key, $default) - Application: Settings applied through:
- PHP conditional rendering in views
- CSS classes and custom properties
- JavaScript configuration injection
- Dynamic CSS variable generation
Compatibility
- ✅ All changes contained within
themes/premiumdirectory - ✅ No core file modifications required
- ✅ Backward compatible with existing installations
- ✅ Works with both JSON and SQLite storage backends
Part 3: Group Management Improvements
Summary
Fixed critical issues with group management including duplicate group creation when renaming default groups, missing protection against deletion of default groups, and improved group identification logic to use IDs and permissions instead of names.
Changes
1. Group Model (app/Models/Group.php)
Modified: initDefaults() method to identify default groups by ID/permissions instead of name
- Problem: When renaming "Administrateur" to "Admin", the system would recreate "Administrateur" because it checked groups by name
- Solution: Modified to use
GroupHelper::getAdminGroupId(),getModeratorGroupId(),getMemberGroupId(), andgetGuestGroupId()to identify default groups by their permissions and IDs - Benefits:
- Prevents duplicate group creation when renaming default groups
- Allows administrators to rename default groups without side effects
- More robust identification system that doesn't depend on group names
2. Group Helper (app/Helpers/GroupHelper.php)
Added: New methods for group management
-
isDefaultGroup(string $groupId): bool- Checks if a group is one of the default groups (admin, moderator, member, guest)
- Uses group IDs identified by permissions
-
hasUsers(string $groupId): bool- Checks if a group has any users assigned to it
- Checks both primary group assignment (
user.group_id) and additional group assignments (user_groupstable) - Prevents deletion of groups that are in use
Modified: Existing methods to use ID-based identification
isAdmin(),isModerator(),getAdminGroupId(),getModeratorGroupId(),getMemberGroupId(),getGuestGroupId()already use permission-based identification with fallback to names
3. Group Controller (app/Controllers/Admin/GroupController.php)
Modified: delete() method to add protection against deletion
-
Added: Validation to prevent deletion of default groups
- Uses
GroupHelper::isDefaultGroup()to check if the group is a default group - Returns error message:
groups.message.cannot_delete_default
- Uses
-
Added: Validation to prevent deletion of groups with users
- Uses
GroupHelper::hasUsers()to check if the group has assigned users - Returns error message:
groups.message.cannot_delete_with_users
- Uses
-
Added: Proper error handling and JSON responses
- Returns appropriate HTTP status codes (400 for validation errors, 500 for server errors)
- Returns user-friendly error messages in the response
4. Translation Files
Added: New translation keys for group deletion errors
-
French (
languages/fr/admin.json):groups.message.cannot_delete_default: "Impossible de supprimer un groupe par défaut (Administrateur, Modérateur, Membre, Invité)"groups.message.cannot_delete_with_users: "Impossible de supprimer un groupe qui a des utilisateurs assignés. Réassignez d'abord les utilisateurs à un autre groupe."
-
English (
languages/en/admin.json):groups.message.cannot_delete_default: "Cannot delete a default group (Administrator, Moderator, Member, Guest)"groups.message.cannot_delete_with_users: "Cannot delete a group that has users assigned. Reassign users to another group first."
5. Admin Groups View (app/Views/admin/groups.php)
Improved: UX for group inheritance dropdown
- Changes:
- Separated base groups (Admin, Moderator, Member, Guest) from other groups using
<optgroup> - Ordered base groups logically (Admin → Moderator → Member → Guest)
- Fixed duplicate "Aucun (groupe vide)" option
- Fixed incorrect group identification (e.g., "Administrateur - invité")
- Uses
GroupHelpermethods to identify groups by ID instead of name - Uses nested translation keys (
groups.type.admin,groups.type.moderator, etc.)
- Separated base groups (Admin, Moderator, Member, Guest) from other groups using
Technical Details
Why these changes were needed
- Duplicate Groups: When administrators renamed default groups,
initDefaults()would recreate them because it checked by name, leading to duplicate groups like "Administrateur - invité" - Missing Protection: Default groups could be deleted, potentially breaking the system
- Data Integrity: Groups with users could be deleted, leaving users in an invalid state
Group Identification Strategy
The system now uses a three-tier identification strategy:
- Primary: Check for specific permissions (
admin.access,moderation.moderate) - Secondary: Use cached group IDs from helper methods
- Fallback: Check group names for backward compatibility
This ensures:
- ✅ Groups can be renamed without breaking the system
- ✅ New groups with similar permissions are correctly identified
- ✅ Backward compatibility with existing installations
Bug Fixes
1. Duplicate Group Creation
- Error: Renaming "Administrateur" to "Admin" would create a duplicate "Administrateur" group
- Root Cause:
initDefaults()checked groups by name instead of ID/permissions - Fix: Modified to use
GroupHelpermethods that identify groups by permissions and IDs
2. Group Deletion Issues
- Error: Default groups could be deleted, and groups with users could be deleted
- Root Cause: No validation in
GroupController::delete() - Fix: Added validation using
isDefaultGroup()andhasUsers()methods
3. Incorrect Group Display
- Error: Groups incorrectly displayed as "Administrateur - invité" in dropdown
- Root Cause: Logic in dropdown view didn't uniquely identify groups
- Fix: Improved logic using
$baseGroupsMapto ensure each group is identified only once
Part 4: Backend UX Improvements
Summary
Improved user experience in the admin panel by eliminating the need to manually refresh pages after create, update, or delete operations. Changes are now reflected immediately in the interface without full page reloads. Applied to all admin pages for consistent behavior across the entire backend.
Changes
1. Admin Utilities JavaScript (themes/assets/js/admin-utils.js)
Added: New utility functions for dynamic admin operations
-
reloadAdminList(url, containerSelector, callback)- Reloads admin list content via AJAX without full page reload
- Preserves scroll position
- Reinitializes Bootstrap tooltips and event listeners
- Falls back to full page reload on error
-
removeAdminListItem(elementSelector, callback)- Removes an item from the DOM with fade-out animation
- Displays empty state message if list becomes empty
- Executes callback after removal
-
resetAdminForm(formId, listTabId)- Resets form fields to initial state
- Clears hidden ID fields
- Resets color and icon previews
- Switches to list tab after reset
-
updateAdminTableRow(rowSelector, data)- Updates table row cells with new data
- Escapes HTML to prevent XSS
-
addAdminTableRow(tbodySelector, data, rowTemplate)- Adds new row to admin table
- Removes empty state message
- Reinitializes tooltips and event listeners
2. Backend Footer (app/Views/layouts/backend/footer.php, themes/premium/views/layouts/backend/footer.php)
Modified: Added admin-utils.js to footer
- Change: Included
admin-utils.jsbefore other admin scripts - Location: After
admin-sidebar.js, before core JS files
3. Groups Management (app/Views/admin/groups.php)
Modified: Replaced page reloads with dynamic updates
-
Create/Update Operations:
- Uses
reloadAdminList()to refresh list via AJAX - Resets form and switches to list tab after success
- No full page reload required
- Uses
-
Delete Operations:
- Uses
removeAdminListItem()to remove row from DOM - Smooth fade-out animation
- Falls back to list reload if element not found
- Uses
-
Added:
data-empty-messageattribute to tbody for empty state handling
4. Categories Management (app/Views/admin/categories.php)
Modified: Replaced page reloads with dynamic updates
-
Create/Update Operations:
- Uses
reloadAdminList()to refresh list via AJAX - Resets form and switches to list tab after success
- Uses
-
Delete Operations:
- Uses
removeAdminListItem()to remove row from DOM - Smooth fade-out animation
- Uses
-
Added:
data-empty-messageattribute to tbody
5. Reactions Management (app/Views/admin/reactions.php)
Modified: Replaced page reloads with dynamic updates
-
Create/Update Operations:
- Uses
reloadAdminList()to refresh list via AJAX - Resets form and switches to list tab after success
- Uses
-
Delete Operations:
- Uses
removeAdminListItem()to remove row from DOM - Smooth fade-out animation
- Uses
-
Added:
data-empty-messageattribute to tbody
6. Users Management (app/Views/admin/users.php, app/Views/admin/users/edit.php)
Modified: Replaced page reloads with dynamic updates
-
Toggle Ban/Active Operations:
- Uses
reloadAdminAfterAction()to reload page dynamically - No full page reload required
- Uses
-
Delete/Anonymize Operations:
- Uses
reloadAdminAfterAction()after modal operations - Maintains user list state
- Uses
-
Edit User:
- Uses
reloadAdminAfterAction()to redirect to users list - Preserves scroll position
- Uses
7. Reports Management (app/Views/admin/reports.php)
Modified: Replaced page reloads with dynamic updates
-
Update/Delete/Purge Operations:
- Uses
reloadAdminAfterAction()to reload page dynamically - Maintains report list state
- Uses
-
View Report Details:
- Uses
reloadAdminAfterAction()for navigation with query parameters
- Uses
8. Bans Management (app/Views/admin/bans.php)
Modified: Replaced page reloads with dynamic updates
- Create/Revoke/Delete Operations:
- Uses
reloadAdminAfterAction()to reload page dynamically - Maintains ban list state
- Uses
9. Backups Management (app/Views/admin/backups.php)
Modified: Replaced page reloads with dynamic updates
- Create/Restore/Delete Operations:
- Uses
reloadAdminAfterAction()to reload page dynamically - Maintains backup list state
- Uses
10. Plugins Management (app/Views/admin/plugins.php)
Note: This page intentionally uses full page reloads
- Toggle Plugin Operations:
- Full page reload required to properly load/unload plugin assets
- Ensures plugin changes are fully applied
11. Themes Management (app/Views/admin/themes.php, app/Views/admin/theme-config.php)
Note: These pages intentionally use full page reloads
-
Activate Theme Operations:
- Full page reload required to apply theme changes
-
Theme Configuration:
- Full page reload required after logo upload/delete and config updates
- Ensures all theme assets and styles are properly reloaded
12. Updates Management (app/Views/admin/updates.php)
Modified: Replaced page reloads with dynamic updates
- Check/Apply Updates:
- Uses
reloadAdminAfterAction()to reload page dynamically - Maintains update status display
- Uses
13. Configuration Page (app/Views/admin/config.php, themes/premium/views/admin/config.php, themes/bootswatch/views/admin/config.php)
Modified: Enhanced dynamic updates with language change detection
-
Language Change Detection:
- Added
data-old-valueattribute to language select - Detects language changes after save
- Full page reload when language changes (required for translations)
- Added
-
Other Settings:
- Dynamic form content reload via AJAX
- Preserves active tab state
- Reinitializes form handlers
Technical Details
Why these changes were needed
- Users had to manually refresh pages after every operation (create, update, delete)
- This created a poor user experience and slowed down workflow
- Full page reloads were unnecessary when only list content needed updating
Implementation Strategy
- AJAX List Reloading: Fetch full page HTML, extract list container, replace DOM content
- Direct DOM Manipulation: Remove deleted items directly from DOM with animation
- Form Reset: Clear form fields and switch to list view after successful operations
- Fallback Handling: Gracefully fall back to full page reload if AJAX fails
Universal Application
The dynamic reloading system has been applied to most admin pages:
- ✅ Groups Management
- ✅ Categories Management
- ✅ Reactions Management
- ✅ Users Management (list and edit)
- ✅ Reports Management
- ✅ Bans Management
- ✅ Backups Management
- ✅ Updates Management
- ✅ Configuration (with special handling for language changes)
Note: The following pages intentionally use full page reloads:
- ⚠️ Plugins Management (requires full reload to load/unload plugin assets)
- ⚠️ Themes Management (requires full reload to apply theme changes and reload assets)
Benefits
- ✅ Faster Operations: No page reload delay
- ✅ Better UX: Immediate visual feedback
- ✅ Preserved State: Scroll position and form state maintained
- ✅ Smooth Animations: Fade-out effects for deletions
- ✅ Backward Compatible: Falls back to full reload if utilities unavailable
- ✅ Consistent Experience: Same behavior across all admin pages
- ✅ Smart Reloading: Language changes trigger full reload (required for translations), other changes use dynamic updates
Compatibility
- ✅ Works with all admin pages using the standard list/form structure
- ✅ Compatible with Bootstrap tooltips and modals
- ✅ Supports drag-and-drop reordering (Sortable.js)
- ✅ Graceful degradation if JavaScript fails
Part 5: Code Quality Improvements
Summary
Cleaned up verbose logging in the translation system to reduce log noise and improve performance.
Changes
1. Translator Helper (app/Helpers/Translator.php)
Modified: Removed verbose debug logging
-
Removed: Debug logs for:
- Translation file loading (
Translator: Loading translations) - Translation file loaded confirmation (
Translator: Translation file loaded) - Translation file not found (
Translator: Translation file not found) - Translations loaded summary (
Translator: Translations loaded) - Common key translation lookups (
Translator: Translating common key) - Key not found in path (
Translator: Key not found in translation path) - Common key translation found (
Translator: Common key translation found) - Domain not found (
Translator: Domain not found) - Common key found in main domain (
Translator: Common key found in main domain (fallback)) - Common key translation not found (
Translator: Common key translation not found)
- Translation file loading (
-
Kept: Warning logs for actual errors:
- Failed to decode translation file (JSON errors)
- Error retrieving user language preference
Benefits:
- ✅ Reduced log file size
- ✅ Improved performance (fewer I/O operations)
- ✅ Cleaner logs focusing on actual issues
- ✅ Better debugging experience
Technical Details
Why these changes were needed
- Debug logs were generated on every page load for every translation lookup
- Log files were growing excessively large
- Most debug information was not useful for production debugging
- Only actual errors need to be logged
Testing Checklist
API Routing
- ✅
GET /api/notificationsreturns correct response - ✅
POST /api/presence/updateworks correctly - ✅ All other
/api/*endpoints function properly - ✅ Works with root installation
- ✅ Works with subdirectory installation
- ✅ Works with subdomain installation
Premium Theme
- ✅ Theme settings are properly loaded from
theme.json - ✅ All feature toggles work correctly (hero, stats, CTA, avatars, signatures, etc.)
- ✅ Visual settings (layout, navbar style, card style) are applied
- ✅ CSS variables are correctly set for dynamic styling
- ✅ JavaScript reads theme configuration from injected JSON
- ✅ Recent registrations section displays correctly in footer
- ✅ Back-to-top button appears and functions correctly
- ✅ RSS feeds and sitemaps respect category permissions
- ✅ Admin panel link is visible for admin users
Group Management
- ✅ Default groups cannot be deleted
- ✅ Groups with users cannot be deleted
- ✅ Renaming default groups doesn't create duplicates
- ✅ Group inheritance dropdown displays correctly
- ✅ Base groups are properly identified and separated
- ✅ Translation keys are correctly displayed
Backend UX
- ✅ No manual page refresh required after operations (all admin pages)
- ✅ Lists update dynamically via AJAX
- ✅ Smooth animations for deletions
- ✅ Forms reset automatically after success
- ✅ Language changes trigger automatic page reload for translations
- ✅ Consistent behavior across all admin pages
Automatic Theme Mode
- ✅ Automatic mode now correctly detects browser/system preferences
- ✅ Real-time updates when system preferences change
- ✅ Uses standard
prefers-color-schememedia query - ✅ Works across all themes (premium, default, bootswatch, futurist)
Code Quality
- ✅ Log files are cleaner and more focused
- ✅ No excessive debug logging
- ✅ Error logs still capture important issues
Migration Notes
For Existing Installations
API Routing
- Apache: No action required, changes are automatic
- Nginx: Test configuration with
sudo nginx -tand reload withsudo systemctl reload nginx
Premium Theme
- No migration needed: All changes are backward compatible
- Optional: Review
themes/premium/theme.jsonto customize settings
Group Management
- No migration needed: Changes are automatic and backward compatible
- Action required: If you have duplicate groups from previous renaming, you may need to manually clean them up:
- Go to Admin Panel > Users > Groups
- Identify duplicate groups (e.g., "Administrateur - invité")
- Reassign any users from duplicate groups to the correct group
- Delete duplicate groups (if they have no users)
Code Quality
- No action required: Log cleanup is automatic
Premium Theme Optimization
- No action required: Removed files automatically fall back to default views
- Benefit: Theme is now lighter and easier to maintain
- Note: Only customized views remain in the theme
Files Modified
API Routing
.htaccess(root directory)public/.htaccessnginx.conf
Premium Theme
themes/premium/theme.json(new)app/Helpers/ThemeHelper.phpthemes/premium/views/layouts/frontend/header.phpthemes/premium/views/layouts/frontend/main.phpthemes/premium/views/layouts/frontend/footer.phpthemes/premium/views/components/banner.phpthemes/premium/views/categories/index.phpthemes/premium/views/discussions/index.phpthemes/premium/views/components/post-thread.phpthemes/premium/assets/js/main.jsthemes/premium/assets/css/frontend.cssthemes/premium/views/components/theme-colors.phpapp/Services/RssService.phpapp/Services/SitemapService.php
Group Management
app/Models/Group.phpapp/Helpers/GroupHelper.phpapp/Controllers/Admin/GroupController.phpapp/Views/admin/groups.phplanguages/fr/admin.jsonlanguages/en/admin.json
Backend UX
themes/assets/js/admin-utils.js(new)app/Views/layouts/backend/footer.phpthemes/premium/views/layouts/backend/footer.phpthemes/bootswatch/views/layouts/backend/footer.phpapp/Views/admin/groups.phpapp/Views/admin/categories.phpapp/Views/admin/reactions.phpapp/Views/admin/users.phpapp/Views/admin/users/edit.phpapp/Views/admin/reports.phpapp/Views/admin/bans.phpapp/Views/admin/backups.phpapp/Views/admin/plugins.phpapp/Views/admin/themes.phpapp/Views/admin/theme-config.phpapp/Views/admin/updates.phpapp/Views/admin/config.phpthemes/premium/views/admin/config.phpthemes/bootswatch/views/admin/config.php
Premium Theme Optimization
- 58 view files removed from
themes/premium/views/(all unmodified views) - Theme now only contains customized views
- Automatic fallback to default views for removed files
Automatic Theme Mode Fix
themes/assets/js/main.jsthemes/assets/js/theme-initializer.jsthemes/assets/js/theme-initializer-backend.jsthemes/premium/assets/js/main.jsthemes/default/assets/js/main.jsthemes/bootswatch/assets/js/main.jsthemes/futurist/assets/js/main.js
Code Quality
app/Helpers/Translator.php
Breaking Changes
None. All changes are backward compatible.
Known Issues
None at this time.
Credits
- API routing improvements
- Premium theme configuration system
- Group management enhancements
- Automatic theme mode improvements
- Code quality improvements
Support
For issues or questions related to this release, please refer to the official Flatboard documentation or contact support.
Changelog - RC6 - Complete Release Notes
Release Date: December 31, 2025
Version: 5.0.0-RC6
Codename: HAPPY NEW YEAR
Translation System
- Fixed confirmation modal translations: Updated
confirm-modal.jsto properly usewindow.__()function for translations instead of hardcoded fallbacks - Added missing translation keys: Added
common.confirm.titleandcommon.button.confirmto both French and English translation files (languages/fr/main.jsonandlanguages/en/main.json) - Improved translation fallback system: Enhanced
getTranslation()helper function inconfirm-modal.jsto properly handle translation keys with fallback support
JavaScript Syntax Errors
- Fixed syntax error in
app/Views/admin/reactions.php: Removed extra closing brace indeleteReaction()function (line 373) - Fixed variable redeclaration in
themes/assets/js/discussion.js: Removed duplicateconst modalElementdeclaration (line 397), now reusing the variable declared at line 376 - Fixed orphaned code in
themes/assets/js/notifications.js: Removed orphaned code fragment (lines 466-479) that was causing syntax error with unexpected token: - Fixed unterminated comment in
themes/assets/js/discussion.js: Added missing closing*/for multi-line comment block starting at line 484
Discussion Management
- Fixed edit discussion link: Updated
editDiscussion()function inapp/Views/discussions/show.phpto useUrlHelper::to()for proper URL construction, ensuring correct behavior when Flatboard is installed in a subfolder - Fixed moderation actions not working:
- Exposed moderation functions (
togglePinDiscussion,toggleLockDiscussion,toggleSolvedDiscussion) to global scope inapp/Views/discussions/show.php - Added event handler for direct moderation actions that don't require confirmation
- Actions now properly parse
data-action-paramsand call the corresponding functions
- Exposed moderation functions (
Code Quality
- Improved code organization: Better separation of concerns between confirmation-required actions and direct actions
- Enhanced error handling: Added proper error logging and user feedback for failed actions
Technical Details
Files Modified
themes/assets/js/confirm-modal.jslanguages/fr/main.jsonlanguages/en/main.jsonapp/Views/admin/reactions.phpthemes/assets/js/discussion.jsthemes/assets/js/notifications.jsapp/Views/discussions/show.php
Translation Keys Added
common.confirm.title(FR: "Confirmation de suppression", EN: "Delete Confirmation")common.button.confirm(FR: "Confirmer", EN: "Confirm")
Functions Exposed to Global Scope
window.togglePinDiscussionwindow.toggleLockDiscussionwindow.toggleSolvedDiscussion
Breaking Changes
None
Migration Notes
No migration required. All fixes are backward compatible.
[RC6-HAPPY NEW YEAR] - 2026-01-01
Removed
Permissions
- Obsolete Permission: Removed
category.view_privatepermission (replaced byallowed_groupson categories)- The permission is automatically cleaned up from existing installations during permission initialization
- Group permissions references are also automatically removed
- Category access is now managed through the
allowed_groupsfield on each category, providing more granular control - Modified Files:
app/Helpers/PermissionHelper.php
Fixed
Permissions Initialization
- Group Permissions: Fixed issue where default group permissions were not initialized when permissions already existed
- Added verification to check if
group_permissionsexist and are valid before skipping recreation - If
group_permissionsare missing or empty, they are now automatically recreated even if permission hash hasn't changed - This ensures that default group permissions are always properly initialized on first installation or after data migration
- Modified Files:
app/Storage/JsonStorage.php
- Added verification to check if
Admin Interface - Dark Mode
- Plugin List Filter Buttons: Fixed invisible text on "All" filter button in dark mode
- Added specific CSS styles for checked
btn-checkbuttons in dark mode to ensure text is always visible - Applied to all outline button variants (primary, success, secondary) for consistency
- Modified Files:
themes/default/assets/css/backend.cssthemes/premium/assets/css/backend.cssthemes/bootswatch/assets/css/backend.css
- Added specific CSS styles for checked
Fixed
Discussion Editing
- Content Update: Fixed issue where discussion modifications were not immediately visible after saving. Now properly updates
rendered_htmlandcontent_hashfor the first post when editing a discussion - Modified Files:
app/Controllers/Discussion/DiscussionController.php
Subfolder Installation Support
- Avatar URLs: Fixed
NS_BINDING_ABORTEDerrors for avatar image requests when Flatboard is installed in a subfolder. All avatar URLs now usewindow.url()helper function - Subscription System: Fixed subscription/unsubscription functionality when Flatboard is installed in a subfolder
- Added CSRF middleware to subscription routes
- Fixed URL construction using
window.url()helper - Improved error handling with proper status code checks
- Added rate limiting error handling (429 status)
- URL Helper: Fixed
window.url()function to correctly handle trailing slashes inBASE_URL - Modified Files:
app/Core/App.phpapp/Views/components/reaction-picker.phpapp/Views/components/translations.phpthemes/assets/js/url-helper.jsapp/Views/discussions/show.phpthemes/premium/views/discussions/show.phpthemes/bootswatch/views/discussions/show.phpthemes/premium/views/components/reaction-picker.phpthemes/bootswatch/views/components/reaction-picker.php
JavaScript Errors
- Variable Redeclaration: Fixed
Uncaught SyntaxError: redeclaration of const errorMsgin discussion view by removing duplicate const declaration - Type Safety: Fixed
Uncaught TypeError: node.id.startsWith is not a functionin MutationObserver by adding proper type and existence checks before callingstartsWith() - Modified Files:
app/Views/discussions/show.php
Translation System
- Subscription Messages: Fixed untranslated subscription status messages (
subscription.status.subscribed,subscription.status.unsubscribed,subscription.subscribed,subscription.unsubscribed)- Changed from using JavaScript translation objects to direct PHP translation loading
- Now correctly accesses translations from
subscriptionnamespace in language files - Applied fixes to all themes (default, premium, bootswatch)
- Modified Files:
app/Views/discussions/show.phpthemes/premium/views/discussions/show.php
Moderation Tools
- URL Construction: Fixed all moderation tool links to properly use
UrlHelperfor correct base path handling- Ban user links now use
UrlHelper::to('/admin/bans')instead of hardcoded paths - Redirect parameters now correctly strip base path to avoid duplication
- Discussion and post URLs for redirects are now properly constructed as relative paths
- Ban user links now use
- Ban Revocation: Improved error handling for ban revocation
- Added CSRF middleware protection to all ban routes (create, revoke, delete, delete-multiple)
- Added validation for ban ID existence before revocation
- Improved error handling with proper HTTP status codes and error messages
- Enhanced JavaScript error handling with response status checks
- Modified Files:
app/Core/App.phpapp/Controllers/Moderation/BanController.phpapp/Views/admin/bans.phpthemes/premium/views/admin/bans.phpapp/Controllers/User/UserController.phpapp/Views/components/post-thread.phpthemes/premium/views/components/post-thread.php
Security - CSRF Protection
- Comprehensive CSRF Protection: Added CSRF middleware to all POST routes that modify data
- Admin Routes: All user management, category, group, permission, reaction, report, ban, plugin, theme, backup, and maintenance routes
- Discussion Routes: All discussion and post creation, update, delete, and moderation actions (pin, lock, solved, best answer)
- User Routes: Profile updates, 2FA enable/disable, preferences, presence updates
- Social Routes: Reactions, share, mentions, reports
- Upload Routes: All file upload operations (images, avatars, attachments)
- Notification Routes: Mark as read, delete operations
- Draft Routes: Save and delete operations
- Modified Files:
app/Core/App.php
Added
Notification System
- Empty State Fix: Fixed infinite spinner issue when no notifications are available
- Spinner is now properly removed and replaced with "Aucune notification" message
- Improved error handling to display empty state instead of leaving spinner visible
- Modified Files:
themes/assets/js/notifications.js
Changed
Users List
- Administrator and Moderator Detection: Now uses
GroupHelper::isAdmin()andGroupHelper::isModerator()instead of group name comparison for more reliable permission-based detection - Staff Sorting: Sort by user ID instead of username to ensure a stable and predictable order
- Display Order: Administrators appear first (order_index = 0), followed by moderators (order_index = 1), then sorted by ID
- Modified Files:
app/Controllers/User/UserController.phpapp/Views/users/list.phpthemes/premium/views/users/list.php
Premium Theme - Statistics Display
- Compact Statistics Card: Improved display of icons and numbers
- Card background adapted to theme (no dark blue in dark mode)
- Added padding to prevent icons and numbers from touching the edges
- More visible icons with increased size and adjusted colors for dark mode
- More readable numbers with increased font size and weight
- Dark Mode: Reduced overlay card effects (shadows, gradients) for a more discreet appearance
- Removed box-shadow on cards in dark mode
- Removed transform effects on hover
- Card-header with transparent background instead of gradient
- Modified Files:
themes/premium/assets/css/frontend.css
[RC7 - 2026-01-02] - Backup System Enhancements: Download, Upload & Improved Restore
Added
-
Backup Download Feature: Added ability to download backup files directly from the admin panel
- New
download()method inBackupControllerwith security validation - Download button added to each backup in the backup list
- Route:
GET /admin/backups/download - Validates backup filename to prevent path traversal attacks
- Sends backup file with proper headers for download
- New
-
Backup Upload Feature: Added ability to upload backup files to the server
- New
upload()method inBackupControllerwith comprehensive validation - Upload modal in admin panel with file selection
- Validates file type (ZIP only), MIME type, extension, and size (max 500MB or
upload_max_filesize) - Validates ZIP archive integrity and checks for
manifest.json - Automatically generates secure filename if needed
- Handles duplicate filenames with numeric suffix
- Route:
POST /admin/backups/upload(with CSRF protection) - Uses native Flatboard
Request::getFile()method for file handling
- New
-
Translation Keys: Added new translation keys for backup download/upload features
backups.download: "Download" / "Télécharger"backups.upload.title: "Upload backup" / "Uploader une sauvegarde"backups.upload.select_file: "Select ZIP file" / "Sélectionner un fichier ZIP"backups.upload.file_help: Help text for file selectionbackups.upload.submit: "Upload" / "Uploader"backups.upload.uploading: "Uploading..." / "Upload en cours..."backups.upload.success: Success messagebackups.upload.error: Error messagebackups.upload.no_file: "Please select a file" / "Veuillez sélectionner un fichier"backups.message.uploaded: "Backup uploaded successfully" / "Sauvegarde uploadée avec succès"backups.message.upload_error: "Error uploading" / "Erreur lors de l'upload"backups.message.download_error: "Error downloading" / "Erreur lors du téléchargement"backups.message.invalid_name: "Invalid filename" / "Nom de fichier invalide"
Changed
-
Backup Restore Process: Improved restore process to clean existing data before restoration
- Restore now deletes existing data (JSON/SQLite) before restoring from backup
- Prevents conflicts with old data (e.g., installation user remaining after restore)
- Only deletes data that will be restored (based on backup manifest)
- Cleans uploads, logs, cache, plugins, and themes if they were included in the backup
- Uses recursive directory deletion with native
deleteDirectory()method - Ensures clean restoration without data conflicts
-
Backup Restore Logic: Simplified restore code using manifest-based deletion
- Restore checks
manifest['options']andmanifest['included_items']to determine what to delete - Only deletes directories/files that will be restored from backup
- More efficient and safer restore process
- Better logging for restore operations
- Restore checks
Technical Details
- Download method validates backup filename with regex:
/^backup_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(_\d+)?\.zip$/ - Upload method validates:
- MIME type:
application/ziporapplication/x-zip-compressed - File extension:
.ziponly - File size: max 500MB or PHP's
upload_max_filesize(whichever is smaller) - ZIP archive integrity
- Presence of
manifest.jsonin archive
- MIME type:
- Upload uses
Request::getFile()for consistency with Flatboard's native file handling - Restore process uses
deleteDirectory()recursively to clean directories before restoration - All fetch requests in backup views now use
window.url()for proper BASE_URL handling
EasyMDE Plugin Path Fix & Simplified YouTube/TikTok Control
Fixed
- EasyMDE Plugin Path: Corrected plugin file paths to use
EasyMDE(uppercase) instead ofeasymde(lowercase) to match the actual folder name- Updated all file paths in
plugins/easymde/EasyMDEPlugin.phpto useplugins/EasyMDE/instead ofplugins/easymde/ - Updated CHANGELOG.md references to use correct case-sensitive paths
- This ensures compatibility on case-sensitive file systems (Linux/Mac)
- Updated all file paths in
Changed
- EasyMDE YouTube/TikTok Control Simplified: Removed global "Allow YouTube" and "Allow TikTok" settings
- Removed
checkbox_allowYoutubeandcheckbox_allowTiktokparameters fromplugin.json - Removed filtering logic based on these parameters from
getToolbarForContext()andbuildEditorConfig() - Removed translation keys for these parameters from language files
- YouTube and TikTok buttons are now controlled exclusively via toolbar context configuration
- Administrators can simply check/uncheck YouTube and TikTok buttons in each toolbar context to enable/disable them
- This simplifies the configuration: no need for separate global settings, just configure each context's toolbar directly
- Removed
Technical Details
- YouTube and TikTok buttons are now controlled only by their presence in toolbar context configurations
- JavaScript
transformToolbar()function detects if "youtube" or "tiktok" are present in the toolbar array and transforms them accordingly - No more global flags or filtering logic needed - simpler and more intuitive configuration
Default Permissions Fix, Upload Settings & File Type Restrictions
Fixed
-
Critical Security: Default Permissions for Groups: Fixed members having administrator rights on first installation
- Modified
PermissionHelper::initDefaults()to properly assign permissions by group admin.accesspermission is now strictly limited to Administrators group only- Members no longer have admin access by default
- Applied to both first installation and existing installations
- Modified
-
Permissions Not Saved: Fixed permissions being reset every time the admin permissions page was loaded
- Removed
PermissionHelper::initDefaults()call fromPermissionController::index()which was resetting permissions on each page load - Modified
PermissionHelper::initDefaults()to detect first installation and reset all permissions only during first install - For existing installations, only corrupted permissions are reset (e.g.,
admin.accesswith wrong groups) - User modifications to permissions are now preserved permanently
- First installation is detected by checking if
admin.accesshas incorrect groups (members having admin access)
- Removed
-
Guest Download Permission: Removed
attachment.downloadpermission from Guest group by default- Guests can view attachments but cannot download them by default
- Only Administrators, Moderators, and Members can download attachments
- Applied in
PermissionHelper::initDefaults()
Changed
-
File Upload Settings: Updated default file type restrictions
- Attachments: Changed default allowed types from
['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'md', 'zip', 'rar', '7z']to['pdf', 'md', 'zip', 'rar', '7z', 'doc'] - Images:
webpis available but not enabled by default (default:['jpg', 'jpeg', 'png', 'gif']). Administrators can enable WEBP in the permissions settings. - Avatars:
webpis available but not enabled by default (default:['jpg', 'jpeg', 'png', 'gif']). Administrators can enable WEBP in the permissions settings. - Updated in:
install.php,app/Controllers/UploadController.php, all admin permissions views, all user settings views, markdown editor components
- Attachments: Changed default allowed types from
-
Permission System: Enhanced permission initialization logic
- Permissions are now properly assigned by group during initialization
- Permissions are only reset if they are detected as corrupted (e.g.,
admin.accesswith wrong groups) - User modifications to permissions are preserved permanently
- Added logging to verify correct permission assignments
admin.accessis strictly enforced to Administrators only
Technical Details
PermissionHelper::initDefaults()now only resets permissions if they are detected as corruptedPermissionController::index()no longer callsinitDefaults()to prevent resetting user modifications- File upload defaults are defined in multiple locations and must be kept in sync:
install.php: Initial configuration during installationapp/Controllers/UploadController.php: Fallback defaults in upload methodsapp/Views/admin/permissions.phpand theme overrides: UI display defaultsapp/Views/users/settings.phpand theme overrides: Avatar upload UI defaultsapp/Views/components/markdown-editor.phpand theme overrides: Image upload UI defaults
EasyMDE Admin View Translations & Language Configuration
Fixed
-
EasyMDE Admin View Not Translated: Fixed EasyMDE admin view displaying hardcoded French text instead of using translations
- Modified
plugins/EasyMDE/views/admin.phpto use translations fromprepareAdminViewVars()instead of hardcoded text - All UI elements now use translation keys:
admin_title,admin_description,current_order,drag_drop_reorder,save_configurations,saving,config_saved_success,config_save_error,error,missing_elements - Context labels and button labels now use translations from
$contextsand$availableButtonsarrays prepared byprepareAdminViewVars() - JavaScript messages now use translations via
json_encode()for proper language display - Added fallback values for compatibility if translations are not loaded
- Modified
-
EasyMDE Language Configuration: Added automatic language detection and configuration for EasyMDE editor
- Modified
plugins/EasyMDE/EasyMDEPlugin.phpto detect current system language inbuildEditorConfig() - Added locale mapping for EasyMDE supported languages:
fr,en,es,it,ja,pt,ru,zh - EasyMDE editor now uses the
localeoption based on system language configuration - Language is automatically applied to all EasyMDE instances, including those in plugin admin views
- Modified
Changed
- EasyMDE Admin View: Refactored to use translation system
- Removed all hardcoded French text from
plugins/EasyMDE/views/admin.php - View now relies on
$translations,$contexts,$toolbars, and$availableButtonsvariables prepared byprepareAdminViewVars() - Added fallback logic for compatibility if variables are not prepared
- All user-facing text now respects system language configuration
- Removed all hardcoded French text from
Technical Details
PluginHelper::getTranslations()automatically usesTranslator::getCurrentLanguage()when$langparameter isnullprepareAdminViewVars()inEasyMDEPlugin.phploads translations and prepares all necessary variables for the admin view- EasyMDE's
localeoption is set in the JavaScript configuration passed viadata-easymde-configattribute - Language detection happens at editor configuration time, ensuring consistency across all editor instances
EasyMDE Spell Checker Fix
Fixed
- EasyMDE Spell Checker Not Working: Fixed spell checker not functioning in EasyMDE editor
- Applied
spellcheckattribute to CodeMirror's editable element after initialization - Set CodeMirror's
spellcheckoption directly viacm.setOption('spellcheck', true) - Applied
spellcheckattribute to original textarea element - Re-applied spellcheck attribute after each CodeMirror refresh to ensure it persists
- Applied to all editable elements within CodeMirror wrapper (textarea, contenteditable divs, CodeMirror-lines)
- Reference: EasyMDE GitHub
- Applied
Technical Details
- CodeMirror (used by EasyMDE) requires explicit
spellcheckoption to be set viacm.setOption('spellcheck', true) - The
spellcheckattribute must be applied to the actual editable element, not just the wrapper - CodeMirror may use either a textarea or contenteditable div depending on configuration
- Spellcheck is re-applied after each CodeMirror refresh to handle dynamic content changes
- The browser's native spell checker is used (requires browser language settings to be configured)
Backup System: Selective Plugins & Themes Support
Added
-
Selective Plugin Backup: Added ability to select specific plugins to include in backups
- New checkbox option "Plugins" in backup creation modal
- Collapsible section showing all available plugins with checkboxes
- "Select All Plugins" checkbox to quickly select/deselect all plugins
- Plugin information displayed: name, version, description
- If "Full backup" is selected, all plugins are automatically included
- If "Plugins" option is enabled but no specific plugins are selected, all plugins are included by default
- Applied to all theme variants: default, premium, bootswatch
-
Selective Theme Backup: Added ability to select specific themes to include in backups
- New checkbox option "Themes" in backup creation modal
- Collapsible section showing all available themes with checkboxes
- "Select All Themes" checkbox to quickly select/deselect all themes
- Theme information displayed: name, version, active status, description
- If "Full backup" is selected, all themes are automatically included
- If "Themes" option is enabled but no specific themes are selected, all themes are included by default
- Applied to all theme variants: default, premium, bootswatch
-
Translation Keys: Added new translation keys for backup options
backups.option.plugins: Label for plugins backup optionbackups.option.themes: Label for themes backup optionbackups.select_all_plugins: Label for "Select All Plugins" checkboxbackups.select_all_themes: Label for "Select All Themes" checkbox- Added to both French (
languages/fr/admin.json) and English (languages/en/admin.json)
Changed
-
BackupController: Enhanced
create()method to handle plugin and theme selection- Added
getAllPlugins()method to scan and list all available plugins - Added
getAllThemes()method to scan and list all available themes - Modified
addDirectoryToZip()to accept exclusion directories for plugins and themes - Plugin exclusions:
data,storage,messages,history,config,index - Theme exclusions:
cache,compiled,node_modules,.git - Updated
manifest.jsonto includeselected_pluginsandselected_themesarrays
- Added
-
Backup Modal UI: Improved user interface for backup creation
- Better organization with collapsible sections for plugins and themes
- Scrollable lists with max-height for better UX when many plugins/themes are available
- Visual indicators for active themes (badge)
- Consistent styling across all theme variants
Technical Details
Plugin and Theme Selection Logic
- When "Full backup" is checked, all plugins and themes are automatically selected
- When "Plugins" or "Themes" option is individually checked, user can select specific items
- If option is checked but no items are selected, all items are included by default
- Selected items are sent as JSON arrays in FormData:
selected_pluginsandselected_themes
JavaScript Implementation
- Added event listeners for "Select All" checkboxes with indeterminate state support
- Dynamic show/hide of selection lists based on main checkbox state
- Automatic selection of all items when "Full backup" is enabled
- Proper handling of checkbox states and form submission
CSRF Token Fixes, Theme Activation Fix, Plugin Activation Improvements & SQLite Config Fix
Fixed
-
Theme Activation with CSRF Token: Fixed theme activation not working due to missing CSRF token
- Added CSRF token retrieval and validation in
activateTheme()JavaScript function - Token is now sent in both
X-CSRF-Tokenheader and request body - Applied to all theme views:
app/Views/admin/themes.php,themes/premium/views/admin/themes.php,themes/bootswatch/views/admin/themes.php - Improved error handling with proper HTTP status code parsing
- Added CSRF token retrieval and validation in
-
Plugin Activation/Deactivation Improvements: Enhanced plugin toggle functionality with better error handling
- Added directory existence and writability checks before saving
plugin.json - Improved error messages with detailed file paths and permission information
- Added try-catch around configuration saving to prevent silent failures
- Better error logging for debugging plugin activation issues
- Added directory existence and writability checks before saving
-
CSRF Token Missing in Admin Views: Fixed multiple admin views that were not sending CSRF tokens
- Backups:
restoreBackup()anddeleteBackup()functions now send CSRF token - Updates:
checkUpdates()function now sends CSRF token - Plugin Settings: Added
X-CSRF-Tokenheader (token was already in FormData) - All functions now validate CSRF token before making requests
- Applied to all theme variants (default, premium, bootswatch)
- Improved error handling with proper HTTP error response parsing
- Backups:
-
Theme Activation with SQLite: Fixed theme activation errors in SQLite storage environments
- Enhanced
Config::save()method with better error handling - Added directory creation with error checking
- Added directory writability validation before attempting file writes
- Improved error messages with detailed path and permission information
- Configuration directory (
/stockage/json/) is now properly created and validated
- Enhanced
Changed
- CSRF Token Handling: Standardized CSRF token handling across all admin AJAX requests
- All POST requests now retrieve token from meta tag or form input
- Token is validated before making requests
- Token is sent in both header (
X-CSRF-Token) and body for maximum compatibility - Consistent error messages when token is missing
Technical Details
CSRF Token Implementation Pattern
All admin AJAX requests now follow this pattern:
// Récupérer le token CSRF
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content ||
document.querySelector('input[name="csrf_token"]')?.value || '';
if (!csrfToken) {
// Error handling
return;
}
// Créer FormData pour envoyer le token CSRF
const formData = new URLSearchParams();
formData.append('csrf_token', csrfToken);
fetch(url, {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': csrfToken
},
body: formData
})Configuration File Handling
The Config class always uses JSON storage (/stockage/json/config.json) regardless of the main storage backend (JSON or SQLite). This ensures:
- Configuration is always accessible
- Directory is created if it doesn't exist
- Permissions are validated before writes
- Detailed error messages help diagnose issues
Files Modified
app/Views/admin/themes.php- Added CSRF token toactivateTheme()themes/premium/views/admin/themes.php- Added CSRF token toactivateTheme()themes/bootswatch/views/admin/themes.php- Added CSRF token toactivateTheme()app/Controllers/Admin/PluginController.php- Enhanced error handling for plugin activationapp/Views/admin/backups.php- Added CSRF token torestoreBackup()anddeleteBackup()app/Views/admin/updates.php- Added CSRF token tocheckUpdates()app/Views/admin/plugin-settings.php- AddedX-CSRF-Tokenheaderthemes/premium/views/admin/backups.php- Added CSRF token to backup functionsthemes/premium/views/admin/updates.php- Added CSRF token tocheckUpdates()themes/premium/views/admin/plugin-settings.php- AddedX-CSRF-Tokenheaderthemes/bootswatch/views/admin/backups.php- Added CSRF token to backup functionsthemes/bootswatch/views/admin/updates.php- Added CSRF token tocheckUpdates()themes/bootswatch/views/admin/plugin-settings.php- AddedX-CSRF-Tokenheaderapp/Core/Config.php- Enhancedsave()method with better error handling and directory validationapp/Controllers/Admin/ThemeController.php- Already had error handling, now works correctly with fixed Config
Testing
- Verified theme activation works with CSRF token
- Verified plugin activation/deactivation with improved error handling
- Verified all admin AJAX requests send CSRF tokens
- Tested in both JSON and SQLite storage environments
- Verified configuration directory creation and permissions
Translation Improvements, Group Management Fixes, Presence Indicator Fix & EasyMDE YouTube/TikTok Fix
Fixed
-
YouTube/TikTok Disable in Discussion Edit: Fixed YouTube and TikTok buttons not being disabled in discussion edit context
- Added explicit
$context = 'discussion-edit'in discussion edit views while keeping$draftType = 'discussion'for draft system compatibility - The context is now correctly detected, allowing the toolbar filter to remove YouTube/TikTok buttons when disabled
- Applied to all themes: default, premium, and bootswatch
- YouTube and TikTok can now be properly disabled in all contexts: reply, post-edit, and discussion-edit
- Added explicit
-
Presence Indicator on All Pages: Fixed presence indicator to work correctly on all pages, not just the index page
- Added
window.currentPath()helper function in translation files to get the current relative path (accounting for subdirectories) - Updated all JavaScript files to use
window.currentPath()instead ofwindow.location.pathnamedirectly - The function uses
window.BASE_URL(defined viaUrlHelper::getBaseUrl()) to extract the relative path - Applied to all theme JavaScript files:
presence.js,main.js(default, premium, bootswatch, futurist themes) - The controller already uses
UrlHelperfor URL normalization, ensuring server-side consistency
- Added
-
User Edit View Translations: Fixed untranslated keys in the admin user edit view (
admin/users/edit)- Merged duplicate
"edit"sections inlanguages/fr/admin.jsonandlanguages/en/admin.json - All translation keys are now properly accessible:
users.edit.title→ "Modifier l'utilisateur" / "Edit user"users.edit.group→ "Groupe" / "Group"users.edit.select_group→ "Sélectionner un groupe" / "Select a group"users.edit.group_select_desc→ Description textusers.edit.email_verified→ "Email vérifié" / "Email verified"users.edit.email_verified_desc→ Description textusers.edit.is_banned→ "Banni" / "Banned"users.edit.password_keep_help→ Help text for password field
- Merged duplicate
-
User Count by Group: Fixed incorrect user count display in the groups list page
- Improved
GroupHelper::countUsersInGroup()to properly validate group IDs - Users with invalid
group_id(pointing to non-existent groups) are now ignored instead of being counted - Fixed issue where users with deleted group IDs were incorrectly counted
- Method now validates that
group_idexists in the groups table before counting - Works correctly with both JSON and SQLite storage backends
- Improved
-
Group Edit Redirection: Fixed redirection issue after saving a group edit
- After saving a group, users are now correctly redirected to
/admin/groups(list view) instead of staying on the edit URL - Simplified JavaScript to always redirect to the list after successful save
- Applied to both main view and premium theme
- After saving a group, users are now correctly redirected to
-
User Edit Redirection: Fixed redirection issue after saving a user edit
- After saving a user, users are now correctly redirected to
/admin/users(list view) instead of staying on the edit URL - Simplified JavaScript to always redirect to the list after successful save
- Applied to both main view and premium theme
- After saving a user, users are now correctly redirected to
-
SQLite Boolean Normalization: Fixed boolean field normalization in SQLite storage
- Added normalization for
is_deleted,is_banned, andis_activefields inSqliteStorage::normalizeUserBooleans() - Ensures consistent boolean handling between JSON and SQLite storage backends
- Prevents issues with user counting and filtering based on boolean fields
- Added normalization for
Changed
-
Debug Logging: Removed verbose debug logs from production code to reduce log noise
- Removed debug logs from
app/Core/Controller.php(requireAdmin(),requirePermission()) - Removed debug logs from
app/Helpers/PermissionHelper.php(can()) - Removed debug logs from
app/Helpers/GroupHelper.php(isAdmin()) - Removed debug logs from
app/Controllers/Admin/DashboardController.php(index()) - Removed debug logs from
app/Views/layouts/backend/header.php - Kept WARNING and ERROR level logs for production debugging
- Removed debug logs from
-
Group User Counting Logic: Improved group user counting to be more strict
- Removed fallback logic that counted users with invalid
group_idas members - Users with invalid
group_idare now ignored (not counted in any group) - This ensures data integrity and prevents incorrect counts
- Users should have valid
group_idvalues assigned to existing groups
- Removed fallback logic that counted users with invalid
Technical Details
Admin Permission Fix Script
The fix_admin_permissions.php script was created to resolve admin access issues:
- Correctly initializes the application using the custom autoloader
- Assigns a specified user (default:
255d6da168ebeaef345baa3485df8f90) to the admin group - Reinitializes all default permissions using
PermissionHelper::initDefaults() - Can be run via web browser or command line
Translation File Structure
The translation files had duplicate "edit" keys within the "users" object:
- First occurrence (lines 144-154): Contained all the edit form translations
- Second occurrence (lines 188-190): Only contained
password_keep_help, overwriting the first - Solution: Merged both sections into a single
"edit"object with all keys
Files Modified
app/Views/discussions/edit.php- Added explicit$context = 'discussion-edit'for toolbar filteringthemes/premium/views/discussions/edit.php- Added explicit$context = 'discussion-edit'for toolbar filteringthemes/bootswatch/views/discussions/edit.php- Added explicit$context = 'discussion-edit'for toolbar filteringapp/Views/components/translations.php- Addedwindow.currentPath()helper functionthemes/premium/views/components/translations.php- Addedwindow.currentPath()helper functionthemes/bootswatch/views/components/translations.php- Addedwindow.currentPath()helper functionthemes/assets/js/presence.js- Updated to usewindow.currentPath()instead ofwindow.location.pathnamethemes/assets/js/main.js- Updated to usewindow.currentPath()instead ofwindow.location.pathnamethemes/default/assets/js/main.js- Updated to usewindow.currentPath()instead ofwindow.location.pathnamethemes/premium/assets/js/main.js- Updated to usewindow.currentPath()instead ofwindow.location.pathnamethemes/bootswatch/assets/js/main.js- Updated to usewindow.currentPath()instead ofwindow.location.pathnamethemes/futurist/assets/js/main.js- Updated to usewindow.currentPath()instead ofwindow.location.pathnameapp/Core/Controller.php- Removed debug logsapp/Helpers/PermissionHelper.php- Removed debug logsapp/Helpers/GroupHelper.php- Removed debug logs, improvedcountUsersInGroup()methodapp/Controllers/Admin/DashboardController.php- Removed debug logsapp/Views/layouts/backend/header.php- Removed debug logsapp/Views/admin/users/edit.php- Fixed redirection after user saveapp/Views/admin/groups.php- Fixed redirection after group savethemes/premium/views/admin/users/edit.php- Fixed redirection after user savethemes/premium/views/admin/groups.php- Fixed redirection after group saveapp/Storage/SqliteStorage.php- Added boolean normalization foris_deleted,is_banned,is_activelanguages/fr/admin.json- Merged duplicateusers.editsectionslanguages/en/admin.json- Merged duplicateusers.editsections
Testing
- Verified admin access works correctly after permission fix
- Verified all translation keys display properly in user edit view
- Verified log files are cleaner without excessive debug messages
- Tested in both French and English locales
[RC7b - 2026-01-02]
Fixed
User Registration and Profile Access
- Fixed 404 error after user registration: Invalidated user cache (by username and email) immediately after user creation to prevent 404 errors when accessing user profiles (
/u/{username}) right after registration. This fixes an issue where the cache could containnullvalues from previous lookup attempts, causing newly created users to appear as non-existent.- Modified:
app/Models/User.php- Added cache invalidation inUser::create()method
- Modified:
URL Generation in Subdirectories
- Fixed URL generation for subdirectory installations: Improved
window.url()JavaScript function to properly handle base URLs with trailing slashes and ensure correct URL construction when Flatboard is installed in a subdirectory (e.g.,/forum/).- Modified:
app/Views/components/translations.php- Enhancedwindow.url()function with better base URL handling - Modified:
themes/premium/views/components/translations.php- Applied same fix to premium theme - Modified:
themes/bootswatch/views/components/translations.php- Applied same fix to bootswatch theme
- Modified:
Post Edit Form Loading
- Fixed 404 error when editing posts in subdirectories: Fixed the edit post form loading to properly construct URLs when Flatboard is installed in a subdirectory. The form now correctly loads via AJAX using the proper base URL.
- Modified:
themes/premium/views/discussions/show.php- Improved error handling and URL construction for edit form loading - Modified:
app/Views/discussions/show.php- Already had correct implementation, verified consistency
- Modified:
Technical Details
Cache Invalidation
The user creation process now properly invalidates all relevant cache entries:
user:{userId}- User cache by IDuser:email:{md5(email)}- User cache by emailuser:username:{md5(username)}- User cache by username
This ensures that immediately after registration, users can access their profiles without encountering 404 errors due to stale cache entries.
URL Helper Improvements
The window.url() function now:
- Properly handles
undefinedornullbase URLs - Removes trailing slashes from base URLs before constructing paths
- Uses
json_encode()for safer JavaScript variable injection - Provides explicit checks for empty base URLs
Notes
These fixes address issues that occur when:
- Flatboard is installed in a subdirectory (e.g.,
/forum/) - Users register and immediately try to access their profile
- Users attempt to edit posts after installation in a subdirectory
Changelog - Release Candidate 8 (RC8)
Release Date: January 4, 2026
Version: 5.0.0-RC8
Summary
This release candidate includes significant improvements to user experience, performance optimizations, bug fixes, and new features across the entire Flatboard platform. All changes are backward compatible and ready for production use.
🎉 Major Features
Complete Archive Creation
- New Feature: Added ability to create a complete Flatboard archive including all data, plugins, themes, and configurations
- Location: Admin Panel > Backups > "Create Complete Archive" button
- Details:
- Creates a ZIP archive with the entire Flatboard installation
- Includes all data directories (
stockage/,uploads/) - Includes all plugins with their data directories
- Includes all themes with their assets and
index.htmlfiles - Includes
versions/anddocs/directories - No exclusions - everything is included for a complete backup
- Files Modified:
app/Controllers/Admin/BackupController.php- AddedcreateFullArchive()methodapp/Views/admin/backups.php- Added UI button and JavaScript handlingapp/Core/App.php- Added route/admin/backups/create-full-archivelanguages/fr/admin.json- Added translationslanguages/en/admin.json- Added translations
Reputation System - Recalculation Feature (plugin in developement)
- New Feature: Added reputation recalculation functionality for all users
- Purpose: Recalculate user reputation points based on actual actions (discussions, replies, best answers, reactions) when backup restoration didn't restore reputation points
- Location: Admin Panel > Reputation > "Recalculate Reputation" button
- Details:
- Recalculates reputation for all users based on their actual contributions
- Counts discussions created, replies posted, best answers received, and reactions received
- Updates points, levels, and badges automatically
- Provides detailed statistics (total users, updated, unchanged, failed)
- Adds history entry
reputation_recalculatedfor tracking
- Files Modified:
plugins/Reputation/ReputationService.php- AddedrecalculateUserReputation()andrecalculateAllUsersReputation()methodsplugins/Reputation/ReputationController.php- AddedrecalculateReputation()API endpointplugins/Reputation/ReputationPlugin.php- Added route/admin/reputation/recalculateplugins/Reputation/views/admin.php- Added UI button and JavaScript handlingplugins/Reputation/langs/fr.json- Added translationsplugins/Reputation/langs/en.json- Added translationsplugins/Reputation/views/history.php- Added support forreputation_recalculatedactionplugins/Reputation/views/profile_tab.php- Added support forreputation_recalculatedaction
✨ User Experience Improvements
Dynamic Post Updates
- Enhancement: Posts are now created, edited, and deleted dynamically without page reload
- Features:
- New posts appear instantly in the discussion
- Edited posts update in place
- Deleted posts fade out with animation
- Automatic scroll to new posts with visual highlight
- URL hash updates (
#post-{id}) for direct linking - Post counter updates automatically
- Files Modified:
app/Views/discussions/show.phpthemes/premium/views/discussions/show.phpapp/Controllers/Api/PostApiController.php- Addedformat=htmlsupport
@username Mentions in Replies
- Enhancement: Mentions are now available in reply forms, not just discussion creation
- Features:
- Real-time user search via API
- Autocomplete dropdown with avatars
- Support for both CodeMirror (EasyMDE) and simple textarea editors
- Dynamic positioning of suggestion container
- Files Modified:
app/Views/discussions/show.phpthemes/premium/views/discussions/show.phpapp/Controllers/Api/UserApiController.php- Added/api/users/searchendpoint
Dynamic Reactions
- Enhancement: Reactions update instantly without page reload
- Features:
- Toggle reactions via API
- Real-time reaction badge updates
- Modal updates with active state indicators
- Automatic color calculation for badges
- Files Modified:
app/Views/components/reaction-picker.phpthemes/premium/views/components/reaction-picker.php
Smart Preloading
- Enhancement: Intelligent preloading of next pages and discussion links
- Features:
- Preloads "Load More" button content when approaching viewport (200px threshold)
- Preloads discussion links when approaching viewport (300px threshold)
- Preloads pagination links when approaching viewport (200px threshold)
- Uses
<link rel="prefetch">for links andfetch()with cache for API calls - Header
X-Preload: truefor server-side optimization
- Files Modified:
themes/assets/js/discussion.js- AddedinitSmartPreloading()functionthemes/assets/js/main.js- Integrated preloading initialization
🔧 Core Improvements
JavaScript Assets Centralization
- Enhancement: Centralized
main.jsfor all themes - Details:
- Single source file:
themes/assets/js/main.js - Removed duplicate copies from bootswatch, premium, default, and futurist themes
- All themes automatically benefit from updates
- Simplified maintenance with one source of truth
- Single source file:
- Files Modified:
themes/bootswatch/theme.json- Removedmain.jsfrom scriptsthemes/premium/theme.json- Removedmain.jsfrom scripts- Removed duplicate
main.jsfiles from all themes
StringHelper Optimization
- Enhancement: Replaced all
strpos()calls withStringHelpermethods - Details:
- More readable and maintainable code
- Consistent string manipulation across the codebase
- Files Modified:
- All views in
app/Views/ - All theme views
- All plugin files
- Core files
- All views in
Cache System Optimization
- Enhancement: Optimized cache operations with
getOrSet()method - Details:
- ~50% reduction in file system calls
- Direct value retrieval with existence check only if needed
- Backward compatible -
remember()automatically benefits
- Files Modified:
app/Core/Cache.php- AddedgetOrSet()method
Router Performance Optimization
- Enhancement: Route indexing by HTTP method
- Details:
- Routes indexed by method (GET, POST, PUT, DELETE, etc.)
- Only relevant routes are sorted and processed
- Significant reduction in route sorting time
- Better scalability as route count increases
- Files Modified:
app/Core/Router.php- Added$routesByMethodindexing
Router Hooks
- Enhancement: Global router hooks for cross-cutting concerns
- Details:
beforeEach()hook called before route handler executionafterEach()hook called after route handler execution- Enables centralized logging, analytics, and monitoring
- Optional - no breaking changes
- Files Modified:
app/Core/Router.php- AddedbeforeEach()andafterEach()methods
API URL Normalization
- Enhancement: Centralized API URL normalization logic
- Details:
- Improved detection of API URLs
- Better handling of subdirectory installations
- Consistent API URL format across all scenarios
- Files Modified:
app/Core/Request.php- AddednormalizeApiUrl()method
🐛 Bug Fixes
Backup Restoration Cache Issues
- Fix: Comprehensive cache invalidation after backup restoration
- Problem: Settings, permissions, and 2FA were not properly restored after backup restoration
- Solution:
- Invalidate permission caches
- Invalidate user group caches
- Invalidate theme settings caches
- Invalidate user data caches (2FA settings)
- Force re-detection of admin group
- Files Modified:
app/Controllers/Admin/BackupController.php- Enhancedrestore()method
Translation Key Structure
- Fix: Corrected translation keys in BackupController
- Problem: Translation keys were using
admin.backups.message.*instead ofbackups.message.* - Solution: Updated all translation keys to use correct structure when domain
adminis passed - Files Modified:
app/Controllers/Admin/BackupController.php- Fixed all translation keys
Backup Download Pattern
- Fix: Updated regex pattern to accept complete archives
- Problem: Download failed for complete archives (filename starts with
flatboard-complete-) - Solution: Extended regex pattern to accept both
backup_*andflatboard-complete-*filenames - Files Modified:
app/Controllers/Admin/BackupController.php- Updated download validation
Theme Assets in Backups
- Fix: Include
themes/assetsfolder andindex.htmlin theme backups - Problem: Theme assets and root
index.htmlwere not included in backups - Solution:
- Explicitly include
themes/assetswhen themes option is selected - Enhanced
addDirectoryToZip()to handle root files correctly
- Explicitly include
- Files Modified:
app/Controllers/Admin/BackupController.php- Enhanced backup creation
Post Scrolling After Creation
- Fix: Improved scroll to post functionality with retry mechanism
- Problem: Posts were not scrolling into view after creation, especially on paginated pages
- Solution:
- Added retry mechanism (up to 10 attempts with 200ms delay)
- Force load all posts if target post not found
- Use
MutationObserverto detect when post is added to DOM - Increased timeout for post detection to 10 seconds
- Files Modified:
app/Views/discussions/show.php- EnhancedscrollToPost()functionthemes/premium/views/discussions/show.php- Same enhancements
URL Routing and JavaScript URL Support (Thanks Arpinux)
- Fix: Improved URL handling for domain/subdomain/subfolder installations
- Problem: 404 errors on
/forum/forumsand JavaScript URL generation issues - Solution:
- Enhanced
UrlHelper::detectBaseUrlStatic()with better route detection - Improved
Request::getUrl()normalization - Enhanced
window.url()JavaScript helper for API paths - Better handling of known routes (plugins, themes, uploads, assets)
- Enhanced
- Files Modified:
app/Helpers/UrlHelper.php- Enhanced base URL detectionapp/Core/Request.php- Improved URL normalizationapp/Views/components/translations.php- Enhancedwindow.url()helper
Toast Notification Translations
- Fix: Toast notifications now display correct translated messages
- Problem: Toast messages showed empty or fallback text instead of translations
- Solution:
- Added translated message to JSON response from
PostController - Fixed translation key structure in JavaScript
- Increased toast display duration from 500ms to 1500ms
- Added translated message to JSON response from
- Files Modified:
app/Controllers/Discussion/PostController.php- Added message to JSON responseapp/Views/discussions/show.php- Fixed translation handlingthemes/premium/views/discussions/show.php- Same fixes
Form Field Translation System
- Fix: Enhanced form field translation with hierarchical keys and cascade fallback
- Problem: Form fields displayed translation keys instead of translated text
- Solution:
- Implemented hierarchical translation key structure
- Added cascade fallback: hierarchical key → direct global key → direct local key → default
- Support for both hierarchical and flat translation structures
- Empty string returned for missing
form_config.*placeholders
- Files Modified:
app/Helpers/FormFieldHelper.php- Enhanced translation systemplugins/EasyMDE/langs/en.json- Added placeholder translationsplugins/EasyMDE/langs/fr.json- Added placeholder translations
📦 Plugin & Theme Updates
ResourceManager Plugin
- Enhancement: Hierarchical translation key structure
- Details:
- Reorganized translation keys by view (admin, frontend, common)
- Updated all views to use hierarchical keys via
Translator::trans() - Removed duplicate keys
- Improved translation loading in plugin boot
All Plugins & Themes
- Enhancement: Migrated to hierarchical translation structure
- Enhancement: Removed duplicate
settingssections - Enhancement: Added
hide_color_settingssupport for themes - Enhancement: Improved form field translation system
🔒 Security Improvements
CSRF Protection
- Enhancement: All backup operations protected with CSRF middleware
- Enhancement: Reputation recalculation protected with admin permission check (plugin in developement)
File Validation
- Enhancement: Enhanced backup filename validation with regex pattern
- Enhancement: Path traversal protection with
basename()usage
🧪 Testing Recommendations
Manual Testing Checklist
-
Complete Archive Creation:
- Create a complete archive
- Verify all files are included
- Restore the archive and verify everything works
-
Reputation Recalculation (plugin in developement):
- Trigger reputation recalculation
- Verify points are recalculated correctly
- Check history entries are created
- Verify badges are updated
-
Dynamic Post Updates:
- Create a new post - verify it appears instantly
- Edit a post - verify it updates in place
- Delete a post - verify animation and removal
-
Mentions:
- Type
@in reply form - Verify autocomplete appears
- Select a user and verify mention is inserted
- Type
-
Reactions:
- Add a reaction - verify instant update
- Remove a reaction - verify instant update
- Check modal updates correctly
-
Smart Preloading:
- Scroll near "Load More" button
- Check DevTools Network tab for prefetch requests
- Verify instant loading when clicking
-
Backup & Restore:
- Create a backup with all options
- Restore the backup
- Verify all settings, permissions, and data are restored
🔄 Migration Notes
For Developers
- All changes are 100% backward compatible
- Existing plugins and themes work without modification
- Optional: Migrate to hierarchical translation keys for better organization
- Optional: Use
getOrSet()instead ofremember()for clarity
For Administrators
- No action required - all improvements are automatic
- New features available in Admin Panel:
- Complete archive creation in Backups section
- Reputation recalculation in Reputation section (plugin in developement)
🙏 Acknowledgments
This release candidate represents months of development, testing, and refinement. Special thanks to all contributors and testers who helped identify issues and improve the platform.
📝 Notes
- All features are production-ready
- All changes are backward compatible
- No breaking changes
- No external dependencies added
- Full documentation provided
Next Steps:
- Continue testing in production-like environments
- Collect user feedback
- Monitor performance metrics
- Prepare for stable release
Flatboard 5 Changelog - Release Candidate 8b (RC8b)
Release Date: January 5, 2026
Version 5.0.0-rc.8b
📋 Executive Summary
This release focuses on security improvements, performance optimizations, code quality enhancements, GDPR compliance, and bug fixes:
- 🔴 Critical Security Fix: Fixed race condition vulnerability in RateLimiter using file locking
- 🔴 Critical Security Fix: Fixed XML injection vulnerabilities in SitemapService and XSS in SearchService
- 🔴 Critical Security Fix: Fixed SSRF vulnerability in WebhookService with URL validation and private IP blocking
- ⚡ Performance: Added caching layers in Visitor model (bot detection, presence data)
- ⚡ Performance: Fixed catastrophic N+1 query problems in SearchService (1000+ queries → few queries)
- ⚡ Performance: Fixed timeout issues in RateLimiter and Cache using non-blocking locks with retry
- 🔄 Async Processing: Implemented asynchronous webhook queue system with retry logic and exponential backoff
- 📊 Monitoring: Added webhook history tracking and admin interface for webhook management
- 🔒 GDPR Compliance: Implemented complete user data export system with ZIP archive generation and email delivery
- 🛠️ Admin Tools: Added cache clearing tool to maintenance dashboard for easy cache management
- 🐛 Bug Fixes: Fixed post navigation, download service syntax error, API post retrieval
- 🛡️ Reliability: Enhanced error handling and logging throughout the codebase
- 📚 Documentation: Comprehensive PHPDoc documentation in French for all core classes
- 🔌 Plugin System: Universal hook
router.plugins.registerfor plugin route registration - ✅ Validation: Enhanced Validator class with 38 validation rules and Laravel-style syntax
- 🔐 Session Management: Improved secure session handling with fingerprinting and timeout management
- 📝 Logging: Added configurable minimum log level to reduce verbosity
Key Files Modified: RateLimiter.php, Visitor.php, DownloadService.php, show.php, PostApiController.php, App.php, Router.php, Session.php, Validator.php, Request.php, Response.php, Sanitizer.php, SearchService.php, SitemapService.php, WebhookService.php, WebhookQueue.php, WebhookHistory.php, Cache.php, Logger.php, MaintenanceController.php, dashboard.php, and all plugin files
🔒 Security Improvements
Sitemap Service (app/Services/SitemapService.php)
- 🔴 Critical: Fixed XML injection vulnerability: Replaced manual XML string construction with
XMLWriterto properly escape all values- All XML values (
loc,lastmod,changefreq,priority) are now properly escaped usingXMLWriter - Prevents injection of malicious XML/HTML code through user-controlled data
htmlspecialchars()was only applied to$loc, leaving$changefreqand$priorityvulnerable
- All XML values (
- 🔴 Critical: Fixed path traversal vulnerability in
saveToFile(): Added strict path validation to prevent writing files outside allowed directory- Validates filename format (must match
/^sitemap[a-z0-9_-]*\.xml$/i) - Ensures file is written only in
BASE_PATH/public/directory - Uses
realpath()to resolve symlinks and prevent directory traversal attacks - Throws security exception if path validation fails
- Validates filename format (must match
- 🟡 Important: Added rate limiting: Prevents DoS attacks by limiting sitemap generation to 5 requests per hour per IP
- Uses native
RateLimiterclass with keysitemap_generation - Throws
RuntimeExceptionwhen rate limit exceeded - Logs rate limit violations for monitoring
- Uses native
- 🟡 Important: Reduced excessive logging: Changed verbose
infologs todebuglevel- Only critical events logged at
infolevel - Prevents log flooding in production environments
- Better log management and performance
- Only critical events logged at
Search Service (app/Services/SearchService.php)
- 🔴 Critical: Fixed XSS vulnerability in excerpt highlighting: All content is now properly escaped before highlighting keywords
extractExcerpt()now escapes content withhtmlspecialchars()before inserting<mark>tags- Keywords are escaped before being used in regex replacement
- Prevents injection of malicious HTML/JavaScript through search keywords
- Attack vector: searching for
<script>alert('xss')</script>would previously inject code
- 🟡 Important: Removed email from user search: Email addresses are no longer searchable for privacy protection
- Search now only matches against
usernameandbiofields - Prevents exposure of email addresses through search functionality
- Better privacy compliance (GDPR-friendly)
- Search now only matches against
- 🟡 Important: Added rate limiting: Prevents DoS attacks by limiting searches to 30 per minute per IP
- Uses native
RateLimiterclass with keysearch - Throws
RuntimeExceptionwhen rate limit exceeded - Logs rate limit violations for monitoring
- Uses native
⚡ Performance & Reliability Improvements
Sitemap Service (app/Services/SitemapService.php)
- 🔴 Critical: Fixed N+1 query problem: Preloads all categories in batch instead of querying per discussion
- Uses
getCategoriesByIds()method when available (SqliteStorage, JsonStorage) - Fallback to batch loading all categories if method not available
- Performance improvement: 1000 discussions = 1 query instead of 1000 queries
- Uses
- 🟡 Important: Added aggressive caching: Sitemap generation cached for 1 hour (3600 seconds)
- Uses native
Cache::getOrSet()with tag['sitemap']for easy invalidation - Cache key includes user ID for permission-based caching
invalidateCache()method available for manual cache invalidation- Reduces server load for frequently accessed sitemaps
- Uses native
- 🟡 Important: Replaced manual XML construction with XMLWriter: Improved maintainability and security
- XML generation now uses
XMLWriterclass for proper XML structure - Automatic indentation and proper escaping
- Better error handling with XML validation before returning
- Code split into focused methods:
addHomePage(),addDiscussions(),addCategories(),addTags()
- XML generation now uses
- 🟡 Important: Extracted duplicate permission checking logic: Created
isPublicCategory()method- Eliminated 3 identical code blocks for checking category permissions
- Centralized logic for determining if category is publicly accessible
- Better maintainability and consistency
- 🟡 Important: Improved error handling: Removed
@operator and added proper try-catch blockssaveToFile()now usesLOCK_EXflag for atomic file writes- Better error messages with context (file path, error details)
- Proper exception handling instead of silent failures
- 🟡 Important: Added HTTP headers in
serve()method: Proper Content-Type, Cache-Control, and Last-Modified headersContent-Type: application/xml; charset=UTF-8Cache-Control: public, max-age=3600(1 hour cache)X-Content-Type-Options: nosnifffor securityLast-Modifiedheader for better caching support
Search Service (app/Services/SearchService.php)
- 🔴 Critical: Fixed catastrophic N+1 query problem in
searchPostsInternal(): Loads all posts in batch instead of querying per discussion- Before:
Post::byDiscussion()called in loop = 1000+ database queries - After: All posts loaded in single pass = few queries total
- Performance improvement: 1000 discussions × 100 posts = 100,000+ queries → few queries
- Prevents search timeouts and server crashes on large sites
- Before:
- 🔴 Critical: Fixed N+1 queries in result formatting: Preloads categories and users in batch
- Uses
Category::findMany()andUser::findMany()to load all entities at once formatDiscussionResult()andformatPostResult()now accept preloaded entities- Performance improvement: 20 results = 40 queries → 2-3 queries
- Uses
- 🟡 Important: Added result caching: Search results cached for 5 minutes (300 seconds)
- Uses native
Cache::getOrSet()with tag['search']for easy invalidation - Cache key includes query, type, filters, and user ID for permission-based caching
invalidateCache()method available for manual cache invalidation- Reduces server load for repeated searches
- Uses native
- 🟡 Important: Optimized string operations in loops: Pre-lowercase query and keywords before loops
mb_strtolower()called once before loops instead of in every iteration- Reduces CPU usage in tight loops
- Better performance for large result sets
- 🟡 Important: Improved relevance scoring algorithm: Enhanced scoring with better weighting
- Exact match in title: +100 points (was +10)
- Title starts with query: +50 points (new)
- Title contains query: +25 points (was +10)
- Content contains query: +10 points (was +5)
- Keyword matching: Better weighting (title matches worth 10x content matches)
- Recency boost: Newer content gets bonus points (decays over 30 days)
- Better search result relevance and user experience
- 🟡 Important: Improved keyword extraction: Added stop word removal for better search accuracy
- Removes common words (le, la, the, a, etc.) that don't add search value
- Stop words list includes French and English common words
- Filters words shorter than 2 characters
- Better search precision and performance
- 🟡 Important: Intelligent loading limits: Limits discussion/user loading to prevent memory exhaustion
$maxDiscussions = min(1000, $limit * 10)to avoid loading entire database- Better memory management for large sites
- Prevents PHP memory limit errors
🔧 Code Quality Improvements
Webhook Service (app/Services/WebhookService.php)
- 🟡 Important: Refactored to instance-based architecture: Converted from static methods to singleton pattern
- Instance methods for dependency injection and testability
- Static methods maintained for backward compatibility
- Better code organization and maintainability
- Uses native Flatboard tools:
Config,Logger,RateLimiter
- 🟡 Important: Improved error handling: Comprehensive try-catch blocks with proper logging
- All operations wrapped in error handling
- Detailed error messages with context
- Graceful degradation on failures
- Duration tracking for performance monitoring
- Code quality:
- Comprehensive PHPDoc documentation in French
- All methods fully documented with parameter descriptions
- Proper exception handling throughout
- Uses only native Flatboard tools (no external dependencies)
Sitemap Service (app/Services/SitemapService.php)
- 🟡 Important: Refactored monolithic
generate()method: Split into focused methodsbuildSitemap(): Main generation logicaddHomePage(),addDiscussions(),addCategories(),addTags(): Focused methodswriteUrl(): Centralized URL writing with proper escapingisPublicCategory(): Extracted duplicate permission checkingloadCategoriesByIds(): Batch category loading with fallback- Better code organization and maintainability
- 🟡 Important: Added XML validation: Validates generated XML before returning
- Uses
simplexml_load_string()to verify XML is well-formed - Logs validation errors with details
- Throws exception if invalid XML generated
- Uses
- Code quality:
- Comprehensive PHPDoc documentation in French
- All methods fully documented with parameter descriptions
- Better error messages with context
- Proper exception handling throughout
Search Service (app/Services/SearchService.php)
- 🟡 Important: Separated caching from search logic: Created
performSearch()methodsearch(): Public method with caching and rate limitingperformSearch(): Internal method without cache (for testing/debugging)- Better separation of concerns
- 🟡 Important: Improved method signatures: Preloaded entities passed to formatters
formatDiscussionResult()accepts preloaded$categoryand$authorformatPostResult()accepts preloaded$discussion,$category, and$author- Fallback to individual queries if entities not provided (backward compatibility)
- Better performance and flexibility
- Code quality:
- Comprehensive PHPDoc documentation in French
- All methods fully documented with parameter descriptions
- Better error messages with context
- Proper exception handling throughout
- Uses only native Flatboard tools (no external dependencies)
🔒 Security Improvements
Email Service (app/Services/EmailService.php)
- 🟡 Important: SSL Certificate Verification Configuration: Added
smtp.verify_sslconfiguration option for SMTP connections- Default:
false(maintains backward compatibility with existing installations) - Can be enabled explicitly via
smtp.verify_ssl = truefor enhanced security - Automatic detection of development environment (localhost, 127.0.0.1, ::1)
- Info logged when SSL verification is disabled in production (with suggestion to enable it)
- Note: For maximum security, enable
verify_ssl: truein production environments
- Default:
- 🟡 Important: Improved Error Handling for SMTP Connection Issues: Enhanced error messages and diagnostics for SMTP connection failures
- Automatic detection of SSL/certificate errors
- Helpful suggestions in error messages (e.g., disable
verify_sslfor self-signed certificates) - Better logging with context (host, port, encryption, verify_ssl status)
- More informative error messages in test email responses
- 🔴 Critical: Email Address Validation: Added validation to prevent email injection attacks
validateEmail()method checks for valid email format and prevents header injection (\r\n)- All email addresses are validated before sending
- 🟡 Important: Sensitive Data Masking in Logs: Email addresses are now masked in logs for security
maskEmail()method masks email addresses (e.g.,u***@example.com)- Applied to all log entries containing email addresses
- Prevents exposure of sensitive information in logs
- 🟡 Important: Removed Error Suppression: Removed
@operator frommail()calls to allow proper error logging- Errors are now properly logged with
error_get_last()information - Better debugging capabilities for email sending issues
- Errors are now properly logged with
🔒 Security Improvements
Notification Service (app/Services/NotificationService.php)
- 🔴 Critical: Fixed XSS vulnerability in email templates: Fixed critical security issue where
$titleand$linkwere not properly escaped inbuildEmailBody()- All variables in email templates are now escaped with
htmlspecialchars($value, ENT_QUOTES, 'UTF-8') - Links in HTML attributes now use double quotes with proper escaping
- Added comprehensive input validation after plugin hooks to prevent injection
- All variables in email templates are now escaped with
- 🟡 Important: Added input validation after plugin hooks: Added validation methods (
validateUserId(),validateNotificationType(),sanitizeString()) to prevent malicious data injection through plugin hooks - 🟡 Important: Improved link validation: Enhanced link normalization to prevent open redirect vulnerabilities by validating against allowed hosts
- 🟡 Important: Email masking in logs: Added
maskEmail()method to mask email addresses in logs (e.g.,u***@example.com) for privacy protection
⚡ Performance & Reliability Improvements
Webhook Service (app/Services/WebhookService.php)
- 🟡 Important: Implemented asynchronous queue system: Webhooks are now processed asynchronously to avoid blocking user requests
- New
WebhookQueueclass for managing webhook jobs - Webhooks are enqueued immediately and processed by background worker
- Prevents user request blocking during webhook delivery
- Better user experience and server responsiveness
- New
- 🟡 Important: Added retry logic with exponential backoff: Automatic retry for failed webhooks
- Maximum 3 attempts per webhook
- Exponential backoff: 1s, 2s, 4s delays (max 60s)
- Failed webhooks are automatically rescheduled
- Prevents permanent loss of webhook deliveries
- 🟡 Important: Added webhook history tracking: Complete audit trail of all webhook attempts
- New
WebhookHistoryclass for storing webhook delivery records - Records success/failure, HTTP codes, duration, errors, and attempt count
- 30-day retention by default (configurable)
- Statistics: total, success, failures, average duration, by event type
- Enables debugging and monitoring of webhook delivery
- New
- 🟡 Important: Support for multiple webhooks: Can send same event to multiple endpoints
- New configuration format:
webhooks.urls(array of webhook configs) - Backward compatible with old
webhooks.urlformat - Per-webhook event filtering (send only specific events to each webhook)
- Per-webhook secret configuration
- New configuration format:
- 🟡 Important: Configurable timeouts: Webhook timeouts can be configured
webhooks.timeout: Request timeout (default: 10s)webhooks.connect_timeout: Connection timeout (default: 5s)- Configurable via
config.json
🔒 GDPR Compliance & Data Export
Export Service (app/Services/ExportService.php)
- 🟢 New: Complete GDPR-compliant user data export system: Users can now request and download their personal data
- New
ExportServiceclass for secure user data export - Access control: Users can only export their own data (admins can export any user's data)
- Rate limiting: 1 export per hour per user to prevent abuse
- Data sanitization: Automatically removes sensitive fields (passwords, tokens, IPs, etc.)
- Complete data export: User profile, discussions, posts, subscriptions, notifications, reactions, bookmarks, drafts, settings
- Retention policy filtering: Filters data based on
gdpr.retention_daysconfiguration (default: 365 days) - Audit logging: All export requests are logged with user ID, requester ID, timestamp, IP, and user agent
- Export file generation: Creates ZIP archives with user data in JSON format
- Secure token-based download: 64-character hexadecimal tokens valid for 24 hours
- Automatic file cleanup: Export files are automatically deleted after 24 hours
- Email notification: Users receive an email with download link when export is ready
- Atomic file operations: Uses
AtomicFileHelperfor thread-safe file writing
- New
- 🟢 New: User profile export button: Added "Export Data" button in user profile
- Modal dialog with user avatar and clear instructions
- Asynchronous request processing with loading states
- Success/error message display
- Fully translated interface (French and English)
- 🟢 New: Export download controller: New
ExportControllerfor secure file downloads- Token validation and expiration checking
- Access control verification
- Secure file delivery with proper headers
- 🟡 Important: Installation configuration: Added GDPR settings to default configuration
gdpr.retention_days: Data retention period in days (default: 365)- Added to
install.phpfor new installations - Export directories created automatically:
/stockage/exportsand/stockage/json/export_logs
- 🟡 Important: Performance optimization: Fixed N+N query problems in export
- Loads all discussions once, then filters in memory
- Only loads posts for user's discussions
- Reduces queries from 10,000+ to ~100 maximum
- 🟡 Important: Custom headers support: Can add custom headers to webhook requests
- Configuration option:
webhooks.custom_headers - Merged with default headers (Content-Type, X-Webhook-Event, etc.)
- Useful for API authentication or custom requirements
- Configuration option:
🛠️ Admin Dashboard & Maintenance Tools
Maintenance Dashboard (app/Controllers/Admin/MaintenanceController.php, app/Views/admin/dashboard.php)
- 🟢 New: Cache clearing tool: Added cache cleanup functionality to maintenance dashboard
- New "Clear Cache" tool in Admin > Maintenance dashboard
- Deletes all system cache files (translations, rate limits, etc.) from
stockage/cache/ - Displays statistics: number of files deleted, total size freed, error count
- Automatic cache regeneration: cache files are automatically recreated on next request
- Useful for testing (resets rate limits, clears translation cache) and troubleshooting
- Confirmation dialog before execution to prevent accidental deletions
- Proper error handling and logging for audit trail
- Fully translated interface (French and English)
- Route:
POST /admin/maintenance/clear-cache(CSRF protected, admin only)
Webhook Queue (app/Services/WebhookQueue.php)
- 🟡 Important: Job queue management: Complete queue system for webhook processing
- Jobs stored in JSON files in
stockage/json/webhooks/queue/ - Status tracking:
pending,processing,completed,failed - Automatic retry scheduling with exponential backoff
- Job cleanup after 7 days (configurable)
- Queue statistics: pending, processing, completed, failed counts
- Jobs stored in JSON files in
Webhook History (app/Services/WebhookHistory.php)
- 🟡 Important: Historical record keeping: Complete audit trail of webhook deliveries
- Records stored in JSON files in
stockage/json/webhooks/history/ - Pagination support for large histories
- Filtering by event, success status, URL, date range
- Statistics generation (total, success, failures, average duration, by event)
- Automatic cleanup of old records (30 days default)
- Records stored in JSON files in
Webhook Worker (app/Cli/Commands/WebhookWorkerCommand.php)
- 🟡 Important: CLI worker for processing webhooks: Background worker for async webhook delivery
- Command:
php app/Cli/console.php webhook:process [limit] [max_time] - Processes pending webhooks from queue
- Configurable batch size and execution time limit
- Statistics reporting after processing
- Commands:
webhook:process,webhook:cleanup,webhook:stats
- Command:
Webhook Admin Interface (app/Controllers/Admin/WebhookController.php, app/Views/admin/webhooks.php)
- 🟡 Important: Complete admin interface for webhook management: User-friendly interface for configuring and monitoring webhooks
- Webhook configuration: add, edit, delete webhooks
- Event filtering per webhook
- Webhook testing functionality
- Real-time statistics dashboard
- History viewer with filtering and pagination
- Accessible via Admin > Webhooks menu
Rate Limiter (app/Core/RateLimiter.php)
- 🟡 Important: Fixed timeout issues with non-blocking locks: Replaced blocking
flock()with non-blocking retry mechanism- Uses
LOCK_EX | LOCK_NB(non-blocking) with 10 retries at 10ms intervals - Maximum wait time: 100ms instead of 30 seconds
- Fail open behavior: if lock cannot be acquired, request is allowed (prevents deadlocks)
- Prevents "Maximum execution time exceeded" errors during high concurrency
- Better performance and reliability under load
- Applied to
check()method for rate limit verification
- Uses
Cache (app/Core/Cache.php)
- 🟡 Important: Fixed timeout issues in
storeKey(): Replaced blockingflock()with non-blocking retry mechanism- Uses
LOCK_EX | LOCK_NB(non-blocking) with 5 retries at 5ms intervals - Maximum wait time: 25ms instead of blocking indefinitely
- Graceful degradation: if lock cannot be acquired, operation is logged and skipped
- Prevents cache operations from blocking other requests
- Better performance and reliability under load
- Uses
Search Service (app/Services/SearchService.php)
- 🟡 Important: Improved rate limiter error handling: Enhanced graceful degradation for rate limiting failures
- No longer throws exceptions on rate limiter errors
- Logs warnings and continues search execution
- Prevents search blocking due to rate limiter timeouts
- Better user experience during high load
Logger (app/Core/Logger.php)
- 🟡 Important: Added configurable minimum log level: Reduces log verbosity in production
- New configuration option:
log_min_levelinconfig.json - Supports all PSR-3 log levels:
debug,info,notice,warning,error,critical,alert,emergency - Can be configured via
config.jsonor PHP constantLOG_MIN_LEVEL - Filters logs below minimum level before writing
- Default:
null(logs everything, backward compatible) - Recommended production setting:
"warning"or"error" - Methods:
setMinLogLevel(),getMinLogLevel() - Prevents log flooding in production environments
- New configuration option:
Atomic File Operations (app/Core/AtomicFileHelper.php, app/Models/Visitor.php, app/Core/RateLimiter.php, app/Services/PresenceService.php)
- 🟡 Important: Migrated file operations to AtomicFileHelper: Replaced direct
file_get_contents()calls withAtomicFileHelper::readAtomic()for thread-safe file operations- Visitor.php: Replaced 2 instances of
file_get_contents()for readinguser_presence.jsonwith atomic operations - RateLimiter.php: Replaced 2 instances of
file_get_contents()ingetRetryAfter()andclearAllLoginRateLimits()with atomic operations - PresenceService.php: Replaced
file_get_contents()for readinguser_presence.jsonwith atomic operations - All file reads now use proper file locking to prevent race conditions
- Improved data consistency in multi-process environments (PHP-FPM)
- Better error handling and logging for file operations
- Visitor.php: Replaced 2 instances of
🔧 Code Quality Improvements
Notification Service (app/Services/NotificationService.php)
- 🟡 Important: Extracted URL generation to eliminate code duplication: Created
generatePostLink()method to centralize URL generation logic- Eliminated 150+ lines of duplicated code across
notifyNewReply(),notifyMention(), andnotifyReaction() - Improved maintainability: URL generation logic is now in a single place
- Enhanced error handling: Added detailed logging when URL generation fails
- Better error messages: Each method now logs specific missing data when notification cannot be created
- Reduced code complexity: Methods are now more focused and easier to understand
- Eliminated 150+ lines of duplicated code across
- 🟡 Important: Email templates moved to separate file: Created
app/Views/emails/notification.phptemplate file- Email HTML is now in a separate, maintainable template file instead of hard-coded strings
- Template can be customized by themes (via ViewHelper pattern)
- Fallback to inline template for backward compatibility
- Better separation of concerns: presentation logic separated from business logic
- 🟡 Important: Optimized notifyModerators() to reduce N+1 queries: Improved moderator retrieval logic
- Load all users once instead of multiple queries
- Filter moderators in a single pass with optimized GroupHelper calls (uses internal cache)
- Added logging for better debugging when no moderators are found
- Better error handling and edge case management
🔒 Security Fixes
Presence Service (app/Services/PresenceService.php)
- 🟡 Important: Fixed N+1 query problem: Optimized
getActiveUsersDetailed()to load all users in a single query- Collects all active user IDs first, then loads all users at once using
User::all() - Filters users in memory instead of making N separate
User::find()calls - Performance improvement: 100 active users = 1 query instead of 100 queries
- Collects all active user IDs first, then loads all users at once using
- 🟡 Important: Added input validation: Added
validateMinutes()method to validate time parameters- Validates that minutes are between 1 and 1440 (max 24 hours)
- Prevents negative values and extremely large values that could cause performance issues
- Applied to all methods that accept
$minutesparameter
- 🟡 Important: Improved error handling: Enhanced file operation error handling
- Wrapped
AtomicFileHelper::readAtomic()in try-catch block - Logs errors with context (file path, error message, stack trace)
- Returns empty array on error instead of failing silently
- Wrapped
- 🟡 Important: Extracted duplicate sorting logic: Created
sortByLastActivity()private method- Eliminates code duplication (was repeated 4 times)
- Centralized sorting logic for easier maintenance
- Consistent sorting behavior across all methods
- 🟡 Important: Added error logging for missing users: Logs warnings when users in presence file are not found
- Replaces silent failure with proper logging
- Helps identify data consistency issues
- Includes user ID and presence data in log context
- 🟡 Important: Optimized statistics calculation: Improved
getPresenceStats()performance- Uses
array_column()andarray_count_values()instead of manual loops - More efficient counting of page types
- Uses
- 🟡 Important: Added URL sanitization: Sanitizes page URLs in
getPresenceByPage()usingSanitizer::sanitizeUrl()- Prevents potential security issues with user-controlled URLs
- Uses native Flatboard sanitization method
Permission Helper (app/Helpers/PermissionHelper.php)
- 🟡 Important: Restricted real-time presence access: Limited "Voir les activités en temps réel" (
presence.view) permission to administrators and moderators only during installation- Previously granted to all groups (administrators, moderators, members, guests)
- Now only administrators and moderators have this permission by default
- Improves privacy by restricting real-time user activity visibility to staff members only
- Applies to new installations only (existing installations are not affected)
- 🟡 Important: Restricted profile viewing access: Excluded guest group from "Voir les profils" (
profile.view) permission during installation- Previously granted to all groups (administrators, moderators, members, guests)
- Now only administrators, moderators, and members have this permission by default
- Guests no longer have access to user profiles by default, improving privacy
- Applies to new installations only (existing installations are not affected)
URL Helper (app/Helpers/UrlHelper.php) & Request (app/Core/Request.php)
- 🟡 Important: Fixed subdirectory installation routing: Improved base URL handling for installations in subdirectories
- Fixed 404 errors when accessing routes like
/forumsin subdirectory installations (e.g.,/forum/forums) - Normalized base_url storage (removed leading/trailing slashes) for consistency
- Enhanced
Request::removeBasePath()to correctly strip base path from request URLs - Improved
UrlHelper::to()to properly construct URLs with base path - Base URL is now normalized during installation to prevent future issues
- Ensures proper route matching and URL generation in subdirectory installations
- Fixed 404 errors when accessing routes like
RSS Service (app/Services/RssService.php)
- 🔴 Critical: Fixed XSS vulnerability in XML generation: Replaced manual string concatenation with
XMLWriterfor secure XML generation- All XML content is now properly escaped using
XMLWriter(automatic escaping) - Prevents XML injection attacks and XSS vulnerabilities
- Proper handling of special XML characters (
&,<,>,',") - Support for XML namespaces (Atom, Dublin Core)
- All XML content is now properly escaped using
- 🟡 Important: Added rate limiting for RSS feeds: Implemented rate limiting to prevent DoS attacks
- Limit: 10 requests per minute per IP address
- Returns HTTP 429 with
Retry-Afterheader when limit exceeded - Logs rate limit violations for monitoring
- 🟡 Important: Added aggressive caching: Implemented 15-minute cache for RSS feeds
- Reduces database load and improves response times
- Separate cache keys for different feed types and pages
- Cache automatically invalidates after TTL expires
- 🟡 Important: Fixed N+1 query problem: Optimized first post retrieval using batch loading
- Loads all first posts in a single batch operation
- Performance improvement: 20 discussions = 1 query instead of 20 queries
- 🟡 Important: Enhanced RSS feed filtering: Improved security for category filtering
- Explicit verification that only public categories are included
- Logging of private categories excluded from feeds
- Better error handling for missing or invalid categories
- 🟡 Important: Added standard RSS elements: Enhanced RSS feeds with recommended elements
language,lastBuildDate,generator,ttlelements- Atom self-link for feed discovery
- Dublin Core namespace support (
dc:creatorfor authors) - Category and tag information in items
- 🟡 Important: Improved content truncation: Content now truncates at word boundaries
- Prevents cutting in the middle of words
- Adds
...indicator when content is truncated - Configurable maximum length (default: 500 characters)
- 🟡 Important: Added feed type filtering: Implemented support for filtered feeds
all: All public discussions (default)category: Discussions from a specific categoryuser: Discussions by a specific usertag: Discussions with a specific tag- Custom descriptions for each feed type
- 🟡 Important: Added complete Atom 1.0 support: Full implementation of Atom 1.0 feed format
- Compliant with Atom 1.0 specification
- Separate
generateAtomFeed()method - Proper Atom elements:
feed,entry,author,content,summary,category - HTML content with CDATA for Markdown content
- Atom link elements for navigation (
self,alternate,prev,next)
- 🟡 Important: Added media enclosures: Support for images and attachments in RSS/Atom feeds
- Automatic extraction of images from Markdown content
- Support for post attachments (files)
- RSS enclosures with
url,type, andlengthattributes - Atom enclosures via
<link rel="enclosure">elements - Automatic MIME type detection
- Conversion of relative URLs to absolute URLs
- 🟡 Important: Added pagination support: Implemented pagination for large feeds
pageparameter for navigating through feed pages- Atom pagination links (
prev,next) in both RSS and Atom feeds - Separate cache per page for optimal performance
- Support for pagination in all feed types (all, category, user, tag)
RSS Controller (app/Controllers/Seo/RssController.php)
- 🟡 Important: Enhanced RSS controller with new routes: Added support for filtered feeds and Atom format
- Routes for category feeds:
/rss/category/{id}and/atom/category/{id} - Routes for user feeds:
/rss/user/{id}and/atom/user/{id} - Routes for tag feeds:
/rss/tag/{id}and/atom/tag/{id} - Support for pagination via
?page=Nquery parameter - Proper error handling with HTTP 400 status for invalid requests
- Added
X-Content-Type-Options: nosniffheader for security
- Routes for category feeds:
Client-Side Storage (app/Views/components/translations.php)
- 🟡 Important: Fixed localStorage conflicts between multiple forums: Implemented automatic namespace isolation for localStorage
- Resolves conflicts when multiple Flatboard forums are installed on the same domain
- Automatic namespace generation based on unique hash of site name, base URL, and site URL
- Uses JavaScript Proxy to transparently prefix all localStorage keys with unique identifier
- Automatic migration of existing localStorage keys to namespaced versions
- No code changes required in existing JavaScript files
- Prevents data corruption, editor conflicts, and login issues when accessing multiple forums
- Each forum now has isolated localStorage space:
fb_{unique_hash}_ - Fallback wrapper (
FlatboardStorage) for browsers that don't support Proxy
Upload Service (app/Services/UploadService.php)
- 🔴 CRITICAL: Fixed command injection vulnerability: Replaced
shell_exec()withproc_open()for all external commands (unrar, 7z, clamscan)- Added whitelist of allowed executable paths
- Validates executable paths before execution
- Uses
proc_open()with proper file descriptors for safer command execution - Prevents arbitrary command execution through malicious filenames
- 🔴 CRITICAL: Added path traversal protection: Uses native
Sanitizer::sanitizePath()method- Leverages Flatboard's built-in path sanitization with comprehensive path traversal prevention
- Validates that final path is within
BASE_PATHusingrealpath() - Logs path traversal attempts for security monitoring
- Uses proven, tested sanitization logic from core framework (no code duplication)
- 🔴 CRITICAL: Removed client MIME type fallback: Files are now rejected if MIME type cannot be determined
- No longer trusts client-provided MIME types (
$file['type']) - Requires
finfo_file()ormime_content_type()to successfully detect MIME type - Logs rejection attempts for security monitoring
- No longer trusts client-provided MIME types (
- 🔴 CRITICAL: Removed extension-based security bypass: Files are no longer accepted based solely on extension
- Extension can be easily spoofed (e.g.,
malware.exerenamed tophoto.jpg) - Exception only for images with magic byte verification
- Added
verifyMagicBytes()method to validate file signatures - Supports JPEG, PNG, GIF, WebP, PDF, ZIP, RAR signatures
- Extension can be easily spoofed (e.g.,
- 🟡 HIGH: Added magic byte verification: Implemented file signature validation
- Verifies actual file content matches declared extension
- Prevents file type spoofing attacks
- Supports common image formats, PDF, and archive formats
- Special handling for WebP format (RIFF + WEBP check)
- 🟡 HIGH: Fixed filename collision handling: Prevents silent file overwrites
- Generates unique filenames with collision detection
- Retries up to 100 times to find unique filename
- Adds numeric suffix for custom filenames on collision
- Logs errors if max attempts reached
🔧 Code Quality Improvements
User Profile Links (app/Views/users/_user_card.php, app/Views/users/_visitor_card.php, app/Views/components/presence-item.php, app/Views/components/visitors-list.php, themes/premium/views/users/_user_card.php)
- 🟡 Important: Added permission check for profile links: Profile links and buttons are now conditionally displayed based on
profile.viewpermission.- Links to user profiles in user cards, visitor cards, presence items, and visitors list are only shown if the user has permission.
- If permission is denied, only the username is displayed (without link) and the "View profile" button is hidden.
- Applied to both default and premium theme views.
- Improves privacy by preventing unauthorized access to profile pages.
Visitors List Component (app/Views/components/visitors-list.php)
- 🟡 Important: Fixed hardcoded translations: Replaced all hardcoded French text with translation keys.
- Added translation keys:
presence.activeVisitors,presence.activeVisitorsCount,presence.noActiveVisitors,presence.anonymousVisitor. - Component now fully supports internationalization.
- All text adapts to user's language preference.
- Added translation keys:
🐛 Bug Fixes
Notification Service (app/Services/NotificationService.php)
- Fixed notifications not translated to user's language: Fixed critical issue where notifications were created using the language of the session (notification creator) instead of the recipient's language
- Added
getUserLanguage()method to retrieve the recipient user's language preference - Modified
notify()to temporarily switch to recipient's language when creating notifications - Modified
notifyModerators()to translate notifications in each moderator's language - Notifications are now properly translated in the recipient's preferred language (user preference → site language → fallback to 'fr')
- Language is properly restored after notification creation to avoid affecting other operations
- Added
- Fixed excessive logging in production: Reduced log verbosity by using
Logger::debug()for routine operations and only logging errors or whenapp.debugis enabled - Fixed JSON error handling: Improved error handling for invalid JSON in notification preferences using
JSON_THROW_ON_ERRORand proper exception handling - Simplified conditional logic: Simplified
forceEmaillogic using null coalescing operator (??) instead of verbose if/elseif/else chain - Fixed duplicate return statement: Removed duplicate
return $preferences;statement ingetUserPreferences()
Theme Nord (themes/nord/assets/css/frontend.css)
- Fixed invisible text in lists in light mode: Fixed issue where list text (ul, ol, li) was white in light mode, making it invisible on light backgrounds
- Added explicit color rules for
.post-body ul,.post-body ol, and theirlielements - Added explicit color rules for
.post-body-flatboard ul,.post-body-flatboard ol, and theirlielements - Changed
.post-body-flatboardcolor fromvar(--text-white)tovar(--text-primary)to respect theme mode - Lists now properly use
var(--text-primary)which adapts to light/dark mode
- Added explicit color rules for
Download Service (plugins/resourcemanager/DownloadService.php)
- Fixed syntax error on line 329: Removed orphaned code fragment that was causing a PHP parse error
- Improved file path recovery: Enhanced logic to handle missing files, especially for temporary files and versioned resources
- Better error handling: Added comprehensive logging for file path resolution issues
Post Navigation (app/Views/discussions/show.php)
- Fixed anchor link navigation: Improved handling of
#post-{id}links when navigating from homepage to paginated discussion pages - Enhanced post detection: Added multiple detection methods (ID, data-post-id, partial selectors) to find posts in the DOM
- Improved page loading: Added explicit page post loading when posts are not immediately available in the DOM
- Better retry logic: Implemented progressive retry mechanism with increasing delays
- Removed debug logs: Cleaned up verbose console logging for production use
Theme Configuration (app/Controllers/Admin/ThemeController.php, app/Views/admin/theme-config.php, themes/bootswatch/views/admin/theme-config.php)
- 🟡 Important: Fixed theme color saving issue: Fixed critical bug where theme colors were not being saved correctly
- Implemented bidirectional synchronization between color picker and text input fields in JavaScript
- Added explicit FormData inclusion for all color fields to ensure they are sent in the request
- Fixed color value extraction from form data (checks both
$dataand$_POSTarrays) - Improved color validation to accept both
#RGBand#RRGGBBformats with automatic conversion - Added opcache invalidation after theme data save to force file reload
- Applied fix to both default theme config view and Bootswatch theme config view
- Files Modified:
app/Controllers/Admin/ThemeController.php,app/Views/admin/theme-config.php,themes/bootswatch/views/admin/theme-config.php,app/Helpers/ThemeHelper.php
Theme Screenshot Modal (app/Views/admin/themes.php, themes/bootswatch/views/admin/themes.php)
- 🟢 Enhancement: Added Bootstrap modal for theme screenshots: Replaced new tab opening with a modal display
- Clicking on a theme screenshot now opens a Bootstrap modal instead of a new tab
- Modal displays the screenshot in large size (max-height: 80vh) with theme name in the title
- Added keyboard support: Escape key closes the modal
- Fallback to new tab if Bootstrap is not available
- Applied to both default and Bootswatch theme views
- Files Modified:
app/Views/admin/themes.php,themes/bootswatch/views/admin/themes.php
Translation (languages/fr/admin.json, languages/en/admin.json)
- 🟢 Enhancement: Added missing translation for theme screenshots: Added
themes.screenshottranslation key- French: "Capture d'écran"
- English: "Screenshot"
- Used in theme screenshot modal title
- Files Modified:
languages/fr/admin.json,languages/en/admin.json,themes/bootswatch/views/admin/themes.php
API Controller (app/Controllers/Api/PostApiController.php)
- Fixed post retrieval: Improved handling of large limit requests (>= 10000) to return all posts when needed
- Better pagination: Enhanced logic to detect when all posts are requested vs. paginated requests
Error Handler (app/Core/ErrorHandler.php)
- Fixed PHP 8.0+ deprecation warning: Replaced all references to deprecated
E_STRICTconstant with direct numeric value check (2048) to completely avoid deprecation warnings - Version-safe approach: Uses
PHP_VERSION_ID < 80000check combined with numeric value comparison instead of referencing the constant - Backward compatibility: Code now works correctly on both PHP 7.x (where E_STRICT exists) and PHP 8.0+ (where it's deprecated/removed)
- Solution: Checking for the literal value
2048(E_STRICT's historical value) with version guard prevents PHP from emitting any deprecation warnings, even in PHP 8.4 where the constant may be removed
Cache System (app/Core/Cache.php)
- Fixed cache key validation: Removed overly restrictive validation that rejected valid cache keys containing colons (
:) - Allowed colons in keys: Colons are now permitted in cache keys as they are commonly used for prefix separation (e.g.,
user:,category:,categories:all) - Improved validation regex: Updated character validation to only reject truly dangerous filesystem characters while allowing safe separators
Sanitizer (app/Core/Sanitizer.php)
- Fixed PHP 8.4 deprecation warning: Changed
callable $sanitizer = nullto?callable $sanitizer = nullinsanitizeArray()method to explicitly mark the parameter as nullable - PHP 8.4 compatibility: PHP 8.4 requires explicit nullable type declarations (
?type) instead of implicit nullable parameters (type = null)
Translations (languages/fr/main.json, languages/en/main.json)
- Fixed missing translation key: Added
errors.validation.requiredtranslation key in both French and English main translation files - Report modal fix: The report modal and user settings pages were using
errors.validation.requiredwhich was missing from the translation files - French translation: "Ce champ est requis"
- English translation: "This field is required"
Report Controller (app/Controllers/Moderation/ReportController.php)
- Fixed report submission errors: Fixed issue where report submission always showed "An error occurred" and "Requête invalide" regardless of the selected option
- Error response format: Changed from returning
errors(plural) array to returningerror(singular) string message to match JavaScript expectations - Error message formatting: Validation errors are now properly formatted and concatenated into a single error message
- JavaScript error handling: Updated report modal JavaScript to properly handle HTTP error responses (400, etc.) and extract error messages from JSON responses
- 🟡 Important: Rate limiting: Added rate limiting to prevent report spam (max 5 reports per hour per user)
- 🟡 Important: Duplicate prevention: Added check to prevent users from reporting the same content twice (if a pending report exists)
- 🟡 Important: Input validation: Added comprehensive validation in
update()method with proper status validation - 🟡 Important: XSS protection: Added
htmlspecialchars()protection for content previews in enriched reports - 🟡 Important: Code refactoring: Extracted repetitive code into private helper methods:
enrichReportWithPostData(): Enriches report with post-specific dataenrichReportWithDiscussionData(): Enriches report with discussion-specific datasetDiscussionUrl(): Sets discussion URL in reportcountRecentReportsByUser(): Counts recent reports for rate limitingfindPendingReportByUserAndContent(): Finds duplicate pending reports
- 🟡 Important: Resolved timestamp: Added
resolved_attimestamp when report status is changed to 'resolved' or 'rejected' - Code quality:
- Better error messages with translation support
- Improved code organization and maintainability
- Comprehensive PHPDoc documentation in French
- 🟡 Important: Client-side validation: Added validation in JavaScript to check that all required fields are filled before submitting the report form
- 🟡 Important: Debug logging: Added debug logging in ReportController to help diagnose validation issues
- 🟡 Important: Better error messages: Improved error message formatting to show which fields are missing
- 🔴 Critical: Fixed empty entityId bug: Fixed issue where
reported_idwas empty when submitting reports. The problem was thatform.reset()was clearing hidden field values. Final solution: Implemented memory-based storage system to completely avoid form reset issues:- Memory storage: Store all critical values in
window.reportModalDataobject before any form manipulation - Helper function:
updateReportModalFields()to apply stored data to form fields - No full form reset: Only reset user-input fields (radio buttons, textarea) instead of entire form
- Form submission: Use stored memory data instead of form values for submission
- Modal event handler: Re-apply stored data when modal opens (
show.bs.modalevent) - Data cleanup: Clear stored data after successful submission
- Applied to both
reportPost()andreportDiscussion()functions - Updated
report-modal.phpsubmit handler to use memory data
- Memory storage: Store all critical values in
- 🔴 Critical: Fixed duplicate email notifications: Fixed issue where moderators received two emails for a single report submission. Implemented multi-layer protection:
- JavaScript protection: Added
isSubmittingflag to prevent multiple form submissions in the same request - Server-side protection in
notifyModerators(): Added static cache to preventnotifyModerators()from being called multiple times for the same report ID - Server-side protection in
notify(): Enhanced email deduplication cache for report notifications using stable keys based onuserId | type | reportIdinstead of timestamps - Flag reset on validation errors:
isSubmittingflag is now properly reset when validation fails to allow retry - Initialization protection: Added
dataset.initializedcheck to prevent multiple event listener attachments
- JavaScript protection: Added
- Files updated:
app/Controllers/Moderation/ReportController.phpapp/Views/components/report-modal.phpthemes/premium/views/components/report-modal.phpapp/Services/NotificationService.php
⚡ Performance Improvements
Email Service (app/Services/EmailService.php)
- PHPMailer Path Caching: Added static cache for PHPMailer path to avoid repeated file system lookups
$phpmailerPathstatic property caches the successful path- Reduces I/O operations on subsequent email sends
- Removed Unnecessary UTF-8 Conversions: Removed redundant
mb_convert_encoding($str, 'UTF-8', 'UTF-8')calls- Data is already UTF-8, no conversion needed
- Removed zero-width space hack that could cause issues with email clients
- Simplified PHPMailer Loading: Optimized
loadPHPMailer()to use cached path when available- Faster subsequent loads after first successful load
- Reduced file system checks
🛠️ Code Quality Improvements
Email Service (app/Services/EmailService.php)
- Utility Method for Boolean Conversion: Created
toBool()method to replace repeated boolean conversion patterns- Uses
filter_var()withFILTER_VALIDATE_BOOLEANfor reliable conversion - Replaced all instances of
($value === true || $value === 'true' || $value === 1 || $value === '1')
- Uses
- Removed Zero-Width Space Hack: Removed invisible UTF-8 marker that could cause issues with email clients and spam filters
- PHPMailer handles UTF-8 correctly without this hack
- Cleaner email content
- Improved Error Handling: Better error messages and logging throughout the service
- More descriptive error messages
- Better context in log entries
⚡ Performance Improvements
Visitor Model (app/Models/Visitor.php)
- Bot detection caching: Added static cache for bot detection results to avoid re-running 100+ regex patterns on the same user-agent
- Presence data caching: Implemented 5-second TTL cache for user presence data to reduce file I/O operations
- Optimized iterations: Reduced multiple iterations in
getConnectedUserIps()by filtering active users in a single pass - Constants for magic numbers: Replaced magic numbers with class constants:
AUTO_CLEANUP_PROBABILITY = 100(1 in 100 chance)SECONDS_PER_MINUTE = 60PRESENCE_CACHE_TTL = 5(seconds)
🛡️ Security & Reliability Improvements
Application Bootstrap (app/Core/App.php)
- 🔴 Critical: Added rate limiting to authentication routes: Implemented specialized rate limit middlewares for login, registration, password reset, 2FA, and uploads
LoginRateLimitMiddleware: 5 attempts per 15 minutes (prevents brute force attacks)RegisterRateLimitMiddleware: 3 attempts per hour (prevents registration spam)PasswordResetRateLimitMiddleware: 3 attempts per hour (prevents password reset abuse)TwoFactorRateLimitMiddleware: 5 attempts per 5 minutes (protects 2FA verification)UploadRateLimitMiddleware: 5 uploads per hour (prevents upload flooding)
- 🔴 Critical: Secured file serving routes:
- Avatar route: Changed from permissive
(.+)pattern to strict([a-f0-9]{32}_[a-f0-9]{16}\.[a-z]{3,4})pattern matching only valid avatar filenames - Attachment route: Changed from
(.+)to strict(\d{4})/(\d{2})/([^/]+)pattern with validation:- Year validation (2020-2050)
- Month validation (01-12)
- Filename sanitization with
basename()to prevent path traversal
- File size limits: Added maximum size checks (5MB for avatars, 50MB for attachments) to prevent DoS attacks
- Rate limiting on file access: Added rate limiting (100 requests/minute per IP) to prevent file serving abuse
- Extension validation: Strict whitelist of allowed file extensions for avatars
- Avatar route: Changed from permissive
- ⚡ Performance: Optimized maintenance mode check:
- Added static caching to avoid checking maintenance mode on every request
- Implemented 60-second cache for maintenance mode status
- Session is now only started when maintenance mode is actually active
- ⚡ Performance: Optimized
initDefaults()method:- Added static flag to prevent multiple initializations in the same request
- Extended cache TTL from default to 24 hours (86400 seconds) for bootstrap initialization
- Reduced database/config queries on every request
New Middleware Classes
app/Middleware/LoginRateLimitMiddleware.php: Rate limiting for login attemptsapp/Middleware/RegisterRateLimitMiddleware.php: Rate limiting for registration attemptsapp/Middleware/PasswordResetRateLimitMiddleware.php: Rate limiting for password reset requestsapp/Middleware/TwoFactorRateLimitMiddleware.php: Rate limiting for 2FA verificationapp/Middleware/UploadRateLimitMiddleware.php: Rate limiting for file uploads
Cache System (app/Core/Cache.php)
- 🔴 Critical: Fixed race condition in
storeKey(): Implemented file locking (flock) to prevent concurrent modifications of the.keysfile, ensuring data consistency - 🟡 Important: Multi-server directory protection: Added protection for Nginx (
index.html) and IIS (web.config) in addition to Apache (.htaccess) - 🟡 Important: Replaced
glob()withDirectoryIterator: Changed allglob()calls to useDirectoryIteratorto prevent memory exhaustion with large cache directories (100,000+ files) - 🟡 Important: Optimized
forget()method: Reads.keysfile only once at the beginning and builds a hash-to-key map for O(1) lookups instead of reading it for each file - 🟡 Important: Optimized
getOrSet()method: Uses sentinel value pattern instead of callinghas()separately, reducing file operations - 🟡 Important: Added key and tag validation: Implemented
validateKey()method to prevent invalid characters and enforce length limits (200 chars max) - 🟡 Important: Added return type declarations: All public methods now have proper return type declarations for better type safety
- 🟡 Important: Added constants for magic numbers: Replaced hardcoded values with named constants:
CLEANUP_INTERVAL = 3600(1 hour)LONG_TERM_TTL = 31536000(1 year)MIN_CLEANUP_LOG_THRESHOLD = 50
- Performance: Reduced file I/O operations by ~70% in
forget()method, improved memory efficiency with iterators - Reliability: File locking ensures atomic operations on
.keysfile, preventing data corruption
Base Controller (app/Core/Controller.php)
- 🟡 Important: Eliminated code duplication: Created centralized
handlePermissionDenied()method to handle all permission errors consistently - 🟡 Important: Added return type
never: Methods that callexitnow useneverreturn type for better type safety (PHP 8.1+) - 🟡 Important: Added CSRF verification helper: Implemented
verifyCsrf()method for manual CSRF token verification when needed - 🟡 Important: Simplified
requireAdmin(): Removed unnecessary try-catch wrapper, now directly callsrequirePermission() - 🟡 Important: Added flash message helpers:
success()method for success messages with optional redirecterror()method for error messages with optional redirect
- 🟡 Important: Added rate limiting helper: Implemented
rateLimit()method usingRateLimiterclass for proper rate limiting in controllers - 🟡 Important: Added ownership checking: Implemented
requireOwnership()method to verify resource ownership (with admin bypass) - 🟡 Important: Added input validation: Implemented
validate()method with support for multiple rules (required, email, min, max, numeric, url) - Code quality:
- Reduced code duplication by ~60%, improved maintainability and consistency
- Added comprehensive PHPDoc documentation for all methods
- Improved imports organization with proper use statements
Config System (app/Core/Config.php)
- 🔴 Critical: Fixed race condition in
save(): Implemented atomic write with file locking using temporary file + rename pattern, preventing concurrent modifications from corrupting the configuration file - 🟡 Important: Removed double encryption: Eliminated redundant SMTP password encryption in
save()method - encryption now only happens inset()method - 🟡 Important: Added configuration validation: Implemented
validateValue()method to validate:- Numeric values (session lifetimes, password min length, SMTP port)
- Boolean values (normalization and validation)
- Range validation (ports 1-65535, password length 4-128)
- 🟡 Important: Added EncryptionHelper availability check: Implemented
hasEncryptionHelper()method to gracefully handle missing EncryptionHelper class - 🟡 Important: Added environment variable support: Sensitive configuration keys (
smtp.password,smtp.username,smtp.host) can now be set via environment variables (takes precedence over file config) - 🟡 Important: Added automatic backup: Configuration file is automatically backed up before each save (
.backup.oldpattern) - 🟡 Important: Optimized plugin deduplication: Improved
deduplicatePlugins()method with better filtering and cleaner logic - Reliability: Atomic writes ensure configuration file integrity even during concurrent access
- Security: Environment variable support allows keeping sensitive credentials out of version control
CacheKeys (app/Core/CacheKeys.php)
- 🟡 Important: Added TTL constants: Centralized TTL values for consistent cache durations:
TTL_SHORT = 300(5 minutes)TTL_MEDIUM = 3600(1 hour)TTL_LONG = 86400(1 day)TTL_PERMANENT = 31536000(1 year)
- 🟡 Important: Added tag constants: Centralized cache tags for consistent invalidation:
TAG_PERMISSIONS,TAG_CATEGORIES,TAG_GROUPS,TAG_REACTIONS,TAG_USERS,TAG_BOOTSTRAP
- 🟡 Important: Enhanced documentation: Added comprehensive class-level documentation with usage examples
- Code quality: Updated
invalidatePermissions()andinvalidateCategories()to use tag constants instead of hardcoded strings
AssetMinifier (app/Core/AssetMinifier.php)
- 🔴 Critical: Added input validation: UTF-8 encoding check and maximum size limit (5MB default) to prevent DoS attacks
- 🔴 Critical: Fixed path traversal vulnerability: Added path validation in
clearCache()anddeleteDirectory()usingrealpath()to ensure all operations stay within cache directory - 🔴 Critical: Mitigated ReDoS risks:
- Added PCRE backtrack/recursion limits
- Added timeout checks (5 seconds default)
- Limited @-rule matches to prevent catastrophic backtracking
- 🟡 Important: Secure placeholders: Replaced sequential counters with cryptographically secure random placeholders (
bin2hex(random_bytes(8))) to prevent collisions - 🟡 Important: Added rate limiting: Implemented rate limiting (10 minifications per minute per file) to prevent abuse
- 🟡 Important: Added file locking: Implemented file locking for
minifyAndSave()to prevent concurrent operations on the same file - 🟡 Important: Optimized file operations: Added up-to-date check to skip minification if output file is newer than source
- Performance: Reduced unnecessary minifications by checking file modification times
Autoloader (app/Core/Autoloader.php)
- 🔴 Critical: Added path traversal protection: Implemented
isPathSafe()method to validate that all loaded files are within the base directory, preventing directory traversal attacks - 🟡 Important: Added class caching: Implemented
$classMapcache to avoid reloading already-loaded classes, improving performance - 🟡 Important: Added failed lookup cache: Implemented
$failedLookupscache to avoid retrying failed class lookups (max 500 entries) - 🟡 Important: Optimized plugin path resolution:
- Cache plugin paths to avoid repeated
glob()calls - Try exact match first (fast path) before case-insensitive search
- Use static cache for plugin directory listing
- Cache plugin paths to avoid repeated
- 🟡 Important: Added rate limiting: Implemented maximum autoload attempts (1000) to prevent DoS attacks via excessive autoload calls
- 🟡 Important: Improved class name validation: Added
isValidClassName()method using PSR-4 compliant regex validation - 🟡 Important: Enhanced plugin loading: Support both
app\Plugins\andApp\Plugins\namespace patterns - Performance: Reduced file system operations by ~80% through caching, especially for frequently loaded classes
AtomicFileHelper (app/Core/AtomicFileHelper.php)
- 🔴 Critical: Removed thread-unsafe static cache: Eliminated in-memory cache arrays that caused race conditions in multi-process environments (PHP-FPM)
- 🔴 Critical: Replaced dangerous fallback: Changed from reading files without lock to using file-level lock (
flock) as a safe fallback when FileLocker fails - 🟡 Important: Added write verification: Implemented integrity checks after atomic writes:
- Verifies file exists after rename
- Checks data size matches expected
- Validates JSON structure of written data
- 🟡 Important: Improved error handling: Enhanced logging and error messages throughout the class
- Performance: Removed cache overhead, but ensures data consistency across all processes
Visitor Model (app/Models/Visitor.php)
- Error handling: Added try-catch blocks around file operations (
file_get_contents,json_decode) - Error logging: Implemented proper error logging via
Logger::error()for file operation failures - Graceful degradation: Added fallback values when file operations fail
RateLimiter (app/Core/RateLimiter.php)
- Fixed race condition vulnerability: Implemented file locking (
flock) incheck()method to ensure atomic read-modify-write operations - Improved
clearLoginRateLimits(): Replaced inefficient O(n) file scanning with direct O(1) key deletion - Added whitelist support: Implemented whitelist functionality to exempt specific identifiers from rate limiting
- Enhanced error handling: Added comprehensive error handling and logging for file operations
- Better retry information: Improved
getRetryAfter()method with proper error handling - Added rate limit logging: Implemented logging when rate limits are hit for security monitoring
- New rate limit type: Added
visitor_trackingrate limit (60 requests per minute)
Technical Implementation Details:
- Atomic operations: All rate limit checks now use file locking (
flockwithLOCK_EX) to prevent concurrent access issues- Read-modify-write operations are now atomic
- Proper lock release in finally block ensures no deadlocks
- Fail-closed approach: returns false on locking errors for security
- Direct key deletion:
clearLoginRateLimits($identifier)now uses direct cache key deletion (O(1)) instead of scanning all files (O(n))- New method:
clearLoginRateLimitsForIdentifiers(array $identifiers)for batch operations - Backward compatible:
clearLoginRateLimits(null)calls deprecatedclearAllLoginRateLimits()
- New method:
- Whitelist management:
addToWhitelist(string $identifier): Exempt identifier from rate limitingremoveFromWhitelist(string $identifier): Remove identifier from whitelistisWhitelisted(string $identifier): Check if identifier is whitelisted- Whitelist checked before any rate limit evaluation
- Security logging:
- Rate limit hits logged with
Logger::warning()for security monitoring - Identifiers anonymized (IPs masked, other identifiers partially hidden) for privacy
- Error logging for file operations and locking failures
- Rate limit hits logged with
- Error handling:
- Try-catch-finally blocks ensure proper resource cleanup
- File operations wrapped in error handling
- Graceful degradation: returns false (fail-closed) on errors
- Cache expiration handling: Automatic expiration check and reset when cache expires
- Backward compatibility:
clearAllLoginRateLimits()method maintained but deprecated- Method signature updated:
clearLoginRateLimits(?string $identifier = null)
🔧 Code Quality
Visitor Model (app/Models/Visitor.php)
- Translation helper: Added private
trans()method to centralize translation calls with consistent defaults - Code maintainability: Improved code structure and readability
- Consistent error handling: Standardized error handling patterns throughout the class
📝 Documentation
- Code cleanup: Removed verbose debug logging from production code
- Improved comments: Enhanced inline documentation for complex logic
⚠️ Known Limitations & Future Recommendations
RateLimiter (app/Core/RateLimiter.php)
The following limitations remain for future improvements:
-
File-based storage limitations:
- While file locking prevents race conditions, file-based storage is still slower than memory-based solutions
- Recommendation: Consider migrating to Redis, APCu, or Memcached for production environments with high traffic
-
Missing distributed system support: File-based rate limiting won't work correctly across multiple servers
- Recommendation: Implement distributed rate limiting solution (Redis-based) for multi-server deployments
-
No graduated penalties: All rate limits are fixed - no exponential backoff for repeated violations
- Recommendation: Consider implementing graduated penalties for repeated rate limit violations
🔄 Migration Notes
No database migrations required for this release. All changes are backward compatible.
📦 Files Modified
plugins/resourcemanager/DownloadService.php- Fixed syntax error, improved file recoveryapp/Views/discussions/show.php- Fixed anchor navigation, improved post detectionapp/Controllers/Api/PostApiController.php- Fixed post retrieval for large limitsapp/Models/Visitor.php- Performance optimizations, caching, error handlingapp/Core/RateLimiter.php- Critical security fixes, race condition prevention, whitelist supportapp/Core/App.php- Added rate limiting to routes, secured file serving, performance optimizationsapp/Core/Controller.php- Eliminated code duplication, added helpers (CSRF, rate limiting, ownership checks, flash messages)app/Core/Config.php- Fixed race conditions with atomic writes, added validation, environment variable support, automatic backupsapp/Core/Cache.php- Fixed race conditions with file locking, replaced glob() with iterators, optimized forget() method, multi-server protection, key validationapp/Core/CacheKeys.php- Added TTL and tag constants, enhanced documentationapp/Core/AssetMinifier.php- Added input validation, path traversal protection, ReDoS mitigation, secure placeholders, rate limitingapp/Core/Autoloader.php- Added caching, path traversal protection, rate limiting, optimized plugin loadingapp/Core/AtomicFileHelper.php- Removed thread-unsafe cache, improved fallback mechanism, added write verificationapp/Middleware/LoginRateLimitMiddleware.php- New: Rate limiting for login attemptsapp/Middleware/RegisterRateLimitMiddleware.php- New: Rate limiting for registrationapp/Middleware/PasswordResetRateLimitMiddleware.php- New: Rate limiting for password resetapp/Middleware/TwoFactorRateLimitMiddleware.php- New: Rate limiting for 2FAapp/Middleware/UploadRateLimitMiddleware.php- New: Rate limiting for file uploads
🔍 Technical Notes
RateLimiter Implementation
The RateLimiter now uses file locking (flock) to ensure atomic operations:
- Before: Non-atomic read-modify-write operations could allow rate limit bypass
- After: Exclusive locks (
LOCK_EX) prevent concurrent modifications - Performance: File locking adds minimal overhead (~1-2ms per check)
- Reliability: Fail-closed approach ensures security is never compromised
- Lock management: Uses try-finally blocks to guarantee lock release even on errors
- Cache expiration: Automatic expiration check and reset when cache expires
Visitor Model Optimizations
- Bot detection cache: Reduces regex execution from 100+ patterns to 1 lookup (static cache)
- Presence data cache: Reduces file I/O by ~95% (5-second TTL cache)
- Single-pass filtering:
getConnectedUserIps()reduced from O(n²) to O(n) complexity - Constants: Magic numbers replaced with class constants for maintainability
App.php Security & Performance Improvements
- Rate limiting integration: All critical authentication routes now protected against brute force attacks
- File serving security: Strict pattern matching prevents path traversal attacks, file size limits prevent DoS
- Maintenance mode optimization: 60-second cache reduces config checks by ~99% (from every request to once per minute)
- Bootstrap optimization: 24-hour cache for initialization checks, static flags prevent redundant operations
- Performance impact: Reduced overhead from ~5-10ms per request to <1ms for maintenance/init checks
AtomicFileHelper Improvements
- Thread safety: Removed static cache arrays that caused race conditions in PHP-FPM multi-process environments
- Safe fallback: Uses file-level locking (
flock) instead of reading without lock when FileLocker fails - Data integrity: Verifies file existence, size, and JSON validity after atomic writes
- Reliability: Ensures consistent data across all processes, even if slightly slower due to no caching
Cache System Improvements
- Thread safety: File locking in
storeKey()prevents race conditions when multiple processes update the.keysfile simultaneously - Security: Multi-server directory protection (Apache
.htaccess, Nginxindex.html, IISweb.config) ensures cache files are not accessible via web - Memory efficiency:
DirectoryIteratorreplacesglob()to handle large cache directories (100,000+ files) without memory exhaustion - Performance:
forget()method optimized to read.keysonce instead of per-file, reducing I/O by ~70% - Validation: Key and tag validation prevents invalid characters and enforces length limits (200 chars for keys, 100 chars for tags)
- Type safety: All public methods now have proper return type declarations
- Code quality: Magic numbers replaced with named constants for better maintainability
- Optimization: Sentinel value pattern in
getOrSet()eliminates redundanthas()calls
Sanitizer (app/Core/Sanitizer.php)
- 🟡 Important: Enhanced HTML sanitization: Improved
sanitizeHtml()with options to control images and links, comprehensive event handler removal, dangerous protocol filtering, and data URI protection - 🟡 Important: Configuration constants: Centralized
DEFAULT_ALLOWED_TAGS,ALLOWED_PROTOCOLS,DANGEROUS_ATTRIBUTES,MAX_FILENAME_LENGTH,MAX_URL_LENGTHfor better maintainability - 🟡 Important: Enhanced URL sanitization:
sanitizeUrl()now validates protocols, removes control characters, checks length, and supports strict mode - 🟡 Important: URL validation: Added
isValidUrl()method to validate URLs without modifying them - 🟡 Important: Enhanced email sanitization:
sanitizeEmail()now validates emails and removes control characters - 🟡 Important: Email validation: Added
isValidEmail()method to validate emails without modifying them - 🟡 Important: Context-specific sanitization: Added
sanitizeForAttribute(),sanitizeForJs(),sanitizeForCss()for different output contexts - 🟡 Important: Enhanced filename sanitization:
sanitizeFilename()now supports extension whitelist, prevents hidden files, and better path traversal protection - 🟡 Important: File extension validation: Added
isAllowedExtension()method to validate file extensions - 🟡 Important: Enhanced path sanitization:
sanitizePath()now supportsmustExistparameter and better path normalization - 🟡 Important: Path normalization: Added
normalizePath()method to normalize paths without requiring them to exist - 🟡 Important: Type-specific sanitization: Added
sanitizeInt(),sanitizeFloat(),sanitizeBool()for type-safe input sanitization - 🟡 Important: Array sanitization: Added
sanitizeArray()method for recursive array sanitization - 🟡 Important: Improved accent removal:
removeAccents()is now public and includes comprehensive accent map (100+ characters) - 🟡 Important: Text utilities: Added
stripHtml()andtruncate()methods for text manipulation - 🟡 Important: Double encoding protection: Added
escapeOnce()method to prevent double HTML encoding - Code quality:
- Better separation of concerns with private helper methods
- Comprehensive PHPDoc documentation
- Improved error handling and validation
- Constants for magic numbers and configuration
Router (app/Core/Router.php)
- 🟡 Important: Named routes: Added
name()method to define named routes andurl()method to generate URLs programmatically - 🟡 Important: Route groups: Added
group()method to group routes with shared prefix and middleware - 🟡 Important: Route caching: Added
enableCache()method to cache compiled routes for 90% performance improvement - 🟡 Important: Resource routes: Added
resource()method to generate all RESTful routes (index, create, store, show, edit, update, destroy) in one call - 🟡 Important: Route constraints: Added
where()method to validate route parameters with regex patterns - 🟡 Important: PATCH method: Added
patch()method for partial updates - 🟡 Important: Chainable methods: All route methods now return
RouteBuilderfor method chaining - 🟡 Important: Unified dispatch logic: Extracted shared logic from
dispatch()anddispatchApi()intohandleRequest()method, eliminating 140 lines of duplication - Code quality:
- Extracted private methods:
normalizeUrl(),isStaticFile(),sortRoutesByPriority(),tryMatchRoute(),handleNotFound(),executeControllerHandler(),executeCallableHandler(),resolveMethodParameters() - Centralized constants:
STATIC_EXTENSIONS,PLUGIN_THEME_PREFIXES,CACHE_FILE - Better separation of concerns with single-responsibility methods
- Improved testability (each method can be tested independently)
- Comprehensive PHPDoc documentation
- Extracted private methods:
- Performance:
- Route caching reduces route resolution time by 90% (from 0.5ms to 0.05ms)
- Route sorting happens once (cached) instead of every request
- 90% memory reduction with cached routes
- Backward compatibility: 100% compatible - all existing code continues to work, new features are opt-in
Response (app/Core/Response.php)
- 🟡 Important: State management: Added
$sentflag to prevent modifying responses after sending - 🟡 Important: Fluent interface: All methods return
selffor method chaining - 🟡 Important: Cache control helpers:
cache()andnoCache()methods for easy cache management - 🟡 Important: Additional response types:
text(),html(),download()for different content types - 🟡 Important: Rate limiting response:
tooManyRequests()method withRetry-Afterheader support - 🟡 Important: Back redirect:
back()method to redirect to referer with fallback - 🟡 Important: Bulk header setting:
withHeaders()method to set multiple headers at once - 🟡 Important: HTML minification: Built-in HTML minification with preservation of
<pre>,<textarea>,<script>,<style>blocks - 🟡 Important: Better error handling: Proper output buffer cleanup in view rendering with try-catch
- 🟡 Important: View resolution: Cleaner separation of view resolution logic into dedicated methods
- 🟡 Important: Compression logic: Improved compression with better checks and header management
- 🟡 Important: Header introspection:
getHeaders()andgetStatusCode()methods for debugging - Code quality:
- Better separation of concerns with private helper methods
- Comprehensive PHPDoc documentation
- State validation prevents modifying sent responses
- Improved error messages
Request (app/Core/Request.php)
- 🟡 Important: HTTP method override: Support for
_methodfield andX-HTTP-Method-Overrideheader for REST APIs - 🟡 Important: Enhanced data access: Separate methods
post(),query(),json()for accessing different data sources - 🟡 Important: Advanced IP detection: Support for Cloudflare (
CF-Connecting-IP), standard proxies, and proper IP validation - 🟡 Important: HTTPS detection:
isSecure()andgetScheme()methods for detecting secure connections - 🟡 Important: Content negotiation:
wantsJson(),accepts(),getAcceptLanguage()for proper API responses - 🟡 Important: URL utilities:
getUrl(),getRawUrl(),getUri(),getFullUrl()for different URL formats - 🟡 Important: Cookie access:
cookie()andcookies()methods for easy cookie access - 🟡 Important: File upload helpers:
hasFile()validates upload success,getFile()for easy access - 🟡 Important: Multiple key checks:
hasAll()andhasAny()for checking multiple keys at once - 🟡 Important: Debug information:
debug()method returns comprehensive request information - 🟡 Important: Header utilities:
hasHeader()for checking header existence, improved header parsing with nginx fallback - Code quality:
- Simplified URL parsing logic (removed complex fallback chains)
- Better organization with single-responsibility methods
- Comprehensive PHPDoc documentation
- Backward compatibility maintained (
getJsonInput()alias)
Plugin Helper (app/Core/PluginHelper.php) (New File)
- 🟡 Important: New utility class: Created comprehensive helper class for plugin development
- 🟡 Important: Translation management:
getTranslations()with caching and fallback to default language - 🟡 Important: Safe execution:
safeExecute()andsafeExecuteBool()for error handling in plugins - 🟡 Important: JSON utilities:
readJsonSafe()andwriteJsonSafe()for safe file operations - 🟡 Important: Dependency checking:
checkDependencies()to verify plugin requirements (PHP version, extensions, other plugins) - 🟡 Important: Asset management:
getAssetUrl(),enqueueStyle(),enqueueScript()for CSS/JS assets - 🟡 Important: View rendering:
renderView()for rendering plugin views with error handling - 🟡 Important: Route registration:
addRoute()for easy route registration via hooks - 🟡 Important: Cron scheduling:
scheduleCron()for registering scheduled tasks - 🟡 Important: Version compatibility:
isVersionCompatible()for version checking with operators - 🟡 Important: Cache management:
getCacheDir(),clearPluginCache(),clearAllCaches()for plugin-specific caching - 🟡 Important: Metadata caching:
getMetadata()with caching for plugin.json data - Code quality:
- Comprehensive caching system (translations, metadata, assets)
- Error handling with detailed logging
- Performance optimization with DirectoryIterator
- Full PHPDoc documentation
Plugin System (app/Core/Plugin.php)
- 🟡 Important: Enhanced plugin loading: Complete rewrite with improved error handling and validation
- 🟡 Important: Security validation: Plugin ID validation using regex to prevent path traversal and invalid IDs
- 🟡 Important: Multiple class file conventions: Support for various naming conventions (ClassName.php, PluginIdPlugin.php, src/ClassName.php)
- 🟡 Important: Hook priorities: Added priority system for hooks (lower priority = executed first)
- 🟡 Important: Slow hook detection: Automatic detection and logging of hooks taking more than 100ms
- 🟡 Important: Enhanced error handling: Detailed error logging for plugin loading, class instantiation, and hook execution
- 🟡 Important: Plugin statistics: Added
getStats()method to monitor loaded plugins and hooks - 🟡 Important: Plugin reset: Added
reset()method to clear all plugins and hooks (useful for testing) - 🟡 Important: Duplicate file loading protection: Tracks loaded plugin files to prevent multiple includes
- 🟡 Important: Improved metadata validation: Comprehensive validation of plugin.json with detailed error messages
- 🟡 Important: Backward compatibility: Maintained compatibility with old
trigger()signature while supporting new one - Code quality:
- Better separation of concerns with private helper methods
- Comprehensive PHPDoc documentation
- Improved logging throughout the plugin lifecycle
- Better callback information for debugging
Logger (app/Core/Logger.php)
- 🟡 Important: PSR-3 compatibility: Full PSR-3 standard compliance with all standard log levels (emergency, alert, critical, error, warning, notice, info, debug)
- 🟡 Important: Automatic log rotation: Automatic rotation when log files exceed 10MB with configurable backup file count
- 🟡 Important: Enhanced formatting: Log entries now include microseconds precision, PID, and formatted context
- 🟡 Important: PSR-3 interpolation: Support for placeholder interpolation in log messages (e.g.,
{user_id}) - 🟡 Important: Context size management: Automatic reduction of large contexts to prevent memory issues (max 5KB per context)
- 🟡 Important: Statistics tracking: Added
getStats()method to track total logs, logs by level, and last log time - 🟡 Important: Performance logging: Added
performance()method for performance metrics - 🟡 Important: Exception logging: Enhanced
exception()method with formatted stack traces and previous exception support - 🟡 Important: Old log cleanup: Added
cleanOldLogs()method to remove logs older than specified days - 🟡 Important: Memory protection: Improved memory protection with 2MB threshold (down from 5MB) for better safety
- Code quality:
- Backward compatibility maintained for old
log($message, $level, $type)signature - Better error handling with fallback to temp directory
- DirectoryIterator used instead of glob() for better performance
- Comprehensive PHPDoc documentation
- Configurable log file size and backup count
- Backward compatibility maintained for old
File Locker (app/Core/FileLocker.php)
- 🟡 Important: Enhanced lock management: Complete rewrite with improved lock acquisition and release
- 🟡 Important: Stale lock detection: Automatic detection and cleanup of locks from dead processes using PID verification
- 🟡 Important: Exponential backoff: Improved retry mechanism with exponential backoff for better performance
- 🟡 Important: Lock metadata: Stores PID, timestamp, key, and hostname in lock files for better debugging
- 🟡 Important: Synchronized execution: Added
synchronized()method to execute code blocks with automatic lock management - 🟡 Important: Lock ownership verification: Verifies lock ownership before releasing to prevent accidental unlocks
- 🟡 Important: Statistics: Added
getStats()method to monitor active locks and lock files - 🟡 Important: Performance optimization: Replaced
glob()withDirectoryIteratorfor better memory efficiency - Code quality:
- Better error handling with detailed logging
- Improved timeout handling using
microtime()for precision - Separate lock directory (
/stockage/cache/locks) for better organization - Comprehensive PHPDoc documentation
Error Handler (app/Core/ErrorHandler.php)
- 🟡 Important: Enhanced error handling: Complete rewrite with comprehensive error management
- 🟡 Important: Context management: Added
setContext()method to update Request/Response context dynamically - 🟡 Important: Debug page: Beautiful HTML debug page with exception details, stack trace, and request context
- 🟡 Important: AJAX/API support: Proper JSON error responses for AJAX and API requests
- 🟡 Important: Fatal error handling: Improved shutdown handler to catch fatal errors and convert them to exceptions
- 🟡 Important: HTTP status code mapping: Automatic mapping of exception types to appropriate HTTP status codes
- 🟡 Important: Context information: Enriched error logs with URL, method, IP, user agent, memory usage, PHP version
- 🟡 Important: Log level mapping: Automatic log level determination based on error severity
- Code quality:
- Protection against multiple registrations
- Better error recovery with fallback log directory
- CLI error output support
- Comprehensive PHPDoc documentation
Base Controller Improvements
- Code quality: Centralized error handling eliminates ~60% of code duplication
- Type safety:
neverreturn type for methods that exit ensures proper type checking - Developer experience: Helper methods (
success(),error(),rateLimit(),verifyCsrf()) simplify common operations - Security: Ownership checking with admin bypass provides flexible access control
- Consistency: All permission errors handled uniformly through
handlePermissionDenied()
Config System Improvements
- Thread safety: Atomic write operations with file locking prevent race conditions and data corruption
- Data integrity: Automatic backups before each save allow recovery from failed writes
- Security: Environment variable support for sensitive credentials (SMTP passwords, etc.)
- Validation: Type and range validation prevents invalid configuration values
- Reliability: Graceful handling of missing EncryptionHelper, prevents fatal errors
- Performance: Optimized plugin deduplication with cleaner logic
CacheKeys Improvements
- Centralization: TTL and tag constants centralized for consistent cache management across the application
- Documentation: Comprehensive class-level documentation with usage examples
- Consistency: All invalidation methods now use tag constants instead of hardcoded strings
AssetMinifier Improvements
- Security: Path traversal protection in cache clearing operations, input validation (UTF-8, size limits)
- ReDoS mitigation: PCRE limits, timeouts, and match limits prevent catastrophic backtracking
- Reliability: Secure random placeholders prevent collisions, file locking prevents concurrent operations
- Performance: Up-to-date checks skip unnecessary minifications, rate limiting prevents abuse
- Robustness: All file operations validated with
realpath()to ensure they stay within allowed directories
Autoloader Improvements
- Security: Path traversal protection ensures all loaded files are within the base directory
- Performance: Class map cache reduces file system operations by ~80% for frequently loaded classes
- DoS protection: Rate limiting prevents excessive autoload attempts (max 1000 per request)
- Plugin optimization: Cached plugin path resolution with exact match first, reducing
glob()calls - Memory management: Failed lookup cache limited to 500 entries to prevent memory bloat
Router (app/Core/Router.php)
- 🟡 Important: Universal plugin hook: Added
router.plugins.registerhook for universal plugin route registration - 🟡 Important: Route resource method: Added
resource()method returningselffor method chaining - 🟡 Important: Plugin route registration: New
registerPluginRoutes()method to trigger plugin route hooks - Code quality:
- Comprehensive PHPDoc documentation in French for all methods
- Documentation grouped with file header
- Detailed examples and usage patterns
- All methods fully documented with parameter descriptions
Session Management (app/Core/Session.php)
- 🟡 Important: Enhanced security: Complete rewrite with comprehensive security features
- 🔴 Critical: Session hijacking prevention: Fingerprinting based on User-Agent and Accept-Language
- 🟡 Important: IP validation: Optional IP address validation (can be disabled for mobile users)
- 🟡 Important: Timeout management: Absolute timeout (24 hours) and idle timeout (1 hour)
- 🟡 Important: Automatic ID regeneration: Periodic session ID regeneration (every 30 minutes)
- 🟡 Important: Remember Me tokens: Support for persistent authentication tokens
- 🟡 Important: Session metadata: Methods to get session age, idle time, and expiration status
- 🟡 Important: Secure cookie configuration: HttpOnly, Secure, SameSite (Lax) with proper defaults
- Code quality:
- Comprehensive PHPDoc documentation in French
- All 28 methods fully documented
- Detailed examples for complex methods
- Documentation grouped with file header
Validator (app/Core/Validator.php)
- 🟡 Important: Laravel-style validation: Added
validate()static method with array-based rules - 🟡 Important: 38 validation rules: Comprehensive rule set including:
- Type validation:
string,integer,boolean,array,numeric - String validation:
alpha,alpha_num,alpha_dash,between - Date validation:
date,date_format,before,after - Network validation:
ip,ipv4,ipv6 - Format validation:
json,uuid,regex - Comparison:
same,different,not_in - Conditional:
requiredIf,requiredUnless,requiredWith
- Type validation:
- 🟡 Important: Validated data extraction:
validated()method returns only validated fields (prevents mass assignment) - 🟡 Important: Bail functionality: Stop validation after first error for performance
- 🟡 Important: Custom attribute names: Replace field names in error messages for better UX
- 🟡 Important: ValidationException: Proper exception class for validation failures
- Code quality:
- Comprehensive PHPDoc documentation in French
- All 38+ methods fully documented
- Detailed examples for each validation rule
- Documentation grouped with file header
Request (app/Core/Request.php)
- Code quality:
- Comprehensive PHPDoc documentation in French
- All methods fully documented with examples
- Documentation grouped with file header
- Detailed descriptions of IP detection, HTTPS detection, content negotiation
Response (app/Core/Response.php)
- Code quality:
- Comprehensive PHPDoc documentation in French
- All methods fully documented with examples
- Documentation grouped with file header
- Detailed descriptions of response types, compression, minification
Sanitizer (app/Core/Sanitizer.php)
- Code quality:
- Comprehensive PHPDoc documentation in French
- All sanitization methods fully documented
- Documentation grouped with file header
- Detailed descriptions of security features and sanitization contexts
Plugin System Updates
- 🟡 Important: Universal route hook: All plugins migrated to
router.plugins.registerhook - Updated plugins (12 total):
ResourceManager,Reputation,EasyPages,AIModeration,CookieConsentForumMonitoring,TranslationManager,ArchiveBuilder,AIchatboxPrivateMessaging,StorageMigrator,Flatboard4UrlFix
- Updated plugin.json files: All hook references updated from
app.routes.registertorouter.plugins.register - Updated README files: Documentation updated in
aichatboxandForumMonitoringplugins - Code quality: Consistent hook usage across all plugins for better maintainability
Cache Value Object (app/Core/Cache/CacheValue.php)
- 🟡 Important: Enhanced validation: Complete rewrite with comprehensive data validation
- 🟡 Important: TTL validation: Validates that TTL is non-negative and prevents timestamp overflow
- 🟡 Important: Tag validation: Validates tag format, length (max 100 chars), and removes invalid characters
- 🟡 Important: Improved error handling:
fromJson()now throwsInvalidArgumentExceptionfor invalid data instead of silently returning null - 🟡 Important: Helper methods: Added
timeToLive(),age(),hasTag(),hasAnyTag(),hasAllTags()for better cache management - 🟡 Important: Removed key field: Removed 'key' field from
toArray()as it's managed byCache::set()(cleaner separation of concerns) - Code quality:
- Comprehensive PHPDoc documentation in French
- All methods fully documented with examples
- Detailed validation with clear error messages
- Tag normalization (trim, deduplication)
- Documentation grouped with file header
NextRouteException (app/Core/Exceptions/NextRouteException.php)
- 🟡 Important: Enhanced exception: Improved with reason tracking and better documentation
- 🟡 Important: Reason property: Added optional
reasonproperty for debugging why a route declined - 🟡 Important: Helper methods: Added
setReason(),getReason(), andgetFullMessage()for better debugging - 🟡 Important: Improved default message: More descriptive default message ("Passage à la route suivante")
- Code quality:
- Comprehensive PHPDoc documentation in French
- Detailed usage examples in documentation
- Clear explanation of the "next route" pattern
- Documentation grouped with file header
FieldValidations (app/Core/Validations/FieldValidations.php)
- 🔴 Critical: Fixed required() logic: Fixed bug where
required()incorrectly rejected "0" and 0 as empty values (now uses!== null && !== ''instead ofempty()) - 🟡 Important: Multibyte support: Replaced
strlen()withmb_strlen()inminLength()andmaxLength()to correctly handle UTF-8 characters (e.g., "café" = 4 characters, not 5 bytes) - 🟡 Important: New validations: Added 10 new validation methods:
integer(): Validates integer valuesbetween(): Validates numeric range (min-max)date(): Validates date formatip(),ipv4(),ipv6(): Validates IP addressesusername(): Validates username format (alphanumeric + underscores)confirmed(): Validates value confirmation (e.g., password confirmation)slug(): Validates URL-friendly slugs
- 🟡 Important: Boolean validation split: Separated strict
boolean()(only true/false) from permissivebooleanLike()(accepts '1', '0', 'on', 'off', 'yes', 'no', etc.) - Code quality:
- Comprehensive PHPDoc documentation in French for all methods
- Detailed examples for each validation method
- Clear parameter descriptions and return value explanations
- Documentation grouped with file header
- All methods fully documented with usage examples
Memory & Performance Impact
- RateLimiter: File locking adds ~1-2ms overhead per check, but ensures correctness
- Visitor Model: Bot cache prevents redundant regex execution, presence cache reduces file reads
- App.php: Maintenance mode and init checks optimized, reducing overhead by ~90%
- Session: Fingerprinting and validation add ~0.5ms per request, but significantly improve security
- Validator: Array-based validation is ~5-10% slower than fluent interface, but provides better security with validated data extraction
- CacheValue: Validation adds minimal overhead (~0.1ms) but ensures data integrity
- Overall: Improved performance while maintaining security and reliability
[5.0.0-rc.10] - 2026-01-14
Complete JavaScript Migration
This version finalizes the migration from inline JavaScript to reusable ES6 modules.
New JavaScript Modules
-
tag-manager.js:- Tag management with autocomplete
- Create new tags on the fly
- Display colored badges
- Delete via button or keyboard
-
mention-manager.js:- Autocomplete for @mentions
- Integration with EasyMDE and standard textarea
- Real-time user search
- Keyboard navigation in suggestions
-
load-more-manager.js:- Generic "Load More" pagination
- Support for discussions, posts, reactions, subscriptions
- Animation for new elements
- State management (loading, error, end)
-
toast-manager.js:- Standardized toast notification system
- Support for success, error, info, warning
- Configurable auto-close
- Message queue
-
form-validator.js:- Centralized client-side validation
- Rules: required, minlength, maxlength, email, pattern
- Translated error messages
- Visual validation styles
-
attachments-manager.js:- File upload with drag & drop
- Size and MIME type validation
- File badge display
- Form synchronization
-
draft-manager.js:- Automatic draft saving
- Load existing drafts
- Delete after submission
- Multi-editor support
-
profile-manager.js:- "Load More" button management for profiles
- User data export
- Animation for new elements
-
auth-2fa-manager.js:- Auto-submission of 2FA code
- Copy secret to clipboard
- Enable/disable 2FA via AJAX
Refactored Views
app/Views/discussions/create.php– Uses TagManager, MentionManager, FormValidatorapp/Views/discussions/edit.php– Uses TagManager, MentionManager, FormValidatorapp/Views/discussions/search.php– Simplified with SearchFormManagerapp/Views/posts/edit.php– Uses StandalonePostEditManagerapp/Views/users/profile.php– Uses ProfileManagerapp/Views/auth/2fa.php– Uses Auth2FAManagerapp/Views/auth/2fa-settings.php– Uses Auth2FAManager
Updated Layouts
app/Views/layouts/frontend/footer.php– Loads all frontend modulesthemes/premium/views/layouts/frontend/footer.php– Sync with default theme
Benefits
- Performance: Conditional module loading
- Maintainability: DRY code, single source of truth
- Testability: Isolated, reusable modules
- CSP Compatible: No inline JavaScript
- Translations: Centralized messages via
window.Flatboard.translations
Phase 1: Services and Helpers
-
Created
DiscussionListService:- Methods:
getDiscussionsPaginated(),separatePinned(),sortDiscussions() - Centralized pagination for discussions
- Separation of pinned discussions
- Methods:
-
Created
DiscussionService:- Methods:
checkCooldown(),checkDuplicate(),filterByPermissions() - Centralized business logic for discussions
- Records creations with
recordDiscussionCreation()
- Methods:
-
Created
PostService:- Methods:
validatePost(),checkCooldown(),checkDuplicate(),processMentions() - Centralized business logic for posts
- Processes @username mentions
- Methods:
-
Created
PaginationHelper:- Methods:
paginate(),getPaginationData() - Generates pagination data for components
- Methods:
-
Created
DiscussionHelper:- Methods:
formatDiscussionUrl(),getBreadcrumbs() - Formats URLs and generates breadcrumbs
- Methods:
-
Created
AttachmentHelper:- Methods:
normalizeAttachments(),validateAttachments(),sanitizeAttachmentUrl() - Validates and normalizes attachments
- Methods:
Phase 2: Controller Refactoring
-
Refactored
DiscussionController:index()usesDiscussionListService,Cache::remember(),RateLimiter::attempt()show()uses$this->authenticate()andDiscussionHelperstore()usesDiscussionServiceandValidator::validate()- All methods use native Flatboard methods
- Removed direct access to
Session::get('user_id')
-
Refactored
PostController:- Uses
PostServiceandAttachmentHelper - Centralized validation via
Validator::validate() - Replaced
$this->authenticate()for authentication
- Uses
-
Refactored
CategoryController:- Uses
DiscussionListService::getDiscussionsPaginated() - Optimized database pagination
- Uses
-
Refactored
SearchController:- Uses
SearchService::searchPaginated()for database pagination - Removed
array_slice()post-fetch - Integrated
Cache::remember()andRateLimiter::attempt() - Uses
Validator::validate()for search parameters
- Uses
Phase 3: ES6 JavaScript Modules
-
Created
discussion-show-manager.js:- Manages discussion display
- Post sorting (ASC/DESC)
- Pagination and keyboard navigation (J/K)
- Inline post editing
- Scroll to specific post (#post-123)
-
Created
discussion-form-manager.js:- Manages discussion creation/editing forms
- Real-time validation (title, content)
- Auto-save drafts
- AJAX submission
- Tag and category management
-
Created
post-edit-manager.js:- Manages post editing
- Loads form via AJAX
- Auto-save drafts
- @username mention autocomplete
-
Created
search-form-manager.js:- Instant search with debounce
- Result autocomplete
- Search history (localStorage)
- Keyboard navigation in suggestions
-
Created
frontend-bundle.js:- Global utilities (debounce, throttle, url, apiRequest)
- Markdown Editor functions (get/set value, focus)
- Toast notification system
- Bootstrap component reset
- Global event delegation for data-actions
Improvements
-
Systematic use of native Flatboard methods:
$this->authenticate()instead ofSession::get('user_id')Validator::validate()for all input validationRateLimiter::attempt()for rate limitingCache::remember()for cachingLogger::info()/Logger::warning()for loggingTranslator::trans()for internationalization
-
Elimination of anti-patterns:
- Removed direct access to
$_GET,$_POST,$_SESSION - Removed
exit()anddie() - Removed N+1 queries in controllers
- Removed business logic in views
- Removed direct access to
Improvement Metrics
| Metric | Before | After | Improvement |
|---|---|---|---|
| PHP Lines in DiscussionController | 1945 | ~800 | -59% |
| DB Queries (index page) | 15+ | 4 | -73% |
| Avg. Response Time | ~500ms | ~150ms | -70% |
| Inline JavaScript (show.php) | ~4000 | 0 | -100% |
| Reusable JS Modules | 0 | 5 | +5 |
Improvements
Complete Refactoring of VersionApiController
-
Multi-method Authentication:
- Session authentication (admin)
- API token authentication (Bearer or query param)
- Timing-safe token comparison with
hash_equals() - Flexible configuration via
security.public_version_check
-
New Endpoints:
GET /api/version/check-updates: Check for available updates (admin only)GET /api/version/changelog: Retrieve version history (public if configured)
-
Multiple Formats:
full: All information (default)simple/version: Version onlyminimal: Version + name
-
Performance:
- Version data cache (1 hour)
- Update check cache (24 hours)
- HTTP cache headers for public mode
-
Enhanced Security:
- Strict format validation
- Sensitive info (server_info) for admins only
- Reuse of
UpdateControllerlogic for update checks - Support for anonymous stats via
send_update_stats
-
Admin Diagnostics:
- Detailed server info (PHP version, extensions, memory limits, etc.)
- Available only for authenticated admins
-
Use of Native Flatboard Methods:
ApiControlleras base class for authentication and standardized responsesCachefor data cachingConfigfor centralized configurationLoggerfor audit trailTranslatorfor internationalized messageserrorResponse()andsuccessResponse()for standardized responses
Complete Refactoring of WebhookController
-
Multi-layer Security:
- IP whitelist validation (CIDR, wildcard, exact IP)
- Rate limiting via
RateLimiter(100 webhooks/hour/IP, configurable) - Mandatory HMAC signature with
timestamp.bodyformat - Protection against replay attacks (±5 minutes timestamp validation)
- Webhook deduplication via
X-Webhook-Idand cache (24h default) - Payload size validation (5MB max, configurable)
- Strict whitelist of allowed events
-
Asynchronous Processing:
- Immediate
202 Acceptedresponse (< 200ms) - Queue for background processing via
WebhookQueue - Automatic retry with exponential backoff (3 max attempts)
- Support for incoming and outgoing webhooks
- Immediate
-
Robust Validation:
WebhookValidatorfor event-specific validation- Native
Validatorfor validation rules - Required field validation per event type
-
Use of Native Flatboard Methods:
RateLimiterwith newwebhookkey (100 attempts/hour)Cachefor deduplication and rate limitingConfigfor centralized configurationLoggerfor audit trailTranslatorfor internationalized error messageserrorResponse()andsuccessResponse()for standardized responses
-
Additional Endpoints:
POST /api/webhook/test: Configuration test (admin only)GET /api/webhook/queue: Queue visualization (admin only)POST /api/webhook/retry/{jobId}: Manual retry of failed webhook (admin only)
-
Improved WebhookQueue:
- Static methods:
add(),getQueue(),getStats(),retry() - Support for incoming (
type: 'incoming') and outgoing (type: 'outgoing') webhooks - Clear distinction between webhook types
- Static methods:
-
Documentation:
- Created
docs/webhooks-config.mdwith complete configuration guide - Configuration examples for different environments
- Documentation of supported IP formats (CIDR, wildcard, exact)
- Created
-
Improved Maintainability:
- Factored code with private methods for each validation
- Robust error handling with try-catch and detailed logging
- Complete PHPDoc for all methods
- Constants for default values (MAX_PAYLOAD_SIZE, TIMESTAMP_TOLERANCE, ALLOWED_EVENTS)
Edited on Jan 14, 2026 By Fred .