mirror of
https://github.com/go-gitea/gitea.git
synced 2026-03-17 05:29:19 +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:
parent
0724344a8a
commit
6f8ab6aaaf
@ -11,7 +11,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"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:
|
||||
@ -27,7 +26,7 @@ func CamoEncode(link string) string {
|
||||
macSum := b64encode(mac.Sum(nil))
|
||||
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 {
|
||||
|
||||
@ -4,13 +4,13 @@
|
||||
package markup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
"golang.org/x/net/html/atom"
|
||||
@ -219,7 +219,7 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
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"))
|
||||
start = 0
|
||||
node = node.NextSibling.NextSibling
|
||||
@ -236,7 +236,7 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
package markup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -162,7 +163,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||
issueOwner := util.Iif(ref.Owner == "", ctx.RenderOptions.Metas["user"], ref.Owner)
|
||||
issueRepo := util.Iif(ref.Owner == "", ctx.RenderOptions.Metas["repo"], ref.Name)
|
||||
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
|
||||
// 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
|
||||
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 {
|
||||
link = strings.ReplaceAll(link, " ", "+")
|
||||
} else {
|
||||
// the hacky wiki name encoding: space to "-"
|
||||
link = strings.ReplaceAll(link, " ", "-") // FIXME: it should support dashes in the link, eg: "the-dash-support.-"
|
||||
}
|
||||
if !strings.Contains(link, "/") {
|
||||
link = url.PathEscape(link) // FIXME: it doesn't seem right and it might cause double-escaping
|
||||
}
|
||||
link = url.PathEscape(link)
|
||||
}
|
||||
if image {
|
||||
title := props["title"]
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
package markup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
@ -26,14 +27,11 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
|
||||
loc.End += start
|
||||
mention := node.Data[loc.Start:loc.End]
|
||||
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, "/") {
|
||||
mentionOrgAndTeam := strings.Split(mention, "/")
|
||||
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*/))
|
||||
node = node.NextSibling.NextSibling
|
||||
start = 0
|
||||
|
||||
@ -389,7 +389,7 @@ func TestRender_ShortLinks(t *testing.T) {
|
||||
imgurl := util.URLJoin(tree, "Link.jpg")
|
||||
otherImgurl := util.URLJoin(tree, "Link+Other.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")
|
||||
unrenderableFileURL := util.URLJoin(tree, "file.zip")
|
||||
favicon := "http://google.com/favicon.ico"
|
||||
@ -466,6 +466,8 @@ func TestRender_ShortLinks(t *testing.T) {
|
||||
"[[Name|Link #.jpg|alt=\"AltName\"|title='Title']]",
|
||||
`<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(
|
||||
"[[some/path/Link #.jpg]]",
|
||||
`<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/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@ -23,7 +22,6 @@ const (
|
||||
AppURL = "http://localhost:3000/"
|
||||
testRepoOwnerName = "user13"
|
||||
testRepoName = "repo11"
|
||||
FullURL = AppURL + testRepoOwnerName + "/" + testRepoName + "/"
|
||||
)
|
||||
|
||||
// these values should match the const above
|
||||
@ -47,8 +45,9 @@ func TestRender_StandardLinks(t *testing.T) {
|
||||
func TestRender_Images(t *testing.T) {
|
||||
setting.AppURL = AppURL
|
||||
|
||||
const baseLink = "http://localhost:3000/user13/repo11"
|
||||
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.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"
|
||||
title := "Train"
|
||||
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
|
||||
|
||||
render(
|
||||
@ -88,6 +87,7 @@ func TestRender_Images(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTotal_RenderString(t *testing.T) {
|
||||
const FullURL = AppURL + testRepoOwnerName + "/" + testRepoName + "/"
|
||||
setting.AppURL = AppURL
|
||||
defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableAdditionalAttributes, true)()
|
||||
|
||||
|
||||
@ -5,28 +5,47 @@ package markup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"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) {
|
||||
if IsFullURLString(link) {
|
||||
return link
|
||||
linkURL, err := url.Parse(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, 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
|
||||
} 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 {
|
||||
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/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))
|
||||
|
||||
// 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
|
||||
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))
|
||||
|
||||
@ -13,7 +13,6 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// Response is the structure of JSON returned from API
|
||||
@ -24,17 +23,16 @@ type Response struct {
|
||||
ErrorCodes []ErrorCode `json:"error-codes"`
|
||||
}
|
||||
|
||||
const apiURL = "api/siteverify"
|
||||
|
||||
// Verify calls Google Recaptcha API to verify token
|
||||
func Verify(ctx context.Context, response string) (bool, error) {
|
||||
post := url.Values{
|
||||
"secret": {setting.Service.RecaptchaSecret},
|
||||
"response": {response},
|
||||
}
|
||||
|
||||
reqURL := strings.TrimSuffix(setting.Service.RecaptchaURL, "/") + "/api/siteverify"
|
||||
// Basically a copy of http.PostForm, but with a context
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
|
||||
util.URLJoin(setting.Service.RecaptchaURL, apiURL), strings.NewReader(post.Encode()))
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, reqURL, strings.NewReader(post.Encode()))
|
||||
if err != nil {
|
||||
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/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// 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
|
||||
func MakeAbsoluteAssetURL(appURL, staticURLPrefix string) string {
|
||||
func MakeAbsoluteAssetURL(appURL *url.URL, staticURLPrefix string) string {
|
||||
parsedPrefix, err := url.Parse(strings.TrimSuffix(staticURLPrefix, "/"))
|
||||
if err != nil {
|
||||
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 staticURLPrefix == "" {
|
||||
return strings.TrimSuffix(appURL, "/")
|
||||
return strings.TrimSuffix(appURL.String(), "/")
|
||||
}
|
||||
|
||||
// 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, "/")
|
||||
@ -316,7 +316,7 @@ func loadServerFrom(rootCfg ConfigProvider) {
|
||||
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)
|
||||
|
||||
manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL)
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
@ -12,18 +13,26 @@ import (
|
||||
)
|
||||
|
||||
func TestMakeAbsoluteAssetURL(t *testing.T) {
|
||||
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL("https://localhost:1234", "https://localhost:2345"))
|
||||
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL("https://localhost:1234/", "https://localhost:2345"))
|
||||
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL("https://localhost:1234/", "https://localhost:2345/"))
|
||||
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL("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/"))
|
||||
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL("https://localhost:1234/foo", "/foo"))
|
||||
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL("https://localhost:1234/foo/", "/foo"))
|
||||
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/bar", MakeAbsoluteAssetURL("https://localhost:1234/foo/", "/bar"))
|
||||
assert.Equal(t, "https://localhost:1234/bar", MakeAbsoluteAssetURL("https://localhost:1234/foo/", "/bar/"))
|
||||
appURL1, _ := url.Parse("https://localhost:1234")
|
||||
appURL2, _ := url.Parse("https://localhost:1234/")
|
||||
appURLSub1, _ := url.Parse("https://localhost:1234/foo")
|
||||
appURLSub2, _ := url.Parse("https://localhost:1234/foo/")
|
||||
|
||||
// static URL is an absolute URL, so should be used
|
||||
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL(appURL1, "https://localhost:2345"))
|
||||
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL(appURL1, "https://localhost:2345/"))
|
||||
|
||||
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURL1, "/foo"))
|
||||
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL(appURL2, "/foo"))
|
||||
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) {
|
||||
|
||||
@ -37,7 +37,6 @@ func NewFuncMap() template.FuncMap {
|
||||
"QueryEscape": queryEscape,
|
||||
"QueryBuild": QueryBuild,
|
||||
"SanitizeHTML": SanitizeHTML,
|
||||
"URLJoin": util.URLJoin,
|
||||
"DotEscape": dotEscape,
|
||||
|
||||
"PathEscape": url.PathEscape,
|
||||
|
||||
@ -20,6 +20,8 @@ func PathEscapeSegments(path string) string {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if !strings.HasSuffix(base, "/") {
|
||||
base += "/"
|
||||
|
||||
@ -45,6 +45,33 @@ const (
|
||||
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.
|
||||
func autoSignIn(ctx *context.Context) (bool, error) {
|
||||
isSucceed := false
|
||||
@ -199,12 +226,10 @@ func prepareSignInPageData(ctx *context.Context) {
|
||||
ctx.Data["PageIsSignIn"] = true
|
||||
ctx.Data["PageIsLogin"] = true
|
||||
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 {
|
||||
context.SetCaptchaData(ctx)
|
||||
}
|
||||
prepareCommonAuthPageData(ctx, CommonAuthOptions{
|
||||
EnableCaptcha: setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin,
|
||||
})
|
||||
}
|
||||
|
||||
// SignIn render sign in page
|
||||
@ -442,50 +467,51 @@ func buildSignOutRedirectURL(ctx *context.Context) string {
|
||||
return setting.AppSubURL + "/"
|
||||
}
|
||||
|
||||
// SignUp render the register page
|
||||
func SignUp(ctx *context.Context) {
|
||||
func prepareSignUpPageData(ctx *context.Context) bool {
|
||||
ctx.Data["Title"] = ctx.Tr("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
|
||||
|
||||
oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true))
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignUp", err)
|
||||
return
|
||||
ctx.ServerError("GetOAuth2Providers", err)
|
||||
return false
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
}
|
||||
|
||||
// SignUpPost response for sign up information submission
|
||||
func SignUpPost(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.RegisterForm)
|
||||
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)
|
||||
if !prepareSignUpPageData(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["OAuth2Providers"] = oauth2Providers
|
||||
context.SetCaptchaData(ctx)
|
||||
|
||||
ctx.Data["PageIsSignUp"] = true
|
||||
form := web.GetForm(ctx).(*forms.RegisterForm)
|
||||
|
||||
// Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
|
||||
if setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration {
|
||||
|
||||
@ -24,30 +24,27 @@ import (
|
||||
|
||||
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 LinkAccount(ctx *context.Context) {
|
||||
// FIXME: these common template variables should be prepared in one common function, but not just copy-paste again and again.
|
||||
func prepareLinkAccountPageData(ctx *context.Context) {
|
||||
// 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["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
|
||||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
|
||||
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)
|
||||
|
||||
@ -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
|
||||
func LinkAccountPostSignIn(ctx *context.Context) {
|
||||
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["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
|
||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
|
||||
ctx.Data["LinkAccountModeSignIn"] = true
|
||||
|
||||
prepareLinkAccountPageData(ctx)
|
||||
|
||||
linkAccountData := oauth2GetLinkAccountData(ctx)
|
||||
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
|
||||
func LinkAccountPostRegister(ctx *context.Context) {
|
||||
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["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
|
||||
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
|
||||
ctx.Data["LinkAccountModeRegister"] = true
|
||||
|
||||
prepareLinkAccountPageData(ctx)
|
||||
|
||||
linkAccountData := oauth2GetLinkAccountData(ctx)
|
||||
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 ConnectOpenID(ctx *context.Context) {
|
||||
oid, _ := ctx.Session.Get("openid_verified_uri").(string)
|
||||
func prepareConnectOpenIDPageData(ctx *context.Context) (oid string) {
|
||||
oid, _ = ctx.Session.Get("openid_verified_uri").(string)
|
||||
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["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
|
||||
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)
|
||||
if 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
|
||||
func ConnectOpenIDPost(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.ConnectOpenIDForm)
|
||||
oid, _ := ctx.Session.Get("openid_verified_uri").(string)
|
||||
oid := prepareConnectOpenIDPageData(ctx)
|
||||
if oid == "" {
|
||||
ctx.Redirect(setting.AppSubURL + "/user/login/openid")
|
||||
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)
|
||||
if err != nil {
|
||||
@ -287,28 +288,29 @@ func ConnectOpenIDPost(ctx *context.Context) {
|
||||
handleSignIn(ctx, u, remember)
|
||||
}
|
||||
|
||||
// RegisterOpenID shows a form to create a new user authenticated via an OpenID URI
|
||||
func RegisterOpenID(ctx *context.Context) {
|
||||
oid, _ := ctx.Session.Get("openid_verified_uri").(string)
|
||||
func prepareRegisterOpenIDPageData(ctx *context.Context) (oid string) {
|
||||
oid, _ = ctx.Session.Get("openid_verified_uri").(string)
|
||||
if oid == "" {
|
||||
ctx.Redirect(setting.AppSubURL + "/user/login/openid")
|
||||
return
|
||||
return ""
|
||||
}
|
||||
ctx.Data["Title"] = "OpenID signup"
|
||||
ctx.Data["PageIsSignIn"] = 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
|
||||
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)
|
||||
if 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
|
||||
func RegisterOpenIDPost(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.SignUpOpenIDForm)
|
||||
oid, _ := ctx.Session.Get("openid_verified_uri").(string)
|
||||
oid := prepareRegisterOpenIDPageData(ctx)
|
||||
if oid == "" {
|
||||
ctx.Redirect(setting.AppSubURL + "/user/login/openid")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Title"] = "OpenID signup"
|
||||
ctx.Data["PageIsSignIn"] = true
|
||||
ctx.Data["PageIsOpenIDRegister"] = true
|
||||
ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
|
||||
context.SetCaptchaData(ctx)
|
||||
ctx.Data["OpenID"] = oid
|
||||
form := web.GetForm(ctx).(*forms.SignUpOpenIDForm)
|
||||
|
||||
if setting.Service.AllowOnlyInternalRegistration {
|
||||
ctx.HTTPError(http.StatusForbidden)
|
||||
|
||||
@ -218,7 +218,8 @@ func redirectForCommitChoice[T any](ctx *context.Context, parsed *preparedEditor
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
||||
@ -238,7 +238,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
|
||||
}
|
||||
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() {
|
||||
return nil, nil
|
||||
|
||||
@ -45,22 +45,6 @@ func GetImageCaptcha() *captcha.Captcha {
|
||||
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 (
|
||||
gRecaptchaResponseField = "g-recaptcha-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 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))
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -203,8 +203,8 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch, repo
|
||||
|
||||
// ToTag convert a git.Tag to an api.Tag
|
||||
func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag {
|
||||
tarballURL := util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz")
|
||||
zipballURL := util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip")
|
||||
tarballURL := repo.HTMLURL() + "/archive/" + url.PathEscape(t.Name+".tar.gz")
|
||||
zipballURL := repo.HTMLURL() + "/archive/" + url.PathEscape(t.Name+".zip")
|
||||
|
||||
// Archive URLs are "" if the download feature is disabled
|
||||
if setting.Repository.DisableDownloadSourceArchives {
|
||||
@ -713,7 +713,7 @@ func ToAnnotatedTag(ctx context.Context, repo *repo_model.Repository, t *git.Tag
|
||||
SHA: t.ID.String(),
|
||||
Object: ToAnnotatedTagObject(repo, c),
|
||||
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),
|
||||
Verification: ToVerification(ctx, c),
|
||||
}
|
||||
@ -724,7 +724,7 @@ func ToAnnotatedTagObject(repo *repo_model.Repository, commit *git.Commit) *api.
|
||||
return &api.AnnotatedTagObject{
|
||||
SHA: commit.ID.String(),
|
||||
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/log"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
ctx "code.gitea.io/gitea/services/context"
|
||||
"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 {
|
||||
return &api.CommitMeta{
|
||||
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,
|
||||
}
|
||||
}
|
||||
@ -58,7 +57,7 @@ func ToPayloadCommit(ctx context.Context, repo *repo_model.Repository, c *git.Co
|
||||
return &api.PayloadCommit{
|
||||
ID: c.ID.String(),
|
||||
Message: c.Message(),
|
||||
URL: util.URLJoin(repo.HTMLURL(), "commit", c.ID.String()),
|
||||
URL: repo.HTMLURL() + "/commit/" + c.ID.String(),
|
||||
Author: &api.PayloadUser{
|
||||
Name: c.Author.Name,
|
||||
Email: c.Author.Email,
|
||||
|
||||
@ -165,7 +165,7 @@ func ToWikiPageMetaData(wikiName WebPath, lastCommit *git.Commit, repo *repo_mod
|
||||
_, title := WebPathToUserTitle(wikiName)
|
||||
return &api.WikiPageMetaData{
|
||||
Title: title,
|
||||
HTMLURL: util.URLJoin(repo.HTMLURL(), "wiki", subURL),
|
||||
HTMLURL: repo.HTMLURL() + "/wiki/" + subURL,
|
||||
SubURL: subURL,
|
||||
LastCommit: convert.ToWikiCommit(lastCommit),
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
{{svg "octicon-package" 48}}
|
||||
<h2>{{ctx.Locale.Tr "packages.empty"}}</h2>
|
||||
{{if and .Repository .CanWritePackages}}
|
||||
{{$packagesUrl := URLJoin .Owner.HomeLink "-" "packages"}}
|
||||
{{$packagesUrl := print .Owner.HomeLink "/-/packages"}}
|
||||
<p>{{ctx.Locale.Tr "packages.empty.repo" $packagesUrl}}</p>
|
||||
{{end}}
|
||||
<p>{{ctx.Locale.Tr "packages.empty.documentation" "https://docs.gitea.com/usage/packages/overview/"}}</p>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{{if or .UsesIgnoreRevs .FaultyIgnoreRevsFile}}
|
||||
{{$revsFileLink := URLJoin .RepoLink "src" .RefTypeNameSubURL "/.git-blame-ignore-revs"}}
|
||||
{{$revsFileLink := print .RepoLink "/src/" .RefTypeNameSubURL "/.git-blame-ignore-revs"}}
|
||||
{{if .UsesIgnoreRevs}}
|
||||
<div class="ui info message">
|
||||
<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 id="captcha" data-captcha-type="g-recaptcha" class="g-recaptcha-style" data-sitekey="{{.RecaptchaSitekey}}"></div>
|
||||
</div>
|
||||
<script defer src='{{URLJoin .RecaptchaURL "api.js"}}'></script>
|
||||
<script defer src='{{.RecaptchaAPIScriptURL}}'></script>
|
||||
{{else if eq .CaptchaType "hcaptcha"}}
|
||||
<div class="inline field tw-text-center required">
|
||||
<div id="captcha" data-captcha-type="h-captcha" class="h-captcha-style" data-sitekey="{{.HcaptchaSitekey}}"></div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user