Skip to content

Commit ae491a4

Browse files
committed
Merge branch 'main' into lunny/move_issue_pin
2 parents 14b2269 + 50873c1 commit ae491a4

File tree

91 files changed

+1388
-717
lines changed

Some content is hidden

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

91 files changed

+1388
-717
lines changed

.changelog.yml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,25 @@ groups:
2222
name: FEATURES
2323
labels:
2424
- type/feature
25-
-
26-
name: API
27-
labels:
28-
- modifies/api
2925
-
3026
name: ENHANCEMENTS
3127
labels:
3228
- type/enhancement
33-
- type/refactoring
34-
- topic/ui
29+
-
30+
name: PERFORMANCE
31+
labels:
32+
- performance/memory
33+
- performance/speed
34+
- performance/bigrepo
35+
- performance/cpu
3536
-
3637
name: BUGFIXES
3738
labels:
3839
- type/bug
40+
-
41+
name: API
42+
labels:
43+
- modifies/api
3944
-
4045
name: TESTING
4146
labels:

models/fixtures/repository.yml

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,19 +1694,6 @@
16941694
is_fsck_enabled: true
16951695
close_issues_via_commit_in_any_branch: false
16961696

1697-
-
1698-
id: 59
1699-
owner_id: 2
1700-
owner_name: user2
1701-
lower_name: test_commit_revert
1702-
name: test_commit_revert
1703-
default_branch: main
1704-
is_empty: false
1705-
is_archived: false
1706-
is_private: true
1707-
status: 0
1708-
num_issues: 0
1709-
17101697
-
17111698
id: 60
17121699
owner_id: 40

models/fixtures/user.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
num_followers: 2
6868
num_following: 1
6969
num_stars: 2
70-
num_repos: 15
70+
num_repos: 14
7171
num_teams: 0
7272
num_members: 0
7373
visibility: 0

