From 7efe153b0bc23e596d5019b9fb3e3e32b6cfeccd Mon Sep 17 00:00:00 2001 From: =?utf8?q?Andr=C3=A9as=20Livet?= Date: Tue, 19 Dec 2017 10:45:49 +0100 Subject: [PATCH] Enh #106 : Add an autoPlayVideo user attribute (#159) Warning : I was not able to run the tests on my machine. It uses a different approach to handle databse connexion and didn't find where to configure it... - create a migration file to add a boolean column in user table - add autoPlayVideo attribute everywhere it is needed (both on client and server side) - add tests - add a way to configure this attribute in account-settings - use the attribute in video-watch component to actually autoplay or not the video --- .../account-details.component.html | 6 +++++ .../account-details.component.ts | 7 +++-- .../account-settings.component.html | 2 +- client/src/app/core/auth/auth-user.model.ts | 8 ++++-- client/src/app/core/auth/auth.service.ts | 4 +++ client/src/app/shared/users/user.model.ts | 6 +++++ .../+video-watch/video-watch.component.ts | 4 +-- server/controllers/api/users.ts | 3 +++ server/helpers/custom-validators/users.ts | 13 +++++++-- server/initializers/constants.ts | 2 +- .../migrations/0130-user-autoplay-video.ts | 27 +++++++++++++++++++ server/middlewares/validators/users.ts | 2 ++ server/models/account/user.ts | 9 ++++++- server/tests/api/check-params/users.ts | 9 +++++++ server/tests/api/users.ts | 9 +++++++ server/tests/utils/users.ts | 4 ++- shared/models/users/user-update-me.model.ts | 1 + shared/models/users/user.model.ts | 1 + 18 files changed, 105 insertions(+), 12 deletions(-) create mode 100644 server/initializers/migrations/0130-user-autoplay-video.ts diff --git a/client/src/app/account/account-settings/account-details/account-details.component.html b/client/src/app/account/account-settings/account-details/account-details.component.html index bc18b39b4..593b87e29 100644 --- a/client/src/app/account/account-settings/account-details/account-details.component.html +++ b/client/src/app/account/account-settings/account-details/account-details.component.html @@ -9,6 +9,12 @@
{{ formErrors['displayNSFW'] }}
+
+ + diff --git a/client/src/app/account/account-settings/account-details/account-details.component.ts b/client/src/app/account/account-settings/account-details/account-details.component.ts index d835c53e5..b8c19d8d6 100644 --- a/client/src/app/account/account-settings/account-details/account-details.component.ts +++ b/client/src/app/account/account-settings/account-details/account-details.component.ts @@ -31,7 +31,8 @@ export class AccountDetailsComponent extends FormReactive implements OnInit { buildForm () { this.form = this.formBuilder.group({ - displayNSFW: [ this.user.displayNSFW ] + displayNSFW: [ this.user.displayNSFW ], + autoPlayVideo: [ this.user.autoPlayVideo ] }) this.form.valueChanges.subscribe(data => this.onValueChanged(data)) @@ -43,8 +44,10 @@ export class AccountDetailsComponent extends FormReactive implements OnInit { updateDetails () { const displayNSFW = this.form.value['displayNSFW'] + const autoPlayVideo = this.form.value['autoPlayVideo'] const details: UserUpdateMe = { - displayNSFW + displayNSFW, + autoPlayVideo } this.error = null diff --git a/client/src/app/account/account-settings/account-settings.component.html b/client/src/app/account/account-settings/account-settings.component.html index c0a74cc47..f14eadd49 100644 --- a/client/src/app/account/account-settings/account-settings.component.html +++ b/client/src/app/account/account-settings/account-settings.component.html @@ -11,5 +11,5 @@
Account settings
-
Filtering
+
Videos
diff --git a/client/src/app/core/auth/auth-user.model.ts b/client/src/app/core/auth/auth-user.model.ts index 7b6c8816f..9ad275392 100644 --- a/client/src/app/core/auth/auth-user.model.ts +++ b/client/src/app/core/auth/auth-user.model.ts @@ -69,7 +69,8 @@ export class AuthUser extends User { ROLE: 'role', EMAIL: 'email', USERNAME: 'username', - DISPLAY_NSFW: 'display_nsfw' + DISPLAY_NSFW: 'display_nsfw', + AUTO_PLAY_VIDEO: 'auto_play_video' } tokens: Tokens @@ -83,7 +84,8 @@ export class AuthUser extends User { username: localStorage.getItem(this.KEYS.USERNAME), email: localStorage.getItem(this.KEYS.EMAIL), role: parseInt(localStorage.getItem(this.KEYS.ROLE), 10) as UserRole, - displayNSFW: localStorage.getItem(this.KEYS.DISPLAY_NSFW) === 'true' + displayNSFW: localStorage.getItem(this.KEYS.DISPLAY_NSFW) === 'true', + autoPlayVideo: localStorage.getItem(this.KEYS.AUTO_PLAY_VIDEO) === 'true' }, Tokens.load() ) @@ -97,6 +99,7 @@ export class AuthUser extends User { localStorage.removeItem(this.KEYS.ID) localStorage.removeItem(this.KEYS.ROLE) localStorage.removeItem(this.KEYS.DISPLAY_NSFW) + localStorage.removeItem(this.KEYS.AUTO_PLAY_VIDEO) localStorage.removeItem(this.KEYS.EMAIL) Tokens.flush() } @@ -133,6 +136,7 @@ export class AuthUser extends User { localStorage.setItem(AuthUser.KEYS.EMAIL, this.email) localStorage.setItem(AuthUser.KEYS.ROLE, this.role.toString()) localStorage.setItem(AuthUser.KEYS.DISPLAY_NSFW, JSON.stringify(this.displayNSFW)) + localStorage.setItem(AuthUser.KEYS.AUTO_PLAY_VIDEO, JSON.stringify(this.autoPlayVideo)) this.tokens.save() } } diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index e2b8b6ba5..37264a8ad 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts @@ -33,6 +33,7 @@ interface UserLoginWithUserInformation extends UserLogin { id: number role: UserRole displayNSFW: boolean + autoPlayVideo: boolean email: string videoQuota: number account: Account @@ -191,6 +192,7 @@ export class AuthService { .subscribe( res => { this.user.displayNSFW = res.displayNSFW + this.user.autoPlayVideo = res.autoPlayVideo this.user.role = res.role this.user.videoChannels = res.videoChannels this.user.account = res.account @@ -212,6 +214,7 @@ export class AuthService { id: res.id, role: res.role, displayNSFW: res.displayNSFW, + autoPlayVideo: res.autoPlayVideo, email: res.email, videoQuota: res.videoQuota, account: res.account, @@ -230,6 +233,7 @@ export class AuthService { role: obj.role, email: obj.email, displayNSFW: obj.displayNSFW, + autoPlayVideo: obj.autoPlayVideo, videoQuota: obj.videoQuota, videoChannels: obj.videoChannels, account: obj.account diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts index b4d13f37c..7a962ae3e 100644 --- a/client/src/app/shared/users/user.model.ts +++ b/client/src/app/shared/users/user.model.ts @@ -8,6 +8,7 @@ export type UserConstructorHash = { role: UserRole, videoQuota?: number, displayNSFW?: boolean, + autoPlayVideo?: boolean, createdAt?: Date, account?: Account, videoChannels?: VideoChannel[] @@ -18,6 +19,7 @@ export class User implements UserServerModel { email: string role: UserRole displayNSFW: boolean + autoPlayVideo: boolean videoQuota: number account: Account videoChannels: VideoChannel[] @@ -42,6 +44,10 @@ export class User implements UserServerModel { this.displayNSFW = hash.displayNSFW } + if (hash.autoPlayVideo !== undefined) { + this.autoPlayVideo = hash.autoPlayVideo + } + if (hash.createdAt !== undefined) { this.createdAt = hash.createdAt } diff --git a/client/src/app/videos/+video-watch/video-watch.component.ts b/client/src/app/videos/+video-watch/video-watch.component.ts index 5e4823c9c..e35b02f3f 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -290,12 +290,12 @@ export class VideoWatchComponent implements OnInit, OnDestroy { const videojsOptions = { controls: true, - autoplay: true, + autoplay: this.user.autoPlayVideo, plugins: { peertube: { videoFiles: this.video.files, playerElement: this.playerElement, - autoplay: true, + autoplay: this.user.autoPlayVideo, peerTubeLink: false } } diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts index d6c0e67f9..995542604 100644 --- a/server/controllers/api/users.ts +++ b/server/controllers/api/users.ts @@ -134,6 +134,7 @@ async function createUser (req: express.Request) { password: body.password, email: body.email, displayNSFW: false, + autoPlayVideo: true, role: body.role, videoQuota: body.videoQuota }) @@ -162,6 +163,7 @@ async function registerUser (req: express.Request) { password: body.password, email: body.email, displayNSFW: false, + autoPlayVideo: true, role: UserRole.USER, videoQuota: CONFIG.USER.VIDEO_QUOTA }) @@ -219,6 +221,7 @@ async function updateMe (req: express.Request, res: express.Response, next: expr if (body.password !== undefined) user.password = body.password if (body.email !== undefined) user.email = body.email if (body.displayNSFW !== undefined) user.displayNSFW = body.displayNSFW + if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo await user.save() diff --git a/server/helpers/custom-validators/users.ts b/server/helpers/custom-validators/users.ts index b5b5642d6..159c2a700 100644 --- a/server/helpers/custom-validators/users.ts +++ b/server/helpers/custom-validators/users.ts @@ -21,10 +21,18 @@ function isUserUsernameValid (value: string) { return exists(value) && validator.matches(value, new RegExp(`^[a-z0-9._]{${min},${max}}$`)) } -function isUserDisplayNSFWValid (value: any) { +function isBoolean (value: any) { return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value)) } +function isUserDisplayNSFWValid (value: any) { + return isBoolean(value) +} + +function isUserAutoPlayVideoValid (value: any) { + return isBoolean(value) +} + function isUserRoleValid (value: any) { return exists(value) && validator.isInt('' + value) && UserRole[value] !== undefined } @@ -36,5 +44,6 @@ export { isUserRoleValid, isUserVideoQuotaValid, isUserUsernameValid, - isUserDisplayNSFWValid + isUserDisplayNSFWValid, + isUserAutoPlayVideoValid } diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 341086bd6..ff322730f 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -8,7 +8,7 @@ import { isTestInstance, root } from '../helpers/core-utils' // --------------------------------------------------------------------------- -const LAST_MIGRATION_VERSION = 125 +const LAST_MIGRATION_VERSION = 130 // --------------------------------------------------------------------------- diff --git a/server/initializers/migrations/0130-user-autoplay-video.ts b/server/initializers/migrations/0130-user-autoplay-video.ts new file mode 100644 index 000000000..9f6878e39 --- /dev/null +++ b/server/initializers/migrations/0130-user-autoplay-video.ts @@ -0,0 +1,27 @@ +import * as Sequelize from 'sequelize' +import * as Promise from 'bluebird' + +function up (utils: { + transaction: Sequelize.Transaction, + queryInterface: Sequelize.QueryInterface, + sequelize: Sequelize.Sequelize +}): Promise { + const q = utils.queryInterface + + const data = { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: true + } + + return q.addColumn('user', 'autoPlayVideo', data) +} + +function down (options) { + throw new Error('Not implemented.') +} + +export { + up, + down +} diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 920176d07..a6fdbe268 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts @@ -5,6 +5,7 @@ import { isSignupAllowed, logger } from '../../helpers' import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' import { isUserDisplayNSFWValid, + isUserAutoPlayVideoValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid, @@ -86,6 +87,7 @@ const usersUpdateMeValidator = [ body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'), body('email').optional().isEmail().withMessage('Should have a valid email attribute'), body('displayNSFW').optional().custom(isUserDisplayNSFWValid).withMessage('Should have a valid display Not Safe For Work attribute'), + body('autoPlayVideo').optional().custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'), (req: express.Request, res: express.Response, next: express.NextFunction) => { // TODO: Add old password verification diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 26f04dcb5..70ed61e07 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts @@ -20,7 +20,7 @@ import { } from '../../helpers' import { isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid, - isUserVideoQuotaValid + isUserVideoQuotaValid, isUserAutoPlayVideoValid } from '../../helpers/custom-validators/users' import { OAuthTokenModel } from '../oauth/oauth-token' import { getSort, throwIfNotValid } from '../utils' @@ -82,6 +82,12 @@ export class UserModel extends Model { @Column displayNSFW: boolean + @AllowNull(false) + @Default(true) + @Is('UserAutoPlayVideo', value => throwIfNotValid(value, isUserAutoPlayVideoValid, 'auto play video boolean')) + @Column + autoPlayVideo: boolean + @AllowNull(false) @Is('UserRole', value => throwIfNotValid(value, isUserRoleValid, 'role')) @Column @@ -223,6 +229,7 @@ export class UserModel extends Model { username: this.username, email: this.email, displayNSFW: this.displayNSFW, + autoPlayVideo: this.autoPlayVideo, role: this.role, roleLabel: USER_ROLE_LABELS[ this.role ], videoQuota: this.videoQuota, diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts index 1e3533bf3..72488e5c4 100644 --- a/server/tests/api/check-params/users.ts +++ b/server/tests/api/check-params/users.ts @@ -350,6 +350,14 @@ describe('Test users API validators', function () { await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) }) + it('Should fail with an invalid autoPlayVideo attribute', async function () { + const fields = { + autoPlayVideo: -1 + } + + await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) + }) + it('Should fail with an non authenticated user', async function () { const fields = { password: 'my super password' @@ -362,6 +370,7 @@ describe('Test users API validators', function () { const fields = { password: 'my super password', displayNSFW: true, + autoPlayVideo: false, email: 'super_email@example.com' } diff --git a/server/tests/api/users.ts b/server/tests/api/users.ts index b3163b1e1..67e4cc8c6 100644 --- a/server/tests/api/users.ts +++ b/server/tests/api/users.ts @@ -415,6 +415,15 @@ describe('Test users', function () { .a('number') }) + it('Should be able to change the autoPlayVideo attribute', async function () { + await updateMyUser(server.url, accessTokenUser, undefined, undefined, undefined, false) + + const res = await getMyUserInformation(server.url, accessTokenUser) + const user = res.body + + expect(user.autoPlayVideo).to.be.false + }) + it('Should be able to change the email display attribute', async function () { await updateMyUser(server.url, accessTokenUser, undefined, undefined, 'updated@example.com') diff --git a/server/tests/utils/users.ts b/server/tests/utils/users.ts index ce04b9d96..a37d84ab4 100644 --- a/server/tests/utils/users.ts +++ b/server/tests/utils/users.ts @@ -111,12 +111,14 @@ function removeUser (url: string, userId: number, accessToken: string, expectedS .expect(expectedStatus) } -function updateMyUser (url: string, accessToken: string, newPassword: string, displayNSFW?: boolean, email?: string) { +function updateMyUser (url: string, accessToken: string, newPassword: string, displayNSFW?: boolean, + email?: string, autoPlayVideo?: boolean) { const path = '/api/v1/users/me' const toSend = {} if (newPassword !== undefined && newPassword !== null) toSend['password'] = newPassword if (displayNSFW !== undefined && displayNSFW !== null) toSend['displayNSFW'] = displayNSFW + if (autoPlayVideo !== undefined && autoPlayVideo !== null) toSend['autoPlayVideo'] = autoPlayVideo if (email !== undefined && email !== null) toSend['email'] = email return request(url) diff --git a/shared/models/users/user-update-me.model.ts b/shared/models/users/user-update-me.model.ts index 0ee41a79b..83417a7bd 100644 --- a/shared/models/users/user-update-me.model.ts +++ b/shared/models/users/user-update-me.model.ts @@ -1,5 +1,6 @@ export interface UserUpdateMe { displayNSFW?: boolean + autoPlayVideo?: boolean email?: string password?: string } diff --git a/shared/models/users/user.model.ts b/shared/models/users/user.model.ts index 4b17881e5..f2b43d371 100644 --- a/shared/models/users/user.model.ts +++ b/shared/models/users/user.model.ts @@ -7,6 +7,7 @@ export interface User { username: string email: string displayNSFW: boolean + autoPlayVideo: boolean role: UserRole videoQuota: number createdAt: Date -- 2.25.1