mirror of
https://github.com/go-gitea/gitea
synced 2025-12-07 13:28:25 +00:00
Merge branch 'main' into allow-force-push-protected-branches
This commit is contained in:
@@ -200,6 +200,7 @@ func List(ctx *context.Context) {
|
||||
pager.AddParamString("actor", fmt.Sprint(actorID))
|
||||
pager.AddParamString("status", fmt.Sprint(status))
|
||||
ctx.Data["Page"] = pager
|
||||
ctx.Data["HasWorkflowsOrRuns"] = len(workflows) > 0 || len(runs) > 0
|
||||
|
||||
ctx.HTML(http.StatusOK, tplListActions)
|
||||
}
|
||||
|
||||
+15
-32
@@ -114,12 +114,12 @@ func RefBlame(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
commitNames, previousCommits := processBlameParts(ctx, result.Parts)
|
||||
commitNames := processBlameParts(ctx, result.Parts)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
renderBlame(ctx, result.Parts, commitNames, previousCommits)
|
||||
renderBlame(ctx, result.Parts, commitNames)
|
||||
|
||||
ctx.HTML(http.StatusOK, tplRepoHome)
|
||||
}
|
||||
@@ -131,7 +131,12 @@ type blameResult struct {
|
||||
}
|
||||
|
||||
func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) {
|
||||
blameReader, err := git.CreateBlameReader(ctx, repoPath, commit, file, bypassBlameIgnore)
|
||||
objectFormat, err := ctx.Repo.GitRepo.GetObjectFormat()
|
||||
if err != nil {
|
||||
ctx.NotFound("CreateBlameReader", err)
|
||||
return nil, err
|
||||
}
|
||||
blameReader, err := git.CreateBlameReader(ctx, objectFormat, repoPath, commit, file, bypassBlameIgnore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -147,7 +152,7 @@ func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, fil
|
||||
if len(r.Parts) == 0 && r.UsesIgnoreRevs {
|
||||
// try again without ignored revs
|
||||
|
||||
blameReader, err = git.CreateBlameReader(ctx, repoPath, commit, file, true)
|
||||
blameReader, err = git.CreateBlameReader(ctx, objectFormat, repoPath, commit, file, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -185,12 +190,9 @@ func fillBlameResult(br *git.BlameReader, r *blameResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[string]*user_model.UserCommit, map[string]string) {
|
||||
func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) map[string]*user_model.UserCommit {
|
||||
// store commit data by SHA to look up avatar info etc
|
||||
commitNames := make(map[string]*user_model.UserCommit)
|
||||
// previousCommits contains links from SHA to parent SHA,
|
||||
// if parent also contains the current TreePath.
|
||||
previousCommits := make(map[string]string)
|
||||
// and as blameParts can reference the same commits multiple
|
||||
// times, we cache the lookup work locally
|
||||
commits := make([]*git.Commit, 0, len(blameParts))
|
||||
@@ -214,29 +216,11 @@ func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[st
|
||||
} else {
|
||||
ctx.ServerError("Repo.GitRepo.GetCommit", err)
|
||||
}
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
commitCache[sha] = commit
|
||||
}
|
||||
|
||||
// find parent commit
|
||||
if commit.ParentCount() > 0 {
|
||||
psha := commit.Parents[0]
|
||||
previousCommit, ok := commitCache[psha.String()]
|
||||
if !ok {
|
||||
previousCommit, _ = commit.Parent(0)
|
||||
if previousCommit != nil {
|
||||
commitCache[psha.String()] = previousCommit
|
||||
}
|
||||
}
|
||||
// only store parent commit ONCE, if it has the file
|
||||
if previousCommit != nil {
|
||||
if haz1, _ := previousCommit.HasFile(ctx.Repo.TreePath); haz1 {
|
||||
previousCommits[commit.ID.String()] = previousCommit.ID.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commits = append(commits, commit)
|
||||
}
|
||||
|
||||
@@ -245,10 +229,10 @@ func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[st
|
||||
commitNames[c.ID.String()] = c
|
||||
}
|
||||
|
||||
return commitNames, previousCommits
|
||||
return commitNames
|
||||
}
|
||||
|
||||
func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames map[string]*user_model.UserCommit, previousCommits map[string]string) {
|
||||
func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames map[string]*user_model.UserCommit) {
|
||||
repoLink := ctx.Repo.RepoLink
|
||||
|
||||
language := ""
|
||||
@@ -295,7 +279,6 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
|
||||
}
|
||||
|
||||
commit := commitNames[part.Sha]
|
||||
previousSha := previousCommits[part.Sha]
|
||||
if index == 0 {
|
||||
// Count commit number
|
||||
commitCnt++
|
||||
@@ -313,8 +296,8 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
|
||||
br.Avatar = gotemplate.HTML(avatar)
|
||||
br.RepoLink = repoLink
|
||||
br.PartSha = part.Sha
|
||||
br.PreviousSha = previousSha
|
||||
br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, url.PathEscape(previousSha), util.PathEscapeSegments(ctx.Repo.TreePath))
|
||||
br.PreviousSha = part.PreviousSha
|
||||
br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, url.PathEscape(part.PreviousSha), util.PathEscapeSegments(part.PreviousPath))
|
||||
br.CommitURL = fmt.Sprintf("%s/commit/%s", repoLink, url.PathEscape(part.Sha))
|
||||
br.CommitMessage = commit.CommitMessage
|
||||
br.CommitSince = commitSince
|
||||
|
||||
@@ -147,11 +147,18 @@ func RestoreBranchPost(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
objectFormat, err := git.GetObjectFormatOfRepo(ctx, ctx.Repo.Repository.RepoPath())
|
||||
if err != nil {
|
||||
log.Error("RestoreBranch: CreateBranch: %w", err)
|
||||
ctx.Flash.Error(ctx.Tr("repo.branch.restore_failed", deletedBranch.Name))
|
||||
return
|
||||
}
|
||||
|
||||
// Don't return error below this
|
||||
if err := repo_service.PushUpdate(
|
||||
&repo_module.PushUpdateOptions{
|
||||
RefFullName: git.RefNameFromBranch(deletedBranch.Name),
|
||||
OldCommitID: git.EmptySHA,
|
||||
OldCommitID: objectFormat.Empty().String(),
|
||||
NewCommitID: deletedBranch.CommitID,
|
||||
PusherID: ctx.Doer.ID,
|
||||
PusherName: ctx.Doer.Name,
|
||||
@@ -191,9 +198,9 @@ func CreateBranch(ctx *context.Context) {
|
||||
}
|
||||
err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, target, form.NewBranchName, "")
|
||||
} else if ctx.Repo.IsViewBranch {
|
||||
err = repo_service.CreateNewBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName)
|
||||
err = repo_service.CreateNewBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.BranchName, form.NewBranchName)
|
||||
} else {
|
||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.CommitID, form.NewBranchName)
|
||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.CommitID, form.NewBranchName)
|
||||
}
|
||||
if err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
|
||||
@@ -294,7 +294,7 @@ func Diff(ctx *context.Context) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(commitID) != git.SHAFullLength {
|
||||
if len(commitID) != commit.ID.Type().FullLength() {
|
||||
commitID = commit.ID.String()
|
||||
}
|
||||
|
||||
|
||||
@@ -310,13 +310,14 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
|
||||
baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(ci.BaseBranch)
|
||||
baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(ci.BaseBranch)
|
||||
baseIsTag := ctx.Repo.GitRepo.IsTagExist(ci.BaseBranch)
|
||||
objectFormat, _ := ctx.Repo.GitRepo.GetObjectFormat()
|
||||
if !baseIsCommit && !baseIsBranch && !baseIsTag {
|
||||
// Check if baseBranch is short sha commit hash
|
||||
if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(ci.BaseBranch); baseCommit != nil {
|
||||
ci.BaseBranch = baseCommit.ID.String()
|
||||
ctx.Data["BaseBranch"] = ci.BaseBranch
|
||||
baseIsCommit = true
|
||||
} else if ci.BaseBranch == git.EmptySHA {
|
||||
} else if ci.BaseBranch == objectFormat.Empty().String() {
|
||||
if isSameRepo {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadBranch))
|
||||
} else {
|
||||
|
||||
@@ -329,7 +329,7 @@ func dummyInfoRefs(ctx *context.Context) {
|
||||
}
|
||||
}()
|
||||
|
||||
if err := git.InitRepository(ctx, tmpDir, true); err != nil {
|
||||
if err := git.InitRepository(ctx, tmpDir, true, git.ObjectFormatFromID(git.Sha1)); err != nil {
|
||||
log.Error("Failed to init bare repo for git-receive-pack cache: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -510,9 +510,8 @@ func Issues(ctx *context.Context) {
|
||||
|
||||
func renderMilestones(ctx *context.Context) {
|
||||
// Get milestones
|
||||
milestones, _, err := issues_model.GetMilestones(ctx, issues_model.GetMilestonesOption{
|
||||
milestones, err := db.Find[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
State: api.StateAll,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetAllRepoMilestones", err)
|
||||
@@ -534,17 +533,17 @@ func renderMilestones(ctx *context.Context) {
|
||||
// RetrieveRepoMilestonesAndAssignees find all the milestones and assignees of a repository
|
||||
func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.Repository) {
|
||||
var err error
|
||||
ctx.Data["OpenMilestones"], _, err = issues_model.GetMilestones(ctx, issues_model.GetMilestonesOption{
|
||||
RepoID: repo.ID,
|
||||
State: api.StateOpen,
|
||||
ctx.Data["OpenMilestones"], err = db.Find[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{
|
||||
RepoID: repo.ID,
|
||||
IsClosed: util.OptionalBoolFalse,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetMilestones", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["ClosedMilestones"], _, err = issues_model.GetMilestones(ctx, issues_model.GetMilestonesOption{
|
||||
RepoID: repo.ID,
|
||||
State: api.StateClosed,
|
||||
ctx.Data["ClosedMilestones"], err = db.Find[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{
|
||||
RepoID: repo.ID,
|
||||
IsClosed: util.OptionalBoolTrue,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetMilestones", err)
|
||||
@@ -1319,7 +1318,7 @@ func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *use
|
||||
return roleDescriptor, err
|
||||
} else if hasMergedPR {
|
||||
roleDescriptor.RoleInRepo = issues_model.RoleRepoContributor
|
||||
} else {
|
||||
} else if issue.IsPull {
|
||||
// only display first time contributor in the first opening pull request
|
||||
roleDescriptor.RoleInRepo = issues_model.RoleRepoFirstTimeContributor
|
||||
}
|
||||
@@ -3106,6 +3105,11 @@ func UpdateCommentContent(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
ctx.Error(http.StatusForbidden)
|
||||
return
|
||||
@@ -3172,6 +3176,11 @@ func DeleteComment(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
ctx.Error(http.StatusForbidden)
|
||||
return
|
||||
@@ -3298,6 +3307,11 @@ func ChangeCommentReaction(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
if log.IsTrace() {
|
||||
if ctx.IsSigned {
|
||||
@@ -3441,6 +3455,21 @@ func GetCommentAttachments(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := comment.LoadIssue(ctx); err != nil {
|
||||
ctx.NotFoundOrServerError("LoadIssue", issues_model.IsErrIssueNotExist, err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.Permission.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
ctx.NotFound("CanReadIssuesOrPulls", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
|
||||
if !comment.Type.HasAttachmentSupport() {
|
||||
ctx.ServerError("GetCommentAttachments", fmt.Errorf("comment type %v does not support attachments", comment.Type))
|
||||
return
|
||||
|
||||
@@ -122,7 +122,7 @@ func GetContentHistoryDetail(ctx *context.Context) {
|
||||
}
|
||||
|
||||
historyID := ctx.FormInt64("history_id")
|
||||
history, prevHistory, err := issues_model.GetIssueContentHistoryAndPrev(ctx, historyID)
|
||||
history, prevHistory, err := issues_model.GetIssueContentHistoryAndPrev(ctx, issue.ID, historyID)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusNotFound, map[string]any{
|
||||
"message": "Can not find the content history",
|
||||
@@ -193,15 +193,29 @@ func SoftDeleteContentHistory(ctx *context.Context) {
|
||||
var comment *issues_model.Comment
|
||||
var history *issues_model.ContentHistory
|
||||
var err error
|
||||
|
||||
if history, err = issues_model.GetIssueContentHistoryByID(ctx, historyID); err != nil {
|
||||
log.Error("can not get issue content history %v. err=%v", historyID, err)
|
||||
return
|
||||
}
|
||||
if history.IssueID != issue.ID {
|
||||
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
if commentID != 0 {
|
||||
if history.CommentID != commentID {
|
||||
ctx.NotFound("CompareCommentID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
|
||||
if comment, err = issues_model.GetCommentByID(ctx, commentID); err != nil {
|
||||
log.Error("can not get comment for issue content history %v. err=%v", historyID, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if history, err = issues_model.GetIssueContentHistoryByID(ctx, historyID); err != nil {
|
||||
log.Error("can not get issue content history %v. err=%v", historyID, err)
|
||||
return
|
||||
if comment.IssueID != issue.ID {
|
||||
ctx.NotFound("CompareIssueID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
canSoftDelete := canSoftDeleteContentHistory(ctx, issue, comment, history)
|
||||
|
||||
@@ -90,6 +90,12 @@ func IssuePinMove(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
log.Error("Issue does not belong to this repository")
|
||||
return
|
||||
}
|
||||
|
||||
err = issue.MovePin(ctx, form.Position)
|
||||
if err != nil {
|
||||
ctx.Status(http.StatusInternalServerError)
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
@@ -46,18 +45,13 @@ func Milestones(ctx *context.Context) {
|
||||
page = 1
|
||||
}
|
||||
|
||||
state := structs.StateOpen
|
||||
if isShowClosed {
|
||||
state = structs.StateClosed
|
||||
}
|
||||
|
||||
miles, total, err := issues_model.GetMilestones(ctx, issues_model.GetMilestonesOption{
|
||||
miles, total, err := db.FindAndCount[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{
|
||||
ListOptions: db.ListOptions{
|
||||
Page: page,
|
||||
PageSize: setting.UI.IssuePagingNum,
|
||||
},
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
State: state,
|
||||
IsClosed: util.OptionalBoolOf(isShowClosed),
|
||||
SortType: sortType,
|
||||
Name: keyword,
|
||||
})
|
||||
@@ -80,7 +74,7 @@ func Milestones(ctx *context.Context) {
|
||||
url.QueryEscape(keyword), url.QueryEscape(sortType))
|
||||
|
||||
if ctx.Repo.Repository.IsTimetrackerEnabled(ctx) {
|
||||
if err := miles.LoadTotalTrackedTimes(ctx); err != nil {
|
||||
if err := issues_model.MilestoneList(miles).LoadTotalTrackedTimes(ctx); err != nil {
|
||||
ctx.ServerError("LoadTotalTrackedTimes", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -468,7 +468,7 @@ func AddBoardToProjectPost(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
|
||||
project, err := project_model.GetProjectForRepoByID(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectNotExist(err) {
|
||||
ctx.NotFound("", nil)
|
||||
|
||||
@@ -174,6 +174,7 @@ func TagsList(ctx *context.Context) {
|
||||
// Disable the showCreateNewBranch form in the dropdown on this page.
|
||||
ctx.Data["CanCreateBranch"] = false
|
||||
ctx.Data["HideBranchesInDropdown"] = true
|
||||
ctx.Data["CanCreateRelease"] = ctx.Repo.CanWrite(unit.TypeReleases) && !ctx.Repo.Repository.IsArchived
|
||||
|
||||
listOptions := db.ListOptions{
|
||||
Page: ctx.FormInt("page"),
|
||||
@@ -616,7 +617,27 @@ func DeleteTag(ctx *context.Context) {
|
||||
}
|
||||
|
||||
func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) {
|
||||
if err := releaseservice.DeleteReleaseByID(ctx, ctx.FormInt64("id"), ctx.Doer, isDelTag); err != nil {
|
||||
redirect := func() {
|
||||
if isDelTag {
|
||||
ctx.JSONRedirect(ctx.Repo.RepoLink + "/tags")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSONRedirect(ctx.Repo.RepoLink + "/releases")
|
||||
}
|
||||
|
||||
rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, ctx.FormInt64("id"))
|
||||
if err != nil {
|
||||
if repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.NotFound("GetReleaseForRepoByID", err)
|
||||
} else {
|
||||
ctx.Flash.Error("DeleteReleaseByID: " + err.Error())
|
||||
redirect()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, isDelTag); err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
|
||||
} else {
|
||||
@@ -630,10 +651,5 @@ func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) {
|
||||
}
|
||||
}
|
||||
|
||||
if isDelTag {
|
||||
ctx.JSONRedirect(ctx.Repo.RepoLink + "/tags")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSONRedirect(ctx.Repo.RepoLink + "/releases")
|
||||
redirect()
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ func Create(ctx *context.Context) {
|
||||
ctx.Data["private"] = getRepoPrivate(ctx)
|
||||
ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
|
||||
ctx.Data["default_branch"] = setting.Repository.DefaultBranch
|
||||
ctx.Data["hash_type"] = "sha1"
|
||||
|
||||
ctxUser := checkContextUser(ctx, ctx.FormInt64("org"))
|
||||
if ctx.Written() {
|
||||
@@ -288,6 +289,7 @@ func CreatePost(ctx *context.Context) {
|
||||
AutoInit: form.AutoInit,
|
||||
IsTemplate: form.Template,
|
||||
TrustModel: repo_model.ToTrustModel(form.TrustModel),
|
||||
ObjectFormat: form.ObjectFormat,
|
||||
})
|
||||
if err == nil {
|
||||
log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
|
||||
@@ -606,7 +608,7 @@ func SearchRepo(ctx *context.Context) {
|
||||
}
|
||||
|
||||
// call the database O(1) times to get the commit statuses for all repos
|
||||
repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoIDsToLatestCommitSHAs, db.ListOptions{})
|
||||
repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoIDsToLatestCommitSHAs, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
log.Error("GetLatestCommitStatusForPairs: %v", err)
|
||||
return
|
||||
|
||||
@@ -388,20 +388,21 @@ func LFSFileFind(ctx *context.Context) {
|
||||
sha := ctx.FormString("sha")
|
||||
ctx.Data["Title"] = oid
|
||||
ctx.Data["PageIsSettingsLFS"] = true
|
||||
var hash git.SHA1
|
||||
objectFormat, _ := ctx.Repo.GitRepo.GetObjectFormat()
|
||||
var objectID git.ObjectID
|
||||
if len(sha) == 0 {
|
||||
pointer := lfs.Pointer{Oid: oid, Size: size}
|
||||
hash = git.ComputeBlobHash([]byte(pointer.StringContent()))
|
||||
sha = hash.String()
|
||||
objectID = git.ComputeBlobHash(objectFormat, []byte(pointer.StringContent()))
|
||||
sha = objectID.String()
|
||||
} else {
|
||||
hash = git.MustIDFromString(sha)
|
||||
objectID = objectFormat.MustIDFromString(sha)
|
||||
}
|
||||
ctx.Data["LFSFilesLink"] = ctx.Repo.RepoLink + "/settings/lfs"
|
||||
ctx.Data["Oid"] = oid
|
||||
ctx.Data["Size"] = size
|
||||
ctx.Data["SHA"] = sha
|
||||
|
||||
results, err := pipeline.FindLFSFile(ctx.Repo.GitRepo, hash)
|
||||
results, err := pipeline.FindLFSFile(ctx.Repo.GitRepo, objectID)
|
||||
if err != nil && err != io.EOF {
|
||||
log.Error("Failure in FindLFSFile: %v", err)
|
||||
ctx.ServerError("LFSFind: FindLFSFile.", err)
|
||||
|
||||
@@ -655,8 +655,14 @@ func TestWebhook(ctx *context.Context) {
|
||||
commit := ctx.Repo.Commit
|
||||
if commit == nil {
|
||||
ghost := user_model.NewGhostUser()
|
||||
objectFormat, err := git.GetObjectFormatOfRepo(ctx, ctx.Repo.Repository.RepoPath())
|
||||
if err != nil {
|
||||
ctx.Flash.Error("GetObjectFormatOfRepo: " + err.Error())
|
||||
ctx.Status(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
commit = &git.Commit{
|
||||
ID: git.MustIDFromString(git.EmptySHA),
|
||||
ID: objectFormat.NewEmptyID(),
|
||||
Author: ghost.NewGitSig(),
|
||||
Committer: ghost.NewGitSig(),
|
||||
CommitMessage: "This is a fake commit",
|
||||
|
||||
@@ -711,21 +711,14 @@ func checkCitationFile(ctx *context.Context, entry *git.TreeEntry) {
|
||||
}
|
||||
for _, entry := range allEntries {
|
||||
if entry.Name() == "CITATION.cff" || entry.Name() == "CITATION.bib" {
|
||||
ctx.Data["CitiationExist"] = true
|
||||
// Read Citation file contents
|
||||
blob := entry.Blob()
|
||||
dataRc, err := blob.DataAsync()
|
||||
if err != nil {
|
||||
ctx.ServerError("DataAsync", err)
|
||||
return
|
||||
if content, err := entry.Blob().GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
|
||||
log.Error("checkCitationFile: GetBlobContent: %v", err)
|
||||
} else {
|
||||
ctx.Data["CitiationExist"] = true
|
||||
ctx.PageData["citationFileContent"] = content
|
||||
break
|
||||
}
|
||||
defer dataRc.Close()
|
||||
ctx.PageData["citationFileContent"], err = blob.GetBlobContent(setting.UI.MaxDisplayFileSize)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBlobContent", err)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
-140
@@ -26,7 +26,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
@@ -213,13 +212,26 @@ func Milestones(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
counts, err := issues_model.CountMilestonesByRepoCondAndKw(ctx, userRepoCond, keyword, isShowClosed)
|
||||
counts, err := issues_model.CountMilestonesMap(ctx, issues_model.FindMilestoneOptions{
|
||||
RepoCond: userRepoCond,
|
||||
Name: keyword,
|
||||
IsClosed: util.OptionalBoolOf(isShowClosed),
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("CountMilestonesByRepoIDs", err)
|
||||
return
|
||||
}
|
||||
|
||||
milestones, err := issues_model.SearchMilestones(ctx, repoCond, page, isShowClosed, sortType, keyword)
|
||||
milestones, err := db.Find[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{
|
||||
ListOptions: db.ListOptions{
|
||||
Page: page,
|
||||
PageSize: setting.UI.IssuePagingNum,
|
||||
},
|
||||
RepoCond: repoCond,
|
||||
IsClosed: util.OptionalBoolOf(isShowClosed),
|
||||
SortType: sortType,
|
||||
Name: keyword,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("SearchMilestones", err)
|
||||
return
|
||||
@@ -335,7 +347,6 @@ func Pulls(ctx *context.Context) {
|
||||
|
||||
ctx.Data["Title"] = ctx.Tr("pull_requests")
|
||||
ctx.Data["PageIsPulls"] = true
|
||||
ctx.Data["SingleRepoAction"] = "pull"
|
||||
buildIssueOverview(ctx, unit.TypePullRequests)
|
||||
}
|
||||
|
||||
@@ -349,7 +360,6 @@ func Issues(ctx *context.Context) {
|
||||
|
||||
ctx.Data["Title"] = ctx.Tr("issues")
|
||||
ctx.Data["PageIsIssues"] = true
|
||||
ctx.Data["SingleRepoAction"] = "issue"
|
||||
buildIssueOverview(ctx, unit.TypeIssues)
|
||||
}
|
||||
|
||||
@@ -475,6 +485,13 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
opts.RepoIDs = []int64{0}
|
||||
}
|
||||
}
|
||||
if ctx.Doer.ID == ctxUser.ID && filterMode != issues_model.FilterModeYourRepositories {
|
||||
// If the doer is the same as the context user, which means the doer is viewing his own dashboard,
|
||||
// it's not enough to show the repos that the doer owns or has been explicitly granted access to,
|
||||
// because the doer may create issues or be mentioned in any public repo.
|
||||
// So we need search issues in all public repos.
|
||||
opts.AllPublic = true
|
||||
}
|
||||
|
||||
switch filterMode {
|
||||
case issues_model.FilterModeAll:
|
||||
@@ -499,14 +516,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
isShowClosed := ctx.FormString("state") == "closed"
|
||||
opts.IsClosed = util.OptionalBoolOf(isShowClosed)
|
||||
|
||||
// Filter repos and count issues in them. Count will be used later.
|
||||
// USING NON-FINAL STATE OF opts FOR A QUERY.
|
||||
issueCountByRepo, err := issue_indexer.CountIssuesByRepo(ctx, issue_indexer.ToSearchOptions(keyword, opts))
|
||||
if err != nil {
|
||||
ctx.ServerError("CountIssuesByRepo", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure page number is at least 1. Will be posted to ctx.Data.
|
||||
page := ctx.FormInt("page")
|
||||
if page <= 1 {
|
||||
@@ -531,17 +540,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
}
|
||||
opts.LabelIDs = labelIDs
|
||||
|
||||
// Parse ctx.FormString("repos") and remember matched repo IDs for later.
|
||||
// Gets set when clicking filters on the issues overview page.
|
||||
selectedRepoIDs := getRepoIDs(ctx.FormString("repos"))
|
||||
// Remove repo IDs that are not accessible to the user.
|
||||
selectedRepoIDs = slices.DeleteFunc(selectedRepoIDs, func(v int64) bool {
|
||||
return !accessibleRepos.Contains(v)
|
||||
})
|
||||
if len(selectedRepoIDs) > 0 {
|
||||
opts.RepoIDs = selectedRepoIDs
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Get issues as defined by opts.
|
||||
// ------------------------------
|
||||
@@ -562,41 +560,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// Add repository pointers to Issues.
|
||||
// ----------------------------------
|
||||
|
||||
// Remove repositories that should not be shown,
|
||||
// which are repositories that have no issues and are not selected by the user.
|
||||
selectedRepos := container.SetOf(selectedRepoIDs...)
|
||||
for k, v := range issueCountByRepo {
|
||||
if v == 0 && !selectedRepos.Contains(k) {
|
||||
delete(issueCountByRepo, k)
|
||||
}
|
||||
}
|
||||
|
||||
// showReposMap maps repository IDs to their Repository pointers.
|
||||
showReposMap, err := loadRepoByIDs(ctx, ctxUser, issueCountByRepo, unitType)
|
||||
if err != nil {
|
||||
if repo_model.IsErrRepoNotExist(err) {
|
||||
ctx.NotFound("GetRepositoryByID", err)
|
||||
return
|
||||
}
|
||||
ctx.ServerError("loadRepoByIDs", err)
|
||||
return
|
||||
}
|
||||
|
||||
// a RepositoryList
|
||||
showRepos := repo_model.RepositoryListOfMap(showReposMap)
|
||||
sort.Sort(showRepos)
|
||||
|
||||
// maps pull request IDs to their CommitStatus. Will be posted to ctx.Data.
|
||||
for _, issue := range issues {
|
||||
if issue.Repo == nil {
|
||||
issue.Repo = showReposMap[issue.RepoID]
|
||||
}
|
||||
}
|
||||
|
||||
commitStatuses, lastStatus, err := pull_service.GetIssuesAllCommitStatus(ctx, issues)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetIssuesLastCommitStatus", err)
|
||||
@@ -606,7 +569,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
// -------------------------------
|
||||
// Fill stats to post to ctx.Data.
|
||||
// -------------------------------
|
||||
issueStats, err := getUserIssueStats(ctx, filterMode, issue_indexer.ToSearchOptions(keyword, opts), ctx.Doer.ID)
|
||||
issueStats, err := getUserIssueStats(ctx, ctxUser, filterMode, issue_indexer.ToSearchOptions(keyword, opts))
|
||||
if err != nil {
|
||||
ctx.ServerError("getUserIssueStats", err)
|
||||
return
|
||||
@@ -619,25 +582,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
} else {
|
||||
shownIssues = int(issueStats.ClosedCount)
|
||||
}
|
||||
if len(opts.RepoIDs) != 0 {
|
||||
shownIssues = 0
|
||||
for _, repoID := range opts.RepoIDs {
|
||||
shownIssues += int(issueCountByRepo[repoID])
|
||||
}
|
||||
}
|
||||
|
||||
var allIssueCount int64
|
||||
for _, issueCount := range issueCountByRepo {
|
||||
allIssueCount += issueCount
|
||||
}
|
||||
ctx.Data["TotalIssueCount"] = allIssueCount
|
||||
|
||||
if len(opts.RepoIDs) == 1 {
|
||||
repo := showReposMap[opts.RepoIDs[0]]
|
||||
if repo != nil {
|
||||
ctx.Data["SingleRepoLink"] = repo.Link()
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Data["IsShowClosed"] = isShowClosed
|
||||
|
||||
@@ -674,12 +618,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
}
|
||||
ctx.Data["CommitLastStatus"] = lastStatus
|
||||
ctx.Data["CommitStatuses"] = commitStatuses
|
||||
ctx.Data["Repos"] = showRepos
|
||||
ctx.Data["Counts"] = issueCountByRepo
|
||||
ctx.Data["IssueStats"] = issueStats
|
||||
ctx.Data["ViewType"] = viewType
|
||||
ctx.Data["SortType"] = sortType
|
||||
ctx.Data["RepoIDs"] = selectedRepoIDs
|
||||
ctx.Data["IsShowClosed"] = isShowClosed
|
||||
ctx.Data["SelectLabels"] = selectedLabels
|
||||
|
||||
@@ -689,15 +630,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
ctx.Data["State"] = "open"
|
||||
}
|
||||
|
||||
// Convert []int64 to string
|
||||
reposParam, _ := json.Marshal(opts.RepoIDs)
|
||||
|
||||
ctx.Data["ReposParam"] = string(reposParam)
|
||||
|
||||
pager := context.NewPagination(shownIssues, setting.UI.IssuePagingNum, page, 5)
|
||||
pager.AddParam(ctx, "q", "Keyword")
|
||||
pager.AddParam(ctx, "type", "ViewType")
|
||||
pager.AddParam(ctx, "repos", "ReposParam")
|
||||
pager.AddParam(ctx, "sort", "SortType")
|
||||
pager.AddParam(ctx, "state", "State")
|
||||
pager.AddParam(ctx, "labels", "SelectLabels")
|
||||
@@ -708,55 +643,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
ctx.HTML(http.StatusOK, tplIssues)
|
||||
}
|
||||
|
||||
func getRepoIDs(reposQuery string) []int64 {
|
||||
if len(reposQuery) == 0 || reposQuery == "[]" {
|
||||
return []int64{}
|
||||
}
|
||||
if !issueReposQueryPattern.MatchString(reposQuery) {
|
||||
log.Warn("issueReposQueryPattern does not match query: %q", reposQuery)
|
||||
return []int64{}
|
||||
}
|
||||
|
||||
var repoIDs []int64
|
||||
// remove "[" and "]" from string
|
||||
reposQuery = reposQuery[1 : len(reposQuery)-1]
|
||||
// for each ID (delimiter ",") add to int to repoIDs
|
||||
for _, rID := range strings.Split(reposQuery, ",") {
|
||||
// Ensure nonempty string entries
|
||||
if rID != "" && rID != "0" {
|
||||
rIDint64, err := strconv.ParseInt(rID, 10, 64)
|
||||
if err == nil {
|
||||
repoIDs = append(repoIDs, rIDint64)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return repoIDs
|
||||
}
|
||||
|
||||
func loadRepoByIDs(ctx *context.Context, ctxUser *user_model.User, issueCountByRepo map[int64]int64, unitType unit.Type) (map[int64]*repo_model.Repository, error) {
|
||||
totalRes := make(map[int64]*repo_model.Repository, len(issueCountByRepo))
|
||||
repoIDs := make([]int64, 0, 500)
|
||||
for id := range issueCountByRepo {
|
||||
if id <= 0 {
|
||||
continue
|
||||
}
|
||||
repoIDs = append(repoIDs, id)
|
||||
if len(repoIDs) == 500 {
|
||||
if err := repo_model.FindReposMapByIDs(ctx, repoIDs, totalRes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repoIDs = repoIDs[:0]
|
||||
}
|
||||
}
|
||||
if len(repoIDs) > 0 {
|
||||
if err := repo_model.FindReposMapByIDs(ctx, repoIDs, totalRes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return totalRes, nil
|
||||
}
|
||||
|
||||
// ShowSSHKeys output all the ssh keys of user by uid
|
||||
func ShowSSHKeys(ctx *context.Context) {
|
||||
keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{
|
||||
@@ -870,8 +756,15 @@ func UsernameSubRoute(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func getUserIssueStats(ctx *context.Context, filterMode int, opts *issue_indexer.SearchOptions, doerID int64) (*issues_model.IssueStats, error) {
|
||||
func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMode int, opts *issue_indexer.SearchOptions) (*issues_model.IssueStats, error) {
|
||||
doerID := ctx.Doer.ID
|
||||
|
||||
opts = opts.Copy(func(o *issue_indexer.SearchOptions) {
|
||||
// If the doer is the same as the context user, which means the doer is viewing his own dashboard,
|
||||
// it's not enough to show the repos that the doer owns or has been explicitly granted access to,
|
||||
// because the doer may create issues or be mentioned in any public repo.
|
||||
// So we need search issues in all public repos.
|
||||
o.AllPublic = doerID == ctxUser.ID
|
||||
o.AssigneeID = nil
|
||||
o.PosterID = nil
|
||||
o.MentionID = nil
|
||||
@@ -887,7 +780,10 @@ func getUserIssueStats(ctx *context.Context, filterMode int, opts *issue_indexer
|
||||
{
|
||||
openClosedOpts := opts.Copy()
|
||||
switch filterMode {
|
||||
case issues_model.FilterModeAll, issues_model.FilterModeYourRepositories:
|
||||
case issues_model.FilterModeAll:
|
||||
// no-op
|
||||
case issues_model.FilterModeYourRepositories:
|
||||
openClosedOpts.AllPublic = false
|
||||
case issues_model.FilterModeAssign:
|
||||
openClosedOpts.AssigneeID = &doerID
|
||||
case issues_model.FilterModeCreate:
|
||||
@@ -911,7 +807,7 @@ func getUserIssueStats(ctx *context.Context, filterMode int, opts *issue_indexer
|
||||
}
|
||||
}
|
||||
|
||||
ret.YourRepositoriesCount, err = issue_indexer.CountIssues(ctx, opts)
|
||||
ret.YourRepositoriesCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AllPublic = false }))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -45,9 +45,7 @@ func TestArchivedIssues(t *testing.T) {
|
||||
// Assert: One Issue (ID 30) from one Repo (ID 50) is retrieved, while nothing from archived Repo 51 is retrieved
|
||||
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
|
||||
|
||||
assert.EqualValues(t, map[int64]int64{50: 1}, ctx.Data["Counts"])
|
||||
assert.Len(t, ctx.Data["Issues"], 1)
|
||||
assert.Len(t, ctx.Data["Repos"], 1)
|
||||
}
|
||||
|
||||
func TestIssues(t *testing.T) {
|
||||
@@ -60,10 +58,8 @@ func TestIssues(t *testing.T) {
|
||||
Issues(ctx)
|
||||
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
|
||||
|
||||
assert.EqualValues(t, map[int64]int64{1: 1, 2: 1}, ctx.Data["Counts"])
|
||||
assert.EqualValues(t, true, ctx.Data["IsShowClosed"])
|
||||
assert.Len(t, ctx.Data["Issues"], 1)
|
||||
assert.Len(t, ctx.Data["Repos"], 2)
|
||||
}
|
||||
|
||||
func TestPulls(t *testing.T) {
|
||||
|
||||
+20
-2
@@ -796,6 +796,24 @@ func registerRoutes(m *web.Route) {
|
||||
}
|
||||
}
|
||||
|
||||
individualPermsChecker := func(ctx *context.Context) {
|
||||
// org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked.
|
||||
if ctx.ContextUser.IsIndividual() {
|
||||
switch {
|
||||
case ctx.ContextUser.Visibility == structs.VisibleTypePrivate:
|
||||
if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) {
|
||||
ctx.NotFound("Visit Project", nil)
|
||||
return
|
||||
}
|
||||
case ctx.ContextUser.Visibility == structs.VisibleTypeLimited:
|
||||
if ctx.Doer == nil {
|
||||
ctx.NotFound("Visit Project", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ***** START: Organization *****
|
||||
m.Group("/org", func() {
|
||||
m.Group("/{org}", func() {
|
||||
@@ -976,11 +994,11 @@ func registerRoutes(m *web.Route) {
|
||||
return
|
||||
}
|
||||
})
|
||||
})
|
||||
}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true), individualPermsChecker)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Get("/code", user.CodeSearch)
|
||||
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false))
|
||||
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker)
|
||||
}, ignSignIn, context_service.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code)
|
||||
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
|
||||
Reference in New Issue
Block a user