From 7db37032759427c5a9b36654933982b7582a3caf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Fri, 20 Mar 2015 22:01:39 -0400
Subject: [PATCH 002/274] Share Panel: The share modal now has an embed option,
gives you an iframe that you can use to embedd a single graph on another web
site, #1622
---
CHANGELOG.md | 1 +
.../features/dashboard/dashboardNavCtrl.js | 2 +-
.../dashboard/partials/shareDashboard.html | 48 ++++++++++++++
.../dashboard/partials/shareModal.html | 53 ---------------
.../dashboard/partials/sharePanel.html | 65 +++++++++++++++++++
src/app/features/dashboard/sharePanelCtrl.js | 5 +-
src/app/features/panel/panelSrv.js | 2 +-
src/app/features/panel/soloPanelCtrl.js | 16 ++++-
src/css/less/forms.less | 6 ++
src/test/specs/soloPanelCtrl-specs.js | 6 ++
10 files changed, 146 insertions(+), 58 deletions(-)
create mode 100644 src/app/features/dashboard/partials/shareDashboard.html
delete mode 100644 src/app/features/dashboard/partials/shareModal.html
create mode 100644 src/app/features/dashboard/partials/sharePanel.html
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eb1ffcdecb9..15e1527ad8c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
# 2.0.0 (unreleased)
**New features**
+- [Issue #1622](https://github.com/grafana/grafana/issues/1622). Share Panel: The share modal now has an embed option, gives you an iframe that you can use to embedd a single graph on another web site
- [Issue #718](https://github.com/grafana/grafana/issues/718). Dashboard: When saving a dashboard and another user has made changes inbetween the user is promted with a warning if he really wants to overwrite the other's changes
- [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
diff --git a/src/app/features/dashboard/dashboardNavCtrl.js b/src/app/features/dashboard/dashboardNavCtrl.js
index a47e19b561f..5950c504a3a 100644
--- a/src/app/features/dashboard/dashboardNavCtrl.js
+++ b/src/app/features/dashboard/dashboardNavCtrl.js
@@ -42,7 +42,7 @@ function (angular, _, moment) {
$scope.shareDashboard = function() {
$scope.appEvent('show-modal', {
- src: './app/features/dashboard/partials/shareModal.html',
+ src: './app/features/dashboard/partials/shareDashboard.html',
scope: $scope.$new(),
});
};
diff --git a/src/app/features/dashboard/partials/shareDashboard.html b/src/app/features/dashboard/partials/shareDashboard.html
new file mode 100644
index 00000000000..e052c5b298f
--- /dev/null
+++ b/src/app/features/dashboard/partials/shareDashboard.html
@@ -0,0 +1,48 @@
+
The html code below can be pasted and included in another web page. Unless anonymous access
+ is enabled the user viewing that page need to be signed into grafana for the graph to load.
+
+
+
+ This will create a snapshot of the dashboard and the data currently visible. It will be saved and you will
+ get a link, anyone with this link will be able view view the dashboard and the data currently visible.
+
+
+
+
This will create a snapshot of the dashboard and the data currently visible. It will be saved and you will
get a link, anyone with this link will be able view view the dashboard and the data currently visible.
+
+ Panel metric queries and panel drilldown links are removed.
-
-
-
-
- Snapshot name
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/app/features/dashboard/shareSnapshotCtrl.js b/src/app/features/dashboard/shareSnapshotCtrl.js
index acc0f38d946..6f4e21cd70d 100644
--- a/src/app/features/dashboard/shareSnapshotCtrl.js
+++ b/src/app/features/dashboard/shareSnapshotCtrl.js
@@ -21,6 +21,11 @@ function (angular) {
var dash = angular.copy($scope.dashboard);
dash.title = $scope.snapshot.name;
+ dash.forEachPanel(function(panel){
+ panel.targets = [];
+ panel.links = [];
+ });
+
var apiUrl = '/api/snapshots';
if (makePublic) {
diff --git a/src/css/less/gfbox.less b/src/css/less/gfbox.less
index 12b16974162..995ccb5b435 100644
--- a/src/css/less/gfbox.less
+++ b/src/css/less/gfbox.less
@@ -96,3 +96,21 @@
}
}
}
+
+.share-snapshot {
+ text-align: center;
+
+ .share-snapshot-header {
+ .fa {
+ position: absolute;
+ font-size: 600%;
+ left: 41%;
+ color: @grafanaTargetFuncBackground;
+ z-index: -1;
+ }
+
+ position: relative;
+ z-index: 1000;
+ line-height: 106px;
+ }
+}
From 4d13a5bffb6f9b2fadb26d187b7e65aa11421293 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Mon, 23 Mar 2015 14:00:03 -0400
Subject: [PATCH 015/274] Fixed failing style check
---
src/app/features/dashboard/sharePanelCtrl.js | 11 -----------
src/app/features/dashboard/shareSnapshotCtrl.js | 2 +-
2 files changed, 1 insertion(+), 12 deletions(-)
diff --git a/src/app/features/dashboard/sharePanelCtrl.js b/src/app/features/dashboard/sharePanelCtrl.js
index 88710660e3c..c7303ab1a68 100644
--- a/src/app/features/dashboard/sharePanelCtrl.js
+++ b/src/app/features/dashboard/sharePanelCtrl.js
@@ -81,17 +81,6 @@ function (angular, _, require, config) {
$scope.imageUrl += '&height=500';
};
- $scope.snapshot = function() {
- $scope.dashboard.snapshot = true;
- $rootScope.$broadcast('refresh');
-
- $timeout(function() {
- $scope.exportDashboard();
- $scope.dashboard.snapshot = false;
- $scope.appEvent('dashboard-snapshot-cleanup');
- }, 1000);
- };
-
$scope.init();
});
diff --git a/src/app/features/dashboard/shareSnapshotCtrl.js b/src/app/features/dashboard/shareSnapshotCtrl.js
index 6f4e21cd70d..88247e3138b 100644
--- a/src/app/features/dashboard/shareSnapshotCtrl.js
+++ b/src/app/features/dashboard/shareSnapshotCtrl.js
@@ -21,7 +21,7 @@ function (angular) {
var dash = angular.copy($scope.dashboard);
dash.title = $scope.snapshot.name;
- dash.forEachPanel(function(panel){
+ dash.forEachPanel(function(panel) {
panel.targets = [];
panel.links = [];
});
From 41820ccb0507cab11f333a265218ea9b626d1943 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Mon, 23 Mar 2015 15:32:38 -0400
Subject: [PATCH 016/274] Dashboard Snapshot sharing: singlestat panel now
works, #1623
---
src/app/features/panel/panelHelper.js | 8 +++++++-
src/app/features/panel/panelSrv.js | 7 +++++++
src/app/panels/graph/module.js | 15 ++++++---------
src/app/panels/singlestat/module.js | 5 +++++
4 files changed, 25 insertions(+), 10 deletions(-)
diff --git a/src/app/features/panel/panelHelper.js b/src/app/features/panel/panelHelper.js
index c2842fb225e..62982f69438 100644
--- a/src/app/features/panel/panelHelper.js
+++ b/src/app/features/panel/panelHelper.js
@@ -70,7 +70,13 @@ function (angular, _, kbn, $) {
cacheTimeout: scope.panel.cacheTimeout
};
- return datasource.query(metricsQuery);
+ return datasource.query(metricsQuery).then(function(results) {
+ if (scope.dashboard.snapshot) {
+ scope.panel.snapshotData = results;
+ }
+
+ return results;
+ });
};
});
diff --git a/src/app/features/panel/panelSrv.js b/src/app/features/panel/panelSrv.js
index 7ce3d69b376..8113194dfaf 100644
--- a/src/app/features/panel/panelSrv.js
+++ b/src/app/features/panel/panelSrv.js
@@ -93,6 +93,13 @@ function (angular, _, config) {
$scope.get_data = function() {
if ($scope.otherPanelInFullscreenMode()) { return; }
+ if ($scope.panel.snapshotData) {
+ if ($scope.loadSnapshot) {
+ $scope.loadSnapshot($scope.panel.snapshotData);
+ }
+ return;
+ }
+
delete $scope.panelMeta.error;
$scope.panelMeta.loading = true;
diff --git a/src/app/panels/graph/module.js b/src/app/panels/graph/module.js
index 966e1f9aa23..c7612c7cc9f 100644
--- a/src/app/panels/graph/module.js
+++ b/src/app/panels/graph/module.js
@@ -130,12 +130,6 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) {
$scope.refreshData = function(datasource) {
panelHelper.updateTimeRange($scope);
- if ($scope.panel.snapshotData) {
- $scope.annotationsPromise = $q.when([]);
- $scope.dataHandler($scope.panel.snapshotData);
- return;
- }
-
$scope.annotationsPromise = annotationsSrv.getAnnotations($scope.rangeUnparsed, $scope.dashboard);
return panelHelper.issueMetricQuery($scope, datasource)
@@ -146,10 +140,13 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) {
});
};
+ $scope.loadSnapshot = function(snapshotData) {
+ panelHelper.updateTimeRange($scope);
+ $scope.annotationsPromise = $q.when([]);
+ $scope.dataHandler(snapshotData);
+ };
+
$scope.dataHandler = function(results) {
- if ($scope.dashboard.snapshot) {
- $scope.panel.snapshotData = results;
- }
// png renderer returns just a url
if (_.isString(results)) {
$scope.render(results);
diff --git a/src/app/panels/singlestat/module.js b/src/app/panels/singlestat/module.js
index 81167a83a7d..8f33ce57cae 100644
--- a/src/app/panels/singlestat/module.js
+++ b/src/app/panels/singlestat/module.js
@@ -87,6 +87,11 @@ function (angular, app, _, TimeSeries, kbn, PanelMeta) {
});
};
+ $scope.loadSnapshot = function(snapshotData) {
+ panelHelper.updateTimeRange($scope);
+ $scope.dataHandler(snapshotData);
+ };
+
$scope.dataHandler = function(results) {
$scope.series = _.map(results.data, $scope.seriesHandler);
$scope.render();
From 6f2a8e27b8a521439630fb13b703510a30856f0a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Mon, 23 Mar 2015 15:36:18 -0400
Subject: [PATCH 017/274] Dashboard Snapshot: added dashboard snapshot to
changelog, #1623
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15e1527ad8c..690c94a0fbf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
# 2.0.0 (unreleased)
**New features**
+- [Issue #1623](https://github.com/grafana/grafana/issues/1623). Share Dashboard: Dashboard snapshot sharing (dash and data snapshot), save to local or save to public snapshot dashboard snapshots.raintank.io site
- [Issue #1622](https://github.com/grafana/grafana/issues/1622). Share Panel: The share modal now has an embed option, gives you an iframe that you can use to embedd a single graph on another web site
- [Issue #718](https://github.com/grafana/grafana/issues/718). Dashboard: When saving a dashboard and another user has made changes inbetween the user is promted with a warning if he really wants to overwrite the other's changes
- [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
From 98c0209976a16bf7e604837718e285ddd62e21c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Mon, 23 Mar 2015 17:34:41 -0400
Subject: [PATCH 018/274] Dashboard snapshot: cleanup snapshot data after
snapshot, #1623
---
src/app/features/dashboard/shareSnapshotCtrl.js | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/app/features/dashboard/shareSnapshotCtrl.js b/src/app/features/dashboard/shareSnapshotCtrl.js
index 88247e3138b..6a787cee752 100644
--- a/src/app/features/dashboard/shareSnapshotCtrl.js
+++ b/src/app/features/dashboard/shareSnapshotCtrl.js
@@ -26,6 +26,12 @@ function (angular) {
panel.links = [];
});
+ // cleanup snapshotData
+ $scope.dashboard.snapshot = false;
+ $scope.dashboard.forEachPanel(function(panel) {
+ delete panel.snapshotData;
+ });
+
var apiUrl = '/api/snapshots';
if (makePublic) {
@@ -46,8 +52,7 @@ function (angular) {
$scope.loading = false;
});
- $scope.dashboard.snapshot = false;
- $scope.appEvent('dashboard-snapshot-cleanup');
+
}, 2000);
};
From 5f0e7cd52a3e78f02190cc769f91e52f27748445 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Mon, 23 Mar 2015 18:28:59 -0400
Subject: [PATCH 019/274] Added custom cache control headers for static content
---
conf/defaults.ini | 7 +-
pkg/api/dashboard_snapshot.go | 1 +
pkg/api/static/static.go | 218 ++++++++++++++++++
pkg/cmd/web.go | 20 +-
.../features/dashboard/shareSnapshotCtrl.js | 58 ++---
tasks/options/requirejs.js | 1 +
6 files changed, 267 insertions(+), 38 deletions(-)
create mode 100644 pkg/api/static/static.go
diff --git a/conf/defaults.ini b/conf/defaults.ini
index d35f71fa4ce..2e4ea89b3f2 100644
--- a/conf/defaults.ini
+++ b/conf/defaults.ini
@@ -1,10 +1,9 @@
app_name = Grafana
app_mode = production
-# Once every 1 hour Grafana will report anonymous data to
-# stats.grafana.org (https). No ip addresses are being tracked.
-# only simple counters to track running instances, dashboard
-# count and errors. It is very helpful to us.
+# Report anonymous usage counters to stats.grafana.org (https).
+# No ip addresses are being tracked, only simple counters to track
+# running instances, dashboard count and errors. It is very helpful to us.
# Change this option to false to disable reporting.
reporting-enabled = true
diff --git a/pkg/api/dashboard_snapshot.go b/pkg/api/dashboard_snapshot.go
index 979a3aa86f2..e4841074901 100644
--- a/pkg/api/dashboard_snapshot.go
+++ b/pkg/api/dashboard_snapshot.go
@@ -35,5 +35,6 @@ func GetDashboardSnapshot(c *middleware.Context) {
Meta: dtos.DashboardMeta{IsSnapshot: true},
}
+ c.Resp.Header().Set("Cache-Control", "public max-age: 31536000")
c.JSON(200, dto)
}
diff --git a/pkg/api/static/static.go b/pkg/api/static/static.go
new file mode 100644
index 00000000000..43ba6a32b20
--- /dev/null
+++ b/pkg/api/static/static.go
@@ -0,0 +1,218 @@
+// Copyright 2013 Martini Authors
+// Copyright 2014 Unknwon
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package httpstatic
+
+import (
+ "log"
+ "net/http"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+ "sync"
+
+ "github.com/Unknwon/macaron"
+)
+
+var Root string
+
+func init() {
+ var err error
+ Root, err = os.Getwd()
+ if err != nil {
+ panic("error getting work directory: " + err.Error())
+ }
+}
+
+// StaticOptions is a struct for specifying configuration options for the macaron.Static middleware.
+type StaticOptions struct {
+ // Prefix is the optional prefix used to serve the static directory content
+ Prefix string
+ // SkipLogging will disable [Static] log messages when a static file is served.
+ SkipLogging bool
+ // IndexFile defines which file to serve as index if it exists.
+ IndexFile string
+ // Expires defines which user-defined function to use for producing a HTTP Expires Header
+ // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching
+ AddHeaders func(ctx *macaron.Context)
+ // FileSystem is the interface for supporting any implmentation of file system.
+ FileSystem http.FileSystem
+}
+
+// FIXME: to be deleted.
+type staticMap struct {
+ lock sync.RWMutex
+ data map[string]*http.Dir
+}
+
+func (sm *staticMap) Set(dir *http.Dir) {
+ sm.lock.Lock()
+ defer sm.lock.Unlock()
+
+ sm.data[string(*dir)] = dir
+}
+
+func (sm *staticMap) Get(name string) *http.Dir {
+ sm.lock.RLock()
+ defer sm.lock.RUnlock()
+
+ return sm.data[name]
+}
+
+func (sm *staticMap) Delete(name string) {
+ sm.lock.Lock()
+ defer sm.lock.Unlock()
+
+ delete(sm.data, name)
+}
+
+var statics = staticMap{sync.RWMutex{}, map[string]*http.Dir{}}
+
+// staticFileSystem implements http.FileSystem interface.
+type staticFileSystem struct {
+ dir *http.Dir
+}
+
+func newStaticFileSystem(directory string) staticFileSystem {
+ if !filepath.IsAbs(directory) {
+ directory = filepath.Join(Root, directory)
+ }
+ dir := http.Dir(directory)
+ statics.Set(&dir)
+ return staticFileSystem{&dir}
+}
+
+func (fs staticFileSystem) Open(name string) (http.File, error) {
+ return fs.dir.Open(name)
+}
+
+func prepareStaticOption(dir string, opt StaticOptions) StaticOptions {
+ // Defaults
+ if len(opt.IndexFile) == 0 {
+ opt.IndexFile = "index.html"
+ }
+ // Normalize the prefix if provided
+ if opt.Prefix != "" {
+ // Ensure we have a leading '/'
+ if opt.Prefix[0] != '/' {
+ opt.Prefix = "/" + opt.Prefix
+ }
+ // Remove any trailing '/'
+ opt.Prefix = strings.TrimRight(opt.Prefix, "/")
+ }
+ if opt.FileSystem == nil {
+ opt.FileSystem = newStaticFileSystem(dir)
+ }
+ return opt
+}
+
+func prepareStaticOptions(dir string, options []StaticOptions) StaticOptions {
+ var opt StaticOptions
+ if len(options) > 0 {
+ opt = options[0]
+ }
+ return prepareStaticOption(dir, opt)
+}
+
+func staticHandler(ctx *macaron.Context, log *log.Logger, opt StaticOptions) bool {
+ if ctx.Req.Method != "GET" && ctx.Req.Method != "HEAD" {
+ return false
+ }
+
+ file := ctx.Req.URL.Path
+ // if we have a prefix, filter requests by stripping the prefix
+ if opt.Prefix != "" {
+ if !strings.HasPrefix(file, opt.Prefix) {
+ return false
+ }
+ file = file[len(opt.Prefix):]
+ if file != "" && file[0] != '/' {
+ return false
+ }
+ }
+
+ f, err := opt.FileSystem.Open(file)
+ if err != nil {
+ return false
+ }
+ defer f.Close()
+
+ fi, err := f.Stat()
+ if err != nil {
+ return true // File exists but fail to open.
+ }
+
+ // Try to serve index file
+ if fi.IsDir() {
+ // Redirect if missing trailing slash.
+ if !strings.HasSuffix(ctx.Req.URL.Path, "/") {
+ http.Redirect(ctx.Resp, ctx.Req.Request, ctx.Req.URL.Path+"/", http.StatusFound)
+ return true
+ }
+
+ file = path.Join(file, opt.IndexFile)
+ f, err = opt.FileSystem.Open(file)
+ if err != nil {
+ return false // Discard error.
+ }
+ defer f.Close()
+
+ fi, err = f.Stat()
+ if err != nil || fi.IsDir() {
+ return true
+ }
+ }
+
+ if !opt.SkipLogging {
+ log.Println("[Static] Serving " + file)
+ }
+
+ // Add an Expires header to the static content
+ if opt.AddHeaders != nil {
+ opt.AddHeaders(ctx)
+ }
+
+ http.ServeContent(ctx.Resp, ctx.Req.Request, file, fi.ModTime(), f)
+ return true
+}
+
+// Static returns a middleware handler that serves static files in the given directory.
+func Static(directory string, staticOpt ...StaticOptions) macaron.Handler {
+ opt := prepareStaticOptions(directory, staticOpt)
+
+ return func(ctx *macaron.Context, log *log.Logger) {
+ staticHandler(ctx, log, opt)
+ }
+}
+
+// Statics registers multiple static middleware handlers all at once.
+func Statics(opt StaticOptions, dirs ...string) macaron.Handler {
+ if len(dirs) == 0 {
+ panic("no static directory is given")
+ }
+ opts := make([]StaticOptions, len(dirs))
+ for i := range dirs {
+ opts[i] = prepareStaticOption(dirs[i], opt)
+ }
+
+ return func(ctx *macaron.Context, log *log.Logger) {
+ for i := range opts {
+ if staticHandler(ctx, log, opts[i]) {
+ return
+ }
+ }
+ }
+}
diff --git a/pkg/cmd/web.go b/pkg/cmd/web.go
index e5516fb52d9..a8482185e78 100644
--- a/pkg/cmd/web.go
+++ b/pkg/cmd/web.go
@@ -11,7 +11,6 @@ import (
"path"
"path/filepath"
"strconv"
- "time"
"github.com/Unknwon/macaron"
"github.com/codegangsta/cli"
@@ -20,6 +19,7 @@ import (
_ "github.com/macaron-contrib/session/postgres"
"github.com/grafana/grafana/pkg/api"
+ "github.com/grafana/grafana/pkg/api/static"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
@@ -65,14 +65,22 @@ func newMacaron() *macaron.Macaron {
}
func mapStatic(m *macaron.Macaron, dir string, prefix string) {
- m.Use(macaron.Static(
+ headers := func(c *macaron.Context) {
+ c.Resp.Header().Set("Cache-Control", "public max-age: 3600")
+ }
+
+ if setting.Env == setting.DEV {
+ headers = func(c *macaron.Context) {
+ c.Resp.Header().Set("Cache-Control", "max-age: 0")
+ }
+ }
+
+ m.Use(httpstatic.Static(
path.Join(setting.StaticRootPath, dir),
- macaron.StaticOptions{
+ httpstatic.StaticOptions{
SkipLogging: true,
Prefix: prefix,
- Expires: func() string {
- return time.Now().UTC().Format(http.TimeFormat)
- },
+ AddHeaders: headers,
},
))
}
diff --git a/src/app/features/dashboard/shareSnapshotCtrl.js b/src/app/features/dashboard/shareSnapshotCtrl.js
index 6a787cee752..fc7973a676c 100644
--- a/src/app/features/dashboard/shareSnapshotCtrl.js
+++ b/src/app/features/dashboard/shareSnapshotCtrl.js
@@ -18,42 +18,44 @@ function (angular) {
$rootScope.$broadcast('refresh');
$timeout(function() {
- var dash = angular.copy($scope.dashboard);
- dash.title = $scope.snapshot.name;
+ $scope.saveSnapshot(makePublic);
+ }, 2000);
+ };
- dash.forEachPanel(function(panel) {
- panel.targets = [];
- panel.links = [];
- });
+ $scope.saveSnapshot = function(makePublic) {
+ var dash = angular.copy($scope.dashboard);
+ dash.title = $scope.snapshot.name;
- // cleanup snapshotData
- $scope.dashboard.snapshot = false;
- $scope.dashboard.forEachPanel(function(panel) {
- delete panel.snapshotData;
- });
+ dash.forEachPanel(function(panel) {
+ panel.targets = [];
+ panel.links = [];
+ });
- var apiUrl = '/api/snapshots';
+ // cleanup snapshotData
+ $scope.dashboard.snapshot = false;
+ $scope.dashboard.forEachPanel(function(panel) {
+ delete panel.snapshotData;
+ });
+ var apiUrl = '/api/snapshots';
+
+ if (makePublic) {
+ apiUrl = 'http://snapshots.raintank.io/api/snapshots';
+ }
+
+ backendSrv.post(apiUrl, {dashboard: dash}).then(function(results) {
+ $scope.loading = false;
+
+ var baseUrl = $location.absUrl().replace($location.url(), "");
if (makePublic) {
- apiUrl = 'http://snapshots.raintank.io/api/snapshots';
+ baseUrl = 'http://snapshots.raintank.io';
}
- backendSrv.post(apiUrl, {dashboard: dash}).then(function(results) {
- $scope.loading = false;
+ $scope.snapshotUrl = baseUrl + '/dashboard/snapshots/' + results.key;
- var baseUrl = $location.absUrl().replace($location.url(), "");
- if (makePublic) {
- baseUrl = 'http://snapshots.raintank.io';
- }
-
- $scope.snapshotUrl = baseUrl + '/dashboard/snapshots/' + results.key;
-
- }, function() {
- $scope.loading = false;
- });
-
-
- }, 2000);
+ }, function() {
+ $scope.loading = false;
+ });
};
});
diff --git a/tasks/options/requirejs.js b/tasks/options/requirejs.js
index 947553f1266..d9edf76ada4 100644
--- a/tasks/options/requirejs.js
+++ b/tasks/options/requirejs.js
@@ -61,6 +61,7 @@ module.exports = function(config,grunt) {
'controllers/all',
'routes/all',
'components/partials',
+ 'plugins/datasource/grafana/datasource',
]
}
];
From 3e9adeefbcf81ba1633801582fe8e553dfeeb73d Mon Sep 17 00:00:00 2001
From: Matt Robenolt
Date: Mon, 23 Mar 2015 21:58:29 -0700
Subject: [PATCH 020/274] Fix format of Cache-Control header
---
pkg/api/dashboard_snapshot.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pkg/api/dashboard_snapshot.go b/pkg/api/dashboard_snapshot.go
index e4841074901..aa20c4b8a7f 100644
--- a/pkg/api/dashboard_snapshot.go
+++ b/pkg/api/dashboard_snapshot.go
@@ -35,6 +35,6 @@ func GetDashboardSnapshot(c *middleware.Context) {
Meta: dtos.DashboardMeta{IsSnapshot: true},
}
- c.Resp.Header().Set("Cache-Control", "public max-age: 31536000")
+ c.Resp.Header().Set("Cache-Control", "public, max-age=31536000")
c.JSON(200, dto)
}
From 527e802b05d808531381ce3020e34f19c8a3aeb1 Mon Sep 17 00:00:00 2001
From: Stefan Wehner
Date: Tue, 24 Mar 2015 11:37:26 +0100
Subject: [PATCH 021/274] Limit ElasticSearch return to title and tags
---
src/app/features/elasticsearch/datasource.js | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/app/features/elasticsearch/datasource.js b/src/app/features/elasticsearch/datasource.js
index 3c82d98f8d2..8fb9d8c12d5 100644
--- a/src/app/features/elasticsearch/datasource.js
+++ b/src/app/features/elasticsearch/datasource.js
@@ -270,7 +270,8 @@ function (angular, _, config, kbn, moment) {
query: { query_string: { query: queryString } },
facets: { tags: { terms: { field: "tags", order: "term", size: 50 } } },
size: this.searchMaxResults,
- sort: ["_uid"]
+ sort: ["_uid"],
+ fields: ["title", "tags"]
};
return this._post('/dashboard/_search', query)
@@ -286,8 +287,8 @@ function (angular, _, config, kbn, moment) {
var hit = resultsHits[i];
displayHits.dashboards.push({
id: hit._id,
- title: hit._source.title,
- tags: hit._source.tags
+ title: hit.fields.title,
+ tags: hit.fields.tags
});
}
From c27db7a3471c0bb61abb5e6cf56025ce1339a4ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?=
Date: Tue, 24 Mar 2015 15:45:31 +0100
Subject: [PATCH 022/274] Small updates to share dashboard snapshot feature
---
pkg/api/dashboard_snapshot.go | 2 +-
.../dashboard/partials/shareDashboard.html | 47 +++++++++----------
.../features/dashboard/shareSnapshotCtrl.js | 7 ++-
src/css/less/gfbox.less | 16 -------
src/css/less/grafana.less | 27 +++++++++++
5 files changed, 55 insertions(+), 44 deletions(-)
diff --git a/pkg/api/dashboard_snapshot.go b/pkg/api/dashboard_snapshot.go
index aa20c4b8a7f..8bb6c6e6df3 100644
--- a/pkg/api/dashboard_snapshot.go
+++ b/pkg/api/dashboard_snapshot.go
@@ -9,7 +9,7 @@ import (
)
func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapshotCommand) {
- cmd.Key = util.GetRandomString(20)
+ cmd.Key = util.GetRandomString(32)
if err := bus.Dispatch(&cmd); err != nil {
c.JsonApiErr(500, "Failed to create snaphost", err)
diff --git a/src/app/features/dashboard/partials/shareDashboard.html b/src/app/features/dashboard/partials/shareDashboard.html
index bfe126b390f..320042b64bf 100644
--- a/src/app/features/dashboard/partials/shareDashboard.html
+++ b/src/app/features/dashboard/partials/shareDashboard.html
@@ -50,36 +50,33 @@
- Create dashboard with embedded data and share with anyone
+ Snapshot dashboard & visible data and share with anyone
- Snapshot dashboard & visible data and share with anyone
-
+
+ A snapshot is an instant way to share a dashboard publicly. The visible data and series names
+ are embedded into the dashboard while metric queries and panel links are stripped. The snapshot can be viewed by
+ anyone that have the link and can reach the URL. Read more.
+
-
-
- This will create a snapshot of the dashboard and the data currently visible. It will be saved and you will
- get a link, anyone with this link will be able view view the dashboard and the data currently visible.
-
- Panel metric queries and panel drilldown links are removed.
-
-
- A snapshot is an instant way to share a dashboard publicly. The visible data and series names
- are embedded into the dashboard while metric queries and panel links are stripped. The snapshot can be viewed by
- anyone that have the link and can reach the URL. Read more.
+ A snapshot is an instant way to share an interactive dashboard publicly.
+ When created, we strip sensitive data like queries (metric, template and annotation) and panel links,
+ leaving only the visible metric data and series names embedded into your dashboard.
+
+
+ Keep in mind, your snapshot can be viewed any anyone that has the link and can reach the URL.
+ Share wisely.