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}} - -
- -
- -       - -
-
- -
- -
-

Included with app:

-
-
-
- - Dashboards -
-
    -
  • None
  • -
-
-
-
- - Panels -
-
    -
  • None
  • -
  • - {{panel.name}} -
  • -
-
-
-
- - Datasources -
-
    -
  • None
  • -
  • - {{ds.name}} -
  • -
-
-
-
- - Pages -
- -
- -
-
- -
-

Dependencies:

-
- Grafana 2.6.x -
-
- -
-

Configuration:

-
-
- -
- -
-
-
- - -
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 -
- -
    -
  • - -
  • -
-
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 @@ - - + + 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 + + + +
+
+ +
+

{{ctrl.model.name}}

+
By {{ctrl.model.info.author.name}}
+
+ + {{ctrl.model.type}} + +
+
+
+ + + +
+
+ README.md +
+ +
+ Details +
+ +
+
+
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + +
+ + +
NameUrlnameurl
+ + + + + + + + + + + + + + + + +
NameType
+ + {{plugin.name}} + + + {{plugin.type}} + + Enabled + Pinned + + + + Edit + +
+
+
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; +// }