Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

Disable collection edit menu items entirely

default discord avatar
ambaltose2 months ago
4

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?

  • discord user avatar
    seanzubrickas
    2 months ago

    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
        },
      ],
    }
  • default discord avatar
    ambaltose2 months ago

    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

    pageType

    field.



    For delete I had a

    beforeDelete

    hook 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

    beforeOperation

    hook 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 🎉 !



    @364562745447940099

    I just realized the

    beforeDuplicate

    hook is only available on field level is that correct? In this case I would need it on collection level. There is also no operation

    duplicate

    available as far as I can see. Any ideas on that? I would stick to the

    beforeOperation

    on

    create

    for now.

  • discord user avatar
    seanzubrickas
    2 months ago

    Ahh shoot—you're right.

    beforeOperation

    would be the way to go here.



    That makes sense actually—

    beforeOperation

    fires 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)
            }
          }
        }
      ]
    }
  • default discord avatar
    ambaltose2 months ago

    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 on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get dedicated engineering support directly from the Payload team.