diff --git a/src/collections/Announcements.ts b/src/collections/Announcements.ts index 897258f..2e7e26c 100644 --- a/src/collections/Announcements.ts +++ b/src/collections/Announcements.ts @@ -1,5 +1,5 @@ import { CollectionConfig } from 'payload' -import { isAdminOrEmployee } from '@/collections/access/admin' +import { hide, isAdminOrEmployee } from '@/collections/access/admin' import { nextSunday } from '@/utils/sunday' export const Announcements: CollectionConfig = { @@ -46,6 +46,9 @@ export const Announcements: CollectionConfig = { required: true } ], + admin: { + hidden: hide + }, access: { read: () => true, create: isAdminOrEmployee(), diff --git a/src/collections/Blog.ts b/src/collections/Blog.ts index 3baab58..fc1d651 100644 --- a/src/collections/Blog.ts +++ b/src/collections/Blog.ts @@ -1,5 +1,5 @@ import { CollectionConfig } from 'payload' -import { isAdminOrEmployee } from '@/collections/access/admin' +import { hide, isAdminOrEmployee } from '@/collections/access/admin' import { ParagraphBlock } from '@/collections/blocks/Paragraph' import { DocumentBlock } from '@/collections/blocks/Document' import { ContactformBlock } from '@/collections/blocks/Contactform' @@ -62,7 +62,8 @@ export const Blog: CollectionConfig = { }, ], admin: { - useAsTitle: 'title' + useAsTitle: 'title', + hidden: hide }, access: { read: () => true, diff --git a/src/collections/Churches.ts b/src/collections/Churches.ts index 80d0bfb..a77d1a2 100644 --- a/src/collections/Churches.ts +++ b/src/collections/Churches.ts @@ -1,5 +1,5 @@ import { CollectionConfig } from 'payload' -import { isAdminOrEmployee } from '@/collections/access/admin' +import { hide, isAdminOrEmployee } from '@/collections/access/admin' export const Churches: CollectionConfig = { slug: 'church', @@ -31,6 +31,7 @@ export const Churches: CollectionConfig = { ], admin: { useAsTitle: 'name', + hidden: hide }, access: { read: () => true, diff --git a/src/collections/ContactPerson.ts b/src/collections/ContactPerson.ts index 83f4476..5d8cd8b 100644 --- a/src/collections/ContactPerson.ts +++ b/src/collections/ContactPerson.ts @@ -1,5 +1,5 @@ import { CollectionConfig } from 'payload' -import { isAdminOrEmployee } from '@/collections/access/admin' +import { hide, isAdminOrEmployee } from '@/collections/access/admin' export const ContactPerson: CollectionConfig = { slug: 'contactPerson', @@ -38,7 +38,8 @@ export const ContactPerson: CollectionConfig = { } ], admin: { - useAsTitle: "name" + useAsTitle: "name", + hidden: hide }, access: { read: () => true, diff --git a/src/collections/Documents.ts b/src/collections/Documents.ts index 1b3651b..b181852 100644 --- a/src/collections/Documents.ts +++ b/src/collections/Documents.ts @@ -1,4 +1,5 @@ import type { CollectionConfig } from 'payload' +import { hide, isAdminOrEmployee } from '@/collections/access/admin' export const Documents: CollectionConfig = { slug: 'documents', @@ -12,8 +13,13 @@ export const Documents: CollectionConfig = { }, access: { read: () => true, + update: isAdminOrEmployee(), + delete: isAdminOrEmployee() }, fields: [], + admin: { + hidden: hide + }, upload: { mimeTypes: ['application/pdf'] }, diff --git a/src/collections/Events.ts b/src/collections/Events.ts index 648feb6..14a49b9 100644 --- a/src/collections/Events.ts +++ b/src/collections/Events.ts @@ -1,5 +1,6 @@ import { CollectionConfig } from 'payload' -import { isAdminOrEmployee } from '@/collections/access/admin' +import { stringify } from 'qs-esm' +import { Event, Group, User } from '@/payload-types' export const Events: CollectionConfig = { slug: 'event', @@ -12,14 +13,6 @@ export const Events: CollectionConfig = { }, }, fields: [ - { - name: 'photo', - label: { - de: 'Foto', - }, - type: 'upload', - relationTo: 'media', - }, { name: 'title', type: 'text', @@ -67,7 +60,7 @@ export const Events: CollectionConfig = { return 'You are not allowed to do this' } - if (user.roles === 'user' && value) { + if (user.roles === 'user' && value && value.length > 0) { return 'Sie sind nur erlaubt Veranstaltungen für Gruppen zu erstellen.' } @@ -82,7 +75,20 @@ export const Events: CollectionConfig = { label: { de: 'Gruppe', }, - validate: (value: any, options: { req: { user: any } }) => { + access: { + update: ({req: { user}, data}) => { + if(user && (user.roles == "admin" || user.roles =="employee")) { + return true + } + + if(hasGroup(user, data)) { + return true + } + + return false + } + }, + validate: (value, options: { req: { user: any } }) => { let user = options.req.user if (!user) { @@ -90,8 +96,16 @@ export const Events: CollectionConfig = { } if (user.roles === 'user') { - if (!user.groups || (user.groups && !user.groups.includes(value))) { - return 'Sie können nur Veranstaltungen für Ihre eigene Gruppen erstellen!' + if(!Array.isArray(value) || value.length === 0) { + return 'Sie müssen die Veranstaltung verknüpfen mit ihrer Gruppe.' + } + + if(!Array.isArray(user.groups) || user.groups.length === 0) { + return "Sie sind kein Mitglied einer Gruppe, und können deswegen keine Veranstaltung erstellen." + } + + if(!value.every(id => user.groups.includes(id))) { + return "Sie sind nur berechtigt Veranstaltungen für ihrer Gruppe zu erstellen" } } @@ -131,8 +145,19 @@ export const Events: CollectionConfig = { de: "Anmeldelink" } }, + { + name: 'photo', + label: { + de: 'Foto', + }, + type: 'upload', + relationTo: 'media', + }, { name: 'flyer', + label: { + de: "Flyer (PDF)" + }, type: 'upload', relationTo: 'documents' }, @@ -160,6 +185,70 @@ export const Events: CollectionConfig = { }, access: { read: () => true, - delete: isAdminOrEmployee(), + // admins and employees can delete, others only if they are member of the group + delete: async ({ req: { user }, id }) => { + if (!user) { + return false + } + + if(user.roles === 'admin' || user.roles === 'employee') + return true + + if(typeof id !== 'string') { + return false + } + + const event = await fetchEvent(id) + if (hasGroup(user, event)) { + return true + } + + return false + }, }, } + +/** + * Check if we have + * - a user + * - data with groups + * - the user is member of one of the groups + * + * @param user + * @param data + */ +const hasGroup = (user: null | User , data: Partial | undefined) => { + return user + && user.roles === 'user' + && data + && Array.isArray(data.group) + && data.group.length > 0 + && data.group.some((group: string | Group) => { + if (!Array.isArray(user.groups)) { + return false; + } + + if (typeof group === "string") + return user.groups.includes(group) + else + return user.groups.includes(group.id) + }) +} + +/** + * Fetch event + * @param id + */ +const fetchEvent = async (id: string): Promise => { + const stringifiedQuery = stringify( + { + select: { + group: true, + }, + }, + { addQueryPrefix: true }, + ) + const res = await fetch(`http://localhost:3000/api/event/${id}${stringifiedQuery}`); + if (!res.ok) return undefined + return res.json() +} \ No newline at end of file diff --git a/src/collections/Highlight.ts b/src/collections/Highlight.ts index 27f5533..dc42983 100644 --- a/src/collections/Highlight.ts +++ b/src/collections/Highlight.ts @@ -1,5 +1,5 @@ import { CollectionConfig } from 'payload' -import { isAdminOrEmployee } from '@/collections/access/admin' +import { hide, isAdminOrEmployee } from '@/collections/access/admin' export const Highlight: CollectionConfig = { slug: 'highlight', @@ -62,7 +62,8 @@ export const Highlight: CollectionConfig = { } ], admin: { - useAsTitle: 'text' + useAsTitle: 'text', + hidden: hide }, access: { read: () => true, diff --git a/src/collections/LiturgicalCalendar.ts b/src/collections/LiturgicalCalendar.ts index 33b3c63..ec0aac4 100644 --- a/src/collections/LiturgicalCalendar.ts +++ b/src/collections/LiturgicalCalendar.ts @@ -1,5 +1,5 @@ import { CollectionConfig } from 'payload' -import { isAdminOrEmployee } from '@/collections/access/admin' +import { hide, isAdminOrEmployee } from '@/collections/access/admin' import { nextSunday } from '@/utils/sunday' export const LiturgicalCalendar: CollectionConfig = { @@ -46,6 +46,9 @@ export const LiturgicalCalendar: CollectionConfig = { required: true } ], + admin: { + hidden: hide + }, access: { read: () => true, create: isAdminOrEmployee(), diff --git a/src/collections/Media.ts b/src/collections/Media.ts index f245fd1..90ccaaf 100644 --- a/src/collections/Media.ts +++ b/src/collections/Media.ts @@ -1,10 +1,12 @@ import type { CollectionConfig } from 'payload' -import { Media as MediaType } from '../payload-types' +import { isAdminOrEmployee } from '@/collections/access/admin' export const Media: CollectionConfig = { slug: 'media', access: { read: () => true, + update: isAdminOrEmployee(), + delete: isAdminOrEmployee() }, admin: { listSearchableFields: ['search'] diff --git a/src/collections/Parish.ts b/src/collections/Parish.ts index 9581deb..a067a5c 100644 --- a/src/collections/Parish.ts +++ b/src/collections/Parish.ts @@ -1,5 +1,5 @@ import { CollectionConfig } from 'payload' -import { isAdmin, isAdminOrEmployee } from '@/collections/access/admin' +import { hide, isAdmin, isAdminOrEmployee } from '@/collections/access/admin' export const Parish: CollectionConfig = { slug: 'parish', @@ -131,6 +131,7 @@ export const Parish: CollectionConfig = { ], admin: { useAsTitle: 'name', + hidden: hide }, access: { read: () => true, diff --git a/src/collections/PopesPrayerIntentions.ts b/src/collections/PopesPrayerIntentions.ts index 8c07827..007814b 100644 --- a/src/collections/PopesPrayerIntentions.ts +++ b/src/collections/PopesPrayerIntentions.ts @@ -1,5 +1,5 @@ import { CollectionConfig } from 'payload' -import { isAdminOrEmployee } from '@/collections/access/admin' +import { hide, isAdminOrEmployee } from '@/collections/access/admin' export const PopesPrayerIntentions: CollectionConfig = { slug: 'popePrayerIntentions', @@ -100,6 +100,7 @@ export const PopesPrayerIntentions: CollectionConfig = { ], admin: { useAsTitle: 'title', + hidden: hide }, access: { read: () => true, diff --git a/src/collections/Users.ts b/src/collections/Users.ts index 81b5175..b20231b 100644 --- a/src/collections/Users.ts +++ b/src/collections/Users.ts @@ -1,5 +1,5 @@ import type { CollectionConfig } from 'payload' -import { isAdminOrEmployee } from '@/collections/access/admin' +import { isAdmin, isAdminOrEmployee } from '@/collections/access/admin' export const Users: CollectionConfig = { slug: 'users', @@ -69,8 +69,9 @@ export const Users: CollectionConfig = { }, ], access: { + read: isAdminOrEmployee(), create: isAdminOrEmployee(), update: isAdminOrEmployee(), - delete: isAdminOrEmployee(), + delete: isAdmin(), }, } diff --git a/src/collections/Worship.ts b/src/collections/Worship.ts index c897b82..cf5f316 100644 --- a/src/collections/Worship.ts +++ b/src/collections/Worship.ts @@ -1,5 +1,5 @@ import { CollectionConfig } from 'payload' -import { isAdminOrEmployee } from '@/collections/access/admin' +import { hide, isAdminOrEmployee } from '@/collections/access/admin' export const Worship: CollectionConfig = { slug: 'worship', @@ -101,7 +101,8 @@ export const Worship: CollectionConfig = { ], admin: { defaultColumns: ["date", 'location', 'type', 'celebrant'], - listSearchableFields: ['date', 'location'] + listSearchableFields: ['date', 'location'], + hidden: hide }, access: { read: () => true, diff --git a/src/collections/access/admin.ts b/src/collections/access/admin.ts index 847fe88..a36af14 100644 --- a/src/collections/access/admin.ts +++ b/src/collections/access/admin.ts @@ -1,4 +1,4 @@ -import type { Access } from 'payload' +import type { Access, ClientUser } from 'payload' export const isAdmin = (): Access => @@ -15,3 +15,5 @@ export const isAdminOrEmployee = return user.roles === 'admin' || user.roles === 'employee' } + +export const hide = (args: { user: ClientUser }) => args.user && args.user.roles === "user" \ No newline at end of file diff --git a/src/payload-types.ts b/src/payload-types.ts index 8e1020c..46c3ec7 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -6,65 +6,10 @@ * and re-run `payload generate:types` to regenerate this file. */ -/** - * Supported timezones in IANA format. - * - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "supportedTimezones". - */ -export type SupportedTimezones = - | 'Pacific/Midway' - | 'Pacific/Niue' - | 'Pacific/Honolulu' - | 'Pacific/Rarotonga' - | 'America/Anchorage' - | 'Pacific/Gambier' - | 'America/Los_Angeles' - | 'America/Tijuana' - | 'America/Denver' - | 'America/Phoenix' - | 'America/Chicago' - | 'America/Guatemala' - | 'America/New_York' - | 'America/Bogota' - | 'America/Caracas' - | 'America/Santiago' - | 'America/Buenos_Aires' - | 'America/Sao_Paulo' - | 'Atlantic/South_Georgia' - | 'Atlantic/Azores' - | 'Atlantic/Cape_Verde' - | 'Europe/London' - | 'Europe/Berlin' - | 'Africa/Lagos' - | 'Europe/Athens' - | 'Africa/Cairo' - | 'Europe/Moscow' - | 'Asia/Riyadh' - | 'Asia/Dubai' - | 'Asia/Baku' - | 'Asia/Karachi' - | 'Asia/Tashkent' - | 'Asia/Calcutta' - | 'Asia/Dhaka' - | 'Asia/Almaty' - | 'Asia/Jakarta' - | 'Asia/Bangkok' - | 'Asia/Shanghai' - | 'Asia/Singapore' - | 'Asia/Tokyo' - | 'Asia/Seoul' - | 'Australia/Sydney' - | 'Pacific/Guam' - | 'Pacific/Noumea' - | 'Pacific/Auckland' - | 'Pacific/Fiji'; - export interface Config { auth: { users: UserAuthOperations; }; - blocks: {}; collections: { parish: Parish; church: Church; @@ -399,7 +344,6 @@ export interface Highlight { */ export interface Event { id: string; - photo?: (string | null) | Media; title: string; date: string; location: string | Location; @@ -409,6 +353,7 @@ export interface Event { shortDescription: string; description: string; rsvpLink?: string | null; + photo?: (string | null) | Media; flyer?: (string | null) | Document; cancelled: boolean; isRecurring: boolean; @@ -814,7 +759,6 @@ export interface HighlightSelect { * via the `definition` "event_select". */ export interface EventSelect { - photo?: T; title?: T; date?: T; location?: T; @@ -824,6 +768,7 @@ export interface EventSelect { shortDescription?: T; description?: T; rsvpLink?: T; + photo?: T; flyer?: T; cancelled?: T; isRecurring?: T;