diff --git a/public/app/features/dashboard/partials/settings.html b/public/app/features/dashboard/partials/settings.html
index 92b5d0aba9c..5ddfb1c57e3 100644
--- a/public/app/features/dashboard/partials/settings.html
+++ b/public/app/features/dashboard/partials/settings.html
@@ -107,7 +107,7 @@
-
+
diff --git a/public/app/features/dashboard/shareModalCtrl.js b/public/app/features/dashboard/shareModalCtrl.js
index 68c0bf606a0..202e658e0b7 100644
--- a/public/app/features/dashboard/shareModalCtrl.js
+++ b/public/app/features/dashboard/shareModalCtrl.js
@@ -9,7 +9,8 @@ function (angular, _, require, config) {
var module = angular.module('grafana.controllers');
- module.controller('ShareModalCtrl', function($scope, $rootScope, $location, $timeout, timeSrv, $element, templateSrv) {
+ module.controller('ShareModalCtrl', function($scope, $rootScope, $location, $timeout, timeSrv, $element, templateSrv, linkSrv) {
+
$scope.options = { forCurrent: true, includeTemplateVars: true, theme: 'current' };
$scope.editor = { index: 0 };
@@ -47,14 +48,7 @@ function (angular, _, require, config) {
params.to = range.to;
if ($scope.options.includeTemplateVars) {
- _.each(templateSrv.variables, function(variable) {
- params['var-' + variable.name] = variable.current.text;
- });
- }
- else {
- _.each(templateSrv.variables, function(variable) {
- delete params['var-' + variable.name];
- });
+ templateSrv.fillVariableValuesForUrl(params);
}
if (!$scope.options.forCurrent) {
@@ -74,19 +68,7 @@ function (angular, _, require, config) {
delete params.fullscreen;
}
- var paramsArray = [];
- _.each(params, function(value, key) {
- if (value === null) { return; }
- if (value === true) {
- paramsArray.push(key);
- } else {
- key += '=' + encodeURIComponent(value);
- paramsArray.push(key);
- }
- });
-
- var queryParams = "?" + paramsArray.join('&');
- $scope.shareUrl = baseUrl + queryParams;
+ $scope.shareUrl = linkSrv.addParamsToUrl(baseUrl, params);
var soloUrl = $scope.shareUrl;
soloUrl = soloUrl.replace('/dashboard/db/', '/dashboard/solo/db/');
diff --git a/public/app/features/dashlinks/editor.html b/public/app/features/dashlinks/editor.html
index a5f442a8e65..576cde9c408 100644
--- a/public/app/features/dashlinks/editor.html
+++ b/public/app/features/dashlinks/editor.html
@@ -9,7 +9,7 @@
-
+
@@ -36,8 +36,6 @@
-
-
Url
@@ -68,24 +66,22 @@
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/app/features/dashlinks/module.js b/public/app/features/dashlinks/module.js
index 40a4a8ad6e6..fbf5104799c 100644
--- a/public/app/features/dashlinks/module.js
+++ b/public/app/features/dashlinks/module.js
@@ -19,9 +19,6 @@ function (angular, _) {
module.directive('dashLinksEditor', function() {
return {
- scope: {
- dashboard: "="
- },
restrict: 'E',
controller: 'DashLinkEditorCtrl',
templateUrl: 'app/features/dashlinks/editor.html',
@@ -76,6 +73,11 @@ function (angular, _) {
icon.attr('class', 'fa fa-fw ' + scope.link.icon);
anchor.attr('target', scope.link.target);
+ // fix for menus on the far right
+ if (link.asDropdown && scope.$last) {
+ elem.find('.dropdown-menu').addClass('pull-right');
+ }
+
update();
scope.$on('refresh', update);
}
@@ -96,12 +98,14 @@ function (angular, _) {
return $q.when([{
title: linkDef.title,
tag: linkDef.tag,
+ keepTime: linkDef.keepTime,
+ includeVars: linkDef.includeVars,
icon: "fa fa-bars",
asDropdown: true
}]);
}
- return $scope.searchDashboards(linkDef.tag);
+ return $scope.searchDashboards(linkDef);
}
if (linkDef.type === 'link') {
@@ -111,6 +115,8 @@ function (angular, _) {
icon: iconMap[linkDef.icon],
tooltip: linkDef.tooltip,
target: linkDef.targetBlank ? "_blank" : "",
+ keepTime: linkDef.keepTime,
+ includeVars: linkDef.includeVars,
}]);
}
@@ -125,12 +131,18 @@ function (angular, _) {
});
}
- $scope.searchDashboards = function(tag) {
- return backendSrv.search({tag: tag}).then(function(results) {
+ $scope.searchDashboards = function(link) {
+ return backendSrv.search({tag: link.tag}).then(function(results) {
return _.reduce(results.dashboards, function(memo, dash) {
// do not add current dashboard
if (dash.id !== currentDashId) {
- memo.push({ title: dash.title, url: 'dashboard/db/'+ dash.slug, icon: 'fa fa-th-large', addTime: true });
+ memo.push({
+ title: dash.title,
+ url: 'dashboard/db/'+ dash.slug,
+ icon: 'fa fa-th-large',
+ keepTime: link.keepTime,
+ includeVars: link.includeVars
+ });
}
return memo;
}, []);
@@ -138,7 +150,7 @@ function (angular, _) {
};
$scope.fillDropdown = function(link) {
- $scope.searchDashboards(link.tag).then(function(results) {
+ $scope.searchDashboards(link).then(function(results) {
_.each(results, function(hit) {
hit.url = linkSrv.getLinkUrl(hit);
});
@@ -154,8 +166,11 @@ function (angular, _) {
$scope.iconMap = iconMap;
$scope.dashboard.links = $scope.dashboard.links || [];
+
$scope.addLink = function() {
$scope.dashboard.links.push({ type: 'dashboards', icon: 'external link' });
+ $scope.updateSubmenuVisibility();
+ $scope.updated();
};
$scope.moveLink = function(index, dir) {
@@ -167,8 +182,10 @@ function (angular, _) {
$rootScope.appEvent('dash-links-updated');
};
- $scope.deleteLink = function(link) {
- $scope.dashboard.links = _.without($scope.dashboard.links, link);
+ $scope.deleteLink = function(index) {
+ $scope.dashboard.links.splice(index, 1);
+ $scope.updateSubmenuVisibility();
+ $scope.updated();
};
});
diff --git a/public/app/features/panellinks/linkSrv.js b/public/app/features/panellinks/linkSrv.js
index b11763d8396..2f943b13f08 100644
--- a/public/app/features/panellinks/linkSrv.js
+++ b/public/app/features/panellinks/linkSrv.js
@@ -1,63 +1,55 @@
define([
'angular',
'kbn',
+ 'lodash',
],
-function (angular, kbn) {
+function (angular, kbn, _) {
'use strict';
angular
.module('grafana.services')
.service('linkSrv', function(templateSrv, timeSrv) {
- // parseUri 1.2.2
- // (c) Steven Levithan
- // MIT License
+ this.getLinkUrl = function(link) {
+ var url = templateSrv.replace(link.url || '');
+ var params = {};
- function parseUri (str) {
- var o = parseUri.options,
- m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
- uri = {},
- i = 14;
-
- while (i--) {
- uri[o.key[i]] = m[i] || "";
+ if (link.keepTime) {
+ var range = timeSrv.timeRangeForUrl();
+ params['from'] = range.from;
+ params['to'] = range.to;
}
- uri[o.q.name] = {};
- uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
- if ($1) {
- uri[o.q.name][$1] = $2;
+ if (link.includeVars) {
+ templateSrv.fillVariableValuesForUrl(params);
+ }
+
+ return this.addParamsToUrl(url, params);
+ };
+
+ this.addParamsToUrl = function(url, params) {
+ var paramsArray = [];
+ _.each(params, function(value, key) {
+ if (value === null) { return; }
+ if (value === true) {
+ paramsArray.push(key);
+ }
+ else if (_.isArray(value)) {
+ _.each(value, function(instance) {
+ paramsArray.push(key + '=' + encodeURIComponent(instance));
+ });
+ }
+ else {
+ paramsArray.push(key + '=' + encodeURIComponent(value));
}
});
- return uri;
- }
-
- parseUri.options = {
- strictMode: false,
- key: ["source","protocol","authority","userInfo","user","password","host",
- "port","relative","path","directory","file","query","anchor"],
- q: {
- name: "queryKey",
- parser: /(?:^|&)([^&=]*)=?([^&]*)/g
- },
- /* jshint ignore:start */
- parser: {
- strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
- loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
+ if (paramsArray.length === 0) {
+ return url;
}
- /* jshint ignore:end */
- };
- this.getLinkUrl = function(link) {
- var href = templateSrv.replace(link.url || '');
- if (link.addTime) {
- var range = timeSrv.timeRangeForUrl();
- href += (href.indexOf('?') !== -1 ? '&' : '?');
- href += 'from=' + range.from;
- href += '&to=' + range.to;
- }
- return href;
+ url += (url.indexOf('?') !== -1 ? '&' : '?');
+ return url + paramsArray.join('&');
};
this.getAnchorInfo = function(link) {
diff --git a/public/app/features/templating/templateSrv.js b/public/app/features/templating/templateSrv.js
index a6b5cf6f90a..028146a1314 100644
--- a/public/app/features/templating/templateSrv.js
+++ b/public/app/features/templating/templateSrv.js
@@ -76,7 +76,7 @@ function (angular, _) {
};
this.replace = function(target, scopedVars) {
- if (!target) { return; }
+ if (!target) { return target; }
var value;
this._regex.lastIndex = 0;
@@ -95,7 +95,7 @@ function (angular, _) {
};
this.replaceWithText = function(target, scopedVars) {
- if (!target) { return; }
+ if (!target) { return target; }
var value;
var text;
@@ -115,6 +115,12 @@ function (angular, _) {
});
};
+ this.fillVariableValuesForUrl = function(params) {
+ _.each(this.variables, function(variable) {
+ params['var-' + variable.name] = variable.current.value;
+ });
+ };
+
});
});
diff --git a/public/css/less/submenu.less b/public/css/less/submenu.less
index 67022dfbe0f..a9fecffefcf 100644
--- a/public/css/less/submenu.less
+++ b/public/css/less/submenu.less
@@ -94,4 +94,5 @@
}
.dash-nav-link {
+ color: @textColor;
}
diff --git a/public/test/specs/helpers.js b/public/test/specs/helpers.js
index 0a0c7b77952..00c33ab5631 100644
--- a/public/test/specs/helpers.js
+++ b/public/test/specs/helpers.js
@@ -131,6 +131,7 @@ define([
return _.template(text, this.data, this.templateSettings);
};
this.init = function() {};
+ this.fillVariableValuesForUrl = function() {};
this.updateTemplateData = function() { };
this.variableExists = function() { return false; };
this.highlightVariablesAsHtml = function(str) { return str; };
diff --git a/public/test/specs/shareModalCtrl-specs.js b/public/test/specs/shareModalCtrl-specs.js
index 94fe2f97937..d0d69479a13 100644
--- a/public/test/specs/shareModalCtrl-specs.js
+++ b/public/test/specs/shareModalCtrl-specs.js
@@ -1,6 +1,7 @@
define([
'helpers',
- 'features/dashboard/shareModalCtrl'
+ 'features/dashboard/shareModalCtrl',
+ 'features/panellinks/linkSrv',
], function(helpers) {
'use strict';
@@ -14,8 +15,10 @@ define([
setTime({ from: 'now-1h', to: 'now' });
beforeEach(module('grafana.controllers'));
+ beforeEach(module('grafana.services'));
beforeEach(ctx.providePhase());
+
beforeEach(ctx.createControllerPhase('ShareModalCtrl'));
describe('shareUrl with current time range and panel', function() {
@@ -63,8 +66,11 @@ define([
ctx.$location.path('/test');
ctx.scope.options.includeTemplateVars = true;
- ctx.templateSrv.variables = [{ name: 'app', current: {text: 'mupp' }}, {name: 'server', current: {text: 'srv-01'}}];
setTime({ from: 'now-1h', to: 'now' });
+ ctx.templateSrv.fillVariableValuesForUrl = function(params) {
+ params['var-app'] = 'mupp';
+ params['var-server'] = 'srv-01';
+ };
ctx.scope.buildUrl();
expect(ctx.scope.shareUrl).to.be('http://server/#/test?from=now-1h&to=now&var-app=mupp&var-server=srv-01');