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
This commit is contained in:
		
							parent
							
								
									49fa03bf42
								
							
						
					
					
						commit
						8a421b1fd7
					
				
					 16 changed files with 668 additions and 89 deletions
				
			
		|  | @ -441,7 +441,7 @@ func runWeb(ctx *cli.Context) error { | ||||||
| 
 | 
 | ||||||
| 		}, func(ctx *context.Context) { | 		}, func(ctx *context.Context) { | ||||||
| 			ctx.Data["PageIsSettings"] = true | 			ctx.Data["PageIsSettings"] = true | ||||||
| 		}) | 		}, context.UnitTypes()) | ||||||
| 	}, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.RepoRef()) | 	}, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.RepoRef()) | ||||||
| 
 | 
 | ||||||
| 	m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action) | 	m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action) | ||||||
|  | @ -535,7 +535,7 @@ func runWeb(ctx *cli.Context) error { | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		}) | 		}) | ||||||
| 	}, reqSignIn, context.RepoAssignment(), repo.MustBeNotBare) | 	}, reqSignIn, context.RepoAssignment(), repo.MustBeNotBare, context.UnitTypes()) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/:username/:reponame", func() { | 	m.Group("/:username/:reponame", func() { | ||||||
| 		m.Group("", func() { | 		m.Group("", func() { | ||||||
|  | @ -581,7 +581,7 @@ func runWeb(ctx *cli.Context) error { | ||||||
| 		m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.RawDiff) | 		m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.RawDiff) | ||||||
| 
 | 
 | ||||||
| 		m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.CompareDiff) | 		m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.CompareDiff) | ||||||
| 	}, ignSignIn, context.RepoAssignment(), repo.MustBeNotBare) | 	}, ignSignIn, context.RepoAssignment(), repo.MustBeNotBare, context.UnitTypes()) | ||||||
| 	m.Group("/:username/:reponame", func() { | 	m.Group("/:username/:reponame", func() { | ||||||
| 		m.Get("/stars", repo.Stars) | 		m.Get("/stars", repo.Stars) | ||||||
| 		m.Get("/watchers", repo.Watchers) | 		m.Get("/watchers", repo.Watchers) | ||||||
|  | @ -591,7 +591,7 @@ func runWeb(ctx *cli.Context) error { | ||||||
| 		m.Group("/:reponame", func() { | 		m.Group("/:reponame", func() { | ||||||
| 			m.Get("", repo.SetEditorconfigIfExists, repo.Home) | 			m.Get("", repo.SetEditorconfigIfExists, repo.Home) | ||||||
| 			m.Get("\\.git$", repo.SetEditorconfigIfExists, repo.Home) | 			m.Get("\\.git$", repo.SetEditorconfigIfExists, repo.Home) | ||||||
| 		}, ignSignIn, context.RepoAssignment(true), context.RepoRef()) | 		}, ignSignIn, context.RepoAssignment(true), context.RepoRef(), context.UnitTypes()) | ||||||
| 
 | 
 | ||||||
| 		m.Group("/:reponame", func() { | 		m.Group("/:reponame", func() { | ||||||
| 			m.Group("/info/lfs", func() { | 			m.Group("/info/lfs", func() { | ||||||
|  |  | ||||||
|  | @ -76,10 +76,12 @@ var migrations = []Migration{ | ||||||
| 
 | 
 | ||||||
| 	// v13 -> v14:v0.9.87
 | 	// v13 -> v14:v0.9.87
 | ||||||
| 	NewMigration("set comment updated with created", setCommentUpdatedWithCreated), | 	NewMigration("set comment updated with created", setCommentUpdatedWithCreated), | ||||||
| 	// v14
 | 	// v14 -> v15
 | ||||||
| 	NewMigration("create user column diff view style", createUserColumnDiffViewStyle), | 	NewMigration("create user column diff view style", createUserColumnDiffViewStyle), | ||||||
| 	// v15
 | 	// v15 -> v16
 | ||||||
| 	NewMigration("create user column allow create organization", createAllowCreateOrganizationColumn), | 	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
 | // Migrate database to current version
 | ||||||
|  |  | ||||||
							
								
								
									
										117
									
								
								models/migrations/v16.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								models/migrations/v16.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||||
|  | } | ||||||
|  | @ -106,6 +106,7 @@ func init() { | ||||||
| 		new(IssueUser), | 		new(IssueUser), | ||||||
| 		new(LFSMetaObject), | 		new(LFSMetaObject), | ||||||
| 		new(TwoFactor), | 		new(TwoFactor), | ||||||
|  | 		new(RepoUnit), | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	gonicNames := []string{"SSL", "UID"} | 	gonicNames := []string{"SSL", "UID"} | ||||||
|  |  | ||||||
							
								
								
									
										150
									
								
								models/repo.go
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								models/repo.go
									
									
									
									
									
								
							|  | @ -200,17 +200,8 @@ type Repository struct { | ||||||
| 	IsMirror bool `xorm:"INDEX"` | 	IsMirror bool `xorm:"INDEX"` | ||||||
| 	*Mirror  `xorm:"-"` | 	*Mirror  `xorm:"-"` | ||||||
| 
 | 
 | ||||||
| 	// Advanced settings
 | 	ExternalMetas map[string]string `xorm:"-"` | ||||||
| 	EnableWiki            bool `xorm:"NOT NULL DEFAULT true"` | 	Units         []*RepoUnit       `xorm:"-"` | ||||||
| 	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"` |  | ||||||
| 
 | 
 | ||||||
| 	IsFork   bool        `xorm:"INDEX NOT NULL DEFAULT false"` | 	IsFork   bool        `xorm:"INDEX NOT NULL DEFAULT false"` | ||||||
| 	ForkID   int64       `xorm:"INDEX"` | 	ForkID   int64       `xorm:"INDEX"` | ||||||
|  | @ -247,10 +238,6 @@ func (repo *Repository) AfterSet(colName string, _ xorm.Cell) { | ||||||
| 		repo.NumOpenPulls = repo.NumPulls - repo.NumClosedPulls | 		repo.NumOpenPulls = repo.NumPulls - repo.NumClosedPulls | ||||||
| 	case "num_closed_milestones": | 	case "num_closed_milestones": | ||||||
| 		repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones | 		repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones | ||||||
| 	case "external_tracker_style": |  | ||||||
| 		if len(repo.ExternalTrackerStyle) == 0 { |  | ||||||
| 			repo.ExternalTrackerStyle = markdown.IssueNameStyleNumeric |  | ||||||
| 		} |  | ||||||
| 	case "created_unix": | 	case "created_unix": | ||||||
| 		repo.Created = time.Unix(repo.CreatedUnix, 0).Local() | 		repo.Created = time.Unix(repo.CreatedUnix, 0).Local() | ||||||
| 	case "updated_unix": | 	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) { | func (repo *Repository) getOwner(e Engine) (err error) { | ||||||
| 	if repo.Owner != nil { | 	if repo.Owner != nil { | ||||||
| 		return 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.
 | // ComposeMetas composes a map of metas for rendering external issue tracker URL.
 | ||||||
| func (repo *Repository) ComposeMetas() map[string]string { | func (repo *Repository) ComposeMetas() map[string]string { | ||||||
| 	if !repo.EnableExternalTracker { | 	unit, err := repo.GetUnit(UnitTypeExternalTracker) | ||||||
|  | 	if err != nil { | ||||||
| 		return nil | 		return nil | ||||||
| 	} else if repo.ExternalMetas == nil { | 	} | ||||||
|  | 
 | ||||||
|  | 	if repo.ExternalMetas == nil { | ||||||
| 		repo.ExternalMetas = map[string]string{ | 		repo.ExternalMetas = map[string]string{ | ||||||
| 			"format": repo.ExternalTrackerFormat, | 			"format": unit.ExternalTrackerConfig().ExternalTrackerFormat, | ||||||
| 			"user":   repo.MustOwner().Name, | 			"user":   repo.MustOwner().Name, | ||||||
| 			"repo":   repo.Name, | 			"repo":   repo.Name, | ||||||
| 		} | 		} | ||||||
| 		switch repo.ExternalTrackerStyle { | 		switch unit.ExternalTrackerConfig().ExternalTrackerStyle { | ||||||
| 		case markdown.IssueNameStyleAlphanumeric: | 		case markdown.IssueNameStyleAlphanumeric: | ||||||
| 			repo.ExternalMetas["style"] = markdown.IssueNameStyleAlphanumeric | 			repo.ExternalMetas["style"] = markdown.IssueNameStyleAlphanumeric | ||||||
| 		default: | 		default: | ||||||
|  | @ -359,6 +415,8 @@ func (repo *Repository) DeleteWiki() { | ||||||
| 	for _, wikiPath := range wikiPaths { | 	for _, wikiPath := range wikiPaths { | ||||||
| 		RemoveAllWithNotice("Delete repository wiki", wikiPath) | 		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) { | 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.
 | // AllowsPulls returns true if repository meets the requirements of accepting pulls and has them enabled.
 | ||||||
| func (repo *Repository) AllowsPulls() bool { | 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.
 | // 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 | 		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++ | 	u.NumRepos++ | ||||||
| 	// Remember visibility preference.
 | 	// Remember visibility preference.
 | ||||||
| 	u.LastRepoVisibility = repo.IsPrivate | 	u.LastRepoVisibility = repo.IsPrivate | ||||||
|  | @ -1035,15 +1107,12 @@ func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	repo := &Repository{ | 	repo := &Repository{ | ||||||
| 		OwnerID:      u.ID, | 		OwnerID:     u.ID, | ||||||
| 		Owner:        u, | 		Owner:       u, | ||||||
| 		Name:         opts.Name, | 		Name:        opts.Name, | ||||||
| 		LowerName:    strings.ToLower(opts.Name), | 		LowerName:   strings.ToLower(opts.Name), | ||||||
| 		Description:  opts.Description, | 		Description: opts.Description, | ||||||
| 		IsPrivate:    opts.IsPrivate, | 		IsPrivate:   opts.IsPrivate, | ||||||
| 		EnableWiki:   true, |  | ||||||
| 		EnableIssues: true, |  | ||||||
| 		EnablePulls:  true, |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sess := x.NewSession() | 	sess := x.NewSession() | ||||||
|  | @ -1380,6 +1449,25 @@ func UpdateRepository(repo *Repository, visibilityChanged bool) (err error) { | ||||||
| 	return sess.Commit() | 	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.
 | // DeleteRepository deletes a repository for a user or organization.
 | ||||||
| func DeleteRepository(uid, repoID int64) error { | func DeleteRepository(uid, repoID int64) error { | ||||||
| 	repo := &Repository{ID: repoID, OwnerID: uid} | 	repo := &Repository{ID: repoID, OwnerID: uid} | ||||||
|  | @ -1467,6 +1555,10 @@ func DeleteRepository(uid, repoID int64) error { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if _, err = sess.Where("repo_id = ?", repoID).Delete(new(RepoUnit)); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if repo.IsFork { | 	if repo.IsFork { | ||||||
| 		if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repo.ForkID); err != nil { | 		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) | 			return fmt.Errorf("decrease fork count: %v", err) | ||||||
|  |  | ||||||
|  | @ -14,34 +14,42 @@ func TestRepo(t *testing.T) { | ||||||
| 		repo.Name = "testrepo" | 		repo.Name = "testrepo" | ||||||
| 		repo.Owner = new(User) | 		repo.Owner = new(User) | ||||||
| 		repo.Owner.Name = "testuser" | 		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("When no external tracker is configured", func() { | ||||||
| 			Convey("It should be nil", func() { | 			Convey("It should be nil", func() { | ||||||
| 				repo.EnableExternalTracker = false | 				repo.Units = nil | ||||||
| 				So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil)) | 				So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil)) | ||||||
| 			}) | 			}) | ||||||
| 			Convey("It should be nil even if other settings are present", func() { | 			Convey("It should be nil even if other settings are present", func() { | ||||||
| 				repo.EnableExternalTracker = false | 				repo.Units = nil | ||||||
| 				repo.ExternalTrackerFormat = "http://someurl.com/{user}/{repo}/{issue}" |  | ||||||
| 				repo.ExternalTrackerStyle = markdown.IssueNameStyleNumeric |  | ||||||
| 				So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil)) | 				So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil)) | ||||||
| 			}) | 			}) | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		Convey("When an external issue tracker is configured", func() { | 		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() { | 			Convey("It should default to numeric issue style", func() { | ||||||
| 				metas := repo.ComposeMetas() | 				metas := repo.ComposeMetas() | ||||||
| 				So(metas["style"], ShouldEqual, markdown.IssueNameStyleNumeric) | 				So(metas["style"], ShouldEqual, markdown.IssueNameStyleNumeric) | ||||||
| 			}) | 			}) | ||||||
| 			Convey("It should pass through numeric issue style setting", func() { | 			Convey("It should pass through numeric issue style setting", func() { | ||||||
| 				repo.ExternalTrackerStyle = markdown.IssueNameStyleNumeric | 				externalTracker.ExternalTrackerConfig().ExternalTrackerStyle = markdown.IssueNameStyleNumeric | ||||||
| 				metas := repo.ComposeMetas() | 				metas := repo.ComposeMetas() | ||||||
| 				So(metas["style"], ShouldEqual, markdown.IssueNameStyleNumeric) | 				So(metas["style"], ShouldEqual, markdown.IssueNameStyleNumeric) | ||||||
| 			}) | 			}) | ||||||
| 			Convey("It should pass through alphanumeric issue style setting", func() { | 			Convey("It should pass through alphanumeric issue style setting", func() { | ||||||
| 				repo.ExternalTrackerStyle = markdown.IssueNameStyleAlphanumeric | 				externalTracker.ExternalTrackerConfig().ExternalTrackerStyle = markdown.IssueNameStyleAlphanumeric | ||||||
| 				metas := repo.ComposeMetas() | 				metas := repo.ComposeMetas() | ||||||
| 				So(metas["style"], ShouldEqual, markdown.IssueNameStyleAlphanumeric) | 				So(metas["style"], ShouldEqual, markdown.IssueNameStyleAlphanumeric) | ||||||
| 			}) | 			}) | ||||||
|  |  | ||||||
							
								
								
									
										137
									
								
								models/repo_unit.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								models/repo_unit.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||||
|  | } | ||||||
							
								
								
									
										137
									
								
								models/unit.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								models/unit.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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, | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|  | @ -477,3 +477,18 @@ func GitHookService() macaron.Handler { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // UnitTypes returns a macaron middleware to set unit types to context variables.
 | ||||||
|  | func UnitTypes() macaron.Handler { | ||||||
|  | 	return func(ctx *Context) { | ||||||
|  | 		ctx.Data["UnitTypeCode"] = models.UnitTypeCode | ||||||
|  | 		ctx.Data["UnitTypeIssues"] = models.UnitTypeIssues | ||||||
|  | 		ctx.Data["UnitTypePullRequests"] = models.UnitTypePullRequests | ||||||
|  | 		ctx.Data["UnitTypeCommits"] = models.UnitTypeCommits | ||||||
|  | 		ctx.Data["UnitTypeReleases"] = models.UnitTypeReleases | ||||||
|  | 		ctx.Data["UnitTypeWiki"] = models.UnitTypeWiki | ||||||
|  | 		ctx.Data["UnitTypeSettings"] = models.UnitTypeSettings | ||||||
|  | 		ctx.Data["UnitTypeExternalWiki"] = models.UnitTypeExternalWiki | ||||||
|  | 		ctx.Data["UnitTypeExternalTracker"] = models.UnitTypeExternalTracker | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -205,7 +205,7 @@ func orgAssignment(args ...bool) macaron.Handler { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func mustEnableIssues(ctx *context.APIContext) { | func mustEnableIssues(ctx *context.APIContext) { | ||||||
| 	if !ctx.Repo.Repository.EnableIssues || ctx.Repo.Repository.EnableExternalTracker { | 	if !ctx.Repo.Repository.EnableUnit(models.UnitTypeIssues) { | ||||||
| 		ctx.Status(404) | 		ctx.Status(404) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -59,13 +59,15 @@ var ( | ||||||
| 
 | 
 | ||||||
| // MustEnableIssues check if repository enable internal issues
 | // MustEnableIssues check if repository enable internal issues
 | ||||||
| func MustEnableIssues(ctx *context.Context) { | func MustEnableIssues(ctx *context.Context) { | ||||||
| 	if !ctx.Repo.Repository.EnableIssues { | 	if !ctx.Repo.Repository.EnableUnit(models.UnitTypeIssues) && | ||||||
|  | 		!ctx.Repo.Repository.EnableUnit(models.UnitTypeExternalTracker) { | ||||||
| 		ctx.Handle(404, "MustEnableIssues", nil) | 		ctx.Handle(404, "MustEnableIssues", nil) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if ctx.Repo.Repository.EnableExternalTracker { | 	unit, err := ctx.Repo.Repository.GetUnit(models.UnitTypeExternalTracker) | ||||||
| 		ctx.Redirect(ctx.Repo.Repository.ExternalTrackerURL) | 	if err == nil { | ||||||
|  | 		ctx.Redirect(unit.ExternalTrackerConfig().ExternalTrackerURL) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -143,18 +143,70 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | ||||||
| 		ctx.Redirect(repo.Link() + "/settings") | 		ctx.Redirect(repo.Link() + "/settings") | ||||||
| 
 | 
 | ||||||
| 	case "advanced": | 	case "advanced": | ||||||
| 		repo.EnableWiki = form.EnableWiki | 		var units []models.RepoUnit | ||||||
| 		repo.EnableExternalWiki = form.EnableExternalWiki |  | ||||||
| 		repo.ExternalWikiURL = form.ExternalWikiURL |  | ||||||
| 		repo.EnableIssues = form.EnableIssues |  | ||||||
| 		repo.EnableExternalTracker = form.EnableExternalTracker |  | ||||||
| 		repo.ExternalTrackerURL = form.ExternalTrackerURL |  | ||||||
| 		repo.ExternalTrackerFormat = form.TrackerURLFormat |  | ||||||
| 		repo.ExternalTrackerStyle = form.TrackerIssueStyle |  | ||||||
| 		repo.EnablePulls = form.EnablePulls |  | ||||||
| 
 | 
 | ||||||
| 		if err := models.UpdateRepository(repo, false); err != nil { | 		for _, tp := range models.MustRepoUnits { | ||||||
| 			ctx.Handle(500, "UpdateRepository", err) | 			units = append(units, models.RepoUnit{ | ||||||
|  | 				RepoID: repo.ID, | ||||||
|  | 				Type:   tp, | ||||||
|  | 				Index:  int(tp), | ||||||
|  | 				Config: new(models.UnitConfig), | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if form.EnableWiki { | ||||||
|  | 			if form.EnableExternalWiki { | ||||||
|  | 				units = append(units, models.RepoUnit{ | ||||||
|  | 					RepoID: repo.ID, | ||||||
|  | 					Type:   models.UnitTypeExternalWiki, | ||||||
|  | 					Index:  int(models.UnitTypeExternalWiki), | ||||||
|  | 					Config: &models.ExternalWikiConfig{ | ||||||
|  | 						ExternalWikiURL: form.ExternalWikiURL, | ||||||
|  | 					}, | ||||||
|  | 				}) | ||||||
|  | 			} else { | ||||||
|  | 				units = append(units, models.RepoUnit{ | ||||||
|  | 					RepoID: repo.ID, | ||||||
|  | 					Type:   models.UnitTypeWiki, | ||||||
|  | 					Index:  int(models.UnitTypeWiki), | ||||||
|  | 					Config: new(models.UnitConfig), | ||||||
|  | 				}) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if form.EnableIssues { | ||||||
|  | 			if form.EnableExternalTracker { | ||||||
|  | 				units = append(units, models.RepoUnit{ | ||||||
|  | 					RepoID: repo.ID, | ||||||
|  | 					Type:   models.UnitTypeExternalWiki, | ||||||
|  | 					Index:  int(models.UnitTypeExternalWiki), | ||||||
|  | 					Config: &models.ExternalTrackerConfig{ | ||||||
|  | 						ExternalTrackerURL:    form.ExternalTrackerURL, | ||||||
|  | 						ExternalTrackerFormat: form.TrackerURLFormat, | ||||||
|  | 						ExternalTrackerStyle:  form.TrackerIssueStyle, | ||||||
|  | 					}, | ||||||
|  | 				}) | ||||||
|  | 			} else { | ||||||
|  | 				units = append(units, models.RepoUnit{ | ||||||
|  | 					RepoID: repo.ID, | ||||||
|  | 					Type:   models.UnitTypeIssues, | ||||||
|  | 					Index:  int(models.UnitTypeIssues), | ||||||
|  | 					Config: new(models.UnitConfig), | ||||||
|  | 				}) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if form.EnablePulls { | ||||||
|  | 			units = append(units, models.RepoUnit{ | ||||||
|  | 				RepoID: repo.ID, | ||||||
|  | 				Type:   models.UnitTypePullRequests, | ||||||
|  | 				Index:  int(models.UnitTypePullRequests), | ||||||
|  | 				Config: new(models.UnitConfig), | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if err := models.UpdateRepositoryUnits(repo, units); err != nil { | ||||||
|  | 			ctx.Handle(500, "UpdateRepositoryUnits", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) | 		log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) | ||||||
|  | @ -281,12 +333,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | ||||||
| 		repo.DeleteWiki() | 		repo.DeleteWiki() | ||||||
| 		log.Trace("Repository wiki deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name) | 		log.Trace("Repository wiki deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name) | ||||||
| 
 | 
 | ||||||
| 		repo.EnableWiki = false |  | ||||||
| 		if err := models.UpdateRepository(repo, false); err != nil { |  | ||||||
| 			ctx.Handle(500, "UpdateRepository", err) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		ctx.Flash.Success(ctx.Tr("repo.settings.wiki_deletion_success")) | 		ctx.Flash.Success(ctx.Tr("repo.settings.wiki_deletion_success")) | ||||||
| 		ctx.Redirect(ctx.Repo.RepoLink + "/settings") | 		ctx.Redirect(ctx.Repo.RepoLink + "/settings") | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -27,13 +27,15 @@ const ( | ||||||
| 
 | 
 | ||||||
| // MustEnableWiki check if wiki is enabled, if external then redirect
 | // MustEnableWiki check if wiki is enabled, if external then redirect
 | ||||||
| func MustEnableWiki(ctx *context.Context) { | func MustEnableWiki(ctx *context.Context) { | ||||||
| 	if !ctx.Repo.Repository.EnableWiki { | 	if !ctx.Repo.Repository.EnableUnit(models.UnitTypeWiki) && | ||||||
|  | 		!ctx.Repo.Repository.EnableUnit(models.UnitTypeExternalWiki) { | ||||||
| 		ctx.Handle(404, "MustEnableWiki", nil) | 		ctx.Handle(404, "MustEnableWiki", nil) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if ctx.Repo.Repository.EnableExternalWiki { | 	unit, err := ctx.Repo.Repository.GetUnit(models.UnitTypeExternalWiki) | ||||||
| 		ctx.Redirect(ctx.Repo.Repository.ExternalWikiURL) | 	if err == nil { | ||||||
|  | 		ctx.Redirect(unit.ExternalWikiConfig().ExternalWikiURL) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -236,7 +236,7 @@ func Issues(ctx *context.Context) { | ||||||
| 	for _, repo := range repos { | 	for _, repo := range repos { | ||||||
| 		if (isPullList && repo.NumPulls == 0) || | 		if (isPullList && repo.NumPulls == 0) || | ||||||
| 			(!isPullList && | 			(!isPullList && | ||||||
| 				(!repo.EnableIssues || repo.EnableExternalTracker || repo.NumIssues == 0)) { | 				(!repo.EnableUnit(models.UnitTypeIssues) || repo.NumIssues == 0)) { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -49,30 +49,48 @@ | ||||||
| {{if not (or .IsBareRepo .IsDiffCompare)}} | {{if not (or .IsBareRepo .IsDiffCompare)}} | ||||||
| 	<div class="ui tabs container"> | 	<div class="ui tabs container"> | ||||||
| 		<div class="ui tabular menu navbar"> | 		<div class="ui tabular menu navbar"> | ||||||
|  | 			{{if .Repository.EnableUnit $.UnitTypeCode}} | ||||||
| 			<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}"> | 			<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}"> | ||||||
| 				<i class="octicon octicon-code"></i> {{.i18n.Tr "repo.code"}} | 				<i class="octicon octicon-code"></i> {{.i18n.Tr "repo.code"}} | ||||||
| 			</a> | 			</a> | ||||||
| 			{{if .Repository.EnableIssues}} | 			{{end}} | ||||||
|  | 
 | ||||||
|  | 			{{if .Repository.EnableUnit $.UnitTypeIssues}} | ||||||
| 				<a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues"> | 				<a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues"> | ||||||
| 					<i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues"}} {{if not .Repository.EnableExternalTracker}}<span class="ui {{if not .Repository.NumOpenIssues}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenIssues}}{{end}}</span> | 					<i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues"}} <span class="ui {{if not .Repository.NumOpenIssues}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenIssues}}</span> | ||||||
| 				</a> | 				</a> | ||||||
| 			{{end}} | 			{{end}} | ||||||
|  | 
 | ||||||
|  | 			{{if .Repository.EnableUnit $.UnitTypeExternalTracker}} | ||||||
|  | 				<a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues"> | ||||||
|  | 					<i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues"}} </span> | ||||||
|  | 				</a> | ||||||
|  | 			{{end}} | ||||||
|  | 			 | ||||||
| 			{{if .Repository.AllowsPulls}} | 			{{if .Repository.AllowsPulls}} | ||||||
| 				<a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls"> | 				<a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls"> | ||||||
| 					<i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls"}} <span class="ui {{if not .Repository.NumOpenPulls}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenPulls}}</span> | 					<i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls"}} <span class="ui {{if not .Repository.NumOpenPulls}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenPulls}}</span> | ||||||
| 				</a> | 				</a> | ||||||
| 			{{end}} | 			{{end}} | ||||||
|  | 
 | ||||||
|  | 			{{if .Repository.EnableUnit $.UnitTypeCommits}} | ||||||
| 			<a class="{{if (or (.PageIsCommits) (.PageIsDiff))}}active{{end}} item" href="{{.RepoLink}}/commits/{{EscapePound .BranchName}}"> | 			<a class="{{if (or (.PageIsCommits) (.PageIsDiff))}}active{{end}} item" href="{{.RepoLink}}/commits/{{EscapePound .BranchName}}"> | ||||||
| 				<i class="octicon octicon-history"></i> {{.i18n.Tr "repo.commits"}} <span class="ui {{if not .CommitsCount}}gray{{else}}blue{{end}} small label">{{.CommitsCount}}</span> | 				<i class="octicon octicon-history"></i> {{.i18n.Tr "repo.commits"}} <span class="ui {{if not .CommitsCount}}gray{{else}}blue{{end}} small label">{{.CommitsCount}}</span> | ||||||
| 			</a> | 			</a> | ||||||
|  | 			{{end}} | ||||||
|  | 
 | ||||||
|  | 			{{if .Repository.EnableUnit $.UnitTypeReleases}} | ||||||
| 			<a class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases"> | 			<a class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases"> | ||||||
| 				<i class="octicon octicon-tag"></i> {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .Repository.NumTags}}gray{{else}}blue{{end}} small label">{{.Repository.NumTags}}</span> | 				<i class="octicon octicon-tag"></i> {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .Repository.NumTags}}gray{{else}}blue{{end}} small label">{{.Repository.NumTags}}</span> | ||||||
| 			</a> | 			</a> | ||||||
| 			{{if .Repository.EnableWiki}} | 			{{end}} | ||||||
|  | 
 | ||||||
|  | 			{{if or (.Repository.EnableUnit $.UnitTypeWiki) (.Repository.EnableUnit $.UnitTypeExternalWiki)}} | ||||||
| 				<a class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki"> | 				<a class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki"> | ||||||
| 					<i class="octicon octicon-book"></i> {{.i18n.Tr "repo.wiki"}} | 					<i class="octicon octicon-book"></i> {{.i18n.Tr "repo.wiki"}} | ||||||
| 				</a> | 				</a> | ||||||
| 			{{end}} | 			{{end}} | ||||||
|  | 
 | ||||||
| 			{{if .IsRepositoryAdmin}} | 			{{if .IsRepositoryAdmin}} | ||||||
| 				<div class="right menu"> | 				<div class="right menu"> | ||||||
| 					<a class="{{if .PageIsSettings}}active{{end}} item" href="{{.RepoLink}}/settings"> | 					<a class="{{if .PageIsSettings}}active{{end}} item" href="{{.RepoLink}}/settings"> | ||||||
|  |  | ||||||
|  | @ -114,26 +114,26 @@ | ||||||
| 						<div class="inline field"> | 						<div class="inline field"> | ||||||
| 							<label>{{.i18n.Tr "repo.wiki"}}</label> | 							<label>{{.i18n.Tr "repo.wiki"}}</label> | ||||||
| 							<div class="ui checkbox"> | 							<div class="ui checkbox"> | ||||||
| 								<input class="enable-system" name="enable_wiki" type="checkbox" data-target="#wiki_box" {{if .Repository.EnableWiki}}checked{{end}}> | 								<input class="enable-system" name="enable_wiki" type="checkbox" data-target="#wiki_box" {{if or (.Repository.EnableUnit $.UnitTypeWiki) (.Repository.EnableUnit $.UnitTypeExternalWiki)}}checked{{end}}> | ||||||
| 								<label>{{.i18n.Tr "repo.settings.wiki_desc"}}</label> | 								<label>{{.i18n.Tr "repo.settings.wiki_desc"}}</label> | ||||||
| 							</div> | 							</div> | ||||||
| 						</div> | 						</div> | ||||||
| 						<div class="field {{if not .Repository.EnableWiki}}disabled{{end}}" id="wiki_box"> | 						<div class="field {{if not (.Repository.EnableUnit $.UnitTypeWiki)}}disabled{{end}}" id="wiki_box"> | ||||||
| 							<div class="field"> | 							<div class="field"> | ||||||
| 								<div class="ui radio checkbox"> | 								<div class="ui radio checkbox"> | ||||||
| 									<input class="hidden enable-system-radio" tabindex="0" name="enable_external_wiki" type="radio" value="false" data-target="#external_wiki_box" {{if not .Repository.EnableExternalWiki}}checked{{end}}/> | 									<input class="hidden enable-system-radio" tabindex="0" name="enable_external_wiki" type="radio" value="false" data-target="#external_wiki_box" {{if not (.Repository.EnableUnit $.UnitTypeExternalWiki)}}checked{{end}}/> | ||||||
| 									<label>{{.i18n.Tr "repo.settings.use_internal_wiki"}}</label> | 									<label>{{.i18n.Tr "repo.settings.use_internal_wiki"}}</label> | ||||||
| 								</div> | 								</div> | ||||||
| 							</div> | 							</div> | ||||||
| 							<div class="field"> | 							<div class="field"> | ||||||
| 								<div class="ui radio checkbox"> | 								<div class="ui radio checkbox"> | ||||||
| 									<input class="hidden enable-system-radio" tabindex="0" name="enable_external_wiki" type="radio" value="true" data-target="#external_wiki_box" {{if .Repository.EnableExternalWiki}}checked{{end}}/> | 									<input class="hidden enable-system-radio" tabindex="0" name="enable_external_wiki" type="radio" value="true" data-target="#external_wiki_box" {{if .Repository.EnableUnit $.UnitTypeExternalWiki}}checked{{end}}/> | ||||||
| 									<label>{{.i18n.Tr "repo.settings.use_external_wiki"}}</label> | 									<label>{{.i18n.Tr "repo.settings.use_external_wiki"}}</label> | ||||||
| 								</div> | 								</div> | ||||||
| 							</div> | 							</div> | ||||||
| 							<div class="field {{if not .Repository.EnableExternalWiki}}disabled{{end}}" id="external_wiki_box"> | 							<div class="field {{if not (.Repository.EnableUnit $.UnitTypeExternalWiki)}}disabled{{end}}" id="external_wiki_box"> | ||||||
| 								<label for="external_wiki_url">{{.i18n.Tr "repo.settings.external_wiki_url"}}</label> | 								<label for="external_wiki_url">{{.i18n.Tr "repo.settings.external_wiki_url"}}</label> | ||||||
| 								<input id="external_wiki_url" name="external_wiki_url" type="url" value="{{.Repository.ExternalWikiURL}}"> | 								<input id="external_wiki_url" name="external_wiki_url" type="url" value="{{(.Repository.MustGetUnit $.UnitTypeExternalWiki).ExternalWikiConfig.ExternalWikiURL}}"> | ||||||
| 								<p class="help">{{.i18n.Tr "repo.settings.external_wiki_url_desc"}}</p> | 								<p class="help">{{.i18n.Tr "repo.settings.external_wiki_url_desc"}}</p> | ||||||
| 							</div> | 							</div> | ||||||
| 						</div> | 						</div> | ||||||
|  | @ -143,45 +143,47 @@ | ||||||
| 						<div class="inline field"> | 						<div class="inline field"> | ||||||
| 							<label>{{.i18n.Tr "repo.issues"}}</label> | 							<label>{{.i18n.Tr "repo.issues"}}</label> | ||||||
| 							<div class="ui checkbox"> | 							<div class="ui checkbox"> | ||||||
| 								<input class="enable-system" name="enable_issues" type="checkbox" data-target="#issue_box" {{if .Repository.EnableIssues}}checked{{end}}> | 								<input class="enable-system" name="enable_issues" type="checkbox" data-target="#issue_box" {{if or (.Repository.EnableUnit $.UnitTypeIssues) (.Repository.EnableUnit $.UnitTypeExternalTracker)}}checked{{end}}> | ||||||
| 								<label>{{.i18n.Tr "repo.settings.issues_desc"}}</label> | 								<label>{{.i18n.Tr "repo.settings.issues_desc"}}</label> | ||||||
| 							</div> | 							</div> | ||||||
| 						</div> | 						</div> | ||||||
| 						<div class="field {{if not .Repository.EnableIssues}}disabled{{end}}" id="issue_box"> | 						<div class="field {{if not (.Repository.EnableUnit $.UnitTypeIssues)}}disabled{{end}}" id="issue_box"> | ||||||
| 							<div class="field"> | 							<div class="field"> | ||||||
| 								<div class="ui radio checkbox"> | 								<div class="ui radio checkbox"> | ||||||
| 									<input class="hidden enable-system-radio" tabindex="0" name="enable_external_tracker" type="radio" value="false" data-target="#external_issue_box" {{if not .Repository.EnableExternalTracker}}checked{{end}}/> | 									<input class="hidden enable-system-radio" tabindex="0" name="enable_external_tracker" type="radio" value="false" data-target="#external_issue_box" {{if not (.Repository.EnableUnit $.UnitTypeExternalTracker)}}checked{{end}}/> | ||||||
| 									<label>{{.i18n.Tr "repo.settings.use_internal_issue_tracker"}}</label> | 									<label>{{.i18n.Tr "repo.settings.use_internal_issue_tracker"}}</label> | ||||||
| 								</div> | 								</div> | ||||||
| 							</div> | 							</div> | ||||||
| 							<div class="field"> | 							<div class="field"> | ||||||
| 								<div class="ui radio checkbox"> | 								<div class="ui radio checkbox"> | ||||||
| 									<input class="hidden enable-system-radio" tabindex="0" name="enable_external_tracker" type="radio" value="true" data-target="#external_issue_box" {{if .Repository.EnableExternalTracker}}checked{{end}}/> | 									<input class="hidden enable-system-radio" tabindex="0" name="enable_external_tracker" type="radio" value="true" data-target="#external_issue_box" {{if .Repository.EnableUnit $.UnitTypeExternalTracker}}checked{{end}}/> | ||||||
| 									<label>{{.i18n.Tr "repo.settings.use_external_issue_tracker"}}</label> | 									<label>{{.i18n.Tr "repo.settings.use_external_issue_tracker"}}</label> | ||||||
| 								</div> | 								</div> | ||||||
| 							</div> | 							</div> | ||||||
| 							<div class="field {{if not .Repository.EnableExternalTracker}}disabled{{end}}" id="external_issue_box"> | 							<div class="field {{if not (.Repository.EnableUnit $.UnitTypeExternalTracker)}}disabled{{end}}" id="external_issue_box"> | ||||||
| 								<div class="field"> | 								<div class="field"> | ||||||
| 									<label for="external_tracker_url">{{.i18n.Tr "repo.settings.external_tracker_url"}}</label> | 									<label for="external_tracker_url">{{.i18n.Tr "repo.settings.external_tracker_url"}}</label> | ||||||
| 									<input id="external_tracker_url" name="external_tracker_url" type="url" value="{{.Repository.ExternalTrackerURL}}"> | 									<input id="external_tracker_url" name="external_tracker_url" type="url" value="{{(.Repository.MustGetUnit $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerURL}}"> | ||||||
| 									<p class="help">{{.i18n.Tr "repo.settings.external_tracker_url_desc"}}</p> | 									<p class="help">{{.i18n.Tr "repo.settings.external_tracker_url_desc"}}</p> | ||||||
| 								</div> | 								</div> | ||||||
| 								<div class="field"> | 								<div class="field"> | ||||||
| 									<label for="tracker_url_format">{{.i18n.Tr "repo.settings.tracker_url_format"}}</label> | 									<label for="tracker_url_format">{{.i18n.Tr "repo.settings.tracker_url_format"}}</label> | ||||||
| 									<input id="tracker_url_format" name="tracker_url_format" type="url" value="{{.Repository.ExternalTrackerFormat}}" placeholder="e.g. https://github.com/{user}/{repo}/issues/{index}"> | 									<input id="tracker_url_format" name="tracker_url_format" type="url" value="{{(.Repository.MustGetUnit $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerFormat}}" placeholder="e.g. https://github.com/{user}/{repo}/issues/{index}"> | ||||||
| 									<p class="help">{{.i18n.Tr "repo.settings.tracker_url_format_desc" | Str2html}}</p> | 									<p class="help">{{.i18n.Tr "repo.settings.tracker_url_format_desc" | Str2html}}</p> | ||||||
| 								</div> | 								</div> | ||||||
| 								<div class="inline fields"> | 								<div class="inline fields"> | ||||||
| 									<label for="issue_style">{{.i18n.Tr "repo.settings.tracker_issue_style"}}</label> | 									<label for="issue_style">{{.i18n.Tr "repo.settings.tracker_issue_style"}}</label> | ||||||
| 									<div class="field"> | 									<div class="field"> | ||||||
| 										<div class="ui radio checkbox"> | 										<div class="ui radio checkbox"> | ||||||
| 											<input class="hidden" tabindex="0" name="tracker_issue_style" type="radio" value="numeric"  {{if eq .Repository.ExternalTrackerStyle "numeric"}}checked=""{{end}}/> | 										{{$externalTracker := (.Repository.MustGetUnit $.UnitTypeExternalTracker)}} | ||||||
|  | 										{{$externalTrackerStyle := $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle}} | ||||||
|  | 											<input class="hidden" tabindex="0" name="tracker_issue_style" type="radio" value="numeric" {{if $externalTrackerStyle}}{{if eq $externalTrackerStyle "numeric"}}checked=""{{end}}{{end}}/> | ||||||
| 											<label>{{.i18n.Tr "repo.settings.tracker_issue_style.numeric"}} <span class="ui light grey text">(#1234)</span></label> | 											<label>{{.i18n.Tr "repo.settings.tracker_issue_style.numeric"}} <span class="ui light grey text">(#1234)</span></label> | ||||||
| 										</div> | 										</div> | ||||||
| 									</div> | 									</div> | ||||||
| 									<div class="field"> | 									<div class="field"> | ||||||
| 										<div class="ui radio checkbox"> | 										<div class="ui radio checkbox"> | ||||||
| 											<input class="hidden" tabindex="0" name="tracker_issue_style" type="radio" value="alphanumeric"  {{if eq .Repository.ExternalTrackerStyle "alphanumeric"}}checked=""{{end}}/> | 											<input class="hidden" tabindex="0" name="tracker_issue_style" type="radio" value="alphanumeric" {{if $externalTrackerStyle}}{{if eq $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle "alphanumeric"}}checked=""{{end}}{{end}} /> | ||||||
| 											<label>{{.i18n.Tr "repo.settings.tracker_issue_style.alphanumeric"}} <span class="ui light grey text">(ABC-123, DEFG-234)</span></label> | 											<label>{{.i18n.Tr "repo.settings.tracker_issue_style.alphanumeric"}} <span class="ui light grey text">(ABC-123, DEFG-234)</span></label> | ||||||
| 										</div> | 										</div> | ||||||
| 									</div> | 									</div> | ||||||
|  | @ -195,7 +197,7 @@ | ||||||
| 							<div class="inline field"> | 							<div class="inline field"> | ||||||
| 								<label>{{.i18n.Tr "repo.pulls"}}</label> | 								<label>{{.i18n.Tr "repo.pulls"}}</label> | ||||||
| 								<div class="ui checkbox"> | 								<div class="ui checkbox"> | ||||||
| 									<input name="enable_pulls" type="checkbox" {{if .Repository.EnablePulls}}checked{{end}}> | 									<input name="enable_pulls" type="checkbox" {{if .Repository.EnableUnit $.UnitTypePullRequests}}checked{{end}}> | ||||||
| 									<label>{{.i18n.Tr "repo.settings.pulls_desc"}}</label> | 									<label>{{.i18n.Tr "repo.settings.pulls_desc"}}</label> | ||||||
| 								</div> | 								</div> | ||||||
| 							</div> | 							</div> | ||||||
|  | @ -236,7 +238,7 @@ | ||||||
| 						</div> | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 
 | 
 | ||||||
| 					{{if .Repository.EnableWiki}} | 					{{if .Repository.EnableUnit $.UnitTypeWiki}} | ||||||
| 						<div class="ui divider"></div> | 						<div class="ui divider"></div> | ||||||
| 
 | 
 | ||||||
| 						<div class="item"> | 						<div class="item"> | ||||||
|  | @ -370,7 +372,7 @@ | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
| 
 | 
 | ||||||
| 	{{if .Repository.EnableWiki}} | 	{{if .Repository.EnableUnit $.UnitTypeWiki}} | ||||||
| 	<div class="ui small modal" id="delete-wiki-modal"> | 	<div class="ui small modal" id="delete-wiki-modal"> | ||||||
| 		<div class="header"> | 		<div class="header"> | ||||||
| 			{{.i18n.Tr "repo.settings.wiki-delete"}} | 			{{.i18n.Tr "repo.settings.wiki-delete"}} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue