Просмотр исходного кода

Merge remote-tracking branch 'grafana/master' into annotations-created

* grafana/master:
  docs: updated debian distro in install docs to stretch, closes #11527
  css: quick fix after IE11 changes
  fixed sidemenu icon issue created by earlier pr
  changelog: adds note for #11165
  migrated dash_class to ts
  migrated segment_srv to ts
  removed indent for manage dashboards
  created closeDropdown function, renamed appevent, added second appevent for open timepicker
  added if to onAppevent, renamed appevent, add appevent to applyCustom and setRelativeFilter
  docker: add users and groups to ldap block
  timepicker now closes without exiting edit/view mode, close order: modal, timepicker, view
  bounnd the esc key to exit timepicker
  added indent to dashboards inside folder in search dropdown, and added indent to dashboard icon in search item
ryan 7 лет назад
Родитель
Сommit
39f37ff8aa

+ 1 - 0
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)

+ 1 - 0
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
 

+ 7 - 3
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_'`
 

+ 13 - 0
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`

+ 10 - 0
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

+ 5 - 0
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

+ 10 - 0
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

+ 5 - 0
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

+ 9 - 0
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

+ 2 - 2
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

+ 1 - 1
public/app/core/components/search/search_results.html

@@ -20,7 +20,7 @@
   <div class="search-section__header" ng-show="section.hideHeader"></div>
 
   <div ng-if="section.expanded">
-    <a ng-repeat="item in section.items" class="search-item" ng-class="{'selected': item.selected}" ng-href="{{::item.url}}" >
+    <a ng-repeat="item in section.items" class="search-item search-item--indent" ng-class="{'selected': item.selected}" ng-href="{{::item.url}}" >
       <div ng-click="ctrl.toggleSelection(item, $event)">
         <gf-form-switch
            ng-show="ctrl.editable"

+ 0 - 36
public/app/core/directives/dash_class.js

@@ -1,36 +0,0 @@
-define([
-  'lodash',
-  'jquery',
-  '../core_module',
-],
-function (_, $, coreModule) {
-  'use strict';
-
-  coreModule.default.directive('dashClass', function() {
-    return {
-      link: function($scope, elem) {
-
-        $scope.onAppEvent('panel-fullscreen-enter', function() {
-          elem.toggleClass('panel-in-fullscreen', true);
-        });
-
-        $scope.onAppEvent('panel-fullscreen-exit', function() {
-          elem.toggleClass('panel-in-fullscreen', false);
-        });
-
-        $scope.$watch('ctrl.dashboardViewState.state.editview', function(newValue) {
-          if (newValue) {
-            elem.toggleClass('dashboard-page--settings-opening', _.isString(newValue));
-            setTimeout(function() {
-              elem.toggleClass('dashboard-page--settings-open', _.isString(newValue));
-            }, 10);
-          } else {
-            elem.removeClass('dashboard-page--settings-opening');
-            elem.removeClass('dashboard-page--settings-open');
-          }
-        });
-      }
-    };
-  });
-
-});

+ 31 - 0
public/app/core/directives/dash_class.ts

@@ -0,0 +1,31 @@
+import _ from 'lodash';
+import coreModule from '../core_module';
+
+/** @ngInject */
+export function dashClass() {
+  return {
+    link: function($scope, elem) {
+      $scope.onAppEvent('panel-fullscreen-enter', function() {
+        elem.toggleClass('panel-in-fullscreen', true);
+      });
+
+      $scope.onAppEvent('panel-fullscreen-exit', function() {
+        elem.toggleClass('panel-in-fullscreen', false);
+      });
+
+      $scope.$watch('ctrl.dashboardViewState.state.editview', function(newValue) {
+        if (newValue) {
+          elem.toggleClass('dashboard-page--settings-opening', _.isString(newValue));
+          setTimeout(function() {
+            elem.toggleClass('dashboard-page--settings-open', _.isString(newValue));
+          }, 10);
+        } else {
+          elem.removeClass('dashboard-page--settings-opening');
+          elem.removeClass('dashboard-page--settings-open');
+        }
+      });
+    },
+  };
+}
+
+coreModule.directive('dashClass', dashClass);

+ 9 - 1
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,8 @@ export class KeybindingSrv {
 
     this.setupGlobal();
     appEvents.on('show-modal', () => (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;
     }

+ 0 - 111
public/app/core/services/segment_srv.js

@@ -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('<i class="fa fa-asterisk"><i>');
-        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: '<i class="fa fa-plus "></i>', type: 'plus-button', cssClass: 'query-part' });
-    };
-
-  });
-
-});

+ 111 - 0
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('<i class="fa fa-asterisk"><i>');
+      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: '<i class="fa fa-plus "></i>',
+      type: 'plus-button',
+      cssClass: 'query-part',
+    });
+  };
+}
+
+coreModule.service('uiSegmentSrv', uiSegmentSrv);

+ 10 - 4
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();
   }
 }
 

+ 2 - 4
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;

+ 4 - 0
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 {

+ 2 - 0
public/sass/components/_sidemenu.scss

@@ -123,6 +123,8 @@
     position: relative;
     opacity: 0.7;
     font-size: 130%;
+    height: 22px;
+    width: 22px;
   }
 
   .fa {

+ 1 - 1
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;