0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-07-21 01:38:30 +02:00

Merge branch 'main' into dev/hezi/fix-skipped-icon

This commit is contained in:
wxiaoguang 2025-05-28 03:47:33 +08:00 committed by GitHub
commit e9b8a15000
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 342 additions and 304 deletions

View File

@ -137,7 +137,7 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
// SearchUsers takes options i.e. keyword and part of user name to search, // SearchUsers takes options i.e. keyword and part of user name to search,
// it returns results in given range and number of total results. // it returns results in given range and number of total results.
func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _ int64, _ error) { func SearchUsers(ctx context.Context, opts SearchUserOptions) (users []*User, _ int64, _ error) {
sessCount := opts.toSearchQueryBase(ctx) sessCount := opts.toSearchQueryBase(ctx)
defer sessCount.Close() defer sessCount.Close()
count, err := sessCount.Count(new(User)) count, err := sessCount.Count(new(User))
@ -152,7 +152,7 @@ func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _
sessQuery := opts.toSearchQueryBase(ctx).OrderBy(opts.OrderBy.String()) sessQuery := opts.toSearchQueryBase(ctx).OrderBy(opts.OrderBy.String())
defer sessQuery.Close() defer sessQuery.Close()
if opts.Page > 0 { if opts.Page > 0 {
sessQuery = db.SetSessionPagination(sessQuery, opts) sessQuery = db.SetSessionPagination(sessQuery, &opts)
} }
// the sql may contain JOIN, so we must only select User related columns // the sql may contain JOIN, so we must only select User related columns

View File

@ -88,7 +88,7 @@ func TestCanCreateOrganization(t *testing.T) {
func TestSearchUsers(t *testing.T) { func TestSearchUsers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(opts *user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) { testSuccess := func(opts user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) {
users, _, err := user_model.SearchUsers(db.DefaultContext, opts) users, _, err := user_model.SearchUsers(db.DefaultContext, opts)
assert.NoError(t, err) assert.NoError(t, err)
cassText := fmt.Sprintf("ids: %v, opts: %v", expectedUserOrOrgIDs, opts) cassText := fmt.Sprintf("ids: %v, opts: %v", expectedUserOrOrgIDs, opts)
@ -100,61 +100,61 @@ func TestSearchUsers(t *testing.T) {
} }
// test orgs // test orgs
testOrgSuccess := func(opts *user_model.SearchUserOptions, expectedOrgIDs []int64) { testOrgSuccess := func(opts user_model.SearchUserOptions, expectedOrgIDs []int64) {
opts.Type = user_model.UserTypeOrganization opts.Type = user_model.UserTypeOrganization
testSuccess(opts, expectedOrgIDs) testSuccess(opts, expectedOrgIDs)
} }
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}}, testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}},
[]int64{3, 6}) []int64{3, 6})
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}}, testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}},
[]int64{7, 17}) []int64{7, 17})
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}}, testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}},
[]int64{19, 25}) []int64{19, 25})
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}}, testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}},
[]int64{26, 41}) []int64{26, 41})
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 5, PageSize: 2}}, testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 5, PageSize: 2}},
[]int64{42}) []int64{42})
testOrgSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 6, PageSize: 2}}, testOrgSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 6, PageSize: 2}},
[]int64{}) []int64{})
// test users // test users
testUserSuccess := func(opts *user_model.SearchUserOptions, expectedUserIDs []int64) { testUserSuccess := func(opts user_model.SearchUserOptions, expectedUserIDs []int64) {
opts.Type = user_model.UserTypeIndividual opts.Type = user_model.UserTypeIndividual
testSuccess(opts, expectedUserIDs) testSuccess(opts, expectedUserIDs)
} }
testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}}, testUserSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},
[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40}) []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)}, testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},
[]int64{9}) []int64{9})
testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)}, testUserSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40}) []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)}, testUserSuccess(user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) []int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
// order by name asc default // order by name asc default
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)}, testUserSuccess(user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) []int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)}, testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},
[]int64{1}) []int64{1})
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)}, testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},
[]int64{29}) []int64{29})
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)}, testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},
[]int64{37}) []int64{37})
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)}, testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
[]int64{24}) []int64{24})
} }

View File

