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) 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 diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index 27c0e41ac15..8b51a75a826 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -34,7 +34,7 @@ sudo dpkg -i grafana_5.0.4_amd64.deb Add the following line to your `/etc/apt/sources.list` file. ```bash -deb https://packagecloud.io/grafana/stable/debian/ jessie main +deb https://packagecloud.io/grafana/stable/debian/ stretch main ``` Use the above line even if you are on Ubuntu or another Debian version. @@ -42,7 +42,7 @@ There is also a testing repository if you want beta or release candidates. ```bash -deb https://packagecloud.io/grafana/testing/debian/ jessie main +deb https://packagecloud.io/grafana/testing/debian/ stretch main ``` Then add the [Package Cloud](https://packagecloud.io/grafana) key. This 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 @@
- +
(this.modalOpen = true)); + $rootScope.onAppEvent('timepickerOpen', () => (this.timepickerOpen = true)); + $rootScope.onAppEvent('timepickerClosed', () => (this.timepickerOpen = false)); } setupGlobal() { @@ -73,7 +76,12 @@ export class KeybindingSrv { appEvents.emit('hide-modal'); 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; } 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); diff --git a/public/app/features/dashboard/timepicker/timepicker.ts b/public/app/features/dashboard/timepicker/timepicker.ts index 2434e691515..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 */ @@ -32,6 +31,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; @@ -96,7 +96,7 @@ export class TimePickerCtrl { openDropdown() { if (this.isOpen) { - this.isOpen = false; + this.closeDropdown(); return; } @@ -112,6 +112,12 @@ 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() { @@ -120,7 +126,7 @@ export class TimePickerCtrl { } this.timeSrv.setTime(this.editTimeRaw); - this.isOpen = false; + this.closeDropdown(); } absoluteFromChanged() { @@ -143,7 +149,7 @@ export class TimePickerCtrl { } this.timeSrv.setTime(range); - this.isOpen = false; + this.closeDropdown(); } } diff --git a/public/sass/base/_icons.scss b/public/sass/base/_icons.scss index 31e5ee62d6f..c701cc1249e 100644 --- a/public/sass/base/_icons.scss +++ b/public/sass/base/_icons.scss @@ -1,10 +1,8 @@ .gicon { line-height: 1; display: inline-block; - //width: 1.1057142857em; - //height: 1.1057142857em; - height: 22px; - width: 22px; + width: 1.1057142857em; + height: 1.1057142857em; text-align: center; background-repeat: no-repeat; background-position: center; diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index e69068b730a..99033b90ff1 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -60,6 +60,10 @@ display: flex; flex-direction: column; flex-grow: 1; + + .search-item--indent { + margin-left: 14px; + } } .search-dropdown__col_2 { diff --git a/public/sass/components/_sidemenu.scss b/public/sass/components/_sidemenu.scss index e48ab0597a2..d1372484074 100644 --- a/public/sass/components/_sidemenu.scss +++ b/public/sass/components/_sidemenu.scss @@ -123,6 +123,8 @@ position: relative; opacity: 0.7; font-size: 130%; + height: 22px; + width: 22px; } .fa { diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index c957b6af790..871db4dfc2d 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;