WIP plugins: list installed plugins in client
authorChocobozzz <me@florianbigard.com>
Wed, 10 Jul 2019 16:30:27 +0000 (18:30 +0200)
committerChocobozzz <chocobozzz@cpy.re>
Wed, 24 Jul 2019 08:58:16 +0000 (10:58 +0200)
22 files changed:
client/src/app/+admin/admin-routing.module.ts
client/src/app/+admin/admin.component.html
client/src/app/+admin/admin.component.ts
client/src/app/+admin/admin.module.ts
client/src/app/+admin/plugins/index.ts [new file with mode: 0644]
client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.html [new file with mode: 0644]
client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.scss [new file with mode: 0644]
client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts [new file with mode: 0644]
client/src/app/+admin/plugins/plugin-search/plugin-search.component.html [new file with mode: 0644]
client/src/app/+admin/plugins/plugin-search/plugin-search.component.scss [new file with mode: 0644]
client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts [new file with mode: 0644]
client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.html [new file with mode: 0644]
client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss [new file with mode: 0644]
client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts [new file with mode: 0644]
client/src/app/+admin/plugins/plugins.component.html [new file with mode: 0644]
client/src/app/+admin/plugins/plugins.component.scss [new file with mode: 0644]
client/src/app/+admin/plugins/plugins.component.ts [new file with mode: 0644]
client/src/app/+admin/plugins/plugins.routes.ts [new file with mode: 0644]
client/src/app/+admin/plugins/shared/plugin-api.service.ts [new file with mode: 0644]
client/src/app/core/plugins/plugin.service.ts
client/src/app/core/theme/theme.service.ts
server/controllers/api/index.ts

index 215da1e4f1b9253d231c4509e10d3f2aa4a4cf20..79c57221b4fc949748a1b821d5fca72e0b188689 100644 (file)
@@ -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
     ]
   }
 ]
index 98f45a7d1bf5c7f4dfb17df24aa6bb8613d7e04b..9a3d90c183911df6a0a4d968abdbe951bc9ad46f 100644 (file)
       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>
index 408de4837bcd21a8ebf1a1324b555f8efe37a0e7..b23999d404654c8306f34c8e44f47b342c21eb64 100644 (file)
@@ -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)
   }
index 9ab883f60c4578e00775e9b37d0121ea6ead3d2f..256b7e1f5de2d86d00f77fb3833a0889f01b528f 100644 (file)
@@ -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 { }
diff --git a/client/src/app/+admin/plugins/index.ts b/client/src/app/+admin/plugins/index.ts
new file mode 100644 (file)
index 0000000..b75a945
--- /dev/null
@@ -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 (file)
index 0000000..6bb8bcd
--- /dev/null
@@ -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>
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 (file)
index 0000000..9e98fcd
--- /dev/null
@@ -0,0 +1,8 @@
+@import '_variables';
+@import '_mixins';
+
+.toggle-plugin-type {
+  display: flex;
+  justify-content: center;
+  margin-bottom: 30px;
+}
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 (file)
index 0000000..9745bc3
--- /dev/null
@@ -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.')
+  }
+}
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 (file)
index 0000000..e69de29
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 (file)
index 0000000..5e67747
--- /dev/null
@@ -0,0 +1,2 @@
+@import '_variables';
+@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 (file)
index 0000000..db1f91f
--- /dev/null
@@ -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 () {
+  }
+}
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 (file)
index 0000000..e69de29
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 (file)
index 0000000..5e67747
--- /dev/null
@@ -0,0 +1,2 @@
+@import '_variables';
+@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 (file)
index 0000000..f655995
--- /dev/null
@@ -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 () {
+
+  }
+
+}
diff --git a/client/src/app/+admin/plugins/plugins.component.html b/client/src/app/+admin/plugins/plugins.component.html
new file mode 100644 (file)
index 0000000..3dc4939
--- /dev/null
@@ -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>
diff --git a/client/src/app/+admin/plugins/plugins.component.scss b/client/src/app/+admin/plugins/plugins.component.scss
new file mode 100644 (file)
index 0000000..9f61bcf
--- /dev/null
@@ -0,0 +1,7 @@
+@import '_variables';
+@import '_mixins';
+
+.form-sub-title {
+  flex-grow: 0;
+  margin-right: 30px;
+}
diff --git a/client/src/app/+admin/plugins/plugins.component.ts b/client/src/app/+admin/plugins/plugins.component.ts
new file mode 100644 (file)
index 0000000..6ec6fa4
--- /dev/null
@@ -0,0 +1,8 @@
+import { Component } from '@angular/core'
+
+@Component({
+  templateUrl: './plugins.component.html',
+  styleUrls: [ './plugins.component.scss' ]
+})
+export class PluginsComponent {
+}
diff --git a/client/src/app/+admin/plugins/plugins.routes.ts b/client/src/app/+admin/plugins/plugins.routes.ts
new file mode 100644 (file)
index 0000000..58b5534
--- /dev/null
@@ -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'
+          }
+        }
+      }
+    ]
+  }
+]
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 (file)
index 0000000..bfc2b91
--- /dev/null
@@ -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)))
+  }
+}
index 4abe9ee8d202a59f416316044126e3858b815b32..86bde2d02369bc3bdef7542147d6debd0d614c89 100644 (file)
@@ -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'
 
index ad59c203be07d79abe8a826a4407ee9d9be93777..76199d1cc03215a96cdc4261932f287e69716587 100644 (file)
@@ -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())
   }
index 0876283a2022436aff4fa95ec0db666ac2a9cf11..6138a32de9305c0d1877ab3042a42d0377e80bcb 100644 (file)
@@ -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)