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