diff options
Diffstat (limited to 'client/src/app/+admin')
19 files changed, 296 insertions, 2 deletions
diff --git a/client/src/app/+admin/admin-routing.module.ts b/client/src/app/+admin/admin-routing.module.ts index 215da1e4f..79c57221b 100644 --- a/client/src/app/+admin/admin-routing.module.ts +++ b/client/src/app/+admin/admin-routing.module.ts | |||
@@ -9,6 +9,7 @@ import { FollowsRoutes } from './follows' | |||
9 | import { UsersRoutes } from './users' | 9 | import { UsersRoutes } from './users' |
10 | import { ModerationRoutes } from '@app/+admin/moderation/moderation.routes' | 10 | import { ModerationRoutes } from '@app/+admin/moderation/moderation.routes' |
11 | import { SystemRoutes } from '@app/+admin/system' | 11 | import { SystemRoutes } from '@app/+admin/system' |
12 | import { PluginsRoutes } from '@app/+admin/plugins/plugins.routes' | ||
12 | 13 | ||
13 | const adminRoutes: Routes = [ | 14 | const adminRoutes: Routes = [ |
14 | { | 15 | { |
@@ -26,7 +27,8 @@ const adminRoutes: Routes = [ | |||
26 | ...UsersRoutes, | 27 | ...UsersRoutes, |
27 | ...ModerationRoutes, | 28 | ...ModerationRoutes, |
28 | ...SystemRoutes, | 29 | ...SystemRoutes, |
29 | ...ConfigRoutes | 30 | ...ConfigRoutes, |
31 | ...PluginsRoutes | ||
30 | ] | 32 | ] |
31 | } | 33 | } |
32 | ] | 34 | ] |
diff --git a/client/src/app/+admin/admin.component.html b/client/src/app/+admin/admin.component.html index 98f45a7d1..9a3d90c18 100644 --- a/client/src/app/+admin/admin.component.html +++ b/client/src/app/+admin/admin.component.html | |||
@@ -16,6 +16,10 @@ | |||
16 | Configuration | 16 | Configuration |
17 | </a> | 17 | </a> |
18 | 18 | ||
19 | <a i18n *ngIf="hasPluginsRight()" routerLink="/admin/plugins" routerLinkActive="active" class="title-page"> | ||
20 | Plugins/Themes | ||
21 | </a> | ||
22 | |||
19 | <a i18n *ngIf="hasJobsRight() || hasLogsRight() || hasDebugRight()" routerLink="/admin/system" routerLinkActive="active" class="title-page"> | 23 | <a i18n *ngIf="hasJobsRight() || hasLogsRight() || hasDebugRight()" routerLink="/admin/system" routerLinkActive="active" class="title-page"> |
20 | System | 24 | System |
21 | </a> | 25 | </a> |
diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts index 408de4837..b23999d40 100644 --- a/client/src/app/+admin/admin.component.ts +++ b/client/src/app/+admin/admin.component.ts | |||
@@ -28,6 +28,10 @@ export class AdminComponent { | |||
28 | return this.auth.getUser().hasRight(UserRight.MANAGE_CONFIGURATION) | 28 | return this.auth.getUser().hasRight(UserRight.MANAGE_CONFIGURATION) |
29 | } | 29 | } |
30 | 30 | ||
31 | hasPluginsRight () { | ||
32 | return this.auth.getUser().hasRight(UserRight.MANAGE_PLUGINS) | ||
33 | } | ||
34 | |||
31 | hasLogsRight () { | 35 | hasLogsRight () { |
32 | return this.auth.getUser().hasRight(UserRight.MANAGE_LOGS) | 36 | return this.auth.getUser().hasRight(UserRight.MANAGE_LOGS) |
33 | } | 37 | } |
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts index 9ab883f60..256b7e1f5 100644 --- a/client/src/app/+admin/admin.module.ts +++ b/client/src/app/+admin/admin.module.ts | |||
@@ -21,11 +21,18 @@ import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } f | |||
21 | import { JobsComponent } from '@app/+admin/system/jobs/jobs.component' | 21 | import { JobsComponent } from '@app/+admin/system/jobs/jobs.component' |
22 | import { JobService, LogsComponent, LogsService, SystemComponent } from '@app/+admin/system' | 22 | import { JobService, LogsComponent, LogsService, SystemComponent } from '@app/+admin/system' |
23 | import { DebugComponent, DebugService } from '@app/+admin/system/debug' | 23 | import { DebugComponent, DebugService } from '@app/+admin/system/debug' |
24 | import { PluginsComponent } from '@app/+admin/plugins/plugins.component' | ||
25 | import { PluginListInstalledComponent } from '@app/+admin/plugins/plugin-list-installed/plugin-list-installed.component' | ||
26 | import { PluginSearchComponent } from '@app/+admin/plugins/plugin-search/plugin-search.component' | ||
27 | import { PluginShowInstalledComponent } from '@app/+admin/plugins/plugin-show-installed/plugin-show-installed.component' | ||
28 | import { SelectButtonModule } from 'primeng/primeng' | ||
29 | import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' | ||
24 | 30 | ||
25 | @NgModule({ | 31 | @NgModule({ |
26 | imports: [ | 32 | imports: [ |
27 | AdminRoutingModule, | 33 | AdminRoutingModule, |
28 | TableModule, | 34 | TableModule, |
35 | SelectButtonModule, | ||
29 | SharedModule | 36 | SharedModule |
30 | ], | 37 | ], |
31 | 38 | ||
@@ -52,6 +59,11 @@ import { DebugComponent, DebugService } from '@app/+admin/system/debug' | |||
52 | InstanceServerBlocklistComponent, | 59 | InstanceServerBlocklistComponent, |
53 | InstanceAccountBlocklistComponent, | 60 | InstanceAccountBlocklistComponent, |
54 | 61 | ||
62 | PluginsComponent, | ||
63 | PluginListInstalledComponent, | ||
64 | PluginSearchComponent, | ||
65 | PluginShowInstalledComponent, | ||
66 | |||
55 | SystemComponent, | 67 | SystemComponent, |
56 | JobsComponent, | 68 | JobsComponent, |
57 | LogsComponent, | 69 | LogsComponent, |
@@ -70,7 +82,8 @@ import { DebugComponent, DebugService } from '@app/+admin/system/debug' | |||
70 | JobService, | 82 | JobService, |
71 | LogsService, | 83 | LogsService, |
72 | DebugService, | 84 | DebugService, |
73 | ConfigService | 85 | ConfigService, |
86 | PluginApiService | ||
74 | ] | 87 | ] |
75 | }) | 88 | }) |
76 | export class AdminModule { } | 89 | export class AdminModule { } |
diff --git a/client/src/app/+admin/plugins/index.ts b/client/src/app/+admin/plugins/index.ts new file mode 100644 index 000000000..b75a94556 --- /dev/null +++ b/client/src/app/+admin/plugins/index.ts | |||
@@ -0,0 +1 @@ | |||
export * from './plugins.component' | |||
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html new file mode 100644 index 000000000..6bb8bcd75 --- /dev/null +++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html | |||
@@ -0,0 +1,13 @@ | |||
1 | <div class="toggle-plugin-type"> | ||
2 | <p-selectButton [options]="pluginTypeOptions" [(ngModel)]="pluginType" (ngModelChange)="reloadPlugins()"></p-selectButton> | ||
3 | </div> | ||
4 | |||
5 | <div class="no-results" i18n *ngIf="pagination.totalItems === 0"> | ||
6 | {{ getNoResultMessage() }} | ||
7 | </div> | ||
8 | |||
9 | <div class="plugins" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true"> | ||
10 | <div class="section plugin" *ngFor="let plugin of plugins"> | ||
11 | {{ plugin.name }} | ||
12 | </div> | ||
13 | </div> | ||
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss new file mode 100644 index 000000000..9e98fcd34 --- /dev/null +++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss | |||
@@ -0,0 +1,8 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .toggle-plugin-type { | ||
5 | display: flex; | ||
6 | justify-content: center; | ||
7 | margin-bottom: 30px; | ||
8 | } | ||
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts new file mode 100644 index 000000000..9745bc36b --- /dev/null +++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts | |||
@@ -0,0 +1,72 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | ||
2 | import { PluginType } from '@shared/models/plugins/plugin.type' | ||
3 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
4 | import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' | ||
5 | import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model' | ||
6 | import { Notifier } from '@app/core' | ||
7 | import { PeerTubePlugin } from '@shared/models/plugins/peertube-plugin.model' | ||
8 | |||
9 | @Component({ | ||
10 | selector: 'my-plugin-list-installed', | ||
11 | templateUrl: './plugin-list-installed.component.html', | ||
12 | styleUrls: [ './plugin-list-installed.component.scss' ] | ||
13 | }) | ||
14 | export class PluginListInstalledComponent implements OnInit { | ||
15 | pluginTypeOptions: { label: string, value: PluginType }[] = [] | ||
16 | pluginType: PluginType = PluginType.PLUGIN | ||
17 | |||
18 | pagination: ComponentPagination = { | ||
19 | currentPage: 1, | ||
20 | itemsPerPage: 10 | ||
21 | } | ||
22 | sort = 'name' | ||
23 | |||
24 | plugins: PeerTubePlugin[] = [] | ||
25 | |||
26 | constructor ( | ||
27 | private i18n: I18n, | ||
28 | private pluginService: PluginApiService, | ||
29 | private notifier: Notifier | ||
30 | ) { | ||
31 | this.pluginTypeOptions = this.pluginService.getPluginTypeOptions() | ||
32 | } | ||
33 | |||
34 | ngOnInit () { | ||
35 | this.reloadPlugins() | ||
36 | } | ||
37 | |||
38 | reloadPlugins () { | ||
39 | this.pagination.currentPage = 1 | ||
40 | this.plugins = [] | ||
41 | |||
42 | this.loadMorePlugins() | ||
43 | } | ||
44 | |||
45 | loadMorePlugins () { | ||
46 | this.pluginService.getPlugins(this.pluginType, this.pagination, this.sort) | ||
47 | .subscribe( | ||
48 | res => { | ||
49 | this.plugins = this.plugins.concat(res.data) | ||
50 | this.pagination.totalItems = res.total | ||
51 | }, | ||
52 | |||
53 | err => this.notifier.error(err.message) | ||
54 | ) | ||
55 | } | ||
56 | |||
57 | onNearOfBottom () { | ||
58 | if (!hasMoreItems(this.pagination)) return | ||
59 | |||
60 | this.pagination.currentPage += 1 | ||
61 | |||
62 | this.loadMorePlugins() | ||
63 | } | ||
64 | |||
65 | getNoResultMessage () { | ||
66 | if (this.pluginType === PluginType.PLUGIN) { | ||
67 | return this.i18n('You don\'t have plugins installed yet.') | ||
68 | } | ||
69 | |||
70 | return this.i18n('You don\'t have themes installed yet.') | ||
71 | } | ||
72 | } | ||
diff --git a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.html | |||
diff --git a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.scss b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.scss new file mode 100644 index 000000000..5e6774739 --- /dev/null +++ b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.scss | |||
@@ -0,0 +1,2 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
diff --git a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts new file mode 100644 index 000000000..db1f91f3d --- /dev/null +++ b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts | |||
@@ -0,0 +1,30 @@ | |||
1 | import { Component, OnInit, ViewChild } from '@angular/core' | ||
2 | import { Notifier } from '@app/core' | ||
3 | import { SortMeta } from 'primeng/components/common/sortmeta' | ||
4 | import { ConfirmService, ServerService } from '../../../core' | ||
5 | import { RestPagination, RestTable, UserService } from '../../../shared' | ||
6 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
7 | import { User } from '../../../../../../shared' | ||
8 | import { UserBanModalComponent } from '@app/shared/moderation' | ||
9 | import { DropdownAction } from '@app/shared/buttons/action-dropdown.component' | ||
10 | import { PluginType } from '@shared/models/plugins/plugin.type' | ||
11 | import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' | ||
12 | |||
13 | @Component({ | ||
14 | selector: 'my-plugin-search', | ||
15 | templateUrl: './plugin-search.component.html', | ||
16 | styleUrls: [ './plugin-search.component.scss' ] | ||
17 | }) | ||
18 | export class PluginSearchComponent implements OnInit { | ||
19 | pluginTypeOptions: { label: string, value: PluginType }[] = [] | ||
20 | |||
21 | constructor ( | ||
22 | private i18n: I18n, | ||
23 | private pluginService: PluginApiService | ||
24 | ) { | ||
25 | this.pluginTypeOptions = this.pluginService.getPluginTypeOptions() | ||
26 | } | ||
27 | |||
28 | ngOnInit () { | ||
29 | } | ||
30 | } | ||
diff --git a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.html b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.html | |||
diff --git a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss new file mode 100644 index 000000000..5e6774739 --- /dev/null +++ b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss | |||
@@ -0,0 +1,2 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
diff --git a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts new file mode 100644 index 000000000..f65599532 --- /dev/null +++ b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts | |||
@@ -0,0 +1,14 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | ||
2 | |||
3 | @Component({ | ||
4 | selector: 'my-plugin-show-installed', | ||
5 | templateUrl: './plugin-show-installed.component.html', | ||
6 | styleUrls: [ './plugin-show-installed.component.scss' ] | ||
7 | }) | ||
8 | export class PluginShowInstalledComponent implements OnInit { | ||
9 | |||
10 | ngOnInit () { | ||
11 | |||
12 | } | ||
13 | |||
14 | } | ||
diff --git a/client/src/app/+admin/plugins/plugins.component.html b/client/src/app/+admin/plugins/plugins.component.html new file mode 100644 index 000000000..3dc4939da --- /dev/null +++ b/client/src/app/+admin/plugins/plugins.component.html | |||
@@ -0,0 +1,11 @@ | |||
1 | <div class="admin-sub-header"> | ||
2 | <div i18n class="form-sub-title">Plugins/Themes</div> | ||
3 | |||
4 | <div class="admin-sub-nav"> | ||
5 | <a i18n routerLink="list-installed" routerLinkActive="active">Installed</a> | ||
6 | |||
7 | <a i18n routerLink="search" routerLinkActive="active">Search</a> | ||
8 | </div> | ||
9 | </div> | ||
10 | |||
11 | <router-outlet></router-outlet> | ||
diff --git a/client/src/app/+admin/plugins/plugins.component.scss b/client/src/app/+admin/plugins/plugins.component.scss new file mode 100644 index 000000000..9f61bcf7a --- /dev/null +++ b/client/src/app/+admin/plugins/plugins.component.scss | |||
@@ -0,0 +1,7 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .form-sub-title { | ||
5 | flex-grow: 0; | ||
6 | margin-right: 30px; | ||
7 | } | ||
diff --git a/client/src/app/+admin/plugins/plugins.component.ts b/client/src/app/+admin/plugins/plugins.component.ts new file mode 100644 index 000000000..6ec6fa4a1 --- /dev/null +++ b/client/src/app/+admin/plugins/plugins.component.ts | |||
@@ -0,0 +1,8 @@ | |||
1 | import { Component } from '@angular/core' | ||
2 | |||
3 | @Component({ | ||
4 | templateUrl: './plugins.component.html', | ||
5 | styleUrls: [ './plugins.component.scss' ] | ||
6 | }) | ||
7 | export class PluginsComponent { | ||
8 | } | ||
diff --git a/client/src/app/+admin/plugins/plugins.routes.ts b/client/src/app/+admin/plugins/plugins.routes.ts new file mode 100644 index 000000000..58b5534fb --- /dev/null +++ b/client/src/app/+admin/plugins/plugins.routes.ts | |||
@@ -0,0 +1,53 @@ | |||
1 | import { Routes } from '@angular/router' | ||
2 | |||
3 | import { UserRightGuard } from '../../core' | ||
4 | import { UserRight } from '../../../../../shared' | ||
5 | import { PluginListInstalledComponent } from '@app/+admin/plugins/plugin-list-installed/plugin-list-installed.component' | ||
6 | import { PluginSearchComponent } from '@app/+admin/plugins/plugin-search/plugin-search.component' | ||
7 | import { PluginShowInstalledComponent } from '@app/+admin/plugins/plugin-show-installed/plugin-show-installed.component' | ||
8 | import { PluginsComponent } from '@app/+admin/plugins/plugins.component' | ||
9 | |||
10 | export const PluginsRoutes: Routes = [ | ||
11 | { | ||
12 | path: 'plugins', | ||
13 | component: PluginsComponent, | ||
14 | canActivate: [ UserRightGuard ], | ||
15 | data: { | ||
16 | userRight: UserRight.MANAGE_PLUGINS | ||
17 | }, | ||
18 | children: [ | ||
19 | { | ||
20 | path: '', | ||
21 | redirectTo: 'list-installed', | ||
22 | pathMatch: 'full' | ||
23 | }, | ||
24 | { | ||
25 | path: 'list-installed', | ||
26 | component: PluginListInstalledComponent, | ||
27 | data: { | ||
28 | meta: { | ||
29 | title: 'List installed plugins' | ||
30 | } | ||
31 | } | ||
32 | }, | ||
33 | { | ||
34 | path: 'search', | ||
35 | component: PluginSearchComponent, | ||
36 | data: { | ||
37 | meta: { | ||
38 | title: 'Search plugins' | ||
39 | } | ||
40 | } | ||
41 | }, | ||
42 | { | ||
43 | path: 'show/:name', | ||
44 | component: PluginShowInstalledComponent, | ||
45 | data: { | ||
46 | meta: { | ||
47 | title: 'Show plugin' | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | ] | ||
52 | } | ||
53 | ] | ||
diff --git a/client/src/app/+admin/plugins/shared/plugin-api.service.ts b/client/src/app/+admin/plugins/shared/plugin-api.service.ts new file mode 100644 index 000000000..bfc2b918f --- /dev/null +++ b/client/src/app/+admin/plugins/shared/plugin-api.service.ts | |||
@@ -0,0 +1,50 @@ | |||
1 | import { catchError } from 'rxjs/operators' | ||
2 | import { HttpClient, HttpParams } from '@angular/common/http' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { environment } from '../../../../environments/environment' | ||
5 | import { RestExtractor, RestService } from '../../../shared' | ||
6 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
7 | import { PluginType } from '@shared/models/plugins/plugin.type' | ||
8 | import { ComponentPagination } from '@app/shared/rest/component-pagination.model' | ||
9 | import { ResultList } from '@shared/models' | ||
10 | import { PeerTubePlugin } from '@shared/models/plugins/peertube-plugin.model' | ||
11 | |||
12 | @Injectable() | ||
13 | export class PluginApiService { | ||
14 | private static BASE_APPLICATION_URL = environment.apiUrl + '/api/v1/plugins' | ||
15 | |||
16 | constructor ( | ||
17 | private authHttp: HttpClient, | ||
18 | private restExtractor: RestExtractor, | ||
19 | private restService: RestService, | ||
20 | private i18n: I18n | ||
21 | ) { } | ||
22 | |||
23 | getPluginTypeOptions () { | ||
24 | return [ | ||
25 | { | ||
26 | label: this.i18n('Plugin'), | ||
27 | value: PluginType.PLUGIN | ||
28 | }, | ||
29 | { | ||
30 | label: this.i18n('Theme'), | ||
31 | value: PluginType.THEME | ||
32 | } | ||
33 | ] | ||
34 | } | ||
35 | |||
36 | getPlugins ( | ||
37 | type: PluginType, | ||
38 | componentPagination: ComponentPagination, | ||
39 | sort: string | ||
40 | ) { | ||
41 | const pagination = this.restService.componentPaginationToRestPagination(componentPagination) | ||
42 | |||
43 | let params = new HttpParams() | ||
44 | params = this.restService.addRestGetParams(params, pagination, sort) | ||
45 | params = params.append('type', type.toString()) | ||
46 | |||
47 | return this.authHttp.get<ResultList<PeerTubePlugin>>(PluginApiService.BASE_APPLICATION_URL, { params }) | ||
48 | .pipe(catchError(res => this.restExtractor.handleError(res))) | ||
49 | } | ||
50 | } | ||