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 () { ngOnInit () {
if (this.hasUsersRight()) this.items.push({ label: this.i18n('Users'), routerLink: '/admin/users' }) 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.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.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.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' }) 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) return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES)
} }
hasVideoBlacklistRight () { hasVideoBlocklistRight () {
return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)
} }

View File

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

View File

@ -430,7 +430,7 @@
<div class="form-group"> <div class="form-group">
<my-peertube-checkbox <my-peertube-checkbox
inputName="autoBlacklistVideosOfUsersEnabled" formControlName="enabled" inputName="autoBlacklistVideosOfUsersEnabled" formControlName="enabled"
i18n-labelText labelText="Blacklist new videos automatically" i18n-labelText labelText="Block new videos automatically"
> >
<ng-container ngProjectAs="description"> <ng-container ngProjectAs="description">
<span i18n>Unless a user is marked as trusted, their videos will stay private until a moderator reviews them.</span> <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"> <div class="form-group">
<my-peertube-checkbox inputName="servicesTwitterWhitelisted" formControlName="whitelisted"> <my-peertube-checkbox inputName="servicesTwitterWhitelisted" formControlName="whitelisted">
<ng-template ptTemplate="label"> <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>
<ng-template ptTemplate="help"> <ng-template ptTemplate="help">
<ng-container i18n> <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 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 whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><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 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> <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-container>
</ng-template> </ng-template>
</my-peertube-checkbox> </my-peertube-checkbox>

View File

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

View File

