mirror of
https://github.com/go-gitea/gitea.git
synced 2024-09-01 14:56:30 +00:00
Merge branch 'main' into pacman-packages
This commit is contained in:
commit
2548414865
35
CHANGELOG.md
35
CHANGELOG.md
@ -2,9 +2,38 @@
|
||||
|
||||
This changelog goes through all the changes that have been made in each release
|
||||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
|
||||
## [1.20.2](https://github.com/go-gitea/gitea/releases/tag/1.20.2) - 2023-07-29
|
||||
## [1.20.3](https://github.com/go-gitea/gitea/releases/tag/v1.20.3) - 2023-08-07
|
||||
|
||||
* BREAKING
|
||||
* Fix the wrong derive path (#26271) (#26318)
|
||||
* SECURITY
|
||||
* Fix API leaking Usermail if not logged in (#25097) (#26350)
|
||||
* ENHANCEMENTS
|
||||
* Display human-readable text instead of cryptic filemodes (#26352) (#26358)
|
||||
* Hide `last indexed SHA` when a repo could not be indexed yet (#26340) (#26345)
|
||||
* Fix the topic validation rule and suport dots (#26286) (#26303)
|
||||
* Fix due date rendering the wrong date in issue (#26268) (#26274)
|
||||
* Don't autosize textarea in diff view (#26233) (#26244)
|
||||
* Fix commit compare style (#26209) (#26226)
|
||||
* Warn instead of reporting an error when a webhook cannot be found (#26039) (#26211)
|
||||
* BUGFIXES
|
||||
* Bypass MariaDB performance bug of the "IN" sub-query, fix incorrect IssueIndex (#26279) (#26368)
|
||||
* Fix incorrect CLI exit code and duplicate error message (#26346) (#26347)
|
||||
* Prevent newline errors with Debian packages (#26332) (#26342)
|
||||
* Fix bug with sqlite load read (#26305) (#26339)
|
||||
* Make git batch operations use parent context timeout instead of default timeout (#26325) (#26330)
|
||||
* Support getting changed files when commit ID is `EmptySHA` (#26290) (#26316)
|
||||
* Clarify the logger's MODE config option (#26267) (#26281)
|
||||
* Use shared template for webhook icons (#26242) (#26246)
|
||||
* Fix pull request check list is limited (#26179) (#26245)
|
||||
* Fix attachment clipboard copy on insecure origin (#26224) (#26231)
|
||||
* Fix access check for org-level project (#26182) (#26223)
|
||||
* MISC
|
||||
* Upgrade x/net to 0.13.0 (#26301)
|
||||
|
||||
## [1.20.2](https://github.com/go-gitea/gitea/releases/tag/v1.20.2) - 2023-07-29
|
||||
|
||||
* ENHANCEMENTS
|
||||
* Calculate MAX_WORKERS default value by CPU number (#26177) (#26183)
|
||||
@ -32,7 +61,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
* Fix wrong workflow status when rerun a job in an already finished workflow (#26119) (#26124)
|
||||
* Fix duplicated url prefix on issue context menu (#26066) (#26067)
|
||||
|
||||
## [1.20.1](https://github.com/go-gitea/gitea/releases/tag/1.20.1) - 2023-07-22
|
||||
## [1.20.1](https://github.com/go-gitea/gitea/releases/tag/v1.20.1) - 2023-07-22
|
||||
|
||||
* SECURITY
|
||||
* Disallow dangerous URL schemes (#25960) (#25964)
|
||||
|
@ -685,18 +685,34 @@ func NotifyWatchersActions(acts []*Action) error {
|
||||
}
|
||||
|
||||
// DeleteIssueActions delete all actions related with issueID
|
||||
func DeleteIssueActions(ctx context.Context, repoID, issueID int64) error {
|
||||
func DeleteIssueActions(ctx context.Context, repoID, issueID, issueIndex int64) error {
|
||||
// delete actions assigned to this issue
|
||||
subQuery := builder.Select("`id`").
|
||||
From("`comment`").
|
||||
Where(builder.Eq{"`issue_id`": issueID})
|
||||
if _, err := db.GetEngine(ctx).In("comment_id", subQuery).Delete(&Action{}); err != nil {
|
||||
return err
|
||||
e := db.GetEngine(ctx)
|
||||
|
||||
// MariaDB has a performance bug: https://jira.mariadb.org/browse/MDEV-16289
|
||||
// so here it uses "DELETE ... WHERE IN" with pre-queried IDs.
|
||||
var lastCommentID int64
|
||||
commentIDs := make([]int64, 0, db.DefaultMaxInSize)
|
||||
for {
|
||||
commentIDs = commentIDs[:0]
|
||||
err := e.Select("`id`").Table(&issues_model.Comment{}).
|
||||
Where(builder.Eq{"issue_id": issueID}).And("`id` > ?", lastCommentID).
|
||||
OrderBy("`id`").Limit(db.DefaultMaxInSize).
|
||||
Find(&commentIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(commentIDs) == 0 {
|
||||
break
|
||||
} else if _, err = db.GetEngine(ctx).In("comment_id", commentIDs).Delete(&Action{}); err != nil {
|
||||
return err
|
||||
} else {
|
||||
lastCommentID = commentIDs[len(commentIDs)-1]
|
||||
}
|
||||
}
|
||||
|
||||
_, err := db.GetEngine(ctx).Table("action").Where("repo_id = ?", repoID).
|
||||
_, err := e.Where("repo_id = ?", repoID).
|
||||
In("op_type", ActionCreateIssue, ActionCreatePullRequest).
|
||||
Where("content LIKE ?", strconv.FormatInt(issueID, 10)+"|%").
|
||||
Where("content LIKE ?", strconv.FormatInt(issueIndex, 10)+"|%"). // "IssueIndex|content..."
|
||||
Delete(&Action{})
|
||||
return err
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
package activities_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
@ -284,3 +285,36 @@ func TestConsistencyUpdateAction(t *testing.T) {
|
||||
assert.NoError(t, db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions))
|
||||
unittest.CheckConsistencyFor(t, &activities_model.Action{})
|
||||
}
|
||||
|
||||
func TestDeleteIssueActions(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
// load an issue
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &issue_model.Issue{ID: 4})
|
||||
assert.NotEqualValues(t, issue.ID, issue.Index) // it needs to use different ID/Index to test the DeleteIssueActions to delete some actions by IssueIndex
|
||||
|
||||
// insert a comment
|
||||
err := db.Insert(db.DefaultContext, &issue_model.Comment{Type: issue_model.CommentTypeComment, IssueID: issue.ID})
|
||||
assert.NoError(t, err)
|
||||
comment := unittest.AssertExistsAndLoadBean(t, &issue_model.Comment{Type: issue_model.CommentTypeComment, IssueID: issue.ID})
|
||||
|
||||
// truncate action table and insert some actions
|
||||
err = db.TruncateBeans(db.DefaultContext, &activities_model.Action{})
|
||||
assert.NoError(t, err)
|
||||
err = db.Insert(db.DefaultContext, &activities_model.Action{
|
||||
OpType: activities_model.ActionCommentIssue,
|
||||
CommentID: comment.ID,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
err = db.Insert(db.DefaultContext, &activities_model.Action{
|
||||
OpType: activities_model.ActionCreateIssue,
|
||||
RepoID: issue.RepoID,
|
||||
Content: fmt.Sprintf("%d|content...", issue.Index),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// assert that the actions exist, then delete them
|
||||
unittest.AssertCount(t, &activities_model.Action{}, 2)
|
||||
assert.NoError(t, activities_model.DeleteIssueActions(db.DefaultContext, issue.RepoID, issue.ID, issue.Index))
|
||||
unittest.AssertCount(t, &activities_model.Action{}, 0)
|
||||
}
|
||||
|
@ -637,3 +637,15 @@
|
||||
repo_id: 58
|
||||
type: 5
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 96
|
||||
repo_id: 49
|
||||
type: 1
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 97
|
||||
repo_id: 49
|
||||
type: 2
|
||||
created_unix: 946684810
|
||||
|
@ -854,8 +854,8 @@ func (issue *Issue) MovePin(ctx context.Context, newPosition int) error {
|
||||
}
|
||||
|
||||
// GetPinnedIssues returns the pinned Issues for the given Repo and type
|
||||
func GetPinnedIssues(ctx context.Context, repoID int64, isPull bool) ([]*Issue, error) {
|
||||
issues := make([]*Issue, 0)
|
||||
func GetPinnedIssues(ctx context.Context, repoID int64, isPull bool) (IssueList, error) {
|
||||
issues := make(IssueList, 0)
|
||||
|
||||
err := db.GetEngine(ctx).
|
||||
Table("issue").
|
||||
@ -868,7 +868,7 @@ func GetPinnedIssues(ctx context.Context, repoID int64, isPull bool) ([]*Issue,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = IssueList(issues).LoadAttributes(ctx)
|
||||
err = issues.LoadAttributes(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ func (issue *Issue) projectBoardID(ctx context.Context) int64 {
|
||||
|
||||
// LoadIssuesFromBoard load issues assigned to this board
|
||||
func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList, error) {
|
||||
issueList := make([]*Issue, 0, 10)
|
||||
issueList := make(IssueList, 0, 10)
|
||||
|
||||
if b.ID != 0 {
|
||||
issues, err := Issues(ctx, &IssuesOptions{
|
||||
@ -79,7 +79,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList
|
||||
issueList = append(issueList, issues...)
|
||||
}
|
||||
|
||||
if err := IssueList(issueList).LoadComments(ctx); err != nil {
|
||||
if err := issueList.LoadComments(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -441,7 +441,7 @@ func GetRepoIDsForIssuesOptions(opts *IssuesOptions, user *user_model.User) ([]i
|
||||
}
|
||||
|
||||
// Issues returns a list of issues by given conditions.
|
||||
func Issues(ctx context.Context, opts *IssuesOptions) ([]*Issue, error) {
|
||||
func Issues(ctx context.Context, opts *IssuesOptions) (IssueList, error) {
|
||||
sess := db.GetEngine(ctx).
|
||||
Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||
applyLimit(sess, opts)
|
||||
|
@ -5,6 +5,7 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"html"
|
||||
"html/template"
|
||||
"io"
|
||||
@ -31,14 +32,16 @@ import (
|
||||
|
||||
// Render represents a template render
|
||||
type Render interface {
|
||||
TemplateLookup(tmpl string) (templates.TemplateExecutor, error)
|
||||
HTML(w io.Writer, status int, name string, data any) error
|
||||
TemplateLookup(tmpl string, templateCtx context.Context) (templates.TemplateExecutor, error)
|
||||
HTML(w io.Writer, status int, name string, data any, templateCtx context.Context) error
|
||||
}
|
||||
|
||||
// Context represents context of a request.
|
||||
type Context struct {
|
||||
*Base
|
||||
|
||||
TemplateContext TemplateContext
|
||||
|
||||
Render Render
|
||||
PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData`
|
||||
|
||||
@ -60,6 +63,8 @@ type Context struct {
|
||||
Package *Package
|
||||
}
|
||||
|
||||
type TemplateContext map[string]any
|
||||
|
||||
func init() {
|
||||
web.RegisterResponseStatusProvider[*Context](func(req *http.Request) web_types.ResponseStatusProvider {
|
||||
return req.Context().Value(WebContextKey).(*Context)
|
||||
@ -133,8 +138,12 @@ func Contexter() func(next http.Handler) http.Handler {
|
||||
}
|
||||
defer baseCleanUp()
|
||||
|
||||
// TODO: "install.go" also shares the same logic, which should be refactored to a general function
|
||||
ctx.TemplateContext = NewTemplateContext(ctx)
|
||||
ctx.TemplateContext["Locale"] = ctx.Locale
|
||||
|
||||
ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
|
||||
ctx.Data["Context"] = &ctx
|
||||
ctx.Data["Context"] = ctx // TODO: use "ctx" in template and remove this
|
||||
ctx.Data["CurrentURL"] = setting.AppSubURL + req.URL.RequestURI()
|
||||
ctx.Data["Link"] = ctx.Link
|
||||
ctx.Data["locale"] = ctx.Locale
|
||||
|
@ -75,7 +75,7 @@ func (ctx *Context) HTML(status int, name base.TplName) {
|
||||
return strconv.FormatInt(time.Since(tmplStartTime).Nanoseconds()/1e6, 10) + "ms"
|
||||
}
|
||||
|
||||
err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data)
|
||||
err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data, ctx.TemplateContext)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
@ -93,7 +93,7 @@ func (ctx *Context) HTML(status int, name base.TplName) {
|
||||
// RenderToString renders the template content to a string
|
||||
func (ctx *Context) RenderToString(name base.TplName, data map[string]any) (string, error) {
|
||||
var buf strings.Builder
|
||||
err := ctx.Render.HTML(&buf, http.StatusOK, string(name), data)
|
||||
err := ctx.Render.HTML(&buf, http.StatusOK, string(name), data, ctx.TemplateContext)
|
||||
return buf.String(), err
|
||||
}
|
||||
|
||||
|
49
modules/context/context_template.go
Normal file
49
modules/context/context_template.go
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
var _ context.Context = TemplateContext(nil)
|
||||
|
||||
func NewTemplateContext(ctx context.Context) TemplateContext {
|
||||
return TemplateContext{"_ctx": ctx}
|
||||
}
|
||||
|
||||
func (c TemplateContext) parentContext() context.Context {
|
||||
return c["_ctx"].(context.Context)
|
||||
}
|
||||
|
||||
func (c TemplateContext) Deadline() (deadline time.Time, ok bool) {
|
||||
return c.parentContext().Deadline()
|
||||
}
|
||||
|
||||
func (c TemplateContext) Done() <-chan struct{} {
|
||||
return c.parentContext().Done()
|
||||
}
|
||||
|
||||
func (c TemplateContext) Err() error {
|
||||
return c.parentContext().Err()
|
||||
}
|
||||
|
||||
func (c TemplateContext) Value(key any) any {
|
||||
return c.parentContext().Value(key)
|
||||
}
|
||||
|
||||
// DataRaceCheck checks whether the template context function "ctx()" returns the consistent context
|
||||
// as the current template's rendering context (request context), to help to find data race issues as early as possible.
|
||||
// When the code is proven to be correct and stable, this function should be removed.
|
||||
func (c TemplateContext) DataRaceCheck(dataCtx context.Context) (string, error) {
|
||||
if c.parentContext() != dataCtx {
|
||||
log.Error("TemplateContext.DataRaceCheck: parent context mismatch\n%s", log.Stack(2))
|
||||
return "", errors.New("parent context mismatch")
|
||||
}
|
||||
return "", nil
|
||||
}
|
@ -852,7 +852,7 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
}
|
||||
|
||||
func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
if ctx.Metas == nil {
|
||||
if ctx.Metas == nil || ctx.Metas["mode"] == "document" {
|
||||
return
|
||||
}
|
||||
var (
|
||||
|
@ -262,6 +262,30 @@ func TestRender_IssueIndexPattern5(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRender_IssueIndexPattern_Document(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
metas := map[string]string{
|
||||
"format": "https://someurl.com/{user}/{repo}/{index}",
|
||||
"user": "someUser",
|
||||
"repo": "someRepo",
|
||||
"style": IssueNameStyleNumeric,
|
||||
"mode": "document",
|
||||
}
|
||||
|
||||
testRenderIssueIndexPattern(t, "#1", "#1", &RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
Metas: metas,
|
||||
})
|
||||
testRenderIssueIndexPattern(t, "#1312", "#1312", &RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
Metas: metas,
|
||||
})
|
||||
testRenderIssueIndexPattern(t, "!1", "!1", &RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
Metas: metas,
|
||||
})
|
||||
}
|
||||
|
||||
func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) {
|
||||
if ctx.URLPrefix == "" {
|
||||
ctx.URLPrefix = TestAppURL
|
||||
|
@ -529,6 +529,42 @@ func Test_ParseClusterFuzz(t *testing.T) {
|
||||
assert.NotContains(t, res.String(), "<html")
|
||||
}
|
||||
|
||||
func TestPostProcess_RenderDocument(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
|
||||
localMetas := map[string]string{
|
||||
"user": "go-gitea",
|
||||
"repo": "gitea",
|
||||
"mode": "document",
|
||||
}
|
||||
|
||||
test := func(input, expected string) {
|
||||
var res strings.Builder
|
||||
err := PostProcess(&RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Metas: localMetas,
|
||||
}, strings.NewReader(input), &res)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res.String()))
|
||||
}
|
||||
|
||||
// Issue index shouldn't be post processing in an document.
|
||||
test(
|
||||
"#1",
|
||||
"#1")
|
||||
|
||||
// Test that other post processing still works.
|
||||
test(
|
||||
":gitea:",
|
||||
`<span class="emoji" aria-label="gitea"><img alt=":gitea:" src="`+setting.StaticURLPrefix+`/assets/img/emoji/gitea.png"/></span>`)
|
||||
test(
|
||||
"Some text with 😄 in the middle",
|
||||
`Some text with <span class="emoji" aria-label="grinning face with smiling eyes">😄</span> in the middle`)
|
||||
test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
|
||||
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="ref-issue">person/repo#4 (comment)</a>`)
|
||||
}
|
||||
|
||||
func TestIssue16020(t *testing.T) {
|
||||
setting.AppURL = TestAppURL
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html"
|
||||
"html/template"
|
||||
@ -13,7 +12,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/emoji"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
@ -28,6 +26,8 @@ import (
|
||||
// NewFuncMap returns functions for injecting to templates
|
||||
func NewFuncMap() template.FuncMap {
|
||||
return map[string]any{
|
||||
"ctx": func() any { return nil }, // template context function
|
||||
|
||||
"DumpVar": dumpVar,
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
@ -57,7 +57,6 @@ func NewFuncMap() template.FuncMap {
|
||||
"avatarHTML": AvatarHTML,
|
||||
"avatarByAction": AvatarByAction,
|
||||
"avatarByEmail": AvatarByEmail,
|
||||
"repoAvatar": RepoAvatar,
|
||||
"EntryIcon": base.EntryIcon,
|
||||
"MigrationIcon": MigrationIcon,
|
||||
"ActionIcon": ActionIcon,
|
||||
@ -103,9 +102,6 @@ func NewFuncMap() template.FuncMap {
|
||||
"AssetVersion": func() string {
|
||||
return setting.AssetVersion
|
||||
},
|
||||
"DisableGravatar": func(ctx context.Context) bool {
|
||||
return system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
|
||||
},
|
||||
"DefaultShowFullName": func() bool {
|
||||
return setting.UI.DefaultShowFullName
|
||||
},
|
||||
|
@ -6,6 +6,7 @@ package templates
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -39,27 +40,28 @@ var (
|
||||
|
||||
var ErrTemplateNotInitialized = errors.New("template system is not initialized, check your log for errors")
|
||||
|
||||
func (h *HTMLRender) HTML(w io.Writer, status int, name string, data any) error {
|
||||
func (h *HTMLRender) HTML(w io.Writer, status int, name string, data any, ctx context.Context) error { //nolint:revive
|
||||
if respWriter, ok := w.(http.ResponseWriter); ok {
|
||||
if respWriter.Header().Get("Content-Type") == "" {
|
||||
respWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
}
|
||||
respWriter.WriteHeader(status)
|
||||
}
|
||||
t, err := h.TemplateLookup(name)
|
||||
t, err := h.TemplateLookup(name, ctx)
|
||||
if err != nil {
|
||||
return texttemplate.ExecError{Name: name, Err: err}
|
||||
}
|
||||
return t.Execute(w, data)
|
||||
}
|
||||
|
||||
func (h *HTMLRender) TemplateLookup(name string) (TemplateExecutor, error) {
|
||||
func (h *HTMLRender) TemplateLookup(name string, ctx context.Context) (TemplateExecutor, error) { //nolint:revive
|
||||
tmpls := h.templates.Load()
|
||||
if tmpls == nil {
|
||||
return nil, ErrTemplateNotInitialized
|
||||
}
|
||||
|
||||
return tmpls.Executor(name, NewFuncMap())
|
||||
m := NewFuncMap()
|
||||
m["ctx"] = func() any { return ctx }
|
||||
return tmpls.Executor(name, m)
|
||||
}
|
||||
|
||||
func (h *HTMLRender) CompileTemplates() error {
|
||||
|
@ -60,17 +60,6 @@ func AvatarByAction(ctx context.Context, action *activities_model.Action, others
|
||||
return Avatar(ctx, action.ActUser, others...)
|
||||
}
|
||||
|
||||
// RepoAvatar renders repo avatars. args: repo, size(int), class (string)
|
||||
func RepoAvatar(repo *repo_model.Repository, others ...any) template.HTML {
|
||||
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
|
||||
|
||||
src := repo.RelAvatarLink()
|
||||
if src != "" {
|
||||
return AvatarHTML(src, size, class, repo.FullName())
|
||||
}
|
||||
return template.HTML("")
|
||||
}
|
||||
|
||||
// AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string)
|
||||
func AvatarByEmail(ctx context.Context, email, name string, others ...any) template.HTML {
|
||||
size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
|
||||
|
@ -5,10 +5,10 @@ package templates
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"mime"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -174,23 +174,12 @@ func FilenameIsImage(filename string) bool {
|
||||
return strings.HasPrefix(mimeType, "image/")
|
||||
}
|
||||
|
||||
func TabSizeClass(ec any, filename string) string {
|
||||
var (
|
||||
value *editorconfig.Editorconfig
|
||||
ok bool
|
||||
)
|
||||
func TabSizeClass(ec *editorconfig.Editorconfig, filename string) string {
|
||||
if ec != nil {
|
||||
if value, ok = ec.(*editorconfig.Editorconfig); !ok || value == nil {
|
||||
return "tab-size-8"
|
||||
}
|
||||
def, err := value.GetDefinitionForFilename(filename)
|
||||
if err != nil {
|
||||
log.Error("tab size class: getting definition for filename: %v", err)
|
||||
return "tab-size-8"
|
||||
}
|
||||
if def.TabWidth > 0 {
|
||||
return fmt.Sprintf("tab-size-%d", def.TabWidth)
|
||||
def, err := ec.GetDefinitionForFilename(filename)
|
||||
if err == nil && def.TabWidth >= 1 && def.TabWidth <= 16 {
|
||||
return "tab-size-" + strconv.Itoa(def.TabWidth)
|
||||
}
|
||||
}
|
||||
return "tab-size-8"
|
||||
return "tab-size-4"
|
||||
}
|
||||
|
@ -150,11 +150,11 @@ func LoadGitRepo(t *testing.T, ctx *context.Context) {
|
||||
|
||||
type mockRender struct{}
|
||||
|
||||
func (tr *mockRender) TemplateLookup(tmpl string) (templates.TemplateExecutor, error) {
|
||||
func (tr *mockRender) TemplateLookup(tmpl string, _ gocontext.Context) (templates.TemplateExecutor, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (tr *mockRender) HTML(w io.Writer, status int, _ string, _ any) error {
|
||||
func (tr *mockRender) HTML(w io.Writer, status int, _ string, _ any, _ gocontext.Context) error {
|
||||
if resp, ok := w.(http.ResponseWriter); ok {
|
||||
resp.WriteHeader(status)
|
||||
}
|
||||
|
16
options/license/HP-1989
Normal file
16
options/license/HP-1989
Normal file
@ -0,0 +1,16 @@
|
||||
Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
|
||||
Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca.
|
||||
Digital Equipment Corporation, Maynard, Mass.
|
||||
Copyright (c) 1998 Microsoft.
|
||||
To anyone who acknowledges that this file is provided "AS IS"
|
||||
without any express or implied warranty: permission to use, copy,
|
||||
modify, and distribute this file for any purpose is hereby
|
||||
granted without fee, provided that the above copyright notices and
|
||||
this notice appears in all source code copies, and that none of
|
||||
the names of Open Software Foundation, Inc., Hewlett-Packard
|
||||
Company, Microsoft, or Digital Equipment Corporation be used in
|
||||
advertising or publicity pertaining to distribution of the software
|
||||
without specific, written prior permission. Neither Open Software
|
||||
Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital
|
||||
Equipment Corporation makes any representations about the
|
||||
suitability of this software for any purpose.
|
7
options/license/Soundex
Normal file
7
options/license/Soundex
Normal file
@ -0,0 +1,7 @@
|
||||
# (c) Copyright 1998-2007 by Mark Mielke # # Freedom to use
|
||||
these sources for whatever you want, as long as credit # is
|
||||
given where credit is due, is hereby granted. You may make
|
||||
modifications # where you see fit but leave this copyright
|
||||
somewhere visible. As well, try # to initial any changes you
|
||||
make so that if I like the changes I can # incorporate them
|
||||
into later versions. # # - Mark Mielke <mark@mielke.cc>
|
4
options/license/Texinfo-exception
Normal file
4
options/license/Texinfo-exception
Normal file
@ -0,0 +1,4 @@
|
||||
As a special exception, when this file is read by TeX when
|
||||
processing a Texinfo source document, you may use the result without
|
||||
restriction. This Exception is an additional permission under
|
||||
section 7 of the GNU General Public License, version 3 ("GPLv3").
|
@ -2833,6 +2833,7 @@ repos.lfs_size = LFS Size
|
||||
packages.package_manage_panel = Package Management
|
||||
packages.total_size = Total Size: %s
|
||||
packages.unreferenced_size = Unreferenced Size: %s
|
||||
packages.cleanup = Clean up expired data
|
||||
packages.owner = Owner
|
||||
packages.creator = Creator
|
||||
packages.name = Name
|
||||
@ -3509,3 +3510,12 @@ variables.update.success = The variable has been edited.
|
||||
type-1.display_name = Individual Project
|
||||
type-2.display_name = Repository Project
|
||||
type-3.display_name = Organization Project
|
||||
|
||||
[git.filemode]
|
||||
changed_filemode = %[1]s → %[2]s
|
||||
# Ordered by git filemode value, ascending. E.g. directory has "040000", normal file has "100644", …
|
||||
directory = Directory
|
||||
normal_file = Normal file
|
||||
executable_file = Executable file
|
||||
symbolic_link = Symbolic link
|
||||
submodule = Submodule
|
||||
|
@ -48,7 +48,7 @@ func RenderPanicErrorPage(w http.ResponseWriter, req *http.Request, err any) {
|
||||
data["ErrorMsg"] = "PANIC: " + combinedErr
|
||||
}
|
||||
|
||||
err = templates.HTMLRenderer().HTML(w, http.StatusInternalServerError, string(tplStatus500), data)
|
||||
err = templates.HTMLRenderer().HTML(w, http.StatusInternalServerError, string(tplStatus500), data, nil)
|
||||
if err != nil {
|
||||
log.Error("Error occurs again when rendering error page: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
@ -68,9 +68,13 @@ func Contexter() func(next http.Handler) http.Handler {
|
||||
}
|
||||
defer baseCleanUp()
|
||||
|
||||
ctx.TemplateContext = context.NewTemplateContext(ctx)
|
||||
ctx.TemplateContext["Locale"] = ctx.Locale
|
||||
|
||||
ctx.AppendContextValue(context.WebContextKey, ctx)
|
||||
ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
|
||||
ctx.Data.MergeFrom(middleware.ContextData{
|
||||
"Context": ctx, // TODO: use "ctx" in template and remove this
|
||||
"locale": ctx.Locale,
|
||||
"Title": ctx.Locale.Tr("install.install"),
|
||||
"PageIsInstall": true,
|
||||
|
@ -6,6 +6,7 @@ package admin
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
@ -14,6 +15,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
packages_service "code.gitea.io/gitea/services/packages"
|
||||
packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -99,3 +101,13 @@ func DeletePackageVersion(ctx *context.Context) {
|
||||
ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/admin/packages?page=" + url.QueryEscape(ctx.FormString("page")) + "&q=" + url.QueryEscape(ctx.FormString("q")) + "&type=" + url.QueryEscape(ctx.FormString("type")))
|
||||
}
|
||||
|
||||
func CleanupExpiredData(ctx *context.Context) {
|
||||
if err := packages_cleanup_service.CleanupExpiredData(ctx, time.Duration(0)); err != nil {
|
||||
ctx.ServerError("CleanupExpiredData", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("packages.cleanup.success"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/packages")
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/auth/password"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
@ -255,6 +256,7 @@ func EditUser(ctx *context.Context) {
|
||||
ctx.Data["DisableRegularOrgCreation"] = setting.Admin.DisableRegularOrgCreation
|
||||
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
|
||||
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
|
||||
ctx.Data["DisableGravatar"] = system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
|
||||
|
||||
prepareUserInfo(ctx)
|
||||
if ctx.Written() {
|
||||
@ -271,6 +273,7 @@ func EditUserPost(ctx *context.Context) {
|
||||
ctx.Data["PageIsAdminUsers"] = true
|
||||
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
|
||||
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
|
||||
ctx.Data["DisableGravatar"] = system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
|
||||
|
||||
u := prepareUserInfo(ctx)
|
||||
if ctx.Written() {
|
||||
|
@ -578,7 +578,7 @@ func GrantApplicationOAuth(ctx *context.Context) {
|
||||
|
||||
// OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities
|
||||
func OIDCWellKnown(ctx *context.Context) {
|
||||
t, err := ctx.Render.TemplateLookup("user/auth/oidc_wellknown")
|
||||
t, err := ctx.Render.TemplateLookup("user/auth/oidc_wellknown", nil)
|
||||
if err != nil {
|
||||
ctx.ServerError("unable to find template", err)
|
||||
return
|
||||
|
@ -191,7 +191,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
|
||||
ctx.Data["last_commit"] = ctx.Repo.CommitID
|
||||
ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
|
||||
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
|
||||
ctx.Data["Editorconfig"] = GetEditorConfig(ctx, treePath)
|
||||
ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, treePath)
|
||||
|
||||
ctx.HTML(http.StatusOK, tplEditFile)
|
||||
}
|
||||
@ -242,7 +242,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
|
||||
ctx.Data["last_commit"] = ctx.Repo.CommitID
|
||||
ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
|
||||
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
|
||||
ctx.Data["Editorconfig"] = GetEditorConfig(ctx, form.TreePath)
|
||||
ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, form.TreePath)
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(http.StatusOK, tplEditFile)
|
||||
|
@ -299,7 +299,7 @@ func ForkPost(ctx *context.Context) {
|
||||
ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
|
||||
}
|
||||
|
||||
func checkPullInfo(ctx *context.Context) *issues_model.Issue {
|
||||
func getPullInfo(ctx *context.Context) (issue *issues_model.Issue, ok bool) {
|
||||
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
@ -307,43 +307,43 @@ func checkPullInfo(ctx *context.Context) *issues_model.Issue {
|
||||
} else {
|
||||
ctx.ServerError("GetIssueByIndex", err)
|
||||
}
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
if err = issue.LoadPoster(ctx); err != nil {
|
||||
ctx.ServerError("LoadPoster", err)
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
if err := issue.LoadRepo(ctx); err != nil {
|
||||
ctx.ServerError("LoadRepo", err)
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title)
|
||||
ctx.Data["Issue"] = issue
|
||||
|
||||
if !issue.IsPull {
|
||||
ctx.NotFound("ViewPullCommits", nil)
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if err = issue.LoadPullRequest(ctx); err != nil {
|
||||
ctx.ServerError("LoadPullRequest", err)
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if err = issue.PullRequest.LoadHeadRepo(ctx); err != nil {
|
||||
ctx.ServerError("LoadHeadRepo", err)
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if ctx.IsSigned {
|
||||
// Update issue-user.
|
||||
if err = activities_model.SetIssueReadBy(ctx, issue.ID, ctx.Doer.ID); err != nil {
|
||||
ctx.ServerError("ReadBy", err)
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
return issue
|
||||
return issue, true
|
||||
}
|
||||
|
||||
func setMergeTarget(ctx *context.Context, pull *issues_model.PullRequest) {
|
||||
@ -361,14 +361,15 @@ func setMergeTarget(ctx *context.Context, pull *issues_model.PullRequest) {
|
||||
|
||||
// GetPullDiffStats get Pull Requests diff stats
|
||||
func GetPullDiffStats(ctx *context.Context) {
|
||||
issue := checkPullInfo(ctx)
|
||||
issue, ok := getPullInfo(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
pull := issue.PullRequest
|
||||
|
||||
mergeBaseCommitID := GetMergedBaseCommitID(ctx, issue)
|
||||
|
||||
if ctx.Written() {
|
||||
return
|
||||
} else if mergeBaseCommitID == "" {
|
||||
if mergeBaseCommitID == "" {
|
||||
ctx.NotFound("PullFiles", nil)
|
||||
return
|
||||
}
|
||||
@ -702,8 +703,8 @@ type pullCommitList struct {
|
||||
|
||||
// GetPullCommits get all commits for given pull request
|
||||
func GetPullCommits(ctx *context.Context) {
|
||||
issue := checkPullInfo(ctx)
|
||||
if ctx.Written() {
|
||||
issue, ok := getPullInfo(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
resp := &pullCommitList{}
|
||||
@ -735,8 +736,8 @@ func ViewPullCommits(ctx *context.Context) {
|
||||
ctx.Data["PageIsPullList"] = true
|
||||
ctx.Data["PageIsPullCommits"] = true
|
||||
|
||||
issue := checkPullInfo(ctx)
|
||||
if ctx.Written() {
|
||||
issue, ok := getPullInfo(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
pull := issue.PullRequest
|
||||
@ -779,8 +780,8 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
|
||||
ctx.Data["PageIsPullList"] = true
|
||||
ctx.Data["PageIsPullFiles"] = true
|
||||
|
||||
issue := checkPullInfo(ctx)
|
||||
if ctx.Written() {
|
||||
issue, ok := getPullInfo(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
pull := issue.PullRequest
|
||||
@ -1016,8 +1017,8 @@ func ViewPullFilesForAllCommitsOfPr(ctx *context.Context) {
|
||||
|
||||
// UpdatePullRequest merge PR's baseBranch into headBranch
|
||||
func UpdatePullRequest(ctx *context.Context) {
|
||||
issue := checkPullInfo(ctx)
|
||||
if ctx.Written() {
|
||||
issue, ok := getPullInfo(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if issue.IsClosed {
|
||||
@ -1101,8 +1102,8 @@ func UpdatePullRequest(ctx *context.Context) {
|
||||
// MergePullRequest response for merging pull request
|
||||
func MergePullRequest(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.MergePullRequestForm)
|
||||
issue := checkPullInfo(ctx)
|
||||
if ctx.Written() {
|
||||
issue, ok := getPullInfo(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1308,8 +1309,8 @@ func MergePullRequest(ctx *context.Context) {
|
||||
|
||||
// CancelAutoMergePullRequest cancels a scheduled pr
|
||||
func CancelAutoMergePullRequest(ctx *context.Context) {
|
||||
issue := checkPullInfo(ctx)
|
||||
if ctx.Written() {
|
||||
issue, ok := getPullInfo(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1447,8 +1448,8 @@ func CompareAndPullRequestPost(ctx *context.Context) {
|
||||
|
||||
// CleanUpPullRequest responses for delete merged branch when PR has been merged
|
||||
func CleanUpPullRequest(ctx *context.Context) {
|
||||
issue := checkPullInfo(ctx)
|
||||
if ctx.Written() {
|
||||
issue, ok := getPullInfo(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -259,8 +259,8 @@ type viewedFilesUpdate struct {
|
||||
|
||||
func UpdateViewedFiles(ctx *context.Context) {
|
||||
// Find corresponding PR
|
||||
issue := checkPullInfo(ctx)
|
||||
if ctx.Written() {
|
||||
issue, ok := getPullInfo(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
pull := issue.PullRequest
|
||||
|
@ -13,7 +13,7 @@ const tplSwaggerV1Json base.TplName = "swagger/v1_json"
|
||||
|
||||
// SwaggerV1Json render swagger v1 json
|
||||
func SwaggerV1Json(ctx *context.Context) {
|
||||
t, err := ctx.Render.TemplateLookup(string(tplSwaggerV1Json))
|
||||
t, err := ctx.Render.TemplateLookup(string(tplSwaggerV1Json), nil)
|
||||
if err != nil {
|
||||
ctx.ServerError("unable to find template", err)
|
||||
return
|
||||
|
@ -296,8 +296,7 @@ func NotificationSubscriptions(ctx *context.Context) {
|
||||
}
|
||||
ctx.Data["CommitStatus"] = commitStatus
|
||||
|
||||
issueList := issues_model.IssueList(issues)
|
||||
approvalCounts, err := issueList.GetApprovalCounts(ctx)
|
||||
approvalCounts, err := issues.GetApprovalCounts(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("ApprovalCounts", err)
|
||||
return
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
@ -43,6 +44,7 @@ func Profile(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("settings.profile")
|
||||
ctx.Data["PageIsSettingsProfile"] = true
|
||||
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
|
||||
ctx.Data["DisableGravatar"] = system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
|
||||
|
||||
ctx.HTML(http.StatusOK, tplSettingsProfile)
|
||||
}
|
||||
@ -83,6 +85,8 @@ func ProfilePost(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.UpdateProfileForm)
|
||||
ctx.Data["Title"] = ctx.Tr("settings")
|
||||
ctx.Data["PageIsSettingsProfile"] = true
|
||||
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
|
||||
ctx.Data["DisableGravatar"] = system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(http.StatusOK, tplSettingsProfile)
|
||||
|
@ -597,6 +597,7 @@ func registerRoutes(m *web.Route) {
|
||||
m.Group("/packages", func() {
|
||||
m.Get("", admin.Packages)
|
||||
m.Post("/delete", admin.DeletePackageVersion)
|
||||
m.Post("/cleanup", admin.CleanupExpiredData)
|
||||
}, packagesEnabled)
|
||||
|
||||
m.Group("/hooks", func() {
|
||||
|
@ -152,7 +152,7 @@ func registerCleanupPackages() {
|
||||
OlderThan: 24 * time.Hour,
|
||||
}, func(ctx context.Context, _ *user_model.User, config Config) error {
|
||||
realConfig := config.(*OlderThanConfig)
|
||||
return packages_cleanup_service.Cleanup(ctx, realConfig.OlderThan)
|
||||
return packages_cleanup_service.CleanupTask(ctx, realConfig.OlderThan)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -427,6 +427,23 @@ func (diffFile *DiffFile) ShouldBeHidden() bool {
|
||||
return diffFile.IsGenerated || diffFile.IsViewed
|
||||
}
|
||||
|
||||
func (diffFile *DiffFile) ModeTranslationKey(mode string) string {
|
||||
switch mode {
|
||||
case "040000":
|
||||
return "git.filemode.directory"
|
||||
case "100644":
|
||||
return "git.filemode.normal_file"
|
||||
case "100755":
|
||||
return "git.filemode.executable_file"
|
||||
case "120000":
|
||||
return "git.filemode.symbolic_link"
|
||||
case "160000":
|
||||
return "git.filemode.submodule"
|
||||
default:
|
||||
return mode
|
||||
}
|
||||
}
|
||||
|
||||
func getCommitFileLineCount(commit *git.Commit, filePath string) int {
|
||||
blob, err := commit.GetBlobByPath(filePath)
|
||||
if err != nil {
|
||||
|
@ -248,7 +248,7 @@ func deleteIssue(ctx context.Context, issue *issues_model.Issue) error {
|
||||
issue.MilestoneID, err)
|
||||
}
|
||||
|
||||
if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID); err != nil {
|
||||
if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID, issue.Index); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -20,9 +20,17 @@ import (
|
||||
debian_service "code.gitea.io/gitea/services/packages/debian"
|
||||
)
|
||||
|
||||
// Cleanup removes expired package data
|
||||
func Cleanup(taskCtx context.Context, olderThan time.Duration) error {
|
||||
ctx, committer, err := db.TxContext(taskCtx)
|
||||
// Task method to execute cleanup rules and cleanup expired package data
|
||||
func CleanupTask(ctx context.Context, olderThan time.Duration) error {
|
||||
if err := ExecuteCleanupRules(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return CleanupExpiredData(ctx, olderThan)
|
||||
}
|
||||
|
||||
func ExecuteCleanupRules(outerCtx context.Context) error {
|
||||
ctx, committer, err := db.TxContext(outerCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -30,7 +38,7 @@ func Cleanup(taskCtx context.Context, olderThan time.Duration) error {
|
||||
|
||||
err = packages_model.IterateEnabledCleanupRules(ctx, func(ctx context.Context, pcr *packages_model.PackageCleanupRule) error {
|
||||
select {
|
||||
case <-taskCtx.Done():
|
||||
case <-outerCtx.Done():
|
||||
return db.ErrCancelledf("While processing package cleanup rules")
|
||||
default:
|
||||
}
|
||||
@ -122,6 +130,16 @@ func Cleanup(taskCtx context.Context, olderThan time.Duration) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
func CleanupExpiredData(outerCtx context.Context, olderThan time.Duration) error {
|
||||
ctx, committer, err := db.TxContext(outerCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
if err := container_service.Cleanup(ctx, olderThan); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -4,6 +4,12 @@
|
||||
{{.locale.Tr "admin.packages.package_manage_panel"}} ({{.locale.Tr "admin.total" .TotalCount}},
|
||||
{{.locale.Tr "admin.packages.total_size" (FileSize .TotalBlobSize)}},
|
||||
{{.locale.Tr "admin.packages.unreferenced_size" (FileSize .TotalUnreferencedBlobSize)}})
|
||||
<div class="ui right">
|
||||
<form method="post" action="/admin/packages/cleanup">
|
||||
{{.CsrfTokenHtml}}
|
||||
<button class="ui primary tiny button">{{.locale.Tr "admin.packages.cleanup"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<form class="ui form ignore-dirty">
|
||||
|
@ -159,7 +159,7 @@
|
||||
<div class="ui attached segment">
|
||||
<form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data">
|
||||
{{.CsrfTokenHtml}}
|
||||
{{if not (DisableGravatar $.Context)}}
|
||||
{{if not .DisableGravatar}}
|
||||
<div class="inline field">
|
||||
<div class="ui radio checkbox">
|
||||
<input name="source" value="lookup" type="radio" {{if not .User.UseCustomAvatar}}checked{{end}}>
|
||||
|
@ -26,6 +26,8 @@
|
||||
{{end}}
|
||||
{{end}}
|
||||
<script src="{{AssetUrlPrefix}}/js/index.js?v={{AssetVersion}}" onerror="alert('Failed to load asset files from ' + this.src + '. Please make sure the asset files can be accessed.')"></script>
|
||||
{{template "custom/footer" .}}
|
||||
|
||||
{{template "custom/footer" .}}
|
||||
{{ctx.DataRaceCheck $.Context}}
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{.locale.Lang}}" class="theme-{{if .SignedUser.Theme}}{{.SignedUser.Theme}}{{else}}{{DefaultTheme}}{{end}}">
|
||||
<html lang="{{ctx.Locale.Lang}}" class="theme-{{if .SignedUser.Theme}}{{.SignedUser.Theme}}{{else}}{{DefaultTheme}}{{end}}">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}}{{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}</title>
|
||||
@ -28,7 +28,7 @@
|
||||
{{if .PageIsUserProfile}}
|
||||
<meta property="og:title" content="{{.ContextUser.DisplayName}}">
|
||||
<meta property="og:type" content="profile">
|
||||
<meta property="og:image" content="{{.ContextUser.AvatarLink $.Context}}">
|
||||
<meta property="og:image" content="{{.ContextUser.AvatarLink ctx}}">
|
||||
<meta property="og:url" content="{{.ContextUser.HTMLURL}}">
|
||||
{{if .ContextUser.Description}}
|
||||
<meta property="og:description" content="{{.ContextUser.Description}}">
|
||||
@ -48,10 +48,10 @@
|
||||
{{end}}
|
||||
{{end}}
|
||||
<meta property="og:type" content="object">
|
||||
{{if (.Repository.AvatarLink $.Context)}}
|
||||
<meta property="og:image" content="{{.Repository.AvatarLink $.Context}}">
|
||||
{{if (.Repository.AvatarLink ctx)}}
|
||||
<meta property="og:image" content="{{.Repository.AvatarLink ctx}}">
|
||||
{{else}}
|
||||
<meta property="og:image" content="{{.Repository.Owner.AvatarLink $.Context}}">
|
||||
<meta property="og:image" content="{{.Repository.Owner.AvatarLink ctx}}">
|
||||
{{end}}
|
||||
{{else}}
|
||||
<meta property="og:title" content="{{AppName}}">
|
||||
@ -65,10 +65,11 @@
|
||||
{{template "custom/header" .}}
|
||||
</head>
|
||||
<body>
|
||||
{{ctx.DataRaceCheck $.Context}}
|
||||
{{template "custom/body_outer_pre" .}}
|
||||
|
||||
<div class="full height">
|
||||
<noscript>{{.locale.Tr "enable_javascript"}}</noscript>
|
||||
<noscript>{{ctx.Locale.Tr "enable_javascript"}}</noscript>
|
||||
|
||||
{{template "custom/body_inner_pre" .}}
|
||||
|
||||
|
@ -2,12 +2,7 @@
|
||||
{{range .Repos}}
|
||||
<div class="flex-item">
|
||||
<div class="flex-item-leading">
|
||||
{{$avatar := (repoAvatar . 32)}}
|
||||
{{if $avatar}}
|
||||
{{$avatar}}
|
||||
{{else}}
|
||||
{{template "repo/icon" .}}
|
||||
{{end}}
|
||||
{{template "repo/icon" .}}
|
||||
</div>
|
||||
<div class="flex-item-main">
|
||||
<div class="flex-item-header">
|
||||
|
@ -19,18 +19,18 @@
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
</span>
|
||||
<div class="menu">
|
||||
<a class="{{if eq .SortType "newest"}}active {{end}}item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.latest"}}</a>
|
||||
<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.oldest"}}</a>
|
||||
<a class="{{if eq .SortType "alphabetically"}}active {{end}}item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
|
||||
<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
|
||||
<a class="{{if eq .SortType "newest"}}active {{end}}item" href="{{$.Link}}?tab={{$.TabName}}&sort=newest&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.latest"}}</a>
|
||||
<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="{{$.Link}}?tab={{$.TabName}}&sort=oldest&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.oldest"}}</a>
|
||||
<a class="{{if eq .SortType "alphabetically"}}active {{end}}item" href="{{$.Link}}?tab={{$.TabName}}&sort=alphabetically&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="{{$.Link}}?tab={{$.TabName}}&sort=reversealphabetically&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="{{$.Link}}?tab={{$.TabName}}&sort=recentupdate&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
|
||||
<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="{{$.Link}}?tab={{$.TabName}}&sort=leastupdate&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
|
||||
{{if not .DisableStars}}
|
||||
<a class="{{if eq .SortType "moststars"}}active {{end}}item" href="{{$.Link}}?sort=moststars&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.moststars"}}</a>
|
||||
<a class="{{if eq .SortType "feweststars"}}active {{end}}item" href="{{$.Link}}?sort=feweststars&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.feweststars"}}</a>
|
||||
<a class="{{if eq .SortType "moststars"}}active {{end}}item" href="{{$.Link}}?tab={{$.TabName}}&sort=moststars&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.moststars"}}</a>
|
||||
<a class="{{if eq .SortType "feweststars"}}active {{end}}item" href="{{$.Link}}?tab={{$.TabName}}&sort=feweststars&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.feweststars"}}</a>
|
||||
{{end}}
|
||||
<a class="{{if eq .SortType "mostforks"}}active {{end}}item" href="{{$.Link}}?sort=mostforks&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.mostforks"}}</a>
|
||||
<a class="{{if eq .SortType "fewestforks"}}active {{end}}item" href="{{$.Link}}?sort=fewestforks&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.fewestforks"}}</a>
|
||||
<a class="{{if eq .SortType "mostforks"}}active {{end}}item" href="{{$.Link}}?tab={{$.TabName}}&sort=mostforks&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.mostforks"}}</a>
|
||||
<a class="{{if eq .SortType "fewestforks"}}active {{end}}item" href="{{$.Link}}?tab={{$.TabName}}&sort=fewestforks&q={{$.Keyword}}&language={{$.Language}}">{{.locale.Tr "repo.issues.filter_sort.fewestforks"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -30,12 +30,7 @@
|
||||
{{range .Team.Repos}}
|
||||
<div class="flex-item flex-item-center">
|
||||
<div class="flex-item-leading">
|
||||
{{$avatar := (repoAvatar . 32)}}
|
||||
{{if $avatar}}
|
||||
{{$avatar}}
|
||||
{{else}}
|
||||
{{template "repo/icon" .}}
|
||||
{{end}}
|
||||
{{template "repo/icon" .}}
|
||||
</div>
|
||||
<div class="flex-item-main">
|
||||
<a class="flex-item-title text primary" href="{{$.Org.HomeLink}}/{{.Name | PathEscape}}">
|
||||
|
@ -130,9 +130,11 @@
|
||||
<span class="ui label">{{$.locale.Tr "repo.diff.vendored"}}</span>
|
||||
{{end}}
|
||||
{{if and $file.Mode $file.OldMode}}
|
||||
<span class="gt-ml-4 gt-mono">{{$file.OldMode}} → {{$file.Mode}}</span>
|
||||
{{$old := $.locale.Tr ($file.ModeTranslationKey $file.OldMode)}}
|
||||
{{$new := $.locale.Tr ($file.ModeTranslationKey $file.Mode)}}
|
||||
<span class="gt-ml-4 gt-mono">{{$.locale.Tr "git.filemode.changed_filemode" $old $new}}</span>
|
||||
{{else if $file.Mode}}
|
||||
<span class="gt-ml-4 gt-mono">{{$file.Mode}}</span>
|
||||
<span class="gt-ml-4 gt-mono">{{$.locale.Tr ($file.ModeTranslationKey $file.Mode)}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="diff-file-header-actions gt-df gt-ac gt-gap-2 gt-fw">
|
||||
|
@ -15,7 +15,7 @@
|
||||
{{range $i, $v := .TreeNames}}
|
||||
<div class="divider"> / </div>
|
||||
{{if eq $i $l}}
|
||||
<input id="file-name" value="{{$v}}" placeholder="{{$.locale.Tr "repo.editor.name_your_file"}}" data-editorconfig="{{$.Editorconfig}}" required autofocus>
|
||||
<input id="file-name" value="{{$v}}" placeholder="{{$.locale.Tr "repo.editor.name_your_file"}}" data-editorconfig="{{$.EditorconfigJson}}" required autofocus>
|
||||
<span data-tooltip-content="{{$.locale.Tr "repo.editor.filename_help"}}">{{svg "octicon-info"}}</span>
|
||||
{{else}}
|
||||
<span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span>
|
||||
|
@ -4,14 +4,9 @@
|
||||
<div class="repo-header">
|
||||
<div class="repo-title-wrap gt-df gt-fc">
|
||||
<div class="repo-title" role="heading" aria-level="1">
|
||||
{{$avatar := (repoAvatar . 32 "gt-mr-3")}}
|
||||
{{if $avatar}}
|
||||
{{$avatar}}
|
||||
{{else}}
|
||||
<div class="gt-mr-3">
|
||||
{{template "repo/icon" .}}
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="gt-mr-3">
|
||||
{{template "repo/icon" .}}
|
||||
</div>
|
||||
<a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>
|
||||
<div class="gt-mx-2">/</div>
|
||||
<a href="{{$.RepoLink}}">{{.Name}}</a>
|
||||
|
@ -1,15 +1,16 @@
|
||||
<div class="repo-icon">
|
||||
{{if $.IsTemplate}}
|
||||
{{$avatarLink := .RelAvatarLink}}
|
||||
{{if $avatarLink}}
|
||||
<img class="ui avatar gt-vm" src="{{$avatarLink}}" width="32" height="32" alt="{{.FullName}}">
|
||||
{{else if $.IsTemplate}}
|
||||
{{svg "octicon-repo-template" 32}}
|
||||
{{else if $.IsPrivate}}
|
||||
{{svg "octicon-lock" 32}}
|
||||
{{else if $.IsMirror}}
|
||||
{{svg "octicon-mirror" 32}}
|
||||
{{else if $.IsFork}}
|
||||
{{svg "octicon-repo-forked" 32}}
|
||||
{{else}}
|
||||
{{if $.IsPrivate}}
|
||||
{{svg "octicon-lock" 32}}
|
||||
{{else if $.IsMirror}}
|
||||
{{svg "octicon-mirror" 32}}
|
||||
{{else if $.IsFork}}
|
||||
{{svg "octicon-repo-forked" 32}}
|
||||
{{else}}
|
||||
{{svg "octicon-repo" 32}}
|
||||
{{end}}
|
||||
{{svg "octicon-repo" 32}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
@ -116,10 +116,10 @@
|
||||
</table>
|
||||
<div class="code-line-menu ui vertical pointing menu tippy-target">
|
||||
{{if $.Permission.CanRead $.UnitTypeIssues}}
|
||||
<a class="item ref-in-new-issue" data-url-issue-new="{{.RepoLink}}/issues/new" data-url-param-body-link="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}" rel="nofollow noindex">{{.locale.Tr "repo.issues.context.reference_issue"}}</a>
|
||||
<a class="item ref-in-new-issue" data-url-issue-new="{{.RepoLink}}/issues/new" data-url-param-body-link="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}" rel="nofollow noindex">{{.locale.Tr "repo.issues.context.reference_issue"}}</a>
|
||||
{{end}}
|
||||
<a class="item view_git_blame" href="{{.Repository.Link}}/blame/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}">{{.locale.Tr "repo.view_git_blame"}}</a>
|
||||
<a class="item copy-line-permalink" data-url="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}">{{.locale.Tr "repo.file_copy_permalink"}}</a>
|
||||
<a class="item copy-line-permalink" data-url="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}">{{.locale.Tr "repo.file_copy_permalink"}}</a>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
@ -99,7 +99,7 @@
|
||||
<div class="ui attached segment">
|
||||
<form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data">
|
||||
{{.CsrfTokenHtml}}
|
||||
{{if not (DisableGravatar $.Context)}}
|
||||
{{if not .DisableGravatar}}
|
||||
<div class="inline field">
|
||||
<div class="ui radio checkbox">
|
||||
<input name="source" value="lookup" type="radio" {{if not .SignedUser.UseCustomAvatar}}checked{{end}}>
|
||||
|
@ -475,7 +475,7 @@ func TestPackageCleanup(t *testing.T) {
|
||||
_, err = packages_model.GetInternalVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, "cleanup-test", container_model.UploadVersion)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = packages_cleanup_service.Cleanup(db.DefaultContext, duration)
|
||||
err = packages_cleanup_service.CleanupTask(db.DefaultContext, duration)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pbs, err = packages_model.FindExpiredUnreferencedBlobs(db.DefaultContext, duration)
|
||||
@ -610,7 +610,7 @@ func TestPackageCleanup(t *testing.T) {
|
||||
pcr, err := packages_model.InsertCleanupRule(db.DefaultContext, c.Rule)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = packages_cleanup_service.Cleanup(db.DefaultContext, duration)
|
||||
err = packages_cleanup_service.CleanupTask(db.DefaultContext, duration)
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, v := range c.Versions {
|
||||
|
@ -408,3 +408,39 @@ func TestMarkDownReadmeImageSubfolder(t *testing.T) {
|
||||
assert.True(t, exists, "Image not found in markdown file")
|
||||
assert.Equal(t, "/user2/repo1/media/branch/sub-home-md-img-check/docs/test-fake-img.jpg", src)
|
||||
}
|
||||
|
||||
func TestGeneratedSourceLink(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
t.Run("Rendered file", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
req := NewRequest(t, "GET", "/user2/repo1/src/branch/master/README.md?display=source")
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
doc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
dataURL, exists := doc.doc.Find(".copy-line-permalink").Attr("data-url")
|
||||
assert.True(t, exists)
|
||||
assert.Equal(t, "/user2/repo1/src/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d/README.md?display=source", dataURL)
|
||||
|
||||
dataURL, exists = doc.doc.Find(".ref-in-new-issue").Attr("data-url-param-body-link")
|
||||
assert.True(t, exists)
|
||||
assert.Equal(t, "/user2/repo1/src/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d/README.md?display=source", dataURL)
|
||||
})
|
||||
|
||||
t.Run("Non-Rendered file", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
session := loginUser(t, "user27")
|
||||
req := NewRequest(t, "GET", "/user27/repo49/src/branch/master/test/test.txt")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
doc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
dataURL, exists := doc.doc.Find(".copy-line-permalink").Attr("data-url")
|
||||
assert.True(t, exists)
|
||||
assert.Equal(t, "/user27/repo49/src/commit/aacbdfe9e1c4b47f60abe81849045fa4e96f1d75/test/test.txt", dataURL)
|
||||
|
||||
dataURL, exists = doc.doc.Find(".ref-in-new-issue").Attr("data-url-param-body-link")
|
||||
assert.True(t, exists)
|
||||
assert.Equal(t, "/user27/repo49/src/commit/aacbdfe9e1c4b47f60abe81849045fa4e96f1d75/test/test.txt", dataURL)
|
||||
})
|
||||
}
|
||||
|
@ -1218,7 +1218,7 @@ img.ui.avatar,
|
||||
}
|
||||
|
||||
.ui .text.truncate {
|
||||
overflow: hidden;
|
||||
overflow-x: clip;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
|
Loading…
Reference in New Issue
Block a user