diff --git a/pkg/api/dtos/apps.go b/pkg/api/dtos/apps.go index 88886e40dd9..b2b33370135 100644 --- a/pkg/api/dtos/apps.go +++ b/pkg/api/dtos/apps.go @@ -12,17 +12,19 @@ type AppSettings struct { Pinned bool `json:"pinned"` Module string `json:"module"` Info *plugins.PluginInfo `json:"info"` - Pages []*plugins.AppPluginPage `json:"pages"` + Pages []plugins.AppPluginPage `json:"pages"` + Includes []plugins.AppIncludeInfo `json:"includes"` JsonData map[string]interface{} `json:"jsonData"` } func NewAppSettingsDto(def *plugins.AppPlugin, data *models.AppSettings) *AppSettings { dto := &AppSettings{ - AppId: def.Id, - Name: def.Name, - Info: &def.Info, - Module: def.Module, - Pages: def.Pages, + AppId: def.Id, + Name: def.Name, + Info: &def.Info, + Module: def.Module, + Pages: def.Pages, + Includes: def.Includes, } if data != nil { diff --git a/pkg/plugins/app_plugin.go b/pkg/plugins/app_plugin.go index 9580f0024f4..898065be8a2 100644 --- a/pkg/plugins/app_plugin.go +++ b/pkg/plugins/app_plugin.go @@ -2,6 +2,7 @@ package plugins import ( "encoding/json" + "strings" "github.com/grafana/grafana/pkg/models" ) @@ -17,10 +18,17 @@ type AppPluginCss struct { Dark string `json:"dark"` } +type AppIncludeInfo struct { + Name string `json:"name"` + Type string `json:"type"` + Id string `json:"id"` +} + type AppPlugin struct { FrontendPluginBase - Css *AppPluginCss `json:"css"` - Pages []*AppPluginPage `json:"pages"` + Css *AppPluginCss `json:"css"` + Pages []AppPluginPage `json:"pages"` + Includes []AppIncludeInfo `json:"-"` Pinned bool `json:"-"` Enabled bool `json:"-"` @@ -38,6 +46,19 @@ func (app *AppPlugin) Load(decoder *json.Decoder, pluginDir string) error { app.PluginDir = pluginDir app.initFrontendPlugin() + + // check if we have child panels + for _, panel := range Panels { + if strings.HasPrefix(panel.PluginDir, app.PluginDir) { + panel.IncludedInAppId = app.Id + app.Includes = append(app.Includes, AppIncludeInfo{ + Name: panel.Name, + Id: panel.Id, + Type: panel.Type, + }) + } + } + Apps[app.Id] = app return nil } diff --git a/pkg/plugins/frontend_plugin.go b/pkg/plugins/frontend_plugin.go index 0f0d1676066..3dd26a78ea5 100644 --- a/pkg/plugins/frontend_plugin.go +++ b/pkg/plugins/frontend_plugin.go @@ -8,21 +8,22 @@ import ( type FrontendPluginBase struct { PluginBase - Module string `json:"module"` - StaticRoot string `json:"staticRoot"` + Module string `json:"module"` + StaticRoot string `json:"staticRoot"` + StaticRootAbs string `json:"-"` } func (fp *FrontendPluginBase) initFrontendPlugin() { if fp.StaticRoot != "" { + fp.StaticRootAbs = filepath.Join(fp.PluginDir, fp.StaticRoot) StaticRoutes = append(StaticRoutes, &PluginStaticRoute{ - Directory: filepath.Join(fp.PluginDir, fp.StaticRoot), + Directory: fp.StaticRootAbs, PluginId: fp.Id, }) } fp.Info.Logos.Small = evalRelativePluginUrlPath(fp.Info.Logos.Small, fp.Id) fp.Info.Logos.Large = evalRelativePluginUrlPath(fp.Info.Logos.Large, fp.Id) - fp.handleModuleDefaults() } diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index 65bd0ef1f2d..375bdf1a52c 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -11,12 +11,13 @@ type PluginLoader interface { } type PluginBase struct { - Type string `json:"type"` - Name string `json:"name"` - Id string `json:"id"` - App string `json:"app"` - Info PluginInfo `json:"info"` - PluginDir string `json:"-"` + Type string `json:"type"` + Name string `json:"name"` + Id string `json:"id"` + Info PluginInfo `json:"info"` + + IncludedInAppId string `json:"-"` + PluginDir string `json:"-"` } type PluginInfo struct { diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index dad7572f446..d6d390d93c0 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -1,16 +1,13 @@ package plugins import ( - "bytes" "encoding/json" "errors" - "io" "os" "path" "path/filepath" "reflect" "strings" - "text/template" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/setting" @@ -122,28 +119,6 @@ func (scanner *PluginScanner) walker(currentPath string, f os.FileInfo, err erro return nil } -func interpolatePluginJson(reader io.Reader, pluginCommon *PluginBase) (io.Reader, error) { - buf := new(bytes.Buffer) - buf.ReadFrom(reader) - jsonStr := buf.String() // - - tmpl, err := template.New("json").Parse(jsonStr) - if err != nil { - return nil, err - } - - data := map[string]interface{}{ - "PluginPublicRoot": "public/plugins/" + pluginCommon.Id, - } - - var resultBuffer bytes.Buffer - if err := tmpl.ExecuteTemplate(&resultBuffer, "json", data); err != nil { - return nil, err - } - - return bytes.NewReader(resultBuffer.Bytes()), nil -} - func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error { currentDir := filepath.Dir(pluginJsonFilePath) reader, err := os.Open(pluginJsonFilePath) @@ -163,20 +138,13 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error { return errors.New("Did not find type and id property in plugin.json") } - reader.Seek(0, 0) - if newReader, err := interpolatePluginJson(reader, &pluginCommon); err != nil { - return err - } else { - jsonParser = json.NewDecoder(newReader) - } - var loader PluginLoader - if pluginGoType, exists := PluginTypes[pluginCommon.Type]; !exists { return errors.New("Unkown plugin type " + pluginCommon.Type) } else { loader = reflect.New(reflect.TypeOf(pluginGoType)).Interface().(PluginLoader) } + reader.Seek(0, 0) return loader.Load(jsonParser, currentDir) } diff --git a/pkg/plugins/queries.go b/pkg/plugins/queries.go index 889cbe654d5..58e7d865c63 100644 --- a/pkg/plugins/queries.go +++ b/pkg/plugins/queries.go @@ -27,8 +27,7 @@ func GetEnabledPlugins(orgId int64) (*EnabledPlugins, error) { return nil, err } - seenPanels := make(map[string]bool) - seenApi := make(map[string]bool) + enabledApps := make(map[string]bool) for appId, installedApp := range Apps { var app AppPlugin @@ -42,32 +41,36 @@ func GetEnabledPlugins(orgId int64) (*EnabledPlugins, error) { } if app.Enabled { + enabledApps[app.Id] = true enabledPlugins.Apps = append(enabledPlugins.Apps, &app) } } + isPluginEnabled := func(appId string) bool { + if appId == "" { + return true + } + + _, ok := enabledApps[appId] + return ok + } + // add all plugins that are not part of an App. - for d, installedDs := range DataSources { - if installedDs.App == "" { - enabledPlugins.DataSources[d] = installedDs + for dsId, ds := range DataSources { + if isPluginEnabled(ds.IncludedInAppId) { + enabledPlugins.DataSources[dsId] = ds } } - for p, panel := range Panels { - if panel.App == "" { - if _, ok := seenPanels[p]; !ok { - seenPanels[p] = true - enabledPlugins.Panels = append(enabledPlugins.Panels, panel) - } + for _, panel := range Panels { + if isPluginEnabled(panel.IncludedInAppId) { + enabledPlugins.Panels = append(enabledPlugins.Panels, panel) } } - for a, api := range ApiPlugins { - if api.App == "" { - if _, ok := seenApi[a]; !ok { - seenApi[a] = true - enabledPlugins.ApiList = append(enabledPlugins.ApiList, api) - } + for _, api := range ApiPlugins { + if isPluginEnabled(api.IncludedInAppId) { + enabledPlugins.ApiList = append(enabledPlugins.ApiList, api) } } diff --git a/public/app/features/apps/edit_ctrl.ts b/public/app/features/apps/edit_ctrl.ts index d793f045690..dfbce64df5e 100644 --- a/public/app/features/apps/edit_ctrl.ts +++ b/public/app/features/apps/edit_ctrl.ts @@ -5,6 +5,7 @@ import _ from 'lodash'; export class AppEditCtrl { appModel: any; + includedPanels: any; /** @ngInject */ constructor(private backendSrv: any, private $routeParams: any) { @@ -12,6 +13,7 @@ export class AppEditCtrl { this.backendSrv.get(`/api/org/apps/${this.$routeParams.appId}/settings`).then(result => { this.appModel = result; + this.includedPanels = _.where(result.includes, {type: 'panel'}); }); } diff --git a/public/app/features/apps/partials/edit.html b/public/app/features/apps/partials/edit.html index 4de73a25901..aae34b8b38d 100644 --- a/public/app/features/apps/partials/edit.html +++ b/public/app/features/apps/partials/edit.html @@ -56,7 +56,10 @@ Panels
diff --git a/public/app/features/panel/panel_loader.ts b/public/app/features/panel/panel_loader.ts index ebcb8d2631c..8dc46f494a1 100644 --- a/public/app/features/panel/panel_loader.ts +++ b/public/app/features/panel/panel_loader.ts @@ -3,13 +3,21 @@ import angular from 'angular'; import config from 'app/core/config'; +import {unknownPanelDirective} from '../../plugins/panel/unknown/module'; + /** @ngInject */ function panelLoader($parse, dynamicDirectiveSrv) { return dynamicDirectiveSrv.create({ directive: scope => { - let modulePath = config.panels[scope.panel.type].module; + let panelInfo = config.panels[scope.panel.type]; + if (!panelInfo) { + return Promise.resolve({ + name: 'panel-directive-' + scope.panel.type, + fn: unknownPanelDirective + }); + } - return System.import(modulePath).then(function(panelModule) { + return System.import(panelInfo.module).then(function(panelModule) { return { name: 'panel-directive-' + scope.panel.type, fn: panelModule.panel, diff --git a/public/app/plugins/panel/unknown/module.ts b/public/app/plugins/panel/unknown/module.ts new file mode 100644 index 00000000000..8d1ce6c1f9a --- /dev/null +++ b/public/app/plugins/panel/unknown/module.ts @@ -0,0 +1,15 @@ +/// + +export function unknownPanelDirective() { + return { + restrict: 'E', + template: ` + +
+ Unknown panel type: {{panel.type}} +
+
+ `, + }; +} + diff --git a/public/less/simple-box.less b/public/less/simple-box.less index 9bf21f960c7..9bc52eb1375 100644 --- a/public/less/simple-box.less +++ b/public/less/simple-box.less @@ -1,7 +1,7 @@ @simpleBoxBorderWidth: 0.2rem; @simpleBoxMargin: 1.5rem; -@simpleBoxBodyPadding: 0.5rem 0 0.5rem 1rem; +@simpleBoxBodyPadding: 1rem 0 0.5rem 1rem; .simple-box { margin-top: @simpleBoxMargin;