From 594477f7bf825bb365f90c5e1627c6ed4fd47652 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 26 Oct 2022 17:42:57 +0200 Subject: [PATCH 01/12] fix: Update Group Avatar after Upload --- webapp/graphql/groups.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webapp/graphql/groups.js b/webapp/graphql/groups.js index 5ee5869ce..10e1e5c17 100644 --- a/webapp/graphql/groups.js +++ b/webapp/graphql/groups.js @@ -93,7 +93,9 @@ export const updateGroupMutation = () => { name icon } - # avatar # test this as result + avatar { + url + } locationName myRole } From 655f1a26c8d20867dcbdb7afc85807dfefb18100 Mon Sep 17 00:00:00 2001 From: ogerly Date: Thu, 27 Oct 2022 09:18:47 +0200 Subject: [PATCH 02/12] add space in register box --- webapp/components/Registration/RegistrationSlideInvite.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp/components/Registration/RegistrationSlideInvite.vue b/webapp/components/Registration/RegistrationSlideInvite.vue index 48c62d412..c9917f63e 100644 --- a/webapp/components/Registration/RegistrationSlideInvite.vue +++ b/webapp/components/Registration/RegistrationSlideInvite.vue @@ -17,6 +17,7 @@ {{ $t('components.registration.invite-code.form.description') }} + From b43d49bfa9348cd773bb79bf41805f767e6de511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Thu, 27 Oct 2022 10:07:02 +0200 Subject: [PATCH 03/12] Make donation progress bar color configurable --- webapp/components/ProgressBar/ProgressBar.vue | 35 ++++++++++++++----- webapp/constants/donation.js | 1 + 2 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 webapp/constants/donation.js diff --git a/webapp/components/ProgressBar/ProgressBar.vue b/webapp/components/ProgressBar/ProgressBar.vue index dd904e5b1..41183b798 100644 --- a/webapp/components/ProgressBar/ProgressBar.vue +++ b/webapp/components/ProgressBar/ProgressBar.vue @@ -2,7 +2,10 @@
-
+
{{ label }}
@@ -14,6 +17,8 @@ @@ -66,15 +76,22 @@ export default { left: 0px; height: 26px; // styleguide-button-size max-width: 100%; - background: repeating-linear-gradient( - 120deg, - $color-primary 0px, - $color-primary 30px, - $color-primary-light 50px, - $color-primary-light 75px, - $color-primary 95px - ); border-radius: $border-radius-base; + + &.color-uni { + background: $color-primary-light; + } + + &.color-repeating-linear-gradient { + background: repeating-linear-gradient( + 120deg, + $color-primary 0px, + $color-primary 30px, + $color-primary-light 50px, + $color-primary-light 75px, + $color-primary 95px + ); + } } .progress-bar__border { diff --git a/webapp/constants/donation.js b/webapp/constants/donation.js new file mode 100644 index 000000000..3e36ae9a8 --- /dev/null +++ b/webapp/constants/donation.js @@ -0,0 +1 @@ +export const PROGRESS_BAR_COLOR_TYPE = 'gradient' // 'uni' is the other option From 932572049be2f1dbee246d5c49f7d417797b8d0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Thu, 27 Oct 2022 12:27:51 +0200 Subject: [PATCH 04/12] Fix not correct working change of slug in group settings --- backend/src/middleware/sluggifyMiddleware.js | 8 +------- webapp/components/Group/GroupForm.vue | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/backend/src/middleware/sluggifyMiddleware.js b/backend/src/middleware/sluggifyMiddleware.js index 5ef2944be..bbe47c9aa 100644 --- a/backend/src/middleware/sluggifyMiddleware.js +++ b/backend/src/middleware/sluggifyMiddleware.js @@ -30,18 +30,12 @@ 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) => { - if (args.name) { - 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) }, UpdatePost: async (resolve, root, args, context, info) => { - // TODO: is this absolutely correct, see condition in 'UpdateGroup' above? may it works accidentally, because args.slug is always send? + // TODO: is this absolutely correct? what happens if "args.title" is not defined? may it works accidentally, because "args.title" or "args.slug" is always send? args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post'))) return resolve(root, args, context, info) }, diff --git a/webapp/components/Group/GroupForm.vue b/webapp/components/Group/GroupForm.vue index 6604a56d5..7b0f7840c 100644 --- a/webapp/components/Group/GroupForm.vue +++ b/webapp/components/Group/GroupForm.vue @@ -333,10 +333,10 @@ export default { this.$refs.groupForm.update('description', value) }, submit() { - const { name, about, description, groupType, actionRadius, /* locationName, */ categoryIds } = - this.formData + const { name, slug, about, description, groupType, actionRadius, categoryIds } = this.formData const variables = { name, + slug, about, description, groupType, From c42c1b2ef67fad93e21b472be334c42b9fa485f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Thu, 27 Oct 2022 12:28:37 +0200 Subject: [PATCH 05/12] Fix group slug tests --- .../src/middleware/slugifyMiddleware.spec.js | 54 ------------------- backend/src/schema/resolvers/groups.spec.js | 2 +- 2 files changed, 1 insertion(+), 55 deletions(-) diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js index 0b022fb53..1fdfb7364 100644 --- a/backend/src/middleware/slugifyMiddleware.spec.js +++ b/backend/src/middleware/slugifyMiddleware.spec.js @@ -213,33 +213,6 @@ describe('slugifyMiddleware', () => { describe('if group exists', () => { describe('if new slug not(!) exists', () => { - describe('setting slug by group name', () => { - it('has the new slug', async () => { - await expect( - mutate({ - mutation: updateGroupMutation(), - variables: { - id: createGroupResult.data.CreateGroup.id, - name: 'My Best Group', - }, - }), - ).resolves.toMatchObject({ - data: { - UpdateGroup: { - name: 'My Best Group', - slug: 'my-best-group', - about: 'Some about', - description: 'Some description' + descriptionAdditional100, - groupType: 'closed', - actionRadius: 'national', - myRole: 'owner', - }, - }, - errors: undefined, - }) - }) - }) - describe('setting slug explicitly', () => { it('has the new slug', async () => { await expect( @@ -284,33 +257,6 @@ describe('slugifyMiddleware', () => { }) }) - describe('setting slug by group name', () => { - it('has unique slug "*-1"', async () => { - await expect( - mutate({ - mutation: updateGroupMutation(), - variables: { - id: createGroupResult.data.CreateGroup.id, - name: 'Pre-Existing Group', - }, - }), - ).resolves.toMatchObject({ - data: { - UpdateGroup: { - name: 'Pre-Existing Group', - slug: 'pre-existing-group-1', - about: 'Some about', - description: 'Some description' + descriptionAdditional100, - groupType: 'closed', - actionRadius: 'national', - myRole: 'owner', - }, - }, - errors: undefined, - }) - }) - }) - describe('setting slug explicitly', () => { it('rejects UpdateGroup', async (done) => { try { diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js index d707440a4..3be8c0f90 100644 --- a/backend/src/schema/resolvers/groups.spec.js +++ b/backend/src/schema/resolvers/groups.spec.js @@ -2714,7 +2714,7 @@ describe('in mode', () => { UpdateGroup: { id: 'my-group', name: 'The New Group For Our Country', - slug: 'the-new-group-for-our-country', // changing the slug is tested in the slugifyMiddleware + slug: 'the-best-group', // changing the slug is tested in the slugifyMiddleware about: 'We will change the land!', description: 'Some country relevant description' + descriptionAdditional100, descriptionExcerpt: From f8c4b74be13f40fcfcaa9166e380601f30c615b2 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 27 Oct 2022 13:02:05 +0200 Subject: [PATCH 06/12] when avatar is uploaded, the description is not present. Therefore no descriptionExcerpt should be created in middleware --- backend/src/middleware/excerptMiddleware.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/middleware/excerptMiddleware.js b/backend/src/middleware/excerptMiddleware.js index 68eea9a74..28b30fb4f 100644 --- a/backend/src/middleware/excerptMiddleware.js +++ b/backend/src/middleware/excerptMiddleware.js @@ -8,7 +8,8 @@ export default { return resolve(root, args, context, info) }, UpdateGroup: async (resolve, root, args, context, info) => { - args.descriptionExcerpt = trunc(args.description, DESCRIPTION_EXCERPT_HTML_LENGTH).html + if (args.description) + args.descriptionExcerpt = trunc(args.description, DESCRIPTION_EXCERPT_HTML_LENGTH).html return resolve(root, args, context, info) }, CreatePost: async (resolve, root, args, context, info) => { From 5f4bf58dc600de12b737bf07284edd6d171e19e3 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 27 Oct 2022 14:06:42 +0200 Subject: [PATCH 07/12] feat: Restrict Comments on Posts in Groups --- .../src/middleware/permissionsMiddleware.js | 36 +++- .../schema/resolvers/postsInGroups.spec.js | 165 ++++++++++++++++++ 2 files changed, 200 insertions(+), 1 deletion(-) diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index 728a248fb..906285d12 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -249,6 +249,40 @@ const isMemberOfGroup = rule({ } }) +const canCommentPost = rule({ + cache: 'no_cache', +})(async (_parent, args, { user, driver }) => { + if (!(user && user.id)) return false + const { postId } = args + const userId = user.id + const session = driver.session() + const readTxPromise = session.readTransaction(async (transaction) => { + const transactionResponse = await transaction.run( + ` + MATCH (post:Post { id: $postId }) + OPTIONAL MATCH (post)-[:IN]->(group:Group) + OPTIONAL MATCH (user:User { id: $userId })-[membership:MEMBER_OF]->(group) + RETURN group AS group, membership AS membership + `, + { postId, userId }, + ) + return { + group: transactionResponse.records.map((record) => record.get('group'))[0], + membership: transactionResponse.records.map((record) => record.get('membership'))[0], + } + }) + try { + const { group, membership } = await readTxPromise + return ( + !group || (membership && ['usual', 'admin', 'owner'].includes(membership.properties.role)) + ) + } catch (error) { + throw new Error(error) + } finally { + session.close() + } +}) + const isAuthor = rule({ cache: 'no_cache', })(async (_parent, args, { user, driver }) => { @@ -361,7 +395,7 @@ export default shield( unshout: isAuthenticated, changePassword: isAuthenticated, review: isModerator, - CreateComment: isAuthenticated, + CreateComment: and(isAuthenticated, canCommentPost), UpdateComment: isAuthor, DeleteComment: isAuthor, DeleteUser: or(isDeletingOwnAccount, isAdmin), diff --git a/backend/src/schema/resolvers/postsInGroups.spec.js b/backend/src/schema/resolvers/postsInGroups.spec.js index d17c928ec..2c1e88b62 100644 --- a/backend/src/schema/resolvers/postsInGroups.spec.js +++ b/backend/src/schema/resolvers/postsInGroups.spec.js @@ -14,6 +14,7 @@ import { profilePagePosts, searchPosts, } from '../../db/graphql/posts' +import { createCommentMutation } from '../../db/graphql/comments' // eslint-disable-next-line no-unused-vars import { DESCRIPTION_WITHOUT_HTML_LENGTH_MIN } from '../../constants/groups' import CONFIG from '../../config' @@ -378,6 +379,170 @@ describe('Posts in Groups', () => { }) }) + describe('commenting posts in groups', () => { + describe('without membership of group', () => { + beforeAll(async () => { + authenticatedUser = await anyUser.toJson() + }) + + it('throws an error for public groups', async () => { + await expect( + mutate({ + mutation: createCommentMutation, + variables: { + postId: 'post-to-public-group', + content: + 'I am commenting a post in a public group without being a member of the group', + }, + }), + ).resolves.toMatchObject({ + errors: expect.arrayContaining([expect.objectContaining({ message: 'Not Authorized!' })]), + }) + }) + + it('throws an error for closed groups', async () => { + await expect( + mutate({ + mutation: createCommentMutation, + variables: { + postId: 'post-to-closed-group', + content: + 'I am commenting a post in a closed group without being a member of the group', + }, + }), + ).resolves.toMatchObject({ + errors: expect.arrayContaining([expect.objectContaining({ message: 'Not Authorized!' })]), + }) + }) + + it('throws an error for hidden groups', async () => { + await expect( + mutate({ + mutation: createCommentMutation, + variables: { + postId: 'post-to-hidden-group', + content: + 'I am commenting a post in a hidden group without being a member of the group', + }, + }), + ).resolves.toMatchObject({ + errors: expect.arrayContaining([expect.objectContaining({ message: 'Not Authorized!' })]), + }) + }) + }) + + describe('as a pending member of group', () => { + beforeAll(async () => { + authenticatedUser = await pendingUser.toJson() + }) + + it('throws an error for public groups', async () => { + await expect( + mutate({ + mutation: createCommentMutation, + variables: { + postId: 'post-to-public-group', + content: 'I am commenting a post in a public group as a pending member of the group', + }, + }), + ).resolves.toMatchObject({ + errors: expect.arrayContaining([expect.objectContaining({ message: 'Not Authorized!' })]), + }) + }) + + it('throws an error for closed groups', async () => { + await expect( + mutate({ + mutation: createCommentMutation, + variables: { + postId: 'post-to-closed-group', + content: 'I am commenting a post in a closed group as a pending member of the group', + }, + }), + ).resolves.toMatchObject({ + errors: expect.arrayContaining([expect.objectContaining({ message: 'Not Authorized!' })]), + }) + }) + + it('throws an error for hidden groups', async () => { + await expect( + mutate({ + mutation: createCommentMutation, + variables: { + postId: 'post-to-hidden-group', + content: 'I am commenting a post in a hidden group as a pending member of the group', + }, + }), + ).resolves.toMatchObject({ + errors: expect.arrayContaining([expect.objectContaining({ message: 'Not Authorized!' })]), + }) + }) + }) + + describe('as a member of group', () => { + beforeAll(async () => { + authenticatedUser = await allGroupsUser.toJson() + }) + + it('comments a post in a public group', async () => { + await expect( + mutate({ + mutation: createCommentMutation, + variables: { + postId: 'post-to-public-group', + content: 'I am commenting a post in a public group as a member of the group', + }, + }), + ).resolves.toMatchObject({ + data: { + CreateComment: { + id: expect.any(String), + }, + }, + errors: undefined, + }) + }) + + it('comments a post in a closed group', async () => { + await expect( + mutate({ + mutation: createCommentMutation, + variables: { + postId: 'post-to-closed-group', + content: 'I am commenting a post in a closed group as a member of the group', + }, + }), + ).resolves.toMatchObject({ + data: { + CreateComment: { + id: expect.any(String), + }, + }, + errors: undefined, + }) + }) + + it('comments a post in a hidden group', async () => { + await expect( + mutate({ + mutation: createCommentMutation, + variables: { + postId: 'post-to-hidden-group', + content: 'I am commenting a post in a hidden group as a member of the group', + }, + }), + ).resolves.toMatchObject({ + data: { + CreateComment: { + id: expect.any(String), + }, + }, + errors: undefined, + }) + }) + }) + }) + describe('visibility of posts', () => { describe('query post by ID', () => { describe('without authentication', () => { From 134a8099c88ce0a6bcf7617e6e3d6a1768054a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Thu, 27 Oct 2022 14:35:41 +0200 Subject: [PATCH 08/12] Add live demo to main readme --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 45006581c..f0688f820 100644 --- a/README.md +++ b/README.md @@ -65,11 +65,18 @@ Change into the new folder. $ cd Ocelot-Social ``` +## Live Demo And Developer Logins + +**Try out our deployed [development environment](https://stage.ocelot.social).** + +Visit our staging networks: + +* central staging network: [stage.ocelot.social](https://stage.ocelot.social) + + ### Login - - -Logins in the browser after the following installations: +Logins for the live demos and developers (local developers after the following installations) in the browser: | email | password | role | | :--- | :--- | :--- | From 76ad2ec0630ce2a9549e1c258b7608e7eb97cccc Mon Sep 17 00:00:00 2001 From: ogerly Date: Thu, 27 Oct 2022 15:22:22 +0200 Subject: [PATCH 09/12] change to --- webapp/components/Registration/RegistrationSlideInvite.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/components/Registration/RegistrationSlideInvite.vue b/webapp/components/Registration/RegistrationSlideInvite.vue index c9917f63e..87c1c48b1 100644 --- a/webapp/components/Registration/RegistrationSlideInvite.vue +++ b/webapp/components/Registration/RegistrationSlideInvite.vue @@ -17,7 +17,7 @@ {{ $t('components.registration.invite-code.form.description') }} - + From 93add91e77f4260d440ed60bdbac3d1663c54595 Mon Sep 17 00:00:00 2001 From: ogerly Date: Thu, 27 Oct 2022 15:23:19 +0200 Subject: [PATCH 10/12] fix lint --- webapp/components/Registration/RegistrationSlideInvite.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/components/Registration/RegistrationSlideInvite.vue b/webapp/components/Registration/RegistrationSlideInvite.vue index 87c1c48b1..723071510 100644 --- a/webapp/components/Registration/RegistrationSlideInvite.vue +++ b/webapp/components/Registration/RegistrationSlideInvite.vue @@ -17,7 +17,7 @@ {{ $t('components.registration.invite-code.form.description') }} - + From 09cb120f9281a5227bce46b529979aeaeaed5203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Thu, 27 Oct 2022 16:03:06 +0200 Subject: [PATCH 11/12] Add 'ds-spaces' to the other registration slider components --- webapp/components/Registration/RegistrationSlideCreate.vue | 3 +++ webapp/components/Registration/RegistrationSlideEmail.vue | 1 + webapp/components/Registration/RegistrationSlideNoPublic.vue | 1 + webapp/components/Registration/RegistrationSlideNonce.vue | 1 + 4 files changed, 6 insertions(+) diff --git a/webapp/components/Registration/RegistrationSlideCreate.vue b/webapp/components/Registration/RegistrationSlideCreate.vue index 35bba6614..0906fc8a4 100644 --- a/webapp/components/Registration/RegistrationSlideCreate.vue +++ b/webapp/components/Registration/RegistrationSlideCreate.vue @@ -6,6 +6,7 @@ {{ $t('components.registration.create-user-account.success') }} +
@@ -21,6 +22,7 @@ {{ $t('site.back-to-login') }} +
diff --git a/webapp/components/Registration/RegistrationSlideEmail.vue b/webapp/components/Registration/RegistrationSlideEmail.vue index 6ba443996..6d6454ac9 100644 --- a/webapp/components/Registration/RegistrationSlideEmail.vue +++ b/webapp/components/Registration/RegistrationSlideEmail.vue @@ -24,6 +24,7 @@ {{ $t('components.registration.email.form.sendEmailAgain') }} + diff --git a/webapp/components/Registration/RegistrationSlideNoPublic.vue b/webapp/components/Registration/RegistrationSlideNoPublic.vue index 1d26cc8e2..c5f815ef4 100644 --- a/webapp/components/Registration/RegistrationSlideNoPublic.vue +++ b/webapp/components/Registration/RegistrationSlideNoPublic.vue @@ -2,6 +2,7 @@ + diff --git a/webapp/components/Registration/RegistrationSlideNonce.vue b/webapp/components/Registration/RegistrationSlideNonce.vue index b9de7afd1..604d9b132 100644 --- a/webapp/components/Registration/RegistrationSlideNonce.vue +++ b/webapp/components/Registration/RegistrationSlideNonce.vue @@ -19,6 +19,7 @@ {{ $t('components.registration.email-nonce.form.description') }} + From ef97b4c388e9bb7fb0a9b90deee3c388c779adcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Fri, 28 Oct 2022 09:11:01 +0200 Subject: [PATCH 12/12] Fix a merging left over with header logo click and fix functionallity on empty string targets --- webapp/components/HeaderMenu/HeaderMenu.vue | 26 +++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/webapp/components/HeaderMenu/HeaderMenu.vue b/webapp/components/HeaderMenu/HeaderMenu.vue index 8876338ee..44d34e5b7 100644 --- a/webapp/components/HeaderMenu/HeaderMenu.vue +++ b/webapp/components/HeaderMenu/HeaderMenu.vue @@ -7,7 +7,13 @@ @@ -91,7 +97,23 @@ - + + + +