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