Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bab7d885aa | |||
| 42229dc0b8 | |||
| e3d8e92bdc | |||
| 6fc73a8433 | |||
| b1a0a78a51 | |||
| 9c7d8b3096 | |||
| 93feb1a666 | |||
| bb0e2121a3 | |||
| d21b7fd3af | |||
| 743553f3e9 | |||
| a3ccbb5b7f | |||
| 4b7cb813e6 | |||
| 23b8214549 | |||
| 08feb6b664 | |||
| 1aa5dc75df | |||
| ee234aff61 | |||
| a3f3e310fb | |||
| ea56bdca5f | |||
| 45c836badc | |||
| f9ea4ab69a | |||
| e6d46eeb55 | |||
| 5bb0c92b6c | |||
| c1e6be47d7 | |||
| 79a5e68816 | |||
| 9bcbbd419f | |||
| f460b7543e | |||
| 1cb649525d | |||
| 99861e3e06 | |||
| 66b8a43e5f | |||
| d285905826 | |||
| 4df2320ba6 | |||
| 0fe99cc00c | |||
| 580401ecbf | |||
| 7aa29720f0 | |||
| 3e5c844a77 | |||
| 4047c5c068 | |||
| 03d924238c | |||
| bc1248ed9e | |||
| dd52c08b74 | |||
| b811b819e2 | |||
| da985b25ce | |||
| ae9c51df7c | |||
| ff1c5815bb | |||
| 87f8d37be5 | |||
| f4b96c1041 | |||
| a3f72303d1 | |||
| 4317806ade | |||
| 578f19a682 | |||
| f9b6404950 | |||
| 52517e3e23 | |||
| 36e96e3481 | |||
| a765410d0f | |||
| 43fc2e528c | |||
| cb90eda213 | |||
| 5f9c18b2b3 | |||
| 4384b85046 | |||
| e0973a84a0 | |||
| 054bc55a1c | |||
| 4fb718d405 | |||
| df35049196 | |||
| ce75461380 | |||
| cea85c30a4 | |||
| 6039138323 | |||
| eb43e73785 | |||
| c077a0361a | |||
| 6f21a94d18 |
+26
-16
@@ -25,7 +25,7 @@ steps:
|
||||
- make deps-frontend
|
||||
|
||||
- name: deps-backend
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- make deps-backend
|
||||
@@ -49,11 +49,14 @@ steps:
|
||||
GOSUMDB: sum.golang.org
|
||||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
depends_on: [deps-backend]
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
|
||||
- name: lint-backend-windows
|
||||
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||
commands:
|
||||
- make golangci-lint vet
|
||||
- make golangci-lint-windows vet
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOSUMDB: sum.golang.org
|
||||
@@ -61,6 +64,9 @@ steps:
|
||||
GOOS: windows
|
||||
GOARCH: amd64
|
||||
depends_on: [deps-backend]
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
|
||||
- name: lint-backend-gogit
|
||||
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||
@@ -71,6 +77,9 @@ steps:
|
||||
GOSUMDB: sum.golang.org
|
||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||
depends_on: [deps-backend]
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
|
||||
- name: checks-frontend
|
||||
image: node:16
|
||||
@@ -79,7 +88,7 @@ steps:
|
||||
depends_on: [deps-frontend]
|
||||
|
||||
- name: checks-backend
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
commands:
|
||||
- make checks-backend
|
||||
depends_on: [deps-backend]
|
||||
@@ -100,7 +109,7 @@ steps:
|
||||
depends_on: [test-frontend]
|
||||
|
||||
- name: build-backend-no-gcc
|
||||
image: golang:1.16 # this step is kept as the lowest version of golang that we support
|
||||
image: golang:1.17 # this step is kept as the lowest version of golang that we support
|
||||
pull: always
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
@@ -113,7 +122,7 @@ steps:
|
||||
path: /go
|
||||
|
||||
- name: build-backend-arm64
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
GOPROXY: https://goproxy.cn
|
||||
@@ -129,7 +138,7 @@ steps:
|
||||
path: /go
|
||||
|
||||
- name: build-backend-windows
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
GOPROXY: https://goproxy.cn
|
||||
@@ -144,7 +153,7 @@ steps:
|
||||
path: /go
|
||||
|
||||
- name: build-backend-386
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
GOPROXY: https://goproxy.cn
|
||||
@@ -233,7 +242,7 @@ steps:
|
||||
- pull_request
|
||||
|
||||
- name: deps-backend
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- make deps-backend
|
||||
@@ -350,7 +359,7 @@ steps:
|
||||
path: /go
|
||||
|
||||
- name: generate-coverage
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
commands:
|
||||
- make coverage
|
||||
environment:
|
||||
@@ -425,7 +434,7 @@ steps:
|
||||
- pull_request
|
||||
|
||||
- name: deps-backend
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- make deps-backend
|
||||
@@ -567,7 +576,7 @@ trigger:
|
||||
|
||||
steps:
|
||||
- name: download
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- timeout -s ABRT 40m make generate-license generate-gitignore
|
||||
@@ -628,7 +637,7 @@ steps:
|
||||
- make deps-frontend
|
||||
|
||||
- name: deps-backend
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- make deps-backend
|
||||
@@ -637,7 +646,7 @@ steps:
|
||||
path: /go
|
||||
|
||||
- name: static
|
||||
image: techknowlogick/xgo:go-1.17.x
|
||||
image: techknowlogick/xgo:go-1.18.x
|
||||
pull: always
|
||||
commands:
|
||||
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs
|
||||
@@ -746,7 +755,7 @@ steps:
|
||||
- make deps-frontend
|
||||
|
||||
- name: deps-backend
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- make deps-backend
|
||||
@@ -755,7 +764,7 @@ steps:
|
||||
path: /go
|
||||
|
||||
- name: static
|
||||
image: techknowlogick/xgo:go-1.17.x
|
||||
image: techknowlogick/xgo:go-1.18.x
|
||||
pull: always
|
||||
commands:
|
||||
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs
|
||||
@@ -804,11 +813,12 @@ steps:
|
||||
depends_on: [gpg-sign]
|
||||
|
||||
- name: github
|
||||
image: plugins/github-release:1
|
||||
image: plugins/github-release:latest
|
||||
pull: always
|
||||
settings:
|
||||
files:
|
||||
- "dist/release/*"
|
||||
file_exists: overwrite
|
||||
environment:
|
||||
GITHUB_TOKEN:
|
||||
from_secret: github_token
|
||||
|
||||
+13
-2
@@ -13,7 +13,7 @@ linters:
|
||||
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
||||
- gofmt
|
||||
- misspell
|
||||
- gocritic
|
||||
#- gocritic # TODO: disabled until fixed with go 1.18
|
||||
- bidichk
|
||||
- ineffassign
|
||||
- revive
|
||||
@@ -22,7 +22,7 @@ linters:
|
||||
fast: false
|
||||
|
||||
run:
|
||||
timeout: 3m
|
||||
timeout: 10m
|
||||
skip-dirs:
|
||||
- node_modules
|
||||
- public
|
||||
@@ -61,6 +61,9 @@ linters-settings:
|
||||
- name: errorf
|
||||
- name: duplicated-imports
|
||||
- name: modifies-value-receiver
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
lang-version: 1.18
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
@@ -148,3 +151,11 @@ issues:
|
||||
- path: models/user/openid.go
|
||||
linters:
|
||||
- golint
|
||||
- linters: staticcheck
|
||||
text: "strings.Title is deprecated: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead."
|
||||
- linters: staticcheck
|
||||
text: "util.FindClosure is deprecated: This function can not handle newlines. Many elements can be existed over multiple lines(e.g. link labels). Use text.Reader.FindClosure."
|
||||
- linters: staticcheck
|
||||
text: "gossh.SigAlgoRSASHA2256 is deprecated: use KeyAlgoRSASHA256."
|
||||
- linters: staticcheck
|
||||
text: "gossh.SigAlgoRSASHA2512 is deprecated: use KeyAlgoRSASHA512."
|
||||
|
||||
@@ -4,6 +4,88 @@ This changelog goes through all the changes that have been made in each release
|
||||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
|
||||
## [1.16.5](https://github.com/go-gitea/gitea/releases/tag/1.16.5) - 2022-03-23
|
||||
|
||||
* BREAKING
|
||||
* Bump to build with go1.18 (#19120 et al) (#19127)
|
||||
* SECURITY
|
||||
* Prevent redirect to Host (2) (#19175) (#19186)
|
||||
* Try to prevent autolinking of displaynames by email readers (#19169) (#19183)
|
||||
* Clean paths when looking in Storage (#19124) (#19179)
|
||||
* Do not send notification emails to inactive users (#19131) (#19139)
|
||||
* Do not send activation email if manual confirm is set (#19119) (#19122)
|
||||
* ENHANCEMENTS
|
||||
* Use the new/choose link for New Issue on project page (#19172) (#19176)
|
||||
* BUGFIXES
|
||||
* Fix showing issues in your repositories (#18916) (#19191)
|
||||
* Fix compare link in active feeds for new branch (#19149) (#19185)
|
||||
* Redirect .wiki/* ui link to /wiki (#18831) (#19184)
|
||||
* Ensure deploy keys with write access can push (#19010) (#19182)
|
||||
* Ensure that setting.LocalURL always has a trailing slash (#19171) (#19177)
|
||||
* Cleanup protected branches when deleting users & teams (#19158) (#19174)
|
||||
* Use IterateBufferSize whilst querying repositories during adoption check (#19140) (#19160)
|
||||
* Fix NPE /repos/issues/search when not signed in (#19154) (#19155)
|
||||
* Use custom favicon when viewing static files if it exists (#19130) (#19152)
|
||||
* Fix the editor height in review box (#19003) (#19147)
|
||||
* Ensure isSSH is set whenever DISABLE_HTTP_GIT is set (#19028) (#19146)
|
||||
* Fix wrong scopes caused by empty scope input (#19029) (#19145)
|
||||
* Make migrations SKIP_TLS_VERIFY apply to git too (#19132) (#19141)
|
||||
* Handle email address not exist (#19089) (#19121)
|
||||
* MISC
|
||||
* Update json-iterator to allow compilation with go1.18 (#18644) (#19100)
|
||||
* Update golang.org/x/crypto (#19097) (#19098)
|
||||
|
||||
## [1.16.4](https://github.com/go-gitea/gitea/releases/tag/v1.16.4) - 2022-03-14
|
||||
|
||||
* SECURITY
|
||||
* Restrict email address validation (#17688) (#19085)
|
||||
* Fix lfs bug (#19072) (#19080)
|
||||
* ENHANCEMENTS
|
||||
* Improve SyncMirrors logging (#19045) (#19050)
|
||||
* BUGFIXES
|
||||
* Refactor mirror code & fix `StartToMirror` (#18904) (#19075)
|
||||
* Update the webauthn_credential_id_sequence in Postgres (#19048) (#19060)
|
||||
* Prevent 500 when there is an error during new auth source post (#19041) (#19059)
|
||||
* If rendering has failed due to a net.OpError stop rendering (attempt 2) (#19049) (#19056)
|
||||
* Fix flag validation (#19046) (#19051)
|
||||
* Add pam account authorization check (#19040) (#19047)
|
||||
* Ignore missing comment for user notifications (#18954) (#19043)
|
||||
* Set `rel="nofollow noindex"` on new issue links (#19023) (#19042)
|
||||
* Upgrading binding package (#19034) (#19035)
|
||||
* Don't show context cancelled errors in attribute reader (#19006) (#19027)
|
||||
* Fix update hint bug (#18996) (#19002)
|
||||
* MISC
|
||||
* Fix potential assignee query for repo (#18994) (#18999)
|
||||
|
||||
## [1.16.3](https://github.com/go-gitea/gitea/releases/tag/v1.16.3) - 2022-03-02
|
||||
|
||||
* SECURITY
|
||||
* Git backend ignore replace objects (#18979) (#18980)
|
||||
* ENHANCEMENTS
|
||||
* Adjust error for already locked db and prevent level db lock on malformed connstr (#18923) (#18938)
|
||||
* BUGFIXES
|
||||
* Set max text height to prevent overflow (#18862) (#18977)
|
||||
* Fix newAttachmentPaths deletion for DeleteRepository() (#18973) (#18974)
|
||||
* Accounts with WebAuthn only (no TOTP) now exist ... fix code to handle that case (#18897) (#18964)
|
||||
* Send 404 on `/{org}.gpg` (#18959) (#18962)
|
||||
* Fix admin user list pagination (#18957) (#18960)
|
||||
* Fix lfs management setting (#18947) (#18946)
|
||||
* Fix login with email panic when email is not exist (#18942)
|
||||
* Update go-org to v1.6.1 (#18932) (#18933)
|
||||
* Fix `<strong>` html in translation (#18929) (#18931)
|
||||
* Fix page and missing return on unadopted repos API (#18848) (#18927)
|
||||
* Allow adminstrator teams members to see other teams (#18918) (#18919)
|
||||
* Don't treat BOM escape sequence as hidden character. (#18909) (#18910)
|
||||
* Correctly link URLs to users/repos with dashes, dots or underscores (… (#18908)
|
||||
* Fix redirect when using lowercase repo name (#18775) (#18902)
|
||||
* Fix migration v210 (#18893) (#18892)
|
||||
* Fix team management UI (#18887) (18886)
|
||||
* BeforeSourcePath should point to base commit (#18880) (#18799)
|
||||
* TRANSLATION
|
||||
* Backport locales from master (#18944)
|
||||
* MISC
|
||||
* Don't update email for organisation (#18905) (#18906)
|
||||
|
||||
## [1.16.2](https://github.com/go-gitea/gitea/releases/tag/v1.16.2) - 2022-02-24
|
||||
|
||||
* ENHANCEMENTS
|
||||
|
||||
+2
-4
@@ -1,7 +1,5 @@
|
||||
|
||||
###################################
|
||||
#Build stage - temporarily using techknowlogick image until we upgrade to latest official alpine/go image
|
||||
FROM techknowlogick/go:1.17-alpine3.13 AS build-env
|
||||
#Build stage
|
||||
FROM golang:1.18-alpine3.15 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY ${GOPROXY:-direct}
|
||||
|
||||
+2
-4
@@ -1,7 +1,5 @@
|
||||
|
||||
###################################
|
||||
#Build stage - temporarily using techknowlogick image until we upgrade to latest official alpine/go image
|
||||
FROM techknowlogick/go:1.17-alpine3.13 AS build-env
|
||||
#Build stage
|
||||
FROM golang:1.18-alpine3.15 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY ${GOPROXY:-direct}
|
||||
|
||||
@@ -24,10 +24,19 @@ SHASUM ?= shasum -a 256
|
||||
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
||||
COMMA := ,
|
||||
|
||||
XGO_VERSION := go-1.17.x
|
||||
MIN_GO_VERSION := 001016000
|
||||
XGO_VERSION := go-1.18.x
|
||||
MIN_GO_VERSION := 001017000
|
||||
MIN_NODE_VERSION := 012017000
|
||||
MIN_GOLANGCI_LINT_VERSION := 001043000
|
||||
|
||||
AIR_PACKAGE ?= github.com/cosmtrek/air@v1.29.0
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.4.0
|
||||
ERRCHECK_PACKAGE ?= github.com/kisielk/errcheck@v1.6.0
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.3.0
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.44.2
|
||||
GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.10
|
||||
MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.29.0
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
|
||||
DOCKER_IMAGE ?= gitea/gitea
|
||||
DOCKER_TAG ?= latest
|
||||
@@ -125,8 +134,6 @@ ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
|
||||
GO_SOURCES += $(BINDATA_DEST)
|
||||
endif
|
||||
|
||||
#To update swagger use: GO111MODULE=on go get -u github.com/go-swagger/go-swagger/cmd/swagger
|
||||
SWAGGER := $(GO) run github.com/go-swagger/go-swagger/cmd/swagger
|
||||
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
||||
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|g
|
||||
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g
|
||||
@@ -234,8 +241,8 @@ clean:
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@echo "Running gitea-fmt(with gofmt)..."
|
||||
@$(GO) run build/code-batch-process.go gitea-fmt -s -w '{file-list}'
|
||||
@echo "Running gitea-fmt (with gofumpt)..."
|
||||
@MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
@@ -254,7 +261,7 @@ endif
|
||||
|
||||
.PHONY: generate-swagger
|
||||
generate-swagger:
|
||||
$(SWAGGER) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
||||
$(GO) run $(SWAGGER_PACKAGE) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
||||
|
||||
@@ -270,21 +277,18 @@ swagger-check: generate-swagger
|
||||
.PHONY: swagger-validate
|
||||
swagger-validate:
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
|
||||
$(SWAGGER) validate './$(SWAGGER_SPEC)'
|
||||
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
|
||||
.PHONY: errcheck
|
||||
errcheck:
|
||||
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/kisielk/errcheck@8ddee489636a8311a376fc92e27a6a13c6658344; \
|
||||
fi
|
||||
@echo "Running errcheck..."
|
||||
@errcheck $(GO_PACKAGES)
|
||||
$(GO) run $(ERRCHECK_PACKAGE) $(GO_PACKAGES)
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check:
|
||||
# get all go files and run gitea-fmt (with gofmt) on them
|
||||
@diff=$$($(GO) run build/code-batch-process.go gitea-fmt -s -d '{file-list}'); \
|
||||
@diff=$$(MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -l '{file-list}'); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
@@ -323,10 +327,7 @@ watch-frontend: node-check node_modules
|
||||
|
||||
.PHONY: watch-backend
|
||||
watch-backend: go-check
|
||||
@hash air > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/cosmtrek/air@bedc18201271882c2be66d216d0e1a275b526ec4; \
|
||||
fi
|
||||
air -c .air.toml
|
||||
$(GO) run $(AIR_PACKAGE) -c .air.toml
|
||||
|
||||
.PHONY: test
|
||||
test: test-frontend test-backend
|
||||
@@ -599,12 +600,9 @@ $(DIST_DIRS):
|
||||
|
||||
.PHONY: release-windows
|
||||
release-windows: | $(DIST_DIRS)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
ifeq (,$(findstring gogit,$(TAGS)))
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
||||
endif
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
@@ -612,20 +610,14 @@ endif
|
||||
|
||||
.PHONY: release-linux
|
||||
release-linux: | $(DIST_DIRS)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-darwin
|
||||
release-darwin: | $(DIST_DIRS)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
@@ -640,10 +632,7 @@ release-check: | $(DIST_DIRS)
|
||||
|
||||
.PHONY: release-compress
|
||||
release-compress: | $(DIST_DIRS)
|
||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/ulikunitz/xz/cmd/gxz@v0.5.10; \
|
||||
fi
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && $(GO) run $(GXZ_PAGAGE) -k -9 $${file}; done;
|
||||
|
||||
.PHONY: release-sources
|
||||
release-sources: | $(DIST_DIRS)
|
||||
@@ -673,6 +662,15 @@ deps-frontend: node_modules
|
||||
.PHONY: deps-backend
|
||||
deps-backend:
|
||||
$(GO) mod download
|
||||
$(GO) install $(AIR_PACKAGE)
|
||||
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE)
|
||||
$(GO) install $(ERRCHECK_PACKAGE)
|
||||
$(GO) install $(GOFUMPT_PACKAGE)
|
||||
$(GO) install $(GOLANGCI_LINT_PACKAGE)
|
||||
$(GO) install $(GXZ_PAGAGE)
|
||||
$(GO) install $(MISSPELL_PACKAGE)
|
||||
$(GO) install $(SWAGGER_PACKAGE)
|
||||
$(GO) install $(XGO_PACKAGE)
|
||||
|
||||
node_modules: package-lock.json
|
||||
npm install --no-save
|
||||
@@ -766,22 +764,19 @@ pr\#%: clean-all
|
||||
$(GO) run contrib/pr/checkout.go $*
|
||||
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint: golangci-lint-check
|
||||
golangci-lint run --timeout 10m
|
||||
golangci-lint:
|
||||
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
|
||||
|
||||
.PHONY: golangci-lint-check
|
||||
golangci-lint-check:
|
||||
$(eval GOLANGCI_LINT_VERSION := $(shell printf "%03d%03d%03d" $(shell golangci-lint --version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');))
|
||||
$(eval MIN_GOLANGCI_LINT_VER_FMT := $(shell printf "%g.%g.%g" $(shell echo $(MIN_GOLANGCI_LINT_VERSION) | grep -o ...)))
|
||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
echo "Downloading golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \
|
||||
export BINARY="golangci-lint"; \
|
||||
curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \
|
||||
elif [ "$(GOLANGCI_LINT_VERSION)" -lt "$(MIN_GOLANGCI_LINT_VERSION)" ]; then \
|
||||
echo "Downloading newer version of golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \
|
||||
export BINARY="golangci-lint"; \
|
||||
curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \
|
||||
fi
|
||||
# workaround step for the lint-backend-windows CI task because 'go run' can not
|
||||
# have distinct GOOS/GOARCH for its build and run steps
|
||||
.PHONY: golangci-lint-windows
|
||||
golangci-lint-windows:
|
||||
@GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
|
||||
golangci-lint run
|
||||
|
||||
.PHONY: editorconfig-checker
|
||||
editorconfig-checker:
|
||||
$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
|
||||
@@ -73,7 +73,7 @@ or if SQLite support is required:
|
||||
|
||||
The `build` target is split into two sub-targets:
|
||||
|
||||
- `make backend` which requires [Go 1.16](https://golang.org/dl/) or greater.
|
||||
- `make backend` which requires [Go 1.17](https://go.dev/dl/) or greater.
|
||||
- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies.
|
||||
|
||||
When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js and Internet connectivity.
|
||||
|
||||
@@ -40,7 +40,7 @@ func passThroughCmd(cmd string, args []string) error {
|
||||
}
|
||||
c := exec.Cmd{
|
||||
Path: foundCmd,
|
||||
Args: args,
|
||||
Args: append([]string{cmd}, args...),
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
@@ -270,9 +270,10 @@ func main() {
|
||||
if containsString(subArgs, "-w") {
|
||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files))
|
||||
}
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("gofmt", substArgs))
|
||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-l"), containsString(subArgs, "-w")))
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", "1.17"}, substArgs...)))
|
||||
case "misspell":
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("misspell", substArgs))
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("MISSPELL_PACKAGE")}, substArgs...)))
|
||||
default:
|
||||
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
|
||||
}
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ func argsSet(c *cli.Context, args ...string) error {
|
||||
return errors.New(a + " is not set")
|
||||
}
|
||||
|
||||
if util.IsEmptyString(a) {
|
||||
if util.IsEmptyString(c.String(a)) {
|
||||
return errors.New(a + " is required")
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -185,7 +185,7 @@ Gitea or set your environment appropriately.`, "")
|
||||
reponame := os.Getenv(models.EnvRepoName)
|
||||
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||
prID, _ := strconv.ParseInt(os.Getenv(models.EnvPRID), 10, 64)
|
||||
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
|
||||
deployKeyID, _ := strconv.ParseInt(os.Getenv(models.EnvDeployKeyID), 10, 64)
|
||||
|
||||
hookOptions := private.HookOptions{
|
||||
UserID: userID,
|
||||
@@ -194,7 +194,7 @@ Gitea or set your environment appropriately.`, "")
|
||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||
GitPushOptions: pushOptions(),
|
||||
PullRequestID: prID,
|
||||
IsDeployKey: isDeployKey,
|
||||
DeployKeyID: deployKeyID,
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
||||
+1
-1
@@ -243,7 +243,7 @@ func runServ(c *cli.Context) error {
|
||||
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
|
||||
os.Setenv(models.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
|
||||
os.Setenv(models.EnvPRID, fmt.Sprintf("%d", 0))
|
||||
os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey))
|
||||
os.Setenv(models.EnvDeployKeyID, fmt.Sprintf("%d", results.DeployKeyID))
|
||||
os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
|
||||
os.Setenv(models.EnvAppURL, setting.AppURL)
|
||||
|
||||
|
||||
+3
-3
@@ -18,9 +18,9 @@ params:
|
||||
description: Git with a cup of tea
|
||||
author: The Gitea Authors
|
||||
website: https://docs.gitea.io
|
||||
version: 1.16.0
|
||||
minGoVersion: 1.16
|
||||
goVersion: 1.17
|
||||
version: 1.16.4
|
||||
minGoVersion: 1.17
|
||||
goVersion: 1.18
|
||||
minNodeVersion: 12.17
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -42,7 +42,7 @@ To maintain understandable code and avoid circular dependencies it is important
|
||||
- `modules/setting`: Store all system configurations read from ini files and has been referenced by everywhere. But they should be used as function parameters when possible.
|
||||
- `modules/git`: Package to interactive with `Git` command line or Gogit package.
|
||||
- `public`: Compiled frontend files (javascript, images, css, etc.)
|
||||
- `routers`: Handling of server requests. As it uses other Gitea packages to serve the request, other packages (models, modules or services) shall not depend on routers.
|
||||
- `routers`: Handling of server requests. As it uses other Gitea packages to serve the request, other packages (models, modules or services) must not depend on routers.
|
||||
- `routers/api` Contains routers for `/api/v1` aims to handle RESTful API requests.
|
||||
- `routers/install` Could only respond when system is in INSTALL mode (INSTALL_LOCK=false).
|
||||
- `routers/private` will only be invoked by internal sub commands, especially `serv` and `hooks`.
|
||||
@@ -106,10 +106,20 @@ i.e. `servcies/user`, `models/repository`.
|
||||
Since there are some packages which use the same package name, it is possible that you find packages like `modules/user`, `models/user`, and `services/user`. When these packages are imported in one Go file, it's difficult to know which package we are using and if it's a variable name or an import name. So, we always recommend to use import aliases. To differ from package variables which are commonly in camelCase, just use **snake_case** for import aliases.
|
||||
i.e. `import user_service "code.gitea.io/gitea/services/user"`
|
||||
|
||||
### Important Gotchas
|
||||
|
||||
- Never write `x.Update(exemplar)` without an explicit `WHERE` clause:
|
||||
- This will cause all rows in the table to be updated with the non-zero values of the exemplar - including IDs.
|
||||
- You should usually write `x.ID(id).Update(exemplar)`.
|
||||
- If during a migration you are inserting into a table using `x.Insert(exemplar)` where the ID is preset:
|
||||
- You will need to ``SET IDENTITY_INSERT `table` ON`` for the MSSQL variant (the migration will fail otherwise)
|
||||
- However, you will also need to update the id sequence for postgres - the migration will silently pass here but later insertions will fail:
|
||||
``SELECT setval('table_name_id_seq', COALESCE((SELECT MAX(id)+1 FROM `table_name`), 1), false)``
|
||||
|
||||
### Future Tasks
|
||||
|
||||
Currently, we are creating some refactors to do the following things:
|
||||
|
||||
- Correct that codes which doesn't follow the rules.
|
||||
- There are too many files in `models`, so we are moving some of them into a sub package `models/xxx`.
|
||||
- Some `modules` sub packages should be moved to `services` because they depends on `models`.
|
||||
- Some `modules` sub packages should be moved to `services` because they depend on `models`.
|
||||
|
||||
@@ -1,33 +1,24 @@
|
||||
module code.gitea.io/gitea
|
||||
|
||||
go 1.16
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.78.0 // indirect
|
||||
cloud.google.com/go v0.99.0 // indirect
|
||||
code.gitea.io/gitea-vet v0.2.1
|
||||
code.gitea.io/sdk/gitea v0.15.1
|
||||
gitea.com/go-chi/binding v0.0.0-20211013065440-d16dc407c2be
|
||||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb
|
||||
gitea.com/go-chi/cache v0.0.0-20211013020926-78790b11abf1
|
||||
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5
|
||||
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8
|
||||
gitea.com/lunny/levelqueue v0.4.1
|
||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210705153151-cc34b1f6908b // indirect
|
||||
github.com/PuerkitoBio/goquery v1.7.0
|
||||
github.com/alecthomas/chroma v0.9.4
|
||||
github.com/andybalholm/brotli v1.0.3 // indirect
|
||||
github.com/andybalholm/cascadia v1.2.0 // indirect
|
||||
github.com/blevesearch/bleve/v2 v2.3.0
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
||||
github.com/caddyserver/certmagic v0.15.2
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
github.com/alecthomas/chroma v0.10.0
|
||||
github.com/blevesearch/bleve/v2 v2.3.1
|
||||
github.com/caddyserver/certmagic v0.15.4
|
||||
github.com/chi-middleware/proxy v1.1.1
|
||||
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect
|
||||
github.com/couchbase/gomemcached v0.1.2 // indirect
|
||||
github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.10.0
|
||||
github.com/denisenkom/go-mssqldb v0.12.0
|
||||
github.com/djherbis/buffer v1.2.0
|
||||
github.com/djherbis/nio/v3 v3.0.1
|
||||
github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951
|
||||
@@ -36,8 +27,7 @@ require (
|
||||
github.com/emirpasic/gods v1.12.0
|
||||
github.com/ethantkoenig/rupture v1.0.0
|
||||
github.com/gliderlabs/ssh v0.3.3
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.4
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
github.com/go-chi/cors v1.2.0
|
||||
github.com/go-enry/go-enry/v2 v2.7.1
|
||||
github.com/go-git/go-billy/v5 v5.3.1
|
||||
@@ -51,87 +41,66 @@ require (
|
||||
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28
|
||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.3.0
|
||||
github.com/google/go-github/v39 v39.2.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/feeds v1.1.1
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/hashicorp/go-version v1.3.1
|
||||
github.com/hashicorp/go-version v1.4.0
|
||||
github.com/hashicorp/golang-lru v0.5.4
|
||||
github.com/huandu/xstrings v1.3.2
|
||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7
|
||||
github.com/json-iterator/go v1.1.11
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/kevinburke/ssh_config v1.1.0 // indirect
|
||||
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
|
||||
github.com/klauspost/compress v1.13.1
|
||||
github.com/klauspost/compress v1.13.6
|
||||
github.com/klauspost/cpuid/v2 v2.0.9
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/lib/pq v1.10.2
|
||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||
github.com/markbates/goth v1.68.0
|
||||
github.com/mattn/go-isatty v0.0.13
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.8
|
||||
github.com/mholt/archiver/v3 v3.5.0
|
||||
github.com/microcosm-cc/bluemonday v1.0.16
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.12
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
github.com/msteinert/pam v0.0.0-20201130170657-e61372126161
|
||||
github.com/markbates/goth v1.69.0
|
||||
github.com/mattn/go-isatty v0.0.14
|
||||
github.com/mattn/go-sqlite3 v1.14.12
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.18
|
||||
github.com/minio/minio-go/v7 v7.0.23
|
||||
github.com/msteinert/pam v1.0.0
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
github.com/niklasfasching/go-org v1.6.0
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/niklasfasching/go-org v1.6.2
|
||||
github.com/oliamb/cutter v0.2.2
|
||||
github.com/olivere/elastic/v7 v7.0.25
|
||||
github.com/pelletier/go-toml v1.9.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.8 // indirect
|
||||
github.com/olivere/elastic/v7 v7.0.31
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/quasoft/websspi v1.0.0
|
||||
github.com/rs/xid v1.3.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1
|
||||
github.com/quasoft/websspi v1.1.2
|
||||
github.com/sergi/go-diff v1.2.0
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/tstranex/u2f v1.0.0
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/unknwon/com v1.0.1
|
||||
github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44
|
||||
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae
|
||||
github.com/unrolled/render v1.4.0
|
||||
github.com/urfave/cli v1.22.5
|
||||
github.com/xanzy/go-gitlab v0.50.1
|
||||
github.com/xanzy/go-gitlab v0.58.0
|
||||
github.com/yohcop/openid-go v1.0.0
|
||||
github.com/yuin/goldmark v1.4.0
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20210516132338-9216f9c5aa01
|
||||
github.com/yuin/goldmark-meta v1.0.0
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
github.com/yuin/goldmark v1.4.8
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
|
||||
github.com/yuin/goldmark-meta v1.1.0
|
||||
go.jolheiser.com/hcaptcha v0.0.4
|
||||
go.jolheiser.com/pwn v0.0.3
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
go.uber.org/zap v1.19.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9
|
||||
golang.org/x/text v0.3.7
|
||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
|
||||
golang.org/x/tools v0.1.0
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
golang.org/x/tools v0.1.9
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.62.0
|
||||
gopkg.in/ini.v1 v1.66.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
mvdan.cc/xurls/v2 v2.2.0
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
||||
@@ -139,6 +108,180 @@ require (
|
||||
xorm.io/xorm v1.2.5
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/RoaringBitmap/roaring v0.9.4 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.2.1 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.0.1 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.3 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.1.0 // indirect
|
||||
github.com/blevesearch/segment v0.9.0 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.1 // indirect
|
||||
github.com/blevesearch/vellum v1.0.7 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.3.3 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.3.3 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.3.3 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.3.3 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.3 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cloudflare/cfssl v1.6.1 // indirect
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect
|
||||
github.com/couchbase/gomemcached v0.1.2 // indirect
|
||||
github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/envoyproxy/go-control-plane v0.10.1 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.2 // indirect
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/fullstorydev/grpcurl v1.8.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-openapi/analysis v0.21.2 // indirect
|
||||
github.com/go-openapi/errors v0.20.2 // indirect
|
||||
github.com/go-openapi/inflect v0.19.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/loads v0.21.0 // indirect
|
||||
github.com/go-openapi/runtime v0.21.1 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.1 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-openapi/validate v0.20.3 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/goccy/go-json v0.9.5 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/certificate-transparency-go v1.1.2-0.20210511102531-373a877eec92 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/gorilla/handlers v1.5.1 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jessevdk/go-flags v1.5.0 // indirect
|
||||
github.com/jhump/protoreflect v1.8.2 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.1.0 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/markbates/going v1.0.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mholt/acmez v1.0.2 // indirect
|
||||
github.com/miekg/dns v1.1.46 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.1 // indirect
|
||||
github.com/rs/xid v1.3.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/soheilhy/cmux v0.1.5 // indirect
|
||||
github.com/spf13/afero v1.8.0 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/cobra v1.3.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.10.1 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/steveyen/gtreap v0.1.0 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
|
||||
github.com/toqueteos/webbrowser v1.2.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.1 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.1 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
|
||||
go.etcd.io/etcd/client/v2 v2.305.1 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/etcdctl/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/server/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/tests/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/v3 v3.5.0-alpha.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.8.2 // indirect
|
||||
golang.org/x/mod v0.5.1 // indirect
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
|
||||
google.golang.org/grpc v1.43.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
||||
|
||||
replace github.com/markbates/goth v1.68.0 => github.com/zeripath/goth v1.68.1-0.20220109111530-754359885dce
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestAPIPrivateServ(t *testing.T) {
|
||||
results, err := private.ServCommand(ctx, 1, "user2", "repo1", perm.AccessModeWrite, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.False(t, results.IsDeployKey)
|
||||
assert.Zero(t, results.DeployKeyID)
|
||||
assert.Equal(t, int64(1), results.KeyID)
|
||||
assert.Equal(t, "user2@localhost", results.KeyName)
|
||||
assert.Equal(t, "user2", results.UserName)
|
||||
@@ -70,7 +70,7 @@ func TestAPIPrivateServ(t *testing.T) {
|
||||
results, err = private.ServCommand(ctx, 1, "user15", "big_test_public_1", perm.AccessModeRead, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.False(t, results.IsDeployKey)
|
||||
assert.Zero(t, results.DeployKeyID)
|
||||
assert.Equal(t, int64(1), results.KeyID)
|
||||
assert.Equal(t, "user2@localhost", results.KeyName)
|
||||
assert.Equal(t, "user2", results.UserName)
|
||||
@@ -92,7 +92,7 @@ func TestAPIPrivateServ(t *testing.T) {
|
||||
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", perm.AccessModeRead, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.True(t, results.IsDeployKey)
|
||||
assert.NotZero(t, results.DeployKeyID)
|
||||
assert.Equal(t, deployKey.KeyID, results.KeyID)
|
||||
assert.Equal(t, "test-deploy", results.KeyName)
|
||||
assert.Equal(t, "user15", results.UserName)
|
||||
@@ -129,7 +129,7 @@ func TestAPIPrivateServ(t *testing.T) {
|
||||
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", perm.AccessModeRead, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.True(t, results.IsDeployKey)
|
||||
assert.NotZero(t, results.DeployKeyID)
|
||||
assert.Equal(t, deployKey.KeyID, results.KeyID)
|
||||
assert.Equal(t, "test-deploy", results.KeyName)
|
||||
assert.Equal(t, "user15", results.UserName)
|
||||
@@ -142,7 +142,7 @@ func TestAPIPrivateServ(t *testing.T) {
|
||||
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", perm.AccessModeWrite, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.True(t, results.IsDeployKey)
|
||||
assert.NotZero(t, results.DeployKeyID)
|
||||
assert.Equal(t, deployKey.KeyID, results.KeyID)
|
||||
assert.Equal(t, "test-deploy", results.KeyName)
|
||||
assert.Equal(t, "user15", results.UserName)
|
||||
|
||||
@@ -445,7 +445,6 @@ func TestAPIRepoTransfer(t *testing.T) {
|
||||
expectedStatus int
|
||||
}{
|
||||
// Disclaimer for test story: "user1" is an admin, "user2" is normal user and part of in owner team of org "user3"
|
||||
|
||||
// Transfer to a user with teams in another org should fail
|
||||
{ctxUserID: 1, newOwner: "user3", teams: &[]int64{5}, expectedStatus: http.StatusForbidden},
|
||||
// Transfer to a user with non-existent team IDs should fail
|
||||
|
||||
@@ -58,7 +58,7 @@ func (key *DeployKey) GetContent() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsReadOnly checks if the key can only be used for read operations
|
||||
// IsReadOnly checks if the key can only be used for read operations, used by template
|
||||
func (key *DeployKey) IsReadOnly() bool {
|
||||
return key.Mode == perm.AccessModeRead
|
||||
}
|
||||
@@ -203,12 +203,6 @@ func UpdateDeployKeyCols(key *DeployKey, cols ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateDeployKey updates deploy key information.
|
||||
func UpdateDeployKey(key *DeployKey) error {
|
||||
_, err := db.GetEngine(db.DefaultContext).ID(key.ID).AllCols().Update(key)
|
||||
return err
|
||||
}
|
||||
|
||||
// ListDeployKeysOptions are options for ListDeployKeys
|
||||
type ListDeployKeysOptions struct {
|
||||
db.ListOptions
|
||||
|
||||
@@ -23,8 +23,8 @@ const (
|
||||
EnvPusherName = "GITEA_PUSHER_NAME"
|
||||
EnvPusherEmail = "GITEA_PUSHER_EMAIL"
|
||||
EnvPusherID = "GITEA_PUSHER_ID"
|
||||
EnvKeyID = "GITEA_KEY_ID"
|
||||
EnvIsDeployKey = "GITEA_IS_DEPLOY_KEY"
|
||||
EnvKeyID = "GITEA_KEY_ID" // public key ID
|
||||
EnvDeployKeyID = "GITEA_DEPLOY_KEY_ID"
|
||||
EnvPRID = "GITEA_PR_ID"
|
||||
EnvIsInternal = "GITEA_INTERNAL_PUSH"
|
||||
EnvAppURL = "GITEA_ROOT_URL"
|
||||
|
||||
+6
-1
@@ -1551,6 +1551,7 @@ const (
|
||||
FilterModeCreate
|
||||
FilterModeMention
|
||||
FilterModeReviewRequested
|
||||
FilterModeYourRepositories
|
||||
)
|
||||
|
||||
func parseCountResult(results []map[string][]byte) int64 {
|
||||
@@ -1695,6 +1696,7 @@ type UserIssueStatsOptions struct {
|
||||
IssueIDs []int64
|
||||
IsArchived util.OptionalBool
|
||||
LabelIDs []int64
|
||||
RepoCond builder.Cond
|
||||
Org *Organization
|
||||
Team *Team
|
||||
}
|
||||
@@ -1712,6 +1714,9 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
|
||||
if len(opts.IssueIDs) > 0 {
|
||||
cond = cond.And(builder.In("issue.id", opts.IssueIDs))
|
||||
}
|
||||
if opts.RepoCond != nil {
|
||||
cond = cond.And(opts.RepoCond)
|
||||
}
|
||||
|
||||
if opts.UserID > 0 {
|
||||
cond = cond.And(issuePullAccessibleRepoCond("issue.repo_id", opts.UserID, opts.Org, opts.Team, opts.IsPull))
|
||||
@@ -1733,7 +1738,7 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
|
||||
}
|
||||
|
||||
switch opts.FilterMode {
|
||||
case FilterModeAll:
|
||||
case FilterModeAll, FilterModeYourRepositories:
|
||||
stats.OpenCount, err = sess(cond).
|
||||
And("issue.is_closed = ?", false).
|
||||
Count(new(Issue))
|
||||
|
||||
+3
-2
@@ -193,12 +193,13 @@ func LFSAutoAssociate(metas []*LFSMetaObject, user *user_model.User, repoID int6
|
||||
// admin can associate any LFS object to any repository, and we do not care about errors (eg: duplicated unique key),
|
||||
// even if error occurs, it won't hurt users and won't make things worse
|
||||
for i := range metas {
|
||||
p := lfs.Pointer{Oid: metas[i].Oid, Size: metas[i].Size}
|
||||
_, err = sess.Insert(&LFSMetaObject{
|
||||
Pointer: lfs.Pointer{Oid: metas[i].Oid, Size: metas[i].Size},
|
||||
Pointer: p,
|
||||
RepositoryID: repoID,
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn("failed to insert LFS meta object into database, err=%v", err)
|
||||
log.Warn("failed to insert LFS meta object %-v for repo_id: %d into database, err=%v", p, repoID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,6 @@ type Version struct {
|
||||
// update minDBVersion accordingly
|
||||
var migrations = []Migration{
|
||||
// Gitea 1.5.0 ends at v69
|
||||
|
||||
// v70 -> v71
|
||||
NewMigration("add issue_dependencies", addIssueDependencies),
|
||||
// v71 -> v72
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"github.com/tstranex/u2f"
|
||||
|
||||
"github.com/tstranex/u2f"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
@@ -137,17 +137,23 @@ func remigrateU2FCredentials(x *xorm.Engine) error {
|
||||
CreatedUnix: reg.CreatedUnix,
|
||||
}
|
||||
|
||||
has, err := sess.ID(reg.ID).Where("id = ?", reg.ID).Get(new(webauthnCredential))
|
||||
has, err := sess.ID(reg.ID).Get(new(webauthnCredential))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get webauthn_credential[%d]. Error: %w", reg.ID, err)
|
||||
}
|
||||
if !has {
|
||||
_, err = sess.Insert(remigrated)
|
||||
has, err := sess.Where("`lower_name`=?", remigrated.LowerName).And("`user_id`=?", remigrated.UserID).Exist(new(webauthnCredential))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to (re)insert webauthn_credential[%d]. Error: %w", reg.ID, err)
|
||||
return fmt.Errorf("unable to check webauthn_credential[lower_name: %s, user_id:%v]. Error: %w", remigrated.LowerName, remigrated.UserID, err)
|
||||
}
|
||||
if !has {
|
||||
_, err = sess.Insert(remigrated)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to (re)insert webauthn_credential[%d]. Error: %w", reg.ID, err)
|
||||
}
|
||||
|
||||
continue
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
_, err = sess.ID(remigrated.ID).AllCols().Update(remigrated)
|
||||
@@ -168,5 +174,11 @@ func remigrateU2FCredentials(x *xorm.Engine) error {
|
||||
regs = regs[:0]
|
||||
}
|
||||
|
||||
if x.Dialect().URI().DBType == schemas.POSTGRES {
|
||||
if _, err := x.Exec("SELECT setval('webauthn_credential_id_seq', COALESCE((SELECT MAX(id)+1 FROM `webauthn_credential`), 1), false)"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
@@ -498,14 +498,15 @@ func (n *Notification) APIURL() string {
|
||||
type NotificationList []*Notification
|
||||
|
||||
// LoadAttributes load Repo Issue User and Comment if not loaded
|
||||
func (nl NotificationList) LoadAttributes() (err error) {
|
||||
func (nl NotificationList) LoadAttributes() error {
|
||||
var err error
|
||||
for i := 0; i < len(nl); i++ {
|
||||
err = nl[i].LoadAttributes()
|
||||
if err != nil && !IsErrCommentNotExist(err) {
|
||||
return
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nl NotificationList) getPendingRepoIDs() []int64 {
|
||||
|
||||
+40
-2
@@ -19,6 +19,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@@ -776,8 +777,45 @@ func DeleteTeam(t *Team) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := t.removeAllRepositories(ctx); err != nil {
|
||||
return err
|
||||
// update branch protections
|
||||
{
|
||||
protections := make([]*ProtectedBranch, 0, 10)
|
||||
err := sess.In("repo_id",
|
||||
builder.Select("id").From("repository").Where(builder.Eq{"owner_id": t.OrgID})).
|
||||
Find(&protections)
|
||||
if err != nil {
|
||||
return fmt.Errorf("findProtectedBranches: %v", err)
|
||||
}
|
||||
for _, p := range protections {
|
||||
var matched1, matched2, matched3 bool
|
||||
if len(p.WhitelistTeamIDs) != 0 {
|
||||
p.WhitelistTeamIDs, matched1 = util.RemoveIDFromList(
|
||||
p.WhitelistTeamIDs, t.ID)
|
||||
}
|
||||
if len(p.ApprovalsWhitelistTeamIDs) != 0 {
|
||||
p.ApprovalsWhitelistTeamIDs, matched2 = util.RemoveIDFromList(
|
||||
p.ApprovalsWhitelistTeamIDs, t.ID)
|
||||
}
|
||||
if len(p.MergeWhitelistTeamIDs) != 0 {
|
||||
p.MergeWhitelistTeamIDs, matched3 = util.RemoveIDFromList(
|
||||
p.MergeWhitelistTeamIDs, t.ID)
|
||||
}
|
||||
if matched1 || matched2 || matched3 {
|
||||
if _, err = sess.ID(p.ID).Cols(
|
||||
"whitelist_team_i_ds",
|
||||
"merge_whitelist_team_i_ds",
|
||||
"approvals_whitelist_team_i_ds",
|
||||
).Update(p); err != nil {
|
||||
return fmt.Errorf("updateProtectedBranches: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !t.IncludesAllRepositories {
|
||||
if err := t.removeAllRepositories(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete team-user.
|
||||
|
||||
+2
-2
@@ -153,7 +153,7 @@ func getRepoAssignees(ctx context.Context, repo *repo_model.Repository) (_ []*us
|
||||
userIDs := make([]int64, 0, 10)
|
||||
if err = e.Table("access").
|
||||
Where("repo_id = ? AND mode >= ?", repo.ID, perm.AccessModeWrite).
|
||||
Select("id").
|
||||
Select("user_id").
|
||||
Find(&userIDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -977,7 +977,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
|
||||
|
||||
// Remove attachment with no issue_id and release_id.
|
||||
for i := range newAttachmentPaths {
|
||||
admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", attachmentPaths[i])
|
||||
admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", newAttachmentPaths[i])
|
||||
}
|
||||
|
||||
if len(repo.Avatar) > 0 {
|
||||
|
||||
@@ -167,3 +167,21 @@ func TestLinkedRepository(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoAssignees(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
|
||||
users, err := GetRepoAssignees(repo2)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, users, 1)
|
||||
assert.Equal(t, users[0].ID, int64(2))
|
||||
|
||||
repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 21}).(*repo_model.Repository)
|
||||
users, err = GetRepoAssignees(repo21)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, users, 3)
|
||||
assert.Equal(t, users[0].ID, int64(15))
|
||||
assert.Equal(t, users[1].ID, int64(18))
|
||||
assert.Equal(t, users[2].ID, int64(16))
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@@ -130,6 +131,50 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// ***** START: Branch Protections *****
|
||||
{
|
||||
const batchSize = 50
|
||||
for start := 0; ; start += batchSize {
|
||||
protections := make([]*ProtectedBranch, 0, batchSize)
|
||||
// @perf: We can't filter on DB side by u.ID, as those IDs are serialized as JSON strings.
|
||||
// We could filter down with `WHERE repo_id IN (reposWithPushPermission(u))`,
|
||||
// though that query will be quite complex and tricky to maintain (compare `getRepoAssignees()`).
|
||||
// Also, as we didn't update branch protections when removing entries from `access` table,
|
||||
// it's safer to iterate all protected branches.
|
||||
if err = e.Limit(batchSize, start).Find(&protections); err != nil {
|
||||
return fmt.Errorf("findProtectedBranches: %v", err)
|
||||
}
|
||||
if len(protections) == 0 {
|
||||
break
|
||||
}
|
||||
for _, p := range protections {
|
||||
var matched1, matched2, matched3 bool
|
||||
if len(p.WhitelistUserIDs) != 0 {
|
||||
p.WhitelistUserIDs, matched1 = util.RemoveIDFromList(
|
||||
p.WhitelistUserIDs, u.ID)
|
||||
}
|
||||
if len(p.ApprovalsWhitelistUserIDs) != 0 {
|
||||
p.ApprovalsWhitelistUserIDs, matched2 = util.RemoveIDFromList(
|
||||
p.ApprovalsWhitelistUserIDs, u.ID)
|
||||
}
|
||||
if len(p.MergeWhitelistUserIDs) != 0 {
|
||||
p.MergeWhitelistUserIDs, matched3 = util.RemoveIDFromList(
|
||||
p.MergeWhitelistUserIDs, u.ID)
|
||||
}
|
||||
if matched1 || matched2 || matched3 {
|
||||
if _, err = e.ID(p.ID).Cols(
|
||||
"whitelist_user_i_ds",
|
||||
"merge_whitelist_user_i_ds",
|
||||
"approvals_whitelist_user_i_ds",
|
||||
).Update(p); err != nil {
|
||||
return fmt.Errorf("updateProtectedBranches: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ***** END: Branch Protections *****
|
||||
|
||||
// ***** START: PublicKey *****
|
||||
if _, err = e.Delete(&asymkey_model.PublicKey{OwnerID: u.ID}); err != nil {
|
||||
return fmt.Errorf("deletePublicKeys: %v", err)
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
@@ -21,10 +22,23 @@ import (
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrEmailNotActivated e-mail address has not been activated error
|
||||
ErrEmailNotActivated = errors.New("E-mail address has not been activated")
|
||||
)
|
||||
// ErrEmailNotActivated e-mail address has not been activated error
|
||||
var ErrEmailNotActivated = errors.New("e-mail address has not been activated")
|
||||
|
||||
// ErrEmailCharIsNotSupported e-mail address contains unsupported character
|
||||
type ErrEmailCharIsNotSupported struct {
|
||||
Email string
|
||||
}
|
||||
|
||||
// IsErrEmailCharIsNotSupported checks if an error is an ErrEmailCharIsNotSupported
|
||||
func IsErrEmailCharIsNotSupported(err error) bool {
|
||||
_, ok := err.(ErrEmailCharIsNotSupported)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrEmailCharIsNotSupported) Error() string {
|
||||
return fmt.Sprintf("e-mail address contains unsupported character [email: %s]", err.Email)
|
||||
}
|
||||
|
||||
// ErrEmailInvalid represents an error where the email address does not comply with RFC 5322
|
||||
type ErrEmailInvalid struct {
|
||||
@@ -108,12 +122,24 @@ func (email *EmailAddress) BeforeInsert() {
|
||||
}
|
||||
}
|
||||
|
||||
var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
||||
|
||||
// ValidateEmail check if email is a allowed address
|
||||
func ValidateEmail(email string) error {
|
||||
if len(email) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !emailRegexp.MatchString(email) {
|
||||
return ErrEmailCharIsNotSupported{email}
|
||||
}
|
||||
|
||||
if !(email[0] >= 'a' && email[0] <= 'z') &&
|
||||
!(email[0] >= 'A' && email[0] <= 'Z') &&
|
||||
!(email[0] >= '0' && email[0] <= '9') {
|
||||
return ErrEmailInvalid{email}
|
||||
}
|
||||
|
||||
if _, err := mail.ParseAddress(email); err != nil {
|
||||
return ErrEmailInvalid{email}
|
||||
}
|
||||
|
||||
@@ -252,3 +252,58 @@ func TestListEmails(t *testing.T) {
|
||||
assert.Len(t, emails, 5)
|
||||
assert.Greater(t, count, int64(len(emails)))
|
||||
}
|
||||
|
||||
func TestEmailAddressValidate(t *testing.T) {
|
||||
kases := map[string]error{
|
||||
"abc@gmail.com": nil,
|
||||
"132@hotmail.com": nil,
|
||||
"1-3-2@test.org": nil,
|
||||
"1.3.2@test.org": nil,
|
||||
"a_123@test.org.cn": nil,
|
||||
`first.last@iana.org`: nil,
|
||||
`first!last@iana.org`: nil,
|
||||
`first#last@iana.org`: nil,
|
||||
`first$last@iana.org`: nil,
|
||||
`first%last@iana.org`: nil,
|
||||
`first&last@iana.org`: nil,
|
||||
`first'last@iana.org`: nil,
|
||||
`first*last@iana.org`: nil,
|
||||
`first+last@iana.org`: nil,
|
||||
`first/last@iana.org`: nil,
|
||||
`first=last@iana.org`: nil,
|
||||
`first?last@iana.org`: nil,
|
||||
`first^last@iana.org`: nil,
|
||||
"first`last@iana.org": nil,
|
||||
`first{last@iana.org`: nil,
|
||||
`first|last@iana.org`: nil,
|
||||
`first}last@iana.org`: nil,
|
||||
`first~last@iana.org`: nil,
|
||||
`first;last@iana.org`: ErrEmailCharIsNotSupported{`first;last@iana.org`},
|
||||
".233@qq.com": ErrEmailInvalid{".233@qq.com"},
|
||||
"!233@qq.com": ErrEmailInvalid{"!233@qq.com"},
|
||||
"#233@qq.com": ErrEmailInvalid{"#233@qq.com"},
|
||||
"$233@qq.com": ErrEmailInvalid{"$233@qq.com"},
|
||||
"%233@qq.com": ErrEmailInvalid{"%233@qq.com"},
|
||||
"&233@qq.com": ErrEmailInvalid{"&233@qq.com"},
|
||||
"'233@qq.com": ErrEmailInvalid{"'233@qq.com"},
|
||||
"*233@qq.com": ErrEmailInvalid{"*233@qq.com"},
|
||||
"+233@qq.com": ErrEmailInvalid{"+233@qq.com"},
|
||||
"/233@qq.com": ErrEmailInvalid{"/233@qq.com"},
|
||||
"=233@qq.com": ErrEmailInvalid{"=233@qq.com"},
|
||||
"?233@qq.com": ErrEmailInvalid{"?233@qq.com"},
|
||||
"^233@qq.com": ErrEmailInvalid{"^233@qq.com"},
|
||||
"`233@qq.com": ErrEmailInvalid{"`233@qq.com"},
|
||||
"{233@qq.com": ErrEmailInvalid{"{233@qq.com"},
|
||||
"|233@qq.com": ErrEmailInvalid{"|233@qq.com"},
|
||||
"}233@qq.com": ErrEmailInvalid{"}233@qq.com"},
|
||||
"~233@qq.com": ErrEmailInvalid{"~233@qq.com"},
|
||||
";233@qq.com": ErrEmailCharIsNotSupported{";233@qq.com"},
|
||||
"Foo <foo@bar.com>": ErrEmailCharIsNotSupported{"Foo <foo@bar.com>"},
|
||||
string([]byte{0xE2, 0x84, 0xAA}): ErrEmailCharIsNotSupported{string([]byte{0xE2, 0x84, 0xAA})},
|
||||
}
|
||||
for kase, err := range kases {
|
||||
t.Run(kase, func(t *testing.T) {
|
||||
assert.EqualValues(t, err, ValidateEmail(kase))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+20
-6
@@ -30,13 +30,19 @@ func (users UserList) GetTwoFaStatus() map[int64]bool {
|
||||
for _, user := range users {
|
||||
results[user.ID] = false // Set default to false
|
||||
}
|
||||
tokenMaps, err := users.loadTwoFactorStatus(db.GetEngine(db.DefaultContext))
|
||||
if err == nil {
|
||||
|
||||
if tokenMaps, err := users.loadTwoFactorStatus(db.GetEngine(db.DefaultContext)); err == nil {
|
||||
for _, token := range tokenMaps {
|
||||
results[token.UID] = true
|
||||
}
|
||||
}
|
||||
|
||||
if ids, err := users.userIDsWithWebAuthn(db.GetEngine(db.DefaultContext)); err == nil {
|
||||
for _, id := range ids {
|
||||
results[id] = true
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
@@ -47,15 +53,23 @@ func (users UserList) loadTwoFactorStatus(e db.Engine) (map[int64]*auth.TwoFacto
|
||||
|
||||
userIDs := users.GetUserIDs()
|
||||
tokenMaps := make(map[int64]*auth.TwoFactor, len(userIDs))
|
||||
err := e.
|
||||
In("uid", userIDs).
|
||||
Find(&tokenMaps)
|
||||
if err != nil {
|
||||
if err := e.In("uid", userIDs).Find(&tokenMaps); err != nil {
|
||||
return nil, fmt.Errorf("find two factor: %v", err)
|
||||
}
|
||||
return tokenMaps, nil
|
||||
}
|
||||
|
||||
func (users UserList) userIDsWithWebAuthn(e db.Engine) ([]int64, error) {
|
||||
if len(users) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
ids := make([]int64, 0, len(users))
|
||||
if err := e.Table(new(auth.WebAuthnCredential)).In("user_id", users.GetUserIDs()).Select("user_id").Distinct("user_id").Find(&ids); err != nil {
|
||||
return nil, fmt.Errorf("find two factor: %v", err)
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// GetUsersByIDs returns all resolved users from a list of Ids.
|
||||
func GetUsersByIDs(ids []int64) (UserList, error) {
|
||||
ous := make([]*User, 0, len(ids))
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
// SearchUserOptions contains the options for searching
|
||||
type SearchUserOptions struct {
|
||||
db.ListOptions
|
||||
|
||||
Keyword string
|
||||
Type UserType
|
||||
UID int64
|
||||
@@ -33,6 +34,8 @@ type SearchUserOptions struct {
|
||||
IsRestricted util.OptionalBool
|
||||
IsTwoFactorEnabled util.OptionalBool
|
||||
IsProhibitLogin util.OptionalBool
|
||||
|
||||
ExtraParamStrings map[string]string
|
||||
}
|
||||
|
||||
func (opts *SearchUserOptions) toSearchQueryBase() *xorm.Session {
|
||||
|
||||
+10
-6
@@ -644,6 +644,15 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e
|
||||
u.Visibility = overwriteDefault[0].Visibility
|
||||
}
|
||||
|
||||
// validate data
|
||||
if err := validateUser(u); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ValidateEmail(u.Email); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, committer, err := db.TxContext()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -652,11 +661,6 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e
|
||||
|
||||
sess := db.GetEngine(ctx)
|
||||
|
||||
// validate data
|
||||
if err := validateUser(u); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isExist, err := isUserExist(sess, 0, u.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -861,7 +865,7 @@ func updateUser(ctx context.Context, u *User, changePrimaryEmail bool, cols ...s
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else { // check if primary email in email_address table
|
||||
} else if !u.IsOrganization() { // check if primary email in email_address table
|
||||
primaryEmailExist, err := e.Where("uid=? AND is_primary=?", u.ID, true).Exist(&EmailAddress{})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -232,7 +232,7 @@ func TestCreateUserInvalidEmail(t *testing.T) {
|
||||
|
||||
err := CreateUser(user)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrEmailInvalid(err))
|
||||
assert.True(t, IsErrEmailCharIsNotSupported(err))
|
||||
}
|
||||
|
||||
func TestCreateUserEmailAlreadyUsed(t *testing.T) {
|
||||
|
||||
@@ -35,6 +35,10 @@ func Auth(serviceName, userName, passwd string) (string, error) {
|
||||
if err = t.Authenticate(0); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = t.AcctMgmt(0); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// PAM login names might suffer transformations in the PAM stack.
|
||||
// We should take whatever the PAM stack returns for it.
|
||||
|
||||
@@ -63,6 +63,7 @@ func EscapeControlBytes(text []byte) (EscapeStatus, []byte) {
|
||||
func EscapeControlReader(text io.Reader, output io.Writer) (escaped EscapeStatus, err error) {
|
||||
buf := make([]byte, 4096)
|
||||
readStart := 0
|
||||
runeCount := 0
|
||||
var n int
|
||||
var writePos int
|
||||
|
||||
@@ -79,6 +80,8 @@ readingloop:
|
||||
|
||||
for i < len(bs) {
|
||||
r, size := utf8.DecodeRune(bs[i:])
|
||||
runeCount++
|
||||
|
||||
// Now handle the codepoints
|
||||
switch {
|
||||
case r == utf8.RuneError:
|
||||
@@ -113,6 +116,8 @@ readingloop:
|
||||
lineHasRTLScript = false
|
||||
lineHasLTRScript = false
|
||||
|
||||
case runeCount == 1 && r == 0xFEFF: // UTF BOM
|
||||
// the first BOM is safe
|
||||
case r == '\r' || r == '\t' || r == ' ':
|
||||
// These are acceptable control characters and space characters
|
||||
case unicode.IsSpace(r):
|
||||
@@ -144,7 +149,8 @@ readingloop:
|
||||
return
|
||||
}
|
||||
writePos = i + size
|
||||
case unicode.Is(unicode.C, r):
|
||||
// 65279 == BOM rune.
|
||||
case unicode.Is(unicode.C, r) && r != rune(65279):
|
||||
escaped.Escaped = true
|
||||
escaped.HasControls = true
|
||||
if writePos < i {
|
||||
|
||||
@@ -129,6 +129,14 @@ then resh (ר), and finally heh (ה) (which should appear leftmost).`,
|
||||
"\n" + `if access_level != "user<span class="escaped-code-point" data-escaped="[U+202E]"><span class="char">` + "\u202e" + `</span></span> <span class="escaped-code-point" data-escaped="[U+2066]"><span class="char">` + "\u2066" + `</span></span>// Check if admin<span class="escaped-code-point" data-escaped="[U+2069]"><span class="char">` + "\u2069" + `</span></span> <span class="escaped-code-point" data-escaped="[U+2066]"><span class="char">` + "\u2066" + `</span></span>" {` + "\n",
|
||||
status: EscapeStatus{Escaped: true, HasBIDI: true, BadBIDI: true, HasLTRScript: true, HasRTLScript: true},
|
||||
},
|
||||
{
|
||||
// UTF-8/16/32 all use the same codepoint for BOM
|
||||
// Gitea could read UTF-16/32 content and convert into UTF-8 internally then render it, so we only process UTF-8 internally
|
||||
name: "UTF BOM",
|
||||
text: "\xef\xbb\xbftest",
|
||||
result: "\xef\xbb\xbftest",
|
||||
status: EscapeStatus{HasLTRScript: true},
|
||||
},
|
||||
}
|
||||
|
||||
func TestEscapeControlString(t *testing.T) {
|
||||
@@ -163,10 +171,18 @@ func TestEscapeControlReader(t *testing.T) {
|
||||
// lets add some control characters to the tests
|
||||
tests := make([]escapeControlTest, 0, len(escapeControlTests)*3)
|
||||
copy(tests, escapeControlTests)
|
||||
|
||||
// if there is a BOM, we should keep the BOM
|
||||
addPrefix := func(prefix, s string) string {
|
||||
if strings.HasPrefix(s, "\xef\xbb\xbf") {
|
||||
return s[:3] + prefix + s[3:]
|
||||
}
|
||||
return prefix + s
|
||||
}
|
||||
for _, test := range escapeControlTests {
|
||||
test.name += " (+Control)"
|
||||
test.text = "\u001E" + test.text
|
||||
test.result = `<span class="escaped-code-point" data-escaped="[U+001E]"><span class="char">` + "\u001e" + `</span></span>` + test.result
|
||||
test.text = addPrefix("\u001E", test.text)
|
||||
test.result = addPrefix(`<span class="escaped-code-point" data-escaped="[U+001E]"><span class="char">`+"\u001e"+`</span></span>`, test.result)
|
||||
test.status.Escaped = true
|
||||
test.status.HasControls = true
|
||||
tests = append(tests, test)
|
||||
@@ -174,8 +190,8 @@ func TestEscapeControlReader(t *testing.T) {
|
||||
|
||||
for _, test := range escapeControlTests {
|
||||
test.name += " (+Mark)"
|
||||
test.text = "\u0300" + test.text
|
||||
test.result = `<span class="escaped-code-point" data-escaped="[U+0300]"><span class="char">` + "\u0300" + `</span></span>` + test.result
|
||||
test.text = addPrefix("\u0300", test.text)
|
||||
test.result = addPrefix(`<span class="escaped-code-point" data-escaped="[U+0300]"><span class="char">`+"\u0300"+`</span></span>`, test.result)
|
||||
test.status.Escaped = true
|
||||
test.status.HasMarks = true
|
||||
tests = append(tests, test)
|
||||
|
||||
@@ -181,6 +181,12 @@ func (ctx *Context) RedirectToFirst(location ...string) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Unfortunately browsers consider a redirect Location with preceding "//" and "/\" as meaning redirect to "http(s)://REST_OF_PATH"
|
||||
// Therefore we should ignore these redirect locations to prevent open redirects
|
||||
if len(loc) > 1 && loc[0] == '/' && (loc[1] == '/' || loc[1] == '\\') {
|
||||
continue
|
||||
}
|
||||
|
||||
u, err := url.Parse(loc)
|
||||
if err != nil || ((u.Scheme != "" || u.Host != "") && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
|
||||
continue
|
||||
@@ -266,7 +272,7 @@ func (ctx *Context) ServerError(logMsg string, logErr error) {
|
||||
func (ctx *Context) serverErrorInternal(logMsg string, logErr error) {
|
||||
if logErr != nil {
|
||||
log.ErrorWithSkip(2, "%s: %v", logMsg, logErr)
|
||||
if errors.Is(logErr, &net.OpError{}) {
|
||||
if _, ok := logErr.(*net.OpError); ok || errors.Is(logErr, &net.OpError{}) {
|
||||
// This is an error within the underlying connection
|
||||
// and further rendering will not work so just return
|
||||
return
|
||||
|
||||
@@ -129,7 +129,23 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
||||
|
||||
// Team.
|
||||
if ctx.Org.IsMember {
|
||||
shouldSeeAllTeams := false
|
||||
if ctx.Org.IsOwner {
|
||||
shouldSeeAllTeams = true
|
||||
} else {
|
||||
teams, err := org.GetUserTeams(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserTeams", err)
|
||||
return
|
||||
}
|
||||
for _, team := range teams {
|
||||
if team.IncludesAllRepositories && team.AccessMode >= perm.AccessModeAdmin {
|
||||
shouldSeeAllTeams = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if shouldSeeAllTeams {
|
||||
ctx.Org.Teams, err = org.LoadTeams()
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadTeams", err)
|
||||
|
||||
+21
-1
@@ -440,6 +440,26 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
|
||||
ctx.Repo.Owner = owner
|
||||
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||
|
||||
// redirect link to wiki
|
||||
if strings.HasSuffix(repoName, ".wiki") {
|
||||
// ctx.Req.URL.Path does not have the preceding appSubURL - any redirect must have this added
|
||||
// Now we happen to know that all of our paths are: /:username/:reponame/whatever_else
|
||||
originalRepoName := ctx.Params(":reponame")
|
||||
redirectRepoName := strings.TrimSuffix(repoName, ".wiki")
|
||||
redirectRepoName += originalRepoName[len(redirectRepoName)+5:]
|
||||
redirectPath := strings.Replace(
|
||||
ctx.Req.URL.EscapedPath(),
|
||||
url.PathEscape(userName)+"/"+url.PathEscape(originalRepoName),
|
||||
url.PathEscape(userName)+"/"+url.PathEscape(redirectRepoName)+"/wiki",
|
||||
1,
|
||||
)
|
||||
if ctx.Req.URL.RawQuery != "" {
|
||||
redirectPath += "?" + ctx.Req.URL.RawQuery
|
||||
}
|
||||
ctx.Redirect(path.Join(setting.AppSubURL, redirectPath))
|
||||
return
|
||||
}
|
||||
|
||||
// Get repository.
|
||||
repo, err := repo_model.GetRepositoryByName(owner.ID, repoName)
|
||||
if err != nil {
|
||||
@@ -911,7 +931,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
|
||||
|
||||
if refType == RepoRefLegacy {
|
||||
// redirect from old URL scheme to new URL scheme
|
||||
prefix := strings.TrimPrefix(setting.AppSubURL+strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*")), ctx.Repo.RepoLink)
|
||||
prefix := strings.TrimPrefix(setting.AppSubURL+strings.ToLower(strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*"))), strings.ToLower(ctx.Repo.RepoLink))
|
||||
|
||||
ctx.Redirect(path.Join(
|
||||
ctx.Repo.RepoLink,
|
||||
|
||||
@@ -163,6 +163,8 @@ func (c *Command) RunWithContext(rc *RunContext) error {
|
||||
fmt.Sprintf("LC_ALL=%s", DefaultLocale),
|
||||
// avoid prompting for credentials interactively, supported since git v2.3
|
||||
"GIT_TERMINAL_PROMPT=0",
|
||||
// ignore replace references (https://git-scm.com/docs/git-replace)
|
||||
"GIT_NO_REPLACE_OBJECTS=1",
|
||||
)
|
||||
|
||||
// TODO: verify if this is still needed in golang 1.15
|
||||
|
||||
+13
-9
@@ -97,15 +97,16 @@ func (repo *Repository) IsEmpty() (bool, error) {
|
||||
|
||||
// CloneRepoOptions options when clone a repository
|
||||
type CloneRepoOptions struct {
|
||||
Timeout time.Duration
|
||||
Mirror bool
|
||||
Bare bool
|
||||
Quiet bool
|
||||
Branch string
|
||||
Shared bool
|
||||
NoCheckout bool
|
||||
Depth int
|
||||
Filter string
|
||||
Timeout time.Duration
|
||||
Mirror bool
|
||||
Bare bool
|
||||
Quiet bool
|
||||
Branch string
|
||||
Shared bool
|
||||
NoCheckout bool
|
||||
Depth int
|
||||
Filter string
|
||||
SkipTLSVerify bool
|
||||
}
|
||||
|
||||
// Clone clones original repository to target path.
|
||||
@@ -128,6 +129,9 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo
|
||||
}
|
||||
|
||||
cmd := NewCommandContextNoGlobals(ctx, args...).AddArguments("clone")
|
||||
if opts.SkipTLSVerify {
|
||||
cmd.AddArguments("-c", "http.sslVerify=false")
|
||||
}
|
||||
if opts.Mirror {
|
||||
cmd.AddArguments("--mirror")
|
||||
}
|
||||
|
||||
@@ -191,7 +191,9 @@ func (c *CheckAttributeReader) Run() error {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil && c.ctx.Err() != nil && err.Error() != "signal: killed" {
|
||||
if err != nil && // If there is an error we need to return but:
|
||||
c.ctx.Err() != err && // 1. Ignore the context error if the context is cancelled or exceeds the deadline (RunWithContext could return c.ctx.Err() which is Canceled or DeadlineExceeded)
|
||||
err.Error() != "signal: killed" { // 2. We should not pass up errors due to the program being killed
|
||||
return fmt.Errorf("failed to run attr-check. Error: %w\nStderr: %s", err, stdErr.String())
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -40,20 +40,19 @@ steps:
|
||||
- go test -v -race -coverprofile=coverage.txt -covermode=atomic
|
||||
`,
|
||||
want: []string{
|
||||
`<span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`,
|
||||
`<span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default</span>`,
|
||||
`<span class="w">
|
||||
</span>`,
|
||||
`<span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
|
||||
`<span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`,
|
||||
`<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`,
|
||||
`<span class="w"> </span><span class="nt">environment</span><span class="p">:</span>`,
|
||||
`<span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`,
|
||||
`<span class="w"> </span><span class="nt">commands</span><span class="p">:</span>`,
|
||||
`<span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>`,
|
||||
`<span class="w"> </span>- <span class="l">go build -v</span>`,
|
||||
`<span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span><span class="w">
|
||||
</span>`,
|
||||
`<span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default</span>`,
|
||||
`</span></span><span class="line"><span class="cl">`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">environment</span><span class="p">:</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">commands</span><span class="p">:</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go build -v</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span><span class="w">
|
||||
</span></span></span>`,
|
||||
`<span class="w">
|
||||
</span>`,
|
||||
},
|
||||
@@ -76,20 +75,19 @@ steps:
|
||||
- go test -v -race -coverprofile=coverage.txt -covermode=atomic
|
||||
`,
|
||||
want: []string{
|
||||
`<span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`,
|
||||
`<span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default </span>`,
|
||||
`<span class="w">
|
||||
</span>`,
|
||||
`<span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
|
||||
`<span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`,
|
||||
`<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`,
|
||||
`<span class="w"> </span><span class="nt">environment</span><span class="p">:</span>`,
|
||||
`<span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`,
|
||||
`<span class="w"> </span><span class="nt">commands</span><span class="p">:</span>`,
|
||||
`<span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>`,
|
||||
`<span class="w"> </span>- <span class="l">go build -v</span>`,
|
||||
`<span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span>`,
|
||||
`<span class="w"> </span>`,
|
||||
`<span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default </span>`,
|
||||
`</span></span><span class="line"><span class="cl">`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">environment</span><span class="p">:</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">commands</span><span class="p">:</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go build -v</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span></span></span>`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"net"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// HostMatchList is used to check if a host or IP is in a list.
|
||||
@@ -104,11 +102,11 @@ func (hl *HostMatchList) checkIP(ip net.IP) bool {
|
||||
for _, builtin := range hl.builtins {
|
||||
switch builtin {
|
||||
case MatchBuiltinExternal:
|
||||
if ip.IsGlobalUnicast() && !util.IsIPPrivate(ip) {
|
||||
if ip.IsGlobalUnicast() && !ip.IsPrivate() {
|
||||
return true
|
||||
}
|
||||
case MatchBuiltinPrivate:
|
||||
if util.IsIPPrivate(ip) {
|
||||
if ip.IsPrivate() {
|
||||
return true
|
||||
}
|
||||
case MatchBuiltinLoopback:
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -111,6 +113,17 @@ func (p Pointer) RelativePath() string {
|
||||
return path.Join(p.Oid[0:2], p.Oid[2:4], p.Oid[4:])
|
||||
}
|
||||
|
||||
// ColorFormat provides a basic color format for a Team
|
||||
func (p Pointer) ColorFormat(s fmt.State) {
|
||||
if p.Oid == "" && p.Size == 0 {
|
||||
log.ColorFprintf(s, "<empty>")
|
||||
return
|
||||
}
|
||||
log.ColorFprintf(s, "%s:%d",
|
||||
log.NewColoredIDValue(p.Oid),
|
||||
p.Size)
|
||||
}
|
||||
|
||||
// GeneratePointer generates a pointer for arbitrary content
|
||||
func GeneratePointer(content io.Reader) (Pointer, error) {
|
||||
h := sha256.New()
|
||||
|
||||
@@ -205,7 +205,7 @@ func (b *footnoteBlockParser) Open(parent ast.Node, reader text.Reader, pc parse
|
||||
}
|
||||
open := pos + 1
|
||||
closes := 0
|
||||
closure := util.FindClosure(line[pos+1:], '[', ']', false, false)
|
||||
closure := util.FindClosure(line[pos+1:], '[', ']', false, false) //nolint
|
||||
closes = pos + 1 + closure
|
||||
next := closes + 1
|
||||
if closure > -1 {
|
||||
@@ -296,7 +296,7 @@ func (s *footnoteParser) Parse(parent ast.Node, block text.Reader, pc parser.Con
|
||||
return nil
|
||||
}
|
||||
open := pos
|
||||
closure := util.FindClosure(line[pos:], '[', ']', false, false)
|
||||
closure := util.FindClosure(line[pos:], '[', ']', false, false) //nolint
|
||||
if closure < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ var issueFullPatternOnce sync.Once
|
||||
func getIssueFullPattern() *regexp.Regexp {
|
||||
issueFullPatternOnce.Do(func() {
|
||||
issueFullPattern = regexp.MustCompile(regexp.QuoteMeta(setting.AppURL) +
|
||||
`\w+/\w+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#](\S+)?)?\b`)
|
||||
`[\w_.-]+/[\w_.-]+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#](\S+)?)?\b`)
|
||||
})
|
||||
return issueFullPattern
|
||||
}
|
||||
|
||||
@@ -95,6 +95,15 @@ func TestRender_CrossReferences(t *testing.T) {
|
||||
test(
|
||||
"/home/gitea/go-gitea/gitea#12345",
|
||||
`<p>/home/gitea/go-gitea/gitea#12345</p>`)
|
||||
test(
|
||||
util.URLJoin(TestAppURL, "gogitea", "gitea", "issues", "12345"),
|
||||
`<p><a href="`+util.URLJoin(TestAppURL, "gogitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/gitea#12345</a></p>`)
|
||||
test(
|
||||
util.URLJoin(TestAppURL, "go-gitea", "gitea", "issues", "12345"),
|
||||
`<p><a href="`+util.URLJoin(TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`)
|
||||
test(
|
||||
util.URLJoin(TestAppURL, "gogitea", "some-repo-name", "issues", "12345"),
|
||||
`<p><a href="`+util.URLJoin(TestAppURL, "gogitea", "some-repo-name", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogitea/some-repo-name#12345</a></p>`)
|
||||
}
|
||||
|
||||
func TestMisc_IsSameDomain(t *testing.T) {
|
||||
|
||||
@@ -77,9 +77,9 @@ func HelloWorld() {
|
||||
}
|
||||
#+end_src
|
||||
`, `<div class="src src-go">
|
||||
<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints "Hello World"
|
||||
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
|
||||
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Hello World"</span><span class="p">)</span>
|
||||
<span class="p">}</span></code></pre>
|
||||
<pre><code class="chroma language-go"><span class="line"><span class="cl"><span class="c1">// HelloWorld prints "Hello World"
|
||||
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Hello World"</span><span class="p">)</span>
|
||||
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre>
|
||||
</div>`)
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
package nosql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||
@@ -20,8 +22,16 @@ func (m *Manager) CloseLevelDB(connection string) error {
|
||||
defer m.mutex.Unlock()
|
||||
db, ok := m.LevelDBConnections[connection]
|
||||
if !ok {
|
||||
connection = ToLevelDBURI(connection).String()
|
||||
db, ok = m.LevelDBConnections[connection]
|
||||
// Try the full URI
|
||||
uri := ToLevelDBURI(connection)
|
||||
db, ok = m.LevelDBConnections[uri.String()]
|
||||
|
||||
if !ok {
|
||||
// Try the datadir directly
|
||||
dataDir := path.Join(uri.Host, uri.Path)
|
||||
|
||||
db, ok = m.LevelDBConnections[dataDir]
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return nil
|
||||
@@ -40,6 +50,12 @@ func (m *Manager) CloseLevelDB(connection string) error {
|
||||
|
||||
// GetLevelDB gets a levelDB for a particular connection
|
||||
func (m *Manager) GetLevelDB(connection string) (*leveldb.DB, error) {
|
||||
// Convert the provided connection description to the common format
|
||||
uri := ToLevelDBURI(connection)
|
||||
|
||||
// Get the datadir
|
||||
dataDir := path.Join(uri.Host, uri.Path)
|
||||
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
db, ok := m.LevelDBConnections[connection]
|
||||
@@ -48,12 +64,28 @@ func (m *Manager) GetLevelDB(connection string) (*leveldb.DB, error) {
|
||||
|
||||
return db.db, nil
|
||||
}
|
||||
uri := ToLevelDBURI(connection)
|
||||
db = &levelDBHolder{
|
||||
name: []string{connection, uri.String()},
|
||||
|
||||
db, ok = m.LevelDBConnections[uri.String()]
|
||||
if ok {
|
||||
db.count++
|
||||
|
||||
return db.db, nil
|
||||
}
|
||||
|
||||
// if there is already a connection to this leveldb reuse that
|
||||
// NOTE: if there differing options then only the first leveldb connection will be used
|
||||
db, ok = m.LevelDBConnections[dataDir]
|
||||
if ok {
|
||||
db.count++
|
||||
log.Warn("Duplicate connnection to level db: %s with different connection strings. Initial connection: %s. This connection: %s", dataDir, db.name[0], connection)
|
||||
db.name = append(db.name, connection)
|
||||
m.LevelDBConnections[connection] = db
|
||||
return db.db, nil
|
||||
}
|
||||
db = &levelDBHolder{
|
||||
name: []string{connection, uri.String(), dataDir},
|
||||
}
|
||||
|
||||
dataDir := path.Join(uri.Host, uri.Path)
|
||||
opts := &opt.Options{}
|
||||
for k, v := range uri.Query() {
|
||||
switch replacer.Replace(strings.ToLower(k)) {
|
||||
@@ -134,7 +166,11 @@ func (m *Manager) GetLevelDB(connection string) (*leveldb.DB, error) {
|
||||
db.db, err = leveldb.OpenFile(dataDir, opts)
|
||||
if err != nil {
|
||||
if !errors.IsCorrupted(err) {
|
||||
return nil, err
|
||||
if strings.Contains(err.Error(), "resource temporarily unavailable") {
|
||||
return nil, fmt.Errorf("unable to lock level db at %s: %w", dataDir, err)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unable to open level db at %s: %w", dataDir, err)
|
||||
}
|
||||
db.db, err = leveldb.RecoverFile(dataDir, opts)
|
||||
if err != nil {
|
||||
|
||||
@@ -204,7 +204,7 @@ func (ns *notificationService) NotifyPullRevieweDismiss(doer *user_model.User, r
|
||||
}
|
||||
|
||||
func (ns *notificationService) NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment) {
|
||||
if !removed {
|
||||
if !removed && doer.ID != assignee.ID {
|
||||
var opts = issueNotificationOpts{
|
||||
IssueID: issue.ID,
|
||||
NotificationAuthorID: doer.ID,
|
||||
|
||||
@@ -56,7 +56,7 @@ type HookOptions struct {
|
||||
GitQuarantinePath string
|
||||
GitPushOptions GitPushOptions
|
||||
PullRequestID int64
|
||||
IsDeployKey bool
|
||||
DeployKeyID int64 // if the pusher is a DeployKey, then UserID is the repo's org user.
|
||||
IsWiki bool
|
||||
}
|
||||
|
||||
|
||||
@@ -46,9 +46,9 @@ func ServNoCommand(ctx context.Context, keyID int64) (*asymkey_model.PublicKey,
|
||||
// ServCommandResults are the results of a call to the private route serv
|
||||
type ServCommandResults struct {
|
||||
IsWiki bool
|
||||
IsDeployKey bool
|
||||
KeyID int64
|
||||
KeyName string
|
||||
DeployKeyID int64
|
||||
KeyID int64 // public key
|
||||
KeyName string // this field is ambiguous, it can be the name of DeployKey, or the name of the PublicKey
|
||||
UserName string
|
||||
UserEmail string
|
||||
UserID int64
|
||||
|
||||
@@ -43,7 +43,7 @@ var defaultTransformers = []transformer{
|
||||
{Name: "PASCAL", Transform: xstrings.ToCamelCase},
|
||||
{Name: "LOWER", Transform: strings.ToLower},
|
||||
{Name: "UPPER", Transform: strings.ToUpper},
|
||||
{Name: "TITLE", Transform: strings.Title},
|
||||
{Name: "TITLE", Transform: strings.Title}, // nolint
|
||||
}
|
||||
|
||||
func generateExpansion(src string, templateRepo, generateRepo *repo_model.Repository) string {
|
||||
@@ -62,7 +62,7 @@ func generateExpansion(src string, templateRepo, generateRepo *repo_model.Reposi
|
||||
{Name: "TEMPLATE_SSH_URL", Value: templateRepo.CloneLink().SSH, Transformers: nil},
|
||||
}
|
||||
|
||||
var expansionMap = make(map[string]string)
|
||||
expansionMap := make(map[string]string)
|
||||
for _, e := range expansions {
|
||||
expansionMap[e.Name] = e.Value
|
||||
for _, tr := range e.Transformers {
|
||||
@@ -159,7 +159,7 @@ func generateRepoCommit(repo, templateRepo, generateRepo *repo_model.Repository,
|
||||
|
||||
if err := os.WriteFile(path,
|
||||
[]byte(generateExpansion(string(content), templateRepo, generateRepo)),
|
||||
0644); err != nil {
|
||||
0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
|
||||
+31
-29
@@ -72,9 +72,10 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
||||
}
|
||||
|
||||
if err = git.CloneWithContext(ctx, opts.CloneAddr, repoPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
SkipTLSVerify: setting.Migrations.SkipTLSVerify,
|
||||
}); err != nil {
|
||||
return repo, fmt.Errorf("Clone: %v", err)
|
||||
}
|
||||
@@ -88,10 +89,11 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
||||
}
|
||||
|
||||
if err = git.CloneWithContext(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
Branch: "master",
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
Branch: "master",
|
||||
SkipTLSVerify: setting.Migrations.SkipTLSVerify,
|
||||
}); err != nil {
|
||||
log.Warn("Clone wiki: %v", err)
|
||||
if err := util.RemoveAll(wikiPath); err != nil {
|
||||
@@ -254,7 +256,7 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
|
||||
opts.Page = page
|
||||
rels, err := models.GetReleasesByRepoID(repo.ID, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetReleasesByRepoID: %v", err)
|
||||
return fmt.Errorf("unable to GetReleasesByRepoID in Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
|
||||
}
|
||||
if len(rels) == 0 {
|
||||
break
|
||||
@@ -265,11 +267,11 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
|
||||
}
|
||||
commitID, err := gitRepo.GetTagCommitID(rel.TagName)
|
||||
if err != nil && !git.IsErrNotExist(err) {
|
||||
return fmt.Errorf("GetTagCommitID: %s: %v", rel.TagName, err)
|
||||
return fmt.Errorf("unable to GetTagCommitID for %q in Repo[%d:%s/%s]: %w", rel.TagName, repo.ID, repo.OwnerName, repo.Name, err)
|
||||
}
|
||||
if git.IsErrNotExist(err) || commitID != rel.Sha1 {
|
||||
if err := models.PushUpdateDeleteTag(repo, rel.TagName); err != nil {
|
||||
return fmt.Errorf("PushUpdateDeleteTag: %s: %v", rel.TagName, err)
|
||||
return fmt.Errorf("unable to PushUpdateDeleteTag: %q in Repo[%d:%s/%s]: %w", rel.TagName, repo.ID, repo.OwnerName, repo.Name, err)
|
||||
}
|
||||
} else {
|
||||
existingRelTags[strings.ToLower(rel.TagName)] = struct{}{}
|
||||
@@ -278,12 +280,12 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
|
||||
}
|
||||
tags, err := gitRepo.GetTags(0, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetTags: %v", err)
|
||||
return fmt.Errorf("unable to GetTags in Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
|
||||
}
|
||||
for _, tagName := range tags {
|
||||
if _, ok := existingRelTags[strings.ToLower(tagName)]; !ok {
|
||||
if err := PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
|
||||
return fmt.Errorf("pushUpdateAddTag: %v", err)
|
||||
return fmt.Errorf("unable to PushUpdateAddTag: %q to Repo[%d:%s/%s]: %w", tagName, repo.ID, repo.OwnerName, repo.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,11 +296,11 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
|
||||
func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagName string) error {
|
||||
tag, err := gitRepo.GetTag(tagName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetTag: %v", err)
|
||||
return fmt.Errorf("unable to GetTag: %w", err)
|
||||
}
|
||||
commit, err := tag.Commit(gitRepo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Commit: %v", err)
|
||||
return fmt.Errorf("unable to get tag Commit: %w", err)
|
||||
}
|
||||
|
||||
sig := tag.Tagger
|
||||
@@ -310,22 +312,22 @@ func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagN
|
||||
}
|
||||
|
||||
var author *user_model.User
|
||||
var createdAt = time.Unix(1, 0)
|
||||
createdAt := time.Unix(1, 0)
|
||||
|
||||
if sig != nil {
|
||||
author, err = user_model.GetUserByEmail(sig.Email)
|
||||
if err != nil && !user_model.IsErrUserNotExist(err) {
|
||||
return fmt.Errorf("GetUserByEmail: %v", err)
|
||||
return fmt.Errorf("unable to GetUserByEmail for %q: %w", sig.Email, err)
|
||||
}
|
||||
createdAt = sig.When
|
||||
}
|
||||
|
||||
commitsCount, err := commit.CommitsCount()
|
||||
if err != nil {
|
||||
return fmt.Errorf("CommitsCount: %v", err)
|
||||
return fmt.Errorf("unable to get CommitsCount: %w", err)
|
||||
}
|
||||
|
||||
var rel = models.Release{
|
||||
rel := models.Release{
|
||||
RepoID: repo.ID,
|
||||
TagName: tagName,
|
||||
LowerTagName: strings.ToLower(tagName),
|
||||
@@ -359,14 +361,14 @@ func StoreMissingLfsObjectsInRepository(ctx context.Context, repo *repo_model.Re
|
||||
|
||||
_, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repo.ID})
|
||||
if err != nil {
|
||||
log.Error("Error creating LFS meta object %v: %v", p, err)
|
||||
log.Error("Repo[%-v]: Error creating LFS meta object %-v: %v", repo, p, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := contentStore.Put(p, content); err != nil {
|
||||
log.Error("Error storing content for LFS meta object %v: %v", p, err)
|
||||
log.Error("Repo[%-v]: Error storing content for LFS meta object %-v: %v", repo, p, err)
|
||||
if _, err2 := models.RemoveLFSMetaObjectByOid(repo.ID, p.Oid); err2 != nil {
|
||||
log.Error("Error removing LFS meta object %v: %v", p, err2)
|
||||
log.Error("Repo[%-v]: Error removing LFS meta object %-v: %v", repo, p, err2)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -386,32 +388,32 @@ func StoreMissingLfsObjectsInRepository(ctx context.Context, repo *repo_model.Re
|
||||
for pointerBlob := range pointerChan {
|
||||
meta, err := models.GetLFSMetaObjectByOid(repo.ID, pointerBlob.Oid)
|
||||
if err != nil && err != models.ErrLFSObjectNotExist {
|
||||
log.Error("Error querying LFS meta object %v: %v", pointerBlob.Pointer, err)
|
||||
log.Error("Repo[%-v]: Error querying LFS meta object %-v: %v", repo, pointerBlob.Pointer, err)
|
||||
return err
|
||||
}
|
||||
if meta != nil {
|
||||
log.Trace("Skipping unknown LFS meta object %v", pointerBlob.Pointer)
|
||||
log.Trace("Repo[%-v]: Skipping unknown LFS meta object %-v", repo, pointerBlob.Pointer)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Trace("LFS object %v not present in repository %s", pointerBlob.Pointer, repo.FullName())
|
||||
log.Trace("Repo[%-v]: LFS object %-v not present in repository", repo, pointerBlob.Pointer)
|
||||
|
||||
exist, err := contentStore.Exists(pointerBlob.Pointer)
|
||||
if err != nil {
|
||||
log.Error("Error checking if LFS object %v exists: %v", pointerBlob.Pointer, err)
|
||||
log.Error("Repo[%-v]: Error checking if LFS object %-v exists: %v", repo, pointerBlob.Pointer, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if exist {
|
||||
log.Trace("LFS object %v already present; creating meta object", pointerBlob.Pointer)
|
||||
log.Trace("Repo[%-v]: LFS object %-v already present; creating meta object", repo, pointerBlob.Pointer)
|
||||
_, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: pointerBlob.Pointer, RepositoryID: repo.ID})
|
||||
if err != nil {
|
||||
log.Error("Error creating LFS meta object %v: %v", pointerBlob.Pointer, err)
|
||||
log.Error("Repo[%-v]: Error creating LFS meta object %-v: %v", repo, pointerBlob.Pointer, err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if setting.LFS.MaxFileSize > 0 && pointerBlob.Size > setting.LFS.MaxFileSize {
|
||||
log.Info("LFS object %v download denied because of LFS_MAX_FILE_SIZE=%d < size %d", pointerBlob.Pointer, setting.LFS.MaxFileSize, pointerBlob.Size)
|
||||
log.Info("Repo[%-v]: LFS object %-v download denied because of LFS_MAX_FILE_SIZE=%d < size %d", repo, pointerBlob.Pointer, setting.LFS.MaxFileSize, pointerBlob.Size)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -432,7 +434,7 @@ func StoreMissingLfsObjectsInRepository(ctx context.Context, repo *repo_model.Re
|
||||
|
||||
err, has := <-errChan
|
||||
if has {
|
||||
log.Error("Error enumerating LFS objects for repository: %v", err)
|
||||
log.Error("Repo[%-v]: Error enumerating LFS objects for repository: %v", repo, err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+10
-6
@@ -15,13 +15,17 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
var filenameSuffix = ""
|
||||
var descriptionLock = sync.RWMutex{}
|
||||
var logDescriptions = make(map[string]*LogDescription)
|
||||
var (
|
||||
filenameSuffix = ""
|
||||
descriptionLock = sync.RWMutex{}
|
||||
logDescriptions = make(map[string]*LogDescription)
|
||||
)
|
||||
|
||||
// GetLogDescriptions returns a race safe set of descriptions
|
||||
func GetLogDescriptions() map[string]*LogDescription {
|
||||
@@ -86,7 +90,7 @@ func RemoveSubLogDescription(key, name string) bool {
|
||||
type defaultLogOptions struct {
|
||||
levelName string // LogLevel
|
||||
flags string
|
||||
filename string //path.Join(LogRootPath, "gitea.log")
|
||||
filename string // path.Join(LogRootPath, "gitea.log")
|
||||
bufferLength int64
|
||||
disableConsole bool
|
||||
}
|
||||
@@ -243,7 +247,7 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription
|
||||
Provider: provider,
|
||||
Config: config,
|
||||
})
|
||||
log.Info("%s Log: %s(%s:%s)", strings.Title(key), strings.Title(name), provider, levelName)
|
||||
log.Info("%s Log: %s(%s:%s)", cases.Title(language.English).String(key), cases.Title(language.English).String(name), provider, levelName)
|
||||
}
|
||||
|
||||
AddLogDescription(key, &description)
|
||||
@@ -327,7 +331,7 @@ func newLogService() {
|
||||
Provider: provider,
|
||||
Config: config,
|
||||
})
|
||||
log.Info("Gitea Log Mode: %s(%s:%s)", strings.Title(name), strings.Title(provider), levelName)
|
||||
log.Info("Gitea Log Mode: %s(%s:%s)", cases.Title(language.English).String(name), cases.Title(language.English).String(provider), levelName)
|
||||
}
|
||||
|
||||
AddLogDescription(log.DEFAULT, &description)
|
||||
|
||||
@@ -30,6 +30,8 @@ import (
|
||||
|
||||
"github.com/unknwon/com"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
@@ -90,13 +92,15 @@ var (
|
||||
// AppDataPath is the default path for storing data.
|
||||
// It maps to ini:"APP_DATA_PATH" and defaults to AppWorkPath + "/data"
|
||||
AppDataPath string
|
||||
// LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix
|
||||
// It maps to ini:"LOCAL_ROOT_URL"
|
||||
LocalURL string
|
||||
|
||||
// Server settings
|
||||
Protocol Scheme
|
||||
Domain string
|
||||
HTTPAddr string
|
||||
HTTPPort string
|
||||
LocalURL string
|
||||
RedirectOtherPort bool
|
||||
PortToRedirect string
|
||||
OfflineMode bool
|
||||
@@ -637,7 +641,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||
}
|
||||
UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
|
||||
UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
|
||||
if err != nil || UnixSocketPermissionParsed > 0777 {
|
||||
if err != nil || UnixSocketPermissionParsed > 0o777 {
|
||||
log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
|
||||
}
|
||||
|
||||
@@ -710,6 +714,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||
}
|
||||
}
|
||||
LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
|
||||
LocalURL = strings.TrimRight(LocalURL, "/") + "/"
|
||||
RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
|
||||
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
|
||||
OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
|
||||
@@ -793,16 +798,16 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||
SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(","))
|
||||
|
||||
if !SSH.Disabled && !SSH.StartBuiltinServer {
|
||||
if err := os.MkdirAll(SSH.RootPath, 0700); err != nil {
|
||||
if err := os.MkdirAll(SSH.RootPath, 0o700); err != nil {
|
||||
log.Fatal("Failed to create '%s': %v", SSH.RootPath, err)
|
||||
} else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil {
|
||||
} else if err = os.MkdirAll(SSH.KeyTestPath, 0o644); err != nil {
|
||||
log.Fatal("Failed to create '%s': %v", SSH.KeyTestPath, err)
|
||||
}
|
||||
|
||||
if len(trustedUserCaKeys) > 0 && SSH.AuthorizedPrincipalsEnabled {
|
||||
fname := sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem"))
|
||||
if err := os.WriteFile(fname,
|
||||
[]byte(strings.Join(trustedUserCaKeys, "\n")), 0600); err != nil {
|
||||
[]byte(strings.Join(trustedUserCaKeys, "\n")), 0o600); err != nil {
|
||||
log.Fatal("Failed to create '%s': %v", fname, err)
|
||||
}
|
||||
}
|
||||
@@ -943,8 +948,9 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||
// The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches.
|
||||
// Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly.
|
||||
unsafeAllowRunAsRoot := Cfg.Section("").Key("I_AM_BEING_UNSAFE_RUNNING_AS_ROOT").MustBool(false)
|
||||
RunMode = Cfg.Section("").Key("RUN_MODE").MustString("prod")
|
||||
IsProd = strings.EqualFold(RunMode, "prod")
|
||||
RunMode = Cfg.Section("").Key("RUN_MODE").MustString("Prod")
|
||||
RunMode = cases.Title(language.English).String(strings.ToLower(RunMode))
|
||||
IsProd = RunMode == "Prod"
|
||||
// Does not check run user when the install lock is off.
|
||||
if InstallLock {
|
||||
currentUser, match := IsRunUserMatchCurrentUser(RunUser)
|
||||
@@ -1074,7 +1080,7 @@ func loadInternalToken(sec *ini.Section) string {
|
||||
}
|
||||
switch tempURI.Scheme {
|
||||
case "file":
|
||||
fp, err := os.OpenFile(tempURI.RequestURI(), os.O_RDWR, 0600)
|
||||
fp, err := os.OpenFile(tempURI.RequestURI(), os.O_RDWR, 0o600)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to open InternalTokenURI (%s): %v", uri, err)
|
||||
}
|
||||
|
||||
@@ -317,64 +317,7 @@ func Listen(host string, port int, ciphers, keyExchanges, macs []string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround slightly broken behaviour in x/crypto/ssh/handshake.go:458-463
|
||||
//
|
||||
// Fundamentally the issue here is that HostKeyAlgos make the incorrect assumption
|
||||
// that the PublicKey().Type() matches the signature algorithm.
|
||||
//
|
||||
// Therefore we need to add duplicates for the RSA with different signing algorithms.
|
||||
signers := make([]ssh.Signer, 0, len(srv.HostSigners))
|
||||
for _, signer := range srv.HostSigners {
|
||||
if signer.PublicKey().Type() == "ssh-rsa" {
|
||||
signers = append(signers,
|
||||
&wrapSigner{
|
||||
Signer: signer,
|
||||
algorithm: gossh.SigAlgoRSASHA2512,
|
||||
},
|
||||
&wrapSigner{
|
||||
Signer: signer,
|
||||
algorithm: gossh.SigAlgoRSASHA2256,
|
||||
},
|
||||
)
|
||||
}
|
||||
signers = append(signers, signer)
|
||||
}
|
||||
srv.HostSigners = signers
|
||||
|
||||
go listen(&srv)
|
||||
|
||||
}
|
||||
|
||||
// wrapSigner wraps a signer and overrides its public key type with the provided algorithm
|
||||
type wrapSigner struct {
|
||||
ssh.Signer
|
||||
algorithm string
|
||||
}
|
||||
|
||||
// PublicKey returns an associated PublicKey instance.
|
||||
func (s *wrapSigner) PublicKey() gossh.PublicKey {
|
||||
return &wrapPublicKey{
|
||||
PublicKey: s.Signer.PublicKey(),
|
||||
algorithm: s.algorithm,
|
||||
}
|
||||
}
|
||||
|
||||
// Sign returns raw signature for the given data. This method
|
||||
// will apply the hash specified for the keytype to the data using
|
||||
// the algorithm assigned for this key
|
||||
func (s *wrapSigner) Sign(rand io.Reader, data []byte) (*gossh.Signature, error) {
|
||||
return s.Signer.(gossh.AlgorithmSigner).SignWithAlgorithm(rand, data, s.algorithm)
|
||||
}
|
||||
|
||||
// wrapPublicKey wraps a PublicKey and overrides its type
|
||||
type wrapPublicKey struct {
|
||||
gossh.PublicKey
|
||||
algorithm string
|
||||
}
|
||||
|
||||
// Type returns the algorithm
|
||||
func (k *wrapPublicKey) Type() string {
|
||||
return k.algorithm
|
||||
}
|
||||
|
||||
// GenKeyPair make a pair of public and private keys for SSH access.
|
||||
|
||||
@@ -9,15 +9,15 @@ import (
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
var (
|
||||
_ ObjectStorage = &LocalStorage{}
|
||||
)
|
||||
var _ ObjectStorage = &LocalStorage{}
|
||||
|
||||
// LocalStorageType is the type descriptor for local storage
|
||||
const LocalStorageType Type = "local"
|
||||
@@ -59,14 +59,18 @@ func NewLocalStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *LocalStorage) buildLocalPath(p string) string {
|
||||
return filepath.Join(l.dir, path.Clean("/" + strings.ReplaceAll(p, "\\", "/"))[1:])
|
||||
}
|
||||
|
||||
// Open a file
|
||||
func (l *LocalStorage) Open(path string) (Object, error) {
|
||||
return os.Open(filepath.Join(l.dir, path))
|
||||
return os.Open(l.buildLocalPath(path))
|
||||
}
|
||||
|
||||
// Save a file
|
||||
func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error) {
|
||||
p := filepath.Join(l.dir, path)
|
||||
p := l.buildLocalPath(path)
|
||||
if err := os.MkdirAll(filepath.Dir(p), os.ModePerm); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -106,13 +110,12 @@ func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error)
|
||||
|
||||
// Stat returns the info of the file
|
||||
func (l *LocalStorage) Stat(path string) (os.FileInfo, error) {
|
||||
return os.Stat(filepath.Join(l.dir, path))
|
||||
return os.Stat(l.buildLocalPath(path))
|
||||
}
|
||||
|
||||
// Delete delete a file
|
||||
func (l *LocalStorage) Delete(path string) error {
|
||||
p := filepath.Join(l.dir, path)
|
||||
return util.Remove(p)
|
||||
return util.Remove(l.buildLocalPath(path))
|
||||
}
|
||||
|
||||
// URL gets the redirect URL to a file
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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 storage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBuildLocalPath(t *testing.T) {
|
||||
kases := []struct {
|
||||
localDir string
|
||||
path string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"a",
|
||||
"0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
"a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
},
|
||||
{
|
||||
"a",
|
||||
"../0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
"a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
},
|
||||
{
|
||||
"a",
|
||||
"0\\a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
"a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
},
|
||||
{
|
||||
"b",
|
||||
"a/../0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
"b/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
},
|
||||
{
|
||||
"b",
|
||||
"a\\..\\0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
"b/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
},
|
||||
}
|
||||
|
||||
for _, k := range kases {
|
||||
t.Run(k.path, func(t *testing.T) {
|
||||
l := LocalStorage{dir: k.localDir}
|
||||
|
||||
assert.EqualValues(t, k.expected, l.buildLocalPath(k.path))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ func NewMinioStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error
|
||||
}
|
||||
|
||||
func (m *MinioStorage) buildMinioPath(p string) string {
|
||||
return strings.TrimPrefix(path.Join(m.basePath, p), "/")
|
||||
return strings.TrimPrefix(path.Join(m.basePath, path.Clean("/" + strings.ReplaceAll(p, "\\", "/"))[1:]), "/")
|
||||
}
|
||||
|
||||
// Open open a file
|
||||
|
||||
@@ -38,6 +38,8 @@ import (
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/editorconfig/editorconfig-core-go/v2"
|
||||
)
|
||||
@@ -49,7 +51,7 @@ var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}[\s]*$`)
|
||||
func NewFuncMap() []template.FuncMap {
|
||||
return []template.FuncMap{map[string]interface{}{
|
||||
"GoVer": func() string {
|
||||
return strings.Title(runtime.Version())
|
||||
return cases.Title(language.English).String(runtime.Version())
|
||||
},
|
||||
"UseHTTPS": func() bool {
|
||||
return strings.HasPrefix(setting.AppURL, "https")
|
||||
@@ -285,7 +287,7 @@ func NewFuncMap() []template.FuncMap {
|
||||
return util.MergeInto(dict, values...)
|
||||
},
|
||||
"percentage": func(n int, values ...int) float32 {
|
||||
var sum = 0
|
||||
sum := 0
|
||||
for i := 0; i < len(values); i++ {
|
||||
sum += values[i]
|
||||
}
|
||||
@@ -378,6 +380,7 @@ func NewFuncMap() []template.FuncMap {
|
||||
},
|
||||
"Join": strings.Join,
|
||||
"QueryEscape": url.QueryEscape,
|
||||
"DotEscape": DotEscape,
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -386,7 +389,7 @@ func NewFuncMap() []template.FuncMap {
|
||||
func NewTextFuncMap() []texttmpl.FuncMap {
|
||||
return []texttmpl.FuncMap{map[string]interface{}{
|
||||
"GoVer": func() string {
|
||||
return strings.Title(runtime.Version())
|
||||
return cases.Title(language.English).String(runtime.Version())
|
||||
},
|
||||
"AppName": func() string {
|
||||
return setting.AppName
|
||||
@@ -477,7 +480,7 @@ func NewTextFuncMap() []texttmpl.FuncMap {
|
||||
return dict, nil
|
||||
},
|
||||
"percentage": func(n int, values ...int) float32 {
|
||||
var sum = 0
|
||||
sum := 0
|
||||
for i := 0; i < len(values); i++ {
|
||||
sum += values[i]
|
||||
}
|
||||
@@ -501,8 +504,10 @@ func NewTextFuncMap() []texttmpl.FuncMap {
|
||||
}}
|
||||
}
|
||||
|
||||
var widthRe = regexp.MustCompile(`width="[0-9]+?"`)
|
||||
var heightRe = regexp.MustCompile(`height="[0-9]+?"`)
|
||||
var (
|
||||
widthRe = regexp.MustCompile(`width="[0-9]+?"`)
|
||||
heightRe = regexp.MustCompile(`height="[0-9]+?"`)
|
||||
)
|
||||
|
||||
func parseOthers(defaultSize int, defaultClass string, others ...interface{}) (int, string) {
|
||||
size := defaultSize
|
||||
@@ -629,6 +634,11 @@ func JSEscape(raw string) string {
|
||||
return template.JSEscapeString(raw)
|
||||
}
|
||||
|
||||
// DotEscape wraps a dots in names with ZWJ [U+200D] in order to prevent autolinkers from detecting these as urls
|
||||
func DotEscape(raw string) string {
|
||||
return strings.ReplaceAll(raw, ".", "\u200d.\u200d")
|
||||
}
|
||||
|
||||
// Sha1 returns sha1 sum of string
|
||||
func Sha1(str string) string {
|
||||
return base.EncodeSha1(str)
|
||||
@@ -736,7 +746,7 @@ func RenderEmoji(text string) template.HTML {
|
||||
return template.HTML(renderedText)
|
||||
}
|
||||
|
||||
//ReactionToEmoji renders emoji for use in reactions
|
||||
// ReactionToEmoji renders emoji for use in reactions
|
||||
func ReactionToEmoji(reaction string) template.HTML {
|
||||
val := emoji.FromCode(reaction)
|
||||
if val != nil {
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright 2021 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 util
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// IsIPPrivate for net.IP.IsPrivate. TODO: replace with `ip.IsPrivate()` if min go version is bumped to 1.17
|
||||
func IsIPPrivate(ip net.IP) bool {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return ip4[0] == 10 ||
|
||||
(ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
|
||||
(ip4[0] == 192 && ip4[1] == 168)
|
||||
}
|
||||
return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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 util
|
||||
|
||||
// RemoveIDFromList removes the given ID from the slice, if found.
|
||||
// It does not preserve order, and assumes the ID is unique.
|
||||
func RemoveIDFromList(list []int64, id int64) ([]int64, bool) {
|
||||
n := len(list) - 1
|
||||
for i, item := range list {
|
||||
if item == id {
|
||||
list[i] = list[n]
|
||||
return list[:n], true
|
||||
}
|
||||
}
|
||||
return list, false
|
||||
}
|
||||
@@ -41,6 +41,7 @@ webauthn_use_twofa=Zwei-Faktor-Authentifizierung via Handy verwenden
|
||||
webauthn_error=Dein Sicherheitsschlüssel konnte nicht gelesen werden.
|
||||
webauthn_unsupported_browser=Dein Browser unterstützt derzeit keinen WebAuthn.
|
||||
webauthn_error_unknown=Ein unbekannter Fehler ist aufgetreten. Bitte versuche es erneut.
|
||||
webauthn_error_insecure=WebAuthn unterstützt nur sichere Verbindungen. Zum Testen über HTTP kannst du "localhost" oder "127.0.0.1" als Host verwenden
|
||||
webauthn_error_unable_to_process=Der Server konnte deine Anfrage nicht bearbeiten.
|
||||
webauthn_error_duplicated=Für diese Anfrage ist der Sicherheitsschlüssel nicht erlaubt. Bitte stell sicher, dass er nicht bereits registriert ist.
|
||||
webauthn_error_timeout=Das Zeitlimit wurde erreicht, bevor dein Schlüssel gelesen werden konnte. Bitte lade die Seite erneut.
|
||||
@@ -140,6 +141,7 @@ charset=Zeichensatz
|
||||
path=Pfad
|
||||
sqlite_helper=Dateipfad zur SQLite3 Datenbank.<br>Gebe einen absoluten Pfad an, wenn Gitea als Service gestartet wird.
|
||||
reinstall_error=Du versuchst, in eine bereits existierende Gitea Datenbank zu installieren
|
||||
reinstall_confirm_message=Eine Neuinstallation mit einer bestehenden Gitea-Datenbank kann mehrere Probleme verursachen. In den meisten Fällen solltest du deine vorhandene "app.ini" verwenden, um Gitea auszuführen. Wenn du weist, was du tust, bestätigen die folgenden Angaben:
|
||||
reinstall_confirm_check_3=Du bestätigst, dass du absolut sicher bist, dass diese Gitea mit der richtigen app.ini läuft, und du sicher bist, dass du neu installieren musst. Du bestätigst, dass du die oben genannten Risiken anerkennst.
|
||||
err_empty_db_path=Der SQLite3 Datenbankpfad darf nicht leer sein.
|
||||
no_admin_and_disable_registration=Du kannst Selbst-Registrierungen nicht deaktivieren, ohne ein Administratorkonto zu erstellen.
|
||||
@@ -2050,8 +2052,8 @@ settings.lfs_pointers.accessible=Nutzer hat Zugriff
|
||||
settings.lfs_pointers.associateAccessible=Ordne %d zugängliche OIDs zu
|
||||
settings.rename_branch_failed_exist=Kann den Branch nicht umbenennen, da der Zielbranch %s bereits existiert.
|
||||
settings.rename_branch_failed_not_exist=Kann den Branch %s nicht umbenennen, da er nicht existiert.
|
||||
settings.rename_branch_success=Zweig %s wurde erfolgreich in %s umbenannt.
|
||||
settings.rename_branch_from=alter Zweigname
|
||||
settings.rename_branch_success=Branch %s wurde erfolgreich in %s umbenannt.
|
||||
settings.rename_branch_from=alter Branchname
|
||||
settings.rename_branch_to=neuer Branchname
|
||||
settings.rename_branch=Branch umbennen
|
||||
|
||||
|
||||
@@ -34,6 +34,20 @@ twofa=Έλεγχος Ταυτότητας Δύο Παραγόντων
|
||||
twofa_scratch=Κωδικός Μίας Χρήσης Δύο Παραγόντων
|
||||
passcode=Κωδικός
|
||||
|
||||
webauthn_insert_key=Εισάγετε το κλειδί ασφαλείας σας
|
||||
webauthn_sign_in=Πατήστε το κουμπί στο κλειδί ασφαλείας. Αν το κλειδί ασφαλείας σας δεν έχει κουμπί, τοποθετήστε το ξανά.
|
||||
webauthn_press_button=Παρακαλώ πατήστε το κουμπί στο κλειδί ασφαλείας…
|
||||
webauthn_use_twofa=Χρησιμοποιήστε έναν κωδικό δύο παραγόντων από το τηλέφωνό σας
|
||||
webauthn_error=Αδύνατη η ανάγνωση του κλειδιού ασφαλείας.
|
||||
webauthn_unsupported_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει επί του παρόντος WebAuthn.
|
||||
webauthn_error_unknown=Παρουσιάστηκε ένα άγνωστο σφάλμα. Παρακαλώ προσπαθήστε ξανά.
|
||||
webauthn_error_insecure=Το WebAuthn υποστηρίζει μόνο ασφαλείς συνδέσεις. Για δοκιμές πάνω από HTTP, μπορείτε να χρησιμοποιήσετε την προέλευση "localhost" ή "127.0.0.1"
|
||||
webauthn_error_unable_to_process=Ο διακομιστής δεν μπόρεσε να επεξεργαστεί το αίτημά σας.
|
||||
webauthn_error_duplicated=Το κλειδί ασφαλείας δεν επιτρέπεται για αυτό το αίτημα. Βεβαιωθείτε ότι το κλειδί δεν έχει ήδη καταχωρηθεί.
|
||||
webauthn_error_empty=Πρέπει να ορίσετε ένα όνομα για αυτό το κλειδί.
|
||||
webauthn_error_timeout=Το χρονικό όριο έφτασε πριν το κλειδί να διαβαστεί. Παρακαλώ ανανεώστε τη σελίδα και προσπαθήστε ξανά.
|
||||
webauthn_u2f_deprecated=Το κλειδί: '%s' πιστοποιεί χρησιμοποιώντας το παρωχημένο πρωτόκολλο U2F. Θα πρέπει να καταχωρήσετε ξανά αυτό το κλειδί και να καταργήσετε την παλιά εγγραφή.
|
||||
webauthn_reload=Ανανέωση
|
||||
|
||||
repository=Αποθετήριο
|
||||
organization=Οργανισμός
|
||||
@@ -305,6 +319,9 @@ oauth_signup_submit=Ολοκληρωμένος Λογαριασμός
|
||||
oauth_signin_tab=Σύνδεση με υπάρχων λογαριασμό
|
||||
oauth_signin_title=Συνδεθείτε για να εγκρίνετε τον Συνδεδεμένο Λογαριασμό
|
||||
oauth_signin_submit=Σύνδεση Λογαριασμού
|
||||
oauth.signin.error=Παρουσιάστηκε σφάλμα κατά την επεξεργασία του αιτήματος εξουσιοδότησης. Εάν αυτό το σφάλμα επιμένει, παρακαλούμε επικοινωνήστε με το διαχειριστή του ιστοτόπου.
|
||||
oauth.signin.error.access_denied=Η αίτηση εξουσιοδότησης απορρίφθηκε.
|
||||
oauth.signin.error.temporarily_unavailable=Η εξουσιοδότηση απέτυχε επειδή ο διακομιστής ταυτοποίησης δεν είναι διαθέσιμος προσωρινά. Παρακαλώ προσπαθήστε ξανά αργότερα.
|
||||
openid_connect_submit=Σύνδεση
|
||||
openid_connect_title=Σύνδεση σε υπάρχων λογαριασμό
|
||||
openid_connect_desc=Το επιλεγμένο OpenID URI είναι άγνωστο. Συνδέστε το με ένα νέο λογαριασμό εδώ.
|
||||
@@ -510,6 +527,7 @@ twofa=Έλεγχος Ταυτότητας Δύο Παραγόντων
|
||||
account_link=Συνδεδεμένοι Λογαριασμοί
|
||||
organization=Οργανισμοί
|
||||
uid=Uid
|
||||
webauthn=Κλειδιά Ασφαλείας
|
||||
|
||||
public_profile=Δημόσιο Προφίλ
|
||||
biography_placeholder=Πείτε μας λίγο για τον εαυτό σας
|
||||
@@ -531,6 +549,7 @@ continue=Συνέχεια
|
||||
cancel=Ακύρωση
|
||||
language=Γλώσσα
|
||||
ui=Θέμα Διεπαφής
|
||||
saved_successfully=Οι ρυθμίσεις σας αποθηκεύτηκαν επιτυχώς.
|
||||
privacy=Απόρρητο
|
||||
keep_activity_private=Απόκρυψη της δραστηριότητας σας από τη σελίδα προφίλ
|
||||
keep_activity_private_popup=Με αυτή την επιλογή η δραστηριότητα σας είναι ορατή μόνο σε εσάς και τους διαχειριστές
|
||||
@@ -730,6 +749,11 @@ passcode_invalid=Ο κωδικός είναι λάθος. Δοκιμάστε ξ
|
||||
twofa_enrolled=Ο λογαριασμός σας έχει εγγραφεί σε ταυτοποίηση δύο παραγόντων. Αποθηκεύστε το διακριτικό μιας χρήσης (%s) σε ασφαλές μέρος καθώς εμφανίζεται μόνο μία φορά!
|
||||
twofa_failed_get_secret=Αποτυχία λήψης μυστικού.
|
||||
|
||||
webauthn_desc=Τα κλειδιά ασφαλείας είναι συσκευές που περιέχουν κρυπτογραφικά κλειδιά. Μπορούν να χρησιμοποιηθούν για έλεγχο ταυτότητας δύο παραγόντων. Τα κλειδιά ασφαλείας πρέπει να υποστηρίζουν το <a rel="noreferrer" target="_blank" href="https://w3c.github.io/webauthn/#webauthn-authenticator">πρότυπο WebAuthn Authn Authenticator</a>.
|
||||
webauthn_register_key=Προσθήκη Κλειδιού Ασφαλείας
|
||||
webauthn_nickname=Ψευδώνυμο
|
||||
webauthn_delete_key=Αφαίρεση Κλειδιού Ασφαλείας
|
||||
webauthn_delete_key_desc=Αν αφαιρέσετε ένα κλειδί ασφαλείας δεν μπορείτε πλέον να συνδεθείτε με αυτό. Συνέχεια;
|
||||
|
||||
manage_account_links=Διαχείριση Συνδεδεμένων Λογαριασμών
|
||||
manage_account_links_desc=Αυτοί οι εξωτερικοί λογαριασμοί είναι συνδεδεμένοι στον Gitea λογαριασμό σας.
|
||||
@@ -986,7 +1010,16 @@ file_view_rendered=Προβολή Απόδοσης
|
||||
file_view_raw=Προβολή Ακατέργαστου
|
||||
file_permalink=Permalink
|
||||
file_too_large=Το αρχείο είναι πολύ μεγάλο για να εμφανιστεί.
|
||||
bidi_bad_header=`Αυτό το αρχείο περιέχει μη αναμενόμενους χαρακτήρες Unicode!`
|
||||
bidi_bad_description=`Αυτό το αρχείο περιέχει μη αναμενόμενους χαρακτήρες Bidirectional Unicode που ίσως να επεξεργάζονται διαφορετικά από ότι εμφανίζεται παρακάτω. Αν η χρήση αυτή είναι σκόπιμη και νόμιμη, μπορείτε να αγνοήσετε με ασφάλεια αυτή την προειδοποίηση. Χρησιμοποιήστε το κουμπί Escape για να αποκαλύψετε κρυμμένους χαρακτήρες.`
|
||||
bidi_bad_description_escaped=`Αυτό το αρχείο περιέχει μη αναμενόμενους χαρακτήρες Bidirectional Unicode. Οι κρυμμένοι χαρακτήρες unicode εμφανίζονται κωδικοποιημένοι παρακάτω. Χρησιμοποιήστε το κουμπί Unescape για να δείτε πώς αποδίδονται.`
|
||||
unicode_header=`Αυτό το αρχείο περιέχει κρυφούς χαρακτήρες Unicode!`
|
||||
unicode_description=`Αυτό το αρχείο περιέχει κρυφούς χαρακτήρες Unicode που μπορεί να επεξεργάζονται διαφορετικά από όπως εμφανίζονται παρακάτω. Αν η χρήση είναι σκόπιμη και νόμιμη, μπορείτε να αγνοήσετε με ασφάλεια αυτή την προειδοποίηση. Χρησιμοποιήστε το κουμπί Escape για να αποκαλύψετε τους κρυφούς χαρακτήρες.`
|
||||
unicode_description_escaped=`Αυτό το αρχείο περιέχει κρυφούς χαρακτήρες Unicode. Οι κρυφοί χαρακτήρες unicode εμφανίζονται κωδικοποιημένοι παρακάτω. Χρησιμοποιήστε το κουμπί Unescape για να δείτε πώς αποδίδονται.`
|
||||
line_unicode=`Αυτή η γραμμή έχει κρυφούς χαρακτήρες unicode`
|
||||
|
||||
escape_control_characters=Escape
|
||||
unescape_control_characters=Unescape
|
||||
file_copy_permalink=Αντιγραφή Permalink
|
||||
video_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 'video'.
|
||||
audio_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 'audio'.
|
||||
@@ -1081,6 +1114,8 @@ commits.signed_by_untrusted_user_unmatched=Υπογράφηκε από ένα μ
|
||||
commits.gpg_key_id=ID Κλειδιού GPG
|
||||
commits.ssh_key_fingerprint=Αποτύπωμα Κλειδιού SSH
|
||||
|
||||
|
||||
ext_issues=Πρόσβαση στα Εξωτερικά Ζητήματα
|
||||
ext_issues.desc=Σύνδεση σε εξωτερικό εφαρμογή ζητημάτων.
|
||||
|
||||
projects=Έργα
|
||||
@@ -1560,6 +1595,7 @@ signing.wont_sign.commitssigned=Η συγχώνευση δεν θα υπογρα
|
||||
signing.wont_sign.approved=Η συγχώνευση δεν θα υπογραφεί καθώς το PR δεν εγκρίνεται
|
||||
signing.wont_sign.not_signed_in=Δεν είστε συνδεδεμένοι
|
||||
|
||||
ext_wiki=Πρόσβαση στο Εξωτερικό Wiki
|
||||
ext_wiki.desc=Σύνδεση σε ένα εξωτερικό wiki.
|
||||
|
||||
wiki=Wiki
|
||||
@@ -1816,6 +1852,8 @@ settings.webhook.response=Απάντηση
|
||||
settings.webhook.headers=Κεφαλίδες
|
||||
settings.webhook.payload=Περιεχόμενο
|
||||
settings.webhook.body=Σώμα
|
||||
settings.webhook.replay.description=Επανάληψη αυτού του webhook.
|
||||
settings.webhook.delivery.success=Ένα γεγονός έχει προστεθεί στην ουρά παράδοσης. Μπορεί να χρειαστούν λίγα δευτερόλεπτα μέχρι να εμφανιστεί στο ιστορικό.
|
||||
settings.githooks_desc=Τα Άγκιστρα Git παρέχονται από το ίδιο το Git. Μπορείτε να επεξεργαστείτε τα αρχεία αγκίστρων παρακάτω για να ρυθμίσετε προσαρμοσμένες λειτουργίες.
|
||||
settings.githook_edit_desc=Αν το hook είναι ανενεργό, θα παρουσιαστεί ένα παράδειγμα. Αφήνοντας το περιεχόμενο του hook κενό θα το απενεργοποιήσετε.
|
||||
settings.githook_name=Όνομα Hook
|
||||
@@ -2079,6 +2117,7 @@ diff.protected=Προστατευμένο
|
||||
diff.image.side_by_side=Δίπλα Δίπλα
|
||||
diff.image.swipe=Σύρσιμο
|
||||
diff.image.overlay=Επικάλυψη
|
||||
diff.has_escaped=Αυτή η γραμμή έχει κρυφούς χαρακτήρες Unicode
|
||||
|
||||
releases.desc=Παρακολούθηση εκδόσεων έργου και λήψεων.
|
||||
release.releases=Κυκλοφορίες
|
||||
@@ -2154,6 +2193,7 @@ branch.new_branch_from=Δημιουργία νέου κλάδου από '%s'
|
||||
branch.renamed=Ο κλάδος %s μετονομάστηκε σε %s.
|
||||
|
||||
tag.create_tag=Δημιουργία ετικέτας <strong>%s</strong>
|
||||
|
||||
tag.create_success=Η ετικέτα '%s' έχει δημιουργηθεί.
|
||||
|
||||
topic.manage_topics=Διαχείριση Θεμάτων
|
||||
@@ -2240,7 +2280,13 @@ teams.leave=Αποχώρηση
|
||||
teams.leave.detail=Αποχώρηση από %s;
|
||||
teams.can_create_org_repo=Δημιουργία αποθετηρίων
|
||||
teams.can_create_org_repo_helper=Τα μέλη μπορούν να δημιουργήσουν νέα αποθετήρια στον οργανισμό. Ο δημιουργός θα αποκτήσει πρόσβαση διαχειριστή στο νέο αποθετήριο.
|
||||
teams.none_access=Καμία Πρόσβαση
|
||||
teams.none_access_helper=Τα μέλη δεν μπορούν να δουν ή να κάνουν οποιαδήποτε άλλη ενέργεια σε αυτή τη μονάδα.
|
||||
teams.general_access=Γενική Πρόσβαση
|
||||
teams.general_access_helper=Τα δικαιώματα των μελών αποφασίζονται από το παρακάτω πίνακα αδειών.
|
||||
teams.read_access=Ανάγνωση
|
||||
teams.read_access_helper=Τα μέλη μπορούν να δουν και να κλωνοποιήσουν τα αποθετήρια της ομάδας.
|
||||
teams.write_access=Εγγραφή
|
||||
teams.write_access_helper=Τα μέλη μπορούν να δουν και να κλωνοποιήσουν τα αποθετήρια της ομάδας.
|
||||
teams.admin_access=Πρόσβαση Διαχειριστή
|
||||
teams.admin_access_helper=Τα μέλη μπορούν να κάνουν push και pull στα αποθετήρια της ομάδας όπως και να προσθέσουν συνεργάτες σε αυτά.
|
||||
@@ -2869,6 +2915,7 @@ error.probable_bad_signature=ΠΡΟΣΟΧΗ! Αν και υπάρχει ένα
|
||||
error.probable_bad_default_signature=ΠΡΟΣΟΧΗ! Αν και το προεπιλεγμένο κλειδί έχει αυτό το ID, δεν επαληθεύει αυτή την υποβολή! Αυτή η υποβολή είναι ΥΠΟΠΤΗ.
|
||||
|
||||
[units]
|
||||
unit=Μονάδα
|
||||
error.no_unit_allowed_repo=Δεν σας επιτρέπεται να έχετε πρόσβαση σε οποιαδήποτε ενότητα αυτού του αποθετηρίου.
|
||||
error.unit_not_allowed=Δεν σας επιτρέπεται να έχετε πρόσβαση σε αυτήν την ενότητα αποθετηρίου.
|
||||
|
||||
|
||||
@@ -34,6 +34,20 @@ twofa=2要素認証
|
||||
twofa_scratch=2要素認証スクラッチコード
|
||||
passcode=パスコード
|
||||
|
||||
webauthn_insert_key=セキュリティキーを挿入
|
||||
webauthn_sign_in=セキュリティキーのボタンを押してください。セキュリティキーにボタンが無い場合は、挿入しなおしてください。
|
||||
webauthn_press_button=セキュリティキーのボタンを押してください...
|
||||
webauthn_use_twofa=携帯電話から2要素認証コードを使用する
|
||||
webauthn_error=セキュリティキーを読み取ることができません。
|
||||
webauthn_unsupported_browser=お使いのブラウザは現在 WebAuthn をサポートしていません。
|
||||
webauthn_error_unknown=不明なエラーが発生しました。 もう一度やり直してください。
|
||||
webauthn_error_insecure=WebAuthn はセキュアな接続のみをサポートしています。HTTP 経由でテストする場合は、"localhost" または "127.0.0.1" のオリジンが使用できます。
|
||||
webauthn_error_unable_to_process=サーバーがリクエストを処理できませんでした。
|
||||
webauthn_error_duplicated=このリクエストに対しては、許可されていないセキュリティキーです。 キーが未登録であることを確認してください。
|
||||
webauthn_error_empty=このキーに名前を設定する必要があります。
|
||||
webauthn_error_timeout=キーを読み取る前にタイムアウトになりました。 このページをリロードしてもう一度やり直してください。
|
||||
webauthn_u2f_deprecated=キー: '%s' は非推奨のU2Fプロセスを使用して認証しています。このキーを再登録して古い登録を削除したほうが良いでしょう。
|
||||
webauthn_reload=リロード
|
||||
|
||||
repository=リポジトリ
|
||||
organization=組織
|
||||
@@ -513,6 +527,7 @@ twofa=2要素認証
|
||||
account_link=連携アカウント
|
||||
organization=組織
|
||||
uid=Uid
|
||||
webauthn=セキュリティキー
|
||||
|
||||
public_profile=公開プロフィール
|
||||
biography_placeholder=自己紹介を少しだけ
|
||||
@@ -534,6 +549,8 @@ continue=続行
|
||||
cancel=キャンセル
|
||||
language=言語
|
||||
ui=テーマ
|
||||
hidden_comment_types=非表示にするコメントの種類
|
||||
saved_successfully=設定は正常に保存されました。
|
||||
privacy=プライバシー
|
||||
keep_activity_private=プロフィールページのアクティビティ表示を隠す
|
||||
keep_activity_private_popup=アクティビティを、あなたと管理者にのみ表示します
|
||||
@@ -733,6 +750,11 @@ passcode_invalid=パスコードが間違っています。 再度お試しく
|
||||
twofa_enrolled=あなたのアカウントに2要素認証が設定されました。 スクラッチトークン (%s) は一度しか表示しませんので安全な場所に保存してください!
|
||||
twofa_failed_get_secret=シークレットが取得できません。
|
||||
|
||||
webauthn_desc=セキュリティキーは暗号化キーを内蔵するハードウェア ・ デバイスです。 2要素認証に使用できます。 セキュリティキーは<a rel="noreferrer" target="_blank" href="https://w3c.github.io/webauthn/#webauthn-authenticator">WebAuthn Authenticator</a>規格をサポートしている必要があります。
|
||||
webauthn_register_key=セキュリティキーを追加
|
||||
webauthn_nickname=ニックネーム
|
||||
webauthn_delete_key=セキュリティキーの登録解除
|
||||
webauthn_delete_key_desc=セキュリティキーの登録を解除すると、今後そのセキュリティキーでサインインすることはできなくなります。 続行しますか?
|
||||
|
||||
manage_account_links=連携アカウントの管理
|
||||
manage_account_links_desc=これらの外部アカウントがGiteaアカウントと連携されています。
|
||||
@@ -1038,6 +1060,10 @@ editor.add_tmpl='<ファイル名>' を追加
|
||||
editor.add='%s' を追加
|
||||
editor.update='%s' を更新
|
||||
editor.delete='%s' を削除
|
||||
editor.patch=パッチの適用
|
||||
editor.patching=パッチ:
|
||||
editor.fail_to_apply_patch=パッチを適用できません '%s'
|
||||
editor.new_patch=新しいパッチ
|
||||
editor.commit_message_desc=詳細な説明を追加…
|
||||
editor.signoff_desc=コミットログメッセージの最後にコミッターの Signed-off-by 行を追加
|
||||
editor.commit_directly_to_this_branch=ブランチ<strong class="branch-name">%s</strong>へ直接コミットする。
|
||||
@@ -1073,6 +1099,8 @@ editor.cannot_commit_to_protected_branch=保護されたブランチ '%s' にコ
|
||||
editor.no_commit_to_branch=ブランチに直接コミットすることはできません、なぜなら:
|
||||
editor.user_no_push_to_branch=ユーザーはブランチにプッシュできません
|
||||
editor.require_signed_commit=ブランチでは署名されたコミットが必須です
|
||||
editor.cherry_pick=チェリーピック %s:
|
||||
editor.revert=リバート %s:
|
||||
|
||||
commits.desc=ソースコードの変更履歴を参照します。
|
||||
commits.commits=コミット
|
||||
@@ -1093,6 +1121,7 @@ commits.signed_by_untrusted_user_unmatched=コミッターと一致しない信
|
||||
commits.gpg_key_id=GPGキーID
|
||||
commits.ssh_key_fingerprint=SSH鍵のフィンガープリント
|
||||
|
||||
|
||||
ext_issues=外部イシューへのアクセス
|
||||
ext_issues.desc=外部のイシュートラッカーへのリンク。
|
||||
|
||||
@@ -2314,6 +2343,7 @@ first_page=最初
|
||||
last_page=最後
|
||||
total=合計: %d
|
||||
|
||||
dashboard.new_version_hint=Gitea %s が入手可能になりました。 現在実行しているのは %s です。 詳細は <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">ブログ</a> を確認してください。
|
||||
dashboard.statistic=サマリー
|
||||
dashboard.operations=メンテナンス操作
|
||||
dashboard.system_status=システム状況
|
||||
@@ -2387,6 +2417,7 @@ dashboard.last_gc_pause=前回のGC停止時間
|
||||
dashboard.gc_times=GC実行回数
|
||||
dashboard.delete_old_actions=データベースから古い操作履歴をすべて削除
|
||||
dashboard.delete_old_actions.started=データベースからの古い操作履歴の削除を開始しました。
|
||||
dashboard.update_checker=更新チェック
|
||||
|
||||
users.user_manage_panel=ユーザーアカウント管理
|
||||
users.new_account=ユーザーアカウントを作成
|
||||
|
||||
@@ -837,7 +837,7 @@ default_branch=Ramo principal
|
||||
default_branch_helper=O ramo principal é o ramo base para pedidos de integração e cometimentos.
|
||||
mirror_prune=Podar
|
||||
mirror_prune_desc=Remover referências obsoletas de seguimento remoto
|
||||
mirror_interval=Intervalo de espelhamento (as unidade de tempo válidas são 'h', 'm' e 's'). O valor zero desabilita a sincronização automática.
|
||||
mirror_interval=Intervalo de espelhamento (as unidades de tempo válidas são 'h', 'm' e 's'). O valor zero desabilita a sincronização automática.
|
||||
mirror_interval_invalid=O intervalo do espelhamento não é válido.
|
||||
mirror_address=Clonar a partir do URL
|
||||
mirror_address_desc=Coloque, na secção de Autorização, as credenciais que, eventualmente, sejam necessárias.
|
||||
@@ -2334,6 +2334,7 @@ first_page=Primeira
|
||||
last_page=Última
|
||||
total=total: %d
|
||||
|
||||
dashboard.new_version_hint=O Gitea %s está agora disponível, você está a correr a versão %s. Verifique o <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">blog</a> para mais detalhes.
|
||||
dashboard.statistic=Resumo
|
||||
dashboard.operations=Operações de manutenção
|
||||
dashboard.system_status=Estado do sistema
|
||||
@@ -2407,6 +2408,7 @@ dashboard.last_gc_pause=Última pausa da recolha de lixo
|
||||
dashboard.gc_times=Tempos da recolha de lixo
|
||||
dashboard.delete_old_actions=Eliminar todas as operações antigas da base de dados
|
||||
dashboard.delete_old_actions.started=Foi iniciado o processo de eliminação de todas as operações antigas da base de dados.
|
||||
dashboard.update_checker=Verificador de novas versões
|
||||
|
||||
users.user_manage_panel=Gestão das contas de utilizadores
|
||||
users.new_account=Criar conta de utilizador
|
||||
|
||||
@@ -781,7 +781,7 @@ template_helper=Сделать репозиторий шаблоном
|
||||
template_description=Шаблонные репозитории дают возможность пользователям создавать новые репозитории с той же структурой каталогов, файлами и дополнительными настройками.
|
||||
visibility=Видимость
|
||||
visibility_description=Только владелец или члены организации, при наличии прав, смогут увидеть это.
|
||||
visibility_helper=Сделать репозиторий приватным
|
||||
visibility_helper=Сделать репозиторий частным
|
||||
visibility_helper_forced=Администратор сайта настроил параметр видимости новых репозиториев. Репозиторий приватный по умолчанию.
|
||||
visibility_fork_helper=(Изменение этого повлияет на все форки.)
|
||||
clone_helper=Нужна помощь в клонировании? Посетите страницу <a target="_blank" rel="noopener noreferrer" href="%s">помощи</a>.
|
||||
|
||||
+111
-105
@@ -34,6 +34,7 @@ twofa=Двофакторна авторизація
|
||||
twofa_scratch=Двофакторний одноразовий пароль
|
||||
passcode=Код доступу
|
||||
|
||||
webauthn_reload=Оновити
|
||||
|
||||
repository=Репозиторій
|
||||
organization=Організація
|
||||
@@ -61,7 +62,7 @@ forks=Форки
|
||||
|
||||
activities=Дії
|
||||
pull_requests=Запити на злиття
|
||||
issues=Проблеми
|
||||
issues=Задачі
|
||||
milestones=Етапи
|
||||
|
||||
ok=OK
|
||||
@@ -92,7 +93,9 @@ error404=Сторінка, до якої ви намагаєтеся зверн
|
||||
never=Ніколи
|
||||
|
||||
[error]
|
||||
occurred=Сталася помилка
|
||||
missing_csrf=Некоректний запит: токен CSRF не задано
|
||||
network_error=Помилка мережі
|
||||
|
||||
[startpage]
|
||||
app_desc=Зручний власний сервіс хостингу репозиторіїв Git
|
||||
@@ -346,7 +349,7 @@ reset_password.text=Перейдіть за цим посиланням, щоб
|
||||
register_success=Реєстрація успішна
|
||||
|
||||
issue_assigned.pull=@%[1]s призначив вам запит злиття %[2]s в репозиторії %[3]s.
|
||||
issue_assigned.issue=@%[1]s призначив вам завдання %[2]s у репозиторії %[3]s.
|
||||
issue_assigned.issue=@%[1]s призначив вам задачу %[2]s у репозиторії %[3]s.
|
||||
|
||||
issue.x_mentioned_you=<b>@%s</b> згадав вас:
|
||||
issue.action.force_push=<b>%[1]s</b> force-pushed <b>%[2]s</b> з %[3]s в %[4]s.
|
||||
@@ -474,7 +477,7 @@ activity=Публічна активність
|
||||
followers=Читачі
|
||||
starred=Обрані Репозиторії
|
||||
watched=Відстежувані репозиторії
|
||||
projects=Проекти
|
||||
projects=Проєкт
|
||||
following=Читає
|
||||
follow=Підписатися
|
||||
unfollow=Відписатися
|
||||
@@ -611,6 +614,7 @@ gpg_token_help=Ви можете створити підпис за допомо
|
||||
gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
|
||||
gpg_token_signature=Текстовий (armored) підпис GPG
|
||||
key_signature_gpg_placeholder=Починається з "-----BEGIN PGP SIGNATURE-----"
|
||||
ssh_token=Токен
|
||||
subkeys=Підключі
|
||||
key_id=ID ключа
|
||||
key_name=Ім'я ключа
|
||||
@@ -743,7 +747,7 @@ visibility.private=Приватний
|
||||
visibility.private_tooltip=Видимий лише членам організації
|
||||
|
||||
[repo]
|
||||
new_repo_helper=Репозиторій містить усі файли проекту, включаючи історію ревізій. Ще десь є? <a href="%s">Мігрувати репозиторій.</a>
|
||||
new_repo_helper=Репозиторій містить усі файли проєкту, включаючи історію ревізій. Ще десь є? <a href="%s">Мігрувати репозиторій.</a>
|
||||
owner=Власник
|
||||
owner_helper=Деякі організації можуть не відображатися у випадаючому списку через максимальну кількість репозиторііїв.
|
||||
repo_name=Назва репозиторію
|
||||
@@ -774,14 +778,14 @@ repo_desc_helper=Введіть короткий опис (опціональн
|
||||
repo_lang=Мова
|
||||
repo_gitignore_helper=Виберіть шаблон .gitignore.
|
||||
repo_gitignore_helper_desc=Оберіть з списку мовних шаблонів файли, які не будуть відстежуватись. Типові артефакти, які генеруються за допомогою інструментів побудови кожної мови, за замовчуванням включені до .gitignor.
|
||||
issue_labels=Мітки проблем
|
||||
issue_labels_helper=Вибрати мітку для проблеми.
|
||||
issue_labels=Мітки задачі
|
||||
issue_labels_helper=Вибрати мітку для задачі.
|
||||
license=Ліцензія
|
||||
license_helper=Виберіть ліцензійний файл.
|
||||
license_helper_desc=Ліцензія регулює те, що інші можуть і не можуть робити з вашим кодом. Не впевнені, що саме підходить для вашого проекту? Дивіться <a target="_blank" rel="noopener noreferrer" href="%s">Виберіть ліцензію.</a>
|
||||
license_helper_desc=Ліцензія регулює те, що інші можуть і не можуть робити з вашим кодом. Не впевнені, що саме підходить для вашого проєкту? Дивіться <a target="_blank" rel="noopener noreferrer" href="%s">Виберіть ліцензію.</a>
|
||||
readme=README
|
||||
readme_helper=Виберіть шаблон README.
|
||||
readme_helper_desc=Це місце, де ви можете написати повний опис вашого проекту.
|
||||
readme_helper_desc=Це місце, де ви можете написати повний опис вашого проєкту.
|
||||
auto_init=Ініціалізувати репозиторій (Додає .gitignore, LICENSE та README)
|
||||
trust_model_helper=Виберіть модель довіри для підтвердження підпису. Можливі варіанти:
|
||||
trust_model_helper_collaborator=Співавтор: підписи довіри від співавторів
|
||||
@@ -846,12 +850,12 @@ template.git_hooks=Перехоплювачі Git
|
||||
template.webhooks=Webhook'и
|
||||
template.topics=Теми
|
||||
template.avatar=Аватар
|
||||
template.issue_labels=Мітки проблем
|
||||
template.issue_labels=Мітки задачі
|
||||
template.one_item=Слід обрати хоча б один елемент шаблону
|
||||
template.invalid=Слід обрати шаблонний репозиторій
|
||||
|
||||
archive.title=Це архівний репозитарій. Ви можете переглядати і клонувати файли, але не можете робити пуш або відкривати питання/запити.
|
||||
archive.issue.nocomment=Це архівний репозитарій. Ви не можете коментувати запити.
|
||||
archive.title=Цей репозиторій архівовано. Ви можете переглядати файли та клонувати його, але не можете виконувати push чи відкривати задачі та запити злиття.
|
||||
archive.issue.nocomment=Цей репозиторій архівовано. Ви не можете коментувати задачі.
|
||||
archive.pull.nocomment=Це архівний репозитарій. Ви не можете коментувати пулл-реквести.
|
||||
|
||||
form.reach_limit_of_creation_1=Ви вже досягли ліміту в %d репозиторіїв.
|
||||
@@ -873,7 +877,7 @@ migrate_items=Деталі міграції
|
||||
migrate_items_wiki=Вікі
|
||||
migrate_items_milestones=Етапи
|
||||
migrate_items_labels=Мітки
|
||||
migrate_items_issues=Проблеми
|
||||
migrate_items_issues=Задачі
|
||||
migrate_items_pullrequests=Запити на злиття
|
||||
migrate_items_merge_requests=Запити на злиття
|
||||
migrate_items_releases=Релізи
|
||||
@@ -906,7 +910,7 @@ migrate.migrating_topics=Міграція тем
|
||||
migrate.migrating_milestones=Міграція етапів
|
||||
migrate.migrating_labels=Міграція міток
|
||||
migrate.migrating_releases=Міграція релізів
|
||||
migrate.migrating_issues=Міграція проблем
|
||||
migrate.migrating_issues=Міграція задач
|
||||
migrate.migrating_pulls=Міграція запитів на злиття
|
||||
|
||||
mirror_from=дзеркало
|
||||
@@ -939,7 +943,7 @@ filter_branch_and_tag=Фільтрувати гілку або тег
|
||||
find_tag=Знайти тег
|
||||
branches=Гілки
|
||||
tags=Теги
|
||||
issues=Проблеми
|
||||
issues=Задачі
|
||||
pulls=Запити на злиття
|
||||
project_board=Проєкти
|
||||
labels=Мітки
|
||||
@@ -1053,10 +1057,12 @@ commits.signed_by_untrusted_user=Підписаний недовіреним к
|
||||
commits.signed_by_untrusted_user_unmatched=Підписаний недовіреним користувачем, який не відповідає комітеру
|
||||
commits.gpg_key_id=Ідентифікатор GPG ключа
|
||||
|
||||
ext_issues.desc=Посилання на зовнішню систему відстеження проблем.
|
||||
|
||||
ext_issues=Доступ до зовнішніх задач
|
||||
ext_issues.desc=Посилання на зовнішню систему відстеження задач.
|
||||
|
||||
projects=Проєкти
|
||||
projects.desc=Керуйте проблемами та запитами злиття на дошках проєкту.
|
||||
projects.desc=Керуйте задачами та запитами злиття на дошках проєкту.
|
||||
projects.description=Опис (необов'язково)
|
||||
projects.description_placeholder=Опис
|
||||
projects.create=Створити проєкт
|
||||
@@ -1065,10 +1071,10 @@ projects.new=Новий проєкт
|
||||
projects.new_subheader=Координуйте, відстежуйте та оновлюйте інформацію про виконувану роботу в одному місці, аби проєкти залишалися прозорими та за розкладом.
|
||||
projects.create_success=Проєкт '%s' створено.
|
||||
projects.deletion=Видалити проєкт
|
||||
projects.deletion_desc=Видалення проєкту видаляє його з усіх пов'язаних проблем. Продовжити?
|
||||
projects.deletion_desc=Видалення проєкту видаляє його з усіх пов'язаних задач. Продовжити?
|
||||
projects.deletion_success=Проєкт видалено.
|
||||
projects.edit=Редагувати проєкти
|
||||
projects.edit_subheader=Проєкти призначені для організації проблем та відстеження поступу.
|
||||
projects.edit_subheader=Проєкти організовують задачі та відстежують прогрес.
|
||||
projects.modify=Оновити проєкт
|
||||
projects.edit_success=Проєкт '%s' оновлено.
|
||||
projects.type.none=Відсутній
|
||||
@@ -1083,9 +1089,9 @@ projects.board.new_title=Назва нової дошки
|
||||
projects.board.new_submit=Створити
|
||||
projects.board.new=Нова дошка
|
||||
projects.board.set_default=Встановити за замовчуванням
|
||||
projects.board.set_default_desc=Встановити цю дошку за замовчуванням для проблем без категорії та витягувань
|
||||
projects.board.set_default_desc=Встановити цю дошку за замовчуванням для задач без категорії та витягувань
|
||||
projects.board.delete=Видалити дошку
|
||||
projects.board.deletion_desc=Видалення дошки проєкту перенесе всі пов'язані проблеми в дошку 'Без категорії'. Продовжити?
|
||||
projects.board.deletion_desc=Видалення дошки проєкту перенесе всі пов'язані задачі в дошку 'Без категорії'. Продовжити?
|
||||
projects.board.color=Колір
|
||||
projects.open=Відкрити
|
||||
projects.close=Закрити
|
||||
@@ -1096,7 +1102,7 @@ issues.filter_milestones=Фільтр етапів
|
||||
issues.filter_projects=Фільтр проєктів
|
||||
issues.filter_labels=Фільтр міток
|
||||
issues.filter_reviewers=Фільтр рецензентів
|
||||
issues.new=Нова проблема
|
||||
issues.new=Нова задача
|
||||
issues.new.title_empty=Заголовок не може бути пустим
|
||||
issues.new.labels=Мітки
|
||||
issues.new.add_labels_title=Застосувати мітки
|
||||
@@ -1125,7 +1131,7 @@ issues.choose.get_started=Початок роботи
|
||||
issues.choose.blank=Типово
|
||||
issues.choose.blank_about=Створити задачу із шаблону за замовчуванням.
|
||||
issues.no_ref=Не вказана гілка або тег
|
||||
issues.create=Створити проблему
|
||||
issues.create=Створити задачу
|
||||
issues.new_label=Нова мітка
|
||||
issues.new_label_placeholder=Назва мітки
|
||||
issues.new_label_desc_placeholder=Опис
|
||||
@@ -1167,7 +1173,7 @@ issues.filter_milestone_no_select=Всі етапи
|
||||
issues.filter_assignee=Виконавець
|
||||
issues.filter_assginee_no_select=Всі виконавці
|
||||
issues.filter_type=Тип
|
||||
issues.filter_type.all_issues=Всі проблеми
|
||||
issues.filter_type.all_issues=Всі задачі
|
||||
issues.filter_type.assigned_to_you=Призначене вам
|
||||
issues.filter_type.created_by_you=Створено вами
|
||||
issues.filter_type.mentioning_you=Вас згадано
|
||||
@@ -1203,7 +1209,7 @@ issues.commented_at=`прокоментував(ла) <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Ви впевнені, що хочете видалити цей коментар?
|
||||
issues.context.copy_link=Скопіювати посилання
|
||||
issues.context.quote_reply=Цитувати відповідь
|
||||
issues.context.reference_issue=Посилання в новій проблемі
|
||||
issues.context.reference_issue=Посилання в новій задачі
|
||||
issues.context.edit=Редагувати
|
||||
issues.context.delete=Видалити
|
||||
issues.no_content=Тут ще немає жодного змісту.
|
||||
@@ -1214,15 +1220,15 @@ issues.close_comment_issue=Прокоментувати і закрити
|
||||
issues.reopen_issue=Відкрити знову
|
||||
issues.reopen_comment_issue=Прокоментувати та відкрити знову
|
||||
issues.create_comment=Коментар
|
||||
issues.closed_at=`закрив цю проблему <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`повторно відкрив цю проблему <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at=`згадано цю проблему в коміті <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_issue_from=`<a href="%[3]s">послався на цю проблему %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.closed_at=`закрив цю задачу <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`повторно відкрив цю задачу <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at=`згадано цю задачу в коміті <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_issue_from=`<a href="%[3]s">посилання на цю задачу %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_pull_from=`<a href="%[3]s">послався на цей запит злиття %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_closing_from=`<a href="%[3]s">послався на запит злиття %[4]s, який закриває цю проблему</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_reopening_from=`<a href="%[3]s">послався на запит злиття %[4]s, який повторно відкриває цю проблему</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_closed_from=`<a href="%[3]s">закрив цю проблему %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_reopened_from=`<a href="%[3]s">повторно відкрив цю проблему %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_closing_from=`<a href="%[3]s">згадав запит на злиття %[4]с, які закриють цю задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_reopening_from=`<a href="%[3]s">згадав запит на злиття %[4]с, які повторно відкриють цю задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_closed_from=`<a href="%[3]s">закрив цю задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_reopened_from=`<a href="%[3]s">повторно відкрито цю задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_from=`із %[1]s`
|
||||
issues.poster=Автор
|
||||
issues.collaborator=Співавтор
|
||||
@@ -1241,12 +1247,12 @@ issues.label_title=Назва мітки
|
||||
issues.label_description=Опис мітки
|
||||
issues.label_color=Колір мітки
|
||||
issues.label_count=%d міток
|
||||
issues.label_open_issues=%d відкритих проблем
|
||||
issues.label_open_issues=%d відкритих задач
|
||||
issues.label_edit=Редагувати
|
||||
issues.label_delete=Видалити
|
||||
issues.label_modify=Редагувати мітку
|
||||
issues.label_deletion=Видалити мітку
|
||||
issues.label_deletion_desc=Видалення мітки видаляє її з усіх обговорень. Продовжити?
|
||||
issues.label_deletion_desc=Видалення мітки видаляє її з усіх задач. Продовжити?
|
||||
issues.label_deletion_success=Мітку було видалено.
|
||||
issues.label.filter_sort.alphabetically=За алфавітом
|
||||
issues.label.filter_sort.reverse_alphabetically=З кінця алфавіту
|
||||
@@ -1259,29 +1265,29 @@ issues.subscribe=Підписатися
|
||||
issues.unsubscribe=Відписатися
|
||||
issues.lock=Блокування обговорення
|
||||
issues.unlock=Розблокування обговорення
|
||||
issues.lock.unknown_reason=Неможливо заблокувати проблему з невідомою причиною.
|
||||
issues.lock_duplicate=Проблема не може бути заблокованим двічі.
|
||||
issues.unlock_error=Не можливо розблокувати проблему, яка не заблокована.
|
||||
issues.lock.unknown_reason=Неможливо заблокувати задачу з невідомою причиною.
|
||||
issues.lock_duplicate=Задача не може бути заблокованим двічі.
|
||||
issues.unlock_error=Не можливо розблокувати задачу, яка не заблокована.
|
||||
issues.lock_with_reason=заблоковано як <strong>%s</strong> та обмежене обговорення для співавторів %s
|
||||
issues.lock_no_reason=заблоковано та обмежене обговорення для співавторів %s
|
||||
issues.unlock_comment=розблоковане обговорення %s
|
||||
issues.lock_confirm=Заблокувати
|
||||
issues.unlock_confirm=Розблокувати
|
||||
issues.lock.notice_1=- Інші користувачі не можуть додавати нові коментарі до цієї проблеми.
|
||||
issues.lock.notice_1=- Інші користувачі не можуть додавати нові коментарі до цієї задачі.
|
||||
issues.lock.notice_2=- Ви й інші співавтори, які мають доступ до цього репозиторію, можете залишати коментарі, які інші можуть бачити.
|
||||
issues.lock.notice_3=- Ви завжди зможете розблокувати цю проблему в майбутньому.
|
||||
issues.unlock.notice_1=- Кожен зможе прокоментувати це питання ще раз.
|
||||
issues.unlock.notice_2=- Ви завжди зможете заблокувати цю проблему в майбутньому.
|
||||
issues.lock.notice_3=- Ви завжди зможете розблокувати цю задачу в майбутньому.
|
||||
issues.unlock.notice_1=- Кожен зможе прокоментувати цю задачу ще раз.
|
||||
issues.unlock.notice_2=- Ви завжди зможете заблокувати цю задачу в майбутньому.
|
||||
issues.lock.reason=Причина блокування
|
||||
issues.lock.title=Заблокувати обговорення цієї проблеми.
|
||||
issues.unlock.title=Розблокувати обговорення цієї проблеми.
|
||||
issues.comment_on_locked=Ви не можете коментувати заблоковану проблему.
|
||||
issues.lock.title=Заблокувати обговорення цієї задачі.
|
||||
issues.unlock.title=Розблокувати обговорення цієї задачі.
|
||||
issues.comment_on_locked=Ви не можете коментувати заблоковану задачу.
|
||||
issues.tracker=Відстеження часу
|
||||
issues.start_tracking_short=Запустити таймер
|
||||
issues.start_tracking=Почати відстеження часу
|
||||
issues.start_tracking_history=`почав працювати %s`
|
||||
issues.tracker_auto_close=Таймер буде автоматично зупинено, коли ця проблема буде закрита
|
||||
issues.tracking_already_started=`Ви вже почали відстежувати час для <a href="%s">іншої проблеми</a>!`
|
||||
issues.tracker_auto_close=Таймер буде автоматично зупинено, коли ця задача буде закрита
|
||||
issues.tracking_already_started=`Ви вже почали відстежувати час для <a href="%s">іншої задачі</a>!`
|
||||
issues.stop_tracking=Зупинити таймер
|
||||
issues.stop_tracking_history=`перестав(-ла) працювати %s`
|
||||
issues.cancel_tracking=Скасувати
|
||||
@@ -1308,7 +1314,7 @@ issues.due_date_form=рррр-мм-дд
|
||||
issues.due_date_form_add=Додати дату завершення
|
||||
issues.due_date_form_edit=Редагувати
|
||||
issues.due_date_form_remove=Видалити
|
||||
issues.due_date_not_writer=Вам потрібен доступ до запису в репозиторії, щоб оновити дату завершення проблем.
|
||||
issues.due_date_not_writer=Вам потрібен доступ до запису в репозиторії, щоб оновити дату завершення задач.
|
||||
issues.due_date_not_set=Термін виконання не встановлений.
|
||||
issues.due_date_added=додав(ла) дату завершення %s %s
|
||||
issues.due_date_modified=термін змінено з %s %s на %s
|
||||
@@ -1316,7 +1322,7 @@ issues.due_date_remove=видалив(ла) дату завершення %s %s
|
||||
issues.due_date_overdue=Прострочено
|
||||
issues.due_date_invalid=Термін дії не дійсний або знаходиться за межами допустимого діапазону. Будь ласка використовуйте формат 'yyyy-mm-dd'.
|
||||
issues.dependency.title=Залежності
|
||||
issues.dependency.issue_no_dependencies=Ця проблема в даний час не має залежностей.
|
||||
issues.dependency.issue_no_dependencies=Ця задача тепер не має залежностей.
|
||||
issues.dependency.pr_no_dependencies=Цей запит на злиття в даний час не має залежностей.
|
||||
issues.dependency.add=Додати залежність…
|
||||
issues.dependency.cancel=Відмінити
|
||||
@@ -1324,24 +1330,24 @@ issues.dependency.remove=Видалити
|
||||
issues.dependency.remove_info=Видалити цю залежність
|
||||
issues.dependency.added_dependency=`додав нову залежність %s`
|
||||
issues.dependency.removed_dependency=`видалив залежність %s`
|
||||
issues.dependency.pr_closing_blockedby=Закриття цього запиту злиття заблоковано наступними проблемами
|
||||
issues.dependency.issue_closing_blockedby=Закриття цієї проблеми заблоковано наступними проблемами
|
||||
issues.dependency.issue_close_blocks=Ця проблема блокує закриття залежних проблем
|
||||
issues.dependency.pr_close_blocks=Цей пулл-реквест блокує закриття залежних проблем
|
||||
issues.dependency.issue_close_blocked=Вам потрібно закрити всі проблеми, що блокують цю проблему, перед її закриттям.
|
||||
issues.dependency.pr_close_blocked=Вам потрібно закрити всі проблеми, що блокують цей пулл-реквест, перед його злиттям.
|
||||
issues.dependency.pr_closing_blockedby=Закриття цього запиту злиття заблоковано наступними задачами
|
||||
issues.dependency.issue_closing_blockedby=Закриття цієї задачи заблоковано наступними задачами
|
||||
issues.dependency.issue_close_blocks=Ця задача блокує закриття залежних задач
|
||||
issues.dependency.pr_close_blocks=Цей запит на злиття блокує закриття залежних задач
|
||||
issues.dependency.issue_close_blocked=Вам потрібно закрити всі задачі, що блокують цю задачу, перед її закриттям.
|
||||
issues.dependency.pr_close_blocked=Вам потрібно закрити всі задачі, що блокують цей запит, перед його злиттям.
|
||||
issues.dependency.blocks_short=Блоки
|
||||
issues.dependency.blocked_by_short=Залежить від
|
||||
issues.dependency.remove_header=Видалити залежність
|
||||
issues.dependency.issue_remove_text=Це призведе до видалення залежності з цієї проблеми. Продовжити?
|
||||
issues.dependency.issue_remove_text=Це призведе до видалення залежності з цієї задачі. Продовжити?
|
||||
issues.dependency.pr_remove_text=Це призведе до видалення залежності з цього пулл-реквесту. Продовжити?
|
||||
issues.dependency.setting=Увімкнути залежності для проблем та пулл-реквестів
|
||||
issues.dependency.add_error_same_issue=Ви не можете зробити проблему залежною від себе.
|
||||
issues.dependency.add_error_dep_issue_not_exist=Залежність для проблеми не існує.
|
||||
issues.dependency.setting=Увімкнути залежності для задач та запитів на злиття
|
||||
issues.dependency.add_error_same_issue=Ви не можете зробити задачу залежною від себе.
|
||||
issues.dependency.add_error_dep_issue_not_exist=Залежність для задачі не існує.
|
||||
issues.dependency.add_error_dep_not_exist=Залежність не існує.
|
||||
issues.dependency.add_error_dep_exists=Залежність уже існує.
|
||||
issues.dependency.add_error_cannot_create_circular=Ви не можете створити залежність з двома проблемами, які блокують одна одну.
|
||||
issues.dependency.add_error_dep_not_same_repo=Обидві проблеми повинні бути в одному репозиторії.
|
||||
issues.dependency.add_error_cannot_create_circular=Ви не можете створити залежність з двома задачами, які блокують одна одну.
|
||||
issues.dependency.add_error_dep_not_same_repo=Обидві задачі повинні бути в одному репозиторії.
|
||||
issues.review.self.approval=Ви не можете схвалити власний пулл-реквест.
|
||||
issues.review.self.rejection=Ви не можете надіслати запит на зміну на власний пулл-реквест.
|
||||
issues.review.approve=зміни затверджено %s
|
||||
@@ -1475,7 +1481,7 @@ pulls.closed_at=`закрив цей запит на злиття <a id="%[1]s"
|
||||
pulls.reopened_at=`повторно відкрив цей запит на злиття <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
pulls.merge_instruction_hint=`Також можна переглянути <a class="show-instruction">інструкції для командного рядка</a>.`
|
||||
|
||||
pulls.merge_instruction_step1_desc=У репозиторії вашого проекту перевірте нову гілку і протестуйте зміни.
|
||||
pulls.merge_instruction_step1_desc=У репозиторії вашого проєкту перевірте нову гілку і протестуйте зміни.
|
||||
pulls.merge_instruction_step2_desc=Об'єднати зміни і оновити на Gitea.
|
||||
|
||||
milestones.new=Новий етап
|
||||
@@ -1486,7 +1492,7 @@ milestones.update_ago=Оновлено %s назад
|
||||
milestones.no_due_date=Немає дати завершення
|
||||
milestones.open=Відкрити
|
||||
milestones.close=Закрити
|
||||
milestones.new_subheader=Створюйте етапи для організації ваших завдань.
|
||||
milestones.new_subheader=Створюйте етапи для організації ваших задач.
|
||||
milestones.completeness=%d%% завершено
|
||||
milestones.create=Створити етап
|
||||
milestones.title=Заголовок
|
||||
@@ -1496,19 +1502,19 @@ milestones.clear=Очистити
|
||||
milestones.invalid_due_date_format=Дата завершення має бути в форматі 'рррр-мм-дд'.
|
||||
milestones.create_success=Етап '%s' створений.
|
||||
milestones.edit=Редагувати етап
|
||||
milestones.edit_subheader=Використовуйте кращий опис контрольної точки, щоб уникнути нерозуміння з боку інших людей.
|
||||
milestones.edit_subheader=Створюйте етапи для організації ваших задач.
|
||||
milestones.cancel=Відмінити
|
||||
milestones.modify=Оновити етап
|
||||
milestones.edit_success=Етап '%s' був оновлений.
|
||||
milestones.deletion=Видалити етап
|
||||
milestones.deletion_desc=Видалення етапу призведе до його видалення з усіх пов'язаних завдань. Продовжити?
|
||||
milestones.deletion_desc=Видалення етапу призведе до його видалення з усіх пов'язаних задач. Продовжити?
|
||||
milestones.deletion_success=Етап успішно видалено.
|
||||
milestones.filter_sort.closest_due_date=Найближче за датою
|
||||
milestones.filter_sort.furthest_due_date=Далі за датою
|
||||
milestones.filter_sort.least_complete=Менш повне
|
||||
milestones.filter_sort.most_complete=Більш повне
|
||||
milestones.filter_sort.most_issues=Найбільш проблем
|
||||
milestones.filter_sort.least_issues=Найменш проблем
|
||||
milestones.filter_sort.most_issues=Найбільш задач
|
||||
milestones.filter_sort.least_issues=Найменш задач
|
||||
|
||||
signing.will_sign=Цей коміт буде підписано ключем '%s'
|
||||
signing.wont_sign.error=Під час підписання коміту, сталася помилка
|
||||
@@ -1574,21 +1580,21 @@ activity.title.prs_merged_by=%s злито %s
|
||||
activity.title.prs_opened_by=%s запропоновано %s
|
||||
activity.merged_prs_label=Злито
|
||||
activity.opened_prs_label=Запропоновано
|
||||
activity.active_issues_count_1=<strong>%d</strong> Активна проблема
|
||||
activity.active_issues_count_n=<strong>%d</strong> Активні проблеми
|
||||
activity.closed_issues_count_1=Закрита проблема
|
||||
activity.closed_issues_count_n=Закриті проблеми
|
||||
activity.title.issues_1=%d Проблема
|
||||
activity.title.issues_n=%d Проблеми
|
||||
activity.active_issues_count_1=<strong>%d</strong> Активна задача
|
||||
activity.active_issues_count_n=<strong>%d</strong> Активні задачі
|
||||
activity.closed_issues_count_1=Закрита задача
|
||||
activity.closed_issues_count_n=Закриті задачі
|
||||
activity.title.issues_1=%d Задач
|
||||
activity.title.issues_n=%d Задач
|
||||
activity.title.issues_closed_from=%s закрито %s
|
||||
activity.title.issues_created_by=%s створена(і) %s
|
||||
activity.closed_issue_label=Закрито
|
||||
activity.new_issues_count_1=Нова Проблема
|
||||
activity.new_issues_count_n=%d Проблем
|
||||
activity.new_issues_count_1=Нова задача
|
||||
activity.new_issues_count_n=Нові Задачі
|
||||
activity.new_issue_label=Відкриті
|
||||
activity.title.unresolved_conv_1=%d Незавершене обговорення
|
||||
activity.title.unresolved_conv_n=%d Незавершених обговорень
|
||||
activity.unresolved_conv_desc=Список всіх старих тікетів і Pull Request'ів з недавньої активністю, але ще не закритих або прийнятих.
|
||||
activity.unresolved_conv_desc=Список всіх старих задач і Pull Request'ів з недавньої активністю, але ще не закритих або прийнятих.
|
||||
activity.unresolved_conv_label=Відкрити
|
||||
activity.title.releases_1=%d Реліз
|
||||
activity.title.releases_n=%d Релізів
|
||||
@@ -1635,7 +1641,7 @@ settings.hooks=Веб-хуки
|
||||
settings.githooks=Git хуки
|
||||
settings.basic_settings=Базові налаштування
|
||||
settings.mirror_settings=Налаштування дзеркала
|
||||
settings.mirror_settings.docs=Налаштуйте свій проект, щоб автоматично відправляти/отримувати зміни з іншого репозиторію. Гілки, теги і коміти будуть синхронізуватися автоматично. <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/repo-mirror/">Як я можу відзеркалити репозиторії?</a>
|
||||
settings.mirror_settings.docs=Налаштуйте свій проєкт, щоб автоматично відправляти/отримувати зміни з іншого репозиторію. Гілки, теги та коміти будуть синхронізуватися автоматично. <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/repo-mirror/">Як я можу відзеркалити репозиторії?</a>
|
||||
settings.mirror_settings.mirrored_repository=Віддзеркалений репозиторій
|
||||
settings.mirror_settings.direction=Напрямок
|
||||
settings.mirror_settings.direction.pull=Pull
|
||||
@@ -1660,12 +1666,12 @@ settings.use_external_wiki=Використовувати зовнішні Ві
|
||||
settings.external_wiki_url=URL зовнішньої вікі
|
||||
settings.external_wiki_url_error=Зовнішня URL-адреса wiki не є допустимою URL-адресою.
|
||||
settings.external_wiki_url_desc=Відвідувачі будуть перенаправлені на URL-адресу, коли вони клацають по вкладці.
|
||||
settings.issues_desc=Увімкнути відстеження проблем в репозиторію
|
||||
settings.use_internal_issue_tracker=Використовувати вбудовану систему відстеження проблем
|
||||
settings.use_external_issue_tracker=Використовувати зовнішню систему обліку завдань
|
||||
settings.external_tracker_url=URL зовнішньої системи відстеження проблем
|
||||
settings.issues_desc=Увімкнути відстеження задач в репозиторію
|
||||
settings.use_internal_issue_tracker=Використовувати вбудовану систему відстеження задач
|
||||
settings.use_external_issue_tracker=Використовувати зовнішню систему обліку задач
|
||||
settings.external_tracker_url=URL зовнішньої системи відстеження задач
|
||||
settings.external_tracker_url_error=URL зовнішнього баг-трекера не є допустимою URL-адресою.
|
||||
settings.external_tracker_url_desc=Відвідувачі перенаправляються на зовнішню URL-адресу, коли натискають вкладку 'Проблеми'.
|
||||
settings.external_tracker_url_desc=Відвідувачі перенаправляються на зовнішню URL-адресу, коли натискають вкладку 'Задачі'.
|
||||
settings.tracker_url_format=Формат URL зовнішнього трекера задач
|
||||
settings.tracker_url_format_error=Неправильний формат URL-адреси зовнішнього баг-трекера.
|
||||
settings.tracker_issue_style=Формат номеру для зовнішньої системи обліку задач
|
||||
@@ -1686,7 +1692,7 @@ settings.pulls.default_delete_branch_after_merge=Видаляти гілку з
|
||||
settings.projects_desc=Увімкнути проєкти у репозиторії
|
||||
settings.admin_settings=Налаштування адміністратора
|
||||
settings.admin_enable_health_check=Включити перевірки працездатності репозиторію (git fsck)
|
||||
settings.admin_enable_close_issues_via_commit_in_any_branch=Закрити проблему за допомогою коміта, зробленого не головній гілці
|
||||
settings.admin_enable_close_issues_via_commit_in_any_branch=Закрити задачу за допомогою коміта, зробленого не в головній гілці
|
||||
settings.danger_zone=Небезпечна зона
|
||||
settings.new_owner_has_same_repo=Новий власник вже має репозиторій з такою назвою. Будь ласка, виберіть інше ім'я.
|
||||
settings.convert=Перетворити на звичайний репозиторій
|
||||
@@ -1736,7 +1742,7 @@ settings.wiki_deletion_success=Дані wiki були видалені.
|
||||
settings.delete=Видалити цей репозиторій
|
||||
settings.delete_desc=Будьте уважні! Як тільки ви видалите репозиторій - шляху назад не буде.
|
||||
settings.delete_notices_1=- Цю операцію <strong>НЕ МОЖНА</strong> відмінити.
|
||||
settings.delete_notices_2=- Ця операція назавжди видалить все з репозиторію <strong>%s</strong>, включаючи дані Git, пов'язані з ним завдання, коментарі і права доступу для співробітників.
|
||||
settings.delete_notices_2=- Ця операція остаточно видалить <strong>%s</strong> репозиторій, включаючи код, задачі, коментарі, вікі та налаштування співавторів.
|
||||
settings.delete_notices_fork_1=- Всі форки стануть незалежними репозиторіями після видалення.
|
||||
settings.deletion_success=Репозиторій успішно видалено.
|
||||
settings.update_settings_success=Налаштування репозиторію було оновлено.
|
||||
@@ -1806,16 +1812,16 @@ settings.event_push_desc=Git push до репозиторію.
|
||||
settings.event_repository=Репозиторій
|
||||
settings.event_repository_desc=Репозиторій створений або видалено.
|
||||
settings.event_header_issue=Події задачі
|
||||
settings.event_issues=Проблеми
|
||||
settings.event_issues_desc=Проблема відкрита, закрита, повторно відкрита або відредагована.
|
||||
settings.event_issue_assign=Проблема прив'язана
|
||||
settings.event_issue_assign_desc=Проблема призначена або скасована.
|
||||
settings.event_issue_label=Проблема з міткою
|
||||
settings.event_issue_label_desc=Мітки проблем оновлено або видалено.
|
||||
settings.event_issue_milestone=Проблеми етапу
|
||||
settings.event_issue_milestone_desc=Проблема призначена на етап або видалена з етапу.
|
||||
settings.event_issue_comment=Коментар проблеми
|
||||
settings.event_issue_comment_desc=Коментар проблеми створено, видалено чи відредаговано.
|
||||
settings.event_issues=Задачі
|
||||
settings.event_issues_desc=Задача відкрита, закрита, повторно відкрита або відредагована.
|
||||
settings.event_issue_assign=Задача прив'язана
|
||||
settings.event_issue_assign_desc=Задачу призначено або скасовано.
|
||||
settings.event_issue_label=Задача з міткою
|
||||
settings.event_issue_label_desc=Мітки задачі оновлено або видалено.
|
||||
settings.event_issue_milestone=Задача з етапом
|
||||
settings.event_issue_milestone_desc=Задача призначена на етап або видалена з етапу.
|
||||
settings.event_issue_comment=Коментар задачі
|
||||
settings.event_issue_comment_desc=Коментар задачі створено, видалено чи відредаговано.
|
||||
settings.event_header_pull_request=Події запиту злиття
|
||||
settings.event_pull_request=Запити до злиття
|
||||
settings.event_pull_request_desc=Запит до злиття відкрито, закрито, перевідкрито або відредаговано.
|
||||
@@ -1942,7 +1948,7 @@ settings.matrix.access_token=Токен Доступу
|
||||
settings.matrix.message_type=Тип повідомлення
|
||||
settings.archive.button=Архівний репозиторій
|
||||
settings.archive.header=Відправити репозиторій в архів
|
||||
settings.archive.text=Архівування репозиторія зробить його доступним лише для читання. Він не відображається на панелі, в нього не можуть вноситись зміни і не можна створювати запити з проблем та пулл-реквести.
|
||||
settings.archive.text=Архівування репозиторія зробить його доступним лише для читання. Він не відображається на панелі, в нього не можуть вноситись зміни і не можна створювати запити з задач та пулл-реквести.
|
||||
settings.archive.success=Репозиторію успішно присвоєно статус архівного.
|
||||
settings.archive.error=Сталася помилка при спробі архівувати репозиторій. Докладнішу інформацію див. у журналі.
|
||||
settings.archive.error_ismirror=Неможливо архівувати дзеркальний репозиротрій.
|
||||
@@ -1950,7 +1956,7 @@ settings.archive.branchsettings_unavailable=Параметри гілки не
|
||||
settings.archive.tagsettings_unavailable=Параметри міток недоступні, якщо репозиторій архівний.
|
||||
settings.unarchive.button=Зняти архівний статус
|
||||
settings.unarchive.header=Зняти архівний статус для репозиторія
|
||||
settings.unarchive.text=Зняття статусу архівного відновить запис в репозиторій, а також відкриє можливість створювати запити з нових проблем та пулл-запити.
|
||||
settings.unarchive.text=Зняття статусу архівного відновить запис в репозиторій, а також відкриє можливість створювати запити з нових задачах та пулл-запити.
|
||||
settings.unarchive.success=Статус архівний успішно знято.
|
||||
settings.unarchive.error=Сталася помилка при спробі скасувати архівний статус репозиторія. Докладнішу інформацію див. у журналі.
|
||||
settings.update_avatar_success=Аватар репозиторію оновлений.
|
||||
@@ -2037,7 +2043,7 @@ diff.image.side_by_side=Пліч-о-пліч
|
||||
diff.image.swipe=Свайп
|
||||
diff.image.overlay=Оверлей
|
||||
|
||||
releases.desc=Відслідковувати версії проекту (релізи) та завантаження.
|
||||
releases.desc=Відслідковувати версії проєкту і завантаження.
|
||||
release.releases=Релізи
|
||||
release.detail=Деталі релізу
|
||||
release.tags=Теги
|
||||
@@ -2050,8 +2056,8 @@ release.edit=редагувати
|
||||
release.ahead.commits=<strong>%d</strong> коміт(ів)
|
||||
release.ahead.target=до %s з моменту цього випуску
|
||||
release.source_code=Код
|
||||
release.new_subheader=Публікація релізів допоможе зберігати чітку історію розвитку вашого проекту.
|
||||
release.edit_subheader=Публікація релізів допоможе зберігати чітку історію розвитку вашого проекту.
|
||||
release.new_subheader=Публікація релізів допоможе вам організувати версію проєкту.
|
||||
release.edit_subheader=Публікація релізів допоможе вам організувати версію проєкту.
|
||||
release.tag_name=Назва тегу
|
||||
release.target=Ціль
|
||||
release.tag_helper=Виберіть існуючий тег або створіть новий.
|
||||
@@ -2175,7 +2181,7 @@ settings.delete_org_title=Видалити організацію
|
||||
settings.delete_org_desc=Ця організація буде безповоротно видалена. Продовжити?
|
||||
settings.hooks_desc=Додайте webhooks, який буде викликатися для <strong>всіх репозиторіїв</strong> якими володіє ця організація.
|
||||
|
||||
settings.labels_desc=Додайте мітки, які можуть використовуватися для <strong>всіх репозиторіїв</strong> цієї органцізації.
|
||||
settings.labels_desc=Додати мітки, які можуть бути використані для задач для <strong>всіх репозиторіїв</strong> в цій організації.
|
||||
|
||||
members.membership_visibility=Видимість учасника:
|
||||
members.public=Показувати
|
||||
@@ -2400,7 +2406,7 @@ repos.private=Приватний
|
||||
repos.watches=Стежать
|
||||
repos.stars=В обраному
|
||||
repos.forks=Форки
|
||||
repos.issues=Проблеми
|
||||
repos.issues=Задачі
|
||||
repos.size=Розмір
|
||||
|
||||
defaulthooks=Веб-хуки за замовчуванням
|
||||
@@ -2583,7 +2589,7 @@ config.default_enable_timetracking=Увімкнути відстеження ч
|
||||
config.default_allow_only_contributors_to_track_time=Враховувати тільки учасників розробки в підрахунку часу
|
||||
config.no_reply_address=Прихований домен електронної пошти
|
||||
config.default_visibility_organization=Видимість за замовчуванням для нових організацій
|
||||
config.default_enable_dependencies=Увімкнути залежності проблем за замовчуванням
|
||||
config.default_enable_dependencies=Увімкнути залежності задачі за замовчуванням
|
||||
|
||||
config.webhook_config=Конфігурація web-хуків
|
||||
config.queue_length=Довжина черги
|
||||
@@ -2738,13 +2744,13 @@ notices.delete_success=Сповіщення системи були видале
|
||||
create_repo=створив(ла) репозиторій <a href="%s">%s</a>
|
||||
rename_repo=репозиторій перейменовано з <code>%[1]s</code> на <a href="%[2]s">%[3]s</a>
|
||||
commit_repo=надіслав зміни (push) до <a href="%[2]s">%[3]s</a> о <a href="%[1]s">%[4]s</a>
|
||||
create_issue=`відкрив проблему <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
close_issue=`закрив проблему <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
reopen_issue=`повторно відкрив проблему <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
create_issue=`відкрив задачу <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
close_issue=`закрив задачу <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
reopen_issue=`повторно відкрив задачу <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
create_pull_request=`створив запит злиття <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
close_pull_request=`закрив запит злиття <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
reopen_pull_request=`повторно відкрив запит злиття <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
comment_issue=`прокоментував проблему <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
comment_issue=`прокоментував задачу <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
comment_pull=`прокоментував запит злиття <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
merge_pull_request=`прийняв запит злиття <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
transfer_repo=перенесено репозиторій <code>%s</code> у <a href="%s">%s</a>
|
||||
|
||||
@@ -673,8 +673,8 @@ last_used=上次使用在
|
||||
no_activity=没有最近活动
|
||||
can_read_info=读取
|
||||
can_write_info=写入
|
||||
key_state_desc=7 天内使用过该密钥
|
||||
token_state_desc=7 天内使用过该密钥
|
||||
key_state_desc=7 天内使用过该密钥
|
||||
token_state_desc=7 天内使用过该密钥
|
||||
principal_state_desc=7 天内使用过该规则
|
||||
show_openid=在个人信息上显示
|
||||
hide_openid=在个人信息上隐藏
|
||||
@@ -855,7 +855,7 @@ watchers=关注者
|
||||
stargazers=称赞者
|
||||
forks=派生仓库
|
||||
pick_reaction=选择你的表情
|
||||
reactions_more=再加载 %d
|
||||
reactions_more=再加载 %d
|
||||
unit_disabled=站点管理员已禁用此仓库单元。
|
||||
language_other=其它
|
||||
adopt_search=输入用户名以搜索未被收录的仓库... (留空以查找全部)
|
||||
@@ -940,14 +940,14 @@ migrate.migrating=正在从 <b>%s</b> 迁移...
|
||||
migrate.migrating_failed=从 <b>%s</b> 迁移失败。
|
||||
migrate.migrating_failed.error=错误:%s
|
||||
migrate.migrating_failed_no_addr=迁移失败。
|
||||
migrate.github.description=从 github.com 或其他 GitHub 实例迁移数据。
|
||||
migrate.github.description=从 github.com 或其他 GitHub 实例迁移数据
|
||||
migrate.git.description=从任意 Git 服务迁移仓库。
|
||||
migrate.gitlab.description=从 gitlab.com 或其他 GitLab 实例迁移数据。
|
||||
migrate.gitea.description=从 gitea.com 或其他 Gitea 实例迁移数据。
|
||||
migrate.gitlab.description=从 gitlab.com 或其他 GitLab 实例迁移数据
|
||||
migrate.gitea.description=从 gitea.com 或其他 Gitea 实例迁移数据
|
||||
migrate.gogs.description=从 notabug.org 或其他 Gogs 实例迁移数据。
|
||||
migrate.onedev.description=从 code.onedev.io 或其他 OneDev 实例迁移数据。
|
||||
migrate.codebase.description=从 codebasehq.com 迁移数据。
|
||||
migrate.gitbucket.description=从 GitBucket 实例迁移数据。
|
||||
migrate.onedev.description=从 code.onedev.io 或其他 OneDev 实例迁移数据
|
||||
migrate.codebase.description=从 codebasehq.com 迁移数据
|
||||
migrate.gitbucket.description=从 GitBucket 实例迁移数据
|
||||
migrate.migrating_git=迁移Git数据
|
||||
migrate.migrating_topics=迁移主题
|
||||
migrate.migrating_milestones=迁移里程碑
|
||||
@@ -1729,7 +1729,7 @@ settings.use_internal_wiki=使用内置百科
|
||||
settings.use_external_wiki=使用外部百科
|
||||
settings.external_wiki_url=外部 Wiki 链接
|
||||
settings.external_wiki_url_error=外部百科链接无效
|
||||
settings.external_wiki_url_desc=当点击工单标签时,访问者将被重定向到外部工单系统的URL。
|
||||
settings.external_wiki_url_desc=当点击百科标签时,访问者将被重定向到外部百科系统的URL。
|
||||
settings.issues_desc=启用工单系统
|
||||
settings.use_internal_issue_tracker=使用内置的轻量级工单管理系统
|
||||
settings.use_external_issue_tracker=使用外部的工单管理系统
|
||||
@@ -2334,7 +2334,7 @@ first_page=首页
|
||||
last_page=末页
|
||||
total=总计:%d
|
||||
|
||||
dashboard.new_version_hint = Gitea %s 可以更新了,您正在运行 %s。请检查 <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">博客</a> 查看更多详情。
|
||||
dashboard.new_version_hint=Gitea %s 现已可用,您正在运行 %s。查看 <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">博客</a> 了解更多详情。
|
||||
dashboard.statistic=摘要
|
||||
dashboard.operations=维护操作
|
||||
dashboard.system_status=系统状态
|
||||
@@ -2408,6 +2408,7 @@ dashboard.last_gc_pause=上次 GC 暂停时间
|
||||
dashboard.gc_times=GC 执行次数
|
||||
dashboard.delete_old_actions=从数据库中删除所有旧操作记录
|
||||
dashboard.delete_old_actions.started=已开始从数据库中删除所有旧操作记录。
|
||||
dashboard.update_checker=更新检查器
|
||||
|
||||
users.user_manage_panel=用户帐户管理
|
||||
users.new_account=创建新帐户
|
||||
|
||||
@@ -34,6 +34,20 @@ twofa=兩步驟驗證
|
||||
twofa_scratch=兩步驟驗證備用驗證碼
|
||||
passcode=驗證碼
|
||||
|
||||
webauthn_insert_key=插入您的安全金鑰
|
||||
webauthn_sign_in=按下您安全金鑰上的按鈕。如果您的安全金鑰沒有按鈕,請重新插入。
|
||||
webauthn_press_button=請按下您安全金鑰上的按鈕…
|
||||
webauthn_use_twofa=使用來自手機的兩步驟驗證碼
|
||||
webauthn_error=無法讀取您的安全金鑰。
|
||||
webauthn_unsupported_browser=您的瀏覽器還不支援 WebAuthn。
|
||||
webauthn_error_unknown=發生未知的錯誤,請再試一次。
|
||||
webauthn_error_insecure=WebAuthn 只支援安全連線。想在 HTTP 上測試,您可以使用「localhost」或「127.0.0.1」
|
||||
webauthn_error_unable_to_process=伺服器無法執行您的請求。
|
||||
webauthn_error_duplicated=此請求不允許使用這個安全金鑰。請確保該金鑰尚未註冊。
|
||||
webauthn_error_empty=您必須命名此金鑰。
|
||||
webauthn_error_timeout=在成功讀取金鑰之前已逾時,請重新載入此頁面並重試。
|
||||
webauthn_u2f_deprecated=「%s」金鑰使用已廢棄的 U2F 流程進行驗證。您應該重新註冊此金鑰並將先前註冊的移除。
|
||||
webauthn_reload=重新載入
|
||||
|
||||
repository=儲存庫
|
||||
organization=組織
|
||||
@@ -389,8 +403,8 @@ repo.collaborator.added.subject=%s 把您加入到 %s
|
||||
repo.collaborator.added.text=您已被新增為儲存庫的協作者:
|
||||
|
||||
[modal]
|
||||
yes=確認操作
|
||||
no=取消操作
|
||||
yes=是
|
||||
no=否
|
||||
modify=更新
|
||||
|
||||
[form]
|
||||
@@ -513,10 +527,11 @@ twofa=兩步驟驗證
|
||||
account_link=已連結帳號
|
||||
organization=組織
|
||||
uid=用戶 ID
|
||||
webauthn=安全金鑰
|
||||
|
||||
public_profile=公開的個人資料
|
||||
biography_placeholder=告訴我們一些關於你的事
|
||||
profile_desc=您的電子信箱將被用於通知提醒和其他操作。
|
||||
profile_desc=您的電子信箱將被用於通知提醒和其他作業。
|
||||
password_username_disabled=非本地使用者不允許更改他們的帳號。詳細資訊請聯絡您的系統管理員。
|
||||
full_name=全名
|
||||
website=個人網站
|
||||
@@ -530,7 +545,7 @@ update_profile_success=已更新您的個人資料。
|
||||
change_username=您的帳號已更改。
|
||||
change_username_prompt=注意:修改帳號也會更改您的帳戶的 URL。
|
||||
change_username_redirect_prompt=舊的帳號被領用前,會重新導向您的新帳號。
|
||||
continue=繼續操作
|
||||
continue=繼續
|
||||
cancel=取消
|
||||
language=語言
|
||||
ui=佈景主題
|
||||
@@ -561,7 +576,7 @@ emails=電子信箱
|
||||
manage_emails=管理電子信箱
|
||||
manage_themes=選擇預設佈景主題
|
||||
manage_openid=管理 OpenID 位址
|
||||
email_desc=您的主要電子信箱將被用於通知提醒和其他操作。
|
||||
email_desc=您的主要電子信箱將被用於通知提醒和其他作業。
|
||||
theme_desc=這將是您在整個網站上的預設佈景主題。
|
||||
primary=主要
|
||||
activated=已啟用
|
||||
@@ -706,7 +721,7 @@ oauth2_regenerate_secret_hint=遺失您的密鑰?
|
||||
oauth2_client_secret_hint=請備份您的祕鑰。祕鑰在您離開這個頁面後將不會再顯示。
|
||||
oauth2_application_edit=編輯
|
||||
oauth2_application_create_description=OAuth2 應用程式讓您的第三方應用程式可以存取此 Gitea 上的帳戶。
|
||||
oauth2_application_remove_description=刪除 OAuth2 應用會拒絕它存取此 Gitea 上已授權的帳戶。是否繼續?
|
||||
oauth2_application_remove_description=刪除 OAuth2 應用程式會拒絕它存取此 Gitea 上已授權的帳戶。是否繼續?
|
||||
|
||||
authorized_oauth2_applications=已授權的 OAuth2 應用程式
|
||||
authorized_oauth2_applications_description=您已授權給這些第三方應用程式存取您個人 Gitea 帳戶。請對不再需要的應用程式撤銷存取權。
|
||||
@@ -733,6 +748,11 @@ passcode_invalid=無效的驗證碼,請重試。
|
||||
twofa_enrolled=您的帳戶已經啟用了兩步驟驗證。請將備用驗證碼 (%s) 保存到一個安全的地方,它只會顯示這麼一次!
|
||||
twofa_failed_get_secret=取得密鑰(Secret)失敗。
|
||||
|
||||
webauthn_desc=安全金鑰是包含加密密鑰的硬體設備,它們可以用於兩步驟驗證。安全金鑰必須支援 <a rel="noreferrer" target="_blank" href="https://w3c.github.io/webauthn/#webauthn-authenticator">WebAuthn Authenticator</a> 標準。
|
||||
webauthn_register_key=新增安全金鑰
|
||||
webauthn_nickname=暱稱
|
||||
webauthn_delete_key=移除安全金鑰
|
||||
webauthn_delete_key_desc=如果您移除安全金鑰,將不能再使用它登入。是否繼續?
|
||||
|
||||
manage_account_links=管理已連結的帳戶
|
||||
manage_account_links_desc=這些外部帳戶已連結到您的 Gitea 帳戶。
|
||||
@@ -920,14 +940,14 @@ migrate.migrating=正在從 <b>%s</b> 遷移...
|
||||
migrate.migrating_failed=從 <b>%s</b> 遷移失敗
|
||||
migrate.migrating_failed.error=錯誤:%s
|
||||
migrate.migrating_failed_no_addr=遷移失敗。
|
||||
migrate.github.description=從 github.com 或其他 GitHub 實例遷移資料。
|
||||
migrate.github.description=從 github.com 或其他 GitHub 執行個體遷移資料。
|
||||
migrate.git.description=從任何 Git 服務遷移儲存庫。
|
||||
migrate.gitlab.description=從 gitlab.com 或其他 GitLab 實例遷移資料。
|
||||
migrate.gitea.description=從 gitea.com 或其他 Gitea 實例遷移資料。
|
||||
migrate.gogs.description=從 notabug.org 或其他 Gogs 實例遷移資料。
|
||||
migrate.onedev.description=從 code.onedev.io 或其他 OneDev 實例遷移資料。
|
||||
migrate.gitlab.description=從 gitlab.com 或其他 GitLab 執行個體遷移資料。
|
||||
migrate.gitea.description=從 gitea.com 或其他 Gitea 執行個體遷移資料。
|
||||
migrate.gogs.description=從 notabug.org 或其他 Gogs 執行個體遷移資料。
|
||||
migrate.onedev.description=從 code.onedev.io 或其他 OneDev 執行個體遷移資料。
|
||||
migrate.codebase.description=從 codebasehq.com 遷移資料。
|
||||
migrate.gitbucket.description=從 GitBucket 實例遷移資料。
|
||||
migrate.gitbucket.description=從 GitBucket 執行個體遷移資料。
|
||||
migrate.migrating_git=正在遷移 Git 資料
|
||||
migrate.migrating_topics=正在遷移主題
|
||||
migrate.migrating_milestones=正在遷移里程碑
|
||||
@@ -956,7 +976,7 @@ clone_this_repo=Clone 此儲存庫
|
||||
create_new_repo_command=從命令列建立新儲存庫。
|
||||
push_exist_repo=從命令行推送已經建立的儲存庫
|
||||
empty_message=此儲存庫未包含任何內容。
|
||||
broken_message=無法讀取此儲存庫底層的 Git 資料。請聯絡此 Gitea 實例的管理員或刪除此儲存庫。
|
||||
broken_message=無法讀取此儲存庫底層的 Git 資料。請聯絡此 Gitea 執行個體的管理員或刪除此儲存庫。
|
||||
|
||||
code=程式碼
|
||||
code.desc=存取原始碼、檔案、提交和分支。
|
||||
@@ -1084,7 +1104,7 @@ commits.find=搜尋
|
||||
commits.search_all=所有分支
|
||||
commits.author=作者
|
||||
commits.message=備註
|
||||
commits.date=提交日期
|
||||
commits.date=日期
|
||||
commits.older=更舊的提交
|
||||
commits.newer=更新的提交
|
||||
commits.signed_by=簽署人
|
||||
@@ -1132,7 +1152,7 @@ projects.open=開啟
|
||||
projects.close=關閉
|
||||
|
||||
issues.desc=管理錯誤報告、任務和里程碑。
|
||||
issues.filter_assignees=篩選成員
|
||||
issues.filter_assignees=篩選負責人
|
||||
issues.filter_milestones=篩選里程碑
|
||||
issues.filter_projects=篩選專案
|
||||
issues.filter_labels=篩選標籤
|
||||
@@ -1156,10 +1176,10 @@ issues.new.no_milestone=未選擇里程碑
|
||||
issues.new.clear_milestone=清除已選取里程碑
|
||||
issues.new.open_milestone=開放中的里程碑
|
||||
issues.new.closed_milestone=已關閉的里程碑
|
||||
issues.new.assignees=成員
|
||||
issues.new.add_assignees_title=指派成員
|
||||
issues.new.clear_assignees=清除成員
|
||||
issues.new.no_assignees=沒有成員
|
||||
issues.new.assignees=負責人
|
||||
issues.new.add_assignees_title=指派負責人
|
||||
issues.new.clear_assignees=清除負責人
|
||||
issues.new.no_assignees=沒有負責人
|
||||
issues.new.no_reviewers=沒有審核者
|
||||
issues.new.add_reviewer_title=請求審核
|
||||
issues.choose.get_started=開始
|
||||
@@ -1205,8 +1225,8 @@ issues.filter_label_exclude=`使用 <code>alt</code> + <code>click/enter</code>
|
||||
issues.filter_label_no_select=所有標籤
|
||||
issues.filter_milestone=里程碑
|
||||
issues.filter_milestone_no_select=所有里程碑
|
||||
issues.filter_assignee=成員
|
||||
issues.filter_assginee_no_select=所有成員
|
||||
issues.filter_assignee=負責人
|
||||
issues.filter_assginee_no_select=所有負責人
|
||||
issues.filter_type=類型
|
||||
issues.filter_type.all_issues=所有問題
|
||||
issues.filter_type.assigned_to_you=指派給您的
|
||||
@@ -1231,8 +1251,8 @@ issues.action_close=關閉
|
||||
issues.action_label=標籤
|
||||
issues.action_milestone=里程碑
|
||||
issues.action_milestone_no_select=無里程碑
|
||||
issues.action_assignee=成員
|
||||
issues.action_assignee_no_select=沒有成員
|
||||
issues.action_assignee=負責人
|
||||
issues.action_assignee_no_select=沒有負責人
|
||||
issues.opened_by=建立於 %[1]s 由 <a href="%[2]s">%[3]s</a>
|
||||
pulls.merged_by=由 <a href="%[2]s">%[3]s</a> 建立,合併於 %[1]s
|
||||
pulls.merged_by_fake=由 %[2]s 建立,合併於 %[1]s
|
||||
@@ -1411,7 +1431,7 @@ issues.review.hide_resolved=隱藏已解決
|
||||
issues.review.resolve_conversation=解決對話
|
||||
issues.review.un_resolve_conversation=取消解決對話
|
||||
issues.review.resolved_by=標記了此對話為已解決
|
||||
issues.assignee.error=因為未預期的錯誤,未能成功指派所有成員。
|
||||
issues.assignee.error=因為未預期的錯誤,未能成功加入所有負責人。
|
||||
issues.reference_issue.body=內容
|
||||
issues.content_history.deleted=刪除
|
||||
issues.content_history.edited=編輯
|
||||
@@ -1552,8 +1572,8 @@ milestones.edit_success=已更新里程碑「%s」。
|
||||
milestones.deletion=刪除里程碑
|
||||
milestones.deletion_desc=刪除里程碑會從所有相關的問題移除它。是否繼續?
|
||||
milestones.deletion_success=里程碑已刪除
|
||||
milestones.filter_sort.closest_due_date=到期日由近到遠
|
||||
milestones.filter_sort.furthest_due_date=到期日由遠到近
|
||||
milestones.filter_sort.closest_due_date=截止日期由近到遠
|
||||
milestones.filter_sort.furthest_due_date=截止日期由遠到近
|
||||
milestones.filter_sort.least_complete=完成度由低到高
|
||||
milestones.filter_sort.most_complete=完成度由高到低
|
||||
milestones.filter_sort.most_issues=問題由多到少
|
||||
@@ -1867,7 +1887,7 @@ settings.event_repository_desc=建立或刪除儲存庫。
|
||||
settings.event_header_issue=問題事件
|
||||
settings.event_issues=問題
|
||||
settings.event_issues_desc=建立、編輯、關閉及重新開放問題。
|
||||
settings.event_issue_assign=指派
|
||||
settings.event_issue_assign=指派問題
|
||||
settings.event_issue_assign_desc=指派或取消指派問題。
|
||||
settings.event_issue_label=標籤
|
||||
settings.event_issue_label_desc=更新或清除問題標籤。
|
||||
@@ -1878,7 +1898,7 @@ settings.event_issue_comment_desc=已經建立、編輯或刪除的問題留言
|
||||
settings.event_header_pull_request=合併請求事件
|
||||
settings.event_pull_request=合併請求
|
||||
settings.event_pull_request_desc=建立、編輯、關閉及重新開放合併請求。
|
||||
settings.event_pull_request_assign=合併請求指派
|
||||
settings.event_pull_request_assign=指派合併請求
|
||||
settings.event_pull_request_assign_desc=指派或取消指派合併請求。
|
||||
settings.event_pull_request_label=合併請求標籤
|
||||
settings.event_pull_request_label_desc=更新或清除合併請求標籤。
|
||||
@@ -2314,11 +2334,12 @@ first_page=首頁
|
||||
last_page=末頁
|
||||
total=總計:%d
|
||||
|
||||
dashboard.new_version_hint=現已推出 Gitea %s,您正在執行 %s。查看<a target="_blank" rel="noreferrer" href="https://blog.gitea.io">部落格</a>以獲得更多資訊。
|
||||
dashboard.statistic=摘要
|
||||
dashboard.operations=維護操作
|
||||
dashboard.operations=維護作業
|
||||
dashboard.system_status=系統狀態
|
||||
dashboard.statistic_info=Gitea 資料庫統計:<b>%d</b> 位使用者,<b>%d</b> 個組織,<b>%d</b> 個公鑰,<b>%d</b> 個儲存庫,<b>%d</b> 個儲存庫關注,<b>%d</b> 個星號,<b>%d</b> 次行為,<b>%d</b> 條權限記錄,<b>%d</b> 個問題,<b>%d</b> 則留言,<b>%d</b> 個社群帳戶,<b>%d</b> 個用戶關注,<b>%d</b> 個鏡像,<b>%d</b> 個版本發佈,<b>%d</b> 個認證來源,<b>%d</b> 個 Webhook ,<b>%d</b> 個里程碑,<b>%d</b> 個標籤,<b>%d</b> 個 Hook 任務,<b>%d</b> 個團隊,<b>%d</b> 個更新任務,<b>%d</b> 個附件。
|
||||
dashboard.operation_name=操作名稱
|
||||
dashboard.operation_name=作業名稱
|
||||
dashboard.operation_switch=開關
|
||||
dashboard.operation_run=執行
|
||||
dashboard.clean_unbind_oauth=清理未綁定的 OAuth 連結
|
||||
@@ -2387,6 +2408,7 @@ dashboard.last_gc_pause=上次 GC 暫停時間
|
||||
dashboard.gc_times=GC 執行次數
|
||||
dashboard.delete_old_actions=從資料庫刪除所有舊行為
|
||||
dashboard.delete_old_actions.started=從資料庫刪除所有舊行為的任務已啟動。
|
||||
dashboard.update_checker=更新檢查器
|
||||
|
||||
users.user_manage_panel=使用者帳戶管理
|
||||
users.new_account=建立使用者帳戶
|
||||
@@ -2567,7 +2589,7 @@ auths.tips.oauth2.general=OAuth2 認證
|
||||
auths.tips.oauth2.general.tip=註冊新的 OAuth2 認證時,callback/redirect 網址應為:<host>/user/oauth2/<Authentication Name>/callback
|
||||
auths.tip.oauth2_provider=OAuth2 提供者
|
||||
auths.tip.bitbucket=註冊新的 OAuth 客戶端並加入權限「Account - Read」。網址:https://bitbucket.org/account/user/<your username>/oauth-consumers/new
|
||||
auths.tip.nextcloud=在您的 Nextcloud 使用「設定 -> 安全性 -> OAuth 2.0 客戶端」註冊新的 OAuth 客戶端
|
||||
auths.tip.nextcloud=在您的執行個體中,於選單「設定 -> 安全性 -> OAuth 2.0 客戶端」註冊新的 OAuth 客戶端
|
||||
auths.tip.dropbox=建立一個新的 App。網址:https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook=註冊一個新的應用程式並新增產品「Facebook 登入」。網址:https://developers.facebook.com/apps
|
||||
auths.tip.github=註冊新的 OAuth 應用程式。網址:https://github.com/settings/applications/new
|
||||
@@ -2874,7 +2896,7 @@ notifications=通知
|
||||
unread=未讀
|
||||
read=已讀
|
||||
no_unread=沒有未讀通知
|
||||
no_read=沒有通知
|
||||
no_read=沒有已讀通知
|
||||
pin=固定通知
|
||||
mark_as_read=標記為已讀
|
||||
mark_as_unread=標記為未讀
|
||||
|
||||
@@ -43,9 +43,13 @@ func ListUnadoptedRepositories(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
if listOptions.Page == 0 {
|
||||
listOptions.Page = 1
|
||||
}
|
||||
repoNames, count, err := repo_service.ListUnadoptedRepositories(ctx.FormString("query"), &listOptions)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(int64(count))
|
||||
|
||||
@@ -119,6 +119,7 @@ func CreateUser(ctx *context.APIContext) {
|
||||
user_model.IsErrEmailAlreadyUsed(err) ||
|
||||
db.IsErrNameReserved(err) ||
|
||||
db.IsErrNameCharsNotAllowed(err) ||
|
||||
user_model.IsErrEmailCharIsNotSupported(err) ||
|
||||
user_model.IsErrEmailInvalid(err) ||
|
||||
db.IsErrNamePatternNotAllowed(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||
@@ -265,7 +266,9 @@ func EditUser(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
if err := user_model.UpdateUser(u, emailChanged); err != nil {
|
||||
if user_model.IsErrEmailAlreadyUsed(err) || user_model.IsErrEmailInvalid(err) {
|
||||
if user_model.IsErrEmailAlreadyUsed(err) ||
|
||||
user_model.IsErrEmailCharIsNotSupported(err) ||
|
||||
user_model.IsErrEmailInvalid(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateUser", err)
|
||||
|
||||
@@ -121,7 +121,7 @@ func ListRepoNotifications(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
err = nl.LoadAttributes()
|
||||
if err != nil && !models.IsErrCommentNotExist(err) {
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -245,18 +245,23 @@ func SearchIssues(ctx *context.APIContext) {
|
||||
UpdatedAfterUnix: since,
|
||||
}
|
||||
|
||||
ctxUserID := int64(0)
|
||||
if ctx.IsSigned {
|
||||
ctxUserID = ctx.User.ID
|
||||
}
|
||||
|
||||
// Filter for: Created by User, Assigned to User, Mentioning User, Review of User Requested
|
||||
if ctx.FormBool("created") {
|
||||
issuesOpt.PosterID = ctx.User.ID
|
||||
issuesOpt.PosterID = ctxUserID
|
||||
}
|
||||
if ctx.FormBool("assigned") {
|
||||
issuesOpt.AssigneeID = ctx.User.ID
|
||||
issuesOpt.AssigneeID = ctxUserID
|
||||
}
|
||||
if ctx.FormBool("mentioned") {
|
||||
issuesOpt.MentionedID = ctx.User.ID
|
||||
issuesOpt.MentionedID = ctxUserID
|
||||
}
|
||||
if ctx.FormBool("review_requested") {
|
||||
issuesOpt.ReviewRequestedID = ctx.User.ID
|
||||
issuesOpt.ReviewRequestedID = ctxUserID
|
||||
}
|
||||
|
||||
if issues, err = models.Issues(issuesOpt); err != nil {
|
||||
@@ -599,7 +604,7 @@ func CreateIssue(ctx *context.APIContext) {
|
||||
DeadlineUnix: deadlineUnix,
|
||||
}
|
||||
|
||||
var assigneeIDs = make([]int64, 0)
|
||||
assigneeIDs := make([]int64, 0)
|
||||
var err error
|
||||
if ctx.Repo.CanWrite(unit.TypeIssues) {
|
||||
issue.MilestoneID = form.Milestone
|
||||
|
||||
@@ -144,7 +144,7 @@ func GetDeployKey(ctx *context.APIContext) {
|
||||
// "200":
|
||||
// "$ref": "#/responses/DeployKey"
|
||||
|
||||
key, err := asymkey_model.GetDeployKeyByID(db.DefaultContext, ctx.ParamsInt64(":id"))
|
||||
key, err := asymkey_model.GetDeployKeyByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if asymkey_model.IsErrDeployKeyNotExist(err) {
|
||||
ctx.NotFound()
|
||||
|
||||
@@ -80,7 +80,8 @@ func AddEmail(ctx *context.APIContext) {
|
||||
if err := user_model.AddEmailAddresses(emails); err != nil {
|
||||
if user_model.IsErrEmailAlreadyUsed(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", "Email address has been used: "+err.(user_model.ErrEmailAlreadyUsed).Email)
|
||||
} else if user_model.IsErrEmailInvalid(err) {
|
||||
} else if user_model.IsErrEmailCharIsNotSupported(err) ||
|
||||
user_model.IsErrEmailInvalid(err) {
|
||||
errMsg := fmt.Sprintf("Email address %s invalid", err.(user_model.ErrEmailInvalid).Email)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", errMsg)
|
||||
} else {
|
||||
|
||||
+3
-2
@@ -10,7 +10,6 @@ import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
@@ -47,6 +46,8 @@ import (
|
||||
"code.gitea.io/gitea/services/repository/archiver"
|
||||
"code.gitea.io/gitea/services/task"
|
||||
"code.gitea.io/gitea/services/webhook"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"gitea.com/go-chi/session"
|
||||
)
|
||||
@@ -111,7 +112,7 @@ func GlobalInitInstalled(ctx context.Context) {
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
log.Info("Run Mode: %s", strings.Title(setting.RunMode))
|
||||
log.Info("Run Mode: %s", cases.Title(language.English).String(setting.RunMode))
|
||||
|
||||
// Setup i18n
|
||||
translation.InitLocales()
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
perm_model "code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
gitea_context "code.gitea.io/gitea/modules/context"
|
||||
@@ -24,8 +26,12 @@ import (
|
||||
|
||||
type preReceiveContext struct {
|
||||
*gitea_context.PrivateContext
|
||||
user *user_model.User
|
||||
perm models.Permission
|
||||
|
||||
// loadedPusher indicates that where the following information are loaded
|
||||
loadedPusher bool
|
||||
user *user_model.User // it's the org user if a DeployKey is used
|
||||
userPerm models.Permission
|
||||
deployKeyAccessMode perm_model.AccessMode
|
||||
|
||||
canCreatePullRequest bool
|
||||
checkedCanCreatePullRequest bool
|
||||
@@ -41,62 +47,52 @@ type preReceiveContext struct {
|
||||
opts *private.HookOptions
|
||||
}
|
||||
|
||||
// User gets or loads User
|
||||
func (ctx *preReceiveContext) User() *user_model.User {
|
||||
if ctx.user == nil {
|
||||
ctx.user, ctx.perm = loadUserAndPermission(ctx.PrivateContext, ctx.opts.UserID)
|
||||
}
|
||||
return ctx.user
|
||||
}
|
||||
|
||||
// Perm gets or loads Perm
|
||||
func (ctx *preReceiveContext) Perm() *models.Permission {
|
||||
if ctx.user == nil {
|
||||
ctx.user, ctx.perm = loadUserAndPermission(ctx.PrivateContext, ctx.opts.UserID)
|
||||
}
|
||||
return &ctx.perm
|
||||
}
|
||||
|
||||
// CanWriteCode returns true if can write code
|
||||
// CanWriteCode returns true if pusher can write code
|
||||
func (ctx *preReceiveContext) CanWriteCode() bool {
|
||||
if !ctx.checkedCanWriteCode {
|
||||
ctx.canWriteCode = ctx.Perm().CanWrite(unit.TypeCode)
|
||||
if !ctx.loadPusherAndPermission() {
|
||||
return false
|
||||
}
|
||||
ctx.canWriteCode = ctx.userPerm.CanWrite(unit.TypeCode) || ctx.deployKeyAccessMode >= perm_model.AccessModeWrite
|
||||
ctx.checkedCanWriteCode = true
|
||||
}
|
||||
return ctx.canWriteCode
|
||||
}
|
||||
|
||||
// AssertCanWriteCode returns true if can write code
|
||||
// AssertCanWriteCode returns true if pusher can write code
|
||||
func (ctx *preReceiveContext) AssertCanWriteCode() bool {
|
||||
if !ctx.CanWriteCode() {
|
||||
if ctx.Written() {
|
||||
return false
|
||||
}
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": "User permission denied.",
|
||||
"err": "User permission denied for writing.",
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CanCreatePullRequest returns true if can create pull requests
|
||||
// CanCreatePullRequest returns true if pusher can create pull requests
|
||||
func (ctx *preReceiveContext) CanCreatePullRequest() bool {
|
||||
if !ctx.checkedCanCreatePullRequest {
|
||||
ctx.canCreatePullRequest = ctx.Perm().CanRead(unit.TypePullRequests)
|
||||
if !ctx.loadPusherAndPermission() {
|
||||
return false
|
||||
}
|
||||
ctx.canCreatePullRequest = ctx.userPerm.CanRead(unit.TypePullRequests)
|
||||
ctx.checkedCanCreatePullRequest = true
|
||||
}
|
||||
return ctx.canCreatePullRequest
|
||||
}
|
||||
|
||||
// AssertCanCreatePullRequest returns true if can create pull requests
|
||||
// AssertCreatePullRequest returns true if can create pull requests
|
||||
func (ctx *preReceiveContext) AssertCreatePullRequest() bool {
|
||||
if !ctx.CanCreatePullRequest() {
|
||||
if ctx.Written() {
|
||||
return false
|
||||
}
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": "User permission denied.",
|
||||
"err": "User permission denied for creating pull-request.",
|
||||
})
|
||||
return false
|
||||
}
|
||||
@@ -246,7 +242,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID, refFullN
|
||||
|
||||
// 5. Check if the doer is allowed to push
|
||||
canPush := false
|
||||
if ctx.opts.IsDeployKey {
|
||||
if ctx.opts.DeployKeyID != 0 {
|
||||
canPush = !changedProtectedfiles && protectBranch.CanPush && (!protectBranch.EnableWhitelist || protectBranch.WhitelistDeployKeys)
|
||||
} else {
|
||||
canPush = !changedProtectedfiles && protectBranch.CanUserPush(ctx.opts.UserID)
|
||||
@@ -303,9 +299,15 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID, refFullN
|
||||
return
|
||||
}
|
||||
|
||||
// although we should have called `loadPusherAndPermission` before, here we call it explicitly again because we need to access ctx.user below
|
||||
if !ctx.loadPusherAndPermission() {
|
||||
// if error occurs, loadPusherAndPermission had written the error response
|
||||
return
|
||||
}
|
||||
|
||||
// Now check if the user is allowed to merge PRs for this repository
|
||||
// Note: we can use ctx.perm and ctx.user directly as they will have been loaded above
|
||||
allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, ctx.perm, ctx.user)
|
||||
allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, ctx.userPerm, ctx.user)
|
||||
if err != nil {
|
||||
log.Error("Error calculating if allowed to merge: %v", err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
@@ -323,7 +325,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID, refFullN
|
||||
}
|
||||
|
||||
// If we're an admin for the repository we can ignore status checks, reviews and override protected files
|
||||
if ctx.perm.IsAdmin() {
|
||||
if ctx.userPerm.IsAdmin() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -450,24 +452,44 @@ func generateGitEnv(opts *private.HookOptions) (env []string) {
|
||||
return env
|
||||
}
|
||||
|
||||
func loadUserAndPermission(ctx *gitea_context.PrivateContext, id int64) (user *user_model.User, perm models.Permission) {
|
||||
user, err := user_model.GetUserByID(id)
|
||||
if err != nil {
|
||||
log.Error("Unable to get User id %d Error: %v", id, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: fmt.Sprintf("Unable to get User id %d Error: %v", id, err),
|
||||
})
|
||||
return
|
||||
// loadPusherAndPermission returns false if an error occurs, and it writes the error response
|
||||
func (ctx *preReceiveContext) loadPusherAndPermission() bool {
|
||||
if ctx.loadedPusher {
|
||||
return true
|
||||
}
|
||||
|
||||
perm, err = models.GetUserRepoPermission(ctx.Repo.Repository, user)
|
||||
user, err := user_model.GetUserByID(ctx.opts.UserID)
|
||||
if err != nil {
|
||||
log.Error("Unable to get User id %d Error: %v", ctx.opts.UserID, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: fmt.Sprintf("Unable to get User id %d Error: %v", ctx.opts.UserID, err),
|
||||
})
|
||||
return false
|
||||
}
|
||||
ctx.user = user
|
||||
|
||||
userPerm, err := models.GetUserRepoPermission(ctx.Repo.Repository, user)
|
||||
if err != nil {
|
||||
log.Error("Unable to get Repo permission of repo %s/%s of User %s", ctx.Repo.Repository.OwnerName, ctx.Repo.Repository.Name, user.Name, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: fmt.Sprintf("Unable to get Repo permission of repo %s/%s of User %s: %v", ctx.Repo.Repository.OwnerName, ctx.Repo.Repository.Name, user.Name, err),
|
||||
})
|
||||
return
|
||||
return false
|
||||
}
|
||||
ctx.userPerm = userPerm
|
||||
|
||||
if ctx.opts.DeployKeyID != 0 {
|
||||
deployKey, err := asymkey_model.GetDeployKeyByID(ctx, ctx.opts.DeployKeyID)
|
||||
if err != nil {
|
||||
log.Error("Unable to get DeployKey id %d Error: %v", ctx.opts.DeployKeyID, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: fmt.Sprintf("Unable to get DeployKey id %d Error: %v", ctx.opts.DeployKeyID, err),
|
||||
})
|
||||
return false
|
||||
}
|
||||
ctx.deployKeyAccessMode = deployKey.Mode
|
||||
}
|
||||
|
||||
return
|
||||
ctx.loadedPusher = true
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func SendEmail(ctx *context.PrivateContext) {
|
||||
}
|
||||
} else {
|
||||
err := user_model.IterateUser(func(user *user_model.User) error {
|
||||
if len(user.Email) > 0 {
|
||||
if len(user.Email) > 0 && user.IsActive {
|
||||
emails = append(emails, user.Email)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -229,8 +229,6 @@ func ServCommand(ctx *context.PrivateContext) {
|
||||
var deployKey *asymkey_model.DeployKey
|
||||
var user *user_model.User
|
||||
if key.Type == asymkey_model.KeyTypeDeploy {
|
||||
results.IsDeployKey = true
|
||||
|
||||
var err error
|
||||
deployKey, err = asymkey_model.GetDeployKeyByRepo(key.ID, repo.ID)
|
||||
if err != nil {
|
||||
@@ -248,6 +246,7 @@ func ServCommand(ctx *context.PrivateContext) {
|
||||
})
|
||||
return
|
||||
}
|
||||
results.DeployKeyID = deployKey.ID
|
||||
results.KeyName = deployKey.Name
|
||||
|
||||
// FIXME: Deploy keys aren't really the owner of the repo pushing changes
|
||||
@@ -410,9 +409,9 @@ func ServCommand(ctx *context.PrivateContext) {
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Debug("Serv Results:\nIsWiki: %t\nIsDeployKey: %t\nKeyID: %d\tKeyName: %s\nUserName: %s\nUserID: %d\nOwnerName: %s\nRepoName: %s\nRepoID: %d",
|
||||
log.Debug("Serv Results:\nIsWiki: %t\nDeployKeyID: %d\nKeyID: %d\tKeyName: %s\nUserName: %s\nUserID: %d\nOwnerName: %s\nRepoName: %s\nRepoID: %d",
|
||||
results.IsWiki,
|
||||
results.IsDeployKey,
|
||||
results.DeployKeyID,
|
||||
results.KeyID,
|
||||
results.KeyName,
|
||||
results.UserName,
|
||||
|
||||
@@ -209,7 +209,7 @@ func shadowPassword(provider, cfgItem string) string {
|
||||
case "redis":
|
||||
return shadowPasswordKV(cfgItem, ",")
|
||||
case "mysql":
|
||||
//root:@tcp(localhost:3306)/macaron?charset=utf8
|
||||
// root:@tcp(localhost:3306)/macaron?charset=utf8
|
||||
atIdx := strings.Index(cfgItem, "@")
|
||||
if atIdx > 0 {
|
||||
colonIdx := strings.Index(cfgItem[:atIdx], ":")
|
||||
@@ -244,7 +244,7 @@ func Config(ctx *context.Context) {
|
||||
ctx.Data["OfflineMode"] = setting.OfflineMode
|
||||
ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
|
||||
ctx.Data["RunUser"] = setting.RunUser
|
||||
ctx.Data["RunMode"] = strings.Title(setting.RunMode)
|
||||
ctx.Data["RunMode"] = setting.RunMode
|
||||
if version, err := git.LocalVersion(); err == nil {
|
||||
ctx.Data["GitVersion"] = version.Original()
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ func NewAuthSource(ctx *context.Context) {
|
||||
ctx.Data["PageIsAdmin"] = true
|
||||
ctx.Data["PageIsAdminAuthentications"] = true
|
||||
|
||||
ctx.Data["type"] = auth.LDAP
|
||||
ctx.Data["type"] = auth.LDAP.Int()
|
||||
ctx.Data["CurrentTypeName"] = auth.Names[auth.LDAP]
|
||||
ctx.Data["CurrentSecurityProtocol"] = ldap.SecurityProtocolNames[ldap.SecurityProtocolUnencrypted]
|
||||
ctx.Data["smtp_auth"] = "PLAIN"
|
||||
@@ -112,7 +112,7 @@ func NewAuthSource(ctx *context.Context) {
|
||||
ctx.Data["SSPIDefaultLanguage"] = ""
|
||||
|
||||
// only the first as default
|
||||
ctx.Data["oauth2_provider"] = oauth2providers[0]
|
||||
ctx.Data["oauth2_provider"] = oauth2providers[0].Name
|
||||
|
||||
ctx.HTML(http.StatusOK, tplAuthNew)
|
||||
}
|
||||
@@ -181,6 +181,14 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source {
|
||||
} else {
|
||||
customURLMapping = nil
|
||||
}
|
||||
var scopes []string
|
||||
for _, s := range strings.Split(form.Oauth2Scopes, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
if s != "" {
|
||||
scopes = append(scopes, s)
|
||||
}
|
||||
}
|
||||
|
||||
return &oauth2.Source{
|
||||
Provider: form.Oauth2Provider,
|
||||
ClientID: form.Oauth2Key,
|
||||
@@ -188,7 +196,7 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source {
|
||||
OpenIDConnectAutoDiscoveryURL: form.OpenIDConnectAutoDiscoveryURL,
|
||||
CustomURLMapping: customURLMapping,
|
||||
IconURL: form.Oauth2IconURL,
|
||||
Scopes: strings.Split(form.Oauth2Scopes, ","),
|
||||
Scopes: scopes,
|
||||
RequiredClaimName: form.Oauth2RequiredClaimName,
|
||||
RequiredClaimValue: form.Oauth2RequiredClaimValue,
|
||||
SkipLocalTwoFA: form.SkipLocalTwoFA,
|
||||
@@ -243,6 +251,9 @@ func NewAuthSourcePost(ctx *context.Context) {
|
||||
ctx.Data["SSPISeparatorReplacement"] = "_"
|
||||
ctx.Data["SSPIDefaultLanguage"] = ""
|
||||
|
||||
// FIXME: most error path to render tplAuthNew will fail and result in 500
|
||||
// * template: admin/auth/new:17:68: executing "admin/auth/new" at <.type.Int>: can't evaluate field Int in type interface {}
|
||||
// * template: admin/auth/source/oauth:5:93: executing "admin/auth/source/oauth" at <.oauth2_provider.Name>: can't evaluate field Name in type interface {}
|
||||
hasTLS := false
|
||||
var config convert.Conversion
|
||||
switch auth.Type(form.Type) {
|
||||
@@ -393,6 +404,7 @@ func EditAuthSourcePost(ctx *context.Context) {
|
||||
source.IsActive = form.IsActive
|
||||
source.IsSyncEnabled = form.IsSyncEnabled
|
||||
source.Cfg = config
|
||||
// FIXME: if the name conflicts, it will result in 500: Error 1062: Duplicate entry 'aa' for key 'login_source.UQE_login_source_name'
|
||||
if err := auth.UpdateSource(source); err != nil {
|
||||
if oauth2.IsErrOpenIDConnectInitialize(err) {
|
||||
ctx.Flash.Error(err.Error(), true)
|
||||
|
||||
+38
-12
@@ -41,10 +41,16 @@ func Users(ctx *context.Context) {
|
||||
ctx.Data["PageIsAdmin"] = true
|
||||
ctx.Data["PageIsAdminUsers"] = true
|
||||
|
||||
extraParamStrings := map[string]string{}
|
||||
statusFilterKeys := []string{"is_active", "is_admin", "is_restricted", "is_2fa_enabled", "is_prohibit_login"}
|
||||
statusFilterMap := map[string]string{}
|
||||
for _, filterKey := range statusFilterKeys {
|
||||
statusFilterMap[filterKey] = ctx.FormString("status_filter[" + filterKey + "]")
|
||||
paramKey := "status_filter[" + filterKey + "]"
|
||||
paramVal := ctx.FormString(paramKey)
|
||||
statusFilterMap[filterKey] = paramVal
|
||||
if paramVal != "" {
|
||||
extraParamStrings[paramKey] = paramVal
|
||||
}
|
||||
}
|
||||
|
||||
sortType := ctx.FormString("sort")
|
||||
@@ -68,6 +74,7 @@ func Users(ctx *context.Context) {
|
||||
IsRestricted: util.OptionalBoolParse(statusFilterMap["is_restricted"]),
|
||||
IsTwoFactorEnabled: util.OptionalBoolParse(statusFilterMap["is_2fa_enabled"]),
|
||||
IsProhibitLogin: util.OptionalBoolParse(statusFilterMap["is_prohibit_login"]),
|
||||
ExtraParamStrings: extraParamStrings,
|
||||
}, tplUsers)
|
||||
}
|
||||
|
||||
@@ -164,6 +171,9 @@ func NewUserPost(ctx *context.Context) {
|
||||
case user_model.IsErrEmailAlreadyUsed(err):
|
||||
ctx.Data["Err_Email"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserNew, &form)
|
||||
case user_model.IsErrEmailCharIsNotSupported(err):
|
||||
ctx.Data["Err_Email"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserNew, &form)
|
||||
case user_model.IsErrEmailInvalid(err):
|
||||
ctx.Data["Err_Email"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserNew, &form)
|
||||
@@ -217,15 +227,17 @@ func prepareUserInfo(ctx *context.Context) *user_model.User {
|
||||
}
|
||||
ctx.Data["Sources"] = sources
|
||||
|
||||
ctx.Data["TwoFactorEnabled"] = true
|
||||
_, err = auth.GetTwoFactorByUID(u.ID)
|
||||
hasTOTP, err := auth.HasTwoFactorByUID(u.ID)
|
||||
if err != nil {
|
||||
if !auth.IsErrTwoFactorNotEnrolled(err) {
|
||||
ctx.ServerError("IsErrTwoFactorNotEnrolled", err)
|
||||
return nil
|
||||
}
|
||||
ctx.Data["TwoFactorEnabled"] = false
|
||||
ctx.ServerError("auth.HasTwoFactorByUID", err)
|
||||
return nil
|
||||
}
|
||||
hasWebAuthn, err := auth.HasWebAuthnRegistrationsByUID(u.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("auth.HasWebAuthnRegistrationsByUID", err)
|
||||
return nil
|
||||
}
|
||||
ctx.Data["TwoFactorEnabled"] = hasTOTP || hasWebAuthn
|
||||
|
||||
return u
|
||||
}
|
||||
@@ -327,14 +339,27 @@ func EditUserPost(ctx *context.Context) {
|
||||
if form.Reset2FA {
|
||||
tf, err := auth.GetTwoFactorByUID(u.ID)
|
||||
if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
|
||||
ctx.ServerError("GetTwoFactorByUID", err)
|
||||
ctx.ServerError("auth.GetTwoFactorByUID", err)
|
||||
return
|
||||
} else if tf != nil {
|
||||
if err := auth.DeleteTwoFactorByID(tf.ID, u.ID); err != nil {
|
||||
ctx.ServerError("auth.DeleteTwoFactorByID", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err = auth.DeleteTwoFactorByID(tf.ID, u.ID); err != nil {
|
||||
ctx.ServerError("DeleteTwoFactorByID", err)
|
||||
wn, err := auth.GetWebAuthnCredentialsByUID(u.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("auth.GetTwoFactorByUID", err)
|
||||
return
|
||||
}
|
||||
for _, cred := range wn {
|
||||
if _, err := auth.DeleteCredential(cred.ID, u.ID); err != nil {
|
||||
ctx.ServerError("auth.DeleteCredential", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u.LoginName = form.LoginName
|
||||
@@ -364,7 +389,8 @@ func EditUserPost(ctx *context.Context) {
|
||||
if user_model.IsErrEmailAlreadyUsed(err) {
|
||||
ctx.Data["Err_Email"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserEdit, &form)
|
||||
} else if user_model.IsErrEmailInvalid(err) {
|
||||
} else if user_model.IsErrEmailCharIsNotSupported(err) ||
|
||||
user_model.IsErrEmailInvalid(err) {
|
||||
ctx.Data["Err_Email"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserEdit, &form)
|
||||
} else {
|
||||
|
||||
@@ -195,7 +195,7 @@ func SignInPost(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.SignInForm)
|
||||
u, source, err := auth_service.UserSignIn(form.UserName, form.Password)
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
if user_model.IsErrUserNotExist(err) || user_model.IsErrEmailAddressNotExist(err) {
|
||||
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form)
|
||||
log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
|
||||
} else if user_model.IsErrEmailAlreadyUsed(err) {
|
||||
@@ -573,6 +573,9 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{
|
||||
case user_model.IsErrEmailAlreadyUsed(err):
|
||||
ctx.Data["Err_Email"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tpl, form)
|
||||
case user_model.IsErrEmailCharIsNotSupported(err):
|
||||
ctx.Data["Err_Email"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form)
|
||||
case user_model.IsErrEmailInvalid(err):
|
||||
ctx.Data["Err_Email"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form)
|
||||
@@ -618,6 +621,12 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
|
||||
|
||||
// Send confirmation email
|
||||
if !u.IsActive && u.ID > 1 {
|
||||
if setting.Service.RegisterManualConfirm {
|
||||
ctx.Data["ManualActivationOnly"] = true
|
||||
ctx.HTML(http.StatusOK, TplActivate)
|
||||
return
|
||||
}
|
||||
|
||||
mailer.SendActivateAccountMail(ctx.Locale, u)
|
||||
|
||||
ctx.Data["IsSendRegisterMail"] = true
|
||||
|
||||
+16
-16
@@ -11,7 +11,6 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
@@ -27,6 +26,8 @@ import (
|
||||
)
|
||||
|
||||
func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler {
|
||||
prefix = strings.Trim(prefix, "/")
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
if storageSetting.ServeDirect {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
@@ -35,12 +36,14 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(req.URL.RequestURI(), "/"+prefix) {
|
||||
if !strings.HasPrefix(req.URL.Path, "/"+prefix+"/") {
|
||||
next.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
rPath := strings.TrimPrefix(req.URL.RequestURI(), "/"+prefix)
|
||||
rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/")
|
||||
rPath = path.Clean("/" + strings.ReplaceAll(rPath, "\\", "/"))[1:]
|
||||
|
||||
u, err := objStore.URL(rPath, path.Base(rPath))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) {
|
||||
@@ -52,11 +55,12 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor
|
||||
http.Error(w, fmt.Sprintf("Error whilst getting URL for %s %s", prefix, rPath), 500)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(
|
||||
w,
|
||||
req,
|
||||
u.String(),
|
||||
301,
|
||||
http.StatusMovedPermanently,
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -67,28 +71,24 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor
|
||||
return
|
||||
}
|
||||
|
||||
prefix := strings.Trim(prefix, "/")
|
||||
|
||||
if !strings.HasPrefix(req.URL.EscapedPath(), "/"+prefix+"/") {
|
||||
if !strings.HasPrefix(req.URL.Path, "/"+prefix+"/") {
|
||||
next.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
rPath := strings.TrimPrefix(req.URL.EscapedPath(), "/"+prefix+"/")
|
||||
rPath = strings.TrimPrefix(rPath, "/")
|
||||
rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/")
|
||||
rPath = path.Clean("/" + strings.ReplaceAll(rPath, "\\", "/"))[1:]
|
||||
if rPath == "" {
|
||||
http.Error(w, "file not found", 404)
|
||||
return
|
||||
}
|
||||
rPath = path.Clean("/" + filepath.ToSlash(rPath))
|
||||
rPath = rPath[1:]
|
||||
|
||||
fi, err := objStore.Stat(rPath)
|
||||
if err == nil && httpcache.HandleTimeCache(req, w, fi) {
|
||||
return
|
||||
}
|
||||
|
||||
//If we have matched and access to release or issue
|
||||
// If we have matched and access to release or issue
|
||||
fr, err := objStore.Open(rPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) {
|
||||
@@ -121,7 +121,7 @@ func (d *dataStore) GetData() map[string]interface{} {
|
||||
// Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so.
|
||||
// This error will be created with the gitea 500 page.
|
||||
func Recovery() func(next http.Handler) http.Handler {
|
||||
var rnd = templates.HTMLRenderer()
|
||||
rnd := templates.HTMLRenderer()
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
defer func() {
|
||||
@@ -131,14 +131,14 @@ func Recovery() func(next http.Handler) http.Handler {
|
||||
|
||||
sessionStore := session.GetSession(req)
|
||||
|
||||
var lc = middleware.Locale(w, req)
|
||||
var store = dataStore{
|
||||
lc := middleware.Locale(w, req)
|
||||
store := dataStore{
|
||||
"Language": lc.Language(),
|
||||
"CurrentURL": setting.AppSubURL + req.URL.RequestURI(),
|
||||
"i18n": lc,
|
||||
}
|
||||
|
||||
var user = context.GetContextUser(req)
|
||||
user := context.GetContextUser(req)
|
||||
if user == nil {
|
||||
// Get user from session if logged in - do not attempt to sign-in
|
||||
user = auth.SessionUser(sessionStore)
|
||||
|
||||
@@ -84,6 +84,9 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions,
|
||||
|
||||
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
|
||||
pager.SetDefaultParams(ctx)
|
||||
for paramKey, paramVal := range opts.ExtraParamStrings {
|
||||
pager.AddParamString(paramKey, paramVal)
|
||||
}
|
||||
ctx.Data["Page"] = pager
|
||||
|
||||
ctx.HTML(http.StatusOK, tplName)
|
||||
|
||||
+10
-2
@@ -6,6 +6,7 @@ package org
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
@@ -23,7 +24,14 @@ const (
|
||||
|
||||
// Home show organization home page
|
||||
func Home(ctx *context.Context) {
|
||||
ctx.SetParams(":org", ctx.Params(":username"))
|
||||
uname := ctx.Params(":username")
|
||||
|
||||
if strings.HasSuffix(uname, ".keys") || strings.HasSuffix(uname, ".gpg") {
|
||||
ctx.NotFound("", nil)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetParams(":org", uname)
|
||||
context.HandleOrgAssignment(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
@@ -109,7 +117,7 @@ func Home(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var opts = &models.FindOrgMembersOpts{
|
||||
opts := &models.FindOrgMembersOpts{
|
||||
OrgID: org.ID,
|
||||
PublicOnly: true,
|
||||
ListOptions: db.ListOptions{Page: 1, PageSize: 25},
|
||||
|
||||
@@ -77,8 +77,8 @@ func setPathsCompareContext(ctx *context.Context, base, head *git.Commit, headOw
|
||||
ctx.Data["SourcePath"] = SourceCommitURL(headOwner, headName, head)
|
||||
ctx.Data["RawPath"] = RawCommitURL(headOwner, headName, head)
|
||||
if base != nil {
|
||||
ctx.Data["BeforeSourcePath"] = SourceCommitURL(headOwner, headName, head)
|
||||
ctx.Data["BeforeRawPath"] = RawCommitURL(headOwner, headName, head)
|
||||
ctx.Data["BeforeSourcePath"] = SourceCommitURL(headOwner, headName, base)
|
||||
ctx.Data["BeforeRawPath"] = RawCommitURL(headOwner, headName, base)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,6 +298,13 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
|
||||
ci.BaseBranch = baseCommit.ID.String()
|
||||
ctx.Data["BaseBranch"] = ci.BaseBranch
|
||||
baseIsCommit = true
|
||||
} else if ci.BaseBranch == git.EmptySHA {
|
||||
if isSameRepo {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadBranch))
|
||||
} else {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadRepo.FullName()) + ":" + util.PathEscapeSegments(ci.HeadBranch))
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
ctx.NotFound("IsRefExist", nil)
|
||||
return nil
|
||||
|
||||
@@ -223,7 +223,6 @@ func httpBase(ctx *context.Context) (h *serviceHandler) {
|
||||
models.EnvRepoName + "=" + reponame,
|
||||
models.EnvPusherName + "=" + ctx.User.Name,
|
||||
models.EnvPusherID + fmt.Sprintf("=%d", ctx.User.ID),
|
||||
models.EnvIsDeployKey + "=false",
|
||||
models.EnvAppURL + "=" + setting.AppURL,
|
||||
}
|
||||
|
||||
|
||||
@@ -843,12 +843,19 @@ func NewIssue(ctx *context.Context) {
|
||||
func NewIssueChooseTemplate(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.issues.new")
|
||||
ctx.Data["PageIsIssueList"] = true
|
||||
ctx.Data["milestone"] = ctx.FormInt64("milestone")
|
||||
|
||||
issueTemplates := ctx.IssueTemplatesFromDefaultBranch()
|
||||
ctx.Data["NewIssueChooseTemplate"] = len(issueTemplates) > 0
|
||||
ctx.Data["IssueTemplates"] = issueTemplates
|
||||
|
||||
if len(issueTemplates) == 0 {
|
||||
// The "issues/new" and "issues/new/choose" share the same query parameters "project" and "milestone", if no template here, just redirect to the "issues/new" page with these parameters.
|
||||
ctx.Redirect(fmt.Sprintf("%s/issues/new?%s", ctx.Repo.Repository.HTMLURL(), ctx.Req.URL.RawQuery), http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["milestone"] = ctx.FormInt64("milestone")
|
||||
ctx.Data["project"] = ctx.FormInt64("project")
|
||||
|
||||
ctx.HTML(http.StatusOK, tplIssueChoose)
|
||||
}
|
||||
|
||||
|
||||
@@ -253,6 +253,13 @@ func LFSFileGet(ctx *context.Context) {
|
||||
}
|
||||
ctx.Data["LFSFilesLink"] = ctx.Repo.RepoLink + "/settings/lfs"
|
||||
oid := ctx.Params("oid")
|
||||
|
||||
p := lfs.Pointer{Oid: oid}
|
||||
if !p.IsValid() {
|
||||
ctx.NotFound("LFSFileGet", nil)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Title"] = oid
|
||||
ctx.Data["PageIsSettingsLFS"] = true
|
||||
meta, err := models.GetLFSMetaObjectByOid(ctx.Repo.Repository.ID, oid)
|
||||
@@ -343,6 +350,12 @@ func LFSDelete(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
oid := ctx.Params("oid")
|
||||
p := lfs.Pointer{Oid: oid}
|
||||
if !p.IsValid() {
|
||||
ctx.NotFound("LFSDelete", nil)
|
||||
return
|
||||
}
|
||||
|
||||
count, err := models.RemoveLFSMetaObjectByOid(ctx.Repo.Repository.ID, oid)
|
||||
if err != nil {
|
||||
ctx.ServerError("LFSDelete", err)
|
||||
|
||||
+55
-14
@@ -197,7 +197,7 @@ func Milestones(ctx *context.Context) {
|
||||
if issueReposQueryPattern.MatchString(reposQuery) {
|
||||
// remove "[" and "]" from string
|
||||
reposQuery = reposQuery[1 : len(reposQuery)-1]
|
||||
//for each ID (delimiter ",") add to int to repoIDs
|
||||
// for each ID (delimiter ",") add to int to repoIDs
|
||||
|
||||
for _, rID := range strings.Split(reposQuery, ",") {
|
||||
// Ensure nonempty string entries
|
||||
@@ -350,7 +350,6 @@ func Issues(ctx *context.Context) {
|
||||
var issueReposQueryPattern = regexp.MustCompile(`^\[\d+(,\d+)*,?\]$`)
|
||||
|
||||
func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
|
||||
// ----------------------------------------------------
|
||||
// Determine user; can be either user or organization.
|
||||
// Return with NotFound or ServerError if unsuccessful.
|
||||
@@ -364,7 +363,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
var (
|
||||
viewType string
|
||||
sortType = ctx.FormString("sort")
|
||||
filterMode = models.FilterModeAll
|
||||
filterMode int
|
||||
)
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
@@ -390,8 +389,10 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
filterMode = models.FilterModeMention
|
||||
case "review_requested":
|
||||
filterMode = models.FilterModeReviewRequested
|
||||
case "your_repositories": // filterMode already set to All
|
||||
case "your_repositories":
|
||||
fallthrough
|
||||
default:
|
||||
filterMode = models.FilterModeYourRepositories
|
||||
viewType = "your_repositories"
|
||||
}
|
||||
|
||||
@@ -421,6 +422,30 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
User: ctx.User,
|
||||
}
|
||||
|
||||
// Search all repositories which
|
||||
//
|
||||
// As user:
|
||||
// - Owns the repository.
|
||||
// - Have collaborator permissions in repository.
|
||||
//
|
||||
// As org:
|
||||
// - Owns the repository.
|
||||
//
|
||||
// As team:
|
||||
// - Team org's owns the repository.
|
||||
// - Team has read permission to repository.
|
||||
repoOpts := &models.SearchRepoOptions{
|
||||
Actor: ctx.User,
|
||||
OwnerID: ctx.User.ID,
|
||||
Private: true,
|
||||
AllPublic: false,
|
||||
AllLimited: false,
|
||||
}
|
||||
|
||||
if ctxUser.IsOrganization() && ctx.Org.Team != nil {
|
||||
repoOpts.TeamID = ctx.Org.Team.ID
|
||||
}
|
||||
|
||||
switch filterMode {
|
||||
case models.FilterModeAll:
|
||||
case models.FilterModeAssign:
|
||||
@@ -431,6 +456,19 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
opts.MentionedID = ctx.User.ID
|
||||
case models.FilterModeReviewRequested:
|
||||
opts.ReviewRequestedID = ctx.User.ID
|
||||
case models.FilterModeYourRepositories:
|
||||
if ctxUser.IsOrganization() && ctx.Org.Team != nil {
|
||||
// Fixes a issue whereby the user's ID would be used
|
||||
// to check if it's in the team(which possible isn't the case).
|
||||
opts.User = nil
|
||||
}
|
||||
userRepoIDs, _, err := models.SearchRepositoryIDs(repoOpts)
|
||||
if err != nil {
|
||||
ctx.ServerError("models.SearchRepositoryIDs: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
opts.RepoIDs = userRepoIDs
|
||||
}
|
||||
|
||||
// keyword holds the search term entered into the search field.
|
||||
@@ -562,8 +600,12 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
Org: org,
|
||||
Team: team,
|
||||
}
|
||||
if len(repoIDs) > 0 {
|
||||
statsOpts.RepoIDs = repoIDs
|
||||
if filterMode == models.FilterModeYourRepositories {
|
||||
statsOpts.RepoCond = models.SearchRepositoryCondition(repoOpts)
|
||||
}
|
||||
// Detect when we only should search by team.
|
||||
if opts.User == nil {
|
||||
statsOpts.UserID = 0
|
||||
}
|
||||
issueStats, err = models.GetUserIssueStats(statsOpts)
|
||||
if err != nil {
|
||||
@@ -586,8 +628,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
|
||||
ctx.Data["IsShowClosed"] = isShowClosed
|
||||
|
||||
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] =
|
||||
issue_service.GetRefEndNamesAndURLs(issues, ctx.FormString("RepoLink"))
|
||||
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = issue_service.GetRefEndNamesAndURLs(issues, ctx.FormString("RepoLink"))
|
||||
|
||||
ctx.Data["Issues"] = issues
|
||||
|
||||
@@ -661,7 +702,7 @@ func getRepoIDs(reposQuery string) []int64 {
|
||||
var repoIDs []int64
|
||||
// remove "[" and "]" from string
|
||||
reposQuery = reposQuery[1 : len(reposQuery)-1]
|
||||
//for each ID (delimiter ",") add to int to repoIDs
|
||||
// for each ID (delimiter ",") add to int to repoIDs
|
||||
for _, rID := range strings.Split(reposQuery, ",") {
|
||||
// Ensure nonempty string entries
|
||||
if rID != "" && rID != "0" {
|
||||
@@ -693,8 +734,8 @@ func issueIDsFromSearch(ctxUser *user_model.User, keyword string, opts *models.I
|
||||
}
|
||||
|
||||
func loadRepoByIDs(ctxUser *user_model.User, issueCountByRepo map[int64]int64, unitType unit.Type) (map[int64]*repo_model.Repository, error) {
|
||||
var totalRes = make(map[int64]*repo_model.Repository, len(issueCountByRepo))
|
||||
var repoIDs = make([]int64, 0, 500)
|
||||
totalRes := make(map[int64]*repo_model.Repository, len(issueCountByRepo))
|
||||
repoIDs := make([]int64, 0, 500)
|
||||
for id := range issueCountByRepo {
|
||||
if id <= 0 {
|
||||
continue
|
||||
@@ -745,7 +786,7 @@ func ShowGPGKeys(ctx *context.Context, uid int64) {
|
||||
if err != nil {
|
||||
if asymkey_model.IsErrGPGKeyImportNotExist(err) {
|
||||
failedEntitiesID = append(failedEntitiesID, k.KeyID)
|
||||
continue //Skip previous import without backup of imported armored key
|
||||
continue // Skip previous import without backup of imported armored key
|
||||
}
|
||||
ctx.ServerError("ShowGPGKeys", err)
|
||||
return
|
||||
@@ -755,12 +796,12 @@ func ShowGPGKeys(ctx *context.Context, uid int64) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
headers := make(map[string]string)
|
||||
if len(failedEntitiesID) > 0 { //If some key need re-import to be exported
|
||||
if len(failedEntitiesID) > 0 { // If some key need re-import to be exported
|
||||
headers["Note"] = fmt.Sprintf("The keys with the following IDs couldn't be exported and need to be reuploaded %s", strings.Join(failedEntitiesID, ", "))
|
||||
}
|
||||
writer, _ := armor.Encode(&buf, "PGP PUBLIC KEY BLOCK", headers)
|
||||
for _, e := range entities {
|
||||
err = e.Serialize(writer) //TODO find why key are exported with a different cipherTypeByte as original (should not be blocking but strange)
|
||||
err = e.Serialize(writer) // TODO find why key are exported with a different cipherTypeByte as original (should not be blocking but strange)
|
||||
if err != nil {
|
||||
ctx.ServerError("ShowGPGKeys", err)
|
||||
return
|
||||
|
||||
@@ -188,7 +188,8 @@ func EmailPost(ctx *context.Context) {
|
||||
|
||||
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsAccount, &form)
|
||||
return
|
||||
} else if user_model.IsErrEmailInvalid(err) {
|
||||
} else if user_model.IsErrEmailCharIsNotSupported(err) ||
|
||||
user_model.IsErrEmailInvalid(err) {
|
||||
loadAccountData(ctx)
|
||||
|
||||
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplSettingsAccount, &form)
|
||||
|
||||
@@ -98,6 +98,11 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route {
|
||||
http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "/assets/img/apple-touch-icon.png"), 301)
|
||||
})
|
||||
|
||||
// redirect default favicon to the path of the custom favicon with a default as a fallback
|
||||
routes.Get("/favicon.ico", func(w http.ResponseWriter, req *http.Request) {
|
||||
http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "/assets/img/favicon.png"), 301)
|
||||
})
|
||||
|
||||
common := []interface{}{}
|
||||
|
||||
if setting.EnableGzip {
|
||||
|
||||
@@ -32,7 +32,7 @@ func UserSignIn(username, password string) (*user_model.User, *auth.Source, erro
|
||||
}
|
||||
if !has {
|
||||
return nil, nil, user_model.ErrEmailAddressNotExist{
|
||||
Email: user.Email,
|
||||
Email: username,
|
||||
}
|
||||
}
|
||||
user = &user_model.User{ID: emailAddress.UID}
|
||||
|
||||
@@ -715,7 +715,8 @@ func TestDiffToHTML_14231(t *testing.T) {
|
||||
diffRecord := diffMatchPatch.DiffMain(highlight.Code("main.v", "", " run()\n"), highlight.Code("main.v", "", " run(db)\n"), true)
|
||||
diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord)
|
||||
|
||||
expected := ` <span class="n">run</span><span class="added-code"><span class="o">(</span><span class="n">db</span></span><span class="o">)</span>`
|
||||
expected := `<span class="line"><span class="cl"> <span class="n">run</span><span class="added-code"><span class="o">(</span><span class="n">db</span></span><span class="o">)</span>
|
||||
</span></span>`
|
||||
output := diffToHTML("main.v", diffRecord, DiffLineAdd)
|
||||
|
||||
assertEqual(t, expected, output.Content)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user