org/members: display 2FA members states + optimize sql requests (#7621)
* org/members: display 2FA state * fix comment typo * lay down UserList bases * add basic test for previous methods * add comment for UserList type * add valid two-fa account * test new UserList methods * optimize MembersIsPublic by side loading info on GetMembers + fix integrations tests * respect fmt rules * use map for data * Optimize GetTwoFaStatus * rewrite by using existing sub func * Optimize IsUserOrgOwner * remove un-used code * tests: cover empty org + fix import order * tests: add ErrTeamNotExist path * tests: fix wrong expected result
This commit is contained in:
parent
3566d2c860
commit
76408d50fb
13 changed files with 346 additions and 25 deletions
|
@ -39,3 +39,9 @@
|
||||||
uid: 20
|
uid: 20
|
||||||
org_id: 19
|
org_id: 19
|
||||||
is_public: true
|
is_public: true
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 8
|
||||||
|
uid: 24
|
||||||
|
org_id: 25
|
||||||
|
is_public: true
|
||||||
|
|
|
@ -78,3 +78,12 @@
|
||||||
authorize: 1 # read
|
authorize: 1 # read
|
||||||
num_repos: 1
|
num_repos: 1
|
||||||
num_members: 1
|
num_members: 1
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 10
|
||||||
|
org_id: 25
|
||||||
|
lower_name: notowners
|
||||||
|
name: NotOwners
|
||||||
|
authorize: 1 # owner
|
||||||
|
num_repos: 0
|
||||||
|
num_members: 1
|
||||||
|
|
|
@ -63,3 +63,9 @@
|
||||||
org_id: 17
|
org_id: 17
|
||||||
team_id: 9
|
team_id: 9
|
||||||
uid: 20
|
uid: 20
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 12
|
||||||
|
org_id: 25
|
||||||
|
team_id: 10
|
||||||
|
uid: 24
|
||||||
|
|
9
models/fixtures/two_factor.yml
Normal file
9
models/fixtures/two_factor.yml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
-
|
||||||
|
id: 1
|
||||||
|
uid: 24
|
||||||
|
secret: KlDporn6Ile4vFcKI8z7Z6sqK1Scj2Qp0ovtUzCZO6jVbRW2lAoT7UDxDPtrab8d2B9zKOocBRdBJnS8orsrUNrsyETY+jJHb79M82uZRioKbRUz15sfOpmJmEzkFeSg6S4LicUBQos=
|
||||||
|
scratch_salt: Qb5bq2DyR2
|
||||||
|
scratch_hash: 068eb9b8746e0bcfe332fac4457693df1bda55800eb0f6894d14ebb736ae6a24e0fc8fc5333c19f57f81599788f0b8e51ec1
|
||||||
|
last_used_passcode:
|
||||||
|
created_unix: 1564253724
|
||||||
|
updated_unix: 1564253724
|
|
@ -366,3 +366,38 @@
|
||||||
num_members: 0
|
num_members: 0
|
||||||
num_teams: 0
|
num_teams: 0
|
||||||
visibility: 2
|
visibility: 2
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 24
|
||||||
|
lower_name: user24
|
||||||
|
name: user24
|
||||||
|
full_name: "user24"
|
||||||
|
email: user24@example.com
|
||||||
|
keep_email_private: true
|
||||||
|
passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a # password
|
||||||
|
type: 0 # individual
|
||||||
|
salt: ZogKvWdyEx
|
||||||
|
is_admin: false
|
||||||
|
avatar: avatar24
|
||||||
|
avatar_email: user24@example.com
|
||||||
|
num_repos: 0
|
||||||
|
num_stars: 0
|
||||||
|
num_followers: 0
|
||||||
|
num_following: 0
|
||||||
|
is_active: true
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 25
|
||||||
|
lower_name: org25
|
||||||
|
name: org25
|
||||||
|
full_name: "org25"
|
||||||
|
email: org25@example.com
|
||||||
|
passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a # password
|
||||||
|
type: 1 # organization
|
||||||
|
salt: ZogKvWdyEx
|
||||||
|
is_admin: false
|
||||||
|
avatar: avatar25
|
||||||
|
avatar_email: org25@example.com
|
||||||
|
num_repos: 0
|
||||||
|
num_members: 1
|
||||||
|
num_teams: 1
|
||||||
|
|
|
@ -72,9 +72,12 @@ func (org *User) GetMembers() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var ids = make([]int64, len(ous))
|
var ids = make([]int64, len(ous))
|
||||||
|
var idsIsPublic = make(map[int64]bool, len(ous))
|
||||||
for i, ou := range ous {
|
for i, ou := range ous {
|
||||||
ids[i] = ou.UID
|
ids[i] = ou.UID
|
||||||
|
idsIsPublic[ou.UID] = ou.IsPublic
|
||||||
}
|
}
|
||||||
|
org.MembersIsPublic = idsIsPublic
|
||||||
org.Members, err = GetUsersByIDs(ids)
|
org.Members, err = GetUsersByIDs(ids)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -298,15 +301,13 @@ type OrgUser struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func isOrganizationOwner(e Engine, orgID, uid int64) (bool, error) {
|
func isOrganizationOwner(e Engine, orgID, uid int64) (bool, error) {
|
||||||
ownerTeam := &Team{
|
ownerTeam, err := getOwnerTeam(e, orgID)
|
||||||
OrgID: orgID,
|
if err != nil {
|
||||||
Name: ownerTeamName,
|
if err == ErrTeamNotExist {
|
||||||
}
|
log.Error("Organization does not have owner team: %d", orgID)
|
||||||
if has, err := e.Get(ownerTeam); err != nil {
|
return false, nil
|
||||||
|
}
|
||||||
return false, err
|
return false, err
|
||||||
} else if !has {
|
|
||||||
log.Error("Organization does not have owner team: %d", orgID)
|
|
||||||
return false, nil
|
|
||||||
}
|
}
|
||||||
return isTeamMember(e, orgID, ownerTeam.ID, uid)
|
return isTeamMember(e, orgID, ownerTeam.ID, uid)
|
||||||
}
|
}
|
||||||
|
|
|
@ -362,6 +362,11 @@ func GetTeam(orgID int64, name string) (*Team, error) {
|
||||||
return getTeam(x, orgID, name)
|
return getTeam(x, orgID, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getOwnerTeam returns team by given team name and organization.
|
||||||
|
func getOwnerTeam(e Engine, orgID int64) (*Team, error) {
|
||||||
|
return getTeam(e, orgID, ownerTeamName)
|
||||||
|
}
|
||||||
|
|
||||||
func getTeamByID(e Engine, teamID int64) (*Team, error) {
|
func getTeamByID(e Engine, teamID int64) (*Team, error) {
|
||||||
t := new(Team)
|
t := new(Team)
|
||||||
has, err := e.ID(teamID).Get(t)
|
has, err := e.ID(teamID).Get(t)
|
||||||
|
|
|
@ -139,11 +139,12 @@ type User struct {
|
||||||
NumRepos int
|
NumRepos int
|
||||||
|
|
||||||
// For organization
|
// For organization
|
||||||
NumTeams int
|
NumTeams int
|
||||||
NumMembers int
|
NumMembers int
|
||||||
Teams []*Team `xorm:"-"`
|
Teams []*Team `xorm:"-"`
|
||||||
Members []*User `xorm:"-"`
|
Members UserList `xorm:"-"`
|
||||||
Visibility structs.VisibleType `xorm:"NOT NULL DEFAULT 0"`
|
MembersIsPublic map[int64]bool `xorm:"-"`
|
||||||
|
Visibility structs.VisibleType `xorm:"NOT NULL DEFAULT 0"`
|
||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"`
|
DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"`
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -15,6 +16,58 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestUserIsPublicMember(t *testing.T) {
|
||||||
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
|
||||||
|
tt := []struct {
|
||||||
|
uid int64
|
||||||
|
orgid int64
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{2, 3, true},
|
||||||
|
{4, 3, false},
|
||||||
|
{5, 6, true},
|
||||||
|
{5, 7, false},
|
||||||
|
}
|
||||||
|
for _, v := range tt {
|
||||||
|
t.Run(fmt.Sprintf("UserId%dIsPublicMemberOf%d", v.uid, v.orgid), func(t *testing.T) {
|
||||||
|
testUserIsPublicMember(t, v.uid, v.orgid, v.expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUserIsPublicMember(t *testing.T, uid int64, orgID int64, expected bool) {
|
||||||
|
user, err := GetUserByID(uid)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expected, user.IsPublicMember(orgID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsUserOrgOwner(t *testing.T) {
|
||||||
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
|
||||||
|
tt := []struct {
|
||||||
|
uid int64
|
||||||
|
orgid int64
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{2, 3, true},
|
||||||
|
{4, 3, false},
|
||||||
|
{5, 6, true},
|
||||||
|
{5, 7, true},
|
||||||
|
}
|
||||||
|
for _, v := range tt {
|
||||||
|
t.Run(fmt.Sprintf("UserId%dIsOrgOwnerOf%d", v.uid, v.orgid), func(t *testing.T) {
|
||||||
|
testIsUserOrgOwner(t, v.uid, v.orgid, v.expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testIsUserOrgOwner(t *testing.T, uid int64, orgID int64, expected bool) {
|
||||||
|
user, err := GetUserByID(uid)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expected, user.IsUserOrgOwner(orgID))
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetUserEmailsByNames(t *testing.T) {
|
func TestGetUserEmailsByNames(t *testing.T) {
|
||||||
assert.NoError(t, PrepareTestDatabase())
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
|
||||||
|
@ -83,7 +136,7 @@ func TestSearchUsers(t *testing.T) {
|
||||||
[]int64{7, 17})
|
[]int64{7, 17})
|
||||||
|
|
||||||
testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 3, PageSize: 2},
|
testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 3, PageSize: 2},
|
||||||
[]int64{19})
|
[]int64{19, 25})
|
||||||
|
|
||||||
testOrgSuccess(&SearchUserOptions{Page: 4, PageSize: 2},
|
testOrgSuccess(&SearchUserOptions{Page: 4, PageSize: 2},
|
||||||
[]int64{})
|
[]int64{})
|
||||||
|
@ -95,13 +148,13 @@ func TestSearchUsers(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1},
|
testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1},
|
||||||
[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21})
|
[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24})
|
||||||
|
|
||||||
testUserSuccess(&SearchUserOptions{Page: 1, IsActive: util.OptionalBoolFalse},
|
testUserSuccess(&SearchUserOptions{Page: 1, IsActive: util.OptionalBoolFalse},
|
||||||
[]int64{9})
|
[]int64{9})
|
||||||
|
|
||||||
testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue},
|
testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue},
|
||||||
[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21})
|
[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24})
|
||||||
|
|
||||||
testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue},
|
testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue},
|
||||||
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
|
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
|
||||||
|
|
95
models/userlist.go
Normal file
95
models/userlist.go
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
//UserList is a list of user.
|
||||||
|
// This type provide valuable methods to retrieve information for a group of users efficiently.
|
||||||
|
type UserList []*User
|
||||||
|
|
||||||
|
func (users UserList) getUserIDs() []int64 {
|
||||||
|
userIDs := make([]int64, len(users))
|
||||||
|
for _, user := range users {
|
||||||
|
userIDs = append(userIDs, user.ID) //Considering that user id are unique in the list
|
||||||
|
}
|
||||||
|
return userIDs
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUserOrgOwner returns true if user is in the owner team of given organization.
|
||||||
|
func (users UserList) IsUserOrgOwner(orgID int64) map[int64]bool {
|
||||||
|
results := make(map[int64]bool, len(users))
|
||||||
|
for _, user := range users {
|
||||||
|
results[user.ID] = false //Set default to false
|
||||||
|
}
|
||||||
|
ownerMaps, err := users.loadOrganizationOwners(x, orgID)
|
||||||
|
if err == nil {
|
||||||
|
for _, owner := range ownerMaps {
|
||||||
|
results[owner.UID] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func (users UserList) loadOrganizationOwners(e Engine, orgID int64) (map[int64]*TeamUser, error) {
|
||||||
|
if len(users) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
ownerTeam, err := getOwnerTeam(e, orgID)
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrTeamNotExist {
|
||||||
|
log.Error("Organization does not have owner team: %d", orgID)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userIDs := users.getUserIDs()
|
||||||
|
ownerMaps := make(map[int64]*TeamUser)
|
||||||
|
err = e.In("uid", userIDs).
|
||||||
|
And("org_id=?", orgID).
|
||||||
|
And("team_id=?", ownerTeam.ID).
|
||||||
|
Find(&ownerMaps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("find team users: %v", err)
|
||||||
|
}
|
||||||
|
return ownerMaps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTwoFaStatus return state of 2FA enrollement
|
||||||
|
func (users UserList) GetTwoFaStatus() map[int64]bool {
|
||||||
|
results := make(map[int64]bool, len(users))
|
||||||
|
for _, user := range users {
|
||||||
|
results[user.ID] = false //Set default to false
|
||||||
|
}
|
||||||
|
tokenMaps, err := users.loadTwoFactorStatus(x)
|
||||||
|
if err == nil {
|
||||||
|
for _, token := range tokenMaps {
|
||||||
|
results[token.UID] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func (users UserList) loadTwoFactorStatus(e Engine) (map[int64]*TwoFactor, error) {
|
||||||
|
if len(users) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
userIDs := users.getUserIDs()
|
||||||
|
tokenMaps := make(map[int64]*TwoFactor, len(userIDs))
|
||||||
|
err := e.
|
||||||
|
In("uid", userIDs).
|
||||||
|
Find(&tokenMaps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("find two factor: %v", err)
|
||||||
|
}
|
||||||
|
return tokenMaps, nil
|
||||||
|
}
|
90
models/userlist_test.go
Normal file
90
models/userlist_test.go
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUserListIsPublicMember(t *testing.T) {
|
||||||
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
tt := []struct {
|
||||||
|
orgid int64
|
||||||
|
expected map[int64]bool
|
||||||
|
}{
|
||||||
|
{3, map[int64]bool{2: true, 4: false}},
|
||||||
|
{6, map[int64]bool{5: true}},
|
||||||
|
{7, map[int64]bool{5: false}},
|
||||||
|
{25, map[int64]bool{24: true}},
|
||||||
|
{22, map[int64]bool{}},
|
||||||
|
}
|
||||||
|
for _, v := range tt {
|
||||||
|
t.Run(fmt.Sprintf("IsPublicMemberOfOrdIg%d", v.orgid), func(t *testing.T) {
|
||||||
|
testUserListIsPublicMember(t, v.orgid, v.expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func testUserListIsPublicMember(t *testing.T, orgID int64, expected map[int64]bool) {
|
||||||
|
org, err := GetUserByID(orgID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, org.GetMembers())
|
||||||
|
assert.Equal(t, expected, org.MembersIsPublic)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserListIsUserOrgOwner(t *testing.T) {
|
||||||
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
tt := []struct {
|
||||||
|
orgid int64
|
||||||
|
expected map[int64]bool
|
||||||
|
}{
|
||||||
|
{3, map[int64]bool{2: true, 4: false}},
|
||||||
|
{6, map[int64]bool{5: true}},
|
||||||
|
{7, map[int64]bool{5: true}},
|
||||||
|
{25, map[int64]bool{24: false}}, // ErrTeamNotExist
|
||||||
|
{22, map[int64]bool{}}, // No member
|
||||||
|
}
|
||||||
|
for _, v := range tt {
|
||||||
|
t.Run(fmt.Sprintf("IsUserOrgOwnerOfOrdIg%d", v.orgid), func(t *testing.T) {
|
||||||
|
testUserListIsUserOrgOwner(t, v.orgid, v.expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUserListIsUserOrgOwner(t *testing.T, orgID int64, expected map[int64]bool) {
|
||||||
|
org, err := GetUserByID(orgID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, org.GetMembers())
|
||||||
|
assert.Equal(t, expected, org.Members.IsUserOrgOwner(orgID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserListIsTwoFaEnrolled(t *testing.T) {
|
||||||
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
tt := []struct {
|
||||||
|
orgid int64
|
||||||
|
expected map[int64]bool
|
||||||
|
}{
|
||||||
|
{3, map[int64]bool{2: false, 4: false}},
|
||||||
|
{6, map[int64]bool{5: false}},
|
||||||
|
{7, map[int64]bool{5: false}},
|
||||||
|
{25, map[int64]bool{24: true}},
|
||||||
|
{22, map[int64]bool{}},
|
||||||
|
}
|
||||||
|
for _, v := range tt {
|
||||||
|
t.Run(fmt.Sprintf("IsTwoFaEnrolledOfOrdIg%d", v.orgid), func(t *testing.T) {
|
||||||
|
testUserListIsTwoFaEnrolled(t, v.orgid, v.expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUserListIsTwoFaEnrolled(t *testing.T, orgID int64, expected map[int64]bool) {
|
||||||
|
org, err := GetUserByID(orgID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, org.GetMembers())
|
||||||
|
assert.Equal(t, expected, org.Members.GetTwoFaStatus())
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ const (
|
||||||
tplMembers base.TplName = "org/member/members"
|
tplMembers base.TplName = "org/member/members"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Members render orgnization users page
|
// Members render organization users page
|
||||||
func Members(ctx *context.Context) {
|
func Members(ctx *context.Context) {
|
||||||
org := ctx.Org.Organization
|
org := ctx.Org.Organization
|
||||||
ctx.Data["Title"] = org.FullName
|
ctx.Data["Title"] = org.FullName
|
||||||
|
@ -30,11 +30,14 @@ func Members(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Data["Members"] = org.Members
|
ctx.Data["Members"] = org.Members
|
||||||
|
ctx.Data["MembersIsPublicMember"] = org.MembersIsPublic
|
||||||
|
ctx.Data["MembersIsUserOrgOwner"] = org.Members.IsUserOrgOwner(org.ID)
|
||||||
|
ctx.Data["MembersTwoFaStatus"] = org.Members.GetTwoFaStatus()
|
||||||
|
|
||||||
ctx.HTML(200, tplMembers)
|
ctx.HTML(200, tplMembers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MembersAction response for operation to a member of orgnization
|
// MembersAction response for operation to a member of organization
|
||||||
func MembersAction(ctx *context.Context) {
|
func MembersAction(ctx *context.Context) {
|
||||||
uid := com.StrTo(ctx.Query("uid")).MustInt64()
|
uid := com.StrTo(ctx.Query("uid")).MustInt64()
|
||||||
if uid == 0 {
|
if uid == 0 {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
|
||||||
<div class="list">
|
<div class="list">
|
||||||
{{range .Members}}
|
{{ range .Members}}
|
||||||
<div class="item ui grid">
|
<div class="item ui grid">
|
||||||
<div class="ui one wide column">
|
<div class="ui one wide column">
|
||||||
<img class="ui avatar" src="{{.SizedRelAvatarLink 48}}">
|
<img class="ui avatar" src="{{.SizedRelAvatarLink 48}}">
|
||||||
|
@ -14,12 +14,12 @@
|
||||||
<div class="meta"><a href="{{.HomeLink}}">{{.Name}}</a></div>
|
<div class="meta"><a href="{{.HomeLink}}">{{.Name}}</a></div>
|
||||||
<div class="meta">{{.FullName}}</div>
|
<div class="meta">{{.FullName}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui five wide column center">
|
<div class="ui four wide column center">
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
{{$.i18n.Tr "org.members.membership_visibility"}}
|
{{$.i18n.Tr "org.members.membership_visibility"}}
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
{{ $isPublic := .IsPublicMember $.Org.ID}}
|
{{ $isPublic := index $.MembersIsPublicMember .ID}}
|
||||||
{{if $isPublic}}
|
{{if $isPublic}}
|
||||||
<strong>{{$.i18n.Tr "org.members.public"}}</strong>
|
<strong>{{$.i18n.Tr "org.members.public"}}</strong>
|
||||||
{{if or (eq $.SignedUser.ID .ID) $.IsOrganizationOwner}}(<a href="{{$.OrgLink}}/members/action/private?uid={{.ID}}">{{$.i18n.Tr "org.members.public_helper"}}</a>){{end}}
|
{{if or (eq $.SignedUser.ID .ID) $.IsOrganizationOwner}}(<a href="{{$.OrgLink}}/members/action/private?uid={{.ID}}">{{$.i18n.Tr "org.members.public_helper"}}</a>){{end}}
|
||||||
|
@ -34,7 +34,15 @@
|
||||||
{{$.i18n.Tr "org.members.member_role"}}
|
{{$.i18n.Tr "org.members.member_role"}}
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<strong>{{if .IsUserOrgOwner $.Org.ID}}<span class="octicon octicon-shield"></span> {{$.i18n.Tr "org.members.owner"}}{{else}}{{$.i18n.Tr "org.members.member"}}{{end}}</strong>
|
<strong>{{if index $.MembersIsUserOrgOwner .ID}}<span class="octicon octicon-shield"></span> {{$.i18n.Tr "org.members.owner"}}{{else}}{{$.i18n.Tr "org.members.member"}}{{end}}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui one wide column center">
|
||||||
|
<div class="meta">
|
||||||
|
2FA
|
||||||
|
</div>
|
||||||
|
<div class="meta">
|
||||||
|
<strong><span class="octicon {{if index $.MembersTwoFaStatus .ID}}octicon-check text green{{else}}octicon-x{{end}}"></span></strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui four wide column">
|
<div class="ui four wide column">
|
||||||
|
|
Reference in a new issue