Template
1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo synced 2024-11-25 03:06:10 +01:00

Compare commits

..

4 commits

Author SHA1 Message Date
Earl Warren a38145ea21
chore(release-notes): notes for the week 2024-47 weekly cherry pick 2024-11-17 21:57:34 +01:00
Lunny Xiao f6a46055aa
Reimplement GetUserOrgsList to make it simple and clear (#32486)
Reimplement GetUserOrgsList and also move some functions and test to
org_list file.

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
(cherry picked from commit b4abb6deff14b741c7666d7579e0eea68443306c)

Conflicts:
	models/organization/org_test.go
	services/oauth2_provider/access_token.go
  trivial conflicts due to codeblocks moving to different files
2024-11-17 21:57:34 +01:00
6543 7751bb64cb
Calculate PublicOnly for org membership only once (#32234)
Refactoring of #32211

this move the PublicOnly() filter calcuation next to the DB querys and
let it be decided by the Doer

---
*Sponsored by Kithara Software GmbH*

(cherry picked from commit 43c252dfeaf9ab03c4db3e7ac5169bc0d69901ac)

Conflicts:
	models/organization/org_test.go
	models/organization/org_user_test.go
	routers/web/org/home.go

  rather simple conflict resolution but not trivial
  tests/integration/user_count_test.go had to be adapted (simple)
  because it does not exist in Gitea and uses the modified model
2024-11-17 21:57:34 +01:00
wxiaoguang 45435a8789
Fix and refactor markdown rendering (#32522)
(cherry picked from commit 5eebe1dc5fb29a162c51d050396fce7b14e47f4e)

Conflicts:
	models/repo/repo.go
	models/repo/repo_test.go
	modules/markup/html.go
	modules/markup/html_commit.go
	modules/markup/html_email.go
	modules/markup/html_emoji.go
	modules/markup/html_internal_test.go
	modules/markup/html_issue.go
	modules/markup/html_link.go
	modules/markup/html_node.go
	modules/markup/html_test.go
	modules/markup/markdown/goldmark.go
	modules/markup/markdown/markdown_test.go
	modules/markup/markdown/transform_image.go
	modules/markup/orgmode/orgmode.go
	modules/markup/orgmode/orgmode_test.go
	modules/markup/render.go
	modules/markup/render_links.go
	modules/templates/util_render.go
	modules/templates/util_render_test.go
	routers/common/markup.go
	routers/web/feed/convert.go
	routers/web/repo/wiki.go
  but a few lines survived and are useful
2024-11-17 20:34:28 +01:00
14 changed files with 287 additions and 237 deletions

View file

@ -1,78 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package organization
import (
"context"
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"xorm.io/builder"
)
// MinimalOrg represents a simple organization with only the needed columns
type MinimalOrg = Organization
// GetUserOrgsList returns all organizations the given user has access to
func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, error) {
schema, err := db.TableInfo(new(user_model.User))
if err != nil {
return nil, err
}
outputCols := []string{
"id",
"name",
"full_name",
"visibility",
"avatar",
"avatar_email",
"use_custom_avatar",
}
groupByCols := &strings.Builder{}
for _, col := range outputCols {
fmt.Fprintf(groupByCols, "`%s`.%s,", schema.Name, col)
}
groupByStr := groupByCols.String()
groupByStr = groupByStr[0 : len(groupByStr)-1]
sess := db.GetEngine(ctx)
sess = sess.Select(groupByStr+", count(distinct repo_id) as org_count").
Table("user").
Join("INNER", "team", "`team`.org_id = `user`.id").
Join("INNER", "team_user", "`team`.id = `team_user`.team_id").
Join("LEFT", builder.
Select("id as repo_id, owner_id as repo_owner_id").
From("repository").
Where(repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)), "`repository`.repo_owner_id = `team`.org_id").
Where("`team_user`.uid = ?", user.ID).
GroupBy(groupByStr)
type OrgCount struct {
Organization `xorm:"extends"`
OrgCount int
}
orgCounts := make([]*OrgCount, 0, 10)
if err := sess.
Asc("`user`.name").
Find(&orgCounts); err != nil {
return nil, err
}
orgs := make([]*MinimalOrg, len(orgCounts))
for i, orgCount := range orgCounts {
orgCount.Organization.NumRepos = orgCount.OrgCount
orgs[i] = &orgCount.Organization
}
return orgs, nil
}

View file

@ -24,13 +24,6 @@ import (
"xorm.io/builder" "xorm.io/builder"
) )
// ________ .__ __ .__
// \_____ \_______ _________ ____ |__|____________ _/ |_|__| ____ ____
// / | \_ __ \/ ___\__ \ / \| \___ /\__ \\ __\ |/ _ \ / \
// / | \ | \/ /_/ > __ \| | \ |/ / / __ \| | | ( <_> ) | \
// \_______ /__| \___ (____ /___| /__/_____ \(____ /__| |__|\____/|___| /
// \/ /_____/ \/ \/ \/ \/ \/
// ErrOrgNotExist represents a "OrgNotExist" kind of error. // ErrOrgNotExist represents a "OrgNotExist" kind of error.
type ErrOrgNotExist struct { type ErrOrgNotExist struct {
ID int64 ID int64
@ -141,8 +134,9 @@ func (org *Organization) LoadTeams(ctx context.Context) ([]*Team, error) {
} }
// GetMembers returns all members of organization. // GetMembers returns all members of organization.
func (org *Organization) GetMembers(ctx context.Context) (user_model.UserList, map[int64]bool, error) { func (org *Organization) GetMembers(ctx context.Context, doer *user_model.User) (user_model.UserList, map[int64]bool, error) {
return FindOrgMembers(ctx, &FindOrgMembersOpts{ return FindOrgMembers(ctx, &FindOrgMembersOpts{
Doer: doer,
OrgID: org.ID, OrgID: org.ID,
}) })
} }
@ -195,16 +189,22 @@ func (org *Organization) CanCreateRepo() bool {
// FindOrgMembersOpts represensts find org members conditions // FindOrgMembersOpts represensts find org members conditions
type FindOrgMembersOpts struct { type FindOrgMembersOpts struct {
db.ListOptions db.ListOptions
Doer *user_model.User
IsDoerMember bool
OrgID int64 OrgID int64
PublicOnly bool }
func (opts FindOrgMembersOpts) PublicOnly() bool {
return opts.Doer == nil || !(opts.IsDoerMember || opts.Doer.IsAdmin)
} }
// CountOrgMembers counts the organization's members // CountOrgMembers counts the organization's members
func CountOrgMembers(ctx context.Context, opts *FindOrgMembersOpts) (int64, error) { func CountOrgMembers(ctx context.Context, opts *FindOrgMembersOpts) (int64, error) {
sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID) sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID)
if opts.PublicOnly { if opts.PublicOnly() {
sess.And("is_public = ?", true) sess.And("is_public = ?", true)
} }
return sess.Count(new(OrgUser)) return sess.Count(new(OrgUser))
} }
@ -439,42 +439,6 @@ func GetUsersWhoCanCreateOrgRepo(ctx context.Context, orgID int64) (map[int64]*u
And("team_user.org_id = ?", orgID).Find(&users) And("team_user.org_id = ?", orgID).Find(&users)
} }
// SearchOrganizationsOptions options to filter organizations
type SearchOrganizationsOptions struct {
db.ListOptions
All bool
}
// FindOrgOptions finds orgs options
type FindOrgOptions struct {
db.ListOptions
UserID int64
IncludePrivate bool
}
func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
cond := builder.Eq{"uid": userID}
if !includePrivate {
cond["is_public"] = true
}
return builder.Select("org_id").From("org_user").Where(cond)
}
func (opts FindOrgOptions) ToConds() builder.Cond {
var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization}
if opts.UserID > 0 {
cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
}
if !opts.IncludePrivate {
cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
}
return cond
}
func (opts FindOrgOptions) ToOrders() string {
return "`user`.name ASC"
}
// HasOrgOrUserVisible tells if the given user can see the given org or user // HasOrgOrUserVisible tells if the given user can see the given org or user
func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool { func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool {
// If user is nil, it's an anonymous user/request. // If user is nil, it's an anonymous user/request.
@ -507,26 +471,13 @@ func HasOrgsVisible(ctx context.Context, orgs []*Organization, user *user_model.
return false return false
} }
// GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID
// are allowed to create repos.
func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organization, error) {
orgs := make([]*Organization, 0, 10)
return orgs, db.GetEngine(ctx).Where(builder.In("id", builder.Select("`user`.id").From("`user`").
Join("INNER", "`team_user`", "`team_user`.org_id = `user`.id").
Join("INNER", "`team`", "`team`.id = `team_user`.team_id").
Where(builder.Eq{"`team_user`.uid": userID}).
And(builder.Eq{"`team`.authorize": perm.AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})))).
Asc("`user`.name").
Find(&orgs)
}
// GetOrgUsersByOrgID returns all organization-user relations by organization ID. // GetOrgUsersByOrgID returns all organization-user relations by organization ID.
func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUser, error) { func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUser, error) {
sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID) sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID)
if opts.PublicOnly { if opts.PublicOnly() {
sess.And("is_public = ?", true) sess.And("is_public = ?", true)
} }
if opts.ListOptions.PageSize > 0 { if opts.ListOptions.PageSize > 0 {
sess = db.SetSessionPagination(sess, opts) sess = db.SetSessionPagination(sess, opts)

View file

@ -0,0 +1,138 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package organization
import (
"context"
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/structs"
"xorm.io/builder"
)
// SearchOrganizationsOptions options to filter organizations
type SearchOrganizationsOptions struct {
db.ListOptions
All bool
}
// FindOrgOptions finds orgs options
type FindOrgOptions struct {
db.ListOptions
UserID int64
IncludePrivate bool
}
func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
cond := builder.Eq{"uid": userID}
if !includePrivate {
cond["is_public"] = true
}
return builder.Select("org_id").From("org_user").Where(cond)
}
func (opts FindOrgOptions) ToConds() builder.Cond {
var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization}
if opts.UserID > 0 {
cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
}
if !opts.IncludePrivate {
cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
}
return cond
}
func (opts FindOrgOptions) ToOrders() string {
return "`user`.lower_name ASC"
}
// GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID
// are allowed to create repos.
func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organization, error) {
orgs := make([]*Organization, 0, 10)
return orgs, db.GetEngine(ctx).Where(builder.In("id", builder.Select("`user`.id").From("`user`").
Join("INNER", "`team_user`", "`team_user`.org_id = `user`.id").
Join("INNER", "`team`", "`team`.id = `team_user`.team_id").
Where(builder.Eq{"`team_user`.uid": userID}).
And(builder.Eq{"`team`.authorize": perm.AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})))).
Asc("`user`.name").
Find(&orgs)
}
// MinimalOrg represents a simple organization with only the needed columns
type MinimalOrg = Organization
// GetUserOrgsList returns all organizations the given user has access to
func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, error) {
schema, err := db.TableInfo(new(user_model.User))
if err != nil {
return nil, err
}
outputCols := []string{
"id",
"name",
"full_name",
"visibility",
"avatar",
"avatar_email",
"use_custom_avatar",
}
selectColumns := &strings.Builder{}
for i, col := range outputCols {
fmt.Fprintf(selectColumns, "`%s`.%s", schema.Name, col)
if i < len(outputCols)-1 {
selectColumns.WriteString(", ")
}
}
columnsStr := selectColumns.String()
var orgs []*MinimalOrg
if err := db.GetEngine(ctx).Select(columnsStr).
Table("user").
Where(builder.In("`user`.`id`", queryUserOrgIDs(user.ID, true))).
Find(&orgs); err != nil {
return nil, err
}
type orgCount struct {
OrgID int64
RepoCount int
}
var orgCounts []orgCount
if err := db.GetEngine(ctx).
Select("owner_id AS org_id, COUNT(DISTINCT(repository.id)) as repo_count").
Table("repository").
Join("INNER", "org_user", "owner_id = org_user.org_id").
Where("org_user.uid = ?", user.ID).
And(builder.Or(
builder.Eq{"repository.is_private": false},
builder.In("repository.id", builder.Select("repo_id").From("team_repo").
InnerJoin("team_user", "team_user.team_id = team_repo.team_id").
Where(builder.Eq{"team_user.uid": user.ID})),
builder.In("repository.id", builder.Select("repo_id").From("collaboration").
Where(builder.Eq{"user_id": user.ID})),
)).
GroupBy("owner_id").Find(&orgCounts); err != nil {
return nil, err
}
orgCountMap := make(map[int64]int, len(orgCounts))
for _, orgCount := range orgCounts {
orgCountMap[orgCount.OrgID] = orgCount.RepoCount
}
for _, org := range orgs {
org.NumRepos = orgCountMap[org.ID]
}
return orgs, nil
}

