mirror of https://github.com/Chocobozzz/PeerTube
WIP plugins: list installed plugins in client
parent
ad91e7006e
commit
d00dc28dd7
|
@ -9,6 +9,7 @@ import { FollowsRoutes } from './follows'
|
|||
import { UsersRoutes } from './users'
|
||||
import { ModerationRoutes } from '@app/+admin/moderation/moderation.routes'
|
||||
import { SystemRoutes } from '@app/+admin/system'
|
||||
import { PluginsRoutes } from '@app/+admin/plugins/plugins.routes'
|
||||
|
||||
const adminRoutes: Routes = [
|
||||
{
|
||||
|
@ -26,7 +27,8 @@ const adminRoutes: Routes = [
|
|||
...UsersRoutes,
|
||||
...ModerationRoutes,
|
||||
...SystemRoutes,
|
||||
...ConfigRoutes
|
||||
...ConfigRoutes,
|
||||
...PluginsRoutes
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
Configuration
|
||||
</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">
|
||||
System
|
||||
</a>
|
||||
|
|
|
@ -28,6 +28,10 @@ export class AdminComponent {
|
|||
return this.auth.getUser().hasRight(UserRight.MANAGE_CONFIGURATION)
|
||||
}
|
||||
|
||||
hasPluginsRight () {
|
||||
return this.auth.getUser().hasRight(UserRight.MANAGE_PLUGINS)
|
||||
}
|
||||
|
||||
hasLogsRight () {
|
||||
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 { JobService, LogsComponent, LogsService, SystemComponent } from '@app/+admin/system'
|
||||
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({
|
||||
imports: [
|
||||
AdminRoutingModule,
|
||||
TableModule,
|
||||
SelectButtonModule,
|
||||
SharedModule
|
||||
],
|
||||
|
||||
|
@ -52,6 +59,11 @@ import { DebugComponent, DebugService } from '@app/+admin/system/debug'
|
|||
InstanceServerBlocklistComponent,
|
||||
InstanceAccountBlocklistComponent,
|
||||
|
||||
PluginsComponent,
|
||||
PluginListInstalledComponent,
|
||||
PluginSearchComponent,
|
||||
PluginShowInstalledComponent,
|
||||
|
||||
SystemComponent,
|
||||
JobsComponent,
|
||||
LogsComponent,
|
||||
|
@ -70,7 +82,8 @@ import { DebugComponent, DebugService } from '@app/+admin/system/debug'
|
|||
JobService,
|
||||
LogsService,
|
||||
DebugService,
|
||||
ConfigService
|
||||
ConfigService,
|
||||
PluginApiService
|
||||
]
|
||||
})
|
||||
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 { PluginScope } from '@shared/models/plugins/plugin-scope.type'
|
||||
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 { first, shareReplay } from 'rxjs/operators'
|
||||
|
||||
|
|
|
@ -83,6 +83,7 @@ export class ThemeService {
|
|||
console.log('Enabling %s theme.', currentTheme)
|
||||
|
||||
this.loadTheme(currentTheme)
|
||||
|
||||
const theme = this.getTheme(currentTheme)
|
||||
if (theme) {
|
||||
console.log('Adding scripts of theme %s.', currentTheme)
|
||||
|
@ -95,6 +96,10 @@ export class ThemeService {
|
|||
}
|
||||
|
||||
private listenUserTheme () {
|
||||
if (!this.auth.isLoggedIn()) {
|
||||
this.updateCurrentTheme()
|
||||
}
|
||||
|
||||
this.auth.userInformationLoaded
|
||||
.subscribe(() => this.updateCurrentTheme())
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import { searchRouter } from './search'
|
|||
import { overviewsRouter } from './overviews'
|
||||
import { videoPlaylistRouter } from './video-playlist'
|
||||
import { CONFIG } from '../../initializers/config'
|
||||
import { pluginsRouter } from '../plugins'
|
||||
import { pluginRouter } from './plugins'
|
||||
|
||||
const apiRouter = express.Router()
|
||||
|
||||
|
@ -43,7 +43,7 @@ apiRouter.use('/videos', videosRouter)
|
|||
apiRouter.use('/jobs', jobsRouter)
|
||||
apiRouter.use('/search', searchRouter)
|
||||
apiRouter.use('/overviews', overviewsRouter)
|
||||
apiRouter.use('/plugins', pluginsRouter)
|
||||
apiRouter.use('/plugins', pluginRouter)
|
||||
apiRouter.use('/ping', pong)
|
||||
apiRouter.use('/*', badRequest)
|
||||
|
||||
|
|
Loading…
Reference in New Issue