Merge branch 'main' into access-token-scope
This commit is contained in:
commit
85db585bee
171 changed files with 6762 additions and 2785 deletions
10
.drone.yml
10
.drone.yml
|
@ -39,6 +39,16 @@ steps:
|
|||
- make lint-frontend
|
||||
depends_on: [deps-frontend]
|
||||
|
||||
- name: security-check
|
||||
image: golang:1.19
|
||||
pull: always
|
||||
commands:
|
||||
- make security-check
|
||||
depends_on: [deps-backend]
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
|
||||
- name: lint-backend
|
||||
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||
pull: always
|
||||
|
|
|
@ -46,7 +46,7 @@ rules:
|
|||
accessor-pairs: [2]
|
||||
array-bracket-newline: [0]
|
||||
array-bracket-spacing: [2, never]
|
||||
array-callback-return: [0]
|
||||
array-callback-return: [2, {checkForEach: true}]
|
||||
array-element-newline: [0]
|
||||
arrow-body-style: [0]
|
||||
arrow-parens: [2, always]
|
||||
|
|
|
@ -86,6 +86,8 @@ linters-settings:
|
|||
- github.com/unknwon/com: "use gitea's util and replacements"
|
||||
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on tests files.
|
||||
- path: _test\.go
|
||||
|
|
6
Makefile
6
Makefile
|
@ -35,6 +35,7 @@ MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4
|
|||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.0
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.3.0
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@latest
|
||||
|
||||
DOCKER_IMAGE ?= gitea/gitea
|
||||
DOCKER_TAG ?= latest
|
||||
|
@ -728,6 +729,10 @@ generate-go: $(TAGS_PREREQ)
|
|||
@echo "Running go generate..."
|
||||
@CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' $(GO_PACKAGES)
|
||||
|
||||
.PHONY: security-check
|
||||
security-check:
|
||||
govulncheck -v ./...
|
||||
|
||||
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||
|
||||
|
@ -813,6 +818,7 @@ deps-backend:
|
|||
$(GO) install $(SWAGGER_PACKAGE)
|
||||
$(GO) install $(XGO_PACKAGE)
|
||||
$(GO) install $(GO_LICENSES_PACKAGE)
|
||||
$(GO) install $(GOVULNCHECK_PACKAGE)
|
||||
|
||||
node_modules: package-lock.json
|
||||
npm install --no-save
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -64,8 +64,8 @@ func main() {
|
|||
}
|
||||
|
||||
entries = append(entries, LicenseEntry{
|
||||
Name: name,
|
||||
Path: path,
|
||||
Name: name,
|
||||
Path: path,
|
||||
LicenseText: string(licenseText),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ const (
|
|||
var CmdServ = cli.Command{
|
||||
Name: "serv",
|
||||
Usage: "This command should only be called by SSH shell",
|
||||
Description: `Serv provide access auth for repositories`,
|
||||
Description: "Serv provides access auth for repositories",
|
||||
Action: runServ,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
|
|
|
@ -1262,6 +1262,9 @@ ROUTER = console
|
|||
;; List of file extensions that should be rendered/edited as Markdown
|
||||
;; Separate the extensions with a comma. To render files without any extension as markdown, just put a comma
|
||||
;FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd
|
||||
;;
|
||||
;; Enables math inline and block detection
|
||||
;ENABLE_MATH = true
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -2150,7 +2153,7 @@ ROUTER = console
|
|||
;[api]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Enables Swagger. True or false; default is true.
|
||||
;; Enables the API documentation endpoints (/api/swagger, /api/v1/swagger, …). True or false.
|
||||
;ENABLE_SWAGGER = true
|
||||
;; Max number of items in a page
|
||||
;MAX_RESPONSE_ITEMS = 50
|
||||
|
@ -2158,7 +2161,7 @@ ROUTER = console
|
|||
;DEFAULT_PAGING_NUM = 30
|
||||
;; Default and maximum number of items per page for git trees api
|
||||
;DEFAULT_GIT_TREES_PER_PAGE = 1000
|
||||
;; Default size of a blob returned by the blobs API (default is 10MiB)
|
||||
;; Default max size of a blob returned by the blobs API (default is 10MiB)
|
||||
;DEFAULT_MAX_BLOB_SIZE = 10485760
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -236,6 +236,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
|||
- `CUSTOM_URL_SCHEMES`: Use a comma separated list (ftp,git,svn) to indicate additional
|
||||
URL hyperlinks to be rendered in Markdown. URLs beginning in http and https are
|
||||
always displayed
|
||||
- `ENABLE_MATH`: **true**: Enables detection of `\(...\)`, `\[...\]`, `$...$` and `$$...$$` blocks as math blocks.
|
||||
|
||||
## Server (`server`)
|
||||
|
||||
|
@ -851,7 +852,7 @@ Default templates for project boards:
|
|||
- `SCHEDULE` accept formats
|
||||
- Full crontab specs, e.g. `* * * * * ?`
|
||||
- Descriptors, e.g. `@midnight`, `@every 1h30m` ...
|
||||
- See more: [cron decument](https://pkg.go.dev/github.com/gogs/cron@v0.0.0-20171120032916-9f6c956d3e14)
|
||||
- See more: [cron documentation](https://pkg.go.dev/github.com/gogs/cron@v0.0.0-20171120032916-9f6c956d3e14)
|
||||
|
||||
### Basic cron tasks - enabled by default
|
||||
|
||||
|
@ -1014,11 +1015,11 @@ Default templates for project boards:
|
|||
|
||||
## API (`api`)
|
||||
|
||||
- `ENABLE_SWAGGER`: **true**: Enables /api/swagger, /api/v1/swagger etc. endpoints. True or false; default is true.
|
||||
- `ENABLE_SWAGGER`: **true**: Enables the API documentation endpoints (`/api/swagger`, `/api/v1/swagger`, …). True or false.
|
||||
- `MAX_RESPONSE_ITEMS`: **50**: Max number of items in a page.
|
||||
- `DEFAULT_PAGING_NUM`: **30**: Default paging number of API.
|
||||
- `DEFAULT_GIT_TREES_PER_PAGE`: **1000**: Default and maximum number of items per page for Git trees API.
|
||||
- `DEFAULT_MAX_BLOB_SIZE`: **10485760**: Default max size of a blob that can be return by the blobs API.
|
||||
- `DEFAULT_MAX_BLOB_SIZE`: **10485760** (10MiB): Default max size of a blob that can be returned by the blobs API.
|
||||
|
||||
## OAuth2 (`oauth2`)
|
||||
|
||||
|
|
|
@ -299,7 +299,7 @@ test01.xls: application/vnd.ms-excel; charset=binary
|
|||
|
||||
## API (`api`)
|
||||
|
||||
- `ENABLE_SWAGGER`: **true**: 是否启用swagger路由 /api/swagger, /api/v1/swagger etc. endpoints. True 或 false; 默认是 true.
|
||||
- `ENABLE_SWAGGER`: **true**: 是否启用swagger路由 /api/swagger, /api/v1/swagger etc. endpoints. True 或 false.
|
||||
- `MAX_RESPONSE_ITEMS`: **50**: 一个页面最大的项目数。
|
||||
- `DEFAULT_PAGING_NUM`: **30**: API中默认分页条数。
|
||||
- `DEFAULT_GIT_TREES_PER_PAGE`: **1000**: GIT TREES API每页的默认最大项数.
|
||||
|
|
|
@ -121,7 +121,7 @@ Apart from `extra_links.tmpl` and `extra_tabs.tmpl`, there are other useful temp
|
|||
- `body_inner_pre.tmpl`, before the top navigation bar, but already inside the main container `<div class="full height">`.
|
||||
- `body_inner_post.tmpl`, before the end of the main container.
|
||||
- `body_outer_post.tmpl`, before the bottom `<footer>` element.
|
||||
- `footer.tmpl`, right before the end of the `<body>` tag, a good place for additional Javascript.
|
||||
- `footer.tmpl`, right before the end of the `<body>` tag, a good place for additional JavaScript.
|
||||
|
||||
#### Example: PlantUML
|
||||
|
||||
|
@ -129,7 +129,7 @@ You can add [PlantUML](https://plantuml.com/) support to Gitea's markdown by usi
|
|||
The data is encoded and sent to the PlantUML server which generates the picture. There is an online
|
||||
demo server at http://www.plantuml.com/plantuml, but if you (or your users) have sensitive data you
|
||||
can set up your own [PlantUML server](https://plantuml.com/server) instead. To set up PlantUML rendering,
|
||||
copy javascript files from https://gitea.com/davidsvantesson/plantuml-code-highlight and put them in your
|
||||
copy JavaScript files from https://gitea.com/davidsvantesson/plantuml-code-highlight and put them in your
|
||||
`$GITEA_CUSTOM/public` folder. Then add the following to `custom/footer.tmpl`:
|
||||
|
||||
```html
|
||||
|
@ -137,9 +137,9 @@ copy javascript files from https://gitea.com/davidsvantesson/plantuml-code-highl
|
|||
$(async () => {
|
||||
if (!$('.language-plantuml').length) return;
|
||||
await Promise.all([
|
||||
$.getScript('https://your-server.com/deflate.js'),
|
||||
$.getScript('https://your-server.com/encode.js'),
|
||||
$.getScript('https://your-server.com/plantuml_codeblock_parse.js'),
|
||||
$.getScript('https://your-gitea-server.com/assets/deflate.js'),
|
||||
$.getScript('https://your-gitea-server.com/assets/encode.js'),
|
||||
$.getScript('https://your-gitea-server.com/assets/plantuml_codeblock_parse.js'),
|
||||
]);
|
||||
// Replace call with address to your plantuml server
|
||||
parsePlantumlCodeBlocks("https://www.plantuml.com/plantuml");
|
||||
|
|
|
@ -74,12 +74,13 @@ RENDER_COMMAND = "timeout 30s pandoc +RTS -M512M -RTS -f rst"
|
|||
IS_INPUT_FILE = false
|
||||
```
|
||||
|
||||
If your external markup relies on additional classes and attributes on the generated HTML elements, you might need to enable custom sanitizer policies. Gitea uses the [`bluemonday`](https://godoc.org/github.com/microcosm-cc/bluemonday) package as our HTML sanitizier. The example below will support [KaTeX](https://katex.org/) output from [`pandoc`](https://pandoc.org/).
|
||||
If your external markup relies on additional classes and attributes on the generated HTML elements, you might need to enable custom sanitizer policies. Gitea uses the [`bluemonday`](https://godoc.org/github.com/microcosm-cc/bluemonday) package as our HTML sanitizer. The example below could be used to support server-side [KaTeX](https://katex.org/) rendering output from [`pandoc`](https://pandoc.org/).
|
||||
|
||||
```ini
|
||||
[markup.sanitizer.TeX]
|
||||
; Pandoc renders TeX segments as <span>s with the "math" class, optionally
|
||||
; with "inline" or "display" classes depending on context.
|
||||
; - note this is different from the built-in math support in our markdown parser which uses <code>
|
||||
ELEMENT = span
|
||||
ALLOW_ATTR = class
|
||||
REGEXP = ^\s*((math(\s+|$)|inline(\s+|$)|display(\s+|$)))+
|
||||
|
|
|
@ -53,6 +53,8 @@ _Symbols used in table:_
|
|||
| WebAuthn (2FA) | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ? |
|
||||
| Built-in CI/CD | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Subgroups: groups within groups | [✘](https://github.com/go-gitea/gitea/issues/1872) | ✘ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
| Mermaid diagrams in Markdown | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Math syntax in Markdown | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
|
||||
## Code management
|
||||
|
||||
|
|
|
@ -31,101 +31,101 @@ _表格中的符号含义:_
|
|||
|
||||
#### 主要特性
|
||||
|
||||
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
|-----------------------|-------|------|-----------|-----------|-----------|-----------|--------------|
|
||||
| 开源免费 | ✓ | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ |
|
||||
| 低资源开销 (RAM/CPU) | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| 支持多种数据库 | ✓ | ✓ | ✘ | ⁄ | ⁄ | ✓ | ✓ |
|
||||
| 支持多种操作系统 | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ |
|
||||
| 升级简便 | ✓ | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
| 支持 Markdown | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 支持 Orgmode | ✓ | ✘ | ✓ | ✘ | ✘ | ✘ | ? |
|
||||
| 支持 CSV | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ | ? |
|
||||
| 支持第三方渲染工具 | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ | ? |
|
||||
| Git 驱动的静态 pages | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Git 驱动的集成化 wiki | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 部署令牌 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 仓库写权限令牌 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✓ |
|
||||
| 内置容器 Registry | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 外部 Git 镜像 | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ |
|
||||
| WebAuthn (2FA) | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ? |
|
||||
| 内置 CI/CD | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 子组织:组织内的组织 | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
| --------------------- | -------------------------------------------------- | ---- | --------- | --------- | --------- | -------------- | ------------ |
|
||||
| 开源免费 | ✓ | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ |
|
||||
| 低资源开销 (RAM/CPU) | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| 支持多种数据库 | ✓ | ✓ | ✘ | ⁄ | ⁄ | ✓ | ✓ |
|
||||
| 支持多种操作系统 | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ |
|
||||
| 升级简便 | ✓ | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
| 支持 Markdown | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 支持 Orgmode | ✓ | ✘ | ✓ | ✘ | ✘ | ✘ | ? |
|
||||
| 支持 CSV | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ | ? |
|
||||
| 支持第三方渲染工具 | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ | ? |
|
||||
| Git 驱动的静态 pages | [✘](https://github.com/go-gitea/gitea/issues/302) | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Git 驱动的集成化 wiki | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (cloud only) | ✘ |
|
||||
| 部署令牌 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 仓库写权限令牌 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 内置容器 Registry | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 外部 Git 镜像 | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ |
|
||||
| WebAuthn (2FA) | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ? |
|
||||
| 内置 CI/CD | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 子组织:组织内的组织 | [✘](https://github.com/go-gitea/gitea/issues/1872) | ✘ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
|
||||
#### 代码管理
|
||||
|
||||
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
|------------------------------------------|-------|------|-----------|-----------|-----------|-----------|--------------|
|
||||
| 仓库主题描述 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 仓库内代码搜索 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 全局代码搜索 | ✓ | ✘ | ✓ | ✘ | ✓ | ✓ | ✓ |
|
||||
| Git LFS 2.0 | ✓ | ✘ | ✓ | ✓ | ✓ | ⁄ | ✓ |
|
||||
| 组织里程碑 | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 细粒度用户角色 (例如 Code, Issues, Wiki) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 提交人的身份验证 | ⁄ | ✘ | ? | ✓ | ✓ | ✓ | ✘ |
|
||||
| GPG 签名的提交 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| SSH 签名的提交 | ✓ | ✘ | ✘ | ✘ | ✘ | ? | ? |
|
||||
| 拒绝未用通过验证的提交 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✓ |
|
||||
| 仓库活跃度页面 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 分支管理 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 建立新分支 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 在线代码编辑 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 提交的统计图表 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 模板仓库 | ✓ | ✘ | ✓ | ✘ | ✓ | ✓ | ✘ |
|
||||
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
| ---------------------------------------- | ------------------------------------------------ | ---- | --------- | --------- | --------- | --------- | ------------ |
|
||||
| 仓库主题描述 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 仓库内代码搜索 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 全局代码搜索 | ✓ | ✘ | ✓ | ✘ | ✓ | ✓ | ✓ |
|
||||
| Git LFS 2.0 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 组织里程碑 | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 细粒度用户角色 (例如 Code, Issues, Wiki) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 提交人的身份验证 | ⁄ | ✘ | ? | ✓ | ✓ | ✓ | ✘ |
|
||||
| GPG 签名的提交 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| SSH 签名的提交 | ✓ | ✘ | ✘ | ✘ | ✘ | ? | ? |
|
||||
| 拒绝未用通过验证的提交 | [✓](https://github.com/go-gitea/gitea/pull/9708) | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 仓库活跃度页面 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 分支管理 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 建立新分支 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 在线代码编辑 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 提交的统计图表 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 模板仓库 | [✓](https://github.com/go-gitea/gitea/pull/8768) | ✘ | ✓ | ✘ | ✓ | ✓ | ✘ |
|
||||
|
||||
#### Issue 管理
|
||||
#### 工单管理
|
||||
|
||||
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
|----------------------|-------|------|-----------|-----------|-----------|-----------|--------------|
|
||||
| 跟踪 Issue | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Issue 模板 | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 标签 | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 跟踪时间 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Issue 可有多个负责人 | ✓ | ✘ | ✓ | ✘ | ✓ | ✘ | ✘ |
|
||||
| 关联的 issues | ✘ | ✘ | ⁄ | ✘ | ✓ | ✘ | ✘ |
|
||||
| 私密 issues | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 评论反馈 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 锁定讨论 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Issue 批量处理 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Issue 看板 | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 从 issues 创建分支 | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Issue 搜索 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 全局 Issue 搜索 | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Issue 依赖 | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| 通过 Email 创建工单 | [✘](https://github.com/go-gitea/gitea/issues/6226) | [✘](https://github.com/gogs/gogs/issues/2602) | ✘ | ✘ | ✓ | ✓ | ✘ |
|
||||
| Service Desk | [✘](https://github.com/go-gitea/gitea/issues/6219) | ✘ | ✘ | ✘ | ✓ | ✘ | ✘ |
|
||||
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
| ------------------- | -------------------------------------------------- | --------------------------------------------- | --------- | ----------------------------------------------------------------------- | --------- | -------------- | ------------ |
|
||||
| 工单跟踪 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (cloud only) | ✘ |
|
||||
| 工单模板 | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 标签 | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 时间跟踪 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 支持多个负责人 | ✓ | ✘ | ✓ | ✘ | ✓ | ✘ | ✘ |
|
||||
| 关联的工单 | ✘ | ✘ | ⁄ | [✓](https://docs.gitlab.com/ce/user/project/issues/related_issues.html) | ✓ | ✘ | ✘ |
|
||||
| 私密工单 | [✘](https://github.com/go-gitea/gitea/issues/3217) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 评论反馈 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 锁定讨论 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 工单批处理 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 工单看板 | [✓](https://github.com/go-gitea/gitea/pull/8346) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 从工单创建分支 | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 工单搜索 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 工单全局搜索 | [✘](https://github.com/go-gitea/gitea/issues/2434) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 工单依赖关系 | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| 通过 Email 创建工单 | [✘](https://github.com/go-gitea/gitea/issues/6226) | [✘](https://github.com/gogs/gogs/issues/2602) | ✘ | ✘ | ✓ | ✓ | ✘ |
|
||||
| 服务台 | [✘](https://github.com/go-gitea/gitea/issues/6219) | ✘ | ✘ | [✓](https://gitlab.com/groups/gitlab-org/-/epics/3103) | ✓ | ✘ | ✘ |
|
||||
|
||||
#### Pull/Merge requests
|
||||
|
||||
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
|--------------------------------------|-------|------|-----------|-----------|-----------|-----------|--------------|
|
||||
| Pull/Merge requests | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Squash merging | ✓ | ✘ | ✓ | ✘ | ✓ | ✓ | ✓ |
|
||||
| Rebase merging | ✓ | ✓ | ✓ | ✘ | ⁄ | ✘ | ✓ |
|
||||
| 评论 Pull/Merge request 中的某行代码 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 指定 Pull/Merge request 的审核人 | ✓ | ✘ | ⁄ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 解决 Merge 冲突 | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 限制某些用户的 push 和 merge 权限 | ✓ | ✘ | ✓ | ⁄ | ✓ | ✓ | ✓ |
|
||||
| 回退某些 commits 或 merge request | ✘ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Pull/Merge requests 模板 | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 查看 Cherry-picking 的更改 | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 下载 Patch | ✓ | ✘ | ✓ | ✓ | ✓ | / | ✘ |
|
||||
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
| ------------------------------------ | -------------------------------------------------- | ---- | --------- | --------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------ | ------------ |
|
||||
| Pull/Merge requests | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Squash merging | ✓ | ✘ | ✓ | [✓](https://docs.gitlab.com/ce/user/project/merge_requests/squash_and_merge.html) | ✓ | ✓ | ✓ |
|
||||
| Rebase merging | ✓ | ✓ | ✓ | ✘ | ⁄ | ✘ | ✓ |
|
||||
| 评论 Pull/Merge request 中的某行代码 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 指定 Pull/Merge request 的审核人 | ✓ | ✘ | ⁄ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 解决 Merge 冲突 | [✘](https://github.com/go-gitea/gitea/issues/5158) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 限制某些用户的 push 和 merge 权限 | ✓ | ✘ | ✓ | ⁄ | ✓ | ✓ | ✓ |
|
||||
| 回退某些 commits 或 merge request | [✓](https://github.com/go-gitea/gitea/issues/5158) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Pull/Merge requests 模板 | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 查看 Cherry-picking 的更改 | [✓](https://github.com/go-gitea/gitea/issues/5158) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 下载 Patch | ✓ | ✘ | ✓ | ✓ | ✓ | [/](https://jira.atlassian.com/plugins/servlet/mobile#issue/BCLOUD-8323) | ✘ |
|
||||
|
||||
#### 第三方集成
|
||||
|
||||
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
|----------------------------|-------|------|-----------|-----------|-----------|-----------|--------------|
|
||||
| 支持 Webhook | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 自定义 Git 钩子 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 集成 AD / LDAP | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 支持多个 LDAP / AD 服务 | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ |
|
||||
| LDAP 用户同步 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| SAML 2.0 service provider | [✘](https://github.com/go-gitea/gitea/issues/5512) | [✘](https://github.com/gogs/gogs/issues/1221) | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 支持 OpenId 连接 | ✓ | ✘ | ✓ | ✓ | ✓ | ? | ✘ |
|
||||
| 集成 OAuth 2.0(外部授权) | ✓ | ✘ | ⁄ | ✓ | ✓ | ? | ✓ |
|
||||
| 作为 OAuth 2.0 provider | [✓](https://github.com/go-gitea/gitea/pull/5378) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 二次验证 (2FA) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 集成 Mattermost/Slack | ✓ | ✓ | ⁄ | ✓ | ✓ | ⁄ | ✓ |
|
||||
| 集成 Discord | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 集成 Microsoft Teams | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 显示外部 CI/CD 的状态 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
| -------------------------- | -------------------------------------------------- | --------------------------------------------- | --------- | --------- | --------- | --------- | ------------ |
|
||||
| 支持 Webhook | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 自定义 Git 钩子 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 集成 AD / LDAP | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 支持多个 LDAP / AD 服务 | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ |
|
||||
| LDAP 用户同步 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| SAML 2.0 service provider | [✘](https://github.com/go-gitea/gitea/issues/5512) | [✘](https://github.com/gogs/gogs/issues/1221) | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 支持 OpenId 连接 | ✓ | ✘ | ✓ | ✓ | ✓ | ? | ✘ |
|
||||
| 集成 OAuth 2.0(外部授权) | ✓ | ✘ | ⁄ | ✓ | ✓ | ? | ✓ |
|
||||
| 作为 OAuth 2.0 provider | [✓](https://github.com/go-gitea/gitea/pull/5378) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 二次验证 (2FA) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 集成 Mattermost/Slack | ✓ | ✓ | ⁄ | ✓ | ✓ | ⁄ | ✓ |
|
||||
| 集成 Discord | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| 集成 Microsoft Teams | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| 显示外部 CI/CD 的状态 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
|
|
|
@ -126,13 +126,13 @@ A "login prohibited" user is a user that is not allowed to log in to Gitea anymo
|
|||
|
||||
## What is Swagger?
|
||||
|
||||
[Swagger](https://swagger.io/) is what Gitea uses for its API.
|
||||
[Swagger](https://swagger.io/) is what Gitea uses for its API documentation.
|
||||
|
||||
All Gitea instances have the built-in API, though it can be disabled by setting `ENABLE_SWAGGER` to `false` in the `api` section of your `app.ini`
|
||||
All Gitea instances have the built-in API and there is no way to disable it completely.
|
||||
You can, however, disable showing its documentation by setting `ENABLE_SWAGGER` to `false` in the `api` section of your `app.ini`.
|
||||
For more information, refer to Gitea's [API docs]({{< relref "doc/developers/api-usage.en-us.md" >}}).
|
||||
|
||||
For more information, refer to Gitea's [API docs]({{< relref "doc/developers/api-usage.en-us.md" >}})
|
||||
|
||||
[Swagger Example](https://try.gitea.io/api/swagger)
|
||||
You can see the latest API (for example) on <https://try.gitea.io/api/swagger>.
|
||||
|
||||
## Adjusting your server for public/private use
|
||||
|
||||
|
@ -214,13 +214,13 @@ Our translations are currently crowd-sourced on our [Crowdin project](https://cr
|
|||
|
||||
Whether you want to change a translation or add a new one, it will need to be there as all translations are overwritten in our CI via the Crowdin integration.
|
||||
|
||||
## Hooks aren't running
|
||||
## Push Hook / Webhook aren't running
|
||||
|
||||
If Gitea is not running hooks, a common cause is incorrect setup of SSH keys.
|
||||
If you can push but can't see push activities on the home dashboard, or the push doesn't trigger webhook, there are a few possibilities:
|
||||
|
||||
See [SSH Issues](#ssh-issues) for more information.
|
||||
|
||||
You can also try logging into the administration panel and running the `Resynchronize pre-receive, update and post-receive hooks of all repositories.` option.
|
||||
1. The git hooks are out of sync: run "Resynchronize pre-receive, update and post-receive hooks of all repositories" on the site admin panel
|
||||
2. The git repositories (and hooks) are stored on some filesystems (ex: mounted by NAS) which don't support script execution, make sure the filesystem supports `chmod a+x any-script`
|
||||
3. If you are using docker, make sure Docker Server (not the client) >= 20.10.6
|
||||
|
||||
## SSH issues
|
||||
|
||||
|
|
|
@ -127,6 +127,10 @@ npm dist-tag add test_package@1.0.2 release
|
|||
|
||||
The tag name must not be a valid version. All tag names which are parsable as a version are rejected.
|
||||
|
||||
## Search packages
|
||||
|
||||
The registry supports [searching](https://docs.npmjs.com/cli/v7/commands/npm-search/) but does not support special search qualifiers like `author:gitea`.
|
||||
|
||||
## Supported commands
|
||||
|
||||
```
|
||||
|
@ -136,4 +140,5 @@ npm publish
|
|||
npm unpublish
|
||||
npm dist-tag
|
||||
npm view
|
||||
npm search
|
||||
```
|
||||
|
|
|
@ -131,7 +131,8 @@ You can try it out using [the online demo](https://try.gitea.io/).
|
|||
- Environment variables
|
||||
- Command line options
|
||||
- Multi-language support ([21 languages](https://github.com/go-gitea/gitea/tree/main/options/locale))
|
||||
- [Mermaid](https://mermaidjs.github.io/) Diagram support
|
||||
- [Mermaid](https://mermaidjs.github.io/) diagrams in Markdown
|
||||
- Math syntax in Markdown
|
||||
- Mail service
|
||||
- Notifications
|
||||
- Registration confirmation
|
||||
|
|
12
go.mod
12
go.mod
|
@ -15,7 +15,7 @@ require (
|
|||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
github.com/alecthomas/chroma v0.10.0
|
||||
github.com/alecthomas/chroma/v2 v2.3.0
|
||||
github.com/blevesearch/bleve/v2 v2.3.2
|
||||
github.com/buildkite/terminal-to-html/v3 v3.7.0
|
||||
github.com/caddyserver/certmagic v0.17.0
|
||||
|
@ -68,7 +68,7 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.16
|
||||
github.com/mattn/go-sqlite3 v1.14.13
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.19
|
||||
github.com/microcosm-cc/bluemonday v1.0.20
|
||||
github.com/minio/minio-go/v7 v7.0.35
|
||||
github.com/msteinert/pam v1.0.0
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
|
@ -89,13 +89,13 @@ require (
|
|||
github.com/urfave/cli v1.22.9
|
||||
github.com/xanzy/go-gitlab v0.73.1
|
||||
github.com/yohcop/openid-go v1.0.0
|
||||
github.com/yuin/goldmark v1.4.13
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
|
||||
github.com/yuin/goldmark v1.4.15
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87
|
||||
github.com/yuin/goldmark-meta v1.1.0
|
||||
go.jolheiser.com/hcaptcha v0.0.4
|
||||
go.jolheiser.com/pwn v0.0.3
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
|
||||
golang.org/x/net v0.0.0-20220927171203-f486391704dc
|
||||
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261
|
||||
golang.org/x/text v0.3.7
|
||||
|
@ -103,6 +103,7 @@ require (
|
|||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
mvdan.cc/xurls/v2 v2.4.0
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
||||
xorm.io/builder v0.3.11
|
||||
|
@ -290,7 +291,6 @@ require (
|
|||
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.1 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
)
|
||||
|
||||
|
|
23
go.sum
23
go.sum
|
@ -158,9 +158,12 @@ github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ
|
|||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
||||
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
|
||||
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||
github.com/alecthomas/chroma/v2 v2.3.0 h1:83xfxrnjv8eK+Cf8qZDzNo3PPF9IbTWHs7z28GY6D0U=
|
||||
github.com/alecthomas/chroma/v2 v2.3.0/go.mod h1:mZxeWZlxP2Dy+/8cBob2PYd8O2DwNAzave5AY7A2eQw=
|
||||
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
|
@ -1066,8 +1069,8 @@ github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
|
|||
github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
|
||||
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
|
||||
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||
github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c=
|
||||
github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE=
|
||||
github.com/microcosm-cc/bluemonday v1.0.20 h1:flpzsq4KU3QIYAYGV/szUat7H+GPOXR0B2JU5A1Wp8Y=
|
||||
github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
|
@ -1467,11 +1470,10 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
||||
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg=
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU=
|
||||
github.com/yuin/goldmark v1.4.15 h1:CFa84T0goNn/UIXYS+dmjjVxMyTAvpOmzld40N/nfK0=
|
||||
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87 h1:Py16JEzkSdKAtEFJjiaYLYBOWGXc1r/xHj/Q/5lA37k=
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
|
||||
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
|
||||
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
|
@ -1708,8 +1710,9 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
|||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ=
|
||||
golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
|
|
@ -218,6 +218,11 @@ func (a *Action) GetRepoLink() string {
|
|||
return path.Join(setting.AppSubURL, "/", url.PathEscape(a.GetRepoUserName()), url.PathEscape(a.GetRepoName()))
|
||||
}
|
||||
|
||||
// GetRepoAbsoluteLink returns the absolute link to action repository.
|
||||
func (a *Action) GetRepoAbsoluteLink() string {
|
||||
return setting.AppURL + url.PathEscape(a.GetRepoUserName()) + "/" + url.PathEscape(a.GetRepoName())
|
||||
}
|
||||
|
||||
// GetCommentLink returns link to action comment.
|
||||
func (a *Action) GetCommentLink() string {
|
||||
return a.getCommentLink(db.DefaultContext)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issue_model "code.gitea.io/gitea/models/issues"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
@ -20,7 +21,7 @@ import (
|
|||
|
||||
func TestAction_GetRepoPath(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{})
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||
action := &activities_model.Action{RepoID: repo.ID}
|
||||
assert.Equal(t, path.Join(owner.Name, repo.Name), action.GetRepoPath())
|
||||
|
@ -28,12 +29,15 @@ func TestAction_GetRepoPath(t *testing.T) {
|
|||
|
||||
func TestAction_GetRepoLink(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{})
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||
action := &activities_model.Action{RepoID: repo.ID}
|
||||
comment := unittest.AssertExistsAndLoadBean(t, &issue_model.Comment{ID: 2})
|
||||
action := &activities_model.Action{RepoID: repo.ID, CommentID: comment.ID}
|
||||
setting.AppSubURL = "/suburl"
|
||||
expected := path.Join(setting.AppSubURL, owner.Name, repo.Name)
|
||||
assert.Equal(t, expected, action.GetRepoLink())
|
||||
assert.Equal(t, repo.HTMLURL(), action.GetRepoAbsoluteLink())
|
||||
assert.Equal(t, comment.HTMLURL(), action.GetCommentLink())
|
||||
}
|
||||
|
||||
func TestGetFeeds(t *testing.T) {
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
|
@ -56,6 +57,18 @@ func (app *OAuth2Application) PrimaryRedirectURI() string {
|
|||
|
||||
// ContainsRedirectURI checks if redirectURI is allowed for app
|
||||
func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
|
||||
uri, err := url.Parse(redirectURI)
|
||||
// ignore port for http loopback uris following https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
|
||||
if err == nil && uri.Scheme == "http" && uri.Port() != "" {
|
||||
ip := net.ParseIP(uri.Hostname())
|
||||
if ip != nil && ip.IsLoopback() {
|
||||
// strip port
|
||||
uri.Host = uri.Hostname()
|
||||
if util.IsStringInSlice(uri.String(), app.RedirectURIs, true) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return util.IsStringInSlice(redirectURI, app.RedirectURIs, true)
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,26 @@ func TestOAuth2Application_ContainsRedirectURI(t *testing.T) {
|
|||
assert.False(t, app.ContainsRedirectURI("d"))
|
||||
}
|
||||
|
||||
func TestOAuth2Application_ContainsRedirectURI_WithPort(t *testing.T) {
|
||||
app := &auth_model.OAuth2Application{
|
||||
RedirectURIs: []string{"http://127.0.0.1/", "http://::1/", "http://192.168.0.1/", "http://intranet/", "https://127.0.0.1/"},
|
||||
}
|
||||
|
||||
// http loopback uris should ignore port
|
||||
// https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
|
||||
assert.True(t, app.ContainsRedirectURI("http://127.0.0.1:3456/"))
|
||||
assert.True(t, app.ContainsRedirectURI("http://127.0.0.1/"))
|
||||
assert.True(t, app.ContainsRedirectURI("http://[::1]:3456/"))
|
||||
|
||||
// not http
|
||||
assert.False(t, app.ContainsRedirectURI("https://127.0.0.1:3456/"))
|
||||
// not loopback
|
||||
assert.False(t, app.ContainsRedirectURI("http://192.168.0.1:9954/"))
|
||||
assert.False(t, app.ContainsRedirectURI("http://intranet:3456/"))
|
||||
// unparseable
|
||||
assert.False(t, app.ContainsRedirectURI(":"))
|
||||
}
|
||||
|
||||
func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
||||
|
|
|
@ -124,3 +124,15 @@
|
|||
repo_id: 24
|
||||
mode: 1
|
||||
|
||||
-
|
||||
id: 22
|
||||
user_id: 31
|
||||
repo_id: 27
|
||||
mode: 4
|
||||
|
||||
-
|
||||
id: 23
|
||||
user_id: 31
|
||||
repo_id: 28
|
||||
mode: 4
|
||||
|
||||
|
|
|
@ -12,3 +12,8 @@
|
|||
id: 3
|
||||
user_id: 2
|
||||
follow_id: 8
|
||||
|
||||
-
|
||||
id: 4
|
||||
user_id: 31
|
||||
follow_id: 33
|
||||
|
|
|
@ -69,3 +69,9 @@
|
|||
uid: 2
|
||||
org_id: 17
|
||||
is_public: true
|
||||
|
||||
-
|
||||
id: 13
|
||||
uid: 31
|
||||
org_id: 19
|
||||
is_public: true
|
||||
|
|
|
@ -60,8 +60,8 @@
|
|||
head_repo_id: 1
|
||||
base_repo_id: 1
|
||||
head_branch: pr-to-update
|
||||
base_branch: branch1
|
||||
merge_base: 1234567890abcdef
|
||||
base_branch: branch2
|
||||
merge_base: 985f0301dba5e7b34be866819cd15ad3d8f508ee
|
||||
has_merged: false
|
||||
|
||||
-
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
name: Owners
|
||||
authorize: 4 # owner
|
||||
num_repos: 2
|
||||
num_members: 1
|
||||
num_members: 2
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
|
|
|
@ -87,3 +87,9 @@
|
|||
org_id: 17
|
||||
team_id: 9
|
||||
uid: 29
|
||||
|
||||
-
|
||||
id: 16
|
||||
org_id: 19
|
||||
team_id: 6
|
||||
uid: 31
|
||||
|
|
|
@ -345,7 +345,7 @@
|
|||
avatar_email: user19@example.com
|
||||
num_repos: 2
|
||||
is_active: true
|
||||
num_members: 1
|
||||
num_members: 2
|
||||
num_teams: 1
|
||||
|
||||
-
|
||||
|
@ -572,6 +572,8 @@
|
|||
avatar: avatar31
|
||||
avatar_email: user31@example.com
|
||||
num_repos: 0
|
||||
num_followers: 0
|
||||
num_following: 1
|
||||
is_active: true
|
||||
|
||||
-
|
||||
|
@ -590,3 +592,23 @@
|
|||
avatar_email: user30@example.com
|
||||
num_repos: 0
|
||||
is_active: true
|
||||
|
||||
-
|
||||
id: 33
|
||||
lower_name: user33
|
||||
name: user33
|
||||
login_name: user33
|
||||
full_name: User 33 (Limited Visibility)
|
||||
email: user33@example.com
|
||||
passwd_hash_algo: argon2
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
|
||||
type: 0 # individual
|
||||
salt: ZogKvWdyEx
|
||||
is_admin: false
|
||||
visibility: 1
|
||||
avatar: avatar33
|
||||
avatar_email: user33@example.com
|
||||
num_repos: 0
|
||||
num_followers: 1
|
||||
num_following: 0
|
||||
is_active: true
|
||||
|
|
|
@ -1186,6 +1186,7 @@ type IssuesOptions struct { //nolint
|
|||
PosterID int64
|
||||
MentionedID int64
|
||||
ReviewRequestedID int64
|
||||
SubscriberID int64
|
||||
MilestoneIDs []int64
|
||||
ProjectID int64
|
||||
ProjectBoardID int64
|
||||
|
@ -1299,6 +1300,10 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
|
|||
applyReviewRequestedCondition(sess, opts.ReviewRequestedID)
|
||||
}
|
||||
|
||||
if opts.SubscriberID > 0 {
|
||||
applySubscribedCondition(sess, opts.SubscriberID)
|
||||
}
|
||||
|
||||
if len(opts.MilestoneIDs) > 0 {
|
||||
sess.In("issue.milestone_id", opts.MilestoneIDs)
|
||||
}
|
||||
|
@ -1463,6 +1468,36 @@ func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64)
|
|||
reviewRequestedID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, reviewRequestedID)
|
||||
}
|
||||
|
||||
func applySubscribedCondition(sess *xorm.Session, subscriberID int64) *xorm.Session {
|
||||
return sess.And(
|
||||
builder.
|
||||
NotIn("issue.id",
|
||||
builder.Select("issue_id").
|
||||
From("issue_watch").
|
||||
Where(builder.Eq{"is_watching": false, "user_id": subscriberID}),
|
||||
),
|
||||
).And(
|
||||
builder.Or(
|
||||
builder.In("issue.id", builder.
|
||||
Select("issue_id").
|
||||
From("issue_watch").
|
||||
Where(builder.Eq{"is_watching": true, "user_id": subscriberID}),
|
||||
),
|
||||
builder.In("issue.id", builder.
|
||||
Select("issue_id").
|
||||
From("comment").
|
||||
Where(builder.Eq{"poster_id": subscriberID}),
|
||||
),
|
||||
builder.Eq{"issue.poster_id": subscriberID},
|
||||
builder.In("issue.repo_id", builder.
|
||||
Select("id").
|
||||
From("watch").
|
||||
Where(builder.Eq{"user_id": subscriberID, "mode": true}),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// CountIssuesByRepo map from repoID to number of issues matching the options
|
||||
func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
|
||||
e := db.GetEngine(db.DefaultContext)
|
||||
|
|
|
@ -181,6 +181,10 @@ func createReaction(ctx context.Context, opts *ReactionOptions) (*Reaction, erro
|
|||
Reaction: opts.Type,
|
||||
UserID: opts.DoerID,
|
||||
}
|
||||
if findOpts.CommentID == 0 {
|
||||
// explicit search of Issue Reactions where CommentID = 0
|
||||
findOpts.CommentID = -1
|
||||
}
|
||||
|
||||
existingR, _, err := FindReactions(ctx, findOpts)
|
||||
if err != nil {
|
||||
|
@ -256,16 +260,23 @@ func DeleteReaction(ctx context.Context, opts *ReactionOptions) error {
|
|||
CommentID: opts.CommentID,
|
||||
}
|
||||
|
||||
_, err := db.GetEngine(ctx).Where("original_author_id = 0").Delete(reaction)
|
||||
sess := db.GetEngine(ctx).Where("original_author_id = 0")
|
||||
if opts.CommentID == -1 {
|
||||
reaction.CommentID = 0
|
||||
sess.MustCols("comment_id")
|
||||
}
|
||||
|
||||
_, err := sess.Delete(reaction)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteIssueReaction deletes a reaction on issue.
|
||||
func DeleteIssueReaction(doerID, issueID int64, content string) error {
|
||||
return DeleteReaction(db.DefaultContext, &ReactionOptions{
|
||||
Type: content,
|
||||
DoerID: doerID,
|
||||
IssueID: issueID,
|
||||
Type: content,
|
||||
DoerID: doerID,
|
||||
IssueID: issueID,
|
||||
CommentID: -1,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -129,29 +129,11 @@ func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) {
|
|||
if opts.UserID > 0 {
|
||||
sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id")
|
||||
}
|
||||
|
||||
count, err := sess.
|
||||
Where(cond).
|
||||
Count(new(Team))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if opts.UserID > 0 {
|
||||
sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id")
|
||||
}
|
||||
|
||||
if opts.PageSize == -1 {
|
||||
opts.PageSize = int(count)
|
||||
} else {
|
||||
sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
|
||||
}
|
||||
sess = db.SetSessionPagination(sess, opts)
|
||||
|
||||
teams := make([]*Team, 0, opts.PageSize)
|
||||
if err = sess.
|
||||
Where(cond).
|
||||
OrderBy("lower_name").
|
||||
Find(&teams); err != nil {
|
||||
count, err := sess.Where(cond).OrderBy("lower_name").FindAndCount(&teams)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
|
|
|
@ -893,14 +893,19 @@ func UpdateUser(ctx context.Context, u *User, changePrimaryEmail bool, cols ...s
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !has {
|
||||
// 1. Update old primary email
|
||||
if _, err = e.Where("uid=? AND is_primary=?", u.ID, true).Cols("is_primary").Update(&EmailAddress{
|
||||
IsPrimary: false,
|
||||
}); err != nil {
|
||||
return err
|
||||
if has && emailAddress.UID != u.ID {
|
||||
return ErrEmailAlreadyUsed{
|
||||
Email: u.Email,
|
||||
}
|
||||
}
|
||||
// 1. Update old primary email
|
||||
if _, err = e.Where("uid=? AND is_primary=?", u.ID, true).Cols("is_primary").Update(&EmailAddress{
|
||||
IsPrimary: false,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !has {
|
||||
emailAddress.Email = u.Email
|
||||
emailAddress.UID = u.ID
|
||||
emailAddress.IsActivated = true
|
||||
|
@ -1267,7 +1272,7 @@ func isUserVisibleToViewerCond(viewer *User) builder.Cond {
|
|||
|
||||
// IsUserVisibleToViewer check if viewer is able to see user profile
|
||||
func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool {
|
||||
if viewer != nil && viewer.IsAdmin {
|
||||
if viewer != nil && (viewer.IsAdmin || viewer.ID == u.ID) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -1306,7 +1311,7 @@ func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if count < 0 {
|
||||
if count == 0 {
|
||||
// No common organization
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -302,10 +302,26 @@ func TestUpdateUser(t *testing.T) {
|
|||
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
assert.True(t, user.KeepActivityPrivate)
|
||||
|
||||
newEmail := "new_" + user.Email
|
||||
user.Email = newEmail
|
||||
assert.NoError(t, user_model.UpdateUser(db.DefaultContext, user, true))
|
||||
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
assert.Equal(t, newEmail, user.Email)
|
||||
|
||||
user.Email = "no mail@mail.org"
|
||||
assert.Error(t, user_model.UpdateUser(db.DefaultContext, user, true))
|
||||
}
|
||||
|
||||
func TestUpdateUserEmailAlreadyUsed(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
||||
|
||||
user2.Email = user3.Email
|
||||
err := user_model.UpdateUser(db.DefaultContext, user2, true)
|
||||
assert.True(t, user_model.IsErrEmailAlreadyUsed(err))
|
||||
}
|
||||
|
||||
func TestNewUserRedirect(t *testing.T) {
|
||||
// redirect to a completely new name
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
@ -400,3 +416,56 @@ func TestUnfollowUser(t *testing.T) {
|
|||
|
||||
unittest.CheckConsistencyFor(t, &user_model.User{})
|
||||
}
|
||||
|
||||
func TestIsUserVisibleToViewer(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) // admin, public
|
||||
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) // normal, public
|
||||
user20 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20}) // public, same team as user31
|
||||
user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29}) // public, is restricted
|
||||
user31 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31}) // private, same team as user20
|
||||
user33 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 33}) // limited, follows 31
|
||||
|
||||
test := func(u, viewer *user_model.User, expected bool) {
|
||||
name := func(u *user_model.User) string {
|
||||
if u == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return u.Name
|
||||
}
|
||||
assert.Equal(t, expected, user_model.IsUserVisibleToViewer(db.DefaultContext, u, viewer), "user %v should be visible to viewer %v: %v", name(u), name(viewer), expected)
|
||||
}
|
||||
|
||||
// admin viewer
|
||||
test(user1, user1, true)
|
||||
test(user20, user1, true)
|
||||
test(user31, user1, true)
|
||||
test(user33, user1, true)
|
||||
|
||||
// non admin viewer
|
||||
test(user4, user4, true)
|
||||
test(user20, user4, true)
|
||||
test(user31, user4, false)
|
||||
test(user33, user4, true)
|
||||
test(user4, nil, true)
|
||||
|
||||
// public user
|
||||
test(user4, user20, true)
|
||||
test(user4, user31, true)
|
||||
test(user4, user33, true)
|
||||
|
||||
// limited user
|
||||
test(user33, user33, true)
|
||||
test(user33, user4, true)
|
||||
test(user33, user29, false)
|
||||
test(user33, nil, false)
|
||||
|
||||
// private user
|
||||
test(user31, user31, true)
|
||||
test(user31, user4, false)
|
||||
test(user31, user20, true)
|
||||
test(user31, user29, false)
|
||||
test(user31, user33, true)
|
||||
test(user31, nil, false)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
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/templates"
|
||||
)
|
||||
|
@ -54,47 +55,11 @@ func packageAssignment(ctx *Context, errCb func(int, string, interface{})) {
|
|||
Owner: ctx.ContextUser,
|
||||
}
|
||||
|
||||
if ctx.Package.Owner.IsOrganization() {
|
||||
org := organization.OrgFromUser(ctx.Package.Owner)
|
||||
|
||||
// 1. Get user max authorize level for the org (may be none, if user is not member of the org)
|
||||
if ctx.Doer != nil {
|
||||
var err error
|
||||
ctx.Package.AccessMode, err = org.GetOrgUserMaxAuthorizeLevel(ctx.Doer.ID)
|
||||
if err != nil {
|
||||
errCb(http.StatusInternalServerError, "GetOrgUserMaxAuthorizeLevel", err)
|
||||
return
|
||||
}
|
||||
// If access mode is less than write check every team for more permissions
|
||||
if ctx.Package.AccessMode < perm.AccessModeWrite {
|
||||
teams, err := organization.GetUserOrgTeams(ctx, org.ID, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
errCb(http.StatusInternalServerError, "GetUserOrgTeams", err)
|
||||
return
|
||||
}
|
||||
for _, t := range teams {
|
||||
perm := t.UnitAccessModeCtx(ctx, unit.TypePackages)
|
||||
if ctx.Package.AccessMode < perm {
|
||||
ctx.Package.AccessMode = perm
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. If authorize level is none, check if org is visible to user
|
||||
if ctx.Package.AccessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) {
|
||||
ctx.Package.AccessMode = perm.AccessModeRead
|
||||
}
|
||||
} else {
|
||||
if ctx.Doer != nil && !ctx.Doer.IsGhost() {
|
||||
// 1. Check if user is package owner
|
||||
if ctx.Doer.ID == ctx.Package.Owner.ID {
|
||||
ctx.Package.AccessMode = perm.AccessModeOwner
|
||||
} else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic || ctx.Package.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
|
||||
ctx.Package.AccessMode = perm.AccessModeRead
|
||||
}
|
||||
} else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
|
||||
ctx.Package.AccessMode = perm.AccessModeRead
|
||||
}
|
||||
var err error
|
||||
ctx.Package.AccessMode, err = determineAccessMode(ctx)
|
||||
if err != nil {
|
||||
errCb(http.StatusInternalServerError, "determineAccessMode", err)
|
||||
return
|
||||
}
|
||||
|
||||
packageType := ctx.Params("type")
|
||||
|
@ -119,6 +84,57 @@ func packageAssignment(ctx *Context, errCb func(int, string, interface{})) {
|
|||
}
|
||||
}
|
||||
|
||||
func determineAccessMode(ctx *Context) (perm.AccessMode, error) {
|
||||
accessMode := perm.AccessModeNone
|
||||
|
||||
if setting.Service.RequireSignInView && ctx.Doer == nil {
|
||||
return accessMode, nil
|
||||
}
|
||||
|
||||
if ctx.Package.Owner.IsOrganization() {
|
||||
org := organization.OrgFromUser(ctx.Package.Owner)
|
||||
|
||||
// 1. Get user max authorize level for the org (may be none, if user is not member of the org)
|
||||
if ctx.Doer != nil {
|
||||
var err error
|
||||
accessMode, err = org.GetOrgUserMaxAuthorizeLevel(ctx.Doer.ID)
|
||||
if err != nil {
|
||||
return accessMode, err
|
||||
}
|
||||
// If access mode is less than write check every team for more permissions
|
||||
if accessMode < perm.AccessModeWrite {
|
||||
teams, err := organization.GetUserOrgTeams(ctx, org.ID, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
return accessMode, err
|
||||
}
|
||||
for _, t := range teams {
|
||||
perm := t.UnitAccessModeCtx(ctx, unit.TypePackages)
|
||||
if accessMode < perm {
|
||||
accessMode = perm
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. If authorize level is none, check if org is visible to user
|
||||
if accessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) {
|
||||
accessMode = perm.AccessModeRead
|
||||
}
|
||||
} else {
|
||||
if ctx.Doer != nil && !ctx.Doer.IsGhost() {
|
||||
// 1. Check if user is package owner
|
||||
if ctx.Doer.ID == ctx.Package.Owner.ID {
|
||||
accessMode = perm.AccessModeOwner
|
||||
} else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic || ctx.Package.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
|
||||
accessMode = perm.AccessModeRead
|
||||
}
|
||||
} else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
|
||||
accessMode = perm.AccessModeRead
|
||||
}
|
||||
}
|
||||
|
||||
return accessMode, nil
|
||||
}
|
||||
|
||||
// PackageContexter initializes a package context for a request.
|
||||
func PackageContexter(ctx gocontext.Context) func(next http.Handler) http.Handler {
|
||||
_, rnd := templates.HTMLRenderer(ctx)
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/log"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
webhook_service "code.gitea.io/gitea/services/webhook"
|
||||
)
|
||||
|
||||
|
@ -295,6 +296,7 @@ func ToOrganization(org *organization.Organization) *api.Organization {
|
|||
return &api.Organization{
|
||||
ID: org.ID,
|
||||
AvatarURL: org.AsUser().AvatarLink(),
|
||||
Name: org.Name,
|
||||
UserName: org.Name,
|
||||
FullName: org.FullName,
|
||||
Description: org.Description,
|
||||
|
@ -414,3 +416,36 @@ func ToLFSLock(l *git_model.LFSLock) *api.LFSLock {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ToChangedFile convert a gitdiff.DiffFile to api.ChangedFile
|
||||
func ToChangedFile(f *gitdiff.DiffFile, repo *repo_model.Repository, commit string) *api.ChangedFile {
|
||||
status := "changed"
|
||||
if f.IsDeleted {
|
||||
status = "deleted"
|
||||
} else if f.IsCreated {
|
||||
status = "added"
|
||||
} else if f.IsRenamed && f.Type == gitdiff.DiffFileCopy {
|
||||
status = "copied"
|
||||
} else if f.IsRenamed && f.Type == gitdiff.DiffFileRename {
|
||||
status = "renamed"
|
||||
} else if f.Addition == 0 && f.Deletion == 0 {
|
||||
status = "unchanged"
|
||||
}
|
||||
|
||||
file := &api.ChangedFile{
|
||||
Filename: f.GetDiffFileName(),
|
||||
Status: status,
|
||||
Additions: f.Addition,
|
||||
Deletions: f.Deletion,
|
||||
Changes: f.Addition + f.Deletion,
|
||||
HTMLURL: fmt.Sprint(repo.HTMLURL(), "/src/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
|
||||
ContentsURL: fmt.Sprint(repo.APIURL(), "/contents/", util.PathEscapeSegments(f.GetDiffFileName()), "?ref=", commit),
|
||||
RawURL: fmt.Sprint(repo.HTMLURL(), "/raw/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
|
||||
}
|
||||
|
||||
if status == "rename" {
|
||||
file.PreviousFilename = f.OldName
|
||||
}
|
||||
|
||||
return file
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
|
|||
case "160000":
|
||||
entry.entryMode = EntryModeCommit
|
||||
pos += 14 // skip over "160000 object "
|
||||
case "040000":
|
||||
case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons
|
||||
entry.entryMode = EntryModeTree
|
||||
pos += 12 // skip over "040000 tree "
|
||||
default:
|
||||
|
@ -119,7 +119,7 @@ loop:
|
|||
entry.entryMode = EntryModeSymlink
|
||||
case "160000":
|
||||
entry.entryMode = EntryModeCommit
|
||||
case "40000":
|
||||
case "40000", "40755": // git uses 40000 for tree object, but some users may get 40755 for unknown reasons
|
||||
entry.entryMode = EntryModeTree
|
||||
default:
|
||||
log.Debug("Unknown mode: %v", string(mode))
|
||||
|
|
|
@ -63,34 +63,32 @@ func (repo *Repository) IsBranchExist(name string) bool {
|
|||
// GetBranchNames returns branches from the repository, skipping skip initial branches and
|
||||
// returning at most limit branches, or all branches if limit is 0.
|
||||
func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
|
||||
return callShowRef(repo.Ctx, repo.Path, BranchPrefix, "--heads", skip, limit)
|
||||
return callShowRef(repo.Ctx, repo.Path, BranchPrefix, []string{BranchPrefix, "--sort=-committerdate"}, skip, limit)
|
||||
}
|
||||
|
||||
// WalkReferences walks all the references from the repository
|
||||
func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refname string) error) (int, error) {
|
||||
return walkShowRef(ctx, repoPath, "", 0, 0, walkfn)
|
||||
return walkShowRef(ctx, repoPath, nil, 0, 0, walkfn)
|
||||
}
|
||||
|
||||
// WalkReferences walks all the references from the repository
|
||||
// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
|
||||
func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
|
||||
var arg string
|
||||
var args []string
|
||||
switch refType {
|
||||
case ObjectTag:
|
||||
arg = "--tags"
|
||||
args = []string{TagPrefix, "--sort=-taggerdate"}
|
||||
case ObjectBranch:
|
||||
arg = "--heads"
|
||||
default:
|
||||
arg = ""
|
||||
args = []string{BranchPrefix, "--sort=-committerdate"}
|
||||
}
|
||||
|
||||
return walkShowRef(repo.Ctx, repo.Path, arg, skip, limit, walkfn)
|
||||
return walkShowRef(repo.Ctx, repo.Path, args, skip, limit, walkfn)
|
||||
}
|
||||
|
||||
// callShowRef return refs, if limit = 0 it will not limit
|
||||
func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) {
|
||||
countAll, err = walkShowRef(ctx, repoPath, arg, skip, limit, func(_, branchName string) error {
|
||||
branchName = strings.TrimPrefix(branchName, prefix)
|
||||
func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs []string, skip, limit int) (branchNames []string, countAll int, err error) {
|
||||
countAll, err = walkShowRef(ctx, repoPath, extraArgs, skip, limit, func(_, branchName string) error {
|
||||
branchName = strings.TrimPrefix(branchName, trimPrefix)
|
||||
branchNames = append(branchNames, branchName)
|
||||
|
||||
return nil
|
||||
|
@ -98,7 +96,7 @@ func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit
|
|||
return branchNames, countAll, err
|
||||
}
|
||||
|
||||
func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
|
||||
func walkShowRef(ctx context.Context, repoPath string, extraArgs []string, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
|
||||
stdoutReader, stdoutWriter := io.Pipe()
|
||||
defer func() {
|
||||
_ = stdoutReader.Close()
|
||||
|
@ -107,10 +105,8 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
|
|||
|
||||
go func() {
|
||||
stderrBuilder := &strings.Builder{}
|
||||
args := []string{"show-ref"}
|
||||
if arg != "" {
|
||||
args = append(args, arg)
|
||||
}
|
||||
args := []string{"for-each-ref", "--format=%(objectname) %(refname)"}
|
||||
args = append(args, extraArgs...)
|
||||
err := NewCommand(ctx, args...).Run(&RunOpts{
|
||||
Dir: repoPath,
|
||||
Stdout: stdoutWriter,
|
||||
|
@ -194,7 +190,7 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
|
|||
// GetRefsBySha returns all references filtered with prefix that belong to a sha commit hash
|
||||
func (repo *Repository) GetRefsBySha(sha, prefix string) ([]string, error) {
|
||||
var revList []string
|
||||
_, err := walkShowRef(repo.Ctx, repo.Path, "", 0, 0, func(walkSha, refname string) error {
|
||||
_, err := walkShowRef(repo.Ctx, repo.Path, nil, 0, 0, func(walkSha, refname string) error {
|
||||
if walkSha == sha && strings.HasPrefix(refname, prefix) {
|
||||
revList = append(revList, refname)
|
||||
}
|
||||
|
|
|
@ -22,14 +22,14 @@ func TestRepository_GetBranches(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Len(t, branches, 2)
|
||||
assert.EqualValues(t, 3, countAll)
|
||||
assert.ElementsMatch(t, []string{"branch1", "branch2"}, branches)
|
||||
assert.ElementsMatch(t, []string{"master", "branch2"}, branches)
|
||||
|
||||
branches, countAll, err = bareRepo1.GetBranchNames(0, 0)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, branches, 3)
|
||||
assert.EqualValues(t, 3, countAll)
|
||||
assert.ElementsMatch(t, []string{"branch1", "branch2", "master"}, branches)
|
||||
assert.ElementsMatch(t, []string{"master", "branch2", "branch1"}, branches)
|
||||
|
||||
branches, countAll, err = bareRepo1.GetBranchNames(5, 1)
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ func (repo *Repository) IsTagExist(name string) bool {
|
|||
// GetTags returns all tags of the repository.
|
||||
// returning at most limit tags, or all if limit is 0.
|
||||
func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) {
|
||||
tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, "--tags", skip, limit)
|
||||
tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, []string{TagPrefix, "--sort=-taggerdate"}, skip, limit)
|
||||
return tags, err
|
||||
}
|
||||
|
||||
|
|
|
@ -19,10 +19,10 @@ import (
|
|||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/alecthomas/chroma"
|
||||
"github.com/alecthomas/chroma/formatters/html"
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/styles"
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
|
@ -147,9 +147,6 @@ func File(fileName, language string, code []byte) ([]string, error) {
|
|||
html.PreventSurroundingPre(true),
|
||||
)
|
||||
|
||||
htmlBuf := bytes.Buffer{}
|
||||
htmlWriter := bufio.NewWriter(&htmlBuf)
|
||||
|
||||
var lexer chroma.Lexer
|
||||
|
||||
// provided language overrides everything
|
||||
|
@ -180,23 +177,21 @@ func File(fileName, language string, code []byte) ([]string, error) {
|
|||
return nil, fmt.Errorf("can't tokenize code: %w", err)
|
||||
}
|
||||
|
||||
err = formatter.Format(htmlWriter, styles.GitHub, iterator)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't format code: %w", err)
|
||||
tokensLines := chroma.SplitTokensIntoLines(iterator.Tokens())
|
||||
htmlBuf := &bytes.Buffer{}
|
||||
|
||||
lines := make([]string, 0, len(tokensLines))
|
||||
for _, tokens := range tokensLines {
|
||||
iterator = chroma.Literator(tokens...)
|
||||
err = formatter.Format(htmlBuf, styles.GitHub, iterator)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't format code: %w", err)
|
||||
}
|
||||
lines = append(lines, htmlBuf.String())
|
||||
htmlBuf.Reset()
|
||||
}
|
||||
|
||||
_ = htmlWriter.Flush()
|
||||
|
||||
// at the moment, Chroma generates stable output `<span class="line"><span class="cl">...\n</span></span>` for each line
|
||||
htmlStr := htmlBuf.String()
|
||||
lines := strings.Split(htmlStr, `<span class="line"><span class="cl">`)
|
||||
m := make([]string, 0, len(lines))
|
||||
for i := 1; i < len(lines); i++ {
|
||||
line := lines[i]
|
||||
line = strings.TrimSuffix(line, "</span></span>")
|
||||
m = append(m, line)
|
||||
}
|
||||
return m, nil
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
// PlainText returns non-highlighted HTML for code
|
||||
|
|
84
modules/markup/markdown/convertyaml.go
Normal file
84
modules/markup/markdown/convertyaml.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
// 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 markdown
|
||||
|
||||
import (
|
||||
"github.com/yuin/goldmark/ast"
|
||||
east "github.com/yuin/goldmark/extension/ast"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func nodeToTable(meta *yaml.Node) ast.Node {
|
||||
for {
|
||||
if meta == nil {
|
||||
return nil
|
||||
}
|
||||
switch meta.Kind {
|
||||
case yaml.DocumentNode:
|
||||
meta = meta.Content[0]
|
||||
continue
|
||||
default:
|
||||
}
|
||||
break
|
||||
}
|
||||
switch meta.Kind {
|
||||
case yaml.MappingNode:
|
||||
return mappingNodeToTable(meta)
|
||||
case yaml.SequenceNode:
|
||||
return sequenceNodeToTable(meta)
|
||||
default:
|
||||
return ast.NewString([]byte(meta.Value))
|
||||
}
|
||||
}
|
||||
|
||||
func mappingNodeToTable(meta *yaml.Node) ast.Node {
|
||||
table := east.NewTable()
|
||||
alignments := []east.Alignment{}
|
||||
for i := 0; i < len(meta.Content); i += 2 {
|
||||
alignments = append(alignments, east.AlignNone)
|
||||
}
|
||||
|
||||
headerRow := east.NewTableRow(alignments)
|
||||
valueRow := east.NewTableRow(alignments)
|
||||
for i := 0; i < len(meta.Content); i += 2 {
|
||||
cell := east.NewTableCell()
|
||||
|
||||
cell.AppendChild(cell, nodeToTable(meta.Content[i]))
|
||||
headerRow.AppendChild(headerRow, cell)
|
||||
|
||||
if i+1 < len(meta.Content) {
|
||||
cell = east.NewTableCell()
|
||||
cell.AppendChild(cell, nodeToTable(meta.Content[i+1]))
|
||||
valueRow.AppendChild(valueRow, cell)
|
||||
}
|
||||
}
|
||||
|
||||
table.AppendChild(table, east.NewTableHeader(headerRow))
|
||||
table.AppendChild(table, valueRow)
|
||||
return table
|
||||
}
|
||||
|
||||
func sequenceNodeToTable(meta *yaml.Node) ast.Node {
|
||||
table := east.NewTable()
|
||||
alignments := []east.Alignment{east.AlignNone}
|
||||
for _, item := range meta.Content {
|
||||
row := east.NewTableRow(alignments)
|
||||
cell := east.NewTableCell()
|
||||
cell.AppendChild(cell, nodeToTable(item))
|
||||
row.AppendChild(row, cell)
|
||||
table.AppendChild(table, row)
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
func nodeToDetails(meta *yaml.Node, icon string) ast.Node {
|
||||
details := NewDetails()
|
||||
summary := NewSummary()
|
||||
summary.AppendChild(summary, NewIcon(icon))
|
||||
details.AppendChild(details, summary)
|
||||
details.AppendChild(details, nodeToTable(meta))
|
||||
|
||||
return details
|
||||
}
|
|
@ -15,7 +15,6 @@ import (
|
|||
"code.gitea.io/gitea/modules/setting"
|
||||
giteautil "code.gitea.io/gitea/modules/util"
|
||||
|
||||
meta "github.com/yuin/goldmark-meta"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
east "github.com/yuin/goldmark/extension/ast"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
|
@ -32,20 +31,12 @@ type ASTTransformer struct{}
|
|||
|
||||
// Transform transforms the given AST tree.
|
||||
func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
|
||||
metaData := meta.GetItems(pc)
|
||||
firstChild := node.FirstChild()
|
||||
createTOC := false
|
||||
ctx := pc.Get(renderContextKey).(*markup.RenderContext)
|
||||
rc := &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}
|
||||
|
||||
if metaData != nil {
|
||||
rc.ToRenderConfig(metaData)
|
||||
|
||||
metaNode := rc.toMetaNode(metaData)
|
||||
rc := pc.Get(renderConfigKey).(*RenderConfig)
|
||||
if rc.yamlNode != nil {
|
||||
metaNode := rc.toMetaNode()
|
||||
if metaNode != nil {
|
||||
node.InsertBefore(node, firstChild, metaNode)
|
||||
}
|
||||
|
|
|
@ -14,12 +14,13 @@ import (
|
|||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/common"
|
||||
"code.gitea.io/gitea/modules/markup/markdown/math"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
giteautil "code.gitea.io/gitea/modules/util"
|
||||
|
||||
chromahtml "github.com/alecthomas/chroma/formatters/html"
|
||||
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/yuin/goldmark"
|
||||
highlighting "github.com/yuin/goldmark-highlighting"
|
||||
highlighting "github.com/yuin/goldmark-highlighting/v2"
|
||||
meta "github.com/yuin/goldmark-meta"
|
||||
"github.com/yuin/goldmark/extension"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
|
@ -38,6 +39,7 @@ var (
|
|||
isWikiKey = parser.NewContextKey()
|
||||
renderMetasKey = parser.NewContextKey()
|
||||
renderContextKey = parser.NewContextKey()
|
||||
renderConfigKey = parser.NewContextKey()
|
||||
)
|
||||
|
||||
type limitWriter struct {
|
||||
|
@ -98,7 +100,7 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer)
|
|||
languageStr := string(language)
|
||||
|
||||
preClasses := []string{"code-block"}
|
||||
if languageStr == "mermaid" {
|
||||
if languageStr == "mermaid" || languageStr == "math" {
|
||||
preClasses = append(preClasses, "is-loading")
|
||||
}
|
||||
|
||||
|
@ -120,6 +122,9 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer)
|
|||
}
|
||||
}),
|
||||
),
|
||||
math.NewExtension(
|
||||
math.Enabled(setting.Markdown.EnableMath),
|
||||
),
|
||||
meta.Meta,
|
||||
),
|
||||
goldmark.WithParserOptions(
|
||||
|
@ -167,7 +172,18 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer)
|
|||
log.Error("Unable to ReadAll: %v", err)
|
||||
return err
|
||||
}
|
||||
if err := converter.Convert(giteautil.NormalizeEOL(buf), lw, parser.WithContext(pc)); err != nil {
|
||||
buf = giteautil.NormalizeEOL(buf)
|
||||
|
||||
rc := &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}
|
||||
buf, _ = ExtractMetadataBytes(buf, rc)
|
||||
|
||||
pc.Set(renderConfigKey, rc)
|
||||
|
||||
if err := converter.Convert(buf, lw, parser.WithContext(pc)); err != nil {
|
||||
log.Error("Unable to render: %v", err)
|
||||
return err
|
||||
}
|
||||
|
|
42
modules/markup/markdown/math/block_node.go
Normal file
42
modules/markup/markdown/math/block_node.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
// 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 math
|
||||
|
||||
import "github.com/yuin/goldmark/ast"
|
||||
|
||||
// Block represents a display math block e.g. $$...$$ or \[...\]
|
||||
type Block struct {
|
||||
ast.BaseBlock
|
||||
Dollars bool
|
||||
Indent int
|
||||
Closed bool
|
||||
}
|
||||
|
||||
// KindBlock is the node kind for math blocks
|
||||
var KindBlock = ast.NewNodeKind("MathBlock")
|
||||
|
||||
// NewBlock creates a new math Block
|
||||
func NewBlock(dollars bool, indent int) *Block {
|
||||
return &Block{
|
||||
Dollars: dollars,
|
||||
Indent: indent,
|
||||
}
|
||||
}
|
||||
|
||||
// Dump dumps the block to a string
|
||||
func (n *Block) Dump(source []byte, level int) {
|
||||
m := map[string]string{}
|
||||
ast.DumpHelper(n, source, level, m, nil)
|
||||
}
|
||||
|
||||
// Kind returns KindBlock for math Blocks
|
||||
func (n *Block) Kind() ast.NodeKind {
|
||||
return KindBlock
|
||||
}
|
||||
|
||||
// IsRaw returns true as this block should not be processed further
|
||||
func (n *Block) IsRaw() bool {
|
||||
return true
|
||||
}
|
123
modules/markup/markdown/math/block_parser.go
Normal file
123
modules/markup/markdown/math/block_parser.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
// 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 math
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/text"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
type blockParser struct {
|
||||
parseDollars bool
|
||||
}
|
||||
|
||||
// NewBlockParser creates a new math BlockParser
|
||||
func NewBlockParser(parseDollarBlocks bool) parser.BlockParser {
|
||||
return &blockParser{
|
||||
parseDollars: parseDollarBlocks,
|
||||
}
|
||||
}
|
||||
|
||||
// Open parses the current line and returns a result of parsing.
|
||||
func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Context) (ast.Node, parser.State) {
|
||||
line, segment := reader.PeekLine()
|
||||
pos := pc.BlockOffset()
|
||||
if pos == -1 || len(line[pos:]) < 2 {
|
||||
return nil, parser.NoChildren
|
||||
}
|
||||
|
||||
dollars := false
|
||||
if b.parseDollars && line[pos] == '$' && line[pos+1] == '$' {
|
||||
dollars = true
|
||||
} else if line[pos] != '\\' || line[pos+1] != '[' {
|
||||
return nil, parser.NoChildren
|
||||
}
|
||||
|
||||
node := NewBlock(dollars, pos)
|
||||
|
||||
// Now we need to check if the ending block is on the segment...
|
||||
endBytes := []byte{'\\', ']'}
|
||||
if dollars {
|
||||
endBytes = []byte{'$', '$'}
|
||||
}
|
||||
idx := bytes.Index(line[pos+2:], endBytes)
|
||||
if idx >= 0 {
|
||||
segment.Stop = segment.Start + idx + 2
|
||||
reader.Advance(segment.Len() - 1)
|
||||
segment.Start += 2
|
||||
node.Lines().Append(segment)
|
||||
node.Closed = true
|
||||
return node, parser.Close | parser.NoChildren
|
||||
}
|
||||
|
||||
reader.Advance(segment.Len() - 1)
|
||||
segment.Start += 2
|
||||
node.Lines().Append(segment)
|
||||
return node, parser.NoChildren
|
||||
}
|
||||
|
||||
// Continue parses the current line and returns a result of parsing.
|
||||
func (b *blockParser) Continue(node ast.Node, reader text.Reader, pc parser.Context) parser.State {
|
||||
block := node.(*Block)
|
||||
if block.Closed {
|
||||
return parser.Close
|
||||
}
|
||||
|
||||
line, segment := reader.PeekLine()
|
||||
w, pos := util.IndentWidth(line, 0)
|
||||
if w < 4 {
|
||||
if block.Dollars {
|
||||
i := pos
|
||||
for ; i < len(line) && line[i] == '$'; i++ {
|
||||
}
|
||||
length := i - pos
|
||||
if length >= 2 && util.IsBlank(line[i:]) {
|
||||
reader.Advance(segment.Stop - segment.Start - segment.Padding)
|
||||
block.Closed = true
|
||||
return parser.Close
|
||||
}
|
||||
} else if len(line[pos:]) > 1 && line[pos] == '\\' && line[pos+1] == ']' && util.IsBlank(line[pos+2:]) {
|
||||
reader.Advance(segment.Stop - segment.Start - segment.Padding)
|
||||
block.Closed = true
|
||||
return parser.Close
|
||||
}
|
||||
}
|
||||
|
||||
pos, padding := util.IndentPosition(line, 0, block.Indent)
|
||||
seg := text.NewSegmentPadding(segment.Start+pos, segment.Stop, padding)
|
||||
node.Lines().Append(seg)
|
||||
reader.AdvanceAndSetPadding(segment.Stop-segment.Start-pos-1, padding)
|
||||
return parser.Continue | parser.NoChildren
|
||||
}
|
||||
|
||||
// Close will be called when the parser returns Close.
|
||||
func (b *blockParser) Close(node ast.Node, reader text.Reader, pc parser.Context) {
|
||||
// noop
|
||||
}
|
||||
|
||||
// CanInterruptParagraph returns true if the parser can interrupt paragraphs,
|
||||
// otherwise false.
|
||||
func (b *blockParser) CanInterruptParagraph() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CanAcceptIndentedLine returns true if the parser can open new node when
|
||||
// the given line is being indented more than 3 spaces.
|
||||
func (b *blockParser) CanAcceptIndentedLine() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Trigger returns a list of characters that triggers Parse method of
|
||||
// this parser.
|
||||
// If Trigger returns a nil, Open will be called with any lines.
|
||||
//
|
||||
// We leave this as nil as our parse method is quick enough
|
||||
func (b *blockParser) Trigger() []byte {
|
||||
return nil
|
||||
}
|
43
modules/markup/markdown/math/block_renderer.go
Normal file
43
modules/markup/markdown/math/block_renderer.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
// 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 math
|
||||
|
||||
import (
|
||||
gast "github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
// BlockRenderer represents a renderer for math Blocks
|
||||
type BlockRenderer struct{}
|
||||
|
||||
// NewBlockRenderer creates a new renderer for math Blocks
|
||||
func NewBlockRenderer() renderer.NodeRenderer {
|
||||
return &BlockRenderer{}
|
||||
}
|
||||
|
||||
// RegisterFuncs registers the renderer for math Blocks
|
||||
func (r *BlockRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(KindBlock, r.renderBlock)
|
||||
}
|
||||
|
||||
func (r *BlockRenderer) writeLines(w util.BufWriter, source []byte, n gast.Node) {
|
||||
l := n.Lines().Len()
|
||||
for i := 0; i < l; i++ {
|
||||
line := n.Lines().At(i)
|
||||
_, _ = w.Write(util.EscapeHTML(line.Value(source)))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *BlockRenderer) renderBlock(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
|
||||
n := node.(*Block)
|
||||
if entering {
|
||||
_, _ = w.WriteString(`<pre class="code-block is-loading"><code class="chroma language-math display">`)
|
||||
r.writeLines(w, source, n)
|
||||
} else {
|
||||
_, _ = w.WriteString(`</code></pre>` + "\n")
|
||||
}
|
||||
return gast.WalkContinue, nil
|
||||
}
|
49
modules/markup/markdown/math/inline_node.go
Normal file
49
modules/markup/markdown/math/inline_node.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
// 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 math
|
||||
|
||||
import (
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
// Inline represents inline math e.g. $...$ or \(...\)
|
||||
type Inline struct {
|
||||
ast.BaseInline
|
||||
}
|
||||
|
||||
// Inline implements Inline.Inline.
|
||||
func (n *Inline) Inline() {}
|
||||
|
||||
// IsBlank returns if this inline node is empty
|
||||
func (n *Inline) IsBlank(source []byte) bool {
|
||||
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
|
||||
text := c.(*ast.Text).Segment
|
||||
if !util.IsBlank(text.Value(source)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Dump renders this inline math as debug
|
||||
func (n *Inline) Dump(source []byte, level int) {
|
||||
ast.DumpHelper(n, source, level, nil, nil)
|
||||
}
|
||||
|
||||
// KindInline is the kind for math inline
|
||||
var KindInline = ast.NewNodeKind("MathInline")
|
||||
|
||||
// Kind returns KindInline
|
||||
func (n *Inline) Kind() ast.NodeKind {
|
||||
return KindInline
|
||||
}
|
||||
|
||||
// NewInline creates a new ast math inline node
|
||||
func NewInline() *Inline {
|
||||
return &Inline{
|
||||
BaseInline: ast.BaseInline{},
|
||||
}
|
||||
}
|
99
modules/markup/markdown/math/inline_parser.go
Normal file
99
modules/markup/markdown/math/inline_parser.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
// 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 math
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/text"
|
||||
)
|
||||
|
||||
type inlineParser struct {
|
||||
start []byte
|
||||
end []byte
|
||||
}
|
||||
|
||||
var defaultInlineDollarParser = &inlineParser{
|
||||
start: []byte{'$'},
|
||||
end: []byte{'$'},
|
||||
}
|
||||
|
||||
// NewInlineDollarParser returns a new inline parser
|
||||
func NewInlineDollarParser() parser.InlineParser {
|
||||
return defaultInlineDollarParser
|
||||
}
|
||||
|
||||
var defaultInlineBracketParser = &inlineParser{
|
||||
start: []byte{'\\', '('},
|
||||
end: []byte{'\\', ')'},
|
||||
}
|
||||
|
||||
// NewInlineDollarParser returns a new inline parser
|
||||
func NewInlineBracketParser() parser.InlineParser {
|
||||
return defaultInlineBracketParser
|
||||
}
|
||||
|
||||
// Trigger triggers this parser on $
|
||||
func (parser *inlineParser) Trigger() []byte {
|
||||
return parser.start[0:1]
|
||||
}
|
||||
|
||||
func isAlphanumeric(b byte) bool {
|
||||
// Github only cares about 0-9A-Za-z
|
||||
return (b >= '0' && b <= '9') || (b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z')
|
||||
}
|
||||
|
||||
// Parse parses the current line and returns a result of parsing.
|
||||
func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
|
||||
line, _ := block.PeekLine()
|
||||
opener := bytes.Index(line, parser.start)
|
||||
if opener < 0 {
|
||||
return nil
|
||||
}
|
||||
if opener != 0 && isAlphanumeric(line[opener-1]) {
|
||||
return nil
|
||||
}
|
||||
|
||||
opener += len(parser.start)
|
||||
ender := bytes.Index(line[opener:], parser.end)
|
||||
if ender < 0 {
|
||||
return nil
|
||||
}
|
||||
if len(line) > opener+ender+len(parser.end) && isAlphanumeric(line[opener+ender+len(parser.end)]) {
|
||||
return nil
|
||||
}
|
||||
|
||||
block.Advance(opener)
|
||||
_, pos := block.Position()
|
||||
node := NewInline()
|
||||
segment := pos.WithStop(pos.Start + ender)
|
||||
node.AppendChild(node, ast.NewRawTextSegment(segment))
|
||||
block.Advance(ender + len(parser.end))
|
||||
|
||||
trimBlock(node, block)
|
||||
return node
|
||||
}
|
||||
|
||||
func trimBlock(node *Inline, block text.Reader) {
|
||||
if node.IsBlank(block.Source()) {
|
||||
return
|
||||
}
|
||||
|
||||
// trim first space and last space
|
||||
first := node.FirstChild().(*ast.Text)
|
||||
if !(!first.Segment.IsEmpty() && block.Source()[first.Segment.Start] == ' ') {
|
||||
return
|
||||
}
|
||||
|
||||
last := node.LastChild().(*ast.Text)
|
||||
if !(!last.Segment.IsEmpty() && block.Source()[last.Segment.Stop-1] == ' ') {
|
||||
return
|
||||
}
|
||||
|
||||
first.Segment = first.Segment.WithStart(first.Segment.Start + 1)
|
||||
last.Segment = last.Segment.WithStop(last.Segment.Stop - 1)
|
||||
}
|
47
modules/markup/markdown/math/inline_renderer.go
Normal file
47
modules/markup/markdown/math/inline_renderer.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
// 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 math
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
// InlineRenderer is an inline renderer
|
||||
type InlineRenderer struct{}
|
||||
|
||||
// NewInlineRenderer returns a new renderer for inline math
|
||||
func NewInlineRenderer() renderer.NodeRenderer {
|
||||
return &InlineRenderer{}
|
||||
}
|
||||
|
||||
func (r *InlineRenderer) renderInline(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if entering {
|
||||
_, _ = w.WriteString(`<code class="language-math is-loading">`)
|
||||
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
|
||||
segment := c.(*ast.Text).Segment
|
||||
value := util.EscapeHTML(segment.Value(source))
|
||||
if bytes.HasSuffix(value, []byte("\n")) {
|
||||
_, _ = w.Write(value[:len(value)-1])
|
||||
if c != n.LastChild() {
|
||||
_, _ = w.Write([]byte(" "))
|
||||
}
|
||||
} else {
|
||||
_, _ = w.Write(value)
|
||||
}
|
||||
}
|
||||
return ast.WalkSkipChildren, nil
|
||||
}
|
||||
_, _ = w.WriteString(`</code>`)
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
// RegisterFuncs registers the renderer for inline math nodes
|
||||
func (r *InlineRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(KindInline, r.renderInline)
|
||||
}
|
108
modules/markup/markdown/math/math.go
Normal file
108
modules/markup/markdown/math/math.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
// 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 math
|
||||
|
||||
import (
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
// Extension is a math extension
|
||||
type Extension struct {
|
||||
enabled bool
|
||||
parseDollarInline bool
|
||||
parseDollarBlock bool
|
||||
}
|
||||
|
||||
// Option is the interface Options should implement
|
||||
type Option interface {
|
||||
SetOption(e *Extension)
|
||||
}
|
||||
|
||||
type extensionFunc func(e *Extension)
|
||||
|
||||
func (fn extensionFunc) SetOption(e *Extension) {
|
||||
fn(e)
|
||||
}
|
||||
|
||||
// Enabled enables or disables this extension
|
||||
func Enabled(enable ...bool) Option {
|
||||
value := true
|
||||
if len(enable) > 0 {
|
||||
value = enable[0]
|
||||
}
|
||||
return extensionFunc(func(e *Extension) {
|
||||
e.enabled = value
|
||||
})
|
||||
}
|
||||
|
||||
// WithInlineDollarParser enables or disables the parsing of $...$
|
||||
func WithInlineDollarParser(enable ...bool) Option {
|
||||
value := true
|
||||
if len(enable) > 0 {
|
||||
value = enable[0]
|
||||
}
|
||||
return extensionFunc(func(e *Extension) {
|
||||
e.parseDollarInline = value
|
||||
})
|
||||
}
|
||||
|
||||
// WithBlockDollarParser enables or disables the parsing of $$...$$
|
||||
func WithBlockDollarParser(enable ...bool) Option {
|
||||
value := true
|
||||
if len(enable) > 0 {
|
||||
value = enable[0]
|
||||
}
|
||||
return extensionFunc(func(e *Extension) {
|
||||
e.parseDollarBlock = value
|
||||
})
|
||||
}
|
||||
|
||||
// Math represents a math extension with default rendered delimiters
|
||||
var Math = &Extension{
|
||||
enabled: true,
|
||||
parseDollarBlock: true,
|
||||
parseDollarInline: true,
|
||||
}
|
||||
|
||||
// NewExtension creates a new math extension with the provided options
|
||||
func NewExtension(opts ...Option) *Extension {
|
||||
r := &Extension{
|
||||
enabled: true,
|
||||
parseDollarBlock: true,
|
||||
parseDollarInline: true,
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o.SetOption(r)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Extend extends goldmark with our parsers and renderers
|
||||
func (e *Extension) Extend(m goldmark.Markdown) {
|
||||
if !e.enabled {
|
||||
return
|
||||
}
|
||||
|
||||
m.Parser().AddOptions(parser.WithBlockParsers(
|
||||
util.Prioritized(NewBlockParser(e.parseDollarBlock), 701),
|
||||
))
|
||||
|
||||
inlines := []util.PrioritizedValue{
|
||||
util.Prioritized(NewInlineBracketParser(), 501),
|
||||
}
|
||||
if e.parseDollarInline {
|
||||
inlines = append(inlines, util.Prioritized(NewInlineDollarParser(), 501))
|
||||
}
|
||||
m.Parser().AddOptions(parser.WithInlineParsers(inlines...))
|
||||
|
||||
m.Renderer().AddOptions(renderer.WithNodeRenderers(
|
||||
util.Prioritized(NewBlockRenderer(), 501),
|
||||
util.Prioritized(NewInlineRenderer(), 502),
|
||||
))
|
||||
}
|
|
@ -5,47 +5,102 @@
|
|||
package markdown
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func isYAMLSeparator(line string) bool {
|
||||
line = strings.TrimSpace(line)
|
||||
for i := 0; i < len(line); i++ {
|
||||
if line[i] != '-' {
|
||||
func isYAMLSeparator(line []byte) bool {
|
||||
idx := 0
|
||||
for ; idx < len(line); idx++ {
|
||||
if line[idx] >= utf8.RuneSelf {
|
||||
r, sz := utf8.DecodeRune(line[idx:])
|
||||
if !unicode.IsSpace(r) {
|
||||
return false
|
||||
}
|
||||
idx += sz
|
||||
continue
|
||||
}
|
||||
if line[idx] != ' ' {
|
||||
break
|
||||
}
|
||||
}
|
||||
dashCount := 0
|
||||
for ; idx < len(line); idx++ {
|
||||
if line[idx] != '-' {
|
||||
break
|
||||
}
|
||||
dashCount++
|
||||
}
|
||||
if dashCount < 3 {
|
||||
return false
|
||||
}
|
||||
for ; idx < len(line); idx++ {
|
||||
if line[idx] >= utf8.RuneSelf {
|
||||
r, sz := utf8.DecodeRune(line[idx:])
|
||||
if !unicode.IsSpace(r) {
|
||||
return false
|
||||
}
|
||||
idx += sz
|
||||
continue
|
||||
}
|
||||
if line[idx] != ' ' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return len(line) > 2
|
||||
return true
|
||||
}
|
||||
|
||||
// ExtractMetadata consumes a markdown file, parses YAML frontmatter,
|
||||
// and returns the frontmatter metadata separated from the markdown content
|
||||
func ExtractMetadata(contents string, out interface{}) (string, error) {
|
||||
var front, body []string
|
||||
lines := strings.Split(contents, "\n")
|
||||
for idx, line := range lines {
|
||||
if idx == 0 {
|
||||
// First line has to be a separator
|
||||
if !isYAMLSeparator(line) {
|
||||
return "", errors.New("frontmatter must start with a separator line")
|
||||
}
|
||||
continue
|
||||
body, err := ExtractMetadataBytes([]byte(contents), out)
|
||||
return string(body), err
|
||||
}
|
||||
|
||||
// ExtractMetadata consumes a markdown file, parses YAML frontmatter,
|
||||
// and returns the frontmatter metadata separated from the markdown content
|
||||
func ExtractMetadataBytes(contents []byte, out interface{}) ([]byte, error) {
|
||||
var front, body []byte
|
||||
|
||||
start, end := 0, len(contents)
|
||||
idx := bytes.IndexByte(contents[start:], '\n')
|
||||
if idx >= 0 {
|
||||
end = start + idx
|
||||
}
|
||||
line := contents[start:end]
|
||||
|
||||
if !isYAMLSeparator(line) {
|
||||
return contents, errors.New("frontmatter must start with a separator line")
|
||||
}
|
||||
frontMatterStart := end + 1
|
||||
for start = frontMatterStart; start < len(contents); start = end + 1 {
|
||||
end = len(contents)
|
||||
idx := bytes.IndexByte(contents[start:], '\n')
|
||||
if idx >= 0 {
|
||||
end = start + idx
|
||||
}
|
||||
line := contents[start:end]
|
||||
if isYAMLSeparator(line) {
|
||||
front, body = lines[1:idx], lines[idx+1:]
|
||||
front = contents[frontMatterStart:start]
|
||||
body = contents[end+1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(front) == 0 {
|
||||
return "", errors.New("could not determine metadata")
|
||||
return contents, errors.New("could not determine metadata")
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal([]byte(strings.Join(front, "\n")), out); err != nil {
|
||||
return "", err
|
||||
log.Info("%s", string(front))
|
||||
|
||||
if err := yaml.Unmarshal(front, out); err != nil {
|
||||
return contents, err
|
||||
}
|
||||
return strings.Join(body, "\n"), nil
|
||||
return body, nil
|
||||
}
|
||||
|
|
|
@ -56,6 +56,38 @@ func TestExtractMetadata(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestExtractMetadataBytes(t *testing.T) {
|
||||
t.Run("ValidFrontAndBody", func(t *testing.T) {
|
||||
var meta structs.IssueTemplate
|
||||
body, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s\n%s", sepTest, frontTest, sepTest, bodyTest)), &meta)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, bodyTest, body)
|
||||
assert.Equal(t, metaTest, meta)
|
||||
assert.True(t, validateMetadata(meta))
|
||||
})
|
||||
|
||||
t.Run("NoFirstSeparator", func(t *testing.T) {
|
||||
var meta structs.IssueTemplate
|
||||
_, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", frontTest, sepTest, bodyTest)), &meta)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("NoLastSeparator", func(t *testing.T) {
|
||||
var meta structs.IssueTemplate
|
||||
_, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, bodyTest)), &meta)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("NoBody", func(t *testing.T) {
|
||||
var meta structs.IssueTemplate
|
||||
body, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, sepTest)), &meta)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "", body)
|
||||
assert.Equal(t, metaTest, meta)
|
||||
assert.True(t, validateMetadata(meta))
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
sepTest = "-----"
|
||||
frontTest = `name: Test
|
||||
|
|
|
@ -5,159 +5,115 @@
|
|||
package markdown
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/yuin/goldmark/ast"
|
||||
east "github.com/yuin/goldmark/extension/ast"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// RenderConfig represents rendering configuration for this file
|
||||
type RenderConfig struct {
|
||||
Meta string
|
||||
Icon string
|
||||
TOC bool
|
||||
Lang string
|
||||
Meta string
|
||||
Icon string
|
||||
TOC bool
|
||||
Lang string
|
||||
yamlNode *yaml.Node
|
||||
}
|
||||
|
||||
// ToRenderConfig converts a yaml.MapSlice to a RenderConfig
|
||||
func (rc *RenderConfig) ToRenderConfig(meta yaml.MapSlice) {
|
||||
if meta == nil {
|
||||
return
|
||||
// UnmarshalYAML implement yaml.v3 UnmarshalYAML
|
||||
func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
|
||||
if rc == nil {
|
||||
rc = &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}
|
||||
}
|
||||
found := false
|
||||
var giteaMetaControl yaml.MapItem
|
||||
for _, item := range meta {
|
||||
strKey, ok := item.Key.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
strKey = strings.TrimSpace(strings.ToLower(strKey))
|
||||
switch strKey {
|
||||
case "gitea":
|
||||
giteaMetaControl = item
|
||||
found = true
|
||||
case "include_toc":
|
||||
val, ok := item.Value.(bool)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
rc.TOC = val
|
||||
case "lang":
|
||||
val, ok := item.Value.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
val = strings.TrimSpace(val)
|
||||
if len(val) == 0 {
|
||||
continue
|
||||
}
|
||||
rc.Lang = val
|
||||
}
|
||||
rc.yamlNode = value
|
||||
|
||||
type basicRenderConfig struct {
|
||||
Gitea *yaml.Node `yaml:"gitea"`
|
||||
TOC bool `yaml:"include_toc"`
|
||||
Lang string `yaml:"lang"`
|
||||
}
|
||||
|
||||
if found {
|
||||
switch v := giteaMetaControl.Value.(type) {
|
||||
case string:
|
||||
switch v {
|
||||
case "none":
|
||||
rc.Meta = "none"
|
||||
case "table":
|
||||
rc.Meta = "table"
|
||||
default: // "details"
|
||||
rc.Meta = "details"
|
||||
}
|
||||
case yaml.MapSlice:
|
||||
for _, item := range v {
|
||||
strKey, ok := item.Key.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
strKey = strings.TrimSpace(strings.ToLower(strKey))
|
||||
switch strKey {
|
||||
case "meta":
|
||||
val, ok := item.Value.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch strings.TrimSpace(strings.ToLower(val)) {
|
||||
case "none":
|
||||
rc.Meta = "none"
|
||||
case "table":
|
||||
rc.Meta = "table"
|
||||
default: // "details"
|
||||
rc.Meta = "details"
|
||||
}
|
||||
case "details_icon":
|
||||
val, ok := item.Value.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
rc.Icon = strings.TrimSpace(strings.ToLower(val))
|
||||
case "include_toc":
|
||||
val, ok := item.Value.(bool)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
rc.TOC = val
|
||||
case "lang":
|
||||
val, ok := item.Value.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
val = strings.TrimSpace(val)
|
||||
if len(val) == 0 {
|
||||
continue
|
||||
}
|
||||
rc.Lang = val
|
||||
}
|
||||
}
|
||||
}
|
||||
var basic basicRenderConfig
|
||||
|
||||
err := value.Decode(&basic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if basic.Lang != "" {
|
||||
rc.Lang = basic.Lang
|
||||
}
|
||||
|
||||
rc.TOC = basic.TOC
|
||||
if basic.Gitea == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var control *string
|
||||
if err := basic.Gitea.Decode(&control); err == nil && control != nil {
|
||||
log.Info("control %v", control)
|
||||
switch strings.TrimSpace(strings.ToLower(*control)) {
|
||||
case "none":
|
||||
rc.Meta = "none"
|
||||
case "table":
|
||||
rc.Meta = "table"
|
||||
default: // "details"
|
||||
rc.Meta = "details"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type giteaControl struct {
|
||||
Meta string `yaml:"meta"`
|
||||
Icon string `yaml:"details_icon"`
|
||||
TOC *yaml.Node `yaml:"include_toc"`
|
||||
Lang string `yaml:"lang"`
|
||||
}
|
||||
|
||||
var controlStruct *giteaControl
|
||||
if err := basic.Gitea.Decode(controlStruct); err != nil || controlStruct == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch strings.TrimSpace(strings.ToLower(controlStruct.Meta)) {
|
||||
case "none":
|
||||
rc.Meta = "none"
|
||||
case "table":
|
||||
rc.Meta = "table"
|
||||
default: // "details"
|
||||
rc.Meta = "details"
|
||||
}
|
||||
|
||||
rc.Icon = strings.TrimSpace(strings.ToLower(controlStruct.Icon))
|
||||
|
||||
if controlStruct.Lang != "" {
|
||||
rc.Lang = controlStruct.Lang
|
||||
}
|
||||
|
||||
var toc bool
|
||||
if err := controlStruct.TOC.Decode(&toc); err == nil {
|
||||
rc.TOC = toc
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rc *RenderConfig) toMetaNode(meta yaml.MapSlice) ast.Node {
|
||||
func (rc *RenderConfig) toMetaNode() ast.Node {
|
||||
if rc.yamlNode == nil {
|
||||
return nil
|
||||
}
|
||||
switch rc.Meta {
|
||||
case "table":
|
||||
return metaToTable(meta)
|
||||
return nodeToTable(rc.yamlNode)
|
||||
case "details":
|
||||
return metaToDetails(meta, rc.Icon)
|
||||
return nodeToDetails(rc.yamlNode, rc.Icon)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func metaToTable(meta yaml.MapSlice) ast.Node {
|
||||
table := east.NewTable()
|
||||
alignments := []east.Alignment{}
|
||||
for range meta {
|
||||
alignments = append(alignments, east.AlignNone)
|
||||
}
|
||||
row := east.NewTableRow(alignments)
|
||||
for _, item := range meta {
|
||||
cell := east.NewTableCell()
|
||||
cell.AppendChild(cell, ast.NewString([]byte(fmt.Sprintf("%v", item.Key))))
|
||||
row.AppendChild(row, cell)
|
||||
}
|
||||
table.AppendChild(table, east.NewTableHeader(row))
|
||||
|
||||
row = east.NewTableRow(alignments)
|
||||
for _, item := range meta {
|
||||
cell := east.NewTableCell()
|
||||
cell.AppendChild(cell, ast.NewString([]byte(fmt.Sprintf("%v", item.Value))))
|
||||
row.AppendChild(row, cell)
|
||||
}
|
||||
table.AppendChild(table, row)
|
||||
return table
|
||||
}
|
||||
|
||||
func metaToDetails(meta yaml.MapSlice, icon string) ast.Node {
|
||||
details := NewDetails()
|
||||
summary := NewSummary()
|
||||
summary.AppendChild(summary, NewIcon(icon))
|
||||
details.AppendChild(details, summary)
|
||||
details.AppendChild(details, metaToTable(meta))
|
||||
|
||||
return details
|
||||
}
|
||||
|
|
162
modules/markup/markdown/renderconfig_test.go
Normal file
162
modules/markup/markdown/renderconfig_test.go
Normal file
|
@ -0,0 +1,162 @@
|
|||
// 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 markdown
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expected *RenderConfig
|
||||
args string
|
||||
}{
|
||||
{
|
||||
"empty", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "",
|
||||
},
|
||||
{
|
||||
"lang", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "test",
|
||||
}, "lang: test",
|
||||
},
|
||||
{
|
||||
"metatable", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "gitea: table",
|
||||
},
|
||||
{
|
||||
"metanone", &RenderConfig{
|
||||
Meta: "none",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "gitea: none",
|
||||
},
|
||||
{
|
||||
"metadetails", &RenderConfig{
|
||||
Meta: "details",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "gitea: details",
|
||||
},
|
||||
{
|
||||
"metawrong", &RenderConfig{
|
||||
Meta: "details",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "gitea: wrong",
|
||||
},
|
||||
{
|
||||
"toc", &RenderConfig{
|
||||
TOC: true,
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "include_toc: true",
|
||||
},
|
||||
{
|
||||
"tocfalse", &RenderConfig{
|
||||
TOC: false,
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}, "include_toc: false",
|
||||
},
|
||||
{
|
||||
"toclang", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
TOC: true,
|
||||
Lang: "testlang",
|
||||
}, `
|
||||
include_toc: true
|
||||
lang: testlang
|
||||
`,
|
||||
},
|
||||
{
|
||||
"complexlang", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "testlang",
|
||||
}, `
|
||||
gitea:
|
||||
lang: testlang
|
||||
`,
|
||||
},
|
||||
{
|
||||
"complexlang2", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "testlang",
|
||||
}, `
|
||||
lang: notright
|
||||
gitea:
|
||||
lang: testlang
|
||||
`,
|
||||
},
|
||||
{
|
||||
"complexlang", &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "testlang",
|
||||
}, `
|
||||
gitea:
|
||||
lang: testlang
|
||||
`,
|
||||
},
|
||||
{
|
||||
"complex2", &RenderConfig{
|
||||
Lang: "two",
|
||||
Meta: "table",
|
||||
TOC: true,
|
||||
Icon: "smiley",
|
||||
}, `
|
||||
lang: one
|
||||
include_toc: true
|
||||
gitea:
|
||||
details_icon: smiley
|
||||
meta: table
|
||||
include_toc: true
|
||||
lang: two
|
||||
`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := &RenderConfig{
|
||||
Meta: "table",
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}
|
||||
if err := yaml.Unmarshal([]byte(tt.args), got); err != nil {
|
||||
t.Errorf("RenderConfig.UnmarshalYAML() error = %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if got.Meta != tt.expected.Meta {
|
||||
t.Errorf("Meta Expected %s Got %s", tt.expected.Meta, got.Meta)
|
||||
}
|
||||
if got.Icon != tt.expected.Icon {
|
||||
t.Errorf("Icon Expected %s Got %s", tt.expected.Icon, got.Icon)
|
||||
}
|
||||
if got.Lang != tt.expected.Lang {
|
||||
t.Errorf("Lang Expected %s Got %s", tt.expected.Lang, got.Lang)
|
||||
}
|
||||
if got.TOC != tt.expected.TOC {
|
||||
t.Errorf("TOC Expected %t Got %t", tt.expected.TOC, got.TOC)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -17,8 +17,8 @@ import (
|
|||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/alecthomas/chroma"
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/niklasfasching/go-org/org"
|
||||
)
|
||||
|
||||
|
|
|
@ -79,9 +79,9 @@ func HelloWorld() {
|
|||
}
|
||||
#+end_src
|
||||
`, `<div class="src src-go">
|
||||
<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>
|
||||
<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>
|
||||
</div>`)
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ func createDefaultPolicy() *bluemonday.Policy {
|
|||
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-block( is-loading)?$`)).OnElements("pre")
|
||||
|
||||
// For Chroma markdown plugin
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+$`)).OnElements("code")
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+( display)?( is-loading)?$`)).OnElements("code")
|
||||
|
||||
// Checkboxes
|
||||
policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
|
||||
|
@ -83,7 +83,7 @@ func createDefaultPolicy() *bluemonday.Policy {
|
|||
policy.AllowAttrs("class").Matching(regexp.MustCompile(`emoji`)).OnElements("img")
|
||||
|
||||
// Allow icons, emojis, chroma syntax and keyword markup on span
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span")
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span")
|
||||
|
||||
// Allow 'style' attribute on text elements.
|
||||
policy.AllowAttrs("style").OnElements("span", "p")
|
||||
|
|
|
@ -95,7 +95,9 @@ func parseOCIImageConfig(r io.Reader) (*Metadata, error) {
|
|||
if i := strings.Index(cmd, "#(nop) "); i != -1 {
|
||||
cmd = strings.TrimSpace(cmd[i+7:])
|
||||
}
|
||||
imageLayers = append(imageLayers, cmd)
|
||||
if cmd != "" {
|
||||
imageLayers = append(imageLayers, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
metadata := &Metadata{
|
||||
|
|
|
@ -96,6 +96,34 @@ type PackageDistribution struct {
|
|||
NpmSignature string `json:"npm-signature,omitempty"`
|
||||
}
|
||||
|
||||
type PackageSearch struct {
|
||||
Objects []*PackageSearchObject `json:"objects"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
type PackageSearchObject struct {
|
||||
Package *PackageSearchPackage `json:"package"`
|
||||
}
|
||||
|
||||
type PackageSearchPackage struct {
|
||||
Scope string `json:"scope"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Date time.Time `json:"date"`
|
||||
Description string `json:"description"`
|
||||
Author User `json:"author"`
|
||||
Publisher User `json:"publisher"`
|
||||
Maintainers []User `json:"maintainers"`
|
||||
Keywords []string `json:"keywords,omitempty"`
|
||||
Links *PackageSearchPackageLinks `json:"links"`
|
||||
}
|
||||
|
||||
type PackageSearchPackageLinks struct {
|
||||
Registry string `json:"npm"`
|
||||
Homepage string `json:"homepage,omitempty"`
|
||||
Repository string `json:"repository,omitempty"`
|
||||
}
|
||||
|
||||
// User https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#package
|
||||
type User struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
|
@ -337,13 +336,6 @@ func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error
|
|||
func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) {
|
||||
repo.LowerName = strings.ToLower(repo.Name)
|
||||
|
||||
if utf8.RuneCountInString(repo.Description) > 255 {
|
||||
repo.Description = string([]rune(repo.Description)[:255])
|
||||
}
|
||||
if utf8.RuneCountInString(repo.Website) > 255 {
|
||||
repo.Website = string([]rune(repo.Website)[:255])
|
||||
}
|
||||
|
||||
e := db.GetEngine(ctx)
|
||||
|
||||
if _, err = e.ID(repo.ID).AllCols().Update(repo); err != nil {
|
||||
|
|
|
@ -344,10 +344,12 @@ var (
|
|||
EnableHardLineBreakInDocuments bool
|
||||
CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
|
||||
FileExtensions []string
|
||||
EnableMath bool
|
||||
}{
|
||||
EnableHardLineBreakInComments: true,
|
||||
EnableHardLineBreakInDocuments: false,
|
||||
FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","),
|
||||
EnableMath: true,
|
||||
}
|
||||
|
||||
// Admin settings
|
||||
|
|
|
@ -102,6 +102,10 @@ func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error)
|
|||
if err := util.Rename(tmp.Name(), p); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Golang's tmp file (os.CreateTemp) always have 0o600 mode, so we need to change the file to follow the umask (as what Create/MkDir does)
|
||||
if err := util.ApplyUmask(p, os.ModePerm); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
tmpRemoved = true
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ package structs
|
|||
// Organization represents an organization
|
||||
type Organization struct {
|
||||
ID int64 `json:"id"`
|
||||
UserName string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
FullName string `json:"full_name"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
Description string `json:"description"`
|
||||
|
@ -15,6 +15,8 @@ type Organization struct {
|
|||
Location string `json:"location"`
|
||||
Visibility string `json:"visibility"`
|
||||
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
|
||||
// deprecated
|
||||
UserName string `json:"username"`
|
||||
}
|
||||
|
||||
// OrganizationPermissions list different users permissions on an organization
|
||||
|
|
|
@ -95,3 +95,16 @@ type EditPullRequestOption struct {
|
|||
RemoveDeadline *bool `json:"unset_due_date"`
|
||||
AllowMaintainerEdit *bool `json:"allow_maintainer_edit"`
|
||||
}
|
||||
|
||||
// ChangedFile store information about files affected by the pull request
|
||||
type ChangedFile struct {
|
||||
Filename string `json:"filename"`
|
||||
PreviousFilename string `json:"previous_filename,omitempty"`
|
||||
Status string `json:"status"`
|
||||
Additions int `json:"additions"`
|
||||
Deletions int `json:"deletions"`
|
||||
Changes int `json:"changes"`
|
||||
HTMLURL string `json:"html_url,omitempty"`
|
||||
ContentsURL string `json:"contents_url,omitempty"`
|
||||
RawURL string `json:"raw_url,omitempty"`
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ type CreateRepoOption struct {
|
|||
// unique: true
|
||||
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"`
|
||||
// Description of the repository to create
|
||||
Description string `json:"description" binding:"MaxSize(255)"`
|
||||
Description string `json:"description" binding:"MaxSize(2048)"`
|
||||
// Whether the repository is private
|
||||
Private bool `json:"private"`
|
||||
// Label-Set to use
|
||||
|
@ -140,9 +140,9 @@ type EditRepoOption struct {
|
|||
// unique: true
|
||||
Name *string `json:"name,omitempty" binding:"OmitEmpty;AlphaDashDot;MaxSize(100);"`
|
||||
// a short description of the repository.
|
||||
Description *string `json:"description,omitempty" binding:"MaxSize(255)"`
|
||||
Description *string `json:"description,omitempty" binding:"MaxSize(2048)"`
|
||||
// a URL with more information about the repository.
|
||||
Website *string `json:"website,omitempty" binding:"MaxSize(255)"`
|
||||
Website *string `json:"website,omitempty" binding:"MaxSize(1024)"`
|
||||
// either `true` to make the repository private or `false` to make it public.
|
||||
// Note: you will get a 422 error if the organization restricts changing repository visibility to organization
|
||||
// owners and a non-owner tries to change the value of private.
|
||||
|
@ -151,13 +151,13 @@ type EditRepoOption struct {
|
|||
Template *bool `json:"template,omitempty"`
|
||||
// either `true` to enable issues for this repository or `false` to disable them.
|
||||
HasIssues *bool `json:"has_issues,omitempty"`
|
||||
// set this structure to configure internal issue tracker (requires has_issues)
|
||||
// set this structure to configure internal issue tracker
|
||||
InternalTracker *InternalTracker `json:"internal_tracker,omitempty"`
|
||||
// set this structure to use external issue tracker (requires has_issues)
|
||||
// set this structure to use external issue tracker
|
||||
ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"`
|
||||
// either `true` to enable the wiki for this repository or `false` to disable it.
|
||||
HasWiki *bool `json:"has_wiki,omitempty"`
|
||||
// set this structure to use external wiki instead of internal (requires has_wiki)
|
||||
// set this structure to use external wiki instead of internal
|
||||
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
|
||||
// sets the default branch for this repository.
|
||||
DefaultBranch *string `json:"default_branch,omitempty"`
|
||||
|
@ -165,25 +165,25 @@ type EditRepoOption struct {
|
|||
HasPullRequests *bool `json:"has_pull_requests,omitempty"`
|
||||
// either `true` to enable project unit, or `false` to disable them.
|
||||
HasProjects *bool `json:"has_projects,omitempty"`
|
||||
// either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `has_pull_requests` must be `true`.
|
||||
// either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace.
|
||||
IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace_conflicts,omitempty"`
|
||||
// either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `has_pull_requests` must be `true`.
|
||||
// either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits.
|
||||
AllowMerge *bool `json:"allow_merge_commits,omitempty"`
|
||||
// either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `has_pull_requests` must be `true`.
|
||||
// either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging.
|
||||
AllowRebase *bool `json:"allow_rebase,omitempty"`
|
||||
// either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. `has_pull_requests` must be `true`.
|
||||
// either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits.
|
||||
AllowRebaseMerge *bool `json:"allow_rebase_explicit,omitempty"`
|
||||
// either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `has_pull_requests` must be `true`.
|
||||
// either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging.
|
||||
AllowSquash *bool `json:"allow_squash_merge,omitempty"`
|
||||
// either `true` to allow mark pr as merged manually, or `false` to prevent it. `has_pull_requests` must be `true`.
|
||||
// either `true` to allow mark pr as merged manually, or `false` to prevent it.
|
||||
AllowManualMerge *bool `json:"allow_manual_merge,omitempty"`
|
||||
// either `true` to enable AutodetectManualMerge, or `false` to prevent it. `has_pull_requests` must be `true`, Note: In some special cases, misjudgments can occur.
|
||||
// either `true` to enable AutodetectManualMerge, or `false` to prevent it. Note: In some special cases, misjudgments can occur.
|
||||
AutodetectManualMerge *bool `json:"autodetect_manual_merge,omitempty"`
|
||||
// either `true` to allow updating pull request branch by rebase, or `false` to prevent it. `has_pull_requests` must be `true`.
|
||||
// either `true` to allow updating pull request branch by rebase, or `false` to prevent it.
|
||||
AllowRebaseUpdate *bool `json:"allow_rebase_update,omitempty"`
|
||||
// set to `true` to delete pr branch after merge by default
|
||||
DefaultDeleteBranchAfterMerge *bool `json:"default_delete_branch_after_merge,omitempty"`
|
||||
// set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash". `has_pull_requests` must be `true`.
|
||||
// set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash".
|
||||
DefaultMergeStyle *string `json:"default_merge_style,omitempty"`
|
||||
// set to `true` to archive this repository.
|
||||
Archived *bool `json:"archived,omitempty"`
|
||||
|
@ -208,7 +208,7 @@ type GenerateRepoOption struct {
|
|||
// Default branch of the new repository
|
||||
DefaultBranch string `json:"default_branch"`
|
||||
// Description of the repository to create
|
||||
Description string `json:"description" binding:"MaxSize(255)"`
|
||||
Description string `json:"description" binding:"MaxSize(2048)"`
|
||||
// Whether the repository is private
|
||||
Private bool `json:"private"`
|
||||
// include git content of default branch in template repo
|
||||
|
@ -316,7 +316,7 @@ type MigrateRepoOptions struct {
|
|||
LFS bool `json:"lfs"`
|
||||
LFSEndpoint string `json:"lfs_endpoint"`
|
||||
Private bool `json:"private"`
|
||||
Description string `json:"description" binding:"MaxSize(255)"`
|
||||
Description string `json:"description" binding:"MaxSize(2048)"`
|
||||
Wiki bool `json:"wiki"`
|
||||
Milestones bool `json:"milestones"`
|
||||
Labels bool `json:"labels"`
|
||||
|
|
|
@ -376,18 +376,18 @@ func NewFuncMap() []template.FuncMap {
|
|||
// the table is NOT sorted with this header
|
||||
return ""
|
||||
},
|
||||
"RenderLabels": func(labels []*issues_model.Label) template.HTML {
|
||||
html := `<span class="labels-list">`
|
||||
"RenderLabels": func(labels []*issues_model.Label, repoLink string) template.HTML {
|
||||
htmlCode := `<span class="labels-list">`
|
||||
for _, label := range labels {
|
||||
// Protect against nil value in labels - shouldn't happen but would cause a panic if so
|
||||
if label == nil {
|
||||
continue
|
||||
}
|
||||
html += fmt.Sprintf("<div class='ui label' style='color: %s; background-color: %s'>%s</div> ",
|
||||
label.ForegroundColor(), label.Color, RenderEmoji(label.Name))
|
||||
htmlCode += fmt.Sprintf("<a href='%s/issues?labels=%d' class='ui label' style='color: %s !important; background-color: %s !important' title='%s'>%s</a> ",
|
||||
repoLink, label.ID, label.ForegroundColor(), label.Color, html.EscapeString(label.Description), RenderEmoji(label.Name))
|
||||
}
|
||||
html += "</span>"
|
||||
return template.HTML(html)
|
||||
htmlCode += "</span>"
|
||||
return template.HTML(htmlCode)
|
||||
},
|
||||
"MermaidMaxSourceCharacters": func() int {
|
||||
return setting.MermaidMaxSourceCharacters
|
||||
|
@ -631,7 +631,7 @@ func SVG(icon string, others ...interface{}) template.HTML {
|
|||
|
||||
// Avatar renders user avatars. args: user, size (int), class (string)
|
||||
func Avatar(item interface{}, others ...interface{}) template.HTML {
|
||||
size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image vm", others...)
|
||||
size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar vm", others...)
|
||||
|
||||
switch t := item.(type) {
|
||||
case *user_model.User:
|
||||
|
@ -662,7 +662,7 @@ func AvatarByAction(action *activities_model.Action, others ...interface{}) temp
|
|||
|
||||
// RepoAvatar renders repo avatars. args: repo, size(int), class (string)
|
||||
func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTML {
|
||||
size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image", others...)
|
||||
size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar", others...)
|
||||
|
||||
src := repo.RelAvatarLink()
|
||||
if src != "" {
|
||||
|
@ -673,7 +673,7 @@ func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTM
|
|||
|
||||
// AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string)
|
||||
func AvatarByEmail(email, name string, others ...interface{}) template.HTML {
|
||||
size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image", others...)
|
||||
size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar", others...)
|
||||
src := avatars.GenerateEmailAvatarFastLink(email, size*setting.Avatar.RenderedSizeFactor)
|
||||
|
||||
if src != "" {
|
||||
|
|
|
@ -34,7 +34,7 @@ type LocaleStore interface {
|
|||
// HasLang returns whether a given language is present in the store
|
||||
HasLang(langName string) bool
|
||||
// AddLocaleByIni adds a new language to the store
|
||||
AddLocaleByIni(langName, langDesc string, source interface{}) error
|
||||
AddLocaleByIni(langName, langDesc string, source, moreSource []byte) error
|
||||
}
|
||||
|
||||
// ResetDefaultLocales resets the current default locales
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_Tr(t *testing.T) {
|
||||
func TestLocaleStore(t *testing.T) {
|
||||
testData1 := []byte(`
|
||||
.dot.name = Dot Name
|
||||
fmt = %[1]s %[2]s
|
||||
|
@ -28,8 +28,8 @@ sub = Changed Sub String
|
|||
`)
|
||||
|
||||
ls := NewLocaleStore()
|
||||
assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1))
|
||||
assert.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", testData2))
|
||||
assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1, nil))
|
||||
assert.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", testData2, nil))
|
||||
ls.SetDefaultLang("lang1")
|
||||
|
||||
result := ls.Tr("lang1", "fmt", "a", "b")
|
||||
|
@ -58,3 +58,21 @@ sub = Changed Sub String
|
|||
assert.False(t, found)
|
||||
assert.NoError(t, ls.Close())
|
||||
}
|
||||
|
||||
func TestLocaleStoreMoreSource(t *testing.T) {
|
||||
testData1 := []byte(`
|
||||
a=11
|
||||
b=12
|
||||
`)
|
||||
|
||||
testData2 := []byte(`
|
||||
b=21
|
||||
c=22
|
||||
`)
|
||||
|
||||
ls := NewLocaleStore()
|
||||
assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1, testData2))
|
||||
assert.Equal(t, "11", ls.Tr("lang1", "a"))
|
||||
assert.Equal(t, "21", ls.Tr("lang1", "b"))
|
||||
assert.Equal(t, "22", ls.Tr("lang1", "c"))
|
||||
}
|
||||
|
|
|
@ -37,9 +37,7 @@ func NewLocaleStore() LocaleStore {
|
|||
}
|
||||
|
||||
// AddLocaleByIni adds locale by ini into the store
|
||||
// if source is a string, then the file is loaded
|
||||
// if source is a []byte, then the content is used
|
||||
func (store *localeStore) AddLocaleByIni(langName, langDesc string, source interface{}) error {
|
||||
func (store *localeStore) AddLocaleByIni(langName, langDesc string, source, moreSource []byte) error {
|
||||
if _, ok := store.localeMap[langName]; ok {
|
||||
return ErrLocaleAlreadyExist
|
||||
}
|
||||
|
@ -53,7 +51,7 @@ func (store *localeStore) AddLocaleByIni(langName, langDesc string, source inter
|
|||
iniFile, err := ini.LoadSources(ini.LoadOptions{
|
||||
IgnoreInlineComment: true,
|
||||
UnescapeValueCommentSymbols: true,
|
||||
}, source)
|
||||
}, source, moreSource)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load ini: %w", err)
|
||||
}
|
||||
|
|
|
@ -60,9 +60,9 @@ func InitLocales(ctx context.Context) {
|
|||
log.Fatal("Failed to list locale files: %v", err)
|
||||
}
|
||||
|
||||
localFiles := make(map[string]interface{}, len(localeNames))
|
||||
localeData := make(map[string][]byte, len(localeNames))
|
||||
for _, name := range localeNames {
|
||||
localFiles[name], err = options.Locale(name)
|
||||
localeData[name], err = options.Locale(name)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to load %s locale file. %v", name, err)
|
||||
}
|
||||
|
@ -75,9 +75,17 @@ func InitLocales(ctx context.Context) {
|
|||
|
||||
matcher = language.NewMatcher(supportedTags)
|
||||
for i := range setting.Names {
|
||||
key := "locale_" + setting.Langs[i] + ".ini"
|
||||
var localeDataBase []byte
|
||||
if i == 0 && setting.Langs[0] != "en-US" {
|
||||
// Only en-US has complete translations. When use other language as default, the en-US should still be used as fallback.
|
||||
localeDataBase = localeData["locale_en-US.ini"]
|
||||
if localeDataBase == nil {
|
||||
log.Fatal("Failed to load locale_en-US.ini file.")
|
||||
}
|
||||
}
|
||||
|
||||
if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], localFiles[key]); err != nil {
|
||||
key := "locale_" + setting.Langs[i] + ".ini"
|
||||
if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], localeDataBase, localeData[key]); err != nil {
|
||||
log.Error("Failed to set messages to %s: %v", setting.Langs[i], err)
|
||||
}
|
||||
}
|
||||
|
|
28
modules/util/file_unix.go
Normal file
28
modules/util/file_unix.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
// 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.
|
||||
|
||||
//go:build !windows
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var defaultUmask int
|
||||
|
||||
func init() {
|
||||
// at the moment, the umask could only be gotten by calling unix.Umask(newUmask)
|
||||
// use 0o077 as temp new umask to reduce the risks if this umask is used anywhere else before the correct umask is recovered
|
||||
tempUmask := 0o077
|
||||
defaultUmask = unix.Umask(tempUmask)
|
||||
unix.Umask(defaultUmask)
|
||||
}
|
||||
|
||||
func ApplyUmask(f string, newMode os.FileMode) error {
|
||||
mod := newMode & ^os.FileMode(defaultUmask)
|
||||
return os.Chmod(f, mod)
|
||||
}
|
36
modules/util/file_unix_test.go
Normal file
36
modules/util/file_unix_test.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
// 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.
|
||||
|
||||
//go:build !windows
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestApplyUmask(t *testing.T) {
|
||||
f, err := os.CreateTemp(t.TempDir(), "test-filemode-")
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = os.Chmod(f.Name(), 0o777)
|
||||
assert.NoError(t, err)
|
||||
st, err := os.Stat(f.Name())
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 0o777, st.Mode().Perm()&0o777)
|
||||
|
||||
oldDefaultUmask := defaultUmask
|
||||
defaultUmask = 0o037
|
||||
defer func() {
|
||||
defaultUmask = oldDefaultUmask
|
||||
}()
|
||||
err = ApplyUmask(f.Name(), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
st, err = os.Stat(f.Name())
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 0o740, st.Mode().Perm()&0o777)
|
||||
}
|
16
modules/util/file_windows.go
Normal file
16
modules/util/file_windows.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
// 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.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func ApplyUmask(f string, newMode os.FileMode) error {
|
||||
// do nothing for Windows, because Windows doesn't use umask
|
||||
return nil
|
||||
}
|
9
options/license/checkmk
Normal file
9
options/license/checkmk
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2006, 2010 Micah Cowan
|
||||
#
|
||||
# Redistribution of this program in any form, with or without
|
||||
# modifications, is permitted, provided that the above copyright is
|
||||
# retained in distributions of this program in source form.
|
||||
#
|
||||
# (This is a free, non-copyleft license compatible with pretty much any
|
||||
# other free or proprietary license, including the GPL. It's essentially
|
||||
# a scaled-down version of the "modified" BSD license.)
|
|
@ -2,6 +2,7 @@ home=Domů
|
|||
dashboard=Přehled
|
||||
explore=Procházet
|
||||
help=Nápověda
|
||||
logo=Logo
|
||||
sign_in=Přihlásit se
|
||||
sign_in_with=Přihlásit se pomocí
|
||||
sign_out=Odhlásit se
|
||||
|
@ -145,6 +146,8 @@ sqlite_helper=Cesta k souboru SQLite3 databáze.<br>Pokud spouštíte Gitea jako
|
|||
reinstall_error=Pokoušíte se nainstalovat do existující databáze Gitea
|
||||
reinstall_confirm_message=Přeinstalování s existující databází Gitea může způsobit více problémů. Ve většině případů byste měli použít existující „app.ini“ pro spuštění Gitea. Pokud víte, co děláte, potvrďte následující:
|
||||
reinstall_confirm_check_1=Data šifrovaná pomocí SECRET_KEY v souboru api.ini mohou být ztracena: uživatelé nemusí být schopni se přihlásit s 2FA/OTP a zrcadla nemusí fungovat správně. Zaškrtnutím tohoto políčka potvrdíte, že aktuální soubor app.ini obsahuje správný SECRET_KEY.
|
||||
reinstall_confirm_check_2=Může být nutné znovu synchronizovat repozitáře a nastavení. Zaškrtnutím tohoto políčka potvrzujete, že budete háčky pro repozitáře a soubor authorized_keys znovu synchronizovat ručně. Potvrzujete, že zajistíte správnost nastavení repozitáře a zrcadla.
|
||||
reinstall_confirm_check_3=Potvrzujete, že jste si naprosto jisti, že tato Gitea je spuštěna se správným umístěním souboru app.ini a že jste si jisti, že musíte provést novou instalaci. Potvrzujete, že berete na vědomí výše uvedená rizika.
|
||||
err_empty_db_path=Cesta k SQLite3 databázi nemůže být prázdná.
|
||||
no_admin_and_disable_registration=Nemůžete vypnout registraci účtů bez vytvoření účtu správce.
|
||||
err_empty_admin_password=Heslo administrátora nemůže být prázdné.
|
||||
|
@ -175,6 +178,8 @@ log_root_path_helper=Soubory protokolu budou zapsány do tohoto adresáře.
|
|||
|
||||
optional_title=Dodatečná nastavení
|
||||
email_title=Nastavení e-mailu
|
||||
smtp_addr=Server SMTP
|
||||
smtp_port=Port SMTP
|
||||
smtp_from=Odeslat e-mail jako
|
||||
smtp_from_helper=E-mailová adresa, kterou bude Gitea používat. Zadejte běžnou e-mailovou adresu, nebo použijte formát "Jméno"<email@example.com>.
|
||||
mailer_user=Uživatelské jméno SMTP
|
||||
|
@ -209,8 +214,11 @@ install_btn_confirm=Nainstalovat Gitea
|
|||
test_git_failed=Chyba při testu příkazu 'git': %v
|
||||
sqlite3_not_available=Tato verze Gitea nepodporuje SQLite3. Stáhněte si oficiální binární verzi od %s (nikoli verzi „gobuild“).
|
||||
invalid_db_setting=Nastavení databáze je neplatné: %v
|
||||
invalid_db_table=Databázová tabulka „%s“ je neplatná: %v
|
||||
invalid_repo_path=Kořenový adresář repozitářů není správný: %v
|
||||
invalid_app_data_path=Cesta k datům aplikace je neplatná: %v
|
||||
run_user_not_match="Run as" uživatelské jméno není aktuální uživatelské jméno: %s -> %s
|
||||
internal_token_failed=Nepodařilo se vytvořit interní token: %v
|
||||
secret_key_failed=Nepodařilo se vytvořit tajný klíč: %v
|
||||
save_config_failed=Uložení konfigurace se nezdařilo: %v
|
||||
invalid_admin_setting=Nastavení účtu správce není správné: %v
|
||||
|
@ -240,6 +248,7 @@ view_home=Zobrazit %s
|
|||
search_repos=Nalézt repozitář…
|
||||
filter=Ostatní filtry
|
||||
filter_by_team_repositories=Filtrovat podle repozitářů týmu
|
||||
feed_of=Kanál z „%s“
|
||||
|
||||
show_archived=Archivováno
|
||||
show_both_archived_unarchived=Zobrazeny jak archivované tak nearchivované
|
||||
|
@ -268,6 +277,8 @@ org_no_results=Nebyly nalezeny žádné odpovídající organizace.
|
|||
code_no_results=Nebyl nalezen žádný zdrojový kód odpovídající hledanému výrazu.
|
||||
code_search_results=Výsledky hledání pro „%s“
|
||||
code_last_indexed_at=Naposledy indexováno %s
|
||||
relevant_repositories_tooltip=Repozitáře, které jsou rozštěpení nebo nemají žádné téma, ikonu a žádný popis jsou skryty.
|
||||
relevant_repositories=Zobrazují se pouze relevantní repositáře, <a href="%s">zobrazit nefiltrované výsledky</a>.
|
||||
|
||||
|
||||
[auth]
|
||||
|
@ -315,7 +326,9 @@ oauth_signup_submit=Dokončit účet
|
|||
oauth_signin_tab=Propojit s existujícím účtem
|
||||
oauth_signin_title=Přihlaste se pro ověření propojeného účtu
|
||||
oauth_signin_submit=Propojit účet
|
||||
oauth.signin.error=Došlo k chybě při zpracování žádosti o autorizaci. Pokud tato chyba přetrvává, obraťte se na správce webu.
|
||||
oauth.signin.error.access_denied=Žádost o autorizaci byla zamítnuta.
|
||||
oauth.signin.error.temporarily_unavailable=Autorizace se nezdařila, protože ověřovací server je dočasně nedostupný. Opakujte akci později.
|
||||
openid_connect_submit=Připojit
|
||||
openid_connect_title=Připojení k existujícímu účtu
|
||||
openid_connect_desc=Zvolené OpenID URI není známé. Přidružte nový účet zde.
|
||||
|
@ -366,7 +379,7 @@ issue_assigned.pull=@%[1]s vás přiřadil/a k požadavku na natažení %[2]v re
|
|||
issue_assigned.issue=@%[1]s vás přiřadil/a k úkolu %[2]v repozitáři %[3]s.
|
||||
|
||||
issue.x_mentioned_you=<b>@%s</b> vás zmínil/a:
|
||||
issue.action.force_push=<b>%[1]s</b> vynutil/a nahrání <b>%[2]s</b> z %[3]do %[4].
|
||||
issue.action.force_push=<b>%[1]s</b> vynutil/a nahrání <b>%[2]s</b> z %[3]s do %[4]s.
|
||||
issue.action.push_1=<b>@%[1]s</b> nahrál/a %[3]d commit do %[2]s
|
||||
issue.action.push_n=<b>@%[1]s</b> nahrál/a %[3]d commity do %[2]s
|
||||
issue.action.close=<b>@%[1]s</b> uzavřel/a #%[2]d.
|
||||
|
@ -432,6 +445,7 @@ size_error=` musí být minimálně velikosti %s.`
|
|||
min_size_error=` musí obsahovat nejméně %s znaků.`
|
||||
max_size_error=` musí obsahovat maximálně %s znaků.`
|
||||
email_error=` není správná e-mailová adresa.`
|
||||
url_error=`„%s“ není platná adresa URL.`
|
||||
include_error=` musí obsahovat řetězec „%s“.`
|
||||
glob_pattern_error=`zástupný vzor je neplatný: %s.`
|
||||
regex_pattern_error=` regex vzor je neplatný: %s.`
|
||||
|
@ -443,6 +457,7 @@ lang_select_error=Vyberte jazyk ze seznamu.
|
|||
username_been_taken=Uživatelské jméno je již obsazeno.
|
||||
username_change_not_local_user=Uživatelé, kteří jsou ověřováni jinak než lokálně, si nemohou změnit uživatelské jméno.
|
||||
repo_name_been_taken=Název repozitáře je již použit.
|
||||
repository_force_private=Vynucené soukromí je povoleno: soukromé repozitáře nelze zveřejnit.
|
||||
repository_files_already_exist=Soubory pro tento repozitář již existují. Obraťte se na správce systému.
|
||||
repository_files_already_exist.adopt=Soubory pro tento repozitář již existují a mohou být pouze přijaty.
|
||||
repository_files_already_exist.delete=Soubory pro tento repozitář již existují. Musíte je odstranit.
|
||||
|
@ -478,7 +493,9 @@ auth_failed=Ověření selhalo: %v
|
|||
|
||||
still_own_repo=Váš účet vlastní jeden nebo více repozitářů; smažte je nebo převeďte.
|
||||
still_has_org=Váš účet je člen jedné nebo více organizací; nejdříve je opusťte.
|
||||
still_own_packages=Váš účet vlastní jeden nebo více balíčků. Nejprve je musíte odstranit.
|
||||
org_still_own_repo=Organizace stále vlastní jeden nebo více repozitářů; smažte je nebo převeďte.
|
||||
org_still_own_packages=Organizace stále vlastní jeden nebo více balíčků; nejprve je smažte.
|
||||
|
||||
target_branch_not_exist=Cílová větev neexistuje.
|
||||
|
||||
|
@ -541,6 +558,7 @@ continue=Pokračovat
|
|||
cancel=Zrušit
|
||||
language=Jazyk
|
||||
ui=Motiv vzhledu
|
||||
hidden_comment_types=Skryté typy komentářů
|
||||
comment_type_group_reference=Reference
|
||||
comment_type_group_label=Štítek
|
||||
comment_type_group_milestone=Milník
|
||||
|
@ -551,9 +569,11 @@ comment_type_group_time_tracking=Sledování času
|
|||
comment_type_group_deadline=Uzávěrka
|
||||
comment_type_group_dependency=Závislost
|
||||
comment_type_group_lock=Stav zámku
|
||||
comment_type_group_review_request=Žádost o posouzení
|
||||
comment_type_group_pull_request_push=Přidané commity
|
||||
comment_type_group_project=Projekt
|
||||
comment_type_group_issue_ref=Referenční číslo úkolu
|
||||
saved_successfully=Vaše nastavení bylo úspěšně uloženo.
|
||||
privacy=Soukromí
|
||||
keep_activity_private=Skrýt aktivitu z profilové stránky
|
||||
keep_activity_private_popup=Učinit aktivitu viditelnou pouze pro vás a administrátory
|
||||
|
@ -639,10 +659,18 @@ gpg_token_required=Musíte zadat podpis pro níže uvedený token
|
|||
gpg_token=Token
|
||||
gpg_token_help=Podpis můžete vygenerovat pomocí:
|
||||
gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
|
||||
gpg_token_signature=Zakódovaný podpis GPG
|
||||
key_signature_gpg_placeholder=Začíná s „-----BEGIN PGP SIGNATURE-----“
|
||||
verify_gpg_key_success=GPG klíč „%s“ byl ověřen.
|
||||
ssh_key_verified=Ověřený klíč
|
||||
ssh_key_verified_long=Klíč byl ověřen pomocí tokenu a může být použit k ověření commitů shodujících se s libovolnou vaší aktivovanou e-mailovou adresou pro tohoto uživatele.
|
||||
ssh_key_verify=Ověřit
|
||||
ssh_invalid_token_signature=Zadaný SSH klíč, podpis nebo token se neshodují nebo je token zastaralý.
|
||||
ssh_token_required=Musíte zadat podpis pro níže uvedený token
|
||||
ssh_token=Token
|
||||
ssh_token_help=Podpis můžete vygenerovat pomocí:
|
||||
ssh_token_signature=Zakódovaný podpis SSH
|
||||
key_signature_ssh_placeholder=Začíná s „-----BEGIN SSH SIGNATURE-----“
|
||||
verify_ssh_key_success=SSH klíč „%s“ byl ověřen.
|
||||
subkeys=Podklíče
|
||||
key_id=ID klíče
|
||||
|
@ -670,7 +698,7 @@ no_activity=Žádná aktuální aktivita
|
|||
can_read_info=Čtení
|
||||
can_write_info=Zápis
|
||||
key_state_desc=Tento klíč byl použit během posledních 7 dní
|
||||
token_state_desc=Tato poukázka byla použita během posledních 7 dní
|
||||
token_state_desc=Tento token byl použit během posledních 7 dní
|
||||
principal_state_desc=Tento SSH Principal certifikát byl použit během posledních 7 dní
|
||||
show_openid=Zobrazit na profilu
|
||||
hide_openid=Odstranit z profilu
|
||||
|
@ -681,23 +709,26 @@ social_desc=Tyto účty sociálních síti jsou propojeny s vaším Gitea účte
|
|||
unbind=Odpojit
|
||||
unbind_success=Účet sociální sítě byl odpojen od vašeho Gitea účtu.
|
||||
|
||||
manage_access_token=Spravovat přístupové poukázky
|
||||
generate_new_token=Vygenerovat novou poukázku
|
||||
tokens_desc=Tyto poukázky umožňují přístup k vašemu účtu pomocí Gitea API.
|
||||
new_token_desc=Aplikace používající poukázku mají plný přístup k vašemu účtu.
|
||||
token_name=Název poukázky
|
||||
generate_token=Vygenerovat poukázku
|
||||
generate_token_success=Váše nová poukázka byla vytvořena. Zkopírujte ji nyní protože se již znovu nezobrazí.
|
||||
manage_access_token=Spravovat přístupové tokeny
|
||||
generate_new_token=Vygenerovat nový token
|
||||
tokens_desc=Tyto tokeny umožňují přístup k vašemu účtu pomocí Gitea API.
|
||||
new_token_desc=Aplikace používající token mají plný přístup k vašemu účtu.
|
||||
token_name=Název tokenu
|
||||
generate_token=Vygenerovat token
|
||||
generate_token_success=Váš nový token byl vytvořen. Zkopírujte jej nyní protože se již znovu nezobrazí.
|
||||
generate_token_name_duplicate=<strong>%s</strong> byl již použit jako název aplikace. Použijte prosím nový.
|
||||
delete_token=Smazat
|
||||
access_token_deletion=Odstranit přístupovou poukázku
|
||||
delete_token_success=Poukázka byla odstraněna. Aplikace, které ji používají již nemají přístup k vašemu účtu.
|
||||
access_token_deletion=Odstranit přístupový token
|
||||
access_token_deletion_cancel_action=Zrušit
|
||||
access_token_deletion_confirm_action=Smazat
|
||||
access_token_deletion_desc=Smazání tokenu zruší přístup k vašemu účtu pro aplikace, které jej používají. Tuto akci nelze vrátit. Pokračovat?
|
||||
delete_token_success=Token byl odstraněn. Aplikace, které jej používají již nemají přístup k vašemu účtu.
|
||||
|
||||
manage_oauth2_applications=Spravovat OAuth2 aplikace
|
||||
edit_oauth2_application=Upravit OAuth2 aplikaci
|
||||
oauth2_applications_desc=OAuth2 aplikace umožní aplikacím třetích stran bezpečně ověřit uživatele v této instanci Gitea.
|
||||
remove_oauth2_application=Odstranit OAuth2 aplikaci
|
||||
remove_oauth2_application_desc=Odstraněním OAuth2 aplikace odeberete přístup všem podepsaným přístupovým poukázkám. Pokračovat?
|
||||
remove_oauth2_application_desc=Odstraněním OAuth2 aplikace odeberete přístup všem podepsaným přístupovým tokenům. Pokračovat?
|
||||
remove_oauth2_application_success=Aplikace byla odstraněna.
|
||||
create_oauth2_application=Vytvořit novou OAuth2 aplikaci
|
||||
create_oauth2_application_button=Vytvořit aplikaci
|
||||
|
@ -729,12 +760,12 @@ twofa_desc=Dvoufaktorový způsob ověřování zvýší zabezpečení vašeho
|
|||
twofa_is_enrolled=Váš účet aktuálně <strong>používá</strong> dvoufaktorové ověřování.
|
||||
twofa_not_enrolled=Váš účet aktuálně nepoužívá dvoufaktorové ověřování.
|
||||
twofa_disable=Zakázat dvoufaktorové ověřování
|
||||
twofa_scratch_token_regenerate=Obnovit pomocnou poukázku
|
||||
twofa_scratch_token_regenerated=Vaše pomocná poukázka je nyní %s. Uložte ji na bezpečném místě.
|
||||
twofa_scratch_token_regenerate=Obnovit pomocný token
|
||||
twofa_scratch_token_regenerated=Váš pomocný token je nyní %s. Uložte jej na bezpečném místě.
|
||||
twofa_enroll=Povolit dvoufaktorové ověřování
|
||||
twofa_disable_note=Dvoufaktorové ověřování můžete zakázat, když bude potřeba.
|
||||
twofa_disable_desc=Zakážete-li dvoufaktorové ověřování, bude váš účet méně zabezpečený. Pokračovat?
|
||||
regenerate_scratch_token_desc=Jestli jste někam založili vaši pomocnou poukázku nebo jste ji již použili k přihlášení, můžete ji resetovat zde.
|
||||
regenerate_scratch_token_desc=Jestli jste někam založili váš pomocný token nebo jste jej již použili k přihlášení, můžete jej resetovat zde.
|
||||
twofa_disabled=Dvoufaktorové ověřování bylo zakázáno.
|
||||
scan_this_image=Naskenujte tento obrázek s vaší ověřovací aplikací:
|
||||
or_enter_secret=Nebo zadejte tajný kód: %s
|
||||
|
@ -743,13 +774,16 @@ passcode_invalid=Přístupový kód není platný. Zkuste to znovu.
|
|||
twofa_enrolled=Ve vašem účtu bylo povoleno dvoufaktorové ověřování. Uložte si pomocný token (%s) na bezpečném místě, protože bude zobrazen pouze jednou!
|
||||
twofa_failed_get_secret=Nepodařilo se získat tajemství.
|
||||
|
||||
webauthn_desc=Bezpečnostní klíče jsou hardwarová zařízení obsahující kryptografické klíče. Mohou být použity pro dvoufaktorové ověřování. Bezpečnostní klíče musí podporovat <a rel="noreferrer" target="_blank" href="https://w3c.github.io/webauthn/#webauthn-authenticator">WebAuthn Authenticator</a> standard.
|
||||
webauthn_register_key=Přidat bezpečnostní klíč
|
||||
webauthn_nickname=Přezdívka
|
||||
webauthn_delete_key=Odstranit bezpečnostní klíč
|
||||
webauthn_delete_key_desc=Pokud odstraníte bezpečnostní klíč, již se s ním nebudete moci přihlásit. Pokračovat?
|
||||
|
||||
manage_account_links=Správa propojených účtů
|
||||
manage_account_links_desc=Tyto externí účty jsou propojeny s vaším Gitea účtem.
|
||||
account_links_not_available=K vašemu Gitea účtu nejsou aktuálně připojené žádné externí účty.
|
||||
link_account=Propojit účet
|
||||
remove_account_link=Odstranit propojený účet
|
||||
remove_account_link_desc=Odstraněním propojeného účtu zrušíte jeho přístup k vašemu Gitea účtu. Pokračovat?
|
||||
remove_account_link_success=Propojený účet byl odstraněn.
|
||||
|
@ -768,6 +802,7 @@ email_notifications.enable=Povolit e-mailová oznámení
|
|||
email_notifications.onmention=E-mail pouze při zmínce
|
||||
email_notifications.disable=Zakázat e-mailová oznámení
|
||||
email_notifications.submit=Nastavit předvolby e-mailu
|
||||
email_notifications.andyourown=A Vaše vlastní upozornění
|
||||
|
||||
visibility=Viditelnost uživatele
|
||||
visibility.public=Veřejný
|
||||
|
@ -796,6 +831,7 @@ visibility_fork_helper=(Změna tohoto ovlivní všechny rozštěpení repozitá
|
|||
clone_helper=Potřebujete pomoci s klonováním? Navštivte <a target="_blank" rel="noopener noreferrer" href="%s">nápovědu</a>.
|
||||
fork_repo=Rozštěpení repozitáře
|
||||
fork_from=Rozštěpit z
|
||||
already_forked=Již jsi rozštěpil %s
|
||||
fork_to_different_account=Rozštěpit na jiný účet
|
||||
fork_visibility_helper=Viditelnost rozštěpeného repozitáře nemůže být změněna.
|
||||
use_template=Použít tuto šablonu
|
||||
|
@ -829,7 +865,9 @@ default_branch=Výchozí větev
|
|||
default_branch_helper=Výchozí větev je základní větev pro požadavky na natažení a commity kódu.
|
||||
mirror_prune=Vyčistit
|
||||
mirror_prune_desc=Odstranit zastaralé reference na vzdálené sledování
|
||||
mirror_interval=Interval zrcadlení (platné časové jednotky jsou „h“, „m“ a „s“). 0 zakáže periodickou synchronizaci. (Minimální interval: %s)
|
||||
mirror_interval_invalid=Interval zrcadlení není platný.
|
||||
mirror_sync_on_commit=Synchronizovat při nahrávání revizí
|
||||
mirror_address=Klonovat z URL
|
||||
mirror_address_desc=Zadejte požadované přístupové údaje do sekce Ověření.
|
||||
mirror_address_url_invalid=Poskytnutá URL je neplatná. Všechny komponenty musíte správně nahradit escape sekvencí.
|
||||
|
@ -858,6 +896,7 @@ delete_preexisting_label=Smazat
|
|||
delete_preexisting=Odstranit již existující soubory
|
||||
delete_preexisting_content=Odstranit soubory v %s
|
||||
delete_preexisting_success=Smazány nepřijaté soubory v %s
|
||||
blame_prior=Zobrazit blame před touto změnou
|
||||
|
||||
transfer.accept=Přijmout převod
|
||||
transfer.accept_desc=Převést do „%s“
|
||||
|
@ -877,6 +916,7 @@ desc.archived=Archivováno
|
|||
template.items=Položky šablony
|
||||
template.git_content=Obsah gitu (výchozí větev)
|
||||
template.git_hooks=Háčky Gitu
|
||||
template.git_hooks_tooltip=Momentálně nemůžete po přidání upravovat nebo odebrat háčky gitu. Vyberte pouze v případě, že důvěřujete šabloně repozitáře.
|
||||
template.webhooks=Webové háčky
|
||||
template.topics=Témata
|
||||
template.avatar=Avatar
|
||||
|
@ -896,6 +936,7 @@ form.name_pattern_not_allowed=Vzor „%s“ není povolený v názvu repozitář
|
|||
need_auth=Ověření
|
||||
migrate_options=Možnosti migrace
|
||||
migrate_service=Migrační služba
|
||||
migrate_options_mirror_helper=Tento repozitář bude zrcadlem
|
||||
migrate_options_lfs=Migrovat LFS soubory
|
||||
migrate_options_lfs_endpoint.label=Koncový bod LFS
|
||||
migrate_options_lfs_endpoint.description=Migrace se pokusí použít váš vzdálený Git pro <a target="_blank" rel="noopener noreferrer" href="%s">určení LFS serveru</a>. Můžete také zadat vlastní koncový bod, pokud jsou data LFS repozitáře uložena někde jinde.
|
||||
|
@ -912,8 +953,10 @@ migrate_items_releases=Vydání
|
|||
migrate_repo=Migrovat repozitář
|
||||
migrate.clone_address=Migrovat / klonovat z URL
|
||||
migrate.clone_address_desc=HTTP(S) nebo URL pro klonování existujícího repozitáře
|
||||
migrate.github_token_desc=Můžete sem vložit jeden nebo více tokenů oddělených čárkou, abyste urychlili migraci kvůli omezení rychlosti rozhraní GitHub API. VAROVÁNÍ: Zneužití této funkce může vést k porušení zásad poskytovatele služeb a zablokování účtu.
|
||||
migrate.clone_local_path=nebo místní cesta serveru
|
||||
migrate.permission_denied=Není dovoleno importovat místní repozitáře.
|
||||
migrate.permission_denied_blocked=Nelze importovat z nepovolených hostitelů, prosím požádejte správce, aby zkontroloval nastavení ALLOWED_DOMAINS/ALLOW_LOCALETWORKS/BLOCKED_DOMAINS.
|
||||
migrate.invalid_local_path=Místní cesta je neplatná, buď neexistuje nebo není adresářem.
|
||||
migrate.invalid_lfs_endpoint=Koncový bod LFS není platný.
|
||||
migrate.failed=Přenesení selhalo: %v
|
||||
|
@ -961,6 +1004,7 @@ clone_this_repo=Naklonovat tento repozitář
|
|||
create_new_repo_command=Vytvořit nový repozitář na příkazové řádce
|
||||
push_exist_repo=Nahrání existujícího repozitáře z příkazové řádky
|
||||
empty_message=Tento repozitář nemá žádný obsah.
|
||||
broken_message=Data gitu, která jsou základem tohoto repozitáře, nelze číst. Kontaktujte správce této instance nebo smažte tento repositář.
|
||||
|
||||
code=Zdrojový kód
|
||||
code.desc=Přístup ke zdrojovým kódům, souborům, commitům a větvím.
|
||||
|
@ -974,6 +1018,7 @@ tags=Značky
|
|||
issues=Úkoly
|
||||
pulls=Požadavky na natažení
|
||||
project_board=Projekty
|
||||
packages=Balíčky
|
||||
labels=Štítky
|
||||
org_labels_desc=Štítky na úrovni organizace, které mohou být použity se <strong>všemi repozitáři</strong> v rámci této organizace
|
||||
org_labels_desc_manage=spravovat
|
||||
|
@ -993,8 +1038,18 @@ file_view_rendered=Zobrazit vykreslené
|
|||
file_view_raw=Zobrazit v surovém stavu
|
||||
file_permalink=Trvalý odkaz
|
||||
file_too_large=Soubor je příliš velký pro zobrazení.
|
||||
invisible_runes_header=`Tento soubor obsahuje neviditelné znaky Unicode!`
|
||||
invisible_runes_description=`Tento soubor obsahuje neviditelné znaky Unicode, které mohou být zpracovány jinak než níže uvedeným způsobem. Pokud je váš případ úmyslný a legitimní, můžete toto varování bezpečně ignorovat. Použijte tlačítko Escape sekvence k odhalení skrytých znaků.`
|
||||
ambiguous_runes_header=`Tento soubor obsahuje nejednoznačné znaky Unicode!`
|
||||
ambiguous_runes_description=`Tento soubor obsahuje nejednoznačné znaky Unicode, které mohou být zaměněny s ostatními v aktuálním prostředí. Pokud je váš případ úmyslný a legitimní, můžete toto varování bezpečně ignorovat. Použijte tlačítko Escape sekvence pro zvýraznění těchto znaků.`
|
||||
invisible_runes_line=`Tento řádek má neviditelné znaky Unicode`
|
||||
ambiguous_runes_line=`Tento řádek má nejednoznačné znaky Unicode`
|
||||
ambiguous_character=`%[1]c [U+%04[1]X] je zaměnitelný s %[2]c [U+%04[2]X]`
|
||||
|
||||
escape_control_characters=Escape sekvence
|
||||
unescape_control_characters=Bez escape sekvencí
|
||||
file_copy_permalink=Kopírovat trvalý odkaz
|
||||
view_git_blame=Zobrazit Git Blame
|
||||
video_not_supported_in_browser=Váš prohlížeč nepodporuje značku pro HTML5 video.
|
||||
audio_not_supported_in_browser=Váš prohlížeč nepodporuje značku pro HTML5 audio.
|
||||
stored_lfs=Uloženo pomocí Git LFS
|
||||
|
@ -1010,6 +1065,7 @@ normal_view=Normální zobrazení
|
|||
line=řádek
|
||||
lines=řádky
|
||||
|
||||
editor.add_file=Přidat soubor
|
||||
editor.new_file=Nový soubor
|
||||
editor.upload_file=Nahrát soubor
|
||||
editor.edit_file=Upravit soubor
|
||||
|
@ -1033,6 +1089,10 @@ editor.add_tmpl=Přidán „<nazev_souboru>“
|
|||
editor.add=Přidat „%s“
|
||||
editor.update=Aktualizovat „%s“
|
||||
editor.delete=Smazat „%s“
|
||||
editor.patch=Použít záplatu
|
||||
editor.patching=Záplatování:
|
||||
editor.fail_to_apply_patch=Nelze použít záplatu „%s“
|
||||
editor.new_patch=Nová záplata
|
||||
editor.commit_message_desc=Přidat volitelný rozšířený popis…
|
||||
editor.signoff_desc=Přidat Signed-off-by podpis přispěvatele na konec zprávy o commitu.
|
||||
editor.commit_directly_to_this_branch=Odevzdat přímo do větve <strong class="branch-name">%s</strong>.
|
||||
|
@ -1057,6 +1117,8 @@ editor.commit_empty_file_text=Soubor, který se chystáte odevzdat, je prázdný
|
|||
editor.no_changes_to_show=Žádné změny k zobrazení.
|
||||
editor.fail_to_update_file=Nepodařilo se aktualizovat/vytvořit soubor „%s“.
|
||||
editor.fail_to_update_file_summary=Chybové hlášení:
|
||||
editor.push_rejected_no_message=Změna byla serverem zamítnuta bez zprávy. Prosím, zkontrolujte háčky Gitu.
|
||||
editor.push_rejected=Změna byla serverem zamítnuta. Prosím, zkontrolujte háčky Gitu.
|
||||
editor.push_rejected_summary=Úplná zpráva o odmítnutí:
|
||||
editor.add_subdir=Přidat adresář…
|
||||
editor.unable_to_upload_files=Nepodařilo se nahrát soubor „%s“. Chyba: %v
|
||||
|
@ -1066,6 +1128,8 @@ editor.cannot_commit_to_protected_branch=Nelze vytvořit commit v chráněné v
|
|||
editor.no_commit_to_branch=Nelze odevzdat přímo do větve, protože:
|
||||
editor.user_no_push_to_branch=Uživatel nemůže nahrávat do větve
|
||||
editor.require_signed_commit=Větev vyžaduje podepsaný commit
|
||||
editor.cherry_pick=Cherry-pick %s na:
|
||||
editor.revert=Vrátit %s na:
|
||||
|
||||
commits.desc=Procházet historii změn zdrojového kódu.
|
||||
commits.commits=Commity
|
||||
|
@ -1092,7 +1156,9 @@ commit.revert-header=Vrátit: %s
|
|||
commit.revert-content=Vyberte větev pro návrat na:
|
||||
commit.cherry-pick=Cherry-pick
|
||||
commit.cherry-pick-header=Cherry-pick: %s
|
||||
commit.cherry-pick-content=Vyberte větev pro Cherry-pick na:
|
||||
|
||||
ext_issues=Přístup k externím úkolům
|
||||
ext_issues.desc=Odkaz na externí systém úkolů.
|
||||
|
||||
projects=Projekty
|
||||
|
@ -1165,6 +1231,8 @@ issues.new.add_reviewer_title=Požádat o posouzení
|
|||
issues.choose.get_started=Začínáme
|
||||
issues.choose.blank=Výchozí
|
||||
issues.choose.blank_about=Vytvořit úkol z výchozí šablony.
|
||||
issues.choose.ignore_invalid_templates=Neplatné šablony byly ignorovány
|
||||
issues.choose.invalid_templates=%v nalezených neplatných šablon
|
||||
issues.no_ref=Není určena žádná větev/značka
|
||||
issues.create=Vytvořit úkol
|
||||
issues.new_label=Nový štítek
|
||||
|
@ -1194,6 +1262,7 @@ issues.add_assignee_at=`byl přiřazen <b>%s</b> %s`
|
|||
issues.remove_assignee_at=`byl odstraněn z přiřazení <b>%s</b> %s`
|
||||
issues.remove_self_assignment=`odstranil/a jejich přiřazení %s`
|
||||
issues.change_title_at=`změnil/a název z <b><strike>%s</strike></b> na <b>%s</b> %s`
|
||||
issues.change_ref_at=`změnil/a referenci z <b><strike>%s</strike></b> na <b>%s</b> %s`
|
||||
issues.remove_ref_at=`odstranil/a referenci <b>%s</b> %s`
|
||||
issues.add_ref_at=`přidal/a referenci <b>%s</b> %s`
|
||||
issues.delete_branch_at=`odstranil/a větev <b>%s</b> %s`
|
||||
|
@ -1204,6 +1273,8 @@ issues.filter_milestone=Milník
|
|||
issues.filter_milestone_no_select=Všechny milníky
|
||||
issues.filter_assignee=Zpracovatel
|
||||
issues.filter_assginee_no_select=Všichni zpracovatelé
|
||||
issues.filter_poster=Autor
|
||||
issues.filter_poster_no_select=Všichni autoři
|
||||
issues.filter_type=Typ
|
||||
issues.filter_type.all_issues=Všechny úkoly
|
||||
issues.filter_type.assigned_to_you=Přiřazené vám
|
||||
|
@ -1223,6 +1294,7 @@ issues.filter_sort.moststars=Nejvíce hvězdiček
|
|||
issues.filter_sort.feweststars=Nejméně hvězdiček
|
||||
issues.filter_sort.mostforks=Nejvíce rozštěpení
|
||||
issues.filter_sort.fewestforks=Nejméně rozštěpení
|
||||
issues.keyword_search_unavailable=V současné době vyhledávání podle klíčového slova není dostupné. Obraťte se na správce webu.
|
||||
issues.action_open=Otevřít
|
||||
issues.action_close=Zavřít
|
||||
issues.action_label=Štítek
|
||||
|
@ -1231,12 +1303,16 @@ issues.action_milestone_no_select=Žádný milník
|
|||
issues.action_assignee=Zpracovatel
|
||||
issues.action_assignee_no_select=Bez zpracovatele
|
||||
issues.opened_by=otevřeno %[1]s uživatelem <a href="%[2]s">%[3]s</a>
|
||||
pulls.merged_by=od <a href="%[2]s">%[3]s</a> byl sloučen %[1]s
|
||||
pulls.merged_by_fake=od %[2]s byl sloučen %[1]s
|
||||
issues.closed_by=od <a href="%[2]s">%[3]s</a> byl uzavřen %[1]s
|
||||
issues.opened_by_fake=otevřeno %[1]s uživatelem %[2]s
|
||||
issues.closed_by_fake=od %[2]s byl uzavřen %[1]s
|
||||
issues.previous=Předchozí
|
||||
issues.next=Další
|
||||
issues.open_title=otevřený
|
||||
issues.closed_title=zavřený
|
||||
issues.draft_title=Koncept
|
||||
issues.num_comments=%d komentářů
|
||||
issues.commented_at=`okomentoval <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Jste si jist, že chcete smazat tento komentář?
|
||||
|
@ -1353,6 +1429,7 @@ issues.due_date_form_remove=Odstranit
|
|||
issues.due_date_not_writer=Potřebujete práva na zápis do repozitáře pro úpravy termínu dokončení úkolu.
|
||||
issues.due_date_not_set=Žádný termín dokončení.
|
||||
issues.due_date_added=přidal/a termín dokončení %s %s
|
||||
issues.due_date_modified=upravil/a termín termínu z %[2]s na %[1]s %[3]s
|
||||
issues.due_date_remove=odstranil/a termín dokončení %s %s
|
||||
issues.due_date_overdue=Zpožděné
|
||||
issues.due_date_invalid=Termín dokončení není platný nebo je mimo rozsah. Použijte prosím formát „rrrr-mm-dd“.
|
||||
|
@ -1397,6 +1474,7 @@ issues.review.add_review_request=vyžádal posouzení od %s %s
|
|||
issues.review.remove_review_request=odstranil žádost o posouzení na %s %s
|
||||
issues.review.remove_review_request_self=odmítl posoudit %s
|
||||
issues.review.pending=Čekající
|
||||
issues.review.pending.tooltip=Tento komentář není momentálně viditelný pro ostatní uživatele. Chcete-li odeslat Vaše čekající komentáře, vyberte „%s“ → „%s/%s/%s“ v horní části stránky.
|
||||
issues.review.review=Posouzení
|
||||
issues.review.reviewers=Posuzovatelé
|
||||
issues.review.outdated=Zastaralé
|
||||
|
@ -1415,8 +1493,9 @@ issues.content_history.created=vytvořeno
|
|||
issues.content_history.delete_from_history=Smazat z historie
|
||||
issues.content_history.delete_from_history_confirm=Smazat z historie?
|
||||
issues.content_history.options=Možnosti
|
||||
issues.reference_link=Reference: %s
|
||||
|
||||
compare.compare_base=základ
|
||||
compare.compare_base=základní
|
||||
compare.compare_head=porovnat
|
||||
|
||||
pulls.desc=Povolit požadavky na natažení a posuzování kódu.
|
||||
|
@ -1427,13 +1506,18 @@ pulls.allow_edits_from_maintainers=Povolit úpravy od správců
|
|||
pulls.allow_edits_from_maintainers_desc=Uživatelé s přístupem k zápisu do základní větve mohou také nahrávat do této větve
|
||||
pulls.allow_edits_from_maintainers_err=Aktualizace se nezdařila
|
||||
pulls.compare_changes_desc=Vyberte větev pro sloučení a větev pro natažení.
|
||||
pulls.has_viewed_file=Zobrazeno
|
||||
pulls.has_changed_since_last_review=Změněno od vašeho posledního posouzení
|
||||
pulls.viewed_files_label=%[1]d / %[2]d souborů zobrazeno
|
||||
pulls.compare_base=sloučit do
|
||||
pulls.compare_compare=natáhnout z
|
||||
pulls.switch_comparison_type=Přepnout typ porovnání
|
||||
pulls.switch_head_and_base=Prohodit hlavní a základní větev
|
||||
pulls.filter_branch=Filtrovat větev
|
||||
pulls.no_results=Nebyly nalezeny žádné výsledky.
|
||||
pulls.nothing_to_compare=Tyto větve jsou stejné. Není potřeba vytvářet požadavek na natažení.
|
||||
pulls.nothing_to_compare_and_allow_empty_pr=Tyto větve jsou stejné. Tento požadavek na natažení bude prázdný.
|
||||
pulls.has_pull_request=`Požadavek na natažení mezi těmito větvemi již existuje: <a href="%[1]s">%[2]s#%[3]d</a>`
|
||||
pulls.create=Vytvořit požadavek na natažení
|
||||
pulls.title_desc=chce sloučit %[1]d commity z větve <code>%[2]s</code> do <code id="branch_target">%[3]s</code>
|
||||
pulls.merged_title_desc=sloučil %[1]d commity z větve <code>%[2]s</code> do větve <code>%[3]s</code> před %[4]s
|
||||
|
@ -1457,6 +1541,8 @@ pulls.remove_prefix=Odstranit prefix <strong>%s</strong>
|
|||
pulls.data_broken=Tento požadavek na natažení je rozbitý kvůli chybějícím informacím o rozštěpení.
|
||||
pulls.files_conflicted=Tento požadavek na natažení obsahuje změny, které kolidují s cílovou větví.
|
||||
pulls.is_checking=Právě probíhá kontrola konfliktů při sloučení. Zkuste to za chvíli.
|
||||
pulls.is_ancestor=Tato větev je již součástí cílové větve. Není co sloučit.
|
||||
pulls.is_empty=Změny na této větvi jsou již na cílové větvi. Toto bude prázdný commit.
|
||||
pulls.required_status_check_failed=Některé požadované kontroly nebyly úspěšné.
|
||||
pulls.required_status_check_missing=Některé požadované kontroly chybí.
|
||||
pulls.required_status_check_administrator=Jako administrátor stále můžete sloučit tento požadavek na natažení.
|
||||
|
@ -1485,6 +1571,7 @@ pulls.no_merge_wip=Požadavek na natažení nemůže být sloučen protože je o
|
|||
pulls.no_merge_not_ready=Tento požadavek na natažení není připraven na sloučení, zkontrolujte stav posouzení a kontrolu stavu.
|
||||
pulls.no_merge_access=Nemáte oprávnění sloučit tento požadavek na natažení.
|
||||
pulls.merge_pull_request=Vytvořit slučovací commit
|
||||
pulls.rebase_merge_pull_request=Rebase pak fast-forward
|
||||
pulls.rebase_merge_commit_pull_request=Rebase a poté vytvořit slučovací commit
|
||||
pulls.squash_merge_pull_request=Vytvořit squash commit
|
||||
pulls.merge_manually=Sloučeno ručně
|
||||
|
@ -1497,9 +1584,12 @@ pulls.merge_conflict_summary=Chybové hlášení
|
|||
pulls.rebase_conflict=Sloučení selhalo: Došlo ke konfliktu při rebase commitu: %[1]s. Tip: Zkuste jinou strategii
|
||||
pulls.rebase_conflict_summary=Chybové hlášení
|
||||
; </summary><code>%[2]s<br>%[3]s</code></details>
|
||||
pulls.unrelated_histories=Sloučení selhalo: Základní revize nesdílí společnou historii. Tip: Zkuste jinou strategii
|
||||
pulls.unrelated_histories=Sloučení selhalo: Hlavní a základní revize nesdílí společnou historii. Tip: Zkuste jinou strategii
|
||||
pulls.merge_out_of_date=Sloučení selhalo: Základ byl aktualizován při generování sloučení. Tip: Zkuste to znovu.
|
||||
pulls.head_out_of_date=Sloučení selhalo: Hlavní revize byla aktualizován při generování sloučení. Tip: Zkuste to znovu.
|
||||
pulls.push_rejected=Sloučení selhalo: Nahrání bylo zamítnuto. Zkontrolujte háčky Gitu pro tento repozitář.
|
||||
pulls.push_rejected_summary=Úplná zpráva o odmítnutí
|
||||
pulls.push_rejected_no_message=Sloučení se nezdařilo: Nahrání bylo odmítnuto, ale nebyla nalezena žádná vzdálená zpráva.<br>Zkontrolujte háčky gitu pro tento repozitář
|
||||
pulls.open_unmerged_pull_exists=`Nemůžete provést operaci znovuotevření protože je tu čekající požadavek na natažení (#%d) s identickými vlastnostmi.`
|
||||
pulls.status_checking=Některé kontroly jsou nedořešeny
|
||||
pulls.status_checks_success=Všechny kontroly byly úspěšné
|
||||
|
@ -1519,9 +1609,20 @@ pulls.merge_instruction_hint=`Můžete také zobrazit <a class="show-instruction
|
|||
pulls.merge_instruction_step1_desc=Z vašeho repositáře projektu se podívejte na novou větev a vyzkoušejte změny.
|
||||
pulls.merge_instruction_step2_desc=Slučte změny a aktualizujte je na Gitea.
|
||||
|
||||
pulls.auto_merge_button_when_succeed=(Když kontroly uspějí)
|
||||
pulls.auto_merge_when_succeed=Automaticky sloučit, když všechny kontroly uspějí
|
||||
pulls.auto_merge_newly_scheduled=Požadavek na natažení byl naplánován na sloučení, jakmile všechny kontroly uspějí.
|
||||
pulls.auto_merge_has_pending_schedule=%[1]s naplánoval/a tento požadavek na natažení pro automatické sloučení, když všechny kontroly uspějí v %[2]s.
|
||||
|
||||
pulls.auto_merge_cancel_schedule=Zrušit automatické sloučení
|
||||
pulls.auto_merge_not_scheduled=Tento požadavek na natažení není naplánován na automatické sloučení.
|
||||
pulls.auto_merge_canceled_schedule=Automatické sloučení bylo zrušeno pro tento požadavek na natažení.
|
||||
|
||||
pulls.auto_merge_newly_scheduled_comment=`požadavek na automatické sloučení tohoto požadavku na natažení je naplánován, když všechny kontroly uspějí %[1]s`
|
||||
pulls.auto_merge_canceled_schedule_comment=`zrušil/a automatické sloučení tohoto požadavku na natažení, když všechny kontroly uspějí %[1]s`
|
||||
|
||||
pulls.delete.title=Odstranit tento požadavek na natažení?
|
||||
pulls.delete.text=Opravdu chcete tento požadavek na natažení smazat? (Tím se trvale odstraní veškerý obsah. Pokud jej hodláte archivovat, zvažte raději jeho uzavření.)
|
||||
|
||||
milestones.new=Nový milník
|
||||
milestones.closed=Zavřen dne %s
|
||||
|
@ -1562,7 +1663,7 @@ signing.wont_sign.pubkey=Commit nebude podepsán, protože nemáte veřejný kl
|
|||
signing.wont_sign.twofa=Pro podepsání commitů musíte mít povoleno dvoufaktorové ověření
|
||||
signing.wont_sign.parentsigned=Commit nebude podepsán, protože nadřazený commit není podepsán
|
||||
signing.wont_sign.basesigned=Sloučení nebude podepsáno, protože základní commit není podepsaný
|
||||
signing.wont_sign.headsigned=Sloučení nebude podepsáno, protože základní commit není podepaný
|
||||
signing.wont_sign.headsigned=Sloučení nebude podepsáno, protože hlavní revize není podepsána
|
||||
signing.wont_sign.commitssigned=Sloučení nebude podepsáno, protože všechny přidružené revize nejsou podepsány
|
||||
signing.wont_sign.approved=Sloučení nebude podepsáno, protože požadavek na natažení není schválen
|
||||
signing.wont_sign.not_signed_in=Nejste přihlášeni
|
||||
|
@ -1592,6 +1693,7 @@ wiki.page_already_exists=Stránka Wiki se stejným názvem již existuje.
|
|||
wiki.reserved_page=Jméno Wiki stránky „%s“ je rezervováno.
|
||||
wiki.pages=Stránky
|
||||
wiki.last_updated=Naposledy aktualizováno: %s
|
||||
wiki.page_name_desc=Zadejte název této Wiki stránky. Některé speciální názvy jsou: „Home“, „_Sidebar“ a „_Footer“.
|
||||
|
||||
activity=Aktivita
|
||||
activity.period.filter_label=Období:
|
||||
|
@ -1664,6 +1766,8 @@ search.search_repo=Hledat repozitář
|
|||
search.fuzzy=Fuzzy
|
||||
search.match=Shoda
|
||||
search.results=Výsledky hledání „%s“ v <a href="%s">%s</a>
|
||||
search.code_no_results=Nebyl nalezen žádný zdrojový kód odpovídající hledanému výrazu.
|
||||
search.code_search_unavailable=V současné době není vyhledávání kódu dostupné. Obraťte se na správce webu.
|
||||
|
||||
settings=Nastavení
|
||||
settings.desc=Nastavení je místo, kde můžete měnit nastavení repozitáře
|
||||
|
@ -1678,6 +1782,7 @@ settings.hooks=Webové háčky
|
|||
settings.githooks=Háčky Gitu
|
||||
settings.basic_settings=Základní nastavení
|
||||
settings.mirror_settings=Nastavení zrcadla
|
||||
settings.mirror_settings.docs=Nastavte váš projekt pro automatické nahrávání a/nebo stahování změn z/do jiného repozitáře. Větve, značky a commity budou synchronizovány automaticky. <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/repo-mirror/">Jak mohu zrcadlit repozitáře?</a>
|
||||
settings.mirror_settings.mirrored_repository=Zrcadlený repozitář
|
||||
settings.mirror_settings.direction=Směr
|
||||
settings.mirror_settings.direction.pull=Natáhnout
|
||||
|
@ -1709,6 +1814,9 @@ settings.tracker_url_format_error=Formát URL externího systému úkolu není p
|
|||
settings.tracker_issue_style=Formát čísel externího systému úkolů
|
||||
settings.tracker_issue_style.numeric=Číselný
|
||||
settings.tracker_issue_style.alphanumeric=Alfanumerický
|
||||
settings.tracker_issue_style.regexp=Regulární výraz
|
||||
settings.tracker_issue_style.regexp_pattern=Vzor regulárního výrazu
|
||||
settings.tracker_issue_style.regexp_pattern_desc=První zachycená skupina bude použita místo <code>{index}</code>.
|
||||
settings.tracker_url_format_desc=Použijte zástupné symboly <code>{user}</code>, <code>{repo}</code> a <code>{index}</code> pro uživatelské jméno, jméno repozitáře a číslo úkolu.
|
||||
settings.enable_timetracker=Povolit sledování času
|
||||
settings.allow_only_contributors_to_track_time=Povolit sledování času pouze přispěvatelům
|
||||
|
@ -1720,6 +1828,9 @@ settings.pulls.allow_rebase_merge_commit=Povolit rebase s vyžádaným slučovac
|
|||
settings.pulls.allow_squash_commits=Povolit squash pro slučovací commity
|
||||
settings.pulls.allow_manual_merge=Povolit označování požadavků na natažení jako ručně sloučené
|
||||
settings.pulls.enable_autodetect_manual_merge=Povolit autodetekci ručních sloučení (Poznámka: V některých zvláštních případech může dojít k nesprávnému rozhodnutí)
|
||||
settings.pulls.allow_rebase_update=Povolit aktualizaci větve požadavku na natažení pomocí rebase
|
||||
settings.pulls.default_delete_branch_after_merge=Ve výchozím nastavení mazat větev požadavku na natažení po jeho sloučení
|
||||
settings.packages_desc=Povolit registr balíčků repozitáře
|
||||
settings.projects_desc=Povolit projekty v repozitáři
|
||||
settings.admin_settings=Nastavení správce
|
||||
settings.admin_enable_health_check=Povolit kontrolu stavu repozitáře (git fsck)
|
||||
|
@ -1753,6 +1864,7 @@ settings.transfer_form_title=Zadejte jméno repozitáře pro potvrzení:
|
|||
settings.transfer_in_progress=V současné době probíhá převod. Zrušte jej, pokud chcete převést tento repozitář jinému uživateli.
|
||||
settings.transfer_notices_1=- Ztratíte přístup k repozitáři, pokud jej převedete na uživatele.
|
||||
settings.transfer_notices_2=- Zůstane vám přístup k repozitáři, pokud jej převedete na organizaci kterou (spolu)vlastníte.
|
||||
settings.transfer_notices_3=- Pokud je repozitář soukromý a je předán jednotlivému uživateli, tato akce se ujistí, že uživatel má alespoň oprávnění ke čtení (a v případě potřeby změní oprávnění).
|
||||
settings.transfer_owner=Nový vlastník
|
||||
settings.transfer_perform=Provést převod
|
||||
settings.transfer_started=Tento repozitář byl označen pro převod a čeká na potvrzení od „%s“
|
||||
|
@ -1786,6 +1898,7 @@ settings.confirm_delete=Smazat repozitář
|
|||
settings.add_collaborator=Přidat spolupracovníka
|
||||
settings.add_collaborator_success=Spolupracovník byl přidán.
|
||||
settings.add_collaborator_inactive_user=Nelze přidat neaktivního uživatele jako spolupracovníka.
|
||||
settings.add_collaborator_owner=Vlastníka nelze přidat jako spolupracovníka.
|
||||
settings.add_collaborator_duplicate=Spolupracovník je již přidán k tomuto repozitáři.
|
||||
settings.delete_collaborator=Odstranit
|
||||
settings.collaborator_deletion=Odstranit spolupracovníka
|
||||
|
@ -1817,6 +1930,8 @@ settings.webhook.headers=Hlavičky
|
|||
settings.webhook.payload=Obsah
|
||||
settings.webhook.body=Tělo zprávy
|
||||
settings.webhook.replay.description=Zopakovat tento webový háček.
|
||||
settings.webhook.delivery.success=Událost byla přidána do fronty doručení. Může to trvat několik sekund, než se zobrazí v historii doručení.
|
||||
settings.githooks_desc=Jelikož háčky Gitu jsou spravovány Gitem samotným, můžete upravit soubory háčků k provádění uživatelských operací.
|
||||
settings.githook_edit_desc=Je-li háček neaktivní, bude zobrazen vzorový obsah. Nebude-li zadán žádný obsah, háček bude vypnut.
|
||||
settings.githook_name=Název háčku
|
||||
settings.githook_content=Obsah háčku
|
||||
|
@ -1842,6 +1957,8 @@ settings.event_delete=Smazat
|
|||
settings.event_delete_desc=Větev nebo značka smazána.
|
||||
settings.event_fork=Rozštěpit
|
||||
settings.event_fork_desc=Repozitář rozštěpen.
|
||||
settings.event_wiki=Wiki
|
||||
settings.event_wiki_desc=Wiki stránka vytvořena, přejmenována nebo smazána.
|
||||
settings.event_release=Vydání
|
||||
settings.event_release_desc=Vydání v tomto repozitáři bylo publikováno, aktualizováno nebo smazáno.
|
||||
settings.event_push=Nahrát
|
||||
|
@ -1877,6 +1994,7 @@ settings.event_pull_request_sync_desc=Požadavek na natažení synchronizován.
|
|||
settings.event_package=Balíček
|
||||
settings.event_package_desc=Balíček vytvořen nebo odstraněn v repozitáři.
|
||||
settings.branch_filter=Filtr větví
|
||||
settings.branch_filter_desc=Povolené větve pro události nahrání, vytvoření větve a smazání větve jsou určeny pomocí zástupného vzoru. Pokud je prázdný nebo <code>*</code>, všechny události jsou ohlášeny. Podívejte se na dokumentaci syntaxe na <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a>. Příklady: <code>master</code>, <code>{master,release*}</code>.
|
||||
settings.active=Aktivní
|
||||
settings.active_helper=Informace o spuštěných událostech budou odeslány na URL webového háčku.
|
||||
settings.add_hook_success=Webový háček byl přidán.
|
||||
|
@ -1885,9 +2003,10 @@ settings.update_hook_success=Webový háček byl aktualizován.
|
|||
settings.delete_webhook=Odstranit webový háček
|
||||
settings.recent_deliveries=Nedávné dodávky
|
||||
settings.hook_type=Typ háčku
|
||||
settings.slack_token=Poukázka
|
||||
settings.slack_token=Token
|
||||
settings.slack_domain=Doména
|
||||
settings.slack_channel=Kanál
|
||||
settings.add_web_hook_desc=Integrovat <a target="_blank" rel="noreferrer" href="%s">%s</a> do vašeho repozitáře.
|
||||
settings.web_hook_name_gitea=Gitea
|
||||
settings.web_hook_name_gogs=Gogs
|
||||
settings.web_hook_name_slack=Slack
|
||||
|
@ -1957,6 +2076,7 @@ settings.require_signed_commits_desc=Odmítnout nahrání do této větve pokud
|
|||
settings.protect_protected_file_patterns=Chráněné vzory souborů (oddělené středníkem „\;“):
|
||||
settings.protect_protected_file_patterns_desc=Chráněné soubory, které nemají povoleno být měněny přímo, i když uživatel má právo přidávat, upravovat nebo mazat soubory v této větvi. Více vzorů lze oddělit pomocí středníku („\;“). Podívejte se na <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> dokumentaci pro syntaxi vzoru. Příklady: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_unprotected_file_patterns=Nechráněné vzory souborů (oddělené středníkem „\;“):
|
||||
settings.protect_unprotected_file_patterns_desc=Nechráněné soubory, které je možné měnit přímo, pokud má uživatel právo zápisu, čímž se obejde omezení push. Více vzorů lze oddělit pomocí středníku ('\;'). Podívejte se na <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> dokumentaci pro syntaxi vzoru. Příklady: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.add_protected_branch=Zapnout ochranu
|
||||
settings.delete_protected_branch=Vypnout ochranu
|
||||
settings.update_protect_branch_success=Ochrana větví pro větev „%s“ byla aktualizována.
|
||||
|
@ -1984,7 +2104,8 @@ settings.tags.protection.allowed.teams=Povolené týmy
|
|||
settings.tags.protection.allowed.noone=Nikdo
|
||||
settings.tags.protection.create=Chránit značku
|
||||
settings.tags.protection.none=Neexistují žádné chráněné značky.
|
||||
settings.bot_token=Poukázka pro robota
|
||||
settings.tags.protection.pattern.description=Můžete použít jediné jméno nebo vzor glob nebo regulární výraz, který bude odpovídat více značek. Přečtěte si více v <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/protected-tags/">průvodci chráněnými značkami</a>.
|
||||
settings.bot_token=Token pro robota
|
||||
settings.chat_id=ID chatu
|
||||
settings.matrix.homeserver_url=URL adresa Homeserveru
|
||||
settings.matrix.room_id=ID místnosti
|
||||
|
@ -2043,7 +2164,7 @@ diff.git-notes=Poznámky
|
|||
diff.data_not_available=Rozdílový obsah není dostupný
|
||||
diff.options_button=Možnosti rozdílového porovnání
|
||||
diff.show_diff_stats=Zobrazit statistiky
|
||||
diff.download_patch=Stáhněte soubor opravy
|
||||
diff.download_patch=Stáhněte soubor záplaty
|
||||
diff.download_diff=Stáhněte rozdílový soubor
|
||||
diff.show_split_view=Rozdělené zobrazení
|
||||
diff.show_unified_view=Jednotný pohled
|
||||
|
@ -2066,7 +2187,9 @@ diff.file_suppressed=Rozdílový obsah nebyl zobrazen, protože je příliš vel
|
|||
diff.file_suppressed_line_too_long=Rozdílový obsah nebyl zobrazen, protože některé řádky jsou příliš dlouhá
|
||||
diff.too_many_files=Některé soubory nejsou zobrazny, neboť je v této revizi změněno mnoho souborů
|
||||
diff.show_more=Zobrazit více
|
||||
diff.load=Načíst rozdílové porovnání
|
||||
diff.generated=vygenerováno
|
||||
diff.vendored=vendorováno
|
||||
diff.comment.placeholder=Zanechat komentář
|
||||
diff.comment.markdown_info=Je podporována úprava vzhledu pomocí markdown.
|
||||
diff.comment.add_single_comment=Přidat jeden komentář
|
||||
|
@ -2172,6 +2295,8 @@ topic.done=Hotovo
|
|||
topic.count_prompt=Nelze vybrat více než 25 témat
|
||||
topic.format_prompt=Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
|
||||
|
||||
find_file.go_to_file=Přejít na soubor
|
||||
find_file.no_matching=Nebyl nalezen žádný odpovídající soubor
|
||||
|
||||
error.csv.too_large=Tento soubor nelze vykreslit, protože je příliš velký.
|
||||
error.csv.unexpected=Tento soubor nelze vykreslit, protože obsahuje neočekávaný znak na řádku %d ve sloupci %d.
|
||||
|
@ -2253,7 +2378,9 @@ teams.leave.detail=Opustit %s?
|
|||
teams.can_create_org_repo=Vytvořit repozitáře
|
||||
teams.can_create_org_repo_helper=Členové mohou vytvářet nové repozitáře v organizaci. Tvůrce získá přístup správce do nového repozitáře.
|
||||
teams.none_access=Bez přístupu
|
||||
teams.none_access_helper=Členové nemohou prohlížet ani dělat žádnou jinou akci pro tuto jednotku.
|
||||
teams.general_access=Obecný přístup
|
||||
teams.general_access_helper=O oprávnění členů bude rozhodnuto níže uvedenou tabulkou oprávnění.
|
||||
teams.read_access=Čtení
|
||||
teams.read_access_helper=Členové mohou zobrazit a klonovat repozitáře týmu.
|
||||
teams.write_access=Zápis
|
||||
|
@ -2307,9 +2434,11 @@ first_page=První
|
|||
last_page=Poslední
|
||||
total=Celkem: %d
|
||||
|
||||
dashboard.new_version_hint=Gitea %s je nyní k dispozici, používáte %s. Pro více informací si přečtěte <a target="_blank" rel="noreferrer" href="https://blog.gitea.io">blog</a>.
|
||||
dashboard.statistic=Souhrn
|
||||
dashboard.operations=Operace údržby
|
||||
dashboard.system_status=Status systému
|
||||
dashboard.statistic_info=Databáze Gitea obsahuje <b>%d</b> uživatelů, <b>%d</b> organizací, <b>%d</b> veřejných klíčů, <b>%d</b> repozitářů, <b>%d</b> hlídání, <b>%d</b> oblíbení, ~<b>%d</b> akcí, <b>%d</b> přístupů, <b>%d</b> úkolů, <b>%d</b> komentářů, <b>%d</b> účtů sociálních sítí, <b>%d</b> sledování, <b>%d</b> zrcadel, <b>%d</b> vydání, <b>%d</b> zdrojů ověřování, <b>%d</b> webových háčků, <b>%d</b> milníků, <b>%d</b> štítků, <b>%d</b> háčků, <b>%d</b> týmů, <b>%d</b> úkolů změn, <b>%d</b> příloh.
|
||||
dashboard.operation_name=Název operace
|
||||
dashboard.operation_switch=Přepnout
|
||||
dashboard.operation_run=Spustit
|
||||
|
@ -2381,6 +2510,7 @@ dashboard.gc_times=Časy GC
|
|||
dashboard.delete_old_actions=Odstranit všechny staré akce z databáze
|
||||
dashboard.delete_old_actions.started=Začalo odstraňování všech starých akcí z databáze.
|
||||
dashboard.update_checker=Kontrola aktualizací
|
||||
dashboard.delete_old_system_notices=Odstranit všechna stará systémová upozornění z databáze
|
||||
|
||||
users.user_manage_panel=Správa uživatelských účtů
|
||||
users.new_account=Vytvořit uživatelský účet
|
||||
|
@ -2415,8 +2545,12 @@ users.allow_import_local=Může importovat lokální repozitáře
|
|||
users.allow_create_organization=Může vytvářet organizace
|
||||
users.update_profile=Aktualizovat uživatelský účet
|
||||
users.delete_account=Smazat uživatelský účet
|
||||
users.cannot_delete_self=Nemůžete smazat sami sebe
|
||||
users.still_own_repo=Tento uživatel stále vlastní jeden nebo více repozitářů. Tyto repozitáře nejprve smažte nebo je převeďte.
|
||||
users.still_has_org=Uživatel je člen organizace. Nejprve odstraňte uživatele ze všech organizací.
|
||||
users.purge=Vymazat uživatele
|
||||
users.purge_help=Vynuceně smazat uživatele a všechny repositáře, organizace a balíčky vlastněné uživatelem. Všechny komentáře budou také smazány.
|
||||
users.still_own_packages=Tento uživatel stále vlastní jeden nebo více balíčků. Nejprve odstraňte tyto balíčky.
|
||||
users.deletion_success=Uživatelský účet byl smazán.
|
||||
users.reset_2fa=Resetovat 2FA
|
||||
users.list_status_filter.menu_text=Filtr
|
||||
|
@ -2507,6 +2641,7 @@ auths.attribute_name=Atribut křestního jména
|
|||
auths.attribute_surname=Atribut příjmení
|
||||
auths.attribute_mail=Atribut e-mailové adresy
|
||||
auths.attribute_ssh_public_key=Atribut veřejného SSH klíče
|
||||
auths.attribute_avatar=Atributy avataru
|
||||
auths.attributes_in_bind=Získat atributy v kontextu Bind DN
|
||||
auths.allow_deactivate_all=Povolit prázdný výsledek hledání pro deaktivaci všech uživatelů
|
||||
auths.use_paged_search=Použijte vyhledávání ve stránce
|
||||
|
@ -2515,6 +2650,7 @@ auths.filter=Uživatelský filtr
|
|||
auths.admin_filter=Správcovský filtr
|
||||
auths.restricted_filter=Filtr omezení
|
||||
auths.restricted_filter_helper=Ponechte prázdné, pokud nechcete nastavit žádné uživatele jako omezené. Použijte hvězdičku („*“) pro nastavení všech uživatelů, kteří neodpovídají filtru administrátora, jako omezené.
|
||||
auths.verify_group_membership=Ověřit členství ve skupině v LDAP (ponechte prázdný filtr pro přeskočení)
|
||||
auths.group_search_base=Základní DN pro hledání skupin
|
||||
auths.group_attribute_list_users=Skupinový atribut obsahující seznam uživatelů
|
||||
auths.user_attribute_in_group=Atribut uživatele ve skupině
|
||||
|
@ -2529,7 +2665,9 @@ auths.allowed_domains=Povolené domény
|
|||
auths.allowed_domains_helper=Nechte prázdné k povolení všech domén. Oddělte více domén pomocí čárky („,“).
|
||||
auths.skip_tls_verify=Přeskočit ověření TLS
|
||||
auths.force_smtps=Vynutit SMTPS
|
||||
auths.force_smtps_helper=SMTPS se vždy používá na portu 465. Nastavením této hodnoty vynutíte použití SMTPS na jiných portech. (V opačném případě se na ostatních portech použije STARTTLS, pokud je podporován hostiteslkým serverem.)
|
||||
auths.helo_hostname=HELO Hostname
|
||||
auths.helo_hostname_helper=Název hostitele odeslaný s HELO. Chcete-li odeslat aktuální název hostitele, ponechte prázdné.
|
||||
auths.disable_helo=Zakázat HELO
|
||||
auths.pam_service_name=Název služby PAM
|
||||
auths.pam_email_domain=PAM e-mailová doména (volitelné)
|
||||
|
@ -2539,11 +2677,21 @@ auths.oauth2_clientID=Klientské ID (klíč)
|
|||
auths.oauth2_clientSecret=Tajný klíč klienta
|
||||
auths.openIdConnectAutoDiscoveryURL=OpenID URL pro automatické objevování
|
||||
auths.oauth2_use_custom_url=Použijte vlastní URL místo výchozích
|
||||
auths.oauth2_tokenURL=URL poukázky
|
||||
auths.oauth2_tokenURL=URL tokenu
|
||||
auths.oauth2_authURL=Autorizační URL
|
||||
auths.oauth2_profileURL=URL profilu
|
||||
auths.oauth2_emailURL=URL e-mailu
|
||||
auths.skip_local_two_fa=Přeskočit lokální 2FA
|
||||
auths.skip_local_two_fa_helper=Ponechání nenastavené hodnoty znamená, že místní uživatelé s nastavenou funkcí 2FA budou muset při přihlašování stále projít funkcí 2FA
|
||||
auths.oauth2_tenant=Nájemník
|
||||
auths.oauth2_scopes=Další rozsahy
|
||||
auths.oauth2_required_claim_name=Požadovaný název tvrzení
|
||||
auths.oauth2_required_claim_name_helper=Nastavte toto jméno pro omezení přihlášení z tohoto zdroje pro uživatele s tvrzením s tímto jménem
|
||||
auths.oauth2_required_claim_value=Požadovaná hodnota tvrzení
|
||||
auths.oauth2_required_claim_value_helper=Nastavte tuto hodnotu pro omezení přihlášení z tohoto zdroje pro uživatele s tvrzením s tímto jménem a hodnotou
|
||||
auths.oauth2_group_claim_name=Název tvrzení poskytující názvy skupin pro tento zdroj. (nepovinné)
|
||||
auths.oauth2_admin_group=Hodnota tvrzení pro skupinu uživatelů administrátorů. (Volitelné - vyžaduje název tvrzení výše)
|
||||
auths.oauth2_restricted_group=Hodnota tvrzení pro skupinu omezených uživatelů. (Volitelné - vyžaduje název tvrzení výše)
|
||||
auths.enable_auto_register=Povolit zaregistrování se
|
||||
auths.sspi_auto_create_users=Automaticky vytvářet uživatele
|
||||
auths.sspi_auto_create_users_helper=Povolit SSPI autentizační metodě automaticky vytvářet nové účty pro uživatele, kteří se poprvé přihlásili
|
||||
|
@ -2658,13 +2806,19 @@ config.queue_length=Délka fronty
|
|||
config.deliver_timeout=Časový limit doručení
|
||||
config.skip_tls_verify=Přeskočit verifikaci TLS
|
||||
|
||||
config.mailer_config=Nastavení odesílání e-mailů
|
||||
config.mailer_enabled=Zapnutý
|
||||
config.mailer_enable_helo=Povolit HELO
|
||||
config.mailer_name=Název
|
||||
config.mailer_protocol=Protokol
|
||||
config.mailer_smtp_addr=Adresa SMTP
|
||||
config.mailer_smtp_port=Port SMTP
|
||||
config.mailer_user=Uživatel
|
||||
config.mailer_use_sendmail=Použít Sendmail
|
||||
config.mailer_sendmail_path=Cesta k Sendmail
|
||||
config.mailer_sendmail_args=Dodatečné argumenty pro Sendmail
|
||||
config.mailer_sendmail_timeout=Časový limit Sandmail
|
||||
config.mailer_use_dummy=Fiktivní
|
||||
config.test_email_placeholder=E-mail (např.: test@example.com)
|
||||
config.send_test_mail=Odeslat zkušební e-mail
|
||||
config.test_mail_failed=Odeslání testovacího e-mailu na „%s“ selhalo: %v
|
||||
|
@ -2724,6 +2878,8 @@ monitor.next=Příští čas spuštění
|
|||
monitor.previous=Předešlý čas spuštění
|
||||
monitor.execute_times=Vykonání
|
||||
monitor.process=Spuštěné procesy
|
||||
monitor.stacktrace=Výpisy zásobníku
|
||||
monitor.goroutines=%d Go-rutiny
|
||||
monitor.desc=Popis
|
||||
monitor.start=Čas zahájení
|
||||
monitor.execute_time=Doba provádění
|
||||
|
@ -2731,6 +2887,7 @@ monitor.last_execution_result=Výsledek
|
|||
monitor.process.cancel=Zrušit proces
|
||||
monitor.process.cancel_desc=Zrušení procesu může způsobit ztrátu dat
|
||||
monitor.process.cancel_notices=Zrušit: <strong>%s</strong>?
|
||||
monitor.process.children=Potomek
|
||||
monitor.queues=Fronty
|
||||
monitor.queue=Fronta: %s
|
||||
monitor.queue.name=Název
|
||||
|
@ -2738,6 +2895,7 @@ monitor.queue.type=Typ
|
|||
monitor.queue.exemplar=Typ vzoru
|
||||
monitor.queue.numberworkers=Počet workerů
|
||||
monitor.queue.maxnumberworkers=Maximální počet workerů
|
||||
monitor.queue.numberinqueue=Číslo ve frontě
|
||||
monitor.queue.review=Konfigurace posouzení
|
||||
monitor.queue.review_add=Posoudit/přidat workery
|
||||
monitor.queue.configuration=Výchozí konfigurace
|
||||
|
@ -2745,6 +2903,7 @@ monitor.queue.nopool.title=Žádný fond workerů
|
|||
monitor.queue.nopool.desc=Tato fronta obaluje jiné fronty ale sama o sobě nemá fond workerů.
|
||||
monitor.queue.wrapped.desc=Zabalená fronta zabalí pomalou startující frontu ukládáním požadavků do vyrovnávací paměti. Nemá vlastní fond workerů.
|
||||
monitor.queue.persistable-channel.desc=Trvalý kanál obaluje dvě fronty, frontu kanálu, která má vlastní fond workerů a vyrovnávací frontu pro přetrvávající požadavky z předchozích vypnutí. Nemá sám o sobě svůj fond workerů.
|
||||
monitor.queue.flush=Vyprázdnit worker
|
||||
monitor.queue.pool.timeout=Časový limit
|
||||
monitor.queue.pool.addworkers.title=Přidat workery
|
||||
monitor.queue.pool.addworkers.submit=Přidat workery
|
||||
|
@ -2819,18 +2978,23 @@ comment_issue=`okomentoval/a problém <a href="%[1]s">%[3]s#%[2]s</a>`
|
|||
comment_pull=`okomentoval/a požadavek na natažení <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
merge_pull_request=`sloučil/a požadavek na natažení <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
transfer_repo=předal/a repozitář <code>%s</code> uživateli/organizaci <a href="%s">%s</a>
|
||||
delete_tag=smazána značka %[2]s z <a href="%[1]s">%[3]s</a>
|
||||
push_tag=nahrál/a značku <a href="%[2]s">%[3]s</a> do <a href="%[1]s">%[4]s</a>
|
||||
delete_tag=smazal/a značku %[2]s z <a href="%[1]s">%[3]s</a>
|
||||
delete_branch=smazal/a větev %[2]s z <a href="%[1]s">%[3]s</a>
|
||||
compare_branch=Porovnat
|
||||
compare_commits=Porovnat %d revizí
|
||||
compare_commits_general=Porovnat revize
|
||||
mirror_sync_push=synchronizoval/a commity do <a href="%[2]s">%[3]s</a> v <a href="%[1]s">%[4]s</a> ze zrcadla
|
||||
mirror_sync_create=synchronizoval/a novou referenci <a href="%[2]s">%[3]s</a> do <a href="%[1]s">%[4]s</a> ze zrcadla
|
||||
mirror_sync_delete=synchronizoval/a a smazal/a referenci <code>%[2]s</code> v <a href="%[1]s">%[3]s</a> ze zrcadla
|
||||
approve_pull_request=`schválil/a <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
reject_pull_request=`navrhl/a změny pro <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
publish_release=`vydal/a <a href="%[2]s"> "%[4]s" </a> v <a href="%[1]s">%[3]s</a>`
|
||||
review_dismissed=`zamítl/a posouzení z <b>%[4]s</b> pro <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
review_dismissed_reason=Důvod:
|
||||
create_branch=vytvořil/a větev <a href="%[2]s">%[3]s</a> v <a href="%[1]s">%[4]s</a>
|
||||
starred_repo=si oblíbil/a <a href="%[1]s">%[2]s</a>
|
||||
watched_repo=začal/a sledovat <a href="%[1]s">%[2]s</a>
|
||||
|
||||
[tool]
|
||||
ago=před %s
|
||||
|
@ -2891,9 +3055,15 @@ error.unit_not_allowed=Nejste oprávněni přistupovat k této části repozitá
|
|||
title=Balíčky
|
||||
desc=Správa balíčků repozitáře.
|
||||
empty=Zatím nejsou žádné balíčky.
|
||||
empty.documentation=Další informace o registru balíčků naleznete v <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/overview">dokumentaci</a>.
|
||||
empty.repo=Nahráli jste balíček, ale nezobrazil se zde? Přejděte na <a href="%[1]s">nastavení balíčku</a> a propojte jej s tímto repozitářem.
|
||||
filter.type=Typ
|
||||
filter.type.all=Vše
|
||||
filter.no_result=Váš filtr nepřinesl žádné výsledky.
|
||||
filter.container.tagged=Označeno
|
||||
filter.container.untagged=Neoznačeno
|
||||
published_by=Zveřejněno %[1]s od <a href="%[2]s">%[3]s</a>
|
||||
published_by_in=Zveřejněno %[1]s od <a href="%[2]s">%[3]s</a> v <a href="%[4]s"><strong>%[5]s</strong></a>
|
||||
installation=Instalace
|
||||
about=O tomto balíčku
|
||||
requirements=Požadavky
|
||||
|
@ -2903,25 +3073,38 @@ details=Podrobnosti
|
|||
details.author=Autor
|
||||
details.project_site=Stránka projektu
|
||||
details.license=Licence
|
||||
assets=Prostředky
|
||||
versions=Verze
|
||||
versions.on=
|
||||
versions.view_all=Zobrazit všechny
|
||||
dependency.id=ID
|
||||
dependency.version=Verze
|
||||
composer.registry=Nastavit tento registr v souboru <code>~/.composer/config.json</code>:
|
||||
composer.install=Pro instalaci balíčku pomocí Compposer spusťte následující příkaz:
|
||||
composer.documentation=Další informace o registru Composer naleznete v <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/composer/">dokumentaci</a>.
|
||||
composer.dependencies=Závislosti
|
||||
composer.dependencies.development=Vývojové závislosti
|
||||
conan.details.repository=Repozitář
|
||||
conan.registry=Nastavte tento registr z příkazového řádku:
|
||||
conan.install=Pro instalaci balíčku pomocí Conan spusťte následující příkaz:
|
||||
conan.documentation=Další informace o registru Conan naleznete v <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conan/">dokumentaci</a>.
|
||||
container.details.type=Typ obrazu
|
||||
container.details.platform=Platforma
|
||||
container.details.repository_site=Stránka repositáře
|
||||
container.details.documentation_site=Stránka dokumentace
|
||||
container.pull=Stáhněte obraz z příkazové řádky:
|
||||
container.digest=Výběr:
|
||||
container.documentation=Další informace o registru Container naleznete v <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/container/">dokumentaci</a>.
|
||||
container.multi_arch=OS/architektura
|
||||
container.layers=Vrstvy obrazů
|
||||
container.labels=Štítky
|
||||
container.labels.key=Klíč
|
||||
container.labels.value=Hodnota
|
||||
generic.download=Stáhnout balíček z příkazové řádky:
|
||||
generic.documentation=Další informace o obecném registru naleznete v <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/generic">dokumentaci</a>.
|
||||
helm.registry=Nastavte tento registr z příkazového řádku:
|
||||
helm.install=Pro instalaci balíčku spusťte následující příkaz:
|
||||
helm.documentation=Další informace o Helm registru naleznete v <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/helm/">dokumentaci</a>.
|
||||
maven.registry=Nastavte tento registr ve vašem projektu <code>pom.xml</code> souboru:
|
||||
maven.install=Pro použití balíčku uveďte následující v bloku <code>dependencies</code> v souboru <code>pom.xml</code>:
|
||||
maven.install2=Spustit pomocí příkazové řádky:
|
||||
|
@ -2937,8 +3120,13 @@ npm.install2=nebo ho přidejte do souboru package.json:
|
|||
npm.documentation=Další informace o npm registru naleznete v <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/npm/">dokumentaci</a>.
|
||||
npm.dependencies=Závislosti
|
||||
npm.dependencies.development=Vývojové závislosti
|
||||
npm.dependencies.peer=Vzájemné závislosti
|
||||
npm.dependencies.optional=Volitelné závislosti
|
||||
npm.details.tag=Značka
|
||||
pub.install=Chcete-li nainstalovat balíček pomocí Dart, spusťte následující příkaz:
|
||||
pub.documentation=Další informace o registru Pub naleznete v <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/pub/">dokumentaci</a>.
|
||||
pub.details.repository_site=Stránka repositáře
|
||||
pub.details.documentation_site=Stránka dokumentace
|
||||
pypi.requires=Vyžaduje Python
|
||||
pypi.install=Pro instalaci balíčku pomocí pip spusťte následující příkaz:
|
||||
pypi.documentation=Další informace o registru PyPI naleznete v <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/pypi/">dokumentaci</a>.
|
||||
|
@ -2949,6 +3137,8 @@ rubygems.dependencies.development=Vývojové závislosti
|
|||
rubygems.required.ruby=Vyžaduje verzi Ruby
|
||||
rubygems.required.rubygems=Vyžaduje verzi RubyGem
|
||||
rubygems.documentation=Další informace o registru RubyGems naleznete v <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/rubygems/">dokumentaci</a>.
|
||||
vagrant.install=Pro přidání Vagrant box spusťte následující příkaz:
|
||||
vagrant.documentation=Další informace o registru Vagrant naleznete v <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/vagrant/">dokumentaci</a>.
|
||||
settings.link=Propojit tento balíček s repozitářem
|
||||
settings.link.description=Pokud propojíte balíček s repozitářem, je tento balíček uveden v seznamu balíčků repozitáře.
|
||||
settings.link.select=Vybrat repozitář
|
||||
|
|
|
@ -1899,6 +1899,7 @@ settings.confirm_delete = Delete Repository
|
|||
settings.add_collaborator = Add Collaborator
|
||||
settings.add_collaborator_success = The collaborator has been added.
|
||||
settings.add_collaborator_inactive_user = Can not add an inactive user as a collaborator.
|
||||
settings.add_collaborator_owner = Can not add an owner as a collaborator.
|
||||
settings.add_collaborator_duplicate = The collaborator is already added to this repository.
|
||||
settings.delete_collaborator = Remove
|
||||
settings.collaborator_deletion = Remove Collaborator
|
||||
|
@ -3034,6 +3035,9 @@ pin = Pin notification
|
|||
mark_as_read = Mark as read
|
||||
mark_as_unread = Mark as unread
|
||||
mark_all_as_read = Mark all as read
|
||||
subscriptions = Subscriptions
|
||||
watching = Watching
|
||||
no_subscriptions = No subscriptions
|
||||
|
||||
[gpg]
|
||||
default_key=Signed with default key
|
||||
|
@ -3093,6 +3097,7 @@ container.details.platform = Platform
|
|||
container.details.repository_site = Repository Site
|
||||
container.details.documentation_site = Documentation Site
|
||||
container.pull = Pull the image from the command line:
|
||||
container.digest = Digest:
|
||||
container.documentation = For more information on the Container registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/container/">the documentation</a>.
|
||||
container.multi_arch = OS / Arch
|
||||
container.layers = Image Layers
|
||||
|
|
|
@ -1231,6 +1231,8 @@ issues.new.add_reviewer_title=レビュー依頼
|
|||
issues.choose.get_started=始める
|
||||
issues.choose.blank=デフォルト
|
||||
issues.choose.blank_about=デフォルトのテンプレートからイシューを作成。
|
||||
issues.choose.ignore_invalid_templates=無効なテンプレートが無視されました
|
||||
issues.choose.invalid_templates=無効なテンプレートが%v 件見つかりました
|
||||
issues.no_ref=ブランチ/タグ指定なし
|
||||
issues.create=イシューを作成
|
||||
issues.new_label=新しいラベル
|
||||
|
@ -1896,6 +1898,7 @@ settings.confirm_delete=リポジトリを削除
|
|||
settings.add_collaborator=共同作業者を追加
|
||||
settings.add_collaborator_success=共同作業者を追加しました。
|
||||
settings.add_collaborator_inactive_user=アクティベートされていないユーザーを共同作業者として追加することはできません。
|
||||
settings.add_collaborator_owner=共同作業者としてオーナーを追加することはできません。
|
||||
settings.add_collaborator_duplicate=共同作業者として既にこのリポジトリに追加されています。
|
||||
settings.delete_collaborator=削除
|
||||
settings.collaborator_deletion=共同作業者の削除
|
||||
|
@ -1954,6 +1957,8 @@ settings.event_delete=削除
|
|||
settings.event_delete_desc=ブランチやタグが削除されたとき。
|
||||
settings.event_fork=フォーク
|
||||
settings.event_fork_desc=リポジトリがフォークされたとき。
|
||||
settings.event_wiki=Wiki
|
||||
settings.event_wiki_desc=Wikiページが作成・名前変更・編集・削除されたとき。
|
||||
settings.event_release=リリース
|
||||
settings.event_release_desc=リポジトリでリリースが作成・更新・削除されたとき。
|
||||
settings.event_push=プッシュ
|
||||
|
@ -3029,6 +3034,9 @@ pin=通知をピン留め
|
|||
mark_as_read=既読にする
|
||||
mark_as_unread=未読にする
|
||||
mark_all_as_read=すべて既読にする
|
||||
subscriptions=購読
|
||||
watching=ウォッチ中
|
||||
no_subscriptions=購読しているものはありません
|
||||
|
||||
[gpg]
|
||||
default_key=デフォルト鍵で署名
|
||||
|
@ -3088,6 +3096,7 @@ container.details.platform=プラットフォーム
|
|||
container.details.repository_site=リポジトリサイト
|
||||
container.details.documentation_site=ドキュメントサイト
|
||||
container.pull=コマンドラインでイメージを取得します:
|
||||
container.digest=ダイジェスト:
|
||||
container.documentation=Container レジストリの詳細については、 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/container/">ドキュメント</a> を参照してください。
|
||||
container.multi_arch=OS / アーキテクチャ
|
||||
container.layers=イメージレイヤー
|
||||
|
|
|
@ -277,6 +277,7 @@ org_no_results=Nenhuma organização correspondente foi encontrada.
|
|||
code_no_results=Nenhum código-fonte correspondente ao seu termo de pesquisa foi encontrado.
|
||||
code_search_results=Resultados da pesquisa por: '%s'
|
||||
code_last_indexed_at=Última indexação %s
|
||||
relevant_repositories_tooltip=Repositórios que são forks ou que não possuem tópico, nem ícone e nem descrição estão ocultos.
|
||||
relevant_repositories=Apenas repositórios relevantes estão sendo mostrados, <a href="%s">mostrar resultados não filtrados</a>.
|
||||
|
||||
|
||||
|
@ -1038,6 +1039,12 @@ file_view_raw=Ver original
|
|||
file_permalink=Link permanente
|
||||
file_too_large=O arquivo é muito grande para ser mostrado.
|
||||
invisible_runes_header=`Este arquivo contém caracteres Unicode invisíveis!`
|
||||
invisible_runes_description=`Este arquivo contém caracteres Unicode invisíveis que podem ser processados de forma diferente do que aparece abaixo. Se seu caso de uso for intencional e legítimo, você pode ignorar com segurança esse aviso. Use o botão Escapar para revelar caracteres ocultos.`
|
||||
ambiguous_runes_header=`Esse arquivo contém caracteres Unicode ambíguos!`
|
||||
ambiguous_runes_description=`Este arquivo contém caracteres ambíguos Unicode que podem ser confundidos com outros no seu idioma atual. Se o seu caso de uso for intencional e legítimo, você pode ignorar com segurança este aviso. Use o botão Escapar para destacar esses caracteres.`
|
||||
invisible_runes_line=`Esta linha tem caracteres unicode invisíveis`
|
||||
ambiguous_runes_line=`Esta linha tem caracteres unicode ambíguos`
|
||||
ambiguous_character=`%[1]c [U+%04[1]X] é confundível com o %[2]c [U+%04[2]X]`
|
||||
|
||||
escape_control_characters=Escapar
|
||||
unescape_control_characters=Desescapar
|
||||
|
@ -1225,6 +1232,7 @@ issues.choose.get_started=Primeiros passos
|
|||
issues.choose.blank=Padrão
|
||||
issues.choose.blank_about=Criar uma issue a partir do modelo padrão.
|
||||
issues.choose.ignore_invalid_templates=Modelos inválidos foram ignorados
|
||||
issues.choose.invalid_templates=%v modelo(s) inválido(s) encontrado(s)
|
||||
issues.no_ref=Nenhum branch/tag especificado
|
||||
issues.create=Criar issue
|
||||
issues.new_label=Nova etiqueta
|
||||
|
@ -1533,6 +1541,7 @@ pulls.remove_prefix=Remover o prefixo <strong>%s</strong>
|
|||
pulls.data_broken=Este pull request está quebrado devido a falta de informação do fork.
|
||||
pulls.files_conflicted=Este pull request tem alterações conflitantes com o branch de destino.
|
||||
pulls.is_checking=Verificação de conflitos do merge está em andamento. Tente novamente em alguns momentos.
|
||||
pulls.is_ancestor=Este branch já está incluído no branch de destino. Não há nada para mesclar.
|
||||
pulls.is_empty=As alterações neste branch já estão na branch de destino. Este será um commit vazio.
|
||||
pulls.required_status_check_failed=Algumas verificações necessárias não foram bem sucedidas.
|
||||
pulls.required_status_check_missing=Estão faltando algumas verificações necessárias.
|
||||
|
@ -1603,11 +1612,13 @@ pulls.merge_instruction_step2_desc=Faça merge das alterações e atualize no Gi
|
|||
pulls.auto_merge_button_when_succeed=(Quando a verificação for bem-sucedida)
|
||||
pulls.auto_merge_when_succeed=Mesclar automaticamente quando todas as verificações forem bem sucedidas
|
||||
pulls.auto_merge_newly_scheduled=O merge do pull request foi agendado para quando todas as verificações forem bem-sucedidas.
|
||||
pulls.auto_merge_has_pending_schedule=%[1]s agendou este pull request para merge automático quando todas as verificações tiverem sucesso %[2]s.
|
||||
|
||||
pulls.auto_merge_cancel_schedule=Cancelar merge automático
|
||||
pulls.auto_merge_not_scheduled=Este pull request não está programado para ser automaticamente mesclado.
|
||||
pulls.auto_merge_canceled_schedule=O merge automático foi cancelado para este pull request.
|
||||
|
||||
pulls.auto_merge_newly_scheduled_comment=`agendou este pull request para merge automático quando todas as verificações tiverem sucesso %[1]s`
|
||||
pulls.auto_merge_canceled_schedule_comment=`cancelou o merge automático deste pull request quando todos as verificações tiverem sucesso %[1]s`
|
||||
|
||||
pulls.delete.title=Excluir este pull request?
|
||||
|
@ -1805,6 +1816,7 @@ settings.tracker_issue_style.numeric=Numérico
|
|||
settings.tracker_issue_style.alphanumeric=Alfanumérico
|
||||
settings.tracker_issue_style.regexp=Expressão Regular
|
||||
settings.tracker_issue_style.regexp_pattern=Padrão de expressão regular
|
||||
settings.tracker_issue_style.regexp_pattern_desc=O primeiro grupo capturado será usado no lugar de <code>{index}</code>.
|
||||
settings.tracker_url_format_desc=Use os espaços reservados <code>{user}</code>, <code>{repo}</code> e <code>{index}</code> para o nome de usuário, nome do repositório e o índice de problemas.
|
||||
settings.enable_timetracker=Habilitar Cronômetro
|
||||
settings.allow_only_contributors_to_track_time=Permitir que apenas os colaboradores acompanhem o contador de tempo
|
||||
|
@ -1886,6 +1898,7 @@ settings.confirm_delete=Excluir repositório
|
|||
settings.add_collaborator=Adicionar colaborador
|
||||
settings.add_collaborator_success=O colaborador foi adicionado.
|
||||
settings.add_collaborator_inactive_user=Não é possível adicionar um usuário inativo como colaborador.
|
||||
settings.add_collaborator_owner=Não é possível adicionar um proprietário como um colaborador.
|
||||
settings.add_collaborator_duplicate=O colaborador já está adicionado a este repositório.
|
||||
settings.delete_collaborator=Remover
|
||||
settings.collaborator_deletion=Remover colaborador
|
||||
|
@ -1944,6 +1957,8 @@ settings.event_delete=Excluir
|
|||
settings.event_delete_desc=Branch ou tag deletado.
|
||||
settings.event_fork=Fork
|
||||
settings.event_fork_desc=Feito fork do repositório.
|
||||
settings.event_wiki=Wiki
|
||||
settings.event_wiki_desc=Página da wiki criada, renomeada, editada ou excluída.
|
||||
settings.event_release=Versão
|
||||
settings.event_release_desc=Versão publicada, atualizada ou excluída em um repositório.
|
||||
settings.event_push=Push
|
||||
|
@ -2423,6 +2438,7 @@ dashboard.new_version_hint=Gitea %s está disponível, você está executando %s
|
|||
dashboard.statistic=Resumo
|
||||
dashboard.operations=Operações de manutenção
|
||||
dashboard.system_status=Status do sistema
|
||||
dashboard.statistic_info=O banco de dados do Gitea contém <b>%d</b> usuários, <b>%d</b> organizações, <b>%d</b> chaves públicas, <b>%d</b> repositórios, <b>%d</b> observadores, <b>%d</b> favoritos, ~<b>%d</b> ações, <b>%d</b> acessos, <b>%d</b> issues, <b>%d</b> comentários, <b>%d</b> contas sociais, <b>%d</b> seguidores, <b>%d</b> espelhos, <b>%d</b> versões, <b>%d</b> fontes de autenticação, <b>%d</b> webhooks, <b>%d</b> marcos, <b>%d</b> etiquetas, <b>%d</b> tarefas hook, <b>%d</b> equipes, <b>%d</b> tarefas de atualização, <b>%d</b> anexos.
|
||||
dashboard.operation_name=Nome da operação
|
||||
dashboard.operation_switch=Trocar
|
||||
dashboard.operation_run=Executar
|
||||
|
@ -2631,6 +2647,8 @@ auths.use_paged_search=Use a pesquisa paginada
|
|||
auths.search_page_size=Tamanho da página
|
||||
auths.filter=Filtro de usuário
|
||||
auths.admin_filter=Filtro de administrador
|
||||
auths.restricted_filter=Filtro de restrição
|
||||
auths.restricted_filter_helper=Deixe em branco para não definir nenhum usuário como restrito. Use um asterisco ('*') para definir todos os usuários que não correspondem ao Filtro de administrador como restritos.
|
||||
auths.group_attribute_list_users=Atributo do Grupo que Contém a Lista de Usuários
|
||||
auths.enable_ldap_groups=Habilitar grupos do LDAP
|
||||
auths.ms_ad_sa=Atributos de pesquisa do MS AD
|
||||
|
@ -2871,6 +2889,7 @@ monitor.queue.nopool.title=Nenhum conjunto de executores
|
|||
monitor.queue.nopool.desc=Essa fila agrupa outras filas e não possui um conjunto de executores.
|
||||
monitor.queue.wrapped.desc=Uma fila agrupada envolve uma fila inicial lenta, armazenando as solicitações da fila em um canal. Ela não possui um conjunto de executores em si.
|
||||
monitor.queue.persistable-channel.desc=Um canal persistente envolve duas filas, uma fila de canais que tem seu próprio conjunto de executores e uma fila de nível para solicitações persistentes de encerramentos anteriores. Ela não tem um conjunto de executores em si.
|
||||
monitor.queue.flush=Liberar executor
|
||||
monitor.queue.pool.timeout=Tempo de espera
|
||||
monitor.queue.pool.addworkers.title=Adicionar executores
|
||||
monitor.queue.pool.addworkers.submit=Adicionar executores
|
||||
|
@ -3023,6 +3042,7 @@ title=Pacotes
|
|||
desc=Gerenciar pacotes do repositório.
|
||||
empty=Não há pacotes ainda.
|
||||
empty.documentation=Para obter mais informações sobre o registro de pacote, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/overview">a documentação</a>.
|
||||
empty.repo=Você enviou um pacote, mas ele não está aqui? Vá para <a href="%[1]s">configurações do pacote</a> e vincule-o a este repositório.
|
||||
filter.type=Tipo
|
||||
filter.type.all=Todos
|
||||
filter.no_result=Seu filtro não produziu resultados.
|
||||
|
@ -3059,6 +3079,7 @@ container.details.platform=Plataforma
|
|||
container.details.repository_site=Site do Repositório
|
||||
container.details.documentation_site=Site da Documentação
|
||||
container.pull=Puxe a imagem pela linha de comando:
|
||||
container.digest=Digest:
|
||||
container.documentation=Para obter mais informações sobre o registro de Container, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/container/">a documentação</a>.
|
||||
container.multi_arch=S.O. / Arquitetura
|
||||
container.layers=Camadas da Imagem
|
||||
|
@ -3088,6 +3109,8 @@ npm.dependencies.development=Dependências de Desenvolvimento
|
|||
npm.dependencies.peer=Dependências Peer
|
||||
npm.dependencies.optional=Dependências Opcionais
|
||||
npm.details.tag=Tag
|
||||
pub.install=Para instalar o pacote usando Dart, execute o seguinte comando:
|
||||
pub.documentation=Para obter mais informações sobre o registro Pub, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/pub/">a documentação</a>.
|
||||
pub.details.repository_site=Site do Repositório
|
||||
pub.details.documentation_site=Site da Documentação
|
||||
pypi.requires=Requer Python
|
||||
|
@ -3100,6 +3123,8 @@ rubygems.dependencies.development=Dependências de Desenvolvimento
|
|||
rubygems.required.ruby=Requer o Ruby versão
|
||||
rubygems.required.rubygems=Requer o RubyGem versão
|
||||
rubygems.documentation=Para obter mais informações sobre o registro do RubyGems, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/rubygems/">a documentação</a>.
|
||||
vagrant.install=Para adicionar uma Vagrant box, execute o seguinte comando:
|
||||
vagrant.documentation=Para obter mais informações sobre o registro do Vagrant, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/vagrant/">a documentação</a>.
|
||||
settings.link=Vincular este pacote a um repositório
|
||||
settings.link.description=Se você vincular um pacote a um repositório, o pacote será listado na lista de pacotes do repositório.
|
||||
settings.link.select=Selecionar Repositório
|
||||
|
|
|
@ -1898,6 +1898,7 @@ settings.confirm_delete=Eliminar repositório
|
|||
settings.add_collaborator=Adicionar colaborador
|
||||
settings.add_collaborator_success=O colaborador foi adicionado.
|
||||
settings.add_collaborator_inactive_user=Não é possível adicionar um utilizador desabilitado como colaborador.
|
||||
settings.add_collaborator_owner=Não é possível adicionar um proprietário como um colaborador.
|
||||
settings.add_collaborator_duplicate=O colaborador já tinha sido adicionado a este repositório.
|
||||
settings.delete_collaborator=Remover
|
||||
settings.collaborator_deletion=Remover colaborador
|
||||
|
@ -3033,6 +3034,9 @@ pin=Fixar notificação
|
|||
mark_as_read=Marcar como lida
|
||||
mark_as_unread=Marcar como não lida
|
||||
mark_all_as_read=Marcar todas como lidas
|
||||
subscriptions=Subscrições
|
||||
watching=Vigiando
|
||||
no_subscriptions=Sem subscrições
|
||||
|
||||
[gpg]
|
||||
default_key=Assinado com a chave padrão
|
||||
|
@ -3092,6 +3096,7 @@ container.details.platform=Plataforma
|
|||
container.details.repository_site=Página web do repositório
|
||||
container.details.documentation_site=Página web da documentação
|
||||
container.pull=Puxar a imagem usando a linha de comandos:
|
||||
container.digest=Resumo:
|
||||
container.documentation=Para obter mais informações sobre o registo do Container, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/container/">a documentação</a>.
|
||||
container.multi_arch=S.O. / Arquit.
|
||||
container.layers=Camadas de imagem
|
||||
|
|
1319
options/locale/locale_sk-SK.ini
Normal file
1319
options/locale/locale_sk-SK.ini
Normal file
File diff suppressed because it is too large
Load diff
|
@ -3092,6 +3092,7 @@ container.details.platform=Platform
|
|||
container.details.repository_site=Depo Sitesi
|
||||
container.details.documentation_site=Belge Sitesi
|
||||
container.pull=Görüntüyü komut satırını kullanarak çekin:
|
||||
container.digest=Özet:
|
||||
container.documentation=Taşıyıcı kütüğü hakkında daha fazla bilgi için, <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/container/">belgeye</a> bakabilirsiniz.
|
||||
container.multi_arch=İşletim Sistemi / Mimari
|
||||
container.layers=Görüntü Katmanları
|
||||
|
|
2883
package-lock.json
generated
2883
package-lock.json
generated
File diff suppressed because it is too large
Load diff
21
package.json
21
package.json
|
@ -9,17 +9,18 @@
|
|||
"dependencies": {
|
||||
"@claviska/jquery-minicolors": "2.3.6",
|
||||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-2",
|
||||
"@primer/octicons": "17.4.1",
|
||||
"@primer/octicons": "17.5.0",
|
||||
"add-asset-webpack-plugin": "2.0.1",
|
||||
"css-loader": "6.7.1",
|
||||
"dropzone": "6.0.0-beta.2",
|
||||
"easymde": "2.17.0",
|
||||
"esbuild-loader": "2.19.0",
|
||||
"esbuild-loader": "2.20.0",
|
||||
"escape-goat": "4.0.0",
|
||||
"fast-glob": "3.2.11",
|
||||
"fast-glob": "3.2.12",
|
||||
"font-awesome": "4.7.0",
|
||||
"jquery": "3.6.0",
|
||||
"jquery": "3.6.1",
|
||||
"jquery.are-you-sure": "1.9.0",
|
||||
"katex": "0.16.2",
|
||||
"less": "4.1.3",
|
||||
"less-loader": "11.0.0",
|
||||
"license-checker-webpack-plugin": "0.2.1",
|
||||
|
@ -46,23 +47,23 @@
|
|||
"wrap-ansi": "8.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.25.1",
|
||||
"@playwright/test": "1.25.2",
|
||||
"@stoplight/spectral-cli": "6.5.1",
|
||||
"eslint": "8.22.0",
|
||||
"eslint": "8.23.0",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-jquery": "1.5.1",
|
||||
"eslint-plugin-sonarjs": "0.15.0",
|
||||
"eslint-plugin-unicorn": "43.0.2",
|
||||
"eslint-plugin-vue": "9.4.0",
|
||||
"jest": "28.1.3",
|
||||
"jest-environment-jsdom": "28.1.3",
|
||||
"jest-extended": "3.0.2",
|
||||
"jest": "29.0.3",
|
||||
"jest-environment-jsdom": "29.0.3",
|
||||
"jest-extended": "3.1.0",
|
||||
"markdownlint-cli": "0.32.2",
|
||||
"postcss-less": "6.0.0",
|
||||
"stylelint": "14.11.0",
|
||||
"stylelint-config-standard": "28.0.0",
|
||||
"svgo": "2.8.0",
|
||||
"updates": "13.1.4"
|
||||
"updates": "13.1.5"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults",
|
||||
|
|
1
public/img/svg/octicon-accessibility-inset.svg
Normal file
1
public/img/svg/octicon-accessibility-inset.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg viewBox="0 0 16 16" class="svg octicon-accessibility-inset" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0zm2 4a2 2 0 0 1-1.05 1.76c.115.069.222.15.32.24h2.98a.75.75 0 0 1 0 1.5H9.888l.608 5.67a.75.75 0 1 1-1.492.16L8.754 11H7.246l-.25 2.33a.75.75 0 1 1-1.49-.16l.607-5.67H3.75a.75.75 0 0 1 0-1.5h2.98a1.87 1.87 0 0 1 .32-.24A2 2 0 1 1 10 4z"/></svg>
|
After Width: | Height: | Size: 413 B |
1
public/img/svg/octicon-shield-slash.svg
Normal file
1
public/img/svg/octicon-shield-slash.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg viewBox="0 0 16 16" class="svg octicon-shield-slash" width="16" height="16" aria-hidden="true"><path d="M8.533.133a1.75 1.75 0 0 0-1.066 0l-2.091.67a.75.75 0 0 0 .457 1.428l2.09-.67a.25.25 0 0 1 .153 0l5.25 1.68a.25.25 0 0 1 .174.239V7c0 .233-.008.464-.025.694a.75.75 0 1 0 1.495.112c.02-.27.03-.538.03-.806V3.48a1.75 1.75 0 0 0-1.217-1.667L8.533.133z"/><path fill-rule="evenodd" d="m1 2.857-.69-.5a.75.75 0 1 1 .88-1.214l14.5 10.5a.75.75 0 1 1-.88 1.214l-1.282-.928c-.995 1.397-2.553 2.624-4.864 3.608-.425.181-.905.18-1.329 0-2.447-1.042-4.049-2.356-5.032-3.855C1.32 10.182 1 8.566 1 7V2.857zm1.5 1.086V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297.05.02.106.02.153 0 2.127-.905 3.439-1.982 4.237-3.108L2.5 3.943z"/></svg>
|
After Width: | Height: | Size: 747 B |
|
@ -69,7 +69,7 @@ func Routes(ctx gocontext.Context) *web.Route {
|
|||
r.Get("/p2/{vendorname}/{projectname}.json", composer.PackageMetadata)
|
||||
r.Get("/files/{package}/{version}/{filename}", composer.DownloadPackageFile)
|
||||
r.Put("", reqPackageAccess(perm.AccessModeWrite), composer.UploadPackage)
|
||||
})
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
r.Group("/conan", func() {
|
||||
r.Group("/v1", func() {
|
||||
r.Get("/ping", conan.Ping)
|
||||
|
@ -157,7 +157,7 @@ func Routes(ctx gocontext.Context) *web.Route {
|
|||
}, conan.ExtractPathParameters)
|
||||
})
|
||||
})
|
||||
})
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
r.Group("/generic", func() {
|
||||
r.Group("/{packagename}/{packageversion}", func() {
|
||||
r.Delete("", reqPackageAccess(perm.AccessModeWrite), generic.DeletePackage)
|
||||
|
@ -169,33 +169,35 @@ func Routes(ctx gocontext.Context) *web.Route {
|
|||
}, reqPackageAccess(perm.AccessModeWrite))
|
||||
})
|
||||
})
|
||||
})
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
r.Group("/helm", func() {
|
||||
r.Get("/index.yaml", helm.Index)
|
||||
r.Get("/{filename}", helm.DownloadPackageFile)
|
||||
r.Post("/api/charts", reqPackageAccess(perm.AccessModeWrite), helm.UploadPackage)
|
||||
})
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
r.Group("/maven", func() {
|
||||
r.Put("/*", reqPackageAccess(perm.AccessModeWrite), maven.UploadPackageFile)
|
||||
r.Get("/*", maven.DownloadPackageFile)
|
||||
})
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
r.Group("/nuget", func() {
|
||||
r.Get("/index.json", nuget.ServiceIndex)
|
||||
r.Get("/query", nuget.SearchService)
|
||||
r.Group("/registration/{id}", func() {
|
||||
r.Get("/index.json", nuget.RegistrationIndex)
|
||||
r.Get("/{version}", nuget.RegistrationLeaf)
|
||||
})
|
||||
r.Group("/package/{id}", func() {
|
||||
r.Get("/index.json", nuget.EnumeratePackageVersions)
|
||||
r.Get("/{version}/{filename}", nuget.DownloadPackageFile)
|
||||
})
|
||||
r.Get("/index.json", nuget.ServiceIndex) // Needs to be unauthenticated for the NuGet client.
|
||||
r.Group("", func() {
|
||||
r.Put("/", nuget.UploadPackage)
|
||||
r.Put("/symbolpackage", nuget.UploadSymbolPackage)
|
||||
r.Delete("/{id}/{version}", nuget.DeletePackage)
|
||||
}, reqPackageAccess(perm.AccessModeWrite))
|
||||
r.Get("/symbols/{filename}/{guid:[0-9a-f]{32}}FFFFFFFF/{filename2}", nuget.DownloadSymbolFile)
|
||||
r.Get("/query", nuget.SearchService)
|
||||
r.Group("/registration/{id}", func() {
|
||||
r.Get("/index.json", nuget.RegistrationIndex)
|
||||
r.Get("/{version}", nuget.RegistrationLeaf)
|
||||
})
|
||||
r.Group("/package/{id}", func() {
|
||||
r.Get("/index.json", nuget.EnumeratePackageVersions)
|
||||
r.Get("/{version}/{filename}", nuget.DownloadPackageFile)
|
||||
})
|
||||
r.Group("", func() {
|
||||
r.Put("/", nuget.UploadPackage)
|
||||
r.Put("/symbolpackage", nuget.UploadSymbolPackage)
|
||||
r.Delete("/{id}/{version}", nuget.DeletePackage)
|
||||
}, reqPackageAccess(perm.AccessModeWrite))
|
||||
r.Get("/symbols/{filename}/{guid:[0-9a-f]{32}}FFFFFFFF/{filename2}", nuget.DownloadSymbolFile)
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
})
|
||||
r.Group("/npm", func() {
|
||||
r.Group("/@{scope}/{id}", func() {
|
||||
|
@ -236,7 +238,10 @@ func Routes(ctx gocontext.Context) *web.Route {
|
|||
r.Delete("", npm.DeletePackageTag)
|
||||
}, reqPackageAccess(perm.AccessModeWrite))
|
||||
})
|
||||
})
|
||||
r.Group("/-/v1/search", func() {
|
||||
r.Get("", npm.PackageSearch)
|
||||
})
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
r.Group("/pub", func() {
|
||||
r.Group("/api/packages", func() {
|
||||
r.Group("/versions/new", func() {
|
||||
|
@ -250,12 +255,12 @@ func Routes(ctx gocontext.Context) *web.Route {
|
|||
r.Get("/{version}", pub.PackageVersionMetadata)
|
||||
})
|
||||
})
|
||||
})
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
r.Group("/pypi", func() {
|
||||
r.Post("/", reqPackageAccess(perm.AccessModeWrite), pypi.UploadPackageFile)
|
||||
r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile)
|
||||
r.Get("/simple/{id}", pypi.PackageMetadata)
|
||||
})
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
r.Group("/rubygems", func() {
|
||||
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
|
||||
r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest)
|
||||
|
@ -266,7 +271,7 @@ func Routes(ctx gocontext.Context) *web.Route {
|
|||
r.Post("/", rubygems.UploadPackageFile)
|
||||
r.Delete("/yank", rubygems.DeletePackage)
|
||||
}, reqPackageAccess(perm.AccessModeWrite))
|
||||
})
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
r.Group("/vagrant", func() {
|
||||
r.Group("/authenticate", func() {
|
||||
r.Get("", vagrant.CheckAuthenticate)
|
||||
|
@ -279,8 +284,8 @@ func Routes(ctx gocontext.Context) *web.Route {
|
|||
r.Put("", reqPackageAccess(perm.AccessModeWrite), vagrant.UploadPackageFile)
|
||||
})
|
||||
})
|
||||
})
|
||||
}, context_service.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
}, context_service.UserAssignmentWeb(), context.PackageAssignment())
|
||||
|
||||
return r
|
||||
}
|
||||
|
|
|
@ -74,3 +74,38 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createPackageSearchResponse(pds []*packages_model.PackageDescriptor, total int64) *npm_module.PackageSearch {
|
||||
objects := make([]*npm_module.PackageSearchObject, 0, len(pds))
|
||||
for _, pd := range pds {
|
||||
metadata := pd.Metadata.(*npm_module.Metadata)
|
||||
|
||||
scope := metadata.Scope
|
||||
if scope == "" {
|
||||
scope = "unscoped"
|
||||
}
|
||||
|
||||
objects = append(objects, &npm_module.PackageSearchObject{
|
||||
Package: &npm_module.PackageSearchPackage{
|
||||
Scope: scope,
|
||||
Name: metadata.Name,
|
||||
Version: pd.Version.Version,
|
||||
Date: pd.Version.CreatedUnix.AsLocalTime(),
|
||||
Description: metadata.Description,
|
||||
Author: npm_module.User{Name: metadata.Author},
|
||||
Publisher: npm_module.User{Name: pd.Owner.Name},
|
||||
Maintainers: []npm_module.User{}, // npm cli needs this field
|
||||
Keywords: metadata.Keywords,
|
||||
Links: &npm_module.PackageSearchPackageLinks{
|
||||
Registry: pd.FullWebLink(),
|
||||
Homepage: metadata.ProjectURL,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return &npm_module.PackageSearch{
|
||||
Objects: objects,
|
||||
Total: total,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -350,3 +350,35 @@ func setPackageTag(tag string, pv *packages_model.PackageVersion, deleteOnly boo
|
|||
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
func PackageSearch(ctx *context.Context) {
|
||||
pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
|
||||
OwnerID: ctx.Package.Owner.ID,
|
||||
Type: packages_model.TypeNpm,
|
||||
Name: packages_model.SearchValue{
|
||||
ExactMatch: false,
|
||||
Value: ctx.FormTrim("text"),
|
||||
},
|
||||
Paginator: db.NewAbsoluteListOptions(
|
||||
ctx.FormInt("from"),
|
||||
ctx.FormInt("size"),
|
||||
),
|
||||
})
|
||||
if err != nil {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
|
||||
if err != nil {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp := createPackageSearchResponse(
|
||||
pds,
|
||||
total,
|
||||
)
|
||||
|
||||
ctx.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
|
|
@ -1025,6 +1025,7 @@ func Routes(ctx gocontext.Context) *web.Route {
|
|||
m.Get(".{diffType:diff|patch}", repo.DownloadPullDiffOrPatch)
|
||||
m.Post("/update", reqToken(""), repo.UpdatePullRequest)
|
||||
m.Get("/commits", repo.GetPullRequestCommits)
|
||||
m.Get("/files", repo.GetPullRequestFiles)
|
||||
m.Combo("/merge").Get(repo.IsPullRequestMerged).
|
||||
Post(reqToken(""), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest).
|
||||
Delete(reqToken(""), mustNotBeArchived, repo.CancelScheduledAutoMerge)
|
||||
|
|
|
@ -759,13 +759,17 @@ func SearchTeam(ctx *context.APIContext) {
|
|||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
opts := &organization.SearchTeamOptions{
|
||||
UserID: ctx.Doer.ID,
|
||||
Keyword: ctx.FormTrim("q"),
|
||||
OrgID: ctx.Org.Organization.ID,
|
||||
IncludeDesc: ctx.FormString("include_desc") == "" || ctx.FormBool("include_desc"),
|
||||
ListOptions: listOptions,
|
||||
}
|
||||
|
||||
// Only admin is allowd to search for all teams
|
||||
if !ctx.Doer.IsAdmin {
|
||||
opts.UserID = ctx.Doer.ID
|
||||
}
|
||||
|
||||
teams, maxResults, err := organization.SearchTeam(opts)
|
||||
if err != nil {
|
||||
log.Error("SearchTeam failed: %v", err)
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
|
@ -33,6 +34,7 @@ import (
|
|||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||
"code.gitea.io/gitea/services/automerge"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
|
@ -1323,3 +1325,137 @@ func GetPullRequestCommits(ctx *context.APIContext) {
|
|||
|
||||
ctx.JSON(http.StatusOK, &apiCommits)
|
||||
}
|
||||
|
||||
// GetPullRequestFiles gets all changed files associated with a given PR
|
||||
func GetPullRequestFiles(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/pulls/{index}/files repository repoGetPullRequestFiles
|
||||
// ---
|
||||
// summary: Get changed files for a pull request
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: index
|
||||
// in: path
|
||||
// description: index of the pull request to get
|
||||
// type: integer
|
||||
// format: int64
|
||||
// required: true
|
||||
// - name: skip-to
|
||||
// in: query
|
||||
// description: skip to given file
|
||||
// type: string
|
||||
// - name: whitespace
|
||||
// in: query
|
||||
// description: whitespace behavior
|
||||
// type: string
|
||||
// enum: [ignore-all, ignore-change, ignore-eol, show-all]
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: page number of results to return (1-based)
|
||||
// type: integer
|
||||
// - name: limit
|
||||
// in: query
|
||||
// description: page size of results
|
||||
// type: integer
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ChangedFileList"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrPullRequestNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := pr.LoadBaseRepo(); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := pr.LoadHeadRepo(); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
baseGitRepo := ctx.Repo.GitRepo
|
||||
|
||||
var prInfo *git.CompareInfo
|
||||
if pr.HasMerged {
|
||||
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitRefName(), true, false)
|
||||
} else {
|
||||
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName(), true, false)
|
||||
}
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCompareInfo", err)
|
||||
return
|
||||
}
|
||||
|
||||
headCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
|
||||
if err != nil {
|
||||
ctx.ServerError("GetRefCommitID", err)
|
||||
return
|
||||
}
|
||||
|
||||
startCommitID := prInfo.MergeBase
|
||||
endCommitID := headCommitID
|
||||
|
||||
maxLines, maxFiles := setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles
|
||||
|
||||
diff, err := gitdiff.GetDiff(baseGitRepo,
|
||||
&gitdiff.DiffOptions{
|
||||
BeforeCommitID: startCommitID,
|
||||
AfterCommitID: endCommitID,
|
||||
SkipTo: ctx.FormString("skip-to"),
|
||||
MaxLines: maxLines,
|
||||
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
|
||||
MaxFiles: maxFiles,
|
||||
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.FormString("whitespace")),
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetDiff", err)
|
||||
return
|
||||
}
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
totalNumberOfFiles := diff.NumFiles
|
||||
totalNumberOfPages := int(math.Ceil(float64(totalNumberOfFiles) / float64(listOptions.PageSize)))
|
||||
|
||||
start, end := listOptions.GetStartEnd()
|
||||
|
||||
if end > totalNumberOfFiles {
|
||||
end = totalNumberOfFiles
|
||||
}
|
||||
|
||||
apiFiles := make([]*api.ChangedFile, 0, end-start)
|
||||
for i := start; i < end; i++ {
|
||||
apiFiles = append(apiFiles, convert.ToChangedFile(diff.Files[i], pr.HeadRepo, endCommitID))
|
||||
}
|
||||
|
||||
ctx.SetLinkHeader(totalNumberOfFiles, listOptions.PageSize)
|
||||
ctx.SetTotalCountHeader(int64(totalNumberOfFiles))
|
||||
|
||||
ctx.RespHeader().Set("X-Page", strconv.Itoa(listOptions.Page))
|
||||
ctx.RespHeader().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
|
||||
ctx.RespHeader().Set("X-PageCount", strconv.Itoa(totalNumberOfPages))
|
||||
ctx.RespHeader().Set("X-HasMore", strconv.FormatBool(listOptions.Page < totalNumberOfPages))
|
||||
ctx.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-PageCount", "X-HasMore")
|
||||
|
||||
ctx.JSON(http.StatusOK, &apiFiles)
|
||||
}
|
||||
|
|
|
@ -732,8 +732,13 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
|||
var units []repo_model.RepoUnit
|
||||
var deleteUnitTypes []unit_model.Type
|
||||
|
||||
currHasIssues := repo.UnitEnabledCtx(ctx, unit_model.TypeIssues)
|
||||
newHasIssues := currHasIssues
|
||||
if opts.HasIssues != nil {
|
||||
if *opts.HasIssues && opts.ExternalTracker != nil && !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
|
||||
newHasIssues = *opts.HasIssues
|
||||
}
|
||||
if currHasIssues || newHasIssues {
|
||||
if newHasIssues && opts.ExternalTracker != nil && !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
|
||||
// Check that values are valid
|
||||
if !validation.IsValidExternalURL(opts.ExternalTracker.ExternalTrackerURL) {
|
||||
err := fmt.Errorf("External tracker URL not valid")
|
||||
|
@ -756,7 +761,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
|||
},
|
||||
})
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
|
||||
} else if *opts.HasIssues && opts.ExternalTracker == nil && !unit_model.TypeIssues.UnitGlobalDisabled() {
|
||||
} else if newHasIssues && opts.ExternalTracker == nil && !unit_model.TypeIssues.UnitGlobalDisabled() {
|
||||
// Default to built-in tracker
|
||||
var config *repo_model.IssuesConfig
|
||||
|
||||
|
@ -783,7 +788,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
|||
Config: config,
|
||||
})
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
|
||||
} else if !*opts.HasIssues {
|
||||
} else if !newHasIssues {
|
||||
if !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
|
||||
}
|
||||
|
@ -793,8 +798,13 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
|||
}
|
||||
}
|
||||
|
||||
currHasWiki := repo.UnitEnabledCtx(ctx, unit_model.TypeWiki)
|
||||
newHasWiki := currHasWiki
|
||||
if opts.HasWiki != nil {
|
||||
if *opts.HasWiki && opts.ExternalWiki != nil && !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
|
||||
newHasWiki = *opts.HasWiki
|
||||
}
|
||||
if currHasWiki || newHasWiki {
|
||||
if newHasWiki && opts.ExternalWiki != nil && !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
|
||||
// Check that values are valid
|
||||
if !validation.IsValidExternalURL(opts.ExternalWiki.ExternalWikiURL) {
|
||||
err := fmt.Errorf("External wiki URL not valid")
|
||||
|
@ -810,7 +820,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
|||
},
|
||||
})
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
|
||||
} else if *opts.HasWiki && opts.ExternalWiki == nil && !unit_model.TypeWiki.UnitGlobalDisabled() {
|
||||
} else if newHasWiki && opts.ExternalWiki == nil && !unit_model.TypeWiki.UnitGlobalDisabled() {
|
||||
config := &repo_model.UnitConfig{}
|
||||
units = append(units, repo_model.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
|
@ -818,7 +828,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
|||
Config: config,
|
||||
})
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
|
||||
} else if !*opts.HasWiki {
|
||||
} else if !newHasWiki {
|
||||
if !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
|
||||
}
|
||||
|
@ -828,8 +838,13 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
|||
}
|
||||
}
|
||||
|
||||
currHasPullRequests := repo.UnitEnabledCtx(ctx, unit_model.TypePullRequests)
|
||||
newHasPullRequests := currHasPullRequests
|
||||
if opts.HasPullRequests != nil {
|
||||
if *opts.HasPullRequests && !unit_model.TypePullRequests.UnitGlobalDisabled() {
|
||||
newHasPullRequests = *opts.HasPullRequests
|
||||
}
|
||||
if currHasPullRequests || newHasPullRequests {
|
||||
if newHasPullRequests && !unit_model.TypePullRequests.UnitGlobalDisabled() {
|
||||
// We do allow setting individual PR settings through the API, so
|
||||
// we get the config settings and then set them
|
||||
// if those settings were provided in the opts.
|
||||
|
@ -889,7 +904,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
|||
Type: unit_model.TypePullRequests,
|
||||
Config: config,
|
||||
})
|
||||
} else if !*opts.HasPullRequests && !unit_model.TypePullRequests.UnitGlobalDisabled() {
|
||||
} else if !newHasPullRequests && !unit_model.TypePullRequests.UnitGlobalDisabled() {
|
||||
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -254,6 +254,28 @@ type swaggerCommitList struct {
|
|||
Body []api.Commit `json:"body"`
|
||||
}
|
||||
|
||||
// ChangedFileList
|
||||
// swagger:response ChangedFileList
|
||||
type swaggerChangedFileList struct {
|
||||
// The current page
|
||||
Page int `json:"X-Page"`
|
||||
|
||||
// Commits per page
|
||||
PerPage int `json:"X-PerPage"`
|
||||
|
||||
// Total commit count
|
||||
Total int `json:"X-Total"`
|
||||
|
||||
// Total number of pages
|
||||
PageCount int `json:"X-PageCount"`
|
||||
|
||||
// True if there is another page
|
||||
HasMore bool `json:"X-HasMore"`
|
||||
|
||||
// in: body
|
||||
Body []api.ChangedFile `json:"body"`
|
||||
}
|
||||
|
||||
// Note
|
||||
// swagger:response Note
|
||||
type swaggerNote struct {
|
||||
|
|
|
@ -645,7 +645,7 @@ func handleRefreshToken(ctx *context.Context, form forms.AccessTokenForm, server
|
|||
if err != nil {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
|
||||
ErrorDescription: "client is not authorized",
|
||||
ErrorDescription: "unable to parse refresh token",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
@ -688,14 +688,14 @@ func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm, s
|
|||
if !app.ValidateClientSecret([]byte(form.ClientSecret)) {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
|
||||
ErrorDescription: "client is not authorized",
|
||||
ErrorDescription: "invalid client secret",
|
||||
})
|
||||
return
|
||||
}
|
||||
if form.RedirectURI != "" && !app.ContainsRedirectURI(form.RedirectURI) {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
|
||||
ErrorDescription: "client is not authorized",
|
||||
ErrorDescription: "unexpected redirect URI",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
@ -711,7 +711,7 @@ func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm, s
|
|||
if !authorizationCode.ValidateCodeChallenge(form.CodeVerifier) {
|
||||
handleAccessTokenError(ctx, AccessTokenError{
|
||||
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
|
||||
ErrorDescription: "client is not authorized",
|
||||
ErrorDescription: "failed PKCE code challenge",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
|
@ -24,27 +24,27 @@ import (
|
|||
)
|
||||
|
||||
func toBranchLink(act *activities_model.Action) string {
|
||||
return act.GetRepoLink() + "/src/branch/" + util.PathEscapeSegments(act.GetBranch())
|
||||
return act.GetRepoAbsoluteLink() + "/src/branch/" + util.PathEscapeSegments(act.GetBranch())
|
||||
}
|
||||
|
||||
func toTagLink(act *activities_model.Action) string {
|
||||
return act.GetRepoLink() + "/src/tag/" + util.PathEscapeSegments(act.GetTag())
|
||||
return act.GetRepoAbsoluteLink() + "/src/tag/" + util.PathEscapeSegments(act.GetTag())
|
||||
}
|
||||
|
||||
func toIssueLink(act *activities_model.Action) string {
|
||||
return act.GetRepoLink() + "/issues/" + url.PathEscape(act.GetIssueInfos()[0])
|
||||
return act.GetRepoAbsoluteLink() + "/issues/" + url.PathEscape(act.GetIssueInfos()[0])
|
||||
}
|
||||
|
||||
func toPullLink(act *activities_model.Action) string {
|
||||
return act.GetRepoLink() + "/pulls/" + url.PathEscape(act.GetIssueInfos()[0])
|
||||
return act.GetRepoAbsoluteLink() + "/pulls/" + url.PathEscape(act.GetIssueInfos()[0])
|
||||
}
|
||||
|
||||
func toSrcLink(act *activities_model.Action) string {
|
||||
return act.GetRepoLink() + "/src/" + util.PathEscapeSegments(act.GetBranch())
|
||||
return act.GetRepoAbsoluteLink() + "/src/" + util.PathEscapeSegments(act.GetBranch())
|
||||
}
|
||||
|
||||
func toReleaseLink(act *activities_model.Action) string {
|
||||
return act.GetRepoLink() + "/releases/tag/" + util.PathEscapeSegments(act.GetBranch())
|
||||
return act.GetRepoAbsoluteLink() + "/releases/tag/" + util.PathEscapeSegments(act.GetBranch())
|
||||
}
|
||||
|
||||
// renderMarkdown creates a minimal markdown render context from an action.
|
||||
|
@ -79,17 +79,17 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
|
|||
title = act.ActUser.DisplayName() + " "
|
||||
switch act.OpType {
|
||||
case activities_model.ActionCreateRepo:
|
||||
title += ctx.TrHTMLEscapeArgs("action.create_repo", act.GetRepoLink(), act.ShortRepoPath())
|
||||
link.Href = act.GetRepoLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.create_repo", act.GetRepoAbsoluteLink(), act.ShortRepoPath())
|
||||
link.Href = act.GetRepoAbsoluteLink()
|
||||
case activities_model.ActionRenameRepo:
|
||||
title += ctx.TrHTMLEscapeArgs("action.rename_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
|
||||
link.Href = act.GetRepoLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.rename_repo", act.GetContent(), act.GetRepoAbsoluteLink(), act.ShortRepoPath())
|
||||
link.Href = act.GetRepoAbsoluteLink()
|
||||
case activities_model.ActionCommitRepo:
|
||||
link.Href = toBranchLink(act)
|
||||
if len(act.Content) != 0 {
|
||||
title += ctx.TrHTMLEscapeArgs("action.commit_repo", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
|
||||
title += ctx.TrHTMLEscapeArgs("action.commit_repo", act.GetRepoAbsoluteLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
|
||||
} else {
|
||||
title += ctx.TrHTMLEscapeArgs("action.create_branch", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
|
||||
title += ctx.TrHTMLEscapeArgs("action.create_branch", act.GetRepoAbsoluteLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
|
||||
}
|
||||
case activities_model.ActionCreateIssue:
|
||||
link.Href = toIssueLink(act)
|
||||
|
@ -98,11 +98,11 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
|
|||
link.Href = toPullLink(act)
|
||||
title += ctx.TrHTMLEscapeArgs("action.create_pull_request", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath())
|
||||
case activities_model.ActionTransferRepo:
|
||||
link.Href = act.GetRepoLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.transfer_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
|
||||
link.Href = act.GetRepoAbsoluteLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.transfer_repo", act.GetContent(), act.GetRepoAbsoluteLink(), act.ShortRepoPath())
|
||||
case activities_model.ActionPushTag:
|
||||
link.Href = toTagLink(act)
|
||||
title += ctx.TrHTMLEscapeArgs("action.push_tag", act.GetRepoLink(), link.Href, act.GetTag(), act.ShortRepoPath())
|
||||
title += ctx.TrHTMLEscapeArgs("action.push_tag", act.GetRepoAbsoluteLink(), link.Href, act.GetTag(), act.ShortRepoPath())
|
||||
case activities_model.ActionCommentIssue:
|
||||
issueLink := toIssueLink(act)
|
||||
if link.Href == "#" {
|
||||
|
@ -140,26 +140,26 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
|
|||
}
|
||||
title += ctx.TrHTMLEscapeArgs("action.reopen_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
|
||||
case activities_model.ActionDeleteTag:
|
||||
link.Href = act.GetRepoLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.delete_tag", act.GetRepoLink(), act.GetTag(), act.ShortRepoPath())
|
||||
link.Href = act.GetRepoAbsoluteLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.delete_tag", act.GetRepoAbsoluteLink(), act.GetTag(), act.ShortRepoPath())
|
||||
case activities_model.ActionDeleteBranch:
|
||||
link.Href = act.GetRepoLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.delete_branch", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
|
||||
link.Href = act.GetRepoAbsoluteLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.delete_branch", act.GetRepoAbsoluteLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
|
||||
case activities_model.ActionMirrorSyncPush:
|
||||
srcLink := toSrcLink(act)
|
||||
if link.Href == "#" {
|
||||
link.Href = srcLink
|
||||
}
|
||||
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_push", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
|
||||
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_push", act.GetRepoAbsoluteLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
|
||||
case activities_model.ActionMirrorSyncCreate:
|
||||
srcLink := toSrcLink(act)
|
||||
if link.Href == "#" {
|
||||
link.Href = srcLink
|
||||
}
|
||||
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_create", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
|
||||
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_create", act.GetRepoAbsoluteLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
|
||||
case activities_model.ActionMirrorSyncDelete:
|
||||
link.Href = act.GetRepoLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_delete", act.GetRepoLink(), act.GetBranch(), act.ShortRepoPath())
|
||||
link.Href = act.GetRepoAbsoluteLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_delete", act.GetRepoAbsoluteLink(), act.GetBranch(), act.ShortRepoPath())
|
||||
case activities_model.ActionApprovePullRequest:
|
||||
pullLink := toPullLink(act)
|
||||
title += ctx.TrHTMLEscapeArgs("action.approve_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
|
||||
|
@ -174,16 +174,16 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
|
|||
if link.Href == "#" {
|
||||
link.Href = releaseLink
|
||||
}
|
||||
title += ctx.TrHTMLEscapeArgs("action.publish_release", act.GetRepoLink(), releaseLink, act.ShortRepoPath(), act.Content)
|
||||
title += ctx.TrHTMLEscapeArgs("action.publish_release", act.GetRepoAbsoluteLink(), releaseLink, act.ShortRepoPath(), act.Content)
|
||||
case activities_model.ActionPullReviewDismissed:
|
||||
pullLink := toPullLink(act)
|
||||
title += ctx.TrHTMLEscapeArgs("action.review_dismissed", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(), act.GetIssueInfos()[1])
|
||||
case activities_model.ActionStarRepo:
|
||||
link.Href = act.GetRepoLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.starred_repo", act.GetRepoLink(), act.GetRepoPath())
|
||||
link.Href = act.GetRepoAbsoluteLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.starred_repo", act.GetRepoAbsoluteLink(), act.GetRepoPath())
|
||||
case activities_model.ActionWatchRepo:
|
||||
link.Href = act.GetRepoLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.watched_repo", act.GetRepoLink(), act.GetRepoPath())
|
||||
link.Href = act.GetRepoAbsoluteLink()
|
||||
title += ctx.TrHTMLEscapeArgs("action.watched_repo", act.GetRepoAbsoluteLink(), act.GetRepoPath())
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown action type: %v", act.OpType)
|
||||
}
|
||||
|
@ -193,14 +193,14 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
|
|||
switch act.OpType {
|
||||
case activities_model.ActionCommitRepo, activities_model.ActionMirrorSyncPush:
|
||||
push := templates.ActionContent2Commits(act)
|
||||
repoLink := act.GetRepoLink()
|
||||
repoLink := act.GetRepoAbsoluteLink()
|
||||
|
||||
for _, commit := range push.Commits {
|
||||
if len(desc) != 0 {
|
||||
desc += "\n\n"
|
||||
}
|
||||
desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s",
|
||||
html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), commit.Sha1)),
|
||||
html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(), commit.Sha1)),
|
||||
commit.Sha1,
|
||||
templates.RenderCommitMessage(ctx, commit.Message, repoLink, nil),
|
||||
)
|
||||
|
@ -209,7 +209,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
|
|||
if push.Len > 1 {
|
||||
link = &feeds.Link{Href: fmt.Sprintf("%s/%s", setting.AppSubURL, push.CompareURL)}
|
||||
} else if push.Len == 1 {
|
||||
link = &feeds.Link{Href: fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), push.Commits[0].Sha1)}
|
||||
link = &feeds.Link{Href: fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(), push.Commits[0].Sha1)}
|
||||
}
|
||||
|
||||
case activities_model.ActionCreateIssue, activities_model.ActionCreatePullRequest:
|
||||
|
|
|
@ -427,5 +427,5 @@ func CreateBranch(ctx *context.Context) {
|
|||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.branch.create_success", form.NewBranchName))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(form.NewBranchName))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(form.NewBranchName) + "/" + util.PathEscapeSegments(form.CurrentPath))
|
||||
}
|
||||
|
|
|
@ -112,17 +112,17 @@ func setCsvCompareContext(ctx *context.Context) {
|
|||
Error string
|
||||
}
|
||||
|
||||
ctx.Data["CreateCsvDiff"] = func(diffFile *gitdiff.DiffFile, baseCommit, headCommit *git.Commit) CsvDiffResult {
|
||||
if diffFile == nil || baseCommit == nil || headCommit == nil {
|
||||
ctx.Data["CreateCsvDiff"] = func(diffFile *gitdiff.DiffFile, baseBlob, headBlob *git.Blob) CsvDiffResult {
|
||||
if diffFile == nil {
|
||||
return CsvDiffResult{nil, ""}
|
||||
}
|
||||
|
||||
errTooLarge := errors.New(ctx.Locale.Tr("repo.error.csv.too_large"))
|
||||
|
||||
csvReaderFromCommit := func(ctx *markup.RenderContext, c *git.Commit) (*csv.Reader, io.Closer, error) {
|
||||
blob, err := c.GetBlobByPath(diffFile.Name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
csvReaderFromCommit := func(ctx *markup.RenderContext, blob *git.Blob) (*csv.Reader, io.Closer, error) {
|
||||
if blob == nil {
|
||||
// It's ok for blob to be nil (file added or deleted)
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < blob.Size() {
|
||||
|
@ -138,28 +138,28 @@ func setCsvCompareContext(ctx *context.Context) {
|
|||
return csvReader, reader, err
|
||||
}
|
||||
|
||||
baseReader, baseBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.OldName}, baseCommit)
|
||||
baseReader, baseBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.OldName}, baseBlob)
|
||||
if baseBlobCloser != nil {
|
||||
defer baseBlobCloser.Close()
|
||||
}
|
||||
if err == errTooLarge {
|
||||
return CsvDiffResult{nil, err.Error()}
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("CreateCsvDiff error whilst creating baseReader from file %s in commit %s in %s: %v", diffFile.Name, baseCommit.ID.String(), ctx.Repo.Repository.Name, err)
|
||||
return CsvDiffResult{nil, "unable to load file from base commit"}
|
||||
if err == errTooLarge {
|
||||
return CsvDiffResult{nil, err.Error()}
|
||||
}
|
||||
log.Error("error whilst creating csv.Reader from file %s in base commit %s in %s: %v", diffFile.Name, baseBlob.ID.String(), ctx.Repo.Repository.Name, err)
|
||||
return CsvDiffResult{nil, "unable to load file"}
|
||||
}
|
||||
|
||||
headReader, headBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.Name}, headCommit)
|
||||
headReader, headBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.Name}, headBlob)
|
||||
if headBlobCloser != nil {
|
||||
defer headBlobCloser.Close()
|
||||
}
|
||||
if err == errTooLarge {
|
||||
return CsvDiffResult{nil, err.Error()}
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("CreateCsvDiff error whilst creating headReader from file %s in commit %s in %s: %v", diffFile.Name, headCommit.ID.String(), ctx.Repo.Repository.Name, err)
|
||||
return CsvDiffResult{nil, "unable to load file from head commit"}
|
||||
if err == errTooLarge {
|
||||
return CsvDiffResult{nil, err.Error()}
|
||||
}
|
||||
log.Error("error whilst creating csv.Reader from file %s in head commit %s in %s: %v", diffFile.Name, headBlob.ID.String(), ctx.Repo.Repository.Name, err)
|
||||
return CsvDiffResult{nil, "unable to load file"}
|
||||
}
|
||||
|
||||
sections, err := gitdiff.CreateCsvDiff(diffFile, baseReader, headReader)
|
||||
|
|
|
@ -917,6 +917,19 @@ func CollaborationPost(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
// find the owner team of the organization the repo belongs too and
|
||||
// check if the user we're trying to add is an owner.
|
||||
if ctx.Repo.Repository.Owner.IsOrganization() {
|
||||
if isOwner, err := organization.IsOrganizationOwner(ctx, ctx.Repo.Repository.Owner.ID, u.ID); err != nil {
|
||||
ctx.ServerError("IsOrganizationOwner", err)
|
||||
return
|
||||
} else if isOwner {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_owner"))
|
||||
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err = repo_module.AddCollaborator(ctx.Repo.Repository, u); err != nil {
|
||||
ctx.ServerError("AddCollaborator", err)
|
||||
return
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue