mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-01-14 11:05:37 +00:00
Merge branch 'master' into graceful-queues
This commit is contained in:
commit
ec83b83c74
161 changed files with 17393 additions and 6702 deletions
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -4,6 +4,26 @@ This changelog goes through all the changes that have been made in each release
|
|||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
|
||||
## [1.10.2](https://github.com/go-gitea/gitea/releases/tag/v1.10.2) - 2020-01-02
|
||||
* BUGFIXES
|
||||
* Allow only specific Columns to be updated on Issue via API (#9539) (#9580)
|
||||
* Add ErrReactionAlreadyExist error (#9550) (#9564)
|
||||
* Fix bug when migrate from API (#8631) (#9563)
|
||||
* Use default avatar for ghost user (#9536) (#9537)
|
||||
* Fix repository issues pagination bug when there are more than one label filter (#9512) (#9528)
|
||||
* Fix deleted branch not removed when push the branch again (#9516) (#9524)
|
||||
* Fix missing repository status when migrating repository via API (#9511)
|
||||
* Trigger webhook when deleting a branch after merging a PR (#9510)
|
||||
* Fix paging on /repos/{owner}/{repo}/git/trees/{sha} API endpoint (#9482)
|
||||
* Fix NewCommitStatus (#9434) (#9435)
|
||||
* Use OriginalURL instead of CloneAddr in migration logging (#9418) (#9420)
|
||||
* Fix Slack webhook payload title generation to work with Mattermost (#9404)
|
||||
* DefaultBranch needs to be prefixed by BranchPrefix (#9356) (#9359)
|
||||
* Fix issue indexer not triggered when migrating a repository (#9333)
|
||||
* Fix bug that release attachment files not deleted when deleting repository (#9322) (#9329)
|
||||
* Fix migration releases (#9319) (#9326) (#9328)
|
||||
* Fix File Edit: Author/Committer interchanged (#9297) (#9300)
|
||||
|
||||
## [1.10.1](https://github.com/go-gitea/gitea/releases/tag/v1.10.1) - 2019-12-05
|
||||
* BUGFIXES
|
||||
* Fix max length check and limit in multiple repo forms (#9148) (#9204)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
- [Code review](#code-review)
|
||||
- [Styleguide](#styleguide)
|
||||
- [Design guideline](#design-guideline)
|
||||
- [API v1](#api-v1)
|
||||
- [Developer Certificate of Origin (DCO)](#developer-certificate-of-origin-dco)
|
||||
- [Release Cycle](#release-cycle)
|
||||
- [Maintainers](#maintainers)
|
||||
|
@ -177,6 +178,43 @@ To maintain understandable code and avoid circular dependencies it is important
|
|||
- **templates:** Golang templates for generating the html output.
|
||||
- **vendor:** External code that Gitea depends on.
|
||||
|
||||
## API v1
|
||||
|
||||
The API is documented by [swagger](http://try.gitea.io/api/swagger) and is based on [GitHub API v3](https://developer.github.com/v3/).
|
||||
Thus, Gitea´s API should use the same endpoints and fields as GitHub´s API as far as possible, unless there are good reasons to deviate.
|
||||
If Gitea provides functionality that GitHub does not, a new endpoint can be created.
|
||||
If information is provided by Gitea that is not provided by the GitHub API, a new field can be used that doesn't collide with any GitHub fields.
|
||||
|
||||
Updating an existing API should not remove existing fields unless there is a really good reason to do so.
|
||||
The same applies to status responses. If you notice a problem, feel free to leave a comment in the code for future refactoring to APIv2 (which is currently not planned).
|
||||
|
||||
All expected results (errors, success, fail messages) should be documented
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L319-L327)).
|
||||
|
||||
All JSON input types must be defined as a struct in `models/structs/`
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L76-L91))
|
||||
and referenced in
|
||||
[routers/api/v1/swagger/options.go](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/options.go).
|
||||
They can then be used like the following:
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L318)).
|
||||
|
||||
All JSON responses must be defined as a struct in `models/structs/`
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L36-L68))
|
||||
and referenced in its category in `routers/api/v1/swagger/`
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/issue.go#L11-L16))
|
||||
They can be used like the following:
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L277-L279))
|
||||
|
||||
In general, HTTP methods are chosen as follows:
|
||||
* **GET** endpoints return requested object and status **OK (200)**
|
||||
* **DELETE** endpoints return status **No Content (204)**
|
||||
* **POST** endpoints return status **Created (201)**, used to **create** new objects (e.g. a User)
|
||||
* **PUT** endpoints return status **No Content (204)**, used to **add/assign** existing Obejcts (e.g. User) to something (e.g. Org-Team)
|
||||
* **PATCH** endpoints return changed object and status **OK (200)**, used to **edit/change** an existing object
|
||||
|
||||
|
||||
An endpoint which changes/edits an object expects all fields to be optional (except ones to identify the object, which are required).
|
||||
|
||||
|
||||
## Developer Certificate of Origin (DCO)
|
||||
|
||||
|
|
|
@ -76,6 +76,16 @@ WORK_IN_PROGRESS_PREFIXES=WIP:,[WIP]
|
|||
CLOSE_KEYWORDS=close,closes,closed,fix,fixes,fixed,resolve,resolves,resolved
|
||||
; List of keywords used in Pull Request comments to automatically reopen a related issue
|
||||
REOPEN_KEYWORDS=reopen,reopens,reopened
|
||||
; In the default merge message for squash commits include at most this many commits
|
||||
DEFAULT_MERGE_MESSAGE_COMMITS_LIMIT=50
|
||||
; In the default merge message for squash commits limit the size of the commit messages to this
|
||||
DEFAULT_MERGE_MESSAGE_SIZE=5120
|
||||
; In the default merge message for squash commits walk all commits to include all authors in the Co-authored-by otherwise just use those in the limited list
|
||||
DEFAULT_MERGE_MESSAGE_ALL_AUTHORS=false
|
||||
; In default merge messages limit the number of approvers listed as Reviewed-by: to this many
|
||||
DEFAULT_MERGE_MESSAGE_MAX_APPROVERS=10
|
||||
; In default merge messages only include approvers who are official
|
||||
DEFAULT_MERGE_MESSAGE_OFFICIAL_APPROVERS_ONLY=true
|
||||
|
||||
[repository.issue]
|
||||
; List of reasons why a Pull Request or Issue can be locked
|
||||
|
|
|
@ -18,7 +18,7 @@ params:
|
|||
description: Git with a cup of tea
|
||||
author: The Gitea Authors
|
||||
website: https://docs.gitea.io
|
||||
version: 1.10.1
|
||||
version: 1.10.2
|
||||
|
||||
outputs:
|
||||
home:
|
||||
|
|
|
@ -77,6 +77,11 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
|||
keywords used in Pull Request comments to automatically close a related issue
|
||||
- `REOPEN_KEYWORDS`: **reopen**, **reopens**, **reopened**: List of keywords used in Pull Request comments to automatically reopen
|
||||
a related issue
|
||||
- `DEFAULT_MERGE_MESSAGE_COMMITS_LIMIT`: **50**: In the default merge message for squash commits include at most this many commits. Set to `-1` to include all commits
|
||||
- `DEFAULT_MERGE_MESSAGE_SIZE`: **5120**: In the default merge message for squash commits limit the size of the commit messages. Set to `-1` to have no limit.
|
||||
- `DEFAULT_MERGE_MESSAGE_ALL_AUTHORS`: **false**: In the default merge message for squash commits walk all commits to include all authors in the Co-authored-by otherwise just use those in the limited list
|
||||
- `DEFAULT_MERGE_MESSAGE_MAX_APPROVERS`: **10**: In default merge messages limit the number of approvers listed as `Reviewed-by:`. Set to `-1` to include all.
|
||||
- `DEFAULT_MERGE_MESSAGE_OFFICIAL_APPROVERS_ONLY`: **true**: In default merge messages only include approvers who are officially allowed to review.
|
||||
|
||||
### Repository - Issue (`repository.issue`)
|
||||
|
||||
|
|
|
@ -80,10 +80,10 @@ Dont forget to restart your gitea to apply the changes.
|
|||
|
||||
### Adding links and tabs
|
||||
|
||||
If all you want is to add extra links to the top navigation bar, or extra tabs to the repository view, you can put them in `extra_links.tmpl` and `extra_tabs.tmpl` inside your `custom/templates/custom/` directory.
|
||||
If all you want is to add extra links to the top navigation bar or footer, or extra tabs to the repository view, you can put them in `extra_links.tmpl` (links added to the navbar), `extra_links_footer.tmpl` (links added to the left side of footer), and `extra_tabs.tmpl` inside your `custom/templates/custom/` directory.
|
||||
|
||||
For instance, let's say you are in Germany and must add the famously legally-required "Impressum"/about page, listing who is responsible for the site's content:
|
||||
just place it under your "custom/public/" directory (for instance `custom/public/impressum.html`) and put a link to it in `custom/templates/custom/extra_links.tmpl`.
|
||||
just place it under your "custom/public/" directory (for instance `custom/public/impressum.html`) and put a link to it in either `custom/templates/custom/extra_links.tmpl` or `custom/templates/custom/extra_links_footer.tmpl`.
|
||||
|
||||
To match the current style, the link should have the class name "item", and you can use `{{AppSubUrl}}` to get the base URL:
|
||||
`<a class="item" href="{{AppSubUrl}}/impressum.html">Impressum</a>`
|
||||
|
|
3
go.mod
3
go.mod
|
@ -79,11 +79,9 @@ require (
|
|||
github.com/prometheus/procfs v0.0.4 // indirect
|
||||
github.com/quasoft/websspi v1.0.0
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.0.1
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/sergi/go-diff v1.0.0
|
||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
|
||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 // indirect
|
||||
github.com/stretchr/testify v1.4.0
|
||||
|
@ -95,6 +93,7 @@ require (
|
|||
github.com/unknwon/paginater v0.0.0-20151104151617-7748a72e0141
|
||||
github.com/urfave/cli v1.20.0
|
||||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53
|
||||
github.com/yuin/goldmark v1.1.19
|
||||
go.etcd.io/bbolt v1.3.3 // indirect
|
||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876
|
||||
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9
|
||||
|
|
6
go.sum
6
go.sum
|
@ -462,16 +462,12 @@ github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qq
|
|||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b h1:4kg1wyftSKxLtnPAvcRWakIPpokB9w780/KwrNLnfPA=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
|
||||
|
@ -550,6 +546,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
|
|||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53 h1:HsIQ6yAjfjQ3IxPGrTusxp6Qxn92gNVq2x5CbvQvx3w=
|
||||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53/go.mod h1:f6elajwZV+xceiaqgRL090YzLEDGSbqr3poGL3ZgXYo=
|
||||
github.com/yuin/goldmark v1.1.19 h1:0s2/60x0XsFCXHeFut+F3azDVAAyIMyUfJRbRexiTYs=
|
||||
github.com/yuin/goldmark v1.1.19/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
|
|
|
@ -47,7 +47,7 @@ func TestAPIIssuesReactions(t *testing.T) {
|
|||
Reaction: "rocket",
|
||||
})
|
||||
resp = session.MakeRequest(t, req, http.StatusCreated)
|
||||
var apiNewReaction api.ReactionResponse
|
||||
var apiNewReaction api.Reaction
|
||||
DecodeJSON(t, resp, &apiNewReaction)
|
||||
|
||||
//Add existing reaction
|
||||
|
@ -56,10 +56,10 @@ func TestAPIIssuesReactions(t *testing.T) {
|
|||
//Get end result of reaction list of issue #1
|
||||
req = NewRequestf(t, "GET", urlStr)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
var apiReactions []*api.ReactionResponse
|
||||
var apiReactions []*api.Reaction
|
||||
DecodeJSON(t, resp, &apiReactions)
|
||||
expectResponse := make(map[int]api.ReactionResponse)
|
||||
expectResponse[0] = api.ReactionResponse{
|
||||
expectResponse := make(map[int]api.Reaction)
|
||||
expectResponse[0] = api.Reaction{
|
||||
User: user2.APIFormat(),
|
||||
Reaction: "eyes",
|
||||
Created: time.Unix(1573248003, 0),
|
||||
|
@ -107,7 +107,7 @@ func TestAPICommentReactions(t *testing.T) {
|
|||
Reaction: "+1",
|
||||
})
|
||||
resp = session.MakeRequest(t, req, http.StatusCreated)
|
||||
var apiNewReaction api.ReactionResponse
|
||||
var apiNewReaction api.Reaction
|
||||
DecodeJSON(t, resp, &apiNewReaction)
|
||||
|
||||
//Add existing reaction
|
||||
|
@ -116,15 +116,15 @@ func TestAPICommentReactions(t *testing.T) {
|
|||
//Get end result of reaction list of issue #1
|
||||
req = NewRequestf(t, "GET", urlStr)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
var apiReactions []*api.ReactionResponse
|
||||
var apiReactions []*api.Reaction
|
||||
DecodeJSON(t, resp, &apiReactions)
|
||||
expectResponse := make(map[int]api.ReactionResponse)
|
||||
expectResponse[0] = api.ReactionResponse{
|
||||
expectResponse := make(map[int]api.Reaction)
|
||||
expectResponse[0] = api.Reaction{
|
||||
User: user2.APIFormat(),
|
||||
Reaction: "laugh",
|
||||
Created: time.Unix(1573248004, 0),
|
||||
}
|
||||
expectResponse[1] = api.ReactionResponse{
|
||||
expectResponse[1] = api.Reaction{
|
||||
User: user1.APIFormat(),
|
||||
Reaction: "laugh",
|
||||
Created: time.Unix(1573248005, 0),
|
||||
|
|
|
@ -62,3 +62,61 @@ func TestAPICreateIssue(t *testing.T) {
|
|||
Title: title,
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPIEditIssue(t *testing.T) {
|
||||
defer prepareTestEnv(t)()
|
||||
|
||||
issueBefore := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
|
||||
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
|
||||
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
|
||||
assert.NoError(t, issueBefore.LoadAttributes())
|
||||
assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
|
||||
assert.Equal(t, api.StateOpen, issueBefore.State())
|
||||
|
||||
session := loginUser(t, owner.Name)
|
||||
token := getTokenForLoggedInUser(t, session)
|
||||
|
||||
// update values of issue
|
||||
issueState := "closed"
|
||||
removeDeadline := true
|
||||
milestone := int64(4)
|
||||
body := "new content!"
|
||||
title := "new title from api set"
|
||||
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repo.Name, issueBefore.Index, token)
|
||||
req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
|
||||
State: &issueState,
|
||||
RemoveDeadline: &removeDeadline,
|
||||
Milestone: &milestone,
|
||||
Body: &body,
|
||||
Title: title,
|
||||
|
||||
// ToDo change more
|
||||
})
|
||||
resp := session.MakeRequest(t, req, http.StatusCreated)
|
||||
var apiIssue api.Issue
|
||||
DecodeJSON(t, resp, &apiIssue)
|
||||
|
||||
issueAfter := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
|
||||
|
||||
// check deleted user
|
||||
assert.Equal(t, int64(500), issueAfter.PosterID)
|
||||
assert.NoError(t, issueAfter.LoadAttributes())
|
||||
assert.Equal(t, int64(-1), issueAfter.PosterID)
|
||||
assert.Equal(t, int64(-1), issueBefore.PosterID)
|
||||
assert.Equal(t, int64(-1), apiIssue.Poster.ID)
|
||||
|
||||
// API response
|
||||
assert.Equal(t, api.StateClosed, apiIssue.State)
|
||||
assert.Equal(t, milestone, apiIssue.Milestone.ID)
|
||||
assert.Equal(t, body, apiIssue.Body)
|
||||
assert.True(t, apiIssue.Deadline == nil)
|
||||
assert.Equal(t, title, apiIssue.Title)
|
||||
|
||||
// in database
|
||||
assert.Equal(t, api.StateClosed, issueAfter.State())
|
||||
assert.Equal(t, milestone, issueAfter.MilestoneID)
|
||||
assert.Equal(t, int64(0), int64(issueAfter.DeadlineUnix))
|
||||
assert.Equal(t, body, issueAfter.Content)
|
||||
assert.Equal(t, title, issueAfter.Title)
|
||||
}
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
// Copyright 2019 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 integrations
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/png"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func generateImg() bytes.Buffer {
|
||||
// Generate image
|
||||
myImage := image.NewRGBA(image.Rect(0, 0, 32, 32))
|
||||
var buff bytes.Buffer
|
||||
png.Encode(&buff, myImage)
|
||||
return buff
|
||||
}
|
||||
|
||||
func createAttachment(t *testing.T, session *TestSession, repoURL, filename string, buff bytes.Buffer, expectedStatus int) string {
|
||||
body := &bytes.Buffer{}
|
||||
|
||||
//Setup multi-part
|
||||
writer := multipart.NewWriter(body)
|
||||
part, err := writer.CreateFormFile("file", filename)
|
||||
assert.NoError(t, err)
|
||||
_, err = io.Copy(part, &buff)
|
||||
assert.NoError(t, err)
|
||||
err = writer.Close()
|
||||
assert.NoError(t, err)
|
||||
|
||||
csrf := GetCSRF(t, session, repoURL)
|
||||
|
||||
req := NewRequestWithBody(t, "POST", "/attachments", body)
|
||||
req.Header.Add("X-Csrf-Token", csrf)
|
||||
req.Header.Add("Content-Type", writer.FormDataContentType())
|
||||
resp := session.MakeRequest(t, req, expectedStatus)
|
||||
|
||||
if expectedStatus != http.StatusOK {
|
||||
return ""
|
||||
}
|
||||
var obj map[string]string
|
||||
DecodeJSON(t, resp, &obj)
|
||||
return obj["uuid"]
|
||||
}
|
||||
|
||||
func TestCreateAnonymousAttachment(t *testing.T) {
|
||||
prepareTestEnv(t)
|
||||
session := emptyTestSession(t)
|
||||
createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusFound)
|
||||
}
|
||||
|
||||
func TestCreateIssueAttachement(t *testing.T) {
|
||||
prepareTestEnv(t)
|
||||
const repoURL = "user2/repo1"
|
||||
session := loginUser(t, "user2")
|
||||
uuid := createAttachment(t, session, repoURL, "image.png", generateImg(), http.StatusOK)
|
||||
|
||||
req := NewRequest(t, "GET", repoURL+"/issues/new")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
link, exists := htmlDoc.doc.Find("form").Attr("action")
|
||||
assert.True(t, exists, "The template has changed")
|
||||
|
||||
postData := map[string]string{
|
||||
"_csrf": htmlDoc.GetCSRF(),
|
||||
"title": "New Issue With Attachement",
|
||||
"content": "some content",
|
||||
"files[0]": uuid,
|
||||
}
|
||||
|
||||
req = NewRequestWithValues(t, "POST", link, postData)
|
||||
resp = session.MakeRequest(t, req, http.StatusFound)
|
||||
test.RedirectURL(resp) // check that redirect URL exists
|
||||
|
||||
//Validate that attachement is available
|
||||
req = NewRequest(t, "GET", "/attachments/"+uuid)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
137
integrations/attachment_test.go
Normal file
137
integrations/attachment_test.go
Normal file
|
@ -0,0 +1,137 @@
|
|||
// Copyright 2019 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 integrations
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/png"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func generateImg() bytes.Buffer {
|
||||
// Generate image
|
||||
myImage := image.NewRGBA(image.Rect(0, 0, 32, 32))
|
||||
var buff bytes.Buffer
|
||||
png.Encode(&buff, myImage)
|
||||
return buff
|
||||
}
|
||||
|
||||
func createAttachment(t *testing.T, session *TestSession, repoURL, filename string, buff bytes.Buffer, expectedStatus int) string {
|
||||
body := &bytes.Buffer{}
|
||||
|
||||
//Setup multi-part
|
||||
writer := multipart.NewWriter(body)
|
||||
part, err := writer.CreateFormFile("file", filename)
|
||||
assert.NoError(t, err)
|
||||
_, err = io.Copy(part, &buff)
|
||||
assert.NoError(t, err)
|
||||
err = writer.Close()
|
||||
assert.NoError(t, err)
|
||||
|
||||
csrf := GetCSRF(t, session, repoURL)
|
||||
|
||||
req := NewRequestWithBody(t, "POST", "/attachments", body)
|
||||
req.Header.Add("X-Csrf-Token", csrf)
|
||||
req.Header.Add("Content-Type", writer.FormDataContentType())
|
||||
resp := session.MakeRequest(t, req, expectedStatus)
|
||||
|
||||
if expectedStatus != http.StatusOK {
|
||||
return ""
|
||||
}
|
||||
var obj map[string]string
|
||||
DecodeJSON(t, resp, &obj)
|
||||
return obj["uuid"]
|
||||
}
|
||||
|
||||
func TestCreateAnonymousAttachment(t *testing.T) {
|
||||
prepareTestEnv(t)
|
||||
session := emptyTestSession(t)
|
||||
createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusFound)
|
||||
}
|
||||
|
||||
func TestCreateIssueAttachment(t *testing.T) {
|
||||
prepareTestEnv(t)
|
||||
const repoURL = "user2/repo1"
|
||||
session := loginUser(t, "user2")
|
||||
uuid := createAttachment(t, session, repoURL, "image.png", generateImg(), http.StatusOK)
|
||||
|
||||
req := NewRequest(t, "GET", repoURL+"/issues/new")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
link, exists := htmlDoc.doc.Find("form").Attr("action")
|
||||
assert.True(t, exists, "The template has changed")
|
||||
|
||||
postData := map[string]string{
|
||||
"_csrf": htmlDoc.GetCSRF(),
|
||||
"title": "New Issue With Attachment",
|
||||
"content": "some content",
|
||||
"files": uuid,
|
||||
}
|
||||
|
||||
req = NewRequestWithValues(t, "POST", link, postData)
|
||||
resp = session.MakeRequest(t, req, http.StatusFound)
|
||||
test.RedirectURL(resp) // check that redirect URL exists
|
||||
|
||||
//Validate that attachment is available
|
||||
req = NewRequest(t, "GET", "/attachments/"+uuid)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
func TestGetAttachment(t *testing.T) {
|
||||
prepareTestEnv(t)
|
||||
adminSession := loginUser(t, "user1")
|
||||
user2Session := loginUser(t, "user2")
|
||||
user8Session := loginUser(t, "user8")
|
||||
emptySession := emptyTestSession(t)
|
||||
testCases := []struct {
|
||||
name string
|
||||
uuid string
|
||||
createFile bool
|
||||
session *TestSession
|
||||
want int
|
||||
}{
|
||||
{"LinkedIssueUUID", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", true, user2Session, http.StatusOK},
|
||||
{"LinkedCommentUUID", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17", true, user2Session, http.StatusOK},
|
||||
{"linked_release_uuid", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a19", true, user2Session, http.StatusOK},
|
||||
{"NotExistingUUID", "b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18", false, user2Session, http.StatusNotFound},
|
||||
{"FileMissing", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18", false, user2Session, http.StatusInternalServerError},
|
||||
{"NotLinked", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a20", true, user2Session, http.StatusNotFound},
|
||||
{"NotLinkedAccessibleByUploader", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a20", true, user8Session, http.StatusOK},
|
||||
{"PublicByNonLogged", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", true, emptySession, http.StatusOK},
|
||||
{"PrivateByNonLogged", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", true, emptySession, http.StatusNotFound},
|
||||
{"PrivateAccessibleByAdmin", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", true, adminSession, http.StatusOK},
|
||||
{"PrivateAccessibleByUser", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", true, user2Session, http.StatusOK},
|
||||
{"RepoNotAccessibleByUser", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", true, user8Session, http.StatusNotFound},
|
||||
{"OrgNotAccessibleByUser", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a21", true, user8Session, http.StatusNotFound},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
//Write empty file to be available for response
|
||||
if tc.createFile {
|
||||
localPath := models.AttachmentLocalPath(tc.uuid)
|
||||
err := os.MkdirAll(path.Dir(localPath), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
err = ioutil.WriteFile(localPath, []byte("hello world"), 0644)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
//Actual test
|
||||
req := NewRequest(t, "GET", "/attachments/"+tc.uuid)
|
||||
tc.session.MakeRequest(t, req, tc.want)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -71,6 +71,26 @@ func (a *Attachment) DownloadURL() string {
|
|||
return fmt.Sprintf("%sattachments/%s", setting.AppURL, a.UUID)
|
||||
}
|
||||
|
||||
// LinkedRepository returns the linked repo if any
|
||||
func (a *Attachment) LinkedRepository() (*Repository, UnitType, error) {
|
||||
if a.IssueID != 0 {
|
||||
iss, err := GetIssueByID(a.IssueID)
|
||||
if err != nil {
|
||||
return nil, UnitTypeIssues, err
|
||||
}
|
||||
repo, err := GetRepositoryByID(iss.RepoID)
|
||||
return repo, UnitTypeIssues, err
|
||||
} else if a.ReleaseID != 0 {
|
||||
rel, err := GetReleaseByID(a.ReleaseID)
|
||||
if err != nil {
|
||||
return nil, UnitTypeReleases, err
|
||||
}
|
||||
repo, err := GetRepositoryByID(rel.RepoID)
|
||||
return repo, UnitTypeReleases, err
|
||||
}
|
||||
return nil, -1, nil
|
||||
}
|
||||
|
||||
// NewAttachment creates a new attachment object.
|
||||
func NewAttachment(attach *Attachment, buf []byte, file io.Reader) (_ *Attachment, err error) {
|
||||
attach.UUID = gouuid.NewV4().String()
|
||||
|
|
|
@ -61,7 +61,7 @@ func TestGetByCommentOrIssueID(t *testing.T) {
|
|||
// count of attachments from issue ID
|
||||
attachments, err := GetAttachmentsByIssueID(1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(attachments))
|
||||
assert.Equal(t, 1, len(attachments))
|
||||
|
||||
attachments, err = GetAttachmentsByCommentID(1)
|
||||
assert.NoError(t, err)
|
||||
|
@ -73,7 +73,7 @@ func TestDeleteAttachments(t *testing.T) {
|
|||
|
||||
count, err := DeleteAttachmentsByIssue(4, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
assert.Equal(t, 2, count)
|
||||
|
||||
count, err = DeleteAttachmentsByComment(2, false)
|
||||
assert.NoError(t, err)
|
||||
|
@ -128,3 +128,31 @@ func TestGetAttachmentsByUUIDs(t *testing.T) {
|
|||
assert.Equal(t, int64(1), attachList[0].IssueID)
|
||||
assert.Equal(t, int64(5), attachList[1].IssueID)
|
||||
}
|
||||
|
||||
func TestLinkedRepository(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
testCases := []struct {
|
||||
name string
|
||||
attachID int64
|
||||
expectedRepo *Repository
|
||||
expectedUnitType UnitType
|
||||
}{
|
||||
{"LinkedIssue", 1, &Repository{ID: 1}, UnitTypeIssues},
|
||||
{"LinkedComment", 3, &Repository{ID: 1}, UnitTypeIssues},
|
||||
{"LinkedRelease", 9, &Repository{ID: 1}, UnitTypeReleases},
|
||||
{"Notlinked", 10, nil, -1},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
attach, err := GetAttachmentByID(tc.attachID)
|
||||
assert.NoError(t, err)
|
||||
repo, unitType, err := attach.LinkedRepository()
|
||||
assert.NoError(t, err)
|
||||
if tc.expectedRepo != nil {
|
||||
assert.Equal(t, tc.expectedRepo.ID, repo.ID)
|
||||
}
|
||||
assert.Equal(t, tc.expectedUnitType, unitType)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ type ProtectedBranch struct {
|
|||
ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
|
||||
ApprovalsWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
|
||||
RequiredApprovals int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
BlockOnRejectedReviews bool `xorm:"NOT NULL DEFAULT false"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
|
||||
}
|
||||
|
@ -166,6 +167,23 @@ func (protectBranch *ProtectedBranch) GetGrantedApprovalsCount(pr *PullRequest)
|
|||
return approvals
|
||||
}
|
||||
|
||||
// MergeBlockedByRejectedReview returns true if merge is blocked by rejected reviews
|
||||
func (protectBranch *ProtectedBranch) MergeBlockedByRejectedReview(pr *PullRequest) bool {
|
||||
if !protectBranch.BlockOnRejectedReviews {
|
||||
return false
|
||||
}
|
||||
rejectExist, err := x.Where("issue_id = ?", pr.IssueID).
|
||||
And("type = ?", ReviewTypeReject).
|
||||
And("official = ?", true).
|
||||
Exist(new(Review))
|
||||
if err != nil {
|
||||
log.Error("MergeBlockedByRejectedReview: %v", err)
|
||||
return true
|
||||
}
|
||||
|
||||
return rejectExist
|
||||
}
|
||||
|
||||
// GetProtectedBranchByRepoID getting protected branch by repo ID
|
||||
func GetProtectedBranchByRepoID(repoID int64) ([]*ProtectedBranch, error) {
|
||||
protectedBranches := make([]*ProtectedBranch, 0)
|
||||
|
@ -340,7 +358,7 @@ func (repo *Repository) IsProtectedBranchForMerging(pr *PullRequest, branchName
|
|||
if err != nil {
|
||||
return true, err
|
||||
} else if has {
|
||||
return !protectedBranch.CanUserMerge(doer.ID) || !protectedBranch.HasEnoughApprovals(pr), nil
|
||||
return !protectedBranch.CanUserMerge(doer.ID) || !protectedBranch.HasEnoughApprovals(pr) || protectedBranch.MergeBlockedByRejectedReview(pr), nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
|
|
|
@ -1201,6 +1201,21 @@ func (err ErrForbiddenIssueReaction) Error() string {
|
|||
return fmt.Sprintf("'%s' is not an allowed reaction", err.Reaction)
|
||||
}
|
||||
|
||||
// ErrReactionAlreadyExist is used when a existing reaction was try to created
|
||||
type ErrReactionAlreadyExist struct {
|
||||
Reaction string
|
||||
}
|
||||
|
||||
// IsErrReactionAlreadyExist checks if an error is a ErrReactionAlreadyExist.
|
||||
func IsErrReactionAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrReactionAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrReactionAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("reaction '%s' already exists", err.Reaction)
|
||||
}
|
||||
|
||||
// __________ .__ .__ __________ __
|
||||
// \______ \__ __| | | |\______ \ ____ ________ __ ____ _______/ |_
|
||||
// | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
-
|
||||
id: 2
|
||||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12
|
||||
issue_id: 1
|
||||
issue_id: 4
|
||||
comment_id: 0
|
||||
name: attach2
|
||||
download_count: 1
|
||||
|
@ -81,6 +81,15 @@
|
|||
-
|
||||
id: 10
|
||||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a20
|
||||
uploader_id: 8
|
||||
name: attach1
|
||||
download_count: 0
|
||||
created_unix: 946684800
|
||||
created_unix: 946684800
|
||||
|
||||
-
|
||||
id: 11
|
||||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a21
|
||||
release_id: 2
|
||||
name: attach1
|
||||
download_count: 0
|
||||
created_unix: 946684800
|
||||
|
|
|
@ -108,4 +108,17 @@
|
|||
is_closed: false
|
||||
is_pull: true
|
||||
created_unix: 946684820
|
||||
updated_unix: 978307180
|
||||
updated_unix: 978307180
|
||||
|
||||
-
|
||||
id: 10
|
||||
repo_id: 42
|
||||
index: 1
|
||||
poster_id: 500
|
||||
name: issue from deleted account
|
||||
content: content from deleted account
|
||||
is_closed: false
|
||||
is_pull: false
|
||||
created_unix: 946684830
|
||||
updated_unix: 999307200
|
||||
deadline_unix: 1019307200
|
||||
|
|
|
@ -21,3 +21,11 @@
|
|||
content: content3
|
||||
is_closed: true
|
||||
num_issues: 0
|
||||
|
||||
-
|
||||
id: 4
|
||||
repo_id: 42
|
||||
name: milestone of repo42
|
||||
content: content random
|
||||
is_closed: false
|
||||
num_issues: 0
|
||||
|
|
|
@ -11,4 +11,19 @@
|
|||
is_draft: false
|
||||
is_prerelease: false
|
||||
is_tag: false
|
||||
created_unix: 946684800
|
||||
created_unix: 946684800
|
||||
|
||||
-
|
||||
id: 2
|
||||
repo_id: 40
|
||||
publisher_id: 2
|
||||
tag_name: "v1.1"
|
||||
lower_tag_name: "v1.1"
|
||||
target: "master"
|
||||
title: "testing-release"
|
||||
sha1: "65f1bf27bc3bf70f64657658635e66094edbcb4d"
|
||||
num_commits: 10
|
||||
is_draft: false
|
||||
is_prerelease: false
|
||||
is_tag: false
|
||||
created_unix: 946684800
|
||||
|
|
|
@ -472,4 +472,10 @@
|
|||
repo_id: 48
|
||||
type: 7
|
||||
config: "{\"ExternalTrackerURL\":\"https://tracker.com\",\"ExternalTrackerFormat\":\"https://tracker.com/{user}/{repo}/issues/{index}\",\"ExternalTrackerStyle\":\"alphanumeric\"}"
|
||||
created_unix: 946684810
|
||||
-
|
||||
id: 69
|
||||
repo_id: 2
|
||||
type: 2
|
||||
config: "{}"
|
||||
created_unix: 946684810
|
|
@ -547,7 +547,8 @@
|
|||
is_private: false
|
||||
num_stars: 0
|
||||
num_forks: 0
|
||||
num_issues: 0
|
||||
num_issues: 1
|
||||
num_milestones: 1
|
||||
is_mirror: false
|
||||
|
||||
-
|
||||
|
@ -588,7 +589,7 @@
|
|||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
-
|
||||
id: 46
|
||||
owner_id: 26
|
||||
lower_name: repo_external_tracker
|
||||
|
@ -600,7 +601,7 @@
|
|||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
-
|
||||
id: 47
|
||||
owner_id: 26
|
||||
lower_name: repo_external_tracker_numeric
|
||||
|
@ -612,7 +613,7 @@
|
|||
is_mirror: false
|
||||
status: 0
|
||||
|
||||
-
|
||||
-
|
||||
id: 48
|
||||
owner_id: 26
|
||||
lower_name: repo_external_tracker_alpha
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2020 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.
|
||||
|
||||
|
@ -239,6 +240,16 @@ func (issue *Issue) loadReactions(e Engine) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (issue *Issue) loadMilestone(e Engine) (err error) {
|
||||
if issue.Milestone == nil && issue.MilestoneID > 0 {
|
||||
issue.Milestone, err = getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
|
||||
if err != nil && !IsErrMilestoneNotExist(err) {
|
||||
return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (issue *Issue) loadAttributes(e Engine) (err error) {
|
||||
if err = issue.loadRepo(e); err != nil {
|
||||
return
|
||||
|
@ -252,11 +263,8 @@ func (issue *Issue) loadAttributes(e Engine) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
if issue.Milestone == nil && issue.MilestoneID > 0 {
|
||||
issue.Milestone, err = getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
|
||||
if err != nil && !IsErrMilestoneNotExist(err) {
|
||||
return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
|
||||
}
|
||||
if err = issue.loadMilestone(e); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = issue.loadAssignees(e); err != nil {
|
||||
|
@ -296,6 +304,11 @@ func (issue *Issue) LoadAttributes() error {
|
|||
return issue.loadAttributes(x)
|
||||
}
|
||||
|
||||
// LoadMilestone load milestone of this issue.
|
||||
func (issue *Issue) LoadMilestone() error {
|
||||
return issue.loadMilestone(x)
|
||||
}
|
||||
|
||||
// GetIsRead load the `IsRead` field of the issue
|
||||
func (issue *Issue) GetIsRead(userID int64) error {
|
||||
issueUser := &IssueUser{IssueID: issue.ID, UID: userID}
|
||||
|
@ -1568,27 +1581,25 @@ func SearchIssueIDsByKeyword(kw string, repoIDs []int64, limit, start int) (int6
|
|||
return total, ids, nil
|
||||
}
|
||||
|
||||
func updateIssue(e Engine, issue *Issue) error {
|
||||
_, err := e.ID(issue.ID).AllCols().Update(issue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateIssue updates all fields of given issue.
|
||||
func UpdateIssue(issue *Issue) error {
|
||||
// UpdateIssueByAPI updates all allowed fields of given issue.
|
||||
func UpdateIssueByAPI(issue *Issue) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updateIssue(sess, issue); err != nil {
|
||||
|
||||
if _, err := sess.ID(issue.ID).Cols(
|
||||
"name", "is_closed", "content", "milestone_id", "priority",
|
||||
"deadline_unix", "updated_unix", "closed_unix", "is_locked").
|
||||
Update(issue); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := issue.loadPoster(sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := issue.addCrossReferences(sess, issue.Poster, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ type Reaction struct {
|
|||
type FindReactionsOptions struct {
|
||||
IssueID int64
|
||||
CommentID int64
|
||||
UserID int64
|
||||
Reaction string
|
||||
}
|
||||
|
||||
func (opts *FindReactionsOptions) toConds() builder.Cond {
|
||||
|
@ -46,6 +48,12 @@ func (opts *FindReactionsOptions) toConds() builder.Cond {
|
|||
} else if opts.CommentID == -1 {
|
||||
cond = cond.And(builder.Eq{"reaction.comment_id": 0})
|
||||
}
|
||||
if opts.UserID > 0 {
|
||||
cond = cond.And(builder.Eq{"reaction.user_id": opts.UserID})
|
||||
}
|
||||
if opts.Reaction != "" {
|
||||
cond = cond.And(builder.Eq{"reaction.type": opts.Reaction})
|
||||
}
|
||||
|
||||
return cond
|
||||
}
|
||||
|
@ -80,9 +88,25 @@ func createReaction(e *xorm.Session, opts *ReactionOptions) (*Reaction, error) {
|
|||
UserID: opts.Doer.ID,
|
||||
IssueID: opts.Issue.ID,
|
||||
}
|
||||
findOpts := FindReactionsOptions{
|
||||
IssueID: opts.Issue.ID,
|
||||
CommentID: -1, // reaction to issue only
|
||||
Reaction: opts.Type,
|
||||
UserID: opts.Doer.ID,
|
||||
}
|
||||
if opts.Comment != nil {
|
||||
reaction.CommentID = opts.Comment.ID
|
||||
findOpts.CommentID = opts.Comment.ID
|
||||
}
|
||||
|
||||
existingR, err := findReactions(e, findOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(existingR) > 0 {
|
||||
return existingR[0], ErrReactionAlreadyExist{Reaction: opts.Type}
|
||||
}
|
||||
|
||||
if _, err := e.Insert(reaction); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -99,23 +123,23 @@ type ReactionOptions struct {
|
|||
}
|
||||
|
||||
// CreateReaction creates reaction for issue or comment.
|
||||
func CreateReaction(opts *ReactionOptions) (reaction *Reaction, err error) {
|
||||
func CreateReaction(opts *ReactionOptions) (*Reaction, error) {
|
||||
if !setting.UI.ReactionsMap[opts.Type] {
|
||||
return nil, ErrForbiddenIssueReaction{opts.Type}
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
if err := sess.Begin(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reaction, err = createReaction(sess, opts)
|
||||
reaction, err := createReaction(sess, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return reaction, err
|
||||
}
|
||||
|
||||
if err = sess.Commit(); err != nil {
|
||||
if err := sess.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reaction, nil
|
||||
|
|
|
@ -50,9 +50,10 @@ func TestIssueAddDuplicateReaction(t *testing.T) {
|
|||
Type: "heart",
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, reaction)
|
||||
assert.Equal(t, ErrReactionAlreadyExist{Reaction: "heart"}, err)
|
||||
|
||||
AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID})
|
||||
existingR := AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID}).(*Reaction)
|
||||
assert.Equal(t, existingR.ID, reaction.ID)
|
||||
}
|
||||
|
||||
func TestIssueDeleteReaction(t *testing.T) {
|
||||
|
@ -129,7 +130,6 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
|
|||
user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||
user3 := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User)
|
||||
user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)
|
||||
ghost := NewGhostUser()
|
||||
|
||||
issue1 := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
|
||||
|
||||
|
@ -139,14 +139,13 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
|
|||
addReaction(t, user2, issue1, comment1, "heart")
|
||||
addReaction(t, user3, issue1, comment1, "heart")
|
||||
addReaction(t, user4, issue1, comment1, "+1")
|
||||
addReaction(t, ghost, issue1, comment1, "heart")
|
||||
|
||||
err := comment1.LoadReactions()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, comment1.Reactions, 5)
|
||||
assert.Len(t, comment1.Reactions, 4)
|
||||
|
||||
reactions := comment1.Reactions.GroupByType()
|
||||
assert.Len(t, reactions["heart"], 4)
|
||||
assert.Len(t, reactions["heart"], 3)
|
||||
assert.Len(t, reactions["+1"], 1)
|
||||
}
|
||||
|
||||
|
@ -160,7 +159,7 @@ func TestIssueCommentReactionCount(t *testing.T) {
|
|||
comment1 := AssertExistsAndLoadBean(t, &Comment{ID: 1}).(*Comment)
|
||||
|
||||
addReaction(t, user1, issue1, comment1, "heart")
|
||||
DeleteCommentReaction(user1, issue1, comment1, "heart")
|
||||
assert.NoError(t, DeleteCommentReaction(user1, issue1, comment1, "heart"))
|
||||
|
||||
AssertNotExistsBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID, CommentID: comment1.ID})
|
||||
}
|
||||
|
|
|
@ -288,6 +288,8 @@ var migrations = []Migration{
|
|||
NewMigration("add user_id prefix to existing user avatar name", renameExistingUserAvatarName),
|
||||
// v116 -> v117
|
||||
NewMigration("Extend TrackedTimes", extendTrackedTimes),
|
||||
// v117 -> v118
|
||||
NewMigration("Add block on rejected reviews branch protection", addBlockOnRejectedReviews),
|
||||
}
|
||||
|
||||
// Migrate database to current version
|
||||
|
|
17
models/migrations/v117.go
Normal file
17
models/migrations/v117.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2020 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 migrations
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addBlockOnRejectedReviews(x *xorm.Engine) error {
|
||||
type ProtectedBranch struct {
|
||||
BlockOnRejectedReviews bool `xorm:"NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
return x.Sync2(new(ProtectedBranch))
|
||||
}
|
201
models/pull.go
201
models/pull.go
|
@ -7,6 +7,7 @@ package models
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
|
@ -177,6 +178,206 @@ func (pr *PullRequest) GetDefaultMergeMessage() string {
|
|||
return fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.MustHeadUserName(), pr.HeadRepo.Name, pr.BaseBranch)
|
||||
}
|
||||
|
||||
// GetCommitMessages returns the commit messages between head and merge base (if there is one)
|
||||
func (pr *PullRequest) GetCommitMessages() string {
|
||||
if err := pr.LoadIssue(); err != nil {
|
||||
log.Error("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
if err := pr.Issue.LoadPoster(); err != nil {
|
||||
log.Error("Cannot load poster %d for pr id %d, index %d Error: %v", pr.Issue.PosterID, pr.ID, pr.Index, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
if pr.HeadRepo == nil {
|
||||
var err error
|
||||
pr.HeadRepo, err = GetRepositoryByID(pr.HeadRepoID)
|
||||
if err != nil {
|
||||
log.Error("GetRepositoryById[%d]: %v", pr.HeadRepoID, err)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error("Unable to open head repository: Error: %v", err)
|
||||
return ""
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
headCommit, err := gitRepo.GetBranchCommit(pr.HeadBranch)
|
||||
if err != nil {
|
||||
log.Error("Unable to get head commit: %s Error: %v", pr.HeadBranch, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
mergeBase, err := gitRepo.GetCommit(pr.MergeBase)
|
||||
if err != nil {
|
||||
log.Error("Unable to get merge base commit: %s Error: %v", pr.MergeBase, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
limit := setting.Repository.PullRequest.DefaultMergeMessageCommitsLimit
|
||||
|
||||
list, err := gitRepo.CommitsBetweenLimit(headCommit, mergeBase, limit, 0)
|
||||
if err != nil {
|
||||
log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
maxSize := setting.Repository.PullRequest.DefaultMergeMessageSize
|
||||
|
||||
posterSig := pr.Issue.Poster.NewGitSig().String()
|
||||
|
||||
authorsMap := map[string]bool{}
|
||||
authors := make([]string, 0, list.Len())
|
||||
stringBuilder := strings.Builder{}
|
||||
element := list.Front()
|
||||
for element != nil {
|
||||
commit := element.Value.(*git.Commit)
|
||||
|
||||
if maxSize < 0 || stringBuilder.Len() < maxSize {
|
||||
toWrite := []byte(commit.CommitMessage)
|
||||
if len(toWrite) > maxSize-stringBuilder.Len() && maxSize > -1 {
|
||||
toWrite = append(toWrite[:maxSize-stringBuilder.Len()], "..."...)
|
||||
}
|
||||
if _, err := stringBuilder.Write(toWrite); err != nil {
|
||||
log.Error("Unable to write commit message Error: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
if _, err := stringBuilder.WriteRune('\n'); err != nil {
|
||||
log.Error("Unable to write commit message Error: %v", err)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
authorString := commit.Author.String()
|
||||
if !authorsMap[authorString] && authorString != posterSig {
|
||||
authors = append(authors, authorString)
|
||||
authorsMap[authorString] = true
|
||||
}
|
||||
element = element.Next()
|
||||
}
|
||||
|
||||
// Consider collecting the remaining authors
|
||||
if limit >= 0 && setting.Repository.PullRequest.DefaultMergeMessageAllAuthors {
|
||||
skip := limit
|
||||
limit = 30
|
||||
for {
|
||||
list, err := gitRepo.CommitsBetweenLimit(headCommit, mergeBase, limit, skip)
|
||||
if err != nil {
|
||||
log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err)
|
||||
return ""
|
||||
|
||||
}
|
||||
if list.Len() == 0 {
|
||||
break
|
||||
}
|
||||
element := list.Front()
|
||||
for element != nil {
|
||||
commit := element.Value.(*git.Commit)
|
||||
|
||||
authorString := commit.Author.String()
|
||||
if !authorsMap[authorString] && authorString != posterSig {
|
||||
authors = append(authors, authorString)
|
||||
authorsMap[authorString] = true
|
||||
}
|
||||
element = element.Next()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if len(authors) > 0 {
|
||||
if _, err := stringBuilder.WriteRune('\n'); err != nil {
|
||||
log.Error("Unable to write to string builder Error: %v", err)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
for _, author := range authors {
|
||||
if _, err := stringBuilder.Write([]byte("Co-authored-by: ")); err != nil {
|
||||
log.Error("Unable to write to string builder Error: %v", err)
|
||||
return ""
|
||||
}
|
||||
if _, err := stringBuilder.Write([]byte(author)); err != nil {
|
||||
log.Error("Unable to write to string builder Error: %v", err)
|
||||
return ""
|
||||
}
|
||||
if _, err := stringBuilder.WriteRune('\n'); err != nil {
|
||||
log.Error("Unable to write to string builder Error: %v", err)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
return stringBuilder.String()
|
||||
}
|
||||
|
||||
// GetApprovers returns the approvers of the pull request
|
||||
func (pr *PullRequest) GetApprovers() string {
|
||||
|
||||
stringBuilder := strings.Builder{}
|
||||
if err := pr.getReviewedByLines(&stringBuilder); err != nil {
|
||||
log.Error("Unable to getReviewedByLines: Error: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return stringBuilder.String()
|
||||
}
|
||||
|
||||
func (pr *PullRequest) getReviewedByLines(writer io.Writer) error {
|
||||
maxReviewers := setting.Repository.PullRequest.DefaultMergeMessageMaxApprovers
|
||||
|
||||
if maxReviewers == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Note: This doesn't page as we only expect a very limited number of reviews
|
||||
reviews, err := findReviews(sess, FindReviewOptions{
|
||||
Type: ReviewTypeApprove,
|
||||
IssueID: pr.IssueID,
|
||||
OfficialOnly: setting.Repository.PullRequest.DefaultMergeMessageOfficialApproversOnly,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Unable to FindReviews for PR ID %d: %v", pr.ID, err)
|
||||
return err
|
||||
}
|
||||
|
||||
reviewersWritten := 0
|
||||
|
||||
for _, review := range reviews {
|
||||
if maxReviewers > 0 && reviewersWritten > maxReviewers {
|
||||
break
|
||||
}
|
||||
|
||||
if err := review.loadReviewer(sess); err != nil && !IsErrUserNotExist(err) {
|
||||
log.Error("Unable to LoadReviewer[%d] for PR ID %d : %v", review.ReviewerID, pr.ID, err)
|
||||
return err
|
||||
} else if review.Reviewer == nil {
|
||||
continue
|
||||
}
|
||||
if _, err := writer.Write([]byte("Reviewed-by: ")); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := writer.Write([]byte(review.Reviewer.NewGitSig().String())); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := writer.Write([]byte{'\n'}); err != nil {
|
||||
return err
|
||||
}
|
||||
reviewersWritten++
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// GetDefaultSquashMessage returns default message used when squash and merging pull request
|
||||
func (pr *PullRequest) GetDefaultSquashMessage() string {
|
||||
if err := pr.LoadIssue(); err != nil {
|
||||
|
|
|
@ -125,9 +125,10 @@ func GetReviewByID(id int64) (*Review, error) {
|
|||
|
||||
// FindReviewOptions represent possible filters to find reviews
|
||||
type FindReviewOptions struct {
|
||||
Type ReviewType
|
||||
IssueID int64
|
||||
ReviewerID int64
|
||||
Type ReviewType
|
||||
IssueID int64
|
||||
ReviewerID int64
|
||||
OfficialOnly bool
|
||||
}
|
||||
|
||||
func (opts *FindReviewOptions) toCond() builder.Cond {
|
||||
|
@ -141,6 +142,9 @@ func (opts *FindReviewOptions) toCond() builder.Cond {
|
|||
if opts.Type != ReviewTypeUnknown {
|
||||
cond = cond.And(builder.Eq{"type": opts.Type})
|
||||
}
|
||||
if opts.OfficialOnly {
|
||||
cond = cond.And(builder.Eq{"official": true})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
|
|
|
@ -791,6 +791,14 @@ func NewGhostUser() *User {
|
|||
}
|
||||
}
|
||||
|
||||
// IsGhost check if user is fake user for a deleted account
|
||||
func (u *User) IsGhost() bool {
|
||||
if u == nil {
|
||||
return false
|
||||
}
|
||||
return u.ID == -1 && u.Name == "Ghost"
|
||||
}
|
||||
|
||||
var (
|
||||
reservedUsernames = []string{
|
||||
"attachments",
|
||||
|
|
|
@ -171,6 +171,7 @@ type ProtectBranchForm struct {
|
|||
EnableApprovalsWhitelist bool
|
||||
ApprovalsWhitelistUsers string
|
||||
ApprovalsWhitelistTeams string
|
||||
BlockOnRejectedReviews bool
|
||||
}
|
||||
|
||||
// Validate validates the fields
|
||||
|
|
|
@ -315,7 +315,28 @@ func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (in
|
|||
|
||||
// CommitsBetween returns a list that contains commits between [last, before).
|
||||
func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List, error) {
|
||||
stdout, err := NewCommand("rev-list", before.ID.String()+"..."+last.ID.String()).RunInDirBytes(repo.Path)
|
||||
var stdout []byte
|
||||
var err error
|
||||
if before == nil {
|
||||
stdout, err = NewCommand("rev-list", before.ID.String()).RunInDirBytes(repo.Path)
|
||||
} else {
|
||||
stdout, err = NewCommand("rev-list", before.ID.String()+"..."+last.ID.String()).RunInDirBytes(repo.Path)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repo.parsePrettyFormatLogToList(bytes.TrimSpace(stdout))
|
||||
}
|
||||
|
||||
// CommitsBetweenLimit returns a list that contains at most limit commits skipping the first skip commits between [last, before)
|
||||
func (repo *Repository) CommitsBetweenLimit(last *Commit, before *Commit, limit, skip int) (*list.List, error) {
|
||||
var stdout []byte
|
||||
var err error
|
||||
if before == nil {
|
||||
stdout, err = NewCommand("rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), last.ID.String()).RunInDirBytes(repo.Path)
|
||||
} else {
|
||||
stdout, err = NewCommand("rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String()+"..."+last.ID.String()).RunInDirBytes(repo.Path)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -328,6 +349,9 @@ func (repo *Repository) CommitsBetweenIDs(last, before string) (*list.List, erro
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if before == "" {
|
||||
return repo.CommitsBetween(lastCommit, nil)
|
||||
}
|
||||
beforeCommit, err := repo.GetCommit(before)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
507
modules/markup/common/footnote.go
Normal file
507
modules/markup/common/footnote.go
Normal file
|
@ -0,0 +1,507 @@
|
|||
// Copyright 2019 Yusuke Inuzuka
|
||||
// Copyright 2019 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.
|
||||
|
||||
// Most of what follows is a subtly changed version of github.com/yuin/goldmark/extension/footnote.go
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"unicode"
|
||||
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/renderer/html"
|
||||
"github.com/yuin/goldmark/text"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
// CleanValue will clean a value to make it safe to be an id
|
||||
// This function is quite different from the original goldmark function
|
||||
// and more closely matches the output from the shurcooL sanitizer
|
||||
// In particular Unicode letters and numbers are a lot more than a-zA-Z0-9...
|
||||
func CleanValue(value []byte) []byte {
|
||||
value = bytes.TrimSpace(value)
|
||||
rs := bytes.Runes(value)
|
||||
result := make([]rune, 0, len(rs))
|
||||
needsDash := false
|
||||
for _, r := range rs {
|
||||
switch {
|
||||
case unicode.IsLetter(r) || unicode.IsNumber(r):
|
||||
if needsDash && len(result) > 0 {
|
||||
result = append(result, '-')
|
||||
}
|
||||
needsDash = false
|
||||
result = append(result, unicode.ToLower(r))
|
||||
default:
|
||||
needsDash = true
|
||||
}
|
||||
}
|
||||
return []byte(string(result))
|
||||
}
|
||||
|
||||
// Most of what follows is a subtly changed version of github.com/yuin/goldmark/extension/footnote.go
|
||||
|
||||
// A FootnoteLink struct represents a link to a footnote of Markdown
|
||||
// (PHP Markdown Extra) text.
|
||||
type FootnoteLink struct {
|
||||
ast.BaseInline
|
||||
Index int
|
||||
Name []byte
|
||||
}
|
||||
|
||||
// Dump implements Node.Dump.
|
||||
func (n *FootnoteLink) Dump(source []byte, level int) {
|
||||
m := map[string]string{}
|
||||
m["Index"] = fmt.Sprintf("%v", n.Index)
|
||||
m["Name"] = fmt.Sprintf("%v", n.Name)
|
||||
ast.DumpHelper(n, source, level, m, nil)
|
||||
}
|
||||
|
||||
// KindFootnoteLink is a NodeKind of the FootnoteLink node.
|
||||
var KindFootnoteLink = ast.NewNodeKind("GiteaFootnoteLink")
|
||||
|
||||
// Kind implements Node.Kind.
|
||||
func (n *FootnoteLink) Kind() ast.NodeKind {
|
||||
return KindFootnoteLink
|
||||
}
|
||||
|
||||
// NewFootnoteLink returns a new FootnoteLink node.
|
||||
func NewFootnoteLink(index int, name []byte) *FootnoteLink {
|
||||
return &FootnoteLink{
|
||||
Index: index,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
// A FootnoteBackLink struct represents a link to a footnote of Markdown
|
||||
// (PHP Markdown Extra) text.
|
||||
type FootnoteBackLink struct {
|
||||
ast.BaseInline
|
||||
Index int
|
||||
Name []byte
|
||||
}
|
||||
|
||||
// Dump implements Node.Dump.
|
||||
func (n *FootnoteBackLink) Dump(source []byte, level int) {
|
||||
m := map[string]string{}
|
||||
m["Index"] = fmt.Sprintf("%v", n.Index)
|
||||
m["Name"] = fmt.Sprintf("%v", n.Name)
|
||||
ast.DumpHelper(n, source, level, m, nil)
|
||||
}
|
||||
|
||||
// KindFootnoteBackLink is a NodeKind of the FootnoteBackLink node.
|
||||
var KindFootnoteBackLink = ast.NewNodeKind("GiteaFootnoteBackLink")
|
||||
|
||||
// Kind implements Node.Kind.
|
||||
func (n *FootnoteBackLink) Kind() ast.NodeKind {
|
||||
return KindFootnoteBackLink
|
||||
}
|
||||
|
||||
// NewFootnoteBackLink returns a new FootnoteBackLink node.
|
||||
func NewFootnoteBackLink(index int, name []byte) *FootnoteBackLink {
|
||||
return &FootnoteBackLink{
|
||||
Index: index,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
// A Footnote struct represents a footnote of Markdown
|
||||
// (PHP Markdown Extra) text.
|
||||
type Footnote struct {
|
||||
ast.BaseBlock
|
||||
Ref []byte
|
||||
Index int
|
||||
Name []byte
|
||||
}
|
||||
|
||||
// Dump implements Node.Dump.
|
||||
func (n *Footnote) Dump(source []byte, level int) {
|
||||
m := map[string]string{}
|
||||
m["Index"] = fmt.Sprintf("%v", n.Index)
|
||||
m["Ref"] = fmt.Sprintf("%s", n.Ref)
|
||||
m["Name"] = fmt.Sprintf("%v", n.Name)
|
||||
ast.DumpHelper(n, source, level, m, nil)
|
||||
}
|
||||
|
||||
// KindFootnote is a NodeKind of the Footnote node.
|
||||
var KindFootnote = ast.NewNodeKind("GiteaFootnote")
|
||||
|
||||
// Kind implements Node.Kind.
|
||||
func (n *Footnote) Kind() ast.NodeKind {
|
||||
return KindFootnote
|
||||
}
|
||||
|
||||
// NewFootnote returns a new Footnote node.
|
||||
func NewFootnote(ref []byte) *Footnote {
|
||||
return &Footnote{
|
||||
Ref: ref,
|
||||
Index: -1,
|
||||
Name: ref,
|
||||
}
|
||||
}
|
||||
|
||||
// A FootnoteList struct represents footnotes of Markdown
|
||||
// (PHP Markdown Extra) text.
|
||||
type FootnoteList struct {
|
||||
ast.BaseBlock
|
||||
Count int
|
||||
}
|
||||
|
||||
// Dump implements Node.Dump.
|
||||
func (n *FootnoteList) Dump(source []byte, level int) {
|
||||
m := map[string]string{}
|
||||
m["Count"] = fmt.Sprintf("%v", n.Count)
|
||||
ast.DumpHelper(n, source, level, m, nil)
|
||||
}
|
||||
|
||||
// KindFootnoteList is a NodeKind of the FootnoteList node.
|
||||
var KindFootnoteList = ast.NewNodeKind("GiteaFootnoteList")
|
||||
|
||||
// Kind implements Node.Kind.
|
||||
func (n *FootnoteList) Kind() ast.NodeKind {
|
||||
return KindFootnoteList
|
||||
}
|
||||
|
||||
// NewFootnoteList returns a new FootnoteList node.
|
||||
func NewFootnoteList() *FootnoteList {
|
||||
return &FootnoteList{
|
||||
Count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
var footnoteListKey = parser.NewContextKey()
|
||||
|
||||
type footnoteBlockParser struct {
|
||||
}
|
||||
|
||||
var defaultFootnoteBlockParser = &footnoteBlockParser{}
|
||||
|
||||
// NewFootnoteBlockParser returns a new parser.BlockParser that can parse
|
||||
// footnotes of the Markdown(PHP Markdown Extra) text.
|
||||
func NewFootnoteBlockParser() parser.BlockParser {
|
||||
return defaultFootnoteBlockParser
|
||||
}
|
||||
|
||||
func (b *footnoteBlockParser) Trigger() []byte {
|
||||
return []byte{'['}
|
||||
}
|
||||
|
||||
func (b *footnoteBlockParser) Open(parent ast.Node, reader text.Reader, pc parser.Context) (ast.Node, parser.State) {
|
||||
line, segment := reader.PeekLine()
|
||||
pos := pc.BlockOffset()
|
||||
if pos < 0 || line[pos] != '[' {
|
||||
return nil, parser.NoChildren
|
||||
}
|
||||
pos++
|
||||
if pos > len(line)-1 || line[pos] != '^' {
|
||||
return nil, parser.NoChildren
|
||||
}
|
||||
open := pos + 1
|
||||
closes := 0
|
||||
closure := util.FindClosure(line[pos+1:], '[', ']', false, false)
|
||||
closes = pos + 1 + closure
|
||||
next := closes + 1
|
||||
if closure > -1 {
|
||||
if next >= len(line) || line[next] != ':' {
|
||||
return nil, parser.NoChildren
|
||||
}
|
||||
} else {
|
||||
return nil, parser.NoChildren
|
||||
}
|
||||
padding := segment.Padding
|
||||
label := reader.Value(text.NewSegment(segment.Start+open-padding, segment.Start+closes-padding))
|
||||
if util.IsBlank(label) {
|
||||
return nil, parser.NoChildren
|
||||
}
|
||||
item := NewFootnote(label)
|
||||
|
||||
pos = next + 1 - padding
|
||||
if pos >= len(line) {
|
||||
reader.Advance(pos)
|
||||
return item, parser.NoChildren
|
||||
}
|
||||
reader.AdvanceAndSetPadding(pos, padding)
|
||||
return item, parser.HasChildren
|
||||
}
|
||||
|
||||
func (b *footnoteBlockParser) Continue(node ast.Node, reader text.Reader, pc parser.Context) parser.State {
|
||||
line, _ := reader.PeekLine()
|
||||
if util.IsBlank(line) {
|
||||
return parser.Continue | parser.HasChildren
|
||||
}
|
||||
childpos, padding := util.IndentPosition(line, reader.LineOffset(), 4)
|
||||
if childpos < 0 {
|
||||
return parser.Close
|
||||
}
|
||||
reader.AdvanceAndSetPadding(childpos, padding)
|
||||
return parser.Continue | parser.HasChildren
|
||||
}
|
||||
|
||||
func (b *footnoteBlockParser) Close(node ast.Node, reader text.Reader, pc parser.Context) {
|
||||
var list *FootnoteList
|
||||
if tlist := pc.Get(footnoteListKey); tlist != nil {
|
||||
list = tlist.(*FootnoteList)
|
||||
} else {
|
||||
list = NewFootnoteList()
|
||||
pc.Set(footnoteListKey, list)
|
||||
node.Parent().InsertBefore(node.Parent(), node, list)
|
||||
}
|
||||
node.Parent().RemoveChild(node.Parent(), node)
|
||||
list.AppendChild(list, node)
|
||||
}
|
||||
|
||||
func (b *footnoteBlockParser) CanInterruptParagraph() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *footnoteBlockParser) CanAcceptIndentedLine() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type footnoteParser struct {
|
||||
}
|
||||
|
||||
var defaultFootnoteParser = &footnoteParser{}
|
||||
|
||||
// NewFootnoteParser returns a new parser.InlineParser that can parse
|
||||
// footnote links of the Markdown(PHP Markdown Extra) text.
|
||||
func NewFootnoteParser() parser.InlineParser {
|
||||
return defaultFootnoteParser
|
||||
}
|
||||
|
||||
func (s *footnoteParser) Trigger() []byte {
|
||||
// footnote syntax probably conflict with the image syntax.
|
||||
// So we need trigger this parser with '!'.
|
||||
return []byte{'!', '['}
|
||||
}
|
||||
|
||||
func (s *footnoteParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
|
||||
line, segment := block.PeekLine()
|
||||
pos := 1
|
||||
if len(line) > 0 && line[0] == '!' {
|
||||
pos++
|
||||
}
|
||||
if pos >= len(line) || line[pos] != '^' {
|
||||
return nil
|
||||
}
|
||||
pos++
|
||||
if pos >= len(line) {
|
||||
return nil
|
||||
}
|
||||
open := pos
|
||||
closure := util.FindClosure(line[pos:], '[', ']', false, false)
|
||||
if closure < 0 {
|
||||
return nil
|
||||
}
|
||||
closes := pos + closure
|
||||
value := block.Value(text.NewSegment(segment.Start+open, segment.Start+closes))
|
||||
block.Advance(closes + 1)
|
||||
|
||||
var list *FootnoteList
|
||||
if tlist := pc.Get(footnoteListKey); tlist != nil {
|
||||
list = tlist.(*FootnoteList)
|
||||
}
|
||||
if list == nil {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
name := []byte{}
|
||||
for def := list.FirstChild(); def != nil; def = def.NextSibling() {
|
||||
d := def.(*Footnote)
|
||||
if bytes.Equal(d.Ref, value) {
|
||||
if d.Index < 0 {
|
||||
list.Count++
|
||||
d.Index = list.Count
|
||||
val := CleanValue(d.Name)
|
||||
if len(val) == 0 {
|
||||
val = []byte(strconv.Itoa(d.Index))
|
||||
}
|
||||
d.Name = pc.IDs().Generate(val, KindFootnote)
|
||||
}
|
||||
index = d.Index
|
||||
name = d.Name
|
||||
break
|
||||
}
|
||||
}
|
||||
if index == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NewFootnoteLink(index, name)
|
||||
}
|
||||
|
||||
type footnoteASTTransformer struct {
|
||||
}
|
||||
|
||||
var defaultFootnoteASTTransformer = &footnoteASTTransformer{}
|
||||
|
||||
// NewFootnoteASTTransformer returns a new parser.ASTTransformer that
|
||||
// insert a footnote list to the last of the document.
|
||||
func NewFootnoteASTTransformer() parser.ASTTransformer {
|
||||
return defaultFootnoteASTTransformer
|
||||
}
|
||||
|
||||
func (a *footnoteASTTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
|
||||
var list *FootnoteList
|
||||
if tlist := pc.Get(footnoteListKey); tlist != nil {
|
||||
list = tlist.(*FootnoteList)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
pc.Set(footnoteListKey, nil)
|
||||
for footnote := list.FirstChild(); footnote != nil; {
|
||||
var container ast.Node = footnote
|
||||
next := footnote.NextSibling()
|
||||
if fc := container.LastChild(); fc != nil && ast.IsParagraph(fc) {
|
||||
container = fc
|
||||
}
|
||||
footnoteNode := footnote.(*Footnote)
|
||||
index := footnoteNode.Index
|
||||
name := footnoteNode.Name
|
||||
if index < 0 {
|
||||
list.RemoveChild(list, footnote)
|
||||
} else {
|
||||
container.AppendChild(container, NewFootnoteBackLink(index, name))
|
||||
}
|
||||
footnote = next
|
||||
}
|
||||
list.SortChildren(func(n1, n2 ast.Node) int {
|
||||
if n1.(*Footnote).Index < n2.(*Footnote).Index {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
})
|
||||
if list.Count <= 0 {
|
||||
list.Parent().RemoveChild(list.Parent(), list)
|
||||
return
|
||||
}
|
||||
|
||||
node.AppendChild(node, list)
|
||||
}
|
||||
|
||||
// FootnoteHTMLRenderer is a renderer.NodeRenderer implementation that
|
||||
// renders FootnoteLink nodes.
|
||||
type FootnoteHTMLRenderer struct {
|
||||
html.Config
|
||||
}
|
||||
|
||||
// NewFootnoteHTMLRenderer returns a new FootnoteHTMLRenderer.
|
||||
func NewFootnoteHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
|
||||
r := &FootnoteHTMLRenderer{
|
||||
Config: html.NewConfig(),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt.SetHTMLOption(&r.Config)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
|
||||
func (r *FootnoteHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(KindFootnoteLink, r.renderFootnoteLink)
|
||||
reg.Register(KindFootnoteBackLink, r.renderFootnoteBackLink)
|
||||
reg.Register(KindFootnote, r.renderFootnote)
|
||||
reg.Register(KindFootnoteList, r.renderFootnoteList)
|
||||
}
|
||||
|
||||
func (r *FootnoteHTMLRenderer) renderFootnoteLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if entering {
|
||||
n := node.(*FootnoteLink)
|
||||
n.Dump(source, 0)
|
||||
is := strconv.Itoa(n.Index)
|
||||
_, _ = w.WriteString(`<sup id="fnref:`)
|
||||
_, _ = w.Write(n.Name)
|
||||
_, _ = w.WriteString(`"><a href="#fn:`)
|
||||
_, _ = w.Write(n.Name)
|
||||
_, _ = w.WriteString(`" class="footnote-ref" role="doc-noteref">`)
|
||||
_, _ = w.WriteString(is)
|
||||
_, _ = w.WriteString(`</a></sup>`)
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
func (r *FootnoteHTMLRenderer) renderFootnoteBackLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if entering {
|
||||
n := node.(*FootnoteBackLink)
|
||||
fmt.Fprintf(os.Stdout, "source:\n%s\n", string(n.Text(source)))
|
||||
_, _ = w.WriteString(` <a href="#fnref:`)
|
||||
_, _ = w.Write(n.Name)
|
||||
_, _ = w.WriteString(`" class="footnote-backref" role="doc-backlink">`)
|
||||
_, _ = w.WriteString("↩︎")
|
||||
_, _ = w.WriteString(`</a>`)
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
func (r *FootnoteHTMLRenderer) renderFootnote(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
n := node.(*Footnote)
|
||||
if entering {
|
||||
fmt.Fprintf(os.Stdout, "source:\n%s\n", string(n.Text(source)))
|
||||
_, _ = w.WriteString(`<li id="fn:`)
|
||||
_, _ = w.Write(n.Name)
|
||||
_, _ = w.WriteString(`" role="doc-endnote"`)
|
||||
if node.Attributes() != nil {
|
||||
html.RenderAttributes(w, node, html.ListItemAttributeFilter)
|
||||
}
|
||||
_, _ = w.WriteString(">\n")
|
||||
} else {
|
||||
_, _ = w.WriteString("</li>\n")
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
func (r *FootnoteHTMLRenderer) renderFootnoteList(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
tag := "div"
|
||||
if entering {
|
||||
_, _ = w.WriteString("<")
|
||||
_, _ = w.WriteString(tag)
|
||||
_, _ = w.WriteString(` class="footnotes" role="doc-endnotes"`)
|
||||
if node.Attributes() != nil {
|
||||
html.RenderAttributes(w, node, html.GlobalAttributeFilter)
|
||||
}
|
||||
_ = w.WriteByte('>')
|
||||
if r.Config.XHTML {
|
||||
_, _ = w.WriteString("\n<hr />\n")
|
||||
} else {
|
||||
_, _ = w.WriteString("\n<hr>\n")
|
||||
}
|
||||
_, _ = w.WriteString("<ol>\n")
|
||||
} else {
|
||||
_, _ = w.WriteString("</ol>\n")
|
||||
_, _ = w.WriteString("</")
|
||||
_, _ = w.WriteString(tag)
|
||||
_, _ = w.WriteString(">\n")
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
type footnoteExtension struct{}
|
||||
|
||||
// FootnoteExtension represents the Gitea Footnote
|
||||
var FootnoteExtension = &footnoteExtension{}
|
||||
|
||||
// Extend extends the markdown converter with the Gitea Footnote parser
|
||||
func (e *footnoteExtension) Extend(m goldmark.Markdown) {
|
||||
m.Parser().AddOptions(
|
||||
parser.WithBlockParsers(
|
||||
util.Prioritized(NewFootnoteBlockParser(), 999),
|
||||
),
|
||||
parser.WithInlineParsers(
|
||||
util.Prioritized(NewFootnoteParser(), 101),
|
||||
),
|
||||
parser.WithASTTransformers(
|
||||
util.Prioritized(NewFootnoteASTTransformer(), 999),
|
||||
),
|
||||
)
|
||||
m.Renderer().AddOptions(renderer.WithNodeRenderers(
|
||||
util.Prioritized(NewFootnoteHTMLRenderer(), 500),
|
||||
))
|
||||
}
|
19
modules/markup/common/html.go
Normal file
19
modules/markup/common/html.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2019 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 common
|
||||
|
||||
import (
|
||||
"mvdan.cc/xurls/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
// NOTE: All below regex matching do not perform any extra validation.
|
||||
// Thus a link is produced even if the linked entity does not exist.
|
||||
// While fast, this is also incorrect and lead to false positives.
|
||||
// TODO: fix invalid linking issue
|
||||
|
||||
// LinkRegex is a regexp matching a valid link
|
||||
LinkRegex, _ = xurls.StrictMatchingScheme("https?://")
|
||||
)
|
156
modules/markup/common/linkify.go
Normal file
156
modules/markup/common/linkify.go
Normal file
|
@ -0,0 +1,156 @@
|
|||
// Copyright 2019 Yusuke Inuzuka
|
||||
// Copyright 2019 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.
|
||||
|
||||
// Most of this file is a subtly changed version of github.com/yuin/goldmark/extension/linkify.go
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"regexp"
|
||||
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/text"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
var wwwURLRegxp = regexp.MustCompile(`^www\.[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}((?:/|[#?])[-a-zA-Z0-9@:%_\+.~#!?&//=\(\);,'">\^{}\[\]` + "`" + `]*)?`)
|
||||
|
||||
type linkifyParser struct {
|
||||
}
|
||||
|
||||
var defaultLinkifyParser = &linkifyParser{}
|
||||
|
||||
// NewLinkifyParser return a new InlineParser can parse
|
||||
// text that seems like a URL.
|
||||
func NewLinkifyParser() parser.InlineParser {
|
||||
return defaultLinkifyParser
|
||||
}
|
||||
|
||||
func (s *linkifyParser) Trigger() []byte {
|
||||
// ' ' indicates any white spaces and a line head
|
||||
return []byte{' ', '*', '_', '~', '('}
|
||||
}
|
||||
|
||||
var protoHTTP = []byte("http:")
|
||||
var protoHTTPS = []byte("https:")
|
||||
var protoFTP = []byte("ftp:")
|
||||
var domainWWW = []byte("www.")
|
||||
|
||||
func (s *linkifyParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
|
||||
if pc.IsInLinkLabel() {
|
||||
return nil
|
||||
}
|
||||
line, segment := block.PeekLine()
|
||||
consumes := 0
|
||||
start := segment.Start
|
||||
c := line[0]
|
||||
// advance if current position is not a line head.
|
||||
if c == ' ' || c == '*' || c == '_' || c == '~' || c == '(' {
|
||||
consumes++
|
||||
start++
|
||||
line = line[1:]
|
||||
}
|
||||
|
||||
var m []int
|
||||
var protocol []byte
|
||||
var typ ast.AutoLinkType = ast.AutoLinkURL
|
||||
if bytes.HasPrefix(line, protoHTTP) || bytes.HasPrefix(line, protoHTTPS) || bytes.HasPrefix(line, protoFTP) {
|
||||
m = LinkRegex.FindSubmatchIndex(line)
|
||||
}
|
||||
if m == nil && bytes.HasPrefix(line, domainWWW) {
|
||||
m = wwwURLRegxp.FindSubmatchIndex(line)
|
||||
protocol = []byte("http")
|
||||
}
|
||||
if m != nil {
|
||||
lastChar := line[m[1]-1]
|
||||
if lastChar == '.' {
|
||||
m[1]--
|
||||
} else if lastChar == ')' {
|
||||
closing := 0
|
||||
for i := m[1] - 1; i >= m[0]; i-- {
|
||||
if line[i] == ')' {
|
||||
closing++
|
||||
} else if line[i] == '(' {
|
||||
closing--
|
||||
}
|
||||
}
|
||||
if closing > 0 {
|
||||
m[1] -= closing
|
||||
}
|
||||
} else if lastChar == ';' {
|
||||
i := m[1] - 2
|
||||
for ; i >= m[0]; i-- {
|
||||
if util.IsAlphaNumeric(line[i]) {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if i != m[1]-2 {
|
||||
if line[i] == '&' {
|
||||
m[1] -= m[1] - i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if m == nil {
|
||||
if len(line) > 0 && util.IsPunct(line[0]) {
|
||||
return nil
|
||||
}
|
||||
typ = ast.AutoLinkEmail
|
||||
stop := util.FindEmailIndex(line)
|
||||
if stop < 0 {
|
||||
return nil
|
||||
}
|
||||
at := bytes.IndexByte(line, '@')
|
||||
m = []int{0, stop, at, stop - 1}
|
||||
if m == nil || bytes.IndexByte(line[m[2]:m[3]], '.') < 0 {
|
||||
return nil
|
||||
}
|
||||
lastChar := line[m[1]-1]
|
||||
if lastChar == '.' {
|
||||
m[1]--
|
||||
}
|
||||
if m[1] < len(line) {
|
||||
nextChar := line[m[1]]
|
||||
if nextChar == '-' || nextChar == '_' {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
if consumes != 0 {
|
||||
s := segment.WithStop(segment.Start + 1)
|
||||
ast.MergeOrAppendTextSegment(parent, s)
|
||||
}
|
||||
consumes += m[1]
|
||||
block.Advance(consumes)
|
||||
n := ast.NewTextSegment(text.NewSegment(start, start+m[1]))
|
||||
link := ast.NewAutoLink(typ, n)
|
||||
link.Protocol = protocol
|
||||
return link
|
||||
}
|
||||
|
||||
func (s *linkifyParser) CloseBlock(parent ast.Node, pc parser.Context) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
type linkify struct {
|
||||
}
|
||||
|
||||
// Linkify is an extension that allow you to parse text that seems like a URL.
|
||||
var Linkify = &linkify{}
|
||||
|
||||
func (e *linkify) Extend(m goldmark.Markdown) {
|
||||
m.Parser().AddOptions(
|
||||
parser.WithInlineParsers(
|
||||
util.Prioritized(NewLinkifyParser(), 999),
|
||||
),
|
||||
)
|
||||
}
|
|
@ -15,6 +15,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup/common"
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
@ -57,8 +58,6 @@ var (
|
|||
// https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type%3Demail)
|
||||
emailRegex = regexp.MustCompile("(?:\\s|^|\\(|\\[)([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+)(?:\\s|$|\\)|\\]|\\.(\\s|$))")
|
||||
|
||||
linkRegex, _ = xurls.StrictMatchingScheme("https?://")
|
||||
|
||||
// blackfriday extensions create IDs like fn:user-content-footnote
|
||||
blackfridayExtRegex = regexp.MustCompile(`[^:]*:user-content-`)
|
||||
)
|
||||
|
@ -118,7 +117,7 @@ func CustomLinkURLSchemes(schemes []string) {
|
|||
}
|
||||
withAuth = append(withAuth, s)
|
||||
}
|
||||
linkRegex, _ = xurls.StrictMatchingScheme(strings.Join(withAuth, "|"))
|
||||
common.LinkRegex, _ = xurls.StrictMatchingScheme(strings.Join(withAuth, "|"))
|
||||
}
|
||||
|
||||
// IsSameDomain checks if given url string has the same hostname as current Gitea instance
|
||||
|
@ -509,6 +508,12 @@ func shortLinkProcessorFull(ctx *postProcessCtx, node *html.Node, noLink bool) {
|
|||
(strings.HasPrefix(val, "‘") && strings.HasSuffix(val, "’")) {
|
||||
const lenQuote = len("‘")
|
||||
val = val[lenQuote : len(val)-lenQuote]
|
||||
} else if (strings.HasPrefix(val, "\"") && strings.HasSuffix(val, "\"")) ||
|
||||
(strings.HasPrefix(val, "'") && strings.HasSuffix(val, "'")) {
|
||||
val = val[1 : len(val)-1]
|
||||
} else if strings.HasPrefix(val, "'") && strings.HasSuffix(val, "’") {
|
||||
const lenQuote = len("‘")
|
||||
val = val[1 : len(val)-lenQuote]
|
||||
}
|
||||
props[key] = val
|
||||
}
|
||||
|
@ -803,7 +808,7 @@ func emailAddressProcessor(ctx *postProcessCtx, node *html.Node) {
|
|||
// linkProcessor creates links for any HTTP or HTTPS URL not captured by
|
||||
// markdown.
|
||||
func linkProcessor(ctx *postProcessCtx, node *html.Node) {
|
||||
m := linkRegex.FindStringIndex(node.Data)
|
||||
m := common.LinkRegex.FindStringIndex(node.Data)
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
@ -832,7 +837,7 @@ func genDefaultLinkProcessor(defaultLink string) processor {
|
|||
|
||||
// descriptionLinkProcessor creates links for DescriptionHTML
|
||||
func descriptionLinkProcessor(ctx *postProcessCtx, node *html.Node) {
|
||||
m := linkRegex.FindStringIndex(node.Data)
|
||||
m := common.LinkRegex.FindStringIndex(node.Data)
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -323,6 +323,6 @@ func TestRender_ShortLinks(t *testing.T) {
|
|||
`<p><a href="`+notencodedImgurlWiki+`" rel="nofollow"><img src="`+notencodedImgurlWiki+`"/></a></p>`)
|
||||
test(
|
||||
"<p><a href=\"https://example.org\">[[foobar]]</a></p>",
|
||||
`<p></p><p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p><p></p>`,
|
||||
`<p></p><p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p><p></p>`)
|
||||
`<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`,
|
||||
`<p><a href="https://example.org" rel="nofollow">[[foobar]]</a></p>`)
|
||||
}
|
||||
|
|
178
modules/markup/markdown/goldmark.go
Normal file
178
modules/markup/markdown/goldmark.go
Normal file
|
@ -0,0 +1,178 @@
|
|||
// Copyright 2019 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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/common"
|
||||
giteautil "code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/yuin/goldmark/ast"
|
||||
east "github.com/yuin/goldmark/extension/ast"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/renderer/html"
|
||||
"github.com/yuin/goldmark/text"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
var byteMailto = []byte("mailto:")
|
||||
|
||||
// GiteaASTTransformer is a default transformer of the goldmark tree.
|
||||
type GiteaASTTransformer struct{}
|
||||
|
||||
// Transform transforms the given AST tree.
|
||||
func (g *GiteaASTTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
|
||||
_ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if !entering {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
switch v := n.(type) {
|
||||
case *ast.Image:
|
||||
// Images need two things:
|
||||
//
|
||||
// 1. Their src needs to munged to be a real value
|
||||
// 2. If they're not wrapped with a link they need a link wrapper
|
||||
|
||||
// Check if the destination is a real link
|
||||
link := v.Destination
|
||||
if len(link) > 0 && !markup.IsLink(link) {
|
||||
prefix := pc.Get(urlPrefixKey).(string)
|
||||
if pc.Get(isWikiKey).(bool) {
|
||||
prefix = giteautil.URLJoin(prefix, "wiki", "raw")
|
||||
}
|
||||
prefix = strings.Replace(prefix, "/src/", "/media/", 1)
|
||||
|
||||
lnk := string(link)
|
||||
lnk = giteautil.URLJoin(prefix, lnk)
|
||||
lnk = strings.Replace(lnk, " ", "+", -1)
|
||||
link = []byte(lnk)
|
||||
}
|
||||
v.Destination = link
|
||||
|
||||
parent := n.Parent()
|
||||
// Create a link around image only if parent is not already a link
|
||||
if _, ok := parent.(*ast.Link); !ok && parent != nil {
|
||||
wrap := ast.NewLink()
|
||||
wrap.Destination = link
|
||||
wrap.Title = v.Title
|
||||
parent.ReplaceChild(parent, n, wrap)
|
||||
wrap.AppendChild(wrap, n)
|
||||
}
|
||||
case *ast.Link:
|
||||
// Links need their href to munged to be a real value
|
||||
link := v.Destination
|
||||
if len(link) > 0 && !markup.IsLink(link) &&
|
||||
link[0] != '#' && !bytes.HasPrefix(link, byteMailto) {
|
||||
// special case: this is not a link, a hash link or a mailto:, so it's a
|
||||
// relative URL
|
||||
lnk := string(link)
|
||||
if pc.Get(isWikiKey).(bool) {
|
||||
lnk = giteautil.URLJoin("wiki", lnk)
|
||||
}
|
||||
link = []byte(giteautil.URLJoin(pc.Get(urlPrefixKey).(string), lnk))
|
||||
}
|
||||
v.Destination = link
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
})
|
||||
}
|
||||
|
||||
type prefixedIDs struct {
|
||||
values map[string]bool
|
||||
}
|
||||
|
||||
// Generate generates a new element id.
|
||||
func (p *prefixedIDs) Generate(value []byte, kind ast.NodeKind) []byte {
|
||||
dft := []byte("id")
|
||||
if kind == ast.KindHeading {
|
||||
dft = []byte("heading")
|
||||
}
|
||||
return p.GenerateWithDefault(value, dft)
|
||||
}
|
||||
|
||||
// Generate generates a new element id.
|
||||
func (p *prefixedIDs) GenerateWithDefault(value []byte, dft []byte) []byte {
|
||||
result := common.CleanValue(value)
|
||||
if len(result) == 0 {
|
||||
result = dft
|
||||
}
|
||||
if !bytes.HasPrefix(result, []byte("user-content-")) {
|
||||
result = append([]byte("user-content-"), result...)
|
||||
}
|
||||
if _, ok := p.values[util.BytesToReadOnlyString(result)]; !ok {
|
||||
p.values[util.BytesToReadOnlyString(result)] = true
|
||||
return result
|
||||
}
|
||||
for i := 1; ; i++ {
|
||||
newResult := fmt.Sprintf("%s-%d", result, i)
|
||||
if _, ok := p.values[newResult]; !ok {
|
||||
p.values[newResult] = true
|
||||
return []byte(newResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Put puts a given element id to the used ids table.
|
||||
func (p *prefixedIDs) Put(value []byte) {
|
||||
p.values[util.BytesToReadOnlyString(value)] = true
|
||||
}
|
||||
|
||||
func newPrefixedIDs() *prefixedIDs {
|
||||
return &prefixedIDs{
|
||||
values: map[string]bool{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewTaskCheckBoxHTMLRenderer creates a TaskCheckBoxHTMLRenderer to render tasklists
|
||||
// in the gitea form.
|
||||
func NewTaskCheckBoxHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
|
||||
r := &TaskCheckBoxHTMLRenderer{
|
||||
Config: html.NewConfig(),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt.SetHTMLOption(&r.Config)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// TaskCheckBoxHTMLRenderer is a renderer.NodeRenderer implementation that
|
||||
// renders checkboxes in list items.
|
||||
// Overrides the default goldmark one to present the gitea format
|
||||
type TaskCheckBoxHTMLRenderer struct {
|
||||
html.Config
|
||||
}
|
||||
|
||||
// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
|
||||
func (r *TaskCheckBoxHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox)
|
||||
}
|
||||
|
||||
func (r *TaskCheckBoxHTMLRenderer) renderTaskCheckBox(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if !entering {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
n := node.(*east.TaskCheckBox)
|
||||
|
||||
end := ">"
|
||||
if r.XHTML {
|
||||
end = " />"
|
||||
}
|
||||
var err error
|
||||
if n.IsChecked {
|
||||
_, err = w.WriteString(`<span class="ui fitted disabled checkbox"><input type="checkbox" disabled="disabled"` + end + `<label` + end + `</span>`)
|
||||
} else {
|
||||
_, err = w.WriteString(`<span class="ui checked fitted disabled checkbox"><input type="checkbox" checked="" disabled="disabled"` + end + `<label` + end + `</span>`)
|
||||
}
|
||||
if err != nil {
|
||||
return ast.WalkStop, err
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
}
|
|
@ -7,161 +7,83 @@ package markdown
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/common"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
giteautil "code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/russross/blackfriday/v2"
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/extension"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/renderer/html"
|
||||
"github.com/yuin/goldmark/util"
|
||||
)
|
||||
|
||||
// Renderer is a extended version of underlying render object.
|
||||
type Renderer struct {
|
||||
blackfriday.Renderer
|
||||
URLPrefix string
|
||||
IsWiki bool
|
||||
var converter goldmark.Markdown
|
||||
var once = sync.Once{}
|
||||
|
||||
var urlPrefixKey = parser.NewContextKey()
|
||||
var isWikiKey = parser.NewContextKey()
|
||||
|
||||
// NewGiteaParseContext creates a parser.Context with the gitea context set
|
||||
func NewGiteaParseContext(urlPrefix string, isWiki bool) parser.Context {
|
||||
pc := parser.NewContext(parser.WithIDs(newPrefixedIDs()))
|
||||
pc.Set(urlPrefixKey, urlPrefix)
|
||||
pc.Set(isWikiKey, isWiki)
|
||||
return pc
|
||||
}
|
||||
|
||||
var byteMailto = []byte("mailto:")
|
||||
|
||||
var htmlEscaper = [256][]byte{
|
||||
'&': []byte("&"),
|
||||
'<': []byte("<"),
|
||||
'>': []byte(">"),
|
||||
'"': []byte("""),
|
||||
}
|
||||
|
||||
func escapeHTML(w io.Writer, s []byte) {
|
||||
var start, end int
|
||||
for end < len(s) {
|
||||
escSeq := htmlEscaper[s[end]]
|
||||
if escSeq != nil {
|
||||
_, _ = w.Write(s[start:end])
|
||||
_, _ = w.Write(escSeq)
|
||||
start = end + 1
|
||||
}
|
||||
end++
|
||||
}
|
||||
if start < len(s) && end <= len(s) {
|
||||
_, _ = w.Write(s[start:end])
|
||||
}
|
||||
}
|
||||
|
||||
// RenderNode is a default renderer of a single node of a syntax tree. For
|
||||
// block nodes it will be called twice: first time with entering=true, second
|
||||
// time with entering=false, so that it could know when it's working on an open
|
||||
// tag and when on close. It writes the result to w.
|
||||
//
|
||||
// The return value is a way to tell the calling walker to adjust its walk
|
||||
// pattern: e.g. it can terminate the traversal by returning Terminate. Or it
|
||||
// can ask the walker to skip a subtree of this node by returning SkipChildren.
|
||||
// The typical behavior is to return GoToNext, which asks for the usual
|
||||
// traversal to the next node.
|
||||
func (r *Renderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
||||
switch node.Type {
|
||||
case blackfriday.Image:
|
||||
prefix := r.URLPrefix
|
||||
if r.IsWiki {
|
||||
prefix = util.URLJoin(prefix, "wiki", "raw")
|
||||
}
|
||||
prefix = strings.Replace(prefix, "/src/", "/media/", 1)
|
||||
link := node.LinkData.Destination
|
||||
if len(link) > 0 && !markup.IsLink(link) {
|
||||
lnk := string(link)
|
||||
lnk = util.URLJoin(prefix, lnk)
|
||||
lnk = strings.Replace(lnk, " ", "+", -1)
|
||||
link = []byte(lnk)
|
||||
}
|
||||
node.LinkData.Destination = link
|
||||
// Render link around image only if parent is not link already
|
||||
if node.Parent != nil && node.Parent.Type != blackfriday.Link {
|
||||
if entering {
|
||||
_, _ = w.Write([]byte(`<a href="`))
|
||||
escapeHTML(w, link)
|
||||
_, _ = w.Write([]byte(`">`))
|
||||
return r.Renderer.RenderNode(w, node, entering)
|
||||
}
|
||||
s := r.Renderer.RenderNode(w, node, entering)
|
||||
_, _ = w.Write([]byte(`</a>`))
|
||||
return s
|
||||
}
|
||||
return r.Renderer.RenderNode(w, node, entering)
|
||||
case blackfriday.Link:
|
||||
// special case: this is not a link, a hash link or a mailto:, so it's a
|
||||
// relative URL
|
||||
link := node.LinkData.Destination
|
||||
if len(link) > 0 && !markup.IsLink(link) &&
|
||||
link[0] != '#' && !bytes.HasPrefix(link, byteMailto) &&
|
||||
node.LinkData.Footnote == nil {
|
||||
lnk := string(link)
|
||||
if r.IsWiki {
|
||||
lnk = util.URLJoin("wiki", lnk)
|
||||
}
|
||||
link = []byte(util.URLJoin(r.URLPrefix, lnk))
|
||||
}
|
||||
node.LinkData.Destination = link
|
||||
return r.Renderer.RenderNode(w, node, entering)
|
||||
case blackfriday.Text:
|
||||
isListItem := false
|
||||
for n := node.Parent; n != nil; n = n.Parent {
|
||||
if n.Type == blackfriday.Item {
|
||||
isListItem = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if isListItem {
|
||||
text := node.Literal
|
||||
switch {
|
||||
case bytes.HasPrefix(text, []byte("[ ] ")):
|
||||
_, _ = w.Write([]byte(`<span class="ui fitted disabled checkbox"><input type="checkbox" disabled="disabled" /><label /></span>`))
|
||||
text = text[3:]
|
||||
case bytes.HasPrefix(text, []byte("[x] ")):
|
||||
_, _ = w.Write([]byte(`<span class="ui checked fitted disabled checkbox"><input type="checkbox" checked="" disabled="disabled" /><label /></span>`))
|
||||
text = text[3:]
|
||||
}
|
||||
node.Literal = text
|
||||
}
|
||||
}
|
||||
return r.Renderer.RenderNode(w, node, entering)
|
||||
}
|
||||
|
||||
const (
|
||||
blackfridayExtensions = 0 |
|
||||
blackfriday.NoIntraEmphasis |
|
||||
blackfriday.Tables |
|
||||
blackfriday.FencedCode |
|
||||
blackfriday.Strikethrough |
|
||||
blackfriday.NoEmptyLineBeforeBlock |
|
||||
blackfriday.DefinitionLists |
|
||||
blackfriday.Footnotes |
|
||||
blackfriday.HeadingIDs |
|
||||
blackfriday.AutoHeadingIDs
|
||||
blackfridayHTMLFlags = 0 |
|
||||
blackfriday.Smartypants
|
||||
)
|
||||
|
||||
// RenderRaw renders Markdown to HTML without handling special links.
|
||||
func RenderRaw(body []byte, urlPrefix string, wikiMarkdown bool) []byte {
|
||||
renderer := &Renderer{
|
||||
Renderer: blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{
|
||||
Flags: blackfridayHTMLFlags,
|
||||
FootnoteAnchorPrefix: "user-content-",
|
||||
HeadingIDPrefix: "user-content-",
|
||||
}),
|
||||
URLPrefix: urlPrefix,
|
||||
IsWiki: wikiMarkdown,
|
||||
once.Do(func() {
|
||||
converter = goldmark.New(
|
||||
goldmark.WithExtensions(extension.Table,
|
||||
extension.Strikethrough,
|
||||
extension.TaskList,
|
||||
extension.DefinitionList,
|
||||
common.FootnoteExtension,
|
||||
extension.NewTypographer(
|
||||
extension.WithTypographicSubstitutions(extension.TypographicSubstitutions{
|
||||
extension.EnDash: nil,
|
||||
extension.EmDash: nil,
|
||||
}),
|
||||
),
|
||||
),
|
||||
goldmark.WithParserOptions(
|
||||
parser.WithAttribute(),
|
||||
parser.WithAutoHeadingID(),
|
||||
parser.WithASTTransformers(
|
||||
util.Prioritized(&GiteaASTTransformer{}, 10000),
|
||||
),
|
||||
),
|
||||
goldmark.WithRendererOptions(
|
||||
html.WithUnsafe(),
|
||||
),
|
||||
)
|
||||
|
||||
// Override the original Tasklist renderer!
|
||||
converter.Renderer().AddOptions(
|
||||
renderer.WithNodeRenderers(
|
||||
util.Prioritized(NewTaskCheckBoxHTMLRenderer(), 1000),
|
||||
),
|
||||
)
|
||||
|
||||
if setting.Markdown.EnableHardLineBreak {
|
||||
converter.Renderer().AddOptions(html.WithHardWraps())
|
||||
}
|
||||
})
|
||||
|
||||
pc := NewGiteaParseContext(urlPrefix, wikiMarkdown)
|
||||
var buf bytes.Buffer
|
||||
if err := converter.Convert(giteautil.NormalizeEOL(body), &buf, parser.WithContext(pc)); err != nil {
|
||||
log.Error("Unable to render: %v", err)
|
||||
}
|
||||
|
||||
exts := blackfridayExtensions
|
||||
if setting.Markdown.EnableHardLineBreak {
|
||||
exts |= blackfriday.HardLineBreak
|
||||
}
|
||||
|
||||
// Need to normalize EOL to UNIX LF to have consistent results in rendering
|
||||
body = blackfriday.Run(util.NormalizeEOL(body), blackfriday.WithRenderer(renderer), blackfriday.WithExtensions(exts))
|
||||
return markup.SanitizeBytes(body)
|
||||
return markup.SanitizeReader(&buf).Bytes()
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -174,8 +96,7 @@ func init() {
|
|||
}
|
||||
|
||||
// Parser implements markup.Parser
|
||||
type Parser struct {
|
||||
}
|
||||
type Parser struct{}
|
||||
|
||||
// Name implements markup.Parser
|
||||
func (Parser) Name() string {
|
||||
|
|
|
@ -98,16 +98,12 @@ func TestRender_Images(t *testing.T) {
|
|||
func testAnswers(baseURLContent, baseURLImages string) []string {
|
||||
return []string{
|
||||
`<p>Wiki! Enjoy :)</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="` + baseURLContent + `/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
|
||||
<li><a href="` + baseURLContent + `/Tips" rel="nofollow">Tips</a></li>
|
||||
</ul>
|
||||
|
||||
<p>See commit <a href="http://localhost:3000/gogits/gogs/commit/65f1bf27bc" rel="nofollow"><code>65f1bf27bc</code></a></p>
|
||||
|
||||
<p>Ideas and codes</p>
|
||||
|
||||
<ul>
|
||||
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" rel="nofollow">ocornut/imgui#786</a></li>
|
||||
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/gogits/gogs/issues/786" rel="nofollow">#786</a></li>
|
||||
|
@ -117,13 +113,9 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
|
|||
</ul>
|
||||
`,
|
||||
`<h2 id="user-content-what-is-wine-staging">What is Wine Staging?</h2>
|
||||
|
||||
<p><strong>Wine Staging</strong> on website <a href="http://wine-staging.com" rel="nofollow">wine-staging.com</a>.</p>
|
||||
|
||||
<h2 id="user-content-quick-links">Quick Links</h2>
|
||||
|
||||
<p>Here are some links to the most important topics. You can find the full list of pages at the sidebar.</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -131,7 +123,6 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
|
|||
<th><a href="` + baseURLContent + `/Installation" rel="nofollow">Installation</a></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="` + baseURLImages + `/images/icon-usage.png" rel="nofollow"><img src="` + baseURLImages + `/images/icon-usage.png" title="icon-usage.png" alt="images/icon-usage.png"/></a></td>
|
||||
|
@ -141,20 +132,15 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
|
|||
</table>
|
||||
`,
|
||||
`<p><a href="http://www.excelsiorjet.com/" rel="nofollow">Excelsior JET</a> allows you to create native executables for Windows, Linux and Mac OS X.</p>
|
||||
|
||||
<ol>
|
||||
<li><a href="https://github.com/libgdx/libgdx/wiki/Gradle-on-the-Commandline#packaging-for-the-desktop" rel="nofollow">Package your libGDX application</a>
|
||||
<a href="` + baseURLImages + `/images/1.png" rel="nofollow"><img src="` + baseURLImages + `/images/1.png" title="1.png" alt="images/1.png"/></a></li>
|
||||
<li>Perform a test run by hitting the Run! button.
|
||||
<a href="` + baseURLImages + `/images/2.png" rel="nofollow"><img src="` + baseURLImages + `/images/2.png" title="2.png" alt="images/2.png"/></a></li>
|
||||
</ol>
|
||||
|
||||
<h2 id="user-content-custom-id">More tests</h2>
|
||||
|
||||
<p>(from <a href="https://www.markdownguide.org/extended-syntax/" rel="nofollow">https://www.markdownguide.org/extended-syntax/</a>)</p>
|
||||
|
||||
<h3 id="user-content-definition-list">Definition list</h3>
|
||||
|
||||
<dl>
|
||||
<dt>First Term</dt>
|
||||
<dd>This is the definition of the first term.</dd>
|
||||
|
@ -162,27 +148,21 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
|
|||
<dd>This is one definition of the second term.</dd>
|
||||
<dd>This is another definition of the second term.</dd>
|
||||
</dl>
|
||||
|
||||
<h3 id="user-content-footnotes">Footnotes</h3>
|
||||
|
||||
<p>Here is a simple footnote,<sup id="fnref:user-content-1"><a href="#fn:user-content-1" rel="nofollow">1</a></sup> and here is a longer one.<sup id="fnref:user-content-bignote"><a href="#fn:user-content-bignote" rel="nofollow">2</a></sup></p>
|
||||
|
||||
<div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<ol>
|
||||
<li id="fn:user-content-1">This is the first footnote.</li>
|
||||
|
||||
<li id="fn:user-content-bignote"><p>Here is one with multiple paragraphs and code.</p>
|
||||
|
||||
<li id="fn:user-content-1">
|
||||
<p>This is the first footnote. <a href="#fnref:user-content-1" rel="nofollow">↩︎</a></p>
|
||||
</li>
|
||||
<li id="fn:user-content-bignote">
|
||||
<p>Here is one with multiple paragraphs and code.</p>
|
||||
<p>Indent paragraphs to include them in the footnote.</p>
|
||||
|
||||
<p><code>{ my code }</code></p>
|
||||
|
||||
<p>Add as many paragraphs as you like.</p></li>
|
||||
<p>Add as many paragraphs as you like. <a href="#fnref:user-content-bignote" rel="nofollow">↩︎</a></p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
</div>
|
||||
`,
|
||||
}
|
||||
|
@ -299,15 +279,15 @@ func TestRender_RenderParagraphs(t *testing.T) {
|
|||
test := func(t *testing.T, str string, cnt int) {
|
||||
unix := []byte(str)
|
||||
res := string(RenderRaw(unix, "", false))
|
||||
assert.Equal(t, strings.Count(res, "<p"), cnt)
|
||||
assert.Equal(t, strings.Count(res, "<p"), cnt, "Rendered result for unix should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
|
||||
|
||||
mac := []byte(strings.ReplaceAll(str, "\n", "\r"))
|
||||
res = string(RenderRaw(mac, "", false))
|
||||
assert.Equal(t, strings.Count(res, "<p"), cnt)
|
||||
assert.Equal(t, strings.Count(res, "<p"), cnt, "Rendered result for mac should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
|
||||
|
||||
dos := []byte(strings.ReplaceAll(str, "\n", "\r\n"))
|
||||
res = string(RenderRaw(dos, "", false))
|
||||
assert.Equal(t, strings.Count(res, "<p"), cnt)
|
||||
assert.Equal(t, strings.Count(res, "<p"), cnt, "Rendered result for windows should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
|
||||
}
|
||||
|
||||
test(t, "\nOne\nTwo\nThree", 1)
|
||||
|
|
|
@ -6,33 +6,86 @@ package mdstripper
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
|
||||
"io"
|
||||
|
||||
"github.com/russross/blackfriday/v2"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup/common"
|
||||
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/extension"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/renderer/html"
|
||||
"github.com/yuin/goldmark/text"
|
||||
)
|
||||
|
||||
// MarkdownStripper extends blackfriday.Renderer
|
||||
type MarkdownStripper struct {
|
||||
links []string
|
||||
coallesce bool
|
||||
empty bool
|
||||
type stripRenderer struct {
|
||||
links []string
|
||||
empty bool
|
||||
}
|
||||
|
||||
const (
|
||||
blackfridayExtensions = 0 |
|
||||
blackfriday.NoIntraEmphasis |
|
||||
blackfriday.Tables |
|
||||
blackfriday.FencedCode |
|
||||
blackfriday.Strikethrough |
|
||||
blackfriday.NoEmptyLineBeforeBlock |
|
||||
blackfriday.DefinitionLists |
|
||||
blackfriday.Footnotes |
|
||||
blackfriday.HeadingIDs |
|
||||
blackfriday.AutoHeadingIDs |
|
||||
// Not included in modules/markup/markdown/markdown.go;
|
||||
// required here to process inline links
|
||||
blackfriday.Autolink
|
||||
)
|
||||
func (r *stripRenderer) Render(w io.Writer, source []byte, doc ast.Node) error {
|
||||
return ast.Walk(doc, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if !entering {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
switch v := n.(type) {
|
||||
case *ast.Text:
|
||||
if !v.IsRaw() {
|
||||
_, prevSibIsText := n.PreviousSibling().(*ast.Text)
|
||||
coalesce := prevSibIsText
|
||||
r.processString(
|
||||
w,
|
||||
v.Text(source),
|
||||
coalesce)
|
||||
if v.SoftLineBreak() {
|
||||
r.doubleSpace(w)
|
||||
}
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
case *ast.Link:
|
||||
r.processLink(w, v.Destination)
|
||||
return ast.WalkSkipChildren, nil
|
||||
case *ast.AutoLink:
|
||||
r.processLink(w, v.URL(source))
|
||||
return ast.WalkSkipChildren, nil
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (r *stripRenderer) doubleSpace(w io.Writer) {
|
||||
if !r.empty {
|
||||
_, _ = w.Write([]byte{'\n'})
|
||||
}
|
||||
}
|
||||
|
||||
func (r *stripRenderer) processString(w io.Writer, text []byte, coalesce bool) {
|
||||
// Always break-up words
|
||||
if !coalesce {
|
||||
r.doubleSpace(w)
|
||||
}
|
||||
_, _ = w.Write(text)
|
||||
r.empty = false
|
||||
}
|
||||
|
||||
func (r *stripRenderer) processLink(w io.Writer, link []byte) {
|
||||
// Links are processed out of band
|
||||
r.links = append(r.links, string(link))
|
||||
}
|
||||
|
||||
// GetLinks returns the list of link data collected while parsing
|
||||
func (r *stripRenderer) GetLinks() []string {
|
||||
return r.links
|
||||
}
|
||||
|
||||
// AddOptions adds given option to this renderer.
|
||||
func (r *stripRenderer) AddOptions(...renderer.Option) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
// StripMarkdown parses markdown content by removing all markup and code blocks
|
||||
// in order to extract links and other references
|
||||
|
@ -41,78 +94,40 @@ func StripMarkdown(rawBytes []byte) (string, []string) {
|
|||
return string(buf), links
|
||||
}
|
||||
|
||||
var stripParser parser.Parser
|
||||
var once = sync.Once{}
|
||||
|
||||
// StripMarkdownBytes parses markdown content by removing all markup and code blocks
|
||||
// in order to extract links and other references
|
||||
func StripMarkdownBytes(rawBytes []byte) ([]byte, []string) {
|
||||
stripper := &MarkdownStripper{
|
||||
once.Do(func() {
|
||||
gdMarkdown := goldmark.New(
|
||||
goldmark.WithExtensions(extension.Table,
|
||||
extension.Strikethrough,
|
||||
extension.TaskList,
|
||||
extension.DefinitionList,
|
||||
common.FootnoteExtension,
|
||||
common.Linkify,
|
||||
),
|
||||
goldmark.WithParserOptions(
|
||||
parser.WithAttribute(),
|
||||
parser.WithAutoHeadingID(),
|
||||
),
|
||||
goldmark.WithRendererOptions(
|
||||
html.WithUnsafe(),
|
||||
),
|
||||
)
|
||||
stripParser = gdMarkdown.Parser()
|
||||
})
|
||||
stripper := &stripRenderer{
|
||||
links: make([]string, 0, 10),
|
||||
empty: true,
|
||||
}
|
||||
|
||||
parser := blackfriday.New(blackfriday.WithRenderer(stripper), blackfriday.WithExtensions(blackfridayExtensions))
|
||||
ast := parser.Parse(rawBytes)
|
||||
reader := text.NewReader(rawBytes)
|
||||
doc := stripParser.Parse(reader)
|
||||
var buf bytes.Buffer
|
||||
stripper.RenderHeader(&buf, ast)
|
||||
ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
||||
return stripper.RenderNode(&buf, node, entering)
|
||||
})
|
||||
stripper.RenderFooter(&buf, ast)
|
||||
if err := stripper.Render(&buf, rawBytes, doc); err != nil {
|
||||
log.Error("Unable to strip: %v", err)
|
||||
}
|
||||
return buf.Bytes(), stripper.GetLinks()
|
||||
}
|
||||
|
||||
// RenderNode is the main rendering method. It will be called once for
|
||||
// every leaf node and twice for every non-leaf node (first with
|
||||
// entering=true, then with entering=false). The method should write its
|
||||
// rendition of the node to the supplied writer w.
|
||||
func (r *MarkdownStripper) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
||||
if !entering {
|
||||
return blackfriday.GoToNext
|
||||
}
|
||||
switch node.Type {
|
||||
case blackfriday.Text:
|
||||
r.processString(w, node.Literal, node.Parent == nil)
|
||||
return blackfriday.GoToNext
|
||||
case blackfriday.Link:
|
||||
r.processLink(w, node.LinkData.Destination)
|
||||
r.coallesce = false
|
||||
return blackfriday.SkipChildren
|
||||
}
|
||||
r.coallesce = false
|
||||
return blackfriday.GoToNext
|
||||
}
|
||||
|
||||
// RenderHeader is a method that allows the renderer to produce some
|
||||
// content preceding the main body of the output document.
|
||||
func (r *MarkdownStripper) RenderHeader(w io.Writer, ast *blackfriday.Node) {
|
||||
}
|
||||
|
||||
// RenderFooter is a symmetric counterpart of RenderHeader.
|
||||
func (r *MarkdownStripper) RenderFooter(w io.Writer, ast *blackfriday.Node) {
|
||||
}
|
||||
|
||||
func (r *MarkdownStripper) doubleSpace(w io.Writer) {
|
||||
if !r.empty {
|
||||
_, _ = w.Write([]byte{'\n'})
|
||||
}
|
||||
}
|
||||
|
||||
func (r *MarkdownStripper) processString(w io.Writer, text []byte, coallesce bool) {
|
||||
// Always break-up words
|
||||
if !coallesce || !r.coallesce {
|
||||
r.doubleSpace(w)
|
||||
}
|
||||
_, _ = w.Write(text)
|
||||
r.coallesce = coallesce
|
||||
r.empty = false
|
||||
}
|
||||
|
||||
func (r *MarkdownStripper) processLink(w io.Writer, link []byte) {
|
||||
// Links are processed out of band
|
||||
r.links = append(r.links, string(link))
|
||||
r.coallesce = false
|
||||
}
|
||||
|
||||
// GetLinks returns the list of link data collected while parsing
|
||||
func (r *MarkdownStripper) GetLinks() []string {
|
||||
return r.links
|
||||
}
|
||||
|
|
|
@ -53,6 +53,20 @@ A HIDDEN ` + "`" + `GHOST` + "`" + ` IN THIS LINE.
|
|||
[]string{
|
||||
"link",
|
||||
}},
|
||||
{
|
||||
"Simply closes: #29 yes",
|
||||
[]string{
|
||||
"Simply closes: #29 yes",
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"Simply closes: !29 yes",
|
||||
[]string{
|
||||
"Simply closes: !29 yes",
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range list {
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
package markup
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"regexp"
|
||||
"sync"
|
||||
|
||||
|
@ -67,6 +69,12 @@ func Sanitize(s string) string {
|
|||
return sanitizer.policy.Sanitize(s)
|
||||
}
|
||||
|
||||
// SanitizeReader sanitizes a Reader
|
||||
func SanitizeReader(r io.Reader) *bytes.Buffer {
|
||||
NewSanitizer()
|
||||
return sanitizer.policy.SanitizeReader(r)
|
||||
}
|
||||
|
||||
// SanitizeBytes takes a []byte slice that contains a HTML fragment or document and applies policy whitelist.
|
||||
func SanitizeBytes(b []byte) []byte {
|
||||
if len(b) == 0 {
|
||||
|
|
|
@ -53,6 +53,7 @@ func (m *mailNotifier) NotifyNewIssue(issue *models.Issue) {
|
|||
|
||||
func (m *mailNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) {
|
||||
var actionType models.ActionType
|
||||
issue.Content = ""
|
||||
if issue.IsPull {
|
||||
if isClosed {
|
||||
actionType = models.ActionClosePullRequest
|
||||
|
@ -105,7 +106,7 @@ func (m *mailNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *mode
|
|||
log.Error("pr.LoadIssue: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
pr.Issue.Content = ""
|
||||
if err := mailer.MailParticipants(pr.Issue, doer, models.ActionMergePullRequest); err != nil {
|
||||
log.Error("MailParticipants: %v", err)
|
||||
}
|
||||
|
|
|
@ -43,10 +43,6 @@ func TestFindAllIssueReferences(t *testing.T) {
|
|||
{29, "", "", "29", true, XRefActionCloses, &RefSpan{Start: 15, End: 18}, &RefSpan{Start: 7, End: 13}},
|
||||
},
|
||||
},
|
||||
{
|
||||
"#123 no, this is a title.",
|
||||
[]testResult{},
|
||||
},
|
||||
{
|
||||
" #124 yes, this is a reference.",
|
||||
[]testResult{
|
||||
|
|
|
@ -60,9 +60,14 @@ var (
|
|||
|
||||
// Pull request settings
|
||||
PullRequest struct {
|
||||
WorkInProgressPrefixes []string
|
||||
CloseKeywords []string
|
||||
ReopenKeywords []string
|
||||
WorkInProgressPrefixes []string
|
||||
CloseKeywords []string
|
||||
ReopenKeywords []string
|
||||
DefaultMergeMessageCommitsLimit int
|
||||
DefaultMergeMessageSize int
|
||||
DefaultMergeMessageAllAuthors bool
|
||||
DefaultMergeMessageMaxApprovers int
|
||||
DefaultMergeMessageOfficialApproversOnly bool
|
||||
} `ini:"repository.pull-request"`
|
||||
|
||||
// Issue Setting
|
||||
|
@ -127,15 +132,25 @@ var (
|
|||
|
||||
// Pull request settings
|
||||
PullRequest: struct {
|
||||
WorkInProgressPrefixes []string
|
||||
CloseKeywords []string
|
||||
ReopenKeywords []string
|
||||
WorkInProgressPrefixes []string
|
||||
CloseKeywords []string
|
||||
ReopenKeywords []string
|
||||
DefaultMergeMessageCommitsLimit int
|
||||
DefaultMergeMessageSize int
|
||||
DefaultMergeMessageAllAuthors bool
|
||||
DefaultMergeMessageMaxApprovers int
|
||||
DefaultMergeMessageOfficialApproversOnly bool
|
||||
}{
|
||||
WorkInProgressPrefixes: []string{"WIP:", "[WIP]"},
|
||||
// Same as GitHub. See
|
||||
// https://help.github.com/articles/closing-issues-via-commit-messages
|
||||
CloseKeywords: strings.Split("close,closes,closed,fix,fixes,fixed,resolve,resolves,resolved", ","),
|
||||
ReopenKeywords: strings.Split("reopen,reopens,reopened", ","),
|
||||
CloseKeywords: strings.Split("close,closes,closed,fix,fixes,fixed,resolve,resolves,resolved", ","),
|
||||
ReopenKeywords: strings.Split("reopen,reopens,reopened", ","),
|
||||
DefaultMergeMessageCommitsLimit: 50,
|
||||
DefaultMergeMessageSize: 5 * 1024,
|
||||
DefaultMergeMessageAllAuthors: false,
|
||||
DefaultMergeMessageMaxApprovers: 10,
|
||||
DefaultMergeMessageOfficialApproversOnly: true,
|
||||
},
|
||||
|
||||
// Issue settings
|
||||
|
|
|
@ -34,15 +34,19 @@ type Hook struct {
|
|||
// HookList represents a list of API hook.
|
||||
type HookList []*Hook
|
||||
|
||||
// CreateHookOptionConfig has all config options in it
|
||||
// required are "content_type" and "url" Required
|
||||
type CreateHookOptionConfig map[string]string
|
||||
|
||||
// CreateHookOption options when create a hook
|
||||
type CreateHookOption struct {
|
||||
// required: true
|
||||
// enum: gitea,gogs,slack,discord
|
||||
// enum: dingtalk,discord,gitea,gogs,msteams,slack,telegram
|
||||
Type string `json:"type" binding:"Required"`
|
||||
// required: true
|
||||
Config map[string]string `json:"config" binding:"Required"`
|
||||
Events []string `json:"events"`
|
||||
BranchFilter string `json:"branch_filter" binding:"GlobPattern"`
|
||||
Config CreateHookOptionConfig `json:"config" binding:"Required"`
|
||||
Events []string `json:"events"`
|
||||
BranchFilter string `json:"branch_filter" binding:"GlobPattern"`
|
||||
// default: false
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ type EditReactionOption struct {
|
|||
Reaction string `json:"content"`
|
||||
}
|
||||
|
||||
// ReactionResponse contain one reaction
|
||||
type ReactionResponse struct {
|
||||
// Reaction contain one reaction
|
||||
type Reaction struct {
|
||||
User *User `json:"user"`
|
||||
Reaction string `json:"content"`
|
||||
// swagger:strfmt date-time
|
||||
|
|
|
@ -132,7 +132,7 @@ func getDingtalkPushPayload(p *api.PushPayload) (*DingtalkPayload, error) {
|
|||
}
|
||||
|
||||
func getDingtalkIssuesPayload(p *api.IssuePayload) (*DingtalkPayload, error) {
|
||||
text, issueTitle, attachmentText, _ := getIssuesPayloadInfo(p, noneLinkFormatter)
|
||||
text, issueTitle, attachmentText, _ := getIssuesPayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
return &DingtalkPayload{
|
||||
MsgType: "actionCard",
|
||||
|
@ -148,7 +148,7 @@ func getDingtalkIssuesPayload(p *api.IssuePayload) (*DingtalkPayload, error) {
|
|||
}
|
||||
|
||||
func getDingtalkIssueCommentPayload(p *api.IssueCommentPayload) (*DingtalkPayload, error) {
|
||||
text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter)
|
||||
text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
return &DingtalkPayload{
|
||||
MsgType: "actionCard",
|
||||
|
@ -163,7 +163,7 @@ func getDingtalkIssueCommentPayload(p *api.IssueCommentPayload) (*DingtalkPayloa
|
|||
}
|
||||
|
||||
func getDingtalkPullRequestPayload(p *api.PullRequestPayload) (*DingtalkPayload, error) {
|
||||
text, issueTitle, attachmentText, _ := getPullRequestPayloadInfo(p, noneLinkFormatter)
|
||||
text, issueTitle, attachmentText, _ := getPullRequestPayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
return &DingtalkPayload{
|
||||
MsgType: "actionCard",
|
||||
|
@ -236,7 +236,7 @@ func getDingtalkRepositoryPayload(p *api.RepositoryPayload) (*DingtalkPayload, e
|
|||
}
|
||||
|
||||
func getDingtalkReleasePayload(p *api.ReleasePayload) (*DingtalkPayload, error) {
|
||||
text, _ := getReleasePayloadInfo(p, noneLinkFormatter)
|
||||
text, _ := getReleasePayloadInfo(p, noneLinkFormatter, true)
|
||||
|
||||
return &DingtalkPayload{
|
||||
MsgType: "actionCard",
|
||||
|
|
|
@ -227,7 +227,7 @@ func getDiscordPushPayload(p *api.PushPayload, meta *DiscordMeta) (*DiscordPaylo
|
|||
}
|
||||
|
||||
func getDiscordIssuesPayload(p *api.IssuePayload, meta *DiscordMeta) (*DiscordPayload, error) {
|
||||
text, _, attachmentText, color := getIssuesPayloadInfo(p, noneLinkFormatter)
|
||||
text, _, attachmentText, color := getIssuesPayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
return &DiscordPayload{
|
||||
Username: meta.Username,
|
||||
|
@ -249,7 +249,7 @@ func getDiscordIssuesPayload(p *api.IssuePayload, meta *DiscordMeta) (*DiscordPa
|
|||
}
|
||||
|
||||
func getDiscordIssueCommentPayload(p *api.IssueCommentPayload, discord *DiscordMeta) (*DiscordPayload, error) {
|
||||
text, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter)
|
||||
text, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
return &DiscordPayload{
|
||||
Username: discord.Username,
|
||||
|
@ -271,7 +271,7 @@ func getDiscordIssueCommentPayload(p *api.IssueCommentPayload, discord *DiscordM
|
|||
}
|
||||
|
||||
func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta) (*DiscordPayload, error) {
|
||||
text, _, attachmentText, color := getPullRequestPayloadInfo(p, noneLinkFormatter)
|
||||
text, _, attachmentText, color := getPullRequestPayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
return &DiscordPayload{
|
||||
Username: meta.Username,
|
||||
|
@ -368,7 +368,7 @@ func getDiscordRepositoryPayload(p *api.RepositoryPayload, meta *DiscordMeta) (*
|
|||
}
|
||||
|
||||
func getDiscordReleasePayload(p *api.ReleasePayload, meta *DiscordMeta) (*DiscordPayload, error) {
|
||||
text, color := getReleasePayloadInfo(p, noneLinkFormatter)
|
||||
text, color := getReleasePayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
return &DiscordPayload{
|
||||
Username: meta.Username,
|
||||
|
|
|
@ -25,8 +25,7 @@ func htmlLinkFormatter(url string, text string) string {
|
|||
return fmt.Sprintf(`<a href="%s">%s</a>`, url, html.EscapeString(text))
|
||||
}
|
||||
|
||||
func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter) (string, string, string, int) {
|
||||
senderLink := linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||
func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, withSender bool) (string, string, string, int) {
|
||||
repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||
issueTitle := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title)
|
||||
titleLink := linkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index), issueTitle)
|
||||
|
@ -35,34 +34,36 @@ func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter) (str
|
|||
|
||||
switch p.Action {
|
||||
case api.HookIssueOpened:
|
||||
text = fmt.Sprintf("[%s] Issue opened: %s by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Issue opened: %s", repoLink, titleLink)
|
||||
color = orangeColor
|
||||
case api.HookIssueClosed:
|
||||
text = fmt.Sprintf("[%s] Issue closed: %s by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Issue closed: %s", repoLink, titleLink)
|
||||
color = redColor
|
||||
case api.HookIssueReOpened:
|
||||
text = fmt.Sprintf("[%s] Issue re-opened: %s by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Issue re-opened: %s", repoLink, titleLink)
|
||||
case api.HookIssueEdited:
|
||||
text = fmt.Sprintf("[%s] Issue edited: %s by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Issue edited: %s", repoLink, titleLink)
|
||||
case api.HookIssueAssigned:
|
||||
text = fmt.Sprintf("[%s] Issue assigned to %s: %s by %s", repoLink,
|
||||
linkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName),
|
||||
titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Issue assigned to %s: %s", repoLink,
|
||||
linkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName), titleLink)
|
||||
color = greenColor
|
||||
case api.HookIssueUnassigned:
|
||||
text = fmt.Sprintf("[%s] Issue unassigned: %s by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Issue unassigned: %s", repoLink, titleLink)
|
||||
case api.HookIssueLabelUpdated:
|
||||
text = fmt.Sprintf("[%s] Issue labels updated: %s by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Issue labels updated: %s", repoLink, titleLink)
|
||||
case api.HookIssueLabelCleared:
|
||||
text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Issue labels cleared: %s", repoLink, titleLink)
|
||||
case api.HookIssueSynchronized:
|
||||
text = fmt.Sprintf("[%s] Issue synchronized: %s by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Issue synchronized: %s", repoLink, titleLink)
|
||||
case api.HookIssueMilestoned:
|
||||
mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID)
|
||||
text = fmt.Sprintf("[%s] Issue milestoned to %s: %s by %s", repoLink,
|
||||
linkFormatter(mileStoneLink, p.Issue.Milestone.Title), titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Issue milestoned to %s: %s", repoLink,
|
||||
linkFormatter(mileStoneLink, p.Issue.Milestone.Title), titleLink)
|
||||
case api.HookIssueDemilestoned:
|
||||
text = fmt.Sprintf("[%s] Issue milestone cleared: %s by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Issue milestone cleared: %s", repoLink, titleLink)
|
||||
}
|
||||
if withSender {
|
||||
text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName))
|
||||
}
|
||||
|
||||
var attachmentText string
|
||||
|
@ -73,8 +74,7 @@ func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter) (str
|
|||
return text, issueTitle, attachmentText, color
|
||||
}
|
||||
|
||||
func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkFormatter) (string, string, string, int) {
|
||||
senderLink := linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||
func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkFormatter, withSender bool) (string, string, string, int) {
|
||||
repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||
issueTitle := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
|
||||
titleLink := linkFormatter(p.PullRequest.URL, issueTitle)
|
||||
|
@ -83,43 +83,45 @@ func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkForm
|
|||
|
||||
switch p.Action {
|
||||
case api.HookIssueOpened:
|
||||
text = fmt.Sprintf("[%s] Pull request %s opened by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Pull request opened: %s", repoLink, titleLink)
|
||||
color = greenColor
|
||||
case api.HookIssueClosed:
|
||||
if p.PullRequest.HasMerged {
|
||||
text = fmt.Sprintf("[%s] Pull request %s merged by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Pull request merged: %s", repoLink, titleLink)
|
||||
color = purpleColor
|
||||
} else {
|
||||
text = fmt.Sprintf("[%s] Pull request %s closed by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Pull request closed: %s", repoLink, titleLink)
|
||||
color = redColor
|
||||
}
|
||||
case api.HookIssueReOpened:
|
||||
text = fmt.Sprintf("[%s] Pull request %s re-opened by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Pull request re-opened: %s", repoLink, titleLink)
|
||||
case api.HookIssueEdited:
|
||||
text = fmt.Sprintf("[%s] Pull request %s edited by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Pull request edited: %s", repoLink, titleLink)
|
||||
case api.HookIssueAssigned:
|
||||
list := make([]string, len(p.PullRequest.Assignees))
|
||||
for i, user := range p.PullRequest.Assignees {
|
||||
list[i] = linkFormatter(setting.AppURL+user.UserName, user.UserName)
|
||||
}
|
||||
text = fmt.Sprintf("[%s] Pull request %s assigned to %s by %s", repoLink,
|
||||
strings.Join(list, ", "),
|
||||
titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Pull request assigned: %s to %s", repoLink,
|
||||
strings.Join(list, ", "), titleLink)
|
||||
color = greenColor
|
||||
case api.HookIssueUnassigned:
|
||||
text = fmt.Sprintf("[%s] Pull request %s unassigned by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Pull request unassigned: %s", repoLink, titleLink)
|
||||
case api.HookIssueLabelUpdated:
|
||||
text = fmt.Sprintf("[%s] Pull request %s labels updated by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Pull request labels updated: %s", repoLink, titleLink)
|
||||
case api.HookIssueLabelCleared:
|
||||
text = fmt.Sprintf("[%s] Pull request %s labels cleared by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Pull request labels cleared: %s", repoLink, titleLink)
|
||||
case api.HookIssueSynchronized:
|
||||
text = fmt.Sprintf("[%s] Pull request %s synchronized by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Pull request synchronized: %s", repoLink, titleLink)
|
||||
case api.HookIssueMilestoned:
|
||||
mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID)
|
||||
text = fmt.Sprintf("[%s] Pull request %s milestoned to %s by %s", repoLink,
|
||||
linkFormatter(mileStoneLink, p.PullRequest.Milestone.Title), titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Pull request milestoned: %s to %s", repoLink,
|
||||
linkFormatter(mileStoneLink, p.PullRequest.Milestone.Title), titleLink)
|
||||
case api.HookIssueDemilestoned:
|
||||
text = fmt.Sprintf("[%s] Pull request %s milestone cleared by %s", repoLink, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Pull request milestone cleared: %s", repoLink, titleLink)
|
||||
}
|
||||
if withSender {
|
||||
text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName))
|
||||
}
|
||||
|
||||
var attachmentText string
|
||||
|
@ -130,28 +132,29 @@ func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkForm
|
|||
return text, issueTitle, attachmentText, color
|
||||
}
|
||||
|
||||
func getReleasePayloadInfo(p *api.ReleasePayload, linkFormatter linkFormatter) (text string, color int) {
|
||||
senderLink := linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||
func getReleasePayloadInfo(p *api.ReleasePayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
|
||||
repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||
refLink := linkFormatter(p.Repository.HTMLURL+"/src/"+p.Release.TagName, p.Release.TagName)
|
||||
|
||||
switch p.Action {
|
||||
case api.HookReleasePublished:
|
||||
text = fmt.Sprintf("[%s] Release %s created by %s", repoLink, refLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Release created: %s", repoLink, refLink)
|
||||
color = greenColor
|
||||
case api.HookReleaseUpdated:
|
||||
text = fmt.Sprintf("[%s] Release %s updated by %s", repoLink, refLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Release updated: %s", repoLink, refLink)
|
||||
color = yellowColor
|
||||
case api.HookReleaseDeleted:
|
||||
text = fmt.Sprintf("[%s] Release %s deleted by %s", repoLink, refLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Release deleted: %s", repoLink, refLink)
|
||||
color = redColor
|
||||
}
|
||||
if withSender {
|
||||
text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName))
|
||||
}
|
||||
|
||||
return text, color
|
||||
}
|
||||
|
||||
func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFormatter) (string, string, int) {
|
||||
senderLink := linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
|
||||
func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFormatter, withSender bool) (string, string, int) {
|
||||
repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
|
||||
issueTitle := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
|
||||
|
||||
|
@ -168,18 +171,21 @@ func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFo
|
|||
|
||||
switch p.Action {
|
||||
case api.HookIssueCommentCreated:
|
||||
text = fmt.Sprintf("[%s] New comment on %s %s by %s", repoLink, typ, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] New comment on %s %s", repoLink, typ, titleLink)
|
||||
if p.IsPull {
|
||||
color = greenColorLight
|
||||
} else {
|
||||
color = orangeColorLight
|
||||
}
|
||||
case api.HookIssueCommentEdited:
|
||||
text = fmt.Sprintf("[%s] Comment on %s %s edited by %s", repoLink, typ, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Comment edited on %s %s", repoLink, typ, titleLink)
|
||||
case api.HookIssueCommentDeleted:
|
||||
text = fmt.Sprintf("[%s] Comment on %s %s deleted by %s", repoLink, typ, titleLink, senderLink)
|
||||
text = fmt.Sprintf("[%s] Comment deleted on %s %s", repoLink, typ, titleLink)
|
||||
color = redColor
|
||||
}
|
||||
if withSender {
|
||||
text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName))
|
||||
}
|
||||
|
||||
return text, issueTitle, color
|
||||
}
|
||||
|
|
|
@ -266,7 +266,7 @@ func getMSTeamsPushPayload(p *api.PushPayload) (*MSTeamsPayload, error) {
|
|||
}
|
||||
|
||||
func getMSTeamsIssuesPayload(p *api.IssuePayload) (*MSTeamsPayload, error) {
|
||||
text, _, attachmentText, color := getIssuesPayloadInfo(p, noneLinkFormatter)
|
||||
text, _, attachmentText, color := getIssuesPayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
return &MSTeamsPayload{
|
||||
Type: "MessageCard",
|
||||
|
@ -308,7 +308,7 @@ func getMSTeamsIssuesPayload(p *api.IssuePayload) (*MSTeamsPayload, error) {
|
|||
}
|
||||
|
||||
func getMSTeamsIssueCommentPayload(p *api.IssueCommentPayload) (*MSTeamsPayload, error) {
|
||||
text, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter)
|
||||
text, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
return &MSTeamsPayload{
|
||||
Type: "MessageCard",
|
||||
|
@ -350,7 +350,7 @@ func getMSTeamsIssueCommentPayload(p *api.IssueCommentPayload) (*MSTeamsPayload,
|
|||
}
|
||||
|
||||
func getMSTeamsPullRequestPayload(p *api.PullRequestPayload) (*MSTeamsPayload, error) {
|
||||
text, _, attachmentText, color := getPullRequestPayloadInfo(p, noneLinkFormatter)
|
||||
text, _, attachmentText, color := getPullRequestPayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
return &MSTeamsPayload{
|
||||
Type: "MessageCard",
|
||||
|
@ -503,7 +503,7 @@ func getMSTeamsRepositoryPayload(p *api.RepositoryPayload) (*MSTeamsPayload, err
|
|||
}
|
||||
|
||||
func getMSTeamsReleasePayload(p *api.ReleasePayload) (*MSTeamsPayload, error) {
|
||||
text, color := getReleasePayloadInfo(p, noneLinkFormatter)
|
||||
text, color := getReleasePayloadInfo(p, noneLinkFormatter, false)
|
||||
|
||||
return &MSTeamsPayload{
|
||||
Type: "MessageCard",
|
||||
|
|
|
@ -144,7 +144,7 @@ func getSlackForkPayload(p *api.ForkPayload, slack *SlackMeta) (*SlackPayload, e
|
|||
}
|
||||
|
||||
func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||
text, issueTitle, attachmentText, color := getIssuesPayloadInfo(p, SlackLinkFormatter)
|
||||
text, issueTitle, attachmentText, color := getIssuesPayloadInfo(p, SlackLinkFormatter, true)
|
||||
|
||||
pl := &SlackPayload{
|
||||
Channel: slack.Channel,
|
||||
|
@ -167,7 +167,7 @@ func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload
|
|||
}
|
||||
|
||||
func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||
text, issueTitle, color := getIssueCommentPayloadInfo(p, SlackLinkFormatter)
|
||||
text, issueTitle, color := getIssueCommentPayloadInfo(p, SlackLinkFormatter, true)
|
||||
|
||||
return &SlackPayload{
|
||||
Channel: slack.Channel,
|
||||
|
@ -184,7 +184,7 @@ func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (
|
|||
}
|
||||
|
||||
func getSlackReleasePayload(p *api.ReleasePayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||
text, _ := getReleasePayloadInfo(p, SlackLinkFormatter)
|
||||
text, _ := getReleasePayloadInfo(p, SlackLinkFormatter, true)
|
||||
|
||||
return &SlackPayload{
|
||||
Channel: slack.Channel,
|
||||
|
@ -239,7 +239,7 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e
|
|||
}
|
||||
|
||||
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||
text, issueTitle, attachmentText, color := getPullRequestPayloadInfo(p, SlackLinkFormatter)
|
||||
text, issueTitle, attachmentText, color := getPullRequestPayloadInfo(p, SlackLinkFormatter, true)
|
||||
|
||||
pl := &SlackPayload{
|
||||
Channel: slack.Channel,
|
||||
|
|
|
@ -70,7 +70,7 @@ func TestSlackReleasePayload(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
require.NotNil(t, pl)
|
||||
|
||||
assert.Equal(t, "[<http://localhost:3000/test/repo|test/repo>] Release <http://localhost:3000/test/repo/src/v1.0|v1.0> created by <https://try.gitea.io/user1|user1>", pl.Text)
|
||||
assert.Equal(t, "[<http://localhost:3000/test/repo|test/repo>] Release created: <http://localhost:3000/test/repo/src/v1.0|v1.0> by <https://try.gitea.io/user1|user1>", pl.Text)
|
||||
}
|
||||
|
||||
func TestSlackPullRequestPayload(t *testing.T) {
|
||||
|
@ -84,5 +84,5 @@ func TestSlackPullRequestPayload(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
require.NotNil(t, pl)
|
||||
|
||||
assert.Equal(t, "[<http://localhost:3000/test/repo|test/repo>] Pull request <http://localhost:3000/test/repo/pulls/12|#2 Fix bug> opened by <https://try.gitea.io/user1|user1>", pl.Text)
|
||||
assert.Equal(t, "[<http://localhost:3000/test/repo|test/repo>] Pull request opened: <http://localhost:3000/test/repo/pulls/12|#2 Fix bug> by <https://try.gitea.io/user1|user1>", pl.Text)
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ func getTelegramPushPayload(p *api.PushPayload) (*TelegramPayload, error) {
|
|||
}
|
||||
|
||||
func getTelegramIssuesPayload(p *api.IssuePayload) (*TelegramPayload, error) {
|
||||
text, _, attachmentText, _ := getIssuesPayloadInfo(p, htmlLinkFormatter)
|
||||
text, _, attachmentText, _ := getIssuesPayloadInfo(p, htmlLinkFormatter, true)
|
||||
|
||||
return &TelegramPayload{
|
||||
Message: text + "\n\n" + attachmentText,
|
||||
|
@ -133,7 +133,7 @@ func getTelegramIssuesPayload(p *api.IssuePayload) (*TelegramPayload, error) {
|
|||
}
|
||||
|
||||
func getTelegramIssueCommentPayload(p *api.IssueCommentPayload) (*TelegramPayload, error) {
|
||||
text, _, _ := getIssueCommentPayloadInfo(p, htmlLinkFormatter)
|
||||
text, _, _ := getIssueCommentPayloadInfo(p, htmlLinkFormatter, true)
|
||||
|
||||
return &TelegramPayload{
|
||||
Message: text + "\n" + p.Comment.Body,
|
||||
|
@ -141,7 +141,7 @@ func getTelegramIssueCommentPayload(p *api.IssueCommentPayload) (*TelegramPayloa
|
|||
}
|
||||
|
||||
func getTelegramPullRequestPayload(p *api.PullRequestPayload) (*TelegramPayload, error) {
|
||||
text, _, attachmentText, _ := getPullRequestPayloadInfo(p, htmlLinkFormatter)
|
||||
text, _, attachmentText, _ := getPullRequestPayloadInfo(p, htmlLinkFormatter, true)
|
||||
|
||||
return &TelegramPayload{
|
||||
Message: text + "\n" + attachmentText,
|
||||
|
@ -166,7 +166,7 @@ func getTelegramRepositoryPayload(p *api.RepositoryPayload) (*TelegramPayload, e
|
|||
}
|
||||
|
||||
func getTelegramReleasePayload(p *api.ReleasePayload) (*TelegramPayload, error) {
|
||||
text, _ := getReleasePayloadInfo(p, htmlLinkFormatter)
|
||||
text, _ := getReleasePayloadInfo(p, htmlLinkFormatter, true)
|
||||
|
||||
return &TelegramPayload{
|
||||
Message: text + "\n",
|
||||
|
|
|
@ -10,6 +10,7 @@ link_account = Link Account
|
|||
register = Register
|
||||
website = Website
|
||||
version = Version
|
||||
powered_by = Powered by %s
|
||||
page = Page
|
||||
template = Template
|
||||
language = Language
|
||||
|
@ -1054,6 +1055,7 @@ pulls.is_checking = "Merge conflict checking is in progress. Try again in few mo
|
|||
pulls.required_status_check_failed = Some required checks were not successful.
|
||||
pulls.required_status_check_administrator = As an administrator, you may still merge this pull request.
|
||||
pulls.blocked_by_approvals = "This Pull Request doesn't have enough approvals yet. %d of %d approvals granted."
|
||||
pulls.blocked_by_rejection = "This Pull Request has changes requested by an official reviewer."
|
||||
pulls.can_auto_merge_desc = This pull request can be merged automatically.
|
||||
pulls.cannot_auto_merge_desc = This pull request cannot be merged automatically due to conflicts.
|
||||
pulls.cannot_auto_merge_helper = Merge manually to resolve the conflicts.
|
||||
|
@ -1417,6 +1419,8 @@ settings.update_protect_branch_success = Branch protection for branch '%s' has b
|
|||
settings.remove_protected_branch_success = Branch protection for branch '%s' has been disabled.
|
||||
settings.protected_branch_deletion = Disable Branch Protection
|
||||
settings.protected_branch_deletion_desc = Disabling branch protection allows users with write permission to push to the branch. Continue?
|
||||
settings.block_rejected_reviews = Block merge on rejected reviews
|
||||
settings.block_rejected_reviews_desc = Merging will not be possible when changes are requested by official reviewers, even if there are enough approvals.
|
||||
settings.default_branch_desc = Select a default repository branch for pull requests and code commits:
|
||||
settings.choose_branch = Choose a branch…
|
||||
settings.no_protected_branch = There are no protected branches.
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,6 +10,7 @@ link_account=Saistītie konti
|
|||
register=Reģistrēties
|
||||
website=Mājas lapa
|
||||
version=Versija
|
||||
powered_by=Darbina %s
|
||||
page=Lapa
|
||||
template=Sagatave
|
||||
language=Valoda
|
||||
|
@ -959,6 +960,7 @@ issues.add_time=Manuāli pievienot laiku
|
|||
issues.add_time_short=Pievienot laiku
|
||||
issues.add_time_cancel=Atcelt
|
||||
issues.add_time_history=` pievienoja patērēto laiku %s`
|
||||
issues.del_time_history=`dzēsts patērētais laiks %s`
|
||||
issues.add_time_hours=Stundas
|
||||
issues.add_time_minutes=Minūtes
|
||||
issues.add_time_sum_to_small=Nav norādīts laiks.
|
||||
|
@ -1052,6 +1054,7 @@ pulls.is_checking=Notiek konfliktu pārbaude, mirkli uzgaidiet un atjaunojiet la
|
|||
pulls.required_status_check_failed=Dažas no pārbaudēm nebija veiksmīgas.
|
||||
pulls.required_status_check_administrator=Kā administrators Jūs varat sapludināt šo izmaiņu pieprasījumu.
|
||||
pulls.blocked_by_approvals=Šim izmaiņu pieprasījumam nav nepieciešamais apstiprinājumu daudzums. %d no %d apstiprinājumi piešķirti.
|
||||
pulls.blocked_by_rejection=Šo izmaiņu pieprasījumu nevar sapludināt, jo tam ir peprasītas izmaiņas.
|
||||
pulls.can_auto_merge_desc=Šo izmaiņu pieprasījumu var automātiski sapludināt.
|
||||
pulls.cannot_auto_merge_desc=Šis izmaiņu pieprasījums nevar tikt automātiski sapludināts konfliktu dēļ.
|
||||
pulls.cannot_auto_merge_helper=Sapludiniet manuāli, lai atrisinātu konfliktus.
|
||||
|
@ -1415,6 +1418,8 @@ settings.update_protect_branch_success=Atzara aizsardzība atzaram '%s' tika sag
|
|||
settings.remove_protected_branch_success=Atzara aizsardzība atzaram '%s' tika atspējota.
|
||||
settings.protected_branch_deletion=Atspējot atzara aizsardzību
|
||||
settings.protected_branch_deletion_desc=Atspējojot atzara aizsardzību, ļaus lietotājiem ar rakstīšanas tiesībām nosūtīt izmaiņas uz atzaru. Vai turpināt?
|
||||
settings.block_rejected_reviews=Neļaut sapludināt izmaiņu pieprasījumus, kam ir pieprasītas izmaiņas
|
||||
settings.block_rejected_reviews_desc=Sapludināšana nebūs iespējama, kad ir pieprasītas izmaiņas, pat ja ir nepieciešamais apstiprinājumu skaits.
|
||||
settings.default_branch_desc=Norādiet noklusēto repozitorija atzaru izmaiņu pieprasījumiem un koda revīzijām:
|
||||
settings.choose_branch=Izvēlieties atzaru…
|
||||
settings.no_protected_branch=Nav neviena aizsargātā atzara.
|
||||
|
@ -2046,6 +2051,7 @@ create_pull_request=`izveidoja izmaiņu pieprasījumu <a href="%s/pulls/%s">%s#%
|
|||
close_pull_request=`aizvēra izmaiņu pieprasījumu <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reopen_pull_request=`atkārtoti atvēra izmaiņu pieprasījumu <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
comment_issue=`pievienoja komentāru problēmai <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
comment_pull=`komentēja izmaiņu pieprasījumu <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
merge_pull_request=`sapludināja izmaiņu pieprasījumu <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
transfer_repo=mainīja repozitorija <code>%s</code> īpašnieku uz <a href="%s">%s</a>
|
||||
push_tag=pievienoja tagu <a href="%s/src/tag/%s">%[2]s</a> repozitorijam <a href="%[1]s">%[3]s</a>
|
||||
|
|
|
@ -1,76 +1,759 @@
|
|||
home=പൂമുഖം
|
||||
dashboard=ഡാഷ്ബോർഡ്
|
||||
explore=കണ്ടെത്തൂ
|
||||
help=സഹായം
|
||||
sign_in=പ്രവേശിക്കുക
|
||||
sign_in_with=ഉപയോഗിച്ചു് പ്രവേശിയ്ക്കുക
|
||||
sign_out=പുറത്തുകടക്കുക
|
||||
sign_up=രജിസ്റ്റർ
|
||||
link_account=അക്കൌണ്ട് ബന്ധിപ്പിയ്ക്കുക
|
||||
register=രജിസ്റ്റർ
|
||||
website=വെബ് സൈറ്റ്
|
||||
version=പതിപ്പ്
|
||||
page=പേജ്
|
||||
template=ടെംപ്ലേറ്റ്
|
||||
language=ഭാഷ
|
||||
notifications=അറിയിപ്പുകൾ
|
||||
create_new=സൃഷ്ടിക്കുക…
|
||||
user_profile_and_more=പ്രൊഫൈലും ക്രമീകരണങ്ങളും…
|
||||
signed_in_as=ഇയാളായി പ്രവേശിയ്ക്കുക
|
||||
enable_javascript=ഈ വെബ്സൈറ്റ് ജാവാസ്ക്രിപ്റ്റിനൊപ്പം മികച്ച രീതിയിൽ പ്രവർത്തിക്കുന്നു.
|
||||
|
||||
username=ഉപയോക്ത്രു നാമം
|
||||
email=ഈമെയില് വിലാസം
|
||||
password=രഹസ്യവാക്കു്
|
||||
re_type=രഹസ്യവാക്കു് വീണ്ടും നല്കുക
|
||||
captcha=ക്യാപ്ച
|
||||
twofa=ഇരട്ട ഘടക പ്രാമാണീകരണം
|
||||
twofa_scratch=ഇരട്ട ഫാക്ടർ സ്ക്രാച്ച് കോഡ്
|
||||
passcode=രഹസ്യ കോഡ്
|
||||
|
||||
u2f_insert_key=സെക്യൂരിറ്റി കീ ഇന്സേര്ട്ടു് ചെയ്യുക
|
||||
u2f_sign_in=സെക്യൂരിറ്റി കീയിലെ ബട്ടണ് അമര്ത്തുക. സെക്യൂരിറ്റി കീയില് ബട്ടണൊന്നും ഇല്ലെങ്കില് വീണ്ടും ഇന്സേര്ട്ടു് ചെയ്യുക.
|
||||
u2f_press_button=ദയവായി സെക്യൂരിറ്റി കീയിലെ ബട്ടണ് അമര്ത്തൂ…
|
||||
u2f_use_twofa=നിങ്ങളുടെ ഫോണിൽ നിന്നുള്ള ഇരട്ട-ഘടക കോഡ് ഉപയോഗിക്കുക
|
||||
u2f_error=സെക്യൂരിറ്റി കീ വായിയ്ക്കാനാകുന്നില്ല.
|
||||
u2f_unsupported_browser=നീങ്ങളുടെ ബ്രൗസര് ഇരട്ട ഘടക സെക്യൂരിറ്റി പിന്തുണയ്ക്കുന്നില്ല.
|
||||
u2f_error_1=ഒരു അവിചാരിതമായ പിശക് സംഭവിച്ചു. ദയവായി വീണ്ടും ശ്രമിക്കുക.
|
||||
u2f_error_2=നിങ്ങള് ഉപയോഗിക്കുന്നത് ശരിയായ, എൻക്രിപ്റ്റ് ചെയ്ത (https://) യുആർഎൽ ആണെന്നു ദയവായി ഉറപ്പാക്കുക.
|
||||
u2f_error_3=നിങ്ങളുടെ അഭ്യർത്ഥന പ്രോസസ്സ് ചെയ്യാൻ സെർവറിന് കഴിഞ്ഞില്ല.
|
||||
u2f_error_4=ഈ അഭ്യർത്ഥന പൂര്ത്തിയാക്കാന് സുരക്ഷാ കീ അനുവദനിയ്ക്കുന്നില്ല. ഈ കീ ഇതിനോടകം രജിസ്റ്റർ ചെയ്തിട്ടില്ലെന്ന് ഉറപ്പു വരുത്തുക.
|
||||
u2f_error_5=നിങ്ങളുടെ കീ വായിക്കുന്നതിന് പൂര്ത്തിയാക്കാനായില്ല. ദയവായി ഈ പേജ് പുതുക്കി വീണ്ടും ശ്രമിക്കുക.
|
||||
u2f_reload=പുതുക്കുക
|
||||
|
||||
repository=കലവറ
|
||||
organization=സംഘടന
|
||||
mirror=മിറര്
|
||||
new_repo=പുതിയ കലവറ
|
||||
new_migrate=പുതിയ കുടിയേറ്റിപ്പാര്പ്പിക്കല്
|
||||
new_mirror=പുതിയ മിറര്
|
||||
new_fork=കലവറയുടെ പുതിയ ശിഖരം
|
||||
new_org=പുതിയ സംഘടന
|
||||
manage_org=സംഘടനകളെ നിയന്ത്രിക്കുക
|
||||
admin_panel=സൈറ്റിന്റെ കാര്യനിര്വ്വാഹണം
|
||||
account_settings=അക്കൌണ്ട് ക്രമീകരണങള്
|
||||
settings=ക്രമീകരണങ്ങള്
|
||||
your_profile=പ്രൊഫൈൽ
|
||||
your_starred=നക്ഷത്ര ചിഹ്നമിട്ടവ
|
||||
your_settings=ക്രമീകരണങ്ങള്
|
||||
|
||||
all=എല്ലാം
|
||||
sources=ഉറവിടങ്ങൾ
|
||||
mirrors=മിററുകള്
|
||||
collaborative=സഹകരിക്കുന്ന
|
||||
forks=ശാഖകള്
|
||||
|
||||
activities=പ്രവര്ത്തനങ്ങള്
|
||||
pull_requests=ലയന അഭ്യർത്ഥനകൾ
|
||||
issues=പ്രശ്നങ്ങൾ
|
||||
|
||||
cancel=റദ്ദാക്കുക
|
||||
|
||||
write=എഴുതുക
|
||||
preview=തിരനോട്ടം
|
||||
loading=ലഭ്യമാക്കുന്നു…
|
||||
|
||||
[startpage]
|
||||
|
||||
[install]
|
||||
install=സന്നിവേശിപ്പിയ്ക്കുക
|
||||
title=പ്രാരംഭ ക്രമീകരണങ്ങള്
|
||||
docker_helper=ഡോക്കറിനുള്ളിലാണ് ഗിറ്റീ പ്രവര്ത്തിപ്പിയ്ക്കുന്നതെങ്കില്, മാറ്റങ്ങള് വരുത്തുന്നതിനു മുമ്പു് ദയവായി <a target="_blank" rel="noopener noreferrer" href="%s">ഡോക്യുമെന്റേഷൻ</a> വായിയ്ക്കുക.
|
||||
requite_db_desc=ഗിറ്റീയ്ക്കു് MySQL, PostgreSQL, MSSQL അല്ലെങ്കിൽ SQLite3 ആവശ്യമാണ്.
|
||||
db_title=ഡാറ്റാബേസ് ക്രമീകരണങ്ങൾ
|
||||
db_type=ഡാറ്റാബേസിന്റെ തരം
|
||||
host=ഹോസ്റ്റ്
|
||||
user=ഉപയോക്ത്രു നാമം
|
||||
password=രഹസ്യവാക്കു്
|
||||
db_name=ഡാറ്റാബേസിന്റെ പേര്
|
||||
db_helper=MySQL ഉപയോക്താക്കൾക്കുള്ള കുറിപ്പ്: ദയവായി InnoDB സ്റ്റോറേജ് എഞ്ചിൻ ഉപയോഗിക്കുക. നിങ്ങൾ "utf8mb4" ഉപയോഗിക്കുകയാണെങ്കിൽ, InnoDB പതിപ്പ് 5.6 നേക്കാൾ വലുതായിരിക്കണം.
|
||||
ssl_mode=SSL
|
||||
charset=ക്യാര്സെറ്റ്
|
||||
path=പാത
|
||||
sqlite_helper=SQLite3 ഡാറ്റാബേസിന്റെ ഫയല് പാത്ത്.<br>നിങ്ങൾ ഗിറ്റീയെ ഒരു സേവനമായി പ്രവർത്തിപ്പിക്കുകയാണെങ്കിൽ സമ്പൂര്ണ്ണ ഫയല് പാത നൽകുക.
|
||||
err_empty_db_path=SQLite3 ഡാറ്റാബേസ് പാത്ത് ശൂന്യമായിരിക്കരുത്.
|
||||
no_admin_and_disable_registration=ഒരു അഡ്മിനിസ്ട്രേറ്റർ അക്കൌണ്ട് സൃഷ്ടിക്കാതെ നിങ്ങൾക്ക് ഉപയോക്തൃ സ്വയം രജിസ്ട്രേഷൻ അപ്രാപ്തമാക്കാൻ കഴിയില്ല.
|
||||
err_empty_admin_password=അഡ്മിനിസ്ട്രേറ്ററുടെ രഹസ്യവാക്കു് ശൂന്യമായിരിക്കരുത്.
|
||||
err_empty_admin_email=അഡ്മിനിസ്ട്രേറ്ററുടെ ഇമെയില് വിലാസം ശൂന്യമായിരിക്കരുത്.
|
||||
err_admin_name_is_reserved=അഡ്മിനിസ്ട്രേറ്റര് ഉപയോക്തൃനാമം അസാധുവാണ്, ഉപയോക്തൃനാമം റിസര്വ്വ് ചെയ്തതാണ്
|
||||
err_admin_name_pattern_not_allowed=അഡ്മിനിസ്ട്രേറ്റര് ഉപയോക്തൃനാമം അസാധുവാണ്, ഉപയോക്തൃനാമം അനുവദിനീയമല്ല
|
||||
err_admin_name_is_invalid=അഡ്മിനിസ്ട്രേറ്റർ ഉപയോക്തൃനാമം അസാധുവാണ്
|
||||
|
||||
general_title=പൊതുവായ ക്രമീകരണങ്ങൾ
|
||||
app_name=സൈറ്റ് ശീർഷകം
|
||||
app_name_helper=നിങ്ങളുടെ കമ്പനിയുടെ പേര് ഇവിടെ നൽകാം.
|
||||
repo_path=സംഭരണിയുടെ റൂട്ട് പാത്ത്
|
||||
repo_path_helper=വിദൂര ഗിറ്റു് സംഭരണികള് ഈ ഡയറക്ടറിയിലേക്ക് സംരക്ഷിക്കും.
|
||||
lfs_path=Git LFS റൂട്ട് പാത്ത്
|
||||
lfs_path_helper=Git LFS ട്രാക്കുചെയ്ത ഫയലുകൾ ഈ ഡയറക്ടറിയിൽ സൂക്ഷിക്കും. പ്രവർത്തനരഹിതമാക്കാൻ ഈ കളം ശൂന്യമായി വിടുക.
|
||||
run_user=ഉപയോക്താവായി പ്രവര്ത്തിപ്പിക്കുക
|
||||
run_user_helper=ഗിറ്റീ പ്രവർത്തിക്കുന്ന ഓപ്പറേറ്റിംഗ് സിസ്റ്റത്തിന്റെ ഉപയോക്തൃനാമം നല്കുക. ഈ ഉപയോക്താവിന് സംഭരണിയുടെ റൂട്ട് പാത്തിലേക്ക് പ്രവേശനം ഉണ്ടായിരിക്കണം.
|
||||
domain=SSH സെർവർ ഡൊമെയ്ൻ
|
||||
domain_helper=SSH ക്ലോൺ URL- കൾക്കായുള്ള ഡൊമെയ്ൻ അല്ലെങ്കിൽ ഹോസ്റ്റ് വിലാസം.
|
||||
ssh_port=SSH സെർവർ പോര്ട്ട്
|
||||
ssh_port_helper=നിങ്ങളുടെ SSH സെർവർ ശ്രവിക്കുന്ന പോർട്ട് നമ്പർ നല്കുക. പ്രവർത്തനരഹിതമാക്കാൻ കളം ശൂന്യമായി വിടുക.
|
||||
http_port=ഗിറ്റീ എച്ച്ടിടിപി ശ്രവിയ്ക്കുന്ന പോർട്ട്
|
||||
http_port_helper=ഗിറ്റീ വെബ് സെർവർ ശ്രവിയ്ക്കുന്ന പോർട്ട് നമ്പർ.
|
||||
app_url=ഗിറ്റീയുടെ അടിസ്ഥാന വിലാസം
|
||||
app_url_helper=എച്ച്ടിടിപി(എസ്) ക്ലോണുകള്ക്കും ഇമെയിൽ അറിയിപ്പുകൾക്കുമായുള്ള അടിസ്ഥാന വിലാസം.
|
||||
log_root_path=ലോഗ് പാത്ത്
|
||||
log_root_path_helper=ലോഗ് ഫയലുകൾ ഈ ഡയറക്ടറിയിലേക്ക് എഴുതപ്പെടും.
|
||||
|
||||
optional_title=ഐച്ഛികമായ ക്രമീകരണങ്ങൾ
|
||||
email_title=ഇമെയിൽ ക്രമീകരണങ്ങൾ
|
||||
smtp_host=SMTP ഹോസ്റ്റ്
|
||||
smtp_from=ഈ വിലാസത്തില് ഇമെയിൽ അയയ്ക്കുക
|
||||
smtp_from_helper=ഗിറ്റീ ഉപയോഗിയ്ക്കുന്ന ഇമെയില് വിലാസം. ഒരു സാധാ ഇമെയിൽ വിലാസം നൽകുക അല്ലെങ്കിൽ "പേര്"<email@example.com> എന്ന ഘടന ഉപയോഗിക്കുക.
|
||||
mailer_user=SMTP ഉപയോക്തൃനാമം
|
||||
mailer_password=SMTP രഹസ്യവാക്കു്
|
||||
register_confirm=രജിസ്റ്റർ ചെയ്യുന്നതിന് ഇമെയിൽ സ്ഥിരീകരണം ആവശ്യമാക്കുക
|
||||
mail_notify=ഇമെയിൽ അറിയിപ്പുകൾ പ്രാപ്തമാക്കുക
|
||||
server_service_title=സെർവറിന്റെയും മൂന്നാം കക്ഷി സേവനങ്ങളുടെയും ക്രമീകരണങ്ങള്
|
||||
offline_mode=പ്രാദേശിക മോഡ് പ്രവർത്തനക്ഷമമാക്കുക
|
||||
offline_mode_popup=മൂന്നാം കക്ഷി ഉള്ളടക്ക ഡെലിവറി നെറ്റ്വർക്കുകൾ അപ്രാപ്തമാക്കി എല്ലാ വിഭവങ്ങളും പ്രാദേശികമായി നല്കുക.
|
||||
disable_gravatar=ഗ്രവതാര് പ്രവർത്തനരഹിതമാക്കുക
|
||||
disable_gravatar_popup=ഗ്രവതാര് അല്ലെങ്കില് മൂന്നാം കക്ഷി അവതാർ ഉറവിടങ്ങൾ പ്രവർത്തനരഹിതമാക്കുക. ഒരു ഉപയോക്താവ് പ്രാദേശികമായി ഒരു അവതാർ അപ്ലോഡുചെയ്യുന്നില്ലെങ്കിൽ സ്ഥിരസ്ഥിതി അവതാർ ഉപയോഗിക്കും.
|
||||
federated_avatar_lookup=കേന്ദ്രീകൃത അവതാര് പ്രാപ്തമാക്കുക
|
||||
federated_avatar_lookup_popup=ലിബ്രാവതാർ ഉപയോഗിച്ച് കേന്ദ്രീക്രത അവതാർ തിരയൽ പ്രാപ്തമാക്കുക.
|
||||
disable_registration=സ്വയം രജിസ്ട്രേഷൻ അപ്രാപ്തമാക്കുക
|
||||
disable_registration_popup=ഉപയോക്താക്കള് സ്വയം രജിസ്റ്റര് ചെയ്യുന്നതു അപ്രാപ്യമാക്കുക. അഡ്മിനിസ്ട്രേറ്റർമാർക്ക് മാത്രമേ പുതിയ ഉപയോക്തൃ അക്കൌണ്ടുകൾ സൃഷ്ടിക്കാന് കഴിയൂ.
|
||||
allow_only_external_registration_popup=ബാഹ്യ സേവനങ്ങളിലൂടെ മാത്രം രജിസ്ട്രേഷന് അനുവദിക്കുക
|
||||
openid_signin=OpenID പ്രവേശനം പ്രവർത്തനക്ഷമമാക്കുക
|
||||
openid_signin_popup=OpenID വഴി ഉപയോക്തൃ പ്രവേശനം പ്രാപ്തമാക്കുക.
|
||||
openid_signup=OpenID സ്വയം രജിസ്ട്രേഷൻ പ്രാപ്തമാക്കുക
|
||||
openid_signup_popup=OpenID അടിസ്ഥാനമാക്കിയുള്ള ഉപയോക്തൃ സ്വയം രജിസ്ട്രേഷൻ പ്രാപ്തമാക്കുക.
|
||||
enable_captcha=ക്യാപ്ച പ്രാപ്തമാക്കുക
|
||||
enable_captcha_popup=ഉപയോക്താക്കള് സ്വയം രജിസ്ട്രേഷന് ചെയ്യുന്നതിനു് ഒരു ക്യാപ്ച ആവശ്യമാണ്.
|
||||
require_sign_in_view=പേജുകൾ കാണുന്നതിന് സൈറ്റില് പ്രവേശിക്കണം
|
||||
require_sign_in_view_popup=പേജ് ആക്സസ്സ്, പ്രവേശിച്ച ഉപയോക്താക്കൾക്കുമാത്രമായി പരിമിതപ്പെടുത്തുക. സന്ദർശകർ 'പ്രവേശനം', രജിസ്ട്രേഷൻ പേജുകൾ എന്നിവ മാത്രമേ കാണൂ.
|
||||
admin_setting_desc=ഒരു അഡ്മിനിസ്ട്രേറ്റര് അക്കൗണ്ട് സൃഷ്ടിക്കുന്നത് ഐച്ഛികമാണ്. ആദ്യം രജിസ്റ്റര് ചെയ്ത ഉപയോക്താവ് യാന്ത്രികമായി ഒരു അഡ്മിനിസ്ട്രേറ്ററായി മാറും.
|
||||
admin_title=അഡ്മിനിസ്ട്രേറ്റര് അക്കൗണ്ട് ക്രമീകരണങ്ങൾ
|
||||
admin_name=അഡ്മിനിസ്ട്രേറ്ററുടെ ഉപയോക്തൃനാമം
|
||||
admin_password=രഹസ്യവാക്കു്
|
||||
confirm_password=രഹസ്യവാക്കു് സ്ഥിരീകരിക്കുക
|
||||
admin_email=ഇ-മെയില് വിലാസം
|
||||
install_btn_confirm=ഗിറ്റീ സന്നിവേശിപ്പിയ്ക്കുക
|
||||
test_git_failed='git' കമാന്ഡ് പരീക്ഷിക്കാന് കഴിഞ്ഞില്ല: %v
|
||||
sqlite3_not_available=ഗിറ്റീയുടെ ഈ വേര്ഷന് SQLite3യെ പിന്തുണക്കുന്നില്ല. %s ൽ നിന്നും ഔദ്യോഗിക ബൈനറി പതിപ്പ് ഡൌണ്ലോഡ് ചെയ്യുക ('gobuild' പതിപ്പല്ല).
|
||||
invalid_db_setting=ഡാറ്റാബേസ് ക്രമീകരണങ്ങൾ അസാധുവാണ്: %v
|
||||
invalid_repo_path=കലവറയുടെ റൂട്ട് പാത്ത് അസാധുവാണ്: %v
|
||||
run_user_not_match='റൺ ആസ്' ഉപയോക്തൃനാമം നിലവിലെ ഉപയോക്തൃനാമമല്ല: %s -> %s
|
||||
save_config_failed=കോൺഫിഗറേഷൻ സംരക്ഷിക്കുന്നതിൽ പരാജയപ്പെട്ടു: %v
|
||||
invalid_admin_setting=അഡ്മിനിസ്ട്രേറ്റര് അക്കൌണ്ട് ക്രമീകരണം അസാധുവാണ്: %v
|
||||
install_success=സ്വാഗതം! ഗിറ്റീ തിരഞ്ഞെടുത്തതിന് നന്ദി. സൂക്ഷിക്കുക, ആസ്വദിക്കൂ,!
|
||||
invalid_log_root_path=ലോഗ് പാത്ത് അസാധുവാണ്: %v
|
||||
default_keep_email_private=സ്ഥിരസ്ഥിതിയായി ഇമെയില് വിലാസങ്ങള് മറയ്ക്കുക
|
||||
default_keep_email_private_popup=സ്ഥിരസ്ഥിതിയായി പുതിയ ഉപയോക്തൃ അക്കൗണ്ടുകളുടെ ഇമെയില് വിലാസങ്ങള് മറയ്ക്കുക.
|
||||
default_allow_create_organization=സ്ഥിരസ്ഥിതിയായി സംഘടനകള് സൃഷ്ടിക്കാന് അനുവദിക്കുക
|
||||
default_allow_create_organization_popup=സ്ഥിരസ്ഥിതിയായി സംഘടനകള് സൃഷ്ടിക്കാന് പുതിയ ഉപയോക്തൃ അക്കൗണ്ടുകളെ അനുവദിക്കുക.
|
||||
default_enable_timetracking=സ്ഥിരസ്ഥിതിയായി സമയം ട്രാക്കു് ചെയ്യുന്നതു പ്രാപ്തമാക്കുക
|
||||
default_enable_timetracking_popup=സ്ഥിരസ്ഥിതിയായി പുതിയ കലവറകള്ക്കു് സമയം ട്രാക്കു് ചെയ്യുന്നതു് പ്രാപ്തമാക്കുക.
|
||||
no_reply_address=മറച്ച ഇമെയിൽ ഡൊമെയ്ൻ
|
||||
no_reply_address_helper=മറഞ്ഞിരിക്കുന്ന ഇമെയിൽ വിലാസമുള്ള ഉപയോക്താക്കൾക്കുള്ള ഡൊമെയ്ൻ നാമം. ഉദാഹരണത്തിന്, മറഞ്ഞിരിക്കുന്ന ഇമെയിൽ ഡൊമെയ്ൻ 'noreply.example.org' ആയി സജ്ജീകരിച്ചിട്ടുണ്ടെങ്കിൽ 'joe' എന്ന ഉപയോക്താവു് 'joe@noreply.example.org' ആയി ലോഗിൻ ചെയ്യും.
|
||||
|
||||
[home]
|
||||
uname_holder=ഉപയോക്തൃനാമമോ ഇമെയിൽ വിലാസമോ
|
||||
password_holder=രഹസ്യവാക്കു്
|
||||
switch_dashboard_context=ഡാഷ്ബോർഡ് സന്ദർഭം മാറ്റുക
|
||||
my_repos=കലവറകള്
|
||||
show_more_repos=കൂടുതൽ കലവറകള് കാണിക്കുക…
|
||||
collaborative_repos=സഹകരിക്കാവുന്ന കലവറകള്
|
||||
my_orgs=എന്റെ സംഘടനകള്
|
||||
my_mirrors=എന്റെ മിററുകള്
|
||||
view_home=%s കാണുക
|
||||
search_repos=ഒരു കലവറ കണ്ടെത്തുക…
|
||||
|
||||
issues.in_your_repos=നിങ്ങളുടെ കലവറകളില്
|
||||
|
||||
[explore]
|
||||
repos=കലവറകള്
|
||||
users=ഉപയോക്താക്കള്
|
||||
organizations=സംഘടനകള്
|
||||
search=തിരയുക
|
||||
code=കോഡ്
|
||||
repo_no_results=പൊരുത്തപ്പെടുന്ന കലവറകളൊന്നും കണ്ടെത്താനായില്ല.
|
||||
user_no_results=പൊരുത്തപ്പെടുന്ന ഉപയോക്താക്കളെയൊന്നും കണ്ടെത്താനായില്ല.
|
||||
org_no_results=പൊരുത്തപ്പെടുന്ന സംഘടനകളൊന്നും കണ്ടെത്താനായില്ല.
|
||||
code_no_results=നിങ്ങളുടെ തിരയൽ പദവുമായി പൊരുത്തപ്പെടുന്ന സോഴ്സ് കോഡുകളൊന്നും കണ്ടെത്താനായില്ല.
|
||||
code_search_results=%s എന്നതിനായുള്ള തിരയൽ ഫലങ്ങൾ
|
||||
|
||||
[auth]
|
||||
create_new_account=അക്കൗണ്ട് രജിസ്റ്റർ ചെയ്യുക
|
||||
register_helper_msg=ഇതിനകം ഒരു അക്കൗണ്ട് ഉണ്ടോ? ഇപ്പോൾ പ്രവേശിക്കുക!
|
||||
social_register_helper_msg=ഇതിനകം ഒരു അക്കൗണ്ട് ഉണ്ടോ? ഇത് ഇപ്പോൾ ബന്ധിപ്പിയ്ക്കുക!
|
||||
disable_register_prompt=രജിസ്ട്രേഷൻ അപ്രാപ്തമാക്കി. നിങ്ങളുടെ സൈറ്റ് അഡ്മിനിസ്ട്രേറ്ററുമായി ബന്ധപ്പെടുക.
|
||||
disable_register_mail=രജിസ്ട്രേഷനായുള്ള ഇമെയിൽ സ്ഥിരീകരണം അപ്രാപ്തമാക്കി.
|
||||
remember_me=ഓര്മ്മിയ്ക്കുക
|
||||
forgot_password_title=അടയാളവാക്യം മറന്നുപോയോ
|
||||
forgot_password=അടയാള വാക്ക് ഓർക്കുന്നില്ലേ?
|
||||
sign_up_now=ഒരു അക്കൗണ്ട് ആവശ്യമുണ്ടോ? ഇപ്പോള് രജിസ്റ്റര് ചെയ്യുക.
|
||||
sign_up_successful=അക്കൗണ്ട് വിജയകരമായി സൃഷ്ടിച്ചു.
|
||||
confirmation_mail_sent_prompt=<b>%s</b> ലേക്ക് ഒരു പുതിയ സ്ഥിരീകരണ ഇമെയിൽ അയച്ചു. രജിസ്ട്രേഷൻ പ്രക്രിയ പൂർത്തിയാക്കുന്നതിന് അടുത്ത %s നുള്ളിൽ നിങ്ങളുടെ ഇൻബോക്സ് പരിശോധിക്കുക.
|
||||
must_change_password=നിങ്ങളുടെ രഹസ്യവാക്കു് പുതുക്കുക
|
||||
allow_password_change=രഹസ്യവാക്കു് മാറ്റാൻ ഉപയോക്താവിനോട് ആവശ്യപ്പെടുക (ശുപാർശിതം)
|
||||
reset_password_mail_sent_prompt=<b>%s</b> ലേക്ക് ഒരു പുതിയ സ്ഥിരീകരണ ഇമെയിൽ അയച്ചു. അക്കൗണ്ട് വീണ്ടെടുക്കൽ പ്രക്രിയ പൂർത്തിയാക്കുന്നതിന് അടുത്ത %s നുള്ളിൽ നിങ്ങളുടെ ഇൻബോക്സ് പരിശോധിക്കുക.
|
||||
active_your_account=നിങ്ങളുടെ അക്കൗണ്ട് സജീവമാക്കുക
|
||||
account_activated=നിങ്ങളുടെ അക്കൗണ്ട് സജീവമാക്കി
|
||||
prohibit_login=പ്രവേശനം നിരോധിച്ചിരിക്കുന്നു
|
||||
prohibit_login_desc=നിങ്ങളുടെ അക്കൗണ്ടിലേയ്ക്കുള്ള പ്രവേശനം നിരോധിച്ചിരിക്കുന്നു, ദയവായി നിങ്ങളുടെ സൈറ്റ് അഡ്മിനിസ്ട്രേറ്ററുമായി ബന്ധപ്പെടുക.
|
||||
resent_limit_prompt=നിങ്ങൾ അടുത്തിടെ ഒരു സജീവമാക്കൽ ഇമെയിൽ അഭ്യർത്ഥിച്ചു. 3 മിനിറ്റ് കാത്തിരുന്ന് വീണ്ടും ശ്രമിക്കുക.
|
||||
has_unconfirmed_mail=ഹായ് %s, നിങ്ങൾക്ക് സ്ഥിരീകരിക്കാത്ത ഇമെയിൽ വിലാസം (<b>%s</b>) ഉണ്ട്. നിങ്ങൾക്ക് ഒരു സ്ഥിരീകരണ ഇമെയിൽ ലഭിച്ചില്ലെങ്കിലോ പുതിയതൊന്ന് വീണ്ടും അയയ്ക്കേണ്ടതുണ്ടെങ്കിലോ, ചുവടെയുള്ള ബട്ടണിൽ ക്ലിക്കുചെയ്യുക.
|
||||
resend_mail=നിങ്ങളുടെ സജീവമാക്കൽ ഇമെയിൽ വീണ്ടും അയയ്ക്കാൻ ഇവിടെ ക്ലിക്കുചെയ്യുക
|
||||
email_not_associate=ഇമെയിൽ വിലാസം ഏതെങ്കിലും അക്കൗണ്ടുമായി ബന്ധപ്പെടുത്തിയിട്ടില്ല.
|
||||
send_reset_mail=അക്കൗണ്ട് വീണ്ടെടുക്കൽ ഇമെയിൽ അയയ്ക്കുക
|
||||
reset_password=അക്കൗണ്ട് വീണ്ടെടുക്കൽ
|
||||
invalid_code=നിങ്ങളുടെ സ്ഥിരീകരണ കോഡ് അസാധുവാണ് അല്ലെങ്കിൽ കാലഹരണപ്പെട്ടു.
|
||||
reset_password_helper=അക്കൗണ്ട് വീണ്ടെടുക്കുക
|
||||
reset_password_wrong_user=നിങ്ങൾ %s ആയി സൈൻ ഇൻ ചെയ്തു, പക്ഷേ അക്കൗണ്ട് വീണ്ടെടുക്കൽ ലിങ്ക് %s എന്നതിനാണ്
|
||||
password_too_short=പാസ്വേഡ് ദൈർഘ്യം %d അക്ഷരങ്ങളിലും കുറവായിരിക്കരുത്.
|
||||
non_local_account=പ്രാദേശിക ഇതര ഉപയോക്താക്കൾക്ക് ഗിറ്റീ വെബ് വഴി പാസ്വേഡ് പുതുക്കാന് ചെയ്യാൻ കഴിയില്ല.
|
||||
verify=പ്രമാണീകരിയ്ക്കുക
|
||||
scratch_code=സ്ക്രാച്ച് കോഡ്
|
||||
use_scratch_code=ഒരു സ്ക്രാച്ച് കോഡ് ഉപയോഗിക്കുക
|
||||
twofa_scratch_used=നിങ്ങളുടെ സ്ക്രാച്ച് കോഡ് ഉപയോഗിച്ചു. നിങ്ങളെ രണ്ട്-ഘടക ക്രമീകരണ പേജിലേക്ക് റീഡയറക്ട് ചെയ്തിരിക്കുന്നതിനാൽ നിങ്ങളുടെ ഉപകരണ എൻറോൾമെന്റ് നീക്കംചെയ്യാനോ പുതിയ സ്ക്രാച്ച് കോഡ് സൃഷ്ടിക്കാനോ കഴിയും.
|
||||
twofa_passcode_incorrect=നിങ്ങളുടെ പാസ്കോഡ് തെറ്റാണ്. നിങ്ങളുടെ ഉപകരണം തെറ്റായി സ്ഥാപിച്ചിട്ടുണ്ടെങ്കിൽ, പ്രവേശിക്കാൻ നിങ്ങളുടെ സ്ക്രാച്ച് കോഡ് ഉപയോഗിക്കുക.
|
||||
twofa_scratch_token_incorrect=നിങ്ങളുടെ സ്ക്രാച്ച് കോഡ് തെറ്റാണ്.
|
||||
login_userpass=പ്രവേശിക്കുക
|
||||
login_openid=OpenID
|
||||
oauth_signup_tab=പുതിയ അക്കൗണ്ട് രജിസ്റ്റർ ചെയ്യുക
|
||||
oauth_signup_title=ഇമെയിലും പാസ്വേഡും ചേർക്കുക (അക്കൗണ്ട് വീണ്ടെടുക്കലിനായി)
|
||||
oauth_signup_submit=അക്കൗണ്ട് പൂർത്തിയാക്കുക
|
||||
oauth_signin_tab=നിലവിലുള്ള അക്കൌണ്ടുമായി ബന്ധിപ്പിയ്ക്കുക
|
||||
oauth_signin_title=അക്കൗണ്ട് ബന്ധിപ്പിയ്ക്കുന്നതു് അംഗീകരിക്കുന്നതിനായി സൈറ്റിലേയ്ക്കു് പ്രവേശിക്കുക
|
||||
oauth_signin_submit=അക്കൌണ്ട് ബന്ധിപ്പിയ്ക്കുക
|
||||
openid_connect_submit=ബന്ധിപ്പിക്കുക
|
||||
openid_connect_title=നിലവിലുള്ള അക്കൗണ്ടുമായി ബന്ധിപ്പിയ്ക്കുക
|
||||
openid_connect_desc=തിരഞ്ഞെടുത്ത ഓപ്പൺഐഡി യുആർഐ അജ്ഞാതമാണ്. ഇവിടെ നിന്നും ഒരു പുതിയ അക്കൗണ്ടുമായി ബന്ധപ്പെടുത്തുക.
|
||||
openid_register_title=അംഗത്വമെടുക്കുക
|
||||
openid_register_desc=തിരഞ്ഞെടുത്ത ഓപ്പൺഐഡി യുആർഐ അജ്ഞാതമാണ്. ഇവിടെ നിന്നും ഒരു പുതിയ അക്കൗണ്ടുമായി ബന്ധപ്പെടുത്തുക.
|
||||
openid_signin_desc=നിങ്ങളുടെ OpenID URI നൽകുക. ഉദാഹരണത്തിന്: https://anne.me, bob.openid.org.cn അല്ലെങ്കിൽ gnusocial.net/carry.
|
||||
disable_forgot_password_mail=അക്കൗണ്ട് വീണ്ടെടുക്കൽ പ്രവർത്തനരഹിതമാണ്. നിങ്ങളുടെ സൈറ്റ് അഡ്മിനിസ്ട്രേറ്ററുമായി ബന്ധപ്പെടുക.
|
||||
email_domain_blacklisted=നിങ്ങളുടെ ഇമെയിൽ വിലാസത്തിൽ രജിസ്റ്റർ ചെയ്യാൻ കഴിയില്ല.
|
||||
authorize_application=അപ്ലിക്കേഷനു് അംഗീകാരം നല്കുക
|
||||
authorize_application_created_by=%s സൃഷ്ടിച്ച അപ്ലിക്കേഷൻ ആണ്.
|
||||
authorize_application_description=നിങ്ങൾ പ്രവേശനം അനുവദിക്കുകയാണെങ്കിൽ, സ്വകാര്യ റിപ്പോകളും ഓർഗനൈസേഷനുകളും ഉൾപ്പെടെ നിങ്ങളുടെ എല്ലാ അക്കൌണ്ട് വിവരങ്ങള് നേടാനും വേണമെങ്കില് മാറ്റങ്ങള് വരുത്താനും അതിന് കഴിയും.
|
||||
authorize_title=നിങ്ങളുടെ അക്കൌണ്ടില് പ്രവേശിയ്ക്കുന്നതിനു് "%s"നു് അംഗീകാരം നൽകണോ?
|
||||
authorization_failed=അംഗീകാരം നല്കുന്നതില് പരാജയപ്പെട്ടു
|
||||
authorization_failed_desc=അസാധുവായ ഒരു അഭ്യർത്ഥന കണ്ടെത്തിയതിനാൽ ഞങ്ങൾ അംഗീകാരം പരാജയപ്പെടുത്തി. ദയവായി നിങ്ങൾ അംഗീകരിക്കാൻ ശ്രമിച്ച അപ്ലിക്കേഷന്റെ പരിപാലകനുമായി ബന്ധപ്പെടുക.
|
||||
|
||||
[mail]
|
||||
activate_account=നിങ്ങളുടെ അക്കൗണ്ട് സജീവമാക്കുക
|
||||
activate_email=ഇമെയില് വിലാസം സ്ഥിരീകരിയ്ക്കുക
|
||||
reset_password=നിങ്ങളുടെ അക്കൗണ്ട് വീണ്ടെടുക്കുക
|
||||
register_success=രജിസ്ട്രേഷൻ വിജയകരം
|
||||
register_notify=ഗിറ്റീയിലേയ്ക്കു് സ്വാഗതം
|
||||
|
||||
[modal]
|
||||
yes=അതെ
|
||||
no=ഇല്ല
|
||||
modify=പുതുക്കുക
|
||||
|
||||
[form]
|
||||
UserName=ഉപയോക്ത്രു നാമം
|
||||
RepoName=കലവറയുടെ പേരു്
|
||||
Email=ഇ-മെയില് വിലാസം
|
||||
Password=രഹസ്യവാക്കു്
|
||||
Retype=രഹസ്യവാക്കു് വീണ്ടും നല്കുക
|
||||
SSHTitle=SSH കീയുടെ പേരു്
|
||||
HttpsUrl=HTTPS URL
|
||||
PayloadUrl=പേലോഡ് URL
|
||||
TeamName=ടീമിന്റെ പേരു്
|
||||
AuthName=അംഗീകാരത്തിന്റെ പേരു്
|
||||
AdminEmail=അഡ്മിൻ ഇമെയിൽ
|
||||
|
||||
NewBranchName=പുതിയ ശാഖയുടെ പേരു്
|
||||
CommitSummary=നിയോഗത്തിന്റെ സംഗ്രഹം
|
||||
CommitMessage=നിയോഗത്തിന്റെ സന്ദേശം
|
||||
CommitChoice=നിയോഗത്തിന്റെ തിരഞ്ഞെടുക്കല്
|
||||
TreeName=ഫയല് പാത്ത്
|
||||
Content=ഉള്ളടക്കം
|
||||
|
||||
|
||||
require_error=`ശൂന്യമായിരിക്കരുത്.`
|
||||
alpha_dash_error=`ആൽഫാന്യൂമെറിക്, ഡാഷ് ('-'), അടിവരയിട്ട ('_') എന്നീ ചിഹ്നങ്ങള് മാത്രം അടങ്ങിയിരിക്കണം.`
|
||||
alpha_dash_dot_error=`ആൽഫാന്യൂമെറിക്, ഡാഷ് ('-'), അടിവരയിടുക ('_'), ഡോട്ട് ('.') എന്നീ ച്ഹ്നങ്ങള് മാത്രം അടങ്ങിയിരിക്കണം.`
|
||||
git_ref_name_error=`നന്നായി രൂപപ്പെടുത്തിയ Git റഫറൻസ് നാമമായിരിക്കണം.`
|
||||
size_error=`വലുപ്പം %s ആയിരിക്കണം.`
|
||||
min_size_error=`കുറഞ്ഞത് %s അക്ഷരങ്ങള് അടങ്ങിയിരിക്കണം.`
|
||||
max_size_error=`പരമാവധി %s അക്ഷരങ്ങള് അടങ്ങിയിരിക്കണം.`
|
||||
email_error=സാധുവായ ഒരു ഈ-മെയിൽ വിലാസം അല്ല
|
||||
url_error=`സാധുവായ ഒരു URL അല്ല.`
|
||||
include_error=`%s'എന്ന ഉപവാക്യം അടങ്ങിയിരിക്കണം.`
|
||||
glob_pattern_error=ഗ്ലോബു് ശൃേണി തെറ്റാണു്: %s
|
||||
unknown_error=അജ്ഞാതമായ പിശക്:
|
||||
captcha_incorrect=ക്യാപ്ച കോഡ് തെറ്റാണ്.
|
||||
password_not_match=രഹസ്യവാക്കുകള് യോജിക്കുന്നില്ല.
|
||||
|
||||
username_been_taken=ഉപയോക്തൃനാമം ലഭ്യമല്ല.
|
||||
repo_name_been_taken=കലവറയുടെ പേരു് ഇതിനോടകം ഉപയോഗിച്ചിട്ടുണ്ടു്.
|
||||
visit_rate_limit=വിദൂര വിലാസം വിവരകൈമാറ്റത്തിനു് പരിധി നിശ്ചയിച്ചിട്ടുണ്ടു്.
|
||||
2fa_auth_required=വിദൂര വിലാസം ഇരട്ട ഘടക പ്രാമാണീകരണം ആവശ്യപ്പെടുന്നുണ്ടു്.
|
||||
org_name_been_taken=സംഘടനയുടെ പേര് ഇതിനകം എടുത്തിട്ടുണ്ട്.
|
||||
team_name_been_taken=ടീമിന്റെ പേര് ഇതിനകം എടുത്തിട്ടുണ്ട്.
|
||||
team_no_units_error=കുറഞ്ഞത് ഒരു കലവറ വിഭാഗത്തിലേക്ക് പ്രവേശനം അനുവദിക്കുക.
|
||||
email_been_used=ഈ ഇമെയിൽ വിലാസം ഇതിനു മുന്നേ എടുത്തിട്ടുണ്ട്.
|
||||
openid_been_used=%s എന്ന ഓപ്പണ്ഐഡി വിലാസം ഇതിനു മുന്നേ എടുത്തിട്ടുണ്ട്.
|
||||
username_password_incorrect=ഉപഭോക്തൃനാമമോ രഹസ്യവാക്കോ തെറ്റാണ്.
|
||||
enterred_invalid_repo_name=ഈ കവവറയുടെ പേരു് തെറ്റാണു്.
|
||||
enterred_invalid_owner_name=പുതിയ ഉടമസ്ഥന്റെ പേരു് സാധുവല്ല.
|
||||
enterred_invalid_password=താങ്കള് നല്കിയ രഹസ്യവാക്കു് തെറ്റാണ്.
|
||||
user_not_exist=ഉപയോക്താവ് നിലവിലില്ല.
|
||||
last_org_owner='ഉടമകളുടെ' ടീമിൽ നിന്നും അവസാനത്തെ ഉപയോക്താവിനെ നീക്കംചെയ്യാൻ നിങ്ങൾക്ക് കഴിയില്ല. ടീമിൽ കുറഞ്ഞത് ഒരു ഉടമയെങ്കിലും ഉണ്ടായിരിക്കണം.
|
||||
cannot_add_org_to_team=ഒരു സംഘടനയെ ടീം അംഗമായി ചേർക്കാൻ കഴിയില്ല.
|
||||
|
||||
invalid_ssh_key=നിങ്ങളുടെ SSH കീ സ്ഥിരീകരിക്കാൻ കഴിയില്ല: %s
|
||||
invalid_gpg_key=നിങ്ങളുടെ GPG കീ സ്ഥിരീകരിക്കാൻ കഴിയില്ല: %s
|
||||
unable_verify_ssh_key=SSH കീ സ്ഥിരീകരിക്കാൻ കഴിയില്ല; തെറ്റുകളുണ്ടോയെന്നു് ഒന്നുകൂടി പരിശോധിക്കുക.
|
||||
auth_failed=പ്രാമാണീകരണം പരാജയപ്പെട്ടു: %v
|
||||
|
||||
still_own_repo=നിങ്ങളുടെ അക്കൗണ്ടിന് ഒന്നോ അതിലധികമോ കലവറകള് ഉണ്ട്; ആദ്യം അവ ഇല്ലാതാക്കുക അല്ലെങ്കിൽ കൈമാറുക.
|
||||
still_has_org=നിങ്ങളുടെ അക്കൗണ്ട് ഒന്നോ അതിലധികമോ സംഘടനകളില് അംഗമാണ്; ആദ്യം അവ വിടുക.
|
||||
org_still_own_repo=നിങ്ങളുടെ സംഘടന ഇനിയും ഒന്നോ അതിലധികമോ കലവറകളുടെ ഉടമസ്ഥനാണു്; ആദ്യം അവ ഇല്ലാതാക്കുക അല്ലെങ്കിൽ കൈമാറുക.
|
||||
|
||||
|
||||
target_branch_not_exist=ലക്ഷ്യമാക്കിയ ശാഖ നിലവിലില്ല.
|
||||
|
||||
[user]
|
||||
change_avatar=നിങ്ങളുടെ അവതാർ മാറ്റുക…
|
||||
join_on=ചേർന്നതു്
|
||||
repositories=കലവറകള്
|
||||
activity=പൊതുവായ പ്രവർത്തനങ്ങള്
|
||||
followers=പിന്തുടരുന്നവര്
|
||||
starred=നക്ഷത്രമിട്ട കലവറകള്
|
||||
following=പിന്തുടരുന്നവര്
|
||||
follow=പിന്തുടരൂ
|
||||
unfollow=പിന്തുടരുന്നത് നിര്ത്തുക
|
||||
heatmap.loading=ഹീറ്റ്മാപ്പ് ലോഡുചെയ്യുന്നു…
|
||||
user_bio=ജീവചരിത്രം
|
||||
|
||||
form.name_reserved='%s' എന്ന ഉപയോക്തൃനാമം മറ്റാവശ്യങ്ങള്ക്കായി നീക്കിവച്ചിരിക്കുന്നു.
|
||||
form.name_pattern_not_allowed=ഉപയോക്തൃനാമത്തിൽ '%s' എന്ന ശ്രേണി അനുവദനീയമല്ല.
|
||||
|
||||
[settings]
|
||||
profile=പ്രൊഫൈൽ
|
||||
account=അക്കൗണ്ട്
|
||||
password=രഹസ്യവാക്കു്
|
||||
security=സുരക്ഷ
|
||||
avatar=അവതാര്
|
||||
ssh_gpg_keys=SSH / GPG കീകള്
|
||||
social=സോഷ്യൽ അക്കൗണ്ടുകൾ
|
||||
applications=അപ്ലിക്കേഷനുകൾ
|
||||
orgs=സംഘടനകളെ നിയന്ത്രിക്കുക
|
||||
repos=കലവറകള്
|
||||
delete=അക്കൗണ്ട് ഇല്ലാതാക്കുക
|
||||
twofa=ഇരട്ട ഘടക പ്രാമാണീകരണം
|
||||
account_link=ബന്ധിപ്പിച്ച അക്കൌണ്ടുകള്
|
||||
organization=സംഘടനകള്
|
||||
uid=Uid
|
||||
u2f=സുരക്ഷാ കീകൾ
|
||||
|
||||
public_profile=പരസ്യമായ പ്രൊഫൈൽ
|
||||
profile_desc=അറിയിപ്പുകൾക്കും മറ്റ് പ്രവർത്തനങ്ങൾക്കുമായി നിങ്ങളുടെ ഇമെയിൽ വിലാസം ഉപയോഗിക്കും.
|
||||
password_username_disabled=പ്രാദേശികമല്ലാത്ത ഉപയോക്താക്കൾക്ക് അവരുടെ ഉപയോക്തൃനാമം മാറ്റാൻ അനുവാദമില്ല. കൂടുതൽ വിവരങ്ങൾക്ക് നിങ്ങളുടെ സൈറ്റ് അഡ്മിനിസ്ട്രേറ്ററുമായി ബന്ധപ്പെടുക.
|
||||
full_name=പൂർണ്ണമായ പേര്
|
||||
website=വെബ് സൈറ്റ്
|
||||
location=സ്ഥലം
|
||||
update_theme=പ്രമേയം പുതുക്കുക
|
||||
update_profile=പ്രോഫൈല് പരിഷ്കരിക്കുക
|
||||
update_profile_success=നിങ്ങളുടെ പ്രൊഫൈൽ പരിഷ്കരിച്ചിരിക്കുന്നു.
|
||||
change_username=നിങ്ങളുടെ ഉപയോക്തൃനാമം മാറ്റി.
|
||||
change_username_prompt=കുറിപ്പ്: ഉപയോക്തൃനാമത്തിലെ മാറ്റം നിങ്ങളുടെ അക്കൗണ്ട് URLഉം മാറ്റുന്നു.
|
||||
continue=തുടരുക
|
||||
cancel=റദ്ദാക്കുക
|
||||
language=ഭാഷ
|
||||
ui=പ്രമേയങ്ങള്
|
||||
|
||||
lookup_avatar_by_mail=ഇമെയിൽ വിലാസം അനുസരിച്ച് അവതാർ കണ്ടെത്തുക
|
||||
federated_avatar_lookup=കേന്ദ്രീക്രത അവതാര് കണ്ടെത്തല്
|
||||
enable_custom_avatar=ഇഷ്ടാനുസൃത അവതാർ ഉപയോഗിക്കുക
|
||||
choose_new_avatar=പുതിയ അവതാർ തിരഞ്ഞെടുക്കുക
|
||||
update_avatar=അവതാർ പുതുക്കുക
|
||||
delete_current_avatar=നിലവിലെ അവതാർ ഇല്ലാതാക്കുക
|
||||
uploaded_avatar_not_a_image=അപ്ലോഡുചെയ്ത ഫയൽ ഒരു ചിത്രമല്ല.
|
||||
uploaded_avatar_is_too_big=അപ്ലോഡുചെയ്ത ഫയൽ പരമാവധി വലുപ്പം കവിഞ്ഞു.
|
||||
update_avatar_success=നിങ്ങളുടെ അവതാര് പരിഷ്കരിച്ചിരിക്കുന്നു.
|
||||
|
||||
change_password=പാസ്വേഡ് പുതുക്കുക
|
||||
old_password=നിലവിലുള്ള രഹസ്യവാക്കു്
|
||||
new_password=പുതിയ രഹസ്യവാക്കു്
|
||||
retype_new_password=പുതിയ രഹസ്യവാക്കു് വീണ്ടും നല്കുക
|
||||
password_incorrect=നിലവിലെ പാസ്വേഡ് തെറ്റാണ്.
|
||||
change_password_success=നിങ്ങളുടെ പാസ്വേഡ് അപ്ഡേറ്റുചെയ്തു. ഇനി മുതൽ നിങ്ങളുടെ പുതിയ പാസ്വേഡ് ഉപയോഗിച്ച് പ്രവേശിക്കുക.
|
||||
password_change_disabled=പ്രാദേശിക ഇതര ഉപയോക്താക്കൾക്ക് ഗിറ്റീ വെബ് വഴി പാസ്വേഡ് പുതുക്കാന് ചെയ്യാൻ കഴിയില്ല.
|
||||
|
||||
emails=ഇ-മെയില് വിലാസങ്ങള്
|
||||
manage_emails=ഇമെയിൽ വിലാസങ്ങൾ നിയന്ത്രിക്കുക
|
||||
manage_themes=സ്ഥിരസ്ഥിതി പ്രമേയം തിരഞ്ഞെടുക്കുക
|
||||
manage_openid=ഓപ്പൺഐഡി വിലാസങ്ങൾ നിയന്ത്രിക്കുക
|
||||
email_desc=അറിയിപ്പുകൾക്കും മറ്റ് പ്രവർത്തനങ്ങൾക്കുമായി നിങ്ങളുടെ പ്രാഥമിക ഇമെയിൽ വിലാസം ഉപയോഗിക്കും.
|
||||
theme_desc=സൈറ്റിലുടനീളം ഇത് നിങ്ങളുടെ സ്ഥിരസ്ഥിതി പ്രമേയം ആയിരിക്കും.
|
||||
primary=പ്രാഥമികം
|
||||
primary_email=പ്രാഥമികമാക്കുക
|
||||
delete_email=നീക്കം ചെയ്യുക
|
||||
email_deletion=ഈ-മെയില് വിലാസം നീക്കം ചെയ്യുക
|
||||
email_deletion_desc=ഇമെയിൽ വിലാസവും അനുബന്ധ വിവരങ്ങളും നിങ്ങളുടെ അക്കൗണ്ടിൽ നിന്ന് നീക്കംചെയ്യും. ഈ ഇമെയിൽ വിലാസം വഴിയുള്ള ഗിറ്റു് നിയോഗങ്ങളും മാറ്റമില്ലാതെ ഉണ്ടാകും. തുടരട്ടെ?
|
||||
email_deletion_success=ഇമെയിൽ വിലാസം നീക്കംചെയ്തു.
|
||||
theme_update_success=നിങ്ങളുടെ പ്രമേയം പുതുക്കി.
|
||||
theme_update_error=തിരഞ്ഞെടുത്ത പ്രമേയം നിലവിലില്ല.
|
||||
openid_deletion=OpenID വിലാസം നീക്കം ചെയ്യുക
|
||||
openid_deletion_desc=നിങ്ങളുടെ അക്കൗണ്ടിൽ നിന്ന് ഓപ്പൺഐഡി വിലാസം നീക്കംചെയ്യുന്നത് ഇതുപയോഗിച്ചു് ഇനി പ്രവേശിക്കുന്നതിൽ നിന്ന് നിങ്ങളെ തടയും. തുടരട്ടെ?
|
||||
openid_deletion_success=ഓപ്പൺഐഡി വിലാസം നീക്കംചെയ്തു.
|
||||
add_new_email=ഈ-മെയില് വിലാസം ചേര്ക്കുക
|
||||
add_new_openid=പുതിയ ഓപ്പണ് ഐഡി വിലാസം ചേര്ക്കുക
|
||||
add_email=ഈ-മെയില് വിലാസം ചേര്ക്കുക
|
||||
add_openid=ഓപ്പണ് ഐഡി വിലാസം ചേര്ക്കുക
|
||||
add_email_confirmation_sent=ഒരു സ്ഥിരീകരണ ഇമെയിൽ '%s' ലേക്ക് അയച്ചു. നിങ്ങളുടെ ഇമെയിൽ വിലാസം സ്ഥിരീകരിക്കുന്നതിന് അടുത്ത %s നുള്ളിൽ നിങ്ങളുടെ ഇൻബോക്സ് പരിശോധിക്കുക.
|
||||
add_email_success=പുതിയ ഇമെയിൽ വിലാസം ചേര്ത്തു.
|
||||
add_openid_success=പുതിയ ഓപ്പണ്ഐഡി വിലാസം ചേര്ത്തു.
|
||||
keep_email_private=ഈ-മെയില് വിലാസം മറയ്ക്കുക
|
||||
keep_email_private_popup=നിങ്ങളുടെ ഇമെയിൽ വിലാസം മറ്റ് ഉപയോക്താക്കു് കാണാനാകില്ല.
|
||||
openid_desc=ഒരു ബാഹ്യ ദാതാവിന് പ്രാമാണീകരണം നിയുക്തമാക്കാൻ ഓപ്പൺഐഡി നിങ്ങളെ അനുവദിക്കുന്നു.
|
||||
|
||||
manage_ssh_keys=എസ്. എസ്. എച്ച് കീകള് നിയന്ത്രിക്കുക
|
||||
manage_gpg_keys=ജീ പീ. ജി കീകള് നിയന്ത്രിക്കുക
|
||||
add_key=കീ ചേര്ക്കുക
|
||||
ssh_desc=ഇവയാണു് നിങ്ങളുടെ അക്കൗണ്ടുമായി ബന്ധപ്പെടുത്തിയിരിക്കുന്ന പൊതുവായ എസ്. എസ്. എച്ച് കീകൾ. ഇതിനോടനു ബന്ധിപ്പിച്ചിട്ടുള്ള സ്വകാര്യ കീകൾ നിങ്ങളുടെ കലവറകളിലേയ്ക്കു് പൂർണ്ണ ആക്സസ് അനുവദിക്കുന്നു.
|
||||
gpg_desc=ഈ പൊതു GPG കീകൾ നിങ്ങളുടെ അക്കൗണ്ടുമായി ബന്ധപ്പെട്ടിരിക്കുന്നു. കമ്മിറ്റുകളെ പരിശോധിച്ചുറപ്പിക്കാൻ നിങ്ങളുടെ സ്വകാര്യ കീകൾ അനുവദിക്കുന്നതിനാൽ അവ സുരക്ഷിതമായി സൂക്ഷിക്കുക.
|
||||
ssh_helper=<strong>സഹായം ആവശ്യമുണ്ടോ?</strong> <a href="%s"> നിങ്ങളുടെ സ്വന്തം SSH കീകൾ സൃഷ്ടിക്കുക,</a> അല്ലെങ്കിൽ <a href="%s"> പൊതുവായ പ്രശ്നങ്ങൾ </a> എന്നിവയ്ക്കായുള്ള ഗിറ്റ്ഹബ്ബിന്റെ മാര്ഗദര്ശനങ്ങള് ഉപയോഗിച്ചു് നിങ്ങൾക്ക് എസ്. എസ്. എച്ചുമായി ബന്ധപ്പെട്ട പ്രശ്നങ്ങള് പരിഹരിക്കാം.
|
||||
gpg_helper=<strong> സഹായം ആവശ്യമുണ്ടോ? </strong> ജിപിജിയെക്കുറിച്ച് ഗിറ്റ്ഹബിന്റെ മാര്ഗ്ഗനിര്ദ്ദേശങ്ങള് <a href="%s"> പരിശോധിയ്ക്കുക</a>.
|
||||
add_new_key=SSH കീ ചേർക്കുക
|
||||
add_new_gpg_key=GPG കീ ചേർക്കുക
|
||||
ssh_key_been_used=ഈ SSH കീ ഇതിനകം ചേർത്തു.
|
||||
ssh_key_name_used=ഇതേ പേരിലുള്ള ഒരു SSH കീ ഇതിനകം നിങ്ങളുടെ അക്കൗണ്ടിലേക്ക് ചേർത്തിട്ടുണ്ടു്.
|
||||
gpg_key_id_used=സമാന ഐഡിയുള്ള ഒരു പൊതു ജിപിജി കീ ഇതിനകം നിലവിലുണ്ട്.
|
||||
gpg_no_key_email_found=നിങ്ങളുടെ അക്കൗണ്ടുമായി ബന്ധപ്പെട്ട ഏതെങ്കിലും ഇമെയിൽ വിലാസത്തിൽ ഈ GPG കീ ഉപയോഗിക്കാൻ കഴിയില്ല.
|
||||
subkeys=സബ് കീകള്
|
||||
key_id=കീ ഐഡി
|
||||
key_name=കീയുടെ പേരു്
|
||||
key_content=ഉള്ളടക്കം
|
||||
add_key_success='%s' എന്ന SSH കീ ചേർത്തു.
|
||||
add_gpg_key_success='%s' എന്ന GPG കീ ചേർത്തു.
|
||||
delete_key=നീക്കം ചെയ്യുക
|
||||
ssh_key_deletion=SSH കീ നീക്കം ചെയ്യുക
|
||||
gpg_key_deletion=GPG കീ നീക്കം ചെയ്യുക
|
||||
ssh_key_deletion_desc=ഒരു SSH കീ നീക്കംചെയ്യുന്നത് നിങ്ങളുടെ അക്കൌണ്ടിലേക്കുള്ള പ്രവേശനം അസാധുവാക്കുന്നു. തുടരട്ടെ?
|
||||
gpg_key_deletion_desc=ഒരു ജിപിജി കീ നീക്കംചെയ്യുന്നത് അതിൽ ഒപ്പിട്ട കമ്മിറ്റുകളെ സ്ഥിരീകരിക്കില്ല. തുടരട്ടെ?
|
||||
ssh_key_deletion_success=SSH കീ നീക്കംചെയ്തു.
|
||||
gpg_key_deletion_success=GPG കീ നീക്കംചെയ്തു.
|
||||
add_on=ചേര്ത്തതു്
|
||||
valid_until=വരെ സാധുവാണ്
|
||||
valid_forever=എന്നും സാധുവാണു്
|
||||
last_used=അവസാനം ഉപയോഗിച്ചത്
|
||||
no_activity=സമീപകാലത്തു് പ്രവർത്തനങ്ങളൊന്നുമില്ല
|
||||
can_read_info=വായിയ്ക്കുക
|
||||
can_write_info=എഴുതുക
|
||||
key_state_desc=കഴിഞ്ഞ 7 ദിവസങ്ങളിൽ ഈ കീ ഉപയോഗിച്ചു
|
||||
token_state_desc=ഈ ടോക്കൺ കഴിഞ്ഞ 7 ദിവസങ്ങളിൽ ഉപയോഗിച്ചു
|
||||
show_openid=പ്രൊഫൈലിൽ കാണുക
|
||||
hide_openid=പ്രൊഫൈലിൽ നിന്ന് മറയ്ക്കുക
|
||||
ssh_disabled=SSH അപ്രാപ്തമാക്കി
|
||||
|
||||
manage_social=സഹവസിക്കുന്ന സോഷ്യൽ അക്കൗണ്ടുകളെ നിയന്ത്രിക്കുക
|
||||
social_desc=ഈ സോഷ്യൽ അക്കൗണ്ടുകൾ നിങ്ങളുടെ ഗിറ്റീ അക്കൗണ്ടുമായി ലിങ്കുചെയ്തു. ഇവ നിങ്ങളുടെ ഗീറ്റീ അക്കൗണ്ടിലേക്ക് പ്രവേശിക്കാൻ ഉപയോഗിക്കാവുന്നതിനാൽ അവയെല്ലാം നിങ്ങൾ തിരിച്ചറിഞ്ഞുവെന്ന് ഉറപ്പാക്കുക.
|
||||
unbind=അൺലിങ്ക് ചെയ്യുക
|
||||
unbind_success=നിങ്ങളുടെ ഗീറ്റീ അക്കൗണ്ടിൽ നിന്ന് സോഷ്യൽ അക്കൗണ്ട് അൺലിങ്ക് ചെയ്തു.
|
||||
|
||||
manage_access_token=ആക്സസ്സ് ടോക്കണുകൾ നിയന്ത്രിക്കുക
|
||||
generate_new_token=പുതിയ ടോക്കൺ സൃഷ്ടിക്കുക
|
||||
tokens_desc=ഈ ടോക്കണുകൾ ഗിറ്റീ API ഉപയോഗിച്ച് നിങ്ങളുടെ അക്കൌണ്ടിലേക്ക് പ്രവേശനം നൽകുന്നു.
|
||||
new_token_desc=ഒരു ടോക്കൺ ഉപയോഗിക്കുന്ന അപ്ലിക്കേഷനുകൾക്ക് നിങ്ങളുടെ അക്കൌണ്ടിലേക്ക് പൂർണ്ണ പ്രവേശനം ഉണ്ട്.
|
||||
token_name=ടോക്കണിന്റെ പേരു്
|
||||
generate_token=ടോക്കൺ സൃഷ്ടിക്കുക
|
||||
generate_token_success=നിങ്ങളുടെ പുതിയ ടോക്കൺ ജനറേറ്റുചെയ്തു. ഇത് വീണ്ടും കാണിക്കാത്തതിനാൽ ഇപ്പോൾ തന്നെ പകർത്തുക.
|
||||
delete_token=നീക്കം ചെയ്യുക
|
||||
access_token_deletion=ആക്സസ്സ് ടോക്കണ് നീക്കം ചെയ്യുക
|
||||
access_token_deletion_desc=ഒരു ടോക്കൺ ഇല്ലാതാക്കുന്നത് നിങ്ങളുടെ അക്കൗണ്ട് ഉപയോഗിക്കുന്ന അപ്ലിക്കേഷനുകൾക്കുള്ള പ്രവേശനം അസാധുവാക്കും. തുടരട്ടേ?
|
||||
delete_token_success=ടോക്കൺ ഇല്ലാതാക്കി. ഇനി ഇത് ഉപയോഗിക്കുന്ന അപ്ലിക്കേഷനുകൾക്ക് നിങ്ങളുടെ അക്കൌണ്ടിലേക്ക് പ്രവേശനം ഉണ്ടാകില്ല.
|
||||
|
||||
manage_oauth2_applications=OAuth2 അപ്ലിക്കേഷനുകൾ നിയന്ത്രിക്കുക
|
||||
edit_oauth2_application=OAuth2 അപ്ലിക്കേഷൻ എഡിറ്റുചെയ്യുക
|
||||
oauth2_applications_desc=നിങ്ങളുടെ മൂന്നാം കക്ഷി അപ്ലിക്കേഷനെ, ഈ ഗിറ്റീ ഇന്സ്റ്റാളേഷനുമായി സുരക്ഷിതമായി ഉപയോക്താക്കളെ പ്രാമാണീകരിക്കാൻ OAuth2 അപ്ലിക്കേഷനുകൾ പ്രാപ്തമാക്കുന്നു.
|
||||
remove_oauth2_application=OAuth2 അപ്ലിക്കേഷനുകൾ നീക്കംചെയ്യുക
|
||||
remove_oauth2_application_desc=ഒരു OAuth2 അപ്ലിക്കേഷൻ നീക്കംചെയ്യുന്നത് ഒപ്പിട്ട എല്ലാ ആക്സസ് ടോക്കണുകളിലേക്കും പ്രവേശനം റദ്ദാക്കും. തുടരട്ടെ?
|
||||
remove_oauth2_application_success=അപ്ലിക്കേഷൻ ഇല്ലാതാക്കി.
|
||||
create_oauth2_application=ഒരു പുതിയ OAuth2 അപ്ലിക്കേഷൻ സൃഷ്ടിക്കുക
|
||||
create_oauth2_application_button=അപ്ലിക്കേഷൻ സൃഷ്ടിക്കുക
|
||||
create_oauth2_application_success=നിങ്ങൾ വിജയകരമായി ഒരു പുതിയ OAuth2 അപ്ലിക്കേഷൻ സൃഷ്ടിച്ചു.
|
||||
update_oauth2_application_success=നിങ്ങൾ വിജയകരമായി ഒരു പുതിയ OAuth2 അപ്ലിക്കേഷൻ പുതുക്കി.
|
||||
oauth2_application_name=അപ്ലിക്കേഷന്റെ പേര്
|
||||
oauth2_select_type=ഏത് തരം അപ്ലിക്കേഷനാണ് ഇതു്?
|
||||
oauth2_type_web=വെബ് (e.g. Node.JS, Tomcat, Go)
|
||||
oauth2_type_native=നേറ്റീവ് (ഉദാ. മൊബൈൽ, ഡെസ്ക്ടോപ്പ്, ബ്രൌസർ)
|
||||
oauth2_redirect_uri=URI റീഡയറക്ട് ചെയ്യുക
|
||||
save_application=സംരക്ഷിയ്ക്കുക
|
||||
oauth2_client_id=ക്ലൈന്റ് ഐഡി
|
||||
oauth2_client_secret=ക്ലൈന്റു് രഹസ്യം
|
||||
oauth2_regenerate_secret=രഹസ്യം പുനഃസൃഷ്ടിയ്ക്കുക
|
||||
oauth2_regenerate_secret_hint=നിങ്ങളുടെ രഹസ്യം നഷ്ടപ്പെട്ടോ?
|
||||
oauth2_client_secret_hint=നിങ്ങൾ ഈ പേജ് വീണ്ടും സന്ദർശിക്കുകയാണെങ്കിൽ രഹസ്യം ദൃശ്യമാകില്ല. നിങ്ങളുടെ രഹസ്യം സംരക്ഷിക്കുക.
|
||||
oauth2_application_edit=ക്രമീകരിക്കുക
|
||||
oauth2_application_create_description=OAuth2 ആപ്ലിക്കേഷനുകൾ നിങ്ങളുടെ മൂന്നാം കക്ഷി ആപ്ലിക്കേഷൻ ഉപയോക്തൃ അക്കൌണ്ടുകളിലേക്ക് ആക്സസ് നൽകുന്നു.
|
||||
oauth2_application_remove_description=ഒരു OAuth2 ആപ്ലിക്കേഷൻ നീക്കംചെയ്യുന്നത് ഈ സന്ദർഭത്തിൽ അംഗീകൃത ഉപയോക്തൃ അക്കൌണ്ടുകളിലേക്ക് പ്രവേശിക്കുന്നത് തടയും. തുടരട്ടെ?
|
||||
|
||||
authorized_oauth2_applications=അംഗീകൃത OAuth2 അപ്ലിക്കേഷനുകൾ
|
||||
authorized_oauth2_applications_description=ഈ മൂന്നാം കക്ഷി അപ്ലിക്കേഷനുകളിലേക്ക് നിങ്ങളുടെ സ്വകാര്യ ഗീറ്റീ അക്കൗണ്ടിലേക്ക് പ്രവേശനം അനുവദിച്ചു. അപ്ലിക്കേഷനുകൾക്കായുള്ള നിയന്ത്രണം ഇനി ആവശ്യമില്ല.
|
||||
revoke_key=അസാധുവാക്കുക
|
||||
revoke_oauth2_grant=നിയന്ത്രണം തിരിച്ചെടുക്കുക
|
||||
revoke_oauth2_grant_description=ഈ മൂന്നാം കക്ഷി ആപ്ലിക്കേഷനായി ആക്സസ് അസാധുവാക്കുന്നത് നിങ്ങളുടെ ഡാറ്റ ആക്സസ് ചെയ്യുന്നതിൽ നിന്ന് ഈ ആപ്ലിക്കേഷനെ തടയും. നിങ്ങള്ക്ക് ഉറപ്പാണോ?
|
||||
revoke_oauth2_grant_success=നിങ്ങൾ വിജയകരമായി പ്രവേശനം റദ്ദാക്കി.
|
||||
|
||||
twofa_desc=ഇരട്ട ഘടക പ്രാമാണീകരണം നിങ്ങളുടെ അക്കൗണ്ടിന്റെ സുരക്ഷ വർദ്ധിപ്പിക്കുന്നു.
|
||||
twofa_is_enrolled=നിങ്ങളുടെ അക്കൗണ്ട് നിലവിൽ ഇരട്ട ഘടക പ്രമാണീകരണത്തിനു് <strong> എൻറോൾ ചെയ്തിട്ടുണ്ട്. </strong>.
|
||||
twofa_not_enrolled=നിങ്ങളുടെ അക്കൗണ്ട് നിലവിൽ ഇരട്ട ഘടക പ്രമാണീകരണത്തിനു് <strong> എൻറോൾ ചെയ്തിട്ടില്ല.</strong>.
|
||||
twofa_disable=ഇരട്ട ഘടക പ്രാമാണീകരണം റദ്ദാക്കി
|
||||
twofa_scratch_token_regenerate=സ്ക്രാച്ച് ടോക്കൺ പുനഃനിര്മ്മിയ്ക്കുക
|
||||
twofa_scratch_token_regenerated=%s ആണ് ഇപ്പോൾ നിങ്ങളുടെ സ്ക്രാച്ച് ടോക്കൺ. സുരക്ഷിതമായ സ്ഥലത്ത് സൂക്ഷിക്കുക.
|
||||
twofa_enroll=ഇരട്ട ഘടക പ്രാമാണീകരണത്തില് അംഗമാകുക
|
||||
twofa_disable_note=ആവശ്യമെങ്കിൽ നിങ്ങൾക്ക് രണ്ട്-ഘടക പ്രാമാണീകരണം അപ്രാപ്തമാക്കാൻ കഴിയും.
|
||||
twofa_disable_desc=രണ്ട്-ഘടക പ്രാമാണീകരണം അപ്രാപ്തമാക്കുന്നത് നിങ്ങളുടെ അക്കൗണ്ട് സുരക്ഷിതമല്ലാത്തതാക്കും. തുടരട്ടെ?
|
||||
regenerate_scratch_token_desc=നിങ്ങളുടെ സ്ക്രാച്ച് ടോക്കൺ തെറ്റായി സ്ഥാപിക്കുകയോ അല്ലെങ്കിൽ സൈൻ ഇൻ ചെയ്യാൻ ഇതിനകം ഉപയോഗിക്കുകയോ ചെയ്തിട്ടുണ്ടെങ്കിൽ അത് ഇവിടെനിന്നു് പുനഃസജ്ജമാക്കാൻ കഴിയും.
|
||||
twofa_disabled=രണ്ട്-ഘട്ട പ്രാമാണീകരണം അപ്രാപ്തമാക്കി.
|
||||
scan_this_image=നിങ്ങളുടെ പ്രാമാണീകരണ ആപ്ലിക്കേഷൻ ഉപയോഗിച്ച് ഈ ചിത്രം സൂക്ഷ്മപരിശോധന നടത്തുക:
|
||||
or_enter_secret=അല്ലെങ്കിൽ രഹസ്യ കോഡ് നൽകുക: %s
|
||||
then_enter_passcode=അപ്ലിക്കേഷനിൽ കാണിച്ചിരിക്കുന്ന പാസ്കോഡ് നൽകുക:
|
||||
passcode_invalid=പാസ്കോഡ് തെറ്റാണ്. വീണ്ടും ശ്രമിക്കുക.
|
||||
twofa_enrolled=നിങ്ങളുടെ അക്കൌണ്ട് രണ്ട്-ഘട്ട പ്രാമാണീകരണത്തിലേക്ക് ചേർത്തിട്ടുണ്ട്. നിങ്ങളുടെ സ്ക്രാച്ച് ടോക്കൺ (%s) ഒരു തവണ മാത്രം കാണിക്കുന്നതിനാൽ അതു് സുരക്ഷിതമായ സ്ഥലത്ത് സൂക്ഷിക്കുക!
|
||||
|
||||
u2f_desc=ക്രിപ്റ്റോഗ്രാഫിക് കീകൾ അടങ്ങിയ ഹാർഡ്വെയർ ഉപകരണങ്ങളാണ് സുരക്ഷാ കീകൾ. രണ്ട്-ഘട്ട പ്രാമാണീകരണത്തിനായി അവ ഉപയോഗിക്കാം. പക്ഷേ സുരക്ഷാ കീകൾ <a rel="noreferrer" href="https://fidoalliance.org/"> FIDO U2F </a> സ്റ്റാൻഡേർഡിനെ പിന്തുണയ്ക്കുന്നവയാകണം.
|
||||
u2f_require_twofa=സുരക്ഷാ കീകൾ ഉപയോഗിക്കുന്നതിന് നിങ്ങളുടെ അക്കൌണ്ട് രണ്ട്-ഘട്ട പ്രാമാണീകരണത്തിൽ ചേർത്തിരിക്കണം.
|
||||
u2f_register_key=സുരക്ഷാ കീ ചേർക്കുക
|
||||
u2f_nickname=വിളിപ്പേരു്
|
||||
u2f_press_button=രജിസ്റ്റർ ചെയ്യുന്നതിന് നിങ്ങളുടെ സുരക്ഷാ കീയിലെ ബട്ടൺ അമർത്തുക.
|
||||
u2f_delete_key=സുരക്ഷാ കീ നീക്കംചെയ്യുക
|
||||
u2f_delete_key_desc=നിങ്ങൾ ഒരു സുരക്ഷാ കീ നീക്കംചെയ്യുകയാണെങ്കിൽ, നിങ്ങൾക്ക് ഇത് ഉപയോഗിച്ച് പ്രവേശിയ്ക്കാന് കഴിയില്ല. തുടരട്ടെ?
|
||||
|
||||
manage_account_links=ബന്ധിപ്പിച്ചിട്ടുള്ള അക്കൗണ്ടുകൾ നിയന്ത്രിക്കുക
|
||||
manage_account_links_desc=ഈ ബാഹ്യ അക്കൗണ്ടുകൾ നിങ്ങളുടെ ഗിറ്റീ അക്കൗണ്ടുമായി ലിങ്കുചെയ്തു.
|
||||
account_links_not_available=നിങ്ങളുടെ ഗിറ്റീ അക്കൌണ്ടുമായി നിലവിൽ മറ്റു് ബാഹ്യ അക്കൌണ്ടുകളൊന്നും ബന്ധിപ്പിച്ചിട്ടില്ല.
|
||||
remove_account_link=ബന്ധിപ്പിച്ച അക്കൗണ്ട് നീക്കംചെയ്യുക
|
||||
remove_account_link_desc=ഒരു ബന്ധിപ്പിച്ച അക്കൗണ്ട് നീക്കംചെയ്യുന്നത് നിങ്ങളുടെ ഗിറ്റീ അക്കൗണ്ടിലേക്കുള്ള പ്രവേശനം അസാധുവാക്കും. തുടരട്ടെ?
|
||||
remove_account_link_success=ബന്ധിപ്പിച്ച അക്കൗണ്ട് നീക്കംചെയ്തു.
|
||||
|
||||
orgs_none=നിങ്ങൾ ഏതെങ്കിലും സംഘടനയില് അംഗമല്ല.
|
||||
repos_none=നിങ്ങൾക്ക് ഒരു കലവറയും സ്വന്തമായി ഇല്ല
|
||||
|
||||
delete_account=അക്കൗണ്ട് ഇല്ലാതാക്കുക
|
||||
delete_prompt=ഈ പ്രവർത്തനം നിങ്ങളുടെ ഉപയോക്തൃ അക്കൗണ്ട് ശാശ്വതമായി ഇല്ലാതാക്കും. ഇത് <strong> പൂർവ്വാവസ്ഥയിലാക്കാൻ കഴിയില്ല.</strong>.
|
||||
confirm_delete_account=ഇല്ലാതാക്കൽ സ്ഥിരീകരിക്കുക
|
||||
delete_account_title=ഉപയോക്തൃ അക്കൗണ്ട് ഇല്ലാതാക്കുക
|
||||
delete_account_desc=ഈ ഉപയോക്തൃ അക്കൗണ്ട് ശാശ്വതമായി ഇല്ലാതാക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുണ്ടോ?
|
||||
|
||||
email_notifications.enable=ഇമെയിൽ അറിയിപ്പുകൾ പ്രാപ്തമാക്കുക
|
||||
email_notifications.onmention=ഇ-മെയിൽ പരാമര്ശിച്ചാൽ മാത്രം അയയ്ക്കുക
|
||||
email_notifications.disable=ഇമെയിൽ അറിയിപ്പുകൾ അപ്രാപ്തമാക്കുക
|
||||
email_notifications.submit=ഇ-മെയില് മുൻഗണനകള്
|
||||
|
||||
[repo]
|
||||
owner=ഉടമസ്ഥന്
|
||||
repo_name=കലവറയുടെ പേരു്
|
||||
repo_name_helper=നല്ല കലവറയുടെ പേരു് ഹ്രസ്വവും അവിസ്മരണീയവും അതുല്യവുമായ കീവേഡുകൾ ഉപയോഗിക്കുന്നു.
|
||||
visibility=കാണാനാവുന്നതു്
|
||||
visibility_description=ഉടമയ്ക്കോ ഓർഗനൈസേഷൻ അംഗങ്ങൾക്കോ അവകാശങ്ങളുണ്ടെങ്കിൽ മാത്രമേ കാണാൻ കഴിയൂ.
|
||||
visibility_helper=കലവറ സ്വകാര്യമാക്കുക
|
||||
visibility_helper_forced=നിങ്ങളുടെ സൈറ്റ് അഡ്മിനിസ്ട്രേറ്റർ പുതിയ കലവറകളെ സ്വകാര്യമാക്കാൻ നിർബന്ധിക്കുന്നു.
|
||||
visibility_fork_helper=(മാറ്റം എല്ലാ ഫോർക്കുകളെയും ബാധിക്കും.)
|
||||
clone_helper=ക്ലോണ് ചെയ്യാന് സഹായം വേണോ? <a target="_blank" rel="noopener noreferrer" href="%s">സഹായം</a> സന്ദര്ശിക്കുക.
|
||||
fork_repo=കലവറ ഫോര്ക്കു് ചെയ്യുക
|
||||
fork_from=ല് നിന്നും ഫോര്ക്കു് ചെയ്യൂ
|
||||
fork_visibility_helper=ഒരു കലവറയുടെ ഫോര്ക്കിന്റെ ദൃശ്യപരത മാറ്റാൻ കഴിയില്ല.
|
||||
repo_desc=വിരരണം
|
||||
repo_lang=ഭാഷ
|
||||
repo_gitignore_helper=.gitignore ടെംപ്ലേറ്റുകൾ തിരഞ്ഞെടുക്കുക.
|
||||
license=ലൈസൻസ്
|
||||
license_helper=ഒരു ലൈസൻസ് ഫയൽ തിരഞ്ഞെടുക്കുക.
|
||||
readme=റീഡ്മീ
|
||||
readme_helper=ഒരു റീഡ്മീ ഫയൽ ടെംപ്ലേറ്റ് തിരഞ്ഞെടുക്കുക.
|
||||
auto_init=കലവറ സമാരംഭിക്കുക (.gitignore, ലൈസൻസ്, റീഡ്മീ എന്നിവ ചേർക്കുന്നു)
|
||||
create_repo=കലവറ സൃഷ്ടിക്കുക
|
||||
default_branch=സ്ഥിരസ്ഥിതി ശാഖ
|
||||
mirror_prune=വെട്ടിഒതുക്കുക
|
||||
mirror_prune_desc=കാലഹരണപ്പെട്ട വിദൂര ട്രാക്കിംഗ് റഫറൻസുകൾ നീക്കംചെയ്യുക
|
||||
mirror_interval=മിറർ ചെയ്യാനുള്ള ഇടവേള (സാധുവായ സമയ യൂണിറ്റുകൾ 'h', 'm', 's' എന്നിവയാണ്). യാന്ത്രിക സമന്വയം പ്രവർത്തനരഹിതമാക്കാൻ 0 നല്കുക.
|
||||
mirror_interval_invalid=മിറർ ചെയ്യാനുള്ള ഇടവേള സാധുവല്ല.
|
||||
mirror_address=URL- ൽ നിന്നുള്ള ക്ലോൺ
|
||||
mirror_address_url_invalid=നൽകിയ url അസാധുവാണ്. നിങ്ങൾ url- ന്റെ എല്ലാ ഘടകങ്ങളും ശരിയായി നല്കണം.
|
||||
mirror_address_protocol_invalid=നൽകിയ url അസാധുവാണ്. http(s):// അല്ലെങ്കിൽ git:// ലൊക്കേഷനുകൾ മാത്രമേ മിറർ ചെയ്യാൻ കഴിയൂ.
|
||||
mirror_last_synced=അവസാനം സമന്വയിപ്പിച്ചതു്
|
||||
watchers=നിരീക്ഷകർ
|
||||
stargazers=സ്റ്റാർഗാസറുകൾ
|
||||
forks=ശാഖകള്
|
||||
pick_reaction=നിങ്ങളുടെ പ്രതികരണം തിരഞ്ഞെടുക്കുക
|
||||
reactions_more=കൂടാതെ %d അധികം
|
||||
|
||||
|
||||
archive.title=ഈ കലവറ ചരിത്രരേഖാപരമായി നിലനിര്ത്തിയിരിക്കുന്നു. നിങ്ങൾക്ക് ഫയലുകൾ കാണാനും ക്ലോൺ ചെയ്യാനും കഴിയും, പക്ഷേ പ്രശ്നങ്ങൾ / ലയന അഭ്യർത്ഥനകൾ ഉണ്ടാക്കാനോ തുറക്കാനോ കഴിയില്ല.
|
||||
archive.issue.nocomment=ഈ കലവറ ചരിത്രപരമായി നിലനിര്ത്തിയിരിക്കുന്നതാണു്. നിങ്ങൾക്ക് പ്രശ്നങ്ങളിൽ അഭിപ്രായമിടാൻ കഴിയില്ല.
|
||||
archive.pull.nocomment=ഈ കലവറ ചരിത്രപരമായി നിലനിര്ത്തിയിരിക്കുന്നതാണു്. നിങ്ങൾക്ക് ലയന അഭ്യർത്ഥനകളില് അഭിപ്രായമിടാൻ കഴിയില്ല.
|
||||
|
||||
form.reach_limit_of_creation=നിങ്ങളുടെ കലവറകളുടെ പരിധിയായ %d നിങ്ങൾ ഇതിനകം എത്തി.
|
||||
form.name_reserved='%s' എന്ന കലവറയുടെ പേരു് മറ്റാവശ്യങ്ങള്ക്കായി നീക്കിവച്ചിരിക്കുന്നു.
|
||||
form.name_pattern_not_allowed=കലവറനാമത്തിൽ '%s' എന്ന ശ്രേണി അനുവദനീയമല്ല.
|
||||
|
||||
need_auth=ക്ലോൺ അംഗീകാരിയ്ക്കുക
|
||||
migrate_type=മൈഗ്രേഷൻ തരം
|
||||
migrate_type_helper=ഈ കലവറ ഒരു <span class="text blue"> മിറർ </span> ആയിരിക്കും
|
||||
migrate_items=മൈഗ്രേഷൻ ഇനങ്ങൾ
|
||||
migrate_items_wiki=വിക്കി
|
||||
migrate_items_milestones=നാഴികക്കല്ലുകള്
|
||||
migrate_items_labels=ലേബലുകള്
|
||||
migrate_items_issues=പ്രശ്നങ്ങൾ
|
||||
migrate_items_pullrequests=ലയന അഭ്യർത്ഥനകൾ
|
||||
migrate_items_releases=പ്രസിദ്ധീകരണങ്ങള്
|
||||
migrate_repo=കലവറ മൈഗ്രേറ്റ് ചെയ്യുക
|
||||
migrate.clone_address=URL- ൽ നിന്ന് മൈഗ്രേറ്റ് / ക്ലോൺ ചെയ്യുക
|
||||
migrate.clone_address_desc=നിലവിലുള്ള ഒരു കലവറയുടെ HTTP(S) അല്ലെങ്കിൽ ഗിറ്റു് 'ക്ലോൺ' URL
|
||||
migrate.clone_local_path=അല്ലെങ്കിൽ ഒരു പ്രാദേശിക സെർവർ പാത
|
||||
migrate.permission_denied=പ്രാദേശിക കലവറകള് ഇറക്കുമതി ചെയ്യാൻ നിങ്ങള്ക്കു് അനുവാദമില്ല.
|
||||
migrate.invalid_local_path=പ്രാദേശിക പാത അസാധുവാണ്. ഇത് നിലവിലില്ല അല്ലെങ്കിൽ ഒരു ഡയറക്ടറിയല്ല.
|
||||
migrate.failed=മൈഗ്രേഷൻ പരാജയപ്പെട്ടു: %v
|
||||
migrate.lfs_mirror_unsupported=എൽഎഫ്എസ് ഒബ്ജക്റ്റുകളുടെ മിററിംഗ് പിന്തുണയ്ക്കുന്നില്ല - പകരം 'git lfs fetch --all', 'git lfs push --all' എന്നിവ ഉപയോഗിക്കുക.
|
||||
migrate.migrate_items_options=ഗിറ്റ്ഹബിൽ നിന്ന് മൈഗ്രേറ്റ് ചെയ്യുമ്പോൾ, ഒരു ഉപയോക്തൃനാമവും മൈഗ്രേഷൻ ഓപ്ഷനുകളും നല്കാം.
|
||||
migrated_from=<a href="%[1]s">%[2]s</a> നിന്ന് മൈഗ്രേറ്റുചെയ്തു
|
||||
migrated_from_fake=%[1]s നിന്ന് മൈഗ്രേറ്റുചെയ്തു
|
||||
|
||||
mirror_from=ന്റെ കണ്ണാടി
|
||||
forked_from=ല് നിന്നും വഴിപിരിഞ്ഞതു്
|
||||
fork_from_self=നിങ്ങളുടെ ഉടമസ്ഥതയിലുള്ള ഒരു ശേഖരം നിങ്ങൾക്ക് ഫോര്ക്കു് ചെയ്യാൻ കഴിയില്ല.
|
||||
fork_guest_user=ഈ ശേഖരം ഫോർക്ക് ചെയ്യുന്നതിന് സൈൻ ഇൻ ചെയ്യുക.
|
||||
copy_link=പകര്ത്തുക
|
||||
copy_link_success=കണ്ണി പകർത്തി
|
||||
copy_link_error=പകർത്താൻ ⌘C അല്ലെങ്കിൽ Ctrl-C ഉപയോഗിക്കുക
|
||||
copied=പകര്ത്തല് പൂര്ത്തിയായി
|
||||
unwatch=ശ്രദ്ധിക്കാതിരിയ്ക്കുക
|
||||
watch=ശ്രദ്ധിയ്ക്കുക
|
||||
unstar=നക്ഷത്രം നീക്കുക
|
||||
star=നക്ഷത്രം നല്ക്കുക
|
||||
fork=ഫോര്ക്കു്
|
||||
download_archive=കലവറ ഡൗൺലോഡുചെയ്യുക
|
||||
|
||||
no_desc=വിവരണം ലഭ്യമല്ല
|
||||
quick_guide=ദ്രുത മാര്ഗദര്ശനം
|
||||
clone_this_repo=ഈ കലവറ ക്ലോൺ ചെയ്യുക
|
||||
create_new_repo_command=കമാൻഡ് ലൈന് വഴി ഒരു പുതിയ കലവറ സൃഷ്ടിക്കുക
|
||||
push_exist_repo=കമാൻഡ് ലൈനിൽ നിന്ന് നിലവിലുള്ള ഒരു കലവറ തള്ളിക്കയറ്റുക
|
||||
empty_message=ഈ കലവറയില് ഉള്ളടക്കമൊന്നും അടങ്ങിയിട്ടില്ല.
|
||||
|
||||
code=കോഡ്
|
||||
code.desc=ഉറവിട കോഡ്, ഫയലുകൾ, കമ്മിറ്റുകളും ശാഖകളും പ്രവേശിയ്ക്കുക.
|
||||
branch=ശാഖ
|
||||
tree=മരം
|
||||
filter_branch_and_tag=ശാഖ അല്ലെങ്കിൽ ടാഗ് അരിച്ചെടുക്കുക
|
||||
branches=ശാഖകള്
|
||||
tags=ടാഗുകള്
|
||||
issues=പ്രശ്നങ്ങൾ
|
||||
pulls=ലയന അഭ്യർത്ഥനകൾ
|
||||
labels=ലേബലുകള്
|
||||
milestones=നാഴികക്കല്ലുകള്
|
||||
commits=കമ്മിറ്റുകള്
|
||||
commit=കമ്മിറ്റ്
|
||||
releases=പ്രസിദ്ധപ്പെടുത്തുക
|
||||
file_raw=കലര്പ്പില്ലാത്തതു്
|
||||
file_history=നാള്വഴി
|
||||
file_view_raw=കലര്പ്പില്ലാതെ കാണുക
|
||||
file_permalink=സ്ഥിരമായ കണ്ണി
|
||||
file_too_large=ഈ ഫയൽ കാണിക്കാൻ കഴിയാത്തത്ര വലുതാണ്.
|
||||
video_not_supported_in_browser=നിങ്ങളുടെ ബ്രൌസർ HTML5 'വീഡിയോ' ടാഗിനെ പിന്തുണയ്ക്കുന്നില്ല.
|
||||
audio_not_supported_in_browser=നിങ്ങളുടെ ബ്ര browser സർ HTML5 'ഓഡിയോ' ടാഗിനെ പിന്തുണയ്ക്കുന്നില്ല.
|
||||
stored_lfs=ഗിറ്റു് LFS ഉപയോഗിച്ച് സംഭരിച്ചു
|
||||
commit_graph=കമ്മിറ്റ് ഗ്രാഫ്
|
||||
blame=ചുമതല
|
||||
normal_view=സാധാരണ കാഴ്ച
|
||||
|
||||
editor.new_file=പുതിയ ഫയൽ
|
||||
editor.upload_file=ഫയൽ അപ്ലോഡ്
|
||||
editor.edit_file=ഫയൽ തിരുത്തുക
|
||||
editor.preview_changes=മാറ്റങ്ങൾ കാണുക
|
||||
editor.cannot_edit_lfs_files=വെബ് ഇന്റർഫേസിൽ LFS ഫയലുകൾ എഡിറ്റുചെയ്യാൻ കഴിയില്ല.
|
||||
editor.cannot_edit_non_text_files=വെബ് ഇന്റർഫേസിൽ ബൈനറി ഫയലുകൾ എഡിറ്റുചെയ്യാൻ കഴിയില്ല.
|
||||
editor.edit_this_file=ഫയൽ തിരുത്തുക
|
||||
editor.must_be_on_a_branch=ഈ ഫയലിൽ മാറ്റങ്ങൾ വരുത്താനോ നിർദ്ദേശിക്കാനോ നിങ്ങൾ ഏതെങ്കിലും ഒരു ശാഖയിൽ ആയിരിക്കണം.
|
||||
editor.fork_before_edit=ഈ ഫയലിൽ മാറ്റങ്ങൾ വരുത്താനോ നിർദ്ദേശിക്കാനോ നിങ്ങൾ ഈ ശേഖരം ഫോര്ക്കു ചെയ്തിരിക്കണം.
|
||||
editor.delete_this_file=ഫയൽ ഇല്ലാതാക്കുക
|
||||
editor.must_have_write_access=ഈ ഫയലിൽ മാറ്റങ്ങൾ വരുത്താനോ നിർദ്ദേശിക്കാനോ നിങ്ങൾക്ക് എഴുതാനുള്ള അനുമതി ഉണ്ടായിരിക്കണം.
|
||||
editor.file_delete_success=%s ഫയൽ ഇല്ലാതാക്കി.
|
||||
editor.name_your_file=നിങ്ങളുടെ ഫയലിന് പേര് നൽകുക…
|
||||
editor.filename_help=ഒരു ഡയറക്ടറിയുടെ പേര് ടൈപ്പുചെയ്ത് സ്ലാഷും ('/') ചേർത്ത് ചേർക്കുക. ഇൻപുട്ട് ഫീൽഡിന്റെ തുടക്കത്തിൽ ബാക്ക്സ്പെയ്സ് ടൈപ്പുചെയ്ത് ഒരു ഡയറക്ടറി നീക്കംചെയ്യുക.
|
||||
editor.or=അഥവാ
|
||||
editor.cancel_lower=റദ്ദാക്കുക
|
||||
editor.commit_changes=മാറ്റങ്ങൾ വരുത്തുക
|
||||
editor.add_tmpl='<ഫയല്>' ചേർക്കുക
|
||||
editor.add=%s ചേര്ക്കുക
|
||||
editor.update=%s പുതുക്കുക
|
||||
editor.delete=%s നീക്കം ചെയ്യുക
|
||||
editor.propose_file_change=ഫയലിനു് മാറ്റങ്ങള് നിർദ്ദേശിക്കുക
|
||||
editor.new_branch_name_desc=പുതിയ ശാഖയുടെ പേരു്…
|
||||
editor.cancel=റദ്ദാക്കുക
|
||||
editor.filename_cannot_be_empty=ഫയലിന്റെ പേരു് ശൂന്യമായിരിക്കരുത്.
|
||||
editor.add_subdir=ഒരു ഡയറക്ടറി ചേർക്കുക…
|
||||
editor.upload_files_to_dir=ഫയലുകൾ %s ലേക്ക് അപ്ലോഡുചെയ്യുക
|
||||
|
||||
|
||||
|
||||
issues.new.clear_labels=ലേബലുകൾ മായ്ക്കുക
|
||||
issues.new.milestone=നാഴികക്കല്ല്
|
||||
issues.new.no_milestone=നാഴികക്കല്ല് ഇല്ല
|
||||
issues.new.clear_milestone=നാഴികക്കല്ല് എടുത്തു മാറ്റുക
|
||||
issues.new.open_milestone=നാഴികക്കല്ലുകൾ തുറക്കുക
|
||||
issues.new.closed_milestone=അടച്ച നാഴികക്കല്ലുകൾ
|
||||
issues.new.assignees=നിശ്ചയിക്കുന്നവര്
|
||||
issues.new.clear_assignees=നിശ്ചയിക്കുന്നവരെ നീക്കം ചെയ്യുക
|
||||
issues.new.no_assignees=നിശ്ചയിക്കുന്നവര് ഇല്ല
|
||||
issues.no_ref=ശാഖാ അഥവാ ടാഗ് വ്യക്തമാക്കിയിട്ടില്ല
|
||||
issues.create=പ്രശ്നം സൃഷ്ടിക്കുക
|
||||
issues.new_label=പുതിയ അടയാളം
|
||||
issues.new_label_placeholder=അടയാള നാമം
|
||||
issues.new_label_desc_placeholder=വിരരണം
|
||||
issues.create_label=അടയാളം സൃഷ്ടിക്കുക
|
||||
issues.label_templates.title=മുൻനിശ്ചയിച്ച ഒരു കൂട്ടം ലേബലുകൾ നിറയ്ക്കുക
|
||||
issues.label_templates.info=ലേബലുകളൊന്നും ഇതുവരെ നിലവിലില്ല. 'പുതിയ ലേബൽ' ഉപയോഗിച്ച് ഒരു ലേബൽ സൃഷ്ടിക്കുക അല്ലെങ്കിൽ മുൻനിശ്ചയിച്ച ലേബൽ സെറ്റ് ഉപയോഗിക്കുക:
|
||||
issues.label_templates.helper=ഒരു ലേബൽ സെറ്റ് തിരഞ്ഞെടുക്കുക
|
||||
issues.label_templates.use=ലേബൽ സെറ്റ് ഉപയോഗിക്കുക
|
||||
issues.deleted_milestone=`(ഇല്ലാതാക്കി)`
|
||||
issues.filter_type.all_issues=എല്ലാ ഇഷ്യൂകളും
|
||||
issues.label_open_issues=%d തുറന്നനിലയിലുള്ള ഇഷ്യൂകള്
|
||||
issues.label_deletion_desc=ഒരു ലേബൽ ഇല്ലാതാക്കിയാല്, അതു് നിയുകതമാക്കിയ എല്ലാ ഇഷ്യൂകളില് നിന്നും നീക്കംചെയ്യും. തുടരട്ടെ?
|
||||
issues.dependency.issue_closing_blockedby=ഈ ലയന അഭ്യര്ത്ഥന അടയ്ക്കുന്നത് ഇനിപ്പറയുന്ന ഇഷ്യൂകള് തടയുന്നു്
|
||||
issues.dependency.pr_closing_blockedby=ഈ ഇഷ്യു അടയ്ക്കുന്നത് ഇനിപ്പറയുന്ന ലയന അഭ്യര്ത്ഥന തടയുന്നു്
|
||||
issues.dependency.issue_close_blocks=ഈ ഇഷ്യു അടയ്ക്കുന്നത് ഇനിപ്പറയുന്ന ഇഷ്യൂകള് തടയുന്നു്
|
||||
issues.dependency.pr_close_blocks=ഈ ഇഷ്യൂകള് അടയ്ക്കുന്നത് ഈ ലയന അഭ്യര്ത്ഥന തടയുന്നു്
|
||||
issues.dependency.issue_close_blocked=ഈ ഇഷ്യൂ അടയ്ക്കുന്നതിന് മുമ്പ് ഇതിനെ തടയുന്ന എല്ലാ ഇഷ്യൂകളും നിങ്ങൾ അടയ്ക്കേണ്ടതുണ്ട്.
|
||||
issues.dependency.pr_close_blocked=ഈ ലയന അഭ്യര്ത്ഥന സ്ഥിരീകരിയ്ക്കുന്നതിനു മുമ്പ് ഇതിനെ തടയുന്ന എല്ലാ ഇഷ്യൂകളും നിങ്ങൾ അടയ്ക്കേണ്ടതുണ്ട്.
|
||||
issues.dependency.setting=ലയന അഭ്യര്ത്ഥനകള്ക്കും ഇഷ്യൂകള്ക്കുമായി ആശ്രിതത്വം സജ്ജമാക്കുക
|
||||
issues.dependency.add_error_cannot_create_circular=രണ്ട് ഇഷ്യൂകളും പരസ്പരം തടയുന്നതാകുന്നതിലൂടെ നിങ്ങൾക്ക് ഒരു ആശ്രയത്വം സൃഷ്ടിക്കാൻ കഴിയില്ല.
|
||||
issues.dependency.add_error_dep_not_same_repo=രണ്ട് പ്രശ്നങ്ങളും ഒരേ കലവറയിലേതു് ആയിരിക്കണം.
|
||||
|
||||
|
||||
milestones.filter_sort.most_issues=മിക്ക ഇഷ്യൂകളും
|
||||
milestones.filter_sort.least_issues=കുറഞ്ഞ ഇഷ്യൂകളെങ്കിലും
|
||||
|
||||
|
||||
|
||||
activity.active_issues_count_n=<strong>%d</strong> സജ്ജീവ ഇഷ്യൂകള്
|
||||
activity.closed_issues_count_n=അടച്ച ഇഷ്യൂകള്
|
||||
activity.title.issues_n=%d ഇഷ്യൂകള്
|
||||
activity.new_issues_count_n=പുതിയ ഇഷ്യൂകള്
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
settings.event_issues=ഇഷ്യൂകള്
|
||||
|
||||
|
||||
|
||||
|
@ -88,6 +771,7 @@
|
|||
|
||||
|
||||
|
||||
repos.issues=ഇഷ്യൂകള്
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ loading=Ładowanie…
|
|||
[startpage]
|
||||
app_desc=Bezbolesna usługa Git na własnym serwerze
|
||||
install=Łatwa instalacja
|
||||
install_desc=Po prostu <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">odpal plik binarny</a> dla swojej platformy. Albo uruchom Gitea przy pomocy <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Dockera</a> lub <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>, albo zainstaluj <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">z paczki</a>.
|
||||
platform=Wieloplatformowość
|
||||
platform_desc=Gitea ruszy gdziekolwiek <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> jest możliwe do skompilowania: Windows, macOS, Linux, ARM, itd. Wybierz swój ulubiony system!
|
||||
lightweight=Niskie wymagania
|
||||
|
@ -268,6 +269,7 @@ authorize_application_description=Jeżeli udzielisz dostępu, aplikacja uzyska d
|
|||
authorize_title=Zezwolić "%s" na dostęp do Twojego konta?
|
||||
authorization_failed=Autoryzacja nie powiodła się
|
||||
authorization_failed_desc=Autoryzacja nie powiodła się ze względu na niewłaściwe żądanie. Skontaktuj się z osobami utrzymującymi aplikację, którą próbowano autoryzować.
|
||||
sspi_auth_failed=Uwierzytelnianie SSPI nie powiodło się
|
||||
|
||||
[mail]
|
||||
activate_account=Aktywuj swoje konto
|
||||
|
@ -301,6 +303,8 @@ CommitChoice=Wybór commita
|
|||
TreeName=Ścieżka pliku
|
||||
Content=Treść
|
||||
|
||||
SSPISeparatorReplacement=Separator
|
||||
SSPIDefaultLanguage=Domyślny język
|
||||
|
||||
require_error=` nie może być puste.`
|
||||
alpha_dash_error=` powinno zawierać tylko znaki alfanumeryczne, myślniki ("-") i znaki podkreślenia ("_").`
|
||||
|
@ -312,9 +316,11 @@ max_size_error=` musi zawierać co najwyżej %s znaków.`
|
|||
email_error=` nie jest poprawnym adresem e-mail.`
|
||||
url_error=` nie jest poprawnym adresem URL.`
|
||||
include_error=`musi zawierać tekst '%s'.`
|
||||
glob_pattern_error=` wzorzec glob jest nieprawidłowy: %s.`
|
||||
unknown_error=Nieznany błąd:
|
||||
captcha_incorrect=Kod CAPTCHA jest nieprawidłowy.
|
||||
password_not_match=Hasła nie są identyczne.
|
||||
lang_select_error=Wybierz język z listy.
|
||||
|
||||
username_been_taken=Ta nazwa użytkownika jest już zajęta.
|
||||
repo_name_been_taken=Nazwa repozytorium jest już zajęta.
|
||||
|
@ -326,6 +332,11 @@ team_no_units_error=Zezwól na dostęp do co najmniej jednej sekcji repozytorium
|
|||
email_been_used=Ten adres e-mail jest już używany.
|
||||
openid_been_used=Ten adres OpenID "%s" jest już używany.
|
||||
username_password_incorrect=Nazwa użytkownika lub hasło jest nieprawidłowe.
|
||||
password_complexity=Hasło nie spełnia wymogów złożoności:
|
||||
password_lowercase_one=Co najmniej jedna mała litera
|
||||
password_uppercase_one=Co najmniej jedna duża litera
|
||||
password_digit_one=Co najmniej jedna cyfra
|
||||
password_special_one=Co najmniej jeden znak specjalny (interpunkcja, nawiasy, cudzysłowy, itp.)
|
||||
enterred_invalid_repo_name=Wprowadzona nazwa repozytorium jest niepoprawna.
|
||||
enterred_invalid_owner_name=Nowa nazwa właściciela nie jest prawidłowa.
|
||||
enterred_invalid_password=Wprowadzone hasło jest nieprawidłowe.
|
||||
|
@ -579,6 +590,11 @@ email_notifications.submit=Ustaw preferencje wiadomości e-mail
|
|||
owner=Właściciel
|
||||
repo_name=Nazwa repozytorium
|
||||
repo_name_helper=Dobra nazwa repozytorium jest utworzona z krótkich, łatwych do zapamiętania i unikalnych słów kluczowych.
|
||||
repo_size=Rozmiar repozytorium
|
||||
template=Szablon
|
||||
template_select=Wybierz szablon.
|
||||
template_helper=Ustaw repozytorium jako szablon
|
||||
template_description=Szablony repozytoriów pozwalają użytkownikom generować nowe repozytoria o takiej samej strukturze katalogów, plików i opcjonalnych ustawieniach.
|
||||
visibility=Widoczność
|
||||
visibility_description=Tylko właściciel lub członkowie organizacji, jeśli mają odpowiednie uprawnienia, będą mogli to zobaczyć.
|
||||
visibility_helper=Przekształć repozytorium na prywatne
|
||||
|
@ -588,9 +604,14 @@ clone_helper=Potrzebujesz pomocy z klonowaniem? Odwiedź <a target="_blank" rel=
|
|||
fork_repo=Forkuj repozytorium
|
||||
fork_from=Forkuj z
|
||||
fork_visibility_helper=Widoczność sforkowanego repozytorium nie może być zmieniona.
|
||||
use_template=Użyj tego szablonu
|
||||
generate_repo=Generuj repozytorium
|
||||
generate_from=Generuj z
|
||||
repo_desc=Opis
|
||||
repo_lang=Język
|
||||
repo_gitignore_helper=Wybierz szablony pliku .gitignore.
|
||||
issue_labels=Etykiety zgłoszenia
|
||||
issue_labels_helper=Wybierz zestaw etykiet zgłoszeń.
|
||||
license=Licencja
|
||||
license_helper=Wybierz plik licencji.
|
||||
readme=README
|
||||
|
@ -613,6 +634,16 @@ forks=Forki
|
|||
pick_reaction=Wybierz swoją reakcję
|
||||
reactions_more=i %d więcej
|
||||
|
||||
template.items=Elementy szablonu
|
||||
template.git_content=Zawartość gita (domyślna gałąź)
|
||||
template.git_hooks=Hooki Git
|
||||
template.git_hooks_tooltip=Aktualnie nie możesz modyfikować lub usuwać hooków gita po ich utworzeniu. Wybierz to tylko wtedy, gdy ufasz szablonowi tego repozytorium.
|
||||
template.webhooks=Webhooki
|
||||
template.topics=Tematy
|
||||
template.avatar=Awatar
|
||||
template.issue_labels=Etykiety zgłoszenia
|
||||
template.one_item=Musisz wybrać co najmniej jeden element szablonu
|
||||
template.invalid=Musisz wybrać repozytorium dla szablonu
|
||||
|
||||
archive.title=To repozytorium jest zarchiwizowane. Możesz wyświetlać pliki i je sklonować, ale nie możesz do niego przepychać zmian lub otwierać zgłoszeń/Pull Requestów.
|
||||
archive.issue.nocomment=To repozytorium jest zarchiwizowane. Nie możesz komentować zgłoszeń.
|
||||
|
@ -643,9 +674,12 @@ migrate.lfs_mirror_unsupported=Tworzenie kopii lustrzanych elementów LFS nie je
|
|||
migrate.migrate_items_options=Przy migracji z GitHub'a, wpisz nazwę użytkownika, a opcje migracji zostaną wyświetlone.
|
||||
migrated_from=Zmigrowane z <a href="%[1]s">%[2]s</a>
|
||||
migrated_from_fake=Zmigrowane z %[1]s
|
||||
migrate.migrating=Migrowanie z <b>%s</b>...
|
||||
migrate.migrating_failed=Migrowanie z <b>%s</b> nie powiodło się.
|
||||
|
||||
mirror_from=kopia lustrzana
|
||||
forked_from=sforkowany z
|
||||
generated_from=wygenerowane z
|
||||
fork_from_self=Nie możesz sforkować swojego własnego repozytorium.
|
||||
fork_guest_user=Zaloguj się, aby sforkować to repozytorium.
|
||||
copy_link=Kopiuj
|
||||
|
@ -691,6 +725,8 @@ stored_lfs=Przechowane za pomocą Git LFS
|
|||
commit_graph=Wykres commitów
|
||||
blame=Wina
|
||||
normal_view=Zwykły widok
|
||||
line=wiersz
|
||||
lines=wiersze
|
||||
|
||||
editor.new_file=Nowy plik
|
||||
editor.upload_file=Wyślij plik
|
||||
|
@ -699,6 +735,7 @@ editor.preview_changes=Podgląd zmian
|
|||
editor.cannot_edit_lfs_files=Pliki LFS nie mogą być edytowane poprzez interfejs przeglądarkowy.
|
||||
editor.cannot_edit_non_text_files=Pliki binarne nie mogą być edytowane poprzez interfejs przeglądarkowy.
|
||||
editor.edit_this_file=Edytuj plik
|
||||
editor.this_file_locked=Plik jest zablokowany
|
||||
editor.must_be_on_a_branch=Musisz znajdować się na gałęzi, aby nanieść lub zaproponować zmiany tego pliku.
|
||||
editor.fork_before_edit=Musisz sforkować to repozytorium, aby nanieść lub zaproponować zmiany tego pliku.
|
||||
editor.delete_this_file=Usuń plik
|
||||
|
@ -716,6 +753,7 @@ editor.delete=Usuń '%s'
|
|||
editor.commit_message_desc=Dodaj dodatkowy rozszerzony opis…
|
||||
editor.commit_directly_to_this_branch=Zmieniaj bezpośrednio gałąź <strong class="branch-name">%s</strong>.
|
||||
editor.create_new_branch=Stwórz <strong>nową gałąź</strong> dla tego commita i rozpocznij Pull Request.
|
||||
editor.create_new_branch_np=Stwórz <strong>nową gałąź</strong> dla tego commita.
|
||||
editor.propose_file_change=Zaproponuj zmiany w pliku
|
||||
editor.new_branch_name_desc=Nazwa nowej gałęzi…
|
||||
editor.cancel=Anuluj
|
||||
|
@ -730,10 +768,13 @@ editor.file_editing_no_longer_exists=Edytowany plik '%s' już nie istnieje w tym
|
|||
editor.file_deleting_no_longer_exists=Usuwany plik '%s' już nie istnieje w tym repozytorium.
|
||||
editor.file_changed_while_editing=Zawartość pliku zmieniła się, odkąd rozpoczęto jego edycję. <a target="_blank" rel="noopener noreferrer" href="%s">Kliknij tutaj</a>, aby zobaczyć zmiany, lub <strong>ponownie Zatwierdź zmiany</strong>, aby je nadpisać.
|
||||
editor.file_already_exists=Plik o nazwie '%s' już istnieje w tym repozytorium.
|
||||
editor.commit_empty_file_header=Commituj pusty plik
|
||||
editor.commit_empty_file_text=Plik, który zamierzasz commitować, jest pusty. Kontynuować?
|
||||
editor.no_changes_to_show=Brak zmian do pokazania.
|
||||
editor.fail_to_update_file=Tworzenie/aktualizacja pliku '%s' nie powiodła się z błędem: %v
|
||||
editor.add_subdir=Dodaj katalog…
|
||||
editor.unable_to_upload_files=Wysyłanie plików do '%s' nie powiodło się z błędem: %v
|
||||
editor.upload_file_is_locked=Plik '%s' jest zablokowany przez %s.
|
||||
editor.upload_files_to_dir=Prześlij pliki do '%s'
|
||||
editor.cannot_commit_to_protected_branch=Nie można commitować do chronionej gałęzi '%s'.
|
||||
|
||||
|
@ -786,12 +827,16 @@ issues.add_milestone_at=`dodaje to do kamienia milowego <b>%s</b> %s`
|
|||
issues.change_milestone_at=`zmienia kamień milowy z <b>%s</b> na <b>%s</b> %s`
|
||||
issues.remove_milestone_at=`usuwa to z kamienia milowego <b>%s</b> %s`
|
||||
issues.deleted_milestone=`(usunięto)`
|
||||
issues.self_assign_at=`przypisuje to na siebie %s`
|
||||
issues.add_assignee_at=`zostaje przypisany(-a) przez <b>%s</b> %s`
|
||||
issues.remove_assignee_at=`usunięto przypisanie przez <b>%s</b> %s`
|
||||
issues.remove_self_assignment=`usuwa swoje przypisanie %s`
|
||||
issues.change_title_at=`zmieniono tytuł z <b><strike>%s</strike></b> na <b>%s</b> %s`
|
||||
issues.delete_branch_at=`usuwa gałąź <b>%s</b> %s`
|
||||
issues.open_tab=Otwarte %d
|
||||
issues.close_tab=Zamknięte %d
|
||||
issues.filter_label=Etykieta
|
||||
issues.filter_label_exclude=`Użyj <code>Alt</code> + <code>Kliknij/Enter</code>, aby wykluczyć etykiety`
|
||||
issues.filter_label_no_select=Wszystkie etykiety
|
||||
issues.filter_milestone=Kamień milowy
|
||||
issues.filter_milestone_no_select=Wszystkie kamienie milowe
|
||||
|
@ -835,6 +880,10 @@ issues.closed_title=Zamknięty
|
|||
issues.num_comments=%d komentarzy
|
||||
issues.commented_at=`skomentował(-a) <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Czy na pewno chcesz usunąć ten komentarz?
|
||||
issues.context.copy_link=Skopiuj link
|
||||
issues.context.quote_reply=Cytuj odpowiedź
|
||||
issues.context.edit=Edytuj
|
||||
issues.context.delete=Usuń
|
||||
issues.no_content=Nie ma jeszcze treści.
|
||||
issues.close_issue=Zamknij
|
||||
issues.close_comment_issue=Skomentuj i zamknij
|
||||
|
@ -844,6 +893,13 @@ issues.create_comment=Skomentuj
|
|||
issues.closed_at=`zamknął(-ęła) <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`otworzył(-a) ponownie <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at=`wspomniał(-a) to zgłoszenie z commita <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_issue_from=`<a href="%[3]s">odwołał(-a) się do tego zgłoszenia %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_pull_from=`<a href="%[3]s">odwołał(-a) się do tego Pull Requesta %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_closing_from=`<a href="%[3]s">odwołał(-a) się do Pull Requesta %[4]s, który zamknie to zgłoszenie</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_reopening_from=`<a href="%[3]s">odwołał(-a) się do Pull Requesta %[4]s, który otworzy na nowo to zgłoszenie</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_closed_from=`<a href="%[3]s">zamknął(-ęła) to zgłoszenie %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_reopened_from=`<a href="%[3]s">ponownie otworzył(-a) to zgłoszenie %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.ref_from=`z %[1]s`
|
||||
issues.poster=Autor
|
||||
issues.collaborator=Współpracownik
|
||||
issues.owner=Właściciel
|
||||
|
@ -956,10 +1012,13 @@ issues.review.self.rejection=Nie możesz zażądać zmian w swoim własnym Pull
|
|||
issues.review.approve=zatwierdza te zmiany %s
|
||||
issues.review.comment=zrecenzowano %s
|
||||
issues.review.content.empty=Musisz pozostawić komentarz o pożądanej zmianie/zmianach.
|
||||
issues.review.reject=zażądał(-a) zmian %s
|
||||
issues.review.pending=Oczekująca
|
||||
issues.review.review=Recenzuj
|
||||
issues.review.reviewers=Recenzenci
|
||||
issues.review.show_outdated=Pokaż przedawnione
|
||||
issues.review.hide_outdated=Ukryj przedawnione
|
||||
issues.assignee.error=Nie udało się dodać wszystkich wybranych osób do przypisanych przez nieoczekiwany błąd.
|
||||
|
||||
pulls.desc=Włącz Pull Requesty i recenzjonowanie kodu.
|
||||
pulls.new=Nowy Pull Request
|
||||
|
@ -972,6 +1031,7 @@ pulls.no_results=Nie znaleziono wyników.
|
|||
pulls.nothing_to_compare=Te gałęzie są sobie równe. Nie ma potrzeby tworzyć Pull Requesta.
|
||||
pulls.has_pull_request=`Pull Request pomiędzy tymi gałęziami już istnieje <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
|
||||
pulls.create=Utwórz Pull Request
|
||||
pulls.title_desc=chce scalić %[1]d commity/ów z <code>%[2]s</code> do <code id="branch_target">%[3]s</code>
|
||||
pulls.merged_title_desc=scala %[1]d commity/ów z <code>%[2]s</code> do <code>%[3]s</code> %[4]s
|
||||
pulls.tab_conversation=Dyskusja
|
||||
pulls.tab_commits=Commity
|
||||
|
@ -986,6 +1046,8 @@ pulls.cannot_merge_work_in_progress=Ten Pull Request został oznaczony jako prac
|
|||
pulls.data_broken=Ten Pull Request jest uszkodzony ze względu na brakujące informacje o forku.
|
||||
pulls.files_conflicted=Ten Pull Request zawiera zmiany konfliktujące z docelową gałęzią.
|
||||
pulls.is_checking=Sprawdzanie konfliktów ze scalaniem w toku. Spróbuj ponownie za chwilę.
|
||||
pulls.required_status_check_failed=Niektóre kontrole stanów nie były pomyślne.
|
||||
pulls.required_status_check_administrator=Jako administrator, możesz wciąż scalić ten Pull Request.
|
||||
pulls.blocked_by_approvals=Ten Pull Request nie ma jeszcze wymaganej ilości zatwierdzeń. Otrzymał %d z %d wymaganych zatwierdzeń.
|
||||
pulls.can_auto_merge_desc=Ten Pull Request może być automatycznie scalony.
|
||||
pulls.cannot_auto_merge_desc=Ten Pull Request nie może być automatycznie scalony z powodu konfliktów.
|
||||
|
@ -993,11 +1055,16 @@ pulls.cannot_auto_merge_helper=Scal ręcznie, aby rozwiązać konflikty.
|
|||
pulls.no_merge_desc=Ten Pull Request nie może zostać scalony, ponieważ wszystkie opcje scalania dla tego repozytorium są wyłączone.
|
||||
pulls.no_merge_helper=Włącz opcje scalania w ustawieniach repozytorium, lub scal ten Pull Request ręcznie.
|
||||
pulls.no_merge_wip=Ten pull request nie może być automatycznie scalony, ponieważ jest oznaczony jako praca w toku.
|
||||
pulls.no_merge_status_check=Ten Pull Request nie może być scalony, bo nie wszystkie kontrole stanów były pomyślne.
|
||||
pulls.merge_pull_request=Scal Pull Request
|
||||
pulls.rebase_merge_pull_request=Zmień bazę i scal
|
||||
pulls.rebase_merge_commit_pull_request=Zmień bazę i scal (--no-ff)
|
||||
pulls.squash_merge_pull_request=Zmiażdż i scal
|
||||
pulls.invalid_merge_option=Nie możesz użyć tej opcji scalania dla tego pull request'a.
|
||||
pulls.merge_conflict=Scalenie nie powiodło się: Wystąpił konflikt przy scalaniu %[1]s<br>%[2]s<br>Porada: Wypróbuj innej strategii scalania
|
||||
pulls.rebase_conflict=Scalenie nie powiodło się: Wystąpił konflikt przy zmianie bazy commita: %[1]s<br>%[2]s<br>%[3]s<br>Porada: Wypróbuj innej strategii scalania
|
||||
pulls.unrelated_histories=Scalenie nie powiodło się: Head scalenia i baza nie mają wspólnej historii. Porada: Spróbuj innej strategii scalania
|
||||
pulls.merge_out_of_date=Scalenie nie powiodło się: Przy generowaniu scalenia, baza została zaktualizowana. Porada: Spróbuj ponownie.
|
||||
pulls.open_unmerged_pull_exists=`Nie możesz wykonać operacji ponownego otwarcia, ponieważ jest już oczekujący pull request (#%d) z identycznymi właściwościami.`
|
||||
pulls.status_checking=Niektóre etapy są w toku
|
||||
pulls.status_checks_success=Wszystkie etapy powiodły się
|
||||
|
@ -1066,6 +1133,9 @@ activity.period.daily=1 dzień
|
|||
activity.period.halfweekly=3 dni
|
||||
activity.period.weekly=1 tydzień
|
||||
activity.period.monthly=1 miesiąc
|
||||
activity.period.quarterly=3 miesiące
|
||||
activity.period.semiyearly=6 miesięcy
|
||||
activity.period.yearly=1 rok
|
||||
activity.overview=Przegląd
|
||||
activity.active_prs_count_1=<strong>%d</strong> aktywny Pull Request
|
||||
activity.active_prs_count_n=<strong>%d</strong> aktywne Pull Requesty
|
||||
|
@ -1218,6 +1288,9 @@ settings.search_user_placeholder=Szukaj użytkownika…
|
|||
settings.org_not_allowed_to_be_collaborator=Organizacji nie można dodać jako współpracownika.
|
||||
settings.change_team_access_not_allowed=Zmiana dostępu zespołu do repozytorium zostało zastrzeżone do właściciela organizacji
|
||||
settings.team_not_in_organization=Zespół nie jest w tej samej organizacji co repozytorium
|
||||
settings.add_team_duplicate=Zespół już posiada repozytorium
|
||||
settings.add_team_success=Zespół ma teraz dostęp do repozytorium.
|
||||
settings.remove_team_success=Dostęp zespołu do repozytorium został usunięty.
|
||||
settings.add_webhook=Dodaj webhooka
|
||||
settings.add_webhook.invalid_channel_name=Nazwa kanału Webhooka nie może być pusta i nie może zawierać jedynie znaku #.
|
||||
settings.hooks_desc=Webhooki automatycznie tworzą zapytania HTTP POST do serwera, kiedy następują pewne zdarzenia w Gitea. Przeczytaj o tym więcej w <a target="_blank" rel="noopener noreferrer" href="%s">przewodniku o Webhookach</a>.
|
||||
|
@ -1267,6 +1340,8 @@ settings.event_pull_request=Pull Request
|
|||
settings.event_pull_request_desc=Pull Request otwarty, zamknięty, ponownie otwarty, zaakceptowany, odrzucony, komentarz oceniający, przypisany, nieprzypisany, etykieta zaktualizowana, etykieta wyczyszczona lub zsynchronizowany.
|
||||
settings.event_push=Wypchnięcie
|
||||
settings.event_push_desc=Wypchnięcie git do repozytorium.
|
||||
settings.branch_filter=Filtr gałęzi
|
||||
settings.branch_filter_desc=Biała lista gałęzi dla przepychania, tworzenia i usuwania gałęzi, określona jako wzorzec glob. Jeśli pusta, lub <code>*</code>, zdarzenia dla wszystkich gałęzi są wyświetlane. Sprawdź dokumentację <a href="https://godoc.org/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> dla składni. Przykładowo: <code>master</code>, <code>{master,release*}</code>.
|
||||
settings.event_repository=Repozytorium
|
||||
settings.event_repository_desc=Repozytorium stworzone lub usunięte.
|
||||
settings.active=Aktywne
|
||||
|
@ -1287,11 +1362,18 @@ settings.add_telegram_hook_desc=Zintegruj <a href="%s">Telegrama</a> ze swoim re
|
|||
settings.add_msteams_hook_desc=Zintegruj <a href="%s">Microsoft Teams</a> ze swoim repozytorium.
|
||||
settings.deploy_keys=Klucze wdrożeniowe
|
||||
settings.add_deploy_key=Dodaj klucz wdrożeniowy
|
||||
settings.deploy_key_desc=Klucze wdrożeniowe mają wyłącznie dostęp "tylko do odczytu" do pobierania danych z repozytorium.
|
||||
settings.is_writable=Włącz dostęp do zapisu
|
||||
settings.is_writable_info=Zezwól temu kluczowi wdrożeniowemu na <strong>przepychanie</strong> zmian do tego repozytorium.
|
||||
settings.no_deploy_keys=W tej chwili nie ma kluczy wdrożeniowych.
|
||||
settings.title=Tytuł
|
||||
settings.deploy_key_content=Treść
|
||||
settings.key_been_used=Klucz wdrożeniowy z identyczną zawartością jest już w użyciu.
|
||||
settings.key_name_used=Klucz wdrożeniowy z identyczną nazwą już istnieje.
|
||||
settings.add_key_success=Klucz wdrożeniowy '%s' został dodany.
|
||||
settings.deploy_key_deletion=Usuń klucz wdrożeniowy
|
||||
settings.deploy_key_deletion_desc=Usunięcie klucza wdrożeniowego wycofa jego dostęp do tego repozytorium. Kontynuować?
|
||||
settings.deploy_key_deletion_success=Klucz wdrożeniowy został usunięty.
|
||||
settings.branches=Gałęzie
|
||||
settings.protected_branch=Ochrona gałęzi
|
||||
settings.protected_branch_can_push=Umożliwić push?
|
||||
|
@ -1299,11 +1381,29 @@ settings.protected_branch_can_push_yes=Możesz wysyłać
|
|||
settings.protected_branch_can_push_no=Nie możesz wysyłać
|
||||
settings.branch_protection=Ochrona gałęzi dla "<b>%s</b>
|
||||
settings.protect_this_branch=Włącz ochronę gałęzi
|
||||
settings.protect_this_branch_desc=Zapobiega usunięciu oraz ogranicza wypychanie i scalanie zmian do tej gałęzi.
|
||||
settings.protect_disable_push=Wyłącz wypychanie
|
||||
settings.protect_disable_push_desc=Wypychanie do tej gałęzi nie będzie możliwe.
|
||||
settings.protect_enable_push=Włącz wypychanie
|
||||
settings.protect_enable_push_desc=Każdy użytkownik z uprawnieniem zapisu będzie miał możliwość wypychania do tej gałęzi (oprócz wymuszonego wypchnięcia).
|
||||
settings.protect_whitelist_committers=Wypychanie ograniczone białą listą
|
||||
settings.protect_whitelist_committers_desc=Tylko dopuszczeni użytkownicy oraz zespoły będą miały możliwość wypychania zmian do tej gałęzi (oprócz wymuszenia wypchnięcia).
|
||||
settings.protect_whitelist_deploy_keys=Biała lista kluczy wdrożeniowych z uprawnieniem zapisu do push'a
|
||||
settings.protect_whitelist_users=Użytkownicy dopuszczeni do wypychania:
|
||||
settings.protect_whitelist_search_users=Szukaj użytkowników…
|
||||
settings.protect_whitelist_teams=Zespoły dopuszczone do wypychania:
|
||||
settings.protect_whitelist_search_teams=Szukaj zespołów…
|
||||
settings.protect_merge_whitelist_committers=Włącz dopuszczenie scalania
|
||||
settings.protect_merge_whitelist_committers_desc=Zezwól jedynie dopuszczonym użytkownikom lub zespołom na scalanie Pull Requestów w tej gałęzi.
|
||||
settings.protect_merge_whitelist_users=Użytkownicy dopuszczeni do scalania:
|
||||
settings.protect_merge_whitelist_teams=Zespoły dopuszczone do scalania:
|
||||
settings.protect_check_status_contexts=Włącz kontrolę stanu
|
||||
settings.protect_check_status_contexts_desc=Wymagaj powodzenia kontroli stanów przed scalaniem Wybierz które kontrole stanów muszą zostać ukończone pomyślnie, zanim gałęzie będą mogły zostać scalone z gałęzią, która pokrywa się z tą zasadą. Kiedy włączone, commity muszą być najpierw przepchnięte do innej gałęzi, a następnie scalone lub przepchnięte bezpośrednio do gałęzi, która pokrywa się z tą zasadą po pomyślnej kontroli stanów. Jeżeli nie zostaną wybrane konteksty, ostatni commit musi zakończyć się powodzeniem niezależnie od kontekstu.
|
||||
settings.protect_check_status_contexts_list=Kontrole stanów w poprzednim tygodniu dla tego repozytorium
|
||||
settings.protect_required_approvals=Wymagane zatwierdzenia:
|
||||
settings.protect_required_approvals_desc=Zezwól na scalanie Pull Requestów tylko z wystarczającą ilością pozytywnych recenzji.
|
||||
settings.protect_approvals_whitelist_enabled=Ogranicz zatwierdzenia do dopuszczonych użytkowników i zespołów
|
||||
settings.protect_approvals_whitelist_enabled_desc=Tylko recenzje pochodzące od użytkowników lub zespołów na białej liście będą liczyły się do wymaganych zatwierdzeń. Bez białej listy zatwierdzeń, recenzja od każdego użytkownika z uprawnieniem zapisu będzie liczyła się do wymaganych zatwierdzeń.
|
||||
settings.protect_approvals_whitelist_users=Dopuszczeni recenzenci:
|
||||
settings.protect_approvals_whitelist_teams=Dopuszczone zespoły do recenzji:
|
||||
settings.add_protected_branch=Włącz ochronę
|
||||
|
@ -1312,6 +1412,7 @@ settings.update_protect_branch_success=Ochrona gałęzi dla gałęzi "%s" zosta
|
|||
settings.remove_protected_branch_success=Ochrona gałęzi dla gałęzi "%s" została wyłączona.
|
||||
settings.protected_branch_deletion=Wyłącz ochronę gałęzi
|
||||
settings.protected_branch_deletion_desc=Wyłączenie ochrony gałęzi pozwoli użytkownikom z uprawnieniami zapisu do przekazywania zmian do gałęzi. Kontynuować?
|
||||
settings.default_branch_desc=Wybierz domyślną gałąź repozytorium dla Pull Requestów i commitów kodu:
|
||||
settings.choose_branch=Wybierz gałąź…
|
||||
settings.no_protected_branch=Nie ma chronionych gałęzi.
|
||||
settings.edit_protected_branch=Zmień
|
||||
|
@ -1322,12 +1423,40 @@ settings.archive.button=Zarchiwizuj repozytorium
|
|||
settings.archive.header=Zarchiwizuj to repozytorium
|
||||
settings.archive.text=Zarchiwizowanie repozytorium sprawi, że będzie ono "tylko do odczytu". Zostanie ukryte z pulpitu, nie będzie możliwe commitowanie, otwieranie zgłoszeń, czy tworzenie Pull Requestów.
|
||||
settings.archive.success=Repozytorium zostało pomyślnie zarchiwizowane.
|
||||
settings.archive.error=Wystąpił błąd przy próbie zarchiwizowania tego repozytorium. Sprawdź dziennik po więcej szczegółów.
|
||||
settings.archive.error_ismirror=Nie możesz archiwizować kopii lustrzanej repozytorium.
|
||||
settings.archive.branchsettings_unavailable=Ustawienia gałęzi nie są dostępne, kiedy repozytorium jest zarchiwizowane.
|
||||
settings.unarchive.button=Przywróć repozytorium
|
||||
settings.unarchive.header=Przywróć to repozytorium z archiwum
|
||||
settings.unarchive.text=Przywrócenie repozytorium z archiwum ponownie umożliwi przyjmowanie commitów i przepchnięć, jak i nowych zgłoszeń oraz Pull Requestów.
|
||||
settings.unarchive.success=Repozytorium zostało pomyślnie przywrócone z archiwum.
|
||||
settings.unarchive.error=Wystąpił błąd przy próbie przywrócenia tego repozytorium z archiwum. Sprawdź dziennik po więcej szczegółów.
|
||||
settings.update_avatar_success=Awatar repozytorium został zaktualizowany.
|
||||
settings.lfs=LFS
|
||||
settings.lfs_filelist=Pliki LFS przechowywane w tym repozytorium
|
||||
settings.lfs_no_lfs_files=Brak plików LFS przechowywanych w tym repozytorium
|
||||
settings.lfs_findcommits=Znajdź commity
|
||||
settings.lfs_lfs_file_no_commits=Nie znaleziono commitów dla tego pliku LFS
|
||||
settings.lfs_delete=Usuń plik LFS z OID %s
|
||||
settings.lfs_delete_warning=Usunięcie pliku LFS może spowodować błędy typu 'obiekt nie istnieje' przy checkout'cie. Czy chcesz kontynuować?
|
||||
settings.lfs_findpointerfiles=Znajdź pliki wskaźnika
|
||||
settings.lfs_pointers.found=Znaleziono %d wskaźników blob - %d powiązanych, %d niepowiązanych (%d brakujących w magazynie danych)
|
||||
settings.lfs_pointers.sha=SHA bloba
|
||||
settings.lfs_pointers.oid=OID
|
||||
settings.lfs_pointers.inRepo=W repozytorium
|
||||
settings.lfs_pointers.exists=Istnieje w magazynie
|
||||
settings.lfs_pointers.accessible=Dostępne dla użytkownika
|
||||
settings.lfs_pointers.associateAccessible=Powiąż dostępne %d OID
|
||||
|
||||
diff.browse_source=Przeglądaj źródła
|
||||
diff.parent=rodzic
|
||||
diff.commit=commit
|
||||
diff.git-notes=Notatki
|
||||
diff.data_not_available=Informacje nt. zmian nie są dostępne
|
||||
diff.options_button=Opcje porównania
|
||||
diff.show_diff_stats=Pokaż statystyki
|
||||
diff.download_patch=Ściągnij plik aktualizacji
|
||||
diff.download_diff=Ściągnij plik porównania
|
||||
diff.show_split_view=Widok podzielony
|
||||
diff.show_unified_view=Zunifikowany widok
|
||||
diff.whitespace_button=Znaki białe
|
||||
|
@ -1338,6 +1467,11 @@ diff.whitespace_ignore_at_eol=Ignoruj zmiany w znakach białych przy EOL
|
|||
diff.stats_desc=<strong>%d zmienionych plików</strong> z <strong>%d dodań</strong> i <strong>%d usunięć</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Wyświetl plik
|
||||
diff.file_before=Przed
|
||||
diff.file_after=Po
|
||||
diff.file_image_width=Szerokość
|
||||
diff.file_image_height=Wysokość
|
||||
diff.file_byte_size=Rozmiar
|
||||
diff.file_suppressed=Plik diff jest za duży
|
||||
diff.too_many_files=Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików
|
||||
diff.comment.placeholder=Zostaw komentarz
|
||||
|
@ -1405,6 +1539,8 @@ branch.restore_failed=Nie udało się przywrócić gałęzi '%s'.
|
|||
branch.protected_deletion_failed=Gałąź '%s' jest chroniona. Nie można jej usunąć.
|
||||
branch.restore=Przywróć gałąź '%s'
|
||||
branch.download=Pobierz gałąź '%s'
|
||||
branch.included_desc=Ta gałąź jest częścią domyślnej gałęzi
|
||||
branch.included=Zawarte
|
||||
|
||||
topic.manage_topics=Zarządzaj tematami
|
||||
topic.done=Gotowe
|
||||
|
@ -1428,6 +1564,7 @@ team_name=Nazwa zespołu
|
|||
team_desc=Opis
|
||||
team_name_helper=Nazwy zespołów powinny być krótkie i łatwe do zapamiętania.
|
||||
team_desc_helper=Opisz cel lub rolę zespołu.
|
||||
team_access_desc=Dostęp do repozytorium
|
||||
team_permission_desc=Uprawnienie
|
||||
team_unit_desc=Zezwól na dostęp do sekcji repozytoriów
|
||||
|
||||
|
@ -1441,6 +1578,7 @@ settings.full_name=Imię i nazwisko
|
|||
settings.website=Strona
|
||||
settings.location=Lokalizacja
|
||||
settings.permission=Uprawnienia
|
||||
settings.repoadminchangeteam=Administrator repozytorium może dać oraz usunąć dostęp zespołom
|
||||
settings.visibility=Widoczność
|
||||
settings.visibility.public=Publiczne
|
||||
settings.visibility.limited=Ukryte (widoczność tylko dla zalogowanych użytkowników)
|
||||
|
@ -1473,6 +1611,8 @@ members.invite_now=Zaproś teraz
|
|||
|
||||
teams.join=Dołącz
|
||||
teams.leave=Opuść
|
||||
teams.can_create_org_repo=Tworzenie repozytoriów
|
||||
teams.can_create_org_repo_helper=Członkowie mogą tworzyć nowe repozytoria w organizacji. Twórca otrzyma uprawnienia administracyjne do nowego repozytorium.
|
||||
teams.read_access=Dostęp do odczytu
|
||||
teams.read_access_helper=Członkowie mogą wyświetlać i klonować repozytoria zespołów.
|
||||
teams.write_access=Dostęp do zapisu
|
||||
|
@ -1492,12 +1632,24 @@ teams.delete_team_success=Zespół został usunięty.
|
|||
teams.read_permission_desc=Ten zespół udziela dostępu <strong>z odczytem</strong>: członkowie mogą wyświetlać i klonować repozytoria zespołu.
|
||||
teams.write_permission_desc=Ten zespół udziela dostępu <strong>z zapisem</strong>: członkowie mogą wyświetlać i wypychać zmiany do repozytoriów zespołu.
|
||||
teams.admin_permission_desc=Ten zespół udziela dostępu <strong>administratora</strong>: członkowie mogą wyświetlać i wypychać zmiany oraz dodawać współpracowników do repozytoriów zespołu.
|
||||
teams.create_repo_permission_desc=Dodatkowo, ten zespół otrzyma uprawnienie <strong>Tworzenie repozytoriów</strong>: jego członkowie mogą tworzyć nowe repozytoria w organizacji.
|
||||
teams.repositories=Repozytoria zespołu
|
||||
teams.search_repo_placeholder=Szukaj repozytorium…
|
||||
teams.remove_all_repos_title=Usuń wszystkie repozytoria zespołu
|
||||
teams.remove_all_repos_desc=Usunie to wszystkie repozytoria przypisane do zespołu.
|
||||
teams.add_all_repos_title=Dodaj wszystkie repozytoria
|
||||
teams.add_all_repos_desc=Doda to wszystkie repozytoria organizacji do przypisanych repozytoriów zespołu.
|
||||
teams.add_nonexistent_repo=Repozytorium, które próbujesz dodać, nie istnieje. Proszę je najpierw utworzyć.
|
||||
teams.add_duplicate_users=Użytkownik jest już członkiem zespołu.
|
||||
teams.repos.none=Ten zespół nie ma dostępu do żadnego repozytorium.
|
||||
teams.members.none=Ten zespół nie ma żadnych członków.
|
||||
teams.specific_repositories=Określone repozytoria
|
||||
teams.specific_repositories_helper=Członkowie uzyskają dostęp wyłącznie do repozytoriów przypisanych do tego zespołu. Wybranie tej opcji <strong>nie</strong> usunie automatycznie repozytoriów dodanych przy pomocy <i>Wszystkie repozytoria</i>.
|
||||
teams.all_repositories=Wszystkie repozytoria
|
||||
teams.all_repositories_helper=Zespół ma dostęp do wszystkich repozytoriów. Wybranie tego <strong>doda wszystkie istniejące</strong> repozytoria do tego zespołu.
|
||||
teams.all_repositories_read_permission_desc=Ten zespół nadaje uprawnienie <strong>Odczytu</strong> do <strong>wszystkich repozytoriów</strong>: jego członkowie mogą wyświetlać i klonować repozytoria.
|
||||
teams.all_repositories_write_permission_desc=Ten zespół nadaje uprawnienie <strong>Zapisu</strong> do <strong>wszystkich repozytoriów</strong>: jego członkowie mogą odczytywać i przesyłać do repozytoriów.
|
||||
teams.all_repositories_admin_permission_desc=Ten zespół nadaje uprawnienia <strong>Administratora</strong> do <strong>wszystkich repozytoriów</strong>: jego członkowie mogą odczytywać, przesyłać oraz dodawać innych współtwórców do repozytoriów.
|
||||
|
||||
[admin]
|
||||
dashboard=Pulpit
|
||||
|
@ -1590,6 +1742,7 @@ users.auth_login_name=Nazwa logowania uwierzytelnienia
|
|||
users.password_helper=Pozostaw hasło puste, aby go nie zmieniać.
|
||||
users.update_profile_success=Konto użytkownika zostało zaktualizowane.
|
||||
users.edit_account=Edytuj konto użytkownika
|
||||
users.max_repo_creation=Maksymalna ilość repozytoriów
|
||||
users.max_repo_creation_desc=(Wpisz -1, aby użyć domyślnego globalnego limitu.)
|
||||
users.is_activated=Konto użytkownika jest aktywne
|
||||
users.prohibit_login=Wyłącz logowanie
|
||||
|
@ -1671,6 +1824,15 @@ auths.oauth2_authURL=URL autoryzacji
|
|||
auths.oauth2_profileURL=URL profilu
|
||||
auths.oauth2_emailURL=URL adresu e-mail
|
||||
auths.enable_auto_register=Włącz automatyczną rejestrację
|
||||
auths.sspi_auto_create_users=Automatycznie twórz użytkowników
|
||||
auths.sspi_auto_create_users_helper=Zezwól metodzie uwierzytelniania SSPI na automatyczne tworzenie nowych kont dla użytkowników, którzy logują się po raz pierwszy
|
||||
auths.sspi_auto_activate_users=Automatycznie aktywuj użytkowników
|
||||
auths.sspi_auto_activate_users_helper=Zezwól metodzie uwierzytelnienia SSPI na automatyczne aktywowanie nowych kont użytkowników
|
||||
auths.sspi_strip_domain_names=Usuwaj nazwy domen z nazw użytkowników
|
||||
auths.sspi_strip_domain_names_helper=Gdy zaznaczone, nazwy domen będą usuwane z nazw logowania (np. zamiast "DOMENA\osoba", czy osoba@example.org" będą po prostu "osoba").
|
||||
auths.sspi_separator_replacement=Używany separator zamiast \, / oraz @
|
||||
auths.sspi_default_language=Domyślny język użytkownika
|
||||
auths.sspi_default_language_helper=Domyślny język dla użytkowników automatycznie stworzonych przy pomocy metody uwierzytelnienia SSPI. Pozostaw puste, jeśli język ma zostać wykryty automatycznie.
|
||||
auths.tips=Wskazówki
|
||||
auths.tips.oauth2.general=Uwierzytelnianie OAuth2
|
||||
auths.tips.oauth2.general.tip=Przy rejestracji nowego uwierzytelnienia OAuth2, URL zwrotny/przekierowań powinien mieć postać <serwer>/user/oauth2/<nazwa uwierzytelnienia>/callback
|
||||
|
@ -1684,6 +1846,7 @@ auths.tip.google_plus=Uzyskaj dane uwierzytelniające klienta OAuth2 z konsoli G
|
|||
auths.tip.openid_connect=Użyj adresu URL OpenID Connect Discovery (<server>/.well-known/openid-configuration), aby określić punkty końcowe
|
||||
auths.tip.twitter=Przejdź na https://dev.twitter.com/apps, stwórz aplikację i upewnij się, że opcja “Allow this application to be used to Sign in with Twitter” jest włączona
|
||||
auths.tip.discord=Zarejestruj nową aplikację na https://discordapp.com/developers/applications/me
|
||||
auths.tip.gitea=Zarejestruj nową aplikację OAuth2. Przewodnik można znaleźć na https://docs.gitea.io/en-us/oauth2-provider/
|
||||
auths.edit=Edytuj źródło uwierzytelniania
|
||||
auths.activated=To źródło uwierzytelniania jest aktywne
|
||||
auths.new_success=Uwierzytelnienie '%s' zostało dodane.
|
||||
|
@ -1692,6 +1855,10 @@ auths.update=Zaktualizuj źródło uwierzytelniania
|
|||
auths.delete=Usuń źródło uwierzytelniania
|
||||
auths.delete_auth_title=Usuń źródło uwierzytelniania
|
||||
auths.delete_auth_desc=Usunięcie źródła uwierzytelniania uniemożliwi użytkownikom używania go do zalogowania się. Kontynuować?
|
||||
auths.still_in_used=Źródło uwierzytelniania jest wciąż w użyciu. Przekonwertuj lub usuń użytkowników przed użyciem tego źródła uwierzytelniania.
|
||||
auths.deletion_success=Źródło uwierzytelniania zostało usunięte.
|
||||
auths.login_source_exist=Źródło uwierzytelniania '%s' już istnieje.
|
||||
auths.login_source_of_type_exist=Źródło uwierzytelniania tego typu już istnieje.
|
||||
|
||||
config.server_config=Konfiguracja serwera
|
||||
config.app_name=Tytuł strony
|
||||
|
@ -1727,6 +1894,7 @@ config.ssh_minimum_key_sizes=Minimalne rozmiary kluczy
|
|||
config.lfs_config=Konfiguracja LFS
|
||||
config.lfs_enabled=Włączone
|
||||
config.lfs_content_path=Ścieżka zawartości LFS
|
||||
config.lfs_http_auth_expiry=Wygasanie uwierzytelnienia LFS HTTP
|
||||
|
||||
config.db_config=Konfiguracja bazy danych
|
||||
config.db_type=Typ
|
||||
|
@ -1737,16 +1905,33 @@ config.db_ssl_mode=SSL
|
|||
config.db_path=Ścieżka
|
||||
|
||||
config.service_config=Konfiguracja usługi
|
||||
config.register_email_confirm=Wymagaj potwierdzenia adresu e-mail przy rejestracji
|
||||
config.disable_register=Wyłącz samodzielną rejestrację
|
||||
config.allow_only_external_registration=Zezwól na rejestrację wyłącznie za pomocą zewnętrznych usług
|
||||
config.enable_openid_signup=Włącz samodzielną rejestrację za pomocą OpenID
|
||||
config.enable_openid_signin=Włącz logowanie za pomocą OpenID
|
||||
config.show_registration_button=Pokazuj przycisk rejestracji
|
||||
config.require_sign_in_view=Wymagaj zalogowania w celu wyświetlania stron
|
||||
config.mail_notify=Włącz powiadomienia e-mail
|
||||
config.disable_key_size_check=Wyłącz sprawdzanie minimalnego rozmiaru klucza
|
||||
config.enable_captcha=Włącz CAPTCHA
|
||||
config.active_code_lives=Ważność kodów aktywacyjnych
|
||||
config.reset_password_code_lives=Czas wygaśnięcia kodu przywracania konta
|
||||
config.default_keep_email_private=Domyślne ukrywanie adresów e-mail
|
||||
config.default_allow_create_organization=Domyślnie zezwalaj na tworzenie organizacji
|
||||
config.enable_timetracking=Włącz śledzenie czasu
|
||||
config.default_enable_timetracking=Domyślnie włącz śledzenie czasu
|
||||
config.default_allow_only_contributors_to_track_time=Zezwalaj wyłącznie współpracownikom na śledzenie czasu
|
||||
config.no_reply_address=Ukryta domena e-mail
|
||||
config.default_visibility_organization=Domyślna widoczność dla nowych organizacji
|
||||
config.default_enable_dependencies=Domyślne włączanie zależności zgłoszeń
|
||||
|
||||
config.webhook_config=Konfiguracja webhooka
|
||||
config.queue_length=Długość kolejki
|
||||
config.deliver_timeout=Limit czasu doręczenia
|
||||
config.skip_tls_verify=Pomiń weryfikację TLS
|
||||
|
||||
config.mailer_config=Konfiguracja dostawcy SMTP
|
||||
config.mailer_enabled=Włączona
|
||||
config.mailer_disable_helo=Wyłącz HELO
|
||||
config.mailer_name=Nazwa
|
||||
|
@ -1754,6 +1939,9 @@ config.mailer_host=Serwer
|
|||
config.mailer_user=Użytkownik
|
||||
config.mailer_use_sendmail=Używaj Sendmail
|
||||
config.mailer_sendmail_path=Ścieżka Sendmail
|
||||
config.mailer_sendmail_args=Dodatkowe argumenty Sendmail
|
||||
config.send_test_mail=Wyślij testową wiadomość e-mail
|
||||
config.test_mail_failed=Nie udało się wysłać testowej wiadomości e-mail do '%s': %v
|
||||
config.test_mail_sent=Testowa wiadomość e-mail została wysłana do '%s'.
|
||||
|
||||
config.oauth_config=Konfiguracja OAuth
|
||||
|
@ -1763,6 +1951,7 @@ config.cache_config=Konfiguracja pamięci podręcznej
|
|||
config.cache_adapter=Adapter pamięci podręcznej
|
||||
config.cache_interval=Interwał pamięci podręcznej
|
||||
config.cache_conn=Połączenie z pamięcią podręczną
|
||||
config.cache_item_ttl=TTL składnika pamięci podręcznej
|
||||
|
||||
config.session_config=Konfiguracja sesji
|
||||
config.session_provider=Dostawca sesji
|
||||
|
@ -1774,6 +1963,7 @@ config.session_life_time=Czas ważności sesji
|
|||
config.https_only=Tylko HTTPS
|
||||
config.cookie_life_time=Czas ważności ciasteczka
|
||||
|
||||
config.picture_config=Konfiguracja obrazu i awataru
|
||||
config.picture_service=Usługa obrazów
|
||||
config.disable_gravatar=Wyłącz Gravatar
|
||||
config.enable_federated_avatar=Włącz sfederowane awatary
|
||||
|
@ -1792,6 +1982,9 @@ config.git_gc_timeout=Limit czasu usuwania śmieci
|
|||
|
||||
config.log_config=Konfiguracja dziennika
|
||||
config.log_mode=Tryb dziennika
|
||||
config.macaron_log_mode=Tryb dziennika Macaron
|
||||
config.own_named_logger=Nazwany logger
|
||||
config.routes_to_default_logger=Ścieżki do domyślnego loggera
|
||||
config.go_log=Używa dziennika Go (domyślne przekierowanie)
|
||||
config.router_log_mode=Tryb dziennika routera
|
||||
config.disabled_logger=Wyłączone
|
||||
|
@ -1810,6 +2003,9 @@ monitor.process=Uruchomione procesy
|
|||
monitor.desc=Opis
|
||||
monitor.start=Czas rozpoczęcia
|
||||
monitor.execute_time=Czas wykonania
|
||||
monitor.process.cancel=Anuluj proces
|
||||
monitor.process.cancel_desc=Anulowanie procesu może spowodować utratę danych
|
||||
monitor.process.cancel_notices=Anuluj: <strong>%s</strong>?
|
||||
|
||||
notices.system_notice_list=Powiadomienia systemu
|
||||
notices.view_detail_header=Pokaż szczegóły powiadomienia
|
||||
|
@ -1846,6 +2042,8 @@ compare_commits_general=Porównaj commity
|
|||
mirror_sync_push=synchronizuje commity do <a href="%[1]s/src/%[2]s">%[3]s</a> w <a href="%[1]s">%[4]s</a> z kopii lustrzanej
|
||||
mirror_sync_create=synchronizuje nowe odwołanie <a href="%s/src/%s">%[2]s</a> do <a href="%[1]s">%[3]s</a> z kopii lustrzanej
|
||||
mirror_sync_delete=synchronizuje i usuwa odwołanie <code>%[2]s</code> w <a href="%[1]s">%[3]s</a> z kopii lustrzanej
|
||||
approve_pull_request=`zatwierdził(-a) <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reject_pull_request=`zaproponował(-a) zmiany dla <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
|
||||
[tool]
|
||||
ago=%s temu
|
||||
|
@ -1887,12 +2085,15 @@ mark_as_unread=Oznacz jak nieprzeczytane
|
|||
mark_all_as_read=Oznacz wszystkie jako przeczytane
|
||||
|
||||
[gpg]
|
||||
default_key=Podpisano domyślnym kluczem
|
||||
error.extract_sign=Nie udało się wyłuskać podpisu
|
||||
error.generate_hash=Nie udało się wygenerować skrótu dla commitu
|
||||
error.no_committer_account=Brak konta powiązanego z adresem e-mail autora
|
||||
error.no_gpg_keys_found=Nie znaleziono w bazie danych klucza dla tego podpisu
|
||||
error.not_signed_commit=Commit nie podpisany
|
||||
error.failed_retrieval_gpg_keys=Nie udało się odzyskać żadnego klucza powiązanego z kontem autora
|
||||
error.probable_bad_signature=OSTRZEŻENIE! Pomimo istnienia klucza z takim ID w bazie, nie weryfikuje on tego commita! Ten commit jest PODEJRZANY.
|
||||
error.probable_bad_default_signature=OSTRZEŻENIE! Pomimo, że domyślny klucz posiada to ID, nie weryfikuje on tego commita! Ten commit jest PODEJRZANY.
|
||||
|
||||
[units]
|
||||
error.no_unit_allowed_repo=Nie masz uprawnień do żadnej sekcji tego repozytorium.
|
||||
|
|
|
@ -959,6 +959,7 @@ issues.add_time=Adicionar tempo manualmente
|
|||
issues.add_time_short=Adicionar tempo
|
||||
issues.add_time_cancel=Cancelar
|
||||
issues.add_time_history=`adicionou tempo gasto %s`
|
||||
issues.del_time_history=`removeu tempo gasto %s`
|
||||
issues.add_time_hours=Horas
|
||||
issues.add_time_minutes=Minutos
|
||||
issues.add_time_sum_to_small=Nenhum tempo foi inserido.
|
||||
|
@ -1016,7 +1017,7 @@ issues.review.content.empty=Você precisa deixar um comentário indicando as alt
|
|||
issues.review.reject=alterações solicitadas %s
|
||||
issues.review.pending=Pendente
|
||||
issues.review.review=Revisão
|
||||
issues.review.reviewers=Revisões
|
||||
issues.review.reviewers=Revisores
|
||||
issues.review.show_outdated=Mostrar desatualizado
|
||||
issues.review.hide_outdated=Ocultar desatualizado
|
||||
issues.assignee.error=Nem todos os responsáveis foram adicionados devido a um erro inesperado.
|
||||
|
@ -1052,6 +1053,7 @@ pulls.is_checking=Verificação de conflitos do merge está em andamento. Tente
|
|||
pulls.required_status_check_failed=Algumas verificações necessárias não foram bem sucedidas.
|
||||
pulls.required_status_check_administrator=Como administrador, você ainda pode aplicar o merge deste pull request.
|
||||
pulls.blocked_by_approvals=Este pull request ainda não possui aprovações suficientes. %d de %d aprovações concedidas.
|
||||
pulls.blocked_by_rejection=Este pull request possui alterações solicitadas por um revisor oficial.
|
||||
pulls.can_auto_merge_desc=O merge deste pull request pode ser aplicado automaticamente.
|
||||
pulls.cannot_auto_merge_desc=O merge deste pull request não pode ser aplicado automaticamente pois há conflitos.
|
||||
pulls.cannot_auto_merge_helper=Faça o merge manualmente para resolver os conflitos.
|
||||
|
@ -1415,6 +1417,8 @@ settings.update_protect_branch_success=Proteção do branch '%s' foi atualizada.
|
|||
settings.remove_protected_branch_success=Proteção do branch '%s' foi desabilitada.
|
||||
settings.protected_branch_deletion=Desabilitar proteção de branch
|
||||
settings.protected_branch_deletion_desc=Desabilitar a proteção de branch permite que os usuários com permissão de escrita realizem push. Continuar?
|
||||
settings.block_rejected_reviews=Bloquear merge em revisões rejeitadas
|
||||
settings.block_rejected_reviews_desc=O merge não será possível quando são solicitadas alterações pelos revisores oficiais, mesmo que haja aprovação suficiente.
|
||||
settings.default_branch_desc=Selecione um branch padrão para pull requests e commits de código:
|
||||
settings.choose_branch=Escolha um branch...
|
||||
settings.no_protected_branch=Não há branches protegidos.
|
||||
|
|
|
@ -66,14 +66,28 @@ forks=Форки
|
|||
activities=Активность
|
||||
pull_requests=Pull Request'ы
|
||||
issues=Задачи
|
||||
milestones=Этапы
|
||||
|
||||
cancel=Отмена
|
||||
add=Добавить
|
||||
add_all=Добавить все
|
||||
remove=Удалить
|
||||
remove_all=Удалить все
|
||||
|
||||
write=Редактирование
|
||||
preview=Предпросмотр
|
||||
loading=Загрузка…
|
||||
|
||||
[startpage]
|
||||
app_desc=Удобный сервис собственного хостинга репозиториев Git
|
||||
install=Простой в установке
|
||||
install_desc=Просто <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">запустите исполняемый файл</a> для вашей платформы. Иcпользуйте Gitea с <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> или <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>, или загрузите <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">пакет</a>.
|
||||
platform=Кроссплатформенный
|
||||
platform_desc=Gitea работает на любой операционной системе, которая может компилировать <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a>: Windows, macOS, Linux, ARM и т. д. Выбирайте, что вам больше нравится!
|
||||
lightweight=Легковесный
|
||||
lightweight_desc=Gitea имеет низкие системные требования и может работать на недорогом Raspberry Pi. Экономьте энергию вашей машины!
|
||||
license=Открытый исходный код
|
||||
license_desc=Всё это на <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>! Присоединяйтесь к нам, внося <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">вклад</a>, чтобы сделать этот проект еще лучше. Не бойтесь помогать!
|
||||
|
||||
[install]
|
||||
install=Установка
|
||||
|
@ -236,6 +250,10 @@ twofa_scratch_token_incorrect=Неверный scratch-код.
|
|||
login_userpass=Вход
|
||||
login_openid=OpenID
|
||||
oauth_signup_tab=Зарегистрировать новый аккаунт
|
||||
oauth_signup_title=Добавить адрес электронной почты и пароль (для восстановления учетной записи)
|
||||
oauth_signup_submit=Полная учетная запись
|
||||
oauth_signin_tab=Ссылка на существующую учетную запись
|
||||
oauth_signin_title=Войдите, чтобы авторизовать связанную учетную запись
|
||||
oauth_signin_submit=Привязать учетную запись
|
||||
openid_connect_submit=Подключить
|
||||
openid_connect_title=Подключение к существующей учетной записи
|
||||
|
@ -244,7 +262,9 @@ openid_register_title=Создать новый аккаунт
|
|||
openid_register_desc=Выбранный OpenID URI неизвестен. Свяжите с новой учетной записью здесь.
|
||||
openid_signin_desc=Введите свой OpenID URI. Например: https://anne.me, bob.openid.org.cn или gnusocial.net/carry.
|
||||
disable_forgot_password_mail=Восстановление аккаунта отключено. Пожалуйста, свяжитесь с администратором сайта.
|
||||
email_domain_blacklisted=С данным email регистрация невозможна.
|
||||
authorize_application=Авторизация приложения
|
||||
authorize_redirect_notice=Вы будете перенаправлены на %s, если вы авторизуете это приложение.
|
||||
authorize_application_created_by=Это приложение было создано %s.
|
||||
authorize_application_description=Если вы предоставите доступ, оно сможет получить доступ и редактировать любую информацию о вашей учетной записи, включая содержимое частных репозиториев и организаций.
|
||||
authorize_title=Разрешить «%s» доступ к вашей учетной записи?
|
||||
|
@ -283,6 +303,8 @@ CommitChoice=Выбор коммита
|
|||
TreeName=Путь к файлу
|
||||
Content=Содержимое
|
||||
|
||||
SSPISeparatorReplacement=Разделитель
|
||||
SSPIDefaultLanguage=Язык по умолчанию
|
||||
|
||||
require_error=` не может быть пустым.`
|
||||
alpha_dash_error=` должен содержать только буквенно-цифровые символы, тире (' - ') и подчеркивания ('_').`
|
||||
|
@ -297,6 +319,7 @@ include_error=` должен содержать '%s'.`
|
|||
unknown_error=Неизвестная ошибка:
|
||||
captcha_incorrect=Капча не пройдена.
|
||||
password_not_match=Пароли не совпадают.
|
||||
lang_select_error=Выберите язык из списка.
|
||||
|
||||
username_been_taken=Имя пользователя уже занято.
|
||||
repo_name_been_taken=Имя репозитория уже используется.
|
||||
|
@ -308,10 +331,15 @@ team_no_units_error=Разрешите доступ хотя бы к одном
|
|||
email_been_used=Этот адрес электронной почты уже используется.
|
||||
openid_been_used=Адрес OpenID '%s' уже используется.
|
||||
username_password_incorrect=Неверное имя пользователя или пароль.
|
||||
password_lowercase_one=Как минимум один строчный символ
|
||||
password_uppercase_one=Как минимум один заглавный символ
|
||||
password_digit_one=По крайней мере одна цифра
|
||||
password_special_one=По крайней мере один специальный символ (знаки пунктуации, скобки, кавычки и т. д.)
|
||||
enterred_invalid_repo_name=Введенное вами имя репозитория неверно.
|
||||
enterred_invalid_owner_name=Имя нового владельца недоступно.
|
||||
enterred_invalid_password=Введенный пароль неверный.
|
||||
user_not_exist=Пользователь не существует.
|
||||
team_not_exist=Команда не существует.
|
||||
last_org_owner=Вы не можете удалить последнего пользователя из команды 'владельцы'. В любой команде должен быть хотя бы один владелец.
|
||||
cannot_add_org_to_team=Организацию нельзя добавить в качестве члена команды.
|
||||
|
||||
|
@ -479,8 +507,12 @@ access_token_deletion_desc=Удаление токена отменит дост
|
|||
delete_token_success=Токен удалён. Приложения, использующие его, больше не имеют доступа к вашему аккаунту.
|
||||
|
||||
manage_oauth2_applications=Управление приложениями OAuth2
|
||||
edit_oauth2_application=Изменить OAuth2 приложение
|
||||
oauth2_applications_desc=Приложения OAuth2 позволяет стороннему приложению к безопасно аутентифицировать пользователей данной установки Gitea.
|
||||
remove_oauth2_application=Удалить OAuth2 приложение
|
||||
remove_oauth2_application_desc=Удаление приложения OAuth2 отменит доступ ко всем подписанным токенам доступа. Продолжить?
|
||||
remove_oauth2_application_success=Приложение было удалено.
|
||||
create_oauth2_application=Создать новое OAuth2 приложение
|
||||
create_oauth2_application_button=Создать приложение
|
||||
create_oauth2_application_success=Вы успешно создали новое приложение OAuth2.
|
||||
update_oauth2_application_success=Изменения настроек приложения OAuth2 успешно применены.
|
||||
|
@ -490,14 +522,20 @@ oauth2_type_web=Веб (например: Node.JS, Tomcat, Go)
|
|||
oauth2_type_native=Нативный (например: телефон, ПК, браузер)
|
||||
oauth2_redirect_uri=URI переадресации
|
||||
save_application=Сохранить
|
||||
oauth2_client_id=ID клиента
|
||||
oauth2_client_secret=Клиентский ключ
|
||||
oauth2_regenerate_secret=Сгенерировать новый ключ
|
||||
oauth2_regenerate_secret_hint=Потеряли свой ключ?
|
||||
oauth2_client_secret_hint=Секретный ключ не будет показан, если вы повторно откроете эту страницу. Пожалуйста сохраните секретный ключ.
|
||||
oauth2_application_edit=Изменить
|
||||
oauth2_application_create_description=Приложения OAuth2 предоставляет стороннему приложению доступ к учетным записям пользователей данного сервиса.
|
||||
oauth2_application_remove_description=Удаление приложения OAuth2 приведёт к отмене его доступа к авторизованным учетным записям пользователей в данном экземпляре. Продолжить?
|
||||
|
||||
authorized_oauth2_applications=Авторизованные приложения OAuth2
|
||||
authorized_oauth2_applications_description=Вы предоставили доступ к вашему персональному аккаунту Gitea этим сторонним приложениям. Пожалуйста, отзовите доступ у приложений, которые больше не используются.
|
||||
revoke_key=Отозвать
|
||||
revoke_oauth2_grant=Отозвать доступ
|
||||
revoke_oauth2_grant_description=Отзыв доступа у этого стороннего приложения не позволит ему получать доступ к вашим данным. Вы уверены?
|
||||
revoke_oauth2_grant_success=Вы успешно отозвали доступ.
|
||||
|
||||
twofa_desc=Двухфакторная проверка подлинности повышает уровень безопасности вашей учётной записи.
|
||||
|
@ -541,12 +579,22 @@ confirm_delete_account=Подтвердите удаление
|
|||
delete_account_title=Удалить аккаунт
|
||||
delete_account_desc=Вы уверены, что хотите навсегда удалить этот аккаунт?
|
||||
|
||||
email_notifications.enable=Включить почтовые уведомления
|
||||
email_notifications.onmention=Только уведомлять по почте при упоминании
|
||||
email_notifications.disable=Отключить почтовые уведомления
|
||||
email_notifications.submit=Установить настройки электронной почты
|
||||
|
||||
[repo]
|
||||
owner=Владелец
|
||||
repo_name=Имя репозитория
|
||||
repo_name_helper=Лучшие названия репозиториев состоят из коротких, легко запоминаемых и уникальных ключевых слов.
|
||||
repo_size=Размер репозитория
|
||||
template=Шаблон
|
||||
template_select=Выбрать шаблон.
|
||||
template_helper=Сделать репозиторий шаблоном
|
||||
template_description=Шаблонные репозитории дают возможность пользователям создавать новые репозитории с той же структурой каталогов, файлами и дополнительными настройками.
|
||||
visibility=Видимость
|
||||
visibility_description=Только владелец или члены организации, при наличии прав, смогут увидеть это.
|
||||
visibility_helper=Сделать репозиторий приватным
|
||||
visibility_helper_forced=Администратор сайта настроил параметр видимости новых репозиториев. Репозиторий приватный по умолчанию.
|
||||
visibility_fork_helper=(Изменение этого повлияет на все форки.)
|
||||
|
@ -554,9 +602,12 @@ clone_helper=Нужна помощь в клонировании? Посетит
|
|||
fork_repo=Форкнуть репозиторий
|
||||
fork_from=Форк от
|
||||
fork_visibility_helper=Видимость форкнутого репозитория изменить нельзя.
|
||||
use_template=Использовать этот шаблон
|
||||
generate_repo=Создать репозиторий
|
||||
repo_desc=Описание
|
||||
repo_lang=Язык
|
||||
repo_gitignore_helper=Выберите шаблон .gitignore.
|
||||
issue_labels=Метки задач
|
||||
license=Лицензия
|
||||
license_helper=Выберите файл лицензии.
|
||||
readme=README
|
||||
|
@ -569,6 +620,8 @@ mirror_prune_desc=Удаление устаревших отслеживаемы
|
|||
mirror_interval=Интервал зеркалирования (допустимые единицы измерения 'h', 'm', 's'). Значение 0 отключает синхронизацию.
|
||||
mirror_interval_invalid=Недопустимый интервал зеркалирования.
|
||||
mirror_address=Клонировать по URL
|
||||
mirror_address_url_invalid=Указанный url неверный. Вы должны правильно экранировать все компоненты url.
|
||||
mirror_address_protocol_invalid=Указанный url неверный. Только http(s):// или git:// местоположения могут быть зеркалированы.
|
||||
mirror_last_synced=Последняя синхронизация
|
||||
watchers=Наблюдатели
|
||||
stargazers=Звездочеты
|
||||
|
@ -576,6 +629,11 @@ forks=Форки
|
|||
pick_reaction=Оставьте свою оценку!
|
||||
reactions_more=и ещё %d
|
||||
|
||||
template.git_hooks=Git хуки
|
||||
template.webhooks=Веб-хуки
|
||||
template.topics=Темы
|
||||
template.avatar=Аватар
|
||||
template.issue_labels=Метки задач
|
||||
|
||||
archive.title=Это архивный репозиторий. Вы можете его клонировать или просматривать файлы, но не вносить изменения или открывать задачи/запросы на слияние.
|
||||
archive.issue.nocomment=Этот репозиторий в архиве. Вы не можете комментировать задачи.
|
||||
|
@ -588,6 +646,7 @@ form.name_pattern_not_allowed=Шаблон имени репозитория '%s
|
|||
need_auth=Требуется авторизация
|
||||
migrate_type=Тип миграции
|
||||
migrate_type_helper=Этот репозиторий будет <span class="text blue">зеркалом</span>
|
||||
migrate_items=Элементы миграции
|
||||
migrate_items_wiki=Вики
|
||||
migrate_items_milestones=Этапы
|
||||
migrate_items_labels=Метки
|
||||
|
@ -602,6 +661,9 @@ migrate.permission_denied=У вас нет прав на импорт локал
|
|||
migrate.invalid_local_path=Недопустимый локальный путь. Возможно он не существует или не является папкой.
|
||||
migrate.failed=Миграция не удалась: %v
|
||||
migrate.lfs_mirror_unsupported=Зеркалирование LFS объектов не поддерживается - используйте 'git lfs fetch --all' и 'git lfs push --all' вручную.
|
||||
migrate.migrate_items_options=При миграции из GitHub, укажите имя пользователя - и появятся параметры миграции.
|
||||
migrated_from=Перенесено с <a href="%[1]s">%[2]s</a>
|
||||
migrated_from_fake=Перенесено с %[1]s
|
||||
|
||||
mirror_from=зеркало из
|
||||
forked_from=форкнуто от
|
||||
|
@ -648,6 +710,9 @@ video_not_supported_in_browser=Ваш браузер не поддерживае
|
|||
audio_not_supported_in_browser=Ваш браузер не поддерживает HTML5 'audio' тэг.
|
||||
stored_lfs=Хранится Git LFS
|
||||
commit_graph=Граф коммитов
|
||||
normal_view=Обычный вид
|
||||
line=строка
|
||||
lines=строки
|
||||
|
||||
editor.new_file=Новый файл
|
||||
editor.upload_file=Загрузить файл
|
||||
|
@ -656,6 +721,7 @@ editor.preview_changes=Просмотр изменений
|
|||
editor.cannot_edit_lfs_files=LFS файлы невозможно редактировать в веб-интерфейсе.
|
||||
editor.cannot_edit_non_text_files=Двоичные файлы нельзя редактировать в веб-интерфейсе.
|
||||
editor.edit_this_file=Редактировать файл
|
||||
editor.this_file_locked=Файл заблокирован
|
||||
editor.must_be_on_a_branch=Чтобы внести или предложить изменения этого файла, необходимо выбрать ветку.
|
||||
editor.fork_before_edit=Необходимо сделать форк этого репозитория, чтобы внести или предложить изменения этого файла.
|
||||
editor.delete_this_file=Удалить файл
|
||||
|
@ -666,12 +732,15 @@ editor.filename_help=Чтобы добавить каталог, просто н
|
|||
editor.or=или
|
||||
editor.cancel_lower=Отменить
|
||||
editor.commit_changes=Сохранить правки
|
||||
editor.add_tmpl=Добавить '<filename>'
|
||||
editor.add=Добавить '%s'
|
||||
editor.update=Изменить '%s'
|
||||
editor.delete=Удалить '%s'
|
||||
editor.commit_message_desc=Добавьте необязательное расширенное описание…
|
||||
editor.commit_directly_to_this_branch=Сделайте коммит прямо в ветку <strong class="branch-name">%s</strong>.
|
||||
editor.create_new_branch=Создайте <strong>новую ветку</strong> для этого коммита, и сделайте Pull Request.
|
||||
editor.create_new_branch_np=Создать <strong>новую ветку</strong> для этого коммита.
|
||||
editor.propose_file_change=Предложить изменение файла
|
||||
editor.new_branch_name_desc=Новое название ветки…
|
||||
editor.cancel=Отмена
|
||||
editor.filename_cannot_be_empty=Имя файла не может быть пустым.
|
||||
|
@ -685,6 +754,7 @@ editor.file_editing_no_longer_exists=Редактируемый файл '%s' б
|
|||
editor.file_deleting_no_longer_exists=Удаляемый файл '%s' больше не существует в этом репозитории.
|
||||
editor.file_changed_while_editing=Содержимое файла изменилось с момента начала редактирования. <a target="_blank" rel="noopener noreferrer" href="%s">Нажмите здесь</a>, чтобы увидеть, что было изменено, или <strong>Зафиксировать изменения снова</strong>, чтобы заменить их.
|
||||
editor.file_already_exists=Файл с именем '%s' уже существует в репозитории.
|
||||
editor.commit_empty_file_header=Закоммитить пустой файл
|
||||
editor.no_changes_to_show=Нет изменений.
|
||||
editor.fail_to_update_file=Не удалось обновить/создать файл «%s» из-за ошибки: %v
|
||||
editor.add_subdir=Добавить каталог…
|
||||
|
@ -696,6 +766,7 @@ commits.desc=Просмотр истории изменений исходног
|
|||
commits.commits=коммитов
|
||||
commits.no_commits=Ничего общего в коммитах. '%s' и '%s' имеют совершенно разные истории.
|
||||
commits.search=Поиск коммитов…
|
||||
commits.search.tooltip=Вы можете предварять ключевые слова словами "author:", "committer:", "after:", или "before:", например, "revert author:Alice before:2019-04-01".
|
||||
commits.find=Поиск
|
||||
commits.search_all=Все ветки
|
||||
commits.author=Автор
|
||||
|
@ -711,6 +782,7 @@ ext_issues.desc=Ссылка на внешнюю систему отслежив
|
|||
|
||||
issues.desc=Организация отчетов об ошибках, задач и этапов.
|
||||
issues.new=Новая задача
|
||||
issues.new.title_empty=Заголовок не может быть пустым
|
||||
issues.new.labels=Метки
|
||||
issues.new.no_label=Нет меток
|
||||
issues.new.clear_labels=Отчистить метки
|
||||
|
@ -742,6 +814,7 @@ issues.deleted_milestone=`(удалено)`
|
|||
issues.self_assign_at=`самоназначился %s`
|
||||
issues.add_assignee_at=`был назначен <b>%s</b> %s`
|
||||
issues.remove_assignee_at=`был снят с назначения <b>%s</b> %s`
|
||||
issues.remove_self_assignment=`убрал их назначение %s`
|
||||
issues.delete_branch_at=`удалена ветка <b>%s</b> %s`
|
||||
issues.open_tab=%d открыто(ы)
|
||||
issues.close_tab=%d закрыто(ы)
|
||||
|
@ -778,6 +851,7 @@ issues.action_assignee=Ответственный
|
|||
issues.action_assignee_no_select=Нет ответственного
|
||||
issues.opened_by=открыта %[1]s <a href="%[2]s">%[3]s</a>
|
||||
pulls.merged_by=принят %[1]s <a href="%[2]s">%[3]s</a>
|
||||
pulls.merged_by_fake=%[1]s слита пользователем %[2]s
|
||||
issues.closed_by=закрыта %[1]s <a href="%[2]s">%[3]s</a>
|
||||
issues.opened_by_fake=%[1]s открыта %[2]s
|
||||
issues.closed_by_fake=%[1]s закрыта пользователем %[2]s
|
||||
|
@ -788,6 +862,9 @@ issues.closed_title=Закрыто
|
|||
issues.num_comments=комментариев: %d
|
||||
issues.commented_at=`прокомментировал <a href="#%s"> %s</a>`
|
||||
issues.delete_comment_confirm=Вы уверены, что хотите удалить этот комментарий?
|
||||
issues.context.copy_link=Копировать ссылку
|
||||
issues.context.edit=Редактировать
|
||||
issues.context.delete=Удалить
|
||||
issues.no_content=Пока нет содержимого.
|
||||
issues.close_issue=Закрыть
|
||||
issues.close_comment_issue=Прокомментировать и закрыть
|
||||
|
@ -829,6 +906,7 @@ issues.unlock=Снять ограничение
|
|||
issues.lock.unknown_reason=Для ограничения обсуждения необходимо указать причину.
|
||||
issues.lock_duplicate=Обсуждение задачи уже ограничено.
|
||||
issues.unlock_error=Невозможно снять несуществующее ограничение обсуждения.
|
||||
issues.lock_with_reason=заблокировано как <strong>%s</strong> и ограничено обсуждение для соучастников %s
|
||||
issues.lock_no_reason=ограничил(а) обсуждение задачи кругом соавторов %s
|
||||
issues.unlock_comment=снял(а) ограничение %s
|
||||
issues.lock_confirm=Ограничить
|
||||
|
@ -846,6 +924,7 @@ issues.tracker=Отслеживание времени
|
|||
issues.start_tracking_short=Начать
|
||||
issues.start_tracking=Начать отслеживание времени
|
||||
issues.start_tracking_history=`начал работать %s`
|
||||
issues.tracker_auto_close=Таймер будет остановлен автоматически, когда эта проблема будет закрыта
|
||||
issues.tracking_already_started=`Вы уже начали отслеживать время для этой <a href="%s">задачи</a>!`
|
||||
issues.stop_tracking=Остановить
|
||||
issues.stop_tracking_history=`перестал работать %s`
|
||||
|
@ -914,6 +993,7 @@ issues.review.reviewers=Рецензенты
|
|||
issues.review.show_outdated=Показать устаревшие
|
||||
issues.review.hide_outdated=Скрыть устаревшие
|
||||
|
||||
pulls.desc=Включить запросы на слияние и проверки кода.
|
||||
pulls.new=Новый Pull Request
|
||||
pulls.compare_changes=Новый Pull Request
|
||||
pulls.compare_changes_desc=Сравнить две ветки и создать запрос на слияние для изменений.
|
||||
|
@ -924,11 +1004,13 @@ pulls.no_results=Результатов не найдено.
|
|||
pulls.nothing_to_compare=Нечего сравнивать, родительская и текущая ветка одинаковые.
|
||||
pulls.has_pull_request=`Уже существует запрос на слияние между двумя целями: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
|
||||
pulls.create=Создать Pull Request
|
||||
pulls.title_desc=хочет смерджить %[1]d коммит(ов) из <code>%[2]s</code> в <code id="branch_target">%[3]s</code>
|
||||
pulls.merged_title_desc=слито %[1]d коммит(ов) из <code>%[2]s</code> в <code>%[3]s</code> %[4]s
|
||||
pulls.tab_conversation=Обсуждение
|
||||
pulls.tab_commits=Коммиты
|
||||
pulls.tab_files=Измененные файлы
|
||||
pulls.reopen_to_merge=Пожалуйста, переоткройте этот Pull Request для выполнения слияния.
|
||||
pulls.cant_reopen_deleted_branch=Этот запрос на слияние не может быть открыт заново, потому что ветка была удалена.
|
||||
pulls.merged=Слито
|
||||
pulls.has_merged=Слияние этого запроса успешно завершено.
|
||||
pulls.title_wip_desc=`<a href="#">Добавьте <strong>%s</strong> в начало заголовка</a> для защиты от случайного досрочного принятия Pull Request'а.`
|
||||
|
@ -949,6 +1031,9 @@ pulls.rebase_merge_commit_pull_request=Выполнить rebase и принят
|
|||
pulls.squash_merge_pull_request=Объединить и принять PR
|
||||
pulls.invalid_merge_option=Этот параметр слияния нельзя использовать для этого Pull Request'а.
|
||||
pulls.open_unmerged_pull_exists=`Вы не можете снова открыть, поскольку уже существует запрос на слияние (#%d) из того же репозитория с той же информацией о слиянии и ожидающий слияния.`
|
||||
pulls.status_checking=Выполняются некоторые проверки
|
||||
pulls.status_checks_success=Все проверки выполнены успешно
|
||||
pulls.status_checks_error=Некоторые проверки не удались
|
||||
|
||||
milestones.new=Новый этап
|
||||
milestones.open_tab=%d открыты
|
||||
|
@ -997,6 +1082,8 @@ wiki.save_page=Сохранить страницу
|
|||
wiki.last_commit_info=%s редактировал эту страницу %s
|
||||
wiki.edit_page_button=Редактировать
|
||||
wiki.new_page_button=Новая страница
|
||||
wiki.file_revision=Версия страницы
|
||||
wiki.back_to_wiki=Вернуться на wiki страницу
|
||||
wiki.delete_page_button=Удалить страницу
|
||||
wiki.delete_page_notice_1=Удаление вики-страницы '%s' не может быть отменено. Продолжить?
|
||||
wiki.page_already_exists=Вики-страница с таким именем уже существует.
|
||||
|
@ -1010,6 +1097,9 @@ activity.period.daily=1 день
|
|||
activity.period.halfweekly=3 дня
|
||||
activity.period.weekly=1 неделя
|
||||
activity.period.monthly=1 месяц
|
||||
activity.period.quarterly=3 месяца
|
||||
activity.period.semiyearly=6 месяцев
|
||||
activity.period.yearly=1 год
|
||||
activity.overview=Обзор
|
||||
activity.active_prs_count_1=<strong>%d</strong> Активный Pull Request
|
||||
activity.active_prs_count_n=<strong>%d</strong> Активных Pull Request'ов
|
||||
|
@ -1046,6 +1136,26 @@ activity.title.releases_n=%d релизов
|
|||
activity.title.releases_published_by=%s опубликованы %s
|
||||
activity.published_release_label=Опубликовано
|
||||
activity.no_git_activity=В этот период не было новых коммитов.
|
||||
activity.git_stats_exclude_merges=За исключением слияний,
|
||||
activity.git_stats_author_1=%d автор
|
||||
activity.git_stats_author_n=%d автора(ов)
|
||||
activity.git_stats_pushed_1=отправлен
|
||||
activity.git_stats_pushed_n=отправлено
|
||||
activity.git_stats_commit_1=%d коммит
|
||||
activity.git_stats_commit_n=%d коммитов
|
||||
activity.git_stats_push_to_branch=к %s и
|
||||
activity.git_stats_push_to_all_branches=во все ветки.
|
||||
activity.git_stats_on_default_branch=На %s,
|
||||
activity.git_stats_file_1=%d файл
|
||||
activity.git_stats_file_n=%d файлов
|
||||
activity.git_stats_files_changed_1=изменилось
|
||||
activity.git_stats_files_changed_n=изменено
|
||||
activity.git_stats_additions=и там было
|
||||
activity.git_stats_addition_1=%d добавление
|
||||
activity.git_stats_addition_n=%d добавлений
|
||||
activity.git_stats_and_deletions=и
|
||||
activity.git_stats_deletion_1=%d удаление
|
||||
activity.git_stats_deletion_n=%d удалений
|
||||
|
||||
search=Поиск
|
||||
search.search_repo=Поиск по репозиторию
|
||||
|
@ -1058,6 +1168,7 @@ settings.collaboration=Соавторы
|
|||
settings.collaboration.admin=Администратор
|
||||
settings.collaboration.write=Запись
|
||||
settings.collaboration.read=Просмотр
|
||||
settings.collaboration.owner=Владелец
|
||||
settings.collaboration.undefined=Не определено
|
||||
settings.hooks=Автоматическое обновление
|
||||
settings.githooks=Git хуки
|
||||
|
@ -1065,6 +1176,9 @@ settings.basic_settings=Основные параметры
|
|||
settings.mirror_settings=Настройки зеркалирования
|
||||
settings.sync_mirror=Синхронизировать
|
||||
settings.mirror_sync_in_progress=Синхронизируются репозитории-зеркала. Подождите минуту и обновите страницу.
|
||||
settings.email_notifications.enable=Включить почтовые уведомления
|
||||
settings.email_notifications.disable=Отключить почтовые уведомления
|
||||
settings.email_notifications.submit=Установить настройки электронной почты
|
||||
settings.site=Сайт
|
||||
settings.update_settings=Обновить настройки
|
||||
settings.advanced_settings=Расширенные настройки
|
||||
|
@ -1135,6 +1249,10 @@ settings.collaborator_deletion_desc=Этот пользователь больш
|
|||
settings.remove_collaborator_success=Соавтор удалён.
|
||||
settings.search_user_placeholder=Поиск пользователя…
|
||||
settings.org_not_allowed_to_be_collaborator=Организации не могут быть добавлены как соавторы.
|
||||
settings.team_not_in_organization=Команда не в той же организации, что и репозиторий
|
||||
settings.add_team_duplicate=Команда уже имеет репозиторий
|
||||
settings.add_team_success=Команда теперь имеет доступ к репозиторию.
|
||||
settings.remove_team_success=Доступ команды к репозиторию был удален.
|
||||
settings.add_webhook=Добавить Webhook
|
||||
settings.add_webhook.invalid_channel_name=Имя канала не может быть пустым или состоять только из символа #.
|
||||
settings.hooks_desc=Webhooks позволяют внешним службам получать уведомления при возникновении определенных событий на Gitea. При возникновении указанных событий мы отправим запрос POST на каждый заданный вами URL. Узнать больше можно в нашем <a target="_blank" rel="noopener noreferrer" href="%s">руководстве по webhooks</a>.
|
||||
|
@ -1184,6 +1302,7 @@ settings.event_pull_request=Pull Request
|
|||
settings.event_pull_request_desc=Запрос слияния открыт, закрыт, переоткрыт, изменён, одобрен, отклонён, рецензирован, назначен, снят, метка обновлена, метка убрана, или синхронизирован.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Push в репозиторий.
|
||||
settings.branch_filter=Фильтр веток
|
||||
settings.event_repository=Репозиторий
|
||||
settings.event_repository_desc=Репозиторий создан или удален.
|
||||
settings.active=Активный
|
||||
|
@ -1223,6 +1342,8 @@ settings.protected_branch_can_push_yes=Вы можете выполнять push
|
|||
settings.protected_branch_can_push_no=Вы не можете выполнять push
|
||||
settings.branch_protection=Защита ветки <b>%s</b>
|
||||
settings.protect_this_branch=Защитить эту ветку
|
||||
settings.protect_disable_push=Отключить Push
|
||||
settings.protect_enable_push=Включить Push
|
||||
settings.protect_whitelist_users=Пользователи, которые могут делать push в эту ветку:
|
||||
settings.protect_whitelist_search_users=Поиск пользователей…
|
||||
settings.protect_whitelist_teams=Команды, члены которых могут делать push в эту ветку:
|
||||
|
@ -1231,7 +1352,9 @@ settings.protect_merge_whitelist_committers=Ограничить право на
|
|||
settings.protect_merge_whitelist_committers_desc=Вы можете добавлять пользователей или целые команды в "белый" список этой ветки. Только присутствующие в списке смогут принимать Pull Request'ы. В противном случае любой с правами на запись в репозиторий будет обладать такой возможностью.
|
||||
settings.protect_merge_whitelist_users=Пользователи с правом на принятие Pull Request'ов в эту ветку:
|
||||
settings.protect_merge_whitelist_teams=Команды, члены которых обладают правом на принятие Pull Request'ов в эту ветку:
|
||||
settings.protect_check_status_contexts=Включить проверку статуса
|
||||
settings.protect_required_approvals=Необходимые одобрения:
|
||||
settings.protect_required_approvals_desc=Разрешить объединение Pull Request'а только с достаточным количеством положительных отзывов.
|
||||
settings.protect_approvals_whitelist_users=Рецензенты в белом списке:
|
||||
settings.protect_approvals_whitelist_teams=Команды в белом списке для рецензирования:
|
||||
settings.add_protected_branch=Включить защиту
|
||||
|
@ -1251,16 +1374,31 @@ settings.archive.button=Архивировать репозиторий
|
|||
settings.archive.header=Архивировать этот репозиторий
|
||||
settings.archive.text=Архивация репозитория переведет его в режим read-only. Он будет скрыт из панели управления, создание задач, запросов на слияние, или создание коммитов будут запрещены.
|
||||
settings.archive.success=Репозиторий был успешно архивирован.
|
||||
settings.archive.error=Ошибка при попытке архивировать репозиторий. Смотрите логи для получения подробностей.
|
||||
settings.archive.error_ismirror=Вы не можете поместить зеркалируемый репозиторий в архив.
|
||||
settings.archive.branchsettings_unavailable=Настройки ветки недоступны, если репозиторий архивирован.
|
||||
settings.unarchive.button=Разархивировать
|
||||
settings.unarchive.header=Разархивировать этот репозиторий
|
||||
settings.unarchive.text=Разархивация восстанавливает возможность совершать push в репозиторий, создавать новые коммиты, задачи и запросы на слияние.
|
||||
settings.unarchive.success=Репозиторий был успешно разархивирован.
|
||||
settings.unarchive.error=Ошибка при попытке разархивировать репозиторий. Смотрите логи для получения подробностей.
|
||||
settings.update_avatar_success=Аватар репозитория обновлен.
|
||||
settings.lfs=LFS
|
||||
settings.lfs_findcommits=Найти коммиты
|
||||
settings.lfs_force_unlock=Принудительная разблокировка
|
||||
settings.lfs_pointers.sha=Blob SHA
|
||||
settings.lfs_pointers.oid=OID
|
||||
settings.lfs_pointers.inRepo=В репозитории
|
||||
|
||||
diff.browse_source=Просмотр исходного кода
|
||||
diff.parent=Родитель
|
||||
diff.commit=Сommit
|
||||
diff.git-notes=Заметки
|
||||
diff.data_not_available=Разница недоступна
|
||||
diff.options_button=Опции Diff
|
||||
diff.show_diff_stats=Показать статистику
|
||||
diff.download_patch=Скачать Patch файл
|
||||
diff.download_diff=Скачать Diff файл
|
||||
diff.show_split_view=Разделённый вид
|
||||
diff.show_unified_view=Единый вид
|
||||
diff.whitespace_button=Пробелы
|
||||
|
@ -1271,6 +1409,11 @@ diff.whitespace_ignore_at_eol=Игнорировать изменения в п
|
|||
diff.stats_desc=<strong> %d измененных файлов</strong>: <strong>%d добавлений</strong> и <strong>%d удалений</strong>
|
||||
diff.bin=Двоичные данные
|
||||
diff.view_file=Просмотреть файл
|
||||
diff.file_before=До
|
||||
diff.file_after=После
|
||||
diff.file_image_width=Ширина
|
||||
diff.file_image_height=Высота
|
||||
diff.file_byte_size=Размер
|
||||
diff.file_suppressed=Разница между файлами не показана из-за своего большого размера
|
||||
diff.too_many_files=Некоторые файлы не были показаны из-за большого количества измененных файлов
|
||||
diff.comment.placeholder=Оставить комментарий
|
||||
|
@ -1284,6 +1427,7 @@ diff.review.header=Отправить рецензию
|
|||
diff.review.placeholder=Рецензионный комментарий
|
||||
diff.review.comment=Комментировать
|
||||
diff.review.approve=Утвердить
|
||||
diff.review.reject=Запрос изменений
|
||||
|
||||
releases.desc=Релизы позволяют организовать хранение готовых сборок проекта в строгом хронологически верном порядке.
|
||||
release.releases=Релизы
|
||||
|
@ -1335,6 +1479,8 @@ branch.deleted_by=Удалён %s
|
|||
branch.restore_success=Ветка '%s' восстановлена.
|
||||
branch.restore_failed=Не удалось восстановить ветку '%s'.
|
||||
branch.protected_deletion_failed=Ветка '%s' защищена. Её нельзя удалить.
|
||||
branch.restore=Восстановить ветку '%s'
|
||||
branch.download=Скачать ветку '%s'
|
||||
|
||||
topic.manage_topics=Редактировать тематические метки
|
||||
topic.done=Сохранить
|
||||
|
@ -1358,6 +1504,7 @@ team_name=Название команды
|
|||
team_desc=Описание
|
||||
team_name_helper=Названия команд должны быть короткими и запоминающимися.
|
||||
team_desc_helper=Что это за команда?
|
||||
team_access_desc=Доступ к репозиторию
|
||||
team_permission_desc=Разрешение
|
||||
team_unit_desc=Разрешить доступ к разделам репозитория
|
||||
|
||||
|
@ -1370,7 +1517,11 @@ settings.options=Организация
|
|||
settings.full_name=Полное имя
|
||||
settings.website=Сайт
|
||||
settings.location=Местоположение
|
||||
settings.permission=Разрешения
|
||||
settings.visibility=Видимость
|
||||
settings.visibility.public=Публичный
|
||||
settings.visibility.limited=Ограничено (Видно только для авторизованных пользователей)
|
||||
settings.visibility.private=Частный (Видимый только для участников организации)
|
||||
|
||||
settings.update_settings=Обновить настройки
|
||||
settings.update_setting_success=Настройки организации обновлены.
|
||||
|
@ -1399,6 +1550,7 @@ members.invite_now=Пригласите сейчас
|
|||
|
||||
teams.join=Объединить
|
||||
teams.leave=Выйти
|
||||
teams.can_create_org_repo=Создать репозитории
|
||||
teams.read_access=Доступ на чтение
|
||||
teams.read_access_helper=Участники могут просматривать и клонировать командные репозитории.
|
||||
teams.write_access=Доступ на запись
|
||||
|
@ -1420,16 +1572,20 @@ teams.write_permission_desc=Эта команда предоставляет д
|
|||
teams.admin_permission_desc=Эта команда дает <strong>административный</strong> доступ: участники могут читать, пушить и добавлять соавторов к ее репозиториям.
|
||||
teams.repositories=Репозитории группы разработки
|
||||
teams.search_repo_placeholder=Поиск репозитория…
|
||||
teams.remove_all_repos_title=Удалить все репозитории команды
|
||||
teams.add_all_repos_title=Добавить все репозитории
|
||||
teams.add_nonexistent_repo=Вы добавляете в отсутствующий репозиторий, пожалуйста сначала его создайте.
|
||||
teams.add_duplicate_users=Пользователь уже состоит в команде.
|
||||
teams.repos.none=Для этой команды нет доступных репозиториев.
|
||||
teams.members.none=В этой команде нет участников.
|
||||
teams.all_repositories=Все репозитории
|
||||
|
||||
[admin]
|
||||
dashboard=Панель
|
||||
users=Пользователи
|
||||
organizations=Организации
|
||||
repositories=Репозитории
|
||||
hooks=Стандартные Веб-хуки
|
||||
authentication=Авторизация
|
||||
config=Конфигурация
|
||||
notices=Системные уведомления
|
||||
|
@ -1453,6 +1609,8 @@ dashboard.delete_repo_archives=Удаление всех архивов репо
|
|||
dashboard.delete_repo_archives_success=Все архивы репозиториев удалены.
|
||||
dashboard.delete_missing_repos=Удалить все записи о репозиториях с отсутствующими файлами Git
|
||||
dashboard.delete_missing_repos_success=Все записи о репозиториях с отсутствующими файлами Git удалены.
|
||||
dashboard.delete_generated_repository_avatars=Удалить генерированные аватары репозитория
|
||||
dashboard.delete_generated_repository_avatars_success=Генерированные аватары репозитория были удалены.
|
||||
dashboard.git_gc_repos=Выполнить сборку мусора для всех репозиториев
|
||||
dashboard.git_gc_repos_success=Сборка мусора выполнена для всех репозиториев.
|
||||
dashboard.resync_all_sshkeys=Перезаписать файл '.ssh/authorized_keys' для SSH ключей Gitea. Не требуется для встроенного SSH сервера.
|
||||
|
@ -1513,6 +1671,7 @@ users.auth_login_name=Логин для авторизации
|
|||
users.password_helper=Оставьте пустым, чтобы оставить без изменений.
|
||||
users.update_profile_success=Профиль учетной записи обновлен успешно.
|
||||
users.edit_account=Изменение учетной записи
|
||||
users.max_repo_creation=Максимальное количество репозиториев
|
||||
users.max_repo_creation_desc=(Установите -1 для использования стандартного глобального значения предела)
|
||||
users.is_activated=Эта учетная запись активирована
|
||||
users.prohibit_login=Этой учетной записи запрещен вход в систему
|
||||
|
@ -1542,6 +1701,8 @@ repos.forks=Форки
|
|||
repos.issues=Задачи
|
||||
repos.size=Размер
|
||||
|
||||
hooks.add_webhook=Добавить стандартный Веб-хук
|
||||
hooks.update_webhook=Обновить стандартный Веб-хук
|
||||
|
||||
auths.auth_manage_panel=Управление аутентификацией
|
||||
auths.new=Добавить новый источник
|
||||
|
@ -1591,6 +1752,14 @@ auths.oauth2_authURL=URL авторизации
|
|||
auths.oauth2_profileURL=URL аккаунта
|
||||
auths.oauth2_emailURL=URL-адрес электронной почты
|
||||
auths.enable_auto_register=Включить автоматическую регистрацию
|
||||
auths.sspi_auto_create_users=Автоматически создавать пользователей
|
||||
auths.sspi_auto_create_users_helper=Разрешить метод аутентификации SSPI для автоматического создания новых учетных записей для пользователей, которые впервые входят в систему
|
||||
auths.sspi_auto_activate_users=Автоматически активировать пользователей
|
||||
auths.sspi_auto_activate_users_helper=Разрешить метод аутентификации SSPI для автоматической активации новых пользователей
|
||||
auths.sspi_strip_domain_names=Удалять доменные имена из имён пользователей
|
||||
auths.sspi_separator_replacement=Разделитель для использования вместо \, / и @
|
||||
auths.sspi_default_language=Язык пользователя по умолчанию
|
||||
auths.sspi_default_language_helper=Язык по умолчанию для пользователей, автоматически создаваемый методом аутентификации SSPI. Оставьте пустым, если вы предпочитаете, чтобы язык определялся автоматически.
|
||||
auths.tips=Советы
|
||||
auths.tips.oauth2.general=OAuth2 аутентификация
|
||||
auths.tips.oauth2.general.tip=При добавлении нового OAuth2 провайдера, URL адрес переадресации по завершении аутентификации должен выглядеть так: <host>/user/oauth2/<Authentication Name>/callback
|
||||
|
@ -1604,6 +1773,7 @@ auths.tip.google_plus=Получите учетные данные клиент
|
|||
auths.tip.openid_connect=Используйте OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) для автоматической настройки входа OAuth
|
||||
auths.tip.twitter=Перейдите на https://dev.twitter.com/apps, создайте приложение и убедитесь, что включена опция «Разрешить это приложение для входа в систему с помощью Twitter»
|
||||
auths.tip.discord=Добавьте новое приложение на https://discordapp.com/developers/applications/me
|
||||
auths.tip.gitea=Зарегистрировать новое приложение OAuth2. Руководство можно найти на https://docs.gitea.io/ru-us/oauth2-provider/
|
||||
auths.edit=Обновить параметры аутентификации
|
||||
auths.activated=Эта аутентификация активирована
|
||||
auths.new_success=Метод аутентификации '%s' был добавлен.
|
||||
|
@ -1615,12 +1785,14 @@ auths.delete_auth_desc=Этот источник аутентификации б
|
|||
auths.still_in_used=Эта проверка подлинности до сих пор используется некоторыми пользователями, удалите или преобразуйте этих пользователей в другой тип входа в систему.
|
||||
auths.deletion_success=Канал аутентификации успешно удален!
|
||||
auths.login_source_exist=Источник входа '%s' уже существует.
|
||||
auths.login_source_of_type_exist=Источник аутентификации этого типа уже существует.
|
||||
|
||||
config.server_config=Конфигурация сервера
|
||||
config.app_name=Название сайта
|
||||
config.app_ver=Версия Gitea
|
||||
config.app_url=Базовый URL-адрес Gitea
|
||||
config.custom_conf=Путь к файлу конфигурации
|
||||
config.custom_file_root_path=Пользовательский путь до папки с файлами
|
||||
config.domain=Домен SSH сервера
|
||||
config.offline_mode=Локальный режим
|
||||
config.disable_router_log=Отключение журнала маршрутизатора
|
||||
|
@ -1646,6 +1818,10 @@ config.ssh_keygen_path=Путь к генератору ключей ('ssh-keyge
|
|||
config.ssh_minimum_key_size_check=Минимальный размер ключа проверки
|
||||
config.ssh_minimum_key_sizes=Минимальные размеры ключа
|
||||
|
||||
config.lfs_config=Конфигурация LFS
|
||||
config.lfs_enabled=Включено
|
||||
config.lfs_content_path=Путь к контенту LFS
|
||||
config.lfs_http_auth_expiry=Устаревание HTTP-аутентификации LFS
|
||||
|
||||
config.db_config=Конфигурация базы данных
|
||||
config.db_type=Тип
|
||||
|
@ -1667,6 +1843,7 @@ config.mail_notify=Почтовые уведомления
|
|||
config.disable_key_size_check=Отключить проверку на минимальный размер ключа
|
||||
config.enable_captcha=Включить CAPTCHA
|
||||
config.active_code_lives=Время жизни кода для активации
|
||||
config.reset_password_code_lives=Время действия кода восстановления аккаунта
|
||||
config.default_keep_email_private=Скрывать адреса электронной почты по умолчанию
|
||||
config.default_allow_create_organization=Разрешить создание организаций по умолчанию
|
||||
config.enable_timetracking=Включить отслеживание времени
|
||||
|
@ -1701,6 +1878,7 @@ config.cache_config=Настройки кеша
|
|||
config.cache_adapter=Адаптер кэша
|
||||
config.cache_interval=Интервал кэширования
|
||||
config.cache_conn=Подключение кэша
|
||||
config.cache_item_ttl=Время жизни данных в кеше
|
||||
|
||||
config.session_config=Конфигурация сессии
|
||||
config.session_provider=Провайдер сессии
|
||||
|
@ -1731,6 +1909,16 @@ config.git_gc_timeout=Лимит времени сборки мусора
|
|||
|
||||
config.log_config=Конфигурация журнала
|
||||
config.log_mode=Режим журналирования
|
||||
config.macaron_log_mode=Режим журнала Macaron
|
||||
config.own_named_logger=Именованный журнал
|
||||
config.routes_to_default_logger=Маршруты в стандартный журнал
|
||||
config.go_log=Использует Go-журнал (перенаправлено в стандартный)
|
||||
config.router_log_mode=Режим журнала маршрутизатора
|
||||
config.disabled_logger=Отключен
|
||||
config.access_log_mode=Режим доступа к журналу
|
||||
config.access_log_template=Шаблон
|
||||
config.xorm_log_mode=Режим журнала XORM
|
||||
config.xorm_log_sql=Лог SQL
|
||||
|
||||
monitor.cron=Задачи cron
|
||||
monitor.name=Название
|
||||
|
@ -1742,6 +1930,9 @@ monitor.process=Запущенные процессы
|
|||
monitor.desc=Описание
|
||||
monitor.start=Время начала
|
||||
monitor.execute_time=Время выполнения
|
||||
monitor.process.cancel=Отменить процесс
|
||||
monitor.process.cancel_desc=Отмена процесса может привести к потере данных
|
||||
monitor.process.cancel_notices=Отменить: <strong>%s</strong>?
|
||||
|
||||
notices.system_notice_list=Уведомления системы
|
||||
notices.view_detail_header=Подробности уведомления
|
||||
|
@ -1774,6 +1965,7 @@ push_tag=создал(а) тэг <a href="%s/src/tag/%s">%[2]s</a> в <a href="%
|
|||
delete_tag=удалил(а) тэг %[2]s из <a href="%[1]s">%[3]s</a>
|
||||
delete_branch=удалил(а) ветку %[2]s из <a href="%[1]s">%[3]s</a>
|
||||
compare_commits=Сравнить %d коммитов
|
||||
compare_commits_general=Сравнить коммиты
|
||||
mirror_sync_push=синхронизированные коммиты с <a href="%[1]s/src/%[2]s">%[3]s</a> на <a href="%[1]s">%[4]s</a> из зеркала
|
||||
mirror_sync_create=синхронизированные новые ссылки <a href="%s/src/%s">%[2]s</a> к <a href="%[1]s">%[3]s</a> из зеркала
|
||||
mirror_sync_delete=синхронизированные и удаленные ссылки <code>%[2]s</code> на <a href="%[1]s">%[3]s</a> из зеркала
|
||||
|
@ -1818,6 +2010,7 @@ mark_as_unread=Пометить как непрочитанное
|
|||
mark_all_as_read=Пометить все как прочитанные
|
||||
|
||||
[gpg]
|
||||
default_key=Подписано ключом по умолчанию
|
||||
error.extract_sign=Не удалось извлечь подпись
|
||||
error.generate_hash=Не удается создать хэш коммита
|
||||
error.no_committer_account=Аккаунт пользователя с таким Email не найден
|
||||
|
|
|
@ -25,6 +25,7 @@ password=Parola
|
|||
re_type=Parolayı yeniden yazın
|
||||
captcha=CAPTCHA
|
||||
twofa=İki Aşamalı Doğrulama
|
||||
twofa_scratch=İki aşamalı kazınmış kod
|
||||
passcode=Şifre
|
||||
|
||||
u2f_insert_key=Güvenlik anahtarınızı girin
|
||||
|
@ -65,6 +66,7 @@ forks=Çatallar
|
|||
activities=Aktiviteler
|
||||
pull_requests=Değişiklik İstekleri
|
||||
issues=Konular
|
||||
milestones=Kilometre Taşları
|
||||
|
||||
cancel=İptal
|
||||
add=Ekle
|
||||
|
@ -85,6 +87,7 @@ platform_desc=Gitea <a target="_blank" rel="noopener noreferrer" href="http://go
|
|||
lightweight=Hafif
|
||||
lightweight_desc=Gitea'nın minimal gereksinimleri çok düşüktür ve ucuz bir Raspberry Pi üzerinde çalışabilmektedir. Makine enerjinizden tasarruf edin!
|
||||
license=Açık Kaynak
|
||||
license_desc=Gidin ve <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>'yı edinin! Bu projeyi daha da iyi yapmak için <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">katkıda bulunarak</a> bize katılın. Katkıda bulunmaktan çekinmeyin!
|
||||
|
||||
[install]
|
||||
install=Kurulum
|
||||
|
@ -122,9 +125,11 @@ run_user_helper=Gitea'nin çalışacağı, işletim sistemi kullanıcı adını
|
|||
domain=SSH Sunucusu Alan Adı
|
||||
domain_helper=SSH kopyalama URL'leri için alan adı veya sunucu adresi.
|
||||
ssh_port=SSH Sunucu Portu
|
||||
ssh_port_helper=SSH sunucusunun dinleyeceği port numarası. Etkisizleştimek için boş bırakın.
|
||||
http_port=Gitea HTTP Dinleme Portu
|
||||
http_port_helper=Gitea'nın web sunucusunun dinleyeceği port numarası.
|
||||
app_url=Gitea Kök URL
|
||||
app_url_helper=HTTP(S) kopyalama URL'leri ve e-posta bildirimleri için temel adres.
|
||||
log_root_path=Günlük Dosyaları Yolu
|
||||
log_root_path_helper=Günlük dosyaları bu dizine kaydedilecektir.
|
||||
|
||||
|
@ -139,7 +144,10 @@ register_confirm=Kayıt için E-posta Doğrulaması Gereksin
|
|||
mail_notify=E-Posta Bildirimlerini Etkinleştir
|
||||
server_service_title=Sunucu ve Diğer Servis Ayarları
|
||||
offline_mode=Yerel Kipi Etkinleştir
|
||||
offline_mode_popup=Üçüncü parti içerik teslim ağlarını etkisizleştirin ve bütün kaynakları yerelden sunun.
|
||||
disable_gravatar=Gravatar'ı Devre Dışı Bırak
|
||||
disable_gravatar_popup=Gravatar ve üçüncü parti avatar kaynaklarını iptal edin. Kullanıcı bir avatar yüklemediği zaman varsayılan bir avatar kullanılacaktır.
|
||||
federated_avatar_lookup=Birleştirilmiş Avatarları Etkinleştir
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Kendi Kendine Kaydolmayı Devre Dışı Bırak
|
||||
disable_registration_popup=Kullanıcının kendi kendine kaydolmasını devre dışı bırak. Yalnızca yöneticiler yeni hesaplar oluşturabilecek.
|
||||
|
@ -237,6 +245,7 @@ verify=Doğrula
|
|||
scratch_code=Çizgi kodu
|
||||
use_scratch_code=Bir çizgi kodu kullanınız
|
||||
twofa_scratch_used=Çizgi kodunuzu kullandınız. İki aşamalı ayarlar sayfasına yönlendirildiniz, burada cihaz kaydınızı kaldırabilir veya yeni bir çizgi kodu oluşturabilirsiniz.
|
||||
twofa_passcode_incorrect=Şifreniz yanlış. Aygıtınızı yanlış yerleştirdiyseniz, oturum açmak için çizgi kodunuzu kullanın.
|
||||
twofa_scratch_token_incorrect=Çizgi kodunuz doğru değildir.
|
||||
login_userpass=Oturum Aç
|
||||
login_openid=Açık Kimlik
|
||||
|
@ -251,6 +260,7 @@ openid_connect_title=Mevcut olan bir hesaba bağlan
|
|||
openid_connect_desc=Seçilen OpenID URI'si bilinmiyor. Burada yeni bir hesapla ilişkilendir.
|
||||
openid_register_title=Yeni hesap oluştur
|
||||
openid_register_desc=Seçilen OpenID URI'si bilinmiyor. Burada yeni bir hesapla ilişkilendir.
|
||||
openid_signin_desc=OpenID URI'nızı girin. Örneğin: https://anne.me, bob.openid.org.cn veya gnusocial.net/carry.
|
||||
disable_forgot_password_mail=Hesap kurtarma devre dışı. Lütfen site yöneticinizle iletişime geçin.
|
||||
email_domain_blacklisted=Bu e-posta adresinizle kayıt olamazsınız.
|
||||
authorize_application=Uygulamayı Yetkilendir
|
||||
|
@ -300,6 +310,7 @@ SSPIDefaultLanguage=Varsayılan Dil
|
|||
require_error=` boş olamaz.`
|
||||
alpha_dash_error=` yalnızca alfasayısal, çizgi ('-') ve alt çizgi ('_') karakterlerini içermelidir. `
|
||||
alpha_dash_dot_error=` yalnızca alfasayısal, çizgi ('-'), alt çizgi ('_') ve nokta ('.') karakterlerini içermelidir. `
|
||||
git_ref_name_error=` git referans ismi iyi oluşturulmuş olmalıdır.`
|
||||
size_error=` uzunluk en fazla %s olmalıdır.`
|
||||
min_size_error=` en az %s karakter içermelidir.`
|
||||
max_size_error=` en fazla %s karakter içermelidir.`
|
||||
|
@ -314,6 +325,8 @@ lang_select_error=Listeden bir dil seçin.
|
|||
|
||||
username_been_taken=Bu kullanıcı adı daha önce alınmış.
|
||||
repo_name_been_taken=Depo adı zaten kullanılıyor.
|
||||
visit_rate_limit=Uzaktan ziyarette oran sınırlaması ele alındı.
|
||||
2fa_auth_required=Uzaktan ziyaret için iki faktörlü kimlik doğrulaması gerekli.
|
||||
org_name_been_taken=Organizasyon adı zaten kullanılıyor.
|
||||
team_name_been_taken=Takım adı zaten alınmış.
|
||||
team_no_units_error=En az bir depo bölümüne erişimine izin ver.
|
||||
|
@ -394,6 +407,7 @@ cancel=İptal
|
|||
language=Dil
|
||||
ui=Tema
|
||||
|
||||
lookup_avatar_by_mail=Avatarı E-posta Adresine Göre Ara
|
||||
federated_avatar_lookup=Birleşmiş Avatar Araması
|
||||
enable_custom_avatar=Özel Avatarı Etkinleştir
|
||||
choose_new_avatar=Yeni Avatar Seç
|
||||
|
@ -512,23 +526,45 @@ oauth2_type_native=Yerel (ör. Mobil, Masaüstü, Tarayıcı)
|
|||
oauth2_redirect_uri=Yönlendirme URI'si
|
||||
save_application=Kaydet
|
||||
oauth2_client_id=İstemci Kimliği
|
||||
oauth2_client_secret=İstemci Gizliliği
|
||||
oauth2_regenerate_secret=Gizliliği Yeniden Oluştur
|
||||
oauth2_regenerate_secret_hint=Gizliliğini mi kaybettin?
|
||||
oauth2_client_secret_hint=Bu sayfayı tekrar ziyaret ederseniz gizlilik görünmez. Lütfen gizliliğinizi kaydedin.
|
||||
oauth2_application_edit=Düzenle
|
||||
oauth2_application_create_description=OAuth2 uygulamaları, üçüncü taraf uygulamanıza bu durumda kullanıcı hesaplarına erişim sağlar.
|
||||
oauth2_application_remove_description=Bir OAuth2 uygulamasının kaldırılması, bu durumda yetkili kullanıcı hesaplarına erişmesini önler. Devam edilsin mi?
|
||||
|
||||
authorized_oauth2_applications=Yetkili OAuth2 Uygulamaları
|
||||
authorized_oauth2_applications_description=Kişisel Gitea hesabınıza bu üçüncü parti uygulamalara erişim izni verdiniz. Lütfen artık ihtiyaç duyulmayan uygulamalara erişimi iptal edin.
|
||||
revoke_key=İptal Et
|
||||
revoke_oauth2_grant=Erişimi İptal Et
|
||||
revoke_oauth2_grant_description=Bu üçüncü taraf uygulamasına erişimin iptal edilmesi bu uygulamanın verilerinize erişmesini önleyecektir. Emin misiniz?
|
||||
revoke_oauth2_grant_success=Erişimi başarıyla iptal ettiniz.
|
||||
|
||||
twofa_desc=İki faktörlü kimlik doğrulama, hesabınızın güvenliğini artırır.
|
||||
twofa_is_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde <strong>kaydedilmiş</strong>.
|
||||
twofa_not_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde kaydedilmemiş.
|
||||
twofa_disable=İki Aşamalı Doğrulamayı Devre Dışı Bırak
|
||||
twofa_scratch_token_regenerate=Kazıma Belirtecini Yenile
|
||||
twofa_scratch_token_regenerated=Kazıma belirteciniz şimdi %s. Güvenli bir yerde saklayın.
|
||||
twofa_enroll=İki Faktörlü Kimlik Doğrulamaya Kaydolun
|
||||
twofa_disable_note=Gerekirse iki faktörlü kimlik doğrulamayı devre dışı bırakabilirsiniz.
|
||||
twofa_disable_desc=İki faktörlü kimlik doğrulamayı devre dışı bırakmak hesabınızı daha az güvenli hale getirir. Devam edilsin mi?
|
||||
regenerate_scratch_token_desc=Karalama belirtecinizi yanlış yerleştirdiyseniz veya oturum açmak için kullandıysanız, buradan sıfırlayabilirsiniz.
|
||||
twofa_disabled=İki faktörlü kimlik doğrulama devre dışı bırakıldı.
|
||||
scan_this_image=Kim doğrulama uygulamanızla bu görüntüyü tarayın:
|
||||
or_enter_secret=Veya gizli şeyi girin: %s
|
||||
then_enter_passcode=Ve uygulamada gösterilen şifreyi girin:
|
||||
passcode_invalid=Şifre geçersiz. Tekrar deneyin.
|
||||
twofa_enrolled=Hesabınız iki faktörlü kimlik doğrulamasına kaydedildi. Kazıma belirtecini (%s) yalnızca bir kez gösterdiği gibi güvenli bir yerde saklayın!
|
||||
|
||||
u2f_desc=Güvenlik anahtarları, şifreleme anahtarlarını içeren donanım aygıtlarıdır. İki faktörlü kimlik doğrulama için kullanılabilirler. Güvenlik anahtarları <a rel="noreferrer" href="https://fidoalliance.org/">FIDO U2F</a> standardını desteklemelidir.
|
||||
u2f_require_twofa=Güvenlik anahtarlarını kullanmak için hesabınızın iki faktörlü kimlik doğrulamasına kaydedilmiş olması gerekir.
|
||||
u2f_register_key=Güvenlik Anahtarı Ekle
|
||||
u2f_nickname=Takma Ad
|
||||
u2f_press_button=Kaydetmek için güvenlik anahtarınızdaki düğmeye basın.
|
||||
u2f_delete_key=Güvenlik Anahtarını Sil
|
||||
u2f_delete_key_desc=Bir güvenlik anahtarını kaldırırsanız, artık onunla giriş yapamazsınız. Devam edilsin mi?
|
||||
|
||||
manage_account_links=Bağlı Hesapları Yönet
|
||||
manage_account_links_desc=Bu harici hesaplar Gitea hesabınızla bağlantılı.
|
||||
|
@ -559,6 +595,7 @@ repo_size=Depo Boyutu
|
|||
template=Şablon
|
||||
template_select=Bir şablon seçin.
|
||||
template_helper=Depoyu şablon yap
|
||||
template_description=Şablon depoları, kullanıcıların aynı dizin yapısı, dosyaları ve isteğe bağlı ayarlarla yeni depoları oluşturmasına izin verir.
|
||||
visibility=Görünürlük
|
||||
visibility_description=Yalnızca sahibi veya haklara sahip organizasyon üyeleri onu görebilecek.
|
||||
visibility_helper=Depoyu Gizli Yap
|
||||
|
@ -584,8 +621,12 @@ auto_init=Depoyu başlat (.gitignore, Lisans ve README dosyalarını ekler)
|
|||
create_repo=Depo Oluştur
|
||||
default_branch=Varsayılan Dal
|
||||
mirror_prune=Buda
|
||||
mirror_prune_desc=Kullanılmayan uzak depoları izleyen referansları kaldır
|
||||
mirror_interval=Yansı Aralığı (geçerli zaman birimleri 'h', 'm', 's'). 0 otomatik senkronizasyonu devre dışı bırakmak için.
|
||||
mirror_interval_invalid=Yansı süre aralığı geçerli değil.
|
||||
mirror_address=URL'den Klonla
|
||||
mirror_address_desc=Gerekli kimlikleri Yetkilendirmeyi Klonla bölümüne girin.
|
||||
mirror_address_url_invalid=Sağlanan Url geçersiz. Url'nin tüm bileşenlerinden doğru olarak kaçmalısınız.
|
||||
mirror_address_protocol_invalid=Sağlanan url geçersiz. Yalnızca http(s):// veya git:// konumları yansıtılabilir.
|
||||
mirror_last_synced=Son Senkronize Edilen
|
||||
watchers=İzleyenler
|
||||
|
@ -597,7 +638,9 @@ reactions_more=ve %d daha fazla
|
|||
template.items=Şablon Öğeleri
|
||||
template.git_content=Git İçeriği (Varsayılan Dal)
|
||||
template.git_hooks=Git İstekleri
|
||||
template.git_hooks_tooltip=Şu anda bir kez eklenen git isteklerini değiştirmek veya kaldırmak mümkün değildir. Bunu yalnızca şablon deposuna güveniyorsanız seçin.
|
||||
template.webhooks=Web İstekleri
|
||||
template.topics=Konular
|
||||
template.avatar=Profil Resmi
|
||||
template.issue_labels=Konu Etiketleri
|
||||
template.one_item=En az bir şablon öğesi seçmelisiniz
|
||||
|
@ -700,6 +743,7 @@ editor.delete_this_file=Dosyayı Sil
|
|||
editor.must_have_write_access=Bu dosyada değişiklikler yapmak veya önermek için yazma erişiminizin olması gerekir.
|
||||
editor.file_delete_success='%s' dosyası silindi.
|
||||
editor.name_your_file=Dosyanızı isimlendirin…
|
||||
editor.filename_help=Bölü ('/') işaretiyle ismini yazarak bir dizin ekleyebilirsiniz. Dizini silmek için girdi sahasının başına backspace yazmalısınız.
|
||||
editor.or=veya
|
||||
editor.cancel_lower=İptal
|
||||
editor.commit_changes=Değişiklikleri Uygula
|
||||
|
@ -739,6 +783,7 @@ commits.desc=Kaynak kodu değişiklik geçmişine göz atın.
|
|||
commits.commits=İşlemeler
|
||||
commits.no_commits=Ortak bir işleme yok. '%s' ve '%s' tamamen farklı geçmişlere sahip.
|
||||
commits.search=İşlemeleri ara…
|
||||
commits.search.tooltip=Anahtar kelimeleri "yazar:", "yorumcu:", "sonra:" veya "önce:", örneğin; "eski haline yazan: Alice önce: 2019-04-01" önekleyebilirsiniz.
|
||||
commits.find=Ara
|
||||
commits.search_all=Tüm Dallar
|
||||
commits.author=Yazar
|
||||
|
@ -837,6 +882,7 @@ issues.num_comments=%d yorum
|
|||
issues.commented_at=`<a href="#%s">%s</a> yorum yaptı`
|
||||
issues.delete_comment_confirm=Bu yorumu silmek istediğinizden emin misiniz?
|
||||
issues.context.copy_link=Bağlantıyı Kopyala
|
||||
issues.context.quote_reply=Alıntı Cevapla
|
||||
issues.context.edit=Düzenle
|
||||
issues.context.delete=Sil
|
||||
issues.no_content=Henüz bir içerik yok.
|
||||
|
@ -848,6 +894,7 @@ issues.create_comment=Yorum yap
|
|||
issues.closed_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> kapattı`
|
||||
issues.reopened_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> yeniden açtı`
|
||||
issues.commit_ref_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> işlemesinde bu konuyu işaret etti`
|
||||
issues.ref_from=`%[1]s'den`
|
||||
issues.poster=Poster
|
||||
issues.collaborator=Katkıcı
|
||||
issues.owner=Sahibi
|
||||
|
@ -906,6 +953,7 @@ issues.add_time=El ile Zaman Ekle
|
|||
issues.add_time_short=Zaman Ekle
|
||||
issues.add_time_cancel=İptal
|
||||
issues.add_time_history=`%s harcanan zaman eklendi`
|
||||
issues.del_time_history=`%s harcanan zaman silindi`
|
||||
issues.add_time_hours=Saat
|
||||
issues.add_time_minutes=Dakika
|
||||
issues.add_time_sum_to_small=Zaman girilmedi.
|
||||
|
@ -972,13 +1020,16 @@ pulls.desc=Değişiklik isteklerini ve kod incelemelerini etkinleştir.
|
|||
pulls.new=Yeni Değişiklik İsteği
|
||||
pulls.compare_changes=Yeni Değişiklik İsteği
|
||||
pulls.compare_changes_desc=Birleştirmek için hedef ve kaynak dalı seçin.
|
||||
pulls.compare_base=birleştir
|
||||
pulls.compare_compare=şuradan çek
|
||||
pulls.filter_branch=Dal filtrele
|
||||
pulls.no_results=Sonuç bulunamadı.
|
||||
pulls.nothing_to_compare=Bu dallar eşit. Değişiklik isteği oluşturmaya gerek yok.
|
||||
pulls.has_pull_request=`Bu dallar arasında bir değişiklik isteği zaten var: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
|
||||
pulls.create=Değişiklik İsteği Oluştur
|
||||
pulls.title_desc=<code id="branch_target">%[3]s</code> içindeki <code>%[2]s</code> işlemelerini %[1]d ile birleştirmek istiyor
|
||||
pulls.merged_title_desc=%[4]s <code>%[2]s</code> içindeki %[1]d işleme <code>%[3]s</code> ile birleştirdi
|
||||
pulls.change_target_branch_at='hedef dal <b>%s</b> adresinden <b>%s</b>%s adresine değiştirildi'
|
||||
pulls.tab_conversation=Sohbet
|
||||
pulls.tab_commits=İşlemeler
|
||||
pulls.tab_files=Değiştirilen Dosyalar
|
||||
|
@ -986,6 +1037,7 @@ pulls.reopen_to_merge=Bir birleşim uygulamak için lütfen bu çekme isteğini
|
|||
pulls.cant_reopen_deleted_branch=Dal silindiğinden bu değişiklik isteği yeniden açılamaz.
|
||||
pulls.merged=Birleştirildi
|
||||
pulls.merged_as=Değişiklik isteği <a rel="nofollow" class="ui sha" href="%[1]s"><code>%[2]s</code></a> olarak birleştirildi.
|
||||
pulls.is_closed=Değişiklik isteği kapatıldı.
|
||||
pulls.has_merged=Değişiklik isteği birleştirildi.
|
||||
pulls.title_wip_desc=`Değişiklik isteğinin yanlışlıkla birleştirilmesini önlemek için, <a href="#">başlığı <strong>%s</strong> ile başlatın</a>`
|
||||
pulls.cannot_merge_work_in_progress=Bu değişiklik isteği devam eden bir çalışma olarak işaretlendi. Hazır olduğunda <strong>%s</strong> ön ekini başlıktan kaldırın
|
||||
|
@ -995,6 +1047,7 @@ pulls.is_checking=Birleştirme çakışması denetimi devam ediyor. Birkaç daki
|
|||
pulls.required_status_check_failed=Bazı gerekli denetimler başarılı olmadı.
|
||||
pulls.required_status_check_administrator=Yönetici olarak, bu değişiklik isteğini yine de birleştirebilirsiniz.
|
||||
pulls.blocked_by_approvals=Bu değişiklik isteği henüz onaylanmadı. %[2]d isteğin %[1]d onayı verildi.
|
||||
pulls.blocked_by_rejection=Bu Değişiklik İsteğinde, resmi bir inceleyeci tarafından istenen değişiklikler var.
|
||||
pulls.can_auto_merge_desc=Bu değişiklik isteği otomatik olarak birleştirilebilir.
|
||||
pulls.cannot_auto_merge_desc=Bu değişiklik isteği, çakışmalar nedeniyle otomatik olarak birleştirilemiyor.
|
||||
pulls.cannot_auto_merge_helper=Çakışmaları çözmek için el ile birleştirin.
|
||||
|
@ -1007,6 +1060,10 @@ pulls.rebase_merge_pull_request=Rebase ve Merge
|
|||
pulls.rebase_merge_commit_pull_request=Rebase ve Merge (--no-ff)
|
||||
pulls.squash_merge_pull_request=Squash ve Merge
|
||||
pulls.invalid_merge_option=Bu değişiklik isteği için bu birleştirme seçeneğini kullanamazsınız.
|
||||
pulls.merge_conflict=Birleştirme Başarısız: Birleşirken bir çakışma oluştu: %[1]s<br>%[2]s<br>İpucu: Farklı bir strateji deneyin
|
||||
pulls.rebase_conflict=Birleştirme Başarısız: Yeniden işleme yaparken bir çakışma vardı: %[1]s<br>%[2]s<br>%[3]s<br>İpucu: Farklı bir strateji deneyin
|
||||
pulls.unrelated_histories=Birleştirme Başarısız: Birleştirme başlığı ve tabanı ortak bir geçmişi paylaşmıyor. İpucu: Farklı bir strateji deneyin
|
||||
pulls.merge_out_of_date=Birleştirme Başarısız: Birleştirme oluşturulurken, taban güncellendi. İpucu: Tekrar deneyin.
|
||||
pulls.open_unmerged_pull_exists=`Aynı özelliklere sahip bekleyen bir çekme isteği (#%d) olduğundan yeniden açma işlemini gerçekleştiremezsiniz.`
|
||||
pulls.status_checking=Bazı denetlemeler beklemede
|
||||
pulls.status_checks_success=Tüm denetlemeler başarılı oldu
|
||||
|
@ -1167,18 +1224,26 @@ settings.use_external_wiki=Harici Wiki Kullan
|
|||
settings.external_wiki_url=Harici Wiki bağlantısı
|
||||
settings.external_wiki_url_error=Harici wiki URL'si geçerli bir URL değil.
|
||||
settings.external_wiki_url_desc=Ziyaretçiler, wiki sekmesine tıklandığında harici wiki URL'sine yönlendirilir.
|
||||
settings.issues_desc=Depo Sorun İzleyicisini Etkinleştir
|
||||
settings.use_internal_issue_tracker=Yerleşik Sorun İzleyiciyi Kullan
|
||||
settings.use_external_issue_tracker=Harici Sorun İzleyici Kullan
|
||||
settings.external_tracker_url=Harici Konu İzleyici URLsi
|
||||
settings.external_tracker_url_error=Harici sorun izleyici URL'si geçerli bir URL değil.
|
||||
settings.external_tracker_url_desc=Ziyaretçiler, konular sekmesine tıklandığında harici konu izleyici URL'sine yönlendirilir.
|
||||
settings.tracker_url_format=Harici Sorun Takipçisi Bağlantı Formatı
|
||||
settings.tracker_url_format_error=Harici konu izleyici URL biçimi geçerli bir URL değil.
|
||||
settings.tracker_issue_style=Harici Konu İzleyici Numara Biçimi
|
||||
settings.tracker_issue_style.numeric=Sayısal
|
||||
settings.tracker_issue_style.alphanumeric=Alfanumerik
|
||||
settings.tracker_url_format_desc=Kullanıcı adı, depo adı ve yayın dizini için <code>{user}</code>, <code>{repo}</code> ve <code>{index}</code> yer tutucularını kullanın.
|
||||
settings.enable_timetracker=Zaman Takibini Etkinleştir
|
||||
settings.allow_only_contributors_to_track_time=Sadece Katkıcılar İçin Zaman Takibine İzin Ver
|
||||
settings.pulls_desc=Değişiklik İsteklerini Etkinleştir
|
||||
settings.pulls.ignore_whitespace=Çakışmalar için Boşlukları Gözardı Et
|
||||
settings.pulls.allow_merge_commits=İşleme Birleştirmeyi Etkinleştir
|
||||
settings.pulls.allow_rebase_merge=İşlemeleri Birleştirmek için Yeniden Yapılandırmayı Etkinleştir
|
||||
settings.pulls.allow_rebase_merge_commit=Açık birleştirme işlemeleri ile Yeniden Yapılandırmayı Etkinleştir (--no-ff)
|
||||
settings.pulls.allow_squash_commits=İşlemeleri Birleştirmek için Sıkmayı Etkinleştir
|
||||
settings.admin_settings=Yönetici Ayarları
|
||||
settings.admin_enable_health_check=Depo Sağlık Kontrollerini Etkinleştir (git fsck)
|
||||
settings.admin_enable_close_issues_via_commit_in_any_branch=Varsayılan olmayan bir dalda yapılan bir işlemeyle konuyu kapat
|
||||
|
@ -1192,6 +1257,7 @@ settings.convert_succeed=Yansı normal bir depoya dönüştürüldü.
|
|||
settings.transfer=Sahipliği Aktar
|
||||
settings.transfer_desc=Bu depoyu bir kullanıcıya veya yönetici haklarına sahip olduğunuz bir organizasyona aktarın.
|
||||
settings.transfer_notices_1=- Bireysel bir kullanıcıya aktarırsanız depoya erişiminizi kaybedersiniz.
|
||||
settings.transfer_notices_2=- Sahip (-yardımcı) olduğunuz bir kuruluşa devrederseniz, depoya erişmeye devam edersiniz.
|
||||
settings.transfer_form_title=Onaylamak için depo adını girin:
|
||||
settings.wiki_delete=Wiki Verisini Sil
|
||||
settings.wiki_delete_desc=Depo wiki verilerini silmek kalıcıdır ve geri alınamaz.
|
||||
|
@ -1201,6 +1267,7 @@ settings.wiki_deletion_success=Depo wiki verisi silindi.
|
|||
settings.delete=Bu Depoyu Sil
|
||||
settings.delete_desc=Bir depoyu silmek kalıcıdır ve geri alınamaz.
|
||||
settings.delete_notices_1=- Bu işlem geri <strong>ALINAMAZ</strong>.
|
||||
settings.delete_notices_2=- Bu işlem, kod, sorunlar, yorumlar, wiki verileri ve katkıcı ayarları dahil olmak üzere <strong>%s</strong> deposunu kalıcı olarak siler.
|
||||
settings.delete_notices_fork_1=- Silme işleminden sonra bu deponun çatalları bağımsız hale gelecektir.
|
||||
settings.deletion_success=Depo silindi.
|
||||
settings.update_settings_success=Depo ayarları güncellendi.
|
||||
|
@ -1273,6 +1340,7 @@ settings.event_pull_request_desc=Değişiklik isteği açıldığında, kapatıl
|
|||
settings.event_push=Çek
|
||||
settings.event_push_desc=Depo ittirildiğinde.
|
||||
settings.branch_filter=Dal filtresi
|
||||
settings.branch_filter_desc=Glob deseni olarak belirtilen itme, dal oluşturma ve dal silme olayları için dal beyaz listesi. Boş veya <code>*</code> ise, tüm dallar için olaylar bildirilir. Sözdizimi için <a href="https://godoc.org/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> belgelerine bakın. Örnekler: <code>master</code>, <code>{master,release*}</code>.
|
||||
settings.event_repository=Depo
|
||||
settings.event_repository_desc=Depo oluşturuldu veya silindi.
|
||||
settings.active=Etkin
|
||||
|
@ -1312,6 +1380,14 @@ settings.protected_branch_can_push_yes=İtebilirsiniz
|
|||
settings.protected_branch_can_push_no=İtemezsiniz
|
||||
settings.branch_protection=<b>%s</b> dalı için Dal Koruması
|
||||
settings.protect_this_branch=Dal Korumayı Etkinleştir
|
||||
settings.protect_this_branch_desc=Silmeyi önler ve dala Git itmesini ve birleştirmesini kısıtlar.
|
||||
settings.protect_disable_push=İtmeyi Devre Dışı Bırak
|
||||
settings.protect_disable_push_desc=Bu dala itme yapılmasına izin verilmeyecek.
|
||||
settings.protect_enable_push=İtmeyi Etkinleştir
|
||||
settings.protect_enable_push_desc=Yazma erişimi olan herkesin bu dala itmesine izin verilir (ancak zorla itmeyin).
|
||||
settings.protect_whitelist_committers=Beyaz Liste Sınırlı İtme
|
||||
settings.protect_whitelist_committers_desc=Sadece beyaz listeye alınmış kullanıcıların veya takımların bu dala itmesine izin verilir (ancak zorla itmeyin).
|
||||
settings.protect_whitelist_deploy_keys=Beyaz liste itmek için yazma erişimli anahtarları dağıtır
|
||||
settings.protect_whitelist_users=İtme için beyaz listedeki kullanıcılar:
|
||||
settings.protect_whitelist_search_users=Kullanıcı ara…
|
||||
settings.protect_whitelist_teams=İtme için beyaz listedeki takımlar:
|
||||
|
@ -1321,8 +1397,12 @@ settings.protect_merge_whitelist_committers_desc=Yalnızca beyaz listedeki kulla
|
|||
settings.protect_merge_whitelist_users=Birleştirme için beyaz listedeki kullanıcılar:
|
||||
settings.protect_merge_whitelist_teams=Birleştirme için beyaz listedeki takımlar:
|
||||
settings.protect_check_status_contexts=Durum Denetimini Etkinleştir
|
||||
settings.protect_check_status_contexts_desc=Birleşmeden önce durum denetimlerinin geçmesini isteyin Dallar bu kuralla eşleşen bir dalda birleştirilmeden önce hangi durum denetimlerinin geçmesi gerektiğini seçin. Etkinleştirildiğinde, işlemelerin önce başka bir dala itilmesi, ardından durum kontrolleri geçtikten sonra doğrudan bu kuralla eşleşen bir dala birleştirilmesi veya itilmesi gerekir. Hiçbir bağlam seçilmezse, bağlam ne olursa olsun son işlem başarılı olmalıdır.
|
||||
settings.protect_check_status_contexts_list=Bu depo için geçen haftadaki durum denetimleri
|
||||
settings.protect_required_approvals=Gerekli onaylar:
|
||||
settings.protect_required_approvals_desc=Değişiklik isteğini yalnızca yeterince olumlu yorumla birleştirmeye izin ver.
|
||||
settings.protect_approvals_whitelist_enabled=Onayları beyaz listeye giren kullanıcılar veya takımlar için kısıtla
|
||||
settings.protect_approvals_whitelist_enabled_desc=Yalnızca beyaz listedeki kullanıcıların veya ekiplerin incelemeleri gerekli onaylar için dikkate alınır. Onaylı beyaz liste olmadan, yazma erişimi olan herkesin incelemeleri gerekli onaylar için dikkate alınır.
|
||||
settings.protect_approvals_whitelist_users=Beyaz listedeki incelemeciler:
|
||||
settings.protect_approvals_whitelist_teams=Gözden geçirme için beyaz listedeki takımlar:
|
||||
settings.add_protected_branch=Korumayı etkinleştir
|
||||
|
@ -1331,6 +1411,8 @@ settings.update_protect_branch_success='%s' dalı için dal koruması güncellen
|
|||
settings.remove_protected_branch_success='%s' dalı için dal koruması devre dışı bırakıldı.
|
||||
settings.protected_branch_deletion=Dal Korumasını Devre Dışı Bırak
|
||||
settings.protected_branch_deletion_desc=Dal korumasını devre dışı bırakmak, kullanıcıların dalı itmek için yazma izni olmasını sağlar. Devam edilsin mi?
|
||||
settings.block_rejected_reviews=Reddedilen incelemelerde birleştirmeyi engelle
|
||||
settings.block_rejected_reviews_desc=Yeterli onay olsa bile resmi inceleyiciler tarafından değişiklik istendiğinde birleşme mümkün olmayacaktır.
|
||||
settings.default_branch_desc=Değişiklik istekleri ve kod işlemeleri için varsayılan bir depo dalı seçin:
|
||||
settings.choose_branch=Bir dal seç…
|
||||
settings.no_protected_branch=Korumalı dal yok.
|
||||
|
@ -1356,12 +1438,26 @@ settings.lfs_filelist=Bu depoda barındırılan LFS dosyaları
|
|||
settings.lfs_no_lfs_files=Bu depoda barındırılan herhangi bir LFS dosyası yok
|
||||
settings.lfs_findcommits=İşleme bul
|
||||
settings.lfs_lfs_file_no_commits=Bu LFS dosyası için hiçbir işleme bulunamadı
|
||||
settings.lfs_noattribute=Bu yol, varsayılan dalda kilitlenebilir özelliğe sahip değil
|
||||
settings.lfs_delete=OID %s ile LFS dosyasını sil
|
||||
settings.lfs_delete_warning=Bir LFS dosyasını silmek, ödeme sırasında 'nesne yok' hatalarına neden olabilir. Emin misiniz?
|
||||
settings.lfs_findpointerfiles=İşaretçi dosyalarını bul
|
||||
settings.lfs_locks=Kilitler
|
||||
settings.lfs_invalid_locking_path=Geçersiz yol: %s
|
||||
settings.lfs_invalid_lock_directory=Dizin kilitlenemiyor: %s
|
||||
settings.lfs_lock_already_exists=Kilit zaten var: %s
|
||||
settings.lfs_lock=Kilitle
|
||||
settings.lfs_lock_path=Kilitlenecek dosya yolu...
|
||||
settings.lfs_locks_no_locks=Kilit yok
|
||||
settings.lfs_lock_file_no_exist=Kilitli dosya varsayılan dalda mevcut değil
|
||||
settings.lfs_force_unlock=Kilidi Açmaya Zorla
|
||||
settings.lfs_pointers.found=Bulunan %d blob işaretçi(leri) - %d ilişkili, %d ilişkilendirilmemiş (%d mağazadan eksik)
|
||||
settings.lfs_pointers.sha=Blob SHA
|
||||
settings.lfs_pointers.oid=OID
|
||||
settings.lfs_pointers.inRepo=Depoda
|
||||
settings.lfs_pointers.exists=Mağazada var
|
||||
settings.lfs_pointers.accessible=Kullanıcı tarafından erişilebilir
|
||||
settings.lfs_pointers.associateAccessible=Erişilebilir %d OID ilişkilendirme
|
||||
|
||||
diff.browse_source=Kaynağa Gözat
|
||||
diff.parent=ebeveyn
|
||||
|
@ -1455,8 +1551,12 @@ branch.protected_deletion_failed='%s' dalı korunuyor. Silinemez.
|
|||
branch.restore='%s' Dalını Geri Yükle
|
||||
branch.download='%s' Dalını İndir
|
||||
branch.included_desc=Bu dal varsayılan dalın bir parçasıdır
|
||||
branch.included=Dahil
|
||||
|
||||
topic.manage_topics=Konuları Yönet
|
||||
topic.done=Bitti
|
||||
topic.count_prompt=25'ten fazla konu seçemezsiniz
|
||||
topic.format_prompt=Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
|
||||
|
||||
[org]
|
||||
org_name_holder=Organizasyon Adı
|
||||
|
@ -1522,6 +1622,8 @@ members.invite_now=Şimdi Davet Et
|
|||
|
||||
teams.join=Katıl
|
||||
teams.leave=Ayrıl
|
||||
teams.can_create_org_repo=Depoları oluştur
|
||||
teams.can_create_org_repo_helper=Üyeler organizasyonda yeni depolar oluşturabilirler. Oluşturan yeni depoya yönetici erişimi sağlayacak.
|
||||
teams.read_access=Okuma Erişimi
|
||||
teams.read_access_helper=Üyeler, takım depolarını görüntüleyebilir ve klonlayabilir.
|
||||
teams.write_access=Yazma Erişimi
|
||||
|
@ -1541,6 +1643,7 @@ teams.delete_team_success=Takım silindi.
|
|||
teams.read_permission_desc=Bu takım <strong>Okuma</strong> erişimi veriyor. Üyeler takım depolarını görüntüleyebilir ve klonlayabilir.
|
||||
teams.write_permission_desc=Bu takım <strong>Yazma</strong> erişimi veriyor. Üyeler takım depolarından okuyabilir ve takım depolarına itme yapabilir.
|
||||
teams.admin_permission_desc=Bu takım <strong>Yönetici</strong> erişimi veriyor. Üyeler takım depolarını okuyabilir, itebilir ve katkıcı ekleyebilir.
|
||||
teams.create_repo_permission_desc=Ayrıca, bu takım <strong>Depo oluşturma</strong> izni verir: üyeler organizasyonda yeni depolar oluşturabilir.
|
||||
teams.repositories=Ekip Depoları
|
||||
teams.search_repo_placeholder=Depo ara…
|
||||
teams.remove_all_repos_title=Tüm takım depolarını kaldır
|
||||
|
@ -1552,7 +1655,12 @@ teams.add_duplicate_users=Kullanıcı zaten takımın üyesi.
|
|||
teams.repos.none=Bu takım tarafından hiçbir depoya erişilemedi.
|
||||
teams.members.none=Bu takımda üye yok.
|
||||
teams.specific_repositories=Belirli depolar
|
||||
teams.specific_repositories_helper=Üyeler, yalnızca takıma açıkça eklenen depolara erişebilir. Bunu seçmek, <i>Tüm depolarla</i> zaten eklenmiş olan depoları otomatik olarak <strong>kaldırmaz</strong>.
|
||||
teams.all_repositories=Tüm depolar
|
||||
teams.all_repositories_helper=Takımın tüm depolara erişimi vardır. Bunu seçmek, <strong>mevcut tüm depoları takıma ekleyecektir</strong>.
|
||||
teams.all_repositories_read_permission_desc=Bu takım <strong>tüm depolara Okuma</strong> erişimi sağlar: üyeler depoları görüntüleyebilir ve kopyalayabilir.
|
||||
teams.all_repositories_write_permission_desc=Bu takım <strong>tüm depolara Yazma</strong> erişimi sağlar: üyeler depolardan okuyabilir ve depolara itebilir.
|
||||
teams.all_repositories_admin_permission_desc=Bu ekip <strong>tüm depolara Yönetici</strong> erişimi sağlar: üyeler depolardan okuyabilir, itebilir ve katkıcıları ekleyebilir.
|
||||
|
||||
[admin]
|
||||
dashboard=Pano
|
||||
|
@ -1560,6 +1668,7 @@ users=Kullanıcı Hesapları
|
|||
organizations=Organizasyonlar
|
||||
repositories=Depolar
|
||||
hooks=Varsayılan Web İstemcileri
|
||||
authentication=Yetkilendirme Kaynakları
|
||||
config=Yapılandırma
|
||||
notices=Sistem Bildirimler
|
||||
monitor=İzleme
|
||||
|
@ -1586,7 +1695,10 @@ dashboard.delete_generated_repository_avatars=Oluşturulan depo resimlerini sil
|
|||
dashboard.delete_generated_repository_avatars_success=Oluşturulan depo resimleri silindi.
|
||||
dashboard.git_gc_repos=Depolardaki çöpleri topla
|
||||
dashboard.git_gc_repos_success=Tüm depolardaki çöp toplama işlemi bitti.
|
||||
dashboard.resync_all_sshkeys=Gitea SSH anahtarları ile '.ssh/authorized_keys' dosyasını güncelleyin. (Dahili SSH sunucusu için gerekli değildir.)
|
||||
dashboard.resync_all_sshkeys_success=Gitea tarafından kontrol edilen açık SSH anahtarları güncellendi.
|
||||
dashboard.resync_all_hooks=Tüm depoların alma öncesi, güncelleme ve alma sonrası kancalarını yeniden senkronize edin.
|
||||
dashboard.resync_all_hooks_success=Tüm depoların alım öncesi, güncelleme ve alım sonrası kancaları yeniden senkronize edildi.
|
||||
dashboard.reinit_missing_repos=Kayıtları bulunanlar için tüm eksik Git depolarını yeniden başlat
|
||||
dashboard.reinit_missing_repos_success=Kayıtları bulunanlar için tüm eksik Git depoları yeniden başlatıldı.
|
||||
dashboard.sync_external_users=Harici kullanıcı verisini senkronize et
|
||||
|
@ -1637,6 +1749,7 @@ users.new_success='%s' kullanıcı hesabı oluşturuldu.
|
|||
users.edit=Düzenle
|
||||
users.auth_source=Yetkilendirme Kaynağı
|
||||
users.local=Yerel
|
||||
users.auth_login_name=Oturum Açma Adı Doğrulaması
|
||||
users.password_helper=Şifreyi değiştirmemek için boş bırakın.
|
||||
users.update_profile_success=Kullanıcı hesabı güncellendi.
|
||||
users.edit_account=Kullanıcı Hesabını Düzenle
|
||||
|
@ -1646,6 +1759,7 @@ users.is_activated=Kullanıcı Hesabı Etkinleştirildi
|
|||
users.prohibit_login=Oturum Açmayı Devre Dışı Bırak
|
||||
users.is_admin=Yöneticidir
|
||||
users.allow_git_hook=Git İstemcileri Oluşturabilir
|
||||
users.allow_import_local=Yerel Depoları Alabilir
|
||||
users.allow_create_organization=Organizasyon Oluşturabilir
|
||||
users.update_profile=Kullanıcı Hesabını Güncelle
|
||||
users.delete_account=Kullanıcı Hesabını Sil
|
||||
|
@ -1669,6 +1783,7 @@ repos.forks=Çatallar
|
|||
repos.issues=Konular
|
||||
repos.size=Boyut
|
||||
|
||||
hooks.desc=Web İstemcileri, belirli Gitea olayları tetiklendiğinde otomatik olarak HTTP POST isteklerini sunucuya yapar. Burada tanımlanan Web İstemcileri varsayılandır ve tüm yeni depolara kopyalanır. <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/webhooks/">web istemcileri kılavuzunda</a> daha fazla bilgi edinin.
|
||||
hooks.add_webhook=Varsayılan Web İstemcisi Ekle
|
||||
hooks.update_webhook=Varsayılan Web İstemcisini Güncelle
|
||||
|
||||
|
@ -1690,11 +1805,13 @@ auths.bind_password=Bağlama Parolası
|
|||
auths.bind_password_helper=Uyarı: Bu parola düz metin olarak saklanır. Mümkünse salt okunur bir hesap kullanın.
|
||||
auths.user_base=Kullanıcı Arama Tabanı
|
||||
auths.user_dn=Kullanıcı DN'i
|
||||
auths.attribute_username=Kullanıcı Adı Özelliği
|
||||
auths.attribute_username_placeholder=Gitea'da girilen kullanıcı adını kullanmak için boş bırakın.
|
||||
auths.attribute_name=Ad Özelliği
|
||||
auths.attribute_surname=Soyad Özelliği
|
||||
auths.attribute_mail=E-posta Özelliği
|
||||
auths.attribute_ssh_public_key=Açık SSH Anahtarı Özelliği
|
||||
auths.attributes_in_bind=Bağlı DN tabanındaki özellikleri çek
|
||||
auths.use_paged_search=Sayfalı Aramayı Kullan
|
||||
auths.search_page_size=Sayfa Boyutu
|
||||
auths.filter=Kullanıcı Filtresi
|
||||
|
@ -1710,6 +1827,9 @@ auths.skip_tls_verify=TLS Doğrulamasını Atla
|
|||
auths.pam_service_name=PAM Servis Adı
|
||||
auths.oauth2_provider=OAuth2 Sağlayıcısı
|
||||
auths.oauth2_clientID=İstemci Kimliği (Anahtar)
|
||||
auths.oauth2_clientSecret=İstemci Gizliliği
|
||||
auths.openIdConnectAutoDiscoveryURL=OpenID Connect Otomatik Keşif URL’si
|
||||
auths.oauth2_use_custom_url=Varsayılan URL'ler Yerine Özel URL'ler Kullanın
|
||||
auths.oauth2_tokenURL=Jeton URL
|
||||
auths.oauth2_authURL=Yetkilendirme URL'si
|
||||
auths.oauth2_profileURL=Profil URL’si
|
||||
|
@ -1720,16 +1840,22 @@ auths.sspi_auto_create_users_helper=SSPI kimlik doğrulama yönteminin ilk kez o
|
|||
auths.sspi_auto_activate_users=Kullanıcıları otomatik olarak etkinleştir
|
||||
auths.sspi_auto_activate_users_helper=SSPI kimlik doğrulama yönteminin yeni kullanıcıları otomatik olarak etkinleştirmesine izin ver
|
||||
auths.sspi_strip_domain_names=Alan adlarını kullanıcı adlarından kaldır
|
||||
auths.sspi_strip_domain_names_helper=İşaretlenirse, etki alanı adları oturum açma adlarından kaldırılacaktır (örn. "ETKİALANI\kullanıcı" ve " kullanıcı@örnek.org "her ikisi de sadece "kullanıcı" olacak).
|
||||
auths.sspi_separator_replacement=\, / ve @ yerine kullanılacak ayırıcı
|
||||
auths.sspi_separator_replacement_helper=Alt seviye oturum açma adlarının (örneğin, "ETKİALANI\kullanıcı" içindeki \) ve kullanıcı asıl adlarının (örneğin, "kullanıcı@örnek.org" daki @) ayırıcılarını değiştirmek için kullanılacak karakter.
|
||||
auths.sspi_default_language=Varsayılan kullanıcı dili
|
||||
auths.sspi_default_language_helper=SSPI kimlik doğrulama yöntemi tarafından otomatik olarak oluşturulan kullanıcılar için varsayılan dil. Dili otomatik olarak algılamayı tercih ederseniz boş bırakın.
|
||||
auths.tips=İpuçları
|
||||
auths.tips.oauth2.general=OAuth2 Kimlik Doğrulama
|
||||
auths.tips.oauth2.general.tip=Yeni bir OAuth2 kimlik doğrulaması kaydederken, geri arama/yönlendirme URL'si şöyle olmalıdır: <host>/kullanıcı/oauth2/<Authentication Name>/callback
|
||||
auths.tip.oauth2_provider=OAuth2 Sağlayıcısı
|
||||
auths.tip.bitbucket=https://bitbucket.org/account/user/<kullanıcı adınız>/oauth-consumers/new adında yeni bir OAuth tüketicisi kaydedin ve 'Hesap' - 'Oku' iznini ekleyin
|
||||
auths.tip.dropbox=https://www.dropbox.com/developers/apps adresinde yeni bir uygulama oluştur
|
||||
auths.tip.facebook=https://developers.facebook.com/apps adresinde yeni bir uygulama kaydedin ve "Facebook Giriş" ürününü ekleyin
|
||||
auths.tip.github=https://github.com/settings/applications/new adresinde yeni bir OAuth uygulaması kaydedin
|
||||
auths.tip.gitlab=https://gitlab.com/profile/applications adresinde yeni bir uygulama kaydedin
|
||||
auths.tip.google_plus=OAuth2 istemci kimlik bilgilerini https://console.developers.google.com/ adresindeki Google API konsolundan edinin
|
||||
auths.tip.openid_connect=Bitiş noktalarını belirlemek için OpenID Connect Discovery URL'sini kullanın (<server>/.well-known/openid-configuration)
|
||||
auths.tip.twitter=https://dev.twitter.com/apps adresine gidin, bir uygulama oluşturun ve “Bu uygulamanın Twitter ile oturum açmak için kullanılmasına izin ver” seçeneğinin etkin olduğundan emin olun
|
||||
auths.tip.discord=https://discordapp.com/developers/applications/me adresinde yeni bir uygulama kaydedin
|
||||
auths.tip.gitea=Yeni bir OAuth2 uygulaması kaydedin. Rehber https://docs.gitea.io/en-us/oauth2-provider/ adresinde bulunabilir
|
||||
|
@ -1749,7 +1875,10 @@ auths.login_source_of_type_exist=Bu tür bir kimlik doğrulama kaynağı zaten v
|
|||
config.server_config=Sunucu Yapılandırması
|
||||
config.app_name=Site Başlığı
|
||||
config.app_ver=Gitea Sürümü
|
||||
config.app_url=Gitea Taban URL'si
|
||||
config.custom_conf=Yapılandırma Dosyası Yolu
|
||||
config.custom_file_root_path=Özel Dosya Kök Yolu
|
||||
config.domain=SSH Sunucusu Etki Alanı
|
||||
config.offline_mode=Yerel Kip
|
||||
config.disable_router_log=Yönlendirici Log'larını Devre Dışı Bırak
|
||||
config.run_user=Şu Kullanıcı Olarak Çalıştır
|
||||
|
@ -1777,6 +1906,7 @@ config.ssh_minimum_key_sizes=Minimum Anahtar Uzunlukları
|
|||
config.lfs_config=LFS Yapılandırması
|
||||
config.lfs_enabled=Etkin
|
||||
config.lfs_content_path=LFS İçerik Yolu
|
||||
config.lfs_http_auth_expiry=LFS HTTP Yetkilendirme Süresi
|
||||
|
||||
config.db_config=Veritabanı Yapılandırması
|
||||
config.db_type=Türü
|
||||
|
@ -1790,13 +1920,21 @@ config.service_config=Servis Yapılandırması
|
|||
config.register_email_confirm=Kayıt Olmak İçin E-posta Onayı Gereksin
|
||||
config.disable_register=Kullanıcı Kaydını Devre Dışı Bırak
|
||||
config.allow_only_external_registration=Sadece Dış Hizmetler Aracılığıyla Kullanıcı Kaydına İzin Ver
|
||||
config.enable_openid_signup=OpenID Kendinden Kaydı'nı Etkinleştir
|
||||
config.enable_openid_signin=OpenID Oturum Açmayı Etkinleştiriniz
|
||||
config.show_registration_button=Kaydolma Tuşunu Göster
|
||||
config.require_sign_in_view=Sayfaları Görüntülemek için Giriş Yapmaya Zorla
|
||||
config.mail_notify=E-Posta Bildirimlerini Etkinleştir
|
||||
config.disable_key_size_check=Minimum Anahtar Uzunluğu Kontrolünü Devre Dışı Bırak
|
||||
config.enable_captcha=CAPTCHA'yı Etkinleştir
|
||||
config.active_code_lives=Kod Yaşamlarını Aktifleştir
|
||||
config.reset_password_code_lives=Hesap Kodunun Sona Erme Zamanını Kurtar
|
||||
config.default_keep_email_private=E-posta Adreslerini Varsayılan Olarak Gizle
|
||||
config.default_allow_create_organization=Varsayılan Olarak Kuruluşların Oluşturulmasına İzin Ver
|
||||
config.enable_timetracking=Zaman İzlemeyi Etkinleştir
|
||||
config.default_enable_timetracking=Varsayılan Olarak Zaman İzlemeyi Etkinleştir
|
||||
config.default_allow_only_contributors_to_track_time=Yalnızca Katkıda Bulunanlar Zaman İzlesin
|
||||
config.no_reply_address=Gizlenmiş E-Postalar için Alan Adı
|
||||
config.default_visibility_organization=Yeni organizasyonlar için varsayılan görünürlük
|
||||
config.default_enable_dependencies=Konu Bağımlılıklarını Varsayılan Olarak Etkinleştir
|
||||
|
||||
|
@ -1825,6 +1963,7 @@ config.cache_config=Önbellek Yapılandırması
|
|||
config.cache_adapter=Önbellek Uyarlayıcısı
|
||||
config.cache_interval=Önbellek Aralığı
|
||||
config.cache_conn=Önbellek Bağlantısı
|
||||
config.cache_item_ttl=TTL Önbellek Öğesi
|
||||
|
||||
config.session_config=Oturum Yapılandırması
|
||||
config.session_provider=Oturum Sağlayıcı
|
||||
|
@ -1839,6 +1978,7 @@ config.cookie_life_time=Çerez Yaşam Zamanı
|
|||
config.picture_config=Resim ve Avatar Yapılandırması
|
||||
config.picture_service=Resim Servisi
|
||||
config.disable_gravatar=Gravatar Hizmet Dışı
|
||||
config.enable_federated_avatar=Birleştirilmiş Avatarları Etkinleştir
|
||||
|
||||
config.git_config=Git Yapılandırması
|
||||
config.git_disable_diff_highlight=Değişiklik Sözdizimi Vurgusunu Devre Dışı Bırak
|
||||
|
@ -1904,6 +2044,7 @@ create_pull_request=`<a href="%s/pulls/%s">%s#%[2]s</a> değişiklik isteğini o
|
|||
close_pull_request=`<a href="%s/pulls/%s">%s#%[2]s</a> değişiklik isteğini kapattı`
|
||||
reopen_pull_request=`<a href="%s/pulls/%s">%s#%[2]s</a> değişiklik isteğini tekrar açtı`
|
||||
comment_issue=`<a href="%s/issues/%s">%s#%[2]s</a> konusuna yorum yazdı`
|
||||
comment_pull=`<a href="%s/pulls/%s">%s#%[2]s</a> değişiklik isteği üzerinde yorum yapıldı`
|
||||
merge_pull_request=`<a href="%s/pulls/%s">%s#%[2]s</a> değişim isteğini birleştirdi`
|
||||
transfer_repo=depo <code>%s</code> <a href="%s">%s</a>'a aktarıldı
|
||||
push_tag=<a href="%[1]s">%[3]s</a> deposuna <a href="%s/src/tag/%s">%[2]s</a> etiketi itildi
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -664,10 +664,10 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||
m.Combo("", reqToken()).
|
||||
Patch(mustNotBeArchived, bind(api.EditIssueCommentOption{}), repo.EditIssueComment).
|
||||
Delete(repo.DeleteIssueComment)
|
||||
m.Combo("/reactions", reqToken()).
|
||||
m.Combo("/reactions").
|
||||
Get(repo.GetIssueCommentReactions).
|
||||
Post(bind(api.EditReactionOption{}), repo.PostIssueCommentReaction).
|
||||
Delete(bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction)
|
||||
Post(bind(api.EditReactionOption{}), reqToken(), repo.PostIssueCommentReaction).
|
||||
Delete(bind(api.EditReactionOption{}), reqToken(), repo.DeleteIssueCommentReaction)
|
||||
})
|
||||
})
|
||||
m.Group("/:index", func() {
|
||||
|
@ -704,10 +704,10 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||
m.Put("/:user", reqToken(), repo.AddIssueSubscription)
|
||||
m.Delete("/:user", reqToken(), repo.DelIssueSubscription)
|
||||
})
|
||||
m.Combo("/reactions", reqToken()).
|
||||
m.Combo("/reactions").
|
||||
Get(repo.GetIssueReactions).
|
||||
Post(bind(api.EditReactionOption{}), repo.PostIssueReaction).
|
||||
Delete(bind(api.EditReactionOption{}), repo.DeleteIssueReaction)
|
||||
Post(bind(api.EditReactionOption{}), reqToken(), repo.PostIssueReaction).
|
||||
Delete(bind(api.EditReactionOption{}), reqToken(), repo.DeleteIssueReaction)
|
||||
})
|
||||
}, mustEnableIssuesOrPulls)
|
||||
m.Group("/labels", func() {
|
||||
|
|
|
@ -69,7 +69,6 @@ func TestAPI_RenderGFM(t *testing.T) {
|
|||
- Bezier widget (by @r-lyeh) https://github.com/ocornut/imgui/issues/786`,
|
||||
// rendered
|
||||
`<p>Wiki! Enjoy :)</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="` + AppSubURL + `wiki/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
|
||||
<li><a href="` + AppSubURL + `wiki/Tips" rel="nofollow">Tips</a></li>
|
||||
|
@ -88,13 +87,9 @@ Here are some links to the most important topics. You can find the full list of
|
|||
`,
|
||||
// rendered
|
||||
`<h2 id="user-content-what-is-wine-staging">What is Wine Staging?</h2>
|
||||
|
||||
<p><strong>Wine Staging</strong> on website <a href="http://wine-staging.com" rel="nofollow">wine-staging.com</a>.</p>
|
||||
|
||||
<h2 id="user-content-quick-links">Quick Links</h2>
|
||||
|
||||
<p>Here are some links to the most important topics. You can find the full list of pages at the sidebar.</p>
|
||||
|
||||
<p><a href="` + AppSubURL + `wiki/Configuration" rel="nofollow">Configuration</a>
|
||||
<a href="` + AppSubURL + `wiki/raw/images/icon-bug.png" rel="nofollow"><img src="` + AppSubURL + `wiki/raw/images/icon-bug.png" title="icon-bug.png" alt="images/icon-bug.png"/></a></p>
|
||||
`,
|
||||
|
|
|
@ -524,8 +524,8 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
|||
}
|
||||
}
|
||||
|
||||
if err = models.UpdateIssue(issue); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateIssue", err)
|
||||
if err = models.UpdateIssueByAPI(issue); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err)
|
||||
return
|
||||
}
|
||||
if form.State != nil {
|
||||
|
@ -542,7 +542,11 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
|||
// Refetch from database to assign some automatic values
|
||||
issue, err = models.GetIssueByID(issue.ID)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetIssueByID", err)
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
if err = issue.LoadMilestone(); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusCreated, issue.APIFormat())
|
||||
|
|
|
@ -41,7 +41,7 @@ func GetIssueCommentReactions(ctx *context.APIContext) {
|
|||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ReactionResponseList"
|
||||
// "$ref": "#/responses/ReactionList"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
|
@ -55,7 +55,7 @@ func GetIssueCommentReactions(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanRead(models.UnitTypeIssues) && !ctx.User.IsAdmin {
|
||||
if !ctx.Repo.CanRead(models.UnitTypeIssues) {
|
||||
ctx.Error(http.StatusForbidden, "GetIssueCommentReactions", errors.New("no permission to get reactions"))
|
||||
return
|
||||
}
|
||||
|
@ -71,9 +71,9 @@ func GetIssueCommentReactions(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
var result []api.ReactionResponse
|
||||
var result []api.Reaction
|
||||
for _, r := range reactions {
|
||||
result = append(result, api.ReactionResponse{
|
||||
result = append(result, api.Reaction{
|
||||
User: r.User.APIFormat(),
|
||||
Reaction: r.Type,
|
||||
Created: r.CreatedUnix.AsTime(),
|
||||
|
@ -114,8 +114,10 @@ func PostIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOpti
|
|||
// schema:
|
||||
// "$ref": "#/definitions/EditReactionOption"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Reaction"
|
||||
// "201":
|
||||
// "$ref": "#/responses/ReactionResponse"
|
||||
// "$ref": "#/responses/Reaction"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
|
@ -177,7 +179,7 @@ func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp
|
|||
ctx.Error(http.StatusInternalServerError, "comment.LoadIssue() failed", err)
|
||||
}
|
||||
|
||||
if comment.Issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin {
|
||||
if comment.Issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) {
|
||||
ctx.Error(http.StatusForbidden, "ChangeIssueCommentReaction", errors.New("no permission to change reaction"))
|
||||
return
|
||||
}
|
||||
|
@ -188,19 +190,20 @@ func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp
|
|||
if err != nil {
|
||||
if models.IsErrForbiddenIssueReaction(err) {
|
||||
ctx.Error(http.StatusForbidden, err.Error(), err)
|
||||
} else if models.IsErrReactionAlreadyExist(err) {
|
||||
ctx.JSON(http.StatusOK, api.Reaction{
|
||||
User: ctx.User.APIFormat(),
|
||||
Reaction: reaction.Type,
|
||||
Created: reaction.CreatedUnix.AsTime(),
|
||||
})
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "CreateCommentReaction", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
_, err = reaction.LoadUser()
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "Reaction.LoadUser()", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, api.ReactionResponse{
|
||||
User: reaction.User.APIFormat(),
|
||||
ctx.JSON(http.StatusCreated, api.Reaction{
|
||||
User: ctx.User.APIFormat(),
|
||||
Reaction: reaction.Type,
|
||||
Created: reaction.CreatedUnix.AsTime(),
|
||||
})
|
||||
|
@ -244,7 +247,7 @@ func GetIssueReactions(ctx *context.APIContext) {
|
|||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ReactionResponseList"
|
||||
// "$ref": "#/responses/ReactionList"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
|
@ -258,7 +261,7 @@ func GetIssueReactions(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanRead(models.UnitTypeIssues) && !ctx.User.IsAdmin {
|
||||
if !ctx.Repo.CanRead(models.UnitTypeIssues) {
|
||||
ctx.Error(http.StatusForbidden, "GetIssueReactions", errors.New("no permission to get reactions"))
|
||||
return
|
||||
}
|
||||
|
@ -274,9 +277,9 @@ func GetIssueReactions(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
var result []api.ReactionResponse
|
||||
var result []api.Reaction
|
||||
for _, r := range reactions {
|
||||
result = append(result, api.ReactionResponse{
|
||||
result = append(result, api.Reaction{
|
||||
User: r.User.APIFormat(),
|
||||
Reaction: r.Type,
|
||||
Created: r.CreatedUnix.AsTime(),
|
||||
|
@ -317,8 +320,10 @@ func PostIssueReaction(ctx *context.APIContext, form api.EditReactionOption) {
|
|||
// schema:
|
||||
// "$ref": "#/definitions/EditReactionOption"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Reaction"
|
||||
// "201":
|
||||
// "$ref": "#/responses/ReactionResponse"
|
||||
// "$ref": "#/responses/Reaction"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
|
@ -375,7 +380,7 @@ func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, i
|
|||
return
|
||||
}
|
||||
|
||||
if issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin {
|
||||
if issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) {
|
||||
ctx.Error(http.StatusForbidden, "ChangeIssueCommentReaction", errors.New("no permission to change reaction"))
|
||||
return
|
||||
}
|
||||
|
@ -386,19 +391,20 @@ func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, i
|
|||
if err != nil {
|
||||
if models.IsErrForbiddenIssueReaction(err) {
|
||||
ctx.Error(http.StatusForbidden, err.Error(), err)
|
||||
} else if models.IsErrReactionAlreadyExist(err) {
|
||||
ctx.JSON(http.StatusOK, api.Reaction{
|
||||
User: ctx.User.APIFormat(),
|
||||
Reaction: reaction.Type,
|
||||
Created: reaction.CreatedUnix.AsTime(),
|
||||
})
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "CreateCommentReaction", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
_, err = reaction.LoadUser()
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "Reaction.LoadUser()", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, api.ReactionResponse{
|
||||
User: reaction.User.APIFormat(),
|
||||
ctx.JSON(http.StatusCreated, api.Reaction{
|
||||
User: ctx.User.APIFormat(),
|
||||
Reaction: reaction.Type,
|
||||
Created: reaction.CreatedUnix.AsTime(),
|
||||
})
|
||||
|
|
|
@ -450,8 +450,8 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
|
|||
}
|
||||
}
|
||||
|
||||
if err = models.UpdateIssue(issue); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateIssue", err)
|
||||
if err = models.UpdateIssueByAPI(issue); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err)
|
||||
return
|
||||
}
|
||||
if form.State != nil {
|
||||
|
|
|
@ -99,23 +99,16 @@ type swaggerResponseStopWatchList struct {
|
|||
Body []api.StopWatch `json:"body"`
|
||||
}
|
||||
|
||||
// EditReactionOption
|
||||
// swagger:response EditReactionOption
|
||||
type swaggerEditReactionOption struct {
|
||||
// Reaction
|
||||
// swagger:response Reaction
|
||||
type swaggerReaction struct {
|
||||
// in:body
|
||||
Body api.EditReactionOption `json:"body"`
|
||||
Body api.Reaction `json:"body"`
|
||||
}
|
||||
|
||||
// ReactionResponse
|
||||
// swagger:response ReactionResponse
|
||||
type swaggerReactionResponse struct {
|
||||
// ReactionList
|
||||
// swagger:response ReactionList
|
||||
type swaggerReactionList struct {
|
||||
// in:body
|
||||
Body api.ReactionResponse `json:"body"`
|
||||
}
|
||||
|
||||
// ReactionResponseList
|
||||
// swagger:response ReactionResponseList
|
||||
type swaggerReactionResponseList struct {
|
||||
// in:body
|
||||
Body []api.ReactionResponse `json:"body"`
|
||||
Body []api.Reaction `json:"body"`
|
||||
}
|
||||
|
|
|
@ -123,4 +123,7 @@ type swaggerParameterBodies struct {
|
|||
|
||||
// in:body
|
||||
RepoTopicOptions api.RepoTopicOptions
|
||||
|
||||
// in:body
|
||||
EditReactionOption api.EditReactionOption
|
||||
}
|
||||
|
|
|
@ -113,6 +113,13 @@ func HookPreReceive(ctx *macaron.Context, opts private.HookOptions) {
|
|||
})
|
||||
return
|
||||
}
|
||||
if protectBranch.MergeBlockedByRejectedReview(pr) {
|
||||
log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v and pr #%d has requested changes", opts.UserID, branchName, repo, pr.Index)
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": fmt.Sprintf("protected branch %s can not be pushed to and pr #%d has requested changes", branchName, opts.ProtectedBranchID),
|
||||
})
|
||||
return
|
||||
}
|
||||
} else if !canPush {
|
||||
log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v", opts.UserID, branchName, repo)
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
|
|
|
@ -6,6 +6,8 @@ package repo
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
|
@ -85,3 +87,57 @@ func DeleteAttachment(ctx *context.Context) {
|
|||
"uuid": attach.UUID,
|
||||
})
|
||||
}
|
||||
|
||||
// GetAttachment serve attachements
|
||||
func GetAttachment(ctx *context.Context) {
|
||||
attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid"))
|
||||
if err != nil {
|
||||
if models.IsErrAttachmentNotExist(err) {
|
||||
ctx.Error(404)
|
||||
} else {
|
||||
ctx.ServerError("GetAttachmentByUUID", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
repository, unitType, err := attach.LinkedRepository()
|
||||
if err != nil {
|
||||
ctx.ServerError("LinkedRepository", err)
|
||||
return
|
||||
}
|
||||
|
||||
if repository == nil { //If not linked
|
||||
if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) { //We block if not the uploader
|
||||
ctx.Error(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
} else { //If we have the repository we check access
|
||||
perm, err := models.GetUserRepoPermission(repository, ctx.User)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err.Error())
|
||||
return
|
||||
}
|
||||
if !perm.CanRead(unitType) {
|
||||
ctx.Error(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//If we have matched and access to release or issue
|
||||
fr, err := os.Open(attach.LocalPath())
|
||||
if err != nil {
|
||||
ctx.ServerError("Open", err)
|
||||
return
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
if err := attach.IncreaseDownloadCount(); err != nil {
|
||||
ctx.ServerError("Update", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = ServeData(ctx, attach.Name, fr); err != nil {
|
||||
ctx.ServerError("ServeData", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
@ -963,7 +962,8 @@ func ViewIssue(ctx *context.Context) {
|
|||
}
|
||||
if pull.ProtectedBranch != nil {
|
||||
cnt := pull.ProtectedBranch.GetGrantedApprovalsCount(pull)
|
||||
ctx.Data["IsBlockedByApprovals"] = pull.ProtectedBranch.RequiredApprovals > 0 && cnt < pull.ProtectedBranch.RequiredApprovals
|
||||
ctx.Data["IsBlockedByApprovals"] = !pull.ProtectedBranch.HasEnoughApprovals(pull)
|
||||
ctx.Data["IsBlockedByRejection"] = pull.ProtectedBranch.MergeBlockedByRejectedReview(pull)
|
||||
ctx.Data["GrantedApprovals"] = cnt
|
||||
}
|
||||
ctx.Data["IsPullBranchDeletable"] = canDelete && pull.HeadRepo != nil && git.IsBranchExist(pull.HeadRepo.RepoPath(), pull.HeadBranch)
|
||||
|
@ -1177,13 +1177,11 @@ func UpdateIssueAssignee(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
removed, comment, err := issue_service.ToggleAssignee(issue, ctx.User, assigneeID)
|
||||
_, _, err = issue_service.ToggleAssignee(issue, ctx.User, assigneeID)
|
||||
if err != nil {
|
||||
ctx.ServerError("ToggleAssignee", err)
|
||||
return
|
||||
}
|
||||
|
||||
notification.NotifyIssueChangeAssignee(ctx.User, issue, assignee, removed, comment)
|
||||
}
|
||||
}
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
|
|
|
@ -244,6 +244,7 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm)
|
|||
approvalsWhitelistTeams, _ = base.StringsToInt64s(strings.Split(f.ApprovalsWhitelistTeams, ","))
|
||||
}
|
||||
}
|
||||
protectBranch.BlockOnRejectedReviews = f.BlockOnRejectedReviews
|
||||
|
||||
err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
|
||||
UserIDs: whitelistUsers,
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"bytes"
|
||||
"encoding/gob"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"text/template"
|
||||
"time"
|
||||
|
@ -482,34 +481,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||
m.Get("/following", user.Following)
|
||||
})
|
||||
|
||||
m.Get("/attachments/:uuid", func(ctx *context.Context) {
|
||||
attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid"))
|
||||
if err != nil {
|
||||
if models.IsErrAttachmentNotExist(err) {
|
||||
ctx.Error(404)
|
||||
} else {
|
||||
ctx.ServerError("GetAttachmentByUUID", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fr, err := os.Open(attach.LocalPath())
|
||||
if err != nil {
|
||||
ctx.ServerError("Open", err)
|
||||
return
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
if err := attach.IncreaseDownloadCount(); err != nil {
|
||||
ctx.ServerError("Update", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = repo.ServeData(ctx, attach.Name, fr); err != nil {
|
||||
ctx.ServerError("ServeData", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
m.Get("/attachments/:uuid", repo.GetAttachment)
|
||||
}, ignSignIn)
|
||||
|
||||
m.Group("/attachments", func() {
|
||||
|
|
|
@ -389,21 +389,23 @@ func Issues(ctx *context.Context) {
|
|||
|
||||
reposQuery := ctx.Query("repos")
|
||||
var repoIDs []int64
|
||||
if issueReposQueryPattern.MatchString(reposQuery) {
|
||||
// remove "[" and "]" from string
|
||||
reposQuery = reposQuery[1 : len(reposQuery)-1]
|
||||
//for each ID (delimiter ",") add to int to repoIDs
|
||||
for _, rID := range strings.Split(reposQuery, ",") {
|
||||
// Ensure nonempty string entries
|
||||
if rID != "" && rID != "0" {
|
||||
rIDint64, err := strconv.ParseInt(rID, 10, 64)
|
||||
if err == nil {
|
||||
repoIDs = append(repoIDs, rIDint64)
|
||||
if len(reposQuery) != 0 {
|
||||
if issueReposQueryPattern.MatchString(reposQuery) {
|
||||
// remove "[" and "]" from string
|
||||
reposQuery = reposQuery[1 : len(reposQuery)-1]
|
||||
//for each ID (delimiter ",") add to int to repoIDs
|
||||
for _, rID := range strings.Split(reposQuery, ",") {
|
||||
// Ensure nonempty string entries
|
||||
if rID != "" && rID != "0" {
|
||||
rIDint64, err := strconv.ParseInt(rID, 10, 64)
|
||||
if err == nil {
|
||||
repoIDs = append(repoIDs, rIDint64)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Error("issueReposQueryPattern not match with query")
|
||||
}
|
||||
} else {
|
||||
log.Error("issueReposQueryPattern not match with query")
|
||||
}
|
||||
|
||||
isShowClosed := ctx.Query("state") == "closed"
|
||||
|
|
|
@ -26,10 +26,10 @@ func TestIssues(t *testing.T) {
|
|||
Issues(ctx)
|
||||
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
|
||||
|
||||
assert.EqualValues(t, map[int64]int64{1: 1}, ctx.Data["Counts"])
|
||||
assert.EqualValues(t, map[int64]int64{1: 1, 2: 1}, ctx.Data["Counts"])
|
||||
assert.EqualValues(t, true, ctx.Data["IsShowClosed"])
|
||||
assert.Len(t, ctx.Data["Issues"], 1)
|
||||
assert.Len(t, ctx.Data["Repos"], 1)
|
||||
assert.Len(t, ctx.Data["Repos"], 2)
|
||||
}
|
||||
|
||||
func TestMilestones(t *testing.T) {
|
||||
|
|
|
@ -177,7 +177,6 @@ func composeIssueCommentMessages(ctx *mailCommentContext, tos []string, fromMent
|
|||
|
||||
commentType := models.CommentTypeComment
|
||||
if ctx.Comment != nil {
|
||||
prefix = "Re: "
|
||||
commentType = ctx.Comment.Type
|
||||
link = ctx.Issue.HTMLURL() + "#" + ctx.Comment.HashTag()
|
||||
} else {
|
||||
|
@ -189,13 +188,16 @@ func composeIssueCommentMessages(ctx *mailCommentContext, tos []string, fromMent
|
|||
reviewType = ctx.Comment.Review.Type
|
||||
}
|
||||
|
||||
fallback = prefix + fallbackMailSubject(ctx.Issue)
|
||||
|
||||
// This is the body of the new issue or comment, not the mail body
|
||||
body := string(markup.RenderByType(markdown.MarkupName, []byte(ctx.Content), ctx.Issue.Repo.HTMLURL(), ctx.Issue.Repo.ComposeMetas()))
|
||||
|
||||
actType, actName, tplName := actionToTemplate(ctx.Issue, ctx.ActionType, commentType, reviewType)
|
||||
|
||||
if actName != "new" {
|
||||
prefix = "Re: "
|
||||
}
|
||||
fallback = prefix + fallbackMailSubject(ctx.Issue)
|
||||
|
||||
if ctx.Comment != nil && ctx.Comment.Review != nil {
|
||||
reviewComments = make([]*models.Comment, 0, 10)
|
||||
for _, lines := range ctx.Comment.Review.CodeComments {
|
||||
|
@ -247,7 +249,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, tos []string, fromMent
|
|||
msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
|
||||
|
||||
// Set Message-ID on first message so replies know what to reference
|
||||
if ctx.Comment == nil {
|
||||
if actName == "new" {
|
||||
msg.SetHeader("Message-ID", "<"+ctx.Issue.ReplyReference()+">")
|
||||
} else {
|
||||
msg.SetHeader("In-Reply-To", "<"+ctx.Issue.ReplyReference()+">")
|
||||
|
|
|
@ -410,7 +410,7 @@ func syncMirror(repoID string) {
|
|||
|
||||
theCommits.CompareURL = m.Repo.ComposeCompareURL(oldCommitID, newCommitID)
|
||||
|
||||
notification.NotifySyncPushCommits(m.Repo.MustOwner(), m.Repo, result.refName, oldCommitID, newCommitID, models.ListToPushCommits(commits))
|
||||
notification.NotifySyncPushCommits(m.Repo.MustOwner(), m.Repo, result.refName, oldCommitID, newCommitID, theCommits)
|
||||
}
|
||||
|
||||
// Get latest commit date and update to current repository updated time
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<footer>
|
||||
<div class="ui container">
|
||||
<div class="ui left">
|
||||
© Gitea {{if (or .ShowFooterVersion .PageIsAdmin)}}{{.i18n.Tr "version"}}: {{AppVer}}{{end}} {{if ShowFooterTemplateLoadTime}}{{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> {{.i18n.Tr "template"}}: <strong>{{call .TmplLoadTimes}}</strong>{{end}}
|
||||
{{.i18n.Tr "powered_by" "Gitea"}} {{if (or .ShowFooterVersion .PageIsAdmin)}}{{.i18n.Tr "version"}}: {{AppVer}}{{end}} {{if ShowFooterTemplateLoadTime}}{{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> {{.i18n.Tr "template"}}: <strong>{{call .TmplLoadTimes}}</strong>{{end}}
|
||||
</div>
|
||||
<div class="ui right links">
|
||||
{{if .ShowFooterBranding}}
|
||||
|
@ -19,6 +19,7 @@
|
|||
<a href="{{StaticUrlPrefix}}/vendor/librejs.html" data-jslicense="1">JavaScript licenses</a>
|
||||
{{if .EnableSwagger}}<a href="{{AppSubUrl}}/api/swagger">API</a>{{end}}
|
||||
<a target="_blank" rel="noopener noreferrer" href="https://gitea.io">{{.i18n.Tr "website"}}</a>
|
||||
{{template "custom/extra_links_footer" .}}
|
||||
{{if (or .ShowFooterVersion .PageIsAdmin)}}<span class="version">{{GoVer}}</span>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
0
templates/custom/extra_links_footer.tmpl
Normal file
0
templates/custom/extra_links_footer.tmpl
Normal file
|
@ -1,17 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.footer { font-size:small; color:#666;}
|
||||
</style>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>{{.Subject}}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>@{{.Doer.Name}} assigned you to the {{if .IsPull}}pull request{{else}}issue{{end}} <a href="{{.Link}}">#{{.Issue.Index}}</a> in repository {{.Repo}}.</p>
|
||||
<p>
|
||||
---
|
||||
<br>
|
||||
<a href="{{.Link}}">View it on {{AppName}}</a>.
|
||||
</p>
|
||||
|
||||
<div class="footer">
|
||||
<p>
|
||||
---
|
||||
<br>
|
||||
<a href="{{.Link}}">View it on {{AppName}}</a>.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,41 +3,56 @@
|
|||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>{{.Subject}}</title>
|
||||
{{if .ReviewComments}}
|
||||
|
||||
<style>
|
||||
.review { padding-left: 1em; margin: 1em 0; }
|
||||
.review > pre { padding: 1em; border-left: 1px solid grey; }
|
||||
.footer { font-size:small; color:#666;}
|
||||
{{if .ReviewComments}}
|
||||
.review { padding-left: 1em; margin: 1em 0; }
|
||||
.review > pre { padding: 1em; border-left: 1px solid grey; }
|
||||
{{end}}
|
||||
</style>
|
||||
{{end}}
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{if .IsMention}}<p>@{{.Doer.Name}} mentioned you:</p>{{end}}
|
||||
{{if .IsMention}}<p><b>@{{.Doer.Name}}</b> mentioned you:</p>{{end}}
|
||||
<p>
|
||||
{{if eq .ActionName "close"}}
|
||||
Closed #{{.Issue.Index}}.
|
||||
{{else if eq .ActionName "reopen"}}
|
||||
Reopened #{{.Issue.Index}}.
|
||||
{{else if eq .ActionName "merge"}}
|
||||
Merged #{{.Issue.Index}} into {{.Issue.PullRequest.BaseBranch}}.
|
||||
{{else if eq .ActionName "approve"}}
|
||||
<b>@{{.Doer.Name}}</b> approved this pull request.
|
||||
{{else if eq .ActionName "reject"}}
|
||||
<b>@{{.Doer.Name}}</b> requested changes on this pull request.
|
||||
{{else if eq .ActionName "review"}}
|
||||
<b>@{{.Doer.Name}}</b> commented on this pull request.
|
||||
{{end}}
|
||||
|
||||
{{- if eq .Body ""}}
|
||||
{{if eq .ActionName "new"}}
|
||||
Created #{{.Issue.Index}}.
|
||||
{{else if eq .ActionName "close"}}
|
||||
Closed #{{.Issue.Index}}.
|
||||
{{else if eq .ActionName "reopen"}}
|
||||
Reopened #{{.Issue.Index}}.
|
||||
{{else if ne .ReviewComments}}
|
||||
Empty comment on #{{.Issue.Index}}.
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{.Body | Str2html}}
|
||||
{{end -}}
|
||||
{{- range .ReviewComments}}
|
||||
<hr>
|
||||
In {{.TreePath}}:
|
||||
<div class="review">
|
||||
<pre>{{.Patch}}</pre>
|
||||
<div>{{.RenderedContent | Safe}}</div>
|
||||
</div>
|
||||
{{end -}}
|
||||
</p>
|
||||
<div class="footer">
|
||||
<p>
|
||||
---
|
||||
<br>
|
||||
<a href="{{.Link}}">View it on {{AppName}}</a>.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.footer { font-size:small; color:#666;}
|
||||
</style>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>{{.Subject}}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>You have been added as a collaborator of repository: <code>{{.RepoName}}</code></p>
|
||||
<p>
|
||||
---
|
||||
<br>
|
||||
<a href="{{.Link}}">View it on Gitea</a>.
|
||||
</p>
|
||||
<div class="footer">
|
||||
<p>
|
||||
---
|
||||
<br>
|
||||
<a href="{{.Link}}">View it on {{AppName}}</a>.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -123,7 +123,7 @@
|
|||
<div class="ui green label">{{$.i18n.Tr "repo.activity.published_release_label"}}</div>
|
||||
{{.TagName}}
|
||||
{{if not .IsTag}}
|
||||
<a class="title has-emoji" href="{{$.Repository.HTMLURL}}/src/{{.TagName | EscapePound}}">{{.Title}}</a>
|
||||
<a class="title has-emoji" href="{{$.RepoLink}}/src/{{.TagName | EscapePound}}">{{.Title}}</a>
|
||||
{{end}}
|
||||
{{TimeSinceUnix .CreatedUnix $.Lang}}
|
||||
</p>
|
||||
|
@ -140,7 +140,7 @@
|
|||
{{range .Activity.MergedPRs}}
|
||||
<p class="desc">
|
||||
<div class="ui purple label">{{$.i18n.Tr "repo.activity.merged_prs_label"}}</div>
|
||||
#{{.Index}} <a class="title has-emoji" href="{{$.Repository.HTMLURL}}/pulls/{{.Index}}">{{.Issue.Title}}</a>
|
||||
#{{.Index}} <a class="title has-emoji" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Issue.Title}}</a>
|
||||
{{TimeSinceUnix .MergedUnix $.Lang}}
|
||||
</p>
|
||||
{{end}}
|
||||
|
@ -156,7 +156,7 @@
|
|||
{{range .Activity.OpenedPRs}}
|
||||
<p class="desc">
|
||||
<div class="ui green label">{{$.i18n.Tr "repo.activity.opened_prs_label"}}</div>
|
||||
#{{.Index}} <a class="title has-emoji" href="{{$.Repository.HTMLURL}}/pulls/{{.Index}}">{{.Issue.Title}}</a>
|
||||
#{{.Index}} <a class="title has-emoji" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Issue.Title}}</a>
|
||||
{{TimeSinceUnix .Issue.CreatedUnix $.Lang}}
|
||||
</p>
|
||||
{{end}}
|
||||
|
@ -172,7 +172,7 @@
|
|||
{{range .Activity.ClosedIssues}}
|
||||
<p class="desc">
|
||||
<div class="ui red label">{{$.i18n.Tr "repo.activity.closed_issue_label"}}</div>
|
||||
#{{.Index}} <a class="title has-emoji" href="{{$.Repository.HTMLURL}}/issues/{{.Index}}">{{.Title}}</a>
|
||||
#{{.Index}} <a class="title has-emoji" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title}}</a>
|
||||
{{TimeSinceUnix .ClosedUnix $.Lang}}
|
||||
</p>
|
||||
{{end}}
|
||||
|
@ -188,7 +188,7 @@
|
|||
{{range .Activity.OpenedIssues}}
|
||||
<p class="desc">
|
||||
<div class="ui green label">{{$.i18n.Tr "repo.activity.new_issue_label"}}</div>
|
||||
#{{.Index}} <a class="title has-emoji" href="{{$.Repository.HTMLURL}}/issues/{{.Index}}">{{.Title}}</a>
|
||||
#{{.Index}} <a class="title has-emoji" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title}}</a>
|
||||
{{TimeSinceUnix .CreatedUnix $.Lang}}
|
||||
</p>
|
||||
{{end}}
|
||||
|
@ -209,9 +209,9 @@
|
|||
<div class="ui green label">{{$.i18n.Tr "repo.activity.unresolved_conv_label"}}</div>
|
||||
#{{.Index}}
|
||||
{{if .IsPull}}
|
||||
<a class="title has-emoji" href="{{$.Repository.HTMLURL}}/pulls/{{.Index}}">{{.Title}}</a>
|
||||
<a class="title has-emoji" href="{{$.RepoLink}}/pulls/{{.Index}}">{{.Title}}</a>
|
||||
{{else}}
|
||||
<a class="title has-emoji" href="{{$.Repository.HTMLURL}}/issues/{{.Index}}">{{.Title}}</a>
|
||||
<a class="title has-emoji" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title}}</a>
|
||||
{{end}}
|
||||
{{TimeSinceUnix .UpdatedUnix $.Lang}}
|
||||
</p>
|
||||
|
|
|
@ -213,7 +213,7 @@
|
|||
<a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
|
||||
</div>
|
||||
<div class="ui bottom attached active write tab segment">
|
||||
<textarea tabindex="1" name="content"></textarea>
|
||||
<textarea class="review-textarea" tabindex="1" name="content"></textarea>
|
||||
</div>
|
||||
<div class="ui bottom attached tab preview segment markdown">
|
||||
{{$.i18n.Tr "loading"}}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
{{else if .IsFilesConflicted}}grey
|
||||
{{else if .IsPullRequestBroken}}red
|
||||
{{else if .IsBlockedByApprovals}}red
|
||||
{{else if .IsBlockedByRejection}}red
|
||||
{{else if and .EnableStatusCheck (not .IsRequiredStatusCheckSuccess)}}red
|
||||
{{else if .Issue.PullRequest.IsChecking}}yellow
|
||||
{{else if .Issue.PullRequest.CanAutoMerge}}green
|
||||
|
@ -100,6 +101,11 @@
|
|||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.blocked_by_approvals" .GrantedApprovals .Issue.PullRequest.ProtectedBranch.RequiredApprovals}}
|
||||
</div>
|
||||
{{else if .IsBlockedByRejection}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.blocked_by_rejection"}}
|
||||
</div>
|
||||
{{else if .Issue.PullRequest.IsChecking}}
|
||||
<div class="item text yellow">
|
||||
<span class="octicon octicon-sync"></span>
|
||||
|
@ -131,6 +137,7 @@
|
|||
{{end}}
|
||||
{{if .AllowMerge}}
|
||||
{{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}}
|
||||
{{$approvers := .Issue.PullRequest.GetApprovers}}
|
||||
{{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash}}
|
||||
<div class="ui divider"></div>
|
||||
{{if $prUnit.PullRequestsConfig.AllowMerge}}
|
||||
|
@ -141,7 +148,7 @@
|
|||
<input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}"></textarea>
|
||||
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}">{{$approvers}}</textarea>
|
||||
</div>
|
||||
<button class="ui green button" type="submit" name="do" value="merge">
|
||||
{{$.i18n.Tr "repo.pulls.merge_pull_request"}}
|
||||
|
@ -173,7 +180,7 @@
|
|||
<input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}"></textarea>
|
||||
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}">{{$approvers}}</textarea>
|
||||
</div>
|
||||
<button class="ui green button" type="submit" name="do" value="rebase-merge">
|
||||
{{$.i18n.Tr "repo.pulls.rebase_merge_commit_pull_request"}}
|
||||
|
@ -185,6 +192,7 @@
|
|||
</div>
|
||||
{{end}}
|
||||
{{if $prUnit.PullRequestsConfig.AllowSquash}}
|
||||
{{$commitMessages := .Issue.PullRequest.GetCommitMessages}}
|
||||
<div class="ui form squash-fields" style="display: none">
|
||||
<form action="{{.Link}}/merge" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
|
@ -192,7 +200,7 @@
|
|||
<input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultSquashMessage}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}"></textarea>
|
||||
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}">{{$commitMessages}}{{$approvers}}</textarea>
|
||||
</div>
|
||||
<button class="ui green button" type="submit" name="do" value="squash">
|
||||
{{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}}
|
||||
|
|
|
@ -204,6 +204,13 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<input name="block_on_rejected_reviews" type="checkbox" {{if .Branch.BlockOnRejectedReviews}}checked{{end}}>
|
||||
<label for="block_on_rejected_reviews">{{.i18n.Tr "repo.settings.block_rejected_reviews"}}</label>
|
||||
<p class="help">{{.i18n.Tr "repo.settings.block_rejected_reviews_desc"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
|
|
|
@ -85,13 +85,11 @@ window.onload = function() {
|
|||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
SwaggerUIBundle.presets.apis
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
]
|
||||
})
|
||||
// End Swagger UI call region
|
||||
|
||||
|
|
|
@ -3130,7 +3130,7 @@
|
|||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/ReactionResponseList"
|
||||
"$ref": "#/responses/ReactionList"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
|
@ -3181,8 +3181,11 @@
|
|||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/Reaction"
|
||||
},
|
||||
"201": {
|
||||
"$ref": "#/responses/ReactionResponse"
|
||||
"$ref": "#/responses/Reaction"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
|
@ -3896,7 +3899,7 @@
|
|||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/ReactionResponseList"
|
||||
"$ref": "#/responses/ReactionList"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
|
@ -3947,8 +3950,11 @@
|
|||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/Reaction"
|
||||
},
|
||||
"201": {
|
||||
"$ref": "#/responses/ReactionResponse"
|
||||
"$ref": "#/responses/Reaction"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
|
@ -8609,11 +8615,7 @@
|
|||
"x-go-name": "BranchFilter"
|
||||
},
|
||||
"config": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "Config"
|
||||
"$ref": "#/definitions/CreateHookOptionConfig"
|
||||
},
|
||||
"events": {
|
||||
"type": "array",
|
||||
|
@ -8625,16 +8627,27 @@
|
|||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"dingtalk",
|
||||
"discord",
|
||||
"gitea",
|
||||
"gogs",
|
||||
"msteams",
|
||||
"slack",
|
||||
"discord"
|
||||
"telegram"
|
||||
],
|
||||
"x-go-name": "Type"
|
||||
}
|
||||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"CreateHookOptionConfig": {
|
||||
"description": "CreateHookOptionConfig has all config options in it\nrequired are \"content_type\" and \"url\" Required",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"CreateIssueCommentOption": {
|
||||
"description": "CreateIssueCommentOption options for creating a comment on an issue",
|
||||
"type": "object",
|
||||
|
@ -10815,8 +10828,8 @@
|
|||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"ReactionResponse": {
|
||||
"description": "ReactionResponse contain one reaction",
|
||||
"Reaction": {
|
||||
"description": "Reaction contain one reaction",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
|
@ -11728,12 +11741,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"EditReactionOption": {
|
||||
"description": "EditReactionOption",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/EditReactionOption"
|
||||
}
|
||||
},
|
||||
"EmailList": {
|
||||
"description": "EmailList",
|
||||
"schema": {
|
||||
|
@ -11920,18 +11927,18 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"ReactionResponse": {
|
||||
"description": "ReactionResponse",
|
||||
"Reaction": {
|
||||
"description": "Reaction",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ReactionResponse"
|
||||
"$ref": "#/definitions/Reaction"
|
||||
}
|
||||
},
|
||||
"ReactionResponseList": {
|
||||
"description": "ReactionResponseList",
|
||||
"ReactionList": {
|
||||
"description": "ReactionList",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ReactionResponse"
|
||||
"$ref": "#/definitions/Reaction"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -12157,7 +12164,7 @@
|
|||
"parameterBodies": {
|
||||
"description": "parameterBodies",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/RepoTopicOptions"
|
||||
"$ref": "#/definitions/EditReactionOption"
|
||||
}
|
||||
},
|
||||
"redirect": {
|
||||
|
|
8
vendor/github.com/russross/blackfriday/v2/.gitignore
generated
vendored
8
vendor/github.com/russross/blackfriday/v2/.gitignore
generated
vendored
|
@ -1,8 +0,0 @@
|
|||
*.out
|
||||
*.swp
|
||||
*.8
|
||||
*.6
|
||||
_obj
|
||||
_test*
|
||||
markdown
|
||||
tags
|
17
vendor/github.com/russross/blackfriday/v2/.travis.yml
generated
vendored
17
vendor/github.com/russross/blackfriday/v2/.travis.yml
generated
vendored
|
@ -1,17 +0,0 @@
|
|||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- "1.10.x"
|
||||
- "1.11.x"
|
||||
- tip
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- go: tip
|
||||
install:
|
||||
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- diff -u <(echo -n) <(gofmt -d -s .)
|
||||
- go tool vet .
|
||||
- go test -v ./...
|
29
vendor/github.com/russross/blackfriday/v2/LICENSE.txt
generated
vendored
29
vendor/github.com/russross/blackfriday/v2/LICENSE.txt
generated
vendored
|
@ -1,29 +0,0 @@
|
|||
Blackfriday is distributed under the Simplified BSD License:
|
||||
|
||||
> Copyright © 2011 Russ Ross
|
||||
> All rights reserved.
|
||||
>
|
||||
> Redistribution and use in source and binary forms, with or without
|
||||
> modification, are permitted provided that the following conditions
|
||||
> are met:
|
||||
>
|
||||
> 1. Redistributions of source code must retain the above copyright
|
||||
> notice, this list of conditions and the following disclaimer.
|
||||
>
|
||||
> 2. Redistributions in binary form must reproduce the above
|
||||
> copyright notice, this list of conditions and the following
|
||||
> disclaimer in the documentation and/or other materials provided with
|
||||
> the distribution.
|
||||
>
|
||||
> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
> "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
> LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
> FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
> COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
> INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
> BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
> LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
> LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
> ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
> POSSIBILITY OF SUCH DAMAGE.
|
291
vendor/github.com/russross/blackfriday/v2/README.md
generated
vendored
291
vendor/github.com/russross/blackfriday/v2/README.md
generated
vendored
|
@ -1,291 +0,0 @@
|
|||
Blackfriday [![Build Status](https://travis-ci.org/russross/blackfriday.svg?branch=master)](https://travis-ci.org/russross/blackfriday)
|
||||
===========
|
||||
|
||||
Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It
|
||||
is paranoid about its input (so you can safely feed it user-supplied
|
||||
data), it is fast, it supports common extensions (tables, smart
|
||||
punctuation substitutions, etc.), and it is safe for all utf-8
|
||||
(unicode) input.
|
||||
|
||||
HTML output is currently supported, along with Smartypants
|
||||
extensions.
|
||||
|
||||
It started as a translation from C of [Sundown][3].
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Blackfriday is compatible with any modern Go release. With Go 1.7 and git
|
||||
installed:
|
||||
|
||||
go get gopkg.in/russross/blackfriday.v2
|
||||
|
||||
will download, compile, and install the package into your `$GOPATH`
|
||||
directory hierarchy. Alternatively, you can achieve the same if you
|
||||
import it into a project:
|
||||
|
||||
import "gopkg.in/russross/blackfriday.v2"
|
||||
|
||||
and `go get` without parameters.
|
||||
|
||||
|
||||
Versions
|
||||
--------
|
||||
|
||||
Currently maintained and recommended version of Blackfriday is `v2`. It's being
|
||||
developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the
|
||||
documentation is available at
|
||||
https://godoc.org/gopkg.in/russross/blackfriday.v2.
|
||||
|
||||
It is `go get`-able via via [gopkg.in][6] at `gopkg.in/russross/blackfriday.v2`,
|
||||
but we highly recommend using package management tool like [dep][7] or
|
||||
[Glide][8] and make use of semantic versioning. With package management you
|
||||
should import `github.com/russross/blackfriday` and specify that you're using
|
||||
version 2.0.0.
|
||||
|
||||
Version 2 offers a number of improvements over v1:
|
||||
|
||||
* Cleaned up API
|
||||
* A separate call to [`Parse`][4], which produces an abstract syntax tree for
|
||||
the document
|
||||
* Latest bug fixes
|
||||
* Flexibility to easily add your own rendering extensions
|
||||
|
||||
Potential drawbacks:
|
||||
|
||||
* Our benchmarks show v2 to be slightly slower than v1. Currently in the
|
||||
ballpark of around 15%.
|
||||
* API breakage. If you can't afford modifying your code to adhere to the new API
|
||||
and don't care too much about the new features, v2 is probably not for you.
|
||||
* Several bug fixes are trailing behind and still need to be forward-ported to
|
||||
v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for
|
||||
tracking.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
For the most sensible markdown processing, it is as simple as getting your input
|
||||
into a byte slice and calling:
|
||||
|
||||
```go
|
||||
output := blackfriday.Run(input)
|
||||
```
|
||||
|
||||
Your input will be parsed and the output rendered with a set of most popular
|
||||
extensions enabled. If you want the most basic feature set, corresponding with
|
||||
the bare Markdown specification, use:
|
||||
|
||||
```go
|
||||
output := blackfriday.Run(input, blackfriday.WithNoExtensions())
|
||||
```
|
||||
|
||||
### Sanitize untrusted content
|
||||
|
||||
Blackfriday itself does nothing to protect against malicious content. If you are
|
||||
dealing with user-supplied markdown, we recommend running Blackfriday's output
|
||||
through HTML sanitizer such as [Bluemonday][5].
|
||||
|
||||
Here's an example of simple usage of Blackfriday together with Bluemonday:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"github.com/russross/blackfriday"
|
||||
)
|
||||
|
||||
// ...
|
||||
unsafe := blackfriday.Run(input)
|
||||
html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
|
||||
```
|
||||
|
||||
### Custom options
|
||||
|
||||
If you want to customize the set of options, use `blackfriday.WithExtensions`,
|
||||
`blackfriday.WithRenderer` and `blackfriday.WithRefOverride`.
|
||||
|
||||
You can also check out `blackfriday-tool` for a more complete example
|
||||
of how to use it. Download and install it using:
|
||||
|
||||
go get github.com/russross/blackfriday-tool
|
||||
|
||||
This is a simple command-line tool that allows you to process a
|
||||
markdown file using a standalone program. You can also browse the
|
||||
source directly on github if you are just looking for some example
|
||||
code:
|
||||
|
||||
* <http://github.com/russross/blackfriday-tool>
|
||||
|
||||
Note that if you have not already done so, installing
|
||||
`blackfriday-tool` will be sufficient to download and install
|
||||
blackfriday in addition to the tool itself. The tool binary will be
|
||||
installed in `$GOPATH/bin`. This is a statically-linked binary that
|
||||
can be copied to wherever you need it without worrying about
|
||||
dependencies and library versions.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
All features of Sundown are supported, including:
|
||||
|
||||
* **Compatibility**. The Markdown v1.0.3 test suite passes with
|
||||
the `--tidy` option. Without `--tidy`, the differences are
|
||||
mostly in whitespace and entity escaping, where blackfriday is
|
||||
more consistent and cleaner.
|
||||
|
||||
* **Common extensions**, including table support, fenced code
|
||||
blocks, autolinks, strikethroughs, non-strict emphasis, etc.
|
||||
|
||||
* **Safety**. Blackfriday is paranoid when parsing, making it safe
|
||||
to feed untrusted user input without fear of bad things
|
||||
happening. The test suite stress tests this and there are no
|
||||
known inputs that make it crash. If you find one, please let me
|
||||
know and send me the input that does it.
|
||||
|
||||
NOTE: "safety" in this context means *runtime safety only*. In order to
|
||||
protect yourself against JavaScript injection in untrusted content, see
|
||||
[this example](https://github.com/russross/blackfriday#sanitize-untrusted-content).
|
||||
|
||||
* **Fast processing**. It is fast enough to render on-demand in
|
||||
most web applications without having to cache the output.
|
||||
|
||||
* **Thread safety**. You can run multiple parsers in different
|
||||
goroutines without ill effect. There is no dependence on global
|
||||
shared state.
|
||||
|
||||
* **Minimal dependencies**. Blackfriday only depends on standard
|
||||
library packages in Go. The source code is pretty
|
||||
self-contained, so it is easy to add to any project, including
|
||||
Google App Engine projects.
|
||||
|
||||
* **Standards compliant**. Output successfully validates using the
|
||||
W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional.
|
||||
|
||||
|
||||
Extensions
|
||||
----------
|
||||
|
||||
In addition to the standard markdown syntax, this package
|
||||
implements the following extensions:
|
||||
|
||||
* **Intra-word emphasis supression**. The `_` character is
|
||||
commonly used inside words when discussing code, so having
|
||||
markdown interpret it as an emphasis command is usually the
|
||||
wrong thing. Blackfriday lets you treat all emphasis markers as
|
||||
normal characters when they occur inside a word.
|
||||
|
||||
* **Tables**. Tables can be created by drawing them in the input
|
||||
using a simple syntax:
|
||||
|
||||
```
|
||||
Name | Age
|
||||
--------|------
|
||||
Bob | 27
|
||||
Alice | 23
|
||||
```
|
||||
|
||||
* **Fenced code blocks**. In addition to the normal 4-space
|
||||
indentation to mark code blocks, you can explicitly mark them
|
||||
and supply a language (to make syntax highlighting simple). Just
|
||||
mark it like this:
|
||||
|
||||
```go
|
||||
func getTrue() bool {
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
You can use 3 or more backticks to mark the beginning of the
|
||||
block, and the same number to mark the end of the block.
|
||||
|
||||
* **Definition lists**. A simple definition list is made of a single-line
|
||||
term followed by a colon and the definition for that term.
|
||||
|
||||
Cat
|
||||
: Fluffy animal everyone likes
|
||||
|
||||
Internet
|
||||
: Vector of transmission for pictures of cats
|
||||
|
||||
Terms must be separated from the previous definition by a blank line.
|
||||
|
||||
* **Footnotes**. A marker in the text that will become a superscript number;
|
||||
a footnote definition that will be placed in a list of footnotes at the
|
||||
end of the document. A footnote looks like this:
|
||||
|
||||
This is a footnote.[^1]
|
||||
|
||||
[^1]: the footnote text.
|
||||
|
||||
* **Autolinking**. Blackfriday can find URLs that have not been
|
||||
explicitly marked as links and turn them into links.
|
||||
|
||||
* **Strikethrough**. Use two tildes (`~~`) to mark text that
|
||||
should be crossed out.
|
||||
|
||||
* **Hard line breaks**. With this extension enabled newlines in the input
|
||||
translate into line breaks in the output. This extension is off by default.
|
||||
|
||||
* **Smart quotes**. Smartypants-style punctuation substitution is
|
||||
supported, turning normal double- and single-quote marks into
|
||||
curly quotes, etc.
|
||||
|
||||
* **LaTeX-style dash parsing** is an additional option, where `--`
|
||||
is translated into `–`, and `---` is translated into
|
||||
`—`. This differs from most smartypants processors, which
|
||||
turn a single hyphen into an ndash and a double hyphen into an
|
||||
mdash.
|
||||
|
||||
* **Smart fractions**, where anything that looks like a fraction
|
||||
is translated into suitable HTML (instead of just a few special
|
||||
cases like most smartypant processors). For example, `4/5`
|
||||
becomes `<sup>4</sup>⁄<sub>5</sub>`, which renders as
|
||||
<sup>4</sup>⁄<sub>5</sub>.
|
||||
|
||||
|
||||
Other renderers
|
||||
---------------
|
||||
|
||||
Blackfriday is structured to allow alternative rendering engines. Here
|
||||
are a few of note:
|
||||
|
||||
* [github_flavored_markdown](https://godoc.org/github.com/shurcooL/github_flavored_markdown):
|
||||
provides a GitHub Flavored Markdown renderer with fenced code block
|
||||
highlighting, clickable heading anchor links.
|
||||
|
||||
It's not customizable, and its goal is to produce HTML output
|
||||
equivalent to the [GitHub Markdown API endpoint](https://developer.github.com/v3/markdown/#render-a-markdown-document-in-raw-mode),
|
||||
except the rendering is performed locally.
|
||||
|
||||
* [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt,
|
||||
but for markdown.
|
||||
|
||||
* [LaTeX output](https://github.com/Ambrevar/Blackfriday-LaTeX):
|
||||
renders output as LaTeX.
|
||||
|
||||
* [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer.
|
||||
|
||||
|
||||
Todo
|
||||
----
|
||||
|
||||
* More unit testing
|
||||
* Improve unicode support. It does not understand all unicode
|
||||
rules (about what constitutes a letter, a punctuation symbol,
|
||||
etc.), so it may fail to detect word boundaries correctly in
|
||||
some instances. It is safe on all utf-8 input.
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
[Blackfriday is distributed under the Simplified BSD License](LICENSE.txt)
|
||||
|
||||
|
||||
[1]: https://daringfireball.net/projects/markdown/ "Markdown"
|
||||
[2]: https://golang.org/ "Go Language"
|
||||
[3]: https://github.com/vmg/sundown "Sundown"
|
||||
[4]: https://godoc.org/gopkg.in/russross/blackfriday.v2#Parse "Parse func"
|
||||
[5]: https://github.com/microcosm-cc/bluemonday "Bluemonday"
|
||||
[6]: https://labix.org/gopkg.in "gopkg.in"
|
1590
vendor/github.com/russross/blackfriday/v2/block.go
generated
vendored
1590
vendor/github.com/russross/blackfriday/v2/block.go
generated
vendored
File diff suppressed because it is too large
Load diff
18
vendor/github.com/russross/blackfriday/v2/doc.go
generated
vendored
18
vendor/github.com/russross/blackfriday/v2/doc.go
generated
vendored
|
@ -1,18 +0,0 @@
|
|||
// Package blackfriday is a markdown processor.
|
||||
//
|
||||
// It translates plain text with simple formatting rules into an AST, which can
|
||||
// then be further processed to HTML (provided by Blackfriday itself) or other
|
||||
// formats (provided by the community).
|
||||
//
|
||||
// The simplest way to invoke Blackfriday is to call the Run function. It will
|
||||
// take a text input and produce a text output in HTML (or other format).
|
||||
//
|
||||
// A slightly more sophisticated way to use Blackfriday is to create a Markdown
|
||||
// processor and to call Parse, which returns a syntax tree for the input
|
||||
// document. You can leverage Blackfriday's parsing for content extraction from
|
||||
// markdown documents. You can assign a custom renderer and set various options
|
||||
// to the Markdown processor.
|
||||
//
|
||||
// If you're interested in calling Blackfriday from command line, see
|
||||
// https://github.com/russross/blackfriday-tool.
|
||||
package blackfriday
|
34
vendor/github.com/russross/blackfriday/v2/esc.go
generated
vendored
34
vendor/github.com/russross/blackfriday/v2/esc.go
generated
vendored
|
@ -1,34 +0,0 @@
|
|||
package blackfriday
|
||||
|
||||
import (
|
||||
"html"
|
||||
"io"
|
||||
)
|
||||
|
||||
var htmlEscaper = [256][]byte{
|
||||
'&': []byte("&"),
|
||||
'<': []byte("<"),
|
||||
'>': []byte(">"),
|
||||
'"': []byte("""),
|
||||
}
|
||||
|
||||
func escapeHTML(w io.Writer, s []byte) {
|
||||
var start, end int
|
||||
for end < len(s) {
|
||||
escSeq := htmlEscaper[s[end]]
|
||||
if escSeq != nil {
|
||||
w.Write(s[start:end])
|
||||
w.Write(escSeq)
|
||||
start = end + 1
|
||||
}
|
||||
end++
|
||||
}
|
||||
if start < len(s) && end <= len(s) {
|
||||
w.Write(s[start:end])
|
||||
}
|
||||
}
|
||||
|
||||
func escLink(w io.Writer, text []byte) {
|
||||
unesc := html.UnescapeString(string(text))
|
||||
escapeHTML(w, []byte(unesc))
|
||||
}
|
1
vendor/github.com/russross/blackfriday/v2/go.mod
generated
vendored
1
vendor/github.com/russross/blackfriday/v2/go.mod
generated
vendored
|
@ -1 +0,0 @@
|
|||
module github.com/russross/blackfriday/v2
|
949
vendor/github.com/russross/blackfriday/v2/html.go
generated
vendored
949
vendor/github.com/russross/blackfriday/v2/html.go
generated
vendored
|
@ -1,949 +0,0 @@
|
|||
//
|
||||
// Blackfriday Markdown Processor
|
||||
// Available at http://github.com/russross/blackfriday
|
||||
//
|
||||
// Copyright © 2011 Russ Ross <russ@russross.com>.
|
||||
// Distributed under the Simplified BSD License.
|
||||
// See README.md for details.
|
||||
//
|
||||
|
||||
//
|
||||
//
|
||||
// HTML rendering backend
|
||||
//
|
||||
//
|
||||
|
||||
package blackfriday
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HTMLFlags control optional behavior of HTML renderer.
|
||||
type HTMLFlags int
|
||||
|
||||
// HTML renderer configuration options.
|
||||
const (
|
||||
HTMLFlagsNone HTMLFlags = 0
|
||||
SkipHTML HTMLFlags = 1 << iota // Skip preformatted HTML blocks
|
||||
SkipImages // Skip embedded images
|
||||
SkipLinks // Skip all links
|
||||
Safelink // Only link to trusted protocols
|
||||
NofollowLinks // Only link with rel="nofollow"
|
||||
NoreferrerLinks // Only link with rel="noreferrer"
|
||||
NoopenerLinks // Only link with rel="noopener"
|
||||
HrefTargetBlank // Add a blank target
|
||||
CompletePage // Generate a complete HTML page
|
||||
UseXHTML // Generate XHTML output instead of HTML
|
||||
FootnoteReturnLinks // Generate a link at the end of a footnote to return to the source
|
||||
Smartypants // Enable smart punctuation substitutions
|
||||
SmartypantsFractions // Enable smart fractions (with Smartypants)
|
||||
SmartypantsDashes // Enable smart dashes (with Smartypants)
|
||||
SmartypantsLatexDashes // Enable LaTeX-style dashes (with Smartypants)
|
||||
SmartypantsAngledQuotes // Enable angled double quotes (with Smartypants) for double quotes rendering
|
||||
SmartypantsQuotesNBSP // Enable « French guillemets » (with Smartypants)
|
||||
TOC // Generate a table of contents
|
||||
)
|
||||
|
||||
var (
|
||||
htmlTagRe = regexp.MustCompile("(?i)^" + htmlTag)
|
||||
)
|
||||
|
||||
const (
|
||||
htmlTag = "(?:" + openTag + "|" + closeTag + "|" + htmlComment + "|" +
|
||||
processingInstruction + "|" + declaration + "|" + cdata + ")"
|
||||
closeTag = "</" + tagName + "\\s*[>]"
|
||||
openTag = "<" + tagName + attribute + "*" + "\\s*/?>"
|
||||
attribute = "(?:" + "\\s+" + attributeName + attributeValueSpec + "?)"
|
||||
attributeValue = "(?:" + unquotedValue + "|" + singleQuotedValue + "|" + doubleQuotedValue + ")"
|
||||
attributeValueSpec = "(?:" + "\\s*=" + "\\s*" + attributeValue + ")"
|
||||
attributeName = "[a-zA-Z_:][a-zA-Z0-9:._-]*"
|
||||
cdata = "<!\\[CDATA\\[[\\s\\S]*?\\]\\]>"
|
||||
declaration = "<![A-Z]+" + "\\s+[^>]*>"
|
||||
doubleQuotedValue = "\"[^\"]*\""
|
||||
htmlComment = "<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->"
|
||||
processingInstruction = "[<][?].*?[?][>]"
|
||||
singleQuotedValue = "'[^']*'"
|
||||
tagName = "[A-Za-z][A-Za-z0-9-]*"
|
||||
unquotedValue = "[^\"'=<>`\\x00-\\x20]+"
|
||||
)
|
||||
|
||||
// HTMLRendererParameters is a collection of supplementary parameters tweaking
|
||||
// the behavior of various parts of HTML renderer.
|
||||
type HTMLRendererParameters struct {
|
||||
// Prepend this text to each relative URL.
|
||||
AbsolutePrefix string
|
||||
// Add this text to each footnote anchor, to ensure uniqueness.
|
||||
FootnoteAnchorPrefix string
|
||||
// Show this text inside the <a> tag for a footnote return link, if the
|
||||
// HTML_FOOTNOTE_RETURN_LINKS flag is enabled. If blank, the string
|
||||
// <sup>[return]</sup> is used.
|
||||
FootnoteReturnLinkContents string
|
||||
// If set, add this text to the front of each Heading ID, to ensure
|
||||
// uniqueness.
|
||||
HeadingIDPrefix string
|
||||
// If set, add this text to the back of each Heading ID, to ensure uniqueness.
|
||||
HeadingIDSuffix string
|
||||
// Increase heading levels: if the offset is 1, <h1> becomes <h2> etc.
|
||||
// Negative offset is also valid.
|
||||
// Resulting levels are clipped between 1 and 6.
|
||||
HeadingLevelOffset int
|
||||
|
||||
Title string // Document title (used if CompletePage is set)
|
||||
CSS string // Optional CSS file URL (used if CompletePage is set)
|
||||
Icon string // Optional icon file URL (used if CompletePage is set)
|
||||
|
||||
Flags HTMLFlags // Flags allow customizing this renderer's behavior
|
||||
}
|
||||
|
||||
// HTMLRenderer is a type that implements the Renderer interface for HTML output.
|
||||
//
|
||||
// Do not create this directly, instead use the NewHTMLRenderer function.
|
||||
type HTMLRenderer struct {
|
||||
HTMLRendererParameters
|
||||
|
||||
closeTag string // how to end singleton tags: either " />" or ">"
|
||||
|
||||
// Track heading IDs to prevent ID collision in a single generation.
|
||||
headingIDs map[string]int
|
||||
|
||||
lastOutputLen int
|
||||
disableTags int
|
||||
|
||||
sr *SPRenderer
|
||||
}
|
||||
|
||||
const (
|
||||
xhtmlClose = " />"
|
||||
htmlClose = ">"
|
||||
)
|
||||
|
||||
// NewHTMLRenderer creates and configures an HTMLRenderer object, which
|
||||
// satisfies the Renderer interface.
|
||||
func NewHTMLRenderer(params HTMLRendererParameters) *HTMLRenderer {
|
||||
// configure the rendering engine
|
||||
closeTag := htmlClose
|
||||
if params.Flags&UseXHTML != 0 {
|
||||
closeTag = xhtmlClose
|
||||
}
|
||||
|
||||
if params.FootnoteReturnLinkContents == "" {
|
||||
params.FootnoteReturnLinkContents = `<sup>[return]</sup>`
|
||||
}
|
||||
|
||||
return &HTMLRenderer{
|
||||
HTMLRendererParameters: params,
|
||||
|
||||
closeTag: closeTag,
|
||||
headingIDs: make(map[string]int),
|
||||
|
||||
sr: NewSmartypantsRenderer(params.Flags),
|
||||
}
|
||||
}
|
||||
|
||||
func isHTMLTag(tag []byte, tagname string) bool {
|
||||
found, _ := findHTMLTagPos(tag, tagname)
|
||||
return found
|
||||
}
|
||||
|
||||
// Look for a character, but ignore it when it's in any kind of quotes, it
|
||||
// might be JavaScript
|
||||
func skipUntilCharIgnoreQuotes(html []byte, start int, char byte) int {
|
||||
inSingleQuote := false
|
||||
inDoubleQuote := false
|
||||
inGraveQuote := false
|
||||
i := start
|
||||
for i < len(html) {
|
||||
switch {
|
||||
case html[i] == char && !inSingleQuote && !inDoubleQuote && !inGraveQuote:
|
||||
return i
|
||||
case html[i] == '\'':
|
||||
inSingleQuote = !inSingleQuote
|
||||
case html[i] == '"':
|
||||
inDoubleQuote = !inDoubleQuote
|
||||
case html[i] == '`':
|
||||
inGraveQuote = !inGraveQuote
|
||||
}
|
||||
i++
|
||||
}
|
||||
return start
|
||||
}
|
||||
|
||||
func findHTMLTagPos(tag []byte, tagname string) (bool, int) {
|
||||
i := 0
|
||||
if i < len(tag) && tag[0] != '<' {
|
||||
return false, -1
|
||||
}
|
||||
i++
|
||||
i = skipSpace(tag, i)
|
||||
|
||||
if i < len(tag) && tag[i] == '/' {
|
||||
i++
|
||||
}
|
||||
|
||||
i = skipSpace(tag, i)
|
||||
j := 0
|
||||
for ; i < len(tag); i, j = i+1, j+1 {
|
||||
if j >= len(tagname) {
|
||||
break
|
||||
}
|
||||
|
||||
if strings.ToLower(string(tag[i]))[0] != tagname[j] {
|
||||
return false, -1
|
||||
}
|
||||
}
|
||||
|
||||
if i == len(tag) {
|
||||
return false, -1
|
||||
}
|
||||
|
||||
rightAngle := skipUntilCharIgnoreQuotes(tag, i, '>')
|
||||
if rightAngle >= i {
|
||||
return true, rightAngle
|
||||
}
|
||||
|
||||
return false, -1
|
||||
}
|
||||
|
||||
func skipSpace(tag []byte, i int) int {
|
||||
for i < len(tag) && isspace(tag[i]) {
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func isRelativeLink(link []byte) (yes bool) {
|
||||
// a tag begin with '#'
|
||||
if link[0] == '#' {
|
||||
return true
|
||||
}
|
||||
|
||||
// link begin with '/' but not '//', the second maybe a protocol relative link
|
||||
if len(link) >= 2 && link[0] == '/' && link[1] != '/' {
|
||||
return true
|
||||
}
|
||||
|
||||
// only the root '/'
|
||||
if len(link) == 1 && link[0] == '/' {
|
||||
return true
|
||||
}
|
||||
|
||||
// current directory : begin with "./"
|
||||
if bytes.HasPrefix(link, []byte("./")) {
|
||||
return true
|
||||
}
|
||||
|
||||
// parent directory : begin with "../"
|
||||
if bytes.HasPrefix(link, []byte("../")) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) ensureUniqueHeadingID(id string) string {
|
||||
for count, found := r.headingIDs[id]; found; count, found = r.headingIDs[id] {
|
||||
tmp := fmt.Sprintf("%s-%d", id, count+1)
|
||||
|
||||
if _, tmpFound := r.headingIDs[tmp]; !tmpFound {
|
||||
r.headingIDs[id] = count + 1
|
||||
id = tmp
|
||||
} else {
|
||||
id = id + "-1"
|
||||
}
|
||||
}
|
||||
|
||||
if _, found := r.headingIDs[id]; !found {
|
||||
r.headingIDs[id] = 0
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) addAbsPrefix(link []byte) []byte {
|
||||
if r.AbsolutePrefix != "" && isRelativeLink(link) && link[0] != '.' {
|
||||
newDest := r.AbsolutePrefix
|
||||
if link[0] != '/' {
|
||||
newDest += "/"
|
||||
}
|
||||
newDest += string(link)
|
||||
return []byte(newDest)
|
||||
}
|
||||
return link
|
||||
}
|
||||
|
||||
func appendLinkAttrs(attrs []string, flags HTMLFlags, link []byte) []string {
|
||||
if isRelativeLink(link) {
|
||||
return attrs
|
||||
}
|
||||
val := []string{}
|
||||
if flags&NofollowLinks != 0 {
|
||||
val = append(val, "nofollow")
|
||||
}
|
||||
if flags&NoreferrerLinks != 0 {
|
||||
val = append(val, "noreferrer")
|
||||
}
|
||||
if flags&NoopenerLinks != 0 {
|
||||
val = append(val, "noopener")
|
||||
}
|
||||
if flags&HrefTargetBlank != 0 {
|
||||
attrs = append(attrs, "target=\"_blank\"")
|
||||
}
|
||||
if len(val) == 0 {
|
||||
return attrs
|
||||
}
|
||||
attr := fmt.Sprintf("rel=%q", strings.Join(val, " "))
|
||||
return append(attrs, attr)
|
||||
}
|
||||
|
||||
func isMailto(link []byte) bool {
|
||||
return bytes.HasPrefix(link, []byte("mailto:"))
|
||||
}
|
||||
|
||||
func needSkipLink(flags HTMLFlags, dest []byte) bool {
|
||||
if flags&SkipLinks != 0 {
|
||||
return true
|
||||
}
|
||||
return flags&Safelink != 0 && !isSafeLink(dest) && !isMailto(dest)
|
||||
}
|
||||
|
||||
func isSmartypantable(node *Node) bool {
|
||||
pt := node.Parent.Type
|
||||
return pt != Link && pt != CodeBlock && pt != Code
|
||||
}
|
||||
|
||||
func appendLanguageAttr(attrs []string, info []byte) []string {
|
||||
if len(info) == 0 {
|
||||
return attrs
|
||||
}
|
||||
endOfLang := bytes.IndexAny(info, "\t ")
|
||||
if endOfLang < 0 {
|
||||
endOfLang = len(info)
|
||||
}
|
||||
return append(attrs, fmt.Sprintf("class=\"language-%s\"", info[:endOfLang]))
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) tag(w io.Writer, name []byte, attrs []string) {
|
||||
w.Write(name)
|
||||
if len(attrs) > 0 {
|
||||
w.Write(spaceBytes)
|
||||
w.Write([]byte(strings.Join(attrs, " ")))
|
||||
}
|
||||
w.Write(gtBytes)
|
||||
r.lastOutputLen = 1
|
||||
}
|
||||
|
||||
func footnoteRef(prefix string, node *Node) []byte {
|
||||
urlFrag := prefix + string(slugify(node.Destination))
|
||||
anchor := fmt.Sprintf(`<a href="#fn:%s">%d</a>`, urlFrag, node.NoteID)
|
||||
return []byte(fmt.Sprintf(`<sup class="footnote-ref" id="fnref:%s">%s</sup>`, urlFrag, anchor))
|
||||
}
|
||||
|
||||
func footnoteItem(prefix string, slug []byte) []byte {
|
||||
return []byte(fmt.Sprintf(`<li id="fn:%s%s">`, prefix, slug))
|
||||
}
|
||||
|
||||
func footnoteReturnLink(prefix, returnLink string, slug []byte) []byte {
|
||||
const format = ` <a class="footnote-return" href="#fnref:%s%s">%s</a>`
|
||||
return []byte(fmt.Sprintf(format, prefix, slug, returnLink))
|
||||
}
|
||||
|
||||
func itemOpenCR(node *Node) bool {
|
||||
if node.Prev == nil {
|
||||
return false
|
||||
}
|
||||
ld := node.Parent.ListData
|
||||
return !ld.Tight && ld.ListFlags&ListTypeDefinition == 0
|
||||
}
|
||||
|
||||
func skipParagraphTags(node *Node) bool {
|
||||
grandparent := node.Parent.Parent
|
||||
if grandparent == nil || grandparent.Type != List {
|
||||
return false
|
||||
}
|
||||
tightOrTerm := grandparent.Tight || node.Parent.ListFlags&ListTypeTerm != 0
|
||||
return grandparent.Type == List && tightOrTerm
|
||||
}
|
||||
|
||||
func cellAlignment(align CellAlignFlags) string {
|
||||
switch align {
|
||||
case TableAlignmentLeft:
|
||||
return "left"
|
||||
case TableAlignmentRight:
|
||||
return "right"
|
||||
case TableAlignmentCenter:
|
||||
return "center"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) out(w io.Writer, text []byte) {
|
||||
if r.disableTags > 0 {
|
||||
w.Write(htmlTagRe.ReplaceAll(text, []byte{}))
|
||||
} else {
|
||||
w.Write(text)
|
||||
}
|
||||
r.lastOutputLen = len(text)
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) cr(w io.Writer) {
|
||||
if r.lastOutputLen > 0 {
|
||||
r.out(w, nlBytes)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
nlBytes = []byte{'\n'}
|
||||
gtBytes = []byte{'>'}
|
||||
spaceBytes = []byte{' '}
|
||||
)
|
||||
|
||||
var (
|
||||
brTag = []byte("<br>")
|
||||
brXHTMLTag = []byte("<br />")
|
||||
emTag = []byte("<em>")
|
||||
emCloseTag = []byte("</em>")
|
||||
strongTag = []byte("<strong>")
|
||||
strongCloseTag = []byte("</strong>")
|
||||
delTag = []byte("<del>")
|
||||
delCloseTag = []byte("</del>")
|
||||
ttTag = []byte("<tt>")
|
||||
ttCloseTag = []byte("</tt>")
|
||||
aTag = []byte("<a")
|
||||
aCloseTag = []byte("</a>")
|
||||
preTag = []byte("<pre>")
|
||||
preCloseTag = []byte("</pre>")
|
||||
codeTag = []byte("<code>")
|
||||
codeCloseTag = []byte("</code>")
|
||||
pTag = []byte("<p>")
|
||||
pCloseTag = []byte("</p>")
|
||||
blockquoteTag = []byte("<blockquote>")
|
||||
blockquoteCloseTag = []byte("</blockquote>")
|
||||
hrTag = []byte("<hr>")
|
||||
hrXHTMLTag = []byte("<hr />")
|
||||
ulTag = []byte("<ul>")
|
||||
ulCloseTag = []byte("</ul>")
|
||||
olTag = []byte("<ol>")
|
||||
olCloseTag = []byte("</ol>")
|
||||
dlTag = []byte("<dl>")
|
||||
dlCloseTag = []byte("</dl>")
|
||||
liTag = []byte("<li>")
|
||||
liCloseTag = []byte("</li>")
|
||||
ddTag = []byte("<dd>")
|
||||
ddCloseTag = []byte("</dd>")
|
||||
dtTag = []byte("<dt>")
|
||||
dtCloseTag = []byte("</dt>")
|
||||
tableTag = []byte("<table>")
|
||||
tableCloseTag = []byte("</table>")
|
||||
tdTag = []byte("<td")
|
||||
tdCloseTag = []byte("</td>")
|
||||
thTag = []byte("<th")
|
||||
thCloseTag = []byte("</th>")
|
||||
theadTag = []byte("<thead>")
|
||||
theadCloseTag = []byte("</thead>")
|
||||
tbodyTag = []byte("<tbody>")
|
||||
tbodyCloseTag = []byte("</tbody>")
|
||||
trTag = []byte("<tr>")
|
||||
trCloseTag = []byte("</tr>")
|
||||
h1Tag = []byte("<h1")
|
||||
h1CloseTag = []byte("</h1>")
|
||||
h2Tag = []byte("<h2")
|
||||
h2CloseTag = []byte("</h2>")
|
||||
h3Tag = []byte("<h3")
|
||||
h3CloseTag = []byte("</h3>")
|
||||
h4Tag = []byte("<h4")
|
||||
h4CloseTag = []byte("</h4>")
|
||||
h5Tag = []byte("<h5")
|
||||
h5CloseTag = []byte("</h5>")
|
||||
h6Tag = []byte("<h6")
|
||||
h6CloseTag = []byte("</h6>")
|
||||
|
||||
footnotesDivBytes = []byte("\n<div class=\"footnotes\">\n\n")
|
||||
footnotesCloseDivBytes = []byte("\n</div>\n")
|
||||
)
|
||||
|
||||
func headingTagsFromLevel(level int) ([]byte, []byte) {
|
||||
if level <= 1 {
|
||||
return h1Tag, h1CloseTag
|
||||
}
|
||||
switch level {
|
||||
case 2:
|
||||
return h2Tag, h2CloseTag
|
||||
case 3:
|
||||
return h3Tag, h3CloseTag
|
||||
case 4:
|
||||
return h4Tag, h4CloseTag
|
||||
case 5:
|
||||
return h5Tag, h5CloseTag
|
||||
}
|
||||
return h6Tag, h6CloseTag
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) outHRTag(w io.Writer) {
|
||||
if r.Flags&UseXHTML == 0 {
|
||||
r.out(w, hrTag)
|
||||
} else {
|
||||
r.out(w, hrXHTMLTag)
|
||||
}
|
||||
}
|
||||
|
||||
// RenderNode is a default renderer of a single node of a syntax tree. For
|
||||
// block nodes it will be called twice: first time with entering=true, second
|
||||
// time with entering=false, so that it could know when it's working on an open
|
||||
// tag and when on close. It writes the result to w.
|
||||
//
|
||||
// The return value is a way to tell the calling walker to adjust its walk
|
||||
// pattern: e.g. it can terminate the traversal by returning Terminate. Or it
|
||||
// can ask the walker to skip a subtree of this node by returning SkipChildren.
|
||||
// The typical behavior is to return GoToNext, which asks for the usual
|
||||
// traversal to the next node.
|
||||
func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkStatus {
|
||||
attrs := []string{}
|
||||
switch node.Type {
|
||||
case Text:
|
||||
if r.Flags&Smartypants != 0 {
|
||||
var tmp bytes.Buffer
|
||||
escapeHTML(&tmp, node.Literal)
|
||||
r.sr.Process(w, tmp.Bytes())
|
||||
} else {
|
||||
if node.Parent.Type == Link {
|
||||
escLink(w, node.Literal)
|
||||
} else {
|
||||
escapeHTML(w, node.Literal)
|
||||
}
|
||||
}
|
||||
case Softbreak:
|
||||
r.cr(w)
|
||||
// TODO: make it configurable via out(renderer.softbreak)
|
||||
case Hardbreak:
|
||||
if r.Flags&UseXHTML == 0 {
|
||||
r.out(w, brTag)
|
||||
} else {
|
||||
r.out(w, brXHTMLTag)
|
||||
}
|
||||
r.cr(w)
|
||||
case Emph:
|
||||
if entering {
|
||||
r.out(w, emTag)
|
||||
} else {
|
||||
r.out(w, emCloseTag)
|
||||
}
|
||||
case Strong:
|
||||
if entering {
|
||||
r.out(w, strongTag)
|
||||
} else {
|
||||
r.out(w, strongCloseTag)
|
||||
}
|
||||
case Del:
|
||||
if entering {
|
||||
r.out(w, delTag)
|
||||
} else {
|
||||
r.out(w, delCloseTag)
|
||||
}
|
||||
case HTMLSpan:
|
||||
if r.Flags&SkipHTML != 0 {
|
||||
break
|
||||
}
|
||||
r.out(w, node.Literal)
|
||||
case Link:
|
||||
// mark it but don't link it if it is not a safe link: no smartypants
|
||||
dest := node.LinkData.Destination
|
||||
if needSkipLink(r.Flags, dest) {
|
||||
if entering {
|
||||
r.out(w, ttTag)
|
||||
} else {
|
||||
r.out(w, ttCloseTag)
|
||||
}
|
||||
} else {
|
||||
if entering {
|
||||
dest = r.addAbsPrefix(dest)
|
||||
var hrefBuf bytes.Buffer
|
||||
hrefBuf.WriteString("href=\"")
|
||||
escLink(&hrefBuf, dest)
|
||||
hrefBuf.WriteByte('"')
|
||||
attrs = append(attrs, hrefBuf.String())
|
||||
if node.NoteID != 0 {
|
||||
r.out(w, footnoteRef(r.FootnoteAnchorPrefix, node))
|
||||
break
|
||||
}
|
||||
attrs = appendLinkAttrs(attrs, r.Flags, dest)
|
||||
if len(node.LinkData.Title) > 0 {
|
||||
var titleBuff bytes.Buffer
|
||||
titleBuff.WriteString("title=\"")
|
||||
escapeHTML(&titleBuff, node.LinkData.Title)
|
||||
titleBuff.WriteByte('"')
|
||||
attrs = append(attrs, titleBuff.String())
|
||||
}
|
||||
r.tag(w, aTag, attrs)
|
||||
} else {
|
||||
if node.NoteID != 0 {
|
||||
break
|
||||
}
|
||||
r.out(w, aCloseTag)
|
||||
}
|
||||
}
|
||||
case Image:
|
||||
if r.Flags&SkipImages != 0 {
|
||||
return SkipChildren
|
||||
}
|
||||
if entering {
|
||||
dest := node.LinkData.Destination
|
||||
dest = r.addAbsPrefix(dest)
|
||||
if r.disableTags == 0 {
|
||||
//if options.safe && potentiallyUnsafe(dest) {
|
||||
//out(w, `<img src="" alt="`)
|
||||
//} else {
|
||||
r.out(w, []byte(`<img src="`))
|
||||
escLink(w, dest)
|
||||
r.out(w, []byte(`" alt="`))
|
||||
//}
|
||||
}
|
||||
r.disableTags++
|
||||
} else {
|
||||
r.disableTags--
|
||||
if r.disableTags == 0 {
|
||||
if node.LinkData.Title != nil {
|
||||
r.out(w, []byte(`" title="`))
|
||||
escapeHTML(w, node.LinkData.Title)
|
||||
}
|
||||
r.out(w, []byte(`" />`))
|
||||
}
|
||||
}
|
||||
case Code:
|
||||
r.out(w, codeTag)
|
||||
escapeHTML(w, node.Literal)
|
||||
r.out(w, codeCloseTag)
|
||||
case Document:
|
||||
break
|
||||
case Paragraph:
|
||||
if skipParagraphTags(node) {
|
||||
break
|
||||
}
|
||||
if entering {
|
||||
// TODO: untangle this clusterfuck about when the newlines need
|
||||
// to be added and when not.
|
||||
if node.Prev != nil {
|
||||
switch node.Prev.Type {
|
||||
case HTMLBlock, List, Paragraph, Heading, CodeBlock, BlockQuote, HorizontalRule:
|
||||
r.cr(w)
|
||||
}
|
||||
}
|
||||
if node.Parent.Type == BlockQuote && node.Prev == nil {
|
||||
r.cr(w)
|
||||
}
|
||||
r.out(w, pTag)
|
||||
} else {
|
||||
r.out(w, pCloseTag)
|
||||
if !(node.Parent.Type == Item && node.Next == nil) {
|
||||
r.cr(w)
|
||||
}
|
||||
}
|
||||
case BlockQuote:
|
||||
if entering {
|
||||
r.cr(w)
|
||||
r.out(w, blockquoteTag)
|
||||
} else {
|
||||
r.out(w, blockquoteCloseTag)
|
||||
r.cr(w)
|
||||
}
|
||||
case HTMLBlock:
|
||||
if r.Flags&SkipHTML != 0 {
|
||||
break
|
||||
}
|
||||
r.cr(w)
|
||||
r.out(w, node.Literal)
|
||||
r.cr(w)
|
||||
case Heading:
|
||||
headingLevel := r.HTMLRendererParameters.HeadingLevelOffset + node.Level
|
||||
openTag, closeTag := headingTagsFromLevel(headingLevel)
|
||||
if entering {
|
||||
if node.IsTitleblock {
|
||||
attrs = append(attrs, `class="title"`)
|
||||
}
|
||||
if node.HeadingID != "" {
|
||||
id := r.ensureUniqueHeadingID(node.HeadingID)
|
||||
if r.HeadingIDPrefix != "" {
|
||||
id = r.HeadingIDPrefix + id
|
||||
}
|
||||
if r.HeadingIDSuffix != "" {
|
||||
id = id + r.HeadingIDSuffix
|
||||
}
|
||||
attrs = append(attrs, fmt.Sprintf(`id="%s"`, id))
|
||||
}
|
||||
r.cr(w)
|
||||
r.tag(w, openTag, attrs)
|
||||
} else {
|
||||
r.out(w, closeTag)
|
||||
if !(node.Parent.Type == Item && node.Next == nil) {
|
||||
r.cr(w)
|
||||
}
|
||||
}
|
||||
case HorizontalRule:
|
||||
r.cr(w)
|
||||
r.outHRTag(w)
|
||||
r.cr(w)
|
||||
case List:
|
||||
openTag := ulTag
|
||||
closeTag := ulCloseTag
|
||||
if node.ListFlags&ListTypeOrdered != 0 {
|
||||
openTag = olTag
|
||||
closeTag = olCloseTag
|
||||
}
|
||||
if node.ListFlags&ListTypeDefinition != 0 {
|
||||
openTag = dlTag
|
||||
closeTag = dlCloseTag
|
||||
}
|
||||
if entering {
|
||||
if node.IsFootnotesList {
|
||||
r.out(w, footnotesDivBytes)
|
||||
r.outHRTag(w)
|
||||
r.cr(w)
|
||||
}
|
||||
r.cr(w)
|
||||
if node.Parent.Type == Item && node.Parent.Parent.Tight {
|
||||
r.cr(w)
|
||||
}
|
||||
r.tag(w, openTag[:len(openTag)-1], attrs)
|
||||
r.cr(w)
|
||||
} else {
|
||||
r.out(w, closeTag)
|
||||
//cr(w)
|
||||
//if node.parent.Type != Item {
|
||||
// cr(w)
|
||||
//}
|
||||
if node.Parent.Type == Item && node.Next != nil {
|
||||
r.cr(w)
|
||||
}
|
||||
if node.Parent.Type == Document || node.Parent.Type == BlockQuote {
|
||||
r.cr(w)
|
||||
}
|
||||
if node.IsFootnotesList {
|
||||
r.out(w, footnotesCloseDivBytes)
|
||||
}
|
||||
}
|
||||
case Item:
|
||||
openTag := liTag
|
||||
closeTag := liCloseTag
|
||||
if node.ListFlags&ListTypeDefinition != 0 {
|
||||
openTag = ddTag
|
||||
closeTag = ddCloseTag
|
||||
}
|
||||
if node.ListFlags&ListTypeTerm != 0 {
|
||||
openTag = dtTag
|
||||
closeTag = dtCloseTag
|
||||
}
|
||||
if entering {
|
||||
if itemOpenCR(node) {
|
||||
r.cr(w)
|
||||
}
|
||||
if node.ListData.RefLink != nil {
|
||||
slug := slugify(node.ListData.RefLink)
|
||||
r.out(w, footnoteItem(r.FootnoteAnchorPrefix, slug))
|
||||
break
|
||||
}
|
||||
r.out(w, openTag)
|
||||
} else {
|
||||
if node.ListData.RefLink != nil {
|
||||
slug := slugify(node.ListData.RefLink)
|
||||
if r.Flags&FootnoteReturnLinks != 0 {
|
||||
r.out(w, footnoteReturnLink(r.FootnoteAnchorPrefix, r.FootnoteReturnLinkContents, slug))
|
||||
}
|
||||
}
|
||||
r.out(w, closeTag)
|
||||
r.cr(w)
|
||||
}
|
||||
case CodeBlock:
|
||||
attrs = appendLanguageAttr(attrs, node.Info)
|
||||
r.cr(w)
|
||||
r.out(w, preTag)
|
||||
r.tag(w, codeTag[:len(codeTag)-1], attrs)
|
||||
escapeHTML(w, node.Literal)
|
||||
r.out(w, codeCloseTag)
|
||||
r.out(w, preCloseTag)
|
||||
if node.Parent.Type != Item {
|
||||
r.cr(w)
|
||||
}
|
||||
case Table:
|
||||
if entering {
|
||||
r.cr(w)
|
||||
r.out(w, tableTag)
|
||||
} else {
|
||||
r.out(w, tableCloseTag)
|
||||
r.cr(w)
|
||||
}
|
||||
case TableCell:
|
||||
openTag := tdTag
|
||||
closeTag := tdCloseTag
|
||||
if node.IsHeader {
|
||||
openTag = thTag
|
||||
closeTag = thCloseTag
|
||||
}
|
||||
if entering {
|
||||
align := cellAlignment(node.Align)
|
||||
if align != "" {
|
||||
attrs = append(attrs, fmt.Sprintf(`align="%s"`, align))
|
||||
}
|
||||
if node.Prev == nil {
|
||||
r.cr(w)
|
||||
}
|
||||
r.tag(w, openTag, attrs)
|
||||
} else {
|
||||
r.out(w, closeTag)
|
||||
r.cr(w)
|
||||
}
|
||||
case TableHead:
|
||||
if entering {
|
||||
r.cr(w)
|
||||
r.out(w, theadTag)
|
||||
} else {
|
||||
r.out(w, theadCloseTag)
|
||||
r.cr(w)
|
||||
}
|
||||
case TableBody:
|
||||
if entering {
|
||||
r.cr(w)
|
||||
r.out(w, tbodyTag)
|
||||
// XXX: this is to adhere to a rather silly test. Should fix test.
|
||||
if node.FirstChild == nil {
|
||||
r.cr(w)
|
||||
}
|
||||
} else {
|
||||
r.out(w, tbodyCloseTag)
|
||||
r.cr(w)
|
||||
}
|
||||
case TableRow:
|
||||
if entering {
|
||||
r.cr(w)
|
||||
r.out(w, trTag)
|
||||
} else {
|
||||
r.out(w, trCloseTag)
|
||||
r.cr(w)
|
||||
}
|
||||
default:
|
||||
panic("Unknown node type " + node.Type.String())
|
||||
}
|
||||
return GoToNext
|
||||
}
|
||||
|
||||
// RenderHeader writes HTML document preamble and TOC if requested.
|
||||
func (r *HTMLRenderer) RenderHeader(w io.Writer, ast *Node) {
|
||||
r.writeDocumentHeader(w)
|
||||
if r.Flags&TOC != 0 {
|
||||
r.writeTOC(w, ast)
|
||||
}
|
||||
}
|
||||
|
||||
// RenderFooter writes HTML document footer.
|
||||
func (r *HTMLRenderer) RenderFooter(w io.Writer, ast *Node) {
|
||||
if r.Flags&CompletePage == 0 {
|
||||
return
|
||||
}
|
||||
io.WriteString(w, "\n</body>\n</html>\n")
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) writeDocumentHeader(w io.Writer) {
|
||||
if r.Flags&CompletePage == 0 {
|
||||
return
|
||||
}
|
||||
ending := ""
|
||||
if r.Flags&UseXHTML != 0 {
|
||||
io.WriteString(w, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" ")
|
||||
io.WriteString(w, "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n")
|
||||
io.WriteString(w, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n")
|
||||
ending = " /"
|
||||
} else {
|
||||
io.WriteString(w, "<!DOCTYPE html>\n")
|
||||
io.WriteString(w, "<html>\n")
|
||||
}
|
||||
io.WriteString(w, "<head>\n")
|
||||
io.WriteString(w, " <title>")
|
||||
if r.Flags&Smartypants != 0 {
|
||||
r.sr.Process(w, []byte(r.Title))
|
||||
} else {
|
||||
escapeHTML(w, []byte(r.Title))
|
||||
}
|
||||
io.WriteString(w, "</title>\n")
|
||||
io.WriteString(w, " <meta name=\"GENERATOR\" content=\"Blackfriday Markdown Processor v")
|
||||
io.WriteString(w, Version)
|
||||
io.WriteString(w, "\"")
|
||||
io.WriteString(w, ending)
|
||||
io.WriteString(w, ">\n")
|
||||
io.WriteString(w, " <meta charset=\"utf-8\"")
|
||||
io.WriteString(w, ending)
|
||||
io.WriteString(w, ">\n")
|
||||
if r.CSS != "" {
|
||||
io.WriteString(w, " <link rel=\"stylesheet\" type=\"text/css\" href=\"")
|
||||
escapeHTML(w, []byte(r.CSS))
|
||||
io.WriteString(w, "\"")
|
||||
io.WriteString(w, ending)
|
||||
io.WriteString(w, ">\n")
|
||||
}
|
||||
if r.Icon != "" {
|
||||
io.WriteString(w, " <link rel=\"icon\" type=\"image/x-icon\" href=\"")
|
||||
escapeHTML(w, []byte(r.Icon))
|
||||
io.WriteString(w, "\"")
|
||||
io.WriteString(w, ending)
|
||||
io.WriteString(w, ">\n")
|
||||
}
|
||||
io.WriteString(w, "</head>\n")
|
||||
io.WriteString(w, "<body>\n\n")
|
||||
}
|
||||
|
||||
func (r *HTMLRenderer) writeTOC(w io.Writer, ast *Node) {
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
inHeading := false
|
||||
tocLevel := 0
|
||||
headingCount := 0
|
||||
|
||||
ast.Walk(func(node *Node, entering bool) WalkStatus {
|
||||
if node.Type == Heading && !node.HeadingData.IsTitleblock {
|
||||
inHeading = entering
|
||||
if entering {
|
||||
node.HeadingID = fmt.Sprintf("toc_%d", headingCount)
|
||||
if node.Level == tocLevel {
|
||||
buf.WriteString("</li>\n\n<li>")
|
||||
} else if node.Level < tocLevel {
|
||||
for node.Level < tocLevel {
|
||||
tocLevel--
|
||||
buf.WriteString("</li>\n</ul>")
|
||||
}
|
||||
buf.WriteString("</li>\n\n<li>")
|
||||
} else {
|
||||
for node.Level > tocLevel {
|
||||
tocLevel++
|
||||
buf.WriteString("\n<ul>\n<li>")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(&buf, `<a href="#toc_%d">`, headingCount)
|
||||
headingCount++
|
||||
} else {
|
||||
buf.WriteString("</a>")
|
||||
}
|
||||
return GoToNext
|
||||
}
|
||||
|
||||
if inHeading {
|
||||
return r.RenderNode(&buf, node, entering)
|
||||
}
|
||||
|
||||
return GoToNext
|
||||
})
|
||||
|
||||
for ; tocLevel > 0; tocLevel-- {
|
||||
buf.WriteString("</li>\n</ul>")
|
||||
}
|
||||
|
||||
if buf.Len() > 0 {
|
||||
io.WriteString(w, "<nav>\n")
|
||||
w.Write(buf.Bytes())
|
||||
io.WriteString(w, "\n\n</nav>\n")
|
||||
}
|
||||
r.lastOutputLen = buf.Len()
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue