From 85900782705334720f27d6c09e3ff716de7f78e1 Mon Sep 17 00:00:00 2001 From: PineBale <272794187+PineBale@users.noreply.github.com> Date: Sat, 18 Apr 2026 18:18:03 +0800 Subject: [PATCH 01/11] Fix header for repo transfer --- templates/repo/header.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index d938508c21098..63db7036589d2 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -39,7 +39,7 @@ {{if not (or .IsBeingCreated .IsBroken)}}
- {{if $.RepoTransfer}} + {{if (and $.RepoTransfer (not $.Permission.IsOwner))}}
-
+
{{if not (or .IsBeingCreated .IsBroken)}}
- {{if (and $.RepoTransfer (not $.Permission.IsOwner))}} + {{if $.CanUserAcceptOrRejectTransfer}}
{{if not (or .IsBeingCreated .IsBroken)}}
- {{if $.CanUserAcceptOrRejectTransfer}} + {{if (and $.RepoTransfer (not $.Permission.IsOwner))}}
{{if not (or .IsBeingCreated .IsBroken)}}
- {{if (and $.RepoTransfer (not $.Permission.IsOwner))}} + {{if $.RepoTransfer}}
{{if not (or .IsBeingCreated .IsBroken)}}
- {{if $.RepoTransfer}} + {{if $.CanUserAcceptOrRejectTransfer}} -
-
-
-
From f82a0efa968038fd009ae8c903daa05dbb563c87 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 19 Apr 2026 22:44:05 +0800 Subject: [PATCH 09/11] fix --- options/locale/locale_en-US.json | 3 + routers/web/repo/repo.go | 9 +- routers/web/repo/transfer.go | 4 +- services/context/repo.go | 150 ++++++++++++++++++++++--------- templates/repo/header.tmpl | 24 +++-- 5 files changed, 125 insertions(+), 65 deletions(-) diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index cf26753d0dc92..22e23486e06e0 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -223,6 +223,7 @@ "error.occurred": "An error occurred", "error.report_message": "If you believe that this is a Gitea bug, please search for issues on GitHub or open a new issue if necessary.", "error.not_found": "The target couldn't be found.", + "error.permission_denied": "Permission denied.", "error.network_error": "Network error", "startpage.app_desc": "A painless, self-hosted Git service", "startpage.install": "Easy to install", @@ -1065,6 +1066,8 @@ "repo.transfer.accept_desc": "Transfer to \"%s\"", "repo.transfer.reject": "Reject Transfer", "repo.transfer.reject_desc": "Cancel transfer to \"%s\"", + "repo.transfer.is_transferring": "Transferring…", + "repo.transfer.is_transferring_prompt": "The repository is being transferred to %s", "repo.desc.private": "Private", "repo.desc.public": "Public", "repo.desc.public_access": "Public Access", diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index a245f707f80cb..dd5ec7dd471f4 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -302,18 +302,15 @@ func CreatePost(ctx *context.Context) { func handleActionError(ctx *context.Context, err error) { switch { case errors.Is(err, user_model.ErrBlockedUser): - ctx.Flash.Error(ctx.Tr("repo.action.blocked_user")) + ctx.JSONError(ctx.Tr("repo.action.blocked_user")) case repo_service.IsRepositoryLimitReached(err): limit := err.(repo_service.LimitReachedError).Limit - ctx.Flash.Error(ctx.TrN(limit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", limit)) + ctx.JSONError(ctx.TrN(limit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", limit)) case errors.Is(err, util.ErrPermissionDenied): - ctx.HTTPError(http.StatusNotFound) - return + ctx.JSONError(ctx.Tr("error.permission_denied")) default: ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.PathParam("action")), err) - return } - ctx.Redirect(ctx.Repo.Repository.Link()) } // RedirectDownload return a file based on the following infos: diff --git a/routers/web/repo/transfer.go b/routers/web/repo/transfer.go index 5553eee67417d..a606e0343f110 100644 --- a/routers/web/repo/transfer.go +++ b/routers/web/repo/transfer.go @@ -12,7 +12,7 @@ func acceptTransfer(ctx *context.Context) { err := repo_service.AcceptTransferOwnership(ctx, ctx.Repo.Repository, ctx.Doer) if err == nil { ctx.Flash.Success(ctx.Tr("repo.settings.transfer.success")) - ctx.Redirect(ctx.Repo.Repository.Link()) + ctx.JSONRedirect(ctx.Repo.Repository.Link()) return } handleActionError(ctx, err) @@ -22,7 +22,7 @@ func rejectTransfer(ctx *context.Context) { err := repo_service.RejectRepositoryTransfer(ctx, ctx.Repo.Repository, ctx.Doer) if err == nil { ctx.Flash.Success(ctx.Tr("repo.settings.transfer.rejected")) - ctx.Redirect(ctx.Repo.Repository.Link()) + ctx.JSONRedirect(ctx.Repo.Repository.Link()) return } handleActionError(ctx, err) diff --git a/services/context/repo.go b/services/context/repo.go index 1c6a92509d065..2208c1a9e8a8b 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -411,8 +411,9 @@ func RedirectToRepo(ctx *Base, redirectRepoID int64) { ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusMovedPermanently) } -func repoAssignment(ctx *Context, repo *repo_model.Repository) { +func repoAssignmentLegacy(ctx *Context, data *repoAssignmentPrepareDataStruct) { var err error + repo := data.repo if err = repo.LoadOwner(ctx); err != nil { ctx.ServerError("LoadOwner", err) return @@ -459,13 +460,26 @@ func InitRepoPullRequestCtx(ctx *Context, base, head *repo_model.Repository) { ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequestCtx } -// RepoAssignment returns a middleware to handle repository assignment -func RepoAssignment(ctx *Context) { +type repoAssignmentPrepareDataStruct struct { + ownerName string + repoName string + isHomeOrSettings bool + repo *repo_model.Repository +} + +func repoAssignmentPreCheck(ctx *Context) { if ctx.Data["Repository"] != nil { setting.PanicInDevOrTesting("RepoAssignment should not be executed twice") } + if ctx.Repo.GitRepo != nil { + setting.PanicInDevOrTesting("RepoAssignment: GitRepo should be nil") + _ = ctx.Repo.GitRepo.Close() + ctx.Repo.GitRepo = nil + } +} - var err error +func repoAssignmentPrepareData(ctx *Context) *repoAssignmentPrepareDataStruct { + // HINT: here it doesn't handle ".wiki" extension, it is handled in repoAssignmentAutoRedirectWiki, need to be refactored in the future userName := ctx.PathParam("username") repoName := ctx.PathParam("reponame") repoName = strings.TrimSuffix(repoName, ".git") @@ -475,6 +489,21 @@ func RepoAssignment(ctx *Context) { repoName = strings.TrimSuffix(repoName, ".atom") } + isHomeOrSettings := ctx.Link == ctx.Repo.RepoLink || + ctx.Link == ctx.Repo.RepoLink+"/settings" || + strings.HasPrefix(ctx.Link, ctx.Repo.RepoLink+"/settings/") || + ctx.Link == ctx.Repo.RepoLink+"/-/migrate/status" + + return &repoAssignmentPrepareDataStruct{ + ownerName: userName, + repoName: repoName, + isHomeOrSettings: isHomeOrSettings, + } +} + +func repoAssignmentPrepareOwner(ctx *Context, data *repoAssignmentPrepareDataStruct) { + var err error + userName := data.ownerName // Check if the user is the same as the repository owner if ctx.IsSigned && strings.EqualFold(ctx.Doer.LowerName, userName) { ctx.Repo.Owner = ctx.Doer @@ -504,7 +533,10 @@ func RepoAssignment(ctx *Context) { } ctx.ContextUser = ctx.Repo.Owner ctx.Data["ContextUser"] = ctx.ContextUser +} +func repoAssignmentAutoRedirectWiki(ctx *Context, data *repoAssignmentPrepareDataStruct) { + userName, repoName := data.ownerName, data.repoName // redirect link to wiki if strings.HasSuffix(repoName, ".wiki") { // ctx.Req.URL.Path does not have the preceding appSubURL - any redirect must have this added @@ -524,7 +556,10 @@ func RepoAssignment(ctx *Context) { ctx.Redirect(path.Join(setting.AppSubURL, redirectPath)) return } +} +func repoAssignmentPrepareRepo(ctx *Context, data *repoAssignmentPrepareDataStruct) { + repoName := data.repoName // Get repository. repo, err := repo_model.GetRepositoryByName(ctx, ctx.Repo.Owner.ID, repoName) if err != nil { @@ -547,12 +582,11 @@ func RepoAssignment(ctx *Context) { return } repo.Owner = ctx.Repo.Owner + data.repo = repo +} - repoAssignment(ctx, repo) - if ctx.Written() { - return - } - +func repoAssignmentPrepareTemplateData(ctx *Context, data *repoAssignmentPrepareDataStruct) { + repo := data.repo ctx.Repo.RepoLink = repo.Link() ctx.Data["RepoLink"] = ctx.Repo.RepoLink ctx.Data["FeedURL"] = ctx.Repo.RepoLink @@ -645,33 +679,28 @@ func RepoAssignment(ctx *Context) { return } } +} - isHomeOrSettings := ctx.Link == ctx.Repo.RepoLink || - ctx.Link == ctx.Repo.RepoLink+"/settings" || - strings.HasPrefix(ctx.Link, ctx.Repo.RepoLink+"/settings/") || - ctx.Link == ctx.Repo.RepoLink+"/-/migrate/status" - +func repoAssignmentAutoRedirectNotRead(ctx *Context, data *repoAssignmentPrepareDataStruct) { // Disable everything when the repo is being created if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() { - if !isHomeOrSettings { + if !data.isHomeOrSettings { ctx.Redirect(ctx.Repo.RepoLink) } return } +} - if ctx.Repo.GitRepo != nil { - setting.PanicInDevOrTesting("RepoAssignment: GitRepo should be nil") - _ = ctx.Repo.GitRepo.Close() - ctx.Repo.GitRepo = nil - } - +func repoAssignmentPrepareGitRepo(ctx *Context, data *repoAssignmentPrepareDataStruct) { + var err error + repo := data.repo ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo) if err != nil { if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") { log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RelativePath(), err) ctx.Repo.Repository.MarkAsBrokenEmpty() // Only allow access to base of repo or settings - if !isHomeOrSettings { + if !data.isHomeOrSettings { ctx.Redirect(ctx.Repo.RepoLink) } return @@ -679,30 +708,12 @@ func RepoAssignment(ctx *Context) { ctx.ServerError("RepoAssignment Invalid repo "+repo.FullName(), err) return } +} - if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer { - repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) - if err != nil { - ctx.ServerError("GetPendingRepositoryTransfer", err) - return - } - - if err := repoTransfer.LoadAttributes(ctx); err != nil { - ctx.ServerError("LoadRecipient", err) - return - } - - ctx.Data["RepoTransfer"] = repoTransfer - if ctx.Doer != nil { - ctx.Data["CanUserAcceptOrRejectTransfer"] = repoTransfer.CanUserAcceptOrRejectTransfer(ctx, ctx.Doer) - } - } - - // Stop at this point when the repo is empty. - if ctx.Repo.Repository.IsEmpty { +func repoAssignmentPrepareBranches(ctx *Context, data *repoAssignmentPrepareDataStruct) { + if data.repo.IsEmpty { return } - branchOpts := git_model.FindBranchOptions{ RepoID: ctx.Repo.Repository.ID, IsDeletedBranch: optional.Some(false), @@ -724,7 +735,13 @@ func RepoAssignment(ctx *Context) { } ctx.Data["BranchesCount"] = branchesTotal +} +func repoAssignmentPreparePullRequests(ctx *Context, data *repoAssignmentPrepareDataStruct) { + repo := data.repo + if repo.IsEmpty { + return + } // Pull request is allowed if this is a fork repository, and base repository accepts pull requests. if repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls(ctx) { // TODO: this (and below) "BaseRepo" var is not clear and should be removed in the future @@ -735,7 +752,28 @@ func RepoAssignment(ctx *Context) { ctx.Data["BaseRepo"] = repo InitRepoPullRequestCtx(ctx, repo, repo) } +} +func repoAssignmentPrepareRepoTransfer(ctx *Context, data *repoAssignmentPrepareDataStruct) { + if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer { + repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) + if err != nil { + ctx.ServerError("GetPendingRepositoryTransfer", err) + return + } + + if err := repoTransfer.LoadAttributes(ctx); err != nil { + ctx.ServerError("LoadRecipient", err) + return + } + + ctx.Data["RepoTransfer"] = repoTransfer + ctx.Data["CanUserAcceptOrRejectTransfer"] = ctx.Doer != nil && repoTransfer.CanUserAcceptOrRejectTransfer(ctx, ctx.Doer) + } +} + +func repoAssignmentHandleGoGet(ctx *Context, data *repoAssignmentPrepareDataStruct) { + repo := data.repo if ctx.FormString("go-get") == "1" { ctx.Data["GoGetImport"] = ComposeGoGetImport(ctx, repo.Owner.Name, repo.Name) fullURLPrefix := repo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName) @@ -744,6 +782,32 @@ func RepoAssignment(ctx *Context) { } } +// RepoAssignment returns a middleware to handle repository assignment +func RepoAssignment(ctx *Context) { + repoAssignmentPreCheck(ctx) + + prepareData := repoAssignmentPrepareData(ctx) + funcs := []func(ctx *Context, data *repoAssignmentPrepareDataStruct){ + repoAssignmentPrepareOwner, + repoAssignmentAutoRedirectWiki, + repoAssignmentPrepareRepo, + repoAssignmentLegacy, + repoAssignmentPrepareTemplateData, + repoAssignmentAutoRedirectNotRead, + repoAssignmentPrepareGitRepo, + repoAssignmentPrepareRepoTransfer, + repoAssignmentPrepareBranches, + repoAssignmentPreparePullRequests, + repoAssignmentHandleGoGet, + } + for _, f := range funcs { + f(ctx, prepareData) + if ctx.Written() { + return + } + } +} + const headRefName = "HEAD" func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool) string { diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 218e0a8d7f8b6..a6e419bcb07ed 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -40,20 +40,16 @@ {{if not (or .IsBeingCreated .IsBroken)}}
{{if $.CanUserAcceptOrRejectTransfer}} - -
- -
- -
-
- -
-
+ + + {{else if $.RepoTransfer}} + {{end}} {{if $.EnableFeed}} {{/* An extra div-element is not necessary here, as this button does not secretly contain two buttons. */}} From dd6a69038d649f2e573f63917420386aecfe3196 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 19 Apr 2026 23:17:41 +0800 Subject: [PATCH 10/11] fix isHomeOrSettings --- services/context/repo.go | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/services/context/repo.go b/services/context/repo.go index 2208c1a9e8a8b..d177315514a01 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -461,10 +461,9 @@ func InitRepoPullRequestCtx(ctx *Context, base, head *repo_model.Repository) { } type repoAssignmentPrepareDataStruct struct { - ownerName string - repoName string - isHomeOrSettings bool - repo *repo_model.Repository + ownerName string + repoName string + repo *repo_model.Repository } func repoAssignmentPreCheck(ctx *Context) { @@ -488,17 +487,7 @@ func repoAssignmentPrepareData(ctx *Context) *repoAssignmentPrepareDataStruct { repoName = strings.TrimSuffix(repoName, ".rss") repoName = strings.TrimSuffix(repoName, ".atom") } - - isHomeOrSettings := ctx.Link == ctx.Repo.RepoLink || - ctx.Link == ctx.Repo.RepoLink+"/settings" || - strings.HasPrefix(ctx.Link, ctx.Repo.RepoLink+"/settings/") || - ctx.Link == ctx.Repo.RepoLink+"/-/migrate/status" - - return &repoAssignmentPrepareDataStruct{ - ownerName: userName, - repoName: repoName, - isHomeOrSettings: isHomeOrSettings, - } + return &repoAssignmentPrepareDataStruct{ownerName: userName, repoName: repoName} } func repoAssignmentPrepareOwner(ctx *Context, data *repoAssignmentPrepareDataStruct) { @@ -681,10 +670,17 @@ func repoAssignmentPrepareTemplateData(ctx *Context, data *repoAssignmentPrepare } } -func repoAssignmentAutoRedirectNotRead(ctx *Context, data *repoAssignmentPrepareDataStruct) { +func repoAssignmentIsHomeOrSettings(ctx *Context, data *repoAssignmentPrepareDataStruct) bool { + repoLink := data.repo.Link() + return ctx.Link == repoLink || + strings.HasPrefix(ctx.Link+"/", repoLink+"/settings/") || + ctx.Link == repoLink+"/-/migrate/status" +} + +func repoAssignmentAutoRedirectNotReady(ctx *Context, data *repoAssignmentPrepareDataStruct) { // Disable everything when the repo is being created if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() { - if !data.isHomeOrSettings { + if !repoAssignmentIsHomeOrSettings(ctx, data) { ctx.Redirect(ctx.Repo.RepoLink) } return @@ -700,7 +696,7 @@ func repoAssignmentPrepareGitRepo(ctx *Context, data *repoAssignmentPrepareDataS log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RelativePath(), err) ctx.Repo.Repository.MarkAsBrokenEmpty() // Only allow access to base of repo or settings - if !data.isHomeOrSettings { + if !repoAssignmentIsHomeOrSettings(ctx, data) { ctx.Redirect(ctx.Repo.RepoLink) } return @@ -793,7 +789,7 @@ func RepoAssignment(ctx *Context) { repoAssignmentPrepareRepo, repoAssignmentLegacy, repoAssignmentPrepareTemplateData, - repoAssignmentAutoRedirectNotRead, + repoAssignmentAutoRedirectNotReady, repoAssignmentPrepareGitRepo, repoAssignmentPrepareRepoTransfer, repoAssignmentPrepareBranches, From affc205b3d3534d5eb96fe25a1f77de793eef0fc Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 20 Apr 2026 00:02:31 +0800 Subject: [PATCH 11/11] fine tune --- services/context/repo.go | 2 +- templates/repo/header.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/context/repo.go b/services/context/repo.go index d177315514a01..3ad629f83ae9a 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -759,7 +759,7 @@ func repoAssignmentPrepareRepoTransfer(ctx *Context, data *repoAssignmentPrepare } if err := repoTransfer.LoadAttributes(ctx); err != nil { - ctx.ServerError("LoadRecipient", err) + ctx.ServerError("LoadAttributes", err) return } diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index a6e419bcb07ed..9ed74d50bc0e3 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -48,7 +48,7 @@ >{{ctx.Locale.Tr "repo.transfer.reject"}} {{else if $.RepoTransfer}}
- {{ctx.Locale.Tr "repo.transfer.is_transferring"}} +
{{end}} {{if $.EnableFeed}}