Browse Source

Merge pull request #1 from gogits/master

同步原作者的更新
pull/4495/head
Jesse Zhu 9 years ago
parent
commit
9e076e9a76
  1. 6
      .bra.toml
  2. 21
      .dockerignore
  3. 6
      .github/CONTRIBUTING.md
  4. 22
      .github/ISSUE_TEMPLATE.md
  5. 4
      .github/PULL_REQUEST_TEMPLATE.md
  6. 23
      .gitignore
  7. 61
      .gopmfile
  8. 5
      .travis.yml
  9. 11
      Dockerfile
  10. 14
      Dockerfile.rpi
  11. 15
      Makefile
  12. 27
      README.md
  13. 8
      README_ZH.md
  14. 26
      cmd/serve.go
  15. 17
      cmd/update.go
  16. 137
      cmd/web.go
  17. 6
      conf/README.md
  18. 48
      conf/app.ini
  19. 14
      conf/locale/TRANSLATORS
  20. 71
      conf/locale/locale_bg-BG.ini
  21. 779
      conf/locale/locale_de-DE.ini
  22. 80
      conf/locale/locale_en-US.ini
  23. 225
      conf/locale/locale_es-ES.ini
  24. 1098
      conf/locale/locale_fi-FI.ini
  25. 273
      conf/locale/locale_fr-FR.ini
  26. 251
      conf/locale/locale_it-IT.ini
  27. 95
      conf/locale/locale_ja-JP.ini
  28. 71
      conf/locale/locale_lv-LV.ini
  29. 235
      conf/locale/locale_nl-NL.ini
  30. 79
      conf/locale/locale_pl-PL.ini
  31. 133
      conf/locale/locale_pt-BR.ini
  32. 149
      conf/locale/locale_ru-RU.ini
  33. 73
      conf/locale/locale_zh-CN.ini
  34. 165
      conf/locale/locale_zh-HK.ini
  35. 29
      docker/README.md
  36. 2
      docker/build.sh
  37. 0
      docker/s6/crond/down
  38. 9
      docker/s6/crond/run
  39. 9
      docker/start.sh
  40. 144
      glide.lock
  41. 56
      glide.yaml
  42. 2
      gogs.go
  43. 50
      models/access.go
  44. 82
      models/action.go
  45. 39
      models/admin.go
  46. 59
      models/cron/cron.go
  47. 43
      models/error.go
  48. 66
      models/git_diff.go
  49. 100
      models/git_diff_test.go
  50. 674
      models/issue.go
  51. 320
      models/issue_comment.go
  52. 234
      models/issue_label.go
  53. 37
      models/login.go
  54. 222
      models/migrations/migrations.go
  55. 31
      models/models.go
  56. 102
      models/org.go
  57. 68
      models/pull.go
  58. 14
      models/release.go
  59. 439
      models/repo.go
  60. 57
      models/repo_branch.go
  61. 159
      models/repo_collaboration.go
  62. 241
      models/ssh_key.go
  63. 45
      models/ssh_key_test.go
  64. 51
      models/token.go
  65. 90
      models/update.go
  66. 155
      models/user.go
  67. 35
      models/webhook.go
  68. 62
      models/wiki.go
  69. 4
      modules/auth/auth.go
  70. 1
      modules/auth/auth_form.go
  71. 68
      modules/auth/ldap/ldap.go
  72. 6
      modules/auth/org.go
  73. 4
      modules/auth/repo_form.go
  74. 1
      modules/auth/user_form.go
  75. 316
      modules/avatar/avatar.go
  76. 64
      modules/avatar/avatar_test.go
  77. 21
      modules/base/base.go
  78. 65
      modules/base/tool.go
  79. 84
      modules/bindata/bindata.go
  80. 71
      modules/context/api.go
  81. 87
      modules/context/auth.go
  82. 82
      modules/context/context.go
  83. 85
      modules/context/org.go
  84. 123
      modules/context/repo.go
  85. 27
      modules/cron/constantdelay.go
  86. 54
      modules/cron/constantdelay_test.go
  87. 237
      modules/cron/cron.go
  88. 255
      modules/cron/cron_test.go
  89. 129
      modules/cron/doc.go
  90. 231
      modules/cron/parser.go
  91. 117
      modules/cron/parser_test.go
  92. 161
      modules/cron/spec.go
  93. 173
      modules/cron/spec_test.go
  94. 206
      modules/httplib/httplib_test.go
  95. 68
      modules/log/database.go
  96. 1
      modules/log/log.go
  97. 11
      modules/mailer/mail.go
  98. 5
      modules/mailer/mailer.go
  99. 195
      modules/markdown/markdown.go
  100. 133
      modules/middleware/auth.go
  101. Some files were not shown because too many files have changed in this diff Show More

6
.bra.toml

@ -1,6 +1,6 @@
[run] [run]
init_cmds = [ init_cmds = [
#["grep", "-rn", "FIXME", "."], ["make", "build-dev", "TAGS=sqlite"],
["./gogs", "web"] ["./gogs", "web"]
] ]
watch_all = true watch_all = true
@ -11,9 +11,9 @@ watch_dirs = [
"$WORKDIR/routers" "$WORKDIR/routers"
] ]
watch_exts = [".go"] watch_exts = [".go"]
ignore_files = [".+_test.go"]
build_delay = 1500 build_delay = 1500
cmds = [ cmds = [
["go", "install", "-v", "-race"], # sqlite redis memcache cert pam tidb ["make", "build-dev", "TAGS=sqlite"], # cert pam tidb
["go", "build", "-race"],
["./gogs", "web"] ["./gogs", "web"]
] ]

21
.dockerignore

@ -1,20 +1,21 @@
.git .git
.git/ .git/**
.git/*
conf conf
conf/ conf/**
conf/*
packager packager
packager/ packager/**
packager/*
scripts scripts
scripts/ scripts/**
scripts/* .github/
.github/**
config.codekit
LICENSE
Makefile
.dockerignore
*.yml *.yml
*.md *.md
.bra.toml .bra.toml
.editorconfig .editorconfig
.gitignore .gitignore
.gopmfile .gopmfile
config.codekit Dockerfile*
LICENSE

6
CONTRIBUTING.md → .github/CONTRIBUTING.md

@ -46,11 +46,7 @@ Please read detailed information on [Wiki](https://github.com/gogits/gogs/wiki/C
### Ask For Help ### Ask For Help
Before opening an issue, please make sure your problem isn't already addressed on the [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.md) and [FAQs](http://gogs.io/docs/intro/faqs.html) pages. Before opening an issue, please make sure your problem isn't already addressed on the [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.html) and [FAQs](http://gogs.io/docs/intro/faqs.html) pages.
## Things To Notice
Please take a moment to check that an issue on [GitHub](https://github.com/gogits/gogs/issues) or card on [Trello](https://trello.com/b/uxAoeLUl/gogs-go-git-service) doesn't already exist documenting your bug report or improvement proposal. If it does, it never hurts to add a quick "+1" or "I have this problem too". This will help prioritize the most common problems and requests.
## Code of conduct ## Code of conduct

22
.github/ISSUE_TEMPLATE.md

@ -0,0 +1,22 @@
We DO NOT take questions or config/deploy problems on GitHub, please use our forum: https://discuss.gogs.io
Please take a moment to search that an issue doesn't already exist.
For bug reports, please give the relevant info:
- Gogs version (or commit ref):
- Git version:
- Operating system:
- Database:
- [ ] PostgreSQL
- [ ] MySQL
- [ ] SQLite
- Can you reproduce the bug at http://try.gogs.io:
- [ ] Yes (provide example URL)
- [ ] No
- [ ] Not relevant
- Log gist:
## Description
...

4
.github/PULL_REQUEST_TEMPLATE.md

@ -0,0 +1,4 @@
Please, make sure you are targeting the `develop` branch!
More instructions about contributing with Gogs code can be found here:
https://github.com/gogits/gogs/wiki/Contributing-Code

23
.gitignore vendored

@ -8,32 +8,13 @@ data/
.idea/ .idea/
*.iml *.iml
public/img/avatar/ public/img/avatar/
files/
*.o
*.a
*.so
_obj
_test
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe *.exe
*.exe~ *.exe~
/gogs /gogs
profile/ profile/
__pycache__
*.pem *.pem
output* output*
.brackets.json
docker/fig.yml
docker/docker/Dockerfile
docker/docker/init_gogs.sh
gogs.sublime-project gogs.sublime-project
gogs.sublime-workspace gogs.sublime-workspace
.tags* /release
release vendor

61
.gopmfile

@ -2,50 +2,55 @@
path = github.com/gogits/gogs path = github.com/gogits/gogs
[deps] [deps]
github.com/bradfitz/gomemcache = commit:72a68649ba github.com/bradfitz/gomemcache = commit:fb1f79c
github.com/codegangsta/cli = commit:b5232bb github.com/codegangsta/cli = commit:aca5b04
github.com/go-macaron/binding = commit:2502aaf github.com/go-macaron/binding = commit:a68f342
github.com/go-macaron/cache = commit:5617353 github.com/go-macaron/cache = commit:5617353
github.com/go-macaron/captcha = commit:8aa5919 github.com/go-macaron/captcha = commit:8aa5919
github.com/go-macaron/csrf = commit:715bca0 github.com/go-macaron/csrf = commit:6a9a7df
github.com/go-macaron/gzip = commit:4938e9b github.com/go-macaron/gzip = commit:cad1c65
github.com/go-macaron/i18n = commit:d2d3329 github.com/go-macaron/i18n = commit:d2d3329
github.com/go-macaron/inject = commit:c5ab7bf github.com/go-macaron/inject = commit:c5ab7bf
github.com/go-macaron/session = commit:66031fc github.com/go-macaron/session = commit:66031fc
github.com/go-macaron/toolbox = commit:ab30a81 github.com/go-macaron/toolbox = commit:82b5115
github.com/go-sql-driver/mysql = commit:d512f20 github.com/go-sql-driver/mysql = commit:66312f7
github.com/go-xorm/core = commit:acb6f00 github.com/go-xorm/core = commit:5021584
github.com/go-xorm/xorm = commit:a8fba4d github.com/go-xorm/xorm = commit:769f6b3
github.com/gogits/chardet = commit:2404f77725 github.com/gogits/chardet = commit:2404f77
github.com/gogits/git-module = github.com/gogits/cron = commit:3abc0f8
github.com/gogits/go-gogs-client = commit:78460e9 github.com/gogits/git-module = commit:76e8cce
github.com/gogits/go-gogs-client = commit:788ec59
github.com/issue9/identicon = commit:f8c0d2c github.com/issue9/identicon = commit:f8c0d2c
github.com/kardianos/minwinsvc = commit:cad6b2b github.com/kardianos/minwinsvc = commit:cad6b2b
github.com/klauspost/compress = commit:f7ff951 github.com/klauspost/compress = commit:006acde
github.com/klauspost/cpuid = commit:ef30b90 github.com/klauspost/cpuid = commit:09cded8
github.com/klauspost/crc32 = commit:41b6596 github.com/klauspost/crc32 = commit:19b0b33
github.com/lib/pq = commit:11fc39a github.com/lib/pq = commit:165a352
github.com/mattn/go-sqlite3 = commit:5651a9d github.com/mattn/go-sqlite3 = commit:76e335f
github.com/mcuadros/go-version = commit:d52711f github.com/mcuadros/go-version = commit:d52711f
github.com/microcosm-cc/bluemonday = commit:4ac6f27 github.com/microcosm-cc/bluemonday = commit:4ac6f27
github.com/msteinert/pam = commit:02ccfbf github.com/msteinert/pam = commit:02ccfbf
github.com/nfnt/resize = commit:dc93e1b98c github.com/nfnt/resize = commit:4d93a29
github.com/russross/blackfriday = commit:d18b67a github.com/russross/blackfriday = commit:b43df97
github.com/sergi/go-diff = github.com/satori/go.uuid = commit:e673fdd
github.com/sergi/go-diff = commit:ec7fdbb
github.com/shurcooL/sanitized_anchor_name = commit:10ef21a github.com/shurcooL/sanitized_anchor_name = commit:10ef21a
github.com/Unknwon/cae = commit:7f5e046 github.com/Unknwon/cae = commit:7f5e046
github.com/Unknwon/com = commit:28b053d github.com/Unknwon/com = commit:28b053d
github.com/Unknwon/i18n = commit:3b48b66 github.com/Unknwon/i18n = commit:3b48b66
github.com/Unknwon/paginater = commit:7748a72 github.com/Unknwon/paginater = commit:7748a72
golang.org/x/net = commit:28273ec golang.org/x/crypto = commit:c197bcf
golang.org/x/text = commit:cf49866 golang.org/x/net = commit:35b06af
golang.org/x/crypto = commit:f18420e golang.org/x/sys = commit:9d4e42a
golang.org/x/text = commit:1b466db
gopkg.in/alexcesaro/quotedprintable.v3 = commit:2caba25
gopkg.in/asn1-ber.v1 = commit:4e86f43 gopkg.in/asn1-ber.v1 = commit:4e86f43
gopkg.in/gomail.v2 = commit:fbb71dd gopkg.in/bufio.v1 = commit:567b2bf
gopkg.in/ini.v1 = commit:77178f2 gopkg.in/gomail.v2 = commit:060a5f4
gopkg.in/ldap.v2 = commit:e9a325d gopkg.in/ini.v1 = commit:776aa73
gopkg.in/macaron.v1 = commit:1c6dd87 gopkg.in/ldap.v2 = commit:07a7330
gopkg.in/redis.v2 = commit:e617904962 gopkg.in/macaron.v1 = commit:94a5ef7
gopkg.in/redis.v2 = commit:e617904
[res] [res]
include = public|scripts|templates include = public|scripts|templates

5
.travis.yml

@ -3,6 +3,7 @@ language: go
go: go:
- 1.4 - 1.4
- 1.5 - 1.5
- 1.6
before_install: before_install:
- sudo apt-get update -qq - sudo apt-get update -qq
@ -12,7 +13,9 @@ before_install:
install: install:
- go get -t -v ./... - go get -t -v ./...
script: go build -v -tags "pam" script:
- go build -v -tags "pam"
- go test -v -cover -race ./...
notifications: notifications:
email: email:

11
Dockerfile

@ -1,13 +1,10 @@
FROM alpine:3.2 FROM alpine:3.3
MAINTAINER jp@roemer.im MAINTAINER jp@roemer.im
# Install system utils & Gogs runtime dependencies # Install system utils & Gogs runtime dependencies
ADD https://github.com/tianon/gosu/releases/download/1.6/gosu-amd64 /usr/sbin/gosu ADD https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64 /usr/sbin/gosu
RUN echo "@edge http://dl-4.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories \ RUN chmod +x /usr/sbin/gosu \
&& echo "@community http://dl-4.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories \ && apk --no-cache --no-progress add ca-certificates bash git linux-pam s6 curl openssh socat
&& apk -U --no-progress upgrade \
&& apk -U --no-progress add ca-certificates bash git linux-pam s6@edge curl openssh socat \
&& chmod +x /usr/sbin/gosu
ENV GOGS_CUSTOM /data/gogs ENV GOGS_CUSTOM /data/gogs

14
Dockerfile.rpi

@ -1,13 +1,13 @@
FROM sander85/rpi-alpine:latest FROM hypriot/rpi-alpine-scratch:v3.2
MAINTAINER jp@roemer.im, raxetul@gmail.com MAINTAINER jp@roemer.im, raxetul@gmail.com
# Install system utils & Gogs runtime dependencies # Install system utils & Gogs runtime dependencies
ADD https://github.com/tianon/gosu/releases/download/1.6/gosu-armhf /usr/sbin/gosu ADD https://github.com/tianon/gosu/releases/download/1.7/gosu-armhf /usr/sbin/gosu
RUN echo "@edge http://dl-4.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories \ RUN chmod +x /usr/sbin/gosu \
&& echo "@community http://dl-4.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories \ && echo "http://dl-4.alpinelinux.org/alpine/v3.3/main/" | tee /etc/apk/repositories \
&& apk -U --no-progress upgrade \ && echo "http://dl-4.alpinelinux.org/alpine/v3.3/community/" | tee -a /etc/apk/repositories \
&& apk -U --no-progress add ca-certificates bash git linux-pam s6@edge curl openssh socat \ && apk -U --no-progress upgrade && rm -f /var/cache/apk/APKINDEX.* \
&& chmod +x /usr/sbin/gosu && apk --no-cache --no-progress add ca-certificates bash git linux-pam s6 curl openssh socat
ENV GOGS_CUSTOM /data/gogs ENV GOGS_CUSTOM /data/gogs

15
Makefile

@ -6,6 +6,7 @@ LESS_FILES := $(wildcard public/less/gogs.less public/less/_*.less)
GENERATED := modules/bindata/bindata.go public/css/gogs.css GENERATED := modules/bindata/bindata.go public/css/gogs.css
TAGS = "" TAGS = ""
BUILD_FLAGS = "-v"
RELEASE_ROOT = "release" RELEASE_ROOT = "release"
RELEASE_GOGS = "release/gogs" RELEASE_GOGS = "release/gogs"
@ -16,12 +17,16 @@ NOW = $(shell date -u '+%Y%m%d%I%M%S')
.IGNORE: public/css/gogs.css .IGNORE: public/css/gogs.css
build: $(GENERATED) build: $(GENERATED)
go install -v -ldflags '$(LDFLAGS)' -tags '$(TAGS)' go install $(BUILD_FLAGS) -ldflags '$(LDFLAGS)' -tags '$(TAGS)'
cp '$(GOPATH)/bin/gogs' . cp '$(GOPATH)/bin/gogs' .
govet: govet:
go tool vet -composites=false -methods=false -structtags=false . go tool vet -composites=false -methods=false -structtags=false .
build-dev: $(GENERATED) govet
go install $(BUILD_FLAGS) -race -tags '$(TAGS)'
cp '$(GOPATH)/bin/gogs' .
pack: pack:
rm -rf $(RELEASE_GOGS) rm -rf $(RELEASE_GOGS)
mkdir -p $(RELEASE_GOGS) mkdir -p $(RELEASE_GOGS)
@ -48,4 +53,10 @@ clean-mac: clean
find . -name ".DS_Store" -print0 | xargs -0 rm find . -name ".DS_Store" -print0 | xargs -0 rm
test: test:
go test ./... go test -cover -race ./...
fixme:
grep -rnw "FIXME" routers models modules
todo:
grep -rnw "TODO" routers models modules

27
README.md

@ -1,9 +1,9 @@
Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?branch=master)](https://travis-ci.org/gogits/gogs) [![Docker Repository on Quay](https://quay.io/repository/gogs/gogs/status "Docker Repository on Quay")](https://quay.io/repository/gogs/gogs) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/gogs/localized.svg)](https://crowdin.com/project/gogs) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gogits/gogs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?branch=master)](https://travis-ci.org/gogits/gogs) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/gogs/localized.svg)](https://crowdin.com/project/gogs) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gogits/gogs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
===================== =====================
![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true) ![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true)
##### Current version: 0.8.23 ##### Current version: 0.9.15
| Web | UI | Preview | | Web | UI | Preview |
|:-------------:|:-------:|:-------:| |:-------------:|:-------:|:-------:|
@ -11,15 +11,14 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra
|![Profile](https://gogs.io/img/screenshots/4.png)|![Admin Dashboard](https://gogs.io/img/screenshots/5.png)|![Diff](https://gogs.io/img/screenshots/6.png)| |![Profile](https://gogs.io/img/screenshots/4.png)|![Admin Dashboard](https://gogs.io/img/screenshots/5.png)|![Diff](https://gogs.io/img/screenshots/6.png)|
|![Issues](https://gogs.io/img/screenshots/7.png)|![Releases](https://gogs.io/img/screenshots/8.png)|![Organization](https://gogs.io/img/screenshots/9.png)| |![Issues](https://gogs.io/img/screenshots/7.png)|![Releases](https://gogs.io/img/screenshots/8.png)|![Organization](https://gogs.io/img/screenshots/9.png)|
### NOTICES ### Important Notes
- :bangbang: You **MUST** read [CONTRIBUTING.md](CONTRIBUTING.md) for bug report and contributing code. :bangbang: 1. **YOU MUST READ [Contributing Code](https://github.com/gogits/gogs/wiki/Contributing-Code) BEFORE STARTING TO WORK ON A PULL REQUEST**.
- Please [start discussion](http://forum.gogs.io/category/2/general-discussion) or [ask a question](http://forum.gogs.io/category/4/getting-help) on [the forum](http://forum.gogs.io/). GitHub issue tracker only keeps **bugs** and **feature requests**, all other topics will be closed without reason. 2. Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) was reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site.
- Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) was reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site. 3. The demo site [try.gogs.io](https://try.gogs.io) is running under `develop` branch.
- The demo site [try.gogs.io](https://try.gogs.io) is running under `develop` branch. 4. If you think there are vulnerabilities in the project, please talk privately to **u@gogs.io**. Thanks!
- If you think there are vulnerabilities in the project, please talk privately to **u@gogs.io**. Thanks! 5. If you're interested in using APIs, we have experimental support with [documentation](https://github.com/gogits/go-gogs-client/wiki).
- If you're interested in using APIs, we have experimental support with [documentation](https://github.com/gogits/go-gogs-client/wiki). 6. If your team/company is using Gogs and would like to put your logo on [our website](http://gogs.io), contact us by any means.
- If your team/company is using Gogs and would like to put your logo on [our website](http://gogs.io), contact us by any means.
[简体中文](README_ZH.md) [简体中文](README_ZH.md)
@ -32,7 +31,7 @@ The goal of this project is to make the easiest, fastest, and most painless way
- Please see the [Documentation](http://gogs.io/docs/intro) for common usages and change log. - Please see the [Documentation](http://gogs.io/docs/intro) for common usages and change log.
- See the [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team. - See the [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team.
- Want to try it before doing anything else? Do it [online](https://try.gogs.io/gogs/gogs)! - Want to try it before doing anything else? Do it [online](https://try.gogs.io/gogs/gogs)!
- Having trouble? Get help with [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.html). - Having trouble? Get help with [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.html) or [User Forum](https://discuss.gogs.io/).
- Want to help with localization? Check out the [guide](http://gogs.io/docs/features/i18n.html)! - Want to help with localization? Check out the [guide](http://gogs.io/docs/features/i18n.html)!
## Features ## Features
@ -50,7 +49,7 @@ The goal of this project is to make the easiest, fastest, and most painless way
- Mail service - Mail service
- Administration panel - Administration panel
- Supports MySQL, PostgreSQL, SQLite3 and [TiDB](https://github.com/pingcap/tidb) (experimental) - Supports MySQL, PostgreSQL, SQLite3 and [TiDB](https://github.com/pingcap/tidb) (experimental)
- Multi-language support ([14 languages](https://crowdin.com/project/gogs)) - Multi-language support ([15 languages](https://crowdin.com/project/gogs))
## System Requirements ## System Requirements
@ -96,6 +95,7 @@ There are 5 ways to install Gogs:
- [Portal](https://portaldemo.xyz/cloud/) - [Portal](https://portaldemo.xyz/cloud/)
- [Sandstorm](https://github.com/cem/gogs-sandstorm) - [Sandstorm](https://github.com/cem/gogs-sandstorm)
- [sloppy.io](https://github.com/sloppyio/quickstarters/tree/master/gogs) - [sloppy.io](https://github.com/sloppyio/quickstarters/tree/master/gogs)
- [YunoHost](https://github.com/mbugeia/gogs_ynh)
## Software and Service Support ## Software and Service Support
@ -105,6 +105,7 @@ There are 5 ways to install Gogs:
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs) (IT) - [Puppet](https://forge.puppetlabs.com/Siteminds/gogs) (IT)
- [Kanboard](http://kanboard.net/plugin/gogs-webhook) (Project Management) - [Kanboard](http://kanboard.net/plugin/gogs-webhook) (Project Management)
- [BearyChat](https://bearychat.com/) (Team Communication) - [BearyChat](https://bearychat.com/) (Team Communication)
- [HiWork](http://www.hiwork.cc/) (Team Communication)
### Product Support ### Product Support
@ -114,11 +115,11 @@ There are 5 ways to install Gogs:
## Acknowledgments ## Acknowledgments
- Router and middleware mechanism of [Macaron](https://github.com/go-macaron/macaron). - Router and middleware mechanism of [Macaron](https://github.com/go-macaron/macaron).
- Modules design is inspired by [WeTalk](https://github.com/beego/wetalk).
- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog). - System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
- Thanks [lavachen](http://www.lavachen.cn/) and [Rocker](http://weibo.com/rocker1989) for designing Logo. - Thanks [lavachen](http://www.lavachen.cn/) and [Rocker](http://weibo.com/rocker1989) for designing Logo.
- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan. - Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
- Thanks [DigitalOcean](https://www.digitalocean.com) for hosting home and demo sites. - Thanks [DigitalOcean](https://www.digitalocean.com) for hosting home and demo sites.
- Thanks [KeyCDN](https://www.keycdn.com/) for providing CDN service.
## Contributors ## Contributors

8
README_ZH.md

@ -12,7 +12,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
- 有关基本用法和变更日志,请通过 [使用手册](http://gogs.io/docs/intro/) 查看。 - 有关基本用法和变更日志,请通过 [使用手册](http://gogs.io/docs/intro/) 查看。
- 您可以到 [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。 - 您可以到 [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。
- 想要先睹为快?直接去 [在线体验](https://try.gogs.io/gogs/gogs) 。 - 想要先睹为快?直接去 [在线体验](https://try.gogs.io/gogs/gogs) 。
- 使用过程中遇到问题?尝试从 [故障排查](http://gogs.io/docs/intro/troubleshooting.html) 页面获取帮助。 - 使用过程中遇到问题?尝试从 [故障排查](http://gogs.io/docs/intro/troubleshooting.html) 页面或 [用户论坛](https://discuss.gogs.io/) 获取帮助。
- 希望帮助多国语言界面的翻译吗?请立即访问 [详情页面](http://gogs.io/docs/features/i18n.html)! - 希望帮助多国语言界面的翻译吗?请立即访问 [详情页面](http://gogs.io/docs/features/i18n.html)!
## 功能特性 ## 功能特性
@ -30,7 +30,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
- 支持邮件服务 - 支持邮件服务
- 支持后台管理面板 - 支持后台管理面板
- 支持 MySQL、PostgreSQL、SQLite3 和 [TiDB](https://github.com/pingcap/tidb)(实验性支持) 数据库 - 支持 MySQL、PostgreSQL、SQLite3 和 [TiDB](https://github.com/pingcap/tidb)(实验性支持) 数据库
- 支持多语言本地化([14 种语言]([more](https://crowdin.com/project/gogs))) - 支持多语言本地化([15 种语言]([more](https://crowdin.com/project/gogs)))
## 系统要求 ## 系统要求
@ -67,6 +67,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
- [Portal](https://portaldemo.xyz/cloud/) - [Portal](https://portaldemo.xyz/cloud/)
- [Sandstorm](https://github.com/cem/gogs-sandstorm) - [Sandstorm](https://github.com/cem/gogs-sandstorm)
- [sloppy.io](https://github.com/sloppyio/quickstarters/tree/master/gogs) - [sloppy.io](https://github.com/sloppyio/quickstarters/tree/master/gogs)
- [YunoHost](https://github.com/mbugeia/gogs_ynh)
## 软件及服务支持 ## 软件及服务支持
@ -76,6 +77,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs)(IT) - [Puppet](https://forge.puppetlabs.com/Siteminds/gogs)(IT)
- [Kanboard](http://kanboard.net/plugin/gogs-webhook)(项目管理) - [Kanboard](http://kanboard.net/plugin/gogs-webhook)(项目管理)
- [BearyChat](https://bearychat.com/)(团队交流) - [BearyChat](https://bearychat.com/)(团队交流)
- [HiWork](http://www.hiwork.cc/)(团队交流)
### 产品支持 ### 产品支持
@ -85,11 +87,11 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
## 特别鸣谢 ## 特别鸣谢
- 基于 [Macaron](https://github.com/go-macaron/macaron) 的路由与中间件机制。 - 基于 [Macaron](https://github.com/go-macaron/macaron) 的路由与中间件机制。
- 基于 [WeTalk](https://github.com/beego/wetalk) 修改的模块设计。
- 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。 - 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。
- 感谢 [lavachen](http://www.lavachen.cn/) 和 [Rocker](http://weibo.com/rocker1989) 设计的 Logo。 - 感谢 [lavachen](http://www.lavachen.cn/) 和 [Rocker](http://weibo.com/rocker1989) 设计的 Logo。
- 感谢 [Crowdin](https://crowdin.com/project/gogs) 提供免费的开源项目本地化支持。 - 感谢 [Crowdin](https://crowdin.com/project/gogs) 提供免费的开源项目本地化支持。
- 感谢 [DigitalOcean](https://www.digitalocean.com) 提供主站和体验站点的服务器赞助。 - 感谢 [DigitalOcean](https://www.digitalocean.com) 提供主站和体验站点的服务器赞助。
- 感谢 [KeyCDN](https://www.keycdn.com/) 提供 CDN 服务赞助。
## 贡献成员 ## 贡献成员

26
cmd/serve.go

@ -15,13 +15,13 @@ import (
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
gouuid "github.com/satori/go.uuid"
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/httplib" "github.com/gogits/gogs/modules/httplib"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/uuid"
) )
const ( const (
@ -42,11 +42,6 @@ func setup(logPath string) {
setting.NewContext() setting.NewContext()
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath)) log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
if setting.DisableSSH {
println("Gogs: SSH has been disabled")
os.Exit(1)
}
models.LoadConfigs() models.LoadConfigs()
if setting.UseSQLite3 || setting.UseTiDB { if setting.UseSQLite3 || setting.UseTiDB {
@ -104,8 +99,15 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
return return
} }
if err = models.Update(task.RefName, task.OldCommitID, task.NewCommitID, if err = models.PushUpdate(models.PushUpdateOptions{
user.Name, repoUser.Name, reponame, user.Id); err != nil { RefName: task.RefName,
OldCommitID: task.OldCommitID,
NewCommitID: task.NewCommitID,
PusherID: user.Id,
PusherName: user.Name,
RepoUserName: repoUser.Name,
RepoName: reponame,
}); err != nil {
log.GitLogger.Error(2, "Update: %v", err) log.GitLogger.Error(2, "Update: %v", err)
} }
@ -131,8 +133,14 @@ func runServ(c *cli.Context) {
if c.IsSet("config") { if c.IsSet("config") {
setting.CustomConf = c.String("config") setting.CustomConf = c.String("config")
} }
setup("serv.log") setup("serv.log")
if setting.SSH.Disabled {
println("Gogs: SSH has been disabled")
return
}
if len(c.Args()) < 1 { if len(c.Args()) < 1 {
fail("Not enough arguments", "Not enough arguments") fail("Not enough arguments", "Not enough arguments")
} }
@ -243,7 +251,7 @@ func runServ(c *cli.Context) {
} }
} }
uuid := uuid.NewV4().String() uuid := gouuid.NewV4().String()
os.Setenv("uuid", uuid) os.Setenv("uuid", uuid)
// Special handle for Windows. // Special handle for Windows.

17
cmd/update.go

@ -16,7 +16,7 @@ import (
var CmdUpdate = cli.Command{ var CmdUpdate = cli.Command{
Name: "update", Name: "update",
Usage: "This command should only be called by SSH shell", Usage: "This command should only be called by Git hook",
Description: `Update get pushed info and insert into database`, Description: `Update get pushed info and insert into database`,
Action: runUpdate, Action: runUpdate,
Flags: []cli.Flag{ Flags: []cli.Flag{
@ -28,18 +28,19 @@ func runUpdate(c *cli.Context) {
if c.IsSet("config") { if c.IsSet("config") {
setting.CustomConf = c.String("config") setting.CustomConf = c.String("config")
} }
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
if cmd == "" {
return
}
setup("update.log") setup("update.log")
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
log.GitLogger.Trace("SSH_ORIGINAL_COMMAND is empty")
return
}
args := c.Args() args := c.Args()
if len(args) != 3 { if len(args) != 3 {
log.GitLogger.Fatal(2, "received less 3 parameters") log.GitLogger.Fatal(2, "Arguments received are not equal to three")
} else if args[0] == "" { } else if len(args[0]) == 0 {
log.GitLogger.Fatal(2, "refName is empty, shouldn't use") log.GitLogger.Fatal(2, "First argument 'refName' is empty, shouldn't use")
} }
task := models.UpdateTask{ task := models.UpdateTask{

137
cmd/web.go

@ -7,7 +7,6 @@ package cmd
import ( import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
gotmpl "html/template"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/fcgi" "net/http/fcgi"
@ -34,10 +33,9 @@ import (
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/avatar"
"github.com/gogits/gogs/modules/bindata" "github.com/gogits/gogs/modules/bindata"
"github.com/gogits/gogs/modules/context"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/middleware"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/template" "github.com/gogits/gogs/modules/template"
"github.com/gogits/gogs/routers" "github.com/gogits/gogs/routers"
@ -80,17 +78,17 @@ func checkVersion() {
// Check dependency version. // Check dependency version.
checkers := []VerChecker{ checkers := []VerChecker{
{"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.4.4.1029"}, {"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.5.2.0304"},
{"github.com/go-macaron/binding", binding.Version, "0.1.0"}, {"github.com/go-macaron/binding", binding.Version, "0.2.1"},
{"github.com/go-macaron/cache", cache.Version, "0.1.2"}, {"github.com/go-macaron/cache", cache.Version, "0.1.2"},
{"github.com/go-macaron/csrf", csrf.Version, "0.0.3"}, {"github.com/go-macaron/csrf", csrf.Version, "0.1.0"},
{"github.com/go-macaron/i18n", i18n.Version, "0.2.0"}, {"github.com/go-macaron/i18n", i18n.Version, "0.2.0"},
{"github.com/go-macaron/session", session.Version, "0.1.6"}, {"github.com/go-macaron/session", session.Version, "0.1.6"},
{"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"}, {"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"},
{"gopkg.in/ini.v1", ini.Version, "1.8.4"}, {"gopkg.in/ini.v1", ini.Version, "1.8.4"},
{"gopkg.in/macaron.v1", macaron.Version, "0.8.0"}, {"gopkg.in/macaron.v1", macaron.Version, "1.1.2"},
{"github.com/gogits/git-module", git.Version, "0.2.3"}, {"github.com/gogits/git-module", git.Version, "0.2.9"},
{"github.com/gogits/go-gogs-client", gogs.Version, "0.7.2"}, {"github.com/gogits/go-gogs-client", gogs.Version, "0.7.4"},
} }
for _, c := range checkers { for _, c := range checkers {
if !version.Compare(c.Version(), c.Expected, ">=") { if !version.Compare(c.Version(), c.Expected, ">=") {
@ -126,9 +124,10 @@ func newMacaron() *macaron.Macaron {
}, },
)) ))
m.Use(macaron.Renderer(macaron.RenderOptions{ m.Use(macaron.Renderer(macaron.RenderOptions{
Directory: path.Join(setting.StaticRootPath, "templates"), Directory: path.Join(setting.StaticRootPath, "templates"),
Funcs: []gotmpl.FuncMap{template.Funcs}, AppendDirectories: []string{path.Join(setting.CustomPath, "templates")},
IndentJSON: macaron.Env != macaron.PROD, Funcs: template.NewFuncMap(),
IndentJSON: macaron.Env != macaron.PROD,
})) }))
localeNames, err := bindata.AssetDir("conf/locale") localeNames, err := bindata.AssetDir("conf/locale")
@ -159,6 +158,7 @@ func newMacaron() *macaron.Macaron {
m.Use(session.Sessioner(setting.SessionConfig)) m.Use(session.Sessioner(setting.SessionConfig))
m.Use(csrf.Csrfer(csrf.Options{ m.Use(csrf.Csrfer(csrf.Options{
Secret: setting.SecretKey, Secret: setting.SecretKey,
Cookie: setting.CSRFCookieName,
SetCookie: true, SetCookie: true,
Header: "X-Csrf-Token", Header: "X-Csrf-Token",
CookiePath: setting.AppSubUrl, CookiePath: setting.AppSubUrl,
@ -171,7 +171,7 @@ func newMacaron() *macaron.Macaron {
}, },
}, },
})) }))
m.Use(middleware.Contexter()) m.Use(context.Contexter())
return m return m
} }
@ -184,26 +184,28 @@ func runWeb(ctx *cli.Context) {
m := newMacaron() m := newMacaron()
reqSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true}) reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true})
ignSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: setting.Service.RequireSignInView}) ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView})
ignSignInAndCsrf := middleware.Toggle(&middleware.ToggleOptions{DisableCsrf: true}) ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true})
reqSignOut := middleware.Toggle(&middleware.ToggleOptions{SignOutRequire: true}) reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true})
bindIgnErr := binding.BindIgnErr bindIgnErr := binding.BindIgnErr
// FIXME: not all routes need go through same middlewares.
// Especially some AJAX requests, we can reduce middleware number to improve performance.
// Routers. // Routers.
m.Get("/", ignSignIn, routers.Home) m.Get("/", ignSignIn, routers.Home)
m.Get("/explore", ignSignIn, routers.Explore) m.Group("/explore", func() {
m.Get("", func(ctx *context.Context) {
ctx.Redirect(setting.AppSubUrl + "/explore/repos")
})
m.Get("/repos", routers.ExploreRepos)
m.Get("/users", routers.ExploreUsers)
}, ignSignIn)
m.Combo("/install", routers.InstallInit).Get(routers.Install). m.Combo("/install", routers.InstallInit).Get(routers.Install).
Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost)
m.Get("/^:type(issues|pulls)$", reqSignIn, user.Issues) m.Get("/^:type(issues|pulls)$", reqSignIn, user.Issues)
// ***** START: API *****
m.Group("/api", func() {
apiv1.RegisterRoutes(m)
}, ignSignIn)
// ***** END: API *****
// ***** START: User ***** // ***** START: User *****
m.Group("/user", func() { m.Group("/user", func() {
m.Get("/login", user.SignIn) m.Get("/login", user.SignIn)
@ -218,6 +220,7 @@ func runWeb(ctx *cli.Context) {
m.Get("", user.Settings) m.Get("", user.Settings)
m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost) m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost)
m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), user.SettingsAvatar) m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), user.SettingsAvatar)
m.Post("/avatar/delete", user.SettingsDeleteAvatar)
m.Combo("/email").Get(user.SettingsEmails). m.Combo("/email").Get(user.SettingsEmails).
Post(bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost) Post(bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost)
m.Post("/email/delete", user.DeleteEmail) m.Post("/email/delete", user.DeleteEmail)
@ -230,7 +233,7 @@ func runWeb(ctx *cli.Context) {
Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost) Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost)
m.Post("/applications/delete", user.SettingsDeleteApplication) m.Post("/applications/delete", user.SettingsDeleteApplication)
m.Route("/delete", "GET,POST", user.SettingsDelete) m.Route("/delete", "GET,POST", user.SettingsDelete)
}, reqSignIn, func(ctx *middleware.Context) { }, reqSignIn, func(ctx *context.Context) {
ctx.Data["PageIsUserSettings"] = true ctx.Data["PageIsUserSettings"] = true
}) })
@ -245,17 +248,13 @@ func runWeb(ctx *cli.Context) {
}) })
// ***** END: User ***** // ***** END: User *****
// Gravatar service. adminReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, AdminRequired: true})
avt := avatar.CacheServer("public/img/avatar/", "public/img/avatar_default.jpg")
os.MkdirAll("public/img/avatar/", os.ModePerm)
m.Get("/avatar/:hash", avt.ServeHTTP)
adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true})
// ***** START: Admin ***** // ***** START: Admin *****
m.Group("/admin", func() { m.Group("/admin", func() {
m.Get("", adminReq, admin.Dashboard) m.Get("", adminReq, admin.Dashboard)
m.Get("/config", admin.Config) m.Get("/config", admin.Config)
m.Post("/config/test_mail", admin.SendTestMail)
m.Get("/monitor", admin.Monitor) m.Get("/monitor", admin.Monitor)
m.Group("/users", func() { m.Group("/users", func() {
@ -298,7 +297,7 @@ func runWeb(ctx *cli.Context) {
m.Get("/stars", user.Stars) m.Get("/stars", user.Stars)
}) })
m.Get("/attachments/:uuid", func(ctx *middleware.Context) { m.Get("/attachments/:uuid", func(ctx *context.Context) {
attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid")) attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid"))
if err != nil { if err != nil {
if models.IsErrAttachmentNotExist(err) { if models.IsErrAttachmentNotExist(err) {
@ -335,8 +334,8 @@ func runWeb(ctx *cli.Context) {
m.Get("/template/*", dev.TemplatePreview) m.Get("/template/*", dev.TemplatePreview)
} }
reqRepoAdmin := middleware.RequireRepoAdmin() reqRepoAdmin := context.RequireRepoAdmin()
reqRepoPusher := middleware.RequireRepoPusher() reqRepoWriter := context.RequireRepoWriter()
// ***** START: Organization ***** // ***** START: Organization *****
m.Group("/org", func() { m.Group("/org", func() {
@ -350,11 +349,14 @@ func runWeb(ctx *cli.Context) {
m.Get("/members/action/:action", org.MembersAction) m.Get("/members/action/:action", org.MembersAction)
m.Get("/teams", org.Teams) m.Get("/teams", org.Teams)
}, context.OrgAssignment(true))
m.Group("/:org", func() {
m.Get("/teams/:team", org.TeamMembers) m.Get("/teams/:team", org.TeamMembers)
m.Get("/teams/:team/repositories", org.TeamRepositories) m.Get("/teams/:team/repositories", org.TeamRepositories)
m.Route("/teams/:team/action/:action", "GET,POST", org.TeamsAction) m.Route("/teams/:team/action/:action", "GET,POST", org.TeamsAction)
m.Route("/teams/:team/action/repo/:action", "GET,POST", org.TeamsRepoAction) m.Route("/teams/:team/action/repo/:action", "GET,POST", org.TeamsRepoAction)
}, middleware.OrgAssignment(true)) }, context.OrgAssignment(true, false, true))
m.Group("/:org", func() { m.Group("/:org", func() {
m.Get("/teams/new", org.NewTeam) m.Get("/teams/new", org.NewTeam)
@ -367,6 +369,7 @@ func runWeb(ctx *cli.Context) {
m.Combo("").Get(org.Settings). m.Combo("").Get(org.Settings).
Post(bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost) Post(bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost)
m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), org.SettingsAvatar) m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), org.SettingsAvatar)
m.Post("/avatar/delete", org.SettingsDeleteAvatar)
m.Group("/hooks", func() { m.Group("/hooks", func() {
m.Get("", org.Webhooks) m.Get("", org.Webhooks)
@ -383,7 +386,7 @@ func runWeb(ctx *cli.Context) {
}) })
m.Route("/invitations/new", "GET,POST", org.Invitation) m.Route("/invitations/new", "GET,POST", org.Invitation)
}, middleware.OrgAssignment(true, true)) }, context.OrgAssignment(true, true))
}, reqSignIn) }, reqSignIn)
// ***** END: Organization ***** // ***** END: Organization *****
@ -401,7 +404,11 @@ func runWeb(ctx *cli.Context) {
m.Group("/settings", func() { m.Group("/settings", func() {
m.Combo("").Get(repo.Settings). m.Combo("").Get(repo.Settings).
Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost) Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
m.Route("/collaboration", "GET,POST", repo.Collaboration) m.Group("/collaboration", func() {
m.Combo("").Get(repo.Collaboration).Post(repo.CollaborationPost)
m.Post("/access_mode", repo.ChangeCollaborationAccessMode)
m.Post("/delete", repo.DeleteCollaboration)
})
m.Group("/hooks", func() { m.Group("/hooks", func() {
m.Get("", repo.Webhooks) m.Get("", repo.Webhooks)
@ -418,7 +425,7 @@ func runWeb(ctx *cli.Context) {
m.Get("", repo.GitHooks) m.Get("", repo.GitHooks)
m.Combo("/:name").Get(repo.GitHooksEdit). m.Combo("/:name").Get(repo.GitHooksEdit).
Post(repo.GitHooksEditPost) Post(repo.GitHooksEditPost)
}, middleware.GitHookService()) }, context.GitHookService())
}) })
m.Group("/keys", func() { m.Group("/keys", func() {
@ -427,15 +434,15 @@ func runWeb(ctx *cli.Context) {
m.Post("/delete", repo.DeleteDeployKey) m.Post("/delete", repo.DeleteDeployKey)
}) })
}, func(ctx *middleware.Context) { }, func(ctx *context.Context) {
ctx.Data["PageIsSettings"] = true ctx.Data["PageIsSettings"] = true
}) })
}, reqSignIn, middleware.RepoAssignment(), reqRepoAdmin, middleware.RepoRef()) }, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.RepoRef())
m.Get("/:username/:reponame/action/:action", reqSignIn, middleware.RepoAssignment(), repo.Action) m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action)
m.Group("/:username/:reponame", func() { m.Group("/:username/:reponame", func() {
m.Group("/issues", func() { m.Group("/issues", func() {
m.Combo("/new", repo.MustEnableIssues).Get(middleware.RepoRef(), repo.NewIssue). m.Combo("/new", repo.MustEnableIssues).Get(context.RepoRef(), repo.NewIssue).
Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost) Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost)
m.Combo("/:index/comments").Post(bindIgnErr(auth.CreateCommentForm{}), repo.NewComment) m.Combo("/:index/comments").Post(bindIgnErr(auth.CreateCommentForm{}), repo.NewComment)
@ -443,7 +450,7 @@ func runWeb(ctx *cli.Context) {
m.Post("/label", repo.UpdateIssueLabel) m.Post("/label", repo.UpdateIssueLabel)
m.Post("/milestone", repo.UpdateIssueMilestone) m.Post("/milestone", repo.UpdateIssueMilestone)
m.Post("/assignee", repo.UpdateIssueAssignee) m.Post("/assignee", repo.UpdateIssueAssignee)
}, reqRepoAdmin) }, reqRepoWriter)
m.Group("/:index", func() { m.Group("/:index", func() {
m.Post("/title", repo.UpdateIssueTitle) m.Post("/title", repo.UpdateIssueTitle)
@ -455,7 +462,7 @@ func runWeb(ctx *cli.Context) {
m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel) m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel) m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
m.Post("/delete", repo.DeleteLabel) m.Post("/delete", repo.DeleteLabel)
}, reqRepoAdmin, middleware.RepoRef()) }, reqRepoWriter, context.RepoRef())
m.Group("/milestones", func() { m.Group("/milestones", func() {
m.Combo("/new").Get(repo.NewMilestone). m.Combo("/new").Get(repo.NewMilestone).
Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost) Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
@ -463,7 +470,7 @@ func runWeb(ctx *cli.Context) {
m.Post("/:id/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost) m.Post("/:id/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost)
m.Get("/:id/:action", repo.ChangeMilestonStatus) m.Get("/:id/:action", repo.ChangeMilestonStatus)
m.Post("/delete", repo.DeleteMilestone) m.Post("/delete", repo.DeleteMilestone)
}, reqRepoAdmin, middleware.RepoRef()) }, reqRepoWriter, context.RepoRef())
m.Group("/releases", func() { m.Group("/releases", func() {
m.Get("/new", repo.NewRelease) m.Get("/new", repo.NewRelease)
@ -471,11 +478,11 @@ func runWeb(ctx *cli.Context) {
m.Get("/edit/:tagname", repo.EditRelease) m.Get("/edit/:tagname", repo.EditRelease)
m.Post("/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost) m.Post("/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
m.Post("/delete", repo.DeleteRelease) m.Post("/delete", repo.DeleteRelease)
}, reqRepoAdmin, middleware.RepoRef()) }, reqRepoWriter, context.RepoRef())
m.Combo("/compare/*", repo.MustEnablePulls).Get(repo.CompareAndPullRequest). m.Combo("/compare/*", repo.MustAllowPulls).Get(repo.CompareAndPullRequest).
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost) Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
}, reqSignIn, middleware.RepoAssignment(), repo.MustBeNotBare) }, reqSignIn, context.RepoAssignment(), repo.MustBeNotBare)
m.Group("/:username/:reponame", func() { m.Group("/:username/:reponame", func() {
m.Group("", func() { m.Group("", func() {
@ -484,7 +491,7 @@ func runWeb(ctx *cli.Context) {
m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue) m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue)
m.Get("/labels/", repo.RetrieveLabels, repo.Labels) m.Get("/labels/", repo.RetrieveLabels, repo.Labels)
m.Get("/milestones", repo.Milestones) m.Get("/milestones", repo.Milestones)
}, middleware.RepoRef()) }, context.RepoRef())
// m.Get("/branches", repo.Branches) // m.Get("/branches", repo.Branches)
@ -497,37 +504,39 @@ func runWeb(ctx *cli.Context) {
Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost) Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost)
m.Combo("/:page/_edit").Get(repo.EditWiki). m.Combo("/:page/_edit").Get(repo.EditWiki).
Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost) Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost)
}, reqSignIn, reqRepoPusher) m.Post("/:page/delete", repo.DeleteWikiPagePost)
}, repo.MustEnableWiki, middleware.RepoRef()) }, reqSignIn, reqRepoWriter)
}, repo.MustEnableWiki, context.RepoRef())
m.Get("/archive/*", repo.Download) m.Get("/archive/*", repo.Download)
m.Group("/pulls/:index", func() { m.Group("/pulls/:index", func() {
m.Get("/commits", middleware.RepoRef(), repo.ViewPullCommits) m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
m.Get("/files", middleware.RepoRef(), repo.ViewPullFiles) m.Get("/files", context.RepoRef(), repo.ViewPullFiles)
m.Post("/merge", reqRepoAdmin, repo.MergePullRequest) m.Post("/merge", reqRepoWriter, repo.MergePullRequest)
}, repo.MustEnablePulls) }, repo.MustAllowPulls)
m.Group("", func() { m.Group("", func() {
m.Get("/src/*", repo.Home) m.Get("/src/*", repo.Home)
m.Get("/raw/*", repo.SingleDownload) m.Get("/raw/*", repo.SingleDownload)
m.Get("/commits/*", repo.RefCommits) m.Get("/commits/*", repo.RefCommits)
m.Get("/commit/*", repo.Diff) m.Get("/commit/:sha([a-z0-9]{40})$", repo.Diff)
m.Get("/forks", repo.Forks) m.Get("/forks", repo.Forks)
}, middleware.RepoRef()) }, context.RepoRef())
m.Get("/commit/:sha([a-z0-9]{40})\\.:ext(patch|diff)", repo.RawDiff)
m.Get("/compare/:before([a-z0-9]{40})...:after([a-z0-9]{40})", repo.CompareDiff) m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.CompareDiff)
}, ignSignIn, middleware.RepoAssignment(), repo.MustBeNotBare) }, ignSignIn, context.RepoAssignment(), repo.MustBeNotBare)
m.Group("/:username/:reponame", func() { m.Group("/:username/:reponame", func() {
m.Get("/stars", repo.Stars) m.Get("/stars", repo.Stars)
m.Get("/watchers", repo.Watchers) m.Get("/watchers", repo.Watchers)
}, ignSignIn, middleware.RepoAssignment(), middleware.RepoRef()) }, ignSignIn, context.RepoAssignment(), context.RepoRef())
m.Group("/:username", func() { m.Group("/:username", func() {
m.Group("/:reponame", func() { m.Group("/:reponame", func() {
m.Get("", repo.Home) m.Get("", repo.Home)
m.Get("\\.git$", repo.Home) m.Get("\\.git$", repo.Home)
}, ignSignIn, middleware.RepoAssignment(true), middleware.RepoRef()) }, ignSignIn, context.RepoAssignment(true), context.RepoRef())
m.Group("/:reponame", func() { m.Group("/:reponame", func() {
m.Any("/*", ignSignInAndCsrf, repo.HTTP) m.Any("/*", ignSignInAndCsrf, repo.HTTP)
@ -536,8 +545,12 @@ func runWeb(ctx *cli.Context) {
}) })
// ***** END: Repository ***** // ***** END: Repository *****
m.Group("/api", func() {
apiv1.RegisterRoutes(m)
}, ignSignIn)
// robots.txt // robots.txt
m.Get("/robots.txt", func(ctx *middleware.Context) { m.Get("/robots.txt", func(ctx *context.Context) {
if setting.HasRobotsTxt { if setting.HasRobotsTxt {
ctx.ServeFileContent(path.Join(setting.CustomPath, "robots.txt")) ctx.ServeFileContent(path.Join(setting.CustomPath, "robots.txt"))
} else { } else {

6
conf/README.md

@ -1,7 +1,3 @@
Execute following command in ROOT directory when anything is changed: Execute following command in ROOT directory when anything is changed:
$ go-bindata -o=modules/bindata/bindata.go -ignore="\\.DS_Store|README.md" -pkg=bindata conf/... $ make bindata
Add -debug flag to make life easier in development(somehow isn't working):
$ go-bindata -debug -o=modules/bindata/bindata.go -ignore="\\.DS_Store|README.md" -pkg=bindata conf/...

48
conf/app.ini

@ -27,6 +27,10 @@ EXPLORE_PAGING_NUM = 20
ISSUE_PAGING_NUM = 10 ISSUE_PAGING_NUM = 10
; Number of maximum commits showed in one activity feed ; Number of maximum commits showed in one activity feed
FEED_MAX_COMMIT_NUM = 5 FEED_MAX_COMMIT_NUM = 5
; Value of `theme-color` meta tag, used by Android >= 5.0
; An invalid color like "none" or "disable" will have the default style
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
THEME_COLOR_META_TAG = `#ff5343`
[ui.admin] [ui.admin]
; Number of users that are showed in one page ; Number of users that are showed in one page
@ -41,6 +45,9 @@ ORG_PAGING_NUM = 50
[markdown] [markdown]
; Enable hard line break extension ; Enable hard line break extension
ENABLE_HARD_LINE_BREAK = false ENABLE_HARD_LINE_BREAK = false
; List of custom URL-Schemes that are allowed as links when rendering Markdown
; for example git,magnet
CUSTOM_URL_SCHEMES =
[server] [server]
PROTOCOL = http PROTOCOL = http
@ -56,9 +63,21 @@ LOCAL_ROOT_URL = http://localhost:%(HTTP_PORT)s/
DISABLE_SSH = false DISABLE_SSH = false
; Whether use builtin SSH server or not. ; Whether use builtin SSH server or not.
START_SSH_SERVER = false START_SSH_SERVER = false
; Domain name to be exposed in clone URL
SSH_DOMAIN = %(DOMAIN)s
; Port number to be exposed in clone URL
SSH_PORT = 22 SSH_PORT = 22
; Root path of SSH directory ; Port number builtin SSH server listens on
SSH_LISTEN_PORT = %(SSH_PORT)s
; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
SSH_ROOT_PATH = SSH_ROOT_PATH =
; Directory to create temporary files when test publick key using ssh-keygen,
; default is system temporary directory.
SSH_KEY_TEST_PATH =
; Path to ssh-keygen, default is 'ssh-keygen' and let shell find out which one to call.
SSH_KEYGEN_PATH = ssh-keygen
; Indicate whether to check minimum key size with corresponding type
MINIMUM_KEY_SIZE_CHECK = false
; Disable CDN even in "prod" mode ; Disable CDN even in "prod" mode
OFFLINE_MODE = false OFFLINE_MODE = false
DISABLE_ROUTER_LOG = false DISABLE_ROUTER_LOG = false
@ -74,11 +93,20 @@ KEY_FILE = custom/https/key.pem
; Upper level of template and static file path ; Upper level of template and static file path
; default is the path where Gogs is executed ; default is the path where Gogs is executed
STATIC_ROOT_PATH = STATIC_ROOT_PATH =
; Default path for App data
APP_DATA_PATH = data
; Application level GZIP support ; Application level GZIP support
ENABLE_GZIP = false ENABLE_GZIP = false
; Landing page for non-logged users, can be "home" or "explore" ; Landing page for non-logged users, can be "home" or "explore"
LANDING_PAGE = home LANDING_PAGE = home
; Define allowed algorithms and their minimum key length (use -1 to disable a type)
[ssh.minimum_key_sizes]
ED25519 = 256
ECDSA = 256
RSA = 2048
DSA = 1024
[database] [database]
; Either "mysql", "postgres" or "sqlite3", it's your choice ; Either "mysql", "postgres" or "sqlite3", it's your choice
DB_TYPE = mysql DB_TYPE = mysql
@ -88,7 +116,7 @@ USER = root
PASSWD = PASSWD =
; For "postgres" only, either "disable", "require" or "verify-full" ; For "postgres" only, either "disable", "require" or "verify-full"
SSL_MODE = disable SSL_MODE = disable
; For "sqlite3" and "tidb" ; For "sqlite3" and "tidb", use absolute path when you start as service
PATH = data/gogs.db PATH = data/gogs.db
[admin] [admin]
@ -113,8 +141,6 @@ REGISTER_EMAIL_CONFIRM = false
DISABLE_REGISTRATION = false DISABLE_REGISTRATION = false
; User must sign in to view anything. ; User must sign in to view anything.
REQUIRE_SIGNIN_VIEW = false REQUIRE_SIGNIN_VIEW = false
; Cache avatar as picture
ENABLE_CACHE_AVATAR = false
; Mail notification ; Mail notification
ENABLE_NOTIFY_MAIL = false ENABLE_NOTIFY_MAIL = false
; More detail: https://github.com/gogits/gogs/issues/165 ; More detail: https://github.com/gogits/gogs/issues/165
@ -191,8 +217,6 @@ GC_INTERVAL_TIME = 86400
SESSION_LIFE_TIME = 86400 SESSION_LIFE_TIME = 86400
[picture] [picture]
; The place to picture data, either "server" or "qiniu", default is "server"
SERVICE = server
AVATAR_UPLOAD_PATH = data/avatars AVATAR_UPLOAD_PATH = data/avatars
; Chinese users can choose "duoshuo" ; Chinese users can choose "duoshuo"
; or a custom avatar source, like: http://cn.gravatar.com/avatar/ ; or a custom avatar source, like: http://cn.gravatar.com/avatar/
@ -307,9 +331,16 @@ MAX_GIT_DIFF_LINES = 10000
; see more on http://git-scm.com/docs/git-gc/1.7.5 ; see more on http://git-scm.com/docs/git-gc/1.7.5
GC_ARGS = GC_ARGS =
; Operation timeout in seconds
[git.timeout]
MIGRATE = 600
MIRROR = 300
CLONE = 300
PULL = 300
[i18n] [i18n]
LANGS = en-US,zh-CN,zh-HK,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT LANGS = en-US,zh-CN,zh-HK,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI
NAMES = English,简体中文,繁體中文,Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,Polski,български,Italiano NAMES = English,简体中文,繁體中文,Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,Polski,български,Italiano,Suomalainen
; Used for datetimepicker ; Used for datetimepicker
[i18n.datelang] [i18n.datelang]
@ -327,6 +358,7 @@ pt-BR = pt-BR
pl-PL = pl pl-PL = pl
bg-BG = bg bg-BG = bg
it-IT = it it-IT = it
fi-FI = fi
; Extension mapping to highlight class ; Extension mapping to highlight class
; e.g. .toml=ini ; e.g. .toml=ini

14
conf/locale/TRANSLATORS

@ -12,32 +12,46 @@ Andrey Nering <andrey AT nering DOT com DOT br>
Andrey Solomatin <toadron AT yandex DOT ru> Andrey Solomatin <toadron AT yandex DOT ru>
Antoine GIRARD <sapk AT sapk DOT fr> Antoine GIRARD <sapk AT sapk DOT fr>
Arthur Aslanyan <arthur DOT e DOT aslanyan AT gmail DOT com> Arthur Aslanyan <arthur DOT e DOT aslanyan AT gmail DOT com>
Aurelien Darragon <aurelien DOT darragon AT gmail DOT com>
Barış Arda Yılmaz <ardayilmazgamer AT gmail DOT com> Barış Arda Yılmaz <ardayilmazgamer AT gmail DOT com>
Camille Baronnet <gogs AT camillebaronnet DOT fr>
Christoph Kisfeld <christoph DOT kisfeld AT gmail DOT com> Christoph Kisfeld <christoph DOT kisfeld AT gmail DOT com>
Cysioland Cysioland
Daniel Speichert <daniel AT speichert DOT pl> Daniel Speichert <daniel AT speichert DOT pl>
David Yzaguirre <dvdyzag AT gmail DOT com> David Yzaguirre <dvdyzag AT gmail DOT com>
Dmitriy Nogay <me AT catwhocode DOT ga> Dmitriy Nogay <me AT catwhocode DOT ga>
Enrico Testori hypertesto AT gmail DOT com
Ezequiel Gonzalez Rial <gonrial AT gmail DOT com> Ezequiel Gonzalez Rial <gonrial AT gmail DOT com>
Gregor Santner <gdev AT live DOT de> Gregor Santner <gdev AT live DOT de>
Hamid Feizabadi <hamidfzm AT gmail DOT com> Hamid Feizabadi <hamidfzm AT gmail DOT com>
Huimin Wang <wanghm2009 AT hotmail DOT co DOT jp> Huimin Wang <wanghm2009 AT hotmail DOT co DOT jp>
ilko <kontact-mr.k AT outlook DOT com"> ilko <kontact-mr.k AT outlook DOT com">
Ilya Makarov Ilya Makarov
Robert Nuske <robert DOT nuske AT web DOT de>
Robin Hübner <profan AT prfn DOT se>
Jamie Mansfield <dev AT jamierocks DOT uk>
Jean THOMAS <contact AT tibounise DOT com>
Juraj Bubniak <contact AT jbub DOT eu> Juraj Bubniak <contact AT jbub DOT eu>
Lafriks <lafriks AT gmail DOT com> Lafriks <lafriks AT gmail DOT com>
Lauri Ojansivu <x AT xet7 DOT org> Lauri Ojansivu <x AT xet7 DOT org>
Luc Stepniewski <luc AT stepniewski DOT fr> Luc Stepniewski <luc AT stepniewski DOT fr>
Luca Bozzo <luca AT bozzo DOT it>
Luca Kröger <l DOT kroeger01 AT gmail DOT com>
Marc Schiller <marc AT schiller DOT im> Marc Schiller <marc AT schiller DOT im>
Marvin Menzerath <github AT marvin-menzerath DOT de>
Michael Härtl <haertl DOT mike AT gmail DOT com>
Miguel de la Cruz <miguel AT mcrx DOT me> Miguel de la Cruz <miguel AT mcrx DOT me>
Mikhail Burdin <xdshot9000 AT gmail DOT com> Mikhail Burdin <xdshot9000 AT gmail DOT com>
Morten Sørensen <klim8d AT gmail DOT com> Morten Sørensen <klim8d AT gmail DOT com>
Muhammad Fawwaz Orabi <mfawwaz93 AT gmail DOT com>
Nakao Takamasa <at.mattenn AT gmail DOT com> Nakao Takamasa <at.mattenn AT gmail DOT com>
Natan Albuquerque <natanalbuquerque5 AT gmail DOT com> Natan Albuquerque <natanalbuquerque5 AT gmail DOT com>
Odilon Junior <odilon DOT junior93 AT gmail DOT com> Odilon Junior <odilon DOT junior93 AT gmail DOT com>
SeongJae Park <sj38 DOT park AT gmail DOT com>
Thomas Fanninger <gogs DOT thomas AT fanninger DOT at> Thomas Fanninger <gogs DOT thomas AT fanninger DOT at>
Tilmann Bach <tilmann AT outlook DOT com> Tilmann Bach <tilmann AT outlook DOT com>
Toni Villena Jiménez <tonivj5 AT gmail DOT com> Toni Villena Jiménez <tonivj5 AT gmail DOT com>
Vladimir Jigulin mogaika AT yandex DOT ru
Vladimir Vissoultchev <wqweto AT gmail DOT com> Vladimir Vissoultchev <wqweto AT gmail DOT com>
YJSoft <yjsoft AT yjsoft DOT pe DOT kr> YJSoft <yjsoft AT yjsoft DOT pe DOT kr>
Łukasz Jan Niemier <lukasz AT niemier DOT pl> Łukasz Jan Niemier <lukasz AT niemier DOT pl>

71
conf/locale/locale_bg-BG.ini

@ -38,19 +38,12 @@ settings=Настройки
your_profile=Вашият профил your_profile=Вашият профил
your_settings=Вашите настройки your_settings=Вашите настройки
news_feed=Поток новини activities=Активности
pull_requests=Заявки за сливане pull_requests=Заявки за сливане
issues=Задачи issues=Задачи
cancel=Отказ cancel=Отказ
[search]
search=Търсене...
repository=Хранилище
user=Потребител
issue=Задача
code=Код
[install] [install]
install=Инсталация install=Инсталация
title=Стъпки за инсталиране при първоначално стартиране title=Стъпки за инсталиране при първоначално стартиране
@ -65,7 +58,7 @@ db_name=Име на база данни
db_helper=Моля, използвайте INNODB engine с utf8_general_ci кодиране на знаци за MySQL. db_helper=Моля, използвайте INNODB engine с utf8_general_ci кодиране на знаци за MySQL.
ssl_mode=Режим SSL ssl_mode=Режим SSL
path=Път path=Път
sqlite_helper=Файл на SQLite3 или TiDB база данни. sqlite_helper=Файл на SQLite3 или TiDB база данни.<br>Моля използвайте абсолютен път до файл когато стартирате Gogs като услуга.
err_empty_db_path=Пътят до SQLite3 или TiDB база данни не може да е празен. err_empty_db_path=Пътят до SQLite3 или TiDB база данни не може да е празен.
err_invalid_tidb_name=TiDB не позволява "." и "-" в името на базата данни. err_invalid_tidb_name=TiDB не позволява "." и "-" в името на базата данни.
no_admin_and_disable_registration=Невъзможно изключване на регистрациите без предварително да е създаден поне един административен профил. no_admin_and_disable_registration=Невъзможно изключване на регистрациите без предварително да е създаден поне един административен профил.
@ -86,6 +79,8 @@ http_port=HTTP порт
http_port_helper=Порт, на който приложението ще слуша. http_port_helper=Порт, на който приложението ще слуша.
app_url=URL адрес на приложението app_url=URL адрес на приложението
app_url_helper=Този настройка променя HTTP/HTTPS адреса за клониране, а понякога и адреса на ел. поща. app_url_helper=Този настройка променя HTTP/HTTPS адреса за клониране, а понякога и адреса на ел. поща.
log_root_path=Път към журналите
log_root_path_helper=Директория в която се записват журналите.
optional_title=Опционални настройки optional_title=Опционални настройки
email_title=Настройки на пощенска услуга email_title=Настройки на пощенска услуга
@ -122,6 +117,7 @@ run_user_not_match=Потребителският контекст на прил
save_config_failed=Неуспешно запазване на конфигурация: %v save_config_failed=Неуспешно запазване на конфигурация: %v
invalid_admin_setting=Настройките на профил на администратора са невалидни: %v invalid_admin_setting=Настройките на профил на администратора са невалидни: %v
install_success=Добре дошли! Радваме се, че избрахте Gogs, и Ви пожелаваме приятна работа и сърдечни поздрави! install_success=Добре дошли! Радваме се, че избрахте Gogs, и Ви пожелаваме приятна работа и сърдечни поздрави!
invalid_log_root_path=Основният път към журналите е невалиден: %v
[home] [home]
uname_holder=Име или ел. поща uname_holder=Име или ел. поща
@ -137,6 +133,8 @@ issues.in_your_repos=Във Вашите хранилища
[explore] [explore]
repos=Хранилища repos=Хранилища
users=Потребители
search=Търсене
[auth] [auth]
create_new_account=Създай нов профил create_new_account=Създай нов профил
@ -203,7 +201,6 @@ repo_name_been_taken=Името на хранилището вече се пол
org_name_been_taken=Името на организацията вече се ползва. org_name_been_taken=Името на организацията вече се ползва.
team_name_been_taken=Името на екипа вече се ползва. team_name_been_taken=Името на екипа вече се ползва.
email_been_used=Този адрес на ел. поща вече се ползва. email_been_used=Този адрес на ел. поща вече се ползва.
illegal_team_name=Името на екипа съдържа недопустими знаци.
username_password_incorrect=Потребителското име или паролата не са верни. username_password_incorrect=Потребителското име или паролата не са верни.
enterred_invalid_repo_name=Моля уверете се, че въведеното име на хранилище е вярно. enterred_invalid_repo_name=Моля уверете се, че въведеното име на хранилище е вярно.
enterred_invalid_owner_name=Моля уверете се, че въведеното име на притежател е вярно. enterred_invalid_owner_name=Моля уверете се, че въведеното име на притежател е вярно.
@ -219,8 +216,6 @@ still_own_repo=Вашият профил притежава поне едно х
still_has_org=Вашият профил все още участва в поне една организация. Първо трябва да напуснете или изтриете Вашите участия в организациите. still_has_org=Вашият профил все още участва в поне една организация. Първо трябва да напуснете или изтриете Вашите участия в организациите.
org_still_own_repo=Тази организация все още притежава хранилище. Първо трябва да го изтриете или да го прехвърлите на друга организация. org_still_own_repo=Тази организация все още притежава хранилище. Първо трябва да го изтриете или да го прехвърлите на друга организация.
still_own_user=Това удостоверяване се използва от поне един потребител. Моля премахнете потребителите към него и опитайте отново.
target_branch_not_exist=Целевият клон не съществува. target_branch_not_exist=Целевият клон не съществува.
[user] [user]
@ -262,11 +257,10 @@ continue=Продължи
cancel=Отказ cancel=Отказ
enable_custom_avatar=Разреши потребителски аватар enable_custom_avatar=Разреши потребителски аватар
enable_custom_avatar_helper=Без зареждане от Gravatar
choose_new_avatar=Избор на нов аватар choose_new_avatar=Избор на нов аватар
update_avatar=Запази настройките на аватара update_avatar=Запази настройките на аватара
delete_current_avatar=Изтрий аватар
uploaded_avatar_not_a_image=Каченият файл не е изображение. uploaded_avatar_not_a_image=Каченият файл не е изображение.
no_custom_avatar_available=Невъзможно използване на външен аватар, защото не е активирано.
update_avatar_success=Настройките на аватара са запазени успешно. update_avatar_success=Настройките на аватара са запазени успешно.
change_password=Промяна на собствената парола change_password=Промяна на собствената парола
@ -477,7 +471,7 @@ issues.closed_at=`затвори <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`повторно отвори <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`повторно отвори <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`посочи тази задача от ревизия <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`посочи тази задача от ревизия <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.poster=Участник issues.poster=Участник
issues.admin=Администратор issues.collaborator=Сътрудник
issues.owner=Притежател issues.owner=Притежател
issues.sign_up_for_free=Регистрирай се безплатно issues.sign_up_for_free=Регистрирай се безплатно
issues.sign_in_require_desc=за да се включите в този разговор. Вече имате профил? <a href="%s">Влезте, за да коментирате</a> issues.sign_in_require_desc=за да се включите в този разговор. Вече имате профил? <a href="%s">Влезте, за да коментирате</a>
@ -494,6 +488,7 @@ issues.label_modify=Промяна на етикет
issues.label_deletion=Изтрий етикет issues.label_deletion=Изтрий етикет
issues.label_deletion_desc=При изтриване на този етикет ще се премахне информацията за него във всички свързани задачи. Желаете ли да продължите? issues.label_deletion_desc=При изтриване на този етикет ще се премахне информацията за него във всички свързани задачи. Желаете ли да продължите?
issues.label_deletion_success=Етикетът е изтрит успешно! issues.label_deletion_success=Етикетът е изтрит успешно!
issues.num_participants=%d участника
pulls.new=Нова заявка за сливане pulls.new=Нова заявка за сливане
pulls.compare_changes=Сравни промените pulls.compare_changes=Сравни промените
@ -557,6 +552,8 @@ wiki.save_page=Запис на страница
wiki.last_commit_info=%s редактира тази страница %s wiki.last_commit_info=%s редактира тази страница %s
wiki.edit_page_button=Редакция wiki.edit_page_button=Редакция
wiki.new_page_button=Нова страница wiki.new_page_button=Нова страница
wiki.delete_page_button=Изтрий страница
wiki.delete_page_notice_1=Това ще изтрие страница <code>"%s"</code>. Моля, бъдете сигурни.
wiki.page_already_exists=Страница със същото име вече съществува. wiki.page_already_exists=Страница със същото име вече съществува.
wiki.pages=Страници wiki.pages=Страници
wiki.last_updated=Последна модификация на %s wiki.last_updated=Последна модификация на %s
@ -581,14 +578,23 @@ settings.tracker_url_format=Формат на URL адрес на външна
settings.tracker_url_format_desc=Можете да използвате текстови маркери <code>{user} {repo} {index}</code> за потребителско име, име на хранилище и индекс на задача съответно. settings.tracker_url_format_desc=Можете да използвате текстови маркери <code>{user} {repo} {index}</code> за потребителско име, име на хранилище и индекс на задача съответно.
settings.pulls_desc=Включва заявки за сливане за да може да се приемат външни доработки settings.pulls_desc=Включва заявки за сливане за да може да се приемат външни доработки
settings.danger_zone=Опасна зона settings.danger_zone=Опасна зона
settings.new_owner_has_same_repo=Новият притежател вече има хранилище със същото име. Изберете друго име.
settings.convert=Промени към редовно хранилище
settings.convert_desc=Можете да промените това огледало към редовно хранилище. Конверсията не може да се отмени.
settings.convert_notices_1=- Тази операция ще конвертира огледалото към редовно хранилище и не може да бъде отменена в последствие.
settings.convert_confirm=Потвърдете конверсията
settings.convert_succeed=Конверсията към редовно хранилище е извършена успешно.
settings.transfer=Прехвърли притежание settings.transfer=Прехвърли притежание
settings.transfer_desc=Прехвърля това хранилище на друг потребител или към организация, в която имате права на администратор. settings.transfer_desc=Прехвърля това хранилище на друг потребител или към организация, в която имате права на администратор.
settings.new_owner_has_same_repo=Новият притежател вече има хранилище със същото име. Изберете друго име.
settings.delete=Изтрий това хранилище
settings.delete_desc=След като изтриете хранилището, няма връщане назад. Моля, бъдете сигурни.
settings.transfer_notices_1=- Вие ще загубите достъп, ако новият притежател е индивидуален потребител. settings.transfer_notices_1=- Вие ще загубите достъп, ако новият притежател е индивидуален потребител.
settings.transfer_notices_2=- Вие ще запазите достъпа си, ако новият притежател е организация и ако вие сте един от притежателите ѝ. settings.transfer_notices_2=- Вие ще запазите достъпа си, ако новият притежател е организация и ако вие сте един от притежателите ѝ.
settings.transfer_form_title=Моля въведете следната информация за да потвърдите операцията: settings.transfer_form_title=Моля въведете следната информация за да потвърдите операцията:
settings.wiki_delete=Изтриване на данни на уики
settings.wiki_delete_desc=След като изтриете данни за уики, няма връщане назад. Моля, бъдете сигурни.
settings.wiki_delete_notices_1=- Това ще изтрие и ще деактивира уики за %s
settings.wiki_deletion_success=Данните за уики на това хранилище са изтрити успешно.
settings.delete=Изтрий това хранилище
settings.delete_desc=След като изтриете хранилището, няма връщане назад. Моля, бъдете сигурни.
settings.delete_notices_1=- Тази операция <strong>НЕ МОЖЕ</strong> да бъде отменена в последствие. settings.delete_notices_1=- Тази операция <strong>НЕ МОЖЕ</strong> да бъде отменена в последствие.
settings.delete_notices_2=- Тази операция ще изтрие всичко от това хранилище, включително Git данни, задачи, коментари и достъпа на сътрудници. settings.delete_notices_2=- Тази операция ще изтрие всичко от това хранилище, включително Git данни, задачи, коментари и достъпа на сътрудници.
settings.delete_notices_fork_1=- Ако това хранилище е публично, всички негови разклонения ще останат независими след изтриването му. settings.delete_notices_fork_1=- Ако това хранилище е публично, всички негови разклонения ще останат независими след изтриването му.
@ -602,8 +608,12 @@ settings.transfer_succeed=Притежанието на хранилището
settings.confirm_delete=Потвърди изтриването settings.confirm_delete=Потвърди изтриването
settings.add_collaborator=Добави нов сътрудник settings.add_collaborator=Добави нов сътрудник
settings.add_collaborator_success=Добавен е нов сътрудник. settings.add_collaborator_success=Добавен е нов сътрудник.
settings.delete_collaborator=Премахни
settings.collaborator_deletion=Премахване на сътрудник
settings.collaborator_deletion_desc=Този потребител няма да има достъп на сътрудник до хранилището след изтриването. Желаете ли да продължите?
settings.remove_collaborator_success=Сътрудникът е премахнат. settings.remove_collaborator_success=Сътрудникът е премахнат.
settings.search_user_placeholder=Име на потребител... settings.search_user_placeholder=Име на потребител...
settings.org_not_allowed_to_be_collaborator=Невъзможно добавяне на организация като сътрудник.
settings.user_is_org_member=Потребителят вече участва в организацията и не може да бъде добавен като сътрудник. settings.user_is_org_member=Потребителят вече участва в организацията и не може да бъде добавен като сътрудник.
settings.add_webhook=Добави уеб-кука settings.add_webhook=Добави уеб-кука
settings.hooks_desc=Уеб-куките много приличат на обикновен HTTP POST тригер. Когато нещо се случи в Gogs, ние ще изпратим уведомление до сървъра, който посочите. Научете повече в <a target="_blank" href="%s">Ръководство за уеб-куки</a>. settings.hooks_desc=Уеб-куките много приличат на обикновен HTTP POST тригер. Когато нещо се случи в Gogs, ние ще изпратим уведомление до сървъра, който посочите. Научете повече в <a target="_blank" href="%s">Ръководство за уеб-куки</a>.
@ -818,6 +828,8 @@ dashboard.resync_all_sshkeys=Презапис на ".ssh/authorized_keys" фай
dashboard.resync_all_sshkeys_success=Всички публични ключове са презаписани успешно. dashboard.resync_all_sshkeys_success=Всички публични ключове са презаписани успешно.
dashboard.resync_all_update_hooks=Презапис на всички куки, закачени на актуализация на хранилищата (необходимо, когато се ползва собствен път за конфигурацията) dashboard.resync_all_update_hooks=Презапис на всички куки, закачени на актуализация на хранилищата (необходимо, когато се ползва собствен път за конфигурацията)
dashboard.resync_all_update_hooks_success=Всички куки, закачени на актуализация на хранилищата, са презаписани успешно. dashboard.resync_all_update_hooks_success=Всички куки, закачени на актуализация на хранилищата, са презаписани успешно.
dashboard.reinit_missing_repos=Реинициализира всички записи за хранилища
dashboard.reinit_missing_repos_success=Всички записи за хранилища със загубени Git файлове са реинициализирани успешно.
dashboard.server_uptime=Операционно време dashboard.server_uptime=Операционно време
dashboard.current_goroutine=Текущи Goroutines dashboard.current_goroutine=Текущи Goroutines
@ -911,6 +923,7 @@ auths.attribute_username_placeholder=Оставете празно за да и
auths.attribute_name=Атрибут за име auths.attribute_name=Атрибут за име
auths.attribute_surname=Атрибут за фамилия auths.attribute_surname=Атрибут за фамилия
auths.attribute_mail=Атрибут за ел. поща auths.attribute_mail=Атрибут за ел. поща
auths.attributes_in_bind=Извличане на атрибути от контекста на име (DN) за свръзка
auths.filter=Филтър за потребител auths.filter=Филтър за потребител
auths.admin_filter=Филтър за администратор auths.admin_filter=Филтър за администратор
auths.ms_ad_sa=Ms Ad SA auths.ms_ad_sa=Ms Ad SA
@ -932,6 +945,7 @@ auths.update=Запази настройки за удостоверяване
auths.delete=Изтриване на това удостоверяване auths.delete=Изтриване на това удостоверяване
auths.delete_auth_title=Изтрий удостоверяването auths.delete_auth_title=Изтрий удостоверяването
auths.delete_auth_desc=Това удостоверяване ще бъде изтрито. Желаете ли да продължите? auths.delete_auth_desc=Това удостоверяване ще бъде изтрито. Желаете ли да продължите?
auths.still_in_used=Това удостоверяване все още се използва от някои потребители. Моля изтрийте ги или ги конвертирайте до друг тип на влизане първо.
auths.deletion_success=Удостоверяването е изтрито успешно! auths.deletion_success=Удостоверяването е изтрито успешно!
config.server_config=Сървърни настройки config.server_config=Сървърни настройки
@ -948,6 +962,19 @@ config.static_file_root_path=Път към статични файлове
config.log_file_root_path=Път към журнал config.log_file_root_path=Път към журнал
config.script_type=Тип на скрипта config.script_type=Тип на скрипта
config.reverse_auth_user=Потребителско име при обратно удостоверяване config.reverse_auth_user=Потребителско име при обратно удостоверяване
config.ssh_config=SSH конфигурация
config.ssh_enabled=Активен
config.ssh_start_builtin_server=Стартирай вграден сървър
config.ssh_domain=Домейн
config.ssh_port=Порт
config.ssh_listen_port=Порт за слушане
config.ssh_root_path=Основен път
config.ssh_key_test_path=Път до ключове
config.ssh_keygen_path=Път до генератор ('ssh-keygen')
config.ssh_minimum_key_size_check=Проверка за минимален размер на ключове
config.ssh_minimum_key_sizes=Минимален размер на ключове
config.db_config=Настройки на базата данни config.db_config=Настройки на базата данни
config.db_type=Тип config.db_type=Тип
config.db_host=Сървър config.db_host=Сървър
@ -962,7 +989,6 @@ config.register_email_confirm=Изисквай потвърждение на а
config.disable_register=Изключи нови регистрации config.disable_register=Изключи нови регистрации
config.show_registration_button=Покажи бутон за регистрация config.show_registration_button=Покажи бутон за регистрация
config.require_sign_in_view=Изисквай вписване за преглед config.require_sign_in_view=Изисквай вписване за преглед
config.enable_cache_avatar=Включи кеширане на аватари
config.mail_notify=Уведомяване по ел. поща config.mail_notify=Уведомяване по ел. поща
config.disable_key_size_check=Изключи проверка минимален размер на ключ config.disable_key_size_check=Изключи проверка минимален размер на ключ
config.enable_captcha=Включи Captcha config.enable_captcha=Включи Captcha
@ -978,6 +1004,9 @@ config.mailer_disable_helo=Изключи HELO
config.mailer_name=Име config.mailer_name=Име
config.mailer_host=Сървър config.mailer_host=Сървър
config.mailer_user=Потребител config.mailer_user=Потребител
config.send_test_mail=Изпрати тестово писмо
config.test_mail_failed=Невъзможно изпращане на тестово писмо до '%s': %v
config.test_mail_sent=Тестово писмо беше изпратено до '%s'.
config.oauth_config=OAuth конфигурация config.oauth_config=OAuth конфигурация
config.oauth_enabled=Активна config.oauth_enabled=Активна
config.cache_config=Конфигурация на кеша config.cache_config=Конфигурация на кеша
@ -1029,7 +1058,11 @@ create_repo=създаде хранилище <a href="%s"> %s</a>
rename_repo=преименува хранилище от <code>%[1]s</code> на <a href="%[2]s">%[3]s</a> rename_repo=преименува хранилище от <code>%[1]s</code> на <a href="%[2]s">%[3]s</a>
commit_repo=предаде към <a href="%[1]s/src/%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a> commit_repo=предаде към <a href="%[1]s/src/%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a>
create_issue=`отвори задача <a href="%s/issues/%s">%s#%[2]s"</a>` create_issue=`отвори задача <a href="%s/issues/%s">%s#%[2]s"</a>`
close_issue=`затвори <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`повторно отвори <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`създаде заявка за сливане <a href="%s/pulls/%s">%s#%[2]s</a>` create_pull_request=`създаде заявка за сливане <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request=`затвори заявка за сливане <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`повторно отвори заявка за сливане <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue=`коментира задача <a href="%s/issues/%s">%s#%[2]s"</a>` comment_issue=`коментира задача <a href="%s/issues/%s">%s#%[2]s"</a>`
merge_pull_request=`обедини заявка за сливане <a href="%s/pulls/%s">%s#%[2]s</a>` merge_pull_request=`обедини заявка за сливане <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo=прехвърли хранилище <code>%s</code> към <a href="%s">%s</a> transfer_repo=прехвърли хранилище <code>%s</code> към <a href="%s">%s</a>

779
conf/locale/locale_de-DE.ini

File diff suppressed because it is too large Load Diff

80
conf/locale/locale_en-US.ini

@ -38,19 +38,12 @@ settings = Settings
your_profile = Your Profile your_profile = Your Profile
your_settings = Your Settings your_settings = Your Settings
news_feed = News Feed activities = Activities
pull_requests = Pull Requests pull_requests = Pull Requests
issues = Issues issues = Issues
cancel = Cancel cancel = Cancel
[search]
search = Search...
repository = Repository
user = User
issue = Issue
code = Code
[install] [install]
install = Installation install = Installation
title = Install Steps For First-time Run title = Install Steps For First-time Run
@ -65,7 +58,7 @@ db_name = Database Name
db_helper = Please use INNODB engine with utf8_general_ci charset for MySQL. db_helper = Please use INNODB engine with utf8_general_ci charset for MySQL.
ssl_mode = SSL Mode ssl_mode = SSL Mode
path = Path path = Path
sqlite_helper = The file path of SQLite3 or TiDB database. sqlite_helper = The file path of SQLite3 or TiDB database. <br>Please use absolute path when you start as service.
err_empty_db_path = SQLite3 or TiDB database path cannot be empty. err_empty_db_path = SQLite3 or TiDB database path cannot be empty.
err_invalid_tidb_name = TiDB database name does not allow characters "." and "-". err_invalid_tidb_name = TiDB database name does not allow characters "." and "-".
no_admin_and_disable_registration = You cannot disable registration without creating an admin account. no_admin_and_disable_registration = You cannot disable registration without creating an admin account.
@ -86,6 +79,8 @@ http_port = HTTP Port
http_port_helper = Port number which application will listen on. http_port_helper = Port number which application will listen on.
app_url = Application URL app_url = Application URL
app_url_helper = This affects HTTP/HTTPS clone URL and somewhere in email. app_url_helper = This affects HTTP/HTTPS clone URL and somewhere in email.
log_root_path = Log Path
log_root_path_helper = Directory to write log files to.
optional_title = Optional Settings optional_title = Optional Settings
email_title = Email Service Settings email_title = Email Service Settings
@ -122,6 +117,7 @@ run_user_not_match = Run user isn't the current user: %s -> %s
save_config_failed = Fail to save configuration: %v save_config_failed = Fail to save configuration: %v
invalid_admin_setting = Admin account setting is invalid: %v invalid_admin_setting = Admin account setting is invalid: %v
install_success = Welcome! We're glad that you chose Gogs, have fun and take care. install_success = Welcome! We're glad that you chose Gogs, have fun and take care.
invalid_log_root_path = Log root path is invalid: %v
[home] [home]
uname_holder = Username or email uname_holder = Username or email
@ -137,6 +133,8 @@ issues.in_your_repos = In your repositories
[explore] [explore]
repos = Repositories repos = Repositories
users = Users
search = Search
[auth] [auth]
create_new_account = Create New Account create_new_account = Create New Account
@ -159,6 +157,7 @@ reset_password = Reset Your Password
invalid_code = Sorry, your confirmation code has expired or not valid. invalid_code = Sorry, your confirmation code has expired or not valid.
reset_password_helper = Click here to reset your password reset_password_helper = Click here to reset your password
password_too_short = Password length cannot be less then 6. password_too_short = Password length cannot be less then 6.
non_local_account = Non-local accounts cannot change passwords through Gogs.
[mail] [mail]
activate_account = Please activate your account activate_account = Please activate your account
@ -203,7 +202,6 @@ repo_name_been_taken = Repository name has already been taken.
org_name_been_taken = Organization name has already been taken. org_name_been_taken = Organization name has already been taken.
team_name_been_taken = Team name has already been taken. team_name_been_taken = Team name has already been taken.
email_been_used = Email address has already been used. email_been_used = Email address has already been used.
illegal_team_name = Team name contains illegal characters.
username_password_incorrect = Username or password is not correct. username_password_incorrect = Username or password is not correct.
enterred_invalid_repo_name = Please make sure that the repository name you entered is correct. enterred_invalid_repo_name = Please make sure that the repository name you entered is correct.
enterred_invalid_owner_name = Please make sure that the owner name you entered is correct. enterred_invalid_owner_name = Please make sure that the owner name you entered is correct.
@ -219,8 +217,6 @@ still_own_repo = Your account still has ownership over at least one repository,
still_has_org = Your account still has membership in at least one organization, you have to leave or delete your memberships first. still_has_org = Your account still has membership in at least one organization, you have to leave or delete your memberships first.
org_still_own_repo = This organization still has ownership of repositories, you must delete or transfer them first. org_still_own_repo = This organization still has ownership of repositories, you must delete or transfer them first.
still_own_user = This authentication is still in use by at least one user, please remove them from the authentication and try again.
target_branch_not_exist = Target branch does not exist. target_branch_not_exist = Target branch does not exist.
[user] [user]
@ -262,11 +258,10 @@ continue = Continue
cancel = Cancel cancel = Cancel
enable_custom_avatar = Enable Custom Avatar enable_custom_avatar = Enable Custom Avatar
enable_custom_avatar_helper = Disable fetch from Gravatar
choose_new_avatar = Choose new avatar choose_new_avatar = Choose new avatar
update_avatar = Update Avatar Setting update_avatar = Update Avatar Setting
delete_current_avatar = Delete Current Avatar
uploaded_avatar_not_a_image = Uploaded file is not a image. uploaded_avatar_not_a_image = Uploaded file is not a image.
no_custom_avatar_available = No custom avatar available, cannot enable it.
update_avatar_success = Your avatar setting has been updated successfully. update_avatar_success = Your avatar setting has been updated successfully.
change_password = Change Password change_password = Change Password
@ -377,7 +372,7 @@ migrate.permission_denied = You are not allowed to import local repositories.
migrate.invalid_local_path = Invalid local path, it does not exist or not a directory. migrate.invalid_local_path = Invalid local path, it does not exist or not a directory.
migrate.failed = Migration failed: %v migrate.failed = Migration failed: %v
mirror_from = mirror from mirror_from = mirror of
forked_from = forked from forked_from = forked from
fork_from_self = You cannot fork a repository you already own! fork_from_self = You cannot fork a repository you already own!
copy_link = Copy copy_link = Copy
@ -477,7 +472,7 @@ issues.closed_at = `closed <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at = `reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at = `reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at = `referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at = `referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.poster = Poster issues.poster = Poster
issues.admin = Admin issues.collaborator = Collaborator
issues.owner = Owner issues.owner = Owner
issues.sign_up_for_free = Sign up for free issues.sign_up_for_free = Sign up for free
issues.sign_in_require_desc = to join this conversation. Already have an account? <a href="%s">Sign in to comment</a> issues.sign_in_require_desc = to join this conversation. Already have an account? <a href="%s">Sign in to comment</a>
@ -494,6 +489,7 @@ issues.label_modify = Label Modification
issues.label_deletion = Label Deletion issues.label_deletion = Label Deletion
issues.label_deletion_desc = Deleting this label will remove its information in all related issues. Do you want to continue? issues.label_deletion_desc = Deleting this label will remove its information in all related issues. Do you want to continue?
issues.label_deletion_success = Label has been deleted successfully! issues.label_deletion_success = Label has been deleted successfully!
issues.num_participants = %d Participants
pulls.new = New Pull Request pulls.new = New Pull Request
pulls.compare_changes = Compare Changes pulls.compare_changes = Compare Changes
@ -557,6 +553,8 @@ wiki.save_page = Save Page
wiki.last_commit_info = %s edited this page %s wiki.last_commit_info = %s edited this page %s
wiki.edit_page_button = Edit wiki.edit_page_button = Edit
wiki.new_page_button = New Page wiki.new_page_button = New Page
wiki.delete_page_button = Delete Page
wiki.delete_page_notice_1 = This will delete the page <code>"%s"</code>. Please be certain.
wiki.page_already_exists = Wiki page with same name already exists. wiki.page_already_exists = Wiki page with same name already exists.
wiki.pages = Pages wiki.pages = Pages
wiki.last_updated = Last updated %s wiki.last_updated = Last updated %s
@ -581,17 +579,26 @@ settings.tracker_url_format = External Issue Tracker URL Format
settings.tracker_url_format_desc = You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index. settings.tracker_url_format_desc = You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index.
settings.pulls_desc = Enable pull requests to accept public contributions settings.pulls_desc = Enable pull requests to accept public contributions
settings.danger_zone = Danger Zone settings.danger_zone = Danger Zone
settings.new_owner_has_same_repo = The new owner already has a repository with same name. Please choose another name.
settings.convert = Convert To Regular Repository
settings.convert_desc = You can convert this mirror to a regular repository. This cannot be reversed.
settings.convert_notices_1 = - This operation will convert this repository mirror into a regular repository and cannot be undone.
settings.convert_confirm = Confirm Conversion
settings.convert_succeed = Repository has been converted to regular type successfully.
settings.transfer = Transfer Ownership settings.transfer = Transfer Ownership
settings.transfer_desc = Transfer this repository to another user or to an organization in which you have admin rights. settings.transfer_desc = Transfer this repository to another user or to an organization in which you have admin rights.
settings.new_owner_has_same_repo = The new owner already has a repository with same name. Please choose another name.
settings.delete = Delete This Repository
settings.delete_desc = Once you delete a repository, there is no going back. Please be certain.
settings.transfer_notices_1 = - You will lose access if new owner is a individual user. settings.transfer_notices_1 = - You will lose access if new owner is a individual user.
settings.transfer_notices_2 = - You will conserve access if new owner is an organization and if you're one of the owners. settings.transfer_notices_2 = - You will conserve access if new owner is an organization and if you're one of the owners.
settings.transfer_form_title = Please enter following information to confirm your operation: settings.transfer_form_title = Please enter following information to confirm your operation:
settings.wiki_delete = Erase Wiki Data
settings.wiki_delete_desc = Once you erase wiki data there is no going back. Please be certain.
settings.wiki_delete_notices_1 = - This will delete and disable the wiki for %s
settings.wiki_deletion_success = Repository wiki data have been erased successfully.
settings.delete = Delete This Repository
settings.delete_desc = Once you delete a repository, there is no going back. Please be certain.
settings.delete_notices_1 = - This operation <strong>CANNOT</strong> be undone. settings.delete_notices_1 = - This operation <strong>CANNOT</strong> be undone.
settings.delete_notices_2 = - This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators. settings.delete_notices_2 = - This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators.
settings.delete_notices_fork_1 = - If this repository is public, all forks will be became independent after deletion. settings.delete_notices_fork_1 = - If this repository is public, all forks will become independent after deletion.
settings.delete_notices_fork_2 = - If this repository is private, all forks will be removed at the same time. settings.delete_notices_fork_2 = - If this repository is private, all forks will be removed at the same time.
settings.delete_notices_fork_3 = - If you want to keep all forks after deletion, please change visibility of this repository to public first. settings.delete_notices_fork_3 = - If you want to keep all forks after deletion, please change visibility of this repository to public first.
settings.deletion_success = Repository has been deleted successfully! settings.deletion_success = Repository has been deleted successfully!
@ -602,8 +609,12 @@ settings.transfer_succeed = Repository ownership has been transferred successful
settings.confirm_delete = Confirm Deletion settings.confirm_delete = Confirm Deletion
settings.add_collaborator = Add New Collaborator settings.add_collaborator = Add New Collaborator
settings.add_collaborator_success = New collaborator has been added. settings.add_collaborator_success = New collaborator has been added.
settings.delete_collaborator = Delete
settings.collaborator_deletion = Collaborator Deletion
settings.collaborator_deletion_desc = This user will no longer have collaboration access to this repository after deletion. Do you want to continue?
settings.remove_collaborator_success = Collaborator has been removed. settings.remove_collaborator_success = Collaborator has been removed.
settings.search_user_placeholder = Search user... settings.search_user_placeholder = Search user...
settings.org_not_allowed_to_be_collaborator = Organization is not allowed to be added as a collaborator.
settings.user_is_org_member = User is organization member who cannot be added as a collaborator. settings.user_is_org_member = User is organization member who cannot be added as a collaborator.
settings.add_webhook = Add Webhook settings.add_webhook = Add Webhook
settings.hooks_desc = Webhooks are much like basic HTTP POST event triggers. Whenever something occurs in Gogs, we will handle the notification to the target host you specify. Learn more in this <a target="_blank" href="%s">Webhooks Guide</a>. settings.hooks_desc = Webhooks are much like basic HTTP POST event triggers. Whenever something occurs in Gogs, we will handle the notification to the target host you specify. Learn more in this <a target="_blank" href="%s">Webhooks Guide</a>.
@ -652,8 +663,8 @@ settings.slack_domain = Domain
settings.slack_channel = Channel settings.slack_channel = Channel
settings.deploy_keys = Deploy Keys settings.deploy_keys = Deploy Keys
settings.add_deploy_key = Add Deploy Key settings.add_deploy_key = Add Deploy Key
settings.deploy_key_desc = Deploy key only has read-only access. It is not same as personal account SSH keys. settings.deploy_key_desc = Deploy keys have read-only access. They are not the same as personal account SSH keys.
settings.no_deploy_keys = You haven't added any deploy key. settings.no_deploy_keys = You haven't added any deploy keys.
settings.title = Title settings.title = Title
settings.deploy_key_content = Content settings.deploy_key_content = Content
settings.key_been_used = Deploy key content has been used. settings.key_been_used = Deploy key content has been used.
@ -818,6 +829,8 @@ dashboard.resync_all_sshkeys = Rewrite '.ssh/authorized_keys' file (caution: non
dashboard.resync_all_sshkeys_success = All public keys have been rewritten successfully. dashboard.resync_all_sshkeys_success = All public keys have been rewritten successfully.
dashboard.resync_all_update_hooks = Rewrite all update hook of repositories (needed when custom config path is changed) dashboard.resync_all_update_hooks = Rewrite all update hook of repositories (needed when custom config path is changed)
dashboard.resync_all_update_hooks_success = All repositories' update hook have been rewritten successfully. dashboard.resync_all_update_hooks_success = All repositories' update hook have been rewritten successfully.
dashboard.reinit_missing_repos = Reinitialize all repository records that lost Git files
dashboard.reinit_missing_repos_success = All repository records that lost Git files have been reinitialized successfully.
dashboard.server_uptime = Server Uptime dashboard.server_uptime = Server Uptime
dashboard.current_goroutine = Current Goroutines dashboard.current_goroutine = Current Goroutines
@ -911,6 +924,7 @@ auths.attribute_username_placeholder = Leave empty to use sign-in form field val
auths.attribute_name = First name attribute auths.attribute_name = First name attribute
auths.attribute_surname = Surname attribute auths.attribute_surname = Surname attribute
auths.attribute_mail = Email attribute auths.attribute_mail = Email attribute
auths.attributes_in_bind = Fetch attributes in Bind DN context
auths.filter = User Filter auths.filter = User Filter
auths.admin_filter = Admin Filter auths.admin_filter = Admin Filter
auths.ms_ad_sa = Ms Ad SA auths.ms_ad_sa = Ms Ad SA
@ -932,6 +946,7 @@ auths.update = Update Authentication Setting
auths.delete = Delete This Authentication auths.delete = Delete This Authentication
auths.delete_auth_title = Authentication Deletion auths.delete_auth_title = Authentication Deletion
auths.delete_auth_desc = This authentication is going to be deleted, do you want to continue? auths.delete_auth_desc = This authentication is going to be deleted, do you want to continue?
auths.still_in_used = This authentication is still used by some users, please delete or convert these users to another login type first.
auths.deletion_success = Authentication has been deleted successfully! auths.deletion_success = Authentication has been deleted successfully!
config.server_config = Server Configuration config.server_config = Server Configuration
@ -948,6 +963,19 @@ config.static_file_root_path = Static File Root Path
config.log_file_root_path = Log File Root Path config.log_file_root_path = Log File Root Path
config.script_type = Script Type config.script_type = Script Type
config.reverse_auth_user = Reverse Authentication User config.reverse_auth_user = Reverse Authentication User
config.ssh_config = SSH Configuration
config.ssh_enabled = Enabled
config.ssh_start_builtin_server = Start Builtin Server
config.ssh_domain = Domain
config.ssh_port = Port
config.ssh_listen_port = Listen Port
config.ssh_root_path = Root Path
config.ssh_key_test_path = Key Test Path
config.ssh_keygen_path = Keygen ('ssh-keygen') Path
config.ssh_minimum_key_size_check = Minimum Key Size Check
config.ssh_minimum_key_sizes = Minimum Key Sizes
config.db_config = Database Configuration config.db_config = Database Configuration
config.db_type = Type config.db_type = Type
config.db_host = Host config.db_host = Host
@ -962,7 +990,6 @@ config.register_email_confirm = Require Email Confirmation
config.disable_register = Disable Registration config.disable_register = Disable Registration
config.show_registration_button = Show Register Button config.show_registration_button = Show Register Button
config.require_sign_in_view = Require Sign In View config.require_sign_in_view = Require Sign In View
config.enable_cache_avatar = Enable Cache Avatar
config.mail_notify = Mail Notification config.mail_notify = Mail Notification
config.disable_key_size_check = Disable Minimum Key Size Check config.disable_key_size_check = Disable Minimum Key Size Check
config.enable_captcha = Enable Captcha config.enable_captcha = Enable Captcha
@ -978,6 +1005,9 @@ config.mailer_disable_helo = Disable HELO
config.mailer_name = Name config.mailer_name = Name
config.mailer_host = Host config.mailer_host = Host
config.mailer_user = User config.mailer_user = User
config.send_test_mail = Send Test Email
config.test_mail_failed = Fail to send test email to '%s': %v
config.test_mail_sent = Test email has been sent to '%s'.
config.oauth_config = OAuth Configuration config.oauth_config = OAuth Configuration
config.oauth_enabled = Enabled config.oauth_enabled = Enabled
config.cache_config = Cache Configuration config.cache_config = Cache Configuration
@ -1029,7 +1059,11 @@ create_repo = created repository <a href="%s">%s</a>
rename_repo = renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a> rename_repo = renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a>
commit_repo = pushed to <a href="%[1]s/src/%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a> commit_repo = pushed to <a href="%[1]s/src/%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a>
create_issue = `opened issue <a href="%s/issues/%s">%s#%[2]s</a>` create_issue = `opened issue <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue = `closed issue <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue = `reopened issue <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request = `created pull request <a href="%s/pulls/%s">%s#%[2]s</a>` create_pull_request = `created pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request = `closed pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request = `reopened pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue = `commented on issue <a href="%s/issues/%s">%s#%[2]s</a>` comment_issue = `commented on issue <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request = `merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>` merge_pull_request = `merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo = transfered repository <code>%s</code> to <a href="%s">%s</a> transfer_repo = transfered repository <code>%s</code> to <a href="%s">%s</a>

225
conf/locale/locale_es-ES.ini

@ -6,9 +6,9 @@ explore=Explorar
help=Ayuda help=Ayuda
sign_in=Iniciar sesión sign_in=Iniciar sesión
sign_out=Cerrar sesión sign_out=Cerrar sesión
sign_up=Suscripción sign_up=Registro
register=Registro register=Registro
website=Página Web website=Página web
version=Versión version=Versión
page=Página page=Página
template=Plantilla template=Plantilla
@ -27,9 +27,9 @@ repository=Repositorio
organization=Organización organization=Organización
mirror=Mirror mirror=Mirror
new_repo=Nuevo repositorio new_repo=Nuevo repositorio
new_migrate=Nueva Migración new_migrate=Nueva migración
new_mirror=Nueva réplica new_mirror=Nueva réplica
new_fork=Nuevo Fork del Repositorio new_fork=Nuevo fork del repositorio
new_org=Nueva organización new_org=Nueva organización
manage_org=Administrar organizaciones manage_org=Administrar organizaciones
admin_panel=Panel de administración admin_panel=Panel de administración
@ -38,19 +38,12 @@ settings=Configuraciones
your_profile=Tu perfil your_profile=Tu perfil
your_settings=Tu configuración your_settings=Tu configuración
news_feed=Feed de noticias activities=Actividad
pull_requests=Pull Requests pull_requests=Pull Requests
issues=Incidencias issues=Incidencias
cancel=Cancelar cancel=Cancelar
[search]
search=Buscar...
repository=Repositorio
user=Usuario
issue=Incidencia
code=Código
[install] [install]
install=Instalación install=Instalación
title=Pasos de la instalación por primera vez title=Pasos de la instalación por primera vez
@ -65,7 +58,7 @@ db_name=Nombre de la base de datos
db_helper=Por favor utilice el motor INNODB con la configuración de caracteres utf8_general_ci para MySQL. db_helper=Por favor utilice el motor INNODB con la configuración de caracteres utf8_general_ci para MySQL.
ssl_mode=Modo SSL ssl_mode=Modo SSL
path=Ruta path=Ruta
sqlite_helper=Ruta del archivo con la base de datos SQLite3 o TiDB. sqlite_helper=Ruta al archivo de base de datos SQLite3 o TiDB. <br>Por favor, usa una ruta absoluta cuando inicies como servicio.
err_empty_db_path=La ruta a la base de datos SQLite3 o TiDB no puede estar vacía. err_empty_db_path=La ruta a la base de datos SQLite3 o TiDB no puede estar vacía.
err_invalid_tidb_name=El nombre de la base de datos TiDB no puede contener los caracteres "." ni "-". err_invalid_tidb_name=El nombre de la base de datos TiDB no puede contener los caracteres "." ni "-".
no_admin_and_disable_registration=No puede deshabilitar el registro sin crear una cuenta de administrador. no_admin_and_disable_registration=No puede deshabilitar el registro sin crear una cuenta de administrador.
@ -86,9 +79,11 @@ http_port=Puerto HTTP
http_port_helper=Puerto en el que escuchará la aplicación. http_port_helper=Puerto en el que escuchará la aplicación.
app_url=URL de la aplicación app_url=URL de la aplicación
app_url_helper=Esto afecta a las URLs para clonar por HTTP/HTTPS y a algunos correos electrónicos. app_url_helper=Esto afecta a las URLs para clonar por HTTP/HTTPS y a algunos correos electrónicos.
log_root_path=Ruta del registro
log_root_path_helper=Directorio donde almacenar los registros.
optional_title=Configuración Opcional optional_title=Configuración opcional
email_title=Configuración del Servicio de Correo email_title=Configuración del servicio de correo
smtp_host=SMTP Host smtp_host=SMTP Host
smtp_from=Desde smtp_from=Desde
smtp_from_helper=Remitente del email, RFC 5322. Puede ser solamente una dirección de correo electrónico, o estar en el formato "Nombre" <email@example.com>. smtp_from_helper=Remitente del email, RFC 5322. Puede ser solamente una dirección de correo electrónico, o estar en el formato "Nombre" <email@example.com>.
@ -96,10 +91,10 @@ mailer_user=Remitente del Correo Electrónico
mailer_password=Contraseña del Remitente mailer_password=Contraseña del Remitente
register_confirm=Habilitar la Confirmación en el Registro register_confirm=Habilitar la Confirmación en el Registro
mail_notify=Habilitar las Notificaciones de Correo mail_notify=Habilitar las Notificaciones de Correo
server_service_title=Configuración de Servidor y Otros Servicios server_service_title=Configuración del servidor y otros servicios
offline_mode=Activar el modo Sin Conexión offline_mode=Activar el modo Sin Conexión
offline_mode_popup=Desactivar el CDN incluso en el modo de producción, todos los recursos se servirán localmente. offline_mode_popup=Desactivar el CDN incluso en el modo de producción, todos los recursos se servirán localmente.
disable_gravatar=Desactivar el Servicio Gravatar disable_gravatar=Desactivar el servicio Gravatar
disable_gravatar_popup=Desactivar Gravatar y cualquier otra fuente personalizada. Todos los avatares deben ser cargados por los usuarios o en su defecto se mostrará el avatar predeterminado. disable_gravatar_popup=Desactivar Gravatar y cualquier otra fuente personalizada. Todos los avatares deben ser cargados por los usuarios o en su defecto se mostrará el avatar predeterminado.
disable_registration=Desactivar Auto-Registro disable_registration=Desactivar Auto-Registro
disable_registration_popup=Desactivar auto-registro del usuario, solo el administrador podrá crear cuentas nuevas. disable_registration_popup=Desactivar auto-registro del usuario, solo el administrador podrá crear cuentas nuevas.
@ -108,7 +103,7 @@ enable_captcha_popup=Requiere validar la captcha para el auto-registro de usuari
require_sign_in_view=Activar el Inicio de Sesión obligatorio para Ver Páginas require_sign_in_view=Activar el Inicio de Sesión obligatorio para Ver Páginas
require_sign_in_view_popup=Solo los usuarios logados pueden ver páginas, los visitantes anónimos solo podrán ver las páginas de login/registro. require_sign_in_view_popup=Solo los usuarios logados pueden ver páginas, los visitantes anónimos solo podrán ver las páginas de login/registro.
admin_setting_desc=No es necesario crear una cuenta de administrador ahora mismo, el usuario que tenga ID=1 obtendrá privilegios de administrador automáticamente. admin_setting_desc=No es necesario crear una cuenta de administrador ahora mismo, el usuario que tenga ID=1 obtendrá privilegios de administrador automáticamente.
admin_title=Configuración de la Cuenta de Administrador admin_title=Configuración de la cuenta de administrador
admin_name=Nombre de usuario admin_name=Nombre de usuario
admin_password=Contraseña admin_password=Contraseña
confirm_password=Confirmar Contraseña confirm_password=Confirmar Contraseña
@ -122,24 +117,27 @@ run_user_not_match=El usuario que está ejecutando la aplicación no es el usuar
save_config_failed=Error al guardar la configuración: %v save_config_failed=Error al guardar la configuración: %v
invalid_admin_setting=La configuración de la cuenta de administración es inválida: %v invalid_admin_setting=La configuración de la cuenta de administración es inválida: %v
install_success=Bienvenido! Estamos encantados de que hayas escogido Gogs, diviértete y cuídate. install_success=Bienvenido! Estamos encantados de que hayas escogido Gogs, diviértete y cuídate.
invalid_log_root_path=La ruta para los registros es inválida: %v
[home] [home]
uname_holder=Nombre de usuario o correo electrónico uname_holder=Nombre de usuario o correo electrónico
password_holder=Contraseña password_holder=Contraseña
switch_dashboard_context=Cambiar el contexto del Dashboard switch_dashboard_context=Cambiar el contexto del Dashboard
my_repos=Mis Repositorios my_repos=Mis repositorios
collaborative_repos=Repositorios Colaborativos collaborative_repos=Repositorios colaborativos
my_orgs=Mis Organizaciones my_orgs=Mis organizaciones
my_mirrors=Mis Mirrors my_mirrors=Mis réplicas
view_home=Ver %s view_home=Ver %s
issues.in_your_repos=En tus repositorios issues.in_your_repos=En tus repositorios
[explore] [explore]
repos=Repositorios repos=Repositorios
users=Usuarios
search=Buscar
[auth] [auth]
create_new_account=Crear una Nueva Cuenta create_new_account=Crear una nueva cuenta
register_hepler_msg=¿Ya tienes una cuenta? ¡Inicia sesión! register_hepler_msg=¿Ya tienes una cuenta? ¡Inicia sesión!
social_register_hepler_msg=¿Ya tienes una cuenta? ¡Enlázala! social_register_hepler_msg=¿Ya tienes una cuenta? ¡Enlázala!
disable_register_prompt=Lo sentimos, el registro está deshabilitado. Por favor, contacta con el administrador del sitio. disable_register_prompt=Lo sentimos, el registro está deshabilitado. Por favor, contacta con el administrador del sitio.
@ -203,7 +201,6 @@ repo_name_been_taken=Ya existe un repositorio con este nombre.
org_name_been_taken=Ya existe una organización con este nombre. org_name_been_taken=Ya existe una organización con este nombre.
team_name_been_taken=Ya existe un equipo con este nombre. team_name_been_taken=Ya existe un equipo con este nombre.
email_been_used=Esta dirección de correo electrónico ya está en uso. email_been_used=Esta dirección de correo electrónico ya está en uso.
illegal_team_name=El nombre del equipo contiene caracteres inválidos.
username_password_incorrect=Nombre de usuario o contraseña incorrectos. username_password_incorrect=Nombre de usuario o contraseña incorrectos.
enterred_invalid_repo_name=Por favor, asegúrate de que has introducido correctamente el nombre del repositorio. enterred_invalid_repo_name=Por favor, asegúrate de que has introducido correctamente el nombre del repositorio.
enterred_invalid_owner_name=Por favor, asegúrate de que has introducido correctamente el nombre del propietario. enterred_invalid_owner_name=Por favor, asegúrate de que has introducido correctamente el nombre del propietario.
@ -219,14 +216,12 @@ still_own_repo=Tu cuenta es la propietaria de uno o más repositorios, tienes qu
still_has_org=Tu cuenta es miembro de una o más organizaciones, tienes que abandonarlas o eliminarlas primero. still_has_org=Tu cuenta es miembro de una o más organizaciones, tienes que abandonarlas o eliminarlas primero.
org_still_own_repo=Esta organización es dueña de uno o más repositorios, tienes que eliminarlos o transferirlos primero. org_still_own_repo=Esta organización es dueña de uno o más repositorios, tienes que eliminarlos o transferirlos primero.
still_own_user=Esta autenticación está en uso por algunos usuarios, debes moverlos y antes de eliminarla.
target_branch_not_exist=La rama de destino no existe target_branch_not_exist=La rama de destino no existe
[user] [user]
change_avatar=Cambia tu avatar en gravatar.com change_avatar=Cambia tu avatar en gravatar.com
change_custom_avatar=Cambia tu avatar en la configuración change_custom_avatar=Cambia tu avatar en la configuración
join_on=Registrado en join_on=Registrado el
repositories=Repositorios repositories=Repositorios
activity=Actividad pública activity=Actividad pública
followers=Seguidores followers=Seguidores
@ -248,25 +243,24 @@ orgs=Organizaciones
delete=Eliminar cuenta delete=Eliminar cuenta
uid=UUID uid=UUID
public_profile=Perfil Público public_profile=Perfil público
profile_desc=Tu correo electrónico es público y será usado para todas las notificaciones relacionadas con cualquier cuenta y cualquier operación hecha a través de la web. profile_desc=Tu correo electrónico es público y será usado para todas las notificaciones relacionadas con cualquier cuenta y cualquier operación hecha a través de la web.
password_username_disabled=Los usuarios que no son locales no tienen permitido cambiar su nombre de usuario. password_username_disabled=Los usuarios que no son locales no tienen permitido cambiar su nombre de usuario.
full_name=Nombre Completo full_name=Nombre completo
website=Página Web website=Página web
location=Localización location=Localización
update_profile=Actualizar Perfil update_profile=Actualizar perfil
update_profile_success=Tu perfil se ha actualizado correctamente. update_profile_success=Tu perfil se ha actualizado correctamente.
change_username=Nombre de usuario modificado change_username=Nombre de usuario modificado
change_username_prompt=Este cambio afectará a los enlaces que hacen referencia a su cuenta. change_username_prompt=Este cambio afectará a los enlaces que hacen referencia a su cuenta.
continue=Continuar continue=Continuar
cancel=Cancelar cancel=Cancelar
enable_custom_avatar=Activar Avatar Personalizado enable_custom_avatar=Activar avatar personalizado
enable_custom_avatar_helper=Activa esto para desactivar los avatares de Gravatar
choose_new_avatar=Selecciona nuevo avatar choose_new_avatar=Selecciona nuevo avatar
update_avatar=Actualizar Configuración del Avatar update_avatar=Actualizar configuración del avatar
delete_current_avatar=Eliminar avatar
uploaded_avatar_not_a_image=El archivo enviado no es una imagen. uploaded_avatar_not_a_image=El archivo enviado no es una imagen.
no_custom_avatar_available=No hay ningún avatar personalizado disponible, no se puede habilitar.
update_avatar_success=La configuración de tu avatar se ha actualizado correctamente. update_avatar_success=La configuración de tu avatar se ha actualizado correctamente.
change_password=Cambiar contraseña change_password=Cambiar contraseña
@ -336,10 +330,10 @@ delete_account_desc=Esta cuenta se va a eliminar permanentemente, ¿quieres cont
[repo] [repo]
owner=Propietario owner=Propietario
repo_name=Nombre del Repositorio repo_name=Nombre del repositorio
repo_name_helper=Los grandes nombres de repositorios son cortos, memorables y <strong>únicos</strong>. repo_name_helper=Los grandes nombres de repositorios son cortos, memorables y <strong>únicos</strong>.
visibility=Visibilidad visibility=Visibilidad
visiblity_helper=Este repositorio es <span class="ui red text">Privado</span> visiblity_helper=Este repositorio es <span class="ui red text">privado</span>
visiblity_helper_forced=El administrador web ha obligado a todos los repositorios nuevos a ser <span class="ui red text"> privados</span> visiblity_helper_forced=El administrador web ha obligado a todos los repositorios nuevos a ser <span class="ui red text"> privados</span>
visiblity_fork_helper=(Este cambio afectará a todos los forks) visiblity_fork_helper=(Este cambio afectará a todos los forks)
clone_helper=¿Necesitas ayuda con el clone? ¡Consulta la <a target="_blank" href="%s">Ayuda</a>! clone_helper=¿Necesitas ayuda con el clone? ¡Consulta la <a target="_blank" href="%s">Ayuda</a>!
@ -347,16 +341,16 @@ fork_repo=Hacer Fork del repositorio
fork_from=Crear un Fork desde fork_from=Crear un Fork desde
fork_visiblity_helper=No es posible cambiar la visibilidad de un Fork fork_visiblity_helper=No es posible cambiar la visibilidad de un Fork
repo_desc=Descripción repo_desc=Descripción
repo_lang=Idioma repo_lang=Lenguaje
repo_lang_helper=Seleccione archivo .gitignore repo_lang_helper=Seleccione archivo .gitignore
license=Licencia license=Licencia
license_helper=Selecciona un fichero de licencia license_helper=Selecciona un fichero de licencia
readme=Readme readme=Readme
readme_helper=Seleccione una plantilla de archivo readme readme_helper=Seleccione una plantilla de archivo readme
auto_init=Inicializar los archivos seleccionados y plantillas de este repositorio auto_init=Inicializar los archivos seleccionados y plantillas de este repositorio
create_repo=Crear Repositorio create_repo=Crear repositorio
default_branch=Rama por defecto default_branch=Rama por defecto
mirror_interval=Intervalo de mirror(en horas) mirror_interval=Intervalo de la réplica (en horas)
mirror_address=Dirección de la réplica mirror_address=Dirección de la réplica
mirror_address_desc=Por favor, incluya las credenciales de usuario necesarias en la dirección. mirror_address_desc=Por favor, incluya las credenciales de usuario necesarias en la dirección.
watchers=Seguidores watchers=Seguidores
@ -367,11 +361,11 @@ form.reach_limit_of_creation=El propietario ha alcanzado el límite máximo de %
form.name_reserved=El nombre del repositorio '%s' está reservado. form.name_reserved=El nombre del repositorio '%s' está reservado.
form.name_pattern_not_allowed=El patrón del nombre del repositorio '%s' no está permitido. form.name_pattern_not_allowed=El patrón del nombre del repositorio '%s' no está permitido.
need_auth=Requiere Autorización need_auth=Requiere autorización
migrate_type=Tipo de Migración migrate_type=Tipo de migración
migrate_type_helper=Este repositorio será un <span class="text blue">mirror</span> migrate_type_helper=Este repositorio será una <span class="text blue">réplica</span>
migrate_repo=Migrar Repositorio migrate_repo=Migrar Repositorio
migrate.clone_address=Clonar Dirección migrate.clone_address=Clonar dirección
migrate.clone_address_desc=Puede ser una URL HTTP/HTTPS/GIT o una ruta local del servidor. migrate.clone_address_desc=Puede ser una URL HTTP/HTTPS/GIT o una ruta local del servidor.
migrate.permission_denied=No te está permitido importar repositorios locales. migrate.permission_denied=No te está permitido importar repositorios locales.
migrate.invalid_local_path=Rutal local inválida, no existe o no es un directorio. migrate.invalid_local_path=Rutal local inválida, no existe o no es un directorio.
@ -379,7 +373,7 @@ migrate.failed=Migración fallida: %v
mirror_from=espejo de mirror_from=espejo de
forked_from=forked de forked_from=forked de
fork_from_self=Eres el propietario del repositorio, ¡no puedes hacer fork! fork_from_self=¡No puedes crear un fork de un repositorio que ya es tuyo!
copy_link=Copiar copy_link=Copiar
copy_link_success=¡Copiado! copy_link_success=¡Copiado!
copy_link_error=Presione ⌘ + C o Ctrl-C para copiar copy_link_error=Presione ⌘ + C o Ctrl-C para copiar
@ -423,7 +417,7 @@ commits.date=Fecha
commits.older=Anterior commits.older=Anterior
commits.newer=Posterior commits.newer=Posterior
issues.new=Nueva Incidencia issues.new=Nueva incidencia
issues.new.labels=Etiquetas issues.new.labels=Etiquetas
issues.new.no_label=Sin etiquetas issues.new.no_label=Sin etiquetas
issues.new.clear_labels=Limpiar etiquetas issues.new.clear_labels=Limpiar etiquetas
@ -467,17 +461,17 @@ issues.open_title=Abierta
issues.closed_title=Cerrada issues.closed_title=Cerrada
issues.num_comments=%d comentarios issues.num_comments=%d comentarios
issues.commented_at=`comentada <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commented_at=`comentada <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.no_content=Aun no existe contenido. issues.no_content=Aún no existe contenido.
issues.close_issue=Cerrar issues.close_issue=Cerrar
issues.close_comment_issue=Comentar y cerrar issues.close_comment_issue=Comentar y cerrar
issues.reopen_issue=Reabrir issues.reopen_issue=Reabrir
issues.reopen_comment_issue=Comentar y reabrir issues.reopen_comment_issue=Comentar y reabrir
issues.create_comment=Comentar issues.create_comment=Comentar
issues.closed_at=`cerrada <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at=`cerró <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`reabierta <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`reabrió <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`mencionada esta incidencia en un commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`mencionada esta incidencia en un commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.poster=Autor issues.poster=Autor
issues.admin=Administrador issues.collaborator=Colaborador
issues.owner=Propietario issues.owner=Propietario
issues.sign_up_for_free=Registro gratuito issues.sign_up_for_free=Registro gratuito
issues.sign_in_require_desc=para unirse a esta conversación. ¿Ya dispone de una cuenta? <a href="%s">Inicie sesión para comentar</a> issues.sign_in_require_desc=para unirse a esta conversación. ¿Ya dispone de una cuenta? <a href="%s">Inicie sesión para comentar</a>
@ -494,9 +488,10 @@ issues.label_modify=Edición de Etiqueta
issues.label_deletion=Borrado de Etiqueta issues.label_deletion=Borrado de Etiqueta
issues.label_deletion_desc=Al borrar la etiqueta su información será eliminada de todas las incidencias relacionadas. Desea continuar? issues.label_deletion_desc=Al borrar la etiqueta su información será eliminada de todas las incidencias relacionadas. Desea continuar?
issues.label_deletion_success=Etiqueta borrada con éxito! issues.label_deletion_success=Etiqueta borrada con éxito!
issues.num_participants=%d participantes
pulls.new=Nuevo Pull Request pulls.new=Nuevo Pull Request
pulls.compare_changes=Comparar Cambios pulls.compare_changes=Comparar cambios
pulls.compare_changes_desc=Comparar dos ramas y generar un pull request con las diferencias. pulls.compare_changes_desc=Comparar dos ramas y generar un pull request con las diferencias.
pulls.compare_base=base pulls.compare_base=base
pulls.compare_compare=comparar con pulls.compare_compare=comparar con
@ -510,7 +505,7 @@ pulls.merged_title_desc=fusionados %[1]d commits de <code>%[2]s</code> en <code>
pulls.tab_conversation=Conversación pulls.tab_conversation=Conversación
pulls.tab_commits=Commits pulls.tab_commits=Commits
pulls.tab_files=Archivos modificados pulls.tab_files=Archivos modificados
pulls.reopen_to_merge=Por favor reabra este pull request para proceder con la operación de fusionado. pulls.reopen_to_merge=Por favor reabra este Pull Request para proceder con la operación de fusionado.
pulls.merged=Fuisionado pulls.merged=Fuisionado
pulls.has_merged=¡Este pull request se ha completado con éxito! pulls.has_merged=¡Este pull request se ha completado con éxito!
pulls.data_broken=Los datos de este pull request ya no están disponibles porque se ha eliminado la información del fork. pulls.data_broken=Los datos de este pull request ya no están disponibles porque se ha eliminado la información del fork.
@ -557,6 +552,8 @@ wiki.save_page=Guardar página
wiki.last_commit_info=%s editó esta página %s wiki.last_commit_info=%s editó esta página %s
wiki.edit_page_button=Editar wiki.edit_page_button=Editar
wiki.new_page_button=Nueva página wiki.new_page_button=Nueva página
wiki.delete_page_button=Eliminar página
wiki.delete_page_notice_1=Esto eliminará la página <code>"%s"</code>. Por favor, asegúrate de que es lo que quieres.
wiki.page_already_exists=Ya existe una página con el mismo nombre. wiki.page_already_exists=Ya existe una página con el mismo nombre.
wiki.pages=Páginas wiki.pages=Páginas
wiki.last_updated=Última actualización %s wiki.last_updated=Última actualización %s
@ -567,7 +564,7 @@ settings.collaboration=Colaboración
settings.hooks=Webhooks settings.hooks=Webhooks
settings.githooks=Git Hooks settings.githooks=Git Hooks
settings.basic_settings=Configuración Básica settings.basic_settings=Configuración Básica
settings.site=Sitio Oficial settings.site=Sitio oficial
settings.update_settings=Actualizar configuración settings.update_settings=Actualizar configuración
settings.change_reponame_prompt=Este cambio afectará a los enlaces al repositorio. settings.change_reponame_prompt=Este cambio afectará a los enlaces al repositorio.
settings.advanced_settings=Ajustes avanzados settings.advanced_settings=Ajustes avanzados
@ -581,14 +578,23 @@ settings.tracker_url_format=Formato URL del tracker de incidencias externo
settings.tracker_url_format_desc=Puedes usar las plantillas <code>{user} {repo} {index}</code> para el nombre de usuario, nombre del repositorio e índice de la incidencia. settings.tracker_url_format_desc=Puedes usar las plantillas <code>{user} {repo} {index}</code> para el nombre de usuario, nombre del repositorio e índice de la incidencia.
settings.pulls_desc=Habilitar Pull Requests para aceptar contribuciones públicas settings.pulls_desc=Habilitar Pull Requests para aceptar contribuciones públicas
settings.danger_zone=Zona de Peligro settings.danger_zone=Zona de Peligro
settings.transfer=Transferir la Propiedad
settings.transfer_desc=Transferir este repositorio a otro usuario u organización donde tengas permisos de administración.
settings.new_owner_has_same_repo=El nuevo propietario tiene un repositorio con el mismo nombre. settings.new_owner_has_same_repo=El nuevo propietario tiene un repositorio con el mismo nombre.
settings.delete=Eliminar este Repositorio settings.convert=Convertir en un repositorio normal
settings.delete_desc=Una vez has eliminado un repositorio, no hay vuelta atrás. Por favor, asegúrate de que es lo que quieres. settings.convert_desc=Puedes convertir este repositorio en un repositorio normal. Este cambio no se puede deshacer.
settings.convert_notices_1=- Esta operación convertirá este repositorio espejo en un repositorio normal y no podrá deshacerse.
settings.convert_confirm=Confirmar conversión
settings.convert_succeed=El repositorio ha sido convertido en normal satisfactoriamente.
settings.transfer=Transferir la propiedad
settings.transfer_desc=Transferir este repositorio a otro usuario u organización donde tengas permisos de administración.
settings.transfer_notices_1=- Perderá el permiso de acceso si el nuevo propietario es otro usuario. settings.transfer_notices_1=- Perderá el permiso de acceso si el nuevo propietario es otro usuario.
settings.transfer_notices_2=- Conservará el privilegio de acceso si el nuevo propietario es una organización y usted es uno de los propietarios de dicha organización. settings.transfer_notices_2=- Conservará el privilegio de acceso si el nuevo propietario es una organización y usted es uno de los propietarios de dicha organización.
settings.transfer_form_title=Por favor introduzca esta información para confirmar la operación: settings.transfer_form_title=Por favor introduzca esta información para confirmar la operación:
settings.wiki_delete=Eliminar datos de la wiki
settings.wiki_delete_desc=Una vez borrados los datos de la wiki no habrá vuelta atrás. Por favor, asegúrate de que es lo que quieres.
settings.wiki_delete_notices_1=- Esto eliminará y deshabilitará la wiki para %s
settings.wiki_deletion_success=Los datos de la wiki del repositorio han sido borrados correctamente.
settings.delete=Eliminar este repositorio
settings.delete_desc=Una vez has eliminado un repositorio, no hay vuelta atrás. Por favor, asegúrate de que es lo que quieres.
settings.delete_notices_1=- Esta operación <strong>NO PUEDE</strong> revertirse. settings.delete_notices_1=- Esta operación <strong>NO PUEDE</strong> revertirse.
settings.delete_notices_2=- Esta operación eliminará de manera permanente todo el contenido de este repositorio, incluyendo los datos de git, las incidencias, los comentarios y los permisos de acceso de los colaboradores. settings.delete_notices_2=- Esta operación eliminará de manera permanente todo el contenido de este repositorio, incluyendo los datos de git, las incidencias, los comentarios y los permisos de acceso de los colaboradores.
settings.delete_notices_fork_1=- Si este repositorio es público, todos los forks se convertirán en repositorios independientes tras el borrado. settings.delete_notices_fork_1=- Si este repositorio es público, todos los forks se convertirán en repositorios independientes tras el borrado.
@ -600,10 +606,14 @@ settings.transfer_owner=Nuevo Propietario
settings.make_transfer=Transferir settings.make_transfer=Transferir
settings.transfer_succeed=La propiedad del repositorio ha sido transferida exitosamente. settings.transfer_succeed=La propiedad del repositorio ha sido transferida exitosamente.
settings.confirm_delete=Confirmar eliminación settings.confirm_delete=Confirmar eliminación
settings.add_collaborator=Añadir Nuevo Colaborador settings.add_collaborator=Añadir nuevo colaborador
settings.add_collaborator_success=Se ha añadido el nuevo colaborador. settings.add_collaborator_success=El nuevo colaborador ha sido añadido.
settings.remove_collaborator_success=Se ha eliminado el colaborador. settings.delete_collaborator=Eliminar
settings.collaborator_deletion=Eliminar colaborador
settings.collaborator_deletion_desc=Este usuario no podrá colaborar en este repositorio tras eliminarlo. ¿Desea continuar?
settings.remove_collaborator_success=El colaborador ha sido eliminado.
settings.search_user_placeholder=Buscar usuario... settings.search_user_placeholder=Buscar usuario...
settings.org_not_allowed_to_be_collaborator=Las organizaciones no tiene permitido ser añadidas como colaboradores.
settings.user_is_org_member=El usuario es miembro de la organización, no puede ser añadido como colaborador. settings.user_is_org_member=El usuario es miembro de la organización, no puede ser añadido como colaborador.
settings.add_webhook=Añadir Webhook settings.add_webhook=Añadir Webhook
settings.hooks_desc=Los Webhooks permiten a servicios externos recibir notificaciones cuando sucedan ciertos eventos en Gogs. Cuando sucedan los eventos especificados, enviaremos una petición POST a cada una de las URLs indicadas. Para obtener más información, consulta nuestra <a target="_blank" href="%s">Guía de Webhooks</a>. settings.hooks_desc=Los Webhooks permiten a servicios externos recibir notificaciones cuando sucedan ciertos eventos en Gogs. Cuando sucedan los eventos especificados, enviaremos una petición POST a cada una de las URLs indicadas. Para obtener más información, consulta nuestra <a target="_blank" href="%s">Guía de Webhooks</a>.
@ -625,7 +635,7 @@ settings.githook_content=Contenido del Hook
settings.update_githook=Actualizar Hook settings.update_githook=Actualizar Hook
settings.add_webhook_desc=Enviaremos una petición <code>POST</code> a la siguiente URL con los detalles de cualquier evento suscrito. También puedes especificar qué formato de datos te gustaría recibir (JSON, <code>x-www-form-urlencoded</code>, <em>etc</em>). Puedes encontrar más información en la <a target="_blank" href="%s">Guía de Webhooks</a>. settings.add_webhook_desc=Enviaremos una petición <code>POST</code> a la siguiente URL con los detalles de cualquier evento suscrito. También puedes especificar qué formato de datos te gustaría recibir (JSON, <code>x-www-form-urlencoded</code>, <em>etc</em>). Puedes encontrar más información en la <a target="_blank" href="%s">Guía de Webhooks</a>.
settings.payload_url=URL de Payload settings.payload_url=URL de Payload
settings.content_type=Tipo de Contenido settings.content_type=Tipo de contenido
settings.secret=Secreto settings.secret=Secreto
settings.slack_username=Nombre de usuario settings.slack_username=Nombre de usuario
settings.slack_icon_url=URL de icono settings.slack_icon_url=URL de icono
@ -667,12 +677,12 @@ diff.browse_source=Explorar el Código
diff.parent=padre diff.parent=padre
diff.commit=commit diff.commit=commit
diff.data_not_available=Los datos del Diff no están disponibles. diff.data_not_available=Los datos del Diff no están disponibles.
diff.show_diff_stats=Mostrar Estadísticas de Diff diff.show_diff_stats=Mostrar estadísticas de diff
diff.show_split_view=Dividir vista diff.show_split_view=Dividir vista
diff.show_unified_view=Unificar vista diff.show_unified_view=Unificar vista
diff.stats_desc=Se han <strong>modificado %d ficheros</strong> con <strong>%d adiciones</strong> y <strong>%d borrados</strong> diff.stats_desc=Se han <strong>modificado %d ficheros</strong> con <strong>%d adiciones</strong> y <strong>%d borrados</strong>
diff.bin=BIN diff.bin=BIN
diff.view_file=Ver Fichero diff.view_file=Ver fichero
release.releases=Releases release.releases=Releases
release.new_release=Nueva Release release.new_release=Nueva Release
@ -690,7 +700,7 @@ release.tag_helper=Escoge una etiqueta o crea una nueva al publicar.
release.title=Título release.title=Título
release.content=Contenido release.content=Contenido
release.write=Escribir release.write=Escribir
release.preview=Vista Previa release.preview=Vista previa
release.loading=Cargando... release.loading=Cargando...
release.prerelease_desc=Esta es una pre-release release.prerelease_desc=Esta es una pre-release
release.prerelease_helper=Esta release está marcada como no apta para producción. release.prerelease_helper=Esta release está marcada como no apta para producción.
@ -706,8 +716,8 @@ release.tag_name_already_exist=Ya existe una Release con esta etiqueta.
release.downloads=Descargas release.downloads=Descargas
[org] [org]
org_name_holder=Nombre de la Organización org_name_holder=Nombre de la organización
org_full_name_holder=Nombre de la organización org_full_name_holder=Nombre completo de la organización
org_name_helper=Los grandes nombres de organizaciones son cortos y memorables. org_name_helper=Los grandes nombres de organizaciones son cortos y memorables.
create_org=Crear Organización create_org=Crear Organización
repo_updated=Actualizado repo_updated=Actualizado
@ -716,9 +726,9 @@ invite_someone=Invitar a alguien
teams=Equipos teams=Equipos
lower_members=miembros lower_members=miembros
lower_repositories=repositorios lower_repositories=repositorios
create_new_team=Crear un Nuevo Equipo create_new_team=Crear un nuevo equipo
org_desc=Descripción org_desc=Descripción
team_name=Nombre del Equipo team_name=Nombre del equipo
team_desc=Descripción team_desc=Descripción
team_name_helper=Utiliza este nombre para mencionar a este equipo en las conversaciones. team_name_helper=Utiliza este nombre para mencionar a este equipo en las conversaciones.
team_desc_helper=¿En qué consiste este equipo? team_desc_helper=¿En qué consiste este equipo?
@ -729,8 +739,8 @@ form.name_pattern_not_allowed=El patrón de nombre de la organización '%s' no e
settings=Configuración settings=Configuración
settings.options=Opciones settings.options=Opciones
settings.full_name=Nombre Completo settings.full_name=Nombre completo
settings.website=Página Web settings.website=Página web
settings.location=Localización settings.location=Localización
settings.update_settings=Actualizar configuración settings.update_settings=Actualizar configuración
settings.update_setting_success=La configuración de la organización se ha actualizado correctamente. settings.update_setting_success=La configuración de la organización se ha actualizado correctamente.
@ -763,24 +773,24 @@ teams.read_access=Acceso de Lectura
teams.read_access_helper=Este equipo podrá ver y clonar sus repositorios. teams.read_access_helper=Este equipo podrá ver y clonar sus repositorios.
teams.write_access=Acceso de Escritura teams.write_access=Acceso de Escritura
teams.write_access_helper=Este equipo podrá leer sus repositorios, así como hacer push en ellos. teams.write_access_helper=Este equipo podrá leer sus repositorios, así como hacer push en ellos.
teams.admin_access=Acceso de Administrador teams.admin_access=Acceso de administrador
teams.admin_access_helper=Este equipo podrá hacer push/pull en sus repositorios, así como añadir colaboradores a ellos. teams.admin_access_helper=Este equipo podrá hacer push/pull en sus repositorios, así como añadir colaboradores a ellos.
teams.no_desc=Este equipo no tiene descripción teams.no_desc=Este equipo no tiene descripción
teams.settings=Configuración teams.settings=Configuración
teams.owners_permission_desc=Los propietarios tienen acceso completo a <strong>todos los repositorios</strong> y tienen <strong>derechos de administración</strong> en la organización. teams.owners_permission_desc=Los propietarios tienen acceso completo a <strong>todos los repositorios</strong> y tienen <strong>derechos de administración</strong> en la organización.
teams.members=Miembros del Equipo teams.members=Miembros del equipo
teams.update_settings=Actualizar configuración teams.update_settings=Actualizar configuración
teams.delete_team=Borrar este Equipo teams.delete_team=Eliminar este equipo
teams.add_team_member=Añadir Miembro al Equipo teams.add_team_member=Añadir miembro al equipo
teams.delete_team_title=Eliminar Equipo teams.delete_team_title=Eliminar equipo
teams.delete_team_desc=Este equipo va a ser eliminado, ¿seguro que quieres continuar? Los miembros de este equipo pueden perder acceso a algunos repositorios. teams.delete_team_desc=Este equipo va a ser eliminado, ¿seguro que quieres continuar? Los miembros de este equipo pueden perder acceso a algunos repositorios.
teams.delete_team_success=El Equipo se ha eliminado correctamente. teams.delete_team_success=El equipo ha sido eliminado correctamente.
teams.read_permission_desc=Este equipo tiene permisos de <strong>Lectura</strong>: sus miembros pueden ver y clonar los repositorios del equipo. teams.read_permission_desc=Este equipo tiene permisos de <strong>Lectura</strong>: sus miembros pueden ver y clonar los repositorios del equipo.
teams.write_permission_desc=Este equipo tiene permisos de <strong>Escritura</strong>: sus miembros pueden leer y hacer push a los repositorios del equipo. teams.write_permission_desc=Este equipo tiene permisos de <strong>Escritura</strong>: sus miembros pueden leer y hacer push a los repositorios del equipo.
teams.admin_permission_desc=Este equipo tiene permisos de <strong>Administración</strong>: sus miembros pueden leer, hacer push y añadir colaboradores a los repositorios del equipo. teams.admin_permission_desc=Este equipo tiene permisos de <strong>Administración</strong>: sus miembros pueden leer, hacer push y añadir colaboradores a los repositorios del equipo.
teams.repositories=Repositorios del Equipo teams.repositories=Repositorios del equipo
teams.search_repo_placeholder=Buscar repositorio... teams.search_repo_placeholder=Buscar repositorio...
teams.add_team_repository=Añadir Repositorio al Equipo teams.add_team_repository=Añadir repositorio al equipo
teams.remove_repo=Eliminar teams.remove_repo=Eliminar
teams.add_nonexistent_repo=El repositorio que estás intentando añadir no existe, por favor, créalo primero. teams.add_nonexistent_repo=El repositorio que estás intentando añadir no existe, por favor, créalo primero.
@ -791,7 +801,7 @@ organizations=Organizaciones
repositories=Repositorios repositories=Repositorios
authentication=Autenticaciones authentication=Autenticaciones
config=Configuración config=Configuración
notices=Avisos del Sistema notices=Notificaciones del sistema
monitor=Monitorización monitor=Monitorización
first_page=Primera first_page=Primera
last_page=Última last_page=Última
@ -801,7 +811,7 @@ dashboard.statistic=Estadísticas
dashboard.operations=Operaciones dashboard.operations=Operaciones
dashboard.system_status=Estado del Monitor del Sistema dashboard.system_status=Estado del Monitor del Sistema
dashboard.statistic_info=La base de datos de Gogs contiene <b>%d</b> usuarios, <b>%d</b> organizaciones, <b>%d</b> claves públicas, <b>%d</b> repositorios, <b>%d</b> vigilados, <b>%d</b> destacados, <b>%d</b> acciones, <b>%d</b> accesos, <b>%d</b> incidencias, <b>%d</b> comentarios, <b>%d</b> cuentas de redes sociales, <b>%d</b> seguidores, <b>%d</b> mirrors, <b>%d</b> releases, <b>%d</b> fuentes de login, <b>%d</b> webhooks, <b>%d</b> milestones, <b>%d</b> etiquetas, <b>%d</b> hooks, <b>%d</b> equipos, <b>%d</b> tareas actualizadas, <b>%d</b> adjuntos. dashboard.statistic_info=La base de datos de Gogs contiene <b>%d</b> usuarios, <b>%d</b> organizaciones, <b>%d</b> claves públicas, <b>%d</b> repositorios, <b>%d</b> vigilados, <b>%d</b> destacados, <b>%d</b> acciones, <b>%d</b> accesos, <b>%d</b> incidencias, <b>%d</b> comentarios, <b>%d</b> cuentas de redes sociales, <b>%d</b> seguidores, <b>%d</b> mirrors, <b>%d</b> releases, <b>%d</b> fuentes de login, <b>%d</b> webhooks, <b>%d</b> milestones, <b>%d</b> etiquetas, <b>%d</b> hooks, <b>%d</b> equipos, <b>%d</b> tareas actualizadas, <b>%d</b> adjuntos.
dashboard.operation_name=Nombre de la Operación dashboard.operation_name=Nombre de la operación
dashboard.operation_switch=Interruptor dashboard.operation_switch=Interruptor
dashboard.operation_run=Ejecutar dashboard.operation_run=Ejecutar
dashboard.clean_unbind_oauth=Limpiar solicitudes de OAuth sin confirmar dashboard.clean_unbind_oauth=Limpiar solicitudes de OAuth sin confirmar
@ -818,13 +828,15 @@ dashboard.resync_all_sshkeys=Reescribir el fichero '.ssh/authorized_keys'(atenci
dashboard.resync_all_sshkeys_success=Todas las claves públicas se han reescrito correctamente. dashboard.resync_all_sshkeys_success=Todas las claves públicas se han reescrito correctamente.
dashboard.resync_all_update_hooks=Reescribir todos los hooks de actualización de los repositorios (necesario cuando se modifica la ruta de configuración personalizada) dashboard.resync_all_update_hooks=Reescribir todos los hooks de actualización de los repositorios (necesario cuando se modifica la ruta de configuración personalizada)
dashboard.resync_all_update_hooks_success=Todos los hooks de actualización de los repositorios se han reescrito correctamente. dashboard.resync_all_update_hooks_success=Todos los hooks de actualización de los repositorios se han reescrito correctamente.
dashboard.reinit_missing_repos=Reinicializar todos los registros del repositorio que tienen archivos Git eliminados
dashboard.reinit_missing_repos_success=Todos los registros del repositorio con archivos Git eliminados han sido reinicializados con éxito.
dashboard.server_uptime=Tiempo de actividad del servidor dashboard.server_uptime=Tiempo de actividad del servidor
dashboard.current_goroutine=Gorutinas actuales dashboard.current_goroutine=Gorutinas actuales
dashboard.current_memory_usage=Uso de memoria actual dashboard.current_memory_usage=Uso de memoria actual
dashboard.total_memory_allocated=Total de Memoria Reservada dashboard.total_memory_allocated=Total de Memoria Reservada
dashboard.memory_obtained=Memoria Obtenida dashboard.memory_obtained=Memoria Obtenida
dashboard.pointer_lookup_times=Tiempos de Búsqueda de Punteros dashboard.pointer_lookup_times=Tiempos de búsqueda de punteros
dashboard.memory_allocate_times=Tiempos de Reserva de Memoria dashboard.memory_allocate_times=Tiempos de Reserva de Memoria
dashboard.memory_free_times=Tiempos de Liberado de Memoria dashboard.memory_free_times=Tiempos de Liberado de Memoria
dashboard.current_heap_usage=Uso de Heap actual dashboard.current_heap_usage=Uso de Heap actual
@ -861,7 +873,7 @@ users.new_success=La cuenta '%s' ha sido creada con éxito.
users.edit=Editar users.edit=Editar
users.auth_source=Fuente de Autenticación users.auth_source=Fuente de Autenticación
users.local=Local users.local=Local
users.auth_login_name=Nombre de Inicio de Sesión de Autenticación users.auth_login_name=Nombre de Inicio de sesión de autenticación
users.password_helper=Deje el campo vacío si no desea cambiar la contraseña. users.password_helper=Deje el campo vacío si no desea cambiar la contraseña.
users.update_profile_success=El perfil de la cuenta se ha actualizado correctamente. users.update_profile_success=El perfil de la cuenta se ha actualizado correctamente.
users.edit_account=Editar Cuenta users.edit_account=Editar Cuenta
@ -890,36 +902,37 @@ repos.watches=Vigilantes
repos.stars=Estrellas repos.stars=Estrellas
repos.issues=Incidencias repos.issues=Incidencias
auths.auth_manage_panel=Panel de Administración de Autenticación auths.auth_manage_panel=Panel de administración de autenticación
auths.new=Añadir Nuevo Origen auths.new=Añadir nuevo origen
auths.name=Nombre auths.name=Nombre
auths.type=Tipo auths.type=Tipo
auths.enabled=Activo auths.enabled=Activo
auths.updated=Actualizado auths.updated=Actualizado
auths.auth_type=Tipo de Autenticación auths.auth_type=Tipo de autenticación
auths.auth_name=Nombre de Autenticación auths.auth_name=Nombre de autenticación
auths.domain=Dominio auths.domain=Dominio
auths.host=Host auths.host=Host
auths.port=Puerto auths.port=Puerto
auths.bind_dn=Bind DN auths.bind_dn=Bind DN
auths.bind_password=Contraseña Bind auths.bind_password=Contraseña Bind
auths.bind_password_helper=Advertencia: La contraseña se almacena como texto plano. No utilice una cuenta con privilegios elevados. auths.bind_password_helper=Advertencia: La contraseña se almacena como texto plano. No utilice una cuenta con privilegios elevados.
auths.user_base=Base de Búsqueda de Usuarios auths.user_base=Base de búsqueda de usuarios
auths.user_dn=DN de Usuario auths.user_dn=DN de Usuario
auths.attribute_username=Atributo de nombre de usuario auths.attribute_username=Atributo de nombre de usuario
auths.attribute_username_placeholder=Dejar vacío para usar el campo de inicio de sesión como nombre de usuario. auths.attribute_username_placeholder=Dejar vacío para usar el campo de inicio de sesión como nombre de usuario.
auths.attribute_name=Atributo nombre auths.attribute_name=Atributo nombre
auths.attribute_surname=Atributo apellido auths.attribute_surname=Atributo apellido
auths.attribute_mail=Atributo correo electrónico auths.attribute_mail=Atributo correo electrónico
auths.filter=Filtro de Usuario auths.attributes_in_bind=Buscar atributos en el contexto del Bind DN
auths.admin_filter=Filtro de Aministrador auths.filter=Filtro de usuario
auths.admin_filter=Filtro de aministrador
auths.ms_ad_sa=Ms Ad SA auths.ms_ad_sa=Ms Ad SA
auths.smtp_auth=Tipo de Autenticación SMTP auths.smtp_auth=Tipo de autenticación SMTP
auths.smtphost=SMTP Host auths.smtphost=SMTP Host
auths.smtpport=Puerto SMTP auths.smtpport=Puerto SMTP
auths.allowed_domains=Dominios Permitidos auths.allowed_domains=Dominios Permitidos
auths.allowed_domains_helper=Deje el campo vacío si no desea restringir ningún dominio. Para restringir más de uno, separe los dominios con una coma ','. auths.allowed_domains_helper=Deje el campo vacío si no desea restringir ningún dominio. Para restringir más de uno, separe los dominios con una coma ','.
auths.enable_tls=Habilitar Cifrado TLS auths.enable_tls=Habilitar cifrado TLS
auths.skip_tls_verify=Omitir la verificación TLS auths.skip_tls_verify=Omitir la verificación TLS
auths.pam_service_name=Nombre del Servicio PAM auths.pam_service_name=Nombre del Servicio PAM
auths.enable_auto_register=Hablilitar Auto-Registro auths.enable_auto_register=Hablilitar Auto-Registro
@ -932,9 +945,10 @@ auths.update=Actualizar la configuración de autenticación
auths.delete=Eliminar Autenticación auths.delete=Eliminar Autenticación
auths.delete_auth_title=Borrado de autenticación auths.delete_auth_title=Borrado de autenticación
auths.delete_auth_desc=Esta autenticación será eliminada. ¿Deseas continuar? auths.delete_auth_desc=Esta autenticación será eliminada. ¿Deseas continuar?
auths.still_in_used=Este método de autentificación aún es utilizado por algunos usuarios, por favor elimine o convierta estos usuarios a otro tipo de autentificación.
auths.deletion_success=¡La autenticación ha sido eliminada con éxito! auths.deletion_success=¡La autenticación ha sido eliminada con éxito!
config.server_config=Configuración del Servidor config.server_config=Configuración del servidor
config.app_name=Nombre de la Aplicación config.app_name=Nombre de la Aplicación
config.app_ver=Versión de la Aplicación config.app_ver=Versión de la Aplicación
config.app_url=URL de la Aplicación config.app_url=URL de la Aplicación
@ -948,21 +962,33 @@ config.static_file_root_path=Ruta de los Ficheros Estáticos
config.log_file_root_path=Ruta de los Ficheros de Log config.log_file_root_path=Ruta de los Ficheros de Log
config.script_type=Tipo de Script config.script_type=Tipo de Script
config.reverse_auth_user=Autenticación Inversa de Usuario config.reverse_auth_user=Autenticación Inversa de Usuario
config.ssh_config=Configuración SSH
config.ssh_enabled=Habilitado
config.ssh_start_builtin_server=Iniciar servidor integrado
config.ssh_domain=Dominio
config.ssh_port=Puerto
config.ssh_listen_port=Puerto de escucha
config.ssh_root_path=Ruta raíz
config.ssh_key_test_path=Ruta de la clave de prueba
config.ssh_keygen_path=Ruta del generador de claves ('ssh-keygen')
config.ssh_minimum_key_size_check=Tamaño mínimo de la clave de verificación
config.ssh_minimum_key_sizes=Tamaños de clave mínimos
config.db_config=Configuración de la Base de Datos config.db_config=Configuración de la Base de Datos
config.db_type=Tipo config.db_type=Tipo
config.db_host=Host config.db_host=Host
config.db_name=Nombre config.db_name=Nombre
config.db_user=Usuario config.db_user=Usuario
config.db_ssl_mode=Modo SSL config.db_ssl_mode=Modo SSL
config.db_ssl_mode_helper=(solo para "postgres") config.db_ssl_mode_helper=(sólo para "postgres")
config.db_path=Ruta config.db_path=Ruta
config.db_path_helper=(para "sqlite3" y "tidb") config.db_path_helper=(para "sqlite3" y "tidb")
config.service_config=Configuración del Servicio config.service_config=Configuración del servicio
config.register_email_confirm=Solicitar Confirmación por Correo Electrónico config.register_email_confirm=Solicitar Confirmación por Correo Electrónico
config.disable_register=Deshabilitar el Registro config.disable_register=Deshabilitar el Registro
config.show_registration_button=Mostrar Botón de Registro config.show_registration_button=Mostrar Botón de Registro
config.require_sign_in_view=Solicitar la Vista de Inicio de Sesión config.require_sign_in_view=Solicitar la Vista de Inicio de Sesión
config.enable_cache_avatar=Activar la Caché de Avatar
config.mail_notify=Notificación por Correo Electrónico config.mail_notify=Notificación por Correo Electrónico
config.disable_key_size_check=Deshabilitar la comprobación de Tamaño Mínimo de Clave config.disable_key_size_check=Deshabilitar la comprobación de Tamaño Mínimo de Clave
config.enable_captcha=Activar Captcha config.enable_captcha=Activar Captcha
@ -972,12 +998,15 @@ config.webhook_config=Configuración de Webhooks
config.queue_length=Tamaño de Cola de Envío config.queue_length=Tamaño de Cola de Envío
config.deliver_timeout=Timeout de Entrega config.deliver_timeout=Timeout de Entrega
config.skip_tls_verify=Omitir la Verificación TLS config.skip_tls_verify=Omitir la Verificación TLS
config.mailer_config=Configuración del Mailer config.mailer_config=Configuración del servidor de correo
config.mailer_enabled=Activado config.mailer_enabled=Activado
config.mailer_disable_helo=Desactivar HELO config.mailer_disable_helo=Desactivar HELO
config.mailer_name=Nombre config.mailer_name=Nombre
config.mailer_host=Host config.mailer_host=Host
config.mailer_user=Usuario config.mailer_user=Usuario
config.send_test_mail=Enviar email de prueba
config.test_mail_failed=Fallo al enviar el email de prueba a '%s': %v
config.test_mail_sent=El email de prueba ha sido enviado a '%s'.
config.oauth_config=Configuración OAuth config.oauth_config=Configuración OAuth
config.oauth_enabled=Activado config.oauth_enabled=Activado
config.cache_config=Configuración de la Caché config.cache_config=Configuración de la Caché
@ -1029,7 +1058,11 @@ create_repo=creó el repositorio <a href="%s">%s</a>
rename_repo=repositorio renombrado de <code>%[1]s</code> a <a href="%[2]s">%[3]s</a> rename_repo=repositorio renombrado de <code>%[1]s</code> a <a href="%[2]s">%[3]s</a>
commit_repo=hizo push a <a href="%[1]s/src/%[2]s">%[3]s</a> en <a href="%[1]s">%[4]s</a> commit_repo=hizo push a <a href="%[1]s/src/%[2]s">%[3]s</a> en <a href="%[1]s">%[4]s</a>
create_issue=`incidencia abierta <a href="%s/issues/%s">%s#%[2]s</a>` create_issue=`incidencia abierta <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`cerró la incidencia <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`reabrió la incidencia <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`creado pull request <a href="%s/pulls/%s">%s#%[2]s</a>` create_pull_request=`creado pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request=`cerró el pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`reabrió el pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue=`comentó en la incidencia <a href="%s/issues/%s">%s#%[2]s</a>` comment_issue=`comentó en la incidencia <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request=`fusionado pull request <a href="%s/pulls/%s">%s#%[2]s</a>` merge_pull_request=`fusionado pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo=transfirió el repositorio <code>%s</code> a <a href="%s">%s</a> transfer_repo=transfirió el repositorio <code>%s</code> a <a href="%s">%s</a>

1098
conf/locale/locale_fi-FI.ini

File diff suppressed because it is too large Load Diff

273
conf/locale/locale_fr-FR.ini

@ -26,31 +26,24 @@ captcha=Captcha
repository=Dépôt repository=Dépôt
organization=Organisation organization=Organisation
mirror=Miroir mirror=Miroir
new_repo=Nouveau Dépôt new_repo=Nouveau dépôt
new_migrate=Nouvelle migration new_migrate=Nouvelle migration
new_mirror=Nouveau miroir new_mirror=Nouveau miroir
new_fork=Nouveau Fork new_fork=Nouveau fork
new_org=Nouvelle Organisation new_org=Nouvelle organisation
manage_org=Gérer les Organisations manage_org=Gérer les organisations
admin_panel=Administration admin_panel=Administration
account_settings=Paramètres du Compte account_settings=Paramètres du compte
settings=Paramètres settings=Paramètres
your_profile=Votre profil your_profile=Votre profil
your_settings=Vos paramètres your_settings=Vos paramètres
news_feed=Flux d'actualités activities=Activités
pull_requests=Pull Requests pull_requests=Pull Requests
issues=Problèmes issues=Tickets
cancel=Annuler cancel=Annuler
[search]
search=Rechercher...
repository=Dépôt
user=Utilisateur
issue=Problème
code=Code
[install] [install]
install=Installation install=Installation
title=Instructions pour la première exécution title=Instructions pour la première exécution
@ -65,18 +58,18 @@ db_name=Nom de base de données
db_helper=Veuillez utiliser le moteur INNODB avec le jeu de caractères utf8_general_ci pour MySQL. db_helper=Veuillez utiliser le moteur INNODB avec le jeu de caractères utf8_general_ci pour MySQL.
ssl_mode=Mode SSL ssl_mode=Mode SSL
path=Chemin path=Chemin
sqlite_helper=Le chemin du fichier de la base de données SQLite3 ou TiDB. sqlite_helper=Le chemin du fichier de base de données SQLite3 ou TiDB. <br>Utilisez un chemin absolu lorsque vous démarrez en tant que service.
err_empty_db_path=Le chemin de la base de données SQLite3 ou TiDB ne peut être vide. err_empty_db_path=Le chemin de la base de données SQLite3 ou TiDB ne peut être vide.
err_invalid_tidb_name=Le nom de la base de données TiDB ne peut contenir les caractères "." ou "-". err_invalid_tidb_name=Le nom de la base de données TiDB ne peut contenir les caractères "." ou "-".
no_admin_and_disable_registration=Vous ne pouvez pas désactiver l'enregistrement sans créer un compte administrateur. no_admin_and_disable_registration=Vous ne pouvez pas désactiver l'enregistrement sans créer un compte administrateur.
err_empty_admin_password=Le mot de passe du compte administrateur ne peut être vide. err_empty_admin_password=Le mot de passe du compte administrateur ne peut être vide.
general_title=Paramètres Généraux de Gogs general_title=Paramètres généraux de Gogs
app_name=Nom de l'application app_name=Nom de l'application
app_name_helper=Inscrivez fièrement le nom de votre organisation ici ! app_name_helper=Inscrivez fièrement le nom de votre organisation ici !
repo_path=Emplacement Racine du Dépôt repo_path=Emplacement racine des dépôts
repo_path_helper=Tous les dépôts Git distants seront sauvegardés ici. repo_path_helper=Tous les dépôts Git distants seront sauvegardés ici.
run_user=Entrer un Utilisateur run_user=Utilisateur système
run_user_helper=L'utilisateur doit avoir accès à la racine des dépôts et exécuter Gogs. run_user_helper=L'utilisateur doit avoir accès à la racine des dépôts et exécuter Gogs.
domain=Domaine domain=Domaine
domain_helper=Cela affecte les doublons d'URL SSH. domain_helper=Cela affecte les doublons d'URL SSH.
@ -84,8 +77,10 @@ ssh_port=Port SSH
ssh_port_helper=Numéro de port utilisé par votre serveur SSH, le laisser vide pour désactiver la fonctionnalité. ssh_port_helper=Numéro de port utilisé par votre serveur SSH, le laisser vide pour désactiver la fonctionnalité.
http_port=Port HTTP http_port=Port HTTP
http_port_helper=Numéro de port que l'application écoutera. http_port_helper=Numéro de port que l'application écoutera.
app_url=URL de l'Application app_url=URL de l'application
app_url_helper=Cela affecte les doublons d'URL HTTP/HTTPS et le contenu d'e-mail. app_url_helper=Cela affecte les doublons d'URL HTTP/HTTPS et le contenu d'e-mail.
log_root_path=Chemin des fichiers log
log_root_path_helper=Répertoire d'écriture des fichiers de log.
optional_title=Paramètres facultatifs optional_title=Paramètres facultatifs
email_title=Paramètres du Service de Messagerie email_title=Paramètres du Service de Messagerie
@ -122,13 +117,14 @@ run_user_not_match=L'utilisateur entré n'est pas l'utilisateur actuel : %s -> %
save_config_failed=La sauvegarde de la configuration a échoué : %v save_config_failed=La sauvegarde de la configuration a échoué : %v
invalid_admin_setting=Paramètres du compte administrateur invalides : %v invalid_admin_setting=Paramètres du compte administrateur invalides : %v
install_success=Bienvenue ! Nous sommes heureux que vous ayez choisi Gogs, amusez-vous et prenez soin de vous. install_success=Bienvenue ! Nous sommes heureux que vous ayez choisi Gogs, amusez-vous et prenez soin de vous.
invalid_log_root_path=Le chemin principal des fichiers logs est invalide: %v
[home] [home]
uname_holder=Nom d'Utilisateur ou E-mail uname_holder=Nom d'Utilisateur ou E-mail
password_holder=Mot de Passe password_holder=Mot de passe
switch_dashboard_context=Basculer le Contexte du Tableau de Bord switch_dashboard_context=Basculer le Contexte du Tableau de Bord
my_repos=Mes dépôts my_repos=Mes dépôts
collaborative_repos=Référentiels collaboratifs collaborative_repos=Dépôts collaboratifs
my_orgs=Mes Organisations my_orgs=Mes Organisations
my_mirrors=Mes Miroirs my_mirrors=Mes Miroirs
view_home=Voir %s view_home=Voir %s
@ -136,27 +132,29 @@ view_home=Voir %s
issues.in_your_repos=Dans vos dépôts issues.in_your_repos=Dans vos dépôts
[explore] [explore]
repos=Référentiels repos=Dépôts
users=Utilisateurs
search=Rechercher
[auth] [auth]
create_new_account=Créer un Nouveau Compte create_new_account=Créer un nouveau compte
register_hepler_msg=Déjà enregistré ? Connectez-vous ! register_hepler_msg=Déjà enregistré ? Connectez-vous !
social_register_hepler_msg=Possesseur d'un compte ? Associez-le ! social_register_hepler_msg=Déjà enregistré ? Associez-le !
disable_register_prompt=Désolé, les enregistrements ont été désactivés. Veuillez contacter l'administrateur du site. disable_register_prompt=Désolé, les enregistrements ont été désactivés. Veuillez contacter l'administrateur du site.
disable_register_mail=Désolé, la Confirmation par Mail des Enregistrements a été désactivée. disable_register_mail=Désolé, la Confirmation par Mail des Enregistrements a été désactivée.
remember_me=Se souvenir de moi remember_me=Se souvenir de moi
forgot_password=Mot de Passe oublié forgot_password=Mot de Passe oublié
forget_password=Mot de Passe oublié ? forget_password=Mot de Passe oublié ?
sign_up_now=Pas de compte ? Créer maintenant. sign_up_now=Pas de compte ? Inscrivez-vous maintenant.
confirmation_mail_sent_prompt=Un nouveau mail de confirmation à été envoyé à <b>%s</b>. Veuillez vérifier votre boîte de réception dans un délai de %d heures pour compléter votre enregistrement. confirmation_mail_sent_prompt=Un nouveau mail de confirmation à été envoyé à <b>%s</b>. Veuillez vérifier votre boîte de réception dans un délai de %d heures pour compléter votre enregistrement.
active_your_account=Activer votre Compte active_your_account=Activer votre Compte
resent_limit_prompt=Désolé, vos tentatives d'activation sont trop fréquentes. Veuillez réessayer dans 3 minutes. resent_limit_prompt=Désolé, vos tentatives d'activation sont trop fréquentes. Veuillez réessayer dans 3 minutes.
has_unconfirmed_mail=Bonjour %s, votre adresse courriel (<b>%s</b>) n'a pas été confirmée. Si vous n'avez reçu aucun courriel de confirmation ou souhaitez renouveler l'envoi, appuyez sur le bouton ci-dessous. has_unconfirmed_mail=Bonjour %s, votre adresse e-mail (<b>%s</b>) n'a pas été confirmée. Si vous n'avez reçu aucun mail de confirmation ou souhaitez renouveler l'envoi, cliquez sur le bouton ci-dessous.
resend_mail=Cliquez ici pour renvoyer un mail de confirmation resend_mail=Cliquez ici pour renvoyer un mail de confirmation
email_not_associate=Cette adresse e-mail n'est associée à aucun compte. email_not_associate=Cette adresse e-mail n'est associée à aucun compte.
send_reset_mail=Cliquez ici pour (r)envoyer le mail de réinitialisation du mot de passe send_reset_mail=Cliquez ici pour (r)envoyer le mail de réinitialisation du mot de passe
reset_password=Réinitialiser le Mot de Passe reset_password=Réinitialiser le mot de passe
invalid_code=Désolé, code de confirmation invalide ou expiré. invalid_code=Désolé, votre code de confirmation est invalide ou a expiré.
reset_password_helper=Cliquez ici pour réinitialiser votre mot de passe reset_password_helper=Cliquez ici pour réinitialiser votre mot de passe
password_too_short=Le mot de passe doit contenir 6 caractères minimum. password_too_short=Le mot de passe doit contenir 6 caractères minimum.
@ -173,11 +171,11 @@ no=Non
modify=Modifier modify=Modifier
[form] [form]
UserName=Nom d'Utilisateur UserName=Nom d'utilisateur
RepoName=Nom du dépôt RepoName=Nom du dépôt
Email=Adresse E-mail Email=Adresse E-mail
Password=Mot de Passe Password=Mot de passe
Retype=Confirmez le Mot de Passe Retype=Confirmez le mot de passe
SSHTitle=Nom de la clé SSH SSHTitle=Nom de la clé SSH
HttpsUrl=URL HTTPS HttpsUrl=URL HTTPS
PayloadUrl=URL des Données Utiles PayloadUrl=URL des Données Utiles
@ -203,7 +201,6 @@ repo_name_been_taken=Nom de dépôt déjà utilisé.
org_name_been_taken=Nom d'organisation déjà pris. org_name_been_taken=Nom d'organisation déjà pris.
team_name_been_taken=Nom d'équipe déjà pris. team_name_been_taken=Nom d'équipe déjà pris.
email_been_used=Adresse e-mail déjà utilisée. email_been_used=Adresse e-mail déjà utilisée.
illegal_team_name=Le nom de l'équipe contient des caractères interdits.
username_password_incorrect=Nom d'utilisateur ou mot de passe incorrect. username_password_incorrect=Nom d'utilisateur ou mot de passe incorrect.
enterred_invalid_repo_name=Veuillez vérifier que le nom saisi du dépôt soit correct. enterred_invalid_repo_name=Veuillez vérifier que le nom saisi du dépôt soit correct.
enterred_invalid_owner_name=Veuillez vérifier que le nom du propriétaire saisi soit correct. enterred_invalid_owner_name=Veuillez vérifier que le nom du propriétaire saisi soit correct.
@ -219,17 +216,15 @@ still_own_repo=Votre compte comporte toujours des propriétés du dépôt. Vous
still_has_org=Votre compte contient toujours au moins une adhésion à une organisation, vous devez quitter ou supprimer votre adhésion. still_has_org=Votre compte contient toujours au moins une adhésion à une organisation, vous devez quitter ou supprimer votre adhésion.
org_still_own_repo=Cette organisation comporte toujours des propriétés du dépôt. Vous devez d'abord les supprimer ou les transférer. org_still_own_repo=Cette organisation comporte toujours des propriétés du dépôt. Vous devez d'abord les supprimer ou les transférer.
still_own_user=Cette authentification a déjà servi à d'autres utilisateurs. Veuillez les déplacer puis supprimez à nouveau.
target_branch_not_exist=La branche cible n'existe pas. target_branch_not_exist=La branche cible n'existe pas.
[user] [user]
change_avatar=Changez d'avatar via gravatar.com change_avatar=Changez d'avatar via gravatar.com
change_custom_avatar=Changer votre avatar dans les paramètres change_custom_avatar=Changez votre avatar dans les paramètres
join_on=Adhéré le join_on=Inscrit le
repositories=Référentiels repositories=Dépôts
activity=Activités publiques activity=Activité publique
followers=Abonnés followers=abonnés
starred=Votés starred=Votés
following=Abonnements following=Abonnements
follow=Suivre follow=Suivre
@ -248,10 +243,10 @@ orgs=Organisations
delete=Supprimer le Compte delete=Supprimer le Compte
uid=ID d'Utilisateur uid=ID d'Utilisateur
public_profile=Profil Public public_profile=Profil public
profile_desc=Votre adresse e-mail est publique et sera utilisée pour les notifications relatives au compte, ainsi que pour toute opération Web effectuée via le site. profile_desc=Votre adresse e-mail est publique et sera utilisée pour les notifications relatives au compte, ainsi que pour toute opération Web effectuée via le site.
password_username_disabled=Les utilisateurs non-locaux n'ont pas le droit de modifier leur nom d'utilisateur. password_username_disabled=Les utilisateurs non-locaux n'ont pas le droit de modifier leur nom d'utilisateur.
full_name=Nom Complet full_name=Nom complet
website=Site Web website=Site Web
location=Localisation location=Localisation
update_profile=Valider les modifications update_profile=Valider les modifications
@ -262,15 +257,14 @@ continue=Continuer
cancel=Annuler cancel=Annuler
enable_custom_avatar=Activer l'Avatar personnalisé enable_custom_avatar=Activer l'Avatar personnalisé
enable_custom_avatar_helper=Cette option désactive l'affichage via Gravatar
choose_new_avatar=Sélectionner un nouvel avatar choose_new_avatar=Sélectionner un nouvel avatar
update_avatar=Mettre à jour l'avatar update_avatar=Mettre à jour l'avatar
delete_current_avatar=Supprimer l'avatar actuel
uploaded_avatar_not_a_image=Le fichier téléchargé n'est pas une image. uploaded_avatar_not_a_image=Le fichier téléchargé n'est pas une image.
no_custom_avatar_available=Aucun avatar personnalisé disponible, activation impossible.
update_avatar_success=Votre avatar a été mis à jour avec succès. update_avatar_success=Votre avatar a été mis à jour avec succès.
change_password=Modifier le Mot de Passe change_password=Modifier le mot de passe
old_password=Mot de Passe actuel old_password=Mot de passe actuel
new_password=Nouveau Mot de Passe new_password=Nouveau Mot de Passe
retype_new_password=Retapez le nouveau mot de passe retype_new_password=Retapez le nouveau mot de passe
password_incorrect=Mot de passe actuel incorrect. password_incorrect=Mot de passe actuel incorrect.
@ -284,12 +278,12 @@ primary=Principale
primary_email=Définir comme principale primary_email=Définir comme principale
delete_email=Supprimer delete_email=Supprimer
email_deletion=Suppression de l'adresse mél email_deletion=Suppression de l'adresse mél
email_deletion_desc=Supprimer cette adresse l supprimera les informations associées à votre compte. Voulez-vous continuer ? email_deletion_desc=Supprimer cette adresse e-mail supprimera les informations associées à votre compte. Voulez-vous continuer ?
email_deletion_success=L'adresse mél a été supprimée avec succès ! email_deletion_success=L'adresse mél a été supprimée avec succès !
add_new_email=Ajouter une nouvelle adresse courriel add_new_email=Ajouter une nouvelle adresse e-mail
add_email=Ajouter un courriel add_email=Ajouter un e-mail
add_email_confirmation_sent=Une nouvelle confirmation d'adresse e-mail a été envoyé à '%s', veuillez vérifier votre boîte de réception dans un délai de %d heures pour terminer le processus de confirmation. add_email_confirmation_sent=Une nouvelle confirmation d'adresse e-mail a été envoyé à '%s', veuillez vérifier votre boîte de réception dans un délai de %d heures pour terminer le processus de confirmation.
add_email_success=Votre courriel a été ajouté avec succès. add_email_success=Votre nouvelle adresse e-mail a été ajoutée avec succès.
manage_ssh_keys=Gérer les clés SSH manage_ssh_keys=Gérer les clés SSH
add_key=Ajouter une Clé add_key=Ajouter une Clé
@ -341,10 +335,10 @@ repo_name_helper=Idéalement, le nom d'un dépot devrait être court, mémorable
visibility=Visibilité visibility=Visibilité
visiblity_helper=Ce dépôt est <span class="ui red text"> privé</span> visiblity_helper=Ce dépôt est <span class="ui red text"> privé</span>
visiblity_helper_forced=L'administrateur du site a forcé tous les nouveaux dépôts à être <span class="ui red text">privés</span> visiblity_helper_forced=L'administrateur du site a forcé tous les nouveaux dépôts à être <span class="ui red text">privés</span>
visiblity_fork_helper=(Les changement de cette valeur affecteront tous les embranchements) visiblity_fork_helper=(Les changement de cette valeur affecteront tous les forks)
clone_helper=Besoin d'aide pour dupliquer ? Visitez <a target="_blank" href="%s">l'aide</a> ! clone_helper=Besoin d'aide pour dupliquer ? Visitez <a target="_blank" href="%s">l'aide</a> !
fork_repo=Créer un fork du dépôt fork_repo=Créer un fork du dépôt
fork_from=Scission de fork_from=Fork de
fork_visiblity_helper=La visibilité d'un fork ne peut pas être modifiée. fork_visiblity_helper=La visibilité d'un fork ne peut pas être modifiée.
repo_desc=Description repo_desc=Description
repo_lang=Langue repo_lang=Langue
@ -361,14 +355,14 @@ mirror_address=Adresse du miroir
mirror_address_desc=Veuillez inclure les informations d'identification nécessaires dans l'adresse. mirror_address_desc=Veuillez inclure les informations d'identification nécessaires dans l'adresse.
watchers=Observateurs watchers=Observateurs
stargazers=Stargazers stargazers=Stargazers
forks=Embranchements forks=Forks
form.reach_limit_of_creation=Le propriétaire a atteint le nombre maximal de %d dépôts créés. form.reach_limit_of_creation=Le propriétaire a atteint le nombre maximal de %d dépôts créés.
form.name_reserved=Le nom de dépôt '%s' est réservé. form.name_reserved=Le nom de dépôt '%s' est réservé.
form.name_pattern_not_allowed=Motif '%s' interdit pour les noms de dépôt. form.name_pattern_not_allowed=Motif '%s' interdit pour les noms de dépôt.
need_auth=Nécessite une Autorisation need_auth=Nécessite une Autorisation
migrate_type=Type de Migration migrate_type=Type de migration
migrate_type_helper=Ce dépôt sera un <span class="text blue"> miroir</span> migrate_type_helper=Ce dépôt sera un <span class="text blue"> miroir</span>
migrate_repo=Migrer le dépôt migrate_repo=Migrer le dépôt
migrate.clone_address=Adresse du clone migrate.clone_address=Adresse du clone
@ -388,7 +382,7 @@ unwatch=Ne plus suivre
watch=Suivre watch=Suivre
unstar=Retirer le vote unstar=Retirer le vote
star=Voter star=Voter
fork=Embranchement fork=Fork
no_desc=Aucune description no_desc=Aucune description
quick_guide=Introduction rapide quick_guide=Introduction rapide
@ -403,7 +397,7 @@ tree=Aborescence
filter_branch_and_tag=Filtrer une branche ou un tag filter_branch_and_tag=Filtrer une branche ou un tag
branches=Branches branches=Branches
tags=Tags tags=Tags
issues=Problèmes issues=Tickets
pulls=Pull Requests pulls=Pull Requests
labels=Etiquettes labels=Etiquettes
milestones=Étapes milestones=Étapes
@ -423,7 +417,7 @@ commits.date=Date
commits.older=Précédemment commits.older=Précédemment
commits.newer=Récemment commits.newer=Récemment
issues.new=Nouveau Problème issues.new=Nouveau ticket
issues.new.labels=Etiquettes issues.new.labels=Etiquettes
issues.new.no_label=Pas d'étiquette issues.new.no_label=Pas d'étiquette
issues.new.clear_labels=Effacer les étiquettes issues.new.clear_labels=Effacer les étiquettes
@ -448,9 +442,9 @@ issues.filter_milestone_no_select=Aucun jalon sélectionné
issues.filter_assignee=Assigné issues.filter_assignee=Assigné
issues.filter_assginee_no_select=Pas d'assignataire selectionné issues.filter_assginee_no_select=Pas d'assignataire selectionné
issues.filter_type=Type issues.filter_type=Type
issues.filter_type.all_issues=Tous les problèmes issues.filter_type.all_issues=Tous les tickets
issues.filter_type.assigned_to_you=Qui vous sont assignés issues.filter_type.assigned_to_you=Qui vous sont assignés
issues.filter_type.created_by_you=Créé(es) par vous issues.filter_type.created_by_you=Crées par vous
issues.filter_type.mentioning_you=Vous mentionnant issues.filter_type.mentioning_you=Vous mentionnant
issues.filter_sort=Trier issues.filter_sort=Trier
issues.filter_sort.latest=Plus récent issues.filter_sort.latest=Plus récent
@ -459,25 +453,25 @@ issues.filter_sort.recentupdate=Mis à jour récemment
issues.filter_sort.leastupdate=Moins récemment mis à jour issues.filter_sort.leastupdate=Moins récemment mis à jour
issues.filter_sort.mostcomment=Les plus commentés issues.filter_sort.mostcomment=Les plus commentés
issues.filter_sort.leastcomment=Les moins commentés issues.filter_sort.leastcomment=Les moins commentés
issues.opened_by=Ouvrir %[1]s by <a href="%[2]s">%[3]s</a> issues.opened_by=Créé %[1]s par <a href="%[2]s">%[3]s</a>
issues.opened_by_fake=ouvert %[1]s par %[2]s issues.opened_by_fake=ouvert %[1]s par %[2]s
issues.previous=Page Précédente issues.previous=Page Précédente
issues.next=Page Suivante issues.next=Page Suivante
issues.open_title=Ouvert issues.open_title=Ouvert
issues.closed_title=Fermé issues.closed_title=Fermé
issues.num_comments=%d commentaires issues.num_comments=%d commentaires
issues.commented_at='commenté à <a id="%[1]s" href="#%[1]s"> %[2]s'</a> issues.commented_at=`commenté à <a id="%[1]s" href="#%[1]s"> %[2]s</a>`
issues.no_content=Il n'existe pas encore de contenu. issues.no_content=Il n'existe pas encore de contenu.
issues.close_issue=Fermer issues.close_issue=Fermer
issues.close_comment_issue=Commenter et fermer issues.close_comment_issue=Commenter et fermer
issues.reopen_issue=Réouvrir issues.reopen_issue=Réouvrir
issues.reopen_comment_issue=Commenter et réouvrir issues.reopen_comment_issue=Commenter et réouvrir
issues.create_comment=Créer un commentaire issues.create_comment=Créer un commentaire
issues.closed_at="fermé à <a id="%[1]s"href="#%[1]s"> %[2]s"</a> issues.closed_at=`fermé à <a id="%[1]s"href="#%[1]s"> %[2]s"</a>`
issues.reopened_at='réouvert à <a id="%[1]s" href="#%[1]s"> %[2]s'</a> issues.reopened_at=`réouvert à <a id="%[1]s" href="#%[1]s"> %[2]s</a>`
issues.commit_ref_at=`a référencé ce problème à partir d'un commit <a id="%[1]s" href="#%[1]s"> %[2]s</a>` issues.commit_ref_at=`a référencé ce problème à partir d'un commit <a id="%[1]s" href="#%[1]s"> %[2]s</a>`
issues.poster=Publier issues.poster=Publier
issues.admin=Admin issues.collaborator=Collaborateur
issues.owner=Propriétaire issues.owner=Propriétaire
issues.sign_up_for_free=Inscrivez-vous gratuitement issues.sign_up_for_free=Inscrivez-vous gratuitement
issues.sign_in_require_desc=pour rejoindre cette conversation. Vous avez déjà un compte ? <a href="%s">Connectez-vous commenter</a> issues.sign_in_require_desc=pour rejoindre cette conversation. Vous avez déjà un compte ? <a href="%s">Connectez-vous commenter</a>
@ -487,13 +481,14 @@ issues.save=Enregistrer
issues.label_title=Nom du Label issues.label_title=Nom du Label
issues.label_color=Couleur du Label issues.label_color=Couleur du Label
issues.label_count=%d labels issues.label_count=%d labels
issues.label_open_issues=%d problèmes ouverts issues.label_open_issues=%d tickets ouverts
issues.label_edit=Éditer issues.label_edit=Éditer
issues.label_delete=Supprimer issues.label_delete=Supprimer
issues.label_modify=Modification du Label issues.label_modify=Modification du Label
issues.label_deletion=Suppression du Label issues.label_deletion=Suppression du Label
issues.label_deletion_desc=Cette opération supprimera également toutes les informations relatives aux problèmes. Voulez-vous continuer ? issues.label_deletion_desc=Cette opération supprimera également toutes les informations relatives aux tickets. Voulez-vous continuer ?
issues.label_deletion_success=Label supprimé avec succès ! issues.label_deletion_success=Label supprimé avec succès !
issues.num_participants=%d Participants
pulls.new=Nouvelle Pull Request pulls.new=Nouvelle Pull Request
pulls.compare_changes=Comparer les changements pulls.compare_changes=Comparer les changements
@ -513,7 +508,7 @@ pulls.tab_files=Fichiers modifiés
pulls.reopen_to_merge=Veuillez rouvrir cette demande de Pull Request pour effectuer l'opération de fusion. pulls.reopen_to_merge=Veuillez rouvrir cette demande de Pull Request pour effectuer l'opération de fusion.
pulls.merged=Fusionné pulls.merged=Fusionné
pulls.has_merged=Cette Pull Request a été fusionnée avec succès ! pulls.has_merged=Cette Pull Request a été fusionnée avec succès !
pulls.data_broken=Les données de cette demande de rattachement ont été compromise en raison de la suppression d'informations sur l'embranchement. pulls.data_broken=Les données de cette pull request ont été compromises en raison de la suppression d'informations sur le fork.
pulls.is_checking=La recherche de conflits est toujours en cours, veuillez rafraichir la page dans quelques instants. pulls.is_checking=La recherche de conflits est toujours en cours, veuillez rafraichir la page dans quelques instants.
pulls.can_auto_merge_desc=Cette pull request peut être fusionnée automatiquement. pulls.can_auto_merge_desc=Cette pull request peut être fusionnée automatiquement.
pulls.cannot_auto_merge_desc=Cette pull request ne peut être fusionnée automatiquement à cause de conflits. pulls.cannot_auto_merge_desc=Cette pull request ne peut être fusionnée automatiquement à cause de conflits.
@ -528,7 +523,7 @@ milestones.closed=%s fermé
milestones.no_due_date=Aucune date d'échéance milestones.no_due_date=Aucune date d'échéance
milestones.open=Ouvrir milestones.open=Ouvrir
milestones.close=Fermer milestones.close=Fermer
milestones.new_subheader=Créez des jalons pour organiser vos problèmes. milestones.new_subheader=Créez des jalons pour organiser vos tickets.
milestones.create=Créer un Jalon milestones.create=Créer un Jalon
milestones.title=Titre milestones.title=Titre
milestones.desc=Description milestones.desc=Description
@ -542,7 +537,7 @@ milestones.cancel=Annuler
milestones.modify=Modifier le Jalon milestones.modify=Modifier le Jalon
milestones.edit_success=Le Jalon '%s' a été modifié avec succès ! milestones.edit_success=Le Jalon '%s' a été modifié avec succès !
milestones.deletion=Supprimer le Jalon milestones.deletion=Supprimer le Jalon
milestones.deletion_desc=Supprimer ce Jalon effacera ses informations dans tous les problèmes relatifs. Voulez-vous continuer ? milestones.deletion_desc=Supprimer ce jalon effacera ses informations dans tous les tickets relatifs. Voulez-vous continuer ?
milestones.deletion_success=Le Jalon a été supprimé avec succès ! milestones.deletion_success=Le Jalon a été supprimé avec succès !
wiki=Wiki wiki=Wiki
@ -557,6 +552,8 @@ wiki.save_page=Enregistrer la page
wiki.last_commit_info=%s a édité cette page %s wiki.last_commit_info=%s a édité cette page %s
wiki.edit_page_button=Modifier wiki.edit_page_button=Modifier
wiki.new_page_button=Nouvelle Page wiki.new_page_button=Nouvelle Page
wiki.delete_page_button=Supprimer la page
wiki.delete_page_notice_1=Cela supprimera la page <code>"%s"</code>. Soyez-en sûr.
wiki.page_already_exists=Une page de wiki avec le même nom existe déjà. wiki.page_already_exists=Une page de wiki avec le même nom existe déjà.
wiki.pages=Pages wiki.pages=Pages
wiki.last_updated=Dernière mise à jour: %s wiki.last_updated=Dernière mise à jour: %s
@ -570,7 +567,7 @@ settings.basic_settings=Paramètres de base
settings.site=Site officiel settings.site=Site officiel
settings.update_settings=Valider settings.update_settings=Valider
settings.change_reponame_prompt=Ce changement affectera comment les liens sont reliés avec le dépôt. settings.change_reponame_prompt=Ce changement affectera comment les liens sont reliés avec le dépôt.
settings.advanced_settings=Paramètres Avancés settings.advanced_settings=Paramètres avancés
settings.wiki_desc=Activer le wiki pour permettre l'écriture de documents settings.wiki_desc=Activer le wiki pour permettre l'écriture de documents
settings.use_external_wiki=Utiliser un wiki externe settings.use_external_wiki=Utiliser un wiki externe
settings.external_wiki_url=URL Wiki externe settings.external_wiki_url=URL Wiki externe
@ -581,19 +578,28 @@ settings.tracker_url_format=Format d'URL du bug tracker
settings.tracker_url_format_desc=Vous pouvez utiliser l'espace réservé <code>{user} {repo} {index}</code> pour le nom d'utilisateur, le nom du dépôt et le numéro de bug. settings.tracker_url_format_desc=Vous pouvez utiliser l'espace réservé <code>{user} {repo} {index}</code> pour le nom d'utilisateur, le nom du dépôt et le numéro de bug.
settings.pulls_desc=Activer les pull requests pour accepter les contributions publiques settings.pulls_desc=Activer les pull requests pour accepter les contributions publiques
settings.danger_zone=Zone de danger settings.danger_zone=Zone de danger
settings.transfer=Transférer les propriétés
settings.transfer_desc=Transférer ce dépôt à un autre utilisateur ou une organisation dont vous possédez des droits d'administrateur.
settings.new_owner_has_same_repo=Le nouveau propriétaire a déjà un dépôt nommé ainsi. settings.new_owner_has_same_repo=Le nouveau propriétaire a déjà un dépôt nommé ainsi.
settings.delete=Supprimer ce dépôt settings.convert=Convertir en dépôt ordinaire
settings.delete_desc=Attention, action irréversible. Soyez sûr de vous. settings.convert_desc=Vous pouvez convertir ce miroir en dépôt ordinaire. Cela ne peut pas être inversée.
settings.convert_notices_1=- Cette opération va convertir ce dépôt miroir en un dépôt standard et ne peut être annulée.
settings.convert_confirm=Confirmer la conversion
settings.convert_succeed=Le dépôt a été converti avec succès en dépôt ordinaire.
settings.transfer=Changer de propriétaire
settings.transfer_desc=Transférer ce dépôt à un autre utilisateur ou une organisation dont vous possédez des droits d'administrateur.
settings.transfer_notices_1=-Vous perdrez l'accès si le nouveau propriétaire est un utilisateur individuel. settings.transfer_notices_1=-Vous perdrez l'accès si le nouveau propriétaire est un utilisateur individuel.
settings.transfer_notices_2=-Vous préserverez l'accès si le nouveau propriétaire est une organisation et si vous y appartenez. settings.transfer_notices_2=- Vous conserverez l'accès si le nouveau propriétaire est une organisation et que vous y appartenez.
settings.transfer_form_title=Veuillez recopier le texte suivant afin de confirmer votre opération : settings.transfer_form_title=Veuillez recopier le texte suivant afin de confirmer votre opération :
settings.wiki_delete=Effacer les données du Wiki
settings.wiki_delete_desc=Une fois que vous effacez les données du wiki, on ne peut revenir en arrière. Soyez-en sûr.
settings.wiki_delete_notices_1=- Cela va supprimer et désactiver le wiki pour %s
settings.wiki_deletion_success=Le dépôt de données wiki ont été effacés avec succès.
settings.delete=Supprimer ce dépôt
settings.delete_desc=Attention, cette action est action irréversible. Soyez sûr de vous.
settings.delete_notices_1=- Cette opération <strong>ne peut pas </strong> être annulée. settings.delete_notices_1=- Cette opération <strong>ne peut pas </strong> être annulée.
settings.delete_notices_2=-Cette opération supprimera définitivement le dépôt, y compris les données Git, problèmes, commentaires et accès des collaborateurs. settings.delete_notices_2=- Cette opération supprimera définitivement le dépôt, y compris les données Git, les tickets, les commentaires et les accès des collaborateurs.
settings.delete_notices_fork_1=- Si ce dépôt est public, tous les forks vont devenir indépendant après sa suppression. settings.delete_notices_fork_1=- Si ce dépôt est public, tous les forks vont devenir indépendant après sa suppression.
settings.delete_notices_fork_2=-Si ce dépôt est privé, tous les embranchements seront supprimés en même temps. settings.delete_notices_fork_2=-Si ce dépôt est privé, tous les forks seront supprimés en même temps.
settings.delete_notices_fork_3=-Si vous souhaitez conserver tous les embranchements après suppression, veuillez tout d'abord modifier la visibilité de ce dépôt en public. settings.delete_notices_fork_3=-Si vous souhaitez conserver tous les forks après suppression, veuillez tout d'abord modifier la visibilité de ce dépôt en public.
settings.deletion_success=Le dépôt a été supprimé avec succès! settings.deletion_success=Le dépôt a été supprimé avec succès!
settings.update_settings_success=Options mises à jour avec succès. settings.update_settings_success=Options mises à jour avec succès.
settings.transfer_owner=Nouveau propriétaire settings.transfer_owner=Nouveau propriétaire
@ -602,15 +608,19 @@ settings.transfer_succeed=Le contrôle du dépôt a été transféré avec succ
settings.confirm_delete=Confirmer la suppression settings.confirm_delete=Confirmer la suppression
settings.add_collaborator=Ajouter un collaborateur settings.add_collaborator=Ajouter un collaborateur
settings.add_collaborator_success=Nouveau collaborateur ajouté. settings.add_collaborator_success=Nouveau collaborateur ajouté.
settings.delete_collaborator=Supprimer
settings.collaborator_deletion=Suppression d'un collaborateur
settings.collaborator_deletion_desc=Cet utilisateur n'aura plus accès pour collaborer à ce dépôt après sa suppression. Voulez-vous continuer?
settings.remove_collaborator_success=Collaborateur supprimé. settings.remove_collaborator_success=Collaborateur supprimé.
settings.search_user_placeholder=Rechercher un utilisateur... settings.search_user_placeholder=Rechercher un utilisateur...
settings.org_not_allowed_to_be_collaborator=Une organisation n'est pas autorisée à être ajoutée en tant que collaborateur.
settings.user_is_org_member=Cet utilisateur ne peut pas être ajouté en tant que collaborateur car il fait partie d'une organisation. settings.user_is_org_member=Cet utilisateur ne peut pas être ajouté en tant que collaborateur car il fait partie d'une organisation.
settings.add_webhook=Ajouter un Webhook settings.add_webhook=Ajouter un Webhook
settings.hooks_desc=Les Webhooks sont des déclencheurs de POST HTTP . Lorsque qu'un événement se produit dans Gogs, une notification sera envoyée vers l'hôte cible préalablement spécifié. Apprenez-en davantage dans le <a target="_blank" href="%s">Guide des Webhooks</a>. settings.hooks_desc=Les Webhooks sont des déclencheurs de POST HTTP . Lorsque qu'un événement se produit dans Gogs, une notification sera envoyée vers l'hôte cible préalablement spécifié. Apprenez-en davantage dans le <a target="_blank" href="%s">Guide des Webhooks</a>.
settings.webhook_deletion=Supprimer le Webhook settings.webhook_deletion=Supprimer le Webhook
settings.webhook_deletion_desc=Supprimer ce webhook va supprimer ses informations et l'historique de livraison. Voulez-vous continuer ? settings.webhook_deletion_desc=Supprimer ce webhook va supprimer ses informations et l'historique de livraison. Voulez-vous continuer ?
settings.webhook_deletion_success=Le webhook a été supprimé avec succès ! settings.webhook_deletion_success=Le webhook a été supprimé avec succès !
settings.webhook.test_delivery=Tester la publication settings.webhook.test_delivery=Tester la version
settings.webhook.test_delivery_desc=Envoyer un faux push pour tester la configuration des webhooks settings.webhook.test_delivery_desc=Envoyer un faux push pour tester la configuration des webhooks
settings.webhook.test_delivery_success=Le webhook de test a été ajouté à la file d'attente de livraison. L'affichage dans l'historique de livraison peut prendre quelques secondes. settings.webhook.test_delivery_success=Le webhook de test a été ajouté à la file d'attente de livraison. L'affichage dans l'historique de livraison peut prendre quelques secondes.
settings.webhook.request=Requête settings.webhook.request=Requête
@ -681,7 +691,7 @@ release.prerelease=Pré-publication
release.stable=Stable release.stable=Stable
release.edit=Éditer release.edit=Éditer
release.ahead=<strong>%d</strong> commits jusqu'à %s depuis cette publication release.ahead=<strong>%d</strong> commits jusqu'à %s depuis cette publication
release.source_code=Code Source release.source_code=Code source
release.new_subheader=Publier une version pour itérer sur le produit. release.new_subheader=Publier une version pour itérer sur le produit.
release.edit_subheader=Un changelog détaillé peut aider les utilisateurs à comprendre ce qui a été amélioré. release.edit_subheader=Un changelog détaillé peut aider les utilisateurs à comprendre ce qui a été amélioré.
release.tag_name=Nom du tag release.tag_name=Nom du tag
@ -697,12 +707,12 @@ release.prerelease_helper=Nous soulignerons que cette version est considérée c
release.cancel=Annuler release.cancel=Annuler
release.publish=Publier release.publish=Publier
release.save_draft=Sauvegarder le Brouillon release.save_draft=Sauvegarder le Brouillon
release.edit_release=Éditer la publication release.edit_release=Modifier la version
release.delete_release=Supprimer Cette Version release.delete_release=Supprimer Cette Version
release.deletion=Suppression de la Version release.deletion=Suppression de la Version
release.deletion_desc=Supprimer cette version supprimera le tag Git correspondant. Voulez-vous continuer ? release.deletion_desc=Supprimer cette version supprimera le tag Git correspondant. Voulez-vous continuer ?
release.deletion_success=La version à été supprimée avec succès ! release.deletion_success=La version à été supprimée avec succès !
release.tag_name_already_exist=Une publication avec ce nom de tag existe déjà. release.tag_name_already_exist=Une version avec ce nom de tag existe déjà.
release.downloads=Téléchargements release.downloads=Téléchargements
[org] [org]
@ -715,7 +725,7 @@ people=Contacts
invite_someone=Inviter quelqu'un invite_someone=Inviter quelqu'un
teams=Équipes teams=Équipes
lower_members=Membres lower_members=Membres
lower_repositories=Référentiels lower_repositories=dépôts
create_new_team=Créer une Nouvelle Équipe create_new_team=Créer une Nouvelle Équipe
org_desc=Description org_desc=Description
team_name=Nom d'Équipe team_name=Nom d'Équipe
@ -742,7 +752,7 @@ settings.delete_prompt=Cela supprimera cette organisation définitivement. Cette
settings.confirm_delete_account=Confirmez la suppression settings.confirm_delete_account=Confirmez la suppression
settings.delete_org_title=Suppression d'organisation settings.delete_org_title=Suppression d'organisation
settings.delete_org_desc=Cette organisation sera définitivement supprimée. Continuer ? settings.delete_org_desc=Cette organisation sera définitivement supprimée. Continuer ?
settings.hooks_desc=Ajoute des Webhooks qui seront activés pour <strong>tous les Référentiels</strong> de cette organisation. settings.hooks_desc=Ajoute des vebhooks qui seront activés pour <strong>tous les dépôts</strong> de cette organisation.
members.membership_visibility=Visibilité des membres: members.membership_visibility=Visibilité des membres:
members.public=Public members.public=Public
@ -760,14 +770,14 @@ members.invite_now=Envoyer une invitation
teams.join=Rejoindre teams.join=Rejoindre
teams.leave=Quitter teams.leave=Quitter
teams.read_access=Accès en Lecture teams.read_access=Accès en Lecture
teams.read_access_helper=Cette équipe aura la possibilité de voir et dupliquer ses Référentiels. teams.read_access_helper=Cette équipe aura la possibilité de voir et cloner ses dépôts.
teams.write_access=Accès en Écriture teams.write_access=Accès en Écriture
teams.write_access_helper=Cette équipe possèdera aussi bien des droits de lecture que d'écriture sur ses Référentiels. teams.write_access_helper=Cette équipe possèdera aussi bien des droits de lecture que d'écriture sur ses dépôts.
teams.admin_access=Accès Administrateur teams.admin_access=Accès Administrateur
teams.admin_access_helper=Cette équipe possèdera des droits de lecture, d'écriture, ainsi que le pouvoir d'ajouter des collaborateurs. teams.admin_access_helper=Cette équipe possèdera des droits de lecture, d'écriture, ainsi que le pouvoir d'ajouter des collaborateurs.
teams.no_desc=Aucune description teams.no_desc=Aucune description
teams.settings=Paramètres teams.settings=Paramètres
teams.owners_permission_desc=Les propriétaires possèdent <strong>les droits d'administrateur</strong> et disposent d'un accès complet à <strong>tous les Référentiels</strong> de l'organisation. teams.owners_permission_desc=Les propriétaires possèdent <strong>les droits d'administrateur</strong> et disposent d'un accès complet à <strong>tous les dépôts</strong> de l'organisation.
teams.members=Membres de L'Équipe teams.members=Membres de L'Équipe
teams.update_settings=Valider teams.update_settings=Valider
teams.delete_team=Supprimer cette Équipe teams.delete_team=Supprimer cette Équipe
@ -775,10 +785,10 @@ teams.add_team_member=Ajouter un Membre
teams.delete_team_title=Suppression de l'équipe teams.delete_team_title=Suppression de l'équipe
teams.delete_team_desc=Cette équipe sera supprimée. Les membres pourraient perdre leurs accès à certains dépôts. teams.delete_team_desc=Cette équipe sera supprimée. Les membres pourraient perdre leurs accès à certains dépôts.
teams.delete_team_success=Équipe supprimée avec succès. teams.delete_team_success=Équipe supprimée avec succès.
teams.read_permission_desc=Cette équipe permet l'accès en <strong>lecture</strong> : les membres peuvent voir et dupliquer ses Référentiels. teams.read_permission_desc=Cette équipe permet l'accès en <strong>lecture</strong> : les membres peuvent voir et dupliquer ses dépôts.
teams.write_permission_desc=Cette équipe permet l'accès en <strong>écriture</strong> : les membres peuvent participer à ses Référentiels. teams.write_permission_desc=Cette équipe permet l'accès en <strong>écriture</strong> : les membres peuvent participer à ses dépôts.
teams.admin_permission_desc=Cette équipe permet l'accès en <strong>administrateur</strong> : les membres peuvent voir, participer et ajouter des collaborateurs à ses Référentiels. teams.admin_permission_desc=Cette équipe permet l'accès en <strong>administrateur</strong> : les membres peuvent voir, participer et ajouter des collaborateurs à ses dépôts.
teams.repositories=Référentiels de l'Équipe teams.repositories=Dépôts de l'Équipe
teams.search_repo_placeholder=Rechercher dans le dépôt... teams.search_repo_placeholder=Rechercher dans le dépôt...
teams.add_team_repository=Ajouter un Dépôt à l'Équipe teams.add_team_repository=Ajouter un Dépôt à l'Équipe
teams.remove_repo=Supprimer teams.remove_repo=Supprimer
@ -788,19 +798,19 @@ teams.add_nonexistent_repo=Dépôt inexistant, veuillez d'abord le créer.
dashboard=Tableau de bord dashboard=Tableau de bord
users=Utilisateurs users=Utilisateurs
organizations=Organisations organizations=Organisations
repositories=Référentiels repositories=Dépôts
authentication=Authentifications authentication=Authentifications
config=Configuration config=Configuration
notices=Notes Systèmes notices=Notes Systèmes
monitor=Supervision monitor=Surveillance
first_page=Première first_page=Première
last_page=Dernière last_page=Dernière
total=Total : %d total=Total : %d
dashboard.statistic=Statistiques dashboard.statistic=Statistiques
dashboard.operations=Opérations dashboard.operations=Opérations
dashboard.system_status=État du Moniteur Système dashboard.system_status=État du système
dashboard.statistic_info=La base de données Gogs contient <b>%d</b> utilisateurs, <b>%d</b> organisations, <b>%d</b> clés publiques, <b>%d</b> Référentiels, <b>%d</b> suivis, <b>%d</b> votes, <b>%d</b> actions, <b>%d</b> accès, <b>%d</b> problèmes, <b>%d</b> commentaires, <b>%d</b> comptes de réseaux sociaux, <b>%d</b> abonnements, <b>%d</b> miroirs, <b>%d</b> publications, <b>%d</b> connexions d'origine, <b>%d</b> webhooks, <b>%d</b> milestones, <b>%d</b> labels, <b>%d</b> tâches hook, <b>%d</b> équipes, <b>%d</b> tâches de mise à jour, <b>%d</b> fichiers. dashboard.statistic_info=La base de données Gogs contient <b>%d</b> utilisateurs, <b>%d</b> organisations, <b>%d</b> clés publiques, <b>%d</b> dépôts, <b>%d</b> surveillances de dépôts, <b>%d</b> votes, <b>%d</b> actions, <b>%d</b> accès, <b>%d</b> tickets, <b>%d</b> commentaires, <b>%d</b> comptes de réseaux sociaux, <b>%d</b> abonnements, <b>%d</b> miroirs, <b>%d</b> versions, <b>%d</b> connexions d'origine, <b>%d</b> webhooks, <b>%d</b> versions, <b>%d</b> labels, <b>%d</b> tâches hook, <b>%d</b> équipes, <b>%d</b> tâches de mise à jour, <b>%d</b> fichiers.
dashboard.operation_name=Nom de l'Opération dashboard.operation_name=Nom de l'Opération
dashboard.operation_switch=Basculer dashboard.operation_switch=Basculer
dashboard.operation_run=Exécuter dashboard.operation_run=Exécuter
@ -808,16 +818,18 @@ dashboard.clean_unbind_oauth=Nettoyer les associations OAuthes
dashboard.clean_unbind_oauth_success=Tous unbind OAuthes ont été supprimés avec succès. dashboard.clean_unbind_oauth_success=Tous unbind OAuthes ont été supprimés avec succès.
dashboard.delete_inactivate_accounts=Supprimer tous les comptes inactifs dashboard.delete_inactivate_accounts=Supprimer tous les comptes inactifs
dashboard.delete_inactivate_accounts_success=Tous les comptes inactifs ont été supprimés avec succès. dashboard.delete_inactivate_accounts_success=Tous les comptes inactifs ont été supprimés avec succès.
dashboard.delete_repo_archives=Supprimer toutes les archives de référentiels dashboard.delete_repo_archives=Supprimer toutes les archives des dépôts
dashboard.delete_repo_archives_success=Toutes les archives des référentiels ont été supprimées avec succès. dashboard.delete_repo_archives_success=Toutes les archives des dépôts ont été supprimées avec succès.
dashboard.delete_missing_repos=Supprimer tous les dépôts ayant perdu leurs fichiers Git dashboard.delete_missing_repos=Supprimer tous les dépôts ayant perdu leurs fichiers Git
dashboard.delete_missing_repos_success=Tous les dépôts ayant perdu leurs fichiers Git ont été supprimés avec succès. dashboard.delete_missing_repos_success=Tous les dépôts ayant perdu leurs fichiers Git ont été supprimés avec succès.
dashboard.git_gc_repos=Collecter les déchets des référentiels dashboard.git_gc_repos=Collecter les déchets des dépôts
dashboard.git_gc_repos_success=Tous les dépôts ont effectué la collecte avec succès. dashboard.git_gc_repos_success=Tous les dépôts ont effectué la collecte avec succès.
dashboard.resync_all_sshkeys=Ré-écrire le fichier '.ssh/authorized_keys' (attention : les clés hors-Gogs vont être perdues) dashboard.resync_all_sshkeys=Ré-écrire le fichier '.ssh/authorized_keys' (attention : les clés hors-Gogs vont être perdues)
dashboard.resync_all_sshkeys_success=Toutes les clés publiques ont été ré-écrites avec succès. dashboard.resync_all_sshkeys_success=Toutes les clés publiques ont été ré-écrites avec succès.
dashboard.resync_all_update_hooks=Ré-écrire tous les hooks de mises à jour des dépôts (requis quand le chemin de la configuration personnalisé est modifié) dashboard.resync_all_update_hooks=Ré-écrire tous les hooks de mises à jour des dépôts (requis quand le chemin de la configuration personnalisé est modifié)
dashboard.resync_all_update_hooks_success=Les mises à jour de hook des référentiels ont toutes été réécrites avec succès. dashboard.resync_all_update_hooks_success=Tous les hooks de mises à jour des dépôts ont été ré-écris avec succès.
dashboard.reinit_missing_repos=Réinitialiser tous les dépôts qui ont perdu des fichiers Git
dashboard.reinit_missing_repos_success=Tous les enregistrements de dépôts qui ont perdu des fichiers Git ont été réinitialisés avec succès.
dashboard.server_uptime=Durée de Marche Serveur dashboard.server_uptime=Durée de Marche Serveur
dashboard.current_goroutine=Goroutines actuelles dashboard.current_goroutine=Goroutines actuelles
@ -874,7 +886,7 @@ users.allow_import_local=Ce compte dispose des permissions nécessaire à l'impo
users.update_profile=Mettre à jour le profil users.update_profile=Mettre à jour le profil
users.delete_account=Supprimer ce Compte users.delete_account=Supprimer ce Compte
users.still_own_repo=Ce compte possède toujours des dépôts. Vous devez d'abord les supprimer ou les transférer. users.still_own_repo=Ce compte possède toujours des dépôts. Vous devez d'abord les supprimer ou les transférer.
users.still_has_org=Ce compte a toujours membres de l'organisation, vous avez à gauche ou supprimez tout d'abord. users.still_has_org=Ce compte est toujours membre d'une ou plusieurs organisations. Vous devez d'abord les supprimer ou en retirer ce compte.
users.deletion_success=Le compte a été supprimé avec succès ! users.deletion_success=Le compte a été supprimé avec succès !
orgs.org_manage_panel=Gestion des Organisations orgs.org_manage_panel=Gestion des Organisations
@ -888,7 +900,7 @@ repos.name=Nom
repos.private=Privé repos.private=Privé
repos.watches=Suivi par repos.watches=Suivi par
repos.stars=Votes repos.stars=Votes
repos.issues=Problèmes repos.issues=Tickets
auths.auth_manage_panel=Panel d'administration des authentifications auths.auth_manage_panel=Panel d'administration des authentifications
auths.new=Ajouter une nouvelle source d'authentification auths.new=Ajouter une nouvelle source d'authentification
@ -911,6 +923,7 @@ auths.attribute_username_placeholder=Laisser vide pour utiliser la valeur du for
auths.attribute_name=Attribut du prénom auths.attribute_name=Attribut du prénom
auths.attribute_surname=Attribut du nom de famille auths.attribute_surname=Attribut du nom de famille
auths.attribute_mail=Attribut de l'e-mail auths.attribute_mail=Attribut de l'e-mail
auths.attributes_in_bind=Aller chercher les attributs dans le contexte de liaison DN
auths.filter=Filtre utilisateur auths.filter=Filtre utilisateur
auths.admin_filter=Filtre administrateur auths.admin_filter=Filtre administrateur
auths.ms_ad_sa=Ms Ad SA auths.ms_ad_sa=Ms Ad SA
@ -932,22 +945,36 @@ auths.update=Mettre à jour les paramètres d'authentifications
auths.delete=Supprimer cette authentification auths.delete=Supprimer cette authentification
auths.delete_auth_title=Suppression de l'authentification auths.delete_auth_title=Suppression de l'authentification
auths.delete_auth_desc=Cette authentification va être supprimée. voulez-vous continuer ? auths.delete_auth_desc=Cette authentification va être supprimée. voulez-vous continuer ?
auths.still_in_used=Cette authentification est encore utilisée par d'autres utilisateurs, supprimez-les ou convertir ces utilisateurs vers un autre type de session, avant.
auths.deletion_success=L'authentification a été supprimée avec succès ! auths.deletion_success=L'authentification a été supprimée avec succès !
config.server_config=Configuration du Serveur config.server_config=Configuration du Serveur
config.app_name=Nom de l'Application config.app_name=Nom de l'application
config.app_ver=Version de l'Application config.app_ver=Version de l'application
config.app_url=URL de l'Application config.app_url=URL de l'application
config.domain=Domaine config.domain=Domaine
config.offline_mode=Mode hors-ligne config.offline_mode=Mode hors-ligne
config.disable_router_log=Désactiver la Journalisation du Routeur config.disable_router_log=Désactiver la Journalisation du Routeur
config.run_user=Entrer un Utilisateur config.run_user=Utilisateur système
config.run_mode=Mode d'Éxécution config.run_mode=Mode d'Éxécution
config.repo_root_path=Emplacement des Dépôts config.repo_root_path=Emplacement des Dépôts
config.static_file_root_path=Emplacement Racine du Fichier Statique config.static_file_root_path=Chemin statique des fichiers racines
config.log_file_root_path=Emplacement Racine du Fichier Journal config.log_file_root_path=Emplacement Racine du Fichier Journal
config.script_type=Type de Script config.script_type=Type de Script
config.reverse_auth_user=Annuler l'Authentification de l'Utilisateur config.reverse_auth_user=Annuler l'Authentification de l'Utilisateur
config.ssh_config=Configuration SSH
config.ssh_enabled=Activé
config.ssh_start_builtin_server=Démarrer le serveur intégré
config.ssh_domain=Domaine
config.ssh_port=Port
config.ssh_listen_port=Port d'écoute
config.ssh_root_path=Emplacement racine
config.ssh_key_test_path=Chemin de test des clés
config.ssh_keygen_path=Chemin vers le générateur de clefs ("ssh-keygen")
config.ssh_minimum_key_size_check=Vérification de la longueur de clé minimale
config.ssh_minimum_key_sizes=Tailles de clé minimales
config.db_config=Configuration de la Base de Données config.db_config=Configuration de la Base de Données
config.db_type=Type config.db_type=Type
config.db_host=Hôte config.db_host=Hôte
@ -958,12 +985,11 @@ config.db_ssl_mode_helper=("postgres" uniquement)
config.db_path=Emplacement config.db_path=Emplacement
config.db_path_helper=(pour « sqlite3 » et « TIDB ») config.db_path_helper=(pour « sqlite3 » et « TIDB »)
config.service_config=Configuration du Service config.service_config=Configuration du Service
config.register_email_confirm=Nécessite une confirmation par courriel config.register_email_confirm=Nécessite une confirmation par e-mail
config.disable_register=Désactiver l'Enregistrement config.disable_register=Désactiver les inscriptions
config.show_registration_button=Afficher le bouton d'enregistrement config.show_registration_button=Afficher le bouton d'enregistrement
config.require_sign_in_view=Connexion Obligatoire pour Visualiser config.require_sign_in_view=Connexion obligatoire pour visualiser
config.enable_cache_avatar=Activer le Cache d'Avatar config.mail_notify=Notifier par mail
config.mail_notify=Mailer les Notifications
config.disable_key_size_check=Désactiver la vérification de la taille de clé minimale config.disable_key_size_check=Désactiver la vérification de la taille de clé minimale
config.enable_captcha=Activez le Captcha config.enable_captcha=Activez le Captcha
config.active_code_lives=Limites de Code Actif config.active_code_lives=Limites de Code Actif
@ -972,32 +998,35 @@ config.webhook_config=Configuration Webhook
config.queue_length=Longueur de la file d'attente config.queue_length=Longueur de la file d'attente
config.deliver_timeout=Expiration d'Envoi config.deliver_timeout=Expiration d'Envoi
config.skip_tls_verify=Ne pas vérifier TLS config.skip_tls_verify=Ne pas vérifier TLS
config.mailer_config=Configuration du Maileur config.mailer_config=Configuration du service de mail
config.mailer_enabled=Activé config.mailer_enabled=Activé
config.mailer_disable_helo=Désactiver HELO config.mailer_disable_helo=Désactiver HELO
config.mailer_name=Nom config.mailer_name=Nom
config.mailer_host=Hôte config.mailer_host=Hôte
config.mailer_user=Utilisateur config.mailer_user=Utilisateur
config.send_test_mail=Envoyer courriel de Test
config.test_mail_failed=Impossible d'envoyer un e-mail de test à '%s': %v
config.test_mail_sent=Un e-mail de test à été envoyé à '%s'.
config.oauth_config=Configuration OAuth config.oauth_config=Configuration OAuth
config.oauth_enabled=Activé config.oauth_enabled=Activé
config.cache_config=Configuration du Cache config.cache_config=Configuration du Cache
config.cache_adapter=Adaptateur du Cache config.cache_adapter=Adaptateur du Cache
config.cache_interval=Intervales du Cache config.cache_interval=Intervales du Cache
config.cache_conn=Liaison du Cache config.cache_conn=Liaison du Cache
config.session_config=Configuration de Session config.session_config=Configuration de session
config.session_provider=Fournisseur de Session config.session_provider=Fournisseur de session
config.provider_config=Configurer le Fournisseur config.provider_config=Configuration du fournisseur
config.cookie_name=Nom du Cookie config.cookie_name=Nom du cookie
config.enable_set_cookie=Activer les Cookies config.enable_set_cookie=Activer les cookies
config.gc_interval_time=Intervals GC config.gc_interval_time=Intervals GC
config.session_life_time=Durée de Session config.session_life_time=Durée des sessions
config.https_only=HTTPS uniquement config.https_only=HTTPS uniquement
config.cookie_life_time=Expiration du Cookie config.cookie_life_time=Expiration du cookie
config.picture_config=Configuration d'Image config.picture_config=Configuration d'Image
config.picture_service=Service d'Imagerie config.picture_service=Service d'Imagerie
config.disable_gravatar=Désactiver Gravatar config.disable_gravatar=Désactiver Gravatar
config.log_config=Configuration du Journal config.log_config=Configuration du Journal
config.log_mode=Mode du Journal config.log_mode=Mode du journal
monitor.cron=Tâches Cron monitor.cron=Tâches Cron
monitor.name=Nom monitor.name=Nom
@ -1005,7 +1034,7 @@ monitor.schedule=Planification
monitor.next=Suivant monitor.next=Suivant
monitor.previous=Précédent monitor.previous=Précédent
monitor.execute_times=Nombre d'Éxécutions monitor.execute_times=Nombre d'Éxécutions
monitor.process=Processus en cours d'Éxécution monitor.process=Processus en cours d'éxécution
monitor.desc=Description monitor.desc=Description
monitor.start=Heure de Démarrage monitor.start=Heure de Démarrage
monitor.execute_time=Heure d'Éxécution monitor.execute_time=Heure d'Éxécution
@ -1029,7 +1058,11 @@ create_repo=a créé le dépôt <a href="%s">%s</a>
rename_repo=rebaptisé le dépôt de <code>%[1]s</code> à <a href="%[2]s">%[3]s</a> rename_repo=rebaptisé le dépôt de <code>%[1]s</code> à <a href="%[2]s">%[3]s</a>
commit_repo=a soumis à <a href="%[1]s/src/%[2]s">%[3]s</a> sur <a href="%[1]s">%[4]s</a> commit_repo=a soumis à <a href="%[1]s/src/%[2]s">%[3]s</a> sur <a href="%[1]s">%[4]s</a>
create_issue=`a ouvert un problème <a href="%s/issues/%s">%s#%[2]s</a>` create_issue=`a ouvert un problème <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`tickets clos <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`tickets ré-ouverts <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`pull request créée le <a href="%s/pulls/%s">%s#%[2]s</a>` create_pull_request=`pull request créée le <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request=`pull request fermé <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`pull request ré-ouverte <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue=`a commenté le problème <a href="%s/issues/%s">%s#%[2]s</a>` comment_issue=`a commenté le problème <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request=`pull request fusionné le <a href="%s/pulls/%s">%s#%[2]s</a>` merge_pull_request=`pull request fusionné le <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo=a transféré le dépôt <code>%s</code> à <a href="%s">%s</a> transfer_repo=a transféré le dépôt <code>%s</code> à <a href="%s">%s</a>

251
conf/locale/locale_it-IT.ini

@ -38,19 +38,12 @@ settings=Impostazioni
your_profile=Il tuo profilo your_profile=Il tuo profilo
your_settings=Impostazioni your_settings=Impostazioni
news_feed=Notizie activities=Attivitá
pull_requests=Pull Request pull_requests=Pull Request
issues=Problemi issues=Problemi
cancel=Annulla cancel=Annulla
[search]
search=Ricerca...
repository=Repository
user=Utente
issue=Problema
code=Codice
[install] [install]
install=Installazione install=Installazione
title=Passi d'installazione per il primo avvio title=Passi d'installazione per il primo avvio
@ -65,7 +58,7 @@ db_name=Nome del database
db_helper=Utilizza il motore INNODB con codifica utf8_general_ci per MySQL. db_helper=Utilizza il motore INNODB con codifica utf8_general_ci per MySQL.
ssl_mode=Modalità SSL ssl_mode=Modalità SSL
path=Percorso path=Percorso
sqlite_helper=Il percorso file del database SQLite3 o TiDB. sqlite_helper=Il path assoluto per il database SQLite3 o TiDB. <br>Per favore usa il path assoluto quando lo avvii come servizio.
err_empty_db_path=Il percorso file del database SQLite3 o TiDB non può essere vuoto. err_empty_db_path=Il percorso file del database SQLite3 o TiDB non può essere vuoto.
err_invalid_tidb_name=Il nome del database TiDB non ammette caratteri "." e "-". err_invalid_tidb_name=Il nome del database TiDB non ammette caratteri "." e "-".
no_admin_and_disable_registration=Non puoi disabilitare la registrazione senza aver creato un amministratore. no_admin_and_disable_registration=Non puoi disabilitare la registrazione senza aver creato un amministratore.
@ -79,13 +72,15 @@ repo_path_helper=Tutti i repository Git remoti saranno salvati in questa directo
run_user=Esegui con l'utente run_user=Esegui con l'utente
run_user_helper=L'utente deve avere accesso al percorso root del repository e avviare Gogs. run_user_helper=L'utente deve avere accesso al percorso root del repository e avviare Gogs.
domain=Dominio domain=Dominio
domain_helper=Questo modifica lo SSH clone URLs. domain_helper=Questo influisce sugli URL per il clonaggio via SSH.
ssh_port=Porta SSH ssh_port=Porta SSH
ssh_port_helper=Numero di porta utilizzato dal server SSH, lasciare vuoto per disabilitare l'integrazione SSH. ssh_port_helper=Numero di porta utilizzato dal server SSH, lasciare vuoto per disabilitare l'integrazione SSH.
http_port=Porta HTTP http_port=Porta HTTP
http_port_helper=Porta di ascolto dell'applicazione. http_port_helper=Porta di ascolto dell'applicazione.
app_url=URL Applicazione app_url=URL Applicazione
app_url_helper=Questo influisce sugli URL per il clonaggio via HTTP/HTTPS e da qualche parte nella posta elettronica. app_url_helper=Questo influisce sugli URL per il clonaggio via HTTP/HTTPS e da qualche parte nella posta elettronica.
log_root_path=Percorso dei log
log_root_path_helper=Directory in cui scrivere i file di log.
optional_title=Impostazioni Facoltative optional_title=Impostazioni Facoltative
email_title=Impostazioni E-mail email_title=Impostazioni E-mail
@ -122,6 +117,7 @@ run_user_not_match=Run user non è l'utente corrente: %s -> %s
save_config_failed=Fallito il salvataggio della configurazione: %v save_config_failed=Fallito il salvataggio della configurazione: %v
invalid_admin_setting=Impostazioni account Admin non valide: %v invalid_admin_setting=Impostazioni account Admin non valide: %v
install_success=Benvenuto! Siamo felici che tu abbia scelto Gogs, buon divertimento. install_success=Benvenuto! Siamo felici che tu abbia scelto Gogs, buon divertimento.
invalid_log_root_path=Log root path is invalid: %v
[home] [home]
uname_holder=Nome Utente o E-mail uname_holder=Nome Utente o E-mail
@ -137,6 +133,8 @@ issues.in_your_repos=Nei tuoi repository
[explore] [explore]
repos=Repository repos=Repository
users=Utenti
search=Cerca
[auth] [auth]
create_new_account=Crea un nuovo Account create_new_account=Crea un nuovo Account
@ -203,7 +201,6 @@ repo_name_been_taken=Il nome del Repository è già utilizzato.
org_name_been_taken=Il nome dell'Organizzazione è già utlizzato. org_name_been_taken=Il nome dell'Organizzazione è già utlizzato.
team_name_been_taken=Il nome del Team è già utilizzato. team_name_been_taken=Il nome del Team è già utilizzato.
email_been_used=L'indirizzo E-mail è già utilizzato. email_been_used=L'indirizzo E-mail è già utilizzato.
illegal_team_name=Il nome del Team contiene caratteri non validi.
username_password_incorrect=Nome utente o password incorretti. username_password_incorrect=Nome utente o password incorretti.
enterred_invalid_repo_name=Si prega di assicurarsi che il nome del repository inserito sia corretto. enterred_invalid_repo_name=Si prega di assicurarsi che il nome del repository inserito sia corretto.
enterred_invalid_owner_name=Si prega di assicurarsi che il nome del proprietario inserito sia corretto. enterred_invalid_owner_name=Si prega di assicurarsi che il nome del proprietario inserito sia corretto.
@ -219,8 +216,6 @@ still_own_repo=Il tuo account possiede ancora almeno un repository, dovete prima
still_has_org=Il tuo account è ancora associato ad almeno un'organizzazione, disassociarsi prima. still_has_org=Il tuo account è ancora associato ad almeno un'organizzazione, disassociarsi prima.
org_still_own_repo=Questa organizzazione ha ancora la proprietà del repository, dovete cancellarla o trasferirli prima. org_still_own_repo=Questa organizzazione ha ancora la proprietà del repository, dovete cancellarla o trasferirli prima.
still_own_user=Questa autenticazione è ancora in uso da almeno un utente, per favore rimuovili dall'autenticazione e riprova.
target_branch_not_exist=Il ramo (branch) di destinazione non esiste. target_branch_not_exist=Il ramo (branch) di destinazione non esiste.
[user] [user]
@ -262,11 +257,10 @@ continue=Continua
cancel=Annulla cancel=Annulla
enable_custom_avatar=Abilita avatar personalizzato enable_custom_avatar=Abilita avatar personalizzato
enable_custom_avatar_helper=Seleziona per disabilitare il fetch da Gravatar
choose_new_avatar=Scegli un nuovo avatar choose_new_avatar=Scegli un nuovo avatar
update_avatar=Aggiorna le impostazioni avatar update_avatar=Aggiorna le impostazioni avatar
delete_current_avatar=Elimina Avatar attuale
uploaded_avatar_not_a_image=Il file caricato non è un'immagine. uploaded_avatar_not_a_image=Il file caricato non è un'immagine.
no_custom_avatar_available=Nessun avatar personalizzato disponibile, impossibile abilitarlo.
update_avatar_success=Le tue impostazioni avatar sono state aggiornate con successo. update_avatar_success=Le tue impostazioni avatar sono state aggiornate con successo.
change_password=Cambia Password change_password=Cambia Password
@ -467,7 +461,7 @@ issues.open_title=Aperto
issues.closed_title=Chiuso issues.closed_title=Chiuso
issues.num_comments=%d commenti issues.num_comments=%d commenti
issues.commented_at=`commented <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commented_at=`commented <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.no_content=There is no content yet. issues.no_content=Non ci sono ancora contenuti.
issues.close_issue=Chiudi issues.close_issue=Chiudi
issues.close_comment_issue=Commenta e chiudi issues.close_comment_issue=Commenta e chiudi
issues.reopen_issue=Riapri issues.reopen_issue=Riapri
@ -477,12 +471,12 @@ issues.closed_at=`closed <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.poster=Autore issues.poster=Autore
issues.admin=Amministratore issues.collaborator=Collaboratori
issues.owner=Proprietario issues.owner=Proprietario
issues.sign_up_for_free=Registrati gratuitamente issues.sign_up_for_free=Registrati gratuitamente
issues.sign_in_require_desc=to join this conversation. Already have an account? <a href="%s">Sign in to comment</a> issues.sign_in_require_desc=per partecipare a questa conversazione. Possiedi già un account?<a href="%s">Fai il login per commentare</a>
issues.edit=Modifica issues.edit=Modifica
issues.cancel=Cancel issues.cancel=Annulla
issues.save=Salva issues.save=Salva
issues.label_title=Nome etichetta issues.label_title=Nome etichetta
issues.label_color=Colore etichetta issues.label_color=Colore etichetta
@ -494,46 +488,47 @@ issues.label_modify=Modifica Etichetta
issues.label_deletion=Elimina Etichetta issues.label_deletion=Elimina Etichetta
issues.label_deletion_desc=Eliminare l'etichetta rimuovera le sue informazioni in tutti i problemi correlati. Vuoi continuare? issues.label_deletion_desc=Eliminare l'etichetta rimuovera le sue informazioni in tutti i problemi correlati. Vuoi continuare?
issues.label_deletion_success=Etichetta eliminata con successo! issues.label_deletion_success=Etichetta eliminata con successo!
issues.num_participants=%d Partecipanti
pulls.new=Nuova Pull Request pulls.new=Nuova Pull Request
pulls.compare_changes=Confronta le modifiche pulls.compare_changes=Confronta le modifiche
pulls.compare_changes_desc=Confronta due branch e fai una pull request per le modifiche. pulls.compare_changes_desc=Confronta due branch e fai una pull request per le modifiche.
pulls.compare_base=base pulls.compare_base=base
pulls.compare_compare=confronta pulls.compare_compare=confronta
pulls.filter_branch=Filter branch pulls.filter_branch=Filtra branch
pulls.no_results=Nessun risultato trovato. pulls.no_results=Nessun risultato trovato.
pulls.nothing_to_compare=There is nothing to compare because base and head branches are even. pulls.nothing_to_compare=Non c'è niente da confrontare perchè i branch base e head uguali.
pulls.has_pull_request=`There is already a pull request between these two targets: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>` pulls.has_pull_request=`E' già presente una pull request tra questi due trargets: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
pulls.create=Crea Pull Request pulls.create=Crea Pull Request
pulls.title_desc=wants to merge %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> pulls.title_desc=wants to merge %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code>
pulls.merged_title_desc=merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s pulls.merged_title_desc=merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s
pulls.tab_conversation=Conversazione pulls.tab_conversation=Conversazione
pulls.tab_commits=Commits pulls.tab_commits=Commit
pulls.tab_files=File modificati pulls.tab_files=File modificati
pulls.reopen_to_merge=Riapri questa pull request per effettuare il merge. pulls.reopen_to_merge=Riapri questa pull request per effettuare il merge.
pulls.merged=Merged pulls.merged=Unito
pulls.has_merged=This pull request has been merged successfully! pulls.has_merged=Questa pull reqeust è stata mergiata con successo!
pulls.data_broken=Data of this pull request has been broken due to deletion of fork information. pulls.data_broken=I dati di questa pull request si sono rotti causa dell'eliminazione delle informazioni di fork.
pulls.is_checking=The conflict checking is still in progress, please refresh page in few moments. pulls.is_checking=Il controllo dei conflitti è ancora in corso, per favore aggiorna pagina tra qualche istante.
pulls.can_auto_merge_desc=This pull request can be merged automatically. pulls.can_auto_merge_desc=La pull request non può essere mergiata automaticamente.
pulls.cannot_auto_merge_desc=This pull request can't be merged automatically because there are conflicts. pulls.cannot_auto_merge_desc=Questa pull request non può essere mergiata automaticamente perchè ci sono dei conflitti.
pulls.cannot_auto_merge_helper=Effettua il merge manualmente per risolvere i conflitti. pulls.cannot_auto_merge_helper=Effettua il merge manualmente per risolvere i conflitti.
pulls.merge_pull_request=Unisci Pull Request pulls.merge_pull_request=Unisci Pull Request
pulls.open_unmerged_pull_exists=`You can't perform reopen operation because there is already an open pull request (#%d) from same repository with same merge information and is waiting for merging.` pulls.open_unmerged_pull_exists=`You can't perform reopen operation because there is already an open pull request (#%d) from same repository with same merge information and is waiting for merging.`
milestones.new=Nuova Milestone milestones.new=Nuova Milestone
milestones.open_tab=%d Open milestones.open_tab=%d Aperti
milestones.close_tab=%d Closed milestones.close_tab=%d Chiusi
milestones.closed=Closed %s milestones.closed=Chiuso %s
milestones.no_due_date=No due date milestones.no_due_date=Nessuna data di scadenza
milestones.open=Open milestones.open=Apri
milestones.close=Close milestones.close=Chiudi
milestones.new_subheader=Create milestones to organize your issues. milestones.new_subheader=Crea delle milestones per organizzare le tue issues.
milestones.create=Crea Milestone milestones.create=Crea Milestone
milestones.title=Titolo milestones.title=Titolo
milestones.desc=Descrizione milestones.desc=Descrizione
milestones.due_date=Data di scadenza (opzionale) milestones.due_date=Data di scadenza (opzionale)
milestones.clear=Clear milestones.clear=Pulisci
milestones.invalid_due_date_format=Il formato della data di scadenza non è valido, deve essere 'yyyy-mm-dd'. milestones.invalid_due_date_format=Il formato della data di scadenza non è valido, deve essere 'yyyy-mm-dd'.
milestones.create_success=La Milestone '%s' è stata creata con successo! milestones.create_success=La Milestone '%s' è stata creata con successo!
milestones.edit=Modifica Milestone milestones.edit=Modifica Milestone
@ -557,6 +552,8 @@ wiki.save_page=Salva pagina
wiki.last_commit_info=%s ha modificato questa pagina %s wiki.last_commit_info=%s ha modificato questa pagina %s
wiki.edit_page_button=Modifica wiki.edit_page_button=Modifica
wiki.new_page_button=Nuova pagina wiki.new_page_button=Nuova pagina
wiki.delete_page_button=Cancella Pagina
wiki.delete_page_notice_1=Questo cancellerà lapagina <code>"%s"</code>. Si prega di esserne certi.
wiki.page_already_exists=Esiste già una pagina Wiki con questo stesso nome. wiki.page_already_exists=Esiste già una pagina Wiki con questo stesso nome.
wiki.pages=Pagine wiki.pages=Pagine
wiki.last_updated=Ultimo aggiornamento: %s wiki.last_updated=Ultimo aggiornamento: %s
@ -575,25 +572,34 @@ settings.wiki_desc=Abilitare il wiki per consentire alle persone di scrivere doc
settings.use_external_wiki=Usa Wiki esterno settings.use_external_wiki=Usa Wiki esterno
settings.external_wiki_url=URL Wiki esterno settings.external_wiki_url=URL Wiki esterno
settings.external_wiki_url_desc=I visitatori verranno reindirizzati all'URL quando cliccano sulla scheda. settings.external_wiki_url_desc=I visitatori verranno reindirizzati all'URL quando cliccano sulla scheda.
settings.issues_desc=Enable builtin lightweight issue tracker settings.issues_desc=Abilita l'issue tracker builtin leggero
settings.use_external_issue_tracker=Utilizza gestore di problemi esterno settings.use_external_issue_tracker=Utilizza gestore di problemi esterno
settings.tracker_url_format=External Issue Tracker URL Format settings.tracker_url_format=External Issue Tracker URL Format
settings.tracker_url_format_desc=You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index. settings.tracker_url_format_desc=You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index.
settings.pulls_desc=Enable pull requests to accept public contributions settings.pulls_desc=Abilita le pull requests per accettare contributi pubblici
settings.danger_zone=Zona Pericolosa settings.danger_zone=Zona Pericolosa
settings.new_owner_has_same_repo=Il nuovo proprietario ha già un repository con lo stesso nome. Per favore scegli un altro nome.
settings.convert=Converti in Repository Regolare
settings.convert_desc=Puoi convertire questo mirror in un repository regolare. Questa operazione non può essere annullata.
settings.convert_notices_1=- Questa operazione non potrà essere annullata e convertirà questo mirror in un repository regolare.
settings.convert_confirm=Conferma la conversione
settings.convert_succeed=Repository has been converted to regular type successfully.
settings.transfer=Trasferisci proprietà settings.transfer=Trasferisci proprietà
settings.transfer_desc=Trasferisci questa repository a un altro utente o a un'organizzazione nella quale hai diritti d'amministratore. settings.transfer_desc=Trasferisci questa repository a un altro utente o a un'organizzazione nella quale hai diritti d'amministratore.
settings.new_owner_has_same_repo=Il nuovo proprietario ha già un repository con lo stesso nome. Per favore scegli un altro nome.
settings.delete=Elimina questo repository
settings.delete_desc=Una volta che hai cancellato il repository, non puoi tornare indietro. Si prega di fare attenzione.
settings.transfer_notices_1=- You will lose access if new owner is a individual user. settings.transfer_notices_1=- You will lose access if new owner is a individual user.
settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners. settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners.
settings.transfer_form_title=Per favore inserisci le informazioni seguenti per confermare l'operazione: settings.transfer_form_title=Per favore inserisci le informazioni seguenti per confermare l'operazione:
settings.wiki_delete=Elimina i dati della Wiki
settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain.
settings.wiki_delete_notices_1=- This will delete and disable the wiki for %s
settings.wiki_deletion_success=I dati della wiki del repository sono stati eliminati con successo.
settings.delete=Elimina questo repository
settings.delete_desc=Una volta che hai cancellato il repository, non puoi tornare indietro. Si prega di fare attenzione.
settings.delete_notices_1=-Questa operazione <strong>NON PUÒ</strong> essere annullata. settings.delete_notices_1=-Questa operazione <strong>NON PUÒ</strong> essere annullata.
settings.delete_notices_2=-Questa operazione eliminerà definitivamente il tutto il contenuto del repository, inclusi i dati di Git, incidenti, commenti e accessi dei collaboratori. settings.delete_notices_2=-Questa operazione eliminerà definitivamente il tutto il contenuto del repository, inclusi i dati di Git, incidenti, commenti e accessi dei collaboratori.
settings.delete_notices_fork_1=-Se questo repository è pubblico, tutti i fork diventeranno indipendenti dopo la sua cancellazione. settings.delete_notices_fork_1=-Se questo repository è pubblico, tutti i fork diventeranno indipendenti dopo la sua cancellazione.
settings.delete_notices_fork_2=-Se questo repository è privato, tutti fork verranno rimossi assieme ad esso. settings.delete_notices_fork_2=-Se questo repository è privato, tutti fork verranno rimossi assieme ad esso.
settings.delete_notices_fork_3=- If you want to keep all forks after deletion, please change visibility of this repository to public first. settings.delete_notices_fork_3=- Se vuoi mantenere i forks dopo la cancellazione, per piacere cambia la visibilità di questo repository in pubblica.
settings.deletion_success=Il repository è stato eliminato con successo! settings.deletion_success=Il repository è stato eliminato con successo!
settings.update_settings_success=Le opzioni repository sono state aggiornate con successo. settings.update_settings_success=Le opzioni repository sono state aggiornate con successo.
settings.transfer_owner=Nuovo Proprietario settings.transfer_owner=Nuovo Proprietario
@ -602,19 +608,23 @@ settings.transfer_succeed=Proprietà del repository trasferita con successo.
settings.confirm_delete=Conferma eliminazione settings.confirm_delete=Conferma eliminazione
settings.add_collaborator=Aggiungi nuovo collaboratore settings.add_collaborator=Aggiungi nuovo collaboratore
settings.add_collaborator_success=Il nuovo collaboratore è stato aggiunto. settings.add_collaborator_success=Il nuovo collaboratore è stato aggiunto.
settings.delete_collaborator=Elimina
settings.collaborator_deletion=Eliminazione collaboratore
settings.collaborator_deletion_desc=Questo utente non potrà più collaborare a questo repository dopo l'eliminazione. Si desidera continuare?
settings.remove_collaborator_success=Il collaboratore è stato rimosso. settings.remove_collaborator_success=Il collaboratore è stato rimosso.
settings.search_user_placeholder=Cerca utente... settings.search_user_placeholder=Cerca utente...
settings.org_not_allowed_to_be_collaborator=Un'organizzazione non può essere aggiunta come collaboratore.
settings.user_is_org_member=L'utente è un membro dell'organizzazione che non può essere aggiunto come collaboratore. settings.user_is_org_member=L'utente è un membro dell'organizzazione che non può essere aggiunto come collaboratore.
settings.add_webhook=Aggiungi Webhook settings.add_webhook=Aggiungi Webhook
settings.hooks_desc=I Webhooks sono molto simili a un basilare evento trigger HTTP POST. Ogni volta che qualcosa si verifica in Gogs, tratteremo la notifica all'host di destinazione specificato. Ulteriori informazioni in questa <a target="_blank" href="%s">Guida ai Webhooks</a>. settings.hooks_desc=I Webhooks sono molto simili a un basilare evento trigger HTTP POST. Ogni volta che qualcosa si verifica in Gogs, tratteremo la notifica all'host di destinazione specificato. Ulteriori informazioni in questa <a target="_blank" href="%s">Guida ai Webhooks</a>.
settings.webhook_deletion=Elimina Webhook settings.webhook_deletion=Elimina Webhook
settings.webhook_deletion_desc=Delete this webhook will remove its information and all delivery history. Do you want to continue? settings.webhook_deletion_desc=Delete this webhook will remove its information and all delivery history. Do you want to continue?
settings.webhook_deletion_success=Webhook has been deleted successfully! settings.webhook_deletion_success=Webhook has been deleted successfully!
settings.webhook.test_delivery=Test Delivery settings.webhook.test_delivery=Test di consegna
settings.webhook.test_delivery_desc=Send a fake push event delivery to test your webhook settings settings.webhook.test_delivery_desc=Send a fake push event delivery to test your webhook settings
settings.webhook.test_delivery_success=Test webhook has been added to delivery queue. It may take few seconds before it shows up in the delivery history. settings.webhook.test_delivery_success=Test webhook has been added to delivery queue. It may take few seconds before it shows up in the delivery history.
settings.webhook.request=Request settings.webhook.request=Richiesta
settings.webhook.response=Response settings.webhook.response=Risposta
settings.webhook.headers=Headers settings.webhook.headers=Headers
settings.webhook.payload=Payload settings.webhook.payload=Payload
settings.webhook.body=Body settings.webhook.body=Body
@ -627,17 +637,17 @@ settings.add_webhook_desc=Gogs manderà una richiesta <code>POST</code> all'URL
settings.payload_url=Payload URL settings.payload_url=Payload URL
settings.content_type=Content Type settings.content_type=Content Type
settings.secret=Secret settings.secret=Secret
settings.slack_username=Username settings.slack_username=Nome utente
settings.slack_icon_url=URL icona settings.slack_icon_url=URL icona
settings.slack_color=Color settings.slack_color=Colore
settings.event_desc=Quali eventi dovrebbero innescare questo webhook? settings.event_desc=Quali eventi dovrebbero innescare questo webhook?
settings.event_push_only=Solo l'evento <code>push</code>. settings.event_push_only=Solo l'evento <code>push</code>.
settings.event_send_everything=I need <strong>everything</strong>. settings.event_send_everything=Ho bisogno di <strong>tutto</strong>.
settings.event_choose=Let me choose what I need. settings.event_choose=Lasciami scegliere ciò di cui ho bisogno.
settings.event_create=Create settings.event_create=Crea
settings.event_create_desc=Branch, or tag created settings.event_create_desc=Branch, o tag creato
settings.event_push=Push settings.event_push=Push
settings.event_push_desc=Git push to a repository settings.event_push_desc=Git push in un repository
settings.active=Attivo settings.active=Attivo
settings.active_helper=Anche i dettagli riguardanti l'evento che ha innescato l'hook saranno inviati. settings.active_helper=Anche i dettagli riguardanti l'evento che ha innescato l'hook saranno inviati.
settings.add_hook_success=Il nuovo webhook è stato aggiunto. settings.add_hook_success=Il nuovo webhook è stato aggiunto.
@ -651,25 +661,25 @@ settings.slack_token=Token
settings.slack_domain=Dominio settings.slack_domain=Dominio
settings.slack_channel=Canale settings.slack_channel=Canale
settings.deploy_keys=Dispiega Chiavi settings.deploy_keys=Dispiega Chiavi
settings.add_deploy_key=Add Deploy Key settings.add_deploy_key=Aggiungi Deploy Key
settings.deploy_key_desc=Deploy key only has read-only access. It is not same as personal account SSH keys. settings.deploy_key_desc=Le deploy keys hanno accesso in sola lettura. Non equivalgono alle chiavi SSH personali.
settings.no_deploy_keys=You haven't added any deploy key. settings.no_deploy_keys=Non hai aggiunto alcuna deploy key.
settings.title=Title settings.title=Titolo
settings.deploy_key_content=Content settings.deploy_key_content=Contenuto
settings.key_been_used=Deploy key content has been used. settings.key_been_used=La deploy key è già in uso.
settings.key_name_used=Deploy key with the same name already exists. settings.key_name_used=Esiste già una deploy key con questo nome.
settings.add_key_success=New deploy key '%s' has been added successfully! settings.add_key_success=La nuova deploy key '%s' è stata aggiunta con successo!
settings.deploy_key_deletion=Delete Deploy Key settings.deploy_key_deletion=Elimina Deploy Key
settings.deploy_key_deletion_desc=Deleting this deploy key will remove all related accesses for this repository. Do you want to continue? settings.deploy_key_deletion_desc=Cancellando questa deploy key verrà rismosso ogni accesso relativo a questa repository. Vuoi continuare?
settings.deploy_key_deletion_success=Deploy key has been deleted successfully! settings.deploy_key_deletion_success=Deploy key eliminata con successo!
diff.browse_source=Sfoglia il codice sorgente diff.browse_source=Sfoglia il codice sorgente
diff.parent=parent diff.parent=parent
diff.commit=commit diff.commit=commit
diff.data_not_available=Diff Data non disponibile. diff.data_not_available=Diff Data non disponibile.
diff.show_diff_stats=Mostra Diff Stats diff.show_diff_stats=Mostra Diff Stats
diff.show_split_view=Split View diff.show_split_view=Visualizzazione separata
diff.show_unified_view=Unified View diff.show_unified_view=Visualizzazione unificata
diff.stats_desc=<strong>%d ha cambiato i file</strong> con <strong>%d aggiunte</strong> e <strong>%d eliminazioni</strong> diff.stats_desc=<strong>%d ha cambiato i file</strong> con <strong>%d aggiunte</strong> e <strong>%d eliminazioni</strong>
diff.bin=BIN diff.bin=BIN
diff.view_file=Vedi File diff.view_file=Vedi File
@ -687,27 +697,27 @@ release.edit_subheader=Detailed change log can help users understand what has be
release.tag_name=Nome tag release.tag_name=Nome tag
release.target=Obbiettivo release.target=Obbiettivo
release.tag_helper=Scegli un tag esistente o crea un nuovo tag una volta pubblicato. release.tag_helper=Scegli un tag esistente o crea un nuovo tag una volta pubblicato.
release.title=Title release.title=Titolo
release.content=Content release.content=Contenuto
release.write=Scrivi release.write=Scrivi
release.preview=Anteprima release.preview=Anteprima
release.loading=Caricamento... release.loading=Caricamento...
release.prerelease_desc=Questo è un pre-rilascio release.prerelease_desc=Questo è un pre-rilascio
release.prerelease_helper=Precisiamo che questo rilascio non è pronta per la produzione. release.prerelease_helper=Precisiamo che questo rilascio non è pronta per la produzione.
release.cancel=Cancel release.cancel=Annulla
release.publish=Pubblica Rilascio release.publish=Pubblica Rilascio
release.save_draft=Salva Bozza release.save_draft=Salva Bozza
release.edit_release=Modifica Rilascio release.edit_release=Modifica Rilascio
release.delete_release=Delete This Release release.delete_release=Cancela questa Release
release.deletion=Release Deletion release.deletion=Eliminazione Release
release.deletion_desc=Deleting this release will delete the corresponding Git tag. Do you want to continue? release.deletion_desc=Eliminando questa release cancellarai anche i tag Git corrispondenti. Vuoi continuare?
release.deletion_success=Release has been deleted successfully! release.deletion_success=La release è stata eliminata con successo!
release.tag_name_already_exist=Un rilascio con questo tag esiste già. release.tag_name_already_exist=Un rilascio con questo tag esiste già.
release.downloads=Download release.downloads=Download
[org] [org]
org_name_holder=Nome dell'Organizzazione org_name_holder=Nome dell'Organizzazione
org_full_name_holder=Organization Full Name org_full_name_holder=Nome completo dell'organizzazione
org_name_helper=Le migliori organizzazioni hanno nomi brevi e memorabili. org_name_helper=Le migliori organizzazioni hanno nomi brevi e memorabili.
create_org=Crea Organizzazione create_org=Crea Organizzazione
repo_updated=Aggiornato repo_updated=Aggiornato
@ -734,7 +744,7 @@ settings.website=Sito Web
settings.location=Residenza settings.location=Residenza
settings.update_settings=Aggiorna Impostazioni settings.update_settings=Aggiorna Impostazioni
settings.update_setting_success=Impostazioni dell'organizzazione aggiornate con successo. settings.update_setting_success=Impostazioni dell'organizzazione aggiornate con successo.
settings.change_orgname_prompt=This change will affect how links relate to the organization. settings.change_orgname_prompt=Questa operazione modificherà il modo in cui i links sono in relazione con l'organizzazione.
settings.update_avatar_success=Organization avatar setting has been updated successfully. settings.update_avatar_success=Organization avatar setting has been updated successfully.
settings.delete=Elimina organizzazione settings.delete=Elimina organizzazione
settings.delete_account=Elimina questa organizzazione settings.delete_account=Elimina questa organizzazione
@ -749,7 +759,7 @@ members.public=Pubblico
members.public_helper=rendi privato members.public_helper=rendi privato
members.private=Privato members.private=Privato
members.private_helper=rendi pubblico members.private_helper=rendi pubblico
members.member_role=Member Role: members.member_role=Ruolo del membro:
members.owner=Proprietario members.owner=Proprietario
members.member=Membro members.member=Membro
members.remove=Rimuovere members.remove=Rimuovere
@ -779,7 +789,7 @@ teams.read_permission_desc=Questo Team concede accesso di <strong>Lettura</stron
teams.write_permission_desc=Questo Team concede accesso di <strong>Scrittura</strong>: i membri possono leggere e pushare i repository del Team. teams.write_permission_desc=Questo Team concede accesso di <strong>Scrittura</strong>: i membri possono leggere e pushare i repository del Team.
teams.admin_permission_desc=Questo Team concede accesso di <strong>Amministratore</strong>: i membri possono leggere i, pushare a, e aggiungere collaboratori ai repository del Team. teams.admin_permission_desc=Questo Team concede accesso di <strong>Amministratore</strong>: i membri possono leggere i, pushare a, e aggiungere collaboratori ai repository del Team.
teams.repositories=Repository di Squadra teams.repositories=Repository di Squadra
teams.search_repo_placeholder=Search repository... teams.search_repo_placeholder=Cerca repository...
teams.add_team_repository=Aggiungere Repository di Squadra teams.add_team_repository=Aggiungere Repository di Squadra
teams.remove_repo=Rimuovi teams.remove_repo=Rimuovi
teams.add_nonexistent_repo=Il repository che stai tentando di aggiungere non esiste, crealo prima. teams.add_nonexistent_repo=Il repository che stai tentando di aggiungere non esiste, crealo prima.
@ -793,8 +803,8 @@ authentication=Autenticazioni
config=Configurazione config=Configurazione
notices=Avvisi di sistema notices=Avvisi di sistema
monitor=Monitoraggio monitor=Monitoraggio
first_page=First first_page=Prima
last_page=Last last_page=Ultima
total=Totale: %d total=Totale: %d
dashboard.statistic=Statistiche dashboard.statistic=Statistiche
@ -818,6 +828,8 @@ dashboard.resync_all_sshkeys=Riscrivi il file '.ssh/authorized_keys' (attenzione
dashboard.resync_all_sshkeys_success=Tutte le chiavi pubbliche riscritte con successo. dashboard.resync_all_sshkeys_success=Tutte le chiavi pubbliche riscritte con successo.
dashboard.resync_all_update_hooks=Riscrivere tutti gli update hook dei repository (necessario quando il percorso di configurazione personalizzata viene modificato) dashboard.resync_all_update_hooks=Riscrivere tutti gli update hook dei repository (necessario quando il percorso di configurazione personalizzata viene modificato)
dashboard.resync_all_update_hooks_success=Tutti gli update hook dei repository riscritti con successo. dashboard.resync_all_update_hooks_success=Tutti gli update hook dei repository riscritti con successo.
dashboard.reinit_missing_repos=Reinitialize all repository records that lost Git files
dashboard.reinit_missing_repos_success=All repository records that lost Git files have been reinitialized successfully.
dashboard.server_uptime=Tempo in Attività del Server dashboard.server_uptime=Tempo in Attività del Server
dashboard.current_goroutine=Goroutine Correnti dashboard.current_goroutine=Goroutine Correnti
@ -865,8 +877,8 @@ users.auth_login_name=Authentication Login Name
users.password_helper=Leave it empty to remain unchanged. users.password_helper=Leave it empty to remain unchanged.
users.update_profile_success=Profilo dell'account aggiornato con successo. users.update_profile_success=Profilo dell'account aggiornato con successo.
users.edit_account=Modifica Account users.edit_account=Modifica Account
users.max_repo_creation=Maximum Repository Creation Limit users.max_repo_creation=Limite massimo per la creazione di Repository
users.max_repo_creation_desc=(Set -1 to use global default limit) users.max_repo_creation_desc=(Inserire -1 per usare il limite globale di default)
users.is_activated=Questo account è attivato users.is_activated=Questo account è attivato
users.is_admin=Questo account ha permessi di amministratore users.is_admin=Questo account ha permessi di amministratore
users.allow_git_hook=Questo account ha il permesso di creare hooks di Git users.allow_git_hook=Questo account ha il permesso di creare hooks di Git
@ -891,47 +903,49 @@ repos.stars=Voti
repos.issues=Problemi repos.issues=Problemi
auths.auth_manage_panel=Authentication Manage Panel auths.auth_manage_panel=Authentication Manage Panel
auths.new=Add New Source auths.new=Aggiungi Nuova Origine
auths.name=Nome auths.name=Nome
auths.type=Tipo auths.type=Tipo
auths.enabled=Attivo auths.enabled=Attivo
auths.updated=Aggiornato auths.updated=Aggiornato
auths.auth_type=Authentication Type auths.auth_type=Tipo di autenticazione
auths.auth_name=Authentication Name auths.auth_name=Nome di autenticazione
auths.domain=Dominio auths.domain=Dominio
auths.host=Host auths.host=Host
auths.port=Porta auths.port=Porta
auths.bind_dn=Bind DN auths.bind_dn=Binda DN
auths.bind_password=Bind Password auths.bind_password=Binda Password
auths.bind_password_helper=Warning: This password is stored in plain text. Do not use a high privileged account. auths.bind_password_helper=Attenzione: Questa password è salvata in chiaro. Non usare su un acount con alti privilegi.
auths.user_base=User Search Base auths.user_base=User Search Base
auths.user_dn=User DN auths.user_dn=DN dell'utente
auths.attribute_username=Username attribute auths.attribute_username=Attributo username
auths.attribute_username_placeholder=Leave empty to use sign-in form field value for user name. auths.attribute_username_placeholder=Leave empty to use sign-in form field value for user name.
auths.attribute_name=Attributo Nome auths.attribute_name=Attributo Nome
auths.attribute_surname=Attributo Cognome auths.attribute_surname=Attributo Cognome
auths.attribute_mail=Attributo Email auths.attribute_mail=Attributo Email
auths.filter=User Filter auths.attributes_in_bind=Fetch attributes in Bind DN context
auths.admin_filter=Admin Filter auths.filter=Fitro utente
auths.admin_filter=Filtro Amministratore
auths.ms_ad_sa=Ms Ad SA auths.ms_ad_sa=Ms Ad SA
auths.smtp_auth=SMTP Authentication Type auths.smtp_auth=Tipo di autenticazione SMTP
auths.smtphost=Host SMTP auths.smtphost=Host SMTP
auths.smtpport=Porta SMTP auths.smtpport=Porta SMTP
auths.allowed_domains=Allowed Domains auths.allowed_domains=Domini consentiti
auths.allowed_domains_helper=Leave it empty to not restrict any domains. Multiple domains should be separated by comma ','. auths.allowed_domains_helper=Leave it empty to not restrict any domains. Multiple domains should be separated by comma ','.
auths.enable_tls=Abilitare Crittografia TLS auths.enable_tls=Abilitare Crittografia TLS
auths.skip_tls_verify=Salta verifica TLS auths.skip_tls_verify=Salta verifica TLS
auths.pam_service_name=Nome del Servizio PAM auths.pam_service_name=Nome del Servizio PAM
auths.enable_auto_register=Abilitare Registrazione Automatica auths.enable_auto_register=Abilitare Registrazione Automatica
auths.tips=Consigli auths.tips=Consigli
auths.edit=Edit Authentication Setting auths.edit=Modifica impostazioni di autenticazione
auths.activated=Questa Autenticazione è stata attivata auths.activated=Questa Autenticazione è stata attivata
auths.new_success=New authentication '%s' has been added successfully. auths.new_success=New authentication '%s' has been added successfully.
auths.update_success=Authentication setting has been updated successfully. auths.update_success=Authentication setting has been updated successfully.
auths.update=Update Authentication Setting auths.update=Aggiornare le impostazioni di autenticazione
auths.delete=Delete This Authentication auths.delete=Elimina questa autenticazione
auths.delete_auth_title=Authentication Deletion auths.delete_auth_title=Authentication Deletion
auths.delete_auth_desc=This authentication is going to be deleted, do you want to continue? auths.delete_auth_desc=This authentication is going to be deleted, do you want to continue?
auths.still_in_used=This authentication is still used by some users, please delete or convert these users to another login type first.
auths.deletion_success=Authentication has been deleted successfully! auths.deletion_success=Authentication has been deleted successfully!
config.server_config=Configurazione Server config.server_config=Configurazione Server
@ -948,6 +962,19 @@ config.static_file_root_path=Percorso Root del File Statico
config.log_file_root_path=Percorso Root del File di Log config.log_file_root_path=Percorso Root del File di Log
config.script_type=Tipo di Script config.script_type=Tipo di Script
config.reverse_auth_user=Autenticazione Utente Inversa config.reverse_auth_user=Autenticazione Utente Inversa
config.ssh_config=Configurazione SSH
config.ssh_enabled=Attivo
config.ssh_start_builtin_server=Avvia server builtin
config.ssh_domain=Dominio
config.ssh_port=Porta
config.ssh_listen_port=Porta in ascolto
config.ssh_root_path=Percorso Root
config.ssh_key_test_path=Percorso chiave di test
config.ssh_keygen_path=Percorso Keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Verifica delle dimensioni minime della chiave
config.ssh_minimum_key_sizes=Dimensioni minime della chiave
config.db_config=Configurazione Database config.db_config=Configurazione Database
config.db_type=Tipo config.db_type=Tipo
config.db_host=Host config.db_host=Host
@ -956,20 +983,19 @@ config.db_user=Utente
config.db_ssl_mode=Modalità SSL config.db_ssl_mode=Modalità SSL
config.db_ssl_mode_helper=(solo per "postgres") config.db_ssl_mode_helper=(solo per "postgres")
config.db_path=Percorso config.db_path=Percorso
config.db_path_helper=(for "sqlite3" and "tidb") config.db_path_helper=(per "sqlite3" e "tidb")
config.service_config=Configurazione Servizio config.service_config=Configurazione Servizio
config.register_email_confirm=Richiedono Conferma dell'Email config.register_email_confirm=Richiedono Conferma dell'Email
config.disable_register=Disabilita Registrazione config.disable_register=Disabilita Registrazione
config.show_registration_button=Mostra Pulsane Registrazione config.show_registration_button=Mostra Pulsane Registrazione
config.require_sign_in_view=Richiesto Accesso per Vedere config.require_sign_in_view=Richiesto Accesso per Vedere
config.enable_cache_avatar=Abilitare Cache dell'Avatar
config.mail_notify=Email di Notifica config.mail_notify=Email di Notifica
config.disable_key_size_check=Disable Minimum Key Size Check config.disable_key_size_check=Disabilita controllo sulle dimensioni minime della chiave
config.enable_captcha=Abilita Captcha config.enable_captcha=Abilita Captcha
config.active_code_lives=Attiva Vita del Codice config.active_code_lives=Attiva Vita del Codice
config.reset_password_code_lives=Reimpostare Password della Vita del Codice config.reset_password_code_lives=Reimpostare Password della Vita del Codice
config.webhook_config=Configurazione Webhook config.webhook_config=Configurazione Webhook
config.queue_length=Queue Length config.queue_length=Lunghezza della coda
config.deliver_timeout=Tempo Limite di Consegna config.deliver_timeout=Tempo Limite di Consegna
config.skip_tls_verify=Salta verifiche TLS config.skip_tls_verify=Salta verifiche TLS
config.mailer_config=Configurazione Mailer config.mailer_config=Configurazione Mailer
@ -978,6 +1004,9 @@ config.mailer_disable_helo=Disattiva HELO
config.mailer_name=Nome config.mailer_name=Nome
config.mailer_host=Host config.mailer_host=Host
config.mailer_user=Utente config.mailer_user=Utente
config.send_test_mail=Invia email di test
config.test_mail_failed=Impossibile inviare mail a '%s': %v
config.test_mail_sent=Una mail di prova è stata inviata a '%s'.
config.oauth_config=Configurazione OAuth config.oauth_config=Configurazione OAuth
config.oauth_enabled=Attivo config.oauth_enabled=Attivo
config.cache_config=Configurazione Cache config.cache_config=Configurazione Cache
@ -1011,25 +1040,29 @@ monitor.start=Orario Avvio
monitor.execute_time=Tempo di Esecuzione monitor.execute_time=Tempo di Esecuzione
notices.system_notice_list=Avvisi di Sistema notices.system_notice_list=Avvisi di Sistema
notices.view_detail_header=View Notice Detail notices.view_detail_header=Visualizza dettagli dell'avviso
notices.actions=Actions notices.actions=Azioni
notices.select_all=Select All notices.select_all=Seleziona tutto
notices.deselect_all=Deselect All notices.deselect_all=Deseleziona tutto
notices.inverse_selection=Inverse Selection notices.inverse_selection=Inverti selezione
notices.delete_selected=Delete Selected notices.delete_selected=Elimina selezionati
notices.delete_all=Delete All Notices notices.delete_all=Elimina tutti gli avvisi
notices.type=Tipo notices.type=Tipo
notices.type_1=Repository notices.type_1=Repository
notices.desc=Descrizione notices.desc=Descrizione
notices.op=Op. notices.op=Op.
notices.delete_success=System notices have been deleted successfully. notices.delete_success=Gli avvisi di sistema sono stati successivamente eliminati.
[action] [action]
create_repo=ha creato il repository <a href="%s">%s</a> create_repo=ha creato il repository <a href="%s">%s</a>
rename_repo=repository rinominato da <code>%[1]s</code> a <a href="%[2]s">[3]s</a> rename_repo=repository rinominato da <code>%[1]s</code> a <a href="%[2]s">[3]s</a>
commit_repo=ha pushato nel <a href="%[1]s/src/%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a> commit_repo=ha pushato nel <a href="%[1]s/src/%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a>
create_issue=`ha aperto il problema <a href="%s/issues/%s">%s#%[2]s</a>` create_issue=`ha aperto il problema <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`closed issue <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`reopened issue <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`creata pull request <a href="%s/pulls/%s">%s#%[2]s</a>` create_pull_request=`creata pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request=`closed pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`reopened pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue=`ha commentato il problema <a href="%s/issues/%s">%s#%[2]s</a>` comment_issue=`ha commentato il problema <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request=`merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>` merge_pull_request=`merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo=ha trasferito il repository <code>%s</code> a <a href="%s">%s</a> transfer_repo=ha trasferito il repository <code>%s</code> a <a href="%s">%s</a>
@ -1061,5 +1094,5 @@ raw_minutes=minuti
default_message=Drop files here or click to upload. default_message=Drop files here or click to upload.
invalid_input_type=You can't upload files of this type. invalid_input_type=You can't upload files of this type.
file_too_big=File size ({{filesize}} MB) exceeds maximum size ({{maxFilesize}} MB). file_too_big=File size ({{filesize}} MB) exceeds maximum size ({{maxFilesize}} MB).
remove_file=Remove file remove_file=Rimuovi file

95
conf/locale/locale_ja-JP.ini

@ -38,23 +38,16 @@ settings=設定
your_profile=あなたのプロファイル your_profile=あなたのプロファイル
your_settings=あなたの設定 your_settings=あなたの設定
news_feed=ニュースのフィード activities=アクティビティ
pull_requests=プルリクエスト pull_requests=プルリクエスト
issues=課題 issues=課題
cancel=キャンセル cancel=キャンセル
[search]
search=検索...
repository=リポジトリ
user=ユーザ
issue=課題
code=コード
[install] [install]
install=インストール install=インストール
title=初回実行のインストール手順 title=インストールをする前に必要な準備をしましょう
docker_helper=DockerでGogsを稼動する場合、このページに変更を加えるまえに、 <a target="_blank" href="%s">Guidelines</a>をよく読んでください! docker_helper=DockerでGogsを稼動する場合は、このページに変更を加える前に、 <a target="_blank" href="%s">ガイドライン</a>をよく読んでください!
requite_db_desc=Gogs は、MySQL、PostgreSQL、SQLite3 または TiDB が必要です。 requite_db_desc=Gogs は、MySQL、PostgreSQL、SQLite3 または TiDB が必要です。
db_title=データベース設定 db_title=データベース設定
db_type=データベースの種類 db_type=データベースの種類
@ -62,10 +55,10 @@ host=ホスト
user=ユーザ user=ユーザ
password=パスワード password=パスワード
db_name=データベース名 db_name=データベース名
db_helper=Mysql INNODB エンジン utf8_general_ci の文字セットを使用してください db_helper=MySQLではエンジンがINNODB、文字セットがutf8_general_ciである必要があります
ssl_mode=SSL モード ssl_mode=SSL モード
path=パス path=パス
sqlite_helper=SQLite3 または TiDB のデータベースのファイル パス sqlite_helper=SQLite3かTiDBのデータベースのファイルパス。<br>サービスとして開始する際には絶対パスを利用してください
err_empty_db_path=SQLite3 または TiDB データベースのパスを空にすることはできません。 err_empty_db_path=SQLite3 または TiDB データベースのパスを空にすることはできません。
err_invalid_tidb_name=TiDB データベース名は文字"."と"-"を許可しない。 err_invalid_tidb_name=TiDB データベース名は文字"."と"-"を許可しない。
no_admin_and_disable_registration=管理者アカウントを作成せずに登録を無効にすることはできません。 no_admin_and_disable_registration=管理者アカウントを作成せずに登録を無効にすることはできません。
@ -86,6 +79,8 @@ http_port=HTTP ポート
http_port_helper=アプリケーションが待ち受けするポート番号。 http_port_helper=アプリケーションが待ち受けするポート番号。
app_url=アプリケーションの URL app_url=アプリケーションの URL
app_url_helper=この設定は、HTTP / HTTPSのクローンURLおよび、一部のメールボックスへのリンクに影響を与えます。 app_url_helper=この設定は、HTTP / HTTPSのクローンURLおよび、一部のメールボックスへのリンクに影響を与えます。
log_root_path=ログのパス
log_root_path_helper=ログファイルを書き込むディレクトリ。
optional_title=オプション設定 optional_title=オプション設定
email_title=E-mailサービス設定 email_title=E-mailサービス設定
@ -97,10 +92,10 @@ mailer_password=送信者のパスワード
register_confirm=登録の確認を有効にする register_confirm=登録の確認を有効にする
mail_notify=メール通知を有効にする mail_notify=メール通知を有効にする
server_service_title=サーバーとその他のサービスの設定 server_service_title=サーバーとその他のサービスの設定
offline_mode=オフラインモード有効化 offline_mode=オフラインモードを有効にする
offline_mode_popup=プロダクション モードでCDN を無効にし、すべてのリソースファイルをローカルで提供します offline_mode_popup=プロダクションモードでは、CDNを使用せずにローカルからリソースファイルを使用します
disable_gravatar=Gravatarのサービスを無効にします disable_gravatar=Gravatarのサービスを無効にします
disable_gravatar_popup=Disable Gravatar and custom sources, all avatars are uploaded by users or default. disable_gravatar_popup=Gravatarとカスタムソースを無効にして、全てのアバターをユーザーによってアップロードされたものかデフォルトなものにします。
disable_registration=自己登録を無効にする disable_registration=自己登録を無効にする
disable_registration_popup=自己登録を無効にし、管理者のみがアカウント作成できる disable_registration_popup=自己登録を無効にし、管理者のみがアカウント作成できる
enable_captcha=Captchaを有効にする enable_captcha=Captchaを有効にする
@ -122,6 +117,7 @@ run_user_not_match=実行ユーザーは、現在のユーザーではない: %s
save_config_failed=構成の保存に失敗した: %v save_config_failed=構成の保存に失敗した: %v
invalid_admin_setting=管理者アカウントの設定が無効です: %v invalid_admin_setting=管理者アカウントの設定が無効です: %v
install_success=ようこそ!我々はあなたが Gogs を選んでくれて嬉しいです!楽しみましょう! install_success=ようこそ!我々はあなたが Gogs を選んでくれて嬉しいです!楽しみましょう!
invalid_log_root_path=ログのルートパスがむこうです: %v
[home] [home]
uname_holder=ユーザー名またはEメール uname_holder=ユーザー名またはEメール
@ -137,6 +133,8 @@ issues.in_your_repos=あなたのリポジトリ
[explore] [explore]
repos=リポジトリ repos=リポジトリ
users=Users
search=Search
[auth] [auth]
create_new_account=新規アカウントを作成 create_new_account=新規アカウントを作成
@ -165,7 +163,7 @@ activate_account=あなたのアカウントを有効にしてください。
activate_email=電子メール アドレスを確認します。 activate_email=電子メール アドレスを確認します。
reset_password=パスワードをリセットします. reset_password=パスワードをリセットします.
register_success=ようこそ、登録成功 register_success=ようこそ、登録成功
register_notify=Welcome on board register_notify=ボードへようこそ
[modal] [modal]
yes=はい yes=はい
@ -203,7 +201,6 @@ repo_name_been_taken=リポジトリ名は既に使用されています。
org_name_been_taken=組織名は既に使用されています。 org_name_been_taken=組織名は既に使用されています。
team_name_been_taken=チーム名は既に使用されています。 team_name_been_taken=チーム名は既に使用されています。
email_been_used=電子メール アドレスは既に使用されています。 email_been_used=電子メール アドレスは既に使用されています。
illegal_team_name=チーム名に無効な文字が含まれています。
username_password_incorrect=ユーザー名またはパスワードが正しくありません。 username_password_incorrect=ユーザー名またはパスワードが正しくありません。
enterred_invalid_repo_name=入力したリポジトリの名前が正しいかどうかを確認してください。 enterred_invalid_repo_name=入力したリポジトリの名前が正しいかどうかを確認してください。
enterred_invalid_owner_name=入力された所有者名が正しいかどうかを確認してください。 enterred_invalid_owner_name=入力された所有者名が正しいかどうかを確認してください。
@ -219,8 +216,6 @@ still_own_repo=アカウント所有のリポジトリがあり、リポジト
still_has_org=アカウントはまだ組織のメンバーであり、組織から退出するか削除する必要があります。 still_has_org=アカウントはまだ組織のメンバーであり、組織から退出するか削除する必要があります。
org_still_own_repo=この組織はまだリポジトリの所有しています、リポジトリを削除または転送する必要があります。 org_still_own_repo=この組織はまだリポジトリの所有しています、リポジトリを削除または転送する必要があります。
still_own_user=この認証はまだ一部のユーザーによって使用されています。一部のユーザを移動させてから、もう一度削除してください。
target_branch_not_exist=ターゲットブランチが存在しない target_branch_not_exist=ターゲットブランチが存在しない
[user] [user]
@ -262,11 +257,10 @@ continue=続行
cancel=キャンセル cancel=キャンセル
enable_custom_avatar=カスタムのアバターを有効にする enable_custom_avatar=カスタムのアバターを有効にする
enable_custom_avatar_helper=Gravatarからのフェッチを無効にするのを、有効にします
choose_new_avatar=新しいアバターを選択 choose_new_avatar=新しいアバターを選択
update_avatar=アバターの設定を更新 update_avatar=アバターの設定を更新
delete_current_avatar=現在のアバターを削除
uploaded_avatar_not_a_image=アップロードされたファイルは画像ではない。 uploaded_avatar_not_a_image=アップロードされたファイルは画像ではない。
no_custom_avatar_available=利用可能なカスタム アバターがないため、有効にできません。
update_avatar_success=あなたのアバターの設定が更新されました。 update_avatar_success=あなたのアバターの設定が更新されました。
change_password=パスワードを変更 change_password=パスワードを変更
@ -377,7 +371,7 @@ migrate.permission_denied=ローカル リポジトリをインポートする
migrate.invalid_local_path=ローカルパスが無効です。存在しないかディレクトリではありません。 migrate.invalid_local_path=ローカルパスが無効です。存在しないかディレクトリではありません。
migrate.failed=移行に失敗しました: %v migrate.failed=移行に失敗しました: %v
mirror_from=mirror from mirror_from=mirror of
forked_from=フォーク元 forked_from=フォーク元
fork_from_self=すでにあなたの所有しているリポジトリはフォークできません fork_from_self=すでにあなたの所有しているリポジトリはフォークできません
copy_link=コピー copy_link=コピー
@ -477,7 +471,7 @@ issues.closed_at=`closed <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.poster=ポスター issues.poster=ポスター
issues.admin=アドミン issues.collaborator=Collaborator
issues.owner=オーナー issues.owner=オーナー
issues.sign_up_for_free=無料でサインアップ issues.sign_up_for_free=無料でサインアップ
issues.sign_in_require_desc=to join this conversation. Already have an account? <a href="%s">Sign in to comment</a> issues.sign_in_require_desc=to join this conversation. Already have an account? <a href="%s">Sign in to comment</a>
@ -494,6 +488,7 @@ issues.label_modify=ラベルの変更
issues.label_deletion=ラベルの削除 issues.label_deletion=ラベルの削除
issues.label_deletion_desc=ラベルを削除すると、関連するすべての問題の情報が削除されます。続行しますか。 issues.label_deletion_desc=ラベルを削除すると、関連するすべての問題の情報が削除されます。続行しますか。
issues.label_deletion_success=ラベルは正常に削除されました。 issues.label_deletion_success=ラベルは正常に削除されました。
issues.num_participants=%d Participants
pulls.new=新しいプルリクエスト pulls.new=新しいプルリクエスト
pulls.compare_changes=変更を比較 pulls.compare_changes=変更を比較
@ -557,6 +552,8 @@ wiki.save_page=ページを保存
wiki.last_commit_info=%s このページを編集 %s wiki.last_commit_info=%s このページを編集 %s
wiki.edit_page_button=編集 wiki.edit_page_button=編集
wiki.new_page_button=新規ページ wiki.new_page_button=新規ページ
wiki.delete_page_button=Delete Page
wiki.delete_page_notice_1=This will delete the page <code>"%s"</code>. Please be certain.
wiki.page_already_exists=既に同じ名前のWiki ページが存在します。 wiki.page_already_exists=既に同じ名前のWiki ページが存在します。
wiki.pages=ページ wiki.pages=ページ
wiki.last_updated=最終更新 %s wiki.last_updated=最終更新 %s
@ -581,18 +578,27 @@ settings.tracker_url_format=外部課題トラッキングツール URLのフォ
settings.tracker_url_format_desc=You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index. settings.tracker_url_format_desc=You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index.
settings.pulls_desc=Enable pull requests to accept public contributions settings.pulls_desc=Enable pull requests to accept public contributions
settings.danger_zone=危険地帯 settings.danger_zone=危険地帯
settings.new_owner_has_same_repo=新しいオーナーは、既に同じ名前のリポジトリを持っています。
settings.convert=Convert To Regular Repository
settings.convert_desc=You can convert this mirror to a regular repository. This cannot be reversed.
settings.convert_notices_1=- This operation will convert this repository mirror into a regular repository and cannot be undone.
settings.convert_confirm=Confirm Conversion
settings.convert_succeed=Repository has been converted to regular type successfully.
settings.transfer=オーナー移転 settings.transfer=オーナー移転
settings.transfer_desc=リポジトリをあなたが管理者権限を持っている別のユーザーまた組織に移譲します。 settings.transfer_desc=リポジトリをあなたが管理者権限を持っている別のユーザーまた組織に移譲します。
settings.new_owner_has_same_repo=新しいオーナーは、既に同じ名前のリポジトリを持っています。
settings.delete=このリポジトリを削除
settings.delete_desc=リポジトリを削除すると元に戻せません。確実に確認してください。
settings.transfer_notices_1=-新しい所有者が個人ユーザーの場合、あなたがアクセスできなくなります。 settings.transfer_notices_1=-新しい所有者が個人ユーザーの場合、あなたがアクセスできなくなります。
settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners. settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners.
settings.transfer_form_title=操作を確認するために、以下の情報を入力してください。 settings.transfer_form_title=操作を確認するために、以下の情報を入力してください。
settings.wiki_delete=Erase Wiki Data
settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain.
settings.wiki_delete_notices_1=- This will delete and disable the wiki for %s
settings.wiki_deletion_success=Repository wiki data have been erased successfully.
settings.delete=このリポジトリを削除
settings.delete_desc=リポジトリを削除すると元に戻せません。確実に確認してください。
settings.delete_notices_1=-この操作は<strong>元に戻せません</strong> 。 settings.delete_notices_1=-この操作は<strong>元に戻せません</strong> 。
settings.delete_notices_2=- This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators. settings.delete_notices_2=- This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators.
settings.delete_notices_fork_1=- If this repository is public, all forks will be became independent after deletion. settings.delete_notices_fork_1=- If this repository is public, all forks will become independent after deletion.
settings.delete_notices_fork_2=- If this repository is private, all forks will be removed at the same time. settings.delete_notices_fork_2=もしプライペートリポジトリの場合、全てのフォークも同時に削除されます。
settings.delete_notices_fork_3=- If you want to keep all forks after deletion, please change visibility of this repository to public first. settings.delete_notices_fork_3=- If you want to keep all forks after deletion, please change visibility of this repository to public first.
settings.deletion_success=Repository has been deleted successfully! settings.deletion_success=Repository has been deleted successfully!
settings.update_settings_success=リポジトリ オプションが更新されました。 settings.update_settings_success=リポジトリ オプションが更新されました。
@ -602,8 +608,12 @@ settings.transfer_succeed=リポジトリの所有権は正常に転送されま
settings.confirm_delete=削除の確認 settings.confirm_delete=削除の確認
settings.add_collaborator=新しい共同編集者を追加 settings.add_collaborator=新しい共同編集者を追加
settings.add_collaborator_success=新しい共同編集者が追加されました。 settings.add_collaborator_success=新しい共同編集者が追加されました。
settings.delete_collaborator=Delete
settings.collaborator_deletion=Collaborator Deletion
settings.collaborator_deletion_desc=This user will no longer have collaboration access to this repository after deletion. Do you want to continue?
settings.remove_collaborator_success=共同編集者が削除されました。 settings.remove_collaborator_success=共同編集者が削除されました。
settings.search_user_placeholder=Search users settings.search_user_placeholder=Search users
settings.org_not_allowed_to_be_collaborator=組織を共同編集者として追加することはできません。
settings.user_is_org_member=ユーザーは組織の一員なので、共同編集者として追加することはできません。 settings.user_is_org_member=ユーザーは組織の一員なので、共同編集者として追加することはできません。
settings.add_webhook=Webhook を追加 settings.add_webhook=Webhook を追加
settings.hooks_desc=Webhooksは、Gogsで特定のイベントの発生時に指定された外部サービスに通知を許可します。イベントが発生すると、それぞれ指定されたUrlに、POSTリクエストが送られます。詳細はこちらのの <a target="_blank"href="%s"> Webhooks ガイド</a>をご覧ください。 settings.hooks_desc=Webhooksは、Gogsで特定のイベントの発生時に指定された外部サービスに通知を許可します。イベントが発生すると、それぞれ指定されたUrlに、POSTリクエストが送られます。詳細はこちらのの <a target="_blank"href="%s"> Webhooks ガイド</a>をご覧ください。
@ -668,7 +678,7 @@ diff.parent=親
diff.commit=コミット diff.commit=コミット
diff.data_not_available=差分データは利用できません。 diff.data_not_available=差分データは利用できません。
diff.show_diff_stats=差分情報を表示 diff.show_diff_stats=差分情報を表示
diff.show_split_view=Split View diff.show_split_view=分割表示
diff.show_unified_view=Unified View diff.show_unified_view=Unified View
diff.stats_desc=共有<strong>%d 個のファイルを変更した</strong>、<strong>%d 個の追加</strong> と <strong>%d 個の削除</strong>を含む diff.stats_desc=共有<strong>%d 個のファイルを変更した</strong>、<strong>%d 個の追加</strong> と <strong>%d 個の削除</strong>を含む
diff.bin=BIN diff.bin=BIN
@ -700,7 +710,7 @@ release.save_draft=下書きを保存
release.edit_release=リリースを編集 release.edit_release=リリースを編集
release.delete_release=このリリースを削除 release.delete_release=このリリースを削除
release.deletion=リリースの削除 release.deletion=リリースの削除
release.deletion_desc=Deleting this release will delete the corresponding Git tag. Do you want to continue? release.deletion_desc=このリリースを削除すると、対応するGitのタグも削除されます。よろしいですか?
release.deletion_success=リリースが正常に削除されました。 release.deletion_success=リリースが正常に削除されました。
release.tag_name_already_exist=このタグ名には既にリリースが存在します。 release.tag_name_already_exist=このタグ名には既にリリースが存在します。
release.downloads=Downloads release.downloads=Downloads
@ -818,6 +828,8 @@ dashboard.resync_all_sshkeys='.ssh/ authorized_keys' ファイルを再生成し
dashboard.resync_all_sshkeys_success=すべての公開鍵が正常に書き換えられました。 dashboard.resync_all_sshkeys_success=すべての公開鍵が正常に書き換えられました。
dashboard.resync_all_update_hooks=リポジトリの update フックをすべて再更新する(カスタムの設定パスが変更されたときに必要) dashboard.resync_all_update_hooks=リポジトリの update フックをすべて再更新する(カスタムの設定パスが変更されたときに必要)
dashboard.resync_all_update_hooks_success=リポジトリの update フックがすべて正常に再更新されました。 dashboard.resync_all_update_hooks_success=リポジトリの update フックがすべて正常に再更新されました。
dashboard.reinit_missing_repos=Reinitialize all repository records that lost Git files
dashboard.reinit_missing_repos_success=All repository records that lost Git files have been reinitialized successfully.
dashboard.server_uptime=サーバーの稼働時間 dashboard.server_uptime=サーバーの稼働時間
dashboard.current_goroutine=現在のGoroutine dashboard.current_goroutine=現在のGoroutine
@ -911,6 +923,7 @@ auths.attribute_username_placeholder=Leave empty to use sign-in form field value
auths.attribute_name=名前属性 auths.attribute_name=名前属性
auths.attribute_surname=名字属性 auths.attribute_surname=名字属性
auths.attribute_mail=Eメール属性 auths.attribute_mail=Eメール属性
auths.attributes_in_bind=Fetch attributes in Bind DN context
auths.filter=User フィルター auths.filter=User フィルター
auths.admin_filter=Admin フィルター auths.admin_filter=Admin フィルター
auths.ms_ad_sa=Ms Ad SA auths.ms_ad_sa=Ms Ad SA
@ -932,6 +945,7 @@ auths.update=認証設定を更新
auths.delete=この認証を削除 auths.delete=この認証を削除
auths.delete_auth_title=認証削除 auths.delete_auth_title=認証削除
auths.delete_auth_desc=認証を削除します、継続しますか? auths.delete_auth_desc=認証を削除します、継続しますか?
auths.still_in_used=This authentication is still used by some users, please delete or convert these users to another login type first.
auths.deletion_success=認証が正常に削除されました。 auths.deletion_success=認証が正常に削除されました。
config.server_config=サーバーの構成 config.server_config=サーバーの構成
@ -948,6 +962,19 @@ config.static_file_root_path=静的ファイルのルートパス
config.log_file_root_path=ログ ファイルのルート パス config.log_file_root_path=ログ ファイルのルート パス
config.script_type=スクリプトの種類 config.script_type=スクリプトの種類
config.reverse_auth_user=リバース認証ユーザ config.reverse_auth_user=リバース認証ユーザ
config.ssh_config=SSH Configuration
config.ssh_enabled=Enabled
config.ssh_start_builtin_server=Start Builtin Server
config.ssh_domain=Domain
config.ssh_port=Port
config.ssh_listen_port=Listen Port
config.ssh_root_path=Root Path
config.ssh_key_test_path=Key Test Path
config.ssh_keygen_path=Keygen ('ssh-keygen') Path
config.ssh_minimum_key_size_check=Minimum Key Size Check
config.ssh_minimum_key_sizes=Minimum Key Sizes
config.db_config=データベースの構成 config.db_config=データベースの構成
config.db_type=タイプ config.db_type=タイプ
config.db_host=ホスト config.db_host=ホスト
@ -962,7 +989,6 @@ config.register_email_confirm=電子メールの確認を必要
config.disable_register=登録を無効にする config.disable_register=登録を無効にする
config.show_registration_button=登録ボタンを表示します。 config.show_registration_button=登録ボタンを表示します。
config.require_sign_in_view=サインインを要求 config.require_sign_in_view=サインインを要求
config.enable_cache_avatar=アバターのキャッシュを有効にします。
config.mail_notify=メール通知 config.mail_notify=メール通知
config.disable_key_size_check=最小キー サイズ チェックを無効にします config.disable_key_size_check=最小キー サイズ チェックを無効にします
config.enable_captcha=Captchaを有効にする config.enable_captcha=Captchaを有効にする
@ -978,6 +1004,9 @@ config.mailer_disable_helo=HELOコマンド無効
config.mailer_name=名前 config.mailer_name=名前
config.mailer_host=ホスト config.mailer_host=ホスト
config.mailer_user=ユーザ config.mailer_user=ユーザ
config.send_test_mail=Send Test Email
config.test_mail_failed=Fail to send test email to '%s': %v
config.test_mail_sent=Test email has been sent to '%s'.
config.oauth_config=OAuth 構成 config.oauth_config=OAuth 構成
config.oauth_enabled=Enabled config.oauth_enabled=Enabled
config.cache_config=キャッシュの構成 config.cache_config=キャッシュの構成
@ -1029,7 +1058,11 @@ create_repo=リポジトリ <a href="%s"> %s</a>を作成しました
rename_repo=<code>%[1]s</code> から <a href="%[2]s">[3]s</a> にリポジトリ名を変更した rename_repo=<code>%[1]s</code> から <a href="%[2]s">[3]s</a> にリポジトリ名を変更した
commit_repo=<a href="%[1]s">%[4]s</a>を<a href="%[1]s/src/%[2]s">%[3]s</a>にプッシュしました commit_repo=<a href="%[1]s">%[4]s</a>を<a href="%[1]s/src/%[2]s">%[3]s</a>にプッシュしました
create_issue=`問題 <a href="%s/issues/%s">%s#%[2]s</a> を開きました` create_issue=`問題 <a href="%s/issues/%s">%s#%[2]s</a> を開きました`
close_issue=`closed issue <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`reopened issue <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`プルリクエスト <a href="%s/pulls/%s"> %s[2]s</a>を作成` create_pull_request=`プルリクエスト <a href="%s/pulls/%s"> %s[2]s</a>を作成`
close_pull_request=`closed pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`reopened pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue=`問題 <a href="%s/issues/%s">%s#%[2]s</a> のコメント` comment_issue=`問題 <a href="%s/issues/%s">%s#%[2]s</a> のコメント`
merge_pull_request=`プルリクエスト <a href="%s/pulls/%s"> %s[2]s</a>をマージしました` merge_pull_request=`プルリクエスト <a href="%s/pulls/%s"> %s[2]s</a>をマージしました`
transfer_repo=リポジトリ <code>%s</code> を <a href="%s">%s</a> へ転送しました transfer_repo=リポジトリ <code>%s</code> を <a href="%s">%s</a> へ転送しました

71
conf/locale/locale_lv-LV.ini

@ -38,19 +38,12 @@ settings=Iestatījumi
your_profile=Tavs profils your_profile=Tavs profils
your_settings=Tavi iestatījumi your_settings=Tavi iestatījumi
news_feed=Jaunumu plūsma activities=Aktivitāte
pull_requests=Izmaiņu pieprasījumi pull_requests=Izmaiņu pieprasījumi
issues=Problēmas issues=Problēmas
cancel=Atcelt cancel=Atcelt
[search]
search=Meklēt...
repository=Repozitorijs
user=Lietotājs
issue=Kļūda
code=Kods
[install] [install]
install=Instalācija install=Instalācija
title=Instalācijas soļi pirmo reizi palaižot title=Instalācijas soļi pirmo reizi palaižot
@ -65,7 +58,7 @@ db_name=Datu bāzes nosaukums
db_helper=Nepieciešams izmantot MySQL INNODB dzini ar rakstzīmju kopu utf8_general_ci. db_helper=Nepieciešams izmantot MySQL INNODB dzini ar rakstzīmju kopu utf8_general_ci.
ssl_mode=SSL režīms ssl_mode=SSL režīms
path=Ceļš path=Ceļš
sqlite_helper=SQLite3 vai TiDB datu bāzes faila atrašanās vieta. sqlite_helper=SQLite3 vai TiDB datu bāzēs faila ceļš.<br>Izmantojiet absolūto ceļu, startējot kā servisu.
err_empty_db_path=Nepieciešams norādīt SQLite3 vai TiDB datu bāzes atrašanās vietu. err_empty_db_path=Nepieciešams norādīt SQLite3 vai TiDB datu bāzes atrašanās vietu.
err_invalid_tidb_name=TiDB datu bāzes nosaukums nevar saturēt simbolus "." un "-". err_invalid_tidb_name=TiDB datu bāzes nosaukums nevar saturēt simbolus "." un "-".
no_admin_and_disable_registration=Reģistrāciju nevar atslēgt, kamēr nav izveidots administratora konts. no_admin_and_disable_registration=Reģistrāciju nevar atslēgt, kamēr nav izveidots administratora konts.
@ -86,6 +79,8 @@ http_port=HTTP ports
http_port_helper=Porta numurs pēc kura lietojumprogrammai būs iespējams pieslēgties. http_port_helper=Porta numurs pēc kura lietojumprogrammai būs iespējams pieslēgties.
app_url=Lietotnes URL app_url=Lietotnes URL
app_url_helper=Tas ietekmē HTTP/HTTPS klonēšanas URL un e-pasta saturā izsūtītās saites. app_url_helper=Tas ietekmē HTTP/HTTPS klonēšanas URL un e-pasta saturā izsūtītās saites.
log_root_path=Žurnalizēšanas direktorija
log_root_path_helper=Direktorija, kurā tiks glabāti žurnāla faili.
optional_title=Neobligātie iestatījumi optional_title=Neobligātie iestatījumi
email_title=E-pasta pakalpojuma iestatījumi email_title=E-pasta pakalpojuma iestatījumi
@ -122,6 +117,7 @@ run_user_not_match=Izpildes lietotājs nav pašreizējais lietotājs: %s -> %s
save_config_failed=Neizdevās saglabāt konfigurāciju: %v save_config_failed=Neizdevās saglabāt konfigurāciju: %v
invalid_admin_setting=Nekorekts admin konta iestatījums: %v invalid_admin_setting=Nekorekts admin konta iestatījums: %v
install_success=Laipni lūdzam! Mēs priecājamies, ka Jūs izvēlaties Gogs, patīkamu lietošanu! install_success=Laipni lūdzam! Mēs priecājamies, ka Jūs izvēlaties Gogs, patīkamu lietošanu!
invalid_log_root_path=Norādītā žurnalizēšanas direktorija ir kļūdaina: %v
[home] [home]
uname_holder=Lietotājvārds vai e-pasts uname_holder=Lietotājvārds vai e-pasts
@ -137,6 +133,8 @@ issues.in_your_repos=Jūsu repozitorijos
[explore] [explore]
repos=Repozitoriji repos=Repozitoriji
users=Lietotāji
search=Meklēt
[auth] [auth]
create_new_account=Izveidot jaunu kontu create_new_account=Izveidot jaunu kontu
@ -203,7 +201,6 @@ repo_name_been_taken=Repozitorija vārds ir jau aizņemts.
org_name_been_taken=Organizācijas nosaukums ir jau aizņemts. org_name_been_taken=Organizācijas nosaukums ir jau aizņemts.
team_name_been_taken=Komandas nosaukums ir jau aizņemts. team_name_been_taken=Komandas nosaukums ir jau aizņemts.
email_been_used=E-pasta adrese jau tiek izmantota. email_been_used=E-pasta adrese jau tiek izmantota.
illegal_team_name=Grupas nosaukums satur neatļautas rakstzīmes.
username_password_incorrect=Lietotājvārds vai parole nav pareiza. username_password_incorrect=Lietotājvārds vai parole nav pareiza.
enterred_invalid_repo_name=Lūdzu, pārliecinieties, vai ievadītā repozitorija nosaukums ir pareizs. enterred_invalid_repo_name=Lūdzu, pārliecinieties, vai ievadītā repozitorija nosaukums ir pareizs.
enterred_invalid_owner_name=Lūdzu, pārliecinieties, vai ievadītā īpašnieka vārds ir pareizs. enterred_invalid_owner_name=Lūdzu, pārliecinieties, vai ievadītā īpašnieka vārds ir pareizs.
@ -219,8 +216,6 @@ still_own_repo=Jūsu esat vismaz viena repozitorija īpašnieks, tos sākumā ir
still_has_org=Jūsu esat vismaz vienas organizācijas biedrs, sākumā nepieciešams pamest vai izdzēst šo organizāciju. still_has_org=Jūsu esat vismaz vienas organizācijas biedrs, sākumā nepieciešams pamest vai izdzēst šo organizāciju.
org_still_own_repo=Šī organizācija ir vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai nomainīt to īpašnieku. org_still_own_repo=Šī organizācija ir vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai nomainīt to īpašnieku.
still_own_user=Šo autentifikāciju joprojām izmanto vismaz viens lietotājs, nepieciešams šiem lietotājiem nomainīt autentifikācijas veidu vai tos izdzēst.
target_branch_not_exist=Mērķa atzars neeksistē target_branch_not_exist=Mērķa atzars neeksistē
[user] [user]
@ -262,11 +257,10 @@ continue=Turpināt
cancel=Atcelt cancel=Atcelt
enable_custom_avatar=Iespējot maināmu profila attēlu enable_custom_avatar=Iespējot maināmu profila attēlu
enable_custom_avatar_helper=Iespējojiet šo, lai atslēgtu profilu attēlu ņemšanu no gravatar.com
choose_new_avatar=Izvēlēties jaunu profila attēlu choose_new_avatar=Izvēlēties jaunu profila attēlu
update_avatar=Saglabāt profila bildi update_avatar=Saglabāt profila bildi
delete_current_avatar=Dzēst pašreizējo profila bildi
uploaded_avatar_not_a_image=Augšupielādētais fails nav attēls. uploaded_avatar_not_a_image=Augšupielādētais fails nav attēls.
no_custom_avatar_available=Nav iespējams mainīt profila bildi.
update_avatar_success=Jūsu profila bilde tika veiksmīgi saglabāta. update_avatar_success=Jūsu profila bilde tika veiksmīgi saglabāta.
change_password=Mainīt paroli change_password=Mainīt paroli
@ -477,7 +471,7 @@ issues.closed_at=`aizvērts <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`atvērts atkārtoti <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`atvērts atkārtoti <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`pieminēja šo problēmu revīzijā <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`pieminēja šo problēmu revīzijā <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.poster=Autors issues.poster=Autors
issues.admin=Administrators issues.collaborator=Līdzstrādnieks
issues.owner=Īpašnieks issues.owner=Īpašnieks
issues.sign_up_for_free=Pievienojieties issues.sign_up_for_free=Pievienojieties
issues.sign_in_require_desc=, lai piedalītos diskusijā. Jau ir konts? <a href="%s">Pierakstieties, lai komentētu</a> issues.sign_in_require_desc=, lai piedalītos diskusijā. Jau ir konts? <a href="%s">Pierakstieties, lai komentētu</a>
@ -494,6 +488,7 @@ issues.label_modify=Etiķetes labošana
issues.label_deletion=Etiķetes dzēšana issues.label_deletion=Etiķetes dzēšana
issues.label_deletion_desc=Dzēšot šo etiķeti, tā tiks noņemta no visām saistītajām problēmām. Vai vēlaties turpināt? issues.label_deletion_desc=Dzēšot šo etiķeti, tā tiks noņemta no visām saistītajām problēmām. Vai vēlaties turpināt?
issues.label_deletion_success=Etiķete tika veiksmīgi izdzēsta! issues.label_deletion_success=Etiķete tika veiksmīgi izdzēsta!
issues.num_participants=%d dalībnieki
pulls.new=Jauns izmaiņu pieprasījums pulls.new=Jauns izmaiņu pieprasījums
pulls.compare_changes=Salīdzināt izmaiņas pulls.compare_changes=Salīdzināt izmaiņas
@ -557,6 +552,8 @@ wiki.save_page=Saglabāt lapu
wiki.last_commit_info=%s laboja lapu %s wiki.last_commit_info=%s laboja lapu %s
wiki.edit_page_button=Labot wiki.edit_page_button=Labot
wiki.new_page_button=Jauna lapa wiki.new_page_button=Jauna lapa
wiki.delete_page_button=Dzēst lapu
wiki.delete_page_notice_1=Tiks izdzēsta lapa <code>"%s"</code>. Pārliecinieties, ka patiešām to vēlaties.
wiki.page_already_exists=Vikivietnes lapa ar šādu nosaukumu jau eksistē. wiki.page_already_exists=Vikivietnes lapa ar šādu nosaukumu jau eksistē.
wiki.pages=Lapas wiki.pages=Lapas
wiki.last_updated=Pēdējo reizi labota %s wiki.last_updated=Pēdējo reizi labota %s
@ -581,14 +578,23 @@ settings.tracker_url_format=Ārējā problēmu sekotāja adreses formāts
settings.tracker_url_format_desc=Jūs varat izmantot <code>{user}{repo}{index}</code> lietotājvārdam, repozitorija nosaukumam un problēmas identifikātoram. settings.tracker_url_format_desc=Jūs varat izmantot <code>{user}{repo}{index}</code> lietotājvārdam, repozitorija nosaukumam un problēmas identifikātoram.
settings.pulls_desc=Iespējot izmaiņu pieprasījumus lai saņemtu publiskus ieguldījumus settings.pulls_desc=Iespējot izmaiņu pieprasījumus lai saņemtu publiskus ieguldījumus
settings.danger_zone=Bīstamā zona settings.danger_zone=Bīstamā zona
settings.new_owner_has_same_repo=Jaunajam īpašniekam jau ir repozitorijs ar šādu nosaukumu.
settings.convert=Konvertēt uz parastu repozitoriju
settings.convert_desc=Šo spoguli ir iespējams konvertēt par parastu repozitoriju. Šī ir neatgriezeniska darbība.
settings.convert_notices_1=- Šī darbība konvertēs šo repozitoriju par parastu repozitoriju un to nebūs iespējams atcelt.
settings.convert_confirm=Apstiprināt konvertēšanu
settings.convert_succeed=Repozitorijs tika veiksmīgi konvertēts uz parastu repozitoriju.
settings.transfer=Mainīt īpašnieku settings.transfer=Mainīt īpašnieku
settings.transfer_desc=Mainīt šī repozitorija īpašnieku uz citu lietotāju vai organizāciju, kurai Jums ir administratora tiesībs. settings.transfer_desc=Mainīt šī repozitorija īpašnieku uz citu lietotāju vai organizāciju, kurai Jums ir administratora tiesībs.
settings.new_owner_has_same_repo=Jaunajam īpašniekam jau ir repozitorijs ar šādu nosaukumu.
settings.delete=Dzēst šo repozitoriju
settings.delete_desc=Dzēšot repozitoriju, tā datus vairs nebūs iespējams atgūt. Pirms dzēšanas pārliecinieites vai patiešām vēlaties to darīt.
settings.transfer_notices_1=- Jūs pazaudēsiet piekļuvi, ja jaunais īpašnieks ir lietotājs. settings.transfer_notices_1=- Jūs pazaudēsiet piekļuvi, ja jaunais īpašnieks ir lietotājs.
settings.transfer_notices_2=- Jūs saglabāsiet piekļuvi, ja jaunais īpašnieks ir organizācija un Jūs esat viens no tās īpašniekiem. settings.transfer_notices_2=- Jūs saglabāsiet piekļuvi, ja jaunais īpašnieks ir organizācija un Jūs esat viens no tās īpašniekiem.
settings.transfer_form_title=Lūdzu, ievadiet sekojošu informāciju, lai apstiprinātu šo darbību: settings.transfer_form_title=Lūdzu, ievadiet sekojošu informāciju, lai apstiprinātu šo darbību:
settings.wiki_delete=Dzēst Vikivietnes datus
settings.wiki_delete_desc=Vikivietnes datu dzēšana ir neatgriezeniska. Pārliecinieties vai patiešām to vēlaties.
settings.wiki_delete_notices_1=- Šī darbība dzēsīs un atspējos %s Vikivietni
settings.wiki_deletion_success=Repozitorija Vikivietnes dati tika veiksmīgi izdzēsti.
settings.delete=Dzēst šo repozitoriju
settings.delete_desc=Dzēšot repozitoriju, tā datus vairs nebūs iespējams atgūt. Pirms dzēšanas pārliecinieites vai patiešām vēlaties to darīt.
settings.delete_notices_1=- Šī darbība ir <strong>NEATGRIEZENISKA</strong>. settings.delete_notices_1=- Šī darbība ir <strong>NEATGRIEZENISKA</strong>.
settings.delete_notices_2=- Šī darbība neatgriezeniski izdzēsīs visus šī repozitorija datus, tai skaitā Git datus, problēmu ziņojumus, komentārus un definētās piekļuves tiesības. settings.delete_notices_2=- Šī darbība neatgriezeniski izdzēsīs visus šī repozitorija datus, tai skaitā Git datus, problēmu ziņojumus, komentārus un definētās piekļuves tiesības.
settings.delete_notices_fork_1=- Ja repozitorijs ir publisks, visi atdalītie repozitoriji kļūs neatkarīgi. settings.delete_notices_fork_1=- Ja repozitorijs ir publisks, visi atdalītie repozitoriji kļūs neatkarīgi.
@ -602,8 +608,12 @@ settings.transfer_succeed=Repozitorija īpašnieks ir veiksmīgi nomainīts.
settings.confirm_delete=Apstiprināt dzēšanu settings.confirm_delete=Apstiprināt dzēšanu
settings.add_collaborator=Pievienot jaunu līdzstrādnieku settings.add_collaborator=Pievienot jaunu līdzstrādnieku
settings.add_collaborator_success=Jauns līdzstrādnieks ir pievienots. settings.add_collaborator_success=Jauns līdzstrādnieks ir pievienots.
settings.delete_collaborator=Dzēst
settings.collaborator_deletion=Līdzstrādnieka dzēšana
settings.collaborator_deletion_desc=Šim lietotājam pēc dzēšanas vairs nebūs sadarbības pieejas šai krātuvei. Vai vēlaties turpināt?
settings.remove_collaborator_success=Līdzstrādnieks tika noņemts. settings.remove_collaborator_success=Līdzstrādnieks tika noņemts.
settings.search_user_placeholder=Meklēt lietotāju... settings.search_user_placeholder=Meklēt lietotāju...
settings.org_not_allowed_to_be_collaborator=Organizāciju nav atļauts pievienot kā līdzstrādnieku.
settings.user_is_org_member=Lietotājs ir organizācijas biedrs, kas nevar tikt pievienots kā līdzstrādnieks. settings.user_is_org_member=Lietotājs ir organizācijas biedrs, kas nevar tikt pievienots kā līdzstrādnieks.
settings.add_webhook=Pievienot tīmekļa āķi settings.add_webhook=Pievienot tīmekļa āķi
settings.hooks_desc=Tīmekļa āķi ļauj paziņot ārējiem servisiem par noteiktiem notikomiem, kas notiek Git servisā. Kad iestāsies kāds notikums, katram ārējā servisa URL tiks nosūtīts POST pieprasījums. Lai uzzinātu sīkāk skatieties <a target="_blank" href="%s">Tīmekļa āķu rokasgrāmatā</a>. settings.hooks_desc=Tīmekļa āķi ļauj paziņot ārējiem servisiem par noteiktiem notikomiem, kas notiek Git servisā. Kad iestāsies kāds notikums, katram ārējā servisa URL tiks nosūtīts POST pieprasījums. Lai uzzinātu sīkāk skatieties <a target="_blank" href="%s">Tīmekļa āķu rokasgrāmatā</a>.
@ -818,6 +828,8 @@ dashboard.resync_all_sshkeys=Pārrakstīt '.ssh/authorized_keys' failu (brīdin
dashboard.resync_all_sshkeys_success=Visas publiskās atslēgas tika veiksmīgi pārrakstītas. dashboard.resync_all_sshkeys_success=Visas publiskās atslēgas tika veiksmīgi pārrakstītas.
dashboard.resync_all_update_hooks=Pārrakstīt visu repozitoriju izmaiņu āķus (nepieciešams, ja tiek mainīta konfigurācijas faila atrašanās vieta) dashboard.resync_all_update_hooks=Pārrakstīt visu repozitoriju izmaiņu āķus (nepieciešams, ja tiek mainīta konfigurācijas faila atrašanās vieta)
dashboard.resync_all_update_hooks_success=Visu repozitoriju izmaiņu āķi tika veiksmīgi pārrakstīti. dashboard.resync_all_update_hooks_success=Visu repozitoriju izmaiņu āķi tika veiksmīgi pārrakstīti.
dashboard.reinit_missing_repos=Atkārtoti inicializēt visus repozitorija ierakstus, kam trūkst Git failu
dashboard.reinit_missing_repos_success=Visi repozitorija ieraksti, kam trūkst Git faili, tika atkārtoti inicializēti.
dashboard.server_uptime=Servera darbības laiks dashboard.server_uptime=Servera darbības laiks
dashboard.current_goroutine=Izmantotās Gorutīnas dashboard.current_goroutine=Izmantotās Gorutīnas
@ -911,6 +923,7 @@ auths.attribute_username_placeholder=Atstājiet tukšu, lai izmantotu lietotājv
auths.attribute_name=Vārda atribūts auths.attribute_name=Vārda atribūts
auths.attribute_surname=Uzvārda atribūts auths.attribute_surname=Uzvārda atribūts
auths.attribute_mail=E-pasta atribūts auths.attribute_mail=E-pasta atribūts
auths.attributes_in_bind=Nolasīt atribūtus no saistīšanas DN konteksta
auths.filter=Lietotāju filts auths.filter=Lietotāju filts
auths.admin_filter=Administratoru filtrs auths.admin_filter=Administratoru filtrs
auths.ms_ad_sa=MS Ad SA auths.ms_ad_sa=MS Ad SA
@ -932,6 +945,7 @@ auths.update=Mainīt autentifikācijas iestatījumus
auths.delete=Dzēst šo autentifikāciju auths.delete=Dzēst šo autentifikāciju
auths.delete_auth_title=Autentifikācijas dzēšana auths.delete_auth_title=Autentifikācijas dzēšana
auths.delete_auth_desc=Šī autentifikācija tiks dzēsta, vai vēlaties turpināt? auths.delete_auth_desc=Šī autentifikācija tiks dzēsta, vai vēlaties turpināt?
auths.still_in_used=Daži lietotāji joprojām izmanto šo autentifikācijas veidu. Nepieciešams veikt šo lietotāju konvertāciju vai dzēšanu.
auths.deletion_success=Autentifikācija tika veiksmīgi izdzēsta! auths.deletion_success=Autentifikācija tika veiksmīgi izdzēsta!
config.server_config=Servera konfigurācija config.server_config=Servera konfigurācija
@ -948,6 +962,19 @@ config.static_file_root_path=Statisko failu atrašanās vieta
config.log_file_root_path=Žurnalizēšanas failu glabāšanas vieta config.log_file_root_path=Žurnalizēšanas failu glabāšanas vieta
config.script_type=Skripta veids config.script_type=Skripta veids
config.reverse_auth_user=Reversā lietotāja autentifikācija config.reverse_auth_user=Reversā lietotāja autentifikācija
config.ssh_config=SSH konfigurācija
config.ssh_enabled=Iespējots
config.ssh_start_builtin_server=Startēt iebūvēto serveri
config.ssh_domain=Domēns
config.ssh_port=Ports
config.ssh_listen_port=Klausīšanās ports
config.ssh_root_path=Saknes ceļš
config.ssh_key_test_path=Atslēgu pārbaudes ceļš
config.ssh_keygen_path=Keygen ('ssh-keygen') ceļš
config.ssh_minimum_key_size_check=Minimālā atslēgas lieluma pārbaude
config.ssh_minimum_key_sizes=Minimālais atslēgas lielums
config.db_config=Datu bāzes konfigurācija config.db_config=Datu bāzes konfigurācija
config.db_type=Veids config.db_type=Veids
config.db_host=Resursdators config.db_host=Resursdators
@ -962,7 +989,6 @@ config.register_email_confirm=Pieprasīt e-pasta apstiprināšanu
config.disable_register=Atspējot jaunu lietotāju reģistrāciju config.disable_register=Atspējot jaunu lietotāju reģistrāciju
config.show_registration_button=Rādīt reģistrēšanās pogu config.show_registration_button=Rādīt reģistrēšanās pogu
config.require_sign_in_view=Nepieciešama autorizācija config.require_sign_in_view=Nepieciešama autorizācija
config.enable_cache_avatar=Glabāt profila attēlus kešatmiņā
config.mail_notify=Pasta paziņojumi config.mail_notify=Pasta paziņojumi
config.disable_key_size_check=Atspējot atslēgas minimālā garuma pārbaudi config.disable_key_size_check=Atspējot atslēgas minimālā garuma pārbaudi
config.enable_captcha=Iespējot drošības kodu config.enable_captcha=Iespējot drošības kodu
@ -978,6 +1004,9 @@ config.mailer_disable_helo=Atspējot HELO
config.mailer_name=Nosaukums config.mailer_name=Nosaukums
config.mailer_host=Resursdators config.mailer_host=Resursdators
config.mailer_user=Lietotājs config.mailer_user=Lietotājs
config.send_test_mail=Nosūtīt pārbaudes e-pastu
config.test_mail_failed=Neizdevās nosūtīt pārbaudes e-pasta vēstuli uz '%s': %v
config.test_mail_sent=Pārbaudes e-pasta vēstule tika nosūtīta uz '%s'.
config.oauth_config=OAuth konfigurācija config.oauth_config=OAuth konfigurācija
config.oauth_enabled=Iespējota config.oauth_enabled=Iespējota
config.cache_config=Kešatmiņas konfigurācija config.cache_config=Kešatmiņas konfigurācija
@ -1029,7 +1058,11 @@ create_repo=izveidoja repozitoriju <a href="%s">%s</a>
rename_repo=pārsauca repozitoriju no <code>%[1]s</code> uz <a href="%[2]s">%[3]s</a> rename_repo=pārsauca repozitoriju no <code>%[1]s</code> uz <a href="%[2]s">%[3]s</a>
commit_repo=veica izmaiņu nosūtīšanu atzaram <a href="%[1]s/src/%[2]s">%[3]s</a> repozitorijā <a href="%[1]s">%[4]s</a> commit_repo=veica izmaiņu nosūtīšanu atzaram <a href="%[1]s/src/%[2]s">%[3]s</a> repozitorijā <a href="%[1]s">%[4]s</a>
create_issue=`reģistrēja problēmu <a href="%s/issues/%s">%s#%[2]s</a>` create_issue=`reģistrēja problēmu <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`slēdza problēmu <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`atkārtoti atvēra problēmu <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`izveidoja izmaiņu pieprasījumu <a href="%s/pulls/%s">%s#%[2]s</a>` create_pull_request=`izveidoja izmaiņu pieprasījumu <a href="%s/pulls/%s">%s#%[2]s</a>`
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_issue=`pievienoja komentāru problēmai <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request=`sapludinā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> transfer_repo=mainīja repozitorija <code>%s</code> īpašnieku uz <a href="%s">%s</a>

235
conf/locale/locale_nl-NL.ini

@ -5,9 +5,9 @@ dashboard=Dashboard
explore=Verkennen explore=Verkennen
help=Help help=Help
sign_in=Inloggen sign_in=Inloggen
sign_out=Afmelden sign_out=Uitloggen
sign_up=Aanmelden sign_up=Aanmelden
register=Registreer register=Registreren
website=Website website=Website
version=Versie version=Versie
page=Pagina page=Pagina
@ -19,17 +19,17 @@ signed_in_as=Aangemeld als
username=Gebruikersnaam username=Gebruikersnaam
email=E-mail email=E-mail
password=Wachttwoord password=Wachtwoord
re_type=Verificatie re_type=Verificatie
captcha=CAPTCHA captcha=CAPTCHA
repository=Repositorie repository=Repository
organization=Organisatie organization=Organisatie
mirror=Spiegel mirror=Spiegel
new_repo=Nieuwe repositorie new_repo=Nieuwe repository
new_migrate=Nieuwe migratie new_migrate=Nieuwe migratie
new_mirror=Nieuwe Kopie new_mirror=Nieuwe Kopie
new_fork=Nieuwe vork Repository new_fork=Nieuwe Fork
new_org=Nieuwe organisatie new_org=Nieuwe organisatie
manage_org=Beheer organisaties manage_org=Beheer organisaties
admin_panel=Adminpaneel admin_panel=Adminpaneel
@ -38,25 +38,18 @@ settings=Instellingen
your_profile=Uw profiel your_profile=Uw profiel
your_settings=Uw instellingen your_settings=Uw instellingen
news_feed=Nieuwsfeed activities=Activiteiten
pull_requests=Pull-aanvragen pull_requests=Pull requests
issues=Kwesties issues=Kwesties
cancel=Annuleer cancel=Annuleren
[search]
search=Zoeken...
repository=Opslagplaats
user=Gebruiker
issue=Probleem
code=Code
[install] [install]
install=Installatie install=Installatie
title=Installatiestappen voor de eerste keer opstarten title=Installatiestappen voor de eerste keer opstarten
docker_helper=Als u gebruik maakt Gogs binnen Docker, lees dan de <a target="_blank" href="%s">richtlijnen</a> voordat u iets veranderen op deze pagina! docker_helper=Als u gebruik maakt Gogs binnen Docker, lees dan de <a target="_blank" href="%s">richtlijnen</a> voordat u iets veranderen op deze pagina!
requite_db_desc=Gogs vereist MySQL, PostgreSQL, SQite3 of TiDB. requite_db_desc=Gogs vereist MySQL, PostgreSQL, SQite3 of TiDB.
db_title=Database instellingen db_title=Database-instellingen
db_type=Database-type db_type=Database-type
host=Host host=Host
user=Gebruikersnaam user=Gebruikersnaam
@ -65,7 +58,7 @@ db_name=Database naam
db_helper=Gebruik InnoDB engine met utf8_general_ci karakterset voor MySQL. db_helper=Gebruik InnoDB engine met utf8_general_ci karakterset voor MySQL.
ssl_mode=SSL-modus ssl_mode=SSL-modus
path=Pad path=Pad
sqlite_helper=Het bestandspad van de SQLite3 of TiDB databank. sqlite_helper=Het pad van de SQLite3- of TiDB-database.<br>Als u Gogs start als een service, geef dan een absoluut pad op.
err_empty_db_path=SQLite3 of TiDB databankpad mag niet leeg. err_empty_db_path=SQLite3 of TiDB databankpad mag niet leeg.
err_invalid_tidb_name=TiDB databank naam niet tekens kunnen "." en "-". err_invalid_tidb_name=TiDB databank naam niet tekens kunnen "." en "-".
no_admin_and_disable_registration=Je kunt niet de registratie uit te schakelen zonder een beheerders account. no_admin_and_disable_registration=Je kunt niet de registratie uit te schakelen zonder een beheerders account.
@ -86,6 +79,8 @@ http_port=HTTP-poort
http_port_helper=Poortnummer waar het programma naar luistert. http_port_helper=Poortnummer waar het programma naar luistert.
app_url=Applicatie URL app_url=Applicatie URL
app_url_helper=Dit heeft invloed op de HTTP/HTTPS kloon urls en de urls die in de email worden gebruikt app_url_helper=Dit heeft invloed op de HTTP/HTTPS kloon urls en de urls die in de email worden gebruikt
log_root_path=Log-pad
log_root_path_helper=Directory waar logbestanden opgeslagen worden.
optional_title=Optionele instellingen optional_title=Optionele instellingen
email_title=E-mail service instellingen email_title=E-mail service instellingen
@ -117,11 +112,12 @@ install_gogs=Installeer Gogs
test_git_failed=Git test niet gelukt: 'git' commando %v test_git_failed=Git test niet gelukt: 'git' commando %v
sqlite3_not_available=Uw versie biedt geen ondersteuning voor SQLite3, download de officiële binaire versie van %s, niet de gobuild versie. sqlite3_not_available=Uw versie biedt geen ondersteuning voor SQLite3, download de officiële binaire versie van %s, niet de gobuild versie.
invalid_db_setting=Uw database instellingen zijn niet correct: %v invalid_db_setting=Uw database instellingen zijn niet correct: %v
invalid_repo_path=Repositorie basis pad is niet correct: %v invalid_repo_path=Repositorie basis map is niet correct: %v
run_user_not_match=De uitvoerende gebruiker is niet de huidig gebruiker: %s -> %s run_user_not_match=De uitvoerende gebruiker is niet de huidig gebruiker: %s -> %s
save_config_failed=Kan de configuratie niet opslaan: %v save_config_failed=Kan de configuratie niet opslaan: %v
invalid_admin_setting=Uw admin-instellingen zijn niet geldig: %v invalid_admin_setting=Uw admin-instellingen zijn niet geldig: %v
install_success=Welkom! Wij zijn veheugd dat u voor Gogs heeft gekozen, veel plezier en tot ziens install_success=Welkom! Wij zijn veheugd dat u voor Gogs heeft gekozen, veel plezier en tot ziens
invalid_log_root_path=Ongeldig log-pad: %v
[home] [home]
uname_holder=Gebruikersnaam of e-mail uname_holder=Gebruikersnaam of e-mail
@ -137,6 +133,8 @@ issues.in_your_repos=In uw repositories
[explore] [explore]
repos=Repositories repos=Repositories
users=Gebruikers
search=Zoeken
[auth] [auth]
create_new_account=Maak nieuw account aan create_new_account=Maak nieuw account aan
@ -174,7 +172,7 @@ modify=Aanpassen
[form] [form]
UserName=Gebruikersnaam UserName=Gebruikersnaam
RepoName=Repositorie naam RepoName=Naam van repository
Email=e-mailadres Email=e-mailadres
Password=Wachtwoord Password=Wachtwoord
Retype=Verifieer wachtwoord Retype=Verifieer wachtwoord
@ -199,11 +197,10 @@ captcha_incorrect=Captcha komt niet overeen.
password_not_match=Wachtwoord en verificatie wachtwoord komen niet overeen. password_not_match=Wachtwoord en verificatie wachtwoord komen niet overeen.
username_been_taken=Gebruikersnaam is al in gebruik. username_been_taken=Gebruikersnaam is al in gebruik.
repo_name_been_taken=Repositorie naam is al in gebruik. repo_name_been_taken=Deze naam is al in gebruik.
org_name_been_taken=Organisatie naam is al in gebruik. org_name_been_taken=Organisatie naam is al in gebruik.
team_name_been_taken=Team naam is al in gebruik. team_name_been_taken=Team naam is al in gebruik.
email_been_used=e-mailadres is al in gebruik. email_been_used=e-mailadres is al in gebruik.
illegal_team_name=Team naam bevat illegale karakters.
username_password_incorrect=Gebruikersnaam of wachtwoord is niet correct. username_password_incorrect=Gebruikersnaam of wachtwoord is niet correct.
enterred_invalid_repo_name=U heeft een onjuiste repositorie naam ingevoerd. enterred_invalid_repo_name=U heeft een onjuiste repositorie naam ingevoerd.
enterred_invalid_owner_name=U heeft een onjuiste eigenaar ingevoerd. enterred_invalid_owner_name=U heeft een onjuiste eigenaar ingevoerd.
@ -219,8 +216,6 @@ still_own_repo=Uw account heeft nog een eigendom op een repositorie. U moet deze
still_has_org=Uw account nog steeds lidmaatschap van organisatie, u hebt naar links of hen eerst verwijderen. still_has_org=Uw account nog steeds lidmaatschap van organisatie, u hebt naar links of hen eerst verwijderen.
org_still_own_repo=De organisatie heeft nog eigendomen op repositories. U moet deze eerst verwijderen of overdragen. org_still_own_repo=De organisatie heeft nog eigendomen op repositories. U moet deze eerst verwijderen of overdragen.
still_own_user=Deze authenticatie methode wordt nog gebruikt door sommige gebruikers. U moet hen eerst verplaatsen of verwijderen.
target_branch_not_exist=Doel branch bestaat niet target_branch_not_exist=Doel branch bestaat niet
[user] [user]
@ -262,11 +257,10 @@ continue=Doorgaan
cancel=Annuleren cancel=Annuleren
enable_custom_avatar=Aangepaste avatar inschakelen enable_custom_avatar=Aangepaste avatar inschakelen
enable_custom_avatar_helper=Avatar niet ophalen van Gravatar
choose_new_avatar=Kies een nieuwe avatar choose_new_avatar=Kies een nieuwe avatar
update_avatar=Avatar instelling bijwerken update_avatar=Avatar instelling bijwerken
delete_current_avatar=Verwijder huidige avatar
uploaded_avatar_not_a_image=Geüpload bestand is geen afbeelding. uploaded_avatar_not_a_image=Geüpload bestand is geen afbeelding.
no_custom_avatar_available=Geen aangepaste avatar beschikbaar, kan niet worden ingeschakeld.
update_avatar_success=Instellingen voor avatar succesvol bijgewerkt. update_avatar_success=Instellingen voor avatar succesvol bijgewerkt.
change_password=Verander wachtwoord change_password=Verander wachtwoord
@ -336,16 +330,16 @@ delete_account_desc=Dit account zal permanent worden verwijderd. Wilt u doorgaan
[repo] [repo]
owner=Eigenaar owner=Eigenaar
repo_name=Repositorie naam repo_name=Naam van repository
repo_name_helper=Een goede repositorie naam is kort, memorabel en <strong>uniek</strong>. repo_name_helper=Een goede repository-naam is kort, makkelijk te onthouden en <strong>uniek</strong>.
visibility=Zichtbaarheid visibility=Zichtbaarheid
visiblity_helper=Deze repositorie is <span class="ui red text">privaat</span> visiblity_helper=Deze repositorie is <span class="ui red text">privaat</span>
visiblity_helper_forced=Sitebeheerder heeft alle nieuwe repositories gedwongen <span class="ui red text">privé</span> te zijn visiblity_helper_forced=Sitebeheerder heeft alle nieuwe repositories gedwongen <span class="ui red text">privé</span> te zijn
visiblity_fork_helper=(Verandering van deze waarde zal van invloed zijn op alle forks) visiblity_fork_helper=(Verandering van deze waarde zal van invloed zijn op alle forks)
clone_helper=De behoeftehulp van klonen? Bezoek <a target="_blank" href="%s"> helpen</a>! clone_helper=De behoeftehulp van klonen? Bezoek <a target="_blank" href="%s"> helpen</a>!
fork_repo=Vork Repository fork_repo=Repository forken
fork_from=Afsplitsing van fork_from=Afsplitsing van
fork_visiblity_helper=Gevorkte repository wijzigen zijn bereik potentiële kopers niet fork_visiblity_helper=U kunt de zichtbaarheid van een geforkte repository niet aanpassen.
repo_desc=Omschrijving repo_desc=Omschrijving
repo_lang=Taal repo_lang=Taal
repo_lang_helper=Selecteer .gitignore bestanden repo_lang_helper=Selecteer .gitignore bestanden
@ -354,12 +348,12 @@ license_helper=Selecteer een licentie bestand
readme=Leesmij-bestand readme=Leesmij-bestand
readme_helper=Selecteer een sjabloon voor het Leesmij-bestand readme_helper=Selecteer een sjabloon voor het Leesmij-bestand
auto_init=Initialiseer deze repositorie met de geselecteerde bestanden en sjabloon auto_init=Initialiseer deze repositorie met de geselecteerde bestanden en sjabloon
create_repo=Nieuwe Repositorie create_repo=Nieuwe repository
default_branch=Standaard branch default_branch=Standaard branch
mirror_interval=Mirror interval(uur) mirror_interval=Mirror interval(uur)
mirror_address=Kopie-adres mirror_address=Kopie-adres
mirror_address_desc=Gelieve noodzakelijke gebruikersgegevens in de adresbalk. mirror_address_desc=Gelieve noodzakelijke gebruikersgegevens in de adresbalk.
watchers=Watchers watchers=Volgers
stargazers=Stargazers stargazers=Stargazers
forks=Forks forks=Forks
@ -432,7 +426,7 @@ issues.new.no_milestone=Geen mijlpaal
issues.new.clear_milestone=Verwijder mijlpaal issues.new.clear_milestone=Verwijder mijlpaal
issues.new.open_milestone=Open mijlpalen issues.new.open_milestone=Open mijlpalen
issues.new.closed_milestone=Gesloten mijlpalen issues.new.closed_milestone=Gesloten mijlpalen
issues.new.assignee=Verantwoordelijke issues.new.assignee=Toegewezen aan
issues.new.clear_assignee=Verwijder verantwoordelijke issues.new.clear_assignee=Verwijder verantwoordelijke
issues.new.no_assignee=Geen verantwoordelijke issues.new.no_assignee=Geen verantwoordelijke
issues.create=Maak probleem issues.create=Maak probleem
@ -477,7 +471,7 @@ issues.closed_at=`gesloten om <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`heropend om <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`heropend om <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at='verwees naar dit probleem vanuit een commit <a id="%[1]s" href="#%[1]s"> %[2]s'</a> issues.commit_ref_at='verwees naar dit probleem vanuit een commit <a id="%[1]s" href="#%[1]s"> %[2]s'</a>
issues.poster=Poster issues.poster=Poster
issues.admin=Admin issues.collaborator=Collaborator
issues.owner=Eigenaar issues.owner=Eigenaar
issues.sign_up_for_free=Gratis aanmelden issues.sign_up_for_free=Gratis aanmelden
issues.sign_in_require_desc=om deel te nemen in deze conversatie. Heeft u al een account? <a href="%s">Meld u aan om te reageren</a> issues.sign_in_require_desc=om deel te nemen in deze conversatie. Heeft u al een account? <a href="%s">Meld u aan om te reageren</a>
@ -494,6 +488,7 @@ issues.label_modify=Wijzig label
issues.label_deletion=Verwijder label issues.label_deletion=Verwijder label
issues.label_deletion_desc=Het verwijderen van dit label zal alle informatie in de gerelateerde problemen verwijderen. Wilt u doorgaan? issues.label_deletion_desc=Het verwijderen van dit label zal alle informatie in de gerelateerde problemen verwijderen. Wilt u doorgaan?
issues.label_deletion_success=Label werd met succes verwijderd! issues.label_deletion_success=Label werd met succes verwijderd!
issues.num_participants=%d deelnemers
pulls.new=Nieuwe Pull aanvraag pulls.new=Nieuwe Pull aanvraag
pulls.compare_changes=Vergelijk veranderingen pulls.compare_changes=Vergelijk veranderingen
@ -510,13 +505,13 @@ pulls.merged_title_desc=%[1] commits samengevoegd van <code>%[2]s</code> naar <c
pulls.tab_conversation=Discussie pulls.tab_conversation=Discussie
pulls.tab_commits=Commits pulls.tab_commits=Commits
pulls.tab_files=Bestanden gewijzigd pulls.tab_files=Bestanden gewijzigd
pulls.reopen_to_merge=Please reopen this pull request to perform merge operation. pulls.reopen_to_merge=Heropen deze pull request aub om een een merge actie uit te voeren.
pulls.merged=Samengevoegd pulls.merged=Samengevoegd
pulls.has_merged=Dit pull-request is samengevoegd! pulls.has_merged=Dit pull-request is samengevoegd!
pulls.data_broken=Data of this pull request has been broken due to deletion of fork information. pulls.data_broken=Omdat informatie over de fork is verwijderd, zijn de gegevens van dit pull-request niet beschikbaar.
pulls.is_checking=Controle van conflicten is nog bezig, ververs deze pagina in enkele ogenblikken. pulls.is_checking=Controle van conflicten is nog bezig, ververs deze pagina in enkele ogenblikken.
pulls.can_auto_merge_desc=Dit pull-request kan automatisch samengevoegd worden. pulls.can_auto_merge_desc=Dit pull-request kan automatisch samengevoegd worden.
pulls.cannot_auto_merge_desc=This pull request can't be merged automatically because there are conflicts. pulls.cannot_auto_merge_desc=Dit pull-request kan niet worden gemerged omdat er conflicten zijn.
pulls.cannot_auto_merge_helper=Please merge manually in order to resolve the conflicts. pulls.cannot_auto_merge_helper=Please merge manually in order to resolve the conflicts.
pulls.merge_pull_request=Samenvoegen van pull verzoek pulls.merge_pull_request=Samenvoegen van pull verzoek
pulls.open_unmerged_pull_exists=`You can't perform reopen operation because there is already an open pull request (#%d) from same repository with same merge information and is waiting for merging.` pulls.open_unmerged_pull_exists=`You can't perform reopen operation because there is already an open pull request (#%d) from same repository with same merge information and is waiting for merging.`
@ -546,55 +541,66 @@ milestones.deletion_desc=Het verwijderen van dit label zal alle informatie in de
milestones.deletion_success=Mijlpaal is met succes verwijderd! milestones.deletion_success=Mijlpaal is met succes verwijderd!
wiki=Wiki wiki=Wiki
wiki.welcome=Welcome to Wiki! wiki.welcome=Welkom op de Wiki!
wiki.welcome_desc=Wiki is the place where you would like to document your project together and make it better. wiki.welcome_desc=In een wiki kunnen gebruikers samen een project documenteren en bediscussiëren.
wiki.create_first_page=Maak de eerste pagina wiki.create_first_page=Maak de eerste pagina
wiki.page=Pagina wiki.page=Pagina
wiki.filter_page=Filter pagina wiki.filter_page=Filter pagina
wiki.new_page=Maak nieuwe pagina wiki.new_page=Maak nieuwe pagina
wiki.default_commit_message=Schrijf een notitie over deze aanpassing (optioneel). wiki.default_commit_message=Schrijf een notitie over deze aanpassing (optioneel).
wiki.save_page=Pagina opslaan wiki.save_page=Pagina opslaan
wiki.last_commit_info=%s edited this page %s wiki.last_commit_info=%s heeft deze pagina aangepast %s
wiki.edit_page_button=Bewerken wiki.edit_page_button=Bewerken
wiki.new_page_button=New Page wiki.new_page_button=Nieuwe pagina
wiki.page_already_exists=Wiki page with same name already exists. wiki.delete_page_button=Verwijder pagina
wiki.pages=Pages wiki.delete_page_notice_1=This will delete the page <code>"%s"</code>. Please be certain.
wiki.last_updated=Last updated %s wiki.page_already_exists=Er bestaat al een wiki-pagina met deze naam.
wiki.pages=Pagina’s
wiki.last_updated=Laatst bijgewerkt: %s
settings=Instellingen settings=Instellingen
settings.options=Opties settings.options=Opties
settings.collaboration=Samenwerking settings.collaboration=Samenwerking
settings.hooks=Webhooks settings.hooks=Webhooks
settings.githooks=Git haken settings.githooks=Git-hooks
settings.basic_settings=Basis instellingen settings.basic_settings=Basis instellingen
settings.site=Officiële site settings.site=Officiële site
settings.update_settings=Instellingen bewerken settings.update_settings=Instellingen bewerken
settings.change_reponame_prompt=This change will affect how links relate to the repository. settings.change_reponame_prompt=This change will affect how links relate to the repository.
settings.advanced_settings=Advanced Settings settings.advanced_settings=Geavanceerde opties
settings.wiki_desc=Enable wiki to allow people write documents settings.wiki_desc=Enable wiki to allow people write documents
settings.use_external_wiki=Use external wiki settings.use_external_wiki=Externe wiki gebruiken
settings.external_wiki_url=External Wiki URL settings.external_wiki_url=Externe wiki-URL
settings.external_wiki_url_desc=Visitors will be redirected to URL when they click on the tab. settings.external_wiki_url_desc=Bezoekers worden doorgestuurd naar de URL als ze op het tabblad klikken.
settings.issues_desc=Enable builtin lightweight issue tracker settings.issues_desc=Ingebouwde compacte issuetracker inschakelen
settings.use_external_issue_tracker=Use external issue tracker settings.use_external_issue_tracker=Externe issuetracker gebruiken
settings.tracker_url_format=External Issue Tracker URL Format settings.tracker_url_format=URL-formaat externe issuetracker
settings.tracker_url_format_desc=You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index. settings.tracker_url_format_desc=You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index.
settings.pulls_desc=Enable pull requests to accept public contributions settings.pulls_desc=Enable pull requests to accept public contributions
settings.danger_zone=Gevaren zone settings.danger_zone=Gevaren zone
settings.new_owner_has_same_repo=De nieuwe eigenaar heeft al een repositorie met deze naam
settings.convert=Converteren naar gewone repository
settings.convert_desc=U kunt deze mirror converteren naar een gewone repository. Dit kan niet ongedaan worden gemaakt.
settings.convert_notices_1=- This operation will convert this repository mirror into a regular repository and cannot be undone.
settings.convert_confirm=Conversie bevestigen
settings.convert_succeed=Deze repository is geconverteerd naar een normale repository.
settings.transfer=Eigendom overdragen settings.transfer=Eigendom overdragen
settings.transfer_desc=Draag deze repo over aan een andere gebruiker of een organisatie waar u beheerders rechten heeft. settings.transfer_desc=Draag deze repo over aan een andere gebruiker of een organisatie waar u beheerders rechten heeft.
settings.new_owner_has_same_repo=De nieuwe eigenaar heeft al een repositorie met deze naam
settings.delete=Verwijder deze repositorie
settings.delete_desc=Als u eenmaal een repositorie verwijderd is er geen weg terug. Gelieve zeker te zijn van uw acties.
settings.transfer_notices_1=- You will lose access if new owner is a individual user. settings.transfer_notices_1=- You will lose access if new owner is a individual user.
settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners. settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners.
settings.transfer_form_title=Please enter following information to confirm your operation: settings.transfer_form_title=Please enter following information to confirm your operation:
settings.wiki_delete=Erase Wiki Data
settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain.
settings.wiki_delete_notices_1=- This will delete and disable the wiki for %s
settings.wiki_deletion_success=Repository wiki data have been erased successfully.
settings.delete=Verwijder deze repositorie
settings.delete_desc=Als u eenmaal een repositorie verwijderd is er geen weg terug. Gelieve zeker te zijn van uw acties.
settings.delete_notices_1=- This operation <strong>CANNOT</strong> be undone. settings.delete_notices_1=- This operation <strong>CANNOT</strong> be undone.
settings.delete_notices_2=- This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators. settings.delete_notices_2=- This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators.
settings.delete_notices_fork_1=- If this repository is public, all forks will be became independent after deletion. settings.delete_notices_fork_1=- If this repository is public, all forks will become independent after deletion.
settings.delete_notices_fork_2=- If this repository is private, all forks will be removed at the same time. settings.delete_notices_fork_2=- If this repository is private, all forks will be removed at the same time.
settings.delete_notices_fork_3=- If you want to keep all forks after deletion, please change visibility of this repository to public first. settings.delete_notices_fork_3=- If you want to keep all forks after deletion, please change visibility of this repository to public first.
settings.deletion_success=Repository has been deleted successfully! settings.deletion_success=Repository is succesvol verwijderd!
settings.update_settings_success=Repositorie instellingen zijn succesvol bijgewerkt. settings.update_settings_success=Repositorie instellingen zijn succesvol bijgewerkt.
settings.transfer_owner=Nieuwe eigenaar settings.transfer_owner=Nieuwe eigenaar
settings.make_transfer=Maak overdracht settings.make_transfer=Maak overdracht
@ -602,15 +608,19 @@ settings.transfer_succeed=Eigendom repositorie succesvol overgedragen
settings.confirm_delete=Bevestig verwijdering settings.confirm_delete=Bevestig verwijdering
settings.add_collaborator=Nieuwe medewerker toevoegen settings.add_collaborator=Nieuwe medewerker toevoegen
settings.add_collaborator_success=medewerker is toegevoegd. settings.add_collaborator_success=medewerker is toegevoegd.
settings.delete_collaborator=Verwijderen
settings.collaborator_deletion=Collaborator Deletion
settings.collaborator_deletion_desc=This user will no longer have collaboration access to this repository after deletion. Do you want to continue?
settings.remove_collaborator_success=medewerker is verwijderd. settings.remove_collaborator_success=medewerker is verwijderd.
settings.search_user_placeholder=Search user... settings.search_user_placeholder=Zoek gebruiker...
settings.org_not_allowed_to_be_collaborator=Organization is not allowed to be added as a collaborator.
settings.user_is_org_member=Gebruiker is lid van de organisatie die als een medewerker kan niet worden toegevoegd. settings.user_is_org_member=Gebruiker is lid van de organisatie die als een medewerker kan niet worden toegevoegd.
settings.add_webhook=Webhook toevoegen settings.add_webhook=Webhook toevoegen
settings.hooks_desc=Webhooks dat de externe diensten om kennisgevingen te ontvangen wanneer bepaalde gebeurtenissen op Gogs plaatsvinden. Wanneer de opgegeven gebeurtenissen plaatsvinden, sturen we een POST-aanvraag naar elk van de URL's die u opgeeft. Meer informatie vindt u in onze <a target="_blank" href="%s"> Webhooks gids</a>. settings.hooks_desc=Webhooks dat de externe diensten om kennisgevingen te ontvangen wanneer bepaalde gebeurtenissen op Gogs plaatsvinden. Wanneer de opgegeven gebeurtenissen plaatsvinden, sturen we een POST-aanvraag naar elk van de URL's die u opgeeft. Meer informatie vindt u in onze <a target="_blank" href="%s"> Webhooks gids</a>.
settings.webhook_deletion=Webhook verwijderen settings.webhook_deletion=Webhook verwijderen
settings.webhook_deletion_desc=Delete this webhook will remove its information and all delivery history. Do you want to continue? settings.webhook_deletion_desc=Delete this webhook will remove its information and all delivery history. Do you want to continue?
settings.webhook_deletion_success=Webhook has been deleted successfully! settings.webhook_deletion_success=Webhook is succesvol verwijderd!
settings.webhook.test_delivery=Test Delivery settings.webhook.test_delivery=Test-bezorging
settings.webhook.test_delivery_desc=Send a fake push event delivery to test your webhook settings settings.webhook.test_delivery_desc=Send a fake push event delivery to test your webhook settings
settings.webhook.test_delivery_success=Test webhook has been added to delivery queue. It may take few seconds before it shows up in the delivery history. settings.webhook.test_delivery_success=Test webhook has been added to delivery queue. It may take few seconds before it shows up in the delivery history.
settings.webhook.request=Verzoek settings.webhook.request=Verzoek
@ -652,7 +662,7 @@ settings.slack_domain=Slack domein
settings.slack_channel=Slack kanaal settings.slack_channel=Slack kanaal
settings.deploy_keys=Installeer sleutels settings.deploy_keys=Installeer sleutels
settings.add_deploy_key=Toevoegen deploy sleutel settings.add_deploy_key=Toevoegen deploy sleutel
settings.deploy_key_desc=Deploy key only has read-only access. It is not same as personal account SSH keys. settings.deploy_key_desc=Deploy keys have read-only access. They are not the same as personal account SSH keys.
settings.no_deploy_keys=U hebt nog geen deploy sleutels toegevoegd. settings.no_deploy_keys=U hebt nog geen deploy sleutels toegevoegd.
settings.title=Titel settings.title=Titel
settings.deploy_key_content=Inhoud settings.deploy_key_content=Inhoud
@ -683,31 +693,31 @@ release.edit=bewerken
release.ahead=<strong>%d</strong> aanpassingen aan %s sinds deze versie release.ahead=<strong>%d</strong> aanpassingen aan %s sinds deze versie
release.source_code=Broncode release.source_code=Broncode
release.new_subheader=Publish releases to iterate product. release.new_subheader=Publish releases to iterate product.
release.edit_subheader=Detailed change log can help users understand what has been improved. release.edit_subheader=Een gedetailleerd changelog helpt gebruikers te begrijpen wat er is verbeterd in deze release.
release.tag_name=Tagnaam release.tag_name=Tagnaam
release.target=Doel release.target=Doel
release.tag_helper=Kies een bestaande tag, of creëer een nieuwe tag bij publiceren. release.tag_helper=Kies een bestaande tag, of creëer een nieuwe tag bij publiceren.
release.title=Title release.title=Titel
release.content=Content release.content=Inhoud
release.write=Schrijf release.write=Schrijf
release.preview=Voorbeeld release.preview=Voorbeeld
release.loading=Laden... release.loading=Laden...
release.prerelease_desc=Dit is een beta-versie release.prerelease_desc=Dit is een beta-versie
release.prerelease_helper=Wij wijzen u erop dat deze release is niet geschikt voor productie doeleinden. release.prerelease_helper=Wij wijzen u erop dat deze release is niet geschikt voor productie doeleinden.
release.cancel=Cancel release.cancel=Annuleren
release.publish=Release publiceren release.publish=Release publiceren
release.save_draft=Concept opslaan release.save_draft=Concept opslaan
release.edit_release=Release bewerken release.edit_release=Release bewerken
release.delete_release=Delete This Release release.delete_release=Deze release verwijderen
release.deletion=Release Deletion release.deletion=Release verwijderen
release.deletion_desc=Deleting this release will delete the corresponding Git tag. Do you want to continue? release.deletion_desc=Deleting this release will delete the corresponding Git tag. Do you want to continue?
release.deletion_success=Release has been deleted successfully! release.deletion_success=Release is verwijderd!
release.tag_name_already_exist=Versie met deze naam bestaat al. release.tag_name_already_exist=Versie met deze naam bestaat al.
release.downloads=Downloads release.downloads=Downloads
[org] [org]
org_name_holder=Organisatienaam org_name_holder=Organisatienaam
org_full_name_holder=Organization Full Name org_full_name_holder=Volledige naam organisatie
org_name_helper=Een goede organisatienaam is kort en memorabel. org_name_helper=Een goede organisatienaam is kort en memorabel.
create_org=Nieuwe organisatie aanmaken create_org=Nieuwe organisatie aanmaken
repo_updated=Geupdate repo_updated=Geupdate
@ -749,7 +759,7 @@ members.public=Openbaar
members.public_helper=maak prive members.public_helper=maak prive
members.private=Prive members.private=Prive
members.private_helper=maak openbaar members.private_helper=maak openbaar
members.member_role=Member Role: members.member_role=Rol van lid:
members.owner=Eigenaar members.owner=Eigenaar
members.member=Lid members.member=Lid
members.remove=Verwijderen members.remove=Verwijderen
@ -779,10 +789,10 @@ teams.read_permission_desc=Dit team heeft <strong>Lees</strong> rechten : leden
teams.write_permission_desc=Dit team heeft <strong>Schrijf</strong> rechten : leden kunnen repositories lezen en push aanvragen verwerken. teams.write_permission_desc=Dit team heeft <strong>Schrijf</strong> rechten : leden kunnen repositories lezen en push aanvragen verwerken.
teams.admin_permission_desc=Dit team heeft <strong>Beheerders</strong> rechten : leden kunnen repositories lezen en push aanvragen verwerken en medewerkers toevoegen. teams.admin_permission_desc=Dit team heeft <strong>Beheerders</strong> rechten : leden kunnen repositories lezen en push aanvragen verwerken en medewerkers toevoegen.
teams.repositories=Teamrepositories teams.repositories=Teamrepositories
teams.search_repo_placeholder=Search repository... teams.search_repo_placeholder=Repository zoeken...
teams.add_team_repository=Nieuwe teamrepositorie aanmaken teams.add_team_repository=Nieuwe teamrepositorie aanmaken
teams.remove_repo=Verwijder teams.remove_repo=Verwijder
teams.add_nonexistent_repo=De opslagplaats die u probeert toe te voegen niet bestaat, kunt u het eerst aanmaken. teams.add_nonexistent_repo=De opslagplaats die u probeert toe te voegen bestaat niet: maak deze eerst aan.
[admin] [admin]
dashboard=Dashboard dashboard=Dashboard
@ -793,9 +803,9 @@ authentication=Autenticaties
config=Configuratie config=Configuratie
notices=Systeem aankondigingen notices=Systeem aankondigingen
monitor=Bijhouden monitor=Bijhouden
first_page=First first_page=Eerste
last_page=Last last_page=Laatste
total=Total: %d total=Totaal: %d
dashboard.statistic=Statistieken dashboard.statistic=Statistieken
dashboard.operations=Bewerkingen dashboard.operations=Bewerkingen
@ -818,6 +828,8 @@ dashboard.resync_all_sshkeys=Herschrijf '.ssh/authorized_keys' (Let op: alle sle
dashboard.resync_all_sshkeys_success=Alle publieke sleutels zijn herschreven. dashboard.resync_all_sshkeys_success=Alle publieke sleutels zijn herschreven.
dashboard.resync_all_update_hooks=Herschrijf alle repositorie-hooks (nodig als de configuratie bestandslocatie is gewijzigd) dashboard.resync_all_update_hooks=Herschrijf alle repositorie-hooks (nodig als de configuratie bestandslocatie is gewijzigd)
dashboard.resync_all_update_hooks_success=Alle repositorie-hooks zijn herschreven. dashboard.resync_all_update_hooks_success=Alle repositorie-hooks zijn herschreven.
dashboard.reinit_missing_repos=Reinitialize all repository records that lost Git files
dashboard.reinit_missing_repos_success=All repository records that lost Git files have been reinitialized successfully.
dashboard.server_uptime=Uptime server dashboard.server_uptime=Uptime server
dashboard.current_goroutine=Huidige Goroutines dashboard.current_goroutine=Huidige Goroutines
@ -856,12 +868,12 @@ users.activated=Geactiveerd
users.admin=Admin users.admin=Admin
users.repos=Repos users.repos=Repos
users.created=Aangemaakt users.created=Aangemaakt
users.send_register_notify=Send Registration Notification To User users.send_register_notify=Stuur notificatie voor registratie naar gebruiker
users.new_success=New account '%s' has been created successfully. users.new_success=Nieuw account '%s' is aangemaakt.
users.edit=Bewerken users.edit=Bewerken
users.auth_source=Authentication Source users.auth_source=Authenticatiebron
users.local=Lokaal users.local=Lokaal
users.auth_login_name=Authentication Login Name users.auth_login_name=Authenticatie-loginnaam
users.password_helper=Leave it empty to remain unchanged. users.password_helper=Leave it empty to remain unchanged.
users.update_profile_success=Profiel is succesvol bijgewerkt. users.update_profile_success=Profiel is succesvol bijgewerkt.
users.edit_account=Bewerk account users.edit_account=Bewerk account
@ -870,12 +882,12 @@ users.max_repo_creation_desc=(Set -1 to use global default limit)
users.is_activated=Dit account is geactiveerd users.is_activated=Dit account is geactiveerd
users.is_admin=Dit account heeft beheerdersrechten users.is_admin=Dit account heeft beheerdersrechten
users.allow_git_hook=Deze account beschikt over machtigingen voor het maken van Git haken users.allow_git_hook=Deze account beschikt over machtigingen voor het maken van Git haken
users.allow_import_local=This account has permissions to import local repositories users.allow_import_local=Dit account mag lokale repositories importeren
users.update_profile=Account profiel bijwerken users.update_profile=Account profiel bijwerken
users.delete_account=Dit account verwijderen users.delete_account=Dit account verwijderen
users.still_own_repo=Dit account is nog steeds eigendom van een repositorie. U moet deze repositorie eerst verwijderen of overdragen. users.still_own_repo=Dit account is nog steeds eigendom van een repositorie. U moet deze repositorie eerst verwijderen of overdragen.
users.still_has_org=Deze account nog steeds lidmaatschap van organisatie, u hebt naar links of hen eerst verwijderen. users.still_has_org=Deze account nog steeds lidmaatschap van organisatie, u hebt naar links of hen eerst verwijderen.
users.deletion_success=Account has been deleted successfully! users.deletion_success=Het account is verwijderd!
orgs.org_manage_panel=Organisaties beheren orgs.org_manage_panel=Organisaties beheren
orgs.name=Naam orgs.name=Naam
@ -891,13 +903,13 @@ repos.stars=Sterren
repos.issues=Kwesties repos.issues=Kwesties
auths.auth_manage_panel=Authentication Manage Panel auths.auth_manage_panel=Authentication Manage Panel
auths.new=Add New Source auths.new=Nieuwe bron toevoegen
auths.name=Naam auths.name=Naam
auths.type=Type auths.type=Type
auths.enabled=Ingeschakeld auths.enabled=Ingeschakeld
auths.updated=Bijgewerkt auths.updated=Bijgewerkt
auths.auth_type=Authentication Type auths.auth_type=Authenticatietype
auths.auth_name=Authentication Name auths.auth_name=Authenticatienaam
auths.domain=Domein auths.domain=Domein
auths.host=Host auths.host=Host
auths.port=Poort auths.port=Poort
@ -911,16 +923,17 @@ auths.attribute_username_placeholder=Leave empty to use sign-in form field value
auths.attribute_name=Voornaam attribuut auths.attribute_name=Voornaam attribuut
auths.attribute_surname=Achternaam attribuut auths.attribute_surname=Achternaam attribuut
auths.attribute_mail=E-mail attribuut auths.attribute_mail=E-mail attribuut
auths.filter=User Filter auths.attributes_in_bind=Fetch attributes in Bind DN context
auths.admin_filter=Admin Filter auths.filter=Gebruikersfilter
auths.admin_filter=Beheerdersfilter
auths.ms_ad_sa=MS Ad SA auths.ms_ad_sa=MS Ad SA
auths.smtp_auth=SMTP Authentication Type auths.smtp_auth=SMTP-authenticatietype
auths.smtphost=SMTP host auths.smtphost=SMTP host
auths.smtpport=SMTP poort auths.smtpport=SMTP poort
auths.allowed_domains=Allowed Domains auths.allowed_domains=Toegelaten domeinen
auths.allowed_domains_helper=Leave it empty to not restrict any domains. Multiple domains should be separated by comma ','. auths.allowed_domains_helper=Leave it empty to not restrict any domains. Multiple domains should be separated by comma ','.
auths.enable_tls=Activeer TLS-encryptie auths.enable_tls=Activeer TLS-encryptie
auths.skip_tls_verify=Skip TLS Verify auths.skip_tls_verify=TLS-verificatie overslaan
auths.pam_service_name=PAM servicenaam auths.pam_service_name=PAM servicenaam
auths.enable_auto_register=Activeer automatische registratie auths.enable_auto_register=Activeer automatische registratie
auths.tips=Tips auths.tips=Tips
@ -928,10 +941,11 @@ auths.edit=Edit Authentication Setting
auths.activated=Deze autorisatiemethode is geactiveerd auths.activated=Deze autorisatiemethode is geactiveerd
auths.new_success=New authentication '%s' has been added successfully. auths.new_success=New authentication '%s' has been added successfully.
auths.update_success=Authentication setting has been updated successfully. auths.update_success=Authentication setting has been updated successfully.
auths.update=Update Authentication Setting auths.update=Authenticatie-instellingen bijwerken
auths.delete=Delete This Authentication auths.delete=Deze authenticatiewijze verwijderen
auths.delete_auth_title=Authentication Deletion auths.delete_auth_title=Authentication Deletion
auths.delete_auth_desc=This authentication is going to be deleted, do you want to continue? auths.delete_auth_desc=This authentication is going to be deleted, do you want to continue?
auths.still_in_used=This authentication is still used by some users, please delete or convert these users to another login type first.
auths.deletion_success=Authentication has been deleted successfully! auths.deletion_success=Authentication has been deleted successfully!
config.server_config=Serverconfiguratie config.server_config=Serverconfiguratie
@ -948,6 +962,19 @@ config.static_file_root_path=Statische bestanden basis pad
config.log_file_root_path=Log bestand basis pad config.log_file_root_path=Log bestand basis pad
config.script_type=Script type config.script_type=Script type
config.reverse_auth_user=Omgekeerde verificatie gebruiker config.reverse_auth_user=Omgekeerde verificatie gebruiker
config.ssh_config=SSH-configuratie
config.ssh_enabled=Ingeschakeld
config.ssh_start_builtin_server=Ingebouwde server starten
config.ssh_domain=Domein
config.ssh_port=Poort
config.ssh_listen_port=Luister op poort
config.ssh_root_path=Root-pad
config.ssh_key_test_path=Pad voor key-tests
config.ssh_keygen_path=Pad van keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Controleer minimale key-lengte
config.ssh_minimum_key_sizes=Minimale key-lengtes
config.db_config=Databaseconfiguratie config.db_config=Databaseconfiguratie
config.db_type=Type config.db_type=Type
config.db_host=Host config.db_host=Host
@ -956,16 +983,15 @@ config.db_user=Gebruiker
config.db_ssl_mode=SSL modus config.db_ssl_mode=SSL modus
config.db_ssl_mode_helper=(alleen voor "postgres") config.db_ssl_mode_helper=(alleen voor "postgres")
config.db_path=Pad config.db_path=Pad
config.db_path_helper=(for "sqlite3" and "tidb") config.db_path_helper=(voor "sqlite3" en "tidb")
config.service_config=Serviceconfiguratie config.service_config=Serviceconfiguratie
config.register_email_confirm=E-mailbevestiging registreren config.register_email_confirm=E-mailbevestiging registreren
config.disable_register=Registratie uitgeschakeld config.disable_register=Registratie uitgeschakeld
config.show_registration_button=Registeren knop weergeven config.show_registration_button=Registeren knop weergeven
config.require_sign_in_view=Inloggen vereist om te kunnen inzien config.require_sign_in_view=Inloggen vereist om te kunnen inzien
config.enable_cache_avatar=Avatar Cache inschakelen
config.mail_notify=E-mailnotificaties config.mail_notify=E-mailnotificaties
config.disable_key_size_check=Disable Minimum Key Size Check config.disable_key_size_check=Controle op key-lengte uitschakelen
config.enable_captcha=Enable Captcha config.enable_captcha=CAPTCHA inschakelen
config.active_code_lives=Actieve Code leven config.active_code_lives=Actieve Code leven
config.reset_password_code_lives=Reset wachtwoord Code leven config.reset_password_code_lives=Reset wachtwoord Code leven
config.webhook_config=Webhook configuratie config.webhook_config=Webhook configuratie
@ -978,6 +1004,9 @@ config.mailer_disable_helo=Schakel HELO uit
config.mailer_name=Naam config.mailer_name=Naam
config.mailer_host=Host config.mailer_host=Host
config.mailer_user=Gebruiker config.mailer_user=Gebruiker
config.send_test_mail=Testbericht verzenden
config.test_mail_failed=Verzending van een testmail naar '%s' is mislukt: %v
config.test_mail_sent=Test-email is verstuurd naar '%s'.
config.oauth_config=OAuth-configuratie config.oauth_config=OAuth-configuratie
config.oauth_enabled=Ingeschakeld config.oauth_enabled=Ingeschakeld
config.cache_config=Cache-configuratie config.cache_config=Cache-configuratie
@ -1012,11 +1041,11 @@ monitor.execute_time=Uitvoertijd
notices.system_notice_list=Systeem aankondigingen notices.system_notice_list=Systeem aankondigingen
notices.view_detail_header=View Notice Detail notices.view_detail_header=View Notice Detail
notices.actions=Actions notices.actions=Acties
notices.select_all=Select All notices.select_all=Alles selecteren
notices.deselect_all=Deselect All notices.deselect_all=Alles deselecteren
notices.inverse_selection=Inverse Selection notices.inverse_selection=Selectie omkeren
notices.delete_selected=Delete Selected notices.delete_selected=Selectie verwijderen
notices.delete_all=Delete All Notices notices.delete_all=Delete All Notices
notices.type=Type notices.type=Type
notices.type_1=Opslagplaats notices.type_1=Opslagplaats
@ -1029,12 +1058,16 @@ create_repo=repositorie aangemaakt in <a href="%s">%s</a>
rename_repo=renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a> rename_repo=renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a>
commit_repo=push update naar <a href="%[1]s/src/%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a> commit_repo=push update naar <a href="%[1]s/src/%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a>
create_issue=`opende issue in <a href="%s/issues/%s">%s#%[2]s</a>` create_issue=`opende issue in <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`closed issue <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`reopened issue <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`created pull request <a href="%s/pulls/%s">%s#%[2]s</a>` create_pull_request=`created pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request=`closed pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`reopened pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue=`reactie op issue <a href="%s/issues/%s">%s#%[2]s</a>` comment_issue=`reactie op issue <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request=`merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>` merge_pull_request=`merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo=repositorie verplaatst naar <code>%s</code> naar <a href="%s">%s</a> transfer_repo=repositorie verplaatst naar <code>%s</code> naar <a href="%s">%s</a>
push_tag=geduwd label <a href="%s/src/%s"> %[2]s</a> naar <a href="%[1]s"> %[3]s</a> push_tag=geduwd label <a href="%s/src/%s"> %[2]s</a> naar <a href="%[1]s"> %[3]s</a>
compare_commits=View comparison for these %d commits compare_commits=Toon vergelijking voor deze %d commits
[tool] [tool]
ago=geleden ago=geleden

79
conf/locale/locale_pl-PL.ini

@ -38,19 +38,12 @@ settings=Ustawienia
your_profile=Twój profil your_profile=Twój profil
your_settings=Twoje ustawienia your_settings=Twoje ustawienia
news_feed=Kanał aktualności activities=Aktywności
pull_requests=Oczekujące zmiany pull_requests=Oczekujące zmiany
issues=Problemy issues=Problemy
cancel=Anuluj cancel=Anuluj
[search]
search=Wyszukiwanie...
repository=Repozytorium
user=Użytkownik
issue=Problem
code=Kod
[install] [install]
install=Instalacja install=Instalacja
title=Kroki instalacyjne dla pierwszego uruchomienia title=Kroki instalacyjne dla pierwszego uruchomienia
@ -65,7 +58,7 @@ db_name=Nazwa bazy danych
db_helper=Proszę użyć silnika INNODB z kodowaniem utf8_general_ci dla MySQL. db_helper=Proszę użyć silnika INNODB z kodowaniem utf8_general_ci dla MySQL.
ssl_mode=Tryb SSL ssl_mode=Tryb SSL
path=Ścieżka path=Ścieżka
sqlite_helper=Ścieżka do pliku bazy danych SQLite3 lub TiDB. sqlite_helper=Ścieżka do pliku bazy danych SQLite3 lub TiDB. <br>Proszę użyć ścieżki bezwzględnej podczas uruchamiania usługi.
err_empty_db_path=Ścieżka do bazy danych SQLite3 lub TiDB nie może być pusta. err_empty_db_path=Ścieżka do bazy danych SQLite3 lub TiDB nie może być pusta.
err_invalid_tidb_name=Nazwa bazy danych TiDB nie może zawierać znaków "." i "-". err_invalid_tidb_name=Nazwa bazy danych TiDB nie może zawierać znaków "." i "-".
no_admin_and_disable_registration=Rejestracji nie można wyłączyć bez tworzenia konta admina. no_admin_and_disable_registration=Rejestracji nie można wyłączyć bez tworzenia konta admina.
@ -86,6 +79,8 @@ http_port=Port HTTP
http_port_helper=Numer portu na którym aplikacja jest dostępna. http_port_helper=Numer portu na którym aplikacja jest dostępna.
app_url=Adres URL aplikacji app_url=Adres URL aplikacji
app_url_helper=To wpłynie na adresy klonowania HTTP/HTTPS i w wiadomościach e-mail. app_url_helper=To wpłynie na adresy klonowania HTTP/HTTPS i w wiadomościach e-mail.
log_root_path=Ścieżka dla logów
log_root_path_helper=Katalog do zapisu logów.
optional_title=Ustawienia opcjonalne optional_title=Ustawienia opcjonalne
email_title=Ustawienia serwera e-mail email_title=Ustawienia serwera e-mail
@ -122,6 +117,7 @@ run_user_not_match=Użytkownik aplikacji nie jest aktualnym użytkownikiem: %s -
save_config_failed=Nie udało się zapisać konfiguracji: %v save_config_failed=Nie udało się zapisać konfiguracji: %v
invalid_admin_setting=Nieprawidłowe ustawienia konta admina: %v invalid_admin_setting=Nieprawidłowe ustawienia konta admina: %v
install_success=Cześć! Cieszymy się, że wybierałeś Gogs, baw się dobrze. install_success=Cześć! Cieszymy się, że wybierałeś Gogs, baw się dobrze.
invalid_log_root_path=Ścieżka dla logów jest niepoprawna: %v
[home] [home]
uname_holder=Nazwa użytkownika lub e-mail uname_holder=Nazwa użytkownika lub e-mail
@ -137,6 +133,8 @@ issues.in_your_repos=W Twoich repozytoriach
[explore] [explore]
repos=Repozytoria repos=Repozytoria
users=Użytkownicy
search=Wyszukiwanie
[auth] [auth]
create_new_account=Załóż nowe konto create_new_account=Załóż nowe konto
@ -203,7 +201,6 @@ repo_name_been_taken=Nazwa repozytorium jest już zajęta.
org_name_been_taken=Nazwa organizacji jest już zajęta. org_name_been_taken=Nazwa organizacji jest już zajęta.
team_name_been_taken=Nazwa zespołu jest już zajęta. team_name_been_taken=Nazwa zespołu jest już zajęta.
email_been_used=Adres e-mail jest już zarejestrowany. email_been_used=Adres e-mail jest już zarejestrowany.
illegal_team_name=Nazwa zespołu zawiera niedozwolone znaki.
username_password_incorrect=Nazwa użytkownika lub hasło nie jest prawidłowe. username_password_incorrect=Nazwa użytkownika lub hasło nie jest prawidłowe.
enterred_invalid_repo_name=Upewnij się, że wprowadzona nazwa repozytorium jest poprawna. enterred_invalid_repo_name=Upewnij się, że wprowadzona nazwa repozytorium jest poprawna.
enterred_invalid_owner_name=Upewnij się, że nazwa właściciela repozytorium jest poprawna. enterred_invalid_owner_name=Upewnij się, że nazwa właściciela repozytorium jest poprawna.
@ -219,8 +216,6 @@ still_own_repo=Twoje konto dalej posiada przynajmniej jedno repozytorium, które
still_has_org=Twoje konto dalej posiada członkostwo w przynajmniej jednej organizacji, którą musisz najpierw opuścić. still_has_org=Twoje konto dalej posiada członkostwo w przynajmniej jednej organizacji, którą musisz najpierw opuścić.
org_still_own_repo=Ta organizacja dalej jest właścicielem repozytorium, które musisz usunąć bądź przekazać. org_still_own_repo=Ta organizacja dalej jest właścicielem repozytorium, które musisz usunąć bądź przekazać.
still_own_user=To uwierzytelnienie dalej jest używane przez kilku użytkowników, których musisz z niego usunąć i spróbować ponownie.
target_branch_not_exist=Gałąź docelowa nie istnieje. target_branch_not_exist=Gałąź docelowa nie istnieje.
[user] [user]
@ -232,8 +227,8 @@ activity=Publiczna aktywność
followers=Obserwujący followers=Obserwujący
starred=Polubionych starred=Polubionych
following=Obserwowani following=Obserwowani
follow=Follow follow=Obserwuj
unfollow=Unfollow unfollow=Przestań obserwować
form.name_reserved=Nazwa użytkownika "%s" jest zarezerwowana. form.name_reserved=Nazwa użytkownika "%s" jest zarezerwowana.
form.name_pattern_not_allowed=Wzorzec nazwy użytkownika "%s" jest niedozwolony. form.name_pattern_not_allowed=Wzorzec nazwy użytkownika "%s" jest niedozwolony.
@ -262,11 +257,10 @@ continue=Kontynuuj
cancel=Anuluj cancel=Anuluj
enable_custom_avatar=Włącz niestandardowe awatary enable_custom_avatar=Włącz niestandardowe awatary
enable_custom_avatar_helper=Wyłącz pobieranie z Gravatar
choose_new_avatar=Wybierz nowy avatar choose_new_avatar=Wybierz nowy avatar
update_avatar=Zaktualizuj ustawienia awatara update_avatar=Zaktualizuj ustawienia awatara
delete_current_avatar=Usuń obecny Avatar
uploaded_avatar_not_a_image=Załadowany plik nie jest obrazem. uploaded_avatar_not_a_image=Załadowany plik nie jest obrazem.
no_custom_avatar_available=Własny avatar niedostępny, nie można go włączyć.
update_avatar_success=Ustawienia awatarów zostały pomyślnie zaktualizowane. update_avatar_success=Ustawienia awatarów zostały pomyślnie zaktualizowane.
change_password=Zmień hasło change_password=Zmień hasło
@ -477,7 +471,7 @@ issues.closed_at=`zamyka <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`otwiera ponownie <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`otwiera ponownie <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`wspomina ten problem w commicie <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`wspomina ten problem w commicie <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.poster=Autor issues.poster=Autor
issues.admin=Admin issues.collaborator=Współpracownik
issues.owner=Właściciel issues.owner=Właściciel
issues.sign_up_for_free=Zarejestruj się za darmo issues.sign_up_for_free=Zarejestruj się za darmo
issues.sign_in_require_desc=do przyłączenia się do tej rozmowy. Masz już konto? <a href="%s">Zaloguj się by komentować</a> issues.sign_in_require_desc=do przyłączenia się do tej rozmowy. Masz już konto? <a href="%s">Zaloguj się by komentować</a>
@ -494,6 +488,7 @@ issues.label_modify=Modyfikacja etykiety
issues.label_deletion=Usunięcie etykiety issues.label_deletion=Usunięcie etykiety
issues.label_deletion_desc=Usunięcie tej etykiety spowoduje usuniecie jej ze wszystkich powiązanych problemów. Czy na pewno chcesz kontynuować? issues.label_deletion_desc=Usunięcie tej etykiety spowoduje usuniecie jej ze wszystkich powiązanych problemów. Czy na pewno chcesz kontynuować?
issues.label_deletion_success=Etykieta została usunięta pomyślnie! issues.label_deletion_success=Etykieta została usunięta pomyślnie!
issues.num_participants=%d uczestników
pulls.new=Nowy pull request pulls.new=Nowy pull request
pulls.compare_changes=Porównaj zmiany pulls.compare_changes=Porównaj zmiany
@ -557,6 +552,8 @@ wiki.save_page=Zapisz stronę
wiki.last_commit_info=%s edytuje tę stronę %s wiki.last_commit_info=%s edytuje tę stronę %s
wiki.edit_page_button=Edytuj wiki.edit_page_button=Edytuj
wiki.new_page_button=Nowa strona wiki.new_page_button=Nowa strona
wiki.delete_page_button=Usuń stronę
wiki.delete_page_notice_1=Strona zostanie usunięta <code>"%s"</code>. Bądź ostrożny.
wiki.page_already_exists=Strona Wiki o tej samej nazwie już istnieje. wiki.page_already_exists=Strona Wiki o tej samej nazwie już istnieje.
wiki.pages=Strony wiki.pages=Strony
wiki.last_updated=Ostatnia aktualizacja %s wiki.last_updated=Ostatnia aktualizacja %s
@ -581,14 +578,23 @@ settings.tracker_url_format=Format dla adresu URL zewnętrznego systemu
settings.tracker_url_format_desc=Symbole zastępcze <code>{user} {repo} {index}</code> mogą być użyte dla nazwy użytkownika, nazwy repozytorium i numeru problemu. settings.tracker_url_format_desc=Symbole zastępcze <code>{user} {repo} {index}</code> mogą być użyte dla nazwy użytkownika, nazwy repozytorium i numeru problemu.
settings.pulls_desc=Włącz obsługę pull request, aby akceptować publiczny wkład settings.pulls_desc=Włącz obsługę pull request, aby akceptować publiczny wkład
settings.danger_zone=Strefa niebezpieczeństwa settings.danger_zone=Strefa niebezpieczeństwa
settings.new_owner_has_same_repo=Nowy właściciel już posiada repozytorium o tej samej nazwie.
settings.convert=Konwersja na repozytorium regularne
settings.convert_desc=Możesz przekonwertować ten mirror na repozytorium regularne. Ta czynność nie może być odwrócona.
settings.convert_notices_1=- Ta operacja przekonwertuje mirror tego repozytorium na repozytorium regularne. Ta czynność nie może być odwrócona.
settings.convert_confirm=Potwierdź konwersję
settings.convert_succeed=Typ repozytorium został zamieniony na regularne.
settings.transfer=Przeniesienie własności settings.transfer=Przeniesienie własności
settings.transfer_desc=Przenieś to repozytorium do innego użytkownika lub organizacji gdzie masz uprawnienia administratora. settings.transfer_desc=Przenieś to repozytorium do innego użytkownika lub organizacji gdzie masz uprawnienia administratora.
settings.new_owner_has_same_repo=Nowy właściciel już posiada repozytorium o tej samej nazwie.
settings.delete=Usuń to repozytorium
settings.delete_desc=Po usunięciu repozytorium nie ma odwrotu. Upewnij się, że tego chcesz.
settings.transfer_notices_1=- Stracisz dostęp jeśli nowy właściciel jest indywidualnym użytkownikiem. settings.transfer_notices_1=- Stracisz dostęp jeśli nowy właściciel jest indywidualnym użytkownikiem.
settings.transfer_notices_2=- Zachowasz dostęp jeśli nowym właścicielem jest organizacja, której jesteś współwłaścicielem. settings.transfer_notices_2=- Zachowasz dostęp jeśli nowym właścicielem jest organizacja, której jesteś współwłaścicielem.
settings.transfer_form_title=Proszę wpisz co następuje w celu potwierdzenia operacji: settings.transfer_form_title=Proszę wpisz co następuje w celu potwierdzenia operacji:
settings.wiki_delete=Kasowanie danych Wiki
settings.wiki_delete_desc=Usunięcie danych z wiki jest nieodwracalne. Bądź ostrożny.
settings.wiki_delete_notices_1=- To usunie i wyłączy wiki dla %s
settings.wiki_deletion_success=Dane wiki zostały usunięte.
settings.delete=Usuń to repozytorium
settings.delete_desc=Po usunięciu repozytorium nie ma odwrotu. Upewnij się, że tego chcesz.
settings.delete_notices_1=- Ta operacja <strong>NIE MOŻE</strong> zostać cofnięta. settings.delete_notices_1=- Ta operacja <strong>NIE MOŻE</strong> zostać cofnięta.
settings.delete_notices_2=- Ta operacja trwale usunie wszystko z tego repozytorium, w tym dane Git, problemy, komentarze i dostęp dla współpracowników. settings.delete_notices_2=- Ta operacja trwale usunie wszystko z tego repozytorium, w tym dane Git, problemy, komentarze i dostęp dla współpracowników.
settings.delete_notices_fork_1=- Jeśli to repozytorium jest publiczne, wszystkie forki staną się niezależne. settings.delete_notices_fork_1=- Jeśli to repozytorium jest publiczne, wszystkie forki staną się niezależne.
@ -602,8 +608,12 @@ settings.transfer_succeed=Własność repozytorium została przeniesiona pomyśl
settings.confirm_delete=Potwierdź usunięcie settings.confirm_delete=Potwierdź usunięcie
settings.add_collaborator=Dodaj nowego współpracownika settings.add_collaborator=Dodaj nowego współpracownika
settings.add_collaborator_success=Został dodany nowy współpracownik. settings.add_collaborator_success=Został dodany nowy współpracownik.
settings.delete_collaborator=Usuń
settings.collaborator_deletion=Usunięcie współpracownika
settings.collaborator_deletion_desc=Ten użytkownik nie będzie miał dostępu współpracownika do repozytorium. Czy chcesz kontynuować?
settings.remove_collaborator_success=Współpracownik został usunięty. settings.remove_collaborator_success=Współpracownik został usunięty.
settings.search_user_placeholder=Szukaj użytkownika... settings.search_user_placeholder=Szukaj użytkownika...
settings.org_not_allowed_to_be_collaborator=Organizacji nie można dodać jako współpracownika.
settings.user_is_org_member=Użytkownik jest członkiem organizacji, który nie może być dodany jako współpracownik. settings.user_is_org_member=Użytkownik jest członkiem organizacji, który nie może być dodany jako współpracownik.
settings.add_webhook=Dodaj webhooka settings.add_webhook=Dodaj webhooka
settings.hooks_desc=Webooki działają tak jak proste wywołania HTTP POST. Jeśli cokolwiek zdarzy się w Gogs, wyślemy powiadomienie do wybranego hosta. Więcej informacji można znaleźć w <a target="_blank" href="%s">przewodniku webhooków</a>. settings.hooks_desc=Webooki działają tak jak proste wywołania HTTP POST. Jeśli cokolwiek zdarzy się w Gogs, wyślemy powiadomienie do wybranego hosta. Więcej informacji można znaleźć w <a target="_blank" href="%s">przewodniku webhooków</a>.
@ -668,8 +678,8 @@ diff.parent=rodzic
diff.commit=commit diff.commit=commit
diff.data_not_available=Informacje nt. zmiany nie są dostępne. diff.data_not_available=Informacje nt. zmiany nie są dostępne.
diff.show_diff_stats=Pokaż statystyki zmian diff.show_diff_stats=Pokaż statystyki zmian
diff.show_split_view=Split View diff.show_split_view=Widok podzielony
diff.show_unified_view=Unified View diff.show_unified_view=Zunifikowany widok
diff.stats_desc=<strong>%d zmienionych plików</strong> z <strong>%d dodań</strong> i <strong>%d usunięć</strong> diff.stats_desc=<strong>%d zmienionych plików</strong> z <strong>%d dodań</strong> i <strong>%d usunięć</strong>
diff.bin=BIN diff.bin=BIN
diff.view_file=Wyświetl plik diff.view_file=Wyświetl plik
@ -818,6 +828,8 @@ dashboard.resync_all_sshkeys=Przeładuj klucze publiczne w pliku '.ssh/authorize
dashboard.resync_all_sshkeys_success=Przeładowanie kluczy publicznych zakończyło się sukcesem. dashboard.resync_all_sshkeys_success=Przeładowanie kluczy publicznych zakończyło się sukcesem.
dashboard.resync_all_update_hooks=Przepisz pliki update hook repozytoriów (wymagane przy zmianie ścieżki do pliku konfiguracji) dashboard.resync_all_update_hooks=Przepisz pliki update hook repozytoriów (wymagane przy zmianie ścieżki do pliku konfiguracji)
dashboard.resync_all_update_hooks_success=Wszystkie pliki update hook repozytoriów zostały pomyślnie przepisane. dashboard.resync_all_update_hooks_success=Wszystkie pliki update hook repozytoriów zostały pomyślnie przepisane.
dashboard.reinit_missing_repos=Ponownie inicjalizuj wszystkie repozytoria, które straciły pliki Git
dashboard.reinit_missing_repos_success=Wszystkie repozytoria, które straciły pliki Git, zostały ponownie zainicjować pomyślnie.
dashboard.server_uptime=Uptime serwera dashboard.server_uptime=Uptime serwera
dashboard.current_goroutine=Bieżące Goroutines dashboard.current_goroutine=Bieżące Goroutines
@ -911,6 +923,7 @@ auths.attribute_username_placeholder=Zostaw puste aby użyć wartości podanej p
auths.attribute_name=Atrybut imienia auths.attribute_name=Atrybut imienia
auths.attribute_surname=Atrybut nazwiska auths.attribute_surname=Atrybut nazwiska
auths.attribute_mail=Atrybut e-mail auths.attribute_mail=Atrybut e-mail
auths.attributes_in_bind=Pobierz atrybuty w kontekście Bind DN
auths.filter=Filtr użytkownika auths.filter=Filtr użytkownika
auths.admin_filter=Filtr administratora auths.admin_filter=Filtr administratora
auths.ms_ad_sa=Ms Ad SA auths.ms_ad_sa=Ms Ad SA
@ -932,6 +945,7 @@ auths.update=Aktualizuj ustawienia uwierzytelniania
auths.delete=Usuń to uwierzytelnienie auths.delete=Usuń to uwierzytelnienie
auths.delete_auth_title=Usunięcie uwierzytelnienia auths.delete_auth_title=Usunięcie uwierzytelnienia
auths.delete_auth_desc=To uwierzytelnienie zostanie usunięte, czy chcesz kontynuować? auths.delete_auth_desc=To uwierzytelnienie zostanie usunięte, czy chcesz kontynuować?
auths.still_in_used=Ten rodzaj autentykacji jest wciąż wykorzystywany przez niektórych użytkowników. Usuń lub przekonwertuj użytkowników, aby wykorzystywali inny typ logowania.
auths.deletion_success=Uwierzytelnienie zostało usunięte pomyślnie! auths.deletion_success=Uwierzytelnienie zostało usunięte pomyślnie!
config.server_config=Konfiguracja serwera config.server_config=Konfiguracja serwera
@ -948,6 +962,19 @@ config.static_file_root_path=Ścieżka plików statycznych
config.log_file_root_path=Ścieżka plików dziennika config.log_file_root_path=Ścieżka plików dziennika
config.script_type=Typ skryptu config.script_type=Typ skryptu
config.reverse_auth_user=Użytkownik dostarczony przez odwrotne proxy config.reverse_auth_user=Użytkownik dostarczony przez odwrotne proxy
config.ssh_config=Konfiguracja SSH
config.ssh_enabled=Aktywne
config.ssh_start_builtin_server=Uruchom serwer wbudowany
config.ssh_domain=Domena
config.ssh_port=Port
config.ssh_listen_port=Port nasłuchu
config.ssh_root_path=Ścieżka katalogu głównego
config.ssh_key_test_path=Ścieżka klucza testowego
config.ssh_keygen_path=Ścieżka generatora ('ssh-keygen')
config.ssh_minimum_key_size_check=Sprawdzanie minimalnej długości klucza
config.ssh_minimum_key_sizes=Minimalne rozmiary kluczy
config.db_config=Konfiguracja bazy danych config.db_config=Konfiguracja bazy danych
config.db_type=Typ config.db_type=Typ
config.db_host=Host config.db_host=Host
@ -962,7 +989,6 @@ config.register_email_confirm=Wymagaj potwierdzenia e-mail
config.disable_register=Wyłącz rejestrację config.disable_register=Wyłącz rejestrację
config.show_registration_button=Pokazuj przycisk rejestracji config.show_registration_button=Pokazuj przycisk rejestracji
config.require_sign_in_view=Wymagaj bycia zalogowanym config.require_sign_in_view=Wymagaj bycia zalogowanym
config.enable_cache_avatar=Włącz cache awatarów
config.mail_notify=Powiadomienia e-mail config.mail_notify=Powiadomienia e-mail
config.disable_key_size_check=Wyłącz sprawdzanie minimalnego rozmiaru klucza config.disable_key_size_check=Wyłącz sprawdzanie minimalnego rozmiaru klucza
config.enable_captcha=Włącz Captcha config.enable_captcha=Włącz Captcha
@ -978,6 +1004,9 @@ config.mailer_disable_helo=Wyłącz HELO
config.mailer_name=Nazwa config.mailer_name=Nazwa
config.mailer_host=Host config.mailer_host=Host
config.mailer_user=Użytkownik config.mailer_user=Użytkownik
config.send_test_mail=Wyślij email testowy
config.test_mail_failed=Nieudane wysłanie wiadomości email do '%s': %v
config.test_mail_sent=Testowa wiadomość email została wysłana do '%s'.
config.oauth_config=Konfiguracja OAuth config.oauth_config=Konfiguracja OAuth
config.oauth_enabled=Aktywne config.oauth_enabled=Aktywne
config.cache_config=Konfiguracja cache config.cache_config=Konfiguracja cache
@ -1029,7 +1058,11 @@ create_repo=tworzy repozytorium <a href="%s">%s</a>
rename_repo=zmienia nazwę repozytorium <code>%[1]s</code> na <a href="%[2]s">%[3]s</a> rename_repo=zmienia nazwę repozytorium <code>%[1]s</code> na <a href="%[2]s">%[3]s</a>
commit_repo=wypycha do <a href="%[1]s/src/%[2]s">%[3]s</a> w <a href="%[1]s">%[4]s</a> commit_repo=wypycha do <a href="%[1]s/src/%[2]s">%[3]s</a> w <a href="%[1]s">%[4]s</a>
create_issue=`zgłasza problem <a href="%s/issues/%s">%s#%[2]s</a>` create_issue=`zgłasza problem <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`zamknięcie problemu <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`ponowne otwarcie problemu <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`tworzy pull request <a href="%s/pulls/%s">%s#%[2]s</a>` create_pull_request=`tworzy pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request=`zamknięcie pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`ponowne otwarcie pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue=`komentuje problem <a href="%s/issues/%s">%s#%[2]s</a>` comment_issue=`komentuje problem <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request=`scala pull request <a href="%s/pulls/%s">%s#%[2]s</a>` merge_pull_request=`scala pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo=przenosi repozytorium <code>%s</code> do <a href="%s">%s</a> transfer_repo=przenosi repozytorium <code>%s</code> do <a href="%s">%s</a>

133
conf/locale/locale_pt-BR.ini

@ -20,7 +20,7 @@ signed_in_as=Você é
username=Usuário username=Usuário
email=E-mail email=E-mail
password=Senha password=Senha
re_type=Redigite re_type=Digite novamente
captcha=Captcha captcha=Captcha
repository=Repositório repository=Repositório
@ -38,22 +38,15 @@ settings=Configurações
your_profile=Seu perfil your_profile=Seu perfil
your_settings=Suas configurações your_settings=Suas configurações
news_feed=Feed de Notícias activities=Atividades
pull_requests=Pull Requests pull_requests=Pull Requests
issues=Problemas issues=Problemas
cancel=Cancelar cancel=Cancelar
[search]
search=Pesquisar...
repository=Repositório
user=Usuário
issue=Problema
code=Código
[install] [install]
install=Instalação install=Instalação
title=Etapas de instalação para Primeira Execução title=Etapas de instalação para primeira execução
docker_helper=Se você está rodando o Gogs dentro do Docker, por favor leia os <a target="_blank" href="%s">Guias</a> cuidadosamente antes de mudar qualquer coisa nesta página! docker_helper=Se você está rodando o Gogs dentro do Docker, por favor leia os <a target="_blank" href="%s">Guias</a> cuidadosamente antes de mudar qualquer coisa nesta página!
requite_db_desc=Gogs requer MySQL, PostgreSQL, SQLite3 ou TiDB. requite_db_desc=Gogs requer MySQL, PostgreSQL, SQLite3 ou TiDB.
db_title=Configurações de Banco de Dados db_title=Configurações de Banco de Dados
@ -61,22 +54,22 @@ db_type=Tipo do Banco de Dados
host=Host host=Host
user=Usuário user=Usuário
password=Senha password=Senha
db_name=Nome do Banco de Dados db_name=Nome do banco de dados
db_helper=Por favor, use o mecanismo INNODB com o conjunto de caracteres utf8_general_ci para MySQL. db_helper=Por favor, use o mecanismo INNODB com o conjunto de caracteres utf8_general_ci para MySQL.
ssl_mode=Modo SSL ssl_mode=Modo SSL
path=Caminho path=Caminho
sqlite_helper=O caminho do arquivo do banco de dados SQLite3 ou TiDB. sqlite_helper=O caminho do arquivo de banco de dados SQLite3 ou TiDB. <br>Por favor use o caminho absoluto quando você iniciar como serviço.
err_empty_db_path=O Caminho do banco de dados SQLite3 ou TiDB não pode ser vazio. err_empty_db_path=O Caminho do banco de dados SQLite3 ou TiDB não pode ser vazio.
err_invalid_tidb_name=Nome do banco de dados TiDB não permite os caracteres "." e "-". err_invalid_tidb_name=Nome do banco de dados TiDB não permite os caracteres "." e "-".
no_admin_and_disable_registration=Você não pode desabilitar o registro sem criar uma conta de administrador. no_admin_and_disable_registration=Você não pode desabilitar o registro sem criar uma conta de administrador.
err_empty_admin_password=A senha de administrador não pode ser vazia. err_empty_admin_password=A senha de administrador não pode ser vazia.
general_title=Configurações Gerais do Gogs general_title=Configurações gerais do Gogs
app_name=Nome do Aplicativo app_name=Nome do aplicativo
app_name_helper=Coloque o nome da sua organização aqui! app_name_helper=Coloque o nome da sua organização aqui!
repo_path=Caminho da Raiz do Repositório repo_path=Caminho da raíz do repositório
repo_path_helper=Todos os repositórios remotos do Git serão salvos neste diretório. repo_path_helper=Todos os repositórios remotos do Git serão salvos neste diretório.
run_user=Executar Usuário run_user=Usuário da execução
run_user_helper=O usuário deve ter acesso ao caminho raiz do repositório e executar o Gogs run_user_helper=O usuário deve ter acesso ao caminho raiz do repositório e executar o Gogs
domain=Domínio domain=Domínio
domain_helper=Isto afeta URLs para clonagem via SSH. domain_helper=Isto afeta URLs para clonagem via SSH.
@ -84,35 +77,37 @@ ssh_port=Porta SSH
ssh_port_helper=Número da porta que seu servidor SSH está usando, deixe vazio para desativar o recurso SSH. ssh_port_helper=Número da porta que seu servidor SSH está usando, deixe vazio para desativar o recurso SSH.
http_port=Porta HTTP http_port=Porta HTTP
http_port_helper=Número da porta em que a aplicação irá executar. http_port_helper=Número da porta em que a aplicação irá executar.
app_url=URL do Aplicativo app_url=URL do aplicativo
app_url_helper=Isto afeta a URL de clonagem via HTTP/HTTPs e também o email. app_url_helper=Isto afeta a URL de clonagem via HTTP/HTTPs e também o email.
log_root_path=Caminho do log
log_root_path_helper=Pasta dos arquivos de log.
optional_title=Configurações Opcionais optional_title=Configurações opcionais
email_title=Configurações do Serviço de E-mail email_title=Configurações do serviço de e-mail
smtp_host=Host SMTP smtp_host=Host SMTP
smtp_from=De smtp_from=De
smtp_from_helper=O endereço de email deve atender a especificação RFC 5322. O formato deve ser um email ou "Nome" <email@example.com>. smtp_from_helper=O endereço de email deve atender a especificação RFC 5322. O formato deve ser um email ou "Nome" <email@example.com>.
mailer_user=E-mail do Remetente mailer_user=E-mail do remetente
mailer_password=Senha do Remetente mailer_password=Senha do remetente
register_confirm=Habilitar Confirmação de Registro register_confirm=Habilitar confirmação de registro
mail_notify=Habilitar Notificação de Correio mail_notify=Habilitar notificação de e-mail
server_service_title=Configurações de Servidor e Outros Serviços server_service_title=Configurações de servidor e outros serviços
offline_mode=Ativar Modo Offline offline_mode=Ativar modo off-line
offline_mode_popup=Desative o CDN mesmo em modo de produção, todos os recursos serão disponibilizados localmente. offline_mode_popup=Desative o CDN mesmo em modo de produção, todos os recursos serão disponibilizados localmente.
disable_gravatar=Desativar Serviço Gravatar disable_gravatar=Desativar serviço Gravatar
disable_gravatar_popup=Desabilitar o Gravatar e fontes personalizadas, todos os avatares são enviados por usuários ou padrão. disable_gravatar_popup=Desabilitar o Gravatar e fontes personalizadas, todos os avatares são enviados por usuários ou padrão.
disable_registration=Desativar auto-registro disable_registration=Desativar auto-registro
disable_registration_popup=Desativar o auto-registro de usuário, para que somente o administrador possa criar contas. disable_registration_popup=Desativar o auto-registro de usuário, para que somente o administrador possa criar contas.
enable_captcha=Habilitar Captcha enable_captcha=Habilitar captcha
enable_captcha_popup=Obrigar validação por captcha para auto-registro de usuários. enable_captcha_popup=Obrigar validação por captcha para auto-registro de usuários.
require_sign_in_view=Requerer login para a visualização de páginas require_sign_in_view=Obrigar login para a visualização de páginas
require_sign_in_view_popup=Somente usuários autenticados podem ver todas as páginas, visitantes somente podem entrar ou se cadastrar. require_sign_in_view_popup=Somente usuários autenticados podem ver todas as páginas, visitantes somente podem entrar ou se cadastrar.
admin_setting_desc=Você não precisa criar uma conta de administrador agora, no entanto o primeiro usuário (ID=1) automaticamente terá acesso de administrador. admin_setting_desc=Você não precisa criar uma conta de administrador agora, no entanto o primeiro usuário (ID=1) automaticamente terá acesso de administrador.
admin_title=Configurações da Conta de Administrador admin_title=Configurações da conta de administrador
admin_name=Nome de Usuário admin_name=Nome de usuário
admin_password=Senha admin_password=Senha
confirm_password=Confirmar Senha confirm_password=Confirmar senha
admin_email=E-mail do Administrador admin_email=E-mail do administrador
install_gogs=Instalar Gogs install_gogs=Instalar Gogs
test_git_failed=Falha ao testar o comando 'git': %v test_git_failed=Falha ao testar o comando 'git': %v
sqlite3_not_available=Sua versão não suporta SQLite3, por favor faça o download da versão binária oficial em %s, NÃO da versão gobuild. sqlite3_not_available=Sua versão não suporta SQLite3, por favor faça o download da versão binária oficial em %s, NÃO da versão gobuild.
@ -122,6 +117,7 @@ run_user_not_match=O usuário da execução não é o usuário atual: %s -> %s
save_config_failed=Falha ao salvar a configuração: %v save_config_failed=Falha ao salvar a configuração: %v
invalid_admin_setting=Configuração da conta de administrador está inválida: %v invalid_admin_setting=Configuração da conta de administrador está inválida: %v
install_success=Bem-vindo! Estamos contentes que você escolheu o Gogs, divirta-se e tenha cuidado. install_success=Bem-vindo! Estamos contentes que você escolheu o Gogs, divirta-se e tenha cuidado.
invalid_log_root_path=Pasta raíz do log é inválida: %v
[home] [home]
uname_holder=Nome de Usuário ou E-mail uname_holder=Nome de Usuário ou E-mail
@ -137,6 +133,8 @@ issues.in_your_repos=Em seus repositórios
[explore] [explore]
repos=Repositórios repos=Repositórios
users=Usuários
search=Pesquisar
[auth] [auth]
create_new_account=Criar Nova Conta create_new_account=Criar Nova Conta
@ -188,7 +186,7 @@ AdminEmail=E-mail do Administrador
require_error=` não pode estar vazio.` require_error=` não pode estar vazio.`
alpha_dash_error=` devem ser caracteres alfanuméricos ou hífen (-) ou sublinhado (_).` alpha_dash_error=` devem ser caracteres alfanuméricos ou hífen (-) ou sublinhado (_).`
alpha_dash_dot_error=` devem ser caracteres alfanuméricos ou hífen (-) ou sublinhado (_).` alpha_dash_dot_error=` devem ser caracteres alfanuméricos ou hífen (-) ou sublinhado (_).`
size_error='deve ser o tamanho %s.' size_error=`deve ser do tamanho %s.`
min_size_error=` deve conter pelo menos %s caracteres.` min_size_error=` deve conter pelo menos %s caracteres.`
max_size_error=` deve conter no máximo %s caracteres.` max_size_error=` deve conter no máximo %s caracteres.`
email_error=` não é um endereço de e-mail válido.` email_error=` não é um endereço de e-mail válido.`
@ -203,7 +201,6 @@ repo_name_been_taken=Nome do repositório já foi tomado.
org_name_been_taken=Nome da organização já foi tomado. org_name_been_taken=Nome da organização já foi tomado.
team_name_been_taken=Nome da equipe já foi tomado. team_name_been_taken=Nome da equipe já foi tomado.
email_been_used=Endereço de e-mail já foi usado. email_been_used=Endereço de e-mail já foi usado.
illegal_team_name=O nome da equipe contém caracteres não permitidos.
username_password_incorrect=Usuário ou senha incorretos. username_password_incorrect=Usuário ou senha incorretos.
enterred_invalid_repo_name=Por favor certifique-se que informou o nome do repositório corretamente. enterred_invalid_repo_name=Por favor certifique-se que informou o nome do repositório corretamente.
enterred_invalid_owner_name=Por favor, verifique se o nome do proprietário está correto. enterred_invalid_owner_name=Por favor, verifique se o nome do proprietário está correto.
@ -219,8 +216,6 @@ still_own_repo=Sua conta ainda tem propriedade do repositório, você tem que ex
still_has_org=Sua conta ainda faz parte da organização, você deve sair ou excluí-la primeiro. still_has_org=Sua conta ainda faz parte da organização, você deve sair ou excluí-la primeiro.
org_still_own_repo=Esta organização ainda tem a propriedade do repositório, você deve excluir ou transferí-la primeiro. org_still_own_repo=Esta organização ainda tem a propriedade do repositório, você deve excluir ou transferí-la primeiro.
still_own_user=Esta autenticação ainda é usada por alguns usuários, você deve movê-los e depois apagar novamente.
target_branch_not_exist=O branch de destino não existe. target_branch_not_exist=O branch de destino não existe.
[user] [user]
@ -262,11 +257,10 @@ continue=Continuar
cancel=Cancelar cancel=Cancelar
enable_custom_avatar=Habilitar Avatar Customizado enable_custom_avatar=Habilitar Avatar Customizado
enable_custom_avatar_helper=Habilite para desativar a busca no Gravatar
choose_new_avatar=Escolha um novo avatar choose_new_avatar=Escolha um novo avatar
update_avatar=Atualizar configuração de Avatar update_avatar=Atualizar configuração de Avatar
delete_current_avatar=Excluir o Avatar atual
uploaded_avatar_not_a_image=O arquivo enviado não é uma imagem. uploaded_avatar_not_a_image=O arquivo enviado não é uma imagem.
no_custom_avatar_available=Nenhum avatar personalizado disponível, não pode habilitá-lo.
update_avatar_success=Sua configuração de avatar foi atualizada com sucesso. update_avatar_success=Sua configuração de avatar foi atualizada com sucesso.
change_password=Mudança de senha change_password=Mudança de senha
@ -477,7 +471,7 @@ issues.closed_at=`fechado em <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`reaberto em <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`reaberto em <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`citou este problema em um commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`citou este problema em um commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.poster=Autor issues.poster=Autor
issues.admin=Administrador issues.collaborator=Colaboradores
issues.owner=Proprietário issues.owner=Proprietário
issues.sign_up_for_free=Cadastre-se gratuitamente issues.sign_up_for_free=Cadastre-se gratuitamente
issues.sign_in_require_desc=para participar nesta conversa. Já tem uma conta? <a href="%s">Faça login para comentar</a> issues.sign_in_require_desc=para participar nesta conversa. Já tem uma conta? <a href="%s">Faça login para comentar</a>
@ -494,6 +488,7 @@ issues.label_modify=Alteração de etiqueta
issues.label_deletion=Exclusão de etiqueta issues.label_deletion=Exclusão de etiqueta
issues.label_deletion_desc=Excluir uma etiqueta a retirará de todos os problemas que ela estiver marcando. Quer mesmo continuar? issues.label_deletion_desc=Excluir uma etiqueta a retirará de todos os problemas que ela estiver marcando. Quer mesmo continuar?
issues.label_deletion_success=A etiqueta foi excluída com sucesso! issues.label_deletion_success=A etiqueta foi excluída com sucesso!
issues.num_participants=%d participantes
pulls.new=Novo Pull Request pulls.new=Novo Pull Request
pulls.compare_changes=Comparar mudanças pulls.compare_changes=Comparar mudanças
@ -515,11 +510,11 @@ pulls.merged=Merge realizado
pulls.has_merged=Este pull request foi mesclado com sucesso! pulls.has_merged=Este pull request foi mesclado com sucesso!
pulls.data_broken=Dados deste pull request foram quebrados devido à deleção de informação do fork. pulls.data_broken=Dados deste pull request foram quebrados devido à deleção de informação do fork.
pulls.is_checking=A verificação do conflito ainda está em progresso, por favor recarregue a página em instantes. pulls.is_checking=A verificação do conflito ainda está em progresso, por favor recarregue a página em instantes.
pulls.can_auto_merge_desc=Este pull request foi mesclado automaticamente. pulls.can_auto_merge_desc=Este pull request pode ser mesclado automaticamente.
pulls.cannot_auto_merge_desc=Este pull request não pode ser mesclado automaticamente pois há conflitos. pulls.cannot_auto_merge_desc=Este pull request não pode ser mesclado automaticamente pois há conflitos.
pulls.cannot_auto_merge_helper=Por favor, mescle manualmente para resolver os conflitos. pulls.cannot_auto_merge_helper=Por favor, mescle manualmente para resolver os conflitos.
pulls.merge_pull_request=Merge Pull Request pulls.merge_pull_request=Merge Pull Request
pulls.open_unmerged_pull_exists=' Você não pode executar a operação de reabrir porque já existe uma solicitação de pull aberta (#%d) do mesmo repositório com as mesmas informações de merge e está esperando pelo merge.' pulls.open_unmerged_pull_exists=`Você não pode executar a operação de reabrir porque já existe uma solicitação de pull aberta (#%d) do mesmo repositório com as mesmas informações de merge e está esperando pelo merge.`
milestones.new=Novo marco milestones.new=Novo marco
milestones.open_tab=%d abertos milestones.open_tab=%d abertos
@ -557,6 +552,8 @@ wiki.save_page=Salvar página
wiki.last_commit_info=%s editou esta página %s wiki.last_commit_info=%s editou esta página %s
wiki.edit_page_button=Editar wiki.edit_page_button=Editar
wiki.new_page_button=Nova página wiki.new_page_button=Nova página
wiki.delete_page_button=Excluir Página
wiki.delete_page_notice_1=Isto irá apagar a página <code>"%s"</code>. Por favor, certifique-se.
wiki.page_already_exists=já existe uma página de wiki com o mesmo nome. wiki.page_already_exists=já existe uma página de wiki com o mesmo nome.
wiki.pages=Páginas wiki.pages=Páginas
wiki.last_updated=Última atualização %s wiki.last_updated=Última atualização %s
@ -568,7 +565,7 @@ settings.hooks=Webhooks
settings.githooks=Hooks do Git settings.githooks=Hooks do Git
settings.basic_settings=Configurações Básicas settings.basic_settings=Configurações Básicas
settings.site=Site Oficial settings.site=Site Oficial
settings.update_settings=Configurações de Atualização settings.update_settings=Atualizar configurações
settings.change_reponame_prompt=Este mudanças vai afetar os links para este repositório. settings.change_reponame_prompt=Este mudanças vai afetar os links para este repositório.
settings.advanced_settings=Configurações avançadas settings.advanced_settings=Configurações avançadas
settings.wiki_desc=Habilitar o wiki para permitir que as pessoas escrevam documentos settings.wiki_desc=Habilitar o wiki para permitir que as pessoas escrevam documentos
@ -581,14 +578,23 @@ settings.tracker_url_format=Formato de URL do issue tracker externo
settings.tracker_url_format_desc=Você pode usar o espaço reservado <code>{user} {repo} {index}</code> para o nome do usuário, índice de nome e a questão do repositório. settings.tracker_url_format_desc=Você pode usar o espaço reservado <code>{user} {repo} {index}</code> para o nome do usuário, índice de nome e a questão do repositório.
settings.pulls_desc=Habilitar pull requests para aceitar contribuições públicas settings.pulls_desc=Habilitar pull requests para aceitar contribuições públicas
settings.danger_zone=Zona de Perigo settings.danger_zone=Zona de Perigo
settings.new_owner_has_same_repo=O novo dono já tem um repositório com o mesmo nome. Por favor, escolha outro nome.
settings.convert=Converter para repositório tradicional
settings.convert_desc=Você pode converter este espelho em um repositório tradicional. Esta ação não pode ser revertida.
settings.convert_notices_1=- Esta operação vai converter este repositório espelho em um repositório tradicional. Esta ação não pode ser desfeita.
settings.convert_confirm=Confirmar conversão
settings.convert_succeed=Repositório espelho convertido para tradicional com sucesso.
settings.transfer=Transferir Propriedade settings.transfer=Transferir Propriedade
settings.transfer_desc=Transferir este repositório para outro usuário ou para uma organização onde você tem direitos de administrador. settings.transfer_desc=Transferir este repositório para outro usuário ou para uma organização onde você tem direitos de administrador.
settings.new_owner_has_same_repo=O novo dono já tem um repositório com o mesmo nome. Por favor, escolha outro nome.
settings.delete=Deletar Este Repositório
settings.delete_desc=Uma vez que você deleta um repositório, não tem volta. Por favor, tenha certeza.
settings.transfer_notices_1=- Você vai perder acesso se o novo dono for um usuário individual. settings.transfer_notices_1=- Você vai perder acesso se o novo dono for um usuário individual.
settings.transfer_notices_2=- Você vai continuar tendo acesso se o novo dono é uma organização e você é um dos membros. settings.transfer_notices_2=- Você vai continuar tendo acesso se o novo dono é uma organização e você é um dos membros.
settings.transfer_form_title=Informe a seguinte informação para confirmar a sua operação: settings.transfer_form_title=Informe a seguinte informação para confirmar a sua operação:
settings.wiki_delete=Apagar dados do Wiki
settings.wiki_delete_desc=Uma vez que você apague os dados da wiki, não há volta. Por favor, certifique-se.
settings.wiki_delete_notices_1=- Isso irá excluir e desativar o wiki para %s
settings.wiki_deletion_success=Dados de wiki do repositório foram deletados com sucesso.
settings.delete=Deletar Este Repositório
settings.delete_desc=Uma vez que você deleta um repositório, não tem volta. Por favor, tenha certeza.
settings.delete_notices_1=-Esta operação <strong>NÃO PODERÁ</strong> ser desfeita. settings.delete_notices_1=-Esta operação <strong>NÃO PODERÁ</strong> ser desfeita.
settings.delete_notices_2=- Esta operação irá apagar permanentemente o tudo deste repositório, incluindo os dados do Git, problemas, comentários e acessos dos colaboradores. settings.delete_notices_2=- Esta operação irá apagar permanentemente o tudo deste repositório, incluindo os dados do Git, problemas, comentários e acessos dos colaboradores.
settings.delete_notices_fork_1=- Se este repositório é público, todos os forks se tornarão independentes após a deleção. settings.delete_notices_fork_1=- Se este repositório é público, todos os forks se tornarão independentes após a deleção.
@ -602,8 +608,12 @@ settings.transfer_succeed=A posse do repositório foi transferido com sucesso.
settings.confirm_delete=Confirmar Deleção settings.confirm_delete=Confirmar Deleção
settings.add_collaborator=Adicionar um Novo Colaborador settings.add_collaborator=Adicionar um Novo Colaborador
settings.add_collaborator_success=O novo colaborador foi adicionado. settings.add_collaborator_success=O novo colaborador foi adicionado.
settings.delete_collaborator=Deletar
settings.collaborator_deletion=Exclusão de colaborador
settings.collaborator_deletion_desc=Este usuário não terá mais acesso de colaboração neste repositório após a deleção. Você quer continuar?
settings.remove_collaborator_success=O colaborador foi removido. settings.remove_collaborator_success=O colaborador foi removido.
settings.search_user_placeholder=Pesquisar usuário... settings.search_user_placeholder=Pesquisar usuário...
settings.org_not_allowed_to_be_collaborator=Organização não tem permissão para ser adicionada como um colaborador.
settings.user_is_org_member=O usuário é um membro da organização que não pode ser adicionado como um colaborador. settings.user_is_org_member=O usuário é um membro da organização que não pode ser adicionado como um colaborador.
settings.add_webhook=Adicionar Webhook settings.add_webhook=Adicionar Webhook
settings.hooks_desc=Hooks da web ou Webhooks permitem serviços externos serem notificados quando certos eventos acontecem no Gogs. Quando acontecem os eventos especificados, enviaremos uma solicitação POST para cada uma das URLs que você fornecer. Saiba mais no nosso <a target="_blank" href="%s"> Guia de Webhooks</a>. settings.hooks_desc=Hooks da web ou Webhooks permitem serviços externos serem notificados quando certos eventos acontecem no Gogs. Quando acontecem os eventos especificados, enviaremos uma solicitação POST para cada uma das URLs que você fornecer. Saiba mais no nosso <a target="_blank" href="%s"> Guia de Webhooks</a>.
@ -748,7 +758,7 @@ members.membership_visibility=Visibilidade da associação:
members.public=Público members.public=Público
members.public_helper=tornar privado members.public_helper=tornar privado
members.private=Privado members.private=Privado
members.private_helper=torar público members.private_helper=tornar público
members.member_role=Categoria de membro: members.member_role=Categoria de membro:
members.owner=Dono members.owner=Dono
members.member=Membro members.member=Membro
@ -818,6 +828,8 @@ dashboard.resync_all_sshkeys=Reescrever o arquivo '.ssh/authorized_keys' (atenç
dashboard.resync_all_sshkeys_success=Todas as chaves públicas foram reescritas com sucesso. dashboard.resync_all_sshkeys_success=Todas as chaves públicas foram reescritas com sucesso.
dashboard.resync_all_update_hooks=Reescrever todos os hooks de atualização dos repositórios (necessário quando o caminho de configuração customizado é alterado) dashboard.resync_all_update_hooks=Reescrever todos os hooks de atualização dos repositórios (necessário quando o caminho de configuração customizado é alterado)
dashboard.resync_all_update_hooks_success=Os hooks de atualização de todos os repositórios foram reescritos com sucesso. dashboard.resync_all_update_hooks_success=Os hooks de atualização de todos os repositórios foram reescritos com sucesso.
dashboard.reinit_missing_repos=Reinicializar todos os registros de repositório que perderam os arquivos do Git
dashboard.reinit_missing_repos_success=Todos os repositórios que perderam arquivos do Git foram reinicializados com sucesso.
dashboard.server_uptime=Servidor Ligado dashboard.server_uptime=Servidor Ligado
dashboard.current_goroutine=Goroutines Atuais dashboard.current_goroutine=Goroutines Atuais
@ -911,6 +923,7 @@ auths.attribute_username_placeholder=Deixe vazio para usar o valor do campo de f
auths.attribute_name=Atributo primeiro nome auths.attribute_name=Atributo primeiro nome
auths.attribute_surname=Atributo sobrenome auths.attribute_surname=Atributo sobrenome
auths.attribute_mail=Atributo e-mail auths.attribute_mail=Atributo e-mail
auths.attributes_in_bind=Buscar os atributos no contexto de Bind DN
auths.filter=Filtro de usuário auths.filter=Filtro de usuário
auths.admin_filter=Filtro de administrador auths.admin_filter=Filtro de administrador
auths.ms_ad_sa=Ms Ad SA auths.ms_ad_sa=Ms Ad SA
@ -932,6 +945,7 @@ auths.update=Atualizar a configuração da autenticação
auths.delete=Excluir esta autenticação auths.delete=Excluir esta autenticação
auths.delete_auth_title=Exclusão da autenticação auths.delete_auth_title=Exclusão da autenticação
auths.delete_auth_desc=Esta autenticação esta prestes a ser deletada, deseja continuar? auths.delete_auth_desc=Esta autenticação esta prestes a ser deletada, deseja continuar?
auths.still_in_used=Esta autenticação ainda é usada por alguns usuários. Por favor delete ou converta esses usuários para outro tipo de login primeiro.
auths.deletion_success=Autenticação deletada com sucesso! auths.deletion_success=Autenticação deletada com sucesso!
config.server_config=Configuração do Servidor config.server_config=Configuração do Servidor
@ -948,6 +962,19 @@ config.static_file_root_path=Caminho Raiz para Arquivo Estático
config.log_file_root_path=Caminho Raiz para Arquivo de Log config.log_file_root_path=Caminho Raiz para Arquivo de Log
config.script_type=Tipo de Script config.script_type=Tipo de Script
config.reverse_auth_user=Usuário de Autenticação Reversa config.reverse_auth_user=Usuário de Autenticação Reversa
config.ssh_config=Configuração de SSH
config.ssh_enabled=Habilitado
config.ssh_start_builtin_server=Iniciar servidor embutido
config.ssh_domain=Domínio
config.ssh_port=Porta
config.ssh_listen_port=Porta de escuta
config.ssh_root_path=Caminho da raiz
config.ssh_key_test_path=Caminho da chave de teste
config.ssh_keygen_path=Caminho do keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Verificar tamanho mínimo da chave
config.ssh_minimum_key_sizes=Tamanhos mínimos da chave
config.db_config=Configuração do Banco de Dados config.db_config=Configuração do Banco de Dados
config.db_type=Tipo config.db_type=Tipo
config.db_host=Host config.db_host=Host
@ -962,7 +989,6 @@ config.register_email_confirm=Requerer Confirmação de E-mail
config.disable_register=Desabilitar Registro config.disable_register=Desabilitar Registro
config.show_registration_button=Mostrar Botão de Registo config.show_registration_button=Mostrar Botão de Registo
config.require_sign_in_view=Requerer Entrar no Gogs para Ver config.require_sign_in_view=Requerer Entrar no Gogs para Ver
config.enable_cache_avatar=Habilitar Cache de Avatar
config.mail_notify=Notificação de Correio config.mail_notify=Notificação de Correio
config.disable_key_size_check=Desativar verificação de tamanho mínimo da chave config.disable_key_size_check=Desativar verificação de tamanho mínimo da chave
config.enable_captcha=Habilitar o Captcha config.enable_captcha=Habilitar o Captcha
@ -978,6 +1004,9 @@ config.mailer_disable_helo=Desabilitar HELO
config.mailer_name=Nome config.mailer_name=Nome
config.mailer_host=Host config.mailer_host=Host
config.mailer_user=Usuário config.mailer_user=Usuário
config.send_test_mail=Enviar email de teste
config.test_mail_failed=Falha ao enviar o email de teste para '%s': %v
config.test_mail_sent=O email de teste foi enviado para '%s'.
config.oauth_config=Configuração do OAuth config.oauth_config=Configuração do OAuth
config.oauth_enabled=Habilitado config.oauth_enabled=Habilitado
config.cache_config=Configuração de Cache config.cache_config=Configuração de Cache
@ -1028,9 +1057,13 @@ notices.delete_success=Avisos do sistema foram excluídos com sucesso.
create_repo=repositório criado <a href="%s"> %s</a> create_repo=repositório criado <a href="%s"> %s</a>
rename_repo=renomeou o o repositório <code>%[1]s</code> para <a href="%[2]s">%[3]s</a> rename_repo=renomeou o o repositório <code>%[1]s</code> para <a href="%[2]s">%[3]s</a>
commit_repo=pushed para <a href="%[1]s/src/%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a> commit_repo=pushed para <a href="%[1]s/src/%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a>
create_issue='questão aberta <a href="%s/issues/%s">%s#%[2]s</a>' create_issue=`questão aberta <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`questão fechada <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`questão reaberta <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`criou o pull request <a href="%s/pulls/%s">%s#%[2]s</a>` create_pull_request=`criou o pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue='comentou sobre a questão <a href="%s/issues/%s">%s#%[2]s</a>' close_pull_request=`fechou o pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`reabriu o pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue=`comentou sobre a questão <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request=`mesclou o pull request <a href="%s/pulls/%s">%s#%[2]s</a>` merge_pull_request=`mesclou o pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo=repositório transferido de <code>%s</code> para <a href="%s">%s</a> transfer_repo=repositório transferido de <code>%s</code> para <a href="%s">%s</a>
push_tag=Foi feito push na tag <a href="%s/src/%s">%[2]s</a> para <a href="%[1]s">%[3]s</a> push_tag=Foi feito push na tag <a href="%s/src/%s">%[2]s</a> para <a href="%[1]s">%[3]s</a>

149
conf/locale/locale_ru-RU.ini

@ -1,7 +1,7 @@
app_desc=Удобная служба для собственного Git-репозитория, написанная на языке Go app_desc=Удобная служба для собственного Git-репозитория
home=Главная home=Главная
dashboard=Панель мониторинга dashboard=Панель управления
explore=Обзор explore=Обзор
help=Помощь help=Помощь
sign_in=Войти sign_in=Войти
@ -14,14 +14,14 @@ page=Страница
template=Шаблон template=Шаблон
language=Язык language=Язык
create_new=Создать... create_new=Создать...
user_profile_and_more=Профиль и остальное user_profile_and_more=Профиль пользователя и прочее
signed_in_as=Вы вошли как signed_in_as=Вы вошли как
username=Имя пользователя username=Имя пользователя
email=Эл. почта email=Эл. почта
password=Пароль password=Пароль
re_type=Повтор re_type=Введите повторно
captcha=Captcha captcha=Капча
repository=Репозиторий repository=Репозиторий
organization=Организация organization=Организация
@ -29,28 +29,21 @@ mirror=Зеркало
new_repo=Новый репозиторий new_repo=Новый репозиторий
new_migrate=Новая Миграция new_migrate=Новая Миграция
new_mirror=Новое зеркало new_mirror=Новое зеркало
new_fork=Новый проект из репозитория new_fork=Новое ответвление репозитория
new_org=Новая организация new_org=Новая организация
manage_org=Управление организацией manage_org=Управление организациями
admin_panel=Панель администратора admin_panel=Панель администратора
account_settings=Настройки аккаунта account_settings=Настройки аккаунта
settings=Настройки settings=Настройки
your_profile=Ваш профиль your_profile=Ваш профиль
your_settings=Ваши настройки your_settings=Ваши настройки
news_feed=Лента новостей activities=Активность
pull_requests=Запросы на слияние pull_requests=Запросы на слияние
issues=Задачи issues=Задачи
cancel=Отмена cancel=Отмена
[search]
search=Поиск...
repository=Репозиторий
user=Пользователь
issue=Задача
code=Код
[install] [install]
install=Установка install=Установка
title=Установочные шаги для первого запуска title=Установочные шаги для первого запуска
@ -65,7 +58,7 @@ db_name=Имя базы данных
db_helper=Для MySQL используйте тип таблиц InnoDB с кодировкой utf8_general_ci. db_helper=Для MySQL используйте тип таблиц InnoDB с кодировкой utf8_general_ci.
ssl_mode=Режим SSL ssl_mode=Режим SSL
path=Путь path=Путь
sqlite_helper=Путь к файлу базы данных SQLite3 или TiDB. sqlite_helper=Путь к файлу базы данных SQLite3 или TiDB. <br>Укажите абсолютный путь при запуске в качестве службы.
err_empty_db_path=Путь к базе данных SQLite3 или TiDB не может быть пустым. err_empty_db_path=Путь к базе данных SQLite3 или TiDB не может быть пустым.
err_invalid_tidb_name=Имя базы данных TiDB не может содержать символы "." и "-". err_invalid_tidb_name=Имя базы данных TiDB не может содержать символы "." и "-".
no_admin_and_disable_registration=Вы не можете отключить регистрацию до создания учетной записи администратора. no_admin_and_disable_registration=Вы не можете отключить регистрацию до создания учетной записи администратора.
@ -86,6 +79,8 @@ http_port=Порт HTTP
http_port_helper=Номер порта, который приложение будет слушать. http_port_helper=Номер порта, который приложение будет слушать.
app_url=URL приложения app_url=URL приложения
app_url_helper=Этот параметр влияет на URL для клонирования по HTTP/HTTPS и на адреса в электронной почте. app_url_helper=Этот параметр влияет на URL для клонирования по HTTP/HTTPS и на адреса в электронной почте.
log_root_path=Путь к журналу
log_root_path_helper=Каталог для записи файлов журнала.
optional_title=Расширенные настройки optional_title=Расширенные настройки
email_title=Настройки службы электронной почты email_title=Настройки службы электронной почты
@ -122,6 +117,7 @@ run_user_not_match=Текущий пользователь не является
save_config_failed=Не удалось сохранить конфигурацию: %v save_config_failed=Не удалось сохранить конфигурацию: %v
invalid_admin_setting=Указан недопустимый параметр учетной записи администратора: %v invalid_admin_setting=Указан недопустимый параметр учетной записи администратора: %v
install_success=Добро пожаловать! Мы рады, что вы выбрали Gogs. Веселитесь и берегите себя. install_success=Добро пожаловать! Мы рады, что вы выбрали Gogs. Веселитесь и берегите себя.
invalid_log_root_path=Недопустимый путь для логов: %v
[home] [home]
uname_holder=Имя пользователь или E-mail uname_holder=Имя пользователь или E-mail
@ -129,7 +125,7 @@ password_holder=Пароль
switch_dashboard_context=Переключить контекст панели управления switch_dashboard_context=Переключить контекст панели управления
my_repos=Мои репозитории my_repos=Мои репозитории
collaborative_repos=Совместные репозитории collaborative_repos=Совместные репозитории
my_orgs=Моя Организация my_orgs=Мои организации
my_mirrors=Мои зеркала my_mirrors=Мои зеркала
view_home=Показать %s view_home=Показать %s
@ -137,6 +133,8 @@ issues.in_your_repos=В ваших репозиториях
[explore] [explore]
repos=Репозитории repos=Репозитории
users=Пользователи
search=Поиск
[auth] [auth]
create_new_account=Создать новый аккаунт create_new_account=Создать новый аккаунт
@ -150,7 +148,7 @@ forget_password=Забыли пароль?
sign_up_now=Нужен аккаунт? Зарегистрируйтесь. sign_up_now=Нужен аккаунт? Зарегистрируйтесь.
confirmation_mail_sent_prompt=Новое письмо для подтверждения было направлено на <b>%s</b>, пожалуйста, проверьте ваш почтовый ящик в течение %d часов для завершения регистрации. confirmation_mail_sent_prompt=Новое письмо для подтверждения было направлено на <b>%s</b>, пожалуйста, проверьте ваш почтовый ящик в течение %d часов для завершения регистрации.
active_your_account=Активируйте свой аккаунт active_your_account=Активируйте свой аккаунт
resent_limit_prompt=Вы слишком часто отправляете письмо с активацией. Подождите 3 минуты, пожалуйста. resent_limit_prompt=Извините, вы уже запросили активацию по электронной почте недавно. Пожалуйста, подождите 3 минуты, а затем повторите попытку.
has_unconfirmed_mail=Здравствуйте, %s! У вас есть неподтвержденный адрес электронной почты (<b>%s</b>). Если вам не приходило письмо с подтверждением или нужно выслать новое письмо, нажмите на кнопку ниже. has_unconfirmed_mail=Здравствуйте, %s! У вас есть неподтвержденный адрес электронной почты (<b>%s</b>). Если вам не приходило письмо с подтверждением или нужно выслать новое письмо, нажмите на кнопку ниже.
resend_mail=Нажмите здесь, чтобы переотправить активационное письмо resend_mail=Нажмите здесь, чтобы переотправить активационное письмо
email_not_associate=Этот адрес электронной почты не связан ни с одной учетной записью. email_not_associate=Этот адрес электронной почты не связан ни с одной учетной записью.
@ -192,24 +190,23 @@ size_error=` должен быть размер %s.`
min_size_error=«должен содержать по крайней мере %s символов.» min_size_error=«должен содержать по крайней мере %s символов.»
max_size_error=` должен содержать максимум %s символов.` max_size_error=` должен содержать максимум %s символов.`
email_error=«не является адресом электронной почты.» email_error=«не является адресом электронной почты.»
url_error=«не является допустимым URL-адресом.» url_error=` не является допустимым URL-адресом.`
include_error=` должен содержать '%s'` include_error=` должен содержать '%s'.`
unknown_error=Неизвестная ошибка: unknown_error=Неизвестная ошибка:
captcha_incorrect=CAPTCHA не совпадает. captcha_incorrect=Капча не пройдена.
password_not_match=Пароль и подтверждение пароля не совпадают. password_not_match=Пароли не совпадают.
username_been_taken=Имя пользователя уже принято. username_been_taken=Имя пользователя занято.
repo_name_been_taken=Имя репозитория уже принято. repo_name_been_taken=Имя репозитория занято.
org_name_been_taken=Название организации было уже принято. org_name_been_taken=Название организации занято.
team_name_been_taken=Название команды было уже принято. team_name_been_taken=Название команды занято.
email_been_used=Адрес электронной почты уже используется. email_been_used=Адрес электронной почты уже используется.
illegal_team_name=Имя группы содержит недопустимые знаки.
username_password_incorrect=Имя пользователя или пароль не правильный. username_password_incorrect=Имя пользователя или пароль не правильный.
enterred_invalid_repo_name=Пожалуйста, убедитесь, что введенно правильное имя хранилища. enterred_invalid_repo_name=Пожалуйста, убедитесь, что введено правильное имя репозитория.
enterred_invalid_owner_name=Убедитесь, что введенное имя владельца верное. enterred_invalid_owner_name=Убедитесь, что введенное имя владельца верное.
enterred_invalid_password=Убедитесь, что введенный пароль верен. enterred_invalid_password=Убедитесь, что введенный пароль верен.
user_not_exist=Данный пользователь не существует. user_not_exist=Данный пользователь не существует.
last_org_owner=Удаляемый пользователь является последним в команде владельцев. Должен быть хотя бы один владелец. last_org_owner=Удаление последнего пользователя из команды владельцев невозможно, поскольку всегда должен быть хотя бы один владелец в любой организации.
invalid_ssh_key=К сожалению, мы не смогли проверить ваш SSH-ключ: %s invalid_ssh_key=К сожалению, мы не смогли проверить ваш SSH-ключ: %s
unable_verify_ssh_key=Gogs не может проверить ваш SSH-ключ, но мы допускаем, что он действителен. Пожалуйста, удостоверьтесь самостоятельно, что ключ действителен. unable_verify_ssh_key=Gogs не может проверить ваш SSH-ключ, но мы допускаем, что он действителен. Пожалуйста, удостоверьтесь самостоятельно, что ключ действителен.
@ -219,8 +216,6 @@ still_own_repo=На вашем аккаунте все еще остается
still_has_org=Вы находитесь в организации, сперва Вам необходимо покинуть ее или удалить. still_has_org=Вы находитесь в организации, сперва Вам необходимо покинуть ее или удалить.
org_still_own_repo=Данная организация все еще является владельцем репозиториев, необходимо удалить или переместить их в начале. org_still_own_repo=Данная организация все еще является владельцем репозиториев, необходимо удалить или переместить их в начале.
still_own_user=Эта проверка подлинности по-прежнему используется некоторыми пользователями, вы должны переместить их и затем снова удалить.
target_branch_not_exist=Целевая ветка не существует target_branch_not_exist=Целевая ветка не существует
[user] [user]
@ -262,11 +257,10 @@ continue=Далее
cancel=Отмена cancel=Отмена
enable_custom_avatar=Включить собственный аватар enable_custom_avatar=Включить собственный аватар
enable_custom_avatar_helper=Включите эту опцию, чтоб отключить загрузку с Gravatar
choose_new_avatar=Выбрать новый аватар choose_new_avatar=Выбрать новый аватар
update_avatar=Обновить настройку аватара update_avatar=Обновить настройку аватара
delete_current_avatar=Удалить текущий аватар
uploaded_avatar_not_a_image=Загружаемый файл не является изображением. uploaded_avatar_not_a_image=Загружаемый файл не является изображением.
no_custom_avatar_available=Собственный аватар недоступен, включить его невозможно.
update_avatar_success=Настройка вашего аватара обновлена успешно. update_avatar_success=Настройка вашего аватара обновлена успешно.
change_password=Сменить пароль change_password=Сменить пароль
@ -324,7 +318,7 @@ token_name=Имя маркера
generate_token=Генерировать маркер generate_token=Генерировать маркер
generate_token_succees=Успешно создан новый токен доступа! Пожалуйста сделайте копию вашего нового токена персонального доступа. Вы не сможете увидеть его снова! generate_token_succees=Успешно создан новый токен доступа! Пожалуйста сделайте копию вашего нового токена персонального доступа. Вы не сможете увидеть его снова!
delete_token=Удалить delete_token=Удалить
access_token_deletion=Удаление персонального токена доступа access_token_deletion=Удаление личного токена доступа
access_token_deletion_desc=Удаление этого персонального токена доступа приведет к удалению всех связанных прав доступа к приложению. Вы хотите продолжить? access_token_deletion_desc=Удаление этого персонального токена доступа приведет к удалению всех связанных прав доступа к приложению. Вы хотите продолжить?
delete_token_success=Персональный токен доступа успешно удален! Не забудьте изменить настройки вашего приложения. delete_token_success=Персональный токен доступа успешно удален! Не забудьте изменить настройки вашего приложения.
@ -341,7 +335,7 @@ repo_name_helper=Лучшие названия репозиториев коро
visibility=Видимость visibility=Видимость
visiblity_helper=<span class="ui red text">Личный</span> репозиторий visiblity_helper=<span class="ui red text">Личный</span> репозиторий
visiblity_helper_forced=Все новые репозитории являются <span class="ui red text">Личными</span> по желанию администратора сайта visiblity_helper_forced=Все новые репозитории являются <span class="ui red text">Личными</span> по желанию администратора сайта
visiblity_fork_helper=(Изменение этого значения затронет все форки) visiblity_fork_helper=(Изменение этого значения затронет все ответвления)
clone_helper=Нужна помощь в клонировании? Посетите страницу <a target="_blank" href="%s">помощи</a>! clone_helper=Нужна помощь в клонировании? Посетите страницу <a target="_blank" href="%s">помощи</a>!
fork_repo=Ответвить репозиторий fork_repo=Ответвить репозиторий
fork_from=Ответвление от fork_from=Ответвление от
@ -361,7 +355,7 @@ mirror_address=Адрес зеркала
mirror_address_desc=Укажите необходимые учетные данные в адрес. mirror_address_desc=Укажите необходимые учетные данные в адрес.
watchers=Наблюдатели watchers=Наблюдатели
stargazers=Звездочеты stargazers=Звездочеты
forks=Форки forks=Ответвления
form.reach_limit_of_creation=У владельца достигнут максимальный предел в %d создаваемых репозиториев. form.reach_limit_of_creation=У владельца достигнут максимальный предел в %d создаваемых репозиториев.
form.name_reserved=Имя репозитория '%s' зарезервировано. form.name_reserved=Имя репозитория '%s' зарезервировано.
@ -378,8 +372,8 @@ migrate.invalid_local_path=Недопустимый локальный путь.
migrate.failed=Миграция не удалась: %v migrate.failed=Миграция не удалась: %v
mirror_from=зеркало из mirror_from=зеркало из
forked_from=форк от forked_from=ответвлено от
fork_from_self=Вы не можете форкнуть репозитарий, так как Вы уже его владелец! fork_from_self=Вы не можете ответвить репозиторий, так как Вы уже его владелец!
copy_link=Скопировать copy_link=Скопировать
copy_link_success=Скопировано! copy_link_success=Скопировано!
copy_link_error=Нажмите ⌘-C или Ctrl-C для копирования copy_link_error=Нажмите ⌘-C или Ctrl-C для копирования
@ -440,7 +434,7 @@ issues.new_label=Новая метка
issues.new_label_placeholder=Имя метки... issues.new_label_placeholder=Имя метки...
issues.create_label=Добавить метку issues.create_label=Добавить метку
issues.open_tab=%d открыто(ы) issues.open_tab=%d открыто(ы)
issues.close_tab=%d Закрыть issues.close_tab=%d закрыто(ы)
issues.filter_label=Метка issues.filter_label=Метка
issues.filter_label_no_select=Нет выбранной метки issues.filter_label_no_select=Нет выбранной метки
issues.filter_milestone=Этап issues.filter_milestone=Этап
@ -477,7 +471,7 @@ issues.closed_at=`закрыл <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`открыл снова <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`открыл снова <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`упомянул эту задачу в коммите <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`упомянул эту задачу в коммите <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.poster=Автор issues.poster=Автор
issues.admin=Администратор issues.collaborator=Соавтор
issues.owner=Владелец issues.owner=Владелец
issues.sign_up_for_free=Зарегистрируйтесь бесплатно issues.sign_up_for_free=Зарегистрируйтесь бесплатно
issues.sign_in_require_desc=чтобы присоединиться к обсуждению. Уже есть аккаунт? <a href="%s">Войдите чтобы прокомментировать</a> issues.sign_in_require_desc=чтобы присоединиться к обсуждению. Уже есть аккаунт? <a href="%s">Войдите чтобы прокомментировать</a>
@ -494,6 +488,7 @@ issues.label_modify=Изменение метки
issues.label_deletion=Удаление метки issues.label_deletion=Удаление метки
issues.label_deletion_desc=Удаление ярлыка затронет все связанные задачи. Продолжить? issues.label_deletion_desc=Удаление ярлыка затронет все связанные задачи. Продолжить?
issues.label_deletion_success=Метка была удалена успешно! issues.label_deletion_success=Метка была удалена успешно!
issues.num_participants=%d участников
pulls.new=Новый запрос на слияние pulls.new=Новый запрос на слияние
pulls.compare_changes=Сравнить изменения pulls.compare_changes=Сравнить изменения
@ -503,7 +498,7 @@ pulls.compare_compare=сравнить
pulls.filter_branch=Фильтр по ветке pulls.filter_branch=Фильтр по ветке
pulls.no_results=Результатов не найдено. pulls.no_results=Результатов не найдено.
pulls.nothing_to_compare=Нечего сравнивать, родительская и текущая ветка одинаковые. pulls.nothing_to_compare=Нечего сравнивать, родительская и текущая ветка одинаковые.
pulls.has_pull_request=`Уже существует пулл-реквест между двумя целями <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>` pulls.has_pull_request=`Уже существует запрос на слияние между двумя целями: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
pulls.create=Создать запрос на слияние pulls.create=Создать запрос на слияние
pulls.title_desc=хочет смерджить %[1]d коммит(ов) из <code>%[2]s</code> в <code>%[3]s</code> pulls.title_desc=хочет смерджить %[1]d коммит(ов) из <code>%[2]s</code> в <code>%[3]s</code>
pulls.merged_title_desc=слито %[1]d коммит(ов) из <code>%[2]s</code> в <code>%[3]s</code> %[4]s pulls.merged_title_desc=слито %[1]d коммит(ов) из <code>%[2]s</code> в <code>%[3]s</code> %[4]s
@ -513,7 +508,7 @@ pulls.tab_files=Измененные файлы
pulls.reopen_to_merge=Пожалуйста снова откройте этот запрос для слияния. pulls.reopen_to_merge=Пожалуйста снова откройте этот запрос для слияния.
pulls.merged=Слито pulls.merged=Слито
pulls.has_merged=Слияние этого запроса успешно завершено! pulls.has_merged=Слияние этого запроса успешно завершено!
pulls.data_broken=Содержимое этого пулл-реквеста было нарушено, вследствии удаления или клонирования информации. pulls.data_broken=Содержимое этого запроса было нарушено вследствие удаления информации ответвления.
pulls.is_checking=Продолжается проверка конфликтов, пожалуйста обновите страницу несколько позже. pulls.is_checking=Продолжается проверка конфликтов, пожалуйста обновите страницу несколько позже.
pulls.can_auto_merge_desc=Этот запрос на слияние может быть объединён автоматически. pulls.can_auto_merge_desc=Этот запрос на слияние может быть объединён автоматически.
pulls.cannot_auto_merge_desc=Этот запрос на слияние не может быть объединён автоматически. pulls.cannot_auto_merge_desc=Этот запрос на слияние не может быть объединён автоматически.
@ -547,7 +542,7 @@ milestones.deletion_success=Контрольная точка успешно у
wiki=Вики wiki=Вики
wiki.welcome=Добро пожаловать в Вики! wiki.welcome=Добро пожаловать в Вики!
wiki.welcome_desc=Вики это место, где вы хотели бы документировать проект вместе и сделать его лучше. wiki.welcome_desc=Вики это место, где вы можете документировать проект вместе и сделать его лучше.
wiki.create_first_page=Создать первую страницу wiki.create_first_page=Создать первую страницу
wiki.page=Страница wiki.page=Страница
wiki.filter_page=Фильтр страницы wiki.filter_page=Фильтр страницы
@ -557,6 +552,8 @@ wiki.save_page=Сохранить страницу
wiki.last_commit_info=%s редактировал эту страницу %s wiki.last_commit_info=%s редактировал эту страницу %s
wiki.edit_page_button=Редактировать wiki.edit_page_button=Редактировать
wiki.new_page_button=Новая страница wiki.new_page_button=Новая страница
wiki.delete_page_button=Удалить страницу
wiki.delete_page_notice_1=Будьте внимательны! Это приведет к удалению страницы <code>«%s»</code>.
wiki.page_already_exists=Вики-страница с таким именем уже существует. wiki.page_already_exists=Вики-страница с таким именем уже существует.
wiki.pages=Страницы wiki.pages=Страницы
wiki.last_updated=Последнее обновление %s wiki.last_updated=Последнее обновление %s
@ -573,7 +570,7 @@ settings.change_reponame_prompt=Это изменение повлияет на
settings.advanced_settings=Расширенные настройки settings.advanced_settings=Расширенные настройки
settings.wiki_desc=Включить Вики, чтобы позволить людям писать документы settings.wiki_desc=Включить Вики, чтобы позволить людям писать документы
settings.use_external_wiki=Использовать внешнюю Wiki settings.use_external_wiki=Использовать внешнюю Wiki
settings.external_wiki_url=URL-адрес внешней вики settings.external_wiki_url=URL-адрес внешней Вики
settings.external_wiki_url_desc=Посетители будут перенаправлены на URL-адрес, когда они кликнут по вкладке. settings.external_wiki_url_desc=Посетители будут перенаправлены на URL-адрес, когда они кликнут по вкладке.
settings.issues_desc=Включить встроенную, легковесную систему отслеживания ошибок settings.issues_desc=Включить встроенную, легковесную систему отслеживания ошибок
settings.use_external_issue_tracker=Использовать внешнюю систему отслеживания ошибок settings.use_external_issue_tracker=Использовать внешнюю систему отслеживания ошибок
@ -581,19 +578,28 @@ settings.tracker_url_format=Внешний формат ссылки систе
settings.tracker_url_format_desc=Вы можете использовать шаблон <code>{user} {repo} {index}</code> для имени пользователя, репозитория и номера задачи. settings.tracker_url_format_desc=Вы можете использовать шаблон <code>{user} {repo} {index}</code> для имени пользователя, репозитория и номера задачи.
settings.pulls_desc=Включить публичные запросы на слияние settings.pulls_desc=Включить публичные запросы на слияние
settings.danger_zone=Опасная зона settings.danger_zone=Опасная зона
settings.new_owner_has_same_repo=У нового владельца уже есть хранилище с таким названием.
settings.convert=Преобразовать в обычный репозиторий
settings.convert_desc=Это зеркало можно преобразовать в обычный репозиторий. Это не может быть отменено.
settings.convert_notices_1=- Эта операция преобразует это зеркало в обычный репозиторий, и она не может быть отменена.
settings.convert_confirm=Подтвердите преобразование
settings.convert_succeed=Репозиторий был успешно преобразован в обычный.
settings.transfer=Передать права собственности settings.transfer=Передать права собственности
settings.transfer_desc=Передать репозиторий другому пользователю или организации где у вас есть права администратора. settings.transfer_desc=Передать репозиторий другому пользователю или организации где у вас есть права администратора.
settings.new_owner_has_same_repo=У нового владельца уже есть хранилище с таким названием.
settings.delete=Удалить этот репозиторий
settings.delete_desc=Как только вы удалите репозиторий — пути назад не будет. Удостоверьтесь, что вам это точно нужно.
settings.transfer_notices_1=- Вы можете потерять доступ, если новый владелец является отдельным пользователем. settings.transfer_notices_1=- Вы можете потерять доступ, если новый владелец является отдельным пользователем.
settings.transfer_notices_2=- Вы сохраните доступ, если новым владельцем станет организация, владельцем которой вы являетесь. settings.transfer_notices_2=- Вы сохраните доступ, если новым владельцем станет организация, владельцем которой вы являетесь.
settings.transfer_form_title=Введите сопутствующую информацию для подтверждения операции: settings.transfer_form_title=Введите сопутствующую информацию для подтверждения операции:
settings.wiki_delete=Стереть данные Вики
settings.wiki_delete_desc=Будьте внимательны! Как только вы удалите Вики — пути назад не будет.
settings.wiki_delete_notices_1=-Это будет удалено и отключит Вики для %s
settings.wiki_deletion_success=Данные Вики успешно стерты.
settings.delete=Удалить этот репозиторий
settings.delete_desc=Будьте внимательны! Как только вы удалите репозиторий — пути назад не будет.
settings.delete_notices_1=- Эта операция <strong>НЕ МОЖЕТ</strong> быть отменена. settings.delete_notices_1=- Эта операция <strong>НЕ МОЖЕТ</strong> быть отменена.
settings.delete_notices_2=- Эта операция навсегда удалит всё из этого репозитория, включая данные Git, связанные с ним задачи, комментарии и права доступа для сотрудников. settings.delete_notices_2=- Эта операция навсегда удалит всё из этого репозитория, включая данные Git, связанные с ним задачи, комментарии и права доступа для сотрудников.
settings.delete_notices_fork_1=- Если данный репозиторий является публичным, все склонированные репозитории останутся независимыми, после его удаления. settings.delete_notices_fork_1=- Если данный репозиторий является публичным, все склонированные репозитории останутся независимыми, после его удаления.
settings.delete_notices_fork_2=- Если данный репозиторий является приватным, все его форки будут удалены вместе с ним. settings.delete_notices_fork_2=- Если данный репозиторий является приватным, все его ответвления будут удалены вместе с ним.
settings.delete_notices_fork_3=- Если вы хотите сохранить все форки после удаления репозитория, то сначала сделайте его публичным. settings.delete_notices_fork_3=- Если вы хотите сохранить все ответвления после удаления репозитория, то сначала сделайте его публичным.
settings.deletion_success=Репозиторий был успешно удалён! settings.deletion_success=Репозиторий был успешно удалён!
settings.update_settings_success=Настройка репозитория обновлена успешно. settings.update_settings_success=Настройка репозитория обновлена успешно.
settings.transfer_owner=Новый владелец settings.transfer_owner=Новый владелец
@ -602,8 +608,12 @@ settings.transfer_succeed=Владение репозиторием было у
settings.confirm_delete=Подтвердить удаление settings.confirm_delete=Подтвердить удаление
settings.add_collaborator=Добавить нового соавтора settings.add_collaborator=Добавить нового соавтора
settings.add_collaborator_success=Был добавлен новый соавтор. settings.add_collaborator_success=Был добавлен новый соавтор.
settings.delete_collaborator=Удалить
settings.collaborator_deletion=Удаление соавтора
settings.collaborator_deletion_desc=Этот пользователь больше не будет иметь доступа для совместной работы в этом репозитории после удаления. Вы хотите продолжить?
settings.remove_collaborator_success=Соавтор был удален. settings.remove_collaborator_success=Соавтор был удален.
settings.search_user_placeholder=Поиск пользователя... settings.search_user_placeholder=Поиск пользователя...
settings.org_not_allowed_to_be_collaborator=Организации не могут быть добавлены как соавторы.
settings.user_is_org_member=Пользователь является членом организации, члены которой не могут быть добавлены в качестве соавтора. settings.user_is_org_member=Пользователь является членом организации, члены которой не могут быть добавлены в качестве соавтора.
settings.add_webhook=Добавить Webhook settings.add_webhook=Добавить Webhook
settings.hooks_desc=Webhooks позволяют внешним службам получать уведомления при возникновении определенных событий на Gogs. При возникновении указанных событий мы отправим запрос POST на каждый заданный вами URL. Узнать больше можно в нашем <a target="_blank" href="%s">Руководстве по Webhooks</a>. settings.hooks_desc=Webhooks позволяют внешним службам получать уведомления при возникновении определенных событий на Gogs. При возникновении указанных событий мы отправим запрос POST на каждый заданный вами URL. Узнать больше можно в нашем <a target="_blank" href="%s">Руководстве по Webhooks</a>.
@ -677,7 +687,7 @@ diff.view_file=Просмотреть файл
release.releases=Релизы release.releases=Релизы
release.new_release=Новый релиз release.new_release=Новый релиз
release.draft=Черновик release.draft=Черновик
release.prerelease=Предрелиз release.prerelease=Пре-релиз
release.stable=Стабильный release.stable=Стабильный
release.edit=Редактировать release.edit=Редактировать
release.ahead=<strong>%d</strong> коммитов %s начиная с этого релиза release.ahead=<strong>%d</strong> коммитов %s начиная с этого релиза
@ -700,9 +710,9 @@ release.save_draft=Сохранить черновик
release.edit_release=Редактировать релиз release.edit_release=Редактировать релиз
release.delete_release=Удалить этот релиз release.delete_release=Удалить этот релиз
release.deletion=Удаление релиза release.deletion=Удаление релиза
release.deletion_desc=Удаление данного релиза так же удалит все относящиеся к нему Git теги. Продолжить? release.deletion_desc=Удаление этого релиза удалит соответствующую Git метку. Вы хотите продолжить?
release.deletion_success=Релиз был успешно удален! release.deletion_success=Релиз был успешно удален!
release.tag_name_already_exist=Релиз с этим именем тега уже существует. release.tag_name_already_exist=Релиз с этим именем метки уже существует.
release.downloads=Загрузки release.downloads=Загрузки
[org] [org]
@ -818,6 +828,8 @@ dashboard.resync_all_sshkeys=Переписать файл «.ssh/authorized_key
dashboard.resync_all_sshkeys_success=Были успешно переписаны все открытые ключи. dashboard.resync_all_sshkeys_success=Были успешно переписаны все открытые ключи.
dashboard.resync_all_update_hooks=Перезаписать все апдейт-хуки этого репозитория (необходимо, когда изменен путь до папки конфигураций) dashboard.resync_all_update_hooks=Перезаписать все апдейт-хуки этого репозитория (необходимо, когда изменен путь до папки конфигураций)
dashboard.resync_all_update_hooks_success=Апдейт-хуки всех репозиториев успешно перезаписаны. dashboard.resync_all_update_hooks_success=Апдейт-хуки всех репозиториев успешно перезаписаны.
dashboard.reinit_missing_repos=Реинициализировать все репозитории с утерянными Git файлами
dashboard.reinit_missing_repos_success=Все репозитории с утерянными Git файлами успешно реинициализированы.
dashboard.server_uptime=Время непрерывной работы сервера dashboard.server_uptime=Время непрерывной работы сервера
dashboard.current_goroutine=Текущий Goroutines dashboard.current_goroutine=Текущий Goroutines
@ -911,6 +923,7 @@ auths.attribute_username_placeholder=Оставьте пустым, чтобы
auths.attribute_name=Имя аттрибута auths.attribute_name=Имя аттрибута
auths.attribute_surname=Фамилия аттрибута auths.attribute_surname=Фамилия аттрибута
auths.attribute_mail=Электронная почта аттрибута auths.attribute_mail=Электронная почта аттрибута
auths.attributes_in_bind=Извлечение атрибутов в виде Bind DN
auths.filter=Фильтр пользователя auths.filter=Фильтр пользователя
auths.admin_filter=Фильтр администратора auths.admin_filter=Фильтр администратора
auths.ms_ad_sa=Ms Ad SA auths.ms_ad_sa=Ms Ad SA
@ -932,6 +945,7 @@ auths.update=Обновить параметры аутентификации
auths.delete=Удалить этот канал аутентификации auths.delete=Удалить этот канал аутентификации
auths.delete_auth_title=Удаление канала аутентификации auths.delete_auth_title=Удаление канала аутентификации
auths.delete_auth_desc=Этот канал аутентификации будет удален. Вы уверены что хотите продолжить? auths.delete_auth_desc=Этот канал аутентификации будет удален. Вы уверены что хотите продолжить?
auths.still_in_used=Эта проверка подлинности до сих пор используется некоторыми пользователями, удалите или преобразуйте этих пользователей в другой тип входа в систему.
auths.deletion_success=Канал аутентификации успешно удален! auths.deletion_success=Канал аутентификации успешно удален!
config.server_config=Конфигурация сервера config.server_config=Конфигурация сервера
@ -948,6 +962,19 @@ config.static_file_root_path=Статичный путь до файла
config.log_file_root_path=Путь до папки с логами config.log_file_root_path=Путь до папки с логами
config.script_type=Тип сценария config.script_type=Тип сценария
config.reverse_auth_user=Заголовок с именем пользователя для авторизации на reverse proxy config.reverse_auth_user=Заголовок с именем пользователя для авторизации на reverse proxy
config.ssh_config=Конфигурация SSH
config.ssh_enabled=Включено
config.ssh_start_builtin_server=Запустить встроенный сервер
config.ssh_domain=Домен
config.ssh_port=Порт
config.ssh_listen_port=Прослушиваемый порт
config.ssh_root_path=Корневой путь
config.ssh_key_test_path=Путь к тестовому ключу
config.ssh_keygen_path=Путь к генератору ключей ('ssh-keygen')
config.ssh_minimum_key_size_check=Минимальный размер ключа проверки
config.ssh_minimum_key_sizes=Минимальные размеры ключа
config.db_config=Конфигурация базы данных config.db_config=Конфигурация базы данных
config.db_type=Тип config.db_type=Тип
config.db_host=Хост config.db_host=Хост
@ -962,7 +989,6 @@ config.register_email_confirm=Требуется подтверждение по
config.disable_register=Отключить регистрацию config.disable_register=Отключить регистрацию
config.show_registration_button=Показать кнопку регистрации config.show_registration_button=Показать кнопку регистрации
config.require_sign_in_view=Для просмотра необходима авторизация config.require_sign_in_view=Для просмотра необходима авторизация
config.enable_cache_avatar=Кешировать аватар
config.mail_notify=Почтовые уведомления config.mail_notify=Почтовые уведомления
config.disable_key_size_check=Отключить проверку на минимальный размер ключа config.disable_key_size_check=Отключить проверку на минимальный размер ключа
config.enable_captcha=Включить капчу config.enable_captcha=Включить капчу
@ -978,6 +1004,9 @@ config.mailer_disable_helo=Отключить HELO
config.mailer_name=Имя config.mailer_name=Имя
config.mailer_host=Сервер config.mailer_host=Сервер
config.mailer_user=Пользователь config.mailer_user=Пользователь
config.send_test_mail=Отправить тестовое письмо
config.test_mail_failed=Не удалось отправить тестовое письмо «%s»: %v
config.test_mail_sent=Тестовое письмо было отправлено «%s».
config.oauth_config=Конфигурация OAuth config.oauth_config=Конфигурация OAuth
config.oauth_enabled=Включено config.oauth_enabled=Включено
config.cache_config=Настройки кеша config.cache_config=Настройки кеша
@ -1025,15 +1054,19 @@ notices.op=Op.
notices.delete_success=Системное уведомление успешно удалено. notices.delete_success=Системное уведомление успешно удалено.
[action] [action]
create_repo=создал репозиторий <a href="%s"> %s</a> create_repo=создал(а) репозиторий <a href="%s"> %s</a>
rename_repo=репозиторий переименован из <code>%[1]s</code>на <a href="%[2]s">%[3]s</a> rename_repo=переименовал(а) репозиторий из <code>%[1]s</code> на <a href="%[2]s">%[3]s</a>
commit_repo=запушил <a href="%[1]s/src/%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a> commit_repo=запушил(а) <a href="%[1]s/src/%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a>
create_issue=`открыл(а) задачу <a href="%s/issues/%s">%s#%[2]s</a>` create_issue=`открыл(а) задачу <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`созданный пулл-реквест <a href="%s/pulls/%s">%s#%[2]s</a>` close_issue=`закрыл(а) задачу <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`возобновил(а) задачу <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`создал запрос на слияние <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request=`закрыл запрос на слияние <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`открыл снова запрос на слияние <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue=`прокомментировал(а) вопрос <a href="%s/issues/%s">%s#%[2]s</a>` comment_issue=`прокомментировал(а) вопрос <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request=`слил пул реквест <a href="%s/pulls/%s">%s#%[2]s</a>` merge_pull_request=`слил пул реквест <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo=перенес репозиторий <code>%s</code> в <a href="%s">%s</a> transfer_repo=перенес репозиторий <code>%s</code> в <a href="%s">%s</a>
push_tag=запушил тэг <a href="%s/src/%s">%[2]s</a> в <a href="%[1]s">%[3]s</a> push_tag=запушил(а) метку <a href="%s/src/%s">%[2]s</a> в <a href="%[1]s">%[3]s</a>
compare_commits=Просмотр сравнение для этих %d коммитов compare_commits=Просмотр сравнение для этих %d коммитов
[tool] [tool]

73
conf/locale/locale_zh-CN.ini

@ -38,19 +38,12 @@ settings=帐户设置
your_profile=个人信息 your_profile=个人信息
your_settings=用户设置 your_settings=用户设置
news_feed=最新活动 activities=最近活动
pull_requests=合并请求 pull_requests=合并请求
issues=工单管理 issues=工单管理
cancel=取消 cancel=取消
[search]
search=搜索...
repository=仓库
user=用户
issue=工单
code=代码
[install] [install]
install=安装页面 install=安装页面
title=首次运行安装程序 title=首次运行安装程序
@ -65,7 +58,7 @@ db_name=数据库名称
db_helper=如果您使用 MySQL,请使用 INNODB 引擎以及 utf8_general_ci 字符集。 db_helper=如果您使用 MySQL,请使用 INNODB 引擎以及 utf8_general_ci 字符集。
ssl_mode=SSL 模式 ssl_mode=SSL 模式
path=数据库文件路径 path=数据库文件路径
sqlite_helper=SQLite3 或 TiDB 数据库路径。 sqlite_helper=SQLite3 或 TiDB 数据库文件路径。<br>作为服务启动时,请使用绝对路径。
err_empty_db_path=SQLite3 或 TiDB 的数据库路径不能为空。 err_empty_db_path=SQLite3 或 TiDB 的数据库路径不能为空。
err_invalid_tidb_name=TiDB 数据库名称不允许包含字符 "." 或 "-" 。 err_invalid_tidb_name=TiDB 数据库名称不允许包含字符 "." 或 "-" 。
no_admin_and_disable_registration=您不能够在未创建管理员用户的情况下禁止注册。 no_admin_and_disable_registration=您不能够在未创建管理员用户的情况下禁止注册。
@ -86,6 +79,8 @@ http_port=HTTP 端口号
http_port_helper=应用监听的端口号 http_port_helper=应用监听的端口号
app_url=应用 URL app_url=应用 URL
app_url_helper=该设置影响 HTTP/HTTPS 克隆地址和一些邮箱中的链接。 app_url_helper=该设置影响 HTTP/HTTPS 克隆地址和一些邮箱中的链接。
log_root_path=日志路径
log_root_path_helper=存放日志文件的目录
optional_title=可选设置 optional_title=可选设置
email_title=邮件服务设置 email_title=邮件服务设置
@ -122,6 +117,7 @@ run_user_not_match=运行系统用户非当前用户:%s -> %s
save_config_failed=应用配置保存失败:%v save_config_failed=应用配置保存失败:%v
invalid_admin_setting=管理员帐户设置不正确:%v invalid_admin_setting=管理员帐户设置不正确:%v
install_success=您好!我们很高兴您选择使用 Gogs,祝您使用愉快,代码从此无 BUG! install_success=您好!我们很高兴您选择使用 Gogs,祝您使用愉快,代码从此无 BUG!
invalid_log_root_path=无效的日志路径:%v
[home] [home]
uname_holder=用户名或邮箱 uname_holder=用户名或邮箱
@ -136,7 +132,9 @@ view_home=访问 %s
issues.in_your_repos=属于该用户仓库的 issues.in_your_repos=属于该用户仓库的
[explore] [explore]
repos=探索仓库 repos=仓库
users=用户
search=搜索
[auth] [auth]
create_new_account=创建帐户 create_new_account=创建帐户
@ -203,7 +201,6 @@ repo_name_been_taken=仓库名称已经被占用。
org_name_been_taken=组织名称已经被占用。 org_name_been_taken=组织名称已经被占用。
team_name_been_taken=团队名称已经被占用。 team_name_been_taken=团队名称已经被占用。
email_been_used=邮箱地址已经被使用。 email_been_used=邮箱地址已经被使用。
illegal_team_name=团队名称包含非法字符。
username_password_incorrect=用户名或密码不正确。 username_password_incorrect=用户名或密码不正确。
enterred_invalid_repo_name=请检查您输入的仓库名称是正确。 enterred_invalid_repo_name=请检查您输入的仓库名称是正确。
enterred_invalid_owner_name=请检查您输入的新所有者用户名是否正确。 enterred_invalid_owner_name=请检查您输入的新所有者用户名是否正确。
@ -219,8 +216,6 @@ still_own_repo=您的帐户仍然是某些仓库的拥有者,您必须先转
still_has_org=您的帐户仍旧是某些组织的成员,您必须先离开或删除组织。 still_has_org=您的帐户仍旧是某些组织的成员,您必须先离开或删除组织。
org_still_own_repo=该组织仍然是某些仓库的拥有者,您必须先转移或删除它们才能执行删除组织操作! org_still_own_repo=该组织仍然是某些仓库的拥有者,您必须先转移或删除它们才能执行删除组织操作!
still_own_user=该授权认证依旧被部分用户使用,请先删除该部分用户后再试!
target_branch_not_exist=目标分支不存在。 target_branch_not_exist=目标分支不存在。
[user] [user]
@ -262,11 +257,10 @@ continue=继续操作
cancel=取消操作 cancel=取消操作
enable_custom_avatar=启动自定义头像 enable_custom_avatar=启动自定义头像
enable_custom_avatar_helper=激活该选项来禁止从 Gravatar 获取头像
choose_new_avatar=选择新的头像 choose_new_avatar=选择新的头像
update_avatar=更新头像设置 update_avatar=更新头像设置
delete_current_avatar=删除当前头像
uploaded_avatar_not_a_image=上传的文件不是一张图片! uploaded_avatar_not_a_image=上传的文件不是一张图片!
no_custom_avatar_available=未上传过自定义头像,无法激活该选项。
update_avatar_success=您的头像设置更新成功! update_avatar_success=您的头像设置更新成功!
change_password=修改密码 change_password=修改密码
@ -477,7 +471,7 @@ issues.closed_at=`于 <a id="%[1]s" href="#%[1]s">%[2]s</a> 关闭`
issues.reopened_at=`于 <a id="%[1]s" href="#%[1]s">%[2]s</a> 重新开启` issues.reopened_at=`于 <a id="%[1]s" href="#%[1]s">%[2]s</a> 重新开启`
issues.commit_ref_at=`在代码提交 <a id="%[1]s" href="#%[1]s">%[2]s</a> 中引用了该工单` issues.commit_ref_at=`在代码提交 <a id="%[1]s" href="#%[1]s">%[2]s</a> 中引用了该工单`
issues.poster=发布者 issues.poster=发布者
issues.admin=管理员 issues.collaborator=协作者
issues.owner=所有者 issues.owner=所有者
issues.sign_up_for_free=免费注册 issues.sign_up_for_free=免费注册
issues.sign_in_require_desc=并加入到对话中来。如果您已经注册,可以直接 <a href="%s">登录并评论</a> issues.sign_in_require_desc=并加入到对话中来。如果您已经注册,可以直接 <a href="%s">登录并评论</a>
@ -494,6 +488,7 @@ issues.label_modify=修改标签
issues.label_deletion=删除标签操作 issues.label_deletion=删除标签操作
issues.label_deletion_desc=删除该标签将会移除所有工单中相关的信息。是否继续? issues.label_deletion_desc=删除该标签将会移除所有工单中相关的信息。是否继续?
issues.label_deletion_success=标签删除成功! issues.label_deletion_success=标签删除成功!
issues.num_participants=%d 名参与者
pulls.new=创建合并请求 pulls.new=创建合并请求
pulls.compare_changes=对比文件变化 pulls.compare_changes=对比文件变化
@ -557,6 +552,8 @@ wiki.save_page=保存页面
wiki.last_commit_info=%s 于 %s 修改了此页面 wiki.last_commit_info=%s 于 %s 修改了此页面
wiki.edit_page_button=修改 wiki.edit_page_button=修改
wiki.new_page_button=新的页面 wiki.new_page_button=新的页面
wiki.delete_page_button=删除页面
wiki.delete_page_notice_1=此操作将删除页面 <code>"%s"</code>,请三思而后行。
wiki.page_already_exists=相同名称的 Wiki 页面已经存在。 wiki.page_already_exists=相同名称的 Wiki 页面已经存在。
wiki.pages=所有页面 wiki.pages=所有页面
wiki.last_updated=最后更新于 %s wiki.last_updated=最后更新于 %s
@ -581,14 +578,23 @@ settings.tracker_url_format=外部工单管理系统的 URL 格式
settings.tracker_url_format_desc=您可以使用 <code>{user} {repo} {index}</code> 分别作为用户名、仓库名和工单索引的占位符。 settings.tracker_url_format_desc=您可以使用 <code>{user} {repo} {index}</code> 分别作为用户名、仓库名和工单索引的占位符。
settings.pulls_desc=启用合并请求以接受社区贡献 settings.pulls_desc=启用合并请求以接受社区贡献
settings.danger_zone=危险操作区 settings.danger_zone=危险操作区
settings.new_owner_has_same_repo=新的仓库拥有者已经存在同名仓库!
settings.convert=转换为普通仓库
settings.convert_desc=您可以将该镜像仓库转换为普通仓库,且此操作不可逆。
settings.convert_notices_1=- 该操作会将该镜像仓库转换为普通仓库,且操作不可逆。
settings.convert_confirm=确认转换
settings.convert_succeed=转换为普通仓库类型成功!
settings.transfer=转移仓库所有权 settings.transfer=转移仓库所有权
settings.transfer_desc=您可以将仓库转移至您拥有管理员权限的帐户或组织。 settings.transfer_desc=您可以将仓库转移至您拥有管理员权限的帐户或组织。
settings.new_owner_has_same_repo=新的仓库拥有者已经存在同名仓库!
settings.delete=删除本仓库
settings.delete_desc=删除仓库操作不可逆转,请三思而后行。
settings.transfer_notices_1=- 如果您将仓库转移给个人用户,您将会丢失操作权限。 settings.transfer_notices_1=- 如果您将仓库转移给个人用户,您将会丢失操作权限。
settings.transfer_notices_2=- 如果您将仓库转移给您是所有者的组织,您的操作权限将被保留。 settings.transfer_notices_2=- 如果您将仓库转移给您是所有者的组织,您的操作权限将被保留。
settings.transfer_form_title=请输入以下信息以确认您的操作: settings.transfer_form_title=请输入以下信息以确认您的操作:
settings.wiki_delete=清除 Wiki 数据
settings.wiki_delete_desc=清除 Wiki 数据操作不可逆转,请三思而后行。
settings.wiki_delete_notices_1=- 此操作将会清除并禁用仓库 %s 的 Wiki
settings.wiki_deletion_success=仓库 Wiki 数据清除成功!
settings.delete=删除本仓库
settings.delete_desc=删除仓库操作不可逆转,请三思而后行。
settings.delete_notices_1=- 此操作 <strong>不可以</strong> 被回滚。 settings.delete_notices_1=- 此操作 <strong>不可以</strong> 被回滚。
settings.delete_notices_2=- 此操作将永久删除该仓库,包括 Git 数据、 工单、 评论和协作者的操作权限。 settings.delete_notices_2=- 此操作将永久删除该仓库,包括 Git 数据、 工单、 评论和协作者的操作权限。
settings.delete_notices_fork_1=- 如果该仓库为公开的,则在删除仓库后所有的派生仓库都将转换成独立的仓库。 settings.delete_notices_fork_1=- 如果该仓库为公开的,则在删除仓库后所有的派生仓库都将转换成独立的仓库。
@ -602,8 +608,12 @@ settings.transfer_succeed=仓库所有权转移成功!
settings.confirm_delete=确认删除仓库 settings.confirm_delete=确认删除仓库
settings.add_collaborator=增加新的协作者 settings.add_collaborator=增加新的协作者
settings.add_collaborator_success=成功添加新的协作者! settings.add_collaborator_success=成功添加新的协作者!
settings.delete_collaborator=删除
settings.collaborator_deletion=删除协作者
settings.collaborator_deletion_desc=此用户被删除后将不再拥有相关的协作权限。是否继续?
settings.remove_collaborator_success=被操作的协作者已经被收回权限! settings.remove_collaborator_success=被操作的协作者已经被收回权限!
settings.search_user_placeholder=搜索用户... settings.search_user_placeholder=搜索用户...
settings.org_not_allowed_to_be_collaborator=组织不允许被添加为仓库协作者!
settings.user_is_org_member=被操作的用户是组织成员,因此无法添加为协作者! settings.user_is_org_member=被操作的用户是组织成员,因此无法添加为协作者!
settings.add_webhook=添加 Web 钩子 settings.add_webhook=添加 Web 钩子
settings.hooks_desc=Web 钩子允许您设定在 Gogs 上发生指定事件时对指定 URL 发送 POST 通知。查看 <a target="_blank" href="%s">Webhooks 文档</a> 获取更多信息。 settings.hooks_desc=Web 钩子允许您设定在 Gogs 上发生指定事件时对指定 URL 发送 POST 通知。查看 <a target="_blank" href="%s">Webhooks 文档</a> 获取更多信息。
@ -818,6 +828,8 @@ dashboard.resync_all_sshkeys=重新生成 '.ssh/authorized_keys' 文件(警告
dashboard.resync_all_sshkeys_success=所有公钥重新生成成功! dashboard.resync_all_sshkeys_success=所有公钥重新生成成功!
dashboard.resync_all_update_hooks=重新生成所有仓库的 Update 钩子(用于自定义配置文件被修改) dashboard.resync_all_update_hooks=重新生成所有仓库的 Update 钩子(用于自定义配置文件被修改)
dashboard.resync_all_update_hooks_success=所有仓库的 Update 钩子重新生成成功! dashboard.resync_all_update_hooks_success=所有仓库的 Update 钩子重新生成成功!
dashboard.reinit_missing_repos=重新初始化所有丢失 Git 文件的仓库
dashboard.reinit_missing_repos_success=所有丢失 Git 文件的仓库重新初始化成功!
dashboard.server_uptime=服务运行时间 dashboard.server_uptime=服务运行时间
dashboard.current_goroutine=当前 Goroutines 数量 dashboard.current_goroutine=当前 Goroutines 数量
@ -911,6 +923,7 @@ auths.attribute_username_placeholder=留空表示使用用户登录时所使用
auths.attribute_name=名字属性 auths.attribute_name=名字属性
auths.attribute_surname=姓氏属性 auths.attribute_surname=姓氏属性
auths.attribute_mail=邮箱属性 auths.attribute_mail=邮箱属性
auths.attributes_in_bind=从 Bind DN 中拉取属性信息
auths.filter=用户过滤规则 auths.filter=用户过滤规则
auths.admin_filter=管理员过滤规则 auths.admin_filter=管理员过滤规则
auths.ms_ad_sa=Ms Ad SA auths.ms_ad_sa=Ms Ad SA
@ -932,6 +945,7 @@ auths.update=更新认证设置
auths.delete=删除该认证 auths.delete=删除该认证
auths.delete_auth_title=删除认证操作 auths.delete_auth_title=删除认证操作
auths.delete_auth_desc=该认证将被删除。是否继续? auths.delete_auth_desc=该认证将被删除。是否继续?
auths.still_in_used=此认证仍旧与一些用户有关联,请先删除或者将这些用户转换为其它登录类型。
auths.deletion_success=授权源删除成功! auths.deletion_success=授权源删除成功!
config.server_config=服务器配置 config.server_config=服务器配置
@ -948,6 +962,19 @@ config.static_file_root_path=静态文件根目录
config.log_file_root_path=日志文件根目录 config.log_file_root_path=日志文件根目录
config.script_type=脚本类型 config.script_type=脚本类型
config.reverse_auth_user=反向代理认证 config.reverse_auth_user=反向代理认证
config.ssh_config=SSH 配置
config.ssh_enabled=启用服务
config.ssh_start_builtin_server=启用内置服务
config.ssh_domain=域名
config.ssh_port=端口
config.ssh_listen_port=监听端口
config.ssh_root_path=根目录
config.ssh_key_test_path=密钥测试路径
config.ssh_keygen_path=密钥生成器('ssh-keygen')路径
config.ssh_minimum_key_size_check=密钥最小长度检查
config.ssh_minimum_key_sizes=密钥最小长度限制
config.db_config=数据库配置 config.db_config=数据库配置
config.db_type=数据库类型 config.db_type=数据库类型
config.db_host=主机地址 config.db_host=主机地址
@ -962,7 +989,6 @@ config.register_email_confirm=注册邮件确认
config.disable_register=关闭注册功能 config.disable_register=关闭注册功能
config.show_registration_button=显示注册按钮 config.show_registration_button=显示注册按钮
config.require_sign_in_view=强制登录浏览 config.require_sign_in_view=强制登录浏览
config.enable_cache_avatar=开启缓存头像
config.mail_notify=邮件通知提醒 config.mail_notify=邮件通知提醒
config.disable_key_size_check=禁用密钥最小长度检查 config.disable_key_size_check=禁用密钥最小长度检查
config.enable_captcha=启用验证码服务 config.enable_captcha=启用验证码服务
@ -978,6 +1004,9 @@ config.mailer_disable_helo=禁用 HELO 操作
config.mailer_name=发送者名称 config.mailer_name=发送者名称
config.mailer_host=邮件主机地址 config.mailer_host=邮件主机地址
config.mailer_user=发送者帐号 config.mailer_user=发送者帐号
config.send_test_mail=发送测试邮件
config.test_mail_failed=发送测试邮件至 '%s' 时失败:%v
config.test_mail_sent=测试邮件已经发送至 '%s'。
config.oauth_config=社交帐号配置 config.oauth_config=社交帐号配置
config.oauth_enabled=启用服务 config.oauth_enabled=启用服务
config.cache_config=Cache 配置 config.cache_config=Cache 配置
@ -1029,7 +1058,11 @@ create_repo=创建了仓库 <a href="%s">%s</a>
rename_repo=重命名仓库 <code>%[1]s</code> 为 <a href="%[2]s">%[3]s</a> rename_repo=重命名仓库 <code>%[1]s</code> 为 <a href="%[2]s">%[3]s</a>
commit_repo=推送了 <a href="%[1]s/src/%[2]s">%[3]s</a> 分支的代码到 <a href="%[1]s">%[4]s</a> commit_repo=推送了 <a href="%[1]s/src/%[2]s">%[3]s</a> 分支的代码到 <a href="%[1]s">%[4]s</a>
create_issue=`创建了工单 <a href="%s/issues/%s">%s#%[2]s</a>` create_issue=`创建了工单 <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`关闭了工单 <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`重新开启了工单 <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`创建了合并请求 <a href="%s/pulls/%s">%s#%[2]s</a>` create_pull_request=`创建了合并请求 <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request=`关闭了合并请求 <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`重新开启了合并请求 <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue=`评论了工单 <a href="%s/issues/%s">%s#%[2]s</a>` comment_issue=`评论了工单 <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request=`合并了合并请求 <a href="%s/pulls/%s">%s#%[2]s</a>` merge_pull_request=`合并了合并请求 <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo=将仓库 <code>%s</code> 转移至 <a href="%s">%s</a> transfer_repo=将仓库 <code>%s</code> 转移至 <a href="%s">%s</a>

165
conf/locale/locale_zh-HK.ini

@ -3,69 +3,62 @@ app_desc=基於 Go 語言的自助 Git 服務
home=首頁 home=首頁
dashboard=控制面版 dashboard=控制面版
explore=探索 explore=探索
help=幫助 help=說明
sign_in= sign_in=
sign_out=退 sign_out=
sign_up=註冊 sign_up=註冊
register=註冊 register=註冊
website=官方網站 website=網站
version=當前版本 version=版本
page=頁面 page=頁面
template=模版 template=樣板
language=語言選項 language=語言
create_new=創建... create_new=創建...
user_profile_and_more=用戶信息及更多 user_profile_and_more=用戶信息及更多
signed_in_as=已登錄用戶 signed_in_as=已登錄用戶
username=用戶名 username=用戶名
email=郵箱 email=電子郵件
password=密碼 password=密碼
re_type=確認密碼 re_type=確認密碼
captcha=驗證碼 captcha=驗證碼
repository= repository=儲存
organization=組織 organization=組織
mirror=鏡像 mirror=鏡像
new_repo=創建新的倉 new_repo=新增儲存
new_migrate=遷移外部倉庫 new_migrate=遷移外部倉庫
new_mirror=新鏡像 new_mirror=新鏡像
new_fork=的派生倉庫 new_fork=增程式庫分支
new_org=創建新的組織 new_org=新增組織
manage_org=管理我的組織 manage_org=管理組織
admin_panel=管理面 admin_panel=管理面
account_settings=戶設置 account_settings=號設定
settings=帳戶設置 settings=設定
your_profile=個人信息 your_profile=個人資料
your_settings=用戶設 your_settings=用戶設
news_feed=最新活動 activities=Activities
pull_requests=合併請求 pull_requests=合併請求
issues=問題管理 issues=問題
cancel=取消 cancel=取消
[search]
search=搜尋...
repository=倉庫
user=用戶
issue=工單
code=程式碼
[install] [install]
install=安裝頁面 install=安裝頁面
title=首次執行安裝程序 title=首次安裝步驟
docker_helper=如果您正在使用 Docker 容器運行 Gogs,請務必先仔細閱讀 <a target="_blank" href="%s">官方文檔</a> 後再對本頁面進行填寫。 docker_helper=如果您正在使用 Docker 容器運行 Gogs,請務必先仔細閱讀 <a target="_blank" href="%s">官方文檔</a> 後再對本頁面進行填寫。
requite_db_desc=Gogs 要求安裝 MySQL、PostgreSQL、SQLite3 或 TiDB。 requite_db_desc=Gogs 要求安裝 MySQL、PostgreSQL、SQLite3 或 TiDB。
db_title=數據庫設置 db_title=數據庫設置
db_type=數據庫類型 db_type=資料庫類型
host=數據庫主機 host=主機
user=數據庫用戶 user=帳號
password=數據庫用戶密碼 password=密碼
db_name=數據庫名稱 db_name=資料庫名稱
db_helper=如果您使用 MySQL,請使用 INNODB 引擎以及 utf8_general_ci 字符集。 db_helper=如果您使用 MySQL,請使用 INNODB 引擎以及 utf8_general_ci 字符集。
ssl_mode=SSL 模式 ssl_mode=SSL 模式
path=數據庫文件路徑 path=數據庫文件路徑
sqlite_helper=SQLite3 或 TiDB 的數據庫路徑。 sqlite_helper=The file path of SQLite3 or TiDB database. <br>Please use absolute path when you start as service.
err_empty_db_path=SQLite3 或 TiDB 的數據庫路徑不能為空。 err_empty_db_path=SQLite3 或 TiDB 的數據庫路徑不能為空。
err_invalid_tidb_name=TiDB 數據庫名稱不允許包含字符 "." 或 "-" 。 err_invalid_tidb_name=TiDB 數據庫名稱不允許包含字符 "." 或 "-" 。
no_admin_and_disable_registration=您不能夠在未創建管理員用戶的情況下禁止註冊。 no_admin_and_disable_registration=您不能夠在未創建管理員用戶的情況下禁止註冊。
@ -74,25 +67,27 @@ err_empty_admin_password=管理員密碼不能為空。
general_title=應用基本設置 general_title=應用基本設置
app_name=應用名稱 app_name=應用名稱
app_name_helper=為您的組織取個響亮而又偉大的名稱 app_name_helper=為您的組織取個響亮而又偉大的名稱
repo_path=倉庫根目錄 repo_path=儲存庫的根目錄
repo_path_helper=所有 Git 遠程倉庫都將被存放於該目錄 repo_path_helper=所有Git遠端儲存庫將會儲存在這個目錄之下
run_user=執行系統用戶 run_user=執行使用者
run_user_helper=該用戶必須具有對庫根目錄和執行 Gogs 的操作權限。 run_user_helper=該用戶必須具有對儲存庫根目錄和執行 Gogs 的操作權限。
domain=域名 domain=域名
domain_helper=該設置影響 SSH 複製地址。 domain_helper=該設置影響 SSH 複製地址。
ssh_port=SSH 埠 ssh_port=SSH 埠
ssh_port_helper=您的 SSH 服務正在使用此埠號,若要禁用SSH 功能請保持欄位空白。 ssh_port_helper=您的 SSH 服務正在使用此埠號,若要禁用SSH 功能請保持欄位空白。
http_port=HTTP 端口號 http_port=HTTP 端口號
http_port_helper=應用監聽的端口號 http_port_helper=應用監聽的端口號
app_url=應用 URL app_url=應用程式網址
app_url_helper=該設置影響 HTTP/HTTPS 複製地址和一些郵箱中的連結。 app_url_helper=該設置影響 HTTP/HTTPS 複製地址和一些郵箱中的連結。
log_root_path=Log Path
log_root_path_helper=Directory to write log files to.
optional_title=可選設置 optional_title=可選設置
email_title=電子郵件服務設定 email_title=電子郵件服務設定
smtp_host=SMTP 主機 smtp_host=SMTP 主機
smtp_from=郵件來自 smtp_from=寄件者
smtp_from_helper=郵件來自地址,遵循 RFC 5322 標准。可以是一個單純的郵箱地址或使用 "name" <email@example.com> 的格式。 smtp_from_helper=郵件來自地址,遵循 RFC 5322 標准。可以是一個單純的郵箱地址或使用 "name" <email@example.com> 的格式。
mailer_user=發送郵箱 mailer_user=寄件者 E-mail
mailer_password=發送郵箱密碼 mailer_password=發送郵箱密碼
register_confirm=啟用註冊郵箱確認 register_confirm=啟用註冊郵箱確認
mail_notify=啟用郵件通知提醒 mail_notify=啟用郵件通知提醒
@ -122,6 +117,7 @@ run_user_not_match=執行系統用戶非當前用戶:%s -> %s
save_config_failed=應用配置保存失敗:%v save_config_failed=應用配置保存失敗:%v
invalid_admin_setting=管理員帳戶設置不正確:%v invalid_admin_setting=管理員帳戶設置不正確:%v
install_success=您好!我們很高興您選擇使用 Gogs,祝您使用愉快,代碼從此無 BUG! install_success=您好!我們很高興您選擇使用 Gogs,祝您使用愉快,代碼從此無 BUG!
invalid_log_root_path=Log root path is invalid: %v
[home] [home]
uname_holder=用戶名或郵箱 uname_holder=用戶名或郵箱
@ -137,6 +133,8 @@ issues.in_your_repos=屬於該用戶倉庫的
[explore] [explore]
repos=探索倉庫 repos=探索倉庫
users=Users
search=Search
[auth] [auth]
create_new_account=創建帳戶 create_new_account=創建帳戶
@ -174,7 +172,7 @@ modify=確認修改
[form] [form]
UserName=用戶名 UserName=用戶名
RepoName=庫名稱 RepoName=儲存庫名稱
Email=郵箱地址 Email=郵箱地址
Password=密碼 Password=密碼
Retype=確認密碼 Retype=確認密碼
@ -203,7 +201,6 @@ repo_name_been_taken=倉庫名稱已經被佔用。
org_name_been_taken=組織名稱已經被佔用。 org_name_been_taken=組織名稱已經被佔用。
team_name_been_taken=團隊名稱已經被佔用。 team_name_been_taken=團隊名稱已經被佔用。
email_been_used=郵箱地址已經被使用。 email_been_used=郵箱地址已經被使用。
illegal_team_name=團隊名稱包含不合法字符。
username_password_incorrect=用戶名或密碼不正確。 username_password_incorrect=用戶名或密碼不正確。
enterred_invalid_repo_name=請檢查您輸入的倉庫名稱是正確。 enterred_invalid_repo_name=請檢查您輸入的倉庫名稱是正確。
enterred_invalid_owner_name=請檢查您輸入的新所有者用戶名是否正確。 enterred_invalid_owner_name=請檢查您輸入的新所有者用戶名是否正確。
@ -219,8 +216,6 @@ still_own_repo=您的帳戶仍然是某些倉庫的擁有者,您必須先轉
still_has_org=您的帳戶仍舊是某些組織的成員,您必須先離開或刪除組織。 still_has_org=您的帳戶仍舊是某些組織的成員,您必須先離開或刪除組織。
org_still_own_repo=該組織仍然是某些倉庫的擁有者,您必須先轉移或刪除它們才能執行刪除組織操作! org_still_own_repo=該組織仍然是某些倉庫的擁有者,您必須先轉移或刪除它們才能執行刪除組織操作!
still_own_user=該授權認證依舊被部分用戶使用,請先刪除該部分用戶後再試!
target_branch_not_exist=目標分支不存在 target_branch_not_exist=目標分支不存在
[user] [user]
@ -262,11 +257,10 @@ continue=繼續操作
cancel=取消操作 cancel=取消操作
enable_custom_avatar=啟動自定義頭像 enable_custom_avatar=啟動自定義頭像
enable_custom_avatar_helper=激活該選項來禁止從 Gravatar 獲取頭像
choose_new_avatar=選擇新的頭像 choose_new_avatar=選擇新的頭像
update_avatar=更新頭像設置 update_avatar=更新頭像設置
delete_current_avatar=Delete Current Avatar
uploaded_avatar_not_a_image=上傳的文件不是一張圖片! uploaded_avatar_not_a_image=上傳的文件不是一張圖片!
no_custom_avatar_available=沒有任何自定義頭像,無法激活該選項。
update_avatar_success=您的頭像設置更新成功! update_avatar_success=您的頭像設置更新成功!
change_password=修改密碼 change_password=修改密碼
@ -363,7 +357,7 @@ watchers=關注者
stargazers=稱讚者 stargazers=稱讚者
forks=派生倉庫 forks=派生倉庫
form.reach_limit_of_creation=The owner has reached maximum creation limit of %d repositories. form.reach_limit_of_creation=擁有者已達到儲存庫最大的新增上限 %d。
form.name_reserved=倉庫名稱 '%s' 是被保留的。 form.name_reserved=倉庫名稱 '%s' 是被保留的。
form.name_pattern_not_allowed=倉庫名稱不允許 '%s' 的格式。 form.name_pattern_not_allowed=倉庫名稱不允許 '%s' 的格式。
@ -384,7 +378,7 @@ copy_link=複製連結
copy_link_success=複製成功! copy_link_success=複製成功!
copy_link_error=請按下 ⌘-C 或 Ctrl-C 複製 copy_link_error=請按下 ⌘-C 或 Ctrl-C 複製
copied=複製成功 copied=複製成功
unwatch=取消關 unwatch=取消關
watch=關註 watch=關註
unstar=取消讚好 unstar=取消讚好
star=讚好 star=讚好
@ -477,7 +471,7 @@ issues.closed_at=`於 <a id="%[1]s" href="#%[1]s">%[2]s</a> 關閉`
issues.reopened_at=`於 <a id="%[1]s" href="#%[1]s">%[2]s</a> 重新開啟` issues.reopened_at=`於 <a id="%[1]s" href="#%[1]s">%[2]s</a> 重新開啟`
issues.commit_ref_at=`在代碼提交 <a id="%[1]s" href="#%[1]s">%[2]s</a> 中引用了該問題` issues.commit_ref_at=`在代碼提交 <a id="%[1]s" href="#%[1]s">%[2]s</a> 中引用了該問題`
issues.poster=發佈者 issues.poster=發佈者
issues.admin=管理員 issues.collaborator=Collaborator
issues.owner=所有者 issues.owner=所有者
issues.sign_up_for_free=免費註冊 issues.sign_up_for_free=免費註冊
issues.sign_in_require_desc=及加入到對話當中。如果您已經註冊,可以直接 <a href="%s">登錄及評論</a> issues.sign_in_require_desc=及加入到對話當中。如果您已經註冊,可以直接 <a href="%s">登錄及評論</a>
@ -494,6 +488,7 @@ issues.label_modify=修改標籤
issues.label_deletion=刪除標籤 issues.label_deletion=刪除標籤
issues.label_deletion_desc=刪除該標籤將會移除所有問題中相關的訊息。是否繼續? issues.label_deletion_desc=刪除該標籤將會移除所有問題中相關的訊息。是否繼續?
issues.label_deletion_success=標籤刪除成功! issues.label_deletion_success=標籤刪除成功!
issues.num_participants=%d Participants
pulls.new=創建合併請求 pulls.new=創建合併請求
pulls.compare_changes=對比文件變化 pulls.compare_changes=對比文件變化
@ -516,8 +511,8 @@ pulls.has_merged=該合併請求已經成功合併!
pulls.data_broken=該合併請求的數據由於派生倉庫的相關信息被刪除而被破壞。 pulls.data_broken=該合併請求的數據由於派生倉庫的相關信息被刪除而被破壞。
pulls.is_checking=該合併請求正在進行衝突檢查,請稍後再刷新頁面。 pulls.is_checking=該合併請求正在進行衝突檢查,請稍後再刷新頁面。
pulls.can_auto_merge_desc=這個拉請求可以自動合併。 pulls.can_auto_merge_desc=這個拉請求可以自動合併。
pulls.cannot_auto_merge_desc=This pull request can't be merged automatically because there are conflicts. pulls.cannot_auto_merge_desc=由於存在衝突,不能自動合併這推送請求。
pulls.cannot_auto_merge_helper=Please merge manually in order to resolve the conflicts. pulls.cannot_auto_merge_helper=請手動合併來解決衝突。
pulls.merge_pull_request=合併請求 pulls.merge_pull_request=合併請求
pulls.open_unmerged_pull_exists=`由於已經存在來自相同倉庫和合併信息的未合併請求(#%d),您無法執行重新開啟操作。` pulls.open_unmerged_pull_exists=`由於已經存在來自相同倉庫和合併信息的未合併請求(#%d),您無法執行重新開啟操作。`
@ -557,6 +552,8 @@ wiki.save_page=保存頁面
wiki.last_commit_info=%s 於 %s 修改了此頁面 wiki.last_commit_info=%s 於 %s 修改了此頁面
wiki.edit_page_button=修改 wiki.edit_page_button=修改
wiki.new_page_button=新的頁面 wiki.new_page_button=新的頁面
wiki.delete_page_button=Delete Page
wiki.delete_page_notice_1=This will delete the page <code>"%s"</code>. Please be certain.
wiki.page_already_exists=相同名稱的 Wiki 頁面已經存在。 wiki.page_already_exists=相同名稱的 Wiki 頁面已經存在。
wiki.pages=所有頁面 wiki.pages=所有頁面
wiki.last_updated=最後更新於 %s wiki.last_updated=最後更新於 %s
@ -574,21 +571,30 @@ settings.advanced_settings=高級設置
settings.wiki_desc=啟用 Wiki 以允許用戶協作文檔 settings.wiki_desc=啟用 Wiki 以允許用戶協作文檔
settings.use_external_wiki=使用外部 wiki settings.use_external_wiki=使用外部 wiki
settings.external_wiki_url=外部 Wiki 連結 settings.external_wiki_url=外部 Wiki 連結
settings.external_wiki_url_desc=Visitors will be redirected to URL when they click on the tab. settings.external_wiki_url_desc=當分頁上按一下,訪客將會重新導到 URL。
settings.issues_desc=啟用內置的輕量級問題管理系統 settings.issues_desc=啟用內置的輕量級問題管理系統
settings.use_external_issue_tracker=使用外部的問題管理系統 settings.use_external_issue_tracker=使用外部的問題管理系統
settings.tracker_url_format=外部問題管理系統的 URL 格式 settings.tracker_url_format=外部問題管理系統的 URL 格式
settings.tracker_url_format_desc=您可以使用 <code>{user} {repo} {index}</code> 分別作為用戶名、倉庫名和問題索引的占位符。 settings.tracker_url_format_desc=您可以使用 <code>{user} {repo} {index}</code> 分別作為用戶名、倉庫名和問題索引的占位符。
settings.pulls_desc=啟用合併請求以接受社區貢獻 settings.pulls_desc=啟用合併請求以接受社區貢獻
settings.danger_zone=危險操作區 settings.danger_zone=危險操作區
settings.new_owner_has_same_repo=新的倉庫擁有者已經存在同名倉庫!
settings.convert=Convert To Regular Repository
settings.convert_desc=You can convert this mirror to a regular repository. This cannot be reversed.
settings.convert_notices_1=- This operation will convert this repository mirror into a regular repository and cannot be undone.
settings.convert_confirm=Confirm Conversion
settings.convert_succeed=Repository has been converted to regular type successfully.
settings.transfer=轉移倉庫所有權 settings.transfer=轉移倉庫所有權
settings.transfer_desc=您可以將倉庫轉移至您擁有管理員權限的帳戶或組織。 settings.transfer_desc=您可以將倉庫轉移至您擁有管理員權限的帳戶或組織。
settings.new_owner_has_same_repo=新的倉庫擁有者已經存在同名倉庫!
settings.delete=刪除本倉庫
settings.delete_desc=刪除倉庫操作不可逆轉,請三思而後行。
settings.transfer_notices_1=- 如果您將倉庫轉移給個人用戶,您將會丟失操作權限。 settings.transfer_notices_1=- 如果您將倉庫轉移給個人用戶,您將會丟失操作權限。
settings.transfer_notices_2=- 如果您將倉庫轉移給您是所有者的組織,您的操作權限將被保留。 settings.transfer_notices_2=- 如果您將倉庫轉移給您是所有者的組織,您的操作權限將被保留。
settings.transfer_form_title=請輸入以下信息以確認您的操作: settings.transfer_form_title=請輸入以下信息以確認您的操作:
settings.wiki_delete=Erase Wiki Data
settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain.
settings.wiki_delete_notices_1=- This will delete and disable the wiki for %s
settings.wiki_deletion_success=Repository wiki data have been erased successfully.
settings.delete=刪除本倉庫
settings.delete_desc=刪除倉庫操作不可逆轉,請三思而後行。
settings.delete_notices_1=- 此操作 <strong>不可以</strong> 被回滾。 settings.delete_notices_1=- 此操作 <strong>不可以</strong> 被回滾。
settings.delete_notices_2=- 此操作將永久刪除該倉庫,包括 Git 數據、 問題、 評論和協作者的操作權限。 settings.delete_notices_2=- 此操作將永久刪除該倉庫,包括 Git 數據、 問題、 評論和協作者的操作權限。
settings.delete_notices_fork_1=- 如果該倉庫為公開的,則在刪除倉庫後所有的派生倉庫都將轉換成獨立的倉庫。 settings.delete_notices_fork_1=- 如果該倉庫為公開的,則在刪除倉庫後所有的派生倉庫都將轉換成獨立的倉庫。
@ -602,8 +608,12 @@ settings.transfer_succeed=倉庫所有權轉移成功!
settings.confirm_delete=確認刪除倉庫 settings.confirm_delete=確認刪除倉庫
settings.add_collaborator=增加新的協作者 settings.add_collaborator=增加新的協作者
settings.add_collaborator_success=成功添加新的協作者! settings.add_collaborator_success=成功添加新的協作者!
settings.delete_collaborator=Delete
settings.collaborator_deletion=Collaborator Deletion
settings.collaborator_deletion_desc=This user will no longer have collaboration access to this repository after deletion. Do you want to continue?
settings.remove_collaborator_success=被操作的協作者已經被收回權限! settings.remove_collaborator_success=被操作的協作者已經被收回權限!
settings.search_user_placeholder=搜索用戶... settings.search_user_placeholder=搜索用戶...
settings.org_not_allowed_to_be_collaborator=Organization is not allowed to be added as a collaborator.
settings.user_is_org_member=被操作的用戶是組織成員,因此無法添加為協作者! settings.user_is_org_member=被操作的用戶是組織成員,因此無法添加為協作者!
settings.add_webhook=添加 Web 鉤子 settings.add_webhook=添加 Web 鉤子
settings.hooks_desc=Web 鉤子允許您設定在 Gogs 上發生指定事件時對指定 URL 發送 POST 通知。查看 <a target="_blank" href="%s">Webhooks 文檔</a> 獲取更多信息。 settings.hooks_desc=Web 鉤子允許您設定在 Gogs 上發生指定事件時對指定 URL 發送 POST 通知。查看 <a target="_blank" href="%s">Webhooks 文檔</a> 獲取更多信息。
@ -669,7 +679,7 @@ diff.commit=當前提交
diff.data_not_available=暫無可用數據 diff.data_not_available=暫無可用數據
diff.show_diff_stats=顯示文件統計 diff.show_diff_stats=顯示文件統計
diff.show_split_view=分割檢視 diff.show_split_view=分割檢視
diff.show_unified_view=Unified View diff.show_unified_view=統一視圖
diff.stats_desc=共有 <strong> %d 個文件被更改</strong>,包括 <strong>%d 次插入</strong> 和 <strong>%d 次删除</strong> diff.stats_desc=共有 <strong> %d 個文件被更改</strong>,包括 <strong>%d 次插入</strong> 和 <strong>%d 次删除</strong>
diff.bin=二進制 diff.bin=二進制
diff.view_file=查看文件 diff.view_file=查看文件
@ -696,7 +706,7 @@ release.prerelease_desc=這是一個預發佈版本
release.prerelease_helper=我們會告知用戶不建議將本發佈投入生產環境使用。 release.prerelease_helper=我們會告知用戶不建議將本發佈投入生產環境使用。
release.cancel=取消 release.cancel=取消
release.publish=發佈版本 release.publish=發佈版本
release.save_draft=保在草稿 release.save_draft=儲存草稿
release.edit_release=編輯發佈信息 release.edit_release=編輯發佈信息
release.delete_release=刪除此次發布 release.delete_release=刪除此次發布
release.deletion=刪除版本發布操作 release.deletion=刪除版本發布操作
@ -818,6 +828,8 @@ dashboard.resync_all_sshkeys=重新生成 '.ssh/authorized_keys' 文件(警告
dashboard.resync_all_sshkeys_success=所有公鑰重新生成成功! dashboard.resync_all_sshkeys_success=所有公鑰重新生成成功!
dashboard.resync_all_update_hooks=重新生成所有倉庫的 Update 鈎子(用於被修改的自定義配置文件) dashboard.resync_all_update_hooks=重新生成所有倉庫的 Update 鈎子(用於被修改的自定義配置文件)
dashboard.resync_all_update_hooks_success=已成功重新生成所有倉庫的 Update 鈎子! dashboard.resync_all_update_hooks_success=已成功重新生成所有倉庫的 Update 鈎子!
dashboard.reinit_missing_repos=Reinitialize all repository records that lost Git files
dashboard.reinit_missing_repos_success=All repository records that lost Git files have been reinitialized successfully.
dashboard.server_uptime=服務執行時間 dashboard.server_uptime=服務執行時間
dashboard.current_goroutine=當前 Goroutines 數量 dashboard.current_goroutine=當前 Goroutines 數量
@ -865,8 +877,8 @@ users.auth_login_name=認證登錄名稱
users.password_helper=留空使其保持不變。 users.password_helper=留空使其保持不變。
users.update_profile_success=該用戶信息更新成功! users.update_profile_success=該用戶信息更新成功!
users.edit_account=編輯用戶信息 users.edit_account=編輯用戶信息
users.max_repo_creation=Maximum Repository Creation Limit users.max_repo_creation=最大儲存庫新增限制
users.max_repo_creation_desc=(Set -1 to use global default limit) users.max_repo_creation_desc=(設定 -1 使用全域預設限制)
users.is_activated=該用戶已被激活 users.is_activated=該用戶已被激活
users.is_admin=該用戶具有管理員權限 users.is_admin=該用戶具有管理員權限
users.allow_git_hook=該帳戶具有創建 Git 鉤子的權限 users.allow_git_hook=該帳戶具有創建 Git 鉤子的權限
@ -911,6 +923,7 @@ auths.attribute_username_placeholder=留空表示使用用戶登錄時所使用
auths.attribute_name=名子屬性 auths.attribute_name=名子屬性
auths.attribute_surname=姓氏屬性 auths.attribute_surname=姓氏屬性
auths.attribute_mail=電子郵箱屬性 auths.attribute_mail=電子郵箱屬性
auths.attributes_in_bind=Fetch attributes in Bind DN context
auths.filter=使用者篩選器 auths.filter=使用者篩選器
auths.admin_filter=管理者篩選器 auths.admin_filter=管理者篩選器
auths.ms_ad_sa=Ms Ad SA auths.ms_ad_sa=Ms Ad SA
@ -932,6 +945,7 @@ auths.update=更新認證設置
auths.delete=刪除該認證 auths.delete=刪除該認證
auths.delete_auth_title=刪除認證操作 auths.delete_auth_title=刪除認證操作
auths.delete_auth_desc=該認證將被刪除。是否繼續? auths.delete_auth_desc=該認證將被刪除。是否繼續?
auths.still_in_used=This authentication is still used by some users, please delete or convert these users to another login type first.
auths.deletion_success=認證源刪除成功! auths.deletion_success=認證源刪除成功!
config.server_config=服務器配置 config.server_config=服務器配置
@ -948,6 +962,19 @@ config.static_file_root_path=靜態文件根目錄
config.log_file_root_path=日志文件根目錄 config.log_file_root_path=日志文件根目錄
config.script_type=腳本類型 config.script_type=腳本類型
config.reverse_auth_user=反向代理認證 config.reverse_auth_user=反向代理認證
config.ssh_config=SSH Configuration
config.ssh_enabled=Enabled
config.ssh_start_builtin_server=Start Builtin Server
config.ssh_domain=Domain
config.ssh_port=Port
config.ssh_listen_port=Listen Port
config.ssh_root_path=Root Path
config.ssh_key_test_path=Key Test Path
config.ssh_keygen_path=Keygen ('ssh-keygen') Path
config.ssh_minimum_key_size_check=Minimum Key Size Check
config.ssh_minimum_key_sizes=Minimum Key Sizes
config.db_config=數據庫配置 config.db_config=數據庫配置
config.db_type=數據庫類型 config.db_type=數據庫類型
config.db_host=主機地址 config.db_host=主機地址
@ -962,7 +989,6 @@ config.register_email_confirm=註冊電子郵件確認
config.disable_register=關閉註冊功能 config.disable_register=關閉註冊功能
config.show_registration_button=顯示註冊按鈕 config.show_registration_button=顯示註冊按鈕
config.require_sign_in_view=強制登錄瀏覽 config.require_sign_in_view=強制登錄瀏覽
config.enable_cache_avatar=開啟緩存頭像
config.mail_notify=郵件通知提醒 config.mail_notify=郵件通知提醒
config.disable_key_size_check=禁用密鑰最小長度檢查 config.disable_key_size_check=禁用密鑰最小長度檢查
config.enable_captcha=啟用驗證碼服務 config.enable_captcha=啟用驗證碼服務
@ -978,6 +1004,9 @@ config.mailer_disable_helo=禁用 HELO 操作
config.mailer_name=發送者名稱 config.mailer_name=發送者名稱
config.mailer_host=郵件主機地址 config.mailer_host=郵件主機地址
config.mailer_user=發送者帳號 config.mailer_user=發送者帳號
config.send_test_mail=Send Test Email
config.test_mail_failed=Fail to send test email to '%s': %v
config.test_mail_sent=Test email has been sent to '%s'.
config.oauth_config=社交帳號配置 config.oauth_config=社交帳號配置
config.oauth_enabled=啟用服務 config.oauth_enabled=啟用服務
config.cache_config=Cache 配置 config.cache_config=Cache 配置
@ -1025,16 +1054,20 @@ notices.op=操作
notices.delete_success=系統提示刪除成功! notices.delete_success=系統提示刪除成功!
[action] [action]
create_repo=創建了庫 <a href="%s">%s</a> create_repo=創建了儲存庫 <a href="%s">%s</a>
rename_repo=重新命名倉庫 <code>%[1]s</code> 為 <a href="%[2]s">%[3]s</a> rename_repo=重新命名倉庫 <code>%[1]s</code> 為 <a href="%[2]s">%[3]s</a>
commit_repo=推送了 <a href="%[1]s/src/%[2]s">%[3]s</a> 分支的代碼到 <a href="%[1]s">%[4]s</a> commit_repo=推送了 <a href="%[1]s/src/%[2]s">%[3]s</a> 分支的代碼到 <a href="%[1]s">%[4]s</a>
create_issue=`創建了問題 <a href="%s/issues/%s">%s#%[2]s</a>` create_issue=`創建了問題 <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`closed issue <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`reopened issue <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`創建了合併請求 <a href="%s/pulls/%s">%s#%[2]s</a>` create_pull_request=`創建了合併請求 <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request=`closed pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`reopened pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
comment_issue=`評論了問題 <a href="%s/issues/%s">%s#%[2]s</a>` comment_issue=`評論了問題 <a href="%s/issues/%s">%s#%[2]s</a>`
merge_pull_request=`合併了合併請求 <a href="%s/pulls/%s">%s#%[2]s</a>` merge_pull_request=`合併了合併請求 <a href="%s/pulls/%s">%s#%[2]s</a>`
transfer_repo=庫 <code>%s</code> 轉移至 <a href="%s">%s</a> transfer_repo=儲存庫 <code>%s</code> 轉移至 <a href="%s">%s</a>
push_tag=推送了標籤 <a href="%s/src/%s">%[2]s</a> 到 <a href="%[1]s">%[3]s</a> push_tag=推送了標籤 <a href="%s/src/%s">%[2]s</a> 到 <a href="%[1]s">%[3]s</a>
compare_commits=View comparison for these %d commits compare_commits=查看 %d 次提交的內容比對
[tool] [tool]
ago=之前 ago=之前

29
docker/README.md

@ -1,10 +1,10 @@
# Docker for Gogs # Docker for Gogs
Visit [Docker Hub](https://hub.docker.com/r/gogs/gogs/) or [Quay](https://quay.io/repository/gogs/gogs) see all available tags. Visit [Docker Hub](https://hub.docker.com/r/gogs/gogs/) see all available tags.
## Usage ## Usage
To keep your data out of Docker container, we do a volume(`/var/gogs` -> `/data`) here, and you can change it based on your situation. To keep your data out of Docker container, we do a volume (`/var/gogs` -> `/data`) here, and you can change it based on your situation.
``` ```
# Pull image from Docker Hub. # Pull image from Docker Hub.
@ -20,6 +20,8 @@ $ docker run --name=gogs -p 10022:22 -p 10080:3000 -v /var/gogs:/data gogs/gogs
$ docker start gogs $ docker start gogs
``` ```
Note: It is important to map the Gogs ssh service from the container to the host and set the appropriate SSH Port and URI settings when setting up Gogs for the first time. To access and clone Gogs Git repositories with the above configuration you would use: `git clone ssh://git@hostname:10022/username/myrepo.git` for example.
Files will be store in local path `/var/gogs` in my case. Files will be store in local path `/var/gogs` in my case.
Directory `/var/gogs` keeps Git repositories and Gogs data: Directory `/var/gogs` keeps Git repositories and Gogs data:
@ -44,19 +46,34 @@ docker run --name=gogs-data --entrypoint /bin/true gogs/gogs
# Use `docker run` for the first time. # Use `docker run` for the first time.
docker run --name=gogs --volumes-from gogs-data -p 10022:22 -p 10080:3000 gogs/gogs docker run --name=gogs --volumes-from gogs-data -p 10022:22 -p 10080:3000 gogs/gogs
``` ```
#### Using Docker 1.9 Volume command
```
# Create docker volume.
$ docker volume create --name gogs-data
# Use `docker run` for the first time.
$ docker run --name=gogs -p 10022:22 -p 10080:3000 -v gogs-data:/data gogs/gogs
```
## Settings ## Settings
### Application
Most of settings are obvious and easy to understand, but there are some settings can be confusing by running Gogs inside Docker: Most of settings are obvious and easy to understand, but there are some settings can be confusing by running Gogs inside Docker:
- **Repository Root Path**: keep it as default value `/home/git/gogs-repositories` because `start.sh` already made a symbolic link for you. - **Repository Root Path**: keep it as default value `/home/git/gogs-repositories` because `start.sh` already made a symbolic link for you.
- **Run User**: keep it as default value `git` because `start.sh` already setup a user with name `git`. - **Run User**: keep it as default value `git` because `start.sh` already setup a user with name `git`.
- **Domain**: fill in with Docker container IP(e.g. `192.168.99.100`). But if you want to access your Gogs instance from a different physical machine, please fill in with the hostname or IP address of the Docker host machine. - **Domain**: fill in with Docker container IP (e.g. `192.168.99.100`). But if you want to access your Gogs instance from a different physical machine, please fill in with the hostname or IP address of the Docker host machine.
- **SSH Port**: Use the exposed port from Docker container. For example, your SSH server listens on `22` inside Docker, but you expose it by `10022:22`, then use `10022` for this value. **Builtin SSH server is not recommended inside Docker Container** - **SSH Port**: Use the exposed port from Docker container. For example, your SSH server listens on `22` inside Docker, but you expose it by `10022:22`, then use `10022` for this value. **Builtin SSH server is not recommended inside Docker Container**
- **HTTP Port**: Use port you want Gogs to listen on inside Docker container. For example, your Gogs listens on `3000` inside Docker, and you expose it by `10080:3000`, but you still use `3000` for this value. - **HTTP Port**: Use port you want Gogs to listen on inside Docker container. For example, your Gogs listens on `3000` inside Docker, and you expose it by `10080:3000`, but you still use `3000` for this value.
- **Application URL**: Use combination of **Domain** and **exposed HTTP Port** values(e.g. `http://192.168.99.100:10080/`). - **Application URL**: Use combination of **Domain** and **exposed HTTP Port** values (e.g. `http://192.168.99.100:10080/`).
Full documentation of application settings can be found [here](http://gogs.io/docs/advanced/configuration_cheat_sheet.html).
### Crond
Full documentation of settings can be found [here](http://gogs.io/docs/advanced/configuration_cheat_sheet.html). Please set environment variable `RUN_CROND` to be `true` or `1` in order to start `crond` inside the container.
## Upgrade ## Upgrade
@ -71,4 +88,4 @@ Steps to upgrade Gogs with Docker:
## Known Issues ## Known Issues
- `.dockerignore` seems to be ignored during Docker Hub Automated build - The docker container can not currently be build on Raspberry 1 (armv6l) as our base image `alpine` does not have a `go` package available for this platform.

2
docker/build.sh

@ -7,7 +7,7 @@ export GOPATH=/tmp/go
export PATH=${PATH}:${GOPATH}/bin export PATH=${PATH}:${GOPATH}/bin
# Install build deps # Install build deps
apk -U --no-progress add --virtual build-deps linux-pam-dev go@community gcc musl-dev apk --no-cache --no-progress add --virtual build-deps linux-pam-dev go gcc musl-dev
# Init go environment to build Gogs # Init go environment to build Gogs
mkdir -p ${GOPATH}/src/github.com/gogits/ mkdir -p ${GOPATH}/src/github.com/gogits/

0
docker/s6/crond/down

9
docker/s6/crond/run

@ -0,0 +1,9 @@
#!/bin/sh
# Crontabs are located by default in /var/spool/cron/crontabs/
# The default configuration is also calling all the scripts in /etc/periodic/${period}
if test -f ./setup; then
source ./setup
fi
exec gosu root /usr/sbin/crond -fS

9
docker/start.sh

@ -48,6 +48,15 @@ else
create_socat_links create_socat_links
fi fi
CROND=$(echo "$RUN_CROND" | tr '[:upper:]' '[:lower:]')
if [ "$CROND" = "true" -o "$CROND" = "1" ]; then
echo "init:crond | Cron Daemon (crond) will be run as requested by s6" 1>&2
rm -f /app/gogs/docker/s6/crond/down
else
# Tell s6 not to run the crond service
touch /app/gogs/docker/s6/crond/down
fi
# Exec CMD or S6 by default if nothing present # Exec CMD or S6 by default if nothing present
if [ $# -gt 0 ];then if [ $# -gt 0 ];then
exec "$@" exec "$@"

144
glide.lock generated

@ -0,0 +1,144 @@
hash: f2fa73b9a379e1fa12f2b48fb0b9942a545b4518a2d71cbd956ee81093347773
updated: 2016-03-19T14:44:26.835671547-04:00
imports:
- name: github.com/bradfitz/gomemcache
version: fb1f79c6b65acda83063cbc69f6bba1522558bfc
subpackages:
- memcache
- name: github.com/codegangsta/cli
version: aca5b047ed14d17224157c3434ea93bf6cdaadee
- name: github.com/go-macaron/binding
version: a68f34212fe257219981e43adfe4c96ab48f42cd
- name: github.com/go-macaron/cache
version: 56173531277692bc2925924d51fda1cd0a6b8178
subpackages:
- memcache
- redis
- name: github.com/go-macaron/captcha
version: 8aa5919789ab301e865595eb4b1114d6b9847deb
- name: github.com/go-macaron/csrf
version: 6a9a7df172cc1fcd81e4585f44b09200b6087cc0
- name: github.com/go-macaron/gzip
version: cad1c6580a07c56f5f6bc52d66002a05985c5854
- name: github.com/go-macaron/i18n
version: d2d3329f13b52314f3292c4cecb601fad13f02c8
- name: github.com/go-macaron/inject
version: c5ab7bf3a307593cd44cb272d1a5beea473dd072
- name: github.com/go-macaron/session
version: 66031fcb37a0fff002a1f028eb0b3a815c78306b
subpackages:
- redis
- name: github.com/go-macaron/toolbox
version: 82b511550b0aefc36b3a28062ad3a52e812bee38
- name: github.com/go-sql-driver/mysql
version: 66312f7fe2678aa0f5ec770f96702f4c4ec5aa8e
- name: github.com/go-xorm/core
version: 502158401cde814951eae62f064d9e5ff39e13ce
- name: github.com/go-xorm/xorm
version: 769f6b3ae663248e8f1b1d8fecbe1eb26ac77ac7
- name: github.com/gogits/chardet
version: 2404f777256163ea3eadb273dada5dcb037993c0
- name: github.com/gogits/cron
version: 3abc0f88f2062336bcc41b43a4febbd847a390ce
- name: github.com/gogits/git-module
version: 76e8cce6c7ef3ba1cf75752261c721ebf14cd129
- name: github.com/gogits/go-gogs-client
version: 788ec59749df076b98e208909b44fdef02779deb
- name: github.com/issue9/identicon
version: f8c0d2ce04db79c663b1da33d3a9f62be753ee88
- name: github.com/kardianos/minwinsvc
version: cad6b2b879b0970e4245a20ebf1a81a756e2bb70
- name: github.com/klauspost/compress
version: 006acde2c5d283d2f8b8aa03d8f0cd2891c680cf
subpackages:
- gzip
- flate
- name: github.com/klauspost/cpuid
version: 09cded8978dc9e80714c4d85b0322337b0a1e5e0
- name: github.com/klauspost/crc32
version: 19b0b332c9e4516a6370a0456e6182c3b5036720
- name: github.com/lib/pq
version: 165a3529e799da61ab10faed1fabff3662d6193f
subpackages:
- oid
- name: github.com/mattn/go-sqlite3
version: 76e335f60bbcee20755df9864f0153af1a80ad2d
- name: github.com/mcuadros/go-version
version: d52711f8d6bea8dc01efafdb68ad95a4e2606630
- name: github.com/microcosm-cc/bluemonday
version: 4ac6f27528d0a3f2a59e0b0a6f6b3ff0bb89fe20
- name: github.com/msteinert/pam
version: 02ccfbfaf0cc627aa3aec8ef7ed5cfeec5b43f63
- name: github.com/nfnt/resize
version: 4d93a29130b1b6aba503e2aa8b50f516213ea80e
- name: github.com/russross/blackfriday
version: b43df972fb5fdf3af8d2e90f38a69d374fe26dd0
- name: github.com/satori/go.uuid
version: e673fdd4dea8a7334adbbe7f57b7e4b00bdc5502
- name: github.com/sergi/go-diff
version: ec7fdbb58eb3e300c8595ad5ac74a5aa50019cc7
subpackages:
- diffmatchpatch
- name: github.com/shurcooL/sanitized_anchor_name
version: 10ef21a441db47d8b13ebcc5fd2310f636973c77
- name: github.com/Unknwon/cae
version: 7f5e046bc8a6c3cde743c233b96ee4fd84ee6ecd
subpackages:
- zip
- name: github.com/Unknwon/com
version: 28b053d5a2923b87ce8c5a08f3af779894a72758
- name: github.com/Unknwon/i18n
version: 3b48b6662051bed72d36efa3c1e897bdf96b2e37
- name: github.com/Unknwon/paginater
version: 7748a72e01415173a27d79866b984328e7b0c12b
- name: golang.org/x/crypto
version: c197bcf24cde29d3f73c7b4ac6fd41f4384e8af6
subpackages:
- ssh
- curve25519
- name: golang.org/x/net
version: 35b06af0720201bc2f326773a80767387544f8c4
subpackages:
- html
- html/charset
- html/atom
- name: golang.org/x/sys
version: 9d4e42a20653790449273b3c85e67d6d8bae6e2e
subpackages:
- windows/svc
- windows
- name: golang.org/x/text
version: 1b466db55e0ba5d56ef5315c728216b42f796491
subpackages:
- transform
- encoding
- encoding/charmap
- encoding/htmlindex
- encoding/internal/identifier
- encoding/internal
- encoding/japanese
- encoding/korean
- encoding/simplifiedchinese
- encoding/traditionalchinese
- encoding/unicode
- language
- internal/utf8internal
- runes
- internal/tag
- name: gopkg.in/alexcesaro/quotedprintable.v3
version: 2caba252f4dc53eaf6b553000885530023f54623
- name: gopkg.in/asn1-ber.v1
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
- name: gopkg.in/bufio.v1
version: 567b2bfa514e796916c4747494d6ff5132a1dfce
- name: gopkg.in/gomail.v2
version: 060a5f4e98dbf37408cf0c745681e4001d877827
- name: gopkg.in/ini.v1
version: 776aa739ce9373377cd16f526cdf06cb4c89b40f
- name: gopkg.in/ldap.v2
version: 07a7330929b9ee80495c88a4439657d89c7dbd87
- name: gopkg.in/macaron.v1
version: 94a5ef7105036242f79e5e07a8eb8651d06c8533
- name: gopkg.in/redis.v2
version: e6179049628164864e6e84e973cfb56335748dea
devImports: []

56
glide.yaml

@ -0,0 +1,56 @@
package: github.com/gogits/gogs
import:
- package: github.com/Unknwon/cae
subpackages:
- zip
- package: github.com/Unknwon/com
- package: github.com/Unknwon/i18n
- package: github.com/Unknwon/paginater
- package: github.com/codegangsta/cli
- package: github.com/go-macaron/binding
- package: github.com/go-macaron/cache
subpackages:
- memcache
- redis
- package: github.com/go-macaron/captcha
- package: github.com/go-macaron/csrf
- package: github.com/go-macaron/gzip
- package: github.com/go-macaron/i18n
- package: github.com/go-macaron/session
subpackages:
- redis
- package: github.com/go-macaron/toolbox
- package: github.com/go-sql-driver/mysql
- package: github.com/go-xorm/core
- package: github.com/go-xorm/xorm
- package: github.com/gogits/chardet
- package: github.com/gogits/cron
- package: github.com/gogits/git-module
- package: github.com/gogits/go-gogs-client
- package: github.com/issue9/identicon
- package: github.com/kardianos/minwinsvc
- package: github.com/lib/pq
- package: github.com/mattn/go-sqlite3
- package: github.com/mcuadros/go-version
- package: github.com/microcosm-cc/bluemonday
- package: github.com/msteinert/pam
- package: github.com/nfnt/resize
- package: github.com/russross/blackfriday
- package: github.com/satori/go.uuid
- package: github.com/sergi/go-diff
subpackages:
- diffmatchpatch
- package: golang.org/x/crypto
subpackages:
- ssh
- package: golang.org/x/net
subpackages:
- html
- html/charset
- package: golang.org/x/text
subpackages:
- transform
- package: gopkg.in/gomail.v2
- package: gopkg.in/ini.v1
- package: gopkg.in/ldap.v2
- package: gopkg.in/macaron.v1

2
gogs.go

@ -17,7 +17,7 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const APP_VER = "0.8.23.0126" const APP_VER = "0.9.15.0323"
func init() { func init() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())

50
models/access.go

@ -13,13 +13,40 @@ import (
type AccessMode int type AccessMode int
const ( const (
ACCESS_MODE_NONE AccessMode = iota ACCESS_MODE_NONE AccessMode = iota // 0
ACCESS_MODE_READ ACCESS_MODE_READ // 1
ACCESS_MODE_WRITE ACCESS_MODE_WRITE // 2
ACCESS_MODE_ADMIN ACCESS_MODE_ADMIN // 3
ACCESS_MODE_OWNER ACCESS_MODE_OWNER // 4
) )
func (mode AccessMode) String() string {
switch mode {
case ACCESS_MODE_READ:
return "read"
case ACCESS_MODE_WRITE:
return "write"
case ACCESS_MODE_ADMIN:
return "admin"
case ACCESS_MODE_OWNER:
return "owner"
default:
return "none"
}
}
// ParseAccessMode returns corresponding access mode to given permission string.
func ParseAccessMode(permission string) AccessMode {
switch permission {
case "write":
return ACCESS_MODE_WRITE
case "admin":
return ACCESS_MODE_ADMIN
default:
return ACCESS_MODE_READ
}
}
// Access represents the highest access level of a user to the repository. The only access type // Access represents the highest access level of a user to the repository. The only access type
// that is not in this table is the real owner of a repository. In case of an organization // that is not in this table is the real owner of a repository. In case of an organization
// repository, the members of the owners team are in this table. // repository, the members of the owners team are in this table.
@ -110,7 +137,7 @@ func (u *User) GetAccessibleRepositories() ([]*Repository, error) {
repoIDs = append(repoIDs, access.RepoID) repoIDs = append(repoIDs, access.RepoID)
} }
repos := make([]*Repository, 0, len(repoIDs)) repos := make([]*Repository, 0, len(repoIDs))
return repos, x.Where("owner_id != ?", u.Id).In("id", repoIDs).Desc("updated").Find(&repos) return repos, x.Where("owner_id != ?", u.Id).In("id", repoIDs).Desc("updated_unix").Find(&repos)
} }
func maxAccessMode(modes ...AccessMode) AccessMode { func maxAccessMode(modes ...AccessMode) AccessMode {
@ -151,15 +178,14 @@ func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode
return nil return nil
} }
// FIXME: should be able to have read-only access. // refreshCollaboratorAccesses retrieves repository collaborations with their access modes.
// Give all collaborators write access.
func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error { func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error {
collaborators, err := repo.getCollaborators(e) collaborations, err := repo.getCollaborations(e)
if err != nil { if err != nil {
return fmt.Errorf("getCollaborators: %v", err) return fmt.Errorf("getCollaborations: %v", err)
} }
for _, c := range collaborators { for _, c := range collaborations {
accessMap[c.Id] = ACCESS_MODE_WRITE accessMap[c.UserID] = c.Mode
} }
return nil return nil
} }

82
models/action.go

@ -28,17 +28,21 @@ import (
type ActionType int type ActionType int
const ( const (
CREATE_REPO ActionType = iota + 1 // 1 ACTION_CREATE_REPO ActionType = iota + 1 // 1
RENAME_REPO // 2 ACTION_RENAME_REPO // 2
STAR_REPO // 3 ACTION_STAR_REPO // 3
FOLLOW_REPO // 4 ACTION_WATCH_REPO // 4
COMMIT_REPO // 5 ACTION_COMMIT_REPO // 5
CREATE_ISSUE // 6 ACTION_CREATE_ISSUE // 6
CREATE_PULL_REQUEST // 7 ACTION_CREATE_PULL_REQUEST // 7
TRANSFER_REPO // 8 ACTION_TRANSFER_REPO // 8
PUSH_TAG // 9 ACTION_PUSH_TAG // 9
COMMENT_ISSUE // 10 ACTION_COMMENT_ISSUE // 10
MERGE_PULL_REQUEST // 11 ACTION_MERGE_PULL_REQUEST // 11
ACTION_CLOSE_ISSUE // 12
ACTION_REOPEN_ISSUE // 13
ACTION_CLOSE_PULL_REQUEST // 14
ACTION_REOPEN_PULL_REQUEST // 15
) )
var ( var (
@ -80,13 +84,18 @@ type Action struct {
RefName string RefName string
IsPrivate bool `xorm:"NOT NULL DEFAULT false"` IsPrivate bool `xorm:"NOT NULL DEFAULT false"`
Content string `xorm:"TEXT"` Content string `xorm:"TEXT"`
Created time.Time `xorm:"created"` Created time.Time `xorm:"-"`
CreatedUnix int64
}
func (a *Action) BeforeInsert() {
a.CreatedUnix = time.Now().UTC().Unix()
} }
func (a *Action) AfterSet(colName string, _ xorm.Cell) { func (a *Action) AfterSet(colName string, _ xorm.Cell) {
switch colName { switch colName {
case "created": case "created_unix":
a.Created = regulateTimeZone(a.Created) a.Created = time.Unix(a.CreatedUnix, 0).Local()
} }
} }
@ -178,7 +187,7 @@ func newRepoAction(e Engine, u *User, repo *Repository) (err error) {
ActUserID: u.Id, ActUserID: u.Id,
ActUserName: u.Name, ActUserName: u.Name,
ActEmail: u.Email, ActEmail: u.Email,
OpType: CREATE_REPO, OpType: ACTION_CREATE_REPO,
RepoID: repo.ID, RepoID: repo.ID,
RepoUserName: repo.Owner.Name, RepoUserName: repo.Owner.Name,
RepoName: repo.Name, RepoName: repo.Name,
@ -201,7 +210,7 @@ func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Reposit
ActUserID: actUser.Id, ActUserID: actUser.Id,
ActUserName: actUser.Name, ActUserName: actUser.Name,
ActEmail: actUser.Email, ActEmail: actUser.Email,
OpType: RENAME_REPO, OpType: ACTION_RENAME_REPO,
RepoID: repo.ID, RepoID: repo.ID,
RepoUserName: repo.Owner.Name, RepoUserName: repo.Owner.Name,
RepoName: repo.Name, RepoName: repo.Name,
@ -366,7 +375,7 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
continue continue
} }
if err = issue.ChangeStatus(u, true); err != nil { if err = issue.ChangeStatus(u, repo, true); err != nil {
return err return err
} }
} }
@ -406,7 +415,7 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
continue continue
} }
if err = issue.ChangeStatus(u, false); err != nil { if err = issue.ChangeStatus(u, repo, false); err != nil {
return err return err
} }
} }
@ -443,10 +452,10 @@ func CommitRepoAction(
} }
isNewBranch := false isNewBranch := false
opType := COMMIT_REPO opType := ACTION_COMMIT_REPO
// Check it's tag push or branch. // Check it's tag push or branch.
if strings.HasPrefix(refFullName, "refs/tags/") { if strings.HasPrefix(refFullName, "refs/tags/") {
opType = PUSH_TAG opType = ACTION_PUSH_TAG
commit = &PushCommits{} commit = &PushCommits{}
} else { } else {
// if not the first commit, set the compareUrl // if not the first commit, set the compareUrl
@ -498,11 +507,11 @@ func CommitRepoAction(
payloadSender := &api.PayloadUser{ payloadSender := &api.PayloadUser{
UserName: pusher.Name, UserName: pusher.Name,
ID: pusher.Id, ID: pusher.Id,
AvatarUrl: setting.AppUrl + pusher.RelAvatarLink(), AvatarUrl: pusher.AvatarLink(),
} }
switch opType { switch opType {
case COMMIT_REPO: // Push case ACTION_COMMIT_REPO: // Push
p := &api.PushPayload{ p := &api.PushPayload{
Ref: refFullName, Ref: refFullName,
Before: oldCommitID, Before: oldCommitID,
@ -530,7 +539,7 @@ func CommitRepoAction(
}) })
} }
case PUSH_TAG: // Create case ACTION_PUSH_TAG: // Create
return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{ return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
Ref: refName, Ref: refName,
RefType: "tag", RefType: "tag",
@ -547,7 +556,7 @@ func transferRepoAction(e Engine, actUser, oldOwner, newOwner *User, repo *Repos
ActUserID: actUser.Id, ActUserID: actUser.Id,
ActUserName: actUser.Name, ActUserName: actUser.Name,
ActEmail: actUser.Email, ActEmail: actUser.Email,
OpType: TRANSFER_REPO, OpType: ACTION_TRANSFER_REPO,
RepoID: repo.ID, RepoID: repo.ID,
RepoUserName: newOwner.Name, RepoUserName: newOwner.Name,
RepoName: repo.Name, RepoName: repo.Name,
@ -578,7 +587,7 @@ func mergePullRequestAction(e Engine, actUser *User, repo *Repository, pull *Iss
ActUserID: actUser.Id, ActUserID: actUser.Id,
ActUserName: actUser.Name, ActUserName: actUser.Name,
ActEmail: actUser.Email, ActEmail: actUser.Email,
OpType: MERGE_PULL_REQUEST, OpType: ACTION_MERGE_PULL_REQUEST,
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Name), Content: fmt.Sprintf("%d|%s", pull.Index, pull.Name),
RepoID: repo.ID, RepoID: repo.ID,
RepoUserName: repo.Owner.Name, RepoUserName: repo.Owner.Name,
@ -593,12 +602,29 @@ func MergePullRequestAction(actUser *User, repo *Repository, pull *Issue) error
} }
// GetFeeds returns action list of given user in given context. // GetFeeds returns action list of given user in given context.
func GetFeeds(uid, offset int64, isProfile bool) ([]*Action, error) { // userID is the user who's requesting, ctxUserID is the user/org that is requested.
// userID can be -1, if isProfile is true or in order to skip the permission check.
func GetFeeds(ctxUserID, userID, offset int64, isProfile bool) ([]*Action, error) {
actions := make([]*Action, 0, 20) actions := make([]*Action, 0, 20)
sess := x.Limit(20, int(offset)).Desc("id").Where("user_id=?", uid) sess := x.Limit(20, int(offset)).Desc("id").Where("user_id=?", ctxUserID)
if isProfile { if isProfile {
sess.And("is_private=?", false).And("act_user_id=?", uid) sess.And("is_private=?", false).And("act_user_id=?", ctxUserID)
} else if ctxUserID != -1 {
ctxUser := &User{Id: ctxUserID}
if err := ctxUser.GetUserRepositories(userID); err != nil {
return nil, err
}
var repoIDs []int64
for _, repo := range ctxUser.Repos {
repoIDs = append(repoIDs, repo.ID)
}
if len(repoIDs) > 0 {
sess.In("repo_id", repoIDs)
}
} }
err := sess.Find(&actions) err := sess.Find(&actions)
return actions, err return actions, err
} }

39
models/admin.go

@ -5,12 +5,18 @@
package models package models
import ( import (
"fmt"
"os"
"os/exec"
"strings" "strings"
"time" "time"
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/go-xorm/xorm"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
) )
type NoticeType int type NoticeType int
@ -24,7 +30,19 @@ type Notice struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
Type NoticeType Type NoticeType
Description string `xorm:"TEXT"` Description string `xorm:"TEXT"`
Created time.Time `xorm:"CREATED"` Created time.Time `xorm:"-"`
CreatedUnix int64
}
func (n *Notice) BeforeInsert() {
n.CreatedUnix = time.Now().UTC().Unix()
}
func (n *Notice) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
n.Created = time.Unix(n.CreatedUnix, 0).Local()
}
} }
// TrStr returns a translation format string. // TrStr returns a translation format string.
@ -47,6 +65,25 @@ func CreateRepositoryNotice(desc string) error {
return CreateNotice(NOTICE_REPOSITORY, desc) return CreateNotice(NOTICE_REPOSITORY, desc)
} }
// RemoveAllWithNotice removes all directories in given path and
// creates a system notice when error occurs.
func RemoveAllWithNotice(title, path string) {
var err error
if setting.IsWindows {
err = exec.Command("cmd", "/C", "rmdir", "/S", "/Q", path).Run()
} else {
err = os.RemoveAll(path)
}
if err != nil {
desc := fmt.Sprintf("%s [%s]: %v", title, path, err)
log.Warn(desc)
if err = CreateRepositoryNotice(desc); err != nil {
log.Error(4, "CreateRepositoryNotice: %v", err)
}
}
}
// CountNotices returns number of notices. // CountNotices returns number of notices.
func CountNotices() int64 { func CountNotices() int64 {
count, _ := x.Count(new(Notice)) count, _ := x.Count(new(Notice))

59
models/cron/cron.go

@ -1,59 +0,0 @@
// Copyright 2014 The Gogs 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 cron
import (
"time"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/cron"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
var c = cron.New()
func NewContext() {
var (
entry *cron.Entry
err error
)
if setting.Cron.UpdateMirror.Enabled {
entry, err = c.AddFunc("Update mirrors", setting.Cron.UpdateMirror.Schedule, models.MirrorUpdate)
if err != nil {
log.Fatal(4, "Cron[Update mirrors]: %v", err)
}
if setting.Cron.UpdateMirror.RunAtStart {
entry.Prev = time.Now()
go models.MirrorUpdate()
}
}
if setting.Cron.RepoHealthCheck.Enabled {
entry, err = c.AddFunc("Repository health check", setting.Cron.RepoHealthCheck.Schedule, models.GitFsck)
if err != nil {
log.Fatal(4, "Cron[Repository health check]: %v", err)
}
if setting.Cron.RepoHealthCheck.RunAtStart {
entry.Prev = time.Now()
go models.GitFsck()
}
}
if setting.Cron.CheckRepoStats.Enabled {
entry, err = c.AddFunc("Check repository statistics", setting.Cron.CheckRepoStats.Schedule, models.CheckRepoStats)
if err != nil {
log.Fatal(4, "Cron[Check repository statistics]: %v", err)
}
if setting.Cron.CheckRepoStats.RunAtStart {
entry.Prev = time.Now()
go models.CheckRepoStats()
}
}
c.Start()
}
// ListTasks returns all running cron tasks.
func ListTasks() []*cron.Entry {
return c.Entries()
}

43
models/error.go

@ -392,6 +392,26 @@ func (err ErrReleaseNotExist) Error() string {
return fmt.Sprintf("Release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName) return fmt.Sprintf("Release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
} }
// __________ .__
// \______ \____________ ____ ____ | |__
// | | _/\_ __ \__ \ / \_/ ___\| | \
// | | \ | | \// __ \| | \ \___| Y \
// |______ / |__| (____ /___| /\___ >___| /
// \/ \/ \/ \/ \/
type ErrBranchNotExist struct {
Name string
}
func IsErrBranchNotExist(err error) bool {
_, ok := err.(ErrBranchNotExist)
return ok
}
func (err ErrBranchNotExist) Error() string {
return fmt.Sprintf("Branch does not exist [name: %s]", err.Name)
}
// __ __ ___. .__ __ // __ __ ___. .__ __
// / \ / \ ____\_ |__ | |__ ____ ____ | | __ // / \ / \ ____\_ |__ | |__ ____ ____ | | __
// \ \/\/ // __ \| __ \| | \ / _ \ / _ \| |/ / // \ \/\/ // __ \| __ \| | \ / _ \ / _ \| |/ /
@ -559,5 +579,26 @@ func IsErrAuthenticationNotExist(err error) bool {
} }
func (err ErrAuthenticationNotExist) Error() string { func (err ErrAuthenticationNotExist) Error() string {
return fmt.Sprintf("Authentication does not exist [id: %d]", err.ID) return fmt.Sprintf("authentication does not exist [id: %d]", err.ID)
}
// ___________
// \__ ___/___ _____ _____
// | |_/ __ \\__ \ / \
// | |\ ___/ / __ \| Y Y \
// |____| \___ >____ /__|_| /
// \/ \/ \/
type ErrTeamAlreadyExist struct {
OrgID int64
Name string
}
func IsErrTeamAlreadyExist(err error) bool {
_, ok := err.(ErrTeamAlreadyExist)
return ok
}
func (err ErrTeamAlreadyExist) Error() string {
return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name)
} }

66
models/git_diff.go

@ -26,6 +26,7 @@ import (
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process" "github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/template/highlight"
) )
type DiffLineType uint8 type DiffLineType uint8
@ -51,7 +52,6 @@ type DiffLine struct {
RightIdx int RightIdx int
Type DiffLineType Type DiffLineType
Content string Content string
ParsedContent template.HTML
} }
func (d *DiffLine) GetType() int { func (d *DiffLine) GetType() int {
@ -112,42 +112,42 @@ func (diffSection *DiffSection) GetLine(lineType DiffLineType, idx int) *DiffLin
return nil return nil
} }
// computes diff of each diff line and set the HTML on diffLine.ParsedContent // computes inline diff for the given line
func (diffSection *DiffSection) ComputeLinesDiff() { func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) template.HTML {
for _, diffLine := range diffSection.Lines { var compareDiffLine *DiffLine
var compareDiffLine *DiffLine var diff1, diff2 string
var diff1, diff2 string
diffLine.ParsedContent = template.HTML(html.EscapeString(diffLine.Content[1:])) getDefaultReturn := func() template.HTML {
return template.HTML(html.EscapeString(diffLine.Content[1:]))
}
// just compute diff for adds and removes // just compute diff for adds and removes
if diffLine.Type != DIFF_LINE_ADD && diffLine.Type != DIFF_LINE_DEL { if diffLine.Type != DIFF_LINE_ADD && diffLine.Type != DIFF_LINE_DEL {
continue return getDefaultReturn()
} }
// try to find equivalent diff line. ignore, otherwise // try to find equivalent diff line. ignore, otherwise
if diffLine.Type == DIFF_LINE_ADD { if diffLine.Type == DIFF_LINE_ADD {
compareDiffLine = diffSection.GetLine(DIFF_LINE_DEL, diffLine.RightIdx) compareDiffLine = diffSection.GetLine(DIFF_LINE_DEL, diffLine.RightIdx)
if compareDiffLine == nil { if compareDiffLine == nil {
continue return getDefaultReturn()
} }
diff1 = compareDiffLine.Content diff1 = compareDiffLine.Content
diff2 = diffLine.Content diff2 = diffLine.Content
} else { } else {
compareDiffLine = diffSection.GetLine(DIFF_LINE_ADD, diffLine.LeftIdx) compareDiffLine = diffSection.GetLine(DIFF_LINE_ADD, diffLine.LeftIdx)
if compareDiffLine == nil { if compareDiffLine == nil {
continue return getDefaultReturn()
}
diff1 = diffLine.Content
diff2 = compareDiffLine.Content
} }
diff1 = diffLine.Content
diff2 = compareDiffLine.Content
}
dmp := diffmatchpatch.New() dmp := diffmatchpatch.New()
diffRecord := dmp.DiffMain(diff1[1:], diff2[1:], true) diffRecord := dmp.DiffMain(diff1[1:], diff2[1:], true)
diffRecord = dmp.DiffCleanupSemantic(diffRecord) diffRecord = dmp.DiffCleanupSemantic(diffRecord)
diffLine.ParsedContent = diffToHTML(diffRecord, diffLine.Type) return diffToHTML(diffRecord, diffLine.Type)
}
} }
type DiffFile struct { type DiffFile struct {
@ -167,6 +167,10 @@ func (diffFile *DiffFile) GetType() int {
return int(diffFile.Type) return int(diffFile.Type)
} }
func (diffFile *DiffFile) GetHighlightClass() string {
return highlight.FileNameToHighlightClass(diffFile.Name)
}
type Diff struct { type Diff struct {
TotalAddition, TotalDeletion int TotalAddition, TotalDeletion int
Files []*DiffFile Files []*DiffFile

100
models/git_diff_test.go

@ -1,70 +1,70 @@
package models package models
import ( import (
dmp "github.com/sergi/go-diff/diffmatchpatch" dmp "github.com/sergi/go-diff/diffmatchpatch"
"html/template" "html/template"
"testing" "testing"
) )
func assertEqual(t *testing.T, s1 string, s2 template.HTML) { func assertEqual(t *testing.T, s1 string, s2 template.HTML) {
if s1 != string(s2) { if s1 != string(s2) {
t.Errorf("%s should be equal %s", s2, s1) t.Errorf("%s should be equal %s", s2, s1)
} }
} }
func assertLineEqual(t *testing.T, d1 *DiffLine, d2 *DiffLine) { func assertLineEqual(t *testing.T, d1 *DiffLine, d2 *DiffLine) {
if d1 != d2 { if d1 != d2 {
t.Errorf("%v should be equal %v", d1, d2) t.Errorf("%v should be equal %v", d1, d2)
} }
} }
func TestDiffToHTML(t *testing.T) { func TestDiffToHTML(t *testing.T) {
assertEqual(t, "foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{ assertEqual(t, "foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{
dmp.Diff{dmp.DiffEqual, "foo "}, dmp.Diff{dmp.DiffEqual, "foo "},
dmp.Diff{dmp.DiffInsert, "bar"}, dmp.Diff{dmp.DiffInsert, "bar"},
dmp.Diff{dmp.DiffDelete, " baz"}, dmp.Diff{dmp.DiffDelete, " baz"},
dmp.Diff{dmp.DiffEqual, " biz"}, dmp.Diff{dmp.DiffEqual, " biz"},
}, DIFF_LINE_ADD)) }, DIFF_LINE_ADD))
assertEqual(t, "foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{ assertEqual(t, "foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
dmp.Diff{dmp.DiffEqual, "foo "}, dmp.Diff{dmp.DiffEqual, "foo "},
dmp.Diff{dmp.DiffDelete, "bar"}, dmp.Diff{dmp.DiffDelete, "bar"},
dmp.Diff{dmp.DiffInsert, " baz"}, dmp.Diff{dmp.DiffInsert, " baz"},
dmp.Diff{dmp.DiffEqual, " biz"}, dmp.Diff{dmp.DiffEqual, " biz"},
}, DIFF_LINE_DEL)) }, DIFF_LINE_DEL))
} }
// test if GetLine is return the correct lines // test if GetLine is return the correct lines
func TestGetLine(t *testing.T) { func TestGetLine(t *testing.T) {
ds := DiffSection{Lines: []*DiffLine{ ds := DiffSection{Lines: []*DiffLine{
&DiffLine{LeftIdx: 28, RightIdx: 28, Type: DIFF_LINE_PLAIN}, &DiffLine{LeftIdx: 28, RightIdx: 28, Type: DIFF_LINE_PLAIN},
&DiffLine{LeftIdx: 29, RightIdx: 29, Type: DIFF_LINE_PLAIN}, &DiffLine{LeftIdx: 29, RightIdx: 29, Type: DIFF_LINE_PLAIN},
&DiffLine{LeftIdx: 30, RightIdx: 30, Type: DIFF_LINE_PLAIN}, &DiffLine{LeftIdx: 30, RightIdx: 30, Type: DIFF_LINE_PLAIN},
&DiffLine{LeftIdx: 31, RightIdx: 0, Type: DIFF_LINE_DEL}, &DiffLine{LeftIdx: 31, RightIdx: 0, Type: DIFF_LINE_DEL},
&DiffLine{LeftIdx: 0, RightIdx: 31, Type: DIFF_LINE_ADD}, &DiffLine{LeftIdx: 0, RightIdx: 31, Type: DIFF_LINE_ADD},
&DiffLine{LeftIdx: 0, RightIdx: 32, Type: DIFF_LINE_ADD}, &DiffLine{LeftIdx: 0, RightIdx: 32, Type: DIFF_LINE_ADD},
&DiffLine{LeftIdx: 32, RightIdx: 33, Type: DIFF_LINE_PLAIN}, &DiffLine{LeftIdx: 32, RightIdx: 33, Type: DIFF_LINE_PLAIN},
&DiffLine{LeftIdx: 33, RightIdx: 0, Type: DIFF_LINE_DEL}, &DiffLine{LeftIdx: 33, RightIdx: 0, Type: DIFF_LINE_DEL},
&DiffLine{LeftIdx: 34, RightIdx: 0, Type: DIFF_LINE_DEL}, &DiffLine{LeftIdx: 34, RightIdx: 0, Type: DIFF_LINE_DEL},
&DiffLine{LeftIdx: 35, RightIdx: 0, Type: DIFF_LINE_DEL}, &DiffLine{LeftIdx: 35, RightIdx: 0, Type: DIFF_LINE_DEL},
&DiffLine{LeftIdx: 36, RightIdx: 0, Type: DIFF_LINE_DEL}, &DiffLine{LeftIdx: 36, RightIdx: 0, Type: DIFF_LINE_DEL},
&DiffLine{LeftIdx: 0, RightIdx: 34, Type: DIFF_LINE_ADD}, &DiffLine{LeftIdx: 0, RightIdx: 34, Type: DIFF_LINE_ADD},
&DiffLine{LeftIdx: 0, RightIdx: 35, Type: DIFF_LINE_ADD}, &DiffLine{LeftIdx: 0, RightIdx: 35, Type: DIFF_LINE_ADD},
&DiffLine{LeftIdx: 0, RightIdx: 36, Type: DIFF_LINE_ADD}, &DiffLine{LeftIdx: 0, RightIdx: 36, Type: DIFF_LINE_ADD},
&DiffLine{LeftIdx: 0, RightIdx: 37, Type: DIFF_LINE_ADD}, &DiffLine{LeftIdx: 0, RightIdx: 37, Type: DIFF_LINE_ADD},
&DiffLine{LeftIdx: 37, RightIdx: 38, Type: DIFF_LINE_PLAIN}, &DiffLine{LeftIdx: 37, RightIdx: 38, Type: DIFF_LINE_PLAIN},
&DiffLine{LeftIdx: 38, RightIdx: 39, Type: DIFF_LINE_PLAIN}, &DiffLine{LeftIdx: 38, RightIdx: 39, Type: DIFF_LINE_PLAIN},
}} }}
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 31), ds.Lines[4]) assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 31), ds.Lines[4])
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 31), ds.Lines[3]) assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 31), ds.Lines[3])
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 33), ds.Lines[11]) assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 33), ds.Lines[11])
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 34), ds.Lines[12]) assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 34), ds.Lines[12])
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 35), ds.Lines[13]) assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 35), ds.Lines[13])
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 36), ds.Lines[14]) assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 36), ds.Lines[14])
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 34), ds.Lines[7]) assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 34), ds.Lines[7])
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 35), ds.Lines[8]) assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 35), ds.Lines[8])
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 36), ds.Lines[9]) assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 36), ds.Lines[9])
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 37), ds.Lines[10]) assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 37), ds.Lines[10])
} }

674
models/issue.go

@ -17,11 +17,11 @@ import (
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
gouuid "github.com/satori/go.uuid"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
gouuid "github.com/gogits/gogs/modules/uuid"
) )
var ( var (
@ -52,14 +52,28 @@ type Issue struct {
RenderedContent string `xorm:"-"` RenderedContent string `xorm:"-"`
Priority int Priority int
NumComments int NumComments int
Deadline time.Time
Created time.Time `xorm:"CREATED"` Deadline time.Time `xorm:"-"`
Updated time.Time `xorm:"UPDATED"` DeadlineUnix int64
Created time.Time `xorm:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-"`
UpdatedUnix int64
Attachments []*Attachment `xorm:"-"` Attachments []*Attachment `xorm:"-"`
Comments []*Comment `xorm:"-"` Comments []*Comment `xorm:"-"`
} }
func (i *Issue) BeforeInsert() {
i.CreatedUnix = time.Now().UTC().Unix()
i.UpdatedUnix = i.CreatedUnix
}
func (i *Issue) BeforeUpdate() {
i.UpdatedUnix = time.Now().UTC().Unix()
i.DeadlineUnix = i.Deadline.UTC().Unix()
}
func (i *Issue) AfterSet(colName string, _ xorm.Cell) { func (i *Issue) AfterSet(colName string, _ xorm.Cell) {
var err error var err error
switch colName { switch colName {
@ -74,6 +88,23 @@ func (i *Issue) AfterSet(colName string, _ xorm.Cell) {
log.Error(3, "GetCommentsByIssueID[%d]: %v", i.ID, err) log.Error(3, "GetCommentsByIssueID[%d]: %v", i.ID, err)
} }
i.Labels, err = GetLabelsByIssueID(i.ID)
if err != nil {
log.Error(3, "GetLabelsByIssueID[%d]: %v", i.ID, err)
}
case "poster_id":
i.Poster, err = GetUserByID(i.PosterID)
if err != nil {
if IsErrUserNotExist(err) {
i.PosterID = -1
i.Poster = NewFakeUser()
} else {
log.Error(3, "GetUserByID[%d]: %v", i.ID, err)
}
return
}
case "milestone_id": case "milestone_id":
if i.MilestoneID == 0 { if i.MilestoneID == 0 {
return return
@ -83,6 +114,7 @@ func (i *Issue) AfterSet(colName string, _ xorm.Cell) {
if err != nil { if err != nil {
log.Error(3, "GetMilestoneById[%d]: %v", i.ID, err) log.Error(3, "GetMilestoneById[%d]: %v", i.ID, err)
} }
case "assignee_id": case "assignee_id":
if i.AssigneeID == 0 { if i.AssigneeID == 0 {
return return
@ -92,8 +124,13 @@ func (i *Issue) AfterSet(colName string, _ xorm.Cell) {
if err != nil { if err != nil {
log.Error(3, "GetUserByID[%d]: %v", i.ID, err) log.Error(3, "GetUserByID[%d]: %v", i.ID, err)
} }
case "created":
i.Created = regulateTimeZone(i.Created) case "deadline_unix":
i.Deadline = time.Unix(i.DeadlineUnix, 0).Local()
case "created_unix":
i.Created = time.Unix(i.CreatedUnix, 0).Local()
case "updated_unix":
i.Updated = time.Unix(i.UpdatedUnix, 0).Local()
} }
} }
@ -102,21 +139,19 @@ func (i *Issue) HashTag() string {
return "issue-" + com.ToStr(i.ID) return "issue-" + com.ToStr(i.ID)
} }
// State returns string representation of issue status.
func (i *Issue) State() string {
if i.IsClosed {
return "closed"
}
return "open"
}
// IsPoster returns true if given user by ID is the poster. // IsPoster returns true if given user by ID is the poster.
func (i *Issue) IsPoster(uid int64) bool { func (i *Issue) IsPoster(uid int64) bool {
return i.PosterID == uid return i.PosterID == uid
} }
func (i *Issue) GetPoster() (err error) {
i.Poster, err = GetUserByID(i.PosterID)
if IsErrUserNotExist(err) {
i.PosterID = -1
i.Poster = NewFakeUser()
return nil
}
return err
}
func (i *Issue) hasLabel(e Engine, labelID int64) bool { func (i *Issue) hasLabel(e Engine, labelID int64) bool {
return hasIssueLabel(e, i.ID, labelID) return hasIssueLabel(e, i.ID, labelID)
} }
@ -157,11 +192,6 @@ func (i *Issue) getLabels(e Engine) (err error) {
return nil return nil
} }
// GetLabels retrieves all labels of issue and assign to corresponding field.
func (i *Issue) GetLabels() error {
return i.getLabels(x)
}
func (i *Issue) removeLabel(e *xorm.Session, label *Label) error { func (i *Issue) removeLabel(e *xorm.Session, label *Label) error {
return deleteIssueLabel(e, i, label) return deleteIssueLabel(e, i, label)
} }
@ -218,7 +248,8 @@ func (i *Issue) ReadBy(uid int64) error {
return UpdateIssueUserByRead(uid, i.ID) return UpdateIssueUserByRead(uid, i.ID)
} }
func (i *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (err error) { func (i *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository, isClosed bool) (err error) {
// Nothing should be performed if current status is same as target status
if i.IsClosed == isClosed { if i.IsClosed == isClosed {
return nil return nil
} }
@ -230,7 +261,7 @@ func (i *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (err er
return err return err
} }
// Update labels. // Update issue count of labels
if err = i.getLabels(e); err != nil { if err = i.getLabels(e); err != nil {
return err return err
} }
@ -245,28 +276,28 @@ func (i *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (err er
} }
} }
// Update milestone. // Update issue count of milestone
if err = changeMilestoneIssueStats(e, i); err != nil { if err = changeMilestoneIssueStats(e, i); err != nil {
return err return err
} }
// New action comment. // New action comment
if _, err = createStatusComment(e, doer, i.Repo, i); err != nil { if _, err = createStatusComment(e, doer, repo, i); err != nil {
return err return err
} }
return nil return nil
} }
// ChangeStatus changes issue status to open/closed. // ChangeStatus changes issue status to open or closed.
func (i *Issue) ChangeStatus(doer *User, isClosed bool) (err error) { func (i *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (err error) {
sess := x.NewSession() sess := x.NewSession()
defer sessionRelease(sess) defer sessionRelease(sess)
if err = sess.Begin(); err != nil { if err = sess.Begin(); err != nil {
return err return err
} }
if err = i.changeStatus(sess, doer, isClosed); err != nil { if err = i.changeStatus(sess, doer, repo, isClosed); err != nil {
return err return err
} }
@ -284,6 +315,19 @@ func (i *Issue) GetPullRequest() (err error) {
// It's caller's responsibility to create action. // It's caller's responsibility to create action.
func newIssue(e *xorm.Session, repo *Repository, issue *Issue, labelIDs []int64, uuids []string, isPull bool) (err error) { func newIssue(e *xorm.Session, repo *Repository, issue *Issue, labelIDs []int64, uuids []string, isPull bool) (err error) {
issue.Name = strings.TrimSpace(issue.Name)
issue.Index = repo.NextIssueIndex()
if issue.AssigneeID > 0 {
// Silently drop invalid assignee
valid, err := hasAccess(e, &User{Id: issue.AssigneeID}, repo, ACCESS_MODE_WRITE)
if err != nil {
return fmt.Errorf("hasAccess: %v", err)
} else if !valid {
issue.AssigneeID = 0
}
}
if _, err = e.Insert(issue); err != nil { if _, err = e.Insert(issue); err != nil {
return err return err
} }
@ -297,20 +341,23 @@ func newIssue(e *xorm.Session, repo *Repository, issue *Issue, labelIDs []int64,
return err return err
} }
var label *Label if len(labelIDs) > 0 {
for _, id := range labelIDs { // During the session, SQLite3 dirver cannot handle retrieve objects after update something.
if id == 0 { // So we have to get all needed labels first.
continue labels := make([]*Label, 0, len(labelIDs))
if err = e.In("id", labelIDs).Find(&labels); err != nil {
return fmt.Errorf("find all labels: %v", err)
} }
label, err = getLabelByID(e, id) for _, label := range labels {
if err != nil { if label.RepoID != repo.ID {
return err continue
} }
if err = issue.addLabel(e, label); err != nil {
return fmt.Errorf("addLabel: %v", err)
}
if err = issue.addLabel(e, label); err != nil {
return fmt.Errorf("addLabel: %v", err)
}
}
} }
if issue.MilestoneID > 0 { if issue.MilestoneID > 0 {
@ -359,23 +406,27 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string)
return fmt.Errorf("newIssue: %v", err) return fmt.Errorf("newIssue: %v", err)
} }
if err = sess.Commit(); err != nil {
return fmt.Errorf("Commit: %v", err)
}
// Notify watchers. // Notify watchers.
act := &Action{ act := &Action{
ActUserID: issue.Poster.Id, ActUserID: issue.Poster.Id,
ActUserName: issue.Poster.Name, ActUserName: issue.Poster.Name,
ActEmail: issue.Poster.Email, ActEmail: issue.Poster.Email,
OpType: CREATE_ISSUE, OpType: ACTION_CREATE_ISSUE,
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Name), Content: fmt.Sprintf("%d|%s", issue.Index, issue.Name),
RepoID: repo.ID, RepoID: repo.ID,
RepoUserName: repo.Owner.Name, RepoUserName: repo.Owner.Name,
RepoName: repo.Name, RepoName: repo.Name,
IsPrivate: repo.IsPrivate, IsPrivate: repo.IsPrivate,
} }
if err = notifyWatchers(sess, act); err != nil { if err = NotifyWatchers(act); err != nil {
return err log.Error(4, "notifyWatchers: %v", err)
} }
return sess.Commit() return nil
} }
// GetIssueByRef returns an Issue specified by a GFM reference. // GetIssueByRef returns an Issue specified by a GFM reference.
@ -449,6 +500,10 @@ type IssuesOptions struct {
// Issues returns a list of issues by given conditions. // Issues returns a list of issues by given conditions.
func Issues(opts *IssuesOptions) ([]*Issue, error) { func Issues(opts *IssuesOptions) ([]*Issue, error) {
if opts.Page <= 0 {
opts.Page = 1
}
sess := x.Limit(setting.IssuePagingNum, (opts.Page-1)*setting.IssuePagingNum) sess := x.Limit(setting.IssuePagingNum, (opts.Page-1)*setting.IssuePagingNum)
if opts.RepoID > 0 { if opts.RepoID > 0 {
@ -477,11 +532,11 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
switch opts.SortType { switch opts.SortType {
case "oldest": case "oldest":
sess.Asc("created") sess.Asc("created_unix")
case "recentupdate": case "recentupdate":
sess.Desc("updated") sess.Desc("updated_unix")
case "leastupdate": case "leastupdate":
sess.Asc("updated") sess.Asc("updated_unix")
case "mostcomment": case "mostcomment":
sess.Desc("num_comments") sess.Desc("num_comments")
case "leastcomment": case "leastcomment":
@ -489,7 +544,7 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
case "priority": case "priority":
sess.Desc("priority") sess.Desc("priority")
default: default:
sess.Desc("created") sess.Desc("created_unix")
} }
labelIDs := base.StringsToInt64s(strings.Split(opts.Labels, ",")) labelIDs := base.StringsToInt64s(strings.Split(opts.Labels, ","))
@ -860,7 +915,7 @@ func UpdateIssue(issue *Issue) error {
return updateIssue(x, issue) return updateIssue(x, issue)
} }
// updateIssueCols updates specific fields of given issue. // updateIssueCols only updates values of specific columns for given issue.
func updateIssueCols(e Engine, issue *Issue, cols ...string) error { func updateIssueCols(e Engine, issue *Issue, cols ...string) error {
_, err := e.Id(issue.ID).Cols(cols...).Update(issue) _, err := e.Id(issue.ID).Cols(cols...).Update(issue)
return err return err
@ -934,213 +989,6 @@ func UpdateIssueUsersByMentions(uids []int64, iid int64) error {
return nil return nil
} }
// .____ ___. .__
// | | _____ \_ |__ ____ | |
// | | \__ \ | __ \_/ __ \| |
// | |___ / __ \| \_\ \ ___/| |__
// |_______ (____ /___ /\___ >____/
// \/ \/ \/ \/
// Label represents a label of repository for issues.
type Label struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX"`
Name string
Color string `xorm:"VARCHAR(7)"`
NumIssues int
NumClosedIssues int
NumOpenIssues int `xorm:"-"`
IsChecked bool `xorm:"-"`
}
// CalOpenIssues calculates the open issues of label.
func (m *Label) CalOpenIssues() {
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
}
// NewLabel creates new label of repository.
func NewLabel(l *Label) error {
_, err := x.Insert(l)
return err
}
func getLabelByID(e Engine, id int64) (*Label, error) {
if id <= 0 {
return nil, ErrLabelNotExist{id}
}
l := &Label{ID: id}
has, err := x.Get(l)
if err != nil {
return nil, err
} else if !has {
return nil, ErrLabelNotExist{l.ID}
}
return l, nil
}
// GetLabelByID returns a label by given ID.
func GetLabelByID(id int64) (*Label, error) {
return getLabelByID(x, id)
}
// GetLabelsByRepoID returns all labels that belong to given repository by ID.
func GetLabelsByRepoID(repoID int64) ([]*Label, error) {
labels := make([]*Label, 0, 10)
return labels, x.Where("repo_id=?", repoID).Find(&labels)
}
func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
issueLabels, err := getIssueLabels(e, issueID)
if err != nil {
return nil, fmt.Errorf("getIssueLabels: %v", err)
}
var label *Label
labels := make([]*Label, 0, len(issueLabels))
for idx := range issueLabels {
label, err = getLabelByID(e, issueLabels[idx].LabelID)
if err != nil && !IsErrLabelNotExist(err) {
return nil, fmt.Errorf("getLabelByID: %v", err)
}
labels = append(labels, label)
}
return labels, nil
}
// GetLabelsByIssueID returns all labels that belong to given issue by ID.
func GetLabelsByIssueID(issueID int64) ([]*Label, error) {
return getLabelsByIssueID(x, issueID)
}
func updateLabel(e Engine, l *Label) error {
_, err := e.Id(l.ID).AllCols().Update(l)
return err
}
// UpdateLabel updates label information.
func UpdateLabel(l *Label) error {
return updateLabel(x, l)
}
// DeleteLabel delete a label of given repository.
func DeleteLabel(repoID, labelID int64) error {
l, err := GetLabelByID(labelID)
if err != nil {
if IsErrLabelNotExist(err) {
return nil
}
return err
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if _, err = x.Where("label_id=?", labelID).Delete(new(IssueLabel)); err != nil {
return err
} else if _, err = sess.Delete(l); err != nil {
return err
}
return sess.Commit()
}
// .___ .____ ___. .__
// | | ______ ________ __ ____ | | _____ \_ |__ ____ | |
// | |/ ___// ___/ | \_/ __ \| | \__ \ | __ \_/ __ \| |
// | |\___ \ \___ \| | /\ ___/| |___ / __ \| \_\ \ ___/| |__
// |___/____ >____ >____/ \___ >_______ (____ /___ /\___ >____/
// \/ \/ \/ \/ \/ \/ \/
// IssueLabel represetns an issue-lable relation.
type IssueLabel struct {
ID int64 `xorm:"pk autoincr"`
IssueID int64 `xorm:"UNIQUE(s)"`
LabelID int64 `xorm:"UNIQUE(s)"`
}
func hasIssueLabel(e Engine, issueID, labelID int64) bool {
has, _ := e.Where("issue_id=? AND label_id=?", issueID, labelID).Get(new(IssueLabel))
return has
}
// HasIssueLabel returns true if issue has been labeled.
func HasIssueLabel(issueID, labelID int64) bool {
return hasIssueLabel(x, issueID, labelID)
}
func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
if _, err = e.Insert(&IssueLabel{
IssueID: issue.ID,
LabelID: label.ID,
}); err != nil {
return err
}
label.NumIssues++
if issue.IsClosed {
label.NumClosedIssues++
}
return updateLabel(e, label)
}
// NewIssueLabel creates a new issue-label relation.
func NewIssueLabel(issue *Issue, label *Label) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if err = newIssueLabel(sess, issue, label); err != nil {
return err
}
return sess.Commit()
}
func getIssueLabels(e Engine, issueID int64) ([]*IssueLabel, error) {
issueLabels := make([]*IssueLabel, 0, 10)
return issueLabels, e.Where("issue_id=?", issueID).Asc("label_id").Find(&issueLabels)
}
// GetIssueLabels returns all issue-label relations of given issue by ID.
func GetIssueLabels(issueID int64) ([]*IssueLabel, error) {
return getIssueLabels(x, issueID)
}
func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
if _, err = e.Delete(&IssueLabel{
IssueID: issue.ID,
LabelID: label.ID,
}); err != nil {
return err
}
label.NumIssues--
if issue.IsClosed {
label.NumClosedIssues--
}
return updateLabel(e, label)
}
// DeleteIssueLabel deletes issue-label relation.
func DeleteIssueLabel(issue *Issue, label *Label) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if err = deleteIssueLabel(sess, issue, label); err != nil {
return err
}
return sess.Commit()
}
// _____ .__.__ __ // _____ .__.__ __
// / \ |__| | ____ _______/ |_ ____ ____ ____ // / \ |__| | ____ _______/ |_ ____ ____ ____
// / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \ // / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \
@ -1158,12 +1006,19 @@ type Milestone struct {
IsClosed bool IsClosed bool
NumIssues int NumIssues int
NumClosedIssues int NumClosedIssues int
NumOpenIssues int `xorm:"-"` NumOpenIssues int `xorm:"-"`
Completeness int // Percentage(1-100). Completeness int // Percentage(1-100).
Deadline time.Time IsOverDue bool `xorm:"-"`
DeadlineString string `xorm:"-"`
IsOverDue bool `xorm:"-"` DeadlineString string `xorm:"-"`
ClosedDate time.Time Deadline time.Time `xorm:"-"`
DeadlineUnix int64
ClosedDate time.Time `xorm:"-"`
ClosedDateUnix int64
}
func (m *Milestone) BeforeInsert() {
m.DeadlineUnix = m.Deadline.UTC().Unix()
} }
func (m *Milestone) BeforeUpdate() { func (m *Milestone) BeforeUpdate() {
@ -1172,24 +1027,38 @@ func (m *Milestone) BeforeUpdate() {
} else { } else {
m.Completeness = 0 m.Completeness = 0
} }
m.DeadlineUnix = m.Deadline.UTC().Unix()
m.ClosedDateUnix = m.ClosedDate.UTC().Unix()
} }
func (m *Milestone) AfterSet(colName string, _ xorm.Cell) { func (m *Milestone) AfterSet(colName string, _ xorm.Cell) {
if colName == "deadline" { switch colName {
case "num_closed_issues":
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
case "deadline_unix":
m.Deadline = time.Unix(m.DeadlineUnix, 0).Local()
if m.Deadline.Year() == 9999 { if m.Deadline.Year() == 9999 {
return return
} }
m.DeadlineString = m.Deadline.Format("2006-01-02") m.DeadlineString = m.Deadline.Format("2006-01-02")
if time.Now().After(m.Deadline) { if time.Now().Local().After(m.Deadline) {
m.IsOverDue = true m.IsOverDue = true
} }
case "closed_date_unix":
m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local()
} }
} }
// CalOpenIssues calculates the open issues of milestone. // State returns string representation of milestone status.
func (m *Milestone) CalOpenIssues() { func (m *Milestone) State() string {
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues if m.IsClosed {
return "closed"
}
return "open"
} }
// NewMilestone creates new milestone of repository. // NewMilestone creates new milestone of repository.
@ -1450,262 +1319,6 @@ func DeleteMilestoneByID(id int64) error {
return sess.Commit() return sess.Commit()
} }
// _________ __
// \_ ___ \ ____ _____ _____ ____ _____/ |_
// / \ \/ / _ \ / \ / \_/ __ \ / \ __\
// \ \___( <_> ) Y Y \ Y Y \ ___/| | \ |
// \______ /\____/|__|_| /__|_| /\___ >___| /__|
// \/ \/ \/ \/ \/
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
type CommentType int
const (
// Plain comment, can be associated with a commit (CommitId > 0) and a line (Line > 0)
COMMENT_TYPE_COMMENT CommentType = iota
COMMENT_TYPE_REOPEN
COMMENT_TYPE_CLOSE
// References.
COMMENT_TYPE_ISSUE_REF
// Reference from a commit (not part of a pull request)
COMMENT_TYPE_COMMIT_REF
// Reference from a comment
COMMENT_TYPE_COMMENT_REF
// Reference from a pull request
COMMENT_TYPE_PULL_REF
)
type CommentTag int
const (
COMMENT_TAG_NONE CommentTag = iota
COMMENT_TAG_POSTER
COMMENT_TAG_ADMIN
COMMENT_TAG_OWNER
)
// Comment represents a comment in commit and issue page.
type Comment struct {
ID int64 `xorm:"pk autoincr"`
Type CommentType
PosterID int64
Poster *User `xorm:"-"`
IssueID int64 `xorm:"INDEX"`
CommitID int64
Line int64
Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-"`
Created time.Time `xorm:"CREATED"`
// Reference issue in commit message
CommitSHA string `xorm:"VARCHAR(40)"`
Attachments []*Attachment `xorm:"-"`
// For view issue page.
ShowTag CommentTag `xorm:"-"`
}
func (c *Comment) AfterSet(colName string, _ xorm.Cell) {
var err error
switch colName {
case "id":
c.Attachments, err = GetAttachmentsByCommentID(c.ID)
if err != nil {
log.Error(3, "GetAttachmentsByCommentID[%d]: %v", c.ID, err)
}
case "poster_id":
c.Poster, err = GetUserByID(c.PosterID)
if err != nil {
if IsErrUserNotExist(err) {
c.PosterID = -1
c.Poster = NewFakeUser()
} else {
log.Error(3, "GetUserByID[%d]: %v", c.ID, err)
}
}
case "created":
c.Created = regulateTimeZone(c.Created)
}
}
func (c *Comment) AfterDelete() {
_, err := DeleteAttachmentsByComment(c.ID, true)
if err != nil {
log.Info("Could not delete files for comment %d on issue #%d: %s", c.ID, c.IssueID, err)
}
}
// HashTag returns unique hash tag for comment.
func (c *Comment) HashTag() string {
return "issuecomment-" + com.ToStr(c.ID)
}
// EventTag returns unique event hash tag for comment.
func (c *Comment) EventTag() string {
return "event-" + com.ToStr(c.ID)
}
func createComment(e *xorm.Session, u *User, repo *Repository, issue *Issue, commitID, line int64, cmtType CommentType, content, commitSHA string, uuids []string) (_ *Comment, err error) {
comment := &Comment{
PosterID: u.Id,
Type: cmtType,
IssueID: issue.ID,
CommitID: commitID,
Line: line,
Content: content,
CommitSHA: commitSHA,
}
if _, err = e.Insert(comment); err != nil {
return nil, err
}
// Check comment type.
switch cmtType {
case COMMENT_TYPE_COMMENT:
if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", issue.ID); err != nil {
return nil, err
}
// Check attachments.
attachments := make([]*Attachment, 0, len(uuids))
for _, uuid := range uuids {
attach, err := getAttachmentByUUID(e, uuid)
if err != nil {
if IsErrAttachmentNotExist(err) {
continue
}
return nil, fmt.Errorf("getAttachmentByUUID[%s]: %v", uuid, err)
}
attachments = append(attachments, attach)
}
for i := range attachments {
attachments[i].IssueID = issue.ID
attachments[i].CommentID = comment.ID
// No assign value could be 0, so ignore AllCols().
if _, err = e.Id(attachments[i].ID).Update(attachments[i]); err != nil {
return nil, fmt.Errorf("update attachment[%d]: %v", attachments[i].ID, err)
}
}
// Notify watchers.
act := &Action{
ActUserID: u.Id,
ActUserName: u.Name,
ActEmail: u.Email,
OpType: COMMENT_ISSUE,
Content: fmt.Sprintf("%d|%s", issue.Index, strings.Split(content, "\n")[0]),
RepoID: repo.ID,
RepoUserName: repo.Owner.Name,
RepoName: repo.Name,
IsPrivate: repo.IsPrivate,
}
if err = notifyWatchers(e, act); err != nil {
return nil, err
}
case COMMENT_TYPE_REOPEN:
if issue.IsPull {
_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", repo.ID)
} else {
_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", repo.ID)
}
if err != nil {
return nil, err
}
case COMMENT_TYPE_CLOSE:
if issue.IsPull {
_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", repo.ID)
} else {
_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", repo.ID)
}
if err != nil {
return nil, err
}
}
return comment, nil
}
func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue) (*Comment, error) {
cmtType := COMMENT_TYPE_CLOSE
if !issue.IsClosed {
cmtType = COMMENT_TYPE_REOPEN
}
return createComment(e, doer, repo, issue, 0, 0, cmtType, "", "", nil)
}
// CreateComment creates comment of issue or commit.
func CreateComment(doer *User, repo *Repository, issue *Issue, commitID, line int64, cmtType CommentType, content, commitSHA string, attachments []string) (comment *Comment, err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return nil, err
}
comment, err = createComment(sess, doer, repo, issue, commitID, line, cmtType, content, commitSHA, attachments)
if err != nil {
return nil, err
}
return comment, sess.Commit()
}
// CreateIssueComment creates a plain issue comment.
func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content string, attachments []string) (*Comment, error) {
return CreateComment(doer, repo, issue, 0, 0, COMMENT_TYPE_COMMENT, content, "", attachments)
}
// CreateRefComment creates a commit reference comment to issue.
func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commitSHA string) error {
if len(commitSHA) == 0 {
return fmt.Errorf("cannot create reference with empty commit SHA")
}
// Check if same reference from same commit has already existed.
has, err := x.Get(&Comment{
Type: COMMENT_TYPE_COMMIT_REF,
IssueID: issue.ID,
CommitSHA: commitSHA,
})
if err != nil {
return fmt.Errorf("check reference comment: %v", err)
} else if has {
return nil
}
_, err = CreateComment(doer, repo, issue, 0, 0, COMMENT_TYPE_COMMIT_REF, content, commitSHA, nil)
return err
}
// GetCommentByID returns the comment by given ID.
func GetCommentByID(id int64) (*Comment, error) {
c := new(Comment)
has, err := x.Id(id).Get(c)
if err != nil {
return nil, err
} else if !has {
return nil, ErrCommentNotExist{id}
}
return c, nil
}
// GetCommentsByIssueID returns all comments of issue by given ID.
func GetCommentsByIssueID(issueID int64) ([]*Comment, error) {
comments := make([]*Comment, 0, 10)
return comments, x.Where("issue_id=?", issueID).Asc("created").Find(&comments)
}
// UpdateComment updates information of comment.
func UpdateComment(c *Comment) error {
_, err := x.Id(c.ID).AllCols().Update(c)
return err
}
// Attachment represent a attachment of issue/comment/release. // Attachment represent a attachment of issue/comment/release.
type Attachment struct { type Attachment struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
@ -1714,7 +1327,20 @@ type Attachment struct {
CommentID int64 CommentID int64
ReleaseID int64 `xorm:"INDEX"` ReleaseID int64 `xorm:"INDEX"`
Name string Name string
Created time.Time `xorm:"CREATED"`
Created time.Time `xorm:"-"`
CreatedUnix int64
}
func (a *Attachment) BeforeInsert() {
a.CreatedUnix = time.Now().UTC().Unix()
}
func (a *Attachment) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
a.Created = time.Unix(a.CreatedUnix, 0).Local()
}
} }
// AttachmentLocalPath returns where attachment is stored in local file system based on given UUID. // AttachmentLocalPath returns where attachment is stored in local file system based on given UUID.

320
models/issue_comment.go

@ -0,0 +1,320 @@
// Copyright 2016 The Gogs 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 models
import (
"fmt"
"strings"
"time"
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
"github.com/gogits/gogs/modules/log"
)
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
type CommentType int
const (
// Plain comment, can be associated with a commit (CommitID > 0) and a line (LineNum > 0)
COMMENT_TYPE_COMMENT CommentType = iota
COMMENT_TYPE_REOPEN
COMMENT_TYPE_CLOSE
// References.
COMMENT_TYPE_ISSUE_REF
// Reference from a commit (not part of a pull request)
COMMENT_TYPE_COMMIT_REF
// Reference from a comment
COMMENT_TYPE_COMMENT_REF
// Reference from a pull request
COMMENT_TYPE_PULL_REF
)
type CommentTag int
const (
COMMENT_TAG_NONE CommentTag = iota
COMMENT_TAG_POSTER
COMMENT_TAG_WRITER
COMMENT_TAG_OWNER
)
// Comment represents a comment in commit and issue page.
type Comment struct {
ID int64 `xorm:"pk autoincr"`
Type CommentType
PosterID int64
Poster *User `xorm:"-"`
IssueID int64 `xorm:"INDEX"`
CommitID int64
Line int64
Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-"`
Created time.Time `xorm:"-"`
CreatedUnix int64
// Reference issue in commit message
CommitSHA string `xorm:"VARCHAR(40)"`
Attachments []*Attachment `xorm:"-"`
// For view issue page.
ShowTag CommentTag `xorm:"-"`
}
func (c *Comment) BeforeInsert() {
c.CreatedUnix = time.Now().UTC().Unix()
}
func (c *Comment) AfterSet(colName string, _ xorm.Cell) {
var err error
switch colName {
case "id":
c.Attachments, err = GetAttachmentsByCommentID(c.ID)
if err != nil {
log.Error(3, "GetAttachmentsByCommentID[%d]: %v", c.ID, err)
}
case "poster_id":
c.Poster, err = GetUserByID(c.PosterID)
if err != nil {
if IsErrUserNotExist(err) {
c.PosterID = -1
c.Poster = NewFakeUser()
} else {
log.Error(3, "GetUserByID[%d]: %v", c.ID, err)
}
}
case "created_unix":
c.Created = time.Unix(c.CreatedUnix, 0).Local()
}
}
func (c *Comment) AfterDelete() {
_, err := DeleteAttachmentsByComment(c.ID, true)
if err != nil {
log.Info("Could not delete files for comment %d on issue #%d: %s", c.ID, c.IssueID, err)
}
}
// HashTag returns unique hash tag for comment.
func (c *Comment) HashTag() string {
return "issuecomment-" + com.ToStr(c.ID)
}
// EventTag returns unique event hash tag for comment.
func (c *Comment) EventTag() string {
return "event-" + com.ToStr(c.ID)
}
func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) {
comment := &Comment{
Type: opts.Type,
PosterID: opts.Doer.Id,
IssueID: opts.Issue.ID,
CommitID: opts.CommitID,
CommitSHA: opts.CommitSHA,
Line: opts.LineNum,
Content: opts.Content,
}
if _, err = e.Insert(comment); err != nil {
return nil, err
}
// Compose comment action, could be plain comment, close or reopen issue/pull request.
// This object will be used to notify watchers in the end of function.
act := &Action{
ActUserID: opts.Doer.Id,
ActUserName: opts.Doer.Name,
ActEmail: opts.Doer.Email,
Content: fmt.Sprintf("%d|%s", opts.Issue.Index, strings.Split(opts.Content, "\n")[0]),
RepoID: opts.Repo.ID,
RepoUserName: opts.Repo.Owner.Name,
RepoName: opts.Repo.Name,
IsPrivate: opts.Repo.IsPrivate,
}
// Check comment type.
switch opts.Type {
case COMMENT_TYPE_COMMENT:
act.OpType = ACTION_COMMENT_ISSUE
if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil {
return nil, err
}
// Check attachments
attachments := make([]*Attachment, 0, len(opts.Attachments))
for _, uuid := range opts.Attachments {
attach, err := getAttachmentByUUID(e, uuid)
if err != nil {
if IsErrAttachmentNotExist(err) {
continue
}
return nil, fmt.Errorf("getAttachmentByUUID[%s]: %v", uuid, err)
}
attachments = append(attachments, attach)
}
for i := range attachments {
attachments[i].IssueID = opts.Issue.ID
attachments[i].CommentID = comment.ID
// No assign value could be 0, so ignore AllCols().
if _, err = e.Id(attachments[i].ID).Update(attachments[i]); err != nil {
return nil, fmt.Errorf("update attachment[%d]: %v", attachments[i].ID, err)
}
}
case COMMENT_TYPE_REOPEN:
act.OpType = ACTION_REOPEN_ISSUE
if opts.Issue.IsPull {
act.OpType = ACTION_REOPEN_PULL_REQUEST
}
if opts.Issue.IsPull {
_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", opts.Repo.ID)
} else {
_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", opts.Repo.ID)
}
if err != nil {
return nil, err
}
case COMMENT_TYPE_CLOSE:
act.OpType = ACTION_CLOSE_ISSUE
if opts.Issue.IsPull {
act.OpType = ACTION_CLOSE_PULL_REQUEST
}
if opts.Issue.IsPull {
_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", opts.Repo.ID)
} else {
_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", opts.Repo.ID)
}
if err != nil {
return nil, err
}
}
// Notify watchers for whatever action comes in, ignore if no action type
if act.OpType > 0 {
if err = notifyWatchers(e, act); err != nil {
return nil, fmt.Errorf("notifyWatchers: %v", err)
}
}
return comment, nil
}
func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue) (*Comment, error) {
cmtType := COMMENT_TYPE_CLOSE
if !issue.IsClosed {
cmtType = COMMENT_TYPE_REOPEN
}
return createComment(e, &CreateCommentOptions{
Type: cmtType,
Doer: doer,
Repo: repo,
Issue: issue,
})
}
type CreateCommentOptions struct {
Type CommentType
Doer *User
Repo *Repository
Issue *Issue
CommitID int64
CommitSHA string
LineNum int64
Content string
Attachments []string // UUIDs of attachments
}
// CreateComment creates comment of issue or commit.
func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return nil, err
}
comment, err = createComment(sess, opts)
if err != nil {
return nil, err
}
return comment, sess.Commit()
}
// CreateIssueComment creates a plain issue comment.
func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content string, attachments []string) (*Comment, error) {
return CreateComment(&CreateCommentOptions{
Type: COMMENT_TYPE_COMMENT,
Doer: doer,
Repo: repo,
Issue: issue,
Content: content,
Attachments: attachments,
})
}
// CreateRefComment creates a commit reference comment to issue.
func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commitSHA string) error {
if len(commitSHA) == 0 {
return fmt.Errorf("cannot create reference with empty commit SHA")
}
// Check if same reference from same commit has already existed.
has, err := x.Get(&Comment{
Type: COMMENT_TYPE_COMMIT_REF,
IssueID: issue.ID,
CommitSHA: commitSHA,
})
if err != nil {
return fmt.Errorf("check reference comment: %v", err)
} else if has {
return nil
}
_, err = CreateComment(&CreateCommentOptions{
Type: COMMENT_TYPE_COMMIT_REF,
Doer: doer,
Repo: repo,
Issue: issue,
CommitSHA: commitSHA,
Content: content,
})
return err
}
// GetCommentByID returns the comment by given ID.
func GetCommentByID(id int64) (*Comment, error) {
c := new(Comment)
has, err := x.Id(id).Get(c)
if err != nil {
return nil, err
} else if !has {
return nil, ErrCommentNotExist{id}
}
return c, nil
}
// GetCommentsByIssueID returns all comments of issue by given ID.
func GetCommentsByIssueID(issueID int64) ([]*Comment, error) {
comments := make([]*Comment, 0, 10)
return comments, x.Where("issue_id=?", issueID).Asc("created_unix").Find(&comments)
}
// UpdateComment updates information of comment.
func UpdateComment(c *Comment) error {
_, err := x.Id(c.ID).AllCols().Update(c)
return err
}

234
models/issue_label.go

@ -0,0 +1,234 @@
// Copyright 2016 The Gogs 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 models
import (
"fmt"
"html/template"
"strconv"
"strings"
"github.com/go-xorm/xorm"
)
// Label represents a label of repository for issues.
type Label struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX"`
Name string
Color string `xorm:"VARCHAR(7)"`
NumIssues int
NumClosedIssues int
NumOpenIssues int `xorm:"-"`
IsChecked bool `xorm:"-"`
}
// CalOpenIssues calculates the open issues of label.
func (m *Label) CalOpenIssues() {
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
}
// ForegroundColor calculates the text color for labels based
// on their background color.
func (l *Label) ForegroundColor() template.CSS {
if strings.HasPrefix(l.Color, "#") {
if color, err := strconv.ParseUint(l.Color[1:], 16, 64); err == nil {
r := float32(0xFF & (color >> 16))
g := float32(0xFF & (color >> 8))
b := float32(0xFF & color)
luminance := (0.2126*r + 0.7152*g + 0.0722*b) / 255
if luminance < 0.5 {
return template.CSS("#fff")
}
}
}
// default to black
return template.CSS("#000")
}
// NewLabel creates new label of repository.
func NewLabel(l *Label) error {
_, err := x.Insert(l)
return err
}
func getLabelByID(e Engine, id int64) (*Label, error) {
if id <= 0 {
return nil, ErrLabelNotExist{id}
}
l := &Label{ID: id}
has, err := x.Get(l)
if err != nil {
return nil, err
} else if !has {
return nil, ErrLabelNotExist{l.ID}
}
return l, nil
}
// GetLabelByID returns a label by given ID.
func GetLabelByID(id int64) (*Label, error) {
return getLabelByID(x, id)
}
// GetLabelsByRepoID returns all labels that belong to given repository by ID.
func GetLabelsByRepoID(repoID int64) ([]*Label, error) {
labels := make([]*Label, 0, 10)
return labels, x.Where("repo_id=?", repoID).Find(&labels)
}
func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
issueLabels, err := getIssueLabels(e, issueID)
if err != nil {
return nil, fmt.Errorf("getIssueLabels: %v", err)
}
var label *Label
labels := make([]*Label, 0, len(issueLabels))
for idx := range issueLabels {
label, err = getLabelByID(e, issueLabels[idx].LabelID)
if err != nil && !IsErrLabelNotExist(err) {
return nil, fmt.Errorf("getLabelByID: %v", err)
}
labels = append(labels, label)
}
return labels, nil
}
// GetLabelsByIssueID returns all labels that belong to given issue by ID.
func GetLabelsByIssueID(issueID int64) ([]*Label, error) {
return getLabelsByIssueID(x, issueID)
}
func updateLabel(e Engine, l *Label) error {
_, err := e.Id(l.ID).AllCols().Update(l)
return err
}
// UpdateLabel updates label information.
func UpdateLabel(l *Label) error {
return updateLabel(x, l)
}
// DeleteLabel delete a label of given repository.
func DeleteLabel(repoID, labelID int64) error {
l, err := GetLabelByID(labelID)
if err != nil {
if IsErrLabelNotExist(err) {
return nil
}
return err
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if _, err = x.Where("label_id=?", labelID).Delete(new(IssueLabel)); err != nil {
return err
} else if _, err = sess.Delete(l); err != nil {
return err
}
return sess.Commit()
}
// .___ .____ ___. .__
// | | ______ ________ __ ____ | | _____ \_ |__ ____ | |
// | |/ ___// ___/ | \_/ __ \| | \__ \ | __ \_/ __ \| |
// | |\___ \ \___ \| | /\ ___/| |___ / __ \| \_\ \ ___/| |__
// |___/____ >____ >____/ \___ >_______ (____ /___ /\___ >____/
// \/ \/ \/ \/ \/ \/ \/
// IssueLabel represetns an issue-lable relation.
type IssueLabel struct {
ID int64 `xorm:"pk autoincr"`
IssueID int64 `xorm:"UNIQUE(s)"`
LabelID int64 `xorm:"UNIQUE(s)"`
}
func hasIssueLabel(e Engine, issueID, labelID int64) bool {
has, _ := e.Where("issue_id=? AND label_id=?", issueID, labelID).Get(new(IssueLabel))
return has
}
// HasIssueLabel returns true if issue has been labeled.
func HasIssueLabel(issueID, labelID int64) bool {
return hasIssueLabel(x, issueID, labelID)
}
func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
if _, err = e.Insert(&IssueLabel{
IssueID: issue.ID,
LabelID: label.ID,
}); err != nil {
return err
}
label.NumIssues++
if issue.IsClosed {
label.NumClosedIssues++
}
return updateLabel(e, label)
}
// NewIssueLabel creates a new issue-label relation.
func NewIssueLabel(issue *Issue, label *Label) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if err = newIssueLabel(sess, issue, label); err != nil {
return err
}
return sess.Commit()
}
func getIssueLabels(e Engine, issueID int64) ([]*IssueLabel, error) {
issueLabels := make([]*IssueLabel, 0, 10)
return issueLabels, e.Where("issue_id=?", issueID).Asc("label_id").Find(&issueLabels)
}
// GetIssueLabels returns all issue-label relations of given issue by ID.
func GetIssueLabels(issueID int64) ([]*IssueLabel, error) {
return getIssueLabels(x, issueID)
}
func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
if _, err = e.Delete(&IssueLabel{
IssueID: issue.ID,
LabelID: label.ID,
}); err != nil {
return err
}
label.NumIssues--
if issue.IsClosed {
label.NumClosedIssues--
}
return updateLabel(e, label)
}
// DeleteIssueLabel deletes issue-label relation.
func DeleteIssueLabel(issue *Issue, label *Label) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if err = deleteIssueLabel(sess, issue, label); err != nil {
return err
}
return sess.Commit()
}

37
models/login.go

@ -28,11 +28,11 @@ type LoginType int
// Note: new type must be added at the end of list to maintain compatibility. // Note: new type must be added at the end of list to maintain compatibility.
const ( const (
LOGIN_NOTYPE LoginType = iota LOGIN_NOTYPE LoginType = iota
LOGIN_PLAIN LOGIN_PLAIN // 1
LOGIN_LDAP LOGIN_LDAP // 2
LOGIN_SMTP LOGIN_SMTP // 3
LOGIN_PAM LOGIN_PAM // 4
LOGIN_DLDAP LOGIN_DLDAP // 5
) )
var ( var (
@ -42,7 +42,7 @@ var (
var LoginNames = map[LoginType]string{ var LoginNames = map[LoginType]string{
LOGIN_LDAP: "LDAP (via BindDN)", LOGIN_LDAP: "LDAP (via BindDN)",
LOGIN_DLDAP: "LDAP (simple auth)", LOGIN_DLDAP: "LDAP (simple auth)", // Via direct bind
LOGIN_SMTP: "SMTP", LOGIN_SMTP: "SMTP",
LOGIN_PAM: "PAM", LOGIN_PAM: "PAM",
} }
@ -101,8 +101,20 @@ type LoginSource struct {
Name string `xorm:"UNIQUE"` Name string `xorm:"UNIQUE"`
IsActived bool `xorm:"NOT NULL DEFAULT false"` IsActived bool `xorm:"NOT NULL DEFAULT false"`
Cfg core.Conversion `xorm:"TEXT"` Cfg core.Conversion `xorm:"TEXT"`
Created time.Time `xorm:"CREATED"`
Updated time.Time `xorm:"UPDATED"` Created time.Time `xorm:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-"`
UpdatedUnix int64
}
func (s *LoginSource) BeforeInsert() {
s.CreatedUnix = time.Now().UTC().Unix()
s.UpdatedUnix = s.CreatedUnix
}
func (s *LoginSource) BeforeUpdate() {
s.UpdatedUnix = time.Now().UTC().Unix()
} }
// Cell2Int64 converts a xorm.Cell type to int64, // Cell2Int64 converts a xorm.Cell type to int64,
@ -132,6 +144,15 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
} }
} }
func (s *LoginSource) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
s.Created = time.Unix(s.CreatedUnix, 0).Local()
case "updated_unix":
s.Updated = time.Unix(s.UpdatedUnix, 0).Local()
}
}
func (source *LoginSource) TypeName() string { func (source *LoginSource) TypeName() string {
return LoginNames[source.Type] return LoginNames[source.Type]
} }

222
models/migrations/migrations.go

@ -13,15 +13,16 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"time"
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
gouuid "github.com/satori/go.uuid"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
gouuid "github.com/gogits/gogs/modules/uuid"
) )
const _MIN_DB_VER = 4 const _MIN_DB_VER = 4
@ -65,6 +66,7 @@ var migrations = []Migration{
NewMigration("rename pull request fields", renamePullRequestFields), // V8 -> V9:v0.6.16 NewMigration("rename pull request fields", renamePullRequestFields), // V8 -> V9:v0.6.16
NewMigration("clean up migrate repo info", cleanUpMigrateRepoInfo), // V9 -> V10:v0.6.20 NewMigration("clean up migrate repo info", cleanUpMigrateRepoInfo), // V9 -> V10:v0.6.20
NewMigration("generate rands and salt for organizations", generateOrgRandsAndSalt), // V10 -> V11:v0.8.5 NewMigration("generate rands and salt for organizations", generateOrgRandsAndSalt), // V10 -> V11:v0.8.5
NewMigration("convert date to unix timestamp", convertDateToUnix), // V11 -> V12:v0.9.2
} }
// Migrate database to current version // Migrate database to current version
@ -453,3 +455,221 @@ func generateOrgRandsAndSalt(x *xorm.Engine) (err error) {
return sess.Commit() return sess.Commit()
} }
type TAction struct {
ID int64 `xorm:"pk autoincr"`
CreatedUnix int64
}
func (t *TAction) TableName() string { return "action" }
type TNotice struct {
ID int64 `xorm:"pk autoincr"`
CreatedUnix int64
}
func (t *TNotice) TableName() string { return "notice" }
type TComment struct {
ID int64 `xorm:"pk autoincr"`
CreatedUnix int64
}
func (t *TComment) TableName() string { return "comment" }
type TIssue struct {
ID int64 `xorm:"pk autoincr"`
DeadlineUnix int64
CreatedUnix int64
UpdatedUnix int64
}
func (t *TIssue) TableName() string { return "issue" }
type TMilestone struct {
ID int64 `xorm:"pk autoincr"`
DeadlineUnix int64
ClosedDateUnix int64
}
func (t *TMilestone) TableName() string { return "milestone" }
type TAttachment struct {
ID int64 `xorm:"pk autoincr"`
CreatedUnix int64
}
func (t *TAttachment) TableName() string { return "attachment" }
type TLoginSource struct {
ID int64 `xorm:"pk autoincr"`
CreatedUnix int64
UpdatedUnix int64
}
func (t *TLoginSource) TableName() string { return "login_source" }
type TPull struct {
ID int64 `xorm:"pk autoincr"`
MergedUnix int64
}
func (t *TPull) TableName() string { return "pull_request" }
type TRelease struct {
ID int64 `xorm:"pk autoincr"`
CreatedUnix int64
}
func (t *TRelease) TableName() string { return "release" }
type TRepo struct {
ID int64 `xorm:"pk autoincr"`
CreatedUnix int64
UpdatedUnix int64
}
func (t *TRepo) TableName() string { return "repository" }
type TMirror struct {
ID int64 `xorm:"pk autoincr"`
UpdatedUnix int64
NextUpdateUnix int64
}
func (t *TMirror) TableName() string { return "mirror" }
type TPublicKey struct {
ID int64 `xorm:"pk autoincr"`
CreatedUnix int64
UpdatedUnix int64
}
func (t *TPublicKey) TableName() string { return "public_key" }
type TDeployKey struct {
ID int64 `xorm:"pk autoincr"`
CreatedUnix int64
UpdatedUnix int64
}
func (t *TDeployKey) TableName() string { return "deploy_key" }
type TAccessToken struct {
ID int64 `xorm:"pk autoincr"`
CreatedUnix int64
UpdatedUnix int64
}
func (t *TAccessToken) TableName() string { return "access_token" }
type TUser struct {
ID int64 `xorm:"pk autoincr"`
CreatedUnix int64
UpdatedUnix int64
}
func (t *TUser) TableName() string { return "user" }
type TWebhook struct {
ID int64 `xorm:"pk autoincr"`
CreatedUnix int64
UpdatedUnix int64
}
func (t *TWebhook) TableName() string { return "webhook" }
func convertDateToUnix(x *xorm.Engine) (err error) {
type Bean struct {
ID int64 `xorm:"pk autoincr"`
Created time.Time
Updated time.Time
Merged time.Time
Deadline time.Time
ClosedDate time.Time
NextUpdate time.Time
}
var tables = []struct {
name string
cols []string
bean interface{}
}{
{"action", []string{"created"}, new(TAction)},
{"notice", []string{"created"}, new(TNotice)},
{"comment", []string{"created"}, new(TComment)},
{"issue", []string{"deadline", "created", "updated"}, new(TIssue)},
{"milestone", []string{"deadline", "closed_date"}, new(TMilestone)},
{"attachment", []string{"created"}, new(TAttachment)},
{"login_source", []string{"created", "updated"}, new(TLoginSource)},
{"pull_request", []string{"merged"}, new(TPull)},
{"release", []string{"created"}, new(TRelease)},
{"repository", []string{"created", "updated"}, new(TRepo)},
{"mirror", []string{"updated", "next_update"}, new(TMirror)},
{"public_key", []string{"created", "updated"}, new(TPublicKey)},
{"deploy_key", []string{"created", "updated"}, new(TDeployKey)},
{"access_token", []string{"created", "updated"}, new(TAccessToken)},
{"user", []string{"created", "updated"}, new(TUser)},
{"webhook", []string{"created", "updated"}, new(TWebhook)},
}
for _, table := range tables {
log.Info("Converting table: %s", table.name)
if err = x.Sync2(table.bean); err != nil {
return fmt.Errorf("Sync [table: %s]: %v", table.name, err)
}
offset := 0
for {
beans := make([]*Bean, 0, 100)
if err = x.Sql(fmt.Sprintf("SELECT * FROM `%s` ORDER BY id ASC LIMIT 100 OFFSET %d",
table.name, offset)).Find(&beans); err != nil {
return fmt.Errorf("select beans [table: %s, offset: %d]: %v", table.name, offset, err)
}
log.Trace("Table [%s]: offset: %d, beans: %d", table.name, offset, len(beans))
if len(beans) == 0 {
break
}
offset += 100
baseSQL := "UPDATE `" + table.name + "` SET "
for _, bean := range beans {
valSQLs := make([]string, 0, len(table.cols))
for _, col := range table.cols {
fieldSQL := ""
fieldSQL += col + "_unix = "
switch col {
case "deadline":
if bean.Deadline.IsZero() {
continue
}
fieldSQL += com.ToStr(bean.Deadline.UTC().Unix())
case "created":
fieldSQL += com.ToStr(bean.Created.UTC().Unix())
case "updated":
fieldSQL += com.ToStr(bean.Updated.UTC().Unix())
case "closed_date":
fieldSQL += com.ToStr(bean.ClosedDate.UTC().Unix())
case "merged":
fieldSQL += com.ToStr(bean.Merged.UTC().Unix())
case "next_update":
fieldSQL += com.ToStr(bean.NextUpdate.UTC().Unix())
}
valSQLs = append(valSQLs, fieldSQL)
}
if len(valSQLs) == 0 {
continue
}
if _, err = x.Exec(baseSQL + strings.Join(valSQLs, ",") + " WHERE id = " + com.ToStr(bean.ID)); err != nil {
return fmt.Errorf("update bean [table: %s, id: %d]: %v", table.name, bean.ID, err)
}
}
}
}
return nil
}

31
models/models.go

@ -11,16 +11,13 @@ import (
"os" "os"
"path" "path"
"strings" "strings"
"time"
"github.com/Unknwon/com"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/go-xorm/core" "github.com/go-xorm/core"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
_ "github.com/lib/pq" _ "github.com/lib/pq"
"github.com/gogits/gogs/models/migrations" "github.com/gogits/gogs/models/migrations"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
@ -44,27 +41,6 @@ func sessionRelease(sess *xorm.Session) {
sess.Close() sess.Close()
} }
// Note: get back time.Time from database Go sees it at UTC where they are really Local.
// So this function makes correct timezone offset.
func regulateTimeZone(t time.Time) time.Time {
if !setting.UseMySQL {
return t
}
zone := t.Local().Format("-0700")
if len(zone) != 5 {
log.Error(4, "Unprocessable timezone: %s - %s", t.Local(), zone)
return t
}
hour := com.StrTo(zone[2:3]).MustInt()
minutes := com.StrTo(zone[3:5]).MustInt()
if zone[0] == '-' {
return t.Add(time.Duration(hour) * time.Hour).Add(time.Duration(minutes) * time.Minute)
}
return t.Add(-1 * time.Duration(hour) * time.Hour).Add(-1 * time.Duration(minutes) * time.Minute)
}
var ( var (
x *xorm.Engine x *xorm.Engine
tables []interface{} tables []interface{}
@ -191,12 +167,7 @@ func SetEngine() (err error) {
return fmt.Errorf("Fail to create xorm.log: %v", err) return fmt.Errorf("Fail to create xorm.log: %v", err)
} }
x.SetLogger(xorm.NewSimpleLogger(f)) x.SetLogger(xorm.NewSimpleLogger(f))
x.ShowSQL(true)
x.ShowSQL = true
x.ShowInfo = true
x.ShowDebug = true
x.ShowErr = true
x.ShowWarn = true
return nil return nil
} }

102
models/org.go

@ -10,14 +10,13 @@ import (
"os" "os"
"strings" "strings"
"github.com/Unknwon/com"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
) )
var ( var (
ErrOrgNotExist = errors.New("Organization does not exist") ErrOrgNotExist = errors.New("Organization does not exist")
ErrTeamAlreadyExist = errors.New("Team already exist") ErrTeamNotExist = errors.New("Team does not exist")
ErrTeamNotExist = errors.New("Team does not exist")
ErrTeamNameIllegal = errors.New("Team name contains illegal characters")
) )
// IsOwnedBy returns true if given user is in the owner team. // IsOwnedBy returns true if given user is in the owner team.
@ -170,7 +169,7 @@ func GetOrgByName(name string) (*User, error) {
} }
u := &User{ u := &User{
LowerName: strings.ToLower(name), LowerName: strings.ToLower(name),
Type: ORGANIZATION, Type: USER_TYPE_ORGANIZATION,
} }
has, err := x.Get(u) has, err := x.Get(u)
if err != nil { if err != nil {
@ -255,6 +254,27 @@ func IsPublicMembership(orgId, uid int64) bool {
return has return has
} }
func getOrgsByUserID(sess *xorm.Session, userID int64, showAll bool) ([]*User, error) {
orgs := make([]*User, 0, 10)
if !showAll {
sess.And("`org_user`.is_public=?", true)
}
return orgs, sess.And("`org_user`.uid=?", userID).
Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").Find(&orgs)
}
// GetOrgsByUserID returns a list of organizations that the given user ID
// has joined.
func GetOrgsByUserID(userID int64, showAll bool) ([]*User, error) {
return getOrgsByUserID(x.NewSession(), userID, showAll)
}
// GetOrgsByUserIDDesc returns a list of organizations that the given user ID
// has joined, ordered descending by the given condition.
func GetOrgsByUserIDDesc(userID int64, desc string, showAll bool) ([]*User, error) {
return getOrgsByUserID(x.NewSession().Desc(desc), userID, showAll)
}
func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) { func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) {
orgs := make([]*User, 0, 10) orgs := make([]*User, 0, 10)
return orgs, sess.Where("`org_user`.uid=?", userID).And("`org_user`.is_owner=?", true). return orgs, sess.Where("`org_user`.uid=?", userID).And("`org_user`.is_owner=?", true).
@ -268,7 +288,7 @@ func GetOwnedOrgsByUserID(userID int64) ([]*User, error) {
} }
// GetOwnedOrganizationsByUserIDDesc returns a list of organizations are owned by // GetOwnedOrganizationsByUserIDDesc returns a list of organizations are owned by
// given user ID and descring order by given condition. // given user ID, ordered descending by the given condition.
func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) { func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
sess := x.NewSession() sess := x.NewSession()
return getOwnedOrgsByUserID(sess.Desc(desc), userID) return getOwnedOrgsByUserID(sess.Desc(desc), userID)
@ -598,9 +618,9 @@ func (t *Team) RemoveRepository(repoID int64) error {
// NewTeam creates a record of new team. // NewTeam creates a record of new team.
// It's caller's responsibility to assign organization ID. // It's caller's responsibility to assign organization ID.
func NewTeam(t *Team) (err error) { func NewTeam(t *Team) error {
if err = IsUsableName(t.Name); err != nil { if len(t.Name) == 0 {
return err return errors.New("empty team name")
} }
has, err := x.Id(t.OrgID).Get(new(User)) has, err := x.Id(t.OrgID).Get(new(User))
@ -615,7 +635,7 @@ func NewTeam(t *Team) (err error) {
if err != nil { if err != nil {
return err return err
} else if has { } else if has {
return ErrTeamAlreadyExist return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
} }
sess := x.NewSession() sess := x.NewSession()
@ -674,8 +694,8 @@ func GetTeamById(teamId int64) (*Team, error) {
// UpdateTeam updates information of team. // UpdateTeam updates information of team.
func UpdateTeam(t *Team, authChanged bool) (err error) { func UpdateTeam(t *Team, authChanged bool) (err error) {
if err = IsUsableName(t.Name); err != nil { if len(t.Name) == 0 {
return err return errors.New("empty team name")
} }
if len(t.Description) > 255 { if len(t.Description) > 255 {
@ -689,6 +709,13 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
} }
t.LowerName = strings.ToLower(t.Name) t.LowerName = strings.ToLower(t.Name)
has, err := x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).And("id!=?", t.ID).Get(new(Team))
if err != nil {
return err
} else if has {
return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
}
if _, err = sess.Id(t.ID).AllCols().Update(t); err != nil { if _, err = sess.Id(t.ID).AllCols().Update(t); err != nil {
return fmt.Errorf("update: %v", err) return fmt.Errorf("update: %v", err)
} }
@ -1023,3 +1050,54 @@ func removeOrgRepo(e Engine, orgID, repoID int64) error {
func RemoveOrgRepo(orgID, repoID int64) error { func RemoveOrgRepo(orgID, repoID int64) error {
return removeOrgRepo(x, orgID, repoID) return removeOrgRepo(x, orgID, repoID)
} }
// GetUserRepositories gets all repositories of an organization,
// that the user with the given userID has access to.
func (org *User) GetUserRepositories(userID int64) (err error) {
teams := make([]*Team, 0, org.NumTeams)
if err = x.Sql(`SELECT team.id FROM team
INNER JOIN team_user ON team_user.team_id = team.id
WHERE team_user.org_id = ? AND team_user.uid = ?`, org.Id, userID).Find(&teams); err != nil {
return fmt.Errorf("get teams: %v", err)
}
teamIDs := make([]string, len(teams))
for i := range teams {
teamIDs[i] = com.ToStr(teams[i].ID)
}
if len(teamIDs) == 0 {
// user has no team but "IN ()" is invalid SQL
teamIDs = append(teamIDs, "-1") // there is no repo with id=-1
}
repos := make([]*Repository, 0, 5)
if err = x.Sql(fmt.Sprintf(`SELECT repository.* FROM repository
INNER JOIN team_repo ON team_repo.repo_id = repository.id
WHERE (repository.owner_id = ? AND repository.is_private = ?) OR team_repo.team_id IN (%s)
GROUP BY repository.id`, strings.Join(teamIDs, ",")), org.Id, false).Find(&repos); err != nil {
return fmt.Errorf("get repositories: %v", err)
}
org.Repos = repos
// FIXME: should I change this value inside method,
// or only in location of caller where it's really needed?
org.NumRepos = len(org.Repos)
return nil
}
// GetTeams returns all teams that belong to organization,
// and that the user has joined.
func (org *User) GetUserTeams(userID int64) error {
teams := make([]*Team, 0, 5)
if err := x.Sql(`SELECT team.* FROM team
INNER JOIN team_user ON team_user.team_id = team.id
WHERE team_user.org_id = ? AND team_user.uid = ?`,
org.Id, userID).Find(&teams); err != nil {
return fmt.Errorf("get teams: %v", err)
}
// FIXME: should I change this value inside method,
// or only in location of caller where it's really needed?
org.NumTeams = len(org.Teams)
return nil
}

68
models/pull.go

@ -58,20 +58,25 @@ type PullRequest struct {
HasMerged bool HasMerged bool
MergedCommitID string `xorm:"VARCHAR(40)"` MergedCommitID string `xorm:"VARCHAR(40)"`
Merged time.Time
MergerID int64 MergerID int64
Merger *User `xorm:"-"` Merger *User `xorm:"-"`
Merged time.Time `xorm:"-"`
MergedUnix int64
}
func (pr *PullRequest) BeforeUpdate() {
pr.MergedUnix = pr.Merged.UTC().Unix()
} }
// Note: don't try to get Pull because will end up recursive querying. // Note: don't try to get Pull because will end up recursive querying.
func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) { func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) {
switch colName { switch colName {
case "merged": case "merged_unix":
if !pr.HasMerged { if !pr.HasMerged {
return return
} }
pr.Merged = regulateTimeZone(pr.Merged) pr.Merged = time.Unix(pr.MergedUnix, 0).Local()
} }
} }
@ -138,7 +143,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
return err return err
} }
if err = pr.Issue.changeStatus(sess, doer, true); err != nil { if err = pr.Issue.changeStatus(sess, doer, pr.Issue.Repo, true); err != nil {
return fmt.Errorf("Issue.changeStatus: %v", err) return fmt.Errorf("Issue.changeStatus: %v", err)
} }
@ -256,6 +261,7 @@ var patchConflicts = []string{
"patch does not apply", "patch does not apply",
"already exists in working directory", "already exists in working directory",
"unrecognized input", "unrecognized input",
"error:",
} }
// testPatch checks if patch can be merged to base repository without conflit. // testPatch checks if patch can be merged to base repository without conflit.
@ -279,7 +285,7 @@ func (pr *PullRequest) testPatch() (err error) {
return nil return nil
} }
log.Trace("PullRequest[%d].testPatch(patchPath): %s", pr.ID, patchPath) log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
if err := pr.BaseRepo.UpdateLocalCopy(); err != nil { if err := pr.BaseRepo.UpdateLocalCopy(); err != nil {
return fmt.Errorf("UpdateLocalCopy: %v", err) return fmt.Errorf("UpdateLocalCopy: %v", err)
@ -287,7 +293,7 @@ func (pr *PullRequest) testPatch() (err error) {
// Checkout base branch. // Checkout base branch.
_, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(), _, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(),
fmt.Sprintf("PullRequest.Merge(git checkout): %v", pr.BaseRepo.ID), fmt.Sprintf("PullRequest.Merge (git checkout): %v", pr.BaseRepo.ID),
"git", "checkout", pr.BaseBranch) "git", "checkout", pr.BaseBranch)
if err != nil { if err != nil {
return fmt.Errorf("git checkout: %s", stderr) return fmt.Errorf("git checkout: %s", stderr)
@ -295,12 +301,12 @@ func (pr *PullRequest) testPatch() (err error) {
pr.Status = PULL_REQUEST_STATUS_CHECKING pr.Status = PULL_REQUEST_STATUS_CHECKING
_, stderr, err = process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(), _, stderr, err = process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(),
fmt.Sprintf("testPatch(git apply --check): %d", pr.BaseRepo.ID), fmt.Sprintf("testPatch (git apply --check): %d", pr.BaseRepo.ID),
"git", "apply", "--check", patchPath) "git", "apply", "--check", patchPath)
if err != nil { if err != nil {
for i := range patchConflicts { for i := range patchConflicts {
if strings.Contains(stderr, patchConflicts[i]) { if strings.Contains(stderr, patchConflicts[i]) {
log.Trace("PullRequest[%d].testPatch(apply): has conflit", pr.ID) log.Trace("PullRequest[%d].testPatch (apply): has conflit", pr.ID)
fmt.Println(stderr) fmt.Println(stderr)
pr.Status = PULL_REQUEST_STATUS_CONFLICT pr.Status = PULL_REQUEST_STATUS_CONFLICT
return nil return nil
@ -329,7 +335,7 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
ActUserID: pull.Poster.Id, ActUserID: pull.Poster.Id,
ActUserName: pull.Poster.Name, ActUserName: pull.Poster.Name,
ActEmail: pull.Poster.Email, ActEmail: pull.Poster.Email,
OpType: CREATE_PULL_REQUEST, OpType: ACTION_CREATE_PULL_REQUEST,
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Name), Content: fmt.Sprintf("%d|%s", pull.Index, pull.Name),
RepoID: repo.ID, RepoID: repo.ID,
RepoUserName: repo.Owner.Name, RepoUserName: repo.Owner.Name,
@ -481,6 +487,37 @@ func (pr *PullRequest) UpdatePatch() (err error) {
return nil return nil
} }
// PushToBaseRepo pushes commits from branches of head repository to
// corresponding branches of base repository.
// FIXME: Only push branches that are actually updates?
func (pr *PullRequest) PushToBaseRepo() (err error) {
log.Trace("PushToBaseRepo[%d]: pushing commits to base repo 'refs/pull/%d/head'", pr.BaseRepoID, pr.Index)
headRepoPath := pr.HeadRepo.RepoPath()
headGitRepo, err := git.OpenRepository(headRepoPath)
if err != nil {
return fmt.Errorf("OpenRepository: %v", err)
}
tmpRemoteName := fmt.Sprintf("tmp-pull-%d", pr.ID)
if err = headGitRepo.AddRemote(tmpRemoteName, pr.BaseRepo.RepoPath(), false); err != nil {
return fmt.Errorf("headGitRepo.AddRemote: %v", err)
}
// Make sure to remove the remote even if the push fails
defer headGitRepo.RemoveRemote(tmpRemoteName)
headFile := fmt.Sprintf("refs/pull/%d/head", pr.Index)
// Remove head in case there is a conflict.
os.Remove(path.Join(pr.BaseRepo.RepoPath(), headFile))
if err = git.Push(headRepoPath, tmpRemoteName, fmt.Sprintf("%s:%s", pr.HeadBranch, headFile)); err != nil {
return fmt.Errorf("Push: %v", err)
}
return nil
}
// AddToTaskQueue adds itself to pull request test task queue. // AddToTaskQueue adds itself to pull request test task queue.
func (pr *PullRequest) AddToTaskQueue() { func (pr *PullRequest) AddToTaskQueue() {
go PullRequestQueue.AddFunc(pr.ID, func() { go PullRequestQueue.AddFunc(pr.ID, func() {
@ -497,6 +534,9 @@ func addHeadRepoTasks(prs []*PullRequest) {
if err := pr.UpdatePatch(); err != nil { if err := pr.UpdatePatch(); err != nil {
log.Error(4, "UpdatePatch: %v", err) log.Error(4, "UpdatePatch: %v", err)
continue continue
} else if err := pr.PushToBaseRepo(); err != nil {
log.Error(4, "PushToBaseRepo: %v", err)
continue
} }
pr.AddToTaskQueue() pr.AddToTaskQueue()
@ -525,6 +565,14 @@ func AddTestPullRequestTask(repoID int64, branch string) {
} }
} }
func ChangeUsernameInPullRequests(oldUserName, newUserName string) error {
pr := PullRequest{
HeadUserName: strings.ToLower(newUserName),
}
_, err := x.Cols("head_user_name").Where("head_user_name = ?", strings.ToLower(oldUserName)).Update(pr)
return err
}
// checkAndUpdateStatus checks if pull request is possible to levaing checking status, // checkAndUpdateStatus checks if pull request is possible to levaing checking status,
// and set to be either conflict or mergeable. // and set to be either conflict or mergeable.
func (pr *PullRequest) checkAndUpdateStatus() { func (pr *PullRequest) checkAndUpdateStatus() {

14
models/release.go

@ -33,13 +33,19 @@ type Release struct {
Note string `xorm:"TEXT"` Note string `xorm:"TEXT"`
IsDraft bool `xorm:"NOT NULL DEFAULT false"` IsDraft bool `xorm:"NOT NULL DEFAULT false"`
IsPrerelease bool IsPrerelease bool
Created time.Time `xorm:"CREATED"`
Created time.Time `xorm:"-"`
CreatedUnix int64
}
func (r *Release) BeforeInsert() {
r.CreatedUnix = time.Now().UTC().Unix()
} }
func (r *Release) AfterSet(colName string, _ xorm.Cell) { func (r *Release) AfterSet(colName string, _ xorm.Cell) {
switch colName { switch colName {
case "created": case "created_unix":
r.Created = regulateTimeZone(r.Created) r.Created = time.Unix(r.CreatedUnix, 0).Local()
} }
} }
@ -125,7 +131,7 @@ func GetReleaseByID(id int64) (*Release, error) {
// GetReleasesByRepoID returns a list of releases of repository. // GetReleasesByRepoID returns a list of releases of repository.
func GetReleasesByRepoID(repoID int64) (rels []*Release, err error) { func GetReleasesByRepoID(repoID int64) (rels []*Release, err error) {
err = x.Desc("created").Find(&rels, Release{RepoID: repoID}) err = x.Desc("created_unix").Find(&rels, Release{RepoID: repoID})
return rels, err return rels, err
} }

439
models/repo.go

@ -27,12 +27,12 @@ import (
"github.com/mcuadros/go-version" "github.com/mcuadros/go-version"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"github.com/gogits/git-module" git "github.com/gogits/git-module"
api "github.com/gogits/go-gogs-client" api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/bindata" "github.com/gogits/gogs/modules/bindata"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/process" "github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
@ -129,8 +129,7 @@ func NewRepoContext() {
log.Fatal(4, "Fail to execute 'git config --global core.quotepath false': %s", stderr) log.Fatal(4, "Fail to execute 'git config --global core.quotepath false': %s", stderr)
} }
// Clean up temporary data. RemoveAllWithNotice("Clean up repository temporary data", filepath.Join(setting.AppDataPath, "tmp"))
os.RemoveAll(filepath.Join(setting.AppDataPath, "tmp"))
} }
// Repository represents a git repository. // Repository represents a git repository.
@ -178,20 +177,38 @@ type Repository struct {
ForkID int64 ForkID int64
BaseRepo *Repository `xorm:"-"` BaseRepo *Repository `xorm:"-"`
Created time.Time `xorm:"CREATED"` Created time.Time `xorm:"-"`
Updated time.Time `xorm:"UPDATED"` CreatedUnix int64
Updated time.Time `xorm:"-"`
UpdatedUnix int64
}
func (repo *Repository) BeforeInsert() {
repo.CreatedUnix = time.Now().UTC().Unix()
repo.UpdatedUnix = repo.CreatedUnix
}
func (repo *Repository) BeforeUpdate() {
repo.UpdatedUnix = time.Now().UTC().Unix()
} }
func (repo *Repository) AfterSet(colName string, _ xorm.Cell) { func (repo *Repository) AfterSet(colName string, _ xorm.Cell) {
switch colName { switch colName {
case "default_branch":
// FIXME: use models migration to solve all at once.
if len(repo.DefaultBranch) == 0 {
repo.DefaultBranch = "master"
}
case "num_closed_issues": case "num_closed_issues":
repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
case "num_closed_pulls": case "num_closed_pulls":
repo.NumOpenPulls = repo.NumPulls - repo.NumClosedPulls repo.NumOpenPulls = repo.NumPulls - repo.NumClosedPulls
case "num_closed_milestones": case "num_closed_milestones":
repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
case "updated": case "created_unix":
repo.Updated = regulateTimeZone(repo.Updated) repo.Created = time.Unix(repo.CreatedUnix, 0).Local()
case "updated_unix":
repo.Updated = time.Unix(repo.UpdatedUnix, 0)
} }
} }
@ -241,6 +258,14 @@ func (repo *Repository) ComposeMetas() map[string]string {
return repo.ExternalMetas return repo.ExternalMetas
} }
// DeleteWiki removes the actual and local copy of repository wiki.
func (repo *Repository) DeleteWiki() {
wikiPaths := []string{repo.WikiPath(), repo.LocalWikiPath()}
for _, wikiPath := range wikiPaths {
RemoveAllWithNotice("Delete repository wiki", wikiPath)
}
}
// GetAssignees returns all users that have write access of repository. // GetAssignees returns all users that have write access of repository.
func (repo *Repository) GetAssignees() (_ []*User, err error) { func (repo *Repository) GetAssignees() (_ []*User, err error) {
if err = repo.GetOwner(); err != nil { if err = repo.GetOwner(); err != nil {
@ -313,6 +338,10 @@ func (repo *Repository) RepoLink() string {
return setting.AppSubUrl + "/" + repo.MustOwner().Name + "/" + repo.Name return setting.AppSubUrl + "/" + repo.MustOwner().Name + "/" + repo.Name
} }
func (repo *Repository) RepoRelLink() string {
return "/" + repo.MustOwner().Name + "/" + repo.Name
}
func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) string { func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) string {
return fmt.Sprintf("%s/%s/compare/%s...%s", repo.MustOwner().Name, repo.Name, oldCommitID, newCommitID) return fmt.Sprintf("%s/%s/compare/%s...%s", repo.MustOwner().Name, repo.Name, oldCommitID, newCommitID)
} }
@ -332,7 +361,17 @@ func (repo *Repository) IsOwnedBy(userID int64) bool {
// CanBeForked returns true if repository meets the requirements of being forked. // CanBeForked returns true if repository meets the requirements of being forked.
func (repo *Repository) CanBeForked() bool { func (repo *Repository) CanBeForked() bool {
return !repo.IsBare && !repo.IsMirror return !repo.IsBare
}
// CanEnablePulls returns true if repository meets the requirements of accepting pulls.
func (repo *Repository) CanEnablePulls() bool {
return !repo.IsMirror
}
// AllowPulls returns true if repository meets the requirements of accepting pulls and has them enabled.
func (repo *Repository) AllowsPulls() bool {
return repo.CanEnablePulls() && repo.EnablePulls
} }
func (repo *Repository) NextIssueIndex() int64 { func (repo *Repository) NextIssueIndex() int64 {
@ -348,7 +387,7 @@ func (repo *Repository) DescriptionHtml() template.HTML {
sanitize := func(s string) string { sanitize := func(s string) string {
return fmt.Sprintf(`<a href="%[1]s" target="_blank">%[1]s</a>`, s) return fmt.Sprintf(`<a href="%[1]s" target="_blank">%[1]s</a>`, s)
} }
return template.HTML(DescPattern.ReplaceAllStringFunc(base.Sanitizer.Sanitize(repo.Description), sanitize)) return template.HTML(DescPattern.ReplaceAllStringFunc(markdown.Sanitizer.Sanitize(repo.Description), sanitize))
} }
func (repo *Repository) LocalCopyPath() string { func (repo *Repository) LocalCopyPath() string {
@ -357,11 +396,16 @@ func (repo *Repository) LocalCopyPath() string {
func updateLocalCopy(repoPath, localPath string) error { func updateLocalCopy(repoPath, localPath string) error {
if !com.IsExist(localPath) { if !com.IsExist(localPath) {
if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{}); err != nil { if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{
Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second,
}); err != nil {
return fmt.Errorf("Clone: %v", err) return fmt.Errorf("Clone: %v", err)
} }
} else { } else {
if err := git.Pull(localPath, true); err != nil { if err := git.Pull(localPath, git.PullRemoteOptions{
All: true,
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
}); err != nil {
return fmt.Errorf("Pull: %v", err) return fmt.Errorf("Pull: %v", err)
} }
} }
@ -414,7 +458,8 @@ func (repo *Repository) ComposePayload() *api.PayloadRepo {
Email: repo.MustOwner().Email, Email: repo.MustOwner().Email,
UserName: repo.MustOwner().Name, UserName: repo.MustOwner().Name,
}, },
Private: repo.IsPrivate, Private: repo.IsPrivate,
DefaultBranch: repo.DefaultBranch,
} }
} }
@ -446,10 +491,10 @@ func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
repo.Owner = repo.MustOwner() repo.Owner = repo.MustOwner()
cl := new(CloneLink) cl := new(CloneLink)
if setting.SSHPort != 22 { if setting.SSH.Port != 22 {
cl.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.SSHDomain, setting.SSHPort, repo.Owner.Name, repoName) cl.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.SSH.Domain, setting.SSH.Port, repo.Owner.Name, repoName)
} else { } else {
cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.SSHDomain, repo.Owner.Name, repoName) cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.SSH.Domain, repo.Owner.Name, repoName)
} }
cl.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, repo.Owner.Name, repoName) cl.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, repo.Owner.Name, repoName)
return cl return cl
@ -490,16 +535,28 @@ func IsUsableName(name string) error {
// Mirror represents a mirror information of repository. // Mirror represents a mirror information of repository.
type Mirror struct { type Mirror struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
RepoID int64 RepoID int64
Repo *Repository `xorm:"-"` Repo *Repository `xorm:"-"`
Interval int // Hour. Interval int // Hour.
Updated time.Time `xorm:"UPDATED"`
NextUpdate time.Time Updated time.Time `xorm:"-"`
UpdatedUnix int64
NextUpdate time.Time `xorm:"-"`
NextUpdateUnix int64
address string `xorm:"-"` address string `xorm:"-"`
} }
func (m *Mirror) BeforeInsert() {
m.NextUpdateUnix = m.NextUpdate.UTC().Unix()
}
func (m *Mirror) BeforeUpdate() {
m.UpdatedUnix = time.Now().UTC().Unix()
m.NextUpdateUnix = m.NextUpdate.UTC().Unix()
}
func (m *Mirror) AfterSet(colName string, _ xorm.Cell) { func (m *Mirror) AfterSet(colName string, _ xorm.Cell) {
var err error var err error
switch colName { switch colName {
@ -508,6 +565,10 @@ func (m *Mirror) AfterSet(colName string, _ xorm.Cell) {
if err != nil { if err != nil {
log.Error(3, "GetRepositoryByID[%d]: %v", m.ID, err) log.Error(3, "GetRepositoryByID[%d]: %v", m.ID, err)
} }
case "updated_unix":
m.Updated = time.Unix(m.UpdatedUnix, 0).Local()
case "next_updated_unix":
m.NextUpdate = time.Unix(m.NextUpdateUnix, 0).Local()
} }
} }
@ -591,6 +652,11 @@ func UpdateMirror(m *Mirror) error {
return updateMirror(x, m) return updateMirror(x, m)
} }
func DeleteMirrorByRepoID(repoID int64) error {
_, err := x.Delete(&Mirror{RepoID: repoID})
return err
}
func createUpdateHook(repoPath string) error { func createUpdateHook(repoPath string) error {
return git.SetUpdateHook(repoPath, return git.SetUpdateHook(repoPath,
fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf)) fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf))
@ -636,11 +702,36 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
if err = git.Clone(opts.RemoteAddr, repoPath, git.CloneRepoOptions{ if err = git.Clone(opts.RemoteAddr, repoPath, git.CloneRepoOptions{
Mirror: true, Mirror: true,
Quiet: true, Quiet: true,
Timeout: 10 * time.Minute, Timeout: time.Duration(setting.Git.Timeout.Migrate) * time.Second,
}); err != nil { }); err != nil {
return repo, fmt.Errorf("Clone: %v", err) return repo, fmt.Errorf("Clone: %v", err)
} }
// Check if repository is empty.
_, stderr, err := com.ExecCmdDir(repoPath, "git", "log", "-1")
if err != nil {
if strings.Contains(stderr, "fatal: bad default revision 'HEAD'") {
repo.IsBare = true
} else {
return repo, fmt.Errorf("check bare: %v - %s", err, stderr)
}
}
if !repo.IsBare {
// Try to get HEAD branch and set it as default branch.
gitRepo, err := git.OpenRepository(repoPath)
if err != nil {
return repo, fmt.Errorf("OpenRepository: %v", err)
}
headBranch, err := gitRepo.GetHEADBranch()
if err != nil {
return repo, fmt.Errorf("GetHEADBranch: %v", err)
}
if headBranch != nil {
repo.DefaultBranch = headBranch.Name
}
}
if opts.IsMirror { if opts.IsMirror {
if _, err = x.InsertOne(&Mirror{ if _, err = x.InsertOne(&Mirror{
RepoID: repo.ID, RepoID: repo.ID,
@ -654,7 +745,12 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
return repo, UpdateRepository(repo, false) return repo, UpdateRepository(repo, false)
} }
if err = createUpdateHook(repoPath); err != nil { return CleanUpMigrateInfo(repo, repoPath)
}
// Finish migrating repository with things that don't need to be done for mirrors.
func CleanUpMigrateInfo(repo *Repository, repoPath string) (*Repository, error) {
if err := createUpdateHook(repoPath); err != nil {
return repo, fmt.Errorf("createUpdateHook: %v", err) return repo, fmt.Errorf("createUpdateHook: %v", err)
} }
@ -670,31 +766,6 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
return repo, fmt.Errorf("save config file: %v", err) return repo, fmt.Errorf("save config file: %v", err)
} }
// Check if repository is empty.
_, stderr, err := com.ExecCmdDir(repoPath, "git", "log", "-1")
if err != nil {
if strings.Contains(stderr, "fatal: bad default revision 'HEAD'") {
repo.IsBare = true
} else {
return repo, fmt.Errorf("check bare: %v - %s", err, stderr)
}
}
// Try to get HEAD branch and set it as default branch.
gitRepo, err := git.OpenRepository(repoPath)
if err != nil {
log.Error(4, "OpenRepository: %v", err)
return repo, nil
}
headBranch, err := gitRepo.GetHEADBranch()
if err != nil {
log.Error(4, "GetHEADBranch: %v", err)
return repo, nil
}
if headBranch != nil {
repo.DefaultBranch = headBranch.Name
}
return repo, UpdateRepository(repo, false) return repo, UpdateRepository(repo, false)
} }
@ -957,10 +1028,13 @@ func countRepositories(showPrivate bool) int64 {
sess := x.NewSession() sess := x.NewSession()
if !showPrivate { if !showPrivate {
sess.Where("is_private=", false) sess.Where("is_private=?", false)
} }
count, _ := sess.Count(new(Repository)) count, err := sess.Count(new(Repository))
if err != nil {
log.Error(4, "countRepositories: %v", err)
}
return count return count
} }
@ -974,11 +1048,16 @@ func CountPublicRepositories() int64 {
return countRepositories(false) return countRepositories(false)
} }
func Repositories(page, pageSize int) (_ []*Repository, err error) {
repos := make([]*Repository, 0, pageSize)
return repos, x.Limit(pageSize, (page-1)*pageSize).Asc("id").Find(&repos)
}
// RepositoriesWithUsers returns number of repos in given page. // RepositoriesWithUsers returns number of repos in given page.
func RepositoriesWithUsers(page, pageSize int) (_ []*Repository, err error) { func RepositoriesWithUsers(page, pageSize int) (_ []*Repository, err error) {
repos := make([]*Repository, 0, pageSize) repos, err := Repositories(page, pageSize)
if err = x.Limit(pageSize, (page-1)*pageSize).Asc("id").Find(&repos); err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("Repositories: %v", err)
} }
for i := range repos { for i := range repos {
@ -1093,13 +1172,16 @@ func TransferOwnership(u *User, newOwnerName string, repo *Repository) error {
return fmt.Errorf("transferRepoAction: %v", err) return fmt.Errorf("transferRepoAction: %v", err)
} }
// Change repository directory name. // Rename remote repository to new path and delete local copy.
if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil {
return fmt.Errorf("rename repository directory: %v", err) return fmt.Errorf("rename repository directory: %v", err)
} }
RemoveAllWithNotice("Delete repository local copy", repo.LocalCopyPath())
// Rename remote wiki repository to new path and delete local copy.
wikiPath := WikiPath(owner.Name, repo.Name) wikiPath := WikiPath(owner.Name, repo.Name)
if com.IsExist(wikiPath) { if com.IsExist(wikiPath) {
RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath())
if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil { if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil {
return fmt.Errorf("rename repository wiki: %v", err) return fmt.Errorf("rename repository wiki: %v", err)
} }
@ -1123,16 +1205,22 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error)
return ErrRepoAlreadyExist{u.Name, newRepoName} return ErrRepoAlreadyExist{u.Name, newRepoName}
} }
repo, err := GetRepositoryByName(u.Id, oldRepoName)
if err != nil {
return fmt.Errorf("GetRepositoryByName: %v", err)
}
// Change repository directory name. // Change repository directory name.
if err = os.Rename(RepoPath(u.Name, oldRepoName), RepoPath(u.Name, newRepoName)); err != nil { if err = os.Rename(repo.RepoPath(), RepoPath(u.Name, newRepoName)); err != nil {
return fmt.Errorf("rename repository directory: %v", err) return fmt.Errorf("rename repository directory: %v", err)
} }
wikiPath := WikiPath(u.Name, oldRepoName) wikiPath := repo.WikiPath()
if com.IsExist(wikiPath) { if com.IsExist(wikiPath) {
if err = os.Rename(wikiPath, WikiPath(u.Name, newRepoName)); err != nil { if err = os.Rename(wikiPath, WikiPath(u.Name, newRepoName)); err != nil {
return fmt.Errorf("rename repository wiki: %v", err) return fmt.Errorf("rename repository wiki: %v", err)
} }
RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath())
} }
return nil return nil
@ -1295,30 +1383,13 @@ func DeleteRepository(uid, repoID int64) error {
// Remove repository files. // Remove repository files.
repoPath := repo.repoPath(sess) repoPath := repo.repoPath(sess)
if err = os.RemoveAll(repoPath); err != nil { RemoveAllWithNotice("Delete repository files", repoPath)
desc := fmt.Sprintf("delete repository files [%s]: %v", repoPath, err)
log.Warn(desc)
if err = CreateRepositoryNotice(desc); err != nil {
log.Error(4, "CreateRepositoryNotice: %v", err)
}
}
wikiPaths := []string{repo.WikiPath(), repo.LocalWikiPath()} repo.DeleteWiki()
for _, wikiPath := range wikiPaths {
if err = os.RemoveAll(wikiPath); err != nil {
desc := fmt.Sprintf("delete repository wiki [%s]: %v", wikiPath, err)
log.Warn(desc)
if err = CreateRepositoryNotice(desc); err != nil {
log.Error(4, "CreateRepositoryNotice: %v", err)
}
}
}
// Remove attachment files. // Remove attachment files.
for i := range attachmentPaths { for i := range attachmentPaths {
if err = os.Remove(attachmentPaths[i]); err != nil { RemoveAllWithNotice("Delete attachment", attachmentPaths[i])
log.Warn("delete attachment: %v", err)
}
} }
if err = sess.Commit(); err != nil { if err = sess.Commit(); err != nil {
@ -1333,7 +1404,7 @@ func DeleteRepository(uid, repoID int64) error {
} }
for i := range forkRepos { for i := range forkRepos {
if err = DeleteRepository(forkRepos[i].OwnerID, forkRepos[i].ID); err != nil { if err = DeleteRepository(forkRepos[i].OwnerID, forkRepos[i].ID); err != nil {
log.Error(4, "updateRepository[%d]: %v", forkRepos[i].ID, err) log.Error(4, "DeleteRepository [%d]: %v", forkRepos[i].ID, err)
} }
} }
} else { } else {
@ -1397,7 +1468,8 @@ func GetRepositoryByID(id int64) (*Repository, error) {
// GetRepositories returns a list of repositories of given user. // GetRepositories returns a list of repositories of given user.
func GetRepositories(uid int64, private bool) ([]*Repository, error) { func GetRepositories(uid int64, private bool) ([]*Repository, error) {
repos := make([]*Repository, 0, 10) repos := make([]*Repository, 0, 10)
sess := x.Desc("updated") sess := x.Desc("updated_unix")
if !private { if !private {
sess.Where("is_private=?", false) sess.Where("is_private=?", false)
} }
@ -1406,9 +1478,9 @@ func GetRepositories(uid int64, private bool) ([]*Repository, error) {
} }
// GetRecentUpdatedRepositories returns the list of repositories that are recently updated. // GetRecentUpdatedRepositories returns the list of repositories that are recently updated.
func GetRecentUpdatedRepositories(page int) (repos []*Repository, err error) { func GetRecentUpdatedRepositories(page, pageSize int) (repos []*Repository, err error) {
return repos, x.Limit(setting.ExplorePagingNum, (page-1)*setting.ExplorePagingNum). return repos, x.Limit(pageSize, (page-1)*pageSize).
Where("is_private=?", false).Limit(setting.ExplorePagingNum).Desc("updated").Find(&repos) Where("is_private=?", false).Limit(pageSize).Desc("updated_unix").Find(&repos)
} }
func getRepositoryCount(e Engine, u *User) (int64, error) { func getRepositoryCount(e Engine, u *User) (int64, error) {
@ -1420,32 +1492,52 @@ func GetRepositoryCount(u *User) (int64, error) {
return getRepositoryCount(x, u) return getRepositoryCount(x, u)
} }
type SearchOption struct { type SearchRepoOptions struct {
Keyword string Keyword string
Uid int64 OwnerID int64
Limit int OrderBy string
Private bool Private bool // Include private repositories in results
Page int
PageSize int // Can be smaller than or equal to setting.ExplorePagingNum
} }
// SearchRepositoryByName returns given number of repositories whose name contains keyword. // SearchRepositoryByName takes keyword and part of repository name to search,
func SearchRepositoryByName(opt SearchOption) (repos []*Repository, err error) { // it returns results in given range and number of total results.
if len(opt.Keyword) == 0 { func SearchRepositoryByName(opts *SearchRepoOptions) (repos []*Repository, _ int64, _ error) {
return repos, nil if len(opts.Keyword) == 0 {
return repos, 0, nil
}
opts.Keyword = strings.ToLower(opts.Keyword)
if opts.PageSize <= 0 || opts.PageSize > setting.ExplorePagingNum {
opts.PageSize = setting.ExplorePagingNum
}
if opts.Page <= 0 {
opts.Page = 1
} }
opt.Keyword = strings.ToLower(opt.Keyword)
repos = make([]*Repository, 0, opt.Limit) repos = make([]*Repository, 0, opts.PageSize)
// Append conditions. // Append conditions
sess := x.Limit(opt.Limit) sess := x.Where("LOWER(lower_name) LIKE ?", "%"+opts.Keyword+"%")
if opt.Uid > 0 { if opts.OwnerID > 0 {
sess.Where("owner_id=?", opt.Uid) sess.And("owner_id = ?", opts.OwnerID)
} }
if !opt.Private { if !opts.Private {
sess.And("is_private=?", false) sess.And("is_private=?", false)
} }
sess.And("lower_name like ?", "%"+opt.Keyword+"%").Find(&repos)
return repos, err var countSess xorm.Session
countSess = *sess
count, err := countSess.Count(new(Repository))
if err != nil {
return nil, 0, fmt.Errorf("Count: %v", err)
}
if len(opts.OrderBy) > 0 {
sess.OrderBy(opts.OrderBy)
}
return repos, count, sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&repos)
} }
// DeleteRepositoryArchives deletes all repositories' archives. // DeleteRepositoryArchives deletes all repositories' archives.
@ -1457,9 +1549,8 @@ func DeleteRepositoryArchives() error {
}) })
} }
// DeleteMissingRepositories deletes all repository records that lost Git files. func gatherMissingRepoRecords() ([]*Repository, error) {
func DeleteMissingRepositories() error { repos := make([]*Repository, 0, 10)
repos := make([]*Repository, 0, 5)
if err := x.Where("id > 0").Iterate(new(Repository), if err := x.Where("id > 0").Iterate(new(Repository),
func(idx int, bean interface{}) error { func(idx int, bean interface{}) error {
repo := bean.(*Repository) repo := bean.(*Repository)
@ -1468,10 +1559,18 @@ func DeleteMissingRepositories() error {
} }
return nil return nil
}); err != nil { }); err != nil {
if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteMissingRepositories: %v", err)); err2 != nil { if err2 := CreateRepositoryNotice(fmt.Sprintf("gatherMissingRepoRecords: %v", err)); err2 != nil {
log.Error(4, "CreateRepositoryNotice: %v", err2) return nil, fmt.Errorf("CreateRepositoryNotice: %v", err)
} }
return nil }
return repos, nil
}
// DeleteMissingRepositories deletes all repository records that lost Git files.
func DeleteMissingRepositories() error {
repos, err := gatherMissingRepoRecords()
if err != nil {
return fmt.Errorf("gatherMissingRepoRecords: %v", err)
} }
if len(repos) == 0 { if len(repos) == 0 {
@ -1482,7 +1581,29 @@ func DeleteMissingRepositories() error {
log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID) log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID)
if err := DeleteRepository(repo.OwnerID, repo.ID); err != nil { if err := DeleteRepository(repo.OwnerID, repo.ID); err != nil {
if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteRepository [%d]: %v", repo.ID, err)); err2 != nil { if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteRepository [%d]: %v", repo.ID, err)); err2 != nil {
log.Error(4, "CreateRepositoryNotice: %v", err2) return fmt.Errorf("CreateRepositoryNotice: %v", err)
}
}
}
return nil
}
// ReinitMissingRepositories reinitializes all repository records that lost Git files.
func ReinitMissingRepositories() error {
repos, err := gatherMissingRepoRecords()
if err != nil {
return fmt.Errorf("gatherMissingRepoRecords: %v", err)
}
if len(repos) == 0 {
return nil
}
for _, repo := range repos {
log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
if err := git.InitRepository(repo.RepoPath(), true); err != nil {
if err2 := CreateRepositoryNotice(fmt.Sprintf("InitRepository [%d]: %v", repo.ID, err)); err2 != nil {
return fmt.Errorf("CreateRepositoryNotice: %v", err)
} }
} }
} }
@ -1562,7 +1683,8 @@ func MirrorUpdate() {
} }
repoPath := m.Repo.RepoPath() repoPath := m.Repo.RepoPath()
if _, stderr, err := process.ExecDir(10*time.Minute, if _, stderr, err := process.ExecDir(
time.Duration(setting.Git.Timeout.Mirror)*time.Second,
repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath), repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath),
"git", "remote", "update", "--prune"); err != nil { "git", "remote", "update", "--prune"); err != nil {
desc := fmt.Sprintf("Fail to update mirror repository(%s): %s", repoPath, stderr) desc := fmt.Sprintf("Fail to update mirror repository(%s): %s", repoPath, stderr)
@ -1602,7 +1724,7 @@ func GitFsck() {
repo := bean.(*Repository) repo := bean.(*Repository)
repoPath := repo.RepoPath() repoPath := repo.RepoPath()
if err := git.Fsck(repoPath, setting.Cron.RepoHealthCheck.Timeout, setting.Cron.RepoHealthCheck.Args...); err != nil { if err := git.Fsck(repoPath, setting.Cron.RepoHealthCheck.Timeout, setting.Cron.RepoHealthCheck.Args...); err != nil {
desc := fmt.Sprintf("Fail to health check repository(%s)", repoPath) desc := fmt.Sprintf("Fail to health check repository (%s): %v", repoPath, err)
log.Warn(desc) log.Warn(desc)
if err = CreateRepositoryNotice(desc); err != nil { if err = CreateRepositoryNotice(desc); err != nil {
log.Error(4, "CreateRepositoryNotice: %v", err) log.Error(4, "CreateRepositoryNotice: %v", err)
@ -1728,105 +1850,6 @@ func CheckRepoStats() {
// ***** END: Repository.NumForks ***** // ***** END: Repository.NumForks *****
} }
// _________ .__ .__ ___. __ .__
// \_ ___ \ ____ | | | | _____ \_ |__ ________________ _/ |_|__| ____ ____
// / \ \/ / _ \| | | | \__ \ | __ \ / _ \_ __ \__ \\ __\ |/ _ \ / \
// \ \___( <_> ) |_| |__/ __ \| \_\ ( <_> ) | \// __ \| | | ( <_> ) | \
// \______ /\____/|____/____(____ /___ /\____/|__| (____ /__| |__|\____/|___| /
// \/ \/ \/ \/ \/
// A Collaboration is a relation between an individual and a repository
type Collaboration struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
Created time.Time `xorm:"CREATED"`
}
// Add collaborator and accompanying access
func (repo *Repository) AddCollaborator(u *User) error {
collaboration := &Collaboration{
RepoID: repo.ID,
UserID: u.Id,
}
has, err := x.Get(collaboration)
if err != nil {
return err
} else if has {
return nil
}
if err = repo.GetOwner(); err != nil {
return fmt.Errorf("GetOwner: %v", err)
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.InsertOne(collaboration); err != nil {
return err
}
if repo.Owner.IsOrganization() {
err = repo.recalculateTeamAccesses(sess, 0)
} else {
err = repo.recalculateAccesses(sess)
}
if err != nil {
return fmt.Errorf("recalculateAccesses 'team=%v': %v", repo.Owner.IsOrganization(), err)
}
return sess.Commit()
}
func (repo *Repository) getCollaborators(e Engine) ([]*User, error) {
collaborations := make([]*Collaboration, 0)
if err := e.Find(&collaborations, &Collaboration{RepoID: repo.ID}); err != nil {
return nil, err
}
users := make([]*User, len(collaborations))
for i, c := range collaborations {
user, err := getUserByID(e, c.UserID)
if err != nil {
return nil, err
}
users[i] = user
}
return users, nil
}
// GetCollaborators returns the collaborators for a repository
func (repo *Repository) GetCollaborators() ([]*User, error) {
return repo.getCollaborators(x)
}
// Delete collaborator and accompanying access
func (repo *Repository) DeleteCollaborator(u *User) (err error) {
collaboration := &Collaboration{
RepoID: repo.ID,
UserID: u.Id,
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if has, err := sess.Delete(collaboration); err != nil || has == 0 {
return err
} else if err = repo.recalculateAccesses(sess); err != nil {
return err
}
return sess.Commit()
}
// __ __ __ .__ // __ __ __ .__
// / \ / \_____ _/ |_ ____ | |__ // / \ / \_____ _/ |_ ____ | |__
// \ \/\/ /\__ \\ __\/ ___\| | \ // \ \/\/ /\__ \\ __\/ ___\| | \

57
models/repo_branch.go

@ -0,0 +1,57 @@
// Copyright 2016 The Gogs 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 models
import (
"github.com/gogits/git-module"
)
type Branch struct {
Path string
Name string
}
func GetBranchesByPath(path string) ([]*Branch, error) {
gitRepo, err := git.OpenRepository(path)
if err != nil {
return nil, err
}
brs, err := gitRepo.GetBranches()
if err != nil {
return nil, err
}
branches := make([]*Branch, len(brs))
for i := range brs {
branches[i] = &Branch{
Path: path,
Name: brs[i],
}
}
return branches, nil
}
func (repo *Repository) GetBranch(br string) (*Branch, error) {
if !git.IsBranchExist(repo.RepoPath(), br) {
return nil, &ErrBranchNotExist{br}
}
return &Branch{
Path: repo.RepoPath(),
Name: br,
}, nil
}
func (repo *Repository) GetBranches() ([]*Branch, error) {
return GetBranchesByPath(repo.RepoPath())
}
func (br *Branch) GetCommit() (*git.Commit, error) {
gitRepo, err := git.OpenRepository(br.Path)
if err != nil {
return nil, err
}
return gitRepo.GetBranchCommit(br.Name)
}

159
models/repo_collaboration.go

@ -0,0 +1,159 @@
// Copyright 2016 The Gogs 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 models
import (
"fmt"
)
// Collaboration represent the relation between an individual and a repository.
type Collaboration struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
Mode AccessMode `xorm:"DEFAULT 2 NOT NULL"`
}
func (c *Collaboration) ModeName() string {
switch c.Mode {
case ACCESS_MODE_READ:
return "Read"
case ACCESS_MODE_WRITE:
return "Write"
case ACCESS_MODE_ADMIN:
return "Admin"
}
return "Undefined"
}
// AddCollaborator adds new collaboration relation between an individual and a repository.
func (repo *Repository) AddCollaborator(u *User) error {
collaboration := &Collaboration{
RepoID: repo.ID,
UserID: u.Id,
}
has, err := x.Get(collaboration)
if err != nil {
return err
} else if has {
return nil
}
collaboration.Mode = ACCESS_MODE_WRITE
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.InsertOne(collaboration); err != nil {
return err
}
if repo.Owner.IsOrganization() {
err = repo.recalculateTeamAccesses(sess, 0)
} else {
err = repo.recalculateAccesses(sess)
}
if err != nil {
return fmt.Errorf("recalculateAccesses 'team=%v': %v", repo.Owner.IsOrganization(), err)
}
return sess.Commit()
}
func (repo *Repository) getCollaborations(e Engine) ([]*Collaboration, error) {
collaborations := make([]*Collaboration, 0)
return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repo.ID})
}
// Collaborator represents a user with collaboration details.
type Collaborator struct {
*User
Collaboration *Collaboration
}
func (repo *Repository) getCollaborators(e Engine) ([]*Collaborator, error) {
collaborations, err := repo.getCollaborations(e)
if err != nil {
return nil, fmt.Errorf("getCollaborations: %v", err)
}
collaborators := make([]*Collaborator, len(collaborations))
for i, c := range collaborations {
user, err := getUserByID(e, c.UserID)
if err != nil {
return nil, err
}
collaborators[i] = &Collaborator{
User: user,
Collaboration: c,
}
}
return collaborators, nil
}
// GetCollaborators returns the collaborators for a repository
func (repo *Repository) GetCollaborators() ([]*Collaborator, error) {
return repo.getCollaborators(x)
}
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode) error {
// Discard invalid input
if mode <= ACCESS_MODE_NONE || mode > ACCESS_MODE_OWNER {
return nil
}
collaboration := &Collaboration{
RepoID: repo.ID,
UserID: uid,
}
has, err := x.Get(collaboration)
if err != nil {
return fmt.Errorf("get collaboration: %v", err)
} else if !has {
return nil
}
collaboration.Mode = mode
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Id(collaboration.ID).AllCols().Update(collaboration); err != nil {
return fmt.Errorf("update collaboration: %v", err)
} else if _, err = sess.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, uid, repo.ID); err != nil {
return fmt.Errorf("update access table: %v", err)
}
return sess.Commit()
}
// DeleteCollaboration removes collaboration relation between the user and repository.
func (repo *Repository) DeleteCollaboration(uid int64) (err error) {
collaboration := &Collaboration{
RepoID: repo.ID,
UserID: uid,
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if has, err := sess.Delete(collaboration); err != nil || has == 0 {
return err
} else if err = repo.recalculateAccesses(sess); err != nil {
return err
}
return sess.Commit()
}

241
models/ssh_key.go

@ -12,6 +12,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"math/big"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
@ -44,22 +45,36 @@ const (
// PublicKey represents a SSH or deploy key. // PublicKey represents a SSH or deploy key.
type PublicKey struct { type PublicKey struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
OwnerID int64 `xorm:"INDEX NOT NULL"` OwnerID int64 `xorm:"INDEX NOT NULL"`
Name string `xorm:"NOT NULL"` Name string `xorm:"NOT NULL"`
Fingerprint string `xorm:"NOT NULL"` Fingerprint string `xorm:"NOT NULL"`
Content string `xorm:"TEXT NOT NULL"` Content string `xorm:"TEXT NOT NULL"`
Mode AccessMode `xorm:"NOT NULL DEFAULT 2"` Mode AccessMode `xorm:"NOT NULL DEFAULT 2"`
Type KeyType `xorm:"NOT NULL DEFAULT 1"` Type KeyType `xorm:"NOT NULL DEFAULT 1"`
Created time.Time `xorm:"CREATED"`
Updated time.Time // Note: Updated must below Created for AfterSet. Created time.Time `xorm:"-"`
HasRecentActivity bool `xorm:"-"` CreatedUnix int64
HasUsed bool `xorm:"-"` Updated time.Time `xorm:"-"` // Note: Updated must below Created for AfterSet.
UpdatedUnix int64
HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"`
}
func (k *PublicKey) BeforeInsert() {
k.CreatedUnix = time.Now().UTC().Unix()
}
func (k *PublicKey) BeforeUpdate() {
k.UpdatedUnix = time.Now().UTC().Unix()
} }
func (k *PublicKey) AfterSet(colName string, _ xorm.Cell) { func (k *PublicKey) AfterSet(colName string, _ xorm.Cell) {
switch colName { switch colName {
case "created": case "created_unix":
k.Created = time.Unix(k.CreatedUnix, 0).Local()
case "updated_unix":
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
k.HasUsed = k.Updated.After(k.Created) k.HasUsed = k.Updated.After(k.Created)
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now()) k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now())
} }
@ -78,19 +93,18 @@ func (key *PublicKey) GetAuthorizedString() string {
func extractTypeFromBase64Key(key string) (string, error) { func extractTypeFromBase64Key(key string) (string, error) {
b, err := base64.StdEncoding.DecodeString(key) b, err := base64.StdEncoding.DecodeString(key)
if err != nil || len(b) < 4 { if err != nil || len(b) < 4 {
return "", errors.New("Invalid key format") return "", fmt.Errorf("Invalid key format: %v", err)
} }
keyLength := int(binary.BigEndian.Uint32(b)) keyLength := int(binary.BigEndian.Uint32(b))
if len(b) < 4+keyLength { if len(b) < 4+keyLength {
return "", errors.New("Invalid key format") return "", fmt.Errorf("Invalid key format: not enough length")
} }
return string(b[4 : 4+keyLength]), nil return string(b[4 : 4+keyLength]), nil
} }
// parseKeyString parses any key string in openssh or ssh2 format to clean openssh string (rfc4253) // parseKeyString parses any key string in OpenSSH or SSH2 format to clean OpenSSH string (RFC4253)
func parseKeyString(content string) (string, error) { func parseKeyString(content string) (string, error) {
// Transform all legal line endings to a single "\n" // Transform all legal line endings to a single "\n"
s := strings.Replace(strings.Replace(strings.TrimSpace(content), "\r\n", "\n", -1), "\r", "\n", -1) s := strings.Replace(strings.Replace(strings.TrimSpace(content), "\r\n", "\n", -1), "\r", "\n", -1)
@ -153,8 +167,115 @@ func parseKeyString(content string) (string, error) {
return keyType + " " + keyContent + " " + keyComment, nil return keyType + " " + keyContent + " " + keyComment, nil
} }
// writeTmpKeyFile writes key content to a temporary file
// and returns the name of that file, along with any possible errors.
func writeTmpKeyFile(content string) (string, error) {
tmpFile, err := ioutil.TempFile(setting.SSH.KeyTestPath, "gogs_keytest")
if err != nil {
return "", fmt.Errorf("TempFile: %v", err)
}
defer tmpFile.Close()
if _, err = tmpFile.WriteString(content); err != nil {
return "", fmt.Errorf("tmpFile.WriteString: %v", err)
}
return tmpFile.Name(), nil
}
// SSHKeyGenParsePublicKey extracts key type and length using ssh-keygen.
func SSHKeyGenParsePublicKey(key string) (string, int, error) {
// The ssh-keygen in Windows does not print key type, so no need go further.
if setting.IsWindows {
return "", 0, nil
}
tmpName, err := writeTmpKeyFile(key)
if err != nil {
return "", 0, fmt.Errorf("writeTmpKeyFile: %v", err)
}
defer os.Remove(tmpName)
stdout, stderr, err := process.Exec("SSHKeyGenParsePublicKey", setting.SSH.KeygenPath, "-lf", tmpName)
if err != nil {
return "", 0, fmt.Errorf("Fail to parse public key: %s - %s", err, stderr)
}
if strings.Contains(stdout, "is not a public key file") {
return "", 0, ErrKeyUnableVerify{stdout}
}
fields := strings.Split(stdout, " ")
if len(fields) < 4 {
return "", 0, fmt.Errorf("Invalid public key line: %s", stdout)
}
keyType := strings.Trim(fields[len(fields)-1], "()\r\n")
return strings.ToLower(keyType), com.StrTo(fields[0]).MustInt(), nil
}
// SSHNativeParsePublicKey extracts the key type and length using the golang SSH library.
// NOTE: ed25519 is not supported.
func SSHNativeParsePublicKey(keyLine string) (string, int, error) {
fields := strings.Fields(keyLine)
if len(fields) < 2 {
return "", 0, fmt.Errorf("not enough fields in public key line: %s", string(keyLine))
}
raw, err := base64.StdEncoding.DecodeString(fields[1])
if err != nil {
return "", 0, err
}
pkey, err := ssh.ParsePublicKey(raw)
if err != nil {
if strings.Contains(err.Error(), "ssh: unknown key algorithm") {
return "", 0, ErrKeyUnableVerify{err.Error()}
}
return "", 0, fmt.Errorf("ssh.ParsePublicKey: %v", err)
}
// The ssh library can parse the key, so next we find out what key exactly we have.
switch pkey.Type() {
case ssh.KeyAlgoDSA:
rawPub := struct {
Name string
P, Q, G, Y *big.Int
}{}
if err := ssh.Unmarshal(pkey.Marshal(), &rawPub); err != nil {
return "", 0, err
}
// as per https://bugzilla.mindrot.org/show_bug.cgi?id=1647 we should never
// see dsa keys != 1024 bit, but as it seems to work, we will not check here
return "dsa", rawPub.P.BitLen(), nil // use P as per crypto/dsa/dsa.go (is L)
case ssh.KeyAlgoRSA:
rawPub := struct {
Name string
E *big.Int
N *big.Int
}{}
if err := ssh.Unmarshal(pkey.Marshal(), &rawPub); err != nil {
return "", 0, err
}
return "rsa", rawPub.N.BitLen(), nil // use N as per crypto/rsa/rsa.go (is bits)
case ssh.KeyAlgoECDSA256:
return "ecdsa", 256, nil
case ssh.KeyAlgoECDSA384:
return "ecdsa", 384, nil
case ssh.KeyAlgoECDSA521:
return "ecdsa", 521, nil
case "ssh-ed25519": // TODO replace with ssh constant when available
return "ed25519", 256, nil
}
return "", 0, fmt.Errorf("Unsupported key length detection for type: %s", pkey.Type())
}
// CheckPublicKeyString checks if the given public key string is recognized by SSH. // CheckPublicKeyString checks if the given public key string is recognized by SSH.
//
// The function returns the actual public key line on success.
func CheckPublicKeyString(content string) (_ string, err error) { func CheckPublicKeyString(content string) (_ string, err error) {
if setting.SSH.Disabled {
return "", errors.New("SSH is disabled")
}
content, err = parseKeyString(content) content, err = parseKeyString(content)
if err != nil { if err != nil {
return "", err return "", err
@ -165,22 +286,32 @@ func CheckPublicKeyString(content string) (_ string, err error) {
return "", errors.New("only a single line with a single key please") return "", errors.New("only a single line with a single key please")
} }
fields := strings.Fields(content) // remove any unnecessary whitespace now
if len(fields) < 2 { content = strings.TrimSpace(content)
return "", errors.New("too less fields")
}
key, err := base64.StdEncoding.DecodeString(fields[1]) var (
if err != nil { keyType string
return "", fmt.Errorf("StdEncoding.DecodeString: %v", err) length int
)
if setting.SSH.StartBuiltinServer {
keyType, length, err = SSHNativeParsePublicKey(content)
} else {
keyType, length, err = SSHKeyGenParsePublicKey(content)
} }
pkey, err := ssh.ParsePublicKey([]byte(key))
if err != nil { if err != nil {
return "", fmt.Errorf("ParsePublicKey: %v", err) return "", fmt.Errorf("ParsePublicKey: %v", err)
} }
log.Trace("Key type: %s", pkey.Type()) log.Trace("Key info [native: %v]: %s-%d", setting.SSH.StartBuiltinServer, keyType, length)
return content, nil if !setting.SSH.MinimumKeySizeCheck {
return content, nil
}
if minLen, found := setting.SSH.MinimumKeySizes[keyType]; found && length >= minLen {
return content, nil
} else if found && length < minLen {
return "", fmt.Errorf("Key length is not enough: got %d, needs %d", length, minLen)
}
return "", fmt.Errorf("Key type is not allowed: %s", keyType)
} }
// saveAuthorizedKeyFile writes SSH key content to authorized_keys file. // saveAuthorizedKeyFile writes SSH key content to authorized_keys file.
@ -188,7 +319,7 @@ func saveAuthorizedKeyFile(keys ...*PublicKey) error {
sshOpLocker.Lock() sshOpLocker.Lock()
defer sshOpLocker.Unlock() defer sshOpLocker.Unlock()
fpath := filepath.Join(setting.SSHRootPath, "authorized_keys") fpath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
f, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) f, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if err != nil { if err != nil {
return err return err
@ -244,7 +375,7 @@ func addKey(e Engine, key *PublicKey) (err error) {
} }
stdout, stderr, err := process.Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath) stdout, stderr, err := process.Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath)
if err != nil { if err != nil {
return errors.New("ssh-keygen -lf: " + stderr) return fmt.Errorf("'ssh-keygen -lf %s' failed with error '%s': %s", tmpPath, err, stderr)
} else if len(stdout) < 2 { } else if len(stdout) < 2 {
return errors.New("not enough output for calculating fingerprint: " + stdout) return errors.New("not enough output for calculating fingerprint: " + stdout)
} }
@ -256,7 +387,7 @@ func addKey(e Engine, key *PublicKey) (err error) {
} }
// Don't need to rewrite this file if builtin SSH server is enabled. // Don't need to rewrite this file if builtin SSH server is enabled.
if setting.StartSSHServer { if setting.SSH.StartBuiltinServer {
return nil return nil
} }
return saveAuthorizedKeyFile(key) return saveAuthorizedKeyFile(key)
@ -264,6 +395,7 @@ func addKey(e Engine, key *PublicKey) (err error) {
// AddPublicKey adds new public key to database and authorized_keys file. // AddPublicKey adds new public key to database and authorized_keys file.
func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) { func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
log.Trace(content)
if err := checkKeyContent(content); err != nil { if err := checkKeyContent(content); err != nil {
return nil, err return nil, err
} }
@ -374,6 +506,11 @@ func rewriteAuthorizedKeys(key *PublicKey, p, tmpP string) error {
break break
} }
} }
if !isFound {
log.Warn("SSH key %d not found in authorized_keys file for deletion", key.ID)
}
return nil return nil
} }
@ -400,12 +537,12 @@ func deletePublicKey(e *xorm.Session, keyID int64) error {
} }
// Don't need to rewrite this file if builtin SSH server is enabled. // Don't need to rewrite this file if builtin SSH server is enabled.
if setting.StartSSHServer { if setting.SSH.StartBuiltinServer {
return nil return nil
} }
fpath := filepath.Join(setting.SSHRootPath, "authorized_keys") fpath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
tmpPath := filepath.Join(setting.SSHRootPath, "authorized_keys.tmp") tmpPath := fpath + ".tmp"
if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil { if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil {
return err return err
} else if err = os.Remove(fpath); err != nil { } else if err = os.Remove(fpath); err != nil {
@ -447,7 +584,8 @@ func RewriteAllPublicKeys() error {
sshOpLocker.Lock() sshOpLocker.Lock()
defer sshOpLocker.Unlock() defer sshOpLocker.Unlock()
tmpPath := filepath.Join(setting.SSHRootPath, "authorized_keys.tmp") fpath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
tmpPath := fpath + ".tmp"
f, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) f, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil { if err != nil {
return err return err
@ -463,7 +601,6 @@ func RewriteAllPublicKeys() error {
return err return err
} }
fpath := filepath.Join(setting.SSHRootPath, "authorized_keys")
if com.IsExist(fpath) { if com.IsExist(fpath) {
if err = os.Remove(fpath); err != nil { if err = os.Remove(fpath); err != nil {
return err return err
@ -485,21 +622,35 @@ func RewriteAllPublicKeys() error {
// DeployKey represents deploy key information and its relation with repository. // DeployKey represents deploy key information and its relation with repository.
type DeployKey struct { type DeployKey struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
KeyID int64 `xorm:"UNIQUE(s) INDEX"` KeyID int64 `xorm:"UNIQUE(s) INDEX"`
RepoID int64 `xorm:"UNIQUE(s) INDEX"` RepoID int64 `xorm:"UNIQUE(s) INDEX"`
Name string Name string
Fingerprint string Fingerprint string
Content string `xorm:"-"` Content string `xorm:"-"`
Created time.Time `xorm:"CREATED"`
Updated time.Time // Note: Updated must below Created for AfterSet. Created time.Time `xorm:"-"`
HasRecentActivity bool `xorm:"-"` CreatedUnix int64
HasUsed bool `xorm:"-"` Updated time.Time `xorm:"-"` // Note: Updated must below Created for AfterSet.
UpdatedUnix int64
HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"`
}
func (k *DeployKey) BeforeInsert() {
k.CreatedUnix = time.Now().UTC().Unix()
}
func (k *DeployKey) BeforeUpdate() {
k.UpdatedUnix = time.Now().UTC().Unix()
} }
func (k *DeployKey) AfterSet(colName string, _ xorm.Cell) { func (k *DeployKey) AfterSet(colName string, _ xorm.Cell) {
switch colName { switch colName {
case "created": case "created_unix":
k.Created = time.Unix(k.CreatedUnix, 0).Local()
case "updated_unix":
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
k.HasUsed = k.Updated.After(k.Created) k.HasUsed = k.Updated.After(k.Created)
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now()) k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now())
} }

45
models/ssh_key_test.go

@ -0,0 +1,45 @@
package models
import (
"fmt"
"testing"
. "github.com/smartystreets/goconvey/convey"
"github.com/gogits/gogs/modules/setting"
)
func init() {
setting.NewContext()
}
func Test_SSHParsePublicKey(t *testing.T) {
testKeys := map[string]struct {
typeName string
length int
content string
}{
"dsa-1024": {"dsa", 1024, "ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ibZ2OkQ3S0SqDIa0HXSEJ1zaExQdmbO+Ux/wsytWZmCczWOVsaszBZSl90q8UnWlSH6P+/YA+RWJm5SFtuV9PtGIhyZgoNuz5kBQ7K139wuQsecdKktISwTakzAAAAFQCzKsO2JhNKlL+wwwLGOcLffoAmkwAAAIBpK7/3xvduajLBD/9vASqBQIHrgK2J+wiQnIb/Wzy0UsVmvfn8A+udRbBo+csM8xrSnlnlJnjkJS3qiM5g+eTwsLIV1IdKPEwmwB+VcP53Cw6lSyWyJcvhFb0N6s08NZysLzvj0N+ZC/FnhKTLzIyMtkHf/IrPCwlM+pV/M/96YgAAAIEAqQcGn9CKgzgPaguIZooTAOQdvBLMI5y0bQjOW6734XOpqQGf/Kra90wpoasLKZjSYKNPjE+FRUOrStLrxcNs4BeVKhy2PYTRnybfYVk1/dmKgH6P1YSRONsGKvTsH6c5IyCRG0ncCgYeF8tXppyd642982daopE7zQ/NPAnJfag= nocomment"},
"rsa-1024": {"rsa", 1024, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\n"},
"rsa-2048": {"rsa", 2048, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMZXh+1OBUwSH9D45wTaxErQIN9IoC9xl7MKJkqvTvv6O5RR9YW/IK9FbfjXgXsppYGhsCZo1hFOOsXHMnfOORqu/xMDx4yPuyvKpw4LePEcg4TDipaDFuxbWOqc/BUZRZcXu41QAWfDLrInwsltWZHSeG7hjhpacl4FrVv9V1pS6Oc5Q1NxxEzTzuNLS/8diZrTm/YAQQ/+B+mzWI3zEtF4miZjjAljWd1LTBPvU23d29DcBmmFahcZ441XZsTeAwGxG/Q6j8NgNXj9WxMeWwxXV2jeAX/EBSpZrCVlCQ1yJswT6xCp8TuBnTiGWYMBNTbOZvPC4e0WI2/yZW/s5F nocomment"},
"ecdsa-256": {"ecdsa", 256, "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFQacN3PrOll7PXmN5B/ZNVahiUIqI05nbBlZk1KXsO3d06ktAWqbNflv2vEmA38bTFTfJ2sbn2B5ksT52cDDbA= nocomment"},
"ecdsa-384": {"ecdsa", 384, "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBINmioV+XRX1Fm9Qk2ehHXJ2tfVxW30ypUWZw670Zyq5GQfBAH6xjygRsJ5wWsHXBsGYgFUXIHvMKVAG1tpw7s6ax9oA+dJOJ7tj+vhn8joFqT+sg3LYHgZkHrfqryRasQ== nocomment"},
"ecdsa-521": {"ecdsa", 521, "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACGt3UG3EzRwNOI17QR84l6PgiAcvCE7v6aXPj/SC6UWKg4EL8vW9ZBcdYL9wzs4FZXh4MOV8jAzu3KRWNTwb4k2wFNUpGOt7l28MztFFEtH5BDDrtAJSPENPy8pvPLMfnPg5NhvWycqIBzNcHipem5wSJFN5PdpNOC2xMrPWKNqj+ZjQ== nocomment"},
}
Convey("Parse public keys in both native and ssh-keygen", t, func() {
for name, key := range testKeys {
fmt.Println("\nTesting key:", name)
keyTypeN, lengthN, errN := SSHNativeParsePublicKey(key.content)
So(errN, ShouldBeNil)
So(keyTypeN, ShouldEqual, key.typeName)
So(lengthN, ShouldEqual, key.length)
keyTypeK, lengthK, errK := SSHKeyGenParsePublicKey(key.content)
So(errK, ShouldBeNil)
So(keyTypeK, ShouldEqual, key.typeName)
So(lengthK, ShouldEqual, key.length)
}
})
}

51
models/token.go

@ -7,25 +7,49 @@ package models
import ( import (
"time" "time"
"github.com/go-xorm/xorm"
gouuid "github.com/satori/go.uuid"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/uuid"
) )
// AccessToken represents a personal access token. // AccessToken represents a personal access token.
type AccessToken struct { type AccessToken struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
UID int64 `xorm:"INDEX"` UID int64 `xorm:"INDEX"`
Name string Name string
Sha1 string `xorm:"UNIQUE VARCHAR(40)"` Sha1 string `xorm:"UNIQUE VARCHAR(40)"`
Created time.Time `xorm:"CREATED"`
Updated time.Time Created time.Time `xorm:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-"` // Note: Updated must below Created for AfterSet.
UpdatedUnix int64
HasRecentActivity bool `xorm:"-"` HasRecentActivity bool `xorm:"-"`
HasUsed bool `xorm:"-"` HasUsed bool `xorm:"-"`
} }
func (t *AccessToken) BeforeInsert() {
t.CreatedUnix = time.Now().UTC().Unix()
}
func (t *AccessToken) BeforeUpdate() {
t.UpdatedUnix = time.Now().UTC().Unix()
}
func (t *AccessToken) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
t.Created = time.Unix(t.CreatedUnix, 0).Local()
case "updated_unix":
t.Updated = time.Unix(t.UpdatedUnix, 0).Local()
t.HasUsed = t.Updated.After(t.Created)
t.HasRecentActivity = t.Updated.Add(7 * 24 * time.Hour).After(time.Now())
}
}
// NewAccessToken creates new access token. // NewAccessToken creates new access token.
func NewAccessToken(t *AccessToken) error { func NewAccessToken(t *AccessToken) error {
t.Sha1 = base.EncodeSha1(uuid.NewV4().String()) t.Sha1 = base.EncodeSha1(gouuid.NewV4().String())
_, err := x.Insert(t) _, err := x.Insert(t)
return err return err
} }
@ -45,16 +69,7 @@ func GetAccessTokenBySHA(sha string) (*AccessToken, error) {
// ListAccessTokens returns a list of access tokens belongs to given user. // ListAccessTokens returns a list of access tokens belongs to given user.
func ListAccessTokens(uid int64) ([]*AccessToken, error) { func ListAccessTokens(uid int64) ([]*AccessToken, error) {
tokens := make([]*AccessToken, 0, 5) tokens := make([]*AccessToken, 0, 5)
err := x.Where("uid=?", uid).Desc("id").Find(&tokens) return tokens, x.Where("uid=?", uid).Desc("id").Find(&tokens)
if err != nil {
return nil, err
}
for _, t := range tokens {
t.HasUsed = t.Updated.After(t.Created)
t.HasRecentActivity = t.Updated.Add(7 * 24 * time.Hour).After(time.Now())
}
return tokens, nil
} }
// UpdateAccessToken updates information of access token. // UpdateAccessToken updates information of access token.

90
models/update.go

@ -10,7 +10,7 @@ import (
"os/exec" "os/exec"
"strings" "strings"
"github.com/gogits/git-module" git "github.com/gogits/git-module"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
) )
@ -65,94 +65,104 @@ func ListToPushCommits(l *list.List) *PushCommits {
return &PushCommits{l.Len(), commits, "", nil} return &PushCommits{l.Len(), commits, "", nil}
} }
func Update(refName, oldCommitID, newCommitID, userName, repoUserName, repoName string, userID int64) error { type PushUpdateOptions struct {
isNew := strings.HasPrefix(oldCommitID, "0000000") RefName string
if isNew && OldCommitID string
strings.HasPrefix(newCommitID, "0000000") { NewCommitID string
return fmt.Errorf("old rev and new rev both 000000") PusherID int64
PusherName string
RepoUserName string
RepoName string
}
// PushUpdate must be called for any push actions in order to
// generates necessary push action history feeds.
func PushUpdate(opts PushUpdateOptions) (err error) {
isNewRef := strings.HasPrefix(opts.OldCommitID, "0000000")
isDelRef := strings.HasPrefix(opts.NewCommitID, "0000000")
if isNewRef && isDelRef {
return fmt.Errorf("Old and new revisions both start with 000000")
} }
f := RepoPath(repoUserName, repoName) repoPath := RepoPath(opts.RepoUserName, opts.RepoName)
gitUpdate := exec.Command("git", "update-server-info") gitUpdate := exec.Command("git", "update-server-info")
gitUpdate.Dir = f gitUpdate.Dir = repoPath
gitUpdate.Run() if err = gitUpdate.Run(); err != nil {
return fmt.Errorf("Fail to call 'git update-server-info': %v", err)
}
isDel := strings.HasPrefix(newCommitID, "0000000") if isDelRef {
if isDel { log.GitLogger.Info("Reference '%s' has been deleted from '%s/%s' by %d",
log.GitLogger.Info("del rev", refName, "from", userName+"/"+repoName+".git", "by", userID) opts.RefName, opts.RepoUserName, opts.RepoName, opts.PusherName)
return nil return nil
} }
gitRepo, err := git.OpenRepository(f) gitRepo, err := git.OpenRepository(repoPath)
if err != nil { if err != nil {
return fmt.Errorf("runUpdate.Open repoId: %v", err) return fmt.Errorf("OpenRepository: %v", err)
} }
user, err := GetUserByName(repoUserName) repoUser, err := GetUserByName(opts.RepoUserName)
if err != nil { if err != nil {
return fmt.Errorf("runUpdate.GetUserByName: %v", err) return fmt.Errorf("GetUserByName: %v", err)
} }
repo, err := GetRepositoryByName(user.Id, repoName) repo, err := GetRepositoryByName(repoUser.Id, opts.RepoName)
if err != nil { if err != nil {
return fmt.Errorf("runUpdate.GetRepositoryByName userId: %v", err) return fmt.Errorf("GetRepositoryByName: %v", err)
} }
// Push tags. // Push tags.
if strings.HasPrefix(refName, "refs/tags/") { if strings.HasPrefix(opts.RefName, "refs/tags/") {
tagName := git.RefEndName(refName) tag, err := gitRepo.GetTag(git.RefEndName(opts.RefName))
tag, err := gitRepo.GetTag(tagName)
if err != nil { if err != nil {
log.GitLogger.Fatal(4, "runUpdate.GetTag: %v", err) return fmt.Errorf("gitRepo.GetTag: %v", err)
} }
// When tagger isn't available, fall back to get committer email.
var actEmail string var actEmail string
if tag.Tagger != nil { if tag.Tagger != nil {
actEmail = tag.Tagger.Email actEmail = tag.Tagger.Email
} else { } else {
cmt, err := tag.Commit() cmt, err := tag.Commit()
if err != nil { if err != nil {
log.GitLogger.Fatal(4, "runUpdate.GetTag Commit: %v", err) return fmt.Errorf("tag.Commit: %v", err)
} }
actEmail = cmt.Committer.Email actEmail = cmt.Committer.Email
} }
commit := &PushCommits{} commit := &PushCommits{}
if err = CommitRepoAction(opts.PusherID, repoUser.Id, opts.PusherName, actEmail,
if err = CommitRepoAction(userID, user.Id, userName, actEmail, repo.ID, opts.RepoUserName, opts.RepoName, opts.RefName, commit, opts.OldCommitID, opts.NewCommitID); err != nil {
repo.ID, repoUserName, repoName, refName, commit, oldCommitID, newCommitID); err != nil { return fmt.Errorf("CommitRepoAction (tag): %v", err)
log.GitLogger.Fatal(4, "CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
} }
return err return err
} }
newCommit, err := gitRepo.GetCommit(newCommitID) newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
if err != nil { if err != nil {
return fmt.Errorf("runUpdate GetCommit of newCommitId: %v", err) return fmt.Errorf("gitRepo.GetCommit: %v", err)
} }
// Push new branch. // Push new branch.
var l *list.List var l *list.List
if isNew { if isNewRef {
l, err = newCommit.CommitsBeforeLimit(10) l, err = newCommit.CommitsBeforeLimit(10)
if err != nil { if err != nil {
return fmt.Errorf("CommitsBefore: %v", err) return fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err)
} }
} else { } else {
l, err = newCommit.CommitsBeforeUntil(oldCommitID) l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
if err != nil { if err != nil {
return fmt.Errorf("CommitsBeforeUntil: %v", err) return fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err)
} }
} }
if err != nil { if err = CommitRepoAction(opts.PusherID, repoUser.Id, opts.PusherName, repoUser.Email,
return fmt.Errorf("runUpdate.Commit repoId: %v", err) repo.ID, opts.RepoUserName, opts.RepoName, opts.RefName, ListToPushCommits(l),
} opts.OldCommitID, opts.NewCommitID); err != nil {
return fmt.Errorf("CommitRepoAction (branch): %v", err)
if err = CommitRepoAction(userID, user.Id, userName, user.Email,
repo.ID, repoUserName, repoName, refName, ListToPushCommits(l), oldCommitID, newCommitID); err != nil {
return fmt.Errorf("runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
} }
return nil return nil
} }

155
models/user.go

@ -16,7 +16,6 @@ import (
_ "image/jpeg" _ "image/jpeg"
"image/png" "image/png"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
@ -30,14 +29,15 @@ import (
"github.com/gogits/gogs/modules/avatar" "github.com/gogits/gogs/modules/avatar"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
type UserType int type UserType int
const ( const (
INDIVIDUAL UserType = iota // Historic reason to make it starts at 0. USER_TYPE_INDIVIDUAL UserType = iota // Historic reason to make it starts at 0.
ORGANIZATION USER_TYPE_ORGANIZATION
) )
var ( var (
@ -68,10 +68,13 @@ type User struct {
Repos []*Repository `xorm:"-"` Repos []*Repository `xorm:"-"`
Location string Location string
Website string Website string
Rands string `xorm:"VARCHAR(10)"` Rands string `xorm:"VARCHAR(10)"`
Salt string `xorm:"VARCHAR(10)"` Salt string `xorm:"VARCHAR(10)"`
Created time.Time `xorm:"CREATED"`
Updated time.Time `xorm:"UPDATED"` Created time.Time `xorm:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-"`
UpdatedUnix int64
// Remember visibility choice for convenience, true for private // Remember visibility choice for convenience, true for private
LastRepoVisibility bool LastRepoVisibility bool
@ -103,18 +106,26 @@ type User struct {
Members []*User `xorm:"-"` Members []*User `xorm:"-"`
} }
func (u *User) BeforeInsert() {
u.CreatedUnix = time.Now().UTC().Unix()
u.UpdatedUnix = u.CreatedUnix
}
func (u *User) BeforeUpdate() { func (u *User) BeforeUpdate() {
if u.MaxRepoCreation < -1 { if u.MaxRepoCreation < -1 {
u.MaxRepoCreation = -1 u.MaxRepoCreation = -1
} }
u.UpdatedUnix = time.Now().UTC().Unix()
} }
func (u *User) AfterSet(colName string, _ xorm.Cell) { func (u *User) AfterSet(colName string, _ xorm.Cell) {
switch colName { switch colName {
case "full_name": case "full_name":
u.FullName = base.Sanitizer.Sanitize(u.FullName) u.FullName = markdown.Sanitizer.Sanitize(u.FullName)
case "created": case "created_unix":
u.Created = regulateTimeZone(u.Created) u.Created = time.Unix(u.CreatedUnix, 0).Local()
case "updated_unix":
u.Updated = time.Unix(u.UpdatedUnix, 0).Local()
} }
} }
@ -211,7 +222,7 @@ func (u *User) GenerateRandomAvatar() error {
if err != nil { if err != nil {
return fmt.Errorf("RandomImage: %v", err) return fmt.Errorf("RandomImage: %v", err)
} }
if err = os.MkdirAll(path.Dir(u.CustomAvatarPath()), os.ModePerm); err != nil { if err = os.MkdirAll(filepath.Dir(u.CustomAvatarPath()), os.ModePerm); err != nil {
return fmt.Errorf("MkdirAll: %v", err) return fmt.Errorf("MkdirAll: %v", err)
} }
fw, err := os.Create(u.CustomAvatarPath()) fw, err := os.Create(u.CustomAvatarPath())
@ -248,8 +259,6 @@ func (u *User) RelAvatarLink() string {
} }
return "/avatars/" + com.ToStr(u.Id) return "/avatars/" + com.ToStr(u.Id)
case setting.Service.EnableCacheAvatar:
return "/avatar/" + u.Avatar
} }
return setting.GravatarSource + u.Avatar return setting.GravatarSource + u.Avatar
} }
@ -348,28 +357,39 @@ func (u *User) UploadAvatar(data []byte) error {
return sess.Commit() return sess.Commit()
} }
// DeleteAvatar deletes the user's custom avatar.
func (u *User) DeleteAvatar() error {
log.Trace("DeleteAvatar[%d]: %s", u.Id, u.CustomAvatarPath())
os.Remove(u.CustomAvatarPath())
u.UseCustomAvatar = false
if err := UpdateUser(u); err != nil {
return fmt.Errorf("UpdateUser: %v", err)
}
return nil
}
// IsAdminOfRepo returns true if user has admin or higher access of repository. // IsAdminOfRepo returns true if user has admin or higher access of repository.
func (u *User) IsAdminOfRepo(repo *Repository) bool { func (u *User) IsAdminOfRepo(repo *Repository) bool {
if err := repo.GetOwner(); err != nil { has, err := HasAccess(u, repo, ACCESS_MODE_ADMIN)
log.Error(3, "GetOwner: %v", err) if err != nil {
return false log.Error(3, "HasAccess: %v", err)
} }
return has
}
if repo.Owner.IsOrganization() { // IsWriterOfRepo returns true if user has write access to given repository.
has, err := HasAccess(u, repo, ACCESS_MODE_ADMIN) func (u *User) IsWriterOfRepo(repo *Repository) bool {
if err != nil { has, err := HasAccess(u, repo, ACCESS_MODE_WRITE)
log.Error(3, "HasAccess: %v", err) if err != nil {
return false log.Error(3, "HasAccess: %v", err)
}
return has
} }
return has
return repo.IsOwnedBy(u.Id)
} }
// IsOrganization returns true if user is actually a organization. // IsOrganization returns true if user is actually a organization.
func (u *User) IsOrganization() bool { func (u *User) IsOrganization() bool {
return u.Type == ORGANIZATION return u.Type == USER_TYPE_ORGANIZATION
} }
// IsUserOrgOwner returns true if user is in the owner team of given organization. // IsUserOrgOwner returns true if user is in the owner team of given organization.
@ -494,7 +514,7 @@ func CreateUser(u *User) (err error) {
u.LowerName = strings.ToLower(u.Name) u.LowerName = strings.ToLower(u.Name)
u.AvatarEmail = u.Email u.AvatarEmail = u.Email
u.Avatar = avatar.HashEmail(u.AvatarEmail) u.Avatar = base.HashEmail(u.AvatarEmail)
u.Rands = GetUserSalt() u.Rands = GetUserSalt()
u.Salt = GetUserSalt() u.Salt = GetUserSalt()
u.EncodePasswd() u.EncodePasswd()
@ -599,11 +619,24 @@ func ChangeUserName(u *User, newUserName string) (err error) {
return ErrUserAlreadyExist{newUserName} return ErrUserAlreadyExist{newUserName}
} }
return os.Rename(UserPath(u.LowerName), UserPath(newUserName)) if err = ChangeUsernameInPullRequests(u.Name, newUserName); err != nil {
return fmt.Errorf("ChangeUsernameInPullRequests: %v", err)
}
// Delete all local copies of repository wiki that user owns.
if err = x.Where("owner_id=?", u.Id).Iterate(new(Repository), func(idx int, bean interface{}) error {
repo := bean.(*Repository)
RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath())
return nil
}); err != nil {
return fmt.Errorf("Delete repository wiki local copy: %v", err)
}
return os.Rename(UserPath(u.Name), UserPath(newUserName))
} }
func updateUser(e Engine, u *User) error { func updateUser(e Engine, u *User) error {
// Organization does not need e-mail. // Organization does not need email
if !u.IsOrganization() { if !u.IsOrganization() {
u.Email = strings.ToLower(u.Email) u.Email = strings.ToLower(u.Email)
has, err := e.Where("id!=?", u.Id).And("type=?", u.Type).And("email=?", u.Email).Get(new(User)) has, err := e.Where("id!=?", u.Id).And("type=?", u.Type).And("email=?", u.Email).Get(new(User))
@ -616,22 +649,15 @@ func updateUser(e Engine, u *User) error {
if len(u.AvatarEmail) == 0 { if len(u.AvatarEmail) == 0 {
u.AvatarEmail = u.Email u.AvatarEmail = u.Email
} }
u.Avatar = avatar.HashEmail(u.AvatarEmail) u.Avatar = base.HashEmail(u.AvatarEmail)
} }
u.LowerName = strings.ToLower(u.Name) u.LowerName = strings.ToLower(u.Name)
u.Location = base.TruncateString(u.Location, 255)
u.Website = base.TruncateString(u.Website, 255)
u.Description = base.TruncateString(u.Description, 255)
if len(u.Location) > 255 { u.FullName = markdown.Sanitizer.Sanitize(u.FullName)
u.Location = u.Location[:255]
}
if len(u.Website) > 255 {
u.Website = u.Website[:255]
}
if len(u.Description) > 255 {
u.Description = u.Description[:255]
}
u.FullName = base.Sanitizer.Sanitize(u.FullName)
_, err := e.Id(u.Id).AllCols().Update(u) _, err := e.Id(u.Id).AllCols().Update(u)
return err return err
} }
@ -1088,16 +1114,47 @@ func GetUserByEmail(email string) (*User, error) {
return nil, ErrUserNotExist{0, email} return nil, ErrUserNotExist{0, email}
} }
// SearchUserByName returns given number of users whose name contains keyword. type SearchUserOptions struct {
func SearchUserByName(opt SearchOption) (us []*User, err error) { Keyword string
if len(opt.Keyword) == 0 { Type UserType
return us, nil OrderBy string
Page int
PageSize int // Can be smaller than or equal to setting.ExplorePagingNum
}
// SearchUserByName takes keyword and part of user name to search,
// it returns results in given range and number of total results.
func SearchUserByName(opts *SearchUserOptions) (users []*User, _ int64, _ error) {
if len(opts.Keyword) == 0 {
return users, 0, nil
}
opts.Keyword = strings.ToLower(opts.Keyword)
if opts.PageSize <= 0 || opts.PageSize > setting.ExplorePagingNum {
opts.PageSize = setting.ExplorePagingNum
} }
opt.Keyword = strings.ToLower(opt.Keyword) if opts.Page <= 0 {
opts.Page = 1
}
searchQuery := "%" + opts.Keyword + "%"
users = make([]*User, 0, opts.PageSize)
// Append conditions
sess := x.Where("LOWER(lower_name) LIKE ?", searchQuery).
Or("LOWER(full_name) LIKE ?", searchQuery).
And("type = ?", opts.Type)
us = make([]*User, 0, opt.Limit) var countSess xorm.Session
err = x.Limit(opt.Limit).Where("type=0").And("lower_name like ?", "%"+opt.Keyword+"%").Find(&us) countSess = *sess
return us, err count, err := countSess.Count(new(User))
if err != nil {
return nil, 0, fmt.Errorf("Count: %v", err)
}
if len(opts.OrderBy) > 0 {
sess.OrderBy(opts.OrderBy)
}
return users, count, sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&users)
} }
// ___________ .__ .__ // ___________ .__ .__

35
models/webhook.go

@ -15,13 +15,13 @@ import (
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
gouuid "github.com/satori/go.uuid"
api "github.com/gogits/go-gogs-client" api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/modules/httplib" "github.com/gogits/gogs/modules/httplib"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/uuid"
) )
type HookContentType int type HookContentType int
@ -94,8 +94,20 @@ type Webhook struct {
HookTaskType HookTaskType HookTaskType HookTaskType
Meta string `xorm:"TEXT"` // store hook-specific attributes Meta string `xorm:"TEXT"` // store hook-specific attributes
LastStatus HookStatus // Last delivery status LastStatus HookStatus // Last delivery status
Created time.Time `xorm:"CREATED"`
Updated time.Time `xorm:"UPDATED"` Created time.Time `xorm:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-"`
UpdatedUnix int64
}
func (w *Webhook) BeforeInsert() {
w.CreatedUnix = time.Now().UTC().Unix()
w.UpdatedUnix = w.CreatedUnix
}
func (w *Webhook) BeforeUpdate() {
w.UpdatedUnix = time.Now().UTC().Unix()
} }
func (w *Webhook) AfterSet(colName string, _ xorm.Cell) { func (w *Webhook) AfterSet(colName string, _ xorm.Cell) {
@ -106,8 +118,10 @@ func (w *Webhook) AfterSet(colName string, _ xorm.Cell) {
if err = json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil { if err = json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
log.Error(3, "Unmarshal[%d]: %v", w.ID, err) log.Error(3, "Unmarshal[%d]: %v", w.ID, err)
} }
case "created": case "created_unix":
w.Created = regulateTimeZone(w.Created) w.Created = time.Unix(w.CreatedUnix, 0).Local()
case "updated_unix":
w.Updated = time.Unix(w.UpdatedUnix, 0).Local()
} }
} }
@ -285,7 +299,7 @@ type HookTask struct {
HookID int64 HookID int64
UUID string UUID string
Type HookTaskType Type HookTaskType
URL string URL string `xorm:"TEXT"`
api.Payloader `xorm:"-"` api.Payloader `xorm:"-"`
PayloadContent string `xorm:"TEXT"` PayloadContent string `xorm:"TEXT"`
ContentType HookContentType ContentType HookContentType
@ -361,7 +375,7 @@ func CreateHookTask(t *HookTask) error {
if err != nil { if err != nil {
return err return err
} }
t.UUID = uuid.NewV4().String() t.UUID = gouuid.NewV4().String()
t.PayloadContent = string(data) t.PayloadContent = string(data)
_, err = x.Insert(t) _, err = x.Insert(t)
return err return err
@ -398,6 +412,7 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err
return nil return nil
} }
var payloader api.Payloader
for _, w := range ws { for _, w := range ws {
switch event { switch event {
case HOOK_EVENT_CREATE: case HOOK_EVENT_CREATE:
@ -410,14 +425,16 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err
} }
} }
// Use separate objects so modifcations won't be made on payload on non-Gogs type hooks.
switch w.HookTaskType { switch w.HookTaskType {
case SLACK: case SLACK:
p, err = GetSlackPayload(p, event, w.Meta) payloader, err = GetSlackPayload(p, event, w.Meta)
if err != nil { if err != nil {
return fmt.Errorf("GetSlackPayload: %v", err) return fmt.Errorf("GetSlackPayload: %v", err)
} }
default: default:
p.SetSecret(w.Secret) p.SetSecret(w.Secret)
payloader = p
} }
if err = CreateHookTask(&HookTask{ if err = CreateHookTask(&HookTask{
@ -425,7 +442,7 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err
HookID: w.ID, HookID: w.ID,
Type: w.HookTaskType, Type: w.HookTaskType,
URL: w.URL, URL: w.URL,
Payloader: p, Payloader: payloader,
ContentType: w.ContentType, ContentType: w.ContentType,
EventType: HOOK_EVENT_PUSH, EventType: HOOK_EVENT_PUSH,
IsSSL: w.IsSSL, IsSSL: w.IsSSL,

62
models/wiki.go

@ -7,12 +7,12 @@ package models
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/url"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
"net/url"
"github.com/Unknwon/com" "github.com/Unknwon/com"
@ -116,6 +116,23 @@ func (repo *Repository) UpdateLocalWiki() error {
return updateLocalCopy(repo.WikiPath(), repo.LocalWikiPath()) return updateLocalCopy(repo.WikiPath(), repo.LocalWikiPath())
} }
// discardLocalWikiChanges discards local commits make sure
// it is even to remote branch when local copy exists.
func discardLocalWikiChanges(localPath string) error {
if !com.IsExist(localPath) {
return nil
}
// No need to check if nothing in the repository.
if !git.IsBranchExist(localPath, "master") {
return nil
}
if err := git.ResetHEAD(localPath, true, "origin/master"); err != nil {
return fmt.Errorf("ResetHEAD: %v", err)
}
return nil
}
// updateWikiPage adds new page to repository wiki. // updateWikiPage adds new page to repository wiki.
func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, message string, isNew bool) (err error) { func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, message string, isNew bool) (err error) {
wikiWorkingPool.CheckIn(com.ToStr(repo.ID)) wikiWorkingPool.CheckIn(com.ToStr(repo.ID))
@ -126,18 +143,9 @@ func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, mes
} }
localPath := repo.LocalWikiPath() localPath := repo.LocalWikiPath()
if err = discardLocalWikiChanges(localPath); err != nil {
// Discard local commits make sure even to remote when local copy exists. return fmt.Errorf("discardLocalWikiChanges: %v", err)
if com.IsExist(localPath) { } else if err = repo.UpdateLocalWiki(); err != nil {
// No need to check if nothing in the repository.
if git.IsBranchExist(localPath, "master") {
if err = git.ResetHEAD(localPath, true, "origin/master"); err != nil {
return fmt.Errorf("Reset: %v", err)
}
}
}
if err = repo.UpdateLocalWiki(); err != nil {
return fmt.Errorf("UpdateLocalWiki: %v", err) return fmt.Errorf("UpdateLocalWiki: %v", err)
} }
@ -178,3 +186,31 @@ func (repo *Repository) AddWikiPage(doer *User, title, content, message string)
func (repo *Repository) EditWikiPage(doer *User, oldTitle, title, content, message string) error { func (repo *Repository) EditWikiPage(doer *User, oldTitle, title, content, message string) error {
return repo.updateWikiPage(doer, oldTitle, title, content, message, false) return repo.updateWikiPage(doer, oldTitle, title, content, message, false)
} }
func (repo *Repository) DeleteWikiPage(doer *User, title string) (err error) {
wikiWorkingPool.CheckIn(com.ToStr(repo.ID))
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID))
localPath := repo.LocalWikiPath()
if err = discardLocalWikiChanges(localPath); err != nil {
return fmt.Errorf("discardLocalWikiChanges: %v", err)
} else if err = repo.UpdateLocalWiki(); err != nil {
return fmt.Errorf("UpdateLocalWiki: %v", err)
}
title = ToWikiPageName(strings.Replace(title, "/", " ", -1))
filename := path.Join(localPath, title+".md")
os.Remove(filename)
message := "Delete page '" + title + "'"
if err = git.AddChanges(localPath, true); err != nil {
return fmt.Errorf("AddChanges: %v", err)
} else if err = git.CommitChanges(localPath, message, doer.NewGitSig()); err != nil {
return fmt.Errorf("CommitChanges: %v", err)
} else if err = git.Push(localPath, "origin", "master"); err != nil {
return fmt.Errorf("Push: %v", err)
}
return nil
}

4
modules/auth/auth.go

@ -12,13 +12,13 @@ import (
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/go-macaron/binding" "github.com/go-macaron/binding"
"github.com/go-macaron/session" "github.com/go-macaron/session"
gouuid "github.com/satori/go.uuid"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/uuid"
) )
func IsAPIPath(url string) bool { func IsAPIPath(url string) bool {
@ -102,7 +102,7 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool)
if setting.Service.EnableReverseProxyAutoRegister { if setting.Service.EnableReverseProxyAutoRegister {
u := &models.User{ u := &models.User{
Name: webAuthUser, Name: webAuthUser,
Email: uuid.NewV4().String() + "@localhost", Email: gouuid.NewV4().String() + "@localhost",
Passwd: webAuthUser, Passwd: webAuthUser,
IsActive: true, IsActive: true,
} }

1
modules/auth/auth_form.go

@ -23,6 +23,7 @@ type AuthenticationForm struct {
AttributeName string AttributeName string
AttributeSurname string AttributeSurname string
AttributeMail string AttributeMail string
AttributesInBind bool
Filter string Filter string
AdminFilter string AdminFilter string
IsActive bool IsActive bool

68
modules/auth/ldap/ldap.go

@ -31,6 +31,7 @@ type Source struct {
AttributeName string // First name attribute AttributeName string // First name attribute
AttributeSurname string // Surname attribute AttributeSurname string // Surname attribute
AttributeMail string // E-mail attribute AttributeMail string // E-mail attribute
AttributesInBind bool // fetch attributes in bind context (not user)
Filter string // Query filter to validate entry Filter string // Query filter to validate entry
AdminFilter string // Query filter to check if user is admin AdminFilter string // Query filter to check if user is admin
Enabled bool // if this source is disabled Enabled bool // if this source is disabled
@ -58,18 +59,10 @@ func (ls *Source) sanitizedUserDN(username string) (string, bool) {
return fmt.Sprintf(ls.UserDN, username), true return fmt.Sprintf(ls.UserDN, username), true
} }
func (ls *Source) FindUserDN(name string) (string, bool) { func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) {
l, err := ldapDial(ls)
if err != nil {
log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err)
ls.Enabled = false
return "", false
}
defer l.Close()
log.Trace("Search for LDAP user: %s", name) log.Trace("Search for LDAP user: %s", name)
if ls.BindDN != "" && ls.BindPassword != "" { if ls.BindDN != "" && ls.BindPassword != "" {
err = l.Bind(ls.BindDN, ls.BindPassword) err := l.Bind(ls.BindDN, ls.BindPassword)
if err != nil { if err != nil {
log.Debug("Failed to bind as BindDN[%s]: %v", ls.BindDN, err) log.Debug("Failed to bind as BindDN[%s]: %v", ls.BindDN, err)
return "", false return "", false
@ -85,7 +78,7 @@ func (ls *Source) FindUserDN(name string) (string, bool) {
return "", false return "", false
} }
log.Trace("Searching using filter %s", userFilter) log.Trace("Searching for DN using filter %s and base %s", userFilter, ls.UserBase)
search := ldap.NewSearchRequest( search := ldap.NewSearchRequest(
ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0,
false, userFilter, []string{}, nil) false, userFilter, []string{}, nil)
@ -111,6 +104,14 @@ func (ls *Source) FindUserDN(name string) (string, bool) {
// searchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter // searchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter
func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, string, string, string, bool, bool) { func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, string, string, string, bool, bool) {
l, err := ldapDial(ls)
if err != nil {
log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err)
ls.Enabled = false
return "", "", "", "", false, false
}
defer l.Close()
var userDN string var userDN string
if directBind { if directBind {
log.Trace("LDAP will bind directly via UserDN template: %s", ls.UserDN) log.Trace("LDAP will bind directly via UserDN template: %s", ls.UserDN)
@ -124,36 +125,29 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
log.Trace("LDAP will use BindDN.") log.Trace("LDAP will use BindDN.")
var found bool var found bool
userDN, found = ls.FindUserDN(name) userDN, found = ls.findUserDN(l, name)
if !found { if !found {
return "", "", "", "", false, false return "", "", "", "", false, false
} }
} }
l, err := ldapDial(ls) if directBind || !ls.AttributesInBind {
if err != nil { // binds user (checking password) before looking-up attributes in user context
log.Error(4, "LDAP Connect error (%s): %v", ls.Host, err) err = bindUser(l, userDN, passwd)
ls.Enabled = false if err != nil {
return "", "", "", "", false, false return "", "", "", "", false, false
} }
defer l.Close()
log.Trace("Binding with userDN: %s", userDN)
err = l.Bind(userDN, passwd)
if err != nil {
log.Debug("LDAP auth. failed for %s, reason: %v", userDN, err)
return "", "", "", "", false, false
} }
log.Trace("Bound successfully with userDN: %s", userDN)
userFilter, ok := ls.sanitizedUserQuery(name) userFilter, ok := ls.sanitizedUserQuery(name)
if !ok { if !ok {
return "", "", "", "", false, false return "", "", "", "", false, false
} }
log.Trace("Fetching attributes '%v', '%v', '%v', '%v' with filter %s and base %s", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, userFilter, userDN)
search := ldap.NewSearchRequest( search := ldap.NewSearchRequest(
userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter, userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
[]string{ls.AttributeName, ls.AttributeSurname, ls.AttributeMail}, []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail},
nil) nil)
sr, err := l.Search(search) sr, err := l.Search(search)
@ -177,6 +171,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
admin_attr := false admin_attr := false
if len(ls.AdminFilter) > 0 { if len(ls.AdminFilter) > 0 {
log.Trace("Checking admin with filter %s and base %s", ls.AdminFilter, userDN)
search = ldap.NewSearchRequest( search = ldap.NewSearchRequest(
userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ls.AdminFilter, userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ls.AdminFilter,
[]string{ls.AttributeName}, []string{ls.AttributeName},
@ -192,9 +187,28 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
} }
} }
if !directBind && ls.AttributesInBind {
// binds user (checking password) after looking-up attributes in BindDN context
err = bindUser(l, userDN, passwd)
if err != nil {
return "", "", "", "", false, false
}
}
return username_attr, name_attr, sn_attr, mail_attr, admin_attr, true return username_attr, name_attr, sn_attr, mail_attr, admin_attr, true
} }
func bindUser(l *ldap.Conn, userDN, passwd string) error {
log.Trace("Binding with userDN: %s", userDN)
err := l.Bind(userDN, passwd)
if err != nil {
log.Debug("LDAP auth. failed for %s, reason: %v", userDN, err)
return err
}
log.Trace("Bound successfully with userDN: %s", userDN)
return err
}
func ldapDial(ls *Source) (*ldap.Conn, error) { func ldapDial(ls *Source) (*ldap.Conn, error) {
if ls.UseSSL { if ls.UseSSL {
log.Debug("Using TLS for LDAP without verifying: %v", ls.SkipVerify) log.Debug("Using TLS for LDAP without verifying: %v", ls.SkipVerify)

6
modules/auth/org.go

@ -45,9 +45,9 @@ func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs binding.Error
// \/ \/ \/ // \/ \/ \/
type CreateTeamForm struct { type CreateTeamForm struct {
TeamName string `form:"team_name" binding:"Required;AlphaDashDot;MaxSize(30)"` TeamName string `binding:"Required;AlphaDashDot;MaxSize(30)"`
Description string `form:"desc" binding:"MaxSize(255)"` Description string `binding:"MaxSize(255)"`
Permission string `form:"permission"` Permission string
} }
func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {

4
modules/auth/repo_form.go

@ -57,7 +57,7 @@ func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) bi
// It also checks if given user has permission when remote address // It also checks if given user has permission when remote address
// is actually a local path. // is actually a local path.
func (f MigrateRepoForm) ParseRemoteAddr(user *models.User) (string, error) { func (f MigrateRepoForm) ParseRemoteAddr(user *models.User) (string, error) {
remoteAddr := f.CloneAddr remoteAddr := strings.TrimSpace(f.CloneAddr)
// Remote address can be HTTP/HTTPS/Git URL or local path. // Remote address can be HTTP/HTTPS/Git URL or local path.
if strings.HasPrefix(remoteAddr, "http://") || if strings.HasPrefix(remoteAddr, "http://") ||
@ -239,7 +239,7 @@ func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) bin
type EditReleaseForm struct { type EditReleaseForm struct {
Title string `form:"title" binding:"Required"` Title string `form:"title" binding:"Required"`
Content string `form:"content" binding:"Required"` Content string `form:"content"`
Draft string `form:"draft"` Draft string `form:"draft"`
Prerelease bool `form:"prerelease"` Prerelease bool `form:"prerelease"`
} }

1
modules/auth/user_form.go

@ -27,6 +27,7 @@ type InstallForm struct {
SSHPort int SSHPort int
HTTPPort string `binding:"Required"` HTTPPort string `binding:"Required"`
AppUrl string `binding:"Required"` AppUrl string `binding:"Required"`
LogRootPath string `binding:"Required"`
SMTPHost string SMTPHost string
SMTPFrom string SMTPFrom string

316
modules/avatar/avatar.go

@ -2,74 +2,23 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// for www.gravatar.com image cache
/*
It is recommend to use this way
cacheDir := "./cache"
defaultImg := "./default.jpg"
http.Handle("/avatar/", avatar.CacheServer(cacheDir, defaultImg))
*/
package avatar package avatar
import ( import (
"crypto/md5"
"encoding/hex"
"errors"
"fmt" "fmt"
"image" "image"
"image/color/palette" "image/color/palette"
"image/jpeg"
"image/png"
"io"
"math/rand" "math/rand"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"sync"
"time" "time"
"github.com/issue9/identicon" "github.com/issue9/identicon"
"github.com/nfnt/resize"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
) )
//FIXME: remove cache module
var gravatarSource string
func UpdateGravatarSource() {
gravatarSource = setting.GravatarSource
if strings.HasPrefix(gravatarSource, "//") {
gravatarSource = "http:" + gravatarSource
} else if !strings.HasPrefix(gravatarSource, "http://") &&
!strings.HasPrefix(gravatarSource, "https://") {
gravatarSource = "http://" + gravatarSource
}
log.Debug("avatar.UpdateGravatarSource(update gavatar source): %s", gravatarSource)
}
// hash email to md5 string
// keep this func in order to make this package independent
func HashEmail(email string) string {
// https://en.gravatar.com/site/implement/hash/
email = strings.TrimSpace(email)
email = strings.ToLower(email)
h := md5.New()
h.Write([]byte(email))
return hex.EncodeToString(h.Sum(nil))
}
const _RANDOM_AVATAR_SIZE = 200 const _RANDOM_AVATAR_SIZE = 200
// RandomImage generates and returns a random avatar image. // RandomImage generates and returns a random avatar image unique to input data
func RandomImage(data []byte) (image.Image, error) { // in custom size (height and width).
func RandomImageSize(size int, data []byte) (image.Image, error) {
randExtent := len(palette.WebSafe) - 32 randExtent := len(palette.WebSafe) - 32
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
colorIndex := rand.Intn(randExtent) colorIndex := rand.Intn(randExtent)
@ -78,262 +27,17 @@ func RandomImage(data []byte) (image.Image, error) {
backColorIndex = randExtent - 1 backColorIndex = randExtent - 1
} }
// Size, background, forecolor // Define size, background, and forecolor
imgMaker, err := identicon.New(_RANDOM_AVATAR_SIZE, imgMaker, err := identicon.New(size,
palette.WebSafe[backColorIndex], palette.WebSafe[colorIndex:colorIndex+32]...) palette.WebSafe[backColorIndex], palette.WebSafe[colorIndex:colorIndex+32]...)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("identicon.New: %v", err)
} }
return imgMaker.Make(data), nil return imgMaker.Make(data), nil
} }
// Avatar represents the avatar object. // RandomImage generates and returns a random avatar image unique to input data
type Avatar struct { // in default size (height and width).
Hash string func RandomImage(data []byte) (image.Image, error) {
AlterImage string // image path return RandomImageSize(_RANDOM_AVATAR_SIZE, data)
cacheDir string // image save dir
reqParams string
imagePath string
expireDuration time.Duration
}
func New(hash string, cacheDir string) *Avatar {
return &Avatar{
Hash: hash,
cacheDir: cacheDir,
expireDuration: time.Minute * 10,
reqParams: url.Values{
"d": {"retro"},
"size": {"290"},
"r": {"pg"}}.Encode(),
imagePath: filepath.Join(cacheDir, hash+".image"), //maybe png or jpeg
}
}
func (this *Avatar) HasCache() bool {
fileInfo, err := os.Stat(this.imagePath)
return err == nil && fileInfo.Mode().IsRegular()
}
func (this *Avatar) Modtime() (modtime time.Time, err error) {
fileInfo, err := os.Stat(this.imagePath)
if err != nil {
return
}
return fileInfo.ModTime(), nil
}
func (this *Avatar) Expired() bool {
modtime, err := this.Modtime()
return err != nil || time.Since(modtime) > this.expireDuration
}
// default image format: jpeg
func (this *Avatar) Encode(wr io.Writer, size int) (err error) {
var img image.Image
decodeImageFile := func(file string) (img image.Image, err error) {
fd, err := os.Open(file)
if err != nil {
return
}
defer fd.Close()
if img, err = jpeg.Decode(fd); err != nil {
fd.Seek(0, os.SEEK_SET)
img, err = png.Decode(fd)
}
return
}
imgPath := this.imagePath
if !this.HasCache() {
if this.AlterImage == "" {
return errors.New("request image failed, and no alt image offered")
}
imgPath = this.AlterImage
}
if img, err = decodeImageFile(imgPath); err != nil {
return
}
m := resize.Resize(uint(size), 0, img, resize.Lanczos3)
return jpeg.Encode(wr, m, nil)
}
// get image from gravatar.com
func (this *Avatar) Update() {
UpdateGravatarSource()
thunder.Fetch(gravatarSource+this.Hash+"?"+this.reqParams,
this.imagePath)
}
func (this *Avatar) UpdateTimeout(timeout time.Duration) (err error) {
UpdateGravatarSource()
select {
case <-time.After(timeout):
err = fmt.Errorf("get gravatar image %s timeout", this.Hash)
case err = <-thunder.GoFetch(gravatarSource+this.Hash+"?"+this.reqParams,
this.imagePath):
}
return err
}
type service struct {
cacheDir string
altImage string
}
func (this *service) mustInt(r *http.Request, defaultValue int, keys ...string) (v int) {
for _, k := range keys {
if _, err := fmt.Sscanf(r.FormValue(k), "%d", &v); err == nil {
defaultValue = v
}
}
return defaultValue
}
func (this *service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
urlPath := r.URL.Path
hash := urlPath[strings.LastIndex(urlPath, "/")+1:]
size := this.mustInt(r, 290, "s", "size") // default size = 290*290
avatar := New(hash, this.cacheDir)
avatar.AlterImage = this.altImage
if avatar.Expired() {
if err := avatar.UpdateTimeout(time.Millisecond * 1000); err != nil {
log.Trace("avatar update error: %v", err)
return
}
}
if modtime, err := avatar.Modtime(); err == nil {
etag := fmt.Sprintf("size(%d)", size)
if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) && etag == r.Header.Get("If-None-Match") {
h := w.Header()
delete(h, "Content-Type")
delete(h, "Content-Length")
w.WriteHeader(http.StatusNotModified)
return
}
w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat))
w.Header().Set("ETag", etag)
}
w.Header().Set("Content-Type", "image/jpeg")
if err := avatar.Encode(w, size); err != nil {
log.Warn("avatar encode error: %v", err)
w.WriteHeader(500)
}
}
// http.Handle("/avatar/", avatar.CacheServer("./cache"))
func CacheServer(cacheDir string, defaultImgPath string) http.Handler {
return &service{
cacheDir: cacheDir,
altImage: defaultImgPath,
}
}
// thunder downloader
var thunder = &Thunder{QueueSize: 10}
type Thunder struct {
QueueSize int // download queue size
q chan *thunderTask
once sync.Once
}
func (t *Thunder) init() {
if t.QueueSize < 1 {
t.QueueSize = 1
}
t.q = make(chan *thunderTask, t.QueueSize)
for i := 0; i < t.QueueSize; i++ {
go func() {
for {
task := <-t.q
task.Fetch()
}
}()
}
}
func (t *Thunder) Fetch(url string, saveFile string) error {
t.once.Do(t.init)
task := &thunderTask{
Url: url,
SaveFile: saveFile,
}
task.Add(1)
t.q <- task
task.Wait()
return task.err
}
func (t *Thunder) GoFetch(url, saveFile string) chan error {
c := make(chan error)
go func() {
c <- t.Fetch(url, saveFile)
}()
return c
}
// thunder download
type thunderTask struct {
Url string
SaveFile string
sync.WaitGroup
err error
}
func (this *thunderTask) Fetch() {
this.err = this.fetch()
this.Done()
}
var client = &http.Client{}
func (this *thunderTask) fetch() error {
log.Debug("avatar.fetch(fetch new avatar): %s", this.Url)
req, _ := http.NewRequest("GET", this.Url, nil)
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/jpeg,image/png,*/*;q=0.8")
req.Header.Set("Accept-Encoding", "deflate,sdch")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8")
req.Header.Set("Cache-Control", "no-cache")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36")
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("status code: %d", resp.StatusCode)
}
/*
log.Println("headers:", resp.Header)
switch resp.Header.Get("Content-Type") {
case "image/jpeg":
this.SaveFile += ".jpeg"
case "image/png":
this.SaveFile += ".png"
}
*/
/*
imgType := resp.Header.Get("Content-Type")
if imgType != "image/jpeg" && imgType != "image/png" {
return errors.New("not png or jpeg")
}
*/
tmpFile := this.SaveFile + ".part" // mv to destination when finished
fd, err := os.Create(tmpFile)
if err != nil {
return err
}
_, err = io.Copy(fd, resp.Body)
fd.Close()
if err != nil {
os.Remove(tmpFile)
return err
}
return os.Rename(tmpFile, this.SaveFile)
} }

64
modules/avatar/avatar_test.go

@ -1,61 +1,23 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package avatar_test
package avatar
import ( import (
"errors"
"os"
"strconv"
"testing" "testing"
"time"
"github.com/gogits/gogs/modules/avatar" . "github.com/smartystreets/goconvey/convey"
"github.com/gogits/gogs/modules/log"
) )
const TMPDIR = "test-avatar" func Test_RandomImage(t *testing.T) {
Convey("Generate a random avatar from email", t, func() {
func TestFetch(t *testing.T) { _, err := RandomImage([]byte("gogs@local"))
os.Mkdir(TMPDIR, 0755) So(err, ShouldBeNil)
defer os.RemoveAll(TMPDIR)
hash := avatar.HashEmail("ssx205@gmail.com")
a := avatar.New(hash, TMPDIR)
a.UpdateTimeout(time.Millisecond * 200)
}
func TestFetchMany(t *testing.T) {
os.Mkdir(TMPDIR, 0755)
defer os.RemoveAll(TMPDIR)
t.Log("start")
var n = 5
ch := make(chan bool, n)
for i := 0; i < n; i++ {
go func(i int) {
hash := avatar.HashEmail(strconv.Itoa(i) + "ssx205@gmail.com")
a := avatar.New(hash, TMPDIR)
a.Update()
t.Log("finish", hash)
ch <- true
}(i)
}
for i := 0; i < n; i++ {
<-ch
}
t.Log("end")
}
// cat
// wget http://www.artsjournal.com/artfulmanager/wp/wp-content/uploads/2013/12/200x200xmirror_cat.jpg.pagespeed.ic.GOZSv6v1_H.jpg -O default.jpg
/*
func TestHttp(t *testing.T) {
http.Handle("/", avatar.CacheServer("./", "default.jpg"))
http.ListenAndServe(":8001", nil)
}
*/
func TestLogTrace(t *testing.T) { Convey("Try to generate an image with size zero", func() {
log.Trace("%v", errors.New("console log test")) _, err := RandomImageSize(0, []byte("gogs@local"))
So(err, ShouldNotBeNil)
})
})
} }

21
modules/base/base.go

@ -4,29 +4,8 @@
package base package base
import (
"os"
"os/exec"
"path/filepath"
)
const DOC_URL = "https://github.com/gogits/go-gogs-client/wiki" const DOC_URL = "https://github.com/gogits/go-gogs-client/wiki"
type ( type (
TplName string TplName string
) )
var GoGetMetas = make(map[string]bool)
// ExecPath returns the executable path.
func ExecPath() (string, error) {
file, err := exec.LookPath(os.Args[0])
if err != nil {
return "", err
}
p, err := filepath.Abs(file)
if err != nil {
return "", err
}
return p, nil
}

65
modules/base/tool.go

@ -15,33 +15,21 @@ import (
"hash" "hash"
"html/template" "html/template"
"math" "math"
"regexp" "net/http"
"strings" "strings"
"time" "time"
"unicode"
"unicode/utf8" "unicode/utf8"
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/Unknwon/i18n" "github.com/Unknwon/i18n"
"github.com/microcosm-cc/bluemonday"
"github.com/gogits/chardet" "github.com/gogits/chardet"
"github.com/gogits/gogs/modules/avatar"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
func BuildSanitizer() (p *bluemonday.Policy) {
p = bluemonday.UGCPolicy()
p.AllowAttrs("class").Matching(regexp.MustCompile(`[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*`)).OnElements("code")
p.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
p.AllowAttrs("checked", "disabled").OnElements("input")
return p
}
var Sanitizer = BuildSanitizer()
// EncodeMD5 encodes string to md5 hex value. // EncodeMD5 encodes string to md5 hex value.
func EncodeMD5(str string) string { func EncodeMD5(str string) string {
m := md5.New() m := md5.New()
@ -109,6 +97,7 @@ func GetRandomString(n int, alphabets ...byte) string {
} }
// http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto // http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto
// FIXME: use https://godoc.org/golang.org/x/crypto/pbkdf2?
func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
prf := hmac.New(h, password) prf := hmac.New(h, password)
hashLen := prf.Size() hashLen := prf.Size()
@ -206,17 +195,22 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string
return code return code
} }
// AvatarLink returns avatar link by given e-mail. // HashEmail hashes email address to MD5 string.
// https://en.gravatar.com/site/implement/hash/
func HashEmail(email string) string {
email = strings.ToLower(strings.TrimSpace(email))
h := md5.New()
h.Write([]byte(email))
return hex.EncodeToString(h.Sum(nil))
}
// AvatarLink returns avatar link by given email.
func AvatarLink(email string) string { func AvatarLink(email string) string {
if setting.DisableGravatar || setting.OfflineMode { if setting.DisableGravatar || setting.OfflineMode {
return setting.AppSubUrl + "/img/avatar_default.jpg" return setting.AppSubUrl + "/img/avatar_default.jpg"
} }
gravatarHash := avatar.HashEmail(email) return setting.GravatarSource + HashEmail(email)
if setting.Service.EnableCacheAvatar {
return setting.AppSubUrl + "/avatar/" + gravatarHash
}
return setting.GravatarSource + gravatarHash
} }
// Seconds-based time units // Seconds-based time units
@ -471,6 +465,15 @@ func EllipsisString(str string, length int) string {
return str[:length-3] + "..." return str[:length-3] + "..."
} }
// TruncateString returns a truncated string with given limit,
// it returns input string if length is not reached limit.
func TruncateString(str string, limit int) string {
if len(str) < limit {
return str
}
return str[:limit]
}
// StringsToInt64s converts a slice of string to a slice of int64. // StringsToInt64s converts a slice of string to a slice of int64.
func StringsToInt64s(strs []string) []int64 { func StringsToInt64s(strs []string) []int64 {
ints := make([]int64, len(strs)) ints := make([]int64, len(strs))
@ -497,3 +500,25 @@ func Int64sToMap(ints []int64) map[int64]bool {
} }
return m return m
} }
// IsLetter reports whether the rune is a letter (category L).
// https://github.com/golang/go/blob/master/src/go/scanner/scanner.go#L257
func IsLetter(ch rune) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
}
func IsTextFile(data []byte) (string, bool) {
contentType := http.DetectContentType(data)
if strings.Index(contentType, "text/") != -1 {
return contentType, true
}
return contentType, false
}
func IsImageFile(data []byte) (string, bool) {
contentType := http.DetectContentType(data)
if strings.Index(contentType, "image/") != -1 {
return contentType, true
}
return contentType, false
}

84
modules/bindata/bindata.go

File diff suppressed because one or more lines are too long

71
modules/context/api.go

@ -0,0 +1,71 @@
// Copyright 2016 The Gogs 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 context
import (
"fmt"
"strings"
"github.com/Unknwon/paginater"
"gopkg.in/macaron.v1"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
type APIContext struct {
*Context
}
// Error responses error message to client with given message.
// If status is 500, also it prints error to log.
func (ctx *APIContext) Error(status int, title string, obj interface{}) {
var message string
if err, ok := obj.(error); ok {
message = err.Error()
} else {
message = obj.(string)
}
if status == 500 {
log.Error(4, "%s: %s", title, message)
}
ctx.JSON(status, map[string]string{
"message": message,
"url": base.DOC_URL,
})
}
func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
page := paginater.New(total, pageSize, ctx.QueryInt("page"), 0)
links := make([]string, 0, 4)
if page.HasNext() {
links = append(links, fmt.Sprintf("<%s%s?page=%d>; rel=\"next\"", setting.AppUrl, ctx.Req.URL.Path[1:], page.Next()))
}
if !page.IsLast() {
links = append(links, fmt.Sprintf("<%s%s?page=%d>; rel=\"last\"", setting.AppUrl, ctx.Req.URL.Path[1:], page.TotalPages()))
}
if !page.IsFirst() {
links = append(links, fmt.Sprintf("<%s%s?page=1>; rel=\"first\"", setting.AppUrl, ctx.Req.URL.Path[1:]))
}
if page.HasPrevious() {
links = append(links, fmt.Sprintf("<%s%s?page=%d>; rel=\"prev\"", setting.AppUrl, ctx.Req.URL.Path[1:], page.Previous()))
}
if len(links) > 0 {
ctx.Header().Set("Link", strings.Join(links, ","))
}
}
func APIContexter() macaron.Handler {
return func(c *Context) {
ctx := &APIContext{
Context: c,
}
c.Map(ctx)
}
}

87
modules/context/auth.go

@ -0,0 +1,87 @@
// Copyright 2014 The Gogs 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 context
import (
"net/url"
"github.com/go-macaron/csrf"
"gopkg.in/macaron.v1"
"github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/setting"
)
type ToggleOptions struct {
SignInRequired bool
SignOutRequired bool
AdminRequired bool
DisableCSRF bool
}
func Toggle(options *ToggleOptions) macaron.Handler {
return func(ctx *Context) {
// Cannot view any page before installation.
if !setting.InstallLock {
ctx.Redirect(setting.AppSubUrl + "/install")
return
}
// Checking non-logged users landing page.
if !ctx.IsSigned && ctx.Req.RequestURI == "/" && setting.LandingPageUrl != setting.LANDING_PAGE_HOME {
ctx.Redirect(setting.AppSubUrl + string(setting.LandingPageUrl))
return
}
// Redirect to dashboard if user tries to visit any non-login page.
if options.SignOutRequired && ctx.IsSigned && ctx.Req.RequestURI != "/" {
ctx.Redirect(setting.AppSubUrl + "/")
return
}
if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" && !auth.IsAPIPath(ctx.Req.URL.Path) {
csrf.Validate(ctx.Context, ctx.csrf)
if ctx.Written() {
return
}
}
if options.SignInRequired {
if !ctx.IsSigned {
// Restrict API calls with error message.
if auth.IsAPIPath(ctx.Req.URL.Path) {
ctx.JSON(403, map[string]string{
"message": "Only signed in user is allowed to call APIs.",
})
return
}
ctx.SetCookie("redirect_to", url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
ctx.Redirect(setting.AppSubUrl + "/user/login")
return
} else if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm {
ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
ctx.HTML(200, "user/auth/activate")
return
}
}
// Redirect to log in page if auto-signin info is provided and has not signed in.
if !options.SignOutRequired && !ctx.IsSigned && !auth.IsAPIPath(ctx.Req.URL.Path) &&
len(ctx.GetCookie(setting.CookieUserName)) > 0 {
ctx.SetCookie("redirect_to", url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
ctx.Redirect(setting.AppSubUrl + "/user/login")
return
}
if options.AdminRequired {
if !ctx.User.IsAdmin {
ctx.Error(403)
return
}
ctx.Data["PageIsAdmin"] = true
}
}
}

82
modules/middleware/context.go → modules/context/context.go

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package middleware package context
import ( import (
"fmt" "fmt"
@ -18,8 +18,6 @@ import (
"github.com/go-macaron/session" "github.com/go-macaron/session"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
"github.com/gogits/git-module"
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
@ -27,27 +25,6 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
type RepoContext struct {
AccessMode models.AccessMode
IsWatching bool
IsViewBranch bool
IsViewTag bool
IsViewCommit bool
Repository *models.Repository
Owner *models.User
Commit *git.Commit
Tag *git.Tag
GitRepo *git.Repository
BranchName string
TagName string
TreeName string
CommitID string
RepoLink string
CloneLink models.CloneLink
CommitsCount int64
Mirror *models.Mirror
}
// Context represents context of a request. // Context represents context of a request.
type Context struct { type Context struct {
*macaron.Context *macaron.Context
@ -60,37 +37,8 @@ type Context struct {
IsSigned bool IsSigned bool
IsBasicAuth bool IsBasicAuth bool
Repo *RepoContext Repo *Repository
Org *Organization
Org struct {
IsOwner bool
IsMember bool
IsAdminTeam bool // In owner team or team that has admin permission level.
Organization *models.User
OrgLink string
Team *models.Team
}
}
// IsOwner returns true if current user is the owner of repository.
func (r *RepoContext) IsOwner() bool {
return r.AccessMode >= models.ACCESS_MODE_OWNER
}
// IsAdmin returns true if current user has admin or higher access of repository.
func (r *RepoContext) IsAdmin() bool {
return r.AccessMode >= models.ACCESS_MODE_ADMIN
}
// IsPusher returns true if current user has write or higher access of repository.
func (r *RepoContext) IsPusher() bool {
return r.AccessMode >= models.ACCESS_MODE_WRITE
}
// Return if the current user has read access for this repository
func (r *RepoContext) HasAccess() bool {
return r.AccessMode >= models.ACCESS_MODE_READ
} }
// HasError returns true if error occurs in form validation. // HasError returns true if error occurs in form validation.
@ -164,25 +112,6 @@ func (ctx *Context) HandleText(status int, title string) {
ctx.PlainText(status, []byte(title)) ctx.PlainText(status, []byte(title))
} }
// APIError logs error with title if status is 500.
func (ctx *Context) APIError(status int, title string, obj interface{}) {
var message string
if err, ok := obj.(error); ok {
message = err.Error()
} else {
message = obj.(string)
}
if status == 500 {
log.Error(4, "%s: %s", title, message)
}
ctx.JSON(status, map[string]string{
"message": message,
"url": base.DOC_URL,
})
}
func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) { func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
modtime := time.Now() modtime := time.Now()
for _, p := range params { for _, p := range params {
@ -210,7 +139,10 @@ func Contexter() macaron.Handler {
csrf: x, csrf: x,
Flash: f, Flash: f,
Session: sess, Session: sess,
Repo: &RepoContext{}, Repo: &Repository{
PullRequest: &PullRequest{},
},
Org: &Organization{},
} }
// Compute current URL for real-time change language. // Compute current URL for real-time change language.
ctx.Data["Link"] = setting.AppSubUrl + strings.TrimSuffix(ctx.Req.URL.Path, "/") ctx.Data["Link"] = setting.AppSubUrl + strings.TrimSuffix(ctx.Req.URL.Path, "/")

85
modules/middleware/org.go → modules/context/org.go

@ -2,20 +2,34 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package middleware package context
import ( import (
"strings"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
type Organization struct {
IsOwner bool
IsMember bool
IsTeamMember bool // Is member of team.
IsTeamAdmin bool // In owner team or team that has admin permission level.
Organization *models.User
OrgLink string
Team *models.Team
}
func HandleOrgAssignment(ctx *Context, args ...bool) { func HandleOrgAssignment(ctx *Context, args ...bool) {
var ( var (
requireMember bool requireMember bool
requireOwner bool requireOwner bool
requireAdminTeam bool requireTeamMember bool
requireTeamAdmin bool
) )
if len(args) >= 1 { if len(args) >= 1 {
requireMember = args[0] requireMember = args[0]
@ -24,7 +38,10 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
requireOwner = args[1] requireOwner = args[1]
} }
if len(args) >= 3 { if len(args) >= 3 {
requireAdminTeam = args[2] requireTeamMember = args[2]
}
if len(args) >= 4 {
requireTeamAdmin = args[3]
} }
orgName := ctx.Params(":org") orgName := ctx.Params(":org")
@ -52,12 +69,14 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
if ctx.IsSigned && ctx.User.IsAdmin { if ctx.IsSigned && ctx.User.IsAdmin {
ctx.Org.IsOwner = true ctx.Org.IsOwner = true
ctx.Org.IsMember = true ctx.Org.IsMember = true
ctx.Org.IsAdminTeam = true ctx.Org.IsTeamMember = true
ctx.Org.IsTeamAdmin = true
} else if ctx.IsSigned { } else if ctx.IsSigned {
ctx.Org.IsOwner = org.IsOwnedBy(ctx.User.Id) ctx.Org.IsOwner = org.IsOwnedBy(ctx.User.Id)
if ctx.Org.IsOwner { if ctx.Org.IsOwner {
ctx.Org.IsMember = true ctx.Org.IsMember = true
ctx.Org.IsAdminTeam = true ctx.Org.IsTeamMember = true
ctx.Org.IsTeamAdmin = true
} else { } else {
if org.IsOrgMember(ctx.User.Id) { if org.IsOrgMember(ctx.User.Id) {
ctx.Org.IsMember = true ctx.Org.IsMember = true
@ -79,24 +98,50 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
ctx.Data["OrgLink"] = ctx.Org.OrgLink ctx.Data["OrgLink"] = ctx.Org.OrgLink
// Team. // Team.
if ctx.Org.IsMember {
if ctx.Org.IsOwner {
if err := org.GetTeams(); err != nil {
ctx.Handle(500, "GetTeams", err)
return
}
} else {
if err := org.GetUserTeams(ctx.User.Id); err != nil {
ctx.Handle(500, "GetUserTeams", err)
return
}
}
}
teamName := ctx.Params(":team") teamName := ctx.Params(":team")
if len(teamName) > 0 { if len(teamName) > 0 {
ctx.Org.Team, err = org.GetTeam(teamName) teamExists := false
if err != nil { for _, team := range org.Teams {
if err == models.ErrTeamNotExist { if team.LowerName == strings.ToLower(teamName) {
ctx.Handle(404, "GetTeam", err) teamExists = true
} else { ctx.Org.Team = team
ctx.Handle(500, "GetTeam", err) ctx.Org.IsTeamMember = true
ctx.Data["Team"] = ctx.Org.Team
break
} }
}
if !teamExists {
ctx.Handle(404, "OrgAssignment", err)
return
}
ctx.Data["IsTeamMember"] = ctx.Org.IsTeamMember
if requireTeamMember && !ctx.Org.IsTeamMember {
ctx.Handle(404, "OrgAssignment", err)
return
}
ctx.Org.IsTeamAdmin = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.Authorize >= models.ACCESS_MODE_ADMIN
ctx.Data["IsTeamAdmin"] = ctx.Org.IsTeamAdmin
if requireTeamAdmin && !ctx.Org.IsTeamAdmin {
ctx.Handle(404, "OrgAssignment", err)
return return
} }
ctx.Data["Team"] = ctx.Org.Team
ctx.Org.IsAdminTeam = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.Authorize >= models.ACCESS_MODE_ADMIN
}
ctx.Data["IsAdminTeam"] = ctx.Org.IsAdminTeam
if requireAdminTeam && !ctx.Org.IsAdminTeam {
ctx.Handle(404, "OrgAssignment", err)
return
} }
} }

123
modules/middleware/repo.go → modules/context/repo.go

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package middleware package context
import ( import (
"fmt" "fmt"
@ -18,6 +18,56 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
type PullRequest struct {
BaseRepo *models.Repository
Allowed bool
SameRepo bool
HeadInfo string // [<user>:]<branch>
}
type Repository struct {
AccessMode models.AccessMode
IsWatching bool
IsViewBranch bool
IsViewTag bool
IsViewCommit bool
Repository *models.Repository
Owner *models.User
Commit *git.Commit
Tag *git.Tag
GitRepo *git.Repository
BranchName string
TagName string
TreeName string
CommitID string
RepoLink string
CloneLink models.CloneLink
CommitsCount int64
Mirror *models.Mirror
PullRequest *PullRequest
}
// IsOwner returns true if current user is the owner of repository.
func (r *Repository) IsOwner() bool {
return r.AccessMode >= models.ACCESS_MODE_OWNER
}
// IsAdmin returns true if current user has admin or higher access of repository.
func (r *Repository) IsAdmin() bool {
return r.AccessMode >= models.ACCESS_MODE_ADMIN
}
// IsWriter returns true if current user has write or higher access of repository.
func (r *Repository) IsWriter() bool {
return r.AccessMode >= models.ACCESS_MODE_WRITE
}
// HasAccess returns true if the current user has at least read access for this repository
func (r *Repository) HasAccess() bool {
return r.AccessMode >= models.ACCESS_MODE_READ
}
func RetrieveBaseRepo(ctx *Context, repo *models.Repository) { func RetrieveBaseRepo(ctx *Context, repo *models.Repository) {
// Non-fork repository will not return error in this method. // Non-fork repository will not return error in this method.
if err := repo.GetBaseRepo(); err != nil { if err := repo.GetBaseRepo(); err != nil {
@ -32,25 +82,6 @@ func RetrieveBaseRepo(ctx *Context, repo *models.Repository) {
ctx.Handle(500, "BaseRepo.GetOwner", err) ctx.Handle(500, "BaseRepo.GetOwner", err)
return return
} }
bsaeRepo := repo.BaseRepo
baseGitRepo, err := git.OpenRepository(models.RepoPath(bsaeRepo.Owner.Name, bsaeRepo.Name))
if err != nil {
ctx.Handle(500, "OpenRepository", err)
return
}
if len(bsaeRepo.DefaultBranch) > 0 && baseGitRepo.IsBranchExist(bsaeRepo.DefaultBranch) {
ctx.Data["BaseDefaultBranch"] = bsaeRepo.DefaultBranch
} else {
baseBranches, err := baseGitRepo.GetBranches()
if err != nil {
ctx.Handle(500, "GetBranches", err)
return
}
if len(baseBranches) > 0 {
ctx.Data["BaseDefaultBranch"] = baseBranches[0]
}
}
} }
func RepoAssignment(args ...bool) macaron.Handler { func RepoAssignment(args ...bool) macaron.Handler {
@ -154,22 +185,14 @@ func RepoAssignment(args ...bool) macaron.Handler {
ctx.Data["Tags"] = tags ctx.Data["Tags"] = tags
ctx.Repo.Repository.NumTags = len(tags) ctx.Repo.Repository.NumTags = len(tags)
if repo.IsFork {
RetrieveBaseRepo(ctx, repo)
if ctx.Written() {
return
}
}
ctx.Data["Title"] = owner.Name + "/" + repo.Name ctx.Data["Title"] = owner.Name + "/" + repo.Name
ctx.Data["Repository"] = repo ctx.Data["Repository"] = repo
ctx.Data["Owner"] = ctx.Repo.Repository.Owner ctx.Data["Owner"] = ctx.Repo.Repository.Owner
ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner() ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin() ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
ctx.Data["IsRepositoryPusher"] = ctx.Repo.IsPusher() ctx.Data["IsRepositoryWriter"] = ctx.Repo.IsWriter()
ctx.Data["CanPullRequest"] = ctx.Repo.IsAdmin() && repo.BaseRepo != nil && repo.BaseRepo.EnablePulls
ctx.Data["DisableSSH"] = setting.DisableSSH ctx.Data["DisableSSH"] = setting.SSH.Disabled
ctx.Data["CloneLink"] = repo.CloneLink() ctx.Data["CloneLink"] = repo.CloneLink()
ctx.Data["WikiCloneLink"] = repo.WikiCloneLink() ctx.Data["WikiCloneLink"] = repo.WikiCloneLink()
@ -210,13 +233,43 @@ func RepoAssignment(args ...bool) macaron.Handler {
ctx.Repo.BranchName = brs[0] ctx.Repo.BranchName = brs[0]
} }
} }
ctx.Data["BranchName"] = ctx.Repo.BranchName ctx.Data["BranchName"] = ctx.Repo.BranchName
ctx.Data["CommitID"] = ctx.Repo.CommitID ctx.Data["CommitID"] = ctx.Repo.CommitID
if repo.IsFork {
RetrieveBaseRepo(ctx, repo)
if ctx.Written() {
return
}
}
// People who have push access and propose a new pull request.
if ctx.Repo.IsWriter() {
// Pull request is allowed if this is a fork repository
// and base repository accepts pull requests.
if repo.BaseRepo != nil {
if repo.BaseRepo.AllowsPulls() {
ctx.Data["BaseRepo"] = repo.BaseRepo
ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo
ctx.Repo.PullRequest.Allowed = true
ctx.Repo.PullRequest.HeadInfo = ctx.Repo.Owner.Name + ":" + ctx.Repo.BranchName
}
} else {
// Or, this is repository accepts pull requests between branches.
if repo.AllowsPulls() {
ctx.Data["BaseRepo"] = repo
ctx.Repo.PullRequest.BaseRepo = repo
ctx.Repo.PullRequest.Allowed = true
ctx.Repo.PullRequest.SameRepo = true
ctx.Repo.PullRequest.HeadInfo = ctx.Repo.BranchName
}
}
}
ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
if ctx.Query("go-get") == "1" { if ctx.Query("go-get") == "1" {
ctx.Data["GoGetImport"] = path.Join(setting.Domain, setting.AppSubUrl, owner.Name, repo.Name) ctx.Data["GoGetImport"] = path.Join(setting.Domain, setting.AppSubUrl, owner.Name, repo.Name)
prefix := path.Join(setting.AppUrl, owner.Name, repo.Name, "src", ctx.Repo.BranchName) prefix := setting.AppUrl + path.Join(owner.Name, repo.Name, "src", ctx.Repo.BranchName)
ctx.Data["GoDocDirectory"] = prefix + "{/dir}" ctx.Data["GoDocDirectory"] = prefix + "{/dir}"
ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}" ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}"
} }
@ -337,16 +390,16 @@ func RepoRef() macaron.Handler {
func RequireRepoAdmin() macaron.Handler { func RequireRepoAdmin() macaron.Handler {
return func(ctx *Context) { return func(ctx *Context) {
if !ctx.Repo.IsAdmin() { if !ctx.IsSigned || (!ctx.Repo.IsAdmin() && !ctx.User.IsAdmin) {
ctx.Handle(404, ctx.Req.RequestURI, nil) ctx.Handle(404, ctx.Req.RequestURI, nil)
return return
} }
} }
} }
func RequireRepoPusher() macaron.Handler { func RequireRepoWriter() macaron.Handler {
return func(ctx *Context) { return func(ctx *Context) {
if !ctx.Repo.IsPusher() { if !ctx.IsSigned || (!ctx.Repo.IsWriter() && !ctx.User.IsAdmin) {
ctx.Handle(404, ctx.Req.RequestURI, nil) ctx.Handle(404, ctx.Req.RequestURI, nil)
return return
} }

27
modules/cron/constantdelay.go

@ -1,27 +0,0 @@
package cron
import "time"
// ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes".
// It does not support jobs more frequent than once a second.
type ConstantDelaySchedule struct {
Delay time.Duration
}
// Every returns a crontab Schedule that activates once every duration.
// Delays of less than a second are not supported (will round up to 1 second).
// Any fields less than a Second are truncated.
func Every(duration time.Duration) ConstantDelaySchedule {
if duration < time.Second {
duration = time.Second
}
return ConstantDelaySchedule{
Delay: duration - time.Duration(duration.Nanoseconds())%time.Second,
}
}
// Next returns the next time this should be run.
// This rounds so that the next activation time will be on the second.
func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time {
return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond)
}

54
modules/cron/constantdelay_test.go

@ -1,54 +0,0 @@
package cron
import (
"testing"
"time"
)
func TestConstantDelayNext(t *testing.T) {
tests := []struct {
time string
delay time.Duration
expected string
}{
// Simple cases
{"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"},
{"Mon Jul 9 14:59 2012", 15 * time.Minute, "Mon Jul 9 15:14 2012"},
{"Mon Jul 9 14:59:59 2012", 15 * time.Minute, "Mon Jul 9 15:14:59 2012"},
// Wrap around hours
{"Mon Jul 9 15:45 2012", 35 * time.Minute, "Mon Jul 9 16:20 2012"},
// Wrap around days
{"Mon Jul 9 23:46 2012", 14 * time.Minute, "Tue Jul 10 00:00 2012"},
{"Mon Jul 9 23:45 2012", 35 * time.Minute, "Tue Jul 10 00:20 2012"},
{"Mon Jul 9 23:35:51 2012", 44*time.Minute + 24*time.Second, "Tue Jul 10 00:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", 25*time.Hour + 44*time.Minute + 24*time.Second, "Thu Jul 11 01:20:15 2012"},
// Wrap around months
{"Mon Jul 9 23:35 2012", 91*24*time.Hour + 25*time.Minute, "Thu Oct 9 00:00 2012"},
// Wrap around minute, hour, day, month, and year
{"Mon Dec 31 23:59:45 2012", 15 * time.Second, "Tue Jan 1 00:00:00 2013"},
// Round to nearest second on the delay
{"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"},
// Round up to 1 second if the duration is less.
{"Mon Jul 9 14:45:00 2012", 15 * time.Millisecond, "Mon Jul 9 14:45:01 2012"},
// Round to nearest second when calculating the next time.
{"Mon Jul 9 14:45:00.005 2012", 15 * time.Minute, "Mon Jul 9 15:00 2012"},
// Round to nearest second for both.
{"Mon Jul 9 14:45:00.005 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"},
}
for _, c := range tests {
actual := Every(c.delay).Next(getTime(c.time))
expected := getTime(c.expected)
if actual != expected {
t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.delay, expected, actual)
}
}
}

237
modules/cron/cron.go

@ -1,4 +1,3 @@
// Copyright 2012 Rob Figueiredo. All rights reserved.
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -6,207 +5,59 @@
package cron package cron
import ( import (
"sort"
"time" "time"
)
// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
entries []*Entry
stop chan struct{}
add chan *Entry
snapshot chan []*Entry
running bool
}
// Job is an interface for submitted cron jobs.
type Job interface {
Run()
}
// The Schedule describes a job's duty cycle.
type Schedule interface {
// Return the next activation time, later than the given time.
// Next is invoked initially, and then each time the job is run.
Next(time.Time) time.Time
}
// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
Description string
Spec string
// The schedule on which this job should be run.
Schedule Schedule
// The next time the job will run. This is the zero time if Cron has not been
// started or this entry's schedule is unsatisfiable
Next time.Time
// The last time this job was run. This is the zero time if the job has never
// been run.
Prev time.Time
// The Job to run.
Job Job
ExecTimes int // Execute times count.
}
// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry
func (s byTime) Len() int { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
// Two zero times should return false.
// Otherwise, zero is "greater" than any other time.
// (To sort it at the end of the list.)
if s[i].Next.IsZero() {
return false
}
if s[j].Next.IsZero() {
return true
}
return s[i].Next.Before(s[j].Next)
}
// New returns a new Cron job runner.
func New() *Cron {
return &Cron{
entries: nil,
add: make(chan *Entry),
stop: make(chan struct{}),
snapshot: make(chan []*Entry),
running: false,
}
}
// A wrapper that turns a func() into a cron.Job
type FuncJob func()
func (f FuncJob) Run() { f() }
// AddFunc adds a func to the Cron to be run on the given schedule.
func (c *Cron) AddFunc(desc, spec string, cmd func()) (*Entry, error) {
return c.AddJob(desc, spec, FuncJob(cmd))
}
// AddFunc adds a Job to the Cron to be run on the given schedule. "github.com/gogits/cron"
func (c *Cron) AddJob(desc, spec string, cmd Job) (*Entry, error) {
schedule, err := Parse(spec)
if err != nil {
return nil, err
}
return c.Schedule(desc, spec, schedule, cmd), nil
}
// Schedule adds a Job to the Cron to be run on the given schedule. "github.com/gogits/gogs/models"
func (c *Cron) Schedule(desc, spec string, schedule Schedule, cmd Job) *Entry { "github.com/gogits/gogs/modules/log"
entry := &Entry{ "github.com/gogits/gogs/modules/setting"
Description: desc, )
Spec: spec,
Schedule: schedule,
Job: cmd,
}
if c.running {
c.add <- entry
} else {
c.entries = append(c.entries, entry)
}
return entry
}
// Entries returns a snapshot of the cron entries. var c = cron.New()
func (c *Cron) Entries() []*Entry {
if c.running { func NewContext() {
c.snapshot <- nil var (
x := <-c.snapshot entry *cron.Entry
return x err error
)
if setting.Cron.UpdateMirror.Enabled {
entry, err = c.AddFunc("Update mirrors", setting.Cron.UpdateMirror.Schedule, models.MirrorUpdate)
if err != nil {
log.Fatal(4, "Cron[Update mirrors]: %v", err)
}
if setting.Cron.UpdateMirror.RunAtStart {
entry.Prev = time.Now()
entry.ExecTimes++
go models.MirrorUpdate()
}
} }
return c.entrySnapshot() if setting.Cron.RepoHealthCheck.Enabled {
} entry, err = c.AddFunc("Repository health check", setting.Cron.RepoHealthCheck.Schedule, models.GitFsck)
if err != nil {
// Start the cron scheduler in its own go-routine. log.Fatal(4, "Cron[Repository health check]: %v", err)
func (c *Cron) Start() { }
c.running = true if setting.Cron.RepoHealthCheck.RunAtStart {
go c.run() entry.Prev = time.Now()
} entry.ExecTimes++
go models.GitFsck()
// Run the scheduler.. this is private just due to the need to synchronize }
// access to the 'running' state variable.
func (c *Cron) run() {
// Figure out the next activation times for each entry.
now := time.Now().Local()
for _, entry := range c.entries {
entry.Next = entry.Schedule.Next(now)
} }
if setting.Cron.CheckRepoStats.Enabled {
for { entry, err = c.AddFunc("Check repository statistics", setting.Cron.CheckRepoStats.Schedule, models.CheckRepoStats)
// Determine the next entry to run. if err != nil {
sort.Sort(byTime(c.entries)) log.Fatal(4, "Cron[Check repository statistics]: %v", err)
var effective time.Time
if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
// If there are no entries yet, just sleep - it still handles new entries
// and stop requests.
effective = now.AddDate(10, 0, 0)
} else {
effective = c.entries[0].Next
} }
if setting.Cron.CheckRepoStats.RunAtStart {
select { entry.Prev = time.Now()
case now = <-time.After(effective.Sub(now)): entry.ExecTimes++
// Run every entry whose next time was this effective time. go models.CheckRepoStats()
for _, e := range c.entries {
if e.Next != effective {
break
}
go e.Job.Run()
e.ExecTimes++
e.Prev = e.Next
e.Next = e.Schedule.Next(effective)
}
continue
case newEntry := <-c.add:
c.entries = append(c.entries, newEntry)
newEntry.Next = newEntry.Schedule.Next(now)
case <-c.snapshot:
c.snapshot <- c.entrySnapshot()
case <-c.stop:
return
} }
// 'now' should be updated after newEntry and snapshot cases.
now = time.Now().Local()
} }
c.Start()
} }
// Stop the cron scheduler. // ListTasks returns all running cron tasks.
func (c *Cron) Stop() { func ListTasks() []*cron.Entry {
c.stop <- struct{}{} return c.Entries()
c.running = false
}
// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []*Entry {
entries := make([]*Entry, 0, len(c.entries))
for _, e := range c.entries {
entries = append(entries, &Entry{
Description: e.Description,
Spec: e.Spec,
Schedule: e.Schedule,
Next: e.Next,
Prev: e.Prev,
Job: e.Job,
ExecTimes: e.ExecTimes,
})
}
return entries
} }

255
modules/cron/cron_test.go

@ -1,255 +0,0 @@
package cron
import (
"fmt"
"sync"
"testing"
"time"
)
// Many tests schedule a job for every second, and then wait at most a second
// for it to run. This amount is just slightly larger than 1 second to
// compensate for a few milliseconds of runtime.
const ONE_SECOND = 1*time.Second + 10*time.Millisecond
// Start and stop cron with no entries.
func TestNoEntries(t *testing.T) {
cron := New()
cron.Start()
select {
case <-time.After(ONE_SECOND):
t.FailNow()
case <-stop(cron):
}
}
// Start, stop, then add an entry. Verify entry doesn't run.
func TestStopCausesJobsToNotRun(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := New()
cron.Start()
cron.Stop()
cron.AddFunc("", "* * * * * ?", func() { wg.Done() })
select {
case <-time.After(ONE_SECOND):
// No job ran!
case <-wait(wg):
t.FailNow()
}
}
// Add a job, start cron, expect it runs.
func TestAddBeforeRunning(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := New()
cron.AddFunc("", "* * * * * ?", func() { wg.Done() })
cron.Start()
defer cron.Stop()
// Give cron 2 seconds to run our job (which is always activated).
select {
case <-time.After(ONE_SECOND):
t.FailNow()
case <-wait(wg):
}
}
// Start cron, add a job, expect it runs.
func TestAddWhileRunning(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := New()
cron.Start()
defer cron.Stop()
cron.AddFunc("", "* * * * * ?", func() { wg.Done() })
select {
case <-time.After(ONE_SECOND):
t.FailNow()
case <-wait(wg):
}
}
// Test timing with Entries.
func TestSnapshotEntries(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := New()
cron.AddFunc("", "@every 2s", func() { wg.Done() })
cron.Start()
defer cron.Stop()
// Cron should fire in 2 seconds. After 1 second, call Entries.
select {
case <-time.After(ONE_SECOND):
cron.Entries()
}
// Even though Entries was called, the cron should fire at the 2 second mark.
select {
case <-time.After(ONE_SECOND):
t.FailNow()
case <-wait(wg):
}
}
// Test that the entries are correctly sorted.
// Add a bunch of long-in-the-future entries, and an immediate entry, and ensure
// that the immediate entry runs immediately.
// Also: Test that multiple jobs run in the same instant.
func TestMultipleEntries(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
cron := New()
cron.AddFunc("", "0 0 0 1 1 ?", func() {})
cron.AddFunc("", "* * * * * ?", func() { wg.Done() })
cron.AddFunc("", "0 0 0 31 12 ?", func() {})
cron.AddFunc("", "* * * * * ?", func() { wg.Done() })
cron.Start()
defer cron.Stop()
select {
case <-time.After(ONE_SECOND):
t.FailNow()
case <-wait(wg):
}
}
// Test running the same job twice.
func TestRunningJobTwice(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
cron := New()
cron.AddFunc("", "0 0 0 1 1 ?", func() {})
cron.AddFunc("", "0 0 0 31 12 ?", func() {})
cron.AddFunc("", "* * * * * ?", func() { wg.Done() })
cron.Start()
defer cron.Stop()
select {
case <-time.After(2 * ONE_SECOND):
t.FailNow()
case <-wait(wg):
}
}
func TestRunningMultipleSchedules(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
cron := New()
cron.AddFunc("", "0 0 0 1 1 ?", func() {})
cron.AddFunc("", "0 0 0 31 12 ?", func() {})
cron.AddFunc("", "* * * * * ?", func() { wg.Done() })
cron.Schedule("", "", Every(time.Minute), FuncJob(func() {}))
cron.Schedule("", "", Every(time.Second), FuncJob(func() { wg.Done() }))
cron.Schedule("", "", Every(time.Hour), FuncJob(func() {}))
cron.Start()
defer cron.Stop()
select {
case <-time.After(2 * ONE_SECOND):
t.FailNow()
case <-wait(wg):
}
}
// Test that the cron is run in the local time zone (as opposed to UTC).
func TestLocalTimezone(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
now := time.Now().Local()
spec := fmt.Sprintf("%d %d %d %d %d ?",
now.Second()+1, now.Minute(), now.Hour(), now.Day(), now.Month())
cron := New()
cron.AddFunc("", spec, func() { wg.Done() })
cron.Start()
defer cron.Stop()
select {
case <-time.After(ONE_SECOND):
t.FailNow()
case <-wait(wg):
}
}
type testJob struct {
wg *sync.WaitGroup
name string
}
func (t testJob) Run() {
t.wg.Done()
}
// Simple test using Runnables.
func TestJob(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := New()
cron.AddJob("", "0 0 0 30 Feb ?", testJob{wg, "job0"})
cron.AddJob("", "0 0 0 1 1 ?", testJob{wg, "job1"})
cron.AddJob("", "* * * * * ?", testJob{wg, "job2"})
cron.AddJob("", "1 0 0 1 1 ?", testJob{wg, "job3"})
cron.Schedule("", "", Every(5*time.Second+5*time.Nanosecond), testJob{wg, "job4"})
cron.Schedule("", "", Every(5*time.Minute), testJob{wg, "job5"})
cron.Start()
defer cron.Stop()
select {
case <-time.After(ONE_SECOND):
t.FailNow()
case <-wait(wg):
}
// Ensure the entries are in the right order.
expecteds := []string{"job2", "job4", "job5", "job1", "job3", "job0"}
var actuals []string
for _, entry := range cron.Entries() {
actuals = append(actuals, entry.Job.(testJob).name)
}
for i, expected := range expecteds {
if actuals[i] != expected {
t.Errorf("Jobs not in the right order. (expected) %s != %s (actual)", expecteds, actuals)
t.FailNow()
}
}
}
func wait(wg *sync.WaitGroup) chan bool {
ch := make(chan bool)
go func() {
wg.Wait()
ch <- true
}()
return ch
}
func stop(cron *Cron) chan bool {
ch := make(chan bool)
go func() {
cron.Stop()
ch <- true
}()
return ch
}

129
modules/cron/doc.go

@ -1,129 +0,0 @@
/*
Package cron implements a cron spec parser and job runner.
Usage
Callers may register Funcs to be invoked on a given schedule. Cron will run
them in their own goroutines.
c := cron.New()
c.AddFunc("Every hour on the half hour","0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("Every hour","@hourly", func() { fmt.Println("Every hour") })
c.AddFunc("Every hour and a half","@every 1h30m", func() { fmt.Println("Every hour thirty") })
c.Start()
..
// Funcs are invoked in their own goroutine, asynchronously.
...
// Funcs may also be added to a running Cron
c.AddFunc("@daily", func() { fmt.Println("Every day") })
..
// Inspect the cron job entries' next and previous run times.
inspect(c.Entries())
..
c.Stop() // Stop the scheduler (does not stop any jobs already running).
CRON Expression Format
A cron expression represents a set of times, using 6 space-separated fields.
Field name | Mandatory? | Allowed values | Allowed special characters
---------- | ---------- | -------------- | --------------------------
Seconds | Yes | 0-59 | * / , -
Minutes | Yes | 0-59 | * / , -
Hours | Yes | 0-23 | * / , -
Day of month | Yes | 1-31 | * / , - ?
Month | Yes | 1-12 or JAN-DEC | * / , -
Day of week | Yes | 0-6 or SUN-SAT | * / , - ?
Note: Month and Day-of-week field values are case insensitive. "SUN", "Sun",
and "sun" are equally accepted.
Special Characters
Asterisk ( * )
The asterisk indicates that the cron expression will match for all values of the
field; e.g., using an asterisk in the 5th field (month) would indicate every
month.
Slash ( / )
Slashes are used to describe increments of ranges. For example 3-59/15 in the
1st field (minutes) would indicate the 3rd minute of the hour and every 15
minutes thereafter. The form "*\/..." is equivalent to the form "first-last/...",
that is, an increment over the largest possible range of the field. The form
"N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the
increment until the end of that specific range. It does not wrap around.
Comma ( , )
Commas are used to separate items of a list. For example, using "MON,WED,FRI" in
the 5th field (day of week) would mean Mondays, Wednesdays and Fridays.
Hyphen ( - )
Hyphens are used to define ranges. For example, 9-17 would indicate every
hour between 9am and 5pm inclusive.
Question mark ( ? )
Question mark may be used instead of '*' for leaving either day-of-month or
day-of-week blank.
Predefined schedules
You may use one of several pre-defined schedules in place of a cron expression.
Entry | Description | Equivalent To
----- | ----------- | -------------
@yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 *
@monthly | Run once a month, midnight, first of month | 0 0 0 1 * *
@weekly | Run once a week, midnight on Sunday | 0 0 0 * * 0
@daily (or @midnight) | Run once a day, midnight | 0 0 0 * * *
@hourly | Run once an hour, beginning of hour | 0 0 * * * *
Intervals
You may also schedule a job to execute at fixed intervals. This is supported by
formatting the cron spec like this:
@every <duration>
where "duration" is a string accepted by time.ParseDuration
(http://golang.org/pkg/time/#ParseDuration).
For example, "@every 1h30m10s" would indicate a schedule that activates every
1 hour, 30 minutes, 10 seconds.
Note: The interval does not take the job runtime into account. For example,
if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes,
it will have only 2 minutes of idle time between each run.
Time zones
All interpretation and scheduling is done in the machine's local time zone (as
provided by the Go time package (http://www.golang.org/pkg/time).
Be aware that jobs scheduled during daylight-savings leap-ahead transitions will
not be run!
Thread safety
Since the Cron service runs concurrently with the calling code, some amount of
care must be taken to ensure proper synchronization.
All cron methods are designed to be correctly synchronized as long as the caller
ensures that invocations have a clear happens-before ordering between them.
Implementation
Cron entries are stored in an array, sorted by their next activation time. Cron
sleeps until the next job is due to be run.
Upon waking:
- it runs each entry that is active on that second
- it calculates the next run times for the jobs that were run
- it re-sorts the array of entries by next activation time.
- it goes to sleep until the soonest job.
*/
package cron

231
modules/cron/parser.go

@ -1,231 +0,0 @@
package cron
import (
"fmt"
"log"
"math"
"strconv"
"strings"
"time"
)
// Parse returns a new crontab schedule representing the given spec.
// It returns a descriptive error if the spec is not valid.
//
// It accepts
// - Full crontab specs, e.g. "* * * * * ?"
// - Descriptors, e.g. "@midnight", "@every 1h30m"
func Parse(spec string) (_ Schedule, err error) {
// Convert panics into errors
defer func() {
if recovered := recover(); recovered != nil {
err = fmt.Errorf("%v", recovered)
}
}()
if spec[0] == '@' {
return parseDescriptor(spec), nil
}
// Split on whitespace. We require 5 or 6 fields.
// (second) (minute) (hour) (day of month) (month) (day of week, optional)
fields := strings.Fields(spec)
if len(fields) != 5 && len(fields) != 6 {
log.Panicf("Expected 5 or 6 fields, found %d: %s", len(fields), spec)
}
// If a sixth field is not provided (DayOfWeek), then it is equivalent to star.
if len(fields) == 5 {
fields = append(fields, "*")
}
schedule := &SpecSchedule{
Second: getField(fields[0], seconds),
Minute: getField(fields[1], minutes),
Hour: getField(fields[2], hours),
Dom: getField(fields[3], dom),
Month: getField(fields[4], months),
Dow: getField(fields[5], dow),
}
return schedule, nil
}
// getField returns an Int with the bits set representing all of the times that
// the field represents. A "field" is a comma-separated list of "ranges".
func getField(field string, r bounds) uint64 {
// list = range {"," range}
var bits uint64
ranges := strings.FieldsFunc(field, func(r rune) bool { return r == ',' })
for _, expr := range ranges {
bits |= getRange(expr, r)
}
return bits
}
// getRange returns the bits indicated by the given expression:
// number | number "-" number [ "/" number ]
func getRange(expr string, r bounds) uint64 {
var (
start, end, step uint
rangeAndStep = strings.Split(expr, "/")
lowAndHigh = strings.Split(rangeAndStep[0], "-")
singleDigit = len(lowAndHigh) == 1
)
var extra_star uint64
if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" {
start = r.min
end = r.max
extra_star = starBit
} else {
start = parseIntOrName(lowAndHigh[0], r.names)
switch len(lowAndHigh) {
case 1:
end = start
case 2:
end = parseIntOrName(lowAndHigh[1], r.names)
default:
log.Panicf("Too many hyphens: %s", expr)
}
}
switch len(rangeAndStep) {
case 1:
step = 1
case 2:
step = mustParseInt(rangeAndStep[1])
// Special handling: "N/step" means "N-max/step".
if singleDigit {
end = r.max
}
default:
log.Panicf("Too many slashes: %s", expr)
}
if start < r.min {
log.Panicf("Beginning of range (%d) below minimum (%d): %s", start, r.min, expr)
}
if end > r.max {
log.Panicf("End of range (%d) above maximum (%d): %s", end, r.max, expr)
}
if start > end {
log.Panicf("Beginning of range (%d) beyond end of range (%d): %s", start, end, expr)
}
return getBits(start, end, step) | extra_star
}
// parseIntOrName returns the (possibly-named) integer contained in expr.
func parseIntOrName(expr string, names map[string]uint) uint {
if names != nil {
if namedInt, ok := names[strings.ToLower(expr)]; ok {
return namedInt
}
}
return mustParseInt(expr)
}
// mustParseInt parses the given expression as an int or panics.
func mustParseInt(expr string) uint {
num, err := strconv.Atoi(expr)
if err != nil {
log.Panicf("Failed to parse int from %s: %s", expr, err)
}
if num < 0 {
log.Panicf("Negative number (%d) not allowed: %s", num, expr)
}
return uint(num)
}
// getBits sets all bits in the range [min, max], modulo the given step size.
func getBits(min, max, step uint) uint64 {
var bits uint64
// If step is 1, use shifts.
if step == 1 {
return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min)
}
// Else, use a simple loop.
for i := min; i <= max; i += step {
bits |= 1 << i
}
return bits
}
// all returns all bits within the given bounds. (plus the star bit)
func all(r bounds) uint64 {
return getBits(r.min, r.max, 1) | starBit
}
// parseDescriptor returns a pre-defined schedule for the expression, or panics
// if none matches.
func parseDescriptor(spec string) Schedule {
switch spec {
case "@yearly", "@annually":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: 1 << dom.min,
Month: 1 << months.min,
Dow: all(dow),
}
case "@monthly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: 1 << dom.min,
Month: all(months),
Dow: all(dow),
}
case "@weekly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: all(dom),
Month: all(months),
Dow: 1 << dow.min,
}
case "@daily", "@midnight":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: all(dom),
Month: all(months),
Dow: all(dow),
}
case "@hourly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: all(hours),
Dom: all(dom),
Month: all(months),
Dow: all(dow),
}
}
const every = "@every "
if strings.HasPrefix(spec, every) {
duration, err := time.ParseDuration(spec[len(every):])
if err != nil {
log.Panicf("Failed to parse duration %s: %s", spec, err)
}
return Every(duration)
}
log.Panicf("Unrecognized descriptor: %s", spec)
return nil
}

117
modules/cron/parser_test.go

@ -1,117 +0,0 @@
package cron
import (
"reflect"
"testing"
"time"
)
func TestRange(t *testing.T) {
ranges := []struct {
expr string
min, max uint
expected uint64
}{
{"5", 0, 7, 1 << 5},
{"0", 0, 7, 1 << 0},
{"7", 0, 7, 1 << 7},
{"5-5", 0, 7, 1 << 5},
{"5-6", 0, 7, 1<<5 | 1<<6},
{"5-7", 0, 7, 1<<5 | 1<<6 | 1<<7},
{"5-6/2", 0, 7, 1 << 5},
{"5-7/2", 0, 7, 1<<5 | 1<<7},
{"5-7/1", 0, 7, 1<<5 | 1<<6 | 1<<7},
{"*", 1, 3, 1<<1 | 1<<2 | 1<<3 | starBit},
{"*/2", 1, 3, 1<<1 | 1<<3 | starBit},
}
for _, c := range ranges {
actual := getRange(c.expr, bounds{c.min, c.max, nil})
if actual != c.expected {
t.Errorf("%s => (expected) %d != %d (actual)", c.expr, c.expected, actual)
}
}
}
func TestField(t *testing.T) {
fields := []struct {
expr string
min, max uint
expected uint64
}{
{"5", 1, 7, 1 << 5},
{"5,6", 1, 7, 1<<5 | 1<<6},
{"5,6,7", 1, 7, 1<<5 | 1<<6 | 1<<7},
{"1,5-7/2,3", 1, 7, 1<<1 | 1<<5 | 1<<7 | 1<<3},
}
for _, c := range fields {
actual := getField(c.expr, bounds{c.min, c.max, nil})
if actual != c.expected {
t.Errorf("%s => (expected) %d != %d (actual)", c.expr, c.expected, actual)
}
}
}
func TestBits(t *testing.T) {
allBits := []struct {
r bounds
expected uint64
}{
{minutes, 0xfffffffffffffff}, // 0-59: 60 ones
{hours, 0xffffff}, // 0-23: 24 ones
{dom, 0xfffffffe}, // 1-31: 31 ones, 1 zero
{months, 0x1ffe}, // 1-12: 12 ones, 1 zero
{dow, 0x7f}, // 0-6: 7 ones
}
for _, c := range allBits {
actual := all(c.r) // all() adds the starBit, so compensate for that..
if c.expected|starBit != actual {
t.Errorf("%d-%d/%d => (expected) %b != %b (actual)",
c.r.min, c.r.max, 1, c.expected|starBit, actual)
}
}
bits := []struct {
min, max, step uint
expected uint64
}{
{0, 0, 1, 0x1},
{1, 1, 1, 0x2},
{1, 5, 2, 0x2a}, // 101010
{1, 4, 2, 0xa}, // 1010
}
for _, c := range bits {
actual := getBits(c.min, c.max, c.step)
if c.expected != actual {
t.Errorf("%d-%d/%d => (expected) %b != %b (actual)",
c.min, c.max, c.step, c.expected, actual)
}
}
}
func TestSpecSchedule(t *testing.T) {
entries := []struct {
expr string
expected Schedule
}{
{"* 5 * * * *", &SpecSchedule{all(seconds), 1 << 5, all(hours), all(dom), all(months), all(dow)}},
{"@every 5m", ConstantDelaySchedule{time.Duration(5) * time.Minute}},
}
for _, c := range entries {
actual, err := Parse(c.expr)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(actual, c.expected) {
t.Errorf("%s => (expected) %v != %v (actual)", c.expr, c.expected, actual)
}
}
}

161
modules/cron/spec.go

@ -1,161 +0,0 @@
package cron
import (
"time"
)
// SpecSchedule specifies a duty cycle (to the second granularity), based on a
// traditional crontab specification. It is computed initially and stored as bit sets.
type SpecSchedule struct {
Second, Minute, Hour, Dom, Month, Dow uint64
}
// bounds provides a range of acceptable values (plus a map of name to value).
type bounds struct {
min, max uint
names map[string]uint
}
// The bounds for each field.
var (
seconds = bounds{0, 59, nil}
minutes = bounds{0, 59, nil}
hours = bounds{0, 23, nil}
dom = bounds{1, 31, nil}
months = bounds{1, 12, map[string]uint{
"jan": 1,
"feb": 2,
"mar": 3,
"apr": 4,
"may": 5,
"jun": 6,
"jul": 7,
"aug": 8,
"sep": 9,
"oct": 10,
"nov": 11,
"dec": 12,
}}
dow = bounds{0, 6, map[string]uint{
"sun": 0,
"mon": 1,
"tue": 2,
"wed": 3,
"thu": 4,
"fri": 5,
"sat": 6,
}}
)
const (
// Set the top bit if a star was included in the expression.
starBit = 1 << 63
)
// Next returns the next time this schedule is activated, greater than the given
// time. If no time can be found to satisfy the schedule, return the zero time.
func (s *SpecSchedule) Next(t time.Time) time.Time {
// General approach:
// For Month, Day, Hour, Minute, Second:
// Check if the time value matches. If yes, continue to the next field.
// If the field doesn't match the schedule, then increment the field until it matches.
// While incrementing the field, a wrap-around brings it back to the beginning
// of the field list (since it is necessary to re-verify previous field
// values)
// Start at the earliest possible time (the upcoming second).
t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond)
// This flag indicates whether a field has been incremented.
added := false
// If no time is found within five years, return zero.
yearLimit := t.Year() + 5
WRAP:
if t.Year() > yearLimit {
return time.Time{}
}
// Find the first applicable month.
// If it's this month, then do nothing.
for 1<<uint(t.Month())&s.Month == 0 {
// If we have to add a month, reset the other parts to 0.
if !added {
added = true
// Otherwise, set the date at the beginning (since the current time is irrelevant).
t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location())
}
t = t.AddDate(0, 1, 0)
// Wrapped around.
if t.Month() == time.January {
goto WRAP
}
}
// Now get a day in that month.
for !dayMatches(s, t) {
if !added {
added = true
t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}
t = t.AddDate(0, 0, 1)
if t.Day() == 1 {
goto WRAP
}
}
for 1<<uint(t.Hour())&s.Hour == 0 {
if !added {
added = true
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, t.Location())
}
t = t.Add(1 * time.Hour)
if t.Hour() == 0 {
goto WRAP
}
}
for 1<<uint(t.Minute())&s.Minute == 0 {
if !added {
added = true
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), 0, 0, t.Location())
}
t = t.Add(1 * time.Minute)
if t.Minute() == 0 {
goto WRAP
}
}
for 1<<uint(t.Second())&s.Second == 0 {
if !added {
added = true
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), 0, t.Location())
}
t = t.Add(1 * time.Second)
if t.Second() == 0 {
goto WRAP
}
}
return t
}
// dayMatches returns true if the schedule's day-of-week and day-of-month
// restrictions are satisfied by the given time.
func dayMatches(s *SpecSchedule, t time.Time) bool {
var (
domMatch bool = 1<<uint(t.Day())&s.Dom > 0
dowMatch bool = 1<<uint(t.Weekday())&s.Dow > 0
)
if s.Dom&starBit > 0 || s.Dow&starBit > 0 {
return domMatch && dowMatch
}
return domMatch || dowMatch
}

173
modules/cron/spec_test.go

@ -1,173 +0,0 @@
package cron
import (
"testing"
"time"
)
func TestActivation(t *testing.T) {
tests := []struct {
time, spec string
expected bool
}{
// Every fifteen minutes.
{"Mon Jul 9 15:00 2012", "0 0/15 * * *", true},
{"Mon Jul 9 15:45 2012", "0 0/15 * * *", true},
{"Mon Jul 9 15:40 2012", "0 0/15 * * *", false},
// Every fifteen minutes, starting at 5 minutes.
{"Mon Jul 9 15:05 2012", "0 5/15 * * *", true},
{"Mon Jul 9 15:20 2012", "0 5/15 * * *", true},
{"Mon Jul 9 15:50 2012", "0 5/15 * * *", true},
// Named months
{"Sun Jul 15 15:00 2012", "0 0/15 * * Jul", true},
{"Sun Jul 15 15:00 2012", "0 0/15 * * Jun", false},
// Everything set.
{"Sun Jul 15 08:30 2012", "0 30 08 ? Jul Sun", true},
{"Sun Jul 15 08:30 2012", "0 30 08 15 Jul ?", true},
{"Mon Jul 16 08:30 2012", "0 30 08 ? Jul Sun", false},
{"Mon Jul 16 08:30 2012", "0 30 08 15 Jul ?", false},
// Predefined schedules
{"Mon Jul 9 15:00 2012", "@hourly", true},
{"Mon Jul 9 15:04 2012", "@hourly", false},
{"Mon Jul 9 15:00 2012", "@daily", false},
{"Mon Jul 9 00:00 2012", "@daily", true},
{"Mon Jul 9 00:00 2012", "@weekly", false},
{"Sun Jul 8 00:00 2012", "@weekly", true},
{"Sun Jul 8 01:00 2012", "@weekly", false},
{"Sun Jul 8 00:00 2012", "@monthly", false},
{"Sun Jul 1 00:00 2012", "@monthly", true},
// Test interaction of DOW and DOM.
// If both are specified, then only one needs to match.
{"Sun Jul 15 00:00 2012", "0 * * 1,15 * Sun", true},
{"Fri Jun 15 00:00 2012", "0 * * 1,15 * Sun", true},
{"Wed Aug 1 00:00 2012", "0 * * 1,15 * Sun", true},
// However, if one has a star, then both need to match.
{"Sun Jul 15 00:00 2012", "0 * * * * Mon", false},
{"Sun Jul 15 00:00 2012", "0 * * */10 * Sun", false},
{"Mon Jul 9 00:00 2012", "0 * * 1,15 * *", false},
{"Sun Jul 15 00:00 2012", "0 * * 1,15 * *", true},
{"Sun Jul 15 00:00 2012", "0 * * */2 * Sun", true},
}
for _, test := range tests {
sched, err := Parse(test.spec)
if err != nil {
t.Error(err)
continue
}
actual := sched.Next(getTime(test.time).Add(-1 * time.Second))
expected := getTime(test.time)
if test.expected && expected != actual || !test.expected && expected == actual {
t.Errorf("Fail evaluating %s on %s: (expected) %s != %s (actual)",
test.spec, test.time, expected, actual)
}
}
}
func TestNext(t *testing.T) {
runs := []struct {
time, spec string
expected string
}{
// Simple cases
{"Mon Jul 9 14:45 2012", "0 0/15 * * *", "Mon Jul 9 15:00 2012"},
{"Mon Jul 9 14:59 2012", "0 0/15 * * *", "Mon Jul 9 15:00 2012"},
{"Mon Jul 9 14:59:59 2012", "0 0/15 * * *", "Mon Jul 9 15:00 2012"},
// Wrap around hours
{"Mon Jul 9 15:45 2012", "0 20-35/15 * * *", "Mon Jul 9 16:20 2012"},
// Wrap around days
{"Mon Jul 9 23:46 2012", "0 */15 * * *", "Tue Jul 10 00:00 2012"},
{"Mon Jul 9 23:45 2012", "0 20-35/15 * * *", "Tue Jul 10 00:20 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * * *", "Tue Jul 10 00:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 1/2 * *", "Tue Jul 10 01:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 10-12 * *", "Tue Jul 10 10:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 1/2 */2 * *", "Thu Jul 11 01:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * 9-20 * *", "Wed Jul 10 00:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * 9-20 Jul *", "Wed Jul 10 00:20:15 2012"},
// Wrap around months
{"Mon Jul 9 23:35 2012", "0 0 0 9 Apr-Oct ?", "Thu Aug 9 00:00 2012"},
{"Mon Jul 9 23:35 2012", "0 0 0 */5 Apr,Aug,Oct Mon", "Mon Aug 6 00:00 2012"},
{"Mon Jul 9 23:35 2012", "0 0 0 */5 Oct Mon", "Mon Oct 1 00:00 2012"},
// Wrap around years
{"Mon Jul 9 23:35 2012", "0 0 0 * Feb Mon", "Mon Feb 4 00:00 2013"},
{"Mon Jul 9 23:35 2012", "0 0 0 * Feb Mon/2", "Fri Feb 1 00:00 2013"},
// Wrap around minute, hour, day, month, and year
{"Mon Dec 31 23:59:45 2012", "0 * * * * *", "Tue Jan 1 00:00:00 2013"},
// Leap year
{"Mon Jul 9 23:35 2012", "0 0 0 29 Feb ?", "Mon Feb 29 00:00 2016"},
// Daylight savings time EST -> EDT
{"2012-03-11T00:00:00-0500", "0 30 2 11 Mar ?", "2013-03-11T02:30:00-0400"},
// Daylight savings time EDT -> EST
{"2012-11-04T00:00:00-0400", "0 30 2 04 Nov ?", "2012-11-04T02:30:00-0500"},
{"2012-11-04T01:45:00-0400", "0 30 1 04 Nov ?", "2012-11-04T01:30:00-0500"},
// Unsatisfiable
{"Mon Jul 9 23:35 2012", "0 0 0 30 Feb ?", ""},
{"Mon Jul 9 23:35 2012", "0 0 0 31 Apr ?", ""},
}
for _, c := range runs {
sched, err := Parse(c.spec)
if err != nil {
t.Error(err)
continue
}
actual := sched.Next(getTime(c.time))
expected := getTime(c.expected)
if !actual.Equal(expected) {
t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.spec, expected, actual)
}
}
}
func TestErrors(t *testing.T) {
invalidSpecs := []string{
"xyz",
"60 0 * * *",
"0 60 * * *",
"0 0 * * XYZ",
}
for _, spec := range invalidSpecs {
_, err := Parse(spec)
if err == nil {
t.Error("expected an error parsing: ", spec)
}
}
}
func getTime(value string) time.Time {
if value == "" {
return time.Time{}
}
t, err := time.Parse("Mon Jan 2 15:04 2006", value)
if err != nil {
t, err = time.Parse("Mon Jan 2 15:04:05 2006", value)
if err != nil {
t, err = time.Parse("2006-01-02T15:04:05-0700", value)
if err != nil {
panic(err)
}
// Daylight savings time tests require location
if ny, err := time.LoadLocation("America/New_York"); err == nil {
t = t.In(ny)
}
}
}
return t
}

206
modules/httplib/httplib_test.go

@ -1,206 +0,0 @@
// Copyright 2013 The Beego Authors. All rights reserved.
// Copyright 2014 The Gogs 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 httplib
import (
"io/ioutil"
"os"
"strings"
"testing"
)
func TestResponse(t *testing.T) {
req := Get("http://httpbin.org/get")
resp, err := req.Response()
if err != nil {
t.Fatal(err)
}
t.Log(resp)
}
func TestGet(t *testing.T) {
req := Get("http://httpbin.org/get")
b, err := req.Bytes()
if err != nil {
t.Fatal(err)
}
t.Log(b)
s, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(s)
if string(b) != s {
t.Fatal("request data not match")
}
}
func TestSimplePost(t *testing.T) {
v := "smallfish"
req := Post("http://httpbin.org/post")
req.Param("username", v)
str, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in post")
}
}
// func TestPostFile(t *testing.T) {
// v := "smallfish"
// req := Post("http://httpbin.org/post")
// req.Param("username", v)
// req.PostFile("uploadfile", "httplib_test.go")
// str, err := req.String()
// if err != nil {
// t.Fatal(err)
// }
// t.Log(str)
// n := strings.Index(str, v)
// if n == -1 {
// t.Fatal(v + " not found in post")
// }
// }
func TestSimplePut(t *testing.T) {
str, err := Put("http://httpbin.org/put").String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
}
func TestSimpleDelete(t *testing.T) {
str, err := Delete("http://httpbin.org/delete").String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
}
func TestWithCookie(t *testing.T) {
v := "smallfish"
str, err := Get("http://httpbin.org/cookies/set?k1=" + v).SetEnableCookie(true).String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
str, err = Get("http://httpbin.org/cookies").SetEnableCookie(true).String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in cookie")
}
}
func TestWithBasicAuth(t *testing.T) {
str, err := Get("http://httpbin.org/basic-auth/user/passwd").SetBasicAuth("user", "passwd").String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, "authenticated")
if n == -1 {
t.Fatal("authenticated not found in response")
}
}
func TestWithUserAgent(t *testing.T) {
v := "beego"
str, err := Get("http://httpbin.org/headers").SetUserAgent(v).String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in user-agent")
}
}
func TestWithSetting(t *testing.T) {
v := "beego"
var setting BeegoHttpSettings
setting.EnableCookie = true
setting.UserAgent = v
setting.Transport = nil
SetDefaultSetting(setting)
str, err := Get("http://httpbin.org/get").String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
n := strings.Index(str, v)
if n == -1 {
t.Fatal(v + " not found in user-agent")
}
}
func TestToJson(t *testing.T) {
req := Get("http://httpbin.org/ip")
resp, err := req.Response()
if err != nil {
t.Fatal(err)
}
t.Log(resp)
// httpbin will return http remote addr
type Ip struct {
Origin string `json:"origin"`
}
var ip Ip
err = req.ToJson(&ip)
if err != nil {
t.Fatal(err)
}
t.Log(ip.Origin)
if n := strings.Count(ip.Origin, "."); n != 3 {
t.Fatal("response is not valid ip")
}
}
func TestToFile(t *testing.T) {
f := "beego_testfile"
req := Get("http://httpbin.org/ip")
err := req.ToFile(f)
if err != nil {
t.Fatal(err)
}
defer os.Remove(f)
b, err := ioutil.ReadFile(f)
if n := strings.Index(string(b), "origin"); n == -1 {
t.Fatal(err)
}
}
func TestHeader(t *testing.T) {
req := Get("http://httpbin.org/headers")
req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36")
str, err := req.String()
if err != nil {
t.Fatal(err)
}
t.Log(str)
}

68
modules/log/database.go

@ -1,68 +0,0 @@
// Copyright 2014 The Gogs 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 log
import (
"encoding/json"
"github.com/go-xorm/xorm"
)
type Log struct {
Id int64
Level int
Msg string `xorm:"TEXT"`
}
// DatabaseWriter implements LoggerInterface and is used to log into database.
type DatabaseWriter struct {
Driver string `json:"driver"`
Conn string `json:"conn"`
Level int `json:"level"`
x *xorm.Engine
}
func NewDatabase() LoggerInterface {
return &DatabaseWriter{Level: TRACE}
}
// init database writer with json config.
// config like:
// {
// "driver": "mysql"
// "conn":"root:root@tcp(127.0.0.1:3306)/gogs?charset=utf8",
// "level": 0
// }
// connection string is based on xorm.
func (d *DatabaseWriter) Init(jsonconfig string) (err error) {
if err = json.Unmarshal([]byte(jsonconfig), d); err != nil {
return err
}
d.x, err = xorm.NewEngine(d.Driver, d.Conn)
if err != nil {
return err
}
return d.x.Sync(new(Log))
}
// write message in database writer.
func (d *DatabaseWriter) WriteMsg(msg string, skip, level int) error {
if level < d.Level {
return nil
}
_, err := d.x.Insert(&Log{Level: level, Msg: msg})
return err
}
func (_ *DatabaseWriter) Flush() {
}
func (_ *DatabaseWriter) Destroy() {
}
func init() {
Register("database", NewDatabase)
}

1
modules/log/log.go

@ -37,6 +37,7 @@ func NewLogger(bufLen int64, mode, config string) {
} }
} }
// FIXME: use same log level as other loggers.
func NewGitLogger(logPath string) { func NewGitLogger(logPath string) {
os.MkdirAll(path.Dir(logPath), os.ModePerm) os.MkdirAll(path.Dir(logPath), os.ModePerm)
GitLogger = newLogger(0) GitLogger = newLogger(0)

11
modules/mailer/mail.go

@ -7,12 +7,15 @@ package mailer
import ( import (
"fmt" "fmt"
"path" "path"
"strings"
"gopkg.in/gomail.v2"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
@ -125,7 +128,7 @@ func SendIssueNotifyMail(u, owner *models.User, repo *models.Repository, issue *
subject := fmt.Sprintf("[%s] %s (#%d)", repo.Name, issue.Name, issue.Index) subject := fmt.Sprintf("[%s] %s (#%d)", repo.Name, issue.Name, issue.Index)
content := fmt.Sprintf("%s<br>-<br> <a href=\"%s%s/%s/issues/%d\">View it on Gogs</a>.", content := fmt.Sprintf("%s<br>-<br> <a href=\"%s%s/%s/issues/%d\">View it on Gogs</a>.",
base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name, repo.ComposeMetas()), markdown.RenderSpecialLink([]byte(strings.Replace(issue.Content, "\n", "<br>", -1)), owner.Name+"/"+repo.Name, repo.ComposeMetas()),
setting.AppUrl, owner.Name, repo.Name, issue.Index) setting.AppUrl, owner.Name, repo.Name, issue.Index)
msg := NewMessage(tos, subject, content) msg := NewMessage(tos, subject, content)
msg.Info = fmt.Sprintf("Subject: %s, issue notify", subject) msg.Info = fmt.Sprintf("Subject: %s, issue notify", subject)
@ -148,7 +151,7 @@ func SendIssueMentionMail(r macaron.Render, u, owner *models.User,
data["IssueLink"] = fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issue.Index) data["IssueLink"] = fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issue.Index)
data["Subject"] = subject data["Subject"] = subject
data["ActUserName"] = u.DisplayName() data["ActUserName"] = u.DisplayName()
data["Content"] = string(base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name, repo.ComposeMetas())) data["Content"] = string(markdown.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name, repo.ComposeMetas()))
body, err := r.HTMLString(string(NOTIFY_MENTION), data) body, err := r.HTMLString(string(NOTIFY_MENTION), data)
if err != nil { if err != nil {
@ -181,3 +184,7 @@ func SendCollaboratorMail(r macaron.Render, u, doer *models.User, repo *models.R
SendAsync(msg) SendAsync(msg)
return nil return nil
} }
func SendTestMail(email string) error {
return gomail.Send(&Sender{}, NewMessage([]string{email}, "Gogs Test Email!", "Gogs Test Email!").Message)
}

5
modules/mailer/mailer.go

@ -197,7 +197,10 @@ func processMailQueue() {
var mailQueue chan *Message var mailQueue chan *Message
func NewContext() { func NewContext() {
if setting.MailService == nil { // Need to check if mailQueue is nil because in during reinstall (user had installed
// before but swithed install lock off), this function will be called again
// while mail queue is already processing tasks, and produces a race condition.
if setting.MailService == nil || mailQueue != nil {
return return
} }

195
modules/base/markdown.go → modules/markdown/markdown.go

@ -2,45 +2,51 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package base package markdown
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"net/http"
"path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday" "github.com/russross/blackfriday"
"golang.org/x/net/html" "golang.org/x/net/html"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
func isletter(c byte) bool { var Sanitizer = bluemonday.UGCPolicy()
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
} // BuildSanitizer initializes sanitizer with allowed attributes based on settings.
// This function should only be called once during entire application lifecycle.
func BuildSanitizer() {
// Normal markdown-stuff
Sanitizer.AllowAttrs("class").Matching(regexp.MustCompile(`[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*`)).OnElements("code")
// Checkboxes
Sanitizer.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
Sanitizer.AllowAttrs("checked", "disabled").OnElements("input")
func isalnum(c byte) bool { // Custom URL-Schemes
return (c >= '0' && c <= '9') || isletter(c) Sanitizer.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
} }
var validLinks = [][]byte{[]byte("http://"), []byte("https://"), []byte("ftp://"), []byte("mailto://")} var validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`)
// isLink reports whether link fits valid format.
func isLink(link []byte) bool { func isLink(link []byte) bool {
for _, prefix := range validLinks { return validLinksPattern.Match(link)
if len(link) > len(prefix) && bytes.Equal(bytes.ToLower(link[:len(prefix)]), prefix) && isalnum(link[len(prefix)]) {
return true
}
}
return false
} }
// IsMarkdownFile reports whether name looks like a Markdown file
// based on its extension.
func IsMarkdownFile(name string) bool { func IsMarkdownFile(name string) bool {
name = strings.ToLower(name) name = strings.ToLower(name)
switch filepath.Ext(name) { switch filepath.Ext(name) {
@ -50,57 +56,46 @@ func IsMarkdownFile(name string) bool {
return false return false
} }
func IsTextFile(data []byte) (string, bool) { // IsReadmeFile reports whether name looks like a README file
contentType := http.DetectContentType(data) // based on its extension.
if strings.Index(contentType, "text/") != -1 {
return contentType, true
}
return contentType, false
}
func IsImageFile(data []byte) (string, bool) {
contentType := http.DetectContentType(data)
if strings.Index(contentType, "image/") != -1 {
return contentType, true
}
return contentType, false
}
// IsReadmeFile returns true if given file name suppose to be a README file.
func IsReadmeFile(name string) bool { func IsReadmeFile(name string) bool {
name = strings.ToLower(name) name = strings.ToLower(name)
if len(name) < 6 { if len(name) < 6 {
return false return false
} else if len(name) == 6 { } else if len(name) == 6 {
if name == "readme" { return name == "readme"
return true
}
return false
}
if name[:7] == "readme." {
return true
} }
return false return name[:7] == "readme."
} }
var ( var (
MentionPattern = regexp.MustCompile(`(\s|^)@[0-9a-zA-Z_\.]+`) // MentionPattern matches string that mentions someone, e.g. @Unknwon
commitPattern = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`) MentionPattern = regexp.MustCompile(`(\s|^)@[0-9a-zA-Z_\.]+`)
issueFullPattern = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
issueIndexPattern = regexp.MustCompile(`( |^|\()#[0-9]+\b`) // CommitPattern matches link to certain commit with or without trailing hash,
sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`) // e.g. https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2
CommitPattern = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`)
// IssueFullPattern matches link to an issue with or without trailing hash,
// e.g. https://try.gogs.io/gogs/gogs/issues/4#issue-685
IssueFullPattern = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
// IssueIndexPattern matches string that references to an issue, e.g. #1287
IssueIndexPattern = regexp.MustCompile(`( |^|\()#[0-9]+\b`)
// Sha1CurrentPattern matches string that represents a commit SHA, e.g. d8a994ef243349f321568f9e36d5c3f444b99cae
Sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`)
) )
type CustomRender struct { // Renderer is a extended version of underlying render object.
type Renderer struct {
blackfriday.Renderer blackfriday.Renderer
urlPrefix string urlPrefix string
} }
func (r *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) { // Link defines how formal links should be processed to produce corresponding HTML elements.
func (r *Renderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
if len(link) > 0 && !isLink(link) { if len(link) > 0 && !isLink(link) {
if link[0] == '#' { if link[0] != '#' {
// link = append([]byte(options.urlPrefix), link...)
} else {
link = []byte(path.Join(r.urlPrefix, string(link))) link = []byte(path.Join(r.urlPrefix, string(link)))
} }
} }
@ -108,14 +103,17 @@ func (r *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte, conten
r.Renderer.Link(out, link, title, content) r.Renderer.Link(out, link, title, content)
} }
func (r *CustomRender) AutoLink(out *bytes.Buffer, link []byte, kind int) { // AutoLink defines how auto-detected links should be processed to produce corresponding HTML elements.
if kind != 1 { // Reference for kind: https://github.com/russross/blackfriday/blob/master/markdown.go#L69-L76
func (r *Renderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {
if kind != blackfriday.LINK_TYPE_NORMAL {
r.Renderer.AutoLink(out, link, kind) r.Renderer.AutoLink(out, link, kind)
return return
} }
// This method could only possibly serve one link at a time, no need to find all. // Since this method could only possibly serve one link at a time,
m := commitPattern.Find(link) // we do not need to find all.
m := CommitPattern.Find(link)
if m != nil { if m != nil {
m = bytes.TrimSpace(m) m = bytes.TrimSpace(m)
i := strings.Index(string(m), "commit/") i := strings.Index(string(m), "commit/")
@ -123,11 +121,11 @@ func (r *CustomRender) AutoLink(out *bytes.Buffer, link []byte, kind int) {
if j == -1 { if j == -1 {
j = len(m) j = len(m)
} }
out.WriteString(fmt.Sprintf(` <code><a href="%s">%s</a></code>`, m, ShortSha(string(m[i+7:j])))) out.WriteString(fmt.Sprintf(` <code><a href="%s">%s</a></code>`, m, base.ShortSha(string(m[i+7:j]))))
return return
} }
m = issueFullPattern.Find(link) m = IssueFullPattern.Find(link)
if m != nil { if m != nil {
m = bytes.TrimSpace(m) m = bytes.TrimSpace(m)
i := strings.Index(string(m), "issues/") i := strings.Index(string(m), "issues/")
@ -135,14 +133,16 @@ func (r *CustomRender) AutoLink(out *bytes.Buffer, link []byte, kind int) {
if j == -1 { if j == -1 {
j = len(m) j = len(m)
} }
out.WriteString(fmt.Sprintf(` <a href="%s">#%s</a>`, m, ShortSha(string(m[i+7:j])))) out.WriteString(fmt.Sprintf(` <a href="%s">#%s</a>`, m, base.ShortSha(string(m[i+7:j]))))
return return
} }
r.Renderer.AutoLink(out, link, kind) r.Renderer.AutoLink(out, link, kind)
} }
func (options *CustomRender) ListItem(out *bytes.Buffer, text []byte, flags int) { // ListItem defines how list items should be processed to produce corresponding HTML elements.
func (options *Renderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
// Detect procedures to draw checkboxes.
switch { switch {
case bytes.HasPrefix(text, []byte("[ ] ")): case bytes.HasPrefix(text, []byte("[ ] ")):
text = append([]byte(`<input type="checkbox" disabled="" />`), text[3:]...) text = append([]byte(`<input type="checkbox" disabled="" />`), text[3:]...)
@ -152,16 +152,24 @@ func (options *CustomRender) ListItem(out *bytes.Buffer, text []byte, flags int)
options.Renderer.ListItem(out, text, flags) options.Renderer.ListItem(out, text, flags)
} }
// Note: this section is for purpose of increase performance and
// reduce memory allocation at runtime since they are constant literals.
var ( var (
svgSuffix = []byte(".svg") svgSuffix = []byte(".svg")
svgSuffixWithMark = []byte(".svg?") svgSuffixWithMark = []byte(".svg?")
spaceBytes = []byte(" ")
spaceEncodedBytes = []byte("%20")
space = " "
spaceEncoded = "%20"
) )
func (r *CustomRender) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) { // Image defines how images should be processed to produce corresponding HTML elements.
func (r *Renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
prefix := strings.Replace(r.urlPrefix, "/src/", "/raw/", 1) prefix := strings.Replace(r.urlPrefix, "/src/", "/raw/", 1)
if len(link) > 0 { if len(link) > 0 {
if isLink(link) { if isLink(link) {
// External link with .svg suffix usually means CI status. // External link with .svg suffix usually means CI status.
// TODO: define a keyword to allow non-svg images render as external link.
if bytes.HasSuffix(link, svgSuffix) || bytes.Contains(link, svgSuffixWithMark) { if bytes.HasSuffix(link, svgSuffix) || bytes.Contains(link, svgSuffixWithMark) {
r.Renderer.Image(out, link, title, alt) r.Renderer.Image(out, link, title, alt)
return return
@ -170,7 +178,8 @@ func (r *CustomRender) Image(out *bytes.Buffer, link []byte, title []byte, alt [
if link[0] != '/' { if link[0] != '/' {
prefix += "/" prefix += "/"
} }
link = []byte(prefix + string(link)) link = bytes.Replace([]byte((prefix + string(link))), spaceBytes, spaceEncodedBytes, -1)
fmt.Println(333, string(link))
} }
} }
@ -181,42 +190,55 @@ func (r *CustomRender) Image(out *bytes.Buffer, link []byte, title []byte, alt [
out.WriteString("</a>") out.WriteString("</a>")
} }
// cutoutVerbosePrefix cutouts URL prefix including sub-path to
// return a clean unified string of request URL path.
func cutoutVerbosePrefix(prefix string) string { func cutoutVerbosePrefix(prefix string) string {
count := 0 count := 0
for i := 0; i < len(prefix); i++ { for i := 0; i < len(prefix); i++ {
if prefix[i] == '/' { if prefix[i] == '/' {
count++ count++
} }
if count >= 3 { if count >= 3+setting.AppSubUrlDepth {
return prefix[:i] return prefix[:i]
} }
} }
return prefix return prefix
} }
// RenderIssueIndexPattern renders issue indexes to corresponding links.
func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string, metas map[string]string) []byte { func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
urlPrefix = cutoutVerbosePrefix(urlPrefix) urlPrefix = cutoutVerbosePrefix(urlPrefix)
ms := issueIndexPattern.FindAll(rawBytes, -1) ms := IssueIndexPattern.FindAll(rawBytes, -1)
for _, m := range ms { for _, m := range ms {
var space string var space string
m2 := m if m[0] != '#' {
if m2[0] != '#' { space = string(m[0])
space = string(m2[0]) m = m[1:]
m2 = m2[1:]
} }
if metas == nil { if metas == nil {
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(`%s<a href="%s/issues/%s">%s</a>`, rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(`%s<a href="%s/issues/%s">%s</a>`,
space, urlPrefix, m2[1:], m2)), 1) space, urlPrefix, m[1:], m)), 1)
} else { } else {
// Support for external issue tracker // Support for external issue tracker
metas["index"] = string(m2[1:]) metas["index"] = string(m[1:])
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(`%s<a href="%s">%s</a>`, rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(`%s<a href="%s">%s</a>`,
space, com.Expand(metas["format"], metas), m2)), 1) space, com.Expand(metas["format"], metas), m)), 1)
} }
} }
return rawBytes return rawBytes
} }
// RenderSha1CurrentPattern renders SHA1 strings to corresponding links that assumes in the same repository.
func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte {
ms := Sha1CurrentPattern.FindAll(rawBytes, -1)
for _, m := range ms {
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(
`<a href="%s/commit/%s"><code>%s</code></a>`, urlPrefix, m, base.ShortSha(string(m)))), -1)
}
return rawBytes
}
// RenderSpecialLink renders mentions, indexes and SHA1 strings to corresponding links.
func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]string) []byte { func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
ms := MentionPattern.FindAll(rawBytes, -1) ms := MentionPattern.FindAll(rawBytes, -1)
for _, m := range ms { for _, m := range ms {
@ -230,20 +252,12 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]strin
return rawBytes return rawBytes
} }
func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte { // RenderRaw renders Markdown to HTML without handling special links.
ms := sha1CurrentPattern.FindAll(rawBytes, -1) func RenderRaw(body []byte, urlPrefix string) []byte {
for _, m := range ms {
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(
`<a href="%s/commit/%s"><code>%s</code></a>`, urlPrefix, m, ShortSha(string(m)))), -1)
}
return rawBytes
}
func RenderRawMarkdown(body []byte, urlPrefix string) []byte {
htmlFlags := 0 htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_STYLE htmlFlags |= blackfriday.HTML_SKIP_STYLE
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
renderer := &CustomRender{ renderer := &Renderer{
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""), Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
urlPrefix: urlPrefix, urlPrefix: urlPrefix,
} }
@ -273,9 +287,9 @@ var (
var noEndTags = []string{"img", "input", "br", "hr"} var noEndTags = []string{"img", "input", "br", "hr"}
// PostProcessMarkdown treats different types of HTML differently, // PostProcess treats different types of HTML differently,
// and only renders special links for plain text blocks. // and only renders special links for plain text blocks.
func PostProcessMarkdown(rawHtml []byte, urlPrefix string, metas map[string]string) []byte { func PostProcess(rawHtml []byte, urlPrefix string, metas map[string]string) []byte {
startTags := make([]string, 0, 5) startTags := make([]string, 0, 5)
var buf bytes.Buffer var buf bytes.Buffer
tokenizer := html.NewTokenizer(bytes.NewReader(rawHtml)) tokenizer := html.NewTokenizer(bytes.NewReader(rawHtml))
@ -304,10 +318,10 @@ OUTER_LOOP:
} }
// If this is the close tag to the outer-most, we are done // If this is the close tag to the outer-most, we are done
if token.Type == html.EndTagToken && strings.EqualFold(tagName, token.Data) { if token.Type == html.EndTagToken {
stackNum-- stackNum--
if stackNum == 0 { if stackNum <= 0 && strings.EqualFold(tagName, token.Data) {
break break
} }
} }
@ -343,13 +357,16 @@ OUTER_LOOP:
return rawHtml return rawHtml
} }
func RenderMarkdown(rawBytes []byte, urlPrefix string, metas map[string]string) []byte { // Render renders Markdown to HTML with special links.
result := RenderRawMarkdown(rawBytes, urlPrefix) func Render(rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
result = PostProcessMarkdown(result, urlPrefix, metas) urlPrefix = strings.Replace(urlPrefix, space, spaceEncoded, -1)
result := RenderRaw(rawBytes, urlPrefix)
result = PostProcess(result, urlPrefix, metas)
result = Sanitizer.SanitizeBytes(result) result = Sanitizer.SanitizeBytes(result)
return result return result
} }
func RenderMarkdownString(raw, urlPrefix string, metas map[string]string) string { // RenderString renders Markdown to HTML with special links and returns string type.
return string(RenderMarkdown([]byte(raw), urlPrefix, metas)) func RenderString(raw, urlPrefix string, metas map[string]string) string {
return string(Render([]byte(raw), urlPrefix, metas))
} }

133
modules/middleware/auth.go

@ -1,133 +0,0 @@
// Copyright 2014 The Gogs 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 middleware
import (
"fmt"
"net/url"
"github.com/go-macaron/csrf"
"gopkg.in/macaron.v1"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
type ToggleOptions struct {
SignInRequire bool
SignOutRequire bool
AdminRequire bool
DisableCsrf bool
}
// AutoSignIn reads cookie and try to auto-login.
func AutoSignIn(ctx *Context) (bool, error) {
if !models.HasEngine {
return false, nil
}
uname := ctx.GetCookie(setting.CookieUserName)
if len(uname) == 0 {
return false, nil
}
isSucceed := false
defer func() {
if !isSucceed {
log.Trace("auto-login cookie cleared: %s", uname)
ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubUrl)
ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubUrl)
}
}()
u, err := models.GetUserByName(uname)
if err != nil {
if !models.IsErrUserNotExist(err) {
return false, fmt.Errorf("GetUserByName: %v", err)
}
return false, nil
}
if val, _ := ctx.GetSuperSecureCookie(
base.EncodeMD5(u.Rands+u.Passwd), setting.CookieRememberName); val != u.Name {
return false, nil
}
isSucceed = true
ctx.Session.Set("uid", u.Id)
ctx.Session.Set("uname", u.Name)
return true, nil
}
func Toggle(options *ToggleOptions) macaron.Handler {
return func(ctx *Context) {
// Cannot view any page before installation.
if !setting.InstallLock {
ctx.Redirect(setting.AppSubUrl + "/install")
return
}
// Checking non-logged users landing page.
if !ctx.IsSigned && ctx.Req.RequestURI == "/" && setting.LandingPageUrl != setting.LANDING_PAGE_HOME {
ctx.Redirect(setting.AppSubUrl + string(setting.LandingPageUrl))
return
}
// Redirect to dashboard if user tries to visit any non-login page.
if options.SignOutRequire && ctx.IsSigned && ctx.Req.RequestURI != "/" {
ctx.Redirect(setting.AppSubUrl + "/")
return
}
if !options.SignOutRequire && !options.DisableCsrf && ctx.Req.Method == "POST" && !auth.IsAPIPath(ctx.Req.URL.Path) {
csrf.Validate(ctx.Context, ctx.csrf)
if ctx.Written() {
return
}
}
if options.SignInRequire {
if !ctx.IsSigned {
// Restrict API calls with error message.
if auth.IsAPIPath(ctx.Req.URL.Path) {
ctx.APIError(403, "", "Only signed in user is allowed to call APIs.")
return
}
ctx.SetCookie("redirect_to", url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
ctx.Redirect(setting.AppSubUrl + "/user/login")
return
} else if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm {
ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
ctx.HTML(200, "user/auth/activate")
return
}
}
// Try auto-signin when not signed in.
if !options.SignOutRequire && !ctx.IsSigned && !auth.IsAPIPath(ctx.Req.URL.Path) {
succeed, err := AutoSignIn(ctx)
if err != nil {
ctx.Handle(500, "AutoSignIn", err)
return
} else if succeed {
log.Trace("Auto-login succeed: %s", ctx.Session.Get("uname"))
ctx.Redirect(setting.AppSubUrl + ctx.Req.RequestURI)
return
}
}
if options.AdminRequire {
if !ctx.User.IsAdmin {
ctx.Error(403)
return
}
ctx.Data["PageIsAdmin"] = true
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save