Skip to content

Commit 7e5c953

Browse files
Add API for Repo Advanced Settings of wiki and issue tracker
Signed-off-by: David Svantesson <[email protected]>
1 parent cde95f9 commit 7e5c953

File tree

4 files changed

+289
-73
lines changed

4 files changed

+289
-73
lines changed

models/repo.go

Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -275,12 +275,37 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool)
275275
}
276276
}
277277
hasIssues := false
278-
if _, err := repo.getUnit(e, UnitTypeIssues); err == nil {
278+
externalTracker := false
279+
externalTrackerURL := ""
280+
externalTrackerFormat := ""
281+
externalTrackerStyle := ""
282+
enableTimeTracker := false
283+
letOnlyContributorsTrackTime := false
284+
enableIssueDependencies := false
285+
if unit, err := repo.getUnit(e, UnitTypeIssues); err == nil {
286+
config := unit.IssuesConfig()
279287
hasIssues = true
288+
enableTimeTracker = config.EnableTimetracker
289+
letOnlyContributorsTrackTime = config.AllowOnlyContributorsToTrackTime
290+
enableIssueDependencies = config.EnableDependencies
291+
} else if unit, err := repo.getUnit(e, UnitTypeExternalTracker); err == nil {
292+
config := unit.ExternalTrackerConfig()
293+
hasIssues = true
294+
externalTracker = true
295+
externalTrackerURL = config.ExternalTrackerURL
296+
externalTrackerFormat = config.ExternalTrackerFormat
297+
externalTrackerStyle = config.ExternalTrackerStyle
280298
}
281299
hasWiki := false
300+
externalWiki := false
301+
externalWikiURL := ""
282302
if _, err := repo.getUnit(e, UnitTypeWiki); err == nil {
283303
hasWiki = true
304+
} else if unit, err := repo.getUnit(e, UnitTypeExternalWiki); err == nil {
305+
hasWiki = true
306+
config := unit.ExternalWikiConfig()
307+
externalWiki = true
308+
externalWikiURL = config.ExternalWikiURL
284309
}
285310
hasPullRequests := false
286311
ignoreWhitespaceConflicts := false
@@ -299,39 +324,48 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool)
299324
}
300325

301326
return &api.Repository{
302-
ID: repo.ID,
303-
Owner: repo.Owner.APIFormat(),
304-
Name: repo.Name,
305-
FullName: repo.FullName(),
306-
Description: repo.Description,
307-
Private: repo.IsPrivate,
308-
Empty: repo.IsEmpty,
309-
Archived: repo.IsArchived,
310-
Size: int(repo.Size / 1024),
311-
Fork: repo.IsFork,
312-
Parent: parent,
313-
Mirror: repo.IsMirror,
314-
HTMLURL: repo.HTMLURL(),
315-
SSHURL: cloneLink.SSH,
316-
CloneURL: cloneLink.HTTPS,
317-
Website: repo.Website,
318-
Stars: repo.NumStars,
319-
Forks: repo.NumForks,
320-
Watchers: repo.NumWatches,
321-
OpenIssues: repo.NumOpenIssues,
322-
DefaultBranch: repo.DefaultBranch,
323-
Created: repo.CreatedUnix.AsTime(),
324-
Updated: repo.UpdatedUnix.AsTime(),
325-
Permissions: permission,
326-
HasIssues: hasIssues,
327-
HasWiki: hasWiki,
328-
HasPullRequests: hasPullRequests,
329-
IgnoreWhitespaceConflicts: ignoreWhitespaceConflicts,
330-
AllowMerge: allowMerge,
331-
AllowRebase: allowRebase,
332-
AllowRebaseMerge: allowRebaseMerge,
333-
AllowSquash: allowSquash,
334-
AvatarURL: repo.avatarLink(e),
327+
ID: repo.ID,
328+
Owner: repo.Owner.APIFormat(),
329+
Name: repo.Name,
330+
FullName: repo.FullName(),
331+
Description: repo.Description,
332+
Private: repo.IsPrivate,
333+
Empty: repo.IsEmpty,
334+
Archived: repo.IsArchived,
335+
Size: int(repo.Size / 1024),
336+
Fork: repo.IsFork,
337+
Parent: parent,
338+
Mirror: repo.IsMirror,
339+
HTMLURL: repo.HTMLURL(),
340+
SSHURL: cloneLink.SSH,
341+
CloneURL: cloneLink.HTTPS,
342+
Website: repo.Website,
343+
Stars: repo.NumStars,
344+
Forks: repo.NumForks,
345+
Watchers: repo.NumWatches,
346+
OpenIssues: repo.NumOpenIssues,
347+
DefaultBranch: repo.DefaultBranch,
348+
Created: repo.CreatedUnix.AsTime(),
349+
Updated: repo.UpdatedUnix.AsTime(),
350+
Permissions: permission,
351+
HasIssues: hasIssues,
352+
ExternalTracker: externalTracker,
353+
ExternalTrackerURL: externalTrackerURL,
354+
ExternalTrackerFormat: externalTrackerFormat,
355+
ExternalTrackerStyle: externalTrackerStyle,
356+
EnableTimeTracker: enableTimeTracker,
357+
LetOnlyContributorsTrackTime: letOnlyContributorsTrackTime,
358+
EnableIssueDependencies: enableIssueDependencies,
359+
HasWiki: hasWiki,
360+
ExternalWiki: externalWiki,
361+
ExternalWikiURL: externalWikiURL,
362+
HasPullRequests: hasPullRequests,
363+
IgnoreWhitespaceConflicts: ignoreWhitespaceConflicts,
364+
AllowMerge: allowMerge,
365+
AllowRebase: allowRebase,
366+
AllowRebaseMerge: allowRebaseMerge,
367+
AllowSquash: allowSquash,
368+
AvatarURL: repo.avatarLink(e),
335369
}
336370
}
337371

