aboutsummaryrefslogtreecommitdiff
path: root/models
diff options
context:
space:
mode:
authorAnthony Wang2023-03-04 00:03:38 +0000
committerAnthony Wang2023-03-04 00:03:38 +0000
commitb4640101f3c8938ed689743606a79601201141ca (patch)
tree8469fc5ef817560db646837b41417d50e2f8ebfc /models
parentdc20c2832871f6462990751ea802e14b02bf41b0 (diff)
parent8540fc45b11eff9a73753ca139f8ea5c38509bf5 (diff)
Merge remote-tracking branch 'origin/main' into forgejo-federation
Diffstat (limited to 'models')
-rw-r--r--models/actions/run.go12
-rw-r--r--models/actions/run_list.go8
-rw-r--r--models/actions/status.go4
-rw-r--r--models/activities/action.go16
-rw-r--r--models/activities/action_test.go27
-rw-r--r--models/activities/user_heatmap_test.go3
-rw-r--r--models/auth/oauth2.go2
-rw-r--r--models/auth/token_scope.go25
-rw-r--r--models/auth/twofactor.go2
-rw-r--r--models/avatars/avatar.go4
-rw-r--r--models/avatars/avatar_test.go2
-rw-r--r--models/db/context.go13
-rwxr-xr-xmodels/db/engine.go1
-rw-r--r--models/db/list.go (renamed from models/db/list_options.go)4
-rw-r--r--models/db/list_test.go48
-rw-r--r--models/issues/issue.go77
-rw-r--r--models/issues/issue_project.go6
-rw-r--r--models/issues/label.go8
-rw-r--r--models/migrations/base/hash.go2
-rw-r--r--models/migrations/migrations.go4
-rw-r--r--models/migrations/v1_14/v166.go2
-rw-r--r--models/migrations/v1_20/v244.go22
-rw-r--r--models/project/project.go13
-rw-r--r--models/repo.go4
-rw-r--r--models/system/setting.go49
-rw-r--r--models/system/setting_test.go6
-rw-r--r--models/unittest/testdb.go2
-rw-r--r--models/user/avatar.go2
28 files changed, 277 insertions, 91 deletions
diff --git a/models/actions/run.go b/models/actions/run.go
index 14d191c81..a8d991471 100644
--- a/models/actions/run.go
+++ b/models/actions/run.go
@@ -32,11 +32,13 @@ type ActionRun struct {
OwnerID int64 `xorm:"index"`
WorkflowID string `xorm:"index"` // the name of workflow file
Index int64 `xorm:"index unique(repo_index)"` // a unique number for each run of a repository
- TriggerUserID int64
- TriggerUser *user_model.User `xorm:"-"`
+ TriggerUserID int64 `xorm:"index"`
+ TriggerUser *user_model.User `xorm:"-"`
Ref string
CommitSHA string
IsForkPullRequest bool
+ NeedApproval bool // may need approval if it's a fork pull request
+ ApprovedBy int64 `xorm:"index"` // who approved
Event webhook_module.HookEventType
EventPayload string `xorm:"LONGTEXT"`
Status Status `xorm:"index"`
@@ -164,10 +166,6 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
}
run.Index = index
- if run.Status.IsUnknown() {
- run.Status = StatusWaiting
- }
-
if err := db.Insert(ctx, run); err != nil {
return err
}
@@ -191,7 +189,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
job.EraseNeeds()
payload, _ := v.Marshal()
status := StatusWaiting
- if len(needs) > 0 {
+ if len(needs) > 0 || run.NeedApproval {
status = StatusBlocked
}
runJobs = append(runJobs, &ActionRunJob{
diff --git a/models/actions/run_list.go b/models/actions/run_list.go
index f9d841722..bc69c6584 100644
--- a/models/actions/run_list.go
+++ b/models/actions/run_list.go
@@ -68,6 +68,8 @@ type FindRunOptions struct {
OwnerID int64
IsClosed util.OptionalBool
WorkflowFileName string
+ TriggerUserID int64
+ Approved bool // not util.OptionalBool, it works only when it's true
}
func (opts FindRunOptions) toConds() builder.Cond {
@@ -89,6 +91,12 @@ func (opts FindRunOptions) toConds() builder.Cond {
if opts.WorkflowFileName != "" {
cond = cond.And(builder.Eq{"workflow_id": opts.WorkflowFileName})
}
+ if opts.TriggerUserID > 0 {
+ cond = cond.And(builder.Eq{"trigger_user_id": opts.TriggerUserID})
+ }
+ if opts.Approved {
+ cond = cond.And(builder.Gt{"approved_by": 0})
+ }
return cond
}
diff --git a/models/actions/status.go b/models/actions/status.go
index 059cf9bc0..c97578f2a 100644
--- a/models/actions/status.go
+++ b/models/actions/status.go
@@ -82,6 +82,10 @@ func (s Status) IsRunning() bool {
return s == StatusRunning
}
+func (s Status) IsBlocked() bool {
+ return s == StatusBlocked
+}
+
// In returns whether s is one of the given statuses
func (s Status) In(statuses ...Status) bool {
for _, v := range statuses {
diff --git a/models/activities/action.go b/models/activities/action.go
index 2e845bf89..1412d2c05 100644
--- a/models/activities/action.go
+++ b/models/activities/action.go
@@ -380,14 +380,14 @@ type GetFeedsOptions struct {
}
// GetFeeds returns actions according to the provided options
-func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, error) {
+func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) {
if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
- return nil, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
+ return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
}
cond, err := activityQueryCondition(opts)
if err != nil {
- return nil, err
+ return nil, 0, err
}
sess := db.GetEngine(ctx).Where(cond).
@@ -398,16 +398,16 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, error) {
sess = db.SetSessionPagination(sess, &opts)
actions := make([]*Action, 0, opts.PageSize)
-
- if err := sess.Desc("`action`.created_unix").Find(&actions); err != nil {
- return nil, fmt.Errorf("Find: %w", err)
+ count, err := sess.Desc("`action`.created_unix").FindAndCount(&actions)
+ if err != nil {
+ return nil, 0, fmt.Errorf("FindAndCount: %w", err)
}
if err := ActionList(actions).loadAttributes(ctx); err != nil {
- return nil, fmt.Errorf("LoadAttributes: %w", err)
+ return nil, 0, fmt.Errorf("LoadAttributes: %w", err)
}
- return actions, nil
+ return actions, count, nil
}
// ActivityReadable return whether doer can read activities of user
diff --git a/models/activities/action_test.go b/models/activities/action_test.go
index f37e58f68..2fd86bb8f 100644
--- a/models/activities/action_test.go
+++ b/models/activities/action_test.go
@@ -44,7 +44,7 @@ func TestGetFeeds(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
+ actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: user,
Actor: user,
IncludePrivate: true,
@@ -56,8 +56,9 @@ func TestGetFeeds(t *testing.T) {
assert.EqualValues(t, 1, actions[0].ID)
assert.EqualValues(t, user.ID, actions[0].UserID)
}
+ assert.Equal(t, int64(1), count)
- actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
+ actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: user,
Actor: user,
IncludePrivate: false,
@@ -65,6 +66,7 @@ func TestGetFeeds(t *testing.T) {
})
assert.NoError(t, err)
assert.Len(t, actions, 0)
+ assert.Equal(t, int64(0), count)
}
func TestGetFeedsForRepos(t *testing.T) {
@@ -74,38 +76,42 @@ func TestGetFeedsForRepos(t *testing.T) {
pubRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 8})
// private repo & no login
- actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
+ actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedRepo: privRepo,
IncludePrivate: true,
})
assert.NoError(t, err)
assert.Len(t, actions, 0)
+ assert.Equal(t, int64(0), count)
// public repo & no login
- actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
+ actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedRepo: pubRepo,
IncludePrivate: true,
})
assert.NoError(t, err)
assert.Len(t, actions, 1)
+ assert.Equal(t, int64(1), count)
// private repo and login
- actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
+ actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedRepo: privRepo,
IncludePrivate: true,
Actor: user,
})
assert.NoError(t, err)
assert.Len(t, actions, 1)
+ assert.Equal(t, int64(1), count)
// public repo & login
- actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
+ actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedRepo: pubRepo,
IncludePrivate: true,
Actor: user,
})
assert.NoError(t, err)
assert.Len(t, actions, 1)
+ assert.Equal(t, int64(1), count)
}
func TestGetFeeds2(t *testing.T) {
@@ -114,7 +120,7 @@ func TestGetFeeds2(t *testing.T) {
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
+ actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: org,
Actor: user,
IncludePrivate: true,
@@ -127,8 +133,9 @@ func TestGetFeeds2(t *testing.T) {
assert.EqualValues(t, 2, actions[0].ID)
assert.EqualValues(t, org.ID, actions[0].UserID)
}
+ assert.Equal(t, int64(1), count)
- actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
+ actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: org,
Actor: user,
IncludePrivate: false,
@@ -137,6 +144,7 @@ func TestGetFeeds2(t *testing.T) {
})
assert.NoError(t, err)
assert.Len(t, actions, 0)
+ assert.Equal(t, int64(0), count)
}
func TestActivityReadable(t *testing.T) {
@@ -224,13 +232,14 @@ func TestGetFeedsCorrupted(t *testing.T) {
RepoID: 1700,
})
- actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
+ actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: user,
Actor: user,
IncludePrivate: true,
})
assert.NoError(t, err)
assert.Len(t, actions, 0)
+ assert.Equal(t, int64(0), count)
}
func TestConsistencyUpdateAction(t *testing.T) {
diff --git a/models/activities/user_heatmap_test.go b/models/activities/user_heatmap_test.go
index 34715ab6d..98df7b38a 100644
--- a/models/activities/user_heatmap_test.go
+++ b/models/activities/user_heatmap_test.go
@@ -73,7 +73,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
}
// get the action for comparison
- actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
+ actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: user,
Actor: doer,
IncludePrivate: true,
@@ -90,6 +90,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
}
assert.NoError(t, err)
assert.Len(t, actions, contributions, "invalid action count: did the test data became too old?")
+ assert.Equal(t, count, int64(contributions))
assert.Equal(t, tc.CountResult, contributions, fmt.Sprintf("testcase '%s'", tc.desc))
// Test JSON rendering
diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go
index 09d4bfc4e..bda0668c4 100644
--- a/models/auth/oauth2.go
+++ b/models/auth/oauth2.go
@@ -5,7 +5,6 @@ package auth
import (
"context"
- "crypto/sha256"
"encoding/base32"
"encoding/base64"
"fmt"
@@ -18,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/util"
uuid "github.com/google/uuid"
+ "github.com/minio/sha256-simd"
"golang.org/x/crypto/bcrypt"
"xorm.io/builder"
"xorm.io/xorm"
diff --git a/models/auth/token_scope.go b/models/auth/token_scope.go
index c61c30649..38733a1c8 100644
--- a/models/auth/token_scope.go
+++ b/models/auth/token_scope.go
@@ -168,10 +168,23 @@ var allAccessTokenScopeBits = map[AccessTokenScope]AccessTokenScopeBitmap{
// Parse parses the scope string into a bitmap, thus removing possible duplicates.
func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) {
- list := strings.Split(string(s), ",")
-
var bitmap AccessTokenScopeBitmap
- for _, v := range list {
+
+ // The following is the more performant equivalent of 'for _, v := range strings.Split(remainingScope, ",")' as this is hot code
+ remainingScopes := string(s)
+ for len(remainingScopes) > 0 {
+ i := strings.IndexByte(remainingScopes, ',')
+ var v string
+ if i < 0 {
+ v = remainingScopes
+ remainingScopes = ""
+ } else if i+1 >= len(remainingScopes) {
+ v = remainingScopes[:i]
+ remainingScopes = ""
+ } else {
+ v = remainingScopes[:i]
+ remainingScopes = remainingScopes[i+1:]
+ }
singleScope := AccessTokenScope(v)
if singleScope == "" {
continue
@@ -187,9 +200,15 @@ func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) {
}
bitmap |= bits
}
+
return bitmap, nil
}
+// StringSlice returns the AccessTokenScope as a []string
+func (s AccessTokenScope) StringSlice() []string {
+ return strings.Split(string(s), ",")
+}
+
// Normalize returns a normalized scope string without any duplicates.
func (s AccessTokenScope) Normalize() (AccessTokenScope, error) {
bitmap, err := s.Parse()
diff --git a/models/auth/twofactor.go b/models/auth/twofactor.go
index 5b3a9d011..751a281f7 100644
--- a/models/auth/twofactor.go
+++ b/models/auth/twofactor.go
@@ -5,7 +5,6 @@ package auth
import (
"crypto/md5"
- "crypto/sha256"
"crypto/subtle"
"encoding/base32"
"encoding/base64"
@@ -18,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
+ "github.com/minio/sha256-simd"
"github.com/pquerna/otp/totp"
"golang.org/x/crypto/pbkdf2"
)
diff --git a/models/avatars/avatar.go b/models/avatars/avatar.go
index 6cf05dd28..265ee6428 100644
--- a/models/avatars/avatar.go
+++ b/models/avatars/avatar.go
@@ -153,7 +153,7 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final
return DefaultAvatarLink()
}
- enableFederatedAvatar := system_model.GetSettingBool(ctx, system_model.KeyPictureEnableFederatedAvatar)
+ enableFederatedAvatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureEnableFederatedAvatar)
var err error
if enableFederatedAvatar && system_model.LibravatarService != nil {
@@ -174,7 +174,7 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final
return urlStr
}
- disableGravatar := system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar)
+ disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
if !disableGravatar {
// copy GravatarSourceURL, because we will modify its Path.
avatarURLCopy := *system_model.GravatarSourceURL
diff --git a/models/avatars/avatar_test.go b/models/avatars/avatar_test.go
index a3cb36d0e..59daaeb66 100644
--- a/models/avatars/avatar_test.go
+++ b/models/avatars/avatar_test.go
@@ -28,7 +28,7 @@ func enableGravatar(t *testing.T) {
err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false")
assert.NoError(t, err)
setting.GravatarSource = gravatarSource
- err = system_model.Init()
+ err = system_model.Init(db.DefaultContext)
assert.NoError(t, err)
}
diff --git a/models/db/context.go b/models/db/context.go
index 4b3f7f0ee..670f6272a 100644
--- a/models/db/context.go
+++ b/models/db/context.go
@@ -209,7 +209,7 @@ func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean interface{
return err
}
-// DeleteBeans deletes all given beans, beans should contain delete conditions.
+// DeleteBeans deletes all given beans, beans must contain delete conditions.
func DeleteBeans(ctx context.Context, beans ...interface{}) (err error) {
e := GetEngine(ctx)
for i := range beans {
@@ -220,6 +220,17 @@ func DeleteBeans(ctx context.Context, beans ...interface{}) (err error) {
return nil
}
+// TruncateBeans deletes all given beans, beans may contain delete conditions.
+func TruncateBeans(ctx context.Context, beans ...interface{}) (err error) {
+ e := GetEngine(ctx)
+ for i := range beans {
+ if _, err = e.Truncate(beans[i]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
// CountByBean counts the number of database records according non-empty fields of the bean as conditions.
func CountByBean(ctx context.Context, bean interface{}) (int64, error) {
return GetEngine(ctx).Count(bean)
diff --git a/models/db/engine.go b/models/db/engine.go
index 3d05fa8b6..5020101d4 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -38,6 +38,7 @@ type Engine interface {
Count(...interface{}) (int64, error)
Decr(column string, arg ...interface{}) *xorm.Session
Delete(...interface{}) (int64, error)
+ Truncate(...interface{}) (int64, error)
Exec(...interface{}) (sql.Result, error)
Find(interface{}, ...interface{}) error
Get(beans ...interface{}) (bool, error)
diff --git a/models/db/list_options.go b/models/db/list.go
index 2456f90f3..9fb4d0741 100644
--- a/models/db/list_options.go
+++ b/models/db/list.go
@@ -134,7 +134,7 @@ func Find[T any](ctx context.Context, opts FindOptions, objects *[]T) error {
if !opts.IsListAll() {
sess.Limit(opts.GetSkipTake())
}
- return sess.Find(&objects)
+ return sess.Find(objects)
}
// Count represents a common count function which accept an options interface
@@ -148,5 +148,5 @@ func FindAndCount[T any](ctx context.Context, opts FindOptions, objects *[]T) (i
if !opts.IsListAll() {
sess.Limit(opts.GetSkipTake())
}
- return sess.FindAndCount(&objects)
+ return sess.FindAndCount(objects)
}
diff --git a/models/db/list_test.go b/models/db/list_test.go
new file mode 100644
index 000000000..ffef1e494
--- /dev/null
+++ b/models/db/list_test.go
@@ -0,0 +1,48 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package db_test
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+ "xorm.io/builder"
+)
+
+type mockListOptions struct {
+ db.ListOptions
+}
+
+func (opts *mockListOptions) IsListAll() bool {
+ return true
+}
+
+func (opts *mockListOptions) ToConds() builder.Cond {
+ return builder.NewCond()
+}
+
+func TestFind(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ xe := unittest.GetXORMEngine()
+ assert.NoError(t, xe.Sync(&repo_model.RepoUnit{}))
+
+ opts := mockListOptions{}
+ var repoUnits []repo_model.RepoUnit
+ err := db.Find(db.DefaultContext, &opts, &repoUnits)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 83, len(repoUnits))
+
+ cnt, err := db.Count(db.DefaultContext, &opts, new(repo_model.RepoUnit))
+ assert.NoError(t, err)
+ assert.EqualValues(t, 83, cnt)
+
+ repoUnits = make([]repo_model.RepoUnit, 0, 10)
+ newCnt, err := db.FindAndCount(db.DefaultContext, &opts, &repoUnits)
+ assert.NoError(t, err)
+ assert.EqualValues(t, cnt, newCnt)
+}
diff --git a/models/issues/issue.go b/models/issues/issue.go
index 99ec1437f..9fdb63cfa 100644
--- a/models/issues/issue.go
+++ b/models/issues/issue.go
@@ -253,13 +253,15 @@ func (issue *Issue) LoadPoster(ctx context.Context) (err error) {
// LoadPullRequest loads pull request info
func (issue *Issue) LoadPullRequest(ctx context.Context) (err error) {
- if issue.IsPull && issue.PullRequest == nil {
- issue.PullRequest, err = GetPullRequestByIssueID(ctx, issue.ID)
- if err != nil {
- if IsErrPullRequestNotExist(err) {
- return err
+ if issue.IsPull {
+ if issue.PullRequest == nil {
+ issue.PullRequest, err = GetPullRequestByIssueID(ctx, issue.ID)
+ if err != nil {
+ if IsErrPullRequestNotExist(err) {
+ return err
+ }
+ return fmt.Errorf("getPullRequestByIssueID [%d]: %w", issue.ID, err)
}
- return fmt.Errorf("getPullRequestByIssueID [%d]: %w", issue.ID, err)
}
issue.PullRequest.Issue = issue
}
@@ -349,7 +351,7 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
return
}
- if err = issue.loadProject(ctx); err != nil {
+ if err = issue.LoadProject(ctx); err != nil {
return
}
@@ -1148,6 +1150,7 @@ type IssuesOptions struct { //nolint
PosterID int64
MentionedID int64
ReviewRequestedID int64
+ ReviewedID int64
SubscriberID int64
MilestoneIDs []int64
ProjectID int64
@@ -1262,6 +1265,10 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
applyReviewRequestedCondition(sess, opts.ReviewRequestedID)
}
+ if opts.ReviewedID > 0 {
+ applyReviewedCondition(sess, opts.ReviewedID)
+ }
+
if opts.SubscriberID > 0 {
applySubscribedCondition(sess, opts.SubscriberID)
}
@@ -1432,6 +1439,36 @@ func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64)
reviewRequestedID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, reviewRequestedID)
}
+func applyReviewedCondition(sess *xorm.Session, reviewedID int64) *xorm.Session {
+ // Query for pull requests where you are a reviewer or commenter, excluding
+ // any pull requests already returned by the the review requested filter.
+ notPoster := builder.Neq{"issue.poster_id": reviewedID}
+ reviewed := builder.In("issue.id", builder.
+ Select("issue_id").
+ From("review").
+ Where(builder.And(
+ builder.Neq{"type": ReviewTypeRequest},
+ builder.Or(
+ builder.Eq{"reviewer_id": reviewedID},
+ builder.In("reviewer_team_id", builder.
+ Select("team_id").
+ From("team_user").
+ Where(builder.Eq{"uid": reviewedID}),
+ ),
+ ),
+ )),
+ )
+ commented := builder.In("issue.id", builder.
+ Select("issue_id").
+ From("comment").
+ Where(builder.And(
+ builder.Eq{"poster_id": reviewedID},
+ builder.In("type", CommentTypeComment, CommentTypeCode, CommentTypeReview),
+ )),
+ )
+ return sess.And(notPoster, builder.Or(reviewed, commented))
+}
+
func applySubscribedCondition(sess *xorm.Session, subscriberID int64) *xorm.Session {
return sess.And(
builder.
@@ -1586,6 +1623,7 @@ type IssueStats struct {
CreateCount int64
MentionCount int64
ReviewRequestedCount int64
+ ReviewedCount int64
}
// Filter modes.
@@ -1595,6 +1633,7 @@ const (
FilterModeCreate
FilterModeMention
FilterModeReviewRequested
+ FilterModeReviewed
FilterModeYourRepositories
)
@@ -1608,6 +1647,7 @@ type IssueStatsOptions struct {
MentionedID int64
PosterID int64
ReviewRequestedID int64
+ ReviewedID int64
IsPull util.OptionalBool
IssueIDs []int64
}
@@ -1646,6 +1686,7 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
accum.CreateCount += stats.CreateCount
accum.OpenCount += stats.MentionCount
accum.ReviewRequestedCount += stats.ReviewRequestedCount
+ accum.ReviewedCount += stats.ReviewedCount
i = chunk
}
return accum, nil
@@ -1703,6 +1744,10 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats,
applyReviewRequestedCondition(sess, opts.ReviewRequestedID)
}
+ if opts.ReviewedID > 0 {
+ applyReviewedCondition(sess, opts.ReviewedID)
+ }
+
switch opts.IsPull {
case util.OptionalBoolTrue:
sess.And("issue.is_pull=?", true)
@@ -1843,6 +1888,19 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
if err != nil {
return nil, err
}
+ case FilterModeReviewed:
+ stats.OpenCount, err = applyReviewedCondition(sess(cond), opts.UserID).
+ And("issue.is_closed = ?", false).
+ Count(new(Issue))
+ if err != nil {
+ return nil, err
+ }
+ stats.ClosedCount, err = applyReviewedCondition(sess(cond), opts.UserID).
+ And("issue.is_closed = ?", true).
+ Count(new(Issue))
+ if err != nil {
+ return nil, err
+ }
}
cond = cond.And(builder.Eq{"issue.is_closed": opts.IsClosed})
@@ -1871,6 +1929,11 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
return nil, err
}
+ stats.ReviewedCount, err = applyReviewedCondition(sess(cond), opts.UserID).Count(new(Issue))
+ if err != nil {
+ return nil, err
+ }
+
return stats, nil
}
diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go
index c9f4c9f53..04d12e055 100644
--- a/models/issues/issue_project.go
+++ b/models/issues/issue_project.go
@@ -13,11 +13,7 @@ import (
)
// LoadProject load the project the issue was assigned to
-func (issue *Issue) LoadProject() (err error) {
- return issue.loadProject(db.DefaultContext)
-}
-
-func (issue *Issue) loadProject(ctx context.Context) (err error) {
+func (issue *Issue) LoadProject(ctx context.Context) (err error) {
if issue.Project == nil {
var p project_model.Project
if _, err = db.GetEngine(ctx).Table("project").
diff --git a/models/issues/label.go b/models/issues/label.go
index 0dd12fb5c..90e4eb458 100644
--- a/models/issues/label.go
+++ b/models/issues/label.go
@@ -778,7 +778,7 @@ func CountOrphanedLabels(ctx context.Context) (int64, error) {
norepo, err := db.GetEngine(ctx).Table("label").
Where(builder.And(
builder.Gt{"repo_id": 0},
- builder.NotIn("repo_id", builder.Select("id").From("repository")),
+ builder.NotIn("repo_id", builder.Select("id").From("`repository`")),
)).
Count()
if err != nil {
@@ -788,7 +788,7 @@ func CountOrphanedLabels(ctx context.Context) (int64, error) {
noorg, err := db.GetEngine(ctx).Table("label").
Where(builder.And(
builder.Gt{"org_id": 0},
- builder.NotIn("org_id", builder.Select("id").From("user")),
+ builder.NotIn("org_id", builder.Select("id").From("`user`")),
)).
Count()
if err != nil {
@@ -809,7 +809,7 @@ func DeleteOrphanedLabels(ctx context.Context) error {
if _, err := db.GetEngine(ctx).
Where(builder.And(
builder.Gt{"repo_id": 0},
- builder.NotIn("repo_id", builder.Select("id").From("repository")),
+ builder.NotIn("repo_id", builder.Select("id").From("`repository`")),
)).
Delete(Label{}); err != nil {
return err
@@ -819,7 +819,7 @@ func DeleteOrphanedLabels(ctx context.Context) error {
if _, err := db.GetEngine(ctx).
Where(builder.And(
builder.Gt{"org_id": 0},
- builder.NotIn("org_id", builder.Select("id").From("user")),
+ builder.NotIn("org_id", builder.Select("id").From("`user`")),
)).
Delete(Label{}); err != nil {
return err
diff --git a/models/migrations/base/hash.go b/models/migrations/base/hash.go
index 00fd1efd4..0debec272 100644
--- a/models/migrations/base/hash.go
+++ b/models/migrations/base/hash.go
@@ -4,9 +4,9 @@
package base
import (
- "crypto/sha256"
"encoding/hex"
+ "github.com/minio/sha256-simd"
"golang.org/x/crypto/pbkdf2"
)
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 989a1d6ae..585457e47 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/models/migrations/v1_17"
"code.gitea.io/gitea/models/migrations/v1_18"
"code.gitea.io/gitea/models/migrations/v1_19"
+ "code.gitea.io/gitea/models/migrations/v1_20"
"code.gitea.io/gitea/models/migrations/v1_6"
"code.gitea.io/gitea/models/migrations/v1_7"
"code.gitea.io/gitea/models/migrations/v1_8"
@@ -463,6 +464,9 @@ var migrations = []Migration{
NewMigration("Add exclusive label", v1_19.AddExclusiveLabel),
// Gitea 1.19.0 ends at v244
+
+ // v244 -> v245
+ NewMigration("Add NeedApproval to actions tables", v1_20.AddNeedApprovalToActionRun),
}
// GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v1_14/v166.go b/models/migrations/v1_14/v166.go
index f797930d6..de7626076 100644
--- a/models/migrations/v1_14/v166.go
+++ b/models/migrations/v1_14/v166.go
@@ -4,9 +4,9 @@
package v1_14 //nolint
import (
- "crypto/sha256"
"encoding/hex"
+ "github.com/minio/sha256-simd"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/pbkdf2"
diff --git a/models/migrations/v1_20/v244.go b/models/migrations/v1_20/v244.go
new file mode 100644
index 000000000..977566ad7
--- /dev/null
+++ b/models/migrations/v1_20/v244.go
@@ -0,0 +1,22 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_20 //nolint
+
+import (
+ "xorm.io/xorm"
+)
+
+func AddNeedApprovalToActionRun(x *xorm.Engine) error {
+ /*
+ New index: TriggerUserID
+ New fields: NeedApproval, ApprovedBy
+ */
+ type ActionRun struct {
+ TriggerUserID int64 `xorm:"index"`
+ NeedApproval bool // may need approval if it's a fork pull request
+ ApprovedBy int64 `xorm:"index"` // who approved
+ }
+
+ return x.Sync(new(ActionRun))
+}
diff --git a/models/project/project.go b/models/project/project.go
index 931ef4467..46b5c07c4 100644
--- a/models/project/project.go
+++ b/models/project/project.go
@@ -217,16 +217,8 @@ func CountProjects(ctx context.Context, opts SearchOptions) (int64, error) {
// FindProjects returns a list of all projects that have been created in the repository
func FindProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) {
- e := db.GetEngine(ctx)
+ e := db.GetEngine(ctx).Where(opts.toConds())
projects := make([]*Project, 0, setting.UI.IssuePagingNum)
- cond := opts.toConds()
-
- count, err := e.Where(cond).Count(new(Project))
- if err != nil {
- return nil, 0, fmt.Errorf("Count: %w", err)
- }
-
- e = e.Where(cond)
if opts.Page > 0 {
e = e.Limit(setting.UI.IssuePagingNum, (opts.Page-1)*setting.UI.IssuePagingNum)
@@ -243,7 +235,8 @@ func FindProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, e
e.Asc("created_unix")
}
- return projects, count, e.Find(&projects)
+ count, err := e.FindAndCount(&projects)
+ return projects, count, err
}
// NewProject creates a new Project
diff --git a/models/repo.go b/models/repo.go
index 38dc3f1ab..5a1e2e028 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -39,9 +39,9 @@ import (
var ItemsPerPage = 40
// Init initialize model
-func Init() error {
+func Init(ctx context.Context) error {
unit.LoadUnitConfig()
- return system_model.Init()
+ return system_model.Init(ctx)
}
// DeleteRepository deletes a repository for a user or organization.
diff --git a/models/system/setting.go b/models/system/setting.go
index 098d9a183..6af759dc8 100644
--- a/models/system/setting.go
+++ b/models/system/setting.go
@@ -79,8 +79,8 @@ func IsErrDataExpired(err error) bool {
return ok
}
-// GetSettingNoCache returns specific setting without using the cache
-func GetSettingNoCache(ctx context.Context, key string) (*Setting, error) {
+// GetSetting returns specific setting without using the cache
+func GetSetting(ctx context.Context, key string) (*Setting, error) {
v, err := GetSettings(ctx, []string{key})
if err != nil {
return nil, err
@@ -93,11 +93,11 @@ func GetSettingNoCache(ctx context.Context, key string) (*Setting, error) {
const contextCacheKey = "system_setting"
-// GetSetting returns the setting value via the key
-func GetSetting(ctx context.Context, key string) (string, error) {
+// GetSettingWithCache returns the setting value via the key
+func GetSettingWithCache(ctx context.Context, key string) (string, error) {
return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) {
return cache.GetString(genSettingCacheKey(key), func() (string, error) {
- res, err := GetSettingNoCache(ctx, key)
+ res, err := GetSetting(ctx, key)
if err != nil {
return "", err
}
@@ -110,6 +110,15 @@ func GetSetting(ctx context.Context, key string) (string, error) {
// none existing keys and errors are ignored and result in false
func GetSettingBool(ctx context.Context, key string) bool {
s, _ := GetSetting(ctx, key)
+ if s == nil {
+ return false
+ }
+ v, _ := strconv.ParseBool(s.SettingValue)
+ return v
+}
+
+func GetSettingWithCacheBool(ctx context.Context, key string) bool {
+ s, _ := GetSettingWithCache(ctx, key)
v, _ := strconv.ParseBool(s)
return v
}
@@ -120,7 +129,7 @@ func GetSettings(ctx context.Context, keys []string) (map[string]*Setting, error
keys[i] = strings.ToLower(keys[i])
}
settings := make([]*Setting, 0, len(keys))
- if err := db.GetEngine(db.DefaultContext).
+ if err := db.GetEngine(ctx).
Where(builder.In("setting_key", keys)).
Find(&settings); err != nil {
return nil, err
@@ -151,9 +160,9 @@ func (settings AllSettings) GetVersion(key string) int {
}
// GetAllSettings returns all settings from user
-func GetAllSettings() (AllSettings, error) {
+func GetAllSettings(ctx context.Context) (AllSettings, error) {
settings := make([]*Setting, 0, 5)
- if err := db.GetEngine(db.DefaultContext).
+ if err := db.GetEngine(ctx).
Find(&settings); err != nil {
return nil, err
}
@@ -168,12 +177,12 @@ func GetAllSettings() (AllSettings, error) {
func DeleteSetting(ctx context.Context, setting *Setting) error {
cache.RemoveContextData(ctx, contextCacheKey, setting.SettingKey)
cache.Remove(genSettingCacheKey(setting.SettingKey))
- _, err := db.GetEngine(db.DefaultContext).Delete(setting)
+ _, err := db.GetEngine(ctx).Delete(setting)
return err
}
func SetSettingNoVersion(ctx context.Context, key, value string) error {
- s, err := GetSettingNoCache(ctx, key)
+ s, err := GetSetting(ctx, key)
if IsErrSettingIsNotExist(err) {
return SetSetting(ctx, &Setting{
SettingKey: key,
@@ -189,7 +198,7 @@ func SetSettingNoVersion(ctx context.Context, key, value string) error {
// SetSetting updates a users' setting for a specific key
func SetSetting(ctx context.Context, setting *Setting) error {
- if err := upsertSettingValue(strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil {
+ if err := upsertSettingValue(ctx, strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil {
return err
}
@@ -205,8 +214,8 @@ func SetSetting(ctx context.Context, setting *Setting) error {
return nil
}
-func upsertSettingValue(key, value string, version int) error {
- return db.WithTx(db.DefaultContext, func(ctx context.Context) error {
+func upsertSettingValue(parentCtx context.Context, key, value string, version int) error {
+ return db.WithTx(parentCtx, func(ctx context.Context) error {
e := db.GetEngine(ctx)
// here we use a general method to do a safe upsert for different databases (and most transaction levels)
@@ -249,9 +258,9 @@ var (
LibravatarService *libravatar.Libravatar
)
-func Init() error {
+func Init(ctx context.Context) error {
var disableGravatar bool
- disableGravatarSetting, err := GetSettingNoCache(db.DefaultContext, KeyPictureDisableGravatar)
+ disableGravatarSetting, err := GetSetting(ctx, KeyPictureDisableGravatar)
if IsErrSettingIsNotExist(err) {
disableGravatar = setting_module.GetDefaultDisableGravatar()
disableGravatarSetting = &Setting{SettingValue: strconv.FormatBool(disableGravatar)}
@@ -262,7 +271,7 @@ func Init() error {
}
var enableFederatedAvatar bool
- enableFederatedAvatarSetting, err := GetSettingNoCache(db.DefaultContext, KeyPictureEnableFederatedAvatar)
+ enableFederatedAvatarSetting, err := GetSetting(ctx, KeyPictureEnableFederatedAvatar)
if IsErrSettingIsNotExist(err) {
enableFederatedAvatar = setting_module.GetDefaultEnableFederatedAvatar(disableGravatar)
enableFederatedAvatarSetting = &Setting{SettingValue: strconv.FormatBool(enableFederatedAvatar)}
@@ -275,13 +284,13 @@ func Init() error {
if setting_module.OfflineMode {
disableGravatar = true
enableFederatedAvatar = false
- if !GetSettingBool(db.DefaultContext, KeyPictureDisableGravatar) {
- if err := SetSettingNoVersion(db.DefaultContext, KeyPictureDisableGravatar, "true"); err != nil {
+ if !GetSettingBool(ctx, KeyPictureDisableGravatar) {
+ if err := SetSettingNoVersion(ctx, KeyPictureDisableGravatar, "true"); err != nil {
return fmt.Errorf("Failed to set setting %q: %w", KeyPictureDisableGravatar, err)
}
}
- if GetSettingBool(db.DefaultContext, KeyPictureEnableFederatedAvatar) {
- if err := SetSettingNoVersion(db.DefaultContext, KeyPictureEnableFederatedAvatar, "false"); err != nil {
+ if GetSettingBool(ctx, KeyPictureEnableFederatedAvatar) {
+ if err := SetSettingNoVersion(ctx, KeyPictureEnableFederatedAvatar, "false"); err != nil {
return fmt.Errorf("Failed to set setting %q: %w", KeyPictureEnableFederatedAvatar, err)
}
}
diff --git a/models/system/setting_test.go b/models/system/setting_test.go
index fbd04088e..e6997ad78 100644
--- a/models/system/setting_test.go
+++ b/models/system/setting_test.go
@@ -40,10 +40,10 @@ func TestSettings(t *testing.T) {
value, err := system.GetSetting(db.DefaultContext, keyName)
assert.NoError(t, err)
- assert.EqualValues(t, updatedSetting.SettingValue, value)
+ assert.EqualValues(t, updatedSetting.SettingValue, value.SettingValue)
// get all settings
- settings, err = system.GetAllSettings()
+ settings, err = system.GetAllSettings(db.DefaultContext)
assert.NoError(t, err)
assert.Len(t, settings, 3)
assert.EqualValues(t, updatedSetting.SettingValue, settings[strings.ToLower(updatedSetting.SettingKey)].SettingValue)
@@ -51,7 +51,7 @@ func TestSettings(t *testing.T) {
// delete setting
err = system.DeleteSetting(db.DefaultContext, &system.Setting{SettingKey: strings.ToLower(keyName)})
assert.NoError(t, err)
- settings, err = system.GetAllSettings()
+ settings, err = system.GetAllSettings(db.DefaultContext)
assert.NoError(t, err)
assert.Len(t, settings, 2)
}
diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go
index 7e327f2bd..2bc41084b 100644
--- a/models/unittest/testdb.go
+++ b/models/unittest/testdb.go
@@ -113,7 +113,7 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
if err = storage.Init(); err != nil {
fatalTestError("storage.Init: %v\n", err)
}
- if err = system_model.Init(); err != nil {
+ if err = system_model.Init(db.DefaultContext); err != nil {
fatalTestError("models.Init: %v\n", err)
}
diff --git a/models/user/avatar.go b/models/user/avatar.go
index f08757b6a..72ccd090d 100644
--- a/models/user/avatar.go
+++ b/models/user/avatar.go
@@ -67,7 +67,7 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
useLocalAvatar := false
autoGenerateAvatar := false
- disableGravatar := system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar)
+ disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
switch {
case u.UseCustomAvatar: