aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/users/me.ts22
-rw-r--r--server/helpers/custom-validators/users.ts10
-rw-r--r--server/initializers/migrations/0425-user-modals.ts40
-rw-r--r--server/middlewares/validators/users.ts7
-rw-r--r--server/models/account/user.ts58
-rw-r--r--server/tests/api/check-params/users.ts20
-rw-r--r--server/tests/api/users/users.ts21
7 files changed, 153 insertions, 25 deletions
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts
index 78e1e7fa3..fb1ddbc6d 100644
--- a/server/controllers/api/users/me.ts
+++ b/server/controllers/api/users/me.ts
@@ -127,7 +127,7 @@ async function getUserInformation (req: express.Request, res: express.Response)
127 // We did not load channels in res.locals.user 127 // We did not load channels in res.locals.user
128 const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username) 128 const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username)
129 129
130 return res.json(user.toFormattedJSON({})) 130 return res.json(user.toFormattedJSON())
131} 131}
132 132
133async function getUserVideoQuotaUsed (req: express.Request, res: express.Response) { 133async function getUserVideoQuotaUsed (req: express.Request, res: express.Response) {
@@ -178,6 +178,8 @@ async function updateMe (req: express.Request, res: express.Response) {
178 if (body.videosHistoryEnabled !== undefined) user.videosHistoryEnabled = body.videosHistoryEnabled 178 if (body.videosHistoryEnabled !== undefined) user.videosHistoryEnabled = body.videosHistoryEnabled
179 if (body.videoLanguages !== undefined) user.videoLanguages = body.videoLanguages 179 if (body.videoLanguages !== undefined) user.videoLanguages = body.videoLanguages
180 if (body.theme !== undefined) user.theme = body.theme 180 if (body.theme !== undefined) user.theme = body.theme
181 if (body.noInstanceConfigWarningModal !== undefined) user.noInstanceConfigWarningModal = body.noInstanceConfigWarningModal
182 if (body.noWelcomeModal !== undefined) user.noWelcomeModal = body.noWelcomeModal
181 183
182 if (body.email !== undefined) { 184 if (body.email !== undefined) {
183 if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { 185 if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
@@ -188,17 +190,19 @@ async function updateMe (req: express.Request, res: express.Response) {
188 } 190 }
189 } 191 }
190 192
191 await sequelizeTypescript.transaction(async t => { 193 if (body.displayName !== undefined || body.description !== undefined) {
192 const userAccount = await AccountModel.load(user.Account.id) 194 await sequelizeTypescript.transaction(async t => {
195 const userAccount = await AccountModel.load(user.Account.id, t)
193 196
194 await user.save({ transaction: t }) 197 await user.save({ transaction: t })
195 198
196 if (body.displayName !== undefined) userAccount.name = body.displayName 199 if (body.displayName !== undefined) userAccount.name = body.displayName
197 if (body.description !== undefined) userAccount.description = body.description 200 if (body.description !== undefined) userAccount.description = body.description
198 await userAccount.save({ transaction: t }) 201 await userAccount.save({ transaction: t })
199 202
200 await sendUpdateActor(userAccount, t) 203 await sendUpdateActor(userAccount, t)
201 }) 204 })
205 }
202 206
203 if (sendVerificationEmail === true) { 207 if (sendVerificationEmail === true) {
204 await sendVerifyUserEmail(user, true) 208 await sendVerifyUserEmail(user, true)
diff --git a/server/helpers/custom-validators/users.ts b/server/helpers/custom-validators/users.ts
index c56ae14ef..68e84d9eb 100644
--- a/server/helpers/custom-validators/users.ts
+++ b/server/helpers/custom-validators/users.ts
@@ -65,6 +65,14 @@ function isUserBlockedValid (value: any) {
65 return isBooleanValid(value) 65 return isBooleanValid(value)
66} 66}
67 67
68function isNoInstanceConfigWarningModal (value: any) {
69 return isBooleanValid(value)
70}
71
72function isNoWelcomeModal (value: any) {
73 return isBooleanValid(value)
74}
75
68function isUserBlockedReasonValid (value: any) { 76function isUserBlockedReasonValid (value: any) {
69 return value === null || (exists(value) && validator.isLength(value, CONSTRAINTS_FIELDS.USERS.BLOCKED_REASON)) 77 return value === null || (exists(value) && validator.isLength(value, CONSTRAINTS_FIELDS.USERS.BLOCKED_REASON))
70} 78}
@@ -100,5 +108,7 @@ export {
100 isUserAutoPlayVideoValid, 108 isUserAutoPlayVideoValid,
101 isUserDisplayNameValid, 109 isUserDisplayNameValid,
102 isUserDescriptionValid, 110 isUserDescriptionValid,
111 isNoInstanceConfigWarningModal,
112 isNoWelcomeModal,
103 isAvatarFile 113 isAvatarFile
104} 114}
diff --git a/server/initializers/migrations/0425-user-modals.ts b/server/initializers/migrations/0425-user-modals.ts
new file mode 100644
index 000000000..5c2aa85b5
--- /dev/null
+++ b/server/initializers/migrations/0425-user-modals.ts
@@ -0,0 +1,40 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction,
5 queryInterface: Sequelize.QueryInterface,
6 sequelize: Sequelize.Sequelize,
7 db: any
8}): Promise<void> {
9 {
10 const data = {
11 type: Sequelize.BOOLEAN,
12 allowNull: false,
13 defaultValue: false
14 }
15
16 await utils.queryInterface.addColumn('user', 'noInstanceConfigWarningModal', data)
17 }
18
19 {
20 const data = {
21 type: Sequelize.BOOLEAN,
22 allowNull: false,
23 defaultValue: true
24 }
25
26 await utils.queryInterface.addColumn('user', 'noWelcomeModal', data)
27 data.defaultValue = false
28
29 await utils.queryInterface.changeColumn('user', 'noWelcomeModal', data)
30 }
31}
32
33function down (options) {
34 throw new Error('Not implemented.')
35}
36
37export {
38 up,
39 down
40}
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index 26f43cec7..544db76d7 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -4,6 +4,7 @@ import { body, param } from 'express-validator'
4import { omit } from 'lodash' 4import { omit } from 'lodash'
5import { isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' 5import { isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
6import { 6import {
7 isNoInstanceConfigWarningModal, isNoWelcomeModal,
7 isUserAdminFlagsValid, 8 isUserAdminFlagsValid,
8 isUserAutoPlayVideoValid, 9 isUserAutoPlayVideoValid,
9 isUserBlockedReasonValid, 10 isUserBlockedReasonValid,
@@ -216,6 +217,12 @@ const usersUpdateMeValidator = [
216 body('theme') 217 body('theme')
217 .optional() 218 .optional()
218 .custom(v => isThemeNameValid(v) && isThemeRegistered(v)).withMessage('Should have a valid theme'), 219 .custom(v => isThemeNameValid(v) && isThemeRegistered(v)).withMessage('Should have a valid theme'),
220 body('noInstanceConfigWarningModal')
221 .optional()
222 .custom(v => isNoInstanceConfigWarningModal(v)).withMessage('Should have a valid noInstanceConfigWarningModal boolean'),
223 body('noWelcomeModal')
224 .optional()
225 .custom(v => isNoWelcomeModal(v)).withMessage('Should have a valid noWelcomeModal boolean'),
219 226
220 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 227 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
221 logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') }) 228 logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') })
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index 616dd603c..451e1fd6b 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -22,6 +22,7 @@ import {
22import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' 22import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared'
23import { User, UserRole } from '../../../shared/models/users' 23import { User, UserRole } from '../../../shared/models/users'
24import { 24import {
25 isNoInstanceConfigWarningModal,
25 isUserAdminFlagsValid, 26 isUserAdminFlagsValid,
26 isUserAutoPlayVideoValid, 27 isUserAutoPlayVideoValid,
27 isUserBlockedReasonValid, 28 isUserBlockedReasonValid,
@@ -35,7 +36,8 @@ import {
35 isUserVideoQuotaDailyValid, 36 isUserVideoQuotaDailyValid,
36 isUserVideoQuotaValid, 37 isUserVideoQuotaValid,
37 isUserVideosHistoryEnabledValid, 38 isUserVideosHistoryEnabledValid,
38 isUserWebTorrentEnabledValid 39 isUserWebTorrentEnabledValid,
40 isNoWelcomeModal
39} from '../../helpers/custom-validators/users' 41} from '../../helpers/custom-validators/users'
40import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' 42import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto'
41import { OAuthTokenModel } from '../oauth/oauth-token' 43import { OAuthTokenModel } from '../oauth/oauth-token'
@@ -203,6 +205,24 @@ export class UserModel extends Model<UserModel> {
203 @Column 205 @Column
204 theme: string 206 theme: string
205 207
208 @AllowNull(false)
209 @Default(false)
210 @Is(
211 'UserNoInstanceConfigWarningModal',
212 value => throwIfNotValid(value, isNoInstanceConfigWarningModal, 'no instance config warning modal')
213 )
214 @Column
215 noInstanceConfigWarningModal: boolean
216
217 @AllowNull(false)
218 @Default(false)
219 @Is(
220 'UserNoInstanceConfigWarningModal',
221 value => throwIfNotValid(value, isNoWelcomeModal, 'no welcome modal')
222 )
223 @Column
224 noWelcomeModal: boolean
225
206 @CreatedAt 226 @CreatedAt
207 createdAt: Date 227 createdAt: Date
208 228
@@ -560,40 +580,52 @@ export class UserModel extends Model<UserModel> {
560 return comparePassword(password, this.password) 580 return comparePassword(password, this.password)
561 } 581 }
562 582
563 toSummaryJSON
564
565 toFormattedJSON (this: MUserFormattable, parameters: { withAdminFlags?: boolean } = {}): User { 583 toFormattedJSON (this: MUserFormattable, parameters: { withAdminFlags?: boolean } = {}): User {
566 const videoQuotaUsed = this.get('videoQuotaUsed') 584 const videoQuotaUsed = this.get('videoQuotaUsed')
567 const videoQuotaUsedDaily = this.get('videoQuotaUsedDaily') 585 const videoQuotaUsedDaily = this.get('videoQuotaUsedDaily')
568 586
569 const json = { 587 const json: User = {
570 id: this.id, 588 id: this.id,
571 username: this.username, 589 username: this.username,
572 email: this.email, 590 email: this.email,
591 theme: getThemeOrDefault(this.theme, DEFAULT_USER_THEME_NAME),
592
573 pendingEmail: this.pendingEmail, 593 pendingEmail: this.pendingEmail,
574 emailVerified: this.emailVerified, 594 emailVerified: this.emailVerified,
595
575 nsfwPolicy: this.nsfwPolicy, 596 nsfwPolicy: this.nsfwPolicy,
576 webTorrentEnabled: this.webTorrentEnabled, 597 webTorrentEnabled: this.webTorrentEnabled,
577 videosHistoryEnabled: this.videosHistoryEnabled, 598 videosHistoryEnabled: this.videosHistoryEnabled,
578 autoPlayVideo: this.autoPlayVideo, 599 autoPlayVideo: this.autoPlayVideo,
579 videoLanguages: this.videoLanguages, 600 videoLanguages: this.videoLanguages,
601
580 role: this.role, 602 role: this.role,
581 theme: getThemeOrDefault(this.theme, DEFAULT_USER_THEME_NAME),
582 roleLabel: USER_ROLE_LABELS[ this.role ], 603 roleLabel: USER_ROLE_LABELS[ this.role ],
604
583 videoQuota: this.videoQuota, 605 videoQuota: this.videoQuota,
584 videoQuotaDaily: this.videoQuotaDaily, 606 videoQuotaDaily: this.videoQuotaDaily,
585 createdAt: this.createdAt, 607 videoQuotaUsed: videoQuotaUsed !== undefined
608 ? parseInt(videoQuotaUsed + '', 10)
609 : undefined,
610 videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined
611 ? parseInt(videoQuotaUsedDaily + '', 10)
612 : undefined,
613
614 noInstanceConfigWarningModal: this.noInstanceConfigWarningModal,
615 noWelcomeModal: this.noWelcomeModal,
616
586 blocked: this.blocked, 617 blocked: this.blocked,
587 blockedReason: this.blockedReason, 618 blockedReason: this.blockedReason,
619
588 account: this.Account.toFormattedJSON(), 620 account: this.Account.toFormattedJSON(),
589 notificationSettings: this.NotificationSetting ? this.NotificationSetting.toFormattedJSON() : undefined, 621
622 notificationSettings: this.NotificationSetting
623 ? this.NotificationSetting.toFormattedJSON()
624 : undefined,
625
590 videoChannels: [], 626 videoChannels: [],
591 videoQuotaUsed: videoQuotaUsed !== undefined 627
592 ? parseInt(videoQuotaUsed + '', 10) 628 createdAt: this.createdAt
593 : undefined,
594 videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined
595 ? parseInt(videoQuotaUsedDaily + '', 10)
596 : undefined
597 } 629 }
598 630
599 if (parameters.withAdminFlags) { 631 if (parameters.withAdminFlags) {
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index 939b919ed..55094795c 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -476,6 +476,22 @@ describe('Test users API validators', function () {
476 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 476 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
477 }) 477 })
478 478
479 it('Should fail with an invalid noInstanceConfigWarningModal attribute', async function () {
480 const fields = {
481 noInstanceConfigWarningModal: -1
482 }
483
484 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
485 })
486
487 it('Should fail with an invalid noWelcomeModal attribute', async function () {
488 const fields = {
489 noWelcomeModal: -1
490 }
491
492 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
493 })
494
479 it('Should succeed to change password with the correct params', async function () { 495 it('Should succeed to change password with the correct params', async function () {
480 const fields = { 496 const fields = {
481 currentPassword: 'my super password', 497 currentPassword: 'my super password',
@@ -483,7 +499,9 @@ describe('Test users API validators', function () {
483 nsfwPolicy: 'blur', 499 nsfwPolicy: 'blur',
484 autoPlayVideo: false, 500 autoPlayVideo: false,
485 email: 'super_email@example.com', 501 email: 'super_email@example.com',
486 theme: 'default' 502 theme: 'default',
503 noInstanceConfigWarningModal: true,
504 noWelcomeModal: true
487 } 505 }
488 506
489 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 }) 507 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 })
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
index 3a3fabb4c..95b1bb626 100644
--- a/server/tests/api/users/users.ts
+++ b/server/tests/api/users/users.ts
@@ -442,7 +442,7 @@ describe('Test users', function () {
442 url: server.url, 442 url: server.url,
443 accessToken: accessTokenUser, 443 accessToken: accessTokenUser,
444 currentPassword: 'super password', 444 currentPassword: 'super password',
445 newPassword: 'new password' 445 password: 'new password'
446 }) 446 })
447 user.password = 'new password' 447 user.password = 'new password'
448 448
@@ -543,7 +543,7 @@ describe('Test users', function () {
543 }) 543 })
544 544
545 const res = await getMyUserInformation(server.url, accessTokenUser) 545 const res = await getMyUserInformation(server.url, accessTokenUser)
546 const user = res.body 546 const user: User = res.body
547 547
548 expect(user.username).to.equal('user_1') 548 expect(user.username).to.equal('user_1')
549 expect(user.email).to.equal('updated@example.com') 549 expect(user.email).to.equal('updated@example.com')
@@ -552,6 +552,8 @@ describe('Test users', function () {
552 expect(user.id).to.be.a('number') 552 expect(user.id).to.be.a('number')
553 expect(user.account.displayName).to.equal('new display name') 553 expect(user.account.displayName).to.equal('new display name')
554 expect(user.account.description).to.equal('my super description updated') 554 expect(user.account.description).to.equal('my super description updated')
555 expect(user.noWelcomeModal).to.be.false
556 expect(user.noInstanceConfigWarningModal).to.be.false
555 }) 557 })
556 558
557 it('Should be able to update my theme', async function () { 559 it('Should be able to update my theme', async function () {
@@ -568,6 +570,21 @@ describe('Test users', function () {
568 expect(body.theme).to.equal(theme) 570 expect(body.theme).to.equal(theme)
569 } 571 }
570 }) 572 })
573
574 it('Should be able to update my modal preferences', async function () {
575 await updateMyUser({
576 url: server.url,
577 accessToken: accessTokenUser,
578 noInstanceConfigWarningModal: true,
579 noWelcomeModal: true
580 })
581
582 const res = await getMyUserInformation(server.url, accessTokenUser)
583 const user: User = res.body
584
585 expect(user.noWelcomeModal).to.be.true
586 expect(user.noInstanceConfigWarningModal).to.be.true
587 })
571 }) 588 })
572 589
573 describe('Updating another user', function () { 590 describe('Updating another user', function () {