| name | pimcore-studio-ui-permissions |
| description | Checking and handling permissions in Pimcore Studio UI - element permissions, user permissions, perspective permissions, and writeable state for config-based features |
| metadata | {"audience":"pimcore-developers","focus":"permissions-security"} |
What This Skill Covers
How to check and handle permissions in Pimcore Studio UI:
- Element Permissions - Check if user can perform actions on specific elements (assets, documents, data objects)
- User Permissions - Check if user has global permissions (e.g., access to certain features)
- Perspective Permissions - Check if features are visible/accessible in the current perspective
- Writeable State - Special case for config-based features (perspectives, widgets, settings) using location-aware config system
- Conditional Rendering - Show/hide UI elements based on permissions
- Disabled States - Disable buttons and actions when permissions are missing or config is not writeable
- Real-World Patterns - Common permission checking patterns in save buttons, toolbars, and actions
When to Use This Skill
Use this when:
- Implementing save, publish, delete, or edit actions on elements
- Controlling access to features based on user roles
- Hiding/showing navigation items based on perspectives
- Disabling buttons when user lacks required permissions
- Building permission-aware toolbars and action menus
- Implementing element-specific actions (e.g., tree context menu)
- Handling writeable state for config-based features (settings store, perspectives, widgets, etc.)
🚨 Special Case: Config Writeable State
Some features use Pimcore's location-aware config system (settings-store) which determines if configurations can be modified. This is different from user permissions!
The isWriteable / writeable Property
Configuration-based features (perspectives, widgets, thumbnails, appearance settings) include a writeable or isWriteable property that indicates if the config can be saved.
When NOT writeable:
- ✅ Always disable the save button
- ✅ Always disable the delete button
- ✅ Always show the same standard tooltip
- ✅ Translation key:
config_not_writeable
- ✅ Tooltip text: "Not writable, this can have several reasons. For details please have a look into the docs."
Why Not Writeable?
Common reasons (location-aware config system):
- Config is defined in a PHP file or environment variable (higher priority than database)
- Config is locked at system level
- Config source is read-only
- Multiple config sources with priority conflicts
Pattern: Config Forms with Writeable Check
Standard pattern for all config-based forms:
import { Tooltip } from 'antd'
import { Button, IconButton, Content, Toolbar, Flex } from '@pimcore/studio-ui-bundle/components'
import { useTranslation } from 'react-i18next'
export const ConfigForm = (): React.JSX.Element => {
const { t } = useTranslation()
const { config, isLoading } = useConfig(id)
const isWriteable = config.isWriteable ?? config.writeable ?? false
return (
<FormKit onFinish={handleSave}>
<Flex vertical justify="space-between" className="absolute-stretch">
<Content padded>
{/* Form fields */}
</Content>
<Toolbar justify="space-between">
<div>
<IconButton
disabled={isLoading}
icon={{ value: 'refresh' }}
onClick={() => form.resetFields()}
/>
{/* Delete button - wrap with writeable tooltip */}
<Tooltip title={isWriteable ? '' : t('config_not_writeable')}>
<IconButton
disabled={isLoading || !isWriteable}
icon={{ value: 'trash' }}
onClick={handleDelete}
/>
</Tooltip>
</div>
{/* Save button - wrap with writeable tooltip */}
<Tooltip title={isWriteable ? '' : t('config_not_writeable')}>
<Button
disabled={!isWriteable}
htmlType="submit"
loading={isLoading}
type="primary"
>
{t('save')}
</Button>
</Tooltip>
</Toolbar>
</Flex>
</FormKit>
)
}
Real-World Examples
Perspective Editor (perspective-editor/components/perspective-form/perspective-form.tsx):
const isWriteable = perspective.isWriteable
Widget Editor (widget-editor/components/widget-type-form/widget-form.tsx):
const isWriteable = widget.isWriteable !== false
Appearance Settings (appearance-branding/components/appearance-form/appearance-form.tsx):
const isWriteable = adminSettings?.writeable ?? false
Writeable Check Checklist
When working with config-based features:
Common Features Using Writeable Check
These features typically have writeable state:
- Perspectives (
isWriteable)
- Widgets (
isWriteable)
- Image Thumbnails (
writeable)
- Video Thumbnails (
writeable)
- Appearance/Branding Settings (
writeable)
- Custom Settings (any settings-store based config)
Writeable vs User Permissions
| Type | Checks | Reason for Restriction | Tooltip |
|---|
| Writeable | Config source/location | System configuration override | config_not_writeable |
| User Permission | User role/capabilities | Access control/security | Custom per feature |
| Element Permission | Element-specific rights | Per-element access control | Custom per action |
IMPORTANT: Writeable state is NOT a permission check - it's a config system constraint. Always check writeable state separately from permissions!
🚨 Import Paths
BEFORE writing any import, read CRITICAL-IMPORT-PATHS.md.
All examples below use bundle imports (@pimcore/studio-ui-bundle/*). For core development (@sdk/*, @Pimcore/*), see the referenced file.
Note: isAllowedInPerspective is not exported via the SDK. It is only available in core via @Pimcore/modules/perspectives/permission-checker.
Element Permissions
Check if a user can perform specific actions on an element (asset, document, or data object).
checkElementPermission() Function
checkElementPermission(
permissions: ElementPermissions | undefined,
permission: ElementPermissionKeys
): boolean
Returns: true if user has the permission, false otherwise
Available Element Permissions
Common element permission keys:
'publish' - Can save and publish the element
'delete' - Can delete the element
'rename' - Can rename the element
'view' - Can view the element
'properties' - Can edit properties
'versions' - Can view/restore versions
'settings' - Can edit settings
Basic Usage
import { checkElementPermission } from '@pimcore/studio-ui-bundle/modules/element'
import { useAssetDraft } from '@pimcore/studio-ui-bundle/modules/asset'
export const SaveButton = (): React.JSX.Element => {
const { asset } = useAssetDraft(id)
const canPublish = checkElementPermission(asset?.permissions, 'publish')
return (
<>
{canPublish && (
<Button onClick={handleSave} type="primary">
Save and publish
</Button>
)}
</>
)
}
Pattern 1: Conditional Rendering
Show/hide UI elements based on permissions:
import { checkElementPermission } from '@pimcore/studio-ui-bundle/modules/element'
import { Button } from '@pimcore/studio-ui-bundle/components'
export const EditorToolbar = (): React.JSX.Element => {
const { t } = useTranslation()
const { asset } = useAssetDraft(id)
return (
<Toolbar>
{/* Only show save button if user can publish */}
{checkElementPermission(asset?.permissions, 'publish') && (
<Button
onClick={handleSave}
type="primary"
>
{t('toolbar.save')}
</Button>
)}
{/* Only show delete button if user can delete */}
{checkElementPermission(asset?.permissions, 'delete') && (
<IconButton
color="danger"
icon={{ value: 'delete' }}
onClick={handleDelete}
tooltip={{ title: t('delete') }}
/>
)}
</Toolbar>
)
}
Pattern 2: Disabled State
Disable actions when permissions are missing:
export const ActionButtons = (): React.JSX.Element => {
const { element } = useElement(id)
const canPublish = checkElementPermission(element?.permissions, 'publish')
const canDelete = checkElementPermission(element?.permissions, 'delete')
return (
<ButtonGroup
items={[
<Button
disabled={!canPublish}
key="save"
onClick={handleSave}
type="primary"
>
{t('save')}
</Button>,
<Button
color="danger"
disabled={!canDelete}
key="delete"
onClick={handleDelete}
>
{t('delete')}
</Button>
]}
/>
)
}
Pattern 3: Conditional Behavior
Check permissions before executing actions:
import { checkElementPermission } from '@pimcore/studio-ui-bundle/modules/element'
import { useMessage } from '@pimcore/studio-ui-bundle/components'
export const useElementActions = (id: number) => {
const { element } = useElement(id)
const messageApi = useMessage()
const handleSave = (): void => {
if (!checkElementPermission(element?.permissions, 'publish')) {
messageApi.error('You do not have permission to publish this element')
return
}
saveElement()
}
const handleDelete = (): void => {
if (!checkElementPermission(element?.permissions, 'delete')) {
messageApi.error('You do not have permission to delete this element')
return
}
showDeleteConfirmation()
}
return { handleSave, handleDelete }
}
Real-World Example: Save Button with Permissions
import { Button } from '@pimcore/studio-ui-bundle/components'
import { useAssetDraft } from '@pimcore/studio-ui-bundle/modules/asset'
import { checkElementPermission } from '@pimcore/studio-ui-bundle/modules/element'
import { useHandleKeyBindings } from '@pimcore/studio-ui-bundle/modules/app'
export const EditorToolbarSaveButton = (): React.JSX.Element => {
const { t } = useTranslation()
const { id } = useElementContext()
const { asset } = useAssetDraft(id)
const [saveAsset, { isLoading }] = useAssetUpdateByIdMutation()
useHandleKeyBindings(async () => {
if (asset != null && checkElementPermission(asset.permissions, 'publish')) {
onSaveClick()
}
}, 'save')
return (
<>
{/* Only render button if user has publish permission */}
{checkElementPermission(asset?.permissions, 'publish') && (
<Button
disabled={isLoading}
loading={isLoading}
onClick={onSaveClick}
type="primary"
>
{t(asset?.type === 'folder' ? 'toolbar.save' : 'toolbar.save-and-publish')}
</Button>
)}
</>
)
function onSaveClick(): void {
if (asset?.changes === undefined) return
saveAsset({ id, body: { data: {...} } })
}
}
User Permissions
Check if the current user has global permissions for features (not element-specific).
isAllowed() Function
isAllowed(permission: UserPermission | undefined): boolean
Returns: true if user has the permission or is admin, false otherwise
Note: Admins always return true for any permission check.
UserPermission Enum
Available user permissions from @pimcore/studio-ui-bundle/modules/auth:
export enum UserPermission {
NotesAndEvents = 'notes_events',
Translations = 'translations',
Appearance = 'system_appearance_settings',
Documents = 'documents',
DocumentTypes = 'document_types',
Objects = 'objects',
Assets = 'assets',
Thumbnails = 'thumbnails',
TagsConfiguration = 'tags_configuration',
PredefinedProperties = 'predefined_properties',
WebsiteSettings = 'website_settings',
Users = 'users',
Notifications = 'notifications',
SendNotifications = 'notifications_send',
Emails = 'emails',
Reports = 'reports',
ReportsConfig = 'reports_config',
RecycleBin = 'recyclebin',
Redirects = 'redirects',
ApplicationLogger = 'application_logging',
PerspectiveEditor = 'studio_perspective_editor',
WidgetEditor = 'studio_perspective_widget_editor',
GDPRDataExtractor = 'gdpr_data_extractor'
}
Basic Usage
import { isAllowed, UserPermission } from '@pimcore/studio-ui-bundle/modules/auth'
export const SettingsMenu = (): React.JSX.Element => {
const { t } = useTranslation()
const canManageUsers = isAllowed(UserPermission.Users)
return (
<Menu>
{canManageUsers && (
<MenuItem onClick={openUsersPage}>
{t('settings.users')}
</MenuItem>
)}
</Menu>
)
}
Pattern 1: Feature Access Control
import { isAllowed, UserPermission } from '@pimcore/studio-ui-bundle/modules/auth'
export const AdminPanel = (): React.JSX.Element => {
const canAccessUsers = isAllowed(UserPermission.Users)
const canAccessTranslations = isAllowed(UserPermission.Translations)
const canAccessReports = isAllowed(UserPermission.Reports)
return (
<div>
<h1>Admin Panel</h1>
{canAccessUsers && (
<Card title="User Management">
<Button onClick={openUsersPage}>Manage users</Button>
</Card>
)}
{canAccessTranslations && (
<Card title="Translations">
<Button onClick={openTranslationsPage}>Manage translations</Button>
</Card>
)}
{canAccessReports && (
<Card title="Reports">
<Button onClick={openReportsPage}>View reports</Button>
</Card>
)}
</div>
)
}
Pattern 2: Conditional Navigation Items
import { isAllowed, UserPermission } from '@pimcore/studio-ui-bundle/modules/auth'
export const useNavigationItems = () => {
const { t } = useTranslation()
const items = []
if (isAllowed(UserPermission.Assets)) {
items.push({
key: 'assets',
label: t('nav.assets'),
icon: 'assets'
})
}
if (isAllowed(UserPermission.Documents)) {
items.push({
key: 'documents',
label: t('nav.documents'),
icon: 'documents'
})
}
if (isAllowed(UserPermission.Objects)) {
items.push({
key: 'objects',
label: t('nav.objects'),
icon: 'objects'
})
}
return items
}
Pattern 3: Editor Tab Visibility
import { isAllowed, UserPermission } from '@pimcore/studio-ui-bundle/modules/auth'
export const editorTabs = [
{
key: 'properties',
label: 'Properties',
userPermission: UserPermission.PredefinedProperties,
component: PropertiesTab
},
{
key: 'notes',
label: 'Notes & Events',
userPermission: UserPermission.NotesAndEvents,
component: NotesTab
}
]
export const EditorTabs = (): React.JSX.Element => {
const visibleTabs = editorTabs.filter(tab =>
!tab.userPermission || isAllowed(tab.userPermission)
)
return (
<Tabs items={visibleTabs} />
)
}
Perspective Permissions
Check if features are visible/accessible in the current active perspective.
isAllowedInPerspective() Function
isAllowedInPerspective(permission: string): boolean
Returns: true if the permission is granted in the current perspective, false otherwise
Permission Format: Use dot notation like 'group.permission' or 'feature.hidden'
Basic Usage
import { isAllowedInPerspective } from '@pimcore/studio-ui-bundle/modules/perspectives'
export const SearchFeature = (): React.JSX.Element => {
if (isAllowedInPerspective('search.hidden')) {
return null
}
return <SearchComponent />
}
Real-World Pattern: Navigation Item Visibility
import { isAllowedInPerspective } from '@pimcore/studio-ui-bundle/modules/perspectives'
interface IMainNavItem {
icon: string
label: string
perspectivePermissionHide?: string
}
export const MainNav = (): React.JSX.Element => {
const renderNavItem = (item: IMainNavItem): React.JSX.Element => {
const isHiddenInPerspective =
!isUndefined(item.perspectivePermissionHide) &&
isAllowedInPerspective(item.perspectivePermissionHide)
if (isHiddenInPerspective) {
return <></>
}
return (
<MenuItem icon={item.icon}>
{item.label}
</MenuItem>
)
}
}
Pattern: Feature Toggles per Perspective
import { isAllowedInPerspective } from '@pimcore/studio-ui-bundle/modules/perspectives'
export const EditorFeatures = (): React.JSX.Element => {
const showAdvancedFeatures = !isAllowedInPerspective('editor.advanced.hidden')
const showExportTools = !isAllowedInPerspective('editor.export.hidden')
const showVersioning = !isAllowedInPerspective('editor.versions.hidden')
return (
<div>
{showAdvancedFeatures && <AdvancedEditor />}
{showExportTools && <ExportToolbar />}
{showVersioning && <VersionHistory />}
</div>
)
}
Combining Multiple Permission Checks
Often you need to check multiple permission types together.
Pattern 1: Element + User Permissions
import { checkElementPermission } from '@pimcore/studio-ui-bundle/modules/element'
import { isAllowed, UserPermission } from '@pimcore/studio-ui-bundle/modules/auth'
export const ElementActions = (): React.JSX.Element => {
const { element } = useElement(id)
const canAccessVersions =
checkElementPermission(element?.permissions, 'versions') &&
isAllowed(UserPermission.NotesAndEvents)
return (
<>
{canAccessVersions && (
<Button onClick={openVersions}>
View versions
</Button>
)}
</>
)
}
Pattern 2: All Three Permission Types
import { checkElementPermission } from '@pimcore/studio-ui-bundle/modules/element'
import { isAllowed, UserPermission } from '@pimcore/studio-ui-bundle/modules/auth'
import { isAllowedInPerspective } from '@pimcore/studio-ui-bundle/modules/perspectives'
export const AdvancedPropertiesTab = (): React.JSX.Element => {
const { element } = useElement(id)
const canShowTab =
checkElementPermission(element?.permissions, 'properties') &&
isAllowed(UserPermission.PredefinedProperties) &&
!isAllowedInPerspective('element.properties.hidden')
if (!canShowTab) {
return null
}
return <PropertiesTabContent />
}
Pattern 3: Permission-Based Action List
import { checkElementPermission } from '@pimcore/studio-ui-bundle/modules/element'
import type { MenuProps } from 'antd'
export const useContextMenuItems = (element: Element) => {
const { t } = useTranslation()
const items: MenuProps['items'] = []
if (checkElementPermission(element?.permissions, 'view')) {
items.push({
key: 'open',
label: t('context-menu.open'),
icon: <Icon value="open" />
})
}
if (checkElementPermission(element?.permissions, 'rename')) {
items.push({
key: 'rename',
label: t('context-menu.rename'),
icon: <Icon value="edit" />
})
}
if (checkElementPermission(element?.permissions, 'delete')) {
items.push({
type: 'divider'
})
items.push({
key: 'delete',
label: t('context-menu.delete'),
icon: <Icon value="delete" />,
danger: true
})
}
return items
}
Common Patterns
Pattern 1: Permission Guard Hook
Create a reusable hook for permission checks:
import { checkElementPermission, type ElementPermissionKeys } from '@pimcore/studio-ui-bundle/modules/element'
export const useElementPermissions = (element?: Element) => {
const hasPermission = (permission: ElementPermissionKeys): boolean => {
return checkElementPermission(element?.permissions, permission)
}
return {
canPublish: hasPermission('publish'),
canDelete: hasPermission('delete'),
canRename: hasPermission('rename'),
canView: hasPermission('view'),
canEditProperties: hasPermission('properties'),
hasPermission
}
}
export const ElementToolbar = (): React.JSX.Element => {
const { element } = useElement(id)
const { canPublish, canDelete } = useElementPermissions(element)
return (
<Toolbar>
{canPublish && <SaveButton />}
{canDelete && <DeleteButton />}
</Toolbar>
)
}
Pattern 2: Permission-Aware Button Component
import { checkElementPermission, type ElementPermissionKeys } from '@pimcore/studio-ui-bundle/modules/element'
import { Button, type ButtonProps } from '@pimcore/studio-ui-bundle/components'
interface PermissionButtonProps extends ButtonProps {
element?: Element
requiredPermission: ElementPermissionKeys
children: React.ReactNode
}
export const PermissionButton = ({
element,
requiredPermission,
children,
...buttonProps
}: PermissionButtonProps): React.JSX.Element => {
const hasPermission = checkElementPermission(element?.permissions, requiredPermission)
if (!hasPermission) {
return null
}
return (
<Button {...buttonProps}>
{children}
</Button>
)
}
<PermissionButton
element={asset}
onClick={handleSave}
requiredPermission="publish"
type="primary"
>
Save
</PermissionButton>
Pattern 3: Centralized Permission Config
import type { ElementPermissionKeys } from '@pimcore/studio-ui-bundle/modules/element'
export interface ActionConfig {
key: string
label: string
icon: string
permission: ElementPermissionKeys
danger?: boolean
}
export const ELEMENT_ACTIONS: ActionConfig[] = [
{
key: 'open',
label: 'Open',
icon: 'open',
permission: 'view'
},
{
key: 'rename',
label: 'Rename',
icon: 'edit',
permission: 'rename'
},
{
key: 'delete',
label: 'Delete',
icon: 'delete',
permission: 'delete',
danger: true
}
]
import { ELEMENT_ACTIONS } from './permissions/element-actions-config'
import { checkElementPermission } from '@pimcore/studio-ui-bundle/modules/element'
export const ContextMenu = ({ element }: Props): React.JSX.Element => {
const { t } = useTranslation()
const allowedActions = ELEMENT_ACTIONS.filter(action =>
checkElementPermission(element?.permissions, action.permission)
)
const menuItems = allowedActions.map(action => ({
key: action.key,
label: t(`context-menu.${action.key}`),
icon: <Icon value={action.icon} />,
danger: action.danger
}))
return <Dropdown menu={{ items: menuItems }} />
}
Common Mistakes
❌ Not Checking for Undefined Permissions
const canPublish = element.permissions.publish
const canPublish = checkElementPermission(element?.permissions, 'publish')
❌ Hardcoding Permission Strings
if (element.permissions?.publsh) {
}
if (checkElementPermission(element?.permissions, 'publish')) {
}
❌ Client-Side Permission Bypass
<Button onClick={deleteElement}>Delete</Button>
{checkElementPermission(element?.permissions, 'delete') && (
<Button onClick={deleteElement}>Delete</Button>
)}
❌ Forgetting Admin Check
const canAccess = user.isAdmin || user.permissions.includes('users')
const canAccess = isAllowed(UserPermission.Users)
❌ Using Wrong Permission Check Function
if (checkElementPermission(UserPermission.Assets)) {
}
if (isAllowed(UserPermission.Assets)) {
}
if (checkElementPermission(element?.permissions, 'publish')) {
}
if (isAllowedInPerspective('feature.hidden')) {
}
❌ Not Disabling Actions During Loading
{canPublish && (
<Button loading={isLoading} onClick={handleSave}>
Save
</Button>
)}
{canPublish && (
<Button
disabled={isLoading}
loading={isLoading}
onClick={handleSave}
>
Save
</Button>
)}
❌ Forgetting Writeable Check for Config Features
export const PerspectiveForm = (): React.JSX.Element => {
const { perspective } = usePerspective(id)
return (
<Button htmlType="submit" type="primary">
{t('save')}
</Button>
)
}
export const PerspectiveForm = (): React.JSX.Element => {
const { perspective } = usePerspective(id)
const isWriteable = perspective.isWriteable
return (
<Tooltip title={isWriteable ? '' : t('config_not_writeable')}>
<Button
disabled={!isWriteable}
htmlType="submit"
type="primary"
>
{t('save')}
</Button>
</Tooltip>
)
}
❌ Custom Writeable Error Messages
<Tooltip title={isWriteable ? '' : 'Cannot save this configuration'}>
<Button disabled={!isWriteable}>Save</Button>
</Tooltip>
<Tooltip title={isWriteable ? '' : t('config.readonly')}>
<Button disabled={!isWriteable}>Save</Button>
</Tooltip>
<Tooltip title={isWriteable ? '' : t('config_not_writeable')}>
<Button disabled={!isWriteable}>Save</Button>
</Tooltip>
❌ Confusing Writeable with Permissions
if (isAllowed('writeable')) {
}
const isWriteable = perspective.isWriteable
if (isWriteable) {
}
const canSave =
checkElementPermission(element?.permissions, 'publish') &&
element.isWriteable
Quick Reference
Permission Check Functions
| Function | Use For | Returns |
|---|
checkElementPermission(permissions, key) | Element-specific actions | true if user has permission on element |
isAllowed(UserPermission.X) | Global user permissions | true if user has permission (or is admin) |
isAllowedInPerspective('key') | Perspective visibility | true if permission is set in perspective |
Writeable State Check (Config Features)
| Property | Found In | Purpose |
|---|
isWriteable | Perspectives, Widgets | Indicates if config can be modified |
writeable | Thumbnails, Appearance Settings | Indicates if config can be modified |
| Tooltip | t('config_not_writeable') | Standard tooltip for non-writeable configs |
| Pattern | <Tooltip title={isWriteable ? '' : t('config_not_writeable')}> | Wrap save/delete buttons |
Common Element Permissions
| Permission | Use For |
|---|
'publish' | Save/publish element |
'delete' | Delete element |
'rename' | Rename element |
'view' | View/open element |
'properties' | Edit properties |
'versions' | View/restore versions |
'settings' | Edit settings |
Permission Check Decision Tree
Need to restrict save/delete buttons?
│
├─ Config-based feature (perspective, widget, thumbnail, settings)?
│ └─ Check: isWriteable or writeable property
│ └─ Pattern: <Tooltip title={isWriteable ? '' : t('config_not_writeable')}>
│
├─ Action on specific element (save, delete, rename)?
│ └─ Use: checkElementPermission(element?.permissions, 'permission')
│
├─ Access to global feature (users, reports, translations)?
│ └─ Use: isAllowed(UserPermission.X)
│
└─ Feature visibility in current perspective?
└─ Use: isAllowedInPerspective('group.permission')
Common Mistakes
❌ Mistake 1: Wrong Permission Check Type
import { isAllowed, UserPermission } from '@pimcore/studio-ui-bundle/modules/auth'
if (isAllowed(UserPermission.ASSETS)) { }
import { checkElementPermission } from '@pimcore/studio-ui-bundle/modules/element'
if (checkElementPermission(asset.permissions, 'update')) { }
❌ Mistake 2: Not Checking isNil Before Permission Check
if (checkElementPermission(asset.permissions, 'delete')) { ... }
import { isNil } from 'lodash'
if (!isNil(asset) && checkElementPermission(asset.permissions, 'delete')) { ... }
❌ Mistake 3: Confusing Perspective vs User Permissions
import { isAllowed, UserPermission } from '@pimcore/studio-ui-bundle/modules/auth'
if (isAllowed(UserPermission.ASSETS)) { }
import { isAllowedInPerspective } from '@pimcore/studio-ui-bundle/modules/perspectives'
if (isAllowedInPerspective('assets.explorer')) { }
❌ Mistake 4: Not Handling Writeable State for Config Features
const asset = useAssetDraft()
if (checkElementPermission(asset.permissions, 'update')) { }
import { isNil } from 'lodash'
const asset = useAssetDraft()
const canEdit = !isNil(asset) && (asset.writeable ?? asset.isWriteable ?? true)
return <FormKit disabled={!canEdit}>...</FormKit>
❌ Mistake 5: Forgetting to Hide UI When Permission Denied
<Button disabled={!hasPermission}>Delete</Button>
{hasPermission && <Button>Delete</Button>}
Next Steps