aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/serv.go89
-rw-r--r--models/renderhelper/commit_checker.go2
-rw-r--r--models/repo/repo.go27
-rw-r--r--models/repo/repo_test.go15
-rw-r--r--modules/git/cmdverb.go36
-rw-r--r--modules/private/serv.go10
-rw-r--r--modules/templates/util_date_test.go2
-rw-r--r--modules/templates/util_render.go21
-rw-r--r--modules/templates/util_render_legacy.go16
-rw-r--r--modules/templates/util_render_test.go128
-rw-r--r--options/locale/locale_pt-PT.ini4
-rw-r--r--package-lock.json8
-rw-r--r--package.json2
-rw-r--r--routers/private/serv.go8
-rw-r--r--routers/web/feed/convert.go4
-rw-r--r--routers/web/repo/actions/view.go6
-rw-r--r--templates/repo/branch/list.tmpl4
-rw-r--r--templates/repo/commit_page.tmpl4
-rw-r--r--templates/repo/commits_list.tmpl4
-rw-r--r--templates/repo/commits_list_small.tmpl4
-rw-r--r--templates/repo/diff/compare.tmpl2
-rw-r--r--templates/repo/graph/commits.tmpl2
-rw-r--r--templates/repo/issue/view_title.tmpl2
-rw-r--r--templates/repo/latest_commit.tmpl4
-rw-r--r--templates/repo/view_list.tmpl2
-rw-r--r--templates/user/dashboard/feeds.tmpl2
-rw-r--r--tests/integration/git_general_test.go40
-rw-r--r--web_src/css/editor/combomarkdowneditor.css64
-rw-r--r--web_src/css/features/expander.css96
-rw-r--r--web_src/css/features/tribute.css32
-rw-r--r--web_src/css/index.css2
-rw-r--r--web_src/js/features/comp/TextExpander.ts1
32 files changed, 358 insertions, 285 deletions
diff --git a/cmd/serv.go b/cmd/serv.go
index b18508459f..26a3af50f3 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -11,7 +11,6 @@ import (
"os"
"os/exec"
"path/filepath"
- "regexp"
"strconv"
"strings"
"time"
@@ -20,7 +19,7 @@ import (
asymkey_model "code.gitea.io/gitea/models/asymkey"
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/perm"
- "code.gitea.io/gitea/modules/container"
+ "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/lfstransfer"
@@ -37,14 +36,6 @@ import (
"github.com/urfave/cli/v2"
)
-const (
- verbUploadPack = "git-upload-pack"
- verbUploadArchive = "git-upload-archive"
- verbReceivePack = "git-receive-pack"
- verbLfsAuthenticate = "git-lfs-authenticate"
- verbLfsTransfer = "git-lfs-transfer"
-)
-
// CmdServ represents the available serv sub-command.
var CmdServ = &cli.Command{
Name: "serv",
@@ -78,22 +69,6 @@ func setup(ctx context.Context, debug bool) {
}
}
-var (
- // keep getAccessMode() in sync
- allowedCommands = container.SetOf(
- verbUploadPack,
- verbUploadArchive,
- verbReceivePack,
- verbLfsAuthenticate,
- verbLfsTransfer,
- )
- allowedCommandsLfs = container.SetOf(
- verbLfsAuthenticate,
- verbLfsTransfer,
- )
- alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
-)
-
// fail prints message to stdout, it's mainly used for git serv and git hook commands.
// The output will be passed to git client and shown to user.
func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error {
@@ -139,19 +114,20 @@ func handleCliResponseExtra(extra private.ResponseExtra) error {
func getAccessMode(verb, lfsVerb string) perm.AccessMode {
switch verb {
- case verbUploadPack, verbUploadArchive:
+ case git.CmdVerbUploadPack, git.CmdVerbUploadArchive:
return perm.AccessModeRead
- case verbReceivePack:
+ case git.CmdVerbReceivePack:
return perm.AccessModeWrite
- case verbLfsAuthenticate, verbLfsTransfer:
+ case git.CmdVerbLfsAuthenticate, git.CmdVerbLfsTransfer:
switch lfsVerb {
- case "upload":
+ case git.CmdSubVerbLfsUpload:
return perm.AccessModeWrite
- case "download":
+ case git.CmdSubVerbLfsDownload:
return perm.AccessModeRead
}
}
// should be unreachable
+ setting.PanicInDevOrTesting("unknown verb: %s %s", verb, lfsVerb)
return perm.AccessModeNone
}
@@ -230,12 +206,12 @@ func runServ(c *cli.Context) error {
log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
}
- words, err := shellquote.Split(cmd)
+ sshCmdArgs, err := shellquote.Split(cmd)
if err != nil {
return fail(ctx, "Error parsing arguments", "Failed to parse arguments: %v", err)
}
- if len(words) < 2 {
+ if len(sshCmdArgs) < 2 {
if git.DefaultFeatures().SupportProcReceive {
// for AGit Flow
if cmd == "ssh_info" {
@@ -246,25 +222,21 @@ func runServ(c *cli.Context) error {
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
}
- verb := words[0]
- repoPath := strings.TrimPrefix(words[1], "/")
-
- var lfsVerb string
-
- rr := strings.SplitN(repoPath, "/", 2)
- if len(rr) != 2 {
+ repoPath := strings.TrimPrefix(sshCmdArgs[1], "/")
+ repoPathFields := strings.SplitN(repoPath, "/", 2)
+ if len(repoPathFields) != 2 {
return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath)
}
- username := rr[0]
- reponame := strings.TrimSuffix(rr[1], ".git")
+ username := repoPathFields[0]
+ reponame := strings.TrimSuffix(repoPathFields[1], ".git") // “the-repo-name" or "the-repo-name.wiki"
// LowerCase and trim the repoPath as that's how they are stored.
// This should be done after splitting the repoPath into username and reponame
// so that username and reponame are not affected.
repoPath = strings.ToLower(strings.TrimSpace(repoPath))
- if alphaDashDotPattern.MatchString(reponame) {
+ if !repo.IsValidSSHAccessRepoName(reponame) {
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
}
@@ -286,22 +258,23 @@ func runServ(c *cli.Context) error {
}()
}
- if allowedCommands.Contains(verb) {
- if allowedCommandsLfs.Contains(verb) {
- if !setting.LFS.StartServer {
- return fail(ctx, "LFS Server is not enabled", "")
- }
- if verb == verbLfsTransfer && !setting.LFS.AllowPureSSH {
- return fail(ctx, "LFS SSH transfer is not enabled", "")
- }
- if len(words) > 2 {
- lfsVerb = words[2]
- }
- }
- } else {
+ verb, lfsVerb := sshCmdArgs[0], ""
+ if !git.IsAllowedVerbForServe(verb) {
return fail(ctx, "Unknown git command", "Unknown git command %s", verb)
}
+ if git.IsAllowedVerbForServeLfs(verb) {
+ if !setting.LFS.StartServer {
+ return fail(ctx, "LFS Server is not enabled", "")
+ }
+ if verb == git.CmdVerbLfsTransfer && !setting.LFS.AllowPureSSH {
+ return fail(ctx, "LFS SSH transfer is not enabled", "")
+ }
+ if len(sshCmdArgs) > 2 {
+ lfsVerb = sshCmdArgs[2]
+ }
+ }
+
requestedMode := getAccessMode(verb, lfsVerb)
results, extra := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
@@ -310,7 +283,7 @@ func runServ(c *cli.Context) error {
}
// LFS SSH protocol
- if verb == verbLfsTransfer {
+ if verb == git.CmdVerbLfsTransfer {
token, err := getLFSAuthToken(ctx, lfsVerb, results)
if err != nil {
return err
@@ -319,7 +292,7 @@ func runServ(c *cli.Context) error {
}
// LFS token authentication
- if verb == verbLfsAuthenticate {
+ if verb == git.CmdVerbLfsAuthenticate {
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
token, err := getLFSAuthToken(ctx, lfsVerb, results)
diff --git a/models/renderhelper/commit_checker.go b/models/renderhelper/commit_checker.go
index 4815643e67..407e45fb54 100644
--- a/models/renderhelper/commit_checker.go
+++ b/models/renderhelper/commit_checker.go
@@ -47,7 +47,7 @@ func (c *commitChecker) IsCommitIDExisting(commitID string) bool {
c.gitRepo, c.gitRepoCloser = r, closer
}
- exist = c.gitRepo.IsReferenceExist(commitID) // Don't use IsObjectExist since it doesn't support short hashs with gogit edition.
+ exist = c.gitRepo.IsReferenceExist(commitID) // Don't use IsObjectExist since it doesn't support short hashes with gogit edition.
c.commitCache[commitID] = exist
return exist
}
diff --git a/models/repo/repo.go b/models/repo/repo.go
index 2977dfb9f1..5aae02c6d8 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -64,18 +64,18 @@ func (err ErrRepoIsArchived) Error() string {
}
type globalVarsStruct struct {
- validRepoNamePattern *regexp.Regexp
- invalidRepoNamePattern *regexp.Regexp
- reservedRepoNames []string
- reservedRepoPatterns []string
+ validRepoNamePattern *regexp.Regexp
+ invalidRepoNamePattern *regexp.Regexp
+ reservedRepoNames []string
+ reservedRepoNamePatterns []string
}
var globalVars = sync.OnceValue(func() *globalVarsStruct {
return &globalVarsStruct{
- validRepoNamePattern: regexp.MustCompile(`[-.\w]+`),
- invalidRepoNamePattern: regexp.MustCompile(`[.]{2,}`),
- reservedRepoNames: []string{".", "..", "-"},
- reservedRepoPatterns: []string{"*.git", "*.wiki", "*.rss", "*.atom"},
+ validRepoNamePattern: regexp.MustCompile(`^[-.\w]+$`),
+ invalidRepoNamePattern: regexp.MustCompile(`[.]{2,}`),
+ reservedRepoNames: []string{".", "..", "-"},
+ reservedRepoNamePatterns: []string{"*.wiki", "*.git", "*.rss", "*.atom"},
}
})
@@ -86,7 +86,16 @@ func IsUsableRepoName(name string) error {
// Note: usually this error is normally caught up earlier in the UI
return db.ErrNameCharsNotAllowed{Name: name}
}
- return db.IsUsableName(vars.reservedRepoNames, vars.reservedRepoPatterns, name)
+ return db.IsUsableName(vars.reservedRepoNames, vars.reservedRepoNamePatterns, name)
+}
+
+// IsValidSSHAccessRepoName is like IsUsableRepoName, but it allows "*.wiki" because wiki repo needs to be accessed in SSH code
+func IsValidSSHAccessRepoName(name string) bool {
+ vars := globalVars()
+ if !vars.validRepoNamePattern.MatchString(name) || vars.invalidRepoNamePattern.MatchString(name) {
+ return false
+ }
+ return db.IsUsableName(vars.reservedRepoNames, vars.reservedRepoNamePatterns[1:], name) == nil
}
// TrustModelType defines the types of trust model for this repository
diff --git a/models/repo/repo_test.go b/models/repo/repo_test.go
index b2604ab575..66abe864fc 100644
--- a/models/repo/repo_test.go
+++ b/models/repo/repo_test.go
@@ -216,8 +216,23 @@ func TestIsUsableRepoName(t *testing.T) {
assert.Error(t, IsUsableRepoName("-"))
assert.Error(t, IsUsableRepoName("🌞"))
+ assert.Error(t, IsUsableRepoName("the/repo"))
assert.Error(t, IsUsableRepoName("the..repo"))
assert.Error(t, IsUsableRepoName("foo.wiki"))
assert.Error(t, IsUsableRepoName("foo.git"))
assert.Error(t, IsUsableRepoName("foo.RSS"))
}
+
+func TestIsValidSSHAccessRepoName(t *testing.T) {
+ assert.True(t, IsValidSSHAccessRepoName("a"))
+ assert.True(t, IsValidSSHAccessRepoName("-1_."))
+ assert.True(t, IsValidSSHAccessRepoName(".profile"))
+ assert.True(t, IsValidSSHAccessRepoName("foo.wiki"))
+
+ assert.False(t, IsValidSSHAccessRepoName("-"))
+ assert.False(t, IsValidSSHAccessRepoName("🌞"))
+ assert.False(t, IsValidSSHAccessRepoName("the/repo"))
+ assert.False(t, IsValidSSHAccessRepoName("the..repo"))
+ assert.False(t, IsValidSSHAccessRepoName("foo.git"))
+ assert.False(t, IsValidSSHAccessRepoName("foo.RSS"))
+}
diff --git a/modules/git/cmdverb.go b/modules/git/cmdverb.go
new file mode 100644
index 0000000000..3d6f4ae0c6
--- /dev/null
+++ b/modules/git/cmdverb.go
@@ -0,0 +1,36 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package git
+
+const (
+ CmdVerbUploadPack = "git-upload-pack"
+ CmdVerbUploadArchive = "git-upload-archive"
+ CmdVerbReceivePack = "git-receive-pack"
+ CmdVerbLfsAuthenticate = "git-lfs-authenticate"
+ CmdVerbLfsTransfer = "git-lfs-transfer"
+
+ CmdSubVerbLfsUpload = "upload"
+ CmdSubVerbLfsDownload = "download"
+)
+
+func IsAllowedVerbForServe(verb string) bool {
+ switch verb {
+ case CmdVerbUploadPack,
+ CmdVerbUploadArchive,
+ CmdVerbReceivePack,
+ CmdVerbLfsAuthenticate,
+ CmdVerbLfsTransfer:
+ return true
+ }
+ return false
+}
+
+func IsAllowedVerbForServeLfs(verb string) bool {
+ switch verb {
+ case CmdVerbLfsAuthenticate,
+ CmdVerbLfsTransfer:
+ return true
+ }
+ return false
+}
diff --git a/modules/private/serv.go b/modules/private/serv.go
index 10e9f7995c..b1dafbd81b 100644
--- a/modules/private/serv.go
+++ b/modules/private/serv.go
@@ -46,18 +46,16 @@ type ServCommandResults struct {
}
// ServCommand preps for a serv call
-func ServCommand(ctx context.Context, keyID int64, ownerName, repoName string, mode perm.AccessMode, verbs ...string) (*ServCommandResults, ResponseExtra) {
+func ServCommand(ctx context.Context, keyID int64, ownerName, repoName string, mode perm.AccessMode, verb, lfsVerb string) (*ServCommandResults, ResponseExtra) {
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/serv/command/%d/%s/%s?mode=%d",
keyID,
url.PathEscape(ownerName),
url.PathEscape(repoName),
mode,
)
- for _, verb := range verbs {
- if verb != "" {
- reqURL += "&verb=" + url.QueryEscape(verb)
- }
- }
+ reqURL += "&verb=" + url.QueryEscape(verb)
+ // reqURL += "&lfs_verb=" + url.QueryEscape(lfsVerb) // TODO: actually there is no use of this parameter. In the future, the URL construction should be more flexible
+ _ = lfsVerb
req := newInternalRequestAPI(ctx, reqURL, "GET")
return requestJSONResp(req, &ServCommandResults{})
}
diff --git a/modules/templates/util_date_test.go b/modules/templates/util_date_test.go
index f3a2409a9f..9015462bbb 100644
--- a/modules/templates/util_date_test.go
+++ b/modules/templates/util_date_test.go
@@ -17,6 +17,7 @@ import (
func TestDateTime(t *testing.T) {
testTz, _ := time.LoadLocation("America/New_York")
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
+ defer test.MockVariableValue(&setting.IsProd, true)()
defer test.MockVariableValue(&setting.IsInTesting, false)()
du := NewDateUtils()
@@ -53,6 +54,7 @@ func TestDateTime(t *testing.T) {
func TestTimeSince(t *testing.T) {
testTz, _ := time.LoadLocation("America/New_York")
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
+ defer test.MockVariableValue(&setting.IsProd, true)()
defer test.MockVariableValue(&setting.IsInTesting, false)()
du := NewDateUtils()
diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go
index 521233db40..8d9ba1000c 100644
--- a/modules/templates/util_render.go
+++ b/modules/templates/util_render.go
@@ -14,6 +14,8 @@ import (
"unicode"
issues_model "code.gitea.io/gitea/models/issues"
+ "code.gitea.io/gitea/models/renderhelper"
+ "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/htmlutil"
"code.gitea.io/gitea/modules/log"
@@ -34,11 +36,11 @@ func NewRenderUtils(ctx reqctx.RequestContext) *RenderUtils {
}
// RenderCommitMessage renders commit message with XSS-safe and special links.
-func (ut *RenderUtils) RenderCommitMessage(msg string, metas map[string]string) template.HTML {
+func (ut *RenderUtils) RenderCommitMessage(msg string, repo *repo.Repository) template.HTML {
cleanMsg := template.HTMLEscapeString(msg)
// we can safely assume that it will not return any error, since there
// shouldn't be any special HTML.
- fullMessage, err := markup.PostProcessCommitMessage(markup.NewRenderContext(ut.ctx).WithMetas(metas), cleanMsg)
+ fullMessage, err := markup.PostProcessCommitMessage(renderhelper.NewRenderContextRepoComment(ut.ctx, repo), cleanMsg)
if err != nil {
log.Error("PostProcessCommitMessage: %v", err)
return ""
@@ -52,7 +54,7 @@ func (ut *RenderUtils) RenderCommitMessage(msg string, metas map[string]string)
// RenderCommitMessageLinkSubject renders commit message as a XSS-safe link to
// the provided default url, handling for special links without email to links.
-func (ut *RenderUtils) RenderCommitMessageLinkSubject(msg, urlDefault string, metas map[string]string) template.HTML {
+func (ut *RenderUtils) RenderCommitMessageLinkSubject(msg, urlDefault string, repo *repo.Repository) template.HTML {
msgLine := strings.TrimLeftFunc(msg, unicode.IsSpace)
lineEnd := strings.IndexByte(msgLine, '\n')
if lineEnd > 0 {
@@ -63,9 +65,8 @@ func (ut *RenderUtils) RenderCommitMessageLinkSubject(msg, urlDefault string, me
return ""
}
- // we can safely assume that it will not return any error, since there
- // shouldn't be any special HTML.
- renderedMessage, err := markup.PostProcessCommitMessageSubject(markup.NewRenderContext(ut.ctx).WithMetas(metas), urlDefault, template.HTMLEscapeString(msgLine))
+ // we can safely assume that it will not return any error, since there shouldn't be any special HTML.
+ renderedMessage, err := markup.PostProcessCommitMessageSubject(renderhelper.NewRenderContextRepoComment(ut.ctx, repo), urlDefault, template.HTMLEscapeString(msgLine))
if err != nil {
log.Error("PostProcessCommitMessageSubject: %v", err)
return ""
@@ -74,7 +75,7 @@ func (ut *RenderUtils) RenderCommitMessageLinkSubject(msg, urlDefault string, me
}
// RenderCommitBody extracts the body of a commit message without its title.
-func (ut *RenderUtils) RenderCommitBody(msg string, metas map[string]string) template.HTML {
+func (ut *RenderUtils) RenderCommitBody(msg string, repo *repo.Repository) template.HTML {
msgLine := strings.TrimSpace(msg)
lineEnd := strings.IndexByte(msgLine, '\n')
if lineEnd > 0 {
@@ -87,7 +88,7 @@ func (ut *RenderUtils) RenderCommitBody(msg string, metas map[string]string) tem
return ""
}
- renderedMessage, err := markup.PostProcessCommitMessage(markup.NewRenderContext(ut.ctx).WithMetas(metas), template.HTMLEscapeString(msgLine))
+ renderedMessage, err := markup.PostProcessCommitMessage(renderhelper.NewRenderContextRepoComment(ut.ctx, repo), template.HTMLEscapeString(msgLine))
if err != nil {
log.Error("PostProcessCommitMessage: %v", err)
return ""
@@ -105,8 +106,8 @@ func renderCodeBlock(htmlEscapedTextToRender template.HTML) template.HTML {
}
// RenderIssueTitle renders issue/pull title with defined post processors
-func (ut *RenderUtils) RenderIssueTitle(text string, metas map[string]string) template.HTML {
- renderedText, err := markup.PostProcessIssueTitle(markup.NewRenderContext(ut.ctx).WithMetas(metas), template.HTMLEscapeString(text))
+func (ut *RenderUtils) RenderIssueTitle(text string, repo *repo.Repository) template.HTML {
+ renderedText, err := markup.PostProcessIssueTitle(renderhelper.NewRenderContextRepoComment(ut.ctx, repo), template.HTMLEscapeString(text))
if err != nil {
log.Error("PostProcessIssueTitle: %v", err)
return ""
diff --git a/modules/templates/util_render_legacy.go b/modules/templates/util_render_legacy.go
index 8f7b84c83d..df8f5e64de 100644
--- a/modules/templates/util_render_legacy.go
+++ b/modules/templates/util_render_legacy.go
@@ -32,22 +32,22 @@ func renderMarkdownToHtmlLegacy(ctx context.Context, input string) template.HTML
return NewRenderUtils(reqctx.FromContext(ctx)).MarkdownToHtml(input)
}
-func renderCommitMessageLegacy(ctx context.Context, msg string, metas map[string]string) template.HTML {
+func renderCommitMessageLegacy(ctx context.Context, msg string, _ map[string]string) template.HTML {
panicIfDevOrTesting()
- return NewRenderUtils(reqctx.FromContext(ctx)).RenderCommitMessage(msg, metas)
+ return NewRenderUtils(reqctx.FromContext(ctx)).RenderCommitMessage(msg, nil)
}
-func renderCommitMessageLinkSubjectLegacy(ctx context.Context, msg, urlDefault string, metas map[string]string) template.HTML {
+func renderCommitMessageLinkSubjectLegacy(ctx context.Context, msg, urlDefault string, _ map[string]string) template.HTML {
panicIfDevOrTesting()
- return NewRenderUtils(reqctx.FromContext(ctx)).RenderCommitMessageLinkSubject(msg, urlDefault, metas)
+ return NewRenderUtils(reqctx.FromContext(ctx)).RenderCommitMessageLinkSubject(msg, urlDefault, nil)
}
-func renderIssueTitleLegacy(ctx context.Context, text string, metas map[string]string) template.HTML {
+func renderIssueTitleLegacy(ctx context.Context, text string, _ map[string]string) template.HTML {
panicIfDevOrTesting()
- return NewRenderUtils(reqctx.FromContext(ctx)).RenderIssueTitle(text, metas)
+ return NewRenderUtils(reqctx.FromContext(ctx)).RenderIssueTitle(text, nil)
}
-func renderCommitBodyLegacy(ctx context.Context, msg string, metas map[string]string) template.HTML {
+func renderCommitBodyLegacy(ctx context.Context, msg string, _ map[string]string) template.HTML {
panicIfDevOrTesting()
- return NewRenderUtils(reqctx.FromContext(ctx)).RenderCommitBody(msg, metas)
+ return NewRenderUtils(reqctx.FromContext(ctx)).RenderCommitBody(msg, nil)
}
diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go
index 460b9dc190..9b51d0cd57 100644
--- a/modules/templates/util_render_test.go
+++ b/modules/templates/util_render_test.go
@@ -11,11 +11,11 @@ import (
"testing"
"code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/models/repo"
+ user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/reqctx"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/translation"
@@ -47,19 +47,8 @@ mail@domain.com
return strings.ReplaceAll(s, "<SPACE>", " ")
}
-var testMetas = map[string]string{
- "user": "user13",
- "repo": "repo11",
- "repoPath": "../../tests/gitea-repositories-meta/user13/repo11.git/",
- "markdownNewLineHardBreak": "true",
- "markupAllowShortIssuePattern": "true",
-}
-
func TestMain(m *testing.M) {
- unittest.InitSettingsForTesting()
- if err := git.InitSimple(context.Background()); err != nil {
- log.Fatal("git init failed, err: %v", err)
- }
+ setting.Markdown.RenderOptionsComment.ShortIssuePattern = true
markup.Init(&markup.RenderHelperFuncs{
IsUsernameMentionable: func(ctx context.Context, username string) bool {
return username == "mention-user"
@@ -74,46 +63,52 @@ func newTestRenderUtils(t *testing.T) *RenderUtils {
return NewRenderUtils(ctx)
}
-func TestRenderCommitBody(t *testing.T) {
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
- type args struct {
- msg string
+func TestRenderRepoComment(t *testing.T) {
+ mockRepo := &repo.Repository{
+ ID: 1, OwnerName: "user13", Name: "repo11",
+ Owner: &user_model.User{ID: 13, Name: "user13"},
+ Units: []*repo.RepoUnit{},
}
- tests := []struct {
- name string
- args args
- want template.HTML
- }{
- {
- name: "multiple lines",
- args: args{
- msg: "first line\nsecond line",
+ t.Run("RenderCommitBody", func(t *testing.T) {
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
+ type args struct {
+ msg string
+ }
+ tests := []struct {
+ name string
+ args args
+ want template.HTML
+ }{
+ {
+ name: "multiple lines",
+ args: args{
+ msg: "first line\nsecond line",
+ },
+ want: "second line",
},
- want: "second line",
- },
- {
- name: "multiple lines with leading newlines",
- args: args{
- msg: "\n\n\n\nfirst line\nsecond line",
+ {
+ name: "multiple lines with leading newlines",
+ args: args{
+ msg: "\n\n\n\nfirst line\nsecond line",
+ },
+ want: "second line",
},
- want: "second line",
- },
- {
- name: "multiple lines with trailing newlines",
- args: args{
- msg: "first line\nsecond line\n\n\n",
+ {
+ name: "multiple lines with trailing newlines",
+ args: args{
+ msg: "first line\nsecond line\n\n\n",
+ },
+ want: "second line",
},
- want: "second line",
- },
- }
- ut := newTestRenderUtils(t)
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- assert.Equalf(t, tt.want, ut.RenderCommitBody(tt.args.msg, nil), "RenderCommitBody(%v, %v)", tt.args.msg, nil)
- })
- }
-
- expected := `/just/a/path.bin
+ }
+ ut := newTestRenderUtils(t)
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.Equalf(t, tt.want, ut.RenderCommitBody(tt.args.msg, mockRepo), "RenderCommitBody(%v, %v)", tt.args.msg, nil)
+ })
+ }
+
+ expected := `/just/a/path.bin
<a href="https://example.com/file.bin">https://example.com/file.bin</a>
[local link](file.bin)
[remote link](<a href="https://example.com">https://example.com</a>)
@@ -132,22 +127,22 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
<a href="/mention-user">@mention-user</a> test
<a href="/user13/repo11/issues/123" class="ref-issue">#123</a>
space`
- assert.Equal(t, expected, string(newTestRenderUtils(t).RenderCommitBody(testInput(), testMetas)))
-}
+ assert.Equal(t, expected, string(newTestRenderUtils(t).RenderCommitBody(testInput(), mockRepo)))
+ })
-func TestRenderCommitMessage(t *testing.T) {
- expected := `space <a href="/mention-user" data-markdown-generated-content="">@mention-user</a> `
- assert.EqualValues(t, expected, newTestRenderUtils(t).RenderCommitMessage(testInput(), testMetas))
-}
+ t.Run("RenderCommitMessage", func(t *testing.T) {
+ expected := `space <a href="/mention-user" data-markdown-generated-content="">@mention-user</a> `
+ assert.EqualValues(t, expected, newTestRenderUtils(t).RenderCommitMessage(testInput(), mockRepo))
+ })
-func TestRenderCommitMessageLinkSubject(t *testing.T) {
- expected := `<a href="https://example.com/link" class="muted">space </a><a href="/mention-user" data-markdown-generated-content="">@mention-user</a>`
- assert.EqualValues(t, expected, newTestRenderUtils(t).RenderCommitMessageLinkSubject(testInput(), "https://example.com/link", testMetas))
-}
+ t.Run("RenderCommitMessageLinkSubject", func(t *testing.T) {
+ expected := `<a href="https://example.com/link" class="muted">space </a><a href="/mention-user" data-markdown-generated-content="">@mention-user</a>`
+ assert.EqualValues(t, expected, newTestRenderUtils(t).RenderCommitMessageLinkSubject(testInput(), "https://example.com/link", mockRepo))
+ })
-func TestRenderIssueTitle(t *testing.T) {
- defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
- expected := ` space @mention-user<SPACE><SPACE>
+ t.Run("RenderIssueTitle", func(t *testing.T) {
+ defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
+ expected := ` space @mention-user<SPACE><SPACE>
/just/a/path.bin
https://example.com/file.bin
[local link](file.bin)
@@ -168,8 +163,9 @@ mail@domain.com
<a href="/user13/repo11/issues/123" class="ref-issue">#123</a>
space<SPACE><SPACE>
`
- expected = strings.ReplaceAll(expected, "<SPACE>", " ")
- assert.Equal(t, expected, string(newTestRenderUtils(t).RenderIssueTitle(testInput(), testMetas)))
+ expected = strings.ReplaceAll(expected, "<SPACE>", " ")
+ assert.Equal(t, expected, string(newTestRenderUtils(t).RenderIssueTitle(testInput(), mockRepo)))
+ })
}
func TestRenderMarkdownToHtml(t *testing.T) {
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 2f04452a35..b47b61f6bd 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -3728,7 +3728,11 @@ creation.name_placeholder=Só sublinhados ou alfanuméricos sem distinguir maiú
creation.value_placeholder=Insira um conteúdo qualquer. Espaços em branco no início ou no fim serão omitidos.
creation.description_placeholder=Escreva uma descrição curta (opcional).
+save_success=O segredo "%s" foi guardado.
+save_failed=Falhou ao guardar o segredo.
+add_secret=Adicionar segredo
+edit_secret=Editar segredo
deletion=Remover segredo
deletion.description=Remover um segredo é permanente e não pode ser revertido. Continuar?
deletion.success=O segredo foi removido.
diff --git a/package-lock.json b/package-lock.json
index 4c5963d0c8..e61fe3472d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
"@citation-js/plugin-csl": "0.7.18",
"@citation-js/plugin-software-formats": "0.6.1",
"@github/markdown-toolbar-element": "2.2.3",
- "@github/relative-time-element": "4.4.7",
+ "@github/relative-time-element": "4.4.8",
"@github/text-expander-element": "2.9.1",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "19.15.1",
@@ -1146,9 +1146,9 @@
"license": "MIT"
},
"node_modules/@github/relative-time-element": {
- "version": "4.4.7",
- "resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.4.7.tgz",
- "integrity": "sha512-NZCePEFYtV7qAUI/pHYuqZ8vRhcsfH/dziUZTY9YR5+JwzDCWtEokYSDbDLZjrRl+SAFr02YHUK+UdtP6hPcbQ==",
+ "version": "4.4.8",
+ "resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.4.8.tgz",
+ "integrity": "sha512-FSLYm6F3TSQnqHE1EMQUVVgi2XjbCvsESwwXfugHFpBnhyF1uhJOtu0Psp/BB/qqazfdkk7f5fVcu7WuXl3t8Q==",
"license": "MIT"
},
"node_modules/@github/text-expander-element": {
diff --git a/package.json b/package.json
index 0202b92ff4..bc2c0c87f3 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"@citation-js/plugin-csl": "0.7.18",
"@citation-js/plugin-software-formats": "0.6.1",
"@github/markdown-toolbar-element": "2.2.3",
- "@github/relative-time-element": "4.4.7",
+ "@github/relative-time-element": "4.4.8",
"@github/text-expander-element": "2.9.1",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "19.15.1",
diff --git a/routers/private/serv.go b/routers/private/serv.go
index 37fbc0730c..b879be0dc2 100644
--- a/routers/private/serv.go
+++ b/routers/private/serv.go
@@ -81,6 +81,7 @@ func ServCommand(ctx *context.PrivateContext) {
ownerName := ctx.PathParam("owner")
repoName := ctx.PathParam("repo")
mode := perm.AccessMode(ctx.FormInt("mode"))
+ verb := ctx.FormString("verb")
// Set the basic parts of the results to return
results := private.ServCommandResults{
@@ -295,8 +296,11 @@ func ServCommand(ctx *context.PrivateContext) {
return
}
} else {
- // Because of the special ref "refs/for" we will need to delay write permission check
- if git.DefaultFeatures().SupportProcReceive && unitType == unit.TypeCode {
+ // Because of the special ref "refs/for" (AGit) we will need to delay write permission check,
+ // AGit flow needs to write its own ref when the doer has "reader" permission (allowing to create PR).
+ // The real permission check is done in HookPreReceive (routers/private/hook_pre_receive.go).
+ // Here it should relax the permission check for "git push (git-receive-pack)", but not for others like LFS operations.
+ if git.DefaultFeatures().SupportProcReceive && unitType == unit.TypeCode && verb == git.CmdVerbReceivePack {
mode = perm.AccessModeRead
}
diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go
index b04855fa6a..7c59132841 100644
--- a/routers/web/feed/convert.go
+++ b/routers/web/feed/convert.go
@@ -201,7 +201,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
switch act.OpType {
case activities_model.ActionCommitRepo, activities_model.ActionMirrorSyncPush:
push := templates.ActionContent2Commits(act)
-
+ _ = act.LoadRepo(ctx)
for _, commit := range push.Commits {
if len(desc) != 0 {
desc += "\n\n"
@@ -209,7 +209,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s",
html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(ctx), commit.Sha1)),
commit.Sha1,
- renderUtils.RenderCommitMessage(commit.Message, nil),
+ renderUtils.RenderCommitMessage(commit.Message, act.Repo),
)
}
diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go
index 2ec6389263..dd18c8380d 100644
--- a/routers/web/repo/actions/view.go
+++ b/routers/web/repo/actions/view.go
@@ -200,13 +200,9 @@ func ViewPost(ctx *context_module.Context) {
}
}
- // TODO: "ComposeCommentMetas" (usually for comment) is not quite right, but it is still the same as what template "RenderCommitMessage" does.
- // need to be refactored together in the future
- metas := ctx.Repo.Repository.ComposeCommentMetas(ctx)
-
// the title for the "run" is from the commit message
resp.State.Run.Title = run.Title
- resp.State.Run.TitleHTML = templates.NewRenderUtils(ctx).RenderCommitMessage(run.Title, metas)
+ resp.State.Run.TitleHTML = templates.NewRenderUtils(ctx).RenderCommitMessage(run.Title, ctx.Repo.Repository)
resp.State.Run.Link = run.Link()
resp.State.Run.CanCancel = !run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions)
resp.State.Run.CanApprove = run.NeedApproval && ctx.Repo.CanWrite(unit.TypeActions)
diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl
index aa38413ad7..fffe3a08cc 100644
--- a/templates/repo/branch/list.tmpl
+++ b/templates/repo/branch/list.tmpl
@@ -27,7 +27,7 @@
<button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DefaultBranchBranch.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_branch"}}">{{svg "octicon-copy" 14}}</button>
{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DefaultBranchBranch.DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DefaultBranchBranch.DBBranch.CommitID)}}
</div>
- <p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{ctx.RenderUtils.RenderCommitMessage .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeCommentMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{DateUtils.TimeSince .DefaultBranchBranch.DBBranch.CommitTime}}{{if .DefaultBranchBranch.DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
+ <p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{ctx.RenderUtils.RenderCommitMessage .DefaultBranchBranch.DBBranch.CommitMessage .Repository}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{DateUtils.TimeSince .DefaultBranchBranch.DBBranch.CommitTime}}{{if .DefaultBranchBranch.DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
</td>
{{/* FIXME: here and below, the tw-overflow-visible is not quite right but it is still needed the moment: to show the important buttons when the width is narrow */}}
<td class="tw-text-right tw-overflow-visible">
@@ -103,7 +103,7 @@
<button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_branch"}}">{{svg "octicon-copy" 14}}</button>
{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DBBranch.CommitID)}}
</div>
- <p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{ctx.RenderUtils.RenderCommitMessage .DBBranch.CommitMessage ($.Repository.ComposeCommentMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{DateUtils.TimeSince .DBBranch.CommitTime}}{{if .DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} &nbsp;{{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
+ <p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{ctx.RenderUtils.RenderCommitMessage .DBBranch.CommitMessage $.Repository}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{DateUtils.TimeSince .DBBranch.CommitTime}}{{if .DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} &nbsp;{{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
{{end}}
</td>
<td class="two wide ui">
diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl
index 5639c87a82..7abd377108 100644
--- a/templates/repo/commit_page.tmpl
+++ b/templates/repo/commit_page.tmpl
@@ -5,7 +5,7 @@
<div class="ui container fluid padded">
<div class="ui top attached header clearing segment tw-relative commit-header">
<div class="tw-flex tw-mb-4 tw-gap-1">
- <h3 class="tw-mb-0 tw-flex-1"><span class="commit-summary" title="{{.Commit.Summary}}">{{ctx.RenderUtils.RenderCommitMessage .Commit.Message ($.Repository.ComposeCommentMetas ctx)}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}</h3>
+ <h3 class="tw-mb-0 tw-flex-1"><span class="commit-summary" title="{{.Commit.Summary}}">{{ctx.RenderUtils.RenderCommitMessage .Commit.Message $.Repository}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}</h3>
{{if not $.PageIsWiki}}
<div class="commit-header-buttons">
<a class="ui primary tiny button" href="{{.SourcePath}}">
@@ -122,7 +122,7 @@
{{end}}
</div>
{{if IsMultilineCommitMessage .Commit.Message}}
- <pre class="commit-body">{{ctx.RenderUtils.RenderCommitBody .Commit.Message ($.Repository.ComposeCommentMetas ctx)}}</pre>
+ <pre class="commit-body">{{ctx.RenderUtils.RenderCommitBody .Commit.Message $.Repository}}</pre>
{{end}}
{{template "repo/commit_load_branches_and_tags" .}}
</div>
diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl
index 17c7240ee4..8a268a5d14 100644
--- a/templates/repo/commits_list.tmpl
+++ b/templates/repo/commits_list.tmpl
@@ -44,7 +44,7 @@
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{.Summary | ctx.RenderUtils.RenderEmoji}}</span>
{{else}}
{{$commitLink:= printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
- <span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{ctx.RenderUtils.RenderCommitMessageLinkSubject .Message $commitLink ($.Repository.ComposeCommentMetas ctx)}}</span>
+ <span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{ctx.RenderUtils.RenderCommitMessageLinkSubject .Message $commitLink $.Repository}}</span>
{{end}}
</span>
{{if IsMultilineCommitMessage .Message}}
@@ -52,7 +52,7 @@
{{end}}
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
{{if IsMultilineCommitMessage .Message}}
- <pre class="commit-body tw-hidden">{{ctx.RenderUtils.RenderCommitBody .Message ($.Repository.ComposeCommentMetas ctx)}}</pre>
+ <pre class="commit-body tw-hidden">{{ctx.RenderUtils.RenderCommitBody .Message $.Repository}}</pre>
{{end}}
{{if $.CommitsTagsMap}}
{{range (index $.CommitsTagsMap .ID.String)}}
diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl
index b054ce19a5..ee94ad7e58 100644
--- a/templates/repo/commits_list_small.tmpl
+++ b/templates/repo/commits_list_small.tmpl
@@ -15,7 +15,7 @@
{{$commitLink:= printf "%s/%s" $commitBaseLink (PathEscape .ID.String)}}
<span class="tw-flex-1 tw-font-mono gt-ellipsis" title="{{.Summary}}">
- {{- ctx.RenderUtils.RenderCommitMessageLinkSubject .Message $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeCommentMetas ctx) -}}
+ {{- ctx.RenderUtils.RenderCommitMessageLinkSubject .Message $commitLink $.comment.Issue.PullRequest.BaseRepo -}}
</span>
{{if IsMultilineCommitMessage .Message}}
@@ -29,7 +29,7 @@
</div>
{{if IsMultilineCommitMessage .Message}}
<pre class="commit-body tw-ml-[33px] tw-hidden" data-singular-commit-body-for="{{$tag}}">
- {{- ctx.RenderUtils.RenderCommitBody .Message ($.comment.Issue.PullRequest.BaseRepo.ComposeCommentMetas ctx) -}}
+ {{- ctx.RenderUtils.RenderCommitBody .Message $.comment.Issue.PullRequest.BaseRepo -}}
</pre>
{{end}}
{{end}}
diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl
index 6f16ce3bd8..4e8ad1326c 100644
--- a/templates/repo/diff/compare.tmpl
+++ b/templates/repo/diff/compare.tmpl
@@ -189,7 +189,7 @@
<div class="ui segment flex-text-block tw-gap-4">
{{template "shared/issueicon" .}}
<div class="issue-title tw-break-anywhere">
- {{ctx.RenderUtils.RenderIssueTitle .PullRequest.Issue.Title ($.Repository.ComposeCommentMetas ctx)}}
+ {{ctx.RenderUtils.RenderIssueTitle .PullRequest.Issue.Title $.Repository}}
<span class="index">#{{.PullRequest.Issue.Index}}</span>
</div>
<a href="{{$.RepoLink}}/pulls/{{.PullRequest.Issue.Index}}" class="ui compact button primary">
diff --git a/templates/repo/graph/commits.tmpl b/templates/repo/graph/commits.tmpl
index 630c4579ea..34167cadc0 100644
--- a/templates/repo/graph/commits.tmpl
+++ b/templates/repo/graph/commits.tmpl
@@ -8,7 +8,7 @@
{{template "repo/commit_sign_badge" dict "Commit" $commit.Commit "CommitBaseLink" (print $.RepoLink "/commit") "CommitSignVerification" $commit.Verification}}
<span class="message tw-inline-block gt-ellipsis">
- <span>{{ctx.RenderUtils.RenderCommitMessage $commit.Subject ($.Repository.ComposeCommentMetas ctx)}}</span>
+ <span>{{ctx.RenderUtils.RenderCommitMessage $commit.Subject $.Repository}}</span>
</span>
<span class="commit-refs flex-text-inline">
diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl
index a4be598540..b8f28dfd9b 100644
--- a/templates/repo/issue/view_title.tmpl
+++ b/templates/repo/issue/view_title.tmpl
@@ -13,7 +13,7 @@
{{$canEditIssueTitle := and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
<div class="issue-title" id="issue-title-display">
<h1 class="tw-break-anywhere">
- {{ctx.RenderUtils.RenderIssueTitle .Issue.Title ($.Repository.ComposeCommentMetas ctx)}}
+ {{ctx.RenderUtils.RenderIssueTitle .Issue.Title $.Repository}}
<span class="index">#{{.Issue.Index}}</span>
</h1>
<div class="issue-title-buttons">
diff --git a/templates/repo/latest_commit.tmpl b/templates/repo/latest_commit.tmpl
index da457e423a..cff338949f 100644
--- a/templates/repo/latest_commit.tmpl
+++ b/templates/repo/latest_commit.tmpl
@@ -21,10 +21,10 @@
{{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses}}
{{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}}
- <span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{ctx.RenderUtils.RenderCommitMessageLinkSubject .LatestCommit.Message $commitLink ($.Repository.ComposeCommentMetas ctx)}}</span>
+ <span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{ctx.RenderUtils.RenderCommitMessageLinkSubject .LatestCommit.Message $commitLink $.Repository}}</span>
{{if IsMultilineCommitMessage .LatestCommit.Message}}
<button class="ui button ellipsis-button" aria-expanded="false" data-global-click="onRepoEllipsisButtonClick">...</button>
- <pre class="commit-body tw-hidden">{{ctx.RenderUtils.RenderCommitBody .LatestCommit.Message ($.Repository.ComposeCommentMetas ctx)}}</pre>
+ <pre class="commit-body tw-hidden">{{ctx.RenderUtils.RenderCommitBody .LatestCommit.Message $.Repository}}</pre>
{{end}}
</span>
{{end}}
diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl
index cd832498b4..c8ee059e89 100644
--- a/templates/repo/view_list.tmpl
+++ b/templates/repo/view_list.tmpl
@@ -47,7 +47,7 @@
<div class="repo-file-cell message loading-icon-2px">
{{if $commit}}
{{$commitLink := printf "%s/commit/%s" $.RepoLink (PathEscape $commit.ID.String)}}
- {{ctx.RenderUtils.RenderCommitMessageLinkSubject $commit.Message $commitLink ($.Repository.ComposeCommentMetas ctx)}}
+ {{ctx.RenderUtils.RenderCommitMessageLinkSubject $commit.Message $commitLink $.Repository}}
{{else}}
… {{/* will be loaded again by LastCommitLoaderURL */}}
{{end}}
diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl
index 97291fc42d..4ee12fa783 100644
--- a/templates/user/dashboard/feeds.tmpl
+++ b/templates/user/dashboard/feeds.tmpl
@@ -94,7 +94,7 @@
<img alt class="ui avatar" src="{{$push.AvatarLink ctx .AuthorEmail}}" title="{{.AuthorName}}" width="16" height="16">
<a class="ui sha label" href="{{$commitLink}}">{{ShortSha .Sha1}}</a>
<span class="text truncate">
- {{ctx.RenderUtils.RenderCommitMessage .Message ($repo.ComposeCommentMetas ctx)}}
+ {{ctx.RenderUtils.RenderCommitMessage .Message $repo}}
</span>
</div>
{{end}}
diff --git a/tests/integration/git_general_test.go b/tests/integration/git_general_test.go
index 34fe212d50..ed60bdb58a 100644
--- a/tests/integration/git_general_test.go
+++ b/tests/integration/git_general_test.go
@@ -11,8 +11,10 @@ import (
"net/http"
"net/url"
"os"
+ "os/exec"
"path"
"path/filepath"
+ "slices"
"strconv"
"testing"
"time"
@@ -30,6 +32,7 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/tests"
+ "github.com/kballard/go-shellquote"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -105,7 +108,12 @@ func testGitGeneral(t *testing.T, u *url.URL) {
// Setup key the user ssh key
withKeyFile(t, keyname, func(keyFile string) {
- t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile))
+ var keyID int64
+ t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile, func(t *testing.T, key api.PublicKey) {
+ keyID = key.ID
+ }))
+ assert.NotZero(t, keyID)
+ t.Run("LFSAccessTest", doSSHLFSAccessTest(sshContext, keyID))
// Setup remote link
// TODO: get url from api
@@ -136,6 +144,36 @@ func testGitGeneral(t *testing.T, u *url.URL) {
})
}
+func doSSHLFSAccessTest(_ APITestContext, keyID int64) func(*testing.T) {
+ return func(t *testing.T) {
+ sshCommand := os.Getenv("GIT_SSH_COMMAND") // it is set in withKeyFile
+ sshCmdParts, err := shellquote.Split(sshCommand) // and parse the ssh command to construct some mocked arguments
+ require.NoError(t, err)
+
+ t.Run("User2AccessOwned", func(t *testing.T) {
+ sshCmdUser2Self := append(slices.Clone(sshCmdParts),
+ "-p", strconv.Itoa(setting.SSH.ListenPort), "git@"+setting.SSH.ListenHost,
+ "git-lfs-authenticate", "user2/repo1.git", "upload", // accessible to own repo
+ )
+ cmd := exec.CommandContext(t.Context(), sshCmdUser2Self[0], sshCmdUser2Self[1:]...)
+ _, err := cmd.Output()
+ assert.NoError(t, err) // accessible, no error
+ })
+
+ t.Run("User2AccessOther", func(t *testing.T) {
+ sshCmdUser2Other := append(slices.Clone(sshCmdParts),
+ "-p", strconv.Itoa(setting.SSH.ListenPort), "git@"+setting.SSH.ListenHost,
+ "git-lfs-authenticate", "user5/repo4.git", "upload", // inaccessible to other's (user5/repo4)
+ )
+ cmd := exec.CommandContext(t.Context(), sshCmdUser2Other[0], sshCmdUser2Other[1:]...)
+ _, err := cmd.Output()
+ var errExit *exec.ExitError
+ require.ErrorAs(t, err, &errExit) // inaccessible, error
+ assert.Contains(t, string(errExit.Stderr), fmt.Sprintf("User: 2:user2 with Key: %d:test-key is not authorized to write to user5/repo4.", keyID))
+ })
+ }
+}
+
func ensureAnonymousClone(t *testing.T, u *url.URL) {
dstLocalPath := t.TempDir()
t.Run("CloneAnonymous", doGitClone(dstLocalPath, u))
diff --git a/web_src/css/editor/combomarkdowneditor.css b/web_src/css/editor/combomarkdowneditor.css
index 835286b795..046010c6c8 100644
--- a/web_src/css/editor/combomarkdowneditor.css
+++ b/web_src/css/editor/combomarkdowneditor.css
@@ -100,67 +100,3 @@
border-bottom: 1px solid var(--color-secondary);
padding-bottom: 1rem;
}
-
-text-expander {
- display: block;
- position: relative;
-}
-
-text-expander .suggestions {
- position: absolute;
- min-width: 180px;
- padding: 0;
- margin-top: 24px;
- list-style: none;
- background: var(--color-box-body);
- border-radius: var(--border-radius);
- border: 1px solid var(--color-secondary);
- box-shadow: 0 .5rem 1rem var(--color-shadow);
- z-index: 100; /* needs to be > 20 to be on top of dropzone's .dz-details */
-}
-
-text-expander .suggestions li {
- display: flex;
- align-items: center;
- cursor: pointer;
- padding: 4px 8px;
- font-weight: var(--font-weight-medium);
-}
-
-text-expander .suggestions li + li {
- border-top: 1px solid var(--color-secondary-alpha-40);
-}
-
-text-expander .suggestions li:first-child {
- border-radius: var(--border-radius) var(--border-radius) 0 0;
-}
-
-text-expander .suggestions li:last-child {
- border-radius: 0 0 var(--border-radius) var(--border-radius);
-}
-
-text-expander .suggestions li:only-child {
- border-radius: var(--border-radius);
-}
-
-text-expander .suggestions li:hover {
- background: var(--color-hover);
-}
-
-text-expander .suggestions .fullname {
- font-weight: var(--font-weight-normal);
- margin-left: 4px;
- color: var(--color-text-light-1);
-}
-
-text-expander .suggestions li[aria-selected="true"],
-text-expander .suggestions li[aria-selected="true"] span {
- background: var(--color-primary);
- color: var(--color-primary-contrast);
-}
-
-text-expander .suggestions img {
- width: 24px;
- height: 24px;
- margin-right: 8px;
-}
diff --git a/web_src/css/features/expander.css b/web_src/css/features/expander.css
new file mode 100644
index 0000000000..f560b2a9fd
--- /dev/null
+++ b/web_src/css/features/expander.css
@@ -0,0 +1,96 @@
+text-expander .suggestions,
+.tribute-container {
+ position: absolute;
+ max-height: min(300px, 95vh);
+ max-width: min(500px, 95vw);
+ overflow-x: hidden;
+ overflow-y: auto;
+ white-space: nowrap;
+ background: var(--color-menu);
+ box-shadow: 0 6px 18px var(--color-shadow);
+ border-radius: var(--border-radius);
+ border: 1px solid var(--color-secondary);
+ z-index: 100; /* needs to be > 20 to be on top of dropzone's .dz-details */
+}
+
+text-expander {
+ display: block;
+ position: relative;
+}
+
+text-expander .suggestions {
+ padding: 0;
+ margin-top: 24px;
+ list-style: none;
+}
+
+text-expander .suggestions li,
+.tribute-item {
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ gap: 6px;
+ font-weight: var(--font-weight-medium);
+}
+
+text-expander .suggestions li,
+.tribute-container li {
+ padding: 3px 6px;
+}
+
+text-expander .suggestions li + li,
+.tribute-container li + li {
+ border-top: 1px solid var(--color-secondary);
+}
+
+text-expander .suggestions li:first-child {
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
+}
+
+text-expander .suggestions li:last-child {
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
+}
+
+text-expander .suggestions li:only-child {
+ border-radius: var(--border-radius);
+}
+
+text-expander .suggestions .fullname,
+.tribute-container li .fullname {
+ font-weight: var(--font-weight-normal);
+ color: var(--color-text-light-1);
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+text-expander .suggestions li:hover,
+text-expander .suggestions li:hover *,
+text-expander .suggestions li[aria-selected="true"],
+text-expander .suggestions li[aria-selected="true"] *,
+.tribute-container li.highlight,
+.tribute-container li.highlight * {
+ background: var(--color-primary);
+ color: var(--color-primary-contrast);
+}
+
+text-expander .suggestions img,
+.tribute-item img {
+ width: 21px;
+ height: 21px;
+ object-fit: contain;
+ aspect-ratio: 1;
+}
+
+.tribute-container {
+ display: block;
+}
+
+.tribute-container ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.tribute-container li.no-match {
+ cursor: default;
+}
diff --git a/web_src/css/features/tribute.css b/web_src/css/features/tribute.css
deleted file mode 100644
index 99a026b9bc..0000000000
--- a/web_src/css/features/tribute.css
+++ /dev/null
@@ -1,32 +0,0 @@
-@import "tributejs/dist/tribute.css";
-
-.tribute-container {
- box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.25);
- border-radius: var(--border-radius);
-}
-
-.tribute-container ul {
- margin-top: 0 !important;
- background: var(--color-body) !important;
-}
-
-.tribute-container li {
- padding: 3px 0.5rem !important;
-}
-
-.tribute-container li span.fullname {
- font-weight: var(--font-weight-normal);
- font-size: 0.8rem;
-}
-
-.tribute-container li.highlight,
-.tribute-container li:hover {
- background: var(--color-primary) !important;
- color: var(--color-primary-contrast) !important;
-}
-
-.tribute-item {
- display: flex;
- align-items: center;
- gap: 6px;
-}
diff --git a/web_src/css/index.css b/web_src/css/index.css
index 84795d6d27..c20aa028e4 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -39,7 +39,7 @@
@import "./features/imagediff.css";
@import "./features/codeeditor.css";
@import "./features/projects.css";
-@import "./features/tribute.css";
+@import "./features/expander.css";
@import "./features/cropper.css";
@import "./features/console.css";
diff --git a/web_src/js/features/comp/TextExpander.ts b/web_src/js/features/comp/TextExpander.ts
index 5be234629d..2d79fe5029 100644
--- a/web_src/js/features/comp/TextExpander.ts
+++ b/web_src/js/features/comp/TextExpander.ts
@@ -97,6 +97,7 @@ export function initTextExpander(expander: TextExpanderElement) {
li.append(img);
const nameSpan = document.createElement('span');
+ nameSpan.classList.add('name');
nameSpan.textContent = name;
li.append(nameSpan);