mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 21:49:25 +08:00
Merge branch 'next' into develop
This commit is contained in:
commit
96cc54d927
@ -2,9 +2,7 @@
|
|||||||
"version": "1.6.0-alpha.28",
|
"version": "1.6.0-alpha.28",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"useWorkspaces": true,
|
"useWorkspaces": true,
|
||||||
"npmClientArgs": [
|
"npmClientArgs": ["--ignore-engines"],
|
||||||
"--ignore-engines"
|
|
||||||
],
|
|
||||||
"command": {
|
"command": {
|
||||||
"version": {
|
"version": {
|
||||||
"forcePublish": true,
|
"forcePublish": true,
|
||||||
|
@ -304,30 +304,32 @@ export const ACLActionProvider = (props) => {
|
|||||||
const collection = useCollection();
|
const collection = useCollection();
|
||||||
const recordPkValue = useRecordPkValue();
|
const recordPkValue = useRecordPkValue();
|
||||||
const resource = useResourceName();
|
const resource = useResourceName();
|
||||||
const { parseAction } = useACLRoleContext();
|
const { parseAction, uiButtonSchemasBlacklist } = useACLRoleContext();
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
|
const currentUid = schema['x-uid'];
|
||||||
let actionPath = schema['x-acl-action'];
|
let actionPath = schema['x-acl-action'];
|
||||||
const editablePath = ['create', 'update', 'destroy', 'importXlsx'];
|
const editablePath = ['create', 'update', 'destroy', 'importXlsx'];
|
||||||
|
|
||||||
if (!actionPath && resource && schema['x-action']) {
|
if (!actionPath && resource && schema['x-action'] && editablePath.includes(schema['x-action'])) {
|
||||||
actionPath = `${resource}:${schema['x-action']}`;
|
actionPath = `${resource}:${schema['x-action']}`;
|
||||||
}
|
}
|
||||||
if (!actionPath?.includes(':')) {
|
if (actionPath && !actionPath?.includes(':')) {
|
||||||
actionPath = `${resource}:${actionPath}`;
|
actionPath = `${resource}:${actionPath}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = useMemo(
|
const params = useMemo(
|
||||||
() => parseAction(actionPath, { schema, recordPkValue }),
|
() => actionPath && parseAction(actionPath, { schema, recordPkValue }),
|
||||||
[parseAction, actionPath, schema, recordPkValue],
|
[parseAction, actionPath, schema, recordPkValue],
|
||||||
);
|
);
|
||||||
|
if (uiButtonSchemasBlacklist.includes(currentUid)) {
|
||||||
|
return <ACLActionParamsContext.Provider value={false}>{props.children}</ACLActionParamsContext.Provider>;
|
||||||
|
}
|
||||||
if (!actionPath) {
|
if (!actionPath) {
|
||||||
return <>{props.children}</>;
|
return <>{props.children}</>;
|
||||||
}
|
}
|
||||||
if (!resource) {
|
if (!resource) {
|
||||||
return <>{props.children}</>;
|
return <>{props.children}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params) {
|
if (!params) {
|
||||||
return <ACLActionParamsContext.Provider value={params}>{props.children}</ACLActionParamsContext.Provider>;
|
return <ACLActionParamsContext.Provider value={params}>{props.children}</ACLActionParamsContext.Provider>;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ export const DestroyActionInitializer = (props) => {
|
|||||||
const schema = {
|
const schema = {
|
||||||
title: '{{ t("Delete") }}',
|
title: '{{ t("Delete") }}',
|
||||||
'x-action': 'destroy',
|
'x-action': 'destroy',
|
||||||
|
'x-acl-action': 'destroy',
|
||||||
'x-component': 'Action',
|
'x-component': 'Action',
|
||||||
'x-use-component-props': 'useDestroyActionProps',
|
'x-use-component-props': 'useDestroyActionProps',
|
||||||
'x-toolbar': 'ActionSchemaToolbar',
|
'x-toolbar': 'ActionSchemaToolbar',
|
||||||
|
@ -20,6 +20,7 @@ export const LinkActionInitializer = (props) => {
|
|||||||
'x-settings': 'actionSettings:link',
|
'x-settings': 'actionSettings:link',
|
||||||
'x-component': props?.['x-component'] || 'Action.Link',
|
'x-component': props?.['x-component'] || 'Action.Link',
|
||||||
'x-use-component-props': 'useLinkActionProps',
|
'x-use-component-props': 'useLinkActionProps',
|
||||||
|
'x-decorator': 'ACLActionProvider',
|
||||||
};
|
};
|
||||||
|
|
||||||
const itemConfig = useSchemaInitializerItem();
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
@ -16,7 +16,11 @@ import { useSchemaToolbar } from '../../../application';
|
|||||||
import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings';
|
import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings';
|
||||||
import { useCollection_deprecated } from '../../../collection-manager';
|
import { useCollection_deprecated } from '../../../collection-manager';
|
||||||
import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/action/Action.Designer';
|
import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/action/Action.Designer';
|
||||||
import { SchemaSettingsLinkageRules, SchemaSettingsModalItem } from '../../../schema-settings';
|
import {
|
||||||
|
SchemaSettingsLinkageRules,
|
||||||
|
SchemaSettingsModalItem,
|
||||||
|
SchemaSettingAccessControl,
|
||||||
|
} from '../../../schema-settings';
|
||||||
import { useURLAndHTMLSchema } from './useURLAndHTMLSchema';
|
import { useURLAndHTMLSchema } from './useURLAndHTMLSchema';
|
||||||
|
|
||||||
export const SchemaSettingsActionLinkItem: FC = () => {
|
export const SchemaSettingsActionLinkItem: FC = () => {
|
||||||
@ -103,6 +107,7 @@ export const customizeLinkActionSettings = new SchemaSettings({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
SchemaSettingAccessControl,
|
||||||
{
|
{
|
||||||
name: 'remove',
|
name: 'remove',
|
||||||
sort: 100,
|
sort: 100,
|
||||||
|
@ -28,6 +28,7 @@ export const PopupActionInitializer = (props) => {
|
|||||||
openMode: defaultOpenMode,
|
openMode: defaultOpenMode,
|
||||||
refreshDataBlockRequest: true,
|
refreshDataBlockRequest: true,
|
||||||
},
|
},
|
||||||
|
'x-decorator': 'ACLActionProvider',
|
||||||
properties: {
|
properties: {
|
||||||
drawer: {
|
drawer: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
|
@ -20,6 +20,7 @@ export const UpdateActionInitializer = (props) => {
|
|||||||
type: 'void',
|
type: 'void',
|
||||||
title: '{{ t("Edit") }}',
|
title: '{{ t("Edit") }}',
|
||||||
'x-action': 'update',
|
'x-action': 'update',
|
||||||
|
'x-acl-action': 'update',
|
||||||
'x-toolbar': 'ActionSchemaToolbar',
|
'x-toolbar': 'ActionSchemaToolbar',
|
||||||
'x-settings': 'actionSettings:edit',
|
'x-settings': 'actionSettings:edit',
|
||||||
'x-component': 'Action',
|
'x-component': 'Action',
|
||||||
|
@ -14,7 +14,7 @@ import { useCollection_deprecated } from '../../../collection-manager';
|
|||||||
import { useCollection } from '../../../data-source';
|
import { useCollection } from '../../../data-source';
|
||||||
import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/action/Action.Designer';
|
import { ButtonEditor, RemoveButton } from '../../../schema-component/antd/action/Action.Designer';
|
||||||
import { SchemaSettingOpenModeSchemaItems } from '../../../schema-items';
|
import { SchemaSettingOpenModeSchemaItems } from '../../../schema-items';
|
||||||
import { SchemaSettingsLinkageRules } from '../../../schema-settings';
|
import { SchemaSettingsLinkageRules, SchemaSettingAccessControl } from '../../../schema-settings';
|
||||||
import { useOpenModeContext } from '../../popup/OpenModeProvider';
|
import { useOpenModeContext } from '../../popup/OpenModeProvider';
|
||||||
import { useCurrentPopupRecord } from '../../variable/variablesProvider/VariablePopupRecordProvider';
|
import { useCurrentPopupRecord } from '../../variable/variablesProvider/VariablePopupRecordProvider';
|
||||||
|
|
||||||
@ -57,6 +57,7 @@ export const customizePopupActionSettings = new SchemaSettings({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
SchemaSettingAccessControl,
|
||||||
{
|
{
|
||||||
name: 'remove',
|
name: 'remove',
|
||||||
sort: 100,
|
sort: 100,
|
||||||
|
@ -29,20 +29,12 @@ export class PMPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addSettings() {
|
addSettings() {
|
||||||
// this.app.pluginSettingsManager.add('acl', {
|
this.app.pluginSettingsManager.add('ui-schema-storage', {
|
||||||
// title: '{{t("Access control")}}',
|
title: '{{t("Block templates")}}',
|
||||||
// icon: 'LockOutlined',
|
icon: 'LayoutOutlined',
|
||||||
// Component: ACLPane,
|
Component: BlockTemplatesPane,
|
||||||
// aclSnippet: 'pm.acl.roles',
|
aclSnippet: 'pm.ui-schema-storage.block-templates',
|
||||||
// });
|
});
|
||||||
|
|
||||||
// Replaced by plugin-block-template
|
|
||||||
// this.app.pluginSettingsManager.add('ui-schema-storage', {
|
|
||||||
// title: '{{t("Block templates")}}',
|
|
||||||
// icon: 'LayoutOutlined',
|
|
||||||
// Component: BlockTemplatesPane,
|
|
||||||
// aclSnippet: 'pm.ui-schema-storage.block-templates',
|
|
||||||
// });
|
|
||||||
this.app.pluginSettingsManager.add('system-settings', {
|
this.app.pluginSettingsManager.add('system-settings', {
|
||||||
icon: 'SettingOutlined',
|
icon: 'SettingOutlined',
|
||||||
title: '{{t("System settings")}}',
|
title: '{{t("System settings")}}',
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { App } from 'antd';
|
||||||
|
import { SchemaSettingsActionModalItem } from './SchemaSettings';
|
||||||
|
import { useAPIClient } from '../api-client/hooks/useAPIClient';
|
||||||
|
import { useRequest } from '../api-client';
|
||||||
|
import { useACLContext } from '../acl';
|
||||||
|
|
||||||
|
export function AccessControl() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const apiClient = useAPIClient();
|
||||||
|
const resource = apiClient.resource('uiSchemas.roles', fieldSchema['x-uid']);
|
||||||
|
const { message } = App.useApp();
|
||||||
|
const { refresh, data }: any = useRequest(
|
||||||
|
{
|
||||||
|
url: `/uiSchemas/${fieldSchema['x-uid']}/roles:list`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
manual: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const { refresh: refreshRoleCheck } = useACLContext();
|
||||||
|
const AccessControl = (
|
||||||
|
<SchemaSettingsActionModalItem
|
||||||
|
scope={t}
|
||||||
|
title={t('Access control')}
|
||||||
|
schema={{
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
roles: {
|
||||||
|
type: 'array',
|
||||||
|
title: t('Roles'),
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-decorator-props': {
|
||||||
|
tooltip: t('If not set, all roles can see this action'),
|
||||||
|
},
|
||||||
|
'x-component': 'RemoteSelect',
|
||||||
|
'x-component-props': {
|
||||||
|
multiple: true,
|
||||||
|
objectValue: true,
|
||||||
|
dataSource: 'main',
|
||||||
|
service: {
|
||||||
|
resource: 'roles',
|
||||||
|
},
|
||||||
|
manual: false,
|
||||||
|
fieldNames: {
|
||||||
|
label: 'title',
|
||||||
|
value: 'name',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
initialValues={{
|
||||||
|
roles: data?.data,
|
||||||
|
}}
|
||||||
|
beforeOpen={() => !data && refresh()}
|
||||||
|
onSubmit={async ({ roles }) => {
|
||||||
|
await resource.set({ values: roles.map((v) => v.name) });
|
||||||
|
await refreshRoleCheck();
|
||||||
|
return message.success(t('Saved successfully'));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
return AccessControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SchemaSettingAccessControl = {
|
||||||
|
name: 'accessControl',
|
||||||
|
Component: AccessControl,
|
||||||
|
useVisible() {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
return fieldSchema['x-decorator'] === 'ACLActionProvider';
|
||||||
|
},
|
||||||
|
};
|
@ -725,8 +725,8 @@ export const SchemaSettingsActionModalItem: FC<SchemaSettingsActionModalItemProp
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}, {});
|
}, {});
|
||||||
await onSubmit?.(cloneDeep(visibleValues));
|
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
|
await onSubmit?.(cloneDeep(visibleValues));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { expect, test } from '@nocobase/test/e2e';
|
||||||
|
import { accessControlActionWithTable } from './template';
|
||||||
|
|
||||||
|
test.describe('Access control', () => {
|
||||||
|
test('popup、link、custom request support access control', async ({ page, mockPage, mockRecord }) => {
|
||||||
|
const nocoPage = await mockPage(accessControlActionWithTable).waitForInit();
|
||||||
|
await nocoPage.goto();
|
||||||
|
await page.getByLabel('block-item-CardItem-users-').hover();
|
||||||
|
//popup
|
||||||
|
await page.getByLabel('action-Action-Popup-customize').hover();
|
||||||
|
await page.getByLabel('designer-schema-settings-Action-actionSettings:popup-users').hover();
|
||||||
|
await expect(page.getByRole('menuitem', { name: 'Access control' })).toBeVisible();
|
||||||
|
await page.getByLabel('designer-schema-settings-Action-actionSettings:popup-users').hover();
|
||||||
|
await page.mouse.move(300, 0);
|
||||||
|
|
||||||
|
//link
|
||||||
|
await page.getByLabel('action-Action-Link-customize:').hover();
|
||||||
|
|
||||||
|
await page.getByLabel('designer-schema-settings-Action-actionSettings:link-users').hover();
|
||||||
|
await expect(page.getByRole('menuitem', { name: 'Access control' })).toBeVisible();
|
||||||
|
await page.mouse.move(300, 0);
|
||||||
|
|
||||||
|
// custom request
|
||||||
|
await page.getByLabel('action-CustomRequestAction-').hover();
|
||||||
|
await page.getByLabel('designer-schema-settings-CustomRequestAction-actionSettings:customRequest-users').hover();
|
||||||
|
await expect(page.getByRole('menuitem', { name: 'Access control' })).toBeVisible();
|
||||||
|
await page.mouse.move(300, 0);
|
||||||
|
});
|
||||||
|
test('access control with role ', async ({ page, mockPage, mockRecord }) => {
|
||||||
|
const nocoPage = await mockPage(accessControlActionWithTable).waitForInit();
|
||||||
|
await nocoPage.goto();
|
||||||
|
await page.getByLabel('block-item-CardItem-users-').hover();
|
||||||
|
//popup only member can see
|
||||||
|
await page.getByLabel('action-Action-Popup-customize').hover();
|
||||||
|
await page.getByLabel('designer-schema-settings-Action-actionSettings:popup-users').hover();
|
||||||
|
await page.getByRole('menuitem', { name: 'Access control' }).click();
|
||||||
|
await page.getByLabel('block-item-RemoteSelect-users').click();
|
||||||
|
await page.getByText('Member').click();
|
||||||
|
await page.getByRole('option', { name: 'Member' }).locator('div').click();
|
||||||
|
await page.getByLabel('block-item-RemoteSelect-users').click();
|
||||||
|
await page.getByRole('button', { name: 'Submit' }).click();
|
||||||
|
|
||||||
|
//root 角色有权限
|
||||||
|
await expect(page.getByLabel('action-Action-Popup-customize')).toBeVisible();
|
||||||
|
|
||||||
|
//切换 为admin
|
||||||
|
await page.getByTestId('user-center-button').click();
|
||||||
|
await page.getByText('Switch roleRoot').click();
|
||||||
|
await page.getByText('Admin', { exact: true }).click();
|
||||||
|
await expect(page.getByLabel('action-Action-Popup-customize')).not.toBeVisible();
|
||||||
|
|
||||||
|
// 切换 为 member
|
||||||
|
|
||||||
|
await page.getByTestId('user-center-button').click();
|
||||||
|
await page.getByText('Switch roleAdmin').click();
|
||||||
|
await page.getByText('Member').click();
|
||||||
|
await expect(page.getByLabel('action-Action-Popup-customize')).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
@ -1964,3 +1964,254 @@ export const whenClearingARelationshipFieldTheValueOfTheAssociatedFieldShouldBeC
|
|||||||
'x-index': 1,
|
'x-index': 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const accessControlActionWithTable = {
|
||||||
|
pageSchema: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Page',
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
properties: {
|
||||||
|
fvgd0c2akgf: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'page:addBlock',
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
properties: {
|
||||||
|
'0c4zy47hhyq': {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
properties: {
|
||||||
|
b1y881c771g: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
properties: {
|
||||||
|
hccefuo80kx: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-acl-action': 'users:view',
|
||||||
|
'x-decorator': 'DetailsBlockProvider',
|
||||||
|
'x-use-decorator-props': 'useDetailsWithPaginationDecoratorProps',
|
||||||
|
'x-decorator-props': {
|
||||||
|
dataSource: 'main',
|
||||||
|
collection: 'users',
|
||||||
|
readPretty: true,
|
||||||
|
action: 'list',
|
||||||
|
params: {
|
||||||
|
pageSize: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:detailsWithPagination',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
properties: {
|
||||||
|
fctprt7i7ut: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Details',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-use-component-props': 'useDetailsWithPaginationProps',
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
properties: {
|
||||||
|
b2xrbq4a060: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': 'details:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
properties: {
|
||||||
|
e2ccvx3c7o5: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Popup") }}',
|
||||||
|
'x-action': 'customize:popup',
|
||||||
|
'x-toolbar': 'ActionSchemaToolbar',
|
||||||
|
'x-settings': 'actionSettings:popup',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
openMode: 'drawer',
|
||||||
|
refreshDataBlockRequest: true,
|
||||||
|
},
|
||||||
|
'x-decorator': 'ACLActionProvider',
|
||||||
|
'x-action-context': {
|
||||||
|
dataSource: 'main',
|
||||||
|
collection: 'users',
|
||||||
|
},
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
properties: {
|
||||||
|
drawer: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Popup") }}',
|
||||||
|
'x-component': 'Action.Container',
|
||||||
|
'x-component-props': {
|
||||||
|
className: 'nb-action-popup',
|
||||||
|
},
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
properties: {
|
||||||
|
tabs: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Tabs',
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-initializer': 'popup:addTab',
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
properties: {
|
||||||
|
tab1: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
title: '{{t("Details")}}',
|
||||||
|
'x-component': 'Tabs.TabPane',
|
||||||
|
'x-designer': 'Tabs.Designer',
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
properties: {
|
||||||
|
grid: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'popup:common:addBlock',
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
'x-uid': '3qfg5qfvsth',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'n0574ydwdua',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'm0039599o9q',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': '5t7ol82dt74',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'gn76wlmk619',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
'3jat4vour4y': {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
title: '{{ t("Custom request") }}',
|
||||||
|
'x-component': 'CustomRequestAction',
|
||||||
|
'x-action': 'customize:form:request',
|
||||||
|
'x-toolbar': 'ActionSchemaToolbar',
|
||||||
|
'x-settings': 'actionSettings:customRequest',
|
||||||
|
'x-decorator': 'CustomRequestAction.Decorator',
|
||||||
|
'x-action-settings': {
|
||||||
|
onSuccess: {
|
||||||
|
manualClose: false,
|
||||||
|
redirecting: false,
|
||||||
|
successMessage: '{{t("Request success")}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'void',
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
'x-uid': 'gu0shkseqoa',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 2,
|
||||||
|
},
|
||||||
|
'0tevnuro5d4': {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Link") }}',
|
||||||
|
'x-action': 'customize:link',
|
||||||
|
'x-toolbar': 'ActionSchemaToolbar',
|
||||||
|
'x-settings': 'actionSettings:link',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-use-component-props': 'useLinkActionProps',
|
||||||
|
'x-decorator': 'ACLActionProvider',
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
'x-uid': 'swtrz2mpnm4',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': '32u6fnlj0ti',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'details:configureFields',
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
'x-uid': '21ksski5wgs',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 2,
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Pagination',
|
||||||
|
'x-use-component-props': 'useDetailsPaginationProps',
|
||||||
|
'x-app-version': '1.6.0-beta.9',
|
||||||
|
'x-uid': '2cz3ilk7hmv',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'f6xosucy75q',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': '33tsqeap83o',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'ppglkb7uvt8',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'af78vam04ux',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'askp9xe9uag',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'huon5vcb8u8',
|
||||||
|
'x-async': true,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
@ -26,6 +26,7 @@ export * from './SchemaSettingsRenderEngine';
|
|||||||
export * from './hooks/useGetAriaLabelOfDesigner';
|
export * from './hooks/useGetAriaLabelOfDesigner';
|
||||||
export * from './hooks/useIsAllowToSetDefaultValue';
|
export * from './hooks/useIsAllowToSetDefaultValue';
|
||||||
export * from './SchemaSettingsLayoutItem';
|
export * from './SchemaSettingsLayoutItem';
|
||||||
|
export * from './SchemaSettingAccessControl';
|
||||||
export { default as useParseDataScopeFilter } from './hooks/useParseDataScopeFilter';
|
export { default as useParseDataScopeFilter } from './hooks/useParseDataScopeFilter';
|
||||||
export * from './isPatternDisabled';
|
export * from './isPatternDisabled';
|
||||||
export { SchemaSettingsPlugin } from './SchemaSettingsPlugin';
|
export { SchemaSettingsPlugin } from './SchemaSettingsPlugin';
|
||||||
|
@ -43,6 +43,23 @@ export async function checkAction(ctx, next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const availableActions = ctx.app.acl.getAvailableActions();
|
const availableActions = ctx.app.acl.getAvailableActions();
|
||||||
|
let uiButtonSchemasBlacklist = [];
|
||||||
|
if (currentRole !== 'root') {
|
||||||
|
const eqCurrentRoleList = await ctx.db
|
||||||
|
.getRepository('uiButtonSchemasRoles')
|
||||||
|
.find({
|
||||||
|
filter: { 'roleName.$eq': currentRole },
|
||||||
|
})
|
||||||
|
.then((list) => list.map((v) => v.uid));
|
||||||
|
|
||||||
|
const NECurrentRoleList = await ctx.db
|
||||||
|
.getRepository('uiButtonSchemasRoles')
|
||||||
|
.find({
|
||||||
|
filter: { 'roleName.$ne': currentRole },
|
||||||
|
})
|
||||||
|
.then((list) => list.map((v) => v.uid));
|
||||||
|
uiButtonSchemasBlacklist = NECurrentRoleList.filter((uid) => !eqCurrentRoleList.includes(uid));
|
||||||
|
}
|
||||||
|
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
...role.toJSON(),
|
...role.toJSON(),
|
||||||
@ -53,6 +70,7 @@ export async function checkAction(ctx, next) {
|
|||||||
allowConfigure: roleInstance.get('allowConfigure'),
|
allowConfigure: roleInstance.get('allowConfigure'),
|
||||||
allowMenuItemIds: roleInstance.get('menuUiSchemas').map((uiSchema) => uiSchema.get('x-uid')),
|
allowMenuItemIds: roleInstance.get('menuUiSchemas').map((uiSchema) => uiSchema.get('x-uid')),
|
||||||
allowAnonymous: !!anonymous,
|
allowAnonymous: !!anonymous,
|
||||||
|
uiButtonSchemasBlacklist,
|
||||||
};
|
};
|
||||||
|
|
||||||
await next();
|
await next();
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Action, useAPIClient, useRequest, withDynamicSchemaProps } from '@nocobase/client';
|
import { Action, useAPIClient, useRequest, withDynamicSchemaProps, ACLActionProvider } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useFieldSchema } from '@formily/react';
|
import { useFieldSchema } from '@formily/react';
|
||||||
import { listByCurrentRoleUrl } from '../constants';
|
import { listByCurrentRoleUrl } from '../constants';
|
||||||
@ -16,23 +16,23 @@ import { CustomRequestActionDesigner } from './CustomRequestActionDesigner';
|
|||||||
|
|
||||||
export const CustomRequestActionACLDecorator = (props) => {
|
export const CustomRequestActionACLDecorator = (props) => {
|
||||||
const apiClient = useAPIClient();
|
const apiClient = useAPIClient();
|
||||||
const isRoot = apiClient.auth.role === 'root';
|
// const isRoot = apiClient.auth.role === 'root';
|
||||||
const fieldSchema = useFieldSchema();
|
// const fieldSchema = useFieldSchema();
|
||||||
const { data } = useRequest<{ data: string[] }>(
|
// const { data } = useRequest<{ data: string[] }>(
|
||||||
{
|
// {
|
||||||
url: listByCurrentRoleUrl,
|
// url: listByCurrentRoleUrl,
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
manual: isRoot,
|
// manual: isRoot,
|
||||||
cacheKey: listByCurrentRoleUrl,
|
// cacheKey: listByCurrentRoleUrl,
|
||||||
},
|
// },
|
||||||
);
|
// );
|
||||||
const requestId = fieldSchema?.['x-custom-request-id'] || fieldSchema?.['x-uid'];
|
|
||||||
if (!isRoot && !data?.data?.includes(requestId)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return props.children;
|
// // if (!isRoot && !data?.data?.includes(fieldSchema?.['x-uid'])) {
|
||||||
|
// // return null;
|
||||||
|
// // }
|
||||||
|
|
||||||
|
return <ACLActionProvider>{props.children}</ACLActionProvider>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
|
@ -17,15 +17,13 @@ import {
|
|||||||
useCollection_deprecated,
|
useCollection_deprecated,
|
||||||
useDataSourceKey,
|
useDataSourceKey,
|
||||||
useDesignable,
|
useDesignable,
|
||||||
useRequest,
|
SchemaSettingAccessControl,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { App } from 'antd';
|
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { listByCurrentRoleUrl } from '../constants';
|
|
||||||
import { useCustomRequestVariableOptions, useGetCustomRequest } from '../hooks';
|
import { useCustomRequestVariableOptions, useGetCustomRequest } from '../hooks';
|
||||||
import { useCustomRequestsResource } from '../hooks/useCustomRequestsResource';
|
import { useCustomRequestsResource } from '../hooks/useCustomRequestsResource';
|
||||||
import { useTranslation } from '../locale';
|
import { useTranslation } from '../locale';
|
||||||
import { CustomRequestACLSchema, CustomRequestConfigurationFieldsSchema } from '../schemas';
|
import { CustomRequestConfigurationFieldsSchema } from '../schemas';
|
||||||
|
|
||||||
export function CustomRequestSettingsItem() {
|
export function CustomRequestSettingsItem() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -89,63 +87,6 @@ export function CustomRequestSettingsItem() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CustomRequestACL() {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const customRequestsResource = useCustomRequestsResource();
|
|
||||||
const { message } = App.useApp();
|
|
||||||
const { data, refresh } = useGetCustomRequest();
|
|
||||||
const { dn } = useDesignable();
|
|
||||||
const { refresh: refreshRoleCustomKeys } = useRequest<{ data: string[] }>(
|
|
||||||
{
|
|
||||||
url: listByCurrentRoleUrl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
manual: true,
|
|
||||||
cacheKey: listByCurrentRoleUrl,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SchemaSettingsActionModalItem
|
|
||||||
title={t('Access control')}
|
|
||||||
schema={CustomRequestACLSchema}
|
|
||||||
initialValues={{
|
|
||||||
roles: data?.data?.roles,
|
|
||||||
}}
|
|
||||||
beforeOpen={() => !data && refresh()}
|
|
||||||
onSubmit={async ({ roles }) => {
|
|
||||||
const isSelfRequest =
|
|
||||||
!fieldSchema['x-custom-request-id'] || fieldSchema['x-custom-request-id'] === fieldSchema['x-uid'];
|
|
||||||
|
|
||||||
if (!isSelfRequest) {
|
|
||||||
fieldSchema['x-custom-request-id'] = fieldSchema['x-uid'];
|
|
||||||
await dn.emit('patch', {
|
|
||||||
schema: {
|
|
||||||
'x-uid': fieldSchema['x-uid'],
|
|
||||||
'x-custom-request-id': fieldSchema['x-uid'],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await customRequestsResource.updateOrCreate({
|
|
||||||
values: {
|
|
||||||
key: fieldSchema['x-uid'],
|
|
||||||
roles,
|
|
||||||
},
|
|
||||||
filterKeys: ['key'],
|
|
||||||
});
|
|
||||||
refresh();
|
|
||||||
refreshRoleCustomKeys();
|
|
||||||
dn.refresh();
|
|
||||||
return message.success(t('Saved successfully'));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
@ -160,10 +101,7 @@ export const customRequestActionSettings = new SchemaSettings({
|
|||||||
name: 'request settings',
|
name: 'request settings',
|
||||||
Component: CustomRequestSettingsItem,
|
Component: CustomRequestSettingsItem,
|
||||||
},
|
},
|
||||||
{
|
SchemaSettingAccessControl,
|
||||||
name: 'accessControl',
|
|
||||||
Component: CustomRequestACL,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -19,8 +19,9 @@ import {
|
|||||||
useCollection,
|
useCollection,
|
||||||
useCollectionRecord,
|
useCollectionRecord,
|
||||||
useSchemaToolbar,
|
useSchemaToolbar,
|
||||||
|
SchemaSettingAccessControl,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { CustomRequestACL, CustomRequestSettingsItem } from './components/CustomRequestActionDesigner';
|
import { CustomRequestSettingsItem } from './components/CustomRequestActionDesigner';
|
||||||
|
|
||||||
export const customizeCustomRequestActionSettings = new SchemaSettings({
|
export const customizeCustomRequestActionSettings = new SchemaSettings({
|
||||||
name: 'actionSettings:customRequest',
|
name: 'actionSettings:customRequest',
|
||||||
@ -64,8 +65,10 @@ export const customizeCustomRequestActionSettings = new SchemaSettings({
|
|||||||
Component: CustomRequestSettingsItem,
|
Component: CustomRequestSettingsItem,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'accessControl',
|
...SchemaSettingAccessControl,
|
||||||
Component: CustomRequestACL,
|
useVisible() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'refreshDataBlockRequest',
|
name: 'refreshDataBlockRequest',
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file is part of the NocoBase (R) project.
|
|
||||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
||||||
* Authors: NocoBase Team.
|
|
||||||
*
|
|
||||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { DEFAULT_DATA_SOURCE_KEY } from '@nocobase/client';
|
|
||||||
import { generateNTemplate } from '../locale';
|
|
||||||
|
|
||||||
export const CustomRequestACLSchema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
roles: {
|
|
||||||
type: 'array',
|
|
||||||
title: generateNTemplate('Roles'),
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-decorator-props': {
|
|
||||||
tooltip: generateNTemplate('If not set, all roles can see this action'),
|
|
||||||
},
|
|
||||||
'x-component': 'RemoteSelect',
|
|
||||||
'x-component-props': {
|
|
||||||
multiple: true,
|
|
||||||
objectValue: true,
|
|
||||||
dataSource: DEFAULT_DATA_SOURCE_KEY,
|
|
||||||
service: {
|
|
||||||
resource: 'roles',
|
|
||||||
},
|
|
||||||
manual: false,
|
|
||||||
fieldNames: {
|
|
||||||
label: 'title',
|
|
||||||
value: 'name',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
@ -8,4 +8,3 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './CustomRequestConfigurationFields';
|
export * from './CustomRequestConfigurationFields';
|
||||||
export * from './CustomRequestACL';
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useFieldSchema } from '@formily/react';
|
import { useFieldSchema } from '@formily/react';
|
||||||
import { Action, Icon, useCompile, useComponent, withDynamicSchemaProps } from '@nocobase/client';
|
import { Action, Icon, useCompile, useComponent, withDynamicSchemaProps, ACLActionProvider } from '@nocobase/client';
|
||||||
import { Avatar } from 'antd';
|
import { Avatar } from 'antd';
|
||||||
import { createStyles } from 'antd-style';
|
import { createStyles } from 'antd-style';
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
@ -46,9 +46,9 @@ function Button() {
|
|||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const title = compile(fieldSchema.title);
|
const title = compile(fieldSchema.title);
|
||||||
return layout === WorkbenchLayout.Grid ? (
|
return layout === WorkbenchLayout.Grid ? (
|
||||||
<div title={fieldSchema.title} className={cx(styles.avatar)}>
|
<div title={title} className={cx(styles.avatar)}>
|
||||||
<Avatar style={{ backgroundColor }} size={48} icon={<Icon type={icon} />} />
|
<Avatar style={{ backgroundColor }} size={48} icon={<Icon type={icon} />} />
|
||||||
<div className={cx(styles.title)}>{fieldSchema.title}</div>
|
<div className={cx(styles.title)}>{title}</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<span>{title}</span>
|
<span>{title}</span>
|
||||||
@ -61,6 +61,7 @@ export const WorkbenchAction = withDynamicSchemaProps((props) => {
|
|||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const Component = useComponent(props?.targetComponent) || Action;
|
const Component = useComponent(props?.targetComponent) || Action;
|
||||||
return (
|
return (
|
||||||
|
<ACLActionProvider>
|
||||||
<Component
|
<Component
|
||||||
className={cx(className, styles.action, 'nb-action-panel')}
|
className={cx(className, styles.action, 'nb-action-panel')}
|
||||||
{...others}
|
{...others}
|
||||||
@ -69,5 +70,6 @@ export const WorkbenchAction = withDynamicSchemaProps((props) => {
|
|||||||
title={<Button />}
|
title={<Button />}
|
||||||
confirmTitle={fieldSchema.title}
|
confirmTitle={fieldSchema.title}
|
||||||
/>
|
/>
|
||||||
|
</ACLActionProvider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
SchemaSettingsActionLinkItem,
|
SchemaSettingsActionLinkItem,
|
||||||
useSchemaInitializer,
|
useSchemaInitializer,
|
||||||
ModalActionSchemaInitializerItem,
|
ModalActionSchemaInitializerItem,
|
||||||
|
SchemaSettingAccessControl,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -30,6 +31,12 @@ export const workbenchActionSettingsCustomRequest = new SchemaSettings({
|
|||||||
name: 'editLink',
|
name: 'editLink',
|
||||||
Component: SchemaSettingsActionLinkItem,
|
Component: SchemaSettingsActionLinkItem,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
...SchemaSettingAccessControl,
|
||||||
|
useVisible() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
sort: 800,
|
sort: 800,
|
||||||
name: 'd1',
|
name: 'd1',
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
useSchemaInitializer,
|
useSchemaInitializer,
|
||||||
useSchemaInitializerItem,
|
useSchemaInitializerItem,
|
||||||
ModalActionSchemaInitializerItem,
|
ModalActionSchemaInitializerItem,
|
||||||
|
SchemaSettingAccessControl,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -32,6 +33,12 @@ export const workbenchActionSettingsLink = new SchemaSettings({
|
|||||||
name: 'editLink',
|
name: 'editLink',
|
||||||
Component: SchemaSettingsActionLinkItem,
|
Component: SchemaSettingsActionLinkItem,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
...SchemaSettingAccessControl,
|
||||||
|
useVisible() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
sort: 800,
|
sort: 800,
|
||||||
name: 'd1',
|
name: 'd1',
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
useSchemaInitializer,
|
useSchemaInitializer,
|
||||||
useOpenModeContext,
|
useOpenModeContext,
|
||||||
ModalActionSchemaInitializerItem,
|
ModalActionSchemaInitializerItem,
|
||||||
|
SchemaSettingAccessControl,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -44,6 +45,12 @@ export const workbenchActionSettingsPopup = new SchemaSettings({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
...SchemaSettingAccessControl,
|
||||||
|
useVisible() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
sort: 800,
|
sort: 800,
|
||||||
name: 'd1',
|
name: 'd1',
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
useSchemaInitializer,
|
useSchemaInitializer,
|
||||||
useSchemaInitializerItem,
|
useSchemaInitializerItem,
|
||||||
ModalActionSchemaInitializerItem,
|
ModalActionSchemaInitializerItem,
|
||||||
|
SchemaSettingAccessControl,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -28,6 +29,12 @@ export const workbenchActionSettingsScanQrCode = new SchemaSettings({
|
|||||||
return { hasIconColor: true };
|
return { hasIconColor: true };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
...SchemaSettingAccessControl,
|
||||||
|
useVisible() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'd1',
|
name: 'd1',
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
|
@ -264,7 +264,7 @@ describe('view collection', function () {
|
|||||||
|
|
||||||
const Role = await collectionRepository.create({
|
const Role = await collectionRepository.create({
|
||||||
values: {
|
values: {
|
||||||
name: 'roles',
|
name: 'my_roles',
|
||||||
fields: [{ name: 'name', type: 'string' }],
|
fields: [{ name: 'name', type: 'string' }],
|
||||||
},
|
},
|
||||||
context: {},
|
context: {},
|
||||||
@ -276,7 +276,7 @@ describe('view collection', function () {
|
|||||||
values: [{ name: 'u1' }, { name: 'u2' }],
|
values: [{ name: 'u1' }, { name: 'u2' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
await db.getRepository('roles').create({
|
await db.getRepository('my_roles').create({
|
||||||
values: [{ name: 'r1' }, { name: 'r2' }],
|
values: [{ name: 'r1' }, { name: 'r2' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -327,7 +327,7 @@ describe('view collection', function () {
|
|||||||
collectionName: 'users',
|
collectionName: 'users',
|
||||||
name: 'roles',
|
name: 'roles',
|
||||||
type: 'belongsToMany',
|
type: 'belongsToMany',
|
||||||
target: 'roles',
|
target: 'my_roles',
|
||||||
through: 'test_view',
|
through: 'test_view',
|
||||||
foreignKey: 'user_id',
|
foreignKey: 'user_id',
|
||||||
otherKey: 'role_id',
|
otherKey: 'role_id',
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Plugin } from '@nocobase/client';
|
import { Plugin } from '@nocobase/client';
|
||||||
|
|
||||||
class PluginUISchemaStorageClient extends Plugin {
|
class PluginUISchemaStorageClient extends Plugin {
|
||||||
async load() {}
|
async load() {}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { defineCollection } from '@nocobase/database';
|
||||||
|
|
||||||
|
export default defineCollection({
|
||||||
|
name: 'uiButtonSchemasRoles',
|
||||||
|
dumpRules: 'required',
|
||||||
|
migrationRules: ['overwrite', 'schema-only'],
|
||||||
|
});
|
@ -39,5 +39,16 @@ export default {
|
|||||||
name: 'schema',
|
name: 'schema',
|
||||||
defaultValue: {},
|
defaultValue: {},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'belongsToMany',
|
||||||
|
name: 'roles',
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
through: 'uiButtonSchemasRoles',
|
||||||
|
target: 'roles',
|
||||||
|
foreignKey: 'uid',
|
||||||
|
otherKey: 'roleName',
|
||||||
|
sourceKey: 'x-uid',
|
||||||
|
targetKey: 'name',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
} as CollectionOptions;
|
} as CollectionOptions;
|
||||||
|
@ -42,7 +42,7 @@ export class PluginUISchemaStorageServer extends Plugin {
|
|||||||
|
|
||||||
this.app.acl.registerSnippet({
|
this.app.acl.registerSnippet({
|
||||||
name: 'ui.uiSchemas',
|
name: 'ui.uiSchemas',
|
||||||
actions: ['uiSchemas:*'],
|
actions: ['uiSchemas:*', 'uiSchemas.roles:list', 'uiSchemas.roles:set'],
|
||||||
});
|
});
|
||||||
|
|
||||||
db.on('uiSchemas.beforeCreate', function setUid(model) {
|
db.on('uiSchemas.beforeCreate', function setUid(model) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user