rename blacklist to block/blocklist, merge block and auto-block views

- also replace whitelist with allowlist
- add advanced filters for video-block-list view
- move icons in video-block-list and video-abuse-list to left side
for visibility
- add robot icon to depict automated nature of a block in
video-block-list

resolves #2790
pull/2868/head
Rigel Kent 2020-06-02 20:50:42 +02:00
parent d840487fed
commit 5baee5fca4
No known key found for this signature in database
GPG Key ID: 5E53E96A494E452F
71 changed files with 627 additions and 643 deletions

View File

@ -18,7 +18,7 @@ export class AdminComponent implements OnInit {
ngOnInit () {
if (this.hasUsersRight()) this.items.push({ label: this.i18n('Users'), routerLink: '/admin/users' })
if (this.hasServerFollowRight()) this.items.push({ label: this.i18n('Follows & redundancies'), routerLink: '/admin/follows' })
if (this.hasVideoAbusesRight() || this.hasVideoBlacklistRight()) this.items.push({ label: this.i18n('Moderation'), routerLink: '/admin/moderation' })
if (this.hasVideoAbusesRight() || this.hasVideoBlocklistRight()) this.items.push({ label: this.i18n('Moderation'), routerLink: '/admin/moderation' })
if (this.hasConfigRight()) this.items.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' })
if (this.hasPluginsRight()) this.items.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' })
if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) this.items.push({ label: this.i18n('System'), routerLink: '/admin/system' })
@ -36,8 +36,8 @@ export class AdminComponent implements OnInit {
return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES)
}
hasVideoBlacklistRight () {
return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)
hasVideoBlocklistRight () {
return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLOCKS)
}
hasConfigRight () {

View File

@ -11,8 +11,7 @@ import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersCom
import {
ModerationCommentModalComponent,
VideoAbuseListComponent,
VideoAutoBlacklistListComponent,
VideoBlacklistListComponent
VideoBlockListComponent
} from './moderation'
import { ModerationComponent } from '@app/+admin/moderation/moderation.component'
import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component'
@ -59,10 +58,9 @@ import { VideoAbuseDetailsComponent } from './moderation/video-abuse-list/video-
UserListComponent,
ModerationComponent,
VideoBlacklistListComponent,
VideoBlockListComponent,
VideoAbuseListComponent,
VideoAbuseDetailsComponent,
VideoAutoBlacklistListComponent,
ModerationCommentModalComponent,
InstanceServerBlocklistComponent,
InstanceAccountBlocklistComponent,

View File

@ -430,7 +430,7 @@
<div class="form-group">
<my-peertube-checkbox
inputName="autoBlacklistVideosOfUsersEnabled" formControlName="enabled"
i18n-labelText labelText="Blacklist new videos automatically"
i18n-labelText labelText="Block new videos automatically"
>
<ng-container ngProjectAs="description">
<span i18n>Unless a user is marked as trusted, their videos will stay private until a moderator reviews them.</span>
@ -671,16 +671,16 @@
<div class="form-group">
<my-peertube-checkbox inputName="servicesTwitterWhitelisted" formControlName="whitelisted">
<ng-template ptTemplate="label">
<ng-container i18n>Instance whitelisted by Twitter</ng-container>
<ng-container i18n>Instance allowed by Twitter</ng-container>
</ng-template>
<ng-template ptTemplate="help">
<ng-container i18n>
If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
If your instance is explicitly allowed by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
If the instance is not, we use an image link card that will redirect on your PeerTube instance.<br /><br />
Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on
<a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a>
to see if you instance is whitelisted.
to see if you instance is allowed.
</ng-container>
</ng-template>
</my-peertube-checkbox>

View File

@ -1,5 +1,4 @@
export * from './video-abuse-list'
export * from './video-auto-blacklist-list'
export * from './video-blacklist-list'
export * from './video-block-list'
export * from './moderation.component'
export * from './moderation.routes'

View File

@ -2,11 +2,9 @@
<div i18n class="form-sub-title">Moderation</div>
<div class="admin-sub-nav">
<a *ngIf="hasVideoAbusesRight()" i18n routerLink="video-abuses/list" routerLinkActive="active">Video abuses</a>
<a *ngIf="hasVideoAbusesRight()" i18n routerLink="video-abuses/list" routerLinkActive="active">Video reports</a>
<a *ngIf="hasVideoBlacklistRight()" i18n routerLink="video-blacklist/list" routerLinkActive="active">{{ autoBlacklistVideosEnabled ? 'Manually blacklisted videos' : 'Blacklisted videos' }}</a>
<a *ngIf="autoBlacklistVideosEnabled && hasVideoBlacklistRight()" i18n routerLink="video-auto-blacklist/list" routerLinkActive="active">Auto-blacklisted videos</a>
<a *ngIf="hasVideoBlocklistRight()" i18n routerLink="video-blocks/list" routerLinkActive="active">Video blocks</a>
<a *ngIf="hasAccountsBlocklistRight()" i18n routerLink="blocklist/accounts" routerLinkActive="active">Muted accounts</a>

View File

@ -7,7 +7,7 @@ import { AuthService, ServerService } from '@app/core'
styleUrls: [ './moderation.component.scss' ]
})
export class ModerationComponent implements OnInit {
autoBlacklistVideosEnabled = false
autoBlockVideosEnabled = false
constructor (
private auth: AuthService,
@ -16,7 +16,7 @@ export class ModerationComponent implements OnInit {
ngOnInit (): void {
this.serverService.getConfig()
.subscribe(config => this.autoBlacklistVideosEnabled = config.autoBlacklist.videos.ofUsers.enabled)
.subscribe(config => this.autoBlockVideosEnabled = config.autoBlacklist.videos.ofUsers.enabled)
}
@ -24,8 +24,8 @@ export class ModerationComponent implements OnInit {
return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES)
}
hasVideoBlacklistRight () {
return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)
hasVideoBlocklistRight () {
return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLOCKS)
}
hasAccountsBlocklistRight () {

View File

@ -2,8 +2,7 @@ import { Routes } from '@angular/router'
import { UserRight } from '../../../../../shared'
import { UserRightGuard } from '@app/core'
import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list'
import { VideoBlacklistListComponent } from '@app/+admin/moderation/video-blacklist-list'
import { VideoAutoBlacklistListComponent } from '@app/+admin/moderation/video-auto-blacklist-list'
import { VideoBlockListComponent } from '@app/+admin/moderation/video-block-list'
import { ModerationComponent } from '@app/+admin/moderation/moderation.component'
import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist'
@ -22,16 +21,6 @@ export const ModerationRoutes: Routes = [
redirectTo: 'video-abuses/list',
pathMatch: 'full'
},
{
path: 'video-blacklist',
redirectTo: 'video-blacklist/list',
pathMatch: 'full'
},
{
path: 'video-auto-blacklist',
redirectTo: 'video-auto-blacklist/list',
pathMatch: 'full'
},
{
path: 'video-abuses/list',
component: VideoAbuseListComponent,
@ -39,29 +28,38 @@ export const ModerationRoutes: Routes = [
data: {
userRight: UserRight.MANAGE_VIDEO_ABUSES,
meta: {
title: 'Video abuses list'
title: 'Video reports'
}
}
},
{
path: 'video-blacklist',
redirectTo: 'video-blocks/list',
pathMatch: 'full'
},
{
path: 'video-auto-blacklist',
redirectTo: 'video-blocks/list',
pathMatch: 'full'
},
{
path: 'video-auto-blacklist/list',
component: VideoAutoBlacklistListComponent,
canActivate: [ UserRightGuard ],
data: {
userRight: UserRight.MANAGE_VIDEO_BLACKLIST,
meta: {
title: 'Auto-blacklisted videos'
}
}
redirectTo: 'video-blocks/list',
pathMatch: 'full'
},
{
path: 'video-blacklist/list',
component: VideoBlacklistListComponent,
path: 'video-blacklist',
redirectTo: 'video-blocks/list',
pathMatch: 'full'
},
{
path: 'video-blocks/list',
component: VideoBlockListComponent,
canActivate: [ UserRightGuard ],
data: {
userRight: UserRight.MANAGE_VIDEO_BLACKLIST,
userRight: UserRight.MANAGE_VIDEO_BLOCKS,
meta: {
title: 'Blacklisted videos'
title: 'Videos blocked'
}
}
},

View File

@ -69,7 +69,7 @@
<div class="screenratio">
<div *ngIf="videoAbuse.video.deleted || videoAbuse.video.blacklisted">
<span i18n *ngIf="videoAbuse.video.deleted">The video was deleted</span>
<span i18n *ngIf="!videoAbuse.video.deleted">The video was blacklisted</span>
<span i18n *ngIf="!videoAbuse.video.deleted">The video was blocked</span>
</div>
<div *ngIf="!videoAbuse.video.deleted && !videoAbuse.video.blacklisted" [innerHTML]="videoAbuse.embedHtml"></div>
</div>

View File

@ -19,7 +19,7 @@
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:pending' }" class="dropdown-item" i18n>Unsolved reports</a>
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:accepted' }" class="dropdown-item" i18n>Accepted reports</a>
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'state:rejected' }" class="dropdown-item" i18n>Refused reports</a>
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'videoIs:blacklisted' }" class="dropdown-item" i18n>Reports with blacklisted videos</a>
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'videoIs:blacklisted' }" class="dropdown-item" i18n>Reports with blocked videos</a>
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'videoIs:deleted' }" class="dropdown-item" i18n>Reports with deleted videos</a>
</div>
</div>
@ -84,9 +84,9 @@
</div>
<div class="video-table-video-text">
<div>
{{ videoAbuse.video.name }}
<span *ngIf="!videoAbuse.video.blacklisted" class="glyphicon glyphicon-new-window"></span>
<span *ngIf="videoAbuse.video.blacklisted" i18n-title title="Video was blacklisted" class="glyphicon glyphicon-ban-circle"></span>
<span *ngIf="videoAbuse.video.blacklisted" i18n-title title="The video was blocked" class="glyphicon glyphicon-ban-circle"></span>
{{ videoAbuse.video.name }}
</div>
<div class="text-muted" i18n>by {{ videoAbuse.video.channel?.displayName }} on {{ videoAbuse.video.channel?.host }} </div>
</div>

View File

