Skip to content

Commit cc8215a

Browse files
committed
Merge remote-tracking branch 'giteaofficial/main'
* giteaofficial/main: (32 commits) [skip ci] Updated translations via Crowdin Prevent double use of `git cat-file` session. (go-gitea#29298) Revert go-gitea#28753 because UI broken. (go-gitea#29293) Fix error display when merging PRs (go-gitea#29288) Refactor markup rendering to accept general "protocol:" prefix (go-gitea#29276) Remove jQuery from the installation page (go-gitea#29284) Always write proc-receive hook for all git versions (go-gitea#29287) Do not use `ctx.Doer` when reset password (go-gitea#29289) Update Discord logo (go-gitea#29285) [skip ci] Updated translations via Crowdin Remove jQuery .map() and enable eslint rules for it (go-gitea#29272) Explained where create issue/PR template (go-gitea#29035) (go-gitea#29266) Remove jQuery from repo wiki creation page (go-gitea#29271) Do not show delete button when time tracker is disabled (go-gitea#29257) Left align the input labels for the link account page (go-gitea#29255) [skip ci] Updated translations via Crowdin Remove jQuery from the repo migration form (go-gitea#29229) Fix content size does not match error when uploading lfs file (go-gitea#29259) Workaround to clean up old reviews on creating a new one (go-gitea#28554) Deduplicate translations for contributors graph (go-gitea#29256) ...
2 parents 530f74f + 2bd999a commit cc8215a

File tree

107 files changed

+1642
-536
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+1642
-536
lines changed

.eslintrc.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ rules:
296296
jquery/no-delegate: [2]
297297
jquery/no-each: [0]
298298
jquery/no-extend: [2]
299-
jquery/no-fade: [0]
299+
jquery/no-fade: [2]
300300
jquery/no-filter: [0]
301301
jquery/no-find: [0]
302302
jquery/no-global-eval: [2]
@@ -309,7 +309,7 @@ rules:
309309
jquery/no-is-function: [2]
310310
jquery/no-is: [0]
311311
jquery/no-load: [2]
312-
jquery/no-map: [0]
312+
jquery/no-map: [2]
313313
jquery/no-merge: [2]
314314
jquery/no-param: [2]
315315
jquery/no-parent: [0]
@@ -451,7 +451,7 @@ rules:
451451
no-jquery/no-load: [2]
452452
no-jquery/no-map-collection: [0]
453453
no-jquery/no-map-util: [2]
454-
no-jquery/no-map: [0]
454+
no-jquery/no-map: [2]
455455
no-jquery/no-merge: [2]
456456
no-jquery/no-node-name: [2]
457457
no-jquery/no-noop: [2]

docs/content/usage/issue-pull-request-templates.en-us.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ menu:
1919

2020
Some projects have a standard list of questions that users need to answer
2121
when creating an issue or pull request. Gitea supports adding templates to the
22-
main branch of the repository so that they can autopopulate the form when users are
22+
**default branch of the repository** so that they can autopopulate the form when users are
2323
creating issues and pull requests. This will cut down on the initial back and forth
2424
of getting some clarifying details.
25+
It is currently not possible to provide generic issue/pull-request templates globally.
2526

2627
Additionally, the New Issue page URL can be suffixed with `?title=Issue+Title&body=Issue+Text` and the form will be populated with those strings. Those strings will be used instead of the template if there is one.
2728

models/issues/review.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,14 @@ func IsOfficialReviewerTeam(ctx context.Context, issue *Issue, team *organizatio
292292

293293
// CreateReview creates a new review based on opts
294294
func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error) {
295+
ctx, committer, err := db.TxContext(ctx)
296+
if err != nil {
297+
return nil, err
298+
}
299+
defer committer.Close()
300+
sess := db.GetEngine(ctx)
301+
295302
review := &Review{
296-
Type: opts.Type,
297303
Issue: opts.Issue,
298304
IssueID: opts.Issue.ID,
299305
Reviewer: opts.Reviewer,
@@ -303,15 +309,39 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error
303309
CommitID: opts.CommitID,
304310
Stale: opts.Stale,
305311
}
312+
306313
if opts.Reviewer != nil {
314+
review.Type = opts.Type
307315
review.ReviewerID = opts.Reviewer.ID
308-
} else {
309-
if review.Type != ReviewTypeRequest {
310-
review.Type = ReviewTypeRequest
316+
317+
reviewCond := builder.Eq{"reviewer_id": opts.Reviewer.ID, "issue_id": opts.Issue.ID}
318+
// make sure user review requests are cleared
319+
if opts.Type != ReviewTypePending {
320+
if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil {
321+
return nil, err
322+
}
311323
}
324+
// make sure if the created review gets dismissed no old review surface
325+
// other types can be ignored, as they don't affect branch protection
326+
if opts.Type == ReviewTypeApprove || opts.Type == ReviewTypeReject {
327+
if _, err := sess.Where(reviewCond.And(builder.In("type", ReviewTypeApprove, ReviewTypeReject))).
328+
Cols("dismissed").Update(&Review{Dismissed: true}); err != nil {
329+
return nil, err
330+
}
331+
}
332+
333+
} else if opts.ReviewerTeam != nil {
334+
review.Type = ReviewTypeRequest
312335
review.ReviewerTeamID = opts.ReviewerTeam.ID
336+
337+
} else {
338+
return nil, fmt.Errorf("provide either reviewer or reviewer team")
339+
}
340+
341+
if _, err := sess.Insert(review); err != nil {
342+
return nil, err
313343
}
314-
return review, db.Insert(ctx, review)
344+
return review, committer.Commit()
315345
}
316346

317347
// GetCurrentReview returns the current pending review of reviewer for given issue

models/unittest/unit_tests.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ func AssertSuccessfulInsert(t assert.TestingT, beans ...any) {
131131
}
132132

133133
// AssertCount assert the count of a bean
134-
func AssertCount(t assert.TestingT, bean, expected any) {
135-
assert.EqualValues(t, expected, GetCount(t, bean))
134+
func AssertCount(t assert.TestingT, bean, expected any) bool {
135+
return assert.EqualValues(t, expected, GetCount(t, bean))
136136
}
137137

138138
// AssertInt64InRange assert value is in range [low, high]
@@ -150,7 +150,7 @@ func GetCountByCond(t assert.TestingT, tableName string, cond builder.Cond) int6
150150
}
151151

152152
// AssertCountByCond test the count of database entries matching bean
153-
func AssertCountByCond(t assert.TestingT, tableName string, cond builder.Cond, expected int) {
154-
assert.EqualValues(t, expected, GetCountByCond(t, tableName, cond),
153+
func AssertCountByCond(t assert.TestingT, tableName string, cond builder.Cond, expected int) bool {
154+
return assert.EqualValues(t, expected, GetCountByCond(t, tableName, cond),
155155
"Failed consistency test, the counted bean (of table %s) was %+v", tableName, cond)
156156
}

modules/base/tool.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func CreateTimeLimitCode(data string, minutes int, startInf any) string {
115115

116116
// create sha1 encode string
117117
sh := sha1.New()
118-
_, _ = sh.Write([]byte(fmt.Sprintf("%s%s%s%s%d", data, setting.SecretKey, startStr, endStr, minutes)))
118+
_, _ = sh.Write([]byte(fmt.Sprintf("%s%s%s%s%d", data, hex.EncodeToString(setting.GetGeneralTokenSigningSecret()), startStr, endStr, minutes)))
119119
encoded := hex.EncodeToString(sh.Sum(nil))
120120

121121
code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)

modules/context/context.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package context
66

77
import (
88
"context"
9+
"encoding/hex"
910
"fmt"
1011
"html/template"
1112
"io"
@@ -124,7 +125,7 @@ func NewWebContext(base *Base, render Render, session session.Store) *Context {
124125
func Contexter() func(next http.Handler) http.Handler {
125126
rnd := templates.HTMLRenderer()
126127
csrfOpts := CsrfOptions{
127-
Secret: setting.SecretKey,
128+
Secret: hex.EncodeToString(setting.GetGeneralTokenSigningSecret()),
128129
Cookie: setting.CSRFCookieName,
129130
SetCookie: true,
130131
Secure: setting.SessionConfig.Secure,

modules/context/context_template.go

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ package context
55

66
import (
77
"context"
8-
"errors"
98
"time"
10-
11-
"code.gitea.io/gitea/modules/log"
129
)
1310

1411
var _ context.Context = TemplateContext(nil)
@@ -36,14 +33,3 @@ func (c TemplateContext) Err() error {
3633
func (c TemplateContext) Value(key any) any {
3734
return c.parentContext().Value(key)
3835
}
39-
40-
// DataRaceCheck checks whether the template context function "ctx()" returns the consistent context
41-
// as the current template's rendering context (request context), to help to find data race issues as early as possible.
42-
// When the code is proven to be correct and stable, this function should be removed.
43-
func (c TemplateContext) DataRaceCheck(dataCtx context.Context) (string, error) {
44-
if c.parentContext() != dataCtx {
45-
log.Error("TemplateContext.DataRaceCheck: parent context mismatch\n%s", log.Stack(2))
46-
return "", errors.New("parent context mismatch")
47-
}
48-
return "", nil
49-
}

modules/git/repo_base_nogogit.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ type Repository struct {
2727

2828
gpgSettings *GPGSettings
2929

30+
batchInUse bool
3031
batchCancel context.CancelFunc
3132
batchReader *bufio.Reader
3233
batchWriter WriteCloserError
3334

35+
checkInUse bool
3436
checkCancel context.CancelFunc
3537
checkReader *bufio.Reader
3638
checkWriter WriteCloserError
@@ -79,23 +81,28 @@ func OpenRepository(ctx context.Context, repoPath string) (*Repository, error) {
7981

8082
// CatFileBatch obtains a CatFileBatch for this repository
8183
func (repo *Repository) CatFileBatch(ctx context.Context) (WriteCloserError, *bufio.Reader, func()) {
82-
if repo.batchCancel == nil || repo.batchReader.Buffered() > 0 {
84+
if repo.batchCancel == nil || repo.batchInUse {
8385
log.Debug("Opening temporary cat file batch for: %s", repo.Path)
8486
return CatFileBatch(ctx, repo.Path)
8587
}
86-
return repo.batchWriter, repo.batchReader, func() {}
88+
repo.batchInUse = true
89+
return repo.batchWriter, repo.batchReader, func() {
90+
repo.batchInUse = false
91+
}
8792
}
8893

8994
// CatFileBatchCheck obtains a CatFileBatchCheck for this repository
9095
func (repo *Repository) CatFileBatchCheck(ctx context.Context) (WriteCloserError, *bufio.Reader, func()) {
91-
if repo.checkCancel == nil || repo.checkReader.Buffered() > 0 {
92-
log.Debug("Opening temporary cat file batch-check: %s", repo.Path)
96+
if repo.checkCancel == nil || repo.checkInUse {
97+
log.Debug("Opening temporary cat file batch-check for: %s", repo.Path)
9398
return CatFileBatchCheck(ctx, repo.Path)
9499
}
95-
return repo.checkWriter, repo.checkReader, func() {}
100+
repo.checkInUse = true
101+
return repo.checkWriter, repo.checkReader, func() {
102+
repo.checkInUse = false
103+
}
96104
}
97105

98-
// Close this repository, in particular close the underlying gogitStorage if this is not nil
99106
func (repo *Repository) Close() (err error) {
100107
if repo == nil {
101108
return nil
@@ -105,12 +112,14 @@ func (repo *Repository) Close() (err error) {
105112
repo.batchReader = nil
106113
repo.batchWriter = nil
107114
repo.batchCancel = nil
115+
repo.batchInUse = false
108116
}
109117
if repo.checkCancel != nil {
110118
repo.checkCancel()
111119
repo.checkCancel = nil
112120
repo.checkReader = nil
113121
repo.checkWriter = nil
122+
repo.checkInUse = false
114123
}
115124
repo.LastCommitCache = nil
116125
repo.tagCache = nil

modules/markup/html.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,38 +53,38 @@ var (
5353
// shortLinkPattern matches short but difficult to parse [[name|link|arg=test]] syntax
5454
shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`)
5555

56-
// anySHA1Pattern splits url containing SHA into parts
56+
// anyHashPattern splits url containing SHA into parts
5757
anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40,64})(/[-+~_%.a-zA-Z0-9/]+)?(#[-+~_%.a-zA-Z0-9]+)?`)
5858

5959
// comparePattern matches "http://domain/org/repo/compare/COMMIT1...COMMIT2#hash"
6060
comparePattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(#[-+~_%.a-zA-Z0-9]+)?`)
6161

62-
validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`)
62+
// fullURLPattern matches full URL like "mailto:...", "https://..." and "ssh+git://..."
63+
fullURLPattern = regexp.MustCompile(`^[a-z][-+\w]+:`)
6364

64-
// While this email regex is definitely not perfect and I'm sure you can come up
65-
// with edge cases, it is still accepted by the CommonMark specification, as
66-
// well as the HTML5 spec:
65+
// emailRegex is definitely not perfect with edge cases,
66+
// it is still accepted by the CommonMark specification, as well as the HTML5 spec:
6767
// http://spec.commonmark.org/0.28/#email-address
6868
// https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type%3Demail)
6969
emailRegex = regexp.MustCompile("(?:\\s|^|\\(|\\[)([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+)(?:\\s|$|\\)|\\]|;|,|\\?|!|\\.(\\s|$))")
7070

71-
// blackfriday extensions create IDs like fn:user-content-footnote
71+
// blackfridayExtRegex is for blackfriday extensions create IDs like fn:user-content-footnote
7272
blackfridayExtRegex = regexp.MustCompile(`[^:]*:user-content-`)
7373

74-
// EmojiShortCodeRegex find emoji by alias like :smile:
75-
EmojiShortCodeRegex = regexp.MustCompile(`:[-+\w]+:`)
74+
// emojiShortCodeRegex find emoji by alias like :smile:
75+
emojiShortCodeRegex = regexp.MustCompile(`:[-+\w]+:`)
7676
)
7777

7878
// CSS class for action keywords (e.g. "closes: #1")
7979
const keywordClass = "issue-keyword"
8080

81-
// IsLink reports whether link fits valid format.
82-
func IsLink(link []byte) bool {
83-
return validLinksPattern.Match(link)
81+
// IsFullURLBytes reports whether link fits valid format.
82+
func IsFullURLBytes(link []byte) bool {
83+
return fullURLPattern.Match(link)
8484
}
8585

86-
func IsLinkStr(link string) bool {
87-
return validLinksPattern.MatchString(link)
86+
func IsFullURLString(link string) bool {
87+
return fullURLPattern.MatchString(link)
8888
}
8989

9090
// regexp for full links to issues/pulls
@@ -399,7 +399,7 @@ func visitNode(ctx *RenderContext, procs []processor, node *html.Node) {
399399
if attr.Key != "src" {
400400
continue
401401
}
402-
if len(attr.Val) > 0 && !IsLinkStr(attr.Val) && !strings.HasPrefix(attr.Val, "data:image/") {
402+
if len(attr.Val) > 0 && !IsFullURLString(attr.Val) && !strings.HasPrefix(attr.Val, "data:image/") {
403403
attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
404404
}
405405
attr.Val = camoHandleLink(attr.Val)
@@ -650,7 +650,7 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
650650
if equalPos := strings.IndexByte(v, '='); equalPos == -1 {
651651
// There is no equal in this argument; this is a mandatory arg
652652
if props["name"] == "" {
653-
if IsLinkStr(v) {
653+
if IsFullURLString(v) {
654654
// If we clearly see it is a link, we save it so
655655

656656
// But first we need to ensure, that if both mandatory args provided
@@ -725,7 +725,7 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
725725
DataAtom: atom.A,
726726
}
727727
childNode.Parent = linkNode
728-
absoluteLink := IsLinkStr(link)
728+
absoluteLink := IsFullURLString(link)
729729
if !absoluteLink {
730730
if image {
731731
link = strings.ReplaceAll(link, " ", "+")
@@ -1059,7 +1059,7 @@ func emojiShortCodeProcessor(ctx *RenderContext, node *html.Node) {
10591059
start := 0
10601060
next := node.NextSibling
10611061
for node != nil && node != next && start < len(node.Data) {
1062-
m := EmojiShortCodeRegex.FindStringSubmatchIndex(node.Data[start:])
1062+
m := emojiShortCodeRegex.FindStringSubmatchIndex(node.Data[start:])
10631063
if m == nil {
10641064
return
10651065
}

modules/markup/html_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,15 @@ func TestRender_links(t *testing.T) {
204204
test(
205205
"magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download",
206206
`<p><a href="magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&amp;dn=download" rel="nofollow">magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&amp;dn=download</a></p>`)
207+
test(
208+
`[link](https://example.com)`,
209+
`<p><a href="https://example.com" rel="nofollow">link</a></p>`)
210+
test(
211+
`[link](mailto:[email protected])`,
212+
`<p><a href="mailto:[email protected]" rel="nofollow">link</a></p>`)
213+
test(
214+
`[link](javascript:xss)`,
215+
`<p>link</p>`)
207216

208217
// Test that should *not* be turned into URL
209218
test(
@@ -673,3 +682,9 @@ func TestIssue18471(t *testing.T) {
673682
assert.NoError(t, err)
674683
assert.Equal(t, "<a href=\"http://domain/org/repo/compare/783b039...da951ce\" class=\"compare\"><code class=\"nohighlight\">783b039...da951ce</code></a>", res.String())
675684
}
685+
686+
func TestIsFullURL(t *testing.T) {
687+
assert.True(t, markup.IsFullURLString("https://example.com"))
688+
assert.True(t, markup.IsFullURLString("mailto:[email protected]"))
689+
assert.False(t, markup.IsFullURLString("/foo:bar"))
690+
}

modules/markup/markdown/goldmark.go

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ import (
2626
"github.com/yuin/goldmark/util"
2727
)
2828

29-
var byteMailto = []byte("mailto:")
30-
3129
// ASTTransformer is a default transformer of the goldmark tree.
3230
type ASTTransformer struct{}
3331

@@ -84,7 +82,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
8482
// 2. If they're not wrapped with a link they need a link wrapper
8583

8684
// Check if the destination is a real link
87-
if len(v.Destination) > 0 && !markup.IsLink(v.Destination) {
85+
if len(v.Destination) > 0 && !markup.IsFullURLBytes(v.Destination) {
8886
v.Destination = []byte(giteautil.URLJoin(
8987
ctx.Links.ResolveMediaLink(ctx.IsWiki),
9088
strings.TrimLeft(string(v.Destination), "/"),
@@ -130,23 +128,17 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
130128
case *ast.Link:
131129
// Links need their href to munged to be a real value
132130
link := v.Destination
133-
if len(link) > 0 && !markup.IsLink(link) &&
134-
link[0] != '#' && !bytes.HasPrefix(link, byteMailto) {
135-
// special case: this is not a link, a hash link or a mailto:, so it's a
136-
// relative URL
137-
138-
var base string
131+
isAnchorFragment := len(link) > 0 && link[0] == '#'
132+
if !isAnchorFragment && !markup.IsFullURLBytes(link) {
133+
base := ctx.Links.Base
139134
if ctx.IsWiki {
140135
base = ctx.Links.WikiLink()
141136
} else if ctx.Links.HasBranchInfo() {
142137
base = ctx.Links.SrcLink()
143-
} else {
144-
base = ctx.Links.Base
145138
}
146-
147139
link = []byte(giteautil.URLJoin(base, string(link)))
148140
}
149-
if len(link) > 0 && link[0] == '#' {
141+
if isAnchorFragment {
150142
link = []byte("#user-content-" + string(link)[1:])
151143
}
152144
v.Destination = link

0 commit comments

Comments
 (0)