v0.5.1
<p>Locked records and addon-shipped pages are enforced, pages can be moved, the query engine is split out of the registry, and fields gain previews, a pages fieldtype and color palettes.</p>
1 October 2026
Added
Page::move($newPath)moves a page and its subtree. It refuses a locked page or descendant, an existing destination, a missing parent and a move into itself. It drops the image variants recorded under the old URL and re-indexes both subtrees.page:moveuses it.User::new(),User::save()andUser::delete(). Saving emitsuser.update:afterand deleting emitsuser.delete:before, so the field index follows.user:create,user:delete,make:page,page:deleteandpage:moveuse the record API instead of writing folders themselves.App::item($uuid)and theitem()template function. Item uuids are site-wide unique, soItem::registryKeyFor()takes only the uuid, andItem::fromRow()builds an item from a registry row.phlat addons:mount [alias]creates the real page an addon's mount slug needs.History::changes()andHistory::revertField(): field-level changes between HEAD and the working tree, and reverting one field. The Changes page lists them.- Users are indexed for search (username and profile fields). Fields nested in
stack,blocksandrepeaterare judged by their own type throughFieldType::indexChildren(). Index text is the stored value, not rendered output (FieldType::indexText()). Registry schema is 13. FieldType::preview()and an optionalfieldtypes/{name}/preview.latte(App::fieldtypePreviewTemplate(),FieldValue::previewTemplate()). The admin shows a collapsed field's preview. Entries in stack, blocks and repeater collapse with a summary.- The
pagesfieldtype: an ordered list of pages stored as uuids, so a rename or move never breaks one. It skips pages that no longer exist. colortakes#hex,rgb(),hsl()andoklch(), stored in the notation it was written in. Apaletteoption limits it to swatches.rgb(),hsl(),hex(),contrast(),lighten()anddarken()are available in templates, backed by the newToolkit\Color.codestores{language, code}with a language per entry. A bare string still reads as code in the field's default language.numbertakesmin,max,step,unitanddisplay(inputorrange), and validatesmin/max.selecttakesdisplay(dropdownorbuttons) andbadge.datetimetakesformat.listhas a tag editor.- A view's
fieldsentry with no global definition can define itself inline:{"primary": {"type": "color", "label": "Primary"}}. - Global option fields
badge,display,format,max,min,paletteandstep. - A
bookmarksfield on the admin root page. Each page becomes a shortcut in the admin nav. - Click-to-edit:
data-edit-fieldregions on the front end highlight and open their field while the edit panel is open. Arr::setPath(),Str::matchAll(), andwoff/woff2inF::mime().- Tasks in
phlat/taskfile.ymlforbuild,watch,watch:admin,sync,deploy,deploy:rollbackanddep.npm run buildalso builds the admin JS (build:admin:js).deploy.example.phpis rewritten for the current layout. - System tests for addon mounts, addon page locks, field definitions, locked records and page move, and a test for
Toolkit\Color.
Changed
- Addon mounting is explicit. A mount needs both the config slug and a real page in
data/pages/whose view is the addon's. The addon's content and saves live in that page, and the addon's ownpages/data.jsonsupplies defaults under what was saved (AddonPage::publishedData()). The addon is not served until the page exists. AddonPage::children()lists from the addon's ownpages/folder, andAddonPage::url()always builds from the mount URL.- The Clockwork mount is
clockworkinstead of__clockwork, and its API is at/clockwork/.phlat/addons/clockwork/pages/index.jsonis gone. - The admin's
docs,registryandtemplatespages moved undersystem, andlogoutunderprofile. - Locked records are enforced.
Record::save()accepts only a change tolockedon a locked record.delete()andPage::autosave()throwForbiddenException, andPage::delete()refuses when a descendant is locked. - Records and files are only written inside
data/(Record::assertInData(), a write guard inPageFiles). Sub-pages an addon ships and the guest user are read-only. Recordreads its envelope fromindex.jsononly. The registry$rowconstructor argument is removed.- The query DSL compiler moved from
RegistrytoData\Query, and image variant bookkeeping moved fromRegistrytoPageFiles. PageFilesmethods no longer take$parentUuid.PageFiles::buildImage()rejects anything that isn't a resizable image.Historyre-indexes afterrestore,pullandswitchBranch, since git rewrites files the registry never saw.- Error pages render with the site's global controller data.
- A response rendered for a signed-in session is never cached, whatever the view's
cachesays. - Admin routes return 403 when requested from an iframe (
Sec-Fetch-Dest: iframe). phlat/Caddyfilerestarts the worker when a.lattefile changes.page:delete,page:moveandmake:pageno longer maintain afolderslist in the parent'sindex.json.
Fixed
- An addon view with no
index.jsonfell through to the site view of the same name. FieldTypeListstored the empty entries a tag editor posts.
Removed
FieldIndexno longer indexes field definitions.
Comments
No comments yet. Be the first!
Loading form…