From b62f1f00cd9e345ce01b6e5c3a2971159bacd94a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 7 Jul 2016 18:11:03 +0200 Subject: [PATCH 01/16] feat(apps): auto update dashboard dashboards, #5529 --- pkg/models/plugin_settings.go | 18 +++- pkg/plugins/dashboards_updater.go | 88 +++++++++++++++++++ pkg/plugins/plugins.go | 2 + .../sqlstore/migrations/plugin_setting.go | 6 ++ pkg/services/sqlstore/plugin_setting.go | 25 +++++- .../plugins/import_list/import_list.html | 7 +- 6 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 pkg/plugins/dashboards_updater.go diff --git a/pkg/models/plugin_settings.go b/pkg/models/plugin_settings.go index d030c125ba9..dbeaf441156 100644 --- a/pkg/models/plugin_settings.go +++ b/pkg/models/plugin_settings.go @@ -20,6 +20,7 @@ type PluginSetting struct { Pinned bool JsonData map[string]interface{} SecureJsonData SecureJsonData + PluginVersion string Created time.Time Updated time.Time @@ -44,11 +45,19 @@ type UpdatePluginSettingCmd struct { Pinned bool `json:"pinned"` JsonData map[string]interface{} `json:"jsonData"` SecureJsonData map[string]string `json:"secureJsonData"` + PluginVersion string `json:"version"` PluginId string `json:"-"` OrgId int64 `json:"-"` } +// specific command, will only update version +type UpdatePluginSettingVersionCmd struct { + PluginVersion string + PluginId string `json:"-"` + OrgId int64 `json:"-"` +} + func (cmd *UpdatePluginSettingCmd) GetEncryptedJsonData() SecureJsonData { encrypted := make(SecureJsonData) for key, data := range cmd.SecureJsonData { @@ -65,10 +74,11 @@ type GetPluginSettingsQuery struct { } type PluginSettingInfoDTO struct { - OrgId int64 - PluginId string - Enabled bool - Pinned bool + OrgId int64 + PluginId string + Enabled bool + Pinned bool + PluginVersion string } type GetPluginSettingByIdQuery struct { diff --git a/pkg/plugins/dashboards_updater.go b/pkg/plugins/dashboards_updater.go new file mode 100644 index 00000000000..3a0714eb675 --- /dev/null +++ b/pkg/plugins/dashboards_updater.go @@ -0,0 +1,88 @@ +package plugins + +import ( + "time" + + "github.com/grafana/grafana/pkg/bus" + m "github.com/grafana/grafana/pkg/models" +) + +func updateAppDashboards() { + time.Sleep(time.Second * 1) + + plog.Debug("Looking for App Dashboard Updates") + + query := m.GetPluginSettingsQuery{OrgId: 0} + + if err := bus.Dispatch(&query); err != nil { + plog.Error("Failed to get all plugin settings", "error", err) + return + } + + for _, pluginSetting := range query.Result { + if appDef, exist := Apps[pluginSetting.PluginId]; exist { + if appDef.Info.Version != pluginSetting.PluginVersion { + handleAppPluginUpdated(appDef, pluginSetting.OrgId) + } + } + } +} + +func autoUpdateAppDashboard(pluginDashInfo *PluginDashboardInfoDTO, orgId int64) error { + if dash, err := loadPluginDashboard(pluginDashInfo.PluginId, pluginDashInfo.Path); err != nil { + return err + } else { + plog.Info("Auto updating App dashboard", "dashboard", dash.Title, "newRev", pluginDashInfo.Revision, "oldRev", pluginDashInfo.ImportedRevision) + updateCmd := ImportDashboardCommand{ + OrgId: orgId, + PluginId: pluginDashInfo.PluginId, + Overwrite: true, + Dashboard: dash.Data, + UserId: 0, + Path: pluginDashInfo.Path, + } + + if err := bus.Dispatch(&updateCmd); err != nil { + return err + } + } + return nil +} + +func handleAppPluginUpdated(appDef *AppPlugin, orgId int64) { + plog.Info("App update detected", "pluginId", appDef.Id) + + // Get plugin dashboards + if dashboards, err := GetPluginDashboards(orgId, appDef.Id); err != nil { + plog.Error("Failed to load app dashboards", "error", err) + return + } else { + // Update dashboards with updated revisions + for _, dash := range dashboards { + if dash.ImportedRevision != dash.Revision { + if err := autoUpdateAppDashboard(dash, orgId); err != nil { + plog.Error("Failed to auto update app dashboard", "pluginId", appDef.Id, "error", err) + return + } + } + } + } + + // update version in plugin_setting table to mark that we have processed the update + query := m.GetPluginSettingByIdQuery{PluginId: appDef.Id, OrgId: orgId} + if err := bus.Dispatch(&query); err != nil { + plog.Error("Failed to read plugin setting by id", "error", err) + return + } + + appSetting := query.Result + cmd := m.UpdatePluginSettingVersionCmd{ + OrgId: appSetting.OrgId, + PluginId: appSetting.PluginId, + PluginVersion: appDef.Info.Version, + } + + if err := bus.Dispatch(&cmd); err != nil { + plog.Error("Failed to update plugin setting version", "error", err) + } +} diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index cf931066cbb..b6c3639cbbf 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -77,6 +77,8 @@ func Init() error { } go StartPluginUpdateChecker() + go updateAppDashboards() + return nil } diff --git a/pkg/services/sqlstore/migrations/plugin_setting.go b/pkg/services/sqlstore/migrations/plugin_setting.go index 4a8729691d3..0700ab67d2f 100644 --- a/pkg/services/sqlstore/migrations/plugin_setting.go +++ b/pkg/services/sqlstore/migrations/plugin_setting.go @@ -26,4 +26,10 @@ func addAppSettingsMigration(mg *Migrator) { //------- indexes ------------------ addTableIndicesMigrations(mg, "v1", pluginSettingTable) + + // add column to store installed version + mg.AddMigration("Add column plugin_version to plugin_settings", NewAddColumnMigration(pluginSettingTable, &Column{ + Name: "plugin_version", Type: DB_NVarchar, Nullable: true, Length: 50, + })) + } diff --git a/pkg/services/sqlstore/plugin_setting.go b/pkg/services/sqlstore/plugin_setting.go index ec0b9b2e2d7..f3fcc2a9c1d 100644 --- a/pkg/services/sqlstore/plugin_setting.go +++ b/pkg/services/sqlstore/plugin_setting.go @@ -13,14 +13,20 @@ func init() { bus.AddHandler("sql", GetPluginSettings) bus.AddHandler("sql", GetPluginSettingById) bus.AddHandler("sql", UpdatePluginSetting) + bus.AddHandler("sql", UpdatePluginSettingVersion) } func GetPluginSettings(query *m.GetPluginSettingsQuery) error { - sql := `SELECT org_id, plugin_id, enabled, pinned - FROM plugin_setting - WHERE org_id=?` + sql := `SELECT org_id, plugin_id, enabled, pinned, plugin_version + FROM plugin_setting ` + params := make([]interface{}, 0) - sess := x.Sql(sql, query.OrgId) + if query.OrgId != 0 { + sql += "WHERE org_id=?" + params = append(params, query.OrgId) + } + + sess := x.Sql(sql, params...) query.Result = make([]*m.PluginSettingInfoDTO, 0) return sess.Find(&query.Result) } @@ -51,6 +57,7 @@ func UpdatePluginSetting(cmd *m.UpdatePluginSettingCmd) error { Enabled: cmd.Enabled, Pinned: cmd.Pinned, JsonData: cmd.JsonData, + PluginVersion: cmd.PluginVersion, SecureJsonData: cmd.GetEncryptedJsonData(), Created: time.Now(), Updated: time.Now(), @@ -65,8 +72,18 @@ func UpdatePluginSetting(cmd *m.UpdatePluginSettingCmd) error { pluginSetting.Enabled = cmd.Enabled pluginSetting.JsonData = cmd.JsonData pluginSetting.Pinned = cmd.Pinned + pluginSetting.PluginVersion = cmd.PluginVersion _, err = sess.Id(pluginSetting.Id).Update(&pluginSetting) return err } }) } + +func UpdatePluginSettingVersion(cmd *m.UpdatePluginSettingVersionCmd) error { + return inTransaction2(func(sess *session) error { + + _, err := sess.Exec("UPDATE plugin_setting SET plugin_version=? WHERE org_id=? AND plugin_id=?", cmd.PluginVersion, cmd.OrgId, cmd.PluginId) + return err + + }) +} diff --git a/public/app/features/plugins/import_list/import_list.html b/public/app/features/plugins/import_list/import_list.html index 746109970e0..b984a9d8be5 100644 --- a/public/app/features/plugins/import_list/import_list.html +++ b/public/app/features/plugins/import_list/import_list.html @@ -14,10 +14,9 @@ - v{{dash.revision}} - -  (Imported v{{dash.importedRevision}}) - + + Revision: {{dash.revision}} + - - diff --git a/public/app/features/plugins/plugin_edit_ctrl.ts b/public/app/features/plugins/plugin_edit_ctrl.ts index a10b5eb6e7a..c4f4953dd5a 100644 --- a/public/app/features/plugins/plugin_edit_ctrl.ts +++ b/public/app/features/plugins/plugin_edit_ctrl.ts @@ -97,28 +97,7 @@ export class PluginEditCtrl { } importDashboards() { - // move to dashboards tab - this.tabIndex = 2; - - return new Promise((resolve) => { - if (!this.$scope.$$phase) { - this.$scope.$digest(); - } - - // let angular load dashboards tab - setTimeout(() => { - resolve(); - }, 1000); - - }).then(() => { - return new Promise((resolve, reject) => { - // send event to import list component - appEvents.emit('dashboard-list-import-all', { - resolve: resolve, - reject: reject - }); - }); - }); + return Promise.resolve(); } setPreUpdateHook(callback: () => any) { From 68a8d9bc91ebc08b22174486ab3a76d6e0658e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 8 Jul 2016 13:41:46 +0200 Subject: [PATCH 05/16] feat(apps): more work on plugin dashboard sync --- pkg/api/dashboard.go | 22 +++++++------------ pkg/models/dashboards.go | 8 +++++++ pkg/services/sqlstore/dashboard.go | 5 +++++ .../app/features/dashboard/dashnav/dashnav.ts | 6 ++--- public/sass/components/_modals.scss | 2 +- 5 files changed, 25 insertions(+), 18 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 0a29a91347a..a6bf96da876 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -130,20 +130,6 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) Response { } } - if !cmd.Overwrite { - if autoUpdate, exists := dash.Data.CheckGet("autoUpdate"); exists { - message := "Dashboard marked as auto updated." - - if pluginId, err := autoUpdate.Get("pluginId").String(); err == nil { - if pluginDef, ok := plugins.Plugins[pluginId]; ok { - message = "Dashboard updated automatically when plugin " + pluginDef.Name + " is updated." - } - } - - return Json(412, util.DynMap{"status": "auto-update-dashboard", "message": message}) - } - } - err := bus.Dispatch(&cmd) if err != nil { if err == m.ErrDashboardWithSameNameExists { @@ -152,6 +138,14 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) Response { if err == m.ErrDashboardVersionMismatch { return Json(412, util.DynMap{"status": "version-mismatch", "message": err.Error()}) } + if pluginErr, ok := err.(m.UpdatePluginDashboardError); ok { + message := "Dashboard is belongs to plugin " + pluginErr.PluginId + "." + // look up plugin name + if pluginDef, exist := plugins.Plugins[pluginErr.PluginId]; exist { + message = "Dashboard is belongs to plugin " + pluginDef.Name + "." + } + return Json(412, util.DynMap{"status": "plugin-dashboard", "message": message}) + } if err == m.ErrDashboardNotFound { return Json(404, util.DynMap{"status": "not-found", "message": err.Error()}) } diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index 242b6d512d3..53ba11c0237 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -17,6 +17,14 @@ var ( ErrDashboardVersionMismatch = errors.New("The dashboard has been changed by someone else") ) +type UpdatePluginDashboardError struct { + PluginId string +} + +func (d UpdatePluginDashboardError) Error() string { + return "Dashboard belong to plugin" +} + var ( DashTypeJson = "file" DashTypeDB = "db" diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index 3712ce6579f..7a0971fcdfd 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -46,6 +46,11 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error { return m.ErrDashboardVersionMismatch } } + + // do not allow plugin dashboard updates without overwrite flag + if existing.PluginId != "" && cmd.Overwrite == false { + return m.UpdatePluginDashboardError{PluginId: existing.PluginId} + } } sameTitleExists, err := sess.Where("org_id=? AND slug=?", dash.OrgId, dash.Slug).Get(&sameTitle) diff --git a/public/app/features/dashboard/dashnav/dashnav.ts b/public/app/features/dashboard/dashnav/dashnav.ts index 25bb587edeb..601be4d7eea 100644 --- a/public/app/features/dashboard/dashnav/dashnav.ts +++ b/public/app/features/dashboard/dashnav/dashnav.ts @@ -135,13 +135,13 @@ export class DashNavCtrl { }); } - if (err.data && err.data.status === "auto-update-dashboard") { + if (err.data && err.data.status === "plugin-dashboard") { err.isHandled = true; $scope.appEvent('confirm-modal', { - title: 'Auto Update Dashboard', + title: 'Plugin Dashboard', text: err.data.message, - text2: 'Use Save As... to create copy or ignore this warning.', + text2: 'Your changes will be overwritten next time you update the plugin. Use Save As to create custom version.', yesText: "Save & Overwrite", icon: "fa-warning", onConfirm: function() { diff --git a/public/sass/components/_modals.scss b/public/sass/components/_modals.scss index 131e2b6364c..40cab02eac8 100644 --- a/public/sass/components/_modals.scss +++ b/public/sass/components/_modals.scss @@ -105,7 +105,7 @@ } .confirm-modal-text2 { - font-size: $font-size-h5; + font-size: $font-size-root; padding-top: $spacer; } From ebdf0564eba824cc0f2e5fe93afc35c07401fb86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 8 Jul 2016 17:44:57 +0200 Subject: [PATCH 06/16] feat(apps): plugin dashboard sync is starting to work --- pkg/plugins/dashboards.go | 14 +++++++++++++ pkg/plugins/dashboards_updater.go | 34 +++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/pkg/plugins/dashboards.go b/pkg/plugins/dashboards.go index c4b3915106d..524756fe24a 100644 --- a/pkg/plugins/dashboards.go +++ b/pkg/plugins/dashboards.go @@ -14,6 +14,7 @@ type PluginDashboardInfoDTO struct { Title string `json:"title"` Imported bool `json:"imported"` ImportedUri string `json:"importedUri"` + Slug string `json:"slug"` ImportedRevision int64 `json:"importedRevision"` Revision int64 `json:"revision"` Description string `json:"description"` @@ -36,6 +37,8 @@ func GetPluginDashboards(orgId int64, pluginId string) ([]*PluginDashboardInfoDT return nil, err } + existingMatches := make(map[int64]bool) + for _, include := range plugin.Includes { if include.Type != PluginTypeDashboard { continue @@ -60,12 +63,23 @@ func GetPluginDashboards(orgId int64, pluginId string) ([]*PluginDashboardInfoDT res.Imported = true res.ImportedUri = "db/" + existingDash.Slug res.ImportedRevision = existingDash.Data.Get("revision").MustInt64(1) + existingMatches[existingDash.Id] = true } } result = append(result, res) } + // find deleted dashboards + for _, dash := range query.Result { + if _, exists := existingMatches[dash.Id]; !exists { + result = append(result, &PluginDashboardInfoDTO{ + Slug: dash.Slug, + Removed: true, + }) + } + } + return result, nil } diff --git a/pkg/plugins/dashboards_updater.go b/pkg/plugins/dashboards_updater.go index 09f3e70a9a9..3036d66165b 100644 --- a/pkg/plugins/dashboards_updater.go +++ b/pkg/plugins/dashboards_updater.go @@ -62,17 +62,33 @@ func syncPluginDashboards(pluginDef *PluginBase, orgId int64) { plog.Info("Syncing plugin dashboards to DB", "pluginId", pluginDef.Id) // Get plugin dashboards - if dashboards, err := GetPluginDashboards(orgId, pluginDef.Id); err != nil { + dashboards, err := GetPluginDashboards(orgId, pluginDef.Id) + + if err != nil { plog.Error("Failed to load app dashboards", "error", err) return - } else { - // Update dashboards with updated revisions - for _, dash := range dashboards { - if dash.ImportedRevision != dash.Revision { - if err := autoUpdateAppDashboard(dash, orgId); err != nil { - plog.Error("Failed to auto update app dashboard", "pluginId", pluginDef.Id, "error", err) - return - } + } + + // Update dashboards with updated revisions + for _, dash := range dashboards { + // remove removed ones + if dash.Removed { + plog.Info("Deleting plugin dashboard", "pluginId", pluginDef.Id, "dashboard", dash.Slug) + + deleteCmd := m.DeleteDashboardCommand{OrgId: orgId, Slug: dash.Slug} + if err := bus.Dispatch(&deleteCmd); err != nil { + plog.Error("Failed to auto update app dashboard", "pluginId", pluginDef.Id, "error", err) + return + } + + continue + } + + // update updated ones + if dash.ImportedRevision != dash.Revision { + if err := autoUpdateAppDashboard(dash, orgId); err != nil { + plog.Error("Failed to auto update app dashboard", "pluginId", pluginDef.Id, "error", err) + return } } } From 615b69244291e7221853b588e7d32f4e2e06e2f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 8 Jul 2016 18:21:25 +0200 Subject: [PATCH 07/16] feat(apps): fixed unit tests --- pkg/plugins/dashboards.go | 30 ------------------- pkg/plugins/dashboards_test.go | 25 ++++++++++++---- tests/test-app/dashboards/connections.json | 2 +- .../dashboards/connections_result.json | 2 +- tests/test-app/dashboards/memory.json | 2 +- 5 files changed, 23 insertions(+), 38 deletions(-) diff --git a/pkg/plugins/dashboards.go b/pkg/plugins/dashboards.go index 524756fe24a..5d26766b8e5 100644 --- a/pkg/plugins/dashboards.go +++ b/pkg/plugins/dashboards.go @@ -105,33 +105,3 @@ func loadPluginDashboard(pluginId, path string) (*m.Dashboard, error) { return m.NewDashboardFromJson(data), nil } - -func getDashboardImportStatus(orgId int64, plugin *PluginBase, path string) (*PluginDashboardInfoDTO, error) { - res := &PluginDashboardInfoDTO{} - - var dashboard *m.Dashboard - var err error - - if dashboard, err = loadPluginDashboard(plugin.Id, path); err != nil { - return nil, err - } - - res.Path = path - res.PluginId = plugin.Id - res.Title = dashboard.Title - res.Revision = dashboard.Data.Get("revision").MustInt64(1) - - query := m.GetDashboardQuery{OrgId: orgId, Slug: dashboard.Slug} - - if err := bus.Dispatch(&query); err != nil { - if err != m.ErrDashboardNotFound { - return nil, err - } - } else { - res.Imported = true - res.ImportedUri = "db/" + query.Result.Slug - res.ImportedRevision = query.Result.Data.Get("revision").MustInt64(1) - } - - return res, nil -} diff --git a/pkg/plugins/dashboards_test.go b/pkg/plugins/dashboards_test.go index 98693349b4c..d8f58cb217e 100644 --- a/pkg/plugins/dashboards_test.go +++ b/pkg/plugins/dashboards_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/components/simplejson" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" . "github.com/smartystreets/goconvey/convey" @@ -31,6 +32,20 @@ func TestPluginDashboards(t *testing.T) { return m.ErrDashboardNotFound }) + bus.AddHandler("test", func(query *m.GetDashboardsByPluginIdQuery) error { + var data = simplejson.New() + data.Set("title", "Nginx Connections") + data.Set("revision", 22) + + query.Result = []*m.Dashboard{ + &m.Dashboard{ + Slug: "nginx-connections", + Data: data, + }, + } + return nil + }) + dashboards, err := GetPluginDashboards(1, "test-app") So(err, ShouldBeNil) @@ -41,12 +56,12 @@ func TestPluginDashboards(t *testing.T) { Convey("should include installed version info", func() { So(dashboards[0].Title, ShouldEqual, "Nginx Connections") - //So(dashboards[0].Revision, ShouldEqual, "1.5") - //So(dashboards[0].InstalledRevision, ShouldEqual, "1.1") - //So(dashboards[0].InstalledUri, ShouldEqual, "db/nginx-connections") + So(dashboards[0].Revision, ShouldEqual, 25) + So(dashboards[0].ImportedRevision, ShouldEqual, 22) + So(dashboards[0].ImportedUri, ShouldEqual, "db/nginx-connections") - //So(dashboards[1].Revision, ShouldEqual, "2.0") - //So(dashboards[1].InstalledRevision, ShouldEqual, "") + So(dashboards[1].Revision, ShouldEqual, 2) + So(dashboards[1].ImportedRevision, ShouldEqual, 0) }) }) diff --git a/tests/test-app/dashboards/connections.json b/tests/test-app/dashboards/connections.json index ee5e40140b3..cc189d2d113 100644 --- a/tests/test-app/dashboards/connections.json +++ b/tests/test-app/dashboards/connections.json @@ -8,7 +8,7 @@ ], "title": "Nginx Connections", - "revision": "1.5", + "revision": 25, "schemaVersion": 11, "tags": ["tag1", "tag2"], "number_array": [1,2,3,10.33], diff --git a/tests/test-app/dashboards/connections_result.json b/tests/test-app/dashboards/connections_result.json index 1c662f6c269..4bf0570ac1e 100644 --- a/tests/test-app/dashboards/connections_result.json +++ b/tests/test-app/dashboards/connections_result.json @@ -1,5 +1,5 @@ { - "revision": "1.5", + "revision": 25, "tags": ["tag1", "tag2"], "boolean_false": false, "boolean_true": true, diff --git a/tests/test-app/dashboards/memory.json b/tests/test-app/dashboards/memory.json index b79cb4d7dba..983b994b40e 100644 --- a/tests/test-app/dashboards/memory.json +++ b/tests/test-app/dashboards/memory.json @@ -1,5 +1,5 @@ { "title": "Nginx Memory", - "revision": "2.0", + "revision": 2, "schemaVersion": 11 } From 4133526d8dfd52a88fa86b08c8beeb85970f4dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 8 Jul 2016 21:04:58 +0200 Subject: [PATCH 08/16] feat(build): updated sass lint --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b9adc3d24b4..b21a589c740 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "phantomjs-prebuilt": "^2.1.7", "reflect-metadata": "0.1.2", "rxjs": "5.0.0-beta.4", - "sass-lint": "^1.6.0", + "sass-lint": "^1.8.2", "systemjs": "0.19.24" }, "engines": { @@ -69,7 +69,7 @@ "dependencies": { "eventemitter3": "^1.2.0", "grunt-jscs": "~1.5.x", - "grunt-sass-lint": "^0.1.0", + "grunt-sass-lint": "^0.2.0", "grunt-sync": "^0.4.1", "karma-sinon": "^1.0.3", "lodash": "^2.4.1", From 4c4cb20bdebdbdedc1603932d7bdef5c0ebafa82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 8 Jul 2016 22:12:10 +0200 Subject: [PATCH 09/16] tech(sass): changed sass lint version to 1.7.0 --- package.json | 2 +- pkg/plugins/dashboards_test.go | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index b21a589c740..16f4d854a5c 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "phantomjs-prebuilt": "^2.1.7", "reflect-metadata": "0.1.2", "rxjs": "5.0.0-beta.4", - "sass-lint": "^1.8.2", + "sass-lint": "^1.7.0", "systemjs": "0.19.24" }, "engines": { diff --git a/pkg/plugins/dashboards_test.go b/pkg/plugins/dashboards_test.go index d8f58cb217e..980d7bb91bd 100644 --- a/pkg/plugins/dashboards_test.go +++ b/pkg/plugins/dashboards_test.go @@ -38,10 +38,7 @@ func TestPluginDashboards(t *testing.T) { data.Set("revision", 22) query.Result = []*m.Dashboard{ - &m.Dashboard{ - Slug: "nginx-connections", - Data: data, - }, + {Slug: "nginx-connections", Data: data}, } return nil }) From 65683ab2419d4c8204a47b2d9595c7b41663ec90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Jul 2016 09:38:06 +0200 Subject: [PATCH 10/16] feat(table): added sanitize html option to column styles for table panel, fixes #4596 --- pkg/plugins/dashboards_updater.go | 2 +- public/app/core/components/search/search.html | 4 ++++ .../features/plugins/partials/plugin_list.html | 4 ++++ public/app/plugins/panel/table/editor.html | 7 ++++++- public/app/plugins/panel/table/module.ts | 4 ++-- public/app/plugins/panel/table/renderer.ts | 17 +++++++++++------ .../plugins/panel/table/specs/renderer_specs.ts | 17 ++++++++++++++++- public/sass/components/_search.scss | 1 + 8 files changed, 45 insertions(+), 11 deletions(-) diff --git a/pkg/plugins/dashboards_updater.go b/pkg/plugins/dashboards_updater.go index 3036d66165b..52a623e73dd 100644 --- a/pkg/plugins/dashboards_updater.go +++ b/pkg/plugins/dashboards_updater.go @@ -12,7 +12,7 @@ func init() { } func updateAppDashboards() { - time.Sleep(time.Second * 1) + time.Sleep(time.Second * 5) plog.Debug("Looking for App Dashboard Updates") diff --git a/public/app/core/components/search/search.html b/public/app/core/components/search/search.html index 6344a26c886..b66eeffdc36 100644 --- a/public/app/core/components/search/search.html +++ b/public/app/core/components/search/search.html @@ -72,6 +72,10 @@ Import + + Explore ready made dashboards on Grafana.net + +
diff --git a/public/app/features/plugins/partials/plugin_list.html b/public/app/features/plugins/partials/plugin_list.html index 64e511805dc..f4ee2b57815 100644 --- a/public/app/features/plugins/partials/plugin_list.html +++ b/public/app/features/plugins/partials/plugin_list.html @@ -5,6 +5,10 @@ + +
-
    -
  1. - -
    -
    - - {{plugin.type}} -
    -
    - Update available! -
    -
    -
    -
    - -
    -
    -
    {{plugin.name}}
    -
    By {{plugin.info.author.name}}
    -
    -
    -
    -
  2. -