@ -2,11 +2,9 @@
<div i18n class="form-sub-title">Moderation</div> <div i18n class="form-sub-title">Moderation</div>
<div class="admin-sub-nav"> <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="hasVideoBlocklistRight()" i18n routerLink="video-blocks/list" routerLinkActive="active">Video blocks</a>
<a *ngIf="autoBlacklistVideosEnabled && hasVideoBlacklistRight()" i18n routerLink="video-auto-blacklist/list" routerLinkActive="active">Auto-blacklisted videos</a>
<a *ngIf="hasAccountsBlocklistRight()" i18n routerLink="blocklist/accounts" routerLinkActive="active">Muted accounts</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' ] styleUrls: [ './moderation.component.scss' ]
}) })
export class ModerationComponent implements OnInit { export class ModerationComponent implements OnInit {
autoBlacklistVideosEnabled = false autoBlockVideosEnabled = false
constructor ( constructor (
private auth: AuthService, private auth: AuthService,
@ -16,7 +16,7 @@ export class ModerationComponent implements OnInit {
ngOnInit (): void { ngOnInit (): void {
this.serverService.getConfig() 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) return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES)
} }
hasVideoBlacklistRight () { hasVideoBlocklistRight () {
return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) 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 { UserRight } from '../../../../../shared'
import { UserRightGuard } from '@app/core' import { UserRightGuard } from '@app/core'
import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list' import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list'
import { VideoBlacklistListComponent } from '@app/+admin/moderation/video-blacklist-list' import { VideoBlockListComponent } from '@app/+admin/moderation/video-block-list'
import { VideoAutoBlacklistListComponent } from '@app/+admin/moderation/video-auto-blacklist-list'
import { ModerationComponent } from '@app/+admin/moderation/moderation.component' import { ModerationComponent } from '@app/+admin/moderation/moderation.component'
import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist' import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist'
@ -22,16 +21,6 @@ export const ModerationRoutes: Routes = [
redirectTo: 'video-abuses/list', redirectTo: 'video-abuses/list',
pathMatch: 'full' 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', path: 'video-abuses/list',
component: VideoAbuseListComponent, component: VideoAbuseListComponent,
@ -39,29 +28,38 @@ export const ModerationRoutes: Routes = [
data: { data: {
userRight: UserRight.MANAGE_VIDEO_ABUSES, userRight: UserRight.MANAGE_VIDEO_ABUSES,
meta: { 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', path: 'video-auto-blacklist/list',
component: VideoAutoBlacklistListComponent, redirectTo: 'video-blocks/list',
canActivate: [ UserRightGuard ], pathMatch: 'full'
data: {
userRight: UserRight.MANAGE_VIDEO_BLACKLIST,
meta: {
title: 'Auto-blacklisted videos'
}
}
}, },
{ {
path: 'video-blacklist/list', path: 'video-blacklist',
component: VideoBlacklistListComponent, redirectTo: 'video-blocks/list',
pathMatch: 'full'
},
{
path: 'video-blocks/list',
component: VideoBlockListComponent,
canActivate: [ UserRightGuard ], canActivate: [ UserRightGuard ],
data: { data: {
userRight: UserRight.MANAGE_VIDEO_BLACKLIST, userRight: UserRight.MANAGE_VIDEO_BLACKLIST,
meta: { meta: {
title: 'Blacklisted videos' title: 'Videos blocked'
} }
} }
}, },

View File

@ -69,7 +69,7 @@
<div class="screenratio"> <div class="screenratio">
<div *ngIf="videoAbuse.video.deleted || videoAbuse.video.blacklisted"> <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 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>
<div *ngIf="!videoAbuse.video.deleted && !videoAbuse.video.blacklisted" [innerHTML]="videoAbuse.embedHtml"></div> <div *ngIf="!videoAbuse.video.deleted && !videoAbuse.video.blacklisted" [innerHTML]="videoAbuse.embedHtml"></div>
</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: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: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': '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> <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'videoIs:deleted' }" class="dropdown-item" i18n>Reports with deleted videos</a>
</div> </div>
</div> </div>
@ -84,9 +84,9 @@
</div> </div>
<div class="video-table-video-text"> <div class="video-table-video-text">
<div> <div>
{{ videoAbuse.video.name }}
<span *ngIf="!videoAbuse.video.blacklisted" class="glyphicon glyphicon-new-window"></span> <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>
<div class="text-muted" i18n>by {{ videoAbuse.video.channel?.displayName }} on {{ videoAbuse.video.channel?.host }} </div> <div class="text-muted" i18n>by {{ videoAbuse.video.channel?.displayName }} on {{ videoAbuse.video.channel?.host }} </div>
</div> </div>

View File

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

View File

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

View File

@ -88,7 +88,7 @@ export abstract class UserEdit extends FormReactive implements OnInit {
} }
protected buildAdminFlags (formValue: any) { 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 () { protected buildQuotaOptions () {

View File

@ -56,7 +56,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
role: this.userValidatorsService.USER_ROLE, role: this.userValidatorsService.USER_ROLE,
videoQuota: this.userValidatorsService.USER_VIDEO_QUOTA, videoQuota: this.userValidatorsService.USER_VIDEO_QUOTA,
videoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY, videoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY,
byPassAutoBlacklist: null byPassAutoBlock: null
}, defaultValues) }, defaultValues)
this.paramsSub = this.route.params.subscribe(routeParams => { this.paramsSub = this.route.params.subscribe(routeParams => {
@ -125,7 +125,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
role: userJson.role.toString(), role: userJson.role.toString(),
videoQuota: userJson.videoQuota, videoQuota: userJson.videoQuota,
videoQuotaDaily: userJson.videoQuotaDaily, 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"> <div class="video" *ngFor="let video of videos">
<my-video-miniature <my-video-miniature
[video]="video" [displayAsRow]="true" [video]="video" [displayAsRow]="true"
(videoRemoved)="removeVideoFromArray(video)" (videoBlacklisted)="removeVideoFromArray(video)"></my-video-miniature> (videoRemoved)="removeVideoFromArray(video)" (videoBlocked)="removeVideoFromArray(video)"></my-video-miniature>
</div> </div>
</div> </div>

View File

@ -35,8 +35,8 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
newVideoFromSubscription: this.i18n('New video from your subscriptions'), newVideoFromSubscription: this.i18n('New video from your subscriptions'),
newCommentOnMyVideo: this.i18n('New comment on your video'), newCommentOnMyVideo: this.i18n('New comment on your video'),
videoAbuseAsModerator: this.i18n('New video abuse'), videoAbuseAsModerator: this.i18n('New video abuse'),
videoAutoBlacklistAsModerator: this.i18n('Video auto-blacklisted waiting review'), videoAutoBlacklistAsModerator: this.i18n('Video blocked automatically waiting review'),
blacklistOnMyVideo: this.i18n('One of your video is blacklisted/unblacklisted'), blacklistOnMyVideo: this.i18n('One of your video is blocked/unblocked'),
myVideoPublished: this.i18n('Video published (after transcoding/scheduled update)'), myVideoPublished: this.i18n('Video published (after transcoding/scheduled update)'),
myVideoImportFinished: this.i18n('Video import finished'), myVideoImportFinished: this.i18n('Video import finished'),
newUserRegistration: this.i18n('A new user registered on your instance'), 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_USERS]: '/admin/users',
[UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends', [UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends',
[UserRight.MANAGE_VIDEO_ABUSES]: '/admin/moderation/video-abuses', [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_JOBS]: '/admin/jobs',
[UserRight.MANAGE_CONFIGURATION]: '/admin/config' [UserRight.MANAGE_CONFIGURATION]: '/admin/config'
} }

View File

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

View File

@ -6,7 +6,7 @@ export * from './login-validators.service'
export * from './reset-password-validators.service' export * from './reset-password-validators.service'
export * from './user-validators.service' export * from './user-validators.service'
export * from './video-abuse-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-channel-validators.service'
export * from './video-comment-validators.service' export * from './video-comment-validators.service'
export * from './video-validators.service' export * from './video-validators.service'

View File

@ -4,15 +4,15 @@ import { Injectable } from '@angular/core'
import { BuildFormValidator } from '@app/shared' import { BuildFormValidator } from '@app/shared'
@Injectable() @Injectable()
export class VideoBlacklistValidatorsService { export class VideoBlockValidatorsService {
readonly VIDEO_BLACKLIST_REASON: BuildFormValidator readonly VIDEO_BLOCK_REASON: BuildFormValidator
constructor (private i18n: I18n) { constructor (private i18n: I18n) {
this.VIDEO_BLACKLIST_REASON = { this.VIDEO_BLOCK_REASON = {
VALIDATORS: [ Validators.minLength(2), Validators.maxLength(300) ], VALIDATORS: [ Validators.minLength(2), Validators.maxLength(300) ],
MESSAGES: { MESSAGES: {
'minlength': this.i18n('Blacklist reason must be at least 2 characters long.'), 'minlength': this.i18n('Block reason must be at least 2 characters long.'),
'maxlength': this.i18n('Blacklist reason cannot be more than 300 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, 'refresh': require('!!raw-loader?!../../../assets/images/global/refresh.svg').default,
'npm': require('!!raw-loader?!../../../assets/images/global/npm.svg').default, 'npm': require('!!raw-loader?!../../../assets/images/global/npm.svg').default,
'fullscreen': require('!!raw-loader?!../../../assets/images/global/fullscreen.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 export type GlobalIconName = keyof typeof icons

View File

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

View File

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

View File

@ -30,7 +30,7 @@
<my-global-icon iconName="undo"></my-global-icon> <my-global-icon iconName="undo"></my-global-icon>
<div class="message" i18n> <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> </div>
</ng-container> </ng-container>
@ -38,7 +38,7 @@
<my-global-icon iconName="no"></my-global-icon> <my-global-icon iconName="no"></my-global-icon>
<div class="message" i18n> <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> </div>
</ng-container> </ng-container>
@ -54,7 +54,7 @@
<my-global-icon iconName="no"></my-global-icon> <my-global-icon iconName="no"></my-global-icon>
<div class="message" i18n> <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> </div>
</ng-container> </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 { SortMeta } from 'primeng/api'
import { from as observableFrom, Observable } from 'rxjs' import { from as observableFrom, Observable } from 'rxjs'
import { VideoBlacklist, VideoBlacklistType, ResultList } from '../../../../../shared' import { VideoBlacklist, VideoBlacklistType, ResultList } from '../../../../../shared'
import { Video } from '../video/video.model'
import { environment } from '../../../environments/environment' import { environment } from '../../../environments/environment'
import { RestExtractor, RestPagination, RestService } from '../rest' import { RestExtractor, RestPagination, RestService } from '../rest'
import { ComponentPaginationLight } from '../rest/component-pagination.model'
@Injectable() @Injectable()
export class VideoBlacklistService { export class VideoBlockService {
private static BASE_VIDEOS_URL = environment.apiUrl + '/api/v1/videos/' private static BASE_VIDEOS_URL = environment.apiUrl + '/api/v1/videos/'
constructor ( constructor (
@ -19,9 +17,9 @@ export class VideoBlacklistService {
private restExtractor: RestExtractor private restExtractor: RestExtractor
) {} ) {}
listBlacklist (options: { listBlocks (options: {
pagination: RestPagination, pagination: RestPagination
sort: SortMeta, sort: SortMeta
search?: string search?: string
type?: VideoBlacklistType type?: VideoBlacklistType
}): Observable<ResultList<VideoBlacklist>> { }): Observable<ResultList<VideoBlacklist>> {
@ -30,57 +28,48 @@ export class VideoBlacklistService {
let params = new HttpParams() let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort) 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()) 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( .pipe(
map(res => this.restExtractor.convertResultListDateToHuman(res)), map(res => this.restExtractor.convertResultListDateToHuman(res)),
catchError(res => this.restExtractor.handleError(res)) catchError(res => this.restExtractor.handleError(res))
) )
} }
getAutoBlacklistedAsVideoList (videoPagination: ComponentPaginationLight): Observable<ResultList<Video>> { unblockVideo (videoIdArgs: number | number[]) {
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 ] const videoIds = Array.isArray(videoIdArgs) ? videoIdArgs : [ videoIdArgs ]
return observableFrom(videoIds) return observableFrom(videoIds)
.pipe( .pipe(
concatMap(id => this.authHttp.delete(VideoBlacklistService.BASE_VIDEOS_URL + id + '/blacklist')), concatMap(id => this.authHttp.delete(VideoBlockService.BASE_VIDEOS_URL + id + '/blacklist')),
toArray(), toArray(),
catchError(err => this.restExtractor.handleError(err)) catchError(err => this.restExtractor.handleError(err))
) )
} }
blacklistVideo (videoId: number, reason: string, unfederate: boolean) { blockVideo (videoId: number, reason: string, unfederate: boolean) {
const body = { const body = {
unfederate, unfederate,
reason reason
} }
return this.authHttp.post(VideoBlacklistService.BASE_VIDEOS_URL + videoId + '/blacklist', body) return this.authHttp.post(VideoBlockService.BASE_VIDEOS_URL + videoId + '/blacklist', body)
.pipe( .pipe(
map(this.restExtractor.extractDataBool), map(this.restExtractor.extractDataBool),
catchError(res => this.restExtractor.handleError(res)) catchError(res => this.restExtractor.handleError(res))

View File

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

View File

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

View File

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

View File

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

View File

@ -43,9 +43,9 @@
</div> </div>
</div> </div>
<div *ngIf="displayOptions.blacklistInfo && video.blacklisted" class="video-info-blacklisted"> <div *ngIf="displayOptions.blacklistInfo && video.blacklisted" class="video-info-blocked">
<span class="blacklisted-label" i18n>Blacklisted</span> <span class="blocked-label" i18n>Blocked</span>
<span class="blacklisted-reason" *ngIf="video.blacklistedReason">{{ video.blacklistedReason }}</span> <span class="blocked-reason" *ngIf="video.blockedReason">{{ video.blockedReason }}</span>
</div> </div>
<div i18n *ngIf="displayOptions.nsfw && video.nsfw" class="video-info-nsfw"> <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 --> <!-- FIXME: remove bottom placement when overflow is fixed in bootstrap dropdown: https://github.com/ng-bootstrap/ng-bootstrap/issues/3495 -->
<my-video-actions-dropdown <my-video-actions-dropdown
*ngIf="showActions" [video]="video" [displayOptions]="videoActionsDisplayOptions" placement="bottom-left bottom-right left auto" *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> ></my-video-actions-dropdown>
</div> </div>
</div> </div>

View File

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

View File

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

View File

@ -54,7 +54,7 @@ export class Video implements VideoServerModel {
state?: VideoConstant<VideoState> state?: VideoConstant<VideoState>
scheduledUpdate?: VideoScheduleUpdate scheduledUpdate?: VideoScheduleUpdate
blacklisted?: boolean blacklisted?: boolean
blacklistedReason?: string blockedReason?: string
account: { account: {
id: number id: number
@ -140,7 +140,7 @@ export class Video implements VideoServerModel {
if (this.state) this.state.label = peertubeTranslate(this.state.label, translations) if (this.state) this.state.label = peertubeTranslate(this.state.label, translations)
this.blacklisted = hash.blacklisted this.blacklisted = hash.blacklisted
this.blacklistedReason = hash.blacklistedReason this.blockedReason = hash.blacklistedReason
this.userHistory = hash.userHistory 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)) 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 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 return this.blacklisted === true && user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true
} }

View File

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

View File

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

View File

@ -335,7 +335,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
this.videoCaptionService.listCaptions(videoId) this.videoCaptionService.listCaptions(videoId)
]) ])
.pipe( .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 ])) catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 401, 403, 404 ]))
) )
.subscribe(([ video, captionsResult ]) => { .subscribe(([ video, captionsResult ]) => {
@ -364,7 +364,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
this.playlistService.getVideoPlaylist(playlistId) this.playlistService.getVideoPlaylist(playlistId)
.pipe( .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 ])) catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 401, 403, 404 ]))
) )
.subscribe(playlist => { .subscribe(playlist => {

View File

@ -13,7 +13,7 @@
</div> </div>
<div *ngFor="let video of (videos$ | async); let i = index; let length = count"> <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> </my-video-miniature>
<hr *ngIf="!playlist && i == 0 && length > 1" /> <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 (!CONFIG.AUTO_BLACKLIST.VIDEOS.OF_USERS.ENABLED || !user) return false
if (isRemote || isNew === false) 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 return true
} }

View File

@ -188,7 +188,7 @@ describe('Test users API validators', function () {
videoQuota: -1, videoQuota: -1,
videoQuotaDaily: -1, videoQuotaDaily: -1,
role: UserRole.USER, 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 () { it('Should fail with a too small username', async function () {

View File

@ -265,7 +265,7 @@ describe('Test users', function () {
username: user.username, username: user.username,
password: user.password, password: user.password,
videoQuota: 2 * 1024 * 1024, 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(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).to.have.lengthOf(1)
expect(userMe.specialPlaylists[0].type).to.equal(VideoPlaylistType.WATCH_LATER) 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, url: servers[0].url,
accessToken: servers[0].accessToken, accessToken: servers[0].accessToken,
username: user.username, username: user.username,
adminFlags: UserAdminFlag.BY_PASS_VIDEO_AUTO_BLACKLIST, adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST,
password: user.password, password: user.password,
role: UserRole.USER role: UserRole.USER
}) })

View File

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