Repository navigation
v2.15.0
·
74 commits
to main
since this release
Immutable
release. Only release title and notes can be modified.
Security
- The Site Editor API now checks abilities, not just authentication — Breaking for hosts whose users relied on the old access. Before 2.15.0, any authenticated user could read and write global styles, templates, template parts, menus, menu items, menu locations, patterns and synced blocks: every Form Request's
authorize()returnedtrue, and the delete actions had no check at all. A low-privilege account could restyle the whole site, point fonts and backgrounds at an outside server, rewrite templates and menus, or wipe the admin's global styles. Every/api/v1Site Editor route now needs thereadability (manage-content; a user with the structure ability passes too), WordPress'sedit_postsanalogue. Writes also need their group's ability: global styles, templates, template parts, menus, menu items and menu locations needmanage-themes(WordPress'sedit_theme_options), and patterns and synced blocks needmanage-content. Users without them get403. The abilities come from the newSiteEditorAbilitiesclass and can be remapped with theap.cmsFramework.abilities.siteEditor.read,.structureand.contentfilters.PermissionsTableSeederalready grantsadminboth abilities andeditormanage-content. Upgrade: grant these abilities to any custom role that uses the Site Editor. visual-editor's own Site Editor routes are unaffected; they go through itsEnsureSiteEditorAccessgate. - Global-styles values that could break out of the stylesheet are dropped (#385, #387) —
GlobalStylesEmittervalidates everysettings.customvalue, everystyles.*declaration value (aftervar:preset|…translation), every preset value and every@font-facedescriptor before writing it. It drops anything containing; { } < >, a backslash, a comment marker or a control character, a string left open, or a parenthesis or bracket outside a string that is unbalanced or crossed. An unmatched(or[would otherwise swallow every rule after it.styles.blockskeys must be a singlenamespace/namein WordPress's block-name charset. Preset slugs andvar:preset|…segments are normalized to[a-z0-9-](#380). - Site Editor block content runs through a filter before it is saved —
TemplatesController,TemplatePartsController,BlocksControllerandBlockPatternsControllerpass incomingblock_contentthrough the newap.cmsFramework.siteEditor.blockContentfilter ($blocks,$storedBlocks,$context) on every create, update and upsert, including the unique-violation retry.$storedBlocksis the record's current block content; on the first DB override of a theme-file template, template part or pattern it is that file's parsed blocks, so Custom CSS the theme ships survives the first save; on any other create it isnull;$contextistemplate,template-part,blockorblock-pattern. A callback that returns something other than an array is ignored. Before 2.15.0 these endpoints wrote the block tree as sent, so a user who could not edit per-block Custom CSS in visual-editor could still set it through cms-framework's API. visual-editor 1.15 hooks this filter to enforce itsvisual-editor.edit-block-cssability here too. With no callback registered, nothing changes.
Added
@font-facerules fromtheme.json(#382) — everysettings.typography.fontFamilies[].fontFaceentry becomes an@font-facerule, so fonts a theme bundles the WordPress way load without hand-written CSS.file:./assets/…sources resolve through thethemes.assetsroute. The URL keeps only its path, so subdirectory installs work and the cached CSS doesn't pin a host.https://and root-relative sources pass through. Otherfile:paths,..segments,%-encoded paths and anything that could break out ofurl("…")are skipped. MissingfontStyle/fontWeight/fontDisplaydefault toWP_Font_Face'snormal/400/fallback.- Shadow presets, font-family classes, more elements and element states (#382):
settings.shadow.presetsemit--wp--preset--shadow--{slug}.- Font families get
.has-{slug}-font-familyclasses. elements.h1–h6,elements.captionandelements.citeemit at the root and per block.linkandbuttonelements emit WordPress's:link,:any-link,:visited,:hover,:focus,:focus-visibleand:activestates, in that order.
outline.*styles —outline.color,outline.offset,outline.styleandoutline.widthemit asoutline-*declarations at the root, on elements and their states, and on blocks. WordPress themes put their focus indicators here (for exampleelements.button[":focus"].outline).- WordPress pattern headers (#383) — theme pattern files'
Keywords:,Inserter:,Template Types:andPost Types:headers are read and exposed askeywords,inserter,template_typesandpost_types. They appear on the/block-patterns/patternsresponses, onResolvedPatternand intoFilterEntry().Inserter:hides a pattern unless it isyes,true,0or empty.Post Types:is lower-cased; empty means every post type.- Keywords are comma-separated, so a phrase such as
call to actionstays one keyword. - Header names now match case-insensitively, as in WordPress's
get_file_data().
- User patterns store the pattern headers — a new migration adds
keywords,inserter,template_types,post_typesandviewport_widthcolumns toblock_patterns. The pattern endpoints accept and validate them:post_typesentries must be lowercase post-type keys, andviewport_widthmust be 320–2560.PatternResolver::cloneToUser()copies them, along with the theme pattern's block content, which clones previously lost. Runphp artisan migrate. ap.cmsFramework.globalStyles.cssfilter —GlobalStylesEmitter::emit()passes its CSS and theResolvedGlobalStylesthrough this filter on every call, after the cache. A host without visual-editor can append block style variation CSS, or anything else, without it being frozen into the cache. The@cmsGlobalStylesdirective andGET /api/v1/global-styles/cssboth carry the filtered CSS.
Changed
- Element, block and block-element rules use WordPress's flat
:root :where(…)specificity (#390) — behaviour change. Every element, block and block-element rule is now (0,1,0), so source order decides between them, as in WordPress 6.6+..has-*preset classes and inline styles still win.- Root element rules (
a,h1…) used to be (0,0,1). They now beat bare element selectors in a theme'sstyle.css/editor.css, even though those files load after the emitter. - Block-element rules (
.wp-block-quote a) used to be (0,1,1). They now lose to a later (0,1,0) rule. - Upgrade: move element colors and spacing into
theme.json, or use class selectors instyle.css.
- Root element rules (
- Link styles no longer restyle buttons —
elements.linknow targetsa:where(:not(.wp-element-button)), WordPress's link selector, instead of everya, so a link color can't override a button rendered as a link. - Pseudo states only apply to links and buttons — matching WordPress,
:hover,:focusand the other states underheading,h1–h6,captionorciteare ignored. - Block style variation partials are no longer theme style variations (#389) — a
styles/*.jsonfile that declaresblockTypes(theme.json v3 block style variations; any value, as in WordPress'sstyle_variation_has_scope()) is no longer listed by/global-styles/variationsor applied as a whole-theme variation. A stored pick of one, or of a variation whose file is gone, now resolves asvariation: null. The stored slug stays in the DB row. Block style variations are emitted by visual-editor, not by@cmsGlobalStyles. post_typesreaches visual-editor — theme patterns with aPost Types:header are now only offered for those post types in the visual-editor inserter, as in WordPress.- The global-styles emitter cache schema moves to
v10, so cached CSS regenerates on upgrade.
Fixed
- Theme-asset font URLs keep the app's base path and skip empty asset paths.
- A failed pattern, template or menu write is no longer reported as a slug conflict — the pattern, synced-block, template, template-part and menu controllers treated every SQLSTATE
23000error (aNOT NULLor foreign-key failure too) as a duplicate slug and answered409. They also searched the exception message, which includes the query's bound values, so a title containing "unique" could match. Only real unique violations count now. /blocksresponses include the pattern-header fields the endpoint accepts (keywords,inserter,template_types,post_types,viewport_width).