Skip to content

[2.x] Rework the admin settings page - #507

Merged
imorland merged 1 commit into
2.xfrom
im/admin-page-rework
Aug 10, 2026
Merged

[2.x] Rework the admin settings page#507
imorland merged 1 commit into
2.xfrom
im/admin-page-rework

Conversation

@imorland

@imorland imorland commented Aug 10, 2026

Copy link
Copy Markdown
Member

Changes proposed in this pull request:

The page was a single ~800 line scroll with eleven fieldsets, everything visible at once. Saved data formats are unchanged throughout — this is presentation only.

1. Four tabs

Files, Storage, Images, Advanced. Only the active panel renders. Each tab carries an unsaved-changes marker, since one save button now serves four panels.

The active tab lives in the page route parameter, following fof/seo's admin pages. This matters: the admin panel routes on the hash (m.route.prefix = '#'), so an earlier version that wrote the tab key into location.hash replaced the route and bounced to the dashboard on reload. Using the route also makes each tab a real link — bookmarkable, middle-clickable, works with browser Back.

2. File types are no longer edited as raw regexes

A pattern that decomposes cleanly renders as a media type plus one chip per subtype:

image/  [jpeg ×] [png ×] [gif ×] [webp ×] [svg+xml ×]  [+]   </> Edit pattern

Anything that does not decompose — no anchors (text\/plain), wildcards (^image\/.*$), optional characters (jpe?g), nested groups, alternation in the type — keeps its raw input untouched and is marked "Advanced pattern". Silently rewriting a working pattern would be far worse than showing a regex. Every row can switch to raw editing regardless, so a technical admin is never boxed in.

An "Add file types…" picker covers the groupings forums actually ask for (Images, PDF, Word documents, Spreadsheets, Archives, Audio, Video), plus "Custom pattern…".

3. Row order is now visible and adjustable

getMimeConfiguration() matches with ->first(), so the first matching pattern wins — invisible in the old UI, and a correctness trap: adding a broad rule above a specific one silently changes where files go. Rows now have move up/down controls and a note explaining the behaviour. Verified that object key order survives the JSON round trip in both JS and PHP.

4. S3 rebuilt around a provider preset

AWS, Cloudflare R2, Backblaze B2, DigitalOcean Spaces, MinIO, Other. Choosing one sets the endpoint format, addressing style and ACL support that an admin previously had to know from the provider's own documentation — R2, for instance, needs path-style addressing and rejects ACLs, none of which the page said. Existing configurations are detected from the saved endpoint, so upgraders see their provider already selected.

The two fieldsets ("AWS S3 storage settings" / "Advanced S3 storage settings") are merged — nothing previously indicated that a non-AWS provider needs fields from both. Help text rewritten to say when each field applies; the old s3_compatible_storage string ("only required when using S3 compatible storage") is gone.

5. Storage visibility

Services are shown when configured or in use by a file type — configuring storage before assigning anything to it is a normal order to work in, and a part-configured service must not vanish. An explicit "Configure another storage service" affordance reveals the rest.

Services whose composer package is absent never appeared at all, so admins had no way to discover S3 or Qiniu existed. There is now a link to install instructions, which the README previously did not contain (it documented configuring S3 but never installing it).

6. Bug fix: blank template descriptions

The download-template list rendered every description blank, because it used React's dangerouslySetInnerHTML rather than Mithril's m.trust.

Reviewers should focus on:

  • mimePatterns.ts parse/build round trip. This rewrites saved config, so it is the highest-risk part. It refuses anything it cannot rebuild byte-identically. Tests cover both patterns from a real forum plus the shipped default.
  • The js/ infrastructure changes"type": "module", webpack.config.js.cjs, and five new devDependencies. Needed for Jest; called out separately below.
  • Whether the four tab groupings are right. Moving a setting between tabs is trivial; say if anything is in the wrong place.

Frontend testing

Adds Jest via @flarum/jest-config, matching the bundled extensions (tags, mentions, embed). Standalone use needs four workarounds for flarum/framework#4689, each documented inline in jest.config.cjs:

  1. setup-env.js imports @flarum/core → mapped to the Composer-vendored copy
  2. test-matchers.ts is TypeScript inside node_modules, never transformed → scoped transformIgnorePatterns
  3. Core deep-imports @babel/runtime/helpers/esm/*, blocked by that package's exports map → mapped directly
  4. @babel/runtime v8 removed those helpers → pinned ^7, matching the monorepo

Worth noting: @flarum/jest-config is published to npm (2.0.0) — issue #4689 states otherwise, so that part appears out of date. The real blockers were the four above. Happy to write this up on the issue if useful.

enable_tests: true added to frontend.yml. 37 tests, all passing.

Confirmed

  • Frontend changes: tested on a local Flarum installation.
  • Backend changes: tests are green (run composer test).

Testing

  • 37 new frontend unit tests (pattern round trip, refusal cases, injection safety, presets)
  • Full backend suite green: 145 unit + 98 integration. PHPStan clean. tsc --noEmit clean. Prettier clean.

The settings page was a single ~800 line scroll with eleven fieldsets and
everything visible at once. Users reported it as complicated, badly laid
out, and confusing to configure — S3 especially.

Splits it into four tabs (Files, Storage, Images, Advanced), with the
active tab held in the `page` route parameter so it survives a reload and
each tab is a real link. The admin panel routes on the hash, so writing
the tab into location.hash replaced the route and bounced to the
dashboard; this follows fof/seo's approach instead.

File types are no longer edited as raw regexes. A pattern that decomposes
cleanly renders as a media type plus one chip per subtype; anything that
does not — no anchors, wildcards, nested groups — keeps its raw input
untouched and is marked advanced, because silently rewriting a working
pattern is worse than showing a regex. Every row can switch to raw
editing regardless. Presets cover the groupings forums actually ask for.

Row order is now visible and adjustable: the backend matches with
first(), so the first matching pattern wins, which the old UI never
showed.

S3 is rebuilt around a provider preset (AWS, R2, Backblaze B2, Spaces,
MinIO, Other) that sets the endpoint format, addressing style and ACL
support an admin previously had to know from the provider's own docs.
Existing configurations are detected from the saved endpoint. The two
fieldsets are merged, and help text rewritten to say when each field
applies rather than only what it is.

Storage services are shown when configured or in use, with an explicit
affordance for setting up another — configuring storage before assigning
a file type to it is a normal order to work in. Services whose composer
package is absent now link to install instructions rather than being
silently absent.

Saved data formats are unchanged throughout; this is presentation only.

Also fixes the download template descriptions, which rendered blank
because they used React's dangerouslySetInnerHTML rather than m.trust.

Adds Jest for pure-logic frontend tests, following the bundled
extensions' @flarum/jest-config setup. Standalone use needs four
workarounds for flarum/framework#4689, each
documented in jest.config.cjs. 37 tests cover the pattern round trip,
which is the part that could corrupt saved config — and they caught a
real bug, where + was rejected in subtypes, so svg+xml (in the shipped
default pattern) could not be entered.
@imorland
imorland requested a review from a team as a code owner August 10, 2026 21:56
@imorland
imorland merged commit 5868baf into 2.x Aug 10, 2026
20 checks passed
@imorland
imorland deleted the im/admin-page-rework branch August 10, 2026 21:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant