diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2017-02-04 23:53:46 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-04 23:53:46 +0800 |
commit | 8a421b1fd702d99e8438f6ef6f4ee339f1eaa130 (patch) | |
tree | c69e598ca9dca29dc64a4e1d8525165ec794106f /models | |
parent | 49fa03bf4286bd2cbf90b271fb65d4f70e5de57f (diff) | |
download | gitea-8a421b1fd702d99e8438f6ef6f4ee339f1eaa130.tar.gz gitea-8a421b1fd702d99e8438f6ef6f4ee339f1eaa130.zip |
Add units concept for modulable functions of a repository (#742)
* Add units concept for modulable functions of a repository
* remove unused comment codes & fix lints and tests
* remove unused comment codes
* use struct config instead of map
* fix lint
* rm wrong files
* fix tests
Diffstat (limited to 'models')
-rw-r--r-- | models/migrations/migrations.go | 6 | ||||
-rw-r--r-- | models/migrations/v16.go | 117 | ||||
-rw-r--r-- | models/models.go | 1 | ||||
-rw-r--r-- | models/repo.go | 150 | ||||
-rw-r--r-- | models/repo_test.go | 24 | ||||
-rw-r--r-- | models/repo_unit.go | 137 | ||||
-rw-r--r-- | models/unit.go | 137 |
7 files changed, 533 insertions, 39 deletions
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 5844e3f923..9832cdca92 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -76,10 +76,12 @@ var migrations = []Migration{ // v13 -> v14:v0.9.87 NewMigration("set comment updated with created", setCommentUpdatedWithCreated), - // v14 + // v14 -> v15 NewMigration("create user column diff view style", createUserColumnDiffViewStyle), - // v15 + // v15 -> v16 NewMigration("create user column allow create organization", createAllowCreateOrganizationColumn), + // V16 -> v17 + NewMigration("create repo unit table and add units for all repos", addUnitsToTables), } // Migrate database to current version diff --git a/models/migrations/v16.go b/models/migrations/v16.go new file mode 100644 index 0000000000..fca8311cbd --- /dev/null +++ b/models/migrations/v16.go @@ -0,0 +1,117 @@ +package migrations + +import ( + "fmt" + "time" + + "code.gitea.io/gitea/modules/markdown" + + "github.com/go-xorm/xorm" +) + +// RepoUnit describes all units of a repository +type RepoUnit struct { + ID int64 + RepoID int64 `xorm:"INDEX(s)"` + Type int `xorm:"INDEX(s)"` + Index int + Config map[string]string `xorm:"JSON"` + CreatedUnix int64 `xorm:"INDEX CREATED"` + Created time.Time `xorm:"-"` +} + +// Enumerate all the unit types +const ( + UnitTypeCode = iota + 1 // 1 code + UnitTypeIssues // 2 issues + UnitTypePRs // 3 PRs + UnitTypeCommits // 4 Commits + UnitTypeReleases // 5 Releases + UnitTypeWiki // 6 Wiki + UnitTypeSettings // 7 Settings + UnitTypeExternalWiki // 8 ExternalWiki + UnitTypeExternalTracker // 9 ExternalTracker +) + +// Repo describes a repository +type Repo struct { + ID int64 + EnableWiki, EnableExternalWiki, EnableIssues, EnableExternalTracker, EnablePulls bool + ExternalWikiURL, ExternalTrackerURL, ExternalTrackerFormat, ExternalTrackerStyle string +} + +func addUnitsToTables(x *xorm.Engine) error { + var repos []Repo + err := x.Table("repository").Find(&repos) + if err != nil { + return fmt.Errorf("Query repositories: %v", err) + } + + sess := x.NewSession() + defer sess.Close() + + if err := sess.Begin(); err != nil { + return err + } + + var repoUnit RepoUnit + if err := sess.CreateTable(&repoUnit); err != nil { + return fmt.Errorf("CreateTable RepoUnit: %v", err) + } + + if err := sess.CreateUniques(&repoUnit); err != nil { + return fmt.Errorf("CreateUniques RepoUnit: %v", err) + } + + if err := sess.CreateIndexes(&repoUnit); err != nil { + return fmt.Errorf("CreateIndexes RepoUnit: %v", err) + } + + for _, repo := range repos { + for i := 1; i <= 9; i++ { + if (i == UnitTypeWiki || i == UnitTypeExternalWiki) && !repo.EnableWiki { + continue + } + if i == UnitTypeExternalWiki && !repo.EnableExternalWiki { + continue + } + if i == UnitTypePRs && !repo.EnablePulls { + continue + } + if (i == UnitTypeIssues || i == UnitTypeExternalTracker) && !repo.EnableIssues { + continue + } + if i == UnitTypeExternalTracker && !repo.EnableExternalTracker { + continue + } + + var config = make(map[string]string) + switch i { + case UnitTypeExternalTracker: + config["ExternalTrackerURL"] = repo.ExternalTrackerURL + config["ExternalTrackerFormat"] = repo.ExternalTrackerFormat + if len(repo.ExternalTrackerStyle) == 0 { + repo.ExternalTrackerStyle = markdown.IssueNameStyleNumeric + } + config["ExternalTrackerStyle"] = repo.ExternalTrackerStyle + case UnitTypeExternalWiki: + config["ExternalWikiURL"] = repo.ExternalWikiURL + } + + if _, err = sess.Insert(&RepoUnit{ + RepoID: repo.ID, + Type: i, + Index: i, + Config: config, + }); err != nil { + return fmt.Errorf("Insert repo unit: %v", err) + } + } + } + + if err := sess.Commit(); err != nil { + return err + } + + return nil +} diff --git a/models/models.go b/models/models.go index 7353c11598..624badda17 100644 --- a/models/models.go +++ b/models/models.go @@ -106,6 +106,7 @@ func init() { new(IssueUser), new(LFSMetaObject), new(TwoFactor), + new(RepoUnit), ) gonicNames := []string{"SSL", "UID"} diff --git a/models/repo.go b/models/repo.go index 3a503d8953..a16b7a86f0 100644 --- a/models/repo.go +++ b/models/repo.go @@ -200,17 +200,8 @@ type Repository struct { IsMirror bool `xorm:"INDEX"` *Mirror `xorm:"-"` - // Advanced settings - EnableWiki bool `xorm:"NOT NULL DEFAULT true"` - EnableExternalWiki bool - ExternalWikiURL string - EnableIssues bool `xorm:"NOT NULL DEFAULT true"` - EnableExternalTracker bool - ExternalTrackerURL string - ExternalTrackerFormat string - ExternalTrackerStyle string - ExternalMetas map[string]string `xorm:"-"` - EnablePulls bool `xorm:"NOT NULL DEFAULT true"` + ExternalMetas map[string]string `xorm:"-"` + Units []*RepoUnit `xorm:"-"` IsFork bool `xorm:"INDEX NOT NULL DEFAULT false"` ForkID int64 `xorm:"INDEX"` @@ -247,10 +238,6 @@ func (repo *Repository) AfterSet(colName string, _ xorm.Cell) { repo.NumOpenPulls = repo.NumPulls - repo.NumClosedPulls case "num_closed_milestones": repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones - case "external_tracker_style": - if len(repo.ExternalTrackerStyle) == 0 { - repo.ExternalTrackerStyle = markdown.IssueNameStyleNumeric - } case "created_unix": repo.Created = time.Unix(repo.CreatedUnix, 0).Local() case "updated_unix": @@ -307,6 +294,72 @@ func (repo *Repository) APIFormat(mode AccessMode) *api.Repository { } } +func (repo *Repository) getUnits(e Engine) (err error) { + if repo.Units != nil { + return nil + } + + repo.Units, err = getUnitsByRepoID(e, repo.ID) + return err +} + +func getUnitsByRepoID(e Engine, repoID int64) (units []*RepoUnit, err error) { + return units, e.Where("repo_id = ?", repoID).Find(&units) +} + +// EnableUnit if this repository enabled some unit +func (repo *Repository) EnableUnit(tp UnitType) bool { + repo.getUnits(x) + for _, unit := range repo.Units { + if unit.Type == tp { + return true + } + } + return false +} + +var ( + // ErrUnitNotExist organization does not exist + ErrUnitNotExist = errors.New("Unit does not exist") +) + +// MustGetUnit always returns a RepoUnit object +func (repo *Repository) MustGetUnit(tp UnitType) *RepoUnit { + ru, err := repo.GetUnit(tp) + if err == nil { + return ru + } + + if tp == UnitTypeExternalWiki { + return &RepoUnit{ + Type: tp, + Config: new(ExternalWikiConfig), + } + } else if tp == UnitTypeExternalTracker { + return &RepoUnit{ + Type: tp, + Config: new(ExternalTrackerConfig), + } + } + return &RepoUnit{ + Type: tp, + Config: new(UnitConfig), + } +} + +// GetUnit returns a RepoUnit object +func (repo *Repository) GetUnit(tp UnitType) (*RepoUnit, error) { + if err := repo.getUnits(x); err != nil { + return nil, err + } + for _, unit := range repo.Units { + if unit.Type == tp { + return unit, nil + } + } + return nil, ErrUnitNotExist +} + func (repo *Repository) getOwner(e Engine) (err error) { if repo.Owner != nil { return nil @@ -334,15 +387,18 @@ func (repo *Repository) mustOwner(e Engine) *User { // ComposeMetas composes a map of metas for rendering external issue tracker URL. func (repo *Repository) ComposeMetas() map[string]string { - if !repo.EnableExternalTracker { + unit, err := repo.GetUnit(UnitTypeExternalTracker) + if err != nil { return nil - } else if repo.ExternalMetas == nil { + } + + if repo.ExternalMetas == nil { repo.ExternalMetas = map[string]string{ - "format": repo.ExternalTrackerFormat, + "format": unit.ExternalTrackerConfig().ExternalTrackerFormat, "user": repo.MustOwner().Name, "repo": repo.Name, } - switch repo.ExternalTrackerStyle { + switch unit.ExternalTrackerConfig().ExternalTrackerStyle { case markdown.IssueNameStyleAlphanumeric: repo.ExternalMetas["style"] = markdown.IssueNameStyleAlphanumeric default: @@ -359,6 +415,8 @@ func (repo *Repository) DeleteWiki() { for _, wikiPath := range wikiPaths { RemoveAllWithNotice("Delete repository wiki", wikiPath) } + + x.Where("repo_id = ?", repo.ID).And("type = ?", UnitTypeWiki).Delete(new(RepoUnit)) } func (repo *Repository) getAssignees(e Engine) (_ []*User, err error) { @@ -482,7 +540,7 @@ func (repo *Repository) CanEnablePulls() bool { // AllowsPulls returns true if repository meets the requirements of accepting pulls and has them enabled. func (repo *Repository) AllowsPulls() bool { - return repo.CanEnablePulls() && repo.EnablePulls + return repo.CanEnablePulls() && repo.EnableUnit(UnitTypePullRequests) } // CanEnableEditor returns true if repository meets the requirements of web editor. @@ -997,6 +1055,20 @@ func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) { return err } + // insert units for repo + var units = make([]RepoUnit, 0, len(defaultRepoUnits)) + for i, tp := range defaultRepoUnits { + units = append(units, RepoUnit{ + RepoID: repo.ID, + Type: tp, + Index: i, + }) + } + + if _, err = e.Insert(&units); err != nil { + return err + } + u.NumRepos++ // Remember visibility preference. u.LastRepoVisibility = repo.IsPrivate @@ -1035,15 +1107,12 @@ func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error } repo := &Repository{ - OwnerID: u.ID, - Owner: u, - Name: opts.Name, - LowerName: strings.ToLower(opts.Name), - Description: opts.Description, - IsPrivate: opts.IsPrivate, - EnableWiki: true, - EnableIssues: true, - EnablePulls: true, + OwnerID: u.ID, + Owner: u, + Name: opts.Name, + LowerName: strings.ToLower(opts.Name), + Description: opts.Description, + IsPrivate: opts.IsPrivate, } sess := x.NewSession() @@ -1380,6 +1449,25 @@ func UpdateRepository(repo *Repository, visibilityChanged bool) (err error) { return sess.Commit() } +// UpdateRepositoryUnits updates a repository's units +func UpdateRepositoryUnits(repo *Repository, units []RepoUnit) (err error) { + sess := x.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return err + } + + if _, err = sess.Where("repo_id = ?", repo.ID).Delete(new(RepoUnit)); err != nil { + return err + } + + if _, err = sess.Insert(units); err != nil { + return err + } + + return sess.Commit() +} + // DeleteRepository deletes a repository for a user or organization. func DeleteRepository(uid, repoID int64) error { repo := &Repository{ID: repoID, OwnerID: uid} @@ -1467,6 +1555,10 @@ func DeleteRepository(uid, repoID int64) error { return err } + if _, err = sess.Where("repo_id = ?", repoID).Delete(new(RepoUnit)); err != nil { + return err + } + if repo.IsFork { if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repo.ForkID); err != nil { return fmt.Errorf("decrease fork count: %v", err) diff --git a/models/repo_test.go b/models/repo_test.go index 70ad1c1f0e..42bde62b46 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -14,34 +14,42 @@ func TestRepo(t *testing.T) { repo.Name = "testrepo" repo.Owner = new(User) repo.Owner.Name = "testuser" - repo.ExternalTrackerFormat = "https://someurl.com/{user}/{repo}/{issue}" + externalTracker := RepoUnit{ + Type: UnitTypeExternalTracker, + Config: &ExternalTrackerConfig{ + ExternalTrackerFormat: "https://someurl.com/{user}/{repo}/{issue}", + }, + } + repo.Units = []*RepoUnit{ + &externalTracker, + } Convey("When no external tracker is configured", func() { Convey("It should be nil", func() { - repo.EnableExternalTracker = false + repo.Units = nil So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil)) }) Convey("It should be nil even if other settings are present", func() { - repo.EnableExternalTracker = false - repo.ExternalTrackerFormat = "http://someurl.com/{user}/{repo}/{issue}" - repo.ExternalTrackerStyle = markdown.IssueNameStyleNumeric + repo.Units = nil So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil)) }) }) Convey("When an external issue tracker is configured", func() { - repo.EnableExternalTracker = true + repo.Units = []*RepoUnit{ + &externalTracker, + } Convey("It should default to numeric issue style", func() { metas := repo.ComposeMetas() So(metas["style"], ShouldEqual, markdown.IssueNameStyleNumeric) }) Convey("It should pass through numeric issue style setting", func() { - repo.ExternalTrackerStyle = markdown.IssueNameStyleNumeric + externalTracker.ExternalTrackerConfig().ExternalTrackerStyle = markdown.IssueNameStyleNumeric metas := repo.ComposeMetas() So(metas["style"], ShouldEqual, markdown.IssueNameStyleNumeric) }) Convey("It should pass through alphanumeric issue style setting", func() { - repo.ExternalTrackerStyle = markdown.IssueNameStyleAlphanumeric + externalTracker.ExternalTrackerConfig().ExternalTrackerStyle = markdown.IssueNameStyleAlphanumeric metas := repo.ComposeMetas() So(metas["style"], ShouldEqual, markdown.IssueNameStyleAlphanumeric) }) diff --git a/models/repo_unit.go b/models/repo_unit.go new file mode 100644 index 0000000000..ee61ef4c9d --- /dev/null +++ b/models/repo_unit.go @@ -0,0 +1,137 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +import ( + "encoding/json" + "time" + + "github.com/Unknwon/com" + "github.com/go-xorm/core" + "github.com/go-xorm/xorm" +) + +// RepoUnit describes all units of a repository +type RepoUnit struct { + ID int64 + RepoID int64 `xorm:"INDEX(s)"` + Type UnitType `xorm:"INDEX(s)"` + Index int + Config core.Conversion `xorm:"TEXT"` + CreatedUnix int64 `xorm:"INDEX CREATED"` + Created time.Time `xorm:"-"` +} + +// UnitConfig describes common unit config +type UnitConfig struct { +} + +// FromDB fills up a UnitConfig from serialized format. +func (cfg *UnitConfig) FromDB(bs []byte) error { + return json.Unmarshal(bs, &cfg) +} + +// ToDB exports a UnitConfig to a serialized format. +func (cfg *UnitConfig) ToDB() ([]byte, error) { + return json.Marshal(cfg) +} + +// ExternalWikiConfig describes external wiki config +type ExternalWikiConfig struct { + ExternalWikiURL string +} + +// FromDB fills up a ExternalWikiConfig from serialized format. +func (cfg *ExternalWikiConfig) FromDB(bs []byte) error { + return json.Unmarshal(bs, &cfg) +} + +// ToDB exports a ExternalWikiConfig to a serialized format. +func (cfg *ExternalWikiConfig) ToDB() ([]byte, error) { + return json.Marshal(cfg) +} + +// ExternalTrackerConfig describes external tracker config +type ExternalTrackerConfig struct { + ExternalTrackerURL string + ExternalTrackerFormat string + ExternalTrackerStyle string +} + +// FromDB fills up a ExternalTrackerConfig from serialized format. +func (cfg *ExternalTrackerConfig) FromDB(bs []byte) error { + return json.Unmarshal(bs, &cfg) +} + +// ToDB exports a ExternalTrackerConfig to a serialized format. +func (cfg *ExternalTrackerConfig) ToDB() ([]byte, error) { + return json.Marshal(cfg) +} + +// BeforeSet is invoked from XORM before setting the value of a field of this object. +func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) { + switch colName { + case "type": + switch UnitType(Cell2Int64(val)) { + case UnitTypeCode, UnitTypeIssues, UnitTypePullRequests, UnitTypeCommits, UnitTypeReleases, + UnitTypeWiki, UnitTypeSettings: + r.Config = new(UnitConfig) + case UnitTypeExternalWiki: + r.Config = new(ExternalWikiConfig) + case UnitTypeExternalTracker: + r.Config = new(ExternalTrackerConfig) + default: + panic("unrecognized repo unit type: " + com.ToStr(*val)) + } + } +} + +// AfterSet is invoked from XORM after setting the value of a field of this object. +func (r *RepoUnit) AfterSet(colName string, _ xorm.Cell) { + switch colName { + case "created_unix": + r.Created = time.Unix(r.CreatedUnix, 0).Local() + } +} + +// Unit returns Unit +func (r *RepoUnit) Unit() Unit { + return Units[r.Type] +} + +// CodeConfig returns config for UnitTypeCode +func (r *RepoUnit) CodeConfig() *UnitConfig { + return r.Config.(*UnitConfig) +} + +// IssuesConfig returns config for UnitTypeIssues +func (r *RepoUnit) IssuesConfig() *UnitConfig { + return r.Config.(*UnitConfig) +} + +// PullRequestsConfig returns config for UnitTypePullRequests +func (r *RepoUnit) PullRequestsConfig() *UnitConfig { + return r.Config.(*UnitConfig) +} + +// CommitsConfig returns config for UnitTypeCommits +func (r *RepoUnit) CommitsConfig() *UnitConfig { + return r.Config.(*UnitConfig) +} + +// ReleasesConfig returns config for UnitTypeReleases +func (r *RepoUnit) ReleasesConfig() *UnitConfig { + return r.Config.(*UnitConfig) +} + +// ExternalWikiConfig returns config for UnitTypeExternalWiki +func (r *RepoUnit) ExternalWikiConfig() *ExternalWikiConfig { + return r.Config.(*ExternalWikiConfig) +} + +// ExternalTrackerConfig returns config for UnitTypeExternalTracker +func (r *RepoUnit) ExternalTrackerConfig() *ExternalTrackerConfig { + return r.Config.(*ExternalTrackerConfig) +} diff --git a/models/unit.go b/models/unit.go new file mode 100644 index 0000000000..54bb928ba7 --- /dev/null +++ b/models/unit.go @@ -0,0 +1,137 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +// UnitType is Unit's Type +type UnitType int + +// Enumerate all the unit types +const ( + UnitTypeCode UnitType = iota + 1 // 1 code + UnitTypeIssues // 2 issues + UnitTypePullRequests // 3 PRs + UnitTypeCommits // 4 Commits + UnitTypeReleases // 5 Releases + UnitTypeWiki // 6 Wiki + UnitTypeSettings // 7 Settings + UnitTypeExternalWiki // 8 ExternalWiki + UnitTypeExternalTracker // 9 ExternalTracker +) + +// Unit is a tab page of one repository +type Unit struct { + Type UnitType + NameKey string + URI string + DescKey string + Idx int +} + +// Enumerate all the units +var ( + UnitCode = Unit{ + UnitTypeCode, + "repo.code", + "/", + "repo.code_desc", + 0, + } + + UnitIssues = Unit{ + UnitTypeIssues, + "repo.issues", + "/issues", + "repo.issues_desc", + 1, + } + + UnitExternalTracker = Unit{ + UnitTypeExternalTracker, + "repo.issues", + "/issues", + "repo.issues_desc", + 1, + } + + UnitPullRequests = Unit{ + UnitTypePullRequests, + "repo.pulls", + "/pulls", + "repo.pulls_desc", + 2, + } + + UnitCommits = Unit{ + UnitTypeCommits, + "repo.commits", + "/commits/master", + "repo.commits_desc", + 3, + } + + UnitReleases = Unit{ + UnitTypeReleases, + "repo.releases", + "/releases", + "repo.releases_desc", + 4, + } + + UnitWiki = Unit{ + UnitTypeWiki, + "repo.wiki", + "/wiki", + "repo.wiki_desc", + 5, + } + + UnitExternalWiki = Unit{ + UnitTypeExternalWiki, + "repo.wiki", + "/wiki", + "repo.wiki_desc", + 5, + } + + UnitSettings = Unit{ + UnitTypeSettings, + "repo.settings", + "/settings", + "repo.settings_desc", + 6, + } + + // defaultRepoUnits contains all the default unit types + defaultRepoUnits = []UnitType{ + UnitTypeCode, + UnitTypeIssues, + UnitTypePullRequests, + UnitTypeCommits, + UnitTypeReleases, + UnitTypeWiki, + UnitTypeSettings, + } + + // MustRepoUnits contains the units could be disabled currently + MustRepoUnits = []UnitType{ + UnitTypeCode, + UnitTypeCommits, + UnitTypeReleases, + UnitTypeSettings, + } + + // Units contains all the units + Units = map[UnitType]Unit{ + UnitTypeCode: UnitCode, + UnitTypeIssues: UnitIssues, + UnitTypeExternalTracker: UnitExternalTracker, + UnitTypePullRequests: UnitPullRequests, + UnitTypeCommits: UnitCommits, + UnitTypeReleases: UnitReleases, + UnitTypeWiki: UnitWiki, + UnitTypeExternalWiki: UnitExternalWiki, + UnitTypeSettings: UnitSettings, + } +) |