mirror of
https://github.com/gogs/gogs.git
synced 2026-02-28 17:20:59 +01:00
Compare commits
261 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
4f74b4e657 | ||
|
|
31d763bc1f | ||
|
|
a596388ebf | ||
|
|
8a09256941 | ||
|
|
c03f5a2c7c | ||
|
|
f94d7c3f51 | ||
|
|
061a879cea | ||
|
|
ed84adb679 | ||
|
|
9f015b4c73 | ||
|
|
bc3abb397f | ||
|
|
9e10304ab2 | ||
|
|
ae3639868e | ||
|
|
d082e821a0 | ||
|
|
78bd144c1c | ||
|
|
ebb4f1b78c | ||
|
|
62f21ff3ed | ||
|
|
0d9c41be7d | ||
|
|
c1ceec45da | ||
|
|
b162e565b3 | ||
|
|
6516ecaa51 | ||
|
|
5163d368e3 | ||
|
|
ad041167f7 | ||
|
|
cff3ca23a5 | ||
|
|
41386fa91a | ||
|
|
ea309acdb2 | ||
|
|
0f037b430a | ||
|
|
632b1b694d |
@@ -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
|
||||
|
||||
22
.gopmfile
22
.gopmfile
@@ -7,23 +7,25 @@ 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:5c8d1b7642
|
||||
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:3b1d86c3a8
|
||||
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:2246f45894
|
||||
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]
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
buildpack: "https://github.com/kr/heroku-buildpack-go.git"
|
||||
targets:
|
||||
ubuntu-14.04:
|
||||
ubuntu-12.04:
|
||||
debian-7:
|
||||
centos6:
|
||||
build_dependencies:
|
||||
- mercurial
|
||||
- bzr
|
||||
|
||||
@@ -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,16 +1,16 @@
|
||||
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.0 Beta
|
||||
##### Current version: 0.5.8 Beta
|
||||
|
||||
### NOTICES
|
||||
|
||||
- Due to testing purpose, data of [try.gogits.org](http://try.gogits.org) has been reset in **June 21, 2014** and will reset multiple times after. Please do **NOT** put your important data on the site.
|
||||
- Demo site [try.gogits.org](http://try.gogits.org) is running under `dev` branch.
|
||||
- Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) has been reset in **June 21, 2014** and will reset multiple times after. Please do **NOT** put your important data on the site.
|
||||
- Demo site [try.gogs.io](https://try.gogs.io) is running under `dev` branch.
|
||||
|
||||
#### Other language version
|
||||
|
||||
@@ -24,7 +24,7 @@ The goal of this project is to make the easiest, fastest and most painless way t
|
||||
|
||||
- Please see [Documentation](http://gogs.io/docs/intro/) for project design, known issues, and change log.
|
||||
- See [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team.
|
||||
- Try it before anything? Do it [online](http://try.gogits.org/Unknown/gogs) or go down to **Installation -> Install from binary** section!
|
||||
- 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).
|
||||
|
||||
## Features
|
||||
@@ -32,19 +32,22 @@ The goal of this project is to make the easiest, fastest and most painless way t
|
||||
- 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 etc.)
|
||||
- Multi-language support(English, Simplified Chinese, Traditional Chinese, Germany, French, Dutch, and [more](https://crowdin.com/project/gogs))
|
||||
|
||||
## System Requirements
|
||||
|
||||
@@ -57,7 +60,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,12 +68,13 @@ 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
|
||||
|
||||
|
||||
34
README_ZH.md
34
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.0 Beta
|
||||
##### 当前版本:0.5.8 Beta
|
||||
|
||||
## 开发目的
|
||||
|
||||
@@ -15,7 +15,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
|
||||
- 有关项目设计、已知问题和变更日志,请通过 [使用手册](http://gogs.io/docs/intro/) 查看。
|
||||
- 您可以到 [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。
|
||||
- 想要先睹为快?通过 [在线体验](http://try.gogits.org/Unknown/gogs) 或查看 **安装部署 -> 二进制安装** 小节。
|
||||
- 想要先睹为快?通过 [在线体验](https://try.gogs.io/Unknown/gogs) 或查看 **安装部署 -> 二进制安装** 小节。
|
||||
- 使用过程中遇到问题?尝试从 [故障排查](http://gogs.io/docs/intro/troubleshooting.md) 页面获取帮助。
|
||||
|
||||
## 功能特性
|
||||
@@ -23,19 +23,22 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
- 活动时间线
|
||||
- 支持 SSH/HTTP(S) 协议
|
||||
- 支持 SMTP/LDAP/反向代理 用户认证
|
||||
- 注册/删除/重命名 用户
|
||||
- 创建/管理/删除 组织以及团队管理功能
|
||||
- 创建/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库
|
||||
- 仓库 浏览/发布/工单管理
|
||||
- 仓库和组织级别 Web 钩子
|
||||
- 添加/删除 仓库协作者
|
||||
- Gravatar 以及缓存支持
|
||||
- 邮件服务(注册、Issue)
|
||||
- 支持反向代理子路径
|
||||
- 支持 注册/删除/重命名 用户
|
||||
- 支持 创建/管理/删除 组织以及团队管理功能
|
||||
- 支持 创建/派生/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库
|
||||
- 支持仓库 浏览/发布/工单管理
|
||||
- 支持仓库和组织级别 Web 钩子
|
||||
- 支持仓库 Git 钩子
|
||||
- 支持 添加/删除 仓库协作者
|
||||
- 支持 Gravatar 以及本地缓存
|
||||
- 支持邮件服务(注册、Issue)
|
||||
- 管理员面板
|
||||
- Slack Web 钩子集成
|
||||
- Drone CI 持续部署集成
|
||||
- 支持 MySQL、PostgreSQL 以及 SQLite3 数据库
|
||||
- 社交帐号登录(GitHub、Google、QQ、微博)
|
||||
- 多语言支持(英文、简体中文、德语等等)
|
||||
- 多语言支持(英文、简体中文、繁体中文、德语、法语、荷兰语以及 [更多]([more](https://crowdin.com/project/gogs)))
|
||||
|
||||
## 系统要求
|
||||
|
||||
@@ -48,7 +51,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,12 +59,13 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
|
||||
## 特别鸣谢
|
||||
|
||||
- [Macaron](https://github.com/Unknwon/macaron) 的路由与中间件机制。
|
||||
- [beego](http://beego.me) 模块的使用与修改。
|
||||
- 基于 [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) 提供免费的开源项目本地化支持。
|
||||
|
||||
## 贡献成员
|
||||
|
||||
|
||||
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")
|
||||
}
|
||||
34
cmd/cert_stub.go
Normal file
34
cmd/cert_stub.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// +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"
|
||||
"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.")
|
||||
}
|
||||
@@ -60,6 +60,7 @@ func runDump(ctx *cli.Context) {
|
||||
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("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)
|
||||
|
||||
18
cmd/serve.go
18
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,7 +157,7 @@ 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")
|
||||
@@ -171,7 +171,13 @@ func runServ(k *cli.Context) {
|
||||
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
|
||||
|
||||
417
cmd/web.go
417
cmd/web.go
@@ -9,11 +9,14 @@ import (
|
||||
"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"
|
||||
@@ -26,9 +29,9 @@ import (
|
||||
"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 +53,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 +61,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.2")) {
|
||||
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,38 +82,48 @@ func newMacaron() *macaron.Macaron {
|
||||
m := macaron.New()
|
||||
m.Use(macaron.Logger())
|
||||
m.Use(macaron.Recovery())
|
||||
m.Use(macaron.Static("public",
|
||||
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.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{
|
||||
@@ -120,73 +148,89 @@ 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", routers.Explore)
|
||||
m.Get("/explore", ignSignIn, routers.Explore)
|
||||
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.Get("/user/repos", middleware.ApiReqToken(), v1.ListMyRepos)
|
||||
m.Group("/repos", func() {
|
||||
m.Get("/search", v1.SearchRepos)
|
||||
m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), v1.Migrate)
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Combo("/hooks").Get(v1.ListRepoHooks).Post(bind(v1.CreateRepoHookForm{}), v1.CreateRepoHook)
|
||||
m.Patch("/hooks/:id:int", bind(v1.EditRepoHookForm{}), 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("/login/: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.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)
|
||||
@@ -194,35 +238,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)
|
||||
|
||||
@@ -234,144 +283,158 @@ 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.Get("/releases/new", repo.NewRelease)
|
||||
m.Post("/releases/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
|
||||
m.Get("/releases/edit/:tagname", repo.EditRelease)
|
||||
m.Post("/releases/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
|
||||
}, 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", 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.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", middleware.RepoAssignment(true, true, true), repo.Home)
|
||||
m.Group("/:reponame", func(r *macaron.Router) {
|
||||
r.Any("/*", repo.Http)
|
||||
})
|
||||
}, ignSignInAndCsrf)
|
||||
// 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.
|
||||
m.NotFound(routers.NotFound)
|
||||
|
||||
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)
|
||||
case setting.FCGI:
|
||||
err = fcgi.Serve(nil, m)
|
||||
default:
|
||||
log.Fatal(4, "Invalid protocol: %s", setting.Protocol)
|
||||
}
|
||||
|
||||
17
conf/app.ini
17
conf/app.ini
@@ -21,14 +21,14 @@ 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
|
||||
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
|
||||
|
||||
[database]
|
||||
; Either "mysql", "postgres" or "sqlite3", it's your choice
|
||||
@@ -163,14 +163,12 @@ 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
|
||||
; Chinese users can choose "duoshuo"
|
||||
GRAVATAR_SOURCE = gravatar
|
||||
DISABLE_GRAVATAR = false
|
||||
|
||||
[attachment]
|
||||
@@ -252,6 +250,9 @@ DRIVER =
|
||||
; Based on xorm, e.g.: root:root@localhost/gogs?charset=utf8
|
||||
CONN =
|
||||
|
||||
[git]
|
||||
MAX_GITDIFF_LINES = 10000
|
||||
|
||||
[i18n]
|
||||
LANGS = en-US,zh-CN,de-DE
|
||||
NAMES = English,简体中文,Deutsch
|
||||
LANGS = en-US,zh-CN,zh-HK,de-DE,fr-CA,nl-NL
|
||||
NAMES = English,简体中文,繁體中文,Deutsch,Français,Nederlands
|
||||
|
||||
1200
conf/locale/locale_de-DE.ini
Normal file → Executable file
1200
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,32 @@ 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
|
||||
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 +196,11 @@ 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
|
||||
|
||||
change_password = Change Password
|
||||
old_password = Current Password
|
||||
@@ -192,7 +211,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 +226,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 +248,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,43 +269,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 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
|
||||
@@ -285,11 +354,20 @@ 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
|
||||
|
||||
[org]
|
||||
org_name_holder = Organization Name
|
||||
org_name_helper = Great organization names are short and memorable.
|
||||
@@ -315,11 +393,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
|
||||
@@ -349,13 +431,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
|
||||
@@ -364,6 +449,7 @@ organizations = Organizations
|
||||
repositories = Repositories
|
||||
authentication = Authentications
|
||||
config = Configuration
|
||||
notices = System Notices
|
||||
monitor = Monitoring
|
||||
prev = Prev.
|
||||
next = Next
|
||||
@@ -376,7 +462,11 @@ 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.server_uptime = Server Uptime
|
||||
dashboard.current_goroutine = Current Goroutines
|
||||
dashboard.current_memory_usage = Current Memory Usage
|
||||
@@ -418,13 +508,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
|
||||
@@ -462,9 +554,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
|
||||
@@ -520,8 +614,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
|
||||
@@ -539,11 +631,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
|
||||
|
||||
683
conf/locale/locale_fr-CA.ini
Executable file
683
conf/locale/locale_fr-CA.ini
Executable file
@@ -0,0 +1,683 @@
|
||||
app_desc=Un service de Git sans prise de tête auto-hébergé codé en Go
|
||||
|
||||
home=Accueil
|
||||
dashboard=Tableau de bord
|
||||
explore=Explorer
|
||||
help=Aide
|
||||
sign_in=Connexion
|
||||
social_sign_in=Connexion avec un réseau social : 2e étape <small>associer un compte</small>
|
||||
sign_out=Déconnexion
|
||||
sign_up=Créer un compte
|
||||
register=S'enregistrer
|
||||
website=Site Web
|
||||
version=Version
|
||||
page=Page
|
||||
template=Modèle
|
||||
language=Langue
|
||||
|
||||
username=Nom d'utilisateur
|
||||
email=E-mail
|
||||
password=Mot de passe
|
||||
re_type=Confirmez
|
||||
captcha=Captcha
|
||||
|
||||
repository=Référentiel
|
||||
organization=Organisation
|
||||
mirror=Miroir
|
||||
new_repo=Nouveau Référentiel
|
||||
new_migrate=Nouvelle Migration
|
||||
new_fork=Nouveau référentiel de fourche
|
||||
new_org=Nouvelle Organisation
|
||||
manage_org=Gérer les Organisations
|
||||
admin_panel=Administration
|
||||
account_settings=Paramètres du Compte
|
||||
settings=Paramètres
|
||||
|
||||
news_feed=Flux d'actualités
|
||||
pull_requests=Extraire les Requêtes
|
||||
issues=Problèmes
|
||||
|
||||
cancel=Annuler
|
||||
|
||||
[install]
|
||||
install=Installation
|
||||
title=Instructions de Première Installation
|
||||
requite_db_desc=Gogs nécessite MySQL, PostgreSQL ou SQLite3.
|
||||
db_type=Type de Base de Données
|
||||
host=Hôte
|
||||
user=Utilisateur
|
||||
password=Mot De Passe
|
||||
db_name=Nom de la Base de Données
|
||||
db_helper=Veuillez utiliser le moteur INNODB avec le jeu de caractères utf8_general_ci pour MySQL.
|
||||
ssl_mode=Mode SSL
|
||||
path=Chemin
|
||||
sqlite_helper=Emplacement du fichier de la base de données SQLite3.
|
||||
general_title=Paramètres Généraux de Gogs
|
||||
repo_path=Emplacement Racine du Référentiel
|
||||
repo_path_helper=Tous les Référentiels Git distants seront sauvegardés ici.
|
||||
run_user=Entrer un Utilisateur
|
||||
run_user_helper=L'utilisateur doit avoir accès à la Racine du Référentiel et éxécuter Gogs.
|
||||
domain=Domaine
|
||||
domain_helper=Cela affecte les doublons d'URL SSH.
|
||||
app_url=URL de l'Application
|
||||
app_url_helper=Cela affecte les doublons d'URL HTTP/HTTPS et le contenu d'e-mail.
|
||||
email_title=Paramètres du Service de Messagerie (Facultatif)
|
||||
smtp_host=Hôte SMTP
|
||||
mailer_user=E-mail de l'Expéditeur
|
||||
mailer_password=Mot de Passe de l'Expéditeur
|
||||
notify_title=Paramètre des Notifications (Facultatif)
|
||||
register_confirm=Activer la Confirmation d'Enregistrement
|
||||
mail_notify=Activer la Notification des Mails reçus
|
||||
admin_title=Paramètres du Compte Administrateur
|
||||
admin_name=Nom d'Utilisateur
|
||||
admin_password=Mot de Passe
|
||||
confirm_password=Confirmez le Mot de Passe
|
||||
admin_email=E-mail
|
||||
install_gogs=Installer Gogs
|
||||
test_git_failed=Tentative de commande "git" échouée : %v
|
||||
sqlite3_not_available=Votre version publiée ne prend pas en charge SQLite3. Veuillez télécharger la version binaire officielle à cette adresse %s.
|
||||
invalid_db_setting=Paramètres de base de données incorrects : %v
|
||||
invalid_repo_path=Chemin Racine du Référentiel invalide : %v
|
||||
run_user_not_match=L'utilisateur entré n'est pas l'utilisateur actuel : %s -> %s
|
||||
save_config_failed=Sauvegarde de la configuration échouée : %v
|
||||
invalid_admin_setting=Paramètres du compte administrateur invalides : %v
|
||||
install_success=Bienvenue ! Nous sommes heureux que vous ayez choisi Gogs, amusez-vous et prenez soin de vous.
|
||||
|
||||
[home]
|
||||
uname_holder=Nom d'Utilisateur ou E-mail
|
||||
password_holder=Mot de Passe
|
||||
switch_dashboard_context=Basculer le Contexte du Tableau de Bord
|
||||
my_repos=Mes Référentiels
|
||||
collaborative_repos=Référentiels collaboratifs
|
||||
my_orgs=Mes Organisations
|
||||
my_mirrors=Mes Miroirs
|
||||
|
||||
[explore]
|
||||
repos=Référentiels
|
||||
|
||||
[auth]
|
||||
create_new_account=Créer un Nouveau Compte
|
||||
register_hepler_msg=Déjà enregistré ? Connectez-vous !
|
||||
social_register_hepler_msg=Possesseur d'un compte ? Associez-le !
|
||||
disable_register_prompt=Désolé, les enregistrements ont été désactivés. Veuillez contacter l'administrateur du site.
|
||||
disable_register_mail=Désolé, la Confirmation par Mail des Enregistrements a été désactivée.
|
||||
remember_me=Se souvenir de Moi
|
||||
forgot_password=Mot de Passe oublié
|
||||
forget_password=Mot de Passe oublié ?
|
||||
sign_up_now=Pas de compte ? Créer maintenant.
|
||||
confirmation_mail_sent_prompt=Un nouveau mail de confirmation à été envoyé à <b>%s</b>. Veuillez vérifier votre boîte de réception dans un délai de %d heures pour compléter votre enregistrement.
|
||||
sign_in_email=Connexion avec l'E-mail
|
||||
active_your_account=Activer votre Compte
|
||||
resent_limit_prompt=Désolé, vos tentatives d'activation sont trop fréquentes. Veuillez réessayer dans 3 minutes.
|
||||
has_unconfirmed_mail=Bonjour %s, votre adresse e-mail n'a pas été confirmée (<b>%s</b>). Si vous n'avez reçu aucun mail de confirmation ou souhaitez renouveler l'envoi, appuyez sur le bouton ci-dessous.
|
||||
resend_mail=Appuyez ici pour renvoyer un mail de confirmation
|
||||
email_not_associate=Cette adresse e-mail n'est associée à aucun compte.
|
||||
send_reset_mail=Appuyez ici pour (r)envoyer le mail de réinitialisation du mot de passe
|
||||
reset_password=Réinitialiser le Mot de Passe
|
||||
invalid_code=Désolé, code de confirmation invalide ou expiré.
|
||||
reset_password_helper=Appuyez ici pour réinitialiser votre mot de passe
|
||||
password_too_short=Le mot de passe doit contenir 6 caractères minimum.
|
||||
|
||||
[form]
|
||||
UserName=Nom d'Utilisateur
|
||||
RepoName=Nom du Référentiel
|
||||
Email=Adresse E-mail
|
||||
Password=Mot de Passe
|
||||
Retype=Confirmez le Mot de Passe
|
||||
SSHTitle=Nom de la clé SSH
|
||||
HttpsUrl=URL HTTPS
|
||||
PayloadUrl=URL des Données Utiles
|
||||
TeamName=Nom d'équipe
|
||||
AuthName=Nom d'autorisation
|
||||
AdminEmail=E-mail d'admin
|
||||
|
||||
require_error=` Ne peut être vide `
|
||||
alpha_dash_error=` doivent être des caractères alpha, numeriques ou console (-_) valides `
|
||||
alpha_dash_dot_error=` doivent être des caractères alpha, numeriques, console (-_) valides ou des points `
|
||||
min_size_error=` %s caractères minimum `
|
||||
max_size_error=` %s caractères maximum `
|
||||
email_error=` adresse e-mail invalide `
|
||||
url_error=` URL invalide `
|
||||
unknown_error=Erreur inconnue :
|
||||
captcha_incorrect=Le Captcha ne correspond pas.
|
||||
password_not_match=Le mot de passe et la confirmation de mot de passe ne correspondent pas.
|
||||
|
||||
username_been_taken=Nom d'utilisateur déjà pris.
|
||||
repo_name_been_taken=Nom de Référentiel déjà pris.
|
||||
org_name_been_taken=Nom d'organisation déjà pris.
|
||||
team_name_been_taken=Nom d'équipe déjà pris.
|
||||
email_been_used=Adresse e-mail déjà utilisée.
|
||||
ssh_key_been_used=Le nom de la clé publique a déjà servi.
|
||||
illegal_username=Le nom d'utilisateur contient des caractères interdits.
|
||||
illegal_repo_name=Le nom du Référentiel contient des caractères interdits.
|
||||
illegal_org_name=Le nom de l'organisation contient des caractères interdits.
|
||||
illegal_team_name=Le nom de l'équipe contient des caractères interdits.
|
||||
username_password_incorrect=Nom d'utilisateur ou mot de passe incorrect.
|
||||
enterred_invalid_repo_name=Veuillez vérifier que le nom saisi du Référentiel soit correct.
|
||||
enterred_invalid_owner_name=Veuillez vérifier que le nom du propriétaire saisi soit correct.
|
||||
enterred_invalid_password=Veuillez vérifier que le mot de passe saisi soit correct.
|
||||
user_not_exist=Cet utilisateur n'existe pas.
|
||||
last_org_owner=L'utilisateur à exclure est le dernier membre de l'équipe propriétaire. Il doit y avoir un autre propriétaire.
|
||||
|
||||
invalid_ssh_key=Désolé, impossible de valider votre clé SSH : %s
|
||||
unable_verify_ssh_key=Gogs n'a pu vérifier la validité de votre clé SSH, même si nous partons du principe qu'elle le soit. Cela-dit, veuillez vous en assurer.
|
||||
auth_failed=Échec d'authentification : %s
|
||||
|
||||
still_own_repo=Votre compte comporte toujours des propriétés de Référentiel. Vous devez d'abord les supprimer ou les transférer.
|
||||
still_has_org=Votre compte a toujours membres de l'organisation, vous avez à gauche ou supprimez tout d'abord.
|
||||
org_still_own_repo=Cette organisation comporte toujours des propriétés de Référentiel. Vous devez d'abord les supprimer ou les transférer.
|
||||
|
||||
still_own_user=Cette authentification a déjà servi à d'autres utilisateurs. Veuillez les déplacer puis supprimez à nouveau.
|
||||
|
||||
target_branch_not_exist=Branche cible n'existe pas
|
||||
|
||||
[user]
|
||||
change_avatar=Changez d'avatar via gravatar.com
|
||||
join_on=Adhéré le
|
||||
repositories=Référentiels
|
||||
activity=Activités publiques
|
||||
followers=Abonnés
|
||||
starred=Votés
|
||||
following=Abonnements
|
||||
|
||||
[settings]
|
||||
profile=Profil
|
||||
password=Mot de Passe
|
||||
ssh_keys=Clés SSH
|
||||
social=Réseaux Sociaux
|
||||
applications=Applications
|
||||
orgs=Organisations
|
||||
delete=Supprimer le Compte
|
||||
uid=ID d'Utilisateur
|
||||
|
||||
public_profile=Profil Public
|
||||
profile_desc=Votre adresse e-mail est publique et sera utilisée pour les notifications relatives au compte, ainsi que pour toute opération Web effectuée via le site.
|
||||
full_name=Non Complet
|
||||
website=Site Web
|
||||
location=Localisation
|
||||
update_profile=Valider les modifications
|
||||
update_profile_success=Profil mis à jour avec succès.
|
||||
change_username=Non d'utilisateur modifié
|
||||
change_username_desc=Nom d'utilisateur modifié. Cela affecte tous les liens relatifs à votre compte. Continuer ?
|
||||
continue=Continuer
|
||||
cancel=Annuler
|
||||
|
||||
change_password=Modifier le Mot de Passe
|
||||
old_password=Mot de Passe actuel
|
||||
new_password=Nouveau Mot de Passe
|
||||
password_incorrect=Mot de passe actuel incorrect.
|
||||
change_password_success=Mot de passe modifié avec succès. Vous pouvez à présent vous connecter avec le nouveau mot de passe.
|
||||
|
||||
manage_ssh_keys=Gérer les clés SSH
|
||||
add_key=Ajouter une Clé
|
||||
ssh_desc=Ceci est une liste des clés SSH associées à votre compte. Supprimez celles que vous ne reconnaissez pas.
|
||||
ssh_helper=<strong>Besoin d'aide?</strong> Consultez notre guide pour <a href="%s"> générer des clés SSH</a> ou résoudre les <a href="%s"> problèmes courants de SSH</a>.
|
||||
add_new_key=Ajouter une Clé SSH
|
||||
key_name=Nom de la Clé
|
||||
key_content=Contenu
|
||||
add_key_success=Nouvelle Clé SSH ajoutée !
|
||||
delete_key=Supprimer
|
||||
add_on=Ajouté le
|
||||
last_used=Dernière utilisation le
|
||||
no_activity=Aucune activité récente
|
||||
|
||||
manage_social=Gérer les réseaux sociaux associés
|
||||
social_desc=Ceci est la liste des comptes de réseaux sociaux associés. Supprimez ceux que vous ne reconnaissez pas.
|
||||
unbind=Dissocier
|
||||
unbind_success=Compte de réseau social dissocié.
|
||||
|
||||
manage_access_token=Gérer les jetons d'accès personnels
|
||||
generate_new_token=Générer le nouveau jeton
|
||||
tokens_desc=Jetons, que vous avez généré qui peuvent être utilisés pour accéder à l'API Gogs.
|
||||
new_token_desc=Comme pour l'instant, chaque jeton aura un accès complet à votre compte.
|
||||
token_name=Nom du jeton
|
||||
generate_token=Générer le jeton
|
||||
generate_token_succees=Nouveau jeton d'accès a été généré avec succès ! Assurez-vous de copier votre nouveau jeton d'accès personnel maintenant. Vous ne serez pas en mesure de le revoir !
|
||||
delete_token=Supprimer
|
||||
delete_token_success=Jeton d'accès personnelle a été supprimée avec succès ! N'oubliez pas de mettre à jour vos applications aussi bien.
|
||||
|
||||
delete_account=Supprimer le Compte
|
||||
delete_prompt=Votre compte sera supprimé définitivement et cette opération est <strong>IRRÉVERSIBLE</strong> !
|
||||
confirm_delete_account=Confirmer la suppression
|
||||
delete_account_title=Suppression de compte
|
||||
delete_account_desc=Ce compte sera supprimé définitivement. Voulez-vous continuer ?
|
||||
|
||||
[repo]
|
||||
owner=Propriétaire
|
||||
repo_name=Nom du Référentiel
|
||||
repo_name_helper=Idéalement, le nom d'un Référentiel devrait être court, mémorable et <strong>unique</strong>.
|
||||
visibility=Visibilité
|
||||
visiblity_helper=Ce Référentiel est <span class="label label-red label-radius">Privé</span>
|
||||
fork_repo=Référentiel de la fourche
|
||||
fork_from=Fourche de
|
||||
fork_visiblity_helper=Référentiel Aristide ne peut pas changer sa visiblité
|
||||
repo_desc=Description
|
||||
repo_lang=Langue
|
||||
repo_lang_helper=Sélectionner un fichier .gitignore
|
||||
license=Licence
|
||||
license_helper=Sélectionner un fichier de licence
|
||||
init_readme=Initialiser ce Référentiel avec un README.md
|
||||
create_repo=Créer un Référentiel
|
||||
default_branch=Branche par défaut
|
||||
mirror_interval=Intervalle du miroir (heure)
|
||||
goget_meta=Méta Go-Get
|
||||
goget_meta_helper=Ce Référentiel sera <span class="label label-blue label-radius">Go-Getable</span>
|
||||
|
||||
need_auth=Nécessite une Autorisation
|
||||
migrate_type=Type de Migration
|
||||
migrate_type_helper=Ce Référentiel sera un <span class="label label-blue label-radius">Miroir</span>
|
||||
migrate_repo=Migrer le Référentiel
|
||||
|
||||
copy_link=Copier
|
||||
click_to_copy=Copier dans le presse-papier
|
||||
copied=Copié
|
||||
clone_helper=Besoin d'aide pour le clonage ? Visitez <a target="_blank" href="%s"> l'aider</a> !
|
||||
unwatch=Ne plus suivre
|
||||
watch=Suivre
|
||||
unstar=Retirer le vote
|
||||
star=Voter
|
||||
fork=Embranchement
|
||||
|
||||
no_desc=Aucune description
|
||||
quick_guide=Introduction rapide
|
||||
clone_this_repo=Dupliquer ce Référentiel
|
||||
create_new_repo_command=Créer un nouveau Référentiel en ligne de commande
|
||||
push_exist_repo=Soumettre un Référentiel existant par ligne de commande
|
||||
|
||||
branch=Branche
|
||||
tree=Aborescence
|
||||
branch_and_tags=Branches & Tags
|
||||
branches=Branches
|
||||
tags=Tags
|
||||
issues=Problèmes
|
||||
commits=Commissions
|
||||
releases=Publications
|
||||
file_raw=Raw
|
||||
file_history=Historique
|
||||
file_view_raw=Voir le Raw
|
||||
|
||||
commits.commits=Commissions
|
||||
commits.search=Rechercher des commissions
|
||||
commits.find=Trouver
|
||||
commits.author=Auteur
|
||||
commits.message=Message
|
||||
commits.date=Date
|
||||
commits.older=Précédemment
|
||||
commits.newer=Récemment
|
||||
|
||||
settings=Paramètres
|
||||
settings.options=Options
|
||||
settings.collaboration=Collaboration
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Git Hooks
|
||||
settings.deploy_keys=Clés de déploiement
|
||||
settings.basic_settings=Paramètres de base
|
||||
settings.danger_zone=Zone de danger
|
||||
settings.site=Site officiel
|
||||
settings.update_settings=Valider
|
||||
settings.change_reponame=Référentiel renommé
|
||||
settings.change_reponame_desc=Le Référentiel a été renommé. Cela affecte tous les liens relatifs à ce Référentiel. Continuer ?
|
||||
settings.transfer=Transférer les propriétés
|
||||
settings.transfer_desc=Transfère ce Référentiel à un autre utilisateur ou organisation dont vous possédez des droits d'administrateur.
|
||||
settings.new_owner_has_same_repo=Le nouveau propriétaire a déjà un Référentiel nommé ainsi.
|
||||
settings.delete=Supprimer ce Référentiel
|
||||
settings.delete_desc=Attention, action irréversible. Soyez sûre de vous.
|
||||
settings.transfer_notices=<p>- Vous perdrez l'accès si le nouveau propriétaire est un utilisateur individuel.</p><p>- Vous garderez l'accès si le nouveau propriétaire est une organisation et que vous en faites partie.</p>
|
||||
settings.update_settings_success=Options mises à jour avec succès.
|
||||
settings.transfer_owner=Nouveau propriétaire
|
||||
settings.make_transfer=Transférer
|
||||
settings.transfer_succeed=Propriétés transférées avec succès.
|
||||
settings.confirm_delete=Confirmer la suppression
|
||||
settings.add_collaborator=Ajouter un collaborateur
|
||||
settings.add_collaborator_success=Nouveau collaborateur ajouté.
|
||||
settings.remove_collaborator_success=Collaborateur supprimé.
|
||||
settings.user_is_org_member=Cet utilisateur ne peut pas être ajouté en tant que collaborateur car il fait partie d'une organisation.
|
||||
settings.add_webhook=Ajouter un Webhook
|
||||
settings.hooks_desc=Webhooks aux services externes être notifié lorsque certains événements se produisent sur Gogs. Lorsque les événements spécifiés se produisent, nous vous enverrons une demande POST à chacun des URL que vous fournissez. Apprenez-en davantage dans notre <a target="_blank" href="%s"> Webhooks Guide</a>.
|
||||
settings.githooks_desc=Les Hooks Git sont alimentés par Git lui même. Les Hooks compatibles sont modifiables dans la liste ci-dessous pour effectuer des opérations personnalisées.
|
||||
settings.githook_edit_desc=Si un Hook est inactif, un exemple de contenu vous sera proposé. Un contenu laissé vide signifie un Hook inactif.
|
||||
settings.githook_name=Nom du Hook
|
||||
settings.githook_content=Contenu du Hook
|
||||
settings.update_githook=Mettre le Hook à jour
|
||||
settings.remove_hook_success=Webhook supprimé.
|
||||
settings.add_webhook_desc=Nous vous enverrons une demande <code>POST</code> à l'URL ci-dessous avec les détails de tous les événements souscrites. Vous pouvez également spécifier quel format de données vous souhaitez recevoir (JSON, <code>x-www-formulaires-urlencoded</code>, <em>etc.</em>). Plus d'informations se trouvent dans le <a target="_blank" href="%s"> Guide de le Webhooks</a>.
|
||||
settings.payload_url=URL des Données Utiles
|
||||
settings.content_type=Type de contenu
|
||||
settings.secret=Confidentiel
|
||||
settings.event_desc=Quel évènement ce Webhook doit-il déclencher ?
|
||||
settings.event_push_only=Uniquement les <code>push</code> (soumissions).
|
||||
settings.active=Actif
|
||||
settings.active_helper=Les détails seront délivrés lorsque ce Hook sera déclenché.
|
||||
settings.add_hook_success=Nouveau Webhook ajouté.
|
||||
settings.update_webhook=Mettre le Webhook à jour
|
||||
settings.update_hook_success=Webhook mis à jour.
|
||||
settings.delete_webhook=Supprimer le Webhook
|
||||
settings.recent_deliveries=Livraisons récentes
|
||||
settings.hook_type=Type de Hook
|
||||
settings.add_slack_hook_desc=Ajouter <a href="%s"> Slack</a> intégration dans votre référentiel.
|
||||
settings.slack_token=Jeton
|
||||
settings.slack_domain=Domaine
|
||||
settings.slack_channel=Canal
|
||||
|
||||
diff.browse_source=Parcourir la Source
|
||||
diff.parent=Parent
|
||||
diff.commit=Commettre
|
||||
diff.data_not_available=Données Diff indisponibles.
|
||||
diff.show_diff_stats=Afficher les stats Diff
|
||||
diff.stats_desc=<strong> %d fichiers modifiés</strong> avec <strong>%d ajouts</strong> et <strong>%d suppressions</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Voir le fichier
|
||||
|
||||
[org]
|
||||
org_name_holder=Nom d'organisation
|
||||
org_name_helper=Idéalement, un nom d'organisation devrait être court et mémorable.
|
||||
org_email_helper=L'e-mail de l'organisation recevra toutes les notifications et confirmations.
|
||||
create_org=Créer une organisation
|
||||
repo_updated=Mis à jour
|
||||
people=Contacts
|
||||
invite_someone=Inviter quelqu'un
|
||||
teams=Équipes
|
||||
lower_members=Membres
|
||||
lower_repositories=Référentiels
|
||||
create_new_team=Créer une Nouvelle Équipe
|
||||
org_desc=Description
|
||||
team_name=Nom d'Équipe
|
||||
team_desc=Description
|
||||
team_name_helper=Ce nom sera utilisé pour mentionner l'équipe dans les conversations.
|
||||
team_desc_helper=Présentation de l'équipe
|
||||
team_permission_desc=Quel niveau d'accès cette équipe devrait-elle posséder ?
|
||||
|
||||
settings=Paramètres
|
||||
settings.options=Options
|
||||
settings.full_name=Non Complet
|
||||
settings.website=Site Web
|
||||
settings.location=Localisation
|
||||
settings.update_settings=Valider
|
||||
settings.change_orgname=Organisation renommée
|
||||
settings.change_orgname_desc=L'Organisation a été renommée. Cela affecte tous les liens relatifs à cette organisation. Continuer ?
|
||||
settings.update_setting_success=Paramètres d'organisation modifiés avec succès.
|
||||
settings.delete=Supprimer l'organisation
|
||||
settings.delete_account=Supprimer cette organisation
|
||||
settings.delete_prompt=Cela supprimera cette organisation définitivement. Cette opération est <strong>IRRÉVERSIBLE</strong> !
|
||||
settings.confirm_delete_account=Confirmez la suppression
|
||||
settings.delete_org_title=Suppression d'organisation
|
||||
settings.delete_org_desc=Cette organisation sera définitivement supprimée. Continuer ?
|
||||
settings.hooks_desc=Ajoute des Webhooks qui seront activés pour <strong>tous les Référentiels</strong> de cette organisation.
|
||||
|
||||
members.public=Public
|
||||
members.public_helper=Rendre privé
|
||||
members.private=Privé
|
||||
members.private_helper=Rendre public
|
||||
members.owner=Propriétaire
|
||||
members.member=Membre
|
||||
members.conceal=Masquer
|
||||
members.remove=Exclure
|
||||
members.leave=Quitter
|
||||
members.invite_desc=Tapez un nom d'utilisateur pour inviter un nouveau membre chez %s :
|
||||
members.invite_now=Lancer l'invitation
|
||||
|
||||
teams.join=Rejoindre
|
||||
teams.leave=Quitter
|
||||
teams.read_access=Accès en Lecture
|
||||
teams.read_access_helper=Cette équipe aura la possibilité de voir et dupliquer ses Référentiels.
|
||||
teams.write_access=Accès en Écriture
|
||||
teams.write_access_helper=Cette équipe possèdera aussi bien des droits de lecture que d'écriture sur ses Référentiels.
|
||||
teams.admin_access=Accès Administrateur
|
||||
teams.admin_access_helper=Cette équipe possèdera des droits de lecture, d'écriture, ainsi que le pouvoir d'ajouter des collaborateurs.
|
||||
teams.no_desc=Aucune description
|
||||
teams.settings=Paramètres
|
||||
teams.owners_permission_desc=Les propriétaires possèdent <strong>les droits d'administrateur</strong> et disposent d'un accès complet à <strong>tous les Référentiels</strong> de l'organisation.
|
||||
teams.members=Membres de L'Équipe
|
||||
teams.update_settings=Valider
|
||||
teams.delete_team=Supprimer cette Équipe
|
||||
teams.add_team_member=Ajouter un Membre
|
||||
teams.delete_team_title=Suppression de l'équipe
|
||||
teams.delete_team_desc=Cette équipe sera supprimée. Les membres pourraient perdre leurs accès à certains Référentiels.
|
||||
teams.delete_team_success=Équipe supprimée avec succès.
|
||||
teams.read_permission_desc=Cette équipe permet l'accès en <strong>lecture</strong> : les membres peuvent voir et dupliquer ses Référentiels.
|
||||
teams.write_permission_desc=Cette équipe permet l'accès en <strong>écriture</strong> : les membres peuvent participer à ses Référentiels.
|
||||
teams.admin_permission_desc=Cette équipe permet l'accès en <strong>administrateur</strong> : les membres peuvent voir, participer et ajouter des collaborateurs à ses Référentiels.
|
||||
teams.repositories=Référentiels de l'Équipe
|
||||
teams.add_team_repository=Ajouter un Référentiel à l'Équipe
|
||||
teams.remove_repo=Supprimer
|
||||
teams.add_nonexistent_repo=Référentiel inexistant, veuillez d'abord le créer.
|
||||
|
||||
[admin]
|
||||
dashboard=Tableau de bord
|
||||
users=Utilisateurs
|
||||
organizations=Organisations
|
||||
repositories=Référentiels
|
||||
authentication=Authentifications
|
||||
config=Configuration
|
||||
notices=Notes Systèmes
|
||||
monitor=Supervision
|
||||
prev=Préc.
|
||||
next=Suiv.
|
||||
|
||||
dashboard.statistic=Statistiques
|
||||
dashboard.operations=Opérations
|
||||
dashboard.system_status=État du Moniteur Système
|
||||
dashboard.statistic_info=La base de données Gogs contient <b>%d</b> utilisateurs, <b>%d</b> organisations, <b>%d</b> clés publiques, <b>%d</b> Référentiels, <b>%d</b> suivis, <b>%d</b> votes, <b>%d</b> actions, <b>%d</b> accès, <b>%d</b> problèmes, <b>%d</b> commentaires, <b>%d</b> comptes de réseaux sociaux, <b>%d</b> abonnements, <b>%d</b> miroirs, <b>%d</b> publications, <b>%d</b> connexions d'origine, <b>%d</b> webhooks, <b>%d</b> milestones, <b>%d</b> labels, <b>%d</b> tâches hook, <b>%d</b> équipes, <b>%d</b> tâches de mise à jour, <b>%d</b> fichiers.
|
||||
dashboard.operation_name=Nom de l'Opération
|
||||
dashboard.operation_switch=Basculer
|
||||
dashboard.operation_run=Exécuter
|
||||
dashboard.clean_unbind_oauth=Nettoyer les associations OAuthes
|
||||
dashboard.clean_unbind_oauth_success=Tous unbind OAuthes ont été supprimés avec succès.
|
||||
dashboard.delete_inactivate_accounts=Supprimer tous les comptes inactifs
|
||||
dashboard.delete_inactivate_accounts_success=Inactivent tous les comptes ont été supprimés avec succès.
|
||||
dashboard.delete_repo_archives=Supprimer toutes les archives de référentiels
|
||||
dashboard.delete_repo_archives_success=Toutes les archives de référentiels ont été supprimés avec succès.
|
||||
dashboard.server_uptime=Durée de Marche Serveur
|
||||
dashboard.current_goroutine=Goroutines actuelles
|
||||
dashboard.current_memory_usage=Utilisation Mémoire actuelle
|
||||
dashboard.total_memory_allocated=Mémoire totale allouée
|
||||
dashboard.memory_obtained=Mémoire obtenue
|
||||
dashboard.pointer_lookup_times=Nombre de Consultations Pointeur
|
||||
dashboard.memory_allocate_times=Nombre d'Allocation Mémoire
|
||||
dashboard.memory_free_times=Nombre de Libération Mémoire
|
||||
dashboard.current_heap_usage=Utilisation Tas (Heap)
|
||||
dashboard.heap_memory_obtained=Mémoire Tas (Heap) obtenue
|
||||
dashboard.heap_memory_idle=Mémoire Tas (Heap) au Repos
|
||||
dashboard.heap_memory_in_use=Utilisation Mémoire Tas (Heap)
|
||||
dashboard.heap_memory_released=Mémoire Tas (Heap) libérée
|
||||
dashboard.heap_objects=Objets Tas (Heap)
|
||||
dashboard.bootstrap_stack_usage=Utilisation Pile Bootstrap
|
||||
dashboard.stack_memory_obtained=Mémoire Pile obtenue
|
||||
dashboard.mspan_structures_usage=Utilisation des Structures MSpan
|
||||
dashboard.mspan_structures_obtained=Structures MSpan obtenues
|
||||
dashboard.mcache_structures_usage=Utilisation des Structures MCache
|
||||
dashboard.mcache_structures_obtained=Structures MCache obtenues
|
||||
dashboard.profiling_bucket_hash_table_obtained=Profilage de Seau de Table de Hashage obtenu
|
||||
dashboard.gc_metadata_obtained=Métadonnées GC obtenues
|
||||
dashboard.other_system_allocation_obtained=Allocation de l'autre Système obtenue
|
||||
dashboard.next_gc_recycle=Traitement GC suivant
|
||||
dashboard.last_gc_time=Depuis le dernier GC
|
||||
dashboard.total_gc_time=Pause GC totale
|
||||
dashboard.total_gc_pause=Pause GC
|
||||
dashboard.last_gc_pause=Dernière Pause GC
|
||||
dashboard.gc_times=Nombres de GC
|
||||
|
||||
users.user_manage_panel=Gestion des Utilisateurs
|
||||
users.new_account=Créer un nouveau compte
|
||||
users.name=Nom
|
||||
users.activated=Activés
|
||||
users.admin=Administrateur
|
||||
users.repos=Repos
|
||||
users.created=Créés
|
||||
users.edit=Éditer
|
||||
users.auth_source=Source d'Autorisation
|
||||
users.local=Locales
|
||||
users.auth_login_name=Autorisation de connexion
|
||||
users.update_profile_success=Profil mis à jour avec succès.
|
||||
users.edit_account=Modifier le Compte
|
||||
users.is_activated=Ce compte est activé
|
||||
users.is_admin=Ce compte possède un niveau d'accès administrateur
|
||||
users.allow_git_hook=Ce compte dispose des autorisations pour créer des crochets de Git
|
||||
users.update_profile=Mettre le profil à jour
|
||||
users.delete_account=Supprimer ce Compte
|
||||
users.still_own_repo=Ce compte comporte toujours des propriétés de Référentiel. Vous devez d'abord les supprimer ou les transférer.
|
||||
users.still_has_org=Ce compte a toujours membres de l'organisation, vous avez à gauche ou supprimez tout d'abord.
|
||||
|
||||
orgs.org_manage_panel=Gestion des Organisations
|
||||
orgs.name=Nom
|
||||
orgs.teams=Équipes
|
||||
orgs.members=Membres
|
||||
|
||||
repos.repo_manage_panel=Gestion des Référentiels
|
||||
repos.owner=Propriétaire
|
||||
repos.name=Nom
|
||||
repos.private=Privé
|
||||
repos.watches=Suivi par
|
||||
repos.stars=Votes
|
||||
repos.issues=Problèmes
|
||||
|
||||
auths.auth_manage_panel=Gestion des Autorisations
|
||||
auths.new=Ajouter une Nouvelle Source d'Autorisation
|
||||
auths.name=Nom
|
||||
auths.type=Type
|
||||
auths.enabled=Activé
|
||||
auths.updated=Mis à jour
|
||||
auths.auth_type=Type d'Autorisation
|
||||
auths.auth_name=Nom d'Autorisation
|
||||
auths.domain=Domaine
|
||||
auths.host=Hôte
|
||||
auths.port=Port
|
||||
auths.base_dn=Base DN (Nom Distingué)
|
||||
auths.attributes=Rechercher les Attributs
|
||||
auths.filter=Filtre de recherche
|
||||
auths.ms_ad_sa=Ms Ad SA
|
||||
auths.smtp_auth=Type d'Autorisation SMTP
|
||||
auths.smtphost=Hôte SMTP
|
||||
auths.smtpport=Port SMTP
|
||||
auths.enable_tls=Activer le Chiffrement TLS
|
||||
auths.enable_auto_register=Connexion Automatique
|
||||
auths.tips=Conseils
|
||||
auths.edit=Modifier les Paramètres d'Autorisation
|
||||
auths.activated=Authentification activée
|
||||
auths.update_success=Paramètres d'autorisation mis à jour avec succès.
|
||||
auths.update=Mettre les Paramètres d'Autorisation à jour
|
||||
auths.delete=Supprimer cette Autorisation
|
||||
auths.delete_auth_title=Suppression d'Autorisation
|
||||
auths.delete_auth_desc=Cette autorisation sera supprimée. Continuer ?
|
||||
|
||||
config.server_config=Configuration du Serveur
|
||||
config.app_name=Nom de l'Application
|
||||
config.app_ver=Version de l'Application
|
||||
config.app_url=URL de l'Application
|
||||
config.domain=Domaine
|
||||
config.offline_mode=Mode hors-ligne
|
||||
config.disable_router_log=Désactiver la Journalisation du Routeur
|
||||
config.run_user=Entrer un Utilisateur
|
||||
config.run_mode=Mode d'Éxécution
|
||||
config.repo_root_path=Emplacement Racine du Référentiel
|
||||
config.static_file_root_path=Emplacement Racine du Fichier Statique
|
||||
config.log_file_root_path=Emplacement Racine du Fichier Journal
|
||||
config.script_type=Type de Script
|
||||
config.reverse_auth_user=Annuler l'Authentification de l'Utilisateur
|
||||
config.db_config=Configuration de la Base de Données
|
||||
config.db_type=Type
|
||||
config.db_host=Hôte
|
||||
config.db_name=Nom
|
||||
config.db_user=Utilisateur
|
||||
config.db_ssl_mode=Mode SSL
|
||||
config.db_ssl_mode_helper=("postgres" uniquement)
|
||||
config.db_path=Emplacement
|
||||
config.db_path_helper=("sqlite3" uniquement)
|
||||
config.service_config=Configuration du Service
|
||||
config.register_email_confirm=E-mail de Confirmation d'Enregistrement
|
||||
config.disable_register=Désactiver l'Enregistrement
|
||||
config.require_sign_in_view=Connexion Obligatoire pour Visualiser
|
||||
config.mail_notify=Mailer les Notifications
|
||||
config.enable_cache_avatar=Activer le Cache d'Avatar
|
||||
config.active_code_lives=Limites de Code Actif
|
||||
config.reset_password_code_lives=Réinitialiser le Mot De Passe des Limites de Code
|
||||
config.webhook_config=Configuration Webhook
|
||||
config.task_interval=Intervalles de Tâches
|
||||
config.deliver_timeout=Expiration d'Envoi
|
||||
config.mailer_config=Configuration du Maileur
|
||||
config.mailer_enabled=Activé
|
||||
config.mailer_name=Nom
|
||||
config.mailer_host=Hôte
|
||||
config.mailer_user=Utilisateur
|
||||
config.oauth_config=Configuration OAuth
|
||||
config.oauth_enabled=Activé
|
||||
config.cache_config=Configuration du Cache
|
||||
config.cache_adapter=Adaptateur du Cache
|
||||
config.cache_interval=Intervals du Cache
|
||||
config.cache_conn=Liaison du Cache
|
||||
config.session_config=Configuration de Session
|
||||
config.session_provider=Fournisseur de Session
|
||||
config.provider_config=Configurer le Fournisseur
|
||||
config.cookie_name=Nom du Cookie
|
||||
config.enable_set_cookie=Activer les Cookies
|
||||
config.gc_interval_time=Intervals GC
|
||||
config.session_life_time=Durée de Session
|
||||
config.https_only=HTTPS uniquement
|
||||
config.cookie_life_time=Expiration du Cookie
|
||||
config.picture_config=Configuration d'Image
|
||||
config.picture_service=Service d'Imagerie
|
||||
config.disable_gravatar=Désactiver Gravatar
|
||||
config.log_config=Configuration du Journal
|
||||
config.log_mode=Mode du Journal
|
||||
|
||||
monitor.cron=Tâches Cron
|
||||
monitor.name=Nom
|
||||
monitor.schedule=Planification
|
||||
monitor.next=Suivant
|
||||
monitor.previous=Précédent
|
||||
monitor.execute_times=Nombre d'Éxécutions
|
||||
monitor.process=Processus en cours d'Éxécution
|
||||
monitor.desc=Description
|
||||
monitor.start=Heure de Démarrage
|
||||
monitor.execute_time=Heure d'Éxécution
|
||||
|
||||
notices.system_notice_list=Notes Systèmes
|
||||
notices.type=Type
|
||||
notices.type_1=Référentiel
|
||||
notices.desc=Description
|
||||
notices.op=Auteur
|
||||
notices.delete_success=Note système supprimée avec succès.
|
||||
|
||||
[action]
|
||||
create_repo=a crée le Référentiel <a href="%s/%s">%s</a>
|
||||
commit_repo=a soumis à <a href="%s/%s/src/%s">%s</a> chez <a href="%s/%s">%s</a>
|
||||
create_issue=a ouvert un problème <a href="%s/%s/issues/%s">%s#%s</a>
|
||||
comment_issue=a commenté le problème <a href="%s/%s/issues/%s">%s#%s</a>
|
||||
transfer_repo=a transféré le Référentiel <code>%s</code> à <a href="/%s%s">%s</a>
|
||||
push_tag=a tagé <a href="%s/%s/src/%s">%s</a> à <a href="%s/%s">%s</a>
|
||||
compare_2_commits=Comparer ces 2 commissions
|
||||
|
||||
[tool]
|
||||
ago=auparavant
|
||||
from_now=à partir de maintenant
|
||||
now=maintenant
|
||||
1s=1 seconde %s
|
||||
1m=1 minute %s
|
||||
1h=1 heure %s
|
||||
1d=1 jour %s
|
||||
1w=1 semaine %s
|
||||
1mon=1 mois %s
|
||||
1y=1 an %s
|
||||
seconds=%d secondes %s
|
||||
minutes=%d minutes %s
|
||||
hours=%d heures %s
|
||||
days=%d jours %s
|
||||
weeks=%d semaines %s
|
||||
months=%d mois %s
|
||||
years=%d ans %s
|
||||
raw_seconds=secondes
|
||||
raw_minutes=minutes
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
683
conf/locale/locale_nl-NL.ini
Executable file
683
conf/locale/locale_nl-NL.ini
Executable file
@@ -0,0 +1,683 @@
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
[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.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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1260
conf/locale/locale_zh-CN.ini
Normal file → Executable file
1260
conf/locale/locale_zh-CN.ini
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
683
conf/locale/locale_zh-HK.ini
Executable file
683
conf/locale/locale_zh-HK.ini
Executable file
@@ -0,0 +1,683 @@
|
||||
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 上修改您的頭像
|
||||
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=取消操作
|
||||
|
||||
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=查看文件
|
||||
|
||||
[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.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.0.0913 Beta"
|
||||
const APP_VER = "0.5.8.1118 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)
|
||||
|
||||
@@ -137,7 +137,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 +153,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 +183,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 +219,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,21 +244,28 @@ 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 {
|
||||
pusher_email = pusher.Email
|
||||
pusher_name = pusher.GetFullNameFallback()
|
||||
}
|
||||
|
||||
commits := make([]*PayloadCommit, len(commit.Commits))
|
||||
for i, cmt := range commit.Commits {
|
||||
author_username := ""
|
||||
author, err := GetUserByEmail(cmt.AuthorEmail)
|
||||
if err == nil {
|
||||
author_username = author.Name
|
||||
}
|
||||
commits[i] = &PayloadCommit{
|
||||
Id: cmt.Sha1,
|
||||
Message: cmt.Message,
|
||||
Url: fmt.Sprintf("%s/commit/%s", repoLink, cmt.Sha1),
|
||||
Author: &PayloadAuthor{
|
||||
Name: cmt.AuthorName,
|
||||
Email: cmt.AuthorEmail,
|
||||
Name: cmt.AuthorName,
|
||||
Email: cmt.AuthorEmail,
|
||||
UserName: author_username,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -266,18 +280,20 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string,
|
||||
Website: repo.Website,
|
||||
Watchers: repo.NumWatches,
|
||||
Owner: &PayloadAuthor{
|
||||
Name: repoUserName,
|
||||
Email: actEmail,
|
||||
Name: repo.Owner.GetFullNameFallback(),
|
||||
Email: repo.Owner.Email,
|
||||
UserName: repo.Owner.Name,
|
||||
},
|
||||
Private: repo.IsPrivate,
|
||||
},
|
||||
Pusher: &PayloadAuthor{
|
||||
Name: repo.Owner.LowerName,
|
||||
Email: repo.Owner.Email,
|
||||
Name: pusher_name,
|
||||
Email: pusher_email,
|
||||
UserName: userName,
|
||||
},
|
||||
Before: oldCommitId,
|
||||
After: newCommitId,
|
||||
CompareUrl: compareUrl,
|
||||
CompareUrl: commit.CompareUrl,
|
||||
}
|
||||
|
||||
for _, w := range ws {
|
||||
@@ -334,13 +350,29 @@ func NewRepoAction(u *User, repo *Repository) (err error) {
|
||||
|
||||
// TransferRepoAction adds new action for transfering 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
|
||||
}
|
||||
@@ -350,7 +382,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
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func (diff *Diff) NumFiles() int {
|
||||
|
||||
const DIFF_HEAD = "diff --git "
|
||||
|
||||
func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
|
||||
func ParsePatch(pid int64, maxlines int, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
var (
|
||||
curFile *DiffFile
|
||||
@@ -79,6 +79,7 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
|
||||
}
|
||||
|
||||
leftLine, rightLine int
|
||||
isTooLong bool
|
||||
)
|
||||
|
||||
diff := &Diff{Files: make([]*DiffFile, 0)}
|
||||
@@ -90,18 +91,19 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
i = i + 1
|
||||
|
||||
// Diff data too large.
|
||||
if i == 5000 {
|
||||
log.Warn("Diff data too large")
|
||||
return &Diff{}, nil
|
||||
}
|
||||
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
i = i + 1
|
||||
|
||||
// Diff data too large, we only show the first about maxlines lines
|
||||
if i == maxlines {
|
||||
isTooLong = true
|
||||
log.Warn("Diff data too large")
|
||||
//return &Diff{}, nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case line[0] == ' ':
|
||||
diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line, LeftIdx: leftLine, RightIdx: rightLine}
|
||||
@@ -110,6 +112,10 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
|
||||
curSection.Lines = append(curSection.Lines, diffLine)
|
||||
continue
|
||||
case line[0] == '@':
|
||||
if isTooLong {
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
curSection = &DiffSection{}
|
||||
curFile.Sections = append(curFile.Sections, curSection)
|
||||
ss := strings.Split(line, "@@")
|
||||
@@ -143,6 +149,10 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
|
||||
|
||||
// Get new file.
|
||||
if strings.HasPrefix(line, DIFF_HEAD) {
|
||||
if isTooLong {
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
fs := strings.Split(line[len(DIFF_HEAD):], " ")
|
||||
a := fs[0]
|
||||
|
||||
@@ -174,7 +184,7 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) {
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
func GetDiffRange(repoPath, beforeCommitId string, afterCommitId string) (*Diff, error) {
|
||||
func GetDiffRange(repoPath, beforeCommitId string, afterCommitId string, maxlines int) (*Diff, error) {
|
||||
repo, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -228,9 +238,9 @@ func GetDiffRange(repoPath, beforeCommitId string, afterCommitId string) (*Diff,
|
||||
}
|
||||
}()
|
||||
|
||||
return ParsePatch(pid, cmd, rd)
|
||||
return ParsePatch(pid, maxlines, cmd, rd)
|
||||
}
|
||||
|
||||
func GetDiffCommit(repoPath, commitId string) (*Diff, error) {
|
||||
return GetDiffRange(repoPath, "", commitId)
|
||||
func GetDiffCommit(repoPath, commitId string, maxlines int) (*Diff, error) {
|
||||
return GetDiffRange(repoPath, "", commitId, maxlines)
|
||||
}
|
||||
|
||||
@@ -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 + "|%'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,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 +161,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.
|
||||
|
||||
@@ -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,7 +104,7 @@ 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,
|
||||
@@ -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
|
||||
|
||||
@@ -845,20 +845,9 @@ 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.
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -32,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{}
|
||||
@@ -77,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
|
||||
@@ -100,13 +102,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…
|
||||
@@ -119,27 +123,35 @@ func CheckPublicKeyString(content string) (bool, error) {
|
||||
tmpFile.WriteString(content)
|
||||
tmpFile.Close()
|
||||
|
||||
// … see if ssh-keygen recognizes its contents
|
||||
// Check if ssh-keygen recognizes its contents.
|
||||
stdout, stderr, err := process.Exec("CheckPublicKeyString", "ssh-keygen", "-l", "-f", tmpPath)
|
||||
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.
|
||||
if setting.IsWindows {
|
||||
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
|
||||
}
|
||||
keySize, err := com.StrTo(sshKeygenOutput[0]).Int()
|
||||
if err != nil {
|
||||
return false, errors.New("Cannot get key size of the given key")
|
||||
|
||||
// Check if key type and key size match.
|
||||
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
|
||||
@@ -160,10 +172,14 @@ func saveAuthorizedKeyFile(key *PublicKey) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if finfo.Mode().Perm() > 0600 {
|
||||
log.Error(4, "authorized_keys file has unusual permission flags: %s - setting to -rw-------", finfo.Mode().Perm().String())
|
||||
if err = f.Chmod(0600); err != nil {
|
||||
return err
|
||||
|
||||
// FIXME: following command does not support in Windows.
|
||||
if !setting.IsWindows {
|
||||
if finfo.Mode().Perm() > 0600 {
|
||||
log.Error(4, "authorized_keys file has unusual permission flags: %s - setting to -rw-------", finfo.Mode().Perm().String())
|
||||
if err = f.Chmod(0600); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,9 +207,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 {
|
||||
@@ -221,10 +240,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
|
||||
}
|
||||
|
||||
422
models/repo.go
422
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() {
|
||||
@@ -95,8 +96,13 @@ func NewRepoContext() {
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to get Git version: %v", err)
|
||||
}
|
||||
if ver.Major < 2 && ver.Minor < 8 {
|
||||
log.Fatal(4, "Gogs requires Git version greater or equal to 1.8.0")
|
||||
|
||||
reqVer, err := git.ParseVersion("1.7.1")
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to parse required Git version: %v", err)
|
||||
}
|
||||
if ver.LessThan(reqVer) {
|
||||
log.Fatal(4, "Gogs requires Git version greater or equal to 1.7.1")
|
||||
}
|
||||
|
||||
// Check if server has basic git setting and set if not.
|
||||
@@ -127,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
|
||||
@@ -148,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
|
||||
}
|
||||
|
||||
@@ -169,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 {
|
||||
@@ -176,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.
|
||||
@@ -647,11 +696,11 @@ 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.
|
||||
has, err := IsRepositoryExist(u, repo.Name)
|
||||
has, err := IsRepositoryExist(newUser, repo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
@@ -664,17 +713,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.
|
||||
@@ -690,17 +756,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,
|
||||
@@ -732,10 +798,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
|
||||
}
|
||||
@@ -744,14 +820,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 {
|
||||
@@ -903,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()
|
||||
}
|
||||
@@ -998,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 {
|
||||
@@ -1023,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
|
||||
}
|
||||
@@ -1049,17 +1137,21 @@ type SearchOption struct {
|
||||
Keyword string
|
||||
Uid int64
|
||||
Limit int
|
||||
Private bool
|
||||
}
|
||||
|
||||
// FilterSQLInject tries to prevent SQL injection.
|
||||
func FilterSQLInject(key string) string {
|
||||
key = strings.TrimSpace(key)
|
||||
key = strings.Split(key, " ")[0]
|
||||
key = strings.Replace(key, ",", "", -1)
|
||||
return key
|
||||
}
|
||||
|
||||
// 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]
|
||||
opt.Keyword = FilterSQLInject(opt.Keyword)
|
||||
if len(opt.Keyword) == 0 {
|
||||
return repos, nil
|
||||
}
|
||||
@@ -1072,10 +1164,32 @@ 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
|
||||
}
|
||||
|
||||
// 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"))
|
||||
})
|
||||
}
|
||||
|
||||
// __ __ __ .__
|
||||
// / \ / \_____ _/ |_ ____ | |__
|
||||
// \ \/\/ /\__ \\ __\/ ___\| | \
|
||||
// \ / / __ \| | \ \___| Y \
|
||||
// \__/\ / (____ /__| \___ >___| /
|
||||
// \/ \/ \/ \/
|
||||
|
||||
// Watch is connection request for receiving repository notifycation.
|
||||
type Watch struct {
|
||||
Id int64
|
||||
@@ -1083,32 +1197,36 @@ type Watch struct {
|
||||
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.
|
||||
@@ -1146,6 +1264,13 @@ func NotifyWatchers(act *Action) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// _________ __
|
||||
// / _____// |______ _______
|
||||
// \_____ \\ __\__ \\_ __ \
|
||||
// / \| | / __ \| | \/
|
||||
// /_______ /|__| (____ /__|
|
||||
// \/ \/
|
||||
|
||||
type Star struct {
|
||||
Id int64
|
||||
Uid int64 `xorm:"UNIQUE(s)"`
|
||||
@@ -1160,16 +1285,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
|
||||
}
|
||||
@@ -1180,6 +1309,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
|
||||
|
||||
115
models/user.go
115
models/user.go
@@ -5,6 +5,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
@@ -66,6 +67,7 @@ type User struct {
|
||||
Website string
|
||||
IsActive bool
|
||||
IsAdmin bool
|
||||
AllowGitHook bool
|
||||
Rands string `xorm:"VARCHAR(10)"`
|
||||
Salt string `xorm:"VARCHAR(10)"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
@@ -82,24 +84,24 @@ 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"
|
||||
return setting.AppSubUrl + "/img/avatar_default.jpg"
|
||||
} else if setting.Service.EnableCacheAvatar {
|
||||
return "/avatar/" + u.Avatar
|
||||
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.
|
||||
@@ -167,6 +169,14 @@ func (u *User) GetOrganizations() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFullNameFallback returns Full Name if set, otherwise username
|
||||
func (u *User) GetFullNameFallback() string {
|
||||
if u.FullName == "" {
|
||||
return u.Name
|
||||
}
|
||||
return u.FullName
|
||||
}
|
||||
|
||||
// IsUserExist checks if given user name exist,
|
||||
// the user name should be noncased unique.
|
||||
func IsUserExist(name string) (bool, error) {
|
||||
@@ -184,7 +194,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)
|
||||
}
|
||||
@@ -367,7 +377,7 @@ func UpdateUser(u *User) error {
|
||||
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.
|
||||
@@ -381,13 +391,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 {
|
||||
@@ -464,22 +474,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 {
|
||||
@@ -505,6 +515,49 @@ func GetUserIdsByNames(names []string) []int64 {
|
||||
return ids
|
||||
}
|
||||
|
||||
// UserCommit represtns a commit with validation of user.
|
||||
type UserCommit struct {
|
||||
UserName string
|
||||
*git.Commit
|
||||
}
|
||||
|
||||
// ValidateCommitWithEmail chceck if author's e-mail of commit is corresponsind to a user.
|
||||
func ValidateCommitWithEmail(c *git.Commit) (uname string) {
|
||||
u, err := GetUserByEmail(c.Author.Email)
|
||||
if err == nil {
|
||||
uname = u.Name
|
||||
}
|
||||
return uname
|
||||
}
|
||||
|
||||
// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
|
||||
func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
|
||||
emails := map[string]string{}
|
||||
newCommits := list.New()
|
||||
e := oldCommits.Front()
|
||||
for e != nil {
|
||||
c := e.Value.(*git.Commit)
|
||||
|
||||
uname := ""
|
||||
if v, ok := emails[c.Author.Email]; !ok {
|
||||
u, err := GetUserByEmail(c.Author.Email)
|
||||
if err == nil {
|
||||
uname = u.Name
|
||||
}
|
||||
emails[c.Author.Email] = uname
|
||||
} else {
|
||||
uname = v
|
||||
}
|
||||
|
||||
newCommits.PushBack(UserCommit{
|
||||
UserName: uname,
|
||||
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 {
|
||||
@@ -522,20 +575,14 @@ 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]
|
||||
opt.Keyword = FilterSQLInject(opt.Keyword)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -548,27 +595,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.
|
||||
@@ -147,15 +175,43 @@ 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"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
UserName string `json:"username"`
|
||||
}
|
||||
|
||||
type PayloadCommit struct {
|
||||
@@ -172,7 +228,7 @@ type PayloadRepo struct {
|
||||
Description string `json:"description"`
|
||||
Website string `json:"website"`
|
||||
Watchers int `json:"watchers"`
|
||||
Owner *PayloadAuthor `json:"author"`
|
||||
Owner *PayloadAuthor `json:"owner"`
|
||||
Private bool `json:"private"`
|
||||
}
|
||||
|
||||
@@ -234,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),
|
||||
@@ -254,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,11 +8,9 @@ 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 {
|
||||
@@ -21,18 +19,13 @@ type MarkdownForm struct {
|
||||
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,37 @@ 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"
|
||||
)
|
||||
|
||||
// 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 +60,60 @@ 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)
|
||||
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
|
||||
}
|
||||
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 +156,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 +181,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)
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
// _______________________________________ _________.______________________ _______________.___.
|
||||
@@ -28,8 +26,8 @@ type CreateRepoForm struct {
|
||||
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,8 +159,8 @@ 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 {
|
||||
@@ -174,6 +171,6 @@ type EditReleaseForm struct {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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 InstallForm struct {
|
||||
@@ -34,8 +32,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 +52,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 +62,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 +82,8 @@ 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 ChangePasswordForm struct {
|
||||
@@ -94,6 +92,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,11 +33,17 @@ 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
|
||||
@@ -121,7 +127,7 @@ func (this *Avatar) Encode(wr io.Writer, size int) (err error) {
|
||||
|
||||
// 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 +135,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,6 +91,14 @@ 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-]*)?`)
|
||||
@@ -112,7 +122,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,39 +163,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,16 +5,18 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogits/gogs/modules/mahonia"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/saintfish/chardet"
|
||||
)
|
||||
|
||||
func Str2html(raw string) template.HTML {
|
||||
@@ -45,6 +47,29 @@ func ShortSha(sha1 string) string {
|
||||
return sha1
|
||||
}
|
||||
|
||||
func ToUtf8WithErr(content []byte) (error, string) {
|
||||
detector := chardet.NewTextDetector()
|
||||
result, err := detector.DetectBest(content)
|
||||
if err != nil {
|
||||
return err, ""
|
||||
}
|
||||
|
||||
if result.Charset == "utf8" {
|
||||
return nil, string(content)
|
||||
}
|
||||
|
||||
decoder := mahonia.NewDecoder(result.Charset)
|
||||
if decoder != nil {
|
||||
return nil, decoder.ConvertString(string(content))
|
||||
}
|
||||
return errors.New("unknow char decoder"), string(content)
|
||||
}
|
||||
|
||||
func ToUtf8(content string) string {
|
||||
_, res := ToUtf8WithErr([]byte(content))
|
||||
return res
|
||||
}
|
||||
|
||||
var mailDomains = map[string]string{
|
||||
"gmail.com": "gmail.com",
|
||||
}
|
||||
@@ -56,6 +81,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
|
||||
},
|
||||
@@ -78,12 +106,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]
|
||||
@@ -94,7 +121,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,
|
||||
@@ -103,6 +140,10 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
|
||||
"ActionContent2Commits": ActionContent2Commits,
|
||||
"Oauth2Icon": Oauth2Icon,
|
||||
"Oauth2Name": Oauth2Name,
|
||||
"ToUtf8": ToUtf8,
|
||||
"EscapePound": func(str string) string {
|
||||
return strings.Replace(str, "#", "%23", -1)
|
||||
},
|
||||
}
|
||||
|
||||
type Actioner interface {
|
||||
@@ -119,14 +160,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:
|
||||
@@ -134,19 +173,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
|
||||
@@ -155,8 +181,9 @@ type PushCommit struct {
|
||||
}
|
||||
|
||||
type PushCommits struct {
|
||||
Len int
|
||||
Commits []*PushCommit
|
||||
Len int
|
||||
Commits []*PushCommit
|
||||
CompareUrl string
|
||||
}
|
||||
|
||||
func ActionContent2Commits(act Actioner) *PushCommits {
|
||||
@@ -167,52 +194,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,11 +9,14 @@ import (
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"html/template"
|
||||
"math"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -23,13 +26,40 @@ import (
|
||||
"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) (user string, name string, err error) {
|
||||
var s []byte
|
||||
s, err = base64.StdEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return user, name, err
|
||||
}
|
||||
|
||||
a := strings.Split(string(s), ":")
|
||||
if len(a) == 2 {
|
||||
user, name = a[0], a[1]
|
||||
} else {
|
||||
err = errors.New("decode failed")
|
||||
}
|
||||
return user, name, err
|
||||
}
|
||||
|
||||
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 +176,11 @@ 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"
|
||||
return setting.AppSubUrl + "/img/avatar_default.jpg"
|
||||
} else if setting.Service.EnableCacheAvatar {
|
||||
return "/avatar/" + EncodeMd5(email)
|
||||
return setting.AppSubUrl + "/avatar/" + EncodeMd5(email)
|
||||
}
|
||||
return "//1.gravatar.com/avatar/" + EncodeMd5(email)
|
||||
return setting.GravatarSource + EncodeMd5(email)
|
||||
}
|
||||
|
||||
// Seconds-based time units
|
||||
@@ -446,3 +476,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.
|
||||
|
||||
@@ -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
|
||||
@@ -137,6 +137,14 @@ func (repo *Repository) GetCommit(commitId string) (*Commit, error) {
|
||||
}
|
||||
|
||||
func (repo *Repository) commitsCount(id sha1) (int, error) {
|
||||
if gitVer.LessThan(MustParseVersion("1.8.0")) {
|
||||
stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", "--pretty=format:''", id.String())
|
||||
if err != nil {
|
||||
return 0, errors.New(string(stderr))
|
||||
}
|
||||
return len(bytes.Split(stdout, []byte("\n"))), nil
|
||||
}
|
||||
|
||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count", id.String())
|
||||
if err != nil {
|
||||
return 0, errors.New(stderr)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -11,33 +11,89 @@ import (
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
var (
|
||||
// Cached Git version.
|
||||
gitVer *Version
|
||||
)
|
||||
|
||||
// Version represents version of Git.
|
||||
type Version struct {
|
||||
Major, Minor, Patch int
|
||||
}
|
||||
|
||||
// GetVersion returns current Git version installed.
|
||||
func GetVersion() (Version, error) {
|
||||
stdout, stderr, err := com.ExecCmd("git", "version")
|
||||
if err != nil {
|
||||
return Version{}, errors.New(stderr)
|
||||
}
|
||||
|
||||
infos := strings.Split(stdout, " ")
|
||||
func ParseVersion(verStr string) (*Version, error) {
|
||||
infos := strings.Split(verStr, ".")
|
||||
if len(infos) < 3 {
|
||||
return Version{}, errors.New("not enough output")
|
||||
return nil, errors.New("incorrect version input")
|
||||
}
|
||||
|
||||
v := Version{}
|
||||
for i, s := range strings.Split(strings.TrimSpace(infos[2]), ".") {
|
||||
v := &Version{}
|
||||
for i, s := range infos {
|
||||
switch i {
|
||||
case 0:
|
||||
v.Major, _ = com.StrTo(s).Int()
|
||||
case 1:
|
||||
v.Minor, _ = com.StrTo(s).Int()
|
||||
case 2:
|
||||
v.Patch, _ = com.StrTo(s).Int()
|
||||
v.Patch, _ = com.StrTo(strings.TrimSpace(s)).Int()
|
||||
}
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func MustParseVersion(verStr string) *Version {
|
||||
v, _ := ParseVersion(verStr)
|
||||
return v
|
||||
}
|
||||
|
||||
// Compare compares two versions,
|
||||
// it returns 1 if original is greater, -1 if original is smaller, 0 if equal.
|
||||
func (v *Version) Compare(that *Version) int {
|
||||
if v.Major > that.Major {
|
||||
return 1
|
||||
} else if v.Major < that.Major {
|
||||
return -1
|
||||
}
|
||||
|
||||
if v.Minor > that.Minor {
|
||||
return 1
|
||||
} else if v.Minor < that.Minor {
|
||||
return -1
|
||||
}
|
||||
|
||||
if v.Patch > that.Patch {
|
||||
return 1
|
||||
} else if v.Patch < that.Patch {
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
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 {
|
||||
return gitVer, nil
|
||||
}
|
||||
|
||||
stdout, stderr, err := com.ExecCmd("git", "version")
|
||||
if err != nil {
|
||||
return nil, errors.New(stderr)
|
||||
}
|
||||
|
||||
infos := strings.Split(stdout, " ")
|
||||
if len(infos) < 3 {
|
||||
return nil, errors.New("not enough output")
|
||||
}
|
||||
|
||||
gitVer, err = ParseVersion(infos[2])
|
||||
return gitVer, err
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -87,6 +87,12 @@ func Fatal(skip int, format string, v ...interface{}) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func Close() {
|
||||
for _, l := range loggers {
|
||||
l.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// .___ __ _____
|
||||
// | | _____/ |_ ____________/ ____\____ ____ ____
|
||||
// | |/ \ __\/ __ \_ __ \ __\\__ \ _/ ___\/ __ \
|
||||
|
||||
@@ -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, "; "))
|
||||
@@ -85,7 +134,7 @@ func Send(msg *Message) (int, error) {
|
||||
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 +145,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,13 @@ 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
|
||||
}
|
||||
|
||||
// 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 +48,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 +67,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,14 +29,14 @@ 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
|
||||
@@ -55,6 +54,7 @@ type Context struct {
|
||||
GitRepo *git.Repository
|
||||
BranchName string
|
||||
TagName string
|
||||
TreeName string
|
||||
CommitId string
|
||||
RepoLink string
|
||||
CloneLink struct {
|
||||
@@ -77,12 +77,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 +134,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 +149,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 +157,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 +173,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)
|
||||
}
|
||||
@@ -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.IsOrgOwner(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"))
|
||||
@@ -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,8 +371,15 @@ 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
|
||||
@@ -189,74 +399,6 @@ 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)
|
||||
@@ -274,7 +416,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 +441,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 +450,13 @@ func RequireTrueOwner() macaron.Handler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GitHookService checks if repsitory 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,10 +6,12 @@ package setting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -26,13 +28,15 @@ type Scheme string
|
||||
const (
|
||||
HTTP Scheme = "http"
|
||||
HTTPS Scheme = "https"
|
||||
FCGI Scheme = "fcgi"
|
||||
)
|
||||
|
||||
var (
|
||||
// App settings.
|
||||
AppVer string
|
||||
AppName string
|
||||
AppUrl string
|
||||
AppVer string
|
||||
AppName string
|
||||
AppUrl string
|
||||
AppSubUrl string
|
||||
|
||||
// Server settings.
|
||||
Protocol Scheme
|
||||
@@ -63,6 +67,7 @@ var (
|
||||
|
||||
// Picture settings.
|
||||
PictureService string
|
||||
GravatarSource string
|
||||
DisableGravatar bool
|
||||
|
||||
// Log settings.
|
||||
@@ -92,18 +97,24 @@ var (
|
||||
SessionProvider string
|
||||
SessionConfig *session.Config
|
||||
|
||||
// Git settings.
|
||||
MaxGitDiffLines int
|
||||
|
||||
// I18n settings.
|
||||
Langs, Names []string
|
||||
|
||||
// Global setting objects.
|
||||
Cfg *goconfig.ConfigFile
|
||||
ConfRootPath string
|
||||
CustomPath string // Custom directory path.
|
||||
ProdMode bool
|
||||
RunUser string
|
||||
|
||||
// I18n settings.
|
||||
Langs, Names []string
|
||||
IsWindows bool
|
||||
HasRobotsTxt bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
IsWindows = runtime.GOOS == "windows"
|
||||
log.NewLogger(0, "console", `{"level": 0}`)
|
||||
}
|
||||
|
||||
@@ -159,12 +170,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")
|
||||
@@ -237,12 +258,21 @@ 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"})
|
||||
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)
|
||||
|
||||
Langs = Cfg.MustValueArray("i18n", "LANGS", ",")
|
||||
Names = Cfg.MustValueArray("i18n", "NAMES", ",")
|
||||
|
||||
HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
|
||||
}
|
||||
|
||||
var Service struct {
|
||||
@@ -363,13 +393,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)
|
||||
|
||||
@@ -20,9 +20,6 @@ APP_USER=$(${CLI} config:get APP_USER)
|
||||
APP_GROUP=$(${CLI} config:get APP_GROUP)
|
||||
APP_CONFIG="/etc/${APP_NAME}/conf/app.ini"
|
||||
|
||||
# source debconf library
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
case "$1" in
|
||||
|
||||
configure)
|
||||
|
||||
@@ -7,9 +7,6 @@ body {
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
.fa {
|
||||
font-size: 14px;
|
||||
}
|
||||
.container {
|
||||
max-width: 1170px;
|
||||
padding: 0 1.5em;
|
||||
@@ -20,6 +17,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;
|
||||
@@ -45,6 +47,9 @@ img.avatar-100 {
|
||||
height: 100px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.drop-down .panel-header {
|
||||
font-size: 14px;
|
||||
}
|
||||
#wrapper {
|
||||
padding: 0;
|
||||
margin: 0 0 -55px 0;
|
||||
@@ -83,13 +88,14 @@ img.avatar-100 {
|
||||
z-index: 100;
|
||||
font-size: 12px;
|
||||
width: 120%;
|
||||
min-width: 100px;
|
||||
}
|
||||
#footer-lang .drop-down li > a {
|
||||
padding: 3px 9px;
|
||||
}
|
||||
#header {
|
||||
background-color: #428bca;
|
||||
height: 44px;
|
||||
height: 45px;
|
||||
}
|
||||
#header > .menu-line > li > a {
|
||||
display: inline-block;
|
||||
@@ -114,14 +120,14 @@ img.avatar-100 {
|
||||
height: 44px;
|
||||
}
|
||||
#header-nav-user img {
|
||||
margin: -4px 10px 0 0;
|
||||
margin: -2px 10px 0 0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
#header-nav-sign-out > a:hover {
|
||||
color: #ff908b !important;
|
||||
}
|
||||
#header-nav-logo {
|
||||
padding: 6px 1.2em;
|
||||
padding: 6px 1.2em 6px 0;
|
||||
}
|
||||
#header-nav-explore,
|
||||
#header-nav-help {
|
||||
@@ -234,6 +240,9 @@ img.avatar-100 {
|
||||
.text-black {
|
||||
color: #444444;
|
||||
}
|
||||
.text-gold {
|
||||
color: #a1882b;
|
||||
}
|
||||
.table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
@@ -265,6 +274,10 @@ img.avatar-100 {
|
||||
.pagination li {
|
||||
display: inline;
|
||||
}
|
||||
.list-unstyled {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.markdown {
|
||||
background-color: white;
|
||||
font-size: 16px;
|
||||
@@ -294,6 +307,12 @@ img.avatar-100 {
|
||||
.markdown li:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.markdown code {
|
||||
padding: 0.2em 0.5em;
|
||||
margin: 0;
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
border-radius: 3px;
|
||||
}
|
||||
.markdown > pre {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
@@ -304,6 +323,10 @@ img.avatar-100 {
|
||||
padding: 10px;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
.markdown > pre code {
|
||||
padding: 0;
|
||||
background-color: inherit;
|
||||
}
|
||||
.markdown img {
|
||||
padding: 10px 0;
|
||||
max-width: 100%;
|
||||
@@ -686,18 +709,26 @@ ol.linenums {
|
||||
width: auto;
|
||||
}
|
||||
/*
|
||||
The dashboard page style
|
||||
The dashboard page style
|
||||
*/
|
||||
#dashboard-header {
|
||||
border-bottom: 1px solid #d6d6d6;
|
||||
height: 69px;
|
||||
background-color: #FFF;
|
||||
}
|
||||
#dashboard-header > .menu-line > li {
|
||||
padding: 12px 0;
|
||||
padding: 12px 6px;
|
||||
}
|
||||
#dashboard-header > .menu-line > li.drop {
|
||||
margin-left: -22px;
|
||||
}
|
||||
#dashboard-header > .menu-line > li.drop:hover a {
|
||||
background-color: transparent;
|
||||
}
|
||||
#dashboard-header > .menu-line > li.right > a {
|
||||
font-size: 1.2em;
|
||||
color: #444444;
|
||||
padding: .4em .8em;
|
||||
}
|
||||
#dashboard-header > .menu-line > li.right > a:hover {
|
||||
background-color: transparent;
|
||||
@@ -727,7 +758,7 @@ The dashboard page style
|
||||
border-top-left-radius: .3em;
|
||||
border-top-right-radius: .3em;
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
height: 32px;
|
||||
}
|
||||
#dashboard-sidebar-menu > li {
|
||||
border: 1px solid #d6d6d6;
|
||||
@@ -736,8 +767,8 @@ The dashboard page style
|
||||
border-bottom: none;
|
||||
}
|
||||
#dashboard-sidebar-menu > li > a {
|
||||
padding-top: .4em;
|
||||
padding-bottom: .4em;
|
||||
padding-top: .3em;
|
||||
padding-bottom: .3em;
|
||||
}
|
||||
#dashboard-sidebar-menu > li.first {
|
||||
border-top-left-radius: .3em;
|
||||
@@ -805,6 +836,11 @@ The dashboard page style
|
||||
#dashboard-my-repo .repo-contrib-header {
|
||||
border-top: 1px solid #d6d6d6;
|
||||
}
|
||||
#dashboard-my-mirror .panel-header,
|
||||
#dashboard-my-org .panel-header,
|
||||
#dashboard-my-repo .panel-header {
|
||||
font-size: 14px;
|
||||
}
|
||||
#dashboard-my-repo .panel-header .octicon {
|
||||
margin-right: 6px;
|
||||
font-size: 12px;
|
||||
@@ -818,7 +854,7 @@ The dashboard page style
|
||||
}
|
||||
#dashboard-new-repo {
|
||||
width: 50px;
|
||||
height: 35px;
|
||||
height: 33px;
|
||||
padding-top: 6px;
|
||||
margin-right: 1px;
|
||||
border-top-left-radius: .3em;
|
||||
@@ -828,7 +864,7 @@ The dashboard page style
|
||||
font-size: 2em;
|
||||
}
|
||||
#dashboard-new-repo-menu {
|
||||
top: 35px;
|
||||
top: 33px;
|
||||
width: 180px;
|
||||
background-color: #FFF;
|
||||
left: -132px;
|
||||
@@ -843,6 +879,7 @@ The dashboard page style
|
||||
}
|
||||
#dashboard-selection-menu > .drop-down {
|
||||
top: 56px;
|
||||
left: 22px;
|
||||
}
|
||||
#dashboard-selection-menu li {
|
||||
white-space: nowrap;
|
||||
@@ -878,6 +915,9 @@ The dashboard page style
|
||||
#dashboard-switch-menu > li.checked > a .octicon {
|
||||
opacity: 1;
|
||||
}
|
||||
#dashboard-news {
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
#dashboard-news .news {
|
||||
margin-right: 2.4em;
|
||||
padding-bottom: 1em;
|
||||
@@ -889,7 +929,7 @@ The dashboard page style
|
||||
color: #CCC;
|
||||
}
|
||||
#dashboard-news .news .avatar {
|
||||
margin: 0 1.2em;
|
||||
margin-right: 1.2em;
|
||||
}
|
||||
#dashboard-news .news .news-content,
|
||||
#dashboard-news .news .news-time {
|
||||
@@ -950,7 +990,6 @@ The register and sign-in page style
|
||||
background-color: #FFF;
|
||||
margin-left: -15px;
|
||||
}
|
||||
/* repository main */
|
||||
#repo-wrapper {
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
@@ -984,8 +1023,8 @@ The register and sign-in page style
|
||||
}
|
||||
#repo-header-meta a > .btn {
|
||||
line-height: 16px;
|
||||
font-size: 1.05em;
|
||||
margin-left: 16px;
|
||||
font-size: 13px;
|
||||
}
|
||||
#repo-header-meta a > .btn i {
|
||||
margin-right: 6px;
|
||||
@@ -1013,9 +1052,9 @@ The register and sign-in page style
|
||||
}
|
||||
#repo-header-download-drop {
|
||||
line-height: 24px;
|
||||
width: 440px;
|
||||
width: 460px;
|
||||
top: 50px;
|
||||
left: -370px;
|
||||
left: -390px;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
z-index: 1;
|
||||
@@ -1027,12 +1066,20 @@ The register and sign-in page style
|
||||
#repo-header-download-drop input {
|
||||
font-size: 11px;
|
||||
}
|
||||
#repo-header-download-drop #repo-clone-copy {
|
||||
width: 85px;
|
||||
text-overflow: clip;
|
||||
}
|
||||
#repo-content {
|
||||
padding: 18px 0;
|
||||
}
|
||||
.repo-wide-wrapper {
|
||||
padding: 18px;
|
||||
position: relative;
|
||||
}
|
||||
#repo-clone-url {
|
||||
border-right: none;
|
||||
width: 200px;
|
||||
width: 190px;
|
||||
border-left: none;
|
||||
}
|
||||
#repo-clone-help {
|
||||
@@ -1054,6 +1101,9 @@ The register and sign-in page style
|
||||
#repo-desc {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
#repo-desc .no-description {
|
||||
color: #999;
|
||||
}
|
||||
#repo-sidebar-nav .label {
|
||||
font-size: 12px;
|
||||
line-height: 1.4em;
|
||||
@@ -1062,6 +1112,37 @@ The register and sign-in page style
|
||||
#repo-sidebar-nav i {
|
||||
margin-right: 6px;
|
||||
}
|
||||
#repo-sidebar-mini {
|
||||
margin-top: 6px;
|
||||
width: 60px;
|
||||
}
|
||||
#repo-sidebar-mini li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
#repo-sidebar-mini li > a {
|
||||
position: relative;
|
||||
padding-left: 12px;
|
||||
width: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
#repo-sidebar-mini li > a > i.octicon {
|
||||
font-size: 21px;
|
||||
}
|
||||
#repo-sidebar-mini .num {
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
top: 0;
|
||||
left: 36px;
|
||||
padding: 0 2px;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
text-align: center;
|
||||
line-height: 16px;
|
||||
border-radius: 4px;
|
||||
opacity: 0.7;
|
||||
-webkit-transform: scale(0.9);
|
||||
font-weight: bold;
|
||||
}
|
||||
#repo-file-nav {
|
||||
padding: .6em 0 1em 0;
|
||||
}
|
||||
@@ -1107,6 +1188,10 @@ The register and sign-in page style
|
||||
background-color: #EEE;
|
||||
font-weight: bold;
|
||||
}
|
||||
#repo-branch-tag .switching-list {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
#repo-branch-list li i,
|
||||
#repo-tag-list li i {
|
||||
margin-right: 12px;
|
||||
@@ -1116,9 +1201,6 @@ The register and sign-in page style
|
||||
#repo-tag-list li.checked i {
|
||||
opacity: 1;
|
||||
}
|
||||
#repo-tag-list {
|
||||
display: none;
|
||||
}
|
||||
#repo-bread .bread {
|
||||
padding-right: 0;
|
||||
font-size: 16px;
|
||||
@@ -1182,6 +1264,7 @@ The register and sign-in page style
|
||||
color: #444;
|
||||
}
|
||||
#repo-files-table thead .last-commit .text-truncate {
|
||||
max-width: 440px;
|
||||
margin-left: .4em;
|
||||
}
|
||||
#repo-files-table thead .last-commit .text-truncate,
|
||||
@@ -1189,7 +1272,8 @@ The register and sign-in page style
|
||||
font-weight: normal;
|
||||
color: #888;
|
||||
}
|
||||
#repo-readme {
|
||||
#repo-readme,
|
||||
#repo-read-file {
|
||||
margin-bottom: 80px;
|
||||
}
|
||||
#repo-bare-start {
|
||||
@@ -1332,12 +1416,10 @@ The register and sign-in page style
|
||||
}
|
||||
.code-view .lines-num span {
|
||||
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
line-height: 18px;
|
||||
padding: 0 8px 0 10px;
|
||||
line-height: 1.6;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.code-view .lines-code > pre {
|
||||
border: none;
|
||||
@@ -1345,6 +1427,7 @@ The register and sign-in page style
|
||||
}
|
||||
.code-view .lines-code > pre > ol.linenums > li {
|
||||
padding: 0 10px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.code-view .lines-code > pre > ol.linenums > li.active {
|
||||
background: #ffffdd;
|
||||
@@ -1396,6 +1479,145 @@ The register and sign-in page style
|
||||
width: 100%;
|
||||
list-style: none;
|
||||
}
|
||||
#commits-list {
|
||||
padding-top: 20px;
|
||||
}
|
||||
#commits-list h4 {
|
||||
line-height: 30px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.commit-list th {
|
||||
background-color: #FFF;
|
||||
line-height: 28px !important;
|
||||
}
|
||||
.commit-list .date {
|
||||
width: 120px;
|
||||
}
|
||||
.commit-list .author {
|
||||
padding-left: 20px;
|
||||
min-width: 180px;
|
||||
}
|
||||
.commit-list .author img {
|
||||
margin-top: -4px;
|
||||
}
|
||||
.commit-list .sha a {
|
||||
font-family: Consolas, Menlo, Monaco, "Lucida Console", monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
.commit-list .message {
|
||||
width: 60%;
|
||||
}
|
||||
.commit-list .message span {
|
||||
max-width: 500px;
|
||||
}
|
||||
.diff-head-box {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.diff-head-box .panel-body {
|
||||
padding: 10px 15px 5px 10px;
|
||||
}
|
||||
.diff-head-box .author img {
|
||||
margin-top: -7px;
|
||||
}
|
||||
.diff-detail-box {
|
||||
margin: 15px 0;
|
||||
line-height: 30px;
|
||||
}
|
||||
.diff-detail-box ol {
|
||||
clear: both;
|
||||
padding-left: 0;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.diff-detail-box ol li {
|
||||
list-style: none;
|
||||
padding-bottom: 4px;
|
||||
margin-bottom: 4px;
|
||||
border-bottom: 1px dashed #DDD;
|
||||
padding-left: 6px;
|
||||
}
|
||||
.diff-detail-box span.status {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.diff-detail-box span.status.modify {
|
||||
background-color: #f0db88;
|
||||
}
|
||||
.diff-detail-box span.status.add {
|
||||
background-color: #b4e2b4;
|
||||
}
|
||||
.diff-detail-box span.status.del {
|
||||
background-color: #e9aeae;
|
||||
}
|
||||
.diff-detail-box span.status.rename {
|
||||
background-color: #dad8ff;
|
||||
}
|
||||
.diff-box .count {
|
||||
margin-right: 12px;
|
||||
}
|
||||
.diff-box .count .bar {
|
||||
background-color: #e75316;
|
||||
height: 12px;
|
||||
width: 40px;
|
||||
display: inline-block;
|
||||
margin: 2px 4px 0 4px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
.diff-box .count .bar .add {
|
||||
background-color: #77c64a;
|
||||
height: 12px;
|
||||
}
|
||||
.diff-box .file {
|
||||
color: #888;
|
||||
}
|
||||
.diff-box .panel-header {
|
||||
font-size: 14px;
|
||||
}
|
||||
.diff-file-box .file-body.file-code .lines-num {
|
||||
text-align: right;
|
||||
color: #999;
|
||||
background: #fafafa;
|
||||
width: 1%;
|
||||
}
|
||||
.diff-file-box .file-body.file-code .lines-num-old {
|
||||
border-right: 1px solid #DDD;
|
||||
}
|
||||
.diff-file-box .code-diff tbody tr.tag-code td,
|
||||
.diff-file-box .code-diff tbody tr.tag-code pre {
|
||||
background-color: #E0E0E0 !important;
|
||||
border-color: #ADADAD !important;
|
||||
}
|
||||
.diff-file-box .code-diff tbody tr.del-code td,
|
||||
.diff-file-box .code-diff tbody tr.del-code pre {
|
||||
background-color: #ffe2dd !important;
|
||||
border-color: #e9aeae !important;
|
||||
}
|
||||
.diff-file-box .code-diff tbody tr.add-code td,
|
||||
.diff-file-box .code-diff tbody tr.add-code pre {
|
||||
background-color: #d1ffd6 !important;
|
||||
border-color: #b4e2b4 !important;
|
||||
}
|
||||
.diff-file-box .code-diff tbody tr:hover td,
|
||||
.diff-file-box .code-diff tbody tr:hover pre {
|
||||
background-color: #FFF8D2 !important;
|
||||
border-color: #F0DB88 !important;
|
||||
}
|
||||
.compare-head-box {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.compare-head-box .compare {
|
||||
padding: 0 15px 15px 15px;
|
||||
}
|
||||
.fork-flag {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
line-height: 10px;
|
||||
white-space: nowrap;
|
||||
margin-left: 44px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
#admin-wrapper,
|
||||
#setting-wrapper {
|
||||
padding-bottom: 100px;
|
||||
@@ -1478,18 +1700,21 @@ The register and sign-in page style
|
||||
#repo-hooks-panel,
|
||||
#repo-hooks-history-panel,
|
||||
#user-social-panel,
|
||||
#user-applications-panel,
|
||||
#user-ssh-panel {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#repo-hooks-panel .setting-list,
|
||||
#repo-hooks-history-panel .setting-list,
|
||||
#user-social-panel .setting-list,
|
||||
#user-applications-panel .setting-list,
|
||||
#user-ssh-panel .setting-list {
|
||||
background-color: #FFF;
|
||||
}
|
||||
#repo-hooks-panel .setting-list li,
|
||||
#repo-hooks-history-panel .setting-list li,
|
||||
#user-social-panel .setting-list li,
|
||||
#user-applications-panel .setting-list li,
|
||||
#user-ssh-panel .setting-list li {
|
||||
padding: 8px 20px;
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
@@ -1497,18 +1722,21 @@ The register and sign-in page style
|
||||
#repo-hooks-panel .setting-list li.ssh:hover,
|
||||
#repo-hooks-history-panel .setting-list li.ssh:hover,
|
||||
#user-social-panel .setting-list li.ssh:hover,
|
||||
#user-applications-panel .setting-list li.ssh:hover,
|
||||
#user-ssh-panel .setting-list li.ssh:hover {
|
||||
background-color: #ffffEE;
|
||||
}
|
||||
#repo-hooks-panel .setting-list li i,
|
||||
#repo-hooks-history-panel .setting-list li i,
|
||||
#user-social-panel .setting-list li i,
|
||||
#user-applications-panel .setting-list li i,
|
||||
#user-ssh-panel .setting-list li i {
|
||||
padding-right: 5px;
|
||||
}
|
||||
#repo-hooks-panel .active-icon,
|
||||
#repo-hooks-history-panel .active-icon,
|
||||
#user-social-panel .active-icon,
|
||||
#user-applications-panel .active-icon,
|
||||
#user-ssh-panel .active-icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
@@ -1520,78 +1748,74 @@ The register and sign-in page style
|
||||
#repo-hooks-panel .ssh-content,
|
||||
#repo-hooks-history-panel .ssh-content,
|
||||
#user-social-panel .ssh-content,
|
||||
#user-applications-panel .ssh-content,
|
||||
#user-ssh-panel .ssh-content {
|
||||
margin-left: 24px;
|
||||
}
|
||||
#repo-hooks-panel .ssh-content .octicon,
|
||||
#repo-hooks-history-panel .ssh-content .octicon,
|
||||
#user-social-panel .ssh-content .octicon,
|
||||
#user-applications-panel .ssh-content .octicon,
|
||||
#user-ssh-panel .ssh-content .octicon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
#repo-hooks-panel .ssh-content .print,
|
||||
#repo-hooks-history-panel .ssh-content .print,
|
||||
#user-social-panel .ssh-content .print,
|
||||
#user-applications-panel .ssh-content .print,
|
||||
#user-ssh-panel .ssh-content .print,
|
||||
#repo-hooks-panel .ssh-content .access,
|
||||
#repo-hooks-history-panel .ssh-content .access,
|
||||
#user-social-panel .ssh-content .access,
|
||||
#user-applications-panel .ssh-content .access,
|
||||
#user-ssh-panel .ssh-content .access,
|
||||
#repo-hooks-panel .ssh-content .activity,
|
||||
#repo-hooks-history-panel .ssh-content .activity,
|
||||
#user-social-panel .ssh-content .activity,
|
||||
#user-applications-panel .ssh-content .activity,
|
||||
#user-ssh-panel .ssh-content .activity {
|
||||
color: #888;
|
||||
}
|
||||
#repo-hooks-panel .ssh-delete-btn,
|
||||
#repo-hooks-history-panel .ssh-delete-btn,
|
||||
#user-social-panel .ssh-delete-btn,
|
||||
#user-ssh-panel .ssh-delete-btn {
|
||||
#repo-hooks-panel .ssh-content .access,
|
||||
#repo-hooks-history-panel .ssh-content .access,
|
||||
#user-social-panel .ssh-content .access,
|
||||
#user-applications-panel .ssh-content .access,
|
||||
#user-ssh-panel .ssh-content .access {
|
||||
max-width: 500px;
|
||||
}
|
||||
#repo-hooks-panel .ssh-btn,
|
||||
#repo-hooks-history-panel .ssh-btn,
|
||||
#user-social-panel .ssh-btn,
|
||||
#user-applications-panel .ssh-btn,
|
||||
#user-ssh-panel .ssh-btn {
|
||||
margin-top: 6px;
|
||||
}
|
||||
#user-ssh-add-form .panel-body {
|
||||
.form-settings-add .panel-body {
|
||||
background-color: #FFF;
|
||||
padding: 30px 0;
|
||||
}
|
||||
#user-ssh-add-form .ipt {
|
||||
.form-settings-add .ipt {
|
||||
width: 500px;
|
||||
}
|
||||
#user-ssh-add-form textarea {
|
||||
.form-settings-add textarea {
|
||||
height: 120px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
#user-ssh-add-form .field {
|
||||
.form-settings-add .field {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.repo-issue-wrapper {
|
||||
padding: 18px 0;
|
||||
}
|
||||
.pr-main {
|
||||
padding-right: 40px;
|
||||
box-sizing: border-box;
|
||||
margin-right: 100px;
|
||||
}
|
||||
.pr-sidebar {
|
||||
border-left: 1px solid #DDD;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 12px;
|
||||
}
|
||||
#pr-sidebar-nav {
|
||||
margin-top: 6px;
|
||||
}
|
||||
#pr-sidebar-nav li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
#pr-sidebar-nav li > a {
|
||||
border: 1px solid transparent;
|
||||
border-left: none;
|
||||
}
|
||||
#pr-sidebar-nav li > a:hover {
|
||||
background-color: #FFF;
|
||||
border-color: #DDD;
|
||||
}
|
||||
#pr-sidebar-nav .label {
|
||||
font-size: 12px;
|
||||
line-height: 1.4em;
|
||||
margin-top: 1px;
|
||||
}
|
||||
#pr-sidebar-nav li.current a {
|
||||
background-color: #FFF;
|
||||
border-color: #DDD;
|
||||
.pr-title {
|
||||
padding: 4px 0;
|
||||
}
|
||||
.pr-title .pr-num {
|
||||
font-weight: normal;
|
||||
@@ -1599,6 +1823,7 @@ The register and sign-in page style
|
||||
}
|
||||
.pr-meta {
|
||||
color: #888;
|
||||
padding: 4px 0 8px 0;
|
||||
}
|
||||
.pr-meta .pr-author {
|
||||
margin: 0 8px;
|
||||
@@ -1672,7 +1897,7 @@ The register and sign-in page style
|
||||
.issue-line,
|
||||
.issue-merge,
|
||||
.issue-add-comment {
|
||||
margin-bottom: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.issue-comment .author-avatar img {
|
||||
margin-right: 12px;
|
||||
@@ -1776,6 +2001,131 @@ textarea#issue-add-content {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
height: 120px;
|
||||
resize: vertical;
|
||||
}
|
||||
#issue-list-nav {
|
||||
border-bottom: 1px solid #DDD;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
#issue-list-nav li > a {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
padding: .2em .8em;
|
||||
}
|
||||
#issue-list-nav li.current > a {
|
||||
background-color: #0079bc;
|
||||
color: #FAFAFA;
|
||||
}
|
||||
#issue-list-nav li.right {
|
||||
margin-left: 4px;
|
||||
}
|
||||
#issue-new > a {
|
||||
padding: 0 !important;
|
||||
}
|
||||
#issue-new > a button {
|
||||
height: 29px;
|
||||
}
|
||||
#issue-list-menu {
|
||||
padding: 16px 0 12px 0;
|
||||
border-bottom: 1px solid #BBB;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
#issue-list-menu .mark a {
|
||||
color: #AAA;
|
||||
}
|
||||
#issue-list-menu .mark a:hover {
|
||||
color: #444;
|
||||
}
|
||||
#issue-list-menu .mark.hover a {
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
}
|
||||
#issue-list-menu > .left .mark {
|
||||
margin-right: 12px;
|
||||
}
|
||||
#issue-list-menu > .right .mark {
|
||||
margin-left: 12px;
|
||||
}
|
||||
#issue-list .item,
|
||||
#pull-list .item {
|
||||
position: relative;
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px dashed #AAA;
|
||||
}
|
||||
#issue-list .item .title > .title-text,
|
||||
#pull-list .item .title > .title-text {
|
||||
color: #444;
|
||||
font-size: 15px;
|
||||
margin: 0 6px;
|
||||
}
|
||||
#issue-list .item .index-num,
|
||||
#pull-list .item .index-num {
|
||||
padding: .25em .6em;
|
||||
}
|
||||
#issue-list .comment,
|
||||
#pull-list .comment {
|
||||
color: #666;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 0;
|
||||
}
|
||||
#issue-list .issue-label,
|
||||
#pull-list .issue-label {
|
||||
padding: .25em .6em;
|
||||
}
|
||||
#issue-list .issue-label a,
|
||||
#pull-list .issue-label a {
|
||||
color: #FFF;
|
||||
}
|
||||
#issue-list .desc,
|
||||
#pull-list .desc {
|
||||
color: #999;
|
||||
}
|
||||
#issue-list .desc a,
|
||||
#pull-list .desc a {
|
||||
color: #999;
|
||||
}
|
||||
#issue-list .desc a:hover,
|
||||
#pull-list .desc a:hover {
|
||||
color: #03a2ef;
|
||||
}
|
||||
#issue-list-pager {
|
||||
margin: 18px 0 24px 0;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
}
|
||||
#labels-num {
|
||||
margin-right: 1em;
|
||||
}
|
||||
#label-list .right {
|
||||
margin-left: 1em;
|
||||
color: #999;
|
||||
line-height: 30px;
|
||||
}
|
||||
#label-list .right i {
|
||||
margin-right: 3px;
|
||||
}
|
||||
#label-list .right:hover {
|
||||
color: #444444;
|
||||
}
|
||||
#label-list .num {
|
||||
margin-right: 3px;
|
||||
}
|
||||
#label-list .issue-num {
|
||||
margin-right: 4em;
|
||||
}
|
||||
#label-list .label:hover {
|
||||
color: #FFF;
|
||||
}
|
||||
#label-list .label i {
|
||||
margin-right: 6px;
|
||||
}
|
||||
#label-list .item {
|
||||
height: 30px;
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px dashed #AAA;
|
||||
}
|
||||
.org-header-alert .alert {
|
||||
margin-top: 10px;
|
||||
@@ -1810,7 +2160,10 @@ textarea#issue-add-content {
|
||||
color: #d9453d;
|
||||
}
|
||||
#org-header > div > .menu-line > li.right > a .octicon {
|
||||
margin-right: 6px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
#org-header > div > .menu-line > li.right > a .label {
|
||||
margin-left: 4px;
|
||||
}
|
||||
#org-header > div > .menu-line > li.right .current {
|
||||
border-bottom: 2px solid #D26911;
|
||||
@@ -2009,3 +2362,57 @@ textarea#issue-add-content {
|
||||
.admin-dl-horizontal > dd {
|
||||
margin-left: 240px;
|
||||
}
|
||||
.profile-avatar {
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
#profile-name {
|
||||
padding: 10px 0;
|
||||
}
|
||||
#profile-fullname {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
#profile-username {
|
||||
font-size: 1.6em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.profile-info {
|
||||
font-size: 14px;
|
||||
}
|
||||
.profile-info ul {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.profile-info ul .list-group-item {
|
||||
background-color: transparent;
|
||||
padding-top: 5px;
|
||||
color: #666;
|
||||
}
|
||||
.profile-info ul .profile-rel {
|
||||
width: 31%;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
.profile-info ul .profile-rel strong {
|
||||
display: block;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
.profile-info ul .profile-rel p {
|
||||
font-size: 12px;
|
||||
}
|
||||
#profile-header li a {
|
||||
font-size: 1.2em;
|
||||
color: #444444;
|
||||
padding: .4em .8em;
|
||||
}
|
||||
#profile-header li a:hover {
|
||||
background-color: transparent;
|
||||
color: #d9453d;
|
||||
}
|
||||
#profile-header li .current {
|
||||
border-bottom: 2px solid #D26911;
|
||||
}
|
||||
#profile-body {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -460,6 +460,9 @@ dt {
|
||||
box-sizing: content-box;
|
||||
text-align: center;
|
||||
}
|
||||
.btn-comb {
|
||||
margin-left: -1px;
|
||||
}
|
||||
.btn-disabled {
|
||||
opacity: .6;
|
||||
cursor: not-allowed;
|
||||
@@ -483,6 +486,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 +719,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 +735,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 +750,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 +814,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 +881,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,8 @@
|
||||
// @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 "lib/jquery.tipsy.js"
|
||||
|
||||
var Gogs = {};
|
||||
|
||||
@@ -202,13 +204,13 @@ var Gogs = {};
|
||||
// Search users by keyword.
|
||||
Gogs.searchUsers = function (val, $target) {
|
||||
$.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 + '</a></li>';
|
||||
});
|
||||
$target.html(html);
|
||||
$target.toggleShow();
|
||||
@@ -222,13 +224,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 +247,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 +264,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 +280,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 +386,79 @@ 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
|
||||
});
|
||||
}
|
||||
|
||||
// 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 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 +486,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 +548,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 +619,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 +645,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 +697,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();
|
||||
@@ -626,8 +771,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 +797,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 +805,9 @@ 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');
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
@import "../ui/var";
|
||||
|
||||
/*
|
||||
The dashboard page style
|
||||
The dashboard page style
|
||||
*/
|
||||
|
||||
@dashboardHeaderBorderColor: #D6D6D6;
|
||||
@dashboardHeaderLinkColor: #444;
|
||||
@dashboardHeaderLinkHoverColor: #D9453D;
|
||||
@@ -14,14 +12,22 @@ The dashboard page style
|
||||
#dashboard-header {
|
||||
border-bottom: 1px solid @dashboardHeaderBorderColor;
|
||||
height: 69px;
|
||||
background-color: #FFF;
|
||||
> .menu-line {
|
||||
> li {
|
||||
padding: 12px 0;
|
||||
padding: 12px 6px;
|
||||
}
|
||||
> li.drop{
|
||||
margin-left: -22px;
|
||||
&:hover a {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
> li.right {
|
||||
> a {
|
||||
font-size: 1.2em;
|
||||
color: @dashboardHeaderLinkColor;
|
||||
padding: .4em .8em;
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
color: @dashboardHeaderLinkHoverColor;
|
||||
@@ -36,18 +42,15 @@ The dashboard page style
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dashboard context switch selection
|
||||
#dashboard-selection-menu {
|
||||
a img {
|
||||
margin: -4px 10px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
#dashboard {
|
||||
padding: 24px 0;
|
||||
}
|
||||
|
||||
// dashboard sidebar contains contributed repositories panel,
|
||||
// and my repositories panel
|
||||
#dashboard-sidebar {
|
||||
@@ -60,7 +63,6 @@ The dashboard page style
|
||||
border-bottom-right-radius: .3em;
|
||||
}
|
||||
}
|
||||
|
||||
#dashboard-sidebar-menu {
|
||||
border-top-left-radius: .3em;
|
||||
border-top-right-radius: .3em;
|
||||
@@ -70,8 +72,8 @@ The dashboard page style
|
||||
margin-right: -1px;
|
||||
border-bottom: none;
|
||||
> a {
|
||||
padding-top: .4em;
|
||||
padding-bottom: .4em;
|
||||
padding-top: .3em;
|
||||
padding-bottom: .3em;
|
||||
}
|
||||
}
|
||||
> li.first {
|
||||
@@ -85,7 +87,7 @@ The dashboard page style
|
||||
float: right;
|
||||
}
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
height: 32px;
|
||||
> li.js-tab-nav-show {
|
||||
background-color: #EEEEEE;
|
||||
}
|
||||
@@ -96,7 +98,6 @@ The dashboard page style
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#dashboard-my-mirror,
|
||||
#dashboard-my-org,
|
||||
#dashboard-my-repo {
|
||||
@@ -131,8 +132,10 @@ The dashboard page style
|
||||
.repo-contrib-header {
|
||||
border-top: 1px solid #d6d6d6;
|
||||
}
|
||||
.panel-header {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
#dashboard-my-repo {
|
||||
.panel-header {
|
||||
.octicon {
|
||||
@@ -144,16 +147,14 @@ The dashboard page style
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
#dashboard-my-org,
|
||||
#dashboard-my-mirror {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// the button of new repository in my repositories panel
|
||||
#dashboard-new-repo {
|
||||
width: 50px;
|
||||
height: 35px;
|
||||
height: 33px;
|
||||
padding-top: 6px;
|
||||
margin-right: 1px;
|
||||
.octicon {
|
||||
@@ -162,10 +163,9 @@ The dashboard page style
|
||||
border-top-left-radius: .3em;
|
||||
border-top-right-radius: .3em;
|
||||
}
|
||||
|
||||
// the drop-down menu of #dashboard-new-repo
|
||||
#dashboard-new-repo-menu {
|
||||
top: 35px;
|
||||
top: 33px;
|
||||
width: 180px;
|
||||
background-color: #FFF;
|
||||
left: -132px;
|
||||
@@ -174,28 +174,27 @@ The dashboard page style
|
||||
font-size: 1.1em;
|
||||
}
|
||||
}
|
||||
|
||||
#dashboard-selection-menu {
|
||||
width: auto;
|
||||
max-width: 300px;
|
||||
> .drop-down {
|
||||
top: 56px;
|
||||
width: auto;
|
||||
max-width: 300px;
|
||||
> .drop-down {
|
||||
top: 56px;
|
||||
left: 22px;
|
||||
}
|
||||
li {
|
||||
white-space: nowrap;
|
||||
&.checked {
|
||||
.octicon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
li {
|
||||
white-space: nowrap;
|
||||
&.checked {
|
||||
.octicon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
a {
|
||||
text-overflow: ellipsis;
|
||||
-o-text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
a {
|
||||
text-overflow: ellipsis;
|
||||
-o-text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the drop-down menu of #dashboard-selection-menu
|
||||
#dashboard-switch-menu {
|
||||
> li {
|
||||
@@ -228,15 +227,15 @@ The dashboard page style
|
||||
border-bottom-left-radius: .3em;
|
||||
border-bottom-right-radius: .3em;
|
||||
}
|
||||
|
||||
#dashboard-news {
|
||||
padding-bottom: 60px;
|
||||
.news {
|
||||
margin-right: 2.4em;
|
||||
.mega-octicon {
|
||||
color: #CCC;
|
||||
}
|
||||
.avatar {
|
||||
margin: 0 1.2em;
|
||||
margin-right: 1.2em;
|
||||
}
|
||||
.news-content,
|
||||
.news-time {
|
||||
|
||||
@@ -1,54 +1,26 @@
|
||||
@import "../ui/var";
|
||||
|
||||
.repo-issue-wrapper {
|
||||
padding: 18px 0;
|
||||
}
|
||||
|
||||
// pull request main content
|
||||
.pr-main {
|
||||
padding-right: 40px;
|
||||
box-sizing: border-box;
|
||||
margin-right: 100px;
|
||||
}
|
||||
|
||||
// right bar in pull request page
|
||||
.pr-sidebar {
|
||||
border-left: 1px solid #DDD;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 12px;
|
||||
}
|
||||
|
||||
#pr-sidebar-nav {
|
||||
margin-top: 6px;
|
||||
li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
li > a {
|
||||
border: 1px solid transparent;
|
||||
border-left: none;
|
||||
&:hover {
|
||||
background-color: #FFF;
|
||||
border-color: #DDD;
|
||||
}
|
||||
}
|
||||
.label {
|
||||
font-size: 12px;
|
||||
line-height: 1.4em;
|
||||
margin-top: 1px;
|
||||
}
|
||||
li.current {
|
||||
a {
|
||||
background-color: #FFF;
|
||||
border-color: #DDD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pr-title {
|
||||
.pr-num {
|
||||
font-weight: normal;
|
||||
color: #888;
|
||||
}
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.pr-meta {
|
||||
color: #888;
|
||||
padding: 4px 0 8px 0;
|
||||
.pr-author {
|
||||
margin: 0 8px;
|
||||
color: #444;
|
||||
@@ -62,7 +34,6 @@
|
||||
padding: 4px 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.pr-nav {
|
||||
border-bottom: 1px solid #DDD;
|
||||
margin-top: 16px;
|
||||
@@ -89,7 +60,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.diff-bar {
|
||||
.diff-add {
|
||||
color: @btnGreenColor;
|
||||
@@ -114,26 +84,22 @@
|
||||
border-bottom-left-radius: .2em;
|
||||
}
|
||||
}
|
||||
|
||||
#pr-commit,
|
||||
#pr-file-diff,
|
||||
#issue-add-comment-preview {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#pr-conversation-list {
|
||||
padding-right: 30px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.issue-comment,
|
||||
.issue-commit,
|
||||
.issue-line,
|
||||
.issue-merge,
|
||||
.issue-add-comment {
|
||||
margin-bottom: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.issue-comment {
|
||||
.author-avatar {
|
||||
img {
|
||||
@@ -165,7 +131,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.issue-commit {
|
||||
line-height: 32px;
|
||||
i, .author-avatar img {
|
||||
@@ -181,7 +146,6 @@
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.issue-merge {
|
||||
.ico {
|
||||
width: 40px;
|
||||
@@ -202,7 +166,6 @@
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.issue-merge-ok {
|
||||
.ico {
|
||||
background-color: #65AD4E;
|
||||
@@ -217,12 +180,10 @@
|
||||
color: darken(#65AD4E, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
.issue-line {
|
||||
height: 4px;
|
||||
background-color: #E6E6E6;
|
||||
}
|
||||
|
||||
.issue-add-comment {
|
||||
.panel {
|
||||
margin-left: 60px;
|
||||
@@ -254,9 +215,152 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea#issue-add-content {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
height: 120px;
|
||||
resize: vertical;
|
||||
}
|
||||
// #issue list navigator
|
||||
#issue-list-nav {
|
||||
border-bottom: 1px solid #DDD;
|
||||
padding-bottom: 12px;
|
||||
li {
|
||||
> a {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
padding: .2em .8em;
|
||||
}
|
||||
}
|
||||
li.current {
|
||||
> a {
|
||||
background-color: #0079bc;
|
||||
color: #FAFAFA;
|
||||
}
|
||||
}
|
||||
li.right {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
// new issue button
|
||||
#issue-new {
|
||||
> a {
|
||||
padding: 0 !important;
|
||||
button {
|
||||
height: 29px;
|
||||
}
|
||||
}
|
||||
}
|
||||
// issue list menu
|
||||
#issue-list-menu {
|
||||
padding: 16px 0 12px 0;
|
||||
border-bottom: 1px solid #BBB;
|
||||
margin-bottom: 12px;
|
||||
.mark {
|
||||
a {
|
||||
color: #AAA;
|
||||
&:hover {
|
||||
color: #444;
|
||||
}
|
||||
}
|
||||
&.hover {
|
||||
a {
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .left {
|
||||
.mark {
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
> .right {
|
||||
.mark {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
// each issue list item
|
||||
#issue-list,
|
||||
#pull-list {
|
||||
.item {
|
||||
position: relative;
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px dashed #AAA;
|
||||
.title > .title-text {
|
||||
color: #444;
|
||||
font-size: 15px;
|
||||
margin: 0 6px;
|
||||
}
|
||||
.index-num {
|
||||
padding: .25em .6em;
|
||||
}
|
||||
}
|
||||
.comment {
|
||||
color: #666;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 0;
|
||||
}
|
||||
.issue-label {
|
||||
padding: .25em .6em;
|
||||
a {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
.desc {
|
||||
color: #999;
|
||||
a {
|
||||
color: #999;
|
||||
&:hover {
|
||||
color: #03a2ef;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// issue list pager
|
||||
#issue-list-pager {
|
||||
margin: 18px 0 24px 0;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
}
|
||||
// labels numbers
|
||||
#labels-num {
|
||||
margin-right: 1em;
|
||||
}
|
||||
// labels list
|
||||
#label-list {
|
||||
.right {
|
||||
margin-left: 1em;
|
||||
color: #999;
|
||||
i {
|
||||
margin-right: 3px;
|
||||
}
|
||||
line-height: 30px;
|
||||
&:hover {
|
||||
color: #444444;
|
||||
}
|
||||
}
|
||||
.num {
|
||||
margin-right: 3px;
|
||||
}
|
||||
.issue-num {
|
||||
margin-right: 4em;
|
||||
}
|
||||
.label {
|
||||
&:hover {
|
||||
color: #FFF;
|
||||
}
|
||||
i {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
.item {
|
||||
height: 30px;
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px dashed #AAA;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,13 @@
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
> pre {
|
||||
code {
|
||||
padding: 0.2em 0.5em;
|
||||
margin: 0;
|
||||
background-color: rgba(0,0,0,0.04);
|
||||
border-radius: 3px;
|
||||
}
|
||||
>pre {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
overflow: auto;
|
||||
@@ -35,6 +41,10 @@
|
||||
margin: 5px 0;
|
||||
padding: 10px;
|
||||
background-color: #f8f8f8;
|
||||
code {
|
||||
padding: 0;
|
||||
background-color: inherit;
|
||||
}
|
||||
}
|
||||
img {
|
||||
padding: 10px 0;
|
||||
|
||||
@@ -1,229 +1,232 @@
|
||||
@import "../ui/var";
|
||||
.org-header-alert .alert {
|
||||
margin-top: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.org-header {
|
||||
padding: 16px 0;
|
||||
background-color: #FFF;
|
||||
border-bottom: 1px solid #DDD;
|
||||
img {
|
||||
padding-right: 10px;
|
||||
}
|
||||
padding: 16px 0;
|
||||
background-color: #FFF;
|
||||
border-bottom: 1px solid #DDD;
|
||||
img {
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
#org-home-header {
|
||||
min-height: 100px;
|
||||
min-height: 100px;
|
||||
}
|
||||
#org-header {
|
||||
height: 48px;
|
||||
.org-name {
|
||||
padding-left: 10px;
|
||||
font-size: 1.4em;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
> div {
|
||||
> .menu-line {
|
||||
> li {
|
||||
&.right {
|
||||
> a {
|
||||
font-size: 1.2em;
|
||||
color: @dashboardHeaderLinkColor;
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
color: @dashboardHeaderLinkHoverColor;
|
||||
}
|
||||
.octicon {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
.current {
|
||||
border-bottom: 2px solid #D26911;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
height: 48px;
|
||||
.org-name {
|
||||
padding-left: 10px;
|
||||
font-size: 1.4em;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
> div {
|
||||
> .menu-line {
|
||||
> li {
|
||||
&.right {
|
||||
> a {
|
||||
font-size: 1.2em;
|
||||
color: @dashboardHeaderLinkColor;
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
color: @dashboardHeaderLinkHoverColor;
|
||||
}
|
||||
.octicon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
.label{
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
.current {
|
||||
border-bottom: 2px solid #D26911;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#org-home-header-info {
|
||||
padding-top: 10px;
|
||||
h2 {
|
||||
font-size: 30px;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
li {
|
||||
float: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
padding-top: 10px;
|
||||
h2 {
|
||||
font-size: 30px;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
li {
|
||||
float: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
#org-home-repo-list {
|
||||
padding: 10px 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
#org-repo-list {
|
||||
padding: 10px 0;
|
||||
.org-repo-item {
|
||||
border-top: 1px solid #eee;
|
||||
padding: 30px 20px;
|
||||
.org-repo-status {
|
||||
list-style: none;
|
||||
color: #888;
|
||||
li {
|
||||
float: left;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
h2 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.org-repo-description {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
.org-repo-updated {
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
margin: 5px 0 0;
|
||||
color: #808080;
|
||||
}
|
||||
}
|
||||
padding: 10px 0;
|
||||
.org-repo-item {
|
||||
border-top: 1px solid #eee;
|
||||
padding: 30px 20px;
|
||||
.org-repo-status {
|
||||
list-style: none;
|
||||
color: #888;
|
||||
li {
|
||||
float: left;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
h2 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.org-repo-description {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
.org-repo-updated {
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
margin: 5px 0 0;
|
||||
color: #808080;
|
||||
}
|
||||
}
|
||||
}
|
||||
.org-sidebar {
|
||||
margin: -80px 0 0 20px;
|
||||
.panel-footer {
|
||||
padding: .8em 1.2em;
|
||||
}
|
||||
.member-avatar-group {
|
||||
padding: 15px;
|
||||
img {
|
||||
width: 59px;
|
||||
height: 59px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
margin: -80px 0 0 20px;
|
||||
.panel-footer {
|
||||
padding: .8em 1.2em;
|
||||
}
|
||||
.member-avatar-group {
|
||||
padding: 15px;
|
||||
img {
|
||||
width: 59px;
|
||||
height: 59px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
#org-home-team-list {
|
||||
padding: 0 15px;
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-top: 10px;
|
||||
li {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
padding: 0 15px;
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-top: 10px;
|
||||
li {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.team-name {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.team-meta {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
color: #777;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
color: #777;
|
||||
}
|
||||
.org-toolbar {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
#org-member-list {
|
||||
.org-member-item {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 15px 20px;
|
||||
.member-name {
|
||||
padding-left: 15px;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
li {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.org-member-item {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 15px 20px;
|
||||
.member-name {
|
||||
padding-left: 15px;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
li {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.invite-box {
|
||||
padding: 50px 0;
|
||||
min-height: 130px;
|
||||
margin: 0 auto;
|
||||
width: 50%;
|
||||
input {
|
||||
width: 300px;
|
||||
}
|
||||
padding: 50px 0;
|
||||
min-height: 130px;
|
||||
margin: 0 auto;
|
||||
width: 50%;
|
||||
input {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
#org-member-list-block {
|
||||
padding-top: 2px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
.org-team-list {
|
||||
.org-team-list-item {
|
||||
float: left;
|
||||
padding: 15px;
|
||||
width: 555px;
|
||||
.member-avatar-group {
|
||||
padding: 5px 15px;
|
||||
img {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.org-team-list-item {
|
||||
float: left;
|
||||
padding: 15px;
|
||||
width: 555px;
|
||||
.member-avatar-group {
|
||||
padding: 5px 15px;
|
||||
img {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#team-create-form {
|
||||
.note {
|
||||
margin-left: 153px;
|
||||
}
|
||||
.note {
|
||||
margin-left: 153px;
|
||||
}
|
||||
}
|
||||
#org-team-card {
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
.team-stats {
|
||||
padding: 0 20px 10px 20px;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
.panel-footer {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
.team-stats {
|
||||
padding: 0 20px 10px 20px;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
.panel-footer {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
}
|
||||
#team-repositories-list,
|
||||
#team-members-list {
|
||||
.panel-body .search {
|
||||
padding: 4px 0 10px 10px;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
li {
|
||||
&.collab {
|
||||
padding-top: 10px !important;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
.panel-body .search {
|
||||
padding: 4px 0 10px 10px;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
li {
|
||||
&.collab {
|
||||
padding-top: 10px !important;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
#team-repositories-list {
|
||||
li {
|
||||
a .octicon {
|
||||
color: #888;
|
||||
}
|
||||
.member {
|
||||
color: @linkColor;
|
||||
font-size: 14px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
li {
|
||||
a .octicon {
|
||||
color: #888;
|
||||
}
|
||||
.member {
|
||||
color: @linkColor;
|
||||
font-size: 14px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
public/ng/less/gogs/profile.less
Normal file
59
public/ng/less/gogs/profile.less
Normal file
@@ -0,0 +1,59 @@
|
||||
.profile-avatar {
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
#profile-name {
|
||||
padding: 10px 0;
|
||||
}
|
||||
#profile-fullname {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
#profile-username {
|
||||
font-size: 1.6em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.profile-info {
|
||||
// padding: 0 50px;
|
||||
font-size: 14px;
|
||||
ul {
|
||||
padding-bottom: 10px;
|
||||
.list-group-item {
|
||||
background-color: transparent;
|
||||
padding-top: 5px;
|
||||
color: #666;
|
||||
}
|
||||
.profile-rel {
|
||||
width: 31%;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
strong {
|
||||
display: block;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
p {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#profile-header {
|
||||
li {
|
||||
a {
|
||||
font-size: 1.2em;
|
||||
color: #444444;
|
||||
padding: .4em .8em;
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
color: @dashboardHeaderLinkHoverColor;
|
||||
}
|
||||
}
|
||||
.current {
|
||||
border-bottom: 2px solid #D26911;
|
||||
}
|
||||
}
|
||||
}
|
||||
#profile-body {
|
||||
margin-left: 10px;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -68,6 +68,7 @@
|
||||
#repo-hooks-panel,
|
||||
#repo-hooks-history-panel,
|
||||
#user-social-panel,
|
||||
#user-applications-panel,
|
||||
#user-ssh-panel {
|
||||
margin-bottom: 20px;
|
||||
.setting-list {
|
||||
@@ -97,16 +98,20 @@
|
||||
margin-right: 4px;
|
||||
}
|
||||
.print,
|
||||
.access,
|
||||
.activity {
|
||||
color: #888;
|
||||
}
|
||||
.access {
|
||||
max-width: 500px;
|
||||
}
|
||||
}
|
||||
.ssh-delete-btn {
|
||||
.ssh-btn {
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
#user-ssh-add-form {
|
||||
.form-settings-add {
|
||||
.panel-body {
|
||||
background-color: #FFF;
|
||||
padding: 30px 0;
|
||||
|
||||
@@ -7,4 +7,5 @@
|
||||
@import "ui/label";
|
||||
@import "ui/bread";
|
||||
@import "ui/alert";
|
||||
@import "ui/table";
|
||||
@import "ui/table";
|
||||
@import "ui/pager";
|
||||
@@ -41,7 +41,15 @@
|
||||
border: 1px solid darken(@alertOrangeColor,10%);
|
||||
background-color: lighten(@alertOrangeColor,45%);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.white-popup-block {
|
||||
background: #FFF;
|
||||
padding: 20px 30px;
|
||||
text-align: left;
|
||||
max-width: 650px;
|
||||
margin: 40px auto;
|
||||
position: relative;
|
||||
p {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
@import "var";
|
||||
|
||||
// colored buttons
|
||||
// Button.
|
||||
.btn {
|
||||
&:hover {
|
||||
}
|
||||
white-space: nowrap;
|
||||
}
|
||||
.btn-small {
|
||||
font-size: 0.9*@baseFontSize;
|
||||
@@ -102,6 +101,9 @@
|
||||
box-sizing: content-box;
|
||||
text-align: center;
|
||||
}
|
||||
.btn-comb {
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
.btn-disabled {
|
||||
opacity: .6;
|
||||
@@ -116,25 +118,24 @@
|
||||
}
|
||||
|
||||
// input form elements
|
||||
|
||||
.ipt {
|
||||
&:focus {
|
||||
border-color: @iptFocusBorderColor;
|
||||
}
|
||||
&:focus {
|
||||
border-color: @iptFocusBorderColor;
|
||||
}
|
||||
}
|
||||
|
||||
.ipt-radius {
|
||||
border-radius: .25em;
|
||||
border-radius: .25em;
|
||||
}
|
||||
|
||||
.ipt-small {
|
||||
font-size: .8*@baseFontSize;
|
||||
font-size: .8*@baseFontSize;
|
||||
}
|
||||
|
||||
.ipt-large {
|
||||
font-size: 1.2*@baseFontSize;
|
||||
font-size: 1.2*@baseFontSize;
|
||||
}
|
||||
.ipt-textarea {
|
||||
height: auto !important;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.ipt-disabled,
|
||||
input[disabled] {
|
||||
background-color: @iptDisabledColor !important;
|
||||
@@ -144,14 +145,12 @@ input[disabled] {
|
||||
color: #888;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.ipt-readonly,
|
||||
input[readonly] {
|
||||
&:focus {
|
||||
background-color: @iptDisabledColor !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ipt-error {
|
||||
border-color: @iptErrorBorderColor !important;
|
||||
background-color: @iptErrorFocusColor !important;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import "var";
|
||||
|
||||
.label {
|
||||
padding: 2px 6px;
|
||||
padding: .3em .6em .2em .6em;
|
||||
color: @labelFontColor;
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
.label-gray {
|
||||
background-color: @labelGrayColor;
|
||||
}
|
||||
|
||||
.label-green {
|
||||
background-color: @labelGreenColor;
|
||||
background-color: @labelGreenColor;
|
||||
&:hover {
|
||||
background-color: @btnHoverGreenColor;
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
|
||||
.label-orange {
|
||||
background-color: @labelOrangeColor;
|
||||
}
|
||||
|
||||
28
public/ng/less/ui/pager.less
Normal file
28
public/ng/less/ui/pager.less
Normal file
@@ -0,0 +1,28 @@
|
||||
@import "var";
|
||||
|
||||
.pager{
|
||||
.page{
|
||||
padding: .3em .5em .2em .5em;
|
||||
margin: 0 .3em;
|
||||
background-color: @pagerPageBgColor;
|
||||
color: @pagerPageColor;
|
||||
&:hover,&.hover{
|
||||
background-color: @pagerHoverBgColor;
|
||||
color: @pagerHoverColor;
|
||||
}
|
||||
}
|
||||
.prev,.next{
|
||||
padding: .4em .6em;
|
||||
margin: 0 .3em;
|
||||
background-color: @pagerDirectionBgColor;
|
||||
color: @pagerDirectionColor;
|
||||
&.invalid{
|
||||
background-color: @pagerInvalidBgColor;
|
||||
color: @pagerInvalidColor;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
.page-radius{
|
||||
border-radius: .2em;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user