@ -3,7 +3,7 @@ import { Account } from '@app/shared/account/account.model'
import { Notifier } from '@app/core'
import { SortMeta } from 'primeng/api'
import { VideoAbuse, VideoAbuseState } from '../../../../../../shared'
import { RestPagination, RestTable, VideoAbuseService, VideoBlacklistService } from '../../../shared'
import { RestPagination, RestTable, VideoAbuseService, VideoBlockService } from '../../../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { DropdownAction } from '../../../shared/buttons/action-dropdown.component'
import { ConfirmService } from '../../../core/index'
@ -53,7 +53,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
private videoAbuseService: VideoAbuseService,
private blocklistService: BlocklistService,
private videoService: VideoService,
private videoBlacklistService: VideoBlacklistService,
private videoBlocklistService: VideoBlockService,
private confirmService: ConfirmService,
private i18n: I18n,
private markdownRenderer: MarkdownService,
@ -101,13 +101,13 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
isDisplayed: videoAbuse => !videoAbuse.video.deleted
},
{
label: this.i18n('Blacklist video'),
label: this.i18n('Block video'),
isDisplayed: videoAbuse => !videoAbuse.video.deleted && !videoAbuse.video.blacklisted,
handler: videoAbuse => {
this.videoBlacklistService.blacklistVideo(videoAbuse.video.id, undefined, true)
this.videoBlocklistService.blockVideo(videoAbuse.video.id, undefined, true)
.subscribe(
() => {
this.notifier.success(this.i18n('Video blacklisted.'))
this.notifier.success(this.i18n('Video blocklisted.'))
this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED)
},
@ -117,13 +117,13 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
}
},
{
label: this.i18n('Unblacklist video'),
label: this.i18n('Unblock video'),
isDisplayed: videoAbuse => !videoAbuse.video.deleted && videoAbuse.video.blacklisted,
handler: videoAbuse => {
this.videoBlacklistService.removeVideoFromBlacklist(videoAbuse.video.id)
this.videoBlocklistService.unblockVideo(videoAbuse.video.id)
.subscribe(
() => {
this.notifier.success(this.i18n('Video unblacklisted.'))
this.notifier.success(this.i18n('Video unblocklisted.'))
this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED)
},
@ -292,7 +292,6 @@ export class VideoAbuseListComponent extends RestTable implements OnInit, AfterV
err => this.notifier.error(err.message)
)
}
protected loadData () {

View File

@ -1 +0,0 @@
export * from './video-auto-blacklist-list.component'

View File

@ -1,20 +0,0 @@
<my-videos-selection
[pagination]="pagination"
[(selection)]="selection"
[(videosModel)]="videos"
[miniatureDisplayOptions]="miniatureDisplayOptions"
[titlePage]="titlePage"
[getVideosObservableFunction]="getVideosObservableFunction"
>
<ng-template ptTemplate="globalButtons">
<span class="action-button action-button-unblacklist-selection" (click)="removeSelectedVideosFromBlacklist()">
<my-global-icon iconName="tick"></my-global-icon>
<ng-container i18n>Unblacklist</ng-container>
</span>
</ng-template>
<ng-template ptTemplate="rowButtons" let-video>
<my-button i18n-label label="Unblacklist" icon="tick" (click)="removeVideoFromBlacklist(video)"></my-button>
</ng-template>
</my-videos-selection>

View File

@ -1,14 +0,0 @@
@import '_variables';
@import '_mixins';
.action-button-unblacklist-selection {
display: inline-block;
@include peertube-button;
@include orange-button;
@include button-with-icon(21px);
my-global-icon {
@include apply-svg-color(#fff);
}
}

View File

@ -1,86 +0,0 @@
import { Component } from '@angular/core'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { ActivatedRoute, Router } from '@angular/router'
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
import { AuthService, Notifier, ServerService } from '@app/core'
import { VideoBlacklistService } from '@app/shared'
import { immutableAssign } from '@app/shared/misc/utils'
import { ScreenService } from '@app/shared/misc/screen.service'
import { MiniatureDisplayOptions } from '@app/shared/video/video-miniature.component'
import { SelectionType } from '@app/shared/video/videos-selection.component'
import { Video } from '@app/shared/video/video.model'
@Component({
selector: 'my-video-auto-blacklist-list',
templateUrl: './video-auto-blacklist-list.component.html',
styleUrls: [ './video-auto-blacklist-list.component.scss' ]
})
export class VideoAutoBlacklistListComponent {
titlePage: string
selection: SelectionType = {}
miniatureDisplayOptions: MiniatureDisplayOptions = {
date: true,
views: false,
by: true,
privacyLabel: false,
privacyText: true,
state: false,
blacklistInfo: false,
nsfw: true
}
pagination: ComponentPagination = {
currentPage: 1,
itemsPerPage: 5,
totalItems: null
}
videos: Video[] = []
getVideosObservableFunction = this.getVideosObservable.bind(this)
constructor (
protected router: Router,
protected route: ActivatedRoute,
protected notifier: Notifier,
protected authService: AuthService,
protected screenService: ScreenService,
protected serverService: ServerService,
private i18n: I18n,
private videoBlacklistService: VideoBlacklistService
) {
this.titlePage = this.i18n('Auto-blacklisted videos')
}
getVideosObservable (page: number) {
const newPagination = immutableAssign(this.pagination, { currentPage: page })
return this.videoBlacklistService.getAutoBlacklistedAsVideoList(newPagination)
}
removeVideoFromBlacklist (entry: Video) {
this.videoBlacklistService.removeVideoFromBlacklist(entry.id).subscribe(
() => {
this.notifier.success(this.i18n('Video {{name}} removed from blacklist.', { name: entry.name }))
this.videos = this.videos.filter(v => v.id !== entry.id)
},
error => this.notifier.error(error.message)
)
}
removeSelectedVideosFromBlacklist () {
const toReleaseVideosIds = Object.keys(this.selection)
.filter(k => this.selection[ k ] === true)
.map(k => parseInt(k, 10))
this.videoBlacklistService.removeVideoFromBlacklist(toReleaseVideosIds).subscribe(
() => {
this.notifier.success(this.i18n('{{num}} videos removed from blacklist.', { num: toReleaseVideosIds.length }))
this.selection = {}
this.videos = this.videos.filter(v => toReleaseVideosIds.includes(v.id) === false)
},
error => this.notifier.error(error.message)
)
}
}

View File

@ -1 +0,0 @@
export * from './video-blacklist-list.component'

View File

@ -1,100 +0,0 @@
<p-table
[value]="blacklist" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blacklisted videos"
(onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
>
<ng-template pTemplate="caption">
<div class="caption">
<div class="ml-auto has-feedback has-clear">
<input
type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
(keyup)="onSearch($event)"
>
<a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
<span class="sr-only" i18n>Clear filters</span>
</div>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th style="width: 40px"></th>
<th i18n pSortableColumn="name">Video <p-sortIcon field="name"></p-sortIcon></th>
<th style="width: 100px;" i18n>Sensitive</th>
<th style="width: 120px;" i18n>Unfederated</th>
<th style="width: 150px;" i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
<th style="width: 150px;"></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-videoBlacklist let-expanded="expanded">
<tr>
<td *ngIf="!videoBlacklist.reason"></td>
<td *ngIf="videoBlacklist.reason" class="expand-cell c-hand" [pRowToggler]="videoBlacklist" i18n-ngbTooltip ngbTooltip="More information" placement="top-left" container="body">
<span class="expander">
<i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
</span>
</td>
<td>
<a [href]="getVideoUrl(videoBlacklist)" class="video-table-video-link" i18n-title title="Open video in a new tab" target="_blank" rel="noopener noreferrer">
<div class="video-table-video">
<div class="video-table-video-image">
<img [src]="videoBlacklist.video.thumbnailPath">
</div>
<div class="video-table-video-text">
<div>
{{ videoBlacklist.video.name }}
<span i18n-title title="Video was blacklisted" class="glyphicon glyphicon-ban-circle"></span>
</div>
<div class="text-muted">by {{ videoBlacklist.video.channel?.displayName }} on {{ videoBlacklist.video.channel?.host }} </div>
</div>
</div>
</a>
</td>
<ng-container *ngIf="videoBlacklist.reason">
<td class="c-hand" [pRowToggler]="videoBlacklist">{{ booleanToText(videoBlacklist.video.nsfw) }}</td>
<td class="c-hand" [pRowToggler]="videoBlacklist">{{ booleanToText(videoBlacklist.unfederated) }}</td>
<td class="c-hand" [pRowToggler]="videoBlacklist">{{ videoBlacklist.createdAt | date: 'short' }}</td>
</ng-container>
<ng-container *ngIf="!videoBlacklist.reason">
<td>{{ booleanToText(videoBlacklist.video.nsfw) }}</td>
<td>{{ booleanToText(videoBlacklist.unfederated) }}</td>
<td>{{ videoBlacklist.createdAt | date: 'short' }}</td>
</ng-container>
<td class="action-cell">
<my-action-dropdown
[ngClass]="{ 'show': expanded }" placement="bottom-right" container="body"
i18n-label label="Actions" [actions]="videoBlacklistActions" [entry]="videoBlacklist"
></my-action-dropdown>
</td>
</tr>
</ng-template>
<ng-template pTemplate="rowexpansion" let-videoBlacklist>
<tr>
<td class="expand-cell" colspan="6">
<div class="d-flex moderation-expanded">
<span class="col-2 moderation-expanded-label" i18n>Blacklist reason:</span>
<span class="col-9 moderation-expanded-text" [innerHTML]="videoBlacklist.reasonHtml"></span>
</div>
</td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr>
<td colspan="6">
<div class="empty-table-message">
<ng-container *ngIf="search" i18n>No blacklisted video found matching current filters.</ng-container>
<ng-container *ngIf="!search" i18n>No blacklisted video found.</ng-container>
</div>
</td>
</tr>
</ng-template>
</p-table>

View File

@ -1,113 +0,0 @@
import { Component, OnInit } from '@angular/core'
import { SortMeta } from 'primeng/api'
import { Notifier, ServerService } from '@app/core'
import { ConfirmService } from '../../../core'
import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared'
import { VideoBlacklist, VideoBlacklistType } from '../../../../../../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { DropdownAction } from '../../../shared/buttons/action-dropdown.component'
import { Video } from '../../../shared/video/video.model'
import { MarkdownService } from '@app/shared/renderer'
@Component({
selector: 'my-video-blacklist-list',
templateUrl: './video-blacklist-list.component.html',
styleUrls: [ '../moderation.component.scss' ]
})
export class VideoBlacklistListComponent extends RestTable implements OnInit {
blacklist: (VideoBlacklist & { reasonHtml?: string })[] = []
totalRecords = 0
sort: SortMeta = { field: 'createdAt', order: -1 }
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
listBlacklistTypeFilter: VideoBlacklistType = undefined
videoBlacklistActions: DropdownAction<VideoBlacklist>[] = []
constructor (
private notifier: Notifier,
private serverService: ServerService,
private confirmService: ConfirmService,
private videoBlacklistService: VideoBlacklistService,
private markdownRenderer: MarkdownService,
private i18n: I18n
) {
super()
}
ngOnInit () {
this.serverService.getConfig()
.subscribe(config => {
// don't filter if auto-blacklist is not enabled as this will be the only list
if (config.autoBlacklist.videos.ofUsers.enabled) {
this.listBlacklistTypeFilter = VideoBlacklistType.MANUAL
}
})
this.initialize()
this.videoBlacklistActions = [
{
label: this.i18n('Unblacklist'),
handler: videoBlacklist => this.removeVideoFromBlacklist(videoBlacklist)
}
]
}
getIdentifier () {
return 'VideoBlacklistListComponent'
}
getVideoUrl (videoBlacklist: VideoBlacklist) {
return Video.buildClientUrl(videoBlacklist.video.uuid)
}
booleanToText (value: boolean) {
if (value === true) return this.i18n('yes')
return this.i18n('no')
}
toHtml (text: string) {
return this.markdownRenderer.textMarkdownToHTML(text)
}
async removeVideoFromBlacklist (entry: VideoBlacklist) {
const confirmMessage = this.i18n(
'Do you really want to remove this video from the blacklist? It will be available again in the videos list.'
)
const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblacklist'))
if (res === false) return
this.videoBlacklistService.removeVideoFromBlacklist(entry.video.id).subscribe(
() => {
this.notifier.success(this.i18n('Video {{name}} removed from the blacklist.', { name: entry.video.name }))
this.loadData()
},
err => this.notifier.error(err.message)
)
}
protected loadData () {
this.videoBlacklistService.listBlacklist({
pagination: this.pagination,
sort: this.sort,
search: this.search,
type: this.listBlacklistTypeFilter
})
.subscribe(
async resultList => {
this.totalRecords = resultList.total
this.blacklist = resultList.data
for (const element of this.blacklist) {
Object.assign(element, { reasonHtml: await this.toHtml(element.reason) })
}
},
err => this.notifier.error(err.message)
)
}
}

View File

@ -0,0 +1 @@
export * from './video-block-list.component'

View File

@ -0,0 +1,113 @@
<p-table
[value]="blocklist" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} blocked videos"
(onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
>
<ng-template pTemplate="caption">
<div class="caption">
<div class="ml-auto">
<div class="input-group has-feedback has-clear">
<div class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body">
<div class="input-group-text" ngbDropdownToggle>
<span class="caret" aria-haspopup="menu" role="button"></span>
</div>
<div role="menu" ngbDropdownMenu>
<h6 class="dropdown-header" i18n>Advanced block filters</h6>
<a [routerLink]="[ '/admin/moderation/video-blocks/list' ]" [queryParams]="{ 'search': 'type:auto' }" class="dropdown-item" i18n>Automatic blocks</a>
<a [routerLink]="[ '/admin/moderation/video-blocks/list' ]" [queryParams]="{ 'search': 'type:manual' }" class="dropdown-item" i18n>Manual blocks</a>
</div>
</div>
<input
type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..."
(keyup)="onBlockSearch($event)"
>
<a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetTableFilter()"></a>
<span class="sr-only" i18n>Clear filters</span>
</div>
</div>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th style="width: 40px"></th>
<th i18n pSortableColumn="name">Video <p-sortIcon field="name"></p-sortIcon></th>
<th style="width: 100px;" i18n>Sensitive</th>
<th style="width: 120px;" i18n>Unfederated</th>
<th style="width: 150px;" i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
<th style="width: 150px;"></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-videoBlock let-expanded="expanded">
<tr>
<td *ngIf="!videoBlock.reason"></td>
<td *ngIf="videoBlock.reason" class="expand-cell c-hand" [pRowToggler]="videoBlock" i18n-ngbTooltip ngbTooltip="More information" placement="top-left" container="body">
<span class="expander">
<i [ngClass]="expanded ? 'glyphicon glyphicon-menu-down' : 'glyphicon glyphicon-menu-right'"></i>
</span>
</td>
<td>
<a [href]="getVideoUrl(videoBlock)" class="video-table-video-link" i18n-title title="Open video in a new tab" target="_blank" rel="noopener noreferrer">
<div class="video-table-video">
<div class="video-table-video-image">
<img [src]="videoBlock.video.thumbnailPath">
</div>
<div class="video-table-video-text">
<div>
<my-global-icon i18n-title title="The video was blocked due to automatic blocking of new videos" *ngIf="videoBlock.type == 2" iconName="robot"></my-global-icon>
{{ videoBlock.video.name }}
</div>
<div class="text-muted">by {{ videoBlock.video.channel?.displayName }} on {{ videoBlock.video.channel?.host }} </div>
</div>
</div>
</a>
</td>
<ng-container *ngIf="videoBlock.reason">
<td class="c-hand" [pRowToggler]="videoBlock">{{ booleanToText(videoBlock.video.nsfw) }}</td>
<td class="c-hand" [pRowToggler]="videoBlock">{{ booleanToText(videoBlock.unfederated) }}</td>
<td class="c-hand" [pRowToggler]="videoBlock">{{ videoBlock.createdAt | date: 'short' }}</td>
</ng-container>
<ng-container *ngIf="!videoBlock.reason">
<td>{{ booleanToText(videoBlock.video.nsfw) }}</td>
<td>{{ booleanToText(videoBlock.unfederated) }}</td>
<td>{{ videoBlock.createdAt | date: 'short' }}</td>
</ng-container>
<td class="action-cell">
<my-action-dropdown
[ngClass]="{ 'show': expanded }" placement="bottom-right" container="body"
i18n-label label="Actions" [actions]="videoBlocklistActions" [entry]="videoBlock"
></my-action-dropdown>
</td>
</tr>
</ng-template>
<ng-template pTemplate="rowexpansion" let-videoBlock>
<tr>
<td class="expand-cell" colspan="6">
<div class="d-flex moderation-expanded">
<span class="col-2 moderation-expanded-label" i18n>Block reason:</span>
<span class="col-9 moderation-expanded-text" [innerHTML]="videoBlock.reasonHtml"></span>
</div>
</td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr>
<td colspan="6">
<div class="empty-table-message">
<ng-container *ngIf="search" i18n>No blocked video found matching current filters.</ng-container>
<ng-container *ngIf="!search" i18n>No blocked video found.</ng-container>
</div>
</td>
</tr>
</ng-template>
</p-table>

View File

@ -0,0 +1,18 @@
@import 'mixins';
my-global-icon {
@include apply-svg-color(#7d7d7d);
width: 12px;
height: 12px;
position: relative;
top: -1px;
}
.input-group {
@include peertube-input-group(300px);
.dropdown-toggle::after {
margin-left: 0;
}
}

View File

@ -0,0 +1,196 @@
import { Component, OnInit } from '@angular/core'
import { SortMeta } from 'primeng/api'
import { Notifier, ServerService } from '@app/core'
import { ConfirmService } from '../../../core'
import { RestPagination, RestTable, VideoBlockService } from '../../../shared'
import { VideoBlocklist, VideoBlockType } from '../../../../../../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { DropdownAction } from '../../../shared/buttons/action-dropdown.component'
import { Video } from '../../../shared/video/video.model'
import { MarkdownService } from '@app/shared/renderer'
import { Params, ActivatedRoute, Router } from '@angular/router'
import { filter, switchMap } from 'rxjs/operators'
import { VideoService } from '@app/shared/video/video.service'
@Component({
selector: 'my-video-block-list',
templateUrl: './video-block-list.component.html',
styleUrls: [ '../moderation.component.scss', './video-block-list.component.scss' ]
})
export class VideoBlockListComponent extends RestTable implements OnInit {
blocklist: (VideoBlocklist & { reasonHtml?: string })[] = []
totalRecords = 0
sort: SortMeta = { field: 'createdAt', order: -1 }
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
listBlockTypeFilter: VideoBlockType = undefined
videoBlocklistActions: DropdownAction<VideoBlocklist>[][] = []
constructor (
private notifier: Notifier,
private serverService: ServerService,
private confirmService: ConfirmService,
private videoBlocklistService: VideoBlockService,
private markdownRenderer: MarkdownService,
private videoService: VideoService,
private route: ActivatedRoute,
private router: Router,
private i18n: I18n
) {
super()
this.videoBlocklistActions = [
[
{
label: this.i18n('Internal actions'),
isHeader: true
},
{
label: this.i18n('Switch video block to manual'),
handler: videoBlock => {
this.videoBlocklistService.unblockVideo(videoBlock.video.id).pipe(
switchMap(_ => this.videoBlocklistService.blockVideo(videoBlock.video.id, undefined, true))
).subscribe(
() => {
this.notifier.success(this.i18n('Video {{name}} switched to manual block.', { name: videoBlock.video.name }))
this.loadData()
},
err => this.notifier.error(err.message)
)
}
}
],
[
{
label: this.i18n('Actions for the video'),
isHeader: true,
},
{
label: this.i18n('Unblock video'),
handler: videoBlock => this.unblockVideo(videoBlock)
},
{
label: this.i18n('Delete video'),
handler: async videoBlock => {
const res = await this.confirmService.confirm(
this.i18n('Do you really want to delete this video?'),
this.i18n('Delete')
)
if (res === false) return
this.videoService.removeVideo(videoBlock.video.id)
.subscribe(
() => {
this.notifier.success(this.i18n('Video deleted.'))
},
err => this.notifier.error(err.message)
)
}
}
]
]
}
ngOnInit () {
this.serverService.getConfig()
.subscribe(config => {
// don't filter if auto-blacklist is not enabled as this will be the only list
if (config.autoBlacklist.videos.ofUsers.enabled) {
this.listBlockTypeFilter = VideoBlockType.MANUAL
}
})
this.initialize()
this.route.queryParams
.pipe(filter(params => params.search !== undefined && params.search !== null))
.subscribe(params => {
this.search = params.search
this.setTableFilter(params.search)
this.loadData()
})
}
ngAfterViewInit () {
if (this.search) this.setTableFilter(this.search)
}
/* Table filter functions */
onBlockSearch (event: Event) {
this.onSearch(event)
this.setQueryParams((event.target as HTMLInputElement).value)
}
setQueryParams (search: string) {
const queryParams: Params = {}
if (search) Object.assign(queryParams, { search })
this.router.navigate([ '/admin/moderation/video-blocks/list' ], { queryParams })
}
resetTableFilter () {
this.setTableFilter('')
this.setQueryParams('')
this.resetSearch()
}
/* END Table filter functions */
getIdentifier () {
return 'VideoBlockListComponent'
}
getVideoUrl (videoBlock: VideoBlocklist) {
return Video.buildClientUrl(videoBlock.video.uuid)
}
booleanToText (value: boolean) {
if (value === true) return this.i18n('yes')
return this.i18n('no')
}
toHtml (text: string) {
return this.markdownRenderer.textMarkdownToHTML(text)
}
async unblockVideo (entry: VideoBlocklist) {
const confirmMessage = this.i18n(
'Do you really want to unblock this video? It will be available again in the videos list.'
)
const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblock'))
if (res === false) return
this.videoBlocklistService.unblockVideo(entry.video.id).subscribe(
() => {
this.notifier.success(this.i18n('Video {{name}} unblocked.', { name: entry.video.name }))
this.loadData()
},
err => this.notifier.error(err.message)
)
}
protected loadData () {
this.videoBlocklistService.listBlocks({
pagination: this.pagination,
sort: this.sort,
search: this.search,
})
.subscribe(
async resultList => {
this.totalRecords = resultList.total
this.blocklist = resultList.data
for (const element of this.blocklist) {
Object.assign(element, { reasonHtml: await this.toHtml(element.reason) })
}
},
err => this.notifier.error(err.message)
)
}
}

View File

@ -88,7 +88,7 @@ export abstract class UserEdit extends FormReactive implements OnInit {
}
protected buildAdminFlags (formValue: any) {
return formValue.byPassAutoBlacklist ? UserAdminFlag.BY_PASS_VIDEO_AUTO_BLACKLIST : UserAdminFlag.NONE
return formValue.byPassAutoBlacklist ? UserAdminFlag.BYPASS_VIDEO_AUTO_BLOCK : UserAdminFlag.NONE
}
protected buildQuotaOptions () {

View File

@ -125,7 +125,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
role: userJson.role.toString(),
videoQuota: userJson.videoQuota,
videoQuotaDaily: userJson.videoQuotaDaily,
byPassAutoBlacklist: userJson.adminFlags & UserAdminFlag.BY_PASS_VIDEO_AUTO_BLACKLIST
byPassAutoBlacklist: userJson.adminFlags & UserAdminFlag.BYPASS_VIDEO_AUTO_BLOCK
})
}
}

View File

@ -17,6 +17,6 @@
<div class="video" *ngFor="let video of videos">
<my-video-miniature
[video]="video" [displayAsRow]="true"
(videoRemoved)="removeVideoFromArray(video)" (videoBlacklisted)="removeVideoFromArray(video)"></my-video-miniature>
(videoRemoved)="removeVideoFromArray(video)" (videoBlocked)="removeVideoFromArray(video)"></my-video-miniature>
</div>
</div>

View File

@ -35,8 +35,8 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
newVideoFromSubscription: this.i18n('New video from your subscriptions'),
newCommentOnMyVideo: this.i18n('New comment on your video'),
videoAbuseAsModerator: this.i18n('New video abuse'),
videoAutoBlacklistAsModerator: this.i18n('Video auto-blacklisted waiting review'),
blacklistOnMyVideo: this.i18n('One of your video is blacklisted/unblacklisted'),
videoAutoBlacklistAsModerator: this.i18n('Video blocked automatically waiting review'),
blacklistOnMyVideo: this.i18n('One of your video is blocked/unblocked'),
myVideoPublished: this.i18n('Video published (after transcoding/scheduled update)'),
myVideoImportFinished: this.i18n('Video import finished'),
newUserRegistration: this.i18n('A new user registered on your instance'),
@ -49,7 +49,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
this.rightNotifications = {
videoAbuseAsModerator: UserRight.MANAGE_VIDEO_ABUSES,
videoAutoBlacklistAsModerator: UserRight.MANAGE_VIDEO_BLACKLIST,
videoAutoBlacklistAsModerator: UserRight.MANAGE_VIDEO_BLOCKS,
newUserRegistration: UserRight.MANAGE_USERS,
newInstanceFollower: UserRight.MANAGE_SERVER_FOLLOW,
autoInstanceFollowing: UserRight.MANAGE_CONFIGURATION

View File

@ -33,7 +33,7 @@ export class MenuComponent implements OnInit {
[UserRight.MANAGE_USERS]: '/admin/users',
[UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends',
[UserRight.MANAGE_VIDEO_ABUSES]: '/admin/moderation/video-abuses',
[UserRight.MANAGE_VIDEO_BLACKLIST]: '/admin/moderation/video-blacklist',
[UserRight.MANAGE_VIDEO_BLOCKS]: '/admin/moderation/video-blocks',
[UserRight.MANAGE_JOBS]: '/admin/jobs',
[UserRight.MANAGE_CONFIGURATION]: '/admin/config'
}
@ -131,7 +131,7 @@ export class MenuComponent implements OnInit {
UserRight.MANAGE_USERS,
UserRight.MANAGE_SERVER_FOLLOW,
UserRight.MANAGE_VIDEO_ABUSES,
UserRight.MANAGE_VIDEO_BLACKLIST,
UserRight.MANAGE_VIDEO_BLOCKS,
UserRight.MANAGE_JOBS,
UserRight.MANAGE_CONFIGURATION
]

View File

@ -55,7 +55,7 @@
<my-video-miniature
[video]="result" [user]="user" [displayAsRow]="true" [displayVideoActions]="!hideActions()"
[useLazyLoadUrl]="advancedSearch.searchTarget === 'search-index'"
(videoBlacklisted)="removeVideoFromArray(result)" (videoRemoved)="removeVideoFromArray(result)"
(videoBlocked)="removeVideoFromArray(result)" (videoRemoved)="removeVideoFromArray(result)"
></my-video-miniature>
</div>
</ng-container>

View File

@ -6,7 +6,7 @@ export * from './login-validators.service'
export * from './reset-password-validators.service'
export * from './user-validators.service'
export * from './video-abuse-validators.service'
export * from './video-blacklist-validators.service'
export * from './video-block-validators.service'
export * from './video-channel-validators.service'
export * from './video-comment-validators.service'
export * from './video-validators.service'

View File

@ -4,15 +4,15 @@ import { Injectable } from '@angular/core'
import { BuildFormValidator } from '@app/shared'
@Injectable()
export class VideoBlacklistValidatorsService {
readonly VIDEO_BLACKLIST_REASON: BuildFormValidator
export class VideoBlockValidatorsService {
readonly VIDEO_BLOCK_REASON: BuildFormValidator
constructor (private i18n: I18n) {
this.VIDEO_BLACKLIST_REASON = {
this.VIDEO_BLOCK_REASON = {
VALIDATORS: [ Validators.minLength(2), Validators.maxLength(300) ],
MESSAGES: {
'minlength': this.i18n('Blacklist reason must be at least 2 characters long.'),
'maxlength': this.i18n('Blacklist reason cannot be more than 300 characters long.')
'minlength': this.i18n('Block reason must be at least 2 characters long.'),
'maxlength': this.i18n('Block reason cannot be more than 300 characters long.')
}
}
}

View File

@ -56,7 +56,8 @@ const icons = {
'refresh': require('!!raw-loader?!../../../assets/images/global/refresh.svg').default,
'npm': require('!!raw-loader?!../../../assets/images/global/npm.svg').default,
'fullscreen': require('!!raw-loader?!../../../assets/images/global/fullscreen.svg').default,
'exit-fullscreen': require('!!raw-loader?!../../../assets/images/global/exit-fullscreen.svg').default
'exit-fullscreen': require('!!raw-loader?!../../../assets/images/global/exit-fullscreen.svg').default,
'robot': require('!!raw-loader?!../../../assets/images/global/robot.svg').default
}
export type GlobalIconName = keyof typeof icons

View File

@ -3,5 +3,5 @@ export * from './forms'
export * from './rest'
export * from './users'
export * from './video-abuse'
export * from './video-blacklist'
export * from './video-block'
export * from './shared.module'

View File

@ -35,7 +35,7 @@ import {
UserValidatorsService,
VideoAbuseValidatorsService,
VideoAcceptOwnershipValidatorsService,
VideoBlacklistValidatorsService,
VideoBlockValidatorsService,
VideoChangeOwnershipValidatorsService,
VideoChannelValidatorsService,
VideoCommentValidatorsService,
@ -78,7 +78,7 @@ import { VideoPlaylistElementMiniatureComponent } from '@app/shared/video-playli
import { VideoPlaylistMiniatureComponent } from '@app/shared/video-playlist/video-playlist-miniature.component'
import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
import { InfiniteScrollerDirective } from '@app/shared/video/infinite-scroller.directive'
import { VideoBlacklistComponent } from '@app/shared/video/modals/video-blacklist.component'
import { VideoBlockComponent } from '@app/shared/video/modals/video-block.component'
import { VideoDownloadComponent } from '@app/shared/video/modals/video-download.component'
import { VideoReportComponent } from '@app/shared/video/modals/video-report.component'
import { RedundancyService } from '@app/shared/video/redundancy.service'
@ -102,7 +102,7 @@ import { LoaderComponent } from './misc/loader.component'
import { RestExtractor, RestService } from './rest'
import { UserService } from './users'
import { VideoAbuseService } from './video-abuse'
import { VideoBlacklistService } from './video-blacklist'
import { VideoBlockService } from './video-block'
import { VideoOwnershipService } from './video-ownership'
import { FeedComponent } from './video/feed.component'
import { VideoMiniatureComponent } from './video/video-miniature.component'
@ -147,7 +147,7 @@ import { VideoService } from './video/video.service'
VideoDownloadComponent,
VideoReportComponent,
VideoBlacklistComponent,
VideoBlockComponent,
FeedComponent,
@ -230,7 +230,7 @@ import { VideoService } from './video/video.service'
VideoDownloadComponent,
VideoReportComponent,
VideoBlacklistComponent,
VideoBlockComponent,
FeedComponent,
@ -282,7 +282,7 @@ import { VideoService } from './video/video.service'
RestExtractor,
RestService,
VideoAbuseService,
VideoBlacklistService,
VideoBlockService,
VideoOwnershipService,
UserService,
VideoService,
@ -305,7 +305,7 @@ import { VideoService } from './video/video.service'
VideoCommentValidatorsService,
VideoValidatorsService,
VideoCaptionsValidatorsService,
VideoBlacklistValidatorsService,
VideoBlockValidatorsService,
OverviewService,
VideoChangeOwnershipValidatorsService,
VideoAcceptOwnershipValidatorsService,

View File

@ -96,7 +96,7 @@ export class UserNotification implements UserNotificationServer {
this.videoUrl = this.buildVideoUrl(this.video)
break
case UserNotificationType.UNBLACKLIST_ON_MY_VIDEO:
case UserNotificationType.UNBLOCK_ON_MY_VIDEO:
this.videoUrl = this.buildVideoUrl(this.video)
break
@ -112,7 +112,7 @@ export class UserNotification implements UserNotificationServer {
this.videoUrl = this.buildVideoUrl(this.videoAbuse.video)
break
case UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS:
case UserNotificationType.VIDEO_AUTO_BLOCK_FOR_MODERATORS:
this.videoAutoBlacklistUrl = '/admin/moderation/video-auto-blacklist/list'
// Backward compatibility where we did not assign videoBlacklist to this type of notification before
if (!this.videoBlacklist) this.videoBlacklist = { id: null, video: this.video }
@ -120,7 +120,7 @@ export class UserNotification implements UserNotificationServer {
this.videoUrl = this.buildVideoUrl(this.videoBlacklist.video)
break
case UserNotificationType.BLACKLIST_ON_MY_VIDEO:
case UserNotificationType.BLOCK_ON_MY_VIDEO:
this.videoUrl = this.buildVideoUrl(this.videoBlacklist.video)
break

View File

@ -26,19 +26,19 @@
</ng-template>
</ng-container>
<ng-container *ngSwitchCase="UserNotificationType.UNBLACKLIST_ON_MY_VIDEO">
<ng-container *ngSwitchCase="UserNotificationType.UNBLOCK_ON_MY_VIDEO">
<my-global-icon iconName="undo"></my-global-icon>
<div class="message" i18n>
Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a> has been unblacklisted
Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a> has been unblocked
</div>
</ng-container>
<ng-container *ngSwitchCase="UserNotificationType.BLACKLIST_ON_MY_VIDEO">
<ng-container *ngSwitchCase="UserNotificationType.BLOCK_ON_MY_VIDEO">
<my-global-icon iconName="no"></my-global-icon>
<div class="message" i18n>
Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoBlacklist.video.name }}</a> has been blacklisted
Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoBlacklist.video.name }}</a> has been blocked
</div>
</ng-container>
@ -50,11 +50,11 @@
</div>
</ng-container>
<ng-container *ngSwitchCase="UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS">
<ng-container *ngSwitchCase="UserNotificationType.VIDEO_AUTO_BLOCK_FOR_MODERATORS">
<my-global-icon iconName="no"></my-global-icon>
<div class="message" i18n>
The recently added video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoBlacklist.video.name }}</a> has been <a (click)="markAsRead(notification)" [routerLink]="notification.videoAutoBlacklistUrl">auto-blacklisted</a>
The recently added video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoBlacklist.video.name }}</a> has been <a (click)="markAsRead(notification)" [routerLink]="notification.videoAutoBlacklistUrl">auto-blocked</a>
</div>
</ng-container>

View File

@ -1 +0,0 @@
export * from './video-blacklist.service'

View File

@ -1,89 +0,0 @@
import { catchError, map, concatMap, toArray } from 'rxjs/operators'
import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { SortMeta } from 'primeng/api'
import { from as observableFrom, Observable } from 'rxjs'
import { VideoBlacklist, VideoBlacklistType, ResultList } from '../../../../../shared'
import { Video } from '../video/video.model'
import { environment } from '../../../environments/environment'
import { RestExtractor, RestPagination, RestService } from '../rest'
import { ComponentPaginationLight } from '../rest/component-pagination.model'
@Injectable()
export class VideoBlacklistService {
private static BASE_VIDEOS_URL = environment.apiUrl + '/api/v1/videos/'
constructor (
private authHttp: HttpClient,
private restService: RestService,
private restExtractor: RestExtractor
) {}
listBlacklist (options: {
pagination: RestPagination,
sort: SortMeta,
search?: string
type?: VideoBlacklistType
}): Observable<ResultList<VideoBlacklist>> {
const { pagination, sort, search, type } = options
let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort)
if (search) params = params.append('search', search)
if (type) params = params.append('type', type.toString())
return this.authHttp.get<ResultList<VideoBlacklist>>(VideoBlacklistService.BASE_VIDEOS_URL + 'blacklist', { params })
.pipe(
map(res => this.restExtractor.convertResultListDateToHuman(res)),
catchError(res => this.restExtractor.handleError(res))
)
}
getAutoBlacklistedAsVideoList (videoPagination: ComponentPaginationLight): Observable<ResultList<Video>> {
const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
// prioritize first created since waiting longest
const AUTO_BLACKLIST_SORT = 'createdAt'
let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, AUTO_BLACKLIST_SORT)
params = params.set('type', VideoBlacklistType.AUTO_BEFORE_PUBLISHED.toString())
return this.authHttp.get<ResultList<VideoBlacklist>>(VideoBlacklistService.BASE_VIDEOS_URL + 'blacklist', { params })
.pipe(
map(res => {
return {
total: res.total,
data: res.data.map(videoBlacklist => new Video(videoBlacklist.video))
}
}),
catchError(res => this.restExtractor.handleError(res))
)
}
removeVideoFromBlacklist (videoIdArgs: number | number[]) {
const videoIds = Array.isArray(videoIdArgs) ? videoIdArgs : [ videoIdArgs ]
return observableFrom(videoIds)
.pipe(
concatMap(id => this.authHttp.delete(VideoBlacklistService.BASE_VIDEOS_URL + id + '/blacklist')),
toArray(),
catchError(err => this.restExtractor.handleError(err))
)
}
blacklistVideo (videoId: number, reason: string, unfederate: boolean) {
const body = {
unfederate,
reason
}
return this.authHttp.post(VideoBlacklistService.BASE_VIDEOS_URL + videoId + '/blacklist', body)
.pipe(
map(this.restExtractor.extractDataBool),
catchError(res => this.restExtractor.handleError(res))
)
}
}

