Flarum 2.0.0-rc.2 Released ๐
Six weeks ago we shipped 2.0.0-rc.1 and asked you to do one thing: run it on real forums and tell us what breaks. You did โ and 2.0.0-rc.2 is the result. 41 merged PRs, almost all of them fixes traced directly back to reports from people running rc.1 in production, on staging, and on the nightly demo.
โ
Still production-safe for early adopters, and the upgrade from rc.1 is a plain composer update. Back up your database, test on staging if you can, keep a rollback plan โ the usual.
๐ This forum is already on it. discuss.flarum.org has been updated to rc.2.
๐งช Want to try before upgrading? The nightly demo at nightly.flarum.site is rebuilt daily from the latest 2.x code.
๐งญ Where we are
Nothing about the release-candidate promise from rc.1 has changed:
- The API is still frozen. An extension built against rc.1 works unchanged on rc.2 and will work on 2.0.0 final.
- The upgrade path stays clean. rc.1 โ rc.2 โ 2.0.0 is
composer update the whole way.
- Regressions go to the top of the pile. That's exactly what this release is made of.
rc.2 is a maintenance and hardening release โ no new surface area to speak of, just a lot of real-world rough edges sanded down. If rc.1 was "we made it to RC", rc.2 is "you found the bugs, we fixed them." Please keep them coming.
Where to report:
- Bugs in core โ flarum/frameworkissues
- General feedback, questions, extension behaviour โSupport
- Security issues โ
security@flarum.org (please don't post publicly)
๐ ๏ธ What changed in rc.2
๐ Notifications & email
The biggest cluster this cycle. Several people reported duplicate notifications and emails under load โ all now fixed:
- Notification and email duplicates from a queue race in
NotificationSyncer are eliminated.
- The notification dedup query never matched non-null
data on MySQL, which is what let duplicates through in the first place.
- Transactional emails now send in the recipient's preferred locale rather than the sender's or the forum default.
- The email logo now resolves via the
flarum-assets disk, so it renders correctly regardless of storage configuration.
- The SMTP ping threshold dropped to 20s to survive idle-disconnects from relays that hang up aggressively.
๐ก Realtime on iOS Safari
@ekumanov did a deep dive on WebSocket behaviour when iOS Safari backgrounds a tab, and landed a series of fixes:
- Reconnect and catch up on missed events after the tab returns from the background.
- Reconstruct Pusher on reconnect to get around iOS's
lives:2 connection budget.
- Only force-reconnect on
visibilitychange on iOS, leaving other platforms alone.
- Plus correct boolean-setting casting and ordering
realtime:info users by joined_at.
๐๏ธ Queue & jobs
- Per-class queue routing on
AbstractJob is restored โ jobs can again declare their own queue.
QueueFactory::getPausedQueues() keeps Flarum compatible with illuminate/queue v13.11+.
- Orphaned queued jobs no longer spam duplicate
ModelNotFoundException logs, and EloquentBuffer now guards against orphaned/mock models.
๐ Search
- PostgreSQL search configuration handling is hardened.
Extend\SearchDriver class-string types widened to SearcherInterface so custom drivers type-check cleanly.
- The search clear button no longer opens the modal when clicked, and is hidden when collapsed in tablet mode.
๐ฑ Frontend & mobile polish
- Mobile discussion total post count now updates when you reply โ the scrubber header was rendering a reused vnode that went stale (#4602).
- User badges sit correctly next to the username instead of drifting left of the avatar.
- Native tooltip flash, async teardown leaks, and dynamic tooltip text are all resolved.
- Flash of unstyled content is gone โ the async-CSS dual path was replaced with a render-blocking stylesheet.
text-overflow: ellipsis restored on long button labels, and the admin extension danger badge stays visible on long names.
TextEditor.onbuild no longer races against an async Mithril redraw.
- Browsers without XSLT support boot again thanks to a bundled polyfill.
๐ Privacy & correctness
- GDPR export downloads are hardened with an audit trail, single-use links, and an active expiry check.
- The
UserResource groups relationship is scoped to viewable groups, so hidden group membership doesn't leak.
UserSecurityPage no longer crashes when the user model loads asynchronously.
- Discussion
PATCH responses surface event posts, and title changes route through rename().
- The profile author filter uses the user slug, not the username.
๐ท๏ธ Extensions
- (tags) the subquery for permitted tag IDs is restored (a regression from #4502), and legacy unused background columns are dropped from the tags table.
- (flags) the literal
'undefined' is gone from the Inappropriate reason, and FlagPostModal is lazy-loaded.
- (nicknames) min/max length settings are cast to int before reaching
Schema\Str.
โก Performance
JsDirectoryCompiler skips MIME detection when the file extension already tells it what it needs to know.
๐ Thank you
rc.2 exists because people ran rc.1 and reported what they found. Special thanks to @ClaudiusH, who continues to run the nightly builds and surface bugs big and small โ including the mobile post-count regression fixed here.
Contributors this cycle:
๐ Try it / upgrade
- Live demo โ nightly.flarum.site, rebuilt daily
- Right here โ discuss.flarum.org is running rc.2
- Update your install โ
composer update -W, then php flarum migrate, php flarum cache:clear, and php flarum assets:publish (back up first, and test on staging if you can)
๐ Changelog
Added
- (core) per-class queue routing on
AbstractJob by @IanM #4656
- (core)
QueueFactory::getPausedQueues() for illuminate/queue v13.11+ compatibility by @forumaker #4673
Fixed
- (core) add XSLT polyfill so the forum still boots on browsers without XSLT support by @IanM #4359
- (core) misaligned user badges in posts โ positioned relative to the username instead of the avatar by @luceos #4582
- (core) hide the search clear button when collapsed in tablet mode by @huoxin233 #4594
- (core) prevent the search modal from opening when the clear button is clicked by @huoxin233 #4595
- (core) prevent discussion list cache wipe on back-navigation to tag pages by @IanM #4598
- (core) send transactional emails in the recipient's preferred locale by @IanM #4627
- (core) restore
text-overflow: ellipsis on long button labels by @IanM #4628
- (core) scope the
UserResource groups relationship to viewable groups by @IanM #4629
- (core) add missing
getHidden() to the Log\Context\Repository stub by @IanM #4633
- (core) avoid duplicate
ModelNotFoundException logs on orphaned queued jobs by @IanM #4634
- (core) resolve
VersionerInterface from the container and cache rev-manifest reads by @IanM #4638
- (core) track avatar HiDPI variants on users to remove
srcsetFor() filesystem calls by @IanM #4639
- (core) resolve the email logo URL via the
flarum-assets disk by @IanM #4640
- (core) lower the SMTP ping threshold to 20s to survive idle-disconnect from relays by @IanM #4641
- (core) notification dedup query never matching non-null data on MySQL by @IanM #4645
- (core) notification and email duplicates from a queue race in
NotificationSyncer by @IanM #4647
- (core)
TypeError in the Revised event when post content is NULL by @IanM #4648
- (core) prevent
UserSecurityPage crash when the user loads asynchronously by @datlechin #4651
- (core) send the user slug, not username, for the profile author filter by @IanM #4655
- (core)
TextEditor.onbuild race against async Mithril redraw by @IanM #4657
- (core) surface event posts in discussion
PATCH responses and route title changes via rename() by @IanM #4658
- (core) harden pgsql search configuration handling by @IanM #4663
- (core) replace the async-CSS dual path with a render-blocking stylesheet to fix FOUC by @IanM #4664
- (core) widen
Extend\SearchDriver class-string types to SearcherInterface by @IanM #4668
- (core) guard
EloquentBuffer against orphaned/mock models by @IanM #4669
- (core) resolve native tooltip flash, async teardown leaks, and dynamic tooltip text by @huoxin233 #4674
- (core) mobile discussion total post count not updating on reply by @IanM #4678
- (admin) keep the extension danger badge visible on long names by @IanM #4660
- (realtime) reconnect the WebSocket and catch up on missed events after iOS Safari backgrounding by @ekumanov #4590
- (realtime) cast boolean setting correctly by @IanM #4624
- (realtime) order users by
joined_at in realtime:info by @IanM #4630
- (realtime) reconstruct Pusher on iOS reconnect to bypass the
lives:2 budget by @ekumanov #4654
- (realtime) only force-reconnect on iOS
visibilitychange by @IanM #4662
- (gdpr) harden export download with an audit trail, single-use links, and an active expiry check by @IanM #4642
- (tags) restore subquery for permitted tag IDs (regression from #4502) by @IanM #4649
- (flags) drop literal
'undefined' from the Inappropriate reason and lazy-load FlagPostModal by @IanM #4659
- (nicknames) cast min/max length settings to int before passing to
Schema\Str by @IanM #4599
Changed
- (tags) drop legacy unused background columns from the tags table by @Abdooo2235 #4529
Performance
- (core) skip MIME detection in
JsDirectoryCompiler when the file extension already matches by @IanM #4632