-
@@ -42,3 +46,5 @@
+
+
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.scss b/client/src/app/+admin/follows/following-list/following-list.component.scss
index a6f0656b8..f4656b88d 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.scss
+++ b/client/src/app/+admin/follows/following-list/following-list.component.scss
@@ -7,4 +7,8 @@
input {
@include peertube-input-text(250px);
}
-}
\ No newline at end of file
+}
+
+.follow-button {
+ @include create-button;
+}
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts
index dd7629ead..477a6c0d7 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.ts
+++ b/client/src/app/+admin/follows/following-list/following-list.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core'
+import { Component, OnInit, ViewChild } from '@angular/core'
import { Notifier } from '@app/core'
import { SortMeta } from 'primeng/api'
import { ActorFollow } from '../../../../../../shared/models/actors/follow.model'
@@ -6,6 +6,7 @@ import { ConfirmService } from '../../../core/confirm/confirm.service'
import { RestPagination, RestTable } from '../../../shared'
import { FollowService } from '@app/shared/instance/follow.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
+import { BatchDomainsModalComponent } from '@app/+admin/config/shared/batch-domains-modal.component'
@Component({
selector: 'my-followers-list',
@@ -13,10 +14,12 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
styleUrls: [ './following-list.component.scss' ]
})
export class FollowingListComponent extends RestTable implements OnInit {
+ @ViewChild('batchDomainsModal') batchDomainsModal: BatchDomainsModalComponent
+
following: ActorFollow[] = []
totalRecords = 0
rowsPerPage = 10
- sort: SortMeta = { field: 'createdAt', order: 1 }
+ sort: SortMeta = { field: 'createdAt', order: -1 }
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
constructor (
@@ -36,6 +39,21 @@ export class FollowingListComponent extends RestTable implements OnInit {
return 'FollowingListComponent'
}
+ addDomainsToFollow () {
+ this.batchDomainsModal.openModal()
+ }
+
+ async addFollowing (hosts: string[]) {
+ this.followService.follow(hosts).subscribe(
+ () => {
+ this.notifier.success(this.i18n('Follow request(s) sent!'))
+ this.loadData()
+ },
+
+ err => this.notifier.error(err.message)
+ )
+ }
+
async removeFollowing (follow: ActorFollow) {
const res = await this.confirmService.confirm(
this.i18n('Do you really want to unfollow {{host}}?', { host: follow.following.host }),
diff --git a/client/src/app/+admin/follows/follows.component.html b/client/src/app/+admin/follows/follows.component.html
index 46581daf9..7b5bcc2db 100644
--- a/client/src/app/+admin/follows/follows.component.html
+++ b/client/src/app/+admin/follows/follows.component.html
@@ -4,8 +4,6 @@
Following
-
Follow
-
Followers
Video redundancies
diff --git a/client/src/app/+admin/follows/follows.routes.ts b/client/src/app/+admin/follows/follows.routes.ts
index 298733eb0..8270ae444 100644
--- a/client/src/app/+admin/follows/follows.routes.ts
+++ b/client/src/app/+admin/follows/follows.routes.ts
@@ -2,7 +2,6 @@ import { Routes } from '@angular/router'
import { UserRightGuard } from '../../core'
import { FollowsComponent } from './follows.component'
-import { FollowingAddComponent } from './following-add'
import { FollowersListComponent } from './followers-list'
import { UserRight } from '../../../../../shared'
import { FollowingListComponent } from './following-list/following-list.component'
@@ -42,12 +41,7 @@ export const FollowsRoutes: Routes = [
},
{
path: 'following-add',
- component: FollowingAddComponent,
- data: {
- meta: {
- title: 'Add follow'
- }
- }
+ redirectTo: 'following-list'
},
{
path: 'video-redundancies-list',
diff --git a/client/src/app/+admin/follows/index.ts b/client/src/app/+admin/follows/index.ts
index 4fcb35cb1..285955468 100644
--- a/client/src/app/+admin/follows/index.ts
+++ b/client/src/app/+admin/follows/index.ts
@@ -1,4 +1,3 @@
-export * from './following-add'
export * from './followers-list'
export * from './following-list'
export * from './video-redundancies-list'
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 44c5c2fb8..0e072d84b 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
@@ -4,6 +4,14 @@
[showCurrentPageReport]="true" i18n-currentPageReportTemplate
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} muted instances"
>
+
+
+
@@ -23,3 +31,11 @@
+
+
+
+
+ It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
+
+
+
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.scss b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.scss
index 6028b75ea..9d3bedd80 100644
--- a/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.scss
+++ b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.scss
@@ -4,4 +4,8 @@
.unblock-button {
@include peertube-button;
@include grey-button;
-}
\ No newline at end of file
+}
+
+.block-button {
+ @include create-button;
+}
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts
index 5af6d8f76..431729ef2 100644
--- a/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts
+++ b/client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts
@@ -1,10 +1,11 @@
-import { Component, OnInit } from '@angular/core'
+import { Component, OnInit, ViewChild } from '@angular/core'
import { Notifier } from '@app/core'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { RestPagination, RestTable } from '@app/shared'
import { SortMeta } from 'primeng/api'
import { BlocklistService } from '@app/shared/blocklist'
import { ServerBlock } from '../../../../../../shared'
+import { BatchDomainsModalComponent } from '@app/+admin/config/shared/batch-domains-modal.component'
@Component({
selector: 'my-instance-server-blocklist',
@@ -12,6 +13,8 @@ import { ServerBlock } from '../../../../../../shared'
templateUrl: './instance-server-blocklist.component.html'
})
export class InstanceServerBlocklistComponent extends RestTable implements OnInit {
+ @ViewChild('batchDomainsModal') batchDomainsModal: BatchDomainsModalComponent
+
blockedServers: ServerBlock[] = []
totalRecords = 0
rowsPerPage = 10
@@ -47,6 +50,27 @@ export class InstanceServerBlocklistComponent extends RestTable implements OnIni
)
}
+ httpEnabled () {
+ return window.location.protocol === 'https:'
+ }
+
+ addServersToBlock () {
+ this.batchDomainsModal.openModal()
+ }
+
+ onDomainsToBlock (domains: string[]) {
+ domains.forEach(domain => {
+ this.blocklistService.blockServerByInstance(domain)
+ .subscribe(
+ () => {
+ this.notifier.success(this.i18n('Instance {{domain}} muted by your instance.', { domain }))
+
+ this.loadData()
+ }
+ )
+ })
+ }
+
protected loadData () {
return this.blocklistService.getInstanceServerBlocklist(this.pagination, this.sort)
.subscribe(
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 155d10dda..3899ee07f 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,13 +48,13 @@
-
+ |
|
-
+
|
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 b135792a7..5e48cf24f 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
@@ -1,9 +1,9 @@
import { Component, OnInit, ViewChild } from '@angular/core'
-import { Account } from '../../../shared/account/account.model'
+import { Account } from '@app/shared/account/account.model'
import { Notifier } from '@app/core'
import { SortMeta } from 'primeng/api'
import { VideoAbuse, VideoAbuseState } from '../../../../../../shared'
-import { RestPagination, RestTable, VideoAbuseService } from '../../../shared'
+import { RestPagination, RestTable, VideoAbuseService, VideoBlacklistService } from '../../../shared'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { DropdownAction } from '../../../shared/buttons/action-dropdown.component'
import { ConfirmService } from '../../../core/index'
@@ -14,6 +14,7 @@ import { Actor } from '@app/shared/actor/actor.model'
import { buildVideoLink, buildVideoEmbed } from 'src/assets/player/utils'
import { getAbsoluteAPIUrl } from '@app/shared/misc/utils'
import { DomSanitizer } from '@angular/platform-browser'
+import { BlocklistService } from '@app/shared/blocklist'
@Component({
selector: 'my-video-abuse-list',
@@ -29,11 +30,13 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
sort: SortMeta = { field: 'createdAt', order: 1 }
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
- videoAbuseActions: DropdownAction
[] = []
+ videoAbuseActions: DropdownAction[][] = []
constructor (
private notifier: Notifier,
private videoAbuseService: VideoAbuseService,
+ private blocklistService: BlocklistService,
+ private videoBlacklistService: VideoBlacklistService,
private confirmService: ConfirmService,
private i18n: I18n,
private markdownRenderer: MarkdownService,
@@ -42,30 +45,57 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
super()
this.videoAbuseActions = [
- {
- label: this.i18n('Delete this report'),
- handler: videoAbuse => this.removeVideoAbuse(videoAbuse)
- },
- {
- label: this.i18n('Add note'),
- handler: videoAbuse => this.openModerationCommentModal(videoAbuse),
- isDisplayed: videoAbuse => !videoAbuse.moderationComment
- },
- {
- label: this.i18n('Update note'),
- handler: videoAbuse => this.openModerationCommentModal(videoAbuse),
- isDisplayed: videoAbuse => !!videoAbuse.moderationComment
- },
- {
- label: this.i18n('Mark as accepted'),
- handler: videoAbuse => this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED),
- isDisplayed: videoAbuse => !this.isVideoAbuseAccepted(videoAbuse)
- },
- {
- label: this.i18n('Mark as rejected'),
- handler: videoAbuse => this.updateVideoAbuseState(videoAbuse, VideoAbuseState.REJECTED),
- isDisplayed: videoAbuse => !this.isVideoAbuseRejected(videoAbuse)
- }
+ [
+ {
+ label: this.i18n('Internal actions'),
+ isHeader: true
+ },
+ {
+ label: this.i18n('Delete report'),
+ handler: videoAbuse => this.removeVideoAbuse(videoAbuse)
+ },
+ {
+ label: this.i18n('Add note'),
+ handler: videoAbuse => this.openModerationCommentModal(videoAbuse),
+ isDisplayed: videoAbuse => !videoAbuse.moderationComment
+ },
+ {
+ label: this.i18n('Update note'),
+ handler: videoAbuse => this.openModerationCommentModal(videoAbuse),
+ isDisplayed: videoAbuse => !!videoAbuse.moderationComment
+ },
+ {
+ label: this.i18n('Mark as accepted'),
+ handler: videoAbuse => this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED),
+ isDisplayed: videoAbuse => !this.isVideoAbuseAccepted(videoAbuse)
+ },
+ {
+ label: this.i18n('Mark as rejected'),
+ handler: videoAbuse => this.updateVideoAbuseState(videoAbuse, VideoAbuseState.REJECTED),
+ isDisplayed: videoAbuse => !this.isVideoAbuseRejected(videoAbuse)
+ }
+ ],
+ [
+ {
+ label: this.i18n('Actions for the video'),
+ isHeader: true
+ },
+ {
+ label: this.i18n('Blacklist video'),
+ handler: videoAbuse => {
+ this.videoBlacklistService.blacklistVideo(videoAbuse.video.id, undefined, true)
+ .subscribe(
+ () => {
+ this.notifier.success(this.i18n('Video blacklisted.'))
+
+ this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED)
+ },
+
+ err => this.notifier.error(err.message)
+ )
+ }
+ }
+ ]
]
}
diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts
index 43b6863af..4e9965bee 100644
--- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts
+++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts
@@ -18,7 +18,7 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
blacklist: (VideoBlacklist & { reasonHtml?: string })[] = []
totalRecords = 0
rowsPerPage = 10
- sort: SortMeta = { field: 'createdAt', order: 1 }
+ sort: SortMeta = { field: 'createdAt', order: -1 }
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
listBlacklistTypeFilter: VideoBlacklistType = undefined
diff --git a/client/src/app/shared/buttons/action-dropdown.component.html b/client/src/app/shared/buttons/action-dropdown.component.html
index cd993db9f..14cfe9a22 100644
--- a/client/src/app/shared/buttons/action-dropdown.component.html
+++ b/client/src/app/shared/buttons/action-dropdown.component.html
@@ -24,17 +24,27 @@
-
+
+
+
diff --git a/client/src/app/shared/buttons/action-dropdown.component.scss b/client/src/app/shared/buttons/action-dropdown.component.scss
index 442c90984..7a030f32c 100644
--- a/client/src/app/shared/buttons/action-dropdown.component.scss
+++ b/client/src/app/shared/buttons/action-dropdown.component.scss
@@ -51,6 +51,10 @@
}
.dropdown-menu {
+ .dropdown-header {
+ padding: 0.2rem 1rem;
+ }
+
.dropdown-item {
display: flex;
cursor: pointer;
diff --git a/client/src/app/shared/buttons/action-dropdown.component.ts b/client/src/app/shared/buttons/action-dropdown.component.ts
index 6649b092a..8fcaa38b9 100644
--- a/client/src/app/shared/buttons/action-dropdown.component.ts
+++ b/client/src/app/shared/buttons/action-dropdown.component.ts
@@ -9,6 +9,7 @@ export type DropdownAction
= {
handler?: (a: T) => any
linkBuilder?: (a: T) => (string | number)[]
isDisplayed?: (a: T) => boolean
+ isHeader?: boolean
}
export type DropdownButtonSize = 'normal' | 'small'
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index a952880a6..01735c187 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -107,6 +107,7 @@ import { InputSwitchModule } from 'primeng/inputswitch'
import { MyAccountVideoSettingsComponent } from '@app/+my-account/my-account-settings/my-account-video-settings'
import { MyAccountInterfaceSettingsComponent } from '@app/+my-account/my-account-settings/my-account-interface'
import { ActorAvatarInfoComponent } from '@app/+my-account/shared/actor-avatar-info.component'
+import { BatchDomainsValidatorsService } from '@app/+admin/config/shared/batch-domains-validators.service'
@NgModule({
imports: [
@@ -297,6 +298,7 @@ import { ActorAvatarInfoComponent } from '@app/+my-account/shared/actor-avatar-i
LoginValidatorsService,
ResetPasswordValidatorsService,
UserValidatorsService,
+ BatchDomainsValidatorsService,
VideoPlaylistValidatorsService,
VideoAbuseValidatorsService,
VideoChannelValidatorsService,
diff --git a/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts b/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts
index 0f7c19765..e1a8f6260 100644
--- a/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts
+++ b/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts
@@ -11,7 +11,6 @@ import { VideoCommentService } from './video-comment.service'
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
import { VideoCommentValidatorsService } from '@app/shared/forms/form-validators/video-comment-validators.service'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
-import { AuthService } from '@app/core/auth'
@Component({
selector: 'my-video-comment-add',
@@ -38,7 +37,6 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
private videoCommentValidatorsService: VideoCommentValidatorsService,
private notifier: Notifier,
private videoCommentService: VideoCommentService,
- private authService: AuthService,
private modalService: NgbModal,
private router: Router
) {
diff --git a/server/middlewares/validators/blocklist.ts b/server/middlewares/validators/blocklist.ts
index 47a0b1a1c..b2183437c 100644
--- a/server/middlewares/validators/blocklist.ts
+++ b/server/middlewares/validators/blocklist.ts
@@ -84,11 +84,9 @@ const blockServerValidator = [
.end()
}
- const server = await ServerModel.loadByHost(host)
+ let server = await ServerModel.loadByHost(host)
if (!server) {
- return res.status(404)
- .send({ error: 'Server host not found.' })
- .end()
+ server = await ServerModel.create({ host })
}
res.locals.server = server
diff --git a/server/tests/api/check-params/blocklist.ts b/server/tests/api/check-params/blocklist.ts
index fb459f756..1219ec9bd 100644
--- a/server/tests/api/check-params/blocklist.ts
+++ b/server/tests/api/check-params/blocklist.ts
@@ -175,13 +175,13 @@ describe('Test blocklist API validators', function () {
})
})
- it('Should fail with an unknown server', async function () {
+ it('Should succeed with an unknown server', async function () {
await makePostBodyRequest({
url: server.url,
token: server.accessToken,
path,
fields: { host: 'localhost:9003' },
- statusCodeExpected: 404
+ statusCodeExpected: 204
})
})
@@ -218,7 +218,7 @@ describe('Test blocklist API validators', function () {
it('Should fail with an unknown server block', async function () {
await makeDeleteRequest({
url: server.url,
- path: path + '/localhost:9003',
+ path: path + '/localhost:9004',
token: server.accessToken,
statusCodeExpected: 404
})
@@ -415,13 +415,13 @@ describe('Test blocklist API validators', function () {
})
})
- it('Should fail with an unknown server', async function () {
+ it('Should succeed with an unknown server', async function () {
await makePostBodyRequest({
url: server.url,
token: server.accessToken,
path,
fields: { host: 'localhost:9003' },
- statusCodeExpected: 404
+ statusCodeExpected: 204
})
})
@@ -467,7 +467,7 @@ describe('Test blocklist API validators', function () {
it('Should fail with an unknown server block', async function () {
await makeDeleteRequest({
url: server.url,
- path: path + '/localhost:9003',
+ path: path + '/localhost:9004',
token: server.accessToken,
statusCodeExpected: 404
})