diff --git a/backend/src/constants/groups.js b/backend/src/constants/groups.js index b4a6063f1..64ffeaa59 100644 --- a/backend/src/constants/groups.js +++ b/backend/src/constants/groups.js @@ -1,2 +1,3 @@ // this file is duplicated in `backend/src/constants/group.js` and `webapp/constants/group.js` export const DESCRIPTION_WITHOUT_HTML_LENGTH_MIN = 100 // with removed HTML tags +export const DESCRIPTION_EXCERPT_HTML_LENGTH = 120 // with removed HTML tags diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js index 2a611f324..4d6bad5fd 100644 --- a/backend/src/db/graphql/groups.js +++ b/backend/src/db/graphql/groups.js @@ -39,6 +39,44 @@ export const createGroupMutation = gql` } ` +export const updateGroupMutation = gql` + mutation ( + $id: ID! + $name: String + $slug: String + $avatar: ImageInput + $about: String + $description: String + $actionRadius: GroupActionRadius + $categoryIds: [ID] + ) { + UpdateGroup( + id: $id + name: $name + slug: $slug + avatar: $avatar + about: $about + description: $description + actionRadius: $actionRadius + categoryIds: $categoryIds + ) { + id + name + slug + avatar + createdAt + updatedAt + disabled + deleted + about + description + groupType + actionRadius + myRole + } + } +` + // ------ queries export const groupQuery = gql` diff --git a/backend/src/middleware/excerptMiddleware.js b/backend/src/middleware/excerptMiddleware.js index ca061609a..68eea9a74 100644 --- a/backend/src/middleware/excerptMiddleware.js +++ b/backend/src/middleware/excerptMiddleware.js @@ -1,9 +1,14 @@ import trunc from 'trunc-html' +import { DESCRIPTION_EXCERPT_HTML_LENGTH } from '../constants/groups' export default { Mutation: { CreateGroup: async (resolve, root, args, context, info) => { - args.descriptionExcerpt = trunc(args.description, 120).html + args.descriptionExcerpt = trunc(args.description, DESCRIPTION_EXCERPT_HTML_LENGTH).html + return resolve(root, args, context, info) + }, + UpdateGroup: async (resolve, root, args, context, info) => { + args.descriptionExcerpt = trunc(args.description, DESCRIPTION_EXCERPT_HTML_LENGTH).html return resolve(root, args, context, info) }, CreatePost: async (resolve, root, args, context, info) => { diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index c81b069d2..9909f542d 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -52,6 +52,36 @@ const isMySocialMedia = rule({ return socialMedia.ownedBy.node.id === user.id }) +const isAllowedToChangeGroupSettings = rule({ + cache: 'no_cache', +})(async (_parent, args, { user, driver }) => { + if (!(user && user.id)) return false + const ownerId = user.id + const { id: groupId } = args + const session = driver.session() + const readTxPromise = session.readTransaction(async (transaction) => { + const transactionResponse = await transaction.run( + ` + MATCH (owner:User {id: $ownerId})-[adminMembership:MEMBER_OF]->(group:Group {id: $groupId}) + RETURN group {.*}, owner {.*, myRoleInGroup: adminMembership.role} + `, + { groupId, ownerId }, + ) + return { + owner: transactionResponse.records.map((record) => record.get('owner'))[0], + group: transactionResponse.records.map((record) => record.get('group'))[0], + } + }) + try { + const { owner, group } = await readTxPromise + return !!group && !!owner && ['owner'].includes(owner.myRoleInGroup) + } catch (error) { + throw new Error(error) + } finally { + session.close() + } +}) + const isAuthor = rule({ cache: 'no_cache', })(async (_parent, args, { user, driver }) => { @@ -142,6 +172,7 @@ export default shield( SignupVerification: allow, UpdateUser: onlyYourself, CreateGroup: isAuthenticated, + UpdateGroup: isAllowedToChangeGroupSettings, CreatePost: isAuthenticated, UpdatePost: isAuthor, DeletePost: isAuthor, diff --git a/backend/src/middleware/sluggifyMiddleware.js b/backend/src/middleware/sluggifyMiddleware.js index 2a965c87f..8fd200e8f 100644 --- a/backend/src/middleware/sluggifyMiddleware.js +++ b/backend/src/middleware/sluggifyMiddleware.js @@ -30,6 +30,10 @@ export default { args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'Group'))) return resolve(root, args, context, info) }, + UpdateGroup: async (resolve, root, args, context, info) => { + args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'Group'))) + return resolve(root, args, context, info) + }, CreatePost: async (resolve, root, args, context, info) => { args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post'))) return resolve(root, args, context, info)