diff --git a/CHANGELOG.md b/CHANGELOG.md index aa3114b39af..5d5721604c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ **New features** - [Issue #1331](https://github.com/grafana/grafana/issues/1331). Graph & Singlestat: New axis/unit format selector and more units (kbytes, Joule, Watt, eV), and new design for graph axis & grid tab and single stat options tab views +- [Issue #1241](https://github.com/grafana/grafana/issues/1242). Timepicker: New option in timepicker (under dashboard settings), to change ``now`` to be for example ``now-1m``, usefull when you want to ignore last minute because it contains incomplete data +- [Issue #171](https://github.com/grafana/grafana/issues/171). Panel: Different time periods, panels can override dashboard relative time and/or add a time shift **Enhancements** - [Issue #1297](https://github.com/grafana/grafana/issues/1297). Graphite: Added cumulative and minimumBelow graphite functions @@ -13,6 +15,9 @@ - [Issue #1298](https://github.com/grafana/grafana/issues/1298). InfluxDB: Fix handling of empty array in templating variable query - [Issue #1309](https://github.com/grafana/grafana/issues/1309). Graph: Fixed issue when using zero as a grid threshold - [Issue #1345](https://github.com/grafana/grafana/issues/1345). UI: Fixed position of confirm modal when scrolled down +- [Issue #1372](https://github.com/grafana/grafana/issues/1372). Graphite: Fix for nested complex queries, where a query references a query that references another query (ie the #[A-Z] syntax) +- [Issue #1363](https://github.com/grafana/grafana/issues/1363). Templating: Fix to allow custom template variables to contain white space, now only splits on ',' +- [Issue #1359](https://github.com/grafana/grafana/issues/1359). Graph: Fix for all series tooltip showing series with all null values when ``Hide Empty`` option is enabled **Tech** - [Issue #1311](https://github.com/grafana/grafana/issues/1311). Tech: Updated Font-Awesome from 3.2 to 4.2 diff --git a/src/app/controllers/all.js b/src/app/controllers/all.js index ef6f473e34e..bf5c8164397 100644 --- a/src/app/controllers/all.js +++ b/src/app/controllers/all.js @@ -7,5 +7,6 @@ define([ './inspectCtrl', './jsonEditorCtrl', './loginCtrl', + './sidemenuCtrl', './errorCtrl', ], function () {}); diff --git a/src/app/controllers/loginCtrl.js b/src/app/controllers/loginCtrl.js index 9cc28d75ddb..45d24987ca1 100644 --- a/src/app/controllers/loginCtrl.js +++ b/src/app/controllers/loginCtrl.js @@ -8,15 +8,27 @@ function (angular, config) { var module = angular.module('grafana.controllers'); module.controller('LoginCtrl', function($scope, backendSrv, $location, $routeParams, alertSrv) { - $scope.loginModel = { + + $scope.formModel = { user: '', - password: '' + email: '', + password: '', }; - $scope.newUser = {}; - $scope.grafana.sidemenu = false; - $scope.mode = 'login'; + $scope.loginMode = true; + $scope.submitBtnClass = 'btn-inverse'; + $scope.submitBtnText = 'Log in'; + $scope.strengthClass = ''; + + $scope.init = function() { + if ($routeParams.logout) { + $scope.logout(); + } + + $scope.$watch("loginMode", $scope.loginModeChanged); + $scope.passwordChanged(); + }; // build info view model $scope.buildInfo = { @@ -26,30 +38,44 @@ function (angular, config) { }; $scope.submit = function() { - if ($scope.mode === 'login') { + if ($scope.loginMode) { $scope.login(); } else { $scope.signUp(); } }; - $scope.init = function() { - if ($routeParams.logout) { - $scope.logout(); - } + $scope.loginModeChanged = function(newValue) { + $scope.submitBtnText = newValue ? 'Log in' : 'Sign up'; }; - $scope.signUp = function() { - if ($scope.mode === 'login') { - $scope.mode = 'signup'; + $scope.passwordChanged = function(newValue) { + if (!newValue) { + $scope.strengthText = ""; + $scope.strengthClass = "hidden"; + return; + } + if (newValue.length < 4) { + $scope.strengthText = "strength: weak sauce."; + $scope.strengthClass = "password-strength-bad"; + return; + } + if (newValue.length <= 6) { + $scope.strengthText = "strength: you can do better."; + $scope.strengthClass = "password-strength-ok"; return; } + $scope.strengthText = "strength: strong like a bull."; + $scope.strengthClass = "password-strength-good"; + }; + + $scope.signUp = function() { if (!$scope.loginForm.$valid) { return; } - backendSrv.put('/api/user/signup', $scope.newUser).then(function() { + backendSrv.post('/api/user/signup', $scope.formModel).then(function() { window.location.href = config.appSubUrl + '/'; }); }; @@ -63,17 +89,13 @@ function (angular, config) { }; $scope.login = function() { - if ($scope.mode === 'signup') { - $scope.mode = 'login'; - return; - } delete $scope.loginError; if (!$scope.loginForm.$valid) { return; } - backendSrv.post('/login', $scope.loginModel).then(function() { + backendSrv.post('/login', $scope.formModel).then(function() { window.location.href = config.appSubUrl + '/'; }); }; diff --git a/src/app/controllers/sidemenuCtrl.js b/src/app/controllers/sidemenuCtrl.js new file mode 100644 index 00000000000..10b73e2458a --- /dev/null +++ b/src/app/controllers/sidemenuCtrl.js @@ -0,0 +1,104 @@ +define([ + 'angular', + 'lodash', + 'jquery', + 'config', +], +function (angular, _, $, config) { + 'use strict'; + + var module = angular.module('grafana.controllers'); + + module.controller('SideMenuCtrl', function($scope, $location) { + + $scope.getUrl = function(url) { + return config.appSubUrl + url; + }; + + $scope.menu = [ + { + text: "Dashbord", + href: $scope.getUrl("/"), + startsWith: config.appSubUrl + '/dashboard/', + icon: "fa fa-th-large", + links: [ + { text: 'Settings', editview: 'settings', icon: "fa fa-cogs" }, + { text: 'Templating', editview: 'templating', icon: "fa fa-cogs" }, + { text: 'Annotations', editview: 'annotations', icon: "fa fa-bolt" }, + { text: 'Export', href:"", icon: "fa fa-bolt" }, + { text: 'JSON', href:"", icon: "fa fa-bolt" }, + ] + }, + { + text: "Account", href: $scope.getUrl("/account"), + icon: "fa fa-shield", + links: [ + { text: 'Info', href: $scope.getUrl("/account"), icon: "fa fa-sitemap" }, + { text: 'Data sources', href: $scope.getUrl("/account/datasources"), icon: "fa fa-sitemap" }, + { text: 'Users', href: $scope.getUrl("/account/users"), icon: "fa fa-users" }, + { text: 'API Keys', href: $scope.getUrl("/account/apikeys"), icon: "fa fa-key" }, + ] + }, + { + text: "Profile", href: $scope.getUrl("/profile"), + icon: "fa fa-user", + links: [ + { text: 'Info', href: $scope.getUrl("/profile"), icon: "fa fa-sitemap" }, + { text: 'Password', href:"", icon: "fa fa-lock" }, + ] + } + ]; + + $scope.onAppEvent('$routeUpdate', function() { + $scope.updateState(); + }); + + $scope.onAppEvent('$routeChangeSuccess', function() { + $scope.updateState(); + }); + + $scope.updateState = function() { + var currentPath = config.appSubUrl + $location.path(); + var search = $location.search(); + + _.each($scope.menu, function(item) { + item.active = false; + + if (item.href === currentPath) { + item.active = true; + } + + if (item.startsWith) { + if (currentPath.indexOf(item.startsWith) === 0) { + item.active = true; + item.href = currentPath; + } + } + + _.each(item.links, function(link) { + link.active = false; + + if (link.editview) { + var params = {}; + _.each(search, function(value, key) { + if (value !== null) { params[key] = value; } + }); + + params.editview = link.editview; + link.href = currentPath + '?' + $.param(params); + } + + if (link.href === currentPath) { + item.active = true; + link.active = true; + } + }); + }); + }; + + $scope.init = function() { + $scope.updateState(); + }; + }); + +}); diff --git a/src/app/directives/dashEditLink.js b/src/app/directives/dashEditLink.js index b0ac97d3423..dbafebae35c 100644 --- a/src/app/directives/dashEditLink.js +++ b/src/app/directives/dashEditLink.js @@ -5,6 +5,12 @@ define([ function (angular, $) { 'use strict'; + var editViewMap = { + 'settings': 'app/partials/dasheditor.html', + 'annotations': 'app/features/annotations/partials/editor.html', + 'templating': 'app/partials/templating_editor.html', + }; + angular .module('grafana.directives') .directive('dashEditorLink', function($timeout) { @@ -25,7 +31,7 @@ function (angular, $) { angular .module('grafana.directives') - .directive('dashEditorView', function($compile) { + .directive('dashEditorView', function($compile, $location) { return { restrict: 'A', link: function(scope, elem) { @@ -48,10 +54,9 @@ function (angular, $) { if (editorScope) { editorScope.dismiss(); } } - scope.onAppEvent("dashboard-loaded", hideEditorPane); - scope.onAppEvent('hide-dash-editor', hideEditorPane); + function showEditorPane(evt, payload, editview) { + if (editview) { payload.src = editViewMap[editview]; } - scope.onAppEvent('show-dash-editor', function(evt, payload) { if (lastEditor === payload.src) { hideEditorPane(); return; @@ -70,6 +75,14 @@ function (angular, $) { lastEditor = null; editorScope = null; hideScrollbars(false); + + if (editview) { + var urlParams = $location.search(); + if (editview === urlParams.editview) { + delete urlParams.editview; + $location.search(urlParams); + } + } }; // hide page scrollbars while edit pane is visible @@ -79,8 +92,18 @@ function (angular, $) { var view = $('
'); elem.append(view); $compile(elem.contents())(editorScope); + } + + scope.$watch("dashboardViewState.state.editview", function(newValue, oldValue) { + if (newValue) { + showEditorPane(null, {}, newValue); + } else if (oldValue) { + hideEditorPane(); + } }); + scope.onAppEvent('hide-dash-editor', hideEditorPane); + scope.onAppEvent('show-dash-editor', showEditorPane); } }; }); diff --git a/src/app/directives/tip.js b/src/app/directives/tip.js index 895015cb671..a0aac161bb2 100644 --- a/src/app/directives/tip.js +++ b/src/app/directives/tip.js @@ -18,6 +18,21 @@ function (angular, kbn) { }; }); + angular + .module('grafana.directives') + .directive('watchChange', function() { + return { + scope: { onchange: '&watchChange' }, + link: function(scope, element) { + element.on('input', function() { + scope.$apply(function () { + scope.onchange({ inputValue: element.val() }); + }); + }); + } + }; + }); + angular .module('grafana.directives') .directive('editorOptBool', function($compile) { diff --git a/src/app/features/account/accountCtrl.js b/src/app/features/account/accountCtrl.js new file mode 100644 index 00000000000..8d4130bda03 --- /dev/null +++ b/src/app/features/account/accountCtrl.js @@ -0,0 +1,29 @@ +define([ + 'angular', +], +function (angular) { + 'use strict'; + + var module = angular.module('grafana.controllers'); + + module.controller('AccountCtrl', function($scope, $http, backendSrv) { + + $scope.init = function() { + $scope.getAccount(); + }; + + $scope.getAccount = function() { + backendSrv.get('/api/account').then(function(account) { + $scope.account = account; + }); + }; + + $scope.update = function() { + if (!$scope.accountForm.$valid) { return; } + backendSrv.put('/api/account', $scope.account).then($scope.getAccount); + }; + + $scope.init(); + + }); +}); diff --git a/src/app/features/account/accountUsersCtrl.js b/src/app/features/account/accountUsersCtrl.js index 560b8ff4bc6..65ad8bcf847 100644 --- a/src/app/features/account/accountUsersCtrl.js +++ b/src/app/features/account/accountUsersCtrl.js @@ -24,22 +24,12 @@ function (angular) { }; $scope.removeUser = function(user) { - backendSrv.request({ - method: 'DELETE', - url: '/api/account/users/' + user.userId - }).then($scope.get); + backendSrv.delete('/api/account/users/' + user.userId).then($scope.get); }; $scope.addUser = function() { - if (!$scope.form.$valid) { - return; - } - - backendSrv.request({ - method: 'PUT', - url: '/api/account/users', - data: $scope.user, - }).then($scope.get); + if (!$scope.form.$valid) { return; } + backendSrv.post('/api/account/users', $scope.user).then($scope.get); }; $scope.init(); diff --git a/src/app/features/account/apiKeysCtrl.js b/src/app/features/account/apiKeysCtrl.js index 8878edc3506..64df055dd90 100644 --- a/src/app/features/account/apiKeysCtrl.js +++ b/src/app/features/account/apiKeysCtrl.js @@ -26,12 +26,7 @@ function (angular) { }; $scope.addToken = function() { - backendSrv.request({ - method: 'PUT', - url: '/api/tokens', - data: $scope.token, - desc: 'Add token' - }).then($scope.getTokens); + backendSrv.post('/api/tokens', $scope.token).then($scope.getTokens); }; $scope.init(); diff --git a/src/app/features/account/partials/account.html b/src/app/features/account/partials/account.html new file mode 100644 index 00000000000..5465d0c7094 --- /dev/null +++ b/src/app/features/account/partials/account.html @@ -0,0 +1,37 @@ + + +
-
-
- For these changes to fully take effect save and reload the dashboard.
-
-
+
+
+ For these changes to fully take effect save and reload the dashboard.
+
+