From 30c72bb7340381d2d32eeec14c0b91951d9ad2d5 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 13 Dec 2022 10:10:42 +0100 Subject: [PATCH 01/36] Setup rejecctContribution method. --- backend/src/graphql/resolver/ContributionResolver.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 15bdbfc2e..71c1d21b7 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -252,4 +252,13 @@ export class ContributionResolver { return new UnconfirmedContribution(contributionToUpdate, user, creations) } + + @Authorized([RIGHTS.REJECT_CONTRIBUTION]) + @Mutation(() => Boolean) + async rejectContribution( + @Arg('id', () => Int) id: number, + @Ctx() context: Context, + ): Promise { + return true + } } From ca01e343942b1adeba6949581fa79bbcc60a7224 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 13 Dec 2022 10:17:35 +0100 Subject: [PATCH 02/36] Get adminUser and find contribution with specific id. --- backend/src/graphql/resolver/ContributionResolver.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 71c1d21b7..a610c9bfd 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -259,6 +259,9 @@ export class ContributionResolver { @Arg('id', () => Int) id: number, @Ctx() context: Context, ): Promise { + const user = getUser(context) + + const contributionToUpdate = await dbContribution.findOne({ id }) return true } } From dabef05dc0ba0e35b9a389707f2b25b38294e947 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 20 Dec 2022 10:24:27 +0100 Subject: [PATCH 03/36] Change name of user to moderatorUser. --- backend/src/graphql/resolver/ContributionResolver.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index feef8ea3c..15edaa9a9 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -686,9 +686,10 @@ export class ContributionResolver { @Arg('id', () => Int) id: number, @Ctx() context: Context, ): Promise { - const user = getUser(context) + const moderatorUser = getUser(context) const contributionToUpdate = await DbContribution.findOne({ id }) + return true } } From 853bfabd57bcb2f5ce7375d33f3b9ff57dfeb55d Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 20 Dec 2022 12:09:59 +0100 Subject: [PATCH 04/36] Finish denie logic, add deniedAt IsNull() to queries. --- backend/src/graphql/model/Contribution.ts | 8 +++++ .../graphql/resolver/ContributionResolver.ts | 30 ++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/model/Contribution.ts b/backend/src/graphql/model/Contribution.ts index cf57e632f..d71d2ce6b 100644 --- a/backend/src/graphql/model/Contribution.ts +++ b/backend/src/graphql/model/Contribution.ts @@ -18,6 +18,8 @@ export class Contribution { this.contributionDate = contribution.contributionDate this.state = contribution.contributionStatus this.messagesCount = contribution.messages ? contribution.messages.length : 0 + this.deniedAt = contribution.deniedAt + this.deniedBy = contribution.deniedBy } @Field(() => Number) @@ -47,6 +49,12 @@ export class Contribution { @Field(() => Number, { nullable: true }) confirmedBy: number | null + @Field(() => Date, { nullable: true }) + deniedAt: Date | null + + @Field(() => Number, { nullable: true }) + deniedBy: number | null + @Field(() => Date) contributionDate: Date diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 15edaa9a9..ea12d1362 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -146,6 +146,7 @@ export class ContributionResolver { @Ctx() context: Context, ): Promise { const user = getUser(context) + // TODO: Check if deniedAt IsNull() const where: { userId: number confirmedBy?: FindOperator | null @@ -214,7 +215,7 @@ export class ContributionResolver { const user = getUser(context) const contributionToUpdate = await DbContribution.findOne({ - where: { id: contributionId, confirmedAt: IsNull() }, + where: { id: contributionId, confirmedAt: IsNull(), deniedAt: IsNull() }, }) if (!contributionToUpdate) { logger.error('No contribution found to given id') @@ -406,7 +407,7 @@ export class ContributionResolver { const moderator = getUser(context) const contributionToUpdate = await DbContribution.findOne({ - where: { id, confirmedAt: IsNull() }, + where: { id, confirmedAt: IsNull(), deniedAt: IsNull() }, }) if (!contributionToUpdate) { logger.error('No contribution found to given id.') @@ -472,6 +473,7 @@ export class ContributionResolver { .from(DbContribution, 'c') .leftJoinAndSelect('c.messages', 'm') .where({ confirmedAt: IsNull() }) + .andWhere({ deniedAt: IsNull() }) .getMany() if (contributions.length === 0) { @@ -686,9 +688,29 @@ export class ContributionResolver { @Arg('id', () => Int) id: number, @Ctx() context: Context, ): Promise { - const moderatorUser = getUser(context) - const contributionToUpdate = await DbContribution.findOne({ id }) + // TODO: Check + // - contribution exists + // - state has accept one + if (!contributionToUpdate) { + logger.error(`Contribution not found for given id: ${id}`) + throw new Error(`Contribution not found for given id.`) + } + if ( + contributionToUpdate.contributionStatus !== ContributionStatus.IN_PROGRESS && + contributionToUpdate.contributionStatus !== ContributionStatus.PENDING + ) { + logger.error( + `Contribution state (${contributionToUpdate.contributionStatus}) is not allowed.`, + ) + throw new Error(`State of the contribution is not allowed.`) + } + const user = getUser(context) + + contributionToUpdate.contributionStatus = ContributionStatus.DENIED + contributionToUpdate.deniedBy = user.id + contributionToUpdate.deniedAt = new Date() + await contributionToUpdate.save() return true } From a584a6991244e7bc3c0d0660a12ea50132a9b876 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 20 Dec 2022 12:10:31 +0100 Subject: [PATCH 05/36] Add deniedAt and deniedBy to the listAllContribution query. --- frontend/src/graphql/queries.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index d261797c2..2bc10c61d 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -201,6 +201,8 @@ export const listAllContributions = gql` contributionDate confirmedAt confirmedBy + deniedAt + deniedBy } } } From df0a396a4562e69b9e02c34bc83805787009811d Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 22 Dec 2022 10:01:48 +0100 Subject: [PATCH 06/36] Change logic so that deny is also integrated. --- .../components/Contributions/ContributionListItem.vue | 10 ++++++++++ frontend/src/graphql/queries.js | 2 ++ 2 files changed, 12 insertions(+) diff --git a/frontend/src/components/Contributions/ContributionListItem.vue b/frontend/src/components/Contributions/ContributionListItem.vue index 53de8c461..bb26893f7 100644 --- a/frontend/src/components/Contributions/ContributionListItem.vue +++ b/frontend/src/components/Contributions/ContributionListItem.vue @@ -130,6 +130,14 @@ export default { type: String, required: false, }, + deniedBy: { + type: Number, + required: false, + }, + deniedAt: { + type: String, + required: false, + }, state: { type: String, required: false, @@ -157,11 +165,13 @@ export default { computed: { icon() { if (this.deletedAt) return 'x-circle' + if (this.deniedAt) return 'x-circle' if (this.confirmedAt) return 'check' return 'bell-fill' }, variant() { if (this.deletedAt) return 'danger' + if (this.deniedAt) return 'danger' if (this.confirmedAt) return 'success' if (this.state === 'IN_PROGRESS') return 'warning' return 'primary' diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 2bc10c61d..d5eea9fdc 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -182,6 +182,8 @@ export const listContributions = gql` deletedAt state messagesCount + deniedAt + deniedBy } } } From 493aa3201106e36ba0027f80b55cf50460330321 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 12 Jan 2023 15:05:41 +0100 Subject: [PATCH 07/36] Deny contribution sends rejected contribution email. --- .../graphql/resolver/ContributionResolver.ts | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index d93b675d2..bca3e7674 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -709,13 +709,33 @@ export class ContributionResolver { ) throw new Error(`State of the contribution is not allowed.`) } - const user = getUser(context) + const moderator = getUser(context) + const user = await DbUser.findOne( + { id: contributionToUpdate.userId }, + { relations: ['emailContact'] }, + ) + if (!user) { + logger.error( + `Could not find User for the Contribution (userId: ${contributionToUpdate.userId}).`, + ) + throw new Error('Could not find User for the Contribution.') + } contributionToUpdate.contributionStatus = ContributionStatus.DENIED - contributionToUpdate.deniedBy = user.id + contributionToUpdate.deniedBy = moderator.id contributionToUpdate.deniedAt = new Date() - await contributionToUpdate.save() + const res = await contributionToUpdate.save() - return true + sendContributionRejectedEmail({ + firstName: user.firstName, + lastName: user.lastName, + email: user.emailContact.email, + language: user.language, + senderFirstName: moderator.firstName, + senderLastName: moderator.lastName, + contributionMemo: contributionToUpdate.memo, + }) + + return !!res } } From b88bfabb781ed0dd991413cb0c49d68592a069b3 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 12 Jan 2023 15:06:45 +0100 Subject: [PATCH 08/36] Add button to deny contribution in the admin interface --- .../components/Tables/OpenCreationsTable.vue | 14 +++++++++++- admin/src/graphql/rejectContribution.js | 7 ++++++ admin/src/pages/CreationConfirm.vue | 22 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 admin/src/graphql/rejectContribution.js diff --git a/admin/src/components/Tables/OpenCreationsTable.vue b/admin/src/components/Tables/OpenCreationsTable.vue index 2c5caabb5..2c32c2fbf 100644 --- a/admin/src/components/Tables/OpenCreationsTable.vue +++ b/admin/src/components/Tables/OpenCreationsTable.vue @@ -8,7 +8,7 @@ @click="$emit('remove-creation', row.item)" class="mr-2" > - + +