Control what users can edit in templates by setting fine-grained permissions on individual blocks or globally across your scene using the CE.SDK Scope system.
Editing constraints let you lock specific properties of design elements while keeping others editable. The Scope system provides granular control over more than 20 editing capabilities, including movement, resizing, rotation, fill changes, text editing, and lifecycle operations. Use it to create brand templates, guided editing experiences, and form-based workflows where design integrity must be preserved while still allowing controlled personalization.
Understanding Scopes#
What are Scopes?#
A scope is a permission key that controls a specific editing capability. Each scope represents a distinct action, such as moving blocks ("layer/move"), changing fills ("fill/change"), or editing text content ("text/edit"). By enabling or disabling scopes, you control exactly what users can and cannot do with each design element.
Scopes exist at two levels:
- Block-level scopes: Per-block permissions set with
setScopeEnabled(_:key:enabled:). - Global scopes: Default behavior for all blocks set with
setGlobalScope(key:value:).
These starting values depend on the editor role, which you set with setRole(_:). Under the default Creator role, every global scope is .allow, so every action is permitted and block-level scopes are not consulted. Blocks created in this role start with their block-level scopes disabled — a setting that only takes effect once a scope defers to the block level, either by switching to the Adopter role (which defers global scopes to the block level) or by deferring individual global scopes yourself, as the rest of this guide does.
Available Scope Categories#
CE.SDK groups scopes into logical categories. Retrieve the full list at runtime with engine.editor.findAllScopes().
| Category | Purpose | Example Scopes |
|---|---|---|
| Text Editing | Control text content and formatting | text/edit, text/character |
| Fill & Stroke | Manage colors and gradients | fill/change, fill/changeType, stroke/change |
| Shape | Modify shape properties | shape/change |
| Layer Transform | Control position and dimensions | layer/move, layer/resize, layer/rotate, layer/flip, layer/crop |
| Layer Appearance | Manage visual properties | layer/opacity, layer/blendMode, layer/visibility |
| Effects & Filters | Apply visual effects | appearance/adjustments, appearance/filter, appearance/effect, appearance/blur, appearance/shadow |
| Lifecycle | Control creation and deletion | lifecycle/destroy, lifecycle/duplicate |
| Editor | Manage scene-level actions | editor/add, editor/select |
Scope Configuration#
Global Scope Modes#
Global scopes set the default behavior for all blocks in the scene. They have three modes:
| Mode | Behavior |
|---|---|
.allow | Always allow the action, overriding block-level settings |
.deny | Always deny the action, overriding block-level settings |
.defer | Use the block-level setting for each block |
To make block-level constraints take effect under the default Creator role, defer the relevant global scopes to the block level:
try engine.editor.setGlobalScope(key: "layer/move", value: .defer)try engine.editor.setGlobalScope(key: "layer/resize", value: .defer)try engine.editor.setGlobalScope(key: "lifecycle/destroy", value: .defer)try engine.editor.setGlobalScope(key: "lifecycle/duplicate", value: .defer)Scope Resolution Priority#
When both global and block-level scopes apply, they resolve in this order:
- Global
.denytakes highest priority — the action is always denied. - Global
.allowtakes second priority — the action is always allowed. - Global
.deferuses the block-level setting for each block.
Setting Block-Level Constraints#
Locking Position#
Prevent users from moving a block while keeping other edits available:
try engine.block.setScopeEnabled(positionLocked, key: "layer/move", enabled: false)try engine.block.setScopeEnabled(positionLocked, key: "layer/resize", enabled: true)Disabling layer/move locks the block’s position. Because deferring a scope makes the engine consult the block-level setting — and block-level scopes start disabled — explicitly enable layer/resize so resizing stays available. Scopes you did not defer (such as fill/change and layer/rotate) remain at their global .allow default and stay editable.
Preventing Deletion#
Protect a block from being deleted or duplicated:
try engine.block.setScopeEnabled(deletionLocked, key: "lifecycle/destroy", enabled: false)try engine.block.setScopeEnabled(deletionLocked, key: "lifecycle/duplicate", enabled: false)try engine.block.setScopeEnabled(deletionLocked, key: "layer/move", enabled: true)try engine.block.setScopeEnabled(deletionLocked, key: "layer/resize", enabled: true)Disabling lifecycle/destroy and lifecycle/duplicate keeps the block in the template. Enabling layer/move and layer/resize keeps those deferred capabilities available, so the block stays movable and resizable while it cannot be removed. Use this for essential template elements that must remain present.
Checking Scope State#
Query the block-level setting for any scope:
let canMove = try engine.block.isScopeEnabled(positionLocked, key: "layer/move")print("layer/move enabled at block level: \(canMove)") // falseisScopeEnabled(_:key:) returns whether the scope is enabled at the block level. It does not consider the global scope.
Checking Effective Permissions#
Check the effective permission, which resolves both the block-level and global settings:
let moveAllowed = try engine.block.isAllowedByScope(positionLocked, key: "layer/move")print("layer/move allowed: \(moveAllowed)") // falseisAllowedByScope(_:key:) returns the final permission after applying the resolution priority above. Use it when you need to know whether an action is actually permitted.
API Reference#
| Method | Description |
|---|---|
engine.block.setScopeEnabled(_:key:enabled:) | Enable or disable a scope for a specific block |
engine.block.isScopeEnabled(_:key:) | Check whether a scope is enabled at the block level |
engine.block.isAllowedByScope(_:key:) | Check whether a scope is allowed, considering both block and global settings |
engine.editor.setGlobalScope(key:value:) | Set the global scope policy (.allow, .deny, or .defer) |
engine.editor.findAllScopes() | List all available scope keys |