diff --git a/client/src/app/+admin/moderation/moderation.component.scss b/client/src/app/+admin/moderation/moderation.component.scss index 9ceff1161..ef6a39b5d 100644 --- a/client/src/app/+admin/moderation/moderation.component.scss +++ b/client/src/app/+admin/moderation/moderation.component.scss @@ -7,19 +7,23 @@ margin-right: 30px; } -.moderation-expanded-label { - font-weight: $font-semibold; - display: inline-block; - vertical-align: top; - text-align: right; -} +.moderation-expanded { + font-size: 90%; -.moderation-expanded-text { - display: inline-block; - word-wrap: break-word; - - ::ng-deep p:last-child { - margin-bottom: 0px !important; + .moderation-expanded-label { + font-weight: $font-semibold; + display: inline-block; + vertical-align: top; + text-align: right; + } + + .moderation-expanded-text { + display: inline-flex; + word-wrap: break-word; + + ::ng-deep p:last-child { + margin-bottom: 0px !important; + } } } @@ -58,3 +62,9 @@ .chip { @include chip; } + +my-action-dropdown.show { + ::ng-deep .dropdown-root { + display: block !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 c1ce093d7..67ef28408 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 @@ -54,7 +54,13 @@
-
+
+ + {{ videoAbuse.nth }}/{{ videoAbuse.count }} +
{{ videoAbuse.video.name }} @@ -85,11 +91,14 @@ - + - + @@ -97,14 +106,59 @@ -
+
+
+
+ Reportee + +
+ Avatar +
+ {{ videoAbuse.video.channel.ownerAccount ? createByString(videoAbuse.video.channel.ownerAccount) : '' }} +
+
+ + {videoAbuse.countReportsForReportee, plural, =1 {1 report} other {{{ videoAbuse.countReportsForReportee }} reports}} + +
+
+
+ Updated + +
+ + +
+ Report
- Note: + Note
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.scss b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.scss index 9b60c39dc..b5dc53b3a 100644 --- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.scss +++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.scss @@ -9,6 +9,15 @@ } } +.video-abuse-date-updated { + font-size: 90%; + margin-top: .1rem; +} + +.video-abuse-links { + @include disable-default-a-behaviour; +} + .video-abuse-video-link { @include disable-outline; position: relative; @@ -32,6 +41,7 @@ display: inline-flex; justify-content: center; align-items: center; + position: relative; img { height: 100%; @@ -42,6 +52,17 @@ span { color: var(--inputPlaceholderColor); } + + .video-abuse-video-image-label { + @include static-thumbnail-overlay; + position: absolute; + border-radius: 3px; + font-size: 10px; + padding: 0 3px; + line-height: 1.3; + bottom: 2px; + right: 2px; + } } .video-abuse-video-text { diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts index 6dcf96ccf..e4e78cdf7 100644 --- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts +++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts @@ -46,7 +46,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit { private i18n: I18n, private markdownRenderer: MarkdownService, private sanitizer: DomSanitizer, - private route: ActivatedRoute, + private route: ActivatedRoute ) { super() @@ -223,7 +223,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit { } getVideoEmbed (videoAbuse: VideoAbuse) { - const absoluteAPIUrl = 'http://localhost:9000' || getAbsoluteAPIUrl() + const absoluteAPIUrl = 'http://localhost:9000' || getAbsoluteAPIUrl() // TODO const embedUrl = buildVideoLink({ baseUrl: absoluteAPIUrl + '/videos/embed/' + videoAbuse.video.uuid, warningTitle: false diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html index 2f9fc8ba4..c5c0fdbbf 100644 --- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html +++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.html @@ -30,9 +30,16 @@ - {{ booleanToText(videoBlacklist.video.nsfw) }} - {{ booleanToText(videoBlacklist.unfederated) }} - {{ videoBlacklist.createdAt }} + + {{ booleanToText(videoBlacklist.video.nsfw) }} + {{ booleanToText(videoBlacklist.unfederated) }} + {{ videoBlacklist.createdAt }} + + + {{ booleanToText(videoBlacklist.video.nsfw) }} + {{ booleanToText(videoBlacklist.unfederated) }} + {{ videoBlacklist.createdAt }} + diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts index 5ead02eca..d68608ca6 100644 --- a/server/models/video/video-abuse.ts +++ b/server/models/video/video-abuse.ts @@ -11,14 +11,13 @@ import { import { AccountModel } from '../account/account' import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils' import { VideoModel } from './video' -import { VideoAbuseState, Video } from '../../../shared' +import { VideoAbuseState, VideoDetails } from '../../../shared' import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants' import { MUserAccountId, MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo } from '../../typings/models' import * as Bluebird from 'bluebird' import { literal, Op } from 'sequelize' import { ThumbnailModel } from './thumbnail' import { VideoChannelModel } from './video-channel' -import { ActorModel } from '../activitypub/actor' import { VideoBlacklistModel } from './video-blacklist' export enum ScopeNames { @@ -78,9 +77,73 @@ export enum ScopeNames { }) } - console.log(where) - return { + attributes: { + include: [ + [ + literal( + '(' + + 'SELECT t.count ' + + 'FROM ( ' + + 'SELECT id, ' + + 'count(id) OVER (PARTITION BY "videoId") ' + + 'FROM "videoAbuse" ' + + ') t ' + + 'WHERE t.id = "VideoAbuseModel".id ' + + ')' + ), + 'countReportsForVideo' + ], + [ + literal( + '(' + + 'SELECT t.nth ' + + 'FROM ( ' + + 'SELECT id, ' + + 'row_number() OVER (PARTITION BY "videoId" ORDER BY "createdAt") AS nth ' + + 'FROM "videoAbuse" ' + + ') t ' + + 'WHERE t.id = "VideoAbuseModel".id ' + + ')' + ), + 'nthReportForVideo' + ], + [ + literal( + '(' + + 'SELECT count("videoAbuse"."id") ' + + 'FROM "videoAbuse" ' + + 'INNER JOIN "video" ON "video"."id" = "videoAbuse"."videoId" ' + + 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + + 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' + + 'WHERE "account"."id" = "VideoAbuseModel"."reporterAccountId" ' + + ')' + ), + 'countReportsForReporter' + ], + [ + literal( + '(' + + 'WITH ' + + 'ids AS ( ' + + 'SELECT "account"."id" ' + + 'FROM "account" ' + + 'INNER JOIN "videoChannel" ON "videoChannel"."accountId" = "account"."id" ' + + 'INNER JOIN "video" ON "video"."channelId" = "videoChannel"."id" ' + + 'WHERE "video"."id" = "VideoAbuseModel"."videoId" ' + + ') ' + + 'SELECT count("videoAbuse"."id") ' + + 'FROM "videoAbuse" ' + + 'INNER JOIN "video" ON "video"."id" = "videoAbuse"."videoId" ' + + 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + + 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' + + 'INNER JOIN ids ON "account"."id" = ids.id ' + + ')' + ), + 'countReportsForReportee' + ] + ] + }, include: [ { model: AccountModel, @@ -96,13 +159,8 @@ export enum ScopeNames { model: ThumbnailModel }, { - model: VideoChannelModel.unscoped(), - where: { ...search(options.searchVideoChannel, 'name') }, - include: [ - { - model: ActorModel - } - ] + model: VideoChannelModel.scope([ 'WITH_ACTOR', 'WITH_ACCOUNT' ]), + where: { ...search(options.searchVideoChannel, 'name') } }, { attributes: [ 'id', 'reason', 'unfederated' ], @@ -149,7 +207,7 @@ export class VideoAbuseModel extends Model { @AllowNull(true) @Default(null) @Column(DataType.JSONB) - deletedVideo: Video + deletedVideo: VideoDetails @CreatedAt createdAt: Date @@ -229,6 +287,11 @@ export class VideoAbuseModel extends Model { } toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse { + const countReportsForVideo = this.get('countReportsForVideo') as number + const nthReportForVideo = this.get('nthReportForVideo') as number + const countReportsForReporter = this.get('countReportsForReporter') as number + const countReportsForReportee = this.get('countReportsForReportee') as number + const video = this.Video ? this.Video : this.deletedVideo @@ -250,9 +313,14 @@ export class VideoAbuseModel extends Model { deleted: !this.Video, blacklisted: this.Video && this.Video.isBlacklisted(), thumbnailPath: this.Video?.getMiniatureStaticPath(), - channel: this.Video?.VideoChannel.toFormattedSummaryJSON() || this.deletedVideo?.channel + channel: this.Video?.VideoChannel.toFormattedJSON() || this.deletedVideo?.channel }, - createdAt: this.createdAt + createdAt: this.createdAt, + updatedAt: this.updatedAt, + count: countReportsForVideo || 0, + nth: nthReportForVideo || 0, + countReportsForReporter: countReportsForReporter || 0, + countReportsForReportee: countReportsForReportee || 0 } } diff --git a/shared/models/videos/abuse/video-abuse.model.ts b/shared/models/videos/abuse/video-abuse.model.ts index 953193e5e..f2c2cdc41 100644 --- a/shared/models/videos/abuse/video-abuse.model.ts +++ b/shared/models/videos/abuse/video-abuse.model.ts @@ -1,7 +1,7 @@ import { Account } from '../../actors/index' import { VideoConstant } from '../video-constant.model' import { VideoAbuseState } from './video-abuse-state.model' -import { VideoChannelSummary } from '../channel/video-channel.model' +import { VideoChannel } from '../channel/video-channel.model' export interface VideoAbuse { id: number @@ -19,8 +19,15 @@ export interface VideoAbuse { deleted: boolean blacklisted: boolean thumbnailPath?: string - channel?: VideoChannelSummary + channel?: VideoChannel } createdAt: Date + updatedAt: Date + + count?: number + nth?: number + + countReportsForReporter?: number + countReportsForReportee?: number }