Преглед изворни кода

Moved dashboard controller to nav bar

Rashid Khan пре 12 година
родитељ
комит
eb25ce5f7e

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
common/css/bootstrap.dark.min.css


Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
common/css/bootstrap.light.min.css


+ 0 - 34
dashboards/default.json

@@ -91,40 +91,6 @@
           "status": "Stable"
         }
       ]
-    },
-    {
-      "title": "Loader",
-      "height": "50px",
-      "editable": true,
-      "collapse": false,
-      "collapsable": true,
-      "panels": [
-        {
-          "error": false,
-          "span": 3,
-          "editable": true,
-          "group": [
-            "default"
-          ],
-          "type": "dashcontrol",
-          "save": {
-            "gist": false,
-            "elasticsearch": true,
-            "local": true,
-            "default": true
-          },
-          "load": {
-            "gist": true,
-            "elasticsearch": true,
-            "local": true
-          },
-          "hide_control": false,
-          "elasticsearch_size": 20,
-          "temp": true,
-          "ttl_enable": true,
-          "temp_ttl": "30d"
-        }
-      ]
     }
   ],
   "editable": true,

+ 0 - 39
dashboards/guided.json

@@ -38,45 +38,6 @@
       "collapse": false,
       "collapsable": true,
       "panels": [
-        {
-          "error": false,
-          "span": 3,
-          "editable": true,
-          "group": [
-            "default"
-          ],
-          "type": "dashcontrol",
-          "save": {
-            "gist": false,
-            "elasticsearch": true,
-            "local": true,
-            "default": true
-          },
-          "load": {
-            "gist": true,
-            "elasticsearch": true,
-            "local": true
-          },
-          "hide_control": false,
-          "elasticsearch_size": 20,
-          "temp": true,
-          "temp_ttl": "30d",
-          "ttl_enable": true
-        },
-        {
-          "error": false,
-          "span": 4,
-          "editable": true,
-          "group": [
-            "default"
-          ],
-          "type": "text",
-          "status": "Stable",
-          "mode": "markdown",
-          "content": "The dashcontrol panel to the left lets you save this dashboard to Elasticsearch once you have it how you like it. See the note on the welcome page about setting a global default.",
-          "style": {},
-          "title": "The dashcontrol panel"
-        },
         {
           "error": false,
           "span": 5,

+ 0 - 4
dashboards/logstash.js

@@ -129,10 +129,6 @@ dashboard.rows[0].panels = [
     type: 'timepicker',
     span: 6,
     timespan: ARGS.from||_d_timespan
-  },
-  {
-    type: 'dashcontrol',
-    span: 3
   }
 ];
 

+ 0 - 25
dashboards/logstash.json

@@ -76,31 +76,6 @@
           },
           "filter_id": 0,
           "status": "Stable"
-        },
-        {
-          "error": false,
-          "span": 3,
-          "editable": true,
-          "group": [
-            "default"
-          ],
-          "type": "dashcontrol",
-          "save": {
-            "gist": false,
-            "elasticsearch": true,
-            "local": true,
-            "default": true
-          },
-          "load": {
-            "gist": true,
-            "elasticsearch": true,
-            "local": true
-          },
-          "hide_control": false,
-          "elasticsearch_size": 20,
-          "temp": true,
-          "temp_ttl": "30d",
-          "ttl_enable": true
         }
       ]
     },

+ 2 - 41
dashboards/noted.json

@@ -38,45 +38,6 @@
       "collapse": false,
       "collapsable": true,
       "panels": [
-        {
-          "error": false,
-          "span": 3,
-          "editable": true,
-          "group": [
-            "default"
-          ],
-          "type": "dashcontrol",
-          "save": {
-            "gist": false,
-            "elasticsearch": true,
-            "local": true,
-            "default": true
-          },
-          "load": {
-            "gist": true,
-            "elasticsearch": true,
-            "local": true
-          },
-          "hide_control": false,
-          "elasticsearch_size": 20,
-          "temp": true,
-          "temp_ttl": "30d",
-          "ttl_enable": true
-        },
-        {
-          "error": false,
-          "span": 4,
-          "editable": true,
-          "group": [
-            "default"
-          ],
-          "type": "text",
-          "status": "Stable",
-          "mode": "markdown",
-          "content": "The dashcontrol panel to the left lets you save this dashboard to Elasticsearch once you have it how you like it. See the note on the welcome page about setting a global default.",
-          "style": {},
-          "title": "The dashcontrol panel"
-        },
         {
           "error": false,
           "span": 5,
@@ -212,6 +173,6 @@
   "index": {
     "interval": "none",
     "pattern": "[logstash-]YYYY.MM.DD",
-    "default": "NOT_CONFIGURED"
+    "default": "_all"
   }
-}
+}

