Merge branch 'master' into graceful-queues

This commit is contained in:
Lauris BH 2020-01-05 01:20:52 +02:00 committed by GitHub
commit ec83b83c74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
161 changed files with 17393 additions and 6702 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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:

View file

@ -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`)

View file

@ -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
View file

@ -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
View file

@ -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=

View file

@ -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),

View file

@ -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)
}

View file

@ -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)
}

View 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)
})
}
}

View file

@ -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()

View file

@ -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)
})
}
}

View file

@ -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

View file

@ -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)
}
// __________ .__ .__ __________ __
// \______ \__ __| | | |\______ \ ____ ________ __ ____ _______/ |_
// | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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})
}

View file

@ -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
View 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))
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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",

View file

@ -171,6 +171,7 @@ type ProtectBranchForm struct {
EnableApprovalsWhitelist bool
ApprovalsWhitelistUsers string
ApprovalsWhitelistTeams string
BlockOnRejectedReviews bool
}
// Validate validates the fields

View file

@ -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

View 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("&#x21a9;&#xfe0e;")
_, _ = 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),
))
}

View 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?://")
)

View 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),
),
)
}

View file

@ -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
}

View file

@ -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>`)
}

View 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
}

View file

@ -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("&amp;"),
'<': []byte("&lt;"),
'>': []byte("&gt;"),
'"': []byte("&quot;"),
}
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 {

View file

@ -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)

View file

@ -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
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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)
}

View file

@ -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{

View file

@ -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

View file

@ -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"`
}

View file

@ -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

View file

@ -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",

View file

@ -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,

View file

@ -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
}

View file

@ -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",

View file

@ -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,

View file

@ -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)
}

View file

@ -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",

View file

@ -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

View file

@ -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>

View file

@ -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=ഇഷ്യൂകള്‍

View file

@ -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.

View file

@ -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.

View file

@ -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 не найден

View file

@ -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=ı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=ı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=ı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=ı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 URLsi
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 URLsi
@ -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

View file

@ -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() {

View file

@ -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>
`,

View file

@ -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())

View file

@ -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(),
})

View file

@ -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 {

View file

@ -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"`
}

View file

@ -123,4 +123,7 @@ type swaggerParameterBodies struct {
// in:body
RepoTopicOptions api.RepoTopicOptions
// in:body
EditReactionOption api.EditReactionOption
}

View file

@ -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{}{

View file

@ -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
}
}

View file

@ -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{}{

View file

@ -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,

View file

@ -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() {

View file

@ -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"

View file

@ -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) {

View file

@ -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()+">")

View file

@ -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

View file

@ -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>

View file

View 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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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"}}

View file

@ -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"}}

View file

@ -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>

View file

@ -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

View file

@ -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": {

View file

@ -1,8 +0,0 @@
*.out
*.swp
*.8
*.6
_obj
_test*
markdown
tags

View file

@ -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 ./...

View file

@ -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.

View file

@ -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 `&ndash;`, and `---` is translated into
`&mdash;`. 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>&frasl;<sub>5</sub>`, which renders as
<sup>4</sup>&frasl;<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"

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -1,34 +0,0 @@
package blackfriday
import (
"html"
"io"
)
var htmlEscaper = [256][]byte{
'&': []byte("&amp;"),
'<': []byte("&lt;"),
'>': []byte("&gt;"),
'"': []byte("&quot;"),
}
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))
}

View file

@ -1 +0,0 @@
module github.com/russross/blackfriday/v2

View file

@ -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