diff --git a/pkg/api/api.go b/pkg/api/api.go index 7d2d679362e..6bc4e2f93a5 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -117,6 +117,7 @@ func Register(r *macaron.Macaron) { r.Get("/:id", wrap(GetUserById)) r.Get("/:id/orgs", wrap(GetUserOrgList)) r.Put("/:id", bind(m.UpdateUserCommand{}), wrap(UpdateUser)) + r.Post("/:id/using/:orgId", wrap(UpdateUserActiveOrg)) }, reqGrafanaAdmin) // org information available to all users. diff --git a/pkg/api/dataproxy.go b/pkg/api/dataproxy.go index b00ef595161..9a062ddfd2d 100644 --- a/pkg/api/dataproxy.go +++ b/pkg/api/dataproxy.go @@ -55,6 +55,13 @@ func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *ht req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.BasicAuthUser, ds.BasicAuthPassword)) } + dsAuth := req.Header.Get("X-DS-Authorization") + if len(dsAuth) > 0 { + req.Header.Del("X-DS-Authorization") + req.Header.Del("Authorization") + req.Header.Add("Authorization", dsAuth) + } + // clear cookie headers req.Header.Del("Cookie") req.Header.Del("Set-Cookie") diff --git a/pkg/api/user.go b/pkg/api/user.go index 8f54feaf6a0..f98eec02c40 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -40,6 +40,24 @@ func UpdateUser(c *middleware.Context, cmd m.UpdateUserCommand) Response { return handleUpdateUser(cmd) } +//POST /api/users/:id/using/:orgId +func UpdateUserActiveOrg(c *middleware.Context) Response { + userId := c.ParamsInt64(":id") + orgId := c.ParamsInt64(":orgId") + + if !validateUsingOrg(userId, orgId) { + return ApiError(401, "Not a valid organization", nil) + } + + cmd := m.SetUsingOrgCommand{UserId: userId, OrgId: orgId} + + if err := bus.Dispatch(&cmd); err != nil { + return ApiError(500, "Failed change active organization", err) + } + + return ApiSuccess("Active organization changed") +} + func handleUpdateUser(cmd m.UpdateUserCommand) Response { if len(cmd.Login) == 0 { cmd.Login = cmd.Email diff --git a/public/app/core/services/backend_srv.js b/public/app/core/services/backend_srv.js index ff3784ab45e..f27e427c70b 100644 --- a/public/app/core/services/backend_srv.js +++ b/public/app/core/services/backend_srv.js @@ -96,6 +96,11 @@ function (angular, _, coreModule, config) { var requestIsLocal = options.url.indexOf('/') === 0; var firstAttempt = options.retry === 0; + if (requestIsLocal && options.headers && options.headers.Authorization) { + options.headers['X-DS-Authorization'] = options.headers.Authorization; + delete options.headers.Authorization; + } + return $http(options).then(null, function(err) { // handle unauthorized for backend requests if (requestIsLocal && firstAttempt && err.status === 401) { diff --git a/public/app/features/dashboard/submenu/submenu.ts b/public/app/features/dashboard/submenu/submenu.ts index 9852dda4fd0..3b9b0b6b0d8 100644 --- a/public/app/features/dashboard/submenu/submenu.ts +++ b/public/app/features/dashboard/submenu/submenu.ts @@ -26,25 +26,8 @@ export class SubmenuCtrl { return this.templateValuesSrv.getValuesForTag(variable, tagKey); } - updateUrlParamsWithCurrentVariables() { - // update url - var params = this.$location.search(); - // remove variable params - _.each(params, function(value, key) { - if (key.indexOf('var-') === 0) { - delete params[key]; - } - }); - - // add new values - this.templateSrv.fillVariableValuesForUrl(params); - // update url - this.$location.search(params); - } - variableUpdated(variable) { this.templateValuesSrv.variableUpdated(variable).then(() => { - this.updateUrlParamsWithCurrentVariables(); this.$rootScope.$emit('template-variable-value-updated'); this.$rootScope.$broadcast('refresh'); }); diff --git a/public/app/features/dashboard/timeSrv.js b/public/app/features/dashboard/timeSrv.js index 96ba832f8b0..cd52155ff5f 100644 --- a/public/app/features/dashboard/timeSrv.js +++ b/public/app/features/dashboard/timeSrv.js @@ -10,7 +10,7 @@ define([ var module = angular.module('grafana.services'); - module.service('timeSrv', function($rootScope, $timeout, $routeParams, timer, $location) { + module.service('timeSrv', function($rootScope, $timeout, $routeParams, timer) { var self = this; this.init = function(dashboard) { @@ -108,13 +108,6 @@ define([ this.old_refresh = null; } - // update url params - var urlParams = $location.search(); - var urlRange = this.timeRangeForUrl(); - urlParams.from = urlRange.from; - urlParams.to = urlRange.to; - $location.search(urlParams); - $rootScope.appEvent('time-range-changed', this.time); $timeout(this.refreshDashboard, 0); }; diff --git a/public/app/features/dashboard/viewStateSrv.js b/public/app/features/dashboard/viewStateSrv.js index cda5e69f006..2138dd37438 100644 --- a/public/app/features/dashboard/viewStateSrv.js +++ b/public/app/features/dashboard/viewStateSrv.js @@ -8,7 +8,7 @@ function (angular, _, $) { var module = angular.module('grafana.services'); - module.factory('dashboardViewStateSrv', function($location, $timeout) { + module.factory('dashboardViewStateSrv', function($location, $timeout, templateSrv, contextSrv, timeSrv) { // represents the transient view state // like fullscreen panel & edit @@ -25,6 +25,19 @@ function (angular, _, $) { } }; + // update url on time range change + $scope.onAppEvent('time-range-changed', function() { + var urlParams = $location.search(); + var urlRange = timeSrv.timeRangeForUrl(); + urlParams.from = urlRange.from; + urlParams.to = urlRange.to; + $location.search(urlParams); + }); + + $scope.onAppEvent('template-variable-value-updated', function() { + self.updateUrlParamsWithCurrentVariables(); + }); + $scope.onAppEvent('$routeUpdate', function() { var urlState = self.getQueryStringState(); if (self.needsSync(urlState)) { @@ -40,10 +53,26 @@ function (angular, _, $) { self.registerPanel(payload.scope); }); - this.update(this.getQueryStringState(), true); + this.update(this.getQueryStringState()); this.expandRowForPanel(); } + DashboardViewState.prototype.updateUrlParamsWithCurrentVariables = function() { + // update url + var params = $location.search(); + // remove variable params + _.each(params, function(value, key) { + if (key.indexOf('var-') === 0) { + delete params[key]; + } + }); + + // add new values + templateSrv.fillVariableValuesForUrl(params); + // update url + $location.search(params); + }; + DashboardViewState.prototype.expandRowForPanel = function() { if (!this.state.panelId) { return; } @@ -63,6 +92,7 @@ function (angular, _, $) { state.fullscreen = state.fullscreen ? true : null; state.edit = (state.edit === "true" || state.edit === true) || null; state.editview = state.editview || null; + state.org = contextSrv.user.orgId; return state; }; @@ -70,10 +100,11 @@ function (angular, _, $) { var urlState = _.clone(this.state); urlState.fullscreen = this.state.fullscreen ? true : null; urlState.edit = this.state.edit ? true : null; + urlState.org = contextSrv.user.orgId; return urlState; }; - DashboardViewState.prototype.update = function(state, skipUrlSync) { + DashboardViewState.prototype.update = function(state) { _.extend(this.state, state); this.dashboard.meta.fullscreen = this.state.fullscreen; @@ -83,10 +114,7 @@ function (angular, _, $) { this.state.edit = null; } - if (!skipUrlSync) { - $location.search(this.serializeToUrl()); - } - + $location.search(this.serializeToUrl()); this.syncState(); }; diff --git a/public/app/features/panel/partials/soloPanel.html b/public/app/features/panel/partials/soloPanel.html index e76ad724343..131d99d4389 100644 --- a/public/app/features/panel/partials/soloPanel.html +++ b/public/app/features/panel/partials/soloPanel.html @@ -1,9 +1,5 @@ -
-
-
-
- - -
-
+
+ +
+
diff --git a/public/app/features/panel/solo_panel_ctrl.js b/public/app/features/panel/solo_panel_ctrl.js index 313c2014de2..355d5e8b265 100644 --- a/public/app/features/panel/solo_panel_ctrl.js +++ b/public/app/features/panel/solo_panel_ctrl.js @@ -17,6 +17,10 @@ function (angular, $) { var params = $location.search(); panelId = parseInt(params.panelId); + // add fullscreen param; + params.fullscreen = true; + $location.search(params); + dashboardLoaderSrv.loadDashboard($routeParams.type, $routeParams.slug).then(function(result) { $scope.initDashboard(result, $scope); }); diff --git a/public/app/features/templating/templateValuesSrv.js b/public/app/features/templating/templateValuesSrv.js index ce41bc6af7b..b79713ebbb4 100644 --- a/public/app/features/templating/templateValuesSrv.js +++ b/public/app/features/templating/templateValuesSrv.js @@ -105,8 +105,6 @@ function (angular, _, kbn) { return op.text === urlValue || op.value === urlValue; }); - option = option || { text: urlValue, value: urlValue }; - self.updateAutoInterval(variable); return self.setVariableValue(variable, option, true); }); @@ -127,8 +125,8 @@ function (angular, _, kbn) { this.setVariableValue = function(variable, option, initPhase) { variable.current = angular.copy(option); - if (_.isArray(variable.current.value)) { - variable.current.text = variable.current.value.join(' + '); + if (_.isArray(variable.current.text)) { + variable.current.text = variable.current.text.join(' + '); } self.selectOptionsForCurrentValue(variable); @@ -226,6 +224,7 @@ function (angular, _, kbn) { this.selectOptionsForCurrentValue = function(variable) { var i, y, value, option; + var selected = []; for (i = 0; i < variable.options.length; i++) { option = variable.options[i]; @@ -235,12 +234,16 @@ function (angular, _, kbn) { value = variable.current.value[y]; if (option.value === value) { option.selected = true; + selected.push(option); } } } else if (option.value === variable.current.value) { option.selected = true; + selected.push(option); } } + + return selected; }; this.validateVariableSelectionState = function(variable) { @@ -250,17 +253,18 @@ function (angular, _, kbn) { } if (_.isArray(variable.current.value)) { - self.selectOptionsForCurrentValue(variable); - // updated selected value - var selected = { - value: _.map(_.filter(variable.options, {selected: true}), function(op) { - return op.value; - }) - }; + var selected = self.selectOptionsForCurrentValue(variable); + // if none pick first - if (selected.value.length === 0) { + if (selected.length === 0) { selected = variable.options[0]; + } else { + selected = { + value: _.map(selected, function(val) {return val.value;}), + text: _.map(selected, function(val) {return val.text;}).join(' + '), + }; } + return self.setVariableValue(variable, selected, false); } else { var currentOption = _.findWhere(variable.options, {text: variable.current.text}); diff --git a/public/test/specs/dashboardViewStateSrv-specs.js b/public/test/specs/dashboardViewStateSrv-specs.js index b33c7156392..202a43670b1 100644 --- a/public/test/specs/dashboardViewStateSrv-specs.js +++ b/public/test/specs/dashboardViewStateSrv-specs.js @@ -5,8 +5,20 @@ define([ describe('when updating view state', function() { var viewState, location; + var timeSrv = {}; + var templateSrv = {}; + var contextSrv = { + user: { + orgId: 19 + } + }; beforeEach(module('grafana.services')); + beforeEach(module(function($provide) { + $provide.value('timeSrv', timeSrv); + $provide.value('templateSrv', templateSrv); + $provide.value('contextSrv', contextSrv); + })); beforeEach(inject(function(dashboardViewStateSrv, $location, $rootScope) { $rootScope.onAppEvent = function() {}; @@ -17,9 +29,9 @@ define([ describe('to fullscreen true and edit true', function() { it('should update querystring and view state', function() { - var updateState = { fullscreen: true, edit: true, panelId: 1 }; + var updateState = {fullscreen: true, edit: true, panelId: 1}; viewState.update(updateState); - expect(location.search()).to.eql(updateState); + expect(location.search()).to.eql({fullscreen: true, edit: true, panelId: 1, org: 19}); expect(viewState.dashboard.meta.fullscreen).to.be(true); expect(viewState.state.fullscreen).to.be(true); }); @@ -29,7 +41,7 @@ define([ it('should remove params from query string', function() { viewState.update({fullscreen: true, panelId: 1, edit: true}); viewState.update({fullscreen: false}); - expect(location.search()).to.eql({}); + expect(location.search()).to.eql({org: 19}); expect(viewState.dashboard.meta.fullscreen).to.be(false); expect(viewState.state.fullscreen).to.be(null); }); diff --git a/public/test/specs/templateValuesSrv-specs.js b/public/test/specs/templateValuesSrv-specs.js index 79aa2eafd8d..7a8d8c1fccb 100644 --- a/public/test/specs/templateValuesSrv-specs.js +++ b/public/test/specs/templateValuesSrv-specs.js @@ -166,6 +166,27 @@ define([ }); }); + describeUpdateVariable('query variable with multi select and $__all selected', function(scenario) { + scenario.setup(function() { + scenario.variable = { + type: 'query', + query: '', + name: 'test', + includeAll: true, + current: { + value: ['$__all'], + text: 'All' + } + }; + scenario.queryResult = [{text: 'val5'}, {text: 'val6'}]; + }); + + it('should keep current All value', function() { + expect(scenario.variable.current.value).to.eql(['$__all']); + expect(scenario.variable.current.text).to.eql('All'); + }); + }); + describeUpdateVariable('query variable with numeric results', function(scenario) { scenario.setup(function() { scenario.variable = { type: 'query', query: '', name: 'test', current: {} }; diff --git a/vendor/phantomjs/render.js b/vendor/phantomjs/render.js index 0de97273c5c..f40a42380ad 100644 --- a/vendor/phantomjs/render.js +++ b/vendor/phantomjs/render.js @@ -39,7 +39,8 @@ var canvas = page.evaluate(function() { if (!window.angular) { return false; } var body = window.angular.element(document.body); - if (!body.scope) { return false; } + if (!body.injector) { return false; } + if (!body.injector()) { return false; } var rootScope = body.injector().get('$rootScope'); if (!rootScope) {return false;} @@ -59,6 +60,7 @@ width: bb.width, height: bb.height }; + page.render(params.png); phantom.exit(); }