modules/structs/repo.go

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,26 @@ type Repository struct {
4242
// swagger:strfmt date-time
4343
Created time.Time `json:"created_at"`
4444
// swagger:strfmt date-time
45-
Updated time.Time `json:"updated_at"`
46-
Permissions *Permission `json:"permissions,omitempty"`
47-
HasIssues bool `json:"has_issues"`
48-
HasWiki bool `json:"has_wiki"`
49-
HasPullRequests bool `json:"has_pull_requests"`
50-
IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"`
51-
AllowMerge bool `json:"allow_merge_commits"`
52-
AllowRebase bool `json:"allow_rebase"`
53-
AllowRebaseMerge bool `json:"allow_rebase_explicit"`
54-
AllowSquash bool `json:"allow_squash_merge"`
55-
AvatarURL string `json:"avatar_url"`
45+
Updated time.Time `json:"updated_at"`
46+
Permissions *Permission `json:"permissions,omitempty"`
47+
HasIssues bool `json:"has_issues"`
48+
ExternalTracker bool `json:"external_tracker"`
49+
ExternalTrackerURL string `json:"external_tracker_url"`
50+
ExternalTrackerFormat string `json:"external_tracker_format"`
51+
ExternalTrackerStyle string `json:"external_tracker_style"`
52+
EnableTimeTracker bool `json:"enable_time_tracker"`
53+
LetOnlyContributorsTrackTime bool `json:"let_only_contributors_track_time"`
54+
EnableIssueDependencies bool `json:"enable_issue_dependencies"`
55+
HasWiki bool `json:"has_wiki"`
56+
ExternalWiki bool `json:"external_wiki"`
57+
ExternalWikiURL string `json:"external_wiki_url"`
58+
HasPullRequests bool `json:"has_pull_requests"`
59+
IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"`
60+
AllowMerge bool `json:"allow_merge_commits"`
61+
AllowRebase bool `json:"allow_rebase"`
62+
AllowRebaseMerge bool `json:"allow_rebase_explicit"`
63+
AllowSquash bool `json:"allow_squash_merge"`
64+
AvatarURL string `json:"avatar_url"`
5665
}
5766

