Hi everyone, I have a special case where certain pages in my pages collection are not allowed to be deleted or duplicated. These options are by default available in the three dot menu in the edit view. From the docs I read that I can only expand this menu with custom components. But I would like to disable it completly for these specific page records. They are identifiable by a select value. Is it possible to disable the three dot menu complely?
this is a tricky one—normally you could do this purely with access control (if a user doesn't have create or delete access the menu is hidden entirely by default) however, if I'm understanding correctly they should still be able to create, just not duplicate (which is tied to create) or delete.
So you can't hide the entire menu but you could:
For delete, that is straightforward (and you could scope this further to roles on users if you're using admin, super-admin, etc.)
access: {
delete: ({ data }) => {
if (data?.type === 'protected') return false
return true
},
}For Duplicate, it wouldn't be hidden, but you could utilize a beforeDuplicate hook to throw an error if a user tries to duplicate one of these specific docs:
hooks: {
beforeDuplicate: [
({ data }) => {
if (data?.type === ‘protected’) {
throw new APIError(‘This document cannot be duplicated’, 403)
}
return data
},
],
}Hi
@364562745447940099, thank you for the detailed information, yes it is a specific case where this behavior should only apply to certain documents of the pages collection, depending on the documents
pageTypefield.
For delete I had a
beforeDeletehook in place like this:
beforeDelete: [
async ({ req, id }) => {
const doc = await req.payload.findByID({
collection: "pages",
id,
depth: 0,
});
if (doc.pageType !== "dynamic") {
throw new Error("Fixed pages cannot be deleted.");
}
},
];But I like your suggestion to do it as access control much more, thanks!
Similar for the duplicate, I kinda solved this with an
beforeOperationhook like this:
beforeOperation: [
async ({ args, operation }) => {
if (operation === "create") {
args.data.pageType = "dynamic";
return args;
}
return args;
},
];It works but I just realized that this prevents the double page type but it still duplicates the content, so not 100% clean. Thanks again for your suggestion, really appreciate it 🎉 !
I just realized the
beforeDuplicatehook is only available on field level is that correct? In this case I would need it on collection level. There is also no operation
duplicateavailable as far as I can see. Any ideas on that? I would stick to the
beforeOperationon
createfor now.
Ahh shoot—you're right.
beforeOperationwould be the way to go here.
That makes sense actually—
beforeOperationfires before Payload reads or processes any data — so at that point you don't have access to the document's field values.
You could try using
beforeChange— it fires after data is available but before the database write
hooks: {
beforeChange: [
async ({ data, operation }) => {
if (operation === 'create' && data.pageType === 'protected') {
throw new APIError('Protected pages cannot be duplicated.', 403)
}
}
]
}The one tradeoff to the above is this blocks all creates with whatever "protected" value you're checking against, not just duplicates, so in order for whatever type of user you want to be able to create, or assign that value to docs you could do something like this:
hooks: {
beforeChange: [
async ({ data, operation, req }) => {
if (operation === 'create' && data.pageType === 'protected') {
const isSuperAdmin = req.user?.role === 'super-admin'
if (!isSuperAdmin) {
throw new APIError('Protected pages cannot be duplicated.', 403)
}
}
}
]
}Understandable, I think this tradeoff is fine for me. Goal is that these protected types only ever exist once. I will prepopulate the collection with these "protected" pages so there is no need to create them after anyways 😄 I just want to make sure no editor acidentially duplicates them or anything. Thanks again for the insights 👍
Star
Discord
online
Get dedicated engineering support directly from the Payload team.