View file

@ -0,0 +1,63 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package organization_test
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCountOrganizations(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&organization.Organization{})
require.NoError(t, err)
cnt, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{IncludePrivate: true})
require.NoError(t, err)
assert.Equal(t, expected, cnt)
}
func TestFindOrgs(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
orgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
UserID: 4,
IncludePrivate: true,
})
require.NoError(t, err)
if assert.Len(t, orgs, 1) {
assert.EqualValues(t, 3, orgs[0].ID)
}
orgs, err = db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
UserID: 4,
IncludePrivate: false,
})
require.NoError(t, err)
assert.Empty(t, orgs)
total, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
UserID: 4,
IncludePrivate: true,
})
require.NoError(t, err)
assert.EqualValues(t, 1, total)
}
func TestGetUserOrgsList(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 4})
require.NoError(t, err)
if assert.Len(t, orgs, 1) {
assert.EqualValues(t, 3, orgs[0].ID)
// repo_id: 3 is in the team, 32 is public, 5 is private with no team
assert.EqualValues(t, 2, orgs[0].NumRepos)
}
}

View file

@ -4,6 +4,7 @@
package organization_test package organization_test
import ( import (
"sort"
"testing" "testing"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
@ -104,7 +105,7 @@ func TestUser_GetTeams(t *testing.T) {
func TestUser_GetMembers(t *testing.T) { func TestUser_GetMembers(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase()) require.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}) org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
members, _, err := org.GetMembers(db.DefaultContext) members, _, err := org.GetMembers(db.DefaultContext, &user_model.User{IsAdmin: true})
require.NoError(t, err) require.NoError(t, err)
if assert.Len(t, members, 3) { if assert.Len(t, members, 3) {
assert.Equal(t, int64(2), members[0].ID) assert.Equal(t, int64(2), members[0].ID)
@ -128,15 +129,6 @@ func TestGetOrgByName(t *testing.T) {
assert.True(t, organization.IsErrOrgNotExist(err)) assert.True(t, organization.IsErrOrgNotExist(err))
} }
func TestCountOrganizations(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&organization.Organization{})
require.NoError(t, err)
cnt, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{IncludePrivate: true})
require.NoError(t, err)
assert.Equal(t, expected, cnt)
}
func TestIsOrganizationOwner(t *testing.T) { func TestIsOrganizationOwner(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase()) require.NoError(t, unittest.PrepareTestDatabase())
test := func(orgID, userID int64, expected bool) { test := func(orgID, userID int64, expected bool) {
@ -181,67 +173,45 @@ func TestIsPublicMembership(t *testing.T) {
test(unittest.NonexistentID, unittest.NonexistentID, false) test(unittest.NonexistentID, unittest.NonexistentID, false)
} }
func TestFindOrgs(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
orgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
UserID: 4,
IncludePrivate: true,
})
require.NoError(t, err)
if assert.Len(t, orgs, 1) {
assert.EqualValues(t, 3, orgs[0].ID)
}
orgs, err = db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
UserID: 4,
IncludePrivate: false,
})
require.NoError(t, err)
assert.Empty(t, orgs)
total, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
UserID: 4,
IncludePrivate: true,
})
require.NoError(t, err)
assert.EqualValues(t, 1, total)
}
func TestGetOrgUsersByOrgID(t *testing.T) { func TestGetOrgUsersByOrgID(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase()) require.NoError(t, unittest.PrepareTestDatabase())
orgUsers, err := organization.GetOrgUsersByOrgID(db.DefaultContext, &organization.FindOrgMembersOpts{ opts := &organization.FindOrgMembersOpts{
ListOptions: db.ListOptions{}, Doer: &user_model.User{IsAdmin: true},
OrgID: 3, OrgID: 3,
PublicOnly: false, }
}) assert.False(t, opts.PublicOnly())
orgUsers, err := organization.GetOrgUsersByOrgID(db.DefaultContext, opts)
require.NoError(t, err) require.NoError(t, err)
if assert.Len(t, orgUsers, 3) { sort.Slice(orgUsers, func(i, j int) bool {
assert.Equal(t, organization.OrgUser{ return orgUsers[i].ID < orgUsers[j].ID
ID: orgUsers[0].ID, })
assert.EqualValues(t, []*organization.OrgUser{{
ID: 1,
OrgID: 3, OrgID: 3,
UID: 2, UID: 2,
IsPublic: true, IsPublic: true,
}, *orgUsers[0]) }, {
assert.Equal(t, organization.OrgUser{ ID: 2,
ID: orgUsers[1].ID,
OrgID: 3, OrgID: 3,
UID: 4, UID: 4,
IsPublic: false, IsPublic: false,
}, *orgUsers[1]) }, {
assert.Equal(t, organization.OrgUser{ ID: 9,
ID: orgUsers[2].ID,
OrgID: 3, OrgID: 3,
UID: 28, UID: 28,
IsPublic: true, IsPublic: true,
}, *orgUsers[2]) }}, orgUsers)
}
opts = &organization.FindOrgMembersOpts{OrgID: 3}
assert.True(t, opts.PublicOnly())
orgUsers, err = organization.GetOrgUsersByOrgID(db.DefaultContext, opts)
require.NoError(t, err)
assert.Len(t, orgUsers, 2)
orgUsers, err = organization.GetOrgUsersByOrgID(db.DefaultContext, &organization.FindOrgMembersOpts{ orgUsers, err = organization.GetOrgUsersByOrgID(db.DefaultContext, &organization.FindOrgMembersOpts{
ListOptions: db.ListOptions{}, ListOptions: db.ListOptions{},
OrgID: unittest.NonexistentID, OrgID: unittest.NonexistentID,
PublicOnly: false,
}) })
require.NoError(t, err) require.NoError(t, err)
assert.Empty(t, orgUsers) assert.Empty(t, orgUsers)

View file

@ -95,7 +95,7 @@ func TestUserListIsPublicMember(t *testing.T) {
func testUserListIsPublicMember(t *testing.T, orgID int64, expected map[int64]bool) { func testUserListIsPublicMember(t *testing.T, orgID int64, expected map[int64]bool) {
org, err := organization.GetOrgByID(db.DefaultContext, orgID) org, err := organization.GetOrgByID(db.DefaultContext, orgID)
require.NoError(t, err) require.NoError(t, err)
_, membersIsPublic, err := org.GetMembers(db.DefaultContext) _, membersIsPublic, err := org.GetMembers(db.DefaultContext, &user_model.User{IsAdmin: true})
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, expected, membersIsPublic) assert.Equal(t, expected, membersIsPublic)
} }
@ -122,7 +122,7 @@ func TestUserListIsUserOrgOwner(t *testing.T) {
func testUserListIsUserOrgOwner(t *testing.T, orgID int64, expected map[int64]bool) { func testUserListIsUserOrgOwner(t *testing.T, orgID int64, expected map[int64]bool) {
org, err := organization.GetOrgByID(db.DefaultContext, orgID) org, err := organization.GetOrgByID(db.DefaultContext, orgID)
require.NoError(t, err) require.NoError(t, err)
members, _, err := org.GetMembers(db.DefaultContext) members, _, err := org.GetMembers(db.DefaultContext, &user_model.User{IsAdmin: true})
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, expected, organization.IsUserOrgOwner(db.DefaultContext, members, orgID)) assert.Equal(t, expected, organization.IsUserOrgOwner(db.DefaultContext, members, orgID))
} }

View file

@ -39,7 +39,7 @@ const (
// SanitizerRules implements markup.Renderer // SanitizerRules implements markup.Renderer
func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule { func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
return []setting.MarkupSanitizerRule{ return []setting.MarkupSanitizerRule{
{Element: "div", AllowAttr: "class", Regexp: regexp.MustCompile(playerClassName)}, {Element: "div", AllowAttr: "class", Regexp: regexp.MustCompile("^" + playerClassName + "$")},
{Element: "div", AllowAttr: playerSrcAttr}, {Element: "div", AllowAttr: playerSrcAttr},
} }
} }

View file

@ -37,9 +37,9 @@ func (Renderer) Extensions() []string {
// SanitizerRules implements markup.Renderer // SanitizerRules implements markup.Renderer
func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule { func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
return []setting.MarkupSanitizerRule{ return []setting.MarkupSanitizerRule{
{Element: "table", AllowAttr: "class", Regexp: regexp.MustCompile(`data-table`)}, {Element: "table", AllowAttr: "class", Regexp: regexp.MustCompile(`^data-table$`)},
{Element: "th", AllowAttr: "class", Regexp: regexp.MustCompile(`line-num`)}, {Element: "th", AllowAttr: "class", Regexp: regexp.MustCompile(`^line-num$`)},
{Element: "td", AllowAttr: "class", Regexp: regexp.MustCompile(`line-num`)}, {Element: "td", AllowAttr: "class", Regexp: regexp.MustCompile(`^line-num$`)},
} }
} }

View file

@ -1,8 +1,8 @@
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/18b967c9de75b07269ea44fdf78cfb6312799927) Remove transaction for archive download fix(security): [commit](https://codeberg.org/forgejo/forgejo/commit/45435a8789f8ff69603799a9031246d2d621d139) Fix and refactor markdown rendering
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/cdc451149b6ef599ba39c893ff530e08c7968a09) Fix oauth2 error handle not return immediately fix: [commit](https://codeberg.org/forgejo/forgejo/commit/a8f2002a9b061ec1092df67c6f05e30aa7d2e2d2) Remove transaction for archive download
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/c243acd9072ff1a9a30fdc635db7fcd28f5bd06a) Trim title before insert/update to database to match the size requirements of database fix: [commit](https://codeberg.org/forgejo/forgejo/commit/96ee0f56475204b2bbdc7f2aeb35b1c32eac469c) Fix oauth2 error handle not return immediately
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/699f04b8a3b5049f75e0b800a638e7626d18331e) Fix nil panic if repo doesn't exist fix: [commit](https://codeberg.org/forgejo/forgejo/commit/c2e8790df37a14b4d2f72c7377db75309e0ebf1d) Trim title before insert/update to database to match the size requirements of database
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/9ad1971066df66191c1a6eef2a2a8026e1c7e752) Disable Oauth check if oauth disabled fix: [commit](https://codeberg.org/forgejo/forgejo/commit/03ab73d92eabaf774278effe3332623b1dc3580a) Fix nil panic if repo doesn't exist
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/9dd9dd449c5120328ed70d2b1048037ba51d98ee) Fix test fixtures for user2/lfs.git fix: [commit](https://codeberg.org/forgejo/forgejo/commit/56971f9ed90a01fd74a634b7496593e6f62ac260) Disable Oauth check if oauth disabled
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/7f51210672031aee7a790455d51a17ce11a70559) Harden runner updateTask and updateLog api fix: [commit](https://codeberg.org/forgejo/forgejo/commit/7f51210672031aee7a790455d51a17ce11a70559) Harden runner updateTask and updateLog api
feat: [commit](https://codeberg.org/forgejo/forgejo/commit/dd3c4d7096cff91854bcc6641f55d9d093e5c86e) Add a doctor check to disable the "Actions" unit for mirrors feat: [commit](https://codeberg.org/forgejo/forgejo/commit/dd3c4d7096cff91854bcc6641f55d9d093e5c86e) Add a doctor check to disable the "Actions" unit for mirrors

View file

@ -18,10 +18,11 @@ import (
) )
// listMembers list an organization's members // listMembers list an organization's members
func listMembers(ctx *context.APIContext, publicOnly bool) { func listMembers(ctx *context.APIContext, isMember bool) {
opts := &organization.FindOrgMembersOpts{ opts := &organization.FindOrgMembersOpts{
Doer: ctx.Doer,
IsDoerMember: isMember,
OrgID: ctx.Org.Organization.ID, OrgID: ctx.Org.Organization.ID,
PublicOnly: publicOnly,
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
} }
@ -73,16 +74,19 @@ func ListMembers(ctx *context.APIContext) {
// "404": // "404":
// "$ref": "#/responses/notFound" // "$ref": "#/responses/notFound"
publicOnly := true var (
isMember bool
err error
)
if ctx.Doer != nil { if ctx.Doer != nil {
isMember, err := ctx.Org.Organization.IsOrgMember(ctx, ctx.Doer.ID) isMember, err = ctx.Org.Organization.IsOrgMember(ctx, ctx.Doer.ID)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "IsOrgMember", err) ctx.Error(http.StatusInternalServerError, "IsOrgMember", err)
return return
} }
publicOnly = !isMember && !ctx.Doer.IsAdmin
} }
listMembers(ctx, publicOnly) listMembers(ctx, isMember)
} }
// ListPublicMembers list an organization's public members // ListPublicMembers list an organization's public members
@ -112,7 +116,7 @@ func ListPublicMembers(ctx *context.APIContext) {
// "404": // "404":
// "$ref": "#/responses/notFound" // "$ref": "#/responses/notFound"
listMembers(ctx, true) listMembers(ctx, false)
} }
// IsMember check if a user is a member of an organization // IsMember check if a user is a member of an organization

