feat(preferences): theme and home dashbord settings now work work on profile and org settings page

This commit is contained in:
Torkel Ödegaard
2016-04-02 13:54:06 -07:00
parent 38a10f8be4
commit ab1048b7ee
21 changed files with 218 additions and 108 deletions

View File

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

View File

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

View File

@ -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 {

View File

@ -1,15 +1,12 @@
package dtos
type UserPrefs struct {
type Prefs 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 UpdatePrefsCmd struct {
Theme string `json:"theme"`
HomeDashboardId int64 `json:"homeDashboardId"`
Timezone string `json:"timezone"`

View File

@ -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,

View File

@ -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}
if err := bus.Dispatch(&userPrefs); err != nil {
c.JsonApiErr(500, "Failed to get preferences", err)
return getPreferencesFor(c.OrgId, c.UserId)
}
dto := dtos.UserPrefs{
Theme: userPrefs.Result.Theme,
HomeDashboardId: userPrefs.Result.HomeDashboardId,
Timezone: userPrefs.Result.Timezone,
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.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)
}

View File

@ -33,6 +33,14 @@ type GetPreferencesQuery struct {
Result *Preferences
}
type GetPreferencesWithDefaultsQuery struct {
Id int64
OrgId int64
UserId int64
Result *Preferences
}
// ---------------------
// COMMANDS
type SavePreferencesCommand struct {

View File

@ -136,7 +136,6 @@ type SignedInUser struct {
Login string
Name string
Email string
Theme string
ApiKeyId int64
IsGrafanaAdmin bool
}

View File

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

View File

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

View File

@ -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', {

View File

@ -7,8 +7,5 @@ define([
'./playlist/all',
'./snapshot/all',
'./panel/all',
'./profile/profile_ctrl',
'./profile/changePasswordCtrl',
'./profile/selectOrgCtrl',
'./styleguide/styleguide',
], function () {});

View File

@ -1,7 +1,11 @@
define([
'./org_users_ctrl',
'./profile_ctrl',
'./org_users_ctrl',
'./select_org_ctrl',
'./newOrgCtrl',
'./userInviteCtrl',
'./orgApiKeysCtrl',
'./orgDetailsCtrl',
'./prefs_control',
], function () {});

View File

@ -19,6 +19,8 @@
</div>
</form>
<prefs-control mode="org"></prefs-control>
<h3 class="page-heading">Address</h3>
<form name="addressForm" class="gf-form-group">

View File

@ -26,36 +26,7 @@
</div>
</form>
<form name="ctrl.prefsForm" class="gf-form-group">
<h3 class="page-heading">Preferences</h3>
<div class="gf-form">
<span class="gf-form-label width-9">UI Theme</span>
<div class="gf-form-select-wrapper max-width-20">
<select class="gf-form-input" ng-model="ctrl.prefs.theme" ng-options="f.value as f.text for f in ctrl.themes"></select>
</div>
</div>
<div class="gf-form">
<span class="gf-form-label width-9">Home Dashboard</span>
<dashboard-selector
class="gf-form-select-wrapper max-width-20"
model="ctrl.prefs.homeDashboardId">
</dashboard-selector>
</div>
<div class="gf-form">
<label class="gf-form-label width-9">Timezone</label>
<div class="gf-form-select-wrapper max-width-20">
<select class="gf-form-input" ng-model="ctrl.prefs.timezone" ng-options="f.value as f.text for f in ctrl.timezones"></select>
</div>
</div>
<div class="gf-form-button-row">
<button type="submit" class="btn btn-success" ng-click="ctrl.updatePrefs()">Update</button>
</div>
</form>
<prefs-control mode="user"></prefs-control>
<h3 class="page-heading">Password</h3>
<div class="gf-form-group">

View File

@ -0,0 +1,100 @@
///<reference path="../../headers/common.d.ts" />
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 = `
<form name="ctrl.prefsForm" class="gf-form-group">
<h3 class="page-heading">Preferences</h3>
<div class="gf-form">
<span class="gf-form-label width-9">UI Theme</span>
<div class="gf-form-select-wrapper max-width-20">
<select class="gf-form-input" ng-model="ctrl.prefs.theme" ng-options="f.value as f.text for f in ctrl.themes"></select>
</div>
</div>
<div class="gf-form">
<span class="gf-form-label width-9">Home Dashboard</span>
<dashboard-selector
class="gf-form-select-wrapper max-width-20"
model="ctrl.prefs.homeDashboardId">
</dashboard-selector>
</div>
<div class="gf-form">
<label class="gf-form-label width-9">Timezone</label>
<div class="gf-form-select-wrapper max-width-20">
<select class="gf-form-input" ng-model="ctrl.prefs.timezone" ng-options="f.value as f.text for f in ctrl.timezones"></select>
</div>
</div>
<div class="gf-form-button-row">
<button type="submit" class="btn btn-success" ng-click="ctrl.updatePrefs()">Update</button>
</div>
</form>
`;
export function prefsControlDirective() {
return {
restrict: 'E',
controller: PrefsControlCtrl,
bindToController: true,
controllerAs: 'ctrl',
template: template,
scope: {
mode: "@"
}
};
}
coreModule.directive('prefsControl', prefsControlDirective);

View File

@ -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);