Add ability to mute a user/instance by server in client
authorChocobozzz <me@florianbigard.com>
Mon, 15 Oct 2018 14:43:14 +0000 (16:43 +0200)
committerChocobozzz <me@florianbigard.com>
Tue, 16 Oct 2018 14:41:36 +0000 (16:41 +0200)
21 files changed:
client/src/app/+accounts/accounts.component.html
client/src/app/+admin/admin.module.ts
client/src/app/+admin/moderation/instance-blocklist/index.ts [new file with mode: 0644]
client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.html [new file with mode: 0644]
client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss [new file with mode: 0644]
client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.ts [new file with mode: 0644]
client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.html [new file with mode: 0644]
client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.scss [new file with mode: 0644]
client/src/app/+admin/moderation/instance-blocklist/instance-server-blocklist.component.ts [new file with mode: 0644]
client/src/app/+admin/moderation/moderation.component.html
client/src/app/+admin/moderation/moderation.component.ts
client/src/app/+admin/moderation/moderation.routes.ts
client/src/app/shared/account/account.model.ts
client/src/app/shared/blocklist/account-block.model.ts
client/src/app/shared/blocklist/blocklist.service.ts
client/src/app/shared/blocklist/index.ts
client/src/app/shared/moderation/user-moderation-dropdown.component.ts
client/src/sass/include/_bootstrap-variables.scss
server/models/video/video-comment.ts
server/models/video/video.ts
server/tests/api/users/blocklist.ts

index 60dbcdf1db31c41c62a330d2f3d7cddc67d33c82..c1377c1ea5cfceb0aa81be14a4da8de2dbf60e94 100644 (file)
           <div class="actor-name">{{ account.nameWithHost }}</div>
 
           <span *ngIf="user?.blocked" [ngbTooltip]="user.blockedReason" class="badge badge-danger" i18n>Banned</span>
-          <span *ngIf="account.muted" class="badge badge-danger" i18n>Muted</span>
-          <span *ngIf="account.mutedServer" class="badge badge-danger" i18n>Instance muted</span>
+          <span *ngIf="account.mutedByUser" class="badge badge-danger" i18n>Muted</span>
+          <span *ngIf="account.mutedServerByUser" class="badge badge-danger" i18n>Muted by your instance</span>
+          <span *ngIf="account.mutedByInstance" class="badge badge-danger" i18n>Instance muted</span>
+          <span *ngIf="account.mutedServerByInstance" class="badge badge-danger" i18n>Instance muted by your instance</span>
 
           <my-user-moderation-dropdown
             buttonSize="small" [account]="account" [user]="user"
