diff --git a/CHANGELOG.md b/CHANGELOG.md index c6699a6bfa..85ad622f7e 100644 --- a/CHANGELOG.md +++ b/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) diff --git a/models/activities/action.go b/models/activities/action.go index 7f22605d0d..432bf8bf3f 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -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 } diff --git a/models/activities/action_test.go b/models/activities/action_test.go index 7044bcc004..9a42740880 100644 --- a/models/activities/action_test.go +++ b/models/activities/action_test.go @@ -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) +} diff --git a/models/fixtures/repo_unit.yml b/models/fixtures/repo_unit.yml index bb8715a202..c22eb8c2a2 100644 --- a/models/fixtures/repo_unit.yml +++ b/models/fixtures/repo_unit.yml @@ -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 diff --git a/models/issues/issue.go b/models/issues/issue.go index 38726de85a..f000f4c660 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -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 } diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 782638d997..ed249527bf 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -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 } diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index f9c1dbb384..281339044b 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -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) diff --git a/modules/analyze/code_langauge.go b/modules/analyze/code_language.go similarity index 100% rename from modules/analyze/code_langauge.go rename to modules/analyze/code_language.go diff --git a/modules/context/context.go b/modules/context/context.go index de0518a1d2..b75ba9ab67 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -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 diff --git a/modules/context/context_response.go b/modules/context/context_response.go index 9dc6d1fc0e..5729865561 100644 --- a/modules/context/context_response.go +++ b/modules/context/context_response.go @@ -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 } diff --git a/modules/context/context_template.go b/modules/context/context_template.go new file mode 100644 index 0000000000..ba90fc170a --- /dev/null +++ b/modules/context/context_template.go @@ -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 +} diff --git a/modules/markup/html.go b/modules/markup/html.go index da16bcd3cb..e53ccc6a79 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -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 ( diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index 00ffe45c28..7b7f6df701 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -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 diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index a8d7ba7948..9156bc6331 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -529,6 +529,42 @@ func Test_ParseClusterFuzz(t *testing.T) { assert.NotContains(t, res.String(), "`) + test( + "Some text with 😄 in the middle", + `Some text with 😄 in the middle`) + test("http://localhost:3000/person/repo/issues/4#issuecomment-1234", + `person/repo#4 (comment)`) +} + func TestIssue16020(t *testing.T) { setting.AppURL = TestAppURL diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 2b918f42c0..d9c297411a 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -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 }, diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go index d470435b63..5ab46cb13a 100644 --- a/modules/templates/htmlrenderer.go +++ b/modules/templates/htmlrenderer.go @@ -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 { diff --git a/modules/templates/util_avatar.go b/modules/templates/util_avatar.go index 9f8f8f87a9..81961041a0 100644 --- a/modules/templates/util_avatar.go +++ b/modules/templates/util_avatar.go @@ -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...) diff --git a/modules/templates/util_misc.go b/modules/templates/util_misc.go index 9cdabeb3ac..7700a13932 100644 --- a/modules/templates/util_misc.go +++ b/modules/templates/util_misc.go @@ -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" } diff --git a/modules/test/context_tests.go b/modules/test/context_tests.go index 9e7095e116..92d7f8b22b 100644 --- a/modules/test/context_tests.go +++ b/modules/test/context_tests.go @@ -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) } diff --git a/options/license/HP-1989 b/options/license/HP-1989 new file mode 100644 index 0000000000..7422055d95 --- /dev/null +++ b/options/license/HP-1989 @@ -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. diff --git a/options/license/Soundex b/options/license/Soundex new file mode 100644 index 0000000000..b745f29db5 --- /dev/null +++ b/options/license/Soundex @@ -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 diff --git a/options/license/Texinfo-exception b/options/license/Texinfo-exception new file mode 100644 index 0000000000..931a4070b4 --- /dev/null +++ b/options/license/Texinfo-exception @@ -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"). diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 0f30bf0486..d2022f144e 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -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 diff --git a/routers/common/errpage.go b/routers/common/errpage.go index 3d82c96deb..9c8ccc3388 100644 --- a/routers/common/errpage.go +++ b/routers/common/errpage.go @@ -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) diff --git a/routers/install/install.go b/routers/install/install.go index 6a8f561271..aa9c0f4986 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -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, diff --git a/routers/web/admin/packages.go b/routers/web/admin/packages.go index 8e4b8a373e..8d4c29813e 100644 --- a/routers/web/admin/packages.go +++ b/routers/web/admin/packages.go @@ -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") +} diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 08a4076418..03c89bdab1 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -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() { diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index 3c367b3d27..78dc84472a 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -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 diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index ad2055fd57..b5129b730b 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -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) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 0be8bede74..be4e9711e7 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -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 } diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index c2271750c4..3e433dcf4d 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -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 diff --git a/routers/web/swagger_json.go b/routers/web/swagger_json.go index 1844b90d95..493c97aa67 100644 --- a/routers/web/swagger_json.go +++ b/routers/web/swagger_json.go @@ -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 diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index 60ae628445..579287ffac 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -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 diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index 47066d5e38..ab7d2e58b3 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -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) diff --git a/routers/web/web.go b/routers/web/web.go index aa3d830f94..2c2309e827 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -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() { diff --git a/services/cron/tasks_basic.go b/services/cron/tasks_basic.go index 2e6560ec0c..2a213ae515 100644 --- a/services/cron/tasks_basic.go +++ b/services/cron/tasks_basic.go @@ -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) }) } diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 9e1db6fd43..4cb2b1303d 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -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 { diff --git a/services/issue/issue.go b/services/issue/issue.go index b6c6a26cbd..9ca4e21b17 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -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 } diff --git a/services/packages/cleanup/cleanup.go b/services/packages/cleanup/cleanup.go index 43fbc1ad9b..77bcfb1942 100644 --- a/services/packages/cleanup/cleanup.go +++ b/services/packages/cleanup/cleanup.go @@ -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 } diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl index 9aa1d933f6..4cf30f58e6 100644 --- a/templates/admin/packages/list.tmpl +++ b/templates/admin/packages/list.tmpl @@ -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)}}) + + + {{.CsrfTokenHtml}} + {{.locale.Tr "admin.packages.cleanup"}} + + diff --git a/templates/admin/user/edit.tmpl b/templates/admin/user/edit.tmpl index 96e09156d1..4fea418e6f 100644 --- a/templates/admin/user/edit.tmpl +++ b/templates/admin/user/edit.tmpl @@ -159,7 +159,7 @@ {{.CsrfTokenHtml}} - {{if not (DisableGravatar $.Context)}} + {{if not .DisableGravatar}} diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl index e3cac806a4..31c669a921 100644 --- a/templates/base/footer.tmpl +++ b/templates/base/footer.tmpl @@ -26,6 +26,8 @@ {{end}} {{end}} -{{template "custom/footer" .}} + + {{template "custom/footer" .}} + {{ctx.DataRaceCheck $.Context}}