Improve blacklist management
This commit is contained in:
parent
26b7305a23
commit
191764f30b
|
@ -42,7 +42,7 @@
|
||||||
<td>{{ videoAbuse.createdAt }}</td>
|
<td>{{ videoAbuse.createdAt }}</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a [href]="videoAbuse.video.url" i18n-title title="Go to the video" target="_blank" rel="noopener noreferrer">
|
<a [href]="getVideoUrl(videoAbuse)" i18n-title title="Go to the video" target="_blank" rel="noopener noreferrer">
|
||||||
{{ videoAbuse.video.name }}
|
{{ videoAbuse.video.name }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
|
import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
|
||||||
import { ConfirmService } from '@app/core'
|
import { ConfirmService } from '@app/core'
|
||||||
import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
|
import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
|
||||||
|
import { Video } from '@app/shared/video/video.model'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-abuse-list',
|
selector: 'my-video-abuse-list',
|
||||||
|
@ -79,6 +80,10 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
|
||||||
return videoAbuse.state.id === VideoAbuseState.REJECTED
|
return videoAbuse.state.id === VideoAbuseState.REJECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getVideoUrl (videoAbuse: VideoAbuse) {
|
||||||
|
return Video.buildClientUrl(videoAbuse.video.uuid)
|
||||||
|
}
|
||||||
|
|
||||||
async removeVideoAbuse (videoAbuse: VideoAbuse) {
|
async removeVideoAbuse (videoAbuse: VideoAbuse) {
|
||||||
const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse?'), this.i18n('Delete'))
|
const res = await this.confirmService.confirm(this.i18n('Do you really want to delete this abuse?'), this.i18n('Delete'))
|
||||||
if (res === false) return
|
if (res === false) return
|
||||||
|
|
|
@ -10,8 +10,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: 40px"></th>
|
<th style="width: 40px"></th>
|
||||||
<th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th>
|
<th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th>
|
||||||
<th i18n>NSFW</th>
|
<th i18n>Sensitive</th>
|
||||||
<th i18n>UUID</th>
|
|
||||||
<th i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
|
<th i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
|
||||||
<th style="width: 50px;"></th>
|
<th style="width: 50px;"></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -25,9 +24,13 @@
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>{{ videoBlacklist.video.name }}</td>
|
<td>
|
||||||
|
<a [href]="getVideoUrl(videoBlacklist)" i18n-title title="Go to the video" target="_blank" rel="noopener noreferrer">
|
||||||
|
{{ videoBlacklist.video.name }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
<td>{{ videoBlacklist.video.nsfw }}</td>
|
<td>{{ videoBlacklist.video.nsfw }}</td>
|
||||||
<td>{{ videoBlacklist.video.uuid }}</td>
|
|
||||||
<td>{{ videoBlacklist.createdAt }}</td>
|
<td>{{ videoBlacklist.createdAt }}</td>
|
||||||
|
|
||||||
<td class="action-cell">
|
<td class="action-cell">
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||||
import { NotificationsService } from 'angular2-notifications'
|
import { NotificationsService } from 'angular2-notifications'
|
||||||
import { ConfirmService } from '../../../core'
|
import { ConfirmService } from '../../../core'
|
||||||
import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared'
|
import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared'
|
||||||
import { BlacklistedVideo } from '../../../../../../shared'
|
import { VideoBlacklist } from '../../../../../../shared'
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
|
import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
|
||||||
|
import { Video } from '@app/shared/video/video.model'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-blacklist-list',
|
selector: 'my-video-blacklist-list',
|
||||||
|
@ -13,13 +14,13 @@ import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
|
||||||
styleUrls: [ './video-blacklist-list.component.scss' ]
|
styleUrls: [ './video-blacklist-list.component.scss' ]
|
||||||
})
|
})
|
||||||
export class VideoBlacklistListComponent extends RestTable implements OnInit {
|
export class VideoBlacklistListComponent extends RestTable implements OnInit {
|
||||||
blacklist: BlacklistedVideo[] = []
|
blacklist: VideoBlacklist[] = []
|
||||||
totalRecords = 0
|
totalRecords = 0
|
||||||
rowsPerPage = 10
|
rowsPerPage = 10
|
||||||
sort: SortMeta = { field: 'createdAt', order: 1 }
|
sort: SortMeta = { field: 'createdAt', order: 1 }
|
||||||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||||
|
|
||||||
videoBlacklistActions: DropdownAction<BlacklistedVideo>[] = []
|
videoBlacklistActions: DropdownAction<VideoBlacklist>[] = []
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private notificationsService: NotificationsService,
|
private notificationsService: NotificationsService,
|
||||||
|
@ -41,7 +42,11 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
|
||||||
this.loadSort()
|
this.loadSort()
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeVideoFromBlacklist (entry: BlacklistedVideo) {
|
getVideoUrl (videoBlacklist: VideoBlacklist) {
|
||||||
|
return Video.buildClientUrl(videoBlacklist.video.uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeVideoFromBlacklist (entry: VideoBlacklist) {
|
||||||
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 remove this video from the blacklist? It will be available again in the videos list.'
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
<a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a>
|
<a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a>
|
||||||
<span i18n class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span>
|
<span i18n class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span>
|
||||||
<div class="video-info-private">{{ video.privacy.label }}{{ getStateLabel(video) }}</div>
|
<div class="video-info-private">{{ video.privacy.label }}{{ getStateLabel(video) }}</div>
|
||||||
|
<div *ngIf="video.blacklisted" class="video-info-blacklisted">
|
||||||
|
<span class="blacklisted-label" i18n>Blacklisted</span>
|
||||||
|
<span class="blacklisted-reason" *ngIf="video.blacklistedReason">{{ video.blacklistedReason }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Display only once -->
|
<!-- Display only once -->
|
||||||
|
|
|
@ -76,12 +76,25 @@
|
||||||
font-weight: $font-semibold;
|
font-weight: $font-semibold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-info-date-views, .video-info-private {
|
.video-info-date-views,
|
||||||
|
.video-info-private,
|
||||||
|
.video-info-blacklisted {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
||||||
&.video-info-private {
|
&.video-info-private,
|
||||||
|
&.video-info-blacklisted .blacklisted-label {
|
||||||
font-weight: $font-semibold;
|
font-weight: $font-semibold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.video-info-blacklisted {
|
||||||
|
color: red;
|
||||||
|
|
||||||
|
.blacklisted-reason {
|
||||||
|
&::before {
|
||||||
|
content: ' - ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ div {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: 150px;
|
padding-top: 150px;
|
||||||
|
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ import { HttpClient, HttpParams } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { SortMeta } from 'primeng/components/common/sortmeta'
|
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||||
import { Observable } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
import { BlacklistedVideo, ResultList } from '../../../../../shared'
|
import { VideoBlacklist, ResultList } from '../../../../../shared'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
import { RestExtractor, RestPagination, RestService } from '../rest'
|
import { RestExtractor, RestPagination, RestService } from '../rest'
|
||||||
|
|
||||||
|
@ -17,11 +17,11 @@ export class VideoBlacklistService {
|
||||||
private restExtractor: RestExtractor
|
private restExtractor: RestExtractor
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
listBlacklist (pagination: RestPagination, sort: SortMeta): Observable<ResultList<BlacklistedVideo>> {
|
listBlacklist (pagination: RestPagination, sort: SortMeta): Observable<ResultList<VideoBlacklist>> {
|
||||||
let params = new HttpParams()
|
let params = new HttpParams()
|
||||||
params = this.restService.addRestGetParams(params, pagination, sort)
|
params = this.restService.addRestGetParams(params, pagination, sort)
|
||||||
|
|
||||||
return this.authHttp.get<ResultList<BlacklistedVideo>>(VideoBlacklistService.BASE_VIDEOS_URL + 'blacklist', { params })
|
return this.authHttp.get<ResultList<VideoBlacklist>>(VideoBlacklistService.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))
|
||||||
|
|
|
@ -44,7 +44,11 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
isBlackistableBy (user: AuthUser) {
|
isBlackistableBy (user: AuthUser) {
|
||||||
return user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true
|
return this.blacklisted !== true && user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true
|
||||||
|
}
|
||||||
|
|
||||||
|
isUnblacklistableBy (user: AuthUser) {
|
||||||
|
return this.blacklisted === true && user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true
|
||||||
}
|
}
|
||||||
|
|
||||||
isUpdatableBy (user: AuthUser) {
|
isUpdatableBy (user: AuthUser) {
|
||||||
|
|
|
@ -41,6 +41,8 @@ export class Video implements VideoServerModel {
|
||||||
waitTranscoding?: boolean
|
waitTranscoding?: boolean
|
||||||
state?: VideoConstant<VideoState>
|
state?: VideoConstant<VideoState>
|
||||||
scheduledUpdate?: VideoScheduleUpdate
|
scheduledUpdate?: VideoScheduleUpdate
|
||||||
|
blacklisted?: boolean
|
||||||
|
blacklistedReason?: string
|
||||||
|
|
||||||
account: {
|
account: {
|
||||||
id: number
|
id: number
|
||||||
|
@ -62,6 +64,10 @@ export class Video implements VideoServerModel {
|
||||||
avatar: Avatar
|
avatar: Avatar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static buildClientUrl (videoUUID: string) {
|
||||||
|
return '/videos/watch/' + videoUUID
|
||||||
|
}
|
||||||
|
|
||||||
private static createDurationString (duration: number) {
|
private static createDurationString (duration: number) {
|
||||||
const hours = Math.floor(duration / 3600)
|
const hours = Math.floor(duration / 3600)
|
||||||
const minutes = Math.floor((duration % 3600) / 60)
|
const minutes = Math.floor((duration % 3600) / 60)
|
||||||
|
@ -116,6 +122,9 @@ export class Video implements VideoServerModel {
|
||||||
|
|
||||||
this.scheduledUpdate = hash.scheduledUpdate
|
this.scheduledUpdate = hash.scheduledUpdate
|
||||||
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.blacklistedReason = hash.blacklistedReason
|
||||||
}
|
}
|
||||||
|
|
||||||
isVideoNSFWForUser (user: User, serverConfig: ServerConfig) {
|
isVideoNSFWForUser (user: User, serverConfig: ServerConfig) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div class="row">
|
<div class="root-row row">
|
||||||
<!-- We need the video container for videojs so we just hide it -->
|
<!-- We need the video container for videojs so we just hide it -->
|
||||||
<div id="video-element-wrapper">
|
<div id="video-element-wrapper">
|
||||||
<div *ngIf="remoteServerDown" class="remote-server-down">
|
<div *ngIf="remoteServerDown" class="remote-server-down">
|
||||||
|
@ -17,7 +17,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div i18n class="alert alert-info" *ngIf="hasVideoScheduledPublication()">
|
<div i18n class="alert alert-info" *ngIf="hasVideoScheduledPublication()">
|
||||||
This video will be published on {{ video.scheduledUpdate.updateAt | date: 'full' }}
|
This video will be published on {{ video.scheduledUpdate.updateAt | date: 'full' }}.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="alert alert-danger" *ngIf="video?.blacklisted">
|
||||||
|
<div class="blacklisted-label" i18n>This video is blacklisted.</div>
|
||||||
|
{{ video.blacklistedReason }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Video information -->
|
<!-- Video information -->
|
||||||
|
@ -98,6 +103,10 @@
|
||||||
<span class="icon icon-blacklist"></span> <ng-container i18n>Blacklist</ng-container>
|
<span class="icon icon-blacklist"></span> <ng-container i18n>Blacklist</ng-container>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a *ngIf="isVideoUnblacklistable()" class="dropdown-item" i18n-title title="Unblacklist this video" href="#" (click)="unblacklistVideo($event)">
|
||||||
|
<span class="icon icon-unblacklist"></span> <ng-container i18n>Unblacklist</ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
<a *ngIf="isVideoRemovable()" class="dropdown-item" i18n-title title="Delete this video" href="#" (click)="removeVideo($event)">
|
<a *ngIf="isVideoRemovable()" class="dropdown-item" i18n-title title="Delete this video" href="#" (click)="removeVideo($event)">
|
||||||
<span class="icon icon-delete"></span> <ng-container i18n>Delete</ng-container>
|
<span class="icon icon-delete"></span> <ng-container i18n>Delete</ng-container>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
@import '_variables';
|
@import '_variables';
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
|
|
||||||
|
.root-row {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blacklisted-label {
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
}
|
||||||
|
|
||||||
#video-element-wrapper {
|
#video-element-wrapper {
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -259,6 +267,10 @@
|
||||||
background-image: url('../../../assets/images/video/blacklist.svg');
|
background-image: url('../../../assets/images/video/blacklist.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.icon-unblacklist {
|
||||||
|
background-image: url('../../../assets/images/global/undo.svg');
|
||||||
|
}
|
||||||
|
|
||||||
&.icon-delete {
|
&.icon-delete {
|
||||||
background-image: url('../../../assets/images/global/delete-black.svg');
|
background-image: url('../../../assets/images/global/delete-black.svg');
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.videoCaptionService.listCaptions(uuid)
|
this.videoCaptionService.listCaptions(uuid)
|
||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ]))
|
// If 401, the video is private or blacklisted so redirect to 404
|
||||||
|
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 401, 404 ]))
|
||||||
)
|
)
|
||||||
.subscribe(([ video, captionsResult ]) => {
|
.subscribe(([ video, captionsResult ]) => {
|
||||||
const startTime = this.route.snapshot.queryParams.start
|
const startTime = this.route.snapshot.queryParams.start
|
||||||
|
@ -217,6 +218,31 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.videoBlacklistModal.show()
|
this.videoBlacklistModal.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async unblacklistVideo (event: Event) {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
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(this.video.id).subscribe(
|
||||||
|
() => {
|
||||||
|
this.notificationsService.success(
|
||||||
|
this.i18n('Success'),
|
||||||
|
this.i18n('Video {{name}} removed from the blacklist.', { name: this.video.name })
|
||||||
|
)
|
||||||
|
|
||||||
|
this.video.blacklisted = false
|
||||||
|
this.video.blacklistedReason = null
|
||||||
|
},
|
||||||
|
|
||||||
|
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
isUserLoggedIn () {
|
isUserLoggedIn () {
|
||||||
return this.authService.isLoggedIn()
|
return this.authService.isLoggedIn()
|
||||||
}
|
}
|
||||||
|
@ -229,6 +255,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
return this.video.isBlackistableBy(this.user)
|
return this.video.isBlackistableBy(this.user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isVideoUnblacklistable () {
|
||||||
|
return this.video.isUnblacklistableBy(this.user)
|
||||||
|
}
|
||||||
|
|
||||||
getVideoPoster () {
|
getVideoPoster () {
|
||||||
if (!this.video) return ''
|
if (!this.video) return ''
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Artboard-4" transform="translate(-180.000000, -115.000000)" fill="#000">
|
||||||
|
<g id="4" transform="translate(180.000000, 115.000000)">
|
||||||
|
<path d="M10,19 C10.5522847,19 11,19.4477153 11,20 C11,20.5522847 10.5522847,21 10,21 C9.99404288,21 9.98809793,20.9999479 9.98216558,20.9998442 C5.01980239,20.990358 1,16.9646166 1,12 C1,7.02943725 5.02943725,3 10,3 C14.9705627,3 19,7.02943725 19,12 L17,12 C17,8.13400675 13.8659932,5 10,5 C6.13400675,5 3,8.13400675 3,12 C3,15.8659932 6.13400675,19 10,19 Z M14,12 L22,12 L18,16 L14,12 Z" id="Combined-Shape" transform="translate(11.500000, 12.000000) scale(-1, 1) translate(-11.500000, -12.000000) "></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1014 B |
|
@ -1,5 +1,5 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { BlacklistedVideo, UserRight, VideoBlacklistCreate } from '../../../../shared'
|
import { VideoBlacklist, UserRight, VideoBlacklistCreate } from '../../../../shared'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { getFormattedObjects } from '../../../helpers/utils'
|
import { getFormattedObjects } from '../../../helpers/utils'
|
||||||
import {
|
import {
|
||||||
|
@ -87,7 +87,7 @@ async function updateVideoBlacklistController (req: express.Request, res: expres
|
||||||
async function listBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function listBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const resultList = await VideoBlacklistModel.listForApi(req.query.start, req.query.count, req.query.sort)
|
const resultList = await VideoBlacklistModel.listForApi(req.query.start, req.query.count, req.query.sort)
|
||||||
|
|
||||||
return res.json(getFormattedObjects<BlacklistedVideo, VideoBlacklistModel>(resultList.data, resultList.total))
|
return res.json(getFormattedObjects<VideoBlacklist, VideoBlacklistModel>(resultList.data, resultList.total))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeVideoFromBlacklistController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function removeVideoFromBlacklistController (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
|
|
|
@ -35,6 +35,8 @@ import { VideoShareModel } from '../../models/video/video-share'
|
||||||
import { authenticate } from '../oauth'
|
import { authenticate } from '../oauth'
|
||||||
import { areValidationErrors } from './utils'
|
import { areValidationErrors } from './utils'
|
||||||
import { cleanUpReqFiles } from '../../helpers/utils'
|
import { cleanUpReqFiles } from '../../helpers/utils'
|
||||||
|
import { VideoModel } from '../../models/video/video'
|
||||||
|
import { UserModel } from '../../models/account/user'
|
||||||
|
|
||||||
const videosAddValidator = getCommonVideoAttributes().concat([
|
const videosAddValidator = getCommonVideoAttributes().concat([
|
||||||
body('videofile')
|
body('videofile')
|
||||||
|
@ -131,7 +133,25 @@ const videosGetValidator = [
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await isVideoExist(req.params.id, res)) return
|
if (!await isVideoExist(req.params.id, res)) return
|
||||||
|
|
||||||
const video = res.locals.video
|
const video: VideoModel = res.locals.video
|
||||||
|
|
||||||
|
// Video private or blacklisted
|
||||||
|
if (video.privacy === VideoPrivacy.PRIVATE || video.VideoBlacklist) {
|
||||||
|
authenticate(req, res, () => {
|
||||||
|
const user: UserModel = res.locals.oauth.token.User
|
||||||
|
|
||||||
|
// Only the owner or a user that have blacklist rights can see the video
|
||||||
|
if (video.VideoChannel.Account.userId !== user.id && !user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) {
|
||||||
|
return res.status(403)
|
||||||
|
.json({ error: 'Cannot get this private or blacklisted video.' })
|
||||||
|
.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
return next()
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Video is public, anyone can access it
|
// Video is public, anyone can access it
|
||||||
if (video.privacy === VideoPrivacy.PUBLIC) return next()
|
if (video.privacy === VideoPrivacy.PUBLIC) return next()
|
||||||
|
@ -143,17 +163,6 @@ const videosGetValidator = [
|
||||||
// Don't leak this unlisted video
|
// Don't leak this unlisted video
|
||||||
return res.status(404).end()
|
return res.status(404).end()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Video is private, check the user
|
|
||||||
authenticate(req, res, () => {
|
|
||||||
if (video.VideoChannel.Account.userId !== res.locals.oauth.token.User.id) {
|
|
||||||
return res.status(403)
|
|
||||||
.json({ error: 'Cannot get this private video of another user' })
|
|
||||||
.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
return next()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,6 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
|
||||||
video: {
|
video: {
|
||||||
id: this.Video.id,
|
id: this.Video.id,
|
||||||
uuid: this.Video.uuid,
|
uuid: this.Video.uuid,
|
||||||
url: this.Video.url,
|
|
||||||
name: this.Video.name
|
name: this.Video.name
|
||||||
},
|
},
|
||||||
createdAt: this.createdAt
|
createdAt: this.createdAt
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { getSortOnModel, throwIfNotValid } from '../utils'
|
||||||
import { VideoModel } from './video'
|
import { VideoModel } from './video'
|
||||||
import { isVideoBlacklistReasonValid } from '../../helpers/custom-validators/video-blacklist'
|
import { isVideoBlacklistReasonValid } from '../../helpers/custom-validators/video-blacklist'
|
||||||
import { Emailer } from '../../lib/emailer'
|
import { Emailer } from '../../lib/emailer'
|
||||||
import { BlacklistedVideo } from '../../../shared/models/videos'
|
import { VideoBlacklist } from '../../../shared/models/videos'
|
||||||
import { CONSTRAINTS_FIELDS } from '../../initializers'
|
import { CONSTRAINTS_FIELDS } from '../../initializers'
|
||||||
|
|
||||||
@Table({
|
@Table({
|
||||||
|
@ -68,7 +68,12 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
|
||||||
offset: start,
|
offset: start,
|
||||||
limit: count,
|
limit: count,
|
||||||
order: getSortOnModel(sort.sortModel, sort.sortValue),
|
order: getSortOnModel(sort.sortModel, sort.sortValue),
|
||||||
include: [ { model: VideoModel } ]
|
include: [
|
||||||
|
{
|
||||||
|
model: VideoModel,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return VideoBlacklistModel.findAndCountAll(query)
|
return VideoBlacklistModel.findAndCountAll(query)
|
||||||
|
@ -90,7 +95,7 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
|
||||||
return VideoBlacklistModel.findOne(query)
|
return VideoBlacklistModel.findOne(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
toFormattedJSON (): BlacklistedVideo {
|
toFormattedJSON (): VideoBlacklist {
|
||||||
const video = this.Video
|
const video = this.Video
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -127,7 +127,8 @@ export enum ScopeNames {
|
||||||
WITH_ACCOUNT_DETAILS = 'WITH_ACCOUNT_DETAILS',
|
WITH_ACCOUNT_DETAILS = 'WITH_ACCOUNT_DETAILS',
|
||||||
WITH_TAGS = 'WITH_TAGS',
|
WITH_TAGS = 'WITH_TAGS',
|
||||||
WITH_FILES = 'WITH_FILES',
|
WITH_FILES = 'WITH_FILES',
|
||||||
WITH_SCHEDULED_UPDATE = 'WITH_SCHEDULED_UPDATE'
|
WITH_SCHEDULED_UPDATE = 'WITH_SCHEDULED_UPDATE',
|
||||||
|
WITH_BLACKLISTED = 'WITH_BLACKLISTED'
|
||||||
}
|
}
|
||||||
|
|
||||||
type AvailableForListOptions = {
|
type AvailableForListOptions = {
|
||||||
|
@ -374,6 +375,15 @@ type AvailableForListOptions = {
|
||||||
[ScopeNames.WITH_TAGS]: {
|
[ScopeNames.WITH_TAGS]: {
|
||||||
include: [ () => TagModel ]
|
include: [ () => TagModel ]
|
||||||
},
|
},
|
||||||
|
[ScopeNames.WITH_BLACKLISTED]: {
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
attributes: [ 'id', 'reason' ],
|
||||||
|
model: () => VideoBlacklistModel,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
[ScopeNames.WITH_FILES]: {
|
[ScopeNames.WITH_FILES]: {
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
|
@ -1004,7 +1014,13 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return VideoModel
|
return VideoModel
|
||||||
.scope([ ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT_DETAILS, ScopeNames.WITH_SCHEDULED_UPDATE ])
|
.scope([
|
||||||
|
ScopeNames.WITH_TAGS,
|
||||||
|
ScopeNames.WITH_BLACKLISTED,
|
||||||
|
ScopeNames.WITH_FILES,
|
||||||
|
ScopeNames.WITH_ACCOUNT_DETAILS,
|
||||||
|
ScopeNames.WITH_SCHEDULED_UPDATE
|
||||||
|
])
|
||||||
.findById(id, options)
|
.findById(id, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1030,7 +1046,13 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return VideoModel
|
return VideoModel
|
||||||
.scope([ ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT_DETAILS, ScopeNames.WITH_SCHEDULED_UPDATE ])
|
.scope([
|
||||||
|
ScopeNames.WITH_TAGS,
|
||||||
|
ScopeNames.WITH_BLACKLISTED,
|
||||||
|
ScopeNames.WITH_FILES,
|
||||||
|
ScopeNames.WITH_ACCOUNT_DETAILS,
|
||||||
|
ScopeNames.WITH_SCHEDULED_UPDATE
|
||||||
|
])
|
||||||
.findOne(options)
|
.findOne(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1276,7 +1298,8 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
toFormattedDetailsJSON (): VideoDetails {
|
toFormattedDetailsJSON (): VideoDetails {
|
||||||
const formattedJson = this.toFormattedJSON({
|
const formattedJson = this.toFormattedJSON({
|
||||||
additionalAttributes: {
|
additionalAttributes: {
|
||||||
scheduledUpdate: true
|
scheduledUpdate: true,
|
||||||
|
blacklistInfo: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,8 @@ function updateVideoBlacklist (url: string, token: string, videoId: number, reas
|
||||||
.send({ reason })
|
.send({ reason })
|
||||||
.set('Accept', 'application/json')
|
.set('Accept', 'application/json')
|
||||||
.set('Authorization', 'Bearer ' + token)
|
.set('Authorization', 'Bearer ' + token)
|
||||||
.expect(specialStatus)}
|
.expect(specialStatus)
|
||||||
|
}
|
||||||
|
|
||||||
function removeVideoFromBlacklist (url: string, token: string, videoId: number | string, specialStatus = 204) {
|
function removeVideoFromBlacklist (url: string, token: string, videoId: number | string, specialStatus = 204) {
|
||||||
const path = '/api/v1/videos/' + videoId + '/blacklist'
|
const path = '/api/v1/videos/' + videoId + '/blacklist'
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
export enum UserRight {
|
export enum UserRight {
|
||||||
ALL,
|
ALL,
|
||||||
|
|
||||||
MANAGE_USERS,
|
MANAGE_USERS,
|
||||||
MANAGE_SERVER_FOLLOW,
|
MANAGE_SERVER_FOLLOW,
|
||||||
MANAGE_VIDEO_ABUSES,
|
MANAGE_VIDEO_ABUSES,
|
||||||
MANAGE_VIDEO_BLACKLIST,
|
|
||||||
MANAGE_JOBS,
|
MANAGE_JOBS,
|
||||||
MANAGE_CONFIGURATION,
|
MANAGE_CONFIGURATION,
|
||||||
|
|
||||||
|
MANAGE_VIDEO_BLACKLIST,
|
||||||
|
|
||||||
REMOVE_ANY_VIDEO,
|
REMOVE_ANY_VIDEO,
|
||||||
REMOVE_ANY_VIDEO_CHANNEL,
|
REMOVE_ANY_VIDEO_CHANNEL,
|
||||||
REMOVE_ANY_VIDEO_COMMENT,
|
REMOVE_ANY_VIDEO_COMMENT,
|
||||||
|
|
|
@ -14,7 +14,6 @@ export interface VideoAbuse {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
uuid: string
|
uuid: string
|
||||||
url: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export interface BlacklistedVideo {
|
export interface VideoBlacklist {
|
||||||
id: number
|
id: number
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
|
|
Loading…
Reference in New Issue