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/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/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/core/routes/routes.ts b/public/app/core/routes/routes.ts index 2e4950cac6f..b8b157b5ad0 100644 --- a/public/app/core/routes/routes.ts +++ b/public/app/core/routes/routes.ts @@ -145,6 +145,12 @@ function setupAngularRoutes($routeProvider, $locationProvider) { controllerAs: 'ctrl', resolve: loadOrgBundle, }) + .when('/org/teams/new', { + templateUrl: 'public/app/features/org/partials/create_team.html', + controller: 'CreateTeamCtrl', + controllerAs: 'ctrl', + resolve: loadOrgBundle, + }) .when('/org/teams/edit/:id', { templateUrl: 'public/app/features/org/partials/team_details.html', controller: 'TeamDetailsCtrl', diff --git a/public/app/features/org/all.ts b/public/app/features/org/all.ts index 11a362d728a..50b81356c70 100644 --- a/public/app/features/org/all.ts +++ b/public/app/features/org/all.ts @@ -1,13 +1,13 @@ -import './org_users_ctrl'; -import './profile_ctrl'; -import './org_users_ctrl'; -import './select_org_ctrl'; -import './change_password_ctrl'; -import './new_org_ctrl'; -import './user_invite_ctrl'; -import './teams_ctrl'; -import './team_details_ctrl'; -import './create_team_modal'; -import './org_api_keys_ctrl'; -import './org_details_ctrl'; -import './prefs_control'; +import "./org_users_ctrl"; +import "./profile_ctrl"; +import "./org_users_ctrl"; +import "./select_org_ctrl"; +import "./change_password_ctrl"; +import "./new_org_ctrl"; +import "./user_invite_ctrl"; +import "./teams_ctrl"; +import "./team_details_ctrl"; +import "./create_team_ctrl"; +import "./org_api_keys_ctrl"; +import "./org_details_ctrl"; +import "./prefs_control"; diff --git a/public/app/features/org/create_team_ctrl.ts b/public/app/features/org/create_team_ctrl.ts new file mode 100644 index 00000000000..08184076a1c --- /dev/null +++ b/public/app/features/org/create_team_ctrl.ts @@ -0,0 +1,26 @@ +import coreModule from "app/core/core_module"; + +export default class CreateTeamCtrl { + name: string; + email: string; + navModel: any; + + /** @ngInject **/ + constructor(private backendSrv, private $location, navModelSrv) { + this.navModel = navModelSrv.getNav("cfg", "teams", 0); + } + + create() { + const payload = { + name: this.name, + email: this.email + }; + this.backendSrv.post("/api/teams", payload).then(result => { + if (result.teamId) { + this.$location.path("/org/teams/edit/" + result.teamId); + } + }); + } +} + +coreModule.controller("CreateTeamCtrl", CreateTeamCtrl); diff --git a/public/app/features/org/create_team_modal.ts b/public/app/features/org/create_team_modal.ts deleted file mode 100644 index acac782fadc..00000000000 --- a/public/app/features/org/create_team_modal.ts +++ /dev/null @@ -1,36 +0,0 @@ -/// - -import coreModule from 'app/core/core_module'; -import appEvents from 'app/core/app_events'; - -export class CreateTeamCtrl { - teamName = ''; - - /** @ngInject */ - constructor(private backendSrv, private $location) {} - - createTeam() { - this.backendSrv.post('/api/teams', { name: this.teamName }).then(result => { - if (result.teamId) { - this.$location.path('/org/teams/edit/' + result.teamId); - } - this.dismiss(); - }); - } - - dismiss() { - appEvents.emit('hide-modal'); - } -} - -export function createTeamModal() { - return { - restrict: 'E', - templateUrl: 'public/app/features/org/partials/create_team.html', - controller: CreateTeamCtrl, - bindToController: true, - controllerAs: 'ctrl', - }; -} - -coreModule.directive('createTeamModal', createTeamModal); diff --git a/public/app/features/org/partials/create_team.html b/public/app/features/org/partials/create_team.html index f45ad0b33a0..9f606408164 100644 --- a/public/app/features/org/partials/create_team.html +++ b/public/app/features/org/partials/create_team.html @@ -1,27 +1,26 @@ -