View file

@ -110,10 +110,12 @@ func Home(ctx *context.Context) {
} }
opts := &organization.FindOrgMembersOpts{ opts := &organization.FindOrgMembersOpts{
Doer: ctx.Doer,
OrgID: org.ID, OrgID: org.ID,
PublicOnly: ctx.Org.PublicMemberOnly, IsDoerMember: ctx.Org.IsMember,
ListOptions: db.ListOptions{Page: 1, PageSize: 25}, ListOptions: db.ListOptions{Page: 1, PageSize: 25},
} }
members, _, err := organization.FindOrgMembers(ctx, opts) members, _, err := organization.FindOrgMembers(ctx, opts)
if err != nil { if err != nil {
ctx.ServerError("FindOrgMembers", err) ctx.ServerError("FindOrgMembers", err)

View file

@ -33,8 +33,8 @@ func Members(ctx *context.Context) {
} }
opts := &organization.FindOrgMembersOpts{ opts := &organization.FindOrgMembersOpts{
Doer: ctx.Doer,
OrgID: org.ID, OrgID: org.ID,
PublicOnly: true,
} }
if ctx.Doer != nil { if ctx.Doer != nil {
@ -43,9 +43,9 @@ func Members(ctx *context.Context) {
ctx.Error(http.StatusInternalServerError, "IsOrgMember") ctx.Error(http.StatusInternalServerError, "IsOrgMember")
return return
} }
opts.PublicOnly = !isMember && !ctx.Doer.IsAdmin opts.IsDoerMember = isMember
} }
ctx.Data["PublicOnly"] = opts.PublicOnly ctx.Data["PublicOnly"] = opts.PublicOnly()
total, err := organization.CountOrgMembers(ctx, opts) total, err := organization.CountOrgMembers(ctx, opts)
if err != nil { if err != nil {

View file

@ -26,7 +26,6 @@ type Organization struct {
Organization *organization.Organization Organization *organization.Organization
OrgLink string OrgLink string
CanCreateOrgRepo bool CanCreateOrgRepo bool
PublicMemberOnly bool // Only display public members
Team *organization.Team Team *organization.Team
Teams []*organization.Team Teams []*organization.Team
@ -176,10 +175,10 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
ctx.Data["OrgLink"] = ctx.Org.OrgLink ctx.Data["OrgLink"] = ctx.Org.OrgLink
// Member // Member
ctx.Org.PublicMemberOnly = ctx.Doer == nil || !ctx.Org.IsMember && !ctx.Doer.IsAdmin
opts := &organization.FindOrgMembersOpts{ opts := &organization.FindOrgMembersOpts{
Doer: ctx.Doer,
OrgID: org.ID, OrgID: org.ID,
PublicOnly: ctx.Org.PublicMemberOnly, IsDoerMember: ctx.Org.IsMember,
} }
ctx.Data["NumMembers"], err = organization.CountOrgMembers(ctx, opts) ctx.Data["NumMembers"], err = organization.CountOrgMembers(ctx, opts)
if err != nil { if err != nil {

View file

@ -75,8 +75,9 @@ func (countTest *userCountTest) Init(t *testing.T, doerID, userID int64) {
require.NoError(t, err) require.NoError(t, err)
countTest.memberCount, err = organization.CountOrgMembers(db.DefaultContext, &organization.FindOrgMembersOpts{ countTest.memberCount, err = organization.CountOrgMembers(db.DefaultContext, &organization.FindOrgMembersOpts{
Doer: countTest.doer,
OrgID: org.ID, OrgID: org.ID,
PublicOnly: !isMember, IsDoerMember: isMember,
}) })
require.NoError(t, err) require.NoError(t, err)