index 8c6db98d9f4a1d222b2e56e3b76753004bd671c9..c06ae1d603c6613ad6bf89b44dd422a96ac74bf3 100644 (file)
@@ -15,6 +15,7 @@ import { ModerationCommentModalComponent, VideoAbuseListComponent, VideoBlacklis
 import { ModerationComponent } from '@app/+admin/moderation/moderation.component'
 import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component'
 import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service'
+import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist'
 
 @NgModule({
   imports: [
@@ -41,6 +42,8 @@ import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service
     VideoBlacklistListComponent,
     VideoAbuseListComponent,
     ModerationCommentModalComponent,
+    InstanceServerBlocklistComponent,
+    InstanceAccountBlocklistComponent,
 
     JobsComponent,
     JobsListComponent,
diff --git a/client/src/app/+admin/moderation/instance-blocklist/index.ts b/client/src/app/+admin/moderation/instance-blocklist/index.ts
new file mode 100644 (file)
index 0000000..3e7a344
--- /dev/null
@@ -0,0 +1,2 @@
+export * from './instance-account-blocklist.component'
+export * from './instance-server-blocklist.component'
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
new file mode 100644 (file)
index 0000000..7797bc5
--- /dev/null
@@ -0,0 +1,22 @@
+<p-table
+  [value]="blockedAccounts" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
+  [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)"
+>
+
+  <ng-template pTemplate="header">
+    <tr>
+      <th i18n>Account</th>
+      <th i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
+    </tr>
+  </ng-template>
+
+  <ng-template pTemplate="body" let-accountBlock>
+    <tr>
+      <td>{{ accountBlock.blockedAccount.nameWithHost }}</td>
+      <td>{{ accountBlock.createdAt }}</td>
+      <td class="action-cell">
+        <button class="unblock-button" (click)="unblockAccount(accountBlock)" i18n>Unmute</button>
+      </td>
+    </tr>
+  </ng-template>
+</p-table>
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.scss
new file mode 100644 (file)
index 0000000..6028b75
--- /dev/null
@@ -0,0 +1,7 @@
+@import '_variables';
+@import '_mixins';
+
+.unblock-button {
+  @include peertube-button;
+  @include grey-button;
+}
\ No newline at end of file
diff --git a/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.ts b/client/src/app/+admin/moderation/instance-blocklist/instance-account-blocklist.component.ts
new file mode 100644 (file)
index 0000000..3f243ae
--- /dev/null
@@ -0,0 +1,59 @@
+import { Component, OnInit } from '@angular/core'
+import { NotificationsService } from 'angular2-notifications'
+import { I18n } from '@ngx-translate/i18n-polyfill'
+import { RestPagination, RestTable } from '@app/shared'
+import { SortMeta } from 'primeng/components/common/sortmeta'
+import { BlocklistService, AccountBlock } from '@app/shared/blocklist'
+
+@Component({
+  selector: 'my-instance-account-blocklist',
+  styleUrls: [ './instance-account-blocklist.component.scss' ],
+  templateUrl: './instance-account-blocklist.component.html'
+})
+export class InstanceAccountBlocklistComponent extends RestTable implements OnInit {
+  blockedAccounts: AccountBlock[] = []
+  totalRecords = 0
+  rowsPerPage = 10
+  sort: SortMeta = { field: 'createdAt', order: -1 }
+  pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
+
+  constructor (
+    private notificationsService: NotificationsService,
+    private blocklistService: BlocklistService,
+    private i18n: I18n
+  ) {
+    super()
+  }
+
+  ngOnInit () {
+    this.initialize()
+  }
+
+  unblockAccount (accountBlock: AccountBlock) {
+    const blockedAccount = accountBlock.blockedAccount
+
+    this.blocklistService.unblockAccountByInstance(blockedAccount)
+        .subscribe(
+          () => {
+            this.notificationsService.success(
+              this.i18n('Success'),
+              this.i18n('Account {{nameWithHost}} unmuted by your instance.', { nameWithHost: blockedAccount.nameWithHost })
+            )
+
+            this.loadData()
+          }
+        )
+  }
+
+  protected loadData () {
+    return this.blocklistService.getInstanceAccountBlocklist(this.pagination, this.sort)
+      .subscribe(
+        resultList => {
+          this.blockedAccounts = resultList.data
+          this.totalRecords = resultList.total
+        },
+
+        err => this.notificationsService.error(this.i18n('Error'), err.message)
+      )
+  }
+}
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
new file mode 100644 (file)
index 0000000..859c0f9
--- /dev/null
@@ -0,0 +1,23 @@
+<p-table
+  [value]="blockedAccounts" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
+  [sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)"
+>
+
+  <ng-template pTemplate="header">
+    <tr>
+      <th i18n>Instance</th>
+      <th i18n pSortableColumn="createdAt">Muted at <p-sortIcon field="createdAt"></p-sortIcon></th>
+      <th></th>
+    </tr>
+  </ng-template>
+
+  <ng-template pTemplate="body" let-serverBlock>
+    <tr>
+      <td>{{ serverBlock.blockedServer.host }}</td>
+      <td>{{ serverBlock.createdAt }}</td>
+      <td class="action-cell">
+        <button class="unblock-button" (click)="unblockServer(serverBlock)" i18n>Unmute</button>
+      </td>
+    </tr>
+  </ng-template>
+</p-table>
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
new file mode 100644 (file)
index 0000000..6028b75
--- /dev/null
@@ -0,0 +1,7 @@
+@import '_variables';
+@import '_mixins';
+
+.unblock-button {
+  @include peertube-button;
+  @include grey-button;
+}
\ No newline at end of file
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
new file mode 100644 (file)
index 0000000..9459117
--- /dev/null
@@ -0,0 +1,60 @@
+import { Component, OnInit } from '@angular/core'
+import { NotificationsService } from 'angular2-notifications'
+import { I18n } from '@ngx-translate/i18n-polyfill'
+import { RestPagination, RestTable } from '@app/shared'
+import { SortMeta } from 'primeng/components/common/sortmeta'
+import { BlocklistService } from '@app/shared/blocklist'
+import { ServerBlock } from '../../../../../../shared'
+
+@Component({
+  selector: 'my-instance-server-blocklist',
+  styleUrls: [ './instance-server-blocklist.component.scss' ],
+  templateUrl: './instance-server-blocklist.component.html'
+})
+export class InstanceServerBlocklistComponent extends RestTable implements OnInit {
+  blockedAccounts: ServerBlock[] = []
+  totalRecords = 0
+  rowsPerPage = 10
+  sort: SortMeta = { field: 'createdAt', order: -1 }
+  pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
+
+  constructor (
+    private notificationsService: NotificationsService,
+    private blocklistService: BlocklistService,
+    private i18n: I18n
+  ) {
+    super()
+  }
+
+  ngOnInit () {
+    this.initialize()
+  }
+
+  unblockServer (serverBlock: ServerBlock) {
+    const host = serverBlock.blockedServer.host
+
+    this.blocklistService.unblockServerByInstance(host)
+      .subscribe(
+        () => {
+          this.notificationsService.success(
+            this.i18n('Success'),
+            this.i18n('Instance {{host}} unmuted by your instance.', { host })
+          )
+
+          this.loadData()
+        }
+      )
+  }
+
+  protected loadData () {
+    return this.blocklistService.getInstanceServerBlocklist(this.pagination, this.sort)
+      .subscribe(
+        resultList => {
+          this.blockedAccounts = resultList.data
+          this.totalRecords = resultList.total
+        },
+
+        err => this.notificationsService.error(this.i18n('Error'), err.message)
+      )
+  }
+}
index 91e87fcd4c963d2351a4007de0b9b84e8aa74194..8ec7278ef0fa45da8f1a902e99153d7ecebd1d0b 100644 (file)
@@ -5,6 +5,10 @@
     <a *ngIf="hasVideoAbusesRight()" i18n routerLink="video-abuses/list" routerLinkActive="active">Video abuses</a>
 
     <a *ngIf="hasVideoBlacklistRight()" i18n routerLink="video-blacklist/list" routerLinkActive="active">Blacklisted videos</a>
+
+    <a *ngIf="hasAccountsBlacklistRight()" i18n routerLink="blocklist/accounts" routerLinkActive="active">Muted accounts</a>
+
+    <a *ngIf="hasServersBlacklistRight()" i18n routerLink="blocklist/servers" routerLinkActive="active">Muted servers</a>
   </div>
 </div>
 
index 0f4efb970177f0fec0f76ecf30ba02acff059b89..7f85f920e3d65dd14e876ffaf34cb7d76a3c4183 100644 (file)
@@ -16,4 +16,12 @@ export class ModerationComponent {
   hasVideoBlacklistRight () {
     return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)
   }
+
+  hasAccountsBlacklistRight () {
+    return this.auth.getUser().hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)
+  }
+
+  hasServersBlacklistRight () {
+    return this.auth.getUser().hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)
+  }
 }
index 6d81b9b363149da288cae5f8837e3f07570a6751..bc6dd49d5715dfe4917fd5db7f3b60fc7760f690 100644 (file)
@@ -4,6 +4,7 @@ import { UserRightGuard } from '@app/core'
 import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list'
 import { VideoBlacklistListComponent } from '@app/+admin/moderation/video-blacklist-list'
 import { ModerationComponent } from '@app/+admin/moderation/moderation.component'
+import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist'
 
 export const ModerationRoutes: Routes = [
   {
@@ -46,6 +47,28 @@ export const ModerationRoutes: Routes = [
             title: 'Blacklisted videos'
           }
         }
+      },
+      {
+        path: 'blocklist/accounts',
+        component: InstanceAccountBlocklistComponent,
+        canActivate: [ UserRightGuard ],
+        data: {
+          userRight: UserRight.MANAGE_ACCOUNTS_BLOCKLIST,
+          meta: {
+            title: 'Muted accounts'
+          }
+        }
+      },
+      {
+        path: 'blocklist/servers',
+        component: InstanceServerBlocklistComponent,
+        canActivate: [ UserRightGuard ],
+        data: {
+          userRight: UserRight.MANAGE_SERVER_REDUNDANCY,
+          meta: {
+            title: 'Muted instances'
+          }
+        }
       }
     ]
   }
