diff --git a/models/repo/repo.go b/models/repo/repo.go index eecd7133cf6..8387e429edf 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -473,9 +473,13 @@ func (repo *Repository) composeCommonMetas(ctx context.Context) map[string]strin "repo": repo.Name, } - unitExternalTracker, err := repo.GetUnit(ctx, unit.TypeExternalTracker) - if err == nil { - metas["format"] = unitExternalTracker.ExternalTrackerConfig().ExternalTrackerFormat + unitInternalTracker, _ := repo.GetUnit(ctx, unit.TypeIssues) + unitExternalTracker, _ := repo.GetUnit(ctx, unit.TypeExternalTracker) + if unitInternalTracker != nil { + metas["internalTrackerEnabled"] = "true" + } + if unitExternalTracker != nil { + metas["externalTrackerLinkFormat"] = unitExternalTracker.ExternalTrackerConfig().ExternalTrackerFormat switch unitExternalTracker.ExternalTrackerConfig().ExternalTrackerStyle { case markup.IssueNameStyleAlphanumeric: metas["style"] = markup.IssueNameStyleAlphanumeric diff --git a/models/repo/repo_test.go b/models/repo/repo_test.go index 1d825568109..afef5f9cd9c 100644 --- a/models/repo/repo_test.go +++ b/models/repo/repo_test.go @@ -103,7 +103,7 @@ func TestMetas(t *testing.T) { assert.Equal(t, expectedStyle, metas["style"]) assert.Equal(t, "testRepo", metas["repo"]) assert.Equal(t, "testOwner", metas["user"]) - assert.Equal(t, "https://someurl.com/{user}/{repo}/{issue}", metas["format"]) + assert.Equal(t, "https://someurl.com/{user}/{repo}/{issue}", metas["externalTrackerLinkFormat"]) } testSuccess(markup.IssueNameStyleNumeric) diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index 6036f368901..f0366f68275 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -39,7 +39,7 @@ func link(href, class, contents string) string { } var numericMetas = map[string]string{ - "format": "https://someurl.com/{user}/{repo}/{index}", + "externalTrackerLinkFormat": "https://someurl.com/{user}/{repo}/{index}", "user": "someUser", "repo": "someRepo", "style": IssueNameStyleNumeric, @@ -47,7 +47,7 @@ var numericMetas = map[string]string{ } var alphanumericMetas = map[string]string{ - "format": "https://someurl.com/{user}/{repo}/{index}", + "externalTrackerLinkFormat": "https://someurl.com/{user}/{repo}/{index}", "user": "someUser", "repo": "someRepo", "style": IssueNameStyleAlphanumeric, @@ -55,10 +55,10 @@ var alphanumericMetas = map[string]string{ } var regexpMetas = map[string]string{ - "format": "https://someurl.com/{user}/{repo}/{index}", - "user": "someUser", - "repo": "someRepo", - "style": IssueNameStyleRegexp, + "externalTrackerLinkFormat": "https://someurl.com/{user}/{repo}/{index}", + "user": "someUser", + "repo": "someRepo", + "style": IssueNameStyleRegexp, } // these values should match the TestOrgRepo const above @@ -219,23 +219,29 @@ func TestRender_IssueIndexPattern5(t *testing.T) { } test("abc ISSUE-123 def", "abc %s def", - "ISSUE-(\\d+)", + `ISSUE-(\d+)`, []string{"123"}, []string{"ISSUE-123"}, ) test("abc (ISSUE 123) def", "abc %s def", - "\\(ISSUE (\\d+)\\)", + `\(ISSUE (\d+)\)`, []string{"123"}, []string{"(ISSUE 123)"}, ) test("abc ISSUE-123 def", "abc %s def", - "(ISSUE-(\\d+))", + `(ISSUE-(\d+))`, []string{"ISSUE-123"}, []string{"ISSUE-123"}, ) + test("123456: TEST-123456", "%s %s", + `(\d+):|TEST-(\d+)`, + []string{"123456", "123456"}, + []string{"123456:", "TEST-123456"}, + ) + testRenderIssueIndexPattern(t, "will not match", "will not match", NewTestRenderContext(regexpMetas)) } diff --git a/modules/markup/html_issue.go b/modules/markup/html_issue.go index 71dc18a9032..e676517cd9b 100644 --- a/modules/markup/html_issue.go +++ b/modules/markup/html_issue.go @@ -110,16 +110,23 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { next := node.NextSibling for node != nil && node != next { - _, hasExtTrackFormat := ctx.RenderOptions.Metas["format"] - + _, hasExternalTracker := ctx.RenderOptions.Metas["externalTrackerLinkFormat"] + hasInternalTracker := ctx.RenderOptions.Metas["internalTrackerEnabled"] == "true" + if !hasExternalTracker && !hasInternalTracker { + hasInternalTracker = true // legacy logic: if no tracker is enabled, fallback to internal + } // Repos with external issue trackers might still need to reference local PRs // We need to concern with the first one that shows up in the text, whichever it is isNumericStyle := ctx.RenderOptions.Metas["style"] == "" || ctx.RenderOptions.Metas["style"] == IssueNameStyleNumeric - refNumeric := references.FindRenderizableReferenceNumeric(node.Data, hasExtTrackFormat && !isNumericStyle, crossLinkOnly) + prOnly := hasExternalTracker && !isNumericStyle + refNumeric := references.FindRenderizableReferenceNumeric(node.Data, prOnly, crossLinkOnly) + useExtTrackerLink := true switch ctx.RenderOptions.Metas["style"] { case "", IssueNameStyleNumeric: ref = refNumeric + // when internal tracker is enabled, Numeric (#123) style should only be use for internal tracker + useExtTrackerLink = !hasInternalTracker case IssueNameStyleAlphanumeric: ref = references.FindRenderizableReferenceAlphanumeric(node.Data) case IssueNameStyleRegexp: @@ -132,7 +139,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { // Repos with external issue trackers might still need to reference local PRs // We need to concern with the first one that shows up in the text, whichever it is - if hasExtTrackFormat && !isNumericStyle && refNumeric != nil { + if useExtTrackerLink && !isNumericStyle && refNumeric != nil { // If numeric (PR) was found, and it was BEFORE the non-numeric pattern, use that // Allow a free-pass when non-numeric pattern wasn't found. if ref == nil || refNumeric.RefLocation.Start < ref.RefLocation.Start { @@ -146,10 +153,10 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { var link *html.Node refText := node.Data[ref.RefLocation.Start:ref.RefLocation.End] - if hasExtTrackFormat && !ref.IsPull { + if useExtTrackerLink && !ref.IsPull { ctx.RenderOptions.Metas["index"] = ref.Issue - res, err := vars.ExpandCurlyBrace(ctx.RenderOptions.Metas["format"], ctx.RenderOptions.Metas) + res, err := vars.ExpandCurlyBrace(ctx.RenderOptions.Metas["externalTrackerLinkFormat"], ctx.RenderOptions.Metas) if err != nil { // here we could just log the error and continue the rendering log.Error("unable to expand template vars for ref %s, err: %v", ref.Issue, err) @@ -183,7 +190,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { // Decorate action keywords if actionable var keyword *html.Node - if references.IsXrefActionable(ref, hasExtTrackFormat) { + if references.IsXrefActionable(ref, useExtTrackerLink) { keyword = createKeyword(ctx, node.Data[ref.ActionLocation.Start:ref.ActionLocation.End]) } else { keyword = &html.Node{ diff --git a/modules/references/references.go b/modules/references/references.go index b14cc5f4359..d3d451269fd 100644 --- a/modules/references/references.go +++ b/modules/references/references.go @@ -378,9 +378,23 @@ func FindRenderizableReferenceRegexp(content string, pattern *regexp.Regexp) *Re return nil } - action, location := findActionKeywords([]byte(content), match[2]) + // The external tracker pattern can use alternatives with separate capture + // groups. Pick the first group that participated in this match instead of + // assuming the first group always did. + issueStart, issueEnd := -1, -1 + for i := 2; i+1 < len(match); i += 2 { + if match[i] >= 0 { + issueStart, issueEnd = match[i], match[i+1] + break + } + } + if issueStart < 0 { + return nil + } + + action, location := findActionKeywords([]byte(content), issueStart) return &RenderizableReference{ - Issue: content[match[2]:match[3]], + Issue: content[issueStart:issueEnd], RefLocation: &RefSpan{Start: match[0], End: match[1]}, Action: action, ActionLocation: location, diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index fffb963fbea..d043a6986b6 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -2140,15 +2140,15 @@ "repo.settings.external_wiki_url": "External Wiki URL", "repo.settings.external_wiki_url_error": "The external wiki URL is not a valid URL.", "repo.settings.external_wiki_url_desc": "Visitors are redirected to the external wiki URL when clicking the wiki tab.", - "repo.settings.issues_desc": "Enable Repository Issue Tracker", "repo.settings.use_internal_issue_tracker": "Use Built-In Issue Tracker", "repo.settings.use_external_issue_tracker": "Use External Issue Tracker", "repo.settings.external_tracker_url": "External Issue Tracker URL", "repo.settings.external_tracker_url_error": "The external issue tracker URL is not a valid URL.", - "repo.settings.external_tracker_url_desc": "Visitors are redirected to the external issue tracker URL when clicking on the issues tab.", + "repo.settings.external_tracker_url_desc": "When built-in issue tracker is disabled, visitors are redirected to the external issue tracker URL when clicking on the issues tab.", "repo.settings.tracker_url_format": "External Issue Tracker URL Format", "repo.settings.tracker_url_format_error": "The external issue tracker URL format is not a valid URL.", "repo.settings.tracker_issue_style": "External Issue Tracker Number Format", + "repo.settings.tracker_issue_style_desc": "When the internal issue tracker is enabled, the Numeric style can only be used for the internal issue tracker.", "repo.settings.tracker_issue_style.numeric": "Numeric", "repo.settings.tracker_issue_style.alphanumeric": "Alphanumeric", "repo.settings.tracker_issue_style.regexp": "Regular Expression", diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 53bb3c5ea44..871b3582088 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -25,6 +25,7 @@ import ( "gitea.dev/modules/git" "gitea.dev/modules/label" "gitea.dev/modules/log" + "gitea.dev/modules/markup" "gitea.dev/modules/optional" repo_module "gitea.dev/modules/repository" "gitea.dev/modules/setting" @@ -612,6 +613,7 @@ func Edit(ctx *context.APIContext) { } if err := updateRepoUnits(ctx, opts); err != nil { + ctx.APIErrorAuto(err) return } @@ -750,24 +752,21 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err // updateRepoUnits updates repo units: Issue settings, Wiki settings, PR settings func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { - owner := ctx.Repo.Owner repo := ctx.Repo.Repository var units []repo_model.RepoUnit var deleteUnitTypes []unit_model.Type - if opts.HasIssues != nil { - if *opts.HasIssues && opts.ExternalTracker != nil && !unit_model.TypeExternalTracker.UnitGlobalDisabled() { - // Check that values are valid - if !validation.IsValidURL(opts.ExternalTracker.ExternalTrackerURL) { - err := errors.New("External tracker URL not valid") - ctx.APIError(http.StatusUnprocessableEntity, err.Error()) - return err + if opts.HasIssues != nil && *opts.HasIssues { + if opts.ExternalTracker != nil && !unit_model.TypeExternalTracker.UnitGlobalDisabled() { + if (opts.InternalTracker == nil || opts.ExternalTracker.ExternalTrackerURL != "") && !validation.IsValidURL(opts.ExternalTracker.ExternalTrackerURL) { + return util.ErrorWrap(util.ErrUnprocessableContent, "external tracker URL not valid") } - if len(opts.ExternalTracker.ExternalTrackerFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(opts.ExternalTracker.ExternalTrackerFormat) { - err := errors.New("External tracker URL format not valid") - ctx.APIError(http.StatusUnprocessableEntity, err.Error()) - return err + if opts.InternalTracker != nil && (opts.ExternalTracker.ExternalTrackerStyle == "" || opts.ExternalTracker.ExternalTrackerStyle == markup.IssueNameStyleNumeric) { + return util.ErrorWrap(util.ErrUnprocessableContent, "external tracker style Numeric is only used for internal tracker") + } + if opts.ExternalTracker.ExternalTrackerFormat != "" && !validation.IsValidExternalTrackerURLFormat(opts.ExternalTracker.ExternalTrackerFormat) { + return util.ErrorWrap(util.ErrUnprocessableContent, "External tracker URL format not valid") } units = append(units, repo_model.RepoUnit{ @@ -780,8 +779,10 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { ExternalTrackerRegexpPattern: opts.ExternalTracker.ExternalTrackerRegexpPattern, }, }) - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues) - } else if *opts.HasIssues && opts.ExternalTracker == nil && !unit_model.TypeIssues.UnitGlobalDisabled() { + } else { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) + } + if (opts.ExternalTracker == nil || opts.InternalTracker != nil) && !unit_model.TypeIssues.UnitGlobalDisabled() { // Default to built-in tracker var config *repo_model.IssuesConfig @@ -807,24 +808,20 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { Type: unit_model.TypeIssues, Config: config, }) - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) - } else if !*opts.HasIssues { - if !unit_model.TypeExternalTracker.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) - } - if !unit_model.TypeIssues.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues) - } + } else { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues) } } + if opts.HasIssues != nil && !*opts.HasIssues { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues) + } if opts.HasWiki != nil { if *opts.HasWiki && opts.ExternalWiki != nil && !unit_model.TypeExternalWiki.UnitGlobalDisabled() { // Check that values are valid if !validation.IsValidURL(opts.ExternalWiki.ExternalWikiURL) { - err := errors.New("External wiki URL not valid") - ctx.APIError(http.StatusUnprocessableEntity, "Invalid external wiki URL") - return err + return util.ErrorWrap(util.ErrUnprocessableContent, "external wiki URL not valid") } units = append(units, repo_model.RepoUnit{ @@ -902,7 +899,6 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { // so unrelated PATCH calls don't reject historical configs. if opts.AllowMergeUpdate != nil || opts.AllowRebaseUpdate != nil || opts.DefaultUpdateStyle != nil { if err := config.ValidateUpdateSettings(); err != nil { - ctx.APIError(http.StatusUnprocessableEntity, err.Error()) return err } } @@ -977,12 +973,9 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { if len(units)+len(deleteUnitTypes) > 0 { if err := repo_service.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil { - ctx.APIErrorInternal(err) return err } } - - log.Trace("Repository advanced settings updated: %s/%s", owner.Name, repo.Name) return nil } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index b7241ee0b5b..511085bcdd4 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -96,10 +96,13 @@ func MustEnableIssues(ctx *context.Context) { return } - unit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalTracker) + unitExtTracker, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalTracker) if err == nil { - ctx.Redirect(unit.ExternalTrackerConfig().ExternalTrackerURL) - return + extURL := unitExtTracker.ExternalTrackerConfig().ExternalTrackerURL + if extURL != "" { + ctx.Redirect(extURL) + return + } } } diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 252f4fa9b1c..9b44abd5685 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -24,6 +24,7 @@ import ( "gitea.dev/modules/indexer/stats" "gitea.dev/modules/lfs" "gitea.dev/modules/log" + "gitea.dev/modules/markup" "gitea.dev/modules/setting" "gitea.dev/modules/structs" "gitea.dev/modules/templates" @@ -570,10 +571,6 @@ func handleSettingsPostAdvanced(ctx *context.Context) { var units []repo_model.RepoUnit var deleteUnitTypes []unit_model.Type - // This section doesn't require repo_name/RepoName to be set in the form, don't show it - // as an error on the UI for this action - ctx.Data["Err_RepoName"] = nil - if repo.CloseIssuesViaCommitInAnyBranch != form.EnableCloseIssuesViaCommitInAnyBranch { repo.CloseIssuesViaCommitInAnyBranch = form.EnableCloseIssuesViaCommitInAnyBranch repoChanged = true @@ -587,8 +584,7 @@ func handleSettingsPostAdvanced(ctx *context.Context) { if form.EnableWiki && form.EnableExternalWiki && !unit_model.TypeExternalWiki.UnitGlobalDisabled() { if !validation.IsValidURL(form.ExternalWikiURL) { - ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error")) - ctx.Redirect(repo.Link() + "/settings") + ctx.JSONError(ctx.Tr("repo.settings.external_wiki_url_error")) return } @@ -611,19 +607,21 @@ func handleSettingsPostAdvanced(ctx *context.Context) { if form.DefaultWikiBranch != "" { if err := wiki_service.ChangeDefaultWikiBranch(ctx, repo, form.DefaultWikiBranch); err != nil { log.Error("ChangeDefaultWikiBranch failed, err: %v", err) - ctx.Flash.Warning(ctx.Tr("repo.settings.failed_to_change_default_wiki_branch")) + ctx.Flash.Warning(ctx.Tr("repo.settings.failed_to_change_default_wiki_branch")) // skip the error, continue, and reload page } } - if form.EnableIssues && form.EnableExternalTracker && !unit_model.TypeExternalTracker.UnitGlobalDisabled() { - if !validation.IsValidURL(form.ExternalTrackerURL) { - ctx.Flash.Error(ctx.Tr("repo.settings.external_tracker_url_error")) - ctx.Redirect(repo.Link() + "/settings") + if form.EnableExternalTracker && !unit_model.TypeExternalTracker.UnitGlobalDisabled() { + if (!form.EnableInternalTracker || form.ExternalTrackerURL != "") && !validation.IsValidURL(form.ExternalTrackerURL) { + ctx.JSONError(ctx.Tr("repo.settings.external_tracker_url_error")) return } - if len(form.TrackerURLFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(form.TrackerURLFormat) { - ctx.Flash.Error(ctx.Tr("repo.settings.tracker_url_format_error")) - ctx.Redirect(repo.Link() + "/settings") + if form.TrackerURLFormat != "" && !validation.IsValidExternalTrackerURLFormat(form.TrackerURLFormat) { + ctx.JSONError(ctx.Tr("repo.settings.tracker_url_format_error")) + return + } + if form.EnableInternalTracker && (form.TrackerIssueStyle == "" || form.TrackerIssueStyle == markup.IssueNameStyleNumeric) { + ctx.JSONError(ctx.Tr("repo.settings.tracker_issue_style_desc")) return } units = append(units, newRepoUnit(repo, unit_model.TypeExternalTracker, &repo_model.ExternalTrackerConfig{ @@ -632,21 +630,18 @@ func handleSettingsPostAdvanced(ctx *context.Context) { ExternalTrackerStyle: form.TrackerIssueStyle, ExternalTrackerRegexpPattern: form.ExternalTrackerRegexpPattern, })) - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues) - } else if form.EnableIssues && !form.EnableExternalTracker && !unit_model.TypeIssues.UnitGlobalDisabled() { + } else { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) + } + + if form.EnableInternalTracker && !unit_model.TypeIssues.UnitGlobalDisabled() { units = append(units, newRepoUnit(repo, unit_model.TypeIssues, &repo_model.IssuesConfig{ EnableTimetracker: form.EnableTimetracker, AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime, EnableDependencies: form.EnableIssueDependencies, })) - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) } else { - if !unit_model.TypeExternalTracker.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) - } - if !unit_model.TypeIssues.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues) - } + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues) } if form.EnableProjects && !unit_model.TypeProjects.UnitGlobalDisabled() { @@ -689,8 +684,7 @@ func handleSettingsPostAdvanced(ctx *context.Context) { DefaultTargetBranch: strings.TrimSpace(form.DefaultTargetBranch), } if err := prConfig.ValidateUpdateSettings(); err != nil { - ctx.Flash.Error(err.Error()) - ctx.Redirect(repo.Link() + "/settings") + ctx.JSONErrorAuto(err) return } units = append(units, newRepoUnit(repo, unit_model.TypePullRequests, prConfig)) @@ -699,8 +693,7 @@ func handleSettingsPostAdvanced(ctx *context.Context) { } if len(units) == 0 { - ctx.Flash.Error(ctx.Tr("repo.settings.update_settings_no_unit")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings") + ctx.JSONError(ctx.Tr("repo.settings.update_settings_no_unit")) return } @@ -714,10 +707,9 @@ func handleSettingsPostAdvanced(ctx *context.Context) { return } } - log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings") + ctx.JSONRedirect("") } func handleSettingsPostSigning(ctx *context.Context) { diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 3fdbb5b2cf2..75efc8e74a1 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -105,12 +105,12 @@ type RepoSettingForm struct { DefaultWikiBranch string ExternalWikiURL string - EnableIssues bool + EnableInternalTracker bool EnableExternalTracker bool - ExternalTrackerURL string - TrackerURLFormat string + ExternalTrackerURL string `binding:"TrimSpace"` + TrackerURLFormat string `binding:"TrimSpace"` TrackerIssueStyle string - ExternalTrackerRegexpPattern string + ExternalTrackerRegexpPattern string `binding:"TrimSpace"` EnableCloseIssuesViaCommitInAnyBranch bool EnableProjects bool diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 7dffd3d0d56..084b96278be 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -303,7 +303,7 @@ {{ctx.Locale.Tr "repo.settings.advanced_settings"}}
-
+ {{$isCodeEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeCode}} @@ -357,25 +357,19 @@
- {{$isIssuesEnabled := or (.Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeIssues) (.Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeExternalTracker)}} {{$isIssuesGlobalDisabled := ctx.Consts.RepoUnitTypeIssues.UnitGlobalDisabled}} {{$isExternalTrackerGlobalDisabled := ctx.Consts.RepoUnitTypeExternalTracker.UnitGlobalDisabled}} {{$isIssuesAndExternalGlobalDisabled := and $isIssuesGlobalDisabled $isExternalTrackerGlobalDisabled}} -
+
-
- - -
-
-
+ {{$isInternalTrackerEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeIssues}}
-
- +
+
-
+
{{if .Repository.CanEnableTimetracker}}
@@ -401,13 +395,15 @@
+ + {{$isExternalTrackerEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeExternalTracker}}
-
- +
+
-
+
@@ -418,9 +414,12 @@

{{ctx.Locale.Tr "repo.settings.tracker_url_format_desc"}}

-
- -
+
+ +

{{ctx.Locale.Tr "repo.settings.tracker_issue_style_desc"}}

+
+
+
{{$externalTracker := (.Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeExternalTracker)}} {{$externalTrackerStyle := $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle}} @@ -428,13 +427,13 @@
-
+
-
+
diff --git a/tests/integration/api_repo_edit_test.go b/tests/integration/api_repo_edit_test.go index 74db4fb1ef7..d60eb3c5752 100644 --- a/tests/integration/api_repo_edit_test.go +++ b/tests/integration/api_repo_edit_test.go @@ -16,7 +16,10 @@ import ( "gitea.dev/models/unittest" user_model "gitea.dev/models/user" "gitea.dev/modules/git" + "gitea.dev/modules/setting" api "gitea.dev/modules/structs" + "gitea.dev/modules/test" + "gitea.dev/services/migrations" mirror_service "gitea.dev/services/mirror" "gitea.dev/tests" @@ -42,7 +45,8 @@ func getRepoEditOptionFromRepo(repo *repo_model.Repository) *api.EditRepoOption AllowOnlyContributorsToTrackTime: config.AllowOnlyContributorsToTrackTime, EnableIssueDependencies: config.EnableDependencies, } - } else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalTracker); err == nil { + } + if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalTracker); err == nil { config := unit.ExternalTrackerConfig() hasIssues = true externalTracker = &api.ExternalTracker{ @@ -460,6 +464,10 @@ func TestAPIRepoEdit(t *testing.T) { require.NoError(t, mirror_service.UpdateAddress(ctx, mirror, "https://existing-user:existing-password@example.com/user2/repo1.git")) + defer migrations.Init() + defer test.MockVariableValue(&setting.Migrations.AllowedDomains, "*")() + _ = migrations.Init() + req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", mirrorRepo.OwnerName, mirrorRepo.Name), &api.EditRepoOption{ MirrorPassword: &newPassword, }).AddTokenAuth(token2) @@ -521,7 +529,7 @@ func TestAPIRepoEditPullUpdateSettingsValidation(t *testing.T) { AllowMergeUpdate: &allowMergeUpdate, AllowRebaseUpdate: &allowRebaseUpdate, }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusUnprocessableEntity) + MakeRequest(t, req, http.StatusBadRequest) allowRebaseUpdate = true defaultUpdateStyle := string(repo_model.UpdateStyleMerge) @@ -530,5 +538,5 @@ func TestAPIRepoEditPullUpdateSettingsValidation(t *testing.T) { AllowRebaseUpdate: &allowRebaseUpdate, DefaultUpdateStyle: &defaultUpdateStyle, }).AddTokenAuth(token) - MakeRequest(t, req, http.StatusUnprocessableEntity) + MakeRequest(t, req, http.StatusBadRequest) } diff --git a/web_src/js/modules/fetch-action.ts b/web_src/js/modules/fetch-action.ts index 87bce528e63..63b9506ec01 100644 --- a/web_src/js/modules/fetch-action.ts +++ b/web_src/js/modules/fetch-action.ts @@ -443,4 +443,11 @@ export function initGlobalFetchAction() { }); registerGlobalSelectorFunc('[data-fetch-url]', initFetchActionTrigger); + + // when the page is reloaded after a fetch action, scroll to the flash message if any + const elFlashMessage = document.querySelector('.ui.message.flash-message'); + if (elFlashMessage) { + window.history.scrollRestoration = 'manual'; + elFlashMessage?.scrollIntoView({block: 'center'}); + } }