diff --git a/pkg/api/dashboard_snapshot.go b/pkg/api/dashboard_snapshot.go index 8de96ec9f21..fe628d580b1 100644 --- a/pkg/api/dashboard_snapshot.go +++ b/pkg/api/dashboard_snapshot.go @@ -65,7 +65,11 @@ func GetDashboardSnapshot(c *middleware.Context) { dto := dtos.Dashboard{ Model: snapshot.Dashboard, - Meta: dtos.DashboardMeta{IsSnapshot: true}, + Meta: dtos.DashboardMeta{ + IsSnapshot: true, + Created: snapshot.Created, + Expires: snapshot.Expires, + }, } metrics.M_Api_Dashboard_Snapshot_Get.Inc(1) diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index 2cb9da0189f..53519631d22 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -4,6 +4,7 @@ import ( "crypto/md5" "fmt" "strings" + "time" m "github.com/grafana/grafana/pkg/models" ) @@ -27,10 +28,12 @@ type CurrentUser struct { } type DashboardMeta struct { - IsStarred bool `json:"isStarred"` - IsHome bool `json:"isHome"` - IsSnapshot bool `json:"isSnapshot"` - Slug string `json:"slug"` + IsStarred bool `json:"isStarred"` + IsHome bool `json:"isHome"` + IsSnapshot bool `json:"isSnapshot"` + Slug string `json:"slug"` + Expires time.Time `json:"expires"` + Created time.Time `json:"created"` } type Dashboard struct { diff --git a/src/app/directives/topnav.js b/src/app/directives/topnav.js index c871dd085cb..88b98d55984 100644 --- a/src/app/directives/topnav.js +++ b/src/app/directives/topnav.js @@ -25,7 +25,7 @@ function (angular) { '' + '' + - '' + + '' + '' + '' + diff --git a/src/app/features/dashboard/all.js b/src/app/features/dashboard/all.js index d297ce502a8..1380034dd19 100644 --- a/src/app/features/dashboard/all.js +++ b/src/app/features/dashboard/all.js @@ -1,6 +1,7 @@ define([ './dashboardCtrl', './dashboardNavCtrl', + './snapshotTopNavCtrl', './saveDashboardAsCtrl', './playlistCtrl', './rowCtrl', diff --git a/src/app/features/dashboard/dashboardCtrl.js b/src/app/features/dashboard/dashboardCtrl.js index a8e0a2e595b..649fc14ede0 100644 --- a/src/app/features/dashboard/dashboardCtrl.js +++ b/src/app/features/dashboard/dashboardCtrl.js @@ -21,6 +21,7 @@ function (angular, $, config) { $timeout) { $scope.editor = { index: 0 }; + $scope.topNavPartial = 'app/features/dashboard/partials/dashboardTopNav.html'; $scope.panels = config.panels; var resizeEventTimeout; @@ -76,10 +77,7 @@ function (angular, $, config) { } if (dashboard.snapshot) { - meta.canEdit = false; - meta.canSave = false; - meta.canStar = false; - meta.canShare = false; + $scope.topNavPartial = 'app/features/dashboard/partials/snapshotTopNav.html'; } $scope.dashboardMeta = meta; diff --git a/src/app/features/dashboard/dashboardNavCtrl.js b/src/app/features/dashboard/dashboardNavCtrl.js index 17bdfb5efc7..6c017ae856e 100644 --- a/src/app/features/dashboard/dashboardNavCtrl.js +++ b/src/app/features/dashboard/dashboardNavCtrl.js @@ -1,25 +1,20 @@ define([ 'angular', 'lodash', - 'moment', 'config', 'store', 'filesaver' ], -function (angular, _, moment) { +function (angular, _) { 'use strict'; var module = angular.module('grafana.controllers'); - module.controller('DashboardNavCtrl', function($scope, $rootScope, alertSrv, $location, playlistSrv, backendSrv, timeSrv, $timeout) { + module.controller('DashboardNavCtrl', function($scope, $rootScope, alertSrv, $location, playlistSrv, backendSrv, $timeout) { $scope.init = function() { $scope.onAppEvent('save-dashboard', $scope.saveDashboard); $scope.onAppEvent('delete-dashboard', $scope.deleteDashboard); - - $scope.onAppEvent('zoom-out', function() { - $scope.zoom(2); - }); }; $scope.openEditView = function(editview) { @@ -136,27 +131,6 @@ function (angular, _, moment) { window.saveAs(blob, $scope.dashboard.title + '-' + new Date().getTime()); }; - $scope.zoom = function(factor) { - var range = timeSrv.timeRange(); - - var timespan = (range.to.valueOf() - range.from.valueOf()); - var center = range.to.valueOf() - timespan/2; - - var to = (center + (timespan*factor)/2); - var from = (center - (timespan*factor)/2); - - if(to > Date.now() && range.to <= Date.now()) { - var offset = to - Date.now(); - from = from - offset; - to = Date.now(); - } - - timeSrv.setTime({ - from: moment.utc(from).toDate(), - to: moment.utc(to).toDate(), - }); - }; - $scope.snapshot = function() { $scope.dashboard.snapshot = true; $rootScope.$broadcast('refresh'); diff --git a/src/app/partials/dashboard_topnav.html b/src/app/features/dashboard/partials/dashboardTopNav.html similarity index 100% rename from src/app/partials/dashboard_topnav.html rename to src/app/features/dashboard/partials/dashboardTopNav.html diff --git a/src/app/features/dashboard/partials/shareDashboard.html b/src/app/features/dashboard/partials/shareDashboard.html index fefca5a9100..d00f95355ea 100644 --- a/src/app/features/dashboard/partials/shareDashboard.html +++ b/src/app/features/dashboard/partials/shareDashboard.html @@ -47,8 +47,12 @@
- -
+
diff --git a/src/app/features/dashboard/snapshotTopNavCtrl.js b/src/app/features/dashboard/snapshotTopNavCtrl.js new file mode 100644 index 00000000000..06e7c9e6fdb --- /dev/null +++ b/src/app/features/dashboard/snapshotTopNavCtrl.js @@ -0,0 +1,22 @@ +define([ + 'angular', + 'moment', +], +function (angular, moment) { + 'use strict'; + + var module = angular.module('grafana.controllers'); + + module.controller('SnapshotTopNavCtrl', function($scope) { + + $scope.init = function() { + var meta = $scope.dashboardMeta; + $scope.titleTooltip = 'Created:  ' + moment(meta.created).calendar(); + if (meta.expires) { + $scope.titleTooltip += '
Expires:  ' + moment(meta.expires).fromNow() + '
'; + } + }; + + }); + +}); diff --git a/src/app/panels/timepicker/module.js b/src/app/panels/timepicker/module.js index 2f0d9bd75b8..928c3dfe5fc 100644 --- a/src/app/panels/timepicker/module.js +++ b/src/app/panels/timepicker/module.js @@ -66,6 +66,10 @@ function (angular, app, _, moment, kbn) { } $scope.time = getScopeTimeObj(time.from, time.to); + + $scope.onAppEvent('zoom-out', function() { + $scope.zoom(2); + }); }; $scope.customTime = function() { @@ -213,5 +217,26 @@ function (angular, app, _, moment, kbn) { return moment(new Date(date.getTime() + date.getTimezoneOffset() * 60000)).toDate(); }; + $scope.zoom = function(factor) { + var range = timeSrv.timeRange(); + + var timespan = (range.to.valueOf() - range.from.valueOf()); + var center = range.to.valueOf() - timespan/2; + + var to = (center + (timespan*factor)/2); + var from = (center - (timespan*factor)/2); + + if(to > Date.now() && range.to <= Date.now()) { + var offset = to - Date.now(); + from = from - offset; + to = Date.now(); + } + + timeSrv.setTime({ + from: moment.utc(from).toDate(), + to: moment.utc(to).toDate(), + }); + }; + }); }); diff --git a/src/app/partials/dashboard.html b/src/app/partials/dashboard.html index 39e05ebecb6..1384c41c1ee 100644 --- a/src/app/partials/dashboard.html +++ b/src/app/partials/dashboard.html @@ -1,14 +1,14 @@
-
+
-
+
diff --git a/src/css/less/fonts.less b/src/css/less/fonts.less new file mode 100644 index 00000000000..644e9199d80 --- /dev/null +++ b/src/css/less/fonts.less @@ -0,0 +1,22 @@ + +@font-face { + font-family: 'raintank'; + src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SAyoAAAC8AAAAYGNtYXAaVcxXAAABHAAAAExnYXNwAAAAEAAAAWgAAAAIZ2x5Zjk9uEcAAAFwAAACLGhlYWQFNCNwAAADnAAAADZoaGVhB14DxgAAA9QAAAAkaG10eAWdAAAAAAP4AAAAFGxvY2EAKAEqAAAEDAAAAAxtYXhwABUAvAAABBgAAAAgbmFtZflvA+cAAAQ4AAABTnBvc3QAAwAAAAAFiAAAACAAAwQAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADmAAPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIOYA//3//wAAAAAAIOYA//3//wAB/+MaBAADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAADwAA/7YDnAO3AAMAHQAhACUAKgAuADEAQgBTAGQAdQCGAJcAqAC5AAA3FyUnJTUTJzUjJwcjJwcjFQcTBxcVIQ0BNzM1NycTAxEXJTkCByERIREHFSc3NzEzAxQWOwEyNj0BNCYrASIGHQE3MzIWHQEUBisBIiY9ATQ2MxcVFBY7ATI2PQE0JisBIgYVMxUUBisBIiY9ATQ2OwE2FhUHNCYrASIGHQEUFjsBMjY9AQc1NDY7ATIWHQEUBisBIiY1BTMyNj0BNCYrASIGHQEUFjM3FRQGKwEiJj0BNDY7ATYWFXsKAS9VAbOKkFsDmDvbEZhXTjY/Alj+wQFGGT4mIU5TU/3CmgJU/awxICDLAWUbE40TGxsTjRMbLo0LERELjQwQEAzQHBONExsbE40UG9cQC40MEBAMjQsQ7BsTjRMbGxONExvXEAuNDBAQDIwMEAEbjRMbGxONFBsbFKgQC40MEBAMjQsQDkMvFvQEAfYoVRcXPDxGDf4JwhFKBFpdHAbPAdn+zwFIF54x/ZsCZUbPygV3/s4TGxsTjBQbHBOMqRAMjQsQEAuMDBEdjBQbHBOMFBsbFIwMEBAMjAwQAREM+RQbHBONExscEo2MjAwQEAyNCxARCy4bE4wUGxwTjRIbuo0LEBALjQwQAREMAAEAAAABAABhLqkRXw889QALBAAAAAAA0TxvgwAAAADRPG+DAAD/tgOcA7cAAAAIAAIAAAAAAAAAAQAAA8D/wAAABAAAAAAAA5wAAQAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAACAAAAA50AAAAAAAAACgAUAB4BFgABAAAABQC6AA8AAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEAEAAAAAEAAAAAAAIADgBOAAEAAAAAAAMAEAAmAAEAAAAAAAQAEABcAAEAAAAAAAUAFgAQAAEAAAAAAAYACAA2AAEAAAAAAAoANABsAAMAAQQJAAEAEAAAAAMAAQQJAAIADgBOAAMAAQQJAAMAEAAmAAMAAQQJAAQAEABcAAMAAQQJAAUAFgAQAAMAAQQJAAYAEAA+AAMAAQQJAAoANABsAHIAYQBpAG4AdABhAG4AawBWAGUAcgBzAGkAbwBuACAAMQAuADAAcgBhAGkAbgB0AGEAbgBrcmFpbnRhbmsAcgBhAGkAbgB0AGEAbgBrAFIAZQBnAHUAbABhAHIAcgBhAGkAbgB0AGEAbgBrAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=) format('truetype'), + url(data:application/font-woff;charset=utf-8;base64,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) format('woff'); + font-weight: normal; + font-style: normal; +} + +.gf-icon { + display: inline-block; + font: normal normal normal 14px/1 'raintank'; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.gf-icon-snapshot:before { + content: "\e600"; +} + diff --git a/src/css/less/grafana.less b/src/css/less/grafana.less index f71ee78f3cc..5232a18bee5 100644 --- a/src/css/less/grafana.less +++ b/src/css/less/grafana.less @@ -14,6 +14,7 @@ @import "dashlist.less"; @import "admin.less"; @import "validation.less"; +@import "fonts.less"; .row-control-inner { padding:0px; @@ -298,6 +299,14 @@ .share-snapshot { text-align: center; + .gf-icon-snapshot { + font-size: 70px; + } + + .fa-spinner { + font-size: 70px; + } + .share-snapshot-info-text { margin: 10px 105px; strong { @@ -307,16 +316,6 @@ } .share-snapshot-header { - .fa-spinner { - position: absolute; - font-size: 600%; - left: 44%; - color: @grafanaTargetFuncBackground; - z-index: -1; - } - - position: relative; - z-index: 1000; margin: 20px 0 22px 0; } diff --git a/src/css/less/navbar.less b/src/css/less/navbar.less index ebbc9c054de..b90ced344c0 100644 --- a/src/css/less/navbar.less +++ b/src/css/less/navbar.less @@ -90,12 +90,12 @@ position: relative; top: 4px; } +} - .dashboard-title { - padding: 0px 6px 5px 5px; - color: @linkColorHover; - font-size: 17px; - } +.dashboard-title { + padding: 0px 6px 5px 5px; + color: @linkColorHover; + font-size: 17px; } .top-nav-icon { @@ -122,3 +122,19 @@ color: @linkColor; } +.top-nav-snapshot-title { + .gf-icon { + position: relative; + top: 3px; + } + a { + display: inline-block; + padding: 3px 15px 5px 10px; + } + display: block; + float: left; + font-size: 1.4em; + margin: 9px 18px 5px 14px; + color: @linkColor; +} + diff --git a/src/vendor/css/font-awesome.min.css b/src/vendor/css/font-awesome.min.css index 4040b3cf859..df6725c0579 100644 --- a/src/vendor/css/font-awesome.min.css +++ b/src/vendor/css/font-awesome.min.css @@ -11,6 +11,7 @@ font-weight: normal; font-style: normal; } + .fa { display: inline-block; font: normal normal normal 14px/1 FontAwesome;