Browse Source

Merge branch 'pro' of github.com:torkelo/grafana-private into pro

Conflicts:
	src/css/less/grafana.less
	src/test/test-main.js
Torkel Ödegaard 11 years ago
parent
commit
c62ee78cba

+ 5 - 2
CHANGELOG.md

@@ -1,7 +1,10 @@
-# 1.8.0 (2014-09-12)
+# 1.8.0 (unreleased)
 
 
 **Fixes**
 **Fixes**
 - [Issue #802](https://github.com/grafana/grafana/issues/802). Annotations: Fix when using InfluxDB datasource
 - [Issue #802](https://github.com/grafana/grafana/issues/802). Annotations: Fix when using InfluxDB datasource
+- [Issue #795](https://github.com/grafana/grafana/issues/795). Chrome: Fix for display issue in chrome beta & chrome canary when entering edit mode
+- [Issue #818](https://github.com/grafana/grafana/issues/818). Graph: Added percent y-axis format
+- [Issue #828](https://github.com/grafana/grafana/issues/828). Elasticsearch: saving new dashboard with title equal to slugified url would cause it to deleted.
 
 
 # 1.8.0-RC1 (2014-09-12)
 # 1.8.0-RC1 (2014-09-12)
 
 
@@ -244,7 +247,7 @@ Read this for more info:
 - More graphite function definitions
 - More graphite function definitions
 - Make "ms" axis format include hour, day, weeks, month and year ([Issue #149](https://github.com/grafana/grafana/issues/149))
 - Make "ms" axis format include hour, day, weeks, month and year ([Issue #149](https://github.com/grafana/grafana/issues/149))
 - Microsecond axis format ([Issue #146](https://github.com/grafana/grafana/issues/146))
 - Microsecond axis format ([Issue #146](https://github.com/grafana/grafana/issues/146))
-- Specify template paramaters in URL ([Issue #123](https://github.com/grafana/grafana/issues/123))
+- Specify template parameters in URL ([Issue #123](https://github.com/grafana/grafana/issues/123))
 
 
 ### Fixes
 ### Fixes
 - Basic Auth fix ([Issue #152](https://github.com/grafana/grafana/issues/152))
 - Basic Auth fix ([Issue #152](https://github.com/grafana/grafana/issues/152))

+ 8 - 4
src/app/components/kbn.js

@@ -531,6 +531,10 @@ function($, _, moment) {
       return function(val) {
       return function(val) {
         return kbn.nanosFormat(val, decimals);
         return kbn.nanosFormat(val, decimals);
       };
       };
+    case 'percent':
+      return function(val, axis) {
+        return kbn.noneFormat(val, axis ? axis.tickDecimals : null) + ' %';
+      };
     default:
     default:
       return function(val, axis) {
       return function(val, axis) {
         return kbn.noneFormat(val, axis ? axis.tickDecimals : null);
         return kbn.noneFormat(val, axis ? axis.tickDecimals : null);
@@ -563,8 +567,8 @@ function($, _, moment) {
 
 
   kbn.msFormat = function(size, decimals) {
   kbn.msFormat = function(size, decimals) {
     // Less than 1 milli, downscale to micro
     // Less than 1 milli, downscale to micro
-    if (Math.abs(size) < 1) {
-      return kbn.microsFormat(size * 1000,decimals);
+    if (size !== 0 && Math.abs(size) < 1) {
+      return kbn.microsFormat(size * 1000, decimals);
     }
     }
     else if (Math.abs(size) < 1000) {
     else if (Math.abs(size) < 1000) {
       return size.toFixed(decimals) + " ms";
       return size.toFixed(decimals) + " ms";
@@ -591,7 +595,7 @@ function($, _, moment) {
 
 
   kbn.sFormat = function(size, decimals) {
   kbn.sFormat = function(size, decimals) {
     // Less than 1 sec, downscale to milli
     // Less than 1 sec, downscale to milli
-    if (Math.abs(size) < 1) {
+    if (size !== 0 && Math.abs(size) < 1) {
       return kbn.msFormat(size * 1000, decimals);
       return kbn.msFormat(size * 1000, decimals);
     }
     }
     // Less than 10 min, use seconds
     // Less than 10 min, use seconds
@@ -620,7 +624,7 @@ function($, _, moment) {
 
 
   kbn.microsFormat = function(size, decimals) {
   kbn.microsFormat = function(size, decimals) {
     // Less than 1 micro, downscale to nano
     // Less than 1 micro, downscale to nano
-    if (Math.abs(size) < 1) {
+    if (size !== 0 && Math.abs(size) < 1) {
       return kbn.nanosFormat(size * 1000, decimals);
       return kbn.nanosFormat(size * 1000, decimals);
     }
     }
     else if (Math.abs(size) < 1000) {
     else if (Math.abs(size) < 1000) {

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

@@ -1,5 +1,5 @@
 define([
 define([
-  './p_grafanaCtrl',
+  './pro/grafanaCtrl',
   './dashboardCtrl',
   './dashboardCtrl',
   './dashboardNavCtrl',
   './dashboardNavCtrl',
   './row',
   './row',

+ 2 - 2
src/app/controllers/dashboardNavCtrl.js

@@ -78,7 +78,7 @@ function (angular, _, moment, config, store) {
       var clone = angular.copy($scope.dashboard);
       var clone = angular.copy($scope.dashboard);
       $scope.db.saveDashboard(clone)
       $scope.db.saveDashboard(clone)
         .then(function(result) {
         .then(function(result) {
-          alertSrv.set('Dashboard Saved', 'Dashboard has been saved as "' + result.title + '"','success', 5000);
+          alertSrv.set('Dashboard Saved', 'Saved as "' + result.title + '"','success', 3000);
 
 
           if (result.url !== $location.path()) {
           if (result.url !== $location.path()) {
             $location.search({});
             $location.search({});
@@ -88,7 +88,7 @@ function (angular, _, moment, config, store) {
           $rootScope.$emit('dashboard-saved', $scope.dashboard);
           $rootScope.$emit('dashboard-saved', $scope.dashboard);
 
 
         }, function(err) {
         }, function(err) {
-          alertSrv.set('Save failed', err, 'error',5000);
+          alertSrv.set('Save failed', err, 'error', 5000);
         });
         });
     };
     };
 
 

+ 13 - 13
src/app/controllers/p_grafanaCtrl.js → src/app/controllers/pro/grafanaCtrl.js

@@ -11,10 +11,8 @@ function (angular, config, _, $, store) {
   var module = angular.module('grafana.controllers');
   var module = angular.module('grafana.controllers');
 
 
   module.controller('GrafanaCtrl', function($scope, alertSrv, grafanaVersion, $rootScope) {
   module.controller('GrafanaCtrl', function($scope, alertSrv, grafanaVersion, $rootScope) {
-
     $scope.grafanaVersion = grafanaVersion[0] === '@' ? 'master' : grafanaVersion;
     $scope.grafanaVersion = grafanaVersion[0] === '@' ? 'master' : grafanaVersion;
-    $scope.consoleEnabled = store.getBool('grafanaConsole');
-    //$scope.showProSideMenu = store.getBool('grafanaProSideMenu');
+    $scope.grafana = {};
 
 
     $rootScope.profilingEnabled = store.getBool('profilingEnabled');
     $rootScope.profilingEnabled = store.getBool('profilingEnabled');
     $rootScope.performance = { loadStart: new Date().getTime() };
     $rootScope.performance = { loadStart: new Date().getTime() };
@@ -25,25 +23,27 @@ function (angular, config, _, $, store) {
       if ($rootScope.profilingEnabled) { $scope.initProfiling(); }
       if ($rootScope.profilingEnabled) { $scope.initProfiling(); }
 
 
       $scope.dashAlerts = alertSrv;
       $scope.dashAlerts = alertSrv;
-      $scope.grafana = { style: 'dark' };
+      $scope.grafana.style = 'dark';
+      $scope.grafana.sidemenu = store.getBool('grafana.sidemenu');
+
+      if (window.grafanaBootData.user.login) {
+        $scope.grafana.user = window.grafanaBootData.user;
+      }
 
 
       $scope.onAppEvent('logged-out', function() {
       $scope.onAppEvent('logged-out', function() {
         $scope.showProSideMenu = false;
         $scope.showProSideMenu = false;
+        $scope.grafana.user = {};
       });
       });
 
 
-      $scope.onAppEvent('logged-in', function() {
-        $scope.showProSideMenu = store.getBool('grafanaProSideMenu');
+      $scope.onAppEvent('logged-in', function(evt, user) {
+        $scope.grafana.sidemenu = store.getBool('grafana.sidemenu');
+        $scope.grafana.user = user;
       });
       });
     };
     };
 
 
     $scope.toggleProSideMenu = function() {
     $scope.toggleProSideMenu = function() {
-      $scope.showProSideMenu = !$scope.showProSideMenu;
-      store.set('grafanaProSideMenu', $scope.showProSideMenu);
-    };
-
-    $scope.toggleConsole = function() {
-      $scope.consoleEnabled = !$scope.consoleEnabled;
-      store.set('grafanaConsole', $scope.consoleEnabled);
+      $scope.grafana.sidemenu = !$scope.grafana.sidemenu;
+      store.set('grafana.sidemenu', $scope.grafana.sidemenu);
     };
     };
 
 
     $rootScope.onAppEvent = function(name, callback) {
     $rootScope.onAppEvent = function(name, callback) {

+ 4 - 2
src/app/controllers/p_loginCtrl.js → src/app/controllers/pro/loginCtrl.js

@@ -8,6 +8,7 @@ function (angular) {
 
 
   module.controller('LoginCtrl', function($scope, $http, $location, $routeParams, alertSrv) {
   module.controller('LoginCtrl', function($scope, $http, $location, $routeParams, alertSrv) {
     $scope.loginModel = {};
     $scope.loginModel = {};
+    $scope.grafana.sidemenu = false;
 
 
     $scope.init = function() {
     $scope.init = function() {
       if ($routeParams.logout) {
       if ($routeParams.logout) {
@@ -20,6 +21,7 @@ function (angular) {
 
 
         alertSrv.set('Logged out!', '', 'success', 3000);
         alertSrv.set('Logged out!', '', 'success', 3000);
         $scope.emitAppEvent('logged-out');
         $scope.emitAppEvent('logged-out');
+        $location.search({});
 
 
       }, function() {
       }, function() {
         alertSrv.set('Logout failed:', 'Unexpected error', 'error', 3000);
         alertSrv.set('Logout failed:', 'Unexpected error', 'error', 3000);
@@ -33,8 +35,8 @@ function (angular) {
         return;
         return;
       }
       }
 
 
-      $http.post('/login', $scope.loginModel).then(function() {
-        $scope.emitAppEvent('logged-in');
+      $http.post('/login', $scope.loginModel).then(function(results) {
+        $scope.emitAppEvent('logged-in', results.data.user);
         $location.path('/');
         $location.path('/');
       }, function(err) {
       }, function(err) {
         if (err.status === 401) {
         if (err.status === 401) {

+ 3 - 2
src/app/controllers/search.js

@@ -119,9 +119,10 @@ function (angular, _, config, $) {
       $scope.searchDashboards($scope.query.query);
       $scope.searchDashboards($scope.query.query);
     };
     };
 
 
-    $scope.deleteDashboard = function(id, evt) {
+    $scope.deleteDashboard = function(dash, evt) {
       evt.stopPropagation();
       evt.stopPropagation();
-      $scope.emitAppEvent('delete-dashboard', { id: id });
+      $scope.emitAppEvent('delete-dashboard', { id: dash.id });
+      $scope.results.dashboards = _.without($scope.results.dashboards, dash);
     };
     };
 
 
     $scope.addMetricToCurrentDashboard = function (metricId) {
     $scope.addMetricToCurrentDashboard = function (metricId) {

+ 1 - 1
src/app/dashboards/default.json

@@ -17,7 +17,7 @@
           "editable": true,
           "editable": true,
           "type": "text",
           "type": "text",
           "mode": "html",
           "mode": "html",
-          "content": "<div class=\"text-center\" style=\"padding-top: 15px\">\n<img src=\"http://grafana.org/assets/img/logo_transparent_200x75.png\"> \n</div>",
+          "content": "<div class=\"text-center\" style=\"padding-top: 15px\">\n<img src=\"//grafana.org/assets/img/logo_transparent_200x75.png\"> \n</div>",
           "style": {},
           "style": {},
           "title": "Welcome to"
           "title": "Welcome to"
         }
         }

+ 5 - 0
src/app/directives/grafanaGraph.js

@@ -110,6 +110,11 @@ function (angular, $, kbn, moment, _) {
 
 
           // Populate element
           // Populate element
           var options = {
           var options = {
+            hooks: {
+              drawSeries: [function() {
+                console.log('drawSeries', arguments);
+              }]
+            },
             legend: { show: false },
             legend: { show: false },
             series: {
             series: {
               stackpercent: panel.stack ? panel.percentage : false,
               stackpercent: panel.stack ? panel.percentage : false,

+ 2 - 2
src/app/panels/graph/axisEditor.html

@@ -4,7 +4,7 @@
     <h5>Left Y Axis</h5>
     <h5>Left Y Axis</h5>
        <div class="editor-option">
        <div class="editor-option">
         <label class="small">Format <tip>Y-axis formatting</tip></label>
         <label class="small">Format <tip>Y-axis formatting</tip></label>
-        <select class="input-small" ng-model="panel.y_formats[0]" ng-options="f for f in ['none','short','bytes', 'bits', 'bps', 's', 'ms', 'µs', 'ns']" ng-change="render()"></select>
+        <select class="input-small" ng-model="panel.y_formats[0]" ng-options="f for f in ['none','short','bytes', 'bits', 'bps', 's', 'ms', 'µs', 'ns', 'percent']" ng-change="render()"></select>
       </div>
       </div>
       <div class="editor-option">
       <div class="editor-option">
         <label class="small">Min / <a ng-click="toggleGridMinMax('leftMin')">Auto <i class="icon-star" ng-show="_.isNull(panel.grid.leftMin)"></i></a></label>
         <label class="small">Min / <a ng-click="toggleGridMinMax('leftMin')">Auto <i class="icon-star" ng-show="_.isNull(panel.grid.leftMin)"></i></a></label>
@@ -23,7 +23,7 @@
     <h5>Right Y Axis</h5>
     <h5>Right Y Axis</h5>
        <div class="editor-option">
        <div class="editor-option">
         <label class="small">Format <tip>Y-axis formatting</tip></label>
         <label class="small">Format <tip>Y-axis formatting</tip></label>
-        <select class="input-small" ng-model="panel.y_formats[1]" ng-options="f for f in ['none','short','bytes', 'bits', 'bps', 's', 'ms', 'µs', 'ns']" ng-change="render()"></select>
+        <select class="input-small" ng-model="panel.y_formats[1]" ng-options="f for f in ['none','short','bytes', 'bits', 'bps', 's', 'ms', 'µs', 'ns', 'percent']" ng-change="render()"></select>
       </div>
       </div>
       <div class="editor-option">
       <div class="editor-option">
         <label class="small">Min / <a ng-click="toggleGridMinMax('rightMin')">Auto <i class="icon-star" ng-show="_.isNull(panel.grid.rightMin)"></i></a></label>
         <label class="small">Min / <a ng-click="toggleGridMinMax('rightMin')">Auto <i class="icon-star" ng-show="_.isNull(panel.grid.rightMin)"></i></a></label>

+ 1 - 1
src/app/panels/graph/seriesOverridesCtrl.js

@@ -70,7 +70,7 @@ define([
     $scope.addOverrideOption('Staircase line', 'steppedLine', [true, false]);
     $scope.addOverrideOption('Staircase line', 'steppedLine', [true, false]);
     $scope.addOverrideOption('Points', 'points', [true, false]);
     $scope.addOverrideOption('Points', 'points', [true, false]);
     $scope.addOverrideOption('Points Radius', 'pointradius', [1,2,3,4,5]);
     $scope.addOverrideOption('Points Radius', 'pointradius', [1,2,3,4,5]);
-    $scope.addOverrideOption('Stack', 'stack', [true, false]);
+    $scope.addOverrideOption('Stack', 'stack', [true, false, 2, 3, 4, 5]);
     $scope.addOverrideOption('Y-axis', 'yaxis', [1, 2]);
     $scope.addOverrideOption('Y-axis', 'yaxis', [1, 2]);
     $scope.addOverrideOption('Z-index', 'zindex', [-1,-2,-3,0,1,2,3]);
     $scope.addOverrideOption('Z-index', 'zindex', [-1,-2,-3,0,1,2,3]);
     $scope.updateCurrentOverrides();
     $scope.updateCurrentOverrides();

+ 4 - 1
src/app/partials/dashboard_topnav.html

@@ -1,7 +1,10 @@
 <div class="navbar navbar-static-top">
 <div class="navbar navbar-static-top">
 	<div class="navbar-inner">
 	<div class="navbar-inner">
 		<div class="container-fluid">
 		<div class="container-fluid">
-			<span class="brand"><img src="img/small.png" bs-tooltip="'Grafana'" data-placement="bottom"> {{dashboard.title}}</span>
+			<span class="brand">
+				<img class="logo-icon" src="img/fav32.png" bs-tooltip="'Grafana'" data-placement="bottom"></img>
+				<span class="page-title">{{dashboard.title}}</span>
+			</span>
 			<ul class="nav pull-right" ng-controller='DashboardNavCtrl' ng-init="init()">
 			<ul class="nav pull-right" ng-controller='DashboardNavCtrl' ng-init="init()">
 
 
 				<li ng-show="dashboardViewState.fullscreen">
 				<li ng-show="dashboardViewState.fullscreen">

+ 1 - 1
src/app/partials/graphite/editor.html

@@ -108,7 +108,7 @@
 				</li>
 				</li>
 				<li class="grafana-target-segment">
 				<li class="grafana-target-segment">
 					<a ng-click="toggleEditorHelp(2);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
 					<a ng-click="toggleEditorHelp(2);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
-						series as paramaters
+						series as parameters
 					</a>
 					</a>
 				</li>
 				</li>
 				<li class="grafana-target-segment">
 				<li class="grafana-target-segment">

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

@@ -38,7 +38,7 @@
 				<div class="editor-option">
 				<div class="editor-option">
 					<div class="span4">
 					<div class="span4">
 						<span><i class="icon-question-sign"></i>
 						<span><i class="icon-question-sign"></i>
-							dashboards available in the playlist are only the once marked as favorites (stored in local browser storage).
+							dashboards available in the playlist are only the ones marked as favorites (stored in local browser storage).
 							to mark a dashboard as favorite, use save icon in the menu and in the dropdown select mark as favorite
 							to mark a dashboard as favorite, use save icon in the menu and in the dropdown select mark as favorite
 							<br/><br/>
 							<br/><br/>
 						</span>
 						</span>

+ 73 - 0
src/app/partials/pro/account.html

@@ -0,0 +1,73 @@
+<div class="navbar navbar-static-top">
+	<div class="navbar-inner">
+		<div class="container-fluid">
+			<span class="brand">
+				<a ng-click="toggleProSideMenu()">
+				  <img class="logo-icon" src="img/fav32.png" bs-tooltip="'Grafana'" data-placement="bottom"></img>
+				</a>
+				<span class="page-title">Account Settings</span>
+			</span>
+		</div>
+	</div>
+</div>
+
+<div class="dashboard-edit-view">
+
+	<div class="dashboard-editor-header">
+		<div class="dashboard-editor-title">
+			<i class="icon icon-user"></i>
+			Personal information
+		</div>
+	</div>
+
+	<div class="dashboard-editor-body">
+		<div class="editor-row">
+		  <div class="editor-option">
+				<label class="small">Name</label>
+				<input type="text" class="input-xxlarge" ng-model='currentAnnotation.name' placeholder="name"></input>
+			</div>
+		</div>
+
+	  <div class="editor-row">
+		  <div class="editor-option">
+				<label class="small">Email</label>
+				<input type="text" class="input-xxlarge" ng-model='currentAnnotation.name' placeholder="name"></input>
+			</div>
+		</div>
+
+	  <div class="editor-row">
+		  <div class="editor-option">
+				<label class="small">Account Name</label>
+				<input type="text" class="input-xxlarge" ng-model='currentAnnotation.name' placeholder="name"></input>
+			</div>
+		</div>
+
+	</div>
+
+	<div class="dashboard-editor-footer">
+		<button class="btn btn-success">Update</button>
+	</div>
+</div>
+
+<div class="dashboard-edit-view" style="border-top: solid 1px black;">
+
+	<div class="dashboard-editor-header">
+		<div class="dashboard-editor-title">
+			<i class="icon icon-eye-open"></i>
+			Collaborators
+		</div>
+	</div>
+
+	<div class="dashboard-editor-body">
+		<div class="editor-row">
+			<div class="editor-option">
+				<form name="addCollaboratorForm" class="form-inline">
+					<label class="small">Add a collaborator</label>
+					<input type="text" class="input-xxlarge" ng-model='collaborator.email' placeholder="collaborator@email.com"></input>
+					<button class="btn btn-success" ng-click="addCollaborator()">Add</button>
+				</form>
+			</div>
+		</div>
+	</div>
+</div>
+

+ 2 - 2
src/app/partials/pro/dashboard_topnav.html

@@ -3,9 +3,9 @@
 		<div class="container-fluid">
 		<div class="container-fluid">
 			<span class="brand">
 			<span class="brand">
 				<a ng-click="toggleProSideMenu()">
 				<a ng-click="toggleProSideMenu()">
-					<img src="img/small.png">
+				  <img class="logo-icon" src="img/fav32.png" bs-tooltip="'Grafana'" data-placement="bottom"></img>
 				</a>
 				</a>
-				{{dashboard.title}}
+				<span class="page-title">{{dashboard.title}}</span>
 			</span>
 			</span>
 			<ul class="nav pull-right" ng-controller='DashboardNavCtrl' ng-init="init()">
 			<ul class="nav pull-right" ng-controller='DashboardNavCtrl' ng-init="init()">
 
 

+ 16 - 19
src/app/partials/pro/sidemenu.html

@@ -1,22 +1,14 @@
-<div class="navbar navbar-static-top">
-	<div class="navbar-inner">
-		<div class="container-fluid">
-			<span class="brand">
-				<i class="icon-gears" style=""></i>
-				<span style="color: white; padding-left: 4px;">Grafana</span>
-				<ul class="nav" ng-controller='DashboardNavCtrl' ng-init="init()">
-			</span>
-			</ul>
-		</div>
-	</div>
-</div>
-
 <section class="pro-sidemenu-items">
 <section class="pro-sidemenu-items">
-	<a class="pro-sidemenu-link pro-side-menu-user" href="/login?logout">
-		<img  src="https://secure.gravatar.com/avatar/c8656e8972626f01e1703681d5e55f92?s=90&default=blank">
-		logout
-	</a>
-	<a class="pro-sidemenu-link" href="/dashboard/db/home">
+	<div class="dropdown">
+		<a class="pro-sidemenu-link pointer" data-toggle="dropdown">
+			<i class="icon-user"></i>
+			{{grafana.user.login}}
+		</a>
+		<ul class="dropdown-menu">
+			<li><a href="/login?logout">Logout</a>
+		</ul>
+	</div>
+	<a class="pro-sidemenu-link" href="/">
 		<i class="icon-th-large"></i>
 		<i class="icon-th-large"></i>
 		Dashboards
 		Dashboards
 	</a>
 	</a>
@@ -36,10 +28,15 @@
 		<i class="icon-tasks"></i>
 		<i class="icon-tasks"></i>
 		Global options
 		Global options
 	</a>
 	</a>
-	<a class="pro-sidemenu-link" href="/admin">
+	<a class="pro-sidemenu-link" href="/account">
 		<i class="icon-user"></i>
 		<i class="icon-user"></i>
 		User accounts
 		User accounts
 	</a>
 	</a>
+	<a class="pro-sidemenu-link" href="/login?logout">
+		<i class="icon-signout"></i>
+		Sign out
+	</a>
+
 </section>
 </section>
 
 
 </div>
 </div>

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

@@ -54,7 +54,7 @@
 					<a ng-click="shareDashboard(row.id, row.id, $event)" config-modal="app/partials/dashLoaderShare.html">
 					<a ng-click="shareDashboard(row.id, row.id, $event)" config-modal="app/partials/dashLoaderShare.html">
 						<i class="icon-share"></i> share &nbsp;&nbsp;&nbsp;
 						<i class="icon-share"></i> share &nbsp;&nbsp;&nbsp;
 					</a>
 					</a>
-					<a ng-click="deleteDashboard(row.id, $event)">
+					<a ng-click="deleteDashboard(row, $event)">
 						<i class="icon-remove"></i> delete
 						<i class="icon-remove"></i> delete
 					</a>
 					</a>
 				</div>
 				</div>

+ 32 - 0
src/app/routes/p_admin.js

@@ -14,8 +14,40 @@ function (angular) {
       });
       });
   });
   });
 
 
+  module.config(function($routeProvider) {
+    $routeProvider
+      .when('/account', {
+        templateUrl: 'app/partials/pro/account.html',
+        controller : 'AccountCtrl',
+      });
+  });
+
   module.controller('AdminCtrl', function() {
   module.controller('AdminCtrl', function() {
 
 
   });
   });
 
 
+  module.controller('AccountCtrl', function($scope, $http, alertSrv) {
+
+    $scope.collaborator = {};
+
+    $scope.addCollaborator = function() {
+      if (!$scope.addCollaboratorForm.$valid) {
+        return;
+      }
+
+      $http.post('/api/account/collaborators/add', $scope.collaborator).then(function() {
+        alertSrv.set('Collaborator added', '', 'success', 3000);
+      }, function(err) {
+        if (err.data && err.data.status) {
+          alertSrv.set('Could not add collaborator', err.data.status, 'warning', 10000);
+        }
+        else if (err.statusText) {
+          alertSrv.set('Could not add collaborator', err.data.status, 'warning', 10000);
+        }
+        console.log("value", err);
+      });
+    };
+
+  });
+
 });
 });

+ 21 - 3
src/app/routes/p_dashboard.js

@@ -1,7 +1,8 @@
 define([
 define([
   'angular',
   'angular',
+  'store',
 ],
 ],
-function (angular) {
+function (angular, store) {
   "use strict";
   "use strict";
 
 
   var module = angular.module('grafana.routes');
   var module = angular.module('grafana.routes');
@@ -25,12 +26,29 @@ function (angular) {
       });
       });
   });
   });
 
 
-  module.controller('DashFromDBProvider', function($scope, $rootScope, datasourceSrv, $routeParams, alertSrv) {
+  module.controller('DashFromDBProvider', function(
+        $scope, $rootScope, datasourceSrv, $routeParams,
+        alertSrv, $http, $location) {
 
 
     var db = datasourceSrv.getGrafanaDB();
     var db = datasourceSrv.getGrafanaDB();
     var isTemp = window.location.href.indexOf('dashboard/temp') !== -1;
     var isTemp = window.location.href.indexOf('dashboard/temp') !== -1;
+
     if (!$routeParams.id) {
     if (!$routeParams.id) {
-      $routeParams.id = 'default';
+      var savedRoute = store.get('grafanaDashboardDefault');
+
+      if (!savedRoute) {
+        $http.get("app/dashboards/default.json?" + new Date().getTime()).then(function(result) {
+          var dashboard = angular.fromJson(result.data);
+          $scope.emitAppEvent('setup-dashboard', dashboard);
+        },function() {
+          alertSrv.set('Error',"Could not load default dashboard", 'error');
+        });
+        return;
+      }
+      else {
+        $location.path(savedRoute);
+        return;
+      }
     }
     }
 
 
     db.getDashboard($routeParams.id, isTemp)
     db.getDashboard($routeParams.id, isTemp)

+ 3 - 52
src/app/routes/p_login.js

@@ -1,5 +1,6 @@
 define([
 define([
   'angular',
   'angular',
+  '../controllers/pro/loginCtrl',
 ],
 ],
 function (angular) {
 function (angular) {
   "use strict";
   "use strict";
@@ -18,14 +19,9 @@ function (angular) {
       });
       });
   });
   });
 
 
-  module.controller('RegisterCtrl', function($scope, $http, $location, $routeParams) {
+  module.controller('RegisterCtrl', function($scope, $http, $location) {
     $scope.loginModel = {};
     $scope.loginModel = {};
-
-    $scope.init = function() {
-      if ($routeParams.logout) {
-        $scope.logout();
-      }
-    };
+    $scope.grafana.sidemenu = false;
 
 
     $scope.register = function() {
     $scope.register = function() {
       delete $scope.registerError;
       delete $scope.registerError;
@@ -43,51 +39,6 @@ function (angular) {
       });
       });
     };
     };
 
 
-    $scope.init();
-
-  });
-
-
-  module.controller('LoginCtrl', function($scope, $http, $location, $routeParams, alertSrv) {
-    $scope.loginModel = {};
-
-    $scope.init = function() {
-      if ($routeParams.logout) {
-        $scope.logout();
-      }
-    };
-
-    $scope.logout = function() {
-      $http.post('/logout').then(function() {
-        alertSrv.set('Logged out!', '', 'success', 3000);
-        $scope.emitAppEvent('logged-out');
-      }, function() {
-        alertSrv.set('Logout failed:', 'Unexpected error', 'error', 3000);
-      });
-    };
-
-    $scope.login = function() {
-      delete $scope.loginError;
-
-      if (!$scope.loginForm.$valid) {
-        return;
-      }
-
-      $http.post('/login', $scope.loginModel).then(function() {
-        $scope.emitAppEvent('logged-in');
-        $location.path('/');
-      }, function(err) {
-        if (err.status === 401) {
-          $scope.loginError = "Username or password is incorrect";
-        }
-        else {
-          $scope.loginError = "Unexpected error";
-        }
-      });
-    };
-
-    $scope.init();
-
   });
   });
 
 
 });
 });

+ 1 - 1
src/app/services/dashboard/dashboardSrv.js

@@ -131,7 +131,7 @@ function (angular, $, kbn, _, moment) {
         if (old.services) {
         if (old.services) {
           if (old.services.filter) {
           if (old.services.filter) {
             this.time = old.services.filter.time;
             this.time = old.services.filter.time;
-            this.templating.list = old.services.filter.list;
+            this.templating.list = old.services.filter.list || [];
           }
           }
           delete this.services;
           delete this.services;
         }
         }

+ 4 - 2
src/app/services/elasticsearch/es-datasource.js

@@ -37,6 +37,7 @@ function (angular, _, config, kbn, moment) {
       };
       };
 
 
       if (this.basicAuth) {
       if (this.basicAuth) {
+        options.withCredentials = true;
         options.headers = {
         options.headers = {
           "Authorization": "Basic " + this.basicAuth
           "Authorization": "Basic " + this.basicAuth
         };
         };
@@ -168,7 +169,7 @@ function (angular, _, config, kbn, moment) {
 
 
         return this._request('PUT', '/dashboard/' + id, this.index, data)
         return this._request('PUT', '/dashboard/' + id, this.index, data)
           .then(function(results) {
           .then(function(results) {
-            self._removeUnslugifiedDashboard(results, title);
+            self._removeUnslugifiedDashboard(results, title, id);
             return { title: title, url: '/dashboard/db/' + id };
             return { title: title, url: '/dashboard/db/' + id };
           }, function() {
           }, function() {
             throw 'Failed to save to elasticsearch';
             throw 'Failed to save to elasticsearch';
@@ -176,8 +177,9 @@ function (angular, _, config, kbn, moment) {
       }
       }
     };
     };
 
 
-    ElasticDatasource.prototype._removeUnslugifiedDashboard = function(saveResult, title) {
+    ElasticDatasource.prototype._removeUnslugifiedDashboard = function(saveResult, title, id) {
       if (saveResult.statusText !== 'Created') { return; }
       if (saveResult.statusText !== 'Created') { return; }
+      if (title === id) { return; }
 
 
       var self = this;
       var self = this;
       this._get('/dashboard/' + title).then(function() {
       this._get('/dashboard/' + title).then(function() {

+ 9 - 0
src/app/services/grafana/grafanaDatasource.js

@@ -55,6 +55,15 @@ function (angular) {
         });
         });
     };
     };
 
 
+    GrafanaDatasource.prototype.deleteDashboard = function(id) {
+      return $http({ method: 'DELETE', url: '/api/dashboard/' + id })
+        .then(function(result) {
+          return result.data.title;
+        }, function(err) {
+          throw err.data;
+        });
+    };
+
     GrafanaDatasource.prototype.searchDashboards = function(query) {
     GrafanaDatasource.prototype.searchDashboards = function(query) {
       return $http.get('/api/search/', { params: { q: query } })
       return $http.get('/api/search/', { params: { q: query } })
         .then(function(results) {
         .then(function(results) {

+ 7 - 0
src/app/services/graphite/gfunc.js

@@ -86,6 +86,13 @@ function (_) {
     category: categories.Calculate,
     category: categories.Calculate,
   });
   });
 
 
+  addFuncDef({
+    name: 'group',
+    params: optionalSeriesRefArgs,
+    defaultParams: ['#A', '#B'],
+    category: categories.Combine,
+  });
+
   addFuncDef({
   addFuncDef({
     name: 'sumSeries',
     name: 'sumSeries',
     shortName: 'sum',
     shortName: 'sum',

+ 1 - 1
src/css/less/bootswatch.dark.less

@@ -56,7 +56,7 @@ hr {
 	}
 	}
 
 
 	.brand {
 	.brand {
-		padding: 15px 20px 15px;
+		padding: 0px 15px;
 		color: @grayLighter;
 		color: @grayLighter;
 		font-weight: normal;
 		font-weight: normal;
 		text-shadow: none;
 		text-shadow: none;

+ 1 - 1
src/css/less/bootswatch.light.less

@@ -59,6 +59,7 @@ a.text-success:hover { color: darken(@green, 10%); }
 	}
 	}
 
 
 	.brand {
 	.brand {
+		padding: 0px 15px;
 
 
 		&:hover {
 		&:hover {
 			color: @navbarLinkColorHover;
 			color: @navbarLinkColorHover;
@@ -461,7 +462,6 @@ legend {
 // -----------------------------------------------------
 // -----------------------------------------------------
 
 
 .alert {
 .alert {
-	.border-radius(0);
 	text-shadow: none;
 	text-shadow: none;
 
 
 	&-heading, h1, h2, h3, h4, h5, h6 {
 	&-heading, h1, h2, h3, h4, h5, h6 {

+ 21 - 6
src/css/less/grafana.less

@@ -3,6 +3,9 @@
 @import "graph.less";
 @import "graph.less";
 @import "console.less";
 @import "console.less";
 @import "bootstrap-tagsinput.less";
 @import "bootstrap-tagsinput.less";
+@import "tables_lists.less";
+@import "search.less";
+@import "panel.less";
 
 
 .hide-controls {
 .hide-controls {
   padding: 0;
   padding: 0;
@@ -33,6 +36,19 @@
   }
   }
 }
 }
 
 
+.logo-icon {
+  width: 24px;
+  padding: 13px 11px 0 0;
+  display: block;
+  float: left;
+}
+
+.page-title {
+ padding: 15px 0;
+ display: block;
+ float: left;
+}
+
 .row-button {
 .row-button {
   width: 24px;
   width: 24px;
 }
 }
@@ -85,7 +101,7 @@
 
 
 .panel-fullscreen {
 .panel-fullscreen {
   z-index: 100;
   z-index: 100;
-  display: block !important;
+  display: block;
   position: fixed;
   position: fixed;
   left: 0px;
   left: 0px;
   right: 0px;
   right: 0px;
@@ -101,11 +117,10 @@
   }
   }
 }
 }
 
 
-.dashboard-fullscreen .main-view-container {
-  height: 0px;
-  width: 0px;
-  position: fixed;
-  right: -10000px;
+.dashboard-fullscreen {
+  .row-control-inner {
+    display: none;
+  }
 }
 }
 
 
 .histogram-chart {
 .histogram-chart {

+ 31 - 6
src/css/less/overrides.less

@@ -317,13 +317,38 @@ div.flot-text {
   color: @textColor !important;
   color: @textColor !important;
 }
 }
 
 
-.dashboard-notice {
+.page-alert-list {
   z-index:8000;
   z-index:8000;
-  margin-left:0px;
-  padding:3px 0px 3px 0px;
-  width:100%;
-  padding-left:20px;
-  color: @white;
+  min-width: 300px;
+  max-width: 300px;
+  position: fixed;
+  right: 20px;
+  top: 56px;
+
+  .alert {
+    color: @white;
+    padding-bottom: 13px;
+    position: relative;
+  }
+
+  .alert-close {
+    position: absolute;
+    top: -4px;
+    right: -2px;
+    width: 19px;
+    height: 19px;
+    padding: 0;
+    background: @grayLighter;
+    border-radius: 50%;
+    border: none;
+    font-size: 1.1rem;
+    color: @grayDarker;
+  }
+
+  .alert-title {
+    font-weight: bold;
+    padding-bottom: 2px;
+  }
 }
 }
 
 
 
 

BIN
src/img/fav16.png


BIN
src/img/fav32.png


BIN
src/img/fav_dark_16.png


BIN
src/img/fav_dark_32.png


+ 13 - 6
src/index.html

@@ -5,9 +5,11 @@
     <meta charset="utf-8">
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
     <meta name="viewport" content="width=device-width">
     <meta name="viewport" content="width=device-width">
+    <meta name="google" value="notranslate">
 
 
     <title>Grafana</title>
     <title>Grafana</title>
     <link rel="stylesheet" href="css/grafana.dark.min.css" title="Dark">
     <link rel="stylesheet" href="css/grafana.dark.min.css" title="Dark">
+    <link rel="icon" type="image/png" href="img/fav32.png">
 
 
     <!-- build:js app/app.js -->
     <!-- build:js app/app.js -->
     <script src="vendor/require/require.js"></script>
     <script src="vendor/require/require.js"></script>
@@ -22,12 +24,17 @@
 
 
     <link rel="stylesheet" href="css/grafana.light.min.css" ng-if="grafana.style === 'light'">
     <link rel="stylesheet" href="css/grafana.light.min.css" ng-if="grafana.style === 'light'">
 
 
-    <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='alert.text'></span> <div style="padding-right:10px" class='pull-right small'> {{$index + 1}} alert(s) </div>
-    </div>
+		<div class="page-alert-list">
+			<div ng-repeat='alert in dashAlerts.list' class="alert-{{alert.severity}} alert">
+				<button type="button" class="alert-close" ng-click="dashAlerts.clear(alert)">
+					<i class="icon-remove-sign"></i>
+				</button>
+				<div class="alert-title">{{alert.title}}</div>
+				<div ng-bind-html='alert.text'></div>
+			</div>
+		</div>
 
 
-    <div ng-view></div>
+		<div ng-view></div>
 
 
-  </body>
+	</body>
 </html>
 </html>

+ 6 - 0
src/test/specs/kbn-format-specs.js

@@ -15,6 +15,12 @@ define([
       expect(str).to.be('1.02 hour');
       expect(str).to.be('1.02 hour');
     });
     });
 
 
+    it('should not downscale when value is zero', function() {
+      var str = kbn.msFormat(0, 2);
+      expect(str).to.be('0.00 ms');
+    });
+
+
     it('should translate 365445 as 6.09 min', function() {
     it('should translate 365445 as 6.09 min', function() {
       var str = kbn.msFormat(365445, 2);
       var str = kbn.msFormat(365445, 2);
       expect(str).to.be('6.09 min');
       expect(str).to.be('6.09 min');

+ 1 - 0
src/test/test-main.js

@@ -135,6 +135,7 @@ require([
     'specs/kbn-format-specs',
     'specs/kbn-format-specs',
     'specs/dashboardSrv-specs',
     'specs/dashboardSrv-specs',
     'specs/dashboardViewStateSrv-specs',
     'specs/dashboardViewStateSrv-specs',
+    'specs/overview-ctrl-specs',
   ], function () {
   ], function () {
     window.__karma__.start();
     window.__karma__.start();
   });
   });