+ 7 - 4
index.html

@@ -1,6 +1,4 @@
 <!DOCTYPE html>
-  <!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en" id="ng-app"> <![endif]-->
-  <!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8" lang="en" id="ng-app"> <![endif]-->
   <!--[if IE 8]>         <html class="no-js lt-ie9" lang="en"> <![endif]-->
   <!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
   <head>
@@ -39,9 +37,14 @@
     <div class="navbar navbar-static-top">
       <div class="navbar-inner">
         <div class="container-fluid">
-          <p class="navbar-text pull-right version"><strong>Kibana 3</strong> milestone 3</p>
           <span class="brand">{{dashboard.current.title}}</span>
-          <div class="brand"><i class='icon-cog pointer' ng-show='dashboard.current.editable' bs-modal="'partials/dasheditor.html'"></i></div>
+          <ul class="nav" ng-show='dashboard.current.editable'>
+            <li>
+              <a href='#' bs-modal="'partials/dasheditor.html'"><i class='icon-cog pointer'></i></a>
+            </li>
+          </ul>
+          <ul class="nav pull-right" ng-controller='dashLoader' ng-init="init()" ng-include="'partials/dashLoader.html'">
+          </ul>
         </div>
       </div>
     </div>

+ 3 - 0
js/app.js

@@ -43,6 +43,7 @@ _.each(config.modules, function(v) {
 
 /* Application level module which depends on filters, controllers, and services */
 labjs.wait(function(){
+  // Create the module
   angular.module('kibana', modules).config(['$routeProvider', function($routeProvider) {
       $routeProvider
         .when('/dashboard', {
@@ -58,6 +59,8 @@ labjs.wait(function(){
           redirectTo: 'dashboard'
         });
     }]);
+
+  // Wait for ready, then bootstrap
   angular.element(document).ready(function() {
     $('body').attr('ng-controller', 'DashCtrl');
     angular.bootstrap(document, ['kibana']);

+ 112 - 0
js/controllers.js

@@ -118,6 +118,118 @@ angular.module('kibana.controllers', [])
 
   $scope.init();
 
+})
+.controller('dashLoader', function($scope, $http, timer, dashboard, alertSrv) {
+
+  $scope.loader = dashboard.current.loader;
+
+  $scope.init = function() {
+    $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(type === 'load') {
+      return (_l.load_elasticsearch || _l.load_gist || _l.load_local);
+    }
+    if(type === 'save') {
+      return (_l.save_elasticsearch || _l.save_gist || _l.local_local || _l.save_default);
+    }
+    if(type === 'share') {
+      return (_l.save_temp);
+    }
+    return false;
+  };
+
+  $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);
+    } else {
+      alertSrv.set('Incompatible Browser','Sorry, your browser is too old for this feature','error',5000);
+    }
+  };
+
+  $scope.purge_default = function() {
+    if(dashboard.purge_default()) {
+      alertSrv.set('Local Default Clear','Your local default dashboard has been cleared','success',5000);
+    } else {
+      alertSrv.set('Incompatible Browser','Sorry, your browser is too old for this feature','error',5000);
+    }
+  };
+
+  $scope.elasticsearch_save = function(type,ttl) {
+    dashboard.elasticsearch_save(
+      type,
+      ($scope.elasticsearch.title || dashboard.current.title),
+      ($scope.loader.save_temp_ttl_enable ? ttl : false)
+    ).then(
+      function(result) {
+      if(!_.isUndefined(result._id)) {
+        alertSrv.set('Dashboard Saved','This dashboard has been saved to Elasticsearch as "' +
+          result._id + '"','success',5000);
+        if(type === 'temp') {
+          $scope.share = dashboard.share_link(dashboard.current.title,'temp',result._id);
+        }
+      } else {
+        alertSrv.set('Save failed','Dashboard could not be saved to Elasticsearch','error',5000);
+      }
+    });
+  };
+
+  $scope.elasticsearch_delete = function(id) {
+    dashboard.elasticsearch_delete(id).then(
+      function(result) {
+        if(!_.isUndefined(result)) {
+          if(result.found) {
+            alertSrv.set('Dashboard Deleted',id+' has been deleted','success',5000);
+            // Find the deleted dashboard in the cached list and remove it
+            var toDelete = _.where($scope.elasticsearch.dashboards,{_id:id})[0];
+            $scope.elasticsearch.dashboards = _.without($scope.elasticsearch.dashboards,toDelete);
+          } else {
+            alertSrv.set('Dashboard Not Found','Could not find '+id+' in Elasticsearch','warning',5000);
+          }
+        } else {
+          alertSrv.set('Dashboard Not Deleted','An error occurred deleting the dashboard','error',5000);
+        }
+      }
+    );
+  };
+
+  $scope.elasticsearch_dblist = function(query) {
+    dashboard.elasticsearch_list(query,$scope.loader.load_elasticsearch_size).then(
+      function(result) {
+      if(!_.isUndefined(result.hits)) {
+        $scope.hits = result.hits.total;
+        $scope.elasticsearch.dashboards = result.hits.hits;
+      }
+    });
+  };
+
+  $scope.save_gist = function() {
+    dashboard.save_gist($scope.gist.title).then(
+      function(link) {
+      if(!_.isUndefined(link)) {
+        $scope.gist.last = link;
+        alertSrv.set('Gist saved','You will be able to access your exported dashboard file at '+
+          '<a href="'+link+'">'+link+'</a> in a moment','success');
+      } else {
+        alertSrv.set('Save failed','Gist could not be saved','error',5000);
+      }
+    });
+  };
+
+  $scope.gist_dblist = function(id) {
+    dashboard.gist_list(id).then(
+      function(files) {
+      if(files && files.length > 0) {
+        $scope.gist.files = files;
+      } else {
+        alertSrv.set('Gist Failed','Could not retrieve dashboard list from gist','error',5000);
+      }
+    });
+  };
 });
 
 

+ 11 - 0
js/filters.js

@@ -68,4 +68,15 @@ angular.module('kibana.filters', [])
     });
     return text;
   };
