ACL Resources in a Hyvä CMS Package
Hyvä CMS uses native Magento ACL permissions under one root, Hyva_Cms::cms. Every Hyvä CMS package nests its ACL resources under it, and so should any module that adds a content type. For Magento ACL itself, see Create an access control list rule in the Adobe Commerce docs.
The Hyvä CMS ACL Tree
The Hyva_Cms::cms root, titled "Hyvä CMS", sits under Magento_Backend::admin and is declared alone in src/liveview-editor/etc/acl.xml. Each entity group lives in the module that owns the entity, such as src/magento-cms/etc/acl.xml for pages and blocks. Your module nests its group under the root from its own etc/acl.xml and sequences Hyva_CmsLiveviewEditor in etc/module.xml.
A Hyvä CMS package comes in one of two shapes:
- Entity-shaped: things with an id, such as pages, menus or forms. One group per entity, one ACL resource per verb, named
<Vendor_Module>::<entity>_<verb>, such asHyva_MenuBuilder::menu_view. - Feature-shaped: capabilities, such as the Media Gallery or AI translation. A group of named ACL resources, or a single one like
Hyva_CmsAiTranslations::translate.
The prefix is always your own module's name.
ACL Verbs for Hyvä CMS Entities
An entity-shaped group holds one ACL resource per verb the entity supports. Nine shared verbs, in tree order:
| Verb | Title | Covers |
|---|---|---|
view |
View | Grids and listings, opening the editor, previews, reading version history and schedules, REST GET |
save |
Save draft | Creating and editing, saving a draft, restoring a version into the draft, import, core settings in the editor |
publish |
Publish | Publish and Publish & Close, restoring a version straight to live, REST POST and PUT |
delete |
Delete | Deleting the entity, REST DELETE |
export |
Export | Export through Import/Export and its REST routes |
version_delete |
Delete versions | Deleting a version from the version history |
version_organise |
Organise versions | Renaming, pinning and adding an emoji to a version |
schedule_save |
Schedule and reschedule | Queuing or rescheduling a publication |
schedule_cancel |
Cancel scheduled | Canceling a pending publication |
The version verbs sit in a <prefix>_version_history sub-group titled "Version history", the scheduling verbs in <prefix>_scheduling, titled "Scheduling". Copy titles and sort orders from the Hyvä CMS package.
Declare only the verbs your entity supports. The resolver reads them from the ACL tree, so acl.xml is the source of truth. Without publish, save is titled "Save" and REST POST and PUT check it.
When no shared verb fits, add a package verb, like Hyva_MediaGallery::asset_upload, and document it. Never reuse a shared verb's name.
This acl.xml declares a thing entity:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Magento_Backend::admin">
<resource id="Hyva_Cms::cms">
<resource id="Vendor_Module::thing" title="Things" translate="title" sortOrder="300">
<resource id="Vendor_Module::thing_view" title="View" translate="title" sortOrder="10"/>
<resource id="Vendor_Module::thing_save" title="Save draft" translate="title" sortOrder="20"/>
<resource id="Vendor_Module::thing_publish" title="Publish" translate="title" sortOrder="30"/>
<!-- delete and export follow the same pattern, then the sub-groups -->
<resource id="Vendor_Module::thing_version_history" title="Version history" translate="title" sortOrder="60">
<resource id="Vendor_Module::thing_version_delete" title="Delete versions" translate="title" sortOrder="10"/>
<resource id="Vendor_Module::thing_version_organise" title="Organise versions" translate="title" sortOrder="20"/>
</resource>
</resource>
</resource>
</resource>
</resources>
</acl>
</config>
For a complete one, see src/magento-cms/etc/acl.xml in the Hyvä CMS package.
Hyvä packages use sortOrder bands under the root: 10 to 80 (CMS entities and Import/Export), 100 to 135 (Menus, Forms, Blogs, Posts, Email and Newsletter templates) and 200 to 225 (Media Gallery, Dashboard, AI generation, AI translation). Pick a value outside them.
The Content Type Authorization Resolver
Shared surfaces, such as the editor, version history, scheduling and Import/Export, serve every content type. They ask Hyva\CmsLiveviewEditor\Model\Security\ContentTypeAuthorization whether the admin user may apply a verb to the request's content type.
Register your content type token, the one you give the ProviderPool, in the global etc/di.xml. Not etc/adminhtml/di.xml, or REST and SOAP never see it:
<type name="Hyva\CmsLiveviewEditor\Model\Security\ContentTypeAuthorization">
<arguments>
<argument name="resources" xsi:type="array">
<item name="thing" xsi:type="string">Vendor_Module::thing</item>
</argument>
<argument name="labels" xsi:type="array">
<item name="thing" xsi:type="string">thing</item>
</argument>
</arguments>
</type>
resources maps the token to a prefix and the resolver appends _<verb>, so thing and publish resolve to Vendor_Module::thing_publish. labels names the type in denial messages. The shared surfaces then enforce your ACL resources with no more code.
In your own code, call isAllowed() or assertAllowed() with a content type and a VERB_* class constant, getWriteVerb() to pick publish or save, and getAllowedVerbs() for one boolean per shared verb. A verb the content type doesn't declare is always denied.
An unregistered content type falls back to Magento_Backend::content for every verb and logs a warning to var/log/hyva_cms.log. That keeps older modules working. Don't rely on it.
Where Each Hyvä CMS Surface Is Gated
Surfaces scoped to one entity type use the verb's ACL resource directly, and view for listings and menu.xml. Surfaces serving several types use the root plus the resolver:
- An admin controller that learns the content type from the request sets
ADMIN_RESOURCEtoHyva_Cms::cms, then callsassertAllowed()before acting. A mass action checks every item first and refuses the whole action if one fails. - A Connect action in the editor checks the resolver, one verb per action.
- A REST route that carries the content type in its payload references
Hyva_Cms::cmsinwebapi.xml. A plugin registered inetc/webapi_rest/di.xmlandetc/webapi_soap/di.xmldoes the per-item check, not the repository, because cron, observers and queue consumers call the repository with no admin session.
Hiding Editor Controls with Capability Flags
The Liveview editor hides controls the admin user can't use, from a permissions object built by getAllowedVerbs(). In a template, use the EditorPermissions view model, whose can() also takes package verbs:
/** @var EditorPermissions $permissions */
$permissions = $viewModels->require(EditorPermissions::class);
$canPublish = $permissions->can('publish', $contentType);
A header menu option declared in layout XML can name its verb in an acl_verb argument, and the editor drops it when can() returns false.
Hiding is a courtesy, not the gate
A hidden button doesn't stop a request. The controller, the REST route or the resolver refuses the action, so every action still needs its server-side check.
Upgrading Existing Admin Roles
Nothing migrates. Grant the new ACL resources by hand to any custom role or REST integration that reached Hyvä CMS through Magento_Backend::content. The Administrators role holds Magento_Backend::all, so it's unaffected.
ACL Checklist for Hyvä CMS Modules
Pick the list that matches what you're adding.
New admin controller
- Declare a literal
ADMIN_RESOURCE: the verb's ACL resource, orHyva_Cms::cmsplus a resolver check if the controller serves several content types. - On a mass action, check every item before acting on any.
New REST route
- Reference the verb's ACL resource in
webapi.xml:GETview,POSTandPUTpublish (or save),DELETEdelete. - For a route carrying the content type in its payload, reference the root and check per item in a
webapi_restandwebapi_soapplugin.
New content type
- Declare the group and one ACL resource per supported verb under
Hyva_Cms::cms. - Register the token in
resourcesandlabelsin the globaletc/di.xml. - Gate editor controls with
EditorPermissions::can().
New package
- Add an
etc/acl.xmlnested underHyva_Cms::cms, with a sort order outside the Hyvä bands. - Sequence
Hyva_CmsLiveviewEditorinetc/module.xml. - Choose entity-shaped or feature-shaped, and document any package verb.
- Give a settings section its own resource under
Magento_Config::config. - Never gate anything on
Magento_Backend::content, and never ship a data patch that grants your ACL resources to existing roles.
Related Topics
- Extending for Other Content Types - The full walkthrough for a new content type, with the ACL step in 4.6
- APIs - The Hyvä CMS REST APIs that these ACL resources gate
- Instance Components - An example of one entity's ACL resources