瀏覽代碼

Moved dashboard theme option from the dashboard to a persisted user setting, #1458

Torkel Ödegaard 10 年之前
父節點
當前提交
04ca85fe89

+ 1 - 0
CHANGELOG.md

@@ -5,6 +5,7 @@
 - [Issue #1241](https://github.com/grafana/grafana/issues/1242). Timepicker: New option in timepicker (under dashboard settings), to change ``now`` to be for example ``now-1m``, usefull when you want to ignore last minute because it contains incomplete data
 - [Issue #171](https://github.com/grafana/grafana/issues/171).   Panel: Different time periods, panels can override dashboard relative time and/or add a time shift
 - [Issue #1488](https://github.com/grafana/grafana/issues/1488). Dashboard: Clone dashboard / Save as
+- [Issue #1458](https://github.com/grafana/grafana/issues/1458). User: persisted user option for dark or light theme  (no longer an option on a dashboard)
 
 **Enhancements**
 - [Issue #1366](https://github.com/grafana/grafana/issues/1366). Graph & Singlestat: Support for additional units, Fahrenheit (°F) and Celsius (°C), Humidity (%H), kW, watt-hour (Wh), kilowatt-hour (kWh), velocities (m/s, km/h, mpg, knot)

+ 1 - 1
pkg/api/admin_users.go

@@ -28,7 +28,7 @@ func AdminGetUser(c *middleware.Context) {
 		return
 	}
 
-	result := m.UserDTO{
+	result := dtos.AdminUserListItem{
 		Name:           query.Result.Name,
 		Email:          query.Result.Email,
 		Login:          query.Result.Login,

+ 1 - 0
pkg/api/dtos/models.go

@@ -19,6 +19,7 @@ type CurrentUser struct {
 	Login          string     `json:"login"`
 	Email          string     `json:"email"`
 	Name           string     `json:"name"`
+	LightTheme     bool       `json:"lightTheme"`
 	OrgRole        m.RoleType `json:"orgRole"`
 	OrgName        string     `json:"orgName"`
 	IsGrafanaAdmin bool       `json:"isGrafanaAdmin"`

+ 7 - 0
pkg/api/dtos/user.go

@@ -20,3 +20,10 @@ type AdminUpdateUserPasswordForm struct {
 type AdminUpdateUserPermissionsForm struct {
 	IsGrafanaAdmin bool `json:"IsGrafanaAdmin" binding:"Required"`
 }
+
+type AdminUserListItem struct {
+	Email          string `json:"email"`
+	Name           string `json:"name"`
+	Login          string `json:"login"`
+	IsGrafanaAdmin bool   `json:"isGrafanaAdmin"`
+}

+ 1 - 0
pkg/api/index.go

@@ -17,6 +17,7 @@ func setIndexViewData(c *middleware.Context) error {
 		Login:          c.Login,
 		Email:          c.Email,
 		Name:           c.Name,
+		LightTheme:     c.Theme == "light",
 		OrgName:        c.OrgName,
 		OrgRole:        c.OrgRole,
 		GravatarUrl:    dtos.GetGravatarUrl(c.Email),

+ 1 - 1
pkg/api/user.go

@@ -8,7 +8,7 @@ import (
 )
 
 func GetUser(c *middleware.Context) {
-	query := m.GetUserInfoQuery{UserId: c.UserId}
+	query := m.GetUserProfileQuery{UserId: c.UserId}
 
 	if err := bus.Dispatch(&query); err != nil {
 		c.JsonApiErr(500, "Failed to get user", err)

+ 6 - 3
pkg/models/user.go

@@ -48,6 +48,7 @@ type UpdateUserCommand struct {
 	Name  string `json:"name"`
 	Email string `json:"email"`
 	Login string `json:"login"`
+	Theme string `json:"theme"`
 
 	UserId int64 `json:"-"`
 }
@@ -91,9 +92,9 @@ type GetSignedInUserQuery struct {
 	Result *SignedInUser
 }
 
-type GetUserInfoQuery struct {
+type GetUserProfileQuery struct {
 	UserId int64
-	Result UserDTO
+	Result UserProfileDTO
 }
 
 type SearchUsersQuery struct {
@@ -120,14 +121,16 @@ type SignedInUser struct {
 	Login          string
 	Name           string
 	Email          string
+	Theme          string
 	ApiKeyId       int64
 	IsGrafanaAdmin bool
 }
 
-type UserDTO struct {
+type UserProfileDTO struct {
 	Email          string `json:"email"`
 	Name           string `json:"name"`
 	Login          string `json:"login"`
+	Theme          string `json:"theme"`
 	IsGrafanaAdmin bool   `json:"isGrafanaAdmin"`
 }
 

+ 2 - 2
pkg/services/sqlstore/org_test.go

@@ -53,8 +53,8 @@ func TestAccountDataAccess(t *testing.T) {
 			ac2 := ac2cmd.Result
 
 			Convey("Should be able to read user info projection", func() {
-				query := m.GetUserInfoQuery{UserId: ac1.Id}
-				err = GetUserInfo(&query)
+				query := m.GetUserProfileQuery{UserId: ac1.Id}
+				err = GetUserProfile(&query)
 
 				So(err, ShouldBeNil)
 				So(query.Result.Email, ShouldEqual, "ac1@test.com")

+ 6 - 3
pkg/services/sqlstore/user.go

@@ -21,7 +21,7 @@ func init() {
 	bus.AddHandler("sql", ChangeUserPassword)
 	bus.AddHandler("sql", GetUserByLogin)
 	bus.AddHandler("sql", SetUsingOrg)
-	bus.AddHandler("sql", GetUserInfo)
+	bus.AddHandler("sql", GetUserProfile)
 	bus.AddHandler("sql", GetSignedInUser)
 	bus.AddHandler("sql", SearchUsers)
 	bus.AddHandler("sql", GetUserOrgList)
@@ -165,6 +165,7 @@ func UpdateUser(cmd *m.UpdateUserCommand) error {
 			Name:    cmd.Name,
 			Email:   cmd.Email,
 			Login:   cmd.Login,
+			Theme:   cmd.Theme,
 			Updated: time.Now(),
 		}
 
@@ -211,7 +212,7 @@ func SetUsingOrg(cmd *m.SetUsingOrgCommand) error {
 	})
 }
 
-func GetUserInfo(query *m.GetUserInfoQuery) error {
+func GetUserProfile(query *m.GetUserProfileQuery) error {
 	var user m.User
 	has, err := x.Id(query.UserId).Get(&user)
 
@@ -221,10 +222,11 @@ func GetUserInfo(query *m.GetUserInfoQuery) error {
 		return m.ErrUserNotFound
 	}
 
-	query.Result = m.UserDTO{
+	query.Result = m.UserProfileDTO{
 		Name:  user.Name,
 		Email: user.Email,
 		Login: user.Login,
+		Theme: user.Theme,
 	}
 
 	return err
@@ -247,6 +249,7 @@ 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

+ 0 - 5
src/app/features/dashboard/dashboardCtrl.js

@@ -65,11 +65,6 @@ function (angular, $, config) {
 
     $scope.setWindowTitleAndTheme = function() {
       window.document.title = config.window_title_prefix + $scope.dashboard.title;
-      $scope.contextSrv.lightTheme = $scope.dashboard.style === 'light';
-    };
-
-    $scope.styleUpdated = function() {
-      $scope.contextSrv.lightTheme = $scope.dashboard.style === 'light';
     };
 
     $scope.broadcastRefresh = function() {

+ 0 - 1
src/app/features/panel/soloPanelCtrl.js

@@ -24,7 +24,6 @@ function (angular, $) {
 
     $scope.initPanelScope = function(dashboard) {
       $scope.dashboard = dashboardSrv.create(dashboard.model);
-      $scope.contextSrv.lightTheme = $scope.dashboard.style === 'light';
 
       $scope.row = {
         height: $(window).height() + 'px',

+ 23 - 10
src/app/features/profile/partials/profile.html

@@ -1,4 +1,4 @@
-<topnav title="{{contextSrv.user.name}}" section="Profile" icon="fa fw fa-user" subnav="true">
+<topnav title="{{contextSrv.user.name}}" section="Profile" icon="fa fa-fw fa-user" subnav="true">
 	<ul class="nav">
 		<li class="active"><a href="profile">Overview</a></li>
 		<li><a href="profile/password">Change password</a></li>
@@ -8,14 +8,14 @@
 <div class="page-container">
 	<div class="page">
 
-		<h2>Personal information</h2>
+		<h2>Profile details</h2>
 
 		<form name="userForm">
 			<div>
 				<div class="tight-form">
 					<ul class="tight-form-list">
 						<li class="tight-form-item" style="width: 100px">
-							<strong>Name</strong>
+							Name
 						</li>
 						<li>
 							<input type="text" required ng-model="user.name" class="input-xxlarge tight-form-input last" >
@@ -23,10 +23,10 @@
 					</ul>
 					<div class="clearfix"></div>
 				</div>
-				<div class="tight-form" style="margin-top: 5px">
+				<div class="tight-form">
 					<ul class="tight-form-list">
 						<li class="tight-form-item" style="width: 100px">
-							<strong>Email</strong>
+							Email
 						</li>
 						<li>
 							<input type="email" required ng-model="user.email" class="input-xxlarge tight-form-input last" >
@@ -34,10 +34,10 @@
 					</ul>
 					<div class="clearfix"></div>
 				</div>
-				<div class="tight-form" style="margin-top: 5px">
+				<div class="tight-form">
 					<ul class="tight-form-list">
 						<li class="tight-form-item" style="width: 100px">
-							<strong>Username</strong>
+							Username
 						</li>
 						<li>
 							<input type="text" required ng-model="user.login" class="input-xxlarge tight-form-input last" >
@@ -45,23 +45,36 @@
 					</ul>
 					<div class="clearfix"></div>
 				</div>
+
+				<div class="tight-form">
+					<ul class="tight-form-list">
+						<li class="tight-form-item" style="width: 100px">
+							UI Theme
+						</li>
+						<li>
+							<select class="input-small tight-form-input" ng-model="user.theme" ng-options="f for f in ['dark', 'light']" ng-change="themeChanged()"></select>
+						</li>
+					</ul>
+					<div class="clearfix"></div>
+				</div>
+
 			</div>
 
 			<br>
 			<button type="submit" class="pull-right btn btn-success" ng-click="update()">Update</button>
 		</form>
 
-		<h2>Your Organizations</h2>
+		<h2>Organizations</h2>
 
 		<table class="grafana-options-table">
 			<tr ng-repeat="org in orgs">
 				<td style="width: 98%"><strong>Name: </strong> {{org.name}}</td>
 				<td><strong>Role: </strong> {{org.role}}</td>
 				<td class="nobg max-width-btns">
-					<span class="btn btn-primary" ng-show="org.isUsing">
+					<span class="btn btn-primary btn-mini" ng-show="org.isUsing">
 						Current
 					</span>
-					<a ng-click="setUsingOrg(org)" class="btn btn-inverse" ng-show="!org.isUsing">
+					<a ng-click="setUsingOrg(org)" class="btn btn-inverse btn-mini" ng-show="!org.isUsing">
 						Select
 					</a>
 				</td>

+ 1 - 0
src/app/features/profile/profileCtrl.js

@@ -17,6 +17,7 @@ function (angular, config) {
     $scope.getUser = function() {
       backendSrv.get('/api/user').then(function(user) {
         $scope.user = user;
+        $scope.user.theme = user.theme || 'dark';
       });
     };
 

+ 0 - 3
src/app/partials/dasheditor.html

@@ -25,9 +25,6 @@
 					<div class="editor-option">
 						<label class="small">Title</label><input type="text" class="input-large" ng-model='dashboard.title'></input>
 					</div>
-					<div class="editor-option">
-						<label class="small">Theme</label><select class="input-small" ng-model="dashboard.style" ng-options="f for f in ['dark','light']" ng-change="styleUpdated()"></select>
-					</div>
 					<div class="editor-option">
 						<label class="small">Time correction</label>
 						<select ng-model="dashboard.timezone" class='input-small' ng-options="f for f in ['browser','utc']"></select>

+ 6 - 6
src/views/index.html

@@ -7,7 +7,12 @@
 
     <title>Grafana</title>
 
-    <link rel="stylesheet" href="[[.AppSubUrl]]/css/grafana.dark.min.css" title="Dark">
+		[[if .User.LightTheme]]
+		  <link rel="stylesheet" href="[[.AppSubUrl]]/css/grafana.light.min.css">
+		[[else]]
+		  <link rel="stylesheet" href="[[.AppSubUrl]]/css/grafana.dark.min.css">
+		[[end]]
+
     <link rel="icon" type="image/png" href="[[.AppSubUrl]]/img/fav32.png">
 		<base href="[[.AppSubUrl]]/" />
 
@@ -19,14 +24,9 @@
     <script src="[[.AppSubUrl]]/public/vendor/require/require.js"></script>
     <script src="[[.AppSubUrl]]/public/app/components/require.backend.js"></script>
     <!-- endbuild -->
-
-
   </head>
 
 	<body ng-cloak ng-controller="GrafanaCtrl" ng-class="{'sidemenu-open': contextSrv.sidemenu}">
-
-		<link rel="stylesheet" href="[[.AppSubUrl]]/css/grafana.light.min.css" ng-if="contextSrv.lightTheme">
-
 		<div class="sidemenu-canvas">
 
 			<aside class="sidemenu-wrapper" ng-if="contextSrv.sidemenu">