diff --git a/backend/src/graphql/resolvers/posts.spec.ts b/backend/src/graphql/resolvers/posts.spec.ts index fc3948d98..dead7a876 100644 --- a/backend/src/graphql/resolvers/posts.spec.ts +++ b/backend/src/graphql/resolvers/posts.spec.ts @@ -10,6 +10,7 @@ import CONFIG from '@config/index' import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' import Image from '@db/models/Image' +import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { createPostMutation } from '@graphql/queries/createPostMutation' import createServer, { getContext } from '@src/server' @@ -1305,6 +1306,130 @@ describe('pin posts', () => { }) }) + describe('post in public group', () => { + beforeEach(async () => { + await mutate({ + mutation: createGroupMutation(), + variables: { + name: 'Public Group', + id: 'public-group', + about: 'This is a public group', + groupType: 'public', + actionRadius: 'regional', + description: + 'This is a public group to test if the posts of this group can be pinned.', + categoryIds, + }, + }) + await mutate({ + mutation: createPostMutation(), + variables: { + id: 'public-group-post', + title: 'Public group post', + content: 'This is a post in a public group', + groupId: 'public-group', + categoryIds, + }, + }) + variables = { ...variables, id: 'public-group-post' } + }) + + it('can be pinned', async () => { + await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject({ + data: { + pinPost: { + id: 'public-group-post', + author: { + slug: 'testuser', + }, + pinnedBy: { + id: 'current-user', + name: 'Admin', + role: 'admin', + }, + }, + }, + errors: undefined, + }) + }) + }) + + describe('post in closed group', () => { + beforeEach(async () => { + await mutate({ + mutation: createGroupMutation(), + variables: { + name: 'Closed Group', + id: 'closed-group', + about: 'This is a closed group', + groupType: 'closed', + actionRadius: 'regional', + description: + 'This is a closed group to test if the posts of this group can be pinned.', + categoryIds, + }, + }) + await mutate({ + mutation: createPostMutation(), + variables: { + id: 'closed-group-post', + title: 'Closed group post', + content: 'This is a post in a closed group', + groupId: 'closed-group', + categoryIds, + }, + }) + variables = { ...variables, id: 'closed-group-post' } + }) + + it('can not be pinned', async () => { + await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject({ + data: { + pinPost: null, + }, + errors: undefined, + }) + }) + }) + + describe('post in hidden group', () => { + beforeEach(async () => { + await mutate({ + mutation: createGroupMutation(), + variables: { + name: 'Hidden Group', + id: 'hidden-group', + about: 'This is a hidden group', + groupType: 'hidden', + actionRadius: 'regional', + description: + 'This is a hidden group to test if the posts of this group can be pinned.', + categoryIds, + }, + }) + await mutate({ + mutation: createPostMutation(), + variables: { + id: 'hidden-group-post', + title: 'Hidden group post', + content: 'This is a post in a hidden group', + groupId: 'hidden-group', + categoryIds, + }, + }) + variables = { ...variables, id: 'hidden-group-post' } + }) + + it('can not be pinned', async () => { + await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject({ + data: { + pinPost: null, + }, + errors: undefined, + }) + }) + }) + describe('PostOrdering', () => { beforeEach(async () => { await Factory.build('post', { diff --git a/backend/src/graphql/resolvers/posts.ts b/backend/src/graphql/resolvers/posts.ts index 1d23770a3..5190cabf8 100644 --- a/backend/src/graphql/resolvers/posts.ts +++ b/backend/src/graphql/resolvers/posts.ts @@ -351,7 +351,8 @@ export default { const pinPostCypher = ` MATCH (user:User {id: $userId}) WHERE user.role = 'admin' MATCH (post:Post {id: $params.id}) - WHERE NOT((post)-[:IN]->(:Group)) + WHERE NOT EXISTS((post)-[:IN]->(:Group)) OR + (post)-[:IN]->(:Group { groupType: 'public'}) MERGE (user)-[pinned:PINNED {createdAt: toString(datetime())}]->(post) SET post.pinned = true RETURN post, pinned.createdAt as pinnedAt` diff --git a/webapp/components/ContentMenu/ContentMenu.spec.js b/webapp/components/ContentMenu/ContentMenu.spec.js index 2bce1496f..71e74ac74 100644 --- a/webapp/components/ContentMenu/ContentMenu.spec.js +++ b/webapp/components/ContentMenu/ContentMenu.spec.js @@ -197,6 +197,79 @@ describe('ContentMenu.vue', () => { ], ]) }) + + describe('post in public group', () => { + it('can pin unpinned post', async () => { + getters['auth/isAdmin'] = () => true + const wrapper = await openContentMenu({ + isOwner: false, + resourceType: 'contribution', + resource: { + id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8', + pinnedBy: null, + group: { + groupType: 'public', + }, + }, + }) + wrapper + .findAll('.ds-menu-item') + .filter((item) => item.text() === 'post.menu.pin') + .at(0) + .trigger('click') + expect(wrapper.emitted('pinPost')).toEqual([ + [ + { + id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8', + pinnedBy: null, + group: { + groupType: 'public', + }, + }, + ], + ]) + }) + }) + + describe('post in closed group', () => { + it('can not be pinned', async () => { + getters['auth/isAdmin'] = () => true + const wrapper = await openContentMenu({ + isOwner: false, + resourceType: 'contribution', + resource: { + id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8', + pinnedBy: null, + group: { + groupType: 'closed', + }, + }, + }) + expect( + wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.pin'), + ).toHaveLength(0) + }) + }) + + describe('post in hidden group', () => { + it('can not be pinned', async () => { + getters['auth/isAdmin'] = () => true + const wrapper = await openContentMenu({ + isOwner: false, + resourceType: 'contribution', + resource: { + id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8', + pinnedBy: null, + group: { + groupType: 'hidden', + }, + }, + }) + expect( + wrapper.findAll('.ds-menu-item').filter((item) => item.text() === 'post.menu.pin'), + ).toHaveLength(0) + }) + }) }) describe('when maxPinnedPosts = 3', () => { diff --git a/webapp/components/ContentMenu/ContentMenu.vue b/webapp/components/ContentMenu/ContentMenu.vue index 3f6f742a1..9c2bdc369 100644 --- a/webapp/components/ContentMenu/ContentMenu.vue +++ b/webapp/components/ContentMenu/ContentMenu.vue @@ -82,7 +82,7 @@ export default { }) } - if (this.isAdmin && !this.resource.group) { + if (this.isAdmin && (!this.resource.group || this.resource.group.groupType === 'public')) { if (!this.resource.pinnedBy && this.canBePinned) { routes.push({ label: this.$t(`post.menu.pin`), diff --git a/webapp/graphql/PostQuery.js b/webapp/graphql/PostQuery.js index d9d38eef4..a3dd67193 100644 --- a/webapp/graphql/PostQuery.js +++ b/webapp/graphql/PostQuery.js @@ -53,6 +53,7 @@ export default (i18n) => { id name slug + groupType } } } @@ -95,6 +96,7 @@ export const filterPosts = (i18n) => { id name slug + groupType } } } @@ -136,6 +138,7 @@ export const profilePagePosts = (i18n) => { id name slug + groupType } } }