@ -34,7 +34,7 @@ type Commit struct {
// CommitSignature represents a git commit signature part. // CommitSignature represents a git commit signature part.
type CommitSignature struct { type CommitSignature struct {
Signature string Signature string
Payload string // TODO check if can be reconstruct from the rest of commit information to not have duplicate data Payload string
} }
// Message returns the commit message. Same as retrieving CommitMessage directly. // Message returns the commit message. Same as retrieving CommitMessage directly.

View File

@ -6,10 +6,44 @@ package git
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"fmt"
"io" "io"
"strings"
) )
const (
commitHeaderGpgsig = "gpgsig"
commitHeaderGpgsigSha256 = "gpgsig-sha256"
)
func assignCommitFields(gitRepo *Repository, commit *Commit, headerKey string, headerValue []byte) error {
if len(headerValue) > 0 && headerValue[len(headerValue)-1] == '\n' {
headerValue = headerValue[:len(headerValue)-1] // remove trailing newline
}
switch headerKey {
case "tree":
objID, err := NewIDFromString(string(headerValue))
if err != nil {
return fmt.Errorf("invalid tree ID %q: %w", string(headerValue), err)
}
commit.Tree = *NewTree(gitRepo, objID)
case "parent":
objID, err := NewIDFromString(string(headerValue))
if err != nil {
return fmt.Errorf("invalid parent ID %q: %w", string(headerValue), err)
}
commit.Parents = append(commit.Parents, objID)
case "author":
commit.Author.Decode(headerValue)
case "committer":
commit.Committer.Decode(headerValue)
case commitHeaderGpgsig, commitHeaderGpgsigSha256:
// if there are duplicate "gpgsig" and "gpgsig-sha256" headers, then the signature must have already been invalid
// so we don't need to handle duplicate headers here
commit.Signature = &CommitSignature{Signature: string(headerValue)}
}
return nil
}
// CommitFromReader will generate a Commit from a provided reader // CommitFromReader will generate a Commit from a provided reader
// We need this to interpret commits from cat-file or cat-file --batch // We need this to interpret commits from cat-file or cat-file --batch
// //
@ -21,90 +55,46 @@ func CommitFromReader(gitRepo *Repository, objectID ObjectID, reader io.Reader)
Committer: &Signature{}, Committer: &Signature{},
} }
payloadSB := new(strings.Builder) bufReader := bufio.NewReader(reader)
signatureSB := new(strings.Builder) inHeader := true
messageSB := new(strings.Builder) var payloadSB, messageSB bytes.Buffer
message := false var headerKey string
pgpsig := false var headerValue []byte
bufReader, ok := reader.(*bufio.Reader)
if !ok {
bufReader = bufio.NewReader(reader)
}
readLoop:
for { for {
line, err := bufReader.ReadBytes('\n') line, err := bufReader.ReadBytes('\n')
if err != nil { if err != nil && err != io.EOF {
if err == io.EOF { return nil, fmt.Errorf("unable to read commit %q: %w", objectID.String(), err)
if message {
_, _ = messageSB.Write(line)
} }
_, _ = payloadSB.Write(line) if len(line) == 0 {
break readLoop break
}
return nil, err
}
if pgpsig {
if len(line) > 0 && line[0] == ' ' {
_, _ = signatureSB.Write(line[1:])
continue
}
pgpsig = false
} }
if !message { if inHeader {
// This is probably not correct but is copied from go-gits interpretation... inHeader = !(len(line) == 1 && line[0] == '\n') // still in header if line is not just a newline
trimmed := bytes.TrimSpace(line) k, v, _ := bytes.Cut(line, []byte{' '})
if len(trimmed) == 0 { if len(k) != 0 || !inHeader {
message = true if headerKey != "" {
_, _ = payloadSB.Write(line) if err = assignCommitFields(gitRepo, commit, headerKey, headerValue); err != nil {
continue return nil, fmt.Errorf("unable to parse commit %q: %w", objectID.String(), err)
} }
split := bytes.SplitN(trimmed, []byte{' '}, 2)
var data []byte
if len(split) > 1 {
data = split[1]
} }
headerKey = string(k) // it also resets the headerValue to empty string if not inHeader
switch string(split[0]) { headerValue = v
case "tree": } else {
commit.Tree = *NewTree(gitRepo, MustIDFromString(string(data))) headerValue = append(headerValue, v...)
}
if headerKey != commitHeaderGpgsig && headerKey != commitHeaderGpgsigSha256 {
_, _ = payloadSB.Write(line) _, _ = payloadSB.Write(line)
case "parent":
commit.Parents = append(commit.Parents, MustIDFromString(string(data)))
_, _ = payloadSB.Write(line)
case "author":
commit.Author = &Signature{}
commit.Author.Decode(data)
_, _ = payloadSB.Write(line)
case "committer":
commit.Committer = &Signature{}
commit.Committer.Decode(data)
_, _ = payloadSB.Write(line)
case "encoding":
_, _ = payloadSB.Write(line)
case "gpgsig":
fallthrough
case "gpgsig-sha256": // FIXME: no intertop, so only 1 exists at present.
_, _ = signatureSB.Write(data)
_ = signatureSB.WriteByte('\n')
pgpsig = true
} }
} else { } else {
_, _ = messageSB.Write(line) _, _ = messageSB.Write(line)
_, _ = payloadSB.Write(line) _, _ = payloadSB.Write(line)
} }
} }
commit.CommitMessage = messageSB.String()
commit.Signature = &CommitSignature{
Signature: signatureSB.String(),
Payload: payloadSB.String(),
}
if len(commit.Signature.Signature) == 0 {
commit.Signature = nil
}
commit.CommitMessage = messageSB.String()
if commit.Signature != nil {
commit.Signature.Payload = payloadSB.String()
}
return commit, nil return commit, nil
} }

