diff --git a/.drone.yml b/.drone.yml index dedf4d7410..44e4b225a2 100644 --- a/.drone.yml +++ b/.drone.yml @@ -66,6 +66,7 @@ steps: image: golang:1.11 # this step is kept as the lowest version of golang that we support environment: GO111MODULE: on + GOPROXY: off commands: - go build -mod=vendor -o gitea_no_gcc # test if build succeeds without the sqlite tag @@ -74,6 +75,7 @@ steps: image: golang:1.12 environment: GO111MODULE: on + GOPROXY: off GOOS: linux GOARCH: 386 commands: @@ -92,6 +94,7 @@ steps: - make test-vendor - make build environment: + GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not TAGS: bindata sqlite sqlite_unlock_notify - name: unit-test @@ -100,6 +103,7 @@ steps: commands: - make unit-test-coverage environment: + GOPROXY: off TAGS: bindata sqlite sqlite_unlock_notify depends_on: - build @@ -116,6 +120,7 @@ steps: commands: - make test environment: + GOPROXY: off TAGS: bindata sqlite sqlite_unlock_notify depends_on: - build @@ -143,6 +148,7 @@ steps: commands: - make test environment: + GOPROXY: off TAGS: bindata depends_on: - tag-pre-condition @@ -159,6 +165,7 @@ steps: - timeout -s ABRT 20m make test-sqlite-migration - timeout -s ABRT 20m make test-sqlite environment: + GOPROXY: off TAGS: bindata depends_on: - build @@ -172,6 +179,7 @@ steps: - make test-mysql-migration - make integration-test-coverage environment: + GOPROXY: off TAGS: bindata TEST_LDAP: 1 depends_on: @@ -192,6 +200,7 @@ steps: - timeout -s ABRT 20m make test-mysql-migration - timeout -s ABRT 20m make test-mysql environment: + GOPROXY: off TAGS: bindata TEST_LDAP: 1 depends_on: @@ -209,6 +218,7 @@ steps: - timeout -s ABRT 20m make test-mysql8-migration - timeout -s ABRT 20m make test-mysql8 environment: + GOPROXY: off TAGS: bindata TEST_LDAP: 1 depends_on: @@ -223,6 +233,7 @@ steps: - timeout -s ABRT 20m make test-pgsql-migration - timeout -s ABRT 20m make test-pgsql environment: + GOPROXY: off TAGS: bindata TEST_LDAP: 1 depends_on: @@ -237,6 +248,7 @@ steps: - make test-mssql-migration - make test-mssql environment: + GOPROXY: off TAGS: bindata TEST_LDAP: 1 depends_on: @@ -248,6 +260,7 @@ steps: commands: - make coverage environment: + GOPROXY: off TAGS: bindata depends_on: - unit-test @@ -380,6 +393,7 @@ steps: - make generate - make release environment: + GOPROXY: off TAGS: bindata sqlite sqlite_unlock_notify - name: gpg-sign @@ -481,6 +495,7 @@ steps: - make generate - make release environment: + GOPROXY: off TAGS: bindata sqlite sqlite_unlock_notify - name: gpg-sign @@ -607,6 +622,8 @@ steps: dry_run: true repo: gitea/gitea tags: linux-amd64 + build_args: + - GOPROXY=off when: event: - pull_request @@ -618,6 +635,8 @@ steps: auto_tag: true auto_tag_suffix: linux-amd64 repo: gitea/gitea + build_args: + - GOPROXY=off password: from_secret: docker_password username: @@ -668,6 +687,8 @@ steps: dry_run: true repo: gitea/gitea tags: linux-arm64 + build_args: + - GOPROXY=off when: event: - pull_request @@ -679,6 +700,8 @@ steps: auto_tag: true auto_tag_suffix: linux-arm64 repo: gitea/gitea + build_args: + - GOPROXY=off password: from_secret: docker_password username: diff --git a/Dockerfile b/Dockerfile index f13fdbe55f..6fa367a3ee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,9 @@ #Build stage FROM golang:1.12-alpine3.10 AS build-env +ARG GOPROXY +ENV GOPROXY ${GOPROXY:-direct} + ARG GITEA_VERSION ARG TAGS="sqlite sqlite_unlock_notify" ENV TAGS "bindata $TAGS" diff --git a/go.mod b/go.mod index 9bf3800077..804573e0d7 100644 --- a/go.mod +++ b/go.mod @@ -115,7 +115,7 @@ require ( gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e // indirect - gopkg.in/editorconfig/editorconfig-core-go.v1 v1.2.0 + gopkg.in/editorconfig/editorconfig-core-go.v1 v1.3.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.42.0 gopkg.in/ldap.v3 v3.0.2 diff --git a/go.sum b/go.sum index a3adcc1803..89a2362e96 100644 --- a/go.sum +++ b/go.sum @@ -451,8 +451,8 @@ gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e/go.mod h1:xsQCaysVCudhrYTfz gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/editorconfig/editorconfig-core-go.v1 v1.2.0 h1:CO465/foR4+bY1xNYjZEl6l8By1g/iMsImoruxfEt84= -gopkg.in/editorconfig/editorconfig-core-go.v1 v1.2.0/go.mod h1:s2mQFI9McjArkyCwyEwU//+luQENTnD/Lfb/7Sj3/kQ= +gopkg.in/editorconfig/editorconfig-core-go.v1 v1.3.0 h1:oxOEwvhxLMpWpN+0pb2r9TWrM0DCFBHxbuIlS27tmFg= +gopkg.in/editorconfig/editorconfig-core-go.v1 v1.3.0/go.mod h1:s2mQFI9McjArkyCwyEwU//+luQENTnD/Lfb/7Sj3/kQ= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= diff --git a/models/fixtures/org_user.yml b/models/fixtures/org_user.yml index 5bb23571fc..385492dd68 100644 --- a/models/fixtures/org_user.yml +++ b/models/fixtures/org_user.yml @@ -39,3 +39,9 @@ uid: 20 org_id: 19 is_public: true + +- + id: 8 + uid: 24 + org_id: 25 + is_public: true diff --git a/models/fixtures/team.yml b/models/fixtures/team.yml index 2d0dd9cd56..b7265ec49e 100644 --- a/models/fixtures/team.yml +++ b/models/fixtures/team.yml @@ -77,4 +77,13 @@ name: review_team authorize: 1 # read num_repos: 1 - num_members: 1 \ No newline at end of file + num_members: 1 + +- + id: 10 + org_id: 25 + lower_name: notowners + name: NotOwners + authorize: 1 # owner + num_repos: 0 + num_members: 1 diff --git a/models/fixtures/team_user.yml b/models/fixtures/team_user.yml index e20b5c9684..4fc609791d 100644 --- a/models/fixtures/team_user.yml +++ b/models/fixtures/team_user.yml @@ -62,4 +62,10 @@ id: 11 org_id: 17 team_id: 9 - uid: 20 \ No newline at end of file + uid: 20 + +- + id: 12 + org_id: 25 + team_id: 10 + uid: 24 diff --git a/models/fixtures/two_factor.yml b/models/fixtures/two_factor.yml new file mode 100644 index 0000000000..d8cb85274b --- /dev/null +++ b/models/fixtures/two_factor.yml @@ -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 diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index ed60e7f5ea..d89dc3c126 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -365,4 +365,39 @@ is_active: true num_members: 0 num_teams: 0 - visibility: 2 \ No newline at end of file + 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 diff --git a/models/migrate.go b/models/migrate.go index b30e6a9d1c..85be3a312c 100644 --- a/models/migrate.go +++ b/models/migrate.go @@ -62,38 +62,50 @@ func insertIssue(sess *xorm.Session, issue *Issue) error { if _, err := sess.Insert(issueLabels); err != nil { return err } + + cols := make([]string, 0) if !issue.IsPull { sess.ID(issue.RepoID).Incr("num_issues") + cols = append(cols, "num_issues") if issue.IsClosed { sess.Incr("num_closed_issues") + cols = append(cols, "num_closed_issues") } } else { sess.ID(issue.RepoID).Incr("num_pulls") + cols = append(cols, "num_pulls") if issue.IsClosed { sess.Incr("num_closed_pulls") + cols = append(cols, "num_closed_pulls") } } - if _, err := sess.NoAutoTime().Update(issue.Repo); err != nil { + if _, err := sess.NoAutoTime().Cols(cols...).Update(issue.Repo); err != nil { return err } + cols = []string{"num_issues"} sess.Incr("num_issues") if issue.IsClosed { sess.Incr("num_closed_issues") + cols = append(cols, "num_closed_issues") } - if _, err := sess.In("id", labelIDs).NoAutoTime().Update(new(Label)); err != nil { + if _, err := sess.In("id", labelIDs).NoAutoTime().Cols(cols...).Update(new(Label)); err != nil { return err } if issue.MilestoneID > 0 { + cols = []string{"num_issues"} sess.Incr("num_issues") + cl := "num_closed_issues" if issue.IsClosed { sess.Incr("num_closed_issues") + cols = append(cols, "num_closed_issues") + cl = "(num_closed_issues + 1)" } if _, err := sess.ID(issue.MilestoneID). - SetExpr("completeness", "num_closed_issues * 100 / num_issues"). - NoAutoTime(). + SetExpr("completeness", cl+" * 100 / (num_issues + 1)"). + NoAutoTime().Cols(cols...). Update(new(Milestone)); err != nil { return err } diff --git a/models/org.go b/models/org.go index d86109de57..7032f6698e 100644 --- a/models/org.go +++ b/models/org.go @@ -72,9 +72,12 @@ func (org *User) GetMembers() error { } var ids = make([]int64, len(ous)) + var idsIsPublic = make(map[int64]bool, len(ous)) for i, ou := range ous { ids[i] = ou.UID + idsIsPublic[ou.UID] = ou.IsPublic } + org.MembersIsPublic = idsIsPublic org.Members, err = GetUsersByIDs(ids) return err } @@ -298,15 +301,13 @@ type OrgUser struct { } func isOrganizationOwner(e Engine, orgID, uid int64) (bool, error) { - ownerTeam := &Team{ - OrgID: orgID, - Name: ownerTeamName, - } - if has, err := e.Get(ownerTeam); err != nil { + ownerTeam, err := getOwnerTeam(e, orgID) + if err != nil { + if err == ErrTeamNotExist { + log.Error("Organization does not have owner team: %d", orgID) + return false, nil + } 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) } diff --git a/models/org_team.go b/models/org_team.go index dcf0743740..1786376d02 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -362,6 +362,11 @@ func GetTeam(orgID int64, name string) (*Team, error) { 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) { t := new(Team) has, err := e.ID(teamID).Get(t) diff --git a/models/repo.go b/models/repo.go index dbd1f2b688..ddeab79805 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1801,6 +1801,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error { &HookTask{RepoID: repoID}, &Notification{RepoID: repoID}, &CommitStatus{RepoID: repoID}, + &RepoIndexerStatus{RepoID: repoID}, ); err != nil { return fmt.Errorf("deleteBeans: %v", err) } diff --git a/models/repo_indexer.go b/models/repo_indexer.go index 83383807a7..c991b1aac9 100644 --- a/models/repo_indexer.go +++ b/models/repo_indexer.go @@ -199,7 +199,7 @@ func addUpdate(update fileUpdate, repo *Repository, batch rupture.FlushingBatch) if size, err := strconv.Atoi(strings.TrimSpace(stdout)); err != nil { return fmt.Errorf("Misformatted git cat-file output: %v", err) } else if int64(size) > setting.Indexer.MaxIndexerFileSize { - return nil + return addDelete(update.Filename, repo, batch) } fileContents, err := git.NewCommand("cat-file", "blob", update.BlobSha). @@ -232,7 +232,7 @@ func addDelete(filename string, repo *Repository, batch rupture.FlushingBatch) e } func isIndexable(entry *git.TreeEntry) bool { - return entry.IsRegular() + return entry.IsRegular() || entry.IsExecutable() } // parseGitLsTreeOutput parses the output of a `git ls-tree -r --full-name` command diff --git a/models/user.go b/models/user.go index 1f684a5940..2e4f971662 100644 --- a/models/user.go +++ b/models/user.go @@ -139,11 +139,12 @@ type User struct { NumRepos int // For organization - NumTeams int - NumMembers int - Teams []*Team `xorm:"-"` - Members []*User `xorm:"-"` - Visibility structs.VisibleType `xorm:"NOT NULL DEFAULT 0"` + NumTeams int + NumMembers int + Teams []*Team `xorm:"-"` + Members UserList `xorm:"-"` + MembersIsPublic map[int64]bool `xorm:"-"` + Visibility structs.VisibleType `xorm:"NOT NULL DEFAULT 0"` // Preferences DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"` @@ -1408,9 +1409,7 @@ type SearchUserOptions struct { } func (opts *SearchUserOptions) toConds() builder.Cond { - - var cond = builder.NewCond() - cond = cond.And(builder.Eq{"type": opts.Type}) + var cond builder.Cond = builder.Eq{"type": opts.Type} if len(opts.Keyword) > 0 { lowerKeyword := strings.ToLower(opts.Keyword) diff --git a/models/user_test.go b/models/user_test.go index 10420a143f..290253c4b1 100644 --- a/models/user_test.go +++ b/models/user_test.go @@ -5,6 +5,7 @@ package models import ( + "fmt" "math/rand" "strings" "testing" @@ -15,6 +16,58 @@ import ( "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) { assert.NoError(t, PrepareTestDatabase()) @@ -83,7 +136,7 @@ func TestSearchUsers(t *testing.T) { []int64{7, 17}) testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 3, PageSize: 2}, - []int64{19}) + []int64{19, 25}) testOrgSuccess(&SearchUserOptions{Page: 4, PageSize: 2}, []int64{}) @@ -95,13 +148,13 @@ func TestSearchUsers(t *testing.T) { } 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}, []int64{9}) 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}, []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) diff --git a/models/userlist.go b/models/userlist.go new file mode 100644 index 0000000000..43838a6804 --- /dev/null +++ b/models/userlist.go @@ -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 +} diff --git a/models/userlist_test.go b/models/userlist_test.go new file mode 100644 index 0000000000..ca08cc90ce --- /dev/null +++ b/models/userlist_test.go @@ -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()) +} diff --git a/modules/git/tree_entry.go b/modules/git/tree_entry.go index 6019e34487..4e18cc8ead 100644 --- a/modules/git/tree_entry.go +++ b/modules/git/tree_entry.go @@ -108,6 +108,11 @@ func (te *TreeEntry) IsRegular() bool { return te.gogitTreeEntry.Mode == filemode.Regular } +// IsExecutable if the entry is an executable file (not necessarily binary) +func (te *TreeEntry) IsExecutable() bool { + return te.gogitTreeEntry.Mode == filemode.Executable +} + // Blob returns the blob object the entry func (te *TreeEntry) Blob() *Blob { encodedObj, err := te.ptree.repo.gogitRepo.Storer.EncodedObject(plumbing.AnyObject, te.gogitTreeEntry.Hash) diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go index cb52f6ac2e..4334480566 100644 --- a/modules/highlight/highlight.go +++ b/modules/highlight/highlight.go @@ -19,9 +19,11 @@ var ( } // File names that are representing highlight classes. - highlightFileNames = map[string]bool{ - "dockerfile": true, - "makefile": true, + highlightFileNames = map[string]string{ + "dockerfile": "dockerfile", + "makefile": "makefile", + "gnumakefile": "makefile", + "cmakelists.txt": "cmake", } // Extensions that are same as highlight classes. @@ -87,8 +89,8 @@ func FileNameToHighlightClass(fname string) string { return "nohighlight" } - if highlightFileNames[fname] { - return fname + if name, ok := highlightFileNames[fname]; ok { + return name } ext := path.Ext(fname) diff --git a/public/css/index.css b/public/css/index.css index b2676282bc..b38d814ea5 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -633,10 +633,11 @@ footer .ui.left,footer .ui.right{line-height:40px} .repository #commits-table thead .shatd{text-align:center} .repository #commits-table td.sha .sha.label{margin:0} .repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n){background-color:rgba(0,0,0,.02)!important} -.repository #commits-table td.sha .sha.label.isSigned,.repository #repo-files-table .sha.label.isSigned{border:1px solid #bbb} -.repository #commits-table td.sha .sha.label.isSigned .detail.icon,.repository #repo-files-table .sha.label.isSigned .detail.icon{background:#fafafa;margin:-6px -10px -4px 0;padding:5px 3px 5px 6px;border-left:1px solid #bbb;border-top-left-radius:0;border-bottom-left-radius:0} +.repository #commits-table td.sha .sha.label,.repository #repo-files-table .sha.label{border:1px solid #bbb} +.repository #commits-table td.sha .sha.label .detail.icon,.repository #repo-files-table .sha.label .detail.icon{background:#fafafa;margin:-6px -10px -4px 0;padding:5px 3px 5px 6px;border-left:1px solid #bbb;border-top-left-radius:0;border-bottom-left-radius:0} .repository #commits-table td.sha .sha.label.isSigned.isVerified,.repository #repo-files-table .sha.label.isSigned.isVerified{border:1px solid #21ba45;background:rgba(33,186,69,.1)} -.repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon,.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon{border-left:1px solid rgba(33,186,69,.5)} +.repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon,.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon{border-left:1px solid #21ba45} +.repository #commits-table td.sha .sha.label.isSigned.isVerified:hover,.repository #repo-files-table .sha.label.isSigned.isVerified:hover{background:rgba(33,186,69,.3)!important} .repository .diff-detail-box{padding:7px 0;background:#fff;line-height:30px} .repository .diff-detail-box>div:after{clear:both;content:"";display:block} .repository .diff-detail-box ol{clear:both;padding-left:0;margin-top:5px;margin-bottom:28px} diff --git a/public/css/theme-arc-green.css b/public/css/theme-arc-green.css index f8722ffa5d..605c9b047a 100644 --- a/public/css/theme-arc-green.css +++ b/public/css/theme-arc-green.css @@ -211,7 +211,9 @@ input{background:#2e323e} .ui.basic.blue.button:hover,.ui.basic.blue.buttons .button:hover{box-shadow:0 0 0 1px #87ab63 inset!important;color:#87ab63!important} .ui.basic.blue.button:focus,.ui.basic.blue.buttons .button:focus{box-shadow:0 0 0 1px #87ab63 inset!important;color:#87ab63!important} .repository.file.list #file-content .code-view .lines-code .hljs,.repository.file.list #file-content .code-view .lines-code ol,.repository.file.list #file-content .code-view .lines-code pre,.repository.file.list #file-content .code-view .lines-num .hljs,.repository.file.list #file-content .code-view .lines-num ol,.repository.file.list #file-content .code-view .lines-num pre{background-color:#2a2e3a} -a.ui.label:hover,a.ui.labels .label:hover{background-color:#505667;color:#dbdbdb} +a.ui.label:hover,a.ui.labels .label:hover{background-color:#505667!important;color:#dbdbdb!important} +.repository #commits-table td.sha .sha.label,.repository #repo-files-table .sha.label{border-color:#888} +.repository #commits-table td.sha .sha.label.isSigned .detail.icon,.repository #repo-files-table .sha.label.isSigned .detail.icon{background:0 0;border-left-color:#888} .repository .label.list .item{border-bottom:1px dashed #4c505c} .ui.basic.blue.button,.ui.basic.blue.buttons .button{box-shadow:0 0 0 1px #87ab63 inset!important;color:#87ab63!important} .repository.file.list #file-content .code-view .hljs,.repository.file.list #file-content .code-view .lines-code ol,.repository.file.list #file-content .code-view .lines-code pre,.repository.file.list #file-content .code-view .lines-num .hljs,.repository.file.list #file-content .code-view .lines-num ol,.repository.file.list #file-content .code-view .lines-num pre{background-color:#2a2e3a} diff --git a/public/less/_repository.less b/public/less/_repository.less index 8d2ca07d93..093f35f831 100644 --- a/public/less/_repository.less +++ b/public/less/_repository.less @@ -1272,17 +1272,15 @@ #commits-table td.sha .sha.label, #repo-files-table .sha.label { - &.isSigned { - border: 1px solid #bbbbbb; + border: 1px solid #bbbbbb; - .detail.icon { - background: #fafafa; - margin: -6px -10px -4px 0; - padding: 5px 3px 5px 6px; - border-left: 1px solid #bbbbbb; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } + .detail.icon { + background: #fafafa; + margin: -6px -10px -4px 0; + padding: 5px 3px 5px 6px; + border-left: 1px solid #bbbbbb; + border-top-left-radius: 0; + border-bottom-left-radius: 0; } &.isSigned.isVerified { @@ -1290,7 +1288,11 @@ background: fade(#21ba45, 10%); .detail.icon { - border-left: 1px solid fade(#21ba45, 50%); + border-left: 1px solid #21ba45; + } + + &:hover { + background: fade(#21ba45, 30%) !important; } } } diff --git a/public/less/themes/arc-green.less b/public/less/themes/arc-green.less index 12d7eb2db1..37911c186c 100644 --- a/public/less/themes/arc-green.less +++ b/public/less/themes/arc-green.less @@ -1091,8 +1091,19 @@ input { a.ui.label:hover, a.ui.labels .label:hover { - background-color: #505667; - color: #dbdbdb; + background-color: #505667 !important; + color: #dbdbdb !important; +} + +.repository #commits-table td.sha .sha.label, +.repository #repo-files-table .sha.label { + border-color: #888; +} + +.repository #commits-table td.sha .sha.label.isSigned .detail.icon, +.repository #repo-files-table .sha.label.isSigned .detail.icon { + background: none; + border-left-color: #888; } .repository .label.list .item { diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index d740647cd4..c90e739626 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -82,6 +82,15 @@ func GetAllOrgs(ctx *context.APIContext) { // summary: List all organizations // produces: // - application/json + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/OrganizationList" @@ -90,7 +99,9 @@ func GetAllOrgs(ctx *context.APIContext) { users, _, err := models.SearchUsers(&models.SearchUserOptions{ Type: models.UserTypeOrganization, OrderBy: models.SearchOrderByAlphabetically, - PageSize: -1, + Page: ctx.QueryInt("page"), + PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")), + Private: true, }) if err != nil { ctx.Error(500, "SearchOrganizations", err) diff --git a/routers/init.go b/routers/init.go index e431260687..26d750d20e 100644 --- a/routers/init.go +++ b/routers/init.go @@ -54,7 +54,7 @@ func initDBEngine() (err error) { } else if i == setting.DBConnectRetries-1 { return err } - log.Debug("ORM engine initialization attempt #%d/%d failed. Error: %v", i+1, setting.DBConnectRetries, err) + log.Error("ORM engine initialization attempt #%d/%d failed. Error: %v", i+1, setting.DBConnectRetries, err) log.Info("Backing off for %d seconds", int64(setting.DBConnectBackoff/time.Second)) time.Sleep(setting.DBConnectBackoff) } diff --git a/routers/org/members.go b/routers/org/members.go index d65bc2a008..20f80cefcd 100644 --- a/routers/org/members.go +++ b/routers/org/members.go @@ -19,7 +19,7 @@ const ( tplMembers base.TplName = "org/member/members" ) -// Members render orgnization users page +// Members render organization users page func Members(ctx *context.Context) { org := ctx.Org.Organization ctx.Data["Title"] = org.FullName @@ -30,11 +30,14 @@ func Members(ctx *context.Context) { return } 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) } -// MembersAction response for operation to a member of orgnization +// MembersAction response for operation to a member of organization func MembersAction(ctx *context.Context) { uid := com.StrTo(ctx.Query("uid")).MustInt64() if uid == 0 { diff --git a/templates/org/member/members.tmpl b/templates/org/member/members.tmpl index 7f0a763610..9db506ee5b 100644 --- a/templates/org/member/members.tmpl +++ b/templates/org/member/members.tmpl @@ -5,7 +5,7 @@ {{template "base/alert" .}}