View File

@ -0,0 +1 @@
export * from './video-block.service'

View File

@ -0,0 +1,77 @@
import { catchError, map, concatMap, toArray } from 'rxjs/operators'
import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { SortMeta } from 'primeng/api'
import { from as observableFrom, Observable } from 'rxjs'
import { VideoBlocklist, VideoBlockType, ResultList } from '../../../../../shared'
import { environment } from '../../../environments/environment'
import { RestExtractor, RestPagination, RestService } from '../rest'
@Injectable()
export class VideoBlockService {
private static BASE_VIDEOS_URL = environment.apiUrl + '/api/v1/videos/'
constructor (
private authHttp: HttpClient,
private restService: RestService,
private restExtractor: RestExtractor
) {}
listBlocks (options: {
pagination: RestPagination
sort: SortMeta
search?: string
type?: VideoBlockType
}): Observable<ResultList<VideoBlocklist>> {
const { pagination, sort, search, type } = options
let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort)
if (search) {
const filters = this.restService.parseQueryStringFilter(search, {
type: {
prefix: 'type:',
handler: v => {
if (v === 'manual') return VideoBlockType.MANUAL
if (v === 'auto') return VideoBlockType.AUTO_BEFORE_PUBLISHED
return undefined
}
}
})
params = this.restService.addObjectParams(params, filters)
}
return this.authHttp.get<ResultList<VideoBlocklist>>(VideoBlockService.BASE_VIDEOS_URL + 'blacklist', { params })
.pipe(
map(res => this.restExtractor.convertResultListDateToHuman(res)),
catchError(res => this.restExtractor.handleError(res))
)
}
unblockVideo (videoIdArgs: number | number[]) {
const videoIds = Array.isArray(videoIdArgs) ? videoIdArgs : [ videoIdArgs ]
return observableFrom(videoIds)
.pipe(
concatMap(id => this.authHttp.delete(VideoBlockService.BASE_VIDEOS_URL + id + '/blacklist')),
toArray(),
catchError(err => this.restExtractor.handleError(err))
)
}
blockVideo (videoId: number, reason: string, unfederate: boolean) {
const body = {
unfederate,
reason
}
return this.authHttp.post(VideoBlockService.BASE_VIDEOS_URL + videoId + '/blacklist', body)
.pipe(
map(this.restExtractor.extractDataBool),
catchError(res => this.restExtractor.handleError(res))
)
}
}

