Merge branch 'rebase-forgejo-dependency' into wip-forgejo
This commit is contained in:
commit
094c84ed6d
292 changed files with 8842 additions and 1269 deletions
|
@ -8,6 +8,7 @@ import (
|
|||
"crypto"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
|
@ -26,8 +27,6 @@ import (
|
|||
chef_module "code.gitea.io/gitea/modules/packages/chef"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/auth"
|
||||
|
||||
"github.com/minio/sha256-simd"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -6,6 +6,7 @@ package maven
|
|||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
|
@ -26,8 +27,6 @@ import (
|
|||
maven_module "code.gitea.io/gitea/modules/packages/maven"
|
||||
"code.gitea.io/gitea/routers/api/packages/helper"
|
||||
packages_service "code.gitea.io/gitea/services/packages"
|
||||
|
||||
"github.com/minio/sha256-simd"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
// This documentation describes the Gitea API.
|
||||
//
|
||||
// Schemes: http, https
|
||||
// Schemes: https, http
|
||||
// BasePath: /api/v1
|
||||
// Version: {{AppVer | JSEscape | Safe}}
|
||||
// License: MIT http://opensource.org/licenses/MIT
|
||||
|
@ -73,6 +73,7 @@ import (
|
|||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
|
@ -230,6 +231,39 @@ func repoAssignment() func(ctx *context.APIContext) {
|
|||
}
|
||||
}
|
||||
|
||||
// must be used within a group with a call to repoAssignment() to set ctx.Repo
|
||||
func commentAssignment(idParam string) func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64(idParam))
|
||||
if err != nil {
|
||||
if issues_model.IsErrCommentNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err = comment.LoadIssue(ctx); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
if comment.Issue == nil || comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
comment.Issue.Repo = ctx.Repo.Repository
|
||||
|
||||
ctx.Comment = comment
|
||||
}
|
||||
}
|
||||
|
||||
func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if ctx.Package.AccessMode < accessMode && !ctx.IsUserSiteAdmin() {
|
||||
|
@ -1104,6 +1138,18 @@ func Routes() *web.Route {
|
|||
m.Get("/permission", repo.GetRepoPermissions)
|
||||
})
|
||||
}, reqToken())
|
||||
if setting.Repository.EnableFlags {
|
||||
m.Group("/flags", func() {
|
||||
m.Combo("").Get(repo.ListFlags).
|
||||
Put(bind(api.ReplaceFlagsOption{}), repo.ReplaceAllFlags).
|
||||
Delete(repo.DeleteAllFlags)
|
||||
m.Group("/{flag}", func() {
|
||||
m.Combo("").Get(repo.HasFlag).
|
||||
Put(repo.AddFlag).
|
||||
Delete(repo.DeleteFlag)
|
||||
})
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryAdmin), reqToken(), reqSiteAdmin())
|
||||
}
|
||||
m.Get("/assignees", reqToken(), reqAnyRepoReader(), repo.GetAssignees)
|
||||
m.Get("/reviewers", reqToken(), reqAnyRepoReader(), repo.GetReviewers)
|
||||
m.Group("/teams", func() {
|
||||
|
@ -1223,8 +1269,16 @@ func Routes() *web.Route {
|
|||
Get(repo.GetPullReview).
|
||||
Delete(reqToken(), repo.DeletePullReview).
|
||||
Post(reqToken(), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview)
|
||||
m.Combo("/comments").
|
||||
Get(repo.GetPullReviewComments)
|
||||
m.Group("/comments", func() {
|
||||
m.Combo("").
|
||||
Get(repo.GetPullReviewComments).
|
||||
Post(reqToken(), bind(api.CreatePullReviewCommentOptions{}), repo.CreatePullReviewComment)
|
||||
m.Group("/{comment}", func() {
|
||||
m.Combo("").
|
||||
Get(repo.GetPullReviewComment).
|
||||
Delete(reqToken(), repo.DeletePullReviewComment)
|
||||
}, commentAssignment("comment"))
|
||||
})
|
||||
m.Post("/dismissals", reqToken(), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview)
|
||||
m.Post("/undismissals", reqToken(), repo.UnDismissPullReview)
|
||||
})
|
||||
|
@ -1328,7 +1382,7 @@ func Routes() *web.Route {
|
|||
Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment).
|
||||
Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueCommentAttachment)
|
||||
}, mustEnableAttachments)
|
||||
})
|
||||
}, commentAssignment(":id"))
|
||||
})
|
||||
m.Group("/{index}", func() {
|
||||
m.Combo("").Get(repo.GetIssue).
|
||||
|
|
|
@ -262,7 +262,9 @@ func GetArchive(ctx *context.APIContext) {
|
|||
// ---
|
||||
// summary: Get an archive of a repository
|
||||
// produces:
|
||||
// - application/json
|
||||
// - application/octet-stream
|
||||
// - application/zip
|
||||
// - application/gzip
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
|
@ -342,7 +344,17 @@ func download(ctx *context.APIContext, archiveName string, archiver *repo_model.
|
|||
}
|
||||
defer fr.Close()
|
||||
|
||||
contentType := ""
|
||||
switch archiver.Type {
|
||||
case git.ZIP:
|
||||
contentType = "application/zip"
|
||||
case git.TARGZ:
|
||||
// Per RFC6713.
|
||||
contentType = "application/gzip"
|
||||
}
|
||||
|
||||
ctx.ServeContent(fr, &context.ServeHeaderOptions{
|
||||
ContentType: contentType,
|
||||
Filename: downloadName,
|
||||
LastModified: archiver.CreatedUnix.AsLocalTime(),
|
||||
})
|
||||
|
|
245
routers/api/v1/repo/flags.go
Normal file
245
routers/api/v1/repo/flags.go
Normal file
|
@ -0,0 +1,245 @@
|
|||
// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
)
|
||||
|
||||
func ListFlags(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/flags repository repoListFlags
|
||||
// ---
|
||||
// summary: List a repository's flags
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/StringSlice"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
repoFlags, err := ctx.Repo.Repository.ListFlags(ctx)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
flags := make([]string, len(repoFlags))
|
||||
for i := range repoFlags {
|
||||
flags[i] = repoFlags[i].Name
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(int64(len(repoFlags)))
|
||||
ctx.JSON(http.StatusOK, flags)
|
||||
}
|
||||
|
||||
func ReplaceAllFlags(ctx *context.APIContext) {
|
||||
// swagger:operation PUT /repos/{owner}/{repo}/flags repository repoReplaceAllFlags
|
||||
// ---
|
||||
// summary: Replace all flags of a repository
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/ReplaceFlagsOption"
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
flagsForm := web.GetForm(ctx).(*api.ReplaceFlagsOption)
|
||||
|
||||
if err := ctx.Repo.Repository.ReplaceAllFlags(ctx, flagsForm.Flags); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func DeleteAllFlags(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/flags repository repoDeleteAllFlags
|
||||
// ---
|
||||
// summary: Remove all flags from a repository
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
if err := ctx.Repo.Repository.ReplaceAllFlags(ctx, nil); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func HasFlag(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/flags/{flag} repository repoCheckFlag
|
||||
// ---
|
||||
// summary: Check if a repository has a given flag
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: flag
|
||||
// in: path
|
||||
// description: name of the flag
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
hasFlag := ctx.Repo.Repository.HasFlag(ctx, ctx.Params(":flag"))
|
||||
if hasFlag {
|
||||
ctx.Status(http.StatusNoContent)
|
||||
} else {
|
||||
ctx.NotFound()
|
||||
}
|
||||
}
|
||||
|
||||
func AddFlag(ctx *context.APIContext) {
|
||||
// swagger:operation PUT /repos/{owner}/{repo}/flags/{flag} repository repoAddFlag
|
||||
// ---
|
||||
// summary: Add a flag to a repository
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: flag
|
||||
// in: path
|
||||
// description: name of the flag
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
flag := ctx.Params(":flag")
|
||||
|
||||
if ctx.Repo.Repository.HasFlag(ctx, flag) {
|
||||
ctx.Status(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
if err := ctx.Repo.Repository.AddFlag(ctx, flag); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func DeleteFlag(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/flags/{flag} repository repoDeleteFlag
|
||||
// ---
|
||||
// summary: Remove a flag from a repository
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: flag
|
||||
// in: path
|
||||
// description: name of the flag
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
flag := ctx.Params(":flag")
|
||||
|
||||
if _, err := ctx.Repo.Repository.DeleteFlag(ctx, flag); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
|
@ -454,29 +454,7 @@ func GetIssueComment(ctx *context.APIContext) {
|
|||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrCommentNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err = comment.LoadIssue(ctx); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
comment := ctx.Comment
|
||||
|
||||
if comment.Type != issues_model.CommentTypeComment {
|
||||
ctx.Status(http.StatusNoContent)
|
||||
|
@ -587,25 +565,7 @@ func EditIssueCommentDeprecated(ctx *context.APIContext) {
|
|||
}
|
||||
|
||||
func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) {
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrCommentNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
comment := ctx.Comment
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
|
@ -617,7 +577,7 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
|
|||
return
|
||||
}
|
||||
|
||||
err = comment.LoadIssue(ctx)
|
||||
err := comment.LoadIssue(ctx)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
|
@ -668,7 +628,7 @@ func DeleteIssueComment(ctx *context.APIContext) {
|
|||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
deleteIssueComment(ctx)
|
||||
deleteIssueComment(ctx, issues_model.CommentTypeComment)
|
||||
}
|
||||
|
||||
// DeleteIssueCommentDeprecated delete a comment from an issue
|
||||
|
@ -707,39 +667,21 @@ func DeleteIssueCommentDeprecated(ctx *context.APIContext) {
|
|||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
deleteIssueComment(ctx)
|
||||
deleteIssueComment(ctx, issues_model.CommentTypeComment)
|
||||
}
|
||||
|
||||
func deleteIssueComment(ctx *context.APIContext) {
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrCommentNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
func deleteIssueComment(ctx *context.APIContext, commentType issues_model.CommentType) {
|
||||
comment := ctx.Comment
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return
|
||||
} else if comment.Type != issues_model.CommentTypeComment {
|
||||
} else if comment.Type != commentType {
|
||||
ctx.Status(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
if err = issue_service.DeleteComment(ctx, ctx.Doer, comment); err != nil {
|
||||
if err := issue_service.DeleteComment(ctx, ctx.Doer, comment); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteCommentByID", err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -55,11 +55,8 @@ func GetIssueCommentAttachment(ctx *context.APIContext) {
|
|||
// "404":
|
||||
// "$ref": "#/responses/error"
|
||||
|
||||
comment := getIssueCommentSafe(ctx)
|
||||
if comment == nil {
|
||||
return
|
||||
}
|
||||
attachment := getIssueCommentAttachmentSafeRead(ctx, comment)
|
||||
comment := ctx.Comment
|
||||
attachment := getIssueCommentAttachmentSafeRead(ctx)
|
||||
if attachment == nil {
|
||||
return
|
||||
}
|
||||
|
@ -101,10 +98,7 @@ func ListIssueCommentAttachments(ctx *context.APIContext) {
|
|||
// "$ref": "#/responses/AttachmentList"
|
||||
// "404":
|
||||
// "$ref": "#/responses/error"
|
||||
comment := getIssueCommentSafe(ctx)
|
||||
if comment == nil {
|
||||
return
|
||||
}
|
||||
comment := ctx.Comment
|
||||
|
||||
if err := comment.LoadAttachments(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttachments", err)
|
||||
|
@ -166,14 +160,12 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
|
|||
// "$ref": "#/responses/repoArchivedError"
|
||||
|
||||
// Check if comment exists and load comment
|
||||
comment := getIssueCommentSafe(ctx)
|
||||
if comment == nil {
|
||||
|
||||
if !canUserWriteIssueCommentAttachment(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
if !canUserWriteIssueCommentAttachment(ctx, comment) {
|
||||
return
|
||||
}
|
||||
comment := ctx.Comment
|
||||
|
||||
updatedAt := ctx.Req.FormValue("updated_at")
|
||||
if len(updatedAt) != 0 {
|
||||
|
@ -341,42 +333,17 @@ func DeleteIssueCommentAttachment(ctx *context.APIContext) {
|
|||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func getIssueCommentSafe(ctx *context.APIContext) *issues_model.Comment {
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64("id"))
|
||||
if err != nil {
|
||||
ctx.NotFoundOrServerError("GetCommentByID", issues_model.IsErrCommentNotExist, err)
|
||||
return nil
|
||||
}
|
||||
if err := comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "comment.LoadIssue", err)
|
||||
return nil
|
||||
}
|
||||
if comment.Issue == nil || comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.Error(http.StatusNotFound, "", "no matching issue comment found")
|
||||
return nil
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
return nil
|
||||
}
|
||||
|
||||
comment.Issue.Repo = ctx.Repo.Repository
|
||||
|
||||
return comment
|
||||
}
|
||||
|
||||
func getIssueCommentAttachmentSafeWrite(ctx *context.APIContext) *repo_model.Attachment {
|
||||
comment := getIssueCommentSafe(ctx)
|
||||
if comment == nil {
|
||||
if !canUserWriteIssueCommentAttachment(ctx) {
|
||||
return nil
|
||||
}
|
||||
if !canUserWriteIssueCommentAttachment(ctx, comment) {
|
||||
return nil
|
||||
}
|
||||
return getIssueCommentAttachmentSafeRead(ctx, comment)
|
||||
return getIssueCommentAttachmentSafeRead(ctx)
|
||||
}
|
||||
|
||||
func canUserWriteIssueCommentAttachment(ctx *context.APIContext, comment *issues_model.Comment) bool {
|
||||
func canUserWriteIssueCommentAttachment(ctx *context.APIContext) bool {
|
||||
// ctx.Comment is assumed to be set in a safe way via a middleware
|
||||
comment := ctx.Comment
|
||||
|
||||
canEditComment := ctx.IsSigned && (ctx.Doer.ID == comment.PosterID || ctx.IsUserRepoAdmin() || ctx.IsUserSiteAdmin()) && ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)
|
||||
if !canEditComment {
|
||||
ctx.Error(http.StatusForbidden, "", "user should have permission to edit comment")
|
||||
|
@ -386,7 +353,10 @@ func canUserWriteIssueCommentAttachment(ctx *context.APIContext, comment *issues
|
|||
return true
|
||||
}
|
||||
|
||||
func getIssueCommentAttachmentSafeRead(ctx *context.APIContext, comment *issues_model.Comment) *repo_model.Attachment {
|
||||
func getIssueCommentAttachmentSafeRead(ctx *context.APIContext) *repo_model.Attachment {
|
||||
// ctx.Comment is assumed to be set in a safe way via a middleware
|
||||
comment := ctx.Comment
|
||||
|
||||
attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("attachment_id"))
|
||||
if err != nil {
|
||||
ctx.NotFoundOrServerError("GetAttachmentByID", repo_model.IsErrAttachmentNotExist, err)
|
||||
|
|
|
@ -51,30 +51,7 @@ func GetIssueCommentReactions(ctx *context.APIContext) {
|
|||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrCommentNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "comment.LoadIssue", err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
ctx.Error(http.StatusForbidden, "GetIssueCommentReactions", errors.New("no permission to get reactions"))
|
||||
return
|
||||
}
|
||||
comment := ctx.Comment
|
||||
|
||||
reactions, _, err := issues_model.FindCommentReactions(ctx, comment.IssueID, comment.ID)
|
||||
if err != nil {
|
||||
|
@ -188,30 +165,7 @@ func DeleteIssueCommentReaction(ctx *context.APIContext) {
|
|||
}
|
||||
|
||||
func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) {
|
||||
comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrCommentNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err = comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "comment.LoadIssue() failed", err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
comment := ctx.Comment
|
||||
|
||||
if comment.Issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull) {
|
||||
ctx.Error(http.StatusForbidden, "ChangeIssueCommentReaction", errors.New("no permission to change reaction"))
|
||||
|
@ -243,7 +197,7 @@ func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp
|
|||
})
|
||||
} else {
|
||||
// DeleteIssueCommentReaction part
|
||||
err = issues_model.DeleteCommentReaction(ctx, ctx.Doer.ID, comment.Issue.ID, comment.ID, form.Reaction)
|
||||
err := issues_model.DeleteCommentReaction(ctx, ctx.Doer.ID, comment.Issue.ID, comment.ID, form.Reaction)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteCommentReaction", err)
|
||||
return
|
||||
|
|
|
@ -208,6 +208,152 @@ func GetPullReviewComments(ctx *context.APIContext) {
|
|||
ctx.JSON(http.StatusOK, apiComments)
|
||||
}
|
||||
|
||||
// GetPullReviewComment get a pull review comment
|
||||
func GetPullReviewComment(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments/{comment} repository repoGetPullReviewComment
|
||||
// ---
|
||||
// summary: Get a pull review comment
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: index
|
||||
// in: path
|
||||
// description: index of the pull request
|
||||
// type: integer
|
||||
// format: int64
|
||||
// required: true
|
||||
// - name: id
|
||||
// in: path
|
||||
// description: id of the review
|
||||
// type: integer
|
||||
// format: int64
|
||||
// required: true
|
||||
// - name: comment
|
||||
// in: path
|
||||
// description: id of the comment
|
||||
// type: integer
|
||||
// format: int64
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/PullReviewComment"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
review, _, statusSet := prepareSingleReview(ctx)
|
||||
if statusSet {
|
||||
return
|
||||
}
|
||||
|
||||
if err := ctx.Comment.LoadPoster(ctx); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
apiComment, err := convert.ToPullReviewComment(ctx, review, ctx.Comment, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, apiComment)
|
||||
}
|
||||
|
||||
// CreatePullReviewComments add a new comment to a pull request review
|
||||
func CreatePullReviewComment(ctx *context.APIContext) {
|
||||
// swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments repository repoCreatePullReviewComment
|
||||
// ---
|
||||
// summary: Add a new comment to a pull request review
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: index
|
||||
// in: path
|
||||
// description: index of the pull request
|
||||
// type: integer
|
||||
// format: int64
|
||||
// required: true
|
||||
// - name: id
|
||||
// in: path
|
||||
// description: id of the review
|
||||
// type: integer
|
||||
// format: int64
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/CreatePullReviewCommentOptions"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/PullReviewComment"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
|
||||
opts := web.GetForm(ctx).(*api.CreatePullReviewCommentOptions)
|
||||
|
||||
review, pr, statusSet := prepareSingleReview(ctx)
|
||||
if statusSet {
|
||||
return
|
||||
}
|
||||
|
||||
if err := pr.Issue.LoadRepo(ctx); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
line := opts.NewLineNum
|
||||
if opts.OldLineNum > 0 {
|
||||
line = opts.OldLineNum * -1
|
||||
}
|
||||
|
||||
comment, err := pull_service.CreateCodeCommentKnownReviewID(ctx,
|
||||
ctx.Doer,
|
||||
pr.Issue.Repo,
|
||||
pr.Issue,
|
||||
opts.Body,
|
||||
opts.Path,
|
||||
line,
|
||||
review.ID,
|
||||
)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
apiComment, err := convert.ToPullReviewComment(ctx, review, comment, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, apiComment)
|
||||
}
|
||||
|
||||
// DeletePullReview delete a specific review from a pull request
|
||||
func DeletePullReview(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/pulls/{index}/reviews/{id} repository repoDeletePullReview
|
||||
|
@ -868,6 +1014,53 @@ func UnDismissPullReview(ctx *context.APIContext) {
|
|||
dismissReview(ctx, "", false, false)
|
||||
}
|
||||
|
||||
// DeletePullReviewComment delete a pull review comment
|
||||
func DeletePullReviewComment(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments/{comment} repository repoDeletePullReviewComment
|
||||
// ---
|
||||
// summary: Delete a pull review comment
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: index
|
||||
// in: path
|
||||
// description: index of the pull request
|
||||
// type: integer
|
||||
// format: int64
|
||||
// required: true
|
||||
// - name: id
|
||||
// in: path
|
||||
// description: id of the review
|
||||
// type: integer
|
||||
// format: int64
|
||||
// required: true
|
||||
// - name: comment
|
||||
// in: path
|
||||
// description: id of the comment
|
||||
// type: integer
|
||||
// format: int64
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
deleteIssueComment(ctx, issues_model.CommentTypeCode)
|
||||
}
|
||||
|
||||
func dismissReview(ctx *context.APIContext, msg string, isDismiss, dismissPriors bool) {
|
||||
if !ctx.Repo.IsAdmin() {
|
||||
ctx.Error(http.StatusForbidden, "", "Must be repo admin")
|
||||
|
|
|
@ -34,6 +34,7 @@ import (
|
|||
"code.gitea.io/gitea/services/convert"
|
||||
"code.gitea.io/gitea/services/issue"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
wiki_service "code.gitea.io/gitea/services/wiki"
|
||||
)
|
||||
|
||||
// Search repositories via options
|
||||
|
@ -740,6 +741,18 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
|
|||
repo.DefaultBranch = *opts.DefaultBranch
|
||||
}
|
||||
|
||||
// Wiki branch is updated if changed
|
||||
if opts.WikiBranch != nil && repo.WikiBranch != *opts.WikiBranch {
|
||||
if err := wiki_service.NormalizeWikiBranch(ctx, repo, *opts.WikiBranch); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "NormalizeWikiBranch", err)
|
||||
return err
|
||||
}
|
||||
// While NormalizeWikiBranch updates the db, we need to update *this*
|
||||
// instance of `repo`, so that the `UpdateRepository` below will not
|
||||
// reset the branch back.
|
||||
repo.WikiBranch = *opts.WikiBranch
|
||||
}
|
||||
|
||||
if err := repo_service.UpdateRepository(ctx, repo, visibilityChanged); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
|
||||
return err
|
||||
|
@ -984,7 +997,7 @@ 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 {
|
||||
if err := repo_model.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateRepositoryUnits", err)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -176,7 +176,7 @@ func CreateTag(ctx *context.APIContext) {
|
|||
// schema:
|
||||
// "$ref": "#/definitions/CreateTagOption"
|
||||
// responses:
|
||||
// "200":
|
||||
// "201":
|
||||
// "$ref": "#/responses/Tag"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
|
|
@ -203,7 +203,7 @@ func getWikiPage(ctx *context.APIContext, wikiName wiki_service.WebPath) *api.Wi
|
|||
}
|
||||
|
||||
return &api.WikiPage{
|
||||
WikiPageMetaData: wiki_service.ToWikiPageMetaData(wikiName, lastCommit, ctx.Repo.Repository),
|
||||
WikiPageMetaData: convert.ToWikiPageMetaData(wikiName, lastCommit, ctx.Repo.Repository),
|
||||
ContentBase64: content,
|
||||
CommitCount: commitsCount,
|
||||
Sidebar: sidebarContent,
|
||||
|
@ -333,7 +333,7 @@ func ListWikiPages(ctx *context.APIContext) {
|
|||
ctx.Error(http.StatusInternalServerError, "WikiFilenameToName", err)
|
||||
return
|
||||
}
|
||||
pages = append(pages, wiki_service.ToWikiPageMetaData(wikiName, c, ctx.Repo.Repository))
|
||||
pages = append(pages, convert.ToWikiPageMetaData(wikiName, c, ctx.Repo.Repository))
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(int64(len(entries)))
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// RegistrationToken is response related to registeration token
|
||||
// RegistrationToken is a string used to register a runner with a server
|
||||
// swagger:response RegistrationToken
|
||||
type RegistrationToken struct {
|
||||
Token string `json:"token"`
|
||||
|
|
|
@ -17,6 +17,9 @@ type swaggerParameterBodies struct {
|
|||
// in:body
|
||||
AddCollaboratorOption api.AddCollaboratorOption
|
||||
|
||||
// in:body
|
||||
ReplaceFlagsOption api.ReplaceFlagsOption
|
||||
|
||||
// in:body
|
||||
CreateEmailOption api.CreateEmailOption
|
||||
// in:body
|
||||
|
@ -158,6 +161,9 @@ type swaggerParameterBodies struct {
|
|||
// in:body
|
||||
CreatePullReviewComment api.CreatePullReviewComment
|
||||
|
||||
// in:body
|
||||
CreatePullReviewCommentOptions api.CreatePullReviewCommentOptions
|
||||
|
||||
// in:body
|
||||
SubmitPullReviewOptions api.SubmitPullReviewOptions
|
||||
|
||||
|
|
|
@ -244,7 +244,7 @@ func CreateOauth2Application(ctx *context.APIContext) {
|
|||
|
||||
// ListOauth2Applications list all the Oauth2 application
|
||||
func ListOauth2Applications(ctx *context.APIContext) {
|
||||
// swagger:operation GET /user/applications/oauth2 user userGetOauth2Application
|
||||
// swagger:operation GET /user/applications/oauth2 user userGetOAuth2Applications
|
||||
// ---
|
||||
// summary: List the authenticated user's oauth2 applications
|
||||
// produces:
|
||||
|
|
|
@ -358,6 +358,12 @@ func SubmitInstall(ctx *context.Context) {
|
|||
ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplInstall, form)
|
||||
return
|
||||
}
|
||||
if len(form.AdminPasswd) < setting.MinPasswordLength {
|
||||
ctx.Data["Err_Admin"] = true
|
||||
ctx.Data["Err_AdminPasswd"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplInstall, form)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Init the engine with migration
|
||||
|
@ -407,7 +413,7 @@ func SubmitInstall(ctx *context.Context) {
|
|||
cfg.Section("server").Key("LFS_START_SERVER").SetValue("true")
|
||||
cfg.Section("lfs").Key("PATH").SetValue(form.LFSRootPath)
|
||||
var lfsJwtSecret string
|
||||
if _, lfsJwtSecret, err = generate.NewJwtSecretBase64(); err != nil {
|
||||
if _, lfsJwtSecret, err = generate.NewJwtSecret(); err != nil {
|
||||
ctx.RenderWithErr(ctx.Tr("install.lfs_jwt_secret_failed", err), tplInstall, &form)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"code.gitea.io/gitea/services/externalaccount"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
"code.gitea.io/gitea/services/mailer"
|
||||
notify_service "code.gitea.io/gitea/services/notify"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
|
||||
"github.com/markbates/goth"
|
||||
|
@ -606,6 +607,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
|
|||
}
|
||||
}
|
||||
|
||||
notify_service.NewUserSignUp(ctx, u)
|
||||
// update external user information
|
||||
if gothUser != nil {
|
||||
if err := externalaccount.UpdateExternalUser(ctx, u, *gothUser); err != nil {
|
||||
|
@ -651,13 +653,22 @@ func Activate(ctx *context.Context) {
|
|||
}
|
||||
// Resend confirmation email.
|
||||
if setting.Service.RegisterEmailConfirm {
|
||||
if ctx.Cache.IsExist("MailResendLimit_" + ctx.Doer.LowerName) {
|
||||
var cacheKey string
|
||||
if ctx.Cache.IsExist("MailChangedJustNow_" + ctx.Doer.LowerName) {
|
||||
cacheKey = "MailChangedLimit_"
|
||||
if err := ctx.Cache.Delete("MailChangedJustNow_" + ctx.Doer.LowerName); err != nil {
|
||||
log.Error("Delete cache(MailChangedJustNow) fail: %v", err)
|
||||
}
|
||||
} else {
|
||||
cacheKey = "MailResendLimit_"
|
||||
}
|
||||
if ctx.Cache.IsExist(cacheKey + ctx.Doer.LowerName) {
|
||||
ctx.Data["ResendLimited"] = true
|
||||
} else {
|
||||
ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
|
||||
mailer.SendActivateAccountMail(ctx.Locale, ctx.Doer)
|
||||
|
||||
if err := ctx.Cache.Put("MailResendLimit_"+ctx.Doer.LowerName, ctx.Doer.LowerName, 180); err != nil {
|
||||
if err := ctx.Cache.Put(cacheKey+ctx.Doer.LowerName, ctx.Doer.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailResendLimit) fail: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -691,6 +702,43 @@ func Activate(ctx *context.Context) {
|
|||
func ActivatePost(ctx *context.Context) {
|
||||
code := ctx.FormString("code")
|
||||
if len(code) == 0 {
|
||||
email := ctx.FormString("email")
|
||||
if len(email) > 0 {
|
||||
ctx.Data["IsActivatePage"] = true
|
||||
if ctx.Doer == nil || ctx.Doer.IsActive {
|
||||
ctx.NotFound("invalid user", nil)
|
||||
return
|
||||
}
|
||||
// Change the primary email
|
||||
if setting.Service.RegisterEmailConfirm {
|
||||
if ctx.Cache.IsExist("MailChangeLimit_" + ctx.Doer.LowerName) {
|
||||
ctx.Data["ResendLimited"] = true
|
||||
} else {
|
||||
ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
|
||||
err := user_service.ReplaceInactivePrimaryEmail(ctx, ctx.Doer.Email, &user_model.EmailAddress{
|
||||
UID: ctx.Doer.ID,
|
||||
Email: email,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.Data["IsActivatePage"] = false
|
||||
log.Error("Couldn't replace inactive primary email of user %d: %v", ctx.Doer.ID, err)
|
||||
ctx.RenderWithErr(ctx.Tr("auth.change_unconfirmed_email_error", err), TplActivate, nil)
|
||||
return
|
||||
}
|
||||
if err := ctx.Cache.Put("MailChangeLimit_"+ctx.Doer.LowerName, ctx.Doer.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailChangeLimit) fail: %v", err)
|
||||
}
|
||||
if err := ctx.Cache.Put("MailChangedJustNow_"+ctx.Doer.LowerName, ctx.Doer.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailChangedJustNow) fail: %v", err)
|
||||
}
|
||||
|
||||
// Confirmation mail will be re-sent after the redirect to `/user/activate` below.
|
||||
}
|
||||
} else {
|
||||
ctx.Data["ServiceNotEnabled"] = true
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/user/activate")
|
||||
return
|
||||
}
|
||||
|
|
|
@ -952,10 +952,16 @@ func SignInOAuthCallback(ctx *context.Context) {
|
|||
return
|
||||
} else if !setting.Service.AllowOnlyInternalRegistration && setting.OAuth2Client.EnableAutoRegistration {
|
||||
// create new user with details from oauth2 provider
|
||||
var missingFields []string
|
||||
if gothUser.UserID == "" {
|
||||
missingFields = append(missingFields, "sub")
|
||||
log.Error("OAuth2 Provider %s returned empty or missing field: UserID", authSource.Name)
|
||||
if authSource.IsOAuth2() && authSource.Cfg.(*oauth2.Source).Provider == "openidConnect" {
|
||||
log.Error("You may need to change the 'OPENID_CONNECT_SCOPES' setting to request all required fields")
|
||||
}
|
||||
err = fmt.Errorf("OAuth2 Provider %s returned empty or missing field: UserID", authSource.Name)
|
||||
ctx.ServerError("CreateUser", err)
|
||||
return
|
||||
}
|
||||
var missingFields []string
|
||||
if gothUser.Email == "" {
|
||||
missingFields = append(missingFields, "email")
|
||||
}
|
||||
|
@ -963,12 +969,10 @@ func SignInOAuthCallback(ctx *context.Context) {
|
|||
missingFields = append(missingFields, "nickname")
|
||||
}
|
||||
if len(missingFields) > 0 {
|
||||
log.Error("OAuth2 Provider %s returned empty or missing fields: %s", authSource.Name, missingFields)
|
||||
if authSource.IsOAuth2() && authSource.Cfg.(*oauth2.Source).Provider == "openidConnect" {
|
||||
log.Error("You may need to change the 'OPENID_CONNECT_SCOPES' setting to request all required fields")
|
||||
}
|
||||
err = fmt.Errorf("OAuth2 Provider %s returned empty or missing fields: %s", authSource.Name, missingFields)
|
||||
ctx.ServerError("CreateUser", err)
|
||||
// we don't have enough information to create an account automatically,
|
||||
// so we prompt the user for the remaining bits
|
||||
log.Trace("OAuth2 Provider %s returned empty or missing fields: %s, prompting the user for them", authSource.Name, missingFields)
|
||||
showLinkingLogin(ctx, gothUser)
|
||||
return
|
||||
}
|
||||
uname, err := getUserName(&gothUser)
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/gorilla/feeds"
|
||||
"github.com/jaytaylor/html2text"
|
||||
)
|
||||
|
||||
func toBranchLink(ctx *context.Context, act *activities_model.Action) string {
|
||||
|
@ -240,8 +241,15 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
|
|||
content = desc
|
||||
}
|
||||
|
||||
// It's a common practice for feed generators to use plain text titles.
|
||||
// See https://codeberg.org/forgejo/forgejo/pulls/1595
|
||||
plainTitle, err := html2text.FromString(title, html2text.Options{OmitLinks: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items = append(items, &feeds.Item{
|
||||
Title: title,
|
||||
Title: plainTitle,
|
||||
Link: link,
|
||||
Description: desc,
|
||||
IsPermaLink: "false",
|
||||
|
|
|
@ -8,11 +8,12 @@ import (
|
|||
)
|
||||
|
||||
// RenderBranchFeed render format for branch or file
|
||||
func RenderBranchFeed(ctx *context.Context) {
|
||||
_, _, showFeedType := GetFeedType(ctx.Params(":reponame"), ctx.Req)
|
||||
if ctx.Repo.TreePath == "" {
|
||||
ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||
} else {
|
||||
ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||
func RenderBranchFeed(feedType string) func(ctx *context.Context) {
|
||||
return func(ctx *context.Context) {
|
||||
if ctx.Repo.TreePath == "" {
|
||||
ShowBranchFeed(ctx, ctx.Repo.Repository, feedType)
|
||||
} else {
|
||||
ShowFileFeed(ctx, ctx.Repo.Repository, feedType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,20 @@ func View(ctx *context_module.Context) {
|
|||
ctx.HTML(http.StatusOK, tplViewActions)
|
||||
}
|
||||
|
||||
func ViewLatest(ctx *context_module.Context) {
|
||||
run, err := actions_model.GetLatestRun(ctx, ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
ctx.NotFound("GetLatestRun", err)
|
||||
return
|
||||
}
|
||||
err = run.LoadAttributes(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
ctx.Redirect(run.HTMLURL(), http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
||||
type ViewRequest struct {
|
||||
LogCursors []struct {
|
||||
Step int `json:"step"`
|
||||
|
|
165
routers/web/repo/badges/badges.go
Normal file
165
routers/web/repo/badges/badges.go
Normal file
|
@ -0,0 +1,165 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package badges
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
context_module "code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func getBadgeURL(ctx *context_module.Context, label, text, color string) string {
|
||||
sb := &strings.Builder{}
|
||||
_ = setting.Badges.GeneratorURLTemplateTemplate.Execute(sb, map[string]string{
|
||||
"label": url.PathEscape(label),
|
||||
"text": url.PathEscape(text),
|
||||
"color": url.PathEscape(color),
|
||||
})
|
||||
|
||||
badgeURL := sb.String()
|
||||
q := ctx.Req.URL.Query()
|
||||
// Remove any `branch` or `event` query parameters. They're used by the
|
||||
// workflow badge route, and do not need forwarding to the badge generator.
|
||||
delete(q, "branch")
|
||||
delete(q, "event")
|
||||
if len(q) > 0 {
|
||||
return fmt.Sprintf("%s?%s", badgeURL, q.Encode())
|
||||
}
|
||||
return badgeURL
|
||||
}
|
||||
|
||||
func redirectToBadge(ctx *context_module.Context, label, text, color string) {
|
||||
ctx.Redirect(getBadgeURL(ctx, label, text, color))
|
||||
}
|
||||
|
||||
func errorBadge(ctx *context_module.Context, label, text string) {
|
||||
ctx.Redirect(getBadgeURL(ctx, label, text, "crimson"))
|
||||
}
|
||||
|
||||
func GetWorkflowBadge(ctx *context_module.Context) {
|
||||
branch := ctx.Req.URL.Query().Get("branch")
|
||||
if branch == "" {
|
||||
branch = ctx.Repo.Repository.DefaultBranch
|
||||
}
|
||||
branch = fmt.Sprintf("refs/heads/%s", branch)
|
||||
event := ctx.Req.URL.Query().Get("event")
|
||||
|
||||
workflowFile := ctx.Params("workflow_name")
|
||||
run, err := actions_model.GetLatestRunForBranchAndWorkflow(ctx, ctx.Repo.Repository.ID, branch, workflowFile, event)
|
||||
if err != nil {
|
||||
errorBadge(ctx, workflowFile, "Not found")
|
||||
return
|
||||
}
|
||||
|
||||
var color string
|
||||
switch run.Status {
|
||||
case actions_model.StatusUnknown:
|
||||
color = "lightgrey"
|
||||
case actions_model.StatusWaiting:
|
||||
color = "lightgrey"
|
||||
case actions_model.StatusRunning:
|
||||
color = "gold"
|
||||
case actions_model.StatusSuccess:
|
||||
color = "brightgreen"
|
||||
case actions_model.StatusFailure:
|
||||
color = "crimson"
|
||||
case actions_model.StatusCancelled:
|
||||
color = "orange"
|
||||
case actions_model.StatusSkipped:
|
||||
color = "blue"
|
||||
case actions_model.StatusBlocked:
|
||||
color = "yellow"
|
||||
default:
|
||||
color = "lightgrey"
|
||||
}
|
||||
|
||||
redirectToBadge(ctx, workflowFile, run.Status.String(), color)
|
||||
}
|
||||
|
||||
func getIssueOrPullBadge(ctx *context_module.Context, label, variant string, num int) {
|
||||
var text string
|
||||
if len(variant) > 0 {
|
||||
text = fmt.Sprintf("%d %s", num, variant)
|
||||
} else {
|
||||
text = fmt.Sprintf("%d", num)
|
||||
}
|
||||
redirectToBadge(ctx, label, text, "blue")
|
||||
}
|
||||
|
||||
func getIssueBadge(ctx *context_module.Context, variant string, num int) {
|
||||
if !ctx.Repo.CanRead(unit.TypeIssues) &&
|
||||
!ctx.Repo.CanRead(unit.TypeExternalTracker) {
|
||||
errorBadge(ctx, "issues", "Not found")
|
||||
return
|
||||
}
|
||||
|
||||
_, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalTracker)
|
||||
if err == nil {
|
||||
errorBadge(ctx, "issues", "Not found")
|
||||
return
|
||||
}
|
||||
|
||||
getIssueOrPullBadge(ctx, "issues", variant, num)
|
||||
}
|
||||
|
||||
func getPullBadge(ctx *context_module.Context, variant string, num int) {
|
||||
if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests) {
|
||||
errorBadge(ctx, "pulls", "Not found")
|
||||
return
|
||||
}
|
||||
|
||||
getIssueOrPullBadge(ctx, "pulls", variant, num)
|
||||
}
|
||||
|
||||
func GetOpenIssuesBadge(ctx *context_module.Context) {
|
||||
getIssueBadge(ctx, "open", ctx.Repo.Repository.NumOpenIssues)
|
||||
}
|
||||
|
||||
func GetClosedIssuesBadge(ctx *context_module.Context) {
|
||||
getIssueBadge(ctx, "closed", ctx.Repo.Repository.NumClosedIssues)
|
||||
}
|
||||
|
||||
func GetTotalIssuesBadge(ctx *context_module.Context) {
|
||||
getIssueBadge(ctx, "", ctx.Repo.Repository.NumIssues)
|
||||
}
|
||||
|
||||
func GetOpenPullsBadge(ctx *context_module.Context) {
|
||||
getPullBadge(ctx, "open", ctx.Repo.Repository.NumOpenPulls)
|
||||
}
|
||||
|
||||
func GetClosedPullsBadge(ctx *context_module.Context) {
|
||||
getPullBadge(ctx, "closed", ctx.Repo.Repository.NumClosedPulls)
|
||||
}
|
||||
|
||||
func GetTotalPullsBadge(ctx *context_module.Context) {
|
||||
getPullBadge(ctx, "", ctx.Repo.Repository.NumPulls)
|
||||
}
|
||||
|
||||
func GetStarsBadge(ctx *context_module.Context) {
|
||||
redirectToBadge(ctx, "stars", fmt.Sprintf("%d", ctx.Repo.Repository.NumStars), "blue")
|
||||
}
|
||||
|
||||
func GetLatestReleaseBadge(ctx *context_module.Context) {
|
||||
release, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
if repo_model.IsErrReleaseNotExist(err) {
|
||||
errorBadge(ctx, "release", "Not found")
|
||||
return
|
||||
}
|
||||
ctx.ServerError("GetLatestReleaseByRepoID", err)
|
||||
}
|
||||
|
||||
if err := release.LoadAttributes(ctx); err != nil {
|
||||
ctx.ServerError("LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
|
||||
redirectToBadge(ctx, "release", release.TagName, "blue")
|
||||
}
|
|
@ -8,7 +8,6 @@ import (
|
|||
gotemplate "html/template"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
@ -39,32 +38,15 @@ type blameRow struct {
|
|||
|
||||
// RefBlame render blame page
|
||||
func RefBlame(ctx *context.Context) {
|
||||
fileName := ctx.Repo.TreePath
|
||||
if len(fileName) == 0 {
|
||||
ctx.NotFound("Blame FileName", nil)
|
||||
if ctx.Repo.TreePath == "" {
|
||||
ctx.NotFound("No file specified", nil)
|
||||
return
|
||||
}
|
||||
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
treeLink := branchLink
|
||||
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
|
||||
|
||||
if len(ctx.Repo.TreePath) > 0 {
|
||||
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
}
|
||||
|
||||
var treeNames []string
|
||||
paths := make([]string, 0, 5)
|
||||
if len(ctx.Repo.TreePath) > 0 {
|
||||
treeNames = strings.Split(ctx.Repo.TreePath, "/")
|
||||
for i := range treeNames {
|
||||
paths = append(paths, strings.Join(treeNames[:i+1], "/"))
|
||||
}
|
||||
|
||||
ctx.Data["HasParentPath"] = true
|
||||
if len(paths)-2 >= 0 {
|
||||
ctx.Data["ParentPath"] = "/" + paths[len(paths)-1]
|
||||
}
|
||||
treeNames := strings.Split(ctx.Repo.TreePath, "/")
|
||||
for i := range treeNames {
|
||||
paths = append(paths, strings.Join(treeNames[:i+1], "/"))
|
||||
}
|
||||
|
||||
// Get current entry user currently looking at.
|
||||
|
@ -73,47 +55,35 @@ func RefBlame(ctx *context.Context) {
|
|||
HandleGitError(ctx, "Repo.Commit.GetTreeEntryByPath", err)
|
||||
return
|
||||
}
|
||||
|
||||
blob := entry.Blob()
|
||||
|
||||
ctx.Data["Paths"] = paths
|
||||
ctx.Data["TreeLink"] = treeLink
|
||||
ctx.Data["TreeNames"] = treeNames
|
||||
ctx.Data["BranchLink"] = branchLink
|
||||
|
||||
ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
ctx.Data["PageIsViewCode"] = true
|
||||
|
||||
ctx.Data["IsBlame"] = true
|
||||
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
ctx.Data["Paths"] = paths
|
||||
ctx.Data["TreeNames"] = treeNames
|
||||
|
||||
ctx.Data["FileSize"] = blob.Size()
|
||||
ctx.Data["FileName"] = blob.Name()
|
||||
|
||||
ctx.Data["NumLines"], err = blob.GetBlobLineCount()
|
||||
ctx.Data["NumLinesSet"] = true
|
||||
|
||||
ctx.Data["NumLines"], err = blob.GetBlobLineCount()
|
||||
if err != nil {
|
||||
ctx.NotFound("GetBlobLineCount", err)
|
||||
ctx.ServerError("GetBlobLineCount", err)
|
||||
return
|
||||
}
|
||||
|
||||
bypassBlameIgnore, _ := strconv.ParseBool(ctx.FormString("bypass-blame-ignore"))
|
||||
|
||||
result, err := performBlame(ctx, ctx.Repo.Repository.RepoPath(), ctx.Repo.Commit, fileName, bypassBlameIgnore)
|
||||
result, err := performBlame(ctx, ctx.Repo.Commit, ctx.Repo.TreePath, ctx.FormBool("bypass-blame-ignore"))
|
||||
if err != nil {
|
||||
ctx.NotFound("CreateBlameReader", err)
|
||||
ctx.ServerError("performBlame", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["UsesIgnoreRevs"] = result.UsesIgnoreRevs
|
||||
ctx.Data["FaultyIgnoreRevsFile"] = result.FaultyIgnoreRevsFile
|
||||
|
||||
// Get Topics of this repo
|
||||
renderRepoTopics(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
commitNames := processBlameParts(ctx, result.Parts)
|
||||
if ctx.Written() {
|
||||
return
|
||||
|
@ -130,12 +100,13 @@ type blameResult struct {
|
|||
FaultyIgnoreRevsFile bool
|
||||
}
|
||||
|
||||
func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) {
|
||||
func performBlame(ctx *context.Context, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) {
|
||||
repoPath := ctx.Repo.Repository.RepoPath()
|
||||
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
|
||||
|
|
|
@ -244,6 +244,22 @@ func FileHistory(ctx *context.Context) {
|
|||
ctx.ServerError("CommitsByFileAndRange", err)
|
||||
return
|
||||
}
|
||||
oldestCommit := commits[len(commits)-1]
|
||||
|
||||
renamedFiles, err := git.GetCommitFileRenames(ctx, ctx.Repo.GitRepo.Path, oldestCommit.ID.String())
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCommitFileRenames", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, renames := range renamedFiles {
|
||||
if renames[1] == fileName {
|
||||
ctx.Data["OldFilename"] = renames[0]
|
||||
ctx.Data["OldFilenameHistory"] = fmt.Sprintf("%s/commits/commit/%s/%s", ctx.Repo.RepoLink, oldestCommit.ID.String(), renames[0])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commits, ctx.Repo.Repository)
|
||||
|
||||
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
git_model "code.gitea.io/gitea/models/git"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/charset"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
|
@ -99,6 +100,27 @@ func getParentTreeFields(treePath string) (treeNames, treePaths []string) {
|
|||
return treeNames, treePaths
|
||||
}
|
||||
|
||||
// getSelectableEmailAddresses returns which emails can be used by the user as
|
||||
// email for a Git commiter.
|
||||
func getSelectableEmailAddresses(ctx *context.Context) ([]*user_model.ActivatedEmailAddress, error) {
|
||||
// Retrieve emails that the user could use for commiter identity.
|
||||
commitEmails, err := user_model.GetActivatedEmailAddresses(ctx, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetActivatedEmailAddresses: %w", err)
|
||||
}
|
||||
|
||||
// Allow for the placeholder mail to be used. Use -1 as ID to identify
|
||||
// this entry to be the placerholder mail of the user.
|
||||
placeholderMail := &user_model.ActivatedEmailAddress{ID: -1, Email: ctx.Doer.GetPlaceholderEmail()}
|
||||
if ctx.Doer.KeepEmailPrivate {
|
||||
commitEmails = append([]*user_model.ActivatedEmailAddress{placeholderMail}, commitEmails...)
|
||||
} else {
|
||||
commitEmails = append(commitEmails, placeholderMail)
|
||||
}
|
||||
|
||||
return commitEmails, nil
|
||||
}
|
||||
|
||||
func editFile(ctx *context.Context, isNewFile bool) {
|
||||
ctx.Data["PageIsEdit"] = true
|
||||
ctx.Data["IsNewFile"] = isNewFile
|
||||
|
@ -177,6 +199,12 @@ func editFile(ctx *context.Context, isNewFile bool) {
|
|||
treeNames = append(treeNames, fileName)
|
||||
}
|
||||
|
||||
commitEmails, err := getSelectableEmailAddresses(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("getSelectableEmailAddresses", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["TreeNames"] = treeNames
|
||||
ctx.Data["TreePaths"] = treePaths
|
||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
|
@ -192,6 +220,8 @@ func editFile(ctx *context.Context, isNewFile bool) {
|
|||
ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
|
||||
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
|
||||
ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, treePath)
|
||||
ctx.Data["CommitMails"] = commitEmails
|
||||
ctx.Data["DefaultCommitMail"] = ctx.Doer.GetEmail()
|
||||
|
||||
ctx.HTML(http.StatusOK, tplEditFile)
|
||||
}
|
||||
|
@ -227,6 +257,12 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
|
|||
branchName = form.NewBranchName
|
||||
}
|
||||
|
||||
commitEmails, err := getSelectableEmailAddresses(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("getSelectableEmailAddresses", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["PageIsEdit"] = true
|
||||
ctx.Data["PageHasPosted"] = true
|
||||
ctx.Data["IsNewFile"] = isNewFile
|
||||
|
@ -243,6 +279,8 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
|
|||
ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
|
||||
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
|
||||
ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, form.TreePath)
|
||||
ctx.Data["CommitMails"] = commitEmails
|
||||
ctx.Data["DefaultCommitMail"] = ctx.Doer.GetEmail()
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(http.StatusOK, tplEditFile)
|
||||
|
@ -277,6 +315,30 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
|
|||
operation = "create"
|
||||
}
|
||||
|
||||
gitIdentity := &files_service.IdentityOptions{
|
||||
Name: ctx.Doer.Name,
|
||||
}
|
||||
|
||||
// -1 is defined as placeholder email.
|
||||
if form.CommitMailID == -1 {
|
||||
gitIdentity.Email = ctx.Doer.GetPlaceholderEmail()
|
||||
} else {
|
||||
// Check if the given email is activated.
|
||||
email, err := user_model.GetEmailAddressByID(ctx, ctx.Doer.ID, form.CommitMailID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetEmailAddressByID", err)
|
||||
return
|
||||
}
|
||||
|
||||
if email == nil || !email.IsActivated {
|
||||
ctx.Data["Err_CommitMailID"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.invalid_commit_mail"), tplEditFile, &form)
|
||||
return
|
||||
}
|
||||
|
||||
gitIdentity.Email = email.Email
|
||||
}
|
||||
|
||||
if _, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{
|
||||
LastCommitID: form.LastCommit,
|
||||
OldBranch: ctx.Repo.BranchName,
|
||||
|
@ -290,7 +352,9 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
|
|||
ContentReader: strings.NewReader(strings.ReplaceAll(form.Content, "\r", "")),
|
||||
},
|
||||
},
|
||||
Signoff: form.Signoff,
|
||||
Signoff: form.Signoff,
|
||||
Author: gitIdentity,
|
||||
Committer: gitIdentity,
|
||||
}); err != nil {
|
||||
// This is where we handle all the errors thrown by files_service.ChangeRepoFiles
|
||||
if git.IsErrNotExist(err) {
|
||||
|
|
49
routers/web/repo/flags/manage.go
Normal file
49
routers/web/repo/flags/manage.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
const (
|
||||
tplRepoFlags base.TplName = "repo/flags"
|
||||
)
|
||||
|
||||
func Manage(ctx *context.Context) {
|
||||
ctx.Data["IsRepoFlagsPage"] = true
|
||||
ctx.Data["Title"] = ctx.Tr("repo.admin.manage_flags")
|
||||
|
||||
flags := map[string]bool{}
|
||||
for _, f := range setting.Repository.SettableFlags {
|
||||
flags[f] = false
|
||||
}
|
||||
repoFlags, _ := ctx.Repo.Repository.ListFlags(ctx)
|
||||
for _, f := range repoFlags {
|
||||
flags[f.Name] = true
|
||||
}
|
||||
|
||||
ctx.Data["Flags"] = flags
|
||||
|
||||
ctx.HTML(http.StatusOK, tplRepoFlags)
|
||||
}
|
||||
|
||||
func ManagePost(ctx *context.Context) {
|
||||
newFlags := ctx.FormStrings("flags")
|
||||
|
||||
err := ctx.Repo.Repository.ReplaceAllFlags(ctx, newFlags)
|
||||
if err != nil {
|
||||
ctx.Flash.Error(ctx.Tr("repo.admin.failed_to_replace_flags"))
|
||||
log.Error("Error replacing repository flags for repo %d: %v", ctx.Repo.Repository.ID, err)
|
||||
} else {
|
||||
ctx.Flash.Success(ctx.Tr("repo.admin.flags_replaced"))
|
||||
}
|
||||
|
||||
ctx.Redirect(ctx.Repo.Repository.HTMLURL() + "/flags")
|
||||
}
|
|
@ -2504,7 +2504,8 @@ func UpdatePullReviewRequest(ctx *context.Context) {
|
|||
func SearchIssues(ctx *context.Context) {
|
||||
before, since, err := context.GetQueryBeforeSince(ctx.Base)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusUnprocessableEntity, err.Error())
|
||||
log.Error("GetQueryBeforeSince: %v", err)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "invalid before or since")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -2541,10 +2542,11 @@ func SearchIssues(ctx *context.Context) {
|
|||
if ctx.FormString("owner") != "" {
|
||||
owner, err := user_model.GetUserByName(ctx, ctx.FormString("owner"))
|
||||
if err != nil {
|
||||
log.Error("GetUserByName: %v", err)
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
ctx.Error(http.StatusBadRequest, "Owner not found", err.Error())
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserByName", err.Error())
|
||||
ctx.Error(http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -2555,15 +2557,16 @@ func SearchIssues(ctx *context.Context) {
|
|||
}
|
||||
if ctx.FormString("team") != "" {
|
||||
if ctx.FormString("owner") == "" {
|
||||
ctx.Error(http.StatusBadRequest, "", "Owner organisation is required for filtering on team")
|
||||
ctx.Error(http.StatusBadRequest, "Owner organisation is required for filtering on team")
|
||||
return
|
||||
}
|
||||
team, err := organization.GetTeam(ctx, opts.OwnerID, ctx.FormString("team"))
|
||||
if err != nil {
|
||||
log.Error("GetTeam: %v", err)
|
||||
if organization.IsErrTeamNotExist(err) {
|
||||
ctx.Error(http.StatusBadRequest, "Team not found", err.Error())
|
||||
ctx.Error(http.StatusBadRequest)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserByName", err.Error())
|
||||
ctx.Error(http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -2576,7 +2579,8 @@ func SearchIssues(ctx *context.Context) {
|
|||
}
|
||||
repoIDs, _, err = repo_model.SearchRepositoryIDs(ctx, opts)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "SearchRepositoryIDs", err.Error())
|
||||
log.Error("SearchRepositoryIDs: %v", err)
|
||||
ctx.Error(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if len(repoIDs) == 0 {
|
||||
|
@ -2610,7 +2614,8 @@ func SearchIssues(ctx *context.Context) {
|
|||
}
|
||||
includedAnyLabels, err = issues_model.GetLabelIDsByNames(ctx, includedLabelNames)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetLabelIDsByNames", err.Error())
|
||||
log.Error("GetLabelIDsByNames: %v", err)
|
||||
ctx.Error(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -2624,7 +2629,8 @@ func SearchIssues(ctx *context.Context) {
|
|||
}
|
||||
includedMilestones, err = issues_model.GetMilestoneIDsByNames(ctx, includedMilestoneNames)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetMilestoneIDsByNames", err.Error())
|
||||
log.Error("GetMilestoneIDsByNames: %v", err)
|
||||
ctx.Error(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -2691,12 +2697,14 @@ func SearchIssues(ctx *context.Context) {
|
|||
|
||||
ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "SearchIssues", err.Error())
|
||||
log.Error("SearchIssues: %v", err)
|
||||
ctx.Error(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
issues, err := issues_model.GetIssuesByIDs(ctx, ids, true)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "FindIssuesByIDs", err.Error())
|
||||
log.Error("GetIssuesByIDs: %v", err)
|
||||
ctx.Error(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,8 @@ func canSoftDeleteContentHistory(ctx *context.Context, issue *issues_model.Issue
|
|||
// CanWrite means the doer can manage the issue/PR list
|
||||
if ctx.Repo.IsOwner() || ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||
canSoftDelete = true
|
||||
} else if ctx.Doer == nil {
|
||||
canSoftDelete = false
|
||||
} else {
|
||||
// for read-only users, they could still post issues or comments,
|
||||
// they should be able to delete the history related to their own issue/comment, a case is:
|
||||
|
|
|
@ -967,6 +967,18 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
|
|||
return
|
||||
}
|
||||
|
||||
// determine if the user viewing the pull request can edit the head branch
|
||||
if ctx.Doer != nil && pull.HeadRepo != nil && !pull.HasMerged {
|
||||
headRepoPerm, err := access_model.GetUserRepoPermission(ctx, pull.HeadRepo, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserRepoPermission", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["HeadBranchIsEditable"] = pull.HeadRepo.CanEnableEditor() && issues_model.CanMaintainerWriteToBranch(ctx, headRepoPerm, pull.HeadBranch, ctx.Doer)
|
||||
ctx.Data["SourceRepoLink"] = pull.HeadRepo.Link()
|
||||
ctx.Data["HeadBranch"] = pull.HeadBranch
|
||||
}
|
||||
|
||||
if ctx.IsSigned && ctx.Doer != nil {
|
||||
if ctx.Data["CanMarkConversation"], err = issues_model.CanMarkConversation(ctx, issue, ctx.Doer); err != nil {
|
||||
ctx.ServerError("CanMarkConversation", err)
|
||||
|
|
|
@ -153,19 +153,12 @@ func UpdateResolveConversation(ctx *context.Context) {
|
|||
}
|
||||
|
||||
func renderConversation(ctx *context.Context, comment *issues_model.Comment, origin string) {
|
||||
ctx.Data["PageIsPullFiles"] = origin == "diff"
|
||||
|
||||
comments, err := issues_model.FetchCodeCommentsByLine(ctx, comment.Issue, ctx.Doer, comment.TreePath, comment.Line, ctx.Data["ShowOutdatedComments"].(bool))
|
||||
comments, err := issues_model.FetchCodeCommentsByLine(ctx, comment.Issue, ctx.Doer, comment.TreePath, comment.Line, true)
|
||||
if err != nil {
|
||||
ctx.ServerError("FetchCodeCommentsByLine", err)
|
||||
return
|
||||
}
|
||||
if len(comments) == 0 {
|
||||
// if the comments are empty (deleted, outdated, etc), it doesn't need to render anything, just return an empty body to replace "conversation-holder" on the page
|
||||
ctx.Resp.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["PageIsPullFiles"] = (origin == "diff")
|
||||
ctx.Data["comments"] = comments
|
||||
if ctx.Data["CanMarkConversation"], err = issues_model.CanMarkConversation(ctx, comment.Issue, ctx.Doer); err != nil {
|
||||
ctx.ServerError("CanMarkConversation", err)
|
||||
|
@ -186,8 +179,6 @@ func renderConversation(ctx *context.Context, comment *issues_model.Comment, ori
|
|||
ctx.HTML(http.StatusOK, tplDiffConversation)
|
||||
} else if origin == "timeline" {
|
||||
ctx.HTML(http.StatusOK, tplTimelineConversation)
|
||||
} else {
|
||||
ctx.Error(http.StatusBadRequest, "Unknown origin: "+origin)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -397,7 +397,11 @@ func NewReleasePost(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.GitRepo.IsBranchExist(form.Target) {
|
||||
objectFormat, _ := ctx.Repo.GitRepo.GetObjectFormat()
|
||||
|
||||
// form.Target can be a branch name or a full commitID.
|
||||
if !ctx.Repo.GitRepo.IsBranchExist(form.Target) &&
|
||||
len(form.Target) == objectFormat.FullLength() && !ctx.Repo.GitRepo.IsCommitExist(form.Target) {
|
||||
ctx.RenderWithErr(ctx.Tr("form.target_branch_not_exist"), tplReleaseNew, &form)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -6,12 +6,13 @@ package setting
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/routers/web/repo"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
notify_service "code.gitea.io/gitea/services/notify"
|
||||
)
|
||||
|
||||
// SetDefaultBranchPost set default branch
|
||||
|
@ -34,14 +35,23 @@ func SetDefaultBranchPost(ctx *context.Context) {
|
|||
}
|
||||
|
||||
branch := ctx.FormString("branch")
|
||||
if err := repo_service.SetRepoDefaultBranch(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, branch); err != nil {
|
||||
switch {
|
||||
case git_model.IsErrBranchNotExist(err):
|
||||
ctx.Status(http.StatusNotFound)
|
||||
default:
|
||||
ctx.ServerError("SetDefaultBranch", err)
|
||||
}
|
||||
if !ctx.Repo.GitRepo.IsBranchExist(branch) {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
return
|
||||
} else if repo.DefaultBranch != branch {
|
||||
repo.DefaultBranch = branch
|
||||
if err := ctx.Repo.GitRepo.SetDefaultBranch(branch); err != nil {
|
||||
if !git.IsErrUnsupportedVersion(err) {
|
||||
ctx.ServerError("SetDefaultBranch", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := repo_model.UpdateDefaultBranch(ctx, repo); err != nil {
|
||||
ctx.ServerError("SetDefaultBranch", err)
|
||||
return
|
||||
}
|
||||
|
||||
notify_service.ChangeDefaultBranch(ctx, repo)
|
||||
}
|
||||
|
||||
log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
||||
|
|
|
@ -474,10 +474,17 @@ func SettingsPost(ctx *context.Context) {
|
|||
})
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
|
||||
} else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
|
||||
var wikiPermissions repo_model.UnitAccessMode
|
||||
if form.GloballyWriteableWiki {
|
||||
wikiPermissions = repo_model.UnitAccessModeWrite
|
||||
} else {
|
||||
wikiPermissions = repo_model.UnitAccessModeRead
|
||||
}
|
||||
units = append(units, repo_model.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: unit_model.TypeWiki,
|
||||
Config: new(repo_model.UnitConfig),
|
||||
RepoID: repo.ID,
|
||||
Type: unit_model.TypeWiki,
|
||||
Config: new(repo_model.UnitConfig),
|
||||
DefaultPermissions: wikiPermissions,
|
||||
})
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
|
||||
} else {
|
||||
|
@ -595,7 +602,7 @@ func SettingsPost(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := repo_service.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
|
||||
if err := repo_model.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
|
||||
ctx.ServerError("UpdateRepositoryUnits", err)
|
||||
return
|
||||
}
|
||||
|
@ -868,6 +875,27 @@ func SettingsPost(ctx *context.Context) {
|
|||
ctx.Flash.Success(ctx.Tr("repo.settings.wiki_deletion_success"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
|
||||
|
||||
case "rename-wiki-branch":
|
||||
if !ctx.Repo.IsOwner() {
|
||||
ctx.Error(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if repo.FullName() != form.RepoName {
|
||||
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err := wiki_service.NormalizeWikiBranch(ctx, repo, setting.Repository.DefaultBranch); err != nil {
|
||||
log.Error("Normalize Wiki branch: %v", err.Error())
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.wiki_branch_rename_failure"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
|
||||
return
|
||||
}
|
||||
log.Trace("Repository wiki normalized: %s#%s", repo.FullName(), setting.Repository.DefaultBranch)
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.wiki_branch_rename_success"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
|
||||
|
||||
case "archive":
|
||||
if !ctx.Repo.IsOwner() {
|
||||
ctx.Error(http.StatusForbidden)
|
||||
|
|
|
@ -166,7 +166,7 @@ func renderDirectory(ctx *context.Context) {
|
|||
|
||||
if ctx.Repo.TreePath != "" {
|
||||
ctx.Data["HideRepoInfo"] = true
|
||||
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
|
||||
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+ctx.Repo.TreePath, ctx.Repo.RefName)
|
||||
}
|
||||
|
||||
subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true)
|
||||
|
@ -381,7 +381,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
|
|||
}
|
||||
defer dataRc.Close()
|
||||
|
||||
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
|
||||
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+ctx.Repo.TreePath, ctx.Repo.RefName)
|
||||
ctx.Data["FileIsSymlink"] = entry.IsLink()
|
||||
ctx.Data["FileName"] = blob.Name()
|
||||
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
|
@ -747,7 +747,7 @@ func checkHomeCodeViewable(ctx *context.Context) {
|
|||
}
|
||||
|
||||
unit, ok := unit_model.Units[repoUnit.Type]
|
||||
if ok && (firstUnit == nil || !firstUnit.IsLessThan(unit)) {
|
||||
if ok && (firstUnit == nil || !firstUnit.IsLessThan(unit)) && repoUnit.Type.CanBeDefault() {
|
||||
firstUnit = &unit
|
||||
}
|
||||
}
|
||||
|
@ -794,12 +794,19 @@ func Home(ctx *context.Context) {
|
|||
if setting.Other.EnableFeed {
|
||||
isFeed, _, showFeedType := feed.GetFeedType(ctx.Params(":reponame"), ctx.Req)
|
||||
if isFeed {
|
||||
switch {
|
||||
case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType):
|
||||
if ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType) {
|
||||
feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||
case ctx.Repo.TreePath == "":
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.Repo.Repository.IsEmpty {
|
||||
ctx.NotFound("MustBeNotEmpty", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.Repo.TreePath == "" {
|
||||
feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||
case ctx.Repo.TreePath != "":
|
||||
} else {
|
||||
feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||
}
|
||||
return
|
||||
|
@ -1028,20 +1035,79 @@ func renderCode(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
showRecentlyPushedNewBranches := true
|
||||
if ctx.Repo.Repository.IsMirror ||
|
||||
!ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypePullRequests) {
|
||||
showRecentlyPushedNewBranches = false
|
||||
// If the repo is a mirror, don't display recently pushed branches.
|
||||
if ctx.Repo.Repository.IsMirror {
|
||||
goto PostRecentBranchCheck
|
||||
}
|
||||
if showRecentlyPushedNewBranches {
|
||||
ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Repo.Repository.ID, ctx.Doer.ID, ctx.Repo.Repository.DefaultBranch)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetRecentlyPushedBranches", err)
|
||||
return
|
||||
|
||||
// If pull requests aren't enabled for either the current repo, or its
|
||||
// base, don't display recently pushed branches.
|
||||
if !(ctx.Repo.Repository.AllowsPulls(ctx) ||
|
||||
(ctx.Repo.Repository.BaseRepo != nil && ctx.Repo.Repository.BaseRepo.AllowsPulls(ctx))) {
|
||||
goto PostRecentBranchCheck
|
||||
}
|
||||
|
||||
// Find recently pushed new branches to *this* repo.
|
||||
branches, err := git_model.FindRecentlyPushedNewBranches(ctx, ctx.Repo.Repository.ID, ctx.Doer.ID, ctx.Repo.Repository.DefaultBranch)
|
||||
if err != nil {
|
||||
ctx.ServerError("FindRecentlyPushedBranches", err)
|
||||
return
|
||||
}
|
||||
|
||||
// If this is not a fork, check if the signed in user has a fork, and
|
||||
// check branches there.
|
||||
if !ctx.Repo.Repository.IsFork {
|
||||
repo := repo_model.GetForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)
|
||||
if repo != nil {
|
||||
baseBranches, err := git_model.FindRecentlyPushedNewBranches(ctx, repo.ID, ctx.Doer.ID, repo.DefaultBranch)
|
||||
if err != nil {
|
||||
ctx.ServerError("FindRecentlyPushedBranches", err)
|
||||
return
|
||||
}
|
||||
branches = append(branches, baseBranches...)
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out branches that have no relation to the default branch of
|
||||
// the repository.
|
||||
var filteredBranches []*git_model.Branch
|
||||
for _, branch := range branches {
|
||||
repo, err := branch.GetRepo(ctx)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
head, err := gitRepo.GetCommit(branch.CommitID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defaultBranch, err := gitRepo.GetDefaultBranch()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defaultBranchHead, err := gitRepo.GetCommit(defaultBranch)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
hasMergeBase, err := head.HasPreviousCommit(defaultBranchHead.ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if hasMergeBase {
|
||||
filteredBranches = append(filteredBranches, branch)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Data["RecentlyPushedNewBranches"] = filteredBranches
|
||||
}
|
||||
|
||||
PostRecentBranchCheck:
|
||||
var treeNames []string
|
||||
paths := make([]string, 0, 5)
|
||||
if len(ctx.Repo.TreePath) > 0 {
|
||||
|
|
|
@ -99,7 +99,7 @@ func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, err
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
commit, err := wikiRepo.GetBranchCommit(wiki_service.DefaultBranch)
|
||||
commit, err := wikiRepo.GetBranchCommit(ctx.Repo.Repository.GetWikiBranchName())
|
||||
if err != nil {
|
||||
return wikiRepo, nil, err
|
||||
}
|
||||
|
@ -316,7 +316,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
|||
}
|
||||
|
||||
// get commit count - wiki revisions
|
||||
commitsCount, _ := wikiRepo.FileCommitsCount(wiki_service.DefaultBranch, pageFilename)
|
||||
commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.GetWikiBranchName(), pageFilename)
|
||||
ctx.Data["CommitCount"] = commitsCount
|
||||
|
||||
return wikiRepo, entry
|
||||
|
@ -368,7 +368,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
|
|||
ctx.Data["footerContent"] = ""
|
||||
|
||||
// get commit count - wiki revisions
|
||||
commitsCount, _ := wikiRepo.FileCommitsCount(wiki_service.DefaultBranch, pageFilename)
|
||||
commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.GetWikiBranchName(), pageFilename)
|
||||
ctx.Data["CommitCount"] = commitsCount
|
||||
|
||||
// get page
|
||||
|
@ -380,7 +380,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
|
|||
// get Commit Count
|
||||
commitsHistory, err := wikiRepo.CommitsByFileAndRange(
|
||||
git.CommitsByFileAndRangeOptions{
|
||||
Revision: wiki_service.DefaultBranch,
|
||||
Revision: ctx.Repo.Repository.GetWikiBranchName(),
|
||||
File: pageFilename,
|
||||
Page: page,
|
||||
})
|
||||
|
|
|
@ -98,7 +98,7 @@ func FindUserProfileReadme(ctx *context.Context, doer *user_model.User) (profile
|
|||
if commit, err := profileGitRepo.GetBranchCommit(profileDbRepo.DefaultBranch); err != nil {
|
||||
log.Error("FindUserProfileReadme failed to GetBranchCommit: %v", err)
|
||||
} else {
|
||||
profileReadmeBlob, _ = commit.GetBlobByPath("README.md")
|
||||
profileReadmeBlob, _ = commit.GetBlobByFoldedPath("README.md")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -715,12 +715,15 @@ func UsernameSubRoute(ctx *context.Context) {
|
|||
reloadParam := func(suffix string) (success bool) {
|
||||
ctx.SetParams("username", strings.TrimSuffix(username, suffix))
|
||||
context_service.UserAssignmentWeb()(ctx)
|
||||
if ctx.Written() {
|
||||
return false
|
||||
}
|
||||
// check view permissions
|
||||
if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) {
|
||||
ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name))
|
||||
return false
|
||||
}
|
||||
return !ctx.Written()
|
||||
return true
|
||||
}
|
||||
switch {
|
||||
case strings.HasSuffix(username, ".png"):
|
||||
|
|
|
@ -37,6 +37,8 @@ import (
|
|||
org_setting "code.gitea.io/gitea/routers/web/org/setting"
|
||||
"code.gitea.io/gitea/routers/web/repo"
|
||||
"code.gitea.io/gitea/routers/web/repo/actions"
|
||||
"code.gitea.io/gitea/routers/web/repo/badges"
|
||||
repo_flags "code.gitea.io/gitea/routers/web/repo/flags"
|
||||
repo_setting "code.gitea.io/gitea/routers/web/repo/setting"
|
||||
"code.gitea.io/gitea/routers/web/user"
|
||||
user_setting "code.gitea.io/gitea/routers/web/user/setting"
|
||||
|
@ -49,17 +51,12 @@ import (
|
|||
_ "code.gitea.io/gitea/modules/session" // to registers all internal adapters
|
||||
|
||||
"gitea.com/go-chi/captcha"
|
||||
"github.com/NYTimes/gziphandler"
|
||||
chi_middleware "github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/cors"
|
||||
"github.com/klauspost/compress/gzhttp"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
// GzipMinSize represents min size to compress for the body size of response
|
||||
GzipMinSize = 1400
|
||||
)
|
||||
|
||||
// optionsCorsHandler return a http handler which sets CORS options if enabled by config, it blocks non-CORS OPTIONS requests.
|
||||
func optionsCorsHandler() func(next http.Handler) http.Handler {
|
||||
var corsHandler func(next http.Handler) http.Handler
|
||||
|
@ -245,11 +242,11 @@ func Routes() *web.Route {
|
|||
var mid []any
|
||||
|
||||
if setting.EnableGzip {
|
||||
h, err := gziphandler.GzipHandlerWithOpts(gziphandler.MinSize(GzipMinSize))
|
||||
wrapper, err := gzhttp.NewWrapper(gzhttp.RandomJitter(32, 0, false))
|
||||
if err != nil {
|
||||
log.Fatal("GzipHandlerWithOpts failed: %v", err)
|
||||
log.Fatal("gzhttp.NewWrapper failed: %v", err)
|
||||
}
|
||||
mid = append(mid, h)
|
||||
mid = append(mid, wrapper)
|
||||
}
|
||||
|
||||
if setting.Service.EnableCaptcha {
|
||||
|
@ -686,7 +683,9 @@ func registerRoutes(m *web.Route) {
|
|||
m.Get("", admin.Dashboard)
|
||||
m.Post("", web.Bind(forms.AdminDashboardForm{}), admin.DashboardPost)
|
||||
|
||||
m.Get("/self_check", admin.SelfCheck)
|
||||
if setting.Database.Type.IsMySQL() || setting.Database.Type.IsMSSQL() {
|
||||
m.Get("/self_check", admin.SelfCheck)
|
||||
}
|
||||
|
||||
m.Group("/config", func() {
|
||||
m.Get("", admin.Config)
|
||||
|
@ -1249,7 +1248,7 @@ func registerRoutes(m *web.Route) {
|
|||
Post(web.Bind(forms.UploadRepoFileForm{}), repo.UploadFilePost)
|
||||
m.Combo("/_diffpatch/*").Get(repo.NewDiffPatch).
|
||||
Post(web.Bind(forms.EditRepoFileForm{}), repo.NewDiffPatchPost)
|
||||
m.Combo("/_cherrypick/{sha:([a-f0-9]{7,64})}/*").Get(repo.CherryPick).
|
||||
m.Combo("/_cherrypick/{sha:([a-f0-9]{4,64})}/*").Get(repo.CherryPick).
|
||||
Post(web.Bind(forms.CherryPickForm{}), repo.CherryPickPost)
|
||||
}, repo.MustBeEditable)
|
||||
m.Group("", func() {
|
||||
|
@ -1334,6 +1333,24 @@ func registerRoutes(m *web.Route) {
|
|||
m.Get("/packages", repo.Packages)
|
||||
}
|
||||
|
||||
if setting.Badges.Enabled {
|
||||
m.Group("/badges", func() {
|
||||
m.Get("/workflows/{workflow_name}/badge.svg", badges.GetWorkflowBadge)
|
||||
m.Group("/issues", func() {
|
||||
m.Get(".svg", badges.GetTotalIssuesBadge)
|
||||
m.Get("/open.svg", badges.GetOpenIssuesBadge)
|
||||
m.Get("/closed.svg", badges.GetClosedIssuesBadge)
|
||||
})
|
||||
m.Group("/pulls", func() {
|
||||
m.Get(".svg", badges.GetTotalPullsBadge)
|
||||
m.Get("/open.svg", badges.GetOpenPullsBadge)
|
||||
m.Get("/closed.svg", badges.GetClosedPullsBadge)
|
||||
})
|
||||
m.Get("/stars.svg", badges.GetStarsBadge)
|
||||
m.Get("/release.svg", badges.GetLatestReleaseBadge)
|
||||
})
|
||||
}
|
||||
|
||||
m.Group("/projects", func() {
|
||||
m.Get("", repo.Projects)
|
||||
m.Get("/{id}", repo.ViewProject)
|
||||
|
@ -1365,23 +1382,28 @@ func registerRoutes(m *web.Route) {
|
|||
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
|
||||
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
|
||||
|
||||
m.Group("/runs/{run}", func() {
|
||||
m.Combo("").
|
||||
Get(actions.View).
|
||||
Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
|
||||
m.Group("/jobs/{job}", func() {
|
||||
m.Group("/runs", func() {
|
||||
m.Get("/latest", actions.ViewLatest)
|
||||
m.Group("/{run}", func() {
|
||||
m.Combo("").
|
||||
Get(actions.View).
|
||||
Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
|
||||
m.Group("/jobs/{job}", func() {
|
||||
m.Combo("").
|
||||
Get(actions.View).
|
||||
Post(web.Bind(actions.ViewRequest{}), actions.ViewPost)
|
||||
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
|
||||
m.Get("/logs", actions.Logs)
|
||||
})
|
||||
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
|
||||
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
|
||||
m.Post("/artifacts", actions.ArtifactsView)
|
||||
m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView)
|
||||
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
|
||||
m.Get("/logs", actions.Logs)
|
||||
})
|
||||
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
|
||||
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
|
||||
m.Post("/artifacts", actions.ArtifactsView)
|
||||
m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView)
|
||||
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
|
||||
})
|
||||
|
||||
m.Get("/workflows/{workflow_name}/badge.svg", badges.GetWorkflowBadge)
|
||||
}, reqRepoActionsReader, actions.MustEnableActions)
|
||||
|
||||
m.Group("/wiki", func() {
|
||||
|
@ -1391,8 +1413,8 @@ func registerRoutes(m *web.Route) {
|
|||
m.Combo("/*").
|
||||
Get(repo.Wiki).
|
||||
Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
|
||||
m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
|
||||
m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff)
|
||||
m.Get("/commit/{sha:[a-f0-9]{4,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
|
||||
m.Get("/commit/{sha:[a-f0-9]{4,64}}.{ext:patch|diff}", repo.RawDiff)
|
||||
}, repo.MustEnableWiki, func(ctx *context.Context) {
|
||||
ctx.Data["PageIsWiki"] = true
|
||||
ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink()
|
||||
|
@ -1452,7 +1474,7 @@ func registerRoutes(m *web.Route) {
|
|||
m.Group("/commits", func() {
|
||||
m.Get("", context.RepoRef(), repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits)
|
||||
m.Get("/list", context.RepoRef(), repo.GetPullCommits)
|
||||
m.Get("/{sha:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
|
||||
m.Get("/{sha:[a-f0-9]{4,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
|
||||
})
|
||||
m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), repo.MergePullRequest)
|
||||
m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest)
|
||||
|
@ -1461,8 +1483,8 @@ func registerRoutes(m *web.Route) {
|
|||
m.Post("/cleanup", context.RepoMustNotBeArchived(), context.RepoRef(), repo.CleanUpPullRequest)
|
||||
m.Group("/files", func() {
|
||||
m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForAllCommitsOfPr)
|
||||
m.Get("/{sha:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesStartingFromCommit)
|
||||
m.Get("/{shaFrom:[a-f0-9]{7,40}}..{shaTo:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange)
|
||||
m.Get("/{sha:[a-f0-9]{4,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesStartingFromCommit)
|
||||
m.Get("/{shaFrom:[a-f0-9]{4,40}}..{shaTo:[a-f0-9]{4,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange)
|
||||
m.Group("/reviews", func() {
|
||||
m.Get("/new_comment", repo.RenderNewCodeCommentForm)
|
||||
m.Post("/comments", web.Bind(forms.CodeCommentForm{}), repo.SetShowOutdatedComments, repo.CreateCodeComment)
|
||||
|
@ -1512,13 +1534,13 @@ func registerRoutes(m *web.Route) {
|
|||
|
||||
m.Group("", func() {
|
||||
m.Get("/graph", repo.Graph)
|
||||
m.Get("/commit/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
|
||||
m.Get("/commit/{sha:([a-f0-9]{7,64})$}/load-branches-and-tags", repo.LoadBranchesAndTags)
|
||||
m.Get("/cherry-pick/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
|
||||
m.Get("/commit/{sha:([a-f0-9]{4,64})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
|
||||
m.Get("/commit/{sha:([a-f0-9]{4,64})$}/load-branches-and-tags", repo.LoadBranchesAndTags)
|
||||
m.Get("/cherry-pick/{sha:([a-f0-9]{4,64})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
|
||||
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
|
||||
|
||||
m.Get("/rss/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
|
||||
m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed)
|
||||
m.Get("/rss/branch/*", repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed("rss"))
|
||||
m.Get("/atom/branch/*", repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed("atom"))
|
||||
|
||||
m.Group("/src", func() {
|
||||
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
|
||||
|
@ -1531,7 +1553,7 @@ func registerRoutes(m *web.Route) {
|
|||
m.Group("", func() {
|
||||
m.Get("/forks", repo.Forks)
|
||||
}, context.RepoRef(), reqRepoCodeReader)
|
||||
m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
|
||||
m.Get("/commit/{sha:([a-f0-9]{4,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
|
||||
}, ignSignIn, context.RepoAssignment, context.UnitTypes())
|
||||
|
||||
m.Post("/{username}/{reponame}/lastcommit/*", ignSignInAndCsrf, context.RepoAssignment, context.UnitTypes(), context.RepoRefByType(context.RepoRefCommit), reqRepoCodeReader, repo.LastCommit)
|
||||
|
@ -1568,6 +1590,13 @@ func registerRoutes(m *web.Route) {
|
|||
gitHTTPRouters(m)
|
||||
})
|
||||
})
|
||||
|
||||
if setting.Repository.EnableFlags {
|
||||
m.Group("/{username}/{reponame}/flags", func() {
|
||||
m.Get("", repo_flags.Manage)
|
||||
m.Post("", repo_flags.ManagePost)
|
||||
}, adminReq, context.RepoAssignment, context.UnitTypes())
|
||||
}
|
||||
// ***** END: Repository *****
|
||||
|
||||
m.Group("/notifications", func() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue