Add ability to not send an email for registration
This commit is contained in:
parent
e854d57bed
commit
4115f20084
|
@ -5,7 +5,7 @@ import { HttpClient, HttpParams } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { RestExtractor, RestPagination, RestService } from '@app/core'
|
import { RestExtractor, RestPagination, RestService } from '@app/core'
|
||||||
import { arrayify } from '@shared/core-utils'
|
import { arrayify } from '@shared/core-utils'
|
||||||
import { ResultList, UserRegistration } from '@shared/models'
|
import { ResultList, UserRegistration, UserRegistrationUpdateState } from '@shared/models'
|
||||||
import { environment } from '../../../../environments/environment'
|
import { environment } from '../../../../environments/environment'
|
||||||
|
|
||||||
@Injectable()
|
@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 url = AdminRegistrationService.BASE_REGISTRATION_URL + '/' + registration.id + '/accept'
|
||||||
const body = { moderationResponse }
|
const body: UserRegistrationUpdateState = { moderationResponse, preventEmailDelivery }
|
||||||
|
|
||||||
return this.authHttp.post(url, body)
|
return this.authHttp.post(url, body)
|
||||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
.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 url = AdminRegistrationService.BASE_REGISTRATION_URL + '/' + registration.id + '/reject'
|
||||||
const body = { moderationResponse }
|
const body: UserRegistrationUpdateState = { moderationResponse, preventEmailDelivery }
|
||||||
|
|
||||||
return this.authHttp.post(url, body)
|
return this.authHttp.post(url, body)
|
||||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<div class="modal-body mb-3">
|
<div class="modal-body mb-3">
|
||||||
|
|
||||||
<div i18n *ngIf="!registration.emailVerified" class="alert alert-warning">
|
<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>
|
||||||
|
|
||||||
<div class="description">
|
<div class="description">
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
<strong>Accepting</strong> <em>{{ registration.username }}</em> registration will create the account and channel.
|
<strong>Accepting</strong> <em>{{ registration.username }}</em> registration will create the account and channel.
|
||||||
</p>
|
</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.
|
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>
|
</p>
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="isReject()">
|
<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.
|
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>
|
</p>
|
||||||
|
|
||||||
|
@ -53,6 +53,13 @@
|
||||||
{{ formErrors.moderationResponse }}
|
{{ formErrors.moderationResponse }}
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
|
|
||||||
<div class="modal-footer inputs">
|
<div class="modal-footer inputs">
|
||||||
|
|
|
@ -34,7 +34,8 @@ export class ProcessRegistrationModalComponent extends FormReactive implements O
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.buildForm({
|
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.processMode = mode
|
||||||
this.registration = registration
|
this.registration = registration
|
||||||
|
|
||||||
|
this.form.patchValue({
|
||||||
|
preventEmailDelivery: !this.isEmailEnabled() || registration.emailVerified !== true
|
||||||
|
})
|
||||||
|
|
||||||
this.openedModal = this.modalService.open(this.modal, { centered: 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
|
return this.server.getHTMLConfig().email.enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isPreventEmailDeliveryChecked () {
|
||||||
|
return this.form.value.preventEmailDelivery
|
||||||
|
}
|
||||||
|
|
||||||
private acceptRegistration () {
|
private acceptRegistration () {
|
||||||
this.registrationService.acceptRegistration(this.registration, this.form.value.moderationResponse)
|
this.registrationService.acceptRegistration({
|
||||||
.subscribe({
|
registration: this.registration,
|
||||||
next: () => {
|
moderationResponse: this.form.value.moderationResponse,
|
||||||
this.notifier.success($localize`${this.registration.username} account created`)
|
preventEmailDelivery: this.form.value.preventEmailDelivery
|
||||||
|
}).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.notifier.success($localize`${this.registration.username} account created`)
|
||||||
|
|
||||||
this.registrationProcessed.emit()
|
this.registrationProcessed.emit()
|
||||||
this.hide()
|
this.hide()
|
||||||
},
|
},
|
||||||
|
|
||||||
error: err => this.notifier.error(err.message)
|
error: err => this.notifier.error(err.message)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private rejectRegistration () {
|
private rejectRegistration () {
|
||||||
this.registrationService.rejectRegistration(this.registration, this.form.value.moderationResponse)
|
this.registrationService.rejectRegistration({
|
||||||
.subscribe({
|
registration: this.registration,
|
||||||
next: () => {
|
moderationResponse: this.form.value.moderationResponse,
|
||||||
this.notifier.success($localize`${this.registration.username} registration rejected`)
|
preventEmailDelivery: this.form.value.preventEmailDelivery
|
||||||
|
}).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.notifier.success($localize`${this.registration.username} registration rejected`)
|
||||||
|
|
||||||
this.registrationProcessed.emit()
|
this.registrationProcessed.emit()
|
||||||
this.hide()
|
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 = {
|
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 { Hooks } from '@server/lib/plugins/hooks'
|
||||||
import { UserRegistrationModel } from '@server/models/user/user-registration'
|
import { UserRegistrationModel } from '@server/models/user/user-registration'
|
||||||
import { pick } from '@shared/core-utils'
|
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 { auditLoggerFactory, UserAuditView } from '../../../helpers/audit-logger'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { CONFIG } from '../../../initializers/config'
|
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) {
|
async function acceptRegistration (req: express.Request, res: express.Response) {
|
||||||
const registration = res.locals.userRegistration
|
const registration = res.locals.userRegistration
|
||||||
|
const body: UserRegistrationUpdateState = req.body
|
||||||
|
|
||||||
const userToCreate = buildUser({
|
const userToCreate = buildUser({
|
||||||
username: registration.username,
|
username: registration.username,
|
||||||
|
@ -150,26 +158,31 @@ async function acceptRegistration (req: express.Request, res: express.Response)
|
||||||
|
|
||||||
registration.userId = user.id
|
registration.userId = user.id
|
||||||
registration.state = UserRegistrationState.ACCEPTED
|
registration.state = UserRegistrationState.ACCEPTED
|
||||||
registration.moderationResponse = req.body.moderationResponse
|
registration.moderationResponse = body.moderationResponse
|
||||||
|
|
||||||
await registration.save()
|
await registration.save()
|
||||||
|
|
||||||
logger.info('Registration of %s accepted', registration.username)
|
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)
|
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function rejectRegistration (req: express.Request, res: express.Response) {
|
async function rejectRegistration (req: express.Request, res: express.Response) {
|
||||||
const registration = res.locals.userRegistration
|
const registration = res.locals.userRegistration
|
||||||
|
const body: UserRegistrationUpdateState = req.body
|
||||||
|
|
||||||
registration.state = UserRegistrationState.REJECTED
|
registration.state = UserRegistrationState.REJECTED
|
||||||
registration.moderationResponse = req.body.moderationResponse
|
registration.moderationResponse = body.moderationResponse
|
||||||
|
|
||||||
await registration.save()
|
await registration.save()
|
||||||
|
|
||||||
Emailer.Instance.addUserRegistrationRequestProcessedJob(registration)
|
if (body.preventEmailDelivery !== true) {
|
||||||
|
Emailer.Instance.addUserRegistrationRequestProcessedJob(registration)
|
||||||
|
}
|
||||||
|
|
||||||
logger.info('Registration of %s rejected', registration.username)
|
logger.info('Registration of %s rejected', registration.username)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { body, param, query, ValidationChain } from 'express-validator'
|
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 { isRegistrationModerationResponseValid, isRegistrationReasonValid } from '@server/helpers/custom-validators/user-registration'
|
||||||
import { CONFIG } from '@server/initializers/config'
|
import { CONFIG } from '@server/initializers/config'
|
||||||
import { Hooks } from '@server/lib/plugins/hooks'
|
import { Hooks } from '@server/lib/plugins/hooks'
|
||||||
|
@ -91,6 +91,11 @@ const acceptOrRejectRegistrationValidator = [
|
||||||
body('moderationResponse')
|
body('moderationResponse')
|
||||||
.custom(isRegistrationModerationResponseValid),
|
.custom(isRegistrationModerationResponseValid),
|
||||||
|
|
||||||
|
body('preventEmailDelivery')
|
||||||
|
.optional()
|
||||||
|
.customSanitizer(toBooleanOrNull)
|
||||||
|
.custom(isBooleanValid).withMessage('Should have preventEmailDelivery boolean'),
|
||||||
|
|
||||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await checkRegistrationIdExist(req.params.registrationId, 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 () {
|
it('Should request a registration without a channel, that will conflict with an already existing channel', async function () {
|
||||||
let id1: number
|
let id1: number
|
||||||
let id2: number
|
let id2: number
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
export interface UserRegistrationUpdateState {
|
export interface UserRegistrationUpdateState {
|
||||||
moderationResponse: string
|
moderationResponse: string
|
||||||
|
preventEmailDelivery?: boolean
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { pick } from '@shared/core-utils'
|
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 { unwrapBody } from '../requests'
|
||||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||||
|
|
||||||
|
@ -47,35 +47,29 @@ export class RegistrationsCommand extends AbstractCommand {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
accept (options: OverrideCommandOptions & {
|
accept (options: OverrideCommandOptions & { id: number } & UserRegistrationUpdateState) {
|
||||||
id: number
|
const { id } = options
|
||||||
moderationResponse: string
|
|
||||||
}) {
|
|
||||||
const { id, moderationResponse } = options
|
|
||||||
const path = '/api/v1/users/registrations/' + id + '/accept'
|
const path = '/api/v1/users/registrations/' + id + '/accept'
|
||||||
|
|
||||||
return this.postBodyRequest({
|
return this.postBodyRequest({
|
||||||
...options,
|
...options,
|
||||||
|
|
||||||
path,
|
path,
|
||||||
fields: { moderationResponse },
|
fields: pick(options, [ 'moderationResponse', 'preventEmailDelivery' ]),
|
||||||
implicitToken: true,
|
implicitToken: true,
|
||||||
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
|
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
reject (options: OverrideCommandOptions & {
|
reject (options: OverrideCommandOptions & { id: number } & UserRegistrationUpdateState) {
|
||||||
id: number
|
const { id } = options
|
||||||
moderationResponse: string
|
|
||||||
}) {
|
|
||||||
const { id, moderationResponse } = options
|
|
||||||
const path = '/api/v1/users/registrations/' + id + '/reject'
|
const path = '/api/v1/users/registrations/' + id + '/reject'
|
||||||
|
|
||||||
return this.postBodyRequest({
|
return this.postBodyRequest({
|
||||||
...options,
|
...options,
|
||||||
|
|
||||||
path,
|
path,
|
||||||
fields: { moderationResponse },
|
fields: pick(options, [ 'moderationResponse', 'preventEmailDelivery' ]),
|
||||||
implicitToken: true,
|
implicitToken: true,
|
||||||
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
|
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
|
||||||
})
|
})
|
||||||
|
|
|
@ -7961,6 +7961,9 @@ components:
|
||||||
moderationResponse:
|
moderationResponse:
|
||||||
type: string
|
type: string
|
||||||
description: Moderation response to send to the user
|
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:
|
required:
|
||||||
- moderationResponse
|
- moderationResponse
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue