From dfe0e258cdf32b67e5958568879ca8fc4558101d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 10 Feb 2016 11:54:17 +0100 Subject: [PATCH 01/19] feat(text and css): html partials and css can be loaded via systemjs --- public/app/core/utils/css_loader.ts | 78 +++++++++++++++++++++++++++++ public/app/system.conf.js | 2 + public/vendor/plugin-css/css.js | 72 ++++++++++++++++++++++++++ public/vendor/plugin-text/text.js | 16 ++++++ 4 files changed, 168 insertions(+) create mode 100644 public/app/core/utils/css_loader.ts create mode 100644 public/vendor/plugin-css/css.js create mode 100644 public/vendor/plugin-text/text.js diff --git a/public/app/core/utils/css_loader.ts b/public/app/core/utils/css_loader.ts new file mode 100644 index 00000000000..e81e659032d --- /dev/null +++ b/public/app/core/utils/css_loader.ts @@ -0,0 +1,78 @@ +/// + +var waitSeconds = 100; +var head = document.getElementsByTagName('head')[0]; + +// get all link tags in the page +var links = document.getElementsByTagName('link'); +var linkHrefs = []; +for (var i = 0; i < links.length; i++) { + linkHrefs.push(links[i].href); +} + +var isWebkit = !!window.navigator.userAgent.match(/AppleWebKit\/([^ ;]*)/); +var webkitLoadCheck = function(link, callback) { + setTimeout(function() { + for (var i = 0; i < document.styleSheets.length; i++) { + var sheet = document.styleSheets[i]; + if (sheet.href === link.href) { + return callback(); + } + } + webkitLoadCheck(link, callback); + }, 10); +}; + +var noop = function() {}; + +var loadCSS = function(url) { + return new Promise(function(resolve, reject) { + var link = document.createElement('link'); + var timeout = setTimeout(function() { + reject('Unable to load CSS'); + }, waitSeconds * 1000); + + var _callback = function(error) { + clearTimeout(timeout); + link.onload = link.onerror = noop; + setTimeout(function() { + if (error) { + reject(error); + } else { + resolve(''); + } + }, 7); + }; + + link.type = 'text/css'; + link.rel = 'stylesheet'; + link.href = url; + + if (!isWebkit) { + link.onload = function() { _callback(undefined); }; + } else { + webkitLoadCheck(link, _callback); + } + + link.onerror = function(evt: any) { + _callback(evt.error || new Error('Error loading CSS file.')); + }; + + head.appendChild(link); + }); +}; + +export function fetch(load): any { + if (typeof window === 'undefined') { + return ''; + } + + // dont reload styles loaded in the head + for (var i = 0; i < linkHrefs.length; i++) { + if (load.address === linkHrefs[i]) { + return ''; + } + } + return loadCSS(load.address); +} + diff --git a/public/app/system.conf.js b/public/app/system.conf.js index 7238bd65ec2..63bbb789f66 100644 --- a/public/app/system.conf.js +++ b/public/app/system.conf.js @@ -43,6 +43,8 @@ System.config({ }, map: { + text: 'vendor/plugin-text/text.js', + css: 'app/core/utils/css_loader.js' }, meta: { diff --git a/public/vendor/plugin-css/css.js b/public/vendor/plugin-css/css.js new file mode 100644 index 00000000000..44839808385 --- /dev/null +++ b/public/vendor/plugin-css/css.js @@ -0,0 +1,72 @@ +"use strict"; + +if (typeof window !== 'undefined') { + var waitSeconds = 100; + + var head = document.getElementsByTagName('head')[0]; + + // get all link tags in the page + var links = document.getElementsByTagName('link'); + var linkHrefs = []; + for (var i = 0; i < links.length; i++) { + linkHrefs.push(links[i].href); + } + + var isWebkit = !!window.navigator.userAgent.match(/AppleWebKit\/([^ ;]*)/); + var webkitLoadCheck = function(link, callback) { + setTimeout(function() { + for (var i = 0; i < document.styleSheets.length; i++) { + var sheet = document.styleSheets[i]; + if (sheet.href === link.href) { + return callback(); + } + } + webkitLoadCheck(link, callback); + }, 10); + }; + + var noop = function() {}; + + var loadCSS = function(url) { + return new Promise(function(resolve, reject) { + var timeout = setTimeout(function() { + reject('Unable to load CSS'); + }, waitSeconds * 1000); + var _callback = function(error) { + clearTimeout(timeout); + link.onload = link.onerror = noop; + setTimeout(function() { + if (error) { + reject(error); + } + else { + resolve(''); + } + }, 7); + }; + var link = document.createElement('link'); + link.type = 'text/css'; + link.rel = 'stylesheet'; + link.href = url; + if (!isWebkit) { + link.onload = function() { + _callback(); + } + } else { + webkitLoadCheck(link, _callback); + } + link.onerror = function(event) { + _callback(event.error || new Error('Error loading CSS file.')); + }; + head.appendChild(link); + }); + }; + + exports.fetch = function(load) { + // dont reload styles loaded in the head + for (var i = 0; i < linkHrefs.length; i++) + if (load.address == linkHrefs[i]) + return ''; + return loadCSS(load.address); + }; +} diff --git a/public/vendor/plugin-text/text.js b/public/vendor/plugin-text/text.js new file mode 100644 index 00000000000..ce23974dddf --- /dev/null +++ b/public/vendor/plugin-text/text.js @@ -0,0 +1,16 @@ +/* + Text plugin +*/ +exports.translate = function(load) { + load.metadata.format = 'amd'; + return 'def' + 'ine(function() {\nreturn "' + load.source + .replace(/(["\\])/g, '\\$1') + .replace(/[\f]/g, "\\f") + .replace(/[\b]/g, "\\b") + .replace(/[\n]/g, "\\n") + .replace(/[\t]/g, "\\t") + .replace(/[\r]/g, "\\r") + .replace(/[\u2028]/g, "\\u2028") + .replace(/[\u2029]/g, "\\u2029") + + '";\n});'; +} From ae39ec858584f942da67aa235ff0500ac1259d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 10 Feb 2016 13:09:39 +0100 Subject: [PATCH 02/19] feat(plugins): changed so that plugins can load css async via util function exposed from app/plugins/sdk --- package.json | 6 +++--- pkg/api/dtos/index.go | 5 +---- pkg/api/index.go | 4 ---- pkg/plugins/app_plugin.go | 6 ------ public/app/plugins/sdk.ts | 10 ++++++++++ public/views/index.html | 6 ------ 6 files changed, 14 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 7a358ce3e04..4b67aeefacb 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "phantomjs": "^1.9.19", "reflect-metadata": "0.1.2", "rxjs": "5.0.0-beta.0", - "systemjs": "0.19.6", + "systemjs": "0.19.20", "zone.js": "0.5.10" }, "engines": { @@ -68,9 +68,9 @@ "grunt-jscs": "~1.5.x", "grunt-sync": "^0.4.1", "karma-sinon": "^1.0.3", - "lodash": "^2.4.1", + "lodash": "^4.0.0", "sinon": "1.16.1", - "systemjs-builder": "^0.14.15", + "systemjs-builder": "^0.15.7", "tslint": "^3.2.1", "typescript": "^1.7.5" } diff --git a/pkg/api/dtos/index.go b/pkg/api/dtos/index.go index 180ec767281..7c28d734be4 100644 --- a/pkg/api/dtos/index.go +++ b/pkg/api/dtos/index.go @@ -7,10 +7,7 @@ type IndexViewData struct { AppSubUrl string GoogleAnalyticsId string GoogleTagManagerId string - - PluginCss []*PluginCss - PluginModules []string - MainNavLinks []*NavLink + MainNavLinks []*NavLink } type PluginCss struct { diff --git a/pkg/api/index.go b/pkg/api/index.go index 094511df1ea..11b1107674e 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -81,10 +81,6 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { } for _, plugin := range enabledPlugins.Apps { - if plugin.Css != nil { - data.PluginCss = append(data.PluginCss, &dtos.PluginCss{Light: plugin.Css.Light, Dark: plugin.Css.Dark}) - } - if plugin.Pinned { pageLink := &dtos.NavLink{ Text: plugin.Name, diff --git a/pkg/plugins/app_plugin.go b/pkg/plugins/app_plugin.go index 474b7354762..d59df9985a4 100644 --- a/pkg/plugins/app_plugin.go +++ b/pkg/plugins/app_plugin.go @@ -28,7 +28,6 @@ type AppIncludeInfo struct { type AppPlugin struct { FrontendPluginBase - Css *AppPluginCss `json:"css"` Pages []*AppPluginPage `json:"pages"` Routes []*AppPluginRoute `json:"routes"` Includes []*AppIncludeInfo `json:"-"` @@ -68,11 +67,6 @@ func (app *AppPlugin) Load(decoder *json.Decoder, pluginDir string) error { func (app *AppPlugin) initApp() { app.initFrontendPlugin() - if app.Css != nil { - app.Css.Dark = evalRelativePluginUrlPath(app.Css.Dark, app.Id) - app.Css.Light = evalRelativePluginUrlPath(app.Css.Light, app.Id) - } - // check if we have child panels for _, panel := range Panels { if strings.HasPrefix(panel.PluginDir, app.PluginDir) { diff --git a/public/app/plugins/sdk.ts b/public/app/plugins/sdk.ts index a3616903908..854b8777766 100644 --- a/public/app/plugins/sdk.ts +++ b/public/app/plugins/sdk.ts @@ -2,6 +2,16 @@ import {PanelCtrl} from 'app/features/panel/panel_ctrl'; import {MetricsPanelCtrl} from 'app/features/panel/metrics_panel_ctrl'; import {QueryCtrl} from 'app/features/panel/query_ctrl'; +import config from 'app/core/config'; + +export function loadPluginCss(options) { + if (config.bootData.user.lightTheme) { + System.import(options.light + '!css'); + } else { + System.import(options.dark + '!css'); + } +} + export { PanelCtrl, MetricsPanelCtrl, diff --git a/public/views/index.html b/public/views/index.html index 724afd33f33..7c492c01480 100644 --- a/public/views/index.html +++ b/public/views/index.html @@ -10,14 +10,8 @@ [[if .User.LightTheme]] - [[ range $css := .PluginCss ]] - - [[ end ]] [[else]] - [[ range $css := .PluginCss ]] - - [[ end ]] [[end]] From 37c6a1ddf09a893844b93ef01e546f681e1ee176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 10 Feb 2016 16:43:35 +0100 Subject: [PATCH 03/19] feat(app routes): worked on app routes, added unit test, changed Grafana-Context header to start with X to be standard compliant, got cloud saas queries to work via app route feature and header template --- pkg/api/app_routes.go | 90 +++------------------- pkg/api/pluginproxy/pluginproxy.go | 99 +++++++++++++++++++++++++ pkg/api/pluginproxy/pluginproxy_test.go | 42 +++++++++++ pkg/models/app_settings.go | 8 ++ pkg/plugins/app_plugin.go | 1 - pkg/services/sqlstore/app_settings.go | 8 +- 6 files changed, 161 insertions(+), 87 deletions(-) create mode 100644 pkg/api/pluginproxy/pluginproxy.go create mode 100644 pkg/api/pluginproxy/pluginproxy_test.go diff --git a/pkg/api/app_routes.go b/pkg/api/app_routes.go index 169c5c6d15c..5796f09bb21 100644 --- a/pkg/api/app_routes.go +++ b/pkg/api/app_routes.go @@ -1,17 +1,9 @@ package api import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "net/http/httputil" - "net/url" - "text/template" - "gopkg.in/macaron.v1" - "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/api/pluginproxy" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" @@ -22,16 +14,14 @@ import ( func InitAppPluginRoutes(r *macaron.Macaron) { for _, plugin := range plugins.Apps { for _, route := range plugin.Routes { - log.Info("Plugin: Adding proxy route for app plugin") - url := util.JoinUrlFragments("/api/plugin-proxy/", route.Path) + url := util.JoinUrlFragments("/api/plugin-proxy/"+plugin.Id, route.Path) handlers := make([]macaron.Handler, 0) - if route.ReqSignedIn { - handlers = append(handlers, middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true})) - } - if route.ReqGrafanaAdmin { - handlers = append(handlers, middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true})) - } - if route.ReqSignedIn && route.ReqRole != "" { + handlers = append(handlers, middleware.Auth(&middleware.AuthOptions{ + ReqSignedIn: true, + ReqGrafanaAdmin: route.ReqGrafanaAdmin, + })) + + if route.ReqRole != "" { if route.ReqRole == m.ROLE_ADMIN { handlers = append(handlers, middleware.RoleAuth(m.ROLE_ADMIN)) } else if route.ReqRole == m.ROLE_EDITOR { @@ -40,7 +30,7 @@ func InitAppPluginRoutes(r *macaron.Macaron) { } handlers = append(handlers, AppPluginRoute(route, plugin.Id)) r.Route(url, route.Method, handlers...) - log.Info("Plugin: Adding route %s", url) + log.Info("Plugins: Adding proxy route %s", url) } } } @@ -49,68 +39,8 @@ func AppPluginRoute(route *plugins.AppPluginRoute, appId string) macaron.Handler return func(c *middleware.Context) { path := c.Params("*") - proxy := NewApiPluginProxy(c, path, route, appId) + proxy := pluginproxy.NewApiPluginProxy(c, path, route, appId) proxy.Transport = dataProxyTransport proxy.ServeHTTP(c.Resp, c.Req.Request) } } - -func NewApiPluginProxy(ctx *middleware.Context, proxyPath string, route *plugins.AppPluginRoute, appId string) *httputil.ReverseProxy { - targetUrl, _ := url.Parse(route.Url) - - director := func(req *http.Request) { - - req.URL.Scheme = targetUrl.Scheme - req.URL.Host = targetUrl.Host - req.Host = targetUrl.Host - - req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath) - - // clear cookie headers - req.Header.Del("Cookie") - req.Header.Del("Set-Cookie") - - //Create a HTTP header with the context in it. - ctxJson, err := json.Marshal(ctx.SignedInUser) - if err != nil { - ctx.JsonApiErr(500, "failed to marshal context to json.", err) - return - } - - req.Header.Add("Grafana-Context", string(ctxJson)) - // add custom headers defined in the plugin config. - for _, header := range route.Headers { - var contentBuf bytes.Buffer - t, err := template.New("content").Parse(header.Content) - if err != nil { - ctx.JsonApiErr(500, fmt.Sprintf("could not parse header content template for header %s.", header.Name), err) - return - } - - //lookup appSettings - query := m.GetAppSettingByAppIdQuery{OrgId: ctx.OrgId, AppId: appId} - - if err := bus.Dispatch(&query); err != nil { - ctx.JsonApiErr(500, "failed to get AppSettings.", err) - return - } - type templateData struct { - JsonData map[string]interface{} - SecureJsonData map[string]string - } - data := templateData{ - JsonData: query.Result.JsonData, - SecureJsonData: query.Result.SecureJsonData.Decrypt(), - } - err = t.Execute(&contentBuf, data) - if err != nil { - ctx.JsonApiErr(500, fmt.Sprintf("failed to execute header content template for header %s.", header.Name), err) - return - } - log.Debug("Adding header to proxy request. %s: %s", header.Name, contentBuf.String()) - req.Header.Add(header.Name, contentBuf.String()) - } - } - - return &httputil.ReverseProxy{Director: director} -} diff --git a/pkg/api/pluginproxy/pluginproxy.go b/pkg/api/pluginproxy/pluginproxy.go new file mode 100644 index 00000000000..55aa0c013ce --- /dev/null +++ b/pkg/api/pluginproxy/pluginproxy.go @@ -0,0 +1,99 @@ +package pluginproxy + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "net/http/httputil" + "net/url" + "text/template" + + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/middleware" + m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/util" +) + +type templateData struct { + JsonData map[string]interface{} + SecureJsonData map[string]string +} + +func getHeaders(route *plugins.AppPluginRoute, orgId int64, appId string) (http.Header, error) { + result := http.Header{} + + query := m.GetAppSettingByAppIdQuery{OrgId: orgId, AppId: appId} + + if err := bus.Dispatch(&query); err != nil { + return nil, err + } + + data := templateData{ + JsonData: query.Result.JsonData, + SecureJsonData: query.Result.SecureJsonData.Decrypt(), + } + + for _, header := range route.Headers { + var contentBuf bytes.Buffer + t, err := template.New("content").Parse(header.Content) + if err != nil { + return nil, errors.New(fmt.Sprintf("could not parse header content template for header %s.", header.Name)) + } + + err = t.Execute(&contentBuf, data) + if err != nil { + return nil, errors.New(fmt.Sprintf("failed to execute header content template for header %s.", header.Name)) + } + + log.Trace("Adding header to proxy request. %s: %s", header.Name, contentBuf.String()) + result.Add(header.Name, contentBuf.String()) + } + + return result, nil +} + +func NewApiPluginProxy(ctx *middleware.Context, proxyPath string, route *plugins.AppPluginRoute, appId string) *httputil.ReverseProxy { + targetUrl, _ := url.Parse(route.Url) + + director := func(req *http.Request) { + + req.URL.Scheme = targetUrl.Scheme + req.URL.Host = targetUrl.Host + req.Host = targetUrl.Host + + req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath) + + // clear cookie headers + req.Header.Del("Cookie") + req.Header.Del("Set-Cookie") + + //Create a HTTP header with the context in it. + ctxJson, err := json.Marshal(ctx.SignedInUser) + if err != nil { + ctx.JsonApiErr(500, "failed to marshal context to json.", err) + return + } + + req.Header.Add("X-Grafana-Context", string(ctxJson)) + + if len(route.Headers) > 0 { + headers, err := getHeaders(route, ctx.OrgId, appId) + if err != nil { + ctx.JsonApiErr(500, "Could not generate plugin route header", err) + return + } + + for key, value := range headers { + log.Info("setting key %v value %v", key, value[0]) + req.Header.Set(key, value[0]) + } + } + + } + + return &httputil.ReverseProxy{Director: director} +} diff --git a/pkg/api/pluginproxy/pluginproxy_test.go b/pkg/api/pluginproxy/pluginproxy_test.go new file mode 100644 index 00000000000..0b6e9523ffd --- /dev/null +++ b/pkg/api/pluginproxy/pluginproxy_test.go @@ -0,0 +1,42 @@ +package pluginproxy + +import ( + "testing" + + "github.com/grafana/grafana/pkg/bus" + m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/util" + . "github.com/smartystreets/goconvey/convey" +) + +func TestPluginProxy(t *testing.T) { + + Convey("When getting proxy headers", t, func() { + route := &plugins.AppPluginRoute{ + Headers: []plugins.AppPluginRouteHeader{ + {Name: "x-header", Content: "my secret {{.SecureJsonData.key}}"}, + }, + } + + setting.SecretKey = "password" + + bus.AddHandler("test", func(query *m.GetAppSettingByAppIdQuery) error { + query.Result = &m.AppSettings{ + SecureJsonData: map[string][]byte{ + "key": util.Encrypt([]byte("123"), "password"), + }, + } + return nil + }) + + header, err := getHeaders(route, 1, "my-app") + So(err, ShouldBeNil) + + Convey("Should render header template", func() { + So(header.Get("x-header"), ShouldEqual, "my secret 123") + }) + }) + +} diff --git a/pkg/models/app_settings.go b/pkg/models/app_settings.go index 78d4c483f2b..6a7bbde694d 100644 --- a/pkg/models/app_settings.go +++ b/pkg/models/app_settings.go @@ -49,6 +49,14 @@ type UpdateAppSettingsCmd struct { OrgId int64 `json:"-"` } +func (cmd *UpdateAppSettingsCmd) GetEncryptedJsonData() SecureJsonData { + encrypted := make(SecureJsonData) + for key, data := range cmd.SecureJsonData { + encrypted[key] = util.Encrypt([]byte(data), setting.SecretKey) + } + return encrypted +} + // --------------------- // QUERIES type GetAppSettingsQuery struct { diff --git a/pkg/plugins/app_plugin.go b/pkg/plugins/app_plugin.go index d59df9985a4..190ce8a9632 100644 --- a/pkg/plugins/app_plugin.go +++ b/pkg/plugins/app_plugin.go @@ -39,7 +39,6 @@ type AppPlugin struct { type AppPluginRoute struct { Path string `json:"path"` Method string `json:"method"` - ReqSignedIn bool `json:"reqSignedIn"` ReqGrafanaAdmin bool `json:"reqGrafanaAdmin"` ReqRole models.RoleType `json:"reqRole"` Url string `json:"url"` diff --git a/pkg/services/sqlstore/app_settings.go b/pkg/services/sqlstore/app_settings.go index f454d2cc5ff..e7d8a90a495 100644 --- a/pkg/services/sqlstore/app_settings.go +++ b/pkg/services/sqlstore/app_settings.go @@ -42,18 +42,13 @@ func UpdateAppSettings(cmd *m.UpdateAppSettingsCmd) error { sess.UseBool("enabled") sess.UseBool("pinned") if !exists { - // encrypt secureJsonData - secureJsonData := make(map[string][]byte) - for key, data := range cmd.SecureJsonData { - secureJsonData[key] = util.Encrypt([]byte(data), setting.SecretKey) - } app = m.AppSettings{ AppId: cmd.AppId, OrgId: cmd.OrgId, Enabled: cmd.Enabled, Pinned: cmd.Pinned, JsonData: cmd.JsonData, - SecureJsonData: secureJsonData, + SecureJsonData: cmd.GetEncryptedJsonData(), Created: time.Now(), Updated: time.Now(), } @@ -63,6 +58,7 @@ func UpdateAppSettings(cmd *m.UpdateAppSettingsCmd) error { for key, data := range cmd.SecureJsonData { app.SecureJsonData[key] = util.Encrypt([]byte(data), setting.SecretKey) } + app.SecureJsonData = cmd.GetEncryptedJsonData() app.Updated = time.Now() app.Enabled = cmd.Enabled app.JsonData = cmd.JsonData From 4c35c83b84b32725ab2dca4afdec8422367cc883 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 10 Feb 2016 11:13:17 -0800 Subject: [PATCH 04/19] Minor html fix --- public/app/plugins/panel/graph/axisEditor.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/graph/axisEditor.html b/public/app/plugins/panel/graph/axisEditor.html index f7e183a5651..d9b7b4599e0 100644 --- a/public/app/plugins/panel/graph/axisEditor.html +++ b/public/app/plugins/panel/graph/axisEditor.html @@ -181,7 +181,7 @@
  • -
  • +
  • Side width
  • From fac51d92c5f5d90d1fd23569bdefb54aaa30135a Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Thu, 11 Feb 2016 00:06:04 -0800 Subject: [PATCH 05/19] Added DS deletion confirmation modal --- public/app/core/routes/routes.ts | 1 + public/app/features/datasources/list_ctrl.js | 36 ------------- public/app/features/datasources/list_ctrl.ts | 53 +++++++++++++++++++ .../features/datasources/partials/list.html | 8 +-- 4 files changed, 58 insertions(+), 40 deletions(-) delete mode 100644 public/app/features/datasources/list_ctrl.js create mode 100644 public/app/features/datasources/list_ctrl.ts diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts index 8963228210b..82da84fbc56 100644 --- a/public/app/core/routes/routes.ts +++ b/public/app/core/routes/routes.ts @@ -45,6 +45,7 @@ function setupAngularRoutes($routeProvider, $locationProvider) { .when('/datasources', { templateUrl: 'public/app/features/datasources/partials/list.html', controller : 'DataSourcesCtrl', + controllerAs: 'ctrl', resolve: loadOrgBundle, }) .when('/datasources/edit/:id', { diff --git a/public/app/features/datasources/list_ctrl.js b/public/app/features/datasources/list_ctrl.js deleted file mode 100644 index da77df252e1..00000000000 --- a/public/app/features/datasources/list_ctrl.js +++ /dev/null @@ -1,36 +0,0 @@ -define([ - 'angular', - 'lodash', -], -function (angular) { - 'use strict'; - - var module = angular.module('grafana.controllers'); - - module.controller('DataSourcesCtrl', function($scope, $http, backendSrv, datasourceSrv) { - - $scope.init = function() { - $scope.datasources = []; - $scope.getDatasources(); - }; - - $scope.getDatasources = function() { - backendSrv.get('/api/datasources').then(function(results) { - $scope.datasources = results; - }); - }; - - $scope.remove = function(ds) { - backendSrv.delete('/api/datasources/' + ds.id).then(function() { - $scope.getDatasources(); - - backendSrv.get('/api/frontend/settings').then(function(settings) { - datasourceSrv.init(settings.datasources); - }); - }); - }; - - $scope.init(); - - }); -}); diff --git a/public/app/features/datasources/list_ctrl.ts b/public/app/features/datasources/list_ctrl.ts new file mode 100644 index 00000000000..20d480bb96a --- /dev/null +++ b/public/app/features/datasources/list_ctrl.ts @@ -0,0 +1,53 @@ +/// + +import angular from 'angular'; +import _ from 'lodash'; +import coreModule from '../../core/core_module'; + +export class DataSourcesCtrl { + datasources: any; + + /** @ngInject */ + constructor(private $scope, private $location, private $http, private backendSrv, private datasourceSrv) { + backendSrv.get('/api/datasources') + .then((result) => { + this.datasources = result; + }); + } + + removeDataSourceConfirmed(ds) { + ///_.remove(this.playlistsd, { id: playlist.id }); + + this.backendSrv.delete('/api/datasources/' + ds.id) + .then(() => { + this.$scope.appEvent('alert-success', ['Datasource deleted', '']); + }, () => { + this.$scope.appEvent('alert-error', ['Unable to delete datasource', '']); + }).then(() => { + this.backendSrv.get('/api/datasources') + .then((result) => { + this.datasources = result; + }); + this.backendSrv.get('/api/frontend/settings') + .then((settings) => { + this.datasourceSrv.init(settings.datasources); + }); + }); + } + + removeDataSource(ds) { + + this.$scope.appEvent('confirm-modal', { + title: 'Confirm delete datasource', + text: 'Are you sure you want to delete datasource ' + ds.name + '?', + yesText: "Delete", + icon: "fa-warning", + onConfirm: () => { + this.removeDataSourceConfirmed(ds); + } + }); + } + +} + +coreModule.controller('DataSourcesCtrl', DataSourcesCtrl); diff --git a/public/app/features/datasources/partials/list.html b/public/app/features/datasources/partials/list.html index ec660ef79d4..bdd6fe363a0 100644 --- a/public/app/features/datasources/partials/list.html +++ b/public/app/features/datasources/partials/list.html @@ -12,11 +12,11 @@

    Data sources


    -
    +
    No data sources defined
    - +
    @@ -27,7 +27,7 @@ - + From 2d8180fc9c7cef3354f08fe6fc78a700ce5be69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 Feb 2016 13:42:18 +0100 Subject: [PATCH 06/19] fix(panels): restored panel name to edit mode --- public/app/features/panel/panel_directive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index 2497732928b..85431964cd9 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -32,7 +32,7 @@ var panelTemplate = `
    - {{ctrl.name}} + {{ctrl.pluginName}}
    From 50ddf7913a6d2affed70dcdc94562cc19942243d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 Feb 2016 14:28:04 +0100 Subject: [PATCH 07/19] docs(): work on example app --- examples/nginx-app/.gitignore | 7 +++ examples/nginx-app/.jscs.json | 13 +++++ examples/nginx-app/.jshintrc | 36 ++++++++++++ examples/nginx-app/Gruntfile.js | 54 ++++++++++++++++++ examples/nginx-app/module.js | 20 ------- examples/nginx-app/package.json | 37 ++++++++++++ examples/nginx-app/panel/module.js | 21 ------- examples/nginx-app/partials/config.html | 1 - examples/nginx-app/partials/logs.html | 2 - examples/nginx-app/partials/stream.html | 1 - examples/nginx-app/src/components/config.html | 3 + examples/nginx-app/src/components/config.js | 6 ++ examples/nginx-app/src/components/logs.html | 3 + examples/nginx-app/src/components/logs.js | 6 ++ examples/nginx-app/src/components/stream.html | 3 + examples/nginx-app/src/components/stream.js | 6 ++ examples/nginx-app/{ => src}/css/dark.css | 0 examples/nginx-app/{ => src}/css/light.css | 0 .../nginx-app/{ => src}/img/logo_large.png | Bin .../nginx-app/{ => src}/img/logo_small.png | Bin examples/nginx-app/src/module.js | 9 +++ examples/nginx-app/src/panel/module.js | 15 +++++ .../nginx-app/{ => src}/panel/plugin.json | 0 23 files changed, 198 insertions(+), 45 deletions(-) create mode 100644 examples/nginx-app/.gitignore create mode 100644 examples/nginx-app/.jscs.json create mode 100644 examples/nginx-app/.jshintrc create mode 100644 examples/nginx-app/Gruntfile.js delete mode 100644 examples/nginx-app/module.js create mode 100644 examples/nginx-app/package.json delete mode 100644 examples/nginx-app/panel/module.js delete mode 100644 examples/nginx-app/partials/config.html delete mode 100644 examples/nginx-app/partials/logs.html delete mode 100644 examples/nginx-app/partials/stream.html create mode 100644 examples/nginx-app/src/components/config.html create mode 100644 examples/nginx-app/src/components/config.js create mode 100644 examples/nginx-app/src/components/logs.html create mode 100644 examples/nginx-app/src/components/logs.js create mode 100644 examples/nginx-app/src/components/stream.html create mode 100644 examples/nginx-app/src/components/stream.js rename examples/nginx-app/{ => src}/css/dark.css (100%) rename examples/nginx-app/{ => src}/css/light.css (100%) rename examples/nginx-app/{ => src}/img/logo_large.png (100%) rename examples/nginx-app/{ => src}/img/logo_small.png (100%) create mode 100644 examples/nginx-app/src/module.js create mode 100644 examples/nginx-app/src/panel/module.js rename examples/nginx-app/{ => src}/panel/plugin.json (100%) diff --git a/examples/nginx-app/.gitignore b/examples/nginx-app/.gitignore new file mode 100644 index 00000000000..8c2c350441b --- /dev/null +++ b/examples/nginx-app/.gitignore @@ -0,0 +1,7 @@ +.DS_Store + +node_modules +tmp/* +npm-debug.log +dist/* + diff --git a/examples/nginx-app/.jscs.json b/examples/nginx-app/.jscs.json new file mode 100644 index 00000000000..dcf694dcc63 --- /dev/null +++ b/examples/nginx-app/.jscs.json @@ -0,0 +1,13 @@ +{ + "disallowImplicitTypeConversion": ["string"], + "disallowKeywords": ["with"], + "disallowMultipleLineBreaks": true, + "disallowMixedSpacesAndTabs": true, + "disallowTrailingWhitespace": true, + "requireSpacesInFunctionExpression": { + "beforeOpeningCurlyBrace": true + }, + "disallowSpacesInsideArrayBrackets": true, + "disallowSpacesInsideParentheses": true, + "validateIndentation": 2 +} \ No newline at end of file diff --git a/examples/nginx-app/.jshintrc b/examples/nginx-app/.jshintrc new file mode 100644 index 00000000000..3725af83afc --- /dev/null +++ b/examples/nginx-app/.jshintrc @@ -0,0 +1,36 @@ +{ + "browser": true, + "esnext": true, + + "bitwise":false, + "curly": true, + "eqnull": true, + "devel": true, + "eqeqeq": true, + "forin": false, + "immed": true, + "supernew": true, + "expr": true, + "indent": 2, + "latedef": true, + "newcap": true, + "noarg": true, + "noempty": true, + "undef": true, + "boss": true, + "trailing": true, + "laxbreak": true, + "laxcomma": true, + "sub": true, + "unused": true, + "maxdepth": 6, + "maxlen": 140, + + "globals": { + "System": true, + "define": true, + "require": true, + "Chromath": false, + "setImmediate": true + } +} diff --git a/examples/nginx-app/Gruntfile.js b/examples/nginx-app/Gruntfile.js new file mode 100644 index 00000000000..d36a4716f31 --- /dev/null +++ b/examples/nginx-app/Gruntfile.js @@ -0,0 +1,54 @@ +module.exports = function(grunt) { + + require('load-grunt-tasks')(grunt); + + grunt.loadNpmTasks('grunt-execute'); + grunt.loadNpmTasks('grunt-contrib-clean'); + + grunt.initConfig({ + + clean: ["dist"], + + copy: { + src_to_dist: { + cwd: 'src', + expand: true, + src: ['**/*', '!**/*.js', '!**/*.scss'], + dest: 'dist' + }, + pluginDef: { + expand: true, + src: 'plugin.json', + dest: 'dist', + } + }, + + watch: { + rebuild_all: { + files: ['src/**/*', 'plugin.json'], + tasks: ['default'], + options: {spawn: false} + }, + }, + + babel: { + options: { + sourceMap: true, + presets: ["es2015"], + plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"], + }, + dist: { + files: [{ + cwd: 'src', + expand: true, + src: ['**/*.js'], + dest: 'dist', + ext:'.js' + }] + }, + }, + + }); + + grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel']); +}; diff --git a/examples/nginx-app/module.js b/examples/nginx-app/module.js deleted file mode 100644 index 9c1cccb2e52..00000000000 --- a/examples/nginx-app/module.js +++ /dev/null @@ -1,20 +0,0 @@ -define([ -], function() { - 'use strict'; - - function StreamPageCtrl() {} - StreamPageCtrl.templateUrl = 'partials/stream.html'; - - function LogsPageCtrl() {} - LogsPageCtrl.templateUrl = 'partials/logs.html'; - - function NginxConfigCtrl() {} - NginxConfigCtrl.templateUrl = 'partials/config.html'; - - return { - ConfigCtrl: NginxConfigCtrl, - StreamPageCtrl: StreamPageCtrl, - LogsPageCtrl: LogsPageCtrl, - }; - -}); diff --git a/examples/nginx-app/package.json b/examples/nginx-app/package.json new file mode 100644 index 00000000000..91c53734ec8 --- /dev/null +++ b/examples/nginx-app/package.json @@ -0,0 +1,37 @@ +{ + "name": "kentik-app", + "private": true, + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/raintank/kentik-app-poc.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/raintank/kentik-app-poc/issues" + }, + "devDependencies": { + "grunt": "~0.4.5", + "babel": "~6.5.1", + "grunt-babel": "~6.0.0", + "grunt-contrib-copy": "~0.8.2", + "grunt-contrib-watch": "^0.6.1", + "grunt-contrib-uglify": "~0.11.0", + "grunt-systemjs-builder": "^0.2.5", + "load-grunt-tasks": "~3.2.0", + "grunt-execute": "~0.2.2", + "grunt-contrib-clean": "~0.6.0" + }, + "dependencies": { + "babel-plugin-transform-es2015-modules-systemjs": "^6.5.0", + "babel-preset-es2015": "^6.5.0", + "lodash": "~4.0.0" + }, + "homepage": "https://github.com/raintank/kentik-app-poc#readme" +} diff --git a/examples/nginx-app/panel/module.js b/examples/nginx-app/panel/module.js deleted file mode 100644 index 9ccc5b6c962..00000000000 --- a/examples/nginx-app/panel/module.js +++ /dev/null @@ -1,21 +0,0 @@ -define([ - 'app/plugins/sdk' -], function(sdk) { - 'use strict'; - - var NginxPanel = (function(_super) { - function NginxPanel($scope, $injector) { - _super.call(this, $scope, $injector); - } - - NginxPanel.template = '

    nginx!

    '; - NginxPanel.prototype = Object.create(_super.prototype); - NginxPanel.prototype.constructor = NginxPanel; - - return NginxPanel; - })(sdk.PanelCtrl); - - return { - PanelCtrl: NginxPanel - }; -}); diff --git a/examples/nginx-app/partials/config.html b/examples/nginx-app/partials/config.html deleted file mode 100644 index cdd6b8d9b60..00000000000 --- a/examples/nginx-app/partials/config.html +++ /dev/null @@ -1 +0,0 @@ -

    nginx config

    diff --git a/examples/nginx-app/partials/logs.html b/examples/nginx-app/partials/logs.html deleted file mode 100644 index f6bef036395..00000000000 --- a/examples/nginx-app/partials/logs.html +++ /dev/null @@ -1,2 +0,0 @@ - -Logs! diff --git a/examples/nginx-app/partials/stream.html b/examples/nginx-app/partials/stream.html deleted file mode 100644 index 092c36cc24c..00000000000 --- a/examples/nginx-app/partials/stream.html +++ /dev/null @@ -1 +0,0 @@ -streams! diff --git a/examples/nginx-app/src/components/config.html b/examples/nginx-app/src/components/config.html new file mode 100644 index 00000000000..c531ec36d76 --- /dev/null +++ b/examples/nginx-app/src/components/config.html @@ -0,0 +1,3 @@ +

    + Nginx config! +

    diff --git a/examples/nginx-app/src/components/config.js b/examples/nginx-app/src/components/config.js new file mode 100644 index 00000000000..bb8f007b9bc --- /dev/null +++ b/examples/nginx-app/src/components/config.js @@ -0,0 +1,6 @@ + +export class NginxAppConfigCtrl { +} +NginxAppConfigCtrl.templateUrl = 'components/config.html'; + + diff --git a/examples/nginx-app/src/components/logs.html b/examples/nginx-app/src/components/logs.html new file mode 100644 index 00000000000..ca215772bf5 --- /dev/null +++ b/examples/nginx-app/src/components/logs.html @@ -0,0 +1,3 @@ +

    + Logs page! +

    diff --git a/examples/nginx-app/src/components/logs.js b/examples/nginx-app/src/components/logs.js new file mode 100644 index 00000000000..5b67290381b --- /dev/null +++ b/examples/nginx-app/src/components/logs.js @@ -0,0 +1,6 @@ + +export class LogsPageCtrl { +} +LogsPageCtrl.templateUrl = 'components/logs.html'; + + diff --git a/examples/nginx-app/src/components/stream.html b/examples/nginx-app/src/components/stream.html new file mode 100644 index 00000000000..ad70ca4df50 --- /dev/null +++ b/examples/nginx-app/src/components/stream.html @@ -0,0 +1,3 @@ +

    + Stream page! +

    diff --git a/examples/nginx-app/src/components/stream.js b/examples/nginx-app/src/components/stream.js new file mode 100644 index 00000000000..8684b36c64d --- /dev/null +++ b/examples/nginx-app/src/components/stream.js @@ -0,0 +1,6 @@ + +export class StreamPageCtrl { +} +StreamPageCtrl.templateUrl = 'components/stream.html'; + + diff --git a/examples/nginx-app/css/dark.css b/examples/nginx-app/src/css/dark.css similarity index 100% rename from examples/nginx-app/css/dark.css rename to examples/nginx-app/src/css/dark.css diff --git a/examples/nginx-app/css/light.css b/examples/nginx-app/src/css/light.css similarity index 100% rename from examples/nginx-app/css/light.css rename to examples/nginx-app/src/css/light.css diff --git a/examples/nginx-app/img/logo_large.png b/examples/nginx-app/src/img/logo_large.png similarity index 100% rename from examples/nginx-app/img/logo_large.png rename to examples/nginx-app/src/img/logo_large.png diff --git a/examples/nginx-app/img/logo_small.png b/examples/nginx-app/src/img/logo_small.png similarity index 100% rename from examples/nginx-app/img/logo_small.png rename to examples/nginx-app/src/img/logo_small.png diff --git a/examples/nginx-app/src/module.js b/examples/nginx-app/src/module.js new file mode 100644 index 00000000000..b5aeecc6ccf --- /dev/null +++ b/examples/nginx-app/src/module.js @@ -0,0 +1,9 @@ +import {LogsPageCtrl} from './components/logs'; +import {StreamPageCtrl} from './components/stream'; +import {NginxAppConfigCtrl} from './components/config'; + +export { + NginxAppConfigCtrl as ConfigCtrl, + StreamPageCtrl, + LogsPageCtrl +}; diff --git a/examples/nginx-app/src/panel/module.js b/examples/nginx-app/src/panel/module.js new file mode 100644 index 00000000000..899586da81b --- /dev/null +++ b/examples/nginx-app/src/panel/module.js @@ -0,0 +1,15 @@ +import {PanelCtrl} from 'app/plugins/sdk'; + +class NginxPanelCtrl extends PanelCtrl { + + constructor($scope, $injector) { + super($scope, $injector); + } + +} +NginxPanelCtrl.template = '

    nginx!

    '; + +export { + NginxPanelCtrl as PanelCtrl +}; + diff --git a/examples/nginx-app/panel/plugin.json b/examples/nginx-app/src/panel/plugin.json similarity index 100% rename from examples/nginx-app/panel/plugin.json rename to examples/nginx-app/src/panel/plugin.json From bf1b60564e97b6cffb82700466e621b399598bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 Feb 2016 14:34:15 +0100 Subject: [PATCH 08/19] fix(png rendering): fixed broken png rendering due to recent panel change --- public/app/features/panel/partials/soloPanel.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/features/panel/partials/soloPanel.html b/public/app/features/panel/partials/soloPanel.html index d2a4f38844f..eb0c95baaec 100644 --- a/public/app/features/panel/partials/soloPanel.html +++ b/public/app/features/panel/partials/soloPanel.html @@ -2,8 +2,8 @@
    - - + +
    From 4e33e8095733699776bc8c9568ee443163ed643e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 Feb 2016 15:25:34 +0100 Subject: [PATCH 09/19] fix(): clicking on items in graphites add function menu did not work, now works again, broken in recent panel refactorings --- examples/nginx-app/src/dashboards/dashboard.js | 17 +++++++++++++++++ .../datasource/graphite/add_graphite_func.js | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 examples/nginx-app/src/dashboards/dashboard.js diff --git a/examples/nginx-app/src/dashboards/dashboard.js b/examples/nginx-app/src/dashboards/dashboard.js new file mode 100644 index 00000000000..794e2c5217b --- /dev/null +++ b/examples/nginx-app/src/dashboards/dashboard.js @@ -0,0 +1,17 @@ +require([ +], function () { + + function Dashboard() { + + this.getInputs = function() { + + }; + + this.buildDashboard = function() { + + }; + } + + return Dashboard; +}); + diff --git a/public/app/plugins/datasource/graphite/add_graphite_func.js b/public/app/plugins/datasource/graphite/add_graphite_func.js index 6c218513d57..4547526d08f 100644 --- a/public/app/plugins/datasource/graphite/add_graphite_func.js +++ b/public/app/plugins/datasource/graphite/add_graphite_func.js @@ -99,7 +99,7 @@ function (angular, _, $, gfunc) { submenu: _.map(list, function(value) { return { text: value.name, - click: "addFunction('" + value.name + "')", + click: "ctrl.addFunction('" + value.name + "')", }; }) }; From ac5f7ecdeafbefa2420cd8e0e844f75d5639ed7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 11 Feb 2016 15:49:55 +0100 Subject: [PATCH 10/19] fix(img): fixed img link on signup --- public/app/partials/signup_step2.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/partials/signup_step2.html b/public/app/partials/signup_step2.html index 3e106cfa159..58cecab1cbf 100644 --- a/public/app/partials/signup_step2.html +++ b/public/app/partials/signup_step2.html @@ -6,7 +6,7 @@
    From 814ca99e3d7f84f2ef152cdc48675c17f82914f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 12 Feb 2016 09:52:45 +0100 Subject: [PATCH 13/19] fix(google tag manager): fixed link to google tag manager, fixes #4005 --- public/views/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/views/index.html b/public/views/index.html index 7c492c01480..e24d4ec6e7d 100644 --- a/public/views/index.html +++ b/public/views/index.html @@ -81,7 +81,7 @@ }]; - + From f797b19825bdb99570e6b2a98d48ff439bf8731a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 12 Feb 2016 09:58:00 +0100 Subject: [PATCH 14/19] fix(build): changed from postinstall script to regular grunt task for the copying of npm dependencies into public dir, fixes #4003 --- package.json | 3 +-- tasks/build_task.js | 1 + tasks/default_task.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4b67aeefacb..2331bf0fe20 100644 --- a/package.json +++ b/package.json @@ -59,8 +59,7 @@ }, "scripts": { "test": "grunt test", - "coveralls": "grunt karma:coveralls && rm -rf ./coverage", - "postinstall": "./node_modules/.bin/grunt copy:node_modules" + "coveralls": "grunt karma:coveralls && rm -rf ./coverage" }, "license": "Apache-2.0", "dependencies": { diff --git a/tasks/build_task.js b/tasks/build_task.js index d844b661722..3ace8514164 100644 --- a/tasks/build_task.js +++ b/tasks/build_task.js @@ -8,6 +8,7 @@ module.exports = function(grunt) { 'jscs', 'tslint', 'clean:release', + 'copy:node_modules', 'copy:public_to_gen', 'typescript:build', 'karma:test', diff --git a/tasks/default_task.js b/tasks/default_task.js index 7877fc2a34b..033acb3921e 100644 --- a/tasks/default_task.js +++ b/tasks/default_task.js @@ -8,6 +8,7 @@ module.exports = function(grunt) { 'jshint', 'tslint', 'clean:gen', + 'copy:node_modules', 'copy:public_to_gen', 'phantomjs', 'css', From 53f5cb6553d9d2e1b01e7eda74d12714e4eaa41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 12 Feb 2016 10:01:45 +0100 Subject: [PATCH 15/19] fix(api): org name taken error now returns HTTP status code 409, closes --- pkg/api/org.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/api/org.go b/pkg/api/org.go index 0316116b6d3..61af62311c6 100644 --- a/pkg/api/org.go +++ b/pkg/api/org.go @@ -84,7 +84,7 @@ func CreateOrg(c *middleware.Context, cmd m.CreateOrgCommand) Response { cmd.UserId = c.UserId if err := bus.Dispatch(&cmd); err != nil { if err == m.ErrOrgNameTaken { - return ApiError(400, "Organization name taken", err) + return ApiError(409, "Organization name taken", err) } return ApiError(500, "Failed to create organization", err) } From b5dc1727d2cf668211ee89c85b22ca32803df375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 12 Feb 2016 10:10:07 +0100 Subject: [PATCH 16/19] fix(postgres): If password or user is empty use empty quotes for connection string, #3985 --- pkg/services/sqlstore/sqlstore.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index 3ab4bf1c00b..e3db2d7f537 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -149,8 +149,13 @@ func getEngine() (*xorm.Engine, error) { if len(fields) > 1 && len(strings.TrimSpace(fields[1])) > 0 { port = fields[1] } - cnnstr = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s", - DbCfg.User, DbCfg.Pwd, host, port, DbCfg.Name, DbCfg.SslMode) + if DbCfg.Pwd == "" { + DbCfg.Pwd = "''" + } + if DbCfg.User == "" { + DbCfg.User = "''" + } + cnnstr = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s", DbCfg.User, DbCfg.Pwd, host, port, DbCfg.Name, DbCfg.SslMode) case "sqlite3": if !filepath.IsAbs(DbCfg.Path) { DbCfg.Path = filepath.Join(setting.DataPath, DbCfg.Path) From 23167a0bb3d1d1651f52dbc7a6be1300bb2429f4 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Fri, 12 Feb 2016 07:34:56 -0800 Subject: [PATCH 17/19] Viewers restricted to edit panel --- public/app/core/controllers/json_editor_ctrl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/core/controllers/json_editor_ctrl.js b/public/app/core/controllers/json_editor_ctrl.js index f83a2ac3e8c..7d7d56fa96b 100644 --- a/public/app/core/controllers/json_editor_ctrl.js +++ b/public/app/core/controllers/json_editor_ctrl.js @@ -8,7 +8,7 @@ function (angular, coreModule) { coreModule.default.controller('JsonEditorCtrl', function($scope) { $scope.json = angular.toJson($scope.object, true); - $scope.canUpdate = $scope.updateHandler !== void 0; + $scope.canUpdate = $scope.updateHandler !== void 0 && $scope.contextSrv.isEditor; $scope.update = function () { var newObject = angular.fromJson($scope.json); From 92f4442356418f4578655e706fea6c73c0aa145a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 14 Feb 2016 10:39:20 +0100 Subject: [PATCH 18/19] fix(influxdb): fixed annotations editor, broken recently in master due to plugin changes, fixes #4023 --- .../datasource/influxdb/partials/annotations.editor.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/datasource/influxdb/partials/annotations.editor.html b/public/app/plugins/datasource/influxdb/partials/annotations.editor.html index 6d71ec3211a..03ef1df0b52 100644 --- a/public/app/plugins/datasource/influxdb/partials/annotations.editor.html +++ b/public/app/plugins/datasource/influxdb/partials/annotations.editor.html @@ -2,7 +2,7 @@
    InfluxDB Query Example: select text from events where $timeFilter
    - +
    @@ -12,17 +12,17 @@
    Column mappings If your influxdb query returns more than one column you need to specify the column names below. An annotation event is composed of a title, tags, and an additional text field.
    - +
    - +
    - +
    From b9b3b75c0cf5b29c38b206929d1515b58727eb65 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Sun, 14 Feb 2016 22:13:59 -0800 Subject: [PATCH 19/19] Fixing snapshot templating bug --- public/app/features/dashboard/shareSnapshotCtrl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/dashboard/shareSnapshotCtrl.js b/public/app/features/dashboard/shareSnapshotCtrl.js index 44c2b3974a6..853e3db3b9c 100644 --- a/public/app/features/dashboard/shareSnapshotCtrl.js +++ b/public/app/features/dashboard/shareSnapshotCtrl.js @@ -117,7 +117,7 @@ function (angular, _) { // remove template queries _.each(dash.templating.list, function(variable) { variable.query = ""; - variable.options = []; + variable.options = variable.current; variable.refresh = false; });
    Name
      {{ds.name}} @@ -48,7 +48,7 @@ - +