Ver Fonte

progress

Torkel Ödegaard há 9 anos atrás
pai
commit
38a10f8be4

+ 2 - 0
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)

+ 0 - 1
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}
 

+ 10 - 1
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"`

+ 35 - 0
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")
+}

+ 2 - 1
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() {

+ 17 - 1
public/app/core/components/dashboard_selector.ts

@@ -6,12 +6,25 @@ import $ from 'jquery';
 import coreModule from 'app/core/core_module';
 
 var template = `
+<select class="gf-form-input" ng-model="ctrl.model" ng-options="f.value as f.text for f in ctrl.options"></select>
 `;
 
 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: '='
+    }
   };
 }
 

+ 2 - 0
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,
 };

+ 1 - 0
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',

+ 1 - 1
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',

+ 0 - 1
public/app/features/org/partials/orgDetails.html

@@ -19,7 +19,6 @@
 		</div>
 	</form>
 
-
 	<h3 class="page-heading">Address</h3>
 
 	<form name="addressForm" class="gf-form-group">

+ 31 - 9
public/app/features/profile/partials/profile.html

@@ -6,35 +6,57 @@
 		<h1>Profile</h1>
 	</div>
 
-	<form name="userForm" class="gf-form-group">
-		<h3 class="page-heading">Preferences</h3>
+	<form name="ctrl.userForm" class="gf-form-group">
+		<h3 class="page-heading">Information</h3>
 
 		<div class="gf-form">
 			<span class="gf-form-label width-9">Name</span>
-			<input class="gf-form-input max-width-21" type="text" required ng-model="user.name" >
+			<input class="gf-form-input max-width-21" type="text" required ng-model="ctrl.user.name" >
 		</div>
 		<div class="gf-form">
 			<span class="gf-form-label width-9">Email</span>
-			<input class="gf-form-input max-width-21" type="email" required ng-model="user.email">
+			<input class="gf-form-input max-width-21" type="email" required ng-model="ctrl.user.email">
 		</div>
 		<div class="gf-form">
 			<span class="gf-form-label width-9">Username</span>
-			<input class="gf-form-input max-width-21" type="text" required ng-model="user.login">
+			<input class="gf-form-input max-width-21" type="text" required ng-model="ctrl.user.login">
+		</div>
+		<div class="gf-form-button-row">
+			<button type="submit" class="btn btn-success" ng-click="ctrl.update()">Update</button>
 		</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>
-			<select class="gf-form-input gf-size-auto" ng-model="user.theme" ng-options="f for f in ['dark', 'light']"></select>
+			<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 model="user.homeDashboardId"></dashboard-selector>
+			<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="update()">Update</button>
+			<button type="submit" class="btn btn-success" ng-click="ctrl.updatePrefs()">Update</button>
 		</div>
 	</form>
 
+
 	<h3 class="page-heading">Password</h3>
 	<div class="gf-form-group">
 		<a href="profile/password" class="btn btn-inverse">Change Password</a>
@@ -51,7 +73,7 @@
 				</tr>
 			</thead>
 			<tbody>
-				<tr ng-repeat="org in orgs">
+				<tr ng-repeat="org in ctrl.orgs">
 					<td>{{org.name}}</td>
 					<td>{{org.role}}</td>
 					<td class="text-right">

+ 0 - 51
public/app/features/profile/profileCtrl.js

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

+ 87 - 0
public/app/features/profile/profile_ctrl.ts

@@ -0,0 +1,87 @@
+///<reference path="../../headers/common.d.ts" />
+
+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);

+ 1 - 1
public/vendor/angular/angular.js

@@ -30711,4 +30711,4 @@ $provide.value("$locale", {
 
 })(window, document);
 
-!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
+!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');