Browse Source

feat(plugins): WIP on new apps concept

Torkel Ödegaard 10 years ago
parent
commit
eacc46da6d

+ 1 - 1
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)
 

+ 4 - 4
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"`
 }

+ 14 - 29
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 _, plugin := range enabledPlugins.Apps {
+		if plugin.Module != "" {
+			data.PluginModules = append(data.PluginModules, plugin.Module)
 		}
-		for _, css := range plugin.Css {
-			data.PluginCss = append(data.PluginCss, &dtos.PluginCss{Light: css.Light, Dark: css.Dark})
+
+		if plugin.Css != nil {
+			data.PluginCss = append(data.PluginCss, &dtos.PluginCss{Light: plugin.Css.Light, Dark: plugin.Css.Dark})
 		}
 
-		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.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,
+				})
 			}
 		}
 	}

+ 4 - 0
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)
+}

+ 10 - 10
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:"-"`

+ 11 - 0
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

+ 26 - 34
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),
 	}
 }

+ 59 - 57
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)
 			}
 		}
 	}

+ 10 - 9
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
 		}

+ 1 - 1
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},