diff --git a/.editorconfig b/.editorconfig index 146224e7330..84bbaf8a420 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,6 +7,8 @@ indent_size = 2 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true +max_line_length = 120 +insert_final_newline = true [*.go] indent_style = tab diff --git a/docs/sources/features/panels/singlestat.md b/docs/sources/features/panels/singlestat.md index 5e2cb36600b..510642337ff 100644 --- a/docs/sources/features/panels/singlestat.md +++ b/docs/sources/features/panels/singlestat.md @@ -47,7 +47,7 @@ The coloring options of the Singlestat Panel config allow you to dynamically cha 2. **Thresholds**: Change the background and value colors dynamically within the panel, depending on the Singlestat value. The threshold field accepts **2 comma-separated** values which represent 3 ranges that correspond to the three colors directly to the right. For example: if the thresholds are 70, 90 then the first color represents < 70, the second color represents between 70 and 90 and the third color represents > 90. 3. **Colors**: Select a color and opacity 4. **Value**: This checkbox applies the configured thresholds and colors to the summary stat. -5. **Invert order**: This link toggles the threshold color order.
For example: Green, Orange, Red () will become Red, Orange, Green (). +5. **Invert order**: This link toggles the threshold color order.
For example: Green, Orange, Red () will become Red, Orange, Green (). ### Spark Lines diff --git a/docs/sources/http_api/user.md b/docs/sources/http_api/user.md index ba8afd4db22..134c1842851 100644 --- a/docs/sources/http_api/user.md +++ b/docs/sources/http_api/user.md @@ -156,7 +156,7 @@ HTTP/1.1 200 Content-Type: application/json { - "email": "user@mygraf.com" + "email": "user@mygraf.com", "name": "admin", "login": "admin", "theme": "light", @@ -409,4 +409,4 @@ HTTP/1.1 200 Content-Type: application/json {"message":"Dashboard unstarred"} -``` \ No newline at end of file +``` diff --git a/package.json b/package.json index 32846b2d493..613e5cfb06c 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,11 @@ "git add" ] }, + "prettier": { + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 120 + }, "license": "Apache-2.0", "dependencies": { "angular": "^1.6.6", diff --git a/pkg/api/api.go b/pkg/api/api.go index 3f6d8d4d954..ea082ff4741 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -40,8 +40,11 @@ func (hs *HttpServer) registerRoutes() { r.Get("/datasources/", reqSignedIn, Index) r.Get("/datasources/new", reqSignedIn, Index) r.Get("/datasources/edit/*", reqSignedIn, Index) + r.Get("/org/users", reqSignedIn, Index) r.Get("/org/users/new", reqSignedIn, Index) r.Get("/org/users/invite", reqSignedIn, Index) + r.Get("/org/teams", reqSignedIn, Index) + r.Get("/org/teams/*", reqSignedIn, Index) r.Get("/org/apikeys/", reqSignedIn, Index) r.Get("/dashboard/import/", reqSignedIn, Index) r.Get("/configuration", reqGrafanaAdmin, Index) diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index 2e9aa78d7d5..a702b06fad5 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -3,6 +3,7 @@ package dtos import ( "crypto/md5" "fmt" + "regexp" "strings" "github.com/grafana/grafana/pkg/components/simplejson" @@ -57,3 +58,19 @@ func GetGravatarUrl(text string) string { hasher.Write([]byte(strings.ToLower(text))) return fmt.Sprintf(setting.AppSubUrl+"/avatar/%x", hasher.Sum(nil)) } + +func GetGravatarUrlWithDefault(text string, defaultText string) string { + if text != "" { + return GetGravatarUrl(text) + } + + reg, err := regexp.Compile("[^a-zA-Z0-9]+") + + if err != nil { + return "" + } + + text = reg.ReplaceAllString(defaultText, "") + "@localhost" + + return GetGravatarUrl(text) +} diff --git a/pkg/api/team.go b/pkg/api/team.go index 31e465d3232..af537224d41 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -1,6 +1,7 @@ package api import ( + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" @@ -70,6 +71,10 @@ func SearchTeams(c *middleware.Context) Response { return ApiError(500, "Failed to search Teams", err) } + for _, team := range query.Result.Teams { + team.AvatarUrl = dtos.GetGravatarUrlWithDefault(team.Email, team.Name) + } + query.Result.Page = page query.Result.PerPage = perPage diff --git a/pkg/api/team_members.go b/pkg/api/team_members.go index 0999c9573a5..412e142edb7 100644 --- a/pkg/api/team_members.go +++ b/pkg/api/team_members.go @@ -1,6 +1,7 @@ package api import ( + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" @@ -15,6 +16,10 @@ func GetTeamMembers(c *middleware.Context) Response { return ApiError(500, "Failed to get Team Members", err) } + for _, member := range query.Result { + member.AvatarUrl = dtos.GetGravatarUrl(member.Email) + } + return Json(200, query.Result) } diff --git a/pkg/models/team.go b/pkg/models/team.go index b9759f059cf..d2912f431b8 100644 --- a/pkg/models/team.go +++ b/pkg/models/team.go @@ -16,6 +16,7 @@ type Team struct { Id int64 `json:"id"` OrgId int64 `json:"orgId"` Name string `json:"name"` + Email string `json:"email"` Created time.Time `json:"created"` Updated time.Time `json:"updated"` @@ -26,14 +27,16 @@ type Team struct { type CreateTeamCommand struct { Name string `json:"name" binding:"Required"` + Email string `json:"email"` OrgId int64 `json:"-"` Result Team `json:"-"` } type UpdateTeamCommand struct { - Id int64 - Name string + Id int64 + Name string + Email string } type DeleteTeamCommand struct { @@ -64,6 +67,8 @@ type SearchTeamDto struct { Id int64 `json:"id"` OrgId int64 `json:"orgId"` Name string `json:"name"` + Email string `json:"email"` + AvatarUrl string `json:"avatarUrl"` MemberCount int64 `json:"memberCount"` } diff --git a/pkg/models/team_member.go b/pkg/models/team_member.go index 71e5cd4ba12..9970678a1ae 100644 --- a/pkg/models/team_member.go +++ b/pkg/models/team_member.go @@ -47,9 +47,10 @@ type GetTeamMembersQuery struct { // Projections and DTOs type TeamMemberDTO struct { - OrgId int64 `json:"orgId"` - TeamId int64 `json:"teamId"` - UserId int64 `json:"userId"` - Email string `json:"email"` - Login string `json:"login"` + OrgId int64 `json:"orgId"` + TeamId int64 `json:"teamId"` + UserId int64 `json:"userId"` + Email string `json:"email"` + Login string `json:"login"` + AvatarUrl string `json:"avatarUrl"` } diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index 31a42a7b3b3..0b6b60a5e11 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -345,8 +345,9 @@ func GetDashboards(query *m.GetDashboardsQuery) error { func GetDashboardsByPluginId(query *m.GetDashboardsByPluginIdQuery) error { var dashboards = make([]*m.Dashboard, 0) + whereExpr := "org_id=? AND plugin_id=? AND is_folder=" + dialect.BooleanStr(false) - err := x.Where("org_id=? AND plugin_id=? AND is_folder=0", query.OrgId, query.PluginId).Find(&dashboards) + err := x.Where(whereExpr, query.OrgId, query.PluginId).Find(&dashboards) query.Result = dashboards if err != nil { diff --git a/pkg/services/sqlstore/dashboard_acl.go b/pkg/services/sqlstore/dashboard_acl.go index 3ab0361d175..3b0c89e02ef 100644 --- a/pkg/services/sqlstore/dashboard_acl.go +++ b/pkg/services/sqlstore/dashboard_acl.go @@ -170,7 +170,12 @@ func GetDashboardAclInfoList(query *m.GetDashboardAclInfoListQuery) error { FROM dashboard_acl as da, dashboard as dash LEFT JOIN dashboard folder on dash.folder_id = folder.id - WHERE dash.id = ? AND (dash.has_acl = 0 or folder.has_acl = 0) AND da.dashboard_id = -1 + WHERE + dash.id = ? AND ( + dash.has_acl = ` + dialect.BooleanStr(false) + ` or + folder.has_acl = ` + dialect.BooleanStr(false) + ` + ) AND + da.dashboard_id = -1 ` query.Result = make([]*m.DashboardAclInfoDTO, 0) diff --git a/pkg/services/sqlstore/migrations/team_mig.go b/pkg/services/sqlstore/migrations/team_mig.go index 374972e5449..eb0641fbc32 100644 --- a/pkg/services/sqlstore/migrations/team_mig.go +++ b/pkg/services/sqlstore/migrations/team_mig.go @@ -45,4 +45,9 @@ func addTeamMigrations(mg *Migrator) { //------- indexes ------------------ mg.AddMigration("add index team_member.org_id", NewAddIndexMigration(teamMemberV1, teamMemberV1.Indices[0])) mg.AddMigration("add unique index team_member_org_id_team_id_user_id", NewAddIndexMigration(teamMemberV1, teamMemberV1.Indices[1])) + + // add column email + mg.AddMigration("Add column email to team table", NewAddColumnMigration(teamV1, &Column{ + Name: "email", Type: DB_NVarchar, Nullable: true, Length: 190, + })) } diff --git a/pkg/services/sqlstore/search_builder.go b/pkg/services/sqlstore/search_builder.go index 6a5e8e60b54..ddf192da5ff 100644 --- a/pkg/services/sqlstore/search_builder.go +++ b/pkg/services/sqlstore/search_builder.go @@ -175,14 +175,14 @@ func (sb *SearchBuilder) buildSearchWhereClause() { } if sb.signedInUser.OrgRole != m.ROLE_ADMIN { - allowedDashboardsSubQuery := ` AND (dashboard.has_acl = 0 OR dashboard.id in ( + allowedDashboardsSubQuery := ` AND (dashboard.has_acl = ` + dialect.BooleanStr(false) + ` OR dashboard.id in ( SELECT distinct d.id AS DashboardId FROM dashboard AS d LEFT JOIN dashboard_acl as da on d.folder_id = da.dashboard_id or d.id = da.dashboard_id LEFT JOIN team_member as ugm on ugm.team_id = da.team_id LEFT JOIN org_user ou on ou.role = da.role WHERE - d.has_acl = 1 and + d.has_acl = ` + dialect.BooleanStr(true) + ` and (da.user_id = ? or ugm.user_id = ? or ou.id is not null) and d.org_id = ? ) @@ -198,11 +198,11 @@ func (sb *SearchBuilder) buildSearchWhereClause() { } if sb.whereTypeFolder { - sb.sql.WriteString(" AND dashboard.is_folder = 1") + sb.sql.WriteString(" AND dashboard.is_folder = " + dialect.BooleanStr(true)) } if sb.whereTypeDash { - sb.sql.WriteString(" AND dashboard.is_folder = 0") + sb.sql.WriteString(" AND dashboard.is_folder = " + dialect.BooleanStr(false)) } if len(sb.whereFolderIds) > 0 { diff --git a/pkg/services/sqlstore/stats.go b/pkg/services/sqlstore/stats.go index aa01c8a3761..7f631859a1d 100644 --- a/pkg/services/sqlstore/stats.go +++ b/pkg/services/sqlstore/stats.go @@ -13,7 +13,7 @@ func init() { bus.AddHandler("sql", GetAdminStats) } -var activeUserTimeLimit time.Duration = time.Hour * 24 * 14 +var activeUserTimeLimit time.Duration = time.Hour * 24 * 30 func GetDataSourceStats(query *m.GetDataSourceStatsQuery) error { var rawSql = `SELECT COUNT(*) as count, type FROM data_source GROUP BY type` diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index 3e9a6e6ec56..98bb1a36eb9 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -33,6 +33,7 @@ func CreateTeam(cmd *m.CreateTeamCommand) error { team := m.Team{ Name: cmd.Name, + Email: cmd.Email, OrgId: cmd.OrgId, Created: time.Now(), Updated: time.Now(), @@ -57,9 +58,12 @@ func UpdateTeam(cmd *m.UpdateTeamCommand) error { team := m.Team{ Name: cmd.Name, + Email: cmd.Email, Updated: time.Now(), } + sess.MustCols("email") + affectedRows, err := sess.Id(cmd.Id).Update(&team) if err != nil { @@ -125,6 +129,7 @@ func SearchTeams(query *m.SearchTeamsQuery) error { sql.WriteString(`select team.id as id, team.name as name, + team.email as email, (select count(*) from team_member where team_member.team_id = team.id) as member_count from team as team where team.org_id = ?`) diff --git a/pkg/services/sqlstore/team_test.go b/pkg/services/sqlstore/team_test.go index 4a099db14ff..dbae4545266 100644 --- a/pkg/services/sqlstore/team_test.go +++ b/pkg/services/sqlstore/team_test.go @@ -27,8 +27,8 @@ func TestTeamCommandsAndQueries(t *testing.T) { userIds = append(userIds, userCmd.Result.Id) } - group1 := m.CreateTeamCommand{Name: "group1 name"} - group2 := m.CreateTeamCommand{Name: "group2 name"} + group1 := m.CreateTeamCommand{Name: "group1 name", Email: "test1@test.com"} + group2 := m.CreateTeamCommand{Name: "group2 name", Email: "test2@test.com"} err := CreateTeam(&group1) So(err, ShouldBeNil) @@ -43,6 +43,7 @@ func TestTeamCommandsAndQueries(t *testing.T) { team1 := query.Result.Teams[0] So(team1.Name, ShouldEqual, "group1 name") + So(team1.Email, ShouldEqual, "test1@test.com") err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: 1, TeamId: team1.Id, UserId: userIds[0]}) So(err, ShouldBeNil) @@ -76,6 +77,7 @@ func TestTeamCommandsAndQueries(t *testing.T) { So(err, ShouldBeNil) So(len(query.Result), ShouldEqual, 1) So(query.Result[0].Name, ShouldEqual, "group2 name") + So(query.Result[0].Email, ShouldEqual, "test2@test.com") }) Convey("Should be able to remove users from a group", func() { diff --git a/public/app/app.ts b/public/app/app.ts index 0fd00c8e1bb..6caa178bc83 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -1,25 +1,24 @@ -import "babel-polyfill"; -import "file-saver"; -import "lodash"; -import "jquery"; -import "angular"; -import "angular-route"; -import "angular-sanitize"; -import "angular-native-dragdrop"; -import "angular-bindonce"; -import "react"; -import "react-dom"; +import 'babel-polyfill'; +import 'file-saver'; +import 'lodash'; +import 'jquery'; +import 'angular'; +import 'angular-route'; +import 'angular-sanitize'; +import 'angular-native-dragdrop'; +import 'angular-bindonce'; +import 'react'; +import 'react-dom'; -import "vendor/bootstrap/bootstrap"; -import "vendor/angular-ui/ui-bootstrap-tpls"; -import "vendor/angular-other/angular-strap"; +import 'vendor/bootstrap/bootstrap'; +import 'vendor/angular-ui/ui-bootstrap-tpls'; +import 'vendor/angular-other/angular-strap'; -import $ from "jquery"; -import angular from "angular"; -import config from "app/core/config"; -import _ from "lodash"; -import moment from "moment"; -import { createStore } from "app/stores/store"; +import $ from 'jquery'; +import angular from 'angular'; +import config from 'app/core/config'; +import _ from 'lodash'; +import moment from 'moment'; // add move to lodash for backward compatabiltiy _.move = function(array, fromIndex, toIndex) { @@ -27,7 +26,8 @@ _.move = function(array, fromIndex, toIndex) { return array; }; -import { coreModule, registerAngularDirectives } from "./core/core"; +import { coreModule, registerAngularDirectives } from './core/core'; +import { setupAngularRoutes } from './routes/routes'; export class GrafanaApp { registerFunctions: any; @@ -51,78 +51,62 @@ export class GrafanaApp { } init() { - var app = angular.module("grafana", []); + var app = angular.module('grafana', []); moment.locale(config.bootData.user.locale); - app.config( - ( - $locationProvider, - $controllerProvider, - $compileProvider, - $filterProvider, - $httpProvider, - $provide - ) => { - // pre assing bindings before constructor calls - $compileProvider.preAssignBindingsEnabled(true); + app.config(($locationProvider, $controllerProvider, $compileProvider, $filterProvider, $httpProvider, $provide) => { + // pre assing bindings before constructor calls + $compileProvider.preAssignBindingsEnabled(true); - if (config.buildInfo.env !== "development") { - $compileProvider.debugInfoEnabled(false); - } - - $httpProvider.useApplyAsync(true); - - this.registerFunctions.controller = $controllerProvider.register; - this.registerFunctions.directive = $compileProvider.directive; - this.registerFunctions.factory = $provide.factory; - this.registerFunctions.service = $provide.service; - this.registerFunctions.filter = $filterProvider.register; - - $provide.decorator("$http", [ - "$delegate", - "$templateCache", - function($delegate, $templateCache) { - var get = $delegate.get; - $delegate.get = function(url, config) { - if (url.match(/\.html$/)) { - // some template's already exist in the cache - if (!$templateCache.get(url)) { - url += "?v=" + new Date().getTime(); - } - } - return get(url, config); - }; - return $delegate; - } - ]); + if (config.buildInfo.env !== 'development') { + $compileProvider.debugInfoEnabled(false); } - ); + + $httpProvider.useApplyAsync(true); + + this.registerFunctions.controller = $controllerProvider.register; + this.registerFunctions.directive = $compileProvider.directive; + this.registerFunctions.factory = $provide.factory; + this.registerFunctions.service = $provide.service; + this.registerFunctions.filter = $filterProvider.register; + + $provide.decorator('$http', [ + '$delegate', + '$templateCache', + function($delegate, $templateCache) { + var get = $delegate.get; + $delegate.get = function(url, config) { + if (url.match(/\.html$/)) { + // some template's already exist in the cache + if (!$templateCache.get(url)) { + url += '?v=' + new Date().getTime(); + } + } + return get(url, config); + }; + return $delegate; + }, + ]); + }); this.ngModuleDependencies = [ - "grafana.core", - "ngRoute", - "ngSanitize", - "$strap.directives", - "ang-drag-drop", - "grafana", - "pasvaz.bindonce", - "ui.bootstrap", - "ui.bootstrap.tpls", - "react" + 'grafana.core', + 'ngRoute', + 'ngSanitize', + '$strap.directives', + 'ang-drag-drop', + 'grafana', + 'pasvaz.bindonce', + 'ui.bootstrap', + 'ui.bootstrap.tpls', + 'react', ]; - var module_types = [ - "controllers", - "directives", - "factories", - "services", - "filters", - "routes" - ]; + var module_types = ['controllers', 'directives', 'factories', 'services', 'filters', 'routes']; _.each(module_types, type => { - var moduleName = "grafana." + type; + var moduleName = 'grafana.' + type; this.useModule(angular.module(moduleName, [])); }); @@ -130,16 +114,16 @@ export class GrafanaApp { this.useModule(coreModule); // register react angular wrappers + coreModule.config(setupAngularRoutes); registerAngularDirectives(); - var preBootRequires = [System.import("app/features/all")]; + var preBootRequires = [System.import('app/features/all')]; Promise.all(preBootRequires) .then(() => { - createStore(); - // disable tool tip animation $.fn.tooltip.defaults.animation = false; + // bootstrap the app angular.bootstrap(document, this.ngModuleDependencies).invoke(() => { _.each(this.preBootModules, module => { @@ -150,7 +134,7 @@ export class GrafanaApp { }); }) .catch(function(err) { - console.log("Application boot failed:", err); + console.log('Application boot failed:', err); }); } } diff --git a/public/app/containers/ServerStats.tsx b/public/app/containers/ServerStats.tsx new file mode 100644 index 00000000000..b4ec56c4311 --- /dev/null +++ b/public/app/containers/ServerStats.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { inject, observer } from 'mobx-react'; +import PageHeader from 'app/core/components/PageHeader/PageHeader'; +import { NavModel, NavModelSrv } from 'app/core/nav_model_srv'; + +export interface IProps { + store: any; +} + +@inject('store') +@observer +export default class ServerStats extends React.Component { + navModel: NavModel; + + constructor(props) { + super(props); + + this.navModel = new NavModelSrv().getNav('cfg', 'admin', 'server-stats', 1); + this.props.store.serverStats.load(); + } + + render() { + return ( +
+ +
+ + + + + + + + {this.props.store.serverStats.stats.map(StatItem)} +
NameValue
+
+
+ ); + } +} + +function StatItem(stat) { + return ( + + {stat.name} + {stat.value} + + ); +} diff --git a/public/app/core/app_events.ts b/public/app/core/app_events.ts index 789e6e14209..26dd74bcb00 100644 --- a/public/app/core/app_events.ts +++ b/public/app/core/app_events.ts @@ -1,4 +1,4 @@ -import { Emitter } from "./utils/emitter"; +import { Emitter } from './utils/emitter'; var appEvents = new Emitter(); export default appEvents; diff --git a/public/app/core/components/PageHeader/PageHeader.tsx b/public/app/core/components/PageHeader/PageHeader.tsx index fb09ed29085..b6b6511c04e 100644 --- a/public/app/core/components/PageHeader/PageHeader.tsx +++ b/public/app/core/components/PageHeader/PageHeader.tsx @@ -9,7 +9,7 @@ export interface IProps { function TabItem(tab: NavModelItem) { if (tab.hideFromTabs) { - return (null); + return null; } let tabClasses = classNames({ @@ -28,8 +28,9 @@ function TabItem(tab: NavModelItem) { } function SelectOption(navItem: NavModelItem) { - if (navItem.hideFromTabs) { // TODO: Rename hideFromTabs => hideFromNav - return (null); + if (navItem.hideFromTabs) { + // TODO: Rename hideFromTabs => hideFromNav + return null; } return ( @@ -39,14 +40,16 @@ function SelectOption(navItem: NavModelItem) { ); } -function Navigation({main}: {main: NavModelItem}) { - return (); +function Navigation({ main }: { main: NavModelItem }) { + return ( + + ); } -function SelectNav({main, customCss}: {main: NavModelItem, customCss: string}) { +function SelectNav({ main, customCss }: { main: NavModelItem; customCss: string }) { const defaultSelectedItem = main.children.find(navItem => { return navItem.active === true; }); @@ -54,16 +57,26 @@ function SelectNav({main, customCss}: {main: NavModelItem, customCss: string}) { const gotoUrl = evt => { var element = evt.target; var url = element.options[element.selectedIndex].value; - appEvents.emit('location-change', {href: url}); + appEvents.emit('location-change', { href: url }); }; - return (); + return ( +
+
+ ); } -function Tabs({main, customCss}: {main: NavModelItem, customCss: string}) { +function Tabs({ main, customCss }: { main: NavModelItem; customCss: string }) { return ; } @@ -77,7 +90,11 @@ export default class PageHeader extends React.Component { for (let i = 0; i < breadcrumbs.length; i++) { const bc = breadcrumbs[i]; if (bc.url) { - breadcrumbsResult.push({bc.title}); + breadcrumbsResult.push( + + {bc.title} + + ); } else { breadcrumbsResult.push( / {bc.title}); } @@ -95,11 +112,10 @@ export default class PageHeader extends React.Component {
{main.text &&

{main.text}

} - {main.breadcrumbs && main.breadcrumbs.length > 0 && ( -

- {this.renderBreadcrumb(main.breadcrumbs)} -

) - } + {main.breadcrumbs && + main.breadcrumbs.length > 0 && ( +

{this.renderBreadcrumb(main.breadcrumbs)}

+ )} {main.subTitle &&
{main.subTitle}
} {main.subType && (
diff --git a/public/app/core/components/code_editor/code_editor.ts b/public/app/core/components/code_editor/code_editor.ts index c8646020892..2a0fe103bcf 100644 --- a/public/app/core/components/code_editor/code_editor.ts +++ b/public/app/core/components/code_editor/code_editor.ts @@ -26,24 +26,24 @@ * Ctrl-Enter (Command-Enter): run onChange() function */ -import coreModule from "app/core/core_module"; -import config from "app/core/config"; -import ace from "brace"; -import "./theme-grafana-dark"; -import "brace/ext/language_tools"; -import "brace/theme/textmate"; -import "brace/mode/text"; -import "brace/snippets/text"; -import "brace/mode/sql"; -import "brace/snippets/sql"; -import "brace/mode/markdown"; -import "brace/snippets/markdown"; -import "brace/mode/json"; -import "brace/snippets/json"; +import coreModule from 'app/core/core_module'; +import config from 'app/core/config'; +import ace from 'brace'; +import './theme-grafana-dark'; +import 'brace/ext/language_tools'; +import 'brace/theme/textmate'; +import 'brace/mode/text'; +import 'brace/snippets/text'; +import 'brace/mode/sql'; +import 'brace/snippets/sql'; +import 'brace/mode/markdown'; +import 'brace/snippets/markdown'; +import 'brace/mode/json'; +import 'brace/snippets/json'; -const DEFAULT_THEME_DARK = "ace/theme/grafana-dark"; -const DEFAULT_THEME_LIGHT = "ace/theme/textmate"; -const DEFAULT_MODE = "text"; +const DEFAULT_THEME_DARK = 'ace/theme/grafana-dark'; +const DEFAULT_THEME_LIGHT = 'ace/theme/textmate'; +const DEFAULT_MODE = 'text'; const DEFAULT_MAX_LINES = 10; const DEFAULT_TAB_SIZE = 2; const DEFAULT_BEHAVIOURS = true; @@ -56,9 +56,7 @@ function link(scope, elem, attrs) { let maxLines = attrs.maxLines || DEFAULT_MAX_LINES; let showGutter = attrs.showGutter !== undefined; let tabSize = attrs.tabSize || DEFAULT_TAB_SIZE; - let behavioursEnabled = attrs.behavioursEnabled - ? attrs.behavioursEnabled === "true" - : DEFAULT_BEHAVIOURS; + let behavioursEnabled = attrs.behavioursEnabled ? attrs.behavioursEnabled === 'true' : DEFAULT_BEHAVIOURS; // Initialize editor let aceElem = elem.get(0); @@ -72,7 +70,7 @@ function link(scope, elem, attrs) { behavioursEnabled: behavioursEnabled, highlightActiveLine: false, showPrintMargin: false, - autoScrollEditorIntoView: true // this is needed if editor is inside scrollable page + autoScrollEditorIntoView: true, // this is needed if editor is inside scrollable page }; // Set options @@ -88,9 +86,9 @@ function link(scope, elem, attrs) { setEditorContent(scope.content); // Add classes - elem.addClass("gf-code-editor"); - let textarea = elem.find("textarea"); - textarea.addClass("gf-form-input"); + elem.addClass('gf-code-editor'); + let textarea = elem.find('textarea'); + textarea.addClass('gf-form-input'); if (scope.codeEditorFocus) { setTimeout(function() { @@ -104,7 +102,7 @@ function link(scope, elem, attrs) { } // Event handlers - editorSession.on("change", e => { + editorSession.on('change', e => { scope.$apply(() => { let newValue = codeEditor.getValue(); scope.content = newValue; @@ -112,7 +110,7 @@ function link(scope, elem, attrs) { }); // Sync with outer scope - update editor content if model has been changed from outside of directive. - scope.$watch("content", (newValue, oldValue) => { + scope.$watch('content', (newValue, oldValue) => { let editorValue = codeEditor.getValue(); if (newValue !== editorValue && newValue !== oldValue) { scope.$$postDigest(function() { @@ -121,29 +119,29 @@ function link(scope, elem, attrs) { } }); - codeEditor.on("blur", () => { + codeEditor.on('blur', () => { scope.onChange(); }); - scope.$on("$destroy", () => { + scope.$on('$destroy', () => { codeEditor.destroy(); }); // Keybindings codeEditor.commands.addCommand({ - name: "executeQuery", - bindKey: { win: "Ctrl-Enter", mac: "Command-Enter" }, + name: 'executeQuery', + bindKey: { win: 'Ctrl-Enter', mac: 'Command-Enter' }, exec: () => { scope.onChange(); - } + }, }); function setLangMode(lang) { - ace.acequire("ace/ext/language_tools"); + ace.acequire('ace/ext/language_tools'); codeEditor.setOptions({ enableBasicAutocompletion: true, enableLiveAutocompletion: true, - enableSnippets: true + enableSnippets: true, }); if (scope.getCompleter()) { @@ -174,17 +172,17 @@ function link(scope, elem, attrs) { export function codeEditorDirective() { return { - restrict: "E", + restrict: 'E', template: editorTemplate, scope: { - content: "=", - datasource: "=", - codeEditorFocus: "<", - onChange: "&", - getCompleter: "&" + content: '=', + datasource: '=', + codeEditorFocus: '<', + onChange: '&', + getCompleter: '&', }, - link: link + link: link, }; } -coreModule.directive("codeEditor", codeEditorDirective); +coreModule.directive('codeEditor', codeEditorDirective); diff --git a/public/app/core/components/colorpicker/spectrum_picker.ts b/public/app/core/components/colorpicker/spectrum_picker.ts index 8b46bc3fdc9..6e93a4f39f4 100644 --- a/public/app/core/components/colorpicker/spectrum_picker.ts +++ b/public/app/core/components/colorpicker/spectrum_picker.ts @@ -3,23 +3,22 @@ * Allows remaining untouched in outdated plugins. * Technically, it's just a wrapper for react component with two-way data binding support. */ -import coreModule from "../../core_module"; +import coreModule from '../../core_module'; /** @ngInject */ export function spectrumPicker() { return { - restrict: "E", - require: "ngModel", + restrict: 'E', + require: 'ngModel', scope: true, replace: true, - template: - '', + template: '', link: function(scope, element, attrs, ngModel) { scope.ngModel = ngModel; scope.onColorChange = color => { ngModel.$setViewValue(color); }; - } + }, }; } -coreModule.directive("spectrumPicker", spectrumPicker); +coreModule.directive('spectrumPicker', spectrumPicker); diff --git a/public/app/core/components/dashboard_selector.ts b/public/app/core/components/dashboard_selector.ts index b52227cc9a6..379fd441a19 100644 --- a/public/app/core/components/dashboard_selector.ts +++ b/public/app/core/components/dashboard_selector.ts @@ -1,4 +1,4 @@ -import coreModule from "app/core/core_module"; +import coreModule from 'app/core/core_module'; var template = ` @@ -12,7 +12,7 @@ export class DashboardSelectorCtrl { constructor(private backendSrv) {} $onInit() { - this.options = [{ value: 0, text: "Default" }]; + this.options = [{ value: 0, text: 'Default' }]; return this.backendSrv.search({ starred: true }).then(res => { res.forEach(dash => { @@ -24,15 +24,15 @@ export class DashboardSelectorCtrl { export function dashboardSelector() { return { - restrict: "E", + restrict: 'E', controller: DashboardSelectorCtrl, bindToController: true, - controllerAs: "ctrl", + controllerAs: 'ctrl', template: template, scope: { - model: "=" - } + model: '=', + }, }; } -coreModule.directive("dashboardSelector", dashboardSelector); +coreModule.directive('dashboardSelector', dashboardSelector); diff --git a/public/app/core/components/form_dropdown/form_dropdown.ts b/public/app/core/components/form_dropdown/form_dropdown.ts index bdea18e6276..1fa1dea4338 100644 --- a/public/app/core/components/form_dropdown/form_dropdown.ts +++ b/public/app/core/components/form_dropdown/form_dropdown.ts @@ -1,13 +1,13 @@ -import _ from "lodash"; -import $ from "jquery"; -import coreModule from "../../core_module"; +import _ from 'lodash'; +import $ from 'jquery'; +import coreModule from '../../core_module'; function typeaheadMatcher(item) { var str = this.query; - if (str[0] === "/") { + if (str[0] === '/') { str = str.substring(1); } - if (str[str.length - 1] === "/") { + if (str[str.length - 1] === '/') { str = str.substring(0, str.length - 1); } return item.toLowerCase().match(str.toLowerCase()); @@ -32,42 +32,35 @@ export class FormDropdownCtrl { lookupText: boolean; /** @ngInject **/ - constructor( - private $scope, - $element, - private $sce, - private templateSrv, - private $q - ) { - this.inputElement = $element.find("input").first(); - this.linkElement = $element.find("a").first(); + constructor(private $scope, $element, private $sce, private templateSrv, private $q) { + this.inputElement = $element.find('input').first(); + this.linkElement = $element.find('a').first(); this.linkMode = true; this.cancelBlur = null; // listen to model changes - $scope.$watch("ctrl.model", this.modelChanged.bind(this)); + $scope.$watch('ctrl.model', this.modelChanged.bind(this)); if (this.labelMode) { - this.cssClasses = "gf-form-label " + this.cssClass; + this.cssClasses = 'gf-form-label ' + this.cssClass; } else { - this.cssClasses = - "gf-form-input gf-form-input--dropdown " + this.cssClass; + this.cssClasses = 'gf-form-input gf-form-input--dropdown ' + this.cssClass; } - this.inputElement.attr("data-provide", "typeahead"); + this.inputElement.attr('data-provide', 'typeahead'); this.inputElement.typeahead({ source: this.typeaheadSource.bind(this), minLength: 0, items: 10000, updater: this.typeaheadUpdater.bind(this), - matcher: typeaheadMatcher + matcher: typeaheadMatcher, }); // modify typeahead lookup // this = typeahead - var typeahead = this.inputElement.data("typeahead"); + var typeahead = this.inputElement.data('typeahead'); typeahead.lookup = function() { - this.query = this.$element.val() || ""; + this.query = this.$element.val() || ''; var items = this.source(this.query, $.proxy(this.process, this)); return items ? this.process(items) : items; }; @@ -99,7 +92,7 @@ export class FormDropdownCtrl { } isPromiseLike(obj) { - return obj && typeof obj.then === "function"; + return obj && typeof obj.then === 'function'; } modelChanged() { @@ -108,7 +101,7 @@ export class FormDropdownCtrl { } else { // if we have text use it if (this.lookupText) { - this.getOptionsInternal("").then(options => { + this.getOptionsInternal('').then(options => { var item = _.find(options, { value: this.model }); this.updateDisplay(item ? item.text : this.model); }); @@ -172,7 +165,7 @@ export class FormDropdownCtrl { updateValue(text) { text = _.unescape(text); - if (text === "" || this.text === text) { + if (text === '' || this.text === text) { return; } @@ -207,16 +200,11 @@ export class FormDropdownCtrl { updateDisplay(text) { this.text = text; - this.display = this.$sce.trustAsHtml( - this.templateSrv.highlightVariablesAsHtml(text) - ); + this.display = this.$sce.trustAsHtml(this.templateSrv.highlightVariablesAsHtml(text)); } open() { - this.inputElement.css( - "width", - Math.max(this.linkElement.width(), 80) + 16 + "px" - ); + this.inputElement.css('width', Math.max(this.linkElement.width(), 80) + 16 + 'px'); this.inputElement.show(); this.inputElement.focus(); @@ -224,9 +212,9 @@ export class FormDropdownCtrl { this.linkElement.hide(); this.linkMode = false; - var typeahead = this.inputElement.data("typeahead"); + var typeahead = this.inputElement.data('typeahead'); if (typeahead) { - this.inputElement.val(""); + this.inputElement.val(''); typeahead.lookup(); } } @@ -249,21 +237,21 @@ const template = ` export function formDropdownDirective() { return { - restrict: "E", + restrict: 'E', template: template, controller: FormDropdownCtrl, bindToController: true, - controllerAs: "ctrl", + controllerAs: 'ctrl', scope: { - model: "=", - getOptions: "&", - onChange: "&", - cssClass: "@", - allowCustom: "@", - labelMode: "@", - lookupText: "@" - } + model: '=', + getOptions: '&', + onChange: '&', + cssClass: '@', + allowCustom: '@', + labelMode: '@', + lookupText: '@', + }, }; } -coreModule.directive("gfFormDropdown", formDropdownDirective); +coreModule.directive('gfFormDropdown', formDropdownDirective); diff --git a/public/app/core/components/gf_page.ts b/public/app/core/components/gf_page.ts index c8009f8c924..5df3bd89715 100644 --- a/public/app/core/components/gf_page.ts +++ b/public/app/core/components/gf_page.ts @@ -1,6 +1,6 @@ /// -import coreModule from "app/core/core_module"; +import coreModule from 'app/core/core_module'; const template = `
@@ -24,19 +24,19 @@ const template = ` export function gfPageDirective() { return { - restrict: "E", + restrict: 'E', template: template, scope: { - model: "=" + model: '=', }, transclude: { - header: "?gfPageHeader", - body: "gfPageBody" + header: '?gfPageHeader', + body: 'gfPageBody', }, link: function(scope, elem, attrs) { console.log(scope); - } + }, }; } -coreModule.directive("gfPage", gfPageDirective); +coreModule.directive('gfPage', gfPageDirective); diff --git a/public/app/core/components/grafana_app.ts b/public/app/core/components/grafana_app.ts index f8511d393e9..01bd80c2bee 100644 --- a/public/app/core/components/grafana_app.ts +++ b/public/app/core/components/grafana_app.ts @@ -1,23 +1,18 @@ -import config from "app/core/config"; -import _ from "lodash"; -import $ from "jquery"; +import config from 'app/core/config'; +import _ from 'lodash'; +import $ from 'jquery'; -import coreModule from "app/core/core_module"; -import { profiler } from "app/core/profiler"; -import appEvents from "app/core/app_events"; -import Drop from "tether-drop"; +import coreModule from 'app/core/core_module'; +import { profiler } from 'app/core/profiler'; +import appEvents from 'app/core/app_events'; +import Drop from 'tether-drop'; +import { createStore } from 'app/stores/store'; export class GrafanaCtrl { /** @ngInject */ - constructor( - $scope, - alertSrv, - utilSrv, - $rootScope, - $controller, - contextSrv, - globalEventSrv - ) { + constructor($scope, alertSrv, utilSrv, $rootScope, $controller, contextSrv, globalEventSrv, backendSrv) { + createStore(backendSrv); + $scope.init = function() { $scope.contextSrv = contextSrv; @@ -33,20 +28,20 @@ export class GrafanaCtrl { }; $scope.initDashboard = function(dashboardData, viewScope) { - $scope.appEvent("dashboard-fetch-end", dashboardData); - $controller("DashboardCtrl", { $scope: viewScope }).init(dashboardData); + $scope.appEvent('dashboard-fetch-end', dashboardData); + $controller('DashboardCtrl', { $scope: viewScope }).init(dashboardData); }; $rootScope.onAppEvent = function(name, callback, localScope) { var unbind = $rootScope.$on(name, callback); var callerScope = this; if (callerScope.$id === 1 && !localScope) { - console.log("warning rootScope onAppEvent called without localscope"); + console.log('warning rootScope onAppEvent called without localscope'); } if (localScope) { callerScope = localScope; } - callerScope.$on("$destroy", unbind); + callerScope.$on('$destroy', unbind); }; $rootScope.appEvent = function(name, payload) { @@ -55,62 +50,62 @@ export class GrafanaCtrl { }; $rootScope.colors = [ - "#7EB26D", - "#EAB839", - "#6ED0E0", - "#EF843C", - "#E24D42", - "#1F78C1", - "#BA43A9", - "#705DA0", - "#508642", - "#CCA300", - "#447EBC", - "#C15C17", - "#890F02", - "#0A437C", - "#6D1F62", - "#584477", - "#B7DBAB", - "#F4D598", - "#70DBED", - "#F9BA8F", - "#F29191", - "#82B5D8", - "#E5A8E2", - "#AEA2E0", - "#629E51", - "#E5AC0E", - "#64B0C8", - "#E0752D", - "#BF1B00", - "#0A50A1", - "#962D82", - "#614D93", - "#9AC48A", - "#F2C96D", - "#65C5DB", - "#F9934E", - "#EA6460", - "#5195CE", - "#D683CE", - "#806EB7", - "#3F6833", - "#967302", - "#2F575E", - "#99440A", - "#58140C", - "#052B51", - "#511749", - "#3F2B5B", - "#E0F9D7", - "#FCEACA", - "#CFFAFF", - "#F9E2D2", - "#FCE2DE", - "#BADFF4", - "#F9D9F9", - "#DEDAF7" + '#7EB26D', + '#EAB839', + '#6ED0E0', + '#EF843C', + '#E24D42', + '#1F78C1', + '#BA43A9', + '#705DA0', + '#508642', + '#CCA300', + '#447EBC', + '#C15C17', + '#890F02', + '#0A437C', + '#6D1F62', + '#584477', + '#B7DBAB', + '#F4D598', + '#70DBED', + '#F9BA8F', + '#F29191', + '#82B5D8', + '#E5A8E2', + '#AEA2E0', + '#629E51', + '#E5AC0E', + '#64B0C8', + '#E0752D', + '#BF1B00', + '#0A50A1', + '#962D82', + '#614D93', + '#9AC48A', + '#F2C96D', + '#65C5DB', + '#F9934E', + '#EA6460', + '#5195CE', + '#D683CE', + '#806EB7', + '#3F6833', + '#967302', + '#2F575E', + '#99440A', + '#58140C', + '#052B51', + '#511749', + '#3F2B5B', + '#E0F9D7', + '#FCEACA', + '#CFFAFF', + '#F9E2D2', + '#FCE2DE', + '#BADFF4', + '#F9D9F9', + '#DEDAF7', ]; $scope.init(); @@ -118,41 +113,36 @@ export class GrafanaCtrl { } /** @ngInject */ -export function grafanaAppDirective( - playlistSrv, - contextSrv, - $timeout, - $rootScope -) { +export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScope) { return { - restrict: "E", + restrict: 'E', controller: GrafanaCtrl, link: (scope, elem) => { var sidemenuOpen; - var body = $("body"); + var body = $('body'); // see https://github.com/zenorocha/clipboard.js/issues/155 $.fn.modal.Constructor.prototype.enforceFocus = function() {}; sidemenuOpen = scope.contextSrv.sidemenu; - body.toggleClass("sidemenu-open", sidemenuOpen); + body.toggleClass('sidemenu-open', sidemenuOpen); - appEvents.on("toggle-sidemenu", () => { - body.toggleClass("sidemenu-open"); + appEvents.on('toggle-sidemenu', () => { + body.toggleClass('sidemenu-open'); }); - appEvents.on("toggle-sidemenu-mobile", () => { - body.toggleClass("sidemenu-open--xs"); + appEvents.on('toggle-sidemenu-mobile', () => { + body.toggleClass('sidemenu-open--xs'); }); - appEvents.on("toggle-sidemenu-hidden", () => { - body.toggleClass("sidemenu-hidden"); + appEvents.on('toggle-sidemenu-hidden', () => { + body.toggleClass('sidemenu-hidden'); }); // tooltip removal fix // manage page classes var pageClass; - scope.$on("$routeChangeSuccess", function(evt, data) { + scope.$on('$routeChangeSuccess', function(evt, data) { if (pageClass) { body.removeClass(pageClass); } @@ -165,13 +155,13 @@ export function grafanaAppDirective( } // clear body class sidemenu states - body.removeClass("sidemenu-open--xs"); + body.removeClass('sidemenu-open--xs'); - $("#tooltip, .tooltip").remove(); + $('#tooltip, .tooltip').remove(); // check for kiosk url param if (data.params.kiosk) { - appEvents.emit("toggle-kiosk-mode"); + appEvents.emit('toggle-kiosk-mode'); } // close all drops @@ -181,8 +171,8 @@ export function grafanaAppDirective( }); // handle kiosk mode - appEvents.on("toggle-kiosk-mode", () => { - body.toggleClass("page-kiosk-mode"); + appEvents.on('toggle-kiosk-mode', () => { + body.toggleClass('page-kiosk-mode'); }); // handle in active view state class @@ -196,19 +186,19 @@ export function grafanaAppDirective( return; } // only go to activity low mode on dashboard page - if (!body.hasClass("page-dashboard")) { + if (!body.hasClass('page-dashboard')) { return; } if (new Date().getTime() - lastActivity > inActiveTimeLimit) { activeUser = false; - body.addClass("user-activity-low"); + body.addClass('user-activity-low'); // hide sidemenu if (sidemenuOpen) { sidemenuHidden = true; - body.removeClass("sidemenu-open"); + body.removeClass('sidemenu-open'); $timeout(function() { - $rootScope.$broadcast("render"); + $rootScope.$broadcast('render'); }, 100); } } @@ -218,14 +208,14 @@ export function grafanaAppDirective( lastActivity = new Date().getTime(); if (!activeUser) { activeUser = true; - body.removeClass("user-activity-low"); + body.removeClass('user-activity-low'); // restore sidemenu if (sidemenuHidden) { sidemenuHidden = false; - body.addClass("sidemenu-open"); + body.addClass('sidemenu-open'); $timeout(function() { - $rootScope.$broadcast("render"); + $rootScope.$broadcast('render'); }, 100); } } @@ -235,12 +225,12 @@ export function grafanaAppDirective( body.mousemove(userActivityDetected); body.keydown(userActivityDetected); // treat tab change as activity - document.addEventListener("visibilitychange", userActivityDetected); + document.addEventListener('visibilitychange', userActivityDetected); // check every 2 seconds setInterval(checkForInActiveUser, 2000); - appEvents.on("toggle-view-mode", () => { + appEvents.on('toggle-view-mode', () => { lastActivity = 0; checkForInActiveUser(); }); @@ -254,7 +244,7 @@ export function grafanaAppDirective( // for stuff that animates, slides out etc, clicking it needs to // hide it right away - var clickAutoHide = target.closest("[data-click-hide]"); + var clickAutoHide = target.closest('[data-click-hide]'); if (clickAutoHide.length) { var clickAutoHideParent = clickAutoHide.parent(); clickAutoHide.detach(); @@ -263,33 +253,27 @@ export function grafanaAppDirective( }, 100); } - if (target.parents(".navbar-buttons--playlist").length === 0) { + if (target.parents('.navbar-buttons--playlist').length === 0) { playlistSrv.stop(); } // hide search - if (body.find(".search-container").length > 0) { - if ( - target.parents(".search-results-container, .search-field-wrapper") - .length === 0 - ) { + if (body.find('.search-container').length > 0) { + if (target.parents('.search-results-container, .search-field-wrapper').length === 0) { scope.$apply(function() { - scope.appEvent("hide-dash-search"); + scope.appEvent('hide-dash-search'); }); } } // hide popovers - var popover = elem.find(".popover"); - if ( - popover.length > 0 && - target.parents(".graph-legend").length === 0 - ) { + var popover = elem.find('.popover'); + if (popover.length > 0 && target.parents('.graph-legend').length === 0) { popover.hide(); } }); - } + }, }; } -coreModule.directive("grafanaApp", grafanaAppDirective); +coreModule.directive('grafanaApp', grafanaAppDirective); diff --git a/public/app/core/components/help/help.ts b/public/app/core/components/help/help.ts index b59ebaaedf6..a594b95bb4d 100644 --- a/public/app/core/components/help/help.ts +++ b/public/app/core/components/help/help.ts @@ -1,7 +1,7 @@ /// -import coreModule from "../../core_module"; -import appEvents from "app/core/app_events"; +import coreModule from '../../core_module'; +import appEvents from 'app/core/app_events'; export class HelpCtrl { tabIndex: any; @@ -12,63 +12,63 @@ export class HelpCtrl { this.tabIndex = 0; this.shortcuts = { Global: [ - { keys: ["g", "h"], description: "Go to Home Dashboard" }, - { keys: ["g", "p"], description: "Go to Profile" }, - { keys: ["s", "o"], description: "Open search" }, - { keys: ["s", "s"], description: "Open search with starred filter" }, - { keys: ["s", "t"], description: "Open search in tags view" }, - { keys: ["esc"], description: "Exit edit/setting views" } + { keys: ['g', 'h'], description: 'Go to Home Dashboard' }, + { keys: ['g', 'p'], description: 'Go to Profile' }, + { keys: ['s', 'o'], description: 'Open search' }, + { keys: ['s', 's'], description: 'Open search with starred filter' }, + { keys: ['s', 't'], description: 'Open search in tags view' }, + { keys: ['esc'], description: 'Exit edit/setting views' }, ], Dashboard: [ - { keys: ["mod+s"], description: "Save dashboard" }, - { keys: ["mod+h"], description: "Hide row controls" }, - { keys: ["d", "r"], description: "Refresh all panels" }, - { keys: ["d", "s"], description: "Dashboard settings" }, - { keys: ["d", "v"], description: "Toggle in-active / view mode" }, - { keys: ["d", "k"], description: "Toggle kiosk mode (hides top nav)" }, - { keys: ["d", "E"], description: "Expand all rows" }, - { keys: ["d", "C"], description: "Collapse all rows" }, - { keys: ["mod+o"], description: "Toggle shared graph crosshair" } + { keys: ['mod+s'], description: 'Save dashboard' }, + { keys: ['mod+h'], description: 'Hide row controls' }, + { keys: ['d', 'r'], description: 'Refresh all panels' }, + { keys: ['d', 's'], description: 'Dashboard settings' }, + { keys: ['d', 'v'], description: 'Toggle in-active / view mode' }, + { keys: ['d', 'k'], description: 'Toggle kiosk mode (hides top nav)' }, + { keys: ['d', 'E'], description: 'Expand all rows' }, + { keys: ['d', 'C'], description: 'Collapse all rows' }, + { keys: ['mod+o'], description: 'Toggle shared graph crosshair' }, ], - "Focused Panel": [ - { keys: ["e"], description: "Toggle panel edit view" }, - { keys: ["v"], description: "Toggle panel fullscreen view" }, - { keys: ["p", "s"], description: "Open Panel Share Modal" }, - { keys: ["p", "r"], description: "Remove Panel" } + 'Focused Panel': [ + { keys: ['e'], description: 'Toggle panel edit view' }, + { keys: ['v'], description: 'Toggle panel fullscreen view' }, + { keys: ['p', 's'], description: 'Open Panel Share Modal' }, + { keys: ['p', 'r'], description: 'Remove Panel' }, ], - "Focused Row": [ - { keys: ["r", "c"], description: "Collapse Row" }, - { keys: ["r", "r"], description: "Remove Row" } + 'Focused Row': [ + { keys: ['r', 'c'], description: 'Collapse Row' }, + { keys: ['r', 'r'], description: 'Remove Row' }, ], - "Time Range": [ - { keys: ["t", "z"], description: "Zoom out time range" }, + 'Time Range': [ + { keys: ['t', 'z'], description: 'Zoom out time range' }, { - keys: ["t", ''], - description: "Move time range back" + keys: ['t', ''], + description: 'Move time range back', }, { - keys: ["t", ''], - description: "Move time range forward" - } - ] + keys: ['t', ''], + description: 'Move time range forward', + }, + ], }; } dismiss() { - appEvents.emit("hide-modal"); + appEvents.emit('hide-modal'); } } export function helpModal() { return { - restrict: "E", - templateUrl: "public/app/core/components/help/help.html", + restrict: 'E', + templateUrl: 'public/app/core/components/help/help.html', controller: HelpCtrl, bindToController: true, transclude: true, - controllerAs: "ctrl", - scope: {} + controllerAs: 'ctrl', + scope: {}, }; } -coreModule.directive("helpModal", helpModal); +coreModule.directive('helpModal', helpModal); diff --git a/public/app/core/components/info_popover.ts b/public/app/core/components/info_popover.ts index cac25b28b9d..9d2d13c9f01 100644 --- a/public/app/core/components/info_popover.ts +++ b/public/app/core/components/info_popover.ts @@ -1,33 +1,33 @@ /// -import _ from "lodash"; -import coreModule from "app/core/core_module"; -import Drop from "tether-drop"; +import _ from 'lodash'; +import coreModule from 'app/core/core_module'; +import Drop from 'tether-drop'; export function infoPopover() { return { - restrict: "E", + restrict: 'E', template: '', transclude: true, link: function(scope, elem, attrs, ctrl, transclude) { - var offset = attrs.offset || "0 -10px"; - var position = attrs.position || "right middle"; - var classes = "drop-help drop-hide-out-of-bounds"; - var openOn = "hover"; + var offset = attrs.offset || '0 -10px'; + var position = attrs.position || 'right middle'; + var classes = 'drop-help drop-hide-out-of-bounds'; + var openOn = 'hover'; - elem.addClass("gf-form-help-icon"); + elem.addClass('gf-form-help-icon'); if (attrs.wide) { - classes += " drop-wide"; + classes += ' drop-wide'; } if (attrs.mode) { - elem.addClass("gf-form-help-icon--" + attrs.mode); + elem.addClass('gf-form-help-icon--' + attrs.mode); } transclude(function(clone, newScope) { - var content = document.createElement("div"); - content.className = "markdown-html"; + var content = document.createElement('div'); + content.className = 'markdown-html'; _.each(clone, node => { content.appendChild(node); @@ -44,21 +44,21 @@ export function infoPopover() { offset: offset, constraints: [ { - to: "window", - attachment: "together", - pin: true - } - ] - } + to: 'window', + attachment: 'together', + pin: true, + }, + ], + }, }); - var unbind = scope.$on("$destroy", function() { + var unbind = scope.$on('$destroy', function() { drop.destroy(); unbind(); }); }); - } + }, }; } -coreModule.directive("infoPopover", infoPopover); +coreModule.directive('infoPopover', infoPopover); diff --git a/public/app/core/components/json_explorer/helpers.ts b/public/app/core/components/json_explorer/helpers.ts index c50fc5a0146..5b053792d73 100644 --- a/public/app/core/components/json_explorer/helpers.ts +++ b/public/app/core/components/json_explorer/helpers.ts @@ -13,7 +13,7 @@ function escapeString(str: string): string { */ export function isObject(value: any): boolean { var type = typeof value; - return !!value && type === "object"; + return !!value && type === 'object'; } /* @@ -23,13 +23,13 @@ export function isObject(value: any): boolean { */ export function getObjectName(object: Object): string { if (object === undefined) { - return ""; + return ''; } if (object === null) { - return "Object"; + return 'Object'; } - if (typeof object === "object" && !object.constructor) { - return "Object"; + if (typeof object === 'object' && !object.constructor) { + return 'Object'; } const funcNameRegex = /function ([^(]*)/; @@ -37,7 +37,7 @@ export function getObjectName(object: Object): string { if (results && results.length > 1) { return results[1]; } else { - return ""; + return ''; } } @@ -46,7 +46,7 @@ export function getObjectName(object: Object): string { */ export function getType(object: Object): string { if (object === null) { - return "null"; + return 'null'; } return typeof object; } @@ -57,20 +57,20 @@ export function getType(object: Object): string { export function getValuePreview(object: Object, value: string): string { var type = getType(object); - if (type === "null" || type === "undefined") { + if (type === 'null' || type === 'undefined') { return type; } - if (type === "string") { + if (type === 'string') { value = '"' + escapeString(value) + '"'; } - if (type === "function") { + if (type === 'function') { // Remove content of the function return ( object .toString() - .replace(/[\r\n]/g, "") - .replace(/\{.*\}/, "") + "{…}" + .replace(/[\r\n]/g, '') + .replace(/\{.*\}/, '') + '{…}' ); } return value; @@ -80,11 +80,11 @@ export function getValuePreview(object: Object, value: string): string { * Generates inline preview for a JavaScript object */ export function getPreview(object: string): string { - let value = ""; + let value = ''; if (isObject(object)) { value = getObjectName(object); if (Array.isArray(object)) { - value += "[" + object.length + "]"; + value += '[' + object.length + ']'; } } else { value = getValuePreview(object, object); @@ -103,11 +103,7 @@ export function cssClass(className: string): string { * Creates a new DOM element wiht given type and class * TODO: move me to helpers */ -export function createElement( - type: string, - className?: string, - content?: Element | string -): Element { +export function createElement(type: string, className?: string, content?: Element | string): Element { const el = document.createElement(type); if (className) { el.classList.add(cssClass(className)); diff --git a/public/app/core/components/json_explorer/json_explorer.ts b/public/app/core/components/json_explorer/json_explorer.ts index 01c0099c178..9cc1b53bc82 100644 --- a/public/app/core/components/json_explorer/json_explorer.ts +++ b/public/app/core/components/json_explorer/json_explorer.ts @@ -1,16 +1,9 @@ // Based on work https://github.com/mohsen1/json-formatter-js // Licence MIT, Copyright (c) 2015 Mohsen Azimi -import { - isObject, - getObjectName, - getType, - getValuePreview, - cssClass, - createElement -} from "./helpers"; +import { isObject, getObjectName, getType, getValuePreview, cssClass, createElement } from './helpers'; -import _ from "lodash"; +import _ from 'lodash'; const DATE_STRING_REGEX = /(^\d{1,4}[\.|\\/|-]\d{1,2}[\.|\\/|-]\d{1,4})(\s*(?:0?[1-9]:[0-5]|1(?=[012])\d:[0-5])\d\s*[ap]m)?$/; const PARTIAL_DATE_REGEX = /\d{2}:\d{2}:\d{2} GMT-\d{4}/; @@ -35,7 +28,7 @@ export interface JsonExplorerConfig { const _defaultConfig: JsonExplorerConfig = { animateOpen: true, animateClose: true, - theme: null + theme: null, }; /** @@ -111,10 +104,8 @@ export class JsonExplorer { */ private get isDate(): boolean { return ( - this.type === "string" && - (DATE_STRING_REGEX.test(this.json) || - JSON_DATE_REGEX.test(this.json) || - PARTIAL_DATE_REGEX.test(this.json)) + this.type === 'string' && + (DATE_STRING_REGEX.test(this.json) || JSON_DATE_REGEX.test(this.json) || PARTIAL_DATE_REGEX.test(this.json)) ); } @@ -122,7 +113,7 @@ export class JsonExplorer { * is this a URL string? */ private get isUrl(): boolean { - return this.type === "string" && this.json.indexOf("http") === 0; + return this.type === 'string' && this.json.indexOf('http') === 0; } /* @@ -151,9 +142,7 @@ export class JsonExplorer { * is this an empty object or array? */ private get isEmpty(): boolean { - return ( - this.isEmptyObject || (this.keys && !this.keys.length && this.isArray) - ); + return this.isEmptyObject || (this.keys && !this.keys.length && this.isArray); } /* @@ -161,7 +150,7 @@ export class JsonExplorer { * This means that the formatter was called as a sub formatter of a parent formatter */ private get hasKey(): boolean { - return typeof this.key !== "undefined"; + return typeof this.key !== 'undefined'; } /* @@ -204,7 +193,7 @@ export class JsonExplorer { } else { this.removeChildren(this.config.animateClose); } - this.element.classList.toggle(cssClass("open")); + this.element.classList.toggle(cssClass('open')); } } @@ -225,44 +214,36 @@ export class JsonExplorer { this.removeChildren(false); if (depth === 0) { - this.element.classList.remove(cssClass("open")); + this.element.classList.remove(cssClass('open')); } else { this.appendChildren(this.config.animateOpen); - this.element.classList.add(cssClass("open")); + this.element.classList.add(cssClass('open')); } } } isNumberArray() { - return ( - this.json.length > 0 && - this.json.length < 4 && - (_.isNumber(this.json[0]) || _.isNumber(this.json[1])) - ); + return this.json.length > 0 && this.json.length < 4 && (_.isNumber(this.json[0]) || _.isNumber(this.json[1])); } renderArray() { - const arrayWrapperSpan = createElement("span"); - arrayWrapperSpan.appendChild(createElement("span", "bracket", "[")); + const arrayWrapperSpan = createElement('span'); + arrayWrapperSpan.appendChild(createElement('span', 'bracket', '[')); // some pretty handling of number arrays if (this.isNumberArray()) { this.json.forEach((val, index) => { if (index > 0) { - arrayWrapperSpan.appendChild( - createElement("span", "array-comma", ",") - ); + arrayWrapperSpan.appendChild(createElement('span', 'array-comma', ',')); } - arrayWrapperSpan.appendChild(createElement("span", "number", val)); + arrayWrapperSpan.appendChild(createElement('span', 'number', val)); }); this.skipChildren = true; } else { - arrayWrapperSpan.appendChild( - createElement("span", "number", this.json.length) - ); + arrayWrapperSpan.appendChild(createElement('span', 'number', this.json.length)); } - arrayWrapperSpan.appendChild(createElement("span", "bracket", "]")); + arrayWrapperSpan.appendChild(createElement('span', 'bracket', ']')); return arrayWrapperSpan; } @@ -273,11 +254,11 @@ export class JsonExplorer { */ render(skipRoot = false): HTMLDivElement { // construct the root element and assign it to this.element - this.element = createElement("div", "row"); + this.element = createElement('div', 'row'); // construct the toggler link - const togglerLink = createElement("a", "toggler-link"); - const togglerIcon = createElement("span", "toggler"); + const togglerLink = createElement('a', 'toggler-link'); + const togglerIcon = createElement('span', 'toggler'); // if this is an object we need a wrapper span (toggler) if (this.isObject) { @@ -286,23 +267,19 @@ export class JsonExplorer { // if this is child of a parent formatter we need to append the key if (this.hasKey) { - togglerLink.appendChild(createElement("span", "key", `${this.key}:`)); + togglerLink.appendChild(createElement('span', 'key', `${this.key}:`)); } // Value for objects and arrays if (this.isObject) { // construct the value holder element - const value = createElement("span", "value"); + const value = createElement('span', 'value'); // we need a wrapper span for objects - const objectWrapperSpan = createElement("span"); + const objectWrapperSpan = createElement('span'); // get constructor name and append it to wrapper span - var constructorName = createElement( - "span", - "constructor-name", - this.constructorName - ); + var constructorName = createElement('span', 'constructor-name', this.constructorName); objectWrapperSpan.appendChild(constructorName); // if it's an array append the array specific elements like brackets and length @@ -317,16 +294,16 @@ export class JsonExplorer { // Primitive values } else { // make a value holder element - const value = this.isUrl ? createElement("a") : createElement("span"); + const value = this.isUrl ? createElement('a') : createElement('span'); // add type and other type related CSS classes value.classList.add(cssClass(this.type)); if (this.isDate) { - value.classList.add(cssClass("date")); + value.classList.add(cssClass('date')); } if (this.isUrl) { - value.classList.add(cssClass("url")); - value.setAttribute("href", this.json); + value.classList.add(cssClass('url')); + value.setAttribute('href', this.json); } // Append value content to value element @@ -338,17 +315,17 @@ export class JsonExplorer { } // construct a children element - const children = createElement("div", "children"); + const children = createElement('div', 'children'); // set CSS classes for children if (this.isObject) { - children.classList.add(cssClass("object")); + children.classList.add(cssClass('object')); } if (this.isArray) { - children.classList.add(cssClass("array")); + children.classList.add(cssClass('array')); } if (this.isEmpty) { - children.classList.add(cssClass("empty")); + children.classList.add(cssClass('empty')); } // set CSS classes for root element @@ -356,7 +333,7 @@ export class JsonExplorer { this.element.classList.add(cssClass(this.config.theme)); } if (this.isOpen) { - this.element.classList.add(cssClass("open")); + this.element.classList.add(cssClass('open')); } // append toggler and children elements to root element @@ -378,7 +355,7 @@ export class JsonExplorer { // add event listener for toggling if (this.isObject) { - togglerLink.addEventListener("click", this.toggleOpen.bind(this)); + togglerLink.addEventListener('click', this.toggleOpen.bind(this)); } return this.element as HTMLDivElement; @@ -389,7 +366,7 @@ export class JsonExplorer { * Animated option is used when user triggers this via a click */ appendChildren(animated = false) { - const children = this.element.querySelector(`div.${cssClass("children")}`); + const children = this.element.querySelector(`div.${cssClass('children')}`); if (!children || this.isEmpty) { return; @@ -399,12 +376,7 @@ export class JsonExplorer { let index = 0; const addAChild = () => { const key = this.keys[index]; - const formatter = new JsonExplorer( - this.json[key], - this.open - 1, - this.config, - key - ); + const formatter = new JsonExplorer(this.json[key], this.open - 1, this.config, key); children.appendChild(formatter.render()); index += 1; @@ -421,12 +393,7 @@ export class JsonExplorer { requestAnimationFrame(addAChild); } else { this.keys.forEach(key => { - const formatter = new JsonExplorer( - this.json[key], - this.open - 1, - this.config, - key - ); + const formatter = new JsonExplorer(this.json[key], this.open - 1, this.config, key); children.appendChild(formatter.render()); }); } @@ -437,9 +404,7 @@ export class JsonExplorer { * Animated option is used when user triggers this via a click */ removeChildren(animated = false) { - const childrenElement = this.element.querySelector( - `div.${cssClass("children")}` - ) as HTMLDivElement; + const childrenElement = this.element.querySelector(`div.${cssClass('children')}`) as HTMLDivElement; if (animated) { let childrenRemoved = 0; @@ -457,7 +422,7 @@ export class JsonExplorer { requestAnimationFrame(removeAChild); } else { if (childrenElement) { - childrenElement.innerHTML = ""; + childrenElement.innerHTML = ''; } } } diff --git a/public/app/core/components/jsontree/jsontree.ts b/public/app/core/components/jsontree/jsontree.ts index 6fac66d32f8..e127d7b14a9 100644 --- a/public/app/core/components/jsontree/jsontree.ts +++ b/public/app/core/components/jsontree/jsontree.ts @@ -1,23 +1,23 @@ -import coreModule from "app/core/core_module"; -import { JsonExplorer } from "../json_explorer/json_explorer"; +import coreModule from 'app/core/core_module'; +import { JsonExplorer } from '../json_explorer/json_explorer'; -coreModule.directive("jsonTree", [ +coreModule.directive('jsonTree', [ function jsonTreeDirective() { return { - restrict: "E", + restrict: 'E', scope: { - object: "=", - startExpanded: "@", - rootName: "@" + object: '=', + startExpanded: '@', + rootName: '@', }, link: function(scope, elem) { var jsonExp = new JsonExplorer(scope.object, 3, { - animateOpen: true + animateOpen: true, }); const html = jsonExp.render(true); elem.html(html); - } + }, }; - } + }, ]); diff --git a/public/app/core/components/layout_selector/layout_selector.ts b/public/app/core/components/layout_selector/layout_selector.ts index d5dd105ddb9..91a3afea250 100644 --- a/public/app/core/components/layout_selector/layout_selector.ts +++ b/public/app/core/components/layout_selector/layout_selector.ts @@ -1,5 +1,5 @@ -import store from "app/core/store"; -import coreModule from "app/core/core_module"; +import store from 'app/core/store'; +import coreModule from 'app/core/core_module'; var template = `
@@ -17,56 +17,56 @@ export class LayoutSelectorCtrl { /** @ngInject **/ constructor(private $rootScope) { - this.mode = store.get("grafana.list.layout.mode") || "grid"; + this.mode = store.get('grafana.list.layout.mode') || 'grid'; } listView() { - this.mode = "list"; - store.set("grafana.list.layout.mode", "list"); - this.$rootScope.appEvent("layout-mode-changed", "list"); + this.mode = 'list'; + store.set('grafana.list.layout.mode', 'list'); + this.$rootScope.appEvent('layout-mode-changed', 'list'); } gridView() { - this.mode = "grid"; - store.set("grafana.list.layout.mode", "grid"); - this.$rootScope.appEvent("layout-mode-changed", "grid"); + this.mode = 'grid'; + store.set('grafana.list.layout.mode', 'grid'); + this.$rootScope.appEvent('layout-mode-changed', 'grid'); } } /** @ngInject **/ export function layoutSelector() { return { - restrict: "E", + restrict: 'E', controller: LayoutSelectorCtrl, bindToController: true, - controllerAs: "ctrl", + controllerAs: 'ctrl', scope: {}, - template: template + template: template, }; } /** @ngInject **/ export function layoutMode($rootScope) { return { - restrict: "A", + restrict: 'A', scope: {}, link: function(scope, elem) { - var layout = store.get("grafana.list.layout.mode") || "grid"; - var className = "card-list-layout-" + layout; + var layout = store.get('grafana.list.layout.mode') || 'grid'; + var className = 'card-list-layout-' + layout; elem.addClass(className); $rootScope.onAppEvent( - "layout-mode-changed", + 'layout-mode-changed', (evt, newLayout) => { elem.removeClass(className); - className = "card-list-layout-" + newLayout; + className = 'card-list-layout-' + newLayout; elem.addClass(className); }, scope ); - } + }, }; } -coreModule.directive("layoutSelector", layoutSelector); -coreModule.directive("layoutMode", layoutMode); +coreModule.directive('layoutSelector', layoutSelector); +coreModule.directive('layoutMode', layoutMode); diff --git a/public/app/core/components/manage_dashboards/manage_dashboards.html b/public/app/core/components/manage_dashboards/manage_dashboards.html index f8c96a78b2e..cb2cec28bab 100644 --- a/public/app/core/components/manage_dashboards/manage_dashboards.html +++ b/public/app/core/components/manage_dashboards/manage_dashboards.html @@ -5,7 +5,7 @@
- + Dashboard @@ -60,20 +60,22 @@ switch-class="gf-form-switch--transparent gf-form-switch--search-result-filter-row__checkbox" />
- +
+ +