Kaynağa Gözat

Merge remote-tracking branch 'upstream/master'

Conflicts:
	docs/kibana/configuration/config.js.asciidoc
	src/app/components/settings.js
	src/app/panels/dashcontrol/module.html
	src/app/partials/dasheditor.html
	src/config.js
Torkel Ödegaard 12 yıl önce
ebeveyn
işleme
d0df36433d

+ 9 - 1
src/app/app.js

@@ -12,9 +12,10 @@ define([
   'angular-strap',
   'angular-dragdrop',
   'extend-jquery',
-  'bindonce'
+  'bindonce',
 ],
 function (angular, $, _, appLevelRequire) {
+
   "use strict";
 
   var app = angular.module('kibana', []),
@@ -25,6 +26,11 @@ function (angular, $, _, appLevelRequire) {
     // features if we define them after boot time
     register_fns = {};
 
+  app.constant('version',"3.0.0pre5");
+
+  // Use this for cache busting partials
+  app.constant('cacheBust',"cache-bust="+Date.now());
+
   /**
    * Tells the application to watch the module, once bootstraping has completed
    * the modules controller, service, etc. functions will be overwritten to register directly
@@ -59,6 +65,7 @@ function (angular, $, _, appLevelRequire) {
   };
 
   app.config(function ($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
+
     $routeProvider
       .when('/dashboard', {
         templateUrl: 'app/partials/dashboard.html',
@@ -72,6 +79,7 @@ function (angular, $, _, appLevelRequire) {
       .otherwise({
         redirectTo: 'dashboard'
       });
+
     // this is how the internet told me to dynamically add modules :/
     register_fns.controller = $controllerProvider.register;
     register_fns.directive  = $compileProvider.directive;

+ 2 - 1
src/app/components/require.config.js

@@ -58,7 +58,7 @@ require.config({
     },
 
     angular: {
-      deps: ['jquery'],
+      deps: ['jquery','config'],
       exports: 'angular'
     },
 
@@ -75,6 +75,7 @@ require.config({
     },
 
     // simple dependency declaration
+    //
     'jquery-ui':            ['jquery'],
     'jquery.flot':          ['jquery'],
     'jquery.flot.byte':     ['jquery', 'jquery.flot'],

+ 5 - 4
src/app/components/settings.js

@@ -10,10 +10,11 @@ function (_) {
      * @type {Object}
      */
     var defaults = {
-      elasticsearch : "http://"+window.location.hostname+":9200",
-      panel_names   : [],
-      kibana_index  : 'kibana-int',
-      graphiteUrl   : null,
+      elasticsearch     : "http://"+window.location.hostname+":9200",
+      panel_names       : [],
+      kibana_index  	: 'kibana-int',
+      graphiteUrl   	: null,
+      default_route     : '/dashboard/file/default.json'
     };
 
     // This initializes a new hash on purpose, to avoid adding parameters to

+ 1 - 1
src/app/controllers/dash.js

@@ -31,7 +31,7 @@ function (angular, config, _) {
   module.controller('DashCtrl', function(
     $scope, $rootScope, $route, ejsResource, fields, dashboard, alertSrv, panelMove, esVersion) {
 
-    $scope.requiredElasticSearchVersion = ">=0.20.5";
+    $scope.requiredElasticSearchVersion = ">=0.90.3";
 
     $scope.editor = {
       index: 0

+ 5 - 4
src/app/controllers/dashLoader.js

@@ -7,7 +7,7 @@ function (angular, _) {
 
   var module = angular.module('kibana.controllers');
 
-  module.controller('dashLoader', function($scope, $http, timer, dashboard, alertSrv) {
+  module.controller('dashLoader', function($scope, $http, timer, dashboard, alertSrv, $location) {
     $scope.loader = dashboard.current.loader;
 
     $scope.init = function() {
@@ -35,8 +35,8 @@ function (angular, _) {
     };
 
     $scope.set_default = function() {
-      if(dashboard.set_default()) {
-        alertSrv.set('Local Default Set',dashboard.current.title+' has been set as your local default','success',5000);
+      if(dashboard.set_default($location.path())) {
+        alertSrv.set('Home Set','This page has been set as your default Kibana dashboard','success',5000);
       } else {
         alertSrv.set('Incompatible Browser','Sorry, your browser is too old for this feature','error',5000);
       }
@@ -44,7 +44,8 @@ function (angular, _) {
 
     $scope.purge_default = function() {
       if(dashboard.purge_default()) {
-        alertSrv.set('Local Default Clear','Your local default dashboard has been cleared','success',5000);
+        alertSrv.set('Local Default Clear','Your Kibana default dashboard has been reset to the default',
+          'success',5000);
       } else {
         alertSrv.set('Incompatible Browser','Sorry, your browser is too old for this feature','error',5000);
       }

+ 2 - 1
src/app/directives/kibanaPanel.js

@@ -77,7 +77,8 @@ function (angular) {
             var nameAsPath = name.replace(".", "/");
             $scope.require([
               'jquery',
-              'text!panels/'+nameAsPath+'/module.html'
+              'text!panels/'+nameAsPath+'/module.html',
+              'text!panels/'+nameAsPath+'/editor.html'
             ], function ($, moduleTemplate) {
               var $module = $(moduleTemplate);
               // top level controllers

+ 4 - 4
src/app/panels/histogram/module.html

@@ -43,11 +43,11 @@
       <!--<a class='small' ng-click='zoom(0.5)'><i class='icon-zoom-in'></i> Zoom In</a>-->
       <a class='small' ng-click='zoom(2)'><i class='icon-zoom-out'></i> Zoom Out</a> |&nbsp
     </span>
-    <span ng-show="panel.legend" ng-repeat='series in data' class="histogram-legend">
-      <i class='icon-circle' ng-style="{color: series.info.color}"></i>
+    <span ng-show="panel.legend" ng-repeat='series in legend' class="histogram-legend">
+      <i class='icon-circle' ng-style="{color: series.color}"></i>
       <span class='small histogram-legend-item'>
-        <span ng-if="panel.show_query">{{series.info.alias || series.info.query}}</span>
-        <span ng-if="!panel.show_query">{{series.info.alias}}</span>
+        <span ng-if="panel.show_query">{{series.alias || series.query}}</span>
+        <span ng-if="!panel.show_query">{{series.alias}}</span>
         <span ng-show="panel.legend_counts"> ({{series.hits}})</span>
       </span>
     </span>

+ 30 - 24
src/app/panels/histogram/module.js

@@ -242,9 +242,6 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
     $scope.init = function() {
       // Hide view options by default
       $scope.options = false;
-      $scope.$on('refresh',function(){
-        $scope.get_data();
-      });
 
       // Always show the query if an alias isn't set. Users can set an alias if the query is too
       // long
@@ -303,7 +300,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
      * @param {number} query_id  The id of the query, generated on the first run and passed back when
      *                            this call is made recursively for more segments
      */
-    $scope.get_data = function(segment, query_id) {
+    $scope.get_data = function(data, segment, query_id) {
       var
         _range,
         _interval,
@@ -380,12 +377,12 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
       results = request.doSearch();
 
       // Populate scope when we have results
-      results.then(function(results) {
-
+      return results.then(function(results) {
         $scope.panelMeta.loading = false;
         if(segment === 0) {
+          $scope.legend = [];
           $scope.hits = 0;
-          $scope.data = [];
+          data = [];
           $scope.annotations = [];
           query_id = $scope.query_id = new Date().getTime();
         }
@@ -407,7 +404,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
             var query_results = results.facets[q.id];
             // we need to initialize the data variable on the first run,
             // and when we are working on the first segment of the data.
-            if(_.isUndefined($scope.data[i]) || segment === 0) {
+            if(_.isUndefined(data[i]) || segment === 0) {
               var tsOpts = {
                 interval: _interval,
                 start_date: _range && _range.from,
@@ -417,8 +414,8 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
               time_series = new timeSeries.ZeroFilled(tsOpts);
               hits = 0;
             } else {
-              time_series = $scope.data[i].time_series;
-              hits = $scope.data[i].hits;
+              time_series = data[i].time_series;
+              hits = data[i].hits;
             }
 
             // push each entry into the time series, while incrementing counters
@@ -427,7 +424,10 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
               hits += entry.count; // The series level hits counter
               $scope.hits += entry.count; // Entire dataset level hits counter
             });
-            $scope.data[i] = {
+
+            $scope.legend[i] = q;
+
+            data[i] = {
               info: q,
               time_series: time_series,
               hits: hits
@@ -462,11 +462,11 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
           }
 
           // Tell the histogram directive to render.
-          $scope.$emit('render');
+          $scope.$emit('render', data);
 
           // If we still have segments left, get them
           if(segment < dashboard.indices.length-1) {
-            $scope.get_data(segment+1,query_id);
+            $scope.get_data(data,segment+1,query_id);
           }
         }
       });
@@ -528,15 +528,21 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
       restrict: 'A',
       template: '<div></div>',
       link: function(scope, elem) {
+        var data;
+
+        scope.$on('refresh',function(){
+          scope.get_data();
+        });
 
         // Receive render events
-        scope.$on('render',function(){
-          render_panel();
+        scope.$on('render',function(event,d){
+          data = d || data;
+          render_panel(data);
         });
 
         // Re-render if the window is resized
         angular.element(window).bind('resize', function(){
-          render_panel();
+          render_panel(data);
         });
 
         var scale = function(series,factor) {
@@ -564,13 +570,13 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
         };
 
         // Function for rendering panel
-        function render_panel() {
+        function render_panel(data) {
           // IE doesn't work without this
           elem.css({height:scope.panel.height || scope.row.height});
 
           // Populate from the query service
           try {
-            _.each(scope.data, function(series) {
+            _.each(data, function(series) {
               series.label = series.info.alias;
               series.color = series.info.color;
             });
@@ -669,8 +675,8 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
             // when rendering stacked bars, we need to ensure each point that has data is zero-filled
             // so that the stacking happens in the proper order
             var required_times = [];
-            if (scope.data.length > 1) {
-              required_times = Array.prototype.concat.apply([], _.map(scope.data, function (query) {
+            if (data.length > 1) {
+              required_times = Array.prototype.concat.apply([], _.map(data, function (query) {
                 return query.time_series.getOrderedTimes();
               }));
               required_times = _.uniq(required_times.sort(function (a, b) {
@@ -680,8 +686,8 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
             }
 
 
-            for (var i = 0; i < scope.data.length; i++) {
-              var _d = scope.data[i].time_series.getFlotPairs(required_times);
+            for (var i = 0; i < data.length; i++) {
+              var _d = data[i].time_series.getFlotPairs(required_times);
               if(scope.panel.derivative) {
                 _d = derivative(_d);
               }
@@ -691,10 +697,10 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
               if(scope.panel.scaleSeconds) {
                 _d = scaleSeconds(_d,scope.panel.interval);
               }
-              scope.data[i].data = _d;
+              data[i].data = _d;
             }
 
-            scope.plot = $.plot(elem, scope.data, options);
+            scope.plot = $.plot(elem, data, options);
 
           } catch(e) {
             // Nothing to do here

+ 5 - 5
src/app/partials/dashLoader.html

@@ -14,15 +14,15 @@
 
 
   <ul class="dropdown-menu" style="padding:10px">
-    <li ng-show='dashboard.current.loader.load_elasticsearch'>
+    <li ng-if='dashboard.current.loader.load_elasticsearch'>
       <form class="nomargin">
         <input type="text" ng-model="elasticsearch.query" ng-change="elasticsearch_dblist('title:'+elasticsearch.query+'*')" placeholder="Type to filter"/>
       </form>
       <h6 ng-hide="elasticsearch.dashboards.length">No dashboards matching your query found</h6>
       <table class="table table-condensed table-striped">
-        <tr ng-repeat="row in elasticsearch.dashboards | orderBy:['_id']">
+        <tr bindonce ng-repeat="row in elasticsearch.dashboards | orderBy:['_id']">
           <td><a ng-click="elasticsearch_delete(row._id)"><i class="icon-remove"></i></a></td>
-          <td><a href="#/dashboard/elasticsearch/{{row._id}}">{{row._id}}</a></td>
+          <td><a href="#/dashboard/elasticsearch/{{row._id}}" bo-text="row._id"></a></td>
           <td><a><i class="icon-share" ng-click="share = dashboard.share_link(row._id,'elasticsearch',row._id)" bs-modal="'app/panels/dashcontrol/share.html'"></i></a></td>
         </tr>
       </table>
@@ -78,10 +78,10 @@
       <ul class="dropdown-menu">
 
         <li ng-show="dashboard.current.loader.save_default">
-          <a class="link" ng-click="set_default()">Set as my home</a>
+          <a class="link" ng-click="set_default()">Save as Home</a>
         </li>
         <li ng-show="dashboard.current.loader.save_default">
-          <a class="link" ng-click="purge_default()">Clear my home</a>
+          <a class="link" ng-click="purge_default()">Reset Home</a>
         </li>
         <li ng-show="dashboard.current.loader.save_local">
           <a class="link" ng-click="dashboard.to_file()">Export schema</a>

+ 1 - 1
src/app/partials/dasheditor.html

@@ -8,7 +8,7 @@
     </div>
   </div>
 
-  <div ng-show="editor.index == 0">
+  <div ng-if="editor.index == 0">
     <div class="row-fluid">
       <div class="span4">
         <label class="small">Title</label><input type="text" class="input-large" ng-model='dashboard.current.title'></input>

+ 2 - 2
src/app/partials/paneleditor.html

@@ -1,5 +1,5 @@
-<div class="modal-body">
-  <div class="pull-right editor-title">{{panel.type}} settings</div>
+<div bindonce class="modal-body">
+  <div class="pull-right editor-title" bo-text="panel.type+' settings'"></div>
   <div ng-model="editor.index" bs-tabs>
     <div ng-repeat="tab in setEditorTabs(panelMeta)" data-title="{{tab}}">
     </div>

+ 3 - 3
src/app/partials/roweditor.html

@@ -6,7 +6,7 @@
     </div>
   </div>
 
-  <div class="row-fluid" ng-show="editor.index == 0">
+  <div class="row-fluid" ng-if="editor.index == 0">
     <div class="span4">
       <label class="small">Title</label><input type="text" class="input-medium" ng-model='row.title'></input>
     </div>
@@ -20,7 +20,7 @@
       <label class="small"> Collapsable </label><input type="checkbox" ng-model="row.collapsable" ng-checked="row.collapsable" />
     </div>
   </div>
-  <div class="row-fluid" ng-show="editor.index == 1">
+  <div class="row-fluid" ng-if="editor.index == 1">
     <div class="span12">
       <h4>Panels <i class="icon-plus-sign link" bs-tooltip="'Add panel'" ng-click="editor.index = 2"></i></h4>
       <table class="table table-condensed table-striped">
@@ -45,7 +45,7 @@
       </table>
     </div>
   </div>
-  <div class="row-fluid" ng-show="editor.index == 2">
+  <div class="row-fluid" ng-if="editor.index == 2">
     <h4>Select Panel Type</h4>
     <form class="form-inline">
       <select class="input-medium" ng-model="panel.type" ng-options="panelType for panelType in dashboard.availablePanels|stringSort"></select>

+ 58 - 15
src/app/services/dashboard.js

@@ -101,22 +101,31 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
         case('script'):
           self.script_load(_id);
           break;
+        case('local'):
+          self.local_load();
+          break;
         default:
-          self.file_load('default.json');
+          $location.path(config.default_route);
         }
-
       // No dashboard in the URL
       } else {
-        // Check if browser supports localstorage, and if there's a dashboard
-        if (Modernizr.localstorage &&
-          !(_.isUndefined(window.localStorage['dashboard'])) &&
-          window.localStorage['dashboard'] !== ''
-        ) {
-          var dashboard = JSON.parse(window.localStorage['dashboard']);
-          self.dash_load(dashboard);
-        // No? Ok, grab default.json, its all we have now
+        // Check if browser supports localstorage, and if there's an old dashboard. If there is,
+        // inform the user that they should save their dashboard to Elasticsearch and then set that
+        // as their default
+        if (Modernizr.localstorage) {
+          if(!(_.isUndefined(window.localStorage['dashboard'])) && window.localStorage['dashboard'] !== '') {
+            console.log(window.localStorage['dashboard']);
+            $location.path(config.default_route);
+            alertSrv.set('Saving to browser storage has been replaced',' with saving to Elasticsearch.'+
+              ' Click <a href="#/dashboard/local/deprecated">here</a> to load your old dashboard anyway.');
+          } else if(!(_.isUndefined(window.localStorage.kibanaDashboardDefault))) {
+            $location.path(window.localStorage.kibanaDashboardDefault);
+          } else {
+            $location.path(config.default_route);
+          }
+        // No? Ok, grab the default route, its all we have now
         } else {
-          self.file_load('default.json');
+          $location.path(config.default_route);
         }
       }
     };
@@ -230,10 +239,14 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
       return true;
     };
 
-    this.set_default = function(dashboard) {
+    this.set_default = function(route) {
+      console.log(route);
       if (Modernizr.localstorage) {
-        window.localStorage['dashboard'] = angular.toJson(dashboard || self.current);
-        $location.path('/dashboard');
+        // Purge any old dashboards
+        if(!_.isUndefined(window.localStorage['dashboard'])) {
+          delete window.localStorage['dashboard'];
+        }
+        window.localStorage.kibanaDashboardDefault = route;
         return true;
       } else {
         return false;
@@ -242,7 +255,12 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
 
     this.purge_default = function() {
       if (Modernizr.localstorage) {
-        window.localStorage['dashboard'] = '';
+        // Purge any old dashboards
+        if(!_.isUndefined(window.localStorage['dashboard'])) {
+
+          delete window.localStorage['dashboard'];
+        }
+        delete window.localStorage.kibanaDashboardDefault;
         return true;
       } else {
         return false;
@@ -273,6 +291,31 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
       return _r;
     };
 
+    this.local_load = function() {
+      var dashboard = JSON.parse(window.localStorage['dashboard']);
+      dashboard.rows.unshift({
+        height: "30",
+        title: "Deprecation Notice",
+        panels: [
+          {
+            title: 'WARNING: Legacy dashboard',
+            type: 'text',
+            span: 12,
+            mode: 'html',
+            content: 'This dashboard has been loaded from the browsers local cache. If you use '+
+            'another brower or computer you will not be able to access it! '+
+            '\n\n  <h4>Good news!</h4> Kibana'+
+            ' now stores saved dashboards in Elasticsearch. Click the <i class="icon-save"></i> '+
+            'button in the top left to save this dashboard. Then select "Set as Home" from'+
+            ' the "advanced" sub menu to automatically use the stored dashboard as your Kibana '+
+            'landing page afterwards'+
+            '<br><br><strong>Tip:</strong> You may with to remove this row before saving!'
+          }
+        ]
+      });
+      self.dash_load(dashboard);
+    };
+
     this.file_load = function(file) {
       return $http({
         url: "app/dashboards/"+file.replace(/\.(?!json)/,"/")+'?' + new Date().getTime(),

+ 9 - 10
src/app/services/fields.js

@@ -13,23 +13,22 @@ function (angular, _, config) {
     var self = this;
 
     this.list = ['_type'];
-    this.mapping = {};
-    this.fullMapping = {};
+    this.indices = [];
 
+    // Stop tracking the full mapping, too expensive, instead we only remember the index names
+    // we've already seen.
+    //
     $rootScope.$watch(function(){return dashboard.indices;},function(n) {
       if(!_.isUndefined(n) && n.length && dashboard.current.index.warm_fields) {
         // Only get the mapping for indices we don't know it for
-        var indices = _.difference(n,_.keys(self.fullMapping));
+        var indices = _.difference(n,_.keys(self.indices));
         // Only get the mapping if there are new indices
         if(indices.length > 0) {
           self.map(indices).then(function(result) {
-            self.fullMapping = _.extend(self.fullMapping,result);
-            self.list = mapFields(self.fullMapping);
+            self.indices = _.union(self.indices,_.keys(result));
+            self.list = mapFields(result);
           });
         // Otherwise just use the cached mapping
-        } else {
-          // This is inefficient, should not need to reprocess?
-          self.list = mapFields(_.pick(self.fullMapping,n));
         }
       }
     });
@@ -37,8 +36,8 @@ function (angular, _, config) {
     var mapFields = function (m) {
       var fields = [];
       _.each(m, function(types) {
-        _.each(types, function(v) {
-          fields = _.without(_.union(fields,_.keys(v)),'_all','_source');
+        _.each(types, function(type) {
+          fields = _.without(_.union(fields,_.keys(type)),'_all','_source');
         });
       });
       return fields;

+ 16 - 5
src/config.js

@@ -1,5 +1,5 @@
 /** @scratch /configuration/config.js/1
- * == Configuration ==
+ * == Configuration
  * config.js is where you will find the core Kibana configuration. This file contains parameter that
  * must be set before kibana is run for the first time.
  */
@@ -8,12 +8,12 @@ function (Settings) {
   "use strict";
 
   /** @scratch /configuration/config.js/2
-   * === Parameters ===
+   * === Parameters
    */
   return new Settings({
 
     /** @scratch /configuration/config.js/5
-     * ==== elasticsearch ====
+     * ==== elasticsearch
      *
      * The URL to your elasticsearch server. You almost certainly don't
      * want +http://localhost:9200+ here. Even if Kibana and Elasticsearch are on
@@ -24,7 +24,18 @@ function (Settings) {
     elasticsearch: "http://se0-elasticstash-01:9200",
 
     /** @scratch /configuration/config.js/5
-     * ==== kibana-int ====
+     * ==== default_route
+     *
+     * This is the default landing page when you don't specify a dashboard to load. You can specify
+     * files, scripts or saved dashboards here. For example, if you had saved a dashboard called
+     * `WebLogs' to elasticsearch you might use:
+     *
+     * +default_route: '/dashboard/elasticsearch/WebLogs',+
+     */
+    default_route     : '/dashboard/file/default.json',
+
+    /** @scratch /configuration/config.js/5
+     * ==== kibana-int
      *
      * The default ES index to use for storing Kibana specific object
      * such as stored dashboards
@@ -32,7 +43,7 @@ function (Settings) {
     kibana_index: "kibana-int",
 
     /** @scratch /configuration/config.js/5
-     * ==== panel_name ====
+     * ==== panel_name
      *
      * An array of panel modules available. Panels will only be loaded when they are defined in the
      * dashboard, but this list is used in the "add panel" interface.

+ 0 - 1
tasks/build_task.js

@@ -30,7 +30,6 @@ module.exports = function(grunt) {
           ]
         }
       });
-
       grunt.task.run('string-replace:config');
     });
     grunt.task.run('git-describe');