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('');