index 0aba9428ac112c62841453fdf3ecbd3b012c88e2..c5cd2051ca858d8e3727b4c5612dbbc0ce80c50b 100644 (file)
@@ -5,8 +5,10 @@ export class Account extends Actor implements ServerAccount {
   displayName: string
   description: string
   nameWithHost: string
-  muted: boolean
-  mutedServer: boolean
+  mutedByUser: boolean
+  mutedByInstance: boolean
+  mutedServerByUser: boolean
+  mutedServerByInstance: boolean
 
   userId?: number
 
@@ -18,7 +20,9 @@ export class Account extends Actor implements ServerAccount {
     this.userId = hash.userId
     this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host)
 
-    this.muted = false
-    this.mutedServer = false
+    this.mutedByUser = false
+    this.mutedByInstance = false
+    this.mutedServerByUser = false
+    this.mutedServerByInstance = false
   }
 }
index 336680f65f375f6c28707fd4390a2acd3a1f935f..e7b433d880cd906474d8165a1ec91bdc28d313b8 100644 (file)
@@ -11,4 +11,4 @@ export class AccountBlock implements AccountBlockServer {
     this.blockedAccount = new Account(block.blockedAccount)
     this.createdAt = block.createdAt
   }
-}
\ No newline at end of file
+}
index d9c318258dd3369a9bb053006770bc10b28c0fc6..c1f7312f0a0dc7d787007118e40ade727d13e778 100644 (file)
@@ -11,6 +11,7 @@ import { AccountBlock } from '@app/shared/blocklist/account-block.model'
 @Injectable()
 export class BlocklistService {
   static BASE_USER_BLOCKLIST_URL = environment.apiUrl + '/api/v1/users/me/blocklist'
+  static BASE_SERVER_BLOCKLIST_URL = environment.apiUrl + '/api/v1/server/blocklist'
 
   constructor (
     private authHttp: HttpClient,
@@ -73,6 +74,61 @@ export class BlocklistService {
                .pipe(catchError(err => this.restExtractor.handleError(err)))
   }
 
+  /*********************** Instance -> Account blocklist ***********************/
+
+  getInstanceAccountBlocklist (pagination: RestPagination, sort: SortMeta) {
+    let params = new HttpParams()
+    params = this.restService.addRestGetParams(params, pagination, sort)
+
+    return this.authHttp.get<ResultList<AccountBlock>>(BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/accounts', { params })
+               .pipe(
+                 map(res => this.restExtractor.convertResultListDateToHuman(res)),
+                 map(res => this.restExtractor.applyToResultListData(res, this.formatAccountBlock.bind(this))),
+                 catchError(err => this.restExtractor.handleError(err))
+               )
+  }
+
+  blockAccountByInstance (account: Account) {
+    const body = { accountName: account.nameWithHost }
+
+    return this.authHttp.post(BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/accounts', body)
+               .pipe(catchError(err => this.restExtractor.handleError(err)))
+  }
+
+  unblockAccountByInstance (account: Account) {
+    const path = BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/accounts/' + account.nameWithHost
+
+    return this.authHttp.delete(path)
+               .pipe(catchError(err => this.restExtractor.handleError(err)))
+  }
+
+  /*********************** Instance -> Server blocklist ***********************/
+
+  getInstanceServerBlocklist (pagination: RestPagination, sort: SortMeta) {
+    let params = new HttpParams()
+    params = this.restService.addRestGetParams(params, pagination, sort)
+
+    return this.authHttp.get<ResultList<ServerBlock>>(BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/servers', { params })
+               .pipe(
+                 map(res => this.restExtractor.convertResultListDateToHuman(res)),
+                 catchError(err => this.restExtractor.handleError(err))
+               )
+  }
+
+  blockServerByInstance (host: string) {
+    const body = { host }
+
+    return this.authHttp.post(BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/servers', body)
+               .pipe(catchError(err => this.restExtractor.handleError(err)))
+  }
+
+  unblockServerByInstance (host: string) {
+    const path = BlocklistService.BASE_SERVER_BLOCKLIST_URL + '/servers/' + host
+
+    return this.authHttp.delete(path)
+               .pipe(catchError(err => this.restExtractor.handleError(err)))
+  }
+
   private formatAccountBlock (accountBlock: AccountBlockServer) {
     return new AccountBlock(accountBlock)
   }
index 8cf6a55f763267c27bf86eb753117f227b62da64..5886ca07eee4a33910dc364a34f29e2309eeb7b1 100644 (file)
@@ -1,2 +1,2 @@
 export * from './blocklist.service'
-export * from './account-block.model'
\ No newline at end of file
+export * from './account-block.model'
index 2f4a55f3795e435bc6afe678fc8f0c4df51a1f27..908f0b8e08aee8c8bfbcc980f82739e8c4294d6f 100644 (file)
@@ -26,7 +26,7 @@ export class UserModerationDropdownComponent implements OnChanges {
   @Output() userChanged = new EventEmitter()
   @Output() userDeleted = new EventEmitter()
 
-  userActions: DropdownAction<User>[] = []
+  userActions: DropdownAction<{ user: User, account: Account }>[] = []
 
   constructor (
     private authService: AuthService,
@@ -106,7 +106,7 @@ export class UserModerationDropdownComponent implements OnChanges {
               this.i18n('Account {{nameWithHost}} muted.', { nameWithHost: account.nameWithHost })
             )
 
-            this.account.muted = true
+            this.account.mutedByUser = true
             this.userChanged.emit()
           },
 
@@ -123,7 +123,7 @@ export class UserModerationDropdownComponent implements OnChanges {
               this.i18n('Account {{nameWithHost}} unmuted.', { nameWithHost: account.nameWithHost })
             )
 
-            this.account.muted = false
+            this.account.mutedByUser = false
             this.userChanged.emit()
           },
 
@@ -140,7 +140,7 @@ export class UserModerationDropdownComponent implements OnChanges {
               this.i18n('Instance {{host}} muted.', { host })
             )
 
-            this.account.mutedServer = true
+            this.account.mutedServerByUser = true
             this.userChanged.emit()
           },
 
@@ -157,7 +157,75 @@ export class UserModerationDropdownComponent implements OnChanges {
               this.i18n('Instance {{host}} unmuted.', { host })
             )
 
-            this.account.mutedServer = false
+            this.account.mutedServerByUser = false
+            this.userChanged.emit()
+          },
+
+          err => this.notificationsService.error(this.i18n('Error'), err.message)
+        )
+  }
+
+  blockAccountByInstance (account: Account) {
+    this.blocklistService.blockAccountByInstance(account)
+        .subscribe(
+          () => {
+            this.notificationsService.success(
+              this.i18n('Success'),
+              this.i18n('Account {{nameWithHost}} muted by the instance.', { nameWithHost: account.nameWithHost })
+            )
+
+            this.account.mutedByInstance = true
+            this.userChanged.emit()
+          },
+
+          err => this.notificationsService.error(this.i18n('Error'), err.message)
+        )
+  }
+
+  unblockAccountByInstance (account: Account) {
+    this.blocklistService.unblockAccountByInstance(account)
+        .subscribe(
+          () => {
+            this.notificationsService.success(
+              this.i18n('Success'),
+              this.i18n('Account {{nameWithHost}} unmuted by the instance.', { nameWithHost: account.nameWithHost })
+            )
+
+            this.account.mutedByInstance = false
+            this.userChanged.emit()
+          },
+
+          err => this.notificationsService.error(this.i18n('Error'), err.message)
+        )
+  }
+
+  blockServerByInstance (host: string) {
+    this.blocklistService.blockServerByInstance(host)
+        .subscribe(
+          () => {
+            this.notificationsService.success(
+              this.i18n('Success'),
+              this.i18n('Instance {{host}} muted by the instance.', { host })
+            )
+
+            this.account.mutedServerByInstance = true
+            this.userChanged.emit()
+          },
+
+          err => this.notificationsService.error(this.i18n('Error'), err.message)
+        )
+  }
+
+  unblockServerByInstance (host: string) {
+    this.blocklistService.unblockServerByInstance(host)
+        .subscribe(
+          () => {
+            this.notificationsService.success(
+              this.i18n('Success'),
+              this.i18n('Instance {{host}} unmuted by the instance.', { host })
+            )
+
+            this.account.mutedServerByInstance = false
             this.userChanged.emit()
           },
 
@@ -189,41 +257,74 @@ export class UserModerationDropdownComponent implements OnChanges {
           },
           {
             label: this.i18n('Ban'),
-            handler: ({ user }) => this.openBanUserModal(user),
-            isDisplayed: ({ user }) => !user.muted
+            handler: ({ user }: { user: User }) => this.openBanUserModal(user),
+            isDisplayed: ({ user }: { user: User }) => !user.blocked
           },
           {
             label: this.i18n('Unban'),
-            handler: ({ user }) => this.unbanUser(user),
-            isDisplayed: ({ user }) => user.muted
+            handler: ({ user }: { user: User }) => this.unbanUser(user),
+            isDisplayed: ({ user }: { user: User }) => user.blocked
           }
         ])
       }
 
-      // User actions on accounts/servers
+      // Actions on accounts/servers
       if (this.account) {
+        // User actions
         this.userActions = this.userActions.concat([
           {
             label: this.i18n('Mute this account'),
-            isDisplayed: ({ account }) => account.muted === false,
-            handler: ({ account }) => this.blockAccountByUser(account)
+            isDisplayed: ({ account }: { account: Account }) => account.mutedByUser === false,
+            handler: ({ account }: { account: Account }) => this.blockAccountByUser(account)
           },
           {
             label: this.i18n('Unmute this account'),
-            isDisplayed: ({ account }) => account.muted === true,
-            handler: ({ account }) => this.unblockAccountByUser(account)
+            isDisplayed: ({ account }: { account: Account }) => account.mutedByUser === true,
+            handler: ({ account }: { account: Account }) => this.unblockAccountByUser(account)
           },
           {
             label: this.i18n('Mute the instance'),
-            isDisplayed: ({ account }) => !account.userId && account.mutedServer === false,
-            handler: ({ account }) => this.blockServerByUser(account.host)
+            isDisplayed: ({ account }: { account: Account }) => !account.userId && account.mutedServerByInstance === false,
+            handler: ({ account }: { account: Account }) => this.blockServerByUser(account.host)
           },
           {
             label: this.i18n('Unmute the instance'),
-            isDisplayed: ({ account }) => !account.userId && account.mutedServer === true,
-            handler: ({ account }) => this.unblockServerByUser(account.host)
+            isDisplayed: ({ account }: { account: Account }) => !account.userId && account.mutedServerByInstance === true,
+            handler: ({ account }: { account: Account }) => this.unblockServerByUser(account.host)
           }
         ])
+
+        // Instance actions
+        if (authUser.hasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST)) {
+          this.userActions = this.userActions.concat([
+            {
+              label: this.i18n('Mute this account by your instance'),
+              isDisplayed: ({ account }: { account: Account }) => account.mutedByInstance === false,
+              handler: ({ account }: { account: Account }) => this.blockAccountByInstance(account)
+            },
+            {
+              label: this.i18n('Unmute this account by your instance'),
+              isDisplayed: ({ account }: { account: Account }) => account.mutedByInstance === true,
+              handler: ({ account }: { account: Account }) => this.unblockAccountByInstance(account)
+            }
+          ])
+        }
+
+        // Instance actions
+        if (authUser.hasRight(UserRight.MANAGE_SERVERS_BLOCKLIST)) {
+          this.userActions = this.userActions.concat([
+            {
+              label: this.i18n('Mute the instance by your instance'),
+              isDisplayed: ({ account }: { account: Account }) => !account.userId && account.mutedServerByInstance === false,
+              handler: ({ account }: { account: Account }) => this.blockServerByInstance(account.host)
+            },
+            {
+              label: this.i18n('Unmute the instance by your instance'),
+              isDisplayed: ({ account }: { account: Account }) => !account.userId && account.mutedServerByInstance === true,
+              handler: ({ account }: { account: Account }) => this.unblockServerByInstance(account.host)
+            }
+          ])
+        }
       }
     }
   }
