From fc9014f9204df635577c4bc0cb57a0bfdb21e25b Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Fri, 16 Mar 2018 12:36:36 +0100 Subject: [PATCH 01/37] added indent to dashboards inside folder in search dropdown, and added indent to dashboard icon in search item --- public/app/core/components/search/search_results.html | 2 +- public/sass/components/_search.scss | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/public/app/core/components/search/search_results.html b/public/app/core/components/search/search_results.html index 4e5bc88e0a9..7435f8d0b7e 100644 --- a/public/app/core/components/search/search_results.html +++ b/public/app/core/components/search/search_results.html @@ -20,7 +20,7 @@
- +
Date: Mon, 26 Mar 2018 14:14:57 +0200 Subject: [PATCH 02/37] styled login page for ie11 --- public/sass/pages/_login.scss | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/public/sass/pages/_login.scss b/public/sass/pages/_login.scss index 8622eec4e99..de10808f122 100644 --- a/public/sass/pages/_login.scss +++ b/public/sass/pages/_login.scss @@ -3,6 +3,7 @@ $login-border: #8daac5; .login { background-position: center; min-height: 85vh; + height: 80vh; background-repeat: no-repeat; min-width: 100%; margin-left: 0; @@ -290,9 +291,14 @@ select:-webkit-autofill:focus { } @include media-breakpoint-up(md) { + .login-content { + flex: 1 0 100%; + } + .login-branding { width: 45%; padding: 2rem 4rem; + flex-grow: 1; .logo-icon { width: 130px; @@ -371,7 +377,7 @@ select:-webkit-autofill:focus { left: 0; right: 0; height: 100%; - content: ""; + content: ''; display: block; } From dbcba4a0094f9c360216182d0d326a0bca127d09 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 27 Mar 2018 10:41:47 +0200 Subject: [PATCH 03/37] sidemenu fix for internet explorer 11, changed icon width/height to pixels and added height to logo --- public/sass/base/_icons.scss | 6 ++++-- public/sass/components/_sidemenu.scss | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/public/sass/base/_icons.scss b/public/sass/base/_icons.scss index c701cc1249e..31e5ee62d6f 100644 --- a/public/sass/base/_icons.scss +++ b/public/sass/base/_icons.scss @@ -1,8 +1,10 @@ .gicon { line-height: 1; display: inline-block; - width: 1.1057142857em; - height: 1.1057142857em; + //width: 1.1057142857em; + //height: 1.1057142857em; + height: 22px; + width: 22px; text-align: center; background-repeat: no-repeat; background-position: center; diff --git a/public/sass/components/_sidemenu.scss b/public/sass/components/_sidemenu.scss index 8a5c3779714..e48ab0597a2 100644 --- a/public/sass/components/_sidemenu.scss +++ b/public/sass/components/_sidemenu.scss @@ -178,6 +178,7 @@ li.sidemenu-org-switcher { padding: 0.4rem 1rem 0.4rem 0.65rem; min-height: $navbarHeight; position: relative; + height: $navbarHeight - 1px; &:hover { background: $navbarButtonBackgroundHighlight; From d4be953d23a4dd15eb2c26a003c11cd0f40dc898 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 27 Mar 2018 12:36:13 +0200 Subject: [PATCH 04/37] fixed alignment in search + fixed issue ie popup --- public/app/features/dashboard/unsaved_changes_srv.ts | 4 ++-- public/sass/components/_search.scss | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/public/app/features/dashboard/unsaved_changes_srv.ts b/public/app/features/dashboard/unsaved_changes_srv.ts index ebf0101cee0..d4c12b8bcd6 100644 --- a/public/app/features/dashboard/unsaved_changes_srv.ts +++ b/public/app/features/dashboard/unsaved_changes_srv.ts @@ -35,12 +35,12 @@ export class Tracker { $window.onbeforeunload = () => { if (this.ignoreChanges()) { - return null; + return undefined; } if (this.hasChanges()) { return 'There are unsaved changes to this dashboard'; } - return null; + return undefined; }; scope.$on('$locationChangeStart', (event, next) => { diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index 47d4a926968..e69068b730a 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -31,7 +31,6 @@ //padding: 0.5rem 1.5rem 0.5rem 0; padding: 1rem 1rem 0.75rem 1rem; height: 51px; - line-height: 51px; box-sizing: border-box; outline: none; background: $side-menu-bg; From a3f15ced6805d3d949d02c5a8a1d5267a6dac3a7 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Wed, 28 Mar 2018 14:19:17 +0200 Subject: [PATCH 05/37] fixed graphpanel editmode and custom width for right side legend for IE11 --- public/app/plugins/panel/graph/legend.ts | 5 ++++- public/sass/pages/_dashboard.scss | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index d1186ae0b1e..4dfeb75ff55 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -131,8 +131,11 @@ module.directive('graphLegend', function(popoverSrv, $timeout) { elem.empty(); // Set min-width if side style and there is a value, otherwise remove the CSS propery - var width = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth + 'px' : ''; + // Set width so it works with IE11 + var width: any = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth + 'px' : ''; + var ieWidth: any = panel.legend.rightSide && panel.legend.sideWidth ? panel.legend.sideWidth - 1 + 'px' : ''; elem.css('min-width', width); + elem.css('width', ieWidth); elem.toggleClass('graph-legend-table', panel.legend.alignAsTable === true); diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index 871db4dfc2d..c957b6af790 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -33,7 +33,7 @@ div.flot-text { border: $panel-border; position: relative; border-radius: 3px; - height: 100%; + //height: 100%; &.panel-transparent { background-color: transparent; From d40b7433ead654eea5e942eb095b94ac1ea73c76 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Wed, 28 Mar 2018 15:06:20 +0200 Subject: [PATCH 06/37] removed padding for icons and added margin --- public/sass/pages/_alerting.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/sass/pages/_alerting.scss b/public/sass/pages/_alerting.scss index f44e26d5c20..5a481b55a8a 100644 --- a/public/sass/pages/_alerting.scss +++ b/public/sass/pages/_alerting.scss @@ -108,7 +108,7 @@ justify-content: center; align-items: center; width: 40px; - padding: 0 28px 0 16px; + margin-right: 8px; .icon-gf, .fa { font-size: 200%; From 2ccbf12d1c57cdbbf6c5ad59208faf6604e19075 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 28 Mar 2018 18:03:33 +0200 Subject: [PATCH 07/37] settings: return error instead of ignoring it closes #11281 --- pkg/setting/setting.go | 25 +++++++++++++++++++------ pkg/setting/setting_test.go | 7 +++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 5b79e866964..30a40602b1c 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -223,7 +223,7 @@ func shouldRedactURLKey(s string) bool { return strings.Contains(uppercased, "DATABASE_URL") } -func applyEnvVariableOverrides() { +func applyEnvVariableOverrides() error { appliedEnvOverrides = make([]string, 0) for _, section := range Cfg.Sections() { for _, key := range section.Keys() { @@ -238,7 +238,10 @@ func applyEnvVariableOverrides() { envValue = "*********" } if shouldRedactURLKey(envKey) { - u, _ := url.Parse(envValue) + u, err := url.Parse(envValue) + if err != nil { + return fmt.Errorf("could not parse environment variable. key: %s, value: %s. error: %v", envKey, envValue, err) + } ui := u.User if ui != nil { _, exists := ui.Password() @@ -252,6 +255,8 @@ func applyEnvVariableOverrides() { } } } + + return nil } func applyCommandLineDefaultProperties(props map[string]string) { @@ -377,7 +382,7 @@ func loadSpecifedConfigFile(configFile string) error { return nil } -func loadConfiguration(args *CommandLineArgs) { +func loadConfiguration(args *CommandLineArgs) error { var err error // load config defaults @@ -395,7 +400,7 @@ func loadConfiguration(args *CommandLineArgs) { if err != nil { fmt.Println(fmt.Sprintf("Failed to parse defaults.ini, %v", err)) os.Exit(1) - return + return err } Cfg.BlockMode = false @@ -413,7 +418,10 @@ func loadConfiguration(args *CommandLineArgs) { } // apply environment overrides - applyEnvVariableOverrides() + err = applyEnvVariableOverrides() + if err != nil { + return err + } // apply command line overrides applyCommandLineProperties(commandLineProps) @@ -424,6 +432,8 @@ func loadConfiguration(args *CommandLineArgs) { // update data path and logging config DataPath = makeAbsolute(Cfg.Section("paths").Key("data").String(), HomePath) initLogging() + + return err } func pathExists(path string) bool { @@ -471,7 +481,10 @@ func validateStaticRootPath() error { func NewConfigContext(args *CommandLineArgs) error { setHomePath(args) - loadConfiguration(args) + err := loadConfiguration(args) + if err != nil { + return err + } Env = Cfg.Section("").Key("app_mode").MustString("development") InstanceName = Cfg.Section("").Key("instance_name").MustString("unknown_instance_name") diff --git a/pkg/setting/setting_test.go b/pkg/setting/setting_test.go index 640a1648340..87f9916075e 100644 --- a/pkg/setting/setting_test.go +++ b/pkg/setting/setting_test.go @@ -37,6 +37,13 @@ func TestLoadingSettings(t *testing.T) { So(appliedEnvOverrides, ShouldContain, "GF_SECURITY_ADMIN_PASSWORD=*********") }) + Convey("Should replace password when defined in environment2", func() { + os.Setenv("GF_DATABASE_URL", "postgres://grafana:sec{ret@postgres:5432/grafana") + err := NewConfigContext(&CommandLineArgs{HomePath: "../../"}) + + So(err, ShouldNotBeNil) + }) + Convey("Should replace password in URL when url environment is defined", func() { os.Setenv("GF_DATABASE_URL", "mysql://user:secret@localhost:3306/database") NewConfigContext(&CommandLineArgs{HomePath: "../../"}) From 45d9bfca97a3a3ab98889ad41d516667d575c735 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 28 Mar 2018 22:51:21 +0200 Subject: [PATCH 08/37] print to stderr since logger might not exist --- pkg/cmd/grafana-server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/grafana-server/server.go b/pkg/cmd/grafana-server/server.go index 5bbf43087ec..b8387403161 100644 --- a/pkg/cmd/grafana-server/server.go +++ b/pkg/cmd/grafana-server/server.go @@ -111,7 +111,7 @@ func (g *GrafanaServerImpl) initLogging() { }) if err != nil { - g.log.Error(err.Error()) + fmt.Fprintf(os.Stderr, "Failed to start grafana. error: %s\n", err.Error()) os.Exit(1) } From 8195c085fa2a095fe753f725663ff7217cc14365 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 29 Mar 2018 09:15:15 +0200 Subject: [PATCH 09/37] bounnd the esc key to exit timepicker --- public/app/core/services/keybindingSrv.ts | 7 +++++++ public/app/features/dashboard/timepicker/timepicker.ts | 3 +++ 2 files changed, 10 insertions(+) diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 0d468b6980f..829a3415cc1 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -10,6 +10,7 @@ import 'mousetrap-global-bind'; export class KeybindingSrv { helpModal: boolean; modalOpen = false; + timepickerOpen = false; /** @ngInject */ constructor(private $rootScope, private $location) { @@ -22,6 +23,7 @@ export class KeybindingSrv { this.setupGlobal(); appEvents.on('show-modal', () => (this.modalOpen = true)); + $rootScope.onAppEvent('openTimepicker', () => (this.timepickerOpen = true)); } setupGlobal() { @@ -72,6 +74,11 @@ export class KeybindingSrv { appEvents.emit('hide-modal'); + if (this.timepickerOpen === true) { + this.$rootScope.appEvent('closeTimepicker'); + this.timepickerOpen = false; + } + if (!this.modalOpen) { this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false }); } else { diff --git a/public/app/features/dashboard/timepicker/timepicker.ts b/public/app/features/dashboard/timepicker/timepicker.ts index 2434e691515..19c3db7f6d3 100644 --- a/public/app/features/dashboard/timepicker/timepicker.ts +++ b/public/app/features/dashboard/timepicker/timepicker.ts @@ -32,6 +32,7 @@ export class TimePickerCtrl { $rootScope.onAppEvent('shift-time-forward', () => this.move(1), $scope); $rootScope.onAppEvent('shift-time-backward', () => this.move(-1), $scope); $rootScope.onAppEvent('refresh', this.onRefresh.bind(this), $scope); + $rootScope.onAppEvent('closeTimepicker', this.openDropdown.bind(this), $scope); // init options this.panel = this.dashboard.timepicker; @@ -100,6 +101,8 @@ export class TimePickerCtrl { return; } + this.$rootScope.appEvent('openTimepicker'); + this.onRefresh(); this.editTimeRaw = this.timeRaw; this.timeOptions = rangeUtil.getRelativeTimesList(this.panel, this.rangeString); From 1f1719c49822d2a43f225143bb6d389408ad7b43 Mon Sep 17 00:00:00 2001 From: Julien Pivotto Date: Tue, 27 Mar 2018 16:16:00 +0200 Subject: [PATCH 10/37] Fix #10555 #6888 Better escape for Prometheus variables Prior this commit, C:\bar was escaped C:\\\bar. Should be C:\\\\bar. Signed-off-by: Julien Pivotto --- .../datasource/prometheus/datasource.ts | 10 +++-- .../prometheus/specs/datasource.jest.ts | 39 ++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 4c736f2c664..6cf6c713a90 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -6,8 +6,12 @@ import * as dateMath from 'app/core/utils/datemath'; import PrometheusMetricFindQuery from './metric_find_query'; import { ResultTransformer } from './result_transformer'; -function prometheusSpecialRegexEscape(value) { - return value.replace(/[\\^$*+?.()|[\]{}]/g, '\\\\$&'); +export function prometheusRegularEscape(value) { + return value.replace(/'/g, "\\\\'"); +} + +export function prometheusSpecialRegexEscape(value) { + return prometheusRegularEscape(value.replace(/\\/g, '\\\\\\\\').replace(/[$^*{}\[\]+?.()]/g, '\\\\$&')); } export class PrometheusDatasource { @@ -80,7 +84,7 @@ export class PrometheusDatasource { interpolateQueryExpr(value, variable, defaultFormatFn) { // if no multi or include all do not regexEscape if (!variable.multi && !variable.includeAll) { - return value; + return prometheusRegularEscape(value); } if (typeof value === 'string') { diff --git a/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts b/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts index cca74e023e7..d2620b93bbc 100644 --- a/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts +++ b/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts @@ -1,7 +1,7 @@ import _ from 'lodash'; import moment from 'moment'; import q from 'q'; -import { PrometheusDatasource } from '../datasource'; +import { PrometheusDatasource, prometheusSpecialRegexEscape, prometheusRegularEscape } from '../datasource'; describe('PrometheusDatasource', () => { let ctx: any = {}; @@ -101,4 +101,41 @@ describe('PrometheusDatasource', () => { }); }); }); + + describe('Prometheus regular escaping', function() { + it('should not escape simple string', function() { + expect(prometheusRegularEscape('cryptodepression')).toEqual('cryptodepression'); + }); + it("should escape '", function() { + expect(prometheusRegularEscape("looking'glass")).toEqual("looking\\\\'glass"); + }); + it('should escape multiple characters', function() { + expect(prometheusRegularEscape("'looking'glass'")).toEqual("\\\\'looking\\\\'glass\\\\'"); + }); + }); + + describe('Prometheus regexes escaping', function() { + it('should not escape simple string', function() { + expect(prometheusSpecialRegexEscape('cryptodepression')).toEqual('cryptodepression'); + }); + it('should escape $^*+?.()\\', function() { + expect(prometheusSpecialRegexEscape("looking'glass")).toEqual("looking\\\\'glass"); + expect(prometheusSpecialRegexEscape('looking{glass')).toEqual('looking\\\\{glass'); + expect(prometheusSpecialRegexEscape('looking}glass')).toEqual('looking\\\\}glass'); + expect(prometheusSpecialRegexEscape('looking[glass')).toEqual('looking\\\\[glass'); + expect(prometheusSpecialRegexEscape('looking]glass')).toEqual('looking\\\\]glass'); + expect(prometheusSpecialRegexEscape('looking$glass')).toEqual('looking\\\\$glass'); + expect(prometheusSpecialRegexEscape('looking^glass')).toEqual('looking\\\\^glass'); + expect(prometheusSpecialRegexEscape('looking*glass')).toEqual('looking\\\\*glass'); + expect(prometheusSpecialRegexEscape('looking+glass')).toEqual('looking\\\\+glass'); + expect(prometheusSpecialRegexEscape('looking?glass')).toEqual('looking\\\\?glass'); + expect(prometheusSpecialRegexEscape('looking.glass')).toEqual('looking\\\\.glass'); + expect(prometheusSpecialRegexEscape('looking(glass')).toEqual('looking\\\\(glass'); + expect(prometheusSpecialRegexEscape('looking)glass')).toEqual('looking\\\\)glass'); + expect(prometheusSpecialRegexEscape('looking\\glass')).toEqual('looking\\\\\\\\glass'); + }); + it('should escape multiple special characters', function() { + expect(prometheusSpecialRegexEscape('+looking$glass?')).toEqual('\\\\+looking\\\\$glass\\\\?'); + }); + }); }); From 65f7c5f08f891bab0cbb87e6cf18fbd8aa23d93b Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 29 Mar 2018 10:40:45 +0200 Subject: [PATCH 11/37] started migration to ts --- .../app/plugins/panel/graph/graph_tooltip.js | 292 ------------------ .../app/plugins/panel/graph/graph_tooltip.ts | 290 +++++++++++++++++ 2 files changed, 290 insertions(+), 292 deletions(-) delete mode 100644 public/app/plugins/panel/graph/graph_tooltip.js create mode 100644 public/app/plugins/panel/graph/graph_tooltip.ts diff --git a/public/app/plugins/panel/graph/graph_tooltip.js b/public/app/plugins/panel/graph/graph_tooltip.js deleted file mode 100644 index 89197717e42..00000000000 --- a/public/app/plugins/panel/graph/graph_tooltip.js +++ /dev/null @@ -1,292 +0,0 @@ -define([ - 'jquery', - 'app/core/core', -], -function ($, core) { - 'use strict'; - - var appEvents = core.appEvents; - - function GraphTooltip(elem, dashboard, scope, getSeriesFn) { - var self = this; - var ctrl = scope.ctrl; - var panel = ctrl.panel; - - var $tooltip = $('
'); - - this.destroy = function() { - $tooltip.remove(); - }; - - this.findHoverIndexFromDataPoints = function(posX, series, last) { - var ps = series.datapoints.pointsize; - var initial = last*ps; - var len = series.datapoints.points.length; - for (var j = initial; j < len; j += ps) { - // Special case of a non stepped line, highlight the very last point just before a null point - if ((!series.lines.steps && series.datapoints.points[initial] != null && series.datapoints.points[j] == null) - //normal case - || series.datapoints.points[j] > posX) { - return Math.max(j - ps, 0)/ps; - } - } - return j/ps - 1; - }; - - this.findHoverIndexFromData = function(posX, series) { - var lower = 0; - var upper = series.data.length - 1; - var middle; - while (true) { - if (lower > upper) { - return Math.max(upper, 0); - } - middle = Math.floor((lower + upper) / 2); - if (series.data[middle][0] === posX) { - return middle; - } else if (series.data[middle][0] < posX) { - lower = middle + 1; - } else { - upper = middle - 1; - } - } - }; - - this.renderAndShow = function(absoluteTime, innerHtml, pos, xMode) { - if (xMode === 'time') { - innerHtml = '
'+ absoluteTime + '
' + innerHtml; - } - $tooltip.html(innerHtml).place_tt(pos.pageX + 20, pos.pageY); - }; - - this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) { - var value, i, series, hoverIndex, hoverDistance, pointTime, yaxis; - // 3 sub-arrays, 1st for hidden series, 2nd for left yaxis, 3rd for right yaxis. - var results = [[],[],[]]; - - //now we know the current X (j) position for X and Y values - var last_value = 0; //needed for stacked values - - var minDistance, minTime; - - for (i = 0; i < seriesList.length; i++) { - series = seriesList[i]; - - if (!series.data.length || (panel.legend.hideEmpty && series.allIsNull)) { - // Init value so that it does not brake series sorting - results[0].push({ hidden: true, value: 0 }); - continue; - } - - if (!series.data.length || (panel.legend.hideZero && series.allIsZero)) { - // Init value so that it does not brake series sorting - results[0].push({ hidden: true, value: 0 }); - continue; - } - - hoverIndex = this.findHoverIndexFromData(pos.x, series); - hoverDistance = pos.x - series.data[hoverIndex][0]; - pointTime = series.data[hoverIndex][0]; - - // Take the closest point before the cursor, or if it does not exist, the closest after - if (! minDistance - || (hoverDistance >=0 && (hoverDistance < minDistance || minDistance < 0)) - || (hoverDistance < 0 && hoverDistance > minDistance)) { - minDistance = hoverDistance; - minTime = pointTime; - } - - if (series.stack) { - if (panel.tooltip.value_type === 'individual') { - value = series.data[hoverIndex][1]; - } else if (!series.stack) { - value = series.data[hoverIndex][1]; - } else { - last_value += series.data[hoverIndex][1]; - value = last_value; - } - } else { - value = series.data[hoverIndex][1]; - } - - // Highlighting multiple Points depending on the plot type - if (series.lines.steps || series.stack) { - // stacked and steppedLine plots can have series with different length. - // Stacked series can increase its length on each new stacked serie if null points found, - // to speed the index search we begin always on the last found hoverIndex. - hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex); - } - - // Be sure we have a yaxis so that it does not brake series sorting - yaxis = 0; - if (series.yaxis) { - yaxis = series.yaxis.n; - } - - results[yaxis].push({ - value: value, - hoverIndex: hoverIndex, - color: series.color, - label: series.aliasEscaped, - time: pointTime, - distance: hoverDistance, - index: i - }); - } - - // Contat the 3 sub-arrays - results = results[0].concat(results[1],results[2]); - - // Time of the point closer to pointer - results.time = minTime; - - return results; - }; - - elem.mouseleave(function () { - if (panel.tooltip.shared) { - var plot = elem.data().plot; - if (plot) { - $tooltip.detach(); - plot.unhighlight(); - } - } - appEvents.emit('graph-hover-clear'); - }); - - elem.bind("plothover", function (event, pos, item) { - self.show(pos, item); - - // broadcast to other graph panels that we are hovering! - pos.panelRelY = (pos.pageY - elem.offset().top) / elem.height(); - appEvents.emit('graph-hover', {pos: pos, panel: panel}); - }); - - elem.bind("plotclick", function (event, pos, item) { - appEvents.emit('graph-click', {pos: pos, panel: panel, item: item}); - }); - - this.clear = function(plot) { - $tooltip.detach(); - plot.clearCrosshair(); - plot.unhighlight(); - }; - - this.show = function(pos, item) { - var plot = elem.data().plot; - var plotData = plot.getData(); - var xAxes = plot.getXAxes(); - var xMode = xAxes[0].options.mode; - var seriesList = getSeriesFn(); - var allSeriesMode = panel.tooltip.shared; - var group, value, absoluteTime, hoverInfo, i, series, seriesHtml, tooltipFormat; - - // if panelRelY is defined another panel wants us to show a tooltip - // get pageX from position on x axis and pageY from relative position in original panel - if (pos.panelRelY) { - var pointOffset = plot.pointOffset({x: pos.x}); - if (Number.isNaN(pointOffset.left) || pointOffset.left < 0 || pointOffset.left > elem.width()) { - self.clear(plot); - return; - } - pos.pageX = elem.offset().left + pointOffset.left; - pos.pageY = elem.offset().top + elem.height() * pos.panelRelY; - var isVisible = pos.pageY >= $(window).scrollTop() && pos.pageY <= $(window).innerHeight() + $(window).scrollTop(); - if (!isVisible) { - self.clear(plot); - return; - } - plot.setCrosshair(pos); - allSeriesMode = true; - - if (dashboard.sharedCrosshairModeOnly()) { - // if only crosshair mode we are done - return; - } - } - - if (seriesList.length === 0) { - return; - } - - if (seriesList[0].hasMsResolution) { - tooltipFormat = 'YYYY-MM-DD HH:mm:ss.SSS'; - } else { - tooltipFormat = 'YYYY-MM-DD HH:mm:ss'; - } - - if (allSeriesMode) { - plot.unhighlight(); - - var seriesHoverInfo = self.getMultiSeriesPlotHoverInfo(plotData, pos); - - seriesHtml = ''; - - absoluteTime = dashboard.formatDate(seriesHoverInfo.time, tooltipFormat); - - // Dynamically reorder the hovercard for the current time point if the - // option is enabled. - if (panel.tooltip.sort === 2) { - seriesHoverInfo.sort(function(a, b) { - return b.value - a.value; - }); - } else if (panel.tooltip.sort === 1) { - seriesHoverInfo.sort(function(a, b) { - return a.value - b.value; - }); - } - - for (i = 0; i < seriesHoverInfo.length; i++) { - hoverInfo = seriesHoverInfo[i]; - - if (hoverInfo.hidden) { - continue; - } - - var highlightClass = ''; - if (item && hoverInfo.index === item.seriesIndex) { - highlightClass = 'graph-tooltip-list-item--highlight'; - } - - series = seriesList[hoverInfo.index]; - - value = series.formatValue(hoverInfo.value); - - seriesHtml += '
'; - seriesHtml += ' ' + hoverInfo.label + ':
'; - seriesHtml += '
' + value + '
'; - plot.highlight(hoverInfo.index, hoverInfo.hoverIndex); - } - - self.renderAndShow(absoluteTime, seriesHtml, pos, xMode); - } - // single series tooltip - else if (item) { - series = seriesList[item.seriesIndex]; - group = '
'; - group += ' ' + series.aliasEscaped + ':
'; - - if (panel.stack && panel.tooltip.value_type === 'individual') { - value = item.datapoint[1] - item.datapoint[2]; - } - else { - value = item.datapoint[1]; - } - - value = series.formatValue(value); - - absoluteTime = dashboard.formatDate(item.datapoint[0], tooltipFormat); - - group += '
' + value + '
'; - - self.renderAndShow(absoluteTime, group, pos, xMode); - } - // no hit - else { - $tooltip.detach(); - } - }; - } - - return GraphTooltip; -}); diff --git a/public/app/plugins/panel/graph/graph_tooltip.ts b/public/app/plugins/panel/graph/graph_tooltip.ts new file mode 100644 index 00000000000..846a165bbaf --- /dev/null +++ b/public/app/plugins/panel/graph/graph_tooltip.ts @@ -0,0 +1,290 @@ +import $ from 'jquery'; +import { appEvents } from 'app/core/core'; + +//var appEvents = appEvents; + +export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { + let self = this; + let ctrl = scope.ctrl; + let panel = ctrl.panel; + + let $tooltip = $('
'); + + this.destroy = function() { + $tooltip.remove(); + }; + + this.findHoverIndexFromDataPoints = function(posX, series, last) { + var ps = series.datapoints.pointsize; + var initial = last * ps; + var len = series.datapoints.points.length; + for (var j = initial; j < len; j += ps) { + // Special case of a non stepped line, highlight the very last point just before a null point + if ( + (!series.lines.steps && series.datapoints.points[initial] != null && series.datapoints.points[j] == null) || + //normal case + series.datapoints.points[j] > posX + ) { + return Math.max(j - ps, 0) / ps; + } + } + return j / ps - 1; + }; + + this.findHoverIndexFromData = function(posX, series) { + var lower = 0; + var upper = series.data.length - 1; + var middle; + while (true) { + if (lower > upper) { + return Math.max(upper, 0); + } + middle = Math.floor((lower + upper) / 2); + if (series.data[middle][0] === posX) { + return middle; + } else if (series.data[middle][0] < posX) { + lower = middle + 1; + } else { + upper = middle - 1; + } + } + }; + + this.renderAndShow = function(absoluteTime, innerHtml, pos, xMode) { + if (xMode === 'time') { + innerHtml = '
' + absoluteTime + '
' + innerHtml; + } + $tooltip.html(innerHtml).place_tt(pos.pageX + 20, pos.pageY); + }; + + this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) { + var value, i, series, hoverIndex, hoverDistance, pointTime, yaxis; + // 3 sub-arrays, 1st for hidden series, 2nd for left yaxis, 3rd for right yaxis. + var results: any = [[], [], []]; + + //now we know the current X (j) position for X and Y values + var last_value = 0; //needed for stacked values + + var minDistance, minTime; + + for (i = 0; i < seriesList.length; i++) { + series = seriesList[i]; + + if (!series.data.length || (panel.legend.hideEmpty && series.allIsNull)) { + // Init value so that it does not brake series sorting + results[0].push({ hidden: true, value: 0 }); + continue; + } + + if (!series.data.length || (panel.legend.hideZero && series.allIsZero)) { + // Init value so that it does not brake series sorting + results[0].push({ hidden: true, value: 0 }); + continue; + } + + hoverIndex = this.findHoverIndexFromData(pos.x, series); + hoverDistance = pos.x - series.data[hoverIndex][0]; + pointTime = series.data[hoverIndex][0]; + + // Take the closest point before the cursor, or if it does not exist, the closest after + if ( + !minDistance || + (hoverDistance >= 0 && (hoverDistance < minDistance || minDistance < 0)) || + (hoverDistance < 0 && hoverDistance > minDistance) + ) { + minDistance = hoverDistance; + minTime = pointTime; + } + + if (series.stack) { + if (panel.tooltip.value_type === 'individual') { + value = series.data[hoverIndex][1]; + } else if (!series.stack) { + value = series.data[hoverIndex][1]; + } else { + last_value += series.data[hoverIndex][1]; + value = last_value; + } + } else { + value = series.data[hoverIndex][1]; + } + + // Highlighting multiple Points depending on the plot type + if (series.lines.steps || series.stack) { + // stacked and steppedLine plots can have series with different length. + // Stacked series can increase its length on each new stacked serie if null points found, + // to speed the index search we begin always on the last found hoverIndex. + hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex); + } + + // Be sure we have a yaxis so that it does not brake series sorting + yaxis = 0; + if (series.yaxis) { + yaxis = series.yaxis.n; + } + + results[yaxis].push({ + value: value, + hoverIndex: hoverIndex, + color: series.color, + label: series.aliasEscaped, + time: pointTime, + distance: hoverDistance, + index: i, + }); + } + + // Contat the 3 sub-arrays + results = results[0].concat(results[1], results[2]); + + // Time of the point closer to pointer + results.time = minTime; + + return results; + }; + + elem.mouseleave(function() { + if (panel.tooltip.shared) { + var plot = elem.data().plot; + if (plot) { + $tooltip.detach(); + plot.unhighlight(); + } + } + appEvents.emit('graph-hover-clear'); + }); + + elem.bind('plothover', function(event, pos, item) { + self.show(pos, item); + + // broadcast to other graph panels that we are hovering! + pos.panelRelY = (pos.pageY - elem.offset().top) / elem.height(); + appEvents.emit('graph-hover', { pos: pos, panel: panel }); + }); + + elem.bind('plotclick', function(event, pos, item) { + appEvents.emit('graph-click', { pos: pos, panel: panel, item: item }); + }); + + this.clear = function(plot) { + $tooltip.detach(); + plot.clearCrosshair(); + plot.unhighlight(); + }; + + this.show = function(pos, item) { + var plot = elem.data().plot; + var plotData = plot.getData(); + var xAxes = plot.getXAxes(); + var xMode = xAxes[0].options.mode; + var seriesList = getSeriesFn(); + var allSeriesMode = panel.tooltip.shared; + var group, value, absoluteTime, hoverInfo, i, series, seriesHtml, tooltipFormat; + + // if panelRelY is defined another panel wants us to show a tooltip + // get pageX from position on x axis and pageY from relative position in original panel + if (pos.panelRelY) { + var pointOffset = plot.pointOffset({ x: pos.x }); + if (Number.isNaN(pointOffset.left) || pointOffset.left < 0 || pointOffset.left > elem.width()) { + self.clear(plot); + return; + } + pos.pageX = elem.offset().left + pointOffset.left; + pos.pageY = elem.offset().top + elem.height() * pos.panelRelY; + var isVisible = + pos.pageY >= $(window).scrollTop() && pos.pageY <= $(window).innerHeight() + $(window).scrollTop(); + if (!isVisible) { + self.clear(plot); + return; + } + plot.setCrosshair(pos); + allSeriesMode = true; + + if (dashboard.sharedCrosshairModeOnly()) { + // if only crosshair mode we are done + return; + } + } + + if (seriesList.length === 0) { + return; + } + + if (seriesList[0].hasMsResolution) { + tooltipFormat = 'YYYY-MM-DD HH:mm:ss.SSS'; + } else { + tooltipFormat = 'YYYY-MM-DD HH:mm:ss'; + } + + if (allSeriesMode) { + plot.unhighlight(); + + var seriesHoverInfo = self.getMultiSeriesPlotHoverInfo(plotData, pos); + + seriesHtml = ''; + + absoluteTime = dashboard.formatDate(seriesHoverInfo.time, tooltipFormat); + + // Dynamically reorder the hovercard for the current time point if the + // option is enabled. + if (panel.tooltip.sort === 2) { + seriesHoverInfo.sort(function(a, b) { + return b.value - a.value; + }); + } else if (panel.tooltip.sort === 1) { + seriesHoverInfo.sort(function(a, b) { + return a.value - b.value; + }); + } + + for (i = 0; i < seriesHoverInfo.length; i++) { + hoverInfo = seriesHoverInfo[i]; + + if (hoverInfo.hidden) { + continue; + } + + var highlightClass = ''; + if (item && hoverInfo.index === item.seriesIndex) { + highlightClass = 'graph-tooltip-list-item--highlight'; + } + + series = seriesList[hoverInfo.index]; + + value = series.formatValue(hoverInfo.value); + + seriesHtml += + '
'; + seriesHtml += + ' ' + hoverInfo.label + ':
'; + seriesHtml += '
' + value + '
'; + plot.highlight(hoverInfo.index, hoverInfo.hoverIndex); + } + + self.renderAndShow(absoluteTime, seriesHtml, pos, xMode); + } else if (item) { + // single series tooltip + series = seriesList[item.seriesIndex]; + group = '
'; + group += + ' ' + series.aliasEscaped + ':
'; + + if (panel.stack && panel.tooltip.value_type === 'individual') { + value = item.datapoint[1] - item.datapoint[2]; + } else { + value = item.datapoint[1]; + } + + value = series.formatValue(value); + + absoluteTime = dashboard.formatDate(item.datapoint[0], tooltipFormat); + + group += '
' + value + '
'; + + self.renderAndShow(absoluteTime, group, pos, xMode); + } else { + // no hit + $tooltip.detach(); + } + }; +} From 7b9b34c6e188344665948cca705c0e4d24389608 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 29 Mar 2018 11:51:34 +0200 Subject: [PATCH 12/37] migrated graph_tooltip to ts --- .../app/plugins/panel/graph/graph_tooltip.ts | 49 +++++++++---------- .../panel/graph/specs/tooltip_specs.ts | 5 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/public/app/plugins/panel/graph/graph_tooltip.ts b/public/app/plugins/panel/graph/graph_tooltip.ts index 846a165bbaf..509d15b8a25 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.ts +++ b/public/app/plugins/panel/graph/graph_tooltip.ts @@ -1,8 +1,6 @@ import $ from 'jquery'; import { appEvents } from 'app/core/core'; -//var appEvents = appEvents; - export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { let self = this; let ctrl = scope.ctrl; @@ -15,10 +13,11 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { }; this.findHoverIndexFromDataPoints = function(posX, series, last) { - var ps = series.datapoints.pointsize; - var initial = last * ps; - var len = series.datapoints.points.length; - for (var j = initial; j < len; j += ps) { + let ps = series.datapoints.pointsize; + let initial = last * ps; + let len = series.datapoints.points.length; + let j; + for (j = initial; j < len; j += ps) { // Special case of a non stepped line, highlight the very last point just before a null point if ( (!series.lines.steps && series.datapoints.points[initial] != null && series.datapoints.points[j] == null) || @@ -32,9 +31,9 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { }; this.findHoverIndexFromData = function(posX, series) { - var lower = 0; - var upper = series.data.length - 1; - var middle; + let lower = 0; + let upper = series.data.length - 1; + let middle; while (true) { if (lower > upper) { return Math.max(upper, 0); @@ -58,14 +57,14 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { }; this.getMultiSeriesPlotHoverInfo = function(seriesList, pos) { - var value, i, series, hoverIndex, hoverDistance, pointTime, yaxis; + let value, i, series, hoverIndex, hoverDistance, pointTime, yaxis; // 3 sub-arrays, 1st for hidden series, 2nd for left yaxis, 3rd for right yaxis. - var results: any = [[], [], []]; + let results: any = [[], [], []]; //now we know the current X (j) position for X and Y values - var last_value = 0; //needed for stacked values + let last_value = 0; //needed for stacked values - var minDistance, minTime; + let minDistance, minTime; for (i = 0; i < seriesList.length; i++) { series = seriesList[i]; @@ -145,7 +144,7 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { elem.mouseleave(function() { if (panel.tooltip.shared) { - var plot = elem.data().plot; + let plot = elem.data().plot; if (plot) { $tooltip.detach(); plot.unhighlight(); @@ -173,25 +172,25 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { }; this.show = function(pos, item) { - var plot = elem.data().plot; - var plotData = plot.getData(); - var xAxes = plot.getXAxes(); - var xMode = xAxes[0].options.mode; - var seriesList = getSeriesFn(); - var allSeriesMode = panel.tooltip.shared; - var group, value, absoluteTime, hoverInfo, i, series, seriesHtml, tooltipFormat; + let plot = elem.data().plot; + let plotData = plot.getData(); + let xAxes = plot.getXAxes(); + let xMode = xAxes[0].options.mode; + let seriesList = getSeriesFn(); + let allSeriesMode = panel.tooltip.shared; + let group, value, absoluteTime, hoverInfo, i, series, seriesHtml, tooltipFormat; // if panelRelY is defined another panel wants us to show a tooltip // get pageX from position on x axis and pageY from relative position in original panel if (pos.panelRelY) { - var pointOffset = plot.pointOffset({ x: pos.x }); + let pointOffset = plot.pointOffset({ x: pos.x }); if (Number.isNaN(pointOffset.left) || pointOffset.left < 0 || pointOffset.left > elem.width()) { self.clear(plot); return; } pos.pageX = elem.offset().left + pointOffset.left; pos.pageY = elem.offset().top + elem.height() * pos.panelRelY; - var isVisible = + let isVisible = pos.pageY >= $(window).scrollTop() && pos.pageY <= $(window).innerHeight() + $(window).scrollTop(); if (!isVisible) { self.clear(plot); @@ -219,7 +218,7 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { if (allSeriesMode) { plot.unhighlight(); - var seriesHoverInfo = self.getMultiSeriesPlotHoverInfo(plotData, pos); + let seriesHoverInfo = self.getMultiSeriesPlotHoverInfo(plotData, pos); seriesHtml = ''; @@ -244,7 +243,7 @@ export default function GraphTooltip(elem, dashboard, scope, getSeriesFn) { continue; } - var highlightClass = ''; + let highlightClass = ''; if (item && hoverInfo.index === item.seriesIndex) { highlightClass = 'graph-tooltip-list-item--highlight'; } diff --git a/public/app/plugins/panel/graph/specs/tooltip_specs.ts b/public/app/plugins/panel/graph/specs/tooltip_specs.ts index c12697eadac..7dd5ed9b8a9 100644 --- a/public/app/plugins/panel/graph/specs/tooltip_specs.ts +++ b/public/app/plugins/panel/graph/specs/tooltip_specs.ts @@ -11,6 +11,7 @@ var scope = { var elem = $('
'); var dashboard = {}; +var getSeriesFn; function describeSharedTooltip(desc, fn) { var ctx: any = {}; @@ -30,7 +31,7 @@ function describeSharedTooltip(desc, fn) { describe(desc, function() { beforeEach(function() { ctx.setupFn(); - var tooltip = new GraphTooltip(elem, dashboard, scope); + var tooltip = new GraphTooltip(elem, dashboard, scope, getSeriesFn); ctx.results = tooltip.getMultiSeriesPlotHoverInfo(ctx.data, ctx.pos); }); @@ -39,7 +40,7 @@ function describeSharedTooltip(desc, fn) { } describe('findHoverIndexFromData', function() { - var tooltip = new GraphTooltip(elem, dashboard, scope); + var tooltip = new GraphTooltip(elem, dashboard, scope, getSeriesFn); var series = { data: [[100, 0], [101, 0], [102, 0], [103, 0], [104, 0], [105, 0], [106, 0], [107, 0]], }; From 9ef5f2700dd12fc7bfa1c6ac1c4f7cedcf2a52f3 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 29 Mar 2018 15:02:00 +0200 Subject: [PATCH 13/37] timepicker now closes without exiting edit/view mode, close order: modal, timepicker, view --- public/app/core/services/keybindingSrv.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 829a3415cc1..e51c0477ffa 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -74,13 +74,13 @@ export class KeybindingSrv { appEvents.emit('hide-modal'); - if (this.timepickerOpen === true) { - this.$rootScope.appEvent('closeTimepicker'); - this.timepickerOpen = false; - } - if (!this.modalOpen) { - this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false }); + if (this.timepickerOpen) { + this.$rootScope.appEvent('closeTimepicker'); + this.timepickerOpen = false; + } else { + this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false }); + } } else { this.modalOpen = false; } From de6cd7ed0bf754e8af1e1a7ca54ff1040b760ac2 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 29 Mar 2018 22:21:41 +0200 Subject: [PATCH 14/37] docker: add users and groups to ldap block --- docker/blocks/openldap/Dockerfile | 1 + docker/blocks/openldap/entrypoint.sh | 10 +++++++--- docker/blocks/openldap/notes.md | 13 +++++++++++++ docker/blocks/openldap/prepopulate/admin.ldif | 10 ++++++++++ docker/blocks/openldap/prepopulate/adminsgroup.ldif | 5 +++++ docker/blocks/openldap/prepopulate/editor.ldif | 10 ++++++++++ docker/blocks/openldap/prepopulate/usersgroup.ldif | 5 +++++ docker/blocks/openldap/prepopulate/viewer.ldif | 9 +++++++++ 8 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 docker/blocks/openldap/notes.md create mode 100644 docker/blocks/openldap/prepopulate/admin.ldif create mode 100644 docker/blocks/openldap/prepopulate/adminsgroup.ldif create mode 100644 docker/blocks/openldap/prepopulate/editor.ldif create mode 100644 docker/blocks/openldap/prepopulate/usersgroup.ldif create mode 100644 docker/blocks/openldap/prepopulate/viewer.ldif diff --git a/docker/blocks/openldap/Dockerfile b/docker/blocks/openldap/Dockerfile index d073e274356..54e383a6a97 100644 --- a/docker/blocks/openldap/Dockerfile +++ b/docker/blocks/openldap/Dockerfile @@ -17,6 +17,7 @@ EXPOSE 389 VOLUME ["/etc/ldap", "/var/lib/ldap"] COPY modules/ /etc/ldap.dist/modules +COPY prepopulate/ /etc/ldap.dist/prepopulate COPY entrypoint.sh /entrypoint.sh diff --git a/docker/blocks/openldap/entrypoint.sh b/docker/blocks/openldap/entrypoint.sh index 39a8b892de8..d560b78d388 100755 --- a/docker/blocks/openldap/entrypoint.sh +++ b/docker/blocks/openldap/entrypoint.sh @@ -65,7 +65,7 @@ EOF fi if [[ -n "$SLAPD_ADDITIONAL_SCHEMAS" ]]; then - IFS=","; declare -a schemas=($SLAPD_ADDITIONAL_SCHEMAS) + IFS=","; declare -a schemas=($SLAPD_ADDITIONAL_SCHEMAS); unset IFS for schema in "${schemas[@]}"; do slapadd -n0 -F /etc/ldap/slapd.d -l "/etc/ldap/schema/${schema}.ldif" >/dev/null 2>&1 @@ -73,14 +73,18 @@ EOF fi if [[ -n "$SLAPD_ADDITIONAL_MODULES" ]]; then - IFS=","; declare -a modules=($SLAPD_ADDITIONAL_MODULES) + IFS=","; declare -a modules=($SLAPD_ADDITIONAL_MODULES); unset IFS for module in "${modules[@]}"; do slapadd -n0 -F /etc/ldap/slapd.d -l "/etc/ldap/modules/${module}.ldif" >/dev/null 2>&1 done fi - chown -R openldap:openldap /etc/ldap/slapd.d/ + for file in `ls /etc/ldap/prepopulate/*.ldif`; do + slapadd -F /etc/ldap/slapd.d -l "$file" + done + + chown -R openldap:openldap /etc/ldap/slapd.d/ /var/lib/ldap/ /var/run/slapd/ else slapd_configs_in_env=`env | grep 'SLAPD_'` diff --git a/docker/blocks/openldap/notes.md b/docker/blocks/openldap/notes.md new file mode 100644 index 00000000000..71813c2899a --- /dev/null +++ b/docker/blocks/openldap/notes.md @@ -0,0 +1,13 @@ +# Notes on OpenLdap Docker Block + +Any ldif files added to the prepopulate subdirectory will be automatically imported into the OpenLdap database. + +The ldif files add three users, `ldapviewer`, `ldapeditor` and `ldapadmin`. Two groups, `admins` and `users`, are added that correspond with the group mappings in the default conf/ldap.toml. `ldapadmin` is a member of `admins` and `ldapeditor` is a member of `users`. + +Note that users that are added here need to specify a `memberOf` attribute manually as well as the `member` attribute for the group. The `memberOf` module usually does this automatically (if you add a group in Apache Directory Studio for example) but this does not work in the entrypoint script as it uses the `slapadd` command to add entries before the server has started and before the `memberOf` module is loaded. + +After adding ldif files to `prepopulate`: + +1. Remove your current docker image: `docker rm docker_openldap_1` +2. Build: `docker-compose build` +3. `docker-compose up` diff --git a/docker/blocks/openldap/prepopulate/admin.ldif b/docker/blocks/openldap/prepopulate/admin.ldif new file mode 100644 index 00000000000..3f4406d5810 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/admin.ldif @@ -0,0 +1,10 @@ +dn: cn=ldapadmin,dc=grafana,dc=org +mail: ldapadmin@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldapadmin +cn: ldapadmin +memberOf: cn=admins,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/adminsgroup.ldif b/docker/blocks/openldap/prepopulate/adminsgroup.ldif new file mode 100644 index 00000000000..d8dece4e458 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/adminsgroup.ldif @@ -0,0 +1,5 @@ +dn: cn=admins,dc=grafana,dc=org +cn: admins +member: cn=ldapadmin,dc=grafana,dc=org +objectClass: groupOfNames +objectClass: top diff --git a/docker/blocks/openldap/prepopulate/editor.ldif b/docker/blocks/openldap/prepopulate/editor.ldif new file mode 100644 index 00000000000..eba3adc4352 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/editor.ldif @@ -0,0 +1,10 @@ +dn: cn=ldapeditor,dc=grafana,dc=org +mail: ldapeditor@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldapeditor +cn: ldapeditor +memberOf: cn=users,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/usersgroup.ldif b/docker/blocks/openldap/prepopulate/usersgroup.ldif new file mode 100644 index 00000000000..a1de3a50d38 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/usersgroup.ldif @@ -0,0 +1,5 @@ +dn: cn=users,dc=grafana,dc=org +cn: users +member: cn=ldapeditor,dc=grafana,dc=org +objectClass: groupOfNames +objectClass: top diff --git a/docker/blocks/openldap/prepopulate/viewer.ldif b/docker/blocks/openldap/prepopulate/viewer.ldif new file mode 100644 index 00000000000..f699a7df57b --- /dev/null +++ b/docker/blocks/openldap/prepopulate/viewer.ldif @@ -0,0 +1,9 @@ +dn: cn=ldapviewer,dc=grafana,dc=org +mail: ldapviewer@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldapviewer +cn: ldapviewer From cb156ee30b415a3b8fea5ad9bfc8a81f5b85cedf Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Fri, 30 Mar 2018 16:42:48 +0200 Subject: [PATCH 15/37] fix some typos --- docs/sources/reference/dashboard.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/sources/reference/dashboard.md b/docs/sources/reference/dashboard.md index dbc3ed8635c..59a5e4155ba 100644 --- a/docs/sources/reference/dashboard.md +++ b/docs/sources/reference/dashboard.md @@ -71,13 +71,13 @@ Each field in the dashboard JSON is explained below with its usage: | **timepicker** | timepicker metadata, see [timepicker section](#timepicker) for details | | **templating** | templating metadata, see [templating section](#templating) for details | | **annotations** | annotations metadata, see [annotations section](#annotations) for details | -| **schemaVersion** | version of the JSON schema (integer), incremented each time a Grafana update brings changes to the said schema | +| **schemaVersion** | version of the JSON schema (integer), incremented each time a Grafana update brings changes to said schema | | **version** | version of the dashboard (integer), incremented each time the dashboard is updated | | **panels** | panels array, see below for detail. | ## Panels -Panels are the building blocks a dashboard. It consists of datasource queries, type of graphs, aliases, etc. Panel JSON consists of an array of JSON objects, each representing a different panel. Most of the fields are common for all panels but some fields depends on the panel type. Following is an example of panel JSON of a text panel. +Panels are the building blocks of a dashboard. It consists of datasource queries, type of graphs, aliases, etc. Panel JSON consists of an array of JSON objects, each representing a different panel. Most of the fields are common for all panels but some fields depend on the panel type. Following is an example of panel JSON of a text panel. ```json "panels": [ @@ -105,7 +105,7 @@ The gridPos property describes the panel size and position in grid coordinates. - `x` The x position, in same unit as `w`. - `y` The y position, in same unit as `h`. -The grid has a negative gravity that moves panels up if there i empty space above a panel. +The grid has a negative gravity that moves panels up if there is empty space above a panel. ### timepicker @@ -161,7 +161,7 @@ Usage of the fields is explained below: ### templating -`templating` fields contains array of template variables with their saved values along with some other metadata, for example: +`templating` field contains an array of template variables with their saved values along with some other metadata, for example: ```json "templating": { @@ -236,7 +236,7 @@ Usage of the above mentioned fields in the templating section is explained below | Name | Usage | | ---- | ----- | | **enable** | whether templating is enabled or not | -| **list** | an array of objects representing, each representing one template variable | +| **list** | an array of objects each representing one template variable | | **allFormat** | format to use while fetching all values from datasource, eg: `wildcard`, `glob`, `regex`, `pipe`, etc. | | **current** | shows current selected variable text/value on the dashboard | | **datasource** | shows datasource for the variables | From 2bdcebb5b1ce52f090dfd39e3367d05596c65521 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Fri, 30 Mar 2018 16:47:51 +0200 Subject: [PATCH 16/37] add article --- docs/sources/reference/dashboard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/reference/dashboard.md b/docs/sources/reference/dashboard.md index 59a5e4155ba..30581968743 100644 --- a/docs/sources/reference/dashboard.md +++ b/docs/sources/reference/dashboard.md @@ -161,7 +161,7 @@ Usage of the fields is explained below: ### templating -`templating` field contains an array of template variables with their saved values along with some other metadata, for example: +The `templating` field contains an array of template variables with their saved values along with some other metadata, for example: ```json "templating": { From 4538ffc0e827777f7731a4519ca102d17d4857e3 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 30 Mar 2018 20:30:57 +0200 Subject: [PATCH 17/37] changelog: adds note about closing #11555 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03ad228f3c3..48b5ab29aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ * **Shortcuts**: Add shortcut for duplicate panel [#11102](https://github.com/grafana/grafana/issues/11102) * **AuthProxy**: Support IPv6 in Auth proxy white list [#11330](https://github.com/grafana/grafana/pull/11330), thx [@corny](https://github.com/corny) * **SMTP**: Don't connect to STMP server using TLS unless configured. [#7189](https://github.com/grafana/grafana/issues/7189) +* **Prometheus**: Escape backslash in labels correctly. [#10555](https://github.com/grafana/grafana/issues/10555), thx [@roidelapluie](https://github.com/roidelapluie) # 5.0.4 (2018-03-28) From 13deb891f35c963a64d2dfe6db1282d6dbde67c2 Mon Sep 17 00:00:00 2001 From: Alexey Velikiy Date: Mon, 2 Apr 2018 09:09:10 +0300 Subject: [PATCH 18/37] No need for node_modules/bin in npm run-script (#11449) how run-script works: https://docs.npmjs.com/cli/run-script --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 6dcfc16b82b..030219fe587 100644 --- a/package.json +++ b/package.json @@ -104,10 +104,10 @@ "test": "grunt test", "test:coverage": "grunt test --coverage=true", "lint": "tslint -c tslint.json --project tsconfig.json --type-check", - "karma": "node ./node_modules/grunt-cli/bin/grunt karma:dev", - "jest": "node ./node_modules/jest-cli/bin/jest.js --notify --watch", - "api-tests": "node ./node_modules/jest-cli/bin/jest.js --notify --watch --config=tests/api/jest.js", - "precommit": "lint-staged && node ./node_modules/grunt-cli/bin/grunt precommit" + "karma": "grunt karma:dev", + "jest": "jest --notify --watch", + "api-tests": "jest --notify --watch --config=tests/api/jest.js", + "precommit": "lint-staged && grunt precommit" }, "lint-staged": { "*.{ts,tsx}": [ From 00f67ea7c7511ff5cb18fd05cfaba9c6f299e98f Mon Sep 17 00:00:00 2001 From: Alexey Velikiy Date: Mon, 2 Apr 2018 09:13:22 +0300 Subject: [PATCH 19/37] rm panel.type constrain from threshold_mapper.ts (#11448) --- public/app/features/alerting/threshold_mapper.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/public/app/features/alerting/threshold_mapper.ts b/public/app/features/alerting/threshold_mapper.ts index 3025e13aacd..9142c74b6e3 100644 --- a/public/app/features/alerting/threshold_mapper.ts +++ b/public/app/features/alerting/threshold_mapper.ts @@ -1,9 +1,5 @@ export class ThresholdMapper { static alertToGraphThresholds(panel) { - if (panel.type !== 'graph') { - return false; - } - for (var i = 0; i < panel.alert.conditions.length; i++) { let condition = panel.alert.conditions[i]; if (condition.type !== 'query') { From 6320bdf39399b0f73b65025f3c55f4d264fc1dc0 Mon Sep 17 00:00:00 2001 From: Alexey Velikiy Date: Tue, 3 Apr 2018 07:21:36 +0300 Subject: [PATCH 20/37] Webpack Grafana plugin template project to links (#11457) --- PLUGIN_DEV.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PLUGIN_DEV.md b/PLUGIN_DEV.md index 9d831a95697..4e2e080ebe6 100644 --- a/PLUGIN_DEV.md +++ b/PLUGIN_DEV.md @@ -9,6 +9,7 @@ upgrading Grafana please check here before creating an issue. - [Datasource plugin written in typescript](https://github.com/grafana/typescript-template-datasource) - [Simple json dataource plugin](https://github.com/grafana/simple-json-datasource) - [Plugin development guide](http://docs.grafana.org/plugins/developing/development/) +- [Webpack Grafana plugin template project](https://github.com/CorpGlory/grafana-plugin-template-webpack) ## Changes in v4.6 From 98e1404fed0a1cab9cdc6f7404d805459f374f48 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 3 Apr 2018 09:39:46 +0200 Subject: [PATCH 21/37] added if to onAppevent, renamed appevent, add appevent to applyCustom and setRelativeFilter --- public/app/core/services/keybindingSrv.ts | 8 +++++++- public/app/features/dashboard/timepicker/timepicker.ts | 5 +++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index e51c0477ffa..35cd7808d12 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -23,7 +23,13 @@ export class KeybindingSrv { this.setupGlobal(); appEvents.on('show-modal', () => (this.modalOpen = true)); - $rootScope.onAppEvent('openTimepicker', () => (this.timepickerOpen = true)); + $rootScope.onAppEvent('escTimepicker', () => { + if (!this.timepickerOpen) { + this.timepickerOpen = true; + } else { + this.timepickerOpen = false; + } + }); } setupGlobal() { diff --git a/public/app/features/dashboard/timepicker/timepicker.ts b/public/app/features/dashboard/timepicker/timepicker.ts index 19c3db7f6d3..32ce07e4468 100644 --- a/public/app/features/dashboard/timepicker/timepicker.ts +++ b/public/app/features/dashboard/timepicker/timepicker.ts @@ -96,13 +96,12 @@ export class TimePickerCtrl { } openDropdown() { + this.$rootScope.appEvent('escTimepicker'); if (this.isOpen) { this.isOpen = false; return; } - this.$rootScope.appEvent('openTimepicker'); - this.onRefresh(); this.editTimeRaw = this.timeRaw; this.timeOptions = rangeUtil.getRelativeTimesList(this.panel, this.rangeString); @@ -118,6 +117,7 @@ export class TimePickerCtrl { } applyCustom() { + this.$rootScope.appEvent('escTimepicker'); if (this.refresh.value !== this.dashboard.refresh) { this.timeSrv.setAutoRefresh(this.refresh.value); } @@ -139,6 +139,7 @@ export class TimePickerCtrl { } setRelativeFilter(timespan) { + this.$rootScope.appEvent('escTimepicker'); var range = { from: timespan.from, to: timespan.to }; if (this.panel.nowDelay && range.to === 'now') { From 8b2441e0985f68b11e16f153c9a485cc2c873656 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 3 Apr 2018 09:51:29 +0200 Subject: [PATCH 22/37] mssql: typos in help sections Fixes #11455 --- .../plugins/datasource/mssql/partials/annotations.editor.html | 2 +- public/app/plugins/datasource/mssql/partials/query.editor.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/datasource/mssql/partials/annotations.editor.html b/public/app/plugins/datasource/mssql/partials/annotations.editor.html index 75eaa3ed1d9..8a94c470379 100644 --- a/public/app/plugins/datasource/mssql/partials/annotations.editor.html +++ b/public/app/plugins/datasource/mssql/partials/annotations.editor.html @@ -28,7 +28,7 @@ An annotation is an event that is overlayed on top of graphs. The query can have Macros: - $__time(column) -> column AS time - $__timeEpoch(column) -> DATEDIFF(second, '1970-01-01', column) AS time -- $__timeFilter(column) -> column >= DATEADD(s, 18446744066914186738, '1970-01-01') AND column &t;= DATEADD(s, 18446744066914187038, '1970-01-01') +- $__timeFilter(column) -> column >= DATEADD(s, 18446744066914186738, '1970-01-01') AND column <= DATEADD(s, 18446744066914187038, '1970-01-01') - $__unixEpochFilter(column) -> column >= 1492750877 AND column <= 1492750877 Or build your own conditionals using these macros which just return the values: diff --git a/public/app/plugins/datasource/mssql/partials/query.editor.html b/public/app/plugins/datasource/mssql/partials/query.editor.html index c7dc030be6e..f29dfa18db2 100644 --- a/public/app/plugins/datasource/mssql/partials/query.editor.html +++ b/public/app/plugins/datasource/mssql/partials/query.editor.html @@ -49,7 +49,7 @@ Table: Macros: - $__time(column) -> column AS time - $__timeEpoch(column) -> DATEDIFF(second, '1970-01-01', column) AS time -- $__timeFilter(column) -> column >= DATEADD(s, 18446744066914186738, '1970-01-01') AND column &t;= DATEADD(s, 18446744066914187038, '1970-01-01') +- $__timeFilter(column) -> column >= DATEADD(s, 18446744066914186738, '1970-01-01') AND column <= DATEADD(s, 18446744066914187038, '1970-01-01') - $__unixEpochFilter(column) -> column >= 1492750877 AND column <= 1492750877 - $__timeGroup(column, '5m'[, fillvalue]) -> CAST(ROUND(DATEDIFF(second, '1970-01-01', column)/300.0, 0) as bigint)*300. Providing a fillValue of NULL or floating value will automatically fill empty series in timerange with that value. From feb222f63327c8dbac43a2d44d4207f80bcead2c Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 3 Apr 2018 09:53:14 +0200 Subject: [PATCH 23/37] changed variable for tabbed close btn hover, and changed text-strong variable for lighttheme, removed commented out variable --- public/sass/_variables.light.scss | 3 +-- public/sass/components/_tabbed_view.scss | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/public/sass/_variables.light.scss b/public/sass/_variables.light.scss index a59350d2195..bb8f93dbe69 100644 --- a/public/sass/_variables.light.scss +++ b/public/sass/_variables.light.scss @@ -59,9 +59,8 @@ $critical: #ec2128; $body-bg: $gray-7; $page-bg: $gray-7; $body-color: $gray-1; -//$text-color: $dark-4; $text-color: $gray-1; -$text-color-strong: $white; +$text-color-strong: $dark-2; $text-color-weak: $gray-2; $text-color-faint: $gray-4; $text-color-emphasis: $dark-5; diff --git a/public/sass/components/_tabbed_view.scss b/public/sass/components/_tabbed_view.scss index dfd760753fe..bf95d453504 100644 --- a/public/sass/components/_tabbed_view.scss +++ b/public/sass/components/_tabbed_view.scss @@ -43,7 +43,7 @@ font-size: 120%; } &:hover { - color: $white; + color: $text-color-strong; } } From 658c6a8ff4b959b6cd394550ea7d9ecc7d2e301c Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Tue, 3 Apr 2018 15:43:25 +0200 Subject: [PATCH 24/37] changed from margin to padding --- public/sass/pages/_alerting.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/sass/pages/_alerting.scss b/public/sass/pages/_alerting.scss index 5a481b55a8a..fb6b6e78d1b 100644 --- a/public/sass/pages/_alerting.scss +++ b/public/sass/pages/_alerting.scss @@ -108,7 +108,8 @@ justify-content: center; align-items: center; width: 40px; - margin-right: 8px; + //margin-right: 8px; + padding: 0 4px 0 2px; .icon-gf, .fa { font-size: 200%; From 533f3a9e8c7df8dfd0f769c707c49aff55241bf8 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 3 Apr 2018 19:09:49 +0200 Subject: [PATCH 25/37] settings: fixes test For some reason, the url parse does not fail anymore for curly braces. Add a colon in the first segment to make sure the url parse fails. --- pkg/setting/setting_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/setting/setting_test.go b/pkg/setting/setting_test.go index 87f9916075e..2da728b7298 100644 --- a/pkg/setting/setting_test.go +++ b/pkg/setting/setting_test.go @@ -37,8 +37,8 @@ func TestLoadingSettings(t *testing.T) { So(appliedEnvOverrides, ShouldContain, "GF_SECURITY_ADMIN_PASSWORD=*********") }) - Convey("Should replace password when defined in environment2", func() { - os.Setenv("GF_DATABASE_URL", "postgres://grafana:sec{ret@postgres:5432/grafana") + Convey("Should return an error when url is invalid", func() { + os.Setenv("GF_DATABASE_URL", "postgres.%31://grafana:secret@postgres:5432/grafana") err := NewConfigContext(&CommandLineArgs{HomePath: "../../"}) So(err, ShouldNotBeNil) From a6c76355285aef2f477e288a9104293870ed8ac6 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 3 Apr 2018 19:39:50 +0200 Subject: [PATCH 26/37] graphite: fixes #11434 --- public/app/plugins/datasource/graphite/add_graphite_func.ts | 1 + public/app/plugins/datasource/graphite/func_editor.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/public/app/plugins/datasource/graphite/add_graphite_func.ts b/public/app/plugins/datasource/graphite/add_graphite_func.ts index f2a596c7071..6e64b5d12d0 100644 --- a/public/app/plugins/datasource/graphite/add_graphite_func.ts +++ b/public/app/plugins/datasource/graphite/add_graphite_func.ts @@ -4,6 +4,7 @@ import $ from 'jquery'; import rst2html from 'rst2html'; import Drop from 'tether-drop'; +/** @ngInject */ export function graphiteAddFunc($compile) { const inputTemplate = ''; diff --git a/public/app/plugins/datasource/graphite/func_editor.ts b/public/app/plugins/datasource/graphite/func_editor.ts index 86135aef343..82a838e7660 100644 --- a/public/app/plugins/datasource/graphite/func_editor.ts +++ b/public/app/plugins/datasource/graphite/func_editor.ts @@ -3,6 +3,7 @@ import _ from 'lodash'; import $ from 'jquery'; import rst2html from 'rst2html'; +/** @ngInject */ export function graphiteFuncEditor($compile, templateSrv, popoverSrv) { const funcSpanTemplate = '
{{func.def.name}}('; const paramTemplate = From 4a93766143a73fe8af896c25cf102e1ca93a90f7 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Fri, 16 Mar 2018 12:56:39 -0400 Subject: [PATCH 27/37] Add case-insensitive sort for variables. --- public/app/features/templating/editor_ctrl.ts | 2 ++ public/app/features/templating/query_variable.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/public/app/features/templating/editor_ctrl.ts b/public/app/features/templating/editor_ctrl.ts index 428770a21e5..f20e93be42c 100644 --- a/public/app/features/templating/editor_ctrl.ts +++ b/public/app/features/templating/editor_ctrl.ts @@ -23,6 +23,8 @@ export class VariableEditorCtrl { { value: 2, text: 'Alphabetical (desc)' }, { value: 3, text: 'Numerical (asc)' }, { value: 4, text: 'Numerical (desc)' }, + { value: 5, text: 'Alphabetical (case-insensitive, asc)' }, + { value: 6, text: 'Alphabetical (case-insensitive, desc)' }, ]; $scope.hideOptions = [{ value: 0, text: '' }, { value: 1, text: 'Label' }, { value: 2, text: 'Variable' }]; diff --git a/public/app/features/templating/query_variable.ts b/public/app/features/templating/query_variable.ts index 58c7b692581..b87167ad646 100644 --- a/public/app/features/templating/query_variable.ts +++ b/public/app/features/templating/query_variable.ts @@ -197,6 +197,8 @@ export class QueryVariable implements Variable { return parseInt(matches[1], 10); } }); + } else if (sortType === 3) { + options = _.sortBy(options, opt => { return _.toLower(opt.text); }); } if (reverseSort) { From 70eb281840f9d7b786eb315356ed4f195b489506 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 3 Apr 2018 22:34:16 +0200 Subject: [PATCH 28/37] variables: adds test for variable sorting ref #11280 --- .../templating/specs/query_variable.jest.ts | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/public/app/features/templating/specs/query_variable.jest.ts b/public/app/features/templating/specs/query_variable.jest.ts index 7840d9e4242..ce753a4b205 100644 --- a/public/app/features/templating/specs/query_variable.jest.ts +++ b/public/app/features/templating/specs/query_variable.jest.ts @@ -40,11 +40,11 @@ describe('QueryVariable', () => { }); describe('can convert and sort metric names', () => { - var variable = new QueryVariable({}, null, null, null, null); - variable.sort = 3; // Numerical (asc) + const variable = new QueryVariable({}, null, null, null, null); + let input; - describe('can sort a mixed array of metric variables', () => { - var input = [ + beforeEach(() => { + input = [ { text: '0', value: '0' }, { text: '1', value: '1' }, { text: null, value: 3 }, @@ -58,11 +58,18 @@ describe('QueryVariable', () => { { text: '', value: undefined }, { text: undefined, value: '' }, ]; + }); + + describe('can sort a mixed array of metric variables in numeric order', () => { + let result; + + beforeEach(() => { + variable.sort = 3; // Numerical (asc) + result = variable.metricNamesToVariableValues(input); + }); - var result = variable.metricNamesToVariableValues(input); it('should return in same order', () => { var i = 0; - expect(result.length).toBe(11); expect(result[i++].text).toBe(''); expect(result[i++].text).toBe('0'); @@ -73,5 +80,27 @@ describe('QueryVariable', () => { expect(result[i++].text).toBe('6'); }); }); + + describe('can sort a mixed array of metric variables in alphabetical order', () => { + let result; + + beforeEach(() => { + variable.sort = 5; // Alphabetical CI (asc) + result = variable.metricNamesToVariableValues(input); + }); + + it('should return in same order', () => { + var i = 0; + console.log(result); + expect(result.length).toBe(11); + expect(result[i++].text).toBe(''); + expect(result[i++].text).toBe('0'); + expect(result[i++].text).toBe('1'); + expect(result[i++].text).toBe('10'); + expect(result[i++].text).toBe('3'); + expect(result[i++].text).toBe('4'); + expect(result[i++].text).toBe('5'); + }); + }); }); }); From 4b1c1acab42f06e317ddbf2e91ff59336f2a9306 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 3 Apr 2018 22:40:48 +0200 Subject: [PATCH 29/37] changelog: adds note for #11128 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48b5ab29aa6..3f5b76486c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ * **AuthProxy**: Support IPv6 in Auth proxy white list [#11330](https://github.com/grafana/grafana/pull/11330), thx [@corny](https://github.com/corny) * **SMTP**: Don't connect to STMP server using TLS unless configured. [#7189](https://github.com/grafana/grafana/issues/7189) * **Prometheus**: Escape backslash in labels correctly. [#10555](https://github.com/grafana/grafana/issues/10555), thx [@roidelapluie](https://github.com/roidelapluie) +* **Variables** Case-insensitive sorting for template values [#11128](https://github.com/grafana/grafana/issues/11128) thx [@cross](https://github.com/cross) # 5.0.4 (2018-03-28) From 9841c81952de6eb13e433ffb6d820dc43ea55b96 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 4 Apr 2018 13:40:08 +0200 Subject: [PATCH 30/37] Notes for closing #7119 [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f5b76486c4..4d221480de2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * **Prometheus**: Show template variable candidate in query editor [#9210](https://github.com/grafana/grafana/issues/9210), thx [@mtanda](https://github.com/mtanda) * **Prometheus**: Support POST for query and query_range [#9859](https://github.com/grafana/grafana/pull/9859), thx [@mtanda](https://github.com/mtanda) * **Alerting**: Add support for retries on alert queries [#5855](https://github.com/grafana/grafana/issues/5855), thx [@Thib17](https://github.com/Thib17) +* **Table**: Table plugin value mappings [#7119](https://github.com/grafana/grafana/issues/7119), thx [infernix](https://github.com/infernix) ### Minor * **OpsGenie**: Add triggered alerts as description [#11046](https://github.com/grafana/grafana/pull/11046), thx [@llamashoes](https://github.com/llamashoes) From 13f6d3be87a6c49a6b1f691324e5aca23aa94158 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Wed, 4 Apr 2018 14:16:39 +0200 Subject: [PATCH 31/37] migrated last all.js to ts --- public/app/core/services/all.js | 13 ------------- public/app/core/services/all.ts | 10 ++++++++++ public/app/features/all.js | 15 --------------- public/app/features/all.ts | 13 +++++++++++++ public/app/features/panel/all.js | 9 --------- public/app/features/panel/all.ts | 7 +++++++ public/app/features/playlist/all.js | 7 ------- public/app/features/playlist/all.ts | 5 +++++ 8 files changed, 35 insertions(+), 44 deletions(-) delete mode 100644 public/app/core/services/all.js create mode 100644 public/app/core/services/all.ts delete mode 100644 public/app/features/all.js create mode 100644 public/app/features/all.ts delete mode 100644 public/app/features/panel/all.js create mode 100644 public/app/features/panel/all.ts delete mode 100644 public/app/features/playlist/all.js create mode 100644 public/app/features/playlist/all.ts diff --git a/public/app/core/services/all.js b/public/app/core/services/all.js deleted file mode 100644 index 0a973440c5e..00000000000 --- a/public/app/core/services/all.js +++ /dev/null @@ -1,13 +0,0 @@ -define([ - './alert_srv', - './util_srv', - './context_srv', - './timer', - './analytics', - './popover_srv', - './segment_srv', - './backend_srv', - './dynamic_directive_srv', - './bridge_srv' -], -function () {}); diff --git a/public/app/core/services/all.ts b/public/app/core/services/all.ts new file mode 100644 index 00000000000..989015d2872 --- /dev/null +++ b/public/app/core/services/all.ts @@ -0,0 +1,10 @@ +import './alert_srv'; +import './util_srv'; +import './context_srv'; +import './timer'; +import './analytics'; +import './popover_srv'; +import './segment_srv'; +import './backend_srv'; +import './dynamic_directive_srv'; +import './bridge_srv'; diff --git a/public/app/features/all.js b/public/app/features/all.js deleted file mode 100644 index 759be6c11d2..00000000000 --- a/public/app/features/all.js +++ /dev/null @@ -1,15 +0,0 @@ -define([ - './panellinks/module', - './dashlinks/module', - './annotations/all', - './templating/all', - './plugins/all', - './dashboard/all', - './playlist/all', - './snapshot/all', - './panel/all', - './org/all', - './admin/admin', - './alerting/all', - './styleguide/styleguide', -], function () {}); diff --git a/public/app/features/all.ts b/public/app/features/all.ts new file mode 100644 index 00000000000..df987a8b59b --- /dev/null +++ b/public/app/features/all.ts @@ -0,0 +1,13 @@ +import './panellinks/module'; +import './dashlinks/module'; +import './annotations/all'; +import './templating/all'; +import './plugins/all'; +import './dashboard/all'; +import './playlist/all'; +import './snapshot/all'; +import './panel/all'; +import './org/all'; +import './admin/admin'; +import './alerting/all'; +import './styleguide/styleguide'; diff --git a/public/app/features/panel/all.js b/public/app/features/panel/all.js deleted file mode 100644 index aaa6d0d4ed0..00000000000 --- a/public/app/features/panel/all.js +++ /dev/null @@ -1,9 +0,0 @@ -define([ - './panel_header', - './panel_directive', - './solo_panel_ctrl', - './query_ctrl', - './panel_editor_tab', - './query_editor_row', - './query_troubleshooter', -], function () {}); diff --git a/public/app/features/panel/all.ts b/public/app/features/panel/all.ts new file mode 100644 index 00000000000..bdf1a097352 --- /dev/null +++ b/public/app/features/panel/all.ts @@ -0,0 +1,7 @@ +import './panel_header'; +import './panel_directive'; +import './solo_panel_ctrl'; +import './query_ctrl'; +import './panel_editor_tab'; +import './query_editor_row'; +import './query_troubleshooter'; diff --git a/public/app/features/playlist/all.js b/public/app/features/playlist/all.js deleted file mode 100644 index 3b07b0d74c5..00000000000 --- a/public/app/features/playlist/all.js +++ /dev/null @@ -1,7 +0,0 @@ -define([ - './playlists_ctrl', - './playlist_search', - './playlist_srv', - './playlist_edit_ctrl', - './playlist_routes' -], function () {}); diff --git a/public/app/features/playlist/all.ts b/public/app/features/playlist/all.ts new file mode 100644 index 00000000000..eb427b883ca --- /dev/null +++ b/public/app/features/playlist/all.ts @@ -0,0 +1,5 @@ +import './playlists_ctrl'; +import './playlist_search'; +import './playlist_srv'; +import './playlist_edit_ctrl'; +import './playlist_routes'; From 84dce3282aaac2a5d2b83de9af027619a880936c Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Wed, 4 Apr 2018 15:26:23 +0200 Subject: [PATCH 32/37] migrated playlist-routes to ts --- .../app/features/playlist/playlist_routes.js | 39 ------------------- .../app/features/playlist/playlist_routes.ts | 33 ++++++++++++++++ 2 files changed, 33 insertions(+), 39 deletions(-) delete mode 100644 public/app/features/playlist/playlist_routes.js create mode 100644 public/app/features/playlist/playlist_routes.ts diff --git a/public/app/features/playlist/playlist_routes.js b/public/app/features/playlist/playlist_routes.js deleted file mode 100644 index 193b0b52b20..00000000000 --- a/public/app/features/playlist/playlist_routes.js +++ /dev/null @@ -1,39 +0,0 @@ -define([ - 'angular', - 'lodash' -], -function (angular) { - 'use strict'; - - var module = angular.module('grafana.routes'); - - module.config(function($routeProvider) { - $routeProvider - .when('/playlists', { - templateUrl: 'public/app/features/playlist/partials/playlists.html', - controllerAs: 'ctrl', - controller : 'PlaylistsCtrl' - }) - .when('/playlists/create', { - templateUrl: 'public/app/features/playlist/partials/playlist.html', - controllerAs: 'ctrl', - controller : 'PlaylistEditCtrl' - }) - .when('/playlists/edit/:id', { - templateUrl: 'public/app/features/playlist/partials/playlist.html', - controllerAs: 'ctrl', - controller : 'PlaylistEditCtrl' - }) - .when('/playlists/play/:id', { - templateUrl: 'public/app/features/playlist/partials/playlists.html', - controllerAs: 'ctrl', - controller : 'PlaylistsCtrl', - resolve: { - init: function(playlistSrv, $route) { - var playlistId = $route.current.params.id; - playlistSrv.start(playlistId); - } - } - }); - }); -}); diff --git a/public/app/features/playlist/playlist_routes.ts b/public/app/features/playlist/playlist_routes.ts new file mode 100644 index 00000000000..c94446c2c1b --- /dev/null +++ b/public/app/features/playlist/playlist_routes.ts @@ -0,0 +1,33 @@ +import angular from 'angular'; + +function grafanaRoutes($routeProvider) { + $routeProvider + .when('/playlists', { + templateUrl: 'public/app/features/playlist/partials/playlists.html', + controllerAs: 'ctrl', + controller: 'PlaylistsCtrl', + }) + .when('/playlists/create', { + templateUrl: 'public/app/features/playlist/partials/playlist.html', + controllerAs: 'ctrl', + controller: 'PlaylistEditCtrl', + }) + .when('/playlists/edit/:id', { + templateUrl: 'public/app/features/playlist/partials/playlist.html', + controllerAs: 'ctrl', + controller: 'PlaylistEditCtrl', + }) + .when('/playlists/play/:id', { + templateUrl: 'public/app/features/playlist/partials/playlists.html', + controllerAs: 'ctrl', + controller: 'PlaylistsCtrl', + resolve: { + init: function(playlistSrv, $route) { + let playlistId = $route.current.params.id; + playlistSrv.start(playlistId); + }, + }, + }); +} + +angular.module('grafana.routes').config(grafanaRoutes); From 0273365df3dd25656330cd638b56617cd221d060 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Wed, 4 Apr 2018 16:20:01 +0200 Subject: [PATCH 33/37] created closeDropdown function, renamed appevent, added second appevent for open timepicker --- public/app/core/services/keybindingSrv.ts | 9 ++------- .../features/dashboard/timepicker/timepicker.ts | 16 +++++++++------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 35cd7808d12..55d968fd981 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -23,13 +23,8 @@ export class KeybindingSrv { this.setupGlobal(); appEvents.on('show-modal', () => (this.modalOpen = true)); - $rootScope.onAppEvent('escTimepicker', () => { - if (!this.timepickerOpen) { - this.timepickerOpen = true; - } else { - this.timepickerOpen = false; - } - }); + $rootScope.onAppEvent('timepickerOpen', () => (this.timepickerOpen = true)); + $rootScope.onAppEvent('timepickerClosed', () => (this.timepickerOpen = false)); } setupGlobal() { diff --git a/public/app/features/dashboard/timepicker/timepicker.ts b/public/app/features/dashboard/timepicker/timepicker.ts index 32ce07e4468..33cfff92e7f 100644 --- a/public/app/features/dashboard/timepicker/timepicker.ts +++ b/public/app/features/dashboard/timepicker/timepicker.ts @@ -22,7 +22,6 @@ export class TimePickerCtrl { refresh: any; isUtc: boolean; firstDayOfWeek: number; - closeDropdown: any; isOpen: boolean; /** @ngInject */ @@ -96,9 +95,8 @@ export class TimePickerCtrl { } openDropdown() { - this.$rootScope.appEvent('escTimepicker'); if (this.isOpen) { - this.isOpen = false; + this.closeDropdown(); return; } @@ -114,16 +112,21 @@ export class TimePickerCtrl { this.refresh.options.unshift({ text: 'off' }); this.isOpen = true; + this.$rootScope.appEvent('timepickerOpen'); + } + + closeDropdown() { + this.isOpen = false; + this.$rootScope.appEvent('timepickerClosed'); } applyCustom() { - this.$rootScope.appEvent('escTimepicker'); if (this.refresh.value !== this.dashboard.refresh) { this.timeSrv.setAutoRefresh(this.refresh.value); } this.timeSrv.setTime(this.editTimeRaw); - this.isOpen = false; + this.closeDropdown(); } absoluteFromChanged() { @@ -139,7 +142,6 @@ export class TimePickerCtrl { } setRelativeFilter(timespan) { - this.$rootScope.appEvent('escTimepicker'); var range = { from: timespan.from, to: timespan.to }; if (this.panel.nowDelay && range.to === 'now') { @@ -147,7 +149,7 @@ export class TimePickerCtrl { } this.timeSrv.setTime(range); - this.isOpen = false; + this.closeDropdown(); } } From 90aab445586b4a5ca00fba4d2f6de72d223d1235 Mon Sep 17 00:00:00 2001 From: Jarno Tuovinen Date: Wed, 4 Apr 2018 21:07:22 +0300 Subject: [PATCH 34/37] Use curly brackets around hyperlink help text #11478 (#11479) --- public/app/plugins/panel/table/column_options.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/panel/table/column_options.html b/public/app/plugins/panel/table/column_options.html index 9e8e4b404ae..4a4a6d0db9c 100644 --- a/public/app/plugins/panel/table/column_options.html +++ b/public/app/plugins/panel/table/column_options.html @@ -163,10 +163,10 @@ Use special variables to specify cell values:
- $__cell refers to current cell value + ${__cell} refers to current cell value
- $__cell_n refers to Nth column value in current row. Column indexes are started from 0. For instance, - $__cell_1 refers to second column's value. + ${__cell_n} refers to Nth column value in current row. Column indexes are started from 0. For instance, + ${__cell_1} refers to second column's value.
From b321a21cb569d1ac3c0dc88c53393aea2e48d2d5 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 5 Apr 2018 11:00:15 +0200 Subject: [PATCH 35/37] removed indent for manage dashboards --- public/sass/components/_search.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index e6b3795d752..ab63f96be50 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -206,7 +206,7 @@ padding: 5px; flex: 0 0 auto; font-size: 19px; - padding: 5px 2px 5px 16px; + padding: 5px 2px 5px 10px; } .search-item__tags { From 7083e8a0a921cb8ab2013f733351cbc796010b71 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Thu, 5 Apr 2018 14:09:32 +0200 Subject: [PATCH 36/37] migrated segment_srv to ts --- public/app/core/services/segment_srv.js | 111 ------------------------ public/app/core/services/segment_srv.ts | 111 ++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 111 deletions(-) delete mode 100644 public/app/core/services/segment_srv.js create mode 100644 public/app/core/services/segment_srv.ts diff --git a/public/app/core/services/segment_srv.js b/public/app/core/services/segment_srv.js deleted file mode 100644 index 71d0cbfe7a9..00000000000 --- a/public/app/core/services/segment_srv.js +++ /dev/null @@ -1,111 +0,0 @@ -define([ - 'angular', - 'lodash', - '../core_module', -], -function (angular, _, coreModule) { - 'use strict'; - - coreModule.default.service('uiSegmentSrv', function($sce, templateSrv) { - var self = this; - - function MetricSegment(options) { - if (options === '*' || options.value === '*') { - this.value = '*'; - this.html = $sce.trustAsHtml(''); - this.type = options.type; - this.expandable = true; - return; - } - - if (_.isString(options)) { - this.value = options; - this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); - return; - } - - // temp hack to work around legacy inconsistency in segment model - this.text = options.value; - - this.cssClass = options.cssClass; - this.custom = options.custom; - this.type = options.type; - this.fake = options.fake; - this.value = options.value; - this.selectMode = options.selectMode; - this.type = options.type; - this.expandable = options.expandable; - this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); - } - - this.getSegmentForValue = function(value, fallbackText) { - if (value) { - return this.newSegment(value); - } else { - return this.newSegment({value: fallbackText, fake: true}); - } - }; - - this.newSelectMeasurement = function() { - return new MetricSegment({value: 'select measurement', fake: true}); - }; - - this.newFake = function(text, type, cssClass) { - return new MetricSegment({value: text, fake: true, type: type, cssClass: cssClass}); - }; - - this.newSegment = function(options) { - return new MetricSegment(options); - }; - - this.newKey = function(key) { - return new MetricSegment({value: key, type: 'key', cssClass: 'query-segment-key' }); - }; - - this.newKeyValue = function(value) { - return new MetricSegment({value: value, type: 'value', cssClass: 'query-segment-value' }); - }; - - this.newCondition = function(condition) { - return new MetricSegment({value: condition, type: 'condition', cssClass: 'query-keyword' }); - }; - - this.newOperator = function(op) { - return new MetricSegment({value: op, type: 'operator', cssClass: 'query-segment-operator' }); - }; - - this.newOperators = function(ops) { - return _.map(ops, function(op) { - return new MetricSegment({value: op, type: 'operator', cssClass: 'query-segment-operator' }); - }); - }; - - this.transformToSegments = function(addTemplateVars, variableTypeFilter) { - return function(results) { - var segments = _.map(results, function(segment) { - return self.newSegment({value: segment.text, expandable: segment.expandable}); - }); - - if (addTemplateVars) { - _.each(templateSrv.variables, function(variable) { - if (variableTypeFilter === void 0 || variableTypeFilter === variable.type) { - segments.unshift(self.newSegment({ type: 'value', value: '$' + variable.name, expandable: true })); - } - }); - } - - return segments; - }; - }; - - this.newSelectMetric = function() { - return new MetricSegment({value: 'select metric', fake: true}); - }; - - this.newPlusButton = function() { - return new MetricSegment({fake: true, html: '', type: 'plus-button', cssClass: 'query-part' }); - }; - - }); - -}); diff --git a/public/app/core/services/segment_srv.ts b/public/app/core/services/segment_srv.ts new file mode 100644 index 00000000000..042340e6102 --- /dev/null +++ b/public/app/core/services/segment_srv.ts @@ -0,0 +1,111 @@ +import _ from 'lodash'; +import coreModule from '../core_module'; + +/** @ngInject */ +export function uiSegmentSrv($sce, templateSrv) { + let self = this; + + function MetricSegment(options) { + if (options === '*' || options.value === '*') { + this.value = '*'; + this.html = $sce.trustAsHtml(''); + this.type = options.type; + this.expandable = true; + return; + } + + if (_.isString(options)) { + this.value = options; + this.html = $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); + return; + } + + // temp hack to work around legacy inconsistency in segment model + this.text = options.value; + + this.cssClass = options.cssClass; + this.custom = options.custom; + this.type = options.type; + this.fake = options.fake; + this.value = options.value; + this.selectMode = options.selectMode; + this.type = options.type; + this.expandable = options.expandable; + this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); + } + + this.getSegmentForValue = function(value, fallbackText) { + if (value) { + return this.newSegment(value); + } else { + return this.newSegment({ value: fallbackText, fake: true }); + } + }; + + this.newSelectMeasurement = function() { + return new MetricSegment({ value: 'select measurement', fake: true }); + }; + + this.newFake = function(text, type, cssClass) { + return new MetricSegment({ value: text, fake: true, type: type, cssClass: cssClass }); + }; + + this.newSegment = function(options) { + return new MetricSegment(options); + }; + + this.newKey = function(key) { + return new MetricSegment({ value: key, type: 'key', cssClass: 'query-segment-key' }); + }; + + this.newKeyValue = function(value) { + return new MetricSegment({ value: value, type: 'value', cssClass: 'query-segment-value' }); + }; + + this.newCondition = function(condition) { + return new MetricSegment({ value: condition, type: 'condition', cssClass: 'query-keyword' }); + }; + + this.newOperator = function(op) { + return new MetricSegment({ value: op, type: 'operator', cssClass: 'query-segment-operator' }); + }; + + this.newOperators = function(ops) { + return _.map(ops, function(op) { + return new MetricSegment({ value: op, type: 'operator', cssClass: 'query-segment-operator' }); + }); + }; + + this.transformToSegments = function(addTemplateVars, variableTypeFilter) { + return function(results) { + let segments = _.map(results, function(segment) { + return self.newSegment({ value: segment.text, expandable: segment.expandable }); + }); + + if (addTemplateVars) { + _.each(templateSrv.variables, function(variable) { + if (variableTypeFilter === void 0 || variableTypeFilter === variable.type) { + segments.unshift(self.newSegment({ type: 'value', value: '$' + variable.name, expandable: true })); + } + }); + } + + return segments; + }; + }; + + this.newSelectMetric = function() { + return new MetricSegment({ value: 'select metric', fake: true }); + }; + + this.newPlusButton = function() { + return new MetricSegment({ + fake: true, + html: '', + type: 'plus-button', + cssClass: 'query-part', + }); + }; +} + +coreModule.service('uiSegmentSrv', uiSegmentSrv); From fcfc33ee5795c5cd745248450212086394c7999e Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 5 Apr 2018 15:02:54 +0200 Subject: [PATCH 37/37] changelog: adds note for #11165 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d221480de2..2f763a15c82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * **Prometheus**: Support POST for query and query_range [#9859](https://github.com/grafana/grafana/pull/9859), thx [@mtanda](https://github.com/mtanda) * **Alerting**: Add support for retries on alert queries [#5855](https://github.com/grafana/grafana/issues/5855), thx [@Thib17](https://github.com/Thib17) * **Table**: Table plugin value mappings [#7119](https://github.com/grafana/grafana/issues/7119), thx [infernix](https://github.com/infernix) +* **IE11**: IE 11 compatibility [#11165](https://github.com/grafana/grafana/issues/11165) ### Minor * **OpsGenie**: Add triggered alerts as description [#11046](https://github.com/grafana/grafana/pull/11046), thx [@llamashoes](https://github.com/llamashoes)