+}).filter('gistid', function() {
+  var gist_pattern = /(\d{5,})|([a-z0-9]{10,})|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;
+  return function(input, scope) {
+    //return input+"boners"
+    if(!(_.isUndefined(input))) {
+      var output = input.match(gist_pattern);
+      if(!_.isNull(output) && !_.isUndefined(output)) {
+        return output[0].replace(/.*\//, '');
+      }
+    }
+  };
 });

+ 25 - 9
js/services.js

@@ -579,6 +579,20 @@ angular.module('kibana.services', [])
     failover: false,
     rows: [],
     services: {},
+    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
+    },
     index: {
       interval: 'none',
       pattern: '_all',
@@ -683,13 +697,20 @@ angular.module('kibana.services', [])
     }
   };
 
+  var dash_defaults = function(dashboard) {
+    _.defaults(dashboard,_dash);
+    _.defaults(dashboard.index,_dash.index);
+    _.defaults(dashboard.loader,_dash.loader);
+    return dashboard;
+  };
+
   this.dash_load = function(dashboard) {
 
     // Cancel all timers
     timer.cancel_all();
 
     // Make sure the dashboard being loaded has everything required
-    _.defaults(dashboard,_dash);
+    dashboard = dash_defaults(dashboard);
 
     // If not using time based indices, use the default index
     if(dashboard.index.interval === 'none') {
@@ -790,9 +811,7 @@ angular.module('kibana.services', [])
         return false;
       }
 
-      var _dashboard = result.data;
-      _.defaults(_dashboard,_dash);
-      self.dash_load(_dashboard);
+      self.dash_load(dash_defaults(result.data));
       return true;
     },function(result) {
       alertSrv.set('Error',"Could not load <i>dashboards/"+file+"</i>. Please make sure it exists" ,'error');
@@ -834,9 +853,7 @@ angular.module('kibana.services', [])
       if(!result) {
         return false;
       }
-      var _dashboard = result.data;
-      _.defaults(_dashboard,_dash);
-      self.dash_load(_dashboard);
+      self.dash_load(dash_defaults(result.data));
       return true;
     },function(result) {
       alertSrv.set('Error',
@@ -907,7 +924,6 @@ angular.module('kibana.services', [])
       );
   };
 
-  // TOFIX: Gist functionality
   this.save_gist = function(title,dashboard) {
     var save = _.clone(dashboard || self.current);
     save.title = title || self.current.title;
@@ -939,7 +955,7 @@ angular.module('kibana.services', [])
           var file = JSON.parse(v.content);
           files.push(file);
         } catch(e) {
-          // Nothing?
+          return false;
         }
       });
       return files;

