Skip to content

Commit

Permalink
Merge remote-tracking branch 'giteaofficial/main'
Browse files Browse the repository at this point in the history
* giteaofficial/main:
  Fix typo (go-gitea#21695)
  Remove template previewer (go-gitea#21701)
  Revert "[skip ci] Updated translations via Crowdin" (go-gitea#21689)
  [skip ci] Updated translations via Crowdin
  Fix docs relref (go-gitea#21685)
  Add "Copy" button to file view of raw text (go-gitea#21629)
  Improve valid user name check (go-gitea#20136)
  • Loading branch information
zjjhot committed Nov 7, 2022
2 parents cc7c2c0 + a0367c2 commit af2a835
Show file tree
Hide file tree
Showing 18 changed files with 120 additions and 51 deletions.
2 changes: 1 addition & 1 deletion docs/content/doc/installation/from-source.fr-fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ git checkout pr-xyz

## Compilation

Comme nous regroupons déjà toutes les bibliothèques requises pour compiler Gitea, vous pouvez continuer avec le processus de compilation lui-même. Nous fournissons diverses [tâches Make](https://github.com/go-gitea/gitea/blob/master/Makefile) pour rendre le processus de construction aussi simple que possible. [Voyez ici comment obtenir Make]({{< relref "doc/developers/hacking-on-gitea.fr-fr.md" >}}#installing-make). Selon vos besoins, vous pourrez éventuellement ajouter diverses options de compilation, vous pouvez choisir entre ces options :
Comme nous regroupons déjà toutes les bibliothèques requises pour compiler Gitea, vous pouvez continuer avec le processus de compilation lui-même. Nous fournissons diverses [tâches Make](https://github.com/go-gitea/gitea/blob/master/Makefile) pour rendre le processus de construction aussi simple que possible. [Voyez ici comment obtenir Make](/fr-fr/hacking-on-gitea/). Selon vos besoins, vous pourrez éventuellement ajouter diverses options de compilation, vous pouvez choisir entre ces options :

* `bindata`: Intègre toutes les ressources nécessaires à l'exécution d'une instance de Gitea, ce qui rend un déploiement facile car il n'est pas nécessaire de se préoccuper des fichiers supplémentaires.
* `sqlite sqlite_unlock_notify`: Active la prise en charge d'une base de données [SQLite3](https://sqlite.org/), ceci n'est recommandé que pour les petites installations de Gitea.
Expand Down
2 changes: 1 addition & 1 deletion docs/content/doc/installation/from-source.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ git checkout v{{< version >}}

- `go` {{< min-go-version >}} 或以上版本, 详见[这里](https://golang.google.cn/doc/install)
- `node` {{< min-node-version >}} 或以上版本,并且安装 `npm`, 详见[这里](https://nodejs.org/zh-cn/download/)
- `make`, 详见[这里]({{< relref "doc/developers/hacking-on-gitea.zh-cn.md" >}})</a>
- `make`, 详见[这里](/zh-cn/hacking-on-gitea/)

各种可用的 [make 任务](https://github.com/go-gitea/gitea/blob/main/Makefile)
可以用来使编译过程更方便。
Expand Down
3 changes: 2 additions & 1 deletion models/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation"

"golang.org/x/crypto/argon2"
"golang.org/x/crypto/bcrypt"
Expand Down Expand Up @@ -621,7 +622,7 @@ var (
// IsUsableUsername returns an error when a username is reserved
func IsUsableUsername(name string) error {
// Validate username make sure it satisfies requirement.
if db.AlphaDashDotPattern.MatchString(name) {
if !validation.IsValidUsername(name) {
// Note: usually this error is normally caught up earlier in the UI
return db.ErrNameCharsNotAllowed{Name: name}
}
Expand Down
2 changes: 1 addition & 1 deletion modules/structs/admin_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type CreateUserOption struct {
SourceID int64 `json:"source_id"`
LoginName string `json:"login_name"`
// required: true
Username string `json:"username" binding:"Required;AlphaDashDot;MaxSize(40)"`
Username string `json:"username" binding:"Required;Username;MaxSize(40)"`
FullName string `json:"full_name" binding:"MaxSize(100)"`
// required: true
// swagger:strfmt email
Expand Down
20 changes: 20 additions & 0 deletions modules/validation/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ const (

// ErrRegexPattern is returned when a regex pattern is invalid
ErrRegexPattern = "RegexPattern"

// ErrUsername is username error
ErrUsername = "UsernameError"
)

// AddBindingRules adds additional binding rules
Expand All @@ -34,6 +37,7 @@ func AddBindingRules() {
addGlobPatternRule()
addRegexPatternRule()
addGlobOrRegexPatternRule()
addUsernamePatternRule()
}

func addGitRefNameBindingRule() {
Expand Down Expand Up @@ -148,6 +152,22 @@ func addGlobOrRegexPatternRule() {
})
}

func addUsernamePatternRule() {
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "Username"
},
IsValid: func(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
if !IsValidUsername(str) {
errs.Add([]string{name}, ErrUsername, "invalid username")
return false, errs
}
return true, errs
},
})
}

func portOnly(hostport string) string {
colon := strings.IndexByte(hostport, ':')
if colon == -1 {
Expand Down
12 changes: 12 additions & 0 deletions modules/validation/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,15 @@ func IsValidExternalTrackerURLFormat(uri string) bool {

return true
}

var (
validUsernamePattern = regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`)
invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`) // No consecutive or trailing non-alphanumeric chars
)

// IsValidUsername checks if username is valid
func IsValidUsername(name string) bool {
// It is difficult to find a single pattern that is both readable and effective,
// but it's easier to use positive and negative checks.
return validUsernamePattern.MatchString(name) && !invalidUsernamePattern.MatchString(name)
}
31 changes: 31 additions & 0 deletions modules/validation/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,34 @@ func Test_IsValidExternalTrackerURLFormat(t *testing.T) {
})
}
}

func TestIsValidUsername(t *testing.T) {
tests := []struct {
arg string
want bool
}{
{arg: "a", want: true},
{arg: "abc", want: true},
{arg: "0.b-c", want: true},
{arg: "a.b-c_d", want: true},
{arg: "", want: false},
{arg: ".abc", want: false},
{arg: "abc.", want: false},
{arg: "a..bc", want: false},
{arg: "a...bc", want: false},
{arg: "a.-bc", want: false},
{arg: "a._bc", want: false},
{arg: "a_-bc", want: false},
{arg: "a/bc", want: false},
{arg: "☁️", want: false},
{arg: "-", want: false},
{arg: "--diff", want: false},
{arg: "-im-here", want: false},
{arg: "a space", want: false},
}
for _, tt := range tests {
t.Run(tt.arg, func(t *testing.T) {
assert.Equalf(t, tt.want, IsValidUsername(tt.arg), "IsValidUsername(%v)", tt.arg)
})
}
}
2 changes: 2 additions & 0 deletions modules/web/middleware/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ func Validate(errs binding.Errors, data map[string]interface{}, f Form, l transl
data["ErrorMsg"] = trName + l.Tr("form.glob_pattern_error", errs[0].Message)
case validation.ErrRegexPattern:
data["ErrorMsg"] = trName + l.Tr("form.regex_pattern_error", errs[0].Message)
case validation.ErrUsername:
data["ErrorMsg"] = trName + l.Tr("form.username_error")
default:
msg := errs[0].Classification
if msg != "" && errs[0].Message != "" {
Expand Down
3 changes: 3 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ edit = Edit

copy = Copy
copy_url = Copy URL
copy_content = Copy content
copy_branch = Copy branch name
copy_success = Copied!
copy_error = Copy failed
Expand Down Expand Up @@ -463,6 +464,7 @@ url_error = `'%s' is not a valid URL.`
include_error = ` must contain substring '%s'.`
glob_pattern_error = ` glob pattern is invalid: %s.`
regex_pattern_error = ` regex pattern is invalid: %s.`
username_error = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-'), underscore ('_') and dot ('.'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.`
unknown_error = Unknown error:
captcha_incorrect = The CAPTCHA code is incorrect.
password_not_match = The passwords do not match.
Expand Down Expand Up @@ -1089,6 +1091,7 @@ editor.cannot_edit_non_text_files = Binary files cannot be edited in the web int
editor.edit_this_file = Edit File
editor.this_file_locked = File is locked
editor.must_be_on_a_branch = You must be on a branch to make or propose changes to this file.
editor.only_copy_raw = You may only copy raw text files.
editor.fork_before_edit = You must fork this repository to make or propose changes to this file.
editor.delete_this_file = Delete File
editor.must_have_write_access = You must have write access to make or propose changes to this file.
Expand Down
29 changes: 0 additions & 29 deletions routers/web/dev/template.go

This file was deleted.

5 changes: 0 additions & 5 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import (
"code.gitea.io/gitea/modules/web/routing"
"code.gitea.io/gitea/routers/web/admin"
"code.gitea.io/gitea/routers/web/auth"
"code.gitea.io/gitea/routers/web/dev"
"code.gitea.io/gitea/routers/web/events"
"code.gitea.io/gitea/routers/web/explore"
"code.gitea.io/gitea/routers/web/feed"
Expand Down Expand Up @@ -619,10 +618,6 @@ func RegisterRoutes(m *web.Route) {

m.Post("/{username}", reqSignIn, context_service.UserAssignmentWeb(), user.Action)

if !setting.IsProd {
m.Get("/template/*", dev.TemplatePreview)
}

reqRepoAdmin := context.RequireRepoAdmin()
reqRepoCodeWriter := context.RequireRepoWriter(unit.TypeCode)
canEnableEditor := context.CanEnableEditor()
Expand Down
4 changes: 2 additions & 2 deletions services/forms/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
type AdminCreateUserForm struct {
LoginType string `binding:"Required"`
LoginName string
UserName string `binding:"Required;AlphaDashDot;MaxSize(40)"`
UserName string `binding:"Required;Username;MaxSize(40)"`
Email string `binding:"Required;Email;MaxSize(254)"`
Password string `binding:"MaxSize(255)"`
SendNotify bool
Expand All @@ -35,7 +35,7 @@ func (f *AdminCreateUserForm) Validate(req *http.Request, errs binding.Errors) b
// AdminEditUserForm form for admin to create user
type AdminEditUserForm struct {
LoginType string `binding:"Required"`
UserName string `binding:"AlphaDashDot;MaxSize(40)"`
UserName string `binding:"Username;MaxSize(40)"`
LoginName string
FullName string `binding:"MaxSize(100)"`
Email string `binding:"Required;Email;MaxSize(254)"`
Expand Down
4 changes: 2 additions & 2 deletions services/forms/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (

// CreateOrgForm form for creating organization
type CreateOrgForm struct {
OrgName string `binding:"Required;AlphaDashDot;MaxSize(40)" locale:"org.org_name_holder"`
OrgName string `binding:"Required;Username;MaxSize(40)" locale:"org.org_name_holder"`
Visibility structs.VisibleType
RepoAdminChangeTeamAccess bool
}
Expand All @@ -37,7 +37,7 @@ func (f *CreateOrgForm) Validate(req *http.Request, errs binding.Errors) binding

// UpdateOrgSettingForm form for updating organization settings
type UpdateOrgSettingForm struct {
Name string `binding:"Required;AlphaDashDot;MaxSize(40)" locale:"org.org_name_holder"`
Name string `binding:"Required;Username;MaxSize(40)" locale:"org.org_name_holder"`
FullName string `binding:"MaxSize(100)"`
Description string `binding:"MaxSize(255)"`
Website string `binding:"ValidUrl;MaxSize(255)"`
Expand Down
6 changes: 3 additions & 3 deletions services/forms/user_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ type InstallForm struct {

PasswordAlgorithm string

AdminName string `binding:"OmitEmpty;AlphaDashDot;MaxSize(30)" locale:"install.admin_name"`
AdminName string `binding:"OmitEmpty;Username;MaxSize(30)" locale:"install.admin_name"`
AdminPasswd string `binding:"OmitEmpty;MaxSize(255)" locale:"install.admin_password"`
AdminConfirmPasswd string
AdminEmail string `binding:"OmitEmpty;MinSize(3);MaxSize(254);Include(@)" locale:"install.admin_email"`
Expand All @@ -91,7 +91,7 @@ func (f *InstallForm) Validate(req *http.Request, errs binding.Errors) binding.E

// RegisterForm form for registering
type RegisterForm struct {
UserName string `binding:"Required;AlphaDashDot;MaxSize(40)"`
UserName string `binding:"Required;Username;MaxSize(40)"`
Email string `binding:"Required;MaxSize(254)"`
Password string `binding:"MaxSize(255)"`
Retype string
Expand Down Expand Up @@ -243,7 +243,7 @@ func (f *IntrospectTokenForm) Validate(req *http.Request, errs binding.Errors) b

// UpdateProfileForm form for updating profile
type UpdateProfileForm struct {
Name string `binding:"AlphaDashDot;MaxSize(40)"`
Name string `binding:"Username;MaxSize(40)"`
FullName string `binding:"MaxSize(100)"`
KeepEmailPrivate bool
Website string `binding:"ValidSiteUrl;MaxSize(255)"`
Expand Down
2 changes: 1 addition & 1 deletion services/forms/user_form_auth_openid.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (f *SignInOpenIDForm) Validate(req *http.Request, errs binding.Errors) bind

// SignUpOpenIDForm form for signin up with OpenID
type SignUpOpenIDForm struct {
UserName string `binding:"Required;AlphaDashDot;MaxSize(40)"`
UserName string `binding:"Required;Username;MaxSize(40)"`
Email string `binding:"Required;Email;MaxSize(254)"`
GRecaptchaResponse string `form:"g-recaptcha-response"`
HcaptchaResponse string `form:"h-captcha-response"`
Expand Down
5 changes: 5 additions & 0 deletions templates/repo/view_file.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@
{{end}}
</div>
<a download href="{{$.RawFileLink}}"><span class="btn-octicon tooltip" data-content="{{.locale.Tr "repo.download_file"}}" data-position="bottom center">{{svg "octicon-download"}}</span></a>
{{if or .IsMarkup .IsRenderedHTML (not .IsTextSource)}}
<span class="btn-octicon tooltip disabled" id="copy-file-content" data-content="{{.locale.Tr "repo.editor.only_copy_raw"}}" aria-label="{{.locale.Tr "repo.editor.only_copy_raw"}}">{{svg "octicon-copy" 14}}</span>
{{else}}
<a class="btn-octicon tooltip" id="copy-file-content" data-content="{{.locale.Tr "copy_content"}}" aria-label="{{.locale.Tr "copy_content"}}">{{svg "octicon-copy" 14}}</a>
{{end}}
{{if .Repository.CanEnableEditor}}
{{if .CanEditFile}}
<a href="{{.RepoLink}}/_edit/{{PathEscapeSegments .BranchName}}/{{PathEscapeSegments .TreePath}}"><span class="btn-octicon tooltip" data-content="{{.EditFileTooltip}}" data-position="bottom center">{{svg "octicon-pencil"}}</span></a>
Expand Down
22 changes: 18 additions & 4 deletions tests/integration/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ func TestRenameInvalidUsername(t *testing.T) {
"%00",
"thisHas ASpace",
"p<A>tho>lo<gical",
".",
"..",
".well-known",
".abc",
"abc.",
"a..bc",
"a...bc",
"a.-bc",
"a._bc",
"a_-bc",
"a/bc",
"☁️",
"-",
"--diff",
"-im-here",
"a space",
}

session := loginUser(t, "user2")
Expand All @@ -68,7 +84,7 @@ func TestRenameInvalidUsername(t *testing.T) {
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Contains(t,
htmlDoc.doc.Find(".ui.negative.message").Text(),
translation.NewLocale("en-US").Tr("form.alpha_dash_dot_error"),
translation.NewLocale("en-US").Tr("form.username_error"),
)

unittest.AssertNotExistsBean(t, &user_model.User{Name: invalidUsername})
Expand All @@ -79,9 +95,7 @@ func TestRenameReservedUsername(t *testing.T) {
defer tests.PrepareTestEnv(t)()

reservedUsernames := []string{
".",
"..",
".well-known",
// ".", "..", ".well-known", // The names are not only reserved but also invalid
"admin",
"api",
"assets",
Expand Down
17 changes: 16 additions & 1 deletion web_src/js/features/repo-code.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import $ from 'jquery';
import {svg} from '../svg.js';
import {invertFileFolding} from './file-fold.js';
import {createTippy} from '../modules/tippy.js';
import {createTippy, showTemporaryTooltip} from '../modules/tippy.js';
import {copyToClipboard} from './clipboard.js';

const {i18n} = window.config;

function changeHash(hash) {
if (window.history.pushState) {
window.history.pushState(null, null, hash);
Expand Down Expand Up @@ -110,6 +112,18 @@ function showLineButton() {
});
}

function initCopyFileContent() {
// get raw text for copy content button, at the moment, only one button (and one related file content) is supported.
const copyFileContent = document.querySelector('#copy-file-content');
if (!copyFileContent) return;

copyFileContent.addEventListener('click', async () => {
const text = Array.from(document.querySelectorAll('.file-view .lines-code')).map((el) => el.textContent).join('');
const success = await copyToClipboard(text);
showTemporaryTooltip(copyFileContent, success ? i18n.copy_success : i18n.copy_error);
});
}

export function initRepoCodeView() {
if ($('.code-view .lines-num').length > 0) {
$(document).on('click', '.lines-num span', function (e) {
Expand Down Expand Up @@ -185,4 +199,5 @@ export function initRepoCodeView() {
if (!success) return;
document.querySelector('.code-line-button')?._tippy?.hide();
});
initCopyFileContent();
}

0 comments on commit af2a835

Please sign in to comment.