diff --git a/CHANGELOG.md b/CHANGELOG.md index cb4ab3ad1e8..3b8239bd437 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,34 @@ -# 3.0.0-beta2 (unreleased) +# 3.0.0-beta5 (2016-04-15) + +### Bug fixes +* **grafana-cli**: Fixed issue grafana-cli tool, did not detect the right plugin dir, fixes [#4723](https://github.com/grafana/grafana/issues/4723) +* **Graph**: Fixed issue with light theme text color issue in tooltip, fixes [#4702](https://github.com/grafana/grafana/issues/4702) +* **Snapshot**: Fixed issue with empty snapshots, fixes [#4706](https://github.com/grafana/grafana/issues/4706) + +# 3.0.0-beta4 (2016-04-13) + +### Bug fixes +* **Home dashboard**: Fixed issue with permission denied error on home dashboard, fixes [#4686](https://github.com/grafana/grafana/issues/4686) +* **Templating**: Fixed issue templating variables that use regex extraction, fixes [#4672](https://github.com/grafana/grafana/issues/4672) + +# 3.0.0-beta3 (2016-04-12) + +### Enhancements +* **InfluxDB**: Changed multi query encoding to work with InfluxDB 0.11 & 0.12, closes [#4533](https://github.com/grafana/grafana/issues/4533) +* **Timepicker**: Add arrows and shortcuts for moving back and forth in current dashboard, closes [#119](https://github.com/grafana/grafana/issues/119) + +### Bug fixes +* **Postgres**: Fixed page render crash when using postgres, fixes [#4558](https://github.com/grafana/grafana/issues/4558) +* **Table panel**: Fixed table panel bug when trying to show annotations in table panel, fixes [#4563](https://github.com/grafana/grafana/issues/4563) +* **App Config**: Fixed app config issue showing content of other app config, fixes [#4575](https://github.com/grafana/grafana/issues/4575) +* **Graph Panel**: Fixed legend option max not updating, fixes [#4601](https://github.com/grafana/grafana/issues/4601) +* **Graph Panel**: Fixed issue where newly added graph panels shared same axes config, fixes [#4582](https://github.com/grafana/grafana/issues/4582) +* **Graph Panel**: Fixed issue with axis labels overlapping Y-axis, fixes [#4626](https://github.com/grafana/grafana/issues/4626) +* **InfluxDB**: Fixed issue with templating query containing template variable, fixes [#4602](https://github.com/grafana/grafana/issues/4602) +* **Graph Panel**: Fixed issue with hiding series and stacking, fixes [#4557](https://github.com/grafana/grafana/issues/4557) +* **Graph Panel**: Fixed issue with legend height in table mode with few series, affected iframe embedding as well, fixes [#4640](https://github.com/grafana/grafana/issues/4640) + +# 3.0.0-beta2 (2016-04-04) ### New Features (introduces since 3.0-beta1) * **Preferences**: Set home dashboard on user and org level, closes [#1678](https://github.com/grafana/grafana/issues/1678) @@ -9,9 +39,8 @@ * **Dashboard**: Fixed dashboard panel layout for mobile devices, fixes [#4529](https://github.com/grafana/grafana/issues/4529) * **Table Panel**: Fixed issue with table panel sort, fixes [#4532](https://github.com/grafana/grafana/issues/4532) * **Page Load Crash**: A Datasource with null jsonData would make Grafana fail to load page, fixes [#4536](https://github.com/grafana/grafana/issues/4536) -* **Metrics tab**: Fix for missing datasource name in datasource selector, fixes [#4540](https://github.com/grafana/grafana/issues/4540) +* **Metrics tab**: Fix for missing datasource name in datasource selector, fixes [#4541](https://github.com/grafana/grafana/issues/4540) * **Graph**: Fix legend in table mode with series on right-y axis, fixes [#4551](https://github.com/grafana/grafana/issues/4551), [#1145](https://github.com/grafana/grafana/issues/1145) -* **Password**: Password reset link/page did not work, fixes [#4542](https://github.com/grafana/grafana/issues/4542) # 3.0.0-beta1 (2016-03-31) diff --git a/README.md b/README.md index a3336e0373b..931055349cf 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ -[Grafana](http://grafana.org) [![Circle CI](https://circleci.com/gh/grafana/grafana.svg?style=svg)](https://circleci.com/gh/grafana/grafana) [![Coverage Status](https://coveralls.io/repos/grafana/grafana/badge.png)](https://coveralls.io/r/grafana/grafana) [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/grafana/grafana?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[Grafana](http://grafana.org) [![Circle CI](https://circleci.com/gh/grafana/grafana.svg?style=svg)](https://circleci.com/gh/grafana/grafana) [![Coverage Status](https://coveralls.io/repos/grafana/grafana/badge.png)](https://coveralls.io/r/grafana/grafana) ================ [Website](http://grafana.org) | [Twitter](https://twitter.com/grafana) | [IRC](https://webchat.freenode.net/?channels=grafana) | +![](https://brandfolder.com/api/favicon/icon?size=16&domain=www.slack.com) +[Slack](http://slack.raintank.io) | [Email](mailto:contact@grafana.org) Grafana is an open source, feature rich metrics dashboard and graph editor for @@ -77,6 +79,7 @@ the latest master builds [here](http://grafana.org/download/builds) - Go 1.5 - NodeJS +- [Godep](https://github.com/tools/godep) ### Get Code 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/docs/sources/http_api/data_source.md b/docs/sources/http_api/data_source.md index 2a3c20e9af8..2a8f79f71f5 100644 --- a/docs/sources/http_api/data_source.md +++ b/docs/sources/http_api/data_source.md @@ -207,35 +207,6 @@ page_keywords: grafana, admin, http, api, documentation, datasource {"message":"Data source deleted"} -## Available data source types - -`GET /api/datasources/plugins` - -**Example Request**: - - GET /api/datasources/plugins HTTP/1.1 - Accept: application/json - Content-Type: application/json - Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk - -**Example Response**: - - HTTP/1.1 200 - Content-Type: application/json - - { - "grafana":{ - "metrics":true,"module":"plugins/datasource/grafana/datasource", - "name":"Grafana (for testing)", - "partials":{ - "query":"app/plugins/datasource/grafana/partials/query.editor.html" - }, - "pluginType":"datasource", - "serviceName":"GrafanaDatasource", - "type":"grafana" - } - } - ## Data source proxy calls `GET /api/datasources/proxy/:datasourceId/*` diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index 2d24e7a9c3f..231b4a601ac 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -11,7 +11,7 @@ page_keywords: grafana, installation, debian, ubuntu, guide Description | Download ------------ | ------------- Stable .deb for Debian-based Linux | [grafana_2.6.0_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_2.6.0_amd64.deb) -Beta .deb for Debian-based Linux | [grafana_3.0.0-beta11459429091_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta11459429091_amd64.deb) +Beta .deb for Debian-based Linux | [grafana_3.0.0-beta51460725904_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta51460725904_amd64.deb) ## Install Stable @@ -21,9 +21,9 @@ Beta .deb for Debian-based Linux | [grafana_3.0.0-beta11459429091_amd64.deb](h ## Install 3.0 Beta - $ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta11459429091_amd64.deb + $ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta51460725904_amd64.deb $ sudo apt-get install -y adduser libfontconfig - $ sudo dpkg -i grafana_3.0.0-beta11459429091_amd64.deb + $ sudo dpkg -i grafana_3.0.0-beta51460725904_amd64.deb ## APT Repository diff --git a/docs/sources/installation/rpm.md b/docs/sources/installation/rpm.md index 5e8e746d0f7..d981a5c4b2c 100644 --- a/docs/sources/installation/rpm.md +++ b/docs/sources/installation/rpm.md @@ -11,7 +11,7 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide Description | Download ------------ | ------------- Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [grafana-2.6.0-1.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-2.6.0-1.x86_64.rpm) -Beta .RPM for CentOS / Fedor / OpenSuse / Redhat Linux | [grafana-3.0.0-beta11459429091.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta11459429091.x86_64.rpm) +Beta .RPM for CentOS / Fedor / OpenSuse / Redhat Linux | [grafana-3.0.0-beta51460725904.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta51460725904ยง.x86_64.rpm) ## Install Stable Release from package file @@ -34,18 +34,18 @@ Or install manually using `rpm`. You can install Grafana using Yum directly. - $ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta11459429091.x86_64.rpm + $ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta51460725904.x86_64.rpm Or install manually using `rpm`. #### On CentOS / Fedora / Redhat: $ sudo yum install initscripts fontconfig - $ sudo rpm -Uvh grafana-3.0.0-beta11459429091.x86_64.rpm + $ sudo rpm -Uvh grafana-3.0.0-beta51460725904.x86_64.rpm #### On OpenSuse: - $ sudo rpm -i --nodeps grafana-3.0.0-beta11459429091.x86_64.rpm + $ sudo rpm -i --nodeps grafana-3.0.0-beta51460725904.x86_64.rpm ## Install via YUM Repository diff --git a/docs/sources/installation/windows.md b/docs/sources/installation/windows.md index 9a1ccbd0a19..1d6c5fc76cd 100644 --- a/docs/sources/installation/windows.md +++ b/docs/sources/installation/windows.md @@ -10,7 +10,7 @@ page_keywords: grafana, installation, windows guide Description | Download ------------ | ------------- -Stable Zip package for Windows | [grafana.2.5.0.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-2.5.0.windows-x64.zip) +Stable Zip package for Windows | [grafana.2.6.0.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-2.5.0.windows-x64.zip) ## Configure diff --git a/docs/sources/plugins/development.md b/docs/sources/plugins/development.md index e8b5c620732..29dcd0568e3 100644 --- a/docs/sources/plugins/development.md +++ b/docs/sources/plugins/development.md @@ -10,7 +10,7 @@ From grafana 3.0 it's very easy to develop your own plugins and share them with ## Short version -1. [Setup grafana](https://github.com/grafana/grafana/blob/master/DEVELOPMENT.md) +1. [Setup grafana](http://docs.grafana.org/project/building_from_source/) 2. Clone an example plugin into ```/var/lib/grafana/plugins``` or `data/plugins` (relative to grafana git repo if your running development version from source dir) 3. Code away! @@ -34,7 +34,7 @@ and [apps](./apps.md) plugins in the documentation. ## Start developing your plugin There are two ways that you can start developing a Grafana plugin. -1. Setup a Grafana development environment. [(described here)](https://github.com/grafana/grafana/blob/master/DEVELOPMENT.md) and place your plugin in the ```data/plugins``` folder. +1. Setup a Grafana development environment. [(described here)](http://docs.grafana.org/project/building_from_source/) and place your plugin in the ```data/plugins``` folder. 2. Install Grafana and place your plugin in the plugins directory which is set in your [config file](../installation/configuration.md). By default this is `/var/lib/grafana/plugins` on Linux systems. 3. Place your plugin directory anywhere you like and specify it grafana.ini. diff --git a/docs/sources/plugins/index.md b/docs/sources/plugins/index.md index 156262a4ec1..08654bfb946 100644 --- a/docs/sources/plugins/index.md +++ b/docs/sources/plugins/index.md @@ -9,7 +9,7 @@ page_keywords: grafana, plugins, documentation From Grafana 3.0 not only datasource plugins are supported but also panel plugins and apps. Having panels as plugins make it easy to create and add any kind of panel, to show your data or improve your favorite dashboards. Apps is something new in Grafana that enables -bundling of datasources, panels, dashboards and Grafana pages into a cohesive experiance. +bundling of datasources, panels, dashboards and Grafana pages into a cohesive experience. Grafana already have a strong community of contributors and plugin developers. By making it easier to develop and install plugins we hope that the community diff --git a/docs/sources/plugins/installation.md b/docs/sources/plugins/installation.md index 0dada8b69a7..c83a54bdce3 100644 --- a/docs/sources/plugins/installation.md +++ b/docs/sources/plugins/installation.md @@ -28,14 +28,14 @@ List installed plugins grafana-cli plugins ls ``` -Upgrade all installed plugins +Update all installed plugins ``` -grafana-cli plugins upgrade-all +grafana-cli plugins update-all ``` -Upgrade one plugin +Update one plugin ``` -grafana-cli plugins upgrade +grafana-cli plugins update ``` Remove one plugin diff --git a/docs/sources/plugins/panels.md b/docs/sources/plugins/panels.md index 7182c3182a3..179af7451c5 100644 --- a/docs/sources/plugins/panels.md +++ b/docs/sources/plugins/panels.md @@ -7,7 +7,7 @@ page_keywords: grafana, plugins, documentation # Panels -Panels are the main bulding block of dashboards. +Panels are the main building blocks of dashboards. ## Panel development diff --git a/karma.conf.js b/karma.conf.js index dc082924b11..c803dda5eae 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -24,9 +24,10 @@ module.exports = function(config) { logLevel: config.LOG_INFO, autoWatch: true, browsers: ['PhantomJS'], - captureTimeout: 2000, + captureTimeout: 20000, singleRun: true, - autoWatchBatchDelay: 1000, + autoWatchBatchDelay: 10000, + browserNoActivityTimeout: 60000, }); diff --git a/latest.json b/latest.json index 79eb42a8527..9354661781a 100644 --- a/latest.json +++ b/latest.json @@ -1,3 +1,4 @@ { - "version": "2.1.1" + "stable": "2.6.0", + "testing": "3.0.0-beta5" } diff --git a/package.json b/package.json index d741bab574e..f454a42ba7a 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,12 @@ "company": "Coding Instinct AB" }, "name": "grafana", - "version": "3.0.0-beta2", + "version": "3.0.0-beta6", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git" }, "devDependencies": { - "angular2": "2.0.0-beta.12", "zone.js": "^0.6.6", "autoprefixer": "^6.3.3", "es6-promise": "^3.0.2", @@ -54,7 +53,7 @@ "mocha": "2.3.4", "phantomjs-prebuilt": "^2.1.3", "reflect-metadata": "0.1.2", - "rxjs": "5.0.0-beta.2", + "rxjs": "5.0.0-beta.4", "sass-lint": "^1.5.0", "systemjs": "0.19.24" }, @@ -68,6 +67,7 @@ }, "license": "Apache-2.0", "dependencies": { + "eventemitter3": "^1.2.0", "grunt-jscs": "~1.5.x", "grunt-sass-lint": "^0.1.0", "grunt-sync": "^0.4.1", diff --git a/packaging/publish/publish.sh b/packaging/publish/publish.sh index 58021bc6ffa..79303707231 100755 --- a/packaging/publish/publish.sh +++ b/packaging/publish/publish.sh @@ -1,21 +1,21 @@ #! /usr/bin/env bash -deb_ver=3.0.0-beta11459429091 -rpm_ver=3.0.0-beta11459429091 +deb_ver=3.0.0-beta51460725904 +rpm_ver=3.0.0-beta51460725904 #rpm_ver=3.0.0-1 -wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb +#wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb -# package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb -# package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb +#package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb +#package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb -package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb -package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb +#package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb +#package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb -wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm +#wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm -package_cloud push grafana/testing/el/6 grafana-${rpm_ver}.x86_64.rpm +#package_cloud push grafana/testing/el/6 grafana-${rpm_ver}.x86_64.rpm package_cloud push grafana/testing/el/7 grafana-${rpm_ver}.x86_64.rpm # package_cloud push grafana/stable/el/7 grafana-${version}-1.x86_64.rpm diff --git a/pkg/api/api.go b/pkg/api/api.go index 85f1e2474c3..684633e0bcd 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -30,6 +30,7 @@ func Register(r *macaron.Macaron) { // authed views r.Get("/profile/", reqSignedIn, Index) r.Get("/profile/password", reqSignedIn, Index) + r.Get("/profile/switch-org/:id", reqSignedIn, ChangeActiveOrgAndRedirectToHome) r.Get("/org/", reqSignedIn, Index) r.Get("/org/new", reqSignedIn, Index) r.Get("/datasources/", reqSignedIn, Index) @@ -190,12 +191,12 @@ func Register(r *macaron.Macaron) { r.Get("/datasources/id/:name", wrap(GetDataSourceIdByName), reqSignedIn) - r.Group("/plugins", func() { - r.Get("/", wrap(GetPluginList)) + r.Get("/plugins", wrap(GetPluginList)) + r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById)) + r.Group("/plugins", func() { r.Get("/:pluginId/readme", wrap(GetPluginReadme)) r.Get("/:pluginId/dashboards/", wrap(GetPluginDashboards)) - r.Get("/:pluginId/settings", wrap(GetPluginSettingById)) r.Post("/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting)) }, reqOrgAdmin) @@ -252,6 +253,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/avatar/avatar.go b/pkg/api/avatar/avatar.go index 79aebece349..41ce857db4f 100644 --- a/pkg/api/avatar/avatar.go +++ b/pkg/api/avatar/avatar.go @@ -116,6 +116,7 @@ func (this *service) ServeHTTP(w http.ResponseWriter, r *http.Request) { if avatar.Expired() { if err := avatar.Update(); err != nil { log.Trace("avatar update error: %v", err) + avatar = this.notFound } } diff --git a/pkg/api/dtos/plugins.go b/pkg/api/dtos/plugins.go index 53c911c10fa..fbdf8d4e0ea 100644 --- a/pkg/api/dtos/plugins.go +++ b/pkg/api/dtos/plugins.go @@ -15,15 +15,20 @@ 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 { - 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"` + LatestVersion string `json:"latestVersion"` + HasUpdate bool `json:"hasUpdate"` } type PluginList []PluginListItem 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/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..6446f7fee33 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,16 +29,23 @@ 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 } 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 +89,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/api/user.go b/pkg/api/user.go index 5af243eeb22..8f54feaf6a0 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -4,6 +4,7 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" ) @@ -109,6 +110,23 @@ func UserSetUsingOrg(c *middleware.Context) Response { return ApiSuccess("Active organization changed") } +// GET /profile/switch-org/:id +func ChangeActiveOrgAndRedirectToHome(c *middleware.Context) { + orgId := c.ParamsInt64(":id") + + if !validateUsingOrg(c.UserId, orgId) { + NotFoundHandler(c) + } + + cmd := m.SetUsingOrgCommand{UserId: c.UserId, OrgId: orgId} + + if err := bus.Dispatch(&cmd); err != nil { + NotFoundHandler(c) + } + + c.Redirect(setting.AppSubUrl + "/") +} + func ChangeUserPassword(c *middleware.Context, cmd m.ChangeUserPasswordCommand) Response { userQuery := m.GetUserByIdQuery{Id: c.UserId} diff --git a/pkg/cmd/grafana-cli/commands/commands.go b/pkg/cmd/grafana-cli/commands/commands.go index 55a0e2660ec..b3821a47844 100644 --- a/pkg/cmd/grafana-cli/commands/commands.go +++ b/pkg/cmd/grafana-cli/commands/commands.go @@ -1,9 +1,10 @@ package commands import ( + "os" + "github.com/codegangsta/cli" "github.com/grafana/grafana/pkg/cmd/grafana-cli/log" - "os" ) func runCommand(command func(commandLine CommandLine) error) func(context *cli.Context) { @@ -25,27 +26,33 @@ func runCommand(command func(commandLine CommandLine) error) func(context *cli.C var pluginCommands = []cli.Command{ { Name: "install", - Usage: "install ", + Usage: "install ", Action: runCommand(installCommand), }, { Name: "list-remote", Usage: "list remote available plugins", Action: runCommand(listremoteCommand), }, { - Name: "upgrade", - Usage: "upgrade ", - Action: runCommand(upgradeCommand), + Name: "update", + Usage: "update ", + Aliases: []string{"upgrade"}, + Action: runCommand(upgradeCommand), }, { - Name: "upgrade-all", - Usage: "upgrades all your installed plugins", - Action: runCommand(upgradeAllCommand), + Name: "update-all", + Aliases: []string{"upgrade-all"}, + Usage: "update all your installed plugins", + Action: runCommand(upgradeAllCommand), }, { Name: "ls", Usage: "list all installed plugins", Action: runCommand(lsCommand), + }, { + Name: "uninstall", + Usage: "uninstall ", + Action: runCommand(removeCommand), }, { Name: "remove", - Usage: "remove ", + Usage: "remove ", Action: runCommand(removeCommand), }, } diff --git a/pkg/cmd/grafana-cli/commands/install_command.go b/pkg/cmd/grafana-cli/commands/install_command.go index 0f783d5d3ff..81e7a15233b 100644 --- a/pkg/cmd/grafana-cli/commands/install_command.go +++ b/pkg/cmd/grafana-cli/commands/install_command.go @@ -120,6 +120,7 @@ func RemoveGitBuildFromName(pluginName, filename string) string { } var retryCount = 0 +var permissionsDeniedMessage = "Could not create %s. Permission denied. Make sure you have write access to plugindir" func downloadFile(pluginName, filePath, url string) (err error) { defer func() { @@ -153,16 +154,16 @@ func downloadFile(pluginName, filePath, url string) (err error) { newFile := path.Join(filePath, RemoveGitBuildFromName(pluginName, zf.Name)) if zf.FileInfo().IsDir() { - os.Mkdir(newFile, 0777) + err := os.Mkdir(newFile, 0777) + if PermissionsError(err) { + return fmt.Errorf(permissionsDeniedMessage, newFile) + } } else { dst, err := os.Create(newFile) - if err != nil { - if strings.Contains(err.Error(), "permission denied") { - return fmt.Errorf( - "Could not create file %s. permission deined. Make sure you have write access to plugindir", - newFile) - } + if PermissionsError(err) { + return fmt.Errorf(permissionsDeniedMessage, newFile) } + defer dst.Close() src, err := zf.Open() if err != nil { @@ -176,3 +177,7 @@ func downloadFile(pluginName, filePath, url string) (err error) { return nil } + +func PermissionsError(err error) bool { + return err != nil && strings.Contains(err.Error(), "permission denied") +} diff --git a/pkg/cmd/grafana-cli/commands/upgrade_all_command.go b/pkg/cmd/grafana-cli/commands/upgrade_all_command.go index e805e8e6f34..7f088be3e14 100644 --- a/pkg/cmd/grafana-cli/commands/upgrade_all_command.go +++ b/pkg/cmd/grafana-cli/commands/upgrade_all_command.go @@ -51,7 +51,7 @@ func upgradeAllCommand(c CommandLine) error { } for _, p := range pluginsToUpgrade { - log.Infof("Upgrading %v \n", p.Id) + log.Infof("Updating %v \n", p.Id) s.RemoveInstalledPlugin(pluginsDir, p.Id) InstallPlugin(p.Id, "", c) diff --git a/pkg/cmd/grafana-cli/main.go b/pkg/cmd/grafana-cli/main.go index b5b13ee36e0..5cc3fb6f306 100644 --- a/pkg/cmd/grafana-cli/main.go +++ b/pkg/cmd/grafana-cli/main.go @@ -8,17 +8,37 @@ import ( "github.com/codegangsta/cli" "github.com/grafana/grafana/pkg/cmd/grafana-cli/commands" "github.com/grafana/grafana/pkg/cmd/grafana-cli/log" + "strings" ) var version = "master" func getGrafanaPluginDir() string { - os := runtime.GOOS - if os == "windows" { + currentOS := runtime.GOOS + defaultNix := "/var/lib/grafana/plugins" + + if currentOS == "windows" { return "C:\\opt\\grafana\\plugins" - } else { - return "/var/lib/grafana/plugins" } + + pwd, err := os.Getwd() + + if err != nil { + log.Error("Could not get current path. using default") + return defaultNix + } + + if isDevenvironment(pwd) { + return "../../../data/plugins" + } + + return defaultNix +} + +func isDevenvironment(pwd string) bool { + // if grafana-cli is executed from the cmd folder we can assume + // that its in development environment. + return strings.HasSuffix(pwd, "/pkg/cmd/grafana-cli") } func main() { diff --git a/pkg/cmd/grafana-server/main.go b/pkg/cmd/grafana-server/main.go index dad3f437390..b2c66ba185e 100644 --- a/pkg/cmd/grafana-server/main.go +++ b/pkg/cmd/grafana-server/main.go @@ -24,7 +24,7 @@ import ( "github.com/grafana/grafana/pkg/social" ) -var version = "3.0.0-pre1" +var version = "3.0.0-beta4" var commit = "NA" var buildstamp string var build_date string diff --git a/pkg/metrics/report_usage.go b/pkg/metrics/report_usage.go index c5c2afed7f7..85a87155f6d 100644 --- a/pkg/metrics/report_usage.go +++ b/pkg/metrics/report_usage.go @@ -10,6 +10,7 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/log" m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/setting" ) @@ -56,6 +57,9 @@ func sendUsageStats() { metrics["stats.users.count"] = statsQuery.Result.UserCount metrics["stats.orgs.count"] = statsQuery.Result.OrgCount metrics["stats.playlist.count"] = statsQuery.Result.PlaylistCount + metrics["stats.plugins.apps.count"] = len(plugins.Apps) + metrics["stats.plugins.panels.count"] = len(plugins.Panels) + metrics["stats.plugins.datasources.count"] = len(plugins.DataSources) dsStats := m.GetDataSourceStatsQuery{} if err := bus.Dispatch(&dsStats); err != nil { diff --git a/pkg/models/stats.go b/pkg/models/stats.go index 63f946b956b..fa9cfdab6e8 100644 --- a/pkg/models/stats.go +++ b/pkg/models/stats.go @@ -21,15 +21,14 @@ type GetDataSourceStatsQuery struct { } type AdminStats struct { - UserCount int `json:"user_count"` - OrgCount int `json:"org_count"` - DashboardCount int `json:"dashboard_count"` - DbSnapshotCount int `json:"db_snapshot_count"` - DbTagCount int `json:"db_tag_count"` - DataSourceCount int `json:"data_source_count"` - PlaylistCount int `json:"playlist_count"` - StarredDbCount int `json:"starred_db_count"` - GrafanaAdminCount int `json:"grafana_admin_count"` + UserCount int `json:"user_count"` + OrgCount int `json:"org_count"` + DashboardCount int `json:"dashboard_count"` + DbSnapshotCount int `json:"db_snapshot_count"` + DbTagCount int `json:"db_tag_count"` + DataSourceCount int `json:"data_source_count"` + PlaylistCount int `json:"playlist_count"` + StarredDbCount int `json:"starred_db_count"` } type GetAdminStatsQuery struct { 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..68268239c51 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -43,6 +43,10 @@ type PluginBase struct { IncludedInAppId string `json:"-"` PluginDir string `json:"-"` DefaultNavUrl string `json:"-"` + IsCorePlugin bool `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..073685afc79 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() } + go StartPluginUpdateChecker() return nil } diff --git a/pkg/plugins/update_checker.go b/pkg/plugins/update_checker.go new file mode 100644 index 00000000000..36169434096 --- /dev/null +++ b/pkg/plugins/update_checker.go @@ -0,0 +1,119 @@ +package plugins + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "strings" + "time" + + "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/setting" +) + +type GrafanaNetPlugin struct { + Slug string `json:"slug"` + Version string `json:"version"` +} + +type GithubLatest struct { + Stable string `json:"stable"` + Testing string `json:"testing"` +} + +func StartPluginUpdateChecker() { + if !setting.CheckForUpdates { + return + } + + // do one check directly + go checkForUpdates() + + ticker := time.NewTicker(time.Minute * 10) + for { + select { + case <-ticker.C: + checkForUpdates() + } + } +} + +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)} + + 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()) + 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 + } + + 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 gNetPlugins { + if gplug.Slug == plug.Id { + plug.GrafanaNetVersion = gplug.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 + GrafanaHasUpdate = !strings.HasPrefix(setting.BuildVersion, githubLatest.Testing) + } else { + GrafanaLatestVersion = githubLatest.Stable + GrafanaHasUpdate = githubLatest.Stable != setting.BuildVersion + } +} diff --git a/pkg/services/sqlstore/migrations/preferences_mig.go b/pkg/services/sqlstore/migrations/preferences_mig.go index 6425c3735bb..67a8169a7a8 100644 --- a/pkg/services/sqlstore/migrations/preferences_mig.go +++ b/pkg/services/sqlstore/migrations/preferences_mig.go @@ -10,12 +10,12 @@ func addPreferencesMigrations(mg *Migrator) { Name: "preferences", Columns: []*Column{ {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, - {Name: "org_id", Type: DB_Int, Nullable: true}, - {Name: "user_id", Type: DB_NVarchar, Length: 255, Nullable: true}, + {Name: "org_id", Type: DB_BigInt, Nullable: false}, + {Name: "user_id", Type: DB_BigInt, Nullable: false}, {Name: "version", Type: DB_Int, Nullable: false}, - {Name: "home_dashboard_id", Type: DB_BigInt, Nullable: true}, - {Name: "timezone", Type: DB_NVarchar, Length: 50, Nullable: true}, - {Name: "theme", Type: DB_NVarchar, Length: 20, Nullable: true}, + {Name: "home_dashboard_id", Type: DB_BigInt, Nullable: false}, + {Name: "timezone", Type: DB_NVarchar, Length: 50, Nullable: false}, + {Name: "theme", Type: DB_NVarchar, Length: 20, Nullable: false}, {Name: "created", Type: DB_DateTime, Nullable: false}, {Name: "updated", Type: DB_DateTime, Nullable: false}, }, @@ -25,6 +25,8 @@ func addPreferencesMigrations(mg *Migrator) { }, } + mg.AddMigration("drop preferences table v3", NewDropTableMigration("preferences")) + // create table - mg.AddMigration("create preferences table v2", NewAddTableMigration(preferencesV2)) + mg.AddMigration("create preferences table v3", NewAddTableMigration(preferencesV2)) } diff --git a/pkg/services/sqlstore/stats.go b/pkg/services/sqlstore/stats.go index 92efab2015d..7580996ad57 100644 --- a/pkg/services/sqlstore/stats.go +++ b/pkg/services/sqlstore/stats.go @@ -85,12 +85,7 @@ func GetAdminStats(query *m.GetAdminStatsQuery) error { ( SELECT COUNT(DISTINCT ` + dialect.Quote("dashboard_id") + ` ) FROM ` + dialect.Quote("star") + ` - ) AS starred_db_count, - ( - SELECT COUNT(*) - FROM ` + dialect.Quote("user") + ` - WHERE ` + dialect.Quote("is_admin") + ` = 1 - ) AS grafana_admin_count + ) AS starred_db_count ` var stats m.AdminStats 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() diff --git a/public/app/core/components/colorpicker.ts b/public/app/core/components/colorpicker.ts index 7c35ac7e4ac..1834a1eb8a1 100644 --- a/public/app/core/components/colorpicker.ts +++ b/public/app/core/components/colorpicker.ts @@ -7,10 +7,6 @@ import coreModule from 'app/core/core_module'; var template = `
- - - -
`; @@ -51,21 +46,17 @@ export class ColorPickerCtrl { toggleAxis(yaxis) { this.$scope.toggleAxis(); - if (!this.$scope.autoClose) { + if (this.$scope.autoClose) { this.$scope.dismiss(); } } colorSelected(color) { this.$scope.colorSelected(color); - if (!this.$scope.autoClose) { + if (this.$scope.autoClose) { this.$scope.dismiss(); } } - - close() { - this.$scope.dismiss(); - } } export function colorPicker() { diff --git a/public/app/core/components/dashboard_selector.ts b/public/app/core/components/dashboard_selector.ts index 913a43911fa..4209ff3f8dc 100644 --- a/public/app/core/components/dashboard_selector.ts +++ b/public/app/core/components/dashboard_selector.ts @@ -7,6 +7,9 @@ import coreModule from 'app/core/core_module'; var template = ` + + Not finding dashboard you want? Star it first, then it should appear in this select box. + `; export class DashboardSelectorCtrl { diff --git a/public/app/core/components/info_popover.ts b/public/app/core/components/info_popover.ts index cc49ecbc4ab..8bc28b8a57d 100644 --- a/public/app/core/components/info_popover.ts +++ b/public/app/core/components/info_popover.ts @@ -8,21 +8,30 @@ import Drop from 'tether-drop'; export function infoPopover() { return { restrict: 'E', + template: '', transclude: true, link: function(scope, elem, attrs, ctrl, transclude) { - var inputElem = elem.prev(); - if (inputElem.length === 0) { - console.log('Failed to find input element for popover'); - return; - } + // var inputElem = elem.prev(); + // if (inputElem.length === 0) { + // console.log('Failed to find input element for popover'); + // return; + // } var offset = attrs.offset || '0 -10px'; var position = attrs.position || 'right middle'; var classes = 'drop-help drop-hide-out-of-bounds'; + var openOn = 'hover'; + + elem.addClass('gf-form-help-icon'); + if (attrs.wide) { classes += ' drop-wide'; } + if (attrs.mode) { + elem.addClass('gf-form-help-icon--' + attrs.mode); + } + transclude(function(clone, newScope) { var content = document.createElement("div"); _.each(clone, (node) => { @@ -30,11 +39,11 @@ export function infoPopover() { }); var drop = new Drop({ - target: inputElem[0], + target: elem[0], content: content, position: position, classes: classes, - openOn: 'click', + openOn: openOn, tetherOptions: { offset: offset } diff --git a/public/app/core/components/sidemenu/sidemenu.html b/public/app/core/components/sidemenu/sidemenu.html index 4ff30c92f47..25aa38c45b8 100644 --- a/public/app/core/components/sidemenu/sidemenu.html +++ b/public/app/core/components/sidemenu/sidemenu.html @@ -21,10 +21,6 @@ {{::menuItem.text}} - - - {{::menuItem.text}} - diff --git a/public/app/core/components/sidemenu/sidemenu.ts b/public/app/core/components/sidemenu/sidemenu.ts index a47704f9d7d..30230586e78 100644 --- a/public/app/core/components/sidemenu/sidemenu.ts +++ b/public/app/core/components/sidemenu/sidemenu.ts @@ -72,9 +72,8 @@ export class SideMenuCtrl { this.orgMenu.push({ text: "Switch to " + org.name, icon: "fa fa-fw fa-random", - click: () => { - this.switchOrg(org.orgId); - } + url: this.getUrl('/profile/switch-org/' + org.orgId), + target: '_self' }); }); @@ -83,12 +82,6 @@ export class SideMenuCtrl { } }); } - - switchOrg(orgId) { - this.backendSrv.post('/api/user/using/' + orgId).then(() => { - window.location.href = `${config.appSubUrl}/`; - }); - }; } export function sideMenuDirective() { diff --git a/public/app/core/components/switch.ts b/public/app/core/components/switch.ts index 1dddae20d49..2a64ec487f7 100644 --- a/public/app/core/components/switch.ts +++ b/public/app/core/components/switch.ts @@ -7,7 +7,12 @@ import coreModule from 'app/core/core_module'; import Drop from 'tether-drop'; var template = ` - +
@@ -21,17 +26,14 @@ export class SwitchCtrl { id: any; /** @ngInject */ - constructor($scope) { + constructor($scope, private $timeout) { this.show = true; this.id = $scope.$id; } internalOnChange() { - return new Promise(resolve => { - setTimeout(() => { - this.onChange(); - resolve(); - }); + return this.$timeout(() => { + return this.onChange(); }); } @@ -52,22 +54,6 @@ export function switchDirective() { onChange: "&", }, template: template, - link: (scope, elem) => { - if (scope.ctrl.tooltip) { - var drop = new Drop({ - target: elem[0], - content: scope.ctrl.tooltip, - position: "right middle", - classes: 'drop-help', - openOn: 'hover', - hoverOpenDelay: 400, - }); - - scope.$on('$destroy', function() { - drop.destroy(); - }); - } - } }; } 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/core/directives/plugin_component.ts b/public/app/core/directives/plugin_component.ts index 4f87a94573e..3e6383cc5c6 100644 --- a/public/app/core/directives/plugin_component.ts +++ b/public/app/core/directives/plugin_component.ts @@ -169,7 +169,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ return System.import(model.module).then(function(appModule) { return { baseUrl: model.baseUrl, - name: 'app-config-' + model.pluginId, + name: 'app-config-' + model.id, bindings: {appModel: "=", appEditCtrl: "="}, attrs: {"app-model": "ctrl.model", "app-edit-ctrl": "ctrl"}, Component: appModule.ConfigCtrl, diff --git a/public/app/core/services/popover_srv.ts b/public/app/core/services/popover_srv.ts index 4711dc1b23c..73249a67b5b 100644 --- a/public/app/core/services/popover_srv.ts +++ b/public/app/core/services/popover_srv.ts @@ -46,9 +46,12 @@ function popoverSrv($compile, $rootScope) { drop.on('close', () => { popoverScope.dismiss({fromDropClose: true}); destroyDrop(); + if (options.onClose) { + options.onClose(); + } }); - drop.open(); + setTimeout(() => { drop.open(); }, 10); }; } diff --git a/public/app/core/time_series2.ts b/public/app/core/time_series2.ts index f5fcf5bd50c..cbceff7afd8 100644 --- a/public/app/core/time_series2.ts +++ b/public/app/core/time_series2.ts @@ -42,6 +42,7 @@ export default class TimeSeries { fillBelowTo: any; transform: any; flotpairs: any; + unit: any; constructor(opts) { this.datapoints = opts.datapoints; @@ -52,6 +53,7 @@ export default class TimeSeries { this.valueFormater = kbn.valueFormats.none; this.stats = {}; this.legend = true; + this.unit = opts.unit; } applySeriesOverrides(overrides) { @@ -170,7 +172,7 @@ export default class TimeSeries { } isMsResolutionNeeded() { - for (var i = 0; i -import {Subject} from 'vendor/npm/rxjs/Subject'; +import EventEmitter from 'eventemitter3'; var hasOwnProp = {}.hasOwnProperty; @@ -9,48 +9,27 @@ function createName(name) { } export class Emitter { - subjects: any; + emitter: any; constructor() { - this.subjects = {}; + this.emitter = new EventEmitter(); } emit(name, data?) { - var fnName = createName(name); - this.subjects[fnName] || (this.subjects[fnName] = new Subject()); - this.subjects[fnName].next(data); + this.emitter.emit(name, data); } on(name, handler, scope?) { - var fnName = createName(name); - this.subjects[fnName] || (this.subjects[fnName] = new Subject()); - var subscription = this.subjects[fnName].subscribe(handler); + this.emitter.on(name, handler); if (scope) { scope.$on('$destroy', function() { - subscription.unsubscribe(); + this.emitter.off(name, handler); }); } - - return subscription; - }; + } off(name, handler) { - var fnName = createName(name); - if (this.subjects[fnName]) { - this.subjects[fnName].dispose(); - delete this.subjects[fnName]; - } - } - - dispose() { - var subjects = this.subjects; - for (var prop in subjects) { - if (hasOwnProp.call(subjects, prop)) { - subjects[prop].dispose(); - } - } - - this.subjects = {}; + this.emitter.off(name, handler); } } diff --git a/public/app/features/admin/partials/stats.html b/public/app/features/admin/partials/stats.html index 1ad114f50e2..bcc246b9e82 100644 --- a/public/app/features/admin/partials/stats.html +++ b/public/app/features/admin/partials/stats.html @@ -22,10 +22,6 @@ Total users {{ctrl.stats.user_count}} - - Total grafana admins - {{ctrl.stats.grafana_admin_count}} - Total organizations {{ctrl.stats.org_count}} diff --git a/public/app/features/annotations/editor_ctrl.js b/public/app/features/annotations/editor_ctrl.js index 8d3887ee757..ca754b094a8 100644 --- a/public/app/features/annotations/editor_ctrl.js +++ b/public/app/features/annotations/editor_ctrl.js @@ -45,6 +45,7 @@ function (angular, _, $) { $scope.reset = function() { $scope.currentAnnotation = angular.copy(annotationDefaults); + $scope.currentAnnotation.datasource = $scope.datasources[0].name; $scope.currentIsNew = true; $scope.datasourceChanged(); }; diff --git a/public/app/features/dashboard/dashboardSrv.js b/public/app/features/dashboard/dashboardSrv.js index ed119f6e23f..89c09ac551a 100644 --- a/public/app/features/dashboard/dashboardSrv.js +++ b/public/app/features/dashboard/dashboardSrv.js @@ -428,6 +428,8 @@ function (angular, $, _, moment) { // update graph yaxes changes panelUpgrades.push(function(panel) { if (panel.type !== 'graph') { return; } + if (!panel.grid) { return; } + if (!panel.yaxes) { panel.yaxes = [ { diff --git a/public/app/features/dashboard/dashnav/dashnav.html b/public/app/features/dashboard/dashnav/dashnav.html index aec626bfe2a..9afd152d8aa 100644 --- a/public/app/features/dashboard/dashnav/dashnav.html +++ b/public/app/features/dashboard/dashnav/dashnav.html @@ -36,7 +36,7 @@
  • - +
  • View JSON
  • Make Editable
  • Save As...
  • -
  • Set As Home
  • Delete dashboard
  • diff --git a/public/app/features/dashboard/keybindings.js b/public/app/features/dashboard/keybindings.js index aea3a476a49..b07dd2fd848 100644 --- a/public/app/features/dashboard/keybindings.js +++ b/public/app/features/dashboard/keybindings.js @@ -60,6 +60,14 @@ function(angular, $) { scope.appEvent('zoom-out', evt); }, { inputDisabled: true }); + keyboardManager.bind('left', function(evt) { + scope.appEvent('shift-time-backward', evt); + }, { inputDisabled: true }); + + keyboardManager.bind('right', function(evt) { + scope.appEvent('shift-time-forward', evt); + }, { inputDisabled: true }); + keyboardManager.bind('ctrl+e', function(evt) { scope.appEvent('export-dashboard', evt); }, { inputDisabled: true }); diff --git a/public/app/features/dashboard/partials/settings.html b/public/app/features/dashboard/partials/settings.html index 2a2287613ae..8d071d90495 100644 --- a/public/app/features/dashboard/partials/settings.html +++ b/public/app/features/dashboard/partials/settings.html @@ -26,7 +26,10 @@
    - +
    @@ -46,19 +49,19 @@ label="Editable" tooltip="Uncheck, then save and reload to disable all dashboard editing" checked="dashboard.editable" - label-class="width-10"> + label-class="width-11"> + label-class="width-11"> + label-class="width-11">
    diff --git a/public/app/features/dashboard/partials/shareModal.html b/public/app/features/dashboard/partials/shareModal.html index 41c08fede0b..c8462c7bffc 100644 --- a/public/app/features/dashboard/partials/shareModal.html +++ b/public/app/features/dashboard/partials/shareModal.html @@ -38,31 +38,26 @@
    -
    -
    +
    +
    -
    -
    - -
    -
    @@ -117,7 +113,7 @@

    -