WIP plugins: list installed plugins in client
This commit is contained in:
parent
ad91e7006e
commit
d00dc28dd7
|
@ -9,6 +9,7 @@ import { FollowsRoutes } from './follows'
|
||||||
import { UsersRoutes } from './users'
|
import { UsersRoutes } from './users'
|
||||||
import { ModerationRoutes } from '@app/+admin/moderation/moderation.routes'
|
import { ModerationRoutes } from '@app/+admin/moderation/moderation.routes'
|
||||||
import { SystemRoutes } from '@app/+admin/system'
|
import { SystemRoutes } from '@app/+admin/system'
|
||||||
|
import { PluginsRoutes } from '@app/+admin/plugins/plugins.routes'
|
||||||
|
|
||||||
const adminRoutes: Routes = [
|
const adminRoutes: Routes = [
|
||||||
{
|
{
|
||||||
|
@ -26,7 +27,8 @@ const adminRoutes: Routes = [
|
||||||
...UsersRoutes,
|
...UsersRoutes,
|
||||||
...ModerationRoutes,
|
...ModerationRoutes,
|
||||||
...SystemRoutes,
|
...SystemRoutes,
|
||||||
...ConfigRoutes
|
...ConfigRoutes,
|
||||||
|
...PluginsRoutes
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -16,6 +16,10 @@
|
||||||
Configuration
|
Configuration
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a i18n *ngIf="hasPluginsRight()" routerLink="/admin/plugins" routerLinkActive="active" class="title-page">
|
||||||
|
Plugins/Themes
|
||||||
|
</a>
|
||||||
|
|
||||||
<a i18n *ngIf="hasJobsRight() || hasLogsRight() || hasDebugRight()" routerLink="/admin/system" routerLinkActive="active" class="title-page">
|
<a i18n *ngIf="hasJobsRight() || hasLogsRight() || hasDebugRight()" routerLink="/admin/system" routerLinkActive="active" class="title-page">
|
||||||
System
|
System
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -28,6 +28,10 @@ export class AdminComponent {
|
||||||
return this.auth.getUser().hasRight(UserRight.MANAGE_CONFIGURATION)
|
return this.auth.getUser().hasRight(UserRight.MANAGE_CONFIGURATION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasPluginsRight () {
|
||||||
|
return this.auth.getUser().hasRight(UserRight.MANAGE_PLUGINS)
|
||||||
|
}
|
||||||
|
|
||||||
hasLogsRight () {
|
hasLogsRight () {
|
||||||
return this.auth.getUser().hasRight(UserRight.MANAGE_LOGS)
|
return this.auth.getUser().hasRight(UserRight.MANAGE_LOGS)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,11 +21,18 @@ import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } f
|
||||||
import { JobsComponent } from '@app/+admin/system/jobs/jobs.component'
|
import { JobsComponent } from '@app/+admin/system/jobs/jobs.component'
|
||||||
import { JobService, LogsComponent, LogsService, SystemComponent } from '@app/+admin/system'
|
import { JobService, LogsComponent, LogsService, SystemComponent } from '@app/+admin/system'
|
||||||
import { DebugComponent, DebugService } from '@app/+admin/system/debug'
|
import { DebugComponent, DebugService } from '@app/+admin/system/debug'
|
||||||
|
import { PluginsComponent } from '@app/+admin/plugins/plugins.component'
|
||||||
|
import { PluginListInstalledComponent } from '@app/+admin/plugins/plugin-list-installed/plugin-list-installed.component'
|
||||||
|
import { PluginSearchComponent } from '@app/+admin/plugins/plugin-search/plugin-search.component'
|
||||||
|
import { PluginShowInstalledComponent } from '@app/+admin/plugins/plugin-show-installed/plugin-show-installed.component'
|
||||||
|
import { SelectButtonModule } from 'primeng/primeng'
|
||||||
|
import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
AdminRoutingModule,
|
AdminRoutingModule,
|
||||||
TableModule,
|
TableModule,
|
||||||
|
SelectButtonModule,
|
||||||
SharedModule
|
SharedModule
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -52,6 +59,11 @@ import { DebugComponent, DebugService } from '@app/+admin/system/debug'
|
||||||
InstanceServerBlocklistComponent,
|
InstanceServerBlocklistComponent,
|
||||||
InstanceAccountBlocklistComponent,
|
InstanceAccountBlocklistComponent,
|
||||||
|
|
||||||
|
PluginsComponent,
|
||||||
|
PluginListInstalledComponent,
|
||||||
|
PluginSearchComponent,
|
||||||
|
PluginShowInstalledComponent,
|
||||||
|
|
||||||
SystemComponent,
|
SystemComponent,
|
||||||
JobsComponent,
|
JobsComponent,
|
||||||
LogsComponent,
|
LogsComponent,
|
||||||
|
@ -70,7 +82,8 @@ import { DebugComponent, DebugService } from '@app/+admin/system/debug'
|
||||||
JobService,
|
JobService,
|
||||||
LogsService,
|
LogsService,
|
||||||
DebugService,
|
DebugService,
|
||||||
ConfigService
|
ConfigService,
|
||||||
|
PluginApiService
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AdminModule { }
|
export class AdminModule { }
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './plugins.component'
|
|
@ -0,0 +1,13 @@
|
||||||
|
<div class="toggle-plugin-type">
|
||||||
|
<p-selectButton [options]="pluginTypeOptions" [(ngModel)]="pluginType" (ngModelChange)="reloadPlugins()"></p-selectButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="no-results" i18n *ngIf="pagination.totalItems === 0">
|
||||||
|
{{ getNoResultMessage() }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="plugins" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true">
|
||||||
|
<div class="section plugin" *ngFor="let plugin of plugins">
|
||||||
|
{{ plugin.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,8 @@
|
||||||
|
@import '_variables';
|
||||||
|
@import '_mixins';
|
||||||
|
|
||||||
|
.toggle-plugin-type {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { Component, OnInit } from '@angular/core'
|
||||||
|
import { PluginType } from '@shared/models/plugins/plugin.type'
|
||||||
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
|
import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
|
||||||
|
import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model'
|
||||||
|
import { Notifier } from '@app/core'
|
||||||
|
import { PeerTubePlugin } from '@shared/models/plugins/peertube-plugin.model'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-plugin-list-installed',
|
||||||
|
templateUrl: './plugin-list-installed.component.html',
|
||||||
|
styleUrls: [ './plugin-list-installed.component.scss' ]
|
||||||
|
})
|
||||||
|
export class PluginListInstalledComponent implements OnInit {
|
||||||
|
pluginTypeOptions: { label: string, value: PluginType }[] = []
|
||||||
|
pluginType: PluginType = PluginType.PLUGIN
|
||||||
|
|
||||||
|
pagination: ComponentPagination = {
|
||||||
|
currentPage: 1,
|
||||||
|
itemsPerPage: 10
|
||||||
|
}
|
||||||
|
sort = 'name'
|
||||||
|
|
||||||
|
plugins: PeerTubePlugin[] = []
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private i18n: I18n,
|
||||||
|
private pluginService: PluginApiService,
|
||||||
|
private notifier: Notifier
|
||||||
|
) {
|
||||||
|
this.pluginTypeOptions = this.pluginService.getPluginTypeOptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
this.reloadPlugins()
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadPlugins () {
|
||||||
|
this.pagination.currentPage = 1
|
||||||
|
this.plugins = []
|
||||||
|
|
||||||
|
this.loadMorePlugins()
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMorePlugins () {
|
||||||
|
this.pluginService.getPlugins(this.pluginType, this.pagination, this.sort)
|
||||||
|
.subscribe(
|
||||||
|
res => {
|
||||||
|
this.plugins = this.plugins.concat(res.data)
|
||||||
|
this.pagination.totalItems = res.total
|
||||||
|
},
|
||||||
|
|
||||||
|
err => this.notifier.error(err.message)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onNearOfBottom () {
|
||||||
|
if (!hasMoreItems(this.pagination)) return
|
||||||
|
|
||||||
|
this.pagination.currentPage += 1
|
||||||
|
|
||||||
|
this.loadMorePlugins()
|
||||||
|
}
|
||||||
|
|
||||||
|
getNoResultMessage () {
|
||||||
|
if (this.pluginType === PluginType.PLUGIN) {
|
||||||
|
return this.i18n('You don\'t have plugins installed yet.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.i18n('You don\'t have themes installed yet.')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
@import '_variables';
|
||||||
|
@import '_mixins';
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||||
|
import { Notifier } from '@app/core'
|
||||||
|
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||||
|
import { ConfirmService, ServerService } from '../../../core'
|
||||||
|
import { RestPagination, RestTable, UserService } from '../../../shared'
|
||||||
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
|
import { User } from '../../../../../../shared'
|
||||||
|
import { UserBanModalComponent } from '@app/shared/moderation'
|
||||||
|
import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
|
||||||
|
import { PluginType } from '@shared/models/plugins/plugin.type'
|
||||||
|
import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-plugin-search',
|
||||||
|
templateUrl: './plugin-search.component.html',
|
||||||
|
styleUrls: [ './plugin-search.component.scss' ]
|
||||||
|
})
|
||||||
|
export class PluginSearchComponent implements OnInit {
|
||||||
|
pluginTypeOptions: { label: string, value: PluginType }[] = []
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private i18n: I18n,
|
||||||
|
private pluginService: PluginApiService
|
||||||
|
) {
|
||||||
|
this.pluginTypeOptions = this.pluginService.getPluginTypeOptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
@import '_variables';
|
||||||
|
@import '_mixins';
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Component, OnInit } from '@angular/core'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-plugin-show-installed',
|
||||||
|
templateUrl: './plugin-show-installed.component.html',
|
||||||
|
styleUrls: [ './plugin-show-installed.component.scss' ]
|
||||||
|
})
|
||||||
|
export class PluginShowInstalledComponent implements OnInit {
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<div class="admin-sub-header">
|
||||||
|
<div i18n class="form-sub-title">Plugins/Themes</div>
|
||||||
|
|
||||||
|
<div class="admin-sub-nav">
|
||||||
|
<a i18n routerLink="list-installed" routerLinkActive="active">Installed</a>
|
||||||
|
|
||||||
|
<a i18n routerLink="search" routerLinkActive="active">Search</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<router-outlet></router-outlet>
|
|
@ -0,0 +1,7 @@
|
||||||
|
@import '_variables';
|
||||||
|
@import '_mixins';
|
||||||
|
|
||||||
|
.form-sub-title {
|
||||||
|
flex-grow: 0;
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Component } from '@angular/core'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: './plugins.component.html',
|
||||||
|
styleUrls: [ './plugins.component.scss' ]
|
||||||
|
})
|
||||||
|
export class PluginsComponent {
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { Routes } from '@angular/router'
|
||||||
|
|
||||||
|
import { UserRightGuard } from '../../core'
|
||||||
|
import { UserRight } from '../../../../../shared'
|
||||||
|
import { PluginListInstalledComponent } from '@app/+admin/plugins/plugin-list-installed/plugin-list-installed.component'
|
||||||
|
import { PluginSearchComponent } from '@app/+admin/plugins/plugin-search/plugin-search.component'
|
||||||
|
import { PluginShowInstalledComponent } from '@app/+admin/plugins/plugin-show-installed/plugin-show-installed.component'
|
||||||
|
import { PluginsComponent } from '@app/+admin/plugins/plugins.component'
|
||||||
|
|
||||||
|
export const PluginsRoutes: Routes = [
|
||||||
|
{
|
||||||
|
path: 'plugins',
|
||||||
|
component: PluginsComponent,
|
||||||
|
canActivate: [ UserRightGuard ],
|
||||||
|
data: {
|
||||||
|
userRight: UserRight.MANAGE_PLUGINS
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
redirectTo: 'list-installed',
|
||||||
|
pathMatch: 'full'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'list-installed',
|
||||||
|
component: PluginListInstalledComponent,
|
||||||
|
data: {
|
||||||
|
meta: {
|
||||||
|
title: 'List installed plugins'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'search',
|
||||||
|
component: PluginSearchComponent,
|
||||||
|
data: {
|
||||||
|
meta: {
|
||||||
|
title: 'Search plugins'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'show/:name',
|
||||||
|
component: PluginShowInstalledComponent,
|
||||||
|
data: {
|
||||||
|
meta: {
|
||||||
|
title: 'Show plugin'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { catchError } from 'rxjs/operators'
|
||||||
|
import { HttpClient, HttpParams } from '@angular/common/http'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { environment } from '../../../../environments/environment'
|
||||||
|
import { RestExtractor, RestService } from '../../../shared'
|
||||||
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
|
import { PluginType } from '@shared/models/plugins/plugin.type'
|
||||||
|
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
||||||
|
import { ResultList } from '@shared/models'
|
||||||
|
import { PeerTubePlugin } from '@shared/models/plugins/peertube-plugin.model'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PluginApiService {
|
||||||
|
private static BASE_APPLICATION_URL = environment.apiUrl + '/api/v1/plugins'
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private authHttp: HttpClient,
|
||||||
|
private restExtractor: RestExtractor,
|
||||||
|
private restService: RestService,
|
||||||
|
private i18n: I18n
|
||||||
|
) { }
|
||||||
|
|
||||||
|
getPluginTypeOptions () {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: this.i18n('Plugin'),
|
||||||
|
value: PluginType.PLUGIN
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.i18n('Theme'),
|
||||||
|
value: PluginType.THEME
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlugins (
|
||||||
|
type: PluginType,
|
||||||
|
componentPagination: ComponentPagination,
|
||||||
|
sort: string
|
||||||
|
) {
|
||||||
|
const pagination = this.restService.componentPaginationToRestPagination(componentPagination)
|
||||||
|
|
||||||
|
let params = new HttpParams()
|
||||||
|
params = this.restService.addRestGetParams(params, pagination, sort)
|
||||||
|
params = params.append('type', type.toString())
|
||||||
|
|
||||||
|
return this.authHttp.get<ResultList<PeerTubePlugin>>(PluginApiService.BASE_APPLICATION_URL, { params })
|
||||||
|
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ import { ServerService } from '@app/core/server/server.service'
|
||||||
import { ClientScript } from '@shared/models/plugins/plugin-package-json.model'
|
import { ClientScript } from '@shared/models/plugins/plugin-package-json.model'
|
||||||
import { PluginScope } from '@shared/models/plugins/plugin-scope.type'
|
import { PluginScope } from '@shared/models/plugins/plugin-scope.type'
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
import { RegisterHookOptions } from '@shared/models/plugins/register.model'
|
import { RegisterHookOptions } from '@shared/models/plugins/register-hook.model'
|
||||||
import { ReplaySubject } from 'rxjs'
|
import { ReplaySubject } from 'rxjs'
|
||||||
import { first, shareReplay } from 'rxjs/operators'
|
import { first, shareReplay } from 'rxjs/operators'
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,7 @@ export class ThemeService {
|
||||||
console.log('Enabling %s theme.', currentTheme)
|
console.log('Enabling %s theme.', currentTheme)
|
||||||
|
|
||||||
this.loadTheme(currentTheme)
|
this.loadTheme(currentTheme)
|
||||||
|
|
||||||
const theme = this.getTheme(currentTheme)
|
const theme = this.getTheme(currentTheme)
|
||||||
if (theme) {
|
if (theme) {
|
||||||
console.log('Adding scripts of theme %s.', currentTheme)
|
console.log('Adding scripts of theme %s.', currentTheme)
|
||||||
|
@ -95,6 +96,10 @@ export class ThemeService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private listenUserTheme () {
|
private listenUserTheme () {
|
||||||
|
if (!this.auth.isLoggedIn()) {
|
||||||
|
this.updateCurrentTheme()
|
||||||
|
}
|
||||||
|
|
||||||
this.auth.userInformationLoaded
|
this.auth.userInformationLoaded
|
||||||
.subscribe(() => this.updateCurrentTheme())
|
.subscribe(() => this.updateCurrentTheme())
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { searchRouter } from './search'
|
||||||
import { overviewsRouter } from './overviews'
|
import { overviewsRouter } from './overviews'
|
||||||
import { videoPlaylistRouter } from './video-playlist'
|
import { videoPlaylistRouter } from './video-playlist'
|
||||||
import { CONFIG } from '../../initializers/config'
|
import { CONFIG } from '../../initializers/config'
|
||||||
import { pluginsRouter } from '../plugins'
|
import { pluginRouter } from './plugins'
|
||||||
|
|
||||||
const apiRouter = express.Router()
|
const apiRouter = express.Router()
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ apiRouter.use('/videos', videosRouter)
|
||||||
apiRouter.use('/jobs', jobsRouter)
|
apiRouter.use('/jobs', jobsRouter)
|
||||||
apiRouter.use('/search', searchRouter)
|
apiRouter.use('/search', searchRouter)
|
||||||
apiRouter.use('/overviews', overviewsRouter)
|
apiRouter.use('/overviews', overviewsRouter)
|
||||||
apiRouter.use('/plugins', pluginsRouter)
|
apiRouter.use('/plugins', pluginRouter)
|
||||||
apiRouter.use('/ping', pong)
|
apiRouter.use('/ping', pong)
|
||||||
apiRouter.use('/*', badRequest)
|
apiRouter.use('/*', badRequest)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue