Commit d0df3643 by Torkel Ödegaard

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
parents a051f30e 4ce88ee7
......@@ -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;
......
......@@ -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'],
......
......@@ -14,6 +14,7 @@ function (_) {
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
......
......@@ -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
......
......@@ -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);
}
......
......@@ -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
......
......@@ -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>
......
......@@ -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
......
......@@ -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>
......
......@@ -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>
......
<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>
......
......@@ -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>
......
......@@ -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(),
......
......@@ -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;
......
/** @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.
......
......@@ -30,7 +30,6 @@ module.exports = function(grunt) {
]
}
});
grunt.task.run('string-replace:config');
});
grunt.task.run('git-describe');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment