diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/controllers/api/users/me.ts | 22 | ||||
-rw-r--r-- | server/helpers/custom-validators/users.ts | 10 | ||||
-rw-r--r-- | server/initializers/migrations/0425-user-modals.ts | 40 | ||||
-rw-r--r-- | server/middlewares/validators/users.ts | 7 | ||||
-rw-r--r-- | server/models/account/user.ts | 58 | ||||
-rw-r--r-- | server/tests/api/check-params/users.ts | 20 | ||||
-rw-r--r-- | server/tests/api/users/users.ts | 21 |
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 | ||
133 | async function getUserVideoQuotaUsed (req: express.Request, res: express.Response) { | 133 | async 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 | ||
68 | function isNoInstanceConfigWarningModal (value: any) { | ||
69 | return isBooleanValid(value) | ||
70 | } | ||
71 | |||
72 | function isNoWelcomeModal (value: any) { | ||
73 | return isBooleanValid(value) | ||
74 | } | ||
75 | |||
68 | function isUserBlockedReasonValid (value: any) { | 76 | function 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 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async 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 | |||
33 | function down (options) { | ||
34 | throw new Error('Not implemented.') | ||
35 | } | ||
36 | |||
37 | export { | ||
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' | |||
4 | import { omit } from 'lodash' | 4 | import { omit } from 'lodash' |
5 | import { isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' | 5 | import { isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' |
6 | import { | 6 | import { |
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 { | |||
22 | import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' | 22 | import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' |
23 | import { User, UserRole } from '../../../shared/models/users' | 23 | import { User, UserRole } from '../../../shared/models/users' |
24 | import { | 24 | import { |
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' |
40 | import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' | 42 | import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' |
41 | import { OAuthTokenModel } from '../oauth/oauth-token' | 43 | import { 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 () { |