models/issues/issue_stats.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func GetIssueStats(ctx context.Context, opts *IssuesOptions) (*IssueStats, error
107107
accum.YourRepositoriesCount += stats.YourRepositoriesCount
108108
accum.AssignCount += stats.AssignCount
109109
accum.CreateCount += stats.CreateCount
110-
accum.OpenCount += stats.MentionCount
110+
accum.MentionCount += stats.MentionCount
111111
accum.ReviewRequestedCount += stats.ReviewRequestedCount
112112
accum.ReviewedCount += stats.ReviewedCount
113113
i = chunk

models/issues/review.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -930,17 +930,19 @@ func MarkConversation(ctx context.Context, comment *Comment, doer *user_model.Us
930930
}
931931

932932
// CanMarkConversation Add or remove Conversation mark for a code comment permission check
933-
// the PR writer , offfcial reviewer and poster can do it
933+
// the PR writer , official reviewer and poster can do it
934934
func CanMarkConversation(ctx context.Context, issue *Issue, doer *user_model.User) (permResult bool, err error) {
935935
if doer == nil || issue == nil {
936936
return false, fmt.Errorf("issue or doer is nil")
937937
}
938938

939+
if err = issue.LoadRepo(ctx); err != nil {
940+
return false, err
941+
}
942+
if issue.Repo.IsArchived {
943+
return false, nil
944+
}
939945
if doer.ID != issue.PosterID {
940-
if err = issue.LoadRepo(ctx); err != nil {
941-
return false, err
942-
}
943-
944946
p, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
945947
if err != nil {
946948
return false, err
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package organization
5+
6+
import (
7+
"sort"
8+
9+
"code.gitea.io/gitea/models/db"
10+
11+
"xorm.io/builder"
12+
)
13+
14+
type WorktimeSumByRepos struct {
15+
RepoName string
16+
SumTime int64
17+
}
18+
19+
func GetWorktimeByRepos(org *Organization, unitFrom, unixTo int64) (results []WorktimeSumByRepos, err error) {
20+
err = db.GetEngine(db.DefaultContext).
21+
Select("repository.name AS repo_name, SUM(tracked_time.time) AS sum_time").
22+
Table("tracked_time").
23+
Join("INNER", "issue", "tracked_time.issue_id = issue.id").
24+
Join("INNER", "repository", "issue.repo_id = repository.id").
25+
Where(builder.Eq{"repository.owner_id": org.ID}).
26+
And(builder.Eq{"tracked_time.deleted": false}).
27+
And(builder.Gte{"tracked_time.created_unix": unitFrom}).
28+
And(builder.Lte{"tracked_time.created_unix": unixTo}).
29+
GroupBy("repository.name").
30+
OrderBy("repository.name").
31+
Find(&results)
32+
return results, err
33+
}
34+
35+
type WorktimeSumByMilestones struct {
36+
RepoName string
37+
MilestoneName string
38+
MilestoneID int64
39+
MilestoneDeadline int64
40+
SumTime int64
41+
HideRepoName bool
42+
}
43+
44+
func GetWorktimeByMilestones(org *Organization, unitFrom, unixTo int64) (results []WorktimeSumByMilestones, err error) {
45+
err = db.GetEngine(db.DefaultContext).
46+
Select("repository.name AS repo_name, milestone.name AS milestone_name, milestone.id AS milestone_id, milestone.deadline_unix as milestone_deadline, SUM(tracked_time.time) AS sum_time").
47+
Table("tracked_time").
48+
Join("INNER", "issue", "tracked_time.issue_id = issue.id").
49+
Join("INNER", "repository", "issue.repo_id = repository.id").
50+
Join("LEFT", "milestone", "issue.milestone_id = milestone.id").
51+
Where(builder.Eq{"repository.owner_id": org.ID}).
52+
And(builder.Eq{"tracked_time.deleted": false}).
53+
And(builder.Gte{"tracked_time.created_unix": unitFrom}).
54+
And(builder.Lte{"tracked_time.created_unix": unixTo}).
55+
GroupBy("repository.name, milestone.name, milestone.deadline_unix, milestone.id").
56+
OrderBy("repository.name, milestone.deadline_unix, milestone.id").
57+
Find(&results)
58+
59+
// TODO: pgsql: NULL values are sorted last in default ascending order, so we need to sort them manually again.
60+
sort.Slice(results, func(i, j int) bool {
61+
if results[i].RepoName != results[j].RepoName {
62+
return results[i].RepoName < results[j].RepoName
63+
}
64+
if results[i].MilestoneDeadline != results[j].MilestoneDeadline {
65+
return results[i].MilestoneDeadline < results[j].MilestoneDeadline
66+
}
67+
return results[i].MilestoneID < results[j].MilestoneID
68+
})
69+
70+
// Show only the first RepoName, for nicer output.
71+
prevRepoName := ""
72+
for i := 0; i < len(results); i++ {
73+
res := &results[i]
74+
res.MilestoneDeadline = 0 // clear the deadline because we do not really need it
75+
if prevRepoName == res.RepoName {
76+
res.HideRepoName = true
77+
}
78+
prevRepoName = res.RepoName
79+
}
80+
return results, err
81+
}
82+
83+
type WorktimeSumByMembers struct {
84+
UserName string
85+
SumTime int64
86+
}
87+
88+
func GetWorktimeByMembers(org *Organization, unitFrom, unixTo int64) (results []WorktimeSumByMembers, err error) {
89+
err = db.GetEngine(db.DefaultContext).
90+
Select("`user`.name AS user_name, SUM(tracked_time.time) AS sum_time").
91+
Table("tracked_time").
92+
Join("INNER", "issue", "tracked_time.issue_id = issue.id").
93+
Join("INNER", "repository", "issue.repo_id = repository.id").
94+
Join("INNER", "`user`", "tracked_time.user_id = `user`.id").
95+
Where(builder.Eq{"repository.owner_id": org.ID}).
96+
And(builder.Eq{"tracked_time.deleted": false}).
97+
And(builder.Gte{"tracked_time.created_unix": unitFrom}).
98+
And(builder.Lte{"tracked_time.created_unix": unixTo}).
99+
GroupBy("`user`.name").
100+
OrderBy("sum_time DESC").
101+
Find(&results)
102+
return results, err
103+
}

models/system/notice_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ func TestCreateRepositoryNotice(t *testing.T) {
4545
unittest.AssertExistsAndLoadBean(t, noticeBean)
4646
}
4747

48-
// TODO TestRemoveAllWithNotice
49-
5048
func TestCountNotices(t *testing.T) {
5149
assert.NoError(t, unittest.PrepareTestDatabase())
5250
assert.Equal(t, int64(3), system.CountNotices(db.DefaultContext))

modules/base/tool.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"time"
1919

2020
"code.gitea.io/gitea/modules/git"
21-
"code.gitea.io/gitea/modules/log"
2221
"code.gitea.io/gitea/modules/setting"
2322
"code.gitea.io/gitea/modules/util"
2423

@@ -64,10 +63,7 @@ func VerifyTimeLimitCode(now time.Time, data string, minutes int, code string) b
6463
// check code
6564
retCode := CreateTimeLimitCode(data, aliveTime, startTimeStr, nil)
6665
if subtle.ConstantTimeCompare([]byte(retCode), []byte(code)) != 1 {
67-
retCode = CreateTimeLimitCode(data, aliveTime, startTimeStr, sha1.New()) // TODO: this is only for the support of legacy codes, remove this in/after 1.23
68-
if subtle.ConstantTimeCompare([]byte(retCode), []byte(code)) != 1 {
69-
return false
70-
}
66+
return false
7167
}
7268

7369
// check time is expired or not: startTime <= now && now < startTime + minutes
@@ -144,13 +140,12 @@ func Int64sToStrings(ints []int64) []string {
144140
return strs
145141
}
146142

147-
// EntryIcon returns the octicon class for displaying files/directories
143+
// EntryIcon returns the octicon name for displaying files/directories
148144
func EntryIcon(entry *git.TreeEntry) string {
149145
switch {
150146
case entry.IsLink():
151147
te, err := entry.FollowLink()
152148
if err != nil {
153-
log.Debug(err.Error())
154149
return "file-symlink-file"
155150
}
156151
if te.IsDir() {

modules/base/tool_test.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,10 @@ JWT_SECRET = %s
8686
verifyDataCode := func(c string) bool {
8787
return VerifyTimeLimitCode(now, "data", 2, c)
8888
}
89-
code1 := CreateTimeLimitCode("data", 2, now, sha1.New())
90-
code2 := CreateTimeLimitCode("data", 2, now, nil)
91-
assert.True(t, verifyDataCode(code1))
92-
assert.True(t, verifyDataCode(code2))
89+
code := CreateTimeLimitCode("data", 2, now, nil)
90+
assert.True(t, verifyDataCode(code))
9391
initGeneralSecret("000_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko")
94-
assert.False(t, verifyDataCode(code1))
95-
assert.False(t, verifyDataCode(code2))
92+
assert.False(t, verifyDataCode(code))
9693
})
9794
}
9895

@@ -137,5 +134,3 @@ func TestInt64sToStrings(t *testing.T) {
137134
Int64sToStrings([]int64{1, 4, 16, 64, 256}),
138135
)
139136
}
140-
141-
// TODO: Test EntryIcon

modules/httplib/request.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,10 @@ func (r *Request) Param(key, value string) *Request {
9999
return r
100100
}
101101

102-
// Body adds request raw body.
103-
// it supports string and []byte.
102+
// Body adds request raw body. It supports string, []byte and io.Reader as body.
104103
func (r *Request) Body(data any) *Request {
105104
switch t := data.(type) {
105+
case nil: // do nothing
106106
case string:
107107
bf := bytes.NewBufferString(t)
108108
r.req.Body = io.NopCloser(bf)
@@ -111,6 +111,12 @@ func (r *Request) Body(data any) *Request {
111111
bf := bytes.NewBuffer(t)
112112
r.req.Body = io.NopCloser(bf)
113113
r.req.ContentLength = int64(len(t))
114+
case io.ReadCloser:
115+
r.req.Body = t
116+
case io.Reader:
117+
r.req.Body = io.NopCloser(t)
118+
default:
119+
panic(fmt.Sprintf("unsupported request body type %T", t))
114120
}
115121
return r
116122
}
@@ -141,7 +147,7 @@ func (r *Request) getResponse() (*http.Response, error) {
141147
}
142148
} else if r.req.Method == "POST" && r.req.Body == nil && len(paramBody) > 0 {
143149
r.Header("Content-Type", "application/x-www-form-urlencoded")
144-
r.Body(paramBody)
150+
r.Body(paramBody) // string
145151
}
146152

147153
var err error
@@ -185,6 +191,7 @@ func (r *Request) getResponse() (*http.Response, error) {
185191
}
186192

187193
// Response executes request client gets response manually.
194+
// Caller MUST close the response body if no error occurs
188195
func (r *Request) Response() (*http.Response, error) {
189196
return r.getResponse()
190197
}

0 commit comments

Comments
 (0)