diff --git a/deploy/plugins/index.js b/deploy/plugins/index.js index 929f19499786e69b7dc1461a27ae96972e32b8a0..8d6bee52dadaf45913c133479a42142b3c2a4a3e 100644 --- a/deploy/plugins/index.js +++ b/deploy/plugins/index.js @@ -7,6 +7,16 @@ const express = require('express') const lruStore = require('../lruStore') const got = require('got') const router = express.Router() +const DEV_PLUGINS = (() => { + try { + return JSON.parse( + process.env.DEV_PLUGINS || `[]` + ) + } catch (e) { + console.warn(`Parsing DEV_PLUGINS failed: ${e}`) + return [] + } +})() const PLUGIN_URLS = (process.env.PLUGIN_URLS && process.env.PLUGIN_URLS.split(';')) || [] const STAGING_PLUGIN_URLS = (process.env.STAGING_PLUGIN_URLS && process.env.STAGING_PLUGIN_URLS.split(';')) || [] @@ -44,7 +54,7 @@ router.get('/manifests', async (_req, res) => { })) res.status(200).json( - allManifests.filter(v => !!v) + [...DEV_PLUGINS, ...allManifests.filter(v => !!v)] ) }) diff --git a/docs/releases/v2.6.0.md b/docs/releases/v2.6.0.md index 72f97e1392db3f20dde8b5c483e105a72ef0a8a6..95fd0880cd9a39f3cf23c3897ef34944f315ce6f 100644 --- a/docs/releases/v2.6.0.md +++ b/docs/releases/v2.6.0.md @@ -1,4 +1,8 @@ -# v2.5.0 +# v2.6.0 + +## New features + +- add the capability of add 3rd party plugins (experimental) ## Under the hood stuff diff --git a/src/atlasComponents/userAnnotations/tools/type.spec.ts b/src/atlasComponents/userAnnotations/tools/type.spec.ts index f4b28738913465c7eda8ee898f28fdc724dbf016..599f20eabb8289dc975b501c876418df055e7d57 100644 --- a/src/atlasComponents/userAnnotations/tools/type.spec.ts +++ b/src/atlasComponents/userAnnotations/tools/type.spec.ts @@ -2,6 +2,7 @@ import { Subject, Subscription } from "rxjs" import { AbsToolClass, IAnnotationEvents, IAnnotationGeometry, TAnnotationEvent } from "./type" class TmpCls extends IAnnotationGeometry{ + annotationType: 'tmpl-cls' getNgAnnotationIds(){ return [] } @@ -110,6 +111,7 @@ describe('> types.ts', () => { describe('> updateSignal$', () => { class TmpCls extends IAnnotationGeometry{ + annotationType = 'tmp-cls' getNgAnnotationIds(){ return [] } diff --git a/src/plugin/atlasViewer.pluginService.service.ts b/src/plugin/atlasViewer.pluginService.service.ts index 1c83a6d28b92622728c9653cf0562846cd28e2cb..668027764cf639fe3be327119a12fb4140352f08 100644 --- a/src/plugin/atlasViewer.pluginService.service.ts +++ b/src/plugin/atlasViewer.pluginService.service.ts @@ -375,6 +375,18 @@ export class PluginServices { return handler } + + public async addPluginViaManifestUrl(manifestUrl: string){ + try { + const json = await this.fetch(manifestUrl) + this.fetchedPluginManifests = [ + ...this.fetchedPluginManifests, + json + ] + } catch (e) { + throw new Error(e.statusText) + } + } } export interface IPluginManifest { diff --git a/src/plugin/pluginBanner/pluginBanner.component.ts b/src/plugin/pluginBanner/pluginBanner.component.ts index 59de1360d44b7db00fc1f9e5567371debf03b650..069be28e672895730be63fb7bdd41fb657c883e7 100644 --- a/src/plugin/pluginBanner/pluginBanner.component.ts +++ b/src/plugin/pluginBanner/pluginBanner.component.ts @@ -1,6 +1,8 @@ import { Component, ViewChild, TemplateRef } from "@angular/core"; import { IPluginManifest, PluginServices } from "../atlasViewer.pluginService.service"; import { MatDialog } from "@angular/material/dialog"; +import { environment } from 'src/environments/environment'; +import { MatSnackBar } from "@angular/material/snack-bar"; @Component({ selector : 'plugin-banner', @@ -12,12 +14,15 @@ import { MatDialog } from "@angular/material/dialog"; export class PluginBannerUI { + EXPERIMENTAL_FEATURE_FLAG = environment.EXPERIMENTAL_FEATURE_FLAG + @ViewChild('pluginInfoTmpl', { read: TemplateRef }) private pluginInfoTmpl: TemplateRef<any> constructor( public pluginServices: PluginServices, private matDialog: MatDialog, + private matSnackbar: MatSnackBar, ) { } @@ -34,4 +39,24 @@ export class PluginBannerUI { } ) } + + public showTmpl(tmpl: TemplateRef<any>){ + this.matDialog.open(tmpl, { + minWidth: '60vw' + }) + } + + public loadingThirdpartyPlugin = false + + public async addThirdPartyPlugin(manifestUrl: string) { + this.loadingThirdpartyPlugin = true + try { + await this.pluginServices.addPluginViaManifestUrl(manifestUrl) + this.loadingThirdpartyPlugin = false + this.matSnackbar.open(`Adding plugin successful`) + } catch (e) { + this.loadingThirdpartyPlugin = false + this.matSnackbar.open(`Error adding plugin: ${e.toString()}`) + } + } } diff --git a/src/plugin/pluginBanner/pluginBanner.template.html b/src/plugin/pluginBanner/pluginBanner.template.html index 6216ee8247a5ac011f3ec41acf7f58cb7e52d0d3..3e452612ebef6347181b825c02c22c5ba2ce8c74 100644 --- a/src/plugin/pluginBanner/pluginBanner.template.html +++ b/src/plugin/pluginBanner/pluginBanner.template.html @@ -15,8 +15,46 @@ {{ plugin.displayName ? plugin.displayName : plugin.name }} </span> </button> + + <button mat-menu-item *ngIf="EXPERIMENTAL_FEATURE_FLAG" + (click)="showTmpl(thirdPartyPluginTmpl)"> + <span> + Add third party plugin + </span> + </button> </mat-action-list> +<ng-template #thirdPartyPluginTmpl> + <h2 mat-dialog-title> + Add thirdparty plugin + </h2> + + <mat-dialog-content> + <form> + <mat-form-field class="d-block"> + <mat-label> + manifest.json URL + </mat-label> + <input type="text" matInput placeholder="https://example.com/manifest.json" #urlInput> + </mat-form-field> + </form> + </mat-dialog-content> + + <mat-dialog-actions align="end"> + <button (click)="addThirdPartyPlugin(urlInput.value)" + mat-raised-button + [disabled]="loadingThirdpartyPlugin" + color="primary"> + Load + </button> + <button mat-dialog-close + mat-button> + cancel + </button> + </mat-dialog-actions> + +</ng-template> + <ng-template #pluginInfoTmpl let-manifest> <h1 mat-dialog-title> About {{ manifest.displayName || manifest.name }}