# Widgets belong to a place, and "Page Builder" is what this is

2026-08-10. Two requests from Ernest, and they turn out to be one idea:

> Widgets (instead of picking forums to show on change to routes to show on
> /forums /blog /downloads etc)

> Change Arrange Widgets to Page Builder because that's what you are doing is
> building a page — doesn't matter if it's on the forum page or blogs.

---

## Why the forum picker was the wrong control

Conditions were `{forums: [3, 7], pages: [2]}` — two lists of ids, added when
Forums was effectively the whole product. Since then Blog, Events, Gallery, the
Knowledge Base, Downloads, Clubs and Pages all render widget areas, and none of
them can be named by a forum id.

So the control asked the wrong question. "Which forums?" is a *sub-question of
Forums*, and what somebody arranging a page actually wants to say is **"this
one goes on the Downloads pages"**. There was no way to express that at all.

The rename lands in the same place from the other direction: if what you are
doing is only meaningful on the forum index, "arrange widgets" is a fair name
for it. Once it works on any application's pages, you are building a page.

## Sections, registered by the application that owns them

`WidgetSections` in System, alongside `WidgetAreas` and `WidgetTypes`, and
registered the same way — in `register()`, never in `boot()`, for the reason
the areas registry already documents.

```php
$sections->register('downloads', [
    'label' => 'download.section',       // a translation key
    'paths' => ['/downloads', '/download/'],
]);
```

An application declares the path prefixes it owns. That is a thing it can state
honestly, because it also owns the routes — the same argument that makes the
areas registry trustworthy.

🚨 **The section is resolved from the request path, not from the controller.**
Every controller *could* have shared one, and eleven of them would have
forgotten. The path is already the truth about where somebody is, and it is
available whether or not the controller that answered has ever heard of
widgets. Longest matching prefix wins, so `/downloads` beats a `/` catch-all.

## Pages keep their ids, and that is not an inconsistency

A page lives at its own slug on the site root — `/rules`, not `/page/rules` —
which is the whole point of how Pages was built. **No path prefix can name
one**, so a section cannot either, and the page picker stays exactly as it is.

The two controls answer different questions and both are worth having:
sections say *which application's pages*, the page list says *which specific
page*. What is being removed is only the forum picker, which is the one Ernest
named and the one that was pretending an application-level question was a
Forums question.

## What happens to the forum ids that were there

Every `conditions` column on this install is null, but dev and production may
differ, so the migration is written rather than assumed: `{forums: [...]}`
becomes `{sections: ['forums']}`.

🚨 **That is a widening, and it is recorded because this kind of change should
never be silent.** A widget pinned to forum 3 will show on every forum. The
alternative — dropping the condition — would put it on the whole site, which is
further from what its author meant. The narrow rule cannot be kept, because the
control that set it no longer exists and an invisible condition that no screen
can show is worse than a visible widening. The migration counts what it
converted so the number appears in the migration output rather than nowhere.

## Keeping "who" and "where" apart

Unchanged, and worth restating because it is what makes the model work: group
membership answers *who*, sections answer *where*, and neither knows about the
other. That is why a widget can be staff-only on Downloads without either rule
having to be aware of the other one.

## The rename

"Arrange widgets" → **Page Builder**, in the account menu and everywhere else
it is named. The route stays `/widgets/arrange/*`: renaming a URL breaks any
bookmark for a cosmetic gain, and nobody types this one. The strings are
translation keys now, so the label is a language file entry rather than a
literal in six templates.
