From d70ef90bddad216435a4fdea4d3f2d14c1ee736f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 8 Apr 2016 16:42:33 -0400 Subject: [PATCH 01/13] feat(): plugin list panel --- pkg/api/api.go | 3 + pkg/api/gnetproxy.go | 46 +++++++ pkg/api/plugins.go | 6 + pkg/plugins/frontend_plugin.go | 7 +- pkg/plugins/models.go | 1 + public/app/plugins/panel/pluginlist/README.md | 2 + .../app/plugins/panel/pluginlist/editor.html | 40 ++++++ .../pluginlist/img/icn-dashlist-panel.svg | 119 ++++++++++++++++++ .../app/plugins/panel/pluginlist/module.html | 17 +++ public/app/plugins/panel/pluginlist/module.ts | 66 ++++++++++ .../app/plugins/panel/pluginlist/plugin.json | 16 +++ 11 files changed, 320 insertions(+), 3 deletions(-) create mode 100644 pkg/api/gnetproxy.go create mode 100644 public/app/plugins/panel/pluginlist/README.md create mode 100644 public/app/plugins/panel/pluginlist/editor.html create mode 100644 public/app/plugins/panel/pluginlist/img/icn-dashlist-panel.svg create mode 100644 public/app/plugins/panel/pluginlist/module.html create mode 100644 public/app/plugins/panel/pluginlist/module.ts create mode 100644 public/app/plugins/panel/pluginlist/plugin.json diff --git a/pkg/api/api.go b/pkg/api/api.go index 85f1e2474c3..62da5c8f084 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -252,6 +252,9 @@ func Register(r *macaron.Macaron) { // rendering r.Get("/render/*", reqSignedIn, RenderToPng) + // grafana.net proxy + r.Any("/api/gnet/*", reqSignedIn, ProxyGnetRequest) + // Gravatar service. avt := avatar.CacheServer() r.Get("/avatar/:hash", avt.ServeHTTP) diff --git a/pkg/api/gnetproxy.go b/pkg/api/gnetproxy.go new file mode 100644 index 00000000000..6511afd39b7 --- /dev/null +++ b/pkg/api/gnetproxy.go @@ -0,0 +1,46 @@ +package api + +import ( + "crypto/tls" + "net" + "net/http" + "net/http/httputil" + "time" + + "github.com/grafana/grafana/pkg/middleware" + "github.com/grafana/grafana/pkg/util" +) + +var gNetProxyTransport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: false}, + Proxy: http.ProxyFromEnvironment, + Dial: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + TLSHandshakeTimeout: 10 * time.Second, +} + +func ReverseProxyGnetReq(proxyPath string) *httputil.ReverseProxy { + director := func(req *http.Request) { + req.URL.Scheme = "https" + req.URL.Host = "grafana.net" + req.Host = "grafana.net" + + req.URL.Path = util.JoinUrlFragments("https://grafana.net/api", proxyPath) + + // clear cookie headers + req.Header.Del("Cookie") + req.Header.Del("Set-Cookie") + } + + return &httputil.ReverseProxy{Director: director} +} + +func ProxyGnetRequest(c *middleware.Context) { + proxyPath := c.Params("*") + proxy := ReverseProxyGnetReq(proxyPath) + proxy.Transport = gNetProxyTransport + proxy.ServeHTTP(c.Resp, c.Req.Request) + c.Resp.Header().Del("Set-Cookie") +} diff --git a/pkg/api/plugins.go b/pkg/api/plugins.go index 0411c0746ef..3e8f80132d3 100644 --- a/pkg/api/plugins.go +++ b/pkg/api/plugins.go @@ -14,6 +14,7 @@ func GetPluginList(c *middleware.Context) Response { typeFilter := c.Query("type") enabledFilter := c.Query("enabled") embeddedFilter := c.Query("embedded") + coreFilter := c.Query("core") pluginSettingsMap, err := plugins.GetPluginSettings(c.OrgId) @@ -28,6 +29,11 @@ func GetPluginList(c *middleware.Context) Response { continue } + // filter out core plugins + if coreFilter == "0" && pluginDef.IsCorePlugin { + continue + } + // filter on type if typeFilter != "" && typeFilter != pluginDef.Type { continue diff --git a/pkg/plugins/frontend_plugin.go b/pkg/plugins/frontend_plugin.go index 55690777b2a..974559001d1 100644 --- a/pkg/plugins/frontend_plugin.go +++ b/pkg/plugins/frontend_plugin.go @@ -14,7 +14,7 @@ type FrontendPluginBase struct { } func (fp *FrontendPluginBase) initFrontendPlugin() { - if isInternalPlugin(fp.PluginDir) { + if isExternalPlugin(fp.PluginDir) { StaticRoutes = append(StaticRoutes, &PluginStaticRoute{ Directory: fp.PluginDir, PluginId: fp.Id, @@ -48,17 +48,18 @@ func (fp *FrontendPluginBase) setPathsBasedOnApp(app *AppPlugin) { func (fp *FrontendPluginBase) handleModuleDefaults() { - if isInternalPlugin(fp.PluginDir) { + if isExternalPlugin(fp.PluginDir) { fp.Module = path.Join("plugins", fp.Id, "module") fp.BaseUrl = path.Join("public/plugins", fp.Id) return } + fp.IsCorePlugin = true fp.Module = path.Join("app/plugins", fp.Type, fp.Id, "module") fp.BaseUrl = path.Join("public/app/plugins", fp.Type, fp.Id) } -func isInternalPlugin(pluginDir string) bool { +func isExternalPlugin(pluginDir string) bool { return !strings.Contains(pluginDir, setting.StaticRootPath) } diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index 30f794285e1..d35a78000b0 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -43,6 +43,7 @@ type PluginBase struct { IncludedInAppId string `json:"-"` PluginDir string `json:"-"` DefaultNavUrl string `json:"-"` + IsCorePlugin bool `json:"-"` // cache for readme file contents Readme []byte `json:"-"` diff --git a/public/app/plugins/panel/pluginlist/README.md b/public/app/plugins/panel/pluginlist/README.md new file mode 100644 index 00000000000..463769dad1f --- /dev/null +++ b/public/app/plugins/panel/pluginlist/README.md @@ -0,0 +1,2 @@ +# Plugin List Panel - Native Plugin + diff --git a/public/app/plugins/panel/pluginlist/editor.html b/public/app/plugins/panel/pluginlist/editor.html new file mode 100644 index 00000000000..c0577578598 --- /dev/null +++ b/public/app/plugins/panel/pluginlist/editor.html @@ -0,0 +1,40 @@ +
+
+
+ Mode +
+ +
+
+
+ + + +
+
+ +
+
+ Search options + Query + + + +
+ +
+ Tags + + + +
+
+ +
+
+ Limit number to + +
+
+
diff --git a/public/app/plugins/panel/pluginlist/img/icn-dashlist-panel.svg b/public/app/plugins/panel/pluginlist/img/icn-dashlist-panel.svg new file mode 100644 index 00000000000..8bac231bedf --- /dev/null +++ b/public/app/plugins/panel/pluginlist/img/icn-dashlist-panel.svg @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/app/plugins/panel/pluginlist/module.html b/public/app/plugins/panel/pluginlist/module.html new file mode 100644 index 00000000000..aaabb1c2c16 --- /dev/null +++ b/public/app/plugins/panel/pluginlist/module.html @@ -0,0 +1,17 @@ +
+ +
diff --git a/public/app/plugins/panel/pluginlist/module.ts b/public/app/plugins/panel/pluginlist/module.ts new file mode 100644 index 00000000000..86108a27961 --- /dev/null +++ b/public/app/plugins/panel/pluginlist/module.ts @@ -0,0 +1,66 @@ +/// + +import _ from 'lodash'; +import config from 'app/core/config'; +import {PanelCtrl} from '../../../features/panel/panel_ctrl'; + +// Set and populate defaults +var panelDefaults = { +}; + +class DashListCtrl extends PanelCtrl { + static templateUrl = 'module.html'; + + pluginList: any[]; + + /** @ngInject */ + constructor($scope, $injector, private backendSrv) { + super($scope, $injector); + _.defaults(this.panel, panelDefaults); + + this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); + this.pluginList = []; + this.update(); + } + + onInitEditMode() { + this.editorTabIndex = 1; + this.addEditorTab('Options', 'public/app/plugins/panel/pluginlist/editor.html'); + } + + update() { + this.backendSrv.get('api/plugins', {embedded: 0, core: 0}).then(plugins => { + this.pluginList = plugins; + + for (let plugin of this.pluginList) { + if (!plugin.enabled) { + plugin.state = 'not-enabled'; + } + } + + }).then(this.checkForUpdates.bind(this)); + } + + checkForUpdates() { + return this.backendSrv.get('api/gnet/plugins/repo').then(data => { + var gNetPlugins = _.reduce(data.plugins, (memo, val) => { + memo[val.id] = val; + return memo; + }, {}); + + for (let plugin of this.pluginList) { + var source = gNetPlugins[plugin.id]; + if (!source) { + continue; + } + + if (plugin.info.version !== source.versions[0].version) { + plugin.hasUpdate = true; + plugin.state = 'has-update'; + } + } + }); + } +} + +export {DashListCtrl, DashListCtrl as PanelCtrl} diff --git a/public/app/plugins/panel/pluginlist/plugin.json b/public/app/plugins/panel/pluginlist/plugin.json new file mode 100644 index 00000000000..be6ae9a5985 --- /dev/null +++ b/public/app/plugins/panel/pluginlist/plugin.json @@ -0,0 +1,16 @@ +{ + "type": "panel", + "name": "Plugin list", + "id": "pluginlist", + + "info": { + "author": { + "name": "Grafana Project", + "url": "http://grafana.org" +}, + "logos": { + "small": "img/icn-dashlist-panel.svg", + "large": "img/icn-dashlist-panel.svg" + } + } +} From d66d25ff14e5d447adb4ea92ee2c8fae9e07fb00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 8 Apr 2016 17:01:17 -0400 Subject: [PATCH 02/13] feat(pluginlist): added plugin logo --- public/app/plugins/panel/pluginlist/module.html | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/plugins/panel/pluginlist/module.html b/public/app/plugins/panel/pluginlist/module.html index aaabb1c2c16..5dd7ca54c40 100644 --- a/public/app/plugins/panel/pluginlist/module.html +++ b/public/app/plugins/panel/pluginlist/module.html @@ -1,6 +1,7 @@
+ {{plugin.name}} From d2421d964a9ec6cb3116c5ea05dd252767ded862 Mon Sep 17 00:00:00 2001 From: Matt Toback Date: Fri, 8 Apr 2016 18:46:45 -0400 Subject: [PATCH 03/13] New styles almost ready --- .../app/plugins/panel/pluginlist/module.html | 41 ++++++++++---- public/dashboards/home.json | 14 ++++- public/sass/_grafana.scss | 1 + public/sass/components/_cards.scss | 9 +++- public/sass/components/_panel_pluginlist.scss | 54 +++++++++++++++++++ 5 files changed, 107 insertions(+), 12 deletions(-) create mode 100644 public/sass/components/_panel_pluginlist.scss diff --git a/public/app/plugins/panel/pluginlist/module.html b/public/app/plugins/panel/pluginlist/module.html index 5dd7ca54c40..fcb20484d3e 100644 --- a/public/app/plugins/panel/pluginlist/module.html +++ b/public/app/plugins/panel/pluginlist/module.html @@ -1,18 +1,41 @@ -
-
- - - + diff --git a/public/dashboards/home.json b/public/dashboards/home.json index 1d57a37edfe..5825f849375 100644 --- a/public/dashboards/home.json +++ b/public/dashboards/home.json @@ -39,7 +39,7 @@ "limit": 10, "mode": "starred", "query": "", - "span": 6, + "span": 3.75, "tags": [], "title": "Starred dashboards", "type": "dashlist" @@ -49,10 +49,20 @@ "limit": 10, "mode": "recently viewed", "query": "", - "span": 6, + "span": 3.75, "tags": [], "title": "Recently viewed dashboards", "type": "dashlist" + }, + { + "title": "", + "error": false, + "span": 4.5, + "editable": true, + "type": "pluginlist", + "isNew": true, + "id": 4, + "links": [] } ], "title": "Row" diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index 900d0c00f80..460adebb9da 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -42,6 +42,7 @@ @import "components/panel_graph"; @import "components/submenu"; @import "components/panel_dashlist"; +@import "components/panel_pluginlist"; @import "components/panel_singlestat"; @import "components/panel_table"; @import "components/panel_text"; diff --git a/public/sass/components/_cards.scss b/public/sass/components/_cards.scss index 08baf146c93..76626566245 100644 --- a/public/sass/components/_cards.scss +++ b/public/sass/components/_cards.scss @@ -116,6 +116,10 @@ padding: 0 1.5rem 1.5rem 0rem; } + .card-item-wrapper--clickable { + cursor: pointer; + } + .card-item-figure { margin: 0 $spacer $spacer 0; height: 6rem; @@ -157,6 +161,10 @@ width: 100%; } + .card-item-wrapper--clickable { + cursor: pointer; + } + .card-item { border-bottom: .2rem solid $page-bg; border-radius: 0; @@ -186,4 +194,3 @@ margin-right: 0; } } - diff --git a/public/sass/components/_panel_pluginlist.scss b/public/sass/components/_panel_pluginlist.scss new file mode 100644 index 00000000000..1535ae96519 --- /dev/null +++ b/public/sass/components/_panel_pluginlist.scss @@ -0,0 +1,54 @@ +.pluginlist-section-header { + margin: ($spacer * 2) 0 $spacer 0; + color: $text-color-weak; +} + +.pluginlist-section-header--first { + margin-top: $spacer /2; +} + +.pluginlist-link { + display: block; + margin: 5px; + padding: 7px; + background-color: $tight-form-bg; + + &:hover { + background-color: $tight-form-func-bg; + } +} + +.pluginlist-icon { + vertical-align: sub; + font-size: $font-size-h1; + margin-right: $spacer / 2; +} + +.pluginlist-image { + width: 20px; +} + +.pluginlist-title { + margin-right: $spacer / 3; +} + +.pluginlist-version { + font-size: $font-size-sm; + color: $text-color-weak; +} + +.pluginlist-message { + float: right; + font-size: $font-size-sm; +} + +.pluginlist-message--enable{ + color: $brand-success; +} + +.pluginlist-message--no-update { + color: $text-color-weak; +} + +.pluginlist-message--update { +} From cf750c53fdffcbfcfa69ef525d538f547780a439 Mon Sep 17 00:00:00 2001 From: Matt Toback Date: Fri, 8 Apr 2016 19:07:20 -0400 Subject: [PATCH 04/13] Few more style adjustments --- .../app/plugins/panel/pluginlist/module.html | 30 +++++++------------ public/sass/components/_panel_pluginlist.scss | 8 ++++- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/public/app/plugins/panel/pluginlist/module.html b/public/app/plugins/panel/pluginlist/module.html index fcb20484d3e..b8989f3040e 100644 --- a/public/app/plugins/panel/pluginlist/module.html +++ b/public/app/plugins/panel/pluginlist/module.html @@ -15,27 +15,17 @@ Up to date - - - - {{plugin.name}} - - - Update available - - - - - - {{plugin.name}} - - - Enable now - -
Installed Panels
- +
Installed Data Sources
- +
diff --git a/public/sass/components/_panel_pluginlist.scss b/public/sass/components/_panel_pluginlist.scss index 1535ae96519..90177b101f3 100644 --- a/public/sass/components/_panel_pluginlist.scss +++ b/public/sass/components/_panel_pluginlist.scss @@ -50,5 +50,11 @@ color: $text-color-weak; } -.pluginlist-message--update { +.pluginlist-emphasis { + font-weight: 600; +} + +.pluginlist-none-installed { + color: $text-color-weak; + font-size: $font-size-sm; } From 97be3c05e68082d81c76914e1679ae6269334d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 9 Apr 2016 15:32:41 -0400 Subject: [PATCH 05/13] feat(pluginlist panel): updates to pluginlist panel, hooked up plugin type categories --- .../app/plugins/panel/pluginlist/module.html | 54 +++++++++---------- public/app/plugins/panel/pluginlist/module.ts | 9 ++++ public/sass/components/_panel_pluginlist.scss | 6 +-- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/public/app/plugins/panel/pluginlist/module.html b/public/app/plugins/panel/pluginlist/module.html index b8989f3040e..9ee9e2167cf 100644 --- a/public/app/plugins/panel/pluginlist/module.html +++ b/public/app/plugins/panel/pluginlist/module.html @@ -1,31 +1,29 @@
-
Installed Apps
- -
Installed Panels
- -
Installed Data Sources
- diff --git a/public/app/plugins/panel/pluginlist/module.ts b/public/app/plugins/panel/pluginlist/module.ts index 86108a27961..015bd2ab340 100644 --- a/public/app/plugins/panel/pluginlist/module.ts +++ b/public/app/plugins/panel/pluginlist/module.ts @@ -12,6 +12,7 @@ class DashListCtrl extends PanelCtrl { static templateUrl = 'module.html'; pluginList: any[]; + viewModel: any; /** @ngInject */ constructor($scope, $injector, private backendSrv) { @@ -20,6 +21,11 @@ class DashListCtrl extends PanelCtrl { this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); this.pluginList = []; + this.viewModel = [ + {header: "Installed Apps", list: [], type: 'app'}, + {header: "Installed Panels", list: [], type: 'panel'}, + {header: "Installed Datasources", list: [], type: 'datasource'}, + ]; this.update(); } @@ -31,6 +37,9 @@ class DashListCtrl extends PanelCtrl { update() { this.backendSrv.get('api/plugins', {embedded: 0, core: 0}).then(plugins => { this.pluginList = plugins; + this.viewModel[0].list = _.filter(plugins, {type: 'app'}); + this.viewModel[1].list = _.filter(plugins, {type: 'panel'}); + this.viewModel[2].list = _.filter(plugins, {type: 'datasource'}); for (let plugin of this.pluginList) { if (!plugin.enabled) { diff --git a/public/sass/components/_panel_pluginlist.scss b/public/sass/components/_panel_pluginlist.scss index 90177b101f3..711565a9480 100644 --- a/public/sass/components/_panel_pluginlist.scss +++ b/public/sass/components/_panel_pluginlist.scss @@ -1,10 +1,10 @@ .pluginlist-section-header { - margin: ($spacer * 2) 0 $spacer 0; + margin-bottom: $spacer; color: $text-color-weak; } -.pluginlist-section-header--first { - margin-top: $spacer /2; +.pluginlist-section { + margin-bottom: $spacer; } .pluginlist-link { From b4a8c227ccfc2914658cb32d45452303417ef8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Apr 2016 12:21:48 -0400 Subject: [PATCH 06/13] feat(update checks): started work on update checks --- conf/defaults.ini | 7 +++ conf/sample.ini | 7 +++ latest.json | 3 +- pkg/api/dtos/plugins.go | 14 +++-- pkg/plugins/models.go | 3 + pkg/plugins/plugins.go | 4 ++ pkg/plugins/update_checker.go | 109 ++++++++++++++++++++++++++++++++++ pkg/setting/setting.go | 2 + 8 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 pkg/plugins/update_checker.go diff --git a/conf/defaults.ini b/conf/defaults.ini index 851c88efc7c..6f63891d1ee 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -111,6 +111,13 @@ gc_interval_time = 86400 # Change this option to false to disable reporting. reporting_enabled = true +# Set to false to disable all checks to https://grafana.net +# for new vesions (grafana itself and plugins), check is used +# in some UI views to notify that grafana or plugin update exists +# This option does not cause any auto updates, nor send any information +# only a GET request to http://grafana.net to get latest versions +check_for_updates = true + # Google Analytics universal tracking code, only enabled if you specify an id here google_analytics_ua_id = diff --git a/conf/sample.ini b/conf/sample.ini index b875cbda093..4ab923c1376 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -100,6 +100,13 @@ # Change this option to false to disable reporting. ;reporting_enabled = true +# Set to false to disable all checks to https://grafana.net +# for new vesions (grafana itself and plugins), check is used +# in some UI views to notify that grafana or plugin update exists +# This option does not cause any auto updates, nor send any information +# only a GET request to http://grafana.net to get latest versions +check_for_updates = true + # Google Analytics universal tracking code, only enabled if you specify an id here ;google_analytics_ua_id = diff --git a/latest.json b/latest.json index 79eb42a8527..6bb5ba3e411 100644 --- a/latest.json +++ b/latest.json @@ -1,3 +1,4 @@ { - "version": "2.1.1" + "stable": "2.6.0", + "testing": "3.0.0-beta1" } diff --git a/pkg/api/dtos/plugins.go b/pkg/api/dtos/plugins.go index 53c911c10fa..ba34b851c08 100644 --- a/pkg/api/dtos/plugins.go +++ b/pkg/api/dtos/plugins.go @@ -18,12 +18,14 @@ type PluginSetting struct { } type PluginListItem struct { - Name string `json:"name"` - Type string `json:"type"` - Id string `json:"id"` - Enabled bool `json:"enabled"` - Pinned bool `json:"pinned"` - Info *plugins.PluginInfo `json:"info"` + Name string `json:"name"` + Type string `json:"type"` + Id string `json:"id"` + Enabled bool `json:"enabled"` + Pinned bool `json:"pinned"` + Info *plugins.PluginInfo `json:"info"` + LastesVersion string `json:"latestVersion"` + HasUpdate bool `json:"hasUpdate"` } type PluginList []PluginListItem diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index 30f794285e1..6b62847ed45 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -44,6 +44,9 @@ type PluginBase struct { PluginDir string `json:"-"` DefaultNavUrl string `json:"-"` + GrafanaNetVersion string `json:"-"` + GrafanaNetHasUpdate bool `json:"-"` + // cache for readme file contents Readme []byte `json:"-"` } diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index 0d69d5745b9..a6fc2d60b78 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -22,6 +22,9 @@ var ( Apps map[string]*AppPlugin Plugins map[string]*PluginBase PluginTypes map[string]interface{} + + GrafanaLatestVersion string + GrafanaHasUpdate bool ) type PluginScanner struct { @@ -70,6 +73,7 @@ func Init() error { app.initApp() } + StartPluginUpdateChecker() return nil } diff --git a/pkg/plugins/update_checker.go b/pkg/plugins/update_checker.go new file mode 100644 index 00000000000..7e8025d2987 --- /dev/null +++ b/pkg/plugins/update_checker.go @@ -0,0 +1,109 @@ +package plugins + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "strings" + "time" + + "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/setting" +) + +type GrafanaNetPlugins struct { + Plugins []GrafanaNetPlugin `json:"plugins"` +} + +type GrafanaNetPlugin struct { + Id string `json:"id"` + Versions []GrafanaNetPluginVersion `json:"versions"` +} + +type GrafanaNetPluginVersion struct { + Version string `json:"version"` +} + +type GithubLatest struct { + Stable string `json:"stable"` + Testing string `json:"testing"` +} + +func StartPluginUpdateChecker() { + if !setting.CheckForUpdates { + return + } + + ticker := time.NewTicker(time.Second * 24) + for { + select { + case <-ticker.C: + checkForUpdates() + } + } +} + +func checkForUpdates() { + log.Trace("Checking for updates") + + client := http.Client{Timeout: time.Duration(5 * time.Second)} + resp, err := client.Get("https://grafana.net/api/plugins/repo?grafanaVersion=" + setting.BuildVersion) + + if err != nil { + log.Trace("Failed to get plugins repo from grafana.net, %v", err.Error()) + return + } + + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Trace("Update check failed, reading response from grafana.net, %v", err.Error()) + return + } + + var data GrafanaNetPlugins + err = json.Unmarshal(body, &data) + if err != nil { + log.Trace("Failed to unmarshal plugin repo, reading response from grafana.net, %v", err.Error()) + return + } + + for _, plug := range Plugins { + for _, gplug := range data.Plugins { + if gplug.Id == plug.Id { + if len(gplug.Versions) > 0 { + plug.GrafanaNetVersion = gplug.Versions[0].Version + plug.GrafanaNetHasUpdate = plug.Info.Version != plug.GrafanaNetVersion + } + } + } + } + + resp2, err := client.Get("https://raw.githubusercontent.com/grafana/grafana/master/latest.json") + if err != nil { + log.Trace("Failed to get lates.json repo from github: %v", err.Error()) + return + } + + defer resp2.Body.Close() + body, err = ioutil.ReadAll(resp2.Body) + if err != nil { + log.Trace("Update check failed, reading response from github.net, %v", err.Error()) + return + } + + var githubLatest GithubLatest + err = json.Unmarshal(body, &githubLatest) + if err != nil { + log.Trace("Failed to unmarshal github latest, reading response from github: %v", err.Error()) + return + } + + if strings.Contains(setting.BuildVersion, "-") { + GrafanaLatestVersion = githubLatest.Testing + } else { + GrafanaLatestVersion = githubLatest.Stable + GrafanaHasUpdate = githubLatest.Stable != setting.BuildVersion + } +} diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index b8e8d7f7fcf..2d1bad945eb 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -124,6 +124,7 @@ var ( appliedEnvOverrides []string ReportingEnabled bool + CheckForUpdates bool GoogleAnalyticsId string GoogleTagManagerId string @@ -475,6 +476,7 @@ func NewConfigContext(args *CommandLineArgs) error { analytics := Cfg.Section("analytics") ReportingEnabled = analytics.Key("reporting_enabled").MustBool(true) + CheckForUpdates = analytics.Key("check_for_updates").MustBool(true) GoogleAnalyticsId = analytics.Key("google_analytics_ua_id").String() GoogleTagManagerId = analytics.Key("google_tag_manager_id").String() From 6ac3bd4c7ccae71ce437ef19b01b9462d1758016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Apr 2016 12:47:04 -0400 Subject: [PATCH 07/13] feat(): update checks starting to work --- pkg/api/dtos/plugins.go | 5 ++++- pkg/api/plugins.go | 12 ++++++++---- pkg/plugins/plugins.go | 2 +- pkg/plugins/update_checker.go | 3 ++- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/pkg/api/dtos/plugins.go b/pkg/api/dtos/plugins.go index ba34b851c08..fbdf8d4e0ea 100644 --- a/pkg/api/dtos/plugins.go +++ b/pkg/api/dtos/plugins.go @@ -15,6 +15,9 @@ type PluginSetting struct { Dependencies *plugins.PluginDependencies `json:"dependencies"` JsonData map[string]interface{} `json:"jsonData"` DefaultNavUrl string `json:"defaultNavUrl"` + + LatestVersion string `json:"latestVersion"` + HasUpdate bool `json:"hasUpdate"` } type PluginListItem struct { @@ -24,7 +27,7 @@ type PluginListItem struct { Enabled bool `json:"enabled"` Pinned bool `json:"pinned"` Info *plugins.PluginInfo `json:"info"` - LastesVersion string `json:"latestVersion"` + LatestVersion string `json:"latestVersion"` HasUpdate bool `json:"hasUpdate"` } diff --git a/pkg/api/plugins.go b/pkg/api/plugins.go index 0411c0746ef..858567d1624 100644 --- a/pkg/api/plugins.go +++ b/pkg/api/plugins.go @@ -34,10 +34,12 @@ func GetPluginList(c *middleware.Context) Response { } listItem := dtos.PluginListItem{ - Id: pluginDef.Id, - Name: pluginDef.Name, - Type: pluginDef.Type, - Info: &pluginDef.Info, + Id: pluginDef.Id, + Name: pluginDef.Name, + Type: pluginDef.Type, + Info: &pluginDef.Info, + LatestVersion: pluginDef.GrafanaNetVersion, + HasUpdate: pluginDef.GrafanaNetHasUpdate, } if pluginSetting, exists := pluginSettingsMap[pluginDef.Id]; exists { @@ -81,6 +83,8 @@ func GetPluginSettingById(c *middleware.Context) Response { BaseUrl: def.BaseUrl, Module: def.Module, DefaultNavUrl: def.DefaultNavUrl, + LatestVersion: def.GrafanaNetVersion, + HasUpdate: def.GrafanaNetHasUpdate, } query := m.GetPluginSettingByIdQuery{PluginId: pluginId, OrgId: c.OrgId} diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index a6fc2d60b78..073685afc79 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -73,7 +73,7 @@ func Init() error { app.initApp() } - StartPluginUpdateChecker() + go StartPluginUpdateChecker() return nil } diff --git a/pkg/plugins/update_checker.go b/pkg/plugins/update_checker.go index 7e8025d2987..1d13df1a629 100644 --- a/pkg/plugins/update_checker.go +++ b/pkg/plugins/update_checker.go @@ -47,7 +47,7 @@ func checkForUpdates() { log.Trace("Checking for updates") client := http.Client{Timeout: time.Duration(5 * time.Second)} - resp, err := client.Get("https://grafana.net/api/plugins/repo?grafanaVersion=" + setting.BuildVersion) + resp, err := client.Get("https://grafana.net/api/plugins/repo") if err != nil { log.Trace("Failed to get plugins repo from grafana.net, %v", err.Error()) @@ -69,6 +69,7 @@ func checkForUpdates() { return } + log.Info("GNET PLUG: %v", len(data.Plugins)) for _, plug := range Plugins { for _, gplug := range data.Plugins { if gplug.Id == plug.Id { From ab34b174f9b76e390a50e4842269ba49a388077a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Apr 2016 13:42:52 -0400 Subject: [PATCH 08/13] Added update notice to plugin list --- pkg/api/frontendsettings.go | 8 +++++--- pkg/plugins/update_checker.go | 2 +- public/app/core/controllers/login_ctrl.js | 4 +++- .../features/plugins/partials/ds_list.html | 5 +++-- .../plugins/partials/plugin_list.html | 9 +++++++-- public/app/partials/login.html | 6 +++--- public/sass/components/_cards.scss | 20 ++++++++++++++++++- 7 files changed, 41 insertions(+), 13 deletions(-) diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index f23f416b47a..dd84f7827eb 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -137,9 +137,11 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro "allowOrgCreate": (setting.AllowUserOrgCreate && c.IsSignedIn) || c.IsGrafanaAdmin, "authProxyEnabled": setting.AuthProxyEnabled, "buildInfo": map[string]interface{}{ - "version": setting.BuildVersion, - "commit": setting.BuildCommit, - "buildstamp": setting.BuildStamp, + "version": setting.BuildVersion, + "commit": setting.BuildCommit, + "buildstamp": setting.BuildStamp, + "latestVersion": plugins.GrafanaLatestVersion, + "hasUpdate": plugins.GrafanaHasUpdate, }, } diff --git a/pkg/plugins/update_checker.go b/pkg/plugins/update_checker.go index 1d13df1a629..6d3626fff16 100644 --- a/pkg/plugins/update_checker.go +++ b/pkg/plugins/update_checker.go @@ -69,7 +69,6 @@ func checkForUpdates() { return } - log.Info("GNET PLUG: %v", len(data.Plugins)) for _, plug := range Plugins { for _, gplug := range data.Plugins { if gplug.Id == plug.Id { @@ -103,6 +102,7 @@ func checkForUpdates() { if strings.Contains(setting.BuildVersion, "-") { GrafanaLatestVersion = githubLatest.Testing + GrafanaHasUpdate = githubLatest.Testing != setting.BuildVersion } else { GrafanaLatestVersion = githubLatest.Stable GrafanaHasUpdate = githubLatest.Stable != setting.BuildVersion diff --git a/public/app/core/controllers/login_ctrl.js b/public/app/core/controllers/login_ctrl.js index 89757c3d141..4249d55a44f 100644 --- a/public/app/core/controllers/login_ctrl.js +++ b/public/app/core/controllers/login_ctrl.js @@ -39,7 +39,9 @@ function (angular, coreModule, config) { $scope.buildInfo = { version: config.buildInfo.version, commit: config.buildInfo.commit, - buildstamp: new Date(config.buildInfo.buildstamp * 1000) + buildstamp: new Date(config.buildInfo.buildstamp * 1000), + latestVersion: config.buildInfo.latestVersion, + hasUpdate: config.buildInfo.hasUpdate, }; $scope.submit = function() { diff --git a/public/app/features/plugins/partials/ds_list.html b/public/app/features/plugins/partials/ds_list.html index c5ca9f514a4..c4625abfe6b 100644 --- a/public/app/features/plugins/partials/ds_list.html +++ b/public/app/features/plugins/partials/ds_list.html @@ -20,8 +20,9 @@
  • - - {{ds.type}} +
    + {{ds.type}} +
  • -
    - - diff --git a/public/sass/components/_cards.scss b/public/sass/components/_cards.scss index 08baf146c93..40d6c0ab85e 100644 --- a/public/sass/components/_cards.scss +++ b/public/sass/components/_cards.scss @@ -76,13 +76,20 @@ } .card-item-header { + margin-bottom: $spacer; +} + +.card-item-type { color: $text-color-weak; text-transform: uppercase; - margin-bottom: $spacer; font-size: $font-size-sm; font-weight: bold; } +.card-item-notice { + font-size: $font-size-sm; +} + .card-item-name { color: $headings-color; overflow: hidden; @@ -107,6 +114,16 @@ .card-list-layout-grid { + .card-item-type { + display: inline-block; + } + + .card-item-notice { + font-size: $font-size-sm; + display: inline-block; + margin-left: $spacer; + } + .card-item-header-action { float: right; } @@ -165,6 +182,7 @@ .card-item-header { float: right; + text-align: right; } .card-item-figure { From 451a8bef39b6f7996cefa53ebd95649b9a9b7206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Apr 2016 14:17:38 -0400 Subject: [PATCH 09/13] feat(pluginslist): minor update --- .../plugins/partials/plugin_edit.html | 5 ++++ .../app/plugins/panel/pluginlist/module.html | 7 +++-- public/app/plugins/panel/pluginlist/module.ts | 27 +++---------------- public/sass/components/_panel_pluginlist.scss | 2 +- 4 files changed, 13 insertions(+), 28 deletions(-) diff --git a/public/app/features/plugins/partials/plugin_edit.html b/public/app/features/plugins/partials/plugin_edit.html index d5f69df2366..46971bd35e7 100644 --- a/public/app/features/plugins/partials/plugin_edit.html +++ b/public/app/features/plugins/partials/plugin_edit.html @@ -55,6 +55,11 @@

    Version

    {{ctrl.model.info.version}} +
    Includes
    diff --git a/public/app/plugins/panel/pluginlist/module.html b/public/app/plugins/panel/pluginlist/module.html index 9ee9e2167cf..05869911d73 100644 --- a/public/app/plugins/panel/pluginlist/module.html +++ b/public/app/plugins/panel/pluginlist/module.html @@ -1,9 +1,8 @@
    -
    - +
    {{category.header}} -
    +
    @@ -12,7 +11,7 @@ Update available! - + Enable now diff --git a/public/app/plugins/panel/pluginlist/module.ts b/public/app/plugins/panel/pluginlist/module.ts index 015bd2ab340..9a9c544a7ec 100644 --- a/public/app/plugins/panel/pluginlist/module.ts +++ b/public/app/plugins/panel/pluginlist/module.ts @@ -26,6 +26,7 @@ class DashListCtrl extends PanelCtrl { {header: "Installed Panels", list: [], type: 'panel'}, {header: "Installed Datasources", list: [], type: 'datasource'}, ]; + this.update(); } @@ -42,30 +43,10 @@ class DashListCtrl extends PanelCtrl { this.viewModel[2].list = _.filter(plugins, {type: 'datasource'}); for (let plugin of this.pluginList) { - if (!plugin.enabled) { - plugin.state = 'not-enabled'; - } - } - - }).then(this.checkForUpdates.bind(this)); - } - - checkForUpdates() { - return this.backendSrv.get('api/gnet/plugins/repo').then(data => { - var gNetPlugins = _.reduce(data.plugins, (memo, val) => { - memo[val.id] = val; - return memo; - }, {}); - - for (let plugin of this.pluginList) { - var source = gNetPlugins[plugin.id]; - if (!source) { - continue; - } - - if (plugin.info.version !== source.versions[0].version) { - plugin.hasUpdate = true; + if (plugin.hasUpdate) { plugin.state = 'has-update'; + } else if (!plugin.enabled) { + plugin.state = 'not-enabled'; } } }); diff --git a/public/sass/components/_panel_pluginlist.scss b/public/sass/components/_panel_pluginlist.scss index 711565a9480..0d02196e975 100644 --- a/public/sass/components/_panel_pluginlist.scss +++ b/public/sass/components/_panel_pluginlist.scss @@ -29,7 +29,7 @@ } .pluginlist-title { - margin-right: $spacer / 3; + margin-right: $spacer / 3; } .pluginlist-version { From 31441f0b4361d7baef3a637d5a3815194c8636d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Apr 2016 16:21:25 -0400 Subject: [PATCH 10/13] feat(dashlist): updated dashlist --- public/app/core/components/switch.ts | 7 +- .../features/panel/partials/panelTime.html | 2 +- public/app/plugins/panel/dashlist/editor.html | 58 +++++----- public/app/plugins/panel/dashlist/module.html | 27 +++-- public/app/plugins/panel/dashlist/module.js | 91 ++++++++++++++++ public/app/plugins/panel/dashlist/module.ts | 102 +++++++++++++----- public/dashboards/home.json | 58 +++++----- public/sass/components/_panel_dashlist.scss | 8 +- public/sass/pages/_dashboard.scss | 4 +- tsconfig.json | 30 +----- 10 files changed, 254 insertions(+), 133 deletions(-) create mode 100644 public/app/plugins/panel/dashlist/module.js diff --git a/public/app/core/components/switch.ts b/public/app/core/components/switch.ts index 10211b5b914..e2ba60d92e8 100644 --- a/public/app/core/components/switch.ts +++ b/public/app/core/components/switch.ts @@ -27,11 +27,8 @@ export class SwitchCtrl { } internalOnChange() { - return new Promise(resolve => { - this.$timeout(() => { - this.onChange(); - resolve(); - }); + return this.$timeout(() => { + return this.onChange(); }); } diff --git a/public/app/features/panel/partials/panelTime.html b/public/app/features/panel/partials/panelTime.html index 4047558317f..18b9cbbda8e 100644 --- a/public/app/features/panel/partials/panelTime.html +++ b/public/app/features/panel/partials/panelTime.html @@ -31,7 +31,7 @@
    + checked="ctrl.panel.hideTimeOverride" switch-class="max-width-6" on-change="ctrl.refresh()">
    diff --git a/public/app/plugins/panel/dashlist/editor.html b/public/app/plugins/panel/dashlist/editor.html index c0577578598..d2159093476 100644 --- a/public/app/plugins/panel/dashlist/editor.html +++ b/public/app/plugins/panel/dashlist/editor.html @@ -1,40 +1,32 @@ -
    -
    -
    - Mode -
    - -
    -
    -
    - - - -
    -
    +
    +
    +
    Options
    -
    -
    - Search options - Query + + + - + -
    +
    + Max items + +
    +
    -
    - Tags +
    +
    Search
    - - -
    -
    +
    + Query + +
    + +
    + Tags + + +
    +
    -
    -
    - Limit number to - -
    -
    diff --git a/public/app/plugins/panel/dashlist/module.html b/public/app/plugins/panel/dashlist/module.html index 79952d0032c..b5c59862e5d 100644 --- a/public/app/plugins/panel/dashlist/module.html +++ b/public/app/plugins/panel/dashlist/module.html @@ -1,12 +1,17 @@ -
    - +
    +
    +
    + {{group.header}} +
    + +
    diff --git a/public/app/plugins/panel/dashlist/module.js b/public/app/plugins/panel/dashlist/module.js new file mode 100644 index 00000000000..5bc42c505fd --- /dev/null +++ b/public/app/plugins/panel/dashlist/module.js @@ -0,0 +1,91 @@ +/// +"use strict"; +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var lodash_1 = require('lodash'); +var sdk_1 = require('app/plugins/sdk'); +var impression_store_1 = require('app/features/dashboard/impression_store'); +// Set and populate defaults +var panelDefaults = { + mode: 'starred', + query: '', + limit: 10, + tags: [], + recent: false, + search: false, + starred: true +}; +var DashListCtrl = (function (_super) { + __extends(DashListCtrl, _super); + /** @ngInject */ + function DashListCtrl($scope, $injector, backendSrv) { + _super.call(this, $scope, $injector); + this.backendSrv = backendSrv; + lodash_1["default"].defaults(this.panel, panelDefaults); + if (this.panel.tag) { + this.panel.tags = [this.panel.tag]; + delete this.panel.tag; + } + this.events.on('refresh', this.onRefresh.bind(this)); + this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); + } + DashListCtrl.prototype.onInitEditMode = function () { + this.editorTabIndex = 1; + this.modes = ['starred', 'search', 'recently viewed']; + this.addEditorTab('Options', 'public/app/plugins/panel/dashlist/editor.html'); + }; + DashListCtrl.prototype.onRefresh = function () { + var promises = []; + if (this.panel.recent) { + promises.push(this.getRecentDashboards()); + } + if (this.panel.starred) { + promises.push(this.getStarred()); + } + if (this.panel.search) { + promises.push(this.getSearch()); + } + return Promise.all(promises) + .then(this.renderingCompleted.bind(this)); + }; + DashListCtrl.prototype.getSearch = function () { + var _this = this; + var params = { + limit: this.panel.limit, + query: this.panel.query, + tag: this.panel.tags + }; + return this.backendSrv.search(params).then(function (result) { + _this.dashList = result; + _this.renderingCompleted(); + }); + }; + DashListCtrl.prototype.getStarred = function () { + var _this = this; + var params = { limit: this.panel.limit, starred: "true" }; + return this.backendSrv.search(params).then(function (result) { + _this.dashList = result; + _this.renderingCompleted(); + }); + }; + DashListCtrl.prototype.getRecentDashboards = function () { + var _this = this; + var dashIds = lodash_1["default"].first(impression_store_1.impressions.getDashboardOpened(), this.panel.limit); + return this.backendSrv.search({ dashboardIds: dashIds, limit: this.panel.limit }).then(function (result) { + _this.dashList = dashIds.map(function (orderId) { + return lodash_1["default"].find(result, function (dashboard) { + return dashboard.id === orderId; + }); + }).filter(function (el) { + return el !== undefined; + }); + }); + }; + DashListCtrl.templateUrl = 'module.html'; + return DashListCtrl; +}(sdk_1.PanelCtrl)); +exports.DashListCtrl = DashListCtrl; +exports.PanelCtrl = DashListCtrl; diff --git a/public/app/plugins/panel/dashlist/module.ts b/public/app/plugins/panel/dashlist/module.ts index b619b7ea7d7..77029bd7ceb 100644 --- a/public/app/plugins/panel/dashlist/module.ts +++ b/public/app/plugins/panel/dashlist/module.ts @@ -7,16 +7,19 @@ import {impressions} from 'app/features/dashboard/impression_store'; // Set and populate defaults var panelDefaults = { - mode: 'starred', query: '', limit: 10, - tags: [] + tags: [], + recent: false, + search: false, + starred: true, + headings: true, }; class DashListCtrl extends PanelCtrl { static templateUrl = 'module.html'; - dashList: any[]; + groups: any[]; modes: any[]; /** @ngInject */ @@ -31,6 +34,31 @@ class DashListCtrl extends PanelCtrl { this.events.on('refresh', this.onRefresh.bind(this)); this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); + + this.groups = [ + {list: [], show: false, header: "Starred dashboards",}, + {list: [], show: false, header: "Recently viewed dashboards"}, + {list: [], show: false, header: "Search"}, + ]; + + // update capability + if (this.panel.mode) { + if (this.panel.mode === 'starred') { + this.panel.starred = true; + this.panel.headings = false; + } + if (this.panel.mode === 'recently viewed') { + this.panel.recent = true; + this.panel.starred = false; + this.panel.headings = false; + } + if (this.panel.mode === 'search') { + this.panel.search = true; + this.panel.starred = false; + this.panel.headings = false; + } + delete this.panel.mode; + } } onInitEditMode() { @@ -40,34 +68,60 @@ class DashListCtrl extends PanelCtrl { } onRefresh() { - var params: any = {limit: this.panel.limit}; + var promises = []; - if (this.panel.mode === 'recently viewed') { - var dashIds = _.first(impressions.getDashboardOpened(), this.panel.limit); + promises.push(this.getRecentDashboards()); + promises.push(this.getStarred()); + promises.push(this.getSearch()); - return this.backendSrv.search({dashboardIds: dashIds, limit: this.panel.limit}).then(result => { - this.dashList = dashIds.map(orderId => { - return _.find(result, dashboard => { - return dashboard.id === orderId; - }); - }).filter(el => { - return el !== undefined; - }); + return Promise.all(promises) + .then(this.renderingCompleted.bind(this)); + } - this.renderingCompleted(); - }); + getSearch() { + this.groups[2].show = this.panel.search; + if (!this.panel.search) { + return Promise.resolve(); } - if (this.panel.mode === 'starred') { - params.starred = "true"; - } else { - params.query = this.panel.query; - params.tag = this.panel.tags; - } + var params = { + limit: this.panel.limit, + query: this.panel.query, + tag: this.panel.tags, + }; return this.backendSrv.search(params).then(result => { - this.dashList = result; - this.renderingCompleted(); + this.groups[2].list = result; + }); + } + + getStarred() { + this.groups[0].show = this.panel.starred; + if (!this.panel.starred) { + return Promise.resolve(); + } + + var params = {limit: this.panel.limit, starred: "true"}; + return this.backendSrv.search(params).then(result => { + this.groups[0].list = result; + }); + } + + getRecentDashboards() { + this.groups[1].show = this.panel.recent; + if (!this.panel.recent) { + return Promise.resolve(); + } + + var dashIds = _.first(impressions.getDashboardOpened(), this.panel.limit); + return this.backendSrv.search({dashboardIds: dashIds, limit: this.panel.limit}).then(result => { + this.groups[1].list = dashIds.map(orderId => { + return _.find(result, dashboard => { + return dashboard.id === orderId; + }); + }).filter(el => { + return el !== undefined; + }); }); } } diff --git a/public/dashboards/home.json b/public/dashboards/home.json index 5825f849375..c3ed1017bad 100644 --- a/public/dashboards/home.json +++ b/public/dashboards/home.json @@ -9,65 +9,61 @@ "hideControls": true, "sharedCrosshair": false, "rows": [ - { + { "collapse": false, "editable": true, - "height": "90px", + "height": "25px", "panels": [ { "content": "
    \n Home Dashboard\n
    ", "editable": true, "id": 1, + "links": [], "mode": "html", "span": 12, "style": {}, "title": "", "transparent": true, - "type": "text", - "links": [] + "type": "text" } ], "title": "New row" - }, - { + }, + { "collapse": false, "editable": true, "height": "510px", "panels": [ - { - "id": 2, - "limit": 10, - "mode": "starred", - "query": "", - "span": 3.75, - "tags": [], - "title": "Starred dashboards", - "type": "dashlist" - }, { "id": 3, - "limit": 10, - "mode": "recently viewed", + "limit": 4, + "links": [], "query": "", - "span": 3.75, + "span": 7, "tags": [], - "title": "Recently viewed dashboards", - "type": "dashlist" + "title": "", + "transparent": false, + "type": "dashlist", + "recent": true, + "search": false, + "starred": true, + "headings": true }, { - "title": "", - "error": false, - "span": 4.5, "editable": true, - "type": "pluginlist", - "isNew": true, + "error": false, "id": 4, - "links": [] + "isNew": true, + "links": [], + "span": 5, + "title": "", + "transparent": false, + "type": "pluginlist" } ], "title": "Row" - } - ], + } + ], "time": { "from": "now-6h", "to": "now" @@ -105,7 +101,7 @@ "annotations": { "list": [] }, - "schemaVersion": 9, - "version": 5, + "schemaVersion": 12, + "version": 2, "links": [] } diff --git a/public/sass/components/_panel_dashlist.scss b/public/sass/components/_panel_dashlist.scss index a69d1654e04..4256c80ac82 100644 --- a/public/sass/components/_panel_dashlist.scss +++ b/public/sass/components/_panel_dashlist.scss @@ -1,5 +1,11 @@ -.dashlist-item { +.dashlist-section-header { + margin-bottom: $spacer; + color: $text-color-weak; +} + +.dashlist-section { + margin-bottom: $spacer; } .dashlist-link { diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index 2b04f0e9b29..9249af41077 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -278,11 +278,11 @@ div.flot-text { .dashboard-header { font-family: $headings-font-family; - font-size: $font-size-h2; + font-size: $font-size-h3; text-align: center; span { display: inline-block; @include brand-bottom-border(); - padding: 1.2rem .5rem .4rem .5rem; + padding: 0.5rem .5rem .2rem .5rem; } } diff --git a/tsconfig.json b/tsconfig.json index 43a3888f711..0afaa053449 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,33 +6,13 @@ "noImplicitAny": false, "target": "es5", "rootDir": "public/", + "sourceRoot": "public/", "module": "system", "noEmitOnError": true, - "emitDecoratorMetadata": true + "emitDecoratorMetadata": true, + "experimentalDecorators": true }, "files": [ - "public/app/app.ts", - "public/app/core/controllers/grafana_ctrl.ts", - "public/app/core/controllers/signup_ctrl.ts", - "public/app/core/core.ts", - "public/app/core/core_module.ts", - "public/app/core/directives/array_join.ts", - "public/app/core/directives/give_focus.ts", - "public/app/core/filters/filters.ts", - "public/app/core/routes/bundle_loader.ts", - "public/app/core/table_model.ts", - "public/app/core/time_series.ts", - "public/app/core/utils/datemath.ts", - "public/app/core/utils/flatten.ts", - "public/app/core/utils/rangeutil.ts", - "public/app/features/dashboard/timepicker/timepicker.ts", - "public/app/features/panel/panel_meta.ts", - "public/app/plugins/datasource/influxdb/influx_query.ts", - "public/app/plugins/datasource/influxdb/query_part.ts", - "public/app/plugins/panels/table/controller.ts", - "public/app/plugins/panels/table/editor.ts", - "public/app/plugins/panels/table/module.ts", - "public/app/plugins/panels/table/renderer.ts", - "public/app/plugins/panels/table/transformers.ts" + "public/app/**/*.ts" ] -} \ No newline at end of file +} From 3849b5962792eb72ba315ccd9a4a1f1086a3ed7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Apr 2016 17:16:52 -0400 Subject: [PATCH 11/13] feat(updates): changed to new api --- pkg/plugins/update_checker.go | 42 ++++++---- public/app/plugins/panel/dashlist/module.js | 91 --------------------- 2 files changed, 24 insertions(+), 109 deletions(-) delete mode 100644 public/app/plugins/panel/dashlist/module.js diff --git a/pkg/plugins/update_checker.go b/pkg/plugins/update_checker.go index 6d3626fff16..b619310a918 100644 --- a/pkg/plugins/update_checker.go +++ b/pkg/plugins/update_checker.go @@ -11,16 +11,8 @@ import ( "github.com/grafana/grafana/pkg/setting" ) -type GrafanaNetPlugins struct { - Plugins []GrafanaNetPlugin `json:"plugins"` -} - type GrafanaNetPlugin struct { - Id string `json:"id"` - Versions []GrafanaNetPluginVersion `json:"versions"` -} - -type GrafanaNetPluginVersion struct { + Slug string `json:"slug"` Version string `json:"version"` } @@ -43,11 +35,27 @@ func StartPluginUpdateChecker() { } } +func getAllExternalPluginSlugs() string { + str := "" + + for _, plug := range Plugins { + if plug.IsCorePlugin { + continue + } + + str += plug.Id + "," + } + + return str +} + func checkForUpdates() { log.Trace("Checking for updates") client := http.Client{Timeout: time.Duration(5 * time.Second)} - resp, err := client.Get("https://grafana.net/api/plugins/repo") + + pluginSlugs := getAllExternalPluginSlugs() + resp, err := client.Get("https://grafana.net/api/plugins/versioncheck?slugIn=" + pluginSlugs + "&grafanaVersion=" + setting.BuildVersion) if err != nil { log.Trace("Failed to get plugins repo from grafana.net, %v", err.Error()) @@ -62,20 +70,18 @@ func checkForUpdates() { return } - var data GrafanaNetPlugins - err = json.Unmarshal(body, &data) + gNetPlugins := []GrafanaNetPlugin{} + err = json.Unmarshal(body, &gNetPlugins) if err != nil { log.Trace("Failed to unmarshal plugin repo, reading response from grafana.net, %v", err.Error()) return } for _, plug := range Plugins { - for _, gplug := range data.Plugins { - if gplug.Id == plug.Id { - if len(gplug.Versions) > 0 { - plug.GrafanaNetVersion = gplug.Versions[0].Version - plug.GrafanaNetHasUpdate = plug.Info.Version != plug.GrafanaNetVersion - } + for _, gplug := range gNetPlugins { + if gplug.Slug == plug.Id { + plug.GrafanaNetVersion = gplug.Version + plug.GrafanaNetHasUpdate = plug.Info.Version != plug.GrafanaNetVersion } } } diff --git a/public/app/plugins/panel/dashlist/module.js b/public/app/plugins/panel/dashlist/module.js deleted file mode 100644 index 5bc42c505fd..00000000000 --- a/public/app/plugins/panel/dashlist/module.js +++ /dev/null @@ -1,91 +0,0 @@ -/// -"use strict"; -var __extends = (this && this.__extends) || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -}; -var lodash_1 = require('lodash'); -var sdk_1 = require('app/plugins/sdk'); -var impression_store_1 = require('app/features/dashboard/impression_store'); -// Set and populate defaults -var panelDefaults = { - mode: 'starred', - query: '', - limit: 10, - tags: [], - recent: false, - search: false, - starred: true -}; -var DashListCtrl = (function (_super) { - __extends(DashListCtrl, _super); - /** @ngInject */ - function DashListCtrl($scope, $injector, backendSrv) { - _super.call(this, $scope, $injector); - this.backendSrv = backendSrv; - lodash_1["default"].defaults(this.panel, panelDefaults); - if (this.panel.tag) { - this.panel.tags = [this.panel.tag]; - delete this.panel.tag; - } - this.events.on('refresh', this.onRefresh.bind(this)); - this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); - } - DashListCtrl.prototype.onInitEditMode = function () { - this.editorTabIndex = 1; - this.modes = ['starred', 'search', 'recently viewed']; - this.addEditorTab('Options', 'public/app/plugins/panel/dashlist/editor.html'); - }; - DashListCtrl.prototype.onRefresh = function () { - var promises = []; - if (this.panel.recent) { - promises.push(this.getRecentDashboards()); - } - if (this.panel.starred) { - promises.push(this.getStarred()); - } - if (this.panel.search) { - promises.push(this.getSearch()); - } - return Promise.all(promises) - .then(this.renderingCompleted.bind(this)); - }; - DashListCtrl.prototype.getSearch = function () { - var _this = this; - var params = { - limit: this.panel.limit, - query: this.panel.query, - tag: this.panel.tags - }; - return this.backendSrv.search(params).then(function (result) { - _this.dashList = result; - _this.renderingCompleted(); - }); - }; - DashListCtrl.prototype.getStarred = function () { - var _this = this; - var params = { limit: this.panel.limit, starred: "true" }; - return this.backendSrv.search(params).then(function (result) { - _this.dashList = result; - _this.renderingCompleted(); - }); - }; - DashListCtrl.prototype.getRecentDashboards = function () { - var _this = this; - var dashIds = lodash_1["default"].first(impression_store_1.impressions.getDashboardOpened(), this.panel.limit); - return this.backendSrv.search({ dashboardIds: dashIds, limit: this.panel.limit }).then(function (result) { - _this.dashList = dashIds.map(function (orderId) { - return lodash_1["default"].find(result, function (dashboard) { - return dashboard.id === orderId; - }); - }).filter(function (el) { - return el !== undefined; - }); - }); - }; - DashListCtrl.templateUrl = 'module.html'; - return DashListCtrl; -}(sdk_1.PanelCtrl)); -exports.DashListCtrl = DashListCtrl; -exports.PanelCtrl = DashListCtrl; From f4fc3f48a73b0724c6578de8e1fd88c340451663 Mon Sep 17 00:00:00 2001 From: Matt Toback Date: Mon, 11 Apr 2016 19:01:18 -0400 Subject: [PATCH 12/13] New modal for upgrades and updated styles on list --- .../plugins/partials/update_instructions.html | 21 +++++ .../app/features/plugins/plugin_edit_ctrl.ts | 3 +- public/app/plugins/panel/dashlist/module.js | 91 ------------------- .../app/plugins/panel/pluginlist/module.html | 16 ++-- public/app/plugins/panel/pluginlist/module.ts | 22 ++++- public/sass/_variables.dark.scss | 3 +- public/sass/_variables.light.scss | 2 +- public/sass/base/_code.scss | 11 ++- public/sass/base/_type.scss | 2 +- public/sass/components/_panel_pluginlist.scss | 17 +++- 10 files changed, 79 insertions(+), 109 deletions(-) create mode 100644 public/app/features/plugins/partials/update_instructions.html delete mode 100644 public/app/plugins/panel/dashlist/module.js diff --git a/public/app/features/plugins/partials/update_instructions.html b/public/app/features/plugins/partials/update_instructions.html new file mode 100644 index 00000000000..7027ebf7154 --- /dev/null +++ b/public/app/features/plugins/partials/update_instructions.html @@ -0,0 +1,21 @@ + +
    diff --git a/public/app/features/plugins/plugin_edit_ctrl.ts b/public/app/features/plugins/plugin_edit_ctrl.ts index 7f3f6bff08f..fd6eed4e2cf 100644 --- a/public/app/features/plugins/plugin_edit_ctrl.ts +++ b/public/app/features/plugins/plugin_edit_ctrl.ts @@ -73,7 +73,7 @@ export class PluginEditCtrl { case 'datasource': return 'icon-gf icon-gf-datasources'; case 'panel': return 'icon-gf icon-gf-panel'; case 'app': return 'icon-gf icon-gf-apps'; - case 'page': return 'icon-gf icon-gf-share'; + case 'page': return 'icon-gf icon-gf-endpoint-tiny'; case 'dashboard': return 'icon-gf icon-gf-dashboard'; } } @@ -142,4 +142,3 @@ export class PluginEditCtrl { } angular.module('grafana.controllers').controller('PluginEditCtrl', PluginEditCtrl); - diff --git a/public/app/plugins/panel/dashlist/module.js b/public/app/plugins/panel/dashlist/module.js deleted file mode 100644 index 5bc42c505fd..00000000000 --- a/public/app/plugins/panel/dashlist/module.js +++ /dev/null @@ -1,91 +0,0 @@ -/// -"use strict"; -var __extends = (this && this.__extends) || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -}; -var lodash_1 = require('lodash'); -var sdk_1 = require('app/plugins/sdk'); -var impression_store_1 = require('app/features/dashboard/impression_store'); -// Set and populate defaults -var panelDefaults = { - mode: 'starred', - query: '', - limit: 10, - tags: [], - recent: false, - search: false, - starred: true -}; -var DashListCtrl = (function (_super) { - __extends(DashListCtrl, _super); - /** @ngInject */ - function DashListCtrl($scope, $injector, backendSrv) { - _super.call(this, $scope, $injector); - this.backendSrv = backendSrv; - lodash_1["default"].defaults(this.panel, panelDefaults); - if (this.panel.tag) { - this.panel.tags = [this.panel.tag]; - delete this.panel.tag; - } - this.events.on('refresh', this.onRefresh.bind(this)); - this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); - } - DashListCtrl.prototype.onInitEditMode = function () { - this.editorTabIndex = 1; - this.modes = ['starred', 'search', 'recently viewed']; - this.addEditorTab('Options', 'public/app/plugins/panel/dashlist/editor.html'); - }; - DashListCtrl.prototype.onRefresh = function () { - var promises = []; - if (this.panel.recent) { - promises.push(this.getRecentDashboards()); - } - if (this.panel.starred) { - promises.push(this.getStarred()); - } - if (this.panel.search) { - promises.push(this.getSearch()); - } - return Promise.all(promises) - .then(this.renderingCompleted.bind(this)); - }; - DashListCtrl.prototype.getSearch = function () { - var _this = this; - var params = { - limit: this.panel.limit, - query: this.panel.query, - tag: this.panel.tags - }; - return this.backendSrv.search(params).then(function (result) { - _this.dashList = result; - _this.renderingCompleted(); - }); - }; - DashListCtrl.prototype.getStarred = function () { - var _this = this; - var params = { limit: this.panel.limit, starred: "true" }; - return this.backendSrv.search(params).then(function (result) { - _this.dashList = result; - _this.renderingCompleted(); - }); - }; - DashListCtrl.prototype.getRecentDashboards = function () { - var _this = this; - var dashIds = lodash_1["default"].first(impression_store_1.impressions.getDashboardOpened(), this.panel.limit); - return this.backendSrv.search({ dashboardIds: dashIds, limit: this.panel.limit }).then(function (result) { - _this.dashList = dashIds.map(function (orderId) { - return lodash_1["default"].find(result, function (dashboard) { - return dashboard.id === orderId; - }); - }).filter(function (el) { - return el !== undefined; - }); - }); - }; - DashListCtrl.templateUrl = 'module.html'; - return DashListCtrl; -}(sdk_1.PanelCtrl)); -exports.DashListCtrl = DashListCtrl; -exports.PanelCtrl = DashListCtrl; diff --git a/public/app/plugins/panel/pluginlist/module.html b/public/app/plugins/panel/pluginlist/module.html index 05869911d73..44e32d19477 100644 --- a/public/app/plugins/panel/pluginlist/module.html +++ b/public/app/plugins/panel/pluginlist/module.html @@ -4,20 +4,22 @@ {{category.header}}
    diff --git a/public/app/plugins/panel/pluginlist/module.ts b/public/app/plugins/panel/pluginlist/module.ts index 9a9c544a7ec..9ad43b25e56 100644 --- a/public/app/plugins/panel/pluginlist/module.ts +++ b/public/app/plugins/panel/pluginlist/module.ts @@ -8,14 +8,14 @@ import {PanelCtrl} from '../../../features/panel/panel_ctrl'; var panelDefaults = { }; -class DashListCtrl extends PanelCtrl { +class PluginListCtrl extends PanelCtrl { static templateUrl = 'module.html'; pluginList: any[]; viewModel: any; /** @ngInject */ - constructor($scope, $injector, private backendSrv) { + constructor($scope, $injector, private backendSrv, private $location) { super($scope, $injector); _.defaults(this.panel, panelDefaults); @@ -35,6 +35,22 @@ class DashListCtrl extends PanelCtrl { this.addEditorTab('Options', 'public/app/plugins/panel/pluginlist/editor.html'); } + gotoPlugin(plugin) { + this.$location.path(`plugins/${plugin.id}/edit`); + } + + updateAvailable(plugin, $event) { + $event.stopPropagation(); + + var modalScope = this.$scope.$new(true); + modalScope.plugin = plugin; + + this.publishAppEvent('show-modal', { + src: 'public/app/features/plugins/partials/update_instructions.html', + scope: modalScope + }); + } + update() { this.backendSrv.get('api/plugins', {embedded: 0, core: 0}).then(plugins => { this.pluginList = plugins; @@ -53,4 +69,4 @@ class DashListCtrl extends PanelCtrl { } } -export {DashListCtrl, DashListCtrl as PanelCtrl} +export {PluginListCtrl, PluginListCtrl as PanelCtrl} diff --git a/public/sass/_variables.dark.scss b/public/sass/_variables.dark.scss index 3a390e2f883..5c8fa703ffc 100644 --- a/public/sass/_variables.dark.scss +++ b/public/sass/_variables.dark.scss @@ -70,6 +70,7 @@ $page-gradient: linear-gradient(60deg, transparent 70%, darken($page-bg, 4%) 98% $link-color: darken($white,11%); $link-color-disabled: darken($link-color,30%); $link-hover-color: $white; +$external-link-color: $blue; // Typography // ------------------------- @@ -263,5 +264,3 @@ $checkboxImageUrl: '../img/checkbox.png'; $card-background: linear-gradient(135deg, #2f2f2f, #262626); $card-background-hover: linear-gradient(135deg, #343434, #262626); $card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .3); - - diff --git a/public/sass/_variables.light.scss b/public/sass/_variables.light.scss index 91b42c5c6be..9e7d3561e38 100644 --- a/public/sass/_variables.light.scss +++ b/public/sass/_variables.light.scss @@ -76,6 +76,7 @@ $page-gradient: linear-gradient(60deg, transparent 70%, darken($page-bg, 4%) 98% $link-color: $gray-1; $link-color-disabled: lighten($link-color, 30%); $link-hover-color: darken($link-color, 20%); +$external-link-color: $blue; // Typography // ------------------------- @@ -290,4 +291,3 @@ $checkboxImageUrl: '../img/checkbox_white.png'; $card-background: linear-gradient(135deg, $gray-5, $gray-6); $card-background-hover: linear-gradient(135deg, $gray-6, $gray-7); $card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .1); - diff --git a/public/sass/base/_code.scss b/public/sass/base/_code.scss index 2a4057a8547..464eed966ce 100644 --- a/public/sass/base/_code.scss +++ b/public/sass/base/_code.scss @@ -23,6 +23,16 @@ code { white-space: nowrap; } +code.code--small { + font-size: $font-size-xs; + padding: 5px; + margin: 0 2px; +} + +p.code--line { + line-height: 1.8; +} + // Blocks of code pre { display: block; @@ -49,4 +59,3 @@ pre { border: 0; } } - diff --git a/public/sass/base/_type.scss b/public/sass/base/_type.scss index e819f71b540..904dfa13b4e 100644 --- a/public/sass/base/_type.scss +++ b/public/sass/base/_type.scss @@ -114,7 +114,7 @@ hr { small, .small { - font-size: 85%; + font-size: $font-size-sm; font-weight: normal; } diff --git a/public/sass/components/_panel_pluginlist.scss b/public/sass/components/_panel_pluginlist.scss index 0d02196e975..605e1afdb6a 100644 --- a/public/sass/components/_panel_pluginlist.scss +++ b/public/sass/components/_panel_pluginlist.scss @@ -42,8 +42,17 @@ font-size: $font-size-sm; } +.pluginlist-message--update { + &:hover { + border-bottom: 1px solid $text-color; + } +} + .pluginlist-message--enable{ - color: $brand-success; + color: $external-link-color; + &:hover { + border-bottom: 1px solid $external-link-color; + } } .pluginlist-message--no-update { @@ -58,3 +67,9 @@ color: $text-color-weak; font-size: $font-size-sm; } + +.pluginlist-inline-logo { + vertical-align: sub; + margin-right: $spacer / 3; + width: 16px; +} From 6ce934f1dd29c14c6f24ae64cef15734987a12a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Apr 2016 20:33:58 -0400 Subject: [PATCH 13/13] feat(pluginlist): worked on plugin list --- pkg/plugins/update_checker.go | 5 ++++- .../features/plugins/partials/plugin_edit.html | 4 +--- .../plugins/partials/update_instructions.html | 2 +- .../app/features/plugins/plugin_edit_ctrl.ts | 11 +++++++++++ .../app/plugins/panel/graph/graph_tooltip.js | 4 ++-- .../app/plugins/panel/pluginlist/module.html | 2 +- public/sass/_variables.dark.scss | 18 ++++++++++-------- public/sass/_variables.light.scss | 16 ++++++++-------- public/sass/components/_panel_graph.scss | 2 ++ public/sass/components/_tooltip.scss | 2 +- 10 files changed, 41 insertions(+), 25 deletions(-) diff --git a/pkg/plugins/update_checker.go b/pkg/plugins/update_checker.go index b619310a918..2067ea3af46 100644 --- a/pkg/plugins/update_checker.go +++ b/pkg/plugins/update_checker.go @@ -26,7 +26,10 @@ func StartPluginUpdateChecker() { return } - ticker := time.NewTicker(time.Second * 24) + // do one check directly + go checkForUpdates() + + ticker := time.NewTicker(time.Minute * 10) for { select { case <-ticker.C: diff --git a/public/app/features/plugins/partials/plugin_edit.html b/public/app/features/plugins/partials/plugin_edit.html index 46971bd35e7..402e2f24c3f 100644 --- a/public/app/features/plugins/partials/plugin_edit.html +++ b/public/app/features/plugins/partials/plugin_edit.html @@ -56,9 +56,7 @@

    Version

    {{ctrl.model.info.version}}
    diff --git a/public/app/features/plugins/partials/update_instructions.html b/public/app/features/plugins/partials/update_instructions.html index 7027ebf7154..80e3a413d1d 100644 --- a/public/app/features/plugins/partials/update_instructions.html +++ b/public/app/features/plugins/partials/update_instructions.html @@ -2,7 +2,7 @@