+ 1 - 1
panels/dashcontrol/module.html

@@ -1,5 +1,5 @@
 <kibana-panel ng-controller='dashcontrol' ng-init="init()">
-  <label class='small'>Dashboard Control</label>
+  <label class='small'>Dash Control <tip icon="warning-sign">This panel is deprecated! Please remove it from your dashboard</tip></label>
   <button class='btn' ng-show="panel.load.gist || panel.load.elasticsearch || panel.load.local" data-placement="bottom" data-unique="1" ng-click="elasticsearch_dblist(elasticsearch.query)" bs-popover="'panels/dashcontrol/load.html'"><i class='icon-folder-open'></i>  <i class='icon-caret-down'></i></button>
   <button class='btn' ng-show="panel.save.gist || panel.save.elasticsearch || panel.save.local || panel.save.default" data-placement="bottom" data-unique="1" bs-popover="'panels/dashcontrol/save.html'"><i class='icon-save'></i>  <i class='icon-caret-down'></i></button>
   <button ng-show="panel.temp" class='btn' ng-click="elasticsearch_save('temp',panel.temp_ttl)" bs-modal="'panels/dashcontrol/share.html'"><i class='icon-share'></i></button>

+ 2 - 2
panels/dashcontrol/module.js

@@ -27,8 +27,8 @@ angular.module('kibana.dashcontrol', [])
 .controller('dashcontrol', function($scope, $http, timer, dashboard, alertSrv) {
 
   $scope.panelMeta = {
-    status  : "Stable",
-    description : "This panel allows for saving, loading, exporting and sharing dashboard schemas."
+    status  : "Deprecated",
+    description : "This panel has been moved to the navigation bar. See the dashboard setting editor to configure it."
   };
 
   $scope.panel = $scope.panel || {};

+ 1 - 1
panels/dashcontrol/save.html

@@ -8,7 +8,7 @@
       <ul class="nav nav-list">
         <li><a ng-show="panel.save.local" ng-click="dashboard.to_file()"><i class="icon-download"></i> Export to File</a></li>
         <li><a ng-show="panel.save.default" ng-click="set_default()"><i class="icon-bookmark"></i> Set as My Default</a></li>
-        <li><a ng-show="panel.save.default" ng-click="purge_default()"><i class="icon-ban-circle"></i> Clear My Default</a></li>    
+        <li><a ng-show="panel.save.default" ng-click="purge_default()"><i class="icon-ban-circle"></i> Clear My Default</a></li>
       </ul>
     </form>
   </div>

+ 74 - 0
partials/dashLoader.html

@@ -0,0 +1,74 @@
+  <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('*')">
+      <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>
+          <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']">
+            <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><i class="icon-share" ng-click="share = dashboard.share_link(row._id,'elasticsearch',row._id)" bs-modal="'panels/dashcontrol/share.html'"></i></a></td>
+          </tr>
+        </table>
+      </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='Title' type="text" ng-model="elasticsearch.title"/>
+          <button class="btn" ng-click="elasticsearch_save('dashboard')"><i class="icon-save"></i></button>
+        </form>
+      </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="'partials/dashLoaderShare.html'"><i class='icon-share'></i></a></li>

+ 11 - 0
partials/dashLoaderShare.html

@@ -0,0 +1,11 @@
+<div class="modal-header">
+  <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+  <h3>{{share.title}} <small>shareable link</small></h3>
+</div>
+<div class="modal-body">
+  <label>Share this dashboard with this URL</label>
+  <input ng-model='share.link' type="text" style="width:90%" onclick="this.select()" onfocus="this.select()" ng-change="share = dashboard.share_link(share.title,share.type,share.id)">
+</div>
+<div class="modal-footer">
+  <button type="button" class="btn btn-success" ng-click="dismiss();$broadcast('render')">Close</button>
+</div>

+ 110 - 60
partials/dasheditor.html

@@ -2,44 +2,47 @@
   <div class="pull-right editor-title">Dashboard settings</div>
 
   <div ng-model="editor.index" bs-tabs>
-    <div ng-repeat="tab in ['General','Index','Rows']" data-title="{{tab}}">
+    <div ng-repeat="tab in ['General','Index','Rows','Controls']" data-title="{{tab}}">
     </div>
   </div>
 
-
-  <div class="row-fluid" ng-show="editor.index == 0">
-    <div class="span4">
-      <label class="small">Title</label><input type="text" class="input-large" ng-model='dashboard.current.title'></input>
-    </div>
-    <div class="span1"> 
-      <label class="small"> Editable </label><input type="checkbox" ng-model="dashboard.current.editable" ng-checked="dashboard.current.editable" />
-    </div>
-    <div class="span3">
-      <label class="small">Style</label><select class="input-small" ng-model="dashboard.current.style" ng-options='f for f in ["dark","light"]'></select>
+  <div ng-show="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>
+      <div class="span1">
+        <label class="small"> Editable </label><input type="checkbox" ng-model="dashboard.current.editable" ng-checked="dashboard.current.editable" />
+      </div>
+      <div class="span3">
+        <label class="small">Style</label><select class="input-small" ng-model="dashboard.current.style" ng-options='f for f in ["dark","light"]'></select>
+      </div>
     </div>
   </div>
-  <div class="row-fluid" ng-show="editor.index == 1">    
-    <h4>Index Settings</h4>
-    <div ng-show="dashboard.current.index.interval != 'none'" class="row-fluid"> 
-       <div class="span12">
-        <p class="small">
-          Time stamped indices use your selected time range to create a list of 
-          indices that match a specified timestamp pattern. This can be very 
-          efficient for some data sets (eg, logs) For example, to match the 
-          default logstash index pattern you might use 
-          <code>[logstash-]YYYY.MM.DD</code>. The [] in "[logstash-]" are 
-          important as they instruct Kibana not to treat those letters as a 
-          pattern.
-          Please also note that indices should rollover at midnight <strong>UTC</strong>.
-        </p>
-        <p class="small">
-          See <a href="http://momentjs.com/docs/#/displaying/format/">http://momentjs.com/docs/#/displaying/format/</a>
-          for documentation on date formatting.
-        </p>
 
-       </div>
+  <div ng-show="editor.index == 1">
+    <div class="row-fluid">
+      <h4>Index Settings</h4>
+      <div ng-show="dashboard.current.index.interval != 'none'" class="row-fluid">
+         <div class="span12">
+          <p class="small">
+            Time stamped indices use your selected time range to create a list of
+            indices that match a specified timestamp pattern. This can be very
+            efficient for some data sets (eg, logs) For example, to match the
+            default logstash index pattern you might use
+            <code>[logstash-]YYYY.MM.DD</code>. The [] in "[logstash-]" are
+            important as they instruct Kibana not to treat those letters as a
+            pattern.
+            Please also note that indices should rollover at midnight <strong>UTC</strong>.
+          </p>
+          <p class="small">
+            See <a href="http://momentjs.com/docs/#/displaying/format/">http://momentjs.com/docs/#/displaying/format/</a>
+            for documentation on date formatting.
+          </p>
+         </div>
+      </div>
     </div>
-    <div class="row-fluid"> 
+    <div class="row-fluid">
       <div class="span2">
         <h6>Timestamping</h6><select class="input-small" ng-model="dashboard.current.index.interval" ng-options='f for f in ["none","hour","day","week","month","year"]'></select>
       </div>
@@ -57,43 +60,90 @@
       </div>
     </div>
   </div>
-  <div class="row-fluid" ng-show="editor.index == 2">
-    <div class="span12">
-      <table class="table table-condensed table-striped">
-        <thead>
-          <th>Title</th>
-          <th>Delete</th>
-          <th>Move</th>
-        </thead>
-        <tr ng-repeat="row in dashboard.current.rows">
-          <td>{{row.title}}</td>
-          <td><i ng-click="dashboard.current.rows = _.without(dashboard.current.rows,row)" class="pointer icon-remove"></i></td>
-          <td><i ng-click="_.move(dashboard.current.rows,$index,$index-1)" ng-hide="$first" class="pointer icon-arrow-up"></i></td>
-          <td><i ng-click="_.move(dashboard.current.rows,$index,$index+1)" ng-hide="$last" class="pointer icon-arrow-down"></i></td>
-        </tr>
-      </table>
+
+  <div ng-show="editor.index == 2">
+    <div class="row-fluid">
+      <div class="span12">
+        <table class="table table-condensed table-striped">
+          <thead>
+            <th>Title</th>
+            <th>Delete</th>
+            <th>Move</th>
+          </thead>
+          <tr ng-repeat="row in dashboard.current.rows">
+            <td>{{row.title}}</td>
+            <td><i ng-click="dashboard.current.rows = _.without(dashboard.current.rows,row)" class="pointer icon-remove"></i></td>
+            <td><i ng-click="_.move(dashboard.current.rows,$index,$index-1)" ng-hide="$first" class="pointer icon-arrow-up"></i></td>
+            <td><i ng-click="_.move(dashboard.current.rows,$index,$index+1)" ng-hide="$last" class="pointer icon-arrow-down"></i></td>
+          </tr>
+        </table>
+      </div>
+    </div>
+    <div class="row-fluid">
+      <form>
+        <div class="span5">
+          <label class="small">Title</label>
+          <input type="text" class="input-large" ng-model='row.title' placeholder="New row"></input>
+        </div>
+        <div class="span2">
+          <label class="small">Height</label>
+          <input type="text" class="input-mini" ng-model='row.height'></input>
+        </div>
+        <div class="span1">
+          <label class="small"> Editable </label>
+          <input type="checkbox" ng-model="row.editable" ng-checked="row.editable" />
+        </div>
+      </form>
     </div>
   </div>
-  <div class="row-fluid" ng-show="editor.index == 2">
-    <form>
-      <div class="span5">      
-        <label class="small">Title</label>
-        <input type="text" class="input-large" ng-model='row.title' placeholder="New row"></input>
+
+  <div ng-show="editor.index == 3" ng-controller="dashLoader">
+    <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">
       </div>
       <div class="span2">
-        <label class="small">Height</label>
-        <input type="text" class="input-mini" ng-model='row.height'></input>
+        <label class="small">Browser</label><input type="checkbox" ng-model="loader.save_default" ng-checked="loader.save_default">
       </div>
-      <div class="span1">
-        <label class="small"> Editable </label>
-        <input type="checkbox" ng-model="row.editable" ng-checked="row.editable" />
+      <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">
+      </div>
+      <div class="span2">
+        <label class="small">Elasticsearch</label><input type="checkbox" ng-model="loader.save_elasticsearch" ng-checked="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">
+      </div>
+      <div class="span2">
+        <label class="small">Gist</label><input type="checkbox" ng-model="loader.load_gist" ng-checked="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">
       </div>
-    </form>
+      <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>
+    </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">
+      </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>
+      <div class="span5" ng-show="loader.save_temp && 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>
+    </div>
   </div>
-  <!--<div class="row-fluid" ng-show="editor.index == 3">
-    <h6>Region</h6><select class="input-small" ng-model="dashboard.current.time.region" ng-options='f for f in ["africa","america","asia","atlantic","australia","europe","indian","pacific"]'></select>
-  </div>-->
+
 </div>
+
 <div class="modal-footer">
   <button ng-click="add_row(dashboard.current,row); reset_row();" class="btn btn-success" ng-show="editor.index == 2">Create Row</button>
   <button type="button" class="btn btn-danger" ng-click="editor.index=0;dismiss();reset_panel();dashboard.refresh()">Close</button>

+ 3 - 3
vendor/bootstrap/less/bootswatch.dark.less

@@ -62,7 +62,7 @@ hr {
 	.nav > li.active > a:hover {
 		color: @grayLighter;
 		background-color: @grayDark;
-		#gradient > .directional(lighten(@grayDarker, 4%), lighten(@grayDark, 4%), 280deg);
+		#gradient > .directional(lighten(@grayDarker, 2%), lighten(@grayDark, 2%), 0deg);
 		border-right: 1px solid darken(@gray, 15%);
 	}
 
@@ -309,7 +309,7 @@ div.subnav {
 
 	li > a,
 	li > span {
-		
+
 		border: 1px solid transparent;
 		.box-shadow('0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)');
 		#gradient > .vertical-three-colors(@gray, @grayDark, 70%, @grayDark);
@@ -318,7 +318,7 @@ div.subnav {
 			#gradient > .directional(@grayDarker, @grayDark, 280deg);
 			border: 1px solid transparent;
 		}
-	}	
+	}
 
 	.disabled a,
 	.disabled a:hover {

+ 1 - 1
vendor/bootstrap/less/variables.light.less

@@ -186,7 +186,7 @@
 @navbarBorder:                    darken(@navbarBackground, 12%);
 
 @navbarText:                      @white;
-@navbarLinkColor:                 @textColor;
+@navbarLinkColor:                 @white;
 @navbarLinkColorHover:            @white;
 @navbarLinkColorActive:           @navbarLinkColorHover;
 @navbarLinkBackgroundHover:       @grayDark;

Неке датотеке нису приказане због велике количине промена