diff options
author | Anthony Wang | 2023-03-12 23:17:11 +0000 |
---|---|---|
committer | Anthony Wang | 2023-03-12 23:19:57 +0000 |
commit | dbc3eb7fa071307e623fde961bfdff0894856948 (patch) | |
tree | 8961dc6a935d9ee89f5adf680a142d36900aaebb /models | |
parent | b4640101f3c8938ed689743606a79601201141ca (diff) | |
parent | e72290fd9aeb77a47311483d1d565e428ce40cd9 (diff) |
Merge remote-tracking branch 'origin/main' into forgejo-federation
Diffstat (limited to 'models')
51 files changed, 347 insertions, 194 deletions
diff --git a/models/actions/run.go b/models/actions/run.go index a8d991471..d5ab45a51 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -192,6 +192,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork if len(needs) > 0 || run.NeedApproval { status = StatusBlocked } + job.Name, _ = util.SplitStringAtByteN(job.Name, 255) runJobs = append(runJobs, &ActionRunJob{ RunID: run.ID, RepoID: run.RepoID, diff --git a/models/actions/task.go b/models/actions/task.go index 5b6206c34..ffec4c92a 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -298,8 +298,9 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask if len(workflowJob.Steps) > 0 { steps := make([]*ActionTaskStep, len(workflowJob.Steps)) for i, v := range workflowJob.Steps { + name, _ := util.SplitStringAtByteN(v.String(), 255) steps[i] = &ActionTaskStep{ - Name: v.String(), + Name: name, TaskID: task.ID, Index: int64(i), RepoID: task.RepoID, diff --git a/models/activities/action.go b/models/activities/action.go index 1412d2c05..c1d17517b 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -99,7 +99,7 @@ func (a *Action) TableIndices() []*schemas.Index { actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted") indices := []*schemas.Index{actUserIndex, repoIndex} - if setting.Database.UsePostgreSQL { + if setting.Database.Type.IsPostgreSQL() { cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType) cudIndex.AddColumn("created_unix", "user_id", "is_deleted") indices = append(indices, cudIndex) @@ -640,7 +640,7 @@ func DeleteIssueActions(ctx context.Context, repoID, issueID int64) error { // CountActionCreatedUnixString count actions where created_unix is an empty string func CountActionCreatedUnixString(ctx context.Context) (int64, error) { - if setting.Database.UseSQLite3 { + if setting.Database.Type.IsSQLite3() { return db.GetEngine(ctx).Where(`created_unix = ""`).Count(new(Action)) } return 0, nil @@ -648,7 +648,7 @@ func CountActionCreatedUnixString(ctx context.Context) (int64, error) { // FixActionCreatedUnixString set created_unix to zero if it is an empty string func FixActionCreatedUnixString(ctx context.Context) (int64, error) { - if setting.Database.UseSQLite3 { + if setting.Database.Type.IsSQLite3() { res, err := db.GetEngine(ctx).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`) if err != nil { return 0, err diff --git a/models/activities/action_test.go b/models/activities/action_test.go index 2fd86bb8f..7044bcc00 100644 --- a/models/activities/action_test.go +++ b/models/activities/action_test.go @@ -243,7 +243,7 @@ func TestGetFeedsCorrupted(t *testing.T) { } func TestConsistencyUpdateAction(t *testing.T) { - if !setting.Database.UseSQLite3 { + if !setting.Database.Type.IsSQLite3() { t.Skip("Test is only for SQLite database.") } assert.NoError(t, unittest.PrepareTestDatabase()) diff --git a/models/activities/user_heatmap.go b/models/activities/user_heatmap.go index 3370103a5..d3f0f0db7 100644 --- a/models/activities/user_heatmap.go +++ b/models/activities/user_heatmap.go @@ -39,9 +39,9 @@ func getUserHeatmapData(user *user_model.User, team *organization.Team, doer *us groupBy := "created_unix / 900 * 900" groupByName := "timestamp" // We need this extra case because mssql doesn't allow grouping by alias switch { - case setting.Database.UseMySQL: + case setting.Database.Type.IsMySQL(): groupBy = "created_unix DIV 900 * 900" - case setting.Database.UseMSSQL: + case setting.Database.Type.IsMSSQL(): groupByName = groupBy } diff --git a/models/auth/token_scope.go b/models/auth/token_scope.go index 38733a1c8..06c89fecc 100644 --- a/models/auth/token_scope.go +++ b/models/auth/token_scope.go @@ -32,6 +32,8 @@ const ( AccessTokenScopeAdminOrgHook AccessTokenScope = "admin:org_hook" + AccessTokenScopeAdminUserHook AccessTokenScope = "admin:user_hook" + AccessTokenScopeNotification AccessTokenScope = "notification" AccessTokenScopeUser AccessTokenScope = "user" @@ -64,7 +66,7 @@ type AccessTokenScopeBitmap uint64 const ( // AccessTokenScopeAllBits is the bitmap of all access token scopes, except `sudo`. AccessTokenScopeAllBits AccessTokenScopeBitmap = AccessTokenScopeRepoBits | - AccessTokenScopeAdminOrgBits | AccessTokenScopeAdminPublicKeyBits | AccessTokenScopeAdminOrgHookBits | + AccessTokenScopeAdminOrgBits | AccessTokenScopeAdminPublicKeyBits | AccessTokenScopeAdminOrgHookBits | AccessTokenScopeAdminUserHookBits | AccessTokenScopeNotificationBits | AccessTokenScopeUserBits | AccessTokenScopeDeleteRepoBits | AccessTokenScopePackageBits | AccessTokenScopeAdminGPGKeyBits | AccessTokenScopeAdminApplicationBits @@ -86,6 +88,8 @@ const ( AccessTokenScopeAdminOrgHookBits AccessTokenScopeBitmap = 1 << iota + AccessTokenScopeAdminUserHookBits AccessTokenScopeBitmap = 1 << iota + AccessTokenScopeNotificationBits AccessTokenScopeBitmap = 1 << iota AccessTokenScopeUserBits AccessTokenScopeBitmap = 1<<iota | AccessTokenScopeReadUserBits | AccessTokenScopeUserEmailBits | AccessTokenScopeUserFollowBits @@ -123,6 +127,7 @@ var allAccessTokenScopes = []AccessTokenScope{ AccessTokenScopeAdminPublicKey, AccessTokenScopeWritePublicKey, AccessTokenScopeReadPublicKey, AccessTokenScopeAdminRepoHook, AccessTokenScopeWriteRepoHook, AccessTokenScopeReadRepoHook, AccessTokenScopeAdminOrgHook, + AccessTokenScopeAdminUserHook, AccessTokenScopeNotification, AccessTokenScopeUser, AccessTokenScopeReadUser, AccessTokenScopeUserEmail, AccessTokenScopeUserFollow, AccessTokenScopeDeleteRepo, @@ -147,6 +152,7 @@ var allAccessTokenScopeBits = map[AccessTokenScope]AccessTokenScopeBitmap{ AccessTokenScopeWriteRepoHook: AccessTokenScopeWriteRepoHookBits, AccessTokenScopeReadRepoHook: AccessTokenScopeReadRepoHookBits, AccessTokenScopeAdminOrgHook: AccessTokenScopeAdminOrgHookBits, + AccessTokenScopeAdminUserHook: AccessTokenScopeAdminUserHookBits, AccessTokenScopeNotification: AccessTokenScopeNotificationBits, AccessTokenScopeUser: AccessTokenScopeUserBits, AccessTokenScopeReadUser: AccessTokenScopeReadUserBits, @@ -263,7 +269,7 @@ func (bitmap AccessTokenScopeBitmap) ToScope() AccessTokenScope { scope := AccessTokenScope(strings.Join(scopes, ",")) scope = AccessTokenScope(strings.ReplaceAll( string(scope), - "repo,admin:org,admin:public_key,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application", + "repo,admin:org,admin:public_key,admin:org_hook,admin:user_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application", "all", )) return scope diff --git a/models/auth/token_scope_test.go b/models/auth/token_scope_test.go index 1d7f4794a..b96a5fd46 100644 --- a/models/auth/token_scope_test.go +++ b/models/auth/token_scope_test.go @@ -40,8 +40,8 @@ func TestAccessTokenScope_Normalize(t *testing.T) { {"admin:gpg_key,write:gpg_key,user", "user,admin:gpg_key", nil}, {"admin:application,write:application,user", "user,admin:application", nil}, {"all", "all", nil}, - {"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application", "all", nil}, - {"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application,sudo", "all,sudo", nil}, + {"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,admin:user_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application", "all", nil}, + {"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,admin:user_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application,sudo", "all,sudo", nil}, } for _, test := range tests { diff --git a/models/db/common.go b/models/db/common.go index 76c7c119f..af6130c9f 100644 --- a/models/db/common.go +++ b/models/db/common.go @@ -15,7 +15,7 @@ import ( // BuildCaseInsensitiveLike returns a condition to check if the given value is like the given key case-insensitively. // Handles especially SQLite correctly as UPPER there only transforms ASCII letters. func BuildCaseInsensitiveLike(key, value string) builder.Cond { - if setting.Database.UseSQLite3 { + if setting.Database.Type.IsSQLite3() { return builder.Like{"UPPER(" + key + ")", util.ToUpperASCII(value)} } return builder.Like{"UPPER(" + key + ")", strings.ToUpper(value)} diff --git a/models/db/engine.go b/models/db/engine.go index 5020101d4..56dd209fd 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -101,12 +101,12 @@ func newXORMEngine() (*xorm.Engine, error) { var engine *xorm.Engine - if setting.Database.UsePostgreSQL && len(setting.Database.Schema) > 0 { + if setting.Database.Type.IsPostgreSQL() && len(setting.Database.Schema) > 0 { // OK whilst we sort out our schema issues - create a schema aware postgres registerPostgresSchemaDriver() engine, err = xorm.NewEngine("postgresschema", connStr) } else { - engine, err = xorm.NewEngine(setting.Database.Type, connStr) + engine, err = xorm.NewEngine(setting.Database.Type.String(), connStr) } if err != nil { diff --git a/models/db/index.go b/models/db/index.go index f840a62c8..7609d8fb6 100644 --- a/models/db/index.go +++ b/models/db/index.go @@ -73,7 +73,7 @@ func postgresGetNextResourceIndex(ctx context.Context, tableName string, groupID // GetNextResourceIndex generates a resource index, it must run in the same transaction where the resource is created func GetNextResourceIndex(ctx context.Context, tableName string, groupID int64) (int64, error) { - if setting.Database.UsePostgreSQL { + if setting.Database.Type.IsPostgreSQL() { return postgresGetNextResourceIndex(ctx, tableName, groupID) } diff --git a/models/db/iterate_test.go b/models/db/iterate_test.go index a713fe0d8..6bcf740c2 100644 --- a/models/db/iterate_test.go +++ b/models/db/iterate_test.go @@ -25,7 +25,7 @@ func TestIterate(t *testing.T) { return nil }) assert.NoError(t, err) - assert.EqualValues(t, 83, repoCnt) + assert.EqualValues(t, 84, repoCnt) err = db.Iterate(db.DefaultContext, nil, func(ctx context.Context, repoUnit *repo_model.RepoUnit) error { reopUnit2 := repo_model.RepoUnit{ID: repoUnit.ID} diff --git a/models/db/list_test.go b/models/db/list_test.go index ffef1e494..1295692ce 100644 --- a/models/db/list_test.go +++ b/models/db/list_test.go @@ -35,11 +35,11 @@ func TestFind(t *testing.T) { var repoUnits []repo_model.RepoUnit err := db.Find(db.DefaultContext, &opts, &repoUnits) assert.NoError(t, err) - assert.EqualValues(t, 83, len(repoUnits)) + assert.EqualValues(t, 84, len(repoUnits)) cnt, err := db.Count(db.DefaultContext, &opts, new(repo_model.RepoUnit)) assert.NoError(t, err) - assert.EqualValues(t, 83, cnt) + assert.EqualValues(t, 84, cnt) repoUnits = make([]repo_model.RepoUnit, 0, 10) newCnt, err := db.FindAndCount(db.DefaultContext, &opts, &repoUnits) diff --git a/models/db/sequence.go b/models/db/sequence.go index 6d801d022..f49ad935d 100644 --- a/models/db/sequence.go +++ b/models/db/sequence.go @@ -13,7 +13,7 @@ import ( // CountBadSequences looks for broken sequences from recreate-table mistakes func CountBadSequences(_ context.Context) (int64, error) { - if !setting.Database.UsePostgreSQL { + if !setting.Database.Type.IsPostgreSQL() { return 0, nil } @@ -34,7 +34,7 @@ func CountBadSequences(_ context.Context) (int64, error) { // FixBadSequences fixes for broken sequences from recreate-table mistakes func FixBadSequences(_ context.Context) error { - if !setting.Database.UsePostgreSQL { + if !setting.Database.Type.IsPostgreSQL() { return nil } diff --git a/models/fixtures/repo_unit.yml b/models/fixtures/repo_unit.yml index 503b8c9dd..ef0b7c1a9 100644 --- a/models/fixtures/repo_unit.yml +++ b/models/fixtures/repo_unit.yml @@ -569,3 +569,9 @@ type: 3 config: "{\"IgnoreWhitespaceConflicts\":false,\"AllowMerge\":true,\"AllowRebase\":true,\"AllowRebaseMerge\":true,\"AllowSquash\":true}" created_unix: 946684810 + +- + id: 84 + repo_id: 56 + type: 1 + created_unix: 946684810 diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml index 58f9b919a..32ba8744d 100644 --- a/models/fixtures/repository.yml +++ b/models/fixtures/repository.yml @@ -25,7 +25,7 @@ fork_id: 0 is_template: false template_id: 0 - size: 6708 + size: 7028 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false @@ -1634,3 +1634,16 @@ is_private: true num_issues: 1 status: 0 + +- + id: 56 + owner_id: 2 + owner_name: user2 + lower_name: readme-test + name: readme-test + default_branch: master + is_empty: false + is_archived: false + is_private: true + status: 0 + num_issues: 0 diff --git a/models/fixtures/team.yml b/models/fixtures/team.yml index dd434d78a..be988b8fc 100644 --- a/models/fixtures/team.yml +++ b/models/fixtures/team.yml @@ -151,3 +151,25 @@ num_members: 1 includes_all_repositories: false can_create_org_repo: true + +- + id: 15 + org_id: 22 + lower_name: owners + name: Owners + authorize: 4 # owner + num_repos: 0 + num_members: 0 + includes_all_repositories: false + can_create_org_repo: true + +- + id: 16 + org_id: 23 + lower_name: owners + name: Owners + authorize: 4 # owner + num_repos: 0 + num_members: 0 + includes_all_repositories: false + can_create_org_repo: true
\ No newline at end of file diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index b1c7fc003..ce54defac 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -66,7 +66,7 @@ num_followers: 2 num_following: 1 num_stars: 2 - num_repos: 11 + num_repos: 12 num_teams: 0 num_members: 0 visibility: 0 @@ -807,7 +807,7 @@ num_following: 0 num_stars: 0 num_repos: 2 - num_teams: 0 + num_teams: 1 num_members: 0 visibility: 1 repo_admin_change_team_access: false @@ -844,7 +844,7 @@ num_following: 0 num_stars: 0 num_repos: 2 - num_teams: 0 + num_teams: 1 num_members: 0 visibility: 2 repo_admin_change_team_access: false diff --git a/models/fixtures/webhook.yml b/models/fixtures/webhook.yml index 5563dcada..f62bae1f3 100644 --- a/models/fixtures/webhook.yml +++ b/models/fixtures/webhook.yml @@ -16,7 +16,7 @@ - id: 3 - org_id: 3 + owner_id: 3 repo_id: 3 url: www.example.com/url3 content_type: 1 # json diff --git a/models/git/commit_status.go b/models/git/commit_status.go index 489507f71..82cbb2363 100644 --- a/models/git/commit_status.go +++ b/models/git/commit_status.go @@ -65,7 +65,7 @@ func postgresGetCommitStatusIndex(ctx context.Context, repoID int64, sha string) // GetNextCommitStatusIndex retried 3 times to generate a resource index func GetNextCommitStatusIndex(ctx context.Context, repoID int64, sha string) (int64, error) { - if setting.Database.UsePostgreSQL { + if setting.Database.Type.IsPostgreSQL() { return postgresGetCommitStatusIndex(ctx, repoID, sha) } diff --git a/models/git/lfs_lock.go b/models/git/lfs_lock.go index 25480f3f9..178fa72f0 100644 --- a/models/git/lfs_lock.go +++ b/models/git/lfs_lock.go @@ -6,7 +6,6 @@ package git import ( "context" "fmt" - "path" "strings" "time" @@ -17,6 +16,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" ) // LFSLock represents a git lfs lock of repository. @@ -34,11 +34,7 @@ func init() { // BeforeInsert is invoked from XORM before inserting an object of this type. func (l *LFSLock) BeforeInsert() { - l.Path = cleanPath(l.Path) -} - -func cleanPath(p string) string { - return path.Clean("/" + p)[1:] + l.Path = util.CleanPath(l.Path) } // CreateLFSLock creates a new lock. @@ -53,7 +49,7 @@ func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLo return nil, err } - lock.Path = cleanPath(lock.Path) + lock.Path = util.CleanPath(lock.Path) lock.RepoID = repo.ID l, err := GetLFSLock(dbCtx, repo, lock.Path) @@ -73,7 +69,7 @@ func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLo // GetLFSLock returns release by given path. func GetLFSLock(ctx context.Context, repo *repo_model.Repository, path string) (*LFSLock, error) { - path = cleanPath(path) + path = util.CleanPath(path) rel := &LFSLock{RepoID: repo.ID} has, err := db.GetEngine(ctx).Where("lower(path) = ?", strings.ToLower(path)).Get(rel) if err != nil { diff --git a/models/issues/label.go b/models/issues/label.go index 90e4eb458..35c649e8f 100644 --- a/models/issues/label.go +++ b/models/issues/label.go @@ -7,12 +7,12 @@ package issues import ( "context" "fmt" - "regexp" "strconv" "strings" "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/label" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -78,9 +78,6 @@ func (err ErrLabelNotExist) Unwrap() error { return util.ErrNotExist } -// LabelColorPattern is a regexp witch can validate LabelColor -var LabelColorPattern = regexp.MustCompile("^#?(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})$") - // Label represents a label of repository for issues. type Label struct { ID int64 `xorm:"pk autoincr"` @@ -109,12 +106,12 @@ func init() { } // CalOpenIssues sets the number of open issues of a label based on the already stored number of closed issues. -func (label *Label) CalOpenIssues() { - label.NumOpenIssues = label.NumIssues - label.NumClosedIssues +func (l *Label) CalOpenIssues() { + l.NumOpenIssues = l.NumIssues - l.NumClosedIssues } // CalOpenOrgIssues calculates the open issues of a label for a specific repo -func (label *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64) { +func (l *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64) { counts, _ := CountIssuesByRepo(ctx, &IssuesOptions{ RepoID: repoID, LabelIDs: []int64{labelID}, @@ -122,22 +119,22 @@ func (label *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64) }) for _, count := range counts { - label.NumOpenRepoIssues += count + l.NumOpenRepoIssues += count } } // LoadSelectedLabelsAfterClick calculates the set of selected labels when a label is clicked -func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64, currentSelectedExclusiveScopes []string) { +func (l *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64, currentSelectedExclusiveScopes []string) { var labelQuerySlice []string labelSelected := false - labelID := strconv.FormatInt(label.ID, 10) - labelScope := label.ExclusiveScope() + labelID := strconv.FormatInt(l.ID, 10) + labelScope := l.ExclusiveScope() for i, s := range currentSelectedLabels { - if s == label.ID { + if s == l.ID { labelSelected = true - } else if -s == label.ID { + } else if -s == l.ID { labelSelected = true - label.IsExcluded = true + l.IsExcluded = true } else if s != 0 { // Exclude other labels in the same scope from selection if s < 0 || labelScope == "" || labelScope != currentSelectedExclusiveScopes[i] { @@ -148,23 +145,23 @@ func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64, if !labelSelected { labelQuerySlice = append(labelQuerySlice, labelID) } - label.IsSelected = labelSelected - label.QueryString = strings.Join(labelQuerySlice, ",") + l.IsSelected = labelSelected + l.QueryString = strings.Join(labelQuerySlice, ",") } // BelongsToOrg returns true if label is an organization label -func (label *Label) BelongsToOrg() bool { - return label.OrgID > 0 +func (l *Label) BelongsToOrg() bool { + return l.OrgID > 0 } // BelongsToRepo returns true if label is a repository label -func (label *Label) BelongsToRepo() bool { - return label.RepoID > 0 +func (l *Label) BelongsToRepo() bool { + return l.RepoID > 0 } // Get color as RGB values in 0..255 range -func (label *Label) ColorRGB() (float64, float64, float64, error) { - color, err := strconv.ParseUint(label.Color[1:], 16, 64) +func (l *Label) ColorRGB() (float64, float64, float64, error) { + color, err := strconv.ParseUint(l.Color[1:], 16, 64) if err != nil { return 0, 0, 0, err } @@ -176,9 +173,9 @@ func (label *Label) ColorRGB() (float64, float64, float64, error) { } // Determine if label text should be light or dark to be readable on background color -func (label *Label) UseLightTextColor() bool { - if strings.HasPrefix(label.Color, "#") { - if r, g, b, err := label.ColorRGB(); err == nil { +func (l *Label) UseLightTextColor() bool { + if strings.HasPrefix(l.Color, "#") { + if r, g, b, err := l.ColorRGB(); err == nil { // Perceived brightness from: https://www.w3.org/TR/AERT/#color-contrast // In the future WCAG 3 APCA may be a better solution brightness := (0.299*r + 0.587*g + 0.114*b) / 255 @@ -190,40 +187,26 @@ func (label *Label) UseLightTextColor() bool { } // Return scope substring of label name, or empty string if none exists -func (label *Label) ExclusiveScope() string { - if !label.Exclusive { +func (l *Label) ExclusiveScope() string { + if !l.Exclusive { return "" } - lastIndex := strings.LastIndex(label.Name, "/") - if lastIndex == -1 || lastIndex == 0 || lastIndex == len(label.Name)-1 { + lastIndex := strings.LastIndex(l.Name, "/") + if lastIndex == -1 || lastIndex == 0 || lastIndex == len(l.Name)-1 { return "" } - return label.Name[:lastIndex] + return l.Name[:lastIndex] } // NewLabel creates a new label -func NewLabel(ctx context.Context, label *Label) error { - if !LabelColorPattern.MatchString(label.Color) { - return fmt.Errorf("bad color code: %s", label.Color) - } - - // normalize case - label.Color = strings.ToLower(label.Color) - - // add leading hash - if label.Color[0] != '#' { - label.Color = "#" + label.Color - } - - // convert 3-character shorthand into 6-character version - if len(label.Color) == 4 { - r := label.Color[1] - g := label.Color[2] - b := label.Color[3] - label.Color = fmt.Sprintf("#%c%c%c%c%c%c", r, r, g, g, b, b) +func NewLabel(ctx context.Context, l *Label) error { + color, err := label.NormalizeColor(l.Color) + if err != nil { + return err } + l.Color = color - return db.Insert(ctx, label) + return db.Insert(ctx, l) } // NewLabels creates new labels @@ -234,11 +217,14 @@ func NewLabels(labels ...*Label) error { } defer committer.Close() - for _, label := range labels { - if !LabelColorPattern.MatchString(label.Color) { - return fmt.Errorf("bad color code: %s", label.Color) + for _, l := range labels { + color, err := label.NormalizeColor(l.Color) + if err != nil { + return err } - if err := db.Insert(ctx, label); err != nil { + l.Color = color + + if err := db.Insert(ctx, l); err != nil { return err } } @@ -247,15 +233,18 @@ func NewLabels(labels ...*Label) error { // UpdateLabel updates label information. func UpdateLabel(l *Label) error { - if !LabelColorPattern.MatchString(l.Color) { - return fmt.Errorf("bad color code: %s", l.Color) + color, err := label.NormalizeColor(l.Color) + if err != nil { + return err } + l.Color = color + return updateLabelCols(db.DefaultContext, l, "name", "description", "color", "exclusive") } // DeleteLabel delete a label func DeleteLabel(id, labelID int64) error { - label, err := GetLabelByID(db.DefaultContext, labelID) + l, err := GetLabelByID(db.DefaultContext, labelID) if err != nil { if IsErrLabelNotExist(err) { return nil @@ -271,10 +260,10 @@ func DeleteLabel(id, labelID int64) error { sess := db.GetEngine(ctx) - if label.BelongsToOrg() && label.OrgID != id { + if l.BelongsToOrg() && l.OrgID != id { return nil } - if label.BelongsToRepo() && label.RepoID != id { + if l.BelongsToRepo() && l.RepoID != id { return nil } @@ -682,14 +671,14 @@ func newIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *us if err = issue.LoadRepo(ctx); err != nil { return err } - for _, label := range labels { + for _, l := range labels { // Don't add already present labels and invalid labels - if HasIssueLabel(ctx, issue.ID, label.ID) || - (label.RepoID != issue.RepoID && label.OrgID != issue.Repo.OwnerID) { + if HasIssueLabel(ctx, issue.ID, l.ID) || + (l.RepoID != issue.RepoID && l.OrgID != issue.Repo.OwnerID) { continue } - if err = newIssueLabel(ctx, issue, label, doer); err != nil { + if err = newIssueLabel(ctx, issue, l, doer); err != nil { return fmt.Errorf("newIssueLabel: %w", err) } } diff --git a/models/issues/label_test.go b/models/issues/label_test.go index 0e45e0db0..1f6ce4f42 100644 --- a/models/issues/label_test.go +++ b/models/issues/label_test.go @@ -15,8 +15,6 @@ import ( "github.com/stretchr/testify/assert" ) -// TODO TestGetLabelTemplateFile - func TestLabel_CalOpenIssues(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}) diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index f4efd916c..9d361c5c5 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -52,13 +52,16 @@ func listPullRequestStatement(baseRepoID int64, opts *PullRequestsOptions) (*xor // GetUnmergedPullRequestsByHeadInfo returns all pull requests that are open and has not been merged // by given head information (repo and branch). -func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequest, error) { +// arg `includeClosed` controls whether the SQL returns closed PRs +func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string, includeClosed bool) ([]*PullRequest, error) { prs := make([]*PullRequest, 0, 2) - return prs, db.GetEngine(db.DefaultContext). - Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND issue.is_closed = ? AND flow = ?", - repoID, branch, false, false, PullRequestFlowGithub). + sess := db.GetEngine(db.DefaultContext). Join("INNER", "issue", "issue.id = pull_request.issue_id"). - Find(&prs) + Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND flow = ?", repoID, branch, false, PullRequestFlowGithub) + if !includeClosed { + sess.Where("issue.is_closed = ?", false) + } + return prs, sess.Find(&prs) } // CanMaintainerWriteToBranch check whether user is a maintainer and could write to the branch @@ -71,7 +74,7 @@ func CanMaintainerWriteToBranch(p access_model.Permission, branch string, user * return false } - prs, err := GetUnmergedPullRequestsByHeadInfo(p.Units[0].RepoID, branch) + prs, err := GetUnmergedPullRequestsByHeadInfo(p.Units[0].RepoID, branch, false) if err != nil { return false } @@ -111,6 +114,7 @@ func GetUnmergedPullRequestsByBaseInfo(repoID int64, branch string) ([]*PullRequ return prs, db.GetEngine(db.DefaultContext). Where("base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?", repoID, branch, false, false). + OrderBy("issue.updated_unix DESC"). Join("INNER", "issue", "issue.id=pull_request.issue_id"). Find(&prs) } diff --git a/models/issues/pull_test.go b/models/issues/pull_test.go index 8ce8eecc4..bcd33329e 100644 --- a/models/issues/pull_test.go +++ b/models/issues/pull_test.go @@ -118,7 +118,7 @@ func TestHasUnmergedPullRequestsByHeadInfo(t *testing.T) { func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(1, "branch2") + prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(1, "branch2", false) assert.NoError(t, err) assert.Len(t, prs, 1) for _, pr := range prs { diff --git a/models/issues/stopwatch.go b/models/issues/stopwatch.go index 6bf936c5d..c8cd5ad33 100644 --- a/models/issues/stopwatch.go +++ b/models/issues/stopwatch.go @@ -9,6 +9,7 @@ import ( "time" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -132,12 +133,26 @@ func StopwatchExists(userID, issueID int64) bool { } // HasUserStopwatch returns true if the user has a stopwatch -func HasUserStopwatch(ctx context.Context, userID int64) (exists bool, sw *Stopwatch, err error) { - sw = new(Stopwatch) +func HasUserStopwatch(ctx context.Context, userID int64) (exists bool, sw *Stopwatch, issue *Issue, err error) { + type stopwatchIssueRepo struct { + Stopwatch `xorm:"extends"` + Issue `xorm:"extends"` + repo.Repository `xorm:"extends"` + } + + swIR := new(stopwatchIssueRepo) exists, err = db.GetEngine(ctx). + Table("stopwatch"). Where("user_id = ?", userID). - Get(sw) - return exists, sw, err + Join("INNER", "issue", "issue.id = stopwatch.issue_id"). + Join("INNER", "repository", "repository.id = issue.repo_id"). + Get(swIR) + if exists { + sw = &swIR.Stopwatch + issue = &swIR.Issue + issue.Repo = &swIR.Repository + } + return exists, sw, issue, err } // FinishIssueStopwatchIfPossible if stopwatch exist then finish it otherwise ignore @@ -217,23 +232,18 @@ func CreateIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss } // if another stopwatch is running: stop it - exists, sw, err := HasUserStopwatch(ctx, user.ID) + exists, _, otherIssue, err := HasUserStopwatch(ctx, user.ID) if err != nil { return err } if exists { - issue, err := GetIssueByID(ctx, sw.IssueID) - if err != nil { - return err - } - - if err := FinishIssueStopwatch(ctx, user, issue); err != nil { + if err := FinishIssueStopwatch(ctx, user, otherIssue); err != nil { return err } } // Create stopwatch - sw = &Stopwatch{ + sw := &Stopwatch{ UserID: user.ID, IssueID: issue.ID, } diff --git a/models/issues/stopwatch_test.go b/models/issues/stopwatch_test.go index ec2778aa8..ea3827a1f 100644 --- a/models/issues/stopwatch_test.go +++ b/models/issues/stopwatch_test.go @@ -45,12 +45,12 @@ func TestStopwatchExists(t *testing.T) { func TestHasUserStopwatch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - exists, sw, err := issues_model.HasUserStopwatch(db.DefaultContext, 1) + exists, sw, _, err := issues_model.HasUserStopwatch(db.DefaultContext, 1) assert.NoError(t, err) assert.True(t, exists) assert.Equal(t, int64(1), sw.ID) - exists, _, err = issues_model.HasUserStopwatch(db.DefaultContext, 3) + exists, _, _, err = issues_model.HasUserStopwatch(db.DefaultContext, 3) assert.NoError(t, err) assert.False(t, exists) } diff --git a/models/migrations/base/db.go b/models/migrations/base/db.go index dcf99c96a..b038ad733 100644 --- a/models/migrations/base/db.go +++ b/models/migrations/base/db.go @@ -89,7 +89,7 @@ func RecreateTable(sess *xorm.Session, bean interface{}) error { hasID = hasID || (column.IsPrimaryKey && column.IsAutoIncrement) } - if hasID && setting.Database.UseMSSQL { + if hasID && setting.Database.Type.IsMSSQL() { if _, err := sess.Exec(fmt.Sprintf("SET IDENTITY_INSERT `%s` ON", tempTableName)); err != nil { log.Error("Unable to set identity insert for table %s. Error: %v", tempTableName, err) return err @@ -143,7 +143,7 @@ func RecreateTable(sess *xorm.Session, bean interface{}) error { return err } - if hasID && setting.Database.UseMSSQL { + if hasID && setting.Database.Type.IsMSSQL() { if _, err := sess.Exec(fmt.Sprintf("SET IDENTITY_INSERT `%s` OFF", tempTableName)); err != nil { log.Error("Unable to switch off identity insert for table %s. Error: %v", tempTableName, err) return err @@ -151,7 +151,7 @@ func RecreateTable(sess *xorm.Session, bean interface{}) error { } switch { - case setting.Database.UseSQLite3: + case setting.Database.Type.IsSQLite3(): // SQLite will drop all the constraints on the old table if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil { log.Error("Unable to drop old table %s. Error: %v", tableName, err) @@ -178,7 +178,7 @@ func RecreateTable(sess *xorm.Session, bean interface{}) error { return err } - case setting.Database.UseMySQL: + case setting.Database.Type.IsMySQL(): // MySQL will drop all the constraints on the old table if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil { log.Error("Unable to drop old table %s. Error: %v", tableName, err) @@ -205,7 +205,7 @@ func RecreateTable(sess *xorm.Session, bean interface{}) error { log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err) return err } - case setting.Database.UsePostgreSQL: + case setting.Database.Type.IsPostgreSQL(): var originalSequences []string type sequenceData struct { LastValue int `xorm:"'last_value'"` @@ -296,7 +296,7 @@ func RecreateTable(sess *xorm.Session, bean interface{}) error { } - case setting.Database.UseMSSQL: + case setting.Database.Type.IsMSSQL(): // MSSQL will drop all the constraints on the old table if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil { log.Error("Unable to drop old table %s. Error: %v", tableName, err) @@ -323,7 +323,7 @@ func DropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin // TODO: This will not work if there are foreign keys switch { - case setting.Database.UseSQLite3: + case setting.Database.Type.IsSQLite3(): // First drop the indexes on the columns res, errIndex := sess.Query(fmt.Sprintf("PRAGMA index_list(`%s`)", tableName)) if errIndex != nil { @@ -405,7 +405,7 @@ func DropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin return err } - case setting.Database.UsePostgreSQL: + case setting.Database.Type.IsPostgreSQL(): cols := "" for _, col := range columnNames { if cols != "" { @@ -416,7 +416,7 @@ func DropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil { return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err) } - case setting.Database.UseMySQL: + case setting.Database.Type.IsMySQL(): // Drop indexes on columns first sql := fmt.Sprintf("SHOW INDEX FROM %s WHERE column_name IN ('%s')", tableName, strings.Join(columnNames, "','")) res, err := sess.Query(sql) @@ -444,7 +444,7 @@ func DropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil { return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err) } - case setting.Database.UseMSSQL: + case setting.Database.Type.IsMSSQL(): cols := "" for _, col := range columnNames { if cols != "" { @@ -543,13 +543,13 @@ func newXORMEngine() (*xorm.Engine, error) { func deleteDB() error { switch { - case setting.Database.UseSQLite3: + case setting.Database.Type.IsSQLite3(): if err := util.Remove(setting.Database.Path); err != nil { return err } return os.MkdirAll(path.Dir(setting.Database.Path), os.ModePerm) - case setting.Database.UseMySQL: + case setting.Database.Type.IsMySQL(): db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/", setting.Database.User, setting.Database.Passwd, setting.Database.Host)) if err != nil { @@ -565,7 +565,7 @@ func deleteDB() error { return err } return nil - case setting.Database.UsePostgreSQL: + case setting.Database.Type.IsPostgreSQL(): db, err := sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/?sslmode=%s", setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.SSLMode)) if err != nil { @@ -612,7 +612,7 @@ func deleteDB() error { } return nil } - case setting.Database.UseMSSQL: + case setting.Database.Type.IsMSSQL(): host, port := setting.ParseMSSQLHostPort(setting.Database.Host) db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, "master", setting.Database.User, setting.Database.Passwd)) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 585457e47..4cbcd95d2 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -467,6 +467,8 @@ var migrations = []Migration{ // v244 -> v245 NewMigration("Add NeedApproval to actions tables", v1_20.AddNeedApprovalToActionRun), + // v245 -> v246 + NewMigration("Rename Webhook org_id to owner_id", v1_20.RenameWebhookOrgToOwner), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_12/v139.go b/models/migrations/v1_12/v139.go index 725b8fa30..279aa7df8 100644 --- a/models/migrations/v1_12/v139.go +++ b/models/migrations/v1_12/v139.go @@ -13,9 +13,9 @@ func PrependRefsHeadsToIssueRefs(x *xorm.Engine) error { var query string switch { - case setting.Database.UseMSSQL: + case setting.Database.Type.IsMSSQL(): query = "UPDATE `issue` SET `ref` = 'refs/heads/' + `ref` WHERE `ref` IS NOT NULL AND `ref` <> '' AND `ref` NOT LIKE 'refs/%'" - case setting.Database.UseMySQL: + case setting.Database.Type.IsMySQL(): query = "UPDATE `issue` SET `ref` = CONCAT('refs/heads/', `ref`) WHERE `ref` IS NOT NULL AND `ref` <> '' AND `ref` NOT LIKE 'refs/%';" default: query = "UPDATE `issue` SET `ref` = 'refs/heads/' || `ref` WHERE `ref` IS NOT NULL AND `ref` <> '' AND `ref` NOT LIKE 'refs/%'" diff --git a/models/migrations/v1_13/v140.go b/models/migrations/v1_13/v140.go index 3de9eaaf7..4d87b955f 100644 --- a/models/migrations/v1_13/v140.go +++ b/models/migrations/v1_13/v140.go @@ -41,7 +41,7 @@ func FixLanguageStatsToSaveSize(x *xorm.Engine) error { // Delete language stat statuses truncExpr := "TRUNCATE TABLE" - if setting.Database.UseSQLite3 { + if setting.Database.Type.IsSQLite3() { truncExpr = "DELETE FROM" } diff --git a/models/migrations/v1_13/v145.go b/models/migrations/v1_13/v145.go index c96e79f8a..ee40bfc77 100644 --- a/models/migrations/v1_13/v145.go +++ b/models/migrations/v1_13/v145.go @@ -21,7 +21,7 @@ func IncreaseLanguageField(x *xorm.Engine) error { return err } - if setting.Database.UseSQLite3 { + if setting.Database.Type.IsSQLite3() { // SQLite maps VARCHAR to TEXT without size so we're done return nil } @@ -41,11 +41,11 @@ func IncreaseLanguageField(x *xorm.Engine) error { } switch { - case setting.Database.UseMySQL: + case setting.Database.Type.IsMySQL(): if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat MODIFY COLUMN language %s", sqlType)); err != nil { return err } - case setting.Database.UseMSSQL: + case setting.Database.Type.IsMSSQL(): // Yet again MSSQL just has to be awkward. // Here we have to drop the constraints first and then rebuild them constraints := make([]string, 0) @@ -71,7 +71,7 @@ func IncreaseLanguageField(x *xorm.Engine) error { if err := sess.CreateUniques(new(LanguageStat)); err != nil { return err } - case setting.Database.UsePostgreSQL: + case setting.Database.Type.IsPostgreSQL(): if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE language_stat ALTER COLUMN language TYPE %s", sqlType)); err != nil { return err } diff --git a/models/migrations/v1_13/v151.go b/models/migrations/v1_13/v151.go index 9490c1778..9aa71ec29 100644 --- a/models/migrations/v1_13/v151.go +++ b/models/migrations/v1_13/v151.go @@ -17,13 +17,13 @@ import ( func SetDefaultPasswordToArgon2(x *xorm.Engine) error { switch { - case setting.Database.UseMySQL: + case setting.Database.Type.IsMySQL(): _, err := x.Exec("ALTER TABLE `user` ALTER passwd_hash_algo SET DEFAULT 'argon2';") return err - case setting.Database.UsePostgreSQL: + case setting.Database.Type.IsPostgreSQL(): _, err := x.Exec("ALTER TABLE `user` ALTER COLUMN passwd_hash_algo SET DEFAULT 'argon2';") return err - case setting.Database.UseMSSQL: + case setting.Database.Type.IsMSSQL(): // need to find the constraint and drop it, then recreate it. sess := x.NewSession() defer sess.Close() @@ -53,7 +53,7 @@ func SetDefaultPasswordToArgon2(x *xorm.Engine) error { } return sess.Commit() - case setting.Database.UseSQLite3: + case setting.Database.Type.IsSQLite3(): // drop through default: log.Fatal("Unrecognized DB") diff --git a/models/migrations/v1_14/v158.go b/models/migrations/v1_14/v158.go index 7ea80a659..2029829ff 100644 --- a/models/migrations/v1_14/v158.go +++ b/models/migrations/v1_14/v158.go @@ -62,7 +62,7 @@ func UpdateCodeCommentReplies(x *xorm.Engine) error { return err } - if setting.Database.UseMSSQL { + if setting.Database.Type.IsMSSQL() { if _, err := sess.Exec(sqlSelect + " INTO #temp_comments" + sqlTail); err != nil { log.Error("unable to create temporary table") return err @@ -72,13 +72,13 @@ func UpdateCodeCommentReplies(x *xorm.Engine) error { comments := make([]*Comment, 0, batchSize) switch { - case setting.Database.UseMySQL: + case setting.Database.Type.IsMySQL(): sqlCmd = sqlSelect + sqlTail + " LIMIT " + strconv.Itoa(batchSize) + ", " + strconv.Itoa(start) - case setting.Database.UsePostgreSQL: + case setting.Database.Type.IsPostgreSQL(): fallthrough - case setting.Database.UseSQLite3: + case setting.Database.Type.IsSQLite3(): sqlCmd = sqlSelect + sqlTail + " LIMIT " + strconv.Itoa(batchSize) + " OFFSET " + strconv.Itoa(start) - case setting.Database.UseMSSQL: + case setting.Database.Type.IsMSSQL(): sqlCmd = "SELECT TOP " + strconv.Itoa(batchSize) + " * FROM #temp_comments WHERE " + "(id NOT IN ( SELECT TOP " + strconv.Itoa(start) + " id FROM #temp_comments ORDER BY id )) ORDER BY id" default: diff --git a/models/migrations/v1_14/v175.go b/models/migrations/v1_14/v175.go index f1b9b974c..70d72b260 100644 --- a/models/migrations/v1_14/v175.go +++ b/models/migrations/v1_14/v175.go @@ -14,7 +14,7 @@ import ( ) func FixPostgresIDSequences(x *xorm.Engine) error { - if !setting.Database.UsePostgreSQL { + if !setting.Database.Type.IsPostgreSQL() { return nil } diff --git a/models/migrations/v1_15/v184.go b/models/migrations/v1_15/v184.go index 48f8b6216..caf41b604 100644 --- a/models/migrations/v1_15/v184.go +++ b/models/migrations/v1_15/v184.go @@ -54,11 +54,11 @@ func RenameTaskErrorsToMessage(x *xorm.Engine) error { } switch { - case setting.Database.UseMySQL: + case setting.Database.Type.IsMySQL(): if _, err := sess.Exec("ALTER TABLE `task` CHANGE errors message text"); err != nil { return err } - case setting.Database.UseMSSQL: + case setting.Database.Type.IsMSSQL(): if _, err := sess.Exec("sp_rename 'task.errors', 'message', 'COLUMN'"); err != nil { return err } diff --git a/models/migrations/v1_16/v191.go b/models/migrations/v1_16/v191.go index 2d2c3d1a5..c618783c0 100644 --- a/models/migrations/v1_16/v191.go +++ b/models/migrations/v1_16/v191.go @@ -16,7 +16,7 @@ func AlterIssueAndCommentTextFieldsToLongText(x *xorm.Engine) error { return err } - if setting.Database.UseMySQL { + if setting.Database.Type.IsMySQL() { if _, err := sess.Exec("ALTER TABLE `issue` CHANGE `content` `content` LONGTEXT"); err != nil { return err } diff --git a/models/migrations/v1_17/v217.go b/models/migrations/v1_17/v217.go index 3ca9215f0..3f970b68a 100644 --- a/models/migrations/v1_17/v217.go +++ b/models/migrations/v1_17/v217.go @@ -16,7 +16,7 @@ func AlterHookTaskTextFieldsToLongText(x *xorm.Engine) error { return err } - if setting.Database.UseMySQL { + if setting.Database.Type.IsMySQL() { if _, err := sess.Exec("ALTER TABLE `hook_task` CHANGE `payload_content` `payload_content` LONGTEXT, CHANGE `request_content` `request_content` LONGTEXT, change `response_content` `response_content` LONGTEXT"); err != nil { return err } diff --git a/models/migrations/v1_17/v218.go b/models/migrations/v1_17/v218.go index 675fd1df9..ae91ba0c4 100644 --- a/models/migrations/v1_17/v218.go +++ b/models/migrations/v1_17/v218.go @@ -38,7 +38,7 @@ func (*improveActionTableIndicesAction) TableIndices() []*schemas.Index { actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType) actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted") indices := []*schemas.Index{actUserIndex, repoIndex} - if setting.Database.UsePostgreSQL { + if setting.Database.Type.IsPostgreSQL() { cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType) cudIndex.AddColumn("created_unix", "user_id", "is_deleted") indices = append(indices, cudIndex) diff --git a/models/migrations/v1_17/v223.go b/models/migrations/v1_17/v223.go index a23d9916b..6c61dbc53 100644 --- a/models/migrations/v1_17/v223.go +++ b/models/migrations/v1_17/v223.go @@ -65,11 +65,11 @@ func RenameCredentialIDBytes(x *xorm.Engine) error { } switch { - case setting.Database.UseMySQL: + case setting.Database.Type.IsMySQL(): if _, err := sess.Exec("ALTER TABLE `webauthn_credential` CHANGE credential_id_bytes credential_id VARBINARY(1024)"); err != nil { return err } - case setting.Database.UseMSSQL: + case setting.Database.Type.IsMSSQL(): if _, err := sess.Exec("sp_rename 'webauthn_credential.credential_id_bytes', 'credential_id', 'COLUMN'"); err != nil { return err } diff --git a/models/migrations/v1_18/v225.go b/models/migrations/v1_18/v225.go index 786772c14..b0ac3777f 100644 --- a/models/migrations/v1_18/v225.go +++ b/models/migrations/v1_18/v225.go @@ -16,7 +16,7 @@ func AlterPublicGPGKeyContentFieldsToMediumText(x *xorm.Engine) error { return err } - if setting.Database.UseMySQL { + if setting.Database.Type.IsMySQL() { if _, err := sess.Exec("ALTER TABLE `gpg_key` CHANGE `content` `content` MEDIUMTEXT"); err != nil { return err } diff --git a/models/migrations/v1_19/v232.go b/models/migrations/v1_19/v232.go index 89b595c54..9caf587c1 100644 --- a/models/migrations/v1_19/v232.go +++ b/models/migrations/v1_19/v232.go @@ -16,7 +16,7 @@ func AlterPackageVersionMetadataToLongText(x *xorm.Engine) error { return err } - if setting.Database.UseMySQL { + if setting.Database.Type.IsMySQL() { if _, err := sess.Exec("ALTER TABLE `package_version` MODIFY COLUMN `metadata_json` LONGTEXT"); err != nil { return err } diff --git a/models/migrations/v1_19/v242.go b/models/migrations/v1_19/v242.go index 517c7767b..447083521 100644 --- a/models/migrations/v1_19/v242.go +++ b/models/migrations/v1_19/v242.go @@ -17,7 +17,7 @@ func AlterPublicGPGKeyImportContentFieldToMediumText(x *xorm.Engine) error { return err } - if setting.Database.UseMySQL { + if setting.Database.Type.IsMySQL() { if _, err := sess.Exec("ALTER TABLE `gpg_key_import` CHANGE `content` `content` MEDIUMTEXT"); err != nil { return err } diff --git a/models/migrations/v1_20/v245.go b/models/migrations/v1_20/v245.go new file mode 100644 index 000000000..466f21c23 --- /dev/null +++ b/models/migrations/v1_20/v245.go @@ -0,0 +1,74 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_20 //nolint + +import ( + "context" + "fmt" + + "code.gitea.io/gitea/models/migrations/base" + "code.gitea.io/gitea/modules/setting" + + "xorm.io/xorm" +) + +func RenameWebhookOrgToOwner(x *xorm.Engine) error { + type Webhook struct { + OrgID int64 `xorm:"INDEX"` + } + + // This migration maybe rerun so that we should check if it has been run + ownerExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webhook", "owner_id") + if err != nil { + return err + } + + if ownerExist { + orgExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webhook", "org_id") + if err != nil { + return err + } + if !orgExist { + return nil + } + } + + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + + if err := sess.Sync2(new(Webhook)); err != nil { + return err + } + + if ownerExist { + if err := base.DropTableColumns(sess, "webhook", "owner_id"); err != nil { + return err + } + } + + switch { + case setting.Database.Type.IsMySQL(): + inferredTable, err := x.TableInfo(new(Webhook)) + if err != nil { + return err + } + sqlType := x.Dialect().SQLType(inferredTable.GetColumn("org_id")) + if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `webhook` CHANGE org_id owner_id %s", sqlType)); err != nil { + return err + } + case setting.Database.Type.IsMSSQL(): + if _, err := sess.Exec("sp_rename 'webhook.org_id', 'owner_id', 'COLUMN'"); err != nil { + return err + } + default: + if _, err := sess.Exec("ALTER TABLE `webhook` RENAME COLUMN org_id TO owner_id"); err != nil { + return err + } + } + + return sess.Commit() +} diff --git a/models/organization/org.go b/models/organization/org.go index f05027be7..269b3e832 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -239,6 +239,32 @@ func (org *Organization) CustomAvatarRelativePath() string { return org.Avatar } +// UnitPermission returns unit permission +func (org *Organization) UnitPermission(ctx context.Context, doer *user_model.User, unitType unit.Type) perm.AccessMode { + if doer != nil { + teams, err := GetUserOrgTeams(ctx, org.ID, doer.ID) + if err != nil { + log.Error("GetUserOrgTeams: %v", err) + return perm.AccessModeNone + } + + if err := teams.LoadUnits(ctx); err != nil { + log.Error("LoadUnits: %v", err) + return perm.AccessModeNone + } + + if len(teams) > 0 { + return teams.UnitMaxAccess(unitType) + } + } + + if org.Visibility.IsPublic() { + return perm.AccessModeRead + } + + return perm.AccessModeNone +} + // CreateOrganization creates record of a new organization. func CreateOrganization(org *Organization, owner *user_model.User) (err error) { if !owner.CanCreateOrganization() { diff --git a/models/project/project.go b/models/project/project.go index 46b5c07c4..f3ed72303 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -409,7 +409,7 @@ func DeleteProjectByID(ctx context.Context, id int64) error { func DeleteProjectByRepoID(ctx context.Context, repoID int64) error { switch { - case setting.Database.UseSQLite3: + case setting.Database.Type.IsSQLite3(): if _, err := db.GetEngine(ctx).Exec("DELETE FROM project_issue WHERE project_issue.id IN (SELECT project_issue.id FROM project_issue INNER JOIN project WHERE project.id = project_issue.project_id AND project.repo_id = ?)", repoID); err != nil { return err } @@ -419,7 +419,7 @@ func DeleteProjectByRepoID(ctx context.Context, repoID int64) error { if _, err := db.GetEngine(ctx).Table("project").Where("repo_id = ? ", repoID).Delete(&Project{}); err != nil { return err } - case setting.Database.UsePostgreSQL: + case setting.Database.Type.IsPostgreSQL(): if _, err := db.GetEngine(ctx).Exec("DELETE FROM project_issue USING project WHERE project.id = project_issue.project_id AND project.repo_id = ? ", repoID); err != nil { return err } diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index d64368daa..d9cd905a1 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -498,7 +498,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { subQueryCond := builder.NewCond() // Topic checking. Topics are present. - if setting.Database.UsePostgreSQL { // postgres stores the topics as json and not as text + if setting.Database.Type.IsPostgreSQL() { // postgres stores the topics as json and not as text subQueryCond = subQueryCond.Or(builder.And(builder.NotNull{"topics"}, builder.Neq{"(topics)::text": "[]"})) } else { subQueryCond = subQueryCond.Or(builder.And(builder.Neq{"topics": "null"}, builder.Neq{"topics": "[]"})) diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index 2bc41084b..cff1489a7 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -76,7 +76,7 @@ func MainTest(m *testing.M, testOpts *TestOptions) { setting.SSH.BuiltinServerUser = "builtinuser" setting.SSH.Port = 3000 setting.SSH.Domain = "try.gitea.io" - setting.Database.UseSQLite3 = true + setting.Database.Type = "sqlite3" setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master" repoRootPath, err := os.MkdirTemp(os.TempDir(), "repos") if err != nil { diff --git a/models/user/user.go b/models/user/user.go index 3a2e4f19b..7bc88e773 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -394,6 +394,11 @@ func (u *User) IsOrganization() bool { return u.Type == UserTypeOrganization } +// IsIndividual returns true if user is actually a individual user. +func (u *User) IsIndividual() bool { + return u.Type == UserTypeIndividual +} + // DisplayName returns full name if it's not empty, // returns username otherwise. func (u *User) DisplayName() string { diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go index 64119f149..e3f6b593d 100644 --- a/models/webhook/webhook.go +++ b/models/webhook/webhook.go @@ -122,7 +122,7 @@ func IsValidHookContentType(name string) bool { type Webhook struct { ID int64 `xorm:"pk autoincr"` RepoID int64 `xorm:"INDEX"` // An ID of 0 indicates either a default or system webhook - OrgID int64 `xorm:"INDEX"` + OwnerID int64 `xorm:"INDEX"` IsSystemWebhook bool URL string `xorm:"url TEXT"` HTTPMethod string `xorm:"http_method"` @@ -412,11 +412,11 @@ func GetWebhookByRepoID(repoID, id int64) (*Webhook, error) { }) } -// GetWebhookByOrgID returns webhook of organization by given ID. -func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) { +// GetWebhookByOwnerID returns webhook of a user or organization by given ID. +func GetWebhookByOwnerID(ownerID, id int64) (*Webhook, error) { return getWebhook(&Webhook{ - ID: id, - OrgID: orgID, + ID: id, + OwnerID: ownerID, }) } @@ -424,7 +424,7 @@ func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) { type ListWebhookOptions struct { db.ListOptions RepoID int64 - OrgID int64 + OwnerID int64 IsActive util.OptionalBool } @@ -433,8 +433,8 @@ func (opts *ListWebhookOptions) toCond() builder.Cond { if opts.RepoID != 0 { cond = cond.And(builder.Eq{"webhook.repo_id": opts.RepoID}) } - if opts.OrgID != 0 { - cond = cond.And(builder.Eq{"webhook.org_id": opts.OrgID}) + if opts.OwnerID != 0 { + cond = cond.And(builder.Eq{"webhook.owner_id": opts.OwnerID}) } if !opts.IsActive.IsNone() { cond = cond.And(builder.Eq{"webhook.is_active": opts.IsActive.IsTrue()}) @@ -503,10 +503,10 @@ func DeleteWebhookByRepoID(repoID, id int64) error { }) } -// DeleteWebhookByOrgID deletes webhook of organization by given ID. -func DeleteWebhookByOrgID(orgID, id int64) error { +// DeleteWebhookByOwnerID deletes webhook of a user or organization by given ID. +func DeleteWebhookByOwnerID(ownerID, id int64) error { return deleteWebhook(&Webhook{ - ID: id, - OrgID: orgID, + ID: id, + OwnerID: ownerID, }) } diff --git a/models/webhook/webhook_system.go b/models/webhook/webhook_system.go index 21dc0406a..2e89f9547 100644 --- a/models/webhook/webhook_system.go +++ b/models/webhook/webhook_system.go @@ -15,7 +15,7 @@ import ( func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) { webhooks := make([]*Webhook, 0, 5) return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, false). + Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, false). Find(&webhooks) } @@ -23,7 +23,7 @@ func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) { func GetSystemOrDefaultWebhook(ctx context.Context, id int64) (*Webhook, error) { webhook := &Webhook{ID: id} has, err := db.GetEngine(ctx). - Where("repo_id=? AND org_id=?", 0, 0). + Where("repo_id=? AND owner_id=?", 0, 0). Get(webhook) if err != nil { return nil, err @@ -38,11 +38,11 @@ func GetSystemWebhooks(ctx context.Context, isActive util.OptionalBool) ([]*Webh webhooks := make([]*Webhook, 0, 5) if isActive.IsNone() { return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, true). + Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, true). Find(&webhooks) } return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND org_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.IsTrue()). + Where("repo_id=? AND owner_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.IsTrue()). Find(&webhooks) } @@ -50,7 +50,7 @@ func GetSystemWebhooks(ctx context.Context, isActive util.OptionalBool) ([]*Webh func DeleteDefaultSystemWebhook(ctx context.Context, id int64) error { return db.WithTx(ctx, func(ctx context.Context) error { count, err := db.GetEngine(ctx). - Where("repo_id=? AND org_id=?", 0, 0). + Where("repo_id=? AND owner_id=?", 0, 0). Delete(&Webhook{ID: id}) if err != nil { return err diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go index c368fc620..74f7aeaa0 100644 --- a/models/webhook/webhook_test.go +++ b/models/webhook/webhook_test.go @@ -109,13 +109,13 @@ func TestGetWebhookByRepoID(t *testing.T) { assert.True(t, IsErrWebhookNotExist(err)) } -func TestGetWebhookByOrgID(t *testing.T) { +func TestGetWebhookByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hook, err := GetWebhookByOrgID(3, 3) + hook, err := GetWebhookByOwnerID(3, 3) assert.NoError(t, err) assert.Equal(t, int64(3), hook.ID) - _, err = GetWebhookByOrgID(unittest.NonexistentID, unittest.NonexistentID) + _, err = GetWebhookByOwnerID(unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) assert.True(t, IsErrWebhookNotExist(err)) } @@ -140,9 +140,9 @@ func TestGetWebhooksByRepoID(t *testing.T) { } } -func TestGetActiveWebhooksByOrgID(t *testing.T) { +func TestGetActiveWebhooksByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OrgID: 3, IsActive: util.OptionalBoolTrue}) + hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OwnerID: 3, IsActive: util.OptionalBoolTrue}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(3), hooks[0].ID) @@ -150,9 +150,9 @@ func TestGetActiveWebhooksByOrgID(t *testing.T) { } } -func TestGetWebhooksByOrgID(t *testing.T) { +func TestGetWebhooksByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OrgID: 3}) + hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OwnerID: 3}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(3), hooks[0].ID) @@ -181,13 +181,13 @@ func TestDeleteWebhookByRepoID(t *testing.T) { assert.True(t, IsErrWebhookNotExist(err)) } -func TestDeleteWebhookByOrgID(t *testing.T) { +func TestDeleteWebhookByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 3, OrgID: 3}) - assert.NoError(t, DeleteWebhookByOrgID(3, 3)) - unittest.AssertNotExistsBean(t, &Webhook{ID: 3, OrgID: 3}) + unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 3, OwnerID: 3}) + assert.NoError(t, DeleteWebhookByOwnerID(3, 3)) + unittest.AssertNotExistsBean(t, &Webhook{ID: 3, OwnerID: 3}) - err := DeleteWebhookByOrgID(unittest.NonexistentID, unittest.NonexistentID) + err := DeleteWebhookByOwnerID(unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) assert.True(t, IsErrWebhookNotExist(err)) } |