From eacc46da6d078472d88819b587cd5a4c67b4759b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 21 Dec 2015 23:09:27 +0100 Subject: [PATCH] feat(plugins): WIP on new apps concept --- pkg/api/datasources.go | 2 +- pkg/api/dtos/index.go | 8 +- pkg/api/index.go | 45 +++---- pkg/middleware/middleware.go | 4 + pkg/models/app_plugin.go | 20 +-- pkg/models/org_user.go | 11 ++ pkg/plugins/models.go | 60 ++++----- pkg/plugins/plugins.go | 116 +++++++++--------- pkg/services/sqlstore/app_plugin.go | 19 +-- .../sqlstore/migrations/app_plugin.go | 2 +- 10 files changed, 141 insertions(+), 146 deletions(-) diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index 52eedb08e9b..8451a3e8c14 100644 --- a/pkg/api/datasources.go +++ b/pkg/api/datasources.go @@ -121,7 +121,7 @@ func GetDataSourcePlugins(c *middleware.Context) { orgApps := m.GetAppPluginsQuery{OrgId: c.OrgId} err := bus.Dispatch(&orgApps) if err != nil { - c.JsonApiErr(500, "Failed to get org plugin Bundles", err) + c.JsonApiErr(500, "Failed to get org apps", err) } enabledPlugins := plugins.GetEnabledPlugins(orgApps.Result) diff --git a/pkg/api/dtos/index.go b/pkg/api/dtos/index.go index 1314d2d94ac..64abec6f4e5 100644 --- a/pkg/api/dtos/index.go +++ b/pkg/api/dtos/index.go @@ -8,9 +8,9 @@ type IndexViewData struct { GoogleAnalyticsId string GoogleTagManagerId string - PluginCss []*PluginCss - PluginJs []string - MainNavLinks []*NavLink + PluginCss []*PluginCss + PluginModules []string + MainNavLinks []*NavLink } type PluginCss struct { @@ -21,5 +21,5 @@ type PluginCss struct { type NavLink struct { Text string `json:"text"` Icon string `json:"icon"` - Href string `json:"href"` + Url string `json:"url"` } diff --git a/pkg/api/index.go b/pkg/api/index.go index 1346a45a7ac..4874a24a6f6 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -60,40 +60,25 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { if err != nil { return nil, err } + enabledPlugins := plugins.GetEnabledPlugins(orgApps.Result) - for _, plugin := range enabledPlugins.AppPlugins { - for _, js := range plugin.Js { - data.PluginJs = append(data.PluginJs, js.Module) - } - for _, css := range plugin.Css { - data.PluginCss = append(data.PluginCss, &dtos.PluginCss{Light: css.Light, Dark: css.Dark}) + for _, plugin := range enabledPlugins.Apps { + if plugin.Module != "" { + data.PluginModules = append(data.PluginModules, plugin.Module) } - if plugin.PinNavLinks { - for _, item := range plugin.MainNavLinks { - // only show menu items for the specified roles. - var validRoles []m.RoleType - if string(item.ReqRole) == "" || item.ReqRole == m.ROLE_VIEWER { - validRoles = []m.RoleType{m.ROLE_ADMIN, m.ROLE_EDITOR, m.ROLE_VIEWER} - } else if item.ReqRole == m.ROLE_EDITOR { - validRoles = []m.RoleType{m.ROLE_ADMIN, m.ROLE_EDITOR} - } else if item.ReqRole == m.ROLE_ADMIN { - validRoles = []m.RoleType{m.ROLE_ADMIN} - } - ok := true - if len(validRoles) > 0 { - ok = false - for _, role := range validRoles { - if role == c.OrgRole { - ok = true - break - } - } - } - if ok { - data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{Text: item.Text, Href: item.Href, Icon: item.Icon}) - } + if plugin.Css != nil { + data.PluginCss = append(data.PluginCss, &dtos.PluginCss{Light: plugin.Css.Light, Dark: plugin.Css.Dark}) + } + + if plugin.Pinned && plugin.Page != nil { + if c.userHasRole(plugin.Page.reqRole) { + data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{ + Text: plugin.Page.Text, + Url: plugin.Page.Url, + Icon: plugin.Page.Icon, + }) } } } diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index 8704ec5a787..d2da32b8ed7 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -253,3 +253,7 @@ func (ctx *Context) JsonApiErr(status int, message string, err error) { ctx.JSON(status, resp) } + +func (ctx *Context) hasUserRole(role m.RoleType) bool { + return ctx.OrgRole.Includes(role) +} diff --git a/pkg/models/app_plugin.go b/pkg/models/app_plugin.go index fc155035f84..3676d85ab01 100644 --- a/pkg/models/app_plugin.go +++ b/pkg/models/app_plugin.go @@ -3,12 +3,12 @@ package models import "time" type AppPlugin struct { - Id int64 - Type string - OrgId int64 - Enabled bool - PinNavLinks bool - JsonData map[string]interface{} + Id int64 + Type string + OrgId int64 + Enabled bool + Pinned bool + JsonData map[string]interface{} Created time.Time Updated time.Time @@ -19,10 +19,10 @@ type AppPlugin struct { // Also acts as api DTO type UpdateAppPluginCmd struct { - Type string `json:"type" binding:"Required"` - Enabled bool `json:"enabled"` - PinNavLinks bool `json:"pin_nav_links"` - JsonData map[string]interface{} `json:"jsonData"` + Type string `json:"type" binding:"Required"` + Enabled bool `json:"enabled"` + Pinned bool `json:"pinned"` + JsonData map[string]interface{} `json:"jsonData"` Id int64 `json:"-"` OrgId int64 `json:"-"` diff --git a/pkg/models/org_user.go b/pkg/models/org_user.go index f456fa95373..48d17deb9db 100644 --- a/pkg/models/org_user.go +++ b/pkg/models/org_user.go @@ -26,6 +26,17 @@ func (r RoleType) IsValid() bool { return r == ROLE_VIEWER || r == ROLE_ADMIN || r == ROLE_EDITOR || r == ROLE_READ_ONLY_EDITOR } +func (r RoleType) Includes(other RoleType) bool { + if r == ROLE_ADMIN { + return true + } + if r == ROLE_EDITOR || r == ROLE_READ_ONLY_EDITOR { + return other != ROLE_ADMIN + } + + return r == other +} + type OrgUser struct { Id int64 OrgId int64 diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index 5f47c7cd52c..67172d952d7 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -15,20 +15,20 @@ type DataSourcePlugin struct { Metrics bool `json:"metrics"` BuiltIn bool `json:"builtIn"` App string `json:"app"` - StaticRootConfig *StaticRootConfig `json:"staticRoot"` + PublicContent *PublicContent `json:"public"` } type PanelPlugin struct { - Type string `json:"type"` - Name string `json:"name"` - Module string `json:"module"` - StaticRootConfig *StaticRootConfig `json:"staticRoot"` - App string `json:"app"` + Type string `json:"type"` + Name string `json:"name"` + Module string `json:"module"` + PublicContent *PublicContent `json:"public"` + App string `json:"app"` } -type StaticRootConfig struct { - Url string `json:"url"` - Path string `json:"path"` +type PublicContent struct { + UrlFragment string `json:"urlFragment"` + Dir string `json:"dir"` } type ApiPluginRoute struct { @@ -41,14 +41,10 @@ type ApiPluginRoute struct { App string `json:"app"` } -type AppPluginJs struct { - Module string `json:"module"` -} - -type AppPluginNavLink struct { +type AppPluginPage struct { Text string `json:"text"` Icon string `json:"icon"` - Href string `json:"href"` + Href string `json:"url"` ReqRole models.RoleType `json:"reqRole"` } @@ -64,31 +60,27 @@ type ApiPlugin struct { } type AppPlugin struct { - Type string `json:"type"` - Enabled bool `json:"enabled"` - PanelPlugins []string `json:"panelPlugins"` - DatasourcePlugins []string `json:"datasourcePlugins"` - ApiPlugins []string `json:"apiPlugins"` - Module string `json:"module"` - Js []*AppPluginJs `json:"js"` - Css []*AppPluginCss `json:"css"` - MainNavLinks []*AppPluginNavLink `json:"mainNavLinks"` - PinNavLinks bool `json:"pinNavLinks"` - StaticRootConfig *StaticRootConfig `json:"staticRoot"` + Type string `json:"type"` + Enabled bool `json:"enabled"` + Pinned bool `json:"pinned"` + Module string `json:"module"` + Css *AppPluginCss `json:"css"` + Page *AppPluginPage `json:"page"` + PublicContent *PublicContent `json:"public"` } type EnabledPlugins struct { - PanelPlugins []*PanelPlugin - DataSourcePlugins map[string]*DataSourcePlugin - ApiPlugins []*ApiPlugin - AppPlugins []*AppPlugin + Panels []*PanelPlugin + DataSources map[string]*DataSourcePlugin + ApiList []*ApiPlugin + Apps []*AppPlugin } func NewEnabledPlugins() EnabledPlugins { return EnabledPlugins{ - PanelPlugins: make([]*PanelPlugin, 0), - DataSourcePlugins: make(map[string]*DataSourcePlugin), - ApiPlugins: make([]*ApiPlugin, 0), - AppPlugins: make([]*AppPlugin, 0), + Panels: make([]*PanelPlugin, 0), + DataSources: make(map[string]*DataSourcePlugin), + ApiList: make([]*ApiPlugin, 0), + Apps: make([]*AppPlugin, 0), } } diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index 10a398b3ef6..7923aebc744 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -18,7 +18,7 @@ var ( DataSources map[string]*DataSourcePlugin Panels map[string]*PanelPlugin ApiPlugins map[string]*ApiPlugin - StaticRoutes []*StaticRootConfig + StaticRoutes []*PublicContent Apps map[string]*AppPlugin ) @@ -30,35 +30,35 @@ type PluginScanner struct { func Init() error { DataSources = make(map[string]*DataSourcePlugin) ApiPlugins = make(map[string]*ApiPlugin) - StaticRoutes = make([]*StaticRootConfig, 0) + StaticRoutes = make([]*PublicContent, 0) Panels = make(map[string]*PanelPlugin) Apps = make(map[string]*AppPlugin) scan(path.Join(setting.StaticRootPath, "app/plugins")) checkPluginPaths() - checkDependencies() + // checkDependencies() return nil } -func checkDependencies() { - for appType, app := range Apps { - for _, reqPanel := range app.PanelPlugins { - if _, ok := Panels[reqPanel]; !ok { - log.Fatal(4, "App %s requires Panel type %s, but it is not present.", appType, reqPanel) - } - } - for _, reqDataSource := range app.DatasourcePlugins { - if _, ok := DataSources[reqDataSource]; !ok { - log.Fatal(4, "App %s requires DataSource type %s, but it is not present.", appType, reqDataSource) - } - } - for _, reqApiPlugin := range app.ApiPlugins { - if _, ok := ApiPlugins[reqApiPlugin]; !ok { - log.Fatal(4, "App %s requires ApiPlugin type %s, but it is not present.", appType, reqApiPlugin) - } - } - } -} +// func checkDependencies() { +// for appType, app := range Apps { +// for _, reqPanel := range app.PanelPlugins { +// if _, ok := Panels[reqPanel]; !ok { +// log.Fatal(4, "App %s requires Panel type %s, but it is not present.", appType, reqPanel) +// } +// } +// for _, reqDataSource := range app.DatasourcePlugins { +// if _, ok := DataSources[reqDataSource]; !ok { +// log.Fatal(4, "App %s requires DataSource type %s, but it is not present.", appType, reqDataSource) +// } +// } +// for _, reqApiPlugin := range app.ApiPlugins { +// if _, ok := ApiPlugins[reqApiPlugin]; !ok { +// log.Fatal(4, "App %s requires ApiPlugin type %s, but it is not present.", appType, reqApiPlugin) +// } +// } +// } +// } func checkPluginPaths() error { for _, section := range setting.Cfg.Sections() { @@ -108,10 +108,10 @@ func (scanner *PluginScanner) walker(currentPath string, f os.FileInfo, err erro return nil } -func addStaticRoot(staticRootConfig *StaticRootConfig, currentDir string) { - if staticRootConfig != nil { - staticRootConfig.Path = path.Join(currentDir, staticRootConfig.Path) - StaticRoutes = append(StaticRoutes, staticRootConfig) +func addPublicContent(public *PublicContent, currentDir string) { + if public != nil { + public.Dir = path.Join(currentDir, public.Dir) + StaticRoutes = append(StaticRoutes, public) } } @@ -148,7 +148,7 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error { } DataSources[p.Type] = &p - addStaticRoot(p.StaticRootConfig, currentDir) + addPublicContent(p.PublicContent, currentDir) } if pluginType == "panel" { @@ -163,7 +163,7 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error { } Panels[p.Type] = &p - addStaticRoot(p.StaticRootConfig, currentDir) + addPublicContent(p.PublicContent, currentDir) } if pluginType == "api" { @@ -188,7 +188,7 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error { return errors.New("Did not find type property in plugin.json") } Apps[p.Type] = &p - addStaticRoot(p.StaticRootConfig, currentDir) + addPublicContent(p.PublicContent, currentDir) } return nil @@ -212,54 +212,56 @@ func GetEnabledPlugins(orgApps []*models.AppPlugin) EnabledPlugins { // state stored there. if b, ok := orgAppsMap[appType]; ok { app.Enabled = b.Enabled - app.PinNavLinks = b.PinNavLinks + app.Pinned = b.Pinned } - if app.Enabled { - for _, d := range app.DatasourcePlugins { - if ds, ok := DataSources[d]; ok { - enabledPlugins.DataSourcePlugins[d] = ds - } - } - for _, p := range app.PanelPlugins { - if panel, ok := Panels[p]; ok { - if _, ok := seenPanels[p]; !ok { - seenPanels[p] = true - enabledPlugins.PanelPlugins = append(enabledPlugins.PanelPlugins, panel) - } - } - } - for _, a := range app.ApiPlugins { - if api, ok := ApiPlugins[a]; ok { - if _, ok := seenApi[a]; !ok { - seenApi[a] = true - enabledPlugins.ApiPlugins = append(enabledPlugins.ApiPlugins, api) - } - } - } - enabledPlugins.AppPlugins = append(enabledPlugins.AppPlugins, &app) - } + // if app.Enabled { + // for _, d := range app.DatasourcePlugins { + // if ds, ok := DataSources[d]; ok { + // enabledPlugins.DataSourcePlugins[d] = ds + // } + // } + // for _, p := range app.PanelPlugins { + // if panel, ok := Panels[p]; ok { + // if _, ok := seenPanels[p]; !ok { + // seenPanels[p] = true + // enabledPlugins.PanelPlugins = append(enabledPlugins.PanelPlugins, panel) + // } + // } + // } + // for _, a := range app.ApiPlugins { + // if api, ok := ApiPlugins[a]; ok { + // if _, ok := seenApi[a]; !ok { + // seenApi[a] = true + // enabledPlugins.ApiPlugins = append(enabledPlugins.ApiPlugins, api) + // } + // } + // } + // enabledPlugins.AppPlugins = append(enabledPlugins.AppPlugins, &app) + // } } // add all plugins that are not part of an App. for d, installedDs := range DataSources { if installedDs.App == "" { - enabledPlugins.DataSourcePlugins[d] = installedDs + enabledPlugins.DataSources[d] = installedDs } } + for p, panel := range Panels { if panel.App == "" { if _, ok := seenPanels[p]; !ok { seenPanels[p] = true - enabledPlugins.PanelPlugins = append(enabledPlugins.PanelPlugins, panel) + enabledPlugins.Panels = append(enabledPlugins.Panels, panel) } } } + for a, api := range ApiPlugins { if api.App == "" { if _, ok := seenApi[a]; !ok { seenApi[a] = true - enabledPlugins.ApiPlugins = append(enabledPlugins.ApiPlugins, api) + enabledPlugins.ApiList = append(enabledPlugins.ApiList, api) } } } diff --git a/pkg/services/sqlstore/app_plugin.go b/pkg/services/sqlstore/app_plugin.go index 63fdcac0d8c..fd0c2bc7be1 100644 --- a/pkg/services/sqlstore/app_plugin.go +++ b/pkg/services/sqlstore/app_plugin.go @@ -25,23 +25,24 @@ func UpdateAppPlugin(cmd *m.UpdateAppPluginCmd) error { exists, err := sess.Where("org_id=? and type=?", cmd.OrgId, cmd.Type).Get(&app) sess.UseBool("enabled") - sess.UseBool("pin_nav_links") + sess.UseBool("pinned") if !exists { app = m.AppPlugin{ - Type: cmd.Type, - OrgId: cmd.OrgId, - Enabled: cmd.Enabled, - PinNavLinks: cmd.PinNavLinks, - JsonData: cmd.JsonData, - Created: time.Now(), - Updated: time.Now(), + Type: cmd.Type, + OrgId: cmd.OrgId, + Enabled: cmd.Enabled, + Pinned: cmd.Pinned, + JsonData: cmd.JsonData, + Created: time.Now(), + Updated: time.Now(), } _, err = sess.Insert(&app) return err } else { + app.Updated = time.Now() app.Enabled = cmd.Enabled app.JsonData = cmd.JsonData - app.PinNavLinks = cmd.PinNavLinks + app.Pinned = cmd.Pinned _, err = sess.Id(app.Id).Update(&app) return err } diff --git a/pkg/services/sqlstore/migrations/app_plugin.go b/pkg/services/sqlstore/migrations/app_plugin.go index a0208f0eac5..8cd16ed22e1 100644 --- a/pkg/services/sqlstore/migrations/app_plugin.go +++ b/pkg/services/sqlstore/migrations/app_plugin.go @@ -11,7 +11,7 @@ func addAppPluginMigration(mg *Migrator) { {Name: "org_id", Type: DB_BigInt, Nullable: true}, {Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "enabled", Type: DB_Bool, Nullable: false}, - {Name: "pin_nav_links", Type: DB_Bool, Nullable: false}, + {Name: "pinned", Type: DB_Bool, Nullable: false}, {Name: "json_data", Type: DB_Text, Nullable: true}, {Name: "created", Type: DB_DateTime, Nullable: false}, {Name: "updated", Type: DB_DateTime, Nullable: false},