Merge branch 'blacklist' into 'develop'

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

See merge request framasoft/peertube/PeerTube!30
This commit is contained in:
Chocobozzz 2020-06-11 10:36:04 +02:00
commit fa58a19819
58 changed files with 518 additions and 533 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,7 +36,7 @@ export class AdminComponent implements OnInit {
return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES)
}
hasVideoBlacklistRight () {
hasVideoBlocklistRight () {
return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)
}

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,7 +24,7 @@ export class ModerationComponent implements OnInit {
return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES)
}
hasVideoBlacklistRight () {
hasVideoBlocklistRight () {
return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)
}

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,
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 blocked.'))
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 unblocked.'))
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 { 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'
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: (VideoBlacklist & { reasonHtml?: string })[] = []
totalRecords = 0
sort: SortMeta = { field: 'createdAt', order: -1 }
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
blocklistTypeFilter: VideoBlacklistType = undefined
videoBlocklistActions: DropdownAction<VideoBlacklist>[][] = []
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.blocklistTypeFilter = VideoBlacklistType.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: VideoBlacklist) {
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: VideoBlacklist) {
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

@ -52,7 +52,7 @@ export class UserCreateComponent extends UserEdit implements OnInit {
role: this.userValidatorsService.USER_ROLE,
videoQuota: this.userValidatorsService.USER_VIDEO_QUOTA,
videoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY,
byPassAutoBlacklist: null
byPassAutoBlock: null
}, defaultValues)
}

View File

@ -164,7 +164,7 @@
<div class="form-group">
<my-peertube-checkbox
inputName="byPassAutoBlacklist" formControlName="byPassAutoBlacklist"
inputName="byPassAutoBlock" formControlName="byPassAutoBlock"
i18n-labelText labelText="Doesn't need review before a video goes public"
></my-peertube-checkbox>
</div>

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.byPassAutoBlock ? UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST : UserAdminFlag.NONE
}
protected buildQuotaOptions () {

View File

@ -56,7 +56,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
role: this.userValidatorsService.USER_ROLE,
videoQuota: this.userValidatorsService.USER_VIDEO_QUOTA,
videoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY,
byPassAutoBlacklist: null
byPassAutoBlock: null
}, defaultValues)
this.paramsSub = this.route.params.subscribe(routeParams => {
@ -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
byPassAutoBlock: userJson.adminFlags & UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST
})
}
}

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'),

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_BLACKLIST]: '/admin/moderation/video-blocks',
[UserRight.MANAGE_JOBS]: '/admin/jobs',
[UserRight.MANAGE_CONFIGURATION]: '/admin/config'
}

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

@ -30,7 +30,7 @@
<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>
@ -38,7 +38,7 @@
<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>
@ -54,7 +54,7 @@
<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">automatically blocked</a>
</div>
</ng-container>

View File

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

View File

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

View File

@ -4,13 +4,11 @@ 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 {
export class VideoBlockService {
private static BASE_VIDEOS_URL = environment.apiUrl + '/api/v1/videos/'
constructor (
@ -19,9 +17,9 @@ export class VideoBlacklistService {
private restExtractor: RestExtractor
) {}
listBlacklist (options: {
pagination: RestPagination,
sort: SortMeta,
listBlocks (options: {
pagination: RestPagination
sort: SortMeta
search?: string
type?: VideoBlacklistType
}): Observable<ResultList<VideoBlacklist>> {
@ -30,57 +28,48 @@ export class VideoBlacklistService {
let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort)
if (search) params = params.append('search', search)
if (search) {
const filters = this.restService.parseQueryStringFilter(search, {
type: {
prefix: 'type:',
handler: v => {
if (v === 'manual') return VideoBlacklistType.MANUAL
if (v === 'auto') return VideoBlacklistType.AUTO_BEFORE_PUBLISHED
return undefined
}
}
})
params = this.restService.addObjectParams(params, filters)
}
if (type) params = params.append('type', type.toString())
return this.authHttp.get<ResultList<VideoBlacklist>>(VideoBlacklistService.BASE_VIDEOS_URL + 'blacklist', { params })
return this.authHttp.get<ResultList<VideoBlacklist>>(VideoBlockService.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[]) {
unblockVideo (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')),
concatMap(id => this.authHttp.delete(VideoBlockService.BASE_VIDEOS_URL + id + '/blacklist')),
toArray(),
catchError(err => this.restExtractor.handleError(err))
)
}
blacklistVideo (videoId: number, reason: string, unfederate: boolean) {
blockVideo (videoId: number, reason: string, unfederate: boolean) {
const body = {
unfederate,
reason
}
return this.authHttp.post(VideoBlacklistService.BASE_VIDEOS_URL + videoId + '/blacklist', body)
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,11 +163,11 @@ export class Video implements VideoServerModel {
return user && this.isLocal === true && (this.account.name === user.username || user.hasRight(UserRight.REMOVE_ANY_VIDEO))
}
isBlackistableBy (user: AuthUser) {
isBlockableBy (user: AuthUser) {
return this.blacklisted !== true && user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true
}
isUnblacklistableBy (user: AuthUser) {
isUnblockableBy (user: AuthUser) {
return this.blacklisted === true && user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true
}

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 blocked 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 blocked 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

@ -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_BLACKLIST) || user.hasAdminFlag(UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST)) return false
return true
}

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_BLACKLIST
}
it('Should fail with a too small username', async function () {

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_BLACKLIST
})
})
@ -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_BLACKLIST)
expect(userMe.specialPlaylists).to.have.lengthOf(1)
expect(userMe.specialPlaylists[0].type).to.equal(VideoPlaylistType.WATCH_LATER)

View File

@ -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_BLACKLIST,
password: user.password,
role: UserRole.USER
})

View File

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