mirror of
https://github.com/gogs/gogs.git
synced 2026-03-01 09:40:55 +01:00
Compare commits
320 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9e0b3b987 | ||
|
|
28766479a7 | ||
|
|
9ac940d31d | ||
|
|
b553ea45ee | ||
|
|
ac4a10456e | ||
|
|
2f3a7e53cb | ||
|
|
42c7bb7529 | ||
|
|
35140f1cc7 | ||
|
|
4f4392b83e | ||
|
|
db6c0ebf76 | ||
|
|
cf7ebfbdc8 | ||
|
|
bc8721fb6c | ||
|
|
01ba771783 | ||
|
|
9ee80e3e54 | ||
|
|
b2c6a6920f | ||
|
|
e321469884 | ||
|
|
b7ebbb4064 | ||
|
|
9a1fe801e5 | ||
|
|
6f71632e3e | ||
|
|
2844674587 | ||
|
|
0daef29053 | ||
|
|
21081836ba | ||
|
|
a2f6e1803b | ||
|
|
39c068400e | ||
|
|
47e7175b80 | ||
|
|
0b785ad967 | ||
|
|
069486d169 | ||
|
|
298ebc58c1 | ||
|
|
3fd41d138c | ||
|
|
35b02997f8 | ||
|
|
adc1ac689e | ||
|
|
528c075ad6 | ||
|
|
e577f2fff3 | ||
|
|
daf96dfae1 | ||
|
|
d4a1d9f82a | ||
|
|
f7f4ea1dcf | ||
|
|
0af3a5b603 | ||
|
|
00cf3e4dab | ||
|
|
cb6be94358 | ||
|
|
54ef1cee1d | ||
|
|
b9999427a8 | ||
|
|
ddeb076890 | ||
|
|
d06d58e461 | ||
|
|
48bb0fadc2 | ||
|
|
cd627ba16e | ||
|
|
d0a80e432d | ||
|
|
9558999698 | ||
|
|
5338808600 | ||
|
|
82da024a4d | ||
|
|
5c866fc737 | ||
|
|
d75013a0e8 | ||
|
|
1591a37ad5 | ||
|
|
3e528f34af | ||
|
|
b6437b5a4c | ||
|
|
db4951bc61 | ||
|
|
d6132aaa88 | ||
|
|
9adc8e9d3f | ||
|
|
be54b4bf26 | ||
|
|
64d90a761b | ||
|
|
4d123d0a93 | ||
|
|
5dd8308686 | ||
|
|
9c2120eb34 | ||
|
|
4f8d888e66 | ||
|
|
ca09a0b516 | ||
|
|
a43164877c | ||
|
|
515641d033 | ||
|
|
54d25c13d7 | ||
|
|
23d53561d1 | ||
|
|
0cce61de3a | ||
|
|
5b96e3fcc7 | ||
|
|
79dae254cf | ||
|
|
5b32cdd960 | ||
|
|
f9ad8d6903 | ||
|
|
1b66600bd0 | ||
|
|
dc53270da9 | ||
|
|
8ea7ba3afa | ||
|
|
ef275ebf62 | ||
|
|
22ab4fa1b0 | ||
|
|
55dfe2c978 | ||
|
|
3c3f7c2a56 | ||
|
|
0c92da7e9c | ||
|
|
8fac41768c | ||
|
|
c418b83678 | ||
|
|
b53f6357fc | ||
|
|
11ca3dedfb | ||
|
|
f77680520f | ||
|
|
2d8c414f8c | ||
|
|
ce8d4cc80b | ||
|
|
37d8d3afe9 | ||
|
|
db0026c507 | ||
|
|
51f6148851 | ||
|
|
c0549a169a | ||
|
|
1e47e2df85 | ||
|
|
5d9ef2bb12 | ||
|
|
d21d7171b0 | ||
|
|
32dcaefafa | ||
|
|
d1a60e3643 | ||
|
|
a0f9197b45 | ||
|
|
340a4595dd | ||
|
|
4c770b87c5 | ||
|
|
47f37e55e9 | ||
|
|
52d66ba6c8 | ||
|
|
437dd5272f | ||
|
|
6bdb8ec4b9 | ||
|
|
9dc3c93a6a | ||
|
|
9e22840483 | ||
|
|
8eb5120fbd | ||
|
|
8c9338a537 | ||
|
|
21b9d5fa1f | ||
|
|
4f360d8f08 | ||
|
|
dfa4b38081 | ||
|
|
e0de6cb5ad | ||
|
|
ff8578082e | ||
|
|
35977cd34c | ||
|
|
abc57b6e43 | ||
|
|
a01b4baca2 | ||
|
|
fc5985f425 | ||
|
|
23eec25274 | ||
|
|
4e7eb5be9d | ||
|
|
b375192352 | ||
|
|
0c5ba4573a | ||
|
|
69a98236bd | ||
|
|
b866dc92d7 | ||
|
|
78f4f59380 | ||
|
|
743b55b9b0 | ||
|
|
0d5d555521 | ||
|
|
80c6cc6321 | ||
|
|
6588ce52fe | ||
|
|
5094e9501c | ||
|
|
e9875edcad | ||
|
|
7e2ea7639e | ||
|
|
5b290013a9 | ||
|
|
2c28ed8c05 | ||
|
|
283c81316c | ||
|
|
beb8a77fc5 | ||
|
|
b9881d1e7b | ||
|
|
87be4623cf | ||
|
|
377530ec21 | ||
|
|
bff1e157d5 | ||
|
|
e84e0ab904 | ||
|
|
c7f56d7483 | ||
|
|
83283bca4c | ||
|
|
f1d8746264 | ||
|
|
baae94b9cf | ||
|
|
3794111460 | ||
|
|
d7ea49b9f5 | ||
|
|
d87a9cb362 | ||
|
|
93b9a2acc0 | ||
|
|
d4a608f64c | ||
|
|
8209bf74f8 | ||
|
|
d88ebd9a4b | ||
|
|
ec8ec58b17 | ||
|
|
a342d58d7e | ||
|
|
d7d167ac63 | ||
|
|
146c8efee3 | ||
|
|
3abc41ccca | ||
|
|
9e3a1bc11a | ||
|
|
cb2da7bf2c | ||
|
|
1aa12c7452 | ||
|
|
fa241efa6d | ||
|
|
ecf3eb4307 | ||
|
|
9ae92459a5 | ||
|
|
5b3f1efd9f | ||
|
|
e0f945959e | ||
|
|
885833892f | ||
|
|
91127d9016 | ||
|
|
1a38f0e0d9 | ||
|
|
fb8beaf19a | ||
|
|
4a8a70dde0 | ||
|
|
8d2a6fc484 | ||
|
|
29ac3980ff | ||
|
|
89bd994c83 | ||
|
|
3e4db7299d | ||
|
|
80dd548b39 | ||
|
|
451f328a4c | ||
|
|
9e4c2afad7 | ||
|
|
2dd4ab65c7 | ||
|
|
b93ae452fe | ||
|
|
c04aea8d9d | ||
|
|
0d7b9065c6 | ||
|
|
452ccff81c | ||
|
|
fb839ca0fb | ||
|
|
b7b7863364 | ||
|
|
3005c4f6db | ||
|
|
963354c5d7 | ||
|
|
17c1bc7383 | ||
|
|
5103f16f78 | ||
|
|
e848b17fab | ||
|
|
3600498c8f | ||
|
|
b2632dec09 | ||
|
|
54930c001d | ||
|
|
41dbb4c148 | ||
|
|
85c9f7c5f3 | ||
|
|
c31606daf9 | ||
|
|
d78abd3561 | ||
|
|
8c71ab5094 | ||
|
|
211dc74816 | ||
|
|
79262173a6 | ||
|
|
7b03b1df0e | ||
|
|
39931f8e00 | ||
|
|
1aa76bd279 | ||
|
|
54c9844d66 | ||
|
|
6705559ce0 | ||
|
|
8e8dfaf227 | ||
|
|
ce7422473a | ||
|
|
fc18741cc7 | ||
|
|
fef09c2de6 | ||
|
|
1e1f9e7166 | ||
|
|
64c68220d2 | ||
|
|
91e5c24a31 | ||
|
|
810ff480b8 | ||
|
|
de65c30770 | ||
|
|
bbf5bcdf99 | ||
|
|
02b53aff0f | ||
|
|
b694cc88e4 | ||
|
|
67c44b7d27 | ||
|
|
263d409326 | ||
|
|
6a79b76531 | ||
|
|
da0fbbadc2 | ||
|
|
1031271224 | ||
|
|
8c70bcee99 | ||
|
|
07c8925805 | ||
|
|
c9e5e38fcf | ||
|
|
29a7d1ce61 | ||
|
|
500b8a2a0f | ||
|
|
ba1270df2d | ||
|
|
ba0feadc34 | ||
|
|
405ee14711 | ||
|
|
1126522a99 | ||
|
|
1601b27ad3 | ||
|
|
f03b6be8f9 | ||
|
|
3ffa17c49a | ||
|
|
98c719c342 | ||
|
|
2a031c1365 | ||
|
|
198567eccb | ||
|
|
f7de6d2b86 | ||
|
|
86eac0842b | ||
|
|
a046a31d2b | ||
|
|
cd084dacf1 | ||
|
|
ac2055e33c | ||
|
|
96f4c9045a | ||
|
|
d95e7065d9 | ||
|
|
5e48c89c5e | ||
|
|
3cfa4a581c | ||
|
|
ce6931a046 | ||
|
|
eb1e6f8e3e | ||
|
|
263fc76b87 | ||
|
|
c1d047d16e | ||
|
|
38f71af363 | ||
|
|
ecdbeea3f5 | ||
|
|
b7b0ee7df9 | ||
|
|
bb05ef907b | ||
|
|
204952439a | ||
|
|
e2e362f2dc | ||
|
|
26a0888bee | ||
|
|
9ce0bd043c | ||
|
|
5e747bc877 | ||
|
|
11af8658cf | ||
|
|
e6e6aaeacb | ||
|
|
e3b78c47e2 | ||
|
|
49193bebd2 | ||
|
|
3598c1435e | ||
|
|
ab59165d2f | ||
|
|
092b59a297 | ||
|
|
7d48f811f1 | ||
|
|
e3a27aeb25 | ||
|
|
dccc50e9d4 | ||
|
|
ad2ab6d214 | ||
|
|
b8368f98ff | ||
|
|
3164354255 | ||
|
|
54724c33ec | ||
|
|
d1911658e1 | ||
|
|
c8b50975bc | ||
|
|
10673417dc | ||
|
|
ad52b2d791 | ||
|
|
09c3c4e70c | ||
|
|
977779cdcf | ||
|
|
71e4689d11 | ||
|
|
f69761563b | ||
|
|
57d48fb6a2 | ||
|
|
7c30ae7002 | ||
|
|
089d934547 | ||
|
|
b0f8b1147c | ||
|
|
25268577a5 | ||
|
|
612fdb98df | ||
|
|
bd55b78775 | ||
|
|
5bbeeb0f1b | ||
|
|
93ee0838eb | ||
|
|
a1109e6fbc | ||
|
|
ebb05475ed | ||
|
|
a11ed51bbb | ||
|
|
e0493259a6 | ||
|
|
b3f0d25ce5 | ||
|
|
d750d53422 | ||
|
|
8d5a4cc9eb | ||
|
|
1476a1a729 | ||
|
|
e3eea745f4 | ||
|
|
60c65415dd | ||
|
|
b92cac7038 | ||
|
|
904f799c1a | ||
|
|
de2a4f8f83 | ||
|
|
135d3733b3 | ||
|
|
3f707b3f32 | ||
|
|
063aacd436 | ||
|
|
196efecaaa | ||
|
|
01e69af2c8 | ||
|
|
25c8d01676 | ||
|
|
7df60af60e | ||
|
|
79ec08141a | ||
|
|
150eef93b2 | ||
|
|
1273b3d3a9 | ||
|
|
b72d7c201a | ||
|
|
4a01bb8fa4 | ||
|
|
d325b23dbb | ||
|
|
976f1486e0 | ||
|
|
cb0ea46d1e | ||
|
|
052ab30409 | ||
|
|
7ba9257a7f | ||
|
|
6a7bd097fe | ||
|
|
0055cbd365 |
@@ -1,5 +1,8 @@
|
||||
[run]
|
||||
init_cmds = [["./gogs", "web"]]
|
||||
init_cmds = [
|
||||
["grep", "-rn", "FIXME", "."],
|
||||
["./gogs", "web"]
|
||||
]
|
||||
watch_all = true
|
||||
watch_dirs = [
|
||||
"$WORKDIR/conf/locale",
|
||||
@@ -11,7 +14,7 @@ watch_dirs = [
|
||||
watch_exts = [".go", ".ini"]
|
||||
build_delay = 1500
|
||||
cmds = [
|
||||
["go", "install"],
|
||||
["go", "build"],
|
||||
["go", "install", "-tags", "sqlite cert"],# redis memcache
|
||||
["go", "build", "-tags", "sqlite cert"],
|
||||
["./gogs", "web"]
|
||||
]
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"paths": ["."],
|
||||
"depth": 2,
|
||||
"exclude": [],
|
||||
"include": ["\\.go$", "\\.ini$"],
|
||||
"command": [
|
||||
"bash", "-c", "go build && ./gogs web"
|
||||
],
|
||||
"env": {
|
||||
"POWERED_BY": "github.com/shxsun/fswatch"
|
||||
}
|
||||
}
|
||||
14
.gitignore
vendored
14
.gitignore
vendored
@@ -8,28 +8,19 @@ data/
|
||||
*.iml
|
||||
public/img/avatar/
|
||||
files/
|
||||
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.exe~
|
||||
/gogs
|
||||
@@ -39,3 +30,8 @@ __pycache__
|
||||
output*
|
||||
config.codekit
|
||||
.brackets.json
|
||||
docker/fig.yml
|
||||
docker/docker/Dockerfile
|
||||
docker/docker/init_gogs.sh
|
||||
gogs.sublime-project
|
||||
gogs.sublime-workspace
|
||||
10
.gobuild.yml
10
.gobuild.yml
@@ -10,3 +10,13 @@ filesets:
|
||||
- README_ZH.md
|
||||
excludes:
|
||||
- \.git
|
||||
depth: 5
|
||||
settings:
|
||||
build: |
|
||||
if test "$GOOS" = "windows" -a "$GOARCH" = "386"
|
||||
then
|
||||
go install -v
|
||||
else
|
||||
go get -v -tags "sqlite redis memcache cert" github.com/gogits/gogs
|
||||
go install -v -tags "sqlite redis memcache cert"
|
||||
fi
|
||||
|
||||
23
.gopmfile
23
.gopmfile
@@ -7,23 +7,26 @@ github.com/beego/redigo = commit:856744a0d5
|
||||
github.com/Unknwon/cae = commit:2e70a1351b
|
||||
github.com/Unknwon/com = commit:2cbcbc6916
|
||||
github.com/Unknwon/goconfig = commit:0f8d8dc1c0
|
||||
github.com/Unknwon/i18n = commit:47baeff8d0
|
||||
github.com/Unknwon/macaron = commit:f22f45d79a
|
||||
github.com/Unknwon/i18n = commit:aec5f77857
|
||||
github.com/Unknwon/macaron = commit:3d307c1232
|
||||
github.com/codegangsta/cli = commit:7381bc4e62
|
||||
github.com/go-sql-driver/mysql = commit:8111ee3ec3
|
||||
github.com/go-xorm/core = commit:750aae0fa5
|
||||
github.com/go-xorm/xorm = commit:2d8b3135b1
|
||||
github.com/gogits/gfm = commit:40f747a9c0
|
||||
github.com/go-xorm/core = commit:3e0fa232ab
|
||||
github.com/go-xorm/xorm = commit:58d33844ce
|
||||
github.com/gogits/go-gogs-client = commit:92e76d616a
|
||||
github.com/gogits/oauth2 = commit:99cbec870a
|
||||
github.com/lib/pq = commit:b021d0ef20
|
||||
github.com/macaron-contrib/cache = commit:204d8e5137
|
||||
github.com/macaron-contrib/captcha = commit:8f3f1ac0e3
|
||||
github.com/macaron-contrib/csrf = commit:cd84c01723
|
||||
github.com/macaron-contrib/i18n = commit:489cc194b5
|
||||
github.com/macaron-contrib/session = commit:80a88a1bba
|
||||
github.com/macaron-contrib/binding = commit:0e23661e7d
|
||||
github.com/macaron-contrib/cache = commit:0bb9e6c9ef
|
||||
github.com/macaron-contrib/captcha = commit:3567dc48b8
|
||||
github.com/macaron-contrib/csrf = commit:422b79675c
|
||||
github.com/macaron-contrib/i18n = commit:184bb832f2
|
||||
github.com/macaron-contrib/oauth2 = commit:8f394c3629
|
||||
github.com/macaron-contrib/session = commit:f00d48fd4f
|
||||
github.com/macaron-contrib/toolbox = commit:57127bcc89
|
||||
github.com/mattn/go-sqlite3 = commit:a80c27ba33
|
||||
github.com/nfnt/resize = commit:581d15cb53
|
||||
github.com/russross/blackfriday = commit:05b8cefd6a
|
||||
github.com/saintfish/chardet = commit:3af4cd4741
|
||||
|
||||
[res]
|
||||
|
||||
@@ -2,9 +2,7 @@ targets:
|
||||
ubuntu-14.04:
|
||||
ubuntu-12.04:
|
||||
debian-7:
|
||||
build_dependencies:
|
||||
- mercurial
|
||||
- bzr
|
||||
centos-6:
|
||||
dependencies:
|
||||
- git
|
||||
before:
|
||||
|
||||
@@ -2,5 +2,4 @@ language: go
|
||||
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- tip
|
||||
- 1.3
|
||||
40
LICENSE
40
LICENSE
@@ -1,27 +1,19 @@
|
||||
Copyright (c) 2014
|
||||
All rights reserved.
|
||||
Copyright (c) 2014 All Gogs Contributors
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the {organization} nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
22
README.md
22
README.md
@@ -1,11 +1,11 @@
|
||||
Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest)
|
||||
Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://travis-ci.org/gogits/gogs)
|
||||
=====================
|
||||
|
||||
Gogs(Go Git Service) is a painless self-hosted Git Service written in Go.
|
||||
|
||||

|
||||
|
||||
##### Current version: 0.5.2 Beta
|
||||
##### Current version: 0.5.8 Beta
|
||||
|
||||
### NOTICES
|
||||
|
||||
@@ -26,25 +26,29 @@ The goal of this project is to make the easiest, fastest and most painless way t
|
||||
- See [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team.
|
||||
- Try it before anything? Do it [online](https://try.gogs.io/Unknown/gogs) or go down to **Installation -> Install from binary** section!
|
||||
- Having troubles? Get help from [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.md).
|
||||
- Want to help on localization? Check out [Crowdin](https://crowdin.com/project/gogs)!
|
||||
|
||||
## Features
|
||||
|
||||
- Activity timeline
|
||||
- SSH/HTTP(S) protocol support
|
||||
- SMTP/LDAP/reverse proxy authentication support
|
||||
- Reverse proxy suburl support
|
||||
- Register/delete/rename account
|
||||
- Create/manage/delete organization with team management
|
||||
- Create/migrate/mirror/delete/watch/rename/transfer public/private repository
|
||||
- Create/fork/migrate/mirror/delete/watch/rename/transfer public/private repository
|
||||
- Repository viewer/release/issue tracker
|
||||
- Repository and Organization level webhooks
|
||||
- Repository Git hooks
|
||||
- Add/remove repository collaborators
|
||||
- Gravatar and cache support
|
||||
- Mail service(register, issue)
|
||||
- Administration panel
|
||||
- Slack webhook integration
|
||||
- Drone CI integration
|
||||
- Supports MySQL, PostgreSQL and SQLite3
|
||||
- Social account login(GitHub, Google, QQ, Weibo)
|
||||
- Multi-language support(English, Chinese, Germany, French etc.)
|
||||
- Multi-language support([7 languages](https://crowdin.com/project/gogs))
|
||||
|
||||
## System Requirements
|
||||
|
||||
@@ -57,7 +61,7 @@ Make sure you install [Prerequirements](http://gogs.io/docs/installation/) first
|
||||
|
||||
There are 5 ways to install Gogs:
|
||||
|
||||
- [Install from binary](http://gogs.io/docs/installation/install_from_binary.md): **STRONGLY RECOMMENDED**
|
||||
- [Install from binary](http://gogs.io/docs/installation/install_from_binary.md)
|
||||
- [Install from source](http://gogs.io/docs/installation/install_from_source.md)
|
||||
- [Install from packages](http://gogs.io/docs/installation/install_from_packages.md)
|
||||
- [Ship with Docker](https://github.com/gogits/gogs/tree/master/docker)
|
||||
@@ -65,16 +69,18 @@ There are 5 ways to install Gogs:
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
- Router and middleware mechanism of [martini](http://martini.codegangsta.io/).
|
||||
- Router and middleware mechanism of [Macaron](https://github.com/Unknwon/macaron).
|
||||
- Mail Service, modules design is inspired by [WeTalk](https://github.com/beego/wetalk).
|
||||
- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
|
||||
- Usage and modification from [beego](http://beego.me) modules.
|
||||
- Thanks [lavachen](http://www.lavachen.cn/) and [Rocker](http://weibo.com/rocker1989) for designing Logo.
|
||||
- Thanks [gobuild.io](http://gobuild.io) for providing binary compile and download service.
|
||||
- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
|
||||
|
||||
## Contributors
|
||||
|
||||
The [core team](http://gogs.io/team) of this project. See [contributors page](https://github.com/gogits/gogs/graphs/contributors) for full list of contributors.
|
||||
- The [core team](http://gogs.io/team) of this project.
|
||||
- See [contributors page](https://github.com/gogits/gogs/graphs/contributors) for full list of contributors.
|
||||
- See [TRANSLATORS](conf/locale/TRANSLATORS) for full list of translators.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
36
README_ZH.md
36
README_ZH.md
@@ -1,11 +1,11 @@
|
||||
Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest)
|
||||
Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://travis-ci.org/gogits/gogs)
|
||||
=====================
|
||||
|
||||
Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。
|
||||
|
||||

|
||||
|
||||
##### 当前版本:0.5.2 Beta
|
||||
##### 当前版本:0.5.8 Beta
|
||||
|
||||
## 开发目的
|
||||
|
||||
@@ -17,25 +17,29 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
- 您可以到 [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。
|
||||
- 想要先睹为快?通过 [在线体验](https://try.gogs.io/Unknown/gogs) 或查看 **安装部署 -> 二进制安装** 小节。
|
||||
- 使用过程中遇到问题?尝试从 [故障排查](http://gogs.io/docs/intro/troubleshooting.md) 页面获取帮助。
|
||||
- 希望帮助多国语言界面的翻译吗?请立即访问 [Crowdin](https://crowdin.com/project/gogs)!
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 活动时间线
|
||||
- 支持 SSH/HTTP(S) 协议
|
||||
- 支持 SMTP/LDAP/反向代理 用户认证
|
||||
- 注册/删除/重命名 用户
|
||||
- 创建/管理/删除 组织以及团队管理功能
|
||||
- 创建/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库
|
||||
- 仓库 浏览/发布/工单管理
|
||||
- 仓库和组织级别 Web 钩子
|
||||
- 添加/删除 仓库协作者
|
||||
- Gravatar 以及缓存支持
|
||||
- 邮件服务(注册、Issue)
|
||||
- 支持反向代理子路径
|
||||
- 支持 注册/删除/重命名 用户
|
||||
- 支持 创建/管理/删除 组织以及团队管理功能
|
||||
- 支持 创建/派生/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库
|
||||
- 支持仓库 浏览/发布/工单管理
|
||||
- 支持仓库和组织级别 Web 钩子
|
||||
- 支持仓库 Git 钩子
|
||||
- 支持 添加/删除 仓库协作者
|
||||
- 支持 Gravatar 以及本地缓存
|
||||
- 支持邮件服务(注册、Issue)
|
||||
- 管理员面板
|
||||
- Slack Web 钩子集成
|
||||
- Drone CI 持续部署集成
|
||||
- 支持 MySQL、PostgreSQL 以及 SQLite3 数据库
|
||||
- 社交帐号登录(GitHub、Google、QQ、微博)
|
||||
- 多语言支持(英文、简体中文、德语、法语等等)
|
||||
- 多语言支持([7 种语言]([more](https://crowdin.com/project/gogs)))
|
||||
|
||||
## 系统要求
|
||||
|
||||
@@ -48,7 +52,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
|
||||
然后,您可以通过以下 5 种方式来安装 Gogs:
|
||||
|
||||
- [二进制安装](http://gogs.io/docs/installation/install_from_binary.md): **强烈推荐**
|
||||
- [二进制安装](http://gogs.io/docs/installation/install_from_binary.md)
|
||||
- [源码安装](http://gogs.io/docs/installation/install_from_source.md)
|
||||
- [包管理安装](http://gogs.io/docs/installation/install_from_packages.md)
|
||||
- [采用 Docker 部署](https://github.com/gogits/gogs/tree/master/docker)
|
||||
@@ -56,16 +60,18 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
|
||||
## 特别鸣谢
|
||||
|
||||
- 基于 [Macaron](https://github.com/Unknwon/macaron) 的路由与中间件机制。
|
||||
- 基于 [WeTalk](https://github.com/beego/wetalk) 修改的邮件服务和模块设计。
|
||||
- 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。
|
||||
- [beego](http://beego.me) 模块的使用与修改。
|
||||
- [martini](http://martini.codegangsta.io/) 的路由与中间件机制。
|
||||
- 感谢 [gobuild.io](http://gobuild.io) 提供二进制编译与下载服务。
|
||||
- 感谢 [lavachen](http://www.lavachen.cn/) 和 [Rocker](http://weibo.com/rocker1989) 设计的 Logo。
|
||||
- 感谢 [Crowdin](https://crowdin.com/project/gogs) 提供免费的开源项目本地化支持。
|
||||
|
||||
## 贡献成员
|
||||
|
||||
本项目的 [开发团队](http://gogs.io/team)。您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。
|
||||
- 本项目的 [开发团队](http://gogs.io/team)。
|
||||
- 您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。
|
||||
- 您可以通过查看 [TRANSLATORS](conf/locale/TRANSLATORS) 文件获取完整的翻译人员列表。
|
||||
|
||||
## 授权许可
|
||||
|
||||
|
||||
160
cmd/cert.go
Normal file
160
cmd/cert.go
Normal file
@@ -0,0 +1,160 @@
|
||||
// +build cert
|
||||
|
||||
// Copyright 2009 The Go 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 cmd
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var CmdCert = cli.Command{
|
||||
Name: "cert",
|
||||
Usage: "Generate self-signed certificate",
|
||||
Description: `Generate a self-signed X.509 certificate for a TLS server.
|
||||
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
|
||||
Action: runCert,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"host", "", "Comma-separated hostnames and IPs to generate a certificate for", ""},
|
||||
cli.StringFlag{"ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521", ""},
|
||||
cli.IntFlag{"rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set", ""},
|
||||
cli.StringFlag{"start-date", "", "Creation date formatted as Jan 1 15:04:05 2011", ""},
|
||||
cli.DurationFlag{"duration", 365 * 24 * time.Hour, "Duration that certificate is valid for", ""},
|
||||
cli.BoolFlag{"ca", "whether this cert should be its own Certificate Authority", ""},
|
||||
},
|
||||
}
|
||||
|
||||
func publicKey(priv interface{}) interface{} {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
case *ecdsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func pemBlockForKey(priv interface{}) *pem.Block {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
|
||||
case *ecdsa.PrivateKey:
|
||||
b, err := x509.MarshalECPrivateKey(k)
|
||||
if err != nil {
|
||||
log.Fatal("unable to marshal ECDSA private key: %v", err)
|
||||
}
|
||||
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func runCert(ctx *cli.Context) {
|
||||
if len(ctx.String("host")) == 0 {
|
||||
log.Fatal("Missing required --host parameter")
|
||||
}
|
||||
|
||||
var priv interface{}
|
||||
var err error
|
||||
switch ctx.String("ecdsa-curve") {
|
||||
case "":
|
||||
priv, err = rsa.GenerateKey(rand.Reader, ctx.Int("rsa-bits"))
|
||||
case "P224":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||
case "P256":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
case "P384":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
case "P521":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
default:
|
||||
log.Fatalf("Unrecognized elliptic curve: %q", ctx.String("ecdsa-curve"))
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate private key: %s", err)
|
||||
}
|
||||
|
||||
var notBefore time.Time
|
||||
if len(ctx.String("start-date")) == 0 {
|
||||
notBefore = time.Now()
|
||||
} else {
|
||||
notBefore, err = time.Parse("Jan 2 15:04:05 2006", ctx.String("start-date"))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse creation date: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
notAfter := notBefore.Add(ctx.Duration("duration"))
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate serial number: %s", err)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Acme Co"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
hosts := strings.Split(ctx.String("host"), ",")
|
||||
for _, h := range hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, h)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.Bool("ca") {
|
||||
template.IsCA = true
|
||||
template.KeyUsage |= x509.KeyUsageCertSign
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create certificate: %s", err)
|
||||
}
|
||||
|
||||
certOut, err := os.Create("cert.pem")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open cert.pem for writing: %s", err)
|
||||
}
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
certOut.Close()
|
||||
log.Println("Written cert.pem")
|
||||
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Fatal("failed to open key.pem for writing: %v", err)
|
||||
}
|
||||
pem.Encode(keyOut, pemBlockForKey(priv))
|
||||
keyOut.Close()
|
||||
log.Println("Written key.pem")
|
||||
}
|
||||
36
cmd/cert_stub.go
Normal file
36
cmd/cert_stub.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// +build !cert
|
||||
|
||||
// Copyright 2009 The Go 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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var CmdCert = cli.Command{
|
||||
Name: "cert",
|
||||
Usage: "Generate self-signed certificate",
|
||||
Description: `Generate a self-signed X.509 certificate for a TLS server.
|
||||
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
|
||||
Action: runCert,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"host", "", "Comma-separated hostnames and IPs to generate a certificate for", ""},
|
||||
cli.StringFlag{"ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521", ""},
|
||||
cli.IntFlag{"rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set", ""},
|
||||
cli.StringFlag{"start-date", "", "Creation date formatted as Jan 1 15:04:05 2011", ""},
|
||||
cli.DurationFlag{"duration", 365 * 24 * time.Hour, "Duration that certificate is valid for", ""},
|
||||
cli.BoolFlag{"ca", "whether this cert should be its own Certificate Authority", ""},
|
||||
},
|
||||
}
|
||||
|
||||
func runCert(ctx *cli.Context) {
|
||||
fmt.Println("Command cert not available, please use build tags 'cert' to rebuild.")
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -58,8 +58,9 @@ func runDump(ctx *cli.Context) {
|
||||
workDir, _ := setting.WorkDir()
|
||||
z.AddFile("gogs-repo.zip", path.Join(workDir, "gogs-repo.zip"))
|
||||
z.AddFile("gogs-db.sql", path.Join(workDir, "gogs-db.sql"))
|
||||
z.AddFile("custom/conf/app.ini", path.Join(workDir, "custom/conf/app.ini"))
|
||||
z.AddDir("custom", path.Join(workDir, "custom"))
|
||||
z.AddDir("log", path.Join(workDir, "log"))
|
||||
// FIXME: SSH key file.
|
||||
if err = z.Close(); err != nil {
|
||||
os.Remove(fileName)
|
||||
log.Fatalf("Fail to save %s: %v", fileName, err)
|
||||
|
||||
11
cmd/fix.go
11
cmd/fix.go
@@ -14,6 +14,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/codegangsta/cli"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
@@ -164,10 +165,12 @@ func runFixLocation(ctx *cli.Context) {
|
||||
|
||||
// Fix in authorized_keys file.
|
||||
sshPath := path.Join(models.SshPath, "authorized_keys")
|
||||
fmt.Printf("Fixing pathes in file: %s\n", sshPath)
|
||||
if err := rewriteAuthorizedKeys(sshPath, oldPath, execPath); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
if com.IsFile(sshPath) {
|
||||
fmt.Printf("Fixing pathes in file: %s\n", sshPath)
|
||||
if err := rewriteAuthorizedKeys(sshPath, oldPath, execPath); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Fix position in gogs-repositories.
|
||||
|
||||
20
cmd/serve.go
20
cmd/serve.go
@@ -96,7 +96,7 @@ func runServ(k *cli.Context) {
|
||||
println("Gogs: you are not the owner of SSH key")
|
||||
log.GitLogger.Fatal(2, "Invalid owner of SSH key: %d", keyId)
|
||||
}
|
||||
println("Gogs: internal error:", err)
|
||||
println("Gogs: internal error:", err.Error())
|
||||
log.GitLogger.Fatal(2, "Fail to get user by key ID(%d): %v", keyId, err)
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ func runServ(k *cli.Context) {
|
||||
println("Gogs: given repository owner are not registered")
|
||||
log.GitLogger.Fatal(2, "Unregistered owner: %s", repoUserName)
|
||||
}
|
||||
println("Gogs: internal error:", err)
|
||||
println("Gogs: internal error:", err.Error())
|
||||
log.GitLogger.Fatal(2, "Fail to get repository owner(%s): %v", repoUserName, err)
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func runServ(k *cli.Context) {
|
||||
case isWrite:
|
||||
has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.WRITABLE)
|
||||
if err != nil {
|
||||
println("Gogs: internal error:", err)
|
||||
println("Gogs: internal error:", err.Error())
|
||||
log.GitLogger.Fatal(2, "Fail to check write access:", err)
|
||||
} else if !has {
|
||||
println("You have no right to write this repository")
|
||||
@@ -147,7 +147,7 @@ func runServ(k *cli.Context) {
|
||||
println("Gogs: given repository does not exist")
|
||||
log.GitLogger.Fatal(2, "Repository does not exist: %s/%s", repoUser.Name, repoName)
|
||||
}
|
||||
println("Gogs: internal error:", err)
|
||||
println("Gogs: internal error:", err.Error())
|
||||
log.GitLogger.Fatal(2, "Fail to get repository: %v", err)
|
||||
}
|
||||
|
||||
@@ -157,21 +157,27 @@ func runServ(k *cli.Context) {
|
||||
|
||||
has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.READABLE)
|
||||
if err != nil {
|
||||
println("Gogs: internal error:", err)
|
||||
println("Gogs: internal error:", err.Error())
|
||||
log.GitLogger.Fatal(2, "Fail to check read access:", err)
|
||||
} else if !has {
|
||||
println("You have no right to access this repository")
|
||||
log.GitLogger.Fatal(2, "User %s has no right to read repository %s", user.Name, repoPath)
|
||||
}
|
||||
default:
|
||||
println("Unknown command")
|
||||
println("Unknown command: " + cmd)
|
||||
return
|
||||
}
|
||||
|
||||
uuid := uuid.NewV4().String()
|
||||
os.Setenv("uuid", uuid)
|
||||
|
||||
gitcmd := exec.Command(verb, repoPath)
|
||||
var gitcmd *exec.Cmd
|
||||
verbs := strings.Split(verb, " ")
|
||||
if len(verbs) == 2 {
|
||||
gitcmd = exec.Command(verbs[0], verbs[1], repoPath)
|
||||
} else {
|
||||
gitcmd = exec.Command(verb, repoPath)
|
||||
}
|
||||
gitcmd.Dir = setting.RepoRootPath
|
||||
gitcmd.Stdout = os.Stdout
|
||||
gitcmd.Stdin = os.Stdin
|
||||
|
||||
436
cmd/web.go
436
cmd/web.go
@@ -5,30 +5,37 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/macaron-contrib/binding"
|
||||
"github.com/macaron-contrib/cache"
|
||||
"github.com/macaron-contrib/captcha"
|
||||
"github.com/macaron-contrib/csrf"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
"github.com/macaron-contrib/oauth2"
|
||||
"github.com/macaron-contrib/session"
|
||||
"github.com/macaron-contrib/toolbox"
|
||||
|
||||
api "github.com/gogits/go-gogs-client"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/auth"
|
||||
"github.com/gogits/gogs/modules/auth/apiv1"
|
||||
"github.com/gogits/gogs/modules/avatar"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/git"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/middleware"
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/routers"
|
||||
"github.com/gogits/gogs/routers/admin"
|
||||
@@ -50,6 +57,7 @@ and it takes care of all the other things for you`,
|
||||
|
||||
// checkVersion checks if binary matches the version of templates files.
|
||||
func checkVersion() {
|
||||
// Templates.
|
||||
data, err := ioutil.ReadFile(path.Join(setting.StaticRootPath, "templates/.VERSION"))
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to read 'templates/.VERSION': %v", err)
|
||||
@@ -57,6 +65,20 @@ func checkVersion() {
|
||||
if string(data) != setting.AppVer {
|
||||
log.Fatal(4, "Binary and template file version does not match, did you forget to recompile?")
|
||||
}
|
||||
|
||||
// Check dependency version.
|
||||
macaronVer := git.MustParseVersion(strings.Join(strings.Split(macaron.Version(), ".")[:3], "."))
|
||||
if macaronVer.LessThan(git.MustParseVersion("0.4.7")) {
|
||||
log.Fatal(4, "Package macaron version is too old, did you forget to update?(github.com/Unknwon/macaron)")
|
||||
}
|
||||
i18nVer := git.MustParseVersion(i18n.Version())
|
||||
if i18nVer.LessThan(git.MustParseVersion("0.0.2")) {
|
||||
log.Fatal(4, "Package i18n version is too old, did you forget to update?(github.com/macaron-contrib/i18n)")
|
||||
}
|
||||
sessionVer := git.MustParseVersion(session.Version())
|
||||
if sessionVer.LessThan(git.MustParseVersion("0.0.5")) {
|
||||
log.Fatal(4, "Package session version is too old, did you forget to update?(github.com/macaron-contrib/session)")
|
||||
}
|
||||
}
|
||||
|
||||
// newMacaron initializes Macaron instance.
|
||||
@@ -64,39 +86,55 @@ func newMacaron() *macaron.Macaron {
|
||||
m := macaron.New()
|
||||
m.Use(macaron.Logger())
|
||||
m.Use(macaron.Recovery())
|
||||
if setting.EnableGzip {
|
||||
m.Use(macaron.Gziper())
|
||||
}
|
||||
if setting.Protocol == setting.FCGI {
|
||||
m.SetURLPrefix(setting.AppSubUrl)
|
||||
}
|
||||
m.Use(macaron.Static(
|
||||
path.Join(setting.StaticRootPath, "public"),
|
||||
macaron.StaticOptions{
|
||||
SkipLogging: !setting.DisableRouterLog,
|
||||
},
|
||||
))
|
||||
// if setting.EnableGzip {
|
||||
// m.Use(macaron.Gzip())
|
||||
// }
|
||||
m.Use(macaron.Static(
|
||||
setting.AvatarUploadPath,
|
||||
macaron.StaticOptions{
|
||||
Prefix: "avatars",
|
||||
SkipLogging: !setting.DisableRouterLog,
|
||||
},
|
||||
))
|
||||
m.Use(macaron.Renderer(macaron.RenderOptions{
|
||||
Directory: path.Join(setting.StaticRootPath, "templates"),
|
||||
Funcs: []template.FuncMap{base.TemplateFuncs},
|
||||
IndentJSON: macaron.Env != macaron.PROD,
|
||||
}))
|
||||
m.Use(i18n.I18n(i18n.Options{
|
||||
Langs: setting.Langs,
|
||||
Names: setting.Names,
|
||||
Redirect: true,
|
||||
SubURL: setting.AppSubUrl,
|
||||
Directory: path.Join(setting.ConfRootPath, "locale"),
|
||||
CustomDirectory: path.Join(setting.CustomPath, "conf/locale"),
|
||||
Langs: setting.Langs,
|
||||
Names: setting.Names,
|
||||
Redirect: true,
|
||||
}))
|
||||
m.Use(cache.Cacher(cache.Options{
|
||||
Adapter: setting.CacheAdapter,
|
||||
Interval: setting.CacheInternal,
|
||||
Conn: setting.CacheConn,
|
||||
}))
|
||||
m.Use(captcha.Captchaer())
|
||||
m.Use(captcha.Captchaer(captcha.Options{
|
||||
SubURL: setting.AppSubUrl,
|
||||
}))
|
||||
m.Use(session.Sessioner(session.Options{
|
||||
Provider: setting.SessionProvider,
|
||||
Config: *setting.SessionConfig,
|
||||
}))
|
||||
m.Use(csrf.Generate(csrf.Options{
|
||||
Secret: setting.SecretKey,
|
||||
SetCookie: true,
|
||||
Header: "X-Csrf-Token",
|
||||
Secret: setting.SecretKey,
|
||||
SetCookie: true,
|
||||
Header: "X-Csrf-Token",
|
||||
CookiePath: setting.AppSubUrl,
|
||||
}))
|
||||
m.Use(toolbox.Toolboxer(m, toolbox.Options{
|
||||
HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{
|
||||
@@ -106,6 +144,13 @@ func newMacaron() *macaron.Macaron {
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
// OAuth 2.
|
||||
if setting.OauthService != nil {
|
||||
for _, info := range setting.OauthService.OauthInfos {
|
||||
m.Use(oauth2.NewOAuth2Provider(info.Options, info.AuthUrl, info.TokenUrl))
|
||||
}
|
||||
}
|
||||
m.Use(middleware.Contexter())
|
||||
return m
|
||||
}
|
||||
@@ -121,73 +166,92 @@ func runWeb(*cli.Context) {
|
||||
ignSignInAndCsrf := middleware.Toggle(&middleware.ToggleOptions{DisableCsrf: true})
|
||||
reqSignOut := middleware.Toggle(&middleware.ToggleOptions{SignOutRequire: true})
|
||||
|
||||
bind := binding.Bind
|
||||
bindIgnErr := binding.BindIgnErr
|
||||
|
||||
// Routers.
|
||||
m.Get("/", ignSignIn, routers.Home)
|
||||
m.Get("/explore", ignSignIn, routers.Explore)
|
||||
// FIXME: when i'm binding form here???
|
||||
m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install)
|
||||
m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost)
|
||||
m.Group("", func(r *macaron.Router) {
|
||||
r.Get("/pulls", user.Pulls)
|
||||
r.Get("/issues", user.Issues)
|
||||
m.Group("", func() {
|
||||
m.Get("/pulls", user.Pulls)
|
||||
m.Get("/issues", user.Issues)
|
||||
}, reqSignIn)
|
||||
|
||||
// API routers.
|
||||
m.Group("/api", func(_ *macaron.Router) {
|
||||
m.Group("/v1", func(r *macaron.Router) {
|
||||
// API.
|
||||
// FIXME: custom form error response.
|
||||
m.Group("/api", func() {
|
||||
m.Group("/v1", func() {
|
||||
// Miscellaneous.
|
||||
r.Post("/markdown", bindIgnErr(apiv1.MarkdownForm{}), v1.Markdown)
|
||||
r.Post("/markdown/raw", v1.MarkdownRaw)
|
||||
m.Post("/markdown", bindIgnErr(apiv1.MarkdownForm{}), v1.Markdown)
|
||||
m.Post("/markdown/raw", v1.MarkdownRaw)
|
||||
|
||||
// Users.
|
||||
m.Group("/users", func(r *macaron.Router) {
|
||||
r.Get("/search", v1.SearchUsers)
|
||||
m.Group("/users", func() {
|
||||
m.Get("/search", v1.SearchUsers)
|
||||
|
||||
m.Group("/:username", func() {
|
||||
m.Get("", v1.GetUserInfo)
|
||||
|
||||
m.Group("/tokens", func() {
|
||||
m.Combo("").Get(v1.ListAccessTokens).Post(bind(v1.CreateAccessTokenForm{}), v1.CreateAccessToken)
|
||||
}, middleware.ApiReqBasicAuth())
|
||||
})
|
||||
})
|
||||
|
||||
// Repositories.
|
||||
m.Group("/repos", func(r *macaron.Router) {
|
||||
r.Get("/search", v1.SearchRepos)
|
||||
r.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), v1.Migrate)
|
||||
m.Combo("/user/repos", middleware.ApiReqToken()).Get(v1.ListMyRepos).Post(bind(api.CreateRepoOption{}), v1.CreateRepo)
|
||||
m.Post("/org/:org/repos", middleware.ApiReqToken(), bind(api.CreateRepoOption{}), v1.CreateOrgRepo)
|
||||
m.Group("/repos", func() {
|
||||
m.Get("/search", v1.SearchRepos)
|
||||
m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), v1.MigrateRepo)
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Combo("/hooks").Get(v1.ListRepoHooks).Post(bind(api.CreateHookOption{}), v1.CreateRepoHook)
|
||||
m.Patch("/hooks/:id:int", bind(api.EditHookOption{}), v1.EditRepoHook)
|
||||
m.Get("/raw/*", middleware.RepoRef(), v1.GetRepoRawFile)
|
||||
}, middleware.ApiRepoAssignment(), middleware.ApiReqToken())
|
||||
})
|
||||
|
||||
r.Any("/*", func(ctx *middleware.Context) {
|
||||
ctx.JSON(404, &base.ApiJsonErr{"Not Found", v1.DOC_URL})
|
||||
m.Any("/*", func(ctx *middleware.Context) {
|
||||
ctx.JSON(404, &base.ApiJsonErr{"Not Found", base.DOC_URL})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// User routers.
|
||||
m.Group("/user", func(r *macaron.Router) {
|
||||
r.Get("/login", user.SignIn)
|
||||
r.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost)
|
||||
r.Get("/login/:name", user.SocialSignIn)
|
||||
r.Get("/sign_up", user.SignUp)
|
||||
r.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
|
||||
r.Get("/reset_password", user.ResetPasswd)
|
||||
r.Post("/reset_password", user.ResetPasswdPost)
|
||||
// User.
|
||||
m.Group("/user", func() {
|
||||
m.Get("/login", user.SignIn)
|
||||
m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost)
|
||||
m.Get("/info/:name", user.SocialSignIn)
|
||||
m.Get("/sign_up", user.SignUp)
|
||||
m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
|
||||
m.Get("/reset_password", user.ResetPasswd)
|
||||
m.Post("/reset_password", user.ResetPasswdPost)
|
||||
}, reqSignOut)
|
||||
m.Group("/user/settings", func(r *macaron.Router) {
|
||||
r.Get("", user.Settings)
|
||||
r.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost)
|
||||
r.Get("/password", user.SettingsPassword)
|
||||
r.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost)
|
||||
r.Get("/ssh", user.SettingsSSHKeys)
|
||||
r.Post("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost)
|
||||
r.Get("/social", user.SettingsSocial)
|
||||
r.Route("/delete", "GET,POST", user.SettingsDelete)
|
||||
m.Group("/user/settings", func() {
|
||||
m.Get("", user.Settings)
|
||||
m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost)
|
||||
m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), user.SettingsAvatar)
|
||||
m.Get("/password", user.SettingsPassword)
|
||||
m.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost)
|
||||
m.Get("/ssh", user.SettingsSSHKeys)
|
||||
m.Post("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost)
|
||||
m.Get("/social", user.SettingsSocial)
|
||||
m.Combo("/applications").Get(user.SettingsApplications).Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost)
|
||||
m.Route("/delete", "GET,POST", user.SettingsDelete)
|
||||
}, reqSignIn)
|
||||
m.Group("/user", func(r *macaron.Router) {
|
||||
m.Group("/user", func() {
|
||||
// r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds)
|
||||
r.Any("/activate", user.Activate)
|
||||
r.Get("/email2user", user.Email2User)
|
||||
r.Get("/forget_password", user.ForgotPasswd)
|
||||
r.Post("/forget_password", user.ForgotPasswdPost)
|
||||
r.Get("/logout", user.SignOut)
|
||||
m.Any("/activate", user.Activate)
|
||||
m.Get("/email2user", user.Email2User)
|
||||
m.Get("/forget_password", user.ForgotPasswd)
|
||||
m.Post("/forget_password", user.ForgotPasswdPost)
|
||||
m.Get("/logout", user.SignOut)
|
||||
})
|
||||
|
||||
m.Get("/user/:username", ignSignIn, user.Profile) // TODO: Legacy
|
||||
|
||||
// Gravatar service.
|
||||
avt := avatar.CacheServer("public/img/avatar/", "public/img/avatar_default.jpg")
|
||||
os.MkdirAll("public/img/avatar/", os.ModePerm)
|
||||
@@ -195,35 +259,40 @@ func runWeb(*cli.Context) {
|
||||
|
||||
adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true})
|
||||
|
||||
m.Group("/admin", func(r *macaron.Router) {
|
||||
m.Group("/admin", func() {
|
||||
m.Get("", adminReq, admin.Dashboard)
|
||||
r.Get("/config", admin.Config)
|
||||
r.Get("/monitor", admin.Monitor)
|
||||
m.Get("/config", admin.Config)
|
||||
m.Get("/monitor", admin.Monitor)
|
||||
|
||||
m.Group("/users", func(r *macaron.Router) {
|
||||
r.Get("", admin.Users)
|
||||
r.Get("/new", admin.NewUser)
|
||||
r.Post("/new", bindIgnErr(auth.RegisterForm{}), admin.NewUserPost)
|
||||
r.Get("/:userid", admin.EditUser)
|
||||
r.Post("/:userid", bindIgnErr(auth.AdminEditUserForm{}), admin.EditUserPost)
|
||||
r.Post("/:userid/delete", admin.DeleteUser)
|
||||
m.Group("/users", func() {
|
||||
m.Get("", admin.Users)
|
||||
m.Get("/new", admin.NewUser)
|
||||
m.Post("/new", bindIgnErr(auth.RegisterForm{}), admin.NewUserPost)
|
||||
m.Get("/:userid", admin.EditUser)
|
||||
m.Post("/:userid", bindIgnErr(auth.AdminEditUserForm{}), admin.EditUserPost)
|
||||
m.Post("/:userid/delete", admin.DeleteUser)
|
||||
})
|
||||
|
||||
m.Group("/orgs", func(r *macaron.Router) {
|
||||
r.Get("", admin.Organizations)
|
||||
m.Group("/orgs", func() {
|
||||
m.Get("", admin.Organizations)
|
||||
})
|
||||
|
||||
m.Group("/repos", func(r *macaron.Router) {
|
||||
r.Get("", admin.Repositories)
|
||||
m.Group("/repos", func() {
|
||||
m.Get("", admin.Repositories)
|
||||
})
|
||||
|
||||
m.Group("/auths", func(r *macaron.Router) {
|
||||
r.Get("", admin.Authentications)
|
||||
r.Get("/new", admin.NewAuthSource)
|
||||
r.Post("/new", bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost)
|
||||
r.Get("/:authid", admin.EditAuthSource)
|
||||
r.Post("/:authid", bindIgnErr(auth.AuthenticationForm{}), admin.EditAuthSourcePost)
|
||||
r.Post("/:authid/delete", admin.DeleteAuthSource)
|
||||
m.Group("/auths", func() {
|
||||
m.Get("", admin.Authentications)
|
||||
m.Get("/new", admin.NewAuthSource)
|
||||
m.Post("/new", bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost)
|
||||
m.Get("/:authid", admin.EditAuthSource)
|
||||
m.Post("/:authid", bindIgnErr(auth.AuthenticationForm{}), admin.EditAuthSourcePost)
|
||||
m.Post("/:authid/delete", admin.DeleteAuthSource)
|
||||
})
|
||||
|
||||
m.Group("/notices", func() {
|
||||
m.Get("", admin.Notices)
|
||||
m.Get("/:id:int/delete", admin.DeleteNotice)
|
||||
})
|
||||
}, adminReq)
|
||||
|
||||
@@ -235,129 +304,147 @@ func runWeb(*cli.Context) {
|
||||
|
||||
reqTrueOwner := middleware.RequireTrueOwner()
|
||||
|
||||
// Organization routers.
|
||||
m.Group("/org", func(r *macaron.Router) {
|
||||
r.Get("/create", org.Create)
|
||||
r.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost)
|
||||
// Organization.
|
||||
m.Group("/org", func() {
|
||||
m.Get("/create", org.Create)
|
||||
m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost)
|
||||
|
||||
m.Group("/:org", func(r *macaron.Router) {
|
||||
r.Get("/dashboard", user.Dashboard)
|
||||
r.Get("/members", org.Members)
|
||||
r.Get("/members/action/:action", org.MembersAction)
|
||||
m.Group("/:org", func() {
|
||||
m.Get("/dashboard", user.Dashboard)
|
||||
m.Get("/members", org.Members)
|
||||
m.Get("/members/action/:action", org.MembersAction)
|
||||
|
||||
r.Get("/teams", org.Teams)
|
||||
r.Get("/teams/:team", org.TeamMembers)
|
||||
r.Get("/teams/:team/repositories", org.TeamRepositories)
|
||||
r.Get("/teams/:team/action/:action", org.TeamsAction)
|
||||
r.Get("/teams/:team/action/repo/:action", org.TeamsRepoAction)
|
||||
m.Get("/teams", org.Teams)
|
||||
m.Get("/teams/:team", org.TeamMembers)
|
||||
m.Get("/teams/:team/repositories", org.TeamRepositories)
|
||||
m.Get("/teams/:team/action/:action", org.TeamsAction)
|
||||
m.Get("/teams/:team/action/repo/:action", org.TeamsRepoAction)
|
||||
}, middleware.OrgAssignment(true, true))
|
||||
|
||||
m.Group("/:org", func(r *macaron.Router) {
|
||||
r.Get("/teams/new", org.NewTeam)
|
||||
r.Post("/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost)
|
||||
r.Get("/teams/:team/edit", org.EditTeam)
|
||||
r.Post("/teams/:team/edit", bindIgnErr(auth.CreateTeamForm{}), org.EditTeamPost)
|
||||
r.Post("/teams/:team/delete", org.DeleteTeam)
|
||||
m.Group("/:org", func() {
|
||||
m.Get("/teams/new", org.NewTeam)
|
||||
m.Post("/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost)
|
||||
m.Get("/teams/:team/edit", org.EditTeam)
|
||||
m.Post("/teams/:team/edit", bindIgnErr(auth.CreateTeamForm{}), org.EditTeamPost)
|
||||
m.Post("/teams/:team/delete", org.DeleteTeam)
|
||||
|
||||
m.Group("/settings", func(r *macaron.Router) {
|
||||
r.Get("", org.Settings)
|
||||
r.Post("", bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost)
|
||||
r.Get("/hooks", org.SettingsHooks)
|
||||
r.Get("/hooks/new", repo.WebHooksNew)
|
||||
r.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
|
||||
r.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
||||
r.Get("/hooks/:id", repo.WebHooksEdit)
|
||||
r.Post("/hooks/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
|
||||
r.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
|
||||
r.Route("/delete", "GET,POST", org.SettingsDelete)
|
||||
m.Group("/settings", func() {
|
||||
m.Get("", org.Settings)
|
||||
m.Post("", bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost)
|
||||
m.Get("/hooks", org.SettingsHooks)
|
||||
m.Get("/hooks/new", repo.WebHooksNew)
|
||||
m.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
|
||||
m.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
||||
m.Get("/hooks/:id", repo.WebHooksEdit)
|
||||
m.Post("/hooks/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
|
||||
m.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
|
||||
m.Route("/delete", "GET,POST", org.SettingsDelete)
|
||||
})
|
||||
|
||||
r.Route("/invitations/new", "GET,POST", org.Invitation)
|
||||
m.Route("/invitations/new", "GET,POST", org.Invitation)
|
||||
}, middleware.OrgAssignment(true, true, true))
|
||||
}, reqSignIn)
|
||||
m.Group("/org", func(r *macaron.Router) {
|
||||
r.Get("/:org", org.Home)
|
||||
m.Group("/org", func() {
|
||||
m.Get("/:org", org.Home)
|
||||
}, middleware.OrgAssignment(true))
|
||||
|
||||
// Repository routers.
|
||||
m.Group("/repo", func(r *macaron.Router) {
|
||||
r.Get("/create", repo.Create)
|
||||
r.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost)
|
||||
r.Get("/migrate", repo.Migrate)
|
||||
r.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), repo.MigratePost)
|
||||
// Repository.
|
||||
m.Group("/repo", func() {
|
||||
m.Get("/create", repo.Create)
|
||||
m.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost)
|
||||
m.Get("/migrate", repo.Migrate)
|
||||
m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), repo.MigratePost)
|
||||
m.Get("/fork", repo.Fork)
|
||||
m.Post("/fork", bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost)
|
||||
}, reqSignIn)
|
||||
|
||||
m.Group("/:username/:reponame", func(r *macaron.Router) {
|
||||
r.Get("/settings", repo.Settings)
|
||||
r.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
|
||||
m.Group("/settings", func(r *macaron.Router) {
|
||||
r.Route("/collaboration", "GET,POST", repo.SettingsCollaboration)
|
||||
r.Get("/hooks", repo.Webhooks)
|
||||
r.Get("/hooks/new", repo.WebHooksNew)
|
||||
r.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
|
||||
r.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
||||
r.Get("/hooks/:id", repo.WebHooksEdit)
|
||||
r.Post("/hooks/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
|
||||
r.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Get("/settings", repo.Settings)
|
||||
m.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
|
||||
m.Group("/settings", func() {
|
||||
m.Route("/collaboration", "GET,POST", repo.SettingsCollaboration)
|
||||
m.Get("/hooks", repo.Webhooks)
|
||||
m.Get("/hooks/new", repo.WebHooksNew)
|
||||
m.Post("/hooks/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
|
||||
m.Post("/hooks/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
||||
m.Get("/hooks/:id", repo.WebHooksEdit)
|
||||
m.Post("/hooks/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
|
||||
m.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
|
||||
|
||||
m.Group("/hooks/git", func() {
|
||||
m.Get("", repo.GitHooks)
|
||||
m.Get("/:name", repo.GitHooksEdit)
|
||||
m.Post("/:name", repo.GitHooksEditPost)
|
||||
}, middleware.GitHookService())
|
||||
})
|
||||
}, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner)
|
||||
|
||||
m.Group("/:username/:reponame", func(r *macaron.Router) {
|
||||
r.Get("/action/:action", repo.Action)
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Get("/action/:action", repo.Action)
|
||||
|
||||
m.Group("/issues", func(r *macaron.Router) {
|
||||
r.Get("/new", repo.CreateIssue)
|
||||
r.Post("/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost)
|
||||
r.Post("/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue)
|
||||
r.Post("/:index/label", repo.UpdateIssueLabel)
|
||||
r.Post("/:index/milestone", repo.UpdateIssueMilestone)
|
||||
r.Post("/:index/assignee", repo.UpdateAssignee)
|
||||
r.Get("/:index/attachment/:id", repo.IssueGetAttachment)
|
||||
r.Post("/labels/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
|
||||
r.Post("/labels/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
|
||||
r.Post("/labels/delete", repo.DeleteLabel)
|
||||
r.Get("/milestones", repo.Milestones)
|
||||
r.Get("/milestones/new", repo.NewMilestone)
|
||||
r.Post("/milestones/new", bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
|
||||
r.Get("/milestones/:index/edit", repo.UpdateMilestone)
|
||||
r.Post("/milestones/:index/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.UpdateMilestonePost)
|
||||
r.Get("/milestones/:index/:action", repo.UpdateMilestone)
|
||||
m.Group("/issues", func() {
|
||||
m.Get("/new", repo.CreateIssue)
|
||||
m.Post("/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost)
|
||||
m.Post("/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue)
|
||||
m.Post("/:index/label", repo.UpdateIssueLabel)
|
||||
m.Post("/:index/milestone", repo.UpdateIssueMilestone)
|
||||
m.Post("/:index/assignee", repo.UpdateAssignee)
|
||||
m.Get("/:index/attachment/:id", repo.IssueGetAttachment)
|
||||
m.Post("/labels/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
|
||||
m.Post("/labels/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
|
||||
m.Post("/labels/delete", repo.DeleteLabel)
|
||||
m.Get("/milestones/new", repo.NewMilestone)
|
||||
m.Post("/milestones/new", bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
|
||||
m.Get("/milestones/:index/edit", repo.UpdateMilestone)
|
||||
m.Post("/milestones/:index/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.UpdateMilestonePost)
|
||||
m.Get("/milestones/:index/:action", repo.UpdateMilestone)
|
||||
})
|
||||
|
||||
r.Post("/comment/:action", repo.Comment)
|
||||
r.Get("/releases/new", repo.NewRelease)
|
||||
r.Get("/releases/edit/:tagname", repo.EditRelease)
|
||||
m.Post("/comment/:action", repo.Comment)
|
||||
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/new", repo.NewRelease)
|
||||
m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
|
||||
m.Get("/edit/:tagname", repo.EditRelease)
|
||||
m.Post("/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
|
||||
}, middleware.RepoRef())
|
||||
}, reqSignIn, middleware.RepoAssignment(true))
|
||||
|
||||
m.Group("/:username/:reponame", func(r *macaron.Router) {
|
||||
r.Post("/releases/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
|
||||
r.Post("/releases/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
|
||||
}, reqSignIn, middleware.RepoAssignment(true, true))
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Get("/releases", middleware.RepoRef(), repo.Releases)
|
||||
m.Get("/issues", repo.Issues)
|
||||
m.Get("/issues/:index", repo.ViewIssue)
|
||||
m.Get("/issues/milestones", repo.Milestones)
|
||||
m.Get("/pulls", repo.Pulls)
|
||||
m.Get("/branches", repo.Branches)
|
||||
m.Get("/archive/*", repo.Download)
|
||||
m.Get("/issues2/", repo.Issues2)
|
||||
m.Get("/pulls2/", repo.PullRequest2)
|
||||
m.Get("/labels2/", repo.Labels2)
|
||||
m.Get("/milestone2/", repo.Milestones2)
|
||||
|
||||
m.Group("/:username/:reponame", func(r *macaron.Router) {
|
||||
r.Get("/issues", repo.Issues)
|
||||
r.Get("/issues/:index", repo.ViewIssue)
|
||||
r.Get("/pulls", repo.Pulls)
|
||||
r.Get("/branches", repo.Branches)
|
||||
m.Group("", func() {
|
||||
m.Get("/src/*", repo.Home)
|
||||
m.Get("/raw/*", repo.SingleDownload)
|
||||
m.Get("/commits/*", repo.RefCommits)
|
||||
m.Get("/commit/*", repo.Diff)
|
||||
}, middleware.RepoRef())
|
||||
|
||||
m.Get("/compare/:before([a-z0-9]+)...:after([a-z0-9]+)", repo.CompareDiff)
|
||||
}, ignSignIn, middleware.RepoAssignment(true))
|
||||
|
||||
m.Group("/:username/:reponame", func(r *macaron.Router) {
|
||||
r.Get("/src/:branchname", repo.Home)
|
||||
r.Get("/src/:branchname/*", repo.Home)
|
||||
r.Get("/raw/:branchname/*", repo.SingleDownload)
|
||||
r.Get("/commits/:branchname", repo.Commits)
|
||||
r.Get("/commits/:branchname/search", repo.SearchCommits)
|
||||
r.Get("/commits/:branchname/*", repo.FileHistory)
|
||||
r.Get("/commit/:branchname", repo.Diff)
|
||||
r.Get("/commit/:branchname/*", repo.Diff)
|
||||
r.Get("/releases", repo.Releases)
|
||||
r.Get("/archive/*.*", repo.Download)
|
||||
r.Get("/compare/:before([a-z0-9]+)...:after([a-z0-9]+)", repo.CompareDiff)
|
||||
}, ignSignIn, middleware.RepoAssignment(true, true))
|
||||
m.Group("/:username", func() {
|
||||
m.Get("/:reponame", ignSignIn, middleware.RepoAssignment(true, true), middleware.RepoRef(), repo.Home)
|
||||
m.Any("/:reponame/*", ignSignInAndCsrf, repo.Http)
|
||||
})
|
||||
|
||||
m.Group("/:username", func(r *macaron.Router) {
|
||||
r.Get("/:reponame", ignSignIn, middleware.RepoAssignment(true, true, true), repo.Home)
|
||||
r.Any("/:reponame/*", ignSignInAndCsrf, repo.Http)
|
||||
// robots.txt
|
||||
m.Get("/robots.txt", func(ctx *middleware.Context) {
|
||||
if setting.HasRobotsTxt {
|
||||
ctx.ServeFile(path.Join(setting.CustomPath, "robots.txt"))
|
||||
} else {
|
||||
ctx.Error(404)
|
||||
}
|
||||
})
|
||||
|
||||
// Not found handler.
|
||||
@@ -365,12 +452,15 @@ func runWeb(*cli.Context) {
|
||||
|
||||
var err error
|
||||
listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
|
||||
log.Info("Listen: %v://%s", setting.Protocol, listenAddr)
|
||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl)
|
||||
switch setting.Protocol {
|
||||
case setting.HTTP:
|
||||
err = http.ListenAndServe(listenAddr, m)
|
||||
case setting.HTTPS:
|
||||
err = http.ListenAndServeTLS(listenAddr, setting.CertFile, setting.KeyFile, m)
|
||||
server := &http.Server{Addr: listenAddr, TLSConfig: &tls.Config{MinVersion: tls.VersionTLS10}, Handler: m}
|
||||
err = server.ListenAndServeTLS(setting.CertFile, setting.KeyFile)
|
||||
case setting.FCGI:
|
||||
err = fcgi.Serve(nil, m)
|
||||
default:
|
||||
log.Fatal(4, "Invalid protocol: %s", setting.Protocol)
|
||||
}
|
||||
|
||||
38
conf/app.ini
38
conf/app.ini
@@ -21,14 +21,21 @@ OFFLINE_MODE = false
|
||||
DISABLE_ROUTER_LOG = false
|
||||
; Generate steps:
|
||||
; $ cd path/to/gogs/custom/https
|
||||
; $ go run $GOROOT/src/pkg/crypto/tls/generate_cert.go -ca=true -duration=8760h0m0s -host=myhost.example.com
|
||||
; $ ./gogs cert -ca=true -duration=8760h0m0s -host=myhost.example.com
|
||||
;
|
||||
; Or from a .pfx file exported from the Windows certificate store (do
|
||||
; not forget to export the private key):
|
||||
; $ openssl pkcs12 -in cert.pfx -out cert.pem -nokeys
|
||||
; $ openssl pkcs12 -in cert.pfx -out key.pem -nocerts -nodes
|
||||
CERT_FILE = custom/https/cert.pem
|
||||
KEY_FILE = custom/https/key.pem
|
||||
; Upper level of template and static file path
|
||||
; default is the path where Gogs is executed
|
||||
STATIC_ROOT_PATH =
|
||||
; Application level GZIP support
|
||||
#ENABLE_GZIP = false
|
||||
ENABLE_GZIP = false
|
||||
; Landing page for non-logged users, can be "home" or "explore"
|
||||
LANDING_PAGE = home
|
||||
|
||||
[database]
|
||||
; Either "mysql", "postgres" or "sqlite3", it's your choice
|
||||
@@ -70,6 +77,7 @@ ENABLE_CACHE_AVATAR = false
|
||||
ENABLE_NOTIFY_MAIL = false
|
||||
; More detail: https://github.com/gogits/gogs/issues/165
|
||||
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
|
||||
ENABLE_REVERSE_PROXY_AUTO_REGISTERATION = false
|
||||
|
||||
[webhook]
|
||||
; Cron task interval in minutes
|
||||
@@ -118,13 +126,10 @@ TOKEN_URL = https://accounts.google.com/o/oauth2/token
|
||||
ENABLED = false
|
||||
CLIENT_ID =
|
||||
CLIENT_SECRET =
|
||||
SCOPES = all
|
||||
SCOPES = get_user_info
|
||||
; QQ 互联
|
||||
; AUTH_URL = https://graph.qq.com/oauth2.0/authorize
|
||||
; TOKEN_URL = https://graph.qq.com/oauth2.0/token
|
||||
; Tencent weibo
|
||||
AUTH_URL = https://open.t.qq.com/cgi-bin/oauth2/authorize
|
||||
TOKEN_URL = https://open.t.qq.com/cgi-bin/oauth2/access_token
|
||||
AUTH_URL = https://graph.qq.com/oauth2.0/authorize
|
||||
TOKEN_URL = https://graph.qq.com/oauth2.0/token
|
||||
|
||||
[oauth.weibo]
|
||||
ENABLED = false
|
||||
@@ -163,14 +168,13 @@ ENABLE_SET_COOKIE = true
|
||||
GC_INTERVAL_TIME = 86400
|
||||
; Session life time, default is 86400
|
||||
SESSION_LIFE_TIME = 86400
|
||||
; session id hash func, Either "sha1", "sha256" or "md5" default is sha1
|
||||
SESSION_ID_HASHFUNC = sha1
|
||||
; Session hash key, default is use random string
|
||||
SESSION_ID_HASHKEY =
|
||||
|
||||
[picture]
|
||||
; The place to picture data, either "server" or "qiniu", default is "server"
|
||||
SERVICE = server
|
||||
AVATAR_UPLOAD_PATH = data/avatars
|
||||
; Chinese users can choose "duoshuo"
|
||||
GRAVATAR_SOURCE = gravatar
|
||||
DISABLE_GRAVATAR = false
|
||||
|
||||
[attachment]
|
||||
@@ -254,7 +258,13 @@ CONN =
|
||||
|
||||
[git]
|
||||
MAX_GITDIFF_LINES = 10000
|
||||
; Arguments for command 'git fsck', e.g.: "--unreachable --tags"
|
||||
; see more on http://git-scm.com/docs/git-fsck/1.7.5
|
||||
FSCK_ARGS =
|
||||
; Arguments for command 'git gc', e.g.: "--aggressive --auto"
|
||||
; see more on http://git-scm.com/docs/git-gc/1.7.5
|
||||
GC_ARGS =
|
||||
|
||||
[i18n]
|
||||
LANGS = en-US,zh-CN,de-DE,fr-CA
|
||||
NAMES = English,简体中文,Deutsch,Français
|
||||
LANGS = en-US,zh-CN,zh-HK,de-DE,fr-CA,nl-NL,lv-LV
|
||||
NAMES = English,简体中文,繁體中文,Deutsch,Français,Nederlands,Latviešu
|
||||
|
||||
6
conf/locale/TRANSLATORS
Normal file
6
conf/locale/TRANSLATORS
Normal file
@@ -0,0 +1,6 @@
|
||||
# This file lists all PUBLIC individuals having contributed content to the translation.
|
||||
# Order of name is meaningless.
|
||||
|
||||
Thomas Fanninger <gogs.thomas@fanninger.at>
|
||||
Łukasz Jan Niemier <lukasz@niemier.pl>
|
||||
Lafriks <lafriks@gmail.com>
|
||||
1235
conf/locale/locale_de-DE.ini
Normal file → Executable file
1235
conf/locale/locale_de-DE.ini
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@ organization = Organization
|
||||
mirror = Mirror
|
||||
new_repo = New Repository
|
||||
new_migrate = New Migration
|
||||
new_fork = New Fork Repository
|
||||
new_org = New Organization
|
||||
manage_org = Manage Organizations
|
||||
admin_panel = Admin Panel
|
||||
@@ -41,7 +42,7 @@ cancel = Cancel
|
||||
[install]
|
||||
install = Installation
|
||||
title = Install Steps For First-time Run
|
||||
requite_db_desc = Gogs requires MySQL, PostgreSQL or SQLite3, but SQLite3 is usually available in the official binary version.
|
||||
requite_db_desc = Gogs requires MySQL, PostgreSQL or SQLite3.
|
||||
db_type = Database Type
|
||||
host = Host
|
||||
user = User
|
||||
@@ -74,7 +75,7 @@ confirm_password = Confirm Password
|
||||
admin_email = E-mail
|
||||
install_gogs = Install Gogs
|
||||
test_git_failed = Fail to test 'git' command: %v
|
||||
sqlite3_not_available = Your release version does not support SQLite3, please download the official binary version from http://gogs.io/docs/installation/install_from_binary.html, NOT the gobuild version.
|
||||
sqlite3_not_available = Your release version does not support SQLite3, please download the official binary version from %s, NOT the gobuild version.
|
||||
invalid_db_setting = Database setting is not correct: %v
|
||||
invalid_repo_path = Repository root path is invalid: %v
|
||||
run_user_not_match = Run user isn't the current user: %s -> %s
|
||||
@@ -159,18 +160,33 @@ user_not_exist = Given user does not exist.
|
||||
last_org_owner = The user to remove is the last member in owner team. There must be another owner.
|
||||
|
||||
invalid_ssh_key = Sorry, we're not able to verify your SSH key: %s
|
||||
unable_verify_ssh_key = Gogs cannot verify your SSH key, but we assume that is valid, please make sure yourself.
|
||||
auth_failed = Authentication failed: %v
|
||||
|
||||
still_own_repo = Your account still have ownership of repository, you have to delete or transfer them first.
|
||||
still_has_org = Your account still have membership of organization, you have to left or delete them first.
|
||||
org_still_own_repo = This organization still have ownership of repository, you have to delete or transfer them first.
|
||||
|
||||
still_own_user = This authentication still has used by some users, you should move them and then delete again.
|
||||
|
||||
target_branch_not_exist = Target branch does not exist
|
||||
|
||||
[user]
|
||||
change_avatar = Change your avatar at gravatar.com
|
||||
change_custom_avatar = Change your avatar in settings
|
||||
join_on = Joined on
|
||||
repositories = Repositories
|
||||
activity = Public Activity
|
||||
followers = Followers
|
||||
starred = Starred
|
||||
following = Following
|
||||
|
||||
[settings]
|
||||
profile = Profile
|
||||
password = Password
|
||||
ssh_keys = SSH Keys
|
||||
social = Social Accounts
|
||||
applications = Applications
|
||||
orgs = Organizations
|
||||
delete = Delete Account
|
||||
uid = Uid
|
||||
@@ -181,7 +197,19 @@ full_name = Full Name
|
||||
website = Website
|
||||
location = Location
|
||||
update_profile = Update Profile
|
||||
update_profile_success = Your profile has been successfully updated.
|
||||
update_profile_success = Your profile has been updated successfully.
|
||||
change_username = Username Changed
|
||||
change_username_desc = Username has been changed, do you want to continue? This will affect all links relate to your account.
|
||||
continue = Continue
|
||||
cancel = Cancel
|
||||
|
||||
enable_custom_avatar = Enable Custom Avatar
|
||||
enable_custom_avatar_helper = Enable this to disable fetch from Gravatar
|
||||
choose_new_avatar = Choose new avatar
|
||||
update_avatar = Update Avatar Setting
|
||||
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.
|
||||
|
||||
change_password = Change Password
|
||||
old_password = Current Password
|
||||
@@ -192,7 +220,7 @@ change_password_success = Password is changed successfully. You can now sign in
|
||||
manage_ssh_keys = Manage SSH Keys
|
||||
add_key = Add Key
|
||||
ssh_desc = This is a list of SSH keys associated with your account. Remove any keys that you do not recognize.
|
||||
ssh_helper = <strong>Need help?</strong> Check out our guide to <a href="https://help.github.com/articles/generating-ssh-keys">generating SSH keys</a> or troubleshoot <a href="https://help.github.com/ssh-issues/">common SSH Problems</a>.
|
||||
ssh_helper = <strong>Need help?</strong> Check out our guide to <a href="%s">generating SSH keys</a> or troubleshoot <a href="%s">common SSH Problems</a>.
|
||||
add_new_key = Add SSH Key
|
||||
key_name = Key Name
|
||||
key_content = Content
|
||||
@@ -207,9 +235,21 @@ social_desc = This is a list of associated social accounts. Remove any binding t
|
||||
unbind = Unbind
|
||||
unbind_success = Social account has been unbound.
|
||||
|
||||
manage_access_token = Manage Personal Access Tokens
|
||||
generate_new_token = Generate New Token
|
||||
tokens_desc = Tokens you have generated that can be used to access the Gogs API.
|
||||
new_token_desc = As for now, every token will have full access to your account.
|
||||
token_name = Token Name
|
||||
generate_token = Generate Token
|
||||
generate_token_succees = New access token has been generated successfully! Make sure to copy your new personal access token now. You won't be able to see it again!
|
||||
delete_token = Delete
|
||||
delete_token_success = Personal access token has been deleted successfully! Don't forget to update your applications as well.
|
||||
|
||||
delete_account = Delete Your Account
|
||||
delete_prompt = The operation will delete your account permanently, and <strong>CANNOT</strong> be undone!
|
||||
confirm_delete_account = Confirm Deletion
|
||||
delete_account_title = Account Deletion
|
||||
delete_account_desc = This account is going to be deleted permanently, do you want to continue?
|
||||
|
||||
[repo]
|
||||
owner = Owner
|
||||
@@ -217,6 +257,9 @@ repo_name = Repository Name
|
||||
repo_name_helper = Great repository names are short, memorable and <strong>unique</strong>.
|
||||
visibility = Visibility
|
||||
visiblity_helper = This repository is <span class="label label-red label-radius">Private</span>
|
||||
fork_repo = Fork Repository
|
||||
fork_from = Fork From
|
||||
fork_visiblity_helper = Forked repository cannot change its visiblity
|
||||
repo_desc = Description
|
||||
repo_lang = Language
|
||||
repo_lang_helper = Select a .gitignore file
|
||||
@@ -235,44 +278,78 @@ migrate_type_helper = This repository will be a <span class="label label-blue la
|
||||
migrate_repo = Migrate Repository
|
||||
|
||||
copy_link = Copy
|
||||
clone_helper = Need help cloning? Visit <a target="_blank" href="http://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository">Help</a>!
|
||||
click_to_copy = Copy to clipboard
|
||||
copied = Copied OK
|
||||
clone_helper = Need help cloning? Visit <a target="_blank" href="%s">Help</a>!
|
||||
unwatch = Unwatch
|
||||
watch = Watch
|
||||
unstar = Unstar
|
||||
star = Star
|
||||
fork = Fork
|
||||
|
||||
no_desc = No Description
|
||||
quick_guide = Quick Guide
|
||||
clone_this_repo = Clone this repository
|
||||
create_new_repo_command = Create a new repository on the command line
|
||||
push_exist_repo = Push an existing repository from the command line
|
||||
|
||||
branch = Branch
|
||||
tree = Tree
|
||||
branch_and_tags = Branches & Tags
|
||||
branches = Branches
|
||||
tags = Tags
|
||||
issues = Issues
|
||||
commits = Commits
|
||||
releases = Releases
|
||||
file_raw = Raw
|
||||
file_history = History
|
||||
file_view_raw = View Raw
|
||||
|
||||
commits.commits = Commits
|
||||
commits.search = Search commits
|
||||
commits.find = Find
|
||||
commits.author = Author
|
||||
commits.message = Message
|
||||
commits.date = Date
|
||||
commits.older = Older
|
||||
commits.newer = Newer
|
||||
|
||||
settings = Settings
|
||||
settings.options = Options
|
||||
settings.collaboration = Collaboration
|
||||
settings.hooks = Webhooks
|
||||
settings.githooks = Git Hooks
|
||||
settings.deploy_keys = Deploy Keys
|
||||
settings.basic_settings = Basic Settings
|
||||
settings.danger_zone = Danger Zone
|
||||
settings.site = Official Site
|
||||
settings.update_settings = Update Settings
|
||||
settings.change_reponame = Repository Name Changed
|
||||
settings.change_reponame_desc = Repository name has been changed, do you want to continue? This will affect all links relate to this repository.
|
||||
settings.transfer = Transfer Ownership
|
||||
settings.transfer_desc = Transfer this repo to another user or to an organization where you have admin rights.
|
||||
settings.new_owner_has_same_repo = New owner already has a repository with same name.
|
||||
settings.delete = Delete This Repository
|
||||
settings.delete_desc = Once you delete a repository, there is no going back. Please be certain.
|
||||
settings.update_settings_success = Repository options has been successfully updated.
|
||||
settings.transfer_notices = <p>- You will lose access if new owner is a individual user.</p><p>- You will remain access if new owner is an organization and you're one of the owners.</p>
|
||||
settings.update_settings_success = Repository options has been updated successfully.
|
||||
settings.transfer_owner = New Owner
|
||||
settings.make_transfer = Make Transfer
|
||||
settings.transfer_succeed = Repository ownership has been successfully transferred.
|
||||
settings.transfer_succeed = Repository ownership has been transferred successfully.
|
||||
settings.confirm_delete = Confirm Deletion
|
||||
settings.add_collaborator = Add New Collaborator
|
||||
settings.add_collaborator_success = New collaborator has been added.
|
||||
settings.remove_collaborator_success = Collaborator has been removed.
|
||||
settings.user_is_org_member = User is organization member who cannot be added as a collaborator.
|
||||
settings.add_webhook = Add Webhook
|
||||
settings.hooks_desc = Webhooks allow external services to be notified when certain events happen on Gogs. When the specified events happen, we'll send a POST request to each of the URLs you provide. Learn more in our <a target="_blank" href="http://gogs.io/docs/features/webhook.html">Webhooks Guide</a>.
|
||||
settings.hooks_desc = Webhooks allow external services to be notified when certain events happen on Gogs. When the specified events happen, we'll send a POST request to each of the URLs you provide. Learn more in our <a target="_blank" href="%s">Webhooks Guide</a>.
|
||||
settings.githooks_desc = Git Hooks are powered by Git itself, you can edit files of supported hooks in the list below to apply custom operations.
|
||||
settings.githook_edit_desc = If hook is not active, sample content will be presented. Leave content to be blank will disable this hook.
|
||||
settings.githook_name = Hook Name
|
||||
settings.githook_content = Hook Content
|
||||
settings.update_githook = Update Hook
|
||||
settings.remove_hook_success = Webhook has been removed.
|
||||
settings.add_webhook_desc = We’ll send a <code>POST</code> request to the URL below with details of any subscribed events. You can also specify which data format you'd like to receive (JSON, <code>x-www-form-urlencoded</code>, <em>etc</em>). More information can be found in <a target="_blank" href="http://gogs.io/docs/features/webhook.html">Webhooks Guide</a>.
|
||||
settings.add_webhook_desc = We’ll send a <code>POST</code> request to the URL below with details of any subscribed events. You can also specify which data format you'd like to receive (JSON, <code>x-www-form-urlencoded</code>, <em>etc</em>). More information can be found in <a target="_blank" href="%s">Webhooks Guide</a>.
|
||||
settings.payload_url = Payload URL
|
||||
settings.content_type = Content Type
|
||||
settings.secret = Secret
|
||||
@@ -286,11 +363,44 @@ settings.update_hook_success = Webhook has been updated.
|
||||
settings.delete_webhook = Delete Webhook
|
||||
settings.recent_deliveries = Recent Deliveries
|
||||
settings.hook_type = Hook Type
|
||||
settings.add_slack_hook_desc = Add <a href="http://slack.com">Slack</a> integration to your repository.
|
||||
settings.add_slack_hook_desc = Add <a href="%s">Slack</a> integration to your repository.
|
||||
settings.slack_token = Token
|
||||
settings.slack_domain = Domain
|
||||
settings.slack_channel = Channel
|
||||
|
||||
diff.browse_source = Browse Source
|
||||
diff.parent = parent
|
||||
diff.commit = commit
|
||||
diff.data_not_available = Diff Data Not Available.
|
||||
diff.show_diff_stats = Show Diff Stats
|
||||
diff.stats_desc = <strong> %d changed files</strong> with <strong>%d additions</strong> and <strong>%d deletions</strong>
|
||||
diff.bin = BIN
|
||||
diff.view_file = View File
|
||||
|
||||
release.releases = Releases
|
||||
release.new_release = New Release
|
||||
release.draft = Draft
|
||||
release.prerelease = Pre-Release
|
||||
release.stable = Stable
|
||||
release.edit = edit
|
||||
release.ahead = <strong>%d</strong> commits to %s since this release
|
||||
release.source_code = Source Code
|
||||
release.tag_name = Tag name
|
||||
release.target = Target
|
||||
release.tag_helper = Choose an existing tag, or create a new tag on publish.
|
||||
release.release_title = Release title
|
||||
release.content_with_md = Content with <a href="%s">Markdown</a>
|
||||
release.write = Write
|
||||
release.preview = Preview
|
||||
release.content_placeholder = Write some content
|
||||
release.loading = Loading...
|
||||
release.prerelease_desc = This is a pre-release
|
||||
release.prerelease_helper = We’ll point out that this release is identified as non-production ready.
|
||||
release.publish = Publish Release
|
||||
release.save_draft = Save Draft
|
||||
release.edit_release = Edit Release
|
||||
release.tag_name_already_exist = Release with this tag name has already existed.
|
||||
|
||||
[org]
|
||||
org_name_holder = Organization Name
|
||||
org_name_helper = Great organization names are short and memorable.
|
||||
@@ -316,11 +426,15 @@ settings.full_name = Full Name
|
||||
settings.website = Website
|
||||
settings.location = Location
|
||||
settings.update_settings = Update Settings
|
||||
settings.update_setting_success = Organization setting has been successfully updated.
|
||||
settings.change_orgname = Organization Name Changed
|
||||
settings.change_orgname_desc = Organization name has been changed, do you want to continue? This will affect all links relate to this organization.
|
||||
settings.update_setting_success = Organization setting has been updated successfully.
|
||||
settings.delete = Delete Organization
|
||||
settings.delete_account = Delete This Organization
|
||||
settings.delete_prompt = The operation will delete this organization permanently, and <strong>CANNOT</strong> be undone!
|
||||
settings.confirm_delete_account = Confirm Deletion
|
||||
settings.delete_org_title = Organization Deletion
|
||||
settings.delete_org_desc = This organization is going to be deleted permanently, do you want to continue?
|
||||
settings.hooks_desc = Add webhooks that will be triggered for <strong>all repositories</strong> under this organization.
|
||||
|
||||
members.public = Public
|
||||
@@ -350,13 +464,16 @@ teams.members = Team Members
|
||||
teams.update_settings = Update Settings
|
||||
teams.delete_team = Delete This Team
|
||||
teams.add_team_member = Add Team Member
|
||||
teams.delete_team_success = Given team has been successfully deleted.
|
||||
teams.delete_team_title = Team Deletion
|
||||
teams.delete_team_desc = This team is going to be deleted, do you want to continue? Members of this team may lose access to some repositories.
|
||||
teams.delete_team_success = Given team has been deleted successfully.
|
||||
teams.read_permission_desc = This team grants <strong>Read</strong> access: members can view and clone the team's repositories.
|
||||
teams.write_permission_desc = This team grants <strong>Write</strong> access: members can read from and push to the team's repositories.
|
||||
teams.admin_permission_desc = This team grants <strong>Admin</strong> access: members can read from, push to, and add collaborators to the team's repositories.
|
||||
teams.repositories = Team Repositories
|
||||
teams.add_team_repository = Add Team Repository
|
||||
teams.remove_repo = Remove
|
||||
teams.add_nonexistent_repo = The repository you're trying to add does not exist, please create it first.
|
||||
|
||||
[admin]
|
||||
dashboard = Dashboard
|
||||
@@ -365,6 +482,7 @@ organizations = Organizations
|
||||
repositories = Repositories
|
||||
authentication = Authentications
|
||||
config = Configuration
|
||||
notices = System Notices
|
||||
monitor = Monitoring
|
||||
prev = Prev.
|
||||
next = Next
|
||||
@@ -377,7 +495,13 @@ dashboard.operation_name = Operation Name
|
||||
dashboard.operation_switch = Switch
|
||||
dashboard.operation_run = Run
|
||||
dashboard.clean_unbind_oauth = Clean unbound OAuthes
|
||||
dashboard.clean_unbind_oauth_success = All unbind OAuthes have been deleted successfully.
|
||||
dashboard.delete_inactivate_accounts = Delete all inactive accounts
|
||||
dashboard.delete_inactivate_accounts_success = All inactivate accounts have been deleted successfully.
|
||||
dashboard.delete_repo_archives = Delete all repositories archives
|
||||
dashboard.delete_repo_archives_success = All repositories archives have been deleted successfully.
|
||||
dashboard.git_gc_repos = Do garbage collection on repositories
|
||||
dashboard.git_gc_repos_success = All repositories have done garbage collection successfully.
|
||||
dashboard.server_uptime = Server Uptime
|
||||
dashboard.current_goroutine = Current Goroutines
|
||||
dashboard.current_memory_usage = Current Memory Usage
|
||||
@@ -419,13 +543,15 @@ users.edit = Edit
|
||||
users.auth_source = Authorization Source
|
||||
users.local = Local
|
||||
users.auth_login_name = Authorization Login Name
|
||||
users.update_profile_success = Account profile has been successfully updated.
|
||||
users.update_profile_success = Account profile has been updated successfully.
|
||||
users.edit_account = Edit Account
|
||||
users.is_activated = This account is activated
|
||||
users.is_admin = This account has administrator permissions
|
||||
users.allow_git_hook = This account has permissions to create Git hooks
|
||||
users.update_profile = Update Account Profile
|
||||
users.delete_account = Delete This Account
|
||||
users.still_own_repo = This account still have ownership of repository, you have to delete or transfer them first.
|
||||
users.still_has_org = This account still have membership of organization, you have to left or delete them first.
|
||||
|
||||
orgs.org_manage_panel = Organization Manage Panel
|
||||
orgs.name = Name
|
||||
@@ -463,9 +589,11 @@ auths.enable_auto_register = Enable Auto Registration
|
||||
auths.tips = Tips
|
||||
auths.edit = Edit Authorization Setting
|
||||
auths.activated = This authentication has activated
|
||||
auths.update_success = Authorization setting has been successfully updated.
|
||||
auths.update_success = Authorization setting has been updated successfully.
|
||||
auths.update = Update Authorization Setting
|
||||
auths.delete = Delete This Authorization
|
||||
auths.delete_auth_title = Authorization Deletion
|
||||
auths.delete_auth_desc = This authorization is going to be deleted, do you want to continue?
|
||||
|
||||
config.server_config = Server Configuration
|
||||
config.app_name = Application Name
|
||||
@@ -521,8 +649,6 @@ config.gc_interval_time = GC Interval Time
|
||||
config.session_life_time = Session Life Time
|
||||
config.https_only = HTTPS Only
|
||||
config.cookie_life_time = Cookie Life Time
|
||||
config.session_hash_function = Session ID Hash Function
|
||||
config.session_hash_key = Session ID Hash Key
|
||||
config.picture_config = Picture Configuration
|
||||
config.picture_service = Picture Service
|
||||
config.disable_gravatar = Disable Gravatar
|
||||
@@ -540,11 +666,21 @@ monitor.desc = Description
|
||||
monitor.start = Start Time
|
||||
monitor.execute_time = Execution Time
|
||||
|
||||
notices.system_notice_list = System Notices
|
||||
notices.type = Type
|
||||
notices.type_1 = Repository
|
||||
notices.desc = Description
|
||||
notices.op = Op.
|
||||
notices.delete_success = System notice has been deleted successfully.
|
||||
|
||||
[action]
|
||||
create_repo = created repository <a href="/%s">%s</a>
|
||||
commit_repo = pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a>
|
||||
create_issue = opened issue <a href="/%s/issues/%s">%s#%s</a>
|
||||
comment_issue = commented on issue <a href="/%s/issues/%s">%s#%s</a>
|
||||
create_repo = created repository <a href="%s/%s">%s</a>
|
||||
commit_repo = pushed to <a href="%s/%s/src/%s">%s</a> at <a href="%s/%s">%s</a>
|
||||
create_issue = opened issue <a href="%s/%s/issues/%s">%s#%s</a>
|
||||
comment_issue = commented on issue <a href="%s/%s/issues/%s">%s#%s</a>
|
||||
transfer_repo = transfered repository <code>%s</code> to <a href="/%s%s">%s</a>
|
||||
push_tag = pushed tag <a href="%s/%s/src/%s">%s</a> to <a href="%s/%s">%s</a>
|
||||
compare_2_commits = View comparison for these 2 commits
|
||||
|
||||
[tool]
|
||||
ago = ago
|
||||
|
||||
1234
conf/locale/locale_fr-CA.ini
Normal file → Executable file
1234
conf/locale/locale_fr-CA.ini
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
718
conf/locale/locale_lv-LV.ini
Executable file
718
conf/locale/locale_lv-LV.ini
Executable file
@@ -0,0 +1,718 @@
|
||||
app_desc=Viegli uzstādāms Git serviss, kas rakstīts valodā Go
|
||||
|
||||
home=Sākums
|
||||
dashboard=Infopanelis
|
||||
explore=Izpētīt
|
||||
help=Palīdzība
|
||||
sign_in=Pierakstīties
|
||||
social_sign_in=Sociālā pieteikšanās: 2. solis <small>piesaistīt kontu</small>
|
||||
sign_out=Izrakstīties
|
||||
sign_up=Pieteikties
|
||||
register=Reģistrēties
|
||||
website=Mājas lapa
|
||||
version=Versija
|
||||
page=Lapa
|
||||
template=Sagatave
|
||||
language=Valoda
|
||||
|
||||
username=Lietotājvārds
|
||||
email=E-pasts
|
||||
password=Parole
|
||||
re_type=Parole atkārtoti
|
||||
captcha=Pārbaudes kods
|
||||
|
||||
repository=Repozitorijs
|
||||
organization=Organizācija
|
||||
mirror=Spogulis
|
||||
new_repo=Jauns repozitorijs
|
||||
new_migrate=Jauna migrācija
|
||||
new_fork=Jauns atdalītais repozitorijs
|
||||
new_org=Jauna organizācija
|
||||
manage_org=Pārvaldīt organizācijas
|
||||
admin_panel=Admin panelis
|
||||
account_settings=Konta iestatījumi
|
||||
settings=Iestatījumi
|
||||
|
||||
news_feed=Jaunumu plūsma
|
||||
pull_requests=Izmaiņu pieprasījumi
|
||||
issues=Problēmas
|
||||
|
||||
cancel=Atcelt
|
||||
|
||||
[install]
|
||||
install=Instalācija
|
||||
title=Instalācijas soļi pirmo reizi palaižot
|
||||
requite_db_desc=Gogs ir nepieciešama MySQL, PostgreSQL vai SQLite3 datu bāze.
|
||||
db_type=Datu bāzes veids
|
||||
host=Resursdators
|
||||
user=Lietotājs
|
||||
password=Parole
|
||||
db_name=Datu bāzes nosaukums
|
||||
db_helper=Nepieciešams izmantot MySQL INNODB dzini ar rakstzīmju kopu utf8_general_ci.
|
||||
ssl_mode=SSL režīms
|
||||
path=Ceļš
|
||||
sqlite_helper=SQLite 3 datu bāzes faila atrašanās vieta.
|
||||
general_title=Gogs vispārīgie iestatījumi
|
||||
repo_path=Repozitoriju glabāšanas vieta
|
||||
repo_path_helper=Visi Git attālinātie repozitoriji tiks glabāti šajā direktorijā.
|
||||
run_user=Izpildes lietotājs
|
||||
run_user_helper=Lietotājam ir jābūt rakstīšanas tiesībām repozitorija saknes direktorijai un Gogs jābūt palaistam zem šī lietotāja.
|
||||
domain=Domēns
|
||||
domain_helper=Tas ietekmē SSH klonēšanas URL.
|
||||
app_url=Lietotnes URL
|
||||
app_url_helper=Tas ietekmē HTTP/HTTPS klonēšanas URL un e-pasta saturā izsūtītās saites.
|
||||
email_title=E-pasta pakalpojuma iestatījumi (neobligāti)
|
||||
smtp_host=SMTP resursdators
|
||||
mailer_user=Sūtītāja e-pasta adrese
|
||||
mailer_password=Sūtītāja parole
|
||||
notify_title=Paziņojumu iestatījumi (neobligāti)
|
||||
register_confirm=Iespējot reģistrēšanās apstiprināšanu
|
||||
mail_notify=Iespējot e-pasta paziņojumus
|
||||
admin_title=Admin konta iestatījumi
|
||||
admin_name=Lietotājvārds
|
||||
admin_password=Parole
|
||||
confirm_password=Apstipriniet paroli
|
||||
admin_email=E-pasts
|
||||
install_gogs=Instalēt Gogs
|
||||
test_git_failed=Kļūda pārbaudot 'git' komandu: %v
|
||||
sqlite3_not_available=Jūsu versija neatbalsta SQLite3, lūdzu lejupielādējiet oficiālo bināro versiju no %s, NEVIS gobuild versiju.
|
||||
invalid_db_setting=Datu bāzes iestatījums nav pareizs: %v
|
||||
invalid_repo_path=Repozitorija atrašanās vieta ir nekorekta: %v
|
||||
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
|
||||
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!
|
||||
|
||||
[home]
|
||||
uname_holder=Lietotājvārds vai e-pasts
|
||||
password_holder=Parole
|
||||
switch_dashboard_context=Mainīt infopaneļa kontekstu
|
||||
my_repos=Mani repozitoriji
|
||||
collaborative_repos=Sadarbības repozitoriji
|
||||
my_orgs=Manas organizācijas
|
||||
my_mirrors=Mani spoguļi
|
||||
|
||||
[explore]
|
||||
repos=Repozitoriji
|
||||
|
||||
[auth]
|
||||
create_new_account=Izveidot jaunu kontu
|
||||
register_hepler_msg=Jau ir konts? Pieraksties tagad!
|
||||
social_register_hepler_msg=Jau ir konts? Sasaisti tagad!
|
||||
disable_register_prompt=Atvainojiet, reģistrācija ir atspējota. Lūdzu, sazinieties ar vietnes administratoru.
|
||||
disable_register_mail=Atvainojiet, reģistrācijas e-pasta apstiprināšana ir atspējota.
|
||||
remember_me=Atcerēties mani
|
||||
forgot_password=Aizmirsu paroli
|
||||
forget_password=Aizmirsi paroli?
|
||||
sign_up_now=Nepieciešams konts? Reģistrējies tagad.
|
||||
confirmation_mail_sent_prompt=Jauns apstiprināšanas e-pasts ir nosūtīts uz <b>%s</b>, lūdzu, pārbaudies savu e-pasta kontu tuvāko %d stundu laikā, lai pabeigtu reģistrācijas procesu.
|
||||
sign_in_email=Atvērt savu e-pasta kontu
|
||||
active_your_account=Aktivizēt savu kontu
|
||||
resent_limit_prompt=Atvainojiet, Jūs sūtījāt aktivizācijas e-pastu pārāk bieži. Lūdzu, gaidiet 3 minūtes.
|
||||
has_unconfirmed_mail=Sveiki %s, Jums ir neapstiprināta e-pasta adrese (<b>%s</b>). Ja neesat saņēmis apstiprināšanas e-pastu vai Jums ir nepieciešams nosūtīt jaunu, lūdzu, nospiediet pogu, kas atrodas zemāk.
|
||||
resend_mail=Nospiediet šeit, lai vēlreiz nosūtītu aktivizācijas e-pastu
|
||||
email_not_associate=Šī e-pasta adrese nav saistīta ar Jūsu kontu.
|
||||
send_reset_mail=Spiediet šeit, lai nosūtītu paroles maiņas vēstuli uz Jūsu e-pastu
|
||||
reset_password=Atjaunot savu paroli
|
||||
invalid_code=Atvainojiet, Jūsu apstiprināšanas kodam ir beidzies derīguma termiņš vai arī tas ir nepareizs.
|
||||
reset_password_helper=Nospiediet šeit, lai atjaunotu paroli
|
||||
password_too_short=Paroles garums nedrīkst būt mazāks par 6.
|
||||
|
||||
[form]
|
||||
UserName=Lietotājvārds
|
||||
RepoName=Repozitorija nosaukums
|
||||
Email=E-pasta adrese
|
||||
Password=Parole
|
||||
Retype=Parole atkārtoti
|
||||
SSHTitle=SSH atslēgas nosaukums
|
||||
HttpsUrl=HTTPS URL
|
||||
PayloadUrl=Vērtuma URL
|
||||
TeamName=Komandas nosaukums
|
||||
AuthName=Autorizācijas nosaukums
|
||||
AdminEmail=Admin e-pasta adrese
|
||||
|
||||
require_error=` nedrīkst būt tukšs.`
|
||||
alpha_dash_error=` drīkst saturēt tikai latīņu alfabēta burtus, ciparus vai domuzīmes (-_).`
|
||||
alpha_dash_dot_error=` drīkst saturēt tikai latīņu alfabēta burtus, ciparus, domuzīmes (-_) vai punktu.`
|
||||
min_size_error=` jabūt vismaz %s simbolu garumā.`
|
||||
max_size_error=` jabūt ne mazāk kā %s simbolu garumā.`
|
||||
email_error=` nav derīga e-pasta adrese.`
|
||||
url_error=` nav korekts URL.`
|
||||
unknown_error=Nezināma kļūda:
|
||||
captcha_incorrect=Pārbaudes kods nesakrīt ar attēlā redzamo.
|
||||
password_not_match=Parole un atkārtoti ievadītā parole nav vienādas.
|
||||
|
||||
username_been_taken=Lietotājvārds ir jau aizņemts.
|
||||
repo_name_been_taken=Repozitorija vārds 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.
|
||||
email_been_used=E-pasta adrese jau tiek izmantota.
|
||||
ssh_key_been_used=Publiskās atslēgas nosaukums jau tiek izmantos.
|
||||
illegal_username=Jūsu lietotājvārds satur neatļautas rakstzīmes.
|
||||
illegal_repo_name=Krātuves nosaukums satur neatļautas rakstzīmes.
|
||||
illegal_org_name=Organizācijas nosaukums satur neatļautas rakstzīmes.
|
||||
illegal_team_name=Grupas nosaukums satur neatļautas rakstzīmes.
|
||||
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_owner_name=Lūdzu, pārliecinieties, vai ievadītā īpašnieka vārds ir pareizs.
|
||||
enterred_invalid_password=Lūdzu pārliecinieties, vai Jūsu ievadītā parole ir pareiza.
|
||||
user_not_exist=Šāds lietotājs neeksistē.
|
||||
last_org_owner=Nav iespējams noņemt vienīgo komandas īpašnieku. Pirms tam ir nepieciešams norādīt jauno īpašnieku.
|
||||
|
||||
invalid_ssh_key=Atvainojiet, nav iespējams pārbaudīt Jūsu SSH atslēgu: %s
|
||||
unable_verify_ssh_key=Nav iespējams pārbaudīt jūsu SSH atslēgu, bet tiks pieņemts, ka tā ir derīga, lūdzu, pārliecinieties par to pats.
|
||||
auth_failed=Autentifikācija neizdevās: %v
|
||||
|
||||
still_own_repo=Jūsu esat vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai nomainīt to īpašnieku.
|
||||
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.
|
||||
|
||||
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ē
|
||||
|
||||
[user]
|
||||
change_avatar=Mainīt savu profila attēlu vietnē gravatar.com
|
||||
change_custom_avatar=Mainīt savu profila attēlu iestatījumos
|
||||
join_on=Pievienojās
|
||||
repositories=Repozitoriji
|
||||
activity=Publiskā aktivitāte
|
||||
followers=Sekotāji
|
||||
starred=Atzīmēti ar zvaigznīti
|
||||
following=Seko
|
||||
|
||||
[settings]
|
||||
profile=Profils
|
||||
password=Parole
|
||||
ssh_keys=SSH atslēgas
|
||||
social=Sociālie konti
|
||||
applications=Lietotnes
|
||||
orgs=Organizācijas
|
||||
delete=Dzēst kontu
|
||||
uid=Lietotāja ID
|
||||
|
||||
public_profile=Publiskais profils
|
||||
profile_desc=Jūsu e-pasta adrese ir publiska un tiks izmantota, lai nosūtītju Jums paziņojumus, kas saistīti ar Jūsu kontu vai darbībām veiktām caur šo mājas lapu.
|
||||
full_name=Pilns vārds
|
||||
website=Mājas lapa
|
||||
location=Atrašanās vieta
|
||||
update_profile=Mainīt profilu
|
||||
update_profile_success=Jūsu profila dati ir veiksmīgi saglabāti.
|
||||
change_username=Lietotāja vārds mainīts
|
||||
change_username_desc=Lietotājvārds tiks mainīts, vai vēlaties turpināt? Tas ietekmēs visas saites, kas attiecas uz Jūsu kontu.
|
||||
continue=Turpināt
|
||||
cancel=Atcelt
|
||||
|
||||
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
|
||||
update_avatar=Saglabāt profila bildi
|
||||
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.
|
||||
|
||||
change_password=Mainīt paroli
|
||||
old_password=Pašreizējā parole
|
||||
new_password=Jauna parole
|
||||
password_incorrect=Ievadīta nepareiza pašreizējā parole.
|
||||
change_password_success=Parole tika veiksmīgi nomainīta. Tagad jūs varat pieraksītites, izmantojot jauno paroli.
|
||||
|
||||
manage_ssh_keys=Pārvaldīt SSH atslēgas
|
||||
add_key=Pievienot atslēgu
|
||||
ssh_desc=Šis ir saraksts ar Jūsu kontam piesaistītajām SSH atslēgām. Dzēsiet visas, kuras Jūs neatpazīstat.
|
||||
ssh_helper=<strong>Vajadzīga palīdzība?</strong> Apskatieties pamācību kā <a href="%s">ģenerēt SSH atslēgas</a> vai kā novērst <a href="%s">biežāk sastopamās SSH problēmas</a>.
|
||||
add_new_key=Pievienot SSH atslēgu
|
||||
key_name=Atslēgas nosaukums
|
||||
key_content=Saturs
|
||||
add_key_success=Pievienota jauna SSH atslēga!
|
||||
delete_key=Dzēst
|
||||
add_on=Pievienota
|
||||
last_used=Pēdējo reizi izmantota
|
||||
no_activity=Nav nesenas aktivitātes
|
||||
|
||||
manage_social=Pārvaldīt piesaistītos sociālos kontus
|
||||
social_desc=Šeit tiek attēloti visi sociālie konti, kas ir piesaistīti Jūsu kontam. Dzēsiet visus, kurus Jūs neatpazīstat.
|
||||
unbind=Atsaistīt
|
||||
unbind_success=Sociālais konts tika atsaistīts.
|
||||
|
||||
manage_access_token=Pārvaldīt personīgos piekļuves talonus
|
||||
generate_new_token=Ģenerēt jaunu talonu
|
||||
tokens_desc=Talons tika uzģēnerēts, tagad varat to izmantot, lai piekļūtu Gogs API.
|
||||
new_token_desc=Pašlaik visiem taloniem ir pilna piekļuve Jūsu kontam.
|
||||
token_name=Talona nosaukums
|
||||
generate_token=Ģenerēt talonu
|
||||
generate_token_succees=Jauns piekļuves talons tika veiksmīgi uzģenerēts! Pārliecinieties, ka esat to nokopējis, jo to Jums vairāk nebūs iespēja izdarīt!
|
||||
delete_token=Dzēst
|
||||
delete_token_success=Jūsu personīgās piekļuves talons tika veiksmīgi izdzēsts! Neazimistiet veikt nepieciešamos labojumus savās lietojumprogrammās, kas to izmantoja.
|
||||
|
||||
delete_account=Dzēst savu kontu
|
||||
delete_prompt=Šī darbība pilnībā izdzēsīs Jūsu kontu, kā arī tā ir <strong>NEATGRIEZENISKA</strong>!
|
||||
confirm_delete_account=Apstiprināt dzēšanu
|
||||
delete_account_title=Konta dzēšana
|
||||
delete_account_desc=Šis konts tiks neatgriezeniski dzēsts, vai vēlaties turpināt?
|
||||
|
||||
[repo]
|
||||
owner=Īpašnieks
|
||||
repo_name=Repozitorija nosaukums
|
||||
repo_name_helper=Labi repzotoriju nosaukumi ir īsi, tādi kurus viegli atcerēties un <strong>unikāli</strong>.
|
||||
visibility=Redzamība
|
||||
visiblity_helper=Šis repozitorijs ir <span class="label label-red label-radius">Privāts</span>
|
||||
fork_repo=Atdalīt repozitoriju
|
||||
fork_from=Atdalīt no
|
||||
fork_visiblity_helper=Atdalītam repozitorijam nav iespējams nomainīt tā redzamību
|
||||
repo_desc=Apraksts
|
||||
repo_lang=Valoda
|
||||
repo_lang_helper=Izvēlieties .gitignore failu
|
||||
license=Licence
|
||||
license_helper=Izvēlieties licences failu
|
||||
init_readme=Inicializēt šo repozitoriju ar README.md failu
|
||||
create_repo=Izveidot repozitoriju
|
||||
default_branch=Noklusējuma atzars
|
||||
mirror_interval=Spoguļošanas intervāls (stundās)
|
||||
goget_meta=Go-Get metadati
|
||||
goget_meta_helper=Šis repozitorijs saturēs <span class="label label-blue label-radius">Go-Get</span> metadatus
|
||||
|
||||
need_auth=Nepieciešama autorizācija
|
||||
migrate_type=Migrācijas veids
|
||||
migrate_type_helper=Šis repozitorijs būs <span class="label label-blue label-radius">Spoguļots</span>
|
||||
migrate_repo=Migrēt repozitoriju
|
||||
|
||||
copy_link=Kopēt
|
||||
click_to_copy=Kopēt uz starpliktuvi
|
||||
copied=Kopēšana notikusi veiksmīgi
|
||||
clone_helper=Nepieciešama palīdzība kā veikt klonēšana? Apmeklējiet <a target="_blank" href="%s">Palīdzība</a> lapu!
|
||||
unwatch=Nevērot
|
||||
watch=Vērot
|
||||
unstar=Noņemt zvaigznīti
|
||||
star=Pievienot zvaigznīti
|
||||
fork=Atdalīts
|
||||
|
||||
no_desc=Nav apraksta
|
||||
quick_guide=Īsa pamācība
|
||||
clone_this_repo=Klonēt šo repozitoriju
|
||||
create_new_repo_command=Izveidot jaunu repozitoriju komandrindā
|
||||
push_exist_repo=Nosūtīt izmaiņas no komandrindas eksistējošam repozitorijam
|
||||
|
||||
branch=Atzars
|
||||
tree=Koks
|
||||
branch_and_tags=Atzari un birkas
|
||||
branches=Atzari
|
||||
tags=Birkas
|
||||
issues=Problēmas
|
||||
commits=Revīzijas
|
||||
releases=Laidieni
|
||||
file_raw=Neapstrādāts
|
||||
file_history=Vēsture
|
||||
file_view_raw=Rādīt neapstrādātu
|
||||
|
||||
commits.commits=Revīzijas
|
||||
commits.search=Meklēt revīzijas
|
||||
commits.find=Meklēt
|
||||
commits.author=Autors
|
||||
commits.message=Ziņojums
|
||||
commits.date=Datums
|
||||
commits.older=Vecāki
|
||||
commits.newer=Jaunāki
|
||||
|
||||
settings=Iestatījumi
|
||||
settings.options=Opcijas
|
||||
settings.collaboration=Sadarbība
|
||||
settings.hooks=Tīmekļa āķi
|
||||
settings.githooks=Git āķi
|
||||
settings.deploy_keys=Izvietot atslēgas
|
||||
settings.basic_settings=Pamatiestatījumi
|
||||
settings.danger_zone=Bīstamā zona
|
||||
settings.site=Oficiālā mājas lapa
|
||||
settings.update_settings=Mainīt iestatījumus
|
||||
settings.change_reponame=Mainīts repozitorija nosaukums
|
||||
settings.change_reponame_desc=Repozitorija nosaukums tiks mainīts, vai vēlaties turpināt? Tas ietekmēs visas saites, kas saistītas ar šo repozitoriju.
|
||||
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.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=<p>- Jūs zaudēsiet piekļuvi repozitorijam, ja jaunais īpašnieks ir individuāls lietotājs.</p><p>- Jūs saglabāsiet piekļuvi repzitorijam, ja jaunais īpašneiks ir organizācija un Jūs esat viens no tās īpašniekiem.</p>
|
||||
settings.update_settings_success=Repozitorija opcijas ir veiksmīgi saglabātas.
|
||||
settings.transfer_owner=Jaunais īpašnieks
|
||||
settings.make_transfer=Mainīt
|
||||
settings.transfer_succeed=Repozitorija īpašnieks ir veiksmīgi nomainīts.
|
||||
settings.confirm_delete=Apstiprināt dzēšanu
|
||||
settings.add_collaborator=Pievienot jaunu līdzstrādnieku
|
||||
settings.add_collaborator_success=Jauns līdzstrādnieks ir pievienots.
|
||||
settings.remove_collaborator_success=Līdzstrādnieks tika noņemts.
|
||||
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.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.githooks_desc=Git āķus apstrādā pats Git. Jūs varat labot atbalsīto āku failus sarakstā zemāk, lai veiktu pielāgotas darbības.
|
||||
settings.githook_edit_desc=Ja āķis nav aktīvs, tiks attēlots piemērs kā to izmantot. Atstājot āķa saturu tukšu, tas tiks atspējots.
|
||||
settings.githook_name=Āķa nosaukums
|
||||
settings.githook_content=Āķa saturs
|
||||
settings.update_githook=Labot āķi
|
||||
settings.remove_hook_success=Tīmekļa āķis tika noņemts.
|
||||
settings.add_webhook_desc=Uz norādīto URL tiks nosūtīts <code>POST</code> pieprasījums ar notikuma datiem. Jūs varat norādīt arī datu formātu, kādā datus vēlaties saņemt (JSON, <code>x-www-form-urlencoded</code> <em>utt.</em>). Detalizētāku informāciju ir iespējams uzzināt <a target="_blank" href="%s">Tīmekļa āķu rokasgrāmatā</a>.
|
||||
settings.payload_url=Vērtuma URL
|
||||
settings.content_type=Satura tips
|
||||
settings.secret=Noslēpums
|
||||
settings.event_desc=Kādu notikumu rezultātā tiktu izsaukts tīmekļā āķis?
|
||||
settings.event_push_only=Tikai izmaiņu nosūtīšanas notikumiem.
|
||||
settings.active=Aktīvs
|
||||
settings.active_helper=Tiks nosūtīti notikuma dati, kad nostrādās šis āķis.
|
||||
settings.add_hook_success=Jauns tīmekļa āķis tika veiksmīgi pievienots.
|
||||
settings.update_webhook=Mainīt tīmekļa āķi
|
||||
settings.update_hook_success=Tīmekļa āķist tika veiksmīgi saglabāts.
|
||||
settings.delete_webhook=Dzēst tīmekļa āķi
|
||||
settings.recent_deliveries=Pēdējās piegādes
|
||||
settings.hook_type=Āķa veids
|
||||
settings.add_slack_hook_desc=PIevienot <a href="%s">Slack</a> integrāciju Jūsu repozitorijā.
|
||||
settings.slack_token=Talons
|
||||
settings.slack_domain=Domēns
|
||||
settings.slack_channel=Kanāls
|
||||
|
||||
diff.browse_source=Pārlūkot izejas kodu
|
||||
diff.parent=vecāks
|
||||
diff.commit=revīzija
|
||||
diff.data_not_available=Salīdzināšanas dati nav pieejami.
|
||||
diff.show_diff_stats=Rādīt salīdzināšanas statistiku
|
||||
diff.stats_desc=<strong>%d mainītis faili</strong> ar <strong>%d papildinājumiem</strong> un <strong>%d dzēšanām</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Parādīt failu
|
||||
|
||||
release.releases=Releases
|
||||
release.new_release=New Release
|
||||
release.draft=Draft
|
||||
release.prerelease=Pre-Release
|
||||
release.stable=Stable
|
||||
release.edit=edit
|
||||
release.ahead=<strong>%d</strong> commits to %s since this release
|
||||
release.source_code=Source Code
|
||||
release.tag_name=Tag name
|
||||
release.target=Target
|
||||
release.tag_helper=Choose an existing tag, or create a new tag on publish.
|
||||
release.release_title=Release title
|
||||
release.content_with_md=Content with <a href="%s">Markdown</a>
|
||||
release.write=Write
|
||||
release.preview=Preview
|
||||
release.content_placeholder=Write some content
|
||||
release.loading=Loading...
|
||||
release.prerelease_desc=This is a pre-release
|
||||
release.prerelease_helper=We’ll point out that this release is identified as non-production ready.
|
||||
release.publish=Publish Release
|
||||
release.save_draft=Save Draft
|
||||
release.edit_release=Edit Release
|
||||
release.tag_name_already_exist=Release with this tag name has already existed.
|
||||
|
||||
[org]
|
||||
org_name_holder=Organizācijas nosaukums
|
||||
org_name_helper=Labi organizāciju nosaukumi ir īsi un tādi, kurus viegli atcerēties.
|
||||
org_email_helper=Uz organizācijas e-pastu tiks sūtītas visas notifikācias un apstiprinājumi.
|
||||
create_org=Izveidot organizāciju
|
||||
repo_updated=Atjaunināts
|
||||
people=Personas
|
||||
invite_someone=Uzaicināt kādu
|
||||
teams=Komandas
|
||||
lower_members=dalībnieki
|
||||
lower_repositories=repozitoriji
|
||||
create_new_team=Izveidot jaunu komandu
|
||||
org_desc=Apraksts
|
||||
team_name=Komandas nosaukums
|
||||
team_desc=Apraksts
|
||||
team_name_helper=Šo nosaukumu varēs izmantot, lai pieminētu komandu sarunās.
|
||||
team_desc_helper=Komandas apraksts
|
||||
team_permission_desc=Kādām tiesībām šai komandai būtu jābūt?
|
||||
|
||||
settings=Iestatījumi
|
||||
settings.options=Opcijas
|
||||
settings.full_name=Pilns vārds, uzvārds
|
||||
settings.website=Mājas lapa
|
||||
settings.location=Atrašanās vieta
|
||||
settings.update_settings=Mainīt iestatījumus
|
||||
settings.change_orgname=Mainīts organizācijas nosaukums
|
||||
settings.change_orgname_desc=Organizācijas nosaukums tiks mainīts, vai vēlaties turpinat? Tas ietekmēs saites, kas attiecas uz šo organizāciju.
|
||||
settings.update_setting_success=Organizācijas iestatījumi tika veiksmīgi saglabāti.
|
||||
settings.delete=Dzēst organizāciju
|
||||
settings.delete_account=Dzēst šo organizāciju
|
||||
settings.delete_prompt=Šī darbība pilnībā dzēsīs šo organizāciju, kā arī tā ir <strong>NEATGRIEZENISKA</strong>!
|
||||
settings.confirm_delete_account=Apstiprināt dzēšanu
|
||||
settings.delete_org_title=Organizācijas dzēšana
|
||||
settings.delete_org_desc=Šī organizācija tiks pilnībā izdzēsta, vai vēlaties turpināt?
|
||||
settings.hooks_desc=Pievienot tīmekļa āķus, kas nostrādās <strong>visiem repozitorijiem</strong> šajā organizācijā.
|
||||
|
||||
members.public=Publisks
|
||||
members.public_helper=padarīt privātu
|
||||
members.private=Privāts
|
||||
members.private_helper=padarīt publisku
|
||||
members.owner=Īpašnieks
|
||||
members.member=Biedri
|
||||
members.conceal=Noslēpt
|
||||
members.remove=Noņemt
|
||||
members.leave=Atstāt
|
||||
members.invite_desc=Sāciet rakstīt lietotājvārdu, lai uzaicinātu jaunu biedru organizācijā %s:
|
||||
members.invite_now=Uzaicināt tagad
|
||||
|
||||
teams.join=Pievienoties
|
||||
teams.leave=Atstāt
|
||||
teams.read_access=Lasīšanas piekļuve
|
||||
teams.read_access_helper=Komanda varēs skatīties un klonēt šīs organizācijas repozitorijus.
|
||||
teams.write_access=Rakstīšanas piekļuve
|
||||
teams.write_access_helper=Komanda varēs skatīties un klonēt, kā arī nosūtīt izmaiņas šīs organizācijas repozitorijiem.
|
||||
teams.admin_access=Administratora piekļuve
|
||||
teams.admin_access_helper=Šī komanda varēs veikt push/pull komandas tās repozitorijiem, kā arī tiem pievienot citus līdzstrādniekus.
|
||||
teams.no_desc=Komandai nav apraksta
|
||||
teams.settings=Iestatījumi
|
||||
teams.owners_permission_desc=Īpašniekiem ir pilna piekļuve <strong>visiem repozitorijiem</strong> un ir organizācijas <strong>administratora tiesības</strong>.
|
||||
teams.members=Komandas biedri
|
||||
teams.update_settings=Saglabāt iestatījumus
|
||||
teams.delete_team=Dzēst komandu
|
||||
teams.add_team_member=Pievienot komandas biedru
|
||||
teams.delete_team_title=Komandas dzēšana
|
||||
teams.delete_team_desc=Komanda tiks dzēsta, vai vēlaties turpināt? Komandas biedri var zaudēt piekļuvi dažiem vai pat visiem repozitorijiem.
|
||||
teams.delete_team_success=Komanda tika veiksmīgi izdzēsta.
|
||||
teams.read_permission_desc=Šai komandai ir <strong>lasīšanas</strong> tiesības: dalībnieki var skatīties un klonēt komandas repozitorijus.
|
||||
teams.write_permission_desc=Šai komandai ir <strong>rakstīšanas</strong> tiesības: dalībnieki var lasīt un nosūtīt izmaiņas repozitorijiem.
|
||||
teams.admin_permission_desc=Šai komandai ir <strong>administratora</strong> tiesības: dalībnieki var lasīt, rakstīt un pievienot citus dalībniekus komandas repozitorijiem.
|
||||
teams.repositories=Komandas repozitoriji
|
||||
teams.add_team_repository=Pievienot komandas repozitoriju
|
||||
teams.remove_repo=Noņemt
|
||||
teams.add_nonexistent_repo=Repozitorijs, kuram Jūs mēģinat pievienot neeksistē, sākumā izveidojiet to.
|
||||
|
||||
[admin]
|
||||
dashboard=Infopanelis
|
||||
users=Lietotāji
|
||||
organizations=Organizācijas
|
||||
repositories=Repozitoriji
|
||||
authentication=Autentifikācijas
|
||||
config=Konfigurācija
|
||||
notices=Sistēmas paziņojumi
|
||||
monitor=Uzraudzība
|
||||
prev=Iepr.
|
||||
next=Tālāk
|
||||
|
||||
dashboard.statistic=Statistika
|
||||
dashboard.operations=Darbības
|
||||
dashboard.system_status=Sistēmas uzraudzības statuss
|
||||
dashboard.statistic_info=Gogs datu bāze satur <b>%d</b> lietotājus, <b>%d</b> organizācijas, <b>%d</b> publiskās atslēgas, <b>%d</b> repozitorijus, <b>%d</b> vērošanas, <b>%d</b> atzīmētas zvaigznītes, <b>%d</b> darbības, <b>%d</b> piekļuves, <b>%d</b> problēmas, <b>%d</b> komentārus, <b>%d</b> sociālos kontus, <b>%d</b> sekošanas, <b>%d</b> spoguļošanas, <b>%d</b> izlaides, <b>%d</b> login sources, <b>%d</b> tīmekļa āķus, <b>%d</b> starpposmus, <b>%d</b> etiķetes, <b>%d</b> āķu uzdevumus, <b>%d</b> komandas, <b>%d</b> labotus uzdevumus, <b>%d</b> pielikumus.
|
||||
dashboard.operation_name=Darbības nosaukums
|
||||
dashboard.operation_switch=Pārslēgt
|
||||
dashboard.operation_run=Palaist
|
||||
dashboard.clean_unbind_oauth=Notīrīt nesaistītās OAuth biļetes
|
||||
dashboard.clean_unbind_oauth_success=Visas nesaistītās OAuth biļetes tika veiksmīgi izdzēstas.
|
||||
dashboard.delete_inactivate_accounts=Dzēst visus neaktīvos kontus
|
||||
dashboard.delete_inactivate_accounts_success=Visi neaktīvie kotni tika veiksmīgi izdzēsti.
|
||||
dashboard.delete_repo_archives=Dzēst visu repozitoriju arhīvus
|
||||
dashboard.delete_repo_archives_success=Visu repozitoriju arhīvi tika veiksmīgi izdzēsti.
|
||||
dashboard.git_gc_repos=Veikt repozitoriju datu sakārtošānu (git gc)
|
||||
dashboard.git_gc_repos_success=Datu sakārtošana visiem repozitorijiem veiksmīgi pabeigta.
|
||||
dashboard.server_uptime=Servera darbības laiks
|
||||
dashboard.current_goroutine=Izmantotās Gorutīnas
|
||||
dashboard.current_memory_usage=Pašreiz izmantotā atmiņa
|
||||
dashboard.total_memory_allocated=Kopējā piešķirtā atmiņa
|
||||
dashboard.memory_obtained=Iegūtā atmiņa
|
||||
dashboard.pointer_lookup_times=Rādītāju meklēšanas reizes
|
||||
dashboard.memory_allocate_times=Atmiņas piešķiršanas reizes
|
||||
dashboard.memory_free_times=Atmiņas atbrīvošanas reizes
|
||||
dashboard.current_heap_usage=Pašreizējā kaudzes izmantošana
|
||||
dashboard.heap_memory_obtained=Iegūtā kaudzes atmiņa
|
||||
dashboard.heap_memory_idle=Neizmantotā kaudzes atmiņa
|
||||
dashboard.heap_memory_in_use=Izmantotā kaudzes atmiņa
|
||||
dashboard.heap_memory_released=Atbrīvotā kaudzes atmiņa
|
||||
dashboard.heap_objects=Kaudzes atmiņas objekti
|
||||
dashboard.bootstrap_stack_usage=Izmantotais sāknēšanas steka lielums
|
||||
dashboard.stack_memory_obtained=Iegūtā steka atmiņa
|
||||
dashboard.mspan_structures_usage=Izmantotās MSpan struktūras
|
||||
dashboard.mspan_structures_obtained=Iegūtās MSpan struktūras
|
||||
dashboard.mcache_structures_usage=Izmantotās MCache struktūras
|
||||
dashboard.mcache_structures_obtained=Iegūtās MCache struktūras
|
||||
dashboard.profiling_bucket_hash_table_obtained=Iegūtā profilēšanas kausa jaucējtabula
|
||||
dashboard.gc_metadata_obtained=Iegūtie GC metadati
|
||||
dashboard.other_system_allocation_obtained=Iegūtās citas sistēmas sadales
|
||||
dashboard.next_gc_recycle=Nākošā GC atkritne
|
||||
dashboard.last_gc_time=Laiks kopš pēdējās GC
|
||||
dashboard.total_gc_time=Kopējais GC izpildes laiks
|
||||
dashboard.total_gc_pause=Kopējais GC izpildes laiks
|
||||
dashboard.last_gc_pause=Pedējās GC izpildes laiks
|
||||
dashboard.gc_times=GC reizes
|
||||
|
||||
users.user_manage_panel=Lietotāju pārvaldības panelis
|
||||
users.new_account=Izveidot jaunu kontu
|
||||
users.name=Vārds
|
||||
users.activated=Aktivizēts
|
||||
users.admin=Administrators
|
||||
users.repos=Repozitoriji
|
||||
users.created=Izveidots
|
||||
users.edit=Labot
|
||||
users.auth_source=Autorizācijas avots
|
||||
users.local=Iebūvētā
|
||||
users.auth_login_name=Autorizāciju, pietiekšanās vārds
|
||||
users.update_profile_success=Konta profils tika veiksmīgi saglabāts.
|
||||
users.edit_account=Labot kontu
|
||||
users.is_activated=Konts ir aktivizēts
|
||||
users.is_admin=Šim kontam ir administratora piekļuves tiesības
|
||||
users.allow_git_hook=Šim kontam ir tiesības pievienot/labot Git āķus
|
||||
users.update_profile=Mainīt konta profilu
|
||||
users.delete_account=Dzēst šo kontu
|
||||
users.still_own_repo=Šis konts ir vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai nomainīt to īpašnieku.
|
||||
users.still_has_org=Šis konts ir vismaz vienas organizācijas biedrs, sākumā nepieciešams pamest vai izdzēst šo organizāciju.
|
||||
|
||||
orgs.org_manage_panel=Organizāciju pārvaldības panelis
|
||||
orgs.name=Nosaukums
|
||||
orgs.teams=Komandas
|
||||
orgs.members=Dalībnieki
|
||||
|
||||
repos.repo_manage_panel=Repozitoriju pārvaldības panelis
|
||||
repos.owner=Īpašnieks
|
||||
repos.name=Vārds
|
||||
repos.private=Privāts
|
||||
repos.watches=Vērošana
|
||||
repos.stars=Atzīmētās zvaigznītes
|
||||
repos.issues=Problēmas
|
||||
|
||||
auths.auth_manage_panel=Autorizāciju pārvaldības panelis
|
||||
auths.new=Pievienot jaunu autorizācijas veidu
|
||||
auths.name=Nosaukums
|
||||
auths.type=Veids
|
||||
auths.enabled=Iespējota
|
||||
auths.updated=Atjaunināta
|
||||
auths.auth_type=Autorizācijas veids
|
||||
auths.auth_name=Autorizācijas nosaukums
|
||||
auths.domain=Domēns
|
||||
auths.host=Resursdators
|
||||
auths.port=Ports
|
||||
auths.base_dn=Pamata DN
|
||||
auths.attributes=Meklēšanas atribūti
|
||||
auths.filter=Meklēšanas filtrs
|
||||
auths.ms_ad_sa=MS Ad SA
|
||||
auths.smtp_auth=SMTP autorizācijas veids
|
||||
auths.smtphost=SMTP resursdators
|
||||
auths.smtpport=SMTP ports
|
||||
auths.enable_tls=Iespējot TLS šifrēšanu
|
||||
auths.enable_auto_register=Iespējot automātisko reģistrāciju
|
||||
auths.tips=Padomi
|
||||
auths.edit=Labot autorizācijas iestatījumus
|
||||
auths.activated=Autentifikācija ir aktivizēta
|
||||
auths.update_success=Autorizācijas iestatījumi tika veiksmīgi saglabāti.
|
||||
auths.update=Mainīt autorizācijas iestatījumus
|
||||
auths.delete=Dzēst šo autorizāciju
|
||||
auths.delete_auth_title=Autorizācijas dzēšana
|
||||
auths.delete_auth_desc=Šī autorizācija tiks dzēsta, vai vēlaties turpināt?
|
||||
|
||||
config.server_config=Servera konfigurācija
|
||||
config.app_name=Lietotnes nosaukums
|
||||
config.app_ver=Lietotnes versija
|
||||
config.app_url=Lietotnes URL
|
||||
config.domain=Domēns
|
||||
config.offline_mode=Bezsaistes režīms
|
||||
config.disable_router_log=Atspējot maršrutētāja žurnalizēšanu
|
||||
config.run_user=Izpildes lietotājs
|
||||
config.run_mode=Izpildes režīms
|
||||
config.repo_root_path=Repozitoriju glabāšanas vieta
|
||||
config.static_file_root_path=Statisko failu atrašanās vieta
|
||||
config.log_file_root_path=Žurnalizēšanas failu glabāšanas vieta
|
||||
config.script_type=Skripta veids
|
||||
config.reverse_auth_user=Reversā lietotāja autentifikācija
|
||||
config.db_config=Datu bāzes konfigurācija
|
||||
config.db_type=Veids
|
||||
config.db_host=Resursdators
|
||||
config.db_name=Nosaukums
|
||||
config.db_user=Lietotājs
|
||||
config.db_ssl_mode=SSL režīms
|
||||
config.db_ssl_mode_helper=(tikai PostgreSQL datu bāzei)
|
||||
config.db_path=Ceļš
|
||||
config.db_path_helper=(tikai Sqlite3 datu bāzei)
|
||||
config.service_config=Pakalpojuma konfigurācija
|
||||
config.register_email_confirm=E-pasta reģistrēšanas apstiprināšana
|
||||
config.disable_register=Atspējot jaunu lietotāju reģistrāciju
|
||||
config.require_sign_in_view=Nepieciešama autorizācija
|
||||
config.mail_notify=Pasta paziņojumi
|
||||
config.enable_cache_avatar=Glabāt profila attēlus kešatmiņā
|
||||
config.active_code_lives=Aktīvā koda ilgums
|
||||
config.reset_password_code_lives=Paroles atiestatīšanas koda ilgums
|
||||
config.webhook_config=Tīkla āķu konfigurācija
|
||||
config.task_interval=Uzdevuma intervāls
|
||||
config.deliver_timeout=Piegādes noildze
|
||||
config.mailer_config=Sūtītāja konfigurācija
|
||||
config.mailer_enabled=Iespējots
|
||||
config.mailer_name=Nosaukums
|
||||
config.mailer_host=Resursdators
|
||||
config.mailer_user=Lietotājs
|
||||
config.oauth_config=OAuth konfigurācija
|
||||
config.oauth_enabled=Iespējota
|
||||
config.cache_config=Kešatmiņas konfigurācija
|
||||
config.cache_adapter=Kešatmiņas adapteris
|
||||
config.cache_interval=Kešatmiņas intervāls
|
||||
config.cache_conn=Kešatmiņas pieslēguma parametri
|
||||
config.session_config=Sesijas konfigurācja
|
||||
config.session_provider=Sesijas nodrošinātājs
|
||||
config.provider_config=Pakalpojumu sniedzēja konfigurācija
|
||||
config.cookie_name=Sīkdatnes nosaukums
|
||||
config.enable_set_cookie=Ļaut izmantot sīkdatnes
|
||||
config.gc_interval_time=GC laika intervāls
|
||||
config.session_life_time=Sesijas ilgums
|
||||
config.https_only=Tikai HTTPS
|
||||
config.cookie_life_time=Sīkdatņu glabāšanas ilgums
|
||||
config.picture_config=Attēlu konfigurācija
|
||||
config.picture_service=Lokāli attēli
|
||||
config.disable_gravatar=Atspējot Gravatar
|
||||
config.log_config=Žurnalizēšanas konfigurācija
|
||||
config.log_mode=Žurnalizēšanas veids
|
||||
|
||||
monitor.cron=Cron uzdevumi
|
||||
monitor.name=Nosaukums
|
||||
monitor.schedule=Grafiks
|
||||
monitor.next=Nākošās izpildes laiks
|
||||
monitor.previous=Pēdējās izpildes laiks
|
||||
monitor.execute_times=Izpilžu skaits
|
||||
monitor.process=Darbojošies procesi
|
||||
monitor.desc=Apraksts
|
||||
monitor.start=Sākuma laiks
|
||||
monitor.execute_time=Izpildes laiks
|
||||
|
||||
notices.system_notice_list=Sistēmas paziņojumi
|
||||
notices.type=Veids
|
||||
notices.type_1=Repozitorijs
|
||||
notices.desc=Apraksts
|
||||
notices.op=Op.
|
||||
notices.delete_success=Sistēmas paziņojums tika veiksmīgi izdzēsts.
|
||||
|
||||
[action]
|
||||
create_repo=izveidoja repozitoriju <a href="%s/%s">%s</a>
|
||||
commit_repo=veica izmaiņu nosūtīšanu atzaram <a href="%s/%s/src/%s">%s</a> repozitorijā <a href="%s/%s">%s</a>
|
||||
create_issue=reģistrēja problēmu <a href="%s/%s/issues/%s">%s#%s</a>
|
||||
comment_issue=pievienoja komentāru problēmai <a href="%s/%s/issues/%s">%s#%s</a>
|
||||
transfer_repo=mainīja repozitorija <code>%s</code> īpašnieku uz <a href="/%s%s">%s</a>
|
||||
push_tag=pievienoja birku <a href="%s/%s/src/%s">%s</a> repozitorijam <a href="%s/%s">%s</a>
|
||||
compare_2_commits=Veikt salīdzināšanu starp šīm 2 revīzijām
|
||||
|
||||
[tool]
|
||||
ago=atpakaļ
|
||||
from_now=no šī brīža
|
||||
now=tagad
|
||||
1s=1 sekundi %s
|
||||
1m=1 minūti %s
|
||||
1h=1 stundu %s
|
||||
1d=1 dienu %s
|
||||
1w=1 nedēļu %s
|
||||
1mon=1 mēnesi %s
|
||||
1y=1 gadu %s
|
||||
seconds=%d sekundes %s
|
||||
minutes=%d minūtes %s
|
||||
hours=%d stundas %s
|
||||
days=%d dienas %s
|
||||
weeks=%d nedēļas %s
|
||||
months=%d mēneši %s
|
||||
years=%d gadi %s
|
||||
raw_seconds=sekundes
|
||||
raw_minutes=minūtes
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
718
conf/locale/locale_nl-NL.ini
Executable file
718
conf/locale/locale_nl-NL.ini
Executable file
@@ -0,0 +1,718 @@
|
||||
app_desc=Een pijnloze self-hosted Git-dienst geschreven in Go
|
||||
|
||||
home=Huis
|
||||
dashboard=Dashboard
|
||||
explore=Verkennen
|
||||
help=Help
|
||||
sign_in=Inloggen
|
||||
social_sign_in=Social netwerk inlog: tweede stap <small>account koppelen</small>
|
||||
sign_out=Afmelden
|
||||
sign_up=Aanmelden
|
||||
register=Registreer
|
||||
website=Website
|
||||
version=Versie
|
||||
page=Pagina
|
||||
template=Sjabloon
|
||||
language=Taal
|
||||
|
||||
username=Gebruikersnaam
|
||||
email=E-mail
|
||||
password=Wachttwoord
|
||||
re_type=Verificatie
|
||||
captcha=CAPTCHA
|
||||
|
||||
repository=Repositorie
|
||||
organization=Organisatie
|
||||
mirror=Spiegel
|
||||
new_repo=Nieuwe repositorie
|
||||
new_migrate=Nieuwe migratie
|
||||
new_fork=Nieuwe vork Repository
|
||||
new_org=Nieuwe organisatie
|
||||
manage_org=Beheer organisaties
|
||||
admin_panel=Adminpaneel
|
||||
account_settings=Accountinstellingen
|
||||
settings=Instellingen
|
||||
|
||||
news_feed=Nieuwsfeed
|
||||
pull_requests=Pull-aanvragen
|
||||
issues=Kwesties
|
||||
|
||||
cancel=Annuleer
|
||||
|
||||
[install]
|
||||
install=Installatie
|
||||
title=Installatiestappen voor de eerste keer opstarten
|
||||
requite_db_desc=Om Gogs te gebruiken is MySQL, PostgreSQL of SQLite3 vereist (SQLite3 is beschikbaar in de officiële versie).
|
||||
db_type=Database-type
|
||||
host=Host
|
||||
user=Gebruikersnaam
|
||||
password=Wachtwoord
|
||||
db_name=Database naam
|
||||
db_helper=Gebruik InnoDB engine met utf8_general_ci karakterset voor MySQL.
|
||||
ssl_mode=SSL-modus
|
||||
path=Pad
|
||||
sqlite_helper=Het pad naar de SQLite3 database.
|
||||
general_title=Algemene instellingen van Gogs
|
||||
repo_path=Repositories basis directorie
|
||||
repo_path_helper=Alle remote Git repositories worden in deze directorie opgeslagen
|
||||
run_user=Uitvoerende gebruikersnaam
|
||||
run_user_helper=Deze gebruiker moet toegang hebben tot de git repositorie directorie en moet Gogs kunnen starten
|
||||
domain=Domein
|
||||
domain_helper=Dit heeft invloed op de SSH kloon URLs
|
||||
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
|
||||
email_title=Email service instellingen (Optioneel)
|
||||
smtp_host=SMTP host
|
||||
mailer_user=Afzender e-mail / gebruikersnaam
|
||||
mailer_password=Wachtwoord
|
||||
notify_title=Notificatie-instelligen (optioneel)
|
||||
register_confirm=Activeer registratie emails
|
||||
mail_notify=Activeer e-mailnotificaties
|
||||
admin_title=Instellingen beheerdersaccount
|
||||
admin_name=Gebruikersnaam
|
||||
admin_password=Wachtwoord
|
||||
confirm_password=Verifieer wachtwoord
|
||||
admin_email=E-mailadres
|
||||
install_gogs=Installeer Gogs
|
||||
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.
|
||||
invalid_db_setting=Uw database instellingen zijn niet correct: %v
|
||||
invalid_repo_path=Repositorie basis pad is niet correct: %v
|
||||
run_user_not_match=De uitvoerende gebruiker is niet de huidig gebruiker: %s -> %s
|
||||
save_config_failed=Kan de configuratie niet opslaan: %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
|
||||
|
||||
[home]
|
||||
uname_holder=Gebruikersnaam of e-mail
|
||||
password_holder=Wachtwoord
|
||||
switch_dashboard_context=Wissel voorpaginacontext
|
||||
my_repos=Mijn repositories
|
||||
collaborative_repos=Gedeelde repositories
|
||||
my_orgs=Mijn organisaties
|
||||
my_mirrors=Mijn mirrors
|
||||
|
||||
[explore]
|
||||
repos=Repositories
|
||||
|
||||
[auth]
|
||||
create_new_account=Maak nieuw account aan
|
||||
register_hepler_msg=Heeft u al een account? Meld u nu aan!
|
||||
social_register_hepler_msg=Heeft u al een account? Koppel nu!
|
||||
disable_register_prompt=Sorry, registratie is uitgeschakeld. Neem contact op met de beheerder van deze site.
|
||||
disable_register_mail=Sorry, bevestiging van registratie per e-mail is uitgeschakeld.
|
||||
remember_me=Onthoud mij
|
||||
forgot_password=Wachtwoord vergeten
|
||||
forget_password=Wachtwoord vergeten?
|
||||
sign_up_now=Een account nodig? Meld u nu aan.
|
||||
confirmation_mail_sent_prompt=Een bevestigingsemail is gestuurd naar <b>%s</b>, Bevestig u aanvraag binnen %d uren om uw registratie te voltooien.
|
||||
sign_in_email=Meld u aan met uw e-mailadres
|
||||
active_your_account=Activeer uw account
|
||||
resent_limit_prompt=Sorry, u heeft te snel na elkaar een aanvraag gedaan voor een activatie mail. Wacht drie minuten voor uw volgende aanvraag.
|
||||
has_unconfirmed_mail=Beste %s, u heeft een onbevestigde e-mailadres (<b>%s</b>). Als u nog geen bevestiging per e-mail heeft ontvangen, of u een nieuwe aanvraag wilt doen, klik dan op de onderstaande knop.
|
||||
resend_mail=Klik hier om uw activatie mail nog een keer te verzenden
|
||||
email_not_associate=Dit e-mailadres is niet gekoppeld aan een account.
|
||||
send_reset_mail=Klik hier om uw wachtwoord reset mail (nogmaals) te versturen
|
||||
reset_password=Reset uw wachtwoord
|
||||
invalid_code=Sorry, uw bevestigingscode is verlopen of niet meer geldig.
|
||||
reset_password_helper=Klik hier om uw wachtwoord opnieuw in te stellen.
|
||||
password_too_short=De lengte van uw wachtwoord moet minimaal zes karakters zijn.
|
||||
|
||||
[form]
|
||||
UserName=Gebruikersnaam
|
||||
RepoName=Repositorie naam
|
||||
Email=e-mailadres
|
||||
Password=Wachtwoord
|
||||
Retype=Verifieer wachtwoord
|
||||
SSHTitle=SSH sleutel naam
|
||||
HttpsUrl=HTTPS-URL
|
||||
PayloadUrl=Nettolading URL
|
||||
TeamName=Team naam
|
||||
AuthName=Autorisatienaam
|
||||
AdminEmail=E-mail beheerder
|
||||
|
||||
require_error=kan niet leeg zijn.
|
||||
alpha_dash_error=moet een valide alfanumeriek of dash(-_) karakter zijn.
|
||||
alpha_dash_dot_error=moet een valide alfanumeriek, dash(-_) of (.) punt karakter zijn.
|
||||
min_size_error=moet minimaal %s karakters bevatten.
|
||||
max_size_error=mag maximaal %s karakters bevatten.
|
||||
email_error=is niet een valide e-mail adres.
|
||||
url_error=is niet een valide URL.
|
||||
unknown_error=Onbekende fout:
|
||||
captcha_incorrect=Captcha komt niet overeen.
|
||||
password_not_match=Wachtwoord en verificatie wachtwoord komen niet overeen.
|
||||
|
||||
username_been_taken=Gebruikersnaam is al in gebruik.
|
||||
repo_name_been_taken=Repositorie 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.
|
||||
email_been_used=e-mailadres is al in gebruik.
|
||||
ssh_key_been_used=Openbare sleutel naam is al in gebruik.
|
||||
illegal_username=Gebruikersnaam bevat illegale karakters.
|
||||
illegal_repo_name=Repositorie naam bevat illegale karakters.
|
||||
illegal_org_name=Organisatie naam bevat illegale karakters.
|
||||
illegal_team_name=Team naam bevat illegale karakters.
|
||||
username_password_incorrect=Gebruikersnaam of wachtwoord is niet correct.
|
||||
enterred_invalid_repo_name=U heeft een onjuiste repositorie naam ingevoerd.
|
||||
enterred_invalid_owner_name=U heeft een onjuiste eigenaar ingevoerd.
|
||||
enterred_invalid_password=U heeft een onjuiste wachtwoord ingevoerd.
|
||||
user_not_exist=Gegeven gebruiker bestaat niet.
|
||||
last_org_owner=De gebruiker die u probeert te verwijderen is het enige lid (eigenaar) van dit team. U moet eerst nieuwe lid (eigenaar) aanstellen.
|
||||
|
||||
invalid_ssh_key=Sorry, we zijn niet in staat om uw SSH-sleutel te verifiëren: %s
|
||||
unable_verify_ssh_key=Gogs kan niet controleren of uw SSH-sleutel, maar wij gaan ervan uit dat is geldig, zorg ervoor dat uzelf.
|
||||
auth_failed=Verificatie mislukt: %v
|
||||
|
||||
still_own_repo=Uw account heeft nog een eigendom op een repositorie. U moet deze eerst verwijderen of overdragen.
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
[user]
|
||||
change_avatar=Verander uw avatar op Gravatar.com
|
||||
change_custom_avatar=Change your avatar in settings
|
||||
join_on=Aangemeld op
|
||||
repositories=repositories
|
||||
activity=Openbare activiteit
|
||||
followers=Volgers
|
||||
starred=Sterren
|
||||
following=Volgt
|
||||
|
||||
[settings]
|
||||
profile=Profiel
|
||||
password=Wachtwoord
|
||||
ssh_keys=SSH-sleutels
|
||||
social=Sociale netwerk-accounts
|
||||
applications=Toepassingen
|
||||
orgs=Organisaties
|
||||
delete=Verwijder account
|
||||
uid=uid
|
||||
|
||||
public_profile=Openbaar profiel
|
||||
profile_desc=Uw e-mailadres is openbaar zichtbaar en zal gebruikt worden gebruikt voor alle account gerlateerde berichtgevingen en web bewerking gemaakt via de website.
|
||||
full_name=Volledige naam
|
||||
website=Website
|
||||
location=Locatie
|
||||
update_profile=Profile bijwerken
|
||||
update_profile_success=Uw profiel is succesvol bijgewerkt.
|
||||
change_username=Username veranderd
|
||||
change_username_desc=Gebruikersnaam is gewijzigd. Wilt u doorgaan? Dit zal gevolgen hebben voor alle koppelingen die betrekking hebben op uw account.
|
||||
continue=Doorgaan
|
||||
cancel=Annuleren
|
||||
|
||||
enable_custom_avatar=Enable Custom Avatar
|
||||
enable_custom_avatar_helper=Enable this to disable fetch from Gravatar
|
||||
choose_new_avatar=Choose new avatar
|
||||
update_avatar=Update Avatar Setting
|
||||
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.
|
||||
|
||||
change_password=Verander wachtwoord
|
||||
old_password=Huidige wachtwoord
|
||||
new_password=Nieuw wachtwoord
|
||||
password_incorrect=Huidig wachtwoord is niet correct.
|
||||
change_password_success=Wachtwoord is succesvol gewijzigd. U kunt nu met uw nieuwe wachtwoord inloggen.
|
||||
|
||||
manage_ssh_keys=Beheer SSH sleutels
|
||||
add_key=Sleutel toevoegen
|
||||
ssh_desc=Dit is een lijst van alle SSH sleutels die gekoppeld zijn aan uw account. Verwijder alle sleutels die u niet herkent.
|
||||
ssh_helper=<strong>De behoeftehulp van?</strong> Check out onze gids voor het <a href="%s"> genereren van SSH sleutels</a> of <a href="%s"> algemene SSH</a> problemen.
|
||||
add_new_key=SSH sleutel toevoegen
|
||||
key_name=Sleutel naam
|
||||
key_content=Inhoud
|
||||
add_key_success=Nieuwe SSH sleutel is toegevoegd!
|
||||
delete_key=Verwijder
|
||||
add_on=Toegevoegd op
|
||||
last_used=Laatst gebruikt op
|
||||
no_activity=Geen recente activiteiten
|
||||
|
||||
manage_social=Beheer gekoppelde sociale accounts
|
||||
social_desc=Dit is een lijst van de bijbehorende sociale accounts koppelingen, Verwijder eventueel koppelingen die u niet herkent.
|
||||
unbind=Loskoppelen
|
||||
unbind_success=Sociaal account is ontkoppeld.
|
||||
|
||||
manage_access_token=Persoonlijke toegangstokens beheren
|
||||
generate_new_token=Nieuwe Token genereren
|
||||
tokens_desc=Tokens u hebt gegenereerd die kunnen worden gebruikt voor toegang tot de API Gogs.
|
||||
new_token_desc=Zoals voor nu, moet elke token zal hebben volledige toegang tot uw account.
|
||||
token_name=Symbolische naam
|
||||
generate_token=Token genereren
|
||||
generate_token_succees=Nieuwe toegangstoken is met succes gegenereerd! Zorg ervoor dat uw nieuwe persoonlijke toegangstoken nu kopiëren. U zal niet zitten kundig voor zien het weer!
|
||||
delete_token=Verwijderen
|
||||
delete_token_success=Persoonlijke toegangstoken is met succes verwijderd! Vergeet niet uw toepassingen ook wilt bijwerken.
|
||||
|
||||
delete_account=Verwijder uw account
|
||||
delete_prompt=Deze handeling zal uw account definitief verwijderen, u kunt dit <strong> NIET </strong> terug draaien!
|
||||
confirm_delete_account=Bevestig verwijdering
|
||||
delete_account_title=Account verwijderen
|
||||
delete_account_desc=Dit account zal permanent worden verwijderd. Wilt u doorgaan?
|
||||
|
||||
[repo]
|
||||
owner=Eigenaar
|
||||
repo_name=Repositorie naam
|
||||
repo_name_helper=Een goede repositorie naam is kort, memorabel en <strong>uniek</strong>.
|
||||
visibility=Zichtbaarheid
|
||||
visiblity_helper=Deze repositorie is <span class="label label-red label-radius">prive</span>
|
||||
fork_repo=Vork Repository
|
||||
fork_from=Afsplitsing van
|
||||
fork_visiblity_helper=Gevorkte repository wijzigen zijn bereik potentiële kopers niet
|
||||
repo_desc=Omschrijving
|
||||
repo_lang=Taal
|
||||
repo_lang_helper=Selecteer een .gitignore bestand
|
||||
license=Licentie
|
||||
license_helper=Selecteer een licentie bestand
|
||||
init_readme=Initialiseer deze repositorie met een README.md
|
||||
create_repo=Nieuwe Repositorie
|
||||
default_branch=Standaard branch
|
||||
mirror_interval=Mirror interval(uur)
|
||||
goget_meta=Go-Get Meta
|
||||
goget_meta_helper=Deze repositorie is nu beschikbaar voor <span class="label label-blue label-radius">Go-Get</span>
|
||||
|
||||
need_auth=Autorisatie vereist
|
||||
migrate_type=Migratie type
|
||||
migrate_type_helper=Deze repositorie zal een <span class="label label-blue label-radius">mirror</span> worden
|
||||
migrate_repo=Migreer repositorie
|
||||
|
||||
copy_link=Kopieer
|
||||
click_to_copy=Kopieer link naar plakbord
|
||||
copied=Gekopieerd
|
||||
clone_helper=De behoeftehulp van klonen? Bezoek <a target="_blank" href="%s"> helpen</a>!
|
||||
unwatch=Negeren
|
||||
watch=Volgen
|
||||
unstar=Ontster
|
||||
star=Ster
|
||||
fork=Vork
|
||||
|
||||
no_desc=Geen omschrijving
|
||||
quick_guide=Snelstart gids
|
||||
clone_this_repo=Kloon deze repositorie
|
||||
create_new_repo_command=Maak een nieuwe repositorie aan vanaf de console
|
||||
push_exist_repo=Push een bestaande repositorie vanaf de console
|
||||
|
||||
branch=Aftakking
|
||||
tree=Boom
|
||||
branch_and_tags=Aftakkingen & labels
|
||||
branches=Aftakkingen
|
||||
tags=Labels
|
||||
issues=Kwesties
|
||||
commits=Commits
|
||||
releases=Publicaties
|
||||
file_raw=Ruwe
|
||||
file_history=Geschiedenis
|
||||
file_view_raw=Weergave ruwe
|
||||
|
||||
commits.commits=Commits
|
||||
commits.search=Zoeken
|
||||
commits.find=zoek
|
||||
commits.author=Auteur
|
||||
commits.message=Bericht
|
||||
commits.date=Datum
|
||||
commits.older=Ouder
|
||||
commits.newer=Nieuwer
|
||||
|
||||
settings=Instellingen
|
||||
settings.options=Opties
|
||||
settings.collaboration=Samenwerking
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Git haken
|
||||
settings.deploy_keys=Installeer sleutels
|
||||
settings.basic_settings=Basis instellingen
|
||||
settings.danger_zone=Gevaren zone
|
||||
settings.site=Officiële site
|
||||
settings.update_settings=Instellingen bewerken
|
||||
settings.change_reponame=Repositorienaam aangepast
|
||||
settings.change_reponame_desc=De repositorienaam is veranderd. Wilt u doorgaan? Dit zal gevolgen hebben voor alle koppelingen die betrekking hebben op deze repositorie.
|
||||
settings.transfer=Eigendom overdragen
|
||||
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=<p> - U kan uw toegang verliezen als de nieuwe eigenaar een individuele gebruiker is</p> <p> - . . U zal uw toegang behouden als de nieuwe eigenaar een organisatie is en u één van de eigenaren bent</p>
|
||||
settings.update_settings_success=Repositorie instellingen zijn succesvol bijgewerkt.
|
||||
settings.transfer_owner=Nieuwe eigenaar
|
||||
settings.make_transfer=Maak overdracht
|
||||
settings.transfer_succeed=Eigendom repositorie succesvol overgedragen
|
||||
settings.confirm_delete=Bevestig verwijdering
|
||||
settings.add_collaborator=Nieuwe medewerker toevoegen
|
||||
settings.add_collaborator_success=medewerker is toegevoegd.
|
||||
settings.remove_collaborator_success=medewerker is verwijderd.
|
||||
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.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.githooks_desc=Git haken worden aangedreven door Git zelf, u kunt bestanden van ondersteunde haken in de lijst hieronder om aangepaste acties van toepassing bewerken.
|
||||
settings.githook_edit_desc=Als haak niet actief is, zal monster inhoud worden gepresenteerd. Verlof inhoud leeg zal deze haak uitschakelen.
|
||||
settings.githook_name=Haak naam
|
||||
settings.githook_content=Haak inhoud
|
||||
settings.update_githook=Haak bijwerken
|
||||
settings.remove_hook_success=Webhook is verwijderd.
|
||||
settings.add_webhook_desc=We sturen een <code>POST</code>-aanvraag naar de onderstaande URL met details voor alle geplaatste gebeurtenissen. U kunt ook opgeven welke gegevensindeling u wenst te ontvangen (JSON, <code>x-1-800-www-Dell-vorm-urlencoded</code>, <em>enz</em>.). Meer informatie kan gevonden worden in <a target="_blank" href="%s"> Webhooks gids</a>.
|
||||
settings.payload_url=Nettolading URL
|
||||
settings.content_type=Content type
|
||||
settings.secret=Geheim
|
||||
settings.event_desc=Bij welke gebeurtenissen wilt u dat deze webhook getriggerd wordt?
|
||||
settings.event_push_only=Alleen bij de <code>push</code> event.
|
||||
settings.active=Actief
|
||||
settings.active_helper=We zullen details van de gebeurtenissen af leveren wanneer deze webhook wordt geactiveerd.
|
||||
settings.add_hook_success=Nieuwe webhook toegevoegd.
|
||||
settings.update_webhook=Bewerk webhook
|
||||
settings.update_hook_success=Webhook is bijgewerkt.
|
||||
settings.delete_webhook=Webhook verwijderen
|
||||
settings.recent_deliveries=Recente bezorgingen
|
||||
settings.hook_type=Type hook
|
||||
settings.add_slack_hook_desc=<a href="%s"> toegestane vertraging</a> integratie toevoegen aan uw repository.
|
||||
settings.slack_token=Slack token
|
||||
settings.slack_domain=Slack domein
|
||||
settings.slack_channel=Slack kanaal
|
||||
|
||||
diff.browse_source=Bladeren bron
|
||||
diff.parent=bovenliggende
|
||||
diff.commit=commit
|
||||
diff.data_not_available=Diff gegevens niet beschikbaar.
|
||||
diff.show_diff_stats=Toon Diff Stats
|
||||
diff.stats_desc=<strong>%d gewijzigde bestanden</strong> met <strong>toevoegingen van %d</strong> en <strong>%d verwijderingen</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Bestand weergeven
|
||||
|
||||
release.releases=Releases
|
||||
release.new_release=New Release
|
||||
release.draft=Draft
|
||||
release.prerelease=Pre-Release
|
||||
release.stable=Stable
|
||||
release.edit=edit
|
||||
release.ahead=<strong>%d</strong> commits to %s since this release
|
||||
release.source_code=Source Code
|
||||
release.tag_name=Tag name
|
||||
release.target=Target
|
||||
release.tag_helper=Choose an existing tag, or create a new tag on publish.
|
||||
release.release_title=Release title
|
||||
release.content_with_md=Content with <a href="%s">Markdown</a>
|
||||
release.write=Write
|
||||
release.preview=Preview
|
||||
release.content_placeholder=Write some content
|
||||
release.loading=Loading...
|
||||
release.prerelease_desc=This is a pre-release
|
||||
release.prerelease_helper=We’ll point out that this release is identified as non-production ready.
|
||||
release.publish=Publish Release
|
||||
release.save_draft=Save Draft
|
||||
release.edit_release=Edit Release
|
||||
release.tag_name_already_exist=Release with this tag name has already existed.
|
||||
|
||||
[org]
|
||||
org_name_holder=Organisatienaam
|
||||
org_name_helper=Een goede organisatienaam is kort en memorabel.
|
||||
org_email_helper=Alle notificaties en bevestigingen worden op het e-mailadres van de organisatie ontvangen.
|
||||
create_org=Nieuwe organisatie aanmaken
|
||||
repo_updated=Geupdate
|
||||
people=Mensen
|
||||
invite_someone=Iemand uitnodigen
|
||||
teams=Teams
|
||||
lower_members=leden
|
||||
lower_repositories=repositories
|
||||
create_new_team=Nieuw team aanmaken
|
||||
org_desc=Omschrijving
|
||||
team_name=Teamnaam
|
||||
team_desc=Omschrijving
|
||||
team_name_helper=U gebruikt deze naam om dit team te vermelden in conversaties.
|
||||
team_desc_helper=Waar gaat dit team doen?
|
||||
team_permission_desc=Welke privileges zou dit team moeten hebben?
|
||||
|
||||
settings=Instellingen
|
||||
settings.options=Opties
|
||||
settings.full_name=Volledige naam
|
||||
settings.website=Website
|
||||
settings.location=Locatie
|
||||
settings.update_settings=Instellingen bijwerken
|
||||
settings.change_orgname=Organisatie naam veranderd
|
||||
settings.change_orgname_desc=De naam van de organisatie is veranderd, wilt u doorgaan? Dit zal gevolgen hebben voor alle koppelingen die betrekking hebben op deze organisatie.
|
||||
settings.update_setting_success=Organisatie instellingen zijn succesvol bijgewerkt.
|
||||
settings.delete=Verwijder organisatie
|
||||
settings.delete_account=Verwijder deze organisatie
|
||||
settings.delete_prompt=Deze actie zal de origanisatie permanent verwijderen. U kunt dit <strong>NIET</strong> terug draaien!
|
||||
settings.confirm_delete_account=Bevestig verwijdering
|
||||
settings.delete_org_title=Verwijderen organsiatie
|
||||
settings.delete_org_desc=Deze organisatie zal permanent worden verwijderd, wilt u doorgaan?
|
||||
settings.hooks_desc=Een webhook toevoegen die door <strong>alle repositories</strong> in deze organisatie getriggerd kan worden.
|
||||
|
||||
members.public=Openbaar
|
||||
members.public_helper=maak prive
|
||||
members.private=Prive
|
||||
members.private_helper=maak openbaar
|
||||
members.owner=Eigenaar
|
||||
members.member=Lid
|
||||
members.conceal=Verbergen
|
||||
members.remove=Verwijderen
|
||||
members.leave=Verlaat
|
||||
members.invite_desc=Begin met het typen van een gebruikersnaam om een nieuw lid aan %s uit te nodigen:
|
||||
members.invite_now=Nu uitnodigen
|
||||
|
||||
teams.join=Lid worden
|
||||
teams.leave=Vertlaat
|
||||
teams.read_access=Leestoegang
|
||||
teams.read_access_helper=Dit team is in staat om zijn repositories te bekijken en te klonen.
|
||||
teams.write_access=Schrijf toegang
|
||||
teams.write_access_helper=Dit team is in staat om zijn repositories te bekijken en push aanvragen te verwerken.
|
||||
teams.admin_access=Beheerder toegang
|
||||
teams.admin_access_helper=Dit team is in staat om push & pull aanvragen te verwerken en om nieuwe medewerkers toe te voegen.
|
||||
teams.no_desc=Dit team heeft geen omschrijving
|
||||
teams.settings=Instellingen
|
||||
teams.owners_permission_desc=Eigenaren hebben volledige toegang tot <strong>alle repositories</strong> en hebben <strong>beheerder rechten</strong> over de organisatie.
|
||||
teams.members=Team leden
|
||||
teams.update_settings=Instellingen bijwerken
|
||||
teams.delete_team=Verwijder deze team
|
||||
teams.add_team_member=Nieuwe team lid aanmaken
|
||||
teams.delete_team_title=Team verwijderen
|
||||
teams.delete_team_desc=Dit team zal worden verwijderd. De leden van dit team zullen toegang tot alle repositories van het team verliezen. Wilt u doorgaan?
|
||||
teams.delete_team_success=Gekozen team is succesvol verwijderd.
|
||||
teams.read_permission_desc=Dit team heeft <strong>Lees</strong> rechten : leden kunnen repositories lezen en klonen.
|
||||
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.repositories=Teamrepositories
|
||||
teams.add_team_repository=Nieuwe teamrepositorie aanmaken
|
||||
teams.remove_repo=Verwijder
|
||||
teams.add_nonexistent_repo=De opslagplaats die u probeert toe te voegen niet bestaat, kunt u het eerst aanmaken.
|
||||
|
||||
[admin]
|
||||
dashboard=Dashboard
|
||||
users=Gebruikers
|
||||
organizations=Orgranisaties
|
||||
repositories=Repositories
|
||||
authentication=Autenticaties
|
||||
config=Configuratie
|
||||
notices=Systeem aankondigingen
|
||||
monitor=Bijhouden
|
||||
prev=Vorige
|
||||
next=Volgende
|
||||
|
||||
dashboard.statistic=Statistieken
|
||||
dashboard.operations=Bewerkingen
|
||||
dashboard.system_status=Status Systeemmonitor
|
||||
dashboard.statistic_info=Gogs database heeft <b>%d</b> gebruikers, <b>%d</b> organisaties, <b>%d</b> openbare sleutels, <b>%d</b> repositories, <b>%d</b> volgers, <b>%d</b> sterren, <b>%d</b> acties, <b>%d</b> participanten, <b>%d</b> issues, <b>%d</b> reacties, <b>%d</b> sociale accounten, <b>%d</b> volgers, <b>%d</b> mirrors, <b>%d</b> publicaties, <b>%d</b> login bronnen, <b>%d</b> webhooks, <b>%d</b> mijlpalen, <b>%d</b> labels, <b>%d</b> hook taken, <b>%d</b> teams, <b>%d</b> bijgewerkte taken, <b>%d</b> bijlagen.
|
||||
dashboard.operation_name=Bewerking naam
|
||||
dashboard.operation_switch=Omschakelen
|
||||
dashboard.operation_run=Uitvoeren
|
||||
dashboard.clean_unbind_oauth=Clean unbound OAuths
|
||||
dashboard.clean_unbind_oauth_success=Alle OAuthes binding hebben verwijderd.
|
||||
dashboard.delete_inactivate_accounts=Verwijder alle inactieve accounts
|
||||
dashboard.delete_inactivate_accounts_success=Alle inactivering van rekeningen hebben verwijderd.
|
||||
dashboard.delete_repo_archives=Verwijderen van alle repositories archieven
|
||||
dashboard.delete_repo_archives_success=Alle repositories archieven hebben verwijderd.
|
||||
dashboard.git_gc_repos=Do garbage collection on repositories
|
||||
dashboard.git_gc_repos_success=All repositories have done garbage collection successfully.
|
||||
dashboard.server_uptime=Uptime server
|
||||
dashboard.current_goroutine=Huidige Goroutines
|
||||
dashboard.current_memory_usage=Huidige geheugen gebruik
|
||||
dashboard.total_memory_allocated=Totaal toegewezen geheugen
|
||||
dashboard.memory_obtained=Geheugen gebruikt
|
||||
dashboard.pointer_lookup_times=Aanwijzer Lookup keer
|
||||
dashboard.memory_allocate_times=Geheugen toewijzen keer
|
||||
dashboard.memory_free_times=Geheugen gratis keer
|
||||
dashboard.current_heap_usage=Huidige Heap gebruik
|
||||
dashboard.heap_memory_obtained=Heap-geheugen verkregen
|
||||
dashboard.heap_memory_idle=Heap Memory inactief
|
||||
dashboard.heap_memory_in_use=Hoop geheugen In gebruik
|
||||
dashboard.heap_memory_released=Heap-geheugen vrijgegeven
|
||||
dashboard.heap_objects=Heap-objecten
|
||||
dashboard.bootstrap_stack_usage=Bootstrap Stack gebruik
|
||||
dashboard.stack_memory_obtained=Stapel geheugen verkregen
|
||||
dashboard.mspan_structures_usage=MSpan structuren gebruik
|
||||
dashboard.mspan_structures_obtained=MSpan structuren verkregen
|
||||
dashboard.mcache_structures_usage=MCache structuren gebruik
|
||||
dashboard.mcache_structures_obtained=MCache structuren verkregen
|
||||
dashboard.profiling_bucket_hash_table_obtained=Profilering emmer hashtabel verkregen
|
||||
dashboard.gc_metadata_obtained=GC Metadada verkregen
|
||||
dashboard.other_system_allocation_obtained=Andere systeem toewijzing verkregen
|
||||
dashboard.next_gc_recycle=Volgende GC recycle
|
||||
dashboard.last_gc_time=Sinds vorige GC verwerkingstijd
|
||||
dashboard.total_gc_time=Totaal GC verwerkingstijd
|
||||
dashboard.total_gc_pause=Totaal GC verwerkingstijd
|
||||
dashboard.last_gc_pause=Laatste GC verwerkingstijd
|
||||
dashboard.gc_times=GC verwerkingen
|
||||
|
||||
users.user_manage_panel=Gebruikers beheren
|
||||
users.new_account=Nieuw account aanmaken
|
||||
users.name=Naam
|
||||
users.activated=Geactiveerd
|
||||
users.admin=Admin
|
||||
users.repos=Repos
|
||||
users.created=Aangemaakt
|
||||
users.edit=Bewerken
|
||||
users.auth_source=Autorisatiebron
|
||||
users.local=Lokaal
|
||||
users.auth_login_name=Autorisatie inlognaam
|
||||
users.update_profile_success=Profiel is succesvol bijgewerkt.
|
||||
users.edit_account=Bewerk account
|
||||
users.is_activated=Dit account is geactiveerd
|
||||
users.is_admin=Dit account heeft beheerdersrechten
|
||||
users.allow_git_hook=Deze account beschikt over machtigingen voor het maken van Git haken
|
||||
users.update_profile=Account profiel bijwerken
|
||||
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_has_org=Deze account nog steeds lidmaatschap van organisatie, u hebt naar links of hen eerst verwijderen.
|
||||
|
||||
orgs.org_manage_panel=Organisaties beheren
|
||||
orgs.name=Naam
|
||||
orgs.teams=Teams
|
||||
orgs.members=Leden
|
||||
|
||||
repos.repo_manage_panel=Repositoriebeheerpaneel
|
||||
repos.owner=Eigenaar
|
||||
repos.name=Naam
|
||||
repos.private=Prive
|
||||
repos.watches=Volgers
|
||||
repos.stars=Sterren
|
||||
repos.issues=Kwesties
|
||||
|
||||
auths.auth_manage_panel=Autorisatiebeheerpaneel
|
||||
auths.new=Nieuwe autorisatiebron
|
||||
auths.name=Naam
|
||||
auths.type=Type
|
||||
auths.enabled=Ingeschakeld
|
||||
auths.updated=Bijgewerkt
|
||||
auths.auth_type=Autorisatietype
|
||||
auths.auth_name=Autorisatienaam
|
||||
auths.domain=Domein
|
||||
auths.host=Host
|
||||
auths.port=Poort
|
||||
auths.base_dn=Base DN
|
||||
auths.attributes=Zoek attributen
|
||||
auths.filter=Zoek filter
|
||||
auths.ms_ad_sa=MS Ad SA
|
||||
auths.smtp_auth=SMTP authenticatietype
|
||||
auths.smtphost=SMTP host
|
||||
auths.smtpport=SMTP poort
|
||||
auths.enable_tls=Activeer TLS-encryptie
|
||||
auths.enable_auto_register=Activeer automatische registratie
|
||||
auths.tips=Tips
|
||||
auths.edit=Bewerk autorisatie-instellingen
|
||||
auths.activated=Deze autorisatiemethode is geactiveerd
|
||||
auths.update_success=Autorisatie-instellingen zijn succesvol bijgewerkt.
|
||||
auths.update=Update autorisatie-instellingen
|
||||
auths.delete=Verwijder deze autorisatie
|
||||
auths.delete_auth_title=Verwijderings-autorisatie
|
||||
auths.delete_auth_desc=Deze autorisatiemethode wordt verwijderd. Weet u zeker dat u wilt doorgaan?
|
||||
|
||||
config.server_config=Serverconfiguratie
|
||||
config.app_name=Applicatienaam
|
||||
config.app_ver=Applicatieversie
|
||||
config.app_url=Applicatie-URL
|
||||
config.domain=Domein
|
||||
config.offline_mode=Offline-modus
|
||||
config.disable_router_log=Router-log uitschakelen
|
||||
config.run_user=Uitvoerende gebruiker
|
||||
config.run_mode=Uitvoer modus
|
||||
config.repo_root_path=Repositorie basis pad
|
||||
config.static_file_root_path=Statische bestanden basis pad
|
||||
config.log_file_root_path=Log bestand basis pad
|
||||
config.script_type=Script type
|
||||
config.reverse_auth_user=Omgekeerde verificatie gebruiker
|
||||
config.db_config=Databaseconfiguratie
|
||||
config.db_type=Type
|
||||
config.db_host=Host
|
||||
config.db_name=Naam
|
||||
config.db_user=Gebruiker
|
||||
config.db_ssl_mode=SSL modus
|
||||
config.db_ssl_mode_helper=(alleen voor "postgres")
|
||||
config.db_path=Pad
|
||||
config.db_path_helper=(alleen voor "sqlite3")
|
||||
config.service_config=Serviceconfiguratie
|
||||
config.register_email_confirm=E-mailbevestiging registreren
|
||||
config.disable_register=Registratie uitgeschakeld
|
||||
config.require_sign_in_view=Inloggen vereist om te kunnen inzien
|
||||
config.mail_notify=E-mailnotificaties
|
||||
config.enable_cache_avatar=Avatar Cache inschakelen
|
||||
config.active_code_lives=Actieve Code leven
|
||||
config.reset_password_code_lives=Reset wachtwoord Code leven
|
||||
config.webhook_config=Webhook configuratie
|
||||
config.task_interval=Taakinterval
|
||||
config.deliver_timeout=Bezorging verlooptijd
|
||||
config.mailer_config=Mailerconfiguatie
|
||||
config.mailer_enabled=Ingeschakeld
|
||||
config.mailer_name=Naam
|
||||
config.mailer_host=Host
|
||||
config.mailer_user=Gebruiker
|
||||
config.oauth_config=OAuth-configuratie
|
||||
config.oauth_enabled=Ingeschakeld
|
||||
config.cache_config=Cache-configuratie
|
||||
config.cache_adapter=Cache-adapter
|
||||
config.cache_interval=Cache-interval
|
||||
config.cache_conn=Cache-connectie
|
||||
config.session_config=Sessieconfiguratie
|
||||
config.session_provider=Sessieprovider
|
||||
config.provider_config=Provider config
|
||||
config.cookie_name=Cookie naam
|
||||
config.enable_set_cookie=Set Cookie inschakelen
|
||||
config.gc_interval_time=GC interval time
|
||||
config.session_life_time=Sessie duur
|
||||
config.https_only=Alleen HTTPS
|
||||
config.cookie_life_time=Cookie duur leeftijd
|
||||
config.picture_config=Foto configuratie
|
||||
config.picture_service=Foto service
|
||||
config.disable_gravatar=Gravatar uitschakelen
|
||||
config.log_config=Logconfiguratie
|
||||
config.log_mode=Log-modus
|
||||
|
||||
monitor.cron=Cron-taken
|
||||
monitor.name=Naam
|
||||
monitor.schedule=Planning
|
||||
monitor.next=Volgende
|
||||
monitor.previous=Vorige
|
||||
monitor.execute_times=Aantal keren uitgevoerd
|
||||
monitor.process=Draaiende processen
|
||||
monitor.desc=Omschrijving
|
||||
monitor.start=Starttijd
|
||||
monitor.execute_time=Uitvoertijd
|
||||
|
||||
notices.system_notice_list=Systeem aankondigingen
|
||||
notices.type=Type
|
||||
notices.type_1=Opslagplaats
|
||||
notices.desc=Beschrijving
|
||||
notices.op=Op.
|
||||
notices.delete_success=Systeem bericht is met succes verwijderd.
|
||||
|
||||
[action]
|
||||
create_repo=repositorie aangemaakt in <a href="%s/%s">%s</a>
|
||||
commit_repo=push update naar <a href="%s/%s/src/%s">%s</a> in <a href="%s/%s">%s</a
|
||||
create_issue=opende issue in <a href="%s/%s/issues/%s">%s#%s</a>
|
||||
comment_issue=reactie op issue <a href="%s/%s/issues/%s">%s#%s</a>
|
||||
transfer_repo=repositorie verplaatst naar <code>%s</code> naar <a href="/%s%s">%s</a>
|
||||
push_tag=geduwd label <a href="%s/%s/src/%s"> %s</a> naar <a href="%s/%s"> %s</a>
|
||||
compare_2_commits=Weergave vergelijking voor deze 2 commits
|
||||
|
||||
[tool]
|
||||
ago=geleden
|
||||
from_now=vanaf nu
|
||||
now=nu
|
||||
1s=1 seconde %s
|
||||
1m=1 minuut %s
|
||||
1h=1 uur %s
|
||||
1d=1 dag %s
|
||||
1w=1 week %s
|
||||
1mon=1 maand %s
|
||||
1y=1 jaar %s
|
||||
seconds=%d seconden %s
|
||||
minutes=%d minuten %s
|
||||
hours=%d uur %s
|
||||
days=%d dagen %s
|
||||
weeks=%d weken %s
|
||||
months=%d maanden %s
|
||||
years=%d jaren %s
|
||||
raw_seconds=seconden
|
||||
raw_minutes=minuten
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1296
conf/locale/locale_zh-CN.ini
Normal file → Executable file
1296
conf/locale/locale_zh-CN.ini
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
718
conf/locale/locale_zh-HK.ini
Executable file
718
conf/locale/locale_zh-HK.ini
Executable file
@@ -0,0 +1,718 @@
|
||||
app_desc=基於 Go 語言的自助 Git 服務
|
||||
|
||||
home=首頁
|
||||
dashboard=控制面版
|
||||
explore=探索
|
||||
help=幫助
|
||||
sign_in=登錄
|
||||
social_sign_in=社交帳號登錄:第 2 步 <small>關聯帳號</small>
|
||||
sign_out=退出
|
||||
sign_up=註冊
|
||||
register=註冊
|
||||
website=官方網站
|
||||
version=當前版本
|
||||
page=頁面
|
||||
template=模版
|
||||
language=語言選項
|
||||
|
||||
username=用戶名
|
||||
email=郵箱
|
||||
password=密碼
|
||||
re_type=確認密碼
|
||||
captcha=驗證碼
|
||||
|
||||
repository=倉庫
|
||||
organization=組織
|
||||
mirror=鏡像
|
||||
new_repo=創建新的倉庫
|
||||
new_migrate=遷移外部倉庫
|
||||
new_fork=新的派生倉庫
|
||||
new_org=創建新的組織
|
||||
manage_org=管理我的組織
|
||||
admin_panel=管理面版
|
||||
account_settings=帳戶設置
|
||||
settings=帳戶設置
|
||||
|
||||
news_feed=最新活動
|
||||
pull_requests=合併請求
|
||||
issues=問題管理
|
||||
|
||||
cancel=取消
|
||||
|
||||
[install]
|
||||
install=安裝頁面
|
||||
title=首次執行安裝程序
|
||||
requite_db_desc=Gogs 允許後端數據庫為 MySQL、PostgreSQL 或 SQLite3,但是 SQLite3 一般只有官方二進制發行版才支持。
|
||||
db_type=數據庫類型
|
||||
host=數據庫主機
|
||||
user=數據庫用戶
|
||||
password=數據庫用戶密碼
|
||||
db_name=數據庫名稱
|
||||
db_helper=如果您使用 MySQL,請使用 INNODB 引擎以及 utf8_general_ci 字符集。
|
||||
ssl_mode=SSL 模式
|
||||
path=數據庫文件路徑
|
||||
sqlite_helper=SQLite3 數據庫的文件路徑。
|
||||
general_title=應用基本設置
|
||||
repo_path=倉庫根目錄
|
||||
repo_path_helper=所有 Git 遠程倉庫都將被存放於該目錄。
|
||||
run_user=執行系統用戶
|
||||
run_user_helper=該用戶必須具有對倉庫根目錄和執行 Gogs 的操作權限。
|
||||
domain=域名
|
||||
domain_helper=該設置影響 SSH 復製地址。
|
||||
app_url=應用 URL
|
||||
app_url_helper=該設置影響 HTTP/HTTPS 復製地址和一些郵箱中的連結。
|
||||
email_title=郵件服務設置(可選)
|
||||
smtp_host=SMTP 主機
|
||||
mailer_user=發送郵箱
|
||||
mailer_password=發送郵箱密碼
|
||||
notify_title=通知提醒設置(可選)
|
||||
register_confirm=啟用註冊郵箱確認
|
||||
mail_notify=啟用郵件通知提醒
|
||||
admin_title=管理員帳號設置
|
||||
admin_name=管理員用戶名
|
||||
admin_password=管理員密碼
|
||||
confirm_password=確認密碼
|
||||
admin_email=管理員郵箱
|
||||
install_gogs=立即安裝
|
||||
test_git_failed=無法識別 'git' 命令:%v
|
||||
sqlite3_not_available=您所使用的發行版本不支持 SQLite3,請從 %s 下載官方構建版,而不是 gobuild 版本。
|
||||
invalid_db_setting=數據庫設置不正確:%v
|
||||
invalid_repo_path=倉庫根目錄設置不正確:%v
|
||||
run_user_not_match=執行系統用戶非當前用戶:%s -> %s
|
||||
save_config_failed=應用配置保存失敗:%v
|
||||
invalid_admin_setting=管理員帳戶設置不正確:%v
|
||||
install_success=您好!我們很高興您選擇使用 Gogs,祝您使用愉快,代碼從此無 BUG!
|
||||
|
||||
[home]
|
||||
uname_holder=用戶名或郵箱
|
||||
password_holder=密碼
|
||||
switch_dashboard_context=切換控制面版用戶
|
||||
my_repos=我的倉庫
|
||||
collaborative_repos=參與協作的倉庫
|
||||
my_orgs=我的組織
|
||||
my_mirrors=我的鏡像
|
||||
|
||||
[explore]
|
||||
repos=探索倉庫
|
||||
|
||||
[auth]
|
||||
create_new_account=創建帳戶
|
||||
register_hepler_msg=已經註冊?立即登錄!
|
||||
social_register_hepler_msg=已經註冊?立即綁定!
|
||||
disable_register_prompt=對不起,註冊功能已被關閉。請聯系網站管理員。
|
||||
disable_register_mail=對不起,註冊郵箱確認功能已被關閉。
|
||||
remember_me=記住登錄
|
||||
forgot_password=忘記密碼
|
||||
forget_password=忘記密碼?
|
||||
sign_up_now=還沒帳戶?馬上註冊。
|
||||
confirmation_mail_sent_prompt=一封新的確認郵件已經被發送至 <b>%s</b>,請檢查您的收件箱並在 %d 小時內完成確認註冊操作。
|
||||
sign_in_email=登錄到您的郵箱
|
||||
active_your_account=激活您的帳戶
|
||||
resent_limit_prompt=對不起,您請求發送激活郵件過於頻繁,請等待 3 分鐘後再試!
|
||||
has_unconfirmed_mail=%s 您好,系統檢測到您有一封發送至 <b>%s</b> 但未被確認的郵件。如果您未收到激活郵件,或需要重新發送,請單擊下方的按鈕。
|
||||
resend_mail=單擊此處重新發送確認郵件
|
||||
email_not_associate=您輸入的郵箱地址未被關聯到任何帳號!
|
||||
send_reset_mail=單擊此處(重新)發送您的密碼重置郵件
|
||||
reset_password=重置密碼
|
||||
invalid_code=對不起,您的確認代碼已過期或已失效。
|
||||
reset_password_helper=單擊此處重置密碼
|
||||
password_too_short=密碼長度不能少於 6 位!
|
||||
|
||||
[form]
|
||||
UserName=用戶名
|
||||
RepoName=倉庫名稱
|
||||
Email=郵箱地址
|
||||
Password=密碼
|
||||
Retype=確認密碼
|
||||
SSHTitle=SSH 密鑰名稱
|
||||
HttpsUrl=HTTPS URL 地址
|
||||
PayloadUrl=推送地址
|
||||
TeamName=團隊名稱
|
||||
AuthName=認證名稱
|
||||
AdminEmail=管理員郵箱
|
||||
|
||||
require_error=不能為空。
|
||||
alpha_dash_error=必須為英文字母、阿拉伯數字或橫線(-_)。
|
||||
alpha_dash_dot_error=必須為英文字母、阿拉伯數字、橫線(-_)或點。
|
||||
min_size_error=長度最小為 %s 個字符。
|
||||
max_size_error=長度最大為 %s 個字符。
|
||||
email_error=不是一個有效的郵箱地址。
|
||||
url_error=不是一個有效的 URL。
|
||||
unknown_error=未知錯誤:
|
||||
captcha_incorrect=驗證碼未匹配。
|
||||
password_not_match=密碼與確認密碼未匹配。
|
||||
|
||||
username_been_taken=用戶名已經被佔用。
|
||||
repo_name_been_taken=倉庫名稱已經被佔用。
|
||||
org_name_been_taken=組織名稱已經被佔用。
|
||||
team_name_been_taken=團隊名稱已經被佔用。
|
||||
email_been_used=郵箱地址已經被使用。
|
||||
ssh_key_been_used=SSH 密鑰已經被使用。
|
||||
illegal_username=您的用戶名包含不合法字符。
|
||||
illegal_repo_name=倉庫名稱包含不合法字符。
|
||||
illegal_org_name=組織名稱包含不合法字符。
|
||||
illegal_team_name=團隊名稱包含不合法字符。
|
||||
username_password_incorrect=用戶名或密碼不正確。
|
||||
enterred_invalid_repo_name=請檢查您輸入的倉庫名稱是正確。
|
||||
enterred_invalid_owner_name=請檢查您輸入的新所有者用戶名是否正確。
|
||||
enterred_invalid_password=請檢查您輸入的密碼是否正確。
|
||||
user_not_exist=被操作的用戶不存在!
|
||||
last_org_owner=被移除用戶為最後一位管理員。請添加一位新的管理員再進行移除成員操作!
|
||||
|
||||
invalid_ssh_key=很抱歉,我們無法驗證您輸入的 SSH 密鑰:%s
|
||||
unable_verify_ssh_key=Gogs 無法驗證您輸入的 SSH 密鑰,但我們假設那是有效的密鑰,請您自行確保其有效性!
|
||||
auth_failed=授權驗證失敗:%v
|
||||
|
||||
still_own_repo=您的帳戶仍然是某些倉庫的擁有者,您必須先轉移或刪除它們才能執行刪除帳戶操作!
|
||||
still_has_org=您的帳戶仍舊是某些組織的成員,您必須先離開或刪除組織。
|
||||
org_still_own_repo=該組織仍然是某些倉庫的擁有者,您必須先轉移或刪除它們才能執行刪除組織操作!
|
||||
|
||||
still_own_user=該授權認證依舊被部分用戶使用,請先刪除該部分用戶後再試!
|
||||
|
||||
target_branch_not_exist=目標分支不存在
|
||||
|
||||
[user]
|
||||
change_avatar=到 gravatar.com 上修改您的頭像
|
||||
change_custom_avatar=到個人設置中修改頭像
|
||||
join_on=加入於
|
||||
repositories=倉庫列表
|
||||
activity=公開活動
|
||||
followers=關註者
|
||||
starred=已讚好
|
||||
following=關註中
|
||||
|
||||
[settings]
|
||||
profile=個人信息
|
||||
password=修改密碼
|
||||
ssh_keys=管理 SSH 密鑰
|
||||
social=社交帳號綁定
|
||||
applications=管理授權應用
|
||||
orgs=管理組織
|
||||
delete=刪除帳戶
|
||||
uid=用戶 ID
|
||||
|
||||
public_profile=公開信息
|
||||
profile_desc=您的郵箱地址將會被公開,並被用於接收帳戶的所有提醒和通知。
|
||||
full_name=自定義名稱
|
||||
website=個人網站
|
||||
location=所在地區
|
||||
update_profile=更新信息
|
||||
update_profile_success=您的個人信息更新成功!
|
||||
change_username=用戶名將被修改
|
||||
change_username_desc=用戶名被修改,您確定要繼續操作嗎?這將會影響到所有與您帳戶有關的連結。
|
||||
continue=繼續操作
|
||||
cancel=取消操作
|
||||
|
||||
enable_custom_avatar=啟動自定義頭像
|
||||
enable_custom_avatar_helper=激活該選項來禁止從 Gravatar 獲取頭像
|
||||
choose_new_avatar=選擇新的頭像
|
||||
update_avatar=更新頭像設置
|
||||
uploaded_avatar_not_a_image=上傳的文件不是一張圖片!
|
||||
no_custom_avatar_available=沒有任何自定義頭像,無法激活該選項。
|
||||
update_avatar_success=您的頭像設置更新成功!
|
||||
|
||||
change_password=修改密碼
|
||||
old_password=當前密碼
|
||||
new_password=新的密碼
|
||||
password_incorrect=當前密碼不正確!
|
||||
change_password_success=密碼修改成功!您現在可以使用新的密碼登錄。
|
||||
|
||||
manage_ssh_keys=管理 SSH 密鑰
|
||||
add_key=增加密鑰
|
||||
ssh_desc=以下是與您帳戶所關聯的 SSH 密鑰,如果您發現有陌生的密鑰,請立即刪除它!
|
||||
ssh_helper=<strong>需要幫助嗎?</strong> 請查看有關 <a href="%s"> 如何生成 SSH 密鑰</a> 的指南或 <a href="%s"> SSH 的常見問題</a> 的疑難排解。
|
||||
add_new_key=增加 SSH 密鑰
|
||||
key_name=密鑰名稱
|
||||
key_content=密鑰內容
|
||||
add_key_success=新的 SSH 密鑰添加成功!
|
||||
delete_key=刪除
|
||||
add_on=增加於
|
||||
last_used=上次使用在
|
||||
no_activity=沒有最近活動
|
||||
|
||||
manage_social=管理關聯社交帳戶
|
||||
social_desc=以下是與您帳戶所關聯的社交帳號,如果您發現有陌生的關聯,請立即解除綁定!
|
||||
unbind=解除綁定
|
||||
unbind_success=社交帳號解除綁定成功!
|
||||
|
||||
manage_access_token=管理個人操作令牌
|
||||
generate_new_token=生成新的令牌
|
||||
tokens_desc=您可以使用這些已生成的令牌來操作 Gogs API。
|
||||
new_token_desc=目前為止,任何令牌都對您的帳戶擁有完整的操作權限。
|
||||
token_name=令牌名稱
|
||||
generate_token=生成令牌
|
||||
generate_token_succees=新的操作令牌生成成功!您必須立即復製到一個安全的地方,因為該令牌只會顯示一次!
|
||||
delete_token=删除令牌
|
||||
delete_token_success=個人操作令牌刪除成功!請更新與該令牌有關的所有應用。
|
||||
|
||||
delete_account=刪除當前帳戶
|
||||
delete_prompt=刪除操作會永久清除您的帳戶信息,並且 <strong>不可恢復</strong>!
|
||||
confirm_delete_account=確認刪除帳戶
|
||||
delete_account_title=帳戶刪除操作
|
||||
delete_account_desc=該帳戶將被永久性刪除,您確定要繼續操作嗎?
|
||||
|
||||
[repo]
|
||||
owner=擁有者
|
||||
repo_name=倉庫名稱
|
||||
repo_name_helper=偉大的倉庫名稱一般都較短、令人深刻並且 <strong>獨一無二</strong> 的。
|
||||
visibility=可見度
|
||||
visiblity_helper=本倉庫將是 <span class="label label-red label-radius">私有的</span>
|
||||
fork_repo=派生倉庫
|
||||
fork_from=派生自
|
||||
fork_visiblity_helper=派生倉庫無法修改可見性。
|
||||
repo_desc=倉庫描述
|
||||
repo_lang=倉庫語言
|
||||
repo_lang_helper=請選擇 .gitignore 文件
|
||||
license=授權許可
|
||||
license_helper=請選擇授權許可文件
|
||||
init_readme=使用 README.md 文件初始化倉庫
|
||||
create_repo=創建倉庫
|
||||
default_branch=默認分支
|
||||
mirror_interval=鏡像同步周期(小時)
|
||||
goget_meta=Go-Get 支持
|
||||
goget_meta_helper=本倉庫將可以通過 <span class="label label-blue label-radius">Go Get</span> 獲取
|
||||
|
||||
need_auth=需要授權驗證
|
||||
migrate_type=遷移類型
|
||||
migrate_type_helper=本倉庫將是 <span class="label label-blue label-radius">鏡像</span>
|
||||
migrate_repo=遷移倉庫
|
||||
|
||||
copy_link=復製連結
|
||||
click_to_copy=復製到剪切簿
|
||||
copied=復製成功
|
||||
clone_helper=不知道如何操作?訪問 <a target="_blank"href="%s"> 帮助説明</a> !
|
||||
unwatch=取消關註
|
||||
watch=關註
|
||||
unstar=取消讚好
|
||||
star=讚好
|
||||
fork=派生
|
||||
|
||||
no_desc=暫無描述
|
||||
quick_guide=快速幫助
|
||||
clone_this_repo=復製當前倉庫
|
||||
create_new_repo_command=從命令行創建一個新的倉庫
|
||||
push_exist_repo=從命令行推送已經創建的倉庫
|
||||
|
||||
branch=分支
|
||||
tree=目錄樹
|
||||
branch_and_tags=分支與標籤
|
||||
branches=分支列表
|
||||
tags=標籤列表
|
||||
issues=問題管理
|
||||
commits=提交歷史
|
||||
releases=版本發佈
|
||||
file_raw=原始文件
|
||||
file_history=文件歷史
|
||||
file_view_raw=查看原始文件
|
||||
|
||||
commits.commits=次代碼提交
|
||||
commits.search=搜索提交歷史
|
||||
commits.find=查找
|
||||
commits.author=作者
|
||||
commits.message=備註
|
||||
commits.date=提交日期
|
||||
commits.older=更舊的提交
|
||||
commits.newer=更新的提交
|
||||
|
||||
settings=倉庫設置
|
||||
settings.options=基本設置
|
||||
settings.collaboration=管理協作者
|
||||
settings.hooks=管理 Web 鉤子
|
||||
settings.githooks=管理 Git 鉤子
|
||||
settings.deploy_keys=管理部署密鑰
|
||||
settings.basic_settings=基本設置
|
||||
settings.danger_zone=危險操作區
|
||||
settings.site=官方網站
|
||||
settings.update_settings=更新倉庫設置
|
||||
settings.change_reponame=倉庫名稱將被修改
|
||||
settings.change_reponame_desc=倉庫名稱被修改,您確定要繼續操作嗎?這將會影響到所有與該倉庫有關的連結。
|
||||
settings.transfer=轉移倉庫所有權
|
||||
settings.transfer_desc=您可以將倉庫轉移至您擁有管理員權限的帳戶或組織。
|
||||
settings.new_owner_has_same_repo=新的倉庫擁有者已經存在同名倉庫!
|
||||
settings.delete=刪除本倉庫
|
||||
settings.delete_desc=刪除倉庫操作不可逆轉,請三思而後行。
|
||||
settings.transfer_notices=<p>- 如果您轉移給個人用戶,您將對倉庫失去所有權限。</p><p>- 如果您轉移給您作為擁有者的組織,則可繼續保持操作權限。</p>
|
||||
settings.update_settings_success=倉庫設置更新成功!
|
||||
settings.transfer_owner=新擁有者
|
||||
settings.make_transfer=確認轉移倉庫
|
||||
settings.transfer_succeed=倉庫所有權轉移成功!
|
||||
settings.confirm_delete=確認刪除倉庫
|
||||
settings.add_collaborator=增加新的協作者
|
||||
settings.add_collaborator_success=成功添加新的協作者!
|
||||
settings.remove_collaborator_success=被操作的協作者已經被收回權限!
|
||||
settings.user_is_org_member=被操作的用戶是組織成員,因此無法添加為協作者!
|
||||
settings.add_webhook=添加 Web 鉤子
|
||||
settings.hooks_desc=Web 鉤子允許您設定在 Gogs 上發生指定事件時對指定 URL 發送 POST 通知。查看 <a target="_blank" href="%s">Webhooks 文檔</a> 獲取更多信息。
|
||||
settings.githooks_desc=Git 鉤子是由 Git 本身提供的功能,以下為 Gogs 所支持的鉤子列表。
|
||||
settings.githook_edit_desc=如果鉤子未啟動,則會顯示樣例文件中的內容。如果想要刪除某個鉤子,則提交空白文本即可。
|
||||
settings.githook_name=鉤子名稱
|
||||
settings.githook_content=鉤子文本
|
||||
settings.update_githook=更新鉤子設置
|
||||
settings.remove_hook_success=Web 鉤子刪除成功!
|
||||
settings.add_webhook_desc=我們會通過 <code>POST</code> 請求將訂閱事件信息發送至向指定 URL 地址。您可以設置不同的數據接收方式(JSON 或 <code>x-www-form-urlencoded</code>)。 請查閱 <a target="_blank" href="%s">Webhooks 指南</a>.
|
||||
settings.payload_url=推送地址
|
||||
settings.content_type=數據格式
|
||||
settings.secret=密鑰文本
|
||||
settings.event_desc=請設置您希望觸發 Web 鉤子的事件:
|
||||
settings.event_push_only=只推送 <code>push</code> 事件。
|
||||
settings.active=是否激活
|
||||
settings.active_helper=當指定事件發生時我們將會觸發此 Web 鉤子。
|
||||
settings.add_hook_success=Web 鉤子添加成功!
|
||||
settings.update_webhook=更新 Web 鉤子
|
||||
settings.update_hook_success=Web 鉤子更新成功!
|
||||
settings.delete_webhook=刪除 Web 鉤子
|
||||
settings.recent_deliveries=最近推送記錄
|
||||
settings.hook_type=鉤子類型
|
||||
settings.add_slack_hook_desc=為您的倉庫增加 <a href="%s">Slack</a> 集成。
|
||||
settings.slack_token=令牌
|
||||
settings.slack_domain=域名
|
||||
settings.slack_channel=頻道
|
||||
|
||||
diff.browse_source=瀏覽代碼
|
||||
diff.parent=父節點
|
||||
diff.commit=當前提交
|
||||
diff.data_not_available=暫無可用數據
|
||||
diff.show_diff_stats=顯示文件統計
|
||||
diff.stats_desc=共有 <strong> %d 個文件被更改</strong>,包括 <strong>%d 次插入</strong> 和 <strong>%d 次删除</strong>
|
||||
diff.bin=二進制
|
||||
diff.view_file=查看文件
|
||||
|
||||
release.releases=Releases
|
||||
release.new_release=New Release
|
||||
release.draft=Draft
|
||||
release.prerelease=Pre-Release
|
||||
release.stable=Stable
|
||||
release.edit=edit
|
||||
release.ahead=<strong>%d</strong> commits to %s since this release
|
||||
release.source_code=Source Code
|
||||
release.tag_name=Tag name
|
||||
release.target=Target
|
||||
release.tag_helper=Choose an existing tag, or create a new tag on publish.
|
||||
release.release_title=Release title
|
||||
release.content_with_md=Content with <a href="%s">Markdown</a>
|
||||
release.write=Write
|
||||
release.preview=Preview
|
||||
release.content_placeholder=Write some content
|
||||
release.loading=Loading...
|
||||
release.prerelease_desc=This is a pre-release
|
||||
release.prerelease_helper=We’ll point out that this release is identified as non-production ready.
|
||||
release.publish=Publish Release
|
||||
release.save_draft=Save Draft
|
||||
release.edit_release=Edit Release
|
||||
release.tag_name_already_exist=Release with this tag name has already existed.
|
||||
|
||||
[org]
|
||||
org_name_holder=組織名稱
|
||||
org_name_helper=偉大的組織都有一個簡短而寓意深刻的名字。
|
||||
org_email_helper=組織的郵箱用於接收所有通知和確認郵件。
|
||||
create_org=創建組織
|
||||
repo_updated=最後更新於
|
||||
people=組織成員
|
||||
invite_someone=邀請他人加入
|
||||
teams=組織團隊
|
||||
lower_members=名成員
|
||||
lower_repositories=個倉庫
|
||||
create_new_team=創建新的團隊
|
||||
org_desc=組織描述
|
||||
team_name=團隊名稱
|
||||
team_desc=團隊描述
|
||||
team_name_helper=您可以使用該名稱來通知改組全體成員。
|
||||
team_desc_helper=一句話描述這個團隊是做什麼的。
|
||||
team_permission_desc=請選擇該團隊所具有的權限等級:
|
||||
|
||||
settings=組織設置
|
||||
settings.options=基本設置
|
||||
settings.full_name=組織全名
|
||||
settings.website=官方網站
|
||||
settings.location=所在地區
|
||||
settings.update_settings=更新組織設置
|
||||
settings.change_orgname=組織名稱將被修改
|
||||
settings.change_orgname_desc=組織名稱被修改,您確定要繼續操作嗎?這將會影響到所有與該組織有關的連結。
|
||||
settings.update_setting_success=組織設置更新成功!
|
||||
settings.delete=刪除組織
|
||||
settings.delete_account=刪除當前組織
|
||||
settings.delete_prompt=刪除操作會永久清除該組織的信息,並且 <strong>不可恢復</strong>!
|
||||
settings.confirm_delete_account=確認刪除組織
|
||||
settings.delete_org_title=組織刪除操作
|
||||
settings.delete_org_desc=該組織將被永久性刪除,您確定要繼續操作嗎?
|
||||
settings.hooks_desc=在此處添加的 Web 鉤子將會應用到該組織下的 <strong>所有倉庫</strong>。
|
||||
|
||||
members.public=公開成員
|
||||
members.public_helper=設為私有
|
||||
members.private=私有成員
|
||||
members.private_helper=設為公開
|
||||
members.owner=管理員
|
||||
members.member=普通成員
|
||||
members.conceal=隱藏身份
|
||||
members.remove=移除成員
|
||||
members.leave=離開組織
|
||||
members.invite_desc=請輸入被邀請到組織 %s 的用戶名稱:
|
||||
members.invite_now=立即邀請
|
||||
|
||||
teams.join=加入團隊
|
||||
teams.leave=離開團隊
|
||||
teams.read_access=讀取權限
|
||||
teams.read_access_helper=這個團隊將擁有查看和復製所屬倉庫的權限。
|
||||
teams.write_access=寫入權限
|
||||
teams.write_access_helper=這個團隊將擁有查看、復製和推送所屬倉庫的權限。
|
||||
teams.admin_access=管理權限
|
||||
teams.admin_access_helper=這個團隊將擁有查看、復製、推送和添加其他組織成員到團隊的權限。
|
||||
teams.no_desc=該團隊暫無描述
|
||||
teams.settings=團隊設置
|
||||
teams.owners_permission_desc=管理員團隊對 <strong>所有倉庫</strong> 具有操作權限,且對組織具有 <strong>管理員權限</strong>。
|
||||
teams.members=團隊成員
|
||||
teams.update_settings=更新團隊設置
|
||||
teams.delete_team=刪除當前團隊
|
||||
teams.add_team_member=添加團隊成員
|
||||
teams.delete_team_title=團隊刪除操作
|
||||
teams.delete_team_desc=刪除操作會永久清除有關該團隊的信息,您確定要繼續操作嗎?團隊成員可能會失去對某些倉庫的操作權限。
|
||||
teams.delete_team_success=指定團隊刪除成功!
|
||||
teams.read_permission_desc=該團隊擁有對所屬倉庫的 <strong>讀取</strong> 權限,團隊成員可以進行查看和復製等只讀操作。
|
||||
teams.write_permission_desc=該團隊擁有對所屬倉庫的 <strong>讀取</strong> 和 <strong>寫入</strong> 的權限。
|
||||
teams.admin_permission_desc=該團隊擁有一定的 <strong>管理</strong> 權限,團隊成員可以讀取、復製、推送以及添加其它倉庫協作者。
|
||||
teams.repositories=團隊倉庫
|
||||
teams.add_team_repository=添加團隊倉庫
|
||||
teams.remove_repo=移除倉庫
|
||||
teams.add_nonexistent_repo=您嘗試添加到團隊的倉庫不存在,請先創建倉庫!
|
||||
|
||||
[admin]
|
||||
dashboard=控制面版
|
||||
users=用戶管理
|
||||
organizations=組織管理
|
||||
repositories=倉庫管理
|
||||
authentication=授權認證管理
|
||||
config=應用配置管理
|
||||
notices=系統提示管理
|
||||
monitor=應用監控面版
|
||||
prev=上一頁
|
||||
next=下一頁
|
||||
|
||||
dashboard.statistic=應用統計數據
|
||||
dashboard.operations=管理員操作
|
||||
dashboard.system_status=系統監視狀態
|
||||
dashboard.statistic_info=Gogs 數據庫統計:<b>%d</b> 位用戶,<b>%d</b> 個組織,<b>%d</b> 個公鑰,<b>%d</b> 個倉庫,<b>%d</b> 個倉庫關註,<b>%d</b> 個贊,<b>%d</b> 次行為,<b>%d</b> 條權限記錄,<b>%d</b> 個問題,<b>%d</b> 次評論,<b>%d</b> 個社交帳號,<b>%d</b> 個用戶關註,<b>%d</b> 個鏡像,<b>%d</b> 個版本發佈,<b>%d</b> 個登錄源,<b>%d</b> 個 Web 鉤子,<b>%d</b> 個里程碑,<b>%d</b> 個標籤,<b>%d</b> 個鉤子任務,<b>%d</b> 個團隊,<b>%d</b> 個更新任務,<b>%d</b> 個附件。
|
||||
dashboard.operation_name=操作名稱
|
||||
dashboard.operation_switch=開關
|
||||
dashboard.operation_run=執行
|
||||
dashboard.clean_unbind_oauth=清理未綁定社交帳號
|
||||
dashboard.clean_unbind_oauth_success=所有未綁定社交數據清除成功!
|
||||
dashboard.delete_inactivate_accounts=刪除所有未激活帳戶
|
||||
dashboard.delete_inactivate_accounts_success=所有未激活帳號清除成功!
|
||||
dashboard.delete_repo_archives=刪除所有倉庫存檔
|
||||
dashboard.delete_repo_archives_success=所有倉庫存檔清除成功!
|
||||
dashboard.git_gc_repos=對倉庫進行垃圾回收
|
||||
dashboard.git_gc_repos_success=所有倉庫的垃圾回收已成功完成!
|
||||
dashboard.server_uptime=服務執行時間
|
||||
dashboard.current_goroutine=當前 Goroutines 數量
|
||||
dashboard.current_memory_usage=當前內存使用量
|
||||
dashboard.total_memory_allocated=所有被分配的內存
|
||||
dashboard.memory_obtained=內存佔用量
|
||||
dashboard.pointer_lookup_times=指針查找次數
|
||||
dashboard.memory_allocate_times=內存分配次數
|
||||
dashboard.memory_free_times=內存釋放次數
|
||||
dashboard.current_heap_usage=當前 Heap 內存使用量
|
||||
dashboard.heap_memory_obtained=Heap 內存佔用量
|
||||
dashboard.heap_memory_idle=Heap 內存空閒量
|
||||
dashboard.heap_memory_in_use=正在使用的 Heap 內存
|
||||
dashboard.heap_memory_released=被釋放的 Heap 內存
|
||||
dashboard.heap_objects=Heap 對象數量
|
||||
dashboard.bootstrap_stack_usage=啟動 Stack 使用量
|
||||
dashboard.stack_memory_obtained=被分配的 Stack 內存
|
||||
dashboard.mspan_structures_usage=MSpan 結構內存使用量
|
||||
dashboard.mspan_structures_obtained=被分配的 MSpan 結構內存
|
||||
dashboard.mcache_structures_usage=MCache 結構內存使用量
|
||||
dashboard.mcache_structures_obtained=被分配的 MCache 結構內存
|
||||
dashboard.profiling_bucket_hash_table_obtained=被分配的剖析哈希表內存
|
||||
dashboard.gc_metadata_obtained=被分配的垃圾收集元數據內存
|
||||
dashboard.other_system_allocation_obtained=其它被分配的系統內存
|
||||
dashboard.next_gc_recycle=下次垃圾收集內存回收量
|
||||
dashboard.last_gc_time=距離上次垃圾收集時間
|
||||
dashboard.total_gc_time=垃圾收集執行時間總量
|
||||
dashboard.total_gc_pause=垃圾收集暫停時間總量
|
||||
dashboard.last_gc_pause=上次垃圾收集暫停時間
|
||||
dashboard.gc_times=垃圾收集執行次數
|
||||
|
||||
users.user_manage_panel=用戶管理面版
|
||||
users.new_account=創建新的帳戶
|
||||
users.name=用戶名
|
||||
users.activated=已激活
|
||||
users.admin=管理員
|
||||
users.repos=倉庫數
|
||||
users.created=創建時間
|
||||
users.edit=編輯
|
||||
users.auth_source=認證源
|
||||
users.local=本地
|
||||
users.auth_login_name=認證登錄名
|
||||
users.update_profile_success=該用戶信息更新成功!
|
||||
users.edit_account=編輯用戶信息
|
||||
users.is_activated=該用戶已被激活
|
||||
users.is_admin=該用戶具有管理員權限
|
||||
users.allow_git_hook=該帳戶具有創建 Git 鉤子的權限
|
||||
users.update_profile=更新用戶信息
|
||||
users.delete_account=刪除該用戶
|
||||
users.still_own_repo=該帳戶仍然是某些倉庫的擁有者,您必須先轉移或刪除它們才能執行刪除帳戶操作!
|
||||
users.still_has_org=該帳戶仍舊是某些組織的成員,您必須先使其離開或刪除組織。
|
||||
|
||||
orgs.org_manage_panel=組織管理面版
|
||||
orgs.name=組織名稱
|
||||
orgs.teams=團隊數
|
||||
orgs.members=成員數
|
||||
|
||||
repos.repo_manage_panel=倉庫管理界面
|
||||
repos.owner=所有者
|
||||
repos.name=倉庫名稱
|
||||
repos.private=私有庫
|
||||
repos.watches=關註數
|
||||
repos.stars=讚好數
|
||||
repos.issues=問題數
|
||||
|
||||
auths.auth_manage_panel=授權認證管理面版
|
||||
auths.new=添加新的認證源
|
||||
auths.name=認證名稱
|
||||
auths.type=認證類型
|
||||
auths.enabled=已啟用
|
||||
auths.updated=最後更新時間
|
||||
auths.auth_type=授權類型
|
||||
auths.auth_name=授權名稱
|
||||
auths.domain=域名
|
||||
auths.host=主機地址
|
||||
auths.port=主機端口
|
||||
auths.base_dn=Base DN
|
||||
auths.attributes=搜尋屬性
|
||||
auths.filter=搜尋過濾
|
||||
auths.ms_ad_sa=Ms Ad SA
|
||||
auths.smtp_auth=SMTP 授權類型
|
||||
auths.smtphost=SMTP 主機地址
|
||||
auths.smtpport=SMTP 主機端口
|
||||
auths.enable_tls=啟用 TLS 加密
|
||||
auths.enable_auto_register=允許授權用戶自動註冊
|
||||
auths.tips=幫助提示
|
||||
auths.edit=修改授權認證設置
|
||||
auths.activated=該授權認證已經啟用
|
||||
auths.update_success=授權認證設置更新成功!
|
||||
auths.update=更新授權認證信息
|
||||
auths.delete=刪除該授權認證
|
||||
auths.delete_auth_title=授權認證刪除操作
|
||||
auths.delete_auth_desc=該授權認證將被刪除,您確定要繼續嗎?
|
||||
|
||||
config.server_config=服務器配置
|
||||
config.app_name=應用名稱
|
||||
config.app_ver=應用版本
|
||||
config.app_url=應用 URL
|
||||
config.domain=應用域名
|
||||
config.offline_mode=離線模式
|
||||
config.disable_router_log=關閉路由日志
|
||||
config.run_user=執行用戶
|
||||
config.run_mode=執行模式
|
||||
config.repo_root_path=倉庫根目錄
|
||||
config.static_file_root_path=靜態文件根目錄
|
||||
config.log_file_root_path=日志文件根目錄
|
||||
config.script_type=腳本類型
|
||||
config.reverse_auth_user=反向代理認證
|
||||
config.db_config=數據庫配置
|
||||
config.db_type=數據庫類型
|
||||
config.db_host=主機地址
|
||||
config.db_name=數據庫名稱
|
||||
config.db_user=數據庫用戶
|
||||
config.db_ssl_mode=SSL 模式
|
||||
config.db_ssl_mode_helper=(僅限 "postgres" 使用)
|
||||
config.db_path=數據庫路徑
|
||||
config.db_path_helper=(僅限 "sqlite3" 使用)
|
||||
config.service_config=服務配置
|
||||
config.register_email_confirm=註冊郵件確認
|
||||
config.disable_register=關閉註冊功能
|
||||
config.require_sign_in_view=強制登錄瀏覽
|
||||
config.mail_notify=郵件通知提醒
|
||||
config.enable_cache_avatar=開啟緩存頭像
|
||||
config.active_code_lives=激活用戶連結有效期
|
||||
config.reset_password_code_lives=重置密碼連結有效期
|
||||
config.webhook_config=Web 鉤子配置
|
||||
config.task_interval=任務周期
|
||||
config.deliver_timeout=推送超時
|
||||
config.mailer_config=郵件配置
|
||||
config.mailer_enabled=啟用服務
|
||||
config.mailer_name=發送者名稱
|
||||
config.mailer_host=郵件主機地址
|
||||
config.mailer_user=發送者帳號
|
||||
config.oauth_config=社交帳號配置
|
||||
config.oauth_enabled=啟用服務
|
||||
config.cache_config=Cache 配置
|
||||
config.cache_adapter=Cache 適配器
|
||||
config.cache_interval=Cache 周期
|
||||
config.cache_conn=Cache 連接字符串
|
||||
config.session_config=Session 配置
|
||||
config.session_provider=Session 提供者
|
||||
config.provider_config=提供者配置
|
||||
config.cookie_name=Cookie 名稱
|
||||
config.enable_set_cookie=啟用設置 Cookie
|
||||
config.gc_interval_time=垃圾收集周期
|
||||
config.session_life_time=Session 生命周期
|
||||
config.https_only=僅限 HTTPS
|
||||
config.cookie_life_time=Cookie 生命周期
|
||||
config.picture_config=圖片配置
|
||||
config.picture_service=圖片服務
|
||||
config.disable_gravatar=禁用 Gravatar 頭像
|
||||
config.log_config=日誌配置
|
||||
config.log_mode=日誌模式
|
||||
|
||||
monitor.cron=Cron 任務
|
||||
monitor.name=任務名稱
|
||||
monitor.schedule=任務安排
|
||||
monitor.next=下次執行時間
|
||||
monitor.previous=上次執行時間
|
||||
monitor.execute_times=執行次數
|
||||
monitor.process=執行中進程
|
||||
monitor.desc=進程描述
|
||||
monitor.start=開始時間
|
||||
monitor.execute_time=已執行時間
|
||||
|
||||
notices.system_notice_list=系統提示管理
|
||||
notices.type=提示類型
|
||||
notices.type_1=倉庫
|
||||
notices.desc=描述
|
||||
notices.op=操作
|
||||
notices.delete_success=系統提示刪除成功!
|
||||
|
||||
[action]
|
||||
create_repo=創建了倉庫 <a href="%s/%s">%s</a>
|
||||
commit_repo=推送了 <a href="%s/%s/src/%s">%s</a> 分支的代碼到 <a href="%s/%s">%s</a>
|
||||
create_issue=創建了問題 <a href="%s/%s/issues/%s">%s#%s</a>
|
||||
comment_issue=評論了問題 <a href="%s/%s/issues/%s">%s#%s</a>
|
||||
transfer_repo=將倉庫 <code>%s</code> 轉移至 <a href="/%s%s">%s</a>
|
||||
push_tag=推送了標籤 <a href="%s/%s/src/%s">%s</a> 到 <a href="%s/%s">%s</a>
|
||||
compare_2_commits=查看 2 次提交的內容對比
|
||||
|
||||
[tool]
|
||||
ago=之前
|
||||
from_now=之後
|
||||
now=現在
|
||||
1s=1 秒%s
|
||||
1m=1 分鐘%s
|
||||
1h=1 小時%s
|
||||
1d=1 天%s
|
||||
1w=1 周%s
|
||||
1mon=1 月%s
|
||||
1y=1 年%s
|
||||
seconds=%d 秒%s
|
||||
minutes=%d 分鐘%s
|
||||
hours=%d 小時%s
|
||||
days=%d 天%s
|
||||
weeks=%d 周%s
|
||||
months=%d 月%s
|
||||
years=%d 年%s
|
||||
raw_seconds=秒
|
||||
raw_minutes=分鐘
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[unix_http_server]
|
||||
file=/tmp/supervisor.sock ; path to your socket file
|
||||
file=log/supervisor.sock ; path to your socket file
|
||||
|
||||
[supervisord]
|
||||
logfile=log/supervisord.log ; supervisord log file
|
||||
logfile_maxbytes=50MB ; maximum size of logfile before rotation
|
||||
logfile_backups=10 ; number of backed up logfiles
|
||||
loglevel=warn ; info, debug, warn, trace
|
||||
pidfile=/tmp/supervisord.pid ; pidfile location
|
||||
pidfile=log/supervisord.pid ; pidfile location
|
||||
nodaemon=false ; run supervisord as a daemon
|
||||
minfds=1024 ; number of startup file descriptors
|
||||
minprocs=200 ; number of process descriptors
|
||||
@@ -17,10 +17,10 @@ childlogdir=log
|
||||
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
||||
|
||||
[supervisorctl]
|
||||
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
|
||||
serverurl=unix://log/supervisor.sock ; use a unix:// URL for a unix socket
|
||||
|
||||
[program:gogs]
|
||||
command = /root/Developer/gopath/src/github.com/gogits/gogs/start.sh ; here must be the real url, not ~ or $GOROOT like
|
||||
command = gogs_start
|
||||
autostart = true
|
||||
stdout_logfile = log/supervisor-gogs-stderr.log
|
||||
stderr_logfile = log/supervisor-gogs-error.log
|
||||
stdout_logfile = log/supervisor-gogs-out.log
|
||||
stderr_logfile = log/supervisor-gogs-err.log
|
||||
|
||||
3
gogs.go
3
gogs.go
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
const APP_VER = "0.5.2.0917 Beta"
|
||||
const APP_VER = "0.5.9.1213 Beta"
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
@@ -35,6 +35,7 @@ func main() {
|
||||
cmd.CmdUpdate,
|
||||
cmd.CmdFix,
|
||||
cmd.CmdDump,
|
||||
cmd.CmdCert,
|
||||
}
|
||||
app.Flags = append(app.Flags, []cli.Flag{}...)
|
||||
app.Run(os.Args)
|
||||
|
||||
@@ -58,6 +58,7 @@ type Action struct {
|
||||
ActUserId int64 // Action user id.
|
||||
ActUserName string // Action user name.
|
||||
ActEmail string
|
||||
ActAvatar string `xorm:"-"`
|
||||
RepoId int64
|
||||
RepoUserName string
|
||||
RepoName string
|
||||
@@ -137,7 +138,7 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
|
||||
return err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("/%s/%s/commit/%s", repoUserName, repoName, c.Sha1)
|
||||
url := fmt.Sprintf("%s/%s/%s/commit/%s", setting.AppSubUrl, repoUserName, repoName, c.Sha1)
|
||||
message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message)
|
||||
|
||||
if _, err = CreateComment(userId, issue.RepoId, issue.Id, 0, 0, COMMIT, message, nil); err != nil {
|
||||
@@ -153,6 +154,8 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
|
||||
|
||||
if err = UpdateIssue(issue); err != nil {
|
||||
return err
|
||||
} else if err = UpdateIssueUserPairsByStatus(issue.Id, issue.IsClosed); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = ChangeMilestoneIssueStats(issue); err != nil {
|
||||
@@ -181,13 +184,19 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
|
||||
commit = &base.PushCommits{}
|
||||
}
|
||||
|
||||
refName := git.RefEndName(refFullName)
|
||||
repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName)
|
||||
// if not the first commit, set the compareUrl
|
||||
if !strings.HasPrefix(oldCommitId, "0000000") {
|
||||
commit.CompareUrl = fmt.Sprintf("%s/compare/%s...%s", repoLink, oldCommitId, newCommitId)
|
||||
}
|
||||
|
||||
bs, err := json.Marshal(commit)
|
||||
if err != nil {
|
||||
return errors.New("action.CommitRepoAction(json): " + err.Error())
|
||||
}
|
||||
|
||||
refName := git.RefEndName(refFullName)
|
||||
|
||||
// Change repository bare status and update last updated time.
|
||||
repo, err := GetRepositoryByName(repoUserId, repoName)
|
||||
if err != nil {
|
||||
@@ -211,7 +220,6 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
|
||||
return errors.New("action.CommitRepoAction(NotifyWatchers): " + err.Error())
|
||||
|
||||
}
|
||||
//qlog.Info("action.CommitRepoAction(end): %d/%s", repoUserId, repoName)
|
||||
|
||||
// New push event hook.
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
@@ -237,13 +245,6 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
|
||||
return nil
|
||||
}
|
||||
|
||||
repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName)
|
||||
compareUrl := ""
|
||||
// if not the first commit, set the compareUrl
|
||||
if !strings.HasPrefix(oldCommitId, "0000000") {
|
||||
compareUrl = fmt.Sprintf("%s/compare/%s...%s", repoLink, oldCommitId, newCommitId)
|
||||
}
|
||||
|
||||
pusher_email, pusher_name := "", ""
|
||||
pusher, err := GetUserByName(userName)
|
||||
if err == nil {
|
||||
@@ -293,7 +294,7 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
|
||||
},
|
||||
Before: oldCommitId,
|
||||
After: newCommitId,
|
||||
CompareUrl: compareUrl,
|
||||
CompareUrl: commit.CompareUrl,
|
||||
}
|
||||
|
||||
for _, w := range ws {
|
||||
@@ -348,15 +349,31 @@ func NewRepoAction(u *User, repo *Repository) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// TransferRepoAction adds new action for transfering repository.
|
||||
// TransferRepoAction adds new action for transferring repository.
|
||||
func TransferRepoAction(u, newUser *User, repo *Repository) (err error) {
|
||||
if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email,
|
||||
OpType: TRANSFER_REPO, RepoId: repo.Id, RepoName: repo.Name, Content: newUser.Name,
|
||||
IsPrivate: repo.IsPrivate}); err != nil {
|
||||
action := &Action{
|
||||
ActUserId: u.Id,
|
||||
ActUserName: u.Name,
|
||||
ActEmail: u.Email,
|
||||
OpType: TRANSFER_REPO,
|
||||
RepoId: repo.Id,
|
||||
RepoUserName: newUser.Name,
|
||||
RepoName: repo.Name,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
Content: path.Join(repo.Owner.LowerName, repo.LowerName),
|
||||
}
|
||||
if err = NotifyWatchers(action); err != nil {
|
||||
log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove watch for organization.
|
||||
if repo.Owner.IsOrganization() {
|
||||
if err = WatchRepo(repo.Owner.Id, repo.Id, false); err != nil {
|
||||
log.Error(4, "WatchRepo", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name)
|
||||
return err
|
||||
}
|
||||
@@ -366,7 +383,7 @@ func GetFeeds(uid, offset int64, isProfile bool) ([]*Action, error) {
|
||||
actions := make([]*Action, 0, 20)
|
||||
sess := x.Limit(20, int(offset)).Desc("id").Where("user_id=?", uid)
|
||||
if isProfile {
|
||||
sess.Where("is_private=?", false).And("act_user_id=?", uid)
|
||||
sess.And("is_private=?", false).And("act_user_id=?", uid)
|
||||
}
|
||||
err := sess.Find(&actions)
|
||||
return actions, err
|
||||
|
||||
64
models/admin.go
Normal file
64
models/admin.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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 models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
type NoticeType int
|
||||
|
||||
const (
|
||||
NOTICE_REPOSITORY NoticeType = iota + 1
|
||||
)
|
||||
|
||||
// Notice represents a system notice for admin.
|
||||
type Notice struct {
|
||||
Id int64
|
||||
Type NoticeType
|
||||
Description string `xorm:"TEXT"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
}
|
||||
|
||||
// TrStr returns a translation format string.
|
||||
func (n *Notice) TrStr() string {
|
||||
return "admin.notices.type_" + com.ToStr(n.Type)
|
||||
}
|
||||
|
||||
// CreateNotice creates new system notice.
|
||||
func CreateNotice(tp NoticeType, desc string) error {
|
||||
n := &Notice{
|
||||
Type: tp,
|
||||
Description: desc,
|
||||
}
|
||||
_, err := x.Insert(n)
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateRepositoryNotice creates new system notice with type NOTICE_REPOSITORY.
|
||||
func CreateRepositoryNotice(desc string) error {
|
||||
return CreateNotice(NOTICE_REPOSITORY, desc)
|
||||
}
|
||||
|
||||
// CountNotices returns number of notices.
|
||||
func CountNotices() int64 {
|
||||
count, _ := x.Count(new(Notice))
|
||||
return count
|
||||
}
|
||||
|
||||
// GetNotices returns given number of notices with offset.
|
||||
func GetNotices(num, offset int) ([]*Notice, error) {
|
||||
notices := make([]*Notice, 0, num)
|
||||
err := x.Limit(num, offset).Desc("id").Find(¬ices)
|
||||
return notices, err
|
||||
}
|
||||
|
||||
// DeleteNotice deletes a system notice by given ID.
|
||||
func DeleteNotice(id int64) error {
|
||||
_, err := x.Id(id).Delete(new(Notice))
|
||||
return err
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package models
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -15,8 +16,10 @@ import (
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/git"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/mahonia"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
)
|
||||
|
||||
@@ -80,6 +83,8 @@ func ParsePatch(pid int64, maxlines int, cmd *exec.Cmd, reader io.Reader) (*Diff
|
||||
|
||||
leftLine, rightLine int
|
||||
isTooLong bool
|
||||
// FIXME: use first 30 lines to detect file encoding. Should use cache in the future.
|
||||
buf bytes.Buffer
|
||||
)
|
||||
|
||||
diff := &Diff{Files: make([]*DiffFile, 0)}
|
||||
@@ -97,6 +102,11 @@ func ParsePatch(pid int64, maxlines int, cmd *exec.Cmd, reader io.Reader) (*Diff
|
||||
|
||||
i = i + 1
|
||||
|
||||
// FIXME: use first 30 lines to detect file encoding.
|
||||
if i <= 30 {
|
||||
buf.WriteString(line)
|
||||
}
|
||||
|
||||
// Diff data too large, we only show the first about maxlines lines
|
||||
if i == maxlines {
|
||||
isTooLong = true
|
||||
@@ -181,6 +191,21 @@ func ParsePatch(pid int64, maxlines int, cmd *exec.Cmd, reader io.Reader) (*Diff
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: use first 30 lines to detect file encoding.
|
||||
charset, err := base.DetectEncoding(buf.Bytes())
|
||||
if charset != "utf8" && err == nil {
|
||||
decoder := mahonia.NewDecoder(charset)
|
||||
if decoder != nil {
|
||||
for _, f := range diff.Files {
|
||||
for _, sec := range f.Sections {
|
||||
for _, l := range sec.Lines {
|
||||
l.Content = decoder.ConvertString(l.Content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +211,10 @@ func GetIssues(uid, rid, pid, mid int64, page int, isClosed bool, labelIds, sort
|
||||
|
||||
if len(labelIds) > 0 {
|
||||
for _, label := range strings.Split(labelIds, ",") {
|
||||
sess.And("label_ids like '%$" + label + "|%'")
|
||||
// Prevent SQL inject.
|
||||
if com.StrTo(label).MustInt() > 0 {
|
||||
sess.And("label_ids like '%$" + label + "|%'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/gogits/gogs/modules/auth/ldap"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/uuid"
|
||||
)
|
||||
|
||||
type LoginType int
|
||||
@@ -40,7 +41,7 @@ var LoginTypes = map[LoginType]string{
|
||||
SMTP: "SMTP",
|
||||
}
|
||||
|
||||
// Ensure structs implmented interface.
|
||||
// Ensure structs implemented interface.
|
||||
var (
|
||||
_ core.Conversion = &LDAPConfig{}
|
||||
_ core.Conversion = &SMTPConfig{}
|
||||
@@ -149,7 +150,7 @@ func DelLoginSource(source *LoginSource) error {
|
||||
|
||||
// UserSignIn validates user name and password.
|
||||
func UserSignIn(uname, passwd string) (*User, error) {
|
||||
var u *User
|
||||
u := new(User)
|
||||
if strings.Contains(uname, "@") {
|
||||
u = &User{Email: uname}
|
||||
} else {
|
||||
@@ -161,12 +162,8 @@ func UserSignIn(uname, passwd string) (*User, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.LoginType == NOTYPE {
|
||||
if has {
|
||||
u.LoginType = PLAIN
|
||||
} else {
|
||||
return nil, ErrUserNotExist
|
||||
}
|
||||
if u.LoginType == NOTYPE && has {
|
||||
u.LoginType = PLAIN
|
||||
}
|
||||
|
||||
// For plain login, user must exist to reach this line.
|
||||
@@ -229,33 +226,35 @@ func UserSignIn(uname, passwd string) (*User, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Query if name/passwd can login against the LDAP direcotry pool
|
||||
// Query if name/passwd can login against the LDAP directory pool
|
||||
// Create a local user if success
|
||||
// Return the same LoginUserPlain semantic
|
||||
// FIXME: https://github.com/gogits/gogs/issues/672
|
||||
func LoginUserLdapSource(u *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) {
|
||||
mail, logged := cfg.Ldapsource.SearchEntry(name, passwd)
|
||||
if !logged {
|
||||
// user not in LDAP, do nothing
|
||||
// User not in LDAP, do nothing
|
||||
return nil, ErrUserNotExist
|
||||
}
|
||||
if !autoRegister {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// fake a local user creation
|
||||
// Fallback.
|
||||
if len(mail) == 0 {
|
||||
mail = uuid.NewV4().String() + "@localhost"
|
||||
}
|
||||
|
||||
u = &User{
|
||||
LowerName: strings.ToLower(name),
|
||||
Name: strings.ToLower(name),
|
||||
Name: name,
|
||||
LoginType: LDAP,
|
||||
LoginSource: sourceId,
|
||||
LoginName: name,
|
||||
IsActive: true,
|
||||
Passwd: passwd,
|
||||
Email: mail,
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
err := CreateUser(u)
|
||||
return u, err
|
||||
return u, CreateUser(u)
|
||||
}
|
||||
|
||||
type loginAuth struct {
|
||||
@@ -319,7 +318,7 @@ func SmtpAuth(host string, port int, a smtp.Auth, useTls bool) error {
|
||||
return ErrUnsupportedLoginType
|
||||
}
|
||||
|
||||
// Query if name/passwd can login against the LDAP direcotry pool
|
||||
// Query if name/passwd can login against the LDAP directory pool
|
||||
// Create a local user if success
|
||||
// Return the same LoginUserPlain semantic
|
||||
func LoginUserSMTPSource(u *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
@@ -17,10 +18,16 @@ import (
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
x *xorm.Engine
|
||||
tables []interface{}
|
||||
// Engine represents a xorm engine or session.
|
||||
type Engine interface {
|
||||
Delete(interface{}) (int64, error)
|
||||
Exec(string, ...interface{}) (sql.Result, error)
|
||||
Insert(...interface{}) (int64, error)
|
||||
}
|
||||
|
||||
var (
|
||||
x *xorm.Engine
|
||||
tables []interface{}
|
||||
HasEngine bool
|
||||
|
||||
DbCfg struct {
|
||||
@@ -32,12 +39,13 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
tables = append(tables, new(User), new(PublicKey),
|
||||
tables = append(tables,
|
||||
new(User), new(PublicKey), new(Follow), new(Oauth2), new(AccessToken),
|
||||
new(Repository), new(Watch), new(Star), new(Action), new(Access),
|
||||
new(Issue), new(Comment), new(Oauth2), new(Follow),
|
||||
new(Mirror), new(Release), new(LoginSource), new(Webhook), new(IssueUser),
|
||||
new(Milestone), new(Label), new(HookTask), new(Team), new(OrgUser), new(TeamUser),
|
||||
new(UpdateTask), new(Attachment))
|
||||
new(Issue), new(Comment), new(Attachment), new(IssueUser), new(Label), new(Milestone),
|
||||
new(Mirror), new(Release), new(LoginSource), new(Webhook),
|
||||
new(UpdateTask), new(HookTask), new(Team), new(OrgUser), new(TeamUser),
|
||||
new(Notice))
|
||||
}
|
||||
|
||||
func LoadModelsConfig() {
|
||||
@@ -87,7 +95,7 @@ func getEngine() (*xorm.Engine, error) {
|
||||
func NewTestEngine(x *xorm.Engine) (err error) {
|
||||
x, err = getEngine()
|
||||
if err != nil {
|
||||
return fmt.Errorf("models.init(fail to conntect database): %v", err)
|
||||
return fmt.Errorf("models.init(fail to connect to database): %v", err)
|
||||
}
|
||||
|
||||
return x.Sync(tables...)
|
||||
@@ -96,10 +104,10 @@ func NewTestEngine(x *xorm.Engine) (err error) {
|
||||
func SetEngine() (err error) {
|
||||
x, err = getEngine()
|
||||
if err != nil {
|
||||
return fmt.Errorf("models.init(fail to conntect database): %v", err)
|
||||
return fmt.Errorf("models.init(fail to connect to database): %v", err)
|
||||
}
|
||||
|
||||
// WARNNING: for serv command, MUST remove the output to os.stdout,
|
||||
// WARNING: for serv command, MUST remove the output to os.stdout,
|
||||
// so use log file to instead print to stdout.
|
||||
logPath := path.Join(setting.LogRootPath, "xorm.log")
|
||||
os.MkdirAll(path.Dir(logPath), os.ModePerm)
|
||||
@@ -122,7 +130,7 @@ func NewEngine() (err error) {
|
||||
if err = SetEngine(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = x.Sync2(tables...); err != nil {
|
||||
if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
|
||||
return fmt.Errorf("sync database struct error: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -79,7 +79,7 @@ func UpdateOauth2(oa *Oauth2) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// GetOauthByUserId returns list of oauthes that are releated to given user.
|
||||
// GetOauthByUserId returns list of oauthes that are related to given user.
|
||||
func GetOauthByUserId(uid int64) ([]*Oauth2, error) {
|
||||
socials := make([]*Oauth2, 0, 5)
|
||||
err := x.Find(&socials, Oauth2{Uid: uid})
|
||||
|
||||
@@ -25,8 +25,8 @@ var (
|
||||
ErrLastOrgOwner = errors.New("The user to remove is the last member in owner team")
|
||||
)
|
||||
|
||||
// IsOrgOwner returns true if given user is in the owner team.
|
||||
func (org *User) IsOrgOwner(uid int64) bool {
|
||||
// IsOwnedBy returns true if given user is in the owner team.
|
||||
func (org *User) IsOwnedBy(uid int64) bool {
|
||||
return IsOrganizationOwner(org.Id, uid)
|
||||
}
|
||||
|
||||
@@ -77,6 +77,17 @@ func (org *User) RemoveMember(uid int64) error {
|
||||
return RemoveOrgUser(org.Id, uid)
|
||||
}
|
||||
|
||||
// IsOrgEmailUsed returns true if the e-mail has been used in organization account.
|
||||
func IsOrgEmailUsed(email string) (bool, error) {
|
||||
if len(email) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
return x.Get(&User{
|
||||
Email: email,
|
||||
Type: ORGANIZATION,
|
||||
})
|
||||
}
|
||||
|
||||
// CreateOrganization creates record of a new organization.
|
||||
func CreateOrganization(org, owner *User) (*User, error) {
|
||||
if !IsLegalName(org.Name) {
|
||||
@@ -90,7 +101,7 @@ func CreateOrganization(org, owner *User) (*User, error) {
|
||||
return nil, ErrUserAlreadyExist
|
||||
}
|
||||
|
||||
isExist, err = IsEmailUsed(org.Email)
|
||||
isExist, err = IsOrgEmailUsed(org.Email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if isExist {
|
||||
@@ -159,6 +170,24 @@ func CreateOrganization(org, owner *User) (*User, error) {
|
||||
return org, sess.Commit()
|
||||
}
|
||||
|
||||
// GetOrgByName returns organization by given name.
|
||||
func GetOrgByName(name string) (*User, error) {
|
||||
if len(name) == 0 {
|
||||
return nil, ErrOrgNotExist
|
||||
}
|
||||
u := &User{
|
||||
LowerName: strings.ToLower(name),
|
||||
Type: ORGANIZATION,
|
||||
}
|
||||
has, err := x.Get(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrOrgNotExist
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// CountOrganizations returns number of organizations.
|
||||
func CountOrganizations() int64 {
|
||||
count, _ := x.Where("type=1").Count(new(User))
|
||||
@@ -229,7 +258,7 @@ func IsOrganizationMember(orgId, uid int64) bool {
|
||||
return has
|
||||
}
|
||||
|
||||
// IsPublicMembership returns ture if given user public his/her membership.
|
||||
// IsPublicMembership returns true if given user public his/her membership.
|
||||
func IsPublicMembership(orgId, uid int64) bool {
|
||||
has, _ := x.Where("uid=?", uid).And("org_id=?", orgId).And("is_public=?", true).Get(new(OrgUser))
|
||||
return has
|
||||
@@ -845,23 +874,12 @@ func IsTeamMember(orgId, teamId, uid int64) bool {
|
||||
|
||||
// GetTeamMembers returns all members in given team of organization.
|
||||
func GetTeamMembers(orgId, teamId int64) ([]*User, error) {
|
||||
tus := make([]*TeamUser, 0, 10)
|
||||
err := x.Where("org_id=?", orgId).And("team_id=?", teamId).Find(&tus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
us := make([]*User, len(tus))
|
||||
for i, tu := range tus {
|
||||
us[i], err = GetUserById(tu.Uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return us, nil
|
||||
us := make([]*User, 0, 10)
|
||||
err := x.Sql("SELECT * FROM `user` JOIN `team_user` ON `team_user`.`team_id` = ? AND `team_user`.`uid` = `user`.`id`", teamId).Find(&us)
|
||||
return us, err
|
||||
}
|
||||
|
||||
// GetUserTeams returns all teams that user belongs to in given origanization.
|
||||
// GetUserTeams returns all teams that user belongs to in given organization.
|
||||
func GetUserTeams(orgId, uid int64) ([]*Team, error) {
|
||||
tus := make([]*TeamUser, 0, 5)
|
||||
if err := x.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil {
|
||||
|
||||
@@ -33,6 +33,7 @@ const (
|
||||
var (
|
||||
ErrKeyAlreadyExist = errors.New("Public key already exist")
|
||||
ErrKeyNotExist = errors.New("Public key does not exist")
|
||||
ErrKeyUnableVerify = errors.New("Unable to verify public key")
|
||||
)
|
||||
|
||||
var sshOpLocker = sync.Mutex{}
|
||||
@@ -78,9 +79,9 @@ func init() {
|
||||
// PublicKey represents a SSH key.
|
||||
type PublicKey struct {
|
||||
Id int64
|
||||
OwnerId int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
Name string `xorm:"UNIQUE(s) NOT NULL"`
|
||||
Fingerprint string
|
||||
OwnerId int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
Name string `xorm:"UNIQUE(s) NOT NULL"`
|
||||
Fingerprint string `xorm:"INDEX NOT NULL"`
|
||||
Content string `xorm:"TEXT NOT NULL"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time
|
||||
@@ -88,6 +89,11 @@ type PublicKey struct {
|
||||
HasUsed bool `xorm:"-"`
|
||||
}
|
||||
|
||||
// OmitEmail returns content of public key but without e-mail address.
|
||||
func (k *PublicKey) OmitEmail() string {
|
||||
return strings.Join(strings.Split(k.Content, " ")[:2], " ")
|
||||
}
|
||||
|
||||
// GetAuthorizedString generates and returns formatted public key string for authorized_keys file.
|
||||
func (key *PublicKey) GetAuthorizedString() string {
|
||||
return fmt.Sprintf(_TPL_PUBLICK_KEY, appPath, key.Id, key.Content)
|
||||
@@ -101,13 +107,15 @@ var (
|
||||
"(MCE)": 1702,
|
||||
"(McE)": 1702,
|
||||
"(RSA)": 2048,
|
||||
"(DSA)": 1024,
|
||||
}
|
||||
)
|
||||
|
||||
// CheckPublicKeyString checks if the given public key string is recognized by SSH.
|
||||
func CheckPublicKeyString(content string) (bool, error) {
|
||||
content = strings.TrimRight(content, "\n\r")
|
||||
if strings.ContainsAny(content, "\n\r") {
|
||||
return false, errors.New("Only a single line with a single key please")
|
||||
return false, errors.New("only a single line with a single key please")
|
||||
}
|
||||
|
||||
// write the key to a file…
|
||||
@@ -125,7 +133,7 @@ func CheckPublicKeyString(content string) (bool, error) {
|
||||
if err != nil {
|
||||
return false, errors.New("ssh-keygen -l -f: " + stderr)
|
||||
} else if len(stdout) < 2 {
|
||||
return false, errors.New("ssh-keygen returned not enough output to evaluate the key")
|
||||
return false, errors.New("ssh-keygen returned not enough output to evaluate the key: " + stdout)
|
||||
}
|
||||
|
||||
// The ssh-keygen in Windows does not print key type, so no need go further.
|
||||
@@ -133,21 +141,22 @@ func CheckPublicKeyString(content string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
fmt.Println(stdout)
|
||||
sshKeygenOutput := strings.Split(stdout, " ")
|
||||
if len(sshKeygenOutput) < 4 {
|
||||
return false, errors.New("Not enough fields returned by ssh-keygen -l -f")
|
||||
return false, ErrKeyUnableVerify
|
||||
}
|
||||
|
||||
// Check if key type and key size match.
|
||||
keySize, err := com.StrTo(sshKeygenOutput[0]).Int()
|
||||
if err != nil {
|
||||
return false, errors.New("Cannot get key size of the given key")
|
||||
keySize := com.StrTo(sshKeygenOutput[0]).MustInt()
|
||||
if keySize == 0 {
|
||||
return false, errors.New("cannot get key size of the given key")
|
||||
}
|
||||
keyType := strings.TrimSpace(sshKeygenOutput[len(sshKeygenOutput)-1])
|
||||
if minimumKeySize := MinimumKeySize[keyType]; minimumKeySize == 0 {
|
||||
return false, errors.New("Sorry, unrecognized public key type")
|
||||
return false, errors.New("sorry, unrecognized public key type")
|
||||
} else if keySize < minimumKeySize {
|
||||
return false, fmt.Errorf("The minimum accepted size of a public key %s is %d", keyType, minimumKeySize)
|
||||
return false, fmt.Errorf("the minimum accepted size of a public key %s is %d", keyType, minimumKeySize)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
@@ -203,9 +212,12 @@ func AddPublicKey(key *PublicKey) (err error) {
|
||||
if err != nil {
|
||||
return errors.New("ssh-keygen -l -f: " + stderr)
|
||||
} else if len(stdout) < 2 {
|
||||
return errors.New("Not enough output for calculating fingerprint")
|
||||
return errors.New("not enough output for calculating fingerprint: " + stdout)
|
||||
}
|
||||
key.Fingerprint = strings.Split(stdout, " ")[1]
|
||||
if has, err := x.Get(&PublicKey{Fingerprint: key.Fingerprint}); err == nil && has {
|
||||
return ErrKeyAlreadyExist
|
||||
}
|
||||
|
||||
// Save SSH key.
|
||||
if _, err = x.Insert(key); err != nil {
|
||||
@@ -233,10 +245,10 @@ func GetPublicKeyById(keyId int64) (*PublicKey, error) {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// ListPublicKey returns a list of all public keys that user has.
|
||||
func ListPublicKey(uid int64) ([]*PublicKey, error) {
|
||||
// ListPublicKeys returns a list of public keys belongs to given user.
|
||||
func ListPublicKeys(uid int64) ([]*PublicKey, error) {
|
||||
keys := make([]*PublicKey, 0, 5)
|
||||
err := x.Find(&keys, &PublicKey{OwnerId: uid})
|
||||
err := x.Where("owner_id=?", uid).Find(&keys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
534
models/repo.go
534
models/repo.go
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/Unknwon/cae/zip"
|
||||
"github.com/Unknwon/com"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/git"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
@@ -48,7 +49,7 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
DescriptionPattern = regexp.MustCompile(`https?://\S+`)
|
||||
DescPattern = regexp.MustCompile(`https?://\S+`)
|
||||
)
|
||||
|
||||
func LoadRepoConfig() {
|
||||
@@ -132,14 +133,15 @@ func NewRepoContext() {
|
||||
|
||||
// Repository represents a git repository.
|
||||
type Repository struct {
|
||||
Id int64
|
||||
OwnerId int64 `xorm:"UNIQUE(s)"`
|
||||
Owner *User `xorm:"-"`
|
||||
ForkId int64
|
||||
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
Name string `xorm:"INDEX NOT NULL"`
|
||||
Description string
|
||||
Website string
|
||||
Id int64
|
||||
OwnerId int64 `xorm:"UNIQUE(s)"`
|
||||
Owner *User `xorm:"-"`
|
||||
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
Name string `xorm:"INDEX NOT NULL"`
|
||||
Description string
|
||||
Website string
|
||||
DefaultBranch string
|
||||
|
||||
NumWatches int
|
||||
NumStars int
|
||||
NumForks int
|
||||
@@ -153,19 +155,26 @@ type Repository struct {
|
||||
NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"`
|
||||
NumOpenMilestones int `xorm:"-"`
|
||||
NumTags int `xorm:"-"`
|
||||
IsPrivate bool
|
||||
IsMirror bool
|
||||
*Mirror `xorm:"-"`
|
||||
IsFork bool `xorm:"NOT NULL DEFAULT false"`
|
||||
IsBare bool
|
||||
IsGoget bool
|
||||
DefaultBranch string
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time `xorm:"UPDATED"`
|
||||
|
||||
IsPrivate bool
|
||||
IsBare bool
|
||||
IsGoget bool
|
||||
|
||||
IsMirror bool
|
||||
*Mirror `xorm:"-"`
|
||||
|
||||
IsFork bool `xorm:"NOT NULL DEFAULT false"`
|
||||
ForkId int64
|
||||
ForkRepo *Repository `xorm:"-"`
|
||||
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time `xorm:"UPDATED"`
|
||||
}
|
||||
|
||||
func (repo *Repository) GetOwner() (err error) {
|
||||
repo.Owner, err = GetUserById(repo.OwnerId)
|
||||
if repo.Owner == nil {
|
||||
repo.Owner, err = GetUserById(repo.OwnerId)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -174,6 +183,41 @@ func (repo *Repository) GetMirror() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
func (repo *Repository) GetForkRepo() (err error) {
|
||||
if !repo.IsFork {
|
||||
return nil
|
||||
}
|
||||
|
||||
repo.ForkRepo, err = GetRepositoryById(repo.ForkId)
|
||||
return err
|
||||
}
|
||||
|
||||
func (repo *Repository) RepoPath() (string, error) {
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return RepoPath(repo.Owner.Name, repo.Name), nil
|
||||
}
|
||||
|
||||
func (repo *Repository) RepoLink() (string, error) {
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return setting.AppSubUrl + "/" + repo.Owner.Name + "/" + repo.Name, nil
|
||||
}
|
||||
|
||||
func (repo *Repository) IsOwnedBy(u *User) bool {
|
||||
return repo.OwnerId == u.Id
|
||||
}
|
||||
|
||||
func (repo *Repository) HasAccess(uname string) bool {
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
return false
|
||||
}
|
||||
has, _ := HasAccess(uname, path.Join(repo.Owner.Name, repo.Name), READABLE)
|
||||
return has
|
||||
}
|
||||
|
||||
// DescriptionHtml does special handles to description and return HTML string.
|
||||
func (repo *Repository) DescriptionHtml() template.HTML {
|
||||
sanitize := func(s string) string {
|
||||
@@ -181,7 +225,7 @@ func (repo *Repository) DescriptionHtml() template.HTML {
|
||||
ss := html.EscapeString(s)
|
||||
return fmt.Sprintf(`<a href="%s" target="_blank">%s</a>`, ss, ss)
|
||||
}
|
||||
return template.HTML(DescriptionPattern.ReplaceAllStringFunc(repo.Description, sanitize))
|
||||
return template.HTML(DescPattern.ReplaceAllStringFunc(base.XSSString(repo.Description), sanitize))
|
||||
}
|
||||
|
||||
// IsRepositoryExist returns true if the repository with given name under user has already existed.
|
||||
@@ -197,9 +241,30 @@ func IsRepositoryExist(u *User, repoName string) (bool, error) {
|
||||
return com.IsDir(RepoPath(u.Name, repoName)), nil
|
||||
}
|
||||
|
||||
// CloneLink represents different types of clone URLs of repository.
|
||||
type CloneLink struct {
|
||||
SSH string
|
||||
HTTPS string
|
||||
Git string
|
||||
}
|
||||
|
||||
// CloneLink returns clone URLs of repository.
|
||||
func (repo *Repository) CloneLink() (cl CloneLink, err error) {
|
||||
if err = repo.GetOwner(); err != nil {
|
||||
return cl, err
|
||||
}
|
||||
if setting.SshPort != 22 {
|
||||
cl.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.Domain, setting.SshPort, repo.Owner.LowerName, repo.LowerName)
|
||||
} else {
|
||||
cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.Domain, repo.Owner.LowerName, repo.LowerName)
|
||||
}
|
||||
cl.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, repo.Owner.LowerName, repo.LowerName)
|
||||
return cl, nil
|
||||
}
|
||||
|
||||
var (
|
||||
illegalEquals = []string{"debug", "raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new"}
|
||||
illegalSuffixs = []string{".git"}
|
||||
illegalSuffixs = []string{".git", ".keys"}
|
||||
)
|
||||
|
||||
// IsLegalName returns false if name contains illegal characters.
|
||||
@@ -264,28 +329,6 @@ func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) er
|
||||
return nil
|
||||
}
|
||||
|
||||
// MirrorUpdate checks and updates mirror repositories.
|
||||
func MirrorUpdate() {
|
||||
if err := x.Iterate(new(Mirror), func(idx int, bean interface{}) error {
|
||||
m := bean.(*Mirror)
|
||||
if m.NextUpdate.After(time.Now()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
repoPath := filepath.Join(setting.RepoRootPath, m.RepoName+".git")
|
||||
if _, stderr, err := process.ExecDir(10*time.Minute,
|
||||
repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath),
|
||||
"git", "remote", "update"); err != nil {
|
||||
return errors.New("git remote update: " + stderr)
|
||||
}
|
||||
|
||||
m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour)
|
||||
return UpdateMirror(m)
|
||||
}); err != nil {
|
||||
log.Error(4, "repo.MirrorUpdate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// MigrateRepository migrates a existing repository from other project hosting.
|
||||
func MigrateRepository(u *User, name, desc string, private, mirror bool, url string) (*Repository, error) {
|
||||
repo, err := CreateRepository(u, name, desc, "", "", private, mirror, false)
|
||||
@@ -652,7 +695,7 @@ func RepoPath(userName, repoName string) string {
|
||||
func TransferOwnership(u *User, newOwner string, repo *Repository) error {
|
||||
newUser, err := GetUserByName(newOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("fail to get new owner(%s): %v", newOwner, err)
|
||||
}
|
||||
|
||||
// Check if new owner has repository with same name.
|
||||
@@ -669,17 +712,34 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Where("repo_name = ?", u.LowerName+"/"+repo.LowerName).
|
||||
And("user_name = ?", u.LowerName).Update(&Access{UserName: newUser.LowerName}); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
owner := repo.Owner
|
||||
oldRepoLink := path.Join(owner.LowerName, repo.LowerName)
|
||||
// Delete all access first if current owner is an organization.
|
||||
if owner.IsOrganization() {
|
||||
if _, err = sess.Where("repo_name=?", oldRepoLink).Delete(new(Access)); err != nil {
|
||||
sess.Rollback()
|
||||
return fmt.Errorf("fail to delete current accesses: %v", err)
|
||||
}
|
||||
} else {
|
||||
// Delete current owner access.
|
||||
if _, err = sess.Where("repo_name=?", oldRepoLink).And("user_name=?", owner.LowerName).
|
||||
Delete(new(Access)); err != nil {
|
||||
sess.Rollback()
|
||||
return fmt.Errorf("fail to delete access(owner): %v", err)
|
||||
}
|
||||
// In case new owner has access.
|
||||
if _, err = sess.Where("repo_name=?", oldRepoLink).And("user_name=?", newUser.LowerName).
|
||||
Delete(new(Access)); err != nil {
|
||||
sess.Rollback()
|
||||
return fmt.Errorf("fail to delete access(new user): %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = sess.Where("repo_name = ?", u.LowerName+"/"+repo.LowerName).Update(&Access{
|
||||
RepoName: newUser.LowerName + "/" + repo.LowerName,
|
||||
}); err != nil {
|
||||
// Change accesses to new repository path.
|
||||
if _, err = sess.Where("repo_name=?", oldRepoLink).
|
||||
Update(&Access{RepoName: path.Join(newUser.LowerName, repo.LowerName)}); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
return fmt.Errorf("fail to update access(change reponame): %v", err)
|
||||
}
|
||||
|
||||
// Update repository.
|
||||
@@ -695,17 +755,17 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", u.Id); err != nil {
|
||||
if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", owner.Id); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
mode := WRITABLE
|
||||
if repo.IsMirror {
|
||||
mode = READABLE
|
||||
}
|
||||
// New owner is organization.
|
||||
if newUser.IsOrganization() {
|
||||
mode := WRITABLE
|
||||
if repo.IsMirror {
|
||||
mode = READABLE
|
||||
}
|
||||
access := &Access{
|
||||
RepoName: path.Join(newUser.LowerName, repo.LowerName),
|
||||
Mode: mode,
|
||||
@@ -737,10 +797,20 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
access := &Access{
|
||||
RepoName: path.Join(newUser.LowerName, repo.LowerName),
|
||||
UserName: newUser.LowerName,
|
||||
Mode: mode,
|
||||
}
|
||||
if _, err = sess.Insert(access); err != nil {
|
||||
sess.Rollback()
|
||||
return fmt.Errorf("fail to insert access: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Change repository directory name.
|
||||
if err = os.Rename(RepoPath(u.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil {
|
||||
if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
@@ -749,14 +819,8 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add watch of new owner to repository.
|
||||
if !newUser.IsOrganization() {
|
||||
if err = WatchRepo(newUser.Id, repo.Id, true); err != nil {
|
||||
log.Error(4, "WatchRepo", err)
|
||||
}
|
||||
}
|
||||
if err = WatchRepo(u.Id, repo.Id, false); err != nil {
|
||||
log.Error(4, "WatchRepo2", err)
|
||||
if err = WatchRepo(newUser.Id, repo.Id, true); err != nil {
|
||||
log.Error(4, "WatchRepo", err)
|
||||
}
|
||||
|
||||
if err = TransferRepoAction(u, newUser, repo); err != nil {
|
||||
@@ -768,6 +832,7 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
|
||||
|
||||
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
|
||||
func ChangeRepositoryName(userName, oldRepoName, newRepoName string) (err error) {
|
||||
newRepoName = strings.ToLower(newRepoName)
|
||||
if !IsLegalName(newRepoName) {
|
||||
return ErrRepoNameIllegal
|
||||
}
|
||||
@@ -813,7 +878,7 @@ func UpdateRepository(repo *Repository) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteRepository deletes a repository for a user or orgnaztion.
|
||||
// DeleteRepository deletes a repository for a user or organization.
|
||||
func DeleteRepository(uid, repoId int64, userName string) error {
|
||||
repo := &Repository{Id: repoId, OwnerId: uid}
|
||||
has, err := x.Get(repo)
|
||||
@@ -908,13 +973,25 @@ func DeleteRepository(uid, repoId int64, userName string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if repo.IsFork {
|
||||
if _, err = sess.Exec("UPDATE `repository` SET num_forks = num_forks - 1 WHERE id = ?", repo.ForkId); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", uid); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove repository files.
|
||||
if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
desc := fmt.Sprintf("Fail to delete repository files(%s/%s): %v", userName, repo.Name, err)
|
||||
log.Warn(desc)
|
||||
if err = CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error(4, "Fail to add notice: %v", err)
|
||||
}
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
@@ -1003,15 +1080,21 @@ func GetCollaboratorNames(repoName string) ([]string, error) {
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// CollaborativeRepository represents a repository with collaborative information.
|
||||
type CollaborativeRepository struct {
|
||||
*Repository
|
||||
CanPush bool
|
||||
}
|
||||
|
||||
// GetCollaborativeRepos returns a list of repositories that user is collaborator.
|
||||
func GetCollaborativeRepos(uname string) ([]*Repository, error) {
|
||||
func GetCollaborativeRepos(uname string) ([]*CollaborativeRepository, error) {
|
||||
uname = strings.ToLower(uname)
|
||||
accesses := make([]*Access, 0, 10)
|
||||
if err := x.Find(&accesses, &Access{UserName: uname}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repos := make([]*Repository, 0, 10)
|
||||
repos := make([]*CollaborativeRepository, 0, 10)
|
||||
for _, access := range accesses {
|
||||
infos := strings.Split(access.RepoName, "/")
|
||||
if infos[0] == uname {
|
||||
@@ -1028,7 +1111,7 @@ func GetCollaborativeRepos(uname string) ([]*Repository, error) {
|
||||
return nil, err
|
||||
}
|
||||
repo.Owner = u
|
||||
repos = append(repos, repo)
|
||||
repos = append(repos, &CollaborativeRepository{repo, access.Mode == WRITABLE})
|
||||
}
|
||||
return repos, nil
|
||||
}
|
||||
@@ -1054,17 +1137,11 @@ type SearchOption struct {
|
||||
Keyword string
|
||||
Uid int64
|
||||
Limit int
|
||||
Private bool
|
||||
}
|
||||
|
||||
// SearchRepositoryByName returns given number of repositories whose name contains keyword.
|
||||
func SearchRepositoryByName(opt SearchOption) (repos []*Repository, err error) {
|
||||
// Prevent SQL inject.
|
||||
opt.Keyword = strings.TrimSpace(opt.Keyword)
|
||||
if len(opt.Keyword) == 0 {
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
opt.Keyword = strings.Split(opt.Keyword, " ")[0]
|
||||
if len(opt.Keyword) == 0 {
|
||||
return repos, nil
|
||||
}
|
||||
@@ -1077,43 +1154,150 @@ func SearchRepositoryByName(opt SearchOption) (repos []*Repository, err error) {
|
||||
if opt.Uid > 0 {
|
||||
sess.Where("owner_id=?", opt.Uid)
|
||||
}
|
||||
sess.And("lower_name like '%" + opt.Keyword + "%'").Find(&repos)
|
||||
if !opt.Private {
|
||||
sess.And("is_private=false")
|
||||
}
|
||||
sess.And("lower_name like ?", "%"+opt.Keyword+"%").Find(&repos)
|
||||
return repos, err
|
||||
}
|
||||
|
||||
// Watch is connection request for receiving repository notifycation.
|
||||
// DeleteRepositoryArchives deletes all repositories' archives.
|
||||
func DeleteRepositoryArchives() error {
|
||||
return x.Where("id > 0").Iterate(new(Repository),
|
||||
func(idx int, bean interface{}) error {
|
||||
repo := bean.(*Repository)
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.RemoveAll(filepath.Join(RepoPath(repo.Owner.Name, repo.Name), "archives"))
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
// Prevent duplicate tasks.
|
||||
isMirrorUpdating = false
|
||||
isGitFscking = false
|
||||
)
|
||||
|
||||
// MirrorUpdate checks and updates mirror repositories.
|
||||
func MirrorUpdate() {
|
||||
if isMirrorUpdating {
|
||||
return
|
||||
}
|
||||
isMirrorUpdating = true
|
||||
defer func() { isMirrorUpdating = false }()
|
||||
|
||||
if err := x.Iterate(new(Mirror), func(idx int, bean interface{}) error {
|
||||
m := bean.(*Mirror)
|
||||
if m.NextUpdate.After(time.Now()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
repoPath := filepath.Join(setting.RepoRootPath, m.RepoName+".git")
|
||||
if _, stderr, err := process.ExecDir(10*time.Minute,
|
||||
repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath),
|
||||
"git", "remote", "update"); err != nil {
|
||||
return errors.New("git remote update: " + stderr)
|
||||
}
|
||||
|
||||
m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour)
|
||||
return UpdateMirror(m)
|
||||
}); err != nil {
|
||||
log.Error(4, "repo.MirrorUpdate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// GitFsck calls 'git fsck' to check repository health.
|
||||
func GitFsck() {
|
||||
if isGitFscking {
|
||||
return
|
||||
}
|
||||
isGitFscking = true
|
||||
defer func() { isGitFscking = false }()
|
||||
|
||||
args := append([]string{"fsck"}, setting.GitFsckArgs...)
|
||||
if err := x.Where("id > 0").Iterate(new(Repository),
|
||||
func(idx int, bean interface{}) error {
|
||||
repo := bean.(*Repository)
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repoPath := RepoPath(repo.Owner.Name, repo.Name)
|
||||
_, _, err := process.ExecDir(-1, repoPath, "Repository health check", "git", args...)
|
||||
if err != nil {
|
||||
desc := fmt.Sprintf("Fail to health check repository(%s)", repoPath)
|
||||
log.Warn(desc)
|
||||
if err = CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error(4, "Fail to add notice: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(4, "repo.Fsck: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func GitGcRepos() error {
|
||||
args := append([]string{"gc"}, setting.GitGcArgs...)
|
||||
return x.Where("id > 0").Iterate(new(Repository),
|
||||
func(idx int, bean interface{}) error {
|
||||
repo := bean.(*Repository)
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, stderr, err := process.ExecDir(-1, RepoPath(repo.Owner.Name, repo.Name), "Repository garbage collection", "git", args...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v: %v", err, stderr)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// __ __ __ .__
|
||||
// / \ / \_____ _/ |_ ____ | |__
|
||||
// \ \/\/ /\__ \\ __\/ ___\| | \
|
||||
// \ / / __ \| | \ \___| Y \
|
||||
// \__/\ / (____ /__| \___ >___| /
|
||||
// \/ \/ \/ \/
|
||||
|
||||
// Watch is connection request for receiving repository notification.
|
||||
type Watch struct {
|
||||
Id int64
|
||||
UserId int64 `xorm:"UNIQUE(watch)"`
|
||||
RepoId int64 `xorm:"UNIQUE(watch)"`
|
||||
}
|
||||
|
||||
// Watch or unwatch repository.
|
||||
func WatchRepo(uid, repoId int64, watch bool) (err error) {
|
||||
// IsWatching checks if user has watched given repository.
|
||||
func IsWatching(uid, repoId int64) bool {
|
||||
has, _ := x.Get(&Watch{0, uid, repoId})
|
||||
return has
|
||||
}
|
||||
|
||||
func watchRepoWithEngine(e Engine, uid, repoId int64, watch bool) (err error) {
|
||||
if watch {
|
||||
if IsWatching(uid, repoId) {
|
||||
return nil
|
||||
}
|
||||
if _, err = x.Insert(&Watch{RepoId: repoId, UserId: uid}); err != nil {
|
||||
if _, err = e.Insert(&Watch{RepoId: repoId, UserId: uid}); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = x.Exec("UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?", repoId)
|
||||
_, err = e.Exec("UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?", repoId)
|
||||
} else {
|
||||
if !IsWatching(uid, repoId) {
|
||||
return nil
|
||||
}
|
||||
if _, err = x.Delete(&Watch{0, uid, repoId}); err != nil {
|
||||
if _, err = e.Delete(&Watch{0, uid, repoId}); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = x.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repoId)
|
||||
_, err = e.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repoId)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// IsWatching checks if user has watched given repository.
|
||||
func IsWatching(uid, rid int64) bool {
|
||||
has, _ := x.Get(&Watch{0, uid, rid})
|
||||
return has
|
||||
// Watch or unwatch repository.
|
||||
func WatchRepo(uid, repoId int64, watch bool) (err error) {
|
||||
return watchRepoWithEngine(x, uid, repoId, watch)
|
||||
}
|
||||
|
||||
// GetWatchers returns all watchers of given repository.
|
||||
@@ -1151,6 +1335,13 @@ func NotifyWatchers(act *Action) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// _________ __
|
||||
// / _____// |______ _______
|
||||
// \_____ \\ __\__ \\_ __ \
|
||||
// / \| | / __ \| | \/
|
||||
// /_______ /|__| (____ /__|
|
||||
// \/ \/
|
||||
|
||||
type Star struct {
|
||||
Id int64
|
||||
Uid int64 `xorm:"UNIQUE(s)"`
|
||||
@@ -1165,16 +1356,20 @@ func StarRepo(uid, repoId int64, star bool) (err error) {
|
||||
}
|
||||
if _, err = x.Insert(&Star{Uid: uid, RepoId: repoId}); err != nil {
|
||||
return err
|
||||
} else if _, err = x.Exec("UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repoId); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = x.Exec("UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repoId)
|
||||
_, err = x.Exec("UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?", uid)
|
||||
} else {
|
||||
if !IsStaring(uid, repoId) {
|
||||
return nil
|
||||
}
|
||||
if _, err = x.Delete(&Star{0, uid, repoId}); err != nil {
|
||||
return err
|
||||
} else if _, err = x.Exec("UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repoId); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = x.Exec("UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repoId)
|
||||
_, err = x.Exec("UPDATE `user` SET num_stars = num_stars - 1 WHERE id = ?", uid)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -1185,6 +1380,157 @@ func IsStaring(uid, repoId int64) bool {
|
||||
return has
|
||||
}
|
||||
|
||||
func ForkRepository(repoName string, uid int64) {
|
||||
// ___________ __
|
||||
// \_ _____/__________| | __
|
||||
// | __)/ _ \_ __ \ |/ /
|
||||
// | \( <_> ) | \/ <
|
||||
// \___ / \____/|__| |__|_ \
|
||||
// \/ \/
|
||||
|
||||
func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repository, error) {
|
||||
isExist, err := IsRepositoryExist(u, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if isExist {
|
||||
return nil, ErrRepoAlreadyExist
|
||||
}
|
||||
|
||||
// In case the old repository is a fork.
|
||||
if oldRepo.IsFork {
|
||||
oldRepo, err = GetRepositoryById(oldRepo.ForkId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repo := &Repository{
|
||||
OwnerId: u.Id,
|
||||
Owner: u,
|
||||
Name: name,
|
||||
LowerName: strings.ToLower(name),
|
||||
Description: desc,
|
||||
IsPrivate: oldRepo.IsPrivate,
|
||||
IsFork: true,
|
||||
ForkId: oldRepo.Id,
|
||||
}
|
||||
|
||||
if _, err = sess.Insert(repo); err != nil {
|
||||
sess.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var t *Team // Owner team.
|
||||
|
||||
mode := WRITABLE
|
||||
|
||||
access := &Access{
|
||||
UserName: u.LowerName,
|
||||
RepoName: path.Join(u.LowerName, repo.LowerName),
|
||||
Mode: mode,
|
||||
}
|
||||
// Give access to all members in owner team.
|
||||
if u.IsOrganization() {
|
||||
t, err = u.GetOwnerTeam()
|
||||
if err != nil {
|
||||
sess.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
if err = t.GetMembers(); err != nil {
|
||||
sess.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
for _, u := range t.Members {
|
||||
access.Id = 0
|
||||
access.UserName = u.LowerName
|
||||
if _, err = sess.Insert(access); err != nil {
|
||||
sess.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, err = sess.Insert(access); err != nil {
|
||||
sess.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = sess.Exec(
|
||||
"UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil {
|
||||
sess.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update owner team info and count.
|
||||
if u.IsOrganization() {
|
||||
t.RepoIds += "$" + com.ToStr(repo.Id) + "|"
|
||||
t.NumRepos++
|
||||
if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
|
||||
sess.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if u.IsOrganization() {
|
||||
t, err := u.GetOwnerTeam()
|
||||
if err != nil {
|
||||
log.Error(4, "GetOwnerTeam: %v", err)
|
||||
} else {
|
||||
if err = t.GetMembers(); err != nil {
|
||||
log.Error(4, "GetMembers: %v", err)
|
||||
} else {
|
||||
for _, u := range t.Members {
|
||||
if err = watchRepoWithEngine(sess, u.Id, repo.Id, true); err != nil {
|
||||
log.Error(4, "WatchRepo2: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err = watchRepoWithEngine(sess, u.Id, repo.Id, true); err != nil {
|
||||
log.Error(4, "WatchRepo3: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = NewRepoAction(u, repo); err != nil {
|
||||
log.Error(4, "NewRepoAction: %v", err)
|
||||
}
|
||||
|
||||
if _, err = sess.Exec(
|
||||
"UPDATE `repository` SET num_forks = num_forks + 1 WHERE id = ?", oldRepo.Id); err != nil {
|
||||
sess.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldRepoPath, err := oldRepo.RepoPath()
|
||||
if err != nil {
|
||||
sess.Rollback()
|
||||
return nil, fmt.Errorf("fail to get repo path(%s): %v", oldRepo.Name, err)
|
||||
}
|
||||
|
||||
if err = sess.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repoPath := RepoPath(u.Name, repo.Name)
|
||||
_, stderr, err := process.ExecTimeout(10*time.Minute,
|
||||
fmt.Sprintf("ForkRepository(git clone): %s/%s", u.Name, repo.Name),
|
||||
"git", "clone", "--bare", oldRepoPath, repoPath)
|
||||
if err != nil {
|
||||
return nil, errors.New("ForkRepository(git clone): " + stderr)
|
||||
}
|
||||
|
||||
_, stderr, err = process.ExecDir(-1,
|
||||
repoPath, fmt.Sprintf("ForkRepository(git update-server-info): %s", repoPath),
|
||||
"git", "update-server-info")
|
||||
if err != nil {
|
||||
return nil, errors.New("ForkRepository(git update-server-info): " + stderr)
|
||||
}
|
||||
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ const (
|
||||
)
|
||||
|
||||
type Slack struct {
|
||||
Domain string `json:"domain"`
|
||||
Token string `json:"token"`
|
||||
Channel string `json:"channel"`
|
||||
}
|
||||
|
||||
@@ -36,14 +34,6 @@ type SlackAttachment struct {
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
func GetSlackURL(domain string, token string) string {
|
||||
return fmt.Sprintf(
|
||||
"https://%s.slack.com/services/hooks/incoming-webhook?token=%s",
|
||||
domain,
|
||||
token,
|
||||
)
|
||||
}
|
||||
|
||||
func (p SlackPayload) GetJSONPayload() ([]byte, error) {
|
||||
data, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
|
||||
69
models/token.go
Normal file
69
models/token.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// 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 models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/uuid"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAccessTokenNotExist = errors.New("Access token does not exist")
|
||||
)
|
||||
|
||||
// AccessToken represents a personal access token.
|
||||
type AccessToken struct {
|
||||
Id int64
|
||||
Uid int64
|
||||
Name string
|
||||
Sha1 string `xorm:"UNIQUE VARCHAR(40)"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time
|
||||
HasRecentActivity bool `xorm:"-"`
|
||||
HasUsed bool `xorm:"-"`
|
||||
}
|
||||
|
||||
// NewAccessToken creates new access token.
|
||||
func NewAccessToken(t *AccessToken) error {
|
||||
t.Sha1 = base.EncodeSha1(uuid.NewV4().String())
|
||||
_, err := x.Insert(t)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetAccessTokenBySha returns access token by given sha1.
|
||||
func GetAccessTokenBySha(sha string) (*AccessToken, error) {
|
||||
t := &AccessToken{Sha1: sha}
|
||||
has, err := x.Get(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrAccessTokenNotExist
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// ListAccessTokens returns a list of access tokens belongs to given user.
|
||||
func ListAccessTokens(uid int64) ([]*AccessToken, error) {
|
||||
tokens := make([]*AccessToken, 0, 5)
|
||||
err := 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
|
||||
}
|
||||
|
||||
// DeleteAccessTokenById deletes access token by given ID.
|
||||
func DeleteAccessTokenById(id int64) error {
|
||||
_, err := x.Id(id).Delete(new(AccessToken))
|
||||
return err
|
||||
}
|
||||
@@ -23,6 +23,10 @@ type UpdateTask struct {
|
||||
NewCommitId string
|
||||
}
|
||||
|
||||
const (
|
||||
MAX_COMMITS int = 5
|
||||
)
|
||||
|
||||
func AddUpdateTask(task *UpdateTask) error {
|
||||
_, err := x.Insert(task)
|
||||
return err
|
||||
@@ -102,7 +106,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
|
||||
|
||||
if err = CommitRepoAction(userId, ru.Id, userName, actEmail,
|
||||
repos.Id, repoUserName, repoName, refName, commit, oldCommitId, newCommitId); err != nil {
|
||||
log.GitLogger.Fatal(4, "runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
|
||||
log.GitLogger.Fatal(4, "CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -112,8 +116,8 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
|
||||
return fmt.Errorf("runUpdate GetCommit of newCommitId: %v", err)
|
||||
}
|
||||
|
||||
// Push new branch.
|
||||
var l *list.List
|
||||
// if a new branch
|
||||
if isNew {
|
||||
l, err = newCommit.CommitsBefore()
|
||||
if err != nil {
|
||||
@@ -130,9 +134,8 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
|
||||
return fmt.Errorf("runUpdate.Commit repoId: %v", err)
|
||||
}
|
||||
|
||||
// if commits push
|
||||
// Push commits.
|
||||
commits := make([]*base.PushCommit, 0)
|
||||
var maxCommits = 2
|
||||
var actEmail string
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
commit := e.Value.(*git.Commit)
|
||||
@@ -145,14 +148,13 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName
|
||||
commit.Message(),
|
||||
commit.Author.Email,
|
||||
commit.Author.Name})
|
||||
if len(commits) >= maxCommits {
|
||||
if len(commits) >= MAX_COMMITS {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
//commits = append(commits, []string{lastCommit.Id().String(), lastCommit.Message()})
|
||||
if err = CommitRepoAction(userId, ru.Id, userName, actEmail,
|
||||
repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits}, oldCommitId, newCommitId); err != nil {
|
||||
repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits, ""}, oldCommitId, newCommitId); err != nil {
|
||||
return fmt.Errorf("runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
|
||||
}
|
||||
return nil
|
||||
|
||||
226
models/user.go
226
models/user.go
@@ -5,17 +5,23 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/nfnt/resize"
|
||||
|
||||
"github.com/gogits/gogs/modules/avatar"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/git"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
@@ -44,32 +50,40 @@ var (
|
||||
|
||||
// User represents the object of individual and member of organization.
|
||||
type User struct {
|
||||
Id int64
|
||||
LowerName string `xorm:"UNIQUE NOT NULL"`
|
||||
Name string `xorm:"UNIQUE NOT NULL"`
|
||||
FullName string
|
||||
Email string `xorm:"UNIQUE NOT NULL"`
|
||||
Passwd string `xorm:"NOT NULL"`
|
||||
LoginType LoginType
|
||||
LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
LoginName string
|
||||
Type UserType
|
||||
Orgs []*User `xorm:"-"`
|
||||
Repos []*Repository `xorm:"-"`
|
||||
Id int64
|
||||
LowerName string `xorm:"UNIQUE NOT NULL"`
|
||||
Name string `xorm:"UNIQUE NOT NULL"`
|
||||
FullName string
|
||||
Email string `xorm:"UNIQUE(s) NOT NULL"`
|
||||
Passwd string `xorm:"NOT NULL"`
|
||||
LoginType LoginType
|
||||
LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
LoginName string
|
||||
Type UserType `xorm:"UNIQUE(s)"`
|
||||
Orgs []*User `xorm:"-"`
|
||||
Repos []*Repository `xorm:"-"`
|
||||
Location string
|
||||
Website string
|
||||
Rands string `xorm:"VARCHAR(10)"`
|
||||
Salt string `xorm:"VARCHAR(10)"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time `xorm:"UPDATED"`
|
||||
|
||||
// Permissions.
|
||||
IsActive bool
|
||||
IsAdmin bool
|
||||
AllowGitHook bool
|
||||
|
||||
// Avatar.
|
||||
Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
|
||||
AvatarEmail string `xorm:"NOT NULL"`
|
||||
UseCustomAvatar bool
|
||||
|
||||
// Counters.
|
||||
NumFollowers int
|
||||
NumFollowings int
|
||||
NumStars int
|
||||
NumRepos int
|
||||
Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
|
||||
AvatarEmail string `xorm:"NOT NULL"`
|
||||
Location string
|
||||
Website string
|
||||
IsActive bool
|
||||
IsAdmin bool
|
||||
Rands string `xorm:"VARCHAR(10)"`
|
||||
Salt string `xorm:"VARCHAR(10)"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time `xorm:"UPDATED"`
|
||||
|
||||
// For organization.
|
||||
Description string
|
||||
@@ -82,24 +96,27 @@ type User struct {
|
||||
// DashboardLink returns the user dashboard page link.
|
||||
func (u *User) DashboardLink() string {
|
||||
if u.IsOrganization() {
|
||||
return "/org/" + u.Name + "/dashboard/"
|
||||
return setting.AppSubUrl + "/org/" + u.Name + "/dashboard/"
|
||||
}
|
||||
return "/"
|
||||
return setting.AppSubUrl + "/"
|
||||
}
|
||||
|
||||
// HomeLink returns the user home page link.
|
||||
func (u *User) HomeLink() string {
|
||||
return "/user/" + u.Name
|
||||
return setting.AppSubUrl + "/" + u.Name
|
||||
}
|
||||
|
||||
// AvatarLink returns user gravatar link.
|
||||
func (u *User) AvatarLink() string {
|
||||
if setting.DisableGravatar {
|
||||
return "/img/avatar_default.jpg"
|
||||
} else if setting.Service.EnableCacheAvatar {
|
||||
return "/avatar/" + u.Avatar
|
||||
switch {
|
||||
case u.UseCustomAvatar:
|
||||
return setting.AppSubUrl + "/avatars/" + com.ToStr(u.Id)
|
||||
case setting.DisableGravatar:
|
||||
return setting.AppSubUrl + "/img/avatar_default.jpg"
|
||||
case setting.Service.EnableCacheAvatar:
|
||||
return setting.AppSubUrl + "/avatar/" + u.Avatar
|
||||
}
|
||||
return "//1.gravatar.com/avatar/" + u.Avatar
|
||||
return setting.GravatarSource + u.Avatar
|
||||
}
|
||||
|
||||
// NewGitSig generates and returns the signature of given user.
|
||||
@@ -124,6 +141,48 @@ func (u *User) ValidtePassword(passwd string) bool {
|
||||
return u.Passwd == newUser.Passwd
|
||||
}
|
||||
|
||||
// CustomAvatarPath returns user custom avatar file path.
|
||||
func (u *User) CustomAvatarPath() string {
|
||||
return filepath.Join(setting.AvatarUploadPath, com.ToStr(u.Id))
|
||||
}
|
||||
|
||||
// UploadAvatar saves custom avatar for user.
|
||||
// FIXME: split uploads to different subdirs in case we have massive users.
|
||||
func (u *User) UploadAvatar(data []byte) error {
|
||||
u.UseCustomAvatar = true
|
||||
|
||||
img, _, err := image.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m := resize.Resize(200, 200, img, resize.NearestNeighbor)
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Id(u.Id).AllCols().Update(u); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
os.MkdirAll(setting.AvatarUploadPath, os.ModePerm)
|
||||
fw, err := os.Create(u.CustomAvatarPath())
|
||||
if err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
defer fw.Close()
|
||||
if err = jpeg.Encode(fw, m, nil); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// IsOrganization returns true if user is actually a organization.
|
||||
func (u *User) IsOrganization() bool {
|
||||
return u.Type == ORGANIZATION
|
||||
@@ -192,7 +251,7 @@ func IsEmailUsed(email string) (bool, error) {
|
||||
return x.Get(&User{Email: email})
|
||||
}
|
||||
|
||||
// GetUserSalt returns a user salt token
|
||||
// GetUserSalt returns a ramdom user salt token.
|
||||
func GetUserSalt() string {
|
||||
return base.GetRandomString(10)
|
||||
}
|
||||
@@ -218,8 +277,8 @@ func CreateUser(u *User) error {
|
||||
}
|
||||
|
||||
u.LowerName = strings.ToLower(u.Name)
|
||||
u.Avatar = base.EncodeMd5(u.Email)
|
||||
u.AvatarEmail = u.Email
|
||||
u.Avatar = avatar.HashEmail(u.AvatarEmail)
|
||||
u.Rands = GetUserSalt()
|
||||
u.Salt = GetUserSalt()
|
||||
u.EncodePasswd()
|
||||
@@ -359,6 +418,13 @@ func ChangeUserName(u *User, newUserName string) (err error) {
|
||||
|
||||
// UpdateUser updates user's information.
|
||||
func UpdateUser(u *User) error {
|
||||
has, err := x.Where("id != ?", u.Id).And("email = ?", u.Email).Get(new(User))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return ErrEmailAlreadyUsed
|
||||
}
|
||||
|
||||
u.LowerName = strings.ToLower(u.Name)
|
||||
|
||||
if len(u.Location) > 255 {
|
||||
@@ -371,11 +437,16 @@ func UpdateUser(u *User) error {
|
||||
u.Description = u.Description[:255]
|
||||
}
|
||||
|
||||
_, err := x.Id(u.Id).AllCols().Update(u)
|
||||
if u.AvatarEmail == "" {
|
||||
u.AvatarEmail = u.Email
|
||||
}
|
||||
u.Avatar = avatar.HashEmail(u.AvatarEmail)
|
||||
|
||||
_, err = x.Id(u.Id).AllCols().Update(u)
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: need some kind of mechanism to record failure.
|
||||
// FIXME: need some kind of mechanism to record failure. HINT: system notice
|
||||
// DeleteUser completely and permanently deletes everything of user.
|
||||
func DeleteUser(u *User) error {
|
||||
// Check ownership of repository.
|
||||
@@ -389,13 +460,13 @@ func DeleteUser(u *User) error {
|
||||
// Check membership of organization.
|
||||
count, err = u.GetOrganizationCount()
|
||||
if err != nil {
|
||||
return errors.New("modesl.GetRepositories(GetOrganizationCount): " + err.Error())
|
||||
return errors.New("GetOrganizationCount: " + err.Error())
|
||||
} else if count > 0 {
|
||||
return ErrUserHasOrgs
|
||||
}
|
||||
|
||||
// TODO: check issues, other repos' commits
|
||||
// TODO: roll backable in some point.
|
||||
// FIXME: check issues, other repos' commits
|
||||
// FIXME: roll backable in some point.
|
||||
|
||||
// Delete all followers.
|
||||
if _, err = x.Delete(&Follow{FollowId: u.Id}); err != nil {
|
||||
@@ -472,22 +543,22 @@ func GetUserById(id int64) (*User, error) {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// GetUserByName returns the user object by given name if exists.
|
||||
// GetUserByName returns user by given name.
|
||||
func GetUserByName(name string) (*User, error) {
|
||||
if len(name) == 0 {
|
||||
return nil, ErrUserNotExist
|
||||
}
|
||||
user := &User{LowerName: strings.ToLower(name)}
|
||||
has, err := x.Get(user)
|
||||
u := &User{LowerName: strings.ToLower(name)}
|
||||
has, err := x.Get(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrUserNotExist
|
||||
}
|
||||
return user, nil
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// GetUserEmailsByNames returns a slice of e-mails corresponds to names.
|
||||
// GetUserEmailsByNames returns a list of e-mails corresponds to names.
|
||||
func GetUserEmailsByNames(names []string) []string {
|
||||
mails := make([]string, 0, len(names))
|
||||
for _, name := range names {
|
||||
@@ -513,6 +584,46 @@ func GetUserIdsByNames(names []string) []int64 {
|
||||
return ids
|
||||
}
|
||||
|
||||
// UserCommit represents a commit with validation of user.
|
||||
type UserCommit struct {
|
||||
User *User
|
||||
*git.Commit
|
||||
}
|
||||
|
||||
// ValidateCommitWithEmail chceck if author's e-mail of commit is corresponsind to a user.
|
||||
func ValidateCommitWithEmail(c *git.Commit) *User {
|
||||
u, err := GetUserByEmail(c.Author.Email)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
|
||||
func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
|
||||
emails := map[string]*User{}
|
||||
newCommits := list.New()
|
||||
e := oldCommits.Front()
|
||||
for e != nil {
|
||||
c := e.Value.(*git.Commit)
|
||||
|
||||
var u *User
|
||||
if v, ok := emails[c.Author.Email]; !ok {
|
||||
u, _ = GetUserByEmail(c.Author.Email)
|
||||
emails[c.Author.Email] = u
|
||||
} else {
|
||||
u = v
|
||||
}
|
||||
|
||||
newCommits.PushBack(UserCommit{
|
||||
User: u,
|
||||
Commit: c,
|
||||
})
|
||||
e = e.Next()
|
||||
}
|
||||
return newCommits
|
||||
}
|
||||
|
||||
// GetUserByEmail returns the user object by given e-mail if exists.
|
||||
func GetUserByEmail(email string) (*User, error) {
|
||||
if len(email) == 0 {
|
||||
@@ -530,24 +641,17 @@ func GetUserByEmail(email string) (*User, error) {
|
||||
|
||||
// SearchUserByName returns given number of users whose name contains keyword.
|
||||
func SearchUserByName(opt SearchOption) (us []*User, err error) {
|
||||
// Prevent SQL inject.
|
||||
opt.Keyword = strings.TrimSpace(opt.Keyword)
|
||||
if len(opt.Keyword) == 0 {
|
||||
return us, nil
|
||||
}
|
||||
|
||||
opt.Keyword = strings.Split(opt.Keyword, " ")[0]
|
||||
if len(opt.Keyword) == 0 {
|
||||
return us, nil
|
||||
}
|
||||
opt.Keyword = strings.ToLower(opt.Keyword)
|
||||
|
||||
us = make([]*User, 0, opt.Limit)
|
||||
err = x.Limit(opt.Limit).Where("type=0").And("lower_name like '%" + opt.Keyword + "%'").Find(&us)
|
||||
err = x.Limit(opt.Limit).Where("type=0").And("lower_name like ?", "%"+opt.Keyword+"%").Find(&us)
|
||||
return us, err
|
||||
}
|
||||
|
||||
// Follow is connection request for receiving user notifycation.
|
||||
// Follow is connection request for receiving user notification.
|
||||
type Follow struct {
|
||||
Id int64
|
||||
UserId int64 `xorm:"unique(follow)"`
|
||||
@@ -556,27 +660,27 @@ type Follow struct {
|
||||
|
||||
// FollowUser marks someone be another's follower.
|
||||
func FollowUser(userId int64, followId int64) (err error) {
|
||||
session := x.NewSession()
|
||||
defer session.Close()
|
||||
session.Begin()
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
sess.Begin()
|
||||
|
||||
if _, err = session.Insert(&Follow{UserId: userId, FollowId: followId}); err != nil {
|
||||
session.Rollback()
|
||||
if _, err = sess.Insert(&Follow{UserId: userId, FollowId: followId}); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
rawSql := "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?"
|
||||
if _, err = session.Exec(rawSql, followId); err != nil {
|
||||
session.Rollback()
|
||||
if _, err = sess.Exec(rawSql, followId); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
rawSql = "UPDATE `user` SET num_followings = num_followings + 1 WHERE id = ?"
|
||||
if _, err = session.Exec(rawSql, userId); err != nil {
|
||||
session.Rollback()
|
||||
if _, err = sess.Exec(rawSql, userId); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
return session.Commit()
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// UnFollowUser unmarks someone be another's follower.
|
||||
|
||||
@@ -27,6 +27,32 @@ const (
|
||||
FORM
|
||||
)
|
||||
|
||||
var hookContentTypes = map[string]HookContentType{
|
||||
"json": JSON,
|
||||
"form": FORM,
|
||||
}
|
||||
|
||||
// ToHookContentType returns HookContentType by given name.
|
||||
func ToHookContentType(name string) HookContentType {
|
||||
return hookContentTypes[name]
|
||||
}
|
||||
|
||||
func (t HookContentType) Name() string {
|
||||
switch t {
|
||||
case JSON:
|
||||
return "json"
|
||||
case FORM:
|
||||
return "form"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsValidHookContentType returns true if given name is a valid hook content type.
|
||||
func IsValidHookContentType(name string) bool {
|
||||
_, ok := hookContentTypes[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
// HookEvent represents events that will delivery hook.
|
||||
type HookEvent struct {
|
||||
PushOnly bool `json:"push_only"`
|
||||
@@ -46,6 +72,8 @@ type Webhook struct {
|
||||
HookTaskType HookTaskType
|
||||
Meta string `xorm:"TEXT"` // store hook-specific attributes
|
||||
OrgId int64
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time `xorm:"UPDATED"`
|
||||
}
|
||||
|
||||
// GetEvent handles conversion from Events to HookEvent.
|
||||
@@ -71,7 +99,7 @@ func (w *Webhook) UpdateEvent() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// HasPushEvent returns true if hook enbaled push event.
|
||||
// HasPushEvent returns true if hook enabled push event.
|
||||
func (w *Webhook) HasPushEvent() bool {
|
||||
if w.PushOnly {
|
||||
return true
|
||||
@@ -147,12 +175,39 @@ const (
|
||||
SLACK
|
||||
)
|
||||
|
||||
var hookTaskTypes = map[string]HookTaskType{
|
||||
"gogs": GOGS,
|
||||
"slack": SLACK,
|
||||
}
|
||||
|
||||
// ToHookTaskType returns HookTaskType by given name.
|
||||
func ToHookTaskType(name string) HookTaskType {
|
||||
return hookTaskTypes[name]
|
||||
}
|
||||
|
||||
func (t HookTaskType) Name() string {
|
||||
switch t {
|
||||
case GOGS:
|
||||
return "gogs"
|
||||
case SLACK:
|
||||
return "slack"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsValidHookTaskType returns true if given name is a valid hook task type.
|
||||
func IsValidHookTaskType(name string) bool {
|
||||
_, ok := hookTaskTypes[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
type HookEventType string
|
||||
|
||||
const (
|
||||
PUSH HookEventType = "push"
|
||||
)
|
||||
|
||||
// FIXME: just use go-gogs-client structs maybe?
|
||||
type PayloadAuthor struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
@@ -235,8 +290,22 @@ func UpdateHookTask(t *HookTask) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
// Prevent duplicate deliveries.
|
||||
// This happens with massive hook tasks cannot finish delivering
|
||||
// before next shooting starts.
|
||||
isShooting = false
|
||||
)
|
||||
|
||||
// DeliverHooks checks and delivers undelivered hooks.
|
||||
// FIXME: maybe can use goroutine to shoot a number of them at same time?
|
||||
func DeliverHooks() {
|
||||
if isShooting {
|
||||
return
|
||||
}
|
||||
isShooting = true
|
||||
defer func() { isShooting = false }()
|
||||
|
||||
tasks := make([]*HookTask, 0, 10)
|
||||
timeout := time.Duration(setting.WebhookDeliverTimeout) * time.Second
|
||||
x.Where("is_delivered=?", false).Iterate(new(HookTask),
|
||||
@@ -255,7 +324,7 @@ func DeliverHooks() {
|
||||
|
||||
t.IsDelivered = true
|
||||
|
||||
// TODO: record response.
|
||||
// FIXME: record response.
|
||||
switch t.Type {
|
||||
case GOGS:
|
||||
{
|
||||
|
||||
@@ -6,22 +6,22 @@ package auth
|
||||
|
||||
import (
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
"github.com/macaron-contrib/binding"
|
||||
)
|
||||
|
||||
type AdminEditUserForm struct {
|
||||
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
|
||||
Passwd string `form:"password"`
|
||||
Website string `form:"website" binding:"MaxSize(50)"`
|
||||
Location string `form:"location" binding:"MaxSize(50)"`
|
||||
Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
|
||||
Active bool `form:"active"`
|
||||
Admin bool `form:"admin"`
|
||||
LoginType int `form:"login_type"`
|
||||
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
|
||||
Passwd string `form:"password"`
|
||||
Website string `form:"website" binding:"MaxSize(50)"`
|
||||
Location string `form:"location" binding:"MaxSize(50)"`
|
||||
Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
|
||||
Active bool `form:"active"`
|
||||
Admin bool `form:"admin"`
|
||||
AllowGitHook bool `form:"allow_git_hook"`
|
||||
LoginType int `form:"login_type"`
|
||||
}
|
||||
|
||||
func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
@@ -8,31 +8,24 @@ import (
|
||||
"reflect"
|
||||
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
"github.com/macaron-contrib/binding"
|
||||
|
||||
"github.com/gogits/gogs/modules/auth"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
)
|
||||
|
||||
type MarkdownForm struct {
|
||||
Text string `form:"text" binding:"Required"`
|
||||
Text string `form:"text"`
|
||||
Mode string `form:"mode"`
|
||||
Context string `form:"context"`
|
||||
}
|
||||
|
||||
func (f *MarkdownForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validateApiReq(errs, ctx.Data, f, l)
|
||||
func (f *MarkdownForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validateApiReq(errs, ctx.Data, f)
|
||||
}
|
||||
|
||||
func validateApiReq(errs *binding.Errors, data map[string]interface{}, f interface{}, l i18n.Locale) {
|
||||
if errs.Count() == 0 {
|
||||
return
|
||||
} else if len(errs.Overall) > 0 {
|
||||
for _, err := range errs.Overall {
|
||||
log.Error(4, "%s: %v", reflect.TypeOf(f), err)
|
||||
}
|
||||
return
|
||||
func validateApiReq(errs binding.Errors, data map[string]interface{}, f auth.Form) binding.Errors {
|
||||
if errs.Len() == 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
data["HasError"] = true
|
||||
@@ -54,26 +47,27 @@ func validateApiReq(errs *binding.Errors, data map[string]interface{}, f interfa
|
||||
continue
|
||||
}
|
||||
|
||||
if err, ok := errs.Fields[field.Name]; ok {
|
||||
switch err {
|
||||
case binding.BindingRequireError:
|
||||
if errs[0].FieldNames[0] == field.Name {
|
||||
switch errs[0].Classification {
|
||||
case binding.RequiredError:
|
||||
data["ErrorMsg"] = fieldName + " cannot be empty"
|
||||
case binding.BindingAlphaDashError:
|
||||
case binding.AlphaDashError:
|
||||
data["ErrorMsg"] = fieldName + " must be valid alpha or numeric or dash(-_) characters"
|
||||
case binding.BindingAlphaDashDotError:
|
||||
case binding.AlphaDashDotError:
|
||||
data["ErrorMsg"] = fieldName + " must be valid alpha or numeric or dash(-_) or dot characters"
|
||||
case binding.BindingMinSizeError:
|
||||
case binding.MinSizeError:
|
||||
data["ErrorMsg"] = fieldName + " must contain at least " + auth.GetMinSize(field) + " characters"
|
||||
case binding.BindingMaxSizeError:
|
||||
case binding.MaxSizeError:
|
||||
data["ErrorMsg"] = fieldName + " must contain at most " + auth.GetMaxSize(field) + " characters"
|
||||
case binding.BindingEmailError:
|
||||
case binding.EmailError:
|
||||
data["ErrorMsg"] = fieldName + " is not a valid e-mail address"
|
||||
case binding.BindingUrlError:
|
||||
case binding.UrlError:
|
||||
data["ErrorMsg"] = fieldName + " is not a valid URL"
|
||||
default:
|
||||
data["ErrorMsg"] = "Unknown error: " + err
|
||||
data["ErrorMsg"] = "Unknown error: " + errs[0].Classification
|
||||
}
|
||||
return
|
||||
return errs
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
@@ -9,32 +9,38 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/macaron-contrib/i18n"
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/binding"
|
||||
"github.com/macaron-contrib/session"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/uuid"
|
||||
)
|
||||
|
||||
// SignedInId returns the id of signed in user.
|
||||
func SignedInId(header http.Header, sess session.Store) int64 {
|
||||
func SignedInId(req *http.Request, sess session.Store) int64 {
|
||||
if !models.HasEngine {
|
||||
return 0
|
||||
}
|
||||
|
||||
if setting.Service.EnableReverseProxyAuth {
|
||||
webAuthUser := header.Get(setting.ReverseProxyAuthUser)
|
||||
if len(webAuthUser) > 0 {
|
||||
u, err := models.GetUserByName(webAuthUser)
|
||||
if err != nil {
|
||||
if err != models.ErrUserNotExist {
|
||||
log.Error(4, "GetUserByName: %v", err)
|
||||
// API calls need to check access token.
|
||||
if strings.HasPrefix(req.URL.Path, "/api/") {
|
||||
auHead := req.Header.Get("Authorization")
|
||||
if len(auHead) > 0 {
|
||||
auths := strings.Fields(auHead)
|
||||
if len(auths) == 2 && auths[0] == "token" {
|
||||
t, err := models.GetAccessTokenBySha(auths[1])
|
||||
if err != nil {
|
||||
if err != models.ErrAccessTokenNotExist {
|
||||
log.Error(4, "GetAccessTokenBySha: %v", err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
return t.Uid
|
||||
}
|
||||
return u.Id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,18 +61,78 @@ func SignedInId(header http.Header, sess session.Store) int64 {
|
||||
}
|
||||
|
||||
// SignedInUser returns the user object of signed user.
|
||||
func SignedInUser(header http.Header, sess session.Store) *models.User {
|
||||
uid := SignedInId(header, sess)
|
||||
// It returns a bool value to indicate whether user uses basic auth or not.
|
||||
func SignedInUser(req *http.Request, sess session.Store) (*models.User, bool) {
|
||||
if !models.HasEngine {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
uid := SignedInId(req, sess)
|
||||
|
||||
if uid <= 0 {
|
||||
return nil
|
||||
if setting.Service.EnableReverseProxyAuth {
|
||||
webAuthUser := req.Header.Get(setting.ReverseProxyAuthUser)
|
||||
if len(webAuthUser) > 0 {
|
||||
u, err := models.GetUserByName(webAuthUser)
|
||||
if err != nil {
|
||||
if err != models.ErrUserNotExist {
|
||||
log.Error(4, "GetUserByName: %v", err)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Check if enabled auto-registration.
|
||||
if setting.Service.EnableReverseProxyAutoRegister {
|
||||
u := &models.User{
|
||||
Name: webAuthUser,
|
||||
Email: uuid.NewV4().String() + "@localhost",
|
||||
Passwd: webAuthUser,
|
||||
IsActive: true,
|
||||
}
|
||||
if err = models.CreateUser(u); err != nil {
|
||||
// FIXME: should I create a system notice?
|
||||
log.Error(4, "CreateUser: %v", err)
|
||||
return nil, false
|
||||
} else {
|
||||
return u, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return u, false
|
||||
}
|
||||
}
|
||||
|
||||
// Check with basic auth.
|
||||
baHead := req.Header.Get("Authorization")
|
||||
if len(baHead) > 0 {
|
||||
auths := strings.Fields(baHead)
|
||||
if len(auths) == 2 && auths[0] == "Basic" {
|
||||
uname, passwd, _ := base.BasicAuthDecode(auths[1])
|
||||
u, err := models.GetUserByName(uname)
|
||||
if err != nil {
|
||||
if err != models.ErrUserNotExist {
|
||||
log.Error(4, "GetUserByName: %v", err)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if u.ValidtePassword(passwd) {
|
||||
return u, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
u, err := models.GetUserById(uid)
|
||||
if err != nil {
|
||||
log.Error(4, "GetUserById: %v", err)
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
return u
|
||||
return u, false
|
||||
}
|
||||
|
||||
type Form interface {
|
||||
binding.Validator
|
||||
}
|
||||
|
||||
// AssignForm assign form values back to the template data.
|
||||
@@ -109,14 +175,9 @@ func GetMaxSize(field reflect.StructField) string {
|
||||
return getSize(field, "MaxSize(")
|
||||
}
|
||||
|
||||
func validate(errs *binding.Errors, data map[string]interface{}, f interface{}, l i18n.Locale) {
|
||||
if errs.Count() == 0 {
|
||||
return
|
||||
} else if len(errs.Overall) > 0 {
|
||||
for _, err := range errs.Overall {
|
||||
log.Error(4, "%s: %v", reflect.TypeOf(f), err)
|
||||
}
|
||||
return
|
||||
func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaron.Locale) binding.Errors {
|
||||
if errs.Len() == 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
data["HasError"] = true
|
||||
@@ -139,28 +200,29 @@ func validate(errs *binding.Errors, data map[string]interface{}, f interface{},
|
||||
continue
|
||||
}
|
||||
|
||||
if err, ok := errs.Fields[field.Name]; ok {
|
||||
if errs[0].FieldNames[0] == field.Name {
|
||||
data["Err_"+field.Name] = true
|
||||
trName := l.Tr("form." + field.Name)
|
||||
switch err {
|
||||
case binding.BindingRequireError:
|
||||
switch errs[0].Classification {
|
||||
case binding.RequiredError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.require_error")
|
||||
case binding.BindingAlphaDashError:
|
||||
case binding.AlphaDashError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_error")
|
||||
case binding.BindingAlphaDashDotError:
|
||||
case binding.AlphaDashDotError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_dot_error")
|
||||
case binding.BindingMinSizeError:
|
||||
case binding.MinSizeError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.min_size_error", GetMinSize(field))
|
||||
case binding.BindingMaxSizeError:
|
||||
case binding.MaxSizeError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.max_size_error", GetMaxSize(field))
|
||||
case binding.BindingEmailError:
|
||||
case binding.EmailError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.email_error")
|
||||
case binding.BindingUrlError:
|
||||
case binding.UrlError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.url_error")
|
||||
default:
|
||||
data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + err
|
||||
data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + errs[0].Classification
|
||||
}
|
||||
return
|
||||
return errs
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ package auth
|
||||
|
||||
import (
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
"github.com/macaron-contrib/binding"
|
||||
)
|
||||
|
||||
type AuthenticationForm struct {
|
||||
@@ -31,6 +29,6 @@ type AuthenticationForm struct {
|
||||
AllowAutoRegister bool `form:"allowautoregister"`
|
||||
}
|
||||
|
||||
func (f *AuthenticationForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *AuthenticationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ type Ldapsource struct {
|
||||
Port int // port number
|
||||
UseSSL bool // Use SSL
|
||||
BaseDN string // Base DN
|
||||
Attributes string // Attribut to search
|
||||
Attributes string // Attribute to search
|
||||
Filter string // Query filter to validate entry
|
||||
MsAdSAFormat string // in the case of MS AD Simple Authen, the format to use (see: http://msdn.microsoft.com/en-us/library/cc223499.aspx)
|
||||
Enabled bool // if this source is disabled
|
||||
@@ -37,7 +37,7 @@ func AddSource(name string, host string, port int, usessl bool, basedn string, a
|
||||
Authensource = append(Authensource, ldaphost)
|
||||
}
|
||||
|
||||
//LoginUser : try to login an user to LDAP sources, return requested (attribut,true) if ok, ("",false) other wise
|
||||
//LoginUser : try to login an user to LDAP sources, return requested (attribute,true) if ok, ("",false) other wise
|
||||
//First match wins
|
||||
//Returns first attribute if exists
|
||||
func LoginUser(name, passwd string) (a string, r bool) {
|
||||
|
||||
@@ -6,9 +6,7 @@ package auth
|
||||
|
||||
import (
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
"github.com/macaron-contrib/binding"
|
||||
)
|
||||
|
||||
// ________ .__ __ .__
|
||||
@@ -23,8 +21,8 @@ type CreateOrgForm struct {
|
||||
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
|
||||
}
|
||||
|
||||
func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type UpdateOrgSettingForm struct {
|
||||
@@ -37,8 +35,8 @@ type UpdateOrgSettingForm struct {
|
||||
Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
|
||||
}
|
||||
|
||||
func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// ___________
|
||||
@@ -54,6 +52,6 @@ type CreateTeamForm struct {
|
||||
Permission string `form:"permission"`
|
||||
}
|
||||
|
||||
func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
@@ -1,21 +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 auth
|
||||
|
||||
import (
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
)
|
||||
|
||||
type AddSSHKeyForm struct {
|
||||
SSHTitle string `form:"title" binding:"Required"`
|
||||
Content string `form:"content" binding:"Required"`
|
||||
}
|
||||
|
||||
func (f *AddSSHKeyForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
@@ -6,9 +6,7 @@ package auth
|
||||
|
||||
import (
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
"github.com/macaron-contrib/binding"
|
||||
)
|
||||
|
||||
// _______________________________________ _________.______________________ _______________.___.
|
||||
@@ -23,13 +21,13 @@ type CreateRepoForm struct {
|
||||
RepoName string `form:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"`
|
||||
Private bool `form:"private"`
|
||||
Description string `form:"desc" binding:"MaxSize(255)"`
|
||||
AutoInit bool `form:"auto_init"`
|
||||
Gitignore string `form:"gitignore"`
|
||||
License string `form:"license"`
|
||||
InitReadme bool `form:"init_readme"`
|
||||
}
|
||||
|
||||
func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type MigrateRepoForm struct {
|
||||
@@ -43,8 +41,8 @@ type MigrateRepoForm struct {
|
||||
Description string `form:"desc" binding:"MaxSize(255)"`
|
||||
}
|
||||
|
||||
func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type RepoSettingForm struct {
|
||||
@@ -57,8 +55,8 @@ type RepoSettingForm struct {
|
||||
GoGet bool `form:"goget"`
|
||||
}
|
||||
|
||||
func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// __ __ ___. .__ .__ __
|
||||
@@ -77,21 +75,20 @@ type NewWebhookForm struct {
|
||||
Active bool `form:"active"`
|
||||
}
|
||||
|
||||
func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type NewSlackHookForm struct {
|
||||
HookTaskType string `form:"hook_type" binding:"Required"`
|
||||
Domain string `form:"domain" binding:"Required`
|
||||
Token string `form:"token" binding:"Required"`
|
||||
PayloadUrl string `form:"payload_url" binding:"Required`
|
||||
Channel string `form:"channel" binding:"Required"`
|
||||
PushOnly bool `form:"push_only"`
|
||||
Active bool `form:"active"`
|
||||
}
|
||||
|
||||
func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// .___
|
||||
@@ -102,15 +99,15 @@ func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs *binding.Errors,
|
||||
// \/ \/ \/
|
||||
|
||||
type CreateIssueForm struct {
|
||||
IssueName string `form:"title" binding:"Required;MaxSize(50)"`
|
||||
IssueName string `form:"title" binding:"Required;MaxSize(255)"`
|
||||
MilestoneId int64 `form:"milestoneid"`
|
||||
AssigneeId int64 `form:"assigneeid"`
|
||||
Labels string `form:"labels"`
|
||||
Content string `form:"content"`
|
||||
}
|
||||
|
||||
func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// _____ .__.__ __
|
||||
@@ -126,8 +123,8 @@ type CreateMilestoneForm struct {
|
||||
Deadline string `form:"due_date"`
|
||||
}
|
||||
|
||||
func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// .____ ___. .__
|
||||
@@ -142,8 +139,8 @@ type CreateLabelForm struct {
|
||||
Color string `form:"color" binding:"Required;Size(7)"`
|
||||
}
|
||||
|
||||
func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// __________ .__
|
||||
@@ -162,18 +159,17 @@ type NewReleaseForm struct {
|
||||
Prerelease bool `form:"prerelease"`
|
||||
}
|
||||
|
||||
func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type EditReleaseForm struct {
|
||||
Target string `form:"tag_target" binding:"Required"`
|
||||
Title string `form:"title" binding:"Required"`
|
||||
Content string `form:"content" binding:"Required"`
|
||||
Draft string `form:"draft"`
|
||||
Prerelease bool `form:"prerelease"`
|
||||
}
|
||||
|
||||
func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
"mime/multipart"
|
||||
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/binding"
|
||||
)
|
||||
|
||||
type InstallForm struct {
|
||||
@@ -34,8 +34,8 @@ type InstallForm struct {
|
||||
AdminEmail string `form:"admin_email" binding:"Required;Email;MaxSize(50)"`
|
||||
}
|
||||
|
||||
func (f *InstallForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// _____ ____ _________________ ___
|
||||
@@ -54,8 +54,8 @@ type RegisterForm struct {
|
||||
LoginName string `form:"loginname"`
|
||||
}
|
||||
|
||||
func (f *RegisterForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *RegisterForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type SignInForm struct {
|
||||
@@ -64,8 +64,8 @@ type SignInForm struct {
|
||||
Remember bool `form:"remember"`
|
||||
}
|
||||
|
||||
func (f *SignInForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *SignInForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// __________________________________________.___ _______ ________ _________
|
||||
@@ -84,8 +84,17 @@ type UpdateProfileForm struct {
|
||||
Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
|
||||
}
|
||||
|
||||
func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type UploadAvatarForm struct {
|
||||
Enable bool `form:"enable"`
|
||||
Avatar *multipart.FileHeader `form:"avatar"`
|
||||
}
|
||||
|
||||
func (f *UploadAvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type ChangePasswordForm struct {
|
||||
@@ -94,6 +103,23 @@ type ChangePasswordForm struct {
|
||||
Retype string `form:"retype"`
|
||||
}
|
||||
|
||||
func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type AddSSHKeyForm struct {
|
||||
SSHTitle string `form:"title" binding:"Required"`
|
||||
Content string `form:"content" binding:"Required"`
|
||||
}
|
||||
|
||||
func (f *AddSSHKeyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type NewAccessTokenForm struct {
|
||||
Name string `form:"name" binding:"Required"`
|
||||
}
|
||||
|
||||
func (f *NewAccessTokenForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
@@ -33,17 +33,27 @@ import (
|
||||
"github.com/nfnt/resize"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
gravatar = "http://www.gravatar.com/avatar"
|
||||
)
|
||||
var gravatarSource string
|
||||
|
||||
func init() {
|
||||
gravatarSource = setting.GravatarSource
|
||||
if !strings.HasPrefix(gravatarSource, "http:") {
|
||||
gravatarSource = "http:" + gravatarSource
|
||||
}
|
||||
}
|
||||
|
||||
// hash email to md5 string
|
||||
// keep this func in order to make this package indenpent
|
||||
// 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(strings.ToLower(email)))
|
||||
h.Write([]byte(email))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
@@ -115,13 +125,13 @@ func (this *Avatar) Encode(wr io.Writer, size int) (err error) {
|
||||
if img, err = decodeImageFile(imgPath); err != nil {
|
||||
return
|
||||
}
|
||||
m := resize.Resize(uint(size), 0, img, resize.Lanczos3)
|
||||
m := resize.Resize(uint(size), 0, img, resize.NearestNeighbor)
|
||||
return jpeg.Encode(wr, m, nil)
|
||||
}
|
||||
|
||||
// get image from gravatar.com
|
||||
func (this *Avatar) Update() {
|
||||
thunder.Fetch(gravatar+"/"+this.Hash+"?"+this.reqParams,
|
||||
thunder.Fetch(gravatarSource+this.Hash+"?"+this.reqParams,
|
||||
this.imagePath)
|
||||
}
|
||||
|
||||
@@ -129,7 +139,7 @@ func (this *Avatar) UpdateTimeout(timeout time.Duration) (err error) {
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
err = fmt.Errorf("get gravatar image %s timeout", this.Hash)
|
||||
case err = <-thunder.GoFetch(gravatar+"/"+this.Hash+"?"+this.reqParams,
|
||||
case err = <-thunder.GoFetch(gravatarSource+this.Hash+"?"+this.reqParams,
|
||||
this.imagePath):
|
||||
}
|
||||
return err
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
|
||||
package base
|
||||
|
||||
const DOC_URL = "http://gogs.io/docs"
|
||||
|
||||
type (
|
||||
TplName string
|
||||
|
||||
ApiJsonErr struct {
|
||||
Message string `json:"message"`
|
||||
DocUrl string `json:"documentation_url"`
|
||||
DocUrl string `json:"url"`
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/gogits/gfm"
|
||||
"github.com/russross/blackfriday"
|
||||
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
func isletter(c byte) bool {
|
||||
@@ -73,7 +75,7 @@ func IsReadmeFile(name string) bool {
|
||||
}
|
||||
|
||||
type CustomRender struct {
|
||||
gfm.Renderer
|
||||
blackfriday.Renderer
|
||||
urlPrefix string
|
||||
}
|
||||
|
||||
@@ -89,11 +91,20 @@ func (options *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte,
|
||||
options.Renderer.Link(out, link, title, content)
|
||||
}
|
||||
|
||||
func (options *CustomRender) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
|
||||
if len(link) > 0 && !isLink(link) {
|
||||
link = []byte(path.Join(strings.Replace(options.urlPrefix, "/src/", "/raw/", 1), string(link)))
|
||||
}
|
||||
|
||||
options.Renderer.Image(out, link, title, alt)
|
||||
}
|
||||
|
||||
var (
|
||||
MentionPattern = regexp.MustCompile(`@[0-9a-zA-Z_]{1,}`)
|
||||
commitPattern = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`)
|
||||
issueFullPattern = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
|
||||
issueIndexPattern = regexp.MustCompile(`#[0-9]+`)
|
||||
MentionPattern = regexp.MustCompile(`@[0-9a-zA-Z_]{1,}`)
|
||||
commitPattern = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`)
|
||||
issueFullPattern = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
|
||||
issueIndexPattern = regexp.MustCompile(`#[0-9]+`)
|
||||
sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`)
|
||||
)
|
||||
|
||||
func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte {
|
||||
@@ -112,7 +123,7 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte {
|
||||
ms := MentionPattern.FindAll(line, -1)
|
||||
for _, m := range ms {
|
||||
line = bytes.Replace(line, m,
|
||||
[]byte(fmt.Sprintf(`<a href="/user/%s">%s</a>`, m[1:], m)), -1)
|
||||
[]byte(fmt.Sprintf(`<a href="%s/user/%s">%s</a>`, setting.AppSubUrl, m[1:], m)), -1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +154,22 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte {
|
||||
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(
|
||||
` <a href="%s">#%s</a>`, m, ShortSha(string(m[i+7:j])))), -1)
|
||||
}
|
||||
ms = issueIndexPattern.FindAll(rawBytes, -1)
|
||||
rawBytes = RenderIssueIndexPattern(rawBytes, urlPrefix)
|
||||
rawBytes = RenderSha1CurrentPattern(rawBytes, urlPrefix)
|
||||
return rawBytes
|
||||
}
|
||||
|
||||
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, ShortSha(string(m)))), -1)
|
||||
}
|
||||
return rawBytes
|
||||
}
|
||||
|
||||
func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string) []byte {
|
||||
ms := issueIndexPattern.FindAll(rawBytes, -1)
|
||||
for _, m := range ms {
|
||||
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(
|
||||
`<a href="%s/issues/%s">%s</a>`, urlPrefix, m[1:], m)), -1)
|
||||
@@ -153,39 +179,40 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte {
|
||||
|
||||
func RenderRawMarkdown(body []byte, urlPrefix string) []byte {
|
||||
htmlFlags := 0
|
||||
// htmlFlags |= gfm.HTML_USE_XHTML
|
||||
// htmlFlags |= gfm.HTML_USE_SMARTYPANTS
|
||||
// htmlFlags |= gfm.HTML_SMARTYPANTS_FRACTIONS
|
||||
// htmlFlags |= gfm.HTML_SMARTYPANTS_LATEX_DASHES
|
||||
// htmlFlags |= gfm.HTML_SKIP_HTML
|
||||
htmlFlags |= gfm.HTML_SKIP_STYLE
|
||||
htmlFlags |= gfm.HTML_SKIP_SCRIPT
|
||||
htmlFlags |= gfm.HTML_GITHUB_BLOCKCODE
|
||||
htmlFlags |= gfm.HTML_OMIT_CONTENTS
|
||||
// htmlFlags |= gfm.HTML_COMPLETE_PAGE
|
||||
// htmlFlags |= blackfriday.HTML_USE_XHTML
|
||||
// htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
|
||||
// htmlFlags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS
|
||||
// htmlFlags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
|
||||
// htmlFlags |= blackfriday.HTML_SKIP_HTML
|
||||
htmlFlags |= blackfriday.HTML_SKIP_STYLE
|
||||
// htmlFlags |= blackfriday.HTML_SKIP_SCRIPT
|
||||
// htmlFlags |= blackfriday.HTML_GITHUB_BLOCKCODE
|
||||
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
|
||||
// htmlFlags |= blackfriday.HTML_COMPLETE_PAGE
|
||||
renderer := &CustomRender{
|
||||
Renderer: gfm.HtmlRenderer(htmlFlags, "", ""),
|
||||
Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
|
||||
urlPrefix: urlPrefix,
|
||||
}
|
||||
|
||||
// set up the parser
|
||||
extensions := 0
|
||||
extensions |= gfm.EXTENSION_NO_INTRA_EMPHASIS
|
||||
extensions |= gfm.EXTENSION_TABLES
|
||||
extensions |= gfm.EXTENSION_FENCED_CODE
|
||||
extensions |= gfm.EXTENSION_AUTOLINK
|
||||
extensions |= gfm.EXTENSION_STRIKETHROUGH
|
||||
extensions |= gfm.EXTENSION_HARD_LINE_BREAK
|
||||
extensions |= gfm.EXTENSION_SPACE_HEADERS
|
||||
extensions |= gfm.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
|
||||
extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
|
||||
extensions |= blackfriday.EXTENSION_TABLES
|
||||
extensions |= blackfriday.EXTENSION_FENCED_CODE
|
||||
extensions |= blackfriday.EXTENSION_AUTOLINK
|
||||
extensions |= blackfriday.EXTENSION_STRIKETHROUGH
|
||||
extensions |= blackfriday.EXTENSION_HARD_LINE_BREAK
|
||||
extensions |= blackfriday.EXTENSION_SPACE_HEADERS
|
||||
extensions |= blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
|
||||
|
||||
body = gfm.Markdown(body, renderer, extensions)
|
||||
body = blackfriday.Markdown(body, renderer, extensions)
|
||||
return body
|
||||
}
|
||||
|
||||
func RenderMarkdown(rawBytes []byte, urlPrefix string) []byte {
|
||||
body := RenderSpecialLink(rawBytes, urlPrefix)
|
||||
body = RenderRawMarkdown(body, urlPrefix)
|
||||
body = XSS(body)
|
||||
return body
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -48,18 +47,23 @@ func ShortSha(sha1 string) string {
|
||||
return sha1
|
||||
}
|
||||
|
||||
func ToUtf8WithErr(content []byte) (error, string) {
|
||||
func DetectEncoding(content []byte) (string, error) {
|
||||
detector := chardet.NewTextDetector()
|
||||
result, err := detector.DetectBest(content)
|
||||
return result.Charset, err
|
||||
}
|
||||
|
||||
func ToUtf8WithErr(content []byte) (error, string) {
|
||||
charset, err := DetectEncoding(content)
|
||||
if err != nil {
|
||||
return err, ""
|
||||
}
|
||||
|
||||
if result.Charset == "utf8" {
|
||||
if charset == "utf8" {
|
||||
return nil, string(content)
|
||||
}
|
||||
|
||||
decoder := mahonia.NewDecoder(result.Charset)
|
||||
decoder := mahonia.NewDecoder(charset)
|
||||
if decoder != nil {
|
||||
return nil, decoder.ConvertString(string(content))
|
||||
}
|
||||
@@ -82,6 +86,9 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
|
||||
"AppName": func() string {
|
||||
return setting.AppName
|
||||
},
|
||||
"AppSubUrl": func() string {
|
||||
return setting.AppSubUrl
|
||||
},
|
||||
"AppVer": func() string {
|
||||
return setting.AppVer
|
||||
},
|
||||
@@ -104,12 +111,11 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
|
||||
return a + b
|
||||
},
|
||||
"ActionIcon": ActionIcon,
|
||||
"ActionDesc": ActionDesc,
|
||||
"DateFormat": DateFormat,
|
||||
"List": List,
|
||||
"Mail2Domain": func(mail string) string {
|
||||
if !strings.Contains(mail, "@") {
|
||||
return "try.gogits.org"
|
||||
return "try.gogs.io"
|
||||
}
|
||||
|
||||
suffix := strings.SplitN(mail, "@", 2)[1]
|
||||
@@ -120,7 +126,17 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
|
||||
return domain
|
||||
},
|
||||
"SubStr": func(str string, start, length int) string {
|
||||
return str[start : start+length]
|
||||
if len(str) == 0 {
|
||||
return ""
|
||||
}
|
||||
end := start + length
|
||||
if length == -1 {
|
||||
end = len(str)
|
||||
}
|
||||
if len(str) < end {
|
||||
return str
|
||||
}
|
||||
return str[start:end]
|
||||
},
|
||||
"DiffTypeToStr": DiffTypeToStr,
|
||||
"DiffLineTypeToStr": DiffLineTypeToStr,
|
||||
@@ -130,6 +146,9 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
|
||||
"Oauth2Icon": Oauth2Icon,
|
||||
"Oauth2Name": Oauth2Name,
|
||||
"ToUtf8": ToUtf8,
|
||||
"EscapePound": func(str string) string {
|
||||
return strings.Replace(str, "#", "%23", -1)
|
||||
},
|
||||
}
|
||||
|
||||
type Actioner interface {
|
||||
@@ -146,14 +165,12 @@ type Actioner interface {
|
||||
// and returns a icon class name.
|
||||
func ActionIcon(opType int) string {
|
||||
switch opType {
|
||||
case 1: // Create repository.
|
||||
case 1, 8: // Create, transfer repository.
|
||||
return "repo"
|
||||
case 5, 9: // Commit repository.
|
||||
return "git-commit"
|
||||
case 6: // Create issue.
|
||||
return "issue-opened"
|
||||
case 8: // Transfer repository.
|
||||
return "share"
|
||||
case 10: // Comment issue.
|
||||
return "comment"
|
||||
default:
|
||||
@@ -161,19 +178,6 @@ func ActionIcon(opType int) string {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Legacy
|
||||
const (
|
||||
TPL_CREATE_REPO = `<a href="/user/%s">%s</a> created repository <a href="/%s">%s</a>`
|
||||
TPL_COMMIT_REPO = `<a href="/user/%s">%s</a> pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a>%s`
|
||||
TPL_COMMIT_REPO_LI = `<div><img src="%s?s=16" alt="user-avatar"/> <a href="/%s/commit/%s" rel="nofollow">%s</a> %s</div>`
|
||||
TPL_CREATE_ISSUE = `<a href="/user/%s">%s</a> opened issue <a href="/%s/issues/%s">%s#%s</a>
|
||||
<div><img src="%s?s=16" alt="user-avatar"/> %s</div>`
|
||||
TPL_TRANSFER_REPO = `<a href="/user/%s">%s</a> transfered repository <code>%s</code> to <a href="/%s">%s</a>`
|
||||
TPL_PUSH_TAG = `<a href="/user/%s">%s</a> pushed tag <a href="/%s/src/%s" rel="nofollow">%s</a> at <a href="/%s">%s</a>`
|
||||
TPL_COMMENT_ISSUE = `<a href="/user/%s">%s</a> commented on issue <a href="/%s/issues/%s">%s#%s</a>
|
||||
<div><img src="%s?s=16" alt="user-avatar"/> %s</div>`
|
||||
)
|
||||
|
||||
type PushCommit struct {
|
||||
Sha1 string
|
||||
Message string
|
||||
@@ -182,8 +186,9 @@ type PushCommit struct {
|
||||
}
|
||||
|
||||
type PushCommits struct {
|
||||
Len int
|
||||
Commits []*PushCommit
|
||||
Len int
|
||||
Commits []*PushCommit
|
||||
CompareUrl string
|
||||
}
|
||||
|
||||
func ActionContent2Commits(act Actioner) *PushCommits {
|
||||
@@ -194,52 +199,6 @@ func ActionContent2Commits(act Actioner) *PushCommits {
|
||||
return push
|
||||
}
|
||||
|
||||
// TODO: Legacy
|
||||
// ActionDesc accepts int that represents action operation type
|
||||
// and returns the description.
|
||||
func ActionDesc(act Actioner) string {
|
||||
actUserName := act.GetActUserName()
|
||||
email := act.GetActEmail()
|
||||
repoUserName := act.GetRepoUserName()
|
||||
repoName := act.GetRepoName()
|
||||
repoLink := repoUserName + "/" + repoName
|
||||
branch := act.GetBranch()
|
||||
content := act.GetContent()
|
||||
switch act.GetOpType() {
|
||||
case 1: // Create repository.
|
||||
return fmt.Sprintf(TPL_CREATE_REPO, actUserName, actUserName, repoLink, repoName)
|
||||
case 5: // Commit repository.
|
||||
var push *PushCommits
|
||||
if err := json.Unmarshal([]byte(content), &push); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte("\n"))
|
||||
for _, commit := range push.Commits {
|
||||
buf.WriteString(fmt.Sprintf(TPL_COMMIT_REPO_LI, AvatarLink(commit.AuthorEmail), repoLink, commit.Sha1, commit.Sha1[:7], commit.Message) + "\n")
|
||||
}
|
||||
if push.Len > 3 {
|
||||
buf.WriteString(fmt.Sprintf(`<div><a href="/%s/%s/commits/%s" rel="nofollow">%d other commits >></a></div>`, actUserName, repoName, branch, push.Len))
|
||||
}
|
||||
return fmt.Sprintf(TPL_COMMIT_REPO, actUserName, actUserName, repoLink, branch, branch, repoLink, repoLink,
|
||||
buf.String())
|
||||
case 6: // Create issue.
|
||||
infos := strings.SplitN(content, "|", 2)
|
||||
return fmt.Sprintf(TPL_CREATE_ISSUE, actUserName, actUserName, repoLink, infos[0], repoLink, infos[0],
|
||||
AvatarLink(email), infos[1])
|
||||
case 8: // Transfer repository.
|
||||
newRepoLink := content + "/" + repoName
|
||||
return fmt.Sprintf(TPL_TRANSFER_REPO, actUserName, actUserName, repoLink, newRepoLink, newRepoLink)
|
||||
case 9: // Push tag.
|
||||
return fmt.Sprintf(TPL_PUSH_TAG, actUserName, actUserName, repoLink, branch, branch, repoLink, repoLink)
|
||||
case 10: // Comment issue.
|
||||
infos := strings.SplitN(content, "|", 2)
|
||||
return fmt.Sprintf(TPL_COMMENT_ISSUE, actUserName, actUserName, repoLink, infos[0], repoLink, infos[0],
|
||||
AvatarLink(email), infos[1])
|
||||
default:
|
||||
return "invalid type"
|
||||
}
|
||||
}
|
||||
|
||||
func DiffTypeToStr(diffType int) string {
|
||||
diffTypes := map[int]string{
|
||||
1: "add", 2: "modify", 3: "del",
|
||||
|
||||
@@ -9,27 +9,51 @@ import (
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"html/template"
|
||||
"math"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/Unknwon/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/avatar"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
// Encode string to md5 hex value
|
||||
// Encode string to md5 hex value.
|
||||
func EncodeMd5(str string) string {
|
||||
m := md5.New()
|
||||
m.Write([]byte(str))
|
||||
return hex.EncodeToString(m.Sum(nil))
|
||||
}
|
||||
|
||||
// Encode string to sha1 hex value.
|
||||
func EncodeSha1(str string) string {
|
||||
h := sha1.New()
|
||||
h.Write([]byte(str))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func BasicAuthDecode(encoded string) (string, string, error) {
|
||||
s, err := base64.StdEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
auth := strings.SplitN(string(s), ":", 2)
|
||||
return auth[0], auth[1], nil
|
||||
}
|
||||
|
||||
func BasicAuthEncode(username, password string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
|
||||
}
|
||||
|
||||
// GetRandomString generate random string by specify chars.
|
||||
func GetRandomString(n int, alphabets ...byte) string {
|
||||
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
@@ -146,11 +170,14 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string
|
||||
// AvatarLink returns avatar link by given e-mail.
|
||||
func AvatarLink(email string) string {
|
||||
if setting.DisableGravatar {
|
||||
return "/img/avatar_default.jpg"
|
||||
} else if setting.Service.EnableCacheAvatar {
|
||||
return "/avatar/" + EncodeMd5(email)
|
||||
return setting.AppSubUrl + "/img/avatar_default.jpg"
|
||||
}
|
||||
return "//1.gravatar.com/avatar/" + EncodeMd5(email)
|
||||
|
||||
gravatarHash := avatar.HashEmail(email)
|
||||
if setting.Service.EnableCacheAvatar {
|
||||
return setting.AppSubUrl + "/avatar/" + gravatarHash
|
||||
}
|
||||
return setting.GravatarSource + gravatarHash
|
||||
}
|
||||
|
||||
// Seconds-based time units
|
||||
@@ -446,3 +473,29 @@ func DateFormat(t time.Time, format string) string {
|
||||
format = replacer.Replace(format)
|
||||
return t.Format(format)
|
||||
}
|
||||
|
||||
type xssFilter struct {
|
||||
reg *regexp.Regexp
|
||||
repl []byte
|
||||
}
|
||||
|
||||
var (
|
||||
whiteSpace = []byte(" ")
|
||||
xssFilters = []xssFilter{
|
||||
{regexp.MustCompile(`\ [ONon]\w*=["]*`), whiteSpace},
|
||||
{regexp.MustCompile(`<[SCRIPTscript]{6}`), whiteSpace},
|
||||
{regexp.MustCompile(`=[` + "`" + `'"]*[JAVASCRIPTjavascript \t\0
]*:`), whiteSpace},
|
||||
}
|
||||
)
|
||||
|
||||
// XSS goes through all the XSS filters to make user input content as safe as possible.
|
||||
func XSS(in []byte) []byte {
|
||||
for _, filter := range xssFilters {
|
||||
in = filter.reg.ReplaceAll(in, filter.repl)
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
func XSSString(in string) string {
|
||||
return string(XSS([]byte(in)))
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ Callers may register Funcs to be invoked on a given schedule. Cron will run
|
||||
them in their own goroutines.
|
||||
|
||||
c := cron.New()
|
||||
c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
|
||||
c.AddFunc("@hourly", func() { fmt.Println("Every hour") })
|
||||
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") })
|
||||
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.
|
||||
|
||||
@@ -16,6 +16,7 @@ var c = New()
|
||||
func NewCronContext() {
|
||||
c.AddFunc("Update mirrors", "@every 1h", models.MirrorUpdate)
|
||||
c.AddFunc("Deliver hooks", fmt.Sprintf("@every %dm", setting.WebhookTaskInterval), models.DeliverHooks)
|
||||
c.AddFunc("Repository health check", "@every 1h", models.GitFsck)
|
||||
c.Start()
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"container/list"
|
||||
"strings"
|
||||
)
|
||||
@@ -17,7 +18,8 @@ type Commit struct {
|
||||
Committer *Signature
|
||||
CommitMessage string
|
||||
|
||||
parents []sha1 // sha1 strings
|
||||
parents []sha1 // sha1 strings
|
||||
submodules map[string]*SubModule
|
||||
}
|
||||
|
||||
// Return the commit message. Same as retrieving CommitMessage directly.
|
||||
@@ -84,3 +86,49 @@ func (c *Commit) CommitsByRange(page int) (*list.List, error) {
|
||||
func (c *Commit) GetCommitOfRelPath(relPath string) (*Commit, error) {
|
||||
return c.repo.getCommitOfRelPath(c.Id, relPath)
|
||||
}
|
||||
|
||||
func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
|
||||
moduels, err := c.GetSubModules()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return moduels[entryname], nil
|
||||
}
|
||||
|
||||
func (c *Commit) GetSubModules() (map[string]*SubModule, error) {
|
||||
if c.submodules != nil {
|
||||
return c.submodules, nil
|
||||
}
|
||||
|
||||
entry, err := c.GetTreeEntryByPath(".gitmodules")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rd, err := entry.Blob().Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(rd)
|
||||
c.submodules = make(map[string]*SubModule)
|
||||
var ismodule bool
|
||||
var path string
|
||||
for scanner.Scan() {
|
||||
if strings.HasPrefix(scanner.Text(), "[submodule") {
|
||||
ismodule = true
|
||||
continue
|
||||
}
|
||||
if ismodule {
|
||||
fields := strings.Split(scanner.Text(), "=")
|
||||
k := strings.TrimSpace(fields[0])
|
||||
if k == "path" {
|
||||
path = strings.TrimSpace(fields[1])
|
||||
} else if k == "url" {
|
||||
c.submodules[path] = &SubModule{path, strings.TrimSpace(fields[1])}
|
||||
ismodule = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c.submodules, nil
|
||||
}
|
||||
|
||||
111
modules/git/hooks.go
Normal file
111
modules/git/hooks.go
Normal file
@@ -0,0 +1,111 @@
|
||||
// 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 git
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// hookNames is a list of Git hooks' name that are supported.
|
||||
var hookNames = []string{
|
||||
"pre-applypatch",
|
||||
"applypatch-msg",
|
||||
"prepare-commit-msg",
|
||||
"commit-msg",
|
||||
"pre-commit",
|
||||
"pre-rebase",
|
||||
"post-commit",
|
||||
"post-receive",
|
||||
"post-update",
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNotValidHook = errors.New("not a valid Git hook")
|
||||
)
|
||||
|
||||
// IsValidHookName returns true if given name is a valid Git hook.
|
||||
func IsValidHookName(name string) bool {
|
||||
for _, hn := range hookNames {
|
||||
if hn == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Hook represents a Git hook.
|
||||
type Hook struct {
|
||||
name string
|
||||
IsActive bool // Indicates whether repository has this hook.
|
||||
Content string // Content of hook if it's active.
|
||||
Sample string // Sample content from Git.
|
||||
path string // Hook file path.
|
||||
}
|
||||
|
||||
// GetHook returns a Git hook by given name and repository.
|
||||
func GetHook(repoPath, name string) (*Hook, error) {
|
||||
if !IsValidHookName(name) {
|
||||
return nil, ErrNotValidHook
|
||||
}
|
||||
h := &Hook{
|
||||
name: name,
|
||||
path: path.Join(repoPath, "hooks", name),
|
||||
}
|
||||
if isFile(h.path) {
|
||||
data, err := ioutil.ReadFile(h.path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.IsActive = true
|
||||
h.Content = string(data)
|
||||
} else if isFile(h.path + ".sample") {
|
||||
data, err := ioutil.ReadFile(h.path + ".sample")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.Sample = string(data)
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (h *Hook) Name() string {
|
||||
return h.name
|
||||
}
|
||||
|
||||
// Update updates hook settings.
|
||||
func (h *Hook) Update() error {
|
||||
if len(strings.TrimSpace(h.Content)) == 0 {
|
||||
return os.Remove(h.path)
|
||||
}
|
||||
return ioutil.WriteFile(h.path, []byte(strings.Replace(h.Content, "\r", "", -1)), os.ModePerm)
|
||||
}
|
||||
|
||||
// ListHooks returns a list of Git hooks of given repository.
|
||||
func ListHooks(repoPath string) (_ []*Hook, err error) {
|
||||
if !isDir(path.Join(repoPath, "hooks")) {
|
||||
return nil, errors.New("hooks path does not exist")
|
||||
}
|
||||
|
||||
hooks := make([]*Hook, len(hookNames))
|
||||
for i, name := range hookNames {
|
||||
hooks[i], err = GetHook(repoPath, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return hooks, nil
|
||||
}
|
||||
|
||||
func (repo *Repository) GetHook(name string) (*Hook, error) {
|
||||
return GetHook(repo.Path, name)
|
||||
}
|
||||
|
||||
func (repo *Repository) Hooks() ([]*Hook, error) {
|
||||
return ListHooks(repo.Path)
|
||||
}
|
||||
@@ -40,11 +40,11 @@ func (repo *Repository) GetCommitIdOfTag(tagName string) (string, error) {
|
||||
}
|
||||
|
||||
func (repo *Repository) GetCommitOfTag(tagName string) (*Commit, error) {
|
||||
commitId, err := repo.GetCommitIdOfTag(tagName)
|
||||
tag, err := repo.GetTag(tagName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repo.GetCommit(commitId)
|
||||
return tag.Commit()
|
||||
}
|
||||
|
||||
// Parse commit information from the (uncompressed) raw
|
||||
|
||||
@@ -22,6 +22,9 @@ func (repo *Repository) IsTagExist(tagName string) bool {
|
||||
|
||||
// GetTags returns all tags of given repository.
|
||||
func (repo *Repository) GetTags() ([]string, error) {
|
||||
if gitVer.AtLeast(MustParseVersion("2.0.0")) {
|
||||
return repo.getTagsReversed()
|
||||
}
|
||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l")
|
||||
if err != nil {
|
||||
return nil, errors.New(stderr)
|
||||
@@ -30,6 +33,15 @@ func (repo *Repository) GetTags() ([]string, error) {
|
||||
return tags[:len(tags)-1], nil
|
||||
}
|
||||
|
||||
func (repo *Repository) getTagsReversed() ([]string, error) {
|
||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l", "--sort=-v:refname")
|
||||
if err != nil {
|
||||
return nil, errors.New(stderr)
|
||||
}
|
||||
tags := strings.Split(stdout, "\n")
|
||||
return tags[:len(tags)-1], nil
|
||||
}
|
||||
|
||||
func (repo *Repository) CreateTag(tagName, idStr string) error {
|
||||
_, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", tagName, idStr)
|
||||
if err != nil {
|
||||
@@ -52,6 +64,7 @@ func (repo *Repository) getTag(id sha1) (*Tag, error) {
|
||||
if err != nil {
|
||||
return nil, errors.New(stderr)
|
||||
}
|
||||
tp = strings.TrimSpace(tp)
|
||||
|
||||
// Tag is a commit.
|
||||
if ObjectType(tp) == COMMIT {
|
||||
|
||||
58
modules/git/submodule.go
Normal file
58
modules/git/submodule.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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 git
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SubModule struct {
|
||||
Name string
|
||||
Url string
|
||||
}
|
||||
|
||||
// SubModuleFile represents a file with submodule type.
|
||||
type SubModuleFile struct {
|
||||
*Commit
|
||||
|
||||
refUrl string
|
||||
refId string
|
||||
}
|
||||
|
||||
func NewSubModuleFile(c *Commit, refUrl, refId string) *SubModuleFile {
|
||||
return &SubModuleFile{
|
||||
Commit: c,
|
||||
refUrl: refUrl,
|
||||
refId: refId,
|
||||
}
|
||||
}
|
||||
|
||||
// RefUrl guesses and returns reference URL.
|
||||
func (sf *SubModuleFile) RefUrl() string {
|
||||
url := strings.TrimSuffix(sf.refUrl, ".git")
|
||||
|
||||
// git://xxx/user/repo
|
||||
if strings.HasPrefix(url, "git://") {
|
||||
return "http://" + strings.TrimPrefix(url, "git://")
|
||||
}
|
||||
|
||||
// http[s]://xxx/user/repo
|
||||
if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
|
||||
return url
|
||||
}
|
||||
|
||||
// sysuser@xxx:user/repo
|
||||
i := strings.Index(url, "@")
|
||||
j := strings.LastIndex(url, ":")
|
||||
if i > -1 && j > -1 {
|
||||
return "http://" + url[i+1:j] + "/" + url[j+1:]
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
// RefId returns reference ID.
|
||||
func (sf *SubModuleFile) RefId() string {
|
||||
return sf.refId
|
||||
}
|
||||
@@ -51,6 +51,8 @@ func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) {
|
||||
case "160000":
|
||||
entry.mode = ModeCommit
|
||||
entry.Type = COMMIT
|
||||
|
||||
step = 8
|
||||
case "040000":
|
||||
entry.mode = ModeTree
|
||||
entry.Type = TREE
|
||||
@@ -107,9 +109,12 @@ func (t *Tree) ListEntries(relpath string) (Entries, error) {
|
||||
}
|
||||
t.entriesParsed = true
|
||||
|
||||
stdout, _, err := com.ExecCmdDirBytes(t.repo.Path,
|
||||
stdout, stderr, err := com.ExecCmdDirBytes(t.repo.Path,
|
||||
"git", "ls-tree", t.Id.String())
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "exit status 128") {
|
||||
return nil, errors.New(strings.TrimSpace(string(stderr)))
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
t.entries, err = parseTreeData(t, stdout)
|
||||
|
||||
@@ -61,6 +61,10 @@ func (te *TreeEntry) Size() int64 {
|
||||
return te.size
|
||||
}
|
||||
|
||||
func (te *TreeEntry) IsSubModule() bool {
|
||||
return te.mode == ModeCommit
|
||||
}
|
||||
|
||||
func (te *TreeEntry) IsDir() bool {
|
||||
return te.mode == ModeTree
|
||||
}
|
||||
@@ -80,7 +84,7 @@ type Entries []*TreeEntry
|
||||
|
||||
var sorter = []func(t1, t2 *TreeEntry) bool{
|
||||
func(t1, t2 *TreeEntry) bool {
|
||||
return t1.IsDir() && !t2.IsDir()
|
||||
return (t1.IsDir() || t1.IsSubModule()) && !t2.IsDir() && !t2.IsSubModule()
|
||||
},
|
||||
func(t1, t2 *TreeEntry) bool {
|
||||
return t1.name < t2.name
|
||||
|
||||
@@ -7,6 +7,7 @@ package git
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
@@ -46,3 +47,23 @@ func RefEndName(refStr string) string {
|
||||
func filepathFromSHA1(rootdir, sha1 string) string {
|
||||
return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:])
|
||||
}
|
||||
|
||||
// isDir returns true if given path is a directory,
|
||||
// or returns false when it's a file or does not exist.
|
||||
func isDir(dir string) bool {
|
||||
f, e := os.Stat(dir)
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
return f.IsDir()
|
||||
}
|
||||
|
||||
// isFile returns true if given path is a file,
|
||||
// or returns false when it's a directory or does not exist.
|
||||
func isFile(filePath string) bool {
|
||||
f, e := os.Stat(filePath)
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
return !f.IsDir()
|
||||
}
|
||||
|
||||
@@ -74,6 +74,10 @@ func (v *Version) LessThan(that *Version) bool {
|
||||
return v.Compare(that) < 0
|
||||
}
|
||||
|
||||
func (v *Version) AtLeast(that *Version) bool {
|
||||
return v.Compare(that) >= 0
|
||||
}
|
||||
|
||||
// GetVersion returns current Git version installed.
|
||||
func GetVersion() (*Version, error) {
|
||||
if gitVer != nil {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
package httplib
|
||||
|
||||
// NOTE: last sync c07b1d8 on Aug 23, 2014.
|
||||
// NOTE: last sync 57e62e5 on Oct 29, 2014.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -252,35 +253,36 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
|
||||
} else {
|
||||
b.url = b.url + "?" + paramBody
|
||||
}
|
||||
} else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 {
|
||||
} else if b.req.Method == "POST" && b.req.Body == nil {
|
||||
if len(b.files) > 0 {
|
||||
bodyBuf := &bytes.Buffer{}
|
||||
bodyWriter := multipart.NewWriter(bodyBuf)
|
||||
for formname, filename := range b.files {
|
||||
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
pr, pw := io.Pipe()
|
||||
bodyWriter := multipart.NewWriter(pw)
|
||||
go func() {
|
||||
for formname, filename := range b.files {
|
||||
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fh, err := os.Open(filename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
//iocopy
|
||||
_, err = io.Copy(fileWriter, fh)
|
||||
fh.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
fh, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for k, v := range b.params {
|
||||
bodyWriter.WriteField(k, v)
|
||||
}
|
||||
//iocopy
|
||||
_, err = io.Copy(fileWriter, fh)
|
||||
fh.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for k, v := range b.params {
|
||||
bodyWriter.WriteField(k, v)
|
||||
}
|
||||
contentType := bodyWriter.FormDataContentType()
|
||||
bodyWriter.Close()
|
||||
b.Header("Content-Type", contentType)
|
||||
b.req.Body = ioutil.NopCloser(bodyBuf)
|
||||
b.req.ContentLength = int64(bodyBuf.Len())
|
||||
} else {
|
||||
bodyWriter.Close()
|
||||
pw.Close()
|
||||
}()
|
||||
b.Header("Content-Type", bodyWriter.FormDataContentType())
|
||||
b.req.Body = ioutil.NopCloser(pr)
|
||||
} else if len(paramBody) > 0 {
|
||||
b.Header("Content-Type", "application/x-www-form-urlencoded")
|
||||
b.Body(paramBody)
|
||||
}
|
||||
@@ -332,7 +334,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
|
||||
Jar: jar,
|
||||
}
|
||||
|
||||
if b.setting.UserAgent != "" {
|
||||
if len(b.setting.UserAgent) > 0 && len(b.req.Header.Get("User-Agent")) == 0 {
|
||||
b.req.Header.Set("User-Agent", b.setting.UserAgent)
|
||||
}
|
||||
|
||||
|
||||
@@ -57,23 +57,23 @@ func TestSimplePost(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostFile(t *testing.T) {
|
||||
v := "smallfish"
|
||||
req := Post("http://httpbin.org/post")
|
||||
req.Param("username", v)
|
||||
req.PostFile("uploadfile", "httplib_test.go")
|
||||
// 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)
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
// 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()
|
||||
@@ -194,3 +194,13 @@ func TestToFile(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/gogits/gogs/modules/asn1-ber"
|
||||
)
|
||||
|
||||
// debbuging type
|
||||
// debugging type
|
||||
// - has a Printf method to write the debug output
|
||||
type debugging bool
|
||||
|
||||
|
||||
@@ -87,6 +87,12 @@ func Fatal(skip int, format string, v ...interface{}) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func Close() {
|
||||
for _, l := range loggers {
|
||||
l.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// .___ __ _____
|
||||
// | | _____/ |_ ____________/ ____\____ ____ ____
|
||||
// | |/ \ __\/ __ \_ __ \ __\\__ \ _/ ___\/ __ \
|
||||
@@ -156,7 +162,7 @@ func newLogger(buffer int64) *Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
// SetLogger sets new logger instanse with given logger adapter and config.
|
||||
// SetLogger sets new logger instance with given logger adapter and config.
|
||||
func (l *Logger) SetLogger(adapter string, config string) error {
|
||||
l.lock.Lock()
|
||||
defer l.lock.Unlock()
|
||||
|
||||
@@ -32,7 +32,7 @@ const (
|
||||
)
|
||||
|
||||
// A Decoder is a function that decodes a character set, one character at a time.
|
||||
// It works much like utf8.DecodeRune, but has an aditional status return value.
|
||||
// It works much like utf8.DecodeRune, but has an additional status return value.
|
||||
type Decoder func(p []byte) (c rune, size int, status Status)
|
||||
|
||||
// An Encoder is a function that encodes a character set, one character at a time.
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
package mailer
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
@@ -33,7 +35,7 @@ func (m Message) Content() string {
|
||||
}
|
||||
|
||||
// create mail content
|
||||
content := "From: " + m.From + "<" + m.User +
|
||||
content := "From: \"" + m.From + "\" <" + m.User +
|
||||
">\r\nSubject: " + m.Subject + "\r\nContent-Type: " + contentType + "\r\n\r\n" + m.Body
|
||||
return content
|
||||
}
|
||||
@@ -64,6 +66,53 @@ func processMailQueue() {
|
||||
}
|
||||
}
|
||||
|
||||
// sendMail allows mail with self-signed certificates.
|
||||
func sendMail(hostAddressWithPort string, auth smtp.Auth, from string, recipients []string, msgContent []byte) error {
|
||||
client, err := smtp.Dial(hostAddressWithPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, _, _ := net.SplitHostPort(hostAddressWithPort)
|
||||
tlsConn := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: host,
|
||||
}
|
||||
if err = client.StartTLS(tlsConn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok, _ := client.Extension("AUTH"); ok && auth != nil {
|
||||
if err = client.Auth(auth); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = client.Mail(from); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rec := range recipients {
|
||||
if err = client.Rcpt(rec); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
w, err := client.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = w.Write([]byte(msgContent)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = w.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return client.Quit()
|
||||
}
|
||||
|
||||
// Direct Send mail message
|
||||
func Send(msg *Message) (int, error) {
|
||||
log.Trace("Sending mails to: %s", strings.Join(msg.To, "; "))
|
||||
@@ -78,14 +127,17 @@ func Send(msg *Message) (int, error) {
|
||||
return 0, fmt.Errorf("empty email body")
|
||||
}
|
||||
|
||||
auth := smtp.PlainAuth("", setting.MailService.User, setting.MailService.Passwd, host[0])
|
||||
var auth smtp.Auth
|
||||
if len(setting.MailService.Passwd) > 0 {
|
||||
auth = smtp.PlainAuth("", setting.MailService.User, setting.MailService.Passwd, host[0])
|
||||
}
|
||||
|
||||
if msg.Massive {
|
||||
// send mail to multiple emails one by one
|
||||
num := 0
|
||||
for _, to := range msg.To {
|
||||
body := []byte("To: " + to + "\r\n" + content)
|
||||
err := smtp.SendMail(setting.MailService.Host, auth, msg.From, []string{to}, body)
|
||||
err := sendMail(setting.MailService.Host, auth, msg.From, []string{to}, body)
|
||||
if err != nil {
|
||||
return num, err
|
||||
}
|
||||
@@ -96,7 +148,7 @@ func Send(msg *Message) (int, error) {
|
||||
body := []byte("To: " + strings.Join(msg.To, ";") + "\r\n" + content)
|
||||
|
||||
// send to multiple emails in one message
|
||||
err := smtp.SendMail(setting.MailService.Host, auth, msg.From, msg.To, body)
|
||||
err := sendMail(setting.MailService.Host, auth, msg.From, msg.To, body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
|
||||
@@ -25,13 +25,19 @@ func Toggle(options *ToggleOptions) macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
// Cannot view any page before installation.
|
||||
if !setting.InstallLock {
|
||||
ctx.Redirect("/install")
|
||||
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(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("/")
|
||||
ctx.Redirect(setting.AppSubUrl + "/")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -48,8 +54,8 @@ func Toggle(options *ToggleOptions) macaron.Handler {
|
||||
if strings.HasSuffix(ctx.Req.RequestURI, "watch") {
|
||||
return
|
||||
}
|
||||
ctx.SetCookie("redirect_to", "/"+url.QueryEscape(ctx.Req.RequestURI))
|
||||
ctx.Redirect("/user/login")
|
||||
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")
|
||||
@@ -67,3 +73,21 @@ func Toggle(options *ToggleOptions) macaron.Handler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ApiReqToken() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.IsSigned {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ApiReqBasicAuth() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.IsBasicAuth {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,472 +0,0 @@
|
||||
// Copyright 2013 The Martini Contrib 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 binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
)
|
||||
|
||||
/*
|
||||
To the land of Middle-ware Earth:
|
||||
|
||||
One func to rule them all,
|
||||
One func to find them,
|
||||
One func to bring them all,
|
||||
And in this package BIND them.
|
||||
*/
|
||||
|
||||
// Bind accepts a copy of an empty struct and populates it with
|
||||
// values from the request (if deserialization is successful). It
|
||||
// wraps up the functionality of the Form and Json middleware
|
||||
// according to the Content-Type of the request, and it guesses
|
||||
// if no Content-Type is specified. Bind invokes the ErrorHandler
|
||||
// middleware to bail out if errors occurred. If you want to perform
|
||||
// your own error handling, use Form or Json middleware directly.
|
||||
// An interface pointer can be added as a second argument in order
|
||||
// to map the struct to a specific interface.
|
||||
func Bind(obj interface{}, ifacePtr ...interface{}) macaron.Handler {
|
||||
return func(ctx *macaron.Context) {
|
||||
contentType := ctx.Req.Header.Get("Content-Type")
|
||||
|
||||
if strings.Contains(contentType, "form-urlencoded") {
|
||||
ctx.Invoke(Form(obj, ifacePtr...))
|
||||
} else if strings.Contains(contentType, "multipart/form-data") {
|
||||
ctx.Invoke(MultipartForm(obj, ifacePtr...))
|
||||
} else if strings.Contains(contentType, "json") {
|
||||
ctx.Invoke(Json(obj, ifacePtr...))
|
||||
} else {
|
||||
ctx.Invoke(Json(obj, ifacePtr...))
|
||||
if getErrors(ctx).Count() > 0 {
|
||||
ctx.Invoke(Form(obj, ifacePtr...))
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Invoke(ErrorHandler)
|
||||
}
|
||||
}
|
||||
|
||||
// BindIgnErr will do the exactly same thing as Bind but without any
|
||||
// error handling, which user has freedom to deal with them.
|
||||
// This allows user take advantages of validation.
|
||||
func BindIgnErr(obj interface{}, ifacePtr ...interface{}) macaron.Handler {
|
||||
return func(ctx *macaron.Context, req *http.Request) {
|
||||
contentType := req.Header.Get("Content-Type")
|
||||
|
||||
if strings.Contains(contentType, "form-urlencoded") {
|
||||
ctx.Invoke(Form(obj, ifacePtr...))
|
||||
} else if strings.Contains(contentType, "multipart/form-data") {
|
||||
ctx.Invoke(MultipartForm(obj, ifacePtr...))
|
||||
} else if strings.Contains(contentType, "json") {
|
||||
ctx.Invoke(Json(obj, ifacePtr...))
|
||||
} else {
|
||||
ctx.Invoke(Json(obj, ifacePtr...))
|
||||
if getErrors(ctx).Count() > 0 {
|
||||
ctx.Invoke(Form(obj, ifacePtr...))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Form is middleware to deserialize form-urlencoded data from the request.
|
||||
// It gets data from the form-urlencoded body, if present, or from the
|
||||
// query string. It uses the http.Request.ParseForm() method
|
||||
// to perform deserialization, then reflection is used to map each field
|
||||
// into the struct with the proper type. Structs with primitive slice types
|
||||
// (bool, float, int, string) can support deserialization of repeated form
|
||||
// keys, for example: key=val1&key=val2&key=val3
|
||||
// An interface pointer can be added as a second argument in order
|
||||
// to map the struct to a specific interface.
|
||||
func Form(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
|
||||
return func(ctx *macaron.Context) {
|
||||
ensureNotPointer(formStruct)
|
||||
formStruct := reflect.New(reflect.TypeOf(formStruct))
|
||||
errors := newErrors()
|
||||
parseErr := ctx.Req.ParseForm()
|
||||
|
||||
// Format validation of the request body or the URL would add considerable overhead,
|
||||
// and ParseForm does not complain when URL encoding is off.
|
||||
// Because an empty request body or url can also mean absence of all needed values,
|
||||
// it is not in all cases a bad request, so let's return 422.
|
||||
if parseErr != nil {
|
||||
errors.Overall[BindingDeserializationError] = parseErr.Error()
|
||||
}
|
||||
|
||||
mapForm(formStruct, ctx.Req.Form, errors)
|
||||
|
||||
validateAndMap(formStruct, ctx, errors, ifacePtr...)
|
||||
}
|
||||
}
|
||||
|
||||
func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
|
||||
return func(ctx *macaron.Context) {
|
||||
ensureNotPointer(formStruct)
|
||||
formStruct := reflect.New(reflect.TypeOf(formStruct))
|
||||
errors := newErrors()
|
||||
|
||||
// Workaround for multipart forms returning nil instead of an error
|
||||
// when content is not multipart
|
||||
// https://code.google.com/p/go/issues/detail?id=6334
|
||||
multipartReader, err := ctx.Req.MultipartReader()
|
||||
if err != nil {
|
||||
errors.Overall[BindingDeserializationError] = err.Error()
|
||||
} else {
|
||||
form, parseErr := multipartReader.ReadForm(MaxMemory)
|
||||
|
||||
if parseErr != nil {
|
||||
errors.Overall[BindingDeserializationError] = parseErr.Error()
|
||||
}
|
||||
|
||||
ctx.Req.MultipartForm = form
|
||||
}
|
||||
|
||||
mapForm(formStruct, ctx.Req.MultipartForm.Value, errors)
|
||||
|
||||
validateAndMap(formStruct, ctx, errors, ifacePtr...)
|
||||
}
|
||||
}
|
||||
|
||||
// Json is middleware to deserialize a JSON payload from the request
|
||||
// into the struct that is passed in. The resulting struct is then
|
||||
// validated, but no error handling is actually performed here.
|
||||
// An interface pointer can be added as a second argument in order
|
||||
// to map the struct to a specific interface.
|
||||
func Json(jsonStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
|
||||
return func(ctx *macaron.Context) {
|
||||
ensureNotPointer(jsonStruct)
|
||||
jsonStruct := reflect.New(reflect.TypeOf(jsonStruct))
|
||||
errors := newErrors()
|
||||
|
||||
if ctx.Req.Body != nil {
|
||||
defer ctx.Req.Body.Close()
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(ctx.Req.Body).Decode(jsonStruct.Interface()); err != nil && err != io.EOF {
|
||||
errors.Overall[BindingDeserializationError] = err.Error()
|
||||
}
|
||||
|
||||
validateAndMap(jsonStruct, ctx, errors, ifacePtr...)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate is middleware to enforce required fields. If the struct
|
||||
// passed in is a Validator, then the user-defined Validate method
|
||||
// is executed, and its errors are mapped to the context. This middleware
|
||||
// performs no error handling: it merely detects them and maps them.
|
||||
func Validate(obj interface{}) macaron.Handler {
|
||||
return func(ctx *macaron.Context, l i18n.Locale) {
|
||||
errors := newErrors()
|
||||
validateStruct(errors, obj)
|
||||
|
||||
if validator, ok := obj.(Validator); ok {
|
||||
validator.Validate(ctx, errors, l)
|
||||
}
|
||||
ctx.Map(*errors)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
alphaDashPattern = regexp.MustCompile("[^\\d\\w-_]")
|
||||
alphaDashDotPattern = regexp.MustCompile("[^\\d\\w-_\\.]")
|
||||
emailPattern = regexp.MustCompile("[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[a-zA-Z0-9](?:[\\w-]*[\\w])?")
|
||||
urlPattern = regexp.MustCompile(`(http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?`)
|
||||
)
|
||||
|
||||
func validateStruct(errors *Errors, obj interface{}) {
|
||||
typ := reflect.TypeOf(obj)
|
||||
val := reflect.ValueOf(obj)
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
|
||||
// Allow ignored fields in the struct
|
||||
if field.Tag.Get("form") == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldValue := val.Field(i).Interface()
|
||||
if field.Type.Kind() == reflect.Struct {
|
||||
validateStruct(errors, fieldValue)
|
||||
continue
|
||||
}
|
||||
|
||||
zero := reflect.Zero(field.Type).Interface()
|
||||
|
||||
// Match rules.
|
||||
for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
|
||||
if len(rule) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case rule == "Required":
|
||||
if reflect.DeepEqual(zero, fieldValue) {
|
||||
errors.Fields[field.Name] = BindingRequireError
|
||||
break
|
||||
}
|
||||
case rule == "AlphaDash":
|
||||
if alphaDashPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
|
||||
errors.Fields[field.Name] = BindingAlphaDashError
|
||||
break
|
||||
}
|
||||
case rule == "AlphaDashDot":
|
||||
if alphaDashDotPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
|
||||
errors.Fields[field.Name] = BindingAlphaDashDotError
|
||||
break
|
||||
}
|
||||
case strings.HasPrefix(rule, "MinSize("):
|
||||
min, err := strconv.Atoi(rule[8 : len(rule)-1])
|
||||
if err != nil {
|
||||
errors.Overall["MinSize"] = err.Error()
|
||||
break
|
||||
}
|
||||
if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) < min {
|
||||
errors.Fields[field.Name] = BindingMinSizeError
|
||||
break
|
||||
}
|
||||
v := reflect.ValueOf(fieldValue)
|
||||
if v.Kind() == reflect.Slice && v.Len() < min {
|
||||
errors.Fields[field.Name] = BindingMinSizeError
|
||||
break
|
||||
}
|
||||
case strings.HasPrefix(rule, "MaxSize("):
|
||||
max, err := strconv.Atoi(rule[8 : len(rule)-1])
|
||||
if err != nil {
|
||||
errors.Overall["MaxSize"] = err.Error()
|
||||
break
|
||||
}
|
||||
if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) > max {
|
||||
errors.Fields[field.Name] = BindingMaxSizeError
|
||||
break
|
||||
}
|
||||
v := reflect.ValueOf(fieldValue)
|
||||
if v.Kind() == reflect.Slice && v.Len() > max {
|
||||
errors.Fields[field.Name] = BindingMinSizeError
|
||||
break
|
||||
}
|
||||
case rule == "Email":
|
||||
if !emailPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
|
||||
errors.Fields[field.Name] = BindingEmailError
|
||||
break
|
||||
}
|
||||
case rule == "Url":
|
||||
str := fmt.Sprintf("%v", fieldValue)
|
||||
if len(str) == 0 {
|
||||
continue
|
||||
} else if !urlPattern.MatchString(str) {
|
||||
errors.Fields[field.Name] = BindingUrlError
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mapForm(formStruct reflect.Value, form map[string][]string, errors *Errors) {
|
||||
typ := formStruct.Elem().Type()
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
typeField := typ.Field(i)
|
||||
if inputFieldName := typeField.Tag.Get("form"); inputFieldName != "" {
|
||||
structField := formStruct.Elem().Field(i)
|
||||
if !structField.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
inputValue, exists := form[inputFieldName]
|
||||
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
numElems := len(inputValue)
|
||||
if structField.Kind() == reflect.Slice && numElems > 0 {
|
||||
sliceOf := structField.Type().Elem().Kind()
|
||||
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
|
||||
for i := 0; i < numElems; i++ {
|
||||
setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors)
|
||||
}
|
||||
formStruct.Elem().Field(i).Set(slice)
|
||||
} else {
|
||||
setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorHandler simply counts the number of errors in the
|
||||
// context and, if more than 0, writes a 400 Bad Request
|
||||
// response and a JSON payload describing the errors with
|
||||
// the "Content-Type" set to "application/json".
|
||||
// Middleware remaining on the stack will not even see the request
|
||||
// if, by this point, there are any errors.
|
||||
// This is a "default" handler, of sorts, and you are
|
||||
// welcome to use your own instead. The Bind middleware
|
||||
// invokes this automatically for convenience.
|
||||
func ErrorHandler(errs Errors, resp http.ResponseWriter) {
|
||||
if errs.Count() > 0 {
|
||||
resp.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
if _, ok := errs.Overall[BindingDeserializationError]; ok {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
} else {
|
||||
resp.WriteHeader(422)
|
||||
}
|
||||
errOutput, _ := json.Marshal(errs)
|
||||
resp.Write(errOutput)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// This sets the value in a struct of an indeterminate type to the
|
||||
// matching value from the request (via Form middleware) in the
|
||||
// same type, so that not all deserialized values have to be strings.
|
||||
// Supported types are string, int, float, and bool.
|
||||
func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value, nameInTag string, errors *Errors) {
|
||||
switch valueKind {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if val == "" {
|
||||
val = "0"
|
||||
}
|
||||
intVal, err := strconv.ParseInt(val, 10, 64)
|
||||
if err != nil {
|
||||
errors.Fields[nameInTag] = BindingIntegerTypeError
|
||||
} else {
|
||||
structField.SetInt(intVal)
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
if val == "" {
|
||||
val = "0"
|
||||
}
|
||||
uintVal, err := strconv.ParseUint(val, 10, 64)
|
||||
if err != nil {
|
||||
errors.Fields[nameInTag] = BindingIntegerTypeError
|
||||
} else {
|
||||
structField.SetUint(uintVal)
|
||||
}
|
||||
case reflect.Bool:
|
||||
structField.SetBool(val == "on")
|
||||
case reflect.Float32:
|
||||
if val == "" {
|
||||
val = "0.0"
|
||||
}
|
||||
floatVal, err := strconv.ParseFloat(val, 32)
|
||||
if err != nil {
|
||||
errors.Fields[nameInTag] = BindingFloatTypeError
|
||||
} else {
|
||||
structField.SetFloat(floatVal)
|
||||
}
|
||||
case reflect.Float64:
|
||||
if val == "" {
|
||||
val = "0.0"
|
||||
}
|
||||
floatVal, err := strconv.ParseFloat(val, 64)
|
||||
if err != nil {
|
||||
errors.Fields[nameInTag] = BindingFloatTypeError
|
||||
} else {
|
||||
structField.SetFloat(floatVal)
|
||||
}
|
||||
case reflect.String:
|
||||
structField.SetString(val)
|
||||
}
|
||||
}
|
||||
|
||||
// Don't pass in pointers to bind to. Can lead to bugs.
|
||||
func ensureNotPointer(obj interface{}) {
|
||||
if reflect.TypeOf(obj).Kind() == reflect.Ptr {
|
||||
panic("Pointers are not accepted as binding models")
|
||||
}
|
||||
}
|
||||
|
||||
// Performs validation and combines errors from validation
|
||||
// with errors from deserialization, then maps both the
|
||||
// resulting struct and the errors to the context.
|
||||
func validateAndMap(obj reflect.Value, ctx *macaron.Context, errors *Errors, ifacePtr ...interface{}) {
|
||||
ctx.Invoke(Validate(obj.Interface()))
|
||||
errors.Combine(getErrors(ctx))
|
||||
ctx.Map(*errors)
|
||||
ctx.Map(obj.Elem().Interface())
|
||||
if len(ifacePtr) > 0 {
|
||||
ctx.MapTo(obj.Elem().Interface(), ifacePtr[0])
|
||||
}
|
||||
}
|
||||
|
||||
func newErrors() *Errors {
|
||||
return &Errors{make(map[string]string), make(map[string]string)}
|
||||
}
|
||||
|
||||
func getErrors(ctx *macaron.Context) Errors {
|
||||
return ctx.GetVal(reflect.TypeOf(Errors{})).Interface().(Errors)
|
||||
}
|
||||
|
||||
type (
|
||||
// Implement the Validator interface to define your own input
|
||||
// validation before the request even gets to your application.
|
||||
// The Validate method will be executed during the validation phase.
|
||||
Validator interface {
|
||||
Validate(*macaron.Context, *Errors, i18n.Locale)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// Maximum amount of memory to use when parsing a multipart form.
|
||||
// Set this to whatever value you prefer; default is 10 MB.
|
||||
MaxMemory = int64(1024 * 1024 * 10)
|
||||
)
|
||||
|
||||
// Errors represents the contract of the response body when the
|
||||
// binding step fails before getting to the application.
|
||||
type Errors struct {
|
||||
Overall map[string]string `json:"overall"`
|
||||
Fields map[string]string `json:"fields"`
|
||||
}
|
||||
|
||||
// Total errors is the sum of errors with the request overall
|
||||
// and errors on individual fields.
|
||||
func (err Errors) Count() int {
|
||||
return len(err.Overall) + len(err.Fields)
|
||||
}
|
||||
|
||||
func (this *Errors) Combine(other Errors) {
|
||||
for key, val := range other.Fields {
|
||||
if _, exists := this.Fields[key]; !exists {
|
||||
this.Fields[key] = val
|
||||
}
|
||||
}
|
||||
for key, val := range other.Overall {
|
||||
if _, exists := this.Overall[key]; !exists {
|
||||
this.Overall[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
BindingRequireError string = "Required"
|
||||
BindingAlphaDashError string = "AlphaDash"
|
||||
BindingAlphaDashDotError string = "AlphaDashDot"
|
||||
BindingMinSizeError string = "MinSize"
|
||||
BindingMaxSizeError string = "MaxSize"
|
||||
BindingEmailError string = "Email"
|
||||
BindingUrlError string = "Url"
|
||||
BindingDeserializationError string = "DeserializationError"
|
||||
BindingIntegerTypeError string = "IntegerTypeError"
|
||||
BindingBooleanTypeError string = "BooleanTypeError"
|
||||
BindingFloatTypeError string = "FloatTypeError"
|
||||
)
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -30,38 +29,35 @@ import (
|
||||
// Context represents context of a request.
|
||||
type Context struct {
|
||||
*macaron.Context
|
||||
i18n.Locale
|
||||
Cache cache.Cache
|
||||
csrf csrf.CSRF
|
||||
Flash *session.Flash
|
||||
Session session.Store
|
||||
|
||||
User *models.User
|
||||
IsSigned bool
|
||||
User *models.User
|
||||
IsSigned bool
|
||||
IsBasicAuth bool
|
||||
|
||||
Repo struct {
|
||||
IsOwner bool
|
||||
IsTrueOwner bool
|
||||
IsWatching bool
|
||||
IsBranch bool
|
||||
IsTag bool
|
||||
IsCommit bool
|
||||
IsAdmin bool // Current user is admin level.
|
||||
HasAccess bool
|
||||
Repository *models.Repository
|
||||
Owner *models.User
|
||||
Commit *git.Commit
|
||||
Tag *git.Tag
|
||||
GitRepo *git.Repository
|
||||
BranchName string
|
||||
TagName string
|
||||
CommitId string
|
||||
RepoLink string
|
||||
CloneLink struct {
|
||||
SSH string
|
||||
HTTPS string
|
||||
Git string
|
||||
}
|
||||
IsOwner bool
|
||||
IsTrueOwner bool
|
||||
IsWatching bool
|
||||
IsBranch bool
|
||||
IsTag bool
|
||||
IsCommit bool
|
||||
IsAdmin bool // Current user is admin level.
|
||||
HasAccess 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 int
|
||||
Mirror *models.Mirror
|
||||
}
|
||||
@@ -77,12 +73,6 @@ type Context struct {
|
||||
}
|
||||
}
|
||||
|
||||
// Query querys form parameter.
|
||||
func (ctx *Context) Query(name string) string {
|
||||
ctx.Req.ParseForm()
|
||||
return ctx.Req.Form.Get(name)
|
||||
}
|
||||
|
||||
// HasError returns true if error occurs in form validation.
|
||||
func (ctx *Context) HasApiError() bool {
|
||||
hasErr, ok := ctx.Data["HasError"]
|
||||
@@ -140,23 +130,6 @@ func (ctx *Context) Handle(status int, title string, err error) {
|
||||
ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status)))
|
||||
}
|
||||
|
||||
func (ctx *Context) ServeFile(file string, names ...string) {
|
||||
var name string
|
||||
if len(names) > 0 {
|
||||
name = names[0]
|
||||
} else {
|
||||
name = path.Base(file)
|
||||
}
|
||||
ctx.Resp.Header().Set("Content-Description", "File Transfer")
|
||||
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
|
||||
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name)
|
||||
ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
|
||||
ctx.Resp.Header().Set("Expires", "0")
|
||||
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
|
||||
ctx.Resp.Header().Set("Pragma", "public")
|
||||
http.ServeFile(ctx.Resp, ctx.Req, file)
|
||||
}
|
||||
|
||||
func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
|
||||
modtime := time.Now()
|
||||
for _, p := range params {
|
||||
@@ -172,7 +145,7 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa
|
||||
ctx.Resp.Header().Set("Expires", "0")
|
||||
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
|
||||
ctx.Resp.Header().Set("Pragma", "public")
|
||||
http.ServeContent(ctx.Resp, ctx.Req, name, modtime, r)
|
||||
http.ServeContent(ctx.Resp, ctx.Req.Request, name, modtime, r)
|
||||
}
|
||||
|
||||
// Contexter initializes a classic context for a request.
|
||||
@@ -180,14 +153,13 @@ func Contexter() macaron.Handler {
|
||||
return func(c *macaron.Context, l i18n.Locale, cache cache.Cache, sess session.Store, f *session.Flash, x csrf.CSRF) {
|
||||
ctx := &Context{
|
||||
Context: c,
|
||||
Locale: l,
|
||||
Cache: cache,
|
||||
csrf: x,
|
||||
Flash: f,
|
||||
Session: sess,
|
||||
}
|
||||
// Compute current URL for real-time change language.
|
||||
link := ctx.Req.RequestURI
|
||||
link := setting.AppSubUrl + ctx.Req.RequestURI
|
||||
i := strings.Index(link, "?")
|
||||
if i > -1 {
|
||||
link = link[:i]
|
||||
@@ -197,12 +169,16 @@ func Contexter() macaron.Handler {
|
||||
ctx.Data["PageStartTime"] = time.Now()
|
||||
|
||||
// Get user from session if logined.
|
||||
ctx.User = auth.SignedInUser(ctx.Req.Header, ctx.Session)
|
||||
ctx.User, ctx.IsBasicAuth = auth.SignedInUser(ctx.Req.Request, ctx.Session)
|
||||
|
||||
if ctx.User != nil {
|
||||
ctx.IsSigned = true
|
||||
ctx.Data["IsSigned"] = ctx.IsSigned
|
||||
ctx.Data["SignedUser"] = ctx.User
|
||||
ctx.Data["SignedUserName"] = ctx.User.Name
|
||||
ctx.Data["IsAdmin"] = ctx.User.IsAdmin
|
||||
} else {
|
||||
ctx.Data["SignedUserName"] = ""
|
||||
}
|
||||
|
||||
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
@@ -37,7 +38,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
ctx.Handle(404, "GetUserByName", err)
|
||||
} else if redirect {
|
||||
log.Error(4, "GetUserByName", err)
|
||||
ctx.Redirect("/")
|
||||
ctx.Redirect(setting.AppSubUrl + "/")
|
||||
} else {
|
||||
ctx.Handle(500, "GetUserByName", err)
|
||||
}
|
||||
@@ -47,7 +48,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
ctx.Data["Org"] = org
|
||||
|
||||
if ctx.IsSigned {
|
||||
ctx.Org.IsOwner = org.IsOrgOwner(ctx.User.Id)
|
||||
ctx.Org.IsOwner = org.IsOwnedBy(ctx.User.Id)
|
||||
if ctx.Org.IsOwner {
|
||||
ctx.Org.IsMember = true
|
||||
ctx.Org.IsAdminTeam = true
|
||||
@@ -67,7 +68,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
}
|
||||
ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
|
||||
|
||||
ctx.Org.OrgLink = "/org/" + org.Name
|
||||
ctx.Org.OrgLink = setting.AppSubUrl + "/org/" + org.Name
|
||||
ctx.Data["OrgLink"] = ctx.Org.OrgLink
|
||||
|
||||
// Team.
|
||||
@@ -79,7 +80,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
ctx.Handle(404, "GetTeam", err)
|
||||
} else if redirect {
|
||||
log.Error(4, "GetTeam", err)
|
||||
ctx.Redirect("/")
|
||||
ctx.Redirect(setting.AppSubUrl + "/")
|
||||
} else {
|
||||
ctx.Handle(500, "GetTeam", err)
|
||||
}
|
||||
|
||||
@@ -13,22 +13,221 @@ import (
|
||||
"github.com/Unknwon/macaron"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/git"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
func ApiRepoAssignment() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
userName := ctx.Params(":username")
|
||||
repoName := ctx.Params(":reponame")
|
||||
|
||||
var (
|
||||
u *models.User
|
||||
err error
|
||||
)
|
||||
|
||||
// Collaborators who have write access can be seen as owners.
|
||||
if ctx.IsSigned {
|
||||
ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE)
|
||||
if err != nil {
|
||||
ctx.JSON(500, &base.ApiJsonErr{"HasAccess: " + err.Error(), base.DOC_URL})
|
||||
return
|
||||
}
|
||||
ctx.Repo.IsTrueOwner = ctx.User.LowerName == strings.ToLower(userName)
|
||||
}
|
||||
|
||||
if !ctx.Repo.IsTrueOwner {
|
||||
u, err = models.GetUserByName(userName)
|
||||
if err != nil {
|
||||
if err == models.ErrUserNotExist {
|
||||
ctx.Error(404)
|
||||
} else {
|
||||
ctx.JSON(500, &base.ApiJsonErr{"GetUserByName: " + err.Error(), base.DOC_URL})
|
||||
}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
u = ctx.User
|
||||
}
|
||||
ctx.Repo.Owner = u
|
||||
|
||||
// Organization owner team members are true owners as well.
|
||||
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
|
||||
ctx.Repo.IsTrueOwner = true
|
||||
}
|
||||
|
||||
// Get repository.
|
||||
repo, err := models.GetRepositoryByName(u.Id, repoName)
|
||||
if err != nil {
|
||||
if err == models.ErrRepoNotExist {
|
||||
ctx.Error(404)
|
||||
return
|
||||
}
|
||||
ctx.JSON(500, &base.ApiJsonErr{"GetRepositoryByName: " + err.Error(), base.DOC_URL})
|
||||
return
|
||||
} else if err = repo.GetOwner(); err != nil {
|
||||
ctx.JSON(500, &base.ApiJsonErr{"GetOwner: " + err.Error(), base.DOC_URL})
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the mirror repository owner(mirror repository doesn't have access).
|
||||
if ctx.IsSigned && !ctx.Repo.IsOwner {
|
||||
if repo.OwnerId == ctx.User.Id {
|
||||
ctx.Repo.IsOwner = true
|
||||
}
|
||||
// Check if current user has admin permission to repository.
|
||||
if u.IsOrganization() {
|
||||
auth, err := models.GetHighestAuthorize(u.Id, ctx.User.Id, repo.Id, 0)
|
||||
if err != nil {
|
||||
ctx.JSON(500, &base.ApiJsonErr{"GetHighestAuthorize: " + err.Error(), base.DOC_URL})
|
||||
return
|
||||
}
|
||||
if auth == models.ORG_ADMIN {
|
||||
ctx.Repo.IsOwner = true
|
||||
ctx.Repo.IsAdmin = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check access.
|
||||
if repo.IsPrivate && !ctx.Repo.IsOwner {
|
||||
if ctx.User == nil {
|
||||
ctx.Error(404)
|
||||
return
|
||||
}
|
||||
|
||||
hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.READABLE)
|
||||
if err != nil {
|
||||
ctx.JSON(500, &base.ApiJsonErr{"HasAccess: " + err.Error(), base.DOC_URL})
|
||||
return
|
||||
} else if !hasAccess {
|
||||
ctx.Error(404)
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.Repo.HasAccess = true
|
||||
|
||||
ctx.Repo.Repository = repo
|
||||
}
|
||||
}
|
||||
|
||||
// RepoRef handles repository reference name including those contain `/`.
|
||||
func RepoRef() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
var (
|
||||
refName string
|
||||
err error
|
||||
)
|
||||
|
||||
// For API calls.
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
||||
gitRepo, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "RepoRef Invalid repo "+repoPath, err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.GitRepo = gitRepo
|
||||
}
|
||||
|
||||
// Get default branch.
|
||||
if len(ctx.Params("*")) == 0 {
|
||||
refName = ctx.Repo.Repository.DefaultBranch
|
||||
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||
brs, err := ctx.Repo.GitRepo.GetBranches()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetBranches", err)
|
||||
return
|
||||
}
|
||||
refName = brs[0]
|
||||
}
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommitOfBranch(refName)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetCommitOfBranch", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitId = ctx.Repo.Commit.Id.String()
|
||||
ctx.Repo.IsBranch = true
|
||||
|
||||
} else {
|
||||
hasMatched := false
|
||||
parts := strings.Split(ctx.Params("*"), "/")
|
||||
for i, part := range parts {
|
||||
refName = strings.TrimPrefix(refName+"/"+part, "/")
|
||||
|
||||
if ctx.Repo.GitRepo.IsBranchExist(refName) ||
|
||||
ctx.Repo.GitRepo.IsTagExist(refName) {
|
||||
if i < len(parts)-1 {
|
||||
ctx.Repo.TreeName = strings.Join(parts[i+1:], "/")
|
||||
}
|
||||
hasMatched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasMatched && len(parts[0]) == 40 {
|
||||
refName = parts[0]
|
||||
ctx.Repo.TreeName = strings.Join(parts[1:], "/")
|
||||
}
|
||||
|
||||
if ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||
ctx.Repo.IsBranch = true
|
||||
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommitOfBranch(refName)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetCommitOfBranch", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitId = ctx.Repo.Commit.Id.String()
|
||||
|
||||
} else if ctx.Repo.GitRepo.IsTagExist(refName) {
|
||||
ctx.Repo.IsTag = true
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommitOfTag(refName)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetCommitOfTag", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitId = ctx.Repo.Commit.Id.String()
|
||||
} else if len(refName) == 40 {
|
||||
ctx.Repo.IsCommit = true
|
||||
ctx.Repo.CommitId = refName
|
||||
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
|
||||
if err != nil {
|
||||
ctx.Handle(404, "GetCommit", nil)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ctx.Handle(404, "RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Repo.BranchName = refName
|
||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||
ctx.Data["CommitId"] = ctx.Repo.CommitId
|
||||
ctx.Data["IsBranch"] = ctx.Repo.IsBranch
|
||||
ctx.Data["IsTag"] = ctx.Repo.IsTag
|
||||
ctx.Data["IsCommit"] = ctx.Repo.IsCommit
|
||||
|
||||
ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "CommitsCount", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
|
||||
}
|
||||
}
|
||||
|
||||
func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
var (
|
||||
validBranch bool // To valid brach name.
|
||||
displayBare bool // To display bare page if it is a bare repo.
|
||||
)
|
||||
if len(args) >= 1 {
|
||||
validBranch = args[0]
|
||||
}
|
||||
if len(args) >= 2 {
|
||||
displayBare = args[1]
|
||||
displayBare = args[0]
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -60,7 +259,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
ctx.Handle(404, "GetUserByName", err)
|
||||
} else if redirect {
|
||||
log.Error(4, "GetUserByName", err)
|
||||
ctx.Redirect("/")
|
||||
ctx.Redirect(setting.AppSubUrl + "/")
|
||||
} else {
|
||||
ctx.Handle(500, "GetUserByName", err)
|
||||
}
|
||||
@@ -72,7 +271,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
|
||||
if u == nil {
|
||||
if redirect {
|
||||
ctx.Redirect("/")
|
||||
ctx.Redirect(setting.AppSubUrl + "/")
|
||||
return
|
||||
}
|
||||
ctx.Handle(404, "RepoAssignment", errors.New("invliad user account for single repository"))
|
||||
@@ -81,7 +280,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
ctx.Repo.Owner = u
|
||||
|
||||
// Organization owner team members are true owners as well.
|
||||
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgOwner(ctx.User.Id) {
|
||||
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
|
||||
ctx.Repo.IsTrueOwner = true
|
||||
}
|
||||
|
||||
@@ -92,7 +291,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
ctx.Handle(404, "GetRepositoryByName", err)
|
||||
return
|
||||
} else if redirect {
|
||||
ctx.Redirect("/")
|
||||
ctx.Redirect(setting.AppSubUrl + "/")
|
||||
return
|
||||
}
|
||||
ctx.Handle(500, "GetRepositoryByName", err)
|
||||
@@ -160,7 +359,11 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
return
|
||||
}
|
||||
ctx.Repo.GitRepo = gitRepo
|
||||
ctx.Repo.RepoLink = "/" + u.Name + "/" + repo.Name
|
||||
ctx.Repo.RepoLink, err = repo.RepoLink()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "RepoLink", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["RepoLink"] = ctx.Repo.RepoLink
|
||||
|
||||
tags, err := ctx.Repo.GitRepo.GetTags()
|
||||
@@ -168,20 +371,26 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
ctx.Handle(500, "GetTags", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["Tags"] = tags
|
||||
ctx.Repo.Repository.NumTags = len(tags)
|
||||
|
||||
// Non-fork repository will not return error in this method.
|
||||
if err = repo.GetForkRepo(); err != nil {
|
||||
ctx.Handle(500, "GetForkRepo", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Title"] = u.Name + "/" + repo.Name
|
||||
ctx.Data["Repository"] = repo
|
||||
ctx.Data["Owner"] = ctx.Repo.Repository.Owner
|
||||
ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner
|
||||
ctx.Data["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner
|
||||
|
||||
if setting.SshPort != 22 {
|
||||
ctx.Repo.CloneLink.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.Domain, setting.SshPort, u.LowerName, repo.LowerName)
|
||||
} else {
|
||||
ctx.Repo.CloneLink.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.Domain, u.LowerName, repo.LowerName)
|
||||
ctx.Repo.CloneLink, err = repo.CloneLink()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "CloneLink", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, u.LowerName, repo.LowerName)
|
||||
ctx.Data["CloneLink"] = ctx.Repo.CloneLink
|
||||
|
||||
if ctx.Repo.Repository.IsGoget {
|
||||
@@ -189,77 +398,11 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
ctx.Data["GoGetImport"] = fmt.Sprintf("%s/%s/%s", setting.Domain, u.LowerName, repo.LowerName)
|
||||
}
|
||||
|
||||
// when repo is bare, not valid branch
|
||||
if !ctx.Repo.Repository.IsBare && validBranch {
|
||||
detect:
|
||||
if len(refName) > 0 {
|
||||
if gitRepo.IsBranchExist(refName) {
|
||||
ctx.Repo.IsBranch = true
|
||||
ctx.Repo.BranchName = refName
|
||||
|
||||
ctx.Repo.Commit, err = gitRepo.GetCommitOfBranch(refName)
|
||||
if err != nil {
|
||||
ctx.Handle(404, "RepoAssignment invalid branch", nil)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitId = ctx.Repo.Commit.Id.String()
|
||||
|
||||
} else if gitRepo.IsTagExist(refName) {
|
||||
ctx.Repo.IsTag = true
|
||||
ctx.Repo.BranchName = refName
|
||||
|
||||
ctx.Repo.Tag, err = gitRepo.GetTag(refName)
|
||||
if err != nil {
|
||||
ctx.Handle(404, "RepoAssignment invalid tag", nil)
|
||||
return
|
||||
}
|
||||
ctx.Repo.Commit, _ = ctx.Repo.Tag.Commit()
|
||||
ctx.Repo.CommitId = ctx.Repo.Commit.Id.String()
|
||||
} else if len(refName) == 40 {
|
||||
ctx.Repo.IsCommit = true
|
||||
ctx.Repo.CommitId = refName
|
||||
ctx.Repo.BranchName = refName
|
||||
|
||||
ctx.Repo.Commit, err = gitRepo.GetCommit(refName)
|
||||
if err != nil {
|
||||
ctx.Handle(404, "RepoAssignment invalid commit", nil)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ctx.Handle(404, "RepoAssignment invalid repo", errors.New("branch or tag not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
if len(refName) == 0 {
|
||||
if gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
|
||||
refName = ctx.Repo.Repository.DefaultBranch
|
||||
} else {
|
||||
brs, err := gitRepo.GetBranches()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetBranches", err)
|
||||
return
|
||||
}
|
||||
refName = brs[0]
|
||||
}
|
||||
}
|
||||
goto detect
|
||||
}
|
||||
|
||||
ctx.Data["IsBranch"] = ctx.Repo.IsBranch
|
||||
ctx.Data["IsCommit"] = ctx.Repo.IsCommit
|
||||
|
||||
ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "CommitsCount", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
|
||||
}
|
||||
|
||||
// repo is bare and display enable
|
||||
if ctx.Repo.Repository.IsBare {
|
||||
log.Debug("Bare repository: %s", ctx.Repo.RepoLink)
|
||||
// NOTE: to prevent templating error
|
||||
ctx.Data["BranchName"] = ""
|
||||
if displayBare {
|
||||
ctx.HTML(200, "repo/bare")
|
||||
}
|
||||
@@ -274,7 +417,8 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
ctx.Data["TagName"] = ctx.Repo.TagName
|
||||
brs, err := ctx.Repo.GitRepo.GetBranches()
|
||||
if err != nil {
|
||||
log.Error(4, "GetBranches: %v", err)
|
||||
ctx.Handle(500, "GetBranches", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["Branches"] = brs
|
||||
ctx.Data["BrancheCount"] = len(brs)
|
||||
@@ -298,8 +442,8 @@ func RequireTrueOwner() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.Repo.IsTrueOwner && !ctx.Repo.IsAdmin {
|
||||
if !ctx.IsSigned {
|
||||
ctx.SetCookie("redirect_to", "/"+url.QueryEscape(ctx.Req.RequestURI))
|
||||
ctx.Redirect("/user/login")
|
||||
ctx.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
|
||||
ctx.Redirect(setting.AppSubUrl + "/user/login")
|
||||
return
|
||||
}
|
||||
ctx.Handle(404, ctx.Req.RequestURI, nil)
|
||||
@@ -307,3 +451,13 @@ func RequireTrueOwner() macaron.Handler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GitHookService checks if repository Git hooks service has been enabled.
|
||||
func GitHookService() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.User.AllowGitHook && !ctx.User.IsAdmin {
|
||||
ctx.Handle(404, "GitHookService", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package setting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -16,10 +17,11 @@ import (
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/Unknwon/goconfig"
|
||||
"github.com/macaron-contrib/oauth2"
|
||||
"github.com/macaron-contrib/session"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
// "github.com/gogits/gogs-ng/modules/ssh"
|
||||
// "github.com/gogits/gogs/modules/ssh"
|
||||
)
|
||||
|
||||
type Scheme string
|
||||
@@ -27,13 +29,22 @@ type Scheme string
|
||||
const (
|
||||
HTTP Scheme = "http"
|
||||
HTTPS Scheme = "https"
|
||||
FCGI Scheme = "fcgi"
|
||||
)
|
||||
|
||||
type LandingPage string
|
||||
|
||||
const (
|
||||
LANDING_PAGE_HOME LandingPage = "/"
|
||||
LANDING_PAGE_EXPLORE LandingPage = "/explore"
|
||||
)
|
||||
|
||||
var (
|
||||
// App settings.
|
||||
AppVer string
|
||||
AppName string
|
||||
AppUrl string
|
||||
AppVer string
|
||||
AppName string
|
||||
AppUrl string
|
||||
AppSubUrl string
|
||||
|
||||
// Server settings.
|
||||
Protocol Scheme
|
||||
@@ -45,6 +56,7 @@ var (
|
||||
CertFile, KeyFile string
|
||||
StaticRootPath string
|
||||
EnableGzip bool
|
||||
LandingPageUrl LandingPage
|
||||
|
||||
// Security settings.
|
||||
InstallLock bool
|
||||
@@ -63,8 +75,10 @@ var (
|
||||
ScriptType string
|
||||
|
||||
// Picture settings.
|
||||
PictureService string
|
||||
DisableGravatar bool
|
||||
PictureService string
|
||||
AvatarUploadPath string
|
||||
GravatarSource string
|
||||
DisableGravatar bool
|
||||
|
||||
// Log settings.
|
||||
LogRootPath string
|
||||
@@ -95,6 +109,8 @@ var (
|
||||
|
||||
// Git settings.
|
||||
MaxGitDiffLines int
|
||||
GitFsckArgs []string
|
||||
GitGcArgs []string
|
||||
|
||||
// I18n settings.
|
||||
Langs, Names []string
|
||||
@@ -106,6 +122,7 @@ var (
|
||||
ProdMode bool
|
||||
RunUser string
|
||||
IsWindows bool
|
||||
HasRobotsTxt bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -165,12 +182,22 @@ func NewConfigContext() {
|
||||
AppUrl += "/"
|
||||
}
|
||||
|
||||
// Check if has app suburl.
|
||||
url, err := url.Parse(AppUrl)
|
||||
if err != nil {
|
||||
log.Fatal(4, "Invalid ROOT_URL(%s): %s", AppUrl, err)
|
||||
}
|
||||
AppSubUrl = strings.TrimSuffix(url.Path, "/")
|
||||
|
||||
Protocol = HTTP
|
||||
if Cfg.MustValue("server", "PROTOCOL") == "https" {
|
||||
Protocol = HTTPS
|
||||
CertFile = Cfg.MustValue("server", "CERT_FILE")
|
||||
KeyFile = Cfg.MustValue("server", "KEY_FILE")
|
||||
}
|
||||
if Cfg.MustValue("server", "PROTOCOL") == "fcgi" {
|
||||
Protocol = FCGI
|
||||
}
|
||||
Domain = Cfg.MustValue("server", "DOMAIN", "localhost")
|
||||
HttpAddr = Cfg.MustValue("server", "HTTP_ADDR", "0.0.0.0")
|
||||
HttpPort = Cfg.MustValue("server", "HTTP_PORT", "3000")
|
||||
@@ -181,6 +208,13 @@ func NewConfigContext() {
|
||||
LogRootPath = Cfg.MustValue("log", "ROOT_PATH", path.Join(workDir, "log"))
|
||||
EnableGzip = Cfg.MustBool("server", "ENABLE_GZIP")
|
||||
|
||||
switch Cfg.MustValue("server", "LANDING_PAGE", "home") {
|
||||
case "explore":
|
||||
LandingPageUrl = LANDING_PAGE_EXPLORE
|
||||
default:
|
||||
LandingPageUrl = LANDING_PAGE_HOME
|
||||
}
|
||||
|
||||
InstallLock = Cfg.MustBool("security", "INSTALL_LOCK")
|
||||
SecretKey = Cfg.MustValue("security", "SECRET_KEY")
|
||||
LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS")
|
||||
@@ -226,7 +260,7 @@ func NewConfigContext() {
|
||||
log.Fatal(4, "Expect user(%s) but current user is: %s", RunUser, curUser)
|
||||
}
|
||||
|
||||
// Determine and create root git reposiroty path.
|
||||
// Determine and create root git repository path.
|
||||
homeDir, err := com.HomeDir()
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to get home directory: %v", err)
|
||||
@@ -243,26 +277,38 @@ func NewConfigContext() {
|
||||
}
|
||||
ScriptType = Cfg.MustValue("repository", "SCRIPT_TYPE", "bash")
|
||||
|
||||
PictureService = Cfg.MustValueRange("picture", "SERVICE", "server",
|
||||
[]string{"server"})
|
||||
PictureService = Cfg.MustValueRange("picture", "SERVICE", "server", []string{"server"})
|
||||
AvatarUploadPath = Cfg.MustValue("picture", "AVATAR_UPLOAD_PATH", "data/avatars")
|
||||
os.MkdirAll(AvatarUploadPath, os.ModePerm)
|
||||
|
||||
switch Cfg.MustValue("picture", "GRAVATAR_SOURCE", "gravatar") {
|
||||
case "duoshuo":
|
||||
GravatarSource = "http://gravatar.duoshuo.com/avatar/"
|
||||
default:
|
||||
GravatarSource = "//1.gravatar.com/avatar/"
|
||||
}
|
||||
DisableGravatar = Cfg.MustBool("picture", "DISABLE_GRAVATAR")
|
||||
|
||||
MaxGitDiffLines = Cfg.MustInt("git", "MAX_GITDIFF_LINES", 10000)
|
||||
GitFsckArgs = Cfg.MustValueArray("git", "FSCK_ARGS", " ")
|
||||
GitGcArgs = Cfg.MustValueArray("git", "GC_ARGS", " ")
|
||||
|
||||
Langs = Cfg.MustValueArray("i18n", "LANGS", ",")
|
||||
Names = Cfg.MustValueArray("i18n", "NAMES", ",")
|
||||
|
||||
HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
|
||||
}
|
||||
|
||||
var Service struct {
|
||||
RegisterEmailConfirm bool
|
||||
DisableRegistration bool
|
||||
RequireSignInView bool
|
||||
EnableCacheAvatar bool
|
||||
EnableNotifyMail bool
|
||||
EnableReverseProxyAuth bool
|
||||
LdapAuth bool
|
||||
ActiveCodeLives int
|
||||
ResetPwdCodeLives int
|
||||
RegisterEmailConfirm bool
|
||||
DisableRegistration bool
|
||||
RequireSignInView bool
|
||||
EnableCacheAvatar bool
|
||||
EnableNotifyMail bool
|
||||
EnableReverseProxyAuth bool
|
||||
EnableReverseProxyAutoRegister bool
|
||||
ActiveCodeLives int
|
||||
ResetPwdCodeLives int
|
||||
}
|
||||
|
||||
func newService() {
|
||||
@@ -272,6 +318,7 @@ func newService() {
|
||||
Service.RequireSignInView = Cfg.MustBool("service", "REQUIRE_SIGNIN_VIEW")
|
||||
Service.EnableCacheAvatar = Cfg.MustBool("service", "ENABLE_CACHE_AVATAR")
|
||||
Service.EnableReverseProxyAuth = Cfg.MustBool("service", "ENABLE_REVERSE_PROXY_AUTHENTICATION")
|
||||
Service.EnableReverseProxyAutoRegister = Cfg.MustBool("service", "ENABLE_REVERSE_PROXY_AUTO_REGISTERATION")
|
||||
}
|
||||
|
||||
var logLevels = map[string]string{
|
||||
@@ -371,13 +418,11 @@ func newSessionService() {
|
||||
SessionConfig = new(session.Config)
|
||||
SessionConfig.ProviderConfig = strings.Trim(Cfg.MustValue("session", "PROVIDER_CONFIG"), "\" ")
|
||||
SessionConfig.CookieName = Cfg.MustValue("session", "COOKIE_NAME", "i_like_gogits")
|
||||
SessionConfig.CookiePath = AppSubUrl
|
||||
SessionConfig.Secure = Cfg.MustBool("session", "COOKIE_SECURE")
|
||||
SessionConfig.EnableSetCookie = Cfg.MustBool("session", "ENABLE_SET_COOKIE", true)
|
||||
SessionConfig.Gclifetime = Cfg.MustInt64("session", "GC_INTERVAL_TIME", 86400)
|
||||
SessionConfig.Maxlifetime = Cfg.MustInt64("session", "SESSION_LIFE_TIME", 86400)
|
||||
SessionConfig.SessionIDHashFunc = Cfg.MustValueRange("session", "SESSION_ID_HASHFUNC",
|
||||
"sha1", []string{"sha1", "sha256", "md5"})
|
||||
SessionConfig.SessionIDHashKey = Cfg.MustValue("session", "SESSION_ID_HASHKEY", string(com.RandomCreateBytes(16)))
|
||||
|
||||
if SessionProvider == "file" {
|
||||
os.MkdirAll(path.Dir(SessionConfig.ProviderConfig), os.ModePerm)
|
||||
@@ -395,9 +440,8 @@ type Mailer struct {
|
||||
}
|
||||
|
||||
type OauthInfo struct {
|
||||
ClientId, ClientSecret string
|
||||
Scopes string
|
||||
AuthUrl, TokenUrl string
|
||||
oauth2.Options
|
||||
AuthUrl, TokenUrl string
|
||||
}
|
||||
|
||||
// Oauther represents oauth service.
|
||||
@@ -464,5 +508,5 @@ func NewServices() {
|
||||
newRegisterMailService()
|
||||
newNotifyMailService()
|
||||
newWebhookService()
|
||||
// ssh.Listen("2022")
|
||||
// ssh.Listen("2222")
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ package social
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
oauth "github.com/gogits/oauth2"
|
||||
"github.com/macaron-contrib/oauth2"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
@@ -27,16 +27,11 @@ type BasicUserInfo struct {
|
||||
|
||||
type SocialConnector interface {
|
||||
Type() int
|
||||
SetRedirectUrl(string)
|
||||
UserInfo(*oauth.Token, *url.URL) (*BasicUserInfo, error)
|
||||
|
||||
AuthCodeURL(string) string
|
||||
Exchange(string) (*oauth.Token, error)
|
||||
UserInfo(*oauth2.Token, *url.URL) (*BasicUserInfo, error)
|
||||
}
|
||||
|
||||
var (
|
||||
SocialBaseUrl = "/user/login"
|
||||
SocialMap = make(map[string]SocialConnector)
|
||||
SocialMap = make(map[string]SocialConnector)
|
||||
)
|
||||
|
||||
func NewOauthService() {
|
||||
@@ -44,27 +39,34 @@ func NewOauthService() {
|
||||
return
|
||||
}
|
||||
|
||||
oauth2.AppSubUrl = setting.AppSubUrl
|
||||
|
||||
setting.OauthService = &setting.Oauther{}
|
||||
setting.OauthService.OauthInfos = make(map[string]*setting.OauthInfo)
|
||||
|
||||
socialConfigs := make(map[string]*oauth.Config)
|
||||
socialConfigs := make(map[string]*oauth2.Options)
|
||||
allOauthes := []string{"github", "google", "qq", "twitter", "weibo"}
|
||||
// Load all OAuth config data.
|
||||
for _, name := range allOauthes {
|
||||
setting.OauthService.OauthInfos[name] = &setting.OauthInfo{
|
||||
ClientId: setting.Cfg.MustValue("oauth."+name, "CLIENT_ID"),
|
||||
ClientSecret: setting.Cfg.MustValue("oauth."+name, "CLIENT_SECRET"),
|
||||
Scopes: setting.Cfg.MustValue("oauth."+name, "SCOPES"),
|
||||
AuthUrl: setting.Cfg.MustValue("oauth."+name, "AUTH_URL"),
|
||||
TokenUrl: setting.Cfg.MustValue("oauth."+name, "TOKEN_URL"),
|
||||
if !setting.Cfg.MustBool("oauth."+name, "ENABLED") {
|
||||
continue
|
||||
}
|
||||
socialConfigs[name] = &oauth.Config{
|
||||
ClientId: setting.OauthService.OauthInfos[name].ClientId,
|
||||
setting.OauthService.OauthInfos[name] = &setting.OauthInfo{
|
||||
Options: oauth2.Options{
|
||||
ClientID: setting.Cfg.MustValue("oauth."+name, "CLIENT_ID"),
|
||||
ClientSecret: setting.Cfg.MustValue("oauth."+name, "CLIENT_SECRET"),
|
||||
Scopes: setting.Cfg.MustValueArray("oauth."+name, "SCOPES", " "),
|
||||
PathLogin: "/user/login/oauth2/" + name,
|
||||
PathCallback: setting.AppSubUrl + "/user/login/" + name,
|
||||
RedirectURL: setting.AppUrl + "user/login/" + name,
|
||||
},
|
||||
AuthUrl: setting.Cfg.MustValue("oauth."+name, "AUTH_URL"),
|
||||
TokenUrl: setting.Cfg.MustValue("oauth."+name, "TOKEN_URL"),
|
||||
}
|
||||
socialConfigs[name] = &oauth2.Options{
|
||||
ClientID: setting.OauthService.OauthInfos[name].ClientID,
|
||||
ClientSecret: setting.OauthService.OauthInfos[name].ClientSecret,
|
||||
RedirectURL: strings.TrimSuffix(setting.AppUrl, "/") + SocialBaseUrl + name,
|
||||
Scope: setting.OauthService.OauthInfos[name].Scopes,
|
||||
AuthURL: setting.OauthService.OauthInfos[name].AuthUrl,
|
||||
TokenURL: setting.OauthService.OauthInfos[name].TokenUrl,
|
||||
Scopes: setting.OauthService.OauthInfos[name].Scopes,
|
||||
}
|
||||
}
|
||||
enabledOauths := make([]string, 0, 10)
|
||||
@@ -91,11 +93,11 @@ func NewOauthService() {
|
||||
}
|
||||
|
||||
// Twitter.
|
||||
if setting.Cfg.MustBool("oauth.twitter", "ENABLED") {
|
||||
setting.OauthService.Twitter = true
|
||||
newTwitterOauth(socialConfigs["twitter"])
|
||||
enabledOauths = append(enabledOauths, "Twitter")
|
||||
}
|
||||
// if setting.Cfg.MustBool("oauth.twitter", "ENABLED") {
|
||||
// setting.OauthService.Twitter = true
|
||||
// newTwitterOauth(socialConfigs["twitter"])
|
||||
// enabledOauths = append(enabledOauths, "Twitter")
|
||||
// }
|
||||
|
||||
// Weibo.
|
||||
if setting.Cfg.MustBool("oauth.weibo", "ENABLED") {
|
||||
@@ -115,38 +117,25 @@ func NewOauthService() {
|
||||
// \/ \/ \/
|
||||
|
||||
type SocialGithub struct {
|
||||
Token *oauth.Token
|
||||
*oauth.Transport
|
||||
opts *oauth2.Options
|
||||
}
|
||||
|
||||
func newGitHubOauth(opts *oauth2.Options) {
|
||||
SocialMap["github"] = &SocialGithub{opts}
|
||||
}
|
||||
|
||||
func (s *SocialGithub) Type() int {
|
||||
return int(models.GITHUB)
|
||||
}
|
||||
|
||||
func newGitHubOauth(config *oauth.Config) {
|
||||
SocialMap["github"] = &SocialGithub{
|
||||
Transport: &oauth.Transport{
|
||||
Config: config,
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SocialGithub) SetRedirectUrl(url string) {
|
||||
s.Transport.Config.RedirectURL = url
|
||||
}
|
||||
|
||||
func (s *SocialGithub) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
transport := &oauth.Transport{
|
||||
Token: token,
|
||||
}
|
||||
func (s *SocialGithub) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
transport := s.opts.NewTransportFromToken(token)
|
||||
var data struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"login"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
var err error
|
||||
r, err := transport.Client().Get(s.Transport.Scope)
|
||||
r, err := transport.Client().Get("https://api.github.com/user")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -169,38 +158,25 @@ func (s *SocialGithub) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo,
|
||||
// \/ /_____/ \/
|
||||
|
||||
type SocialGoogle struct {
|
||||
Token *oauth.Token
|
||||
*oauth.Transport
|
||||
opts *oauth2.Options
|
||||
}
|
||||
|
||||
func (s *SocialGoogle) Type() int {
|
||||
return int(models.GOOGLE)
|
||||
}
|
||||
|
||||
func newGoogleOauth(config *oauth.Config) {
|
||||
SocialMap["google"] = &SocialGoogle{
|
||||
Transport: &oauth.Transport{
|
||||
Config: config,
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
func newGoogleOauth(opts *oauth2.Options) {
|
||||
SocialMap["google"] = &SocialGoogle{opts}
|
||||
}
|
||||
|
||||
func (s *SocialGoogle) SetRedirectUrl(url string) {
|
||||
s.Transport.Config.RedirectURL = url
|
||||
}
|
||||
|
||||
func (s *SocialGoogle) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
transport := &oauth.Transport{Token: token}
|
||||
func (s *SocialGoogle) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
transport := s.opts.NewTransportFromToken(token)
|
||||
var data struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
var err error
|
||||
|
||||
reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
|
||||
r, err := transport.Client().Get(reqUrl)
|
||||
r, err := transport.Client().Get("https://www.googleapis.com/userinfo/v2/me")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -223,62 +199,35 @@ func (s *SocialGoogle) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo,
|
||||
// \__> \__>
|
||||
|
||||
type SocialTencent struct {
|
||||
Token *oauth.Token
|
||||
*oauth.Transport
|
||||
reqUrl string
|
||||
opts *oauth2.Options
|
||||
}
|
||||
|
||||
func newTencentOauth(opts *oauth2.Options) {
|
||||
SocialMap["qq"] = &SocialTencent{opts}
|
||||
}
|
||||
|
||||
func (s *SocialTencent) Type() int {
|
||||
return int(models.QQ)
|
||||
}
|
||||
|
||||
func newTencentOauth(config *oauth.Config) {
|
||||
SocialMap["qq"] = &SocialTencent{
|
||||
reqUrl: "https://open.t.qq.com/api/user/info",
|
||||
Transport: &oauth.Transport{
|
||||
Config: config,
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SocialTencent) SetRedirectUrl(url string) {
|
||||
s.Transport.Config.RedirectURL = url
|
||||
}
|
||||
|
||||
func (s *SocialTencent) UserInfo(token *oauth.Token, URL *url.URL) (*BasicUserInfo, error) {
|
||||
var data struct {
|
||||
Data struct {
|
||||
Id string `json:"openid"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
} `json:"data"`
|
||||
}
|
||||
var err error
|
||||
// https://open.t.qq.com/api/user/info?
|
||||
//oauth_consumer_key=APP_KEY&
|
||||
//access_token=ACCESSTOKEN&openid=openid
|
||||
//clientip=CLIENTIP&oauth_version=2.a
|
||||
//scope=all
|
||||
var urls = url.Values{
|
||||
"oauth_consumer_key": {s.Transport.Config.ClientId},
|
||||
"access_token": {token.AccessToken},
|
||||
"openid": URL.Query()["openid"],
|
||||
"oauth_version": {"2.a"},
|
||||
"scope": {"all"},
|
||||
}
|
||||
r, err := http.Get(s.reqUrl + "?" + urls.Encode())
|
||||
func (s *SocialTencent) UserInfo(token *oauth2.Token, URL *url.URL) (*BasicUserInfo, error) {
|
||||
r, err := http.Get("https://graph.z.qq.com/moc2/me?access_token=" + url.QueryEscape(token.AccessToken))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vals, err := url.ParseQuery(string(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &BasicUserInfo{
|
||||
Identity: data.Data.Id,
|
||||
Name: data.Data.Name,
|
||||
Email: data.Data.Email,
|
||||
Identity: vals.Get("openid"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -289,54 +238,54 @@ func (s *SocialTencent) UserInfo(token *oauth.Token, URL *url.URL) (*BasicUserIn
|
||||
// |____| \/\_/ |__||__| |__| \___ >__|
|
||||
// \/
|
||||
|
||||
type SocialTwitter struct {
|
||||
Token *oauth.Token
|
||||
*oauth.Transport
|
||||
}
|
||||
// type SocialTwitter struct {
|
||||
// Token *oauth2.Token
|
||||
// *oauth2.Transport
|
||||
// }
|
||||
|
||||
func (s *SocialTwitter) Type() int {
|
||||
return int(models.TWITTER)
|
||||
}
|
||||
// func (s *SocialTwitter) Type() int {
|
||||
// return int(models.TWITTER)
|
||||
// }
|
||||
|
||||
func newTwitterOauth(config *oauth.Config) {
|
||||
SocialMap["twitter"] = &SocialTwitter{
|
||||
Transport: &oauth.Transport{
|
||||
Config: config,
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
}
|
||||
// func newTwitterOauth(config *oauth2.Config) {
|
||||
// SocialMap["twitter"] = &SocialTwitter{
|
||||
// Transport: &oauth.Transport{
|
||||
// Config: config,
|
||||
// Transport: http.DefaultTransport,
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
||||
func (s *SocialTwitter) SetRedirectUrl(url string) {
|
||||
s.Transport.Config.RedirectURL = url
|
||||
}
|
||||
// func (s *SocialTwitter) SetRedirectUrl(url string) {
|
||||
// s.Transport.Config.RedirectURL = url
|
||||
// }
|
||||
|
||||
//https://github.com/mrjones/oauth
|
||||
func (s *SocialTwitter) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
// transport := &oauth.Transport{Token: token}
|
||||
// var data struct {
|
||||
// Id string `json:"id"`
|
||||
// Name string `json:"name"`
|
||||
// Email string `json:"email"`
|
||||
// }
|
||||
// var err error
|
||||
// //https://github.com/mrjones/oauth
|
||||
// func (s *SocialTwitter) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
// // transport := &oauth.Transport{Token: token}
|
||||
// // var data struct {
|
||||
// // Id string `json:"id"`
|
||||
// // Name string `json:"name"`
|
||||
// // Email string `json:"email"`
|
||||
// // }
|
||||
// // var err error
|
||||
|
||||
// reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
|
||||
// r, err := transport.Client().Get(reqUrl)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// defer r.Body.Close()
|
||||
// if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return &BasicUserInfo{
|
||||
// Identity: data.Id,
|
||||
// Name: data.Name,
|
||||
// Email: data.Email,
|
||||
// }, nil
|
||||
return nil, nil
|
||||
}
|
||||
// // reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
|
||||
// // r, err := transport.Client().Get(reqUrl)
|
||||
// // if err != nil {
|
||||
// // return nil, err
|
||||
// // }
|
||||
// // defer r.Body.Close()
|
||||
// // if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
// // return nil, err
|
||||
// // }
|
||||
// // return &BasicUserInfo{
|
||||
// // Identity: data.Id,
|
||||
// // Name: data.Name,
|
||||
// // Email: data.Email,
|
||||
// // }, nil
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
// __ __ ._____.
|
||||
// / \ / \ ____ |__\_ |__ ____
|
||||
@@ -346,37 +295,25 @@ func (s *SocialTwitter) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo
|
||||
// \/ \/ \/
|
||||
|
||||
type SocialWeibo struct {
|
||||
Token *oauth.Token
|
||||
*oauth.Transport
|
||||
opts *oauth2.Options
|
||||
}
|
||||
|
||||
func newWeiboOauth(opts *oauth2.Options) {
|
||||
SocialMap["weibo"] = &SocialWeibo{opts}
|
||||
}
|
||||
|
||||
func (s *SocialWeibo) Type() int {
|
||||
return int(models.WEIBO)
|
||||
}
|
||||
|
||||
func newWeiboOauth(config *oauth.Config) {
|
||||
SocialMap["weibo"] = &SocialWeibo{
|
||||
Transport: &oauth.Transport{
|
||||
Config: config,
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SocialWeibo) SetRedirectUrl(url string) {
|
||||
s.Transport.Config.RedirectURL = url
|
||||
}
|
||||
|
||||
func (s *SocialWeibo) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
transport := &oauth.Transport{Token: token}
|
||||
func (s *SocialWeibo) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
transport := s.opts.NewTransportFromToken(token)
|
||||
var data struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
var err error
|
||||
|
||||
var urls = url.Values{
|
||||
"access_token": {token.AccessToken},
|
||||
"uid": {token.Extra["id_token"]},
|
||||
"uid": {token.Extra("uid")},
|
||||
}
|
||||
reqUrl := "https://api.weibo.com/2/users/show.json"
|
||||
r, err := transport.Client().Get(reqUrl + "?" + urls.Encode())
|
||||
@@ -384,11 +321,12 @@ func (s *SocialWeibo) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo,
|
||||
return nil, err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &BasicUserInfo{
|
||||
Identity: token.Extra["id_token"],
|
||||
Identity: token.Extra("uid"),
|
||||
Name: data.Name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -13,9 +13,8 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/go.crypto/ssh"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
)
|
||||
@@ -35,8 +34,11 @@ func handleServerConn(keyId string, chans <-chan ssh.NewChannel) {
|
||||
go func(in <-chan *ssh.Request) {
|
||||
defer channel.Close()
|
||||
for req := range in {
|
||||
ok, payload := false, strings.TrimLeft(string(req.Payload), "\x00")
|
||||
ok, payload := false, strings.TrimLeft(string(req.Payload), "\x00&")
|
||||
fmt.Println("Request:", req.Type, req.WantReply, payload)
|
||||
if req.WantReply {
|
||||
fmt.Println(req.Reply(true, nil))
|
||||
}
|
||||
switch req.Type {
|
||||
case "env":
|
||||
args := strings.Split(strings.Replace(payload, "\x00", "", -1), "\v")
|
||||
@@ -54,7 +56,7 @@ func handleServerConn(keyId string, chans <-chan ssh.NewChannel) {
|
||||
case "exec":
|
||||
os.Setenv("SSH_ORIGINAL_COMMAND", strings.TrimLeft(payload, "'("))
|
||||
log.Info("Payload: %v", strings.TrimLeft(payload, "'("))
|
||||
cmd := exec.Command("/Users/jiahuachen/Applications/Go/src/github.com/gogits/gogs-ng/gogs-ng", "serv", "key-"+keyId)
|
||||
cmd := exec.Command("/Users/jiahuachen/Applications/Go/src/github.com/gogits/gogs/gogs", "serv", "key-"+keyId)
|
||||
cmd.Stdout = channel
|
||||
cmd.Stdin = channel
|
||||
cmd.Stderr = channel.Stderr()
|
||||
@@ -65,7 +67,6 @@ func handleServerConn(keyId string, chans <-chan ssh.NewChannel) {
|
||||
}
|
||||
}
|
||||
fmt.Println("Done:", ok)
|
||||
req.Reply(ok, nil) // BUG: Git on Mac seems not know this reply and hang?
|
||||
}
|
||||
fmt.Println("Done!!!")
|
||||
}(requests)
|
||||
@@ -101,7 +102,7 @@ func Listen(port string) {
|
||||
config := &ssh.ServerConfig{
|
||||
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
|
||||
// keyCache[string(ssh.MarshalAuthorizedKey(key))] = 2
|
||||
return &ssh.Permissions{Extensions: map[string]string{"key-id": "2"}}, nil
|
||||
return &ssh.Permissions{Extensions: map[string]string{"key-id": "1"}}, nil
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -2,18 +2,6 @@
|
||||
|
||||
set -e
|
||||
|
||||
# summary of how this script can be called:
|
||||
# * <postinst> `configure' <most-recently-configured-version>
|
||||
# * <old-postinst> `abort-upgrade' <new version>
|
||||
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
|
||||
# <new-version>
|
||||
# * <postinst> `abort-remove'
|
||||
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
|
||||
# <failed-install-package> <version> `removing'
|
||||
# <conflicting-package> <version>
|
||||
# for details, see http://www.debian.org/doc/debian-policy/ or
|
||||
# the debian-policy package
|
||||
|
||||
APP_NAME="gogs"
|
||||
CLI="${APP_NAME}"
|
||||
APP_USER=$(${CLI} config:get APP_USER)
|
||||
@@ -21,28 +9,27 @@ APP_GROUP=$(${CLI} config:get APP_GROUP)
|
||||
APP_CONFIG="/etc/${APP_NAME}/conf/app.ini"
|
||||
|
||||
case "$1" in
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
exit 0
|
||||
;;
|
||||
|
||||
configure)
|
||||
configure|*)
|
||||
mkdir -p $(dirname ${APP_CONFIG})
|
||||
chown ${APP_USER}.${APP_GROUP} $(dirname ${APP_CONFIG})
|
||||
[ -f ${APP_CONFIG} ] || ${CLI} run cp conf/app.ini ${APP_CONFIG}
|
||||
${CLI} config:set USER=${APP_USER}
|
||||
${CLI} config:set GOGS_CUSTOM="/etc/${APP_NAME}"
|
||||
PORT=$(${CLI} config:get PORT || echo "6000")
|
||||
sed -i "s|HTTP_PORT = 3000|HTTP_PORT = ${PORT}|" ${APP_CONFIG}
|
||||
sed -i "s|RUN_USER = git|RUN_USER = ${APP_USER}|" ${APP_CONFIG}
|
||||
sed -i "s|RUN_MODE = dev|RUN_MODE = prod|" ${APP_CONFIG}
|
||||
|
||||
# setup symlink towards custom conf
|
||||
mkdir -p /opt/${APP_NAME}/custom/conf
|
||||
chown -R ${APP_USER}.${APP_GROUP} /opt/${APP_NAME}/custom
|
||||
ln -f -s ${APP_CONFIG} /opt/${APP_NAME}/custom/conf/app.ini
|
||||
|
||||
# scale
|
||||
${CLI} scale web=1 || true
|
||||
;;
|
||||
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
exit 0
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "postinst called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -303,6 +303,9 @@ var Gogits = {};
|
||||
|
||||
// api working
|
||||
Gogits.getUsers = function (val, $target) {
|
||||
var notEmpty = function (str) {
|
||||
return str && str.length > 0;
|
||||
}
|
||||
$.ajax({
|
||||
url: '/api/v1/users/search?q=' + val,
|
||||
dataType: "json",
|
||||
@@ -310,7 +313,11 @@ var Gogits = {};
|
||||
if (json.ok && json.data.length) {
|
||||
var html = '';
|
||||
$.each(json.data, function (i, item) {
|
||||
html += '<li><img src="' + item.avatar + '">' + item.username + '</li>';
|
||||
html += '<li><img src="' + item.avatar + '">' + item.username;
|
||||
if (notEmpty(item.full_name)) {
|
||||
html += ' (' + item.full_name + ')';
|
||||
}
|
||||
html += '</li>';
|
||||
});
|
||||
$target.toggleShow();
|
||||
$target.find('ul').html(html);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
368
public/ng/css/magnific-popup.css
Normal file
368
public/ng/css/magnific-popup.css
Normal file
@@ -0,0 +1,368 @@
|
||||
/* Magnific Popup CSS */
|
||||
.mfp-bg {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1042;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
background: #0b0b0b;
|
||||
opacity: 0.8;
|
||||
filter: alpha(opacity=80); }
|
||||
|
||||
.mfp-wrap {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1043;
|
||||
position: fixed;
|
||||
outline: none !important;
|
||||
-webkit-backface-visibility: hidden; }
|
||||
|
||||
.mfp-container {
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding: 0 8px;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box; }
|
||||
|
||||
.mfp-container:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle; }
|
||||
|
||||
.mfp-align-top .mfp-container:before {
|
||||
display: none; }
|
||||
|
||||
.mfp-content {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
z-index: 1045; }
|
||||
|
||||
.mfp-inline-holder .mfp-content, .mfp-ajax-holder .mfp-content {
|
||||
width: 100%;
|
||||
cursor: auto; }
|
||||
|
||||
.mfp-ajax-cur {
|
||||
cursor: progress; }
|
||||
|
||||
.mfp-zoom-out-cur, .mfp-zoom-out-cur .mfp-image-holder .mfp-close {
|
||||
cursor: -moz-zoom-out;
|
||||
cursor: -webkit-zoom-out;
|
||||
cursor: zoom-out; }
|
||||
|
||||
.mfp-zoom {
|
||||
cursor: pointer;
|
||||
cursor: -webkit-zoom-in;
|
||||
cursor: -moz-zoom-in;
|
||||
cursor: zoom-in; }
|
||||
|
||||
.mfp-auto-cursor .mfp-content {
|
||||
cursor: auto; }
|
||||
|
||||
.mfp-close, .mfp-arrow, .mfp-preloader, .mfp-counter {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none; }
|
||||
|
||||
.mfp-loading.mfp-figure {
|
||||
display: none; }
|
||||
|
||||
.mfp-hide {
|
||||
display: none !important; }
|
||||
|
||||
.mfp-preloader {
|
||||
color: #cccccc;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: auto;
|
||||
text-align: center;
|
||||
margin-top: -0.8em;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
z-index: 1044; }
|
||||
.mfp-preloader a {
|
||||
color: #cccccc; }
|
||||
.mfp-preloader a:hover {
|
||||
color: white; }
|
||||
|
||||
.mfp-s-ready .mfp-preloader {
|
||||
display: none; }
|
||||
|
||||
.mfp-s-error .mfp-content {
|
||||
display: none; }
|
||||
|
||||
button.mfp-close, button.mfp-arrow {
|
||||
overflow: visible;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
-webkit-appearance: none;
|
||||
display: block;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
z-index: 1046;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none; }
|
||||
button::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0; }
|
||||
|
||||
.mfp-close {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
opacity: 0.65;
|
||||
filter: alpha(opacity=65);
|
||||
padding: 0 0 18px 10px;
|
||||
color: white;
|
||||
font-style: normal;
|
||||
font-size: 28px;
|
||||
font-family: Arial, Baskerville, monospace; }
|
||||
.mfp-close:hover, .mfp-close:focus {
|
||||
opacity: 1;
|
||||
filter: alpha(opacity=100); }
|
||||
.mfp-close:active {
|
||||
top: 1px; }
|
||||
|
||||
.mfp-close-btn-in .mfp-close {
|
||||
color: #333333; }
|
||||
|
||||
.mfp-image-holder .mfp-close, .mfp-iframe-holder .mfp-close {
|
||||
color: white;
|
||||
right: -6px;
|
||||
text-align: right;
|
||||
padding-right: 6px;
|
||||
width: 100%; }
|
||||
|
||||
.mfp-counter {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
color: #cccccc;
|
||||
font-size: 12px;
|
||||
line-height: 18px; }
|
||||
|
||||
.mfp-arrow {
|
||||
position: absolute;
|
||||
opacity: 0.65;
|
||||
filter: alpha(opacity=65);
|
||||
margin: 0;
|
||||
top: 50%;
|
||||
margin-top: -55px;
|
||||
padding: 0;
|
||||
width: 90px;
|
||||
height: 110px;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
|
||||
.mfp-arrow:active {
|
||||
margin-top: -54px; }
|
||||
.mfp-arrow:hover, .mfp-arrow:focus {
|
||||
opacity: 1;
|
||||
filter: alpha(opacity=100); }
|
||||
.mfp-arrow:before, .mfp-arrow:after, .mfp-arrow .mfp-b, .mfp-arrow .mfp-a {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
margin-top: 35px;
|
||||
margin-left: 35px;
|
||||
border: medium inset transparent; }
|
||||
.mfp-arrow:after, .mfp-arrow .mfp-a {
|
||||
border-top-width: 13px;
|
||||
border-bottom-width: 13px;
|
||||
top: 8px; }
|
||||
.mfp-arrow:before, .mfp-arrow .mfp-b {
|
||||
border-top-width: 21px;
|
||||
border-bottom-width: 21px;
|
||||
opacity: 0.7; }
|
||||
|
||||
.mfp-arrow-left {
|
||||
left: 0; }
|
||||
.mfp-arrow-left:after, .mfp-arrow-left .mfp-a {
|
||||
border-right: 17px solid white;
|
||||
margin-left: 31px; }
|
||||
.mfp-arrow-left:before, .mfp-arrow-left .mfp-b {
|
||||
margin-left: 25px;
|
||||
border-right: 27px solid #3f3f3f; }
|
||||
|
||||
.mfp-arrow-right {
|
||||
right: 0; }
|
||||
.mfp-arrow-right:after, .mfp-arrow-right .mfp-a {
|
||||
border-left: 17px solid white;
|
||||
margin-left: 39px; }
|
||||
.mfp-arrow-right:before, .mfp-arrow-right .mfp-b {
|
||||
border-left: 27px solid #3f3f3f; }
|
||||
|
||||
.mfp-iframe-holder {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px; }
|
||||
.mfp-iframe-holder .mfp-content {
|
||||
line-height: 0;
|
||||
width: 100%;
|
||||
max-width: 900px; }
|
||||
.mfp-iframe-holder .mfp-close {
|
||||
top: -40px; }
|
||||
|
||||
.mfp-iframe-scaler {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
padding-top: 56.25%; }
|
||||
.mfp-iframe-scaler iframe {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
|
||||
background: black; }
|
||||
|
||||
/* Main image in popup */
|
||||
img.mfp-img {
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
line-height: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 40px 0 40px;
|
||||
margin: 0 auto; }
|
||||
|
||||
/* The shadow behind the image */
|
||||
.mfp-figure {
|
||||
line-height: 0; }
|
||||
.mfp-figure:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 40px;
|
||||
bottom: 40px;
|
||||
display: block;
|
||||
right: 0;
|
||||
width: auto;
|
||||
height: auto;
|
||||
z-index: -1;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
|
||||
background: #444444; }
|
||||
.mfp-figure small {
|
||||
color: #bdbdbd;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
line-height: 14px; }
|
||||
.mfp-figure figure {
|
||||
margin: 0; }
|
||||
|
||||
.mfp-bottom-bar {
|
||||
margin-top: -36px;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
cursor: auto; }
|
||||
|
||||
.mfp-title {
|
||||
text-align: left;
|
||||
line-height: 18px;
|
||||
color: #f3f3f3;
|
||||
word-wrap: break-word;
|
||||
padding-right: 36px; }
|
||||
|
||||
.mfp-image-holder .mfp-content {
|
||||
max-width: 100%; }
|
||||
|
||||
.mfp-gallery .mfp-image-holder .mfp-figure {
|
||||
cursor: pointer; }
|
||||
|
||||
@media screen and (max-width: 800px) and (orientation: landscape), screen and (max-height: 300px) {
|
||||
/**
|
||||
* Remove all paddings around the image on small screen
|
||||
*/
|
||||
.mfp-img-mobile .mfp-image-holder {
|
||||
padding-left: 0;
|
||||
padding-right: 0; }
|
||||
.mfp-img-mobile img.mfp-img {
|
||||
padding: 0; }
|
||||
.mfp-img-mobile .mfp-figure:after {
|
||||
top: 0;
|
||||
bottom: 0; }
|
||||
.mfp-img-mobile .mfp-figure small {
|
||||
display: inline;
|
||||
margin-left: 5px; }
|
||||
.mfp-img-mobile .mfp-bottom-bar {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
top: auto;
|
||||
padding: 3px 5px;
|
||||
position: fixed;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box; }
|
||||
.mfp-img-mobile .mfp-bottom-bar:empty {
|
||||
padding: 0; }
|
||||
.mfp-img-mobile .mfp-counter {
|
||||
right: 5px;
|
||||
top: 3px; }
|
||||
.mfp-img-mobile .mfp-close {
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
padding: 0; } }
|
||||
|
||||
@media all and (max-width: 900px) {
|
||||
.mfp-arrow {
|
||||
-webkit-transform: scale(0.75);
|
||||
transform: scale(0.75); }
|
||||
.mfp-arrow-left {
|
||||
-webkit-transform-origin: 0;
|
||||
transform-origin: 0; }
|
||||
.mfp-arrow-right {
|
||||
-webkit-transform-origin: 100%;
|
||||
transform-origin: 100%; }
|
||||
.mfp-container {
|
||||
padding-left: 6px;
|
||||
padding-right: 6px; } }
|
||||
|
||||
.mfp-ie7 .mfp-img {
|
||||
padding: 0; }
|
||||
.mfp-ie7 .mfp-bottom-bar {
|
||||
width: 600px;
|
||||
left: 50%;
|
||||
margin-left: -300px;
|
||||
margin-top: 5px;
|
||||
padding-bottom: 5px; }
|
||||
.mfp-ie7 .mfp-container {
|
||||
padding: 0; }
|
||||
.mfp-ie7 .mfp-content {
|
||||
padding-top: 44px; }
|
||||
.mfp-ie7 .mfp-close {
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding-top: 0; }
|
||||
25
public/ng/css/tipsy.css
Normal file
25
public/ng/css/tipsy.css
Normal file
@@ -0,0 +1,25 @@
|
||||
.tipsy { font-size: 10px; position: absolute; padding: 5px; z-index: 100000; }
|
||||
.tipsy-inner { background-color: #000; color: #FFF; max-width: 200px; padding: 5px 8px 4px 8px; text-align: center; }
|
||||
|
||||
/* Rounded corners */
|
||||
.tipsy-inner { border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; }
|
||||
|
||||
/* Uncomment for shadow */
|
||||
/*.tipsy-inner { box-shadow: 0 0 5px #000000; -webkit-box-shadow: 0 0 5px #000000; -moz-box-shadow: 0 0 5px #000000; }*/
|
||||
|
||||
.tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed #000; }
|
||||
|
||||
/* Rules to colour arrows */
|
||||
.tipsy-arrow-n { border-bottom-color: #000; }
|
||||
.tipsy-arrow-s { border-top-color: #000; }
|
||||
.tipsy-arrow-e { border-left-color: #000; }
|
||||
.tipsy-arrow-w { border-right-color: #000; }
|
||||
|
||||
.tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
|
||||
.tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
|
||||
.tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; }
|
||||
.tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; }
|
||||
@@ -4,7 +4,7 @@
|
||||
}
|
||||
html {
|
||||
font-size: 13px;
|
||||
font-family: Helvetica, "Microsoft Yahei", Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
font-family: Helvetica, "Microsoft Yahei", Arial, sans-serif;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
line-height: 24px;
|
||||
@@ -16,7 +16,7 @@ textarea,
|
||||
select,
|
||||
option,
|
||||
button {
|
||||
font-family: Helvetica, "Microsoft Yahei", Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
font-family: Helvetica, "Microsoft Yahei", Arial, sans-serif;
|
||||
}
|
||||
article,
|
||||
aside,
|
||||
@@ -151,7 +151,7 @@ code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace, monospace;
|
||||
font-family: monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
.text-left {
|
||||
@@ -277,9 +277,6 @@ hr {
|
||||
border-bottom: 1px solid #dddddd;
|
||||
margin-bottom: .75em;
|
||||
}
|
||||
p code {
|
||||
color: #b63b2c;
|
||||
}
|
||||
.radius {
|
||||
border-radius: .25em;
|
||||
}
|
||||
@@ -369,6 +366,9 @@ dt {
|
||||
.grid-4-5 {
|
||||
width: 80%;
|
||||
}
|
||||
.btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.btn-small {
|
||||
font-size: 10.8px;
|
||||
padding: .4em .9em;
|
||||
@@ -379,6 +379,7 @@ dt {
|
||||
}
|
||||
.btn-large {
|
||||
font-size: 14.4px;
|
||||
padding: .4em .9em;
|
||||
}
|
||||
.btn-green {
|
||||
background-color: #65ad4e;
|
||||
@@ -460,6 +461,9 @@ dt {
|
||||
box-sizing: content-box;
|
||||
text-align: center;
|
||||
}
|
||||
.btn-comb {
|
||||
margin-left: -1px;
|
||||
}
|
||||
.btn-disabled {
|
||||
opacity: .6;
|
||||
cursor: not-allowed;
|
||||
@@ -483,6 +487,10 @@ dt {
|
||||
.ipt-large {
|
||||
font-size: 14.4px;
|
||||
}
|
||||
.ipt-textarea {
|
||||
height: auto !important;
|
||||
width: auto;
|
||||
}
|
||||
.ipt-disabled,
|
||||
input[disabled] {
|
||||
background-color: #f2f2f2 !important;
|
||||
@@ -712,6 +720,14 @@ ul.menu-radius > li:last-child > a {
|
||||
border-bottom-left-radius: .3em;
|
||||
border-bottom-right-radius: .3em;
|
||||
}
|
||||
.panel.panel-info {
|
||||
border-color: #85c5e5;
|
||||
}
|
||||
.panel.panel-info > .panel-header {
|
||||
color: #31708f;
|
||||
background-color: #d9edf7;
|
||||
border-color: #85c5e5;
|
||||
}
|
||||
.panel.panel-warning {
|
||||
border-color: #F0C36D;
|
||||
}
|
||||
@@ -720,7 +736,7 @@ ul.menu-radius > li:last-child > a {
|
||||
border-color: #F0C36D;
|
||||
}
|
||||
.label {
|
||||
padding: 2px 6px;
|
||||
padding: .3em .6em .2em .6em;
|
||||
color: #ffffff;
|
||||
}
|
||||
.label-red {
|
||||
@@ -735,6 +751,10 @@ ul.menu-radius > li:last-child > a {
|
||||
.label-green {
|
||||
background-color: #65ad4e;
|
||||
}
|
||||
.label-green:hover {
|
||||
background-color: #71bf57;
|
||||
color: #FFF;
|
||||
}
|
||||
.label-orange {
|
||||
background-color: #df7514;
|
||||
}
|
||||
@@ -795,6 +815,17 @@ ul.menu-radius > li:last-child > a {
|
||||
border: 1px solid #b05c10;
|
||||
background-color: #fcecdd;
|
||||
}
|
||||
.white-popup-block {
|
||||
background: #FFF;
|
||||
padding: 20px 30px;
|
||||
text-align: left;
|
||||
max-width: 650px;
|
||||
margin: 40px auto;
|
||||
position: relative;
|
||||
}
|
||||
.white-popup-block p {
|
||||
font-size: 14px;
|
||||
}
|
||||
table th,
|
||||
table td {
|
||||
padding: .3em .6em;
|
||||
@@ -851,3 +882,30 @@ table td {
|
||||
.table-radius tbody tr:last-child > td:last-child {
|
||||
border-bottom-right-radius: .3em;
|
||||
}
|
||||
.pager .page {
|
||||
padding: .3em .5em .2em .5em;
|
||||
margin: 0 .3em;
|
||||
background-color: #cccccc;
|
||||
color: #444444;
|
||||
}
|
||||
.pager .page:hover,
|
||||
.pager .page.hover {
|
||||
background-color: #428bca;
|
||||
color: #ffffff;
|
||||
}
|
||||
.pager .prev,
|
||||
.pager .next {
|
||||
padding: .4em .6em;
|
||||
margin: 0 .3em;
|
||||
background-color: #505050;
|
||||
color: #fafafa;
|
||||
}
|
||||
.pager .prev.invalid,
|
||||
.pager .next.invalid {
|
||||
background-color: #eeeeee;
|
||||
color: #aaaaaa;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.pager .page-radius {
|
||||
border-radius: .2em;
|
||||
}
|
||||
|
||||
20
public/ng/fonts/octicons.css
vendored
20
public/ng/fonts/octicons.css
vendored
@@ -14,22 +14,20 @@
|
||||
.mega-octicon is optimized for 32px but can be used larger.
|
||||
|
||||
*/
|
||||
.octicon {
|
||||
font: normal normal 16px octicons;
|
||||
line-height: 1;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
.mega-octicon {
|
||||
font: normal normal 32px octicons;
|
||||
line-height: 1;
|
||||
.octicon, .mega-octicon {
|
||||
font: normal normal normal 16px/1 octicons;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.mega-octicon { font-size: 32px; }
|
||||
|
||||
|
||||
.octicon-alert:before { content: '\f02d'} /* */
|
||||
.octicon-alignment-align:before { content: '\f08a'} /* */
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,9 @@
|
||||
// @codekit-prepend "lib/jquery-1.11.1.min.js"
|
||||
// @codekit-prepend "lib/lib.js"
|
||||
// @codekit-prepend "lib/tabs.js"
|
||||
// @codekit-prepend "utils/tabs.js"
|
||||
// @codekit-prepend "utils/preview.js"
|
||||
// @codekit-prepend "gogs/issue_label.js"
|
||||
// @codekit-prepend "lib/jquery.tipsy.js"
|
||||
|
||||
var Gogs = {};
|
||||
|
||||
@@ -201,14 +204,21 @@ var Gogs = {};
|
||||
|
||||
// Search users by keyword.
|
||||
Gogs.searchUsers = function (val, $target) {
|
||||
var notEmpty = function (str) {
|
||||
return str && str.length > 0;
|
||||
}
|
||||
$.ajax({
|
||||
url: '/api/v1/users/search?q=' + val,
|
||||
url: Gogs.AppSubUrl + '/api/v1/users/search?q=' + val,
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
if (json.ok && json.data.length) {
|
||||
var html = '';
|
||||
$.each(json.data, function (i, item) {
|
||||
html += '<li><a><img src="' + item.avatar + '">' + item.username + '</a></li>';
|
||||
html += '<li><a><img src="' + item.avatar_url + '">' + item.username;
|
||||
if (notEmpty(item.full_name)) {
|
||||
html += ' (' + item.full_name + ')';
|
||||
}
|
||||
html += '</a></li>';
|
||||
});
|
||||
$target.html(html);
|
||||
$target.toggleShow();
|
||||
@@ -222,13 +232,13 @@ var Gogs = {};
|
||||
// Search repositories by keyword.
|
||||
Gogs.searchRepos = function (val, $target, $param) {
|
||||
$.ajax({
|
||||
url: '/api/v1/repos/search?q=' + val + '&' + $param,
|
||||
url: Gogs.AppSubUrl + '/api/v1/repos/search?q=' + val + '&' + $param,
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
if (json.ok && json.data.length) {
|
||||
var html = '';
|
||||
$.each(json.data, function (i, item) {
|
||||
html += '<li><a><span class="octicon octicon-repo"></span> ' + item.repolink + '</a></li>';
|
||||
html += '<li><a><span class="octicon octicon-repo"></span> ' + item.full_name + '</a></li>';
|
||||
});
|
||||
$target.html(html);
|
||||
$target.toggleShow();
|
||||
@@ -245,7 +255,7 @@ var Gogs = {};
|
||||
return;
|
||||
}
|
||||
$(selector).zclip({
|
||||
path: "/js/ZeroClipboard.swf",
|
||||
path: Gogs.AppSubUrl + "/js/ZeroClipboard.swf",
|
||||
copy: function () {
|
||||
var t = $(this).data("copy-val");
|
||||
var to = $($(this).data("copy-from"));
|
||||
@@ -262,17 +272,14 @@ var Gogs = {};
|
||||
return str;
|
||||
},
|
||||
afterCopy: function () {
|
||||
alert("Clone URL has copied!");
|
||||
// var $this = $(this);
|
||||
// $this.tooltip('hide')
|
||||
// .attr('data-original-title', 'Copied OK');
|
||||
// setTimeout(function () {
|
||||
// $this.tooltip("show");
|
||||
// }, 200);
|
||||
// setTimeout(function () {
|
||||
// $this.tooltip('hide')
|
||||
// .attr('data-original-title', 'Copy to Clipboard');
|
||||
// }, 3000);
|
||||
var $this = $(this);
|
||||
$this.tipsy("hide").attr('original-title', $this.data('after-title'));
|
||||
setTimeout(function () {
|
||||
$this.tipsy("show");
|
||||
}, 200);
|
||||
setTimeout(function () {
|
||||
$this.tipsy('hide').attr('original-title', $this.data('original-title'));
|
||||
}, 2000);
|
||||
}
|
||||
}).addClass("js-copy-bind");
|
||||
}
|
||||
@@ -281,29 +288,73 @@ var Gogs = {};
|
||||
function initCore() {
|
||||
Gogs.renderMarkdown();
|
||||
Gogs.renderCodeView();
|
||||
|
||||
// Switch list.
|
||||
$('.js-tab-nav').click(function (e) {
|
||||
if (!$(this).hasClass('js-tab-nav-show')) {
|
||||
$(this).parent().find('.js-tab-nav-show').each(function () {
|
||||
$(this).removeClass('js-tab-nav-show');
|
||||
$($(this).data('tab-target')).hide();
|
||||
});
|
||||
$(this).addClass('js-tab-nav-show');
|
||||
$($(this).data('tab-target')).show();
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// Popup.
|
||||
$(document).on('click', '.popup-modal-dismiss', function (e) {
|
||||
e.preventDefault();
|
||||
$.magnificPopup.close();
|
||||
});
|
||||
|
||||
// Plugins.
|
||||
$('.collapse').hide();
|
||||
$('.tipsy-tooltip').tipsy({
|
||||
fade: true
|
||||
});
|
||||
}
|
||||
|
||||
function initUserSetting() {
|
||||
// Confirmation of change username in user profile page.
|
||||
$('#user-profile-form').submit(function (e) {
|
||||
var $username = $('#username');
|
||||
if (($username.data('uname') != $username.val()) && !confirm('Username has been changed, do you want to continue?')) {
|
||||
var $username = $('#username');
|
||||
var $profile_form = $('#user-profile-form');
|
||||
$('#change-username-btn').magnificPopup({
|
||||
modal: true,
|
||||
callbacks: {
|
||||
open: function () {
|
||||
if (($username.data('uname') == $username.val())) {
|
||||
$.magnificPopup.close();
|
||||
$profile_form.submit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}).click(function () {
|
||||
if (($username.data('uname') != $username.val())) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
$('#change-username-submit').click(function () {
|
||||
$.magnificPopup.close();
|
||||
$profile_form.submit();
|
||||
});
|
||||
|
||||
// Show add SSH key panel.
|
||||
$('#ssh-add').click(function () {
|
||||
$('#user-ssh-add-form').removeClass("hide");
|
||||
// Show panels.
|
||||
$('.show-form-btn').click(function () {
|
||||
$($(this).data('target-form')).removeClass("hide");
|
||||
});
|
||||
|
||||
// Confirmation of delete account.
|
||||
$('#delete-account-button').click(function (e) {
|
||||
if (!confirm('This account is going to be deleted, do you want to continue?')) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
}
|
||||
$('#delete-account-btn').magnificPopup({
|
||||
modal: true
|
||||
}).click(function (e) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
});
|
||||
$('#delete-account-submit').click(function () {
|
||||
$.magnificPopup.close();
|
||||
$('#delete-account-form').submit();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -343,48 +394,101 @@ function initRepo() {
|
||||
$('#repo-clone-url').val($(this).data('link'));
|
||||
$('.clone-url').text($(this).data('link'))
|
||||
});
|
||||
|
||||
// Copy URL.
|
||||
$('#repo-clone-copy').hover(function () {
|
||||
var $clone_btn = $('#repo-clone-copy');
|
||||
$clone_btn.hover(function () {
|
||||
Gogs.bindCopy($(this));
|
||||
})
|
||||
$clone_btn.tipsy({
|
||||
fade: true
|
||||
});
|
||||
|
||||
// Markdown preview.
|
||||
$('.markdown-preview').click(function() {
|
||||
var $this = $(this);
|
||||
$this.toggleAjax(function (resp) {
|
||||
$($this.data("preview")).html(resp);
|
||||
}, function () {
|
||||
$($this.data("preview")).html("no content");
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// when user changes hook type, hide/show proper divs
|
||||
function initHookTypeChange() {
|
||||
// web hook type change
|
||||
$('select#hook-type').on("change", function () {
|
||||
hookTypes = ['Gogs','Slack'];
|
||||
hookTypes = ['Gogs', 'Slack'];
|
||||
|
||||
var curHook = $(this).val();
|
||||
hookTypes.forEach(function(hookType) {
|
||||
if (curHook === hookType) {
|
||||
$('div#'+hookType.toLowerCase()).toggleShow();
|
||||
}
|
||||
else {
|
||||
$('div#'+hookType.toLowerCase()).toggleHide();
|
||||
}
|
||||
});
|
||||
var curHook = $(this).val();
|
||||
hookTypes.forEach(function (hookType) {
|
||||
if (curHook === hookType) {
|
||||
$('div#' + hookType.toLowerCase()).toggleShow();
|
||||
}
|
||||
else {
|
||||
$('div#' + hookType.toLowerCase()).toggleHide();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initRepoRelease() {
|
||||
$('#release-new-target-branch-list li').click(function() {
|
||||
if (!$(this).hasClass('checked')) {
|
||||
$('#repo-branch-current').text($(this).text());
|
||||
$('#tag-target').val($(this).text());
|
||||
|
||||
$(this).parent().find('.checked').removeClass('checked');
|
||||
$(this).addClass('checked');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function initRepoSetting() {
|
||||
// Options.
|
||||
// Confirmation of changing repository name.
|
||||
$('#repo-setting-form').submit(function (e) {
|
||||
var $reponame = $('#repo_name');
|
||||
if (($reponame.data('repo-name') != $reponame.val()) && !confirm('Repository name has been changed, do you want to continue?')) {
|
||||
var $reponame = $('#repo_name');
|
||||
var $setting_form = $('#repo-setting-form');
|
||||
$('#change-reponame-btn').magnificPopup({
|
||||
modal: true,
|
||||
callbacks: {
|
||||
open: function () {
|
||||
if (($reponame.data('repo-name') == $reponame.val())) {
|
||||
$.magnificPopup.close();
|
||||
$setting_form.submit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}).click(function () {
|
||||
if (($reponame.data('repo-name') != $reponame.val())) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
$('#change-reponame-submit').click(function () {
|
||||
$.magnificPopup.close();
|
||||
$setting_form.submit();
|
||||
});
|
||||
|
||||
initHookTypeChange();
|
||||
|
||||
$('#transfer-button').click(function () {
|
||||
$('#transfer-form').show();
|
||||
// Transfer repository.
|
||||
$('#transfer-repo-btn').magnificPopup({
|
||||
modal: true
|
||||
});
|
||||
$('#delete-button').click(function () {
|
||||
$('#delete-form').show();
|
||||
$('#transfer-repo-submit').click(function () {
|
||||
$.magnificPopup.close();
|
||||
$('#transfer-repo-form').submit();
|
||||
});
|
||||
|
||||
// Delete repository.
|
||||
$('#delete-repo-btn').magnificPopup({
|
||||
modal: true
|
||||
});
|
||||
$('#delete-repo-submit').click(function () {
|
||||
$.magnificPopup.close();
|
||||
$('#delete-repo-form').submit();
|
||||
});
|
||||
|
||||
// Collaboration.
|
||||
@@ -412,19 +516,39 @@ function initRepoSetting() {
|
||||
function initOrgSetting() {
|
||||
// Options.
|
||||
// Confirmation of changing organization name.
|
||||
$('#org-setting-form').submit(function (e) {
|
||||
var $orgname = $('#orgname');
|
||||
if (($orgname.data('orgname') != $orgname.val()) && !confirm('Organization name has been changed, do you want to continue?')) {
|
||||
var $orgname = $('#orgname');
|
||||
var $setting_form = $('#org-setting-form');
|
||||
$('#change-orgname-btn').magnificPopup({
|
||||
modal: true,
|
||||
callbacks: {
|
||||
open: function () {
|
||||
if (($orgname.data('orgname') == $orgname.val())) {
|
||||
$.magnificPopup.close();
|
||||
$setting_form.submit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}).click(function () {
|
||||
if (($orgname.data('orgname') != $orgname.val())) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
$('#change-orgname-submit').click(function () {
|
||||
$.magnificPopup.close();
|
||||
$setting_form.submit();
|
||||
});
|
||||
|
||||
// Confirmation of delete organization.
|
||||
$('#delete-org-button').click(function (e) {
|
||||
if (!confirm('This organization is going to be deleted, do you want to continue?')) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
}
|
||||
$('#delete-org-btn').magnificPopup({
|
||||
modal: true
|
||||
}).click(function (e) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
});
|
||||
$('#delete-org-submit').click(function () {
|
||||
$.magnificPopup.close();
|
||||
$('#delete-org-form').submit();
|
||||
});
|
||||
|
||||
initHookTypeChange();
|
||||
@@ -454,11 +578,14 @@ function initInvite() {
|
||||
|
||||
function initOrgTeamCreate() {
|
||||
// Delete team.
|
||||
$('#org-team-delete').click(function (e) {
|
||||
if (!confirm('This team is going to be deleted, do you want to continue?')) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
}
|
||||
$('#org-team-delete').magnificPopup({
|
||||
modal: true
|
||||
}).click(function (e) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
});
|
||||
$('#delete-team-submit').click(function () {
|
||||
$.magnificPopup.close();
|
||||
var $form = $('#team-create-form');
|
||||
$form.attr('action', $form.data('delete-url'));
|
||||
});
|
||||
@@ -522,15 +649,20 @@ function initAdmin() {
|
||||
$('.auth-name').toggleShow();
|
||||
}
|
||||
});
|
||||
|
||||
// Delete account.
|
||||
$('#user-delete').click(function (e) {
|
||||
if (!confirm('This account is going to be deleted, do you want to continue?')) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
}
|
||||
$('#delete-account-btn').magnificPopup({
|
||||
modal: true
|
||||
}).click(function (e) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
});
|
||||
$('#delete-account-submit').click(function () {
|
||||
$.magnificPopup.close();
|
||||
var $form = $('#user-profile-form');
|
||||
$form.attr('action', $form.data('delete-url'));
|
||||
});
|
||||
|
||||
// Create authorization.
|
||||
$('#auth-type').on("change", function () {
|
||||
var v = $(this).val();
|
||||
@@ -543,13 +675,17 @@ function initAdmin() {
|
||||
$('.ldap').toggleHide();
|
||||
}
|
||||
});
|
||||
|
||||
// Delete authorization.
|
||||
$('#auth-delete').click(function (e) {
|
||||
if (!confirm('This authorization is going to be deleted, do you want to continue?')) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
}
|
||||
var $form = $('auth-setting-form');
|
||||
$('#delete-auth-btn').magnificPopup({
|
||||
modal: true
|
||||
}).click(function (e) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
});
|
||||
$('#delete-auth-submit').click(function () {
|
||||
$.magnificPopup.close();
|
||||
var $form = $('#auth-setting-form');
|
||||
$form.attr('action', $form.data('delete-url'));
|
||||
});
|
||||
}
|
||||
@@ -591,7 +727,46 @@ function initInstall() {
|
||||
}());
|
||||
}
|
||||
|
||||
function initProfile() {
|
||||
// Avatar.
|
||||
$('#profile-avatar').tipsy({
|
||||
fade: true
|
||||
});
|
||||
}
|
||||
|
||||
function initTimeSwitch() {
|
||||
// Time switch.
|
||||
$(".time-since[title]").on("click", function () {
|
||||
var $this = $(this);
|
||||
|
||||
var title = $this.attr("title");
|
||||
var text = $this.text();
|
||||
|
||||
$this.text(title);
|
||||
$this.attr("title", text);
|
||||
});
|
||||
}
|
||||
|
||||
function initDiff() {
|
||||
$('.diff-detail-box>a').click(function () {
|
||||
$($(this).data('target')).slideToggle(100);
|
||||
})
|
||||
|
||||
var $counter = $('.diff-counter');
|
||||
if ($counter.length < 1) {
|
||||
return;
|
||||
}
|
||||
$counter.each(function (i, item) {
|
||||
var $item = $(item);
|
||||
var addLine = $item.find('span[data-line].add').data("line");
|
||||
var delLine = $item.find('span[data-line].del').data("line");
|
||||
var addPercent = parseFloat(addLine) / (parseFloat(addLine) + parseFloat(delLine)) * 100;
|
||||
$item.find(".bar .add").css("width", addPercent + "%");
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
Gogs.AppSubUrl = $('head').data('suburl') || '';
|
||||
initCore();
|
||||
if ($('#user-profile-setting').length) {
|
||||
initUserSetting();
|
||||
@@ -600,8 +775,12 @@ $(document).ready(function () {
|
||||
initRepoCreate();
|
||||
}
|
||||
if ($('#repo-header').length) {
|
||||
initTimeSwitch();
|
||||
initRepo();
|
||||
}
|
||||
if ($('#release').length) {
|
||||
initRepoRelease();
|
||||
}
|
||||
if ($('#repo-setting').length) {
|
||||
initRepoSetting();
|
||||
}
|
||||
@@ -626,8 +805,16 @@ $(document).ready(function () {
|
||||
if ($('#install-form').length) {
|
||||
initInstall();
|
||||
}
|
||||
if ($('#user-profile-page').length) {
|
||||
initProfile();
|
||||
}
|
||||
if ($('#diff-page').length) {
|
||||
initTimeSwitch();
|
||||
initDiff();
|
||||
}
|
||||
|
||||
Tabs('#dashboard-sidebar-menu');
|
||||
$('#dashboard-sidebar-menu').tabs();
|
||||
$('#pull-issue-preview').markdown_preview(".issue-add-comment");
|
||||
|
||||
homepage();
|
||||
|
||||
@@ -644,7 +831,7 @@ function homepage() {
|
||||
$('#promo-form').submit(function (e) {
|
||||
if ($('#username').val() === "") {
|
||||
e.preventDefault();
|
||||
window.location.href = '/user/login';
|
||||
window.location.href = Gogs.AppSubUrl + '/user/login';
|
||||
return true
|
||||
}
|
||||
});
|
||||
@@ -652,9 +839,13 @@ function homepage() {
|
||||
$('#register-button').click(function (e) {
|
||||
if ($('#username').val() === "") {
|
||||
e.preventDefault();
|
||||
window.location.href = '/user/sign_up';
|
||||
window.location.href = Gogs.AppSubUrl + '/user/sign_up';
|
||||
return true
|
||||
}
|
||||
$('#promo-form').attr('action', '/user/sign_up');
|
||||
$('#promo-form').attr('action', Gogs.AppSubUrl + '/user/sign_up');
|
||||
});
|
||||
}
|
||||
|
||||
String.prototype.endsWith = function (suffix) {
|
||||
return this.indexOf(suffix, this.length - suffix.length) !== -1;
|
||||
};
|
||||
|
||||
43
public/ng/js/gogs/issue_label.js
Normal file
43
public/ng/js/gogs/issue_label.js
Normal file
@@ -0,0 +1,43 @@
|
||||
// when dom ready, init issue label events
|
||||
$(document).ready(function(){
|
||||
var labelColors = ["#e11d21","#EB6420","#FBCA04","#009800",
|
||||
"#006B75","#207DE5","#0052cc","#53E917",
|
||||
"#F6C6C7","#FAD8C7","#FEF2C0","#BFE5BF",
|
||||
"#BFDADC","#C7DEF8","#BFD4F2","#D4C5F9"];
|
||||
|
||||
var colorDropHtml = "";
|
||||
labelColors.forEach(function(item){
|
||||
colorDropHtml += '<a class="color" style="background-color:'+item+'" data-color-hex="'+item+'"></a>';
|
||||
});
|
||||
|
||||
|
||||
|
||||
// render label color input
|
||||
var color_input = $('#label-add-color');
|
||||
var color_label = $('#label-color-drop label');
|
||||
color_label.css("background-color",labelColors[0]);
|
||||
color_input.val(labelColors[0]);
|
||||
|
||||
|
||||
// render label color drop
|
||||
$('#label-color-drop .drop-down')
|
||||
.html(colorDropHtml)
|
||||
.on("click","a",function(){
|
||||
var color = $(this).data("color-hex");
|
||||
color_label.css("background-color",color);
|
||||
color_input.val(color);
|
||||
});
|
||||
|
||||
// color drop visible
|
||||
var form = $('#label-add-form');
|
||||
$('#label-new-btn').on("click",function(){
|
||||
if(form.hasClass("hidden")){
|
||||
form.removeClass("hidden");
|
||||
}
|
||||
});
|
||||
$('#label-cancel-btn').on("click",function(){
|
||||
form.addClass("hidden");
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
4
public/ng/js/lib/jquery.magnific-popup.min.js
vendored
Normal file
4
public/ng/js/lib/jquery.magnific-popup.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
258
public/ng/js/lib/jquery.tipsy.js
Normal file
258
public/ng/js/lib/jquery.tipsy.js
Normal file
@@ -0,0 +1,258 @@
|
||||
// tipsy, facebook style tooltips for jquery
|
||||
// version 1.0.0a
|
||||
// (c) 2008-2010 jason frame [jason@onehackoranother.com]
|
||||
// released under the MIT license
|
||||
|
||||
(function($) {
|
||||
|
||||
function maybeCall(thing, ctx) {
|
||||
return (typeof thing == 'function') ? (thing.call(ctx)) : thing;
|
||||
};
|
||||
|
||||
function isElementInDOM(ele) {
|
||||
while (ele = ele.parentNode) {
|
||||
if (ele == document) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
function Tipsy(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = options;
|
||||
this.enabled = true;
|
||||
this.fixTitle();
|
||||
};
|
||||
|
||||
Tipsy.prototype = {
|
||||
show: function() {
|
||||
var title = this.getTitle();
|
||||
if (title && this.enabled) {
|
||||
var $tip = this.tip();
|
||||
|
||||
$tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
|
||||
$tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity
|
||||
$tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
|
||||
|
||||
var pos = $.extend({}, this.$element.offset(), {
|
||||
width: this.$element[0].offsetWidth,
|
||||
height: this.$element[0].offsetHeight
|
||||
});
|
||||
|
||||
var actualWidth = $tip[0].offsetWidth,
|
||||
actualHeight = $tip[0].offsetHeight,
|
||||
gravity = maybeCall(this.options.gravity, this.$element[0]);
|
||||
|
||||
var tp;
|
||||
switch (gravity.charAt(0)) {
|
||||
case 'n':
|
||||
tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
|
||||
break;
|
||||
case 's':
|
||||
tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
|
||||
break;
|
||||
case 'e':
|
||||
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset};
|
||||
break;
|
||||
case 'w':
|
||||
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset};
|
||||
break;
|
||||
}
|
||||
|
||||
if (gravity.length == 2) {
|
||||
if (gravity.charAt(1) == 'w') {
|
||||
tp.left = pos.left + pos.width / 2 - 15;
|
||||
} else {
|
||||
tp.left = pos.left + pos.width / 2 - actualWidth + 15;
|
||||
}
|
||||
}
|
||||
|
||||
$tip.css(tp).addClass('tipsy-' + gravity);
|
||||
$tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0);
|
||||
if (this.options.className) {
|
||||
$tip.addClass(maybeCall(this.options.className, this.$element[0]));
|
||||
}
|
||||
|
||||
if (this.options.fade) {
|
||||
$tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity});
|
||||
} else {
|
||||
$tip.css({visibility: 'visible', opacity: this.options.opacity});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
if (this.options.fade) {
|
||||
this.tip().stop().fadeOut(function() { $(this).remove(); });
|
||||
} else {
|
||||
this.tip().remove();
|
||||
}
|
||||
},
|
||||
|
||||
fixTitle: function() {
|
||||
var $e = this.$element;
|
||||
if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') {
|
||||
$e.attr('original-title', $e.attr('title') || '').removeAttr('title');
|
||||
}
|
||||
},
|
||||
|
||||
getTitle: function() {
|
||||
var title, $e = this.$element, o = this.options;
|
||||
this.fixTitle();
|
||||
var title, o = this.options;
|
||||
if (typeof o.title == 'string') {
|
||||
title = $e.attr(o.title == 'title' ? 'original-title' : o.title);
|
||||
} else if (typeof o.title == 'function') {
|
||||
title = o.title.call($e[0]);
|
||||
}
|
||||
title = ('' + title).replace(/(^\s*|\s*$)/, "");
|
||||
return title || o.fallback;
|
||||
},
|
||||
|
||||
tip: function() {
|
||||
if (!this.$tip) {
|
||||
this.$tip = $('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>');
|
||||
this.$tip.data('tipsy-pointee', this.$element[0]);
|
||||
}
|
||||
return this.$tip;
|
||||
},
|
||||
|
||||
validate: function() {
|
||||
if (!this.$element[0].parentNode) {
|
||||
this.hide();
|
||||
this.$element = null;
|
||||
this.options = null;
|
||||
}
|
||||
},
|
||||
|
||||
enable: function() { this.enabled = true; },
|
||||
disable: function() { this.enabled = false; },
|
||||
toggleEnabled: function() { this.enabled = !this.enabled; }
|
||||
};
|
||||
|
||||
$.fn.tipsy = function(options) {
|
||||
|
||||
if (options === true) {
|
||||
return this.data('tipsy');
|
||||
} else if (typeof options == 'string') {
|
||||
var tipsy = this.data('tipsy');
|
||||
if (tipsy) tipsy[options]();
|
||||
return this;
|
||||
}
|
||||
|
||||
options = $.extend({}, $.fn.tipsy.defaults, options);
|
||||
|
||||
function get(ele) {
|
||||
var tipsy = $.data(ele, 'tipsy');
|
||||
if (!tipsy) {
|
||||
tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options));
|
||||
$.data(ele, 'tipsy', tipsy);
|
||||
}
|
||||
return tipsy;
|
||||
}
|
||||
|
||||
function enter() {
|
||||
var tipsy = get(this);
|
||||
tipsy.hoverState = 'in';
|
||||
if (options.delayIn == 0) {
|
||||
tipsy.show();
|
||||
} else {
|
||||
tipsy.fixTitle();
|
||||
setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn);
|
||||
}
|
||||
};
|
||||
|
||||
function leave() {
|
||||
var tipsy = get(this);
|
||||
tipsy.hoverState = 'out';
|
||||
if (options.delayOut == 0) {
|
||||
tipsy.hide();
|
||||
} else {
|
||||
setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut);
|
||||
}
|
||||
};
|
||||
|
||||
if (!options.live) this.each(function() { get(this); });
|
||||
|
||||
if (options.trigger != 'manual') {
|
||||
var binder = options.live ? 'live' : 'bind',
|
||||
eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus',
|
||||
eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur';
|
||||
this[binder](eventIn, enter)[binder](eventOut, leave);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
};
|
||||
|
||||
$.fn.tipsy.defaults = {
|
||||
className: null,
|
||||
delayIn: 0,
|
||||
delayOut: 0,
|
||||
fade: false,
|
||||
fallback: '',
|
||||
gravity: 'n',
|
||||
html: false,
|
||||
live: false,
|
||||
offset: 0,
|
||||
opacity: 0.8,
|
||||
title: 'title',
|
||||
trigger: 'hover'
|
||||
};
|
||||
|
||||
$.fn.tipsy.revalidate = function() {
|
||||
$('.tipsy').each(function() {
|
||||
var pointee = $.data(this, 'tipsy-pointee');
|
||||
if (!pointee || !isElementInDOM(pointee)) {
|
||||
$(this).remove();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Overwrite this method to provide options on a per-element basis.
|
||||
// For example, you could store the gravity in a 'tipsy-gravity' attribute:
|
||||
// return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
|
||||
// (remember - do not modify 'options' in place!)
|
||||
$.fn.tipsy.elementOptions = function(ele, options) {
|
||||
return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
|
||||
};
|
||||
|
||||
$.fn.tipsy.autoNS = function() {
|
||||
return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
|
||||
};
|
||||
|
||||
$.fn.tipsy.autoWE = function() {
|
||||
return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
|
||||
};
|
||||
|
||||
/**
|
||||
* yields a closure of the supplied parameters, producing a function that takes
|
||||
* no arguments and is suitable for use as an autogravity function like so:
|
||||
*
|
||||
* @param margin (int) - distance from the viewable region edge that an
|
||||
* element should be before setting its tooltip's gravity to be away
|
||||
* from that edge.
|
||||
* @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer
|
||||
* if there are no viewable region edges effecting the tooltip's
|
||||
* gravity. It will try to vary from this minimally, for example,
|
||||
* if 'sw' is preferred and an element is near the right viewable
|
||||
* region edge, but not the top edge, it will set the gravity for
|
||||
* that element's tooltip to be 'se', preserving the southern
|
||||
* component.
|
||||
*/
|
||||
$.fn.tipsy.autoBounds = function(margin, prefer) {
|
||||
return function() {
|
||||
var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)},
|
||||
boundTop = $(document).scrollTop() + margin,
|
||||
boundLeft = $(document).scrollLeft() + margin,
|
||||
$this = $(this);
|
||||
|
||||
if ($this.offset().top < boundTop) dir.ns = 'n';
|
||||
if ($this.offset().left < boundLeft) dir.ew = 'w';
|
||||
if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e';
|
||||
if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's';
|
||||
|
||||
return dir.ns + (dir.ew ? dir.ew : '');
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
10
public/ng/js/min/gogs-min.js
vendored
10
public/ng/js/min/gogs-min.js
vendored
File diff suppressed because one or more lines are too long
53
public/ng/js/utils/preview.js
Normal file
53
public/ng/js/utils/preview.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* preview plugin
|
||||
* @param selector
|
||||
* @param target_selector
|
||||
*/
|
||||
function Preview(selector, target_selector) {
|
||||
|
||||
// get input element
|
||||
function get_input($e) {
|
||||
return $e.find(".js-preview-input").eq(0);
|
||||
}
|
||||
|
||||
// get result html container element
|
||||
function get_container($t) {
|
||||
if ($t.hasClass("js-preview-container")) {
|
||||
return $t
|
||||
}
|
||||
return $t.find(".js-preview-container").eq(0);
|
||||
}
|
||||
|
||||
var $e = $(selector);
|
||||
var $t = $(target_selector);
|
||||
|
||||
var $ipt = get_input($t);
|
||||
if (!$ipt.length) {
|
||||
console.log("[preview]: no preview input");
|
||||
return
|
||||
}
|
||||
var $cnt = get_container($t);
|
||||
if (!$cnt.length) {
|
||||
console.log("[preview]: no preview container");
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// call api via ajax
|
||||
$e.on("click", function () {
|
||||
$.post("/api/v1/markdown", {
|
||||
text: $ipt.val()
|
||||
}, function (html) {
|
||||
$cnt.html(html);
|
||||
})
|
||||
});
|
||||
|
||||
console.log("[preview]: init preview @", selector, "&", target_selector);
|
||||
}
|
||||
|
||||
|
||||
$.fn.extend({
|
||||
markdown_preview: function (target) {
|
||||
Preview(this, target);
|
||||
}
|
||||
});
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* Created by fuxiaohei on 14-6-26.
|
||||
/*
|
||||
js tabs and tabbed content plugin
|
||||
*/
|
||||
|
||||
function Tabs(selector) {
|
||||
|
||||
function hide($nav) {
|
||||
@@ -24,7 +23,8 @@ function Tabs(selector) {
|
||||
$($current.data("tab-target")).addClass("js-tab-show");
|
||||
}
|
||||
// bind nav click
|
||||
$e.on("click", ".js-tab-nav", function () {
|
||||
$e.on("click", ".js-tab-nav", function (e) {
|
||||
e.preventDefault();
|
||||
var $this = $(this);
|
||||
// is showing, not change.
|
||||
if ($this.hasClass("js-tab-nav-show")) {
|
||||
@@ -36,4 +36,10 @@ function Tabs(selector) {
|
||||
});
|
||||
console.log("init tabs @", selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$.fn.extend({
|
||||
tabs: function () {
|
||||
Tabs(this);
|
||||
}
|
||||
});
|
||||
@@ -7,4 +7,5 @@
|
||||
@import "gogs/settings";
|
||||
@import "gogs/issue";
|
||||
@import "gogs/organization";
|
||||
@import "gogs/admin";
|
||||
@import "gogs/admin";
|
||||
@import "gogs/profile";
|
||||
@@ -17,9 +17,6 @@ body {
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
.fa {
|
||||
font-size: 14px;
|
||||
}
|
||||
.container {
|
||||
max-width: 1170px;
|
||||
padding: 0 1.5em;
|
||||
@@ -30,6 +27,11 @@ img.avatar-16 {
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
img.avatar-20 {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
img.avatar-24 {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
@@ -55,6 +57,11 @@ img.avatar-100{
|
||||
height: 100px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.drop-down{
|
||||
.panel-header{
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
#wrapper {
|
||||
padding: 0;
|
||||
margin: 0 0 -55px 0;
|
||||
@@ -95,6 +102,7 @@ clear: both;
|
||||
z-index: 100;
|
||||
font-size: 12px;
|
||||
width: 120%;
|
||||
min-width: 100px;
|
||||
li > a {
|
||||
padding: 3px 9px;
|
||||
}
|
||||
@@ -102,7 +110,7 @@ clear: both;
|
||||
}
|
||||
#header {
|
||||
background-color: @headerBgColor;
|
||||
height: 44px;
|
||||
height: 45px;
|
||||
> .menu-line {
|
||||
> li > a {
|
||||
display: inline-block;
|
||||
@@ -128,7 +136,7 @@ clear: both;
|
||||
#header-nav-user {
|
||||
height: 44px;
|
||||
img {
|
||||
margin: -4px 10px 0 0;
|
||||
margin: -2px 10px 0 0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
@@ -136,7 +144,7 @@ clear: both;
|
||||
color: @headerSignOutColor !important;
|
||||
}
|
||||
#header-nav-logo {
|
||||
padding: 6px 1.2em;
|
||||
padding: 6px 1.2em 6px 0;
|
||||
}
|
||||
#header-nav-explore,
|
||||
#header-nav-help {
|
||||
@@ -251,6 +259,9 @@ clear: both;
|
||||
.text-black {
|
||||
color: #444444;
|
||||
}
|
||||
.text-gold {
|
||||
color: #a1882b;
|
||||
}
|
||||
.table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
@@ -283,4 +294,8 @@ clear: both;
|
||||
li {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
.list-unstyled {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user