From cf926134ef03d4a1ccbb44178660181ff40b8fd5 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Sat, 5 Mar 2016 13:15:49 -0800 Subject: [PATCH 01/54] Added savePreferencesAPI --- pkg/api/api.go | 1 + pkg/api/user.go | 21 ++++++++++++++++++++- pkg/models/preferences.go | 28 ++++++++++++++++++++++++++++ pkg/services/sqlstore/user.go | 25 +++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 pkg/models/preferences.go diff --git a/pkg/api/api.go b/pkg/api/api.go index ed029a5171a..3f805cd9c98 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -96,6 +96,7 @@ func Register(r *macaron.Macaron) { r.Delete("/stars/dashboard/:id", wrap(UnstarDashboard)) r.Put("/password", bind(m.ChangeUserPasswordCommand{}), wrap(ChangeUserPassword)) r.Get("/quotas", wrap(GetUserQuotas)) + r.Put("/prefs", bind(m.SavePreferenceCommand{}), wrap(SaveUserPreferences)) }) // users (admin permission required) diff --git a/pkg/api/user.go b/pkg/api/user.go index 5af243eeb22..0f972431426 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -5,6 +5,7 @@ import ( "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/util" + "github.com/grafana/grafana/pkg/log" ) // GET /api/user (current authenticated user) @@ -110,7 +111,10 @@ func UserSetUsingOrg(c *middleware.Context) Response { } func ChangeUserPassword(c *middleware.Context, cmd m.ChangeUserPasswordCommand) Response { - userQuery := m.GetUserByIdQuery{Id: c.UserId} + + log.Info("%v", cmd) + + userQuery := m.GetUserByIdQuery{Id: c.UserId} if err := bus.Dispatch(&userQuery); err != nil { return ApiError(500, "Could not read user from database", err) @@ -144,3 +148,18 @@ func SearchUsers(c *middleware.Context) Response { return Json(200, query.Result) } + +func SaveUserPreferences(c *middleware.Context, cmd m.SavePreferenceCommand) Response { + + log.Info("%v", cmd.PrefData) + + cmd.PrefId = c.UserId + cmd.PrefType = `user` + + if err := bus.Dispatch(&cmd); err != nil { + return ApiError(500, "Failed to saved user preferences", err) + } + + return ApiSuccess("User preferences saved") + +} diff --git a/pkg/models/preferences.go b/pkg/models/preferences.go new file mode 100644 index 00000000000..cdaab0f6af8 --- /dev/null +++ b/pkg/models/preferences.go @@ -0,0 +1,28 @@ +package models + +import ( + "errors" +) + +// Typed errors +var ( + ErrPreferenceNotFound = errors.New("Preference not found") +) + +type Preference struct { + Id int64 + PrefId int64 + PrefType string + PrefData map[string]interface{} +} + +// --------------------- +// COMMANDS + +type SavePreferenceCommand struct { + + PrefData map[string]interface{} `json:"prefData"` + PrefId int64 `json:"-"` + PrefType string `json:"-"` + +} diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go index 96b8c24b8fc..a5c992d10b6 100644 --- a/pkg/services/sqlstore/user.go +++ b/pkg/services/sqlstore/user.go @@ -11,6 +11,7 @@ import ( m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" + "github.com/grafana/grafana/pkg/log" ) func init() { @@ -27,6 +28,7 @@ func init() { bus.AddHandler("sql", DeleteUser) bus.AddHandler("sql", SetUsingOrg) bus.AddHandler("sql", UpdateUserPermissions) + bus.AddHandler("sql", SaveUserPreferences) } func getOrgIdForNewUser(cmd *m.CreateUserCommand, sess *session) (int64, error) { @@ -346,3 +348,26 @@ func UpdateUserPermissions(cmd *m.UpdateUserPermissionsCommand) error { return err }) } + +func SaveUserPreferences(cmd *m.SavePreferenceCommand) error { + return inTransaction2(func(sess *session) error { + + log.Info("%v", cmd) + + pref := m.Preference{ + PrefId: cmd.PrefId, + PrefType: cmd.PrefType, + PrefData: cmd.PrefData, + } + + sess.Table("preferences").Where("pref_id", pref.PrefId).And("pref_type", pref.PrefType) + + if _, err := sess.Update(&pref); err != nil { + return err + } + + log.Info("%v", pref) + + return nil + }) +} From 1ef332e82ceacbfa0ab7ff1d038016f6e282353e Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Sat, 5 Mar 2016 23:44:56 -0800 Subject: [PATCH 02/54] preferences UI poc --- pkg/api/user.go | 3 -- .../app/core/components/sidemenu/sidemenu.ts | 1 + public/app/core/routes/routes.ts | 4 +++ public/app/features/all.js | 1 + .../profile/partials/preferences.html | 32 +++++++++++++++++++ .../features/profile/partials/profile.html | 2 +- .../app/features/profile/preferencesCtrl.js | 25 +++++++++++++++ 7 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 public/app/features/profile/partials/preferences.html create mode 100644 public/app/features/profile/preferencesCtrl.js diff --git a/pkg/api/user.go b/pkg/api/user.go index 0f972431426..d4545a7c709 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -111,9 +111,6 @@ func UserSetUsingOrg(c *middleware.Context) Response { } func ChangeUserPassword(c *middleware.Context, cmd m.ChangeUserPasswordCommand) Response { - - log.Info("%v", cmd) - userQuery := m.GetUserByIdQuery{Id: c.UserId} if err := bus.Dispatch(&userQuery); err != nil { diff --git a/public/app/core/components/sidemenu/sidemenu.ts b/public/app/core/components/sidemenu/sidemenu.ts index 4c0e100f85c..f68edf8fca5 100644 --- a/public/app/core/components/sidemenu/sidemenu.ts +++ b/public/app/core/components/sidemenu/sidemenu.ts @@ -39,6 +39,7 @@ export class SideMenuCtrl { this.orgMenu = [ {section: 'You', cssClass: 'dropdown-menu-title'}, {text: 'Profile', url: this.getUrl('/profile')}, + {text: 'Preferences', url: this.getUrl('/preferences')}, ]; if (this.isSignedIn) { diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts index 6e3af683c82..334800c22f5 100644 --- a/public/app/core/routes/routes.ts +++ b/public/app/core/routes/routes.ts @@ -89,6 +89,10 @@ function setupAngularRoutes($routeProvider, $locationProvider) { templateUrl: 'public/app/features/profile/partials/profile.html', controller : 'ProfileCtrl', }) + .when('/preferences', { + templateUrl: 'public/app/features/profile/partials/preferences.html', + controller : 'PreferencesCtrl', + }) .when('/profile/password', { templateUrl: 'public/app/features/profile/partials/password.html', controller : 'ChangePasswordCtrl', diff --git a/public/app/features/all.js b/public/app/features/all.js index c110bcff7cd..94da3809ae7 100644 --- a/public/app/features/all.js +++ b/public/app/features/all.js @@ -10,5 +10,6 @@ define([ './profile/profileCtrl', './profile/changePasswordCtrl', './profile/selectOrgCtrl', + './profile/preferencesCtrl', './styleguide/styleguide', ], function () {}); diff --git a/public/app/features/profile/partials/preferences.html b/public/app/features/profile/partials/preferences.html new file mode 100644 index 00000000000..093163dde32 --- /dev/null +++ b/public/app/features/profile/partials/preferences.html @@ -0,0 +1,32 @@ + + + +
+ + +
+
+ Home Dashboard + +
+ +
+ Time Range + +
+ +
+ Theme + +
+ +
+ + Cancel +
+
+ +
+ diff --git a/public/app/features/profile/partials/profile.html b/public/app/features/profile/partials/profile.html index 042344eeac6..dd5afebeaac 100644 --- a/public/app/features/profile/partials/profile.html +++ b/public/app/features/profile/partials/profile.html @@ -7,7 +7,7 @@
-

Preferences

+

Information