5867
// CreateRepoOption options when creating repository
@@ -93,8 +102,26 @@ type EditRepoOption struct {
93102
Private *bool `json:"private,omitempty"`
94103
// either `true` to enable issues for this repository or `false` to disable them.
95104
HasIssues *bool `json:"has_issues,omitempty"`
105+
// either `true` to enable external issue tracker or `false` to disable it.
106+
ExternalTracker *bool `json:"external_tracker,omitempty"`
107+
// URL of external issue tracker.
108+
ExternalTrackerURL *string `json:"external_tracker_url,omitempty"`
109+
// External Issue Tracker URL Format. Use the placeholders {user}, {repo} and {index} for the username, repository name and issue index.
110+
ExternalTrackerFormat *string `json:"external_tracker_format,omitempty"`
111+
// External Issue Tracker Number Format, either `numeric` or `alphanumeric`
112+
ExternalTrackerStyle *string `json:"external_tracker_style,omitempty"`
113+
// Enable time tracking (Built-in issue tracker)
114+
EnableTimeTracker *bool `json:"enable_time_tracker,omitempty"`
115+
// Let only contributors track time (Built-in issue tracker)
116+
LetOnlyContributorsTrackTime *bool `json:"let_only_contributors_track_time,omitempty"`
117+
// Enable dependencies for issues and pull requests (Built-in issue tracker)
118+
EnableIssueDependencies *bool `json:"enable_issue_dependencies,omitempty"`
96119
// either `true` to enable the wiki for this repository or `false` to disable it.
97120
HasWiki *bool `json:"has_wiki,omitempty"`
121+
// either `true` to enable external wiki or `false` to disable it.
122+
ExternalWiki *bool `json:"external_wiki,omitempty"`
123+
// URL of external wiki.
124+
ExternalWikiURL *string `json:"external_wiki_url,omitempty"`
98125
// sets the default branch for this repository.
99126
DefaultBranch *string `json:"default_branch,omitempty"`
100127
// either `true` to allow pull requests, or `false` to prevent pull request.

routers/api/v1/repo/repo.go

Lines changed: 102 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"code.gitea.io/gitea/modules/notification"
1919
"code.gitea.io/gitea/modules/setting"
2020
"code.gitea.io/gitea/modules/util"
21+
"code.gitea.io/gitea/modules/validation"
2122
"code.gitea.io/gitea/routers/api/v1/convert"
2223

2324
api "code.gitea.io/gitea/modules/structs"
@@ -659,27 +660,77 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
659660
units = append(units, *unit)
660661
}
661662
} else if *opts.HasIssues {
662-
// We don't currently allow setting individual issue settings through the API,
663-
// only can enable/disable issues, so when enabling issues,
664-
// we either get the existing config which means it was already enabled,
665-
// or create a new config since it doesn't exist.
666-
unit, err := repo.GetUnit(models.UnitTypeIssues)
667-
var config *models.IssuesConfig
668-
if err != nil {
669-
// Unit type doesn't exist so we make a new config file with default values
670-
config = &models.IssuesConfig{
671-
EnableTimetracker: true,
672-
AllowOnlyContributorsToTrackTime: true,
673-
EnableDependencies: true,
663+
if opts.ExternalTracker != nil && *opts.ExternalTracker {
664+
665+
var config *models.ExternalTrackerConfig
666+
if unit, err := repo.GetUnit(models.UnitTypeExternalTracker); err != nil {
667+
// Unit type doesn't exist so we make a new config file, default empty strings
668+
config = &models.ExternalTrackerConfig{
669+
ExternalTrackerURL: "",
670+
ExternalTrackerFormat: "",
671+
ExternalTrackerStyle: "",
672+
}
673+
} else {
674+
config = unit.ExternalTrackerConfig()
675+
}
676+
677+
// Update values if set and valid
678+
if opts.ExternalTrackerURL != nil {
679+
if !validation.IsValidExternalURL(*opts.ExternalTrackerURL) {
680+
err := fmt.Errorf("External tracker URL not valid")
681+
ctx.Error(http.StatusBadRequest, "Invalid external tracker URL", err)
682+
return err
683+
}
684+
config.ExternalTrackerURL = *opts.ExternalTrackerURL
685+
}
686+
if opts.ExternalTrackerFormat != nil {
687+
if len(*opts.ExternalTrackerFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(*opts.ExternalTrackerFormat) {
688+
err := fmt.Errorf("External tracker URL format not valid")
689+
ctx.Error(http.StatusBadRequest, "Invalid external tracker URL format", err)
690+
return err
691+
}
692+
config.ExternalTrackerFormat = *opts.ExternalTrackerFormat
693+
}
694+
if opts.ExternalTrackerStyle != nil {
695+
config.ExternalTrackerStyle = *opts.ExternalTrackerStyle
674696
}
697+
698+
units = append(units, models.RepoUnit{
699+
RepoID: repo.ID,
700+
Type: models.UnitTypeExternalTracker,
701+
Config: config,
702+
})
675703
} else {
676-
config = unit.IssuesConfig()
704+
// Default to built-in tracker
705+
var config *models.IssuesConfig
706+
if unit, err := repo.GetUnit(models.UnitTypeIssues); err != nil {
707+
// Unit type doesn't exist so we make a new config file with default values
708+
config = &models.IssuesConfig{
709+
EnableTimetracker: true,
710+
AllowOnlyContributorsToTrackTime: true,
711+
EnableDependencies: true,
712+
}
713+
} else {
714+
config = unit.IssuesConfig()
715+
}
716+
717+
// Update values if set
718+
if opts.EnableTimeTracker != nil {
719+
config.EnableTimetracker = *opts.EnableTimeTracker
720+
}
721+
if opts.LetOnlyContributorsTrackTime != nil {
722+
config.AllowOnlyContributorsToTrackTime = *opts.LetOnlyContributorsTrackTime
723+
}
724+
if opts.EnableIssueDependencies != nil {
725+
config.EnableDependencies = *opts.EnableIssueDependencies
726+
}
727+
728+
units = append(units, models.RepoUnit{
729+
RepoID: repo.ID,
730+
Type: models.UnitTypeIssues,
731+
Config: config,
732+
})
677733
}
678-
units = append(units, models.RepoUnit{
679-
RepoID: repo.ID,
680-
Type: models.UnitTypeIssues,
681-
Config: config,
682-
})
683734
}
684735

685736
if opts.HasWiki == nil {
@@ -690,16 +741,39 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
690741
units = append(units, *unit)
691742
}
692743
} else if *opts.HasWiki {
693-
// We don't currently allow setting individual wiki settings through the API,
694-
// only can enable/disable the wiki, so when enabling the wiki,
695-
// we either get the existing config which means it was already enabled,
696-
// or create a new config since it doesn't exist.
697-
config := &models.UnitConfig{}
698-
units = append(units, models.RepoUnit{
699-
RepoID: repo.ID,
700-
Type: models.UnitTypeWiki,
701-
Config: config,
702-
})
744+
if opts.ExternalWiki != nil && *opts.ExternalWiki {
745+
var config *models.ExternalWikiConfig
746+
if unit, err := repo.GetUnit(models.UnitTypeExternalWiki); err != nil {
747+
// Unit type doesn't exist so we make a new config file, default empty strings
748+
config = &models.ExternalWikiConfig{
749+
ExternalWikiURL: "",
750+
}
751+
} else {
752+
config = unit.ExternalWikiConfig()
753+
}
754+
755+
// Update values if set and valid
756+
if opts.ExternalWikiURL != nil {
757+
if !validation.IsValidExternalURL(*opts.ExternalWikiURL) {
758+
err := fmt.Errorf("External wiki URL not valid")
759+
ctx.Error(http.StatusBadRequest, "", "Invalid external wiki URL")
760+
return err
761+
}
762+
config.ExternalWikiURL = *opts.ExternalWikiURL
763+
}
764+
units = append(units, models.RepoUnit{
765+
RepoID: repo.ID,
766+
Type: models.UnitTypeExternalWiki,
767+
Config: config,
768+
})
769+
} else {
770+
config := &models.UnitConfig{}
771+
units = append(units, models.RepoUnit{
772+
RepoID: repo.ID,
773+
Type: models.UnitTypeWiki,
774+
Config: config,
775+
})
776+
}
703777
}
704778

705779
if opts.HasPullRequests == nil {

0 commit comments

Comments
 (0)