diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.html b/client/src/app/+admin/follows/followers-list/followers-list.component.html
index c532b5f32..7455cdf2b 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.html
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.html
@@ -14,10 +14,10 @@
Follower handle |
- State |
- Score |
- Created |
- |
+ State |
+ Score |
+ Created |
+ |
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html
index cb62d52dd..f3bb7216b 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.html
+++ b/client/src/app/+admin/follows/following-list/following-list.component.html
@@ -20,10 +20,10 @@
Host |
- State |
- Created |
- Redundancy allowed |
- |
+ State |
+ Created |
+ Redundancy allowed |
+ |
diff --git a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.html b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.html
index 07362b3b9..a8dcc69d2 100644
--- a/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.html
+++ b/client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.html
@@ -17,11 +17,11 @@
>
- |
- Strategy |
+ |
+ Strategy |
Video name |
Video URL |
- Total size |
+ Total size |
|
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html
index 2efdd2bc3..e40c29abf 100644
--- a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html
+++ b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html
@@ -8,8 +8,8 @@
Account |
- Muted at |
- |
+ Muted at |
+ |
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.html b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.html
index cec703289..bf5c00918 100644
--- a/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.html
+++ b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.html
@@ -16,8 +16,8 @@
Instance |
- Muted at |
- |
+ Muted at |
+ |
diff --git a/client/src/app/+admin/moderation/moderation.component.scss b/client/src/app/+admin/moderation/moderation.component.scss
index 89e9b47d3..9af76d2dd 100644
--- a/client/src/app/+admin/moderation/moderation.component.scss
+++ b/client/src/app/+admin/moderation/moderation.component.scss
@@ -1,5 +1,6 @@
@import 'variables';
@import 'mixins';
+@import 'miniature';
.form-sub-title {
flex-grow: 0;
@@ -22,12 +23,28 @@
}
}
+.video-abuse-states {
+ & > :not(:first-child) {
+ margin-left: .4rem;
+ }
+}
+
.screenratio {
position: relative;
width: 100%;
height: 0;
padding-bottom: 56%;
+ div {
+ @include miniature-thumbnail;
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ }
+
::ng-deep iframe {
position: absolute;
width: 100% !important;
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
index 4ecb395f8..3d356dc7c 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
+++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.html
@@ -48,9 +48,10 @@
-
+ |
+
|
@@ -75,7 +76,12 @@
-
+
+
+ The video was {{ videoAbuse.video.deleted ? 'deleted' : 'blacklisted' }}
+
+
+
|
diff --git a/server/helpers/middlewares/video-abuses.ts b/server/helpers/middlewares/video-abuses.ts
index 8a1d3d618..7553a5eb3 100644
--- a/server/helpers/middlewares/video-abuses.ts
+++ b/server/helpers/middlewares/video-abuses.ts
@@ -1,9 +1,17 @@
import { Response } from 'express'
import { VideoAbuseModel } from '../../models/video/video-abuse'
+import { fetchVideo } from '../video'
-async function doesVideoAbuseExist (abuseIdArg: number | string, videoId: number, res: Response) {
+async function doesVideoAbuseExist (abuseIdArg: number | string, videoUUID: string, res: Response) {
const abuseId = parseInt(abuseIdArg + '', 10)
- const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId)
+ let videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, null, videoUUID)
+
+ if (!videoAbuse) {
+ const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined
+ const video = await fetchVideo(videoUUID, 'all', userId)
+
+ if (video) videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, video.id)
+ }
if (videoAbuse === null) {
res.status(404)
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index bc6c58b06..c8623a5d4 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -14,7 +14,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
// ---------------------------------------------------------------------------
-const LAST_MIGRATION_VERSION = 485
+const LAST_MIGRATION_VERSION = 490
// ---------------------------------------------------------------------------
diff --git a/server/initializers/migrations/0490-abuse-video.ts b/server/initializers/migrations/0490-abuse-video.ts
new file mode 100644
index 000000000..26333feb5
--- /dev/null
+++ b/server/initializers/migrations/0490-abuse-video.ts
@@ -0,0 +1,28 @@
+import * as Sequelize from 'sequelize'
+
+async function up (utils: {
+ transaction: Sequelize.Transaction
+ queryInterface: Sequelize.QueryInterface
+ sequelize: Sequelize.Sequelize
+}): Promise {
+
+ const deletedVideo = {
+ type: Sequelize.JSONB,
+ allowNull: true
+ }
+ await utils.queryInterface.addColumn('videoAbuse', 'deletedVideo', deletedVideo)
+ await utils.sequelize.query(`ALTER TABLE "videoAbsue" ALTER COLUMN "videoId" DROP NOT NULL;`)
+ await utils.sequelize.query(`ALTER TABLE "videoAbuse" DROP CONSTRAINT IF EXISTS "videoAbuse_videoId_fkey";`)
+ await utils.sequelize.query(`ALTER TABLE "videoAbuse" ADD CONSTRAINT "videoAbuse_videoId_fkey"
+ FOREIGN KEY ("videoId") REFERENCES video(id) ON UPDATE CASCADE ON DELETE SET NULL;`)
+
+}
+
+function down (options) {
+ throw new Error('Not implemented.')
+}
+
+export {
+ up,
+ down
+}
diff --git a/server/middlewares/validators/videos/video-abuses.ts b/server/middlewares/validators/videos/video-abuses.ts
index a4aef4024..7c316fe13 100644
--- a/server/middlewares/validators/videos/video-abuses.ts
+++ b/server/middlewares/validators/videos/video-abuses.ts
@@ -32,8 +32,7 @@ const videoAbuseGetValidator = [
logger.debug('Checking videoAbuseGetValidator parameters', { parameters: req.body })
if (areValidationErrors(req, res)) return
- if (!await doesVideoExist(req.params.videoId, res)) return
- if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return
+ if (!await doesVideoAbuseExist(req.params.id, req.params.videoId, res)) return
return next()
}
@@ -53,8 +52,7 @@ const videoAbuseUpdateValidator = [
logger.debug('Checking videoAbuseUpdateValidator parameters', { parameters: req.body })
if (areValidationErrors(req, res)) return
- if (!await doesVideoExist(req.params.videoId, res)) return
- if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return
+ if (!await doesVideoAbuseExist(req.params.id, req.params.videoId, res)) return
return next()
}
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts
index da8c1577c..ea9856213 100644
--- a/server/models/video/video-abuse.ts
+++ b/server/models/video/video-abuse.ts
@@ -9,7 +9,7 @@ import {
import { AccountModel } from '../account/account'
import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils'
import { VideoModel } from './video'
-import { VideoAbuseState } from '../../../shared'
+import { VideoAbuseState, Video } from '../../../shared'
import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants'
import { MUserAccountId, MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo } from '../../typings/models'
import * as Bluebird from 'bluebird'
@@ -46,6 +46,11 @@ export class VideoAbuseModel extends Model {
@Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max))
moderationComment: string
+ @AllowNull(true)
+ @Default(null)
+ @Column(DataType.JSONB)
+ deletedVideo: Video
+
@CreatedAt
createdAt: Date
@@ -58,9 +63,9 @@ export class VideoAbuseModel extends Model {
@BelongsTo(() => AccountModel, {
foreignKey: {
- allowNull: false
+ allowNull: true
},
- onDelete: 'cascade'
+ onDelete: 'set null'
})
Account: AccountModel
@@ -70,17 +75,21 @@ export class VideoAbuseModel extends Model {
@BelongsTo(() => VideoModel, {
foreignKey: {
- allowNull: false
+ allowNull: true
},
- onDelete: 'cascade'
+ onDelete: 'set null'
})
Video: VideoModel
- static loadByIdAndVideoId (id: number, videoId: number): Bluebird {
+ static loadByIdAndVideoId (id: number, videoId?: number, uuid?: string): Bluebird {
+ const videoAttributes = {}
+ if (videoId) videoAttributes['videoId'] = videoId
+ if (uuid) videoAttributes['deletedVideo'] = { uuid }
+
const query = {
where: {
id,
- videoId
+ ...videoAttributes
}
}
return VideoAbuseModel.findOne(query)
@@ -112,7 +121,7 @@ export class VideoAbuseModel extends Model {
},
{
model: VideoModel,
- required: true
+ required: false
}
]
}
@@ -124,6 +133,10 @@ export class VideoAbuseModel extends Model {
}
toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse {
+ const video = this.Video
+ ? this.Video
+ : this.deletedVideo
+
return {
id: this.id,
reason: this.reason,
@@ -134,9 +147,11 @@ export class VideoAbuseModel extends Model {
},
moderationComment: this.moderationComment,
video: {
- id: this.Video.id,
- uuid: this.Video.uuid,
- name: this.Video.name
+ id: video.id,
+ uuid: video.uuid,
+ name: video.name,
+ nsfw: video.nsfw,
+ deleted: !this.Video
},
createdAt: this.createdAt
}
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 0e7505af5..2636ebd8e 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -628,9 +628,9 @@ export class VideoModel extends Model {
@HasMany(() => VideoAbuseModel, {
foreignKey: {
name: 'videoId',
- allowNull: false
+ allowNull: true
},
- onDelete: 'cascade'
+ onDelete: 'set null'
})
VideoAbuses: VideoAbuseModel[]
@@ -798,6 +798,35 @@ export class VideoModel extends Model {
ModelCache.Instance.invalidateCache('video', instance.id)
}
+ @BeforeDestroy
+ static async saveEssentialDataToAbuses (instance: VideoModel, options) {
+ const tasks: Promise[] = []
+
+ logger.info('Saving video abuses details of video %s.', instance.url)
+
+ if (!Array.isArray(instance.VideoAbuses)) {
+ instance.VideoAbuses = await instance.$get('VideoAbuses')
+
+ if (instance.VideoAbuses.length === 0) return undefined
+ }
+
+ const details = instance.toFormattedJSON()
+
+ for (const abuse of instance.VideoAbuses) {
+ tasks.push((_ => {
+ abuse.deletedVideo = details
+ return abuse.save({ transaction: options.transaction })
+ })())
+ }
+
+ Promise.all(tasks)
+ .catch(err => {
+ logger.error('Some errors when saving details of video %s in its abuses before destroy hook.', instance.uuid, { err })
+ })
+
+ return undefined
+ }
+
static listLocal (): Bluebird {
const query = {
where: {
diff --git a/server/typings/models/video/video-abuse.ts b/server/typings/models/video/video-abuse.ts
index 955ec4780..49bd1ff2e 100644
--- a/server/typings/models/video/video-abuse.ts
+++ b/server/typings/models/video/video-abuse.ts
@@ -31,4 +31,4 @@ export type MVideoAbuseAccountVideo =
export type MVideoAbuseFormattable =
MVideoAbuse &
Use<'Account', MAccountFormattable> &
- Use<'Video', Pick>
+ Use<'Video', Pick>
diff --git a/shared/models/videos/abuse/video-abuse.model.ts b/shared/models/videos/abuse/video-abuse.model.ts
index 4f668795a..b47ee05a0 100644
--- a/shared/models/videos/abuse/video-abuse.model.ts
+++ b/shared/models/videos/abuse/video-abuse.model.ts
@@ -14,6 +14,8 @@ export interface VideoAbuse {
id: number
name: string
uuid: string
+ nsfw: boolean
+ deleted: boolean
}
createdAt: Date