View File

@ -39,7 +39,7 @@
[fitWidth]="true"
[video]="video" [user]="user" [ownerDisplayType]="ownerDisplayType"
[displayVideoActions]="displayVideoActions" [displayOptions]="displayOptions"
(videoBlacklisted)="removeVideoFromArray(video)" (videoRemoved)="removeVideoFromArray(video)"
(videoBlocked)="removeVideoFromArray(video)" (videoRemoved)="removeVideoFromArray(video)"
>
</my-video-miniature>
</ng-container>

View File

@ -1,12 +1,12 @@
<ng-template #modal>
<div class="modal-header">
<h4 i18n class="modal-title">Blacklist video</h4>
<h4 i18n class="modal-title">Blocklist video</h4>
<my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
</div>
<div class="modal-body">
<form novalidate [formGroup]="form" (ngSubmit)="blacklist()">
<form novalidate [formGroup]="form" (ngSubmit)="block()">
<div class="form-group">
<textarea
i18n-placeholder placeholder="Reason..." formControlName="reason"

View File

@ -1,24 +1,24 @@
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
import { Notifier, RedirectService } from '@app/core'
import { VideoBlacklistService } from '../../../shared/video-blacklist'
import { VideoBlockService } from '../../video-block'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
import { FormReactive, VideoBlacklistValidatorsService } from '@app/shared/forms'
import { FormReactive, VideoBlockValidatorsService } from '@app/shared/forms'
import { Video } from '@app/shared/video/video.model'
@Component({
selector: 'my-video-blacklist',
templateUrl: './video-blacklist.component.html',
styleUrls: [ './video-blacklist.component.scss' ]
selector: 'my-video-block',
templateUrl: './video-block.component.html',
styleUrls: [ './video-block.component.scss' ]
})
export class VideoBlacklistComponent extends FormReactive implements OnInit {
export class VideoBlockComponent extends FormReactive implements OnInit {
@Input() video: Video = null
@ViewChild('modal', { static: true }) modal: NgbModal
@Output() videoBlacklisted = new EventEmitter()
@Output() videoBlocked = new EventEmitter()
error: string = null
@ -27,10 +27,9 @@ export class VideoBlacklistComponent extends FormReactive implements OnInit {
constructor (
protected formValidatorService: FormValidatorService,
private modalService: NgbModal,
private videoBlacklistValidatorsService: VideoBlacklistValidatorsService,
private videoBlacklistService: VideoBlacklistService,
private videoBlockValidatorsService: VideoBlockValidatorsService,
private videoBlocklistService: VideoBlockService,
private notifier: Notifier,
private redirectService: RedirectService,
private i18n: I18n
) {
super()
@ -40,7 +39,7 @@ export class VideoBlacklistComponent extends FormReactive implements OnInit {
const defaultValues = { unfederate: 'true' }
this.buildForm({
reason: this.videoBlacklistValidatorsService.VIDEO_BLACKLIST_REASON,
reason: this.videoBlockValidatorsService.VIDEO_BLOCK_REASON,
unfederate: null
}, defaultValues)
}
@ -54,20 +53,20 @@ export class VideoBlacklistComponent extends FormReactive implements OnInit {
this.openedModal = null
}
blacklist () {
block () {
const reason = this.form.value[ 'reason' ] || undefined
const unfederate = this.video.isLocal ? this.form.value[ 'unfederate' ] : undefined
this.videoBlacklistService.blacklistVideo(this.video.id, reason, unfederate)
this.videoBlocklistService.blockVideo(this.video.id, reason, unfederate)
.subscribe(
() => {
this.notifier.success(this.i18n('Video blacklisted.'))
this.notifier.success(this.i18n('Video blocked.'))
this.hide()
this.video.blacklisted = true
this.video.blacklistedReason = reason
this.video.blockedReason = reason
this.videoBlacklisted.emit()
this.videoBlocked.emit()
},
err => this.notifier.error(err.message)

View File

@ -17,5 +17,5 @@
<my-video-download #videoDownloadModal></my-video-download>
<my-video-report #videoReportModal [video]="video"></my-video-report>
<my-video-blacklist #videoBlacklistModal [video]="video" (videoBlacklisted)="onVideoBlacklisted()"></my-video-blacklist>
<my-video-block #videoBlockModal [video]="video" (videoBlocked)="onVideoBlocked()"></my-video-block>
</ng-container>

View File

@ -9,8 +9,8 @@ import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
import { VideoAddToPlaylistComponent } from '@app/shared/video-playlist/video-add-to-playlist.component'
import { VideoDownloadComponent } from '@app/shared/video/modals/video-download.component'
import { VideoReportComponent } from '@app/shared/video/modals/video-report.component'
import { VideoBlacklistComponent } from '@app/shared/video/modals/video-blacklist.component'
import { VideoBlacklistService } from '@app/shared/video-blacklist'
import { VideoBlockComponent } from '@app/shared/video/modals/video-block.component'
import { VideoBlockService } from '@app/shared/video-block'
import { ScreenService } from '@app/shared/misc/screen.service'
import { VideoCaption } from '@shared/models'
import { RedundancyService } from '@app/shared/video/redundancy.service'
@ -36,7 +36,7 @@ export class VideoActionsDropdownComponent implements OnChanges {
@ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent
@ViewChild('videoReportModal') videoReportModal: VideoReportComponent
@ViewChild('videoBlacklistModal') videoBlacklistModal: VideoBlacklistComponent
@ViewChild('videoBlockModal') videoBlockModal: VideoBlockComponent
@Input() video: Video | VideoDetails
@Input() videoCaptions: VideoCaption[] = []
@ -59,8 +59,8 @@ export class VideoActionsDropdownComponent implements OnChanges {
@Input() buttonDirection: DropdownDirection = 'vertical'
@Output() videoRemoved = new EventEmitter()
@Output() videoUnblacklisted = new EventEmitter()
@Output() videoBlacklisted = new EventEmitter()
@Output() videoUnblocked = new EventEmitter()
@Output() videoBlocked = new EventEmitter()
@Output() modalOpened = new EventEmitter()
videoActions: DropdownAction<{ video: Video }>[][] = []
@ -71,7 +71,7 @@ export class VideoActionsDropdownComponent implements OnChanges {
private authService: AuthService,
private notifier: Notifier,
private confirmService: ConfirmService,
private videoBlacklistService: VideoBlacklistService,
private videoBlocklistService: VideoBlockService,
private screenService: ScreenService,
private videoService: VideoService,
private redundancyService: RedundancyService,
@ -117,10 +117,10 @@ export class VideoActionsDropdownComponent implements OnChanges {
this.videoReportModal.show()
}
showBlacklistModal () {
showBlockModal () {
this.modalOpened.emit()
this.videoBlacklistModal.show()
this.videoBlockModal.show()
}
/* Actions checker */
@ -133,12 +133,12 @@ export class VideoActionsDropdownComponent implements OnChanges {
return this.video.isRemovableBy(this.user)
}
isVideoBlacklistable () {
return this.video.isBlackistableBy(this.user)
isVideoBlockable () {
return this.video.isBlockableBy(this.user)
}
isVideoUnblacklistable () {
return this.video.isUnblacklistableBy(this.user)
isVideoUnblockable () {
return this.video.isUnblockableBy(this.user)
}
isVideoDownloadable () {
@ -151,22 +151,22 @@ export class VideoActionsDropdownComponent implements OnChanges {
/* Action handlers */
async unblacklistVideo () {
async unblockVideo () {
const confirmMessage = this.i18n(
'Do you really want to remove this video from the blacklist? It will be available again in the videos list.'
'Do you really want to unblock this video? It will be available again in the videos list.'
)
const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblacklist'))
const res = await this.confirmService.confirm(confirmMessage, this.i18n('Unblock'))
if (res === false) return
this.videoBlacklistService.removeVideoFromBlacklist(this.video.id).subscribe(
this.videoBlocklistService.unblockVideo(this.video.id).subscribe(
() => {
this.notifier.success(this.i18n('Video {{name}} removed from the blacklist.', { name: this.video.name }))
this.notifier.success(this.i18n('Video {{name}} unblocked.', { name: this.video.name }))
this.video.blacklisted = false
this.video.blacklistedReason = null
this.video.blockedReason = null
this.videoUnblacklisted.emit()
this.videoUnblocked.emit()
},
err => this.notifier.error(err.message)
@ -203,8 +203,8 @@ export class VideoActionsDropdownComponent implements OnChanges {
)
}
onVideoBlacklisted () {
this.videoBlacklisted.emit()
onVideoBlocked () {
this.videoBlocked.emit()
}
getPlaylistDropdownPlacement () {
@ -239,16 +239,16 @@ export class VideoActionsDropdownComponent implements OnChanges {
isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.update && this.isVideoUpdatable()
},
{
label: this.i18n('Blacklist'),
handler: () => this.showBlacklistModal(),
label: this.i18n('Block'),
handler: () => this.showBlockModal(),
iconName: 'no',
isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.blacklist && this.isVideoBlacklistable()
isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.blacklist && this.isVideoBlockable()
},
{
label: this.i18n('Unblacklist'),
handler: () => this.unblacklistVideo(),
label: this.i18n('Unblock'),
handler: () => this.unblockVideo(),
iconName: 'undo',
isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.blacklist && this.isVideoUnblacklistable()
isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.blacklist && this.isVideoUnblockable()
},
{
label: this.i18n('Mirror'),

View File

@ -43,9 +43,9 @@
</div>
</div>
<div *ngIf="displayOptions.blacklistInfo && video.blacklisted" class="video-info-blacklisted">
<span class="blacklisted-label" i18n>Blacklisted</span>
<span class="blacklisted-reason" *ngIf="video.blacklistedReason">{{ video.blacklistedReason }}</span>
<div *ngIf="displayOptions.blacklistInfo && video.blacklisted" class="video-info-blocked">
<span class="blocked-label" i18n>Blocked</span>
<span class="blocked-reason" *ngIf="video.blockedReason">{{ video.blockedReason }}</span>
</div>
<div i18n *ngIf="displayOptions.nsfw && video.nsfw" class="video-info-nsfw">
@ -57,7 +57,7 @@
<!-- FIXME: remove bottom placement when overflow is fixed in bootstrap dropdown: https://github.com/ng-bootstrap/ng-bootstrap/issues/3495 -->
<my-video-actions-dropdown
*ngIf="showActions" [video]="video" [displayOptions]="videoActionsDisplayOptions" placement="bottom-left bottom-right left auto"
(videoRemoved)="onVideoRemoved()" (videoBlacklisted)="onVideoBlacklisted()" (videoUnblacklisted)="onVideoUnblacklisted()"
(videoRemoved)="onVideoRemoved()" (videoBlocked)="onVideoBlocked()" (videoUnblocked)="onVideoUnblocked()"
></my-video-actions-dropdown>
</div>
</div>

View File

@ -45,15 +45,15 @@ $more-margin-right: 15px;
}
.video-info-privacy,
.video-info-blacklisted .blacklisted-label,
.video-info-blocked .blocked-label,
.video-info-nsfw {
font-weight: $font-semibold;
}
.video-info-blacklisted {
.video-info-blocked {
color: red;
.blacklisted-reason::before {
.blocked-reason::before {
content: ' - ';
}
}
@ -160,7 +160,7 @@ $more-margin-right: 15px;
margin-top: 5px;
}
.video-info-blacklisted {
.video-info-blocked {
margin-top: 3px;
}
}

View File

@ -59,8 +59,8 @@ export class VideoMiniatureComponent implements OnInit {
@Input() useLazyLoadUrl = false
@Output() videoBlacklisted = new EventEmitter()
@Output() videoUnblacklisted = new EventEmitter()
@Output() videoBlocked = new EventEmitter()
@Output() videoUnblocked = new EventEmitter()
@Output() videoRemoved = new EventEmitter()
videoActionsDisplayOptions: VideoActionsDisplayType = {
@ -184,12 +184,12 @@ export class VideoMiniatureComponent implements OnInit {
this.loadWatchLater()
}
onVideoBlacklisted () {
this.videoBlacklisted.emit()
onVideoBlocked () {
this.videoBlocked.emit()
}
onVideoUnblacklisted () {
this.videoUnblacklisted.emit()
onVideoUnblocked () {
this.videoUnblocked.emit()
}
onVideoRemoved () {

View File

@ -54,7 +54,7 @@ export class Video implements VideoServerModel {
state?: VideoConstant<VideoState>
scheduledUpdate?: VideoScheduleUpdate
blacklisted?: boolean
blacklistedReason?: string
blockedReason?: string
account: {
id: number
@ -140,7 +140,7 @@ export class Video implements VideoServerModel {
if (this.state) this.state.label = peertubeTranslate(this.state.label, translations)
this.blacklisted = hash.blacklisted
this.blacklistedReason = hash.blacklistedReason
this.blockedReason = hash.blacklistedReason
this.userHistory = hash.userHistory
@ -163,12 +163,12 @@ export class Video implements VideoServerModel {
return user && this.isLocal === true && (this.account.name === user.username || user.hasRight(UserRight.REMOVE_ANY_VIDEO))
}
isBlackistableBy (user: AuthUser) {
return this.blacklisted !== true && user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true
isBlockableBy (user: AuthUser) {
return this.blacklisted !== true && user && user.hasRight(UserRight.MANAGE_VIDEO_BLOCKS) === true
}
isUnblacklistableBy (user: AuthUser) {
return this.blacklisted === true && user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true
isUnblockableBy (user: AuthUser) {
return this.blacklisted === true && user && user.hasRight(UserRight.MANAGE_VIDEO_BLOCKS) === true
}
isUpdatableBy (user: AuthUser) {

View File

@ -29,8 +29,8 @@
</div>
<div class="col-md-12 alert alert-danger" *ngIf="video?.blacklisted">
<div class="blacklisted-label" i18n>This video is blacklisted.</div>
{{ video.blacklistedReason }}
<div class="blocked-label" i18n>This video is blocked.</div>
{{ video.blockedReason }}
</div>
</div>

View File

@ -45,7 +45,7 @@ $video-info-margin-left: 44px;
}
}
.blacklisted-label {
.blocked-label {
font-weight: $font-semibold;
}

View File

@ -335,7 +335,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
this.videoCaptionService.listCaptions(videoId)
])
.pipe(
// If 401, the video is private or blacklisted so redirect to 404
// If 401, the video is private or blocklisted so redirect to 404
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 401, 403, 404 ]))
)
.subscribe(([ video, captionsResult ]) => {
@ -364,7 +364,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
this.playlistService.getVideoPlaylist(playlistId)
.pipe(
// If 401, the video is private or blacklisted so redirect to 404
// If 401, the video is private or blocklisted so redirect to 404
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 401, 403, 404 ]))
)
.subscribe(playlist => {

View File

@ -13,7 +13,7 @@
</div>
<div *ngFor="let video of (videos$ | async); let i = index; let length = count">
<my-video-miniature [displayOptions]="displayOptions" [video]="video" [user]="user" (videoBlacklisted)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()">
<my-video-miniature [displayOptions]="displayOptions" [video]="video" [user]="user" (videoBlocked)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()">
</my-video-miniature>
<hr *ngIf="!playlist && i == 0 && length > 1" />

View File

@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" width="24px" viewBox="0 0 24 24">
<defs/>
<g fill="none" fill-rule="evenodd">
<rect width="22" height="14" x="1" y="7" stroke="#000" stroke-width="2" rx="2"/>
<path fill="#000" d="M11 3h2v4h-2z"/>
<circle cx="12" cy="2" r="2" fill="#000"/>
<circle cx="18" cy="12" r="2" fill="#000"/>
<circle cx="6" cy="12" r="2" fill="#000"/>
<path stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 15c0 1.1.9 2 2 2h0a2 2 0 002-2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 548 B

View File

@ -23,14 +23,14 @@ const blacklistRouter = express.Router()
blacklistRouter.post('/:videoId/blacklist',
authenticate,
ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST),
ensureUserHasRight(UserRight.MANAGE_VIDEO_BLOCKS),
asyncMiddleware(videosBlacklistAddValidator),
asyncMiddleware(addVideoToBlacklistController)
)
blacklistRouter.get('/blacklist',
authenticate,
ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST),
ensureUserHasRight(UserRight.MANAGE_VIDEO_BLOCKS),
paginationValidator,
blacklistSortValidator,
setBlacklistSort,
@ -41,14 +41,14 @@ blacklistRouter.get('/blacklist',
blacklistRouter.put('/:videoId/blacklist',
authenticate,
ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST),
ensureUserHasRight(UserRight.MANAGE_VIDEO_BLOCKS),
asyncMiddleware(videosBlacklistUpdateValidator),
asyncMiddleware(updateVideoBlacklistController)
)
blacklistRouter.delete('/:videoId/blacklist',
authenticate,
ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST),
ensureUserHasRight(UserRight.MANAGE_VIDEO_BLOCKS),
asyncMiddleware(videosBlacklistRemoveValidator),
asyncMiddleware(removeVideoFromBlacklistController)
)

View File

@ -1,7 +1,7 @@
import validator from 'validator'
import { exists } from './misc'
import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
import { VideoBlacklistType } from '../../../shared/models/videos'
import { VideoBlockType } from '../../../shared/models/videos'
const VIDEO_BLACKLIST_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_BLACKLIST
@ -10,7 +10,7 @@ function isVideoBlacklistReasonValid (value: string) {
}
function isVideoBlacklistTypeValid (value: any) {
return exists(value) && validator.isInt('' + value) && VideoBlacklistType[value] !== undefined
return exists(value) && validator.isInt('' + value) && VideoBlockType[value] !== undefined
}
// ---------------------------------------------------------------------------

View File

@ -1,5 +1,5 @@
import * as Sequelize from 'sequelize'
import { VideoBlacklistType } from '../../../shared/models/videos'
import { VideoBlockType } from '../../../shared/models/videos'
async function up (utils: {
transaction: Sequelize.Transaction
@ -18,7 +18,7 @@ async function up (utils: {
}
{
const query = 'UPDATE "videoBlacklist" SET "type" = ' + VideoBlacklistType.MANUAL
const query = 'UPDATE "videoBlacklist" SET "type" = ' + VideoBlockType.MANUAL
await utils.sequelize.query(query)
}

View File

@ -387,7 +387,7 @@ class Notifier {
}
private async notifyModeratorsOfVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo) {
const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST)
const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLOCKS)
if (moderators.length === 0) return
logger.info('Notifying %s moderators of video auto-blacklist %s.', moderators.length, videoBlacklist.Video.url)
@ -398,7 +398,7 @@ class Notifier {
async function notificationCreator (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
type: UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS,
type: UserNotificationType.VIDEO_AUTO_BLOCK_FOR_MODERATORS,
userId: user.id,
videoBlacklistId: videoBlacklist.id
})
@ -426,7 +426,7 @@ class Notifier {
async function notificationCreator (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
type: UserNotificationType.BLACKLIST_ON_MY_VIDEO,
type: UserNotificationType.BLOCK_ON_MY_VIDEO,
userId: user.id,
videoBlacklistId: videoBlacklist.id
})
@ -454,7 +454,7 @@ class Notifier {
async function notificationCreator (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
type: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO,
type: UserNotificationType.UNBLOCK_ON_MY_VIDEO,
userId: user.id,
videoId: video.id
})

View File

@ -8,7 +8,7 @@ import {
MVideoFullLight,
MVideoWithBlacklistLight
} from '@server/typings/models'
import { UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../shared/models'
import { UserRight, VideoBlacklistCreate, VideoBlockType } from '../../shared/models'
import { UserAdminFlag } from '../../shared/models/users/user-flag.model'
import { logger } from '../helpers/logger'
import { CONFIG } from '../initializers/config'
@ -39,7 +39,7 @@ async function autoBlacklistVideoIfNeeded (parameters: {
videoId: video.id,
unfederated: true,
reason: 'Auto-blacklisted. Moderator review required.',
type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
type: VideoBlockType.AUTO_BEFORE_PUBLISHED
}
const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate<MVideoBlacklistVideo>({
where: {
@ -64,7 +64,7 @@ async function blacklistVideo (videoInstance: MVideoAccountLight, options: Video
videoId: videoInstance.id,
unfederated: options.unfederate === true,
reason: options.reason,
type: VideoBlacklistType.MANUAL
type: VideoBlockType.MANUAL
}
)
blacklist.Video = videoInstance
@ -94,7 +94,7 @@ async function unblacklistVideo (videoBlacklist: MVideoBlacklist, video: MVideoF
Notifier.Instance.notifyOnVideoUnblacklist(video)
if (videoBlacklistType === VideoBlacklistType.AUTO_BEFORE_PUBLISHED) {
if (videoBlacklistType === VideoBlockType.AUTO_BEFORE_PUBLISHED) {
Notifier.Instance.notifyOnVideoPublishedAfterRemovedFromAutoBlacklist(video)
// Delete on object so new video notifications will send
@ -126,7 +126,7 @@ function autoBlacklistNeeded (parameters: {
if (!CONFIG.AUTO_BLACKLIST.VIDEOS.OF_USERS.ENABLED || !user) return false
if (isRemote || isNew === false) return false
if (user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) || user.hasAdminFlag(UserAdminFlag.BY_PASS_VIDEO_AUTO_BLACKLIST)) return false
if (user.hasRight(UserRight.MANAGE_VIDEO_BLOCKS) || user.hasAdminFlag(UserAdminFlag.BYPASS_VIDEO_AUTO_BLOCK)) return false
return true
}

View File

@ -739,11 +739,11 @@ export class UserModel extends Model<UserModel> {
const videoUserId = video.VideoChannel.Account.userId
if (video.isBlacklisted()) {
return videoUserId === this.id || this.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)
return videoUserId === this.id || this.hasRight(UserRight.MANAGE_VIDEO_BLOCKS)
}
if (video.privacy === VideoPrivacy.PRIVATE) {
return video.VideoChannel && videoUserId === this.id || this.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)
return video.VideoChannel && videoUserId === this.id || this.hasRight(UserRight.MANAGE_VIDEO_BLOCKS)
}
if (video.privacy === VideoPrivacy.INTERNAL) return true

View File

@ -3,7 +3,7 @@ import { getBlacklistSort, SortType, throwIfNotValid, searchAttribute } from '..
import { VideoModel } from './video'
import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel'
import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist'
import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos'
import { VideoBlocklist, VideoBlockType } from '../../../shared/models/videos'
import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
import { FindOptions } from 'sequelize'
import { ThumbnailModel } from './thumbnail'
@ -34,7 +34,7 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
@Default(null)
@Is('VideoBlacklistType', value => throwIfNotValid(value, isVideoBlacklistTypeValid, 'type'))
@Column
type: VideoBlacklistType
type: VideoBlockType
@CreatedAt
createdAt: Date
@ -59,7 +59,7 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
count: number
sort: SortType
search?: string
type?: VideoBlacklistType
type?: VideoBlockType
}) {
const { start, count, sort, search, type } = parameters
@ -119,7 +119,7 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
return VideoBlacklistModel.findOne(query)
}
toFormattedJSON (this: MVideoBlacklistFormattable): VideoBlacklist {
toFormattedJSON (this: MVideoBlacklistFormattable): VideoBlocklist {
return {
id: this.id,
createdAt: this.createdAt,

View File

@ -188,7 +188,7 @@ describe('Test users API validators', function () {
videoQuota: -1,
videoQuotaDaily: -1,
role: UserRole.USER,
adminFlags: UserAdminFlag.BY_PASS_VIDEO_AUTO_BLACKLIST
adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLOCK
}
it('Should fail with a too small username', async function () {

View File

@ -24,7 +24,7 @@ import {
checkBadSortPagination,
checkBadStartPagination
} from '../../../../shared/extra-utils/requests/check-api-params'
import { VideoBlacklistType, VideoDetails } from '../../../../shared/models/videos'
import { VideoBlockType, VideoDetails } from '../../../../shared/models/videos'
import { expect } from 'chai'
describe('Test video blacklist API validators', function () {
@ -243,7 +243,7 @@ describe('Test video blacklist API validators', function () {
})
it('Should succeed with the correct parameters', async function () {
await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, type: VideoBlacklistType.MANUAL })
await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, type: VideoBlockType.MANUAL })
})
})

View File

@ -265,7 +265,7 @@ describe('Test users', function () {
username: user.username,
password: user.password,
videoQuota: 2 * 1024 * 1024,
adminFlags: UserAdminFlag.BY_PASS_VIDEO_AUTO_BLACKLIST
adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLOCK
})
})
@ -292,7 +292,7 @@ describe('Test users', function () {
}
expect(userMe.adminFlags).to.be.undefined
expect(userGet.adminFlags).to.equal(UserAdminFlag.BY_PASS_VIDEO_AUTO_BLACKLIST)
expect(userGet.adminFlags).to.equal(UserAdminFlag.BYPASS_VIDEO_AUTO_BLOCK)
expect(userMe.specialPlaylists).to.have.lengthOf(1)
expect(userMe.specialPlaylists[0].type).to.equal(VideoPlaylistType.WATCH_LATER)

View File

@ -25,7 +25,7 @@ import {
} from '../../../../shared/extra-utils/index'
import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { VideoBlacklist, VideoBlacklistType } from '../../../../shared/models/videos'
import { VideoBlocklist, VideoBlockType } from '../../../../shared/models/videos'
import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
import { User, UserRole } from '../../../../shared/models/users'
import { getMagnetURI, getYoutubeVideoUrl, importVideo } from '../../../../shared/extra-utils/videos/video-imports'
@ -127,7 +127,7 @@ describe('Test video blacklist', function () {
const res = await getBlacklistedVideosList({
url: servers[0].url,
token: servers[0].accessToken,
type: VideoBlacklistType.MANUAL
type: VideoBlockType.MANUAL
})
expect(res.body.total).to.equal(2)
@ -141,7 +141,7 @@ describe('Test video blacklist', function () {
const res = await getBlacklistedVideosList({
url: servers[0].url,
token: servers[0].accessToken,
type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
type: VideoBlockType.AUTO_BEFORE_PUBLISHED
})
expect(res.body.total).to.equal(0)
@ -219,7 +219,7 @@ describe('Test video blacklist', function () {
})
describe('When removing a blacklisted video', function () {
let videoToRemove: VideoBlacklist
let videoToRemove: VideoBlocklist
let blacklist = []
it('Should not have any video in videos list on server 1', async function () {
@ -328,7 +328,7 @@ describe('Test video blacklist', function () {
it('Should have the correct video blacklist unfederate attribute', async function () {
const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: 'createdAt' })
const blacklistedVideos: VideoBlacklist[] = res.body.data
const blacklistedVideos: VideoBlocklist[] = res.body.data
const video3Blacklisted = blacklistedVideos.find(b => b.video.uuid === video3UUID)
const video4Blacklisted = blacklistedVideos.find(b => b.video.uuid === video4UUID)
@ -396,7 +396,7 @@ describe('Test video blacklist', function () {
url: servers[0].url,
accessToken: servers[0].accessToken,
username: user.username,
adminFlags: UserAdminFlag.BY_PASS_VIDEO_AUTO_BLACKLIST,
adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLOCK,
password: user.password,
role: UserRole.USER
})
@ -413,7 +413,7 @@ describe('Test video blacklist', function () {
const res = await getBlacklistedVideosList({
url: servers[0].url,
token: servers[0].accessToken,
type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
type: VideoBlockType.AUTO_BEFORE_PUBLISHED
})
expect(res.body.total).to.equal(1)
@ -434,7 +434,7 @@ describe('Test video blacklist', function () {
url: servers[0].url,
token: servers[0].accessToken,
sort: 'createdAt',
type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
type: VideoBlockType.AUTO_BEFORE_PUBLISHED
})
expect(res.body.total).to.equal(2)
@ -453,7 +453,7 @@ describe('Test video blacklist', function () {
url: servers[0].url,
token: servers[0].accessToken,
sort: 'createdAt',
type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
type: VideoBlockType.AUTO_BEFORE_PUBLISHED
})
expect(res.body.total).to.equal(3)
@ -466,7 +466,7 @@ describe('Test video blacklist', function () {
const res = await getBlacklistedVideosList({
url: servers[0].url,
token: servers[0].accessToken,
type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
type: VideoBlockType.AUTO_BEFORE_PUBLISHED
})
expect(res.body.total).to.equal(3)

View File

@ -455,7 +455,7 @@ async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUU
}
async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS
const notificationType = UserNotificationType.VIDEO_AUTO_BLOCK_FOR_MODERATORS
function notificationChecker (notification: UserNotification, type: CheckerType) {
if (type === 'presence') {
@ -486,8 +486,8 @@ async function checkNewBlacklistOnMyVideo (
blacklistType: 'blacklist' | 'unblacklist'
) {
const notificationType = blacklistType === 'blacklist'
? UserNotificationType.BLACKLIST_ON_MY_VIDEO
: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
? UserNotificationType.BLOCK_ON_MY_VIDEO
: UserNotificationType.UNBLOCK_ON_MY_VIDEO
function notificationChecker (notification: UserNotification) {
expect(notification).to.not.be.undefined

View File

@ -1,5 +1,5 @@
import * as request from 'supertest'
import { VideoBlacklistType } from '../../models/videos'
import { VideoBlockType } from '../../models/videos'
import { makeGetRequest } from '..'
function addVideoToBlacklist (
@ -45,7 +45,7 @@ function getBlacklistedVideosList (parameters: {
url: string
token: string
sort?: string
type?: VideoBlacklistType
type?: VideoBlockType
specialStatus?: number
}) {
const { url, token, sort, type, specialStatus = 200 } = parameters

View File

@ -1,4 +1,4 @@
export enum UserAdminFlag {
NONE = 0,
BY_PASS_VIDEO_AUTO_BLACKLIST = 1 << 0
BYPASS_VIDEO_AUTO_BLOCK = 1 << 0
}

View File

@ -5,8 +5,8 @@ export enum UserNotificationType {
NEW_COMMENT_ON_MY_VIDEO = 2,
NEW_VIDEO_ABUSE_FOR_MODERATORS = 3,
BLACKLIST_ON_MY_VIDEO = 4,
UNBLACKLIST_ON_MY_VIDEO = 5,
BLOCK_ON_MY_VIDEO = 4,
UNBLOCK_ON_MY_VIDEO = 5,
MY_VIDEO_PUBLISHED = 6,
@ -17,7 +17,7 @@ export enum UserNotificationType {
NEW_FOLLOW = 10,
COMMENT_MENTION = 11,
VIDEO_AUTO_BLACKLIST_FOR_MODERATORS = 12,
VIDEO_AUTO_BLOCK_FOR_MODERATORS = 12,
NEW_INSTANCE_FOLLOWER = 13,

View File

@ -20,7 +20,7 @@ export enum UserRight {
MANAGE_ACCOUNTS_BLOCKLIST,
MANAGE_SERVERS_BLOCKLIST,
MANAGE_VIDEO_BLACKLIST,
MANAGE_VIDEO_BLOCKS,
REMOVE_ANY_VIDEO,
REMOVE_ANY_VIDEO_CHANNEL,

View File

@ -19,7 +19,7 @@ const userRoleRights: { [ id in UserRole ]: UserRight[] } = {
],
[UserRole.MODERATOR]: [
UserRight.MANAGE_VIDEO_BLACKLIST,
UserRight.MANAGE_VIDEO_BLOCKS,
UserRight.MANAGE_VIDEO_ABUSES,
UserRight.REMOVE_ANY_VIDEO,
UserRight.REMOVE_ANY_VIDEO_CHANNEL,

View File

@ -1,15 +1,15 @@
import { Video } from '../video.model'
export enum VideoBlacklistType {
export enum VideoBlockType {
MANUAL = 1,
AUTO_BEFORE_PUBLISHED = 2
}
export interface VideoBlacklist {
export interface VideoBlocklist {
id: number
unfederated: boolean
reason?: string
type: VideoBlacklistType
type: VideoBlockType
video: Video