From e58ccc5a831f7f4f51f18293101f8a7922c56323 Mon Sep 17 00:00:00 2001 From: bmundt Date: Thu, 7 Jul 2016 02:54:51 -0400 Subject: [PATCH 01/27] Hex units (#5530) * added hex units * deleted blank line, to fix style error * added unit tests * updated unit tests and changed the way it handles negative hex0x --- public/app/core/utils/kbn.js | 19 ++++++++++++ public/test/core/utils/kbn_specs.js | 46 +++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/public/app/core/utils/kbn.js b/public/app/core/utils/kbn.js index fbbab141614..29cd7c49890 100644 --- a/public/app/core/utils/kbn.js +++ b/public/app/core/utils/kbn.js @@ -368,6 +368,23 @@ function($, _, moment) { return kbn.toFixed(100*size, decimals) + '%'; }; + /* Formats the value to hex. Uses float if specified decimals are not 0. + * There are two options, one with 0x, and one without */ + + kbn.valueFormats.hex = function(value, decimals) { + if (value == null) { return ""; } + return parseFloat(kbn.toFixed(value, decimals)).toString(16).toUpperCase(); + }; + + kbn.valueFormats.hex0x = function(value, decimals) { + if (value == null) { return ""; } + var hexString = kbn.valueFormats.hex(value, decimals); + if (hexString.substring(0,1) === "-") { + return "-0x" + hexString.substring(1); + } + return "0x" + hexString; + }; + // Currencies kbn.valueFormats.currencyUSD = kbn.formatBuilders.currency('$'); kbn.valueFormats.currencyGBP = kbn.formatBuilders.currency('£'); @@ -617,6 +634,8 @@ function($, _, moment) { {text: 'Humidity (%H)', value: 'humidity' }, {text: 'ppm', value: 'ppm' }, {text: 'decibel', value: 'dB' }, + {text: 'hexadecimal (0x)', value: 'hex0x' }, + {text: 'hexadecimal', value: 'hex' }, ] }, { diff --git a/public/test/core/utils/kbn_specs.js b/public/test/core/utils/kbn_specs.js index 2c828235c55..e851e9b82f5 100644 --- a/public/test/core/utils/kbn_specs.js +++ b/public/test/core/utils/kbn_specs.js @@ -161,4 +161,50 @@ define([ expect(str).to.be('15ms'); }); }); + + describe('hex', function() { + it('positive integer', function() { + var str = kbn.valueFormats.hex(100, 0); + expect(str).to.be('64'); + }); + it('negative integer', function() { + var str = kbn.valueFormats.hex(-100, 0); + expect(str).to.be('-64'); + }); + it('null', function() { + var str = kbn.valueFormats.hex(null, 0); + expect(str).to.be(''); + }); + it('positive float', function() { + var str = kbn.valueFormats.hex(50.52, 1); + expect(str).to.be('32.8'); + }); + it('negative float', function() { + var str = kbn.valueFormats.hex(-50.333, 2); + expect(str).to.be('-32.547AE147AE14'); + }); + }); + + describe('hex 0x', function() { + it('positive integeter', function() { + var str = kbn.valueFormats.hex0x(7999,0); + expect(str).to.be('0x1F3F'); + }); + it('negative integer', function() { + var str = kbn.valueFormats.hex0x(-584,0); + expect(str).to.be('-0x248'); + }); + it('null', function() { + var str = kbn.valueFormats.hex0x(null, 0); + expect(str).to.be(''); + }); + it('positive float', function() { + var str = kbn.valueFormats.hex0x(74.443, 3); + expect(str).to.be('0x4A.716872B020C4'); + }); + it('negative float', function() { + var str = kbn.valueFormats.hex0x(-65.458, 1); + expect(str).to.be('-0x41.8'); + }); + }); }); From 4545b4d32385350a6ca485a1c8a52a1b8b198e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 7 Jul 2016 14:48:22 +0200 Subject: [PATCH 02/27] fix(login): minor fix redirect on login change --- public/app/core/components/sidemenu/sidemenu.html | 2 +- public/app/core/components/sidemenu/sidemenu.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/app/core/components/sidemenu/sidemenu.html b/public/app/core/components/sidemenu/sidemenu.html index 1ee2776bfe9..975e198b009 100644 --- a/public/app/core/components/sidemenu/sidemenu.html +++ b/public/app/core/components/sidemenu/sidemenu.html @@ -45,7 +45,7 @@
  • - + Sign in diff --git a/public/app/core/components/sidemenu/sidemenu.ts b/public/app/core/components/sidemenu/sidemenu.ts index a4b1f5d454a..84154621a0a 100644 --- a/public/app/core/components/sidemenu/sidemenu.ts +++ b/public/app/core/components/sidemenu/sidemenu.ts @@ -23,13 +23,13 @@ export class SideMenuCtrl { this.mainLinks = config.bootData.mainNavLinks; this.openUserDropdown(); - this.loginUrl = '/login?redirect=' + encodeURIComponent(this.$location.path()); + this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path()); this.$scope.$on('$routeChangeSuccess', () => { if (!this.contextSrv.pinned) { this.contextSrv.sidemenu = false; } - this.loginUrl = '/login?redirect=' + encodeURIComponent(this.$location.path()); + this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path()); }); } 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 03/27] 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 07/27] 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 08/27] 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 09/27] 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 010dbddf5933fdd2034e259ad8d49692203d2d9b Mon Sep 17 00:00:00 2001 From: Alexander Akulov Date: Fri, 8 Jul 2016 21:24:08 +0500 Subject: [PATCH 10/27] Fix double slashes in POST request to ES (#5536) --- public/app/plugins/datasource/elasticsearch/datasource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/elasticsearch/datasource.js b/public/app/plugins/datasource/elasticsearch/datasource.js index 2e3d36c2925..c952bf4d4f6 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.js +++ b/public/app/plugins/datasource/elasticsearch/datasource.js @@ -256,7 +256,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes esQuery = esQuery.replace(/\$timeTo/g, range.to.valueOf()); esQuery = header + '\n' + esQuery + '\n'; - return this._post('/_msearch?search_type=count', esQuery).then(function(res) { + return this._post('_msearch?search_type=count', esQuery).then(function(res) { var buckets = res.responses[0].aggregations["1"].buckets; return _.map(buckets, function(bucket) { return {text: bucket.key, value: bucket.key}; 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 11/27] 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 12/27] 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 13/27] 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 @@
  • - +
  • diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index 473ce4baa48..e731b2709cf 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -45,7 +45,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { }; /** @ngInject */ - constructor($scope, $injector, private annotationsSrv) { + constructor($scope, $injector, private annotationsSrv, private $sanitize) { super($scope, $injector); this.pageIndex = 0; @@ -159,7 +159,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { } function appendTableRows(tbodyElem) { - var renderer = new TableRenderer(panel, data, ctrl.dashboard.isTimezoneUtc()); + var renderer = new TableRenderer(panel, data, ctrl.dashboard.isTimezoneUtc(), ctrl.$sanitize); tbodyElem.empty(); tbodyElem.html(renderer.render(ctrl.pageIndex)); } diff --git a/public/app/plugins/panel/table/renderer.ts b/public/app/plugins/panel/table/renderer.ts index 63301a37309..9544e6aa068 100644 --- a/public/app/plugins/panel/table/renderer.ts +++ b/public/app/plugins/panel/table/renderer.ts @@ -8,7 +8,7 @@ export class TableRenderer { formaters: any[]; colorState: any; - constructor(private panel, private table, private isUtc) { + constructor(private panel, private table, private isUtc, private sanitize) { this.formaters = []; this.colorState = {}; } @@ -24,7 +24,7 @@ export class TableRenderer { return _.first(style.colors); } - defaultCellFormater(v) { + defaultCellFormater(v, style) { if (v === null || v === void 0 || v === undefined) { return ''; } @@ -33,7 +33,11 @@ export class TableRenderer { v = v.join(', '); } - return v; + if (style && style.sanitize) { + return this.sanitize(v); + } else { + return _.escape(v); + } } createColumnFormater(style, column) { @@ -61,7 +65,7 @@ export class TableRenderer { } if (_.isString(v)) { - return v; + return this.defaultCellFormater(v, style); } if (style.colorMode) { @@ -72,7 +76,9 @@ export class TableRenderer { }; } - return this.defaultCellFormater; + return (value) => { + return this.defaultCellFormater(value, style); + }; } formatColumnValue(colIndex, value) { @@ -96,7 +102,6 @@ export class TableRenderer { renderCell(columnIndex, value, addWidthHack = false) { value = this.formatColumnValue(columnIndex, value); - value = _.escape(value); var style = ''; if (this.colorState.cell) { style = ' style="background-color:' + this.colorState.cell + ';color: white"'; diff --git a/public/app/plugins/panel/table/specs/renderer_specs.ts b/public/app/plugins/panel/table/specs/renderer_specs.ts index edbe136b079..0cb129df015 100644 --- a/public/app/plugins/panel/table/specs/renderer_specs.ts +++ b/public/app/plugins/panel/table/specs/renderer_specs.ts @@ -13,6 +13,7 @@ describe('when rendering table', () => { {text: 'Undefined'}, {text: 'String'}, {text: 'United', unit: 'bps'}, + {text: 'Sanitized'}, ]; var panel = { @@ -47,11 +48,20 @@ describe('when rendering table', () => { type: 'number', unit: 'ms', decimals: 2, + }, + { + pattern: 'Sanitized', + type: 'string', + sanitize: true, } ] }; - var renderer = new TableRenderer(panel, table, 'utc'); + var sanitize = function(value) { + return 'sanitized'; + }; + + var renderer = new TableRenderer(panel, table, 'utc', sanitize); it('time column should be formated', () => { var html = renderer.renderCell(0, 1388556366666); @@ -107,6 +117,11 @@ describe('when rendering table', () => { var html = renderer.renderCell(3, undefined); expect(html).to.be(''); }); + + it('sanitized value should render as', () => { + var html = renderer.renderCell(6, 'text link'); + expect(html).to.be('sanitized'); + }); }); }); diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index 8824c827562..ad48b29f512 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -101,6 +101,7 @@ .search-button-row { padding-top: 20px; + line-height: 2.5rem; button, a { margin-right: 10px; } From e518f50248163e82251e78f97f260ea6df5f2352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Jul 2016 10:09:49 +0200 Subject: [PATCH 14/27] docs(): updated changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 368a8da5b34..917d9b7fda8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,10 @@ # 3.1.0 stable (unreleased) -### Bugfixes +### Bugfixes & Enhancements, * **User Alert Notices**: Backend error alert popups did not show properly, fixes [#5435](https://github.com/grafana/grafana/issues/5435) +* **Table**: Added sanitize HTML option to allow links in table cells, fixes [#4596](https://github.com/grafana/grafana/issues/4596) +* **Apps**: App dashboards are automatically synced to DB at startup after plugin update, fixes [#5529](https://github.com/grafana/grafana/issues/5529) # 3.1.0-beta1 (2016-06-23) From 9055beff1a89aa8743ab27dc4078359940080aee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Jul 2016 11:09:20 +0200 Subject: [PATCH 15/27] fix(table): fixed merge issue --- public/app/plugins/panel/table/module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index 402203204bd..86847a01aac 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -139,7 +139,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { } exportCsv() { - var renderer = new TableRenderer(this.panel, this.table, this.dashboard.isTimezoneUtc()); + var renderer = new TableRenderer(this.panel, this.table, this.dashboard.isTimezoneUtc(), this.$sanitize); FileExport.exportTableDataToCsv(renderer.render_values()); } From eb35f8cb8900a6ef0c25c497423fcf4c2b23c81c Mon Sep 17 00:00:00 2001 From: stuart nelson Date: Thu, 30 Jun 2016 11:29:02 +0200 Subject: [PATCH 16/27] [prometheus] Use `panelId` and `target.refId` for requestId Using these two values ties requests to a particular query position within a panel, ensuring that requests are canceled if: - Duplicate requests with the same query are sent - Requests from the same query position (but a different query) are sent The last point is important as it identifies queries by a physical location in the dashboard instead of with the query expression. If a different query from the same position in a panel is sent, the previous request should be canceled. --- public/app/plugins/datasource/prometheus/datasource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 4284ce78e8f..cbde594b6b1 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -77,7 +77,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS var query: any = {}; query.expr = templateSrv.replace(target.expr, options.scopedVars, self.interpolateQueryExpr); - query.requestId = target.expr; + query.requestId = options.panelId + target.refId; var interval = target.interval || options.interval; var intervalFactor = target.intervalFactor || 1; From eafb0f32481e280c656c1bc91ee55cc252bd5fb8 Mon Sep 17 00:00:00 2001 From: Bryan Irvine Date: Mon, 11 Jul 2016 06:23:39 -0700 Subject: [PATCH 17/27] get rid of weird line breaks and use action (#4926) * get rid of weird line breaks and use action When using restarts/stops/starts you'd get weird output sometimes, Strange line breaks 'OK' status overlapping the next lines etc... This fixes those moves OK to the right place and colorizes them correctly. * added : for more uniformity when doing a restart the output looked like: Stopping Grafana Server ... [ OK ] Starting Grafana Server: ... [ OK ] The Stopping line did not have a colon. I added it just to make it look better. --- packaging/rpm/init.d/grafana-server | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packaging/rpm/init.d/grafana-server b/packaging/rpm/init.d/grafana-server index c60f4fb6080..cb9bb73de7d 100755 --- a/packaging/rpm/init.d/grafana-server +++ b/packaging/rpm/init.d/grafana-server @@ -72,8 +72,6 @@ function isRunning() { case "$1" in start) - echo -n $"Starting $DESC: .... " - isRunning if [ $? -eq 0 ]; then echo "Already running." @@ -90,7 +88,7 @@ case "$1" in # Start Daemon cd $GRAFANA_HOME - su -s /bin/sh -c "nohup ${DAEMON} ${DAEMON_OPTS} >> /dev/null 3>&1 &" $GRAFANA_USER 2> /dev/null + action $"Starting $DESC: ..." su -s /bin/sh -c "nohup ${DAEMON} ${DAEMON_OPTS} >> /dev/null 3>&1 &" $GRAFANA_USER 2> /dev/null return=$? if [ $return -eq 0 ] then @@ -114,26 +112,25 @@ case "$1" in done fi - echo "OK" exit $return ;; stop) - echo -n "Stopping $DESC ..." + echo -n "Stopping $DESC: ..." if [ -f "$PID_FILE" ]; then killproc -p $PID_FILE -d 20 $NAME if [ $? -eq 1 ]; then - echo -n "$DESC is not running but pid file exists, cleaning up" + echo "$DESC is not running but pid file exists, cleaning up" elif [ $? -eq 3 ]; then PID="`cat $PID_FILE`" - echo -n "Failed to stop $DESC (pid $PID)" + echo "Failed to stop $DESC (pid $PID)" exit 1 fi rm -f "$PID_FILE" - echo "OK" + echo "" exit 0 else - echo -n "(not running)" + echo "(not running)" fi exit 0 ;; From 81af8f072cf8c2324bba06cd85f3774fec11aea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Jul 2016 19:07:16 +0200 Subject: [PATCH 18/27] feat(links): updated links to grafana.net --- public/app/core/components/search/search.html | 4 +- .../plugins/partials/plugin_list.html | 66 ++++++++++--------- public/img/grafana_net_logo.svg | 1 + public/sass/components/_search.scss | 17 ++++- public/sass/pages/_plugins.scss | 39 ++++------- 5 files changed, 68 insertions(+), 59 deletions(-) create mode 100644 public/img/grafana_net_logo.svg diff --git a/public/app/core/components/search/search.html b/public/app/core/components/search/search.html index b66eeffdc36..e8e2179ce5c 100644 --- a/public/app/core/components/search/search.html +++ b/public/app/core/components/search/search.html @@ -72,8 +72,8 @@ Import - - Explore ready made dashboards on Grafana.net + + Find dashboards on
    diff --git a/public/app/features/plugins/partials/plugin_list.html b/public/app/features/plugins/partials/plugin_list.html index f4ee2b57815..c276faae93a 100644 --- a/public/app/features/plugins/partials/plugin_list.html +++ b/public/app/features/plugins/partials/plugin_list.html @@ -5,9 +5,9 @@ -
    + + Find plugins on + + + + +
    -
      -
    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 19/27] 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 20/27] 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 @@ -