Add ability to not send an email for registration
This commit is contained in:
parent
e854d57bed
commit
4115f20084
10 changed files with 133 additions and 47 deletions
|
@ -5,7 +5,7 @@ import { HttpClient, HttpParams } from '@angular/common/http'
|
|||
import { Injectable } from '@angular/core'
|
||||
import { RestExtractor, RestPagination, RestService } from '@app/core'
|
||||
import { arrayify } from '@shared/core-utils'
|
||||
import { ResultList, UserRegistration } from '@shared/models'
|
||||
import { ResultList, UserRegistration, UserRegistrationUpdateState } from '@shared/models'
|
||||
import { environment } from '../../../../environments/environment'
|
||||
|
||||
@Injectable()
|
||||
|
@ -40,17 +40,29 @@ export class AdminRegistrationService {
|
|||
)
|
||||
}
|
||||
|
||||
acceptRegistration (registration: UserRegistration, moderationResponse: string) {
|
||||
acceptRegistration (options: {
|
||||
registration: UserRegistration
|
||||
moderationResponse: string
|
||||
preventEmailDelivery: boolean
|
||||
}) {
|
||||
const { registration, moderationResponse, preventEmailDelivery } = options
|
||||
|
||||
const url = AdminRegistrationService.BASE_REGISTRATION_URL + '/' + registration.id + '/accept'
|
||||
const body = { moderationResponse }
|
||||
const body: UserRegistrationUpdateState = { moderationResponse, preventEmailDelivery }
|
||||
|
||||
return this.authHttp.post(url, body)
|
||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
||||
}
|
||||
|
||||
rejectRegistration (registration: UserRegistration, moderationResponse: string) {
|
||||
rejectRegistration (options: {
|
||||
registration: UserRegistration
|
||||
moderationResponse: string
|
||||
preventEmailDelivery: boolean
|
||||
}) {
|
||||
const { registration, moderationResponse, preventEmailDelivery } = options
|
||||
|
||||
const url = AdminRegistrationService.BASE_REGISTRATION_URL + '/' + registration.id + '/reject'
|
||||
const body = { moderationResponse }
|
||||
const body: UserRegistrationUpdateState = { moderationResponse, preventEmailDelivery }
|
||||
|
||||
return this.authHttp.post(url, body)
|
||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<div class="modal-body mb-3">
|
||||
|
||||
<div i18n *ngIf="!registration.emailVerified" class="alert alert-warning">
|
||||
Registration email has not been verified.
|
||||
Registration email has not been verified. Email delivery has been disabled by default.
|
||||
</div>
|
||||
|
||||
<div class="description">
|
||||
|
@ -21,7 +21,7 @@
|
|||
<strong>Accepting</strong> <em>{{ registration.username }}</em> registration will create the account and channel.
|
||||
</p>
|
||||
|
||||
<p *ngIf="isEmailEnabled()" i18n>
|
||||
<p *ngIf="isEmailEnabled()" i18n [ngClass]="{ 'text-decoration-line-through': isPreventEmailDeliveryChecked() }">
|
||||
An email will be sent to <em>{{ registration.email }}</em> explaining its account has been created with the moderation response you'll write below.
|
||||
</p>
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
|||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="isReject()">
|
||||
<p i18n>
|
||||
<p i18n [ngClass]="{ 'text-decoration-line-through': isPreventEmailDeliveryChecked() }">
|
||||
An email will be sent to <em>{{ registration.email }}</em> explaining its registration request has been <strong>rejected</strong> with the moderation response you'll write below.
|
||||
</p>
|
||||
|
||||
|
@ -53,6 +53,13 @@
|
|||
{{ formErrors.moderationResponse }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="preventEmailDelivery" formControlName="preventEmailDelivery" [disabled]="!isEmailEnabled()"
|
||||
i18n-labelText labelText="Prevent email from being sent to the user"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer inputs">
|
||||
|
|
|
@ -34,7 +34,8 @@ export class ProcessRegistrationModalComponent extends FormReactive implements O
|
|||
|
||||
ngOnInit () {
|
||||
this.buildForm({
|
||||
moderationResponse: REGISTRATION_MODERATION_RESPONSE_VALIDATOR
|
||||
moderationResponse: REGISTRATION_MODERATION_RESPONSE_VALIDATOR,
|
||||
preventEmailDelivery: null
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -50,6 +51,10 @@ export class ProcessRegistrationModalComponent extends FormReactive implements O
|
|||
this.processMode = mode
|
||||
this.registration = registration
|
||||
|
||||
this.form.patchValue({
|
||||
preventEmailDelivery: !this.isEmailEnabled() || registration.emailVerified !== true
|
||||
})
|
||||
|
||||
this.openedModal = this.modalService.open(this.modal, { centered: true })
|
||||
}
|
||||
|
||||
|
@ -77,31 +82,41 @@ export class ProcessRegistrationModalComponent extends FormReactive implements O
|
|||
return this.server.getHTMLConfig().email.enabled
|
||||
}
|
||||
|
||||
isPreventEmailDeliveryChecked () {
|
||||
return this.form.value.preventEmailDelivery
|
||||
}
|
||||
|
||||
private acceptRegistration () {
|
||||
this.registrationService.acceptRegistration(this.registration, this.form.value.moderationResponse)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`${this.registration.username} account created`)
|
||||
this.registrationService.acceptRegistration({
|
||||
registration: this.registration,
|
||||
moderationResponse: this.form.value.moderationResponse,
|
||||
preventEmailDelivery: this.form.value.preventEmailDelivery
|
||||
}).subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`${this.registration.username} account created`)
|
||||
|
||||
this.registrationProcessed.emit()
|
||||
this.hide()
|
||||
},
|
||||
this.registrationProcessed.emit()
|
||||
this.hide()
|
||||
},
|
||||
|
||||
error: err => this.notifier.error(err.message)
|
||||
})
|
||||
error: err => this.notifier.error(err.message)
|
||||
})
|
||||
}
|
||||
|
||||
private rejectRegistration () {
|
||||
this.registrationService.rejectRegistration(this.registration, this.form.value.moderationResponse)
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`${this.registration.username} registration rejected`)
|
||||
this.registrationService.rejectRegistration({
|
||||
registration: this.registration,
|
||||
moderationResponse: this.form.value.moderationResponse,
|
||||
preventEmailDelivery: this.form.value.preventEmailDelivery
|
||||
}).subscribe({
|
||||
next: () => {
|
||||
this.notifier.success($localize`${this.registration.username} registration rejected`)
|
||||
|
||||
this.registrationProcessed.emit()
|
||||
this.hide()
|
||||
},
|
||||
this.registrationProcessed.emit()
|
||||
this.hide()
|
||||
},
|
||||
|
||||
error: err => this.notifier.error(err.message)
|
||||
})
|
||||
error: err => this.notifier.error(err.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,5 +12,5 @@ export type BuildFormArgument = {
|
|||
}
|
||||
|
||||
export type BuildFormDefaultValues = {
|
||||
[ name: string ]: number | string | string[] | BuildFormDefaultValues
|
||||
[ name: string ]: boolean | number | string | string[] | BuildFormDefaultValues
|
||||
}
|
||||
|
|
|
@ -3,7 +3,14 @@ import { Emailer } from '@server/lib/emailer'
|
|||
import { Hooks } from '@server/lib/plugins/hooks'
|
||||
import { UserRegistrationModel } from '@server/models/user/user-registration'
|
||||
import { pick } from '@shared/core-utils'
|
||||
import { HttpStatusCode, UserRegister, UserRegistrationRequest, UserRegistrationState, UserRight } from '@shared/models'
|
||||
import {
|
||||
HttpStatusCode,
|
||||
UserRegister,
|
||||
UserRegistrationRequest,
|
||||
UserRegistrationState,
|
||||
UserRegistrationUpdateState,
|
||||
UserRight
|
||||
} from '@shared/models'
|
||||
import { auditLoggerFactory, UserAuditView } from '../../../helpers/audit-logger'
|
||||
import { logger } from '../../../helpers/logger'
|
||||
import { CONFIG } from '../../../initializers/config'
|
||||
|
@ -125,6 +132,7 @@ async function requestRegistration (req: express.Request, res: express.Response)
|
|||
|
||||
async function acceptRegistration (req: express.Request, res: express.Response) {
|
||||
const registration = res.locals.userRegistration
|
||||
const body: UserRegistrationUpdateState = req.body
|
||||
|
||||
const userToCreate = buildUser({
|
||||
username: registration.username,
|
||||
|
@ -150,26 +158,31 @@ async function acceptRegistration (req: express.Request, res: express.Response)
|
|||
|
||||
registration.userId = user.id
|
||||
registration.state = UserRegistrationState.ACCEPTED
|
||||
registration.moderationResponse = req.body.moderationResponse
|
||||
registration.moderationResponse = body.moderationResponse
|
||||
|
||||
await registration.save()
|
||||
|
||||
logger.info('Registration of %s accepted', registration.username)
|
||||
|
||||
Emailer.Instance.addUserRegistrationRequestProcessedJob(registration)
|
||||
if (body.preventEmailDelivery !== true) {
|
||||
Emailer.Instance.addUserRegistrationRequestProcessedJob(registration)
|
||||
}
|
||||
|
||||
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
||||
}
|
||||
|
||||
async function rejectRegistration (req: express.Request, res: express.Response) {
|
||||
const registration = res.locals.userRegistration
|
||||
const body: UserRegistrationUpdateState = req.body
|
||||
|
||||
registration.state = UserRegistrationState.REJECTED
|
||||
registration.moderationResponse = req.body.moderationResponse
|
||||
registration.moderationResponse = body.moderationResponse
|
||||
|
||||
await registration.save()
|
||||
|
||||
Emailer.Instance.addUserRegistrationRequestProcessedJob(registration)
|
||||
if (body.preventEmailDelivery !== true) {
|
||||
Emailer.Instance.addUserRegistrationRequestProcessedJob(registration)
|
||||
}
|
||||
|
||||
logger.info('Registration of %s rejected', registration.username)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import express from 'express'
|
||||
import { body, param, query, ValidationChain } from 'express-validator'
|
||||
import { exists, isIdValid } from '@server/helpers/custom-validators/misc'
|
||||
import { exists, isBooleanValid, isIdValid, toBooleanOrNull } from '@server/helpers/custom-validators/misc'
|
||||
import { isRegistrationModerationResponseValid, isRegistrationReasonValid } from '@server/helpers/custom-validators/user-registration'
|
||||
import { CONFIG } from '@server/initializers/config'
|
||||
import { Hooks } from '@server/lib/plugins/hooks'
|
||||
|
@ -91,6 +91,11 @@ const acceptOrRejectRegistrationValidator = [
|
|||
body('moderationResponse')
|
||||
.custom(isRegistrationModerationResponseValid),
|
||||
|
||||
body('preventEmailDelivery')
|
||||
.optional()
|
||||
.customSanitizer(toBooleanOrNull)
|
||||
.custom(isBooleanValid).withMessage('Should have preventEmailDelivery boolean'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!await checkRegistrationIdExist(req.params.registrationId, res)) return
|
||||
|
|
|
@ -329,6 +329,42 @@ describe('Test registrations', function () {
|
|||
}
|
||||
})
|
||||
|
||||
it('Should be able to prevent email delivery on accept/reject', async function () {
|
||||
this.timeout(50000)
|
||||
|
||||
let id1: number
|
||||
let id2: number
|
||||
|
||||
{
|
||||
const { id } = await server.registrations.requestRegistration({
|
||||
username: 'user7',
|
||||
email: 'user7@example.com',
|
||||
registrationReason: 'tt'
|
||||
})
|
||||
id1 = id
|
||||
}
|
||||
{
|
||||
const { id } = await server.registrations.requestRegistration({
|
||||
username: 'user8',
|
||||
email: 'user8@example.com',
|
||||
registrationReason: 'tt'
|
||||
})
|
||||
id2 = id
|
||||
}
|
||||
|
||||
await server.registrations.accept({ id: id1, moderationResponse: 'tt', preventEmailDelivery: true })
|
||||
await server.registrations.reject({ id: id2, moderationResponse: 'tt', preventEmailDelivery: true })
|
||||
|
||||
await waitJobs([ server ])
|
||||
|
||||
const filtered = emails.filter(e => {
|
||||
const address = e['to'][0]['address']
|
||||
return address === 'user7@example.com' || address === 'user8@example.com'
|
||||
})
|
||||
|
||||
expect(filtered).to.have.lengthOf(0)
|
||||
})
|
||||
|
||||
it('Should request a registration without a channel, that will conflict with an already existing channel', async function () {
|
||||
let id1: number
|
||||
let id2: number
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export interface UserRegistrationUpdateState {
|
||||
moderationResponse: string
|
||||
preventEmailDelivery?: boolean
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { pick } from '@shared/core-utils'
|
||||
import { HttpStatusCode, ResultList, UserRegistration, UserRegistrationRequest } from '@shared/models'
|
||||
import { HttpStatusCode, ResultList, UserRegistration, UserRegistrationRequest, UserRegistrationUpdateState } from '@shared/models'
|
||||
import { unwrapBody } from '../requests'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
|
||||
|
@ -47,35 +47,29 @@ export class RegistrationsCommand extends AbstractCommand {
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
accept (options: OverrideCommandOptions & {
|
||||
id: number
|
||||
moderationResponse: string
|
||||
}) {
|
||||
const { id, moderationResponse } = options
|
||||
accept (options: OverrideCommandOptions & { id: number } & UserRegistrationUpdateState) {
|
||||
const { id } = options
|
||||
const path = '/api/v1/users/registrations/' + id + '/accept'
|
||||
|
||||
return this.postBodyRequest({
|
||||
...options,
|
||||
|
||||
path,
|
||||
fields: { moderationResponse },
|
||||
fields: pick(options, [ 'moderationResponse', 'preventEmailDelivery' ]),
|
||||
implicitToken: true,
|
||||
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
|
||||
})
|
||||
}
|
||||
|
||||
reject (options: OverrideCommandOptions & {
|
||||
id: number
|
||||
moderationResponse: string
|
||||
}) {
|
||||
const { id, moderationResponse } = options
|
||||
reject (options: OverrideCommandOptions & { id: number } & UserRegistrationUpdateState) {
|
||||
const { id } = options
|
||||
const path = '/api/v1/users/registrations/' + id + '/reject'
|
||||
|
||||
return this.postBodyRequest({
|
||||
...options,
|
||||
|
||||
path,
|
||||
fields: { moderationResponse },
|
||||
fields: pick(options, [ 'moderationResponse', 'preventEmailDelivery' ]),
|
||||
implicitToken: true,
|
||||
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
|
||||
})
|
||||
|
|
|
@ -7961,6 +7961,9 @@ components:
|
|||
moderationResponse:
|
||||
type: string
|
||||
description: Moderation response to send to the user
|
||||
preventEmailDelivery:
|
||||
type: boolean
|
||||
description: Set it to true if you don't want PeerTube to send an email to the user
|
||||
required:
|
||||
- moderationResponse
|
||||
|
||||
|
|
Loading…
Reference in a new issue