diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html index 714a3af15..df40bba9f 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html @@ -62,6 +62,22 @@ +
+ + + +
+ +
+
+ {{ formErrors.instanceDefaultNSFWPolicy }} +
+
+
Cache
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts index d73ee71e4..2ab371cbb 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts @@ -48,6 +48,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { instanceDescription: '', instanceTerms: '', instanceDefaultClientRoute: '', + instanceDefaultNSFWPolicy: '', cachePreviewsSize: '', signupLimit: '', adminEmail: '', @@ -90,6 +91,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { instanceDescription: [ '' ], instanceTerms: [ '' ], instanceDefaultClientRoute: [ '' ], + instanceDefaultNSFWPolicy: [ '' ], cachePreviewsSize: [ '', CACHE_PREVIEWS_SIZE.VALIDATORS ], signupEnabled: [ ], signupLimit: [ '', SIGNUP_LIMIT.VALIDATORS ], @@ -167,6 +169,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { description: this.form.value['instanceDescription'], terms: this.form.value['instanceTerms'], defaultClientRoute: this.form.value['instanceDefaultClientRoute'], + defaultNSFWPolicy: this.form.value['instanceDefaultNSFWPolicy'], customizations: { javascript: this.form.value['customizationJavascript'], css: this.form.value['customizationCSS'] @@ -224,6 +227,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { instanceDescription: this.customConfig.instance.description, instanceTerms: this.customConfig.instance.terms, instanceDefaultClientRoute: this.customConfig.instance.defaultClientRoute, + instanceDefaultNSFWPolicy: this.customConfig.instance.defaultNSFWPolicy, cachePreviewsSize: this.customConfig.cache.previews.size, signupEnabled: this.customConfig.signup.enabled, signupLimit: this.customConfig.signup.limit, 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 8f1475a4d..9dcc66a75 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 @@ -1,11 +1,18 @@
- - - + + + +
+ +
+
+ {{ formErrors.nsfwPolicy }} +
diff --git a/client/src/app/account/account-settings/account-details/account-details.component.scss b/client/src/app/account/account-settings/account-details/account-details.component.scss index 4e8dfde1d..ed59e4689 100644 --- a/client/src/app/account/account-settings/account-details/account-details.component.scss +++ b/client/src/app/account/account-settings/account-details/account-details.component.scss @@ -12,3 +12,9 @@ input[type=submit] { display: block; margin-top: 15px; } + +.peertube-select-container { + @include peertube-select-container(340px); + + margin-bottom: 30px; +} \ No newline at end of file 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 917f31651..de213717e 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 @@ -29,7 +29,7 @@ export class AccountDetailsComponent extends FormReactive implements OnInit { buildForm () { this.form = this.formBuilder.group({ - displayNSFW: [ this.user.displayNSFW ], + nsfwPolicy: [ this.user.nsfwPolicy ], autoPlayVideo: [ this.user.autoPlayVideo ] }) @@ -41,10 +41,10 @@ export class AccountDetailsComponent extends FormReactive implements OnInit { } updateDetails () { - const displayNSFW = this.form.value['displayNSFW'] + const nsfwPolicy = this.form.value['nsfwPolicy'] const autoPlayVideo = this.form.value['autoPlayVideo'] const details: UserUpdateMe = { - displayNSFW, + nsfwPolicy, autoPlayVideo } diff --git a/client/src/app/core/auth/auth-user.model.ts b/client/src/app/core/auth/auth-user.model.ts index 366eea110..60fe57899 100644 --- a/client/src/app/core/auth/auth-user.model.ts +++ b/client/src/app/core/auth/auth-user.model.ts @@ -3,6 +3,7 @@ import { UserRight } from '../../../../../shared/models/users/user-right.enum' // Do not use the barrel (dependency loop) import { hasUserRight, UserRole } from '../../../../../shared/models/users/user-role' import { User, UserConstructorHash } from '../../shared/users/user.model' +import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type' export type TokenOptions = { accessToken: string @@ -70,7 +71,7 @@ export class AuthUser extends User { ROLE: 'role', EMAIL: 'email', USERNAME: 'username', - DISPLAY_NSFW: 'display_nsfw', + DEFAULT_NSFW_POLICY: 'nsfw_policy', AUTO_PLAY_VIDEO: 'auto_play_video' } @@ -85,7 +86,7 @@ export class AuthUser extends User { username: peertubeLocalStorage.getItem(this.KEYS.USERNAME), email: peertubeLocalStorage.getItem(this.KEYS.EMAIL), role: parseInt(peertubeLocalStorage.getItem(this.KEYS.ROLE), 10) as UserRole, - displayNSFW: peertubeLocalStorage.getItem(this.KEYS.DISPLAY_NSFW) === 'true', + nsfwPolicy: peertubeLocalStorage.getItem(this.KEYS.DEFAULT_NSFW_POLICY) as NSFWPolicyType, autoPlayVideo: peertubeLocalStorage.getItem(this.KEYS.AUTO_PLAY_VIDEO) === 'true' }, Tokens.load() @@ -99,7 +100,7 @@ export class AuthUser extends User { peertubeLocalStorage.removeItem(this.KEYS.USERNAME) peertubeLocalStorage.removeItem(this.KEYS.ID) peertubeLocalStorage.removeItem(this.KEYS.ROLE) - peertubeLocalStorage.removeItem(this.KEYS.DISPLAY_NSFW) + peertubeLocalStorage.removeItem(this.KEYS.DEFAULT_NSFW_POLICY) peertubeLocalStorage.removeItem(this.KEYS.AUTO_PLAY_VIDEO) peertubeLocalStorage.removeItem(this.KEYS.EMAIL) Tokens.flush() @@ -136,7 +137,7 @@ export class AuthUser extends User { peertubeLocalStorage.setItem(AuthUser.KEYS.USERNAME, this.username) peertubeLocalStorage.setItem(AuthUser.KEYS.EMAIL, this.email) peertubeLocalStorage.setItem(AuthUser.KEYS.ROLE, this.role.toString()) - peertubeLocalStorage.setItem(AuthUser.KEYS.DISPLAY_NSFW, JSON.stringify(this.displayNSFW)) + peertubeLocalStorage.setItem(AuthUser.KEYS.DEFAULT_NSFW_POLICY, this.nsfwPolicy.toString()) peertubeLocalStorage.setItem(AuthUser.KEYS.AUTO_PLAY_VIDEO, JSON.stringify(this.autoPlayVideo)) this.tokens.save() } diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index 987d64d2a..a8beb242d 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts @@ -5,7 +5,6 @@ import 'rxjs/add/operator/do' import { ReplaySubject } from 'rxjs/ReplaySubject' import { ServerConfig } from '../../../../../shared' import { About } from '../../../../../shared/models/server/about.model' -import { ServerStats } from '../../../../../shared/models/server/server-stats.model' import { environment } from '../../../environments/environment' @Injectable() @@ -26,6 +25,7 @@ export class ServerService { shortDescription: 'PeerTube, a federated (ActivityPub) video streaming platform ' + 'using P2P (BitTorrent) directly in the web browser with WebTorrent and Angular.', defaultClientRoute: '', + defaultNSFWPolicy: 'do_not_list' as 'do_not_list', customizations: { javascript: '', css: '' diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts index 4a94b032d..2bdc48a1d 100644 --- a/client/src/app/shared/users/user.model.ts +++ b/client/src/app/shared/users/user.model.ts @@ -1,5 +1,6 @@ import { hasUserRight, User as UserServerModel, UserRight, UserRole, VideoChannel } from '../../../../../shared' import { Account } from '../account/account.model' +import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type' export type UserConstructorHash = { id: number, @@ -7,7 +8,7 @@ export type UserConstructorHash = { email: string, role: UserRole, videoQuota?: number, - displayNSFW?: boolean, + nsfwPolicy?: NSFWPolicyType, autoPlayVideo?: boolean, createdAt?: Date, account?: Account, @@ -18,7 +19,7 @@ export class User implements UserServerModel { username: string email: string role: UserRole - displayNSFW: boolean + nsfwPolicy: NSFWPolicyType autoPlayVideo: boolean videoQuota: number account: Account @@ -40,8 +41,8 @@ export class User implements UserServerModel { this.videoQuota = hash.videoQuota } - if (hash.displayNSFW !== undefined) { - this.displayNSFW = hash.displayNSFW + if (hash.nsfwPolicy !== undefined) { + this.nsfwPolicy = hash.nsfwPolicy } if (hash.autoPlayVideo !== undefined) { diff --git a/client/src/app/shared/video/video-miniature.component.html b/client/src/app/shared/video/video-miniature.component.html index f28e9b8d9..233432142 100644 --- a/client/src/app/shared/video/video-miniature.component.html +++ b/client/src/app/shared/video/video-miniature.component.html @@ -1,11 +1,11 @@
- +
{{ video.name }} diff --git a/client/src/app/shared/video/video-miniature.component.ts b/client/src/app/shared/video/video-miniature.component.ts index 4d79a74bb..d3f6dc1f6 100644 --- a/client/src/app/shared/video/video-miniature.component.ts +++ b/client/src/app/shared/video/video-miniature.component.ts @@ -1,6 +1,7 @@ import { Component, Input } from '@angular/core' import { User } from '../users' import { Video } from './video.model' +import { ServerService } from '@app/core' @Component({ selector: 'my-video-miniature', @@ -11,7 +12,9 @@ export class VideoMiniatureComponent { @Input() user: User @Input() video: Video - isVideoNSFWForThisUser () { - return this.video.isVideoNSFWForUser(this.user) + constructor (private serverService: ServerService) { } + + isVideoBlur () { + return this.video.isVideoNSFWForUser(this.user, this.serverService.getConfig()) } } diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts index 0c02cbcb9..adc248a1e 100644 --- a/client/src/app/shared/video/video.model.ts +++ b/client/src/app/shared/video/video.model.ts @@ -4,6 +4,7 @@ import { Video as VideoServerModel } from '../../../../../shared' import { Avatar } from '../../../../../shared/models/avatars/avatar.model' import { VideoConstant } from '../../../../../shared/models/videos/video.model' import { getAbsoluteAPIUrl } from '../misc/utils' +import { ServerConfig } from '../../../../../shared/models' export class Video implements VideoServerModel { by: string @@ -83,8 +84,14 @@ export class Video implements VideoServerModel { this.by = Account.CREATE_BY_STRING(hash.account.name, hash.account.host) } - isVideoNSFWForUser (user: User) { - // If the video is NSFW and the user is not logged in, or the user does not want to display NSFW videos... - return (this.nsfw && (!user || user.displayNSFW === false)) + isVideoNSFWForUser (user: User, serverConfig: ServerConfig) { + // Video is not NSFW, skip + if (this.nsfw === false) return false + + // Return user setting if logged in + if (user) return user.nsfwPolicy !== 'display' + + // Return default instance config + return serverConfig.instance.defaultNSFWPolicy !== 'display' } } 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 182703cdf..6f6f02378 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -22,6 +22,7 @@ import { VideoDownloadComponent } from './modal/video-download.component' import { VideoReportComponent } from './modal/video-report.component' import { VideoShareComponent } from './modal/video-share.component' import { getVideojsOptions } from '../../../assets/player/peertube-player' +import { ServerService } from '@app/core' @Component({ selector: 'my-video-watch', @@ -66,6 +67,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { private confirmService: ConfirmService, private metaService: MetaService, private authService: AuthService, + private serverService: ServerService, private notificationsService: NotificationsService, private markdownService: MarkdownService, private zone: NgZone, @@ -335,7 +337,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { this.updateOtherVideosDisplayed() - if (this.video.isVideoNSFWForUser(this.user)) { + if (this.video.isVideoNSFWForUser(this.user, this.serverService.getConfig())) { const res = await this.confirmService.confirm( 'This video contains mature or explicit content. Are you sure you want to watch it?', 'Mature or explicit content' diff --git a/config/default.yaml b/config/default.yaml index 9f4a76621..25dde72c9 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -84,6 +84,9 @@ instance: description: 'Welcome to this PeerTube instance!' # Support markdown terms: 'No terms for now.' # Support markdown default_client_route: '/videos/trending' + # By default, "do_not_list" or "blur" or "display" NSFW videos + # Could be overridden per user with a setting + default_nsfw_policy: 'do_not_list' customizations: javascript: '' # Directly your JavaScript code (without