From 04ed10b21e8e1339514faae0bb690e4d97c23b0a Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 18 Apr 2018 16:29:15 +0200 Subject: [PATCH 1/8] Use popover for help component --- .../src/app/shared/misc/help.component.html | 6 +++-- .../src/app/shared/misc/help.component.scss | 22 ++++++++----------- client/src/app/shared/misc/help.component.ts | 15 +------------ 3 files changed, 14 insertions(+), 29 deletions(-) diff --git a/client/src/app/shared/misc/help.component.html b/client/src/app/shared/misc/help.component.html index e37d93b62..2019a4988 100644 --- a/client/src/app/shared/misc/help.component.html +++ b/client/src/app/shared/misc/help.component.html @@ -13,6 +13,8 @@ diff --git a/client/src/app/shared/misc/help.component.scss b/client/src/app/shared/misc/help.component.scss index b8bf3a7a5..0df8b86fa 100644 --- a/client/src/app/shared/misc/help.component.scss +++ b/client/src/app/shared/misc/help.component.scss @@ -12,20 +12,16 @@ } /deep/ { - .help-tooltip { - opacity: 1 !important; + .popover-body { + text-align: left; + padding: 10px; + max-width: 300px; - .tooltip-inner { - text-align: left; - padding: 10px; - max-width: 300px; - - font-size: 13px; - font-family: $main-fonts; - background-color: #fff; - color: #000; - box-shadow: 0 0 6px rgba(0, 0, 0, 0.5); - } + font-size: 13px; + font-family: $main-fonts; + background-color: #fff; + color: #000; + box-shadow: 0 0 6px rgba(0, 0, 0, 0.5); ul { padding-left: 20px; diff --git a/client/src/app/shared/misc/help.component.ts b/client/src/app/shared/misc/help.component.ts index 89dd1dae5..9defd9aa4 100644 --- a/client/src/app/shared/misc/help.component.ts +++ b/client/src/app/shared/misc/help.component.ts @@ -1,6 +1,5 @@ -import { Component, ElementRef, HostListener, Input, OnInit, ViewChild, OnChanges } from '@angular/core' +import { Component, Input, OnChanges, OnInit } from '@angular/core' import { MarkdownService } from '@app/videos/shared' -import { TooltipDirective } from 'ngx-bootstrap/tooltip' @Component({ selector: 'my-help', @@ -9,7 +8,6 @@ import { TooltipDirective } from 'ngx-bootstrap/tooltip' }) export class HelpComponent implements OnInit, OnChanges { - @ViewChild('tooltipDirective') tooltipDirective: TooltipDirective @Input() preHtml = '' @Input() postHtml = '' @Input() customHtml = '' @@ -17,8 +15,6 @@ export class HelpComponent implements OnInit, OnChanges { mainHtml = '' - constructor (private elementRef: ElementRef) { } - ngOnInit () { this.init() } @@ -27,15 +23,6 @@ export class HelpComponent implements OnInit, OnChanges { this.init() } - @HostListener('document:click', ['$event.target']) - public onClick (targetElement) { - const clickedInside = this.elementRef.nativeElement.contains(targetElement) - - if (this.tooltipDirective.isOpen && !clickedInside) { - this.tooltipDirective.hide() - } - } - private init () { if (this.helpType === 'custom') { this.mainHtml = this.customHtml From 0883b3245bf0deb9106c4041e9afbd3521b79280 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 19 Apr 2018 11:01:34 +0200 Subject: [PATCH 2/8] Add ability to choose what policy we have for NSFW videos There is a global instance setting and a per user setting --- .../edit-custom-config.component.html | 16 ++ .../edit-custom-config.component.ts | 4 + .../account-details.component.html | 19 +- .../account-details.component.scss | 6 + .../account-details.component.ts | 6 +- client/src/app/core/auth/auth-user.model.ts | 9 +- client/src/app/core/server/server.service.ts | 2 +- client/src/app/shared/users/user.model.ts | 9 +- .../video/video-miniature.component.html | 4 +- .../shared/video/video-miniature.component.ts | 7 +- client/src/app/shared/video/video.model.ts | 13 +- .../+video-watch/video-watch.component.ts | 4 +- config/default.yaml | 3 + config/production.yaml.example | 3 + config/test.yaml | 3 + server/controllers/api/config.ts | 3 + server/controllers/api/users.ts | 15 +- server/controllers/api/videos/index.ts | 38 +++- server/controllers/feeds.ts | 9 +- server/helpers/custom-validators/users.ts | 10 +- server/initializers/checker.ts | 11 +- server/initializers/constants.ts | 11 +- server/initializers/installer.ts | 1 + .../migrations/0205-user-nsfw-policy.ts | 46 ++++ server/middlewares/oauth.ts | 10 + server/middlewares/validators/config.ts | 3 +- server/middlewares/validators/users.ts | 4 +- server/models/account/user.ts | 14 +- server/models/video/video.ts | 24 ++- server/tests/api/check-params/config.ts | 19 +- server/tests/api/check-params/users.ts | 6 +- server/tests/api/server/config.ts | 5 + server/tests/api/users/users.ts | 26 +-- server/tests/api/videos/video-nsfw.ts | 197 ++++++++++++++++++ server/tests/utils/users/users.ts | 5 +- server/tests/utils/videos/videos.ts | 26 +++ shared/models/server/custom-config.model.ts | 3 + shared/models/server/server-config.model.ts | 3 + shared/models/users/user-update-me.model.ts | 4 +- shared/models/users/user.model.ts | 3 +- shared/models/videos/nsfw-policy.type.ts | 1 + 41 files changed, 519 insertions(+), 86 deletions(-) create mode 100644 server/initializers/migrations/0205-user-nsfw-policy.ts create mode 100644 server/tests/api/videos/video-nsfw.ts create mode 100644 shared/models/videos/nsfw-policy.type.ts 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