Search Docs
Loading...
Skip to content

Lock the Template

Set up a two-surface integration where template creators have full editing access while template adopters can only modify designated areas.

8 mins
estimated time
GitHub

Many integrations need two different editing experiences: one for designers who build templates, and one for end users who customize them. The Creator and Adopter roles make this possible—same CE.SDK, different permissions based on who’s using it. For detailed scope configuration patterns, see Lock Content.

The example builds a brand template with a logo and a headline, marks only the headline as editable, then switches between the Creator and Adopter roles to show how the effective permissions change.

Understanding the Two-Surface Pattern#

Template-based workflows typically involve two distinct user groups with different needs:

Surface Users Role What they can do
Creator Surface Designers, admins Creator Full editing—build templates, set locks
Adopter Surface End users, marketers Adopter Restricted editing—only modify unlocked areas

This separation protects design intent while enabling customization. Each role installs its own global scope defaults: the Creator role sets every global scope to .allow, so block-level locks are never consulted. The Adopter role defers the global scopes to each block’s own settings, so the locks configured by creators decide what users can modify.

Setting Up the Creator Surface#

The Creator surface is where templates are built. Call engine.editor.setRole(_:) with "Creator" to give designers unrestricted access. New engine instances already start in the Creator role; the explicit call documents the surface’s intent. Read the active role back with engine.editor.getRole().

try engine.editor.setRole("Creator")
let activeRole = try engine.editor.getRole()
print("Active role:", activeRole) // Creator

Under the Creator role’s defaults, every operation is permitted regardless of block-level scope settings. This is where designers build the template layout, configure which elements should be editable with engine.block.setScopeEnabled(_:key:enabled:), and save the template for distribution.

Configuring What Users Can Edit#

The scope system controls what Adopters can modify. While in the Creator role, enable specific scopes on the blocks that should stay editable. Verify a block-level setting with engine.block.isScopeEnabled(_:key:).

try engine.block.setScopeEnabled(headline, key: "editor/select", enabled: true)
try engine.block.setScopeEnabled(headline, key: "text/edit", enabled: true)
let headlineIsSelectable = try engine.block.isScopeEnabled(headline, key: "editor/select")
print("Headline editor/select enabled:", headlineIsSelectable) // true

When Adopters load this template, they can edit the headline text but nothing else. The editor/select scope must be enabled for users to interact with a block at all. The logo keeps its defaults—blocks created under the Creator role start with every block-level scope disabled—so it stays locked for Adopters. For comprehensive scope configuration patterns, see Lock Content.

Setting Up the Adopter Surface#

The Adopter surface is where templates are used. Call engine.editor.setRole(_:) with "Adopter" to apply the restrictions configured by creators. Check the resulting permissions with engine.block.isAllowedByScope(_:key:), which evaluates the global and block-level settings together.

try engine.editor.setRole("Adopter")
let canEditHeadline = try engine.block.isAllowedByScope(headline, key: "text/edit")
let canSelectLogo = try engine.block.isAllowedByScope(logo, key: "editor/select")
let canMoveLogo = try engine.block.isAllowedByScope(logo, key: "layer/move")
print("Adopter can edit the headline:", canEditHeadline) // true
print("Adopter can select the logo:", canSelectLogo) // false
print("Adopter can move the logo:", canMoveLogo) // false

The Adopter role sets every global scope to .defer—except editor/add, which stays .allow so users can still add their own content. Blocks an Adopter creates start with all block-level scopes enabled, leaving users in full control of content they add themselves.

Scopes describe what users may do: query the effective permission with isAllowedByScope(_:key:) and use it to gate the editing controls you expose to users. Engine API calls from your own code aren’t blocked by scopes.

When to Use This Pattern#

This two-surface approach works well for:

  • Brand template systems: Marketing teams customize approved templates
  • Design approval workflows: Creators build, reviewers can’t accidentally modify
  • Self-service customization: End users personalize within guardrails
  • White-label products: Customers can only edit designated areas

For simpler use cases where all users have the same permissions, you may not need separate surfaces. For preview or approval surfaces that should allow no editing at all, set the "Viewer" role—it sets every global scope to .deny.

Switching Roles at Runtime#

The same engine instance can switch roles at any time, for example when a designer previews the Adopter experience. Each setRole(_:) call re-applies that role’s global scope defaults and overwrites any manual engine.editor.setGlobalScope(key:value:) customizations. Block-level scopes are untouched, so the locks configured in the Creator role persist across switches.

try engine.editor.setRole("Creator")
let creatorCanMoveLogo = try engine.block.isAllowedByScope(logo, key: "layer/move")
let headlineStillEditable = try engine.block.isScopeEnabled(headline, key: "text/edit")
print("Creator can move the logo:", creatorCanMoveLogo) // true
print("Headline text/edit survived the switch:", headlineStillEditable) // true

Troubleshooting#

Issue Cause Solution
Adopter can edit everything The role is still Creator Call setRole(_:) with "Adopter" on the adopter surface
Adopter can’t edit anything editor/select scope not enabled Enable editor/select on blocks users should interact with
Manual global scope settings disappear setRole(_:) re-applies the role’s global defaults Re-apply setGlobalScope(key:value:) customizations after switching roles
Changes not persisting Template not saved after scope changes Save the template after configuring scopes in the Creator role

API Reference#

Method Description
engine.editor.setRole(_:) Set the editing role: "Creator", "Adopter" or "Viewer"
engine.editor.getRole() Get the current editing role
engine.block.setScopeEnabled(_:key:enabled:) Enable or disable a scope on a block
engine.block.isScopeEnabled(_:key:) Check if a scope is enabled on a block
engine.block.isAllowedByScope(_:key:) Check the effective permission after evaluating global and block-level settings

Common Scopes#

Scope Description
editor/select Allow selecting the block (required for any interaction)
fill/change Allow changing the block’s fill (images, colors)
text/edit Allow editing text content
text/character Allow changing text formatting (font, size, color)
layer/move Allow moving the block
layer/resize Allow resizing the block
layer/rotate Allow rotating the block
layer/crop Allow cropping the block
lifecycle/destroy Allow deleting the block

Next Steps#