From 0bffede0214b9cdbfad843f6221631dbed34dd6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adem=C3=ADlson=20F=2E=20Tonato?= Date: Sat, 24 Oct 2020 11:29:02 +0100 Subject: [PATCH 001/105] feat: add mutation for new functionality (mark-all-as-read) - Add mutation for markAllAsRead (notifications) - Add method to the schema that returns a list of all notifications - Add permissions rules to invoke this method (isAuthenticated) See #2660 --- .../src/middleware/permissionsMiddleware.js | 1 + backend/src/schema/resolvers/notifications.js | 29 +++++++++++++++ backend/src/schema/types/type/NOTIFIED.gql | 1 + webapp/graphql/User.js | 36 +++++++++++++++++++ 4 files changed, 67 insertions(+) diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index 2c8d7ff63..d3aa7ce9a 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -143,6 +143,7 @@ export default shield( blockUser: isAuthenticated, unblockUser: isAuthenticated, markAsRead: isAuthenticated, + markAllAsRead: isAuthenticated, AddEmailAddress: isAuthenticated, VerifyEmailAddress: isAuthenticated, pinPost: isAdmin, diff --git a/backend/src/schema/resolvers/notifications.js b/backend/src/schema/resolvers/notifications.js index 3c01ddb97..a96181a50 100644 --- a/backend/src/schema/resolvers/notifications.js +++ b/backend/src/schema/resolvers/notifications.js @@ -99,6 +99,35 @@ export default { session.close() } }, + markAllAsRead: async (parent, args, context, resolveInfo) => { + const { user: currentUser } = context + const session = context.driver.session() + const writeTxResultPromise = session.writeTransaction(async (transaction) => { + const markAllNotificationAsReadTransactionResponse = await transaction.run( + ` + MATCH (resource {deleted: false, disabled: false})-[notification:NOTIFIED {read: FALSE}]->(user:User {id:$id}) + SET notification.read = TRUE + WITH user, notification, resource, + [(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors, + [(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts + WITH resource, user, notification, authors, posts, + resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0]} AS finalResource + RETURN notification {.*, from: finalResource, to: properties(user)} + `, + { id: currentUser.id }, + ) + log(markAllNotificationAsReadTransactionResponse) + return markAllNotificationAsReadTransactionResponse.records.map((record) => + record.get('notification'), + ) + }) + try { + const notifications = await writeTxResultPromise + return notifications + } finally { + session.close() + } + }, }, NOTIFIED: { id: async (parent) => { diff --git a/backend/src/schema/types/type/NOTIFIED.gql b/backend/src/schema/types/type/NOTIFIED.gql index 88ecd3882..864cdea4d 100644 --- a/backend/src/schema/types/type/NOTIFIED.gql +++ b/backend/src/schema/types/type/NOTIFIED.gql @@ -29,6 +29,7 @@ type Query { type Mutation { markAsRead(id: ID!): NOTIFIED + markAllAsRead: [NOTIFIED] } type Subscription { diff --git a/webapp/graphql/User.js b/webapp/graphql/User.js index 3b015dacc..8a0494402 100644 --- a/webapp/graphql/User.js +++ b/webapp/graphql/User.js @@ -136,6 +136,42 @@ export const markAsReadMutation = (i18n) => { ` } +export const markAllAsReadMutation = (i18n) => { + return gql` + ${userFragment} + ${commentFragment} + ${postFragment} + + mutation { + markAllAsRead { + id + read + reason + createdAt + updatedAt + from { + __typename + ... on Post { + ...post + author { + ...user + } + } + ... on Comment { + ...comment + post { + ...post + author { + ...user + } + } + } + } + } + } + ` +} + export const notificationAdded = () => { return gql` ${userFragment} From e37fb535dce43cd5629ee0a900568be5409608be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adem=C3=ADlson=20F=2E=20Tonato?= Date: Sat, 24 Oct 2020 11:54:52 +0100 Subject: [PATCH 002/105] feat: add translations for notifications label (mark all as read) --- webapp/locales/de.json | 1 + webapp/locales/en.json | 1 + webapp/locales/es.json | 1 + webapp/locales/fr.json | 1 + webapp/locales/it.json | 1 + webapp/locales/nl.json | 20 ++++++++++++++++++++ webapp/locales/pt.json | 1 + webapp/locales/ru.json | 1 + 8 files changed, 27 insertions(+) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index adee8921c..8de89bd5c 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -456,6 +456,7 @@ "unread": "Ungelesen" }, "pageLink": "Alle Benachrichtigungen", + "markAllAsRead": "Markiere alle als gelesen", "post": "Beitrag", "reason": { "commented_on_post": "Hat Deinen Beitrag kommentiert …", diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 8959e3830..f9e7a43e6 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -456,6 +456,7 @@ "unread": "Unread" }, "pageLink": "All notifications", + "markAllAsRead": "Mark all as read", "post": "Post", "reason": { "commented_on_post": "Commented on your post …", diff --git a/webapp/locales/es.json b/webapp/locales/es.json index 0925687fc..8dc91ac52 100644 --- a/webapp/locales/es.json +++ b/webapp/locales/es.json @@ -449,6 +449,7 @@ "unread": "No leído" }, "pageLink": "Todas las notificaciones", + "markAllAsRead": "Marcar todas como leido", "post": "Contribución", "reason": { "commented_on_post": "Comentó su contribución ...", diff --git a/webapp/locales/fr.json b/webapp/locales/fr.json index e0bcf14e1..0bab5e73d 100644 --- a/webapp/locales/fr.json +++ b/webapp/locales/fr.json @@ -438,6 +438,7 @@ "unread": "Non lu" }, "pageLink": "Toutes les notifications", + "markAllAsRead": "Tout marquer comme lu", "post": "Post", "reason": { "commented_on_post": "Commenté sur votre post…", diff --git a/webapp/locales/it.json b/webapp/locales/it.json index b53b863cc..898a80d63 100644 --- a/webapp/locales/it.json +++ b/webapp/locales/it.json @@ -388,6 +388,7 @@ "unread": "" }, "pageLink": "", + "markAllAsRead": "Segna tutti come letti", "post": "", "reason": { "commented_on_post": "", diff --git a/webapp/locales/nl.json b/webapp/locales/nl.json index 64643133c..35e31591f 100644 --- a/webapp/locales/nl.json +++ b/webapp/locales/nl.json @@ -172,5 +172,25 @@ "taxident": "Identificatienummer voor de belasting over de toegevoegde waarde overeenkomstig § 27 a Wet op de belasting over de toegevoegde waarde (Duitsland).", "termsAc": "Gebruiksvoorwaarden", "tribunal": "registerrechtbank" + }, + "notifications": { + "comment": "", + "content": "", + "empty": "", + "filterLabel": { + "all": "", + "read": "", + "unread": "" + }, + "pageLink": "", + "markAllAsRead": "Markeer alles als gelezen", + "post": "", + "reason": { + "commented_on_post": "", + "mentioned_in_comment": "", + "mentioned_in_post": "" + }, + "title": "", + "user": "" } } diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index 1a3efeb44..96be89f8e 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -377,6 +377,7 @@ "unread": "Não lido" }, "pageLink": "Todas as notificações", + "markAllAsRead": "Marcar todas como lidas", "post": "Post", "reason": { "commented_on_post": "Comentou no seu post …", diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json index 59928a2c5..bb66aa862 100644 --- a/webapp/locales/ru.json +++ b/webapp/locales/ru.json @@ -449,6 +449,7 @@ "unread": "Непрочитанные" }, "pageLink": "Все уведомления", + "markAllAsRead": "Отметить все как прочитанное", "post": "Пост", "reason": { "commented_on_post": "Комментарий к посту...", From 1df69c90fdd5233d4ab1059a3afb0b95f85db134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adem=C3=ADlson=20F=2E=20Tonato?= Date: Sat, 24 Oct 2020 19:38:45 +0100 Subject: [PATCH 003/105] feat: add tests for mark all as read (backend) --- .../schema/resolvers/notifications.spec.js | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/backend/src/schema/resolvers/notifications.spec.js b/backend/src/schema/resolvers/notifications.spec.js index 9d7795dd4..d4805bfc9 100644 --- a/backend/src/schema/resolvers/notifications.spec.js +++ b/backend/src/schema/resolvers/notifications.spec.js @@ -393,4 +393,51 @@ describe('given some notifications', () => { }) }) }) + describe('markAllAsRead', () => { + const markAllAsReadMutation = gql` + mutation { + markAllAsRead { + from { + __typename + ... on Post { + content + } + ... on Comment { + content + } + } + read + createdAt + } + } + ` + describe('unauthenticated', () => { + it('throws authorization error', async () => { + const result = await mutate({ + mutation: markAllAsReadMutation, + }) + expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') + }) + }) + + describe('authenticated', () => { + beforeEach(async () => { + authenticatedUser = await user.toJson() + }) + + describe('not being notified at all', () => { + beforeEach(async () => { + variables = { + ...variables, + } + }) + + it('returns undefined', async () => { + const response = await mutate({ mutation: markAllAsReadMutation, variables }) + expect(response.data.markAsRead).toEqual(undefined) + expect(response.errors).toBeUndefined() + }) + }) + }) + }) }) From bfe66adaacc9a6bc19976ea7e616f62ac061bb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adem=C3=ADlson=20F=2E=20Tonato?= Date: Sat, 24 Oct 2020 19:40:05 +0100 Subject: [PATCH 004/105] feat: add button to mark all notifications as read on notification menu --- .../NotificationMenu/NotificationMenu.vue | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/webapp/components/NotificationMenu/NotificationMenu.vue b/webapp/components/NotificationMenu/NotificationMenu.vue index ec991f0ef..c53545b91 100644 --- a/webapp/components/NotificationMenu/NotificationMenu.vue +++ b/webapp/components/NotificationMenu/NotificationMenu.vue @@ -16,11 +16,24 @@
- + + + + {{ $t('notifications.pageLink') }} + + + + + {{ $t('notifications.markAllAsRead') }} + + + @@ -28,7 +41,12 @@ From e298a29d8b7b141a249acb729e426ea253d3ee9b Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Fri, 3 Mar 2023 18:38:41 +0100 Subject: [PATCH 035/105] test remove user from group --- webapp/components/Group/GroupMember.spec.js | 162 +++++++++++++++++++- 1 file changed, 158 insertions(+), 4 deletions(-) diff --git a/webapp/components/Group/GroupMember.spec.js b/webapp/components/Group/GroupMember.spec.js index 0c0b46e43..ef8b96568 100644 --- a/webapp/components/Group/GroupMember.spec.js +++ b/webapp/components/Group/GroupMember.spec.js @@ -1,26 +1,65 @@ import { mount } from '@vue/test-utils' import GroupMember from './GroupMember.vue' +import { changeGroupMemberRoleMutation, removeUserFromGroupMutation } from '~/graphql/groups.js' const localVue = global.localVue const propsData = { - groupId: '', - groupMembers: [], + groupId: 'group-id', + groupMembers: [ + { + slug: 'owner', + id: 'owner', + myRoleInGroup: 'owner', + }, + { + slug: 'user', + id: 'user', + myRoleInGroup: 'usual', + }, + ], } +const stubs = { + 'nuxt-link': true, +} + +const apolloMock = jest + .fn() + .mockRejectedValueOnce({ message: 'Oh no!' }) + .mockResolvedValue({ + data: { + ChangeGroupMemberRole: { + slug: 'user', + id: 'user', + myRoleInGroup: 'admin', + }, + }, + }) + +const toastErrorMock = jest.fn() +const toastSuccessMock = jest.fn() + describe('GroupMember', () => { let wrapper let mocks beforeEach(() => { mocks = { - $t: jest.fn(), + $t: jest.fn((t) => t), + $apollo: { + mutate: apolloMock, + }, + $toast: { + error: toastErrorMock, + success: toastSuccessMock, + }, } }) describe('mount', () => { const Wrapper = () => { - return mount(GroupMember, { propsData, mocks, localVue }) + return mount(GroupMember, { propsData, mocks, localVue, stubs }) } beforeEach(() => { @@ -30,5 +69,120 @@ describe('GroupMember', () => { it('renders', () => { expect(wrapper.findAll('.group-member')).toHaveLength(1) }) + + it('has two users in table', () => { + expect(wrapper.find('tbody').findAll('tr')).toHaveLength(2) + }) + + it('has no modal', () => { + expect(wrapper.find('div.ds-modal-wrapper').exists()).toBe(false) + }) + + describe('change user role', () => { + beforeEach(() => { + jest.clearAllMocks() + wrapper + .find('tbody') + .findAll('tr') + .at(1) + .find('select') + .findAll('option') + .at(2) + .setSelected() + wrapper.find('tbody').findAll('tr').at(1).find('select').trigger('change') + }) + + describe('with server error', () => { + it('toasts an error message', () => { + expect(toastErrorMock).toBeCalledWith('Oh no!') + }) + }) + + describe('with server success', () => { + it('calls the API', () => { + expect(apolloMock).toBeCalledWith({ + mutation: changeGroupMemberRoleMutation(), + variables: { groupId: 'group-id', userId: 'user', roleInGroup: 'admin' }, + }) + }) + + it('toasts a success message', () => { + expect(toastSuccessMock).toBeCalledWith('group.changeMemberRole') + }) + }) + }) + + describe('click remove user', () => { + beforeAll(() => { + apolloMock.mockRejectedValueOnce({ message: 'Oh no!!' }).mockResolvedValue({ + data: { + RemoveUserFromGroup: { + slug: 'user', + id: 'user', + myRoleInGroup: null, + }, + }, + }) + }) + + beforeEach(() => { + wrapper = Wrapper() + wrapper.find('tbody').findAll('tr').at(1).find('button').trigger('click') + }) + + it('opens the modal', () => { + expect(wrapper.find('div.ds-modal-wrapper').isVisible()).toBe(true) + }) + + describe('click on cancel', () => { + beforeEach(() => { + wrapper.find('div.ds-modal-wrapper').find('button.ds-button-ghost').trigger('click') + }) + + it('closes the modal', () => { + expect(wrapper.find('div.ds-modal-wrapper').exists()).toBe(false) + }) + }) + + describe('click on confirm with server error', () => { + beforeEach(() => { + wrapper.find('div.ds-modal-wrapper').find('button.ds-button-primary').trigger('click') + }) + + it('toasts an error message', () => { + expect(toastErrorMock).toBeCalledWith('Oh no!!') + }) + + it('closes the modal', () => { + expect(wrapper.find('div.ds-modal-wrapper').exists()).toBe(false) + }) + }) + + describe('click on confirm with success', () => { + beforeEach(() => { + jest.clearAllMocks() + wrapper.find('div.ds-modal-wrapper').find('button.ds-button-primary').trigger('click') + }) + + it('calls the API', () => { + expect(apolloMock).toBeCalledWith({ + mutation: removeUserFromGroupMutation(), + variables: { groupId: 'group-id', userId: 'user' }, + }) + }) + + it('emits load group members', () => { + expect(wrapper.emitted('loadGroupMembers')).toBeTruthy() + }) + + it('toasts a success message', () => { + expect(toastSuccessMock).toBeCalledWith('group.memberRemoved') + }) + + it('closes the modal', () => { + expect(wrapper.find('div.ds-modal-wrapper').exists()).toBe(false) + }) + }) + }) }) }) From 6d09dbcf08c4d6be28bb7eae443eb64389dfbe4d Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 6 Mar 2023 13:47:14 +0100 Subject: [PATCH 036/105] Update webapp/locales/de.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- webapp/locales/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index aebf8d5cd..67ba5aaa4 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -452,7 +452,7 @@ "message": "Eine Gruppe zu verlassen ist möglicherweise nicht rückgängig zu machen!
Gruppe „{name}“ verlassen!", "title": "Möchtest du wirklich die Gruppe verlassen?" }, - "memberRemoved": "Nutzer „{name}“ wurde aus der Gruppe entfernt", + "memberRemoved": "Nutzer „{name}“ wurde aus der Gruppe entfernt!", "members": "Mitglieder", "membersAdministrationList": { "avatar": "Avatar", From eb0bc971ecbe6129dc945ae7d6ce99073b77e521 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 6 Mar 2023 13:47:28 +0100 Subject: [PATCH 037/105] Update webapp/locales/en.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- webapp/locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 8f82b4578..31784ac2e 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -452,7 +452,7 @@ "message": "Leaving a group may be irreversible!
Leave group “{name}”!", "title": "Do you really want to leave the group?" }, - "memberRemoved": "User “{name}” was removed from group", + "memberRemoved": "User “{name}” was removed from group!", "members": "Members", "membersAdministrationList": { "avatar": "Avatar", From db8ad8897e542aad48a461fc26baa5645f6495e4 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 6 Mar 2023 14:11:55 +0100 Subject: [PATCH 038/105] same cypher for removeUserFromGroup and leaveGroup, adjust tests, admin cannot remove users fro group --- .../src/middleware/permissionsMiddleware.js | 5 +- backend/src/schema/resolvers/groups.js | 65 ++++++++----------- backend/src/schema/resolvers/groups.spec.js | 21 ++++++ .../schema/resolvers/postsInGroups.spec.js | 32 +++++++-- 4 files changed, 74 insertions(+), 49 deletions(-) diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index 2e04dd4a0..00a34f9ab 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -280,10 +280,7 @@ const canRemoveUserFromGroup = rule({ try { const { currentUserRole, userRole } = await readTxPromise return ( - currentUserRole && - ['admin', 'owner'].includes(currentUserRole) && - userRole && - userRole !== 'owner' + currentUserRole && ['owner'].includes(currentUserRole) && userRole && userRole !== 'owner' ) } catch (error) { throw new Error(error) diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js index 8ec0e9a4b..4a13dcc88 100644 --- a/backend/src/schema/resolvers/groups.js +++ b/backend/src/schema/resolvers/groups.js @@ -295,25 +295,8 @@ export default { LeaveGroup: async (_parent, params, context, _resolveInfo) => { const { groupId, userId } = params const session = context.driver.session() - const writeTxResultPromise = session.writeTransaction(async (transaction) => { - const leaveGroupCypher = ` - MATCH (member:User {id: $userId})-[membership:MEMBER_OF]->(group:Group {id: $groupId}) - DELETE membership - WITH member, group - OPTIONAL MATCH (p:Post)-[:IN]->(group) - WHERE NOT group.groupType = 'public' - WITH member, group, collect(p) AS posts - FOREACH (post IN posts | - MERGE (member)-[:CANNOT_SEE]->(post)) - RETURN member {.*, myRoleInGroup: NULL} - ` - - const transactionResponse = await transaction.run(leaveGroupCypher, { groupId, userId }) - const [member] = await transactionResponse.records.map((record) => record.get('member')) - return member - }) try { - return await writeTxResultPromise + return await removeUserFromGroupWriteTxResultPromise(session, groupId, userId) } catch (error) { throw new Error(error) } finally { @@ -371,28 +354,8 @@ export default { RemoveUserFromGroup: async (_parent, params, context, _resolveInfo) => { const { groupId, userId } = params const session = context.driver.session() - const writeTxResultPromise = session.writeTransaction(async (transaction) => { - const removeUserFromGroupCypher = ` - MATCH (member:User {id: $userId})-[membership:MEMBER_OF]->(group:Group {id: $groupId}) - DELETE membership - WITH member AS user, group - OPTIONAL MATCH (u:User)-[:WROTE]->(p:Post)-[:IN]->(group) - WHERE NOT u.id = $userId - WITH user, collect(p) AS posts - FOREACH (post IN posts | - MERGE (user)-[:CANNOT_SEE]->(post)) - RETURN user {.*, myRoleInGroup: null}` - - const transactionResponse = await transaction.run(removeUserFromGroupCypher, { - groupId, - userId, - }) - - const [user] = await transactionResponse.records.map((record) => record.get('user')) - return user - }) try { - return await writeTxResultPromise + return await removeUserFromGroupWriteTxResultPromise(session, groupId, userId) } catch (error) { throw new Error(error) } finally { @@ -414,3 +377,27 @@ export default { }), }, } + +const removeUserFromGroupWriteTxResultPromise = async (session, groupId, userId) => { + return session.writeTransaction(async (transaction) => { + const removeUserFromGroupCypher = ` + MATCH (user:User {id: $userId})-[membership:MEMBER_OF]->(group:Group {id: $groupId}) + DELETE membership + WITH user, group + OPTIONAL MATCH (author:User)-[:WROTE]->(p:Post)-[:IN]->(group) + WHERE NOT group.groupType = 'public' + AND NOT author.id = $userId + WITH user, collect(p) AS posts + FOREACH (post IN posts | + MERGE (user)-[:CANNOT_SEE]->(post)) + RETURN user {.*, myRoleInGroup: NULL} + ` + + const transactionResponse = await transaction.run(removeUserFromGroupCypher, { + groupId, + userId, + }) + const [user] = await transactionResponse.records.map((record) => record.get('user')) + return user + }) +} diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js index 1142b0b32..13291383d 100644 --- a/backend/src/schema/resolvers/groups.spec.js +++ b/backend/src/schema/resolvers/groups.spec.js @@ -3076,6 +3076,26 @@ describe('in mode', () => { }) }) + it('throws an error', async () => { + authenticatedUser = await usualMemberUser.toJson() + await expect( + mutate({ + mutation: removeUserFromGroupMutation(), + variables: { + groupId: 'hidden-group', + userId: 'admin-member-user', + }, + }), + ).resolves.toMatchObject({ + errors: expect.arrayContaining([ + expect.objectContaining({ + message: 'Not Authorized!', + }), + ]), + }) + }) + + /* it('removes the user from the group', async () => { await expect( mutate({ @@ -3131,6 +3151,7 @@ describe('in mode', () => { ]), }) }) + */ }) }) }) diff --git a/backend/src/schema/resolvers/postsInGroups.spec.js b/backend/src/schema/resolvers/postsInGroups.spec.js index 5bf5820f0..404c3f25f 100644 --- a/backend/src/schema/resolvers/postsInGroups.spec.js +++ b/backend/src/schema/resolvers/postsInGroups.spec.js @@ -1524,9 +1524,9 @@ describe('Posts in Groups', () => { }) }) - it('does not show the posts of the closed group anymore', async () => { + it('stil shows the posts of the closed group', async () => { const result = await query({ query: filterPosts(), variables: {} }) - expect(result.data.Post).toHaveLength(3) + expect(result.data.Post).toHaveLength(4) expect(result).toMatchObject({ data: { Post: expect.arrayContaining([ @@ -1540,6 +1540,11 @@ describe('Posts in Groups', () => { title: 'A post without a group', content: 'I am a user who does not belong to a group yet.', }, + { + id: 'post-to-closed-group', + title: 'A post to a closed group', + content: 'I am posting into a closed group as a member of the group', + }, { id: 'post-to-hidden-group', title: 'A post to a hidden group', @@ -1564,9 +1569,9 @@ describe('Posts in Groups', () => { }) }) - it('does only show the public posts', async () => { + it('still shows the post of the hidden group', async () => { const result = await query({ query: filterPosts(), variables: {} }) - expect(result.data.Post).toHaveLength(2) + expect(result.data.Post).toHaveLength(4) expect(result).toMatchObject({ data: { Post: expect.arrayContaining([ @@ -1580,6 +1585,16 @@ describe('Posts in Groups', () => { title: 'A post without a group', content: 'I am a user who does not belong to a group yet.', }, + { + id: 'post-to-closed-group', + title: 'A post to a closed group', + content: 'I am posting into a closed group as a member of the group', + }, + { + id: 'post-to-hidden-group', + title: 'A post to a hidden group', + content: 'I am posting into a hidden group as a member of the group', + }, ]), }, errors: undefined, @@ -1603,9 +1618,9 @@ describe('Posts in Groups', () => { authenticatedUser = await allGroupsUser.toJson() }) - it('does not show the posts of the closed group', async () => { + it('shows the posts of the closed group', async () => { const result = await query({ query: filterPosts(), variables: {} }) - expect(result.data.Post).toHaveLength(3) + expect(result.data.Post).toHaveLength(4) expect(result).toMatchObject({ data: { Post: expect.arrayContaining([ @@ -1624,6 +1639,11 @@ describe('Posts in Groups', () => { title: 'A post to a closed group', content: 'I am posting into a closed group as a member of the group', }, + { + id: 'post-to-hidden-group', + title: 'A post to a hidden group', + content: 'I am posting into a hidden group as a member of the group', + }, ]), }, errors: undefined, From 0b7e0fbc4b540156f78c27c1bd123576754e98b2 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 7 Mar 2023 13:16:38 +0100 Subject: [PATCH 039/105] Change Infinity to exact count. --- webapp/components/features/ProfileList/FollowList.vue | 2 +- webapp/pages/profile/_id/_slug.vue | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/webapp/components/features/ProfileList/FollowList.vue b/webapp/components/features/ProfileList/FollowList.vue index 7e626308b..e04b4003e 100644 --- a/webapp/components/features/ProfileList/FollowList.vue +++ b/webapp/components/features/ProfileList/FollowList.vue @@ -6,7 +6,7 @@ :allProfilesCount="allConnectionsCount" :profiles="connections" :loading="loading" - @fetchAllProfiles="$emit('fetchAllConnections', type)" + @fetchAllProfiles="$emit('fetchAllConnections', type, allConnectionsCount)" /> diff --git a/webapp/pages/profile/_id/_slug.vue b/webapp/pages/profile/_id/_slug.vue index a2b959b6d..06e95abb4 100644 --- a/webapp/pages/profile/_id/_slug.vue +++ b/webapp/pages/profile/_id/_slug.vue @@ -384,9 +384,9 @@ export default { this.user.followedByCurrentUser = followedByCurrentUser this.user.followedBy = followedBy }, - fetchAllConnections(type) { - if (type === 'following') this.followingCount = Infinity - if (type === 'followedBy') this.followedByCount = Infinity + fetchAllConnections(type, count) { + if (type === 'following') this.followingCount = count + if (type === 'followedBy') this.followedByCount = count }, }, apollo: { From cec9276a0f550fa51ea7b7f82e81f4b0a8633fc1 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Tue, 7 Mar 2023 13:34:36 +0100 Subject: [PATCH 040/105] Update CONTRIBUTING.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 918eff871..785f51642 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -190,7 +190,7 @@ For Docker compose `up` or `build` commands, you can use our Apple M1 override f # for development $ docker compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.apple-m1.override.yml up -# only once: init admin user and create indices and constraints in Neo4j database +# only once: init admin user and create indexes and constraints in Neo4j database $ docker compose exec backend yarn prod:migrate init # clean db $ docker compose exec backend yarn db:reset From cde40b82748fe7f6cfc18bea6fe0494b32c97705 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Tue, 7 Mar 2023 13:34:47 +0100 Subject: [PATCH 041/105] Update CONTRIBUTING.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 785f51642..e5839a576 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -199,7 +199,7 @@ $ docker compose exec backend yarn db:seed # for production $ docker compose -f docker-compose.yml -f docker-compose.apple-m1.override.yml up -# only once: init admin user and create indices and constraints in Neo4j database +# only once: init admin user and create indexes and constraints in Neo4j database $ docker compose exec backend /bin/sh -c "yarn prod:migrate init" ``` From 7fcb9a0ff6c9aec01858d90420aeea48e7faed93 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 7 Mar 2023 22:33:14 +0100 Subject: [PATCH 042/105] use base button --- webapp/components/Group/GroupMember.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/components/Group/GroupMember.vue b/webapp/components/Group/GroupMember.vue index c0cdc67ea..d6c09746e 100644 --- a/webapp/components/Group/GroupMember.vue +++ b/webapp/components/Group/GroupMember.vue @@ -53,7 +53,7 @@ Date: Wed, 8 Mar 2023 09:21:44 +0100 Subject: [PATCH 043/105] fix deploy branded --- .github/workflows/publish-branded.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/publish-branded.yml b/.github/workflows/publish-branded.yml index a404453e6..869eb6302 100644 --- a/.github/workflows/publish-branded.yml +++ b/.github/workflows/publish-branded.yml @@ -46,6 +46,11 @@ jobs: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + ref: ${{ github.event.client_payload.ref }} + - name: Download Docker Image (Backend) uses: actions/download-artifact@v2 with: From 3b724b802267bbbd36a5df9021adba03faaace6e Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:32:03 +0100 Subject: [PATCH 044/105] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5b36dbc87..6712922ce 100644 --- a/README.md +++ b/README.md @@ -138,8 +138,8 @@ Prepare database once before you start by running the following command in a sec ```bash # in main folder while docker-compose is up -$ docker-compose exec backend yarn run db:migrate init -$ docker-compose exec backend yarn run db:migrate up +$ docker compose exec backend yarn run db:migrate init +$ docker compose exec backend yarn run db:migrate up ``` Then clear and seed database by running the following command as well in the second terminal: From 7d761363d5daeaf3cd6c37cb381fa531c7d2bf7b Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:32:10 +0100 Subject: [PATCH 045/105] Update backend/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- backend/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/README.md b/backend/README.md index 130b9b05b..5173286d1 100644 --- a/backend/README.md +++ b/backend/README.md @@ -135,7 +135,7 @@ To reset the database run: $ docker exec backend yarn run db:reset # you could also wipe out your neo4j database and delete all volumes with: $ docker-compose down -v -# if container is not running, run this command to set up your database indices and constraints +# if container is not running, run this command to set up your database indexes and constraints $ docker exec backend yarn run db:migrate init # And then upgrade the indices and const $ docker exec backend yarn run db:migrate up From 3c7d4df8ba2fe8a22ffedcd4348e4f5520bef9b3 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:32:20 +0100 Subject: [PATCH 046/105] Update neo4j/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- neo4j/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j/README.md b/neo4j/README.md index a9834ae20..788b4e87e 100644 --- a/neo4j/README.md +++ b/neo4j/README.md @@ -103,7 +103,7 @@ $ kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-backen ```bash # in browser command line or cypher shell -# show all indices and constraints +# show all indexes and constraints $ :schema # show all indices From 0c6fd23f8ad01d57d130fdb7aba148316d1bc959 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:32:31 +0100 Subject: [PATCH 047/105] Update neo4j/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- neo4j/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j/README.md b/neo4j/README.md index 788b4e87e..31f4d5739 100644 --- a/neo4j/README.md +++ b/neo4j/README.md @@ -113,7 +113,7 @@ $ CALL db.indexes(); $ CALL db.constraints(); ``` -***Cypher commands to create and drop indices and constraints*** +***Cypher commands to create and drop indexes and constraints*** ```bash # in browser command line or cypher shell From a24bca7d70a33214dfd0e25a1e1dab7515a0bbfd Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:32:39 +0100 Subject: [PATCH 048/105] Update neo4j/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- neo4j/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j/README.md b/neo4j/README.md index 31f4d5739..da6b8fad1 100644 --- a/neo4j/README.md +++ b/neo4j/README.md @@ -118,7 +118,7 @@ $ CALL db.constraints(); ```bash # in browser command line or cypher shell -# create indices +# create indexes $ CALL db.index.fulltext.createNodeIndex("post_fulltext_search",["Post"],["title", "content"]); $ CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["User"],["name", "slug"]); $ CALL db.index.fulltext.createNodeIndex("tag_fulltext_search",["Tag"],["id"]); From b9a5de277eaeaa48ce113359d940d42e02ecb394 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:32:48 +0100 Subject: [PATCH 049/105] Update neo4j/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- neo4j/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j/README.md b/neo4j/README.md index da6b8fad1..281c2904b 100644 --- a/neo4j/README.md +++ b/neo4j/README.md @@ -126,6 +126,6 @@ $ CALL db.index.fulltext.createNodeIndex("tag_fulltext_search",["Tag"],["id"]); # drop an index $ DROP CONSTRAINT ON ( image:Image ) ASSERT image.url IS UNIQUE -# drop all indices and constraints +# drop all indexes and constraints $ CALL apoc.schema.assert({},{},true) YIELD label, key RETURN * ; ``` From 0ae1e47076c725eddcdb0af8a17e31bdec4a1098 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:33:24 +0100 Subject: [PATCH 050/105] Update neo4j/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- neo4j/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j/README.md b/neo4j/README.md index 281c2904b..12ec90240 100644 --- a/neo4j/README.md +++ b/neo4j/README.md @@ -106,7 +106,7 @@ $ kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-backen # show all indexes and constraints $ :schema -# show all indices +# show all indexes $ CALL db.indexes(); # show all constraints From 6a54e8cb1bfceca463abdd2f60a8cce9d1d3a3bf Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:33:34 +0100 Subject: [PATCH 051/105] Update backend/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- backend/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/README.md b/backend/README.md index 5173286d1..3dd8ced83 100644 --- a/backend/README.md +++ b/backend/README.md @@ -137,7 +137,7 @@ $ docker exec backend yarn run db:reset $ docker-compose down -v # if container is not running, run this command to set up your database indexes and constraints $ docker exec backend yarn run db:migrate init -# And then upgrade the indices and const +# And then upgrade the indexes and const $ docker exec backend yarn run db:migrate up ``` From 92a74bd2a3bf2adb51b93f255ba00e106b3839ce Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:33:47 +0100 Subject: [PATCH 052/105] Update deployment/DOCKER_MORE_CLOSELY.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- deployment/DOCKER_MORE_CLOSELY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/DOCKER_MORE_CLOSELY.md b/deployment/DOCKER_MORE_CLOSELY.md index 1768d74fa..67488fe81 100644 --- a/deployment/DOCKER_MORE_CLOSELY.md +++ b/deployment/DOCKER_MORE_CLOSELY.md @@ -24,7 +24,7 @@ $ docker compose -f docker-compose.yml -f docker-compose.apple-m1.override.yml u # for production testing Docker images from DockerHub $ docker compose -f docker-compose.ocelotsocial-branded.yml -f docker-compose.apple-m1.override.yml up -# only once: init admin user and create indices and constraints in Neo4j database +# only once: init admin user and create indexes and constraints in Neo4j database $ docker compose exec backend /bin/sh -c "yarn prod:migrate init" ``` From 5460a8121707891fa396d2fa661a5b7df1ec213a Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:34:01 +0100 Subject: [PATCH 053/105] Update neo4j/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- neo4j/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j/README.md b/neo4j/README.md index 12ec90240..0f2af9365 100644 --- a/neo4j/README.md +++ b/neo4j/README.md @@ -57,7 +57,7 @@ Here we describe some rarely used Cypher commands for Neo4j that are needed from ### Index And Constraint Commands -If indices or constraints are missing or not set correctly, the browser search will not work or the database seed for development will not work. +If indexes or constraints are missing or not set correctly, the browser search will not work or the database seed for development will not work. The indices and constraints of our database are set in `backend/src/db/migrate/store.js`. This is where the magic happens. From 9162ae1192e44ea3d165506f57dd92b1108d5452 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:34:14 +0100 Subject: [PATCH 054/105] Update neo4j/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- neo4j/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j/README.md b/neo4j/README.md index 0f2af9365..a715a4630 100644 --- a/neo4j/README.md +++ b/neo4j/README.md @@ -98,7 +98,7 @@ On a server with Kubernetes cluster: $ kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-backend | awk '{ print $1 }') -- /bin/sh -c "yarn prod:migrate init" ``` -***Cypher commands to show indices and constraints*** +***Cypher commands to show indexes and constraints*** ```bash # in browser command line or cypher shell From 9f2211d7d1391f5af2d76fa082be4c0226511f15 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:34:25 +0100 Subject: [PATCH 055/105] Update neo4j/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- neo4j/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j/README.md b/neo4j/README.md index a715a4630..8b61cef53 100644 --- a/neo4j/README.md +++ b/neo4j/README.md @@ -59,7 +59,7 @@ Here we describe some rarely used Cypher commands for Neo4j that are needed from If indexes or constraints are missing or not set correctly, the browser search will not work or the database seed for development will not work. -The indices and constraints of our database are set in `backend/src/db/migrate/store.js`. +The indexes and constraints of our database are set in `backend/src/db/migrate/store.js`. This is where the magic happens. It's called by our `prod:migrate init` command. From 5e5c1fef04ac95d1674f9da770c74aacd341c271 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Wed, 8 Mar 2023 09:34:35 +0100 Subject: [PATCH 056/105] Update neo4j/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- neo4j/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo4j/README.md b/neo4j/README.md index 8b61cef53..1ea625d89 100644 --- a/neo4j/README.md +++ b/neo4j/README.md @@ -63,7 +63,7 @@ The indexes and constraints of our database are set in `backend/src/db/migrate/s This is where the magic happens. It's called by our `prod:migrate init` command. -This command initializes the Admin user and creates all necessary indices and constraints in the Neo4j database. +This command initializes the Admin user and creates all necessary indexes and constraints in the Neo4j database. ***Calls in development*** From b28a595ae409c99b181cd228523558a222b096d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Wed, 8 Mar 2023 16:22:53 +0100 Subject: [PATCH 057/105] Change group filter button text --- webapp/locales/de.json | 2 +- webapp/locales/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 1db5a3dcd..5b95cd632 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -270,7 +270,7 @@ "filterFollow": "Beiträge von Nutzern filtern, denen ich folge", "filterMasonryGrid": { "myFriends": "Nutzer denen ich folge", - "myGroups": "Meine Gruppen", + "myGroups": "Aus meinen Gruppen", "myTopics": "Meine Themen", "noFilter": "Beiträge filtern" }, diff --git a/webapp/locales/en.json b/webapp/locales/en.json index f7a7a54ad..b13757893 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -270,7 +270,7 @@ "filterFollow": "Filter contributions from users I follow", "filterMasonryGrid": { "myFriends": "Users I follow", - "myGroups": "My groups", + "myGroups": "By my groups", "myTopics": "My topics", "noFilter": "Filter posts" }, From b64e35c69bae7bc1ffa26705f3147911e6701e4d Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 8 Mar 2023 17:11:57 +0100 Subject: [PATCH 058/105] fix typo --- webapp/components/FilterMenu/FilterMenu.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/components/FilterMenu/FilterMenu.vue b/webapp/components/FilterMenu/FilterMenu.vue index a1aed3950..fcd386c38 100644 --- a/webapp/components/FilterMenu/FilterMenu.vue +++ b/webapp/components/FilterMenu/FilterMenu.vue @@ -10,7 +10,7 @@ > -