index ce2532af509e5329e910967967ccf179cbf9d2ff..77a20cfe160a13e2da9bb2186d9f1fa5921e3e23 100644 (file)
@@ -29,4 +29,6 @@ $input-btn-focus-color: inherit;
 $input-focus-border-color: #ced4da;
 
 $nav-pills-link-active-bg: #F0F0F0;
-$nav-pills-link-active-color: #000;
\ No newline at end of file
+$nav-pills-link-active-color: #000;
+
+$zindex-dropdown: 10000;
\ No newline at end of file
index 08c6b3ff0f1ee3ba28f22d94d22c7b83dc4eade4..dd6d08139158b295f350f435c053ea8a34e22d0d 100644 (file)
@@ -294,7 +294,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
   static async listThreadsForApi (videoId: number, start: number, count: number, sort: string, user?: UserModel) {
     const serverActor = await getServerActor()
     const serverAccountId = serverActor.Account.id
-    const userAccountId = user.Account.id
+    const userAccountId = user ? user.Account.id : undefined
 
     const query = {
       offset: start,
@@ -330,7 +330,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
   static async listThreadCommentsForApi (videoId: number, threadId: number, user?: UserModel) {
     const serverActor = await getServerActor()
     const serverAccountId = serverActor.Account.id
-    const userAccountId = user.Account.id
+    const userAccountId = user ? user.Account.id : undefined
 
     const query = {
       order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ],
index eab99cba7eeaa575592622512b99adbe007a4b71..6c183933b165452829000d5a65c6dd6dfa5678af 100644 (file)
@@ -1255,9 +1255,11 @@ export class VideoModel extends Model<VideoModel> {
 
   // threshold corresponds to how many video the field should have to be returned
   static async getRandomFieldSamples (field: 'category' | 'channelId', threshold: number, count: number) {
-    const actorId = (await getServerActor()).id
+    const serverActor = await getServerActor()
+    const actorId = serverActor.id
 
-    const scopeOptions = {
+    const scopeOptions: AvailableForListIDsOptions = {
+      serverAccountId: serverActor.Account.id,
       actorId,
       includeLocalVideos: true
     }
index 99fe04b8c0de1376f61c2d05e5158622777390b3..eed4b9f3efb94f6c52585cc2fbdaf07881ac524f 100644 (file)
@@ -14,7 +14,7 @@ import {
   userLogin
 } from '../../utils/index'
 import { setAccessTokensToServers } from '../../utils/users/login'
-import { getVideosListWithToken } from '../../utils/videos/videos'
+import { getVideosListWithToken, getVideosList } from '../../utils/videos/videos'
 import {
   addVideoCommentReply,
   addVideoCommentThread,
@@ -41,9 +41,17 @@ import {
 const expect = chai.expect
 
 async function checkAllVideos (url: string, token: string) {
-  const res = await getVideosListWithToken(url, token)
+  {
+    const res = await getVideosListWithToken(url, token)
 
-  expect(res.body.data).to.have.lengthOf(4)
+    expect(res.body.data).to.have.lengthOf(4)
+  }
+
+  {
+    const res = await getVideosList(url)
+
+    expect(res.body.data).to.have.lengthOf(4)
+  }
 }
 
 async function checkAllComments (url: string, token: string, videoUUID: string) {
@@ -444,16 +452,19 @@ describe('Test blocklist', function () {
 
       it('Should hide its videos', async function () {
         for (const token of [ userModeratorToken, servers[ 0 ].accessToken ]) {
-          const res = await getVideosListWithToken(servers[ 0 ].url, token)
+          const res1 = await getVideosList(servers[ 0 ].url)
+          const res2 = await getVideosListWithToken(servers[ 0 ].url, token)
 
-          const videos: Video[] = res.body.data
-          expect(videos).to.have.lengthOf(2)
+          for (const res of [ res1, res2 ]) {
+            const videos: Video[] = res.body.data
+            expect(videos).to.have.lengthOf(2)
 
-          const v1 = videos.find(v => v.name === 'video user 2')
-          const v2 = videos.find(v => v.name === 'video server 2')
+            const v1 = videos.find(v => v.name === 'video user 2')
+            const v2 = videos.find(v => v.name === 'video server 2')
 
-          expect(v1).to.be.undefined
-          expect(v2).to.be.undefined
+            expect(v1).to.be.undefined
+            expect(v2).to.be.undefined
+          }
         }
       })