Name diff --git a/public/app/features/profile/preferencesCtrl.js b/public/app/features/profile/preferencesCtrl.js new file mode 100644 index 00000000000..00e7d59182c --- /dev/null +++ b/public/app/features/profile/preferencesCtrl.js @@ -0,0 +1,25 @@ +define([ + 'angular', + 'app/core/config', +], +function (angular) { + 'use strict'; + + var module = angular.module('grafana.controllers'); + + module.controller('PreferencesCtrl', function($scope, backendSrv, $location) { + + $scope.command = {}; + + $scope.setUserPreferences = function() { + if (!$scope.userForm.$valid) { return; } + + console.log($scope.command); + + backendSrv.put('/api/user/prefs', $scope.command).then(function() { + $location.path("profile"); + }); + }; + + }); +}); From 660d3fa1e9a75e79468eb66c2dd14ff3f282e318 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Sun, 6 Mar 2016 03:47:39 -0800 Subject: [PATCH 03/54] Implemented savePreferences API --- pkg/api/api.go | 2 +- pkg/api/preferences.go | 21 +++++++++++ pkg/api/user.go | 18 +-------- pkg/models/preferences.go | 18 ++++----- pkg/services/sqlstore/preferences.go | 37 +++++++++++++++++++ pkg/services/sqlstore/user.go | 25 ------------- .../profile/partials/preferences.html | 6 +-- .../app/features/profile/preferencesCtrl.js | 4 +- 8 files changed, 73 insertions(+), 58 deletions(-) create mode 100644 pkg/api/preferences.go create mode 100644 pkg/services/sqlstore/preferences.go diff --git a/pkg/api/api.go b/pkg/api/api.go index 3f805cd9c98..a3d206c4956 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -96,7 +96,7 @@ func Register(r *macaron.Macaron) { r.Delete("/stars/dashboard/:id", wrap(UnstarDashboard)) r.Put("/password", bind(m.ChangeUserPasswordCommand{}), wrap(ChangeUserPassword)) r.Get("/quotas", wrap(GetUserQuotas)) - r.Put("/prefs", bind(m.SavePreferenceCommand{}), wrap(SaveUserPreferences)) + r.Put("/prefs", bind(m.SavePreferencesCommand{}), wrap(SaveUserPreferences)) }) // users (admin permission required) diff --git a/pkg/api/preferences.go b/pkg/api/preferences.go new file mode 100644 index 00000000000..849ed4aa8aa --- /dev/null +++ b/pkg/api/preferences.go @@ -0,0 +1,21 @@ +package api + +import ( + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/middleware" + m "github.com/grafana/grafana/pkg/models" +) + +// PUT /api/user/prefs +func SaveUserPreferences(c *middleware.Context, cmd m.SavePreferencesCommand) Response { + + cmd.PrefId = c.UserId + cmd.PrefType = `user` + + if err := bus.Dispatch(&cmd); err != nil { + return ApiError(500, "Failed to saved user preferences", err) + } + + return ApiSuccess("User preferences saved") + +} diff --git a/pkg/api/user.go b/pkg/api/user.go index d4545a7c709..5af243eeb22 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -5,7 +5,6 @@ import ( "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/util" - "github.com/grafana/grafana/pkg/log" ) // GET /api/user (current authenticated user) @@ -111,7 +110,7 @@ func UserSetUsingOrg(c *middleware.Context) Response { } func ChangeUserPassword(c *middleware.Context, cmd m.ChangeUserPasswordCommand) Response { - userQuery := m.GetUserByIdQuery{Id: c.UserId} + userQuery := m.GetUserByIdQuery{Id: c.UserId} if err := bus.Dispatch(&userQuery); err != nil { return ApiError(500, "Could not read user from database", err) @@ -145,18 +144,3 @@ func SearchUsers(c *middleware.Context) Response { return Json(200, query.Result) } - -func SaveUserPreferences(c *middleware.Context, cmd m.SavePreferenceCommand) Response { - - log.Info("%v", cmd.PrefData) - - cmd.PrefId = c.UserId - cmd.PrefType = `user` - - if err := bus.Dispatch(&cmd); err != nil { - return ApiError(500, "Failed to saved user preferences", err) - } - - return ApiSuccess("User preferences saved") - -} diff --git a/pkg/models/preferences.go b/pkg/models/preferences.go index cdaab0f6af8..8476ec49429 100644 --- a/pkg/models/preferences.go +++ b/pkg/models/preferences.go @@ -9,20 +9,18 @@ var ( ErrPreferenceNotFound = errors.New("Preference not found") ) -type Preference struct { - Id int64 - PrefId int64 - PrefType string - PrefData map[string]interface{} +type Preferences struct { + Id int64 + PrefId int64 + PrefType string + PrefData map[string]interface{} } // --------------------- // COMMANDS -type SavePreferenceCommand struct { - - PrefData map[string]interface{} `json:"prefData"` - PrefId int64 `json:"-"` +type SavePreferencesCommand struct { + PrefData map[string]interface{} `json:"prefData" binding:"Required"` + PrefId int64 `json:"-"` PrefType string `json:"-"` - } diff --git a/pkg/services/sqlstore/preferences.go b/pkg/services/sqlstore/preferences.go new file mode 100644 index 00000000000..756048360ec --- /dev/null +++ b/pkg/services/sqlstore/preferences.go @@ -0,0 +1,37 @@ +package sqlstore + +import ( + "github.com/grafana/grafana/pkg/bus" + m "github.com/grafana/grafana/pkg/models" +) + +func init() { + bus.AddHandler("sql", SavePreferences) +} + +func SavePreferences(cmd *m.SavePreferencesCommand) error { + return inTransaction2(func(sess *session) error { + + sql := `SELECT * FROM preferences WHERE pref_id = ? ` + + `AND pref_type = ?` + + var prefResults = make([]m.Preferences, 0) + + resultsErr := sess.Sql(sql, cmd.PrefId, cmd.PrefType).Find(&prefResults) + + if resultsErr != nil { + return resultsErr + } + + var matchedPref m.Preferences + matchedPref = prefResults[0] + matchedPref.PrefData = cmd.PrefData + affectedRows, updateErr := sess.Id(matchedPref.Id).Update(&matchedPref) + + if affectedRows == 0 { + return m.ErrPreferenceNotFound + } + + return updateErr + }) +} diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go index a5c992d10b6..96b8c24b8fc 100644 --- a/pkg/services/sqlstore/user.go +++ b/pkg/services/sqlstore/user.go @@ -11,7 +11,6 @@ import ( m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" - "github.com/grafana/grafana/pkg/log" ) func init() { @@ -28,7 +27,6 @@ func init() { bus.AddHandler("sql", DeleteUser) bus.AddHandler("sql", SetUsingOrg) bus.AddHandler("sql", UpdateUserPermissions) - bus.AddHandler("sql", SaveUserPreferences) } func getOrgIdForNewUser(cmd *m.CreateUserCommand, sess *session) (int64, error) { @@ -348,26 +346,3 @@ func UpdateUserPermissions(cmd *m.UpdateUserPermissionsCommand) error { return err }) } - -func SaveUserPreferences(cmd *m.SavePreferenceCommand) error { - return inTransaction2(func(sess *session) error { - - log.Info("%v", cmd) - - pref := m.Preference{ - PrefId: cmd.PrefId, - PrefType: cmd.PrefType, - PrefData: cmd.PrefData, - } - - sess.Table("preferences").Where("pref_id", pref.PrefId).And("pref_type", pref.PrefType) - - if _, err := sess.Update(&pref); err != nil { - return err - } - - log.Info("%v", pref) - - return nil - }) -} diff --git a/public/app/features/profile/partials/preferences.html b/public/app/features/profile/partials/preferences.html index 093163dde32..39cf64957cb 100644 --- a/public/app/features/profile/partials/preferences.html +++ b/public/app/features/profile/partials/preferences.html @@ -9,17 +9,17 @@
Home Dashboard - +
Time Range - +
Theme - +
diff --git a/public/app/features/profile/preferencesCtrl.js b/public/app/features/profile/preferencesCtrl.js index 00e7d59182c..f9d1e4da4f0 100644 --- a/public/app/features/profile/preferencesCtrl.js +++ b/public/app/features/profile/preferencesCtrl.js @@ -9,14 +9,14 @@ function (angular) { module.controller('PreferencesCtrl', function($scope, backendSrv, $location) { - $scope.command = {}; + $scope.prefData = {}; $scope.setUserPreferences = function() { if (!$scope.userForm.$valid) { return; } console.log($scope.command); - backendSrv.put('/api/user/prefs', $scope.command).then(function() { + backendSrv.put('/api/user/prefs', { prefData : $scope.prefData }).then(function() { $location.path("profile"); }); }; From 8f42bec270428bd1714258dd7cd0d8e60d7d87af Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Sun, 6 Mar 2016 11:42:15 -0800 Subject: [PATCH 04/54] Implemented GetUserPreferences API --- pkg/api/api.go | 2 +- pkg/api/preferences.go | 11 +++++++++++ pkg/models/preferences.go | 19 +++++++++++++++++++ pkg/services/sqlstore/preferences.go | 22 ++++++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index a3d206c4956..c02629ddd8f 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -96,7 +96,7 @@ func Register(r *macaron.Macaron) { r.Delete("/stars/dashboard/:id", wrap(UnstarDashboard)) r.Put("/password", bind(m.ChangeUserPasswordCommand{}), wrap(ChangeUserPassword)) r.Get("/quotas", wrap(GetUserQuotas)) - r.Put("/prefs", bind(m.SavePreferencesCommand{}), wrap(SaveUserPreferences)) + r.Combo("/prefs").Get(GetUserPreferences).Put(bind(m.SavePreferencesCommand{}), wrap(SaveUserPreferences)) }) // users (admin permission required) diff --git a/pkg/api/preferences.go b/pkg/api/preferences.go index 849ed4aa8aa..3a72467acb0 100644 --- a/pkg/api/preferences.go +++ b/pkg/api/preferences.go @@ -19,3 +19,14 @@ func SaveUserPreferences(c *middleware.Context, cmd m.SavePreferencesCommand) Re return ApiSuccess("User preferences saved") } + +func GetUserPreferences(c *middleware.Context) Response { + + query := m.GetPreferencesQuery{PrefId: c.UserId, PrefType: `user`} + + if err := bus.Dispatch(&query); err != nil { + return ApiError(500, "Failed to get user", err) + } + + return Json(200, query.Result) +} diff --git a/pkg/models/preferences.go b/pkg/models/preferences.go index 8476ec49429..d54bbcda9bc 100644 --- a/pkg/models/preferences.go +++ b/pkg/models/preferences.go @@ -16,6 +16,16 @@ type Preferences struct { PrefData map[string]interface{} } +// --------------------- +// QUERIES + +type GetPreferencesQuery struct { + PrefId int64 + PrefType string + + Result PreferencesDTO +} + // --------------------- // COMMANDS @@ -24,3 +34,12 @@ type SavePreferencesCommand struct { PrefId int64 `json:"-"` PrefType string `json:"-"` } + +// ---------------------- +// DTO & Projections + +type PreferencesDTO struct { + PrefId int64 `json:"prefId"` + PrefType string `json:"prefType"` + PrefData map[string]interface{} `json:"prefData"` +} diff --git a/pkg/services/sqlstore/preferences.go b/pkg/services/sqlstore/preferences.go index 756048360ec..7d210749a7d 100644 --- a/pkg/services/sqlstore/preferences.go +++ b/pkg/services/sqlstore/preferences.go @@ -6,9 +6,31 @@ import ( ) func init() { + bus.AddHandler("sql", GetPreferences) bus.AddHandler("sql", SavePreferences) } +func GetPreferences(query *m.GetPreferencesQuery) error { + + sql := `SELECT * FROM preferences WHERE pref_id = ? ` + + `AND pref_type = ?` + + var prefResults = make([]m.Preferences, 0) + + resultsErr := x.Sql(sql, query.PrefId, query.PrefType).Find(&prefResults) + + if resultsErr != nil { + return resultsErr + } + query.Result = m.PreferencesDTO{ + PrefId: prefResults[0].PrefId, + PrefType: prefResults[0].PrefType, + PrefData: prefResults[0].PrefData, + } + + return nil +} + func SavePreferences(cmd *m.SavePreferencesCommand) error { return inTransaction2(func(sess *session) error { From 9c8d508247e1c3b048edd6b68248ab5859d5b653 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Sun, 6 Mar 2016 12:32:22 -0800 Subject: [PATCH 05/54] Made API handling better, removed unused components --- pkg/api/preferences.go | 13 ++++++-- pkg/models/preferences.go | 4 +-- pkg/services/sqlstore/preferences.go | 33 ++++++++++++------- .../app/core/components/sidemenu/sidemenu.ts | 1 - public/app/core/routes/routes.ts | 4 --- public/app/features/all.js | 1 - .../profile/partials/preferences.html | 32 ------------------ .../features/profile/partials/profile.html | 2 +- .../app/features/profile/preferencesCtrl.js | 25 -------------- 9 files changed, 35 insertions(+), 80 deletions(-) delete mode 100644 public/app/features/profile/partials/preferences.html delete mode 100644 public/app/features/profile/preferencesCtrl.js diff --git a/pkg/api/preferences.go b/pkg/api/preferences.go index 3a72467acb0..b39d0446a9b 100644 --- a/pkg/api/preferences.go +++ b/pkg/api/preferences.go @@ -20,13 +20,20 @@ func SaveUserPreferences(c *middleware.Context, cmd m.SavePreferencesCommand) Re } -func GetUserPreferences(c *middleware.Context) Response { +// GET /api/user/prefs +func GetUserPreferences(c *middleware.Context) { query := m.GetPreferencesQuery{PrefId: c.UserId, PrefType: `user`} if err := bus.Dispatch(&query); err != nil { - return ApiError(500, "Failed to get user", err) + c.JsonApiErr(500, "Failed to get preferences for user", err) } - return Json(200, query.Result) + dto := m.PreferencesDTO{ + PrefId: query.Result.PrefId, + PrefType: query.Result.PrefType, + PrefData: query.Result.PrefData, + } + + c.JSON(200, dto) } diff --git a/pkg/models/preferences.go b/pkg/models/preferences.go index d54bbcda9bc..237e577dbc9 100644 --- a/pkg/models/preferences.go +++ b/pkg/models/preferences.go @@ -6,7 +6,7 @@ import ( // Typed errors var ( - ErrPreferenceNotFound = errors.New("Preference not found") + ErrPreferencesNotFound = errors.New("Preferences not found") ) type Preferences struct { @@ -23,7 +23,7 @@ type GetPreferencesQuery struct { PrefId int64 PrefType string - Result PreferencesDTO + Result *Preferences } // --------------------- diff --git a/pkg/services/sqlstore/preferences.go b/pkg/services/sqlstore/preferences.go index 7d210749a7d..b03463f8089 100644 --- a/pkg/services/sqlstore/preferences.go +++ b/pkg/services/sqlstore/preferences.go @@ -22,10 +22,11 @@ func GetPreferences(query *m.GetPreferencesQuery) error { if resultsErr != nil { return resultsErr } - query.Result = m.PreferencesDTO{ - PrefId: prefResults[0].PrefId, - PrefType: prefResults[0].PrefType, - PrefData: prefResults[0].PrefData, + + if len(prefResults) > 0 { + query.Result = &prefResults[0] + } else { + query.Result = new(m.Preferences) } return nil @@ -45,15 +46,25 @@ func SavePreferences(cmd *m.SavePreferencesCommand) error { return resultsErr } - var matchedPref m.Preferences - matchedPref = prefResults[0] - matchedPref.PrefData = cmd.PrefData - affectedRows, updateErr := sess.Id(matchedPref.Id).Update(&matchedPref) + var savePref m.Preferences + var affectedRows int64 + var saveErr error - if affectedRows == 0 { - return m.ErrPreferenceNotFound + if len(prefResults) == 0 { + savePref.PrefId = cmd.PrefId + savePref.PrefType = cmd.PrefType + savePref.PrefData = cmd.PrefData + affectedRows, saveErr = sess.Insert(&savePref) + } else { + savePref = prefResults[0] + savePref.PrefData = cmd.PrefData + affectedRows, saveErr = sess.Id(savePref.Id).Update(&savePref) } - return updateErr + if affectedRows == 0 { + return m.ErrPreferencesNotFound + } + + return saveErr }) } diff --git a/public/app/core/components/sidemenu/sidemenu.ts b/public/app/core/components/sidemenu/sidemenu.ts index f68edf8fca5..4c0e100f85c 100644 --- a/public/app/core/components/sidemenu/sidemenu.ts +++ b/public/app/core/components/sidemenu/sidemenu.ts @@ -39,7 +39,6 @@ export class SideMenuCtrl { this.orgMenu = [ {section: 'You', cssClass: 'dropdown-menu-title'}, {text: 'Profile', url: this.getUrl('/profile')}, - {text: 'Preferences', url: this.getUrl('/preferences')}, ]; if (this.isSignedIn) { diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts index 334800c22f5..6e3af683c82 100644 --- a/public/app/core/routes/routes.ts +++ b/public/app/core/routes/routes.ts @@ -89,10 +89,6 @@ function setupAngularRoutes($routeProvider, $locationProvider) { templateUrl: 'public/app/features/profile/partials/profile.html', controller : 'ProfileCtrl', }) - .when('/preferences', { - templateUrl: 'public/app/features/profile/partials/preferences.html', - controller : 'PreferencesCtrl', - }) .when('/profile/password', { templateUrl: 'public/app/features/profile/partials/password.html', controller : 'ChangePasswordCtrl', diff --git a/public/app/features/all.js b/public/app/features/all.js index 94da3809ae7..c110bcff7cd 100644 --- a/public/app/features/all.js +++ b/public/app/features/all.js @@ -10,6 +10,5 @@ define([ './profile/profileCtrl', './profile/changePasswordCtrl', './profile/selectOrgCtrl', - './profile/preferencesCtrl', './styleguide/styleguide', ], function () {}); diff --git a/public/app/features/profile/partials/preferences.html b/public/app/features/profile/partials/preferences.html deleted file mode 100644 index 39cf64957cb..00000000000 --- a/public/app/features/profile/partials/preferences.html +++ /dev/null @@ -1,32 +0,0 @@ - - - -
- - - -
- Home Dashboard - -
- -
- Time Range - -
- -
- Theme - -
- -
- - Cancel -
- - -
- diff --git a/public/app/features/profile/partials/profile.html b/public/app/features/profile/partials/profile.html index dd5afebeaac..042344eeac6 100644 --- a/public/app/features/profile/partials/profile.html +++ b/public/app/features/profile/partials/profile.html @@ -7,7 +7,7 @@
-

Information

+

Preferences

Name diff --git a/public/app/features/profile/preferencesCtrl.js b/public/app/features/profile/preferencesCtrl.js deleted file mode 100644 index f9d1e4da4f0..00000000000 --- a/public/app/features/profile/preferencesCtrl.js +++ /dev/null @@ -1,25 +0,0 @@ -define([ - 'angular', - 'app/core/config', -], -function (angular) { - 'use strict'; - - var module = angular.module('grafana.controllers'); - - module.controller('PreferencesCtrl', function($scope, backendSrv, $location) { - - $scope.prefData = {}; - - $scope.setUserPreferences = function() { - if (!$scope.userForm.$valid) { return; } - - console.log($scope.command); - - backendSrv.put('/api/user/prefs', { prefData : $scope.prefData }).then(function() { - $location.path("profile"); - }); - }; - - }); -}); From 43b474143cf1c55264ef033299aa253a7c98e618 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Fri, 11 Mar 2016 06:30:05 -0800 Subject: [PATCH 06/54] Preferences model updated --- pkg/api/api.go | 3 +- pkg/api/preferences.go | 23 +++++++------- pkg/models/preferences.go | 30 +++++++++++-------- .../sqlstore/migrations/preferences_mig.go | 9 ++++-- pkg/services/sqlstore/preferences.go | 20 ++++++------- 5 files changed, 48 insertions(+), 37 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index c02629ddd8f..bbf0fc522c9 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -96,7 +96,6 @@ func Register(r *macaron.Macaron) { r.Delete("/stars/dashboard/:id", wrap(UnstarDashboard)) r.Put("/password", bind(m.ChangeUserPasswordCommand{}), wrap(ChangeUserPassword)) r.Get("/quotas", wrap(GetUserQuotas)) - r.Combo("/prefs").Get(GetUserPreferences).Put(bind(m.SavePreferencesCommand{}), wrap(SaveUserPreferences)) }) // users (admin permission required) @@ -165,6 +164,8 @@ func Register(r *macaron.Macaron) { r.Delete("/:id", wrap(DeleteApiKey)) }, reqOrgAdmin) + r.Combo("/preferences").Get(GetPreferences).Put(bind(m.SavePreferencesCommand{}), wrap(SavePreferences)) + // Data sources r.Group("/datasources", func() { r.Get("/", GetDataSources) diff --git a/pkg/api/preferences.go b/pkg/api/preferences.go index b39d0446a9b..c95bcd46aa2 100644 --- a/pkg/api/preferences.go +++ b/pkg/api/preferences.go @@ -7,32 +7,33 @@ import ( ) // PUT /api/user/prefs -func SaveUserPreferences(c *middleware.Context, cmd m.SavePreferencesCommand) Response { +func SavePreferences(c *middleware.Context, cmd m.SavePreferencesCommand) Response { - cmd.PrefId = c.UserId - cmd.PrefType = `user` + cmd.UserId = c.UserId + cmd.OrgId = c.OrgId if err := bus.Dispatch(&cmd); err != nil { - return ApiError(500, "Failed to saved user preferences", err) + return ApiError(500, "Failed to saved preferences", err) } - return ApiSuccess("User preferences saved") + return ApiSuccess("Preferences saved") } // GET /api/user/prefs -func GetUserPreferences(c *middleware.Context) { +func GetPreferences(c *middleware.Context) { - query := m.GetPreferencesQuery{PrefId: c.UserId, PrefType: `user`} + query := m.GetPreferencesQuery{UserId: c.UserId, OrgId: c.OrgId} if err := bus.Dispatch(&query); err != nil { - c.JsonApiErr(500, "Failed to get preferences for user", err) + c.JsonApiErr(500, "Failed to get preferences", err) } dto := m.PreferencesDTO{ - PrefId: query.Result.PrefId, - PrefType: query.Result.PrefType, - PrefData: query.Result.PrefData, + Id: query.Result.Id, + UserId: query.Result.UserId, + OrgId: query.Result.OrgId, + Preference: query.Result.Preference, } c.JSON(200, dto) diff --git a/pkg/models/preferences.go b/pkg/models/preferences.go index 237e577dbc9..f2a26da4ba9 100644 --- a/pkg/models/preferences.go +++ b/pkg/models/preferences.go @@ -2,6 +2,7 @@ package models import ( "errors" + "time" ) // Typed errors @@ -10,18 +11,22 @@ var ( ) type Preferences struct { - Id int64 - PrefId int64 - PrefType string - PrefData map[string]interface{} + Id int64 + OrgId int64 + UserId int64 + Version int + Preference map[string]interface{} + Created time.Time + Updated time.Time } // --------------------- // QUERIES type GetPreferencesQuery struct { - PrefId int64 - PrefType string + Id int64 + OrgId int64 + UserId int64 Result *Preferences } @@ -30,16 +35,17 @@ type GetPreferencesQuery struct { // COMMANDS type SavePreferencesCommand struct { - PrefData map[string]interface{} `json:"prefData" binding:"Required"` - PrefId int64 `json:"-"` - PrefType string `json:"-"` + Preference map[string]interface{} `json:"Preference" binding:"Required"` + UserId int64 `json:"-"` + OrgId int64 `json:"-"` } // ---------------------- // DTO & Projections type PreferencesDTO struct { - PrefId int64 `json:"prefId"` - PrefType string `json:"prefType"` - PrefData map[string]interface{} `json:"prefData"` + Id int64 `json:"Id"` + UserId int64 `json:"UserId"` + OrgId int64 `json:"OrgId"` + Preference map[string]interface{} `json:"Preference"` } diff --git a/pkg/services/sqlstore/migrations/preferences_mig.go b/pkg/services/sqlstore/migrations/preferences_mig.go index 0ce01857b75..c14e47a8733 100644 --- a/pkg/services/sqlstore/migrations/preferences_mig.go +++ b/pkg/services/sqlstore/migrations/preferences_mig.go @@ -8,9 +8,12 @@ func addPreferencesMigrations(mg *Migrator) { Name: "preferences", Columns: []*Column{ {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, - {Name: "pref_id", Type: DB_Int, Nullable: false}, - {Name: "pref_type", Type: DB_NVarchar, Length: 255, Nullable: false}, - {Name: "pref_data", Type: DB_Text, Nullable: false}, + {Name: "org_id", Type: DB_Int, Nullable: false}, + {Name: "user_id", Type: DB_NVarchar, Length: 255, Nullable: false}, + {Name: "version", Type: DB_Int, Nullable: false}, + {Name: "preference", Type: DB_Text, Nullable: false}, + {Name: "created", Type: DB_DateTime, Nullable: false}, + {Name: "updated", Type: DB_DateTime, Nullable: false}, }, } diff --git a/pkg/services/sqlstore/preferences.go b/pkg/services/sqlstore/preferences.go index b03463f8089..9d395d95304 100644 --- a/pkg/services/sqlstore/preferences.go +++ b/pkg/services/sqlstore/preferences.go @@ -12,12 +12,12 @@ func init() { func GetPreferences(query *m.GetPreferencesQuery) error { - sql := `SELECT * FROM preferences WHERE pref_id = ? ` + - `AND pref_type = ?` + sql := `SELECT * FROM preferences WHERE user_id = ? ` + + `AND org_id = ?` var prefResults = make([]m.Preferences, 0) - resultsErr := x.Sql(sql, query.PrefId, query.PrefType).Find(&prefResults) + resultsErr := x.Sql(sql, query.UserId, query.OrgId).Find(&prefResults) if resultsErr != nil { return resultsErr @@ -35,12 +35,12 @@ func GetPreferences(query *m.GetPreferencesQuery) error { func SavePreferences(cmd *m.SavePreferencesCommand) error { return inTransaction2(func(sess *session) error { - sql := `SELECT * FROM preferences WHERE pref_id = ? ` + - `AND pref_type = ?` + sql := `SELECT * FROM preferences WHERE user_id = ? ` + + `AND org_id = ?` var prefResults = make([]m.Preferences, 0) - resultsErr := sess.Sql(sql, cmd.PrefId, cmd.PrefType).Find(&prefResults) + resultsErr := sess.Sql(sql, cmd.UserId, cmd.OrgId).Find(&prefResults) if resultsErr != nil { return resultsErr @@ -51,13 +51,13 @@ func SavePreferences(cmd *m.SavePreferencesCommand) error { var saveErr error if len(prefResults) == 0 { - savePref.PrefId = cmd.PrefId - savePref.PrefType = cmd.PrefType - savePref.PrefData = cmd.PrefData + savePref.UserId = cmd.UserId + savePref.OrgId = cmd.OrgId + savePref.Preference = cmd.Preference affectedRows, saveErr = sess.Insert(&savePref) } else { savePref = prefResults[0] - savePref.PrefData = cmd.PrefData + savePref.Preference = cmd.Preference affectedRows, saveErr = sess.Id(savePref.Id).Update(&savePref) } From 0bf721a74ce3cf102142889976fe7f6ff9bc9bda Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Sun, 13 Mar 2016 23:57:38 -0700 Subject: [PATCH 07/54] Able to save home dashboard --- .../features/dashboard/dashnav/dashnav.html | 1 + .../app/features/dashboard/dashnav/dashnav.ts | 22 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/public/app/features/dashboard/dashnav/dashnav.html b/public/app/features/dashboard/dashnav/dashnav.html index 19112f77a43..e8e1ddc9011 100644 --- a/public/app/features/dashboard/dashnav/dashnav.html +++ b/public/app/features/dashboard/dashnav/dashnav.html @@ -48,6 +48,7 @@
  • View JSON
  • Make Editable
  • Save As...
  • +
  • Save As Home
  • Delete dashboard
  • diff --git a/public/app/features/dashboard/dashnav/dashnav.ts b/public/app/features/dashboard/dashnav/dashnav.ts index dcc2f40d0dc..44ab8710144 100644 --- a/public/app/features/dashboard/dashnav/dashnav.ts +++ b/public/app/features/dashboard/dashnav/dashnav.ts @@ -7,7 +7,7 @@ import angular from 'angular'; export class DashNavCtrl { /** @ngInject */ - constructor($scope, $rootScope, alertSrv, $location, playlistSrv, backendSrv, $timeout) { + constructor($scope, $rootScope, alertSrv, $location, playlistSrv, backendSrv, contextSrv, $timeout) { $scope.init = function() { $scope.onAppEvent('save-dashboard', $scope.saveDashboard); @@ -103,6 +103,26 @@ export class DashNavCtrl { }, $scope.handleSaveDashError); }; + $scope.saveDashboardAsHome = function() { + var orgId = 'org-' + contextSrv.user.orgId; + backendSrv.get('/api/preferences').then(function(prefs) { + + // Checking if the preferences already exists or not + if (prefs.userId === 0 && prefs.orgId === 0 && prefs.preference === null) { + prefs.preference = {}; + } + if (prefs.preference == null) { + prefs.preference = { + home_dashboard_id: $scope.dashboard.id + }; + } else { + var orgPrefs = prefs.preference; + orgPrefs.home_dashboard = $scope.dashboard.id; + } + backendSrv.put('api/preferences', prefs); + }); + }; + $scope.handleSaveDashError = function(err) { if (err.data && err.data.status === "version-mismatch") { err.isHandled = true; From e371e03696acde9151f8594a740e9682e6fa90d5 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Mon, 14 Mar 2016 03:12:52 -0700 Subject: [PATCH 08/54] Able to save preference version, created, updated fields --- pkg/api/api.go | 2 +- pkg/api/preferences.go | 6 +++--- pkg/models/preferences.go | 12 ++++++------ .../sqlstore/migrations/preferences_mig.go | 6 +++--- pkg/services/sqlstore/preferences.go | 16 ++++++++++++++++ 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index bbf0fc522c9..d58f54b1783 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -164,7 +164,7 @@ func Register(r *macaron.Macaron) { r.Delete("/:id", wrap(DeleteApiKey)) }, reqOrgAdmin) - r.Combo("/preferences").Get(GetPreferences).Put(bind(m.SavePreferencesCommand{}), wrap(SavePreferences)) + r.Combo("/preferences").Get(GetPreferences).Put(bind(m.SavePreferencesCommand{}), wrap(SavePreferences)) // Data sources r.Group("/datasources", func() { diff --git a/pkg/api/preferences.go b/pkg/api/preferences.go index c95bcd46aa2..f7a8028fd6d 100644 --- a/pkg/api/preferences.go +++ b/pkg/api/preferences.go @@ -10,10 +10,10 @@ import ( func SavePreferences(c *middleware.Context, cmd m.SavePreferencesCommand) Response { cmd.UserId = c.UserId - cmd.OrgId = c.OrgId + cmd.OrgId = c.OrgId if err := bus.Dispatch(&cmd); err != nil { - return ApiError(500, "Failed to saved preferences", err) + return ApiError(500, "Failed to save preferences", err) } return ApiSuccess("Preferences saved") @@ -32,7 +32,7 @@ func GetPreferences(c *middleware.Context) { dto := m.PreferencesDTO{ Id: query.Result.Id, UserId: query.Result.UserId, - OrgId: query.Result.OrgId, + OrgId: query.Result.OrgId, Preference: query.Result.Preference, } diff --git a/pkg/models/preferences.go b/pkg/models/preferences.go index f2a26da4ba9..3d6ad44a515 100644 --- a/pkg/models/preferences.go +++ b/pkg/models/preferences.go @@ -2,7 +2,7 @@ package models import ( "errors" - "time" + "time" ) // Typed errors @@ -14,10 +14,10 @@ type Preferences struct { Id int64 OrgId int64 UserId int64 - Version int + Version int Preference map[string]interface{} - Created time.Time - Updated time.Time + Created time.Time + Updated time.Time } // --------------------- @@ -26,7 +26,7 @@ type Preferences struct { type GetPreferencesQuery struct { Id int64 OrgId int64 - UserId int64 + UserId int64 Result *Preferences } @@ -46,6 +46,6 @@ type SavePreferencesCommand struct { type PreferencesDTO struct { Id int64 `json:"Id"` UserId int64 `json:"UserId"` - OrgId int64 `json:"OrgId"` + OrgId int64 `json:"OrgId"` Preference map[string]interface{} `json:"Preference"` } diff --git a/pkg/services/sqlstore/migrations/preferences_mig.go b/pkg/services/sqlstore/migrations/preferences_mig.go index c14e47a8733..46a91307a5d 100644 --- a/pkg/services/sqlstore/migrations/preferences_mig.go +++ b/pkg/services/sqlstore/migrations/preferences_mig.go @@ -10,10 +10,10 @@ func addPreferencesMigrations(mg *Migrator) { {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "org_id", Type: DB_Int, Nullable: false}, {Name: "user_id", Type: DB_NVarchar, Length: 255, Nullable: false}, - {Name: "version", Type: DB_Int, Nullable: false}, + {Name: "version", Type: DB_Int, Nullable: false}, {Name: "preference", Type: DB_Text, Nullable: false}, - {Name: "created", Type: DB_DateTime, Nullable: false}, - {Name: "updated", Type: DB_DateTime, Nullable: false}, + {Name: "created", Type: DB_DateTime, Nullable: false}, + {Name: "updated", Type: DB_DateTime, Nullable: false}, }, } diff --git a/pkg/services/sqlstore/preferences.go b/pkg/services/sqlstore/preferences.go index 9d395d95304..ece113cf523 100644 --- a/pkg/services/sqlstore/preferences.go +++ b/pkg/services/sqlstore/preferences.go @@ -3,6 +3,7 @@ package sqlstore import ( "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" + "time" ) func init() { @@ -54,10 +55,12 @@ func SavePreferences(cmd *m.SavePreferencesCommand) error { savePref.UserId = cmd.UserId savePref.OrgId = cmd.OrgId savePref.Preference = cmd.Preference + savePref = SetPreferencesModel(savePref, false) affectedRows, saveErr = sess.Insert(&savePref) } else { savePref = prefResults[0] savePref.Preference = cmd.Preference + savePref = SetPreferencesModel(savePref, true) affectedRows, saveErr = sess.Id(savePref.Id).Update(&savePref) } @@ -68,3 +71,16 @@ func SavePreferences(cmd *m.SavePreferencesCommand) error { return saveErr }) } + +func SetPreferencesModel(pref m.Preferences, updating bool) m.Preferences { + + if updating { + pref.Version = pref.Version + 1 + } else { + pref.Version = 0 + pref.Created = time.Now() + } + pref.Updated = time.Now() + + return pref +} From a88176e06086e1e1e670f0f7b52ae3c0ced758f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 15 Mar 2016 22:49:52 +0100 Subject: [PATCH 09/54] feat(preferences): lots of refactoring and changes to #3214 --- pkg/api/dtos/prefs.go | 7 ++ pkg/api/preferences.go | 10 +-- pkg/models/preferences.go | 34 ++++---- .../sqlstore/migrations/preferences_mig.go | 19 +++-- pkg/services/sqlstore/preferences.go | 77 ++++++------------- .../features/dashboard/dashnav/dashnav.html | 2 +- .../app/features/dashboard/dashnav/dashnav.ts | 19 +---- 7 files changed, 66 insertions(+), 102 deletions(-) create mode 100644 pkg/api/dtos/prefs.go diff --git a/pkg/api/dtos/prefs.go b/pkg/api/dtos/prefs.go new file mode 100644 index 00000000000..f66068edccb --- /dev/null +++ b/pkg/api/dtos/prefs.go @@ -0,0 +1,7 @@ +package dtos + +type Preferences struct { + Theme string `json:"theme"` + HomeDashboardId int64 `json:"homeDashboardId"` + Timezone string `json:"timezone"` +} diff --git a/pkg/api/preferences.go b/pkg/api/preferences.go index f7a8028fd6d..7738d1f987a 100644 --- a/pkg/api/preferences.go +++ b/pkg/api/preferences.go @@ -1,6 +1,7 @@ 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" @@ -29,11 +30,10 @@ func GetPreferences(c *middleware.Context) { c.JsonApiErr(500, "Failed to get preferences", err) } - dto := m.PreferencesDTO{ - Id: query.Result.Id, - UserId: query.Result.UserId, - OrgId: query.Result.OrgId, - Preference: query.Result.Preference, + dto := dtos.Preferences{ + HomeDashboardId: query.Result.HomeDashboardId, + Timezone: query.Result.Timezone, + Theme: query.Result.Theme, } c.JSON(200, dto) diff --git a/pkg/models/preferences.go b/pkg/models/preferences.go index 3d6ad44a515..5163835daf0 100644 --- a/pkg/models/preferences.go +++ b/pkg/models/preferences.go @@ -11,13 +11,15 @@ var ( ) type Preferences struct { - Id int64 - OrgId int64 - UserId int64 - Version int - Preference map[string]interface{} - Created time.Time - Updated time.Time + Id int64 + OrgId int64 + UserId int64 + Version int + HomeDashboardId int64 + Timezone string + Theme string + Created time.Time + Updated time.Time } // --------------------- @@ -33,19 +35,11 @@ type GetPreferencesQuery struct { // --------------------- // COMMANDS - type SavePreferencesCommand struct { - Preference map[string]interface{} `json:"Preference" binding:"Required"` - UserId int64 `json:"-"` - OrgId int64 `json:"-"` -} + UserId int64 + OrgId int64 -// ---------------------- -// DTO & Projections - -type PreferencesDTO struct { - Id int64 `json:"Id"` - UserId int64 `json:"UserId"` - OrgId int64 `json:"OrgId"` - Preference map[string]interface{} `json:"Preference"` + HomeDashboardId int64 + Timezone string + Theme string } diff --git a/pkg/services/sqlstore/migrations/preferences_mig.go b/pkg/services/sqlstore/migrations/preferences_mig.go index 46a91307a5d..6425c3735bb 100644 --- a/pkg/services/sqlstore/migrations/preferences_mig.go +++ b/pkg/services/sqlstore/migrations/preferences_mig.go @@ -4,20 +4,27 @@ import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator" func addPreferencesMigrations(mg *Migrator) { - preferencesV1 := Table{ + mg.AddMigration("drop preferences table v2", NewDropTableMigration("preferences")) + + preferencesV2 := Table{ Name: "preferences", Columns: []*Column{ {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, - {Name: "org_id", Type: DB_Int, Nullable: false}, - {Name: "user_id", Type: DB_NVarchar, Length: 255, Nullable: false}, + {Name: "org_id", Type: DB_Int, Nullable: true}, + {Name: "user_id", Type: DB_NVarchar, Length: 255, Nullable: true}, {Name: "version", Type: DB_Int, Nullable: false}, - {Name: "preference", Type: DB_Text, Nullable: false}, + {Name: "home_dashboard_id", Type: DB_BigInt, Nullable: true}, + {Name: "timezone", Type: DB_NVarchar, Length: 50, Nullable: true}, + {Name: "theme", Type: DB_NVarchar, Length: 20, Nullable: true}, {Name: "created", Type: DB_DateTime, Nullable: false}, {Name: "updated", Type: DB_DateTime, Nullable: false}, }, + Indices: []*Index{ + {Cols: []string{"org_id"}}, + {Cols: []string{"user_id"}}, + }, } // create table - mg.AddMigration("create preferences table v1", NewAddTableMigration(preferencesV1)) - + mg.AddMigration("create preferences table v2", NewAddTableMigration(preferencesV2)) } diff --git a/pkg/services/sqlstore/preferences.go b/pkg/services/sqlstore/preferences.go index ece113cf523..dfe960d1f39 100644 --- a/pkg/services/sqlstore/preferences.go +++ b/pkg/services/sqlstore/preferences.go @@ -3,7 +3,6 @@ package sqlstore import ( "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" - "time" ) func init() { @@ -13,19 +12,15 @@ func init() { func GetPreferences(query *m.GetPreferencesQuery) error { - sql := `SELECT * FROM preferences WHERE user_id = ? ` + - `AND org_id = ?` + var prefs m.Preferences + exists, err := x.Where("org_id=? AND user_id=?", query.OrgId, query.UserId).Get(&prefs) - var prefResults = make([]m.Preferences, 0) - - resultsErr := x.Sql(sql, query.UserId, query.OrgId).Find(&prefResults) - - if resultsErr != nil { - return resultsErr + if err != nil { + return err } - if len(prefResults) > 0 { - query.Result = &prefResults[0] + if exists { + query.Result = &prefs } else { query.Result = new(m.Preferences) } @@ -36,51 +31,25 @@ func GetPreferences(query *m.GetPreferencesQuery) error { func SavePreferences(cmd *m.SavePreferencesCommand) error { return inTransaction2(func(sess *session) error { - sql := `SELECT * FROM preferences WHERE user_id = ? ` + - `AND org_id = ?` + var prefs m.Preferences + exists, err := sess.Where("org_id=? AND user_id=?", cmd.OrgId, cmd.UserId).Get(&prefs) - var prefResults = make([]m.Preferences, 0) - - resultsErr := sess.Sql(sql, cmd.UserId, cmd.OrgId).Find(&prefResults) - - if resultsErr != nil { - return resultsErr - } - - var savePref m.Preferences - var affectedRows int64 - var saveErr error - - if len(prefResults) == 0 { - savePref.UserId = cmd.UserId - savePref.OrgId = cmd.OrgId - savePref.Preference = cmd.Preference - savePref = SetPreferencesModel(savePref, false) - affectedRows, saveErr = sess.Insert(&savePref) + if !exists { + prefs = m.Preferences{ + UserId: cmd.UserId, + OrgId: cmd.OrgId, + HomeDashboardId: cmd.HomeDashboardId, + Timezone: cmd.Timezone, + Theme: cmd.Theme, + } + _, err = sess.Insert(&prefs) + return err } else { - savePref = prefResults[0] - savePref.Preference = cmd.Preference - savePref = SetPreferencesModel(savePref, true) - affectedRows, saveErr = sess.Id(savePref.Id).Update(&savePref) + prefs.HomeDashboardId = cmd.HomeDashboardId + prefs.Timezone = cmd.Timezone + prefs.Theme = cmd.Theme + _, err = sess.Id(prefs.Id).Update(&prefs) + return err } - - if affectedRows == 0 { - return m.ErrPreferencesNotFound - } - - return saveErr }) } - -func SetPreferencesModel(pref m.Preferences, updating bool) m.Preferences { - - if updating { - pref.Version = pref.Version + 1 - } else { - pref.Version = 0 - pref.Created = time.Now() - } - pref.Updated = time.Now() - - return pref -} diff --git a/public/app/features/dashboard/dashnav/dashnav.html b/public/app/features/dashboard/dashnav/dashnav.html index e8e1ddc9011..aec626bfe2a 100644 --- a/public/app/features/dashboard/dashnav/dashnav.html +++ b/public/app/features/dashboard/dashnav/dashnav.html @@ -48,7 +48,7 @@
  • View JSON
  • Make Editable
  • Save As...
  • -
  • Save As Home
  • +
  • Set As Home
  • Delete dashboard
  • diff --git a/public/app/features/dashboard/dashnav/dashnav.ts b/public/app/features/dashboard/dashnav/dashnav.ts index 4960b20397a..c6faaf9a738 100644 --- a/public/app/features/dashboard/dashnav/dashnav.ts +++ b/public/app/features/dashboard/dashnav/dashnav.ts @@ -104,22 +104,9 @@ export class DashNavCtrl { }; $scope.saveDashboardAsHome = function() { - var orgId = 'org-' + contextSrv.user.orgId; - backendSrv.get('/api/preferences').then(function(prefs) { - - // Checking if the preferences already exists or not - if (prefs.userId === 0 && prefs.orgId === 0 && prefs.preference === null) { - prefs.preference = {}; - } - if (prefs.preference == null) { - prefs.preference = { - home_dashboard_id: $scope.dashboard.id - }; - } else { - var orgPrefs = prefs.preference; - orgPrefs.home_dashboard = $scope.dashboard.id; - } - backendSrv.put('api/preferences', prefs); + // TODO: this backend method needs to be implemented + backendSrv.post('/api/preferences/set-home-dash', { + dashboardId: $scope.dashboard.id }); }; From 749fd618a949ff5a49b1fe293710fffcf3213f03 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 16 Mar 2016 23:22:27 -0700 Subject: [PATCH 10/54] Saving home dashboard id in table --- pkg/api/api.go | 7 ++++++- pkg/api/preferences.go | 18 ++++++++++++++++-- pkg/models/preferences.go | 6 +++--- pkg/services/sqlstore/preferences.go | 5 +++++ 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 45ef5e9030d..e360a43f78b 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -160,7 +160,12 @@ func Register(r *macaron.Macaron) { r.Delete("/:id", wrap(DeleteApiKey)) }, reqOrgAdmin) - r.Combo("/preferences").Get(GetPreferences).Put(bind(m.SavePreferencesCommand{}), wrap(SavePreferences)) + // Preferences + r.Group("/preferences", func() { + r.Get("/", wrap(GetPreferences)) + r.Put("/", bind(m.SavePreferencesCommand{}), wrap(SavePreferences)) + r.Post("/set-home-dash", bind(m.SavePreferencesCommand{}), wrap(SetHomeDashboard)) + }) // Data sources r.Group("/datasources", func() { diff --git a/pkg/api/preferences.go b/pkg/api/preferences.go index 7738d1f987a..a3d398dd556 100644 --- a/pkg/api/preferences.go +++ b/pkg/api/preferences.go @@ -7,7 +7,7 @@ import ( m "github.com/grafana/grafana/pkg/models" ) -// PUT /api/user/prefs +// PUT /api/preferences func SavePreferences(c *middleware.Context, cmd m.SavePreferencesCommand) Response { cmd.UserId = c.UserId @@ -21,7 +21,7 @@ func SavePreferences(c *middleware.Context, cmd m.SavePreferencesCommand) Respon } -// GET /api/user/prefs +// GET /api/preferences func GetPreferences(c *middleware.Context) { query := m.GetPreferencesQuery{UserId: c.UserId, OrgId: c.OrgId} @@ -38,3 +38,17 @@ func GetPreferences(c *middleware.Context) { c.JSON(200, dto) } + +// POST /api/preferences/set-home-dash +func SetHomeDashboard(c *middleware.Context, cmd m.SavePreferencesCommand) Response { + + cmd.UserId = c.UserId + cmd.OrgId = c.OrgId + + if err := bus.Dispatch(&cmd); err != nil { + return ApiError(500, "Failed to set home dashboard", err) + } + + return ApiSuccess("Home dashboard set") + +} diff --git a/pkg/models/preferences.go b/pkg/models/preferences.go index 5163835daf0..ac3dcf1481d 100644 --- a/pkg/models/preferences.go +++ b/pkg/models/preferences.go @@ -39,7 +39,7 @@ type SavePreferencesCommand struct { UserId int64 OrgId int64 - HomeDashboardId int64 - Timezone string - Theme string + HomeDashboardId int64 `json:"dashboardId"` + Timezone string `json:"timezone"` + Theme string `json:"theme"` } diff --git a/pkg/services/sqlstore/preferences.go b/pkg/services/sqlstore/preferences.go index dfe960d1f39..f744b2eb50f 100644 --- a/pkg/services/sqlstore/preferences.go +++ b/pkg/services/sqlstore/preferences.go @@ -1,6 +1,7 @@ package sqlstore import ( + "time" "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" ) @@ -41,6 +42,8 @@ func SavePreferences(cmd *m.SavePreferencesCommand) error { HomeDashboardId: cmd.HomeDashboardId, Timezone: cmd.Timezone, Theme: cmd.Theme, + Created: time.Now(), + Updated: time.Now(), } _, err = sess.Insert(&prefs) return err @@ -48,6 +51,8 @@ func SavePreferences(cmd *m.SavePreferencesCommand) error { prefs.HomeDashboardId = cmd.HomeDashboardId prefs.Timezone = cmd.Timezone prefs.Theme = cmd.Theme + prefs.Updated = time.Now() + prefs.Version += 1 _, err = sess.Id(prefs.Id).Update(&prefs) return err } From 9dd6aefcec34a2fb8770e54c170be7c6f7917a60 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 16 Mar 2016 23:35:06 -0700 Subject: [PATCH 11/54] Minor corrections --- pkg/api/api.go | 12 ++++++------ pkg/api/preferences.go | 12 ++++++------ pkg/models/preferences.go | 6 +++--- pkg/services/sqlstore/preferences.go | 10 +++++----- public/app/features/dashboard/dashnav/dashnav.ts | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index e360a43f78b..0abc4bd6026 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -160,12 +160,12 @@ func Register(r *macaron.Macaron) { r.Delete("/:id", wrap(DeleteApiKey)) }, reqOrgAdmin) - // Preferences - r.Group("/preferences", func() { - r.Get("/", wrap(GetPreferences)) - r.Put("/", bind(m.SavePreferencesCommand{}), wrap(SavePreferences)) - r.Post("/set-home-dash", bind(m.SavePreferencesCommand{}), wrap(SetHomeDashboard)) - }) + // Preferences + r.Group("/preferences", func() { + r.Get("/", wrap(GetPreferences)) + r.Put("/", bind(m.SavePreferencesCommand{}), wrap(SavePreferences)) + r.Post("/set-home-dash", bind(m.SavePreferencesCommand{}), wrap(SetHomeDashboard)) + }) // Data sources r.Group("/datasources", func() { diff --git a/pkg/api/preferences.go b/pkg/api/preferences.go index a3d398dd556..74419492d09 100644 --- a/pkg/api/preferences.go +++ b/pkg/api/preferences.go @@ -42,13 +42,13 @@ func GetPreferences(c *middleware.Context) { // POST /api/preferences/set-home-dash func SetHomeDashboard(c *middleware.Context, cmd m.SavePreferencesCommand) Response { - cmd.UserId = c.UserId - cmd.OrgId = c.OrgId + cmd.UserId = c.UserId + cmd.OrgId = c.OrgId - if err := bus.Dispatch(&cmd); err != nil { - return ApiError(500, "Failed to set home dashboard", err) - } + if err := bus.Dispatch(&cmd); err != nil { + return ApiError(500, "Failed to set home dashboard", err) + } - return ApiSuccess("Home dashboard set") + return ApiSuccess("Home dashboard set") } diff --git a/pkg/models/preferences.go b/pkg/models/preferences.go index ac3dcf1481d..523a3bfc83f 100644 --- a/pkg/models/preferences.go +++ b/pkg/models/preferences.go @@ -39,7 +39,7 @@ type SavePreferencesCommand struct { UserId int64 OrgId int64 - HomeDashboardId int64 `json:"dashboardId"` - Timezone string `json:"timezone"` - Theme string `json:"theme"` + HomeDashboardId int64 `json:"homeDashboardId"` + Timezone string `json:"timezone"` + Theme string `json:"theme"` } diff --git a/pkg/services/sqlstore/preferences.go b/pkg/services/sqlstore/preferences.go index f744b2eb50f..8882495329f 100644 --- a/pkg/services/sqlstore/preferences.go +++ b/pkg/services/sqlstore/preferences.go @@ -1,9 +1,9 @@ package sqlstore import ( - "time" "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" + "time" ) func init() { @@ -42,8 +42,8 @@ func SavePreferences(cmd *m.SavePreferencesCommand) error { HomeDashboardId: cmd.HomeDashboardId, Timezone: cmd.Timezone, Theme: cmd.Theme, - Created: time.Now(), - Updated: time.Now(), + Created: time.Now(), + Updated: time.Now(), } _, err = sess.Insert(&prefs) return err @@ -51,8 +51,8 @@ func SavePreferences(cmd *m.SavePreferencesCommand) error { prefs.HomeDashboardId = cmd.HomeDashboardId prefs.Timezone = cmd.Timezone prefs.Theme = cmd.Theme - prefs.Updated = time.Now() - prefs.Version += 1 + prefs.Updated = time.Now() + prefs.Version += 1 _, err = sess.Id(prefs.Id).Update(&prefs) return err } diff --git a/public/app/features/dashboard/dashnav/dashnav.ts b/public/app/features/dashboard/dashnav/dashnav.ts index c6faaf9a738..14dfa8cc67a 100644 --- a/public/app/features/dashboard/dashnav/dashnav.ts +++ b/public/app/features/dashboard/dashnav/dashnav.ts @@ -106,7 +106,7 @@ export class DashNavCtrl { $scope.saveDashboardAsHome = function() { // TODO: this backend method needs to be implemented backendSrv.post('/api/preferences/set-home-dash', { - dashboardId: $scope.dashboard.id + homeDashboardId: $scope.dashboard.id }); }; From 66621d762e84c20e3835ba7386d9e5aeb5ff64d3 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Thu, 17 Mar 2016 01:01:58 -0700 Subject: [PATCH 12/54] Added getDashboardSlugById api --- pkg/api/api.go | 1 + pkg/api/dashboard.go | 14 ++++++++++++++ pkg/api/dtos/models.go | 4 ++++ pkg/models/dashboards.go | 5 +++++ pkg/services/sqlstore/dashboard.go | 15 +++++++++++++++ 5 files changed, 39 insertions(+) diff --git a/pkg/api/api.go b/pkg/api/api.go index 0abc4bd6026..a30c88eb58d 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -200,6 +200,7 @@ func Register(r *macaron.Macaron) { r.Get("/home", GetHomeDashboard) r.Get("/tags", GetDashboardTags) r.Post("/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard)) + r.Get("/id/:id", GetDashboardSlugById) }) // Dashboard snapshots diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 22f9e1e22a1..d6c0fa0a827 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -204,3 +204,17 @@ func GetDashboardTags(c *middleware.Context) { c.JSON(200, query.Result) } + +func GetDashboardSlugById(c *middleware.Context) { + dashId := c.ParamsInt64(":id") + query := m.GetDashboardSlugByIdQuery{Id: dashId} + err := bus.Dispatch(&query) + if err != nil { + c.JsonApiErr(500, "Failed to get slug from database", err) + return + } + + slug := dtos.DashboardSlug{Slug: query.Result} + + c.JSON(200, &slug) +} diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index 26295dd3d3c..9fb48e059b4 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -57,6 +57,10 @@ type DashboardFullWithMeta struct { Dashboard *simplejson.Json `json:"dashboard"` } +type DashboardSlug struct { + Slug string `json:"slug"` +} + type DataSource struct { Id int64 `json:"id"` OrgId int64 `json:"orgId"` diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index 6243c729624..6b19224f934 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -148,3 +148,8 @@ type GetDashboardsQuery struct { DashboardIds []int64 Result *[]Dashboard } + +type GetDashboardSlugByIdQuery struct { + Id int64 + Result string +} diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index 396d507cfd2..2e8fd748154 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -18,6 +18,7 @@ func init() { bus.AddHandler("sql", DeleteDashboard) bus.AddHandler("sql", SearchDashboards) bus.AddHandler("sql", GetDashboardTags) + bus.AddHandler("sql", GetDashboardSlugById) } func SaveDashboard(cmd *m.SaveDashboardCommand) error { @@ -255,3 +256,17 @@ func GetDashboards(query *m.GetDashboardsQuery) error { return nil } + +func GetDashboardSlugById(query *m.GetDashboardSlugByIdQuery) error { + dashboard := m.Dashboard{Id: query.Id} + has, err := x.Get(&dashboard) + query.Result = dashboard.Slug + + if err != nil { + return err + } else if has == false { + return m.ErrDashboardNotFound + } + + return nil +} From 4fbe954a7996e65d2a96309fda846138489f1662 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Thu, 17 Mar 2016 01:38:18 -0700 Subject: [PATCH 13/54] Home dashboard per user per org works --- pkg/api/api.go | 2 +- public/app/core/routes/dashboard_loaders.js | 22 +++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index a30c88eb58d..d06e3a5fac4 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -162,7 +162,7 @@ func Register(r *macaron.Macaron) { // Preferences r.Group("/preferences", func() { - r.Get("/", wrap(GetPreferences)) + r.Get("/", GetPreferences) r.Put("/", bind(m.SavePreferencesCommand{}), wrap(SavePreferences)) r.Post("/set-home-dash", bind(m.SavePreferencesCommand{}), wrap(SetHomeDashboard)) }) diff --git a/public/app/core/routes/dashboard_loaders.js b/public/app/core/routes/dashboard_loaders.js index 8e2157ad84b..c24ed1ea7a2 100644 --- a/public/app/core/routes/dashboard_loaders.js +++ b/public/app/core/routes/dashboard_loaders.js @@ -7,11 +7,25 @@ function (coreModule) { coreModule.default.controller('LoadDashboardCtrl', function($scope, $routeParams, dashboardLoaderSrv, backendSrv) { if (!$routeParams.slug) { - backendSrv.get('/api/dashboards/home').then(function(result) { - var meta = result.meta; - meta.canSave = meta.canShare = meta.canStar = false; - $scope.initDashboard(result, $scope); + + backendSrv.get('/api/preferences').then(function(preferences) { + if (preferences !== null && preferences.homeDashboardId !== 0) { + backendSrv.get('/api/dashboards/id/' + preferences.homeDashboardId).then(function(dashSlug) { + $routeParams.type = 'db'; + $routeParams.slug = dashSlug.slug; + dashboardLoaderSrv.loadDashboard($routeParams.type, $routeParams.slug).then(function(result) { + $scope.initDashboard(result, $scope); + }); + }); + } else { + backendSrv.get('/api/dashboards/home').then(function(result) { + var meta = result.meta; + meta.canSave = meta.canShare = meta.canStar = false; + $scope.initDashboard(result, $scope); + }); + } }); + return; } From cb42cfc6af43b9ec9a0b2f0416d65fc78f8ce2ed Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Thu, 17 Mar 2016 02:29:34 -0700 Subject: [PATCH 14/54] Removed unwanted api, moved logic into backend --- pkg/api/api.go | 1 - pkg/api/dashboard.go | 36 +++++++++++++-------- public/app/core/routes/dashboard_loaders.js | 22 +++++-------- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index d06e3a5fac4..0f94e3f7f57 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -200,7 +200,6 @@ func Register(r *macaron.Macaron) { r.Get("/home", GetHomeDashboard) r.Get("/tags", GetDashboardTags) r.Post("/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard)) - r.Get("/id/:id", GetDashboardSlugById) }) // Dashboard snapshots diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index d6c0fa0a827..1ea22984521 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -159,6 +159,28 @@ func canEditDashboard(role m.RoleType) bool { } func GetHomeDashboard(c *middleware.Context) { + + // Checking if there is any preference set for home dashboard + query := m.GetPreferencesQuery{UserId: c.UserId, OrgId: c.OrgId} + + if err := bus.Dispatch(&query); err != nil { + c.JsonApiErr(500, "Failed to get preferences", err) + } + + if query.Result.HomeDashboardId != 0 { + query := m.GetDashboardSlugByIdQuery{Id: query.Result.HomeDashboardId} + err := bus.Dispatch(&query) + if err != nil { + c.JsonApiErr(500, "Failed to get slug from database", err) + return + } + + slug := dtos.DashboardSlug{Slug: query.Result} + + c.JSON(200, &slug) + return + } + filePath := path.Join(setting.StaticRootPath, "dashboards/home.json") file, err := os.Open(filePath) if err != nil { @@ -204,17 +226,3 @@ func GetDashboardTags(c *middleware.Context) { c.JSON(200, query.Result) } - -func GetDashboardSlugById(c *middleware.Context) { - dashId := c.ParamsInt64(":id") - query := m.GetDashboardSlugByIdQuery{Id: dashId} - err := bus.Dispatch(&query) - if err != nil { - c.JsonApiErr(500, "Failed to get slug from database", err) - return - } - - slug := dtos.DashboardSlug{Slug: query.Result} - - c.JSON(200, &slug) -} diff --git a/public/app/core/routes/dashboard_loaders.js b/public/app/core/routes/dashboard_loaders.js index c24ed1ea7a2..9c276a7c3fd 100644 --- a/public/app/core/routes/dashboard_loaders.js +++ b/public/app/core/routes/dashboard_loaders.js @@ -7,25 +7,19 @@ function (coreModule) { coreModule.default.controller('LoadDashboardCtrl', function($scope, $routeParams, dashboardLoaderSrv, backendSrv) { if (!$routeParams.slug) { - - backendSrv.get('/api/preferences').then(function(preferences) { - if (preferences !== null && preferences.homeDashboardId !== 0) { - backendSrv.get('/api/dashboards/id/' + preferences.homeDashboardId).then(function(dashSlug) { - $routeParams.type = 'db'; - $routeParams.slug = dashSlug.slug; - dashboardLoaderSrv.loadDashboard($routeParams.type, $routeParams.slug).then(function(result) { - $scope.initDashboard(result, $scope); - }); - }); + backendSrv.get('/api/dashboards/home').then(function(result) { + if (result.slug == null) { + var meta = result.meta; + meta.canSave = meta.canShare = meta.canStar = false; + $scope.initDashboard(result, $scope); } else { - backendSrv.get('/api/dashboards/home').then(function(result) { - var meta = result.meta; - meta.canSave = meta.canShare = meta.canStar = false; + $routeParams.type = 'db'; + $routeParams.slug = result.slug; + dashboardLoaderSrv.loadDashboard($routeParams.type, $routeParams.slug).then(function(result) { $scope.initDashboard(result, $scope); }); } }); - return; } From 7023dedc0523d1db83110663744f88e9f240279a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 20 Mar 2016 11:52:19 +0100 Subject: [PATCH 15/54] feat(preferences): refactoring PR #4399 --- pkg/api/dashboard.go | 5 ++--- pkg/api/dtos/models.go | 4 ++-- pkg/services/sqlstore/dashboard.go | 14 ++++++++++---- public/app/core/routes/dashboard_loaders.js | 18 +++++++----------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 1ea22984521..1a45ffcd020 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -175,9 +175,8 @@ func GetHomeDashboard(c *middleware.Context) { return } - slug := dtos.DashboardSlug{Slug: query.Result} - - c.JSON(200, &slug) + dashRedirect := dtos.DashboardRedirect{RedirectUri: "db/" + query.Result} + c.JSON(200, &dashRedirect) return } diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index b9fd49a19c8..a95bd464f35 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -57,8 +57,8 @@ type DashboardFullWithMeta struct { Dashboard *simplejson.Json `json:"dashboard"` } -type DashboardSlug struct { - Slug string `json:"slug"` +type DashboardRedirect struct { + RedirectUri string `json:"redirectUri"` } type DataSource struct { diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index 2e8fd748154..a64094cb65e 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -257,16 +257,22 @@ func GetDashboards(query *m.GetDashboardsQuery) error { return nil } +type DashboardSlugDTO struct { + Slug string +} + func GetDashboardSlugById(query *m.GetDashboardSlugByIdQuery) error { - dashboard := m.Dashboard{Id: query.Id} - has, err := x.Get(&dashboard) - query.Result = dashboard.Slug + var rawSql = `SELECT slug from dashboard WHERE Id=?` + var slug = DashboardSlugDTO{} + + exists, err := x.Sql(rawSql, query.Id).Get(&slug) if err != nil { return err - } else if has == false { + } else if exists == false { return m.ErrDashboardNotFound } + query.Result = slug.Slug return nil } diff --git a/public/app/core/routes/dashboard_loaders.js b/public/app/core/routes/dashboard_loaders.js index 9c276a7c3fd..61cdf32c128 100644 --- a/public/app/core/routes/dashboard_loaders.js +++ b/public/app/core/routes/dashboard_loaders.js @@ -4,20 +4,16 @@ define([ function (coreModule) { "use strict"; - coreModule.default.controller('LoadDashboardCtrl', function($scope, $routeParams, dashboardLoaderSrv, backendSrv) { + coreModule.default.controller('LoadDashboardCtrl', function($scope, $routeParams, dashboardLoaderSrv, backendSrv, $location) { if (!$routeParams.slug) { - backendSrv.get('/api/dashboards/home').then(function(result) { - if (result.slug == null) { - var meta = result.meta; - meta.canSave = meta.canShare = meta.canStar = false; - $scope.initDashboard(result, $scope); + backendSrv.get('/api/dashboards/home').then(function(homeDash) { + if (homeDash.redirectUri) { + $location.path('dashboard/' + homeDash.redirectUri); } else { - $routeParams.type = 'db'; - $routeParams.slug = result.slug; - dashboardLoaderSrv.loadDashboard($routeParams.type, $routeParams.slug).then(function(result) { - $scope.initDashboard(result, $scope); - }); + var meta = homeDash.meta; + meta.canSave = meta.canShare = meta.canStar = false; + $scope.initDashboard(homeDash, $scope); } }); return; From 5e431149cd5f69c2ababcc77b64ab4615eb663b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 20 Mar 2016 11:54:24 +0100 Subject: [PATCH 16/54] feat(preferences): removed unused api prefernce methods, these need to be abstracted more from an http api perpsective, they are too low level for the http api, #4399 --- pkg/api/api.go | 2 -- pkg/api/preferences.go | 34 ---------------------------------- 2 files changed, 36 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index eb3f830b6c6..5241bc1abb7 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -162,8 +162,6 @@ func Register(r *macaron.Macaron) { // Preferences r.Group("/preferences", func() { - r.Get("/", GetPreferences) - r.Put("/", bind(m.SavePreferencesCommand{}), wrap(SavePreferences)) r.Post("/set-home-dash", bind(m.SavePreferencesCommand{}), wrap(SetHomeDashboard)) }) diff --git a/pkg/api/preferences.go b/pkg/api/preferences.go index 74419492d09..490da451afc 100644 --- a/pkg/api/preferences.go +++ b/pkg/api/preferences.go @@ -1,44 +1,11 @@ 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" ) -// PUT /api/preferences -func SavePreferences(c *middleware.Context, cmd m.SavePreferencesCommand) Response { - - cmd.UserId = c.UserId - cmd.OrgId = c.OrgId - - if err := bus.Dispatch(&cmd); err != nil { - return ApiError(500, "Failed to save preferences", err) - } - - return ApiSuccess("Preferences saved") - -} - -// GET /api/preferences -func GetPreferences(c *middleware.Context) { - - query := m.GetPreferencesQuery{UserId: c.UserId, OrgId: c.OrgId} - - if err := bus.Dispatch(&query); err != nil { - c.JsonApiErr(500, "Failed to get preferences", err) - } - - dto := dtos.Preferences{ - HomeDashboardId: query.Result.HomeDashboardId, - Timezone: query.Result.Timezone, - Theme: query.Result.Theme, - } - - c.JSON(200, dto) -} - // POST /api/preferences/set-home-dash func SetHomeDashboard(c *middleware.Context, cmd m.SavePreferencesCommand) Response { @@ -50,5 +17,4 @@ func SetHomeDashboard(c *middleware.Context, cmd m.SavePreferencesCommand) Respo } return ApiSuccess("Home dashboard set") - } From 9be1ffd6337ca50d6b0b5beb505e818c2122d92d Mon Sep 17 00:00:00 2001 From: Matt Toback Date: Mon, 28 Mar 2016 18:37:02 -0400 Subject: [PATCH 17/54] Fixed code blocks in Grafana --- public/sass/_grafana.scss | 2 ++ public/sass/base/_code.scss | 14 +++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index c7b4567bfb5..2adada88ce1 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -17,6 +17,8 @@ @import "base/grid"; @import "base/font_awesome"; @import "base/grafana_icons"; +@import "base/code"; + // UTILS @import "utils/utils"; diff --git a/public/sass/base/_code.scss b/public/sass/base/_code.scss index db7a8cfab18..2a4057a8547 100644 --- a/public/sass/base/_code.scss +++ b/public/sass/base/_code.scss @@ -11,28 +11,28 @@ pre { background-color: $code-tag-bg; color: $text-color; border: 1px solid darken($code-tag-bg, 15%); - padding: 2px; + padding: 10px; + border-radius: 4px; } // Inline code code { - color: #d14; - background-color: #f7f7f9; - border: 1px solid #e1e1e8; + color: $text-color; + background-color: $code-tag-bg; + border: 1px solid darken($code-tag-bg, 15%); white-space: nowrap; } // Blocks of code pre { display: block; - margin: 0 0 $line-height-base / 2; - font-size: $font-size-base - 1; // 14px to 13px + margin: 0 0 $line-height-base; line-height: $line-height-base; word-break: break-all; word-wrap: break-word; white-space: pre; white-space: pre-wrap; - background-color: #f5f5f5; + background-color: $code-tag-bg; // Make prettyprint styles more spaced out for readability &.prettyprint { From ef89dabb61cef108aad2c9baca33068e81483af1 Mon Sep 17 00:00:00 2001 From: Matt Toback Date: Mon, 28 Mar 2016 18:37:52 -0400 Subject: [PATCH 18/54] Go away extra line --- public/sass/_grafana.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index 2adada88ce1..900d0c00f80 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -19,7 +19,6 @@ @import "base/grafana_icons"; @import "base/code"; - // UTILS @import "utils/utils"; @import "utils/validation"; From 2f3627e74b261d55f7700c035220a1c1a4030096 Mon Sep 17 00:00:00 2001 From: woodsaj Date: Tue, 29 Mar 2016 22:53:02 +0800 Subject: [PATCH 19/54] add missing ngInject comment. fixes #4501 --- public/app/features/plugins/import_list/import_list.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/features/plugins/import_list/import_list.ts b/public/app/features/plugins/import_list/import_list.ts index 8eb726b81c0..6c82d3faa92 100644 --- a/public/app/features/plugins/import_list/import_list.ts +++ b/public/app/features/plugins/import_list/import_list.ts @@ -10,6 +10,7 @@ export class DashImportListCtrl { plugin: any; datasource: any; + /** @ngInject */ constructor($scope, private $http, private backendSrv, private $rootScope) { this.dashboards = []; From aa62c2eff63e830eb0f383cabe8ba37e0e439b6c Mon Sep 17 00:00:00 2001 From: Sebastian Borza Date: Tue, 29 Mar 2016 12:36:21 -0500 Subject: [PATCH 20/54] Force the query check to lowercase for tag values, fix issue with templating influxdb v0.11.0 --- public/app/plugins/datasource/influxdb/response_parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/influxdb/response_parser.ts b/public/app/plugins/datasource/influxdb/response_parser.ts index ae6f2cb75a9..2e33b398a88 100644 --- a/public/app/plugins/datasource/influxdb/response_parser.ts +++ b/public/app/plugins/datasource/influxdb/response_parser.ts @@ -15,7 +15,7 @@ export default class ResponseParser { var series = influxResults.series[0]; return _.map(series.values, (value) => { if (_.isArray(value)) { - if (query.indexOf('SHOW TAG VALUES') >= 0) { + if (query.toLowerCase().indexOf('show tag values') >= 0) { return { text: (value[1] || value[0]) }; } else { return { text: value[0] }; From 2ee9376df22d6c640f4357047d377c430ae2882d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 29 Mar 2016 20:49:42 +0200 Subject: [PATCH 21/54] ux(): form tweaks --- public/app/plugins/panel/graph/tab_axes.html | 52 ++++++++++---------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/public/app/plugins/panel/graph/tab_axes.html b/public/app/plugins/panel/graph/tab_axes.html index e7e35055d5c..81ab3deeb31 100644 --- a/public/app/plugins/panel/graph/tab_axes.html +++ b/public/app/plugins/panel/graph/tab_axes.html @@ -41,30 +41,32 @@
    X-Axis
    -
    -
    -
    Thresholds
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    +
    +
    Thresholds
    +
    +
    + + +
    +
    + +
    + +
    +
    +
    +
    +
    + + +
    +
    + +
    + +
    +
    +
    +
    From 3dbfd494144a7fb65a838219fce0e7802ffc96f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 29 Mar 2016 22:23:29 +0200 Subject: [PATCH 22/54] ux(): templating forms polish --- public/app/features/dashboard/dashboardSrv.js | 9 +++- .../features/dashboard/submenu/submenu.html | 4 +- public/app/features/templating/editorCtrl.js | 7 +++ .../features/templating/partials/editor.html | 48 +++++++++++-------- 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/public/app/features/dashboard/dashboardSrv.js b/public/app/features/dashboard/dashboardSrv.js index 00610624867..b3194868d97 100644 --- a/public/app/features/dashboard/dashboardSrv.js +++ b/public/app/features/dashboard/dashboardSrv.js @@ -401,11 +401,18 @@ function (angular, $, _, moment) { }); } - if (oldVersion < 11) { + if (oldVersion < 12) { // update template variables _.each(this.templating.list, function(templateVariable) { if (templateVariable.refresh) { templateVariable.refresh = 1; } if (!templateVariable.refresh) { templateVariable.refresh = 0; } + if (templateVariable.hideVariable) { + templateVariable.hide = 2; + } else if (templateVariable.hideLabel) { + templateVariable.hide = 1; + } else { + templateVariable.hide = 0; + } }); } diff --git a/public/app/features/dashboard/submenu/submenu.html b/public/app/features/dashboard/submenu/submenu.html index 21a9744b359..464d8c4cecf 100644 --- a/public/app/features/dashboard/submenu/submenu.html +++ b/public/app/features/dashboard/submenu/submenu.html @@ -1,7 +1,7 @@ - -
    -
    Rows settings
    +
    -
    -
    -
    - Title - -
    - +
    +
    Rows settings
    -
    - - - -
    -
    -
    -
    +
    +
    +
    + Title + +
    + -
    - -
    +
    + + + +
    +
    +
    + -
    - -
    +
    + +
    -
    -
    Dashboard info
    -
    -
    - Last updated at: - {{formatDate(dashboardMeta.updated)}} -
    -
    - Last updated by: - {{dashboardMeta.updatedBy}}  -
    -
    - Created at: - {{formatDate(dashboardMeta.created)}}  -
    -
    - Created by: - {{dashboardMeta.createdBy}}  -
    -
    - Current version: - {{dashboardMeta.version}}  -
    -
    -
    +
    + +
    + +
    +
    Dashboard info
    +
    +
    + Last updated at: + {{formatDate(dashboardMeta.updated)}} +
    +
    + Last updated by: + {{dashboardMeta.updatedBy}}  +
    +
    + Created at: + {{formatDate(dashboardMeta.created)}}  +
    +
    + Created by: + {{dashboardMeta.createdBy}}  +
    +
    + Current version: + {{dashboardMeta.version}}  +
    +
    +
    diff --git a/public/app/features/profile/partials/profile.html b/public/app/features/profile/partials/profile.html index 042344eeac6..0d6e5fb2951 100644 --- a/public/app/features/profile/partials/profile.html +++ b/public/app/features/profile/partials/profile.html @@ -10,21 +10,25 @@

    Preferences

    - Name + Name
    - Email + Email
    - Username + Username
    - UI Theme + UI Theme
    +
    + Home Dashboard + +
    From 45b90972dc2d02fc95d02b63ce953f947846f78e Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 30 Mar 2016 23:33:27 +0200 Subject: [PATCH 31/54] feat(plugins): adds a readme for every native plugin --- public/app/plugins/datasource/cloudwatch/README.md | 7 +++++++ .../app/plugins/datasource/elasticsearch/README.md | 7 +++++++ public/app/plugins/datasource/grafana/README.md | 3 +++ public/app/plugins/datasource/graphite/README.md | 9 +++++++++ public/app/plugins/datasource/influxdb/README.md | 13 +++++++++++++ public/app/plugins/datasource/mixed/README.md | 3 +++ public/app/plugins/datasource/opentsdb/README.md | 7 +++++++ public/app/plugins/datasource/prometheus/README.md | 7 +++++++ public/app/plugins/panel/dashlist/README.md | 9 +++++++++ public/app/plugins/panel/graph/README.md | 7 +++++++ public/app/plugins/panel/singlestat/README.md | 9 +++++++++ public/app/plugins/panel/table/README.md | 9 +++++++++ public/app/plugins/panel/text/README.md | 5 +++++ 13 files changed, 95 insertions(+) create mode 100644 public/app/plugins/datasource/cloudwatch/README.md create mode 100644 public/app/plugins/datasource/elasticsearch/README.md create mode 100644 public/app/plugins/datasource/grafana/README.md create mode 100644 public/app/plugins/datasource/graphite/README.md create mode 100644 public/app/plugins/datasource/influxdb/README.md create mode 100644 public/app/plugins/datasource/mixed/README.md create mode 100644 public/app/plugins/datasource/opentsdb/README.md create mode 100644 public/app/plugins/datasource/prometheus/README.md create mode 100644 public/app/plugins/panel/dashlist/README.md create mode 100644 public/app/plugins/panel/graph/README.md create mode 100644 public/app/plugins/panel/singlestat/README.md create mode 100644 public/app/plugins/panel/table/README.md create mode 100644 public/app/plugins/panel/text/README.md diff --git a/public/app/plugins/datasource/cloudwatch/README.md b/public/app/plugins/datasource/cloudwatch/README.md new file mode 100644 index 00000000000..a1b7bf5cc50 --- /dev/null +++ b/public/app/plugins/datasource/cloudwatch/README.md @@ -0,0 +1,7 @@ +# CloudWatch Datasource - Native Plugin + +Grafana ships with **built in** support for CloudWatch. You just have to add it as a data source and you will be ready to build dashboards for you CloudWatch metrics. + +Read more about it here: + +[http://docs.grafana.org/datasources/cloudwatch/](http://docs.grafana.org/datasources/cloudwatch/) \ No newline at end of file diff --git a/public/app/plugins/datasource/elasticsearch/README.md b/public/app/plugins/datasource/elasticsearch/README.md new file mode 100644 index 00000000000..21978025eeb --- /dev/null +++ b/public/app/plugins/datasource/elasticsearch/README.md @@ -0,0 +1,7 @@ +# CloudWatch Datasource - Native Plugin + +Grafana ships with **advanced support** for Elasticsearch. You can do many types of simple or complex elasticsearch queries to visualize logs or metrics stored in Elasticsearch. You can also annotate your graphs with log events stored in Elasticsearch. + +Read more about it here: + +[http://docs.grafana.org/datasources/elasticsearch/](http://docs.grafana.org/datasources/elasticsearch/) \ No newline at end of file diff --git a/public/app/plugins/datasource/grafana/README.md b/public/app/plugins/datasource/grafana/README.md new file mode 100644 index 00000000000..a8319c8dec5 --- /dev/null +++ b/public/app/plugins/datasource/grafana/README.md @@ -0,0 +1,3 @@ +# Grafana Fake Data Datasource - Native Plugin + +This is the built in Fake Data Datasource that is used before any datasources are set up in your Grafana installation. It means you can create a graph without any data and still get an idea of what it would look like. diff --git a/public/app/plugins/datasource/graphite/README.md b/public/app/plugins/datasource/graphite/README.md new file mode 100644 index 00000000000..c27c5789bca --- /dev/null +++ b/public/app/plugins/datasource/graphite/README.md @@ -0,0 +1,9 @@ +# Graphite Datasource - Native Plugin + +Grafana ships with **built in** support for Graphite (of course!). + +Grafana has an advanced Graphite query editor that lets you quickly navigate the metric space, add functions, change function parameters and much more. The editor can handle all types of graphite queries. It can even handle complex nested queries through the use of query references. + +Read more about it here: + +[http://docs.grafana.org/datasources/graphite/](http://docs.grafana.org/datasources/graphite/) \ No newline at end of file diff --git a/public/app/plugins/datasource/influxdb/README.md b/public/app/plugins/datasource/influxdb/README.md new file mode 100644 index 00000000000..45eaa51eb0f --- /dev/null +++ b/public/app/plugins/datasource/influxdb/README.md @@ -0,0 +1,13 @@ +# InfluxDB Datasource - Native Plugin + +Grafana ships with **built in** support for InfluxDB 0.9. + +There are currently two separate datasources for InfluxDB in Grafana: InfluxDB 0.8.x and InfluxDB 0.9.x. The API and capabilities of InfluxDB 0.9.x are completely different from InfluxDB 0.8.x which is why Grafana handles them as different data sources. + +This is the plugin for InfluxDB 0.9. It is rapidly evolving and we continue to track its API. + +InfluxDB 0.8 is no longer maintained by InfluxDB Inc, but we provide support as a convenience to existing users. You can find it [here](https://www.grafana.net/plugins/grafana-influxdb-08-datasource). + +Read more about InfluxDB here: + +[http://docs.grafana.org/datasources/influxdb/](http://docs.grafana.org/datasources/influxdb/) \ No newline at end of file diff --git a/public/app/plugins/datasource/mixed/README.md b/public/app/plugins/datasource/mixed/README.md new file mode 100644 index 00000000000..b69bb626c15 --- /dev/null +++ b/public/app/plugins/datasource/mixed/README.md @@ -0,0 +1,3 @@ +# Mixed Datasource - Native Plugin + +This is a **built in** datasource that allows you to mix different datasource on the same graph! You can enable this by selecting the built in -- Mixed -- data source. When selected this will allow you to specify data source on a per query basis. This will, for example, allow you to plot metrics from different Graphite servers on the same Graph or plot data from Elasticsearch alongside data from Prometheus. Mixing different data sources on the same graph works for any data source, even custom ones. \ No newline at end of file diff --git a/public/app/plugins/datasource/opentsdb/README.md b/public/app/plugins/datasource/opentsdb/README.md new file mode 100644 index 00000000000..697ecd5c4dc --- /dev/null +++ b/public/app/plugins/datasource/opentsdb/README.md @@ -0,0 +1,7 @@ +# OpenTSDB Datasource - Native Plugin + +Grafana ships with **built in** support for OpenTSDB, a scalable, distributed time series database. + +Read more about it here: + +[http://docs.grafana.org/datasources/opentsdb/](http://docs.grafana.org/datasources/opentsdb/) \ No newline at end of file diff --git a/public/app/plugins/datasource/prometheus/README.md b/public/app/plugins/datasource/prometheus/README.md new file mode 100644 index 00000000000..ee714043157 --- /dev/null +++ b/public/app/plugins/datasource/prometheus/README.md @@ -0,0 +1,7 @@ +# CloudWatch Datasource - Native Plugin + +Grafana ships with **built in** support for Prometheus, the open-source service monitoring system and time series database. + +Read more about it here: + +[http://docs.grafana.org/datasources/prometheus/](http://docs.grafana.org/datasources/prometheus/) \ No newline at end of file diff --git a/public/app/plugins/panel/dashlist/README.md b/public/app/plugins/panel/dashlist/README.md new file mode 100644 index 00000000000..55996b5aff4 --- /dev/null +++ b/public/app/plugins/panel/dashlist/README.md @@ -0,0 +1,9 @@ +# Dashlist Panel - Native Plugin + +This Dashlist panel is **included** with Grafana. + +The dashboard list panel allows you to display dynamic links to other dashboards. The list can be configured to use starred dashboards, a search query and/or dashboard tags. + +Read more about it here: + +[http://docs.grafana.org/reference/dashlist/](http://docs.grafana.org/reference/dashlist/) \ No newline at end of file diff --git a/public/app/plugins/panel/graph/README.md b/public/app/plugins/panel/graph/README.md new file mode 100644 index 00000000000..2dc8682f0e3 --- /dev/null +++ b/public/app/plugins/panel/graph/README.md @@ -0,0 +1,7 @@ +# Graph Panel - Native Plugin + +The Graph is the main graph panel and is **included** with Grafana. It provides a very rich set of graphing options. + +Read more about it here: + +[http://docs.grafana.org/reference/graph/](http://docs.grafana.org/reference/graph/) \ No newline at end of file diff --git a/public/app/plugins/panel/singlestat/README.md b/public/app/plugins/panel/singlestat/README.md new file mode 100644 index 00000000000..42d72825c27 --- /dev/null +++ b/public/app/plugins/panel/singlestat/README.md @@ -0,0 +1,9 @@ +# Singlestat Panel - Native Plugin + +The Singlestat Panel is **included** with Grafana. + +The Singlestat Panel allows you to show the one main summary stat of a SINGLE series. It reduces the series into a single number (by looking at the max, min, average, or sum of values in the series). Singlestat also provides thresholds to color the stat or the Panel background. It can also translate the single number into a text value, and show a sparkline summary of the series. + +Read more about it here: + +[http://docs.grafana.org/reference/singlestat/](http://docs.grafana.org/reference/singlestat/) \ No newline at end of file diff --git a/public/app/plugins/panel/table/README.md b/public/app/plugins/panel/table/README.md new file mode 100644 index 00000000000..48c4fac641b --- /dev/null +++ b/public/app/plugins/panel/table/README.md @@ -0,0 +1,9 @@ +# Table Panel - Native Plugin + +The Table Panel is **included** with Grafana. + +The table panel is very flexible, supporting both multiple modes for time series as well as for table, annotation and raw JSON data. It also provides date formatting and value formatting and coloring options. + +Check out the [Table Panel Showcase in the Grafana Playground](http://play.grafana.org/dashboard/db/table-panel-showcase) or read more about it here: + +[http://docs.grafana.org/reference/table_panel/](http://docs.grafana.org/reference/table_panel/) \ No newline at end of file diff --git a/public/app/plugins/panel/text/README.md b/public/app/plugins/panel/text/README.md new file mode 100644 index 00000000000..14751842990 --- /dev/null +++ b/public/app/plugins/panel/text/README.md @@ -0,0 +1,5 @@ +# Text Panel - Native Plugin + +The Text Panel is **included** with Grafana. + +The Text Panel is a very simple panel that displays text. The source text is written in the Markdown syntax meaning you can format the text. Read [GitHub's Mastering Markdown](https://guides.github.com/features/mastering-markdown/) to learn more. \ No newline at end of file From b85b5e00d4c3388407649917ee7548db96abbc13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 30 Mar 2016 21:26:54 -0700 Subject: [PATCH 32/54] fix(): fixed panel resize and fullscreen panel height issue --- public/app/features/panel/panel_ctrl.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 2821164bb10..ff2e7fde1bd 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -45,7 +45,7 @@ export class PanelCtrl { } $scope.$on("refresh", () => this.refresh()); - $scope.$on("render", () => this.calculatePanelHeight()); + $scope.$on("render", () => this.render()); } init() { @@ -149,6 +149,7 @@ export class PanelCtrl { return; } + this.calculatePanelHeight(); this.events.emit('render', payload); } From 2bf305b177803117045ac36dfcca2429a92f00a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 30 Mar 2016 22:30:41 -0700 Subject: [PATCH 33/54] build(): updated build version --- package.json | 2 +- packaging/publish/publish.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f2f7af468b5..ad4840144dc 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Coding Instinct AB" }, "name": "grafana", - "version": "3.0.0-pre", + "version": "3.0.0-beta1", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git" diff --git a/packaging/publish/publish.sh b/packaging/publish/publish.sh index bc644b71042..d918f9a89d8 100755 --- a/packaging/publish/publish.sh +++ b/packaging/publish/publish.sh @@ -1,11 +1,11 @@ #! /usr/bin/env bash -deb_ver=3.0.0-pre1459365183 -rpm_ver=3.0.0-pre1459365183 +deb_ver=3.0.0-pre1459399258 +rpm_ver=3.0.0-pre1459399258 #rpm_ver=3.0.0-1 -# wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb +#wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb # package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb # package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb From eb850ddd365257de75cc53c8b156b7bee7f6a917 Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Thu, 31 Mar 2016 11:43:04 +0100 Subject: [PATCH 34/54] doc(plugins): typo --- public/app/plugins/datasource/prometheus/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/README.md b/public/app/plugins/datasource/prometheus/README.md index ee714043157..5b188c3393c 100644 --- a/public/app/plugins/datasource/prometheus/README.md +++ b/public/app/plugins/datasource/prometheus/README.md @@ -1,7 +1,7 @@ -# CloudWatch Datasource - Native Plugin +# Prometheus Datasource - Native Plugin Grafana ships with **built in** support for Prometheus, the open-source service monitoring system and time series database. Read more about it here: -[http://docs.grafana.org/datasources/prometheus/](http://docs.grafana.org/datasources/prometheus/) \ No newline at end of file +[http://docs.grafana.org/datasources/prometheus/](http://docs.grafana.org/datasources/prometheus/) From 601e90f5fe6c631405550b1b24ea59febe2df6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 31 Mar 2016 06:23:52 -0700 Subject: [PATCH 35/54] docs(): updated install instructions --- docs/sources/installation/debian.md | 11 +++++++++-- docs/sources/installation/rpm.md | 23 +++++++++++++++++++++-- docs/sources/installation/windows.md | 2 +- packaging/publish/publish.sh | 6 +++--- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index b5c72f3182c..2d24e7a9c3f 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -10,14 +10,21 @@ page_keywords: grafana, installation, debian, ubuntu, guide Description | Download ------------ | ------------- -.deb for Debian-based Linux | [grafana_2.6.0_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_2.6.0_amd64.deb) +Stable .deb for Debian-based Linux | [grafana_2.6.0_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_2.6.0_amd64.deb) +Beta .deb for Debian-based Linux | [grafana_3.0.0-beta11459429091_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta11459429091_amd64.deb) -## Install +## Install Stable $ wget https://grafanarel.s3.amazonaws.com/builds/grafana_2.6.0_amd64.deb $ sudo apt-get install -y adduser libfontconfig $ sudo dpkg -i grafana_2.6.0_amd64.deb +## Install 3.0 Beta + + $ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta11459429091_amd64.deb + $ sudo apt-get install -y adduser libfontconfig + $ sudo dpkg -i grafana_3.0.0-beta11459429091_amd64.deb + ## APT Repository Add the following line to your `/etc/apt/sources.list` file. diff --git a/docs/sources/installation/rpm.md b/docs/sources/installation/rpm.md index ec4f648e44b..5e8e746d0f7 100644 --- a/docs/sources/installation/rpm.md +++ b/docs/sources/installation/rpm.md @@ -10,9 +10,10 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide Description | Download ------------ | ------------- -.RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [grafana-2.6.0-1.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-2.6.0-1.x86_64.rpm) +Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [grafana-2.6.0-1.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-2.6.0-1.x86_64.rpm) +Beta .RPM for CentOS / Fedor / OpenSuse / Redhat Linux | [grafana-3.0.0-beta11459429091.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta11459429091.x86_64.rpm) -## Install from package file +## Install Stable Release from package file You can install Grafana using Yum directly. @@ -29,6 +30,24 @@ Or install manually using `rpm`. $ sudo rpm -i --nodeps grafana-2.6.0-1.x86_64.rpm +## Install Beta Release from package file + +You can install Grafana using Yum directly. + + $ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta11459429091.x86_64.rpm + +Or install manually using `rpm`. + +#### On CentOS / Fedora / Redhat: + + $ sudo yum install initscripts fontconfig + $ sudo rpm -Uvh grafana-3.0.0-beta11459429091.x86_64.rpm + +#### On OpenSuse: + + $ sudo rpm -i --nodeps grafana-3.0.0-beta11459429091.x86_64.rpm + + ## Install via YUM Repository Add the following to a new file at `/etc/yum.repos.d/grafana.repo` diff --git a/docs/sources/installation/windows.md b/docs/sources/installation/windows.md index 1bc2b5a2b92..9a1ccbd0a19 100644 --- a/docs/sources/installation/windows.md +++ b/docs/sources/installation/windows.md @@ -10,7 +10,7 @@ page_keywords: grafana, installation, windows guide Description | Download ------------ | ------------- -Zip package for Windows | [grafana.2.5.0.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-2.5.0.windows-x64.zip) +Stable Zip package for Windows | [grafana.2.5.0.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-2.5.0.windows-x64.zip) ## Configure diff --git a/packaging/publish/publish.sh b/packaging/publish/publish.sh index d918f9a89d8..58021bc6ffa 100755 --- a/packaging/publish/publish.sh +++ b/packaging/publish/publish.sh @@ -1,11 +1,11 @@ #! /usr/bin/env bash -deb_ver=3.0.0-pre1459399258 -rpm_ver=3.0.0-pre1459399258 +deb_ver=3.0.0-beta11459429091 +rpm_ver=3.0.0-beta11459429091 #rpm_ver=3.0.0-1 -#wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb +wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb # package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb # package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb From 5e6c3f314c4678aad3700be36fe6283437640307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 31 Mar 2016 06:47:50 -0700 Subject: [PATCH 36/54] build(): trying to fix windows build on appveyor --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 83506312912..7d84bafc148 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,7 +5,7 @@ os: Windows Server 2012 R2 clone_folder: c:\gopath\src\github.com\grafana\grafana environment: - nodejs_version: "4" + nodejs_version: "5" GOPATH: c:\gopath install: From 171f6422b1144960000197ab1d9abca946d8acde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 31 Mar 2016 07:05:44 -0700 Subject: [PATCH 37/54] docs(): corrected version --- docs/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/VERSION b/docs/VERSION index fcdb2e109f6..4a36342fcab 100644 --- a/docs/VERSION +++ b/docs/VERSION @@ -1 +1 @@ -4.0.0 +3.0.0 From 2f2029a5c1d96ea958f9cd1b5a26d31f4ca564c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 1 Apr 2016 10:56:56 -0700 Subject: [PATCH 38/54] fix(dashboard): fixed dashboard layout for mobile devices, fixes #4529 --- CHANGELOG.md | 7 ++++++- package.json | 2 +- public/sass/_old_responsive.scss | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9959767c087..7e810a7801f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ -# 3.0.0 (unrelased master branch) +# 3.0.0-beta2 (unreleased) + +### Bug fixes +* **Dashboard**: Fixed dashboard panel layout for mobile devices, fixes [#4529](https://github.com/grafana/grafana/issues/4529) + +# 3.0.0-beta1 (2016-03-31) ### New Features * **Playlists**: Playlists can now be persisted and started from urls, closes [#3655](https://github.com/grafana/grafana/issues/3655) diff --git a/package.json b/package.json index ad4840144dc..4b526f5a4f3 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Coding Instinct AB" }, "name": "grafana", - "version": "3.0.0-beta1", + "version": "3.0.0-beta2", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git" diff --git a/public/sass/_old_responsive.scss b/public/sass/_old_responsive.scss index a7b2917277a..899a785c19b 100644 --- a/public/sass/_old_responsive.scss +++ b/public/sass/_old_responsive.scss @@ -7,8 +7,8 @@ // --------------------- @include media-breakpoint-down(sm) { div.panel { - width: 100%; - padding: 0px; + width: 100% !important; + padding: 0px !important; } .panel-margin { margin-right: 0; From 38a10f8be44a4f84018629925b95d4d9780b9067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 1 Apr 2016 17:34:30 -0700 Subject: [PATCH 39/54] progress --- pkg/api/api.go | 2 + pkg/api/dashboard.go | 1 - pkg/api/dtos/prefs.go | 11 ++- pkg/api/preferences.go | 35 ++++++++ pkg/services/sqlstore/preferences.go | 3 +- .../app/core/components/dashboard_selector.ts | 18 +++- public/app/core/core.ts | 2 + public/app/core/routes/routes.ts | 1 + public/app/features/all.js | 2 +- .../app/features/org/partials/orgDetails.html | 1 - .../features/profile/partials/profile.html | 40 +++++++-- public/app/features/profile/profileCtrl.js | 51 ----------- public/app/features/profile/profile_ctrl.ts | 87 +++++++++++++++++++ public/vendor/angular/angular.js | 2 +- 14 files changed, 189 insertions(+), 67 deletions(-) delete mode 100644 public/app/features/profile/profileCtrl.js create mode 100644 public/app/features/profile/profile_ctrl.ts diff --git a/pkg/api/api.go b/pkg/api/api.go index 277e5f4373a..b47c632a918 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -100,6 +100,8 @@ func Register(r *macaron.Macaron) { r.Delete("/stars/dashboard/:id", wrap(UnstarDashboard)) r.Put("/password", bind(m.ChangeUserPasswordCommand{}), wrap(ChangeUserPassword)) r.Get("/quotas", wrap(GetUserQuotas)) + r.Get("/preferences", wrap(GetUserPreferences)) + r.Put("/preferences", bind(dtos.UpdateUserPrefsCmd{}), wrap(UpdateUserPreferences)) }) // users (admin permission required) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 1a45ffcd020..fc4aa59fbb0 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -159,7 +159,6 @@ func canEditDashboard(role m.RoleType) bool { } func GetHomeDashboard(c *middleware.Context) { - // Checking if there is any preference set for home dashboard query := m.GetPreferencesQuery{UserId: c.UserId, OrgId: c.OrgId} diff --git a/pkg/api/dtos/prefs.go b/pkg/api/dtos/prefs.go index f66068edccb..77ef0a1da14 100644 --- a/pkg/api/dtos/prefs.go +++ b/pkg/api/dtos/prefs.go @@ -1,6 +1,15 @@ package dtos -type Preferences struct { +type UserPrefs struct { + Theme string `json:"theme"` + ThemeDefault string `json:"themeDefault"` + HomeDashboardId int64 `json:"homeDashboardId"` + HomeDashboardIdDefault int64 `json:"homeDashboardIdDefault"` + Timezone string `json:"timezone"` + TimezoneDefault string `json:"timezoneDefault"` +} + +type UpdateUserPrefsCmd struct { Theme string `json:"theme"` HomeDashboardId int64 `json:"homeDashboardId"` Timezone string `json:"timezone"` diff --git a/pkg/api/preferences.go b/pkg/api/preferences.go index 490da451afc..ed51ddaa9ae 100644 --- a/pkg/api/preferences.go +++ b/pkg/api/preferences.go @@ -1,6 +1,7 @@ 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" @@ -18,3 +19,37 @@ func SetHomeDashboard(c *middleware.Context, cmd m.SavePreferencesCommand) Respo return ApiSuccess("Home dashboard set") } + +// GET /api/user/preferences +func GetUserPreferences(c *middleware.Context) Response { + userPrefs := m.GetPreferencesQuery{UserId: c.UserId, OrgId: c.OrgId} + + if err := bus.Dispatch(&userPrefs); err != nil { + c.JsonApiErr(500, "Failed to get preferences", err) + } + + dto := dtos.UserPrefs{ + Theme: userPrefs.Result.Theme, + HomeDashboardId: userPrefs.Result.HomeDashboardId, + Timezone: userPrefs.Result.Timezone, + } + + return Json(200, &dto) +} + +// PUT /api/user/preferences +func UpdateUserPreferences(c *middleware.Context, dtoCmd dtos.UpdateUserPrefsCmd) Response { + saveCmd := m.SavePreferencesCommand{ + UserId: c.UserId, + OrgId: c.OrgId, + Theme: dtoCmd.Theme, + Timezone: dtoCmd.Timezone, + HomeDashboardId: dtoCmd.HomeDashboardId, + } + + if err := bus.Dispatch(&saveCmd); err != nil { + c.JsonApiErr(500, "Failed to save preferences", err) + } + + return ApiSuccess("User preferences updated") +} diff --git a/pkg/services/sqlstore/preferences.go b/pkg/services/sqlstore/preferences.go index 8882495329f..1b5fa79d879 100644 --- a/pkg/services/sqlstore/preferences.go +++ b/pkg/services/sqlstore/preferences.go @@ -1,9 +1,10 @@ package sqlstore import ( + "time" + "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" - "time" ) func init() { diff --git a/public/app/core/components/dashboard_selector.ts b/public/app/core/components/dashboard_selector.ts index ada4866f98e..913a43911fa 100644 --- a/public/app/core/components/dashboard_selector.ts +++ b/public/app/core/components/dashboard_selector.ts @@ -6,12 +6,25 @@ import $ from 'jquery'; import coreModule from 'app/core/core_module'; var template = ` + `; export class DashboardSelectorCtrl { + model: any; + options: any; /** @ngInject */ - constructor(private $scope, private $rootScope) { + constructor(private backendSrv) { + } + + $onInit() { + this.options = [{value: 0, text: 'Default'}]; + + return this.backendSrv.search({starred: true}).then(res => { + res.forEach(dash => { + this.options.push({value: dash.id, text: dash.title}); + }); + }); } } @@ -22,6 +35,9 @@ export function dashboardSelector() { bindToController: true, controllerAs: 'ctrl', template: template, + scope: { + model: '=' + } }; } diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 279f54d4e45..abebb5ce560 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -32,6 +32,7 @@ import {liveSrv} from './live/live_srv'; import {Emitter} from './utils/emitter'; import {layoutSelector} from './components/layout_selector/layout_selector'; import {switchDirective} from './components/switch'; +import {dashboardSelector} from './components/dashboard_selector'; import 'app/core/controllers/all'; import 'app/core/services/all'; import 'app/core/routes/routes'; @@ -54,4 +55,5 @@ export { infoPopover, Emitter, appEvents, + dashboardSelector, }; diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts index 92cf32448a1..ddc0ac06250 100644 --- a/public/app/core/routes/routes.ts +++ b/public/app/core/routes/routes.ts @@ -90,6 +90,7 @@ function setupAngularRoutes($routeProvider, $locationProvider) { .when('/profile', { templateUrl: 'public/app/features/profile/partials/profile.html', controller : 'ProfileCtrl', + controllerAs: 'ctrl', }) .when('/profile/password', { templateUrl: 'public/app/features/profile/partials/password.html', diff --git a/public/app/features/all.js b/public/app/features/all.js index c110bcff7cd..19ca8d7e2c9 100644 --- a/public/app/features/all.js +++ b/public/app/features/all.js @@ -7,7 +7,7 @@ define([ './playlist/all', './snapshot/all', './panel/all', - './profile/profileCtrl', + './profile/profile_ctrl', './profile/changePasswordCtrl', './profile/selectOrgCtrl', './styleguide/styleguide', diff --git a/public/app/features/org/partials/orgDetails.html b/public/app/features/org/partials/orgDetails.html index fe988570e50..b3ba0f73d83 100644 --- a/public/app/features/org/partials/orgDetails.html +++ b/public/app/features/org/partials/orgDetails.html @@ -19,7 +19,6 @@
    -

    Address

    diff --git a/public/app/features/profile/partials/profile.html b/public/app/features/profile/partials/profile.html index 0d6e5fb2951..e899b017ea6 100644 --- a/public/app/features/profile/partials/profile.html +++ b/public/app/features/profile/partials/profile.html @@ -6,35 +6,57 @@

    Profile

    - -

    Preferences

    + +

    Information

    Name - +
    Email - +
    Username - +
    +
    + +
    +
    + +
    +

    Preferences

    +
    UI Theme - +
    + +
    +
    Home Dashboard - + + +
    + +
    + +
    + +
    - +
    +

    Password

    Change Password @@ -51,7 +73,7 @@ - + {{org.name}} {{org.role}} diff --git a/public/app/features/profile/profileCtrl.js b/public/app/features/profile/profileCtrl.js deleted file mode 100644 index bae85a3d0c4..00000000000 --- a/public/app/features/profile/profileCtrl.js +++ /dev/null @@ -1,51 +0,0 @@ -define([ - 'angular', - 'app/core/config', -], -function (angular, config) { - 'use strict'; - - var module = angular.module('grafana.controllers'); - - module.controller('ProfileCtrl', function($scope, backendSrv, contextSrv, $location) { - - $scope.init = function() { - $scope.getUser(); - $scope.getUserOrgs(); - }; - - $scope.getUser = function() { - backendSrv.get('/api/user').then(function(user) { - $scope.user = user; - $scope.user.theme = user.theme || 'dark'; - $scope.old_theme = $scope.user.theme; - }); - }; - - $scope.getUserOrgs = function() { - backendSrv.get('/api/user/orgs').then(function(orgs) { - $scope.orgs = orgs; - }); - }; - - $scope.setUsingOrg = function(org) { - backendSrv.post('/api/user/using/' + org.orgId).then(function() { - window.location.href = config.appSubUrl + '/profile'; - }); - }; - - $scope.update = function() { - if (!$scope.userForm.$valid) { return; } - - backendSrv.put('/api/user/', $scope.user).then(function() { - contextSrv.user.name = $scope.user.name || $scope.user.login; - if ($scope.old_theme !== $scope.user.theme) { - window.location.href = config.appSubUrl + $location.path(); - } - }); - }; - - $scope.init(); - - }); -}); diff --git a/public/app/features/profile/profile_ctrl.ts b/public/app/features/profile/profile_ctrl.ts new file mode 100644 index 00000000000..7b37f5484be --- /dev/null +++ b/public/app/features/profile/profile_ctrl.ts @@ -0,0 +1,87 @@ +/// + +import config from 'app/core/config'; +import {coreModule} from 'app/core/core'; +import _ from 'lodash'; + +export class ProfileCtrl { + user: any; + old_theme: any; + orgs: any; + prefs: any; + userForm: any; + prefsForm: any; + + timezones: any = [ + {value: '', text: 'Default'}, + {value: 'browser', text: 'Local browser time'}, + {value: 'utc', text: 'UTC'}, + ]; + themes: any = [ + {value: '', text: 'Default'}, + {value: 'dark', text: 'Dark'}, + {value: 'light', text: 'Light'}, + ]; + + /** @ngInject **/ + constructor(private $scope, private backendSrv, private contextSrv, private $location) { + this.getUser(); + this.getUserOrgs(); + this.getUserPrefs(); + } + + getUser() { + this.backendSrv.get('/api/user').then(user => { + this.user = user; + this.user.theme = user.theme || 'dark'; + }); + } + + getUserPrefs() { + this.backendSrv.get('/api/user/preferences').then(prefs => { + this.prefs = prefs; + this.old_theme = prefs.theme; + }); + } + + getUserOrgs() { + this.backendSrv.get('/api/user/orgs').then(orgs => { + this.orgs = orgs; + }); + } + + setUsingOrg(org) { + this.backendSrv.post('/api/user/using/' + org.orgId).then(() => { + window.location.href = config.appSubUrl + '/profile'; + }); + } + + update() { + if (!this.userForm.$valid) { return; } + + this.backendSrv.put('/api/user/', this.user).then(() => { + this.contextSrv.user.name = this.user.name || this.user.login; + if (this.old_theme !== this.user.theme) { + window.location.href = config.appSubUrl + this.$location.path(); + } + }); + } + + updatePrefs() { + if (!this.prefsForm.$valid) { return; } + + var cmd = { + theme: this.prefs.theme, + timezone: this.prefs.timezone, + homeDashboardId: this.prefs.homeDashboardId + }; + + this.backendSrv.put('/api/user/preferences', cmd).then(() => { + if (this.old_theme !== cmd.theme) { + window.location.href = config.appSubUrl + this.$location.path(); + } + }); + } +} + +coreModule.controller('ProfileCtrl', ProfileCtrl); diff --git a/public/vendor/angular/angular.js b/public/vendor/angular/angular.js index 5e6fb2c363d..9ef1f59c0b7 100644 --- a/public/vendor/angular/angular.js +++ b/public/vendor/angular/angular.js @@ -30711,4 +30711,4 @@ $provide.value("$locale", { })(window, document); -!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend(''); \ No newline at end of file +!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend(''); From ab1048b7ee7fd13c3e596f0b49b38d17b03c211f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 2 Apr 2016 13:54:06 -0700 Subject: [PATCH 40/54] feat(preferences): theme and home dashbord settings now work work on profile and org settings page --- pkg/api/api.go | 8 +- pkg/api/dashboard.go | 14 ++- pkg/api/dtos/models.go | 1 + pkg/api/dtos/prefs.go | 17 ++- pkg/api/index.go | 10 +- pkg/api/preferences.go | 42 +++++--- pkg/models/preferences.go | 8 ++ pkg/models/user.go | 1 - pkg/services/sqlstore/preferences.go | 37 ++++++- pkg/services/sqlstore/user.go | 1 - public/app/core/routes/routes.ts | 9 +- public/app/features/all.js | 3 - public/app/features/org/all.js | 4 + .../change_password_ctrl.js} | 0 .../partials/change_password.html} | 0 .../app/features/org/partials/orgDetails.html | 2 + .../{profile => org}/partials/profile.html | 31 +----- .../{profile => org}/partials/select_org.html | 0 public/app/features/org/prefs_control.ts | 100 ++++++++++++++++++ .../features/{profile => org}/profile_ctrl.ts | 38 +------ .../select_org_ctrl.js} | 0 21 files changed, 218 insertions(+), 108 deletions(-) rename public/app/features/{profile/changePasswordCtrl.js => org/change_password_ctrl.js} (100%) rename public/app/features/{profile/partials/password.html => org/partials/change_password.html} (100%) rename public/app/features/{profile => org}/partials/profile.html (62%) rename public/app/features/{profile => org}/partials/select_org.html (100%) create mode 100644 public/app/features/org/prefs_control.ts rename public/app/features/{profile => org}/profile_ctrl.ts (53%) rename public/app/features/{profile/selectOrgCtrl.js => org/select_org_ctrl.js} (100%) diff --git a/pkg/api/api.go b/pkg/api/api.go index b47c632a918..5ae51b58904 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -96,12 +96,15 @@ func Register(r *macaron.Macaron) { r.Put("/", bind(m.UpdateUserCommand{}), wrap(UpdateSignedInUser)) r.Post("/using/:id", wrap(UserSetUsingOrg)) r.Get("/orgs", wrap(GetSignedInUserOrgList)) + r.Post("/stars/dashboard/:id", wrap(StarDashboard)) r.Delete("/stars/dashboard/:id", wrap(UnstarDashboard)) + r.Put("/password", bind(m.ChangeUserPasswordCommand{}), wrap(ChangeUserPassword)) r.Get("/quotas", wrap(GetUserQuotas)) + r.Get("/preferences", wrap(GetUserPreferences)) - r.Put("/preferences", bind(dtos.UpdateUserPrefsCmd{}), wrap(UpdateUserPreferences)) + r.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateUserPreferences)) }) // users (admin permission required) @@ -132,6 +135,9 @@ func Register(r *macaron.Macaron) { r.Post("/invites", quota("user"), bind(dtos.AddInviteForm{}), wrap(AddOrgInvite)) r.Patch("/invites/:code/revoke", wrap(RevokeInvite)) + // prefs + r.Get("/preferences", wrap(GetOrgPreferences)) + r.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateOrgPreferences)) }, reqOrgAdmin) // create new org diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index fc4aa59fbb0..b55a1377bd8 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -159,22 +159,20 @@ func canEditDashboard(role m.RoleType) bool { } func GetHomeDashboard(c *middleware.Context) { - // Checking if there is any preference set for home dashboard - query := m.GetPreferencesQuery{UserId: c.UserId, OrgId: c.OrgId} - - if err := bus.Dispatch(&query); err != nil { + prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId} + if err := bus.Dispatch(&prefsQuery); err != nil { c.JsonApiErr(500, "Failed to get preferences", err) } - if query.Result.HomeDashboardId != 0 { - query := m.GetDashboardSlugByIdQuery{Id: query.Result.HomeDashboardId} - err := bus.Dispatch(&query) + if prefsQuery.Result.HomeDashboardId != 0 { + slugQuery := m.GetDashboardSlugByIdQuery{Id: prefsQuery.Result.HomeDashboardId} + err := bus.Dispatch(&slugQuery) if err != nil { c.JsonApiErr(500, "Failed to get slug from database", err) return } - dashRedirect := dtos.DashboardRedirect{RedirectUri: "db/" + query.Result} + dashRedirect := dtos.DashboardRedirect{RedirectUri: "db/" + slugQuery.Result} c.JSON(200, &dashRedirect) return } diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index a95bd464f35..8db36be2140 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -33,6 +33,7 @@ type CurrentUser struct { OrgRole m.RoleType `json:"orgRole"` IsGrafanaAdmin bool `json:"isGrafanaAdmin"` GravatarUrl string `json:"gravatarUrl"` + Timezone string `json:"timezone"` } type DashboardMeta struct { diff --git a/pkg/api/dtos/prefs.go b/pkg/api/dtos/prefs.go index 77ef0a1da14..97e20bb4011 100644 --- a/pkg/api/dtos/prefs.go +++ b/pkg/api/dtos/prefs.go @@ -1,15 +1,12 @@ package dtos -type UserPrefs struct { - Theme string `json:"theme"` - ThemeDefault string `json:"themeDefault"` - HomeDashboardId int64 `json:"homeDashboardId"` - HomeDashboardIdDefault int64 `json:"homeDashboardIdDefault"` - Timezone string `json:"timezone"` - TimezoneDefault string `json:"timezoneDefault"` -} - -type UpdateUserPrefsCmd struct { +type Prefs struct { + Theme string `json:"theme"` + HomeDashboardId int64 `json:"homeDashboardId"` + Timezone string `json:"timezone"` +} + +type UpdatePrefsCmd struct { Theme string `json:"theme"` HomeDashboardId int64 `json:"homeDashboardId"` Timezone string `json:"timezone"` diff --git a/pkg/api/index.go b/pkg/api/index.go index a376f9504a4..575ea35cfaf 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -2,6 +2,7 @@ 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" @@ -14,6 +15,12 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { return nil, err } + prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId} + if err := bus.Dispatch(&prefsQuery); err != nil { + return nil, err + } + prefs := prefsQuery.Result + var data = dtos.IndexViewData{ User: &dtos.CurrentUser{ Id: c.UserId, @@ -21,12 +28,13 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { Login: c.Login, Email: c.Email, Name: c.Name, - LightTheme: c.Theme == "light", OrgId: c.OrgId, OrgName: c.OrgName, OrgRole: c.OrgRole, GravatarUrl: dtos.GetGravatarUrl(c.Email), IsGrafanaAdmin: c.IsGrafanaAdmin, + LightTheme: prefs.Theme == "light", + Timezone: prefs.Timezone, }, Settings: settings, AppUrl: setting.AppUrl, diff --git a/pkg/api/preferences.go b/pkg/api/preferences.go index ed51ddaa9ae..795b8994470 100644 --- a/pkg/api/preferences.go +++ b/pkg/api/preferences.go @@ -22,34 +22,52 @@ func SetHomeDashboard(c *middleware.Context, cmd m.SavePreferencesCommand) Respo // GET /api/user/preferences func GetUserPreferences(c *middleware.Context) Response { - userPrefs := m.GetPreferencesQuery{UserId: c.UserId, OrgId: c.OrgId} + return getPreferencesFor(c.OrgId, c.UserId) +} - if err := bus.Dispatch(&userPrefs); err != nil { - c.JsonApiErr(500, "Failed to get preferences", err) +func getPreferencesFor(orgId int64, userId int64) Response { + prefsQuery := m.GetPreferencesQuery{UserId: userId, OrgId: orgId} + + if err := bus.Dispatch(&prefsQuery); err != nil { + return ApiError(500, "Failed to get preferences", err) } - dto := dtos.UserPrefs{ - Theme: userPrefs.Result.Theme, - HomeDashboardId: userPrefs.Result.HomeDashboardId, - Timezone: userPrefs.Result.Timezone, + dto := dtos.Prefs{ + Theme: prefsQuery.Result.Theme, + HomeDashboardId: prefsQuery.Result.HomeDashboardId, + Timezone: prefsQuery.Result.Timezone, } return Json(200, &dto) } // PUT /api/user/preferences -func UpdateUserPreferences(c *middleware.Context, dtoCmd dtos.UpdateUserPrefsCmd) Response { +func UpdateUserPreferences(c *middleware.Context, dtoCmd dtos.UpdatePrefsCmd) Response { + return updatePreferencesFor(c.OrgId, c.UserId, &dtoCmd) +} + +func updatePreferencesFor(orgId int64, userId int64, dtoCmd *dtos.UpdatePrefsCmd) Response { saveCmd := m.SavePreferencesCommand{ - UserId: c.UserId, - OrgId: c.OrgId, + UserId: userId, + OrgId: orgId, Theme: dtoCmd.Theme, Timezone: dtoCmd.Timezone, HomeDashboardId: dtoCmd.HomeDashboardId, } if err := bus.Dispatch(&saveCmd); err != nil { - c.JsonApiErr(500, "Failed to save preferences", err) + return ApiError(500, "Failed to save preferences", err) } - return ApiSuccess("User preferences updated") + return ApiSuccess("Preferences updated") +} + +// GET /api/org/preferences +func GetOrgPreferences(c *middleware.Context) Response { + return getPreferencesFor(c.OrgId, 0) +} + +// PUT /api/org/preferences +func UpdateOrgPreferences(c *middleware.Context, dtoCmd dtos.UpdatePrefsCmd) Response { + return updatePreferencesFor(c.OrgId, 0, &dtoCmd) } diff --git a/pkg/models/preferences.go b/pkg/models/preferences.go index 523a3bfc83f..4c77bc96d4d 100644 --- a/pkg/models/preferences.go +++ b/pkg/models/preferences.go @@ -33,6 +33,14 @@ type GetPreferencesQuery struct { Result *Preferences } +type GetPreferencesWithDefaultsQuery struct { + Id int64 + OrgId int64 + UserId int64 + + Result *Preferences +} + // --------------------- // COMMANDS type SavePreferencesCommand struct { diff --git a/pkg/models/user.go b/pkg/models/user.go index 2842bad490d..a231156b7b0 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -136,7 +136,6 @@ type SignedInUser struct { Login string Name string Email string - Theme string ApiKeyId int64 IsGrafanaAdmin bool } diff --git a/pkg/services/sqlstore/preferences.go b/pkg/services/sqlstore/preferences.go index 1b5fa79d879..d120c485ed3 100644 --- a/pkg/services/sqlstore/preferences.go +++ b/pkg/services/sqlstore/preferences.go @@ -9,9 +9,44 @@ import ( func init() { bus.AddHandler("sql", GetPreferences) + bus.AddHandler("sql", GetPreferencesWithDefaults) bus.AddHandler("sql", SavePreferences) } +func GetPreferencesWithDefaults(query *m.GetPreferencesWithDefaultsQuery) error { + + prefs := make([]*m.Preferences, 0) + filter := "(org_id=? AND user_id=?) OR (org_id=? AND user_id=0)" + err := x.Where(filter, query.OrgId, query.UserId, query.OrgId). + OrderBy("user_id ASC"). + Find(&prefs) + + if err != nil { + return err + } + + res := &m.Preferences{ + Theme: "dark", + Timezone: "browser", + HomeDashboardId: 0, + } + + for _, p := range prefs { + if p.Theme != "" { + res.Theme = p.Theme + } + if p.Timezone != "" { + res.Timezone = p.Timezone + } + if p.HomeDashboardId != 0 { + res.HomeDashboardId = p.HomeDashboardId + } + } + + query.Result = res + return nil +} + func GetPreferences(query *m.GetPreferencesQuery) error { var prefs m.Preferences @@ -54,7 +89,7 @@ func SavePreferences(cmd *m.SavePreferencesCommand) error { prefs.Theme = cmd.Theme prefs.Updated = time.Now() prefs.Version += 1 - _, err = sess.Id(prefs.Id).Update(&prefs) + _, err := sess.Id(prefs.Id).AllCols().Update(&prefs) return err } }) diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go index 96b8c24b8fc..623e85ec472 100644 --- a/pkg/services/sqlstore/user.go +++ b/pkg/services/sqlstore/user.go @@ -273,7 +273,6 @@ func GetSignedInUser(query *m.GetSignedInUserQuery) error { u.email as email, u.login as login, u.name as name, - u.theme as theme, org.name as org_name, org_user.role as org_role, org.id as org_id diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts index ddc0ac06250..c258fb1a611 100644 --- a/public/app/core/routes/routes.ts +++ b/public/app/core/routes/routes.ts @@ -88,17 +88,20 @@ function setupAngularRoutes($routeProvider, $locationProvider) { resolve: loadOrgBundle, }) .when('/profile', { - templateUrl: 'public/app/features/profile/partials/profile.html', + templateUrl: 'public/app/features/org/partials/profile.html', controller : 'ProfileCtrl', controllerAs: 'ctrl', + resolve: loadOrgBundle, }) .when('/profile/password', { - templateUrl: 'public/app/features/profile/partials/password.html', + templateUrl: 'public/app/features/org/partials/password.html', controller : 'ChangePasswordCtrl', + resolve: loadOrgBundle, }) .when('/profile/select-org', { - templateUrl: 'public/app/features/profile/partials/select_org.html', + templateUrl: 'public/app/features/org/partials/select_org.html', controller : 'SelectOrgCtrl', + resolve: loadOrgBundle, }) // ADMIN .when('/admin', { diff --git a/public/app/features/all.js b/public/app/features/all.js index 19ca8d7e2c9..43d9f33f190 100644 --- a/public/app/features/all.js +++ b/public/app/features/all.js @@ -7,8 +7,5 @@ define([ './playlist/all', './snapshot/all', './panel/all', - './profile/profile_ctrl', - './profile/changePasswordCtrl', - './profile/selectOrgCtrl', './styleguide/styleguide', ], function () {}); diff --git a/public/app/features/org/all.js b/public/app/features/org/all.js index e04634d709a..b610a20e686 100644 --- a/public/app/features/org/all.js +++ b/public/app/features/org/all.js @@ -1,7 +1,11 @@ define([ './org_users_ctrl', + './profile_ctrl', + './org_users_ctrl', + './select_org_ctrl', './newOrgCtrl', './userInviteCtrl', './orgApiKeysCtrl', './orgDetailsCtrl', + './prefs_control', ], function () {}); diff --git a/public/app/features/profile/changePasswordCtrl.js b/public/app/features/org/change_password_ctrl.js similarity index 100% rename from public/app/features/profile/changePasswordCtrl.js rename to public/app/features/org/change_password_ctrl.js diff --git a/public/app/features/profile/partials/password.html b/public/app/features/org/partials/change_password.html similarity index 100% rename from public/app/features/profile/partials/password.html rename to public/app/features/org/partials/change_password.html diff --git a/public/app/features/org/partials/orgDetails.html b/public/app/features/org/partials/orgDetails.html index b3ba0f73d83..5f264517b0d 100644 --- a/public/app/features/org/partials/orgDetails.html +++ b/public/app/features/org/partials/orgDetails.html @@ -19,6 +19,8 @@
    + +

    Address

    diff --git a/public/app/features/profile/partials/profile.html b/public/app/features/org/partials/profile.html similarity index 62% rename from public/app/features/profile/partials/profile.html rename to public/app/features/org/partials/profile.html index e899b017ea6..811891ae317 100644 --- a/public/app/features/profile/partials/profile.html +++ b/public/app/features/org/partials/profile.html @@ -26,36 +26,7 @@
    -
    -

    Preferences

    - -
    - UI Theme -
    - -
    -
    - -
    - Home Dashboard - - -
    - -
    - -
    - -
    -
    - -
    - -
    -
    - +

    Password

    diff --git a/public/app/features/profile/partials/select_org.html b/public/app/features/org/partials/select_org.html similarity index 100% rename from public/app/features/profile/partials/select_org.html rename to public/app/features/org/partials/select_org.html diff --git a/public/app/features/org/prefs_control.ts b/public/app/features/org/prefs_control.ts new file mode 100644 index 00000000000..bd1cee06bac --- /dev/null +++ b/public/app/features/org/prefs_control.ts @@ -0,0 +1,100 @@ +/// + +import config from 'app/core/config'; +import _ from 'lodash'; +import coreModule from '../../core/core_module'; + +export class PrefsControlCtrl { + prefs: any; + oldTheme: any; + prefsForm: any; + mode: string; + + timezones: any = [ + {value: '', text: 'Default'}, + {value: 'browser', text: 'Local browser time'}, + {value: 'utc', text: 'UTC'}, + ]; + themes: any = [ + {value: '', text: 'Default'}, + {value: 'dark', text: 'Dark'}, + {value: 'light', text: 'Light'}, + ]; + + /** @ngInject **/ + constructor(private backendSrv, private $location) { + } + + $onInit() { + return this.backendSrv.get(`/api/${this.mode}/preferences`).then(prefs => { + this.prefs = prefs; + this.oldTheme = prefs.theme; + }); + } + + updatePrefs() { + if (!this.prefsForm.$valid) { return; } + + var cmd = { + theme: this.prefs.theme, + timezone: this.prefs.timezone, + homeDashboardId: this.prefs.homeDashboardId + }; + + this.backendSrv.put(`/api/${this.mode}/preferences`, cmd).then(() => { + if (this.oldTheme !== cmd.theme) { + window.location.href = config.appSubUrl + this.$location.path(); + } + }); + } + +} + +var template = ` +
    +

    Preferences

    + +
    + UI Theme +
    + +
    +
    + +
    + Home Dashboard + + +
    + +
    + +
    + +
    +
    + +
    + +
    +
    +`; + +export function prefsControlDirective() { + return { + restrict: 'E', + controller: PrefsControlCtrl, + bindToController: true, + controllerAs: 'ctrl', + template: template, + scope: { + mode: "@" + } + }; +} + +coreModule.directive('prefsControl', prefsControlDirective); + + diff --git a/public/app/features/profile/profile_ctrl.ts b/public/app/features/org/profile_ctrl.ts similarity index 53% rename from public/app/features/profile/profile_ctrl.ts rename to public/app/features/org/profile_ctrl.ts index 7b37f5484be..65bff1f6c12 100644 --- a/public/app/features/profile/profile_ctrl.ts +++ b/public/app/features/org/profile_ctrl.ts @@ -8,26 +8,12 @@ export class ProfileCtrl { user: any; old_theme: any; orgs: any; - prefs: any; userForm: any; - prefsForm: any; - - timezones: any = [ - {value: '', text: 'Default'}, - {value: 'browser', text: 'Local browser time'}, - {value: 'utc', text: 'UTC'}, - ]; - themes: any = [ - {value: '', text: 'Default'}, - {value: 'dark', text: 'Dark'}, - {value: 'light', text: 'Light'}, - ]; /** @ngInject **/ - constructor(private $scope, private backendSrv, private contextSrv, private $location) { + constructor(private backendSrv, private contextSrv, private $location) { this.getUser(); this.getUserOrgs(); - this.getUserPrefs(); } getUser() { @@ -37,13 +23,6 @@ export class ProfileCtrl { }); } - getUserPrefs() { - this.backendSrv.get('/api/user/preferences').then(prefs => { - this.prefs = prefs; - this.old_theme = prefs.theme; - }); - } - getUserOrgs() { this.backendSrv.get('/api/user/orgs').then(orgs => { this.orgs = orgs; @@ -67,21 +46,6 @@ export class ProfileCtrl { }); } - updatePrefs() { - if (!this.prefsForm.$valid) { return; } - - var cmd = { - theme: this.prefs.theme, - timezone: this.prefs.timezone, - homeDashboardId: this.prefs.homeDashboardId - }; - - this.backendSrv.put('/api/user/preferences', cmd).then(() => { - if (this.old_theme !== cmd.theme) { - window.location.href = config.appSubUrl + this.$location.path(); - } - }); - } } coreModule.controller('ProfileCtrl', ProfileCtrl); diff --git a/public/app/features/profile/selectOrgCtrl.js b/public/app/features/org/select_org_ctrl.js similarity index 100% rename from public/app/features/profile/selectOrgCtrl.js rename to public/app/features/org/select_org_ctrl.js From 55d95f90092d499afdcd8e14a36de60a3112bd40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 3 Apr 2016 05:27:35 -0700 Subject: [PATCH 41/54] feat(prefs): moved context srv to typescript --- public/app/core/services/context_srv.js | 47 ----------------- public/app/core/services/context_srv.ts | 67 +++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 47 deletions(-) delete mode 100644 public/app/core/services/context_srv.js create mode 100644 public/app/core/services/context_srv.ts diff --git a/public/app/core/services/context_srv.js b/public/app/core/services/context_srv.js deleted file mode 100644 index b4a133afea3..00000000000 --- a/public/app/core/services/context_srv.js +++ /dev/null @@ -1,47 +0,0 @@ -define([ - 'angular', - 'lodash', - '../core_module', - 'app/core/store', - 'app/core/config', -], -function (angular, _, coreModule, store, config) { - 'use strict'; - - coreModule.default.service('contextSrv', function() { - - function User() { - if (config.bootData.user) { - _.extend(this, config.bootData.user); - } - } - - this.hasRole = function(role) { - return this.user.orgRole === role; - }; - - this.setPinnedState = function(val) { - this.pinned = val; - store.set('grafana.sidemenu.pinned', val); - }; - - this.toggleSideMenu = function() { - this.sidemenu = !this.sidemenu; - if (!this.sidemenu) { - this.setPinnedState(false); - } - }; - - this.pinned = store.getBool('grafana.sidemenu.pinned', false); - if (this.pinned) { - this.sidemenu = true; - } - - this.version = config.buildInfo.version; - this.lightTheme = false; - this.user = new User(); - this.isSignedIn = this.user.isSignedIn; - this.isGrafanaAdmin = this.user.isGrafanaAdmin; - this.isEditor = this.hasRole('Editor') || this.hasRole('Admin'); - }); -}); diff --git a/public/app/core/services/context_srv.ts b/public/app/core/services/context_srv.ts new file mode 100644 index 00000000000..0ce00c5f444 --- /dev/null +++ b/public/app/core/services/context_srv.ts @@ -0,0 +1,67 @@ +/// + +import config from 'app/core/config'; +import _ from 'lodash'; +import $ from 'jquery'; +import coreModule from 'app/core/core_module'; +import store from 'app/core/store'; + +export class User { + isGrafanaAdmin: any; + isSignedIn: any; + orgRole: any; + + constructor() { + if (config.bootData.user) { + _.extend(this, config.bootData.user); + } + } +} + +export class ContextSrv { + pinned: any; + version: any; + user: User; + isSignedIn: any; + isGrafanaAdmin: any; + isEditor: any; + sidemenu: any; + lightTheme: any; + + constructor() { + this.pinned = store.getBool('grafana.sidemenu.pinned', false); + if (this.pinned) { + this.sidemenu = true; + } + + this.version = config.buildInfo.version; + this.lightTheme = false; + this.user = new User(); + this.isSignedIn = this.user.isSignedIn; + this.isGrafanaAdmin = this.user.isGrafanaAdmin; + this.isEditor = this.hasRole('Editor') || this.hasRole('Admin'); + } + + hasRole(role) { + return this.user.orgRole === role; + } + + setPinnedState(val) { + this.pinned = val; + store.set('grafana.sidemenu.pinned', val); + } + + toggleSideMenu() { + this.sidemenu = !this.sidemenu; + if (!this.sidemenu) { + this.setPinnedState(false); + } + } +} + +var contextSrv = new ContextSrv(); +export {contextSrv}; + +coreModule.factory('contextSrv', function() { + return contextSrv; +}); From a2c6469d416a03c84accd7cceba0d94802b517f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 3 Apr 2016 05:32:15 -0700 Subject: [PATCH 42/54] fix(): A Datasource with null jsonData would make Grafana fail to load page, fixes #4536 --- CHANGELOG.md | 1 + pkg/api/frontendsettings.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e810a7801f..586cf96f56e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Bug fixes * **Dashboard**: Fixed dashboard panel layout for mobile devices, fixes [#4529](https://github.com/grafana/grafana/issues/4529) +* **Page Load Crash**: A Datasource with null jsonData would make Grafana fail to load page, fixes [#4536](https://github.com/grafana/grafana/issues/4536) # 3.0.0-beta1 (2016-03-31) diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index c84d7faccff..f23f416b47a 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -59,7 +59,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro defaultDatasource = ds.Name } - if len(ds.JsonData.MustMap()) > 0 { + if ds.JsonData != nil { dsMap["jsonData"] = ds.JsonData } From b30b78e44264e89a729bd280ae67407ded610a3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 3 Apr 2016 06:05:43 -0700 Subject: [PATCH 43/54] feat(preferences): got timezone option to work on org and profile level, as well as dashboard --- public/app/features/dashboard/dashboardCtrl.js | 4 ++++ public/app/features/dashboard/dashboardSrv.js | 12 ++++++++++-- public/app/features/dashboard/partials/settings.html | 2 +- .../app/features/dashboard/timepicker/timepicker.ts | 4 ++-- public/app/features/org/prefs_control.ts | 6 ++---- public/app/plugins/panel/graph/graph.js | 2 +- public/app/plugins/panel/table/module.ts | 2 +- public/app/plugins/panel/table/renderer.ts | 4 ++-- 8 files changed, 23 insertions(+), 13 deletions(-) diff --git a/public/app/features/dashboard/dashboardCtrl.js b/public/app/features/dashboard/dashboardCtrl.js index ea0d91019b8..b6702631155 100644 --- a/public/app/features/dashboard/dashboardCtrl.js +++ b/public/app/features/dashboard/dashboardCtrl.js @@ -134,6 +134,10 @@ function (angular, $, config, moment) { }); }; + $scope.timezoneChanged = function() { + $rootScope.$broadcast("refresh"); + }; + $scope.formatDate = function(date) { return moment(date).format('MMM Do YYYY, h:mm:ss a'); }; diff --git a/public/app/features/dashboard/dashboardSrv.js b/public/app/features/dashboard/dashboardSrv.js index b3194868d97..ed119f6e23f 100644 --- a/public/app/features/dashboard/dashboardSrv.js +++ b/public/app/features/dashboard/dashboardSrv.js @@ -9,7 +9,7 @@ function (angular, $, _, moment) { var module = angular.module('grafana.services'); - module.factory('dashboardSrv', function() { + module.factory('dashboardSrv', function(contextSrv) { function DashboardModel (data, meta) { if (!data) { @@ -25,7 +25,7 @@ function (angular, $, _, moment) { this.originalTitle = this.title; this.tags = data.tags || []; this.style = data.style || "dark"; - this.timezone = data.timezone || 'browser'; + this.timezone = data.timezone || ''; this.editable = data.editable !== false; this.hideControls = data.hideControls || false; this.sharedCrosshair = data.sharedCrosshair || false; @@ -208,6 +208,14 @@ function (angular, $, _, moment) { }); }; + p.isTimezoneUtc = function() { + return this.getTimezone() === 'utc'; + }; + + p.getTimezone = function() { + return this.timezone ? this.timezone : contextSrv.user.timezone; + }; + p._updateSchema = function(old) { var i, j, k; var oldVersion = this.schemaVersion; diff --git a/public/app/features/dashboard/partials/settings.html b/public/app/features/dashboard/partials/settings.html index ca591c0c481..2a2287613ae 100644 --- a/public/app/features/dashboard/partials/settings.html +++ b/public/app/features/dashboard/partials/settings.html @@ -34,7 +34,7 @@
    - +
    diff --git a/public/app/features/dashboard/timepicker/timepicker.ts b/public/app/features/dashboard/timepicker/timepicker.ts index 453eaaf3204..2f757d36357 100644 --- a/public/app/features/dashboard/timepicker/timepicker.ts +++ b/public/app/features/dashboard/timepicker/timepicker.ts @@ -44,7 +44,7 @@ export class TimePickerCtrl { var time = angular.copy(this.timeSrv.timeRange()); var timeRaw = angular.copy(this.timeSrv.timeRange(false)); - if (this.dashboard.timezone === 'browser') { + if (!this.dashboard.isTimezoneUtc()) { time.from.local(); time.to.local(); if (moment.isMoment(timeRaw.from)) { @@ -125,7 +125,7 @@ export class TimePickerCtrl { } getAbsoluteMomentForTimezone(jsDate) { - return this.dashboard.timezone === 'browser' ? moment(jsDate) : moment(jsDate).utc(); + return this.dashboard.isTimezoneUtc() ? moment(jsDate).utc() : moment(jsDate); } setRelativeFilter(timespan) { diff --git a/public/app/features/org/prefs_control.ts b/public/app/features/org/prefs_control.ts index bd1cee06bac..53704d5e390 100644 --- a/public/app/features/org/prefs_control.ts +++ b/public/app/features/org/prefs_control.ts @@ -2,7 +2,7 @@ import config from 'app/core/config'; import _ from 'lodash'; -import coreModule from '../../core/core_module'; +import coreModule from 'app/core/core_module'; export class PrefsControlCtrl { prefs: any; @@ -42,9 +42,7 @@ export class PrefsControlCtrl { }; this.backendSrv.put(`/api/${this.mode}/preferences`, cmd).then(() => { - if (this.oldTheme !== cmd.theme) { - window.location.href = config.appSubUrl + this.$location.path(); - } + window.location.href = config.appSubUrl + this.$location.path(); }); } diff --git a/public/app/plugins/panel/graph/graph.js b/public/app/plugins/panel/graph/graph.js index 0443a294eed..4e948bf8e5b 100755 --- a/public/app/plugins/panel/graph/graph.js +++ b/public/app/plugins/panel/graph/graph.js @@ -279,7 +279,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) { var max = _.isUndefined(ctrl.range.to) ? null : ctrl.range.to.valueOf(); options.xaxis = { - timezone: dashboard.timezone, + timezone: dashboard.getTimezone(), show: panel['x-axis'], mode: "time", min: min, diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index d5ac802a62b..2d39d349d9e 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -155,7 +155,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { } function appendTableRows(tbodyElem) { - var renderer = new TableRenderer(panel, data, ctrl.dashboard.timezone); + var renderer = new TableRenderer(panel, data, ctrl.dashboard.isTimezoneUtc()); tbodyElem.empty(); tbodyElem.html(renderer.render(ctrl.pageIndex)); } diff --git a/public/app/plugins/panel/table/renderer.ts b/public/app/plugins/panel/table/renderer.ts index 0a306b103a4..c00656071be 100644 --- a/public/app/plugins/panel/table/renderer.ts +++ b/public/app/plugins/panel/table/renderer.ts @@ -8,7 +8,7 @@ export class TableRenderer { formaters: any[]; colorState: any; - constructor(private panel, private table, private timezone) { + constructor(private panel, private table, private isUtc) { this.formaters = []; this.colorState = {}; } @@ -45,7 +45,7 @@ export class TableRenderer { return v => { if (_.isArray(v)) { v = v[0]; } var date = moment(v); - if (this.timezone === 'utc') { + if (this.isUtc) { date = date.utc(); } return date.format(style.dateFormat); From 6e6ebc59477503561c8f2da2a4f68559d1bb47fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 3 Apr 2016 09:43:33 -0400 Subject: [PATCH 44/54] trying to fix karma issue --- karma.conf.js | 1 - package.json | 4 ++-- public/app/features/dashboard/dashnav/dashnav.ts | 9 +-------- public/vendor/angular/.bower.json | 2 +- public/vendor/angular/angular.js | 2 +- 5 files changed, 5 insertions(+), 13 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index cbb0abff7c6..dc082924b11 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -9,7 +9,6 @@ module.exports = function(config) { // list of files / patterns to load in the browser files: [ 'vendor/npm/es6-shim/es6-shim.js', - 'vendor/npm/es6-promise/dist/es6-promise.js', 'vendor/npm/systemjs/dist/system.src.js', 'test/test-main.js', diff --git a/package.json b/package.json index 4b526f5a4f3..d741bab574e 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "grunt-contrib-watch": "^0.6.1", "grunt-filerev": "^0.2.1", "grunt-git-describe": "~2.3.2", - "grunt-karma": "~0.12.1", + "grunt-karma": "~0.12.2", "grunt-ng-annotate": "^1.0.1", "grunt-notify": "^0.4.3", "grunt-postcss": "^0.8.0", @@ -43,7 +43,7 @@ "grunt-typescript": "^0.8.0", "grunt-usemin": "3.0.0", "jshint-stylish": "~2.1.0", - "karma": "~0.13.15", + "karma": "0.13.22", "karma-chrome-launcher": "~0.2.2", "karma-coverage": "0.5.3", "karma-coveralls": "1.1.2", diff --git a/public/app/features/dashboard/dashnav/dashnav.ts b/public/app/features/dashboard/dashnav/dashnav.ts index 14dfa8cc67a..2d5b66cb13c 100644 --- a/public/app/features/dashboard/dashnav/dashnav.ts +++ b/public/app/features/dashboard/dashnav/dashnav.ts @@ -7,7 +7,7 @@ import angular from 'angular'; export class DashNavCtrl { /** @ngInject */ - constructor($scope, $rootScope, alertSrv, $location, playlistSrv, backendSrv, contextSrv, $timeout) { + constructor($scope, $rootScope, alertSrv, $location, playlistSrv, backendSrv, $timeout) { $scope.init = function() { $scope.onAppEvent('save-dashboard', $scope.saveDashboard); @@ -103,13 +103,6 @@ export class DashNavCtrl { }, $scope.handleSaveDashError); }; - $scope.saveDashboardAsHome = function() { - // TODO: this backend method needs to be implemented - backendSrv.post('/api/preferences/set-home-dash', { - homeDashboardId: $scope.dashboard.id - }); - }; - $scope.handleSaveDashError = function(err) { if (err.data && err.data.status === "version-mismatch") { err.isHandled = true; diff --git a/public/vendor/angular/.bower.json b/public/vendor/angular/.bower.json index b7b5e0bb59f..4221ce7cb09 100644 --- a/public/vendor/angular/.bower.json +++ b/public/vendor/angular/.bower.json @@ -13,6 +13,6 @@ "commit": "5a07c5107b4d24f41744a02b07717d55bad88e70" }, "_source": "git://github.com/angular/bower-angular.git", - "_target": "1.5.3", + "_target": "~1.5.3", "_originalSource": "angular" } \ No newline at end of file diff --git a/public/vendor/angular/angular.js b/public/vendor/angular/angular.js index 9ef1f59c0b7..5e6fb2c363d 100644 --- a/public/vendor/angular/angular.js +++ b/public/vendor/angular/angular.js @@ -30711,4 +30711,4 @@ $provide.value("$locale", { })(window, document); -!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend(''); +!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend(''); \ No newline at end of file From 431a610f0015171984f0d2f166ca63161ab6b7e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 3 Apr 2016 10:12:43 -0400 Subject: [PATCH 45/54] fix(): fixed failing unit test --- public/app/core/services/context_srv.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/public/app/core/services/context_srv.ts b/public/app/core/services/context_srv.ts index 0ce00c5f444..ad670f68a32 100644 --- a/public/app/core/services/context_srv.ts +++ b/public/app/core/services/context_srv.ts @@ -2,7 +2,6 @@ import config from 'app/core/config'; import _ from 'lodash'; -import $ from 'jquery'; import coreModule from 'app/core/core_module'; import store from 'app/core/store'; @@ -34,6 +33,13 @@ export class ContextSrv { this.sidemenu = true; } + if (!config.buildInfo) { + config.buildInfo = {}; + } + if (!config.bootData) { + config.bootData = {user: {}, settings: {}}; + } + this.version = config.buildInfo.version; this.lightTheme = false; this.user = new User(); From a12de0953364aa67adb62764627f325c601011d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 3 Apr 2016 10:21:11 -0400 Subject: [PATCH 46/54] changelog(): updated with preferences features --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 586cf96f56e..b14626c3126 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # 3.0.0-beta2 (unreleased) +### New Features (introduces since 3.0-beta1) +* **Preferences**: Set home dashboard on user and org level, closes [#1678](https://github.com/grafana/grafana/issues/1678) +* **Preferences**: Set timezone on user and org level, closes [#3214](https://github.com/grafana/grafana/issues/3214), [#1200](https://github.com/grafana/grafana/issues/1200) +* **Preferences**: Set theme on user and org level, closes [#3214](https://github.com/grafana/grafana/issues/3214), [#1917](https://github.com/grafana/grafana/issues/1917) + ### Bug fixes * **Dashboard**: Fixed dashboard panel layout for mobile devices, fixes [#4529](https://github.com/grafana/grafana/issues/4529) * **Page Load Crash**: A Datasource with null jsonData would make Grafana fail to load page, fixes [#4536](https://github.com/grafana/grafana/issues/4536) From 105a678d642417197844b0c85c2a4636e013b200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 3 Apr 2016 10:28:31 -0400 Subject: [PATCH 47/54] fix(): another test fix --- .../app/plugins/panel/graph/specs/graph_specs.ts | 4 +++- public/test/specs/dashboardSrv-specs.js | 4 ++++ public/test/specs/dynamicDashboardSrv-specs.js | 15 ++++++++++----- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/panel/graph/specs/graph_specs.ts b/public/app/plugins/panel/graph/specs/graph_specs.ts index 1a708696680..b9c9362e5de 100644 --- a/public/app/plugins/panel/graph/specs/graph_specs.ts +++ b/public/app/plugins/panel/graph/specs/graph_specs.ts @@ -53,7 +53,9 @@ describe('grafanaGraph', function() { }, renderingCompleted: sinon.spy(), hiddenSeries: {}, - dashboard: {timezone: 'browser'}, + dashboard: { + getTimezone: sinon.stub().returns('browser') + }, range: { from: moment([2015, 1, 1, 10]), to: moment([2015, 1, 1, 22]), diff --git a/public/test/specs/dashboardSrv-specs.js b/public/test/specs/dashboardSrv-specs.js index e02495c1236..6fc53f09190 100644 --- a/public/test/specs/dashboardSrv-specs.js +++ b/public/test/specs/dashboardSrv-specs.js @@ -7,6 +7,10 @@ define([ var _dashboardSrv; beforeEach(module('grafana.services')); + beforeEach(module(function($provide) { + $provide.value('contextSrv', { + }); + })); beforeEach(inject(function(dashboardSrv) { _dashboardSrv = dashboardSrv; diff --git a/public/test/specs/dynamicDashboardSrv-specs.js b/public/test/specs/dynamicDashboardSrv-specs.js index 0104081b768..b988203009a 100644 --- a/public/test/specs/dynamicDashboardSrv-specs.js +++ b/public/test/specs/dynamicDashboardSrv-specs.js @@ -12,6 +12,11 @@ define([ ctx.setup = function (setupFunc) { beforeEach(module('grafana.services')); + beforeEach(module(function($provide) { + $provide.value('contextSrv', { + user: { timezone: 'utc'} + }); + })); beforeEach(inject(function(dynamicDashboardSrv, dashboardSrv) { ctx.dynamicDashboardSrv = dynamicDashboardSrv; @@ -45,10 +50,10 @@ define([ value: ['se1', 'se2', 'se3'] }, options: [ - {text: 'se1', value: 'se1', selected: true}, - {text: 'se2', value: 'se2', selected: true}, - {text: 'se3', value: 'se3', selected: true}, - {text: 'se4', value: 'se4', selected: false} + {text: 'se1', value: 'se1', selected: true}, + {text: 'se2', value: 'se2', selected: true}, + {text: 'se3', value: 'se3', selected: true}, + {text: 'se4', value: 'se4', selected: false} ] }); }); @@ -93,7 +98,7 @@ define([ describe('After a second iteration and selected values reduced', function() { beforeEach(function() { ctx.dash.templating.list[0].options[1].selected = false; - + ctx.dynamicDashboardSrv.update(ctx.dash); }); From d8499e6941c8c758feb98a2b4aad571c7ae833e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 3 Apr 2016 15:51:54 -0400 Subject: [PATCH 48/54] fix(influxdb): added regex start string char to templating suggestion, fixes #4405 --- public/app/plugins/datasource/influxdb/query_ctrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/influxdb/query_ctrl.ts b/public/app/plugins/datasource/influxdb/query_ctrl.ts index d59bbbbccd5..041e3c8a882 100644 --- a/public/app/plugins/datasource/influxdb/query_ctrl.ts +++ b/public/app/plugins/datasource/influxdb/query_ctrl.ts @@ -193,7 +193,7 @@ export class InfluxQueryCtrl extends QueryCtrl { if (addTemplateVars) { for (let variable of this.templateSrv.variables) { - segments.unshift(this.uiSegmentSrv.newSegment({ type: 'template', value: '/$' + variable.name + '$/', expandable: true })); + segments.unshift(this.uiSegmentSrv.newSegment({ type: 'template', value: '/^$' + variable.name + '$/', expandable: true })); } } From 1c97f10d8a4757bdaa100cc63efe0503bfc0a86f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 3 Apr 2016 16:01:48 -0400 Subject: [PATCH 49/54] fix(table panel): fixed column sort issue with table panel, #4532 --- CHANGELOG.md | 1 + public/app/plugins/panel/table/module.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b14626c3126..e20da1142d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Bug fixes * **Dashboard**: Fixed dashboard panel layout for mobile devices, fixes [#4529](https://github.com/grafana/grafana/issues/4529) +* **Table Panel**: Fixed issue with table panel sort, fixes [#4532](https://github.com/grafana/grafana/issues/4532) * **Page Load Crash**: A Datasource with null jsonData would make Grafana fail to load page, fixes [#4536](https://github.com/grafana/grafana/issues/4536) # 3.0.0-beta1 (2016-03-31) diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index 2d39d349d9e..91fe1a5fb8b 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -131,7 +131,9 @@ class TablePanelCtrl extends MetricsPanelCtrl { this.panel.sort.desc = true; } - this.render(); + this.table = transformDataToTable(this.dataRaw, this.panel); + this.table.sort(this.panel.sort); + this.render(this.table); } exportCsv() { From c764e7fd974dd1695459efd97e7ec96cea93ab01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 3 Apr 2016 16:11:42 -0400 Subject: [PATCH 50/54] fix(elasticsearch): add a better default terms size limit of 10, fixes #4013 --- public/app/plugins/datasource/elasticsearch/bucket_agg.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/elasticsearch/bucket_agg.js b/public/app/plugins/datasource/elasticsearch/bucket_agg.js index 47d88a6ea3c..18e2f3cf3b1 100644 --- a/public/app/plugins/datasource/elasticsearch/bucket_agg.js +++ b/public/app/plugins/datasource/elasticsearch/bucket_agg.js @@ -77,7 +77,7 @@ function (angular, _, queryDef) { switch($scope.agg.type) { case 'terms': { settings.order = settings.order || "asc"; - settings.size = settings.size || "0"; + settings.size = settings.size || "10"; settings.orderBy = settings.orderBy || "_term"; if (settings.size !== '0') { From f35eb0f148dc7035d5e33858caad2af14e1ebc63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 3 Apr 2016 16:19:14 -0400 Subject: [PATCH 51/54] fix(metrics tab): Fix for missing datasource name in datasource selector, fixes #4540 --- CHANGELOG.md | 1 + public/app/features/panel/metrics_panel_ctrl.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e20da1142d4..fc75b3fa734 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * **Dashboard**: Fixed dashboard panel layout for mobile devices, fixes [#4529](https://github.com/grafana/grafana/issues/4529) * **Table Panel**: Fixed issue with table panel sort, fixes [#4532](https://github.com/grafana/grafana/issues/4532) * **Page Load Crash**: A Datasource with null jsonData would make Grafana fail to load page, fixes [#4536](https://github.com/grafana/grafana/issues/4536) +* **Metrics tab**: Fix for missing datasource name in datasource selector, fixes [#4540](https://github.com/grafana/grafana/issues/4540) # 3.0.0-beta1 (2016-03-31) diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index 52e9c64981c..c1f408e9374 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -165,6 +165,7 @@ class MetricsPanelCtrl extends PanelCtrl { issueQueries(datasource) { this.updateTimeRange(); + this.datasource = datasource; if (!this.panel.targets || this.panel.targets.length === 0) { return this.$q.when([]); @@ -251,7 +252,6 @@ class MetricsPanelCtrl extends PanelCtrl { addDataQuery(datasource) { var target = { - datasource: datasource ? datasource.name : undefined }; this.panel.targets.push(target); } From 542e080f0a7777860355223d714aab985e6cb014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 4 Apr 2016 13:13:25 -0400 Subject: [PATCH 52/54] fix(graph): fixed issue with graph legend when in table mode and series on righ-y axis, also fixed so that you can see what series are on right-y axis when legend is in table mode, fixes #4551, fixes #1145 --- CHANGELOG.md | 1 + public/app/plugins/panel/graph/legend.js | 2 +- public/sass/base/_font_awesome.scss | 6 ------ public/sass/components/_panel_graph.scss | 16 ++++++++++++++-- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc75b3fa734..4e92dd13f52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * **Table Panel**: Fixed issue with table panel sort, fixes [#4532](https://github.com/grafana/grafana/issues/4532) * **Page Load Crash**: A Datasource with null jsonData would make Grafana fail to load page, fixes [#4536](https://github.com/grafana/grafana/issues/4536) * **Metrics tab**: Fix for missing datasource name in datasource selector, fixes [#4540](https://github.com/grafana/grafana/issues/4540) +* **Graph**: Fix legend in table mode with series on right-y axis, fixes [#4551](https://github.com/grafana/grafana/issues/4551), [#1145](https://github.com/grafana/grafana/issues/1145) # 3.0.0-beta1 (2016-03-31) diff --git a/public/app/plugins/panel/graph/legend.js b/public/app/plugins/panel/graph/legend.js index 22bbb756b59..4cad3c16844 100644 --- a/public/app/plugins/panel/graph/legend.js +++ b/public/app/plugins/panel/graph/legend.js @@ -157,7 +157,7 @@ function (angular, _, $) { } var html = '
    '; html += '
    '; diff --git a/public/sass/base/_font_awesome.scss b/public/sass/base/_font_awesome.scss index 438185f3198..51d9bac987d 100644 --- a/public/sass/base/_font_awesome.scss +++ b/public/sass/base/_font_awesome.scss @@ -66,12 +66,6 @@ border: solid 0.08em #eeeeee; border-radius: .1em; } -.pull-right { - float: right; -} -.pull-left { - float: left; -} .fa.pull-left { margin-right: .3em; } diff --git a/public/sass/components/_panel_graph.scss b/public/sass/components/_panel_graph.scss index ccecbc6c9a7..9837f33a543 100644 --- a/public/sass/components/_panel_graph.scss +++ b/public/sass/components/_panel_graph.scss @@ -74,6 +74,10 @@ float: left; white-space: nowrap; padding-left: 10px; + + &--right-y { + float: right; + } } .graph-legend-value { @@ -83,14 +87,22 @@ .graph-legend-table { overflow-y: scroll; - .graph-legend-series { display: table-row; + .graph-legend-series { + display: table-row; float: none; padding-left: 0; - &.pull-right { + &--right-y { float: none; + + .graph-legend-alias:after { + content: '(right-y)'; + padding: 0 5px; + color: $text-color-weak; + } } } + td, .graph-legend-alias, .graph-legend-icon, .graph-legend-value { float: none; display: table-cell; From 892e79a9827e74e784117005929c162062c0d448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 4 Apr 2016 13:43:13 -0400 Subject: [PATCH 53/54] fix(table): another fix for table panel sorting, fixes #4544 --- public/app/plugins/panel/table/module.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index 91fe1a5fb8b..295c7e0ee33 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -114,9 +114,13 @@ class TablePanelCtrl extends MetricsPanelCtrl { } } + this.render(); + } + + render() { this.table = transformDataToTable(this.dataRaw, this.panel); this.table.sort(this.panel.sort); - this.render(this.table); + return super.render(this.table); } toggleColumnSort(col, colIndex) { @@ -130,10 +134,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { this.panel.sort.col = colIndex; this.panel.sort.desc = true; } - - this.table = transformDataToTable(this.dataRaw, this.panel); - this.table.sort(this.panel.sort); - this.render(this.table); + this.render(); } exportCsv() { From 43ebc172cb57907869fb4d261ad361286eaccfdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 4 Apr 2016 13:52:41 -0400 Subject: [PATCH 54/54] fix(profile): fixed change password link and page, fixes #4542 --- CHANGELOG.md | 1 + pkg/api/api.go | 1 + public/app/core/routes/routes.ts | 2 +- public/app/features/org/all.js | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e92dd13f52..cb4ab3ad1e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * **Page Load Crash**: A Datasource with null jsonData would make Grafana fail to load page, fixes [#4536](https://github.com/grafana/grafana/issues/4536) * **Metrics tab**: Fix for missing datasource name in datasource selector, fixes [#4540](https://github.com/grafana/grafana/issues/4540) * **Graph**: Fix legend in table mode with series on right-y axis, fixes [#4551](https://github.com/grafana/grafana/issues/4551), [#1145](https://github.com/grafana/grafana/issues/1145) +* **Password**: Password reset link/page did not work, fixes [#4542](https://github.com/grafana/grafana/issues/4542) # 3.0.0-beta1 (2016-03-31) diff --git a/pkg/api/api.go b/pkg/api/api.go index 5ae51b58904..85f1e2474c3 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -29,6 +29,7 @@ func Register(r *macaron.Macaron) { // authed views r.Get("/profile/", reqSignedIn, Index) + r.Get("/profile/password", reqSignedIn, Index) r.Get("/org/", reqSignedIn, Index) r.Get("/org/new", reqSignedIn, Index) r.Get("/datasources/", reqSignedIn, Index) diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts index c258fb1a611..1608a772e87 100644 --- a/public/app/core/routes/routes.ts +++ b/public/app/core/routes/routes.ts @@ -94,7 +94,7 @@ function setupAngularRoutes($routeProvider, $locationProvider) { resolve: loadOrgBundle, }) .when('/profile/password', { - templateUrl: 'public/app/features/org/partials/password.html', + templateUrl: 'public/app/features/org/partials/change_password.html', controller : 'ChangePasswordCtrl', resolve: loadOrgBundle, }) diff --git a/public/app/features/org/all.js b/public/app/features/org/all.js index b610a20e686..e206583a8c7 100644 --- a/public/app/features/org/all.js +++ b/public/app/features/org/all.js @@ -3,6 +3,7 @@ define([ './profile_ctrl', './org_users_ctrl', './select_org_ctrl', + './change_password_ctrl', './newOrgCtrl', './userInviteCtrl', './orgApiKeysCtrl',