mirror of
https://github.com/go-gitea/gitea.git
synced 2026-04-02 00:18:35 +01:00
Fix URLJoin, markup render link reoslving, sign-in/up/linkaccount page common data (#36861)
The logic of "URLJoin" is unclear and it is often abused. Also: * Correct the `resolveLinkRelative` behavior * Fix missing "PathEscape" in `ToTag` * Fix more FIXMEs, and add new FIXMEs for newly found problems * Refactor "auth page common template data"
This commit is contained in:
@@ -11,7 +11,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CamoEncode encodes a lnk to fit with the go-camo and camo proxy links. The purposes of camo-proxy are:
|
// CamoEncode encodes a lnk to fit with the go-camo and camo proxy links. The purposes of camo-proxy are:
|
||||||
@@ -27,7 +26,7 @@ func CamoEncode(link string) string {
|
|||||||
macSum := b64encode(mac.Sum(nil))
|
macSum := b64encode(mac.Sum(nil))
|
||||||
encodedURL := b64encode([]byte(link))
|
encodedURL := b64encode([]byte(link))
|
||||||
|
|
||||||
return util.URLJoin(setting.Camo.ServerURL, macSum, encodedURL)
|
return strings.TrimSuffix(setting.Camo.ServerURL, "/") + "/" + macSum + "/" + encodedURL
|
||||||
}
|
}
|
||||||
|
|
||||||
func b64encode(data []byte) string {
|
func b64encode(data []byte) string {
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
package markup
|
package markup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/httplib"
|
"code.gitea.io/gitea/modules/httplib"
|
||||||
"code.gitea.io/gitea/modules/references"
|
"code.gitea.io/gitea/modules/references"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
|
|
||||||
"golang.org/x/net/html"
|
"golang.org/x/net/html"
|
||||||
"golang.org/x/net/html/atom"
|
"golang.org/x/net/html/atom"
|
||||||
@@ -219,7 +219,7 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
link := "/:root/" + util.URLJoin(ctx.RenderOptions.Metas["user"], ctx.RenderOptions.Metas["repo"], "commit", hash)
|
link := fmt.Sprintf("/:root/%s/%s/commit/%s", ctx.RenderOptions.Metas["user"], ctx.RenderOptions.Metas["repo"], hash)
|
||||||
replaceContent(node, m[2], m[3], createCodeLink(link, base.ShortSha(hash), "commit"))
|
replaceContent(node, m[2], m[3], createCodeLink(link, base.ShortSha(hash), "commit"))
|
||||||
start = 0
|
start = 0
|
||||||
node = node.NextSibling.NextSibling
|
node = node.NextSibling.NextSibling
|
||||||
@@ -236,7 +236,7 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refText := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
|
refText := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
|
||||||
linkHref := "/:root/" + util.URLJoin(ref.Owner, ref.Name, "commit", ref.CommitSha)
|
linkHref := fmt.Sprintf("/:root/%s/%s/commit/%s", ref.Owner, ref.Name, ref.CommitSha)
|
||||||
link := createLink(ctx, linkHref, refText, "commit")
|
link := createLink(ctx, linkHref, refText, "commit")
|
||||||
|
|
||||||
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
|
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package markup
|
package markup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -162,7 +163,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
|
|||||||
issueOwner := util.Iif(ref.Owner == "", ctx.RenderOptions.Metas["user"], ref.Owner)
|
issueOwner := util.Iif(ref.Owner == "", ctx.RenderOptions.Metas["user"], ref.Owner)
|
||||||
issueRepo := util.Iif(ref.Owner == "", ctx.RenderOptions.Metas["repo"], ref.Name)
|
issueRepo := util.Iif(ref.Owner == "", ctx.RenderOptions.Metas["repo"], ref.Name)
|
||||||
issuePath := util.Iif(ref.IsPull, "pulls", "issues")
|
issuePath := util.Iif(ref.IsPull, "pulls", "issues")
|
||||||
linkHref := "/:root/" + util.URLJoin(issueOwner, issueRepo, issuePath, ref.Issue)
|
linkHref := fmt.Sprintf("/:root/%s/%s/%s/%s", issueOwner, issueRepo, issuePath, ref.Issue)
|
||||||
|
|
||||||
// at the moment, only render the issue index in a full line (or simple line) as icon+title
|
// at the moment, only render the issue index in a full line (or simple line) as icon+title
|
||||||
// otherwise it would be too noisy for "take #1 as an example" in a sentence
|
// otherwise it would be too noisy for "take #1 as an example" in a sentence
|
||||||
|
|||||||
@@ -113,16 +113,17 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
|
|||||||
}
|
}
|
||||||
childNode.Parent = linkNode
|
childNode.Parent = linkNode
|
||||||
absoluteLink := IsFullURLString(link)
|
absoluteLink := IsFullURLString(link)
|
||||||
if !absoluteLink {
|
// FIXME: it should be fully refactored in the future, it uses various hacky approaches to guess how to encode a path for wiki
|
||||||
|
// When a link contains "/", then we assume that the user has provided a well-encoded link.
|
||||||
|
if !absoluteLink && !strings.Contains(link, "/") {
|
||||||
|
// So only guess for links without "/".
|
||||||
if image {
|
if image {
|
||||||
link = strings.ReplaceAll(link, " ", "+")
|
link = strings.ReplaceAll(link, " ", "+")
|
||||||
} else {
|
} else {
|
||||||
// the hacky wiki name encoding: space to "-"
|
// the hacky wiki name encoding: space to "-"
|
||||||
link = strings.ReplaceAll(link, " ", "-") // FIXME: it should support dashes in the link, eg: "the-dash-support.-"
|
link = strings.ReplaceAll(link, " ", "-") // FIXME: it should support dashes in the link, eg: "the-dash-support.-"
|
||||||
}
|
}
|
||||||
if !strings.Contains(link, "/") {
|
link = url.PathEscape(link)
|
||||||
link = url.PathEscape(link) // FIXME: it doesn't seem right and it might cause double-escaping
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if image {
|
if image {
|
||||||
title := props["title"]
|
title := props["title"]
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package markup
|
package markup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/references"
|
"code.gitea.io/gitea/modules/references"
|
||||||
@@ -26,14 +27,11 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
|
|||||||
loc.End += start
|
loc.End += start
|
||||||
mention := node.Data[loc.Start:loc.End]
|
mention := node.Data[loc.Start:loc.End]
|
||||||
teams, ok := ctx.RenderOptions.Metas["teams"]
|
teams, ok := ctx.RenderOptions.Metas["teams"]
|
||||||
// FIXME: util.URLJoin may not be necessary here:
|
|
||||||
// - setting.AppURL is defined to have a terminal '/' so unless mention[1:]
|
|
||||||
// is an AppSubURL link we can probably fallback to concatenation.
|
|
||||||
// team mention should follow @orgName/teamName style
|
|
||||||
if ok && strings.Contains(mention, "/") {
|
if ok && strings.Contains(mention, "/") {
|
||||||
mentionOrgAndTeam := strings.Split(mention, "/")
|
mentionOrgAndTeam := strings.Split(mention, "/")
|
||||||
if mentionOrgAndTeam[0][1:] == ctx.RenderOptions.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") {
|
if mentionOrgAndTeam[0][1:] == ctx.RenderOptions.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") {
|
||||||
link := "/:root/" + util.URLJoin("org", ctx.RenderOptions.Metas["org"], "teams", mentionOrgAndTeam[1])
|
link := fmt.Sprintf("/:root/org/%s/teams/%s", ctx.RenderOptions.Metas["org"], mentionOrgAndTeam[1])
|
||||||
replaceContent(node, loc.Start, loc.End, createLink(ctx, link, mention, "" /*mention*/))
|
replaceContent(node, loc.Start, loc.End, createLink(ctx, link, mention, "" /*mention*/))
|
||||||
node = node.NextSibling.NextSibling
|
node = node.NextSibling.NextSibling
|
||||||
start = 0
|
start = 0
|
||||||
|
|||||||
@@ -389,7 +389,7 @@ func TestRender_ShortLinks(t *testing.T) {
|
|||||||
imgurl := util.URLJoin(tree, "Link.jpg")
|
imgurl := util.URLJoin(tree, "Link.jpg")
|
||||||
otherImgurl := util.URLJoin(tree, "Link+Other.jpg")
|
otherImgurl := util.URLJoin(tree, "Link+Other.jpg")
|
||||||
encodedImgurl := util.URLJoin(tree, "Link+%23.jpg")
|
encodedImgurl := util.URLJoin(tree, "Link+%23.jpg")
|
||||||
notencodedImgurl := util.URLJoin(tree, "some", "path", "Link+#.jpg")
|
notencodedImgurl := util.URLJoin(tree, "some", "path", "Link%20#.jpg")
|
||||||
renderableFileURL := util.URLJoin(tree, "markdown_file.md")
|
renderableFileURL := util.URLJoin(tree, "markdown_file.md")
|
||||||
unrenderableFileURL := util.URLJoin(tree, "file.zip")
|
unrenderableFileURL := util.URLJoin(tree, "file.zip")
|
||||||
favicon := "http://google.com/favicon.ico"
|
favicon := "http://google.com/favicon.ico"
|
||||||
@@ -466,6 +466,8 @@ func TestRender_ShortLinks(t *testing.T) {
|
|||||||
"[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]",
|
"[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]",
|
||||||
`<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Title" alt="AltName"/></a></p>`,
|
`<p><a href="`+encodedImgurl+`" rel="nofollow"><img src="`+encodedImgurl+`" title="Title" alt="AltName"/></a></p>`,
|
||||||
)
|
)
|
||||||
|
// FIXME: it's unable to resolve: [[link?k=v]]
|
||||||
|
// FIXME: it is a wrong test case, it is not an image, but a link with anchor "#.jpg"
|
||||||
test(
|
test(
|
||||||
"[[some/path/Link #.jpg]]",
|
"[[some/path/Link #.jpg]]",
|
||||||
`<p><a href="`+notencodedImgurl+`" rel="nofollow"><img src="`+notencodedImgurl+`" title="Link #.jpg" alt="some/path/Link #.jpg"/></a></p>`,
|
`<p><a href="`+notencodedImgurl+`" rel="nofollow"><img src="`+notencodedImgurl+`" title="Link #.jpg" alt="some/path/Link #.jpg"/></a></p>`,
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/markup/markdown"
|
"code.gitea.io/gitea/modules/markup/markdown"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/test"
|
"code.gitea.io/gitea/modules/test"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@@ -23,7 +22,6 @@ const (
|
|||||||
AppURL = "http://localhost:3000/"
|
AppURL = "http://localhost:3000/"
|
||||||
testRepoOwnerName = "user13"
|
testRepoOwnerName = "user13"
|
||||||
testRepoName = "repo11"
|
testRepoName = "repo11"
|
||||||
FullURL = AppURL + testRepoOwnerName + "/" + testRepoName + "/"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// these values should match the const above
|
// these values should match the const above
|
||||||
@@ -47,8 +45,9 @@ func TestRender_StandardLinks(t *testing.T) {
|
|||||||
func TestRender_Images(t *testing.T) {
|
func TestRender_Images(t *testing.T) {
|
||||||
setting.AppURL = AppURL
|
setting.AppURL = AppURL
|
||||||
|
|
||||||
|
const baseLink = "http://localhost:3000/user13/repo11"
|
||||||
render := func(input, expected string) {
|
render := func(input, expected string) {
|
||||||
buffer, err := markdown.RenderString(markup.NewTestRenderContext(FullURL), input)
|
buffer, err := markdown.RenderString(markup.NewTestRenderContext(baseLink), input)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
|
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
|
||||||
}
|
}
|
||||||
@@ -56,7 +55,7 @@ func TestRender_Images(t *testing.T) {
|
|||||||
url := "../../.images/src/02/train.jpg"
|
url := "../../.images/src/02/train.jpg"
|
||||||
title := "Train"
|
title := "Train"
|
||||||
href := "https://gitea.io"
|
href := "https://gitea.io"
|
||||||
result := util.URLJoin(FullURL, url)
|
result := baseLink + "/.images/src/02/train.jpg" // resolved link should not go out of the base link
|
||||||
// hint: With Markdown v2.5.2, there is a new syntax: [link](URL){:target="_blank"} , but we do not support it now
|
// hint: With Markdown v2.5.2, there is a new syntax: [link](URL){:target="_blank"} , but we do not support it now
|
||||||
|
|
||||||
render(
|
render(
|
||||||
@@ -88,6 +87,7 @@ func TestRender_Images(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTotal_RenderString(t *testing.T) {
|
func TestTotal_RenderString(t *testing.T) {
|
||||||
|
const FullURL = AppURL + testRepoOwnerName + "/" + testRepoName + "/"
|
||||||
setting.AppURL = AppURL
|
setting.AppURL = AppURL
|
||||||
defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
|
defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
|
||||||
|
|
||||||
|
|||||||
@@ -5,28 +5,47 @@ package markup
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/httplib"
|
"code.gitea.io/gitea/modules/httplib"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// resolveLinkRelative tries to resolve the link relative to the "{base}/{cur}", and returns the final link.
|
||||||
|
// It only resolves the link, doesn't do any sanitization or validation, invalid links will be returned as is.
|
||||||
func resolveLinkRelative(ctx context.Context, base, cur, link string, absolute bool) (finalLink string) {
|
func resolveLinkRelative(ctx context.Context, base, cur, link string, absolute bool) (finalLink string) {
|
||||||
if IsFullURLString(link) {
|
linkURL, err := url.Parse(link)
|
||||||
return link
|
if err != nil {
|
||||||
|
return link // invalid URL, return as is
|
||||||
}
|
}
|
||||||
|
if linkURL.Scheme != "" || linkURL.Host != "" {
|
||||||
|
return link // absolute URL, return as is
|
||||||
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(link, "/") {
|
if strings.HasPrefix(link, "/") {
|
||||||
if strings.HasPrefix(link, base) && strings.Count(base, "/") >= 4 {
|
if strings.HasPrefix(link, base) && strings.Count(base, "/") >= 4 {
|
||||||
// a trick to tolerate that some users were using absolute paths (the old gitea's behavior)
|
// a trick to tolerate that some users were using absolute paths (the old Gitea's behavior)
|
||||||
|
// if the link is likely "{base}/src/main" while "{base}" is something like "/owner/repo"
|
||||||
finalLink = link
|
finalLink = link
|
||||||
} else {
|
} else {
|
||||||
finalLink = util.URLJoin(base, "./", link)
|
// need to resolve the link relative to "{base}"
|
||||||
|
cur = ""
|
||||||
|
}
|
||||||
|
} // else: link is relative to "{base}/{cur}"
|
||||||
|
|
||||||
|
if finalLink == "" {
|
||||||
|
finalLink = strings.TrimSuffix(base, "/") + path.Join("/"+cur, "/"+linkURL.EscapedPath())
|
||||||
|
finalLink = strings.TrimSuffix(finalLink, "/")
|
||||||
|
if linkURL.RawQuery != "" {
|
||||||
|
finalLink += "?" + linkURL.RawQuery
|
||||||
|
}
|
||||||
|
if linkURL.Fragment != "" {
|
||||||
|
finalLink += "#" + linkURL.Fragment
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
finalLink = util.URLJoin(base, "./", cur, link)
|
|
||||||
}
|
}
|
||||||
finalLink = strings.TrimSuffix(finalLink, "/")
|
|
||||||
if absolute {
|
if absolute {
|
||||||
finalLink = httplib.MakeAbsoluteURL(ctx, finalLink)
|
finalLink = httplib.MakeAbsoluteURL(ctx, finalLink)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,16 @@ func TestResolveLinkRelative(t *testing.T) {
|
|||||||
assert.Equal(t, "/a/b", resolveLinkRelative(ctx, "/a", "b", "", false))
|
assert.Equal(t, "/a/b", resolveLinkRelative(ctx, "/a", "b", "", false))
|
||||||
assert.Equal(t, "/a/b/c", resolveLinkRelative(ctx, "/a", "b", "c", false))
|
assert.Equal(t, "/a/b/c", resolveLinkRelative(ctx, "/a", "b", "c", false))
|
||||||
assert.Equal(t, "/a/c", resolveLinkRelative(ctx, "/a", "b", "/c", false))
|
assert.Equal(t, "/a/c", resolveLinkRelative(ctx, "/a", "b", "/c", false))
|
||||||
|
assert.Equal(t, "/a/c#id", resolveLinkRelative(ctx, "/a", "b", "/c#id", false))
|
||||||
|
assert.Equal(t, "/a/%2f?k=/", resolveLinkRelative(ctx, "/a", "b", "/%2f/?k=/", false))
|
||||||
|
assert.Equal(t, "/a/b/c?k=v#id", resolveLinkRelative(ctx, "/a", "b", "c/?k=v#id", false))
|
||||||
|
assert.Equal(t, "%invalid", resolveLinkRelative(ctx, "/a", "b", "%invalid", false))
|
||||||
assert.Equal(t, "http://localhost:3000/a", resolveLinkRelative(ctx, "/a", "", "", true))
|
assert.Equal(t, "http://localhost:3000/a", resolveLinkRelative(ctx, "/a", "", "", true))
|
||||||
|
|
||||||
|
// absolute link is returned as is
|
||||||
|
assert.Equal(t, "mailto:user@domain.com", resolveLinkRelative(ctx, "/a", "", "mailto:user@domain.com", false))
|
||||||
|
assert.Equal(t, "http://other/path/", resolveLinkRelative(ctx, "/a", "", "http://other/path/", false))
|
||||||
|
|
||||||
// some users might have used absolute paths a lot, so if the prefix overlaps and has enough slashes, we should tolerate it
|
// some users might have used absolute paths a lot, so if the prefix overlaps and has enough slashes, we should tolerate it
|
||||||
assert.Equal(t, "/owner/repo/foo/owner/repo/foo/bar/xxx", resolveLinkRelative(ctx, "/owner/repo/foo", "", "/owner/repo/foo/bar/xxx", false))
|
assert.Equal(t, "/owner/repo/foo/owner/repo/foo/bar/xxx", resolveLinkRelative(ctx, "/owner/repo/foo", "", "/owner/repo/foo/bar/xxx", false))
|
||||||
assert.Equal(t, "/owner/repo/foo/bar/xxx", resolveLinkRelative(ctx, "/owner/repo/foo/bar", "", "/owner/repo/foo/bar/xxx", false))
|
assert.Equal(t, "/owner/repo/foo/bar/xxx", resolveLinkRelative(ctx, "/owner/repo/foo/bar", "", "/owner/repo/foo/bar/xxx", false))
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Response is the structure of JSON returned from API
|
// Response is the structure of JSON returned from API
|
||||||
@@ -24,17 +23,16 @@ type Response struct {
|
|||||||
ErrorCodes []ErrorCode `json:"error-codes"`
|
ErrorCodes []ErrorCode `json:"error-codes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiURL = "api/siteverify"
|
|
||||||
|
|
||||||
// Verify calls Google Recaptcha API to verify token
|
// Verify calls Google Recaptcha API to verify token
|
||||||
func Verify(ctx context.Context, response string) (bool, error) {
|
func Verify(ctx context.Context, response string) (bool, error) {
|
||||||
post := url.Values{
|
post := url.Values{
|
||||||
"secret": {setting.Service.RecaptchaSecret},
|
"secret": {setting.Service.RecaptchaSecret},
|
||||||
"response": {response},
|
"response": {response},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reqURL := strings.TrimSuffix(setting.Service.RecaptchaURL, "/") + "/api/siteverify"
|
||||||
// Basically a copy of http.PostForm, but with a context
|
// Basically a copy of http.PostForm, but with a context
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, reqURL, strings.NewReader(post.Encode()))
|
||||||
util.URLJoin(setting.Service.RecaptchaURL, apiURL), strings.NewReader(post.Encode()))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("Failed to create CAPTCHA request: %w", err)
|
return false, fmt.Errorf("Failed to create CAPTCHA request: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Scheme describes protocol types
|
// Scheme describes protocol types
|
||||||
@@ -163,7 +162,7 @@ func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash
|
// MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash
|
||||||
func MakeAbsoluteAssetURL(appURL, staticURLPrefix string) string {
|
func MakeAbsoluteAssetURL(appURL *url.URL, staticURLPrefix string) string {
|
||||||
parsedPrefix, err := url.Parse(strings.TrimSuffix(staticURLPrefix, "/"))
|
parsedPrefix, err := url.Parse(strings.TrimSuffix(staticURLPrefix, "/"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Unable to parse STATIC_URL_PREFIX: %v", err)
|
log.Fatal("Unable to parse STATIC_URL_PREFIX: %v", err)
|
||||||
@@ -171,11 +170,12 @@ func MakeAbsoluteAssetURL(appURL, staticURLPrefix string) string {
|
|||||||
|
|
||||||
if err == nil && parsedPrefix.Hostname() == "" {
|
if err == nil && parsedPrefix.Hostname() == "" {
|
||||||
if staticURLPrefix == "" {
|
if staticURLPrefix == "" {
|
||||||
return strings.TrimSuffix(appURL, "/")
|
return strings.TrimSuffix(appURL.String(), "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
// StaticURLPrefix is just a path
|
// StaticURLPrefix is just a path
|
||||||
return util.URLJoin(appURL, strings.TrimSuffix(staticURLPrefix, "/"))
|
appHostURL := &url.URL{Scheme: appURL.Scheme, Host: appURL.Host}
|
||||||
|
return appHostURL.String() + "/" + strings.Trim(staticURLPrefix, "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.TrimSuffix(staticURLPrefix, "/")
|
return strings.TrimSuffix(staticURLPrefix, "/")
|
||||||
@@ -316,7 +316,7 @@ func loadServerFrom(rootCfg ConfigProvider) {
|
|||||||
Domain = urlHostname
|
Domain = urlHostname
|
||||||
}
|
}
|
||||||
|
|
||||||
AbsoluteAssetURL = MakeAbsoluteAssetURL(AppURL, StaticURLPrefix)
|
AbsoluteAssetURL = MakeAbsoluteAssetURL(appURL, StaticURLPrefix)
|
||||||
AssetVersion = strings.ReplaceAll(AppVer, "+", "~") // make sure the version string is clear (no real escaping is needed)
|
AssetVersion = strings.ReplaceAll(AppVer, "+", "~") // make sure the version string is clear (no real escaping is needed)
|
||||||
|
|
||||||
manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL)
|
manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package setting
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
@@ -12,18 +13,26 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestMakeAbsoluteAssetURL(t *testing.T) {
|
func TestMakeAbsoluteAssetURL(t *testing.T) {
|
||||||
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL("https://localhost:1234", "https://localhost:2345"))
|
appURL1, _ := url.Parse("https://localhost:1234")
|
||||||
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL("https://localhost:1234/", "https://localhost:2345"))
|
appURL2, _ := url.Parse("https://localhost:1234/")
|
||||||
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL("https://localhost:1234/", "https://localhost:2345/"))
|
appURLSub1, _ := url.Parse("https://localhost:1234/foo")
|
||||||
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL("https://localhost:1234", "/foo"))
|
appURLSub2, _ := url.Parse("https://localhost:1234/foo/")
|
||||||
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL("https://localhost:1234/", "/foo"))
|
|
||||||
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL("https://localhost:1234/", "/foo/"))
|
// static URL is an absolute URL, so should be used
|
||||||
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL("https://localhost:1234/foo", "/foo"))
|
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL(appURL1, "https://localhost:2345"))
|
||||||
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL("https://localhost:1234/foo/", "/foo"))
|
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL(appURL1, "https://localhost:2345/"))
|
||||||
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL("https://localhost:1234/foo/", "/foo/"))
|
|
||||||
assert.Equal(t, "https://localhost:1234/bar", MakeAbsoluteAssetURL("https://localhost:1234/foo", "/bar"))
|
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURL1, "/foo"))
|
||||||
assert.Equal(t, "https://localhost:1234/bar", MakeAbsoluteAssetURL("https://localhost:1234/foo/", "/bar"))
|
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURL2, "/foo"))
|
||||||
assert.Equal(t, "https://localhost:1234/bar", MakeAbsoluteAssetURL("https://localhost:1234/foo/", "/bar/"))
|
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURL1, "/foo/"))
|
||||||
|
|
||||||
|
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURLSub1, "/foo"))
|
||||||
|
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURLSub2, "/foo"))
|
||||||
|
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURLSub1, "/foo/"))
|
||||||
|
|
||||||
|
assert.Equal(t, "https://localhost:1234/bar", MakeAbsoluteAssetURL(appURLSub1, "/bar"))
|
||||||
|
assert.Equal(t, "https://localhost:1234/bar", MakeAbsoluteAssetURL(appURLSub2, "/bar"))
|
||||||
|
assert.Equal(t, "https://localhost:1234/bar", MakeAbsoluteAssetURL(appURLSub1, "/bar/"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMakeManifestData(t *testing.T) {
|
func TestMakeManifestData(t *testing.T) {
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ func NewFuncMap() template.FuncMap {
|
|||||||
"QueryEscape": queryEscape,
|
"QueryEscape": queryEscape,
|
||||||
"QueryBuild": QueryBuild,
|
"QueryBuild": QueryBuild,
|
||||||
"SanitizeHTML": SanitizeHTML,
|
"SanitizeHTML": SanitizeHTML,
|
||||||
"URLJoin": util.URLJoin,
|
|
||||||
"DotEscape": dotEscape,
|
"DotEscape": dotEscape,
|
||||||
|
|
||||||
"PathEscape": url.PathEscape,
|
"PathEscape": url.PathEscape,
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ func PathEscapeSegments(path string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// URLJoin joins url components, like path.Join, but preserving contents
|
// URLJoin joins url components, like path.Join, but preserving contents
|
||||||
|
// Deprecated: it has unclear behaviors, should not be used anymore. It is only used in some tests.
|
||||||
|
// Need to be removed in the future.
|
||||||
func URLJoin(base string, elems ...string) string {
|
func URLJoin(base string, elems ...string) string {
|
||||||
if !strings.HasSuffix(base, "/") {
|
if !strings.HasSuffix(base, "/") {
|
||||||
base += "/"
|
base += "/"
|
||||||
|
|||||||
@@ -45,6 +45,33 @@ const (
|
|||||||
TplActivatePrompt templates.TplName = "user/auth/activate_prompt" // for showing a message for user activation
|
TplActivatePrompt templates.TplName = "user/auth/activate_prompt" // for showing a message for user activation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CommonAuthOptions struct {
|
||||||
|
EnableCaptcha bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareCommonAuthPageData(ctx *context.Context, opt CommonAuthOptions) {
|
||||||
|
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
|
||||||
|
ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth
|
||||||
|
|
||||||
|
// for OpenID Connect
|
||||||
|
ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
|
||||||
|
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
|
||||||
|
|
||||||
|
if opt.EnableCaptcha {
|
||||||
|
ctx.Data["EnableCaptcha"] = true
|
||||||
|
ctx.Data["RecaptchaAPIScriptURL"] = strings.TrimSuffix(setting.Service.RecaptchaURL, "/") + "/api.js"
|
||||||
|
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
|
||||||
|
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
|
||||||
|
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
|
||||||
|
ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
|
||||||
|
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
|
||||||
|
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
|
||||||
|
if setting.Service.CaptchaType == setting.ImageCaptcha {
|
||||||
|
ctx.Data["Captcha"] = context.GetImageCaptcha()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// autoSignIn reads cookie and try to auto-login.
|
// autoSignIn reads cookie and try to auto-login.
|
||||||
func autoSignIn(ctx *context.Context) (bool, error) {
|
func autoSignIn(ctx *context.Context) (bool, error) {
|
||||||
isSucceed := false
|
isSucceed := false
|
||||||
@@ -199,12 +226,10 @@ func prepareSignInPageData(ctx *context.Context) {
|
|||||||
ctx.Data["PageIsSignIn"] = true
|
ctx.Data["PageIsSignIn"] = true
|
||||||
ctx.Data["PageIsLogin"] = true
|
ctx.Data["PageIsLogin"] = true
|
||||||
ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx)
|
ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx)
|
||||||
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
|
|
||||||
ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth
|
|
||||||
|
|
||||||
if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin {
|
prepareCommonAuthPageData(ctx, CommonAuthOptions{
|
||||||
context.SetCaptchaData(ctx)
|
EnableCaptcha: setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignIn render sign in page
|
// SignIn render sign in page
|
||||||
@@ -442,50 +467,51 @@ func buildSignOutRedirectURL(ctx *context.Context) string {
|
|||||||
return setting.AppSubURL + "/"
|
return setting.AppSubURL + "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignUp render the register page
|
func prepareSignUpPageData(ctx *context.Context) bool {
|
||||||
func SignUp(ctx *context.Context) {
|
|
||||||
ctx.Data["Title"] = ctx.Tr("sign_up")
|
ctx.Data["Title"] = ctx.Tr("sign_up")
|
||||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up"
|
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up"
|
||||||
|
ctx.Data["PageIsSignUp"] = true
|
||||||
|
|
||||||
hasUsers, _ := user_model.HasUsers(ctx)
|
hasUsers, err := user_model.HasUsers(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("HasUsers", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
ctx.Data["IsFirstTimeRegistration"] = !hasUsers.HasAnyUser
|
ctx.Data["IsFirstTimeRegistration"] = !hasUsers.HasAnyUser
|
||||||
|
|
||||||
oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true))
|
oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("UserSignUp", err)
|
ctx.ServerError("GetOAuth2Providers", err)
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["OAuth2Providers"] = oauth2Providers
|
ctx.Data["OAuth2Providers"] = oauth2Providers
|
||||||
context.SetCaptchaData(ctx)
|
|
||||||
|
|
||||||
ctx.Data["PageIsSignUp"] = true
|
prepareCommonAuthPageData(ctx, CommonAuthOptions{
|
||||||
|
EnableCaptcha: setting.Service.EnableCaptcha,
|
||||||
|
})
|
||||||
|
|
||||||
// Show Disabled Registration message if DisableRegistration or AllowOnlyExternalRegistration options are true
|
// Show Disabled Registration message if DisableRegistration or AllowOnlyExternalRegistration options are true
|
||||||
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration
|
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration
|
||||||
|
|
||||||
rememberAuthRedirectLink(ctx)
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignUp render the register page
|
||||||
|
func SignUp(ctx *context.Context) {
|
||||||
|
if !prepareSignUpPageData(ctx) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rememberAuthRedirectLink(ctx)
|
||||||
ctx.HTML(http.StatusOK, tplSignUp)
|
ctx.HTML(http.StatusOK, tplSignUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignUpPost response for sign up information submission
|
// SignUpPost response for sign up information submission
|
||||||
func SignUpPost(ctx *context.Context) {
|
func SignUpPost(ctx *context.Context) {
|
||||||
form := web.GetForm(ctx).(*forms.RegisterForm)
|
if !prepareSignUpPageData(ctx) {
|
||||||
ctx.Data["Title"] = ctx.Tr("sign_up")
|
|
||||||
|
|
||||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up"
|
|
||||||
|
|
||||||
oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true))
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("UserSignUp", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["OAuth2Providers"] = oauth2Providers
|
form := web.GetForm(ctx).(*forms.RegisterForm)
|
||||||
context.SetCaptchaData(ctx)
|
|
||||||
|
|
||||||
ctx.Data["PageIsSignUp"] = true
|
|
||||||
|
|
||||||
// Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
|
// Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
|
||||||
if setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration {
|
if setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration {
|
||||||
|
|||||||
@@ -24,30 +24,27 @@ import (
|
|||||||
|
|
||||||
var tplLinkAccount templates.TplName = "user/auth/link_account"
|
var tplLinkAccount templates.TplName = "user/auth/link_account"
|
||||||
|
|
||||||
// LinkAccount shows the page where the user can decide to login or create a new account
|
func prepareLinkAccountPageData(ctx *context.Context) {
|
||||||
func LinkAccount(ctx *context.Context) {
|
// TODO Make insecure passwords optional for local accounts also, once email-based Second-Factor Auth is available
|
||||||
// FIXME: these common template variables should be prepared in one common function, but not just copy-paste again and again.
|
|
||||||
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration
|
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration
|
||||||
|
|
||||||
ctx.Data["Title"] = ctx.Tr("link_account")
|
ctx.Data["Title"] = ctx.Tr("link_account")
|
||||||
ctx.Data["LinkAccountMode"] = true
|
ctx.Data["LinkAccountMode"] = true
|
||||||
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha
|
|
||||||
ctx.Data["Captcha"] = context.GetImageCaptcha()
|
|
||||||
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
|
|
||||||
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
|
|
||||||
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
|
|
||||||
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
|
|
||||||
ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
|
|
||||||
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
|
|
||||||
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
|
|
||||||
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
|
|
||||||
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
|
|
||||||
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
|
|
||||||
ctx.Data["ShowRegistrationButton"] = false
|
|
||||||
ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth
|
|
||||||
|
|
||||||
// use this to set the right link into the signIn and signUp templates in the link_account template
|
// use this to set the right link into the signIn and signUp templates in the link_account template
|
||||||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
|
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
|
||||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
|
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
|
||||||
|
ctx.Data["ShowRegistrationButton"] = false
|
||||||
|
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
|
||||||
|
|
||||||
|
prepareCommonAuthPageData(ctx, CommonAuthOptions{
|
||||||
|
EnableCaptcha: setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinkAccount shows the page where the user can decide to login or create a new account
|
||||||
|
func LinkAccount(ctx *context.Context) {
|
||||||
|
prepareLinkAccountPageData(ctx)
|
||||||
|
|
||||||
linkAccountData := oauth2GetLinkAccountData(ctx)
|
linkAccountData := oauth2GetLinkAccountData(ctx)
|
||||||
|
|
||||||
@@ -126,28 +123,10 @@ func handleSignInError(ctx *context.Context, userName string, ptrForm any, tmpl
|
|||||||
// LinkAccountPostSignIn handle the coupling of external account with another account using signIn
|
// LinkAccountPostSignIn handle the coupling of external account with another account using signIn
|
||||||
func LinkAccountPostSignIn(ctx *context.Context) {
|
func LinkAccountPostSignIn(ctx *context.Context) {
|
||||||
signInForm := web.GetForm(ctx).(*forms.SignInForm)
|
signInForm := web.GetForm(ctx).(*forms.SignInForm)
|
||||||
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration
|
|
||||||
ctx.Data["Title"] = ctx.Tr("link_account")
|
|
||||||
ctx.Data["LinkAccountMode"] = true
|
|
||||||
ctx.Data["LinkAccountModeSignIn"] = true
|
|
||||||
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha
|
|
||||||
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
|
|
||||||
ctx.Data["Captcha"] = context.GetImageCaptcha()
|
|
||||||
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
|
|
||||||
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
|
|
||||||
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
|
|
||||||
ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
|
|
||||||
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
|
|
||||||
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
|
|
||||||
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
|
|
||||||
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
|
|
||||||
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
|
|
||||||
ctx.Data["ShowRegistrationButton"] = false
|
|
||||||
ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth
|
|
||||||
|
|
||||||
// use this to set the right link into the signIn and signUp templates in the link_account template
|
ctx.Data["LinkAccountModeSignIn"] = true
|
||||||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
|
|
||||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
|
prepareLinkAccountPageData(ctx)
|
||||||
|
|
||||||
linkAccountData := oauth2GetLinkAccountData(ctx)
|
linkAccountData := oauth2GetLinkAccountData(ctx)
|
||||||
if linkAccountData == nil {
|
if linkAccountData == nil {
|
||||||
@@ -218,30 +197,10 @@ func oauth2LinkAccount(ctx *context.Context, u *user_model.User, linkAccountData
|
|||||||
// LinkAccountPostRegister handle the creation of a new account for an external account using signUp
|
// LinkAccountPostRegister handle the creation of a new account for an external account using signUp
|
||||||
func LinkAccountPostRegister(ctx *context.Context) {
|
func LinkAccountPostRegister(ctx *context.Context) {
|
||||||
form := web.GetForm(ctx).(*forms.RegisterForm)
|
form := web.GetForm(ctx).(*forms.RegisterForm)
|
||||||
// TODO Make insecure passwords optional for local accounts also,
|
|
||||||
// once email-based Second-Factor Auth is available
|
|
||||||
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration
|
|
||||||
ctx.Data["Title"] = ctx.Tr("link_account")
|
|
||||||
ctx.Data["LinkAccountMode"] = true
|
|
||||||
ctx.Data["LinkAccountModeRegister"] = true
|
|
||||||
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha
|
|
||||||
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
|
|
||||||
ctx.Data["Captcha"] = context.GetImageCaptcha()
|
|
||||||
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
|
|
||||||
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
|
|
||||||
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
|
|
||||||
ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
|
|
||||||
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
|
|
||||||
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
|
|
||||||
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
|
|
||||||
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
|
|
||||||
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
|
|
||||||
ctx.Data["ShowRegistrationButton"] = false
|
|
||||||
ctx.Data["EnablePasskeyAuth"] = setting.Service.EnablePasskeyAuth
|
|
||||||
|
|
||||||
// use this to set the right link into the signIn and signUp templates in the link_account template
|
ctx.Data["LinkAccountModeRegister"] = true
|
||||||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
|
|
||||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
|
prepareLinkAccountPageData(ctx)
|
||||||
|
|
||||||
linkAccountData := oauth2GetLinkAccountData(ctx)
|
linkAccountData := oauth2GetLinkAccountData(ctx)
|
||||||
if linkAccountData == nil {
|
if linkAccountData == nil {
|
||||||
|
|||||||
@@ -229,19 +229,26 @@ func signInOpenIDVerify(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectOpenID shows a form to connect an OpenID URI to an existing account
|
func prepareConnectOpenIDPageData(ctx *context.Context) (oid string) {
|
||||||
func ConnectOpenID(ctx *context.Context) {
|
oid, _ = ctx.Session.Get("openid_verified_uri").(string)
|
||||||
oid, _ := ctx.Session.Get("openid_verified_uri").(string)
|
|
||||||
if oid == "" {
|
if oid == "" {
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/login/openid")
|
ctx.Redirect(setting.AppSubURL + "/user/login/openid")
|
||||||
return
|
return ""
|
||||||
}
|
}
|
||||||
ctx.Data["Title"] = "OpenID connect"
|
ctx.Data["Title"] = "OpenID connect"
|
||||||
ctx.Data["PageIsSignIn"] = true
|
ctx.Data["PageIsSignIn"] = true
|
||||||
ctx.Data["PageIsOpenIDConnect"] = true
|
ctx.Data["PageIsOpenIDConnect"] = true
|
||||||
ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
|
|
||||||
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
|
|
||||||
ctx.Data["OpenID"] = oid
|
ctx.Data["OpenID"] = oid
|
||||||
|
prepareCommonAuthPageData(ctx, CommonAuthOptions{EnableCaptcha: false})
|
||||||
|
return oid
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectOpenID shows a form to connect an OpenID URI to an existing account
|
||||||
|
func ConnectOpenID(ctx *context.Context) {
|
||||||
|
oid := prepareConnectOpenIDPageData(ctx)
|
||||||
|
if oid == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
userName, _ := ctx.Session.Get("openid_determined_username").(string)
|
userName, _ := ctx.Session.Get("openid_determined_username").(string)
|
||||||
if userName != "" {
|
if userName != "" {
|
||||||
ctx.Data["user_name"] = userName
|
ctx.Data["user_name"] = userName
|
||||||
@@ -252,16 +259,10 @@ func ConnectOpenID(ctx *context.Context) {
|
|||||||
// ConnectOpenIDPost handles submission of a form to connect an OpenID URI to an existing account
|
// ConnectOpenIDPost handles submission of a form to connect an OpenID URI to an existing account
|
||||||
func ConnectOpenIDPost(ctx *context.Context) {
|
func ConnectOpenIDPost(ctx *context.Context) {
|
||||||
form := web.GetForm(ctx).(*forms.ConnectOpenIDForm)
|
form := web.GetForm(ctx).(*forms.ConnectOpenIDForm)
|
||||||
oid, _ := ctx.Session.Get("openid_verified_uri").(string)
|
oid := prepareConnectOpenIDPageData(ctx)
|
||||||
if oid == "" {
|
if oid == "" {
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/login/openid")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Data["Title"] = "OpenID connect"
|
|
||||||
ctx.Data["PageIsSignIn"] = true
|
|
||||||
ctx.Data["PageIsOpenIDConnect"] = true
|
|
||||||
ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
|
|
||||||
ctx.Data["OpenID"] = oid
|
|
||||||
|
|
||||||
u, _, err := auth.UserSignIn(ctx, form.UserName, form.Password)
|
u, _, err := auth.UserSignIn(ctx, form.UserName, form.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -287,28 +288,29 @@ func ConnectOpenIDPost(ctx *context.Context) {
|
|||||||
handleSignIn(ctx, u, remember)
|
handleSignIn(ctx, u, remember)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterOpenID shows a form to create a new user authenticated via an OpenID URI
|
func prepareRegisterOpenIDPageData(ctx *context.Context) (oid string) {
|
||||||
func RegisterOpenID(ctx *context.Context) {
|
oid, _ = ctx.Session.Get("openid_verified_uri").(string)
|
||||||
oid, _ := ctx.Session.Get("openid_verified_uri").(string)
|
|
||||||
if oid == "" {
|
if oid == "" {
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/login/openid")
|
ctx.Redirect(setting.AppSubURL + "/user/login/openid")
|
||||||
return
|
return ""
|
||||||
}
|
}
|
||||||
ctx.Data["Title"] = "OpenID signup"
|
ctx.Data["Title"] = "OpenID signup"
|
||||||
ctx.Data["PageIsSignIn"] = true
|
ctx.Data["PageIsSignIn"] = true
|
||||||
ctx.Data["PageIsOpenIDRegister"] = true
|
ctx.Data["PageIsOpenIDRegister"] = true
|
||||||
ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
|
|
||||||
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
|
|
||||||
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
|
|
||||||
ctx.Data["Captcha"] = context.GetImageCaptcha()
|
|
||||||
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
|
|
||||||
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
|
|
||||||
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
|
|
||||||
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
|
|
||||||
ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
|
|
||||||
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
|
|
||||||
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
|
|
||||||
ctx.Data["OpenID"] = oid
|
ctx.Data["OpenID"] = oid
|
||||||
|
prepareCommonAuthPageData(ctx, CommonAuthOptions{
|
||||||
|
EnableCaptcha: setting.Service.EnableCaptcha,
|
||||||
|
})
|
||||||
|
return oid
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterOpenID shows a form to create a new user authenticated via an OpenID URI
|
||||||
|
func RegisterOpenID(ctx *context.Context) {
|
||||||
|
oid := prepareRegisterOpenIDPageData(ctx)
|
||||||
|
if oid == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
userName, _ := ctx.Session.Get("openid_determined_username").(string)
|
userName, _ := ctx.Session.Get("openid_determined_username").(string)
|
||||||
if userName != "" {
|
if userName != "" {
|
||||||
ctx.Data["user_name"] = userName
|
ctx.Data["user_name"] = userName
|
||||||
@@ -322,19 +324,12 @@ func RegisterOpenID(ctx *context.Context) {
|
|||||||
|
|
||||||
// RegisterOpenIDPost handles submission of a form to create a new user authenticated via an OpenID URI
|
// RegisterOpenIDPost handles submission of a form to create a new user authenticated via an OpenID URI
|
||||||
func RegisterOpenIDPost(ctx *context.Context) {
|
func RegisterOpenIDPost(ctx *context.Context) {
|
||||||
form := web.GetForm(ctx).(*forms.SignUpOpenIDForm)
|
oid := prepareRegisterOpenIDPageData(ctx)
|
||||||
oid, _ := ctx.Session.Get("openid_verified_uri").(string)
|
|
||||||
if oid == "" {
|
if oid == "" {
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/login/openid")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["Title"] = "OpenID signup"
|
form := web.GetForm(ctx).(*forms.SignUpOpenIDForm)
|
||||||
ctx.Data["PageIsSignIn"] = true
|
|
||||||
ctx.Data["PageIsOpenIDRegister"] = true
|
|
||||||
ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
|
|
||||||
context.SetCaptchaData(ctx)
|
|
||||||
ctx.Data["OpenID"] = oid
|
|
||||||
|
|
||||||
if setting.Service.AllowOnlyInternalRegistration {
|
if setting.Service.AllowOnlyInternalRegistration {
|
||||||
ctx.HTTPError(http.StatusForbidden)
|
ctx.HTTPError(http.StatusForbidden)
|
||||||
|
|||||||
@@ -218,7 +218,8 @@ func redirectForCommitChoice[T any](ctx *context.Context, parsed *preparedEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// redirect to the newly updated file
|
// redirect to the newly updated file
|
||||||
redirectTo := util.URLJoin(ctx.Repo.RepoLink, "src/branch", util.PathEscapeSegments(parsed.NewBranchName), util.PathEscapeSegments(treePath))
|
redirectTo := ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(parsed.NewBranchName) + "/" + util.PathEscapeSegments(treePath)
|
||||||
|
redirectTo = strings.TrimSuffix(redirectTo, "/")
|
||||||
ctx.JSONRedirect(redirectTo)
|
ctx.JSONRedirect(redirectTo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
|||||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
|
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
|
||||||
}
|
}
|
||||||
if isRaw {
|
if isRaw {
|
||||||
ctx.Redirect(util.URLJoin(ctx.Repo.RepoLink, "wiki/raw", string(pageName)))
|
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/raw/" + string(pageName))
|
||||||
}
|
}
|
||||||
if entry == nil || ctx.Written() {
|
if entry == nil || ctx.Written() {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|||||||
@@ -45,22 +45,6 @@ func GetImageCaptcha() *captcha.Captcha {
|
|||||||
return cpt
|
return cpt
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCaptchaData sets common captcha data
|
|
||||||
func SetCaptchaData(ctx *Context) {
|
|
||||||
if !setting.Service.EnableCaptcha {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
|
|
||||||
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
|
|
||||||
ctx.Data["Captcha"] = GetImageCaptcha()
|
|
||||||
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
|
|
||||||
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
|
|
||||||
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
|
|
||||||
ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
|
|
||||||
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
|
|
||||||
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
gRecaptchaResponseField = "g-recaptcha-response"
|
gRecaptchaResponseField = "g-recaptcha-response"
|
||||||
hCaptchaResponseField = "h-captcha-response"
|
hCaptchaResponseField = "h-captcha-response"
|
||||||
|
|||||||
@@ -961,7 +961,8 @@ func RepoRefByType(detectRefType git.RefType) func(*Context) {
|
|||||||
}
|
}
|
||||||
// If short commit ID add canonical link header
|
// If short commit ID add canonical link header
|
||||||
if len(refShortName) < ctx.Repo.GetObjectFormat().FullLength() {
|
if len(refShortName) < ctx.Repo.GetObjectFormat().FullLength() {
|
||||||
canonicalURL := util.URLJoin(httplib.GuessCurrentAppURL(ctx), strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refShortName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))
|
// FIXME: the dirty hack of "strings.Replace" should be fixed
|
||||||
|
canonicalURL := strings.TrimSuffix(httplib.GuessCurrentAppURL(ctx), "/") + strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refShortName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1)
|
||||||
ctx.RespHeader().Set("Link", fmt.Sprintf(`<%s>; rel="canonical"`, canonicalURL))
|
ctx.RespHeader().Set("Link", fmt.Sprintf(`<%s>; rel="canonical"`, canonicalURL))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -203,8 +203,8 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch, repo
|
|||||||
|
|
||||||
// ToTag convert a git.Tag to an api.Tag
|
// ToTag convert a git.Tag to an api.Tag
|
||||||
func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag {
|
func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag {
|
||||||
tarballURL := util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz")
|
tarballURL := repo.HTMLURL() + "/archive/" + url.PathEscape(t.Name+".tar.gz")
|
||||||
zipballURL := util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip")
|
zipballURL := repo.HTMLURL() + "/archive/" + url.PathEscape(t.Name+".zip")
|
||||||
|
|
||||||
// Archive URLs are "" if the download feature is disabled
|
// Archive URLs are "" if the download feature is disabled
|
||||||
if setting.Repository.DisableDownloadSourceArchives {
|
if setting.Repository.DisableDownloadSourceArchives {
|
||||||
@@ -713,7 +713,7 @@ func ToAnnotatedTag(ctx context.Context, repo *repo_model.Repository, t *git.Tag
|
|||||||
SHA: t.ID.String(),
|
SHA: t.ID.String(),
|
||||||
Object: ToAnnotatedTagObject(repo, c),
|
Object: ToAnnotatedTagObject(repo, c),
|
||||||
Message: t.Message,
|
Message: t.Message,
|
||||||
URL: util.URLJoin(repo.APIURL(), "git/tags", t.ID.String()),
|
URL: repo.APIURL() + "/git/tags/" + t.ID.String(),
|
||||||
Tagger: ToCommitUser(t.Tagger),
|
Tagger: ToCommitUser(t.Tagger),
|
||||||
Verification: ToVerification(ctx, c),
|
Verification: ToVerification(ctx, c),
|
||||||
}
|
}
|
||||||
@@ -724,7 +724,7 @@ func ToAnnotatedTagObject(repo *repo_model.Repository, commit *git.Commit) *api.
|
|||||||
return &api.AnnotatedTagObject{
|
return &api.AnnotatedTagObject{
|
||||||
SHA: commit.ID.String(),
|
SHA: commit.ID.String(),
|
||||||
Type: string(git.ObjectCommit),
|
Type: string(git.ObjectCommit),
|
||||||
URL: util.URLJoin(repo.APIURL(), "git/commits", commit.ID.String()),
|
URL: repo.APIURL() + "/git/commits/" + commit.ID.String(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/gitrepo"
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
ctx "code.gitea.io/gitea/services/context"
|
ctx "code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/services/gitdiff"
|
"code.gitea.io/gitea/services/gitdiff"
|
||||||
)
|
)
|
||||||
@@ -34,7 +33,7 @@ func ToCommitUser(sig *git.Signature) *api.CommitUser {
|
|||||||
func ToCommitMeta(repo *repo_model.Repository, tag *git.Tag) *api.CommitMeta {
|
func ToCommitMeta(repo *repo_model.Repository, tag *git.Tag) *api.CommitMeta {
|
||||||
return &api.CommitMeta{
|
return &api.CommitMeta{
|
||||||
SHA: tag.Object.String(),
|
SHA: tag.Object.String(),
|
||||||
URL: util.URLJoin(repo.APIURL(), "git/commits", tag.ID.String()),
|
URL: repo.APIURL() + "/git/commits/" + tag.ID.String(),
|
||||||
Created: tag.Tagger.When,
|
Created: tag.Tagger.When,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +57,7 @@ func ToPayloadCommit(ctx context.Context, repo *repo_model.Repository, c *git.Co
|
|||||||
return &api.PayloadCommit{
|
return &api.PayloadCommit{
|
||||||
ID: c.ID.String(),
|
ID: c.ID.String(),
|
||||||
Message: c.Message(),
|
Message: c.Message(),
|
||||||
URL: util.URLJoin(repo.HTMLURL(), "commit", c.ID.String()),
|
URL: repo.HTMLURL() + "/commit/" + c.ID.String(),
|
||||||
Author: &api.PayloadUser{
|
Author: &api.PayloadUser{
|
||||||
Name: c.Author.Name,
|
Name: c.Author.Name,
|
||||||
Email: c.Author.Email,
|
Email: c.Author.Email,
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ func ToWikiPageMetaData(wikiName WebPath, lastCommit *git.Commit, repo *repo_mod
|
|||||||
_, title := WebPathToUserTitle(wikiName)
|
_, title := WebPathToUserTitle(wikiName)
|
||||||
return &api.WikiPageMetaData{
|
return &api.WikiPageMetaData{
|
||||||
Title: title,
|
Title: title,
|
||||||
HTMLURL: util.URLJoin(repo.HTMLURL(), "wiki", subURL),
|
HTMLURL: repo.HTMLURL() + "/wiki/" + subURL,
|
||||||
SubURL: subURL,
|
SubURL: subURL,
|
||||||
LastCommit: convert.ToWikiCommit(lastCommit),
|
LastCommit: convert.ToWikiCommit(lastCommit),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
{{svg "octicon-package" 48}}
|
{{svg "octicon-package" 48}}
|
||||||
<h2>{{ctx.Locale.Tr "packages.empty"}}</h2>
|
<h2>{{ctx.Locale.Tr "packages.empty"}}</h2>
|
||||||
{{if and .Repository .CanWritePackages}}
|
{{if and .Repository .CanWritePackages}}
|
||||||
{{$packagesUrl := URLJoin .Owner.HomeLink "-" "packages"}}
|
{{$packagesUrl := print .Owner.HomeLink "/-/packages"}}
|
||||||
<p>{{ctx.Locale.Tr "packages.empty.repo" $packagesUrl}}</p>
|
<p>{{ctx.Locale.Tr "packages.empty.repo" $packagesUrl}}</p>
|
||||||
{{end}}
|
{{end}}
|
||||||
<p>{{ctx.Locale.Tr "packages.empty.documentation" "https://docs.gitea.com/usage/packages/overview/"}}</p>
|
<p>{{ctx.Locale.Tr "packages.empty.documentation" "https://docs.gitea.com/usage/packages/overview/"}}</p>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{{if or .UsesIgnoreRevs .FaultyIgnoreRevsFile}}
|
{{if or .UsesIgnoreRevs .FaultyIgnoreRevsFile}}
|
||||||
{{$revsFileLink := URLJoin .RepoLink "src" .RefTypeNameSubURL "/.git-blame-ignore-revs"}}
|
{{$revsFileLink := print .RepoLink "/src/" .RefTypeNameSubURL "/.git-blame-ignore-revs"}}
|
||||||
{{if .UsesIgnoreRevs}}
|
{{if .UsesIgnoreRevs}}
|
||||||
<div class="ui info message">
|
<div class="ui info message">
|
||||||
<p>{{ctx.Locale.Tr "repo.blame.ignore_revs" $revsFileLink "?bypass-blame-ignore=true"}}</p>
|
<p>{{ctx.Locale.Tr "repo.blame.ignore_revs" $revsFileLink "?bypass-blame-ignore=true"}}</p>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<div class="inline field tw-text-center required">
|
<div class="inline field tw-text-center required">
|
||||||
<div id="captcha" data-captcha-type="g-recaptcha" class="g-recaptcha-style" data-sitekey="{{.RecaptchaSitekey}}"></div>
|
<div id="captcha" data-captcha-type="g-recaptcha" class="g-recaptcha-style" data-sitekey="{{.RecaptchaSitekey}}"></div>
|
||||||
</div>
|
</div>
|
||||||
<script defer src='{{URLJoin .RecaptchaURL "api.js"}}'></script>
|
<script defer src='{{.RecaptchaAPIScriptURL}}'></script>
|
||||||
{{else if eq .CaptchaType "hcaptcha"}}
|
{{else if eq .CaptchaType "hcaptcha"}}
|
||||||
<div class="inline field tw-text-center required">
|
<div class="inline field tw-text-center required">
|
||||||
<div id="captcha" data-captcha-type="h-captcha" class="h-captcha-style" data-sitekey="{{.HcaptchaSitekey}}"></div>
|
<div id="captcha" data-captcha-type="h-captcha" class="h-captcha-style" data-sitekey="{{.HcaptchaSitekey}}"></div>
|
||||||
|
|||||||
Reference in New Issue
Block a user