View File

@ -60,8 +60,7 @@ func TestGetFullCommitIDErrorSha256(t *testing.T) {
} }
func TestCommitFromReaderSha256(t *testing.T) { func TestCommitFromReaderSha256(t *testing.T) {
commitString := `9433b2a62b964c17a4485ae180f45f595d3e69d31b786087775e28c6b6399df0 commit 1114 commitString := `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8 parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8
author Adam Majer <amajer@suse.de> 1698676906 +0100 author Adam Majer <amajer@suse.de> 1698676906 +0100
committer Adam Majer <amajer@suse.de> 1698676906 +0100 committer Adam Majer <amajer@suse.de> 1698676906 +0100
@ -112,8 +111,7 @@ VAEUo6ecdDxSpyt2naeg9pKus/BRi7P6g4B1hkk/zZstUX/QP4IQuAJbXjkvsC+X
HKRr3NlRM/DygzTyj0gN74uoa0goCIbyAQhiT42nm0cuhM7uN/W0ayrlZjGF1cbR HKRr3NlRM/DygzTyj0gN74uoa0goCIbyAQhiT42nm0cuhM7uN/W0ayrlZjGF1cbR
8NCJUL2Nwj0ywKIavC99Ipkb8AsFwpVT6U6effs6 8NCJUL2Nwj0ywKIavC99Ipkb8AsFwpVT6U6effs6
=xybZ =xybZ
-----END PGP SIGNATURE----- -----END PGP SIGNATURE-----`, commitFromReader.Signature.Signature)
`, commitFromReader.Signature.Signature)
assert.Equal(t, `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e assert.Equal(t, `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8 parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8
author Adam Majer <amajer@suse.de> 1698676906 +0100 author Adam Majer <amajer@suse.de> 1698676906 +0100

View File

@ -59,8 +59,7 @@ func TestGetFullCommitIDError(t *testing.T) {
} }
func TestCommitFromReader(t *testing.T) { func TestCommitFromReader(t *testing.T) {
commitString := `feaf4ba6bc635fec442f46ddd4512416ec43c2c2 commit 1074 commitString := `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
parent 37991dec2c8e592043f47155ce4808d4580f9123 parent 37991dec2c8e592043f47155ce4808d4580f9123
author silverwind <me@silverwind.io> 1563741793 +0200 author silverwind <me@silverwind.io> 1563741793 +0200
committer silverwind <me@silverwind.io> 1563741793 +0200 committer silverwind <me@silverwind.io> 1563741793 +0200
@ -108,8 +107,7 @@ sD53z/f0J+We4VZjY+pidvA9BGZPFVdR3wd3xGs8/oH6UWaLJAMGkLG6dDb3qDLm
mfeFhT57UbE4qukTDIQ0Y0WM40UYRTakRaDY7ubhXgLgx09Cnp9XTVMsHgT6j9/i mfeFhT57UbE4qukTDIQ0Y0WM40UYRTakRaDY7ubhXgLgx09Cnp9XTVMsHgT6j9/i
1pxsB104XLWjQHTjr1JtiaBQEwFh9r2OKTcpvaLcbNtYpo7CzOs= 1pxsB104XLWjQHTjr1JtiaBQEwFh9r2OKTcpvaLcbNtYpo7CzOs=
=FRsO =FRsO
-----END PGP SIGNATURE----- -----END PGP SIGNATURE-----`, commitFromReader.Signature.Signature)
`, commitFromReader.Signature.Signature)
assert.Equal(t, `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930 assert.Equal(t, `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
parent 37991dec2c8e592043f47155ce4808d4580f9123 parent 37991dec2c8e592043f47155ce4808d4580f9123
author silverwind <me@silverwind.io> 1563741793 +0200 author silverwind <me@silverwind.io> 1563741793 +0200
@ -126,8 +124,7 @@ empty commit`, commitFromReader.Signature.Payload)
} }
func TestCommitWithEncodingFromReader(t *testing.T) { func TestCommitWithEncodingFromReader(t *testing.T) {
commitString := `feaf4ba6bc635fec442f46ddd4512416ec43c2c2 commit 1074 commitString := `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
parent 47b24e7ab977ed31c5a39989d570847d6d0052af parent 47b24e7ab977ed31c5a39989d570847d6d0052af
author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100 author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
committer KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100 committer KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
@ -172,8 +169,7 @@ SONRzusmu5n3DgV956REL7x62h7JuqmBz/12HZkr0z0zgXkcZ04q08pSJATX5N1F
yN+tWxTsWg+zhDk96d5Esdo9JMjcFvPv0eioo30GAERaz1hoD7zCMT4jgUFTQwgz yN+tWxTsWg+zhDk96d5Esdo9JMjcFvPv0eioo30GAERaz1hoD7zCMT4jgUFTQwgz
jw4YcO5u jw4YcO5u
=r3UU =r3UU
-----END PGP SIGNATURE----- -----END PGP SIGNATURE-----`, commitFromReader.Signature.Signature)
`, commitFromReader.Signature.Signature)
assert.Equal(t, `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5 assert.Equal(t, `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
parent 47b24e7ab977ed31c5a39989d570847d6d0052af parent 47b24e7ab977ed31c5a39989d570847d6d0052af
author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100 author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100

View File

@ -101,7 +101,7 @@ func GetAllOrgs(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx) listOptions := utils.GetListOptions(ctx)
users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ users, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Type: user_model.UserTypeOrganization, Type: user_model.UserTypeOrganization,
OrderBy: db.SearchOrderByAlphabetically, OrderBy: db.SearchOrderByAlphabetically,

View File

@ -423,7 +423,7 @@ func SearchUsers(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx) listOptions := utils.GetListOptions(ctx)
users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ users, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Type: user_model.UserTypeIndividual, Type: user_model.UserTypeIndividual,
LoginName: ctx.FormTrim("login_name"), LoginName: ctx.FormTrim("login_name"),

View File

@ -201,7 +201,7 @@ func GetAll(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx) listOptions := utils.GetListOptions(ctx)
publicOrgs, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ publicOrgs, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
ListOptions: listOptions, ListOptions: listOptions,
Type: user_model.UserTypeOrganization, Type: user_model.UserTypeOrganization,

View File

@ -73,7 +73,7 @@ func Search(ctx *context.APIContext) {
if ctx.PublicOnly { if ctx.PublicOnly {
visible = []structs.VisibleType{structs.VisibleTypePublic} visible = []structs.VisibleType{structs.VisibleTypePublic}
} }
users, maxResults, err = user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ users, maxResults, err = user_model.SearchUsers(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Keyword: ctx.FormTrim("q"), Keyword: ctx.FormTrim("q"),
UID: uid, UID: uid,

View File

@ -27,7 +27,7 @@ func Organizations(ctx *context.Context) {
ctx.SetFormString("sort", UserSearchDefaultAdminSort) ctx.SetFormString("sort", UserSearchDefaultAdminSort)
} }
explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{ explore.RenderUserSearch(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Type: user_model.UserTypeOrganization, Type: user_model.UserTypeOrganization,
IncludeReserved: true, // administrator needs to list all accounts include reserved IncludeReserved: true, // administrator needs to list all accounts include reserved

View File

@ -64,7 +64,7 @@ func Users(ctx *context.Context) {
"SortType": sortType, "SortType": sortType,
} }
explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{ explore.RenderUserSearch(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Type: user_model.UserTypeIndividual, Type: user_model.UserTypeIndividual,
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{

View File

@ -44,7 +44,7 @@ func Organizations(ctx *context.Context) {
ctx.SetFormString("sort", sortOrder) ctx.SetFormString("sort", sortOrder)
} }
RenderUserSearch(ctx, &user_model.SearchUserOptions{ RenderUserSearch(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Type: user_model.UserTypeOrganization, Type: user_model.UserTypeOrganization,
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum}, ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},

View File

@ -32,7 +32,7 @@ func isKeywordValid(keyword string) bool {
} }
// RenderUserSearch render user search page // RenderUserSearch render user search page
func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, tplName templates.TplName) { func RenderUserSearch(ctx *context.Context, opts user_model.SearchUserOptions, tplName templates.TplName) {
// Sitemap index for sitemap paths // Sitemap index for sitemap paths
opts.Page = int(ctx.PathParamInt64("idx")) opts.Page = int(ctx.PathParamInt64("idx"))
isSitemap := ctx.PathParam("idx") != "" isSitemap := ctx.PathParam("idx") != ""
@ -151,7 +151,7 @@ func Users(ctx *context.Context) {
ctx.SetFormString("sort", sortOrder) ctx.SetFormString("sort", sortOrder)
} }
RenderUserSearch(ctx, &user_model.SearchUserOptions{ RenderUserSearch(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Type: user_model.UserTypeIndividual, Type: user_model.UserTypeIndividual,
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum}, ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},

View File

@ -68,7 +68,7 @@ func Home(ctx *context.Context) {
func HomeSitemap(ctx *context.Context) { func HomeSitemap(ctx *context.Context) {
m := sitemap.NewSitemapIndex() m := sitemap.NewSitemapIndex()
if !setting.Service.Explore.DisableUsersPage { if !setting.Service.Explore.DisableUsersPage {
_, cnt, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ _, cnt, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
Type: user_model.UserTypeIndividual, Type: user_model.UserTypeIndividual,
ListOptions: db.ListOptions{PageSize: 1}, ListOptions: db.ListOptions{PageSize: 1},
IsActive: optional.Some(true), IsActive: optional.Some(true),

View File

@ -16,7 +16,7 @@ import (
// SearchCandidates searches candidate users for dropdown list // SearchCandidates searches candidate users for dropdown list
func SearchCandidates(ctx *context.Context) { func SearchCandidates(ctx *context.Context) {
users, _, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ users, _, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Keyword: ctx.FormTrim("q"), Keyword: ctx.FormTrim("q"),
Type: user_model.UserTypeIndividual, Type: user_model.UserTypeIndividual,

View File

@ -54,6 +54,8 @@ func ToggleAssigneeWithNotify(ctx context.Context, issue *issues_model.Issue, do
if err != nil { if err != nil {
return false, nil, err return false, nil, err
} }
issue.AssigneeID = assigneeID
issue.Assignee = assignee
notify_service.IssueChangeAssignee(ctx, doer, issue, assignee, removed, comment) notify_service.IssueChangeAssignee(ctx, doer, issue, assignee, removed, comment)

View File

@ -180,11 +180,15 @@ func (c *CodeCommitDownloader) GetPullRequests(ctx context.Context, page, perPag
continue continue
} }
target := orig.PullRequestTargets[0] target := orig.PullRequestTargets[0]
description := ""
if orig.Description != nil {
description = *orig.Description
}
pr := &base.PullRequest{ pr := &base.PullRequest{
Number: number, Number: number,
Title: *orig.Title, Title: *orig.Title,
PosterName: c.getUsernameFromARN(*orig.AuthorArn), PosterName: c.getUsernameFromARN(*orig.AuthorArn),
Content: *orig.Description, Content: description,
State: "open", State: "open",
Created: *orig.CreationDate, Created: *orig.CreationDate,
Updated: *orig.LastActivityDate, Updated: *orig.LastActivityDate,

View File

@ -151,6 +151,15 @@ func testNewIssue(t *testing.T, session *TestSession, user, repo, title, content
return issueURL return issueURL
} }
func testIssueAssign(t *testing.T, session *TestSession, repoLink string, issueID, assigneeID int64) {
req := NewRequestWithValues(t, "POST", fmt.Sprintf(repoLink+"/issues/assignee?issue_ids=%d", issueID), map[string]string{
"_csrf": GetUserCSRFToken(t, session),
"id": strconv.FormatInt(assigneeID, 10),
"action": "", // empty action means assign
})
session.MakeRequest(t, req, http.StatusOK)
}
func testIssueAddComment(t *testing.T, session *TestSession, issueURL, content, status string) int64 { func testIssueAddComment(t *testing.T, session *TestSession, issueURL, content, status string) int64 {
req := NewRequest(t, "GET", issueURL) req := NewRequest(t, "GET", issueURL)
resp := session.MakeRequest(t, req, http.StatusOK) resp := session.MakeRequest(t, req, http.StatusOK)

View File

@ -131,6 +131,7 @@ func (m *mockWebhookProvider) Close() {
} }
func Test_WebhookCreate(t *testing.T) { func Test_WebhookCreate(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.CreatePayload var payloads []api.CreatePayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -143,7 +144,6 @@ func Test_WebhookCreate(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2") session := loginUser(t, "user2")
@ -163,6 +163,7 @@ func Test_WebhookCreate(t *testing.T) {
} }
func Test_WebhookDelete(t *testing.T) { func Test_WebhookDelete(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.DeletePayload var payloads []api.DeletePayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -175,7 +176,6 @@ func Test_WebhookDelete(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2") session := loginUser(t, "user2")
@ -196,6 +196,7 @@ func Test_WebhookDelete(t *testing.T) {
} }
func Test_WebhookFork(t *testing.T) { func Test_WebhookFork(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.ForkPayload var payloads []api.ForkPayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -208,7 +209,6 @@ func Test_WebhookFork(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user1") session := loginUser(t, "user1")
@ -228,6 +228,7 @@ func Test_WebhookFork(t *testing.T) {
} }
func Test_WebhookIssueComment(t *testing.T) { func Test_WebhookIssueComment(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.IssueCommentPayload var payloads []api.IssueCommentPayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -240,7 +241,6 @@ func Test_WebhookIssueComment(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2") session := loginUser(t, "user2")
@ -312,6 +312,7 @@ func Test_WebhookIssueComment(t *testing.T) {
} }
func Test_WebhookRelease(t *testing.T) { func Test_WebhookRelease(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.ReleasePayload var payloads []api.ReleasePayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -324,7 +325,6 @@ func Test_WebhookRelease(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2") session := loginUser(t, "user2")
@ -345,6 +345,7 @@ func Test_WebhookRelease(t *testing.T) {
} }
func Test_WebhookPush(t *testing.T) { func Test_WebhookPush(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.PushPayload var payloads []api.PushPayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -357,7 +358,6 @@ func Test_WebhookPush(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2") session := loginUser(t, "user2")
@ -416,6 +416,7 @@ func Test_WebhookPushDevBranch(t *testing.T) {
} }
func Test_WebhookIssue(t *testing.T) { func Test_WebhookIssue(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.IssuePayload var payloads []api.IssuePayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -428,7 +429,6 @@ func Test_WebhookIssue(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2") session := loginUser(t, "user2")
@ -445,6 +445,45 @@ func Test_WebhookIssue(t *testing.T) {
assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName) assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
assert.Equal(t, "Title1", payloads[0].Issue.Title) assert.Equal(t, "Title1", payloads[0].Issue.Title)
assert.Equal(t, "Description1", payloads[0].Issue.Body) assert.Equal(t, "Description1", payloads[0].Issue.Body)
assert.Positive(t, payloads[0].Issue.Created.Unix())
assert.Positive(t, payloads[0].Issue.Updated.Unix())
})
}
func Test_WebhookIssueAssign(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.PullRequestPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.PullRequestPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "pull_request_assign"
}, http.StatusOK)
defer provider.Close()
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "pull_request_assign")
// 2. trigger the webhook, issue 2 is a pull request
testIssueAssign(t, session, repo1.Link(), 2, user2.ID)
// 3. validate the webhook is triggered
assert.Equal(t, "pull_request_assign", triggeredEvent)
assert.Len(t, payloads, 1)
assert.EqualValues(t, "assigned", payloads[0].Action)
assert.Equal(t, "repo1", payloads[0].PullRequest.Base.Repository.Name)
assert.Equal(t, "user2/repo1", payloads[0].PullRequest.Base.Repository.FullName)
assert.Equal(t, "issue2", payloads[0].PullRequest.Title)
assert.Equal(t, "content for the second issue", payloads[0].PullRequest.Body)
assert.Equal(t, user2.ID, payloads[0].PullRequest.Assignee.ID)
}) })
} }
@ -521,6 +560,7 @@ func Test_WebhookIssueMilestone(t *testing.T) {
} }
func Test_WebhookPullRequest(t *testing.T) { func Test_WebhookPullRequest(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.PullRequestPayload var payloads []api.PullRequestPayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -533,7 +573,6 @@ func Test_WebhookPullRequest(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2") session := loginUser(t, "user2")
@ -558,6 +597,7 @@ func Test_WebhookPullRequest(t *testing.T) {
} }
func Test_WebhookPullRequestComment(t *testing.T) { func Test_WebhookPullRequestComment(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.IssueCommentPayload var payloads []api.IssueCommentPayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -570,7 +610,6 @@ func Test_WebhookPullRequestComment(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2") session := loginUser(t, "user2")
@ -596,6 +635,7 @@ func Test_WebhookPullRequestComment(t *testing.T) {
} }
func Test_WebhookWiki(t *testing.T) { func Test_WebhookWiki(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.WikiPayload var payloads []api.WikiPayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -608,7 +648,6 @@ func Test_WebhookWiki(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2") session := loginUser(t, "user2")
@ -628,6 +667,7 @@ func Test_WebhookWiki(t *testing.T) {
} }
func Test_WebhookRepository(t *testing.T) { func Test_WebhookRepository(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.RepositoryPayload var payloads []api.RepositoryPayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -640,7 +680,6 @@ func Test_WebhookRepository(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user1") session := loginUser(t, "user1")
@ -660,6 +699,7 @@ func Test_WebhookRepository(t *testing.T) {
} }
func Test_WebhookPackage(t *testing.T) { func Test_WebhookPackage(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.PackagePayload var payloads []api.PackagePayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -672,7 +712,6 @@ func Test_WebhookPackage(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user1") session := loginUser(t, "user1")
@ -697,6 +736,7 @@ func Test_WebhookPackage(t *testing.T) {
} }
func Test_WebhookStatus(t *testing.T) { func Test_WebhookStatus(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.CommitStatusPayload var payloads []api.CommitStatusPayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -714,7 +754,6 @@ func Test_WebhookStatus(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2") session := loginUser(t, "user2")
@ -750,6 +789,7 @@ func Test_WebhookStatus(t *testing.T) {
} }
func Test_WebhookStatus_NoWrongTrigger(t *testing.T) { func Test_WebhookStatus_NoWrongTrigger(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var trigger string var trigger string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
assert.NotContains(t, r.Header["X-Github-Event-Type"], "status", "X-GitHub-Event-Type should not contain status") assert.NotContains(t, r.Header["X-Github-Event-Type"], "status", "X-GitHub-Event-Type should not contain status")
@ -759,7 +799,6 @@ func Test_WebhookStatus_NoWrongTrigger(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2") session := loginUser(t, "user2")
@ -775,6 +814,7 @@ func Test_WebhookStatus_NoWrongTrigger(t *testing.T) {
} }
func Test_WebhookWorkflowJob(t *testing.T) { func Test_WebhookWorkflowJob(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.WorkflowJobPayload var payloads []api.WorkflowJobPayload
var triggeredEvent string var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) { provider := newMockWebhookProvider(func(r *http.Request) {
@ -790,7 +830,6 @@ func Test_WebhookWorkflowJob(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, "user2") session := loginUser(t, "user2")