diff --git a/build/backport-locales.go b/build/backport-locales.go index a717b37367dc4..054b623d698e8 100644 --- a/build/backport-locales.go +++ b/build/backport-locales.go @@ -62,6 +62,7 @@ func main() { // use old en-US as the base, and copy the new translations to the old locales enUsOld := inisOld["options/locale/locale_en-US.ini"] + brokenWarned := map[string]bool{} for path, iniOld := range inisOld { if iniOld == enUsOld { continue @@ -77,11 +78,14 @@ func main() { if secNew.HasKey(keyEnUs.Name()) { oldStr := secOld.Key(keyEnUs.Name()).String() newStr := secNew.Key(keyEnUs.Name()).String() - if oldStr != "" && strings.Count(oldStr, "%") != strings.Count(newStr, "%") { - fmt.Printf("WARNING: locale %s [%s]%s has different number of arguments, skipping\n", path, secEnUS.Name(), keyEnUs.Name()) - fmt.Printf("\told: %s\n", oldStr) - fmt.Printf("\tnew: %s\n", newStr) - fmt.Println("---- ") + broken := oldStr != "" && strings.Count(oldStr, "%") != strings.Count(newStr, "%") + broken = broken || strings.Contains(oldStr, "\n") || strings.Contains(oldStr, "\n") + if broken { + brokenWarned[secOld.Name()+"."+keyEnUs.Name()] = true + fmt.Println("----") + fmt.Printf("WARNING: skip broken locale: %s , [%s] %s\n", path, secEnUS.Name(), keyEnUs.Name()) + fmt.Printf("\told: %s\n", strings.ReplaceAll(oldStr, "\n", "\\n")) + fmt.Printf("\tnew: %s\n", strings.ReplaceAll(newStr, "\n", "\\n")) continue } secOld.Key(keyEnUs.Name()).SetValue(newStr) @@ -90,4 +94,25 @@ func main() { } mustNoErr(iniOld.SaveTo(path)) } + + fmt.Println("========") + + for path, iniNew := range inisNew { + for _, sec := range iniNew.Sections() { + for _, key := range sec.Keys() { + str := sec.Key(key.Name()).String() + broken := strings.Contains(str, "\n") + broken = broken || strings.HasPrefix(str, "`") != strings.HasSuffix(str, "`") + broken = broken || strings.HasPrefix(str, "\"`") + broken = broken || strings.HasPrefix(str, "`\"") + broken = broken || strings.Count(str, `"`)%2 == 1 + broken = broken || strings.Count(str, "`")%2 == 1 + if broken && !brokenWarned[sec.Name()+"."+key.Name()] { + fmt.Printf("WARNING: found broken locale: %s , [%s] %s\n", path, sec.Name(), key.Name()) + fmt.Printf("\tstr: %s\n", strings.ReplaceAll(str, "\n", "\\n")) + fmt.Println("----") + } + } + } + } } diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 2a840d5ef1fc7..0543c85d50daa 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -947,10 +947,10 @@ ROUTER = console ;USE_COMPAT_SSH_URI = false ;; ;; Close issues as long as a commit on any branch marks it as fixed -;; Comma separated list of globally disabled repo units. Allowed values: repo.issues, repo.ext_issues, repo.pulls, repo.wiki, repo.ext_wiki, repo.projects, repo.packages +;; Comma separated list of globally disabled repo units. Allowed values: repo.issues, repo.ext_issues, repo.pulls, repo.wiki, repo.ext_wiki, repo.projects, repo.packages, repo.actions. ;DISABLED_REPO_UNITS = ;; -;; Comma separated list of default new repo units. Allowed values: repo.code, repo.releases, repo.issues, repo.pulls, repo.wiki, repo.projects, repo.packages. +;; Comma separated list of default new repo units. Allowed values: repo.code, repo.releases, repo.issues, repo.pulls, repo.wiki, repo.projects, repo.packages, repo.actions. ;; Note: Code and Releases can currently not be deactivated. If you specify default repo units you should still list them for future compatibility. ;; External wiki and issue tracker can't be enabled by default as it requires additional settings. ;; Disabled repo units will not be added to new repositories regardless if it is in the default list. diff --git a/docs/content/doc/administration/config-cheat-sheet.en-us.md b/docs/content/doc/administration/config-cheat-sheet.en-us.md index f8082bd3784f2..67618d05b675e 100644 --- a/docs/content/doc/administration/config-cheat-sheet.en-us.md +++ b/docs/content/doc/administration/config-cheat-sheet.en-us.md @@ -103,8 +103,8 @@ In addition there is _`StaticRootPath`_ which can be set as a built-in at build - `DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH`: **false**: Close an issue if a commit on a non default branch marks it as closed. - `ENABLE_PUSH_CREATE_USER`: **false**: Allow users to push local repositories to Gitea and have them automatically created for a user. - `ENABLE_PUSH_CREATE_ORG`: **false**: Allow users to push local repositories to Gitea and have them automatically created for an org. -- `DISABLED_REPO_UNITS`: **_empty_**: Comma separated list of globally disabled repo units. Allowed values: \[repo.issues, repo.ext_issues, repo.pulls, repo.wiki, repo.ext_wiki, repo.projects\] -- `DEFAULT_REPO_UNITS`: **repo.code,repo.releases,repo.issues,repo.pulls,repo.wiki,repo.projects,repo.packages**: Comma separated list of default new repo units. Allowed values: \[repo.code, repo.releases, repo.issues, repo.pulls, repo.wiki, repo.projects\]. Note: Code and Releases can currently not be deactivated. If you specify default repo units you should still list them for future compatibility. External wiki and issue tracker can't be enabled by default as it requires additional settings. Disabled repo units will not be added to new repositories regardless if it is in the default list. +- `DISABLED_REPO_UNITS`: **_empty_**: Comma separated list of globally disabled repo units. Allowed values: \[repo.issues, repo.ext_issues, repo.pulls, repo.wiki, repo.ext_wiki, repo.projects, repo.packages, repo.actions\] +- `DEFAULT_REPO_UNITS`: **repo.code,repo.releases,repo.issues,repo.pulls,repo.wiki,repo.projects,repo.packages**: Comma separated list of default new repo units. Allowed values: \[repo.code, repo.releases, repo.issues, repo.pulls, repo.wiki, repo.projects, repo.packages, repo.actions\]. Note: Code and Releases can currently not be deactivated. If you specify default repo units you should still list them for future compatibility. External wiki and issue tracker can't be enabled by default as it requires additional settings. Disabled repo units will not be added to new repositories regardless if it is in the default list. - `DEFAULT_FORK_REPO_UNITS`: **repo.code,repo.pulls**: Comma separated list of default forked repo units. The set of allowed values and rules is the same as `DEFAULT_REPO_UNITS`. - `PREFIX_ARCHIVE_FILES`: **true**: Prefix archive files by placing them in a directory named after the repository. - `DISABLE_MIGRATIONS`: **false**: Disable migrating feature. diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index ea3619db97ab9..07240c8e69ed5 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -479,6 +479,8 @@ var migrations = []Migration{ NewMigration("Improve Action table indices v3", v1_20.ImproveActionTableIndices), // v250 -> v251 NewMigration("Change Container Metadata", v1_20.ChangeContainerMetadataMultiArch), + // v251 -> v252 + NewMigration("Fix incorrect owner team unit access mode", v1_20.FixIncorrectOwnerTeamUnitAccessMode), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_20/v251.go b/models/migrations/v1_20/v251.go new file mode 100644 index 0000000000000..7743248a3f17b --- /dev/null +++ b/models/migrations/v1_20/v251.go @@ -0,0 +1,47 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_20 //nolint + +import ( + "code.gitea.io/gitea/modules/log" + + "xorm.io/xorm" +) + +func FixIncorrectOwnerTeamUnitAccessMode(x *xorm.Engine) error { + type UnitType int + type AccessMode int + + type TeamUnit struct { + ID int64 `xorm:"pk autoincr"` + OrgID int64 `xorm:"INDEX"` + TeamID int64 `xorm:"UNIQUE(s)"` + Type UnitType `xorm:"UNIQUE(s)"` + AccessMode AccessMode + } + + const ( + // AccessModeOwner owner access + AccessModeOwner = 4 + ) + + sess := x.NewSession() + defer sess.Close() + + if err := sess.Begin(); err != nil { + return err + } + + count, err := sess.Table("team_unit"). + Where("team_id IN (SELECT id FROM team WHERE authorize = ?)", AccessModeOwner). + Update(&TeamUnit{ + AccessMode: AccessModeOwner, + }) + if err != nil { + return err + } + log.Debug("Updated %d owner team unit access mode to belong to owner instead of none", count) + + return sess.Commit() +} diff --git a/models/organization/org.go b/models/organization/org.go index 269b3e83288dc..fa2a604721288 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -338,9 +338,10 @@ func CreateOrganization(org *Organization, owner *user_model.User) (err error) { units := make([]TeamUnit, 0, len(unit.AllRepoUnitTypes)) for _, tp := range unit.AllRepoUnitTypes { units = append(units, TeamUnit{ - OrgID: org.ID, - TeamID: t.ID, - Type: tp, + OrgID: org.ID, + TeamID: t.ID, + Type: tp, + AccessMode: perm.AccessModeOwner, }) } diff --git a/models/repo/release.go b/models/repo/release.go index f7b24044b9c2c..c8dd7fbc7a368 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -79,7 +79,7 @@ type Release struct { RenderedNote string `xorm:"-"` IsDraft bool `xorm:"NOT NULL DEFAULT false"` IsPrerelease bool `xorm:"NOT NULL DEFAULT false"` - IsTag bool `xorm:"NOT NULL DEFAULT false"` + IsTag bool `xorm:"NOT NULL DEFAULT false"` // will be true only if the record is a tag and has no related releases Attachments []*Attachment `xorm:"-"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX"` } diff --git a/models/unit/unit.go b/models/unit/unit.go index a2a9079dc25c7..3d5a8842cd638 100644 --- a/models/unit/unit.go +++ b/models/unit/unit.go @@ -151,7 +151,11 @@ func validateDefaultRepoUnits(defaultUnits, settingDefaultUnits []Type) []Type { // LoadUnitConfig load units from settings func LoadUnitConfig() { - DisabledRepoUnits = FindUnitTypes(setting.Repository.DisabledRepoUnits...) + var invalidKeys []string + DisabledRepoUnits, invalidKeys = FindUnitTypes(setting.Repository.DisabledRepoUnits...) + if len(invalidKeys) > 0 { + log.Warn("Invalid keys in disabled repo units: %s", strings.Join(invalidKeys, ", ")) + } // Check that must units are not disabled for i, disabledU := range DisabledRepoUnits { if !disabledU.CanDisable() { @@ -160,9 +164,15 @@ func LoadUnitConfig() { } } - setDefaultRepoUnits := FindUnitTypes(setting.Repository.DefaultRepoUnits...) + setDefaultRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DefaultRepoUnits...) + if len(invalidKeys) > 0 { + log.Warn("Invalid keys in default repo units: %s", strings.Join(invalidKeys, ", ")) + } DefaultRepoUnits = validateDefaultRepoUnits(DefaultRepoUnits, setDefaultRepoUnits) - setDefaultForkRepoUnits := FindUnitTypes(setting.Repository.DefaultForkRepoUnits...) + setDefaultForkRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DefaultForkRepoUnits...) + if len(invalidKeys) > 0 { + log.Warn("Invalid keys in default fork repo units: %s", strings.Join(invalidKeys, ", ")) + } DefaultForkRepoUnits = validateDefaultRepoUnits(DefaultForkRepoUnits, setDefaultForkRepoUnits) } @@ -312,7 +322,7 @@ var ( UnitActions = Unit{ TypeActions, - "actions.actions", + "repo.actions", "/actions", "actions.unit.desc", 7, @@ -334,22 +344,19 @@ var ( } ) -// FindUnitTypes give the unit key names and return unit -func FindUnitTypes(nameKeys ...string) (res []Type) { +// FindUnitTypes give the unit key names and return valid unique units and invalid keys +func FindUnitTypes(nameKeys ...string) (res []Type, invalidKeys []string) { + m := map[Type]struct{}{} for _, key := range nameKeys { - var found bool - for t, u := range Units { - if strings.EqualFold(key, u.NameKey) { - res = append(res, t) - found = true - break - } - } - if !found { - res = append(res, TypeInvalid) + t := TypeFromKey(key) + if t == TypeInvalid { + invalidKeys = append(invalidKeys, key) + } else if _, ok := m[t]; !ok { + res = append(res, t) + m[t] = struct{}{} } } - return res + return res, invalidKeys } // TypeFromKey give the unit key name and return unit diff --git a/models/unit/unit_test.go b/models/unit/unit_test.go new file mode 100644 index 0000000000000..50d781719771d --- /dev/null +++ b/models/unit/unit_test.go @@ -0,0 +1,53 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package unit + +import ( + "testing" + + "code.gitea.io/gitea/modules/setting" + + "github.com/stretchr/testify/assert" +) + +func TestLoadUnitConfig(t *testing.T) { + defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) { + DisabledRepoUnits = disabledRepoUnits + DefaultRepoUnits = defaultRepoUnits + DefaultForkRepoUnits = defaultForkRepoUnits + }(DisabledRepoUnits, DefaultRepoUnits, DefaultForkRepoUnits) + defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) { + setting.Repository.DisabledRepoUnits = disabledRepoUnits + setting.Repository.DefaultRepoUnits = defaultRepoUnits + setting.Repository.DefaultForkRepoUnits = defaultForkRepoUnits + }(setting.Repository.DisabledRepoUnits, setting.Repository.DefaultRepoUnits, setting.Repository.DefaultForkRepoUnits) + + t.Run("regular", func(t *testing.T) { + setting.Repository.DisabledRepoUnits = []string{"repo.issues"} + setting.Repository.DefaultRepoUnits = []string{"repo.code", "repo.releases", "repo.issues", "repo.pulls"} + setting.Repository.DefaultForkRepoUnits = []string{"repo.releases"} + LoadUnitConfig() + assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnits) + assert.Equal(t, []Type{TypeCode, TypeReleases, TypePullRequests}, DefaultRepoUnits) + assert.Equal(t, []Type{TypeCode, TypeReleases}, DefaultForkRepoUnits) + }) + t.Run("invalid", func(t *testing.T) { + setting.Repository.DisabledRepoUnits = []string{"repo.issues", "invalid.1"} + setting.Repository.DefaultRepoUnits = []string{"repo.code", "invalid.2", "repo.releases", "repo.issues", "repo.pulls"} + setting.Repository.DefaultForkRepoUnits = []string{"invalid.3", "repo.releases"} + LoadUnitConfig() + assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnits) + assert.Equal(t, []Type{TypeCode, TypeReleases, TypePullRequests}, DefaultRepoUnits) + assert.Equal(t, []Type{TypeCode, TypeReleases}, DefaultForkRepoUnits) + }) + t.Run("duplicate", func(t *testing.T) { + setting.Repository.DisabledRepoUnits = []string{"repo.issues", "repo.issues"} + setting.Repository.DefaultRepoUnits = []string{"repo.code", "repo.releases", "repo.issues", "repo.pulls", "repo.code"} + setting.Repository.DefaultForkRepoUnits = []string{"repo.releases", "repo.releases"} + LoadUnitConfig() + assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnits) + assert.Equal(t, []Type{TypeCode, TypeReleases, TypePullRequests}, DefaultRepoUnits) + assert.Equal(t, []Type{TypeCode, TypeReleases}, DefaultForkRepoUnits) + }) +} diff --git a/modules/base/tool.go b/modules/base/tool.go index 94f19576b48d7..bd3a8458eeb29 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -22,7 +22,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "github.com/dustin/go-humanize" "github.com/minio/sha256-simd" @@ -142,12 +141,6 @@ func FileSize(s int64) string { return humanize.IBytes(uint64(s)) } -// PrettyNumber produces a string form of the given number in base 10 with -// commas after every three orders of magnitude -func PrettyNumber(i interface{}) string { - return humanize.Comma(util.NumberIntoInt64(i)) -} - // Subtract deals with subtraction of all types of number. func Subtract(left, right interface{}) interface{} { var rleft, rright int64 diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go index 81f4b464e6d36..33677a910cc22 100644 --- a/modules/base/tool_test.go +++ b/modules/base/tool_test.go @@ -114,13 +114,6 @@ func TestFileSize(t *testing.T) { assert.Equal(t, "2.0 EiB", FileSize(size)) } -func TestPrettyNumber(t *testing.T) { - assert.Equal(t, "23,342,432", PrettyNumber(23342432)) - assert.Equal(t, "23,342,432", PrettyNumber(int32(23342432))) - assert.Equal(t, "0", PrettyNumber(0)) - assert.Equal(t, "-100,000", PrettyNumber(-100000)) -} - func TestSubtract(t *testing.T) { toFloat64 := func(n interface{}) float64 { switch v := n.(type) { diff --git a/modules/context/context.go b/modules/context/context.go index 5876e23cc40a2..1eff1459a14af 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -628,7 +628,9 @@ func (ctx *Context) Value(key interface{}) interface{} { if key == git.RepositoryContextKey && ctx.Repo != nil { return ctx.Repo.GitRepo } - + if key == translation.ContextKey && ctx.Locale != nil { + return ctx.Locale + } return ctx.Req.Context().Value(key) } diff --git a/modules/markup/html.go b/modules/markup/html.go index 76fc54cf465eb..11888b8536353 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -22,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/regexplru" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates/vars" + "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/util" "golang.org/x/net/html" @@ -97,14 +98,30 @@ var issueFullPattern *regexp.Regexp // Once for to prevent races var issueFullPatternOnce sync.Once +// regexp for full links to hash comment in pull request files changed tab +var filesChangedFullPattern *regexp.Regexp + +// Once for to prevent races +var filesChangedFullPatternOnce sync.Once + func getIssueFullPattern() *regexp.Regexp { issueFullPatternOnce.Do(func() { + // example: https://domain/org/repo/pulls/27#hash issueFullPattern = regexp.MustCompile(regexp.QuoteMeta(setting.AppURL) + `[\w_.-]+/[\w_.-]+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#](\S+)?)?\b`) }) return issueFullPattern } +func getFilesChangedFullPattern() *regexp.Regexp { + filesChangedFullPatternOnce.Do(func() { + // example: https://domain/org/repo/pulls/27/files#hash + filesChangedFullPattern = regexp.MustCompile(regexp.QuoteMeta(setting.AppURL) + + `[\w_.-]+/[\w_.-]+/pulls/((?:\w{1,10}-)?[1-9][0-9]*)/files([\?|#](\S+)?)?\b`) + }) + return filesChangedFullPattern +} + // CustomLinkURLSchemes allows for additional schemes to be detected when parsing links within text func CustomLinkURLSchemes(schemes []string) { schemes = append(schemes, "http", "https") @@ -793,15 +810,30 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil { return } - next := node.NextSibling for node != nil && node != next { m := getIssueFullPattern().FindStringSubmatchIndex(node.Data) if m == nil { return } + + mDiffView := getFilesChangedFullPattern().FindStringSubmatchIndex(node.Data) + // leave it as it is if the link is from "Files Changed" tab in PR Diff View https://domain/org/repo/pulls/27/files + if mDiffView != nil { + return + } + link := node.Data[m[0]:m[1]] - id := "#" + node.Data[m[2]:m[3]] + text := "#" + node.Data[m[2]:m[3]] + // if m[4] and m[5] is not -1, then link is to a comment + // indicate that in the text by appending (comment) + if m[4] != -1 && m[5] != -1 { + if locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale); ok { + text += " " + locale.Tr("repo.from_comment") + } else { + text += " (comment)" + } + } // extract repo and org name from matched link like // http://localhost:3000/gituser/myrepo/issues/1 @@ -810,12 +842,10 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) { matchRepo := linkParts[len(linkParts)-3] if matchOrg == ctx.Metas["user"] && matchRepo == ctx.Metas["repo"] { - // TODO if m[4]:m[5] is not nil, then link is to a comment, - // and we should indicate that in the text somehow - replaceContent(node, m[0], m[1], createLink(link, id, "ref-issue")) + replaceContent(node, m[0], m[1], createLink(link, text, "ref-issue")) } else { - orgRepoID := matchOrg + "/" + matchRepo + id - replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "ref-issue")) + text = matchOrg + "/" + matchRepo + text + replaceContent(node, m[0], m[1], createLink(link, text, "ref-issue")) } node = node.NextSibling.NextSibling } diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index a048f1f527889..7e04b03531d87 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -330,13 +330,17 @@ func TestRender_FullIssueURLs(t *testing.T) { test("Look here http://localhost:3000/person/repo/issues/4", `Look here person/repo#4`) test("http://localhost:3000/person/repo/issues/4#issuecomment-1234", - `person/repo#4`) + `person/repo#4 (comment)`) test("http://localhost:3000/gogits/gogs/issues/4", `#4`) test("http://localhost:3000/gogits/gogs/issues/4 test", `#4 test`) test("http://localhost:3000/gogits/gogs/issues/4?a=1&b=2#comment-123 test", - `#4 test`) + `#4 (comment) test`) + test("http://localhost:3000/testOrg/testOrgRepo/pulls/2/files#issuecomment-24", + "http://localhost:3000/testOrg/testOrgRepo/pulls/2/files#issuecomment-24") + test("http://localhost:3000/testOrg/testOrgRepo/pulls/2/files", + "http://localhost:3000/testOrg/testOrgRepo/pulls/2/files") } func TestRegExp_sha1CurrentPattern(t *testing.T) { diff --git a/modules/templates/helper.go b/modules/templates/helper.go index a8343428dc19b..54c85863bd827 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -19,7 +19,6 @@ import ( "reflect" "regexp" "runtime" - "strconv" "strings" texttmpl "text/template" "time" @@ -112,18 +111,17 @@ func NewFuncMap() []template.FuncMap { "IsShowFullName": func() bool { return setting.UI.DefaultShowFullName }, - "Safe": Safe, - "SafeJS": SafeJS, - "JSEscape": JSEscape, - "Str2html": Str2html, - "TimeSince": timeutil.TimeSince, - "TimeSinceUnix": timeutil.TimeSinceUnix, - "FileSize": base.FileSize, - "PrettyNumber": base.PrettyNumber, - "JsPrettyNumber": JsPrettyNumber, - "Subtract": base.Subtract, - "EntryIcon": base.EntryIcon, - "MigrationIcon": MigrationIcon, + "Safe": Safe, + "SafeJS": SafeJS, + "JSEscape": JSEscape, + "Str2html": Str2html, + "TimeSince": timeutil.TimeSince, + "TimeSinceUnix": timeutil.TimeSinceUnix, + "FileSize": base.FileSize, + "LocaleNumber": LocaleNumber, + "Subtract": base.Subtract, + "EntryIcon": base.EntryIcon, + "MigrationIcon": MigrationIcon, "Add": func(a ...int) int { sum := 0 for _, val := range a { @@ -410,62 +408,9 @@ func NewFuncMap() []template.FuncMap { "Join": strings.Join, "QueryEscape": url.QueryEscape, "DotEscape": DotEscape, - "Iterate": func(arg interface{}) (items []uint64) { - count := uint64(0) - switch val := arg.(type) { - case uint64: - count = val - case *uint64: - count = *val - case int64: - if val < 0 { - val = 0 - } - count = uint64(val) - case *int64: - if *val < 0 { - *val = 0 - } - count = uint64(*val) - case int: - if val < 0 { - val = 0 - } - count = uint64(val) - case *int: - if *val < 0 { - *val = 0 - } - count = uint64(*val) - case uint: - count = uint64(val) - case *uint: - count = uint64(*val) - case int32: - if val < 0 { - val = 0 - } - count = uint64(val) - case *int32: - if *val < 0 { - *val = 0 - } - count = uint64(*val) - case uint32: - count = uint64(val) - case *uint32: - count = uint64(*val) - case string: - cnt, _ := strconv.ParseInt(val, 10, 64) - if cnt < 0 { - cnt = 0 - } - count = uint64(cnt) - } - if count <= 0 { - return items - } - for i := uint64(0); i < count; i++ { + "Iterate": func(arg interface{}) (items []int64) { + count := util.ToInt64(arg) + for i := int64(0); i < count; i++ { items = append(items, i) } return items @@ -1067,10 +1012,8 @@ func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteNa return a } -// JsPrettyNumber renders a number using english decimal separators, e.g. 1,200 and subsequent -// JS will replace the number with locale-specific separators, based on the user's selected language -func JsPrettyNumber(i interface{}) template.HTML { - num := util.NumberIntoInt64(i) - - return template.HTML(`` + base.PrettyNumber(num) + ``) +// LocaleNumber renders a number with a Custom Element, browser will render it with a locale number +func LocaleNumber(v interface{}) template.HTML { + num := util.ToInt64(v) + return template.HTML(fmt.Sprintf(`%d`, num, num)) } diff --git a/modules/translation/translation.go b/modules/translation/translation.go index 5a1009bfa3d80..3165390c3238c 100644 --- a/modules/translation/translation.go +++ b/modules/translation/translation.go @@ -18,6 +18,10 @@ import ( "golang.org/x/text/language" ) +type contextKey struct{} + +var ContextKey interface{} = &contextKey{} + // Locale represents an interface to translation type Locale interface { Language() string diff --git a/modules/util/truncate.go b/modules/util/truncate.go index 032a6c0872c97..f41d27d8b7432 100644 --- a/modules/util/truncate.go +++ b/modules/util/truncate.go @@ -35,27 +35,3 @@ func SplitStringAtByteN(input string, n int) (left, right string) { return input[:end] + utf8Ellipsis, utf8Ellipsis + input[end:] } - -// SplitStringAtRuneN splits a string at rune n accounting for rune boundaries. (Combining characters are not accounted for.) -func SplitStringAtRuneN(input string, n int) (left, right string) { - if !utf8.ValidString(input) { - if len(input) <= n || n-3 < 0 { - return input, "" - } - return input[:n-3] + asciiEllipsis, asciiEllipsis + input[n-3:] - } - - if utf8.RuneCountInString(input) <= n { - return input, "" - } - - count := 0 - end := 0 - for count < n-1 { - _, size := utf8.DecodeRuneInString(input[end:]) - end += size - count++ - } - - return input[:end] + utf8Ellipsis, utf8Ellipsis + input[end:] -} diff --git a/modules/util/truncate_test.go b/modules/util/truncate_test.go index 912bfb3d5fde0..05e2bc03019b2 100644 --- a/modules/util/truncate_test.go +++ b/modules/util/truncate_test.go @@ -43,18 +43,4 @@ func TestSplitString(t *testing.T) { {"\xef\x03", 1, "\xef\x03", ""}, } test(tc, SplitStringAtByteN) - - tc = []*testCase{ - {"abc123xyz", 0, "", utf8Ellipsis}, - {"abc123xyz", 1, "", utf8Ellipsis}, - {"abc123xyz", 4, "abc", utf8Ellipsis}, - {"啊bc123xyz", 4, "啊bc", utf8Ellipsis}, - {"啊bc123xyz", 6, "啊bc12", utf8Ellipsis}, - {"啊bc", 3, "啊bc", ""}, - {"啊bc", 4, "啊bc", ""}, - {"abc\xef\x03\xfe", 3, "", asciiEllipsis}, - {"abc\xef\x03\xfe", 4, "a", asciiEllipsis}, - {"\xef\x03", 1, "\xef\x03", ""}, - } - test(tc, SplitStringAtRuneN) } diff --git a/modules/util/util.go b/modules/util/util.go index 9d3a8dcfac219..e9ea007ccb8d6 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -7,8 +7,9 @@ import ( "bytes" "crypto/rand" "errors" + "fmt" "math/big" - "regexp" + "os" "strconv" "strings" @@ -185,55 +186,26 @@ func ToUpperASCII(s string) string { return string(b) } -var ( - titleCaser = cases.Title(language.English) - titleCaserNoLower = cases.Title(language.English, cases.NoLower) -) - // ToTitleCase returns s with all english words capitalized func ToTitleCase(s string) string { - return titleCaser.String(s) + // `cases.Title` is not thread-safe, do not use global shared variable for it + return cases.Title(language.English).String(s) } -// ToTitleCaseNoLower returns s with all english words capitalized without lowercasing +// ToTitleCaseNoLower returns s with all english words capitalized without lower-casing func ToTitleCaseNoLower(s string) string { - return titleCaserNoLower.String(s) + // `cases.Title` is not thread-safe, do not use global shared variable for it + return cases.Title(language.English, cases.NoLower).String(s) } -var ( - whitespaceOnly = regexp.MustCompile("(?m)^[ \t]+$") - leadingWhitespace = regexp.MustCompile("(?m)(^[ \t]*)(?:[^ \t\n])") -) - -// Dedent removes common indentation of a multi-line string along with whitespace around it -// Based on https://github.com/lithammer/dedent -func Dedent(s string) string { - var margin string - - s = whitespaceOnly.ReplaceAllString(s, "") - indents := leadingWhitespace.FindAllStringSubmatch(s, -1) - - for i, indent := range indents { - if i == 0 { - margin = indent[1] - } else if strings.HasPrefix(indent[1], margin) { - continue - } else if strings.HasPrefix(margin, indent[1]) { - margin = indent[1] - } else { - margin = "" - break - } - } - - if margin != "" { - s = regexp.MustCompile("(?m)^"+margin).ReplaceAllString(s, "") - } - return strings.TrimSpace(s) +func logError(msg string, args ...any) { + // TODO: the "util" package can not import the "modules/log" package, so we use the "fmt" package here temporarily. + // In the future, we should decouple the dependency between them. + _, _ = fmt.Fprintf(os.Stderr, msg, args...) } -// NumberIntoInt64 transform a given int into int64. -func NumberIntoInt64(number interface{}) int64 { +// ToInt64 transform a given int into int64. +func ToInt64(number interface{}) int64 { var value int64 switch v := number.(type) { case int: @@ -246,6 +218,23 @@ func NumberIntoInt64(number interface{}) int64 { value = int64(v) case int64: value = v + case uint: + value = int64(v) + case uint8: + value = int64(v) + case uint16: + value = int64(v) + case uint32: + value = int64(v) + case uint64: + value = int64(v) + case string: + var err error + if value, err = strconv.ParseInt(v, 10, 64); err != nil { + logError("strconv.ParseInt failed for %q: %v", v, err) + } + default: + logError("unable to convert %q to int64", v) } return value } diff --git a/modules/util/util_test.go b/modules/util/util_test.go index 34fe070d22d44..8cceafa2f66d7 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -224,10 +224,3 @@ func TestToTitleCase(t *testing.T) { assert.Equal(t, ToTitleCase(`foo bar baz`), `Foo Bar Baz`) assert.Equal(t, ToTitleCase(`FOO BAR BAZ`), `Foo Bar Baz`) } - -func TestDedent(t *testing.T) { - assert.Equal(t, Dedent(` - foo - bar - `), "foo\n\tbar") -} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 347f5e43120bd..5834703556c29 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1057,6 +1057,7 @@ issues = Issues pulls = Pull Requests project_board = Projects packages = Packages +actions = Actions labels = Labels org_labels_desc = Organization level labels that can be used with all repositories under this organization org_labels_desc_manage = manage @@ -1103,6 +1104,7 @@ download_file = Download file normal_view = Normal View line = line lines = lines +from_comment = (comment) editor.add_file = Add File editor.new_file = New File diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 35ca44c375bd6..dd284f43bdf67 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -2140,6 +2140,10 @@ settings.dismiss_stale_approvals_desc=プルリクエストの内容を変える settings.require_signed_commits=コミット署名必須 settings.require_signed_commits_desc=署名されていない場合、または署名が検証できなかった場合は、このブランチへのプッシュを拒否します。 settings.protect_branch_name_pattern=保護ブランチ名のパターン +settings.protect_protected_file_patterns=保護されるファイルのパターン (セミコロン';'で区切る): +settings.protect_protected_file_patterns_desc=保護されたファイルは、このブランチにファイルを追加・編集・削除する権限を持つユーザーであっても、直接変更することができなくなります。 セミコロン(';')で区切って複数のパターンを指定できます。 パターンの文法については github.com/gobwas/glob を参照してください。 例: .drone.yml, /docs/**/*.txt +settings.protect_unprotected_file_patterns=保護しないファイルのパターン (セミコロン';'で区切る): +settings.protect_unprotected_file_patterns_desc=保護しないファイルは、ユーザーに書き込み権限があればプッシュ制限をバイパスして直接変更できます。 セミコロン(';')で区切って複数のパターンを指定できます。 パターンの文法については github.com/gobwas/glob を参照してください。 例: .drone.yml, /docs/**/*.txt settings.add_protected_branch=保護を有効にする settings.delete_protected_branch=保護を無効にする settings.update_protect_branch_success=ルール '%s' に対するブランチ保護を更新しました。 @@ -2276,6 +2280,8 @@ diff.image.side_by_side=並べて表示 diff.image.swipe=スワイプ diff.image.overlay=オーバーレイ diff.has_escaped=この行には不可視Unicode文字があります +diff.show_file_tree=ファイルツリーを表示 +diff.hide_file_tree=ファイルツリーを隠す releases.desc=プロジェクトバージョンとダウンロードの追跡。 release.releases=リリース @@ -3361,6 +3367,7 @@ runners.status.idle=アイドル runners.status.active=稼働中 runners.status.offline=オフライン runners.version=バージョン +runners.reset_registration_token_success=ランナー登録トークンをリセットしました runs.all_workflows=すべてのワークフロー runs.open_tab=%d オープン diff --git a/package-lock.json b/package-lock.json index 9d0c83f65641b..b9d998a69d2a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@citation-js/plugin-csl": "0.6.7", "@citation-js/plugin-software-formats": "0.6.1", "@claviska/jquery-minicolors": "2.3.6", + "@github/markdown-toolbar-element": "2.1.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "18.3.0", "@vue/compiler-sfc": "3.2.47", @@ -838,6 +839,11 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@github/markdown-toolbar-element": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@github/markdown-toolbar-element/-/markdown-toolbar-element-2.1.1.tgz", + "integrity": "sha512-J++rpd5H9baztabJQB82h26jtueOeBRSTqetk9Cri+Lj/s28ndu6Tovn0uHQaOKtBWDobFunk9b5pP5vcqt7cA==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", diff --git a/package.json b/package.json index 8ac5c312f6b11..3ccf0c0840baa 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@citation-js/plugin-csl": "0.6.7", "@citation-js/plugin-software-formats": "0.6.1", "@claviska/jquery-minicolors": "2.3.6", + "@github/markdown-toolbar-element": "2.1.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "18.3.0", "@vue/compiler-sfc": "3.2.47", diff --git a/routers/api/v1/activitypub/person.go b/routers/api/v1/activitypub/person.go index 492930b84929b..bc6b82b179921 100644 --- a/routers/api/v1/activitypub/person.go +++ b/routers/api/v1/activitypub/person.go @@ -4,6 +4,7 @@ package activitypub import ( + "fmt" "net/http" "strings" @@ -18,22 +19,23 @@ import ( // Person function returns the Person actor for a user func Person(ctx *context.APIContext) { - // swagger:operation GET /activitypub/user/{username} activitypub activitypubPerson + // swagger:operation GET /activitypub/user-id/{user-id} activitypub activitypubPerson // --- // summary: Returns the Person actor for a user // produces: // - application/json // parameters: - // - name: username + // - name: user-id // in: path - // description: username of the user - // type: string + // description: user ID of the user + // type: integer // required: true // responses: // "200": // "$ref": "#/responses/ActivityPub" - link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name + // TODO: the setting.AppURL during the test doesn't follow the definition: "It always has a '/' suffix" + link := fmt.Sprintf("%s/api/v1/activitypub/user-id/%d", strings.TrimSuffix(setting.AppURL, "/"), ctx.ContextUser.ID) person := ap.PersonNew(ap.IRI(link)) person.Name = ap.NaturalLanguageValuesNew() @@ -85,16 +87,16 @@ func Person(ctx *context.APIContext) { // PersonInbox function handles the incoming data for a user inbox func PersonInbox(ctx *context.APIContext) { - // swagger:operation POST /activitypub/user/{username}/inbox activitypub activitypubPersonInbox + // swagger:operation POST /activitypub/user-id/{user-id}/inbox activitypub activitypubPersonInbox // --- // summary: Send to the inbox // produces: // - application/json // parameters: - // - name: username + // - name: user-id // in: path - // description: username of the user - // type: string + // description: user ID of the user + // type: integer // required: true // responses: // "204": diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 8b13f5492c148..21797bd1a0aaf 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -704,10 +704,15 @@ func Routes(ctx gocontext.Context) *web.Route { if setting.Federation.Enabled { m.Get("/nodeinfo", misc.NodeInfo) m.Group("/activitypub", func() { + // deprecated, remove in 1.20, use /user-id/{user-id} instead m.Group("/user/{username}", func() { m.Get("", activitypub.Person) m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) }, context_service.UserAssignmentAPI()) + m.Group("/user-id/{user-id}", func() { + m.Get("", activitypub.Person) + m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) + }, context_service.UserIDAssignmentAPI()) }) } m.Get("/signing-key.gpg", misc.SigningKey) diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 0c6926759a768..597f846206043 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -135,7 +135,7 @@ func GetTeam(ctx *context.APIContext) { } func attachTeamUnits(team *organization.Team, units []string) { - unitTypes := unit_model.FindUnitTypes(units...) + unitTypes, _ := unit_model.FindUnitTypes(units...) team.Units = make([]*organization.TeamUnit, 0, len(units)) for _, tp := range unitTypes { team.Units = append(team.Units, &organization.TeamUnit{ diff --git a/routers/web/devtest/devtest.go b/routers/web/devtest/devtest.go new file mode 100644 index 0000000000000..eb77d0b927b6b --- /dev/null +++ b/routers/web/devtest/devtest.go @@ -0,0 +1,35 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package devtest + +import ( + "net/http" + "path" + "strings" + + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/templates" +) + +// List all devtest templates, they will be used for e2e tests for the UI components +func List(ctx *context.Context) { + templateNames := templates.GetTemplateAssetNames() + var subNames []string + const prefix = "templates/devtest/" + for _, tmplName := range templateNames { + if strings.HasPrefix(tmplName, prefix) { + subName := strings.TrimSuffix(strings.TrimPrefix(tmplName, prefix), ".tmpl") + if subName != "list" { + subNames = append(subNames, subName) + } + } + } + ctx.Data["SubNames"] = subNames + ctx.HTML(http.StatusOK, "devtest/list") +} + +func Tmpl(ctx *context.Context) { + ctx.HTML(http.StatusOK, base.TplName("devtest"+path.Clean("/"+ctx.Params("sub")))) +} diff --git a/routers/web/misc/markup.go b/routers/web/misc/markup.go index f678316f4429f..169037894530c 100644 --- a/routers/web/misc/markup.go +++ b/routers/web/misc/markup.go @@ -15,24 +15,6 @@ import ( // Markup render markup document to HTML func Markup(ctx *context.Context) { - // swagger:operation POST /markup miscellaneous renderMarkup - // --- - // summary: Render a markup document as HTML - // parameters: - // - name: body - // in: body - // schema: - // "$ref": "#/definitions/MarkupOption" - // consumes: - // - application/json - // produces: - // - text/html - // responses: - // "200": - // "$ref": "#/responses/MarkupRender" - // "422": - // "$ref": "#/responses/validationError" - form := web.GetForm(ctx).(*api.MarkupOption) if ctx.HasAPIError() { diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 654e9000fa1b5..7d84c101d8a60 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -246,7 +246,6 @@ func Labels(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.labels") ctx.Data["PageIsOrgSettings"] = true ctx.Data["PageIsOrgSettingsLabels"] = true - ctx.Data["RequireTribute"] = true ctx.Data["LabelTemplates"] = repo_module.LabelTemplates ctx.HTML(http.StatusOK, tplSettingsLabels) } diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index 843b1d8dfd01d..7439c2411b908 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -253,7 +253,6 @@ func FileHistory(ctx *context.Context) { // Diff show different from current commit to previous commit func Diff(ctx *context.Context) { ctx.Data["PageIsDiff"] = true - ctx.Data["RequireTribute"] = true userName := ctx.Repo.Owner.Name repoName := ctx.Repo.Repository.Name diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index d7e7bac7b7815..c49eb762d8a66 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -781,7 +781,6 @@ func CompareDiff(ctx *context.Context) { ctx.Data["IsRepoToolbarCommits"] = true ctx.Data["IsDiffCompare"] = true - ctx.Data["RequireTribute"] = true templateErrs := setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates) if len(templateErrs) > 0 { diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 2b66be22aeb96..f65e1ad3d81b1 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -538,7 +538,6 @@ func DeleteFilePost(ctx *context.Context) { // UploadFile render upload file page func UploadFile(ctx *context.Context) { ctx.Data["PageIsUpload"] = true - ctx.Data["RequireTribute"] = true upload.AddUploadContext(ctx, "repo") canCommit := renderCommitRights(ctx) treePath := cleanUploadFileName(ctx.Repo.TreePath) @@ -573,7 +572,6 @@ func UploadFile(ctx *context.Context) { func UploadFilePost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.UploadRepoFileForm) ctx.Data["PageIsUpload"] = true - ctx.Data["RequireTribute"] = true upload.AddUploadContext(ctx, "repo") canCommit := renderCommitRights(ctx) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 612222598f2fa..e4f1172dd966c 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -849,7 +849,6 @@ func NewIssue(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.issues.new") ctx.Data["PageIsIssueList"] = true ctx.Data["NewIssueChooseTemplate"] = ctx.HasIssueTemplatesOrContactLinks() - ctx.Data["RequireTribute"] = true ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes title := ctx.FormString("title") ctx.Data["TitleQuery"] = title @@ -1295,7 +1294,6 @@ func ViewIssue(ctx *context.Context) { ctx.Data["IssueType"] = "all" } - ctx.Data["RequireTribute"] = true ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(unit.TypeProjects) ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") diff --git a/routers/web/repo/issue_label.go b/routers/web/repo/issue_label.go index 31bf85fedb29a..3123359a65e6f 100644 --- a/routers/web/repo/issue_label.go +++ b/routers/web/repo/issue_label.go @@ -28,7 +28,6 @@ func Labels(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.labels") ctx.Data["PageIsIssueList"] = true ctx.Data["PageIsLabels"] = true - ctx.Data["RequireTribute"] = true ctx.Data["LabelTemplates"] = repo_module.LabelTemplates ctx.HTML(http.StatusOK, tplLabels) } diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 4f99687738247..30004cefb70be 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -203,6 +203,7 @@ func Fork(ctx *context.Context) { func ForkPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.CreateRepoForm) ctx.Data["Title"] = ctx.Tr("new_fork") + ctx.Data["CanForkRepo"] = true ctxUser := checkContextUser(ctx, form.UID) if ctx.Written() { @@ -791,7 +792,6 @@ func ViewPullFiles(ctx *context.Context) { setCompareContext(ctx, baseCommit, commit, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name) - ctx.Data["RequireTribute"] = true if ctx.Data["Assignees"], err = repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository); err != nil { ctx.ServerError("GetAssignees", err) return @@ -1160,7 +1160,6 @@ func CompareAndPullRequestPost(ctx *context.Context) { ctx.Data["PageIsComparePull"] = true ctx.Data["IsDiffCompare"] = true ctx.Data["IsRepoToolbarCommits"] = true - ctx.Data["RequireTribute"] = true ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 3ffadd34ace7b..b8c5f67f45a7e 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -308,7 +308,6 @@ func LatestRelease(ctx *context.Context) { func NewRelease(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.release.new_release") ctx.Data["PageIsReleaseList"] = true - ctx.Data["RequireTribute"] = true ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch if tagName := ctx.FormString("tag"); len(tagName) > 0 { rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName) @@ -351,7 +350,6 @@ func NewReleasePost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.NewReleaseForm) ctx.Data["Title"] = ctx.Tr("repo.release.new_release") ctx.Data["PageIsReleaseList"] = true - ctx.Data["RequireTribute"] = true if ctx.HasError() { ctx.HTML(http.StatusOK, tplReleaseNew) @@ -469,7 +467,6 @@ func EditRelease(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.release.edit_release") ctx.Data["PageIsReleaseList"] = true ctx.Data["PageIsEditRelease"] = true - ctx.Data["RequireTribute"] = true ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "release") @@ -514,7 +511,6 @@ func EditReleasePost(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.release.edit_release") ctx.Data["PageIsReleaseList"] = true ctx.Data["PageIsEditRelease"] = true - ctx.Data["RequireTribute"] = true tagName := ctx.Params("*") rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName) diff --git a/routers/web/web.go b/routers/web/web.go index 4bd2f76c571f1..6b62ff6f83722 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -27,6 +27,7 @@ import ( "code.gitea.io/gitea/modules/web/routing" "code.gitea.io/gitea/routers/web/admin" "code.gitea.io/gitea/routers/web/auth" + "code.gitea.io/gitea/routers/web/devtest" "code.gitea.io/gitea/routers/web/events" "code.gitea.io/gitea/routers/web/explore" "code.gitea.io/gitea/routers/web/feed" @@ -1491,6 +1492,12 @@ func RegisterRoutes(m *web.Route) { if setting.API.EnableSwagger { m.Get("/swagger.v1.json", SwaggerV1Json) } + + if !setting.IsProd { + m.Any("/devtest", devtest.List) + m.Any("/devtest/{sub}", devtest.Tmpl) + } + m.NotFound(func(w http.ResponseWriter, req *http.Request) { ctx := context.GetContext(req) ctx.NotFound("", nil) diff --git a/routers/web/webfinger.go b/routers/web/webfinger.go index a8e816d7b7ab4..1442e09a31431 100644 --- a/routers/web/webfinger.go +++ b/routers/web/webfinger.go @@ -85,7 +85,7 @@ func WebfingerQuery(ctx *context.Context) { aliases := []string{ u.HTMLURL(), - appURL.String() + "api/v1/activitypub/user/" + url.PathEscape(u.Name), + appURL.String() + "api/v1/activitypub/user-id/" + fmt.Sprint(u.ID), } if !u.KeepEmailPrivate { aliases = append(aliases, fmt.Sprintf("mailto:%s", u.Email)) @@ -104,7 +104,7 @@ func WebfingerQuery(ctx *context.Context) { { Rel: "self", Type: "application/activity+json", - Href: appURL.String() + "api/v1/activitypub/user/" + url.PathEscape(u.Name), + Href: appURL.String() + "api/v1/activitypub/user-id/" + fmt.Sprint(u.ID), }, } diff --git a/services/context/user.go b/services/context/user.go index 9dc84c3ac15e1..c713667bca75a 100644 --- a/services/context/user.go +++ b/services/context/user.go @@ -29,6 +29,27 @@ func UserAssignmentWeb() func(ctx *context.Context) { } } +// UserIDAssignmentAPI returns a middleware to handle context-user assignment for api routes +func UserIDAssignmentAPI() func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + userID := ctx.ParamsInt64(":user-id") + + if ctx.IsSigned && ctx.Doer.ID == userID { + ctx.ContextUser = ctx.Doer + } else { + var err error + ctx.ContextUser, err = user_model.GetUserByID(ctx, userID) + if err != nil { + if user_model.IsErrUserNotExist(err) { + ctx.Error(http.StatusNotFound, "GetUserByID", err) + } else { + ctx.Error(http.StatusInternalServerError, "GetUserByID", err) + } + } + } + } +} + // UserAssignmentAPI returns a middleware to handle context-user assignment for api routes func UserAssignmentAPI() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 9b07774653478..49007e572da96 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -212,7 +212,7 @@
- +
diff --git a/templates/base/head_script.tmpl b/templates/base/head_script.tmpl index 62fb10d89fa96..670d146b56a04 100644 --- a/templates/base/head_script.tmpl +++ b/templates/base/head_script.tmpl @@ -15,23 +15,19 @@ If you introduce mistakes in it, Gitea JavaScript code wouldn't run correctly. useServiceWorker: {{UseServiceWorker}}, csrfToken: '{{.CsrfToken}}', pageData: {{.PageData}}, - requireTribute: {{.RequireTribute}}, notificationSettings: {{NotificationSettings}}, {{/*a map provided by NewFuncMap in helper.go*/}} enableTimeTracking: {{EnableTimetracking}}, - {{if .RequireTribute}} + {{if or .Participants .Assignees .MentionableTeams}} tributeValues: Array.from(new Map([ - {{range .Participants}} - ['{{.Name}}', {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', - name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink $.Context}}'}], - {{end}} - {{range .Assignees}} - ['{{.Name}}', {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', - name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink $.Context}}'}], - {{end}} - {{range .MentionableTeams}} - ['{{$.MentionableTeamsOrg}}/{{.Name}}', {key: '{{$.MentionableTeamsOrg}}/{{.Name}}', value: '{{$.MentionableTeamsOrg}}/{{.Name}}', - name: '{{$.MentionableTeamsOrg}}/{{.Name}}', avatar: '{{$.MentionableTeamsOrgAvatar}}'}], - {{end}} + {{- range .Participants -}} + ['{{.Name}}', {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink $.Context}}'}], + {{- end -}} + {{- range .Assignees -}} + ['{{.Name}}', {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink $.Context}}'}], + {{- end -}} + {{- range .MentionableTeams -}} + ['{{$.MentionableTeamsOrg}}/{{.Name}}', {key: '{{$.MentionableTeamsOrg}}/{{.Name}}', value: '{{$.MentionableTeamsOrg}}/{{.Name}}', name: '{{$.MentionableTeamsOrg}}/{{.Name}}', avatar: '{{$.MentionableTeamsOrgAvatar}}'}], + {{- end -}} ]).values()), {{end}} mermaidMaxSourceCharacters: {{MermaidMaxSourceCharacters}}, diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl new file mode 100644 index 0000000000000..c5ab863d00d04 --- /dev/null +++ b/templates/devtest/gitea-ui.tmpl @@ -0,0 +1,12 @@ +{{template "base/head" .}} +
+
+ + +
+
+ text with tooltip +
+ {{template "shared/combomarkdowneditor" .}} +
+{{template "base/footer" .}} diff --git a/templates/devtest/list.tmpl b/templates/devtest/list.tmpl new file mode 100644 index 0000000000000..3a519c328ee93 --- /dev/null +++ b/templates/devtest/list.tmpl @@ -0,0 +1,5 @@ + diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl index 5062109161ee1..73ae5ab6e4076 100644 --- a/templates/projects/list.tmpl +++ b/templates/projects/list.tmpl @@ -13,11 +13,11 @@ @@ -46,9 +46,9 @@ {{end}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} - {{JsPrettyNumber .NumOpenIssues}} {{$.locale.Tr "repo.issues.open_title"}} + {{LocaleNumber .NumOpenIssues}} {{$.locale.Tr "repo.issues.open_title"}} {{svg "octicon-check" 16 "gt-mr-3"}} - {{JsPrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}} + {{LocaleNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}}
{{if and $.CanWriteProjects (not $.Repository.IsArchived)}} diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index 36e669276eff9..21ea63cc0a466 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -198,24 +198,21 @@ {{if not $.Repository.IsArchived}} -
+ {{end}} {{template "repo/issue/view_content/reference_issue_dialog" .}} diff --git a/templates/repo/diff/comment_form.tmpl b/templates/repo/diff/comment_form.tmpl index 394a392bb9b88..109f1679672d3 100644 --- a/templates/repo/diff/comment_form.tmpl +++ b/templates/repo/diff/comment_form.tmpl @@ -9,18 +9,16 @@ - -
-
- -
-
- {{.locale.Tr "loading"}} -
-
+ + {{template "shared/combomarkdowneditor" (dict + "locale" $.root.locale + "MarkdownPreviewUrl" (print $.root.Repository.Link "/markup") + "MarkdownPreviewContext" $.root.RepoLink + "TextareaName" "content" + "TextareaPlaceholder" ($.locale.Tr "repo.diff.comment.placeholder") + "DropzoneParentContainer" "form" + )}} +