https://mcaptcha.org/ Co-authored-by: Felipe Leopoldo Sologuren Gutiérrez <fsologureng@users.noreply.github.com>tags/v1.18.0-rc0
@@ -698,9 +698,11 @@ ROUTER = console | |||
;; Enable captcha validation for registration | |||
;ENABLE_CAPTCHA = false | |||
;; | |||
;; Type of captcha you want to use. Options: image, recaptcha, hcaptcha | |||
;; Type of captcha you want to use. Options: image, recaptcha, hcaptcha, mcaptcha. | |||
;CAPTCHA_TYPE = image | |||
;; | |||
;; Change this to use recaptcha.net or other recaptcha service | |||
;RECAPTCHA_URL = https://www.google.com/recaptcha/ | |||
;; Enable recaptcha to use Google's recaptcha service | |||
;; Go to https://www.google.com/recaptcha/admin to sign up for a key | |||
;RECAPTCHA_SECRET = | |||
@@ -710,8 +712,13 @@ ROUTER = console | |||
;HCAPTCHA_SECRET = | |||
;HCAPTCHA_SITEKEY = | |||
;; | |||
;; Change this to use recaptcha.net or other recaptcha service | |||
;RECAPTCHA_URL = https://www.google.com/recaptcha/ | |||
;; Change this to use demo.mcaptcha.org or your self-hosted mcaptcha.org instance. | |||
;MCAPTCHA_URL = https://demo.mcaptcha.org | |||
;; | |||
;; Go to your configured mCaptcha instance and register a sitekey | |||
;; and use your account's secret. | |||
;MCAPTCHA_SECRET = | |||
;MCAPTCHA_SITEKEY = | |||
;; | |||
;; Default value for KeepEmailPrivate | |||
;; Each new user will get the value of this setting copied into their profile |
@@ -579,13 +579,16 @@ Certain queues have defaults that override the defaults set in `[queue]` (this o | |||
provided email rather than a generated email. | |||
- `ENABLE_CAPTCHA`: **false**: Enable this to use captcha validation for registration. | |||
- `REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA`: **false**: Enable this to force captcha validation | |||
even for External Accounts (i.e. GitHub, OpenID Connect, etc). You must `ENABLE_CAPTCHA` also. | |||
- `CAPTCHA_TYPE`: **image**: \[image, recaptcha, hcaptcha\] | |||
even for External Accounts (i.e. GitHub, OpenID Connect, etc). You also must enable `ENABLE_CAPTCHA`. | |||
- `CAPTCHA_TYPE`: **image**: \[image, recaptcha, hcaptcha, mcaptcha\] | |||
- `RECAPTCHA_SECRET`: **""**: Go to https://www.google.com/recaptcha/admin to get a secret for recaptcha. | |||
- `RECAPTCHA_SITEKEY`: **""**: Go to https://www.google.com/recaptcha/admin to get a sitekey for recaptcha. | |||
- `RECAPTCHA_URL`: **https://www.google.com/recaptcha/**: Set the recaptcha url - allows the use of recaptcha net. | |||
- `HCAPTCHA_SECRET`: **""**: Sign up at https://www.hcaptcha.com/ to get a secret for hcaptcha. | |||
- `HCAPTCHA_SITEKEY`: **""**: Sign up at https://www.hcaptcha.com/ to get a sitekey for hcaptcha. | |||
- `MCAPTCHA_SECRET`: **""**: Go to your mCaptcha instance to get a secret for mCaptcha. | |||
- `MCAPTCHA_SITEKEY`: **""**: Go to your mCaptcha instance to get a sitekey for mCaptcha. | |||
- `MCAPTCHA_URL` **https://demo.mcaptcha.org/**: Set the mCaptcha URL. | |||
- `DEFAULT_KEEP_EMAIL_PRIVATE`: **false**: By default set users to keep their email address private. | |||
- `DEFAULT_ALLOW_CREATE_ORGANIZATION`: **true**: Allow new users to create organizations by default. | |||
- `DEFAULT_USER_IS_RESTRICTED`: **false**: Give new users restricted permissions by default |
@@ -5,6 +5,7 @@ go 1.18 | |||
require ( | |||
code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b | |||
code.gitea.io/sdk/gitea v0.15.1 | |||
codeberg.org/gusted/mcaptcha v0.0.0-20220722211632-55c1ffff1222 | |||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb | |||
gitea.com/go-chi/cache v0.2.0 | |||
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5 |
@@ -62,6 +62,8 @@ code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b/go.mod h1:zcNbT/aJE | |||
code.gitea.io/sdk/gitea v0.11.3/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= | |||
code.gitea.io/sdk/gitea v0.15.1 h1:WJreC7YYuxbn0UDaPuWIe/mtiNKTvLN8MLkaw71yx/M= | |||
code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA= | |||
codeberg.org/gusted/mcaptcha v0.0.0-20220722211632-55c1ffff1222 h1:PCW4i+gnQ9XxF8V+nBch3KWdGe4MiP3xXUCA/z0jhHk= | |||
codeberg.org/gusted/mcaptcha v0.0.0-20220722211632-55c1ffff1222/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM= | |||
contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= | |||
contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0= | |||
contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw= |
@@ -0,0 +1,27 @@ | |||
// Copyright 2022 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package mcaptcha | |||
import ( | |||
"context" | |||
"fmt" | |||
"code.gitea.io/gitea/modules/setting" | |||
"codeberg.org/gusted/mcaptcha" | |||
) | |||
func Verify(ctx context.Context, token string) (bool, error) { | |||
valid, err := mcaptcha.Verify(ctx, &mcaptcha.VerifyOpts{ | |||
InstanceURL: setting.Service.McaptchaURL, | |||
Sitekey: setting.Service.McaptchaSitekey, | |||
Secret: setting.Service.McaptchaSecret, | |||
Token: token, | |||
}) | |||
if err != nil { | |||
return false, fmt.Errorf("wasn't able to verify mCaptcha: %v", err) | |||
} | |||
return valid, nil | |||
} |
@@ -47,6 +47,9 @@ var Service = struct { | |||
RecaptchaURL string | |||
HcaptchaSecret string | |||
HcaptchaSitekey string | |||
McaptchaSecret string | |||
McaptchaSitekey string | |||
McaptchaURL string | |||
DefaultKeepEmailPrivate bool | |||
DefaultAllowCreateOrganization bool | |||
DefaultUserIsRestricted bool | |||
@@ -133,6 +136,9 @@ func newService() { | |||
Service.RecaptchaURL = sec.Key("RECAPTCHA_URL").MustString("https://www.google.com/recaptcha/") | |||
Service.HcaptchaSecret = sec.Key("HCAPTCHA_SECRET").MustString("") | |||
Service.HcaptchaSitekey = sec.Key("HCAPTCHA_SITEKEY").MustString("") | |||
Service.McaptchaURL = sec.Key("MCAPTCHA_URL").MustString("https://demo.mcaptcha.org/") | |||
Service.McaptchaSecret = sec.Key("MCAPTCHA_SECRET").MustString("") | |||
Service.McaptchaSitekey = sec.Key("MCAPTCHA_SITEKEY").MustString("") | |||
Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool() | |||
Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true) | |||
Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false) |
@@ -59,6 +59,7 @@ const ( | |||
ImageCaptcha = "image" | |||
ReCaptcha = "recaptcha" | |||
HCaptcha = "hcaptcha" | |||
MCaptcha = "mcaptcha" | |||
) | |||
// settings |
@@ -8,6 +8,7 @@ | |||
"license": "MIT", | |||
"dependencies": { | |||
"@claviska/jquery-minicolors": "2.3.6", | |||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-2", | |||
"@primer/octicons": "17.4.0", | |||
"add-asset-webpack-plugin": "2.0.1", | |||
"css-loader": "6.7.1", | |||
@@ -1662,6 +1663,55 @@ | |||
"jsep": "^0.4.0||^1.0.0" | |||
} | |||
}, | |||
"node_modules/@mcaptcha/core-glue": { | |||
"version": "0.1.0-alpha-3", | |||
"resolved": "https://registry.npmjs.org/@mcaptcha/core-glue/-/core-glue-0.1.0-alpha-3.tgz", | |||
"integrity": "sha512-avphBVgf3PPDWuUoDsB2qiXAss2pc00lUILswJaMQofr8FQyflzkhha8H2Z+qGFiX0Iib/yyP2TOtBDbHqE9Tg==", | |||
"funding": [ | |||
{ | |||
"type": "individual", | |||
"url": "http://mcaptcha.org/donate" | |||
}, | |||
{ | |||
"type": "liberapay", | |||
"url": "https://liberapay.com/mcaptcha" | |||
}, | |||
{ | |||
"type": "individual", | |||
"url": "http://batsense.net/donate" | |||
}, | |||
{ | |||
"type": "liberapay", | |||
"url": "https://liberapay.com/realaravinth" | |||
} | |||
] | |||
}, | |||
"node_modules/@mcaptcha/vanilla-glue": { | |||
"version": "0.1.0-alpha-2", | |||
"resolved": "https://registry.npmjs.org/@mcaptcha/vanilla-glue/-/vanilla-glue-0.1.0-alpha-2.tgz", | |||
"integrity": "sha512-cQOg3EIhdjk1xoZtjD9SVPwQAnd49FCvHKchwFZZuhdNTeFs7SUHynOCekuGow2Ip0RJZuMZGcRxvWMgd0ogng==", | |||
"funding": [ | |||
{ | |||
"type": "individual", | |||
"url": "http://mcaptcha.org/donate" | |||
}, | |||
{ | |||
"type": "liberapay", | |||
"url": "https://liberapay.com/mcaptcha" | |||
}, | |||
{ | |||
"type": "individual", | |||
"url": "http://batsense.net/donate" | |||
}, | |||
{ | |||
"type": "liberapay", | |||
"url": "https://liberapay.com/realaravinth" | |||
} | |||
], | |||
"dependencies": { | |||
"@mcaptcha/core-glue": "^0.1.0-alpha-3" | |||
} | |||
}, | |||
"node_modules/@nodelib/fs.scandir": { | |||
"version": "2.1.5", | |||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", | |||
@@ -14144,6 +14194,19 @@ | |||
"dev": true, | |||
"requires": {} | |||
}, | |||
"@mcaptcha/core-glue": { | |||
"version": "0.1.0-alpha-3", | |||
"resolved": "https://registry.npmjs.org/@mcaptcha/core-glue/-/core-glue-0.1.0-alpha-3.tgz", | |||
"integrity": "sha512-avphBVgf3PPDWuUoDsB2qiXAss2pc00lUILswJaMQofr8FQyflzkhha8H2Z+qGFiX0Iib/yyP2TOtBDbHqE9Tg==" | |||
}, | |||
"@mcaptcha/vanilla-glue": { | |||
"version": "0.1.0-alpha-2", | |||
"resolved": "https://registry.npmjs.org/@mcaptcha/vanilla-glue/-/vanilla-glue-0.1.0-alpha-2.tgz", | |||
"integrity": "sha512-cQOg3EIhdjk1xoZtjD9SVPwQAnd49FCvHKchwFZZuhdNTeFs7SUHynOCekuGow2Ip0RJZuMZGcRxvWMgd0ogng==", | |||
"requires": { | |||
"@mcaptcha/core-glue": "^0.1.0-alpha-3" | |||
} | |||
}, | |||
"@nodelib/fs.scandir": { | |||
"version": "2.1.5", | |||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", |
@@ -8,6 +8,7 @@ | |||
}, | |||
"dependencies": { | |||
"@claviska/jquery-minicolors": "2.3.6", | |||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-2", | |||
"@primer/octicons": "17.4.0", | |||
"add-asset-webpack-plugin": "2.0.1", | |||
"css-loader": "6.7.1", |
@@ -18,6 +18,7 @@ import ( | |||
"code.gitea.io/gitea/modules/eventsource" | |||
"code.gitea.io/gitea/modules/hcaptcha" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/mcaptcha" | |||
"code.gitea.io/gitea/modules/password" | |||
"code.gitea.io/gitea/modules/recaptcha" | |||
"code.gitea.io/gitea/modules/session" | |||
@@ -414,6 +415,8 @@ func SignUp(ctx *context.Context) { | |||
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["PageIsSignUp"] = true | |||
// Show Disabled Registration message if DisableRegistration or AllowOnlyExternalRegistration options are true | |||
@@ -435,6 +438,8 @@ func SignUpPost(ctx *context.Context) { | |||
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["PageIsSignUp"] = true | |||
// Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true | |||
@@ -458,6 +463,8 @@ func SignUpPost(ctx *context.Context) { | |||
valid, err = recaptcha.Verify(ctx, form.GRecaptchaResponse) | |||
case setting.HCaptcha: | |||
valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse) | |||
case setting.MCaptcha: | |||
valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse) | |||
default: | |||
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType)) | |||
return |
@@ -16,6 +16,7 @@ import ( | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/hcaptcha" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/mcaptcha" | |||
"code.gitea.io/gitea/modules/recaptcha" | |||
"code.gitea.io/gitea/modules/session" | |||
"code.gitea.io/gitea/modules/setting" | |||
@@ -40,6 +41,8 @@ func LinkAccount(ctx *context.Context) { | |||
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["DisableRegistration"] = setting.Service.DisableRegistration | |||
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration | |||
ctx.Data["ShowRegistrationButton"] = false | |||
@@ -96,6 +99,8 @@ func LinkAccountPostSignIn(ctx *context.Context) { | |||
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["DisableRegistration"] = setting.Service.DisableRegistration | |||
ctx.Data["ShowRegistrationButton"] = false | |||
@@ -195,6 +200,8 @@ func LinkAccountPostRegister(ctx *context.Context) { | |||
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["DisableRegistration"] = setting.Service.DisableRegistration | |||
ctx.Data["ShowRegistrationButton"] = false | |||
@@ -233,6 +240,8 @@ func LinkAccountPostRegister(ctx *context.Context) { | |||
valid, err = recaptcha.Verify(ctx, form.GRecaptchaResponse) | |||
case setting.HCaptcha: | |||
valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse) | |||
case setting.MCaptcha: | |||
valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse) | |||
default: | |||
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType)) | |||
return |
@@ -15,6 +15,7 @@ import ( | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/hcaptcha" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/mcaptcha" | |||
"code.gitea.io/gitea/modules/recaptcha" | |||
"code.gitea.io/gitea/modules/session" | |||
"code.gitea.io/gitea/modules/setting" | |||
@@ -341,6 +342,8 @@ func RegisterOpenID(ctx *context.Context) { | |||
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["OpenID"] = oid | |||
userName, _ := ctx.Session.Get("openid_determined_username").(string) | |||
if userName != "" { | |||
@@ -372,6 +375,8 @@ func RegisterOpenIDPost(ctx *context.Context) { | |||
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["OpenID"] = oid | |||
if setting.Service.AllowOnlyInternalRegistration { | |||
@@ -397,6 +402,12 @@ func RegisterOpenIDPost(ctx *context.Context) { | |||
return | |||
} | |||
valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse) | |||
case setting.MCaptcha: | |||
if err := ctx.Req.ParseForm(); err != nil { | |||
ctx.ServerError("", err) | |||
return | |||
} | |||
valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse) | |||
default: | |||
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType)) | |||
return |
@@ -96,6 +96,7 @@ type RegisterForm struct { | |||
Retype string | |||
GRecaptchaResponse string `form:"g-recaptcha-response"` | |||
HcaptchaResponse string `form:"h-captcha-response"` | |||
McaptchaResponse string `form:"m-captcha-response"` | |||
} | |||
// Validate validates the fields |
@@ -31,6 +31,7 @@ type SignUpOpenIDForm struct { | |||
Email string `binding:"Required;Email;MaxSize(254)"` | |||
GRecaptchaResponse string `form:"g-recaptcha-response"` | |||
HcaptchaResponse string `form:"h-captcha-response"` | |||
McaptchaResponse string `form:"m-captcha-response"` | |||
} | |||
// Validate validates the fields |
@@ -54,6 +54,14 @@ | |||
<div class="h-captcha" data-sitekey="{{ .HcaptchaSitekey }}"></div> | |||
</div> | |||
{{end}} | |||
{{if and .EnableCaptcha (eq .CaptchaType "mcaptcha")}} | |||
<div class="inline field df ac db-small"> | |||
<span>{{.locale.Tr "captcha"}}</span> | |||
<div class="border-secondary w-100-small" id="mcaptcha__widget-container" style="width: 50%; height: 5em"></div> | |||
<div class="m-captcha" data-sitekey="{{ .McaptchaSitekey }}" data-instance-url="{{ .McaptchaURL }}"></div> | |||
</div> | |||
{{end}} | |||
<div class="inline field"> | |||
<label></label> |
@@ -40,6 +40,11 @@ | |||
<div class="h-captcha" data-sitekey="{{ .HcaptchaSitekey }}"></div> | |||
</div> | |||
{{end}} | |||
{{if and .EnableCaptcha (eq .CaptchaType "mcaptcha")}} | |||
<div class="inline field required"> | |||
<div class="m-captcha" data-sitekey="{{ .McaptchaSitekey }}" data-instance-url="{{ .McaptchaURL }}"></div> | |||
</div> | |||
{{end}} | |||
<div class="inline field"> | |||
<label for="openid">OpenID URI</label> | |||
<input id="openid" value="{{ .OpenID }}" readonly> |
@@ -0,0 +1,16 @@ | |||
export async function initMcaptcha() { | |||
const mCaptchaEl = document.querySelector('.m-captcha'); | |||
if (!mCaptchaEl) return; | |||
const {default: mCaptcha} = await import(/* webpackChunkName: "mcaptcha-vanilla-glue" */'@mcaptcha/vanilla-glue'); | |||
mCaptcha.INPUT_NAME = 'm-captcha-response'; | |||
const siteKey = mCaptchaEl.getAttribute('data-sitekey'); | |||
const instanceURL = mCaptchaEl.getAttribute('data-instance-url'); | |||
mCaptcha.default({ | |||
siteKey: { | |||
instanceUrl: new URL(instanceURL), | |||
key: siteKey, | |||
} | |||
}); | |||
} |
@@ -86,6 +86,7 @@ import {initCommonOrganization} from './features/common-organization.js'; | |||
import {initRepoWikiForm} from './features/repo-wiki.js'; | |||
import {initRepoCommentForm, initRepository} from './features/repo-legacy.js'; | |||
import {initFormattingReplacements} from './features/formatting.js'; | |||
import {initMcaptcha} from './features/mcaptcha.js'; | |||
// Run time-critical code as soon as possible. This is safe to do because this | |||
// script appears at the end of <body> and rendered HTML is accessible at that point. | |||
@@ -182,6 +183,7 @@ $(document).ready(() => { | |||
initRepository(); | |||
initCommitStatuses(); | |||
initMcaptcha(); | |||
initUserAuthLinkAccountView(); | |||
initUserAuthOauth2(); |
@@ -156,7 +156,8 @@ textarea:focus, | |||
padding-left: @create-page-form-input-padding+30px; | |||
} | |||
.inline.field > label { | |||
.inline.field > label, | |||
.inline.field > span { | |||
text-align: right; | |||
width: @create-page-form-input-padding; | |||
word-wrap: break-word; |
@@ -168,3 +168,8 @@ | |||
.py-3 { padding-top: .5rem !important; padding-bottom: .5rem !important; } | |||
.py-4 { padding-top: 1rem !important; padding-bottom: 1rem !important; } | |||
.py-5 { padding-top: 2rem !important; padding-bottom: 2rem !important; } | |||
@media @mediaSm { | |||
.db-small { display: block !important; } | |||
.w-100-small { width: 100% !important; } | |||
} |