Merge branch 'main' into v17/dev
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
export const EXAMPLE_USER_PERMISSION_DICTIONARY_ACTION_1 = 'Example.Dictionary.Action1';
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
export const EXAMPLE_USER_PERMISSION_DICTIONARY_ACTION_2 = 'Example.Dictionary.Action2';
|
||||
@@ -0,0 +1,9 @@
|
||||
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
|
||||
|
||||
export class ExampleAction1EntityAction extends UmbEntityActionBase<never> {
|
||||
override async execute() {
|
||||
alert('Example action 1 executed');
|
||||
}
|
||||
}
|
||||
|
||||
export { ExampleAction1EntityAction as default };
|
||||
@@ -0,0 +1,9 @@
|
||||
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
|
||||
|
||||
export class ExampleAction2EntityAction extends UmbEntityActionBase<never> {
|
||||
override async execute() {
|
||||
alert('Example action 2 executed');
|
||||
}
|
||||
}
|
||||
|
||||
export { ExampleAction2EntityAction as default };
|
||||
@@ -0,0 +1,47 @@
|
||||
import {
|
||||
EXAMPLE_USER_PERMISSION_DICTIONARY_ACTION_1,
|
||||
EXAMPLE_USER_PERMISSION_DICTIONARY_ACTION_2,
|
||||
} from '../constants.js';
|
||||
import { UMB_DICTIONARY_ENTITY_TYPE } from '@umbraco-cms/backoffice/dictionary';
|
||||
import { UMB_FALLBACK_USER_PERMISSION_CONDITION_ALIAS } from '@umbraco-cms/backoffice/user-permission';
|
||||
|
||||
export const manifests: Array<UmbExtensionManifest> = [
|
||||
{
|
||||
type: 'entityAction',
|
||||
kind: 'default',
|
||||
alias: 'Example.EntityAction.Action1',
|
||||
name: 'Action 1 Entity Action',
|
||||
forEntityTypes: [UMB_DICTIONARY_ENTITY_TYPE],
|
||||
api: () => import('./action-1.js'),
|
||||
weight: 10000,
|
||||
meta: {
|
||||
label: 'Action 1',
|
||||
icon: 'icon-car',
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
alias: UMB_FALLBACK_USER_PERMISSION_CONDITION_ALIAS,
|
||||
allOf: [EXAMPLE_USER_PERMISSION_DICTIONARY_ACTION_1],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'entityAction',
|
||||
kind: 'default',
|
||||
alias: 'Example.EntityAction.Action2',
|
||||
name: 'Action 2 Entity Action',
|
||||
forEntityTypes: [UMB_DICTIONARY_ENTITY_TYPE],
|
||||
api: () => import('./action-2.js'),
|
||||
weight: 9000,
|
||||
meta: {
|
||||
label: 'Action 2',
|
||||
icon: 'icon-bus',
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
alias: UMB_FALLBACK_USER_PERMISSION_CONDITION_ALIAS,
|
||||
allOf: [EXAMPLE_USER_PERMISSION_DICTIONARY_ACTION_2],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
EXAMPLE_USER_PERMISSION_DICTIONARY_ACTION_1,
|
||||
EXAMPLE_USER_PERMISSION_DICTIONARY_ACTION_2,
|
||||
} from '../constants.js';
|
||||
import { UMB_DICTIONARY_ENTITY_TYPE } from '@umbraco-cms/backoffice/dictionary';
|
||||
|
||||
export const manifests: Array<UmbExtensionManifest> = [
|
||||
{
|
||||
type: 'entityUserPermission',
|
||||
alias: 'Example.EntityUserPermission.Entity.Action1',
|
||||
name: 'Action 1 for Entity User Permission',
|
||||
forEntityTypes: [UMB_DICTIONARY_ENTITY_TYPE],
|
||||
meta: {
|
||||
verbs: [EXAMPLE_USER_PERMISSION_DICTIONARY_ACTION_1],
|
||||
label: 'Action 1',
|
||||
description: 'Description for action 1',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'entityUserPermission',
|
||||
alias: 'Example.EntityUserPermission.Entity.Action2',
|
||||
name: 'Action 2 for Entity User Permission',
|
||||
forEntityTypes: [UMB_DICTIONARY_ENTITY_TYPE],
|
||||
meta: {
|
||||
verbs: [EXAMPLE_USER_PERMISSION_DICTIONARY_ACTION_2],
|
||||
label: 'Action 2',
|
||||
description: 'Description for action 2',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,9 @@
|
||||
import { manifests as entityUserPermissionManifests } from './entity-user-permission/manifests.js';
|
||||
import { manifests as entityActionManifests } from './entity-action/manifests.js';
|
||||
import { manifests as localizationManifests } from './localization/manifests.js';
|
||||
|
||||
export const manifests: Array<UmbExtensionManifest> = [
|
||||
...entityUserPermissionManifests,
|
||||
...entityActionManifests,
|
||||
...localizationManifests,
|
||||
];
|
||||
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
user: {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
permissionsEntityGroup_dictionary: 'Dictionary',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
export const manifests: Array<UmbExtensionManifest> = [
|
||||
{
|
||||
type: 'localization',
|
||||
alias: 'Example.UserPermission.Localization.En',
|
||||
name: 'en-US User Permission Localization Example',
|
||||
js: () => import('./en.js'),
|
||||
weight: 1,
|
||||
meta: {
|
||||
culture: 'en',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -198,8 +198,8 @@ export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement {
|
||||
} else {
|
||||
return html`
|
||||
<div id="header">
|
||||
<b>${this.property.name}</b>
|
||||
<i>${this.property.alias}</i>
|
||||
<p><b>${this.property.name}</b></p>
|
||||
<p><i>${this.property.alias}</i></p>
|
||||
<p>${this.property.description}</p>
|
||||
</div>
|
||||
<div id="editor">
|
||||
@@ -451,6 +451,12 @@ export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement {
|
||||
border-radius: var(--uui-border-radius);
|
||||
}
|
||||
|
||||
:host([_inherited]) {
|
||||
#header {
|
||||
padding: 0 var(--uui-size-3, 9px);
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -470,6 +476,11 @@ export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement {
|
||||
--uui-button-background-color: var(--uui-color-background);
|
||||
--uui-button-background-color-hover: var(--uui-color-background);
|
||||
}
|
||||
#editor:not(uui-button) {
|
||||
background-color: var(--uui-color-background);
|
||||
border-radius: var(--uui-button-border-radius, var(--uui-border-radius, 3px));
|
||||
min-height: 143px;
|
||||
}
|
||||
#editor uui-action-bar {
|
||||
--uui-button-background-color: var(--uui-color-surface);
|
||||
--uui-button-background-color-hover: var(--uui-color-surface);
|
||||
|
||||
@@ -116,7 +116,7 @@ export class UmbWorkspaceVariantMenuBreadcrumbElement extends UmbLitElement {
|
||||
#getHref(structureItem: any) {
|
||||
if (structureItem.isFolder) return undefined;
|
||||
const workspaceBasePath = `section/${this.#sectionContext?.getPathname()}/workspace/${structureItem.entityType}/edit`;
|
||||
return `${workspaceBasePath}/${structureItem.unique}/${this._workspaceActiveVariantId?.culture}`;
|
||||
return `${workspaceBasePath}/${structureItem.unique}/${this._workspaceActiveVariantId?.toCultureString()}`;
|
||||
}
|
||||
|
||||
override render() {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './fallback-permission-condition/constants.js';
|
||||
@@ -0,0 +1 @@
|
||||
export const UMB_FALLBACK_USER_PERMISSION_CONDITION_ALIAS = 'Umb.Condition.UserPermission.Fallback';
|
||||
@@ -0,0 +1,142 @@
|
||||
import { expect } from '@open-wc/testing';
|
||||
import { customElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbControllerHostElementMixin } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbCurrentUserContext, UmbCurrentUserStore } from '@umbraco-cms/backoffice/current-user';
|
||||
import { UmbNotificationContext } from '@umbraco-cms/backoffice/notification';
|
||||
import { UmbFallbackUserPermissionCondition } from './fallback-user-permission.condition';
|
||||
import { UMB_USER_PERMISSION_DOCUMENT_READ } from '@umbraco-cms/backoffice/document';
|
||||
import { UMB_FALLBACK_USER_PERMISSION_CONDITION_ALIAS } from './constants';
|
||||
|
||||
@customElement('test-controller-host')
|
||||
class UmbTestControllerHostElement extends UmbControllerHostElementMixin(HTMLElement) {
|
||||
currentUserContext = new UmbCurrentUserContext(this);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
new UmbNotificationContext(this);
|
||||
new UmbCurrentUserStore(this);
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.currentUserContext.load();
|
||||
}
|
||||
}
|
||||
|
||||
describe('UmbFallbackUserPermissionCondition', () => {
|
||||
let hostElement: UmbTestControllerHostElement;
|
||||
let condition: UmbFallbackUserPermissionCondition;
|
||||
|
||||
beforeEach(async () => {
|
||||
hostElement = new UmbTestControllerHostElement();
|
||||
document.body.appendChild(hostElement);
|
||||
await hostElement.init();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
describe('Fallback Permissions', () => {
|
||||
it('should permit the condition when allOf is satisfied', (done) => {
|
||||
let callbackCount = 0;
|
||||
|
||||
// We expect to find the read permission in the fallback permissions
|
||||
condition = new UmbFallbackUserPermissionCondition(hostElement, {
|
||||
host: hostElement,
|
||||
config: {
|
||||
alias: UMB_FALLBACK_USER_PERMISSION_CONDITION_ALIAS,
|
||||
allOf: [UMB_USER_PERMISSION_DOCUMENT_READ],
|
||||
},
|
||||
onChange: () => {
|
||||
callbackCount++;
|
||||
if (callbackCount === 1) {
|
||||
expect(condition.permitted).to.be.true;
|
||||
condition.hostDisconnected();
|
||||
done();
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should forbid the condition when allOf is not satisfied', (done) => {
|
||||
let callbackCount = 0;
|
||||
|
||||
// We expect to find the read permission in the fallback permissions
|
||||
condition = new UmbFallbackUserPermissionCondition(hostElement, {
|
||||
host: hostElement,
|
||||
config: {
|
||||
alias: UMB_FALLBACK_USER_PERMISSION_CONDITION_ALIAS,
|
||||
allOf: [UMB_USER_PERMISSION_DOCUMENT_READ, 'non-existing-permission'],
|
||||
},
|
||||
onChange: () => {
|
||||
callbackCount++;
|
||||
if (callbackCount === 1) {
|
||||
expect(condition.permitted).to.be.false;
|
||||
condition.hostDisconnected();
|
||||
done();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// The onChange callback is not called when the condition is false, so we need to wait and check manually
|
||||
setTimeout(() => {
|
||||
expect(condition.permitted).to.be.false;
|
||||
condition.hostDisconnected();
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
it('should permit the condition when oneOf is satisfied', (done) => {
|
||||
let callbackCount = 0;
|
||||
|
||||
// We expect to find the read permission in the fallback permissions
|
||||
condition = new UmbFallbackUserPermissionCondition(hostElement, {
|
||||
host: hostElement,
|
||||
config: {
|
||||
alias: UMB_FALLBACK_USER_PERMISSION_CONDITION_ALIAS,
|
||||
oneOf: [UMB_USER_PERMISSION_DOCUMENT_READ, 'non-existing-permission'],
|
||||
},
|
||||
onChange: () => {
|
||||
/* The onChange callback is not called when the condition is false, so this should never be called
|
||||
But in case it is, we want to fail the test */
|
||||
callbackCount++;
|
||||
if (callbackCount === 1) {
|
||||
expect(condition.permitted).to.be.true;
|
||||
condition.hostDisconnected();
|
||||
done();
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should forbid the condition when oneOf is not satisfied', (done) => {
|
||||
let callbackCount = 0;
|
||||
|
||||
// We expect to find the read permission in the fallback permissions
|
||||
condition = new UmbFallbackUserPermissionCondition(hostElement, {
|
||||
host: hostElement,
|
||||
config: {
|
||||
alias: UMB_FALLBACK_USER_PERMISSION_CONDITION_ALIAS,
|
||||
oneOf: ['non-existing-permission', 'another-non-existing-permission'],
|
||||
},
|
||||
onChange: () => {
|
||||
/* The onChange callback is not called when the condition is false, so this should never be called
|
||||
But in case it is, we want to fail the test */
|
||||
callbackCount++;
|
||||
if (callbackCount === 1) {
|
||||
expect(condition.permitted).to.be.false;
|
||||
condition.hostDisconnected();
|
||||
done();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// The onChange callback is not called when the condition is false, so we need to wait and check manually
|
||||
setTimeout(() => {
|
||||
expect(condition.permitted).to.be.false;
|
||||
condition.hostDisconnected();
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import type { UmbFallbackUserPermissionConditionConfig } from './types.js';
|
||||
import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user';
|
||||
import type { UmbConditionControllerArguments, UmbExtensionCondition } from '@umbraco-cms/backoffice/extension-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export class UmbFallbackUserPermissionCondition
|
||||
extends UmbConditionBase<UmbFallbackUserPermissionConditionConfig>
|
||||
implements UmbExtensionCondition
|
||||
{
|
||||
constructor(
|
||||
host: UmbControllerHost,
|
||||
args: UmbConditionControllerArguments<UmbFallbackUserPermissionConditionConfig>,
|
||||
) {
|
||||
super(host, args);
|
||||
|
||||
this.consumeContext(UMB_CURRENT_USER_CONTEXT, (context) => {
|
||||
this.observe(
|
||||
context?.currentUser,
|
||||
(currentUser) => {
|
||||
const fallbackPermissions = currentUser?.fallbackPermissions || [];
|
||||
this.#check(fallbackPermissions);
|
||||
},
|
||||
'umbUserFallbackPermissionConditionObserver',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#check(verbs: Array<string>) {
|
||||
/* we default to true se we don't require both allOf and oneOf to be defined
|
||||
but they can be combined for more complex scenarios */
|
||||
let allOfPermitted = true;
|
||||
let oneOfPermitted = true;
|
||||
|
||||
// check if all of the verbs are present
|
||||
if (this.config.allOf?.length) {
|
||||
allOfPermitted = this.config.allOf.every((verb) => verbs.includes(verb));
|
||||
}
|
||||
|
||||
// check if at least one of the verbs is present
|
||||
if (this.config.oneOf?.length) {
|
||||
oneOfPermitted = this.config.oneOf.some((verb) => verbs.includes(verb));
|
||||
}
|
||||
|
||||
// if neither allOf or oneOf is defined we default to false
|
||||
if (!allOfPermitted && !oneOfPermitted) {
|
||||
allOfPermitted = false;
|
||||
oneOfPermitted = false;
|
||||
}
|
||||
|
||||
this.permitted = allOfPermitted && oneOfPermitted;
|
||||
}
|
||||
}
|
||||
|
||||
export { UmbFallbackUserPermissionCondition as api };
|
||||
@@ -0,0 +1,10 @@
|
||||
import { UMB_FALLBACK_USER_PERMISSION_CONDITION_ALIAS } from './constants.js';
|
||||
|
||||
export const manifests: Array<UmbExtensionManifest> = [
|
||||
{
|
||||
type: 'condition',
|
||||
name: 'User Fallback Permission Condition',
|
||||
alias: UMB_FALLBACK_USER_PERMISSION_CONDITION_ALIAS,
|
||||
api: () => import('./fallback-user-permission.condition.js'),
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { UmbConditionConfigBase } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
export type UmbFallbackUserPermissionConditionConfig =
|
||||
UmbConditionConfigBase<'Umb.Condition.UserPermission.Fallback'> & {
|
||||
/**
|
||||
* The user must have all of the permissions in this array for the condition to be met.
|
||||
* @example
|
||||
* ["Umb.PermissionOne", "Umb.PermissionTwo"]
|
||||
*/
|
||||
allOf?: Array<string>;
|
||||
|
||||
/**
|
||||
* The user must have at least one of the permissions in this array for the condition to be met.
|
||||
* @example
|
||||
* ["Umb.PermissionOne", "Umb.PermissionTwo"]
|
||||
*/
|
||||
oneOf?: Array<string>;
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface UmbExtensionConditionConfigMap {
|
||||
UmbFallbackUserPermissionConditionConfig: UmbFallbackUserPermissionConditionConfig;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './components/index.js';
|
||||
export * from './constants.js';
|
||||
export * from './modals/index.js';
|
||||
|
||||
export type * from './types.js';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { manifests as userPermissionModalManifests } from './modals/manifests.js';
|
||||
import { manifests as fallbackConditionPermission } from './fallback-permission-condition/manifests.js';
|
||||
|
||||
export const manifests: Array<UmbExtensionManifest> = [...userPermissionModalManifests];
|
||||
export const manifests: Array<UmbExtensionManifest> = [...userPermissionModalManifests, ...fallbackConditionPermission];
|
||||
|
||||
Reference in New Issue
Block a user