Commit e54c868e by Rashid Khan

Added pulldowns for query and filter. Added filter notifications, moved…

Added pulldowns for query and filter. Added filter notifications, moved timepicker to navbar, reimplemented auto refresh
parent d5bc550b
......@@ -295,7 +295,6 @@ function($, _, moment) {
} else if (c === '-') {
type = 2;
} else {
console.log("no type");
return false;
}
......@@ -311,7 +310,6 @@ function($, _, moment) {
if (type === 0) {
// rounding is only allowed on whole numbers
if (num !== 1) {
console.log("nbad rounding");
return false;
}
}
......@@ -373,7 +371,6 @@ function($, _, moment) {
}
break;
default:
console.log("unknown unit");
return false;
}
}
......
define([
'./dash',
'./dashLoader',
'./row'
'./row',
'./pulldown'
], function () {});
\ No newline at end of file
......@@ -11,13 +11,20 @@ function (angular, _) {
$scope.loader = dashboard.current.loader;
$scope.init = function() {
$scope.advancedLoad = false;
$scope.advancedSave = false;
$scope.gist_pattern = /(^\d{5,}$)|(^[a-z0-9]{10,}$)|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;
$scope.gist = $scope.gist || {};
$scope.elasticsearch = $scope.elasticsearch || {};
};
$scope.showDropdown = function(type) {
var _l = $scope.loader;
if(_.isUndefined(dashboard.current.loader)) {
return true;
}
var _l = dashboard.current.loader;
if(type === 'load') {
return (_l.load_elasticsearch || _l.load_gist || _l.load_local);
}
......
define([
'angular',
'app',
'underscore'
],
function (angular, app, _) {
'use strict';
var module = angular.module('kibana.controllers');
module.controller('PulldownCtrl', function($scope, $rootScope, $timeout,ejsResource, querySrv) {
var _d = {
collapse: false,
notice: false,
};
_.defaults($scope.pulldown,_d);
$scope.init = function() {
$scope.querySrv = querySrv;
// Provide a combined skeleton for panels that must interact with panel and row.
// This might create name spacing issues.
$scope.panel = $scope.pulldown;
$scope.row = $scope.pulldown;
};
$scope.toggle_pulldown = function(pulldown) {
pulldown.collapse = pulldown.collapse ? false : true;
if (!pulldown.collapse) {
$timeout(function() {
$scope.$broadcast('render');
});
} else {
$scope.row.notice = false;
}
};
$scope.init();
}
);
});
\ No newline at end of file
......@@ -13,7 +13,9 @@
"query": "*",
"alias": "",
"color": "#7EB26D",
"id": 0
"id": 0,
"pin": false,
"type": "lucene"
}
},
"ids": [
......@@ -22,24 +24,12 @@
},
"filter": {
"idQueue": [
0,
1,
2
],
"list": {
"0": {
"from": "2013-07-27T22:08:06.800Z",
"to": "2013-07-27T23:08:06.801Z",
"field": "@timestamp",
"type": "time",
"mandate": "must",
"active": true,
"alias": "",
"id": 0
}
},
"ids": [
0
]
"list": {},
"ids": []
}
},
"rows": [
......@@ -77,7 +67,8 @@
"style": {},
"status": "Stable"
}
]
],
"notice": false
}
],
"editable": false,
......@@ -85,5 +76,73 @@
"interval": "none",
"pattern": "[logstash-]YYYY.MM.DD",
"default": "_all"
}
}
},
"style": "dark",
"failover": false,
"panel_hints": true,
"pulldowns": [
{
"type": "query",
"collapse": true,
"notice": false,
"query": "*",
"pinned": true,
"history": [],
"remember": 10
},
{
"type": "filtering",
"collapse": true,
"notice": false
}
],
"nav": [
{
"type": "timepicker2",
"collapse": false,
"notice": false,
"status": "Stable",
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
],
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"timefield": "@timestamp",
"now": true,
"filter_id": 0
}
],
"loader": {
"save_gist": false,
"save_elasticsearch": true,
"save_local": true,
"save_default": true,
"save_temp": true,
"save_temp_ttl_enable": true,
"save_temp_ttl": "30d",
"load_gist": true,
"load_elasticsearch": true,
"load_elasticsearch_size": 20,
"load_local": true,
"hide": false
},
"refresh": false
}
\ No newline at end of file
/* global _, kbn */
/* global _ */
/*
* Complex scripted Logstash dashboard
......@@ -29,7 +29,7 @@ var dashboard, queries, _d_timespan;
var ARGS;
// Set a default timespan if one isn't specified
_d_timespan = '1h';
_d_timespan = '1d';
// Intialize a skeleton with nothing but a rows array and service object
dashboard = {
......@@ -85,12 +85,11 @@ dashboard.services.query = {
};
// Lets also add a default time filter, the value of which can be specified by the user
// This isn't strictly needed, but it gets rid of the info alert about the missing time filter
dashboard.services.filter = {
list: {
0: {
from: kbn.time_ago(ARGS.from||_d_timespan),
to: new Date(),
from: "now-"+(ARGS.from||_d_timespan),
to: "now",
field: ARGS.timefield||"@timestamp",
type: "time",
active: true,
......@@ -103,19 +102,6 @@ dashboard.services.filter = {
// Ok, lets make some rows. The Filters row is collapsed by default
dashboard.rows = [
{
title: "Time span",
height: "30px"
},
{
title: "Query",
height: "30px"
},
{
title: "Filters",
height: "100px",
collapse: true
},
{
title: "Chart",
height: "300px"
},
......@@ -125,37 +111,8 @@ dashboard.rows = [
}
];
// Setup some panels. A query panel and a filter panel on the same row
dashboard.rows[0].panels = [
{
title: "Set time filter",
type: 'timepicker',
span: 6,
timespan: ARGS.from||_d_timespan
}
];
// Add a filtering panel to the 3rd row
dashboard.rows[1].panels = [
{
title: 'search',
type: 'query',
span: 12
}
];
// Add a filtering panel to the 3rd row
dashboard.rows[2].panels = [
{
title: 'filters (applied globally)',
type: 'filtering',
span: 12
}
];
// And a histogram that allows the user to specify the interval and time field
dashboard.rows[3].panels = [
dashboard.rows[0].panels = [
{
title: 'events over time',
type: 'histogram',
......@@ -166,7 +123,7 @@ dashboard.rows[3].panels = [
];
// And a table row where you can specify field and sort order
dashboard.rows[4].panels = [
dashboard.rows[1].panels = [
{
title: 'all events',
type: 'table',
......
......@@ -10,7 +10,9 @@
"query": "{{ARGS.query || '*'}}",
"alias": "",
"color": "#7EB26D",
"id": 0
"id": 0,
"pin": false,
"type": "lucene"
}
},
"ids": [
......@@ -23,10 +25,10 @@
],
"list": {
"0": {
"from": "2013-07-30T18:58:13.977Z",
"to": "2013-07-30T19:58:13.977Z",
"field": "@timestamp",
"type": "time",
"field": "@timestamp",
"from": "now-{{ARGS.from || '24h'}}",
"to": "now",
"mandate": "must",
"active": true,
"alias": "",
......@@ -40,90 +42,6 @@
},
"rows": [
{
"title": "Options",
"height": "50px",
"editable": true,
"collapse": false,
"collapsable": true,
"panels": [
{
"title": "Set time span",
"error": "",
"span": 6,
"editable": true,
"group": [
"default"
],
"type": "timepicker",
"mode": "relative",
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
],
"timespan": "{{ARGS.from || '1h'}}",
"timefield": "@timestamp",
"timeformat": "",
"refresh": {
"enable": false,
"interval": 30,
"min": 3
},
"filter_id": 0,
"status": "Stable"
}
]
},
{
"title": "Query",
"height": "50px",
"editable": true,
"collapse": false,
"collapsable": true,
"panels": [
{
"title": "Search",
"error": false,
"span": 12,
"editable": true,
"group": [
"default"
],
"type": "query",
"label": "Search",
"history": [],
"remember": 10,
"pinned": true,
"query": "*"
}
]
},
{
"title": "Filters",
"height": "50px",
"editable": true,
"collapse": true,
"collapsable": true,
"panels": [
{
"title": "dashboard filters",
"error": false,
"span": 12,
"editable": true,
"group": [
"default"
],
"type": "filtering"
}
]
},
{
"title": "Graph",
"height": "350px",
"editable": true,
......@@ -142,7 +60,7 @@
"value_field": null,
"auto_int": true,
"resolution": 100,
"interval": "30s",
"interval": "10m",
"fill": 3,
"linewidth": 3,
"timezone": "browser",
......@@ -163,9 +81,30 @@
0
]
},
"title": "Events over time"
"title": "Events over time",
"intervals": [
"auto",
"1s",
"1m",
"5m",
"10m",
"30m",
"1h",
"3h",
"12h",
"1d",
"1w",
"1M",
"1y"
],
"options": true,
"tooltip": {
"value_type": "cumulative",
"query_as_alias": false
}
}
]
],
"notice": false
},
{
"title": "Events",
......@@ -207,9 +146,12 @@
]
},
"field_list": true,
"status": "Stable"
"status": "Stable",
"trimFactor": 300,
"normTimes": true
}
]
],
"notice": false
}
],
"editable": true,
......@@ -218,5 +160,72 @@
"interval": "day",
"pattern": "[logstash-]YYYY.MM.DD",
"default": "NO_TIME_FILTER_OR_INDEX_PATTERN_NOT_MATCHED"
}
},
"style": "dark",
"panel_hints": true,
"pulldowns": [
{
"type": "query",
"collapse": false,
"notice": false,
"query": "*",
"pinned": true,
"history": [],
"remember": 10
},
{
"type": "filtering",
"collapse": true,
"notice": false
}
],
"nav": [
{
"type": "timepicker2",
"collapse": false,
"notice": false,
"status": "Stable",
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
],
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"timefield": "@timestamp",
"now": true,
"filter_id": 0
}
],
"loader": {
"save_gist": false,
"save_elasticsearch": true,
"save_local": true,
"save_default": true,
"save_temp": true,
"save_temp_ttl_enable": true,
"save_temp_ttl": "30d",
"load_gist": true,
"load_elasticsearch": true,
"load_elasticsearch_size": 20,
"load_local": true,
"hide": false
},
"refresh": false
}
\ No newline at end of file
......@@ -10,6 +10,7 @@ function (angular) {
return {
restrict: 'E',
link: function($scope, elem, attr) {
// once we have the template, scan it for controllers and
// load the module.js if we have any
......
<div class="row-fluid">
<div class="span2">
<label class="small">Mode</label>
<select ng-change="set_refresh(true)" class="input-small" ng-model="panel.mode" ng-options="f for f in ['count','min','mean','max','total']"></select>
<select ng-change="set_refresh(true)" class="input-small" ng-model="panel.mode" ng-options="f for f in ['count','min','mean','max','total','change']"></select>
</div>
<div class="span2">
<label class="small">Time Field</label>
......
......@@ -50,30 +50,32 @@
<span ng-show="panel.legend" class="small"><span ng-show="panel.value_field && panel.mode != 'count'">{{panel.value_field}}</span> {{panel.mode}} per <strong>{{panel.interval}}</strong> | (<strong>{{hits}}</strong> hits)</span>
</div>
<form class="form-inline bordered histogram-options" ng-show="options">
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.bars" ng-checked="panel.bars" ng-change="render()">
Bars
</label>
</div>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.lines" ng-checked="panel.lines" ng-change="render()">
Lines
</label>
</div>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.points" ng-checked="panel.points" ng-change="render()">
Points
</label>
</div>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.stack" ng-checked="panel.stack" ng-change="render()">
Stack
</label>
</div>
<span>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.bars" ng-checked="panel.bars" ng-change="render()">
Bars
</label>
</div>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.lines" ng-checked="panel.lines" ng-change="render()">
Lines
</label>
</div>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.points" ng-checked="panel.points" ng-change="render()">
Points
</label>
</div>
<div class="checkbox">
<label class="small">
<input type="checkbox" ng-model="panel.stack" ng-checked="panel.stack" ng-change="render()">
Stack
</label>
</div>
</span>
<span ng-show="panel.stack">
<div class="checkbox">
<label style="white-space:nowrap" class="small">
......
......@@ -308,8 +308,8 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
}
filterSrv.set({
type:'time',
from:moment.utc(_from),
to:moment.utc(_to),
from:moment.utc(_from).toDate(),
to:moment.utc(_to).toDate(),
field:$scope.panel.time_field
});
};
......
......@@ -49,7 +49,7 @@
</th>
</thead>
<tbody ng-repeat="event in data | slice:panel.offset:panel.offset+panel.size" ng-class-odd="'odd'">
<tbody ng-repeat="event in data| slice:panel.offset:panel.offset+panel.size" ng-class-odd="'odd'">
<tr ng-click="toggle_details(event)" class="pointer">
<td ng-show="panel.fields.length<1">{{event._source|stringify|tableTruncate:panel.trimFactor:1}}</td>
<td ng-show="panel.fields.length>0" ng-repeat="field in panel.fields" ng-bind-html-unsafe="(event.kibana.highlight[field]||event.kibana._source[field]) |tableHighlight | tableTruncate:panel.trimFactor:panel.fields.length"></td>
......@@ -69,7 +69,7 @@
<th>Action</th>
<th>Value</th>
</thead>
<tr ng-repeat="(key,value) in event.kibana._source" ng-class-odd="'odd'">
<tr ng-repeat="(key,value) in event.kibana._source track by $index" ng-class-odd="'odd'">
<td>{{key}}</td>
<td style="white-space:nowrap">
<i class='icon-search pointer' ng-click="build_search(key,value)" bs-tooltip="'Add filter to match this value'"></i>
......
......@@ -39,7 +39,7 @@
<div class="timepicker-from-column">
<label class="small">From</label>
<div class="fake-input timepicker-input">
<input class="timepicker-date" type="text" ng-change="validate(temptime)" ng-model="temptime.from.date" data-date-format="mm/dd/yyyy" required bs-datepicker />@
<input class="timepicker-date" type="text" ng-change="validate(temptime)" ng-model="temptime.from.date" data-date-format="yyyy-mm-dd" required bs-datepicker />@
<input class="timepicker-hms" type="text" maxlength="2" ng-change="validate(temptime)" ng-model="temptime.from.hour" required ng-pattern="patterns.hour" onClick="this.select();"/>:
<input class="timepicker-hms" type="text" maxlength="2" ng-change="validate(temptime)" ng-model="temptime.from.minute" required ng-pattern="patterns.minute" onClick="this.select();"/>:
<input class="timepicker-hms" type="text" maxlength="2" ng-change="validate(temptime)" ng-model="temptime.from.second" required ng-pattern="patterns.second" onClick="this.select();"/>.
......@@ -53,7 +53,7 @@
<div class="fake-input timepicker-input">
<div ng-hide="panel.now">
<input class="timepicker-date" type="text" ng-change="validate(temptime)" ng-model="temptime.to.date" data-date-format="mm/dd/yyyy" required bs-datepicker />@
<input class="timepicker-date" type="text" ng-change="validate(temptime)" ng-model="temptime.to.date" data-date-format="yyyy-mm-dd" required bs-datepicker />@
<input class="timepicker-hms" type="text" maxlength="2" ng-change="validate(temptime)" ng-model="temptime.to.hour" required ng-pattern="patterns.hour" onClick="this.select();"/>:
<input class="timepicker-hms" type="text" maxlength="2" ng-change="validate(temptime)" ng-model="temptime.to.minute" required ng-pattern="patterns.minute" onClick="this.select();"/>:
<input class="timepicker-hms" type="text" maxlength="2" ng-change="validate(temptime)" ng-model="temptime.to.second" required ng-pattern="patterns.second" onClick="this.select();"/>.
......
<div class="row-fluid">
<div class="span6">
<div class="span4">
<label class="small">Relative time options <small>comma seperated</small></label>
<input type="text" array-join class="input-large" ng-model="panel.time_options">
</div>
<div class="span3">
<div class="span4">
<label class="small">Auto-refresh options <small>comma seperated</small></label>
<input type="text" array-join class="input-large" ng-model="panel.refresh_intervals">
</div>
<div class="span2">
<label class="small">Time Field</label>
<input type="text" class="input-small" ng-model="panel.timefield">
</div>
......
......@@ -14,9 +14,10 @@
<form name="input" style="margin:3px 0 0 0">
<ul class="nav nav-pills timepicker-dropdown">
<li class="dropdown" bs-tooltip="(time.from.date | date:'yyyy-MM-dd HH:mm:ss.sss') + ' to ' +(time.to.date | date:'yyyy-MM-dd HH:mm:ss.sss')" data-placement="bottom" ng-click="dismiss();">
<a class="dropdown-toggle timepicker-dropdown" data-toggle="dropdown" href="#">
<span class="small" ng-show="filterSrv.idsByType('time').length">
<span ng-show="filterSrv.idsByType('time').length">
<span class="pointer" ng-hide="panel.now">{{time.from.date | date:'MMM d, y HH:mm:ss'}}</span>
<span class="pointer" ng-show="panel.now">{{time.from.date | moment:'ago'}}</span>
to
......@@ -24,14 +25,27 @@
<span class="pointer" ng-show="panel.now">{{time.to.date | moment:'ago'}}</span>
</span>
<span ng-hide="filterSrv.idsByType('time').length">Set a time filter</span>
<i class="small icon-caret-down"></i>
<span ng-show="dashboard.current.refresh" class="text-warning">refreshed every {{dashboard.current.refresh}} </span>
<i class="icon-caret-down"></i>
</a>
<ul class="dropdown-menu">
<li ng-repeat='timespan in panel.time_options'>
<!-- Relative time options -->
<li ng-repeat='timespan in panel.time_options track by $index'>
<a ng-click="setRelativeFilter(timespan)">Last {{timespan}}</a>
</li>
<!-- Auto refresh submenu -->
<li class="dropdown-submenu">
<a href="#">Auto-Refresh</a>
<ul class="dropdown-menu">
<li><a ng-click="dashboard.set_interval(false)">Off</a></li>
<li ng-repeat="interval in panel.refresh_intervals track by $index"><a ng-click="dashboard.set_interval(interval)">Every {{interval}}</a></li>
</ul>
</li>
<li><a ng-click="customTime()">Custom</a></li>
</ul>
</li>
</ul>
......
......@@ -36,16 +36,10 @@ function (angular, app, _, moment, kbn) {
// Set and populate defaults
var _d = {
status : "Stable",
mode : "relative",
time_options : ['5m','15m','1h','6h','12h','24h','2d','7d','30d'],
timespan : '15m',
timefield : '@timestamp',
timeformat : "",
refresh : {
enable : false,
interval: 30,
min : 3
}
refresh_intervals : ['5s','10s','30s','1m','5m','15m','30m','1h','2h','1d'],
timefield : '@timestamp'
};
_.defaults($scope.panel,_d);
......@@ -212,9 +206,7 @@ function (angular, app, _, moment, kbn) {
// Do not use the results of this function unless you plan to use setHour/Minutes/etc on the result
var datepickerToLocal = function(date) {
console.log(date);
date = moment(date).clone().toDate();
console.log(moment(new Date(date.getTime() + date.getTimezoneOffset() * 60000)).toDate());
return moment(new Date(date.getTime() + date.getTimezoneOffset() * 60000)).toDate();
};
......
<li ng-controller="RowCtrl"><kibana-simple-panel type="'timepicker2'" ng-cloak></kibana-simple-panel></li>
<style>
.noarrow>a:after {
display: none !important;
}
</style>
<li><a bs-tooltip="'Goto saved default'" data-placement="bottom" href='#/dashboard'><i class='icon-home'></i></a></li>
<li ng-repeat="pulldown in dashboard.current.nav" ng-controller="PulldownCtrl"><kibana-simple-panel type="pulldown.type" panel="pulldown" ng-cloak></kibana-simple-panel></li>
<li><a bs-tooltip="'Goto saved default'" data-placement="bottom" href='#/dashboard'><i class='icon-home'></i></a></li>
<li class="dropdown" bs-tooltip="'Load'" data-placement="bottom" ng-show="showDropdown('load')" >
<a href="#" class="dropdown-toggle" data-toggle="dropdown" ng-click="elasticsearch_dblist('title:'+elasticsearch.query+'*')">
<i class='icon-folder-open'></i>
</a>
<ul class="dropdown-menu" style="padding:10px">
<li ng-show='loader.load_local'>
<h5>Local File <tip>Load dashboard JSON layout from file</tip></h5>
<form>
<input type="file" id="dashupload" dash-upload /><br>
</form>
</li>
<li ng-show='loader.load_gist'>
<h5>Gist <tip>Enter a gist number or url</tip></h5>
<form>
<input type="text" ng-model="gist.url"/ placeholder="Gist number or URL"><br>
<button class="btn" ng-click="gist_dblist(dashboard.gist_id(gist.url))" ng-show="dashboard.is_gist(gist.url)"><i class="icon-github-alt"></i> Get gist:{{gist.url | gistid}}</button>
<h6 ng-show="gist.files.length">Dashboards in gist:{{gist.url | gistid}} <small>click to load</small></h6>
<h6 ng-hide="gist.files.length">No gist dashboards found</h6>
<table class="table table-condensed table-striped">
<tr ng-repeat="file in gist.files">
<td><a ng-click="dashboard.dash_load(file)">{{file.title}}</a></td>
</tr>
</table>
</form>
</li>
<li ng-show='loader.load_elasticsearch'>
<h5>Elasticsearch</h5>
<form>
<li ng-show='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-show="elasticsearch.dashboards.length">Elasticsearch stored dashboards</h6>
<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']">
......@@ -42,38 +27,81 @@
</tr>
</table>
</li>
<li class="dropdown-submenu noarrow">
<a tabindex="-1" href="#" class="small" style="padding:0"><i class="icon-caret-left"></i> Advanced</a>
<ul class="dropdown-menu" style="padding:10px">
<li ng-show='dashboard.current.loader.load_local'>
<h5>Local File <tip>Load dashboard JSON layout from file</tip></h5>
<form>
<input type="file" id="dashupload" dash-upload /><br>
</form>
</li>
<li ng-show='dashboard.current.loader.load_gist'>
<h5>Gist <tip>Enter a gist number or url</tip></h5>
<form>
<input type="text" ng-model="gist.url"/ placeholder="Gist number or URL"><br>
<button class="btn" ng-click="gist_dblist(dashboard.gist_id(gist.url))" ng-show="dashboard.is_gist(gist.url)"><i class="icon-github-alt"></i> Get gist:{{gist.url | gistid}}</button>
<h6 ng-show="gist.files.length">Dashboards in gist:{{gist.url | gistid}} <small>click to load</small></h6>
<h6 ng-hide="gist.files.length || !gist.url.length">No gist dashboards found</h6>
<table class="table table-condensed table-striped">
<tr ng-repeat="file in gist.files">
<td><a ng-click="dashboard.dash_load(file)">{{file.title}}</a></td>
</tr>
</table>
</form>
</li>
</ul>
</li>
</ul>
</li>
<li class="dropdown" bs-tooltip="'Save'" data-placement="bottom" ng-show="showDropdown('save')">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class='icon-save'></i>
</a>
<ul class="dropdown-menu" style="padding:10px">
<li ng-show="loader.save_default || loader.save_local">
<h5>Locally</h5>
<ul class="unstyled">
<li><a class="link" ng-show="loader.save_local" ng-click="dashboard.to_file()"><i class="icon-download"></i> Export to File</a> <tip>Export layout, not data, to file</tip></li>
<li><a class="link" ng-show="loader.save_default" ng-click="set_default()"><i class="icon-bookmark"></i> Set as Browser Default</a> <tip>Store dashboard preference to browser's localStorage</tip></li>
<li><a class="link" ng-show="loader.save_default" ng-click="purge_default()"><i class="icon-ban-circle"></i> Clear Browser Default</a></li>
</ul>
</li>
<li ng-show="loader.save_gist">
<h5>Gist</h5>
<form class="input-append">
<input class='input-medium' placeholder='Title' type="text" ng-model="gist.title"/>
<button class="btn" ng-click="save_gist()"><i class="icon-github-alt"></i></button>
</form><br>
<small ng-show="gist.last">Last gist: <a target="_blank" href="{{gist.last}}">{{gist.last}}</a></small>
</li>
<li ng-show="loader.save_elasticsearch">
<h5>Elasticsearch</h5>
<form class="input-append">
<input class='input-medium' placeholder="{{dashboard.current.title}}" type="text" ng-model="elasticsearch.title"/>
<li ng-show="dashboard.current.loader.save_elasticsearch">
<form class="input-append nomargin">
<input class='input-medium' ng-model="dashboard.current.title" type="text" ng-model="elasticsearch.title"/>
<button class="btn" ng-click="elasticsearch_save('dashboard')"><i class="icon-save"></i></button>
</form>
</li>
<li class="dropdown-submenu noarrow">
<a tabindex="-1" href="#" class="small" style="padding:0"><i class="icon-caret-left"></i> Advanced</a>
<ul class="dropdown-menu">
<li ng-show="dashboard.current.loader.save_default">
<a class="link" ng-click="set_default()">Set as my home</a>
</li>
<li ng-show="dashboard.current.loader.save_default">
<a class="link" ng-click="purge_default()">Clear my home</a>
</li>
<li ng-show="dashboard.current.loader.save_local">
<a class="link" ng-click="dashboard.to_file()">Export schema</a>
</li>
<li ng-show="dashboard.current.loader.save_gist" style="margin:10px">
<h6>Gist</h6>
<form class="input-append">
<input class='input-medium' placeholder='Title' type="text" ng-model="gist.title"/>
<button class="btn" ng-click="save_gist()"><i class="icon-github-alt"></i></button>
</form><br>
<small ng-show="gist.last">Last gist: <a target="_blank" href="{{gist.last}}">{{gist.last}}</a></small>
</li>
</ul>
</li>
</ul>
</li>
<li ng-show="showDropdown('share')"><a bs-tooltip="'Share'" data-placement="bottom" ng-click="elasticsearch_save('temp',loader.save_temp_ttl)" bs-modal="'app/partials/dashLoaderShare.html'"><i class='icon-share'></i></a></li>
<li ng-show="showDropdown('share')"><a bs-tooltip="'Share'" data-placement="bottom" ng-click="elasticsearch_save('temp',dashboard.current.loader.save_temp_ttl)" bs-modal="'app/partials/dashLoaderShare.html'"><i class='icon-share'></i></a></li>
<li ng-show="dashboard.current.editable" bs-tooltip="'Configure dashboard'" data-placement="bottom"><a href='#' bs-modal="'app/partials/dasheditor.html'"><i class='icon-cog pointer'></i></a></li>
<li ng-show="dashboard.current.editable "bs-tooltip="'Configure dashboard'" data-placement="bottom"><a href='#' bs-modal="'app/partials/dasheditor.html'"><i class='icon-cog pointer'></i></a></li>
<!-- is there a better way to repeat without actually affecting the page? -->
<nil ng-repeat="row in dashboard.current.pulldowns" ng-controller="RowCtrl">
<div class="top-row-open" ng-hide="row.collapse">
<kibana-simple-panel type="row.type" ng-cloak></kibana-simple-panel>
<nil ng-repeat="pulldown in dashboard.current.pulldowns" ng-controller="PulldownCtrl">
<div class="top-row-open" ng-hide="pulldown.collapse">
<kibana-simple-panel type="pulldown.type" ng-cloak></kibana-simple-panel>
</div>
<div class="top-row-close pointer" ng-click="toggle_row(row);dismiss();" bs-tooltip="'Toggle '+row.type" data-placement="bottom">
<span class="small row-text">{{row.type}}</span>
<i class="small" ng-class="{'icon-expand':row.collapse,'icon-collapse-top':!row.collapse}"></i>
<i class="small icon-circle text-warning" ng-show="row.notice && row.collapse"></i>
<div class="top-row-close pointer" ng-click="toggle_pulldown(pulldown);dismiss();" bs-tooltip="'Toggle '+pulldown.type" data-placement="bottom">
<span class="small row-text">{{pulldown.type}}</span>
<i class="small" ng-class="{'icon-expand':pulldown.collapse,'icon-collapse-top':!pulldown.collapse}"></i>
<i class="small icon-star text-warning" ng-show="row.notice && pulldown.collapse"></i>
</div>
</nil>
......
<div class="modal-body">
<div class="pull-right editor-title">Dashboard settings</div>
<div ng-model="editor.index" bs-tabs>
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
<div ng-repeat="tab in ['General','Index','Rows','Controls']" data-title="{{tab}}">
</div>
<div ng-repeat="tab in dashboard.current.nav" data-title="{{tab.type}}">
</div>
</div>
<div ng-show="editor.index == 0">
......@@ -103,46 +105,49 @@
<h5>Allow saving to</h5>
<div class="row-fluid">
<div class="span2">
<label class="small">File</label><input type="checkbox" ng-model="loader.save_local" ng-checked="loader.save_local">
<label class="small">File</label><input type="checkbox" ng-model="dashboard.current.loader.save_local" ng-checked="dashboard.current.loader.save_local">
</div>
<div class="span2">
<label class="small">Browser</label><input type="checkbox" ng-model="loader.save_default" ng-checked="loader.save_default">
<label class="small">Browser</label><input type="checkbox" ng-model="dashboard.current.loader.save_default" ng-checked="dashboard.current.loader.save_default">
</div>
<div class="span2">
<label class="small">Gist <tip>Requires your domain to be OAUTH registered with Github<tip></label><input type="checkbox" ng-model="loader.save_gist" ng-checked="loader.save_gist">
<label class="small">Gist <tip>Requires your domain to be OAUTH registered with Github<tip></label><input type="checkbox" ng-model="dashboard.current.loader.save_gist" ng-checked="dashboard.current.loader.save_gist">
</div>
<div class="span2">
<label class="small">Elasticsearch</label><input type="checkbox" ng-model="loader.save_elasticsearch" ng-checked="loader.save_elasticsearch">
<label class="small">Elasticsearch</label><input type="checkbox" ng-model="dashboard.current.loader.save_elasticsearch" ng-checked="dashboard.current.loader.save_elasticsearch">
</div>
</div>
<h5>Allow loading from</h5>
<div class="row-fluid">
<div class="span2">
<label class="small">Local file</label><input type="checkbox" ng-model="loader.load_local" ng-checked="loader.load_local">
<label class="small">Local file</label><input type="checkbox" ng-model="dashboard.current.loader.load_local" ng-checked="dashboard.current.loader.load_local">
</div>
<div class="span2">
<label class="small">Gist</label><input type="checkbox" ng-model="loader.load_gist" ng-checked="loader.load_gist">
<label class="small">Gist</label><input type="checkbox" ng-model="dashboard.current.loader.load_gist" ng-checked="dashboard.current.loader.load_gist">
</div>
<div class="span2">
<label class="small">Elasticsearch</label><input type="checkbox" ng-model="loader.load_elasticsearch" ng-checked="loader.load_elasticsearch">
<label class="small">Elasticsearch</label><input type="checkbox" ng-model="dashboard.current.loader.load_elasticsearch" ng-checked="dashboard.current.loader.load_elasticsearch">
</div>
<div class="span3" ng-show="loader.load.elasticsearch">
<label class="small">ES list size</label><input class="input-mini" type="number" ng-model="loader.load_elasticsearch_size">
<div class="span3" ng-show="dashboard.current.loader.load.elasticsearch">
<label class="small">ES list size</label><input class="input-mini" type="number" ng-model="dashboard.current.loader.load_elasticsearch_size">
</div>
</div>
<h5>Sharing</h5>
<div class="row-fluid">
<div class="span2" >
<label class="small">Allow Sharing <tip>Allow generating adhoc links to dashboards</tip></label><input type="checkbox" ng-model="loader.save_temp" ng-checked="loader.save_temp">
<label class="small">Allow Sharing <tip>Allow generating adhoc links to dashboards</tip></label><input type="checkbox" ng-model="dashboard.current.loader.save_temp" ng-checked="dashboard.current.loader.save_temp">
</div>
<div class="span2" ng-show="loader.save_temp">
<label class="small">TTL <tip>Expire temp urls</tip></label><input type="checkbox" ng-model="loader.save_temp_ttl_enable">
<div class="span2" ng-show="dashboard.current.loader.save_temp">
<label class="small">TTL <tip>Expire temp urls</tip></label><input type="checkbox" ng-model="dashboard.current.loader.save_temp_ttl_enable">
</div>
<div class="span5" ng-show="loader.save_temp &amp;&amp; loader.save_temp_ttl_enable">
<label class="small">TTL Duration <tip>Elasticsearch date math, eg: 1m,1d,1w,30d </tip></label><input class="input-small" type="text" ng-model="loader.save_temp_ttl">
<div class="span5" ng-show="dashboard.current.loader.save_temp &amp;&amp; dashboard.current.loader.save_temp_ttl_enable">
<label class="small">TTL Duration <tip>Elasticsearch date math, eg: 1m,1d,1w,30d </tip></label><input class="input-small" type="text" ng-model="dashboard.current.loader.save_temp_ttl">
</div>
</div>
</div>
<div ng-repeat="pulldown in dashboard.current.nav" ng-controller="PulldownCtrl" ng-include="'./app/panels/'+pulldown.type+'/editor.html'" ng-show="editor.index == 4+$index">
</div>
</div>
<div class="modal-footer">
......
......@@ -13,7 +13,8 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
var module = angular.module('kibana.services');
module.service('dashboard', function($routeParams, $http, $rootScope, $injector, $location,
module.service('dashboard', function(
$routeParams, $http, $rootScope, $injector, $location, $timeout,
ejsResource, timer, kbnIndex, alertSrv
) {
// A hash of defaults to use when loading a dashboard
......@@ -33,6 +34,11 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
type: 'filtering'
}
],
nav: [
{
type: 'timepicker2'
}
],
services: {},
loader: {
save_gist: false,
......@@ -42,10 +48,10 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
save_temp: true,
save_temp_ttl_enable: true,
save_temp_ttl: '30d',
load_gist: true,
load_gist: false,
load_elasticsearch: true,
load_elasticsearch_size: 20,
load_local: true,
load_local: false,
hide: false
},
index: {
......@@ -53,6 +59,7 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
pattern: '_all',
default: 'INDEX_MISSING'
},
refresh: false
};
// An elasticJS client to use
......@@ -172,6 +179,7 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
self.indices = [dashboard.index.default];
}
// Set the current dashboard
self.current = _.clone(dashboard);
// Ok, now that we've setup the current dashboard, we can inject our services
......@@ -182,11 +190,16 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
querySrv.init();
filterSrv.init();
// If there's an index interval set and no existing time filter, send a refresh to set one
// If there's an interval set, the indices have not been calculated yet,
// so there is no data. Call refresh to calculate the indices and notify the panels.
if(dashboard.index.interval !== 'none') {
self.refresh();
}
if(dashboard.refresh) {
self.set_interval(dashboard.refresh);
}
return true;
};
......@@ -420,6 +433,23 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
return false;
});
};
this.set_interval = function (interval) {
self.current.refresh = interval;
if(interval) {
var _i = kbn.interval_to_ms(interval);
timer.cancel(self.refresh_timer);
self.refresh_timer = timer.register($timeout(function() {
self.set_interval(interval);
self.refresh();
},_i));
self.refresh();
} else {
timer.cancel(self.refresh_timer);
}
};
});
});
\ No newline at end of file
......@@ -7,26 +7,20 @@ function (angular, _) {
var module = angular.module('kibana.services');
module.service('panelMove', function(dashboard, $rootScope, alertSrv) {
module.service('panelMove', function(dashboard, $rootScope) {
/* each of these can take event,ui,data parameters */
var notices = [];
this.onStart = function() {
dashboard.panelDragging = true;
notices.push(alertSrv.set('Moving','Drop this panel into an available space, or on top of another panel','info'));
$rootScope.$apply();
};
this.onOver = function() {
notices.push(alertSrv.set('Add panel',
'Drop to add panel to this row. Panel will use row height, but retain their span','success'));
$rootScope.$apply();
};
this.onOut = function() {
clearNotices({severity:'success'});
$rootScope.$apply();
};
......@@ -63,21 +57,12 @@ function (angular, _) {
};
var cleanup = function () {
_.each(notices, function(n){
alertSrv.clear(n);
});
_.each(dashboard.current.rows, function(row) {
row.panels = _.without(row.panels,{});
row.panels = _.compact(row.panels);
});
};
var clearNotices = function(options) {
_.each(_.where(notices,options), function(n) {
alertSrv.clear(n);
});
};
});
});
\ No newline at end of file
......@@ -26,7 +26,7 @@
<link rel="stylesheet" href="css/bootstrap-responsive.min.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<div ng-repeat='alert in dashAlerts.list' class="alert-{{alert.severity}} dashboard-notice" ng-show="$last" style="position:fixed">
<div ng-repeat='alert in dashAlerts.list' class="alert-{{alert.severity}} dashboard-notice" ng-show="$last">
<button type="button" class="close" ng-click="dashAlerts.clear(alert)" style="padding-right:50px">&times;</button>
<strong>{{alert.title}}</strong> <span ng-bind-html-unsafe='alert.text'></span> <div style="padding-right:10px" class='pull-right small'> {{$index + 1}} alert(s) </div>
</div>
......
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