Search Docs
Loading...
Skip to content

Lock Content

Lock design elements to prevent unwanted modifications using CE.SDK’s scope-based permission system.

8 mins
estimated time
GitHub

CE.SDK uses scopes to control what users can modify in a design. Each scope gates a specific capability, such as moving, resizing, text editing, or image replacement. The permission system has two layers: global scopes set defaults for the whole scene, and block-level scopes override those defaults when the global scope is set to GlobalScope.DEFER.

The snippets below use the Android Engine API directly. The same scope settings are respected by the CE.SDK editor UI; the Design Editor Starter Kit is a complete Android UI surface you can configure around these rules.

This guide covers how to discover available scopes, lock an entire design, and selectively enable specific editing capabilities on individual blocks.

Understanding the Scope Permission Model#

Global and block-level scopes combine to determine whether an operation is permitted. The global scope can be set to one of three values:

Global Scope Block Scope Result
GlobalScope.ALLOW any Permitted
GlobalScope.DENY any Blocked
GlobalScope.DEFER enabled Permitted
GlobalScope.DEFER disabled Blocked

When global is GlobalScope.ALLOW, the operation is always permitted. When global is GlobalScope.DENY, it is always blocked. When global is GlobalScope.DEFER, the block-level enabled state determines the outcome.

Discovering Available Scopes#

Retrieve every available scope name with engine.editor.findAllScopes(). Use the returned identifiers with the global and block-level scope APIs.

val scopes = engine.editor.findAllScopes()

Locking an Entire Design#

To lock every editing operation, iterate through all scopes and set each global scope to GlobalScope.DENY. This keeps the code resilient when CE.SDK adds new scopes.

scopes.forEach { scope ->
engine.editor.setGlobalScope(key = scope, globalScope = GlobalScope.DENY)
}

When all scopes are set to GlobalScope.DENY, the editor/select scope is locked too. Users cannot interact with a block they cannot select, so enable selection before enabling operation-specific scopes.

engine.editor.setGlobalScope(key = "editor/select", globalScope = GlobalScope.DEFER)
engine.block.setScopeEnabled(blocks.headline, key = "editor/select", enabled = true)
engine.block.setScopeEnabled(blocks.mediaPlaceholder, key = "editor/select", enabled = true)
engine.block.setScopeEnabled(blocks.movableBadge, key = "editor/select", enabled = true)

Selective Locking Patterns#

Most integrations lock the full design first, then selectively enable the exact operations users should perform on specific blocks.

Allowing Text Editing#

To let users edit text content and styling, set the text/edit and text/character global scopes to GlobalScope.DEFER, then enable them on the chosen text block.

engine.editor.setGlobalScope(key = "text/edit", globalScope = GlobalScope.DEFER)
engine.editor.setGlobalScope(key = "text/character", globalScope = GlobalScope.DEFER)
engine.block.setScopeEnabled(blocks.headline, key = "text/edit", enabled = true)
engine.block.setScopeEnabled(blocks.headline, key = "text/character", enabled = true)

Allowing Image Replacement#

To let users replace media while protecting layout, set fill/change and fill/changeType to GlobalScope.DEFER, then enable both scopes on the image placeholder block.

engine.editor.setGlobalScope(key = "fill/change", globalScope = GlobalScope.DEFER)
engine.editor.setGlobalScope(key = "fill/changeType", globalScope = GlobalScope.DEFER)
engine.block.setScopeEnabled(blocks.mediaPlaceholder, key = "fill/change", enabled = true)
engine.block.setScopeEnabled(blocks.mediaPlaceholder, key = "fill/changeType", enabled = true)
engine.block.setScopeEnabled(blocks.movableBadge, key = "fill/change", enabled = false)
engine.block.setScopeEnabled(blocks.movableBadge, key = "fill/changeType", enabled = false)

Allowing Position Adjustments#

To let users reposition selected elements, set layer/move and layer/resize to GlobalScope.DEFER, then enable those scopes only on blocks that should be movable.

engine.editor.setGlobalScope(key = "layer/move", globalScope = GlobalScope.DEFER)
engine.editor.setGlobalScope(key = "layer/resize", globalScope = GlobalScope.DEFER)
listOf(blocks.headline, blocks.mediaPlaceholder).forEach { block ->
engine.block.setScopeEnabled(block, key = "layer/move", enabled = false)
engine.block.setScopeEnabled(block, key = "layer/resize", enabled = false)
}
engine.block.setScopeEnabled(blocks.movableBadge, key = "layer/move", enabled = true)
engine.block.setScopeEnabled(blocks.movableBadge, key = "layer/resize", enabled = true)

Checking Permissions#

Use engine.block.isAllowedByScope() when you need the effective permission. It evaluates the global scope and the block-level scope together.

val result = LockContentResult(
canEditHeadline = engine.block.isAllowedByScope(blocks.headline, key = "text/edit"),
canRestyleHeadline = engine.block.isAllowedByScope(blocks.headline, key = "text/character"),
canMoveHeadline = engine.block.isAllowedByScope(blocks.headline, key = "layer/move"),
canResizeHeadline = engine.block.isAllowedByScope(blocks.headline, key = "layer/resize"),
canReplaceMedia = engine.block.isAllowedByScope(blocks.mediaPlaceholder, key = "fill/change"),
canChangeMediaFillType = engine.block.isAllowedByScope(blocks.mediaPlaceholder, key = "fill/changeType"),
canMoveMedia = engine.block.isAllowedByScope(blocks.mediaPlaceholder, key = "layer/move"),
canResizeMedia = engine.block.isAllowedByScope(blocks.mediaPlaceholder, key = "layer/resize"),
canReplaceBadge = engine.block.isAllowedByScope(blocks.movableBadge, key = "fill/change"),
canChangeBadgeFillType = engine.block.isAllowedByScope(blocks.movableBadge, key = "fill/changeType"),
canMoveBadge = engine.block.isAllowedByScope(blocks.movableBadge, key = "layer/move"),
canResizeBadge = engine.block.isAllowedByScope(blocks.movableBadge, key = "layer/resize"),
headlineTextScopeEnabled = engine.block.isScopeEnabled(blocks.headline, key = "text/edit"),
textEditGlobalScope = engine.editor.getGlobalScope(key = "text/edit"),
)
check(result.canEditHeadline)
check(result.canRestyleHeadline)
check(!result.canMoveHeadline)
check(!result.canResizeHeadline)
check(result.canReplaceMedia)
check(result.canChangeMediaFillType)
check(!result.canMoveMedia)
check(!result.canResizeMedia)
check(!result.canReplaceBadge)
check(!result.canChangeBadgeFillType)
check(result.canMoveBadge)
check(result.canResizeBadge)
check(result.headlineTextScopeEnabled)
check(result.textEditGlobalScope == GlobalScope.DEFER)

Use engine.block.isScopeEnabled() to read only the block-level setting, and engine.editor.getGlobalScope() to read only the global setting.

Saving and Restoring Scope State#

If your code applies locks temporarily, store the previous global and block-level scope values before changing them. Restore both layers when the operation completes. Skip this restoration step when the scene should remain locked after your function returns.

val blockScopesToRestore = listOf(
blocks.headline to "editor/select",
blocks.mediaPlaceholder to "editor/select",
blocks.movableBadge to "editor/select",
blocks.headline to "text/edit",
blocks.headline to "text/character",
blocks.mediaPlaceholder to "fill/change",
blocks.mediaPlaceholder to "fill/changeType",
blocks.movableBadge to "fill/change",
blocks.movableBadge to "fill/changeType",
blocks.headline to "layer/move",
blocks.headline to "layer/resize",
blocks.mediaPlaceholder to "layer/move",
blocks.mediaPlaceholder to "layer/resize",
blocks.movableBadge to "layer/move",
blocks.movableBadge to "layer/resize",
)
val scopes = engine.editor.findAllScopes()
val originalGlobalScopes = scopes.associateWith { scope ->
engine.editor.getGlobalScope(key = scope)
}
val originalBlockScopes = blockScopesToRestore.map { (block, scope) ->
Triple(
first = block,
second = scope,
third = engine.block.isScopeEnabled(block, key = scope),
)
}
originalBlockScopes.forEach { (block, scope, enabled) ->
engine.block.setScopeEnabled(block, key = scope, enabled = enabled)
}
originalGlobalScopes.forEach { (scope, globalScope) ->
engine.editor.setGlobalScope(key = scope, globalScope = globalScope)
}

Available Scopes Reference#

Scope Description
layer/move Move block position
layer/resize Resize block dimensions
layer/rotate Rotate block
layer/flip Flip block horizontally or vertically
layer/crop Crop block content
layer/opacity Change block opacity
layer/blendMode Change blend mode
layer/visibility Toggle block visibility
layer/clipping Change clipping behavior
fill/change Change fill content or text color
fill/changeType Change fill type
stroke/change Change stroke properties
shape/change Change shape type
text/edit Edit text content
text/character Change text styling such as font or size
appearance/adjustments Change color adjustments
appearance/filter Apply or change filters
appearance/effect Apply or change effects
appearance/blur Apply or change blur
appearance/shadow Apply or change shadows
appearance/animation Apply or change animations
lifecycle/destroy Delete the block
lifecycle/duplicate Duplicate the block
editor/add Add new blocks
editor/select Select blocks

Troubleshooting#

Issue Cause Solution
Block is still editable The global scope is GlobalScope.ALLOW Set the global scope to GlobalScope.DENY or GlobalScope.DEFER
Block is unexpectedly locked The global scope is GlobalScope.DENY Set the global scope to GlobalScope.DEFER and enable the block-level scope
Users cannot select a block editor/select is still locked Enable editor/select for blocks users should select
Permission check returns false The code checks the wrong scope level Use isAllowedByScope() for the effective permission
New scopes are not locked The code uses a hardcoded scope list Use findAllScopes() to discover scopes dynamically

API Reference#

Method Purpose
engine.editor.findAllScopes() Get all available scope names
engine.editor.setGlobalScope(key=_, globalScope=_) Set a global scope to GlobalScope.ALLOW, GlobalScope.DENY, or GlobalScope.DEFER
engine.editor.getGlobalScope(key=_) Get the current global setting for one scope
engine.block.setScopeEnabled(block=_, key=_, enabled=_) Enable or disable a scope on one block
engine.block.isScopeEnabled(block=_, key=_) Check only the block-level scope setting
engine.block.isAllowedByScope(block=_, key=_) Check the effective permission after global and block-level scopes are evaluated

Next Steps#