diff --git a/pkg/api/api.go b/pkg/api/api.go
index c21deae5a59..90d613e46e0 100644
--- a/pkg/api/api.go
+++ b/pkg/api/api.go
@@ -126,9 +126,9 @@ func Register(r *macaron.Macaron) {
r.Patch("/invites/:code/revoke", wrap(RevokeInvite))
// apps
- r.Get("/apps", wrap(GetOrgAppsList))
- r.Get("/apps/:appId/settings", wrap(GetAppSettingsById))
- r.Post("/apps/:appId/settings", bind(m.UpdateAppSettingsCmd{}), wrap(UpdateAppSettings))
+ r.Get("/plugins", wrap(GetPluginList))
+ r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById))
+ r.Post("/plugins/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting))
}, reqOrgAdmin)
// create new org
diff --git a/pkg/api/app_settings.go b/pkg/api/app_settings.go
deleted file mode 100644
index fd0f1a1eab1..00000000000
--- a/pkg/api/app_settings.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package api
-
-import (
- "github.com/grafana/grafana/pkg/api/dtos"
- "github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/middleware"
- m "github.com/grafana/grafana/pkg/models"
- "github.com/grafana/grafana/pkg/plugins"
-)
-
-func GetOrgAppsList(c *middleware.Context) Response {
- orgApps, err := plugins.GetOrgAppSettings(c.OrgId)
-
- if err != nil {
- return ApiError(500, "Failed to list of apps", err)
- }
-
- result := make([]*dtos.AppSettings, 0)
- for _, app := range plugins.Apps {
- orgApp := orgApps[app.Id]
- result = append(result, dtos.NewAppSettingsDto(app, orgApp))
- }
-
- return Json(200, result)
-}
-
-func GetAppSettingsById(c *middleware.Context) Response {
- appId := c.Params(":appId")
-
- if pluginDef, exists := plugins.Apps[appId]; !exists {
- return ApiError(404, "PluginId not found, no installed plugin with that id", nil)
- } else {
- orgApps, err := plugins.GetOrgAppSettings(c.OrgId)
- if err != nil {
- return ApiError(500, "Failed to get org app settings ", nil)
- }
- orgApp := orgApps[appId]
-
- return Json(200, dtos.NewAppSettingsDto(pluginDef, orgApp))
- }
-}
-
-func UpdateAppSettings(c *middleware.Context, cmd m.UpdateAppSettingsCmd) Response {
- appId := c.Params(":appId")
-
- cmd.OrgId = c.OrgId
- cmd.AppId = appId
-
- if _, ok := plugins.Apps[cmd.AppId]; !ok {
- return ApiError(404, "App type not installed.", nil)
- }
-
- err := bus.Dispatch(&cmd)
- if err != nil {
- return ApiError(500, "Failed to update App Plugin", err)
- }
-
- return ApiSuccess("App updated")
-}
diff --git a/pkg/api/dtos/apps.go b/pkg/api/dtos/apps.go
deleted file mode 100644
index ba26e3eb617..00000000000
--- a/pkg/api/dtos/apps.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package dtos
-
-import (
- "github.com/grafana/grafana/pkg/models"
- "github.com/grafana/grafana/pkg/plugins"
-)
-
-type AppSettings struct {
- Name string `json:"name"`
- AppId string `json:"appId"`
- Enabled bool `json:"enabled"`
- Pinned bool `json:"pinned"`
- Module string `json:"module"`
- BaseUrl string `json:"baseUrl"`
- Info *plugins.PluginInfo `json:"info"`
- 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,
- BaseUrl: def.BaseUrl,
- Pages: def.Pages,
- Includes: def.Includes,
- }
-
- if data != nil {
- dto.Enabled = data.Enabled
- dto.Pinned = data.Pinned
- dto.Info = &def.Info
- dto.JsonData = data.JsonData
- }
-
- return dto
-}
diff --git a/pkg/api/dtos/plugins.go b/pkg/api/dtos/plugins.go
new file mode 100644
index 00000000000..af96202222f
--- /dev/null
+++ b/pkg/api/dtos/plugins.go
@@ -0,0 +1,26 @@
+package dtos
+
+import "github.com/grafana/grafana/pkg/plugins"
+
+type PluginSetting struct {
+ Name string `json:"name"`
+ Type string `json:"type"`
+ PluginId string `json:"pluginId"`
+ Enabled bool `json:"enabled"`
+ Pinned bool `json:"pinned"`
+ Module string `json:"module"`
+ BaseUrl string `json:"baseUrl"`
+ Info *plugins.PluginInfo `json:"info"`
+ Pages []*plugins.AppPluginPage `json:"pages"`
+ Includes []*plugins.AppIncludeInfo `json:"includes"`
+ JsonData map[string]interface{} `json:"jsonData"`
+}
+
+type PluginListItem struct {
+ Name string `json:"name"`
+ Type string `json:"type"`
+ PluginId string `json:"pluginId"`
+ Enabled bool `json:"enabled"`
+ Pinned bool `json:"pinned"`
+ Info *plugins.PluginInfo `json:"info"`
+}
diff --git a/pkg/api/index.go b/pkg/api/index.go
index 9edbdda7c96..df752109530 100644
--- a/pkg/api/index.go
+++ b/pkg/api/index.go
@@ -72,7 +72,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
Text: "Plugins",
Icon: "icon-gf icon-gf-apps",
- Url: setting.AppSubUrl + "/apps",
+ Url: setting.AppSubUrl + "/plugins",
})
}
diff --git a/pkg/api/plugin_setting.go b/pkg/api/plugin_setting.go
new file mode 100644
index 00000000000..08fc5296434
--- /dev/null
+++ b/pkg/api/plugin_setting.go
@@ -0,0 +1,88 @@
+package api
+
+import (
+ "github.com/grafana/grafana/pkg/api/dtos"
+ "github.com/grafana/grafana/pkg/bus"
+ "github.com/grafana/grafana/pkg/middleware"
+ m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/plugins"
+)
+
+func GetPluginList(c *middleware.Context) Response {
+ pluginSettingsMap, err := plugins.GetPluginSettings(c.OrgId)
+
+ if err != nil {
+ return ApiError(500, "Failed to get list of plugins", err)
+ }
+
+ result := make([]*dtos.PluginListItem, 0)
+ for _, pluginDef := range plugins.Plugins {
+ listItem := &dtos.PluginListItem{
+ PluginId: pluginDef.Id,
+ Name: pluginDef.Name,
+ Type: pluginDef.Type,
+ Info: &pluginDef.Info,
+ }
+
+ if pluginSetting, exists := pluginSettingsMap[pluginDef.Id]; exists {
+ listItem.Enabled = pluginSetting.Enabled
+ listItem.Pinned = pluginSetting.Pinned
+ }
+
+ result = append(result, listItem)
+ }
+
+ return Json(200, result)
+}
+
+func GetPluginSettingById(c *middleware.Context) Response {
+ pluginId := c.Params(":pluginId")
+
+ if def, exists := plugins.Plugins[pluginId]; !exists {
+ return ApiError(404, "Plugin not found, no installed plugin with that id", nil)
+ } else {
+ dto := &dtos.PluginSetting{
+ Type: def.Type,
+ PluginId: def.Id,
+ Name: def.Name,
+ Info: &def.Info,
+ }
+
+ if app, exists := plugins.Apps[pluginId]; exists {
+ dto.Pages = app.Pages
+ dto.Includes = app.Includes
+ dto.BaseUrl = app.BaseUrl
+ dto.Module = app.Module
+ }
+
+ query := m.GetPluginSettingByIdQuery{PluginId: pluginId, OrgId: c.OrgId}
+ if err := bus.Dispatch(&query); err != nil {
+ if err != m.ErrPluginSettingNotFound {
+ return ApiError(500, "Failed to get login settings", nil)
+ }
+ } else {
+ dto.Enabled = query.Result.Enabled
+ dto.Pinned = query.Result.Pinned
+ dto.JsonData = query.Result.JsonData
+ }
+
+ return Json(200, dto)
+ }
+}
+
+func UpdatePluginSetting(c *middleware.Context, cmd m.UpdatePluginSettingCmd) Response {
+ pluginId := c.Params(":pluginId")
+
+ cmd.OrgId = c.OrgId
+ cmd.PluginId = pluginId
+
+ if _, ok := plugins.Apps[cmd.PluginId]; !ok {
+ return ApiError(404, "Plugin not installed.", nil)
+ }
+
+ if err := bus.Dispatch(&cmd); err != nil {
+ return ApiError(500, "Failed to update plugin setting", err)
+ }
+
+ return ApiSuccess("Plugin settings updated")
+}
diff --git a/pkg/api/pluginproxy/pluginproxy.go b/pkg/api/pluginproxy/pluginproxy.go
index 55aa0c013ce..92d07988b64 100644
--- a/pkg/api/pluginproxy/pluginproxy.go
+++ b/pkg/api/pluginproxy/pluginproxy.go
@@ -26,7 +26,7 @@ type templateData struct {
func getHeaders(route *plugins.AppPluginRoute, orgId int64, appId string) (http.Header, error) {
result := http.Header{}
- query := m.GetAppSettingByAppIdQuery{OrgId: orgId, AppId: appId}
+ query := m.GetPluginSettingByIdQuery{OrgId: orgId, PluginId: appId}
if err := bus.Dispatch(&query); err != nil {
return nil, err
diff --git a/pkg/models/app_settings.go b/pkg/models/plugin_setting.go
similarity index 71%
rename from pkg/models/app_settings.go
rename to pkg/models/plugin_setting.go
index 6a7bbde694d..777f4599241 100644
--- a/pkg/models/app_settings.go
+++ b/pkg/models/plugin_setting.go
@@ -9,12 +9,12 @@ import (
)
var (
- ErrAppSettingNotFound = errors.New("AppSetting not found")
+ ErrPluginSettingNotFound = errors.New("Plugin setting not found")
)
-type AppSettings struct {
+type PluginSetting struct {
Id int64
- AppId string
+ PluginId string
OrgId int64
Enabled bool
Pinned bool
@@ -39,17 +39,17 @@ func (s SecureJsonData) Decrypt() map[string]string {
// COMMANDS
// Also acts as api DTO
-type UpdateAppSettingsCmd struct {
+type UpdatePluginSettingCmd struct {
Enabled bool `json:"enabled"`
Pinned bool `json:"pinned"`
JsonData map[string]interface{} `json:"jsonData"`
SecureJsonData map[string]string `json:"secureJsonData"`
- AppId string `json:"-"`
- OrgId int64 `json:"-"`
+ PluginId string `json:"-"`
+ OrgId int64 `json:"-"`
}
-func (cmd *UpdateAppSettingsCmd) GetEncryptedJsonData() SecureJsonData {
+func (cmd *UpdatePluginSettingCmd) GetEncryptedJsonData() SecureJsonData {
encrypted := make(SecureJsonData)
for key, data := range cmd.SecureJsonData {
encrypted[key] = util.Encrypt([]byte(data), setting.SecretKey)
@@ -59,13 +59,13 @@ func (cmd *UpdateAppSettingsCmd) GetEncryptedJsonData() SecureJsonData {
// ---------------------
// QUERIES
-type GetAppSettingsQuery struct {
+type GetPluginSettingsQuery struct {
OrgId int64
- Result []*AppSettings
+ Result []*PluginSetting
}
-type GetAppSettingByAppIdQuery struct {
- AppId string
- OrgId int64
- Result *AppSettings
+type GetPluginSettingByIdQuery struct {
+ PluginId string
+ OrgId int64
+ Result *PluginSetting
}
diff --git a/pkg/plugins/frontend_plugin.go b/pkg/plugins/frontend_plugin.go
index a1bcd0c68b5..5acb9966495 100644
--- a/pkg/plugins/frontend_plugin.go
+++ b/pkg/plugins/frontend_plugin.go
@@ -56,6 +56,10 @@ func (fp *FrontendPluginBase) handleModuleDefaults() {
}
func evalRelativePluginUrlPath(pathStr string, pluginId string) string {
+ if pathStr == "" {
+ return ""
+ }
+
u, _ := url.Parse(pathStr)
if u.IsAbs() {
return pathStr
diff --git a/pkg/plugins/queries.go b/pkg/plugins/queries.go
index 8e628c5024e..b3cc92bf7d1 100644
--- a/pkg/plugins/queries.go
+++ b/pkg/plugins/queries.go
@@ -5,44 +5,40 @@ import (
m "github.com/grafana/grafana/pkg/models"
)
-func GetOrgAppSettings(orgId int64) (map[string]*m.AppSettings, error) {
- query := m.GetAppSettingsQuery{OrgId: orgId}
+func GetPluginSettings(orgId int64) (map[string]*m.PluginSetting, error) {
+ query := m.GetPluginSettingsQuery{OrgId: orgId}
if err := bus.Dispatch(&query); err != nil {
return nil, err
}
- orgAppsMap := make(map[string]*m.AppSettings)
- for _, orgApp := range query.Result {
- orgAppsMap[orgApp.AppId] = orgApp
+ pluginMap := make(map[string]*m.PluginSetting)
+ for _, plug := range query.Result {
+ pluginMap[plug.PluginId] = plug
}
- return orgAppsMap, nil
+ return pluginMap, nil
}
func GetEnabledPlugins(orgId int64) (*EnabledPlugins, error) {
enabledPlugins := NewEnabledPlugins()
- orgApps, err := GetOrgAppSettings(orgId)
+ orgPlugins, err := GetPluginSettings(orgId)
if err != nil {
return nil, err
}
enabledApps := make(map[string]bool)
- for appId, installedApp := range Apps {
- var app AppPlugin
- app = *installedApp
+ for pluginId, app := range Apps {
- // check if the app is stored in the DB for this org and if so, use the
- // state stored there.
- if b, ok := orgApps[appId]; ok {
+ if b, ok := orgPlugins[pluginId]; ok {
app.Enabled = b.Enabled
app.Pinned = b.Pinned
}
if app.Enabled {
- enabledApps[app.Id] = true
- enabledPlugins.Apps = append(enabledPlugins.Apps, &app)
+ enabledApps[pluginId] = true
+ enabledPlugins.Apps = append(enabledPlugins.Apps, app)
}
}
diff --git a/pkg/services/sqlstore/app_settings.go b/pkg/services/sqlstore/app_settings.go
deleted file mode 100644
index e7d8a90a495..00000000000
--- a/pkg/services/sqlstore/app_settings.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package sqlstore
-
-import (
- "time"
-
- "github.com/grafana/grafana/pkg/bus"
- m "github.com/grafana/grafana/pkg/models"
- "github.com/grafana/grafana/pkg/setting"
- "github.com/grafana/grafana/pkg/util"
-)
-
-func init() {
- bus.AddHandler("sql", GetAppSettings)
- bus.AddHandler("sql", GetAppSettingByAppId)
- bus.AddHandler("sql", UpdateAppSettings)
-}
-
-func GetAppSettings(query *m.GetAppSettingsQuery) error {
- sess := x.Where("org_id=?", query.OrgId)
-
- query.Result = make([]*m.AppSettings, 0)
- return sess.Find(&query.Result)
-}
-
-func GetAppSettingByAppId(query *m.GetAppSettingByAppIdQuery) error {
- appSetting := m.AppSettings{OrgId: query.OrgId, AppId: query.AppId}
- has, err := x.Get(&appSetting)
- if err != nil {
- return err
- } else if has == false {
- return m.ErrAppSettingNotFound
- }
- query.Result = &appSetting
- return nil
-}
-
-func UpdateAppSettings(cmd *m.UpdateAppSettingsCmd) error {
- return inTransaction2(func(sess *session) error {
- var app m.AppSettings
-
- exists, err := sess.Where("org_id=? and app_id=?", cmd.OrgId, cmd.AppId).Get(&app)
- sess.UseBool("enabled")
- sess.UseBool("pinned")
- if !exists {
- app = m.AppSettings{
- AppId: cmd.AppId,
- OrgId: cmd.OrgId,
- Enabled: cmd.Enabled,
- Pinned: cmd.Pinned,
- JsonData: cmd.JsonData,
- SecureJsonData: cmd.GetEncryptedJsonData(),
- Created: time.Now(),
- Updated: time.Now(),
- }
- _, err = sess.Insert(&app)
- return err
- } else {
- for key, data := range cmd.SecureJsonData {
- app.SecureJsonData[key] = util.Encrypt([]byte(data), setting.SecretKey)
- }
- app.SecureJsonData = cmd.GetEncryptedJsonData()
- app.Updated = time.Now()
- app.Enabled = cmd.Enabled
- app.JsonData = cmd.JsonData
- app.Pinned = cmd.Pinned
- _, err = sess.Id(app.Id).Update(&app)
- return err
- }
- })
-}
diff --git a/pkg/services/sqlstore/migrations/app_settings.go b/pkg/services/sqlstore/migrations/plugin_setting.go
similarity index 63%
rename from pkg/services/sqlstore/migrations/app_settings.go
rename to pkg/services/sqlstore/migrations/plugin_setting.go
index 885dbbf9f05..4a8729691d3 100644
--- a/pkg/services/sqlstore/migrations/app_settings.go
+++ b/pkg/services/sqlstore/migrations/plugin_setting.go
@@ -4,12 +4,12 @@ import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
func addAppSettingsMigration(mg *Migrator) {
- appSettingsV2 := Table{
- Name: "app_settings",
+ pluginSettingTable := Table{
+ Name: "plugin_setting",
Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "org_id", Type: DB_BigInt, Nullable: true},
- {Name: "app_id", Type: DB_NVarchar, Length: 255, Nullable: false},
+ {Name: "plugin_id", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "enabled", Type: DB_Bool, Nullable: false},
{Name: "pinned", Type: DB_Bool, Nullable: false},
{Name: "json_data", Type: DB_Text, Nullable: true},
@@ -18,14 +18,12 @@ func addAppSettingsMigration(mg *Migrator) {
{Name: "updated", Type: DB_DateTime, Nullable: false},
},
Indices: []*Index{
- {Cols: []string{"org_id", "app_id"}, Type: UniqueIndex},
+ {Cols: []string{"org_id", "plugin_id"}, Type: UniqueIndex},
},
}
- mg.AddMigration("Drop old table app_settings v1", NewDropTableMigration("app_settings"))
-
- mg.AddMigration("create app_settings table v2", NewAddTableMigration(appSettingsV2))
+ mg.AddMigration("create plugin_setting table", NewAddTableMigration(pluginSettingTable))
//------- indexes ------------------
- addTableIndicesMigrations(mg, "v3", appSettingsV2)
+ addTableIndicesMigrations(mg, "v1", pluginSettingTable)
}
diff --git a/pkg/services/sqlstore/plugin_setting.go b/pkg/services/sqlstore/plugin_setting.go
new file mode 100644
index 00000000000..53f4857d9b6
--- /dev/null
+++ b/pkg/services/sqlstore/plugin_setting.go
@@ -0,0 +1,70 @@
+package sqlstore
+
+import (
+ "time"
+
+ "github.com/grafana/grafana/pkg/bus"
+ m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/setting"
+ "github.com/grafana/grafana/pkg/util"
+)
+
+func init() {
+ bus.AddHandler("sql", GetPluginSettings)
+ bus.AddHandler("sql", GetPluginSettingById)
+ bus.AddHandler("sql", UpdatePluginSetting)
+}
+
+func GetPluginSettings(query *m.GetPluginSettingsQuery) error {
+ sess := x.Where("org_id=?", query.OrgId)
+
+ query.Result = make([]*m.PluginSetting, 0)
+ return sess.Find(&query.Result)
+}
+
+func GetPluginSettingById(query *m.GetPluginSettingByIdQuery) error {
+ pluginSetting := m.PluginSetting{OrgId: query.OrgId, PluginId: query.PluginId}
+ has, err := x.Get(&pluginSetting)
+ if err != nil {
+ return err
+ } else if has == false {
+ return m.ErrPluginSettingNotFound
+ }
+ query.Result = &pluginSetting
+ return nil
+}
+
+func UpdatePluginSetting(cmd *m.UpdatePluginSettingCmd) error {
+ return inTransaction2(func(sess *session) error {
+ var pluginSetting m.PluginSetting
+
+ exists, err := sess.Where("org_id=? and plugin_id=?", cmd.OrgId, cmd.PluginId).Get(&pluginSetting)
+ sess.UseBool("enabled")
+ sess.UseBool("pinned")
+ if !exists {
+ pluginSetting = m.PluginSetting{
+ PluginId: cmd.PluginId,
+ OrgId: cmd.OrgId,
+ Enabled: cmd.Enabled,
+ Pinned: cmd.Pinned,
+ JsonData: cmd.JsonData,
+ SecureJsonData: cmd.GetEncryptedJsonData(),
+ Created: time.Now(),
+ Updated: time.Now(),
+ }
+ _, err = sess.Insert(&pluginSetting)
+ return err
+ } else {
+ for key, data := range cmd.SecureJsonData {
+ pluginSetting.SecureJsonData[key] = util.Encrypt([]byte(data), setting.SecretKey)
+ }
+ pluginSetting.SecureJsonData = cmd.GetEncryptedJsonData()
+ pluginSetting.Updated = time.Now()
+ pluginSetting.Enabled = cmd.Enabled
+ pluginSetting.JsonData = cmd.JsonData
+ pluginSetting.Pinned = cmd.Pinned
+ _, err = sess.Id(pluginSetting.Id).Update(&pluginSetting)
+ return err
+ }
+ })
+}
diff --git a/public/app/core/components/sidemenu/sidemenu.ts b/public/app/core/components/sidemenu/sidemenu.ts
index 0147eb51062..4c0e100f85c 100644
--- a/public/app/core/components/sidemenu/sidemenu.ts
+++ b/public/app/core/components/sidemenu/sidemenu.ts
@@ -38,7 +38,6 @@ export class SideMenuCtrl {
openUserDropdown() {
this.orgMenu = [
{section: 'You', cssClass: 'dropdown-menu-title'},
- {text: 'Preferences', url: this.getUrl('/profile')},
{text: 'Profile', url: this.getUrl('/profile')},
];
@@ -100,6 +99,22 @@ export function sideMenuDirective() {
bindToController: true,
controllerAs: 'ctrl',
scope: {},
+ link: function(scope, elem) {
+ // hack to hide dropdown menu
+ elem.on('click.dropdown', '.dropdown-menu a', function(evt) {
+ var menu = $(evt.target).parents('.dropdown-menu');
+ var parent = menu.parent();
+ menu.detach();
+
+ setTimeout(function() {
+ parent.append(menu);
+ }, 100);
+ });
+
+ scope.$on("$destory", function() {
+ elem.off('click.dropdown');
+ });
+ }
};
}
diff --git a/public/app/core/directives/plugin_component.ts b/public/app/core/directives/plugin_component.ts
index 5c3baf3d223..858aab02035 100644
--- a/public/app/core/directives/plugin_component.ts
+++ b/public/app/core/directives/plugin_component.ts
@@ -160,13 +160,13 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
}
// AppConfigCtrl
case 'app-config-ctrl': {
- let appModel = scope.ctrl.appModel;
- return System.import(appModel.module).then(function(appModule) {
+ let model = scope.ctrl.model;
+ return System.import(model.module).then(function(appModule) {
return {
- baseUrl: appModel.baseUrl,
- name: 'app-config-' + appModel.appId,
+ baseUrl: model.baseUrl,
+ name: 'app-config-' + model.pluginId,
bindings: {appModel: "=", appEditCtrl: "="},
- attrs: {"app-model": "ctrl.appModel", "app-edit-ctrl": "ctrl"},
+ attrs: {"app-model": "ctrl.model", "app-edit-ctrl": "ctrl"},
Component: appModule.ConfigCtrl,
};
});
diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts
index 604dc9f9876..6e3af683c82 100644
--- a/public/app/core/routes/routes.ts
+++ b/public/app/core/routes/routes.ts
@@ -11,7 +11,7 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
var loadOrgBundle = new BundleLoader('app/features/org/all');
- var loadAppsBundle = new BundleLoader('app/features/apps/all');
+ var loadPluginsBundle = new BundleLoader('app/features/plugins/all');
var loadAdminBundle = new BundleLoader('app/features/admin/admin');
$routeProvider
@@ -165,23 +165,23 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
controller : 'SnapshotsCtrl',
controllerAs: 'ctrl',
})
- .when('/apps', {
- templateUrl: 'public/app/features/apps/partials/list.html',
- controller: 'AppListCtrl',
+ .when('/plugins', {
+ templateUrl: 'public/app/features/plugins/partials/list.html',
+ controller: 'PluginListCtrl',
controllerAs: 'ctrl',
- resolve: loadAppsBundle,
+ resolve: loadPluginsBundle,
})
- .when('/apps/:appId/edit', {
- templateUrl: 'public/app/features/apps/partials/edit.html',
- controller: 'AppEditCtrl',
+ .when('/plugins/:pluginId/edit', {
+ templateUrl: 'public/app/features/plugins/partials/edit.html',
+ controller: 'PluginEditCtrl',
controllerAs: 'ctrl',
- resolve: loadAppsBundle,
+ resolve: loadPluginsBundle,
})
- .when('/apps/:appId/page/:slug', {
- templateUrl: 'public/app/features/apps/partials/page.html',
+ .when('/plugins/:pluginId/page/:slug', {
+ templateUrl: 'public/app/features/plugins/partials/page.html',
controller: 'AppPageCtrl',
controllerAs: 'ctrl',
- resolve: loadAppsBundle,
+ resolve: loadPluginsBundle,
})
.when('/global-alerts', {
templateUrl: 'public/app/features/dashboard/partials/globalAlerts.html',
diff --git a/public/app/features/admin/partials/edit_org.html b/public/app/features/admin/partials/edit_org.html
index de92cd3adf5..b08d0c49a45 100644
--- a/public/app/features/admin/partials/edit_org.html
+++ b/public/app/features/admin/partials/edit_org.html
@@ -1,5 +1,8 @@
-
+
+
+ Orgs
+
diff --git a/public/app/features/admin/partials/edit_user.html b/public/app/features/admin/partials/edit_user.html
index 24f98a206f4..6d3e6e68f54 100644
--- a/public/app/features/admin/partials/edit_user.html
+++ b/public/app/features/admin/partials/edit_user.html
@@ -1,5 +1,8 @@
-
+
+
+ Users
+
diff --git a/public/app/features/admin/partials/new_user.html b/public/app/features/admin/partials/new_user.html
index 3ae2dbfc209..1d4a4b231b5 100644
--- a/public/app/features/admin/partials/new_user.html
+++ b/public/app/features/admin/partials/new_user.html
@@ -1,5 +1,8 @@
-
+
+
+ Users
+
diff --git a/public/app/features/admin/partials/orgs.html b/public/app/features/admin/partials/orgs.html
index aba3ef70a92..ae02b72b418 100644
--- a/public/app/features/admin/partials/orgs.html
+++ b/public/app/features/admin/partials/orgs.html
@@ -1,5 +1,8 @@
-
+
+
+ Orgs
+
diff --git a/public/app/features/admin/partials/users.html b/public/app/features/admin/partials/users.html
index adafe2ddf6f..a1c86391088 100644
--- a/public/app/features/admin/partials/users.html
+++ b/public/app/features/admin/partials/users.html
@@ -1,5 +1,8 @@
-
+
+
+ Users
+
diff --git a/public/app/features/apps/edit_ctrl.ts b/public/app/features/apps/edit_ctrl.ts
deleted file mode 100644
index 1cb4dd269ef..00000000000
--- a/public/app/features/apps/edit_ctrl.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-///
-
-import angular from 'angular';
-import _ from 'lodash';
-
-export class AppEditCtrl {
- appModel: any;
- appId: any;
- includedPanels: any;
- includedDatasources: any;
-
- /** @ngInject */
- constructor(private backendSrv: any, private $routeParams: any) {
- this.appModel = {};
- this.appId = $routeParams.appId;
-
- this.backendSrv.get(`/api/org/apps/${this.appId}/settings`).then(result => {
- this.appModel = result;
- this.includedPanels = _.where(result.includes, {type: 'panel'});
- this.includedDatasources = _.where(result.includes, {type: 'datasource'});
- });
- }
-
- update() {
- var updateCmd = _.extend({
- appId: this.appModel.appId,
- orgId: this.appModel.orgId,
- enabled: this.appModel.enabled,
- pinned: this.appModel.pinned,
- jsonData: this.appModel.jsonData,
- secureJsonData: this.appModel.secureJsonData,
- }, {});
-
- this.backendSrv.post(`/api/org/apps/${this.appId}/settings`, updateCmd).then(function() {
- window.location.href = window.location.href;
- });
- }
-
- toggleEnabled() {
- this.update();
- }
-
- togglePinned() {
- this.update();
- }
-}
-
-angular.module('grafana.controllers').controller('AppEditCtrl', AppEditCtrl);
-
diff --git a/public/app/features/apps/list_ctrl.ts b/public/app/features/apps/list_ctrl.ts
deleted file mode 100644
index 8777f206d88..00000000000
--- a/public/app/features/apps/list_ctrl.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-///
-
-import angular from 'angular';
-
-export class AppListCtrl {
- apps: any[];
-
- /** @ngInject */
- constructor(private backendSrv: any) {
-
- this.backendSrv.get('api/org/apps').then(apps => {
- this.apps = apps;
- });
- }
-}
-
-angular.module('grafana.controllers').controller('AppListCtrl', AppListCtrl);
diff --git a/public/app/features/apps/partials/edit.html b/public/app/features/apps/partials/edit.html
deleted file mode 100644
index 7def9a3c96e..00000000000
--- a/public/app/features/apps/partials/edit.html
+++ /dev/null
@@ -1,108 +0,0 @@
-
-
-
-
-
-
-

-
-
-
- {{ctrl.appModel.name}}
-
-
- {{ctrl.appModel.info.description}}
-
- Version: {{ctrl.appModel.info.version}} Updated: {{ctrl.appModel.info.updated}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Panels
-
-
- - None
- -
- {{panel.name}}
-
-
-
-
-
-
- Datasources
-
-
- - None
- -
- {{ds.name}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/app/features/apps/partials/list.html b/public/app/features/apps/partials/list.html
deleted file mode 100644
index bb39e6c6504..00000000000
--- a/public/app/features/apps/partials/list.html
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
- No apps defined
-
-
-
- -
-
- -
-
-
- -
-
-
-
- {{app.name}}
-
-
-
- Enabled
-
-
-
- Pinned
-
-
-
-
- Dashboards: 1
-
-
-
-
-
-
diff --git a/public/app/features/datasources/partials/list.html b/public/app/features/datasources/partials/list.html
index 0a2e2673bbd..6ad9c18bc53 100644
--- a/public/app/features/datasources/partials/list.html
+++ b/public/app/features/datasources/partials/list.html
@@ -22,8 +22,8 @@
- Name |
- Url |
+ name |
+ url |
|
|
|
diff --git a/public/app/features/apps/all.ts b/public/app/features/plugins/all.ts
similarity index 100%
rename from public/app/features/apps/all.ts
rename to public/app/features/plugins/all.ts
diff --git a/public/app/features/plugins/edit_ctrl.ts b/public/app/features/plugins/edit_ctrl.ts
new file mode 100644
index 00000000000..3d1e724e0c4
--- /dev/null
+++ b/public/app/features/plugins/edit_ctrl.ts
@@ -0,0 +1,51 @@
+///
+
+import angular from 'angular';
+import _ from 'lodash';
+
+export class PluginEditCtrl {
+ model: any;
+ pluginId: any;
+ includedPanels: any;
+ includedDatasources: any;
+ tabIndex: number;
+
+ /** @ngInject */
+ constructor(private backendSrv: any, private $routeParams: any) {
+ this.model = {};
+ this.pluginId = $routeParams.pluginId;
+ this.tabIndex = 0;
+
+ this.backendSrv.get(`/api/org/plugins/${this.pluginId}/settings`).then(result => {
+ this.model = result;
+ this.includedPanels = _.where(result.includes, {type: 'panel'});
+ this.includedDatasources = _.where(result.includes, {type: 'datasource'});
+ });
+ }
+
+ update() {
+ var updateCmd = _.extend({
+ pluginId: this.model.pluginId,
+ orgId: this.model.orgId,
+ enabled: this.model.enabled,
+ pinned: this.model.pinned,
+ jsonData: this.model.jsonData,
+ secureJsonData: this.model.secureJsonData,
+ }, {});
+
+ this.backendSrv.post(`/api/org/plugins/${this.pluginId}/settings`, updateCmd).then(function() {
+ window.location.href = window.location.href;
+ });
+ }
+
+ toggleEnabled() {
+ this.update();
+ }
+
+ togglePinned() {
+ this.update();
+ }
+}
+
+angular.module('grafana.controllers').controller('PluginEditCtrl', PluginEditCtrl);
+
diff --git a/public/app/features/plugins/list_ctrl.ts b/public/app/features/plugins/list_ctrl.ts
new file mode 100644
index 00000000000..a1bacc09c14
--- /dev/null
+++ b/public/app/features/plugins/list_ctrl.ts
@@ -0,0 +1,17 @@
+///
+
+import angular from 'angular';
+
+export class PluginListCtrl {
+ plugins: any[];
+
+ /** @ngInject */
+ constructor(private backendSrv: any) {
+
+ this.backendSrv.get('api/org/plugins').then(plugins => {
+ this.plugins = plugins;
+ });
+ }
+}
+
+angular.module('grafana.controllers').controller('PluginListCtrl', PluginListCtrl);
diff --git a/public/app/features/apps/page_ctrl.ts b/public/app/features/plugins/page_ctrl.ts
similarity index 100%
rename from public/app/features/apps/page_ctrl.ts
rename to public/app/features/plugins/page_ctrl.ts
diff --git a/public/app/features/plugins/partials/edit.html b/public/app/features/plugins/partials/edit.html
new file mode 100644
index 00000000000..8747ac66610
--- /dev/null
+++ b/public/app/features/plugins/partials/edit.html
@@ -0,0 +1,185 @@
+
+
+
+ Apps
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/app/features/plugins/partials/list.html b/public/app/features/plugins/partials/list.html
new file mode 100644
index 00000000000..2b2f9741be0
--- /dev/null
+++ b/public/app/features/plugins/partials/list.html
@@ -0,0 +1,42 @@
+
+
+
+
+
diff --git a/public/app/features/apps/partials/page.html b/public/app/features/plugins/partials/page.html
similarity index 100%
rename from public/app/features/apps/partials/page.html
rename to public/app/features/plugins/partials/page.html
diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss
index 33c8fefa390..42956f3c3fa 100644
--- a/public/sass/_grafana.scss
+++ b/public/sass/_grafana.scss
@@ -26,6 +26,7 @@
@import "utils/widths";
// LAYOUTS
+@import "layout/lists";
@import "layout/page";
// COMPONENTS
@@ -44,7 +45,6 @@
@import "components/tagsinput";
@import "components/tables_lists";
@import "components/search";
-@import "components/dashboard";
@import "components/tightform";
@import "components/gf-form";
@import "components/sidemenu";
@@ -69,10 +69,11 @@
// PAGES
@import "pages/login";
+@import "pages/dashboard";
@import "pages/playlist";
@import "pages/admin";
@import "pages/alerting";
-@import "pages/apps";
+@import "pages/plugins";
@import "pages/signup";
@import "pages/styleguide";
diff --git a/public/sass/_variables.dark.scss b/public/sass/_variables.dark.scss
index 7c6c4338b72..4cabda83901 100644
--- a/public/sass/_variables.dark.scss
+++ b/public/sass/_variables.dark.scss
@@ -50,8 +50,10 @@ $critical: #ed2e18;
// -------------------------
$body-bg: rgb(20,20,20);
$page-bg: $dark-2;
-$body-color: $gray-4;
-$text-color: $gray-4;
+$body-color: $gray-4;
+$text-color: $gray-4;
+$text-color-strong: $white;
+$text-color-weak: $gray-2;
// gradients
$brand-gradient: linear-gradient(to right, rgba(255,213,0,0.7) 0%, rgba(255,68,0,0.7) 99%, rgba(255,68,0,0.7) 100%);
diff --git a/public/sass/_variables.light.scss b/public/sass/_variables.light.scss
index 2995ac44717..af66e2c2389 100644
--- a/public/sass/_variables.light.scss
+++ b/public/sass/_variables.light.scss
@@ -58,6 +58,8 @@ $body-bg: $white;
$page-bg: $white;
$body-color: $gray-1;
$text-color: $gray-1;
+$text-color-strong: $white;
+$text-color-weak: $gray-1;
// gradients
$brand-gradient: linear-gradient(to right, rgba(255,213,0,1.0) 0%, rgba(255,68,0,1.0) 99%, rgba(255,68,0,1.0) 100%);
diff --git a/public/sass/_variables.scss b/public/sass/_variables.scss
index fd5857c30a0..91e7865e630 100644
--- a/public/sass/_variables.scss
+++ b/public/sass/_variables.scss
@@ -93,7 +93,7 @@ $font-size-sm: .875rem !default;
$font-size-xs: .75rem !default;
$line-height-base: 1.5 !default;
-$font-weight-semi-bold: 600;
+$font-weight-semi-bold: 600;
$font-size-h1: 2.0rem !default;
$font-size-h2: 1.75rem !default;
@@ -141,6 +141,11 @@ $border-radius-sm: 0.1rem !default;
$caret-width: .3em !default;
$caret-width-lg: $caret-width !default;
+// Page
+
+$page-sidebar-width: 10rem;
+$page-sidebar-margin: 5rem;
+
// Links
// -------------------------
$link-decoration: none !default;
diff --git a/public/sass/base/_type.scss b/public/sass/base/_type.scss
index bf4045daa1d..b26f5493d8e 100644
--- a/public/sass/base/_type.scss
+++ b/public/sass/base/_type.scss
@@ -65,7 +65,7 @@ h1, h2, h3, h4, h5, h6,
color: $headings-color;
}
-h1, .h1 { font-size: $font-size-h1; }
+h1, .h1 { font-size: $font-size-h1; font-style: italic; }
h2, .h2 { font-size: $font-size-h2; }
h3, .h3 { font-size: $font-size-h3; }
h4, .h4 { font-size: $font-size-h4; }
diff --git a/public/sass/components/_sidemenu.scss b/public/sass/components/_sidemenu.scss
index 9a0d12cf8c7..0cc0d9c5f91 100644
--- a/public/sass/components/_sidemenu.scss
+++ b/public/sass/components/_sidemenu.scss
@@ -73,7 +73,7 @@
// again by the mouse getting outside the hover space
left: $side-menu-width - 0.2rem;
background-color: rgba($side-menu-bg,$side-menu-opacity);
- @include animation('dropdown-anim 100ms ease-in-out 100ms forwards');
+ @include animation('dropdown-anim 150ms ease-in-out 100ms forwards');
z-index: -9999;
}
}
@@ -82,11 +82,6 @@
@include keyframes(dropdown-anim) {
0% {
- display: none;
- opacity: 0;
- }
- 1% {
- display: block;
opacity: 0;
transform: translate3d(-5%,0,0);
}
diff --git a/public/sass/components/_tabs.scss b/public/sass/components/_tabs.scss
index 05988c7c763..0165eec93d6 100644
--- a/public/sass/components/_tabs.scss
+++ b/public/sass/components/_tabs.scss
@@ -13,7 +13,7 @@
@include border-radius(3px);
border: 1px solid $divider-border-color;
background-color: transparent;
- border-bottom: 1px solid $panel-bg;
+ border-bottom: 1px solid $page-bg;
color: $link-color;
}
diff --git a/public/sass/layout/_lists.scss b/public/sass/layout/_lists.scss
new file mode 100644
index 00000000000..3333e8d937b
--- /dev/null
+++ b/public/sass/layout/_lists.scss
@@ -0,0 +1,14 @@
+
+.ui-list {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+
+ > li {
+ margin-bottom: 0.3125rem;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+}
diff --git a/public/sass/layout/_page.scss b/public/sass/layout/_page.scss
index cf53b0958a3..bf323a61dfc 100644
--- a/public/sass/layout/_page.scss
+++ b/public/sass/layout/_page.scss
@@ -3,21 +3,10 @@
height: 100%;
}
-.dashboard-container {
- padding: $dashboard-padding;
- width: 100%;
-}
-
.main-view {
height: 100%;
}
-.page-dashboard {
- .main-view {
- background-image: none;
- }
-}
-
.page-container {
background-color: $page-bg;
padding: ($spacer * 2) ($spacer * 4);
@@ -27,8 +16,29 @@
background-image: linear-gradient(60deg, transparent 70%, darken($page-bg, 4%) 98%)
}
+.page-body {
+ @include media-breakpoint-up(md) {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ }
+}
+
+.page-content-with-sidebar {
+ width: calc(100% - #{$page-sidebar-width + $page-sidebar-margin}); // sidebar width + margin
+}
+
+.page-sidebar {
+ @include media-breakpoint-up(md) {
+ width: $page-sidebar-width;
+ margin-left: $page-sidebar-margin;
+ }
+}
+
.page-header {
padding: $spacer 0 $spacer/2 0;
+ margin-bottom: 2rem;
+
display: flex;
justify-content: flex-end;
align-items: flex-end;
@@ -36,13 +46,12 @@
@include brand-bottom-border();
h1 {
- font-style: italic;
flex-grow: 1;
}
+
button, a {
margin-left: $spacer;
}
- margin-bottom: 2rem;
}
.page-heading {
@@ -71,3 +80,20 @@
}
}
+.page-sidebar {
+ color: $text-color-weak;
+ h4 {
+ font-size: $font-size-base;
+ font-weight: $font-weight-semi-bold;
+ color: $text-color-strong;
+ }
+ h5 {
+ font-size: $font-size-base;
+ color: $text-color-weak;
+ font-weight: $font-weight-semi-bold;
+ }
+}
+
+.page-sidebar-section {
+ margin-bottom: $spacer*2;
+}
diff --git a/public/sass/pages/_apps.scss b/public/sass/pages/_apps.scss
deleted file mode 100644
index 73959edf50b..00000000000
--- a/public/sass/pages/_apps.scss
+++ /dev/null
@@ -1,26 +0,0 @@
-
-.app-edit-logo-box {
- padding: 1.2rem;
- background: $panel-bg;
- text-align: center;
- img {
- max-width: 7rem;
- }
- margin-right: 2rem;
-}
-
-.app-edit-links {
- list-style: none;
- margin: 0 0 0 2rem;
-
- li {
- background: $panel-bg;
- margin-top: 4px;
- padding: 0.2rem 1rem;
- }
-}
-
-.app-edit-description {
- font-style: italic;
- margin-bottom: 1.5rem;
-}
diff --git a/public/sass/components/_dashboard.scss b/public/sass/pages/_dashboard.scss
similarity index 96%
rename from public/sass/components/_dashboard.scss
rename to public/sass/pages/_dashboard.scss
index c4fbb994b24..b04a9ffa084 100644
--- a/public/sass/components/_dashboard.scss
+++ b/public/sass/pages/_dashboard.scss
@@ -1,3 +1,14 @@
+.dashboard-container {
+ padding: $dashboard-padding;
+ width: 100%;
+}
+
+.page-dashboard {
+ .main-view {
+ background-image: none;
+ }
+}
+
.template-variable {
color: $variable;
}
diff --git a/public/sass/pages/_plugins.scss b/public/sass/pages/_plugins.scss
new file mode 100644
index 00000000000..299a1ff739a
--- /dev/null
+++ b/public/sass/pages/_plugins.scss
@@ -0,0 +1,53 @@
+.plugin-header {
+ @include clearfix();
+
+ padding: $spacer 0 $spacer/2 0;
+ margin-bottom: 2rem;
+}
+
+.plugin-header-logo {
+ float: left;
+ width: 7rem;
+ img {
+ width: 7rem;
+ }
+ margin-right: $spacer;
+}
+
+.plugin-header-info-block {
+ padding-top: $spacer;
+}
+
+.plugin-header-author {
+}
+
+.plugin-header-stamps-type {
+ color: $link-color-disabled;
+ text-transform: uppercase;
+}
+
+// .app-edit-logo-box {
+// padding: 1.2rem;
+// background: $panel-bg;
+// text-align: center;
+// img {
+// max-width: 7rem;
+// }
+// margin-right: 2rem;
+// }
+//
+// .app-edit-links {
+// list-style: none;
+// margin: 0 0 0 2rem;
+//
+// li {
+// background: $panel-bg;
+// margin-top: 4px;
+// padding: 0.2rem 1rem;
+// }
+// }
+//
+// .app-edit-description {
+// font-style: italic;
+// margin-bottom: 1.5rem;
+// }