aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+admin/overview
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/+admin/overview')
-rw-r--r--client/src/app/+admin/overview/index.ts1
-rw-r--r--client/src/app/+admin/overview/overview.routes.ts4
-rw-r--r--client/src/app/+admin/overview/videos/index.ts2
-rw-r--r--client/src/app/+admin/overview/videos/video-list.component.html86
-rw-r--r--client/src/app/+admin/overview/videos/video-list.component.scss10
-rw-r--r--client/src/app/+admin/overview/videos/video-list.component.ts123
-rw-r--r--client/src/app/+admin/overview/videos/video.routes.ts30
7 files changed, 255 insertions, 1 deletions
diff --git a/client/src/app/+admin/overview/index.ts b/client/src/app/+admin/overview/index.ts
index b71a6a45f..a9c46893f 100644
--- a/client/src/app/+admin/overview/index.ts
+++ b/client/src/app/+admin/overview/index.ts
@@ -1,2 +1,3 @@
1export * from './users' 1export * from './users'
2export * from './videos'
2export * from './overview.routes' 3export * from './overview.routes'
diff --git a/client/src/app/+admin/overview/overview.routes.ts b/client/src/app/+admin/overview/overview.routes.ts
index cb5986072..1e6686d16 100644
--- a/client/src/app/+admin/overview/overview.routes.ts
+++ b/client/src/app/+admin/overview/overview.routes.ts
@@ -1,6 +1,8 @@
1import { Routes } from '@angular/router' 1import { Routes } from '@angular/router'
2import { UsersRoutes } from './users' 2import { UsersRoutes } from './users'
3import { VideosRoutes } from './videos'
3 4
4export const OverviewRoutes: Routes = [ 5export const OverviewRoutes: Routes = [
5 ...UsersRoutes 6 ...UsersRoutes,
7 ...VideosRoutes
6] 8]
diff --git a/client/src/app/+admin/overview/videos/index.ts b/client/src/app/+admin/overview/videos/index.ts
new file mode 100644
index 000000000..40c2ffe72
--- /dev/null
+++ b/client/src/app/+admin/overview/videos/index.ts
@@ -0,0 +1,2 @@
1export * from './video-list.component'
2export * from './video.routes'
diff --git a/client/src/app/+admin/overview/videos/video-list.component.html b/client/src/app/+admin/overview/videos/video-list.component.html
new file mode 100644
index 000000000..1f1e9cc6e
--- /dev/null
+++ b/client/src/app/+admin/overview/videos/video-list.component.html
@@ -0,0 +1,86 @@
1<h1>
2 <my-global-icon iconName="videos" aria-hidden="true"></my-global-icon>
3 <ng-container i18n>Videos</ng-container>
4</h1>
5
6<p-table
7 [value]="videos" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
8 [sortField]="sort.field" [sortOrder]="sort.order" dataKey="id" [resizableColumns]="true" [(selection)]="selectedVideos"
9 [lazy]="true" (onLazyLoad)="loadLazy($event)" [lazyLoadOnInit]="false"
10 [showCurrentPageReport]="true" i18n-currentPageReportTemplate
11 currentPageReportTemplate="Showing {{'{first}'}} to {{'{last}'}} of {{'{totalRecords}'}} videos"
12 (onPage)="onPage($event)" [expandedRowKeys]="expandedRows"
13>
14 <ng-template pTemplate="caption">
15 <div class="caption">
16 <div class="left-buttons">
17 <my-action-dropdown
18 *ngIf="isInSelectionMode()" i18n-label label="Batch actions" theme="orange"
19 [actions]="bulkVideoActions" [entry]="selectedVideos"
20 >
21 </my-action-dropdown>
22 </div>
23
24 <div class="ml-auto">
25 <my-advanced-input-filter [filters]="inputFilters" (search)="onSearch($event)"></my-advanced-input-filter>
26 </div>
27
28 </div>
29 </ng-template>
30
31 <ng-template pTemplate="header">
32 <tr>
33 <th style="width: 40px">
34 <p-tableHeaderCheckbox ariaLabel="Select all rows" i18n-ariaLabel></p-tableHeaderCheckbox>
35 </th>
36 <th style="width: 40px"></th>
37 <th style="width: 60px;"></th>
38 <th i18n>Video</th>
39 <th i18n>Info</th>
40 <th style="width: 150px;" i18n pSortableColumn="publishedAt">Published <p-sortIcon field="publishedAt"></p-sortIcon></th>
41 </tr>
42 </ng-template>
43
44 <ng-template pTemplate="body" let-expanded="expanded" let-video>
45
46 <tr [pSelectableRow]="video">
47 <td class="checkbox-cell">
48 <p-tableCheckbox [value]="video" ariaLabel="Select this row" i18n-ariaLabel></p-tableCheckbox>
49 </td>
50
51 <td class="expand-cell" [pRowToggler]="video">
52 <my-table-expander-icon [expanded]="expanded"></my-table-expander-icon>
53 </td>
54
55 <td class="action-cell">
56 <my-video-actions-dropdown
57 placement="bottom auto" buttonDirection="horizontal" [buttonStyled]="true" [video]="video"
58 [displayOptions]="videoActionsOptions" (videoRemoved)="onVideoRemoved()"
59 ></my-video-actions-dropdown>
60 </td>
61
62 <td>
63 <my-video-cell [video]="video"></my-video-cell>
64 </td>
65
66 <td>
67 <span class="badge badge-blue" i18n>{{ video.privacy.label }}</span>
68 <span *ngIf="video.nsfw" class="badge badge-red" i18n>NSFW</span>
69 <span *ngIf="video.blocked" class="badge badge-red" i18n>NSFW</span>
70 </td>
71
72 <td>
73 {{ video.publishedAt | date: 'short' }}
74 </td>
75
76 </tr>
77 </ng-template>
78
79 <ng-template pTemplate="rowexpansion" let-video>
80 <tr>
81 <td colspan="50">
82 <my-embed [video]="video"></my-embed>
83 </td>
84 </tr>
85 </ng-template>
86</p-table>
diff --git a/client/src/app/+admin/overview/videos/video-list.component.scss b/client/src/app/+admin/overview/videos/video-list.component.scss
new file mode 100644
index 000000000..fcdb457f2
--- /dev/null
+++ b/client/src/app/+admin/overview/videos/video-list.component.scss
@@ -0,0 +1,10 @@
1@use '_variables' as *;
2@use '_mixins' as *;
3my-embed {
4 display: block;
5 max-width: 500px;
6}
7
8.badge {
9 @include table-badge;
10}
diff --git a/client/src/app/+admin/overview/videos/video-list.component.ts b/client/src/app/+admin/overview/videos/video-list.component.ts
new file mode 100644
index 000000000..a445bc209
--- /dev/null
+++ b/client/src/app/+admin/overview/videos/video-list.component.ts
@@ -0,0 +1,123 @@
1import { SortMeta } from 'primeng/api'
2import { Component, OnInit } from '@angular/core'
3import { ActivatedRoute, Router } from '@angular/router'
4import { AuthService, ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
5import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
6import { UserRight } from '@shared/models'
7import { AdvancedInputFilter } from '@app/shared/shared-forms'
8import { VideoActionsDisplayType } from '@app/shared/shared-video-miniature'
9
10@Component({
11 selector: 'my-video-list',
12 templateUrl: './video-list.component.html',
13 styleUrls: [ './video-list.component.scss' ]
14})
15export class VideoListComponent extends RestTable implements OnInit {
16 videos: Video[] = []
17
18 totalRecords = 0
19 sort: SortMeta = { field: 'publishedAt', order: 1 }
20 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
21
22 bulkVideoActions: DropdownAction<Video[]>[][] = []
23
24 selectedVideos: Video[] = []
25
26 inputFilters: AdvancedInputFilter[] = [
27 {
28 title: $localize`Advanced filters`,
29 children: [
30 {
31 queryParams: { search: 'local:true' },
32 label: $localize`Only local videos`
33 }
34 ]
35 }
36 ]
37
38 videoActionsOptions: VideoActionsDisplayType = {
39 playlist: false,
40 download: false,
41 update: true,
42 blacklist: true,
43 delete: true,
44 report: false,
45 duplicate: true,
46 mute: true,
47 liveInfo: false
48 }
49
50 constructor (
51 protected route: ActivatedRoute,
52 protected router: Router,
53 private confirmService: ConfirmService,
54 private auth: AuthService,
55 private notifier: Notifier,
56 private videoService: VideoService
57 ) {
58 super()
59 }
60
61 get authUser () {
62 return this.auth.getUser()
63 }
64
65 ngOnInit () {
66 this.initialize()
67
68 this.bulkVideoActions = [
69 [
70 {
71 label: $localize`Delete`,
72 handler: videos => this.removeVideos(videos),
73 isDisplayed: () => this.authUser.hasRight(UserRight.REMOVE_ANY_VIDEO)
74 }
75 ]
76 ]
77 }
78
79 getIdentifier () {
80 return 'VideoListComponent'
81 }
82
83 isInSelectionMode () {
84 return this.selectedVideos.length !== 0
85 }
86
87 onVideoRemoved () {
88 this.reloadData()
89 }
90
91 protected reloadData () {
92 this.selectedVideos = []
93
94 this.videoService.getAdminVideos({
95 pagination: this.pagination,
96 sort: this.sort,
97 search: this.search
98 }).subscribe({
99 next: resultList => {
100 this.videos = resultList.data
101 this.totalRecords = resultList.total
102 },
103
104 error: err => this.notifier.error(err.message)
105 })
106 }
107
108 private async removeVideos (videos: Video[]) {
109 const message = $localize`Are you sure you want to delete these ${videos.length} videos?`
110 const res = await this.confirmService.confirm(message, $localize`Delete`)
111 if (res === false) return
112
113 this.videoService.removeVideo(videos.map(v => v.id))
114 .subscribe({
115 next: () => {
116 this.notifier.success($localize`${videos.length} videos deleted.`)
117 this.reloadData()
118 },
119
120 error: err => this.notifier.error(err.message)
121 })
122 }
123}
diff --git a/client/src/app/+admin/overview/videos/video.routes.ts b/client/src/app/+admin/overview/videos/video.routes.ts
new file mode 100644
index 000000000..984df7b82
--- /dev/null
+++ b/client/src/app/+admin/overview/videos/video.routes.ts
@@ -0,0 +1,30 @@
1import { Routes } from '@angular/router'
2import { UserRightGuard } from '@app/core'
3import { UserRight } from '@shared/models'
4import { VideoListComponent } from './video-list.component'
5
6export const VideosRoutes: Routes = [
7 {
8 path: 'videos',
9 canActivate: [ UserRightGuard ],
10 data: {
11 userRight: UserRight.SEE_ALL_VIDEOS
12 },
13 children: [
14 {
15 path: '',
16 redirectTo: 'list',
17 pathMatch: 'full'
18 },
19 {
20 path: 'list',
21 component: VideoListComponent,
22 data: {
23 meta: {
24 title: $localize`Videos list`
25 }
26 }
27 }
28 ]
29 }
30]