diff --git a/packages/core/acl/src/acl.ts b/packages/core/acl/src/acl.ts index 1bbd5949e..a604dce66 100644 --- a/packages/core/acl/src/acl.ts +++ b/packages/core/acl/src/acl.ts @@ -1,12 +1,12 @@ import { Action } from '@nocobase/resourcer'; import EventEmitter from 'events'; +import parse from 'json-templates'; import compose from 'koa-compose'; import lodash from 'lodash'; import { AclAvailableAction, AvailableActionOptions } from './acl-available-action'; import { ACLAvailableStrategy, AvailableStrategyOptions, predicate } from './acl-available-strategy'; import { ACLRole, RoleActionParams } from './acl-role'; import { AllowManager } from './allow-manager'; -const parse = require('json-templates'); interface CanResult { role: string; diff --git a/packages/core/client/src/collection-manager/interfaces/properties/operators.ts b/packages/core/client/src/collection-manager/interfaces/properties/operators.ts index 15d43e2ca..60a909d07 100644 --- a/packages/core/client/src/collection-manager/interfaces/properties/operators.ts +++ b/packages/core/client/src/collection-manager/interfaces/properties/operators.ts @@ -70,6 +70,14 @@ export const number = [ export const id = [ { label: '{{t("is")}}', value: '$eq', selected: true }, { label: '{{t("is not")}}', value: '$ne' }, + { + label: '{{t("is variable")}}', + value: '$isVar', + schema: { + 'x-component': 'VariableCascader', + 'x-component-props': {}, + }, + }, { label: '{{t("is current logged-in user")}}', value: '$isCurrentUser', diff --git a/packages/core/client/src/schema-component/antd/filter/DynamicComponent.tsx b/packages/core/client/src/schema-component/antd/filter/DynamicComponent.tsx index d8a6cad3f..d8ac71f51 100644 --- a/packages/core/client/src/schema-component/antd/filter/DynamicComponent.tsx +++ b/packages/core/client/src/schema-component/antd/filter/DynamicComponent.tsx @@ -1,10 +1,52 @@ import { createForm, onFieldValueChange } from '@formily/core'; -import { FieldContext, FormContext } from '@formily/react'; +import { connect, FieldContext, FormContext } from '@formily/react'; import { merge } from '@formily/shared'; +import { Cascader } from 'antd'; import React, { useContext, useMemo } from 'react'; import { SchemaComponent } from '../../core'; -import { useComponent } from '../../hooks'; +import { useCompile, useComponent } from '../../hooks'; import { FilterContext } from './context'; +import { useFilterOptions } from './useFilterActionProps'; + +const VariableCascader = connect((props) => { + const fields = useFilterOptions('users'); + const compile = useCompile(); + const { value, onChange } = props; + return ( + { + onChange(value ? value.join('.') : null); + }} + options={compile([ + { + title: '{{t("Current user")}}', + name: 'currentUser', + children: fields + .filter((field) => { + if (!field.target) { + return true; + } + return field.type === 'belongsTo'; + }) + .map((field) => { + if (field.children) { + field.children = field.children.filter((child) => { + return !child.target; + }); + } + return field; + }), + }, + ])} + /> + ); +}); export const DynamicComponent = (props) => { const { dynamicComponent } = useContext(FilterContext); @@ -38,6 +80,9 @@ export const DynamicComponent = (props) => { 'x-validator': undefined, 'x-decorator': undefined, }} + components={{ + VariableCascader, + }} /> ); diff --git a/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts b/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts index 4b6acbe69..74f25e3df 100644 --- a/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts +++ b/packages/core/client/src/schema-component/antd/filter/useFilterActionProps.ts @@ -24,6 +24,8 @@ export const useFilterOptions = (collectionName: string) => { const { nested, children, operators } = fieldInterface.filterable; const option = { name: field.name, + type: field.type, + target: field.target, title: field?.uiSchema?.title || field.name, schema: field?.uiSchema, operators: diff --git a/packages/plugins/users/package.json b/packages/plugins/users/package.json index 0d30b3d62..a65b521ea 100644 --- a/packages/plugins/users/package.json +++ b/packages/plugins/users/package.json @@ -14,7 +14,8 @@ }, "devDependencies": { "@nocobase/test": "0.7.1-alpha.7", - "@types/jsonwebtoken": "^8.5.8" + "@types/jsonwebtoken": "^8.5.8", + "json-templates": "^4.2.0" }, "gitHead": "7e9556e489007577fc0ed89063b3a9ce2f9aae53" } diff --git a/packages/plugins/users/src/middlewares/parseToken.ts b/packages/plugins/users/src/middlewares/parseToken.ts index b0e557f01..fc997ed8e 100644 --- a/packages/plugins/users/src/middlewares/parseToken.ts +++ b/packages/plugins/users/src/middlewares/parseToken.ts @@ -5,6 +5,7 @@ export function parseToken(options?: { plugin: UsersPlugin }) { return async function parseToken(ctx: Context, next: Next) { const user = await findUserByToken(ctx, options.plugin); if (user) { + console.log('appends', user.toJSON()); ctx.state.currentUser = user; setCurrentRole(ctx); } @@ -45,12 +46,18 @@ async function findUserByToken(ctx: Context, plugin: UsersPlugin) { try { const { userId } = await plugin.jwtService.decode(token); - + const collection = ctx.db.getCollection('users'); + const appends = ['roles']; + for (const [, field] of collection.fields) { + if (field.type === 'belongsTo') { + appends.push(field.name); + } + } return await ctx.db.getRepository('users').findOne({ + appends, filter: { id: userId, }, - appends: ['roles'], }); } catch (error) { return null; diff --git a/packages/plugins/users/src/server.ts b/packages/plugins/users/src/server.ts index 53c02caa0..821f77119 100644 --- a/packages/plugins/users/src/server.ts +++ b/packages/plugins/users/src/server.ts @@ -1,5 +1,6 @@ import { Collection, Op } from '@nocobase/database'; import { Plugin } from '@nocobase/server'; +import parse from 'json-templates'; import { resolve } from 'path'; import { namespace } from './'; import * as actions from './actions/users'; @@ -35,6 +36,12 @@ export default class UsersPlugin extends Plugin { [Op.eq]: ctx?.app?.ctx?.state?.currentUser?.id || -1, }; }, + $isVar(val, ctx) { + const obj = parse({ val: `{{${val}}}` })(JSON.parse(JSON.stringify(ctx?.app?.ctx?.state))); + return { + [Op.eq]: obj.val, + }; + }, }); this.db.registerModels({ UserModel }); this.db.on('users.afterCreateWithAssociations', async (model, options) => {