diff --git a/adminforth/modules/configValidator.ts b/adminforth/modules/configValidator.ts index f283013c6..5e8637fa2 100644 --- a/adminforth/modules/configValidator.ts +++ b/adminforth/modules/configValidator.ts @@ -1,8 +1,9 @@ -import { - AdminForthConfig, - AdminForthResource, - IAdminForth, IConfigValidator, +import { + AdminForthConfig, + AdminForthResource, + IAdminForth, IConfigValidator, AdminForthBulkAction, + AdminForthActionInput, AdminForthInputConfig, AdminForthConfigCustomization, AdminForthResourceInput, @@ -388,7 +389,7 @@ export default class ConfigValidator implements IConfigValidator { }); } - validateAndNormalizeCustomActions(resInput: AdminForthResourceInput, res: Partial, errors: string[]): any[] { + validateAndNormalizeCustomActions(resInput: AdminForthResourceInput, res: Partial, errors: string[]): AdminForthActionInput[] { if (!resInput.options?.actions) { return []; } @@ -430,13 +431,18 @@ export default class ConfigValidator implements IConfigValidator { action.showIn.showThreeDotsMenu = action.showIn.showThreeDotsMenu ?? false; } + if (typeof action.allowed === 'boolean') { + const val = action.allowed; + action.allowed = () => val; + } + const shownInNonBulk = action.showIn.list || action.showIn.listThreeDotsMenu || action.showIn.showButton || action.showIn.showThreeDotsMenu; if (shownInNonBulk && !action.action && !action.url) { errors.push(`Resource "${res.resourceId}" action "${action.name}" has showIn enabled for non-bulk locations (list, listThreeDotsMenu, showButton, showThreeDotsMenu) but has no "action" or "url" handler. Either add an "action" handler or set those showIn flags to false.`); } }); - return actions; + return actions as AdminForthActionInput[]; } validateAndNormalizeResources(errors: string[], warnings: string[]): AdminForthResource[] { diff --git a/adminforth/modules/restApi.ts b/adminforth/modules/restApi.ts index 1b994337b..5bc391e69 100644 --- a/adminforth/modules/restApi.ts +++ b/adminforth/modules/restApi.ts @@ -23,9 +23,9 @@ import { afLogger } from "./logger.js"; import { ADMINFORTH_VERSION, listify, md5hash, getLoginPromptHTML, hookResponseError } from './utils.js'; import AdminForthAuth from "../auth.js"; -import { ActionCheckSource, AdminForthConfigMenuItem, AdminForthDataTypes, AdminForthFilterOperators, AdminForthResourceColumnInputCommon, AdminForthResourceFrontend, AdminForthResourcePages, +import { ActionCheckSource, AdminForthActionFront, AdminForthConfigMenuItem, AdminForthDataTypes, AdminForthFilterOperators, AdminForthResourceColumnInputCommon, AdminForthResourceFrontend, AdminForthResourcePages, AdminForthSortDirections, - AdminUser, AllowedActionsEnum, AllowedActionsResolved, + AdminUser, AllowedActionsEnum, AllowedActionsResolved, AnnouncementBadgeResponse, GetBaseConfigResponse, ShowInResolved} from "../types/Common.js"; @@ -1079,6 +1079,22 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { }) ); + const allowedCustomActions = []; + if (resource.options.actions) { + await Promise.all( + resource.options.actions.map(async (action) => { + if (typeof action.allowed === 'function') { + const res = await action.allowed({ adminUser, standardAllowedActions: allowedActions }); + if (res) { + allowedCustomActions.push(action); + } + } else { + allowedCustomActions.push(action); + } + }) + ); + } + // translate const translateRoutines: Record> = {}; translateRoutines.resLabel = tr(resource.label, `resource.${resource.resourceId}`); @@ -1191,12 +1207,10 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { confirm: action.confirm ? translated[`bulkActionConfirm${i}`] : action.confirm, }) ), - actions: resource.options.actions?.map((action) => ({ - ...action, - id: action.id!, - hasBulkHandler: !!action.bulkHandler, - bulkHandler: undefined, - })), + actions: allowedCustomActions.map(({ bulkHandler, allowed, action: actionFn, ...rest }) => ({ + ...rest, + ...(bulkHandler && { bulkHandler: true }), + })) as AdminForthActionFront[], allowedActions, } } @@ -2096,7 +2110,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { if (!action) { return { error: await tr(`Action {actionId} not found`, 'errors', { actionId }) }; } - if (action.allowed) { + if (typeof action.allowed === 'function') { const execAllowed = await action.allowed({ adminUser, standardAllowedActions: allowedActions }); if (!execAllowed) { return { error: await tr(`Action "{actionId}" not allowed`, 'errors', { actionId: action.name }) }; @@ -2148,7 +2162,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { if (!action.bulkHandler) { return { error: await tr(`Action "{actionId}" has no bulkHandler`, 'errors', { actionId }) }; } - if (action.allowed) { + if (typeof action.allowed === 'function') { const execAllowed = await action.allowed({ adminUser, standardAllowedActions: allowedActions }); if (!execAllowed) { return { error: await tr(`Action "{actionId}" not allowed`, 'errors', { actionId: action.name }) }; diff --git a/adminforth/spa/src/components/ThreeDotsMenu.vue b/adminforth/spa/src/components/ThreeDotsMenu.vue index 7a7030b4c..9ac7bd855 100644 --- a/adminforth/spa/src/components/ThreeDotsMenu.vue +++ b/adminforth/spa/src/components/ThreeDotsMenu.vue @@ -103,7 +103,6 @@ import { useRoute, useRouter } from 'vue-router'; import CallActionWrapper from '@/components/CallActionWrapper.vue' import { ref, type ComponentPublicInstance, onMounted, onUnmounted } from 'vue'; import type { AdminForthActionFront, AdminForthBulkActionFront, AdminForthComponentDeclarationFull } from '@/types/Common'; -import type { AdminForthActionInput } from '@/types/Back'; import { Spinner } from '@/afcl'; const { list, alert} = useAdminforth(); @@ -137,7 +136,7 @@ function setComponentRef(el: ComponentPublicInstance | null, index: number) { } } -async function handleActionClick(action: AdminForthActionInput, payload: any) { +async function handleActionClick(action: AdminForthActionFront, payload: any) { list.closeThreeDotsDropdown(); await executeCustomAction({ actionId: action.id, diff --git a/adminforth/spa/src/utils/utils.ts b/adminforth/spa/src/utils/utils.ts index f77d8aa3f..7129f114d 100644 --- a/adminforth/spa/src/utils/utils.ts +++ b/adminforth/spa/src/utils/utils.ts @@ -803,7 +803,7 @@ export async function executeCustomBulkAction({ try { const action = resource?.options?.actions?.find((a: any) => a.id === actionId) as AdminForthActionFront | undefined; - if (action?.hasBulkHandler && action?.showIn?.bulkButton) { + if (action?.bulkHandler && action?.showIn?.bulkButton) { const result = await callAdminForthApi({ path: '/start_custom_bulk_action', method: 'POST', diff --git a/adminforth/spa/src/views/ListView.vue b/adminforth/spa/src/views/ListView.vue index 266a7b25f..0c2d82d07 100644 --- a/adminforth/spa/src/views/ListView.vue +++ b/adminforth/spa/src/views/ListView.vue @@ -344,7 +344,7 @@ async function startCustomBulkActionInner(actionId: string | number) { const successResults = results.filter(r => r?.successMessage); if (successResults.length > 0) { alert({ - message: action?.bulkSuccessMessage ? action.bulkSuccessMessage : action?.hasBulkHandler ? successResults[0].successMessage : `${successResults.length} out of ${results.length} items processed successfully`, + message: action?.bulkSuccessMessage ? action.bulkSuccessMessage : action?.bulkHandler ? successResults[0].successMessage : `${successResults.length} out of ${results.length} items processed successfully`, variant: 'success' }); } diff --git a/adminforth/types/Back.ts b/adminforth/types/Back.ts index f51b101a7..c9331bea8 100644 --- a/adminforth/types/Back.ts +++ b/adminforth/types/Back.ts @@ -1389,10 +1389,10 @@ export interface AdminForthActionInput { showThreeDotsMenu?: boolean, bulkButton?: boolean, }; - allowed?: (params: { + allowed?: boolean | ((params: { adminUser: AdminUser; standardAllowedActions: AllowedActions; - }) => boolean; + }) => boolean | Promise); url?: string; bulkHandler?: (params: { adminforth: IAdminForth; diff --git a/adminforth/types/Common.ts b/adminforth/types/Common.ts index d10beb1fb..bdd503799 100644 --- a/adminforth/types/Common.ts +++ b/adminforth/types/Common.ts @@ -316,7 +316,7 @@ export type FieldGroup = { export interface AdminForthActionFront extends Omit { id: string; - hasBulkHandler?: boolean; + bulkHandler?: boolean; } export interface AdminForthBulkActionFront extends Omit {