初始提交: Gitea 项目代码

This commit is contained in:
root
2026-05-30 22:47:36 +08:00
commit f288f76350
6116 changed files with 776822 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"testing"
"gitea.dev/models/unittest"
)
func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{
FixtureFiles: []string{"user.yml", "repository.yml", "access.yml", "repo_unit.yml", "issue.yml"},
})
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"context"
"gitea.dev/models/user"
"gitea.dev/modules/markup"
gitea_context "gitea.dev/services/context"
)
func FormalRenderHelperFuncs() *markup.RenderHelperFuncs {
return &markup.RenderHelperFuncs{
RenderRepoFileCodePreview: renderRepoFileCodePreview,
RenderRepoIssueIconTitle: renderRepoIssueIconTitle,
IsUsernameMentionable: func(ctx context.Context, username string) bool {
mentionedUser, err := user.GetUserByName(ctx, username)
if err != nil {
return false
}
giteaCtx := gitea_context.GetWebContext(ctx)
if giteaCtx == nil {
// when using general context, use user's visibility to check
return mentionedUser.Visibility.IsPublic()
}
// when using gitea context (web context), use user's visibility and user's permission to check
return user.IsUserVisibleToViewer(giteaCtx, mentionedUser, giteaCtx.Doer)
},
}
}
+120
View File
@@ -0,0 +1,120 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"bufio"
"context"
"errors"
"html/template"
"strings"
"gitea.dev/models/perm/access"
"gitea.dev/models/repo"
"gitea.dev/models/unit"
"gitea.dev/modules/charset"
"gitea.dev/modules/git/languagestats"
"gitea.dev/modules/gitrepo"
"gitea.dev/modules/indexer/code"
"gitea.dev/modules/markup"
"gitea.dev/modules/setting"
"gitea.dev/modules/util"
gitea_context "gitea.dev/services/context"
)
func renderRepoFileCodePreview(ctx context.Context, opts markup.RenderCodePreviewOptions) (template.HTML, error) {
opts.LineStop = max(opts.LineStop, opts.LineStart)
lineCount := opts.LineStop - opts.LineStart + 1
if lineCount <= 0 || lineCount > 140 /* GitHub at most show 140 lines */ {
lineCount = 10
opts.LineStop = opts.LineStart + lineCount
}
dbRepo, err := repo.GetRepositoryByOwnerAndName(ctx, opts.OwnerName, opts.RepoName)
if err != nil {
return "", err
}
webCtx := gitea_context.GetWebContext(ctx)
if webCtx == nil {
return "", errors.New("context is not a web context")
}
doer := webCtx.Doer
perms, err := access.GetDoerRepoPermission(ctx, dbRepo, doer)
if err != nil {
return "", err
}
if !perms.CanRead(unit.TypeCode) {
return "", util.ErrPermissionDenied
}
gitRepo, err := gitrepo.OpenRepository(ctx, dbRepo)
if err != nil {
return "", err
}
defer gitRepo.Close()
commit, err := gitRepo.GetCommit(opts.CommitID)
if err != nil {
return "", err
}
language, _ := languagestats.GetFileLanguage(ctx, gitRepo, opts.CommitID, opts.FilePath)
blob, err := commit.GetBlobByPath(opts.FilePath)
if err != nil {
return "", err
}
if blob.Size() > setting.UI.MaxDisplayFileSize {
return "", errors.New("file is too large")
}
dataRc, err := blob.DataAsync()
if err != nil {
return "", err
}
defer dataRc.Close()
reader := bufio.NewReader(dataRc)
for i := 1; i < opts.LineStart; i++ {
if _, err = reader.ReadBytes('\n'); err != nil {
return "", err
}
}
lineNums := make([]int, 0, lineCount)
lineCodes := make([]string, 0, lineCount)
for i := opts.LineStart; i <= opts.LineStop; i++ {
line, err := reader.ReadString('\n')
if err != nil && line == "" {
break
}
lineNums = append(lineNums, i)
lineCodes = append(lineCodes, line)
}
realLineStop := max(opts.LineStart, opts.LineStart+len(lineNums)-1)
highlightLines := code.HighlightSearchResultCode(opts.FilePath, language, lineNums, strings.Join(lineCodes, ""))
escapeStatus := &charset.EscapeStatus{}
lineEscapeStatus := make([]*charset.EscapeStatus, len(highlightLines))
for i, hl := range highlightLines {
lineEscapeStatus[i], hl.FormattedContent = charset.EscapeControlHTML(hl.FormattedContent, webCtx.Base.Locale, charset.EscapeOptionsForView())
escapeStatus = escapeStatus.Or(lineEscapeStatus[i])
}
return webCtx.RenderToHTML("base/markup_codepreview", map[string]any{
"FullURL": opts.FullURL,
"FilePath": opts.FilePath,
"LineStart": opts.LineStart,
"LineStop": realLineStop,
"RepoName": opts.RepoName,
"RepoLink": dbRepo.Link(),
"CommitID": opts.CommitID,
"HighlightLines": highlightLines,
"EscapeStatus": escapeStatus,
"LineEscapeStatus": lineEscapeStatus,
})
}
@@ -0,0 +1,87 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"testing"
"gitea.dev/models/unittest"
"gitea.dev/modules/markup"
"gitea.dev/modules/templates"
"gitea.dev/modules/util"
"gitea.dev/services/contexttest"
"github.com/stretchr/testify/assert"
)
func TestRenderHelperCodePreview(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.PageRenderer()})
htm, err := renderRepoFileCodePreview(ctx, markup.RenderCodePreviewOptions{
FullURL: "http://full",
OwnerName: "user2",
RepoName: "repo1",
CommitID: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
FilePath: "README.md",
LineStart: 1,
LineStop: 2,
})
assert.NoError(t, err)
assert.Equal(t, `<div class="code-preview-container file-content">
<div class="code-preview-header">
<a href="http://full" class="tw-font-semibold" rel="nofollow">repo1/README.md</a>
repo.code_preview_line_from_to:1,2,<a href="/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d" class="muted tw-font-mono tw-text-text" rel="nofollow">65f1bf27bc</a>
</div>
<table class="file-view">
<tbody><tr>
<td class="lines-num"><span data-line-number="1"></span></td>
<td class="lines-code chroma"><div class="code-inner"><span class="gh"># repo1
</span></div></td>
</tr><tr>
<td class="lines-num"><span data-line-number="2"></span></td>
<td class="lines-code chroma"><div class="code-inner">
</div></td>
</tr></tbody>
</table>
</div>
`, string(htm))
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.PageRenderer()})
htm, err = renderRepoFileCodePreview(ctx, markup.RenderCodePreviewOptions{
FullURL: "http://full",
OwnerName: "user2",
RepoName: "repo1",
CommitID: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
FilePath: "README.md",
LineStart: 1,
})
assert.NoError(t, err)
assert.Equal(t, `<div class="code-preview-container file-content">
<div class="code-preview-header">
<a href="http://full" class="tw-font-semibold" rel="nofollow">repo1/README.md</a>
repo.code_preview_line_in:1,<a href="/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d" class="muted tw-font-mono tw-text-text" rel="nofollow">65f1bf27bc</a>
</div>
<table class="file-view">
<tbody><tr>
<td class="lines-num"><span data-line-number="1"></span></td>
<td class="lines-code chroma"><div class="code-inner"><span class="gh"># repo1
</span></div></td>
</tr></tbody>
</table>
</div>
`, string(htm))
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.PageRenderer()})
_, err = renderRepoFileCodePreview(ctx, markup.RenderCodePreviewOptions{
FullURL: "http://full",
OwnerName: "user15",
RepoName: "big_test_private_1",
CommitID: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
FilePath: "README.md",
LineStart: 1,
LineStop: 10,
})
assert.ErrorIs(t, err, util.ErrPermissionDenied)
}
@@ -0,0 +1,67 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"context"
"errors"
"fmt"
"html/template"
"gitea.dev/models/issues"
"gitea.dev/models/perm/access"
"gitea.dev/models/repo"
"gitea.dev/modules/htmlutil"
"gitea.dev/modules/markup"
"gitea.dev/modules/util"
gitea_context "gitea.dev/services/context"
)
func renderRepoIssueIconTitle(ctx context.Context, opts markup.RenderIssueIconTitleOptions) (_ template.HTML, err error) {
webCtx := gitea_context.GetWebContext(ctx)
if webCtx == nil {
return "", errors.New("context is not a web context")
}
textIssueIndex := fmt.Sprintf("(#%d)", opts.IssueIndex)
dbRepo := webCtx.Repo.Repository
if opts.OwnerName != "" {
dbRepo, err = repo.GetRepositoryByOwnerAndName(ctx, opts.OwnerName, opts.RepoName)
if err != nil {
return "", err
}
textIssueIndex = fmt.Sprintf("(%s/%s#%d)", dbRepo.OwnerName, dbRepo.Name, opts.IssueIndex)
}
if dbRepo == nil {
return "", nil
}
issue, err := issues.GetIssueByIndex(ctx, dbRepo.ID, opts.IssueIndex)
if err != nil {
return "", err
}
if webCtx.Repo.Repository == nil || dbRepo.ID != webCtx.Repo.Repository.ID {
perms, err := access.GetDoerRepoPermission(ctx, dbRepo, webCtx.Doer)
if err != nil {
return "", err
}
if !perms.CanReadIssuesOrPulls(issue.IsPull) {
return "", util.ErrPermissionDenied
}
}
if issue.IsPull {
if err = issue.LoadPullRequest(ctx); err != nil {
return "", err
}
}
htmlIcon, err := webCtx.RenderToHTML("shared/issueicon", issue)
if err != nil {
return "", err
}
return htmlutil.HTMLFormat(`<a href="%s">%s %s %s</a>`, opts.LinkHref, htmlIcon, issue.Title, textIssueIndex), nil
}
@@ -0,0 +1,49 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"testing"
"gitea.dev/models/repo"
"gitea.dev/models/unittest"
"gitea.dev/modules/markup"
"gitea.dev/modules/templates"
"gitea.dev/modules/util"
"gitea.dev/services/contexttest"
"github.com/stretchr/testify/assert"
)
func TestRenderHelperIssueIconTitle(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.PageRenderer()})
ctx.Repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
htm, err := renderRepoIssueIconTitle(ctx, markup.RenderIssueIconTitleOptions{
LinkHref: "/link",
IssueIndex: 1,
})
assert.NoError(t, err)
assert.Equal(t, `<a href="/link"><span>octicon-issue-opened(16/tw-text-green)</span> issue1 (#1)</a>`, string(htm))
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.PageRenderer()})
htm, err = renderRepoIssueIconTitle(ctx, markup.RenderIssueIconTitleOptions{
OwnerName: "user2",
RepoName: "repo1",
LinkHref: "/link",
IssueIndex: 1,
})
assert.NoError(t, err)
assert.Equal(t, `<a href="/link"><span>octicon-issue-opened(16/tw-text-green)</span> issue1 (user2/repo1#1)</a>`, string(htm))
ctx, _ = contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.PageRenderer()})
_, err = renderRepoIssueIconTitle(ctx, markup.RenderIssueIconTitleOptions{
OwnerName: "user2",
RepoName: "repo2",
LinkHref: "/link",
IssueIndex: 2,
})
assert.ErrorIs(t, err, util.ErrPermissionDenied)
}
@@ -0,0 +1,51 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"net/http"
"net/http/httptest"
"testing"
"gitea.dev/models/unittest"
"gitea.dev/models/user"
gitea_context "gitea.dev/services/context"
"gitea.dev/services/contexttest"
"github.com/stretchr/testify/assert"
)
func TestRenderHelperMention(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
userPublic := "user1"
userPrivate := "user31"
userLimited := "user33"
userNoSuch := "no-such-user"
unittest.AssertCount(t, &user.User{Name: userPublic}, 1)
unittest.AssertCount(t, &user.User{Name: userPrivate}, 1)
unittest.AssertCount(t, &user.User{Name: userLimited}, 1)
unittest.AssertCount(t, &user.User{Name: userNoSuch}, 0)
// when using general context, use user's visibility to check
assert.True(t, FormalRenderHelperFuncs().IsUsernameMentionable(t.Context(), userPublic))
assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(t.Context(), userLimited))
assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(t.Context(), userPrivate))
assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(t.Context(), userNoSuch))
// when using web context, use user.IsUserVisibleToViewer to check
req, err := http.NewRequest(http.MethodGet, "/", nil)
assert.NoError(t, err)
base := gitea_context.NewBaseContextForTest(httptest.NewRecorder(), req)
giteaCtx := gitea_context.NewWebContext(base, &contexttest.MockRender{}, nil)
assert.True(t, FormalRenderHelperFuncs().IsUsernameMentionable(giteaCtx, userPublic))
assert.False(t, FormalRenderHelperFuncs().IsUsernameMentionable(giteaCtx, userPrivate))
giteaCtx.Doer, err = user.GetUserByName(t.Context(), userPrivate)
assert.NoError(t, err)
assert.True(t, FormalRenderHelperFuncs().IsUsernameMentionable(giteaCtx, userPublic))
assert.True(t, FormalRenderHelperFuncs().IsUsernameMentionable(giteaCtx, userPrivate))
}