Improve blacklist management

This commit is contained in:
Chocobozzz 2018-08-14 09:08:47 +02:00
parent 26b7305a23
commit 191764f30b
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
23 changed files with 189 additions and 45 deletions

View File

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

View File

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

View File

@ -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">

View File

@ -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.'
) )

View File

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

View File

@ -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: ' - ';
}
}
}
} }
} }

View File

@ -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;
} }

View File

@ -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))

View File

@ -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) {

View File

@ -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) {

View File

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

View File

@ -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');
} }

View File

@ -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 ''

View File

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

View File

@ -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) {

View File

@ -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()
})
} }
] ]

View File

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

View File

@ -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 {

View File

@ -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
} }
}) })

View File

@ -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'

View File

@ -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,

View File

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

View File

@ -1,4 +1,4 @@
export interface BlacklistedVideo { export interface VideoBlacklist {
id: number id: number
createdAt: Date createdAt: Date
updatedAt: Date updatedAt: Date