-
+
    +
  1. + +
    +
    + + {{plugin.type}} +
    +
    + Update available! +
    +
    +
    +
    + +
    +
    +
    {{plugin.name}}
    +
    By {{plugin.info.author.name}}
    +
    +
    +
    +
  2. +
+ diff --git a/public/img/grafana_net_logo.svg b/public/img/grafana_net_logo.svg new file mode 100644 index 00000000000..d73b9b944a8 --- /dev/null +++ b/public/img/grafana_net_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index ad48b29f512..563f89919e5 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -101,9 +101,24 @@ .search-button-row { padding-top: 20px; - line-height: 2.5rem; + button, a { margin-right: 10px; } + + .search-button-row-explore-link { + color: $text-muted; + font-size: $font-size-sm; + padding-right: 7rem; + background: url(../img/grafana_net_logo.svg); + background-size: 6.5rem 3rem; + background-repeat: no-repeat; + background-position: right; + position: relative; + top: 0.8rem; + &:hover { + color: $link-hover-color; + } + } } diff --git a/public/sass/pages/_plugins.scss b/public/sass/pages/_plugins.scss index 970d048cce3..f473a52ec94 100644 --- a/public/sass/pages/_plugins.scss +++ b/public/sass/pages/_plugins.scss @@ -63,28 +63,17 @@ } } -// .app-edit-logo-box { -// padding: 1.2rem; -// background: $panel-bg; -// text-align: center; -// img { -// max-width: 7rem; -// } -// margin-right: 2rem; -// } -// -// .app-edit-links { -// list-style: none; -// margin: 0 0 0 2rem; -// -// li { -// background: $panel-bg; -// margin-top: 4px; -// padding: 0.2rem 1rem; -// } -// } -// -// .app-edit-description { -// font-style: italic; -// margin-bottom: 1.5rem; -// } +.get-more-plugins-link { + color: $text-muted; + font-size: $font-size-sm; + padding-right: 7rem; + background: url(../img/grafana_net_logo.svg); + background-size: 6.5rem 3rem; + background-repeat: no-repeat; + background-position: right; + position: relative; + top: 1.2rem; + &:hover { + color: $link-hover-color; + } +} From f3db2fa262f818d43878ac623d31b2219aa09de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Jul 2016 19:16:35 +0200 Subject: [PATCH 13/16] feat(plugins): hide link on small screens --- public/sass/pages/_plugins.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/public/sass/pages/_plugins.scss b/public/sass/pages/_plugins.scss index f473a52ec94..ed4d39d316f 100644 --- a/public/sass/pages/_plugins.scss +++ b/public/sass/pages/_plugins.scss @@ -77,3 +77,9 @@ color: $link-hover-color; } } + +@include media-breakpoint-down(sm) { + .get-more-plugins-link { + display: none; + } +} From b2acac3a41fbef7bd6bb918849cbfc2ddf5dbba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 12 Jul 2016 08:41:56 +0200 Subject: [PATCH 14/16] feat(apps): more polish on app dashboard save warning, #5529 --- pkg/api/dashboard.go | 4 ++-- public/app/core/services/alert_srv.ts | 2 ++ public/app/features/dashboard/dashnav/dashnav.ts | 8 ++++++-- public/app/partials/confirm_modal.html | 3 ++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index a6bf96da876..041f2a7f8cf 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -139,10 +139,10 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) Response { return Json(412, util.DynMap{"status": "version-mismatch", "message": err.Error()}) } if pluginErr, ok := err.(m.UpdatePluginDashboardError); ok { - message := "Dashboard is belongs to plugin " + pluginErr.PluginId + "." + message := "The dashboard belongs to plugin " + pluginErr.PluginId + "." // look up plugin name if pluginDef, exist := plugins.Plugins[pluginErr.PluginId]; exist { - message = "Dashboard is belongs to plugin " + pluginDef.Name + "." + message = "The dashboard belongs to plugin " + pluginDef.Name + "." } return Json(412, util.DynMap{"status": "plugin-dashboard", "message": message}) } diff --git a/public/app/core/services/alert_srv.ts b/public/app/core/services/alert_srv.ts index 3003e59c74b..edfff2e8d00 100644 --- a/public/app/core/services/alert_srv.ts +++ b/public/app/core/services/alert_srv.ts @@ -73,6 +73,8 @@ export class AlertSrv { scope.text = payload.text; scope.text2 = payload.text2; scope.onConfirm = payload.onConfirm; + scope.onAltAction = payload.onAltAction; + scope.altActionText = payload.altActionText; scope.icon = payload.icon || "fa-check"; scope.yesText = payload.yesText || "Yes"; scope.noText = payload.noText || "Cancel"; diff --git a/public/app/features/dashboard/dashnav/dashnav.ts b/public/app/features/dashboard/dashnav/dashnav.ts index 601be4d7eea..7d25e352e9e 100644 --- a/public/app/features/dashboard/dashnav/dashnav.ts +++ b/public/app/features/dashboard/dashnav/dashnav.ts @@ -141,9 +141,13 @@ export class DashNavCtrl { $scope.appEvent('confirm-modal', { title: 'Plugin Dashboard', text: err.data.message, - text2: 'Your changes will be overwritten next time you update the plugin. Use Save As to create custom version.', - yesText: "Save & Overwrite", + text2: 'Your changes will be lost when you update the plugin. Use Save As to create custom version.', + yesText: "Overwrite", icon: "fa-warning", + altActionText: "Save As", + onAltAction: function() { + $scope.saveDashboardAs(); + }, onConfirm: function() { $scope.saveDashboard({overwrite: true}); } diff --git a/public/app/partials/confirm_modal.html b/public/app/partials/confirm_modal.html index d9da8bdacc4..6ab4a6b32c5 100644 --- a/public/app/partials/confirm_modal.html +++ b/public/app/partials/confirm_modal.html @@ -1,4 +1,4 @@ -