mirror of
https://github.com/gogs/gogs.git
synced 2026-02-28 09:10:57 +01:00
Compare commits
157 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e61ec316e | ||
|
|
13e35398aa | ||
|
|
9f4e584122 | ||
|
|
d5c6b53571 | ||
|
|
f0ca16d78f | ||
|
|
9b0858b1ad | ||
|
|
c73e9057ae | ||
|
|
fd70f9ec1b | ||
|
|
0b56272c13 | ||
|
|
677b1ec627 | ||
|
|
3ea1443885 | ||
|
|
b6272d1803 | ||
|
|
40de2f78c4 | ||
|
|
aa2148a7a9 | ||
|
|
bd555551ce | ||
|
|
e1c5008238 | ||
|
|
0f1ff9b1ad | ||
|
|
52cc58fd9d | ||
|
|
477bea574a | ||
|
|
37566f71a9 | ||
|
|
afdb0c7f9d | ||
|
|
63c1f9a23f | ||
|
|
e948c7c262 | ||
|
|
81a44e4cd0 | ||
|
|
f059866a21 | ||
|
|
234a7c19a4 | ||
|
|
35dd41c3a2 | ||
|
|
afc659442d | ||
|
|
6a6636d451 | ||
|
|
46742a79ca | ||
|
|
d61def86e1 | ||
|
|
30c750a5df | ||
|
|
bae1d65564 | ||
|
|
995a805a31 | ||
|
|
c0cfd62b90 | ||
|
|
ebbe6177a9 | ||
|
|
97b39ae2e4 | ||
|
|
0f77ad219c | ||
|
|
8b31be43c6 | ||
|
|
21dbcb7c77 | ||
|
|
0d7bb9af46 | ||
|
|
86e9ebdcc9 | ||
|
|
350e0080e6 | ||
|
|
66e2016eeb | ||
|
|
030b3d751e | ||
|
|
79537467da | ||
|
|
a18decf4cc | ||
|
|
20b5c23a19 | ||
|
|
e6e2cf7855 | ||
|
|
c884ecfea1 | ||
|
|
edbe1de026 | ||
|
|
2321b4b272 | ||
|
|
007cf33e88 | ||
|
|
b231b8c927 | ||
|
|
d01e7b0173 | ||
|
|
bb267e30b6 | ||
|
|
eca42bcb44 | ||
|
|
5ffeca35e7 | ||
|
|
87be137b88 | ||
|
|
0a697517ac | ||
|
|
c4820f119d | ||
|
|
57b3be4016 | ||
|
|
b033f2f535 | ||
|
|
f34b04cfc0 | ||
|
|
ec71d538fc | ||
|
|
6919c80f0b | ||
|
|
99599c099f | ||
|
|
d01f2f3c22 | ||
|
|
1a5aa5e0c0 | ||
|
|
9803c421f5 | ||
|
|
792ec63c8a | ||
|
|
c9e0b3b987 | ||
|
|
28766479a7 | ||
|
|
9ac940d31d | ||
|
|
b553ea45ee | ||
|
|
ac4a10456e | ||
|
|
2f3a7e53cb | ||
|
|
42c7bb7529 | ||
|
|
35140f1cc7 | ||
|
|
4f4392b83e | ||
|
|
db6c0ebf76 | ||
|
|
cf7ebfbdc8 | ||
|
|
bc8721fb6c | ||
|
|
01ba771783 | ||
|
|
9ee80e3e54 | ||
|
|
b2c6a6920f | ||
|
|
e321469884 | ||
|
|
b7ebbb4064 | ||
|
|
9a1fe801e5 | ||
|
|
6f71632e3e | ||
|
|
2844674587 | ||
|
|
0daef29053 | ||
|
|
21081836ba | ||
|
|
a2f6e1803b | ||
|
|
39c068400e | ||
|
|
47e7175b80 | ||
|
|
0b785ad967 | ||
|
|
069486d169 | ||
|
|
298ebc58c1 | ||
|
|
3fd41d138c | ||
|
|
35b02997f8 | ||
|
|
adc1ac689e | ||
|
|
528c075ad6 | ||
|
|
e577f2fff3 | ||
|
|
daf96dfae1 | ||
|
|
d4a1d9f82a | ||
|
|
f7f4ea1dcf | ||
|
|
0af3a5b603 | ||
|
|
00cf3e4dab | ||
|
|
cb6be94358 | ||
|
|
54ef1cee1d | ||
|
|
b9999427a8 | ||
|
|
ddeb076890 | ||
|
|
d06d58e461 | ||
|
|
48bb0fadc2 | ||
|
|
cd627ba16e | ||
|
|
d0a80e432d | ||
|
|
9558999698 | ||
|
|
5338808600 | ||
|
|
82da024a4d | ||
|
|
5c866fc737 | ||
|
|
d75013a0e8 | ||
|
|
1591a37ad5 | ||
|
|
3e528f34af | ||
|
|
b6437b5a4c | ||
|
|
db4951bc61 | ||
|
|
d6132aaa88 | ||
|
|
9adc8e9d3f | ||
|
|
be54b4bf26 | ||
|
|
64d90a761b | ||
|
|
4d123d0a93 | ||
|
|
5dd8308686 | ||
|
|
9c2120eb34 | ||
|
|
4f8d888e66 | ||
|
|
ca09a0b516 | ||
|
|
a43164877c | ||
|
|
515641d033 | ||
|
|
54d25c13d7 | ||
|
|
23d53561d1 | ||
|
|
0cce61de3a | ||
|
|
5b96e3fcc7 | ||
|
|
79dae254cf | ||
|
|
5b32cdd960 | ||
|
|
f9ad8d6903 | ||
|
|
1b66600bd0 | ||
|
|
dc53270da9 | ||
|
|
8ea7ba3afa | ||
|
|
ef275ebf62 | ||
|
|
22ab4fa1b0 | ||
|
|
55dfe2c978 | ||
|
|
3c3f7c2a56 | ||
|
|
0c92da7e9c | ||
|
|
8fac41768c | ||
|
|
c418b83678 | ||
|
|
b53f6357fc | ||
|
|
11ca3dedfb | ||
|
|
f77680520f |
37
.gopmfile
37
.gopmfile
@@ -3,30 +3,31 @@ path = github.com/gogits/gogs
|
||||
|
||||
[deps]
|
||||
github.com/beego/memcache = commit:2aea774416
|
||||
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: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: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/binding = commit:0e23661e7d
|
||||
github.com/macaron-contrib/cache = commit:0bb9e6c9ef
|
||||
github.com/Unknwon/com = commit:d9bcf409c8
|
||||
github.com/Unknwon/i18n = commit:1e88666229
|
||||
github.com/Unknwon/macaron = commit:256ba0beab
|
||||
github.com/codegangsta/cli = commit:a14c5b47c7
|
||||
github.com/go-sql-driver/mysql = commit:04cf947760
|
||||
github.com/go-xorm/core = commit:e7882d8b00
|
||||
github.com/go-xorm/xorm = commit:dcc529b68a
|
||||
github.com/gogits/go-gogs-client = commit:92e76d616a
|
||||
github.com/lib/pq = commit:3e3efe51a0
|
||||
github.com/macaron-contrib/binding = commit:0fbe4b9707
|
||||
github.com/macaron-contrib/cache = commit:1d99a5b621
|
||||
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/csrf = commit:3ea14e7ee7
|
||||
github.com/macaron-contrib/i18n = commit:0ee0539c84
|
||||
github.com/macaron-contrib/oauth2 = commit:8f394c3629
|
||||
github.com/macaron-contrib/session = commit:2633cef14f
|
||||
github.com/macaron-contrib/toolbox = commit:57127bcc89
|
||||
github.com/mattn/go-sqlite3 = commit:a80c27ba33
|
||||
github.com/nfnt/resize = commit:581d15cb53
|
||||
github.com/nfnt/resize = commit:8f44931448
|
||||
github.com/russross/blackfriday = commit:05b8cefd6a
|
||||
github.com/shurcooL/go = commit:48293cbc7a
|
||||
github.com/saintfish/chardet = commit:3af4cd4741
|
||||
gopkg.in/ini.v1 = commit:28ad8c408b
|
||||
gopkg.in/redis.v2 = commit:e617904962
|
||||
|
||||
[res]
|
||||
include = conf|etc|public|scripts|templates
|
||||
|
||||
@@ -2,10 +2,7 @@ targets:
|
||||
ubuntu-14.04:
|
||||
ubuntu-12.04:
|
||||
debian-7:
|
||||
centos6:
|
||||
build_dependencies:
|
||||
- mercurial
|
||||
- bzr
|
||||
centos-6:
|
||||
dependencies:
|
||||
- git
|
||||
before:
|
||||
|
||||
@@ -2,4 +2,6 @@ language: go
|
||||
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.3
|
||||
|
||||
sudo: false
|
||||
|
||||
@@ -64,8 +64,17 @@ Before open any new issue, please check your problem on [Troubleshooting](http:/
|
||||
|
||||
Please take a moment to check that an issue on [GitHub](https://github.com/gogits/gogs/issues) or card on [Trello](https://trello.com/b/uxAoeLUl/gogs-go-git-service) doesn't already exist documenting your bug report or improvement proposal. If it does, it never hurts to add a quick "+1" or "I have this problem too". This will help prioritize the most common problems and requests.
|
||||
|
||||
### Discuss your design on the mailing list
|
||||
## Code of conduct
|
||||
|
||||
We recommend discussing your plans [on the mailing list](https://groups.google.com/forum/#!forum/gogits) before starting to code - especially for more ambitious contributions. This gives other contributors a chance to point you in the right direction, give feedback on your design, and maybe point out if someone else is working on the same thing.
|
||||
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
||||
|
||||
We may close your pull request if not first discussed on the mailing list. We aren't doing this to be jerks. We are doing this to prevent people from spending large amounts of time on changes that may need to be designed or architected in a specific way, or may not align with the vision of the project.
|
||||
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
||||
|
||||
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior can be
|
||||
reported by emailing contact@gitlab.com
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
||||
12
README.md
12
README.md
@@ -1,11 +1,13 @@
|
||||
Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://travis-ci.org/gogits/gogs)
|
||||
=====================
|
||||
|
||||
[](https://gitter.im/gogits/gogs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
Gogs(Go Git Service) is a painless self-hosted Git Service written in Go.
|
||||
|
||||

|
||||
|
||||
##### Current version: 0.5.8 Beta
|
||||
##### Current version: 0.5.11 Beta
|
||||
|
||||
### NOTICES
|
||||
|
||||
@@ -26,6 +28,7 @@ The goal of this project is to make the easiest, fastest and most painless way t
|
||||
- See [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team.
|
||||
- Try it before anything? Do it [online](https://try.gogs.io/Unknown/gogs) or go down to **Installation -> Install from binary** section!
|
||||
- Having troubles? Get help from [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.md).
|
||||
- Want to help on localization? Check out [Crowdin](https://crowdin.com/project/gogs)!
|
||||
|
||||
## Features
|
||||
|
||||
@@ -47,7 +50,7 @@ The goal of this project is to make the easiest, fastest and most painless way t
|
||||
- Drone CI integration
|
||||
- Supports MySQL, PostgreSQL and SQLite3
|
||||
- Social account login(GitHub, Google, QQ, Weibo)
|
||||
- Multi-language support(English, Simplified Chinese, Traditional Chinese, Germany, French, Dutch, and [more](https://crowdin.com/project/gogs))
|
||||
- Multi-language support([7 languages](https://crowdin.com/project/gogs))
|
||||
|
||||
## System Requirements
|
||||
|
||||
@@ -71,14 +74,15 @@ There are 5 ways to install Gogs:
|
||||
- Router and middleware mechanism of [Macaron](https://github.com/Unknwon/macaron).
|
||||
- Mail Service, modules design is inspired by [WeTalk](https://github.com/beego/wetalk).
|
||||
- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
|
||||
- Usage and modification from [beego](http://beego.me) modules.
|
||||
- Thanks [lavachen](http://www.lavachen.cn/) and [Rocker](http://weibo.com/rocker1989) for designing Logo.
|
||||
- Thanks [gobuild.io](http://gobuild.io) for providing binary compile and download service.
|
||||
- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
|
||||
|
||||
## Contributors
|
||||
|
||||
The [core team](http://gogs.io/team) of this project. See [contributors page](https://github.com/gogits/gogs/graphs/contributors) for full list of contributors.
|
||||
- The [core team](http://gogs.io/team) of this project.
|
||||
- See [contributors page](https://github.com/gogits/gogs/graphs/contributors) for full list of contributors.
|
||||
- See [TRANSLATORS](conf/locale/TRANSLATORS) for full list of translators.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
12
README_ZH.md
12
README_ZH.md
@@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。
|
||||
|
||||

|
||||
|
||||
##### 当前版本:0.5.8 Beta
|
||||
##### 当前版本:0.5.11 Beta
|
||||
|
||||
## 开发目的
|
||||
|
||||
@@ -17,6 +17,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
- 您可以到 [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。
|
||||
- 想要先睹为快?通过 [在线体验](https://try.gogs.io/Unknown/gogs) 或查看 **安装部署 -> 二进制安装** 小节。
|
||||
- 使用过程中遇到问题?尝试从 [故障排查](http://gogs.io/docs/intro/troubleshooting.md) 页面获取帮助。
|
||||
- 希望帮助多国语言界面的翻译吗?请立即访问 [Crowdin](https://crowdin.com/project/gogs)!
|
||||
|
||||
## 功能特性
|
||||
|
||||
@@ -38,7 +39,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
- Drone CI 持续部署集成
|
||||
- 支持 MySQL、PostgreSQL 以及 SQLite3 数据库
|
||||
- 社交帐号登录(GitHub、Google、QQ、微博)
|
||||
- 多语言支持(英文、简体中文、繁体中文、德语、法语、荷兰语以及 [更多]([more](https://crowdin.com/project/gogs)))
|
||||
- 多语言支持([7 种语言]([more](https://crowdin.com/project/gogs)))
|
||||
|
||||
## 系统要求
|
||||
|
||||
@@ -59,8 +60,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
|
||||
## 特别鸣谢
|
||||
|
||||
- [Macaron](https://github.com/Unknwon/macaron) 的路由与中间件机制。
|
||||
- [beego](http://beego.me) 模块的使用与修改。
|
||||
- 基于 [Macaron](https://github.com/Unknwon/macaron) 的路由与中间件机制。
|
||||
- 基于 [WeTalk](https://github.com/beego/wetalk) 修改的邮件服务和模块设计。
|
||||
- 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。
|
||||
- 感谢 [gobuild.io](http://gobuild.io) 提供二进制编译与下载服务。
|
||||
@@ -69,7 +69,9 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
|
||||
## 贡献成员
|
||||
|
||||
本项目的 [开发团队](http://gogs.io/team)。您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。
|
||||
- 本项目的 [开发团队](http://gogs.io/team)。
|
||||
- 您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。
|
||||
- 您可以通过查看 [TRANSLATORS](conf/locale/TRANSLATORS) 文件获取完整的翻译人员列表。
|
||||
|
||||
## 授权许可
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
@@ -31,4 +32,5 @@ Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
|
||||
|
||||
func runCert(ctx *cli.Context) {
|
||||
fmt.Println("Command cert not available, please use build tags 'cert' to rebuild.")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func runDump(ctx *cli.Context) {
|
||||
workDir, _ := setting.WorkDir()
|
||||
z.AddFile("gogs-repo.zip", path.Join(workDir, "gogs-repo.zip"))
|
||||
z.AddFile("gogs-db.sql", path.Join(workDir, "gogs-db.sql"))
|
||||
z.AddFile("custom/conf/app.ini", path.Join(workDir, "custom/conf/app.ini"))
|
||||
z.AddDir("custom", path.Join(workDir, "custom"))
|
||||
z.AddDir("log", path.Join(workDir, "log"))
|
||||
// FIXME: SSH key file.
|
||||
if err = z.Close(); err != nil {
|
||||
|
||||
11
cmd/fix.go
11
cmd/fix.go
@@ -14,6 +14,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/codegangsta/cli"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
@@ -164,10 +165,12 @@ func runFixLocation(ctx *cli.Context) {
|
||||
|
||||
// Fix in authorized_keys file.
|
||||
sshPath := path.Join(models.SshPath, "authorized_keys")
|
||||
fmt.Printf("Fixing pathes in file: %s\n", sshPath)
|
||||
if err := rewriteAuthorizedKeys(sshPath, oldPath, execPath); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
if com.IsFile(sshPath) {
|
||||
fmt.Printf("Fixing pathes in file: %s\n", sshPath)
|
||||
if err := rewriteAuthorizedKeys(sshPath, oldPath, execPath); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Fix position in gogs-repositories.
|
||||
|
||||
@@ -164,7 +164,7 @@ func runServ(k *cli.Context) {
|
||||
log.GitLogger.Fatal(2, "User %s has no right to read repository %s", user.Name, repoPath)
|
||||
}
|
||||
default:
|
||||
println("Unknown command")
|
||||
println("Unknown command: " + cmd)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
94
cmd/web.go
94
cmd/web.go
@@ -5,6 +5,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
@@ -21,8 +22,12 @@ import (
|
||||
"github.com/macaron-contrib/captcha"
|
||||
"github.com/macaron-contrib/csrf"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
"github.com/macaron-contrib/oauth2"
|
||||
"github.com/macaron-contrib/session"
|
||||
"github.com/macaron-contrib/toolbox"
|
||||
"gopkg.in/ini.v1"
|
||||
|
||||
api "github.com/gogits/go-gogs-client"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/auth"
|
||||
@@ -51,6 +56,12 @@ and it takes care of all the other things for you`,
|
||||
Flags: []cli.Flag{},
|
||||
}
|
||||
|
||||
type VerChecker struct {
|
||||
ImportPath string
|
||||
Version func() string
|
||||
Expected string
|
||||
}
|
||||
|
||||
// checkVersion checks if binary matches the version of templates files.
|
||||
func checkVersion() {
|
||||
// Templates.
|
||||
@@ -63,17 +74,20 @@ func checkVersion() {
|
||||
}
|
||||
|
||||
// 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)")
|
||||
checkers := []VerChecker{
|
||||
{"github.com/Unknwon/macaron", macaron.Version, "0.4.9"},
|
||||
{"github.com/macaron-contrib/binding", binding.Version, "0.0.4"},
|
||||
{"github.com/macaron-contrib/cache", cache.Version, "0.0.5"},
|
||||
{"github.com/macaron-contrib/csrf", csrf.Version, "0.0.1"},
|
||||
{"github.com/macaron-contrib/i18n", i18n.Version, "0.0.5"},
|
||||
{"github.com/macaron-contrib/session", session.Version, "0.1.4"},
|
||||
{"gopkg.in/ini.v1", ini.Version, "1.0.1"},
|
||||
}
|
||||
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)")
|
||||
for _, c := range checkers {
|
||||
ver := strings.Join(strings.Split(c.Version(), ".")[:3], ".")
|
||||
if git.MustParseVersion(ver).LessThan(git.MustParseVersion(c.Expected)) {
|
||||
log.Fatal(4, "Package '%s' version is too old(%s -> %s), did you forget to update?", c.ImportPath, ver, c.Expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +108,13 @@ func newMacaron() *macaron.Macaron {
|
||||
SkipLogging: !setting.DisableRouterLog,
|
||||
},
|
||||
))
|
||||
m.Use(macaron.Static(
|
||||
setting.AvatarUploadPath,
|
||||
macaron.StaticOptions{
|
||||
Prefix: "avatars",
|
||||
SkipLogging: !setting.DisableRouterLog,
|
||||
},
|
||||
))
|
||||
m.Use(macaron.Renderer(macaron.RenderOptions{
|
||||
Directory: path.Join(setting.StaticRootPath, "templates"),
|
||||
Funcs: []template.FuncMap{base.TemplateFuncs},
|
||||
@@ -108,18 +129,15 @@ func newMacaron() *macaron.Macaron {
|
||||
Redirect: true,
|
||||
}))
|
||||
m.Use(cache.Cacher(cache.Options{
|
||||
Adapter: setting.CacheAdapter,
|
||||
Interval: setting.CacheInternal,
|
||||
Conn: setting.CacheConn,
|
||||
Adapter: setting.CacheAdapter,
|
||||
AdapterConfig: setting.CacheConn,
|
||||
Interval: setting.CacheInternal,
|
||||
}))
|
||||
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{
|
||||
m.Use(session.Sessioner(setting.SessionConfig))
|
||||
m.Use(csrf.Csrfer(csrf.Options{
|
||||
Secret: setting.SecretKey,
|
||||
SetCookie: true,
|
||||
Header: "X-Csrf-Token",
|
||||
@@ -133,6 +151,13 @@ func newMacaron() *macaron.Macaron {
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
// OAuth 2.
|
||||
if setting.OauthService != nil {
|
||||
for _, info := range setting.OauthService.OauthInfos {
|
||||
m.Use(oauth2.NewOAuth2Provider(info.Options, info.AuthUrl, info.TokenUrl))
|
||||
}
|
||||
}
|
||||
m.Use(middleware.Contexter())
|
||||
return m
|
||||
}
|
||||
@@ -154,6 +179,7 @@ func runWeb(*cli.Context) {
|
||||
// Routers.
|
||||
m.Get("/", ignSignIn, routers.Home)
|
||||
m.Get("/explore", ignSignIn, routers.Explore)
|
||||
// FIXME: when i'm binding form here???
|
||||
m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install)
|
||||
m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost)
|
||||
m.Group("", func() {
|
||||
@@ -183,14 +209,15 @@ func runWeb(*cli.Context) {
|
||||
})
|
||||
|
||||
// Repositories.
|
||||
m.Get("/user/repos", middleware.ApiReqToken(), v1.ListMyRepos)
|
||||
m.Combo("/user/repos", middleware.ApiReqToken()).Get(v1.ListMyRepos).Post(bind(api.CreateRepoOption{}), v1.CreateRepo)
|
||||
m.Post("/org/:org/repos", middleware.ApiReqToken(), bind(api.CreateRepoOption{}), v1.CreateOrgRepo)
|
||||
m.Group("/repos", func() {
|
||||
m.Get("/search", v1.SearchRepos)
|
||||
m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), v1.Migrate)
|
||||
m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), v1.MigrateRepo)
|
||||
|
||||
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.Combo("/hooks").Get(v1.ListRepoHooks).Post(bind(api.CreateHookOption{}), v1.CreateRepoHook)
|
||||
m.Patch("/hooks/:id:int", bind(api.EditHookOption{}), v1.EditRepoHook)
|
||||
m.Get("/raw/*", middleware.RepoRef(), v1.GetRepoRawFile)
|
||||
}, middleware.ApiRepoAssignment(), middleware.ApiReqToken())
|
||||
})
|
||||
@@ -205,7 +232,7 @@ func runWeb(*cli.Context) {
|
||||
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("/info/:name", user.SocialSignIn)
|
||||
m.Get("/sign_up", user.SignUp)
|
||||
m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
|
||||
m.Get("/reset_password", user.ResetPasswd)
|
||||
@@ -214,6 +241,9 @@ func runWeb(*cli.Context) {
|
||||
m.Group("/user/settings", func() {
|
||||
m.Get("", user.Settings)
|
||||
m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost)
|
||||
m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), user.SettingsAvatar)
|
||||
m.Get("/email", user.SettingsEmails)
|
||||
m.Post("/email", bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost)
|
||||
m.Get("/password", user.SettingsPassword)
|
||||
m.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost)
|
||||
m.Get("/ssh", user.SettingsSSHKeys)
|
||||
@@ -225,6 +255,7 @@ func runWeb(*cli.Context) {
|
||||
m.Group("/user", func() {
|
||||
// r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds)
|
||||
m.Any("/activate", user.Activate)
|
||||
m.Any("/activate_email", user.ActivateEmail)
|
||||
m.Get("/email2user", user.Email2User)
|
||||
m.Get("/forget_password", user.ForgotPasswd)
|
||||
m.Post("/forget_password", user.ForgotPasswdPost)
|
||||
@@ -380,14 +411,17 @@ func runWeb(*cli.Context) {
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/new", repo.NewRelease)
|
||||
m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
|
||||
m.Get("/edit/:tagname", repo.EditRelease)
|
||||
m.Post("/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
|
||||
}, middleware.RepoRef())
|
||||
}, reqSignIn, middleware.RepoAssignment(true))
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Get("/releases", repo.Releases)
|
||||
m.Get("/releases", middleware.RepoRef(), repo.Releases)
|
||||
m.Get("/issues", repo.Issues)
|
||||
m.Get("/issues/:index", repo.ViewIssue)
|
||||
m.Get("/issues/milestones", repo.Milestones)
|
||||
@@ -397,6 +431,7 @@ func runWeb(*cli.Context) {
|
||||
m.Get("/issues2/", repo.Issues2)
|
||||
m.Get("/pulls2/", repo.PullRequest2)
|
||||
m.Get("/labels2/", repo.Labels2)
|
||||
m.Get("/milestone2/", repo.Milestones2)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Get("/src/*", repo.Home)
|
||||
@@ -432,7 +467,8 @@ func runWeb(*cli.Context) {
|
||||
case setting.HTTP:
|
||||
err = http.ListenAndServe(listenAddr, m)
|
||||
case setting.HTTPS:
|
||||
err = http.ListenAndServeTLS(listenAddr, setting.CertFile, setting.KeyFile, m)
|
||||
server := &http.Server{Addr: listenAddr, TLSConfig: &tls.Config{MinVersion: tls.VersionTLS10}, Handler: m}
|
||||
err = server.ListenAndServeTLS(setting.CertFile, setting.KeyFile)
|
||||
case setting.FCGI:
|
||||
err = fcgi.Serve(nil, m)
|
||||
default:
|
||||
|
||||
43
conf/app.ini
43
conf/app.ini
@@ -22,6 +22,11 @@ DISABLE_ROUTER_LOG = false
|
||||
; Generate steps:
|
||||
; $ cd path/to/gogs/custom/https
|
||||
; $ ./gogs cert -ca=true -duration=8760h0m0s -host=myhost.example.com
|
||||
;
|
||||
; Or from a .pfx file exported from the Windows certificate store (do
|
||||
; not forget to export the private key):
|
||||
; $ openssl pkcs12 -in cert.pfx -out cert.pem -nokeys
|
||||
; $ openssl pkcs12 -in cert.pfx -out key.pem -nocerts -nodes
|
||||
CERT_FILE = custom/https/cert.pem
|
||||
KEY_FILE = custom/https/key.pem
|
||||
; Upper level of template and static file path
|
||||
@@ -29,6 +34,8 @@ KEY_FILE = custom/https/key.pem
|
||||
STATIC_ROOT_PATH =
|
||||
; Application level GZIP support
|
||||
ENABLE_GZIP = false
|
||||
; Landing page for non-logged users, can be "home" or "explore"
|
||||
LANDING_PAGE = home
|
||||
|
||||
[database]
|
||||
; Either "mysql", "postgres" or "sqlite3", it's your choice
|
||||
@@ -70,6 +77,7 @@ ENABLE_CACHE_AVATAR = false
|
||||
ENABLE_NOTIFY_MAIL = false
|
||||
; More detail: https://github.com/gogits/gogs/issues/165
|
||||
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
|
||||
ENABLE_REVERSE_PROXY_AUTO_REGISTERATION = false
|
||||
|
||||
[webhook]
|
||||
; Cron task interval in minutes
|
||||
@@ -86,8 +94,11 @@ SUBJECT = %(APP_NAME)s
|
||||
; Mail server
|
||||
; Gmail: smtp.gmail.com:587
|
||||
; QQ: smtp.qq.com:25
|
||||
; Note, if the port ends with "465", SMTPS will be used. Using STARTTLS on port 587 is recommended per RFC 6409. If the server supports STARTTLS it will always be used.
|
||||
HOST =
|
||||
; Mail from address
|
||||
; Do not verify the certificate of the server. Only use this for self-signed certificates
|
||||
SKIP_VERIFY =
|
||||
; Mail from address, RFC 5322. This can be just an email address, or the "Name" <email@example.com> format
|
||||
FROM =
|
||||
; Mailer user name and password
|
||||
USER =
|
||||
@@ -118,13 +129,10 @@ TOKEN_URL = https://accounts.google.com/o/oauth2/token
|
||||
ENABLED = false
|
||||
CLIENT_ID =
|
||||
CLIENT_SECRET =
|
||||
SCOPES = all
|
||||
SCOPES = get_user_info
|
||||
; QQ 互联
|
||||
; AUTH_URL = https://graph.qq.com/oauth2.0/authorize
|
||||
; TOKEN_URL = https://graph.qq.com/oauth2.0/token
|
||||
; Tencent weibo
|
||||
AUTH_URL = https://open.t.qq.com/cgi-bin/oauth2/authorize
|
||||
TOKEN_URL = https://open.t.qq.com/cgi-bin/oauth2/access_token
|
||||
AUTH_URL = https://graph.qq.com/oauth2.0/authorize
|
||||
TOKEN_URL = https://graph.qq.com/oauth2.0/token
|
||||
|
||||
[oauth.weibo]
|
||||
ENABLED = false
|
||||
@@ -146,7 +154,7 @@ HOST =
|
||||
|
||||
[session]
|
||||
; Either "memory", "file", "redis" or "mysql", default is "memory"
|
||||
PROVIDER = file
|
||||
PROVIDER = memory
|
||||
; Provider config options
|
||||
; memory: not have any config yet
|
||||
; file: session file path, e.g. `data/sessions`
|
||||
@@ -167,6 +175,7 @@ SESSION_LIFE_TIME = 86400
|
||||
[picture]
|
||||
; The place to picture data, either "server" or "qiniu", default is "server"
|
||||
SERVICE = server
|
||||
AVATAR_UPLOAD_PATH = data/avatars
|
||||
; Chinese users can choose "duoshuo"
|
||||
GRAVATAR_SOURCE = gravatar
|
||||
DISABLE_GRAVATAR = false
|
||||
@@ -251,8 +260,20 @@ DRIVER =
|
||||
CONN =
|
||||
|
||||
[git]
|
||||
MAX_GITDIFF_LINES = 10000
|
||||
MAX_GIT_DIFF_LINES = 10000
|
||||
; Arguments for command 'git gc', e.g.: "--aggressive --auto"
|
||||
; see more on http://git-scm.com/docs/git-gc/1.7.5
|
||||
GC_ARGS =
|
||||
|
||||
; Git health check.
|
||||
[git.fsck]
|
||||
ENABLE = true
|
||||
; Execution interval in hours. Default is 24.
|
||||
INTERVAL = 24
|
||||
; Arguments for command 'git fsck', e.g.: "--unreachable --tags"
|
||||
; see more on http://git-scm.com/docs/git-fsck/1.7.5
|
||||
ARGS =
|
||||
|
||||
[i18n]
|
||||
LANGS = en-US,zh-CN,zh-HK,de-DE,fr-CA,nl-NL
|
||||
NAMES = English,简体中文,繁體中文,Deutsch,Français,Nederlands
|
||||
LANGS = en-US,zh-CN,zh-HK,de-DE,fr-CA,nl-NL,lv-LV
|
||||
NAMES = English,简体中文,繁體中文,Deutsch,Français,Nederlands,Latviešu
|
||||
|
||||
6
conf/locale/TRANSLATORS
Normal file
6
conf/locale/TRANSLATORS
Normal file
@@ -0,0 +1,6 @@
|
||||
# This file lists all PUBLIC individuals having contributed content to the translation.
|
||||
# Order of name is meaningless.
|
||||
|
||||
Thomas Fanninger <gogs.thomas@fanninger.at>
|
||||
Łukasz Jan Niemier <lukasz@niemier.pl>
|
||||
Lafriks <lafriks@gmail.com>
|
||||
@@ -164,7 +164,7 @@ unable_verify_ssh_key=Gogs kann deinen SSH-Schlüssel nicht verifizieren, nimmt
|
||||
auth_failed=Authentifizierung fehlgeschlagen: %v
|
||||
|
||||
still_own_repo=Dein Konto besitzt noch Repositorys. Diese müssen zuerst gelöscht oder übertragen werden.
|
||||
still_has_org=Ihr Konto noch Mitgliedschaft in der Organisation, Sie nach links oder löschen Sie sie zuerst.
|
||||
still_has_org=Du bist noch Mitglied einer Organisation, bitte lösche zunächst diese Mitgliedschaft.
|
||||
org_still_own_repo=Diese Organisation besitzt noch Repositorys. Diese müssen zuerst gelöscht oder übertragen werden.
|
||||
|
||||
still_own_user=Diese Authentifizierung wird noch von einigen Benutzern genutzt. Entferne diese zuvor und lösche erneut.
|
||||
@@ -173,6 +173,7 @@ target_branch_not_exist=Ziel-Branch existiert nicht
|
||||
|
||||
[user]
|
||||
change_avatar=Ändere dein Profilbild auf gravatar.com
|
||||
change_custom_avatar=Ändere deinen Avatar in den Einstellungen
|
||||
join_on=Registriert
|
||||
repositories=Repositorys
|
||||
activity=Öffentliche Aktivität
|
||||
@@ -202,6 +203,14 @@ change_username_desc=Benutzername wurde geändert, möchtest du fortfahren? Dies
|
||||
continue=Weiter
|
||||
cancel=Abbrechen
|
||||
|
||||
enable_custom_avatar=Aktiviere benuztzerdefinierten Avatar
|
||||
enable_custom_avatar_helper=Aktiviere dies, um deinen Avatar nicht von Gravatar zu laden
|
||||
choose_new_avatar=Neuen Avatar auswählen
|
||||
update_avatar=Avatar-Einstellung aktualisieren
|
||||
uploaded_avatar_not_a_image=Die hochgeladene Datei ist kein Bild.
|
||||
no_custom_avatar_available=Kein benutzerdefinierter Avatar verfügbar, Aktivierung ist nicht möglich.
|
||||
update_avatar_success=Deine Avatar-Einstellung wurde aktualisiert.
|
||||
|
||||
change_password=Passwort ändern
|
||||
old_password=Aktuelles Passwort
|
||||
new_password=Neues Passwort
|
||||
@@ -368,6 +377,30 @@ diff.stats_desc=<strong> %d geänderte Dateien</strong> mit <strong>%d neuen Zei
|
||||
diff.bin=BIN
|
||||
diff.view_file=Datei anzeigen
|
||||
|
||||
release.releases=Releases
|
||||
release.new_release=Neues Release
|
||||
release.draft=Entwurf
|
||||
release.prerelease=Pre-Release
|
||||
release.stable=Endversion
|
||||
release.edit=bearbeiten
|
||||
release.ahead=<strong>%d</strong> Commits zu %s seit diesem Release
|
||||
release.source_code=Quelltext
|
||||
release.tag_name=Tag-Name
|
||||
release.target=Ziel
|
||||
release.tag_helper=Wähle ein neues Tag oder erstelle ein Tag beim Veröffentlichen.
|
||||
release.release_title=Release-Titel
|
||||
release.content_with_md=Inhalt mit <a href="%s">Markdown</a>
|
||||
release.write=Schreiben
|
||||
release.preview=Vorschau
|
||||
release.content_placeholder=Schreibe hier etwas
|
||||
release.loading=Laden…
|
||||
release.prerelease_desc=Dies ist eine Pre-Release-Version
|
||||
release.prerelease_helper=Wir möchten darauf hinweisen, dass dieses Release nicht für den produktiven Einsatz gedacht ist.
|
||||
release.publish=Release veröffentlichen
|
||||
release.save_draft=Entwurf speichern
|
||||
release.edit_release=Release bearbeiten
|
||||
release.tag_name_already_exist=Ein Release mit diesem Tag existiert bereits.
|
||||
|
||||
[org]
|
||||
org_name_holder=Name der Organisation
|
||||
org_name_helper=Gute Namen von Organisationen sind kurz und einprägsam.
|
||||
@@ -462,11 +495,13 @@ dashboard.operation_name=Name der Operation
|
||||
dashboard.operation_switch=Switch
|
||||
dashboard.operation_run=Ausführen
|
||||
dashboard.clean_unbind_oauth=ungebundene OAuths bereinigen
|
||||
dashboard.clean_unbind_oauth_success=Alle aufheben OAuthes erfolgreich gelöscht wurden.
|
||||
dashboard.clean_unbind_oauth_success=Alle ungebundenen OAuth-Tokens wurden gelöscht.
|
||||
dashboard.delete_inactivate_accounts=inaktive Konten löschen
|
||||
dashboard.delete_inactivate_accounts_success=Alle inaktive Konten erfolgreich gelöscht wurden.
|
||||
dashboard.delete_repo_archives=Alle Repositories Archive löschen
|
||||
dashboard.delete_repo_archives_success=Alle Repositories Archive wurden erfolgreich gelöscht.
|
||||
dashboard.delete_inactivate_accounts_success=Alle inaktiven Konten wurden erfolgreich gelöscht.
|
||||
dashboard.delete_repo_archives=Alle Repository-Archive löschen
|
||||
dashboard.delete_repo_archives_success=Alle Repositoriy-Archive wurden gelöscht.
|
||||
dashboard.git_gc_repos=Führe Garbage Collection auf Repositories aus
|
||||
dashboard.git_gc_repos_success=Garbage Collection wurde auf allen Repositories erfolgreich ausgeführt.
|
||||
dashboard.server_uptime=Server-Uptime
|
||||
dashboard.current_goroutine=Aktuelle Goroutines
|
||||
dashboard.current_memory_usage=Aktuelle Speichernutzung
|
||||
@@ -512,11 +547,11 @@ users.update_profile_success=Kontoprofil aktualisiert
|
||||
users.edit_account=Konto bearbeiten
|
||||
users.is_activated=Dieses Konto ist aktiviert
|
||||
users.is_admin=Dieses Konto hat Administratorrechte
|
||||
users.allow_git_hook=Dieses Konto verfügt über Berechtigungen zum Erstellen von Git hooks
|
||||
users.allow_git_hook=Dieses Konto ist berechtigt, Git-Hooks zu erstellen
|
||||
users.update_profile=Kontoprofil aktualisieren
|
||||
users.delete_account=Dieses Konto löschen
|
||||
users.still_own_repo=Dieses Konto besitzt noch Repositorys. Diese müssen zuerst gelöscht oder übertragen werden.
|
||||
users.still_has_org=Dieses Konto noch Mitgliedschaft in der Organisation, Sie nach links oder löschen Sie sie zuerst.
|
||||
users.still_has_org=Dieses Konto ist noch Mitglied einer Organisation, bitte entferne diese Mitgliedschaft zuerst.
|
||||
|
||||
orgs.org_manage_panel=Organisationenverwaltung
|
||||
orgs.name=Name
|
||||
|
||||
@@ -61,7 +61,7 @@ domain = Domain
|
||||
domain_helper = This affects SSH clone URLs.
|
||||
app_url = Application URL
|
||||
app_url_helper = This affects HTTP/HTTPS clone URL and somewhere in e-mail.
|
||||
email_title = Email Service Settings(Optional)
|
||||
email_title = E-mail Service Settings(Optional)
|
||||
smtp_host = SMTP Host
|
||||
mailer_user = Sender E-mail
|
||||
mailer_password = Sender Password
|
||||
@@ -109,7 +109,7 @@ confirmation_mail_sent_prompt = A new confirmation e-mail has been sent to <b>%s
|
||||
sign_in_email = Sign in to your e-mail
|
||||
active_your_account = Activate Your Account
|
||||
resent_limit_prompt = Sorry, you are sending an activation e-mail too frequently. Please wait 3 minutes.
|
||||
has_unconfirmed_mail = Hi %s, you have an unconfirmed email address(<b>%s</b>). If you haven't received a confirmation e-mail or need to resend a new one, please click on the button below.
|
||||
has_unconfirmed_mail = Hi %s, you have an unconfirmed e-mail address(<b>%s</b>). If you haven't received a confirmation e-mail or need to resend a new one, please click on the button below.
|
||||
resend_mail = Click here to resend your activation e-mail
|
||||
email_not_associate = This e-mail address does not associate to any account.
|
||||
send_reset_mail = Click here to (re)send your password reset e-mail
|
||||
@@ -173,6 +173,7 @@ target_branch_not_exist = Target branch does not exist
|
||||
|
||||
[user]
|
||||
change_avatar = Change your avatar at gravatar.com
|
||||
change_custom_avatar = Change your avatar in settings
|
||||
join_on = Joined on
|
||||
repositories = Repositories
|
||||
activity = Public Activity
|
||||
@@ -191,7 +192,7 @@ delete = Delete Account
|
||||
uid = Uid
|
||||
|
||||
public_profile = Public Profile
|
||||
profile_desc = Your Email address is public and will be used for any account related notifications, and any web based operations made via the site.
|
||||
profile_desc = Your E-mail address is public and will be used for any account related notifications, and any web based operations made via the site.
|
||||
full_name = Full Name
|
||||
website = Website
|
||||
location = Location
|
||||
@@ -202,12 +203,29 @@ change_username_desc = Username has been changed, do you want to continue? This
|
||||
continue = Continue
|
||||
cancel = Cancel
|
||||
|
||||
enable_custom_avatar = Enable Custom Avatar
|
||||
enable_custom_avatar_helper = Enable this to disable fetch from Gravatar
|
||||
choose_new_avatar = Choose new avatar
|
||||
update_avatar = Update Avatar Setting
|
||||
uploaded_avatar_not_a_image = Uploaded file is not a image.
|
||||
no_custom_avatar_available = No custom avatar available, cannot enable it.
|
||||
update_avatar_success = Your avatar setting has been updated successfully.
|
||||
|
||||
change_password = Change Password
|
||||
old_password = Current Password
|
||||
new_password = New Password
|
||||
password_incorrect = Current password is not correct.
|
||||
change_password_success = Password is changed successfully. You can now sign in via new password.
|
||||
|
||||
emails = E-mail Addresses
|
||||
manage_emails = Manage e-mail addresses
|
||||
email_desc = Current e-mail addresses
|
||||
primary = Primary
|
||||
primary_email = Make primary
|
||||
delete_email = Delete
|
||||
add_new_email = Add new e-mail address
|
||||
add_email = Add e-mail
|
||||
|
||||
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.
|
||||
@@ -368,10 +386,34 @@ diff.stats_desc = <strong> %d changed files</strong> with <strong>%d additions</
|
||||
diff.bin = BIN
|
||||
diff.view_file = View File
|
||||
|
||||
release.releases = Releases
|
||||
release.new_release = New Release
|
||||
release.draft = Draft
|
||||
release.prerelease = Pre-Release
|
||||
release.stable = Stable
|
||||
release.edit = edit
|
||||
release.ahead = <strong>%d</strong> commits to %s since this release
|
||||
release.source_code = Source Code
|
||||
release.tag_name = Tag name
|
||||
release.target = Target
|
||||
release.tag_helper = Choose an existing tag, or create a new tag on publish.
|
||||
release.release_title = Release title
|
||||
release.content_with_md = Content with <a href="%s">Markdown</a>
|
||||
release.write = Write
|
||||
release.preview = Preview
|
||||
release.content_placeholder = Write some content
|
||||
release.loading = Loading...
|
||||
release.prerelease_desc = This is a pre-release
|
||||
release.prerelease_helper = We’ll point out that this release is identified as non-production ready.
|
||||
release.publish = Publish Release
|
||||
release.save_draft = Save Draft
|
||||
release.edit_release = Edit Release
|
||||
release.tag_name_already_exist = Release with this tag name has already existed.
|
||||
|
||||
[org]
|
||||
org_name_holder = Organization Name
|
||||
org_name_helper = Great organization names are short and memorable.
|
||||
org_email_helper = Organization's Email receives all notifications and confirmations.
|
||||
org_email_helper = Organization's E-mail receives all notifications and confirmations.
|
||||
create_org = Create Organization
|
||||
repo_updated = Updated
|
||||
people = People
|
||||
@@ -467,6 +509,8 @@ dashboard.delete_inactivate_accounts = Delete all inactive accounts
|
||||
dashboard.delete_inactivate_accounts_success = All inactivate accounts have been deleted successfully.
|
||||
dashboard.delete_repo_archives = Delete all repositories archives
|
||||
dashboard.delete_repo_archives_success = All repositories archives have been deleted successfully.
|
||||
dashboard.git_gc_repos = Do garbage collection on repositories
|
||||
dashboard.git_gc_repos_success = All repositories have done garbage collection successfully.
|
||||
dashboard.server_uptime = Server Uptime
|
||||
dashboard.current_goroutine = Current Goroutines
|
||||
dashboard.current_memory_usage = Current Memory Usage
|
||||
@@ -584,7 +628,7 @@ config.db_ssl_mode_helper = (for "postgres" only)
|
||||
config.db_path = Path
|
||||
config.db_path_helper = (for "sqlite3" only)
|
||||
config.service_config = Service Configuration
|
||||
config.register_email_confirm = Register Email Confirmation
|
||||
config.register_email_confirm = Register E-mail Confirmation
|
||||
config.disable_register = Disable Registration
|
||||
config.require_sign_in_view = Require Sign In View
|
||||
config.mail_notify = Mail Notification
|
||||
|
||||
@@ -26,7 +26,7 @@ organization=Organisation
|
||||
mirror=Miroir
|
||||
new_repo=Nouveau Référentiel
|
||||
new_migrate=Nouvelle Migration
|
||||
new_fork=Nouveau référentiel de fourche
|
||||
new_fork=Nouveau Référentiel d'Embranchement
|
||||
new_org=Nouvelle Organisation
|
||||
manage_org=Gérer les Organisations
|
||||
admin_panel=Administration
|
||||
@@ -173,6 +173,7 @@ target_branch_not_exist=Branche cible n'existe pas
|
||||
|
||||
[user]
|
||||
change_avatar=Changez d'avatar via gravatar.com
|
||||
change_custom_avatar=Changer de vignette dans les réglages
|
||||
join_on=Adhéré le
|
||||
repositories=Référentiels
|
||||
activity=Activités publiques
|
||||
@@ -202,6 +203,14 @@ change_username_desc=Nom d'utilisateur modifié. Cela affecte tous les liens rel
|
||||
continue=Continuer
|
||||
cancel=Annuler
|
||||
|
||||
enable_custom_avatar=Permettre vignette personnalisée
|
||||
enable_custom_avatar_helper=Cette option désactive l'affichage via Gravatar
|
||||
choose_new_avatar=Choisir nouvelle vignette
|
||||
update_avatar=Réglage de mise à jour de vignette
|
||||
uploaded_avatar_not_a_image=Le fichier téléchargé n'est pas une image.
|
||||
no_custom_avatar_available=Aucun avatar personnalisé disponible, activation impossible.
|
||||
update_avatar_success=La mise à jour de votre vignette a réussi.
|
||||
|
||||
change_password=Modifier le Mot de Passe
|
||||
old_password=Mot de Passe actuel
|
||||
new_password=Nouveau Mot de Passe
|
||||
@@ -248,9 +257,9 @@ 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é
|
||||
fork_repo=Référentiel d'Embranchement
|
||||
fork_from=Embranchement de
|
||||
fork_visiblity_helper=Un Référentiel d'embranchement ne peut pas changer sa visiblité
|
||||
repo_desc=Description
|
||||
repo_lang=Langue
|
||||
repo_lang_helper=Sélectionner un fichier .gitignore
|
||||
@@ -368,6 +377,30 @@ diff.stats_desc=<strong> %d fichiers modifiés</strong> avec <strong>%d ajouts</
|
||||
diff.bin=BIN
|
||||
diff.view_file=Voir le fichier
|
||||
|
||||
release.releases=Versions
|
||||
release.new_release=Nouvelle version
|
||||
release.draft=Brouillon
|
||||
release.prerelease=Pré-publication
|
||||
release.stable=Stable
|
||||
release.edit=Éditer
|
||||
release.ahead=<strong>%d</strong> commissions à %s depuis cette publication
|
||||
release.source_code=Code Source
|
||||
release.tag_name=Nom du tag
|
||||
release.target=Cible
|
||||
release.tag_helper=Choisissez un tag existant ou créez-en un nouveau à publier.
|
||||
release.release_title=Titre de la publication
|
||||
release.content_with_md=Contenu avec <a href="%s">Démarque(s)</a>
|
||||
release.write=Écrire
|
||||
release.preview=Prévisualiser
|
||||
release.content_placeholder=Rédiger du contenu
|
||||
release.loading=Chargement…
|
||||
release.prerelease_desc=Il s'agit d'une version préliminaire
|
||||
release.prerelease_helper=Nous soulignerons que cette version est considérée comme non prête pour la production.
|
||||
release.publish=Publier
|
||||
release.save_draft=Sauvegarder le Brouillon
|
||||
release.edit_release=Éditer la publication
|
||||
release.tag_name_already_exist=Une publication avec ce nom de tag a déjà existée.
|
||||
|
||||
[org]
|
||||
org_name_holder=Nom d'organisation
|
||||
org_name_helper=Idéalement, un nom d'organisation devrait être court et mémorable.
|
||||
@@ -467,6 +500,8 @@ 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.git_gc_repos=Collecter les déchets des référentiels
|
||||
dashboard.git_gc_repos_success=Tous les référentiels ont effectué la collecte avec succès.
|
||||
dashboard.server_uptime=Durée de Marche Serveur
|
||||
dashboard.current_goroutine=Goroutines actuelles
|
||||
dashboard.current_memory_usage=Utilisation Mémoire actuelle
|
||||
|
||||
718
conf/locale/locale_lv-LV.ini
Executable file
718
conf/locale/locale_lv-LV.ini
Executable file
@@ -0,0 +1,718 @@
|
||||
app_desc=Viegli uzstādāms Git serviss, kas rakstīts valodā Go
|
||||
|
||||
home=Sākums
|
||||
dashboard=Infopanelis
|
||||
explore=Izpētīt
|
||||
help=Palīdzība
|
||||
sign_in=Pierakstīties
|
||||
social_sign_in=Sociālā pieteikšanās: 2. solis <small>piesaistīt kontu</small>
|
||||
sign_out=Izrakstīties
|
||||
sign_up=Pieteikties
|
||||
register=Reģistrēties
|
||||
website=Mājas lapa
|
||||
version=Versija
|
||||
page=Lapa
|
||||
template=Sagatave
|
||||
language=Valoda
|
||||
|
||||
username=Lietotājvārds
|
||||
email=E-pasts
|
||||
password=Parole
|
||||
re_type=Parole atkārtoti
|
||||
captcha=Pārbaudes kods
|
||||
|
||||
repository=Repozitorijs
|
||||
organization=Organizācija
|
||||
mirror=Spogulis
|
||||
new_repo=Jauns repozitorijs
|
||||
new_migrate=Jauna migrācija
|
||||
new_fork=Jauns atdalītais repozitorijs
|
||||
new_org=Jauna organizācija
|
||||
manage_org=Pārvaldīt organizācijas
|
||||
admin_panel=Admin panelis
|
||||
account_settings=Konta iestatījumi
|
||||
settings=Iestatījumi
|
||||
|
||||
news_feed=Jaunumu plūsma
|
||||
pull_requests=Izmaiņu pieprasījumi
|
||||
issues=Problēmas
|
||||
|
||||
cancel=Atcelt
|
||||
|
||||
[install]
|
||||
install=Instalācija
|
||||
title=Instalācijas soļi pirmo reizi palaižot
|
||||
requite_db_desc=Gogs ir nepieciešama MySQL, PostgreSQL vai SQLite3 datu bāze.
|
||||
db_type=Datu bāzes veids
|
||||
host=Resursdators
|
||||
user=Lietotājs
|
||||
password=Parole
|
||||
db_name=Datu bāzes nosaukums
|
||||
db_helper=Nepieciešams izmantot MySQL INNODB dzini ar rakstzīmju kopu utf8_general_ci.
|
||||
ssl_mode=SSL režīms
|
||||
path=Ceļš
|
||||
sqlite_helper=SQLite 3 datu bāzes faila atrašanās vieta.
|
||||
general_title=Gogs vispārīgie iestatījumi
|
||||
repo_path=Repozitoriju glabāšanas vieta
|
||||
repo_path_helper=Visi Git attālinātie repozitoriji tiks glabāti šajā direktorijā.
|
||||
run_user=Izpildes lietotājs
|
||||
run_user_helper=Lietotājam ir jābūt rakstīšanas tiesībām repozitorija saknes direktorijai un Gogs jābūt palaistam zem šī lietotāja.
|
||||
domain=Domēns
|
||||
domain_helper=Tas ietekmē SSH klonēšanas URL.
|
||||
app_url=Lietotnes URL
|
||||
app_url_helper=Tas ietekmē HTTP/HTTPS klonēšanas URL un e-pasta saturā izsūtītās saites.
|
||||
email_title=E-pasta pakalpojuma iestatījumi (neobligāti)
|
||||
smtp_host=SMTP resursdators
|
||||
mailer_user=Sūtītāja e-pasta adrese
|
||||
mailer_password=Sūtītāja parole
|
||||
notify_title=Paziņojumu iestatījumi (neobligāti)
|
||||
register_confirm=Iespējot reģistrēšanās apstiprināšanu
|
||||
mail_notify=Iespējot e-pasta paziņojumus
|
||||
admin_title=Admin konta iestatījumi
|
||||
admin_name=Lietotājvārds
|
||||
admin_password=Parole
|
||||
confirm_password=Apstipriniet paroli
|
||||
admin_email=E-pasts
|
||||
install_gogs=Instalēt Gogs
|
||||
test_git_failed=Kļūda pārbaudot 'git' komandu: %v
|
||||
sqlite3_not_available=Jūsu versija neatbalsta SQLite3, lūdzu lejupielādējiet oficiālo bināro versiju no %s, NEVIS gobuild versiju.
|
||||
invalid_db_setting=Datu bāzes iestatījums nav pareizs: %v
|
||||
invalid_repo_path=Repozitorija atrašanās vieta ir nekorekta: %v
|
||||
run_user_not_match=Izpildes lietotājs nav pašreizējais lietotājs: %s -> %s
|
||||
save_config_failed=Neizdevās saglabāt konfigurāciju: %v
|
||||
invalid_admin_setting=Nekorekts admin konta iestatījums: %v
|
||||
install_success=Laipni lūdzam! Mēs priecājamies, ka Jūs izvēlaties Gogs, patīkamu lietošanu!
|
||||
|
||||
[home]
|
||||
uname_holder=Lietotājvārds vai e-pasts
|
||||
password_holder=Parole
|
||||
switch_dashboard_context=Mainīt infopaneļa kontekstu
|
||||
my_repos=Mani repozitoriji
|
||||
collaborative_repos=Sadarbības repozitoriji
|
||||
my_orgs=Manas organizācijas
|
||||
my_mirrors=Mani spoguļi
|
||||
|
||||
[explore]
|
||||
repos=Repozitoriji
|
||||
|
||||
[auth]
|
||||
create_new_account=Izveidot jaunu kontu
|
||||
register_hepler_msg=Jau ir konts? Pieraksties tagad!
|
||||
social_register_hepler_msg=Jau ir konts? Sasaisti tagad!
|
||||
disable_register_prompt=Atvainojiet, reģistrācija ir atspējota. Lūdzu, sazinieties ar vietnes administratoru.
|
||||
disable_register_mail=Atvainojiet, reģistrācijas e-pasta apstiprināšana ir atspējota.
|
||||
remember_me=Atcerēties mani
|
||||
forgot_password=Aizmirsu paroli
|
||||
forget_password=Aizmirsi paroli?
|
||||
sign_up_now=Nepieciešams konts? Reģistrējies tagad.
|
||||
confirmation_mail_sent_prompt=Jauns apstiprināšanas e-pasts ir nosūtīts uz <b>%s</b>, lūdzu, pārbaudies savu e-pasta kontu tuvāko %d stundu laikā, lai pabeigtu reģistrācijas procesu.
|
||||
sign_in_email=Atvērt savu e-pasta kontu
|
||||
active_your_account=Aktivizēt savu kontu
|
||||
resent_limit_prompt=Atvainojiet, Jūs sūtījāt aktivizācijas e-pastu pārāk bieži. Lūdzu, gaidiet 3 minūtes.
|
||||
has_unconfirmed_mail=Sveiki %s, Jums ir neapstiprināta e-pasta adrese (<b>%s</b>). Ja neesat saņēmis apstiprināšanas e-pastu vai Jums ir nepieciešams nosūtīt jaunu, lūdzu, nospiediet pogu, kas atrodas zemāk.
|
||||
resend_mail=Nospiediet šeit, lai vēlreiz nosūtītu aktivizācijas e-pastu
|
||||
email_not_associate=Šī e-pasta adrese nav saistīta ar Jūsu kontu.
|
||||
send_reset_mail=Spiediet šeit, lai nosūtītu paroles maiņas vēstuli uz Jūsu e-pastu
|
||||
reset_password=Atjaunot savu paroli
|
||||
invalid_code=Atvainojiet, Jūsu apstiprināšanas kodam ir beidzies derīguma termiņš vai arī tas ir nepareizs.
|
||||
reset_password_helper=Nospiediet šeit, lai atjaunotu paroli
|
||||
password_too_short=Paroles garums nedrīkst būt mazāks par 6.
|
||||
|
||||
[form]
|
||||
UserName=Lietotājvārds
|
||||
RepoName=Repozitorija nosaukums
|
||||
Email=E-pasta adrese
|
||||
Password=Parole
|
||||
Retype=Parole atkārtoti
|
||||
SSHTitle=SSH atslēgas nosaukums
|
||||
HttpsUrl=HTTPS URL
|
||||
PayloadUrl=Vērtuma URL
|
||||
TeamName=Komandas nosaukums
|
||||
AuthName=Autorizācijas nosaukums
|
||||
AdminEmail=Admin e-pasta adrese
|
||||
|
||||
require_error=` nedrīkst būt tukšs.`
|
||||
alpha_dash_error=` drīkst saturēt tikai latīņu alfabēta burtus, ciparus vai domuzīmes (-_).`
|
||||
alpha_dash_dot_error=` drīkst saturēt tikai latīņu alfabēta burtus, ciparus, domuzīmes (-_) vai punktu.`
|
||||
min_size_error=` jabūt vismaz %s simbolu garumā.`
|
||||
max_size_error=` jabūt ne mazāk kā %s simbolu garumā.`
|
||||
email_error=` nav derīga e-pasta adrese.`
|
||||
url_error=` nav korekts URL.`
|
||||
unknown_error=Nezināma kļūda:
|
||||
captcha_incorrect=Pārbaudes kods nesakrīt ar attēlā redzamo.
|
||||
password_not_match=Parole un atkārtoti ievadītā parole nav vienādas.
|
||||
|
||||
username_been_taken=Lietotājvārds ir jau aizņemts.
|
||||
repo_name_been_taken=Repozitorija vārds ir jau aizņemts.
|
||||
org_name_been_taken=Organizācijas nosaukums ir jau aizņemts.
|
||||
team_name_been_taken=Komandas nosaukums ir jau aizņemts.
|
||||
email_been_used=E-pasta adrese jau tiek izmantota.
|
||||
ssh_key_been_used=Publiskās atslēgas nosaukums jau tiek izmantos.
|
||||
illegal_username=Jūsu lietotājvārds satur neatļautas rakstzīmes.
|
||||
illegal_repo_name=Krātuves nosaukums satur neatļautas rakstzīmes.
|
||||
illegal_org_name=Organizācijas nosaukums satur neatļautas rakstzīmes.
|
||||
illegal_team_name=Grupas nosaukums satur neatļautas rakstzīmes.
|
||||
username_password_incorrect=Lietotājvārds vai parole nav pareiza.
|
||||
enterred_invalid_repo_name=Lūdzu, pārliecinieties, vai ievadītā repozitorija nosaukums ir pareizs.
|
||||
enterred_invalid_owner_name=Lūdzu, pārliecinieties, vai ievadītā īpašnieka vārds ir pareizs.
|
||||
enterred_invalid_password=Lūdzu pārliecinieties, vai Jūsu ievadītā parole ir pareiza.
|
||||
user_not_exist=Šāds lietotājs neeksistē.
|
||||
last_org_owner=Nav iespējams noņemt vienīgo komandas īpašnieku. Pirms tam ir nepieciešams norādīt jauno īpašnieku.
|
||||
|
||||
invalid_ssh_key=Atvainojiet, nav iespējams pārbaudīt Jūsu SSH atslēgu: %s
|
||||
unable_verify_ssh_key=Nav iespējams pārbaudīt jūsu SSH atslēgu, bet tiks pieņemts, ka tā ir derīga, lūdzu, pārliecinieties par to pats.
|
||||
auth_failed=Autentifikācija neizdevās: %v
|
||||
|
||||
still_own_repo=Jūsu esat vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai nomainīt to īpašnieku.
|
||||
still_has_org=Jūsu esat vismaz vienas organizācijas biedrs, sākumā nepieciešams pamest vai izdzēst šo organizāciju.
|
||||
org_still_own_repo=Šī organizācija ir vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai nomainīt to īpašnieku.
|
||||
|
||||
still_own_user=Šo autentifikāciju joprojām izmanto vismaz viens lietotājs, nepieciešams šiem lietotājiem nomainīt autentifikācijas veidu vai tos izdzēst.
|
||||
|
||||
target_branch_not_exist=Mērķa atzars neeksistē
|
||||
|
||||
[user]
|
||||
change_avatar=Mainīt savu profila attēlu vietnē gravatar.com
|
||||
change_custom_avatar=Mainīt savu profila attēlu iestatījumos
|
||||
join_on=Pievienojās
|
||||
repositories=Repozitoriji
|
||||
activity=Publiskā aktivitāte
|
||||
followers=Sekotāji
|
||||
starred=Atzīmēti ar zvaigznīti
|
||||
following=Seko
|
||||
|
||||
[settings]
|
||||
profile=Profils
|
||||
password=Parole
|
||||
ssh_keys=SSH atslēgas
|
||||
social=Sociālie konti
|
||||
applications=Lietotnes
|
||||
orgs=Organizācijas
|
||||
delete=Dzēst kontu
|
||||
uid=Lietotāja ID
|
||||
|
||||
public_profile=Publiskais profils
|
||||
profile_desc=Jūsu e-pasta adrese ir publiska un tiks izmantota, lai nosūtītju Jums paziņojumus, kas saistīti ar Jūsu kontu vai darbībām veiktām caur šo mājas lapu.
|
||||
full_name=Pilns vārds
|
||||
website=Mājas lapa
|
||||
location=Atrašanās vieta
|
||||
update_profile=Mainīt profilu
|
||||
update_profile_success=Jūsu profila dati ir veiksmīgi saglabāti.
|
||||
change_username=Lietotāja vārds mainīts
|
||||
change_username_desc=Lietotājvārds tiks mainīts, vai vēlaties turpināt? Tas ietekmēs visas saites, kas attiecas uz Jūsu kontu.
|
||||
continue=Turpināt
|
||||
cancel=Atcelt
|
||||
|
||||
enable_custom_avatar=Iespējot maināmu profila attēlu
|
||||
enable_custom_avatar_helper=Iespējojiet šo, lai atslēgtu profilu attēlu ņemšanu no gravatar.com
|
||||
choose_new_avatar=Izvēlēties jaunu profila attēlu
|
||||
update_avatar=Saglabāt profila bildi
|
||||
uploaded_avatar_not_a_image=Augšupielādētais fails nav attēls.
|
||||
no_custom_avatar_available=Nav iespējams mainīt profila bildi.
|
||||
update_avatar_success=Jūsu profila bilde tika veiksmīgi saglabāta.
|
||||
|
||||
change_password=Mainīt paroli
|
||||
old_password=Pašreizējā parole
|
||||
new_password=Jauna parole
|
||||
password_incorrect=Ievadīta nepareiza pašreizējā parole.
|
||||
change_password_success=Parole tika veiksmīgi nomainīta. Tagad jūs varat pieraksītites, izmantojot jauno paroli.
|
||||
|
||||
manage_ssh_keys=Pārvaldīt SSH atslēgas
|
||||
add_key=Pievienot atslēgu
|
||||
ssh_desc=Šis ir saraksts ar Jūsu kontam piesaistītajām SSH atslēgām. Dzēsiet visas, kuras Jūs neatpazīstat.
|
||||
ssh_helper=<strong>Vajadzīga palīdzība?</strong> Apskatieties pamācību kā <a href="%s">ģenerēt SSH atslēgas</a> vai kā novērst <a href="%s">biežāk sastopamās SSH problēmas</a>.
|
||||
add_new_key=Pievienot SSH atslēgu
|
||||
key_name=Atslēgas nosaukums
|
||||
key_content=Saturs
|
||||
add_key_success=Pievienota jauna SSH atslēga!
|
||||
delete_key=Dzēst
|
||||
add_on=Pievienota
|
||||
last_used=Pēdējo reizi izmantota
|
||||
no_activity=Nav nesenas aktivitātes
|
||||
|
||||
manage_social=Pārvaldīt piesaistītos sociālos kontus
|
||||
social_desc=Šeit tiek attēloti visi sociālie konti, kas ir piesaistīti Jūsu kontam. Dzēsiet visus, kurus Jūs neatpazīstat.
|
||||
unbind=Atsaistīt
|
||||
unbind_success=Sociālais konts tika atsaistīts.
|
||||
|
||||
manage_access_token=Pārvaldīt personīgos piekļuves talonus
|
||||
generate_new_token=Ģenerēt jaunu talonu
|
||||
tokens_desc=Talons tika uzģēnerēts, tagad varat to izmantot, lai piekļūtu Gogs API.
|
||||
new_token_desc=Pašlaik visiem taloniem ir pilna piekļuve Jūsu kontam.
|
||||
token_name=Talona nosaukums
|
||||
generate_token=Ģenerēt talonu
|
||||
generate_token_succees=Jauns piekļuves talons tika veiksmīgi uzģenerēts! Pārliecinieties, ka esat to nokopējis, jo to Jums vairāk nebūs iespēja izdarīt!
|
||||
delete_token=Dzēst
|
||||
delete_token_success=Jūsu personīgās piekļuves talons tika veiksmīgi izdzēsts! Neazimistiet veikt nepieciešamos labojumus savās lietojumprogrammās, kas to izmantoja.
|
||||
|
||||
delete_account=Dzēst savu kontu
|
||||
delete_prompt=Šī darbība pilnībā izdzēsīs Jūsu kontu, kā arī tā ir <strong>NEATGRIEZENISKA</strong>!
|
||||
confirm_delete_account=Apstiprināt dzēšanu
|
||||
delete_account_title=Konta dzēšana
|
||||
delete_account_desc=Šis konts tiks neatgriezeniski dzēsts, vai vēlaties turpināt?
|
||||
|
||||
[repo]
|
||||
owner=Īpašnieks
|
||||
repo_name=Repozitorija nosaukums
|
||||
repo_name_helper=Labi repzotoriju nosaukumi ir īsi, tādi kurus viegli atcerēties un <strong>unikāli</strong>.
|
||||
visibility=Redzamība
|
||||
visiblity_helper=Šis repozitorijs ir <span class="label label-red label-radius">Privāts</span>
|
||||
fork_repo=Atdalīt repozitoriju
|
||||
fork_from=Atdalīt no
|
||||
fork_visiblity_helper=Atdalītam repozitorijam nav iespējams nomainīt tā redzamību
|
||||
repo_desc=Apraksts
|
||||
repo_lang=Valoda
|
||||
repo_lang_helper=Izvēlieties .gitignore failu
|
||||
license=Licence
|
||||
license_helper=Izvēlieties licences failu
|
||||
init_readme=Inicializēt šo repozitoriju ar README.md failu
|
||||
create_repo=Izveidot repozitoriju
|
||||
default_branch=Noklusējuma atzars
|
||||
mirror_interval=Spoguļošanas intervāls (stundās)
|
||||
goget_meta=Go-Get metadati
|
||||
goget_meta_helper=Šis repozitorijs saturēs <span class="label label-blue label-radius">Go-Get</span> metadatus
|
||||
|
||||
need_auth=Nepieciešama autorizācija
|
||||
migrate_type=Migrācijas veids
|
||||
migrate_type_helper=Šis repozitorijs būs <span class="label label-blue label-radius">Spoguļots</span>
|
||||
migrate_repo=Migrēt repozitoriju
|
||||
|
||||
copy_link=Kopēt
|
||||
click_to_copy=Kopēt uz starpliktuvi
|
||||
copied=Kopēšana notikusi veiksmīgi
|
||||
clone_helper=Nepieciešama palīdzība kā veikt klonēšana? Apmeklējiet <a target="_blank" href="%s">Palīdzība</a> lapu!
|
||||
unwatch=Nevērot
|
||||
watch=Vērot
|
||||
unstar=Noņemt zvaigznīti
|
||||
star=Pievienot zvaigznīti
|
||||
fork=Atdalīts
|
||||
|
||||
no_desc=Nav apraksta
|
||||
quick_guide=Īsa pamācība
|
||||
clone_this_repo=Klonēt šo repozitoriju
|
||||
create_new_repo_command=Izveidot jaunu repozitoriju komandrindā
|
||||
push_exist_repo=Nosūtīt izmaiņas no komandrindas eksistējošam repozitorijam
|
||||
|
||||
branch=Atzars
|
||||
tree=Koks
|
||||
branch_and_tags=Atzari un birkas
|
||||
branches=Atzari
|
||||
tags=Birkas
|
||||
issues=Problēmas
|
||||
commits=Revīzijas
|
||||
releases=Laidieni
|
||||
file_raw=Neapstrādāts
|
||||
file_history=Vēsture
|
||||
file_view_raw=Rādīt neapstrādātu
|
||||
|
||||
commits.commits=Revīzijas
|
||||
commits.search=Meklēt revīzijas
|
||||
commits.find=Meklēt
|
||||
commits.author=Autors
|
||||
commits.message=Ziņojums
|
||||
commits.date=Datums
|
||||
commits.older=Vecāki
|
||||
commits.newer=Jaunāki
|
||||
|
||||
settings=Iestatījumi
|
||||
settings.options=Opcijas
|
||||
settings.collaboration=Sadarbība
|
||||
settings.hooks=Tīmekļa āķi
|
||||
settings.githooks=Git āķi
|
||||
settings.deploy_keys=Izvietot atslēgas
|
||||
settings.basic_settings=Pamatiestatījumi
|
||||
settings.danger_zone=Bīstamā zona
|
||||
settings.site=Oficiālā mājas lapa
|
||||
settings.update_settings=Mainīt iestatījumus
|
||||
settings.change_reponame=Mainīts repozitorija nosaukums
|
||||
settings.change_reponame_desc=Repozitorija nosaukums tiks mainīts, vai vēlaties turpināt? Tas ietekmēs visas saites, kas saistītas ar šo repozitoriju.
|
||||
settings.transfer=Mainīt īpašnieku
|
||||
settings.transfer_desc=Mainīt šī repozitorija īpašnieku uz citu lietotāju vai organizāciju, kurai Jums ir administratora tiesībs.
|
||||
settings.new_owner_has_same_repo=Jaunajam īpašniekam jau ir repozitorijs ar šādu nosaukumu.
|
||||
settings.delete=Dzēst šo repozitoriju
|
||||
settings.delete_desc=Dzēšot repozitoriju, tā datus vairs nebūs iespējams atgūt. Pirms dzēšanas pārliecinieites vai patiešām vēlaties to darīt.
|
||||
settings.transfer_notices=<p>- Jūs zaudēsiet piekļuvi repozitorijam, ja jaunais īpašnieks ir individuāls lietotājs.</p><p>- Jūs saglabāsiet piekļuvi repzitorijam, ja jaunais īpašneiks ir organizācija un Jūs esat viens no tās īpašniekiem.</p>
|
||||
settings.update_settings_success=Repozitorija opcijas ir veiksmīgi saglabātas.
|
||||
settings.transfer_owner=Jaunais īpašnieks
|
||||
settings.make_transfer=Mainīt
|
||||
settings.transfer_succeed=Repozitorija īpašnieks ir veiksmīgi nomainīts.
|
||||
settings.confirm_delete=Apstiprināt dzēšanu
|
||||
settings.add_collaborator=Pievienot jaunu līdzstrādnieku
|
||||
settings.add_collaborator_success=Jauns līdzstrādnieks ir pievienots.
|
||||
settings.remove_collaborator_success=Līdzstrādnieks tika noņemts.
|
||||
settings.user_is_org_member=Lietotājs ir organizācijas biedrs, kas nevar tikt pievienots kā līdzstrādnieks.
|
||||
settings.add_webhook=Pievienot tīmekļa āķi
|
||||
settings.hooks_desc=Tīmekļa āķi ļauj paziņot ārējiem servisiem par noteiktiem notikomiem, kas notiek Git servisā. Kad iestāsies kāds notikums, katram ārējā servisa URL tiks nosūtīts POST pieprasījums. Lai uzzinātu sīkāk skatieties <a target="_blank" href="%s">Tīmekļa āķu rokasgrāmatā</a>.
|
||||
settings.githooks_desc=Git āķus apstrādā pats Git. Jūs varat labot atbalsīto āku failus sarakstā zemāk, lai veiktu pielāgotas darbības.
|
||||
settings.githook_edit_desc=Ja āķis nav aktīvs, tiks attēlots piemērs kā to izmantot. Atstājot āķa saturu tukšu, tas tiks atspējots.
|
||||
settings.githook_name=Āķa nosaukums
|
||||
settings.githook_content=Āķa saturs
|
||||
settings.update_githook=Labot āķi
|
||||
settings.remove_hook_success=Tīmekļa āķis tika noņemts.
|
||||
settings.add_webhook_desc=Uz norādīto URL tiks nosūtīts <code>POST</code> pieprasījums ar notikuma datiem. Jūs varat norādīt arī datu formātu, kādā datus vēlaties saņemt (JSON, <code>x-www-form-urlencoded</code> <em>utt.</em>). Detalizētāku informāciju ir iespējams uzzināt <a target="_blank" href="%s">Tīmekļa āķu rokasgrāmatā</a>.
|
||||
settings.payload_url=Vērtuma URL
|
||||
settings.content_type=Satura tips
|
||||
settings.secret=Noslēpums
|
||||
settings.event_desc=Kādu notikumu rezultātā tiktu izsaukts tīmekļā āķis?
|
||||
settings.event_push_only=Tikai izmaiņu nosūtīšanas notikumiem.
|
||||
settings.active=Aktīvs
|
||||
settings.active_helper=Tiks nosūtīti notikuma dati, kad nostrādās šis āķis.
|
||||
settings.add_hook_success=Jauns tīmekļa āķis tika veiksmīgi pievienots.
|
||||
settings.update_webhook=Mainīt tīmekļa āķi
|
||||
settings.update_hook_success=Tīmekļa āķist tika veiksmīgi saglabāts.
|
||||
settings.delete_webhook=Dzēst tīmekļa āķi
|
||||
settings.recent_deliveries=Pēdējās piegādes
|
||||
settings.hook_type=Āķa veids
|
||||
settings.add_slack_hook_desc=PIevienot <a href="%s">Slack</a> integrāciju Jūsu repozitorijā.
|
||||
settings.slack_token=Talons
|
||||
settings.slack_domain=Domēns
|
||||
settings.slack_channel=Kanāls
|
||||
|
||||
diff.browse_source=Pārlūkot izejas kodu
|
||||
diff.parent=vecāks
|
||||
diff.commit=revīzija
|
||||
diff.data_not_available=Salīdzināšanas dati nav pieejami.
|
||||
diff.show_diff_stats=Rādīt salīdzināšanas statistiku
|
||||
diff.stats_desc=<strong>%d mainītis faili</strong> ar <strong>%d papildinājumiem</strong> un <strong>%d dzēšanām</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Parādīt failu
|
||||
|
||||
release.releases=Releases
|
||||
release.new_release=New Release
|
||||
release.draft=Draft
|
||||
release.prerelease=Pre-Release
|
||||
release.stable=Stable
|
||||
release.edit=edit
|
||||
release.ahead=<strong>%d</strong> commits to %s since this release
|
||||
release.source_code=Source Code
|
||||
release.tag_name=Tag name
|
||||
release.target=Target
|
||||
release.tag_helper=Choose an existing tag, or create a new tag on publish.
|
||||
release.release_title=Release title
|
||||
release.content_with_md=Content with <a href="%s">Markdown</a>
|
||||
release.write=Write
|
||||
release.preview=Preview
|
||||
release.content_placeholder=Write some content
|
||||
release.loading=Loading...
|
||||
release.prerelease_desc=This is a pre-release
|
||||
release.prerelease_helper=We’ll point out that this release is identified as non-production ready.
|
||||
release.publish=Publish Release
|
||||
release.save_draft=Save Draft
|
||||
release.edit_release=Edit Release
|
||||
release.tag_name_already_exist=Release with this tag name has already existed.
|
||||
|
||||
[org]
|
||||
org_name_holder=Organizācijas nosaukums
|
||||
org_name_helper=Labi organizāciju nosaukumi ir īsi un tādi, kurus viegli atcerēties.
|
||||
org_email_helper=Uz organizācijas e-pastu tiks sūtītas visas notifikācias un apstiprinājumi.
|
||||
create_org=Izveidot organizāciju
|
||||
repo_updated=Atjaunināts
|
||||
people=Personas
|
||||
invite_someone=Uzaicināt kādu
|
||||
teams=Komandas
|
||||
lower_members=dalībnieki
|
||||
lower_repositories=repozitoriji
|
||||
create_new_team=Izveidot jaunu komandu
|
||||
org_desc=Apraksts
|
||||
team_name=Komandas nosaukums
|
||||
team_desc=Apraksts
|
||||
team_name_helper=Šo nosaukumu varēs izmantot, lai pieminētu komandu sarunās.
|
||||
team_desc_helper=Komandas apraksts
|
||||
team_permission_desc=Kādām tiesībām šai komandai būtu jābūt?
|
||||
|
||||
settings=Iestatījumi
|
||||
settings.options=Opcijas
|
||||
settings.full_name=Pilns vārds, uzvārds
|
||||
settings.website=Mājas lapa
|
||||
settings.location=Atrašanās vieta
|
||||
settings.update_settings=Mainīt iestatījumus
|
||||
settings.change_orgname=Mainīts organizācijas nosaukums
|
||||
settings.change_orgname_desc=Organizācijas nosaukums tiks mainīts, vai vēlaties turpinat? Tas ietekmēs saites, kas attiecas uz šo organizāciju.
|
||||
settings.update_setting_success=Organizācijas iestatījumi tika veiksmīgi saglabāti.
|
||||
settings.delete=Dzēst organizāciju
|
||||
settings.delete_account=Dzēst šo organizāciju
|
||||
settings.delete_prompt=Šī darbība pilnībā dzēsīs šo organizāciju, kā arī tā ir <strong>NEATGRIEZENISKA</strong>!
|
||||
settings.confirm_delete_account=Apstiprināt dzēšanu
|
||||
settings.delete_org_title=Organizācijas dzēšana
|
||||
settings.delete_org_desc=Šī organizācija tiks pilnībā izdzēsta, vai vēlaties turpināt?
|
||||
settings.hooks_desc=Pievienot tīmekļa āķus, kas nostrādās <strong>visiem repozitorijiem</strong> šajā organizācijā.
|
||||
|
||||
members.public=Publisks
|
||||
members.public_helper=padarīt privātu
|
||||
members.private=Privāts
|
||||
members.private_helper=padarīt publisku
|
||||
members.owner=Īpašnieks
|
||||
members.member=Biedri
|
||||
members.conceal=Noslēpt
|
||||
members.remove=Noņemt
|
||||
members.leave=Atstāt
|
||||
members.invite_desc=Sāciet rakstīt lietotājvārdu, lai uzaicinātu jaunu biedru organizācijā %s:
|
||||
members.invite_now=Uzaicināt tagad
|
||||
|
||||
teams.join=Pievienoties
|
||||
teams.leave=Atstāt
|
||||
teams.read_access=Lasīšanas piekļuve
|
||||
teams.read_access_helper=Komanda varēs skatīties un klonēt šīs organizācijas repozitorijus.
|
||||
teams.write_access=Rakstīšanas piekļuve
|
||||
teams.write_access_helper=Komanda varēs skatīties un klonēt, kā arī nosūtīt izmaiņas šīs organizācijas repozitorijiem.
|
||||
teams.admin_access=Administratora piekļuve
|
||||
teams.admin_access_helper=Šī komanda varēs veikt push/pull komandas tās repozitorijiem, kā arī tiem pievienot citus līdzstrādniekus.
|
||||
teams.no_desc=Komandai nav apraksta
|
||||
teams.settings=Iestatījumi
|
||||
teams.owners_permission_desc=Īpašniekiem ir pilna piekļuve <strong>visiem repozitorijiem</strong> un ir organizācijas <strong>administratora tiesības</strong>.
|
||||
teams.members=Komandas biedri
|
||||
teams.update_settings=Saglabāt iestatījumus
|
||||
teams.delete_team=Dzēst komandu
|
||||
teams.add_team_member=Pievienot komandas biedru
|
||||
teams.delete_team_title=Komandas dzēšana
|
||||
teams.delete_team_desc=Komanda tiks dzēsta, vai vēlaties turpināt? Komandas biedri var zaudēt piekļuvi dažiem vai pat visiem repozitorijiem.
|
||||
teams.delete_team_success=Komanda tika veiksmīgi izdzēsta.
|
||||
teams.read_permission_desc=Šai komandai ir <strong>lasīšanas</strong> tiesības: dalībnieki var skatīties un klonēt komandas repozitorijus.
|
||||
teams.write_permission_desc=Šai komandai ir <strong>rakstīšanas</strong> tiesības: dalībnieki var lasīt un nosūtīt izmaiņas repozitorijiem.
|
||||
teams.admin_permission_desc=Šai komandai ir <strong>administratora</strong> tiesības: dalībnieki var lasīt, rakstīt un pievienot citus dalībniekus komandas repozitorijiem.
|
||||
teams.repositories=Komandas repozitoriji
|
||||
teams.add_team_repository=Pievienot komandas repozitoriju
|
||||
teams.remove_repo=Noņemt
|
||||
teams.add_nonexistent_repo=Repozitorijs, kuram Jūs mēģinat pievienot neeksistē, sākumā izveidojiet to.
|
||||
|
||||
[admin]
|
||||
dashboard=Infopanelis
|
||||
users=Lietotāji
|
||||
organizations=Organizācijas
|
||||
repositories=Repozitoriji
|
||||
authentication=Autentifikācijas
|
||||
config=Konfigurācija
|
||||
notices=Sistēmas paziņojumi
|
||||
monitor=Uzraudzība
|
||||
prev=Iepr.
|
||||
next=Tālāk
|
||||
|
||||
dashboard.statistic=Statistika
|
||||
dashboard.operations=Darbības
|
||||
dashboard.system_status=Sistēmas uzraudzības statuss
|
||||
dashboard.statistic_info=Gogs datu bāze satur <b>%d</b> lietotājus, <b>%d</b> organizācijas, <b>%d</b> publiskās atslēgas, <b>%d</b> repozitorijus, <b>%d</b> vērošanas, <b>%d</b> atzīmētas zvaigznītes, <b>%d</b> darbības, <b>%d</b> piekļuves, <b>%d</b> problēmas, <b>%d</b> komentārus, <b>%d</b> sociālos kontus, <b>%d</b> sekošanas, <b>%d</b> spoguļošanas, <b>%d</b> izlaides, <b>%d</b> login sources, <b>%d</b> tīmekļa āķus, <b>%d</b> starpposmus, <b>%d</b> etiķetes, <b>%d</b> āķu uzdevumus, <b>%d</b> komandas, <b>%d</b> labotus uzdevumus, <b>%d</b> pielikumus.
|
||||
dashboard.operation_name=Darbības nosaukums
|
||||
dashboard.operation_switch=Pārslēgt
|
||||
dashboard.operation_run=Palaist
|
||||
dashboard.clean_unbind_oauth=Notīrīt nesaistītās OAuth biļetes
|
||||
dashboard.clean_unbind_oauth_success=Visas nesaistītās OAuth biļetes tika veiksmīgi izdzēstas.
|
||||
dashboard.delete_inactivate_accounts=Dzēst visus neaktīvos kontus
|
||||
dashboard.delete_inactivate_accounts_success=Visi neaktīvie kotni tika veiksmīgi izdzēsti.
|
||||
dashboard.delete_repo_archives=Dzēst visu repozitoriju arhīvus
|
||||
dashboard.delete_repo_archives_success=Visu repozitoriju arhīvi tika veiksmīgi izdzēsti.
|
||||
dashboard.git_gc_repos=Veikt repozitoriju datu sakārtošānu (git gc)
|
||||
dashboard.git_gc_repos_success=Datu sakārtošana visiem repozitorijiem veiksmīgi pabeigta.
|
||||
dashboard.server_uptime=Servera darbības laiks
|
||||
dashboard.current_goroutine=Izmantotās Gorutīnas
|
||||
dashboard.current_memory_usage=Pašreiz izmantotā atmiņa
|
||||
dashboard.total_memory_allocated=Kopējā piešķirtā atmiņa
|
||||
dashboard.memory_obtained=Iegūtā atmiņa
|
||||
dashboard.pointer_lookup_times=Rādītāju meklēšanas reizes
|
||||
dashboard.memory_allocate_times=Atmiņas piešķiršanas reizes
|
||||
dashboard.memory_free_times=Atmiņas atbrīvošanas reizes
|
||||
dashboard.current_heap_usage=Pašreizējā kaudzes izmantošana
|
||||
dashboard.heap_memory_obtained=Iegūtā kaudzes atmiņa
|
||||
dashboard.heap_memory_idle=Neizmantotā kaudzes atmiņa
|
||||
dashboard.heap_memory_in_use=Izmantotā kaudzes atmiņa
|
||||
dashboard.heap_memory_released=Atbrīvotā kaudzes atmiņa
|
||||
dashboard.heap_objects=Kaudzes atmiņas objekti
|
||||
dashboard.bootstrap_stack_usage=Izmantotais sāknēšanas steka lielums
|
||||
dashboard.stack_memory_obtained=Iegūtā steka atmiņa
|
||||
dashboard.mspan_structures_usage=Izmantotās MSpan struktūras
|
||||
dashboard.mspan_structures_obtained=Iegūtās MSpan struktūras
|
||||
dashboard.mcache_structures_usage=Izmantotās MCache struktūras
|
||||
dashboard.mcache_structures_obtained=Iegūtās MCache struktūras
|
||||
dashboard.profiling_bucket_hash_table_obtained=Iegūtā profilēšanas kausa jaucējtabula
|
||||
dashboard.gc_metadata_obtained=Iegūtie GC metadati
|
||||
dashboard.other_system_allocation_obtained=Iegūtās citas sistēmas sadales
|
||||
dashboard.next_gc_recycle=Nākošā GC atkritne
|
||||
dashboard.last_gc_time=Laiks kopš pēdējās GC
|
||||
dashboard.total_gc_time=Kopējais GC izpildes laiks
|
||||
dashboard.total_gc_pause=Kopējais GC izpildes laiks
|
||||
dashboard.last_gc_pause=Pedējās GC izpildes laiks
|
||||
dashboard.gc_times=GC reizes
|
||||
|
||||
users.user_manage_panel=Lietotāju pārvaldības panelis
|
||||
users.new_account=Izveidot jaunu kontu
|
||||
users.name=Vārds
|
||||
users.activated=Aktivizēts
|
||||
users.admin=Administrators
|
||||
users.repos=Repozitoriji
|
||||
users.created=Izveidots
|
||||
users.edit=Labot
|
||||
users.auth_source=Autorizācijas avots
|
||||
users.local=Iebūvētā
|
||||
users.auth_login_name=Autorizāciju, pietiekšanās vārds
|
||||
users.update_profile_success=Konta profils tika veiksmīgi saglabāts.
|
||||
users.edit_account=Labot kontu
|
||||
users.is_activated=Konts ir aktivizēts
|
||||
users.is_admin=Šim kontam ir administratora piekļuves tiesības
|
||||
users.allow_git_hook=Šim kontam ir tiesības pievienot/labot Git āķus
|
||||
users.update_profile=Mainīt konta profilu
|
||||
users.delete_account=Dzēst šo kontu
|
||||
users.still_own_repo=Šis konts ir vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai nomainīt to īpašnieku.
|
||||
users.still_has_org=Šis konts ir vismaz vienas organizācijas biedrs, sākumā nepieciešams pamest vai izdzēst šo organizāciju.
|
||||
|
||||
orgs.org_manage_panel=Organizāciju pārvaldības panelis
|
||||
orgs.name=Nosaukums
|
||||
orgs.teams=Komandas
|
||||
orgs.members=Dalībnieki
|
||||
|
||||
repos.repo_manage_panel=Repozitoriju pārvaldības panelis
|
||||
repos.owner=Īpašnieks
|
||||
repos.name=Vārds
|
||||
repos.private=Privāts
|
||||
repos.watches=Vērošana
|
||||
repos.stars=Atzīmētās zvaigznītes
|
||||
repos.issues=Problēmas
|
||||
|
||||
auths.auth_manage_panel=Autorizāciju pārvaldības panelis
|
||||
auths.new=Pievienot jaunu autorizācijas veidu
|
||||
auths.name=Nosaukums
|
||||
auths.type=Veids
|
||||
auths.enabled=Iespējota
|
||||
auths.updated=Atjaunināta
|
||||
auths.auth_type=Autorizācijas veids
|
||||
auths.auth_name=Autorizācijas nosaukums
|
||||
auths.domain=Domēns
|
||||
auths.host=Resursdators
|
||||
auths.port=Ports
|
||||
auths.base_dn=Pamata DN
|
||||
auths.attributes=Meklēšanas atribūti
|
||||
auths.filter=Meklēšanas filtrs
|
||||
auths.ms_ad_sa=MS Ad SA
|
||||
auths.smtp_auth=SMTP autorizācijas veids
|
||||
auths.smtphost=SMTP resursdators
|
||||
auths.smtpport=SMTP ports
|
||||
auths.enable_tls=Iespējot TLS šifrēšanu
|
||||
auths.enable_auto_register=Iespējot automātisko reģistrāciju
|
||||
auths.tips=Padomi
|
||||
auths.edit=Labot autorizācijas iestatījumus
|
||||
auths.activated=Autentifikācija ir aktivizēta
|
||||
auths.update_success=Autorizācijas iestatījumi tika veiksmīgi saglabāti.
|
||||
auths.update=Mainīt autorizācijas iestatījumus
|
||||
auths.delete=Dzēst šo autorizāciju
|
||||
auths.delete_auth_title=Autorizācijas dzēšana
|
||||
auths.delete_auth_desc=Šī autorizācija tiks dzēsta, vai vēlaties turpināt?
|
||||
|
||||
config.server_config=Servera konfigurācija
|
||||
config.app_name=Lietotnes nosaukums
|
||||
config.app_ver=Lietotnes versija
|
||||
config.app_url=Lietotnes URL
|
||||
config.domain=Domēns
|
||||
config.offline_mode=Bezsaistes režīms
|
||||
config.disable_router_log=Atspējot maršrutētāja žurnalizēšanu
|
||||
config.run_user=Izpildes lietotājs
|
||||
config.run_mode=Izpildes režīms
|
||||
config.repo_root_path=Repozitoriju glabāšanas vieta
|
||||
config.static_file_root_path=Statisko failu atrašanās vieta
|
||||
config.log_file_root_path=Žurnalizēšanas failu glabāšanas vieta
|
||||
config.script_type=Skripta veids
|
||||
config.reverse_auth_user=Reversā lietotāja autentifikācija
|
||||
config.db_config=Datu bāzes konfigurācija
|
||||
config.db_type=Veids
|
||||
config.db_host=Resursdators
|
||||
config.db_name=Nosaukums
|
||||
config.db_user=Lietotājs
|
||||
config.db_ssl_mode=SSL režīms
|
||||
config.db_ssl_mode_helper=(tikai PostgreSQL datu bāzei)
|
||||
config.db_path=Ceļš
|
||||
config.db_path_helper=(tikai Sqlite3 datu bāzei)
|
||||
config.service_config=Pakalpojuma konfigurācija
|
||||
config.register_email_confirm=E-pasta reģistrēšanas apstiprināšana
|
||||
config.disable_register=Atspējot jaunu lietotāju reģistrāciju
|
||||
config.require_sign_in_view=Nepieciešama autorizācija
|
||||
config.mail_notify=Pasta paziņojumi
|
||||
config.enable_cache_avatar=Glabāt profila attēlus kešatmiņā
|
||||
config.active_code_lives=Aktīvā koda ilgums
|
||||
config.reset_password_code_lives=Paroles atiestatīšanas koda ilgums
|
||||
config.webhook_config=Tīkla āķu konfigurācija
|
||||
config.task_interval=Uzdevuma intervāls
|
||||
config.deliver_timeout=Piegādes noildze
|
||||
config.mailer_config=Sūtītāja konfigurācija
|
||||
config.mailer_enabled=Iespējots
|
||||
config.mailer_name=Nosaukums
|
||||
config.mailer_host=Resursdators
|
||||
config.mailer_user=Lietotājs
|
||||
config.oauth_config=OAuth konfigurācija
|
||||
config.oauth_enabled=Iespējota
|
||||
config.cache_config=Kešatmiņas konfigurācija
|
||||
config.cache_adapter=Kešatmiņas adapteris
|
||||
config.cache_interval=Kešatmiņas intervāls
|
||||
config.cache_conn=Kešatmiņas pieslēguma parametri
|
||||
config.session_config=Sesijas konfigurācja
|
||||
config.session_provider=Sesijas nodrošinātājs
|
||||
config.provider_config=Pakalpojumu sniedzēja konfigurācija
|
||||
config.cookie_name=Sīkdatnes nosaukums
|
||||
config.enable_set_cookie=Ļaut izmantot sīkdatnes
|
||||
config.gc_interval_time=GC laika intervāls
|
||||
config.session_life_time=Sesijas ilgums
|
||||
config.https_only=Tikai HTTPS
|
||||
config.cookie_life_time=Sīkdatņu glabāšanas ilgums
|
||||
config.picture_config=Attēlu konfigurācija
|
||||
config.picture_service=Lokāli attēli
|
||||
config.disable_gravatar=Atspējot Gravatar
|
||||
config.log_config=Žurnalizēšanas konfigurācija
|
||||
config.log_mode=Žurnalizēšanas veids
|
||||
|
||||
monitor.cron=Cron uzdevumi
|
||||
monitor.name=Nosaukums
|
||||
monitor.schedule=Grafiks
|
||||
monitor.next=Nākošās izpildes laiks
|
||||
monitor.previous=Pēdējās izpildes laiks
|
||||
monitor.execute_times=Izpilžu skaits
|
||||
monitor.process=Darbojošies procesi
|
||||
monitor.desc=Apraksts
|
||||
monitor.start=Sākuma laiks
|
||||
monitor.execute_time=Izpildes laiks
|
||||
|
||||
notices.system_notice_list=Sistēmas paziņojumi
|
||||
notices.type=Veids
|
||||
notices.type_1=Repozitorijs
|
||||
notices.desc=Apraksts
|
||||
notices.op=Op.
|
||||
notices.delete_success=Sistēmas paziņojums tika veiksmīgi izdzēsts.
|
||||
|
||||
[action]
|
||||
create_repo=izveidoja repozitoriju <a href="%s/%s">%s</a>
|
||||
commit_repo=veica izmaiņu nosūtīšanu atzaram <a href="%s/%s/src/%s">%s</a> repozitorijā <a href="%s/%s">%s</a>
|
||||
create_issue=reģistrēja problēmu <a href="%s/%s/issues/%s">%s#%s</a>
|
||||
comment_issue=pievienoja komentāru problēmai <a href="%s/%s/issues/%s">%s#%s</a>
|
||||
transfer_repo=mainīja repozitorija <code>%s</code> īpašnieku uz <a href="/%s%s">%s</a>
|
||||
push_tag=pievienoja birku <a href="%s/%s/src/%s">%s</a> repozitorijam <a href="%s/%s">%s</a>
|
||||
compare_2_commits=Veikt salīdzināšanu starp šīm 2 revīzijām
|
||||
|
||||
[tool]
|
||||
ago=atpakaļ
|
||||
from_now=no šī brīža
|
||||
now=tagad
|
||||
1s=1 sekundi %s
|
||||
1m=1 minūti %s
|
||||
1h=1 stundu %s
|
||||
1d=1 dienu %s
|
||||
1w=1 nedēļu %s
|
||||
1mon=1 mēnesi %s
|
||||
1y=1 gadu %s
|
||||
seconds=%d sekundes %s
|
||||
minutes=%d minūtes %s
|
||||
hours=%d stundas %s
|
||||
days=%d dienas %s
|
||||
weeks=%d nedēļas %s
|
||||
months=%d mēneši %s
|
||||
years=%d gadi %s
|
||||
raw_seconds=sekundes
|
||||
raw_minutes=minūtes
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -173,6 +173,7 @@ target_branch_not_exist=Doel branch bestaat niet
|
||||
|
||||
[user]
|
||||
change_avatar=Verander uw avatar op Gravatar.com
|
||||
change_custom_avatar=Change your avatar in settings
|
||||
join_on=Aangemeld op
|
||||
repositories=repositories
|
||||
activity=Openbare activiteit
|
||||
@@ -202,6 +203,14 @@ change_username_desc=Gebruikersnaam is gewijzigd. Wilt u doorgaan? Dit zal gevol
|
||||
continue=Doorgaan
|
||||
cancel=Annuleren
|
||||
|
||||
enable_custom_avatar=Enable Custom Avatar
|
||||
enable_custom_avatar_helper=Enable this to disable fetch from Gravatar
|
||||
choose_new_avatar=Choose new avatar
|
||||
update_avatar=Update Avatar Setting
|
||||
uploaded_avatar_not_a_image=Uploaded file is not a image.
|
||||
no_custom_avatar_available=No custom avatar available, cannot enable it.
|
||||
update_avatar_success=Your avatar setting has been updated successfully.
|
||||
|
||||
change_password=Verander wachtwoord
|
||||
old_password=Huidige wachtwoord
|
||||
new_password=Nieuw wachtwoord
|
||||
@@ -368,6 +377,30 @@ diff.stats_desc=<strong>%d gewijzigde bestanden</strong> met <strong>toevoeginge
|
||||
diff.bin=BIN
|
||||
diff.view_file=Bestand weergeven
|
||||
|
||||
release.releases=Releases
|
||||
release.new_release=New Release
|
||||
release.draft=Draft
|
||||
release.prerelease=Pre-Release
|
||||
release.stable=Stable
|
||||
release.edit=edit
|
||||
release.ahead=<strong>%d</strong> commits to %s since this release
|
||||
release.source_code=Source Code
|
||||
release.tag_name=Tag name
|
||||
release.target=Target
|
||||
release.tag_helper=Choose an existing tag, or create a new tag on publish.
|
||||
release.release_title=Release title
|
||||
release.content_with_md=Content with <a href="%s">Markdown</a>
|
||||
release.write=Write
|
||||
release.preview=Preview
|
||||
release.content_placeholder=Write some content
|
||||
release.loading=Loading...
|
||||
release.prerelease_desc=This is a pre-release
|
||||
release.prerelease_helper=We’ll point out that this release is identified as non-production ready.
|
||||
release.publish=Publish Release
|
||||
release.save_draft=Save Draft
|
||||
release.edit_release=Edit Release
|
||||
release.tag_name_already_exist=Release with this tag name has already existed.
|
||||
|
||||
[org]
|
||||
org_name_holder=Organisatienaam
|
||||
org_name_helper=Een goede organisatienaam is kort en memorabel.
|
||||
@@ -467,6 +500,8 @@ dashboard.delete_inactivate_accounts=Verwijder alle inactieve accounts
|
||||
dashboard.delete_inactivate_accounts_success=Alle inactivering van rekeningen hebben verwijderd.
|
||||
dashboard.delete_repo_archives=Verwijderen van alle repositories archieven
|
||||
dashboard.delete_repo_archives_success=Alle repositories archieven hebben verwijderd.
|
||||
dashboard.git_gc_repos=Do garbage collection on repositories
|
||||
dashboard.git_gc_repos_success=All repositories have done garbage collection successfully.
|
||||
dashboard.server_uptime=Uptime server
|
||||
dashboard.current_goroutine=Huidige Goroutines
|
||||
dashboard.current_memory_usage=Huidige geheugen gebruik
|
||||
|
||||
@@ -173,6 +173,7 @@ target_branch_not_exist=目标分支不存在。
|
||||
|
||||
[user]
|
||||
change_avatar=到 gravatar.com 上修改您的头像
|
||||
change_custom_avatar=到个人设置中修改头像
|
||||
join_on=加入于
|
||||
repositories=仓库列表
|
||||
activity=公开活动
|
||||
@@ -202,6 +203,14 @@ change_username_desc=用户名被修改,您确定要继续操作吗?这将
|
||||
continue=继续操作
|
||||
cancel=取消操作
|
||||
|
||||
enable_custom_avatar=启动自定义头像
|
||||
enable_custom_avatar_helper=激活该选项来禁止从 Gravatar 获取头像
|
||||
choose_new_avatar=选择新的头像
|
||||
update_avatar=更新头像设置
|
||||
uploaded_avatar_not_a_image=上传的文件不是一张图片!
|
||||
no_custom_avatar_available=未上传过自定义头像,无法激活该选项。
|
||||
update_avatar_success=您的头像设置更新成功!
|
||||
|
||||
change_password=修改密码
|
||||
old_password=当前密码
|
||||
new_password=新的密码
|
||||
@@ -368,6 +377,30 @@ diff.stats_desc=共有 <strong> %d 个文件被更改</strong>,包括 <strong>
|
||||
diff.bin=二进制
|
||||
diff.view_file=查看文件
|
||||
|
||||
release.releases=版本发布
|
||||
release.new_release=发布新版
|
||||
release.draft=草稿
|
||||
release.prerelease=预发行
|
||||
release.stable=稳定
|
||||
release.edit=编辑
|
||||
release.ahead=在该版本发布之后已有 <strong>%d</strong> 次代码提交到 %s 分支
|
||||
release.source_code=源代码
|
||||
release.tag_name=标签名称
|
||||
release.target=目标分支
|
||||
release.tag_helper=选择或创建一个已经存在的标签
|
||||
release.release_title=发布标题
|
||||
release.content_with_md=使用 <a href="%s">Markdown</a> 编辑内容
|
||||
release.write=内容编辑
|
||||
release.preview=效果预览
|
||||
release.content_placeholder=请输入内容
|
||||
release.loading=正在加载...
|
||||
release.prerelease_desc=这是一个预发行版本
|
||||
release.prerelease_helper=我们会告知用户不建议将本次发布投入生产环境使用。
|
||||
release.publish=发布版本
|
||||
release.save_draft=保存草稿
|
||||
release.edit_release=编辑发布信息
|
||||
release.tag_name_already_exist=已经存在使用相同标签进行发布的版本。
|
||||
|
||||
[org]
|
||||
org_name_holder=组织名称
|
||||
org_name_helper=伟大的组织都有一个简短而寓意深刻的名字。
|
||||
@@ -467,6 +500,8 @@ dashboard.delete_inactivate_accounts=删除所有未激活帐户
|
||||
dashboard.delete_inactivate_accounts_success=所有未激活帐号清除成功!
|
||||
dashboard.delete_repo_archives=删除所有仓库存档
|
||||
dashboard.delete_repo_archives_success=所有仓库存档清除成功!
|
||||
dashboard.git_gc_repos=对仓库进行垃圾回收
|
||||
dashboard.git_gc_repos_success=所有仓库垃圾回收成功!
|
||||
dashboard.server_uptime=服务运行时间
|
||||
dashboard.current_goroutine=当前 Goroutines 数量
|
||||
dashboard.current_memory_usage=当前内存使用量
|
||||
|
||||
@@ -173,6 +173,7 @@ target_branch_not_exist=目標分支不存在
|
||||
|
||||
[user]
|
||||
change_avatar=到 gravatar.com 上修改您的頭像
|
||||
change_custom_avatar=到個人設置中修改頭像
|
||||
join_on=加入於
|
||||
repositories=倉庫列表
|
||||
activity=公開活動
|
||||
@@ -202,6 +203,14 @@ change_username_desc=用戶名被修改,您確定要繼續操作嗎?這將
|
||||
continue=繼續操作
|
||||
cancel=取消操作
|
||||
|
||||
enable_custom_avatar=啟動自定義頭像
|
||||
enable_custom_avatar_helper=激活該選項來禁止從 Gravatar 獲取頭像
|
||||
choose_new_avatar=選擇新的頭像
|
||||
update_avatar=更新頭像設置
|
||||
uploaded_avatar_not_a_image=上傳的文件不是一張圖片!
|
||||
no_custom_avatar_available=沒有任何自定義頭像,無法激活該選項。
|
||||
update_avatar_success=您的頭像設置更新成功!
|
||||
|
||||
change_password=修改密碼
|
||||
old_password=當前密碼
|
||||
new_password=新的密碼
|
||||
@@ -368,6 +377,30 @@ diff.stats_desc=共有 <strong> %d 個文件被更改</strong>,包括 <strong>
|
||||
diff.bin=二進制
|
||||
diff.view_file=查看文件
|
||||
|
||||
release.releases=版本發佈
|
||||
release.new_release=發佈新版本
|
||||
release.draft=草稿
|
||||
release.prerelease=預發佈版本
|
||||
release.stable=穩定
|
||||
release.edit=編輯
|
||||
release.ahead=在該版本發佈之後已有 <strong>%d</strong> 次代碼提交到 %s 分支
|
||||
release.source_code=源代碼
|
||||
release.tag_name=標籤名稱
|
||||
release.target=目標分支
|
||||
release.tag_helper=選擇或創建一個已存在的標籤
|
||||
release.release_title=發佈標題
|
||||
release.content_with_md=使用 <a href="%s">Markdown</a> 編輯內容
|
||||
release.write=內容編輯
|
||||
release.preview=效果預覽
|
||||
release.content_placeholder=請輸入內容
|
||||
release.loading=正在加載...
|
||||
release.prerelease_desc=這是一個預發佈版本
|
||||
release.prerelease_helper=我們會告知用戶不建議將本發佈投入生產環境使用。
|
||||
release.publish=發佈版本
|
||||
release.save_draft=保在草稿
|
||||
release.edit_release=編輯發佈信息
|
||||
release.tag_name_already_exist=已經存在使用相同標籤的發佈版本。
|
||||
|
||||
[org]
|
||||
org_name_holder=組織名稱
|
||||
org_name_helper=偉大的組織都有一個簡短而寓意深刻的名字。
|
||||
@@ -467,6 +500,8 @@ dashboard.delete_inactivate_accounts=刪除所有未激活帳戶
|
||||
dashboard.delete_inactivate_accounts_success=所有未激活帳號清除成功!
|
||||
dashboard.delete_repo_archives=刪除所有倉庫存檔
|
||||
dashboard.delete_repo_archives_success=所有倉庫存檔清除成功!
|
||||
dashboard.git_gc_repos=對倉庫進行垃圾回收
|
||||
dashboard.git_gc_repos_success=所有倉庫的垃圾回收已成功完成!
|
||||
dashboard.server_uptime=服務執行時間
|
||||
dashboard.current_goroutine=當前 Goroutines 數量
|
||||
dashboard.current_memory_usage=當前內存使用量
|
||||
|
||||
@@ -30,7 +30,7 @@ The `config` file contains lines which will in the gogs docker container end up
|
||||
Here you can define things like the MySQL server for your database block.
|
||||
|
||||
The `fig` file will just be added to `fig.yml`, which is used by fig to manage your containers.
|
||||
This inculdes container linking!
|
||||
This includes container linking!
|
||||
|
||||
Just have a look at them and it will be clear how to write your own blocks.
|
||||
|
||||
@@ -53,7 +53,7 @@ Example:
|
||||
More sophisticated Example
|
||||
--------------------------
|
||||
|
||||
Her is a more elaborated example
|
||||
Here is a more elaborated example
|
||||
|
||||
```sh
|
||||
./assemble_blocks.sh docker_gogs w_db_cache_session option_db_postgresql option_cache_redis option_session_mysql
|
||||
@@ -86,4 +86,4 @@ This will pull in the `Dockerfile` from `docker_gogs` instead of the one from `d
|
||||
`Dockerfile`s for the `master` and `dev` branch are provided as `docker_gogs` and `docker_gogs_dev`
|
||||
|
||||
|
||||
[fig]:http://www.fig.sh/
|
||||
[fig]:http://www.fig.sh/
|
||||
|
||||
@@ -46,7 +46,7 @@ ENV HOME /home/git
|
||||
ENV USER git
|
||||
ENV PATH $GOGS_PATH:$PATH
|
||||
|
||||
RUN git config --global user.name "GoGS"
|
||||
RUN git config --global user.name "GoGS" && git config --global user.email "gogitservice@gmail.com"
|
||||
|
||||
ENTRYPOINT ["/tmp/init_gogs.sh"]
|
||||
CMD ["gogs", "web"]
|
||||
|
||||
@@ -47,7 +47,7 @@ ENV HOME /home/git
|
||||
ENV USER git
|
||||
ENV PATH $GOGS_PATH:$PATH
|
||||
|
||||
RUN git config --global user.name "GoGS"
|
||||
RUN git config --global user.name "GoGS" && git config --global user.email "gogitservice@gmail.com"
|
||||
|
||||
ENTRYPOINT ["/tmp/init_gogs.sh"]
|
||||
CMD ["gogs", "web"]
|
||||
|
||||
2
gogs.go
2
gogs.go
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
const APP_VER = "0.5.8.1118 Beta"
|
||||
const APP_VER = "0.5.11.0103 Beta"
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
@@ -58,6 +58,7 @@ type Action struct {
|
||||
ActUserId int64 // Action user id.
|
||||
ActUserName string // Action user name.
|
||||
ActEmail string
|
||||
ActAvatar string `xorm:"-"`
|
||||
RepoId int64
|
||||
RepoUserName string
|
||||
RepoName string
|
||||
@@ -348,7 +349,7 @@ func NewRepoAction(u *User, repo *Repository) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// TransferRepoAction adds new action for transfering repository.
|
||||
// TransferRepoAction adds new action for transferring repository.
|
||||
func TransferRepoAction(u, newUser *User, repo *Repository) (err error) {
|
||||
action := &Action{
|
||||
ActUserId: u.Id,
|
||||
|
||||
@@ -6,6 +6,7 @@ package models
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -15,8 +16,10 @@ import (
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/git"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/mahonia"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
)
|
||||
|
||||
@@ -80,6 +83,8 @@ func ParsePatch(pid int64, maxlines int, cmd *exec.Cmd, reader io.Reader) (*Diff
|
||||
|
||||
leftLine, rightLine int
|
||||
isTooLong bool
|
||||
// FIXME: use first 30 lines to detect file encoding. Should use cache in the future.
|
||||
buf bytes.Buffer
|
||||
)
|
||||
|
||||
diff := &Diff{Files: make([]*DiffFile, 0)}
|
||||
@@ -97,6 +102,11 @@ func ParsePatch(pid int64, maxlines int, cmd *exec.Cmd, reader io.Reader) (*Diff
|
||||
|
||||
i = i + 1
|
||||
|
||||
// FIXME: use first 30 lines to detect file encoding.
|
||||
if i <= 30 {
|
||||
buf.WriteString(line)
|
||||
}
|
||||
|
||||
// Diff data too large, we only show the first about maxlines lines
|
||||
if i == maxlines {
|
||||
isTooLong = true
|
||||
@@ -181,6 +191,21 @@ func ParsePatch(pid int64, maxlines int, cmd *exec.Cmd, reader io.Reader) (*Diff
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: use first 30 lines to detect file encoding.
|
||||
charset, err := base.DetectEncoding(buf.Bytes())
|
||||
if charset != "utf8" && err == nil {
|
||||
decoder := mahonia.NewDecoder(charset)
|
||||
if decoder != nil {
|
||||
for _, f := range diff.Files {
|
||||
for _, sec := range f.Sections {
|
||||
for _, l := range sec.Lines {
|
||||
l.Content = decoder.ConvertString(l.Content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -472,8 +472,8 @@ func UpdateIssueUserPairByAssignee(aid, iid int64) error {
|
||||
if aid == 0 {
|
||||
return nil
|
||||
}
|
||||
rawSql = "UPDATE `issue_user` SET is_assigned = true WHERE uid = ? AND issue_id = ?"
|
||||
_, err := x.Exec(rawSql, aid, iid)
|
||||
rawSql = "UPDATE `issue_user` SET is_assigned = ? WHERE uid = ? AND issue_id = ?"
|
||||
_, err := x.Exec(rawSql, true, aid, iid)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/gogits/gogs/modules/auth/ldap"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/uuid"
|
||||
)
|
||||
|
||||
type LoginType int
|
||||
@@ -40,7 +41,7 @@ var LoginTypes = map[LoginType]string{
|
||||
SMTP: "SMTP",
|
||||
}
|
||||
|
||||
// Ensure structs implmented interface.
|
||||
// Ensure structs implemented interface.
|
||||
var (
|
||||
_ core.Conversion = &LDAPConfig{}
|
||||
_ core.Conversion = &SMTPConfig{}
|
||||
@@ -225,33 +226,35 @@ func UserSignIn(uname, passwd string) (*User, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Query if name/passwd can login against the LDAP direcotry pool
|
||||
// Query if name/passwd can login against the LDAP directory pool
|
||||
// Create a local user if success
|
||||
// Return the same LoginUserPlain semantic
|
||||
// FIXME: https://github.com/gogits/gogs/issues/672
|
||||
func LoginUserLdapSource(u *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) {
|
||||
mail, logged := cfg.Ldapsource.SearchEntry(name, passwd)
|
||||
if !logged {
|
||||
// user not in LDAP, do nothing
|
||||
// User not in LDAP, do nothing
|
||||
return nil, ErrUserNotExist
|
||||
}
|
||||
if !autoRegister {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// fake a local user creation
|
||||
// Fallback.
|
||||
if len(mail) == 0 {
|
||||
mail = uuid.NewV4().String() + "@localhost"
|
||||
}
|
||||
|
||||
u = &User{
|
||||
LowerName: strings.ToLower(name),
|
||||
Name: strings.ToLower(name),
|
||||
Name: name,
|
||||
LoginType: LDAP,
|
||||
LoginSource: sourceId,
|
||||
LoginName: name,
|
||||
IsActive: true,
|
||||
Passwd: passwd,
|
||||
Email: mail,
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
err := CreateUser(u)
|
||||
return u, err
|
||||
return u, CreateUser(u)
|
||||
}
|
||||
|
||||
type loginAuth struct {
|
||||
@@ -315,7 +318,7 @@ func SmtpAuth(host string, port int, a smtp.Auth, useTls bool) error {
|
||||
return ErrUnsupportedLoginType
|
||||
}
|
||||
|
||||
// Query if name/passwd can login against the LDAP direcotry pool
|
||||
// Query if name/passwd can login against the LDAP directory pool
|
||||
// Create a local user if success
|
||||
// Return the same LoginUserPlain semantic
|
||||
func LoginUserSMTPSource(u *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
|
||||
|
||||
@@ -45,22 +45,23 @@ func init() {
|
||||
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))
|
||||
new(Notice), new(EmailAddress))
|
||||
}
|
||||
|
||||
func LoadModelsConfig() {
|
||||
DbCfg.Type = setting.Cfg.MustValue("database", "DB_TYPE")
|
||||
sec := setting.Cfg.Section("database")
|
||||
DbCfg.Type = sec.Key("DB_TYPE").String()
|
||||
if DbCfg.Type == "sqlite3" {
|
||||
UseSQLite3 = true
|
||||
}
|
||||
DbCfg.Host = setting.Cfg.MustValue("database", "HOST")
|
||||
DbCfg.Name = setting.Cfg.MustValue("database", "NAME")
|
||||
DbCfg.User = setting.Cfg.MustValue("database", "USER")
|
||||
DbCfg.Host = sec.Key("HOST").String()
|
||||
DbCfg.Name = sec.Key("NAME").String()
|
||||
DbCfg.User = sec.Key("USER").String()
|
||||
if len(DbCfg.Pwd) == 0 {
|
||||
DbCfg.Pwd = setting.Cfg.MustValue("database", "PASSWD")
|
||||
DbCfg.Pwd = sec.Key("PASSWD").String()
|
||||
}
|
||||
DbCfg.SslMode = setting.Cfg.MustValue("database", "SSL_MODE")
|
||||
DbCfg.Path = setting.Cfg.MustValue("database", "PATH", "data/gogs.db")
|
||||
DbCfg.SslMode = sec.Key("SSL_MODE").String()
|
||||
DbCfg.Path = sec.Key("PATH").MustString("data/gogs.db")
|
||||
}
|
||||
|
||||
func getEngine() (*xorm.Engine, error) {
|
||||
@@ -107,7 +108,7 @@ func SetEngine() (err error) {
|
||||
return fmt.Errorf("models.init(fail to connect to database): %v", err)
|
||||
}
|
||||
|
||||
// WARNNING: for serv command, MUST remove the output to os.stdout,
|
||||
// WARNING: for serv command, MUST remove the output to os.stdout,
|
||||
// so use log file to instead print to stdout.
|
||||
logPath := path.Join(setting.LogRootPath, "xorm.log")
|
||||
os.MkdirAll(path.Dir(logPath), os.ModePerm)
|
||||
|
||||
@@ -79,7 +79,7 @@ func UpdateOauth2(oa *Oauth2) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// GetOauthByUserId returns list of oauthes that are releated to given user.
|
||||
// GetOauthByUserId returns list of oauthes that are related to given user.
|
||||
func GetOauthByUserId(uid int64) ([]*Oauth2, error) {
|
||||
socials := make([]*Oauth2, 0, 5)
|
||||
err := x.Find(&socials, Oauth2{Uid: uid})
|
||||
|
||||
@@ -25,8 +25,8 @@ var (
|
||||
ErrLastOrgOwner = errors.New("The user to remove is the last member in owner team")
|
||||
)
|
||||
|
||||
// IsOrgOwner returns true if given user is in the owner team.
|
||||
func (org *User) IsOrgOwner(uid int64) bool {
|
||||
// IsOwnedBy returns true if given user is in the owner team.
|
||||
func (org *User) IsOwnedBy(uid int64) bool {
|
||||
return IsOrganizationOwner(org.Id, uid)
|
||||
}
|
||||
|
||||
@@ -77,6 +77,17 @@ func (org *User) RemoveMember(uid int64) error {
|
||||
return RemoveOrgUser(org.Id, uid)
|
||||
}
|
||||
|
||||
// IsOrgEmailUsed returns true if the e-mail has been used in organization account.
|
||||
func IsOrgEmailUsed(email string) (bool, error) {
|
||||
if len(email) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
return x.Get(&User{
|
||||
Email: email,
|
||||
Type: ORGANIZATION,
|
||||
})
|
||||
}
|
||||
|
||||
// CreateOrganization creates record of a new organization.
|
||||
func CreateOrganization(org, owner *User) (*User, error) {
|
||||
if !IsLegalName(org.Name) {
|
||||
@@ -90,7 +101,7 @@ func CreateOrganization(org, owner *User) (*User, error) {
|
||||
return nil, ErrUserAlreadyExist
|
||||
}
|
||||
|
||||
isExist, err = IsEmailUsed(org.Email)
|
||||
isExist, err = IsOrgEmailUsed(org.Email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if isExist {
|
||||
@@ -159,6 +170,24 @@ func CreateOrganization(org, owner *User) (*User, error) {
|
||||
return org, sess.Commit()
|
||||
}
|
||||
|
||||
// GetOrgByName returns organization by given name.
|
||||
func GetOrgByName(name string) (*User, error) {
|
||||
if len(name) == 0 {
|
||||
return nil, ErrOrgNotExist
|
||||
}
|
||||
u := &User{
|
||||
LowerName: strings.ToLower(name),
|
||||
Type: ORGANIZATION,
|
||||
}
|
||||
has, err := x.Get(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrOrgNotExist
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// CountOrganizations returns number of organizations.
|
||||
func CountOrganizations() int64 {
|
||||
count, _ := x.Where("type=1").Count(new(User))
|
||||
@@ -229,7 +258,7 @@ func IsOrganizationMember(orgId, uid int64) bool {
|
||||
return has
|
||||
}
|
||||
|
||||
// IsPublicMembership returns ture if given user public his/her membership.
|
||||
// IsPublicMembership returns true if given user public his/her membership.
|
||||
func IsPublicMembership(orgId, uid int64) bool {
|
||||
has, _ := x.Where("uid=?", uid).And("org_id=?", orgId).And("is_public=?", true).Get(new(OrgUser))
|
||||
return has
|
||||
@@ -850,7 +879,7 @@ func GetTeamMembers(orgId, teamId int64) ([]*User, error) {
|
||||
return us, err
|
||||
}
|
||||
|
||||
// GetUserTeams returns all teams that user belongs to in given origanization.
|
||||
// GetUserTeams returns all teams that user belongs to in given organization.
|
||||
func GetUserTeams(orgId, uid int64) ([]*Team, error) {
|
||||
tus := make([]*TeamUser, 0, 5)
|
||||
if err := x.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil {
|
||||
|
||||
@@ -89,6 +89,11 @@ type PublicKey struct {
|
||||
HasUsed bool `xorm:"-"`
|
||||
}
|
||||
|
||||
// OmitEmail returns content of public key but without e-mail address.
|
||||
func (k *PublicKey) OmitEmail() string {
|
||||
return strings.Join(strings.Split(k.Content, " ")[:2], " ")
|
||||
}
|
||||
|
||||
// GetAuthorizedString generates and returns formatted public key string for authorized_keys file.
|
||||
func (key *PublicKey) GetAuthorizedString() string {
|
||||
return fmt.Sprintf(_TPL_PUBLICK_KEY, appPath, key.Id, key.Content)
|
||||
|
||||
184
models/repo.go
184
models/repo.go
@@ -105,21 +105,18 @@ func NewRepoContext() {
|
||||
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.
|
||||
if stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", "user.name"); err != nil || strings.TrimSpace(stdout) == "" {
|
||||
// ExitError indicates user.name is not set
|
||||
if _, ok := err.(*exec.ExitError); ok || strings.TrimSpace(stdout) == "" {
|
||||
stndrdUserName := "Gogs"
|
||||
stndrdUserEmail := "gogitservice@gmail.com"
|
||||
if _, stderr, gerr := process.Exec("NewRepoContext(set name)", "git", "config", "--global", "user.name", stndrdUserName); gerr != nil {
|
||||
log.Fatal(4, "Fail to set git user.name(%s): %s", gerr, stderr)
|
||||
// Check if server has user.email and user.name set correctly and set if they're not.
|
||||
for configKey, defaultValue := range map[string]string{"user.name": "Gogs", "user.email": "gogitservice@gmail.com"} {
|
||||
if stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", configKey); err != nil || strings.TrimSpace(stdout) == "" {
|
||||
// ExitError indicates this config is not set
|
||||
if _, ok := err.(*exec.ExitError); ok || strings.TrimSpace(stdout) == "" {
|
||||
if _, stderr, gerr := process.Exec("NewRepoContext(set "+configKey+")", "git", "config", "--global", configKey, defaultValue); gerr != nil {
|
||||
log.Fatal(4, "Fail to set git %s(%s): %s", configKey, gerr, stderr)
|
||||
}
|
||||
log.Info("Git config %s set to %s", configKey, defaultValue)
|
||||
} else {
|
||||
log.Fatal(4, "Fail to get git %s(%s): %s", configKey, err, stderr)
|
||||
}
|
||||
if _, stderr, gerr := process.Exec("NewRepoContext(set email)", "git", "config", "--global", "user.email", stndrdUserEmail); gerr != nil {
|
||||
log.Fatal(4, "Fail to set git user.email(%s): %s", gerr, stderr)
|
||||
}
|
||||
log.Info("Git user.name and user.email set to %s <%s>", stndrdUserName, stndrdUserEmail)
|
||||
} else {
|
||||
log.Fatal(4, "Fail to get git user.name(%s): %s", err, stderr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,9 +238,30 @@ func IsRepositoryExist(u *User, repoName string) (bool, error) {
|
||||
return com.IsDir(RepoPath(u.Name, repoName)), nil
|
||||
}
|
||||
|
||||
// CloneLink represents different types of clone URLs of repository.
|
||||
type CloneLink struct {
|
||||
SSH string
|
||||
HTTPS string
|
||||
Git string
|
||||
}
|
||||
|
||||
// CloneLink returns clone URLs of repository.
|
||||
func (repo *Repository) CloneLink() (cl CloneLink, err error) {
|
||||
if err = repo.GetOwner(); err != nil {
|
||||
return cl, err
|
||||
}
|
||||
if setting.SshPort != 22 {
|
||||
cl.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.Domain, setting.SshPort, repo.Owner.LowerName, repo.LowerName)
|
||||
} else {
|
||||
cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.Domain, repo.Owner.LowerName, repo.LowerName)
|
||||
}
|
||||
cl.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, repo.Owner.LowerName, repo.LowerName)
|
||||
return cl, nil
|
||||
}
|
||||
|
||||
var (
|
||||
illegalEquals = []string{"debug", "raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new"}
|
||||
illegalSuffixs = []string{".git"}
|
||||
illegalSuffixs = []string{".git", ".keys"}
|
||||
)
|
||||
|
||||
// IsLegalName returns false if name contains illegal characters.
|
||||
@@ -308,28 +326,6 @@ func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) er
|
||||
return nil
|
||||
}
|
||||
|
||||
// MirrorUpdate checks and updates mirror repositories.
|
||||
func MirrorUpdate() {
|
||||
if err := x.Iterate(new(Mirror), func(idx int, bean interface{}) error {
|
||||
m := bean.(*Mirror)
|
||||
if m.NextUpdate.After(time.Now()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
repoPath := filepath.Join(setting.RepoRootPath, m.RepoName+".git")
|
||||
if _, stderr, err := process.ExecDir(10*time.Minute,
|
||||
repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath),
|
||||
"git", "remote", "update"); err != nil {
|
||||
return errors.New("git remote update: " + stderr)
|
||||
}
|
||||
|
||||
m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour)
|
||||
return UpdateMirror(m)
|
||||
}); err != nil {
|
||||
log.Error(4, "repo.MirrorUpdate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// MigrateRepository migrates a existing repository from other project hosting.
|
||||
func MigrateRepository(u *User, name, desc string, private, mirror bool, url string) (*Repository, error) {
|
||||
repo, err := CreateRepository(u, name, desc, "", "", private, mirror, false)
|
||||
@@ -833,13 +829,16 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
|
||||
|
||||
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
|
||||
func ChangeRepositoryName(userName, oldRepoName, newRepoName string) (err error) {
|
||||
userName = strings.ToLower(userName)
|
||||
oldRepoName = strings.ToLower(oldRepoName)
|
||||
newRepoName = strings.ToLower(newRepoName)
|
||||
if !IsLegalName(newRepoName) {
|
||||
return ErrRepoNameIllegal
|
||||
}
|
||||
|
||||
// Update accesses.
|
||||
accesses := make([]Access, 0, 10)
|
||||
if err = x.Find(&accesses, &Access{RepoName: strings.ToLower(userName + "/" + oldRepoName)}); err != nil {
|
||||
if err = x.Find(&accesses, &Access{RepoName: userName + "/" + oldRepoName}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -878,7 +877,7 @@ func UpdateRepository(repo *Repository) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteRepository deletes a repository for a user or orgnaztion.
|
||||
// DeleteRepository deletes a repository for a user or organization.
|
||||
func DeleteRepository(uid, repoId int64, userName string) error {
|
||||
repo := &Repository{Id: repoId, OwnerId: uid}
|
||||
has, err := x.Get(repo)
|
||||
@@ -1140,18 +1139,8 @@ type SearchOption struct {
|
||||
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 = FilterSQLInject(opt.Keyword)
|
||||
if len(opt.Keyword) == 0 {
|
||||
return repos, nil
|
||||
}
|
||||
@@ -1183,6 +1172,101 @@ func DeleteRepositoryArchives() error {
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
// Prevent duplicate tasks.
|
||||
isMirrorUpdating = false
|
||||
isGitFscking = false
|
||||
)
|
||||
|
||||
// MirrorUpdate checks and updates mirror repositories.
|
||||
func MirrorUpdate() {
|
||||
if isMirrorUpdating {
|
||||
return
|
||||
}
|
||||
isMirrorUpdating = true
|
||||
defer func() { isMirrorUpdating = false }()
|
||||
|
||||
mirrors := make([]*Mirror, 0, 10)
|
||||
|
||||
if err := x.Iterate(new(Mirror), func(idx int, bean interface{}) error {
|
||||
m := bean.(*Mirror)
|
||||
if m.NextUpdate.After(time.Now()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
repoPath := filepath.Join(setting.RepoRootPath, m.RepoName+".git")
|
||||
if _, stderr, err := process.ExecDir(10*time.Minute,
|
||||
repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath),
|
||||
"git", "remote", "update"); err != nil {
|
||||
desc := fmt.Sprintf("Fail to update mirror repository(%s): %s", repoPath, stderr)
|
||||
log.Error(4, desc)
|
||||
if err = CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error(4, "Fail to add notice: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour)
|
||||
mirrors = append(mirrors, m)
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(4, "MirrorUpdate: %v", err)
|
||||
}
|
||||
|
||||
for i := range mirrors {
|
||||
if err := UpdateMirror(mirrors[i]); err != nil {
|
||||
log.Error(4, "UpdateMirror", fmt.Sprintf("%s: %v", mirrors[i].RepoName, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GitFsck calls 'git fsck' to check repository health.
|
||||
func GitFsck() {
|
||||
if isGitFscking {
|
||||
return
|
||||
}
|
||||
isGitFscking = true
|
||||
defer func() { isGitFscking = false }()
|
||||
|
||||
args := append([]string{"fsck"}, setting.Git.Fsck.Args...)
|
||||
if err := x.Where("id > 0").Iterate(new(Repository),
|
||||
func(idx int, bean interface{}) error {
|
||||
repo := bean.(*Repository)
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repoPath := RepoPath(repo.Owner.Name, repo.Name)
|
||||
_, _, err := process.ExecDir(-1, repoPath, "Repository health check", "git", args...)
|
||||
if err != nil {
|
||||
desc := fmt.Sprintf("Fail to health check repository(%s)", repoPath)
|
||||
log.Warn(desc)
|
||||
if err = CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error(4, "Fail to add notice: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(4, "repo.Fsck: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func GitGcRepos() error {
|
||||
args := append([]string{"gc"}, setting.Git.GcArgs...)
|
||||
return x.Where("id > 0").Iterate(new(Repository),
|
||||
func(idx int, bean interface{}) error {
|
||||
repo := bean.(*Repository)
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, stderr, err := process.ExecDir(-1, RepoPath(repo.Owner.Name, repo.Name), "Repository garbage collection", "git", args...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v: %v", err, stderr)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// __ __ __ .__
|
||||
// / \ / \_____ _/ |_ ____ | |__
|
||||
// \ \/\/ /\__ \\ __\/ ___\| | \
|
||||
@@ -1190,7 +1274,7 @@ func DeleteRepositoryArchives() error {
|
||||
// \__/\ / (____ /__| \___ >___| /
|
||||
// \/ \/ \/ \/
|
||||
|
||||
// Watch is connection request for receiving repository notifycation.
|
||||
// Watch is connection request for receiving repository notification.
|
||||
type Watch struct {
|
||||
Id int64
|
||||
UserId int64 `xorm:"UNIQUE(watch)"`
|
||||
|
||||
329
models/user.go
329
models/user.go
@@ -5,18 +5,23 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/nfnt/resize"
|
||||
|
||||
"github.com/gogits/gogs/modules/avatar"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/git"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
@@ -37,6 +42,8 @@ var (
|
||||
ErrUserNotExist = errors.New("User does not exist")
|
||||
ErrUserNotKeyOwner = errors.New("User does not the owner of public key")
|
||||
ErrEmailAlreadyUsed = errors.New("E-mail already used")
|
||||
ErrEmailNotExist = errors.New("E-mail does not exist")
|
||||
ErrEmailNotActivated = errors.New("E-mail address has not been activated")
|
||||
ErrUserNameIllegal = errors.New("User name contains illegal characters")
|
||||
ErrLoginSourceNotExist = errors.New("Login source does not exist")
|
||||
ErrLoginSourceNotActived = errors.New("Login source is not actived")
|
||||
@@ -45,33 +52,41 @@ var (
|
||||
|
||||
// User represents the object of individual and member of organization.
|
||||
type User struct {
|
||||
Id int64
|
||||
LowerName string `xorm:"UNIQUE NOT NULL"`
|
||||
Name string `xorm:"UNIQUE NOT NULL"`
|
||||
FullName string
|
||||
Email string `xorm:"UNIQUE NOT NULL"`
|
||||
Passwd string `xorm:"NOT NULL"`
|
||||
LoginType LoginType
|
||||
LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
LoginName string
|
||||
Type UserType
|
||||
Orgs []*User `xorm:"-"`
|
||||
Repos []*Repository `xorm:"-"`
|
||||
Id int64
|
||||
LowerName string `xorm:"UNIQUE NOT NULL"`
|
||||
Name string `xorm:"UNIQUE NOT NULL"`
|
||||
FullName string
|
||||
// Email is the primary email address (to be used for communication).
|
||||
Email string `xorm:"UNIQUE(s) NOT NULL"`
|
||||
Passwd string `xorm:"NOT NULL"`
|
||||
LoginType LoginType
|
||||
LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
LoginName string
|
||||
Type UserType `xorm:"UNIQUE(s)"`
|
||||
Orgs []*User `xorm:"-"`
|
||||
Repos []*Repository `xorm:"-"`
|
||||
Location string
|
||||
Website string
|
||||
Rands string `xorm:"VARCHAR(10)"`
|
||||
Salt string `xorm:"VARCHAR(10)"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time `xorm:"UPDATED"`
|
||||
|
||||
// Permissions.
|
||||
IsActive bool
|
||||
IsAdmin bool
|
||||
AllowGitHook bool
|
||||
|
||||
// Avatar.
|
||||
Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
|
||||
AvatarEmail string `xorm:"NOT NULL"`
|
||||
UseCustomAvatar bool
|
||||
|
||||
// Counters.
|
||||
NumFollowers int
|
||||
NumFollowings int
|
||||
NumStars int
|
||||
NumRepos int
|
||||
Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
|
||||
AvatarEmail string `xorm:"NOT NULL"`
|
||||
Location string
|
||||
Website string
|
||||
IsActive bool
|
||||
IsAdmin bool
|
||||
AllowGitHook bool
|
||||
Rands string `xorm:"VARCHAR(10)"`
|
||||
Salt string `xorm:"VARCHAR(10)"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time `xorm:"UPDATED"`
|
||||
|
||||
// For organization.
|
||||
Description string
|
||||
@@ -81,6 +96,16 @@ type User struct {
|
||||
Members []*User `xorm:"-"`
|
||||
}
|
||||
|
||||
// EmailAdresses is the list of all email addresses of a user. Can contain the
|
||||
// primary email address, but is not obligatory
|
||||
type EmailAddress struct {
|
||||
Id int64
|
||||
Uid int64 `xorm:"INDEX NOT NULL"`
|
||||
Email string `xorm:"UNIQUE NOT NULL"`
|
||||
IsActivated bool
|
||||
IsPrimary bool `xorm:"-"`
|
||||
}
|
||||
|
||||
// DashboardLink returns the user dashboard page link.
|
||||
func (u *User) DashboardLink() string {
|
||||
if u.IsOrganization() {
|
||||
@@ -96,9 +121,12 @@ func (u *User) HomeLink() string {
|
||||
|
||||
// AvatarLink returns user gravatar link.
|
||||
func (u *User) AvatarLink() string {
|
||||
if setting.DisableGravatar {
|
||||
switch {
|
||||
case u.UseCustomAvatar:
|
||||
return setting.AppSubUrl + "/avatars/" + com.ToStr(u.Id)
|
||||
case setting.DisableGravatar:
|
||||
return setting.AppSubUrl + "/img/avatar_default.jpg"
|
||||
} else if setting.Service.EnableCacheAvatar {
|
||||
case setting.Service.EnableCacheAvatar:
|
||||
return setting.AppSubUrl + "/avatar/" + u.Avatar
|
||||
}
|
||||
return setting.GravatarSource + u.Avatar
|
||||
@@ -126,6 +154,48 @@ func (u *User) ValidtePassword(passwd string) bool {
|
||||
return u.Passwd == newUser.Passwd
|
||||
}
|
||||
|
||||
// CustomAvatarPath returns user custom avatar file path.
|
||||
func (u *User) CustomAvatarPath() string {
|
||||
return filepath.Join(setting.AvatarUploadPath, com.ToStr(u.Id))
|
||||
}
|
||||
|
||||
// UploadAvatar saves custom avatar for user.
|
||||
// FIXME: split uploads to different subdirs in case we have massive users.
|
||||
func (u *User) UploadAvatar(data []byte) error {
|
||||
u.UseCustomAvatar = true
|
||||
|
||||
img, _, err := image.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m := resize.Resize(200, 200, img, resize.NearestNeighbor)
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Id(u.Id).AllCols().Update(u); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
os.MkdirAll(setting.AvatarUploadPath, os.ModePerm)
|
||||
fw, err := os.Create(u.CustomAvatarPath())
|
||||
if err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
defer fw.Close()
|
||||
if err = jpeg.Encode(fw, m, nil); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// IsOrganization returns true if user is actually a organization.
|
||||
func (u *User) IsOrganization() bool {
|
||||
return u.Type == ORGANIZATION
|
||||
@@ -191,6 +261,9 @@ func IsEmailUsed(email string) (bool, error) {
|
||||
if len(email) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
if has, err := x.Get(&EmailAddress{Email: email}); has || err != nil {
|
||||
return has, err
|
||||
}
|
||||
return x.Get(&User{Email: email})
|
||||
}
|
||||
|
||||
@@ -220,8 +293,8 @@ func CreateUser(u *User) error {
|
||||
}
|
||||
|
||||
u.LowerName = strings.ToLower(u.Name)
|
||||
u.Avatar = base.EncodeMd5(u.Email)
|
||||
u.AvatarEmail = u.Email
|
||||
u.Avatar = avatar.HashEmail(u.AvatarEmail)
|
||||
u.Rands = GetUserSalt()
|
||||
u.Salt = GetUserSalt()
|
||||
u.EncodePasswd()
|
||||
@@ -298,6 +371,25 @@ func VerifyUserActiveCode(code string) (user *User) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// verify active code when active account
|
||||
func VerifyActiveEmailCode(code, email string) *EmailAddress {
|
||||
minutes := setting.Service.ActiveCodeLives
|
||||
|
||||
if user := getVerifyUser(code); user != nil {
|
||||
// time limit code
|
||||
prefix := code[:base.TimeLimitCodeLength]
|
||||
data := com.ToStr(user.Id) + email + user.LowerName + user.Passwd + user.Rands
|
||||
|
||||
if base.VerifyTimeLimitCode(data, minutes, prefix) {
|
||||
emailAddress := &EmailAddress{Email: email}
|
||||
if has, _ := x.Get(emailAddress); has {
|
||||
return emailAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangeUserName changes all corresponding setting from old user name to new one.
|
||||
func ChangeUserName(u *User, newUserName string) (err error) {
|
||||
if !IsLegalName(newUserName) {
|
||||
@@ -361,6 +453,13 @@ func ChangeUserName(u *User, newUserName string) (err error) {
|
||||
|
||||
// UpdateUser updates user's information.
|
||||
func UpdateUser(u *User) error {
|
||||
has, err := x.Where("id!=?", u.Id).And("type=?", INDIVIDUAL).And("email=?", u.Email).Get(new(User))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return ErrEmailAlreadyUsed
|
||||
}
|
||||
|
||||
u.LowerName = strings.ToLower(u.Name)
|
||||
|
||||
if len(u.Location) > 255 {
|
||||
@@ -373,7 +472,12 @@ func UpdateUser(u *User) error {
|
||||
u.Description = u.Description[:255]
|
||||
}
|
||||
|
||||
_, err := x.Id(u.Id).AllCols().Update(u)
|
||||
if u.AvatarEmail == "" {
|
||||
u.AvatarEmail = u.Email
|
||||
}
|
||||
u.Avatar = avatar.HashEmail(u.AvatarEmail)
|
||||
|
||||
_, err = x.Id(u.Id).AllCols().Update(u)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -419,6 +523,10 @@ func DeleteUser(u *User) error {
|
||||
if _, err = x.Delete(&Access{UserName: u.LowerName}); err != nil {
|
||||
return err
|
||||
}
|
||||
// Delete all alternative email addresses
|
||||
if _, err = x.Delete(&EmailAddress{Uid: u.Id}); err != nil {
|
||||
return err
|
||||
}
|
||||
// Delete all SSH keys.
|
||||
keys := make([]*PublicKey, 0, 10)
|
||||
if err = x.Find(&keys, &PublicKey{OwnerId: u.Id}); err != nil {
|
||||
@@ -439,9 +547,12 @@ func DeleteUser(u *User) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteInactivateUsers deletes all inactivate users.
|
||||
// DeleteInactivateUsers deletes all inactivate users and email addresses.
|
||||
func DeleteInactivateUsers() error {
|
||||
_, err := x.Where("is_active=?", false).Delete(new(User))
|
||||
if err == nil {
|
||||
_, err = x.Where("is_activated=?", false).Delete(new(EmailAddress))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -515,43 +626,151 @@ func GetUserIdsByNames(names []string) []int64 {
|
||||
return ids
|
||||
}
|
||||
|
||||
// UserCommit represtns a commit with validation of user.
|
||||
// Get all email addresses
|
||||
func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
|
||||
emails := make([]*EmailAddress, 0, 5)
|
||||
err := x.Where("uid=?", uid).Find(&emails)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, err := GetUserById(uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isPrimaryFound := false
|
||||
|
||||
for _, email := range emails {
|
||||
if email.Email == u.Email {
|
||||
isPrimaryFound = true
|
||||
email.IsPrimary = true
|
||||
} else {
|
||||
email.IsPrimary = false
|
||||
}
|
||||
}
|
||||
|
||||
// We alway want the primary email address displayed, even if it's not in
|
||||
// the emailaddress table (yet)
|
||||
if !isPrimaryFound {
|
||||
emails = append(emails, &EmailAddress{Email: u.Email, IsActivated: true, IsPrimary: true})
|
||||
}
|
||||
return emails, nil
|
||||
}
|
||||
|
||||
func AddEmailAddress(email *EmailAddress) error {
|
||||
used, err := IsEmailUsed(email.Email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if used {
|
||||
return ErrEmailAlreadyUsed
|
||||
}
|
||||
|
||||
_, err = x.Insert(email)
|
||||
return err
|
||||
}
|
||||
|
||||
func (email *EmailAddress) Activate() error {
|
||||
email.IsActivated = true
|
||||
if _, err := x.Id(email.Id).AllCols().Update(email); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user, err := GetUserById(email.Uid); err != nil {
|
||||
return err
|
||||
} else {
|
||||
user.Rands = GetUserSalt()
|
||||
return UpdateUser(user)
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteEmailAddress(email *EmailAddress) error {
|
||||
has, err := x.Get(email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrEmailNotExist
|
||||
}
|
||||
|
||||
if _, err = x.Delete(email); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func MakeEmailPrimary(email *EmailAddress) error {
|
||||
has, err := x.Get(email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrEmailNotExist
|
||||
}
|
||||
|
||||
if !email.IsActivated {
|
||||
return ErrEmailNotActivated
|
||||
}
|
||||
|
||||
user := &User{Id: email.Uid}
|
||||
has, err = x.Get(user)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrUserNotExist
|
||||
}
|
||||
|
||||
// Make sure the former primary email doesn't disappear
|
||||
former_primary_email := &EmailAddress{Email: user.Email}
|
||||
has, err = x.Get(former_primary_email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
former_primary_email.Uid = user.Id
|
||||
former_primary_email.IsActivated = user.IsActive
|
||||
x.Insert(former_primary_email)
|
||||
}
|
||||
|
||||
user.Email = email.Email
|
||||
_, err = x.Id(user.Id).AllCols().Update(user)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// UserCommit represents a commit with validation of user.
|
||||
type UserCommit struct {
|
||||
UserName string
|
||||
User *User
|
||||
*git.Commit
|
||||
}
|
||||
|
||||
// ValidateCommitWithEmail chceck if author's e-mail of commit is corresponsind to a user.
|
||||
func ValidateCommitWithEmail(c *git.Commit) (uname string) {
|
||||
func ValidateCommitWithEmail(c *git.Commit) *User {
|
||||
u, err := GetUserByEmail(c.Author.Email)
|
||||
if err == nil {
|
||||
uname = u.Name
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return uname
|
||||
return u
|
||||
}
|
||||
|
||||
// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
|
||||
func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
|
||||
emails := map[string]string{}
|
||||
emails := map[string]*User{}
|
||||
newCommits := list.New()
|
||||
e := oldCommits.Front()
|
||||
for e != nil {
|
||||
c := e.Value.(*git.Commit)
|
||||
|
||||
uname := ""
|
||||
var u *User
|
||||
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
|
||||
u, _ = GetUserByEmail(c.Author.Email)
|
||||
emails[c.Author.Email] = u
|
||||
} else {
|
||||
uname = v
|
||||
u = v
|
||||
}
|
||||
|
||||
newCommits.PushBack(UserCommit{
|
||||
UserName: uname,
|
||||
Commit: c,
|
||||
User: u,
|
||||
Commit: c,
|
||||
})
|
||||
e = e.Next()
|
||||
}
|
||||
@@ -563,19 +782,31 @@ func GetUserByEmail(email string) (*User, error) {
|
||||
if len(email) == 0 {
|
||||
return nil, ErrUserNotExist
|
||||
}
|
||||
// First try to find the user by primary email
|
||||
user := &User{Email: strings.ToLower(email)}
|
||||
has, err := x.Get(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrUserNotExist
|
||||
}
|
||||
return user, nil
|
||||
if has {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// Otherwise, check in alternative list for activated email addresses
|
||||
emailAddress := &EmailAddress{Email: strings.ToLower(email), IsActivated: true}
|
||||
has, err = x.Get(emailAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if has {
|
||||
return GetUserById(emailAddress.Uid)
|
||||
}
|
||||
|
||||
return nil, ErrUserNotExist
|
||||
}
|
||||
|
||||
// SearchUserByName returns given number of users whose name contains keyword.
|
||||
func SearchUserByName(opt SearchOption) (us []*User, err error) {
|
||||
opt.Keyword = FilterSQLInject(opt.Keyword)
|
||||
if len(opt.Keyword) == 0 {
|
||||
return us, nil
|
||||
}
|
||||
@@ -586,7 +817,7 @@ func SearchUserByName(opt SearchOption) (us []*User, err error) {
|
||||
return us, err
|
||||
}
|
||||
|
||||
// Follow is connection request for receiving user notifycation.
|
||||
// Follow is connection request for receiving user notification.
|
||||
type Follow struct {
|
||||
Id int64
|
||||
UserId int64 `xorm:"unique(follow)"`
|
||||
|
||||
@@ -99,7 +99,7 @@ func (w *Webhook) UpdateEvent() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// HasPushEvent returns true if hook enbaled push event.
|
||||
// HasPushEvent returns true if hook enabled push event.
|
||||
func (w *Webhook) HasPushEvent() bool {
|
||||
if w.PushOnly {
|
||||
return true
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
type MarkdownForm struct {
|
||||
Text string `form:"text" binding:"Required"`
|
||||
Text string `form:"text"`
|
||||
Mode string `form:"mode"`
|
||||
Context string `form:"context"`
|
||||
}
|
||||
@@ -49,19 +49,19 @@ func validateApiReq(errs binding.Errors, data map[string]interface{}, f auth.For
|
||||
|
||||
if errs[0].FieldNames[0] == field.Name {
|
||||
switch errs[0].Classification {
|
||||
case binding.RequiredError:
|
||||
case binding.ERR_REQUIRED:
|
||||
data["ErrorMsg"] = fieldName + " cannot be empty"
|
||||
case binding.AlphaDashError:
|
||||
case binding.ERR_ALPHA_DASH:
|
||||
data["ErrorMsg"] = fieldName + " must be valid alpha or numeric or dash(-_) characters"
|
||||
case binding.AlphaDashDotError:
|
||||
case binding.ERR_ALPHA_DASH_DOT:
|
||||
data["ErrorMsg"] = fieldName + " must be valid alpha or numeric or dash(-_) or dot characters"
|
||||
case binding.MinSizeError:
|
||||
case binding.ERR_MIN_SIZE:
|
||||
data["ErrorMsg"] = fieldName + " must contain at least " + auth.GetMinSize(field) + " characters"
|
||||
case binding.MaxSizeError:
|
||||
case binding.ERR_MAX_SIZE:
|
||||
data["ErrorMsg"] = fieldName + " must contain at most " + auth.GetMaxSize(field) + " characters"
|
||||
case binding.EmailError:
|
||||
case binding.ERR_EMAIL:
|
||||
data["ErrorMsg"] = fieldName + " is not a valid e-mail address"
|
||||
case binding.UrlError:
|
||||
case binding.ERR_URL:
|
||||
data["ErrorMsg"] = fieldName + " is not a valid URL"
|
||||
default:
|
||||
data["ErrorMsg"] = "Unknown error: " + errs[0].Classification
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/uuid"
|
||||
)
|
||||
|
||||
// SignedInId returns the id of signed in user.
|
||||
@@ -60,6 +61,7 @@ func SignedInId(req *http.Request, sess session.Store) int64 {
|
||||
}
|
||||
|
||||
// SignedInUser returns the user object of signed user.
|
||||
// It returns a bool value to indicate whether user uses basic auth or not.
|
||||
func SignedInUser(req *http.Request, sess session.Store) (*models.User, bool) {
|
||||
if !models.HasEngine {
|
||||
return nil, false
|
||||
@@ -75,8 +77,25 @@ func SignedInUser(req *http.Request, sess session.Store) (*models.User, bool) {
|
||||
if err != nil {
|
||||
if err != models.ErrUserNotExist {
|
||||
log.Error(4, "GetUserByName: %v", err)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Check if enabled auto-registration.
|
||||
if setting.Service.EnableReverseProxyAutoRegister {
|
||||
u := &models.User{
|
||||
Name: webAuthUser,
|
||||
Email: uuid.NewV4().String() + "@localhost",
|
||||
Passwd: webAuthUser,
|
||||
IsActive: true,
|
||||
}
|
||||
if err = models.CreateUser(u); err != nil {
|
||||
// FIXME: should I create a system notice?
|
||||
log.Error(4, "CreateUser: %v", err)
|
||||
return nil, false
|
||||
} else {
|
||||
return u, false
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
return u, false
|
||||
}
|
||||
@@ -185,19 +204,19 @@ func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaro
|
||||
data["Err_"+field.Name] = true
|
||||
trName := l.Tr("form." + field.Name)
|
||||
switch errs[0].Classification {
|
||||
case binding.RequiredError:
|
||||
case binding.ERR_REQUIRED:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.require_error")
|
||||
case binding.AlphaDashError:
|
||||
case binding.ERR_ALPHA_DASH:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_error")
|
||||
case binding.AlphaDashDotError:
|
||||
case binding.ERR_ALPHA_DASH_DOT:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_dot_error")
|
||||
case binding.MinSizeError:
|
||||
case binding.ERR_MIN_SIZE:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.min_size_error", GetMinSize(field))
|
||||
case binding.MaxSizeError:
|
||||
case binding.ERR_MAX_SIZE:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.max_size_error", GetMaxSize(field))
|
||||
case binding.EmailError:
|
||||
case binding.ERR_EMAIL:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.email_error")
|
||||
case binding.UrlError:
|
||||
case binding.ERR_URL:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.url_error")
|
||||
default:
|
||||
data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + errs[0].Classification
|
||||
|
||||
@@ -20,7 +20,7 @@ type Ldapsource struct {
|
||||
Port int // port number
|
||||
UseSSL bool // Use SSL
|
||||
BaseDN string // Base DN
|
||||
Attributes string // Attribut to search
|
||||
Attributes string // Attribute to search
|
||||
Filter string // Query filter to validate entry
|
||||
MsAdSAFormat string // in the case of MS AD Simple Authen, the format to use (see: http://msdn.microsoft.com/en-us/library/cc223499.aspx)
|
||||
Enabled bool // if this source is disabled
|
||||
@@ -37,7 +37,7 @@ func AddSource(name string, host string, port int, usessl bool, basedn string, a
|
||||
Authensource = append(Authensource, ldaphost)
|
||||
}
|
||||
|
||||
//LoginUser : try to login an user to LDAP sources, return requested (attribut,true) if ok, ("",false) other wise
|
||||
//LoginUser : try to login an user to LDAP sources, return requested (attribute,true) if ok, ("",false) other wise
|
||||
//First match wins
|
||||
//Returns first attribute if exists
|
||||
func LoginUser(name, passwd string) (a string, r bool) {
|
||||
|
||||
@@ -21,9 +21,9 @@ type CreateRepoForm struct {
|
||||
RepoName string `form:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"`
|
||||
Private bool `form:"private"`
|
||||
Description string `form:"desc" binding:"MaxSize(255)"`
|
||||
AutoInit bool `form:"auto_init"`
|
||||
Gitignore string `form:"gitignore"`
|
||||
License string `form:"license"`
|
||||
InitReadme bool `form:"init_readme"`
|
||||
}
|
||||
|
||||
func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
@@ -164,7 +164,6 @@ func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) bin
|
||||
}
|
||||
|
||||
type EditReleaseForm struct {
|
||||
Target string `form:"tag_target" binding:"Required"`
|
||||
Title string `form:"title" binding:"Required"`
|
||||
Content string `form:"content" binding:"Required"`
|
||||
Draft string `form:"draft"`
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"mime/multipart"
|
||||
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/binding"
|
||||
)
|
||||
@@ -86,6 +88,23 @@ func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs binding.Errors)
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type UploadAvatarForm struct {
|
||||
Enable bool `form:"enable"`
|
||||
Avatar *multipart.FileHeader `form:"avatar"`
|
||||
}
|
||||
|
||||
func (f *UploadAvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type AddEmailForm struct {
|
||||
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
|
||||
}
|
||||
|
||||
func (f *AddEmailForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type ChangePasswordForm struct {
|
||||
OldPassword string `form:"old_password" binding:"Required;MinSize(6);MaxSize(255)"`
|
||||
Password string `form:"password" binding:"Required;MinSize(6);MaxSize(255)"`
|
||||
|
||||
@@ -46,10 +46,14 @@ func init() {
|
||||
}
|
||||
|
||||
// hash email to md5 string
|
||||
// keep this func in order to make this package indenpent
|
||||
// keep this func in order to make this package independent
|
||||
func HashEmail(email string) string {
|
||||
// https://en.gravatar.com/site/implement/hash/
|
||||
email = strings.TrimSpace(email)
|
||||
email = strings.ToLower(email)
|
||||
|
||||
h := md5.New()
|
||||
h.Write([]byte(strings.ToLower(email)))
|
||||
h.Write([]byte(email))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
@@ -121,7 +125,7 @@ func (this *Avatar) Encode(wr io.Writer, size int) (err error) {
|
||||
if img, err = decodeImageFile(imgPath); err != nil {
|
||||
return
|
||||
}
|
||||
m := resize.Resize(uint(size), 0, img, resize.Lanczos3)
|
||||
m := resize.Resize(uint(size), 0, img, resize.NearestNeighbor)
|
||||
return jpeg.Encode(wr, m, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -100,10 +100,11 @@ func (options *CustomRender) Image(out *bytes.Buffer, link []byte, title []byte,
|
||||
}
|
||||
|
||||
var (
|
||||
MentionPattern = regexp.MustCompile(`@[0-9a-zA-Z_]{1,}`)
|
||||
commitPattern = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`)
|
||||
issueFullPattern = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
|
||||
issueIndexPattern = regexp.MustCompile(`#[0-9]+`)
|
||||
MentionPattern = regexp.MustCompile(`@[0-9a-zA-Z_]{1,}`)
|
||||
commitPattern = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`)
|
||||
issueFullPattern = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
|
||||
issueIndexPattern = regexp.MustCompile(`#[0-9]+`)
|
||||
sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`)
|
||||
)
|
||||
|
||||
func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte {
|
||||
@@ -153,7 +154,22 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte {
|
||||
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(
|
||||
` <a href="%s">#%s</a>`, m, ShortSha(string(m[i+7:j])))), -1)
|
||||
}
|
||||
ms = issueIndexPattern.FindAll(rawBytes, -1)
|
||||
rawBytes = RenderIssueIndexPattern(rawBytes, urlPrefix)
|
||||
rawBytes = RenderSha1CurrentPattern(rawBytes, urlPrefix)
|
||||
return rawBytes
|
||||
}
|
||||
|
||||
func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte {
|
||||
ms := sha1CurrentPattern.FindAll(rawBytes, -1)
|
||||
for _, m := range ms {
|
||||
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(
|
||||
`<a href="%s/commit/%s"><code>%s</code></a>`, urlPrefix, m, ShortSha(string(m)))), -1)
|
||||
}
|
||||
return rawBytes
|
||||
}
|
||||
|
||||
func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string) []byte {
|
||||
ms := issueIndexPattern.FindAll(rawBytes, -1)
|
||||
for _, m := range ms {
|
||||
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(
|
||||
`<a href="%s/issues/%s">%s</a>`, urlPrefix, m[1:], m)), -1)
|
||||
|
||||
@@ -47,18 +47,23 @@ func ShortSha(sha1 string) string {
|
||||
return sha1
|
||||
}
|
||||
|
||||
func ToUtf8WithErr(content []byte) (error, string) {
|
||||
func DetectEncoding(content []byte) (string, error) {
|
||||
detector := chardet.NewTextDetector()
|
||||
result, err := detector.DetectBest(content)
|
||||
return result.Charset, err
|
||||
}
|
||||
|
||||
func ToUtf8WithErr(content []byte) (error, string) {
|
||||
charset, err := DetectEncoding(content)
|
||||
if err != nil {
|
||||
return err, ""
|
||||
}
|
||||
|
||||
if result.Charset == "utf8" {
|
||||
if charset == "utf8" {
|
||||
return nil, string(content)
|
||||
}
|
||||
|
||||
decoder := mahonia.NewDecoder(result.Charset)
|
||||
decoder := mahonia.NewDecoder(charset)
|
||||
if decoder != nil {
|
||||
return nil, decoder.ConvertString(string(content))
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"html/template"
|
||||
@@ -23,6 +22,7 @@ import (
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/Unknwon/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/avatar"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
@@ -40,20 +40,14 @@ func EncodeSha1(str string) string {
|
||||
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)
|
||||
func BasicAuthDecode(encoded string) (string, string, error) {
|
||||
s, err := base64.StdEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return user, name, err
|
||||
return "", "", 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
|
||||
auth := strings.SplitN(string(s), ":", 2)
|
||||
return auth[0], auth[1], nil
|
||||
}
|
||||
|
||||
func BasicAuthEncode(username, password string) string {
|
||||
@@ -177,10 +171,13 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string
|
||||
func AvatarLink(email string) string {
|
||||
if setting.DisableGravatar {
|
||||
return setting.AppSubUrl + "/img/avatar_default.jpg"
|
||||
} else if setting.Service.EnableCacheAvatar {
|
||||
return setting.AppSubUrl + "/avatar/" + EncodeMd5(email)
|
||||
}
|
||||
return setting.GravatarSource + EncodeMd5(email)
|
||||
|
||||
gravatarHash := avatar.HashEmail(email)
|
||||
if setting.Service.EnableCacheAvatar {
|
||||
return setting.AppSubUrl + "/avatar/" + gravatarHash
|
||||
}
|
||||
return setting.GravatarSource + gravatarHash
|
||||
}
|
||||
|
||||
// Seconds-based time units
|
||||
|
||||
@@ -16,6 +16,9 @@ var c = New()
|
||||
func NewCronContext() {
|
||||
c.AddFunc("Update mirrors", "@every 1h", models.MirrorUpdate)
|
||||
c.AddFunc("Deliver hooks", fmt.Sprintf("@every %dm", setting.WebhookTaskInterval), models.DeliverHooks)
|
||||
if setting.Git.Fsck.Enable {
|
||||
c.AddFunc("Repository health check", fmt.Sprintf("@every %dh", setting.Git.Fsck.Interval), models.GitFsck)
|
||||
}
|
||||
c.Start()
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/gogits/gogs/modules/asn1-ber"
|
||||
)
|
||||
|
||||
// debbuging type
|
||||
// debugging type
|
||||
// - has a Printf method to write the debug output
|
||||
type debugging bool
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ func newLogger(buffer int64) *Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
// SetLogger sets new logger instanse with given logger adapter and config.
|
||||
// SetLogger sets new logger instance with given logger adapter and config.
|
||||
func (l *Logger) SetLogger(adapter string, config string) error {
|
||||
l.lock.Lock()
|
||||
defer l.lock.Unlock()
|
||||
|
||||
@@ -32,7 +32,7 @@ const (
|
||||
)
|
||||
|
||||
// A Decoder is a function that decodes a character set, one character at a time.
|
||||
// It works much like utf8.DecodeRune, but has an aditional status return value.
|
||||
// It works much like utf8.DecodeRune, but has an additional status return value.
|
||||
type Decoder func(p []byte) (c rune, size int, status Status)
|
||||
|
||||
// An Encoder is a function that encodes a character set, one character at a time.
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
|
||||
const (
|
||||
AUTH_ACTIVE base.TplName = "mail/auth/active"
|
||||
AUTH_ACTIVATE_EMAIL base.TplName = "mail/auth/activate_email"
|
||||
AUTH_REGISTER_SUCCESS base.TplName = "mail/auth/register_success"
|
||||
AUTH_RESET_PASSWORD base.TplName = "mail/auth/reset_passwd"
|
||||
|
||||
@@ -30,9 +31,7 @@ const (
|
||||
|
||||
// Create New mail message use MailFrom and MailUser
|
||||
func NewMailMessageFrom(To []string, from, subject, body string) Message {
|
||||
msg := NewHtmlMessage(To, from, subject, body)
|
||||
msg.User = setting.MailService.User
|
||||
return msg
|
||||
return NewHtmlMessage(To, from, subject, body)
|
||||
}
|
||||
|
||||
// Create New mail message use MailFrom and MailUser
|
||||
@@ -64,6 +63,17 @@ func CreateUserActiveCode(u *models.User, startInf interface{}) string {
|
||||
return code
|
||||
}
|
||||
|
||||
// create a time limit code for user active
|
||||
func CreateUserEmailActivateCode(u *models.User, e *models.EmailAddress, startInf interface{}) string {
|
||||
minutes := setting.Service.ActiveCodeLives
|
||||
data := com.ToStr(u.Id) + e.Email + u.LowerName + u.Passwd + u.Rands
|
||||
code := base.CreateTimeLimitCode(data, minutes, startInf)
|
||||
|
||||
// add tail hex username
|
||||
code += hex.EncodeToString([]byte(u.LowerName))
|
||||
return code
|
||||
}
|
||||
|
||||
// Send user register mail with active code
|
||||
func SendRegisterMail(r macaron.Render, u *models.User) {
|
||||
code := CreateUserActiveCode(u, nil)
|
||||
@@ -103,6 +113,27 @@ func SendActiveMail(r macaron.Render, u *models.User) {
|
||||
SendAsync(&msg)
|
||||
}
|
||||
|
||||
// Send email to verify secondary email.
|
||||
func SendActivateEmail(r macaron.Render, user *models.User, email *models.EmailAddress) {
|
||||
code := CreateUserEmailActivateCode(user, email, nil)
|
||||
|
||||
subject := "Verify your e-mail address"
|
||||
|
||||
data := GetMailTmplData(user)
|
||||
data["Code"] = code
|
||||
data["Email"] = email.Email
|
||||
body, err := r.HTMLString(string(AUTH_ACTIVATE_EMAIL), data)
|
||||
if err != nil {
|
||||
log.Error(4, "mail.SendActiveMail(fail to render): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
msg := NewMailMessage([]string{email.Email}, subject, body)
|
||||
msg.Info = fmt.Sprintf("UID: %d, send activate email to %s", user.Id, email.Email)
|
||||
|
||||
SendAsync(&msg)
|
||||
}
|
||||
|
||||
// Send reset password email.
|
||||
func SendResetPasswdMail(r macaron.Render, u *models.User) {
|
||||
code := CreateUserActiveCode(u, nil)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
@@ -20,7 +21,6 @@ type Message struct {
|
||||
From string
|
||||
Subject string
|
||||
Body string
|
||||
User string
|
||||
Type string
|
||||
Massive bool
|
||||
Info string
|
||||
@@ -35,15 +35,14 @@ func (m Message) Content() string {
|
||||
}
|
||||
|
||||
// create mail content
|
||||
content := "From: \"" + m.From + "\" <" + m.User +
|
||||
">\r\nSubject: " + m.Subject + "\r\nContent-Type: " + contentType + "\r\n\r\n" + m.Body
|
||||
content := "From: " + m.From + "\r\nSubject: " + m.Subject + "\r\nContent-Type: " + contentType + "\r\n\r\n" + m.Body
|
||||
return content
|
||||
}
|
||||
|
||||
var mailQueue chan *Message
|
||||
|
||||
func NewMailerContext() {
|
||||
mailQueue = make(chan *Message, setting.Cfg.MustInt("mailer", "SEND_BUFFER_LEN", 10))
|
||||
mailQueue = make(chan *Message, setting.Cfg.Section("mailer").Key("SEND_BUFFER_LEN").MustInt(10))
|
||||
go processMailQueue()
|
||||
}
|
||||
|
||||
@@ -67,29 +66,67 @@ 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)
|
||||
func sendMail(settings *setting.Mailer, recipients []string, msgContent []byte) error {
|
||||
host, port, err := net.SplitHostPort(settings.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, _, _ := net.SplitHostPort(hostAddressWithPort)
|
||||
tlsConn := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
tlsconfig := &tls.Config{
|
||||
InsecureSkipVerify: settings.SkipVerify,
|
||||
ServerName: host,
|
||||
}
|
||||
if err = client.StartTLS(tlsConn); err != nil {
|
||||
|
||||
conn, err := net.Dial("tcp", net.JoinHostPort(host, port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
isSecureConn := false
|
||||
// Start TLS directly if the port ends with 465 (SMTPS protocol)
|
||||
if strings.HasSuffix(port, "465") {
|
||||
conn = tls.Client(conn, tlsconfig)
|
||||
isSecureConn = true
|
||||
}
|
||||
|
||||
client, err := smtp.NewClient(conn, host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok, _ := client.Extension("AUTH"); ok && auth != nil {
|
||||
if err = client.Auth(auth); err != nil {
|
||||
// If not using SMTPS, alway use STARTTLS if available
|
||||
hasStartTLS, _ := client.Extension("STARTTLS")
|
||||
if !isSecureConn && hasStartTLS {
|
||||
if err = client.StartTLS(tlsconfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = client.Mail(from); err != nil {
|
||||
canAuth, options := client.Extension("AUTH")
|
||||
|
||||
if canAuth && len(settings.User) > 0 {
|
||||
var auth smtp.Auth
|
||||
|
||||
if strings.Contains(options, "CRAM-MD5") {
|
||||
auth = smtp.CRAMMD5Auth(settings.User, settings.Passwd)
|
||||
} else if strings.Contains(options, "PLAIN") {
|
||||
auth = smtp.PlainAuth("", settings.User, settings.Passwd, host)
|
||||
}
|
||||
|
||||
if auth != nil {
|
||||
if err = client.Auth(auth); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if fromAddress, err := mail.ParseAddress(settings.From); err != nil {
|
||||
return err
|
||||
} else {
|
||||
if err = client.Mail(fromAddress.Address); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, rec := range recipients {
|
||||
@@ -116,7 +153,6 @@ func sendMail(hostAddressWithPort string, auth smtp.Auth, from string, recipient
|
||||
// Direct Send mail message
|
||||
func Send(msg *Message) (int, error) {
|
||||
log.Trace("Sending mails to: %s", strings.Join(msg.To, "; "))
|
||||
host := strings.Split(setting.MailService.Host, ":")
|
||||
|
||||
// get message body
|
||||
content := msg.Content()
|
||||
@@ -127,14 +163,12 @@ func Send(msg *Message) (int, error) {
|
||||
return 0, fmt.Errorf("empty email body")
|
||||
}
|
||||
|
||||
auth := smtp.PlainAuth("", setting.MailService.User, setting.MailService.Passwd, host[0])
|
||||
|
||||
if msg.Massive {
|
||||
// send mail to multiple emails one by one
|
||||
num := 0
|
||||
for _, to := range msg.To {
|
||||
body := []byte("To: " + to + "\r\n" + content)
|
||||
err := sendMail(setting.MailService.Host, auth, msg.From, []string{to}, body)
|
||||
err := sendMail(setting.MailService, []string{to}, body)
|
||||
if err != nil {
|
||||
return num, err
|
||||
}
|
||||
@@ -145,7 +179,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 := sendMail(setting.MailService.Host, auth, msg.From, msg.To, body)
|
||||
err := sendMail(setting.MailService, msg.To, body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
|
||||
@@ -29,6 +29,12 @@ func Toggle(options *ToggleOptions) macaron.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
// Checking non-logged users landing page.
|
||||
if !ctx.IsSigned && ctx.Req.RequestURI == "/" && setting.LandingPageUrl != setting.LANDING_PAGE_HOME {
|
||||
ctx.Redirect(string(setting.LandingPageUrl))
|
||||
return
|
||||
}
|
||||
|
||||
// Redirect to dashboard if user tries to visit any non-login page.
|
||||
if options.SignOutRequire && ctx.IsSigned && ctx.Req.RequestURI != "/" {
|
||||
ctx.Redirect(setting.AppSubUrl + "/")
|
||||
|
||||
@@ -39,29 +39,25 @@ type Context struct {
|
||||
IsBasicAuth bool
|
||||
|
||||
Repo struct {
|
||||
IsOwner bool
|
||||
IsTrueOwner bool
|
||||
IsWatching bool
|
||||
IsBranch bool
|
||||
IsTag bool
|
||||
IsCommit bool
|
||||
IsAdmin bool // Current user is admin level.
|
||||
HasAccess bool
|
||||
Repository *models.Repository
|
||||
Owner *models.User
|
||||
Commit *git.Commit
|
||||
Tag *git.Tag
|
||||
GitRepo *git.Repository
|
||||
BranchName string
|
||||
TagName string
|
||||
TreeName string
|
||||
CommitId string
|
||||
RepoLink string
|
||||
CloneLink struct {
|
||||
SSH string
|
||||
HTTPS string
|
||||
Git string
|
||||
}
|
||||
IsOwner bool
|
||||
IsTrueOwner bool
|
||||
IsWatching bool
|
||||
IsBranch bool
|
||||
IsTag bool
|
||||
IsCommit bool
|
||||
IsAdmin bool // Current user is admin level.
|
||||
HasAccess bool
|
||||
Repository *models.Repository
|
||||
Owner *models.User
|
||||
Commit *git.Commit
|
||||
Tag *git.Tag
|
||||
GitRepo *git.Repository
|
||||
BranchName string
|
||||
TagName string
|
||||
TreeName string
|
||||
CommitId string
|
||||
RepoLink string
|
||||
CloneLink models.CloneLink
|
||||
CommitsCount int
|
||||
Mirror *models.Mirror
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
ctx.Data["Org"] = org
|
||||
|
||||
if ctx.IsSigned {
|
||||
ctx.Org.IsOwner = org.IsOrgOwner(ctx.User.Id)
|
||||
ctx.Org.IsOwner = org.IsOwnedBy(ctx.User.Id)
|
||||
if ctx.Org.IsOwner {
|
||||
ctx.Org.IsMember = true
|
||||
ctx.Org.IsAdminTeam = true
|
||||
|
||||
@@ -55,7 +55,7 @@ func ApiRepoAssignment() macaron.Handler {
|
||||
ctx.Repo.Owner = u
|
||||
|
||||
// Organization owner team members are true owners as well.
|
||||
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgOwner(ctx.User.Id) {
|
||||
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
|
||||
ctx.Repo.IsTrueOwner = true
|
||||
}
|
||||
|
||||
@@ -280,7 +280,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
ctx.Repo.Owner = u
|
||||
|
||||
// Organization owner team members are true owners as well.
|
||||
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgOwner(ctx.User.Id) {
|
||||
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
|
||||
ctx.Repo.IsTrueOwner = true
|
||||
}
|
||||
|
||||
@@ -386,12 +386,11 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner
|
||||
ctx.Data["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner
|
||||
|
||||
if setting.SshPort != 22 {
|
||||
ctx.Repo.CloneLink.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.Domain, setting.SshPort, u.LowerName, repo.LowerName)
|
||||
} else {
|
||||
ctx.Repo.CloneLink.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.Domain, u.LowerName, repo.LowerName)
|
||||
ctx.Repo.CloneLink, err = repo.CloneLink()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "CloneLink", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, u.LowerName, repo.LowerName)
|
||||
ctx.Data["CloneLink"] = ctx.Repo.CloneLink
|
||||
|
||||
if ctx.Repo.Repository.IsGoget {
|
||||
@@ -402,6 +401,8 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
// repo is bare and display enable
|
||||
if ctx.Repo.Repository.IsBare {
|
||||
log.Debug("Bare repository: %s", ctx.Repo.RepoLink)
|
||||
// NOTE: to prevent templating error
|
||||
ctx.Data["BranchName"] = ""
|
||||
if displayBare {
|
||||
ctx.HTML(200, "repo/bare")
|
||||
}
|
||||
@@ -451,7 +452,7 @@ func RequireTrueOwner() macaron.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// GitHookService checks if repsitory Git hooks service has been enabled.
|
||||
// GitHookService checks if repository Git hooks service has been enabled.
|
||||
func GitHookService() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.User.AllowGitHook && !ctx.User.IsAdmin {
|
||||
|
||||
@@ -16,11 +16,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/Unknwon/goconfig"
|
||||
"github.com/macaron-contrib/oauth2"
|
||||
"github.com/macaron-contrib/session"
|
||||
"gopkg.in/ini.v1"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
// "github.com/gogits/gogs-ng/modules/ssh"
|
||||
// "github.com/gogits/gogs/modules/ssh"
|
||||
)
|
||||
|
||||
type Scheme string
|
||||
@@ -31,6 +32,13 @@ const (
|
||||
FCGI Scheme = "fcgi"
|
||||
)
|
||||
|
||||
type LandingPage string
|
||||
|
||||
const (
|
||||
LANDING_PAGE_HOME LandingPage = "/"
|
||||
LANDING_PAGE_EXPLORE LandingPage = "/explore"
|
||||
)
|
||||
|
||||
var (
|
||||
// App settings.
|
||||
AppVer string
|
||||
@@ -48,6 +56,7 @@ var (
|
||||
CertFile, KeyFile string
|
||||
StaticRootPath string
|
||||
EnableGzip bool
|
||||
LandingPageUrl LandingPage
|
||||
|
||||
// Security settings.
|
||||
InstallLock bool
|
||||
@@ -66,9 +75,10 @@ var (
|
||||
ScriptType string
|
||||
|
||||
// Picture settings.
|
||||
PictureService string
|
||||
GravatarSource string
|
||||
DisableGravatar bool
|
||||
PictureService string
|
||||
AvatarUploadPath string
|
||||
GravatarSource string
|
||||
DisableGravatar bool
|
||||
|
||||
// Log settings.
|
||||
LogRootPath string
|
||||
@@ -94,17 +104,24 @@ var (
|
||||
EnableMemcache bool
|
||||
|
||||
// Session settings.
|
||||
SessionProvider string
|
||||
SessionConfig *session.Config
|
||||
SessionConfig session.Options
|
||||
|
||||
// Git settings.
|
||||
MaxGitDiffLines int
|
||||
Git struct {
|
||||
MaxGitDiffLines int
|
||||
GcArgs []string `delim:" "`
|
||||
Fsck struct {
|
||||
Enable bool
|
||||
Interval int
|
||||
Args []string `delim:" "`
|
||||
} `ini:"git.fsck"`
|
||||
}
|
||||
|
||||
// I18n settings.
|
||||
Langs, Names []string
|
||||
|
||||
// Global setting objects.
|
||||
Cfg *goconfig.ConfigFile
|
||||
Cfg *ini.File
|
||||
ConfRootPath string
|
||||
CustomPath string // Custom directory path.
|
||||
ProdMode bool
|
||||
@@ -145,7 +162,7 @@ func NewConfigContext() {
|
||||
}
|
||||
ConfRootPath = path.Join(workDir, "conf")
|
||||
|
||||
Cfg, err = goconfig.LoadConfigFile(path.Join(workDir, "conf/app.ini"))
|
||||
Cfg, err = ini.Load(path.Join(workDir, "conf/app.ini"))
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to parse 'conf/app.ini': %v", err)
|
||||
}
|
||||
@@ -157,15 +174,19 @@ func NewConfigContext() {
|
||||
|
||||
cfgPath := path.Join(CustomPath, "conf/app.ini")
|
||||
if com.IsFile(cfgPath) {
|
||||
if err = Cfg.AppendFiles(cfgPath); err != nil {
|
||||
if err = Cfg.Append(cfgPath); err != nil {
|
||||
log.Fatal(4, "Fail to load custom 'conf/app.ini': %v", err)
|
||||
}
|
||||
} else {
|
||||
log.Warn("No custom 'conf/app.ini' found, please go to '/install'")
|
||||
}
|
||||
Cfg.NameMapper = ini.AllCapsUnderscore
|
||||
|
||||
AppName = Cfg.MustValue("", "APP_NAME", "Gogs: Go Git Service")
|
||||
AppUrl = Cfg.MustValue("server", "ROOT_URL", "http://localhost:3000/")
|
||||
LogRootPath = Cfg.Section("log").Key("ROOT_PATH").MustString(path.Join(workDir, "log"))
|
||||
|
||||
sec := Cfg.Section("server")
|
||||
AppName = Cfg.Section("").Key("APP_NAME").MustString("Gogs: Go Git Service")
|
||||
AppUrl = sec.Key("ROOT_URL").MustString("http://localhost:3000/")
|
||||
if AppUrl[len(AppUrl)-1] != '/' {
|
||||
AppUrl += "/"
|
||||
}
|
||||
@@ -178,36 +199,43 @@ func NewConfigContext() {
|
||||
AppSubUrl = strings.TrimSuffix(url.Path, "/")
|
||||
|
||||
Protocol = HTTP
|
||||
if Cfg.MustValue("server", "PROTOCOL") == "https" {
|
||||
if sec.Key("PROTOCOL").String() == "https" {
|
||||
Protocol = HTTPS
|
||||
CertFile = Cfg.MustValue("server", "CERT_FILE")
|
||||
KeyFile = Cfg.MustValue("server", "KEY_FILE")
|
||||
}
|
||||
if Cfg.MustValue("server", "PROTOCOL") == "fcgi" {
|
||||
CertFile = sec.Key("CERT_FILE").String()
|
||||
KeyFile = sec.Key("KEY_FILE").String()
|
||||
} else if sec.Key("PROTOCOL").String() == "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")
|
||||
SshPort = Cfg.MustInt("server", "SSH_PORT", 22)
|
||||
OfflineMode = Cfg.MustBool("server", "OFFLINE_MODE")
|
||||
DisableRouterLog = Cfg.MustBool("server", "DISABLE_ROUTER_LOG")
|
||||
StaticRootPath = Cfg.MustValue("server", "STATIC_ROOT_PATH", workDir)
|
||||
LogRootPath = Cfg.MustValue("log", "ROOT_PATH", path.Join(workDir, "log"))
|
||||
EnableGzip = Cfg.MustBool("server", "ENABLE_GZIP")
|
||||
Domain = sec.Key("DOMAIN").MustString("localhost")
|
||||
HttpAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
|
||||
HttpPort = sec.Key("HTTP_PORT").MustString("3000")
|
||||
SshPort = sec.Key("SSH_PORT").MustInt(22)
|
||||
OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
|
||||
DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
|
||||
StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(workDir)
|
||||
EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
|
||||
|
||||
InstallLock = Cfg.MustBool("security", "INSTALL_LOCK")
|
||||
SecretKey = Cfg.MustValue("security", "SECRET_KEY")
|
||||
LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS")
|
||||
CookieUserName = Cfg.MustValue("security", "COOKIE_USERNAME")
|
||||
CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME")
|
||||
ReverseProxyAuthUser = Cfg.MustValue("security", "REVERSE_PROXY_AUTHENTICATION_USER", "X-WEBAUTH-USER")
|
||||
switch sec.Key("LANDING_PAGE").MustString("home") {
|
||||
case "explore":
|
||||
LandingPageUrl = LANDING_PAGE_EXPLORE
|
||||
default:
|
||||
LandingPageUrl = LANDING_PAGE_HOME
|
||||
}
|
||||
|
||||
AttachmentPath = Cfg.MustValue("attachment", "PATH", "data/attachments")
|
||||
AttachmentAllowedTypes = Cfg.MustValue("attachment", "ALLOWED_TYPES", "image/jpeg|image/png")
|
||||
AttachmentMaxSize = Cfg.MustInt64("attachment", "MAX_SIZE", 32)
|
||||
AttachmentMaxFiles = Cfg.MustInt("attachment", "MAX_FILES", 10)
|
||||
AttachmentEnabled = Cfg.MustBool("attachment", "ENABLE", true)
|
||||
sec = Cfg.Section("security")
|
||||
InstallLock = sec.Key("INSTALL_LOCK").MustBool()
|
||||
SecretKey = sec.Key("SECRET_KEY").String()
|
||||
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt()
|
||||
CookieUserName = sec.Key("COOKIE_USERNAME").String()
|
||||
CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").String()
|
||||
ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
|
||||
|
||||
sec = Cfg.Section("attachment")
|
||||
AttachmentPath = sec.Key("PATH").MustString("data/attachments")
|
||||
AttachmentAllowedTypes = sec.Key("ALLOWED_TYPES").MustString("image/jpeg|image/png")
|
||||
AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(32)
|
||||
AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(10)
|
||||
AttachmentEnabled = sec.Key("ENABLE").MustBool(true)
|
||||
|
||||
TimeFormat = map[string]string{
|
||||
"ANSIC": time.ANSIC,
|
||||
@@ -225,13 +253,13 @@ func NewConfigContext() {
|
||||
"StampMilli": time.StampMilli,
|
||||
"StampMicro": time.StampMicro,
|
||||
"StampNano": time.StampNano,
|
||||
}[Cfg.MustValue("time", "FORMAT", "RFC1123")]
|
||||
}[Cfg.Section("time").Key("FORMAT").MustString("RFC1123")]
|
||||
|
||||
if err = os.MkdirAll(AttachmentPath, os.ModePerm); err != nil {
|
||||
log.Fatal(4, "Could not create directory %s: %s", AttachmentPath, err)
|
||||
}
|
||||
|
||||
RunUser = Cfg.MustValue("", "RUN_USER")
|
||||
RunUser = Cfg.Section("").Key("RUN_USER").String()
|
||||
curUser := os.Getenv("USER")
|
||||
if len(curUser) == 0 {
|
||||
curUser = os.Getenv("USERNAME")
|
||||
@@ -241,59 +269,65 @@ func NewConfigContext() {
|
||||
log.Fatal(4, "Expect user(%s) but current user is: %s", RunUser, curUser)
|
||||
}
|
||||
|
||||
// Determine and create root git reposiroty path.
|
||||
// Determine and create root git repository path.
|
||||
homeDir, err := com.HomeDir()
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to get home directory: %v", err)
|
||||
}
|
||||
RepoRootPath = Cfg.MustValue("repository", "ROOT", filepath.Join(homeDir, "gogs-repositories"))
|
||||
sec = Cfg.Section("repository")
|
||||
RepoRootPath = sec.Key("ROOT").MustString(filepath.Join(homeDir, "gogs-repositories"))
|
||||
if !filepath.IsAbs(RepoRootPath) {
|
||||
RepoRootPath = filepath.Join(workDir, RepoRootPath)
|
||||
} else {
|
||||
RepoRootPath = filepath.Clean(RepoRootPath)
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil {
|
||||
log.Fatal(4, "Fail to create repository root path(%s): %v", RepoRootPath, err)
|
||||
}
|
||||
ScriptType = Cfg.MustValue("repository", "SCRIPT_TYPE", "bash")
|
||||
ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash")
|
||||
|
||||
PictureService = Cfg.MustValueRange("picture", "SERVICE", "server", []string{"server"})
|
||||
switch Cfg.MustValue("picture", "GRAVATAR_SOURCE", "gravatar") {
|
||||
sec = Cfg.Section("picture")
|
||||
PictureService = sec.Key("SERVICE").In("server", []string{"server"})
|
||||
AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString("data/avatars")
|
||||
os.MkdirAll(AvatarUploadPath, os.ModePerm)
|
||||
switch sec.Key("GRAVATAR_SOURCE").MustString("gravatar") {
|
||||
case "duoshuo":
|
||||
GravatarSource = "http://gravatar.duoshuo.com/avatar/"
|
||||
default:
|
||||
GravatarSource = "//1.gravatar.com/avatar/"
|
||||
}
|
||||
DisableGravatar = Cfg.MustBool("picture", "DISABLE_GRAVATAR")
|
||||
DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
|
||||
|
||||
MaxGitDiffLines = Cfg.MustInt("git", "MAX_GITDIFF_LINES", 10000)
|
||||
if err = Cfg.Section("git").MapTo(&Git); err != nil {
|
||||
log.Fatal(4, "Fail to map Git settings: %v", err)
|
||||
}
|
||||
|
||||
Langs = Cfg.MustValueArray("i18n", "LANGS", ",")
|
||||
Names = Cfg.MustValueArray("i18n", "NAMES", ",")
|
||||
Langs = Cfg.Section("i18n").Key("LANGS").Strings(",")
|
||||
Names = Cfg.Section("i18n").Key("NAMES").Strings(",")
|
||||
|
||||
HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
|
||||
}
|
||||
|
||||
var Service struct {
|
||||
RegisterEmailConfirm bool
|
||||
DisableRegistration bool
|
||||
RequireSignInView bool
|
||||
EnableCacheAvatar bool
|
||||
EnableNotifyMail bool
|
||||
EnableReverseProxyAuth bool
|
||||
LdapAuth bool
|
||||
ActiveCodeLives int
|
||||
ResetPwdCodeLives int
|
||||
RegisterEmailConfirm bool
|
||||
DisableRegistration bool
|
||||
RequireSignInView bool
|
||||
EnableCacheAvatar bool
|
||||
EnableNotifyMail bool
|
||||
EnableReverseProxyAuth bool
|
||||
EnableReverseProxyAutoRegister bool
|
||||
ActiveCodeLives int
|
||||
ResetPwdCodeLives int
|
||||
}
|
||||
|
||||
func newService() {
|
||||
Service.ActiveCodeLives = Cfg.MustInt("service", "ACTIVE_CODE_LIVE_MINUTES", 180)
|
||||
Service.ResetPwdCodeLives = Cfg.MustInt("service", "RESET_PASSWD_CODE_LIVE_MINUTES", 180)
|
||||
Service.DisableRegistration = Cfg.MustBool("service", "DISABLE_REGISTRATION")
|
||||
Service.RequireSignInView = Cfg.MustBool("service", "REQUIRE_SIGNIN_VIEW")
|
||||
Service.EnableCacheAvatar = Cfg.MustBool("service", "ENABLE_CACHE_AVATAR")
|
||||
Service.EnableReverseProxyAuth = Cfg.MustBool("service", "ENABLE_REVERSE_PROXY_AUTHENTICATION")
|
||||
Service.ActiveCodeLives = Cfg.Section("service").Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
|
||||
Service.ResetPwdCodeLives = Cfg.Section("service").Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)
|
||||
Service.DisableRegistration = Cfg.Section("service").Key("DISABLE_REGISTRATION").MustBool()
|
||||
Service.RequireSignInView = Cfg.Section("service").Key("REQUIRE_SIGNIN_VIEW").MustBool()
|
||||
Service.EnableCacheAvatar = Cfg.Section("service").Key("ENABLE_CACHE_AVATAR").MustBool()
|
||||
Service.EnableReverseProxyAuth = Cfg.Section("service").Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
|
||||
Service.EnableReverseProxyAutoRegister = Cfg.Section("service").Key("ENABLE_REVERSE_PROXY_AUTO_REGISTERATION").MustBool()
|
||||
}
|
||||
|
||||
var logLevels = map[string]string{
|
||||
@@ -309,17 +343,17 @@ func newLogService() {
|
||||
log.Info("%s %s", AppName, AppVer)
|
||||
|
||||
// Get and check log mode.
|
||||
LogModes = strings.Split(Cfg.MustValue("log", "MODE", "console"), ",")
|
||||
LogModes = strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")
|
||||
LogConfigs = make([]string, len(LogModes))
|
||||
for i, mode := range LogModes {
|
||||
mode = strings.TrimSpace(mode)
|
||||
modeSec := "log." + mode
|
||||
if _, err := Cfg.GetSection(modeSec); err != nil {
|
||||
sec, err := Cfg.GetSection("log." + mode)
|
||||
if err != nil {
|
||||
log.Fatal(4, "Unknown log mode: %s", mode)
|
||||
}
|
||||
|
||||
// Log level.
|
||||
levelName := Cfg.MustValueRange("log."+mode, "LEVEL", "Trace",
|
||||
levelName := Cfg.Section("log."+mode).Key("LEVEL").In("Trace",
|
||||
[]string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"})
|
||||
level, ok := logLevels[levelName]
|
||||
if !ok {
|
||||
@@ -331,42 +365,42 @@ func newLogService() {
|
||||
case "console":
|
||||
LogConfigs[i] = fmt.Sprintf(`{"level":%s}`, level)
|
||||
case "file":
|
||||
logPath := Cfg.MustValue(modeSec, "FILE_NAME", path.Join(LogRootPath, "gogs.log"))
|
||||
logPath := sec.Key("FILE_NAME").MustString(path.Join(LogRootPath, "gogs.log"))
|
||||
os.MkdirAll(path.Dir(logPath), os.ModePerm)
|
||||
LogConfigs[i] = fmt.Sprintf(
|
||||
`{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,
|
||||
logPath,
|
||||
Cfg.MustBool(modeSec, "LOG_ROTATE", true),
|
||||
Cfg.MustInt(modeSec, "MAX_LINES", 1000000),
|
||||
1<<uint(Cfg.MustInt(modeSec, "MAX_SIZE_SHIFT", 28)),
|
||||
Cfg.MustBool(modeSec, "DAILY_ROTATE", true),
|
||||
Cfg.MustInt(modeSec, "MAX_DAYS", 7))
|
||||
sec.Key("LOG_ROTATE").MustBool(true),
|
||||
sec.Key("MAX_LINES").MustInt(1000000),
|
||||
1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
|
||||
sec.Key("DAILY_ROTATE").MustBool(true),
|
||||
sec.Key("MAX_DAYS").MustInt(7))
|
||||
case "conn":
|
||||
LogConfigs[i] = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":"%s","addr":"%s"}`, level,
|
||||
Cfg.MustBool(modeSec, "RECONNECT_ON_MSG"),
|
||||
Cfg.MustBool(modeSec, "RECONNECT"),
|
||||
Cfg.MustValueRange(modeSec, "PROTOCOL", "tcp", []string{"tcp", "unix", "udp"}),
|
||||
Cfg.MustValue(modeSec, "ADDR", ":7020"))
|
||||
sec.Key("RECONNECT_ON_MSG").MustBool(),
|
||||
sec.Key("RECONNECT").MustBool(),
|
||||
sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"}),
|
||||
sec.Key("ADDR").MustString(":7020"))
|
||||
case "smtp":
|
||||
LogConfigs[i] = fmt.Sprintf(`{"level":%s,"username":"%s","password":"%s","host":"%s","sendTos":"%s","subject":"%s"}`, level,
|
||||
Cfg.MustValue(modeSec, "USER", "example@example.com"),
|
||||
Cfg.MustValue(modeSec, "PASSWD", "******"),
|
||||
Cfg.MustValue(modeSec, "HOST", "127.0.0.1:25"),
|
||||
Cfg.MustValue(modeSec, "RECEIVERS", "[]"),
|
||||
Cfg.MustValue(modeSec, "SUBJECT", "Diagnostic message from serve"))
|
||||
sec.Key("USER").MustString("example@example.com"),
|
||||
sec.Key("PASSWD").MustString("******"),
|
||||
sec.Key("HOST").MustString("127.0.0.1:25"),
|
||||
sec.Key("RECEIVERS").MustString("[]"),
|
||||
sec.Key("SUBJECT").MustString("Diagnostic message from serve"))
|
||||
case "database":
|
||||
LogConfigs[i] = fmt.Sprintf(`{"level":%s,"driver":"%s","conn":"%s"}`, level,
|
||||
Cfg.MustValue(modeSec, "DRIVER"),
|
||||
Cfg.MustValue(modeSec, "CONN"))
|
||||
sec.Key("DRIVER").String(),
|
||||
sec.Key("CONN").String())
|
||||
}
|
||||
|
||||
log.NewLogger(Cfg.MustInt64("log", "BUFFER_LEN", 10000), mode, LogConfigs[i])
|
||||
log.NewLogger(Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000), mode, LogConfigs[i])
|
||||
log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName)
|
||||
}
|
||||
}
|
||||
|
||||
func newCacheService() {
|
||||
CacheAdapter = Cfg.MustValueRange("cache", "ADAPTER", "memory", []string{"memory", "redis", "memcache"})
|
||||
CacheAdapter = Cfg.Section("cache").Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"})
|
||||
if EnableRedis {
|
||||
log.Info("Redis Enabled")
|
||||
}
|
||||
@@ -376,9 +410,9 @@ func newCacheService() {
|
||||
|
||||
switch CacheAdapter {
|
||||
case "memory":
|
||||
CacheInternal = Cfg.MustInt("cache", "INTERVAL", 60)
|
||||
CacheInternal = Cfg.Section("cache").Key("INTERVAL").MustInt(60)
|
||||
case "redis", "memcache":
|
||||
CacheConn = strings.Trim(Cfg.MustValue("cache", "HOST"), "\" ")
|
||||
CacheConn = strings.Trim(Cfg.Section("cache").Key("HOST").String(), "\" ")
|
||||
default:
|
||||
log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter)
|
||||
}
|
||||
@@ -387,21 +421,14 @@ func newCacheService() {
|
||||
}
|
||||
|
||||
func newSessionService() {
|
||||
SessionProvider = Cfg.MustValueRange("session", "PROVIDER", "memory",
|
||||
SessionConfig.Provider = Cfg.Section("session").Key("PROVIDER").In("memory",
|
||||
[]string{"memory", "file", "redis", "mysql"})
|
||||
|
||||
SessionConfig = new(session.Config)
|
||||
SessionConfig.ProviderConfig = strings.Trim(Cfg.MustValue("session", "PROVIDER_CONFIG"), "\" ")
|
||||
SessionConfig.CookieName = Cfg.MustValue("session", "COOKIE_NAME", "i_like_gogits")
|
||||
SessionConfig.ProviderConfig = strings.Trim(Cfg.Section("session").Key("PROVIDER_CONFIG").String(), "\" ")
|
||||
SessionConfig.CookieName = Cfg.Section("session").Key("COOKIE_NAME").MustString("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)
|
||||
|
||||
if SessionProvider == "file" {
|
||||
os.MkdirAll(path.Dir(SessionConfig.ProviderConfig), os.ModePerm)
|
||||
}
|
||||
SessionConfig.Secure = Cfg.Section("session").Key("COOKIE_SECURE").MustBool()
|
||||
SessionConfig.Gclifetime = Cfg.Section("session").Key("GC_INTERVAL_TIME").MustInt64(86400)
|
||||
SessionConfig.Maxlifetime = Cfg.Section("session").Key("SESSION_LIFE_TIME").MustInt64(86400)
|
||||
|
||||
log.Info("Session Service Enabled")
|
||||
}
|
||||
@@ -412,12 +439,12 @@ type Mailer struct {
|
||||
Host string
|
||||
From string
|
||||
User, Passwd string
|
||||
SkipVerify bool
|
||||
}
|
||||
|
||||
type OauthInfo struct {
|
||||
ClientId, ClientSecret string
|
||||
Scopes string
|
||||
AuthUrl, TokenUrl string
|
||||
oauth2.Options
|
||||
AuthUrl, TokenUrl string
|
||||
}
|
||||
|
||||
// Oauther represents oauth service.
|
||||
@@ -433,23 +460,25 @@ var (
|
||||
)
|
||||
|
||||
func newMailService() {
|
||||
sec := Cfg.Section("mailer")
|
||||
// Check mailer setting.
|
||||
if !Cfg.MustBool("mailer", "ENABLED") {
|
||||
if !sec.Key("ENABLED").MustBool() {
|
||||
return
|
||||
}
|
||||
|
||||
MailService = &Mailer{
|
||||
Name: Cfg.MustValue("mailer", "NAME", AppName),
|
||||
Host: Cfg.MustValue("mailer", "HOST"),
|
||||
User: Cfg.MustValue("mailer", "USER"),
|
||||
Passwd: Cfg.MustValue("mailer", "PASSWD"),
|
||||
Name: sec.Key("NAME").MustString(AppName),
|
||||
Host: sec.Key("HOST").String(),
|
||||
User: sec.Key("USER").String(),
|
||||
Passwd: sec.Key("PASSWD").String(),
|
||||
SkipVerify: sec.Key("SKIP_VERIFY").MustBool(),
|
||||
}
|
||||
MailService.From = Cfg.MustValue("mailer", "FROM", MailService.User)
|
||||
MailService.From = sec.Key("FROM").MustString(MailService.User)
|
||||
log.Info("Mail Service Enabled")
|
||||
}
|
||||
|
||||
func newRegisterMailService() {
|
||||
if !Cfg.MustBool("service", "REGISTER_EMAIL_CONFIRM") {
|
||||
if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
|
||||
return
|
||||
} else if MailService == nil {
|
||||
log.Warn("Register Mail Service: Mail Service is not enabled")
|
||||
@@ -460,7 +489,7 @@ func newRegisterMailService() {
|
||||
}
|
||||
|
||||
func newNotifyMailService() {
|
||||
if !Cfg.MustBool("service", "ENABLE_NOTIFY_MAIL") {
|
||||
if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
|
||||
return
|
||||
} else if MailService == nil {
|
||||
log.Warn("Notify Mail Service: Mail Service is not enabled")
|
||||
@@ -471,8 +500,8 @@ func newNotifyMailService() {
|
||||
}
|
||||
|
||||
func newWebhookService() {
|
||||
WebhookTaskInterval = Cfg.MustInt("webhook", "TASK_INTERVAL", 1)
|
||||
WebhookDeliverTimeout = Cfg.MustInt("webhook", "DELIVER_TIMEOUT", 5)
|
||||
WebhookTaskInterval = Cfg.Section("webhook").Key("TASK_INTERVAL").MustInt(1)
|
||||
WebhookDeliverTimeout = Cfg.Section("webhook").Key("DELIVER_TIMEOUT").MustInt(5)
|
||||
}
|
||||
|
||||
func NewServices() {
|
||||
@@ -484,5 +513,5 @@ func NewServices() {
|
||||
newRegisterMailService()
|
||||
newNotifyMailService()
|
||||
newWebhookService()
|
||||
// ssh.Listen("2022")
|
||||
// ssh.Listen("2222")
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ package social
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
oauth "github.com/gogits/oauth2"
|
||||
"github.com/macaron-contrib/oauth2"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
@@ -27,78 +27,81 @@ type BasicUserInfo struct {
|
||||
|
||||
type SocialConnector interface {
|
||||
Type() int
|
||||
SetRedirectUrl(string)
|
||||
UserInfo(*oauth.Token, *url.URL) (*BasicUserInfo, error)
|
||||
|
||||
AuthCodeURL(string) string
|
||||
Exchange(string) (*oauth.Token, error)
|
||||
UserInfo(*oauth2.Token, *url.URL) (*BasicUserInfo, error)
|
||||
}
|
||||
|
||||
var (
|
||||
SocialBaseUrl = "/user/login"
|
||||
SocialMap = make(map[string]SocialConnector)
|
||||
SocialMap = make(map[string]SocialConnector)
|
||||
)
|
||||
|
||||
func NewOauthService() {
|
||||
if !setting.Cfg.MustBool("oauth", "ENABLED") {
|
||||
if !setting.Cfg.Section("oauth").Key("ENABLED").MustBool() {
|
||||
return
|
||||
}
|
||||
|
||||
oauth2.AppSubUrl = setting.AppSubUrl
|
||||
|
||||
setting.OauthService = &setting.Oauther{}
|
||||
setting.OauthService.OauthInfos = make(map[string]*setting.OauthInfo)
|
||||
|
||||
socialConfigs := make(map[string]*oauth.Config)
|
||||
socialConfigs := make(map[string]*oauth2.Options)
|
||||
allOauthes := []string{"github", "google", "qq", "twitter", "weibo"}
|
||||
// Load all OAuth config data.
|
||||
for _, name := range allOauthes {
|
||||
setting.OauthService.OauthInfos[name] = &setting.OauthInfo{
|
||||
ClientId: setting.Cfg.MustValue("oauth."+name, "CLIENT_ID"),
|
||||
ClientSecret: setting.Cfg.MustValue("oauth."+name, "CLIENT_SECRET"),
|
||||
Scopes: setting.Cfg.MustValue("oauth."+name, "SCOPES"),
|
||||
AuthUrl: setting.Cfg.MustValue("oauth."+name, "AUTH_URL"),
|
||||
TokenUrl: setting.Cfg.MustValue("oauth."+name, "TOKEN_URL"),
|
||||
sec := setting.Cfg.Section("oauth." + name)
|
||||
if !sec.Key("ENABLED").MustBool() {
|
||||
continue
|
||||
}
|
||||
socialConfigs[name] = &oauth.Config{
|
||||
ClientId: setting.OauthService.OauthInfos[name].ClientId,
|
||||
setting.OauthService.OauthInfos[name] = &setting.OauthInfo{
|
||||
Options: oauth2.Options{
|
||||
ClientID: sec.Key("CLIENT_ID").String(),
|
||||
ClientSecret: sec.Key("CLIENT_SECRET").String(),
|
||||
Scopes: sec.Key("SCOPES").Strings(" "),
|
||||
PathLogin: "/user/login/oauth2/" + name,
|
||||
PathCallback: setting.AppSubUrl + "/user/login/" + name,
|
||||
RedirectURL: setting.AppUrl + "user/login/" + name,
|
||||
},
|
||||
AuthUrl: sec.Key("AUTH_URL").String(),
|
||||
TokenUrl: sec.Key("TOKEN_URL").String(),
|
||||
}
|
||||
socialConfigs[name] = &oauth2.Options{
|
||||
ClientID: setting.OauthService.OauthInfos[name].ClientID,
|
||||
ClientSecret: setting.OauthService.OauthInfos[name].ClientSecret,
|
||||
RedirectURL: strings.TrimSuffix(setting.AppUrl, "/") + SocialBaseUrl + name,
|
||||
Scope: setting.OauthService.OauthInfos[name].Scopes,
|
||||
AuthURL: setting.OauthService.OauthInfos[name].AuthUrl,
|
||||
TokenURL: setting.OauthService.OauthInfos[name].TokenUrl,
|
||||
Scopes: setting.OauthService.OauthInfos[name].Scopes,
|
||||
}
|
||||
}
|
||||
enabledOauths := make([]string, 0, 10)
|
||||
|
||||
// GitHub.
|
||||
if setting.Cfg.MustBool("oauth.github", "ENABLED") {
|
||||
if setting.Cfg.Section("oauth.github").Key("ENABLED").MustBool() {
|
||||
setting.OauthService.GitHub = true
|
||||
newGitHubOauth(socialConfigs["github"])
|
||||
enabledOauths = append(enabledOauths, "GitHub")
|
||||
}
|
||||
|
||||
// Google.
|
||||
if setting.Cfg.MustBool("oauth.google", "ENABLED") {
|
||||
if setting.Cfg.Section("oauth.google").Key("ENABLED").MustBool() {
|
||||
setting.OauthService.Google = true
|
||||
newGoogleOauth(socialConfigs["google"])
|
||||
enabledOauths = append(enabledOauths, "Google")
|
||||
}
|
||||
|
||||
// QQ.
|
||||
if setting.Cfg.MustBool("oauth.qq", "ENABLED") {
|
||||
if setting.Cfg.Section("oauth.qq").Key("ENABLED").MustBool() {
|
||||
setting.OauthService.Tencent = true
|
||||
newTencentOauth(socialConfigs["qq"])
|
||||
enabledOauths = append(enabledOauths, "QQ")
|
||||
}
|
||||
|
||||
// Twitter.
|
||||
if setting.Cfg.MustBool("oauth.twitter", "ENABLED") {
|
||||
setting.OauthService.Twitter = true
|
||||
newTwitterOauth(socialConfigs["twitter"])
|
||||
enabledOauths = append(enabledOauths, "Twitter")
|
||||
}
|
||||
// if setting.Cfg.Section("oauth.twitter").Key( "ENABLED").MustBool() {
|
||||
// setting.OauthService.Twitter = true
|
||||
// newTwitterOauth(socialConfigs["twitter"])
|
||||
// enabledOauths = append(enabledOauths, "Twitter")
|
||||
// }
|
||||
|
||||
// Weibo.
|
||||
if setting.Cfg.MustBool("oauth.weibo", "ENABLED") {
|
||||
if setting.Cfg.Section("oauth.weibo").Key("ENABLED").MustBool() {
|
||||
setting.OauthService.Weibo = true
|
||||
newWeiboOauth(socialConfigs["weibo"])
|
||||
enabledOauths = append(enabledOauths, "Weibo")
|
||||
@@ -115,38 +118,25 @@ func NewOauthService() {
|
||||
// \/ \/ \/
|
||||
|
||||
type SocialGithub struct {
|
||||
Token *oauth.Token
|
||||
*oauth.Transport
|
||||
opts *oauth2.Options
|
||||
}
|
||||
|
||||
func newGitHubOauth(opts *oauth2.Options) {
|
||||
SocialMap["github"] = &SocialGithub{opts}
|
||||
}
|
||||
|
||||
func (s *SocialGithub) Type() int {
|
||||
return int(models.GITHUB)
|
||||
}
|
||||
|
||||
func newGitHubOauth(config *oauth.Config) {
|
||||
SocialMap["github"] = &SocialGithub{
|
||||
Transport: &oauth.Transport{
|
||||
Config: config,
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SocialGithub) SetRedirectUrl(url string) {
|
||||
s.Transport.Config.RedirectURL = url
|
||||
}
|
||||
|
||||
func (s *SocialGithub) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
transport := &oauth.Transport{
|
||||
Token: token,
|
||||
}
|
||||
func (s *SocialGithub) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
transport := s.opts.NewTransportFromToken(token)
|
||||
var data struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"login"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
var err error
|
||||
r, err := transport.Client().Get(s.Transport.Scope)
|
||||
r, err := transport.Client().Get("https://api.github.com/user")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -169,38 +159,25 @@ func (s *SocialGithub) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo,
|
||||
// \/ /_____/ \/
|
||||
|
||||
type SocialGoogle struct {
|
||||
Token *oauth.Token
|
||||
*oauth.Transport
|
||||
opts *oauth2.Options
|
||||
}
|
||||
|
||||
func (s *SocialGoogle) Type() int {
|
||||
return int(models.GOOGLE)
|
||||
}
|
||||
|
||||
func newGoogleOauth(config *oauth.Config) {
|
||||
SocialMap["google"] = &SocialGoogle{
|
||||
Transport: &oauth.Transport{
|
||||
Config: config,
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
func newGoogleOauth(opts *oauth2.Options) {
|
||||
SocialMap["google"] = &SocialGoogle{opts}
|
||||
}
|
||||
|
||||
func (s *SocialGoogle) SetRedirectUrl(url string) {
|
||||
s.Transport.Config.RedirectURL = url
|
||||
}
|
||||
|
||||
func (s *SocialGoogle) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
transport := &oauth.Transport{Token: token}
|
||||
func (s *SocialGoogle) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
transport := s.opts.NewTransportFromToken(token)
|
||||
var data struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
var err error
|
||||
|
||||
reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
|
||||
r, err := transport.Client().Get(reqUrl)
|
||||
r, err := transport.Client().Get("https://www.googleapis.com/userinfo/v2/me")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -223,62 +200,35 @@ func (s *SocialGoogle) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo,
|
||||
// \__> \__>
|
||||
|
||||
type SocialTencent struct {
|
||||
Token *oauth.Token
|
||||
*oauth.Transport
|
||||
reqUrl string
|
||||
opts *oauth2.Options
|
||||
}
|
||||
|
||||
func newTencentOauth(opts *oauth2.Options) {
|
||||
SocialMap["qq"] = &SocialTencent{opts}
|
||||
}
|
||||
|
||||
func (s *SocialTencent) Type() int {
|
||||
return int(models.QQ)
|
||||
}
|
||||
|
||||
func newTencentOauth(config *oauth.Config) {
|
||||
SocialMap["qq"] = &SocialTencent{
|
||||
reqUrl: "https://open.t.qq.com/api/user/info",
|
||||
Transport: &oauth.Transport{
|
||||
Config: config,
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SocialTencent) SetRedirectUrl(url string) {
|
||||
s.Transport.Config.RedirectURL = url
|
||||
}
|
||||
|
||||
func (s *SocialTencent) UserInfo(token *oauth.Token, URL *url.URL) (*BasicUserInfo, error) {
|
||||
var data struct {
|
||||
Data struct {
|
||||
Id string `json:"openid"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
} `json:"data"`
|
||||
}
|
||||
var err error
|
||||
// https://open.t.qq.com/api/user/info?
|
||||
//oauth_consumer_key=APP_KEY&
|
||||
//access_token=ACCESSTOKEN&openid=openid
|
||||
//clientip=CLIENTIP&oauth_version=2.a
|
||||
//scope=all
|
||||
var urls = url.Values{
|
||||
"oauth_consumer_key": {s.Transport.Config.ClientId},
|
||||
"access_token": {token.AccessToken},
|
||||
"openid": URL.Query()["openid"],
|
||||
"oauth_version": {"2.a"},
|
||||
"scope": {"all"},
|
||||
}
|
||||
r, err := http.Get(s.reqUrl + "?" + urls.Encode())
|
||||
func (s *SocialTencent) UserInfo(token *oauth2.Token, URL *url.URL) (*BasicUserInfo, error) {
|
||||
r, err := http.Get("https://graph.z.qq.com/moc2/me?access_token=" + url.QueryEscape(token.AccessToken))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vals, err := url.ParseQuery(string(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &BasicUserInfo{
|
||||
Identity: data.Data.Id,
|
||||
Name: data.Data.Name,
|
||||
Email: data.Data.Email,
|
||||
Identity: vals.Get("openid"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -289,54 +239,54 @@ func (s *SocialTencent) UserInfo(token *oauth.Token, URL *url.URL) (*BasicUserIn
|
||||
// |____| \/\_/ |__||__| |__| \___ >__|
|
||||
// \/
|
||||
|
||||
type SocialTwitter struct {
|
||||
Token *oauth.Token
|
||||
*oauth.Transport
|
||||
}
|
||||
// type SocialTwitter struct {
|
||||
// Token *oauth2.Token
|
||||
// *oauth2.Transport
|
||||
// }
|
||||
|
||||
func (s *SocialTwitter) Type() int {
|
||||
return int(models.TWITTER)
|
||||
}
|
||||
// func (s *SocialTwitter) Type() int {
|
||||
// return int(models.TWITTER)
|
||||
// }
|
||||
|
||||
func newTwitterOauth(config *oauth.Config) {
|
||||
SocialMap["twitter"] = &SocialTwitter{
|
||||
Transport: &oauth.Transport{
|
||||
Config: config,
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
}
|
||||
// func newTwitterOauth(config *oauth2.Config) {
|
||||
// SocialMap["twitter"] = &SocialTwitter{
|
||||
// Transport: &oauth.Transport{
|
||||
// Config: config,
|
||||
// Transport: http.DefaultTransport,
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
||||
func (s *SocialTwitter) SetRedirectUrl(url string) {
|
||||
s.Transport.Config.RedirectURL = url
|
||||
}
|
||||
// func (s *SocialTwitter) SetRedirectUrl(url string) {
|
||||
// s.Transport.Config.RedirectURL = url
|
||||
// }
|
||||
|
||||
//https://github.com/mrjones/oauth
|
||||
func (s *SocialTwitter) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
// transport := &oauth.Transport{Token: token}
|
||||
// var data struct {
|
||||
// Id string `json:"id"`
|
||||
// Name string `json:"name"`
|
||||
// Email string `json:"email"`
|
||||
// }
|
||||
// var err error
|
||||
// //https://github.com/mrjones/oauth
|
||||
// func (s *SocialTwitter) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
// // transport := &oauth.Transport{Token: token}
|
||||
// // var data struct {
|
||||
// // Id string `json:"id"`
|
||||
// // Name string `json:"name"`
|
||||
// // Email string `json:"email"`
|
||||
// // }
|
||||
// // var err error
|
||||
|
||||
// reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
|
||||
// r, err := transport.Client().Get(reqUrl)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// defer r.Body.Close()
|
||||
// if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return &BasicUserInfo{
|
||||
// Identity: data.Id,
|
||||
// Name: data.Name,
|
||||
// Email: data.Email,
|
||||
// }, nil
|
||||
return nil, nil
|
||||
}
|
||||
// // reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo"
|
||||
// // r, err := transport.Client().Get(reqUrl)
|
||||
// // if err != nil {
|
||||
// // return nil, err
|
||||
// // }
|
||||
// // defer r.Body.Close()
|
||||
// // if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
// // return nil, err
|
||||
// // }
|
||||
// // return &BasicUserInfo{
|
||||
// // Identity: data.Id,
|
||||
// // Name: data.Name,
|
||||
// // Email: data.Email,
|
||||
// // }, nil
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
// __ __ ._____.
|
||||
// / \ / \ ____ |__\_ |__ ____
|
||||
@@ -346,37 +296,25 @@ func (s *SocialTwitter) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo
|
||||
// \/ \/ \/
|
||||
|
||||
type SocialWeibo struct {
|
||||
Token *oauth.Token
|
||||
*oauth.Transport
|
||||
opts *oauth2.Options
|
||||
}
|
||||
|
||||
func newWeiboOauth(opts *oauth2.Options) {
|
||||
SocialMap["weibo"] = &SocialWeibo{opts}
|
||||
}
|
||||
|
||||
func (s *SocialWeibo) Type() int {
|
||||
return int(models.WEIBO)
|
||||
}
|
||||
|
||||
func newWeiboOauth(config *oauth.Config) {
|
||||
SocialMap["weibo"] = &SocialWeibo{
|
||||
Transport: &oauth.Transport{
|
||||
Config: config,
|
||||
Transport: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SocialWeibo) SetRedirectUrl(url string) {
|
||||
s.Transport.Config.RedirectURL = url
|
||||
}
|
||||
|
||||
func (s *SocialWeibo) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
transport := &oauth.Transport{Token: token}
|
||||
func (s *SocialWeibo) UserInfo(token *oauth2.Token, _ *url.URL) (*BasicUserInfo, error) {
|
||||
transport := s.opts.NewTransportFromToken(token)
|
||||
var data struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
var err error
|
||||
|
||||
var urls = url.Values{
|
||||
"access_token": {token.AccessToken},
|
||||
"uid": {token.Extra["id_token"]},
|
||||
"uid": {token.Extra("uid")},
|
||||
}
|
||||
reqUrl := "https://api.weibo.com/2/users/show.json"
|
||||
r, err := transport.Client().Get(reqUrl + "?" + urls.Encode())
|
||||
@@ -384,11 +322,12 @@ func (s *SocialWeibo) UserInfo(token *oauth.Token, _ *url.URL) (*BasicUserInfo,
|
||||
return nil, err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &BasicUserInfo{
|
||||
Identity: token.Extra["id_token"],
|
||||
Identity: token.Extra("uid"),
|
||||
Name: data.Name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -13,9 +13,8 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/go.crypto/ssh"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
)
|
||||
@@ -35,8 +34,11 @@ func handleServerConn(keyId string, chans <-chan ssh.NewChannel) {
|
||||
go func(in <-chan *ssh.Request) {
|
||||
defer channel.Close()
|
||||
for req := range in {
|
||||
ok, payload := false, strings.TrimLeft(string(req.Payload), "\x00")
|
||||
ok, payload := false, strings.TrimLeft(string(req.Payload), "\x00&")
|
||||
fmt.Println("Request:", req.Type, req.WantReply, payload)
|
||||
if req.WantReply {
|
||||
fmt.Println(req.Reply(true, nil))
|
||||
}
|
||||
switch req.Type {
|
||||
case "env":
|
||||
args := strings.Split(strings.Replace(payload, "\x00", "", -1), "\v")
|
||||
@@ -54,7 +56,7 @@ func handleServerConn(keyId string, chans <-chan ssh.NewChannel) {
|
||||
case "exec":
|
||||
os.Setenv("SSH_ORIGINAL_COMMAND", strings.TrimLeft(payload, "'("))
|
||||
log.Info("Payload: %v", strings.TrimLeft(payload, "'("))
|
||||
cmd := exec.Command("/Users/jiahuachen/Applications/Go/src/github.com/gogits/gogs-ng/gogs-ng", "serv", "key-"+keyId)
|
||||
cmd := exec.Command("/Users/jiahuachen/Applications/Go/src/github.com/gogits/gogs/gogs", "serv", "key-"+keyId)
|
||||
cmd.Stdout = channel
|
||||
cmd.Stdin = channel
|
||||
cmd.Stderr = channel.Stderr()
|
||||
@@ -65,7 +67,6 @@ func handleServerConn(keyId string, chans <-chan ssh.NewChannel) {
|
||||
}
|
||||
}
|
||||
fmt.Println("Done:", ok)
|
||||
req.Reply(ok, nil) // BUG: Git on Mac seems not know this reply and hang?
|
||||
}
|
||||
fmt.Println("Done!!!")
|
||||
}(requests)
|
||||
@@ -101,7 +102,7 @@ func Listen(port string) {
|
||||
config := &ssh.ServerConfig{
|
||||
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
|
||||
// keyCache[string(ssh.MarshalAuthorizedKey(key))] = 2
|
||||
return &ssh.Permissions{Extensions: map[string]string{"key-id": "2"}}, nil
|
||||
return &ssh.Permissions{Extensions: map[string]string{"key-id": "1"}}, nil
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -2,18 +2,6 @@
|
||||
|
||||
set -e
|
||||
|
||||
# summary of how this script can be called:
|
||||
# * <postinst> `configure' <most-recently-configured-version>
|
||||
# * <old-postinst> `abort-upgrade' <new version>
|
||||
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
|
||||
# <new-version>
|
||||
# * <postinst> `abort-remove'
|
||||
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
|
||||
# <failed-install-package> <version> `removing'
|
||||
# <conflicting-package> <version>
|
||||
# for details, see http://www.debian.org/doc/debian-policy/ or
|
||||
# the debian-policy package
|
||||
|
||||
APP_NAME="gogs"
|
||||
CLI="${APP_NAME}"
|
||||
APP_USER=$(${CLI} config:get APP_USER)
|
||||
@@ -21,28 +9,27 @@ APP_GROUP=$(${CLI} config:get APP_GROUP)
|
||||
APP_CONFIG="/etc/${APP_NAME}/conf/app.ini"
|
||||
|
||||
case "$1" in
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
exit 0
|
||||
;;
|
||||
|
||||
configure)
|
||||
configure|*)
|
||||
mkdir -p $(dirname ${APP_CONFIG})
|
||||
chown ${APP_USER}.${APP_GROUP} $(dirname ${APP_CONFIG})
|
||||
[ -f ${APP_CONFIG} ] || ${CLI} run cp conf/app.ini ${APP_CONFIG}
|
||||
${CLI} config:set USER=${APP_USER}
|
||||
${CLI} config:set GOGS_CUSTOM="/etc/${APP_NAME}"
|
||||
PORT=$(${CLI} config:get PORT || echo "6000")
|
||||
sed -i "s|HTTP_PORT = 3000|HTTP_PORT = ${PORT}|" ${APP_CONFIG}
|
||||
sed -i "s|RUN_USER = git|RUN_USER = ${APP_USER}|" ${APP_CONFIG}
|
||||
sed -i "s|RUN_MODE = dev|RUN_MODE = prod|" ${APP_CONFIG}
|
||||
|
||||
# setup symlink towards custom conf
|
||||
mkdir -p /opt/${APP_NAME}/custom/conf
|
||||
chown -R ${APP_USER}.${APP_GROUP} /opt/${APP_NAME}/custom
|
||||
ln -f -s ${APP_CONFIG} /opt/${APP_NAME}/custom/conf/app.ini
|
||||
|
||||
# scale
|
||||
${CLI} scale web=1 || true
|
||||
;;
|
||||
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
exit 0
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "postinst called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -303,6 +303,9 @@ var Gogits = {};
|
||||
|
||||
// api working
|
||||
Gogits.getUsers = function (val, $target) {
|
||||
var notEmpty = function (str) {
|
||||
return str && str.length > 0;
|
||||
}
|
||||
$.ajax({
|
||||
url: '/api/v1/users/search?q=' + val,
|
||||
dataType: "json",
|
||||
@@ -310,7 +313,11 @@ var Gogits = {};
|
||||
if (json.ok && json.data.length) {
|
||||
var html = '';
|
||||
$.each(json.data, function (i, item) {
|
||||
html += '<li><img src="' + item.avatar + '">' + item.username + '</li>';
|
||||
html += '<li><img src="' + item.avatar + '">' + item.username;
|
||||
if (notEmpty(item.full_name)) {
|
||||
html += ' (' + item.full_name + ')';
|
||||
}
|
||||
html += '</li>';
|
||||
});
|
||||
$target.toggleShow();
|
||||
$target.find('ul').html(html);
|
||||
|
||||
@@ -340,6 +340,30 @@ img.avatar-100 {
|
||||
padding: 5px 15px;
|
||||
color: #777;
|
||||
}
|
||||
.markdown table {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
word-break: normal;
|
||||
margin: 15px 0;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
display: block;
|
||||
}
|
||||
.markdown table th {
|
||||
font-weight: 700;
|
||||
}
|
||||
.markdown table th,
|
||||
.markdown table td {
|
||||
border: 1px solid #DDD;
|
||||
padding: 6px 13px !important;
|
||||
}
|
||||
.markdown table tr {
|
||||
background-color: #FFF;
|
||||
border-top: 1px solid #CCC;
|
||||
}
|
||||
.markdown table tr:nth-child(2n) {
|
||||
background-color: #F8F8F8;
|
||||
}
|
||||
.markdown a {
|
||||
color: #428BCA;
|
||||
}
|
||||
@@ -376,29 +400,6 @@ img.avatar-100 {
|
||||
.markdown h4 {
|
||||
font-size: 18px;
|
||||
}
|
||||
.markdown table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
display: block;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
margin: 0 0 9px;
|
||||
}
|
||||
.markdown table th {
|
||||
font-weight: 700;
|
||||
}
|
||||
.markdown table th,
|
||||
.markdown table td {
|
||||
border: 1px solid #DDD;
|
||||
padding: 6px 13px;
|
||||
}
|
||||
.markdown table tr {
|
||||
background-color: #FFF;
|
||||
border-top: 1px solid #CCC;
|
||||
}
|
||||
.markdown table tr:nth-child(2n) {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
.markdown dl dt {
|
||||
font-style: italic;
|
||||
margin-top: 9px;
|
||||
@@ -1071,7 +1072,8 @@ The register and sign-in page style
|
||||
text-overflow: clip;
|
||||
}
|
||||
#repo-content {
|
||||
padding: 18px 0;
|
||||
padding-top: 18px;
|
||||
padding-bottom: 18px;
|
||||
}
|
||||
.repo-wide-wrapper {
|
||||
padding: 18px;
|
||||
@@ -1248,6 +1250,9 @@ The register and sign-in page style
|
||||
color: #428BCA;
|
||||
text-decoration: underline;
|
||||
}
|
||||
#repo-files-table td.message .text-truncate {
|
||||
max-width: 360px;
|
||||
}
|
||||
#repo-files-table tbody {
|
||||
background-color: #FFF;
|
||||
}
|
||||
@@ -1352,31 +1357,54 @@ The register and sign-in page style
|
||||
#repo-create-cancel {
|
||||
margin-left: 4em;
|
||||
}
|
||||
#release-new-target-branch-list,
|
||||
#repo-create-owner-list {
|
||||
top: 30px;
|
||||
left: 0;
|
||||
width: auto;
|
||||
max-width: 300px;
|
||||
}
|
||||
#release-new-target-branch-list .octicon,
|
||||
#repo-create-owner-list .octicon {
|
||||
margin-right: 12px;
|
||||
opacity: 0;
|
||||
}
|
||||
#release-new-target-branch-list .avatar,
|
||||
#repo-create-owner-list .avatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
#release-new-target-branch-list li,
|
||||
#repo-create-owner-list li {
|
||||
white-space: nowrap;
|
||||
}
|
||||
#release-new-target-branch-list li.checked .octicon,
|
||||
#repo-create-owner-list li.checked .octicon {
|
||||
opacity: 1;
|
||||
}
|
||||
#release-new-target-branch-list li a,
|
||||
#repo-create-owner-list li a {
|
||||
text-overflow: ellipsis;
|
||||
-o-text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
#release-new-target-branch-list {
|
||||
margin-top: -1px;
|
||||
min-width: 150px;
|
||||
}
|
||||
#release-new-title {
|
||||
margin-top: 10px;
|
||||
}
|
||||
#release-new-content {
|
||||
width: 100%;
|
||||
}
|
||||
#release-preview-btn .btn {
|
||||
border-left: 0;
|
||||
}
|
||||
#release-preview.markdown {
|
||||
margin-top: 5px;
|
||||
background-color: transparent;
|
||||
}
|
||||
.file-name {
|
||||
margin-left: 1em;
|
||||
}
|
||||
@@ -1416,7 +1444,7 @@ The register and sign-in page style
|
||||
}
|
||||
.code-view .lines-num span {
|
||||
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||
line-height: 1.6;
|
||||
line-height: 20px;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
@@ -1510,6 +1538,9 @@ The register and sign-in page style
|
||||
.commit-list .message span {
|
||||
max-width: 500px;
|
||||
}
|
||||
.commit-message {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.diff-head-box {
|
||||
margin-top: 10px;
|
||||
}
|
||||
@@ -1618,6 +1649,87 @@ The register and sign-in page style
|
||||
margin-left: 44px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
#release h4 {
|
||||
font-size: 18px;
|
||||
}
|
||||
#release h4 small {
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
color: #999;
|
||||
}
|
||||
#release #release-head {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #DDD;
|
||||
}
|
||||
#release #release-head .btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
#release .release-item > div {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
#release .release-item .label-green:hover {
|
||||
background-color: #65ad4e;
|
||||
}
|
||||
#release .release-item .release-meta {
|
||||
position: relative;
|
||||
float: left;
|
||||
padding-right: 15px;
|
||||
}
|
||||
#release .release-item .tag,
|
||||
#release .release-item .commit {
|
||||
display: block;
|
||||
margin-top: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
#release .release-item .release-detail {
|
||||
margin-top: -1px;
|
||||
border-left: 1px solid #DDD;
|
||||
position: relative;
|
||||
float: left;
|
||||
padding-left: 15px;
|
||||
}
|
||||
#release .release-item .title {
|
||||
line-height: 25px;
|
||||
margin-top: 0;
|
||||
}
|
||||
#release .release-item p.info {
|
||||
line-height: 20px;
|
||||
color: #666;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
#release .release-item p.info > * {
|
||||
margin-right: 10px;
|
||||
}
|
||||
#release .release-item .author img {
|
||||
margin-top: -3px;
|
||||
}
|
||||
#release .release-item div.desc {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
#release .release-item div.desc.markdown {
|
||||
background-color: transparent;
|
||||
}
|
||||
#release .release-item .download a {
|
||||
margin-right: 10px;
|
||||
}
|
||||
#release .release-item .dot {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
background-color: #ccc;
|
||||
z-index: 999;
|
||||
position: absolute;
|
||||
display: block;
|
||||
left: -6px;
|
||||
top: 27px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #FFF;
|
||||
}
|
||||
#release #release-new-form {
|
||||
padding-top: 15px;
|
||||
}
|
||||
#admin-wrapper,
|
||||
#setting-wrapper {
|
||||
padding-bottom: 100px;
|
||||
@@ -2019,10 +2131,14 @@ textarea#issue-add-content {
|
||||
#issue-list-nav li.right {
|
||||
margin-left: 4px;
|
||||
}
|
||||
#issue-new > a {
|
||||
#issue-new > a,
|
||||
#label-new > a,
|
||||
#milestone-new > a {
|
||||
padding: 0 !important;
|
||||
}
|
||||
#issue-new > a button {
|
||||
#issue-new > a button,
|
||||
#label-new > a button,
|
||||
#milestone-new > a button {
|
||||
height: 29px;
|
||||
}
|
||||
#issue-list-menu {
|
||||
@@ -2059,10 +2175,6 @@ textarea#issue-add-content {
|
||||
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;
|
||||
@@ -2070,10 +2182,6 @@ textarea#issue-add-content {
|
||||
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;
|
||||
@@ -2090,6 +2198,53 @@ textarea#issue-add-content {
|
||||
#pull-list .desc a:hover {
|
||||
color: #03a2ef;
|
||||
}
|
||||
#issue-list-filter .drop > a {
|
||||
width: 90px;
|
||||
padding: 0;
|
||||
margin-left: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
#issue-list-filter .drop-down {
|
||||
z-index: 999;
|
||||
width: 236px;
|
||||
left: -158px;
|
||||
top: 22px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
#issue-list-filter .drop-down h4 {
|
||||
line-height: 40px;
|
||||
border-bottom: 1px solid #CCC;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#issue-list-filter .drop-down li {
|
||||
line-height: 30px;
|
||||
border-bottom: 1px dashed #EEE;
|
||||
padding-left: 9px;
|
||||
}
|
||||
#issue-list-filter .drop-down li:hover {
|
||||
background-color: #fcffec;
|
||||
}
|
||||
#issue-list-filter .drop-down > ul > li > a {
|
||||
display: block;
|
||||
}
|
||||
#issue-list-filter .labels .color {
|
||||
margin-top: 8px;
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: red;
|
||||
margin-right: 9px;
|
||||
margin-left: 9px;
|
||||
}
|
||||
#issue-list-filter .labels .name {
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
color: #444;
|
||||
}
|
||||
#issue-list-filter .milestones a {
|
||||
color: #444;
|
||||
font-weight: bold;
|
||||
}
|
||||
#issue-list-pager {
|
||||
margin: 18px 0 24px 0;
|
||||
font-size: 14px;
|
||||
@@ -2098,15 +2253,15 @@ textarea#issue-add-content {
|
||||
#labels-num {
|
||||
margin-right: 1em;
|
||||
}
|
||||
#label-list .right {
|
||||
#label-list a.right {
|
||||
margin-left: 1em;
|
||||
color: #999;
|
||||
line-height: 30px;
|
||||
}
|
||||
#label-list .right i {
|
||||
#label-list a.right i {
|
||||
margin-right: 3px;
|
||||
}
|
||||
#label-list .right:hover {
|
||||
#label-list a.right:hover {
|
||||
color: #444444;
|
||||
}
|
||||
#label-list .num {
|
||||
@@ -2127,6 +2282,95 @@ textarea#issue-add-content {
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px dashed #AAA;
|
||||
}
|
||||
#label-add-form .ipt,
|
||||
#label-edit-form .ipt,
|
||||
#label-delete-form .ipt {
|
||||
font-size: 14px;
|
||||
}
|
||||
#label-add-form .ipt[name=name],
|
||||
#label-edit-form .ipt[name=name],
|
||||
#label-delete-form .ipt[name=name] {
|
||||
width: 300px;
|
||||
}
|
||||
#label-add-form .btn,
|
||||
#label-edit-form .btn,
|
||||
#label-delete-form .btn {
|
||||
height: 33px;
|
||||
font-size: 14px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
#label-add-form {
|
||||
padding: 18px 0;
|
||||
border-bottom: 1px solid #DDD;
|
||||
}
|
||||
#label-delete-form span {
|
||||
line-height: 33px;
|
||||
}
|
||||
.label-color-drop .ipt {
|
||||
width: 100px;
|
||||
}
|
||||
.label-color-drop .drop-down {
|
||||
width: 128px !important;
|
||||
top: 22px !important;
|
||||
left: 50px !important;
|
||||
padding: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
.label-color-drop .drop-down a.color {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
}
|
||||
.label-color-drop label {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: inline-block;
|
||||
margin: 0 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#milestone-list {
|
||||
padding-top: 6px;
|
||||
}
|
||||
#milestone-list .title-text {
|
||||
font-size: 16px;
|
||||
}
|
||||
#milestone-list .desc {
|
||||
color: #999;
|
||||
line-height: 30px;
|
||||
}
|
||||
#milestone-list .content {
|
||||
width: 400px;
|
||||
}
|
||||
#milestone-list .item {
|
||||
padding-bottom: 18px;
|
||||
margin-bottom: 18px;
|
||||
border-bottom: 1px dashed #AAA;
|
||||
position: relative;
|
||||
}
|
||||
#milestone-list .action {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
#milestone-list .status-bar > .bar {
|
||||
margin: -2px 8px 0 8px;
|
||||
width: 360px;
|
||||
background-color: #DDD;
|
||||
height: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#milestone-list .status-bar .opening {
|
||||
background-color: #65ad4e;
|
||||
width: 40%;
|
||||
height: 14px;
|
||||
vertical-align: top;
|
||||
}
|
||||
#milestone-list .action-bar {
|
||||
margin-top: 8px;
|
||||
}
|
||||
#milestone-list .action-bar a {
|
||||
margin-left: 12px;
|
||||
}
|
||||
.org-header-alert .alert {
|
||||
margin-top: 10px;
|
||||
}
|
||||
@@ -2414,5 +2658,5 @@ textarea#issue-add-content {
|
||||
border-bottom: 2px solid #D26911;
|
||||
}
|
||||
#profile-body {
|
||||
margin-left: 10px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
@@ -379,6 +379,7 @@ dt {
|
||||
}
|
||||
.btn-large {
|
||||
font-size: 14.4px;
|
||||
padding: .4em .9em;
|
||||
}
|
||||
.btn-green {
|
||||
background-color: #65ad4e;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// @codekit-prepend "lib/lib.js"
|
||||
// @codekit-prepend "utils/tabs.js"
|
||||
// @codekit-prepend "utils/preview.js"
|
||||
// @codekit-prepend "gogs/issue_label.js"
|
||||
// @codekit-prepend "lib/jquery.tipsy.js"
|
||||
|
||||
var Gogs = {};
|
||||
@@ -203,6 +204,9 @@ var Gogs = {};
|
||||
|
||||
// Search users by keyword.
|
||||
Gogs.searchUsers = function (val, $target) {
|
||||
var notEmpty = function (str) {
|
||||
return str && str.length > 0;
|
||||
}
|
||||
$.ajax({
|
||||
url: Gogs.AppSubUrl + '/api/v1/users/search?q=' + val,
|
||||
dataType: "json",
|
||||
@@ -210,7 +214,11 @@ var Gogs = {};
|
||||
if (json.ok && json.data.length) {
|
||||
var html = '';
|
||||
$.each(json.data, function (i, item) {
|
||||
html += '<li><a><img src="' + item.avatar_url + '">' + item.username + '</a></li>';
|
||||
html += '<li><a><img src="' + item.avatar_url + '"><span class="username">' + item.username + '</span>';
|
||||
if (notEmpty(item.full_name)) {
|
||||
html += ' (' + item.full_name + ')';
|
||||
}
|
||||
html += '</a></li>';
|
||||
});
|
||||
$target.html(html);
|
||||
$target.toggleShow();
|
||||
@@ -395,6 +403,16 @@ function initRepo() {
|
||||
$clone_btn.tipsy({
|
||||
fade: true
|
||||
});
|
||||
|
||||
// Markdown preview.
|
||||
$('.markdown-preview').click(function() {
|
||||
var $this = $(this);
|
||||
$this.toggleAjax(function (resp) {
|
||||
$($this.data("preview")).html(resp);
|
||||
}, function () {
|
||||
$($this.data("preview")).html("no content");
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// when user changes hook type, hide/show proper divs
|
||||
@@ -415,6 +433,18 @@ function initHookTypeChange() {
|
||||
});
|
||||
}
|
||||
|
||||
function initRepoRelease() {
|
||||
$('#release-new-target-branch-list li').click(function() {
|
||||
if (!$(this).hasClass('checked')) {
|
||||
$('#repo-branch-current').text($(this).text());
|
||||
$('#tag-target').val($(this).text());
|
||||
|
||||
$(this).parent().find('.checked').removeClass('checked');
|
||||
$(this).addClass('checked');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function initRepoSetting() {
|
||||
// Options.
|
||||
// Confirmation of changing repository name.
|
||||
@@ -541,7 +571,7 @@ function initInvite() {
|
||||
$ul.toggleShow();
|
||||
}
|
||||
}).next().next().find('ul').on("click", 'li', function () {
|
||||
$('#org-member-invite').val($(this).text());
|
||||
$('#org-member-invite').val($(this).find('.username').text());
|
||||
$ul.toggleHide();
|
||||
});
|
||||
}
|
||||
@@ -745,8 +775,12 @@ $(document).ready(function () {
|
||||
initRepoCreate();
|
||||
}
|
||||
if ($('#repo-header').length) {
|
||||
initTimeSwitch();
|
||||
initRepo();
|
||||
}
|
||||
if ($('#release').length) {
|
||||
initRepoRelease();
|
||||
}
|
||||
if ($('#repo-setting').length) {
|
||||
initRepoSetting();
|
||||
}
|
||||
@@ -811,3 +845,7 @@ function homepage() {
|
||||
$('#promo-form').attr('action', Gogs.AppSubUrl + '/user/sign_up');
|
||||
});
|
||||
}
|
||||
|
||||
String.prototype.endsWith = function (suffix) {
|
||||
return this.indexOf(suffix, this.length - suffix.length) !== -1;
|
||||
};
|
||||
|
||||
97
public/ng/js/gogs/issue_label.js
Normal file
97
public/ng/js/gogs/issue_label.js
Normal file
@@ -0,0 +1,97 @@
|
||||
// when dom ready, init issue label events
|
||||
$(document).ready(function(){
|
||||
var labelColors = ["#e11d21","#EB6420","#FBCA04","#009800",
|
||||
"#006B75","#207DE5","#0052cc","#53E917",
|
||||
"#F6C6C7","#FAD8C7","#FEF2C0","#BFE5BF",
|
||||
"#BFDADC","#C7DEF8","#BFD4F2","#D4C5F9"];
|
||||
|
||||
var colorDropHtml = "";
|
||||
labelColors.forEach(function(item){
|
||||
colorDropHtml += '<a class="color" style="background-color:'+item+'" data-color-hex="'+item+'"></a>';
|
||||
});
|
||||
|
||||
|
||||
|
||||
// render label color input
|
||||
var color_input = $('#label-add-color');
|
||||
var color_label = $('#label-add-form .label-color-drop label');
|
||||
color_label.css("background-color",labelColors[0]);
|
||||
color_input.val(labelColors[0]);
|
||||
|
||||
|
||||
// render label color drop
|
||||
function render_color_drop($e){
|
||||
$e.find('.label-color-drop .drop-down')
|
||||
.html(colorDropHtml)
|
||||
.on("click","a",function(){
|
||||
var $form = $(this).parents(".form");
|
||||
var color_label = $form.find(".label-color-drop label");
|
||||
var color_input = $form.find("input[name=color]");
|
||||
var color = $(this).data("color-hex");
|
||||
color_label.css("background-color",color);
|
||||
color_input.val(color);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// color drop visible
|
||||
var form = $('#label-add-form');
|
||||
render_color_drop(form);
|
||||
$('#label-new-btn').on("click",function(){
|
||||
if(form.hasClass("hidden")){
|
||||
form.removeClass("hidden");
|
||||
}
|
||||
});
|
||||
$('#label-cancel-btn').on("click",function(){
|
||||
form.addClass("hidden");
|
||||
});
|
||||
|
||||
// label edit form render
|
||||
var $edit_form_tpl = $("#label-edit-form-tpl");
|
||||
$("#label-list").on("click","a.edit",function(){
|
||||
var $label_item = $(this).parents(".item");
|
||||
var $clone_form = $edit_form_tpl.clone();
|
||||
render_color_drop($clone_form);
|
||||
|
||||
// add default color
|
||||
var color_label = $clone_form.find(".label-color-drop label");
|
||||
var color_input = $clone_form.find("input[name=color]");
|
||||
var color = $label_item.find(".label").data("color-hex");
|
||||
color_label.css("background-color",color);
|
||||
color_input.val(color);
|
||||
|
||||
// add label name
|
||||
$clone_form.find("input[name=name]").val($label_item.find(".label").text());
|
||||
|
||||
// add label id
|
||||
$clone_form.find("input[name=id]").val($label_item.attr("id").replace("label-",""));
|
||||
|
||||
// append form
|
||||
$label_item.after($clone_form.show());
|
||||
|
||||
// add cancel button event
|
||||
$('#label-edit-cancel-btn').on("click",function(){
|
||||
$clone_form.remove();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// label delete form render
|
||||
var $del_form_tpl = $('#label-delete-form-tpl');
|
||||
$("#label-list").on("click","a.delete",function(){
|
||||
var $label_item = $(this).parents(".item");
|
||||
var $clone_form = $del_form_tpl.clone();
|
||||
|
||||
// add label id
|
||||
$clone_form.find("input[name=id]").val($label_item.attr("id").replace("label-",""));
|
||||
|
||||
// append form
|
||||
$label_item.after($clone_form.show());
|
||||
|
||||
// add cancel button event
|
||||
$('#label-del-cancel-btn').on("click",function(){
|
||||
$clone_form.remove();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
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
@@ -243,7 +243,9 @@ textarea#issue-add-content {
|
||||
}
|
||||
}
|
||||
// new issue button
|
||||
#issue-new {
|
||||
#issue-new,
|
||||
#label-new,
|
||||
#milestone-new{
|
||||
> a {
|
||||
padding: 0 !important;
|
||||
button {
|
||||
@@ -294,9 +296,6 @@ textarea#issue-add-content {
|
||||
font-size: 15px;
|
||||
margin: 0 6px;
|
||||
}
|
||||
.index-num {
|
||||
padding: .25em .6em;
|
||||
}
|
||||
}
|
||||
.comment {
|
||||
color: #666;
|
||||
@@ -305,7 +304,6 @@ textarea#issue-add-content {
|
||||
right: 0;
|
||||
}
|
||||
.issue-label {
|
||||
padding: .25em .6em;
|
||||
a {
|
||||
color: #FFF;
|
||||
}
|
||||
@@ -320,6 +318,60 @@ textarea#issue-add-content {
|
||||
}
|
||||
}
|
||||
}
|
||||
// issue list filter
|
||||
#issue-list-filter{
|
||||
.drop > a{
|
||||
width: 90px;
|
||||
padding: 0;
|
||||
margin-left: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.drop-down{
|
||||
z-index: 999;
|
||||
width: 236px;
|
||||
left:-158px; // 260 - 90 - 12
|
||||
top:22px;
|
||||
padding: 0 12px;
|
||||
h4{
|
||||
line-height: 40px;
|
||||
border-bottom: 1px solid #CCC;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
li{
|
||||
line-height: 30px;
|
||||
border-bottom: 1px dashed #EEE;
|
||||
padding-left: 9px;
|
||||
&:hover{
|
||||
background-color: #fcffec;
|
||||
}
|
||||
}
|
||||
> ul > li > a{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.labels{
|
||||
.color{
|
||||
margin-top: 8px;
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: red;
|
||||
margin-right: 9px;
|
||||
margin-left: 9px;
|
||||
}
|
||||
.name{
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
color: #444;
|
||||
}
|
||||
}
|
||||
.milestones{
|
||||
a{
|
||||
color: #444;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
// issue list pager
|
||||
#issue-list-pager {
|
||||
margin: 18px 0 24px 0;
|
||||
@@ -332,7 +384,7 @@ textarea#issue-add-content {
|
||||
}
|
||||
// labels list
|
||||
#label-list {
|
||||
.right {
|
||||
a.right {
|
||||
margin-left: 1em;
|
||||
color: #999;
|
||||
i {
|
||||
@@ -363,4 +415,100 @@ textarea#issue-add-content {
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px dashed #AAA;
|
||||
}
|
||||
}
|
||||
// label add form, label edit form
|
||||
#label-add-form,
|
||||
#label-edit-form,
|
||||
#label-delete-form{
|
||||
.ipt{
|
||||
font-size: 14px;
|
||||
}
|
||||
.ipt[name=name]{
|
||||
width: 300px;
|
||||
}
|
||||
.btn{
|
||||
height: 33px;
|
||||
font-size: 14px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
#label-add-form{
|
||||
padding: 18px 0;
|
||||
border-bottom: 1px solid #DDD;
|
||||
}
|
||||
#label-delete-form{
|
||||
span{
|
||||
line-height: 33px;
|
||||
}
|
||||
}
|
||||
// label color drop
|
||||
.label-color-drop{
|
||||
.ipt{
|
||||
width:100px;
|
||||
}
|
||||
.drop-down{
|
||||
width:128px !important;
|
||||
top:22px !important;
|
||||
left:50px !important;
|
||||
padding: 12px;
|
||||
line-height: 16px;
|
||||
a.color{
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
label{
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: inline-block;
|
||||
margin: 0 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
// milestone items
|
||||
#milestone-list{
|
||||
padding-top: 6px;
|
||||
.title-text{
|
||||
font-size: 16px;
|
||||
}
|
||||
.desc {
|
||||
color: #999;
|
||||
line-height: 30px;
|
||||
}
|
||||
.content{
|
||||
width: 400px;
|
||||
}
|
||||
.item {
|
||||
padding-bottom: 18px;
|
||||
margin-bottom: 18px;
|
||||
border-bottom: 1px dashed #AAA;
|
||||
position: relative;
|
||||
}
|
||||
.action{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
.status-bar {
|
||||
> .bar {
|
||||
margin: -2px 8px 0 8px;
|
||||
width: 360px;
|
||||
background-color: #DDD;
|
||||
height: 14px;
|
||||
vertical-align:middle;
|
||||
}
|
||||
.opening{
|
||||
background-color: #65ad4e;
|
||||
width: 40%;
|
||||
height: 14px;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
.action-bar{
|
||||
margin-top: 8px;
|
||||
a{
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,30 @@
|
||||
color: #777;
|
||||
}
|
||||
}
|
||||
table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
word-break: normal;
|
||||
margin: 15px 0;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
display: block;
|
||||
th {
|
||||
font-weight: 700;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #DDD;
|
||||
padding: 6px 13px !important;
|
||||
}
|
||||
tr {
|
||||
background-color: #FFF;
|
||||
border-top: 1px solid #CCC;
|
||||
&:nth-child(2n) {
|
||||
background-color: #F8F8F8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.markdown a {
|
||||
color: #428BCA;
|
||||
@@ -96,29 +120,6 @@
|
||||
.markdown h4 {
|
||||
font-size: 18px
|
||||
}
|
||||
.markdown table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
display: block;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
margin: 0 0 9px;
|
||||
}
|
||||
.markdown table th {
|
||||
font-weight: 700
|
||||
}
|
||||
.markdown table th,
|
||||
.markdown table td {
|
||||
border: 1px solid #DDD;
|
||||
padding: 6px 13px;
|
||||
}
|
||||
.markdown table tr {
|
||||
background-color: #FFF;
|
||||
border-top: 1px solid #CCC;
|
||||
}
|
||||
.markdown table tr:nth-child(2n) {
|
||||
background-color: #F8F8F8
|
||||
}
|
||||
.markdown dl dt {
|
||||
font-style: italic;
|
||||
margin-top: 9px;
|
||||
|
||||
@@ -55,5 +55,5 @@
|
||||
}
|
||||
}
|
||||
#profile-body {
|
||||
margin-left: 10px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
@@ -90,7 +90,8 @@
|
||||
}
|
||||
}
|
||||
#repo-content {
|
||||
padding: 18px 0;
|
||||
padding-top: 18px;
|
||||
padding-bottom: 18px;
|
||||
}
|
||||
.repo-wide-wrapper {
|
||||
padding: 18px;
|
||||
@@ -280,6 +281,11 @@
|
||||
color: #428BCA;
|
||||
text-decoration: underline;
|
||||
}
|
||||
td.message {
|
||||
.text-truncate {
|
||||
max-width: 360px;
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
background-color: #FFF;
|
||||
tr:hover {
|
||||
@@ -383,6 +389,7 @@
|
||||
#repo-create-cancel {
|
||||
margin-left: 4em;
|
||||
}
|
||||
#release-new-target-branch-list,
|
||||
#repo-create-owner-list {
|
||||
top: 30px;
|
||||
left: 0;
|
||||
@@ -410,6 +417,23 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
#release-new-target-branch-list {
|
||||
margin-top: -1px;
|
||||
min-width: 150px;
|
||||
}
|
||||
#release-new-title {
|
||||
margin-top: 10px;
|
||||
}
|
||||
#release-new-content {
|
||||
width: 100%;
|
||||
}
|
||||
#release-preview-btn .btn {
|
||||
border-left: 0;
|
||||
}
|
||||
#release-preview.markdown {
|
||||
margin-top: 5px;
|
||||
background-color: transparent;
|
||||
}
|
||||
.file-name {
|
||||
margin-left: 1em;
|
||||
}
|
||||
@@ -447,7 +471,7 @@
|
||||
width: 1%;
|
||||
span {
|
||||
font-family: Monaco,Menlo,Consolas,"Courier New",monospace;
|
||||
line-height: 1.6;
|
||||
line-height: 20px;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
@@ -535,6 +559,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.commit-message {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.diff-head-box {
|
||||
margin-top: 10px;
|
||||
.panel-body {
|
||||
@@ -661,3 +690,90 @@
|
||||
margin-left: 44px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
#release {
|
||||
h4 {
|
||||
font-size: 18px;
|
||||
small {
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
#release-head {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #DDD;
|
||||
.btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
.release-item {
|
||||
&>div {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.label-green:hover {
|
||||
background-color: @labelGreenColor;
|
||||
}
|
||||
.release-meta {
|
||||
position: relative;
|
||||
float: left;
|
||||
padding-right: 15px;
|
||||
}
|
||||
.tag,
|
||||
.commit {
|
||||
display: block;
|
||||
margin-top: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.release-detail {
|
||||
margin-top: -1px;
|
||||
border-left: 1px solid #DDD;
|
||||
position: relative;
|
||||
float: left;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.title {
|
||||
line-height: 25px;
|
||||
margin-top: 0;
|
||||
}
|
||||
p.info {
|
||||
line-height: 20px;
|
||||
color: #666;
|
||||
margin-bottom: 15px;
|
||||
>* {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.author {
|
||||
img {
|
||||
margin-top: -3px;
|
||||
}
|
||||
}
|
||||
div.desc {
|
||||
margin-bottom: 25px;
|
||||
&.markdown {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
.download a {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.dot {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
background-color: #ccc;
|
||||
z-index: 999;
|
||||
position: absolute;
|
||||
display: block;
|
||||
left: -6px;
|
||||
top: 27px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #FFF;
|
||||
}
|
||||
}
|
||||
#release-new-form {
|
||||
padding-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
}
|
||||
.btn-large {
|
||||
font-size: 1.2*@baseFontSize;
|
||||
padding: .4em .9em;
|
||||
}
|
||||
|
||||
.btn-green {
|
||||
|
||||
@@ -117,6 +117,7 @@ const (
|
||||
CLEAN_UNBIND_OAUTH AdminOperation = iota + 1
|
||||
CLEAN_INACTIVATE_USER
|
||||
CLEAN_REPO_ARCHIVES
|
||||
GIT_GC_REPOS
|
||||
)
|
||||
|
||||
func Dashboard(ctx *middleware.Context) {
|
||||
@@ -140,6 +141,9 @@ func Dashboard(ctx *middleware.Context) {
|
||||
case CLEAN_REPO_ARCHIVES:
|
||||
success = ctx.Tr("admin.dashboard.delete_repo_archives_success")
|
||||
err = models.DeleteRepositoryArchives()
|
||||
case GIT_GC_REPOS:
|
||||
success = ctx.Tr("admin.dashboard.git_gc_repos_success")
|
||||
err = models.GitGcRepos()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -198,7 +202,6 @@ func Config(ctx *middleware.Context) {
|
||||
ctx.Data["CacheInternal"] = setting.CacheInternal
|
||||
ctx.Data["CacheConn"] = setting.CacheConn
|
||||
|
||||
ctx.Data["SessionProvider"] = setting.SessionProvider
|
||||
ctx.Data["SessionConfig"] = setting.SessionConfig
|
||||
|
||||
ctx.Data["PictureService"] = setting.PictureService
|
||||
|
||||
@@ -192,13 +192,19 @@ func EditUserPost(ctx *middleware.Context, form auth.AdminEditUserForm) {
|
||||
u.IsActive = form.Active
|
||||
u.IsAdmin = form.Admin
|
||||
u.AllowGitHook = form.AllowGitHook
|
||||
|
||||
ctx.Data["User"] = u
|
||||
|
||||
if err := models.UpdateUser(u); err != nil {
|
||||
ctx.Handle(500, "UpdateUser", err)
|
||||
if err == models.ErrEmailAlreadyUsed {
|
||||
ctx.Data["Err_Email"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), USER_EDIT, &form)
|
||||
} else {
|
||||
ctx.Handle(500, "UpdateUser", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
log.Trace("Account profile updated by admin(%s): %s", ctx.User.Name, u.Name)
|
||||
|
||||
ctx.Data["User"] = u
|
||||
ctx.Flash.Success(ctx.Tr("admin.users.update_profile_success"))
|
||||
ctx.Redirect(setting.AppSubUrl + "/admin/users/" + ctx.Params(":userid"))
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@ func Markdown(ctx *middleware.Context, form apiv1.MarkdownForm) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(form.Text) == 0 {
|
||||
ctx.Write([]byte(""))
|
||||
return
|
||||
}
|
||||
|
||||
switch form.Mode {
|
||||
case "gfm":
|
||||
ctx.Write(base.RenderMarkdown([]byte(form.Text),
|
||||
|
||||
@@ -21,6 +21,25 @@ import (
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
// ToApiRepository converts repository to API format.
|
||||
func ToApiRepository(owner *models.User, repo *models.Repository, permission api.Permission) *api.Repository {
|
||||
cl, err := repo.CloneLink()
|
||||
if err != nil {
|
||||
log.Error(4, "CloneLink: %v", err)
|
||||
}
|
||||
return &api.Repository{
|
||||
Id: repo.Id,
|
||||
Owner: *ToApiUser(owner),
|
||||
FullName: owner.Name + "/" + repo.Name,
|
||||
Private: repo.IsPrivate,
|
||||
Fork: repo.IsFork,
|
||||
HtmlUrl: setting.AppUrl + owner.Name + "/" + repo.Name,
|
||||
CloneUrl: cl.HTTPS,
|
||||
SshUrl: cl.SSH,
|
||||
Permissions: permission,
|
||||
}
|
||||
}
|
||||
|
||||
func SearchRepos(ctx *middleware.Context) {
|
||||
opt := models.SearchOption{
|
||||
Keyword: path.Base(ctx.Query("q")),
|
||||
@@ -44,7 +63,7 @@ func SearchRepos(ctx *middleware.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
if u.IsOrganization() && u.IsOrgOwner(ctx.User.Id) {
|
||||
if u.IsOrganization() && u.IsOwnedBy(ctx.User.Id) {
|
||||
opt.Private = true
|
||||
}
|
||||
// FIXME: how about collaborators?
|
||||
@@ -75,13 +94,66 @@ func SearchRepos(ctx *middleware.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Render.JSON(200, map[string]interface{}{
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"ok": true,
|
||||
"data": results,
|
||||
})
|
||||
}
|
||||
|
||||
func Migrate(ctx *middleware.Context, form auth.MigrateRepoForm) {
|
||||
func createRepo(ctx *middleware.Context, owner *models.User, opt api.CreateRepoOption) {
|
||||
repo, err := models.CreateRepository(owner, opt.Name, opt.Description,
|
||||
opt.Gitignore, opt.License, opt.Private, false, opt.AutoInit)
|
||||
if err != nil {
|
||||
if err == models.ErrRepoAlreadyExist ||
|
||||
err == models.ErrRepoNameIllegal {
|
||||
ctx.JSON(422, &base.ApiJsonErr{err.Error(), base.DOC_URL})
|
||||
} else {
|
||||
log.Error(4, "CreateRepository: %v", err)
|
||||
if repo != nil {
|
||||
if err = models.DeleteRepository(ctx.User.Id, repo.Id, ctx.User.Name); err != nil {
|
||||
log.Error(4, "DeleteRepository: %v", err)
|
||||
}
|
||||
}
|
||||
ctx.Error(500)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(200, ToApiRepository(owner, repo, api.Permission{true, true, true}))
|
||||
}
|
||||
|
||||
// POST /user/repos
|
||||
// https://developer.github.com/v3/repos/#create
|
||||
func CreateRepo(ctx *middleware.Context, opt api.CreateRepoOption) {
|
||||
// Shouldn't reach this condition, but just in case.
|
||||
if ctx.User.IsOrganization() {
|
||||
ctx.JSON(422, "not allowed creating repository for organization")
|
||||
return
|
||||
}
|
||||
createRepo(ctx, ctx.User, opt)
|
||||
}
|
||||
|
||||
// POST /orgs/:org/repos
|
||||
// https://developer.github.com/v3/repos/#create
|
||||
func CreateOrgRepo(ctx *middleware.Context, opt api.CreateRepoOption) {
|
||||
org, err := models.GetOrgByName(ctx.Params(":org"))
|
||||
if err != nil {
|
||||
if err == models.ErrUserNotExist {
|
||||
ctx.Error(404)
|
||||
} else {
|
||||
ctx.Error(500)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !org.IsOwnedBy(ctx.User.Id) {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
createRepo(ctx, org, opt)
|
||||
}
|
||||
|
||||
func MigrateRepo(ctx *middleware.Context, form auth.MigrateRepoForm) {
|
||||
u, err := models.GetUserByName(ctx.Query("username"))
|
||||
if err != nil {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
@@ -103,17 +175,15 @@ func Migrate(ctx *middleware.Context, form auth.MigrateRepoForm) {
|
||||
if form.Uid != u.Id {
|
||||
org, err := models.GetUserById(form.Uid)
|
||||
if err != nil {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
log.Error(4, "GetUserById: %v", err)
|
||||
ctx.Error(500)
|
||||
return
|
||||
}
|
||||
ctxUser = org
|
||||
}
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
ctx.JSON(422, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": ctx.GetErrMsg(),
|
||||
})
|
||||
@@ -122,7 +192,7 @@ func Migrate(ctx *middleware.Context, form auth.MigrateRepoForm) {
|
||||
|
||||
if ctxUser.IsOrganization() {
|
||||
// Check ownership of organization.
|
||||
if !ctxUser.IsOrgOwner(u.Id) {
|
||||
if !ctxUser.IsOwnedBy(u.Id) {
|
||||
ctx.JSON(403, map[string]interface{}{
|
||||
"ok": false,
|
||||
"error": "given user is not owner of organization",
|
||||
@@ -173,29 +243,9 @@ func ListMyRepos(ctx *middleware.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
sshUrlFmt := "%s@%s:%s/%s.git"
|
||||
if setting.SshPort != 22 {
|
||||
sshUrlFmt = "ssh://%s@%s:%d/%s/%s.git"
|
||||
}
|
||||
|
||||
repos := make([]*api.Repository, numOwnRepos+len(collaRepos))
|
||||
// FIXME: make only one loop
|
||||
for i := range ownRepos {
|
||||
repos[i] = &api.Repository{
|
||||
Id: ownRepos[i].Id,
|
||||
Owner: api.User{
|
||||
Id: ctx.User.Id,
|
||||
UserName: ctx.User.Name,
|
||||
AvatarUrl: string(setting.Protocol) + ctx.User.AvatarLink(),
|
||||
},
|
||||
FullName: ctx.User.Name + "/" + ownRepos[i].Name,
|
||||
Private: ownRepos[i].IsPrivate,
|
||||
Fork: ownRepos[i].IsFork,
|
||||
HtmlUrl: setting.AppUrl + ctx.User.Name + "/" + ownRepos[i].Name,
|
||||
SshUrl: fmt.Sprintf(sshUrlFmt, setting.RunUser, setting.Domain, ctx.User.LowerName, ownRepos[i].LowerName),
|
||||
Permissions: api.Permission{true, true, true},
|
||||
}
|
||||
repos[i].CloneUrl = repos[i].HtmlUrl + ".git"
|
||||
repos[i] = ToApiRepository(ctx.User, ownRepos[i], api.Permission{true, true, true})
|
||||
}
|
||||
for i := range collaRepos {
|
||||
if err = collaRepos[i].GetOwner(); err != nil {
|
||||
@@ -203,24 +253,10 @@ func ListMyRepos(ctx *middleware.Context) {
|
||||
return
|
||||
}
|
||||
j := i + numOwnRepos
|
||||
repos[j] = &api.Repository{
|
||||
Id: collaRepos[i].Id,
|
||||
Owner: api.User{
|
||||
Id: collaRepos[i].Owner.Id,
|
||||
UserName: collaRepos[i].Owner.Name,
|
||||
AvatarUrl: string(setting.Protocol) + collaRepos[i].Owner.AvatarLink(),
|
||||
},
|
||||
FullName: collaRepos[i].Owner.Name + "/" + collaRepos[i].Name,
|
||||
Private: collaRepos[i].IsPrivate,
|
||||
Fork: collaRepos[i].IsFork,
|
||||
HtmlUrl: setting.AppUrl + collaRepos[i].Owner.Name + "/" + collaRepos[i].Name,
|
||||
SshUrl: fmt.Sprintf(sshUrlFmt, setting.RunUser, setting.Domain, collaRepos[i].Owner.LowerName, collaRepos[i].LowerName),
|
||||
Permissions: api.Permission{false, collaRepos[i].CanPush, true},
|
||||
}
|
||||
repos[j].CloneUrl = repos[j].HtmlUrl + ".git"
|
||||
repos[j] = ToApiRepository(collaRepos[i].Owner, collaRepos[i].Repository, api.Permission{false, collaRepos[i].CanPush, true})
|
||||
|
||||
// FIXME: cache result to reduce DB query?
|
||||
if collaRepos[i].Owner.IsOrganization() && collaRepos[i].Owner.IsOrgOwner(ctx.User.Id) {
|
||||
if collaRepos[i].Owner.IsOrganization() && collaRepos[i].Owner.IsOwnedBy(ctx.User.Id) {
|
||||
repos[j].Permissions.Admin = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,15 +48,9 @@ func ListRepoHooks(ctx *middleware.Context) {
|
||||
ctx.JSON(200, &apiHooks)
|
||||
}
|
||||
|
||||
type CreateRepoHookForm struct {
|
||||
Type string `json:"type" binding:"Required"`
|
||||
Config map[string]string `json:"config" binding:"Required"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
// POST /repos/:username/:reponame/hooks
|
||||
// https://developer.github.com/v3/repos/hooks/#create-a-hook
|
||||
func CreateRepoHook(ctx *middleware.Context, form CreateRepoHookForm) {
|
||||
func CreateRepoHook(ctx *middleware.Context, form api.CreateHookOption) {
|
||||
if !models.IsValidHookTaskType(form.Type) {
|
||||
ctx.JSON(422, &base.ApiJsonErr{"invalid hook type", base.DOC_URL})
|
||||
return
|
||||
@@ -124,14 +118,9 @@ func CreateRepoHook(ctx *middleware.Context, form CreateRepoHookForm) {
|
||||
ctx.JSON(201, apiHook)
|
||||
}
|
||||
|
||||
type EditRepoHookForm struct {
|
||||
Config map[string]string `json:"config"`
|
||||
Active *bool `json:"active"`
|
||||
}
|
||||
|
||||
// PATCH /repos/:username/:reponame/hooks/:id
|
||||
// https://developer.github.com/v3/repos/hooks/#edit-a-hook
|
||||
func EditRepoHook(ctx *middleware.Context, form EditRepoHookForm) {
|
||||
func EditRepoHook(ctx *middleware.Context, form api.EditHookOption) {
|
||||
w, err := models.GetWebhookById(ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
ctx.JSON(500, &base.ApiJsonErr{"GetWebhookById: " + err.Error(), base.DOC_URL})
|
||||
|
||||
@@ -12,8 +12,18 @@ import (
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/middleware"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
// ToApiUser converts user to API format.
|
||||
func ToApiUser(u *models.User) *api.User {
|
||||
return &api.User{
|
||||
Id: u.Id,
|
||||
UserName: u.Name,
|
||||
AvatarUrl: string(setting.Protocol) + u.AvatarLink(),
|
||||
}
|
||||
}
|
||||
|
||||
func SearchUsers(ctx *middleware.Context) {
|
||||
opt := models.SearchOption{
|
||||
Keyword: ctx.Query("q"),
|
||||
@@ -37,6 +47,7 @@ func SearchUsers(ctx *middleware.Context) {
|
||||
results[i] = &api.User{
|
||||
UserName: us[i].Name,
|
||||
AvatarUrl: us[i].AvatarLink(),
|
||||
FullName: us[i].FullName,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/Unknwon/goconfig"
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
@@ -32,7 +31,7 @@ const (
|
||||
)
|
||||
|
||||
func checkRunMode() {
|
||||
switch setting.Cfg.MustValue("", "RUN_MODE") {
|
||||
switch setting.Cfg.Section("").Key("RUN_MODE").String() {
|
||||
case "prod":
|
||||
macaron.Env = macaron.PROD
|
||||
setting.ProdMode = true
|
||||
@@ -88,6 +87,7 @@ func Install(ctx *middleware.Context, form auth.InstallForm) {
|
||||
ctx.Data["Title"] = ctx.Tr("install.install")
|
||||
ctx.Data["PageIsInstall"] = true
|
||||
|
||||
// FIXME: when i'm ckeching length here? should they all be 0 no matter when?
|
||||
// Get and assign values to install form.
|
||||
if len(form.DbHost) == 0 {
|
||||
form.DbHost = models.DbCfg.Host
|
||||
@@ -109,7 +109,15 @@ func Install(ctx *middleware.Context, form auth.InstallForm) {
|
||||
form.RepoRootPath = setting.RepoRootPath
|
||||
}
|
||||
if len(form.RunUser) == 0 {
|
||||
form.RunUser = setting.RunUser
|
||||
// Note: it's not normall to use SSH in windows so current user can be first option(not git).
|
||||
if setting.IsWindows && setting.RunUser == "git" {
|
||||
form.RunUser = os.Getenv("USER")
|
||||
if len(form.RunUser) == 0 {
|
||||
form.RunUser = os.Getenv("USERNAME")
|
||||
}
|
||||
} else {
|
||||
form.RunUser = setting.RunUser
|
||||
}
|
||||
}
|
||||
if len(form.Domain) == 0 {
|
||||
form.Domain = setting.Domain
|
||||
@@ -201,38 +209,40 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
|
||||
}
|
||||
|
||||
// Save settings.
|
||||
setting.Cfg.SetValue("database", "DB_TYPE", models.DbCfg.Type)
|
||||
setting.Cfg.SetValue("database", "HOST", models.DbCfg.Host)
|
||||
setting.Cfg.SetValue("database", "NAME", models.DbCfg.Name)
|
||||
setting.Cfg.SetValue("database", "USER", models.DbCfg.User)
|
||||
setting.Cfg.SetValue("database", "PASSWD", models.DbCfg.Pwd)
|
||||
setting.Cfg.SetValue("database", "SSL_MODE", models.DbCfg.SslMode)
|
||||
setting.Cfg.SetValue("database", "PATH", models.DbCfg.Path)
|
||||
setting.Cfg.Section("database").Key("DB_TYPE").SetValue(models.DbCfg.Type)
|
||||
setting.Cfg.Section("database").Key("HOST").SetValue(models.DbCfg.Host)
|
||||
setting.Cfg.Section("database").Key("NAME").SetValue(models.DbCfg.Name)
|
||||
setting.Cfg.Section("database").Key("USER").SetValue(models.DbCfg.User)
|
||||
setting.Cfg.Section("database").Key("PASSWD").SetValue(models.DbCfg.Pwd)
|
||||
setting.Cfg.Section("database").Key("SSL_MODE").SetValue(models.DbCfg.SslMode)
|
||||
setting.Cfg.Section("database").Key("PATH").SetValue(models.DbCfg.Path)
|
||||
|
||||
setting.Cfg.SetValue("repository", "ROOT", form.RepoRootPath)
|
||||
setting.Cfg.SetValue("", "RUN_USER", form.RunUser)
|
||||
setting.Cfg.SetValue("server", "DOMAIN", form.Domain)
|
||||
setting.Cfg.SetValue("server", "ROOT_URL", form.AppUrl)
|
||||
setting.Cfg.Section("repository").Key("ROOT").SetValue(form.RepoRootPath)
|
||||
setting.Cfg.Section("").Key("RUN_USER").SetValue(form.RunUser)
|
||||
setting.Cfg.Section("server").Key("DOMAIN").SetValue(form.Domain)
|
||||
setting.Cfg.Section("server").Key("ROOT_URL").SetValue(form.AppUrl)
|
||||
|
||||
if len(strings.TrimSpace(form.SmtpHost)) > 0 {
|
||||
setting.Cfg.SetValue("mailer", "ENABLED", "true")
|
||||
setting.Cfg.SetValue("mailer", "HOST", form.SmtpHost)
|
||||
setting.Cfg.SetValue("mailer", "USER", form.SmtpEmail)
|
||||
setting.Cfg.SetValue("mailer", "PASSWD", form.SmtpPasswd)
|
||||
setting.Cfg.Section("mailer").Key("ENABLED").SetValue("true")
|
||||
setting.Cfg.Section("mailer").Key("HOST").SetValue(form.SmtpHost)
|
||||
setting.Cfg.Section("mailer").Key("USER").SetValue(form.SmtpEmail)
|
||||
setting.Cfg.Section("mailer").Key("PASSWD").SetValue(form.SmtpPasswd)
|
||||
|
||||
setting.Cfg.SetValue("service", "REGISTER_EMAIL_CONFIRM", com.ToStr(form.RegisterConfirm == "on"))
|
||||
setting.Cfg.SetValue("service", "ENABLE_NOTIFY_MAIL", com.ToStr(form.MailNotify == "on"))
|
||||
setting.Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").SetValue(com.ToStr(form.RegisterConfirm == "on"))
|
||||
setting.Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(com.ToStr(form.MailNotify == "on"))
|
||||
}
|
||||
|
||||
setting.Cfg.SetValue("", "RUN_MODE", "prod")
|
||||
setting.Cfg.Section("").Key("RUN_MODE").SetValue("prod")
|
||||
|
||||
setting.Cfg.SetValue("log", "MODE", "file")
|
||||
setting.Cfg.Section("session").Key("PROVIDER").SetValue("file")
|
||||
|
||||
setting.Cfg.SetValue("security", "INSTALL_LOCK", "true")
|
||||
setting.Cfg.SetValue("security", "SECRET_KEY", base.GetRandomString(15))
|
||||
setting.Cfg.Section("log").Key("MODE").SetValue("file")
|
||||
|
||||
setting.Cfg.Section("security").Key("INSTALL_LOCK").SetValue("true")
|
||||
setting.Cfg.Section("security").Key("SECRET_KEY").SetValue(base.GetRandomString(15))
|
||||
|
||||
os.MkdirAll("custom/conf", os.ModePerm)
|
||||
if err := goconfig.SaveConfigFile(setting.Cfg, path.Join(setting.CustomPath, "conf/app.ini")); err != nil {
|
||||
if err := setting.Cfg.SaveTo(path.Join(setting.CustomPath, "conf/app.ini")); err != nil {
|
||||
ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), INSTALL, &form)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"path"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/git"
|
||||
"github.com/gogits/gogs/modules/middleware"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
@@ -31,6 +33,16 @@ func RefCommits(ctx *middleware.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func RenderIssueLinks(oldCommits *list.List, repoLink string) *list.List {
|
||||
newCommits := list.New()
|
||||
for e := oldCommits.Front(); e != nil; e = e.Next() {
|
||||
c := e.Value.(*git.Commit)
|
||||
c.CommitMessage = string(base.RenderIssueIndexPattern([]byte(c.CommitMessage), repoLink))
|
||||
newCommits.PushBack(c)
|
||||
}
|
||||
return newCommits
|
||||
}
|
||||
|
||||
func Commits(ctx *middleware.Context) {
|
||||
ctx.Data["IsRepoToolbarCommits"] = true
|
||||
|
||||
@@ -62,7 +74,7 @@ func Commits(ctx *middleware.Context) {
|
||||
lastPage = 0
|
||||
}
|
||||
nextPage := page + 1
|
||||
if nextPage*50 > commitsCount {
|
||||
if page*50 > commitsCount {
|
||||
nextPage = 0
|
||||
}
|
||||
|
||||
@@ -72,6 +84,7 @@ func Commits(ctx *middleware.Context) {
|
||||
ctx.Handle(500, "CommitsByRange", err)
|
||||
return
|
||||
}
|
||||
commits = RenderIssueLinks(commits, ctx.Repo.RepoLink)
|
||||
commits = models.ValidateCommitsWithEmails(commits)
|
||||
|
||||
ctx.Data["Commits"] = commits
|
||||
@@ -110,6 +123,7 @@ func SearchCommits(ctx *middleware.Context) {
|
||||
ctx.Handle(500, "SearchCommits", err)
|
||||
return
|
||||
}
|
||||
commits = RenderIssueLinks(commits, ctx.Repo.RepoLink)
|
||||
commits = models.ValidateCommitsWithEmails(commits)
|
||||
|
||||
ctx.Data["Keyword"] = keyword
|
||||
@@ -171,6 +185,7 @@ func FileHistory(ctx *middleware.Context) {
|
||||
ctx.Handle(500, "repo.FileHistory(CommitsByRange)", err)
|
||||
return
|
||||
}
|
||||
commits = RenderIssueLinks(commits, ctx.Repo.RepoLink)
|
||||
commits = models.ValidateCommitsWithEmails(commits)
|
||||
|
||||
ctx.Data["Commits"] = commits
|
||||
@@ -191,9 +206,9 @@ func Diff(ctx *middleware.Context) {
|
||||
commitId := ctx.Repo.CommitId
|
||||
|
||||
commit := ctx.Repo.Commit
|
||||
|
||||
commit.CommitMessage = string(base.RenderIssueIndexPattern([]byte(commit.CommitMessage), ctx.Repo.RepoLink))
|
||||
diff, err := models.GetDiffCommit(models.RepoPath(userName, repoName),
|
||||
commitId, setting.MaxGitDiffLines)
|
||||
commitId, setting.Git.MaxGitDiffLines)
|
||||
if err != nil {
|
||||
ctx.Handle(404, "GetDiffCommit", err)
|
||||
return
|
||||
@@ -257,7 +272,7 @@ func CompareDiff(ctx *middleware.Context) {
|
||||
}
|
||||
|
||||
diff, err := models.GetDiffRange(models.RepoPath(userName, repoName), beforeCommitId,
|
||||
afterCommitId, setting.MaxGitDiffLines)
|
||||
afterCommitId, setting.Git.MaxGitDiffLines)
|
||||
if err != nil {
|
||||
ctx.Handle(404, "GetDiffRange", err)
|
||||
return
|
||||
|
||||
@@ -227,6 +227,7 @@ var routes = []route{
|
||||
func HttpBackend(config *Config) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
for _, route := range routes {
|
||||
r.URL.Path = strings.ToLower(r.URL.Path) // blue: In case some repo name has upper case name
|
||||
if m := route.cr.FindStringSubmatch(r.URL.Path); m != nil {
|
||||
if route.method != r.Method {
|
||||
renderMethodNotAllowed(w, r)
|
||||
|
||||
@@ -628,7 +628,7 @@ func UpdateAssignee(ctx *middleware.Context) {
|
||||
}
|
||||
|
||||
aid := com.StrTo(ctx.Query("assigneeid")).MustInt64()
|
||||
// Not check for invalid assignne id and give responsibility to owners.
|
||||
// Not check for invalid assignee id and give responsibility to owners.
|
||||
issue.AssigneeId = aid
|
||||
if err = models.UpdateIssueUserPairByAssignee(aid, issue.Id); err != nil {
|
||||
ctx.Handle(500, "UpdateIssueUserPairByAssignee: %v", err)
|
||||
@@ -1133,3 +1133,7 @@ func PullRequest2(ctx *middleware.Context){
|
||||
func Labels2(ctx *middleware.Context){
|
||||
ctx.HTML(200,"repo/issue2/labels")
|
||||
}
|
||||
|
||||
func Milestones2(ctx *middleware.Context){
|
||||
ctx.HTML(200,"repo/milestone2/list")
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ const (
|
||||
)
|
||||
|
||||
func Releases(ctx *middleware.Context) {
|
||||
ctx.Data["Title"] = "Releases"
|
||||
ctx.Data["Title"] = ctx.Tr("repo.release.releases")
|
||||
ctx.Data["IsRepoToolbarReleases"] = true
|
||||
ctx.Data["IsRepoReleaseNew"] = false
|
||||
|
||||
@@ -35,35 +35,13 @@ func Releases(ctx *middleware.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get default branch.
|
||||
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]
|
||||
}
|
||||
commit, err := ctx.Repo.GitRepo.GetCommitOfBranch(refName)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetCommitOfBranch", err)
|
||||
return
|
||||
}
|
||||
|
||||
commitsCount, err := commit.CommitsCount()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "CommitsCount", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Temproray cache commits count of used branches to speed up.
|
||||
countCache := make(map[string]int)
|
||||
|
||||
tags := make([]*models.Release, len(rawTags))
|
||||
for i, rawTag := range rawTags {
|
||||
for _, rel := range rels {
|
||||
if rel.IsDraft && !ctx.Repo.IsOwner {
|
||||
for j, rel := range rels {
|
||||
if rel == nil || (rel.IsDraft && !ctx.Repo.IsOwner) {
|
||||
continue
|
||||
}
|
||||
if rel.TagName == rawTag {
|
||||
@@ -72,6 +50,7 @@ func Releases(ctx *middleware.Context) {
|
||||
ctx.Handle(500, "GetUserById", err)
|
||||
return
|
||||
}
|
||||
// FIXME: duplicated code.
|
||||
// Get corresponding target if it's not the current branch.
|
||||
if ctx.Repo.BranchName != rel.Target {
|
||||
// Get count if not exists.
|
||||
@@ -89,11 +68,12 @@ func Releases(ctx *middleware.Context) {
|
||||
}
|
||||
rel.NumCommitsBehind = countCache[ctx.Repo.BranchName] - rel.NumCommits
|
||||
} else {
|
||||
rel.NumCommitsBehind = commitsCount - rel.NumCommits
|
||||
rel.NumCommitsBehind = ctx.Repo.CommitsCount - rel.NumCommits
|
||||
}
|
||||
|
||||
rel.Note = base.RenderMarkdownString(rel.Note, ctx.Repo.RepoLink)
|
||||
tags[i] = rel
|
||||
rels[j] = nil // Mark as used.
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -116,9 +96,44 @@ func Releases(ctx *middleware.Context) {
|
||||
ctx.Handle(500, "CommitsCount", err)
|
||||
return
|
||||
}
|
||||
tags[i].NumCommitsBehind = commitsCount - tags[i].NumCommits
|
||||
tags[i].NumCommitsBehind = ctx.Repo.CommitsCount - tags[i].NumCommits
|
||||
}
|
||||
}
|
||||
|
||||
for _, rel := range rels {
|
||||
if rel == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
rel.Publisher, err = models.GetUserById(rel.PublisherId)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetUserById", err)
|
||||
return
|
||||
}
|
||||
// FIXME: duplicated code.
|
||||
// Get corresponding target if it's not the current branch.
|
||||
if ctx.Repo.BranchName != rel.Target {
|
||||
// Get count if not exists.
|
||||
if _, ok := countCache[rel.Target]; !ok {
|
||||
commit, err := ctx.Repo.GitRepo.GetCommitOfBranch(ctx.Repo.BranchName)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetCommitOfBranch", err)
|
||||
return
|
||||
}
|
||||
countCache[ctx.Repo.BranchName], err = commit.CommitsCount()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "CommitsCount2", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
rel.NumCommitsBehind = countCache[ctx.Repo.BranchName] - rel.NumCommits
|
||||
} else {
|
||||
rel.NumCommitsBehind = ctx.Repo.CommitsCount - rel.NumCommits
|
||||
}
|
||||
|
||||
rel.Note = base.RenderMarkdownString(rel.Note, ctx.Repo.RepoLink)
|
||||
tags = append(tags, rel)
|
||||
}
|
||||
models.SortReleases(tags)
|
||||
ctx.Data["Releases"] = tags
|
||||
ctx.HTML(200, RELEASES)
|
||||
@@ -130,7 +145,8 @@ func NewRelease(ctx *middleware.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Title"] = "New Release"
|
||||
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
|
||||
ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch
|
||||
ctx.Data["IsRepoToolbarReleases"] = true
|
||||
ctx.Data["IsRepoReleaseNew"] = true
|
||||
ctx.HTML(200, RELEASE_NEW)
|
||||
@@ -142,7 +158,7 @@ func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Title"] = "New Release"
|
||||
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
|
||||
ctx.Data["IsRepoToolbarReleases"] = true
|
||||
ctx.Data["IsRepoReleaseNew"] = true
|
||||
|
||||
@@ -183,9 +199,9 @@ func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
|
||||
|
||||
if err = models.CreateRelease(ctx.Repo.GitRepo, rel); err != nil {
|
||||
if err == models.ErrReleaseAlreadyExist {
|
||||
ctx.RenderWithErr("Release with this tag name has already existed", "release/new", &form)
|
||||
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), RELEASE_NEW, &form)
|
||||
} else {
|
||||
ctx.Handle(500, "release.ReleasesNewPost(IsReleaseExist)", err)
|
||||
ctx.Handle(500, "CreateRelease", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -204,15 +220,15 @@ func EditRelease(ctx *middleware.Context) {
|
||||
rel, err := models.GetRelease(ctx.Repo.Repository.Id, tagName)
|
||||
if err != nil {
|
||||
if err == models.ErrReleaseNotExist {
|
||||
ctx.Handle(404, "release.ReleasesEdit(GetRelease)", err)
|
||||
ctx.Handle(404, "GetRelease", err)
|
||||
} else {
|
||||
ctx.Handle(500, "release.ReleasesEdit(GetRelease)", err)
|
||||
ctx.Handle(500, "GetRelease", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.Data["Release"] = rel
|
||||
|
||||
ctx.Data["Title"] = "Edit Release"
|
||||
ctx.Data["Title"] = ctx.Tr("repo.release.edit_release")
|
||||
ctx.Data["IsRepoToolbarReleases"] = true
|
||||
ctx.HTML(200, RELEASE_EDIT)
|
||||
}
|
||||
@@ -227,9 +243,9 @@ func EditReleasePost(ctx *middleware.Context, form auth.EditReleaseForm) {
|
||||
rel, err := models.GetRelease(ctx.Repo.Repository.Id, tagName)
|
||||
if err != nil {
|
||||
if err == models.ErrReleaseNotExist {
|
||||
ctx.Handle(404, "release.EditReleasePost(GetRelease)", err)
|
||||
ctx.Handle(404, "GetRelease", err)
|
||||
} else {
|
||||
ctx.Handle(500, "release.EditReleasePost(GetRelease)", err)
|
||||
ctx.Handle(500, "GetRelease", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -240,7 +256,7 @@ func EditReleasePost(ctx *middleware.Context, form auth.EditReleaseForm) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Title"] = "Edit Release"
|
||||
ctx.Data["Title"] = ctx.Tr("repo.release.edit_release")
|
||||
ctx.Data["IsRepoToolbarReleases"] = true
|
||||
|
||||
rel.Title = form.Title
|
||||
@@ -248,7 +264,7 @@ func EditReleasePost(ctx *middleware.Context, form auth.EditReleaseForm) {
|
||||
rel.IsDraft = len(form.Draft) > 0
|
||||
rel.IsPrerelease = form.Prerelease
|
||||
if err = models.UpdateRelease(ctx.Repo.GitRepo, rel); err != nil {
|
||||
ctx.Handle(500, "release.EditReleasePost(UpdateRelease)", err)
|
||||
ctx.Handle(500, "UpdateRelease", err)
|
||||
return
|
||||
}
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/releases")
|
||||
|
||||
@@ -97,14 +97,14 @@ func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) {
|
||||
|
||||
if ctxUser.IsOrganization() {
|
||||
// Check ownership of organization.
|
||||
if !ctxUser.IsOrgOwner(ctx.User.Id) {
|
||||
if !ctxUser.IsOwnedBy(ctx.User.Id) {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
repo, err := models.CreateRepository(ctxUser, form.RepoName, form.Description,
|
||||
form.Gitignore, form.License, form.Private, false, form.InitReadme)
|
||||
form.Gitignore, form.License, form.Private, false, form.AutoInit)
|
||||
if err == nil {
|
||||
log.Trace("Repository created: %s/%s", ctxUser.Name, repo.Name)
|
||||
ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name)
|
||||
@@ -174,7 +174,7 @@ func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) {
|
||||
|
||||
if ctxUser.IsOrganization() {
|
||||
// Check ownership of organization.
|
||||
if !ctxUser.IsOrgOwner(ctx.User.Id) {
|
||||
if !ctxUser.IsOwnedBy(ctx.User.Id) {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
@@ -292,7 +292,7 @@ func ForkPost(ctx *middleware.Context, form auth.CreateRepoForm) {
|
||||
|
||||
if ctxUser.IsOrganization() {
|
||||
// Check ownership of organization.
|
||||
if !ctxUser.IsOrgOwner(ctx.User.Id) {
|
||||
if !ctxUser.IsOwnedBy(ctx.User.Id) {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
"path"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
|
||||
@@ -72,6 +73,7 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
|
||||
}
|
||||
log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName)
|
||||
ctx.Repo.Repository.Name = newRepoName
|
||||
ctx.Repo.Repository.LowerName = strings.ToLower(newRepoName)
|
||||
}
|
||||
|
||||
br := form.Branch
|
||||
@@ -136,7 +138,7 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
|
||||
}
|
||||
|
||||
if ctx.Repo.Owner.IsOrganization() {
|
||||
if !ctx.Repo.Owner.IsOrgOwner(ctx.User.Id) {
|
||||
if !ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) {
|
||||
ctx.Error(404)
|
||||
return
|
||||
}
|
||||
@@ -168,7 +170,7 @@ func SettingsCollaboration(ctx *middleware.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
||||
ctx.Data["PageIsSettingsCollaboration"] = true
|
||||
|
||||
repoLink := strings.TrimPrefix(ctx.Repo.RepoLink, "/")
|
||||
repoLink := path.Join(ctx.Repo.Owner.LowerName, ctx.Repo.Repository.LowerName)
|
||||
|
||||
if ctx.Req.Method == "POST" {
|
||||
name := strings.ToLower(ctx.Query("collaborator"))
|
||||
|
||||
@@ -98,7 +98,7 @@ func Home(ctx *middleware.Context) {
|
||||
readmeExist := base.IsMarkdownFile(blob.Name()) || base.IsReadmeFile(blob.Name())
|
||||
ctx.Data["ReadmeExist"] = readmeExist
|
||||
if readmeExist {
|
||||
ctx.Data["FileContent"] = string(base.RenderMarkdown(buf, ""))
|
||||
ctx.Data["FileContent"] = string(base.RenderMarkdown(buf, branchLink))
|
||||
} else {
|
||||
if err, content := base.ToUtf8WithErr(buf); err != nil {
|
||||
if err != nil {
|
||||
@@ -152,6 +152,15 @@ func Home(ctx *middleware.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// Render issue index links.
|
||||
for _, f := range files {
|
||||
switch c := f[1].(type) {
|
||||
case *git.Commit:
|
||||
c.CommitMessage = string(base.RenderIssueIndexPattern([]byte(c.CommitMessage), ctx.Repo.RepoLink))
|
||||
case *git.SubModuleFile:
|
||||
c.CommitMessage = string(base.RenderIssueIndexPattern([]byte(c.CommitMessage), ctx.Repo.RepoLink))
|
||||
}
|
||||
}
|
||||
ctx.Data["Files"] = files
|
||||
|
||||
var readmeFile *git.Blob
|
||||
@@ -199,6 +208,7 @@ func Home(ctx *middleware.Context) {
|
||||
}
|
||||
|
||||
lastCommit := ctx.Repo.Commit
|
||||
lastCommit.CommitMessage = string(base.RenderIssueIndexPattern([]byte(lastCommit.CommitMessage), ctx.Repo.RepoLink))
|
||||
if len(treePath) > 0 {
|
||||
c, err := ctx.Repo.Commit.GetCommitOfRelPath(treePath)
|
||||
if err != nil {
|
||||
|
||||
@@ -62,6 +62,8 @@ func SignIn(ctx *middleware.Context) {
|
||||
if err != nil {
|
||||
if err != models.ErrUserNotExist {
|
||||
ctx.Handle(500, "GetUserByName", err)
|
||||
} else {
|
||||
ctx.HTML(200, SIGNIN)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -343,6 +345,27 @@ func Activate(ctx *middleware.Context) {
|
||||
ctx.HTML(200, ACTIVATE)
|
||||
}
|
||||
|
||||
func ActivateEmail(ctx *middleware.Context) {
|
||||
code := ctx.Query("code")
|
||||
email_string := ctx.Query("email")
|
||||
|
||||
// Verify code.
|
||||
if email := models.VerifyActiveEmailCode(code, email_string); email != nil {
|
||||
err := email.Activate()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "ActivateEmail", err)
|
||||
}
|
||||
|
||||
log.Trace("Email activated: %s", email.Email)
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("settings.activate_email_success"))
|
||||
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubUrl + "/user/settings/email")
|
||||
return
|
||||
}
|
||||
|
||||
func ForgotPasswd(ctx *middleware.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("auth.forgot_password")
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
|
||||
@@ -100,6 +102,16 @@ func Dashboard(ctx *middleware.Context) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// FIXME: cache results?
|
||||
u, err := models.GetUserByName(act.ActUserName)
|
||||
if err != nil {
|
||||
if err == models.ErrUserNotExist {
|
||||
continue
|
||||
}
|
||||
ctx.Handle(500, "GetUserByName", err)
|
||||
return
|
||||
}
|
||||
act.ActAvatar = u.AvatarLink()
|
||||
feeds = append(feeds, act)
|
||||
}
|
||||
ctx.Data["Feeds"] = feeds
|
||||
@@ -120,6 +132,20 @@ func Pulls(ctx *middleware.Context) {
|
||||
ctx.HTML(200, PULLS)
|
||||
}
|
||||
|
||||
func ShowSSHKeys(ctx *middleware.Context, uid int64) {
|
||||
keys, err := models.ListPublicKeys(uid)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "ListPublicKeys", err)
|
||||
return
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
for i := range keys {
|
||||
buf.WriteString(keys[i].OmitEmail())
|
||||
}
|
||||
ctx.RenderData(200, buf.Bytes())
|
||||
}
|
||||
|
||||
func Profile(ctx *middleware.Context) {
|
||||
ctx.Data["Title"] = "Profile"
|
||||
ctx.Data["PageIsUserProfile"] = true
|
||||
@@ -131,6 +157,12 @@ func Profile(ctx *middleware.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
isShowKeys := false
|
||||
if strings.HasSuffix(uname, ".keys") {
|
||||
isShowKeys = true
|
||||
uname = strings.TrimSuffix(uname, ".keys")
|
||||
}
|
||||
|
||||
u, err := models.GetUserByName(uname)
|
||||
if err != nil {
|
||||
if err == models.ErrUserNotExist {
|
||||
@@ -141,6 +173,12 @@ func Profile(ctx *middleware.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Show SSH keys.
|
||||
if isShowKeys {
|
||||
ShowSSHKeys(ctx, u.Id)
|
||||
return
|
||||
}
|
||||
|
||||
if u.IsOrganization() {
|
||||
ctx.Redirect(setting.AppSubUrl + "/org/" + u.Name)
|
||||
return
|
||||
@@ -156,11 +194,35 @@ func Profile(ctx *middleware.Context) {
|
||||
ctx.Data["TabName"] = tab
|
||||
switch tab {
|
||||
case "activity":
|
||||
ctx.Data["Feeds"], err = models.GetFeeds(u.Id, 0, true)
|
||||
actions, err := models.GetFeeds(u.Id, 0, false)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetFeeds", err)
|
||||
return
|
||||
}
|
||||
feeds := make([]*models.Action, 0, len(actions))
|
||||
for _, act := range actions {
|
||||
if act.IsPrivate {
|
||||
if !ctx.IsSigned {
|
||||
continue
|
||||
}
|
||||
if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName,
|
||||
models.READABLE); !has {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// FIXME: cache results?
|
||||
u, err := models.GetUserByName(act.ActUserName)
|
||||
if err != nil {
|
||||
if err == models.ErrUserNotExist {
|
||||
continue
|
||||
}
|
||||
ctx.Handle(500, "GetUserByName", err)
|
||||
return
|
||||
}
|
||||
act.ActAvatar = u.AvatarLink()
|
||||
feeds = append(feeds, act)
|
||||
}
|
||||
ctx.Data["Feeds"] = feeds
|
||||
default:
|
||||
ctx.Data["Repos"], err = models.GetRepositories(u.Id, ctx.IsSigned && ctx.User.Id == u.Id)
|
||||
if err != nil {
|
||||
@@ -185,32 +247,6 @@ func Email2User(ctx *middleware.Context) {
|
||||
ctx.Redirect(setting.AppSubUrl + "/user/" + u.Name)
|
||||
}
|
||||
|
||||
const (
|
||||
TPL_FEED = `<i class="icon fa fa-%s"></i>
|
||||
<div class="info"><span class="meta">%s</span><br>%s</div>`
|
||||
)
|
||||
|
||||
// func Feeds(ctx *middleware.Context, form auth.FeedsForm) {
|
||||
// actions, err := models.GetFeeds(form.UserId, form.Page*20, false)
|
||||
// if err != nil {
|
||||
// ctx.JSON(500, err)
|
||||
// return
|
||||
// }
|
||||
|
||||
// feeds := make([]string, 0, len(actions))
|
||||
// for _, act := range actions {
|
||||
// if act.IsPrivate {
|
||||
// if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName,
|
||||
// models.READABLE); !has {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// feeds = append(feeds, fmt.Sprintf(TPL_FEED, base.ActionIcon(act.OpType),
|
||||
// base.TimeSince(act.Created), base.ActionDesc(act)))
|
||||
// }
|
||||
// ctx.JSON(200, &feeds)
|
||||
// }
|
||||
|
||||
func Issues(ctx *middleware.Context) {
|
||||
ctx.Data["Title"] = "Your Issues"
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"github.com/gogits/gogs/modules/auth"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/mailer"
|
||||
"github.com/gogits/gogs/modules/middleware"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
@@ -20,6 +22,7 @@ import (
|
||||
const (
|
||||
SETTINGS_PROFILE base.TplName = "user/settings/profile"
|
||||
SETTINGS_PASSWORD base.TplName = "user/settings/password"
|
||||
SETTINGS_EMAILS base.TplName = "user/settings/email"
|
||||
SETTINGS_SSH_KEYS base.TplName = "user/settings/sshkeys"
|
||||
SETTINGS_SOCIAL base.TplName = "user/settings/social"
|
||||
SETTINGS_APPLICATIONS base.TplName = "user/settings/applications"
|
||||
@@ -83,6 +86,154 @@ func SettingsPost(ctx *middleware.Context, form auth.UpdateProfileForm) {
|
||||
ctx.Redirect(setting.AppSubUrl + "/user/settings")
|
||||
}
|
||||
|
||||
// FIXME: limit size.
|
||||
func SettingsAvatar(ctx *middleware.Context, form auth.UploadAvatarForm) {
|
||||
defer ctx.Redirect(setting.AppSubUrl + "/user/settings")
|
||||
|
||||
ctx.User.UseCustomAvatar = form.Enable
|
||||
|
||||
if form.Avatar != nil {
|
||||
fr, err := form.Avatar.Open()
|
||||
if err != nil {
|
||||
ctx.Flash.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(fr)
|
||||
if err != nil {
|
||||
ctx.Flash.Error(err.Error())
|
||||
return
|
||||
}
|
||||
if _, ok := base.IsImageFile(data); !ok {
|
||||
ctx.Flash.Error(ctx.Tr("settings.uploaded_avatar_not_a_image"))
|
||||
return
|
||||
}
|
||||
if err = ctx.User.UploadAvatar(data); err != nil {
|
||||
ctx.Flash.Error(err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// In case no avatar at all.
|
||||
if form.Enable && !com.IsFile(ctx.User.CustomAvatarPath()) {
|
||||
ctx.Flash.Error(ctx.Tr("settings.no_custom_avatar_available"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := models.UpdateUser(ctx.User); err != nil {
|
||||
ctx.Flash.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("settings.update_avatar_success"))
|
||||
}
|
||||
|
||||
func SettingsEmails(ctx *middleware.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("settings")
|
||||
ctx.Data["PageIsUserSettings"] = true
|
||||
ctx.Data["PageIsSettingsEmails"] = true
|
||||
|
||||
var err error
|
||||
ctx.Data["Emails"], err = models.GetEmailAddresses(ctx.User.Id)
|
||||
|
||||
if err != nil {
|
||||
ctx.Handle(500, "email.GetEmailAddresses", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.HTML(200, SETTINGS_EMAILS)
|
||||
}
|
||||
|
||||
func SettingsEmailPost(ctx *middleware.Context, form auth.AddEmailForm) {
|
||||
ctx.Data["Title"] = ctx.Tr("settings")
|
||||
ctx.Data["PageIsUserSettings"] = true
|
||||
ctx.Data["PageIsSettingsEmails"] = true
|
||||
|
||||
var err error
|
||||
ctx.Data["Emails"], err = models.GetEmailAddresses(ctx.User.Id)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "email.GetEmailAddresses", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete Email address.
|
||||
if ctx.Query("_method") == "DELETE" {
|
||||
id := com.StrTo(ctx.Query("id")).MustInt64()
|
||||
if id <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = models.DeleteEmailAddress(&models.EmailAddress{Id: id}); err != nil {
|
||||
ctx.Handle(500, "DeleteEmail", err)
|
||||
} else {
|
||||
log.Trace("Email address deleted: %s", ctx.User.Name)
|
||||
ctx.Redirect(setting.AppSubUrl + "/user/settings/email")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Make emailaddress primary.
|
||||
if ctx.Query("_method") == "PRIMARY" {
|
||||
id := com.StrTo(ctx.Query("id")).MustInt64()
|
||||
if id <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = models.MakeEmailPrimary(&models.EmailAddress{Id: id}); err != nil {
|
||||
ctx.Handle(500, "MakeEmailPrimary", err)
|
||||
} else {
|
||||
log.Trace("Email made primary: %s", ctx.User.Name)
|
||||
ctx.Redirect(setting.AppSubUrl + "/user/settings/email")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Add Email address.
|
||||
if ctx.Req.Method == "POST" {
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(200, SETTINGS_EMAILS)
|
||||
return
|
||||
}
|
||||
|
||||
cleanEmail := strings.Replace(form.Email, "\n", "", -1)
|
||||
e := &models.EmailAddress{
|
||||
Uid: ctx.User.Id,
|
||||
Email: cleanEmail,
|
||||
IsActivated: !setting.Service.RegisterEmailConfirm,
|
||||
}
|
||||
|
||||
if err := models.AddEmailAddress(e); err != nil {
|
||||
if err == models.ErrEmailAlreadyUsed {
|
||||
ctx.RenderWithErr(ctx.Tr("form.email_has_been_used"), SETTINGS_EMAILS, &form)
|
||||
return
|
||||
}
|
||||
ctx.Handle(500, "email.AddEmailAddress", err)
|
||||
return
|
||||
} else {
|
||||
|
||||
// Send confirmation e-mail
|
||||
if setting.Service.RegisterEmailConfirm {
|
||||
mailer.SendActivateEmail(ctx.Render, ctx.User, e)
|
||||
|
||||
if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil {
|
||||
log.Error(4, "Set cache(MailResendLimit) fail: %v", err)
|
||||
}
|
||||
ctx.Flash.Success(ctx.Tr("settings.add_email_success_confirmation_email_sent"))
|
||||
} else {
|
||||
ctx.Flash.Success(ctx.Tr("settings.add_email_success"))
|
||||
}
|
||||
|
||||
log.Trace("Email address added: %s", e.Email)
|
||||
|
||||
ctx.Redirect(setting.AppSubUrl + "/user/settings/email")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ctx.HTML(200, SETTINGS_EMAILS)
|
||||
}
|
||||
|
||||
func SettingsPassword(ctx *middleware.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("settings")
|
||||
ctx.Data["PageIsUserSettings"] = true
|
||||
|
||||
@@ -8,10 +8,11 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
// "strings"
|
||||
"time"
|
||||
|
||||
"github.com/macaron-contrib/oauth2"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/middleware"
|
||||
@@ -19,52 +20,39 @@ import (
|
||||
"github.com/gogits/gogs/modules/social"
|
||||
)
|
||||
|
||||
func extractPath(next string) string {
|
||||
n, err := url.Parse(next)
|
||||
if err != nil {
|
||||
return setting.AppSubUrl + "/"
|
||||
}
|
||||
return n.Path
|
||||
}
|
||||
|
||||
func SocialSignIn(ctx *middleware.Context) {
|
||||
if setting.OauthService == nil {
|
||||
ctx.Handle(404, "social.SocialSignIn(oauth service not enabled)", nil)
|
||||
ctx.Handle(404, "OAuth2 service not enabled", nil)
|
||||
return
|
||||
}
|
||||
|
||||
next := setting.AppSubUrl + "/user/login"
|
||||
info := ctx.Session.Get(oauth2.KEY_TOKEN)
|
||||
if info == nil {
|
||||
ctx.Redirect(next)
|
||||
return
|
||||
}
|
||||
|
||||
next := extractPath(ctx.Query("next"))
|
||||
name := ctx.Params(":name")
|
||||
connect, ok := social.SocialMap[name]
|
||||
if !ok {
|
||||
ctx.Handle(404, "social.SocialSignIn(social login not enabled)", errors.New(name))
|
||||
return
|
||||
}
|
||||
appUrl := strings.TrimSuffix(setting.AppUrl, "/")
|
||||
if name == "weibo" {
|
||||
appUrl = strings.Replace(appUrl, "localhost", "127.0.0.1", 1)
|
||||
}
|
||||
|
||||
code := ctx.Query("code")
|
||||
if code == "" {
|
||||
// redirect to social login page
|
||||
connect.SetRedirectUrl(appUrl + ctx.Req.URL.Path)
|
||||
ctx.Redirect(connect.AuthCodeURL(next))
|
||||
ctx.Handle(404, "social login not enabled", errors.New(name))
|
||||
return
|
||||
}
|
||||
|
||||
// handle call back
|
||||
tk, err := connect.Exchange(code)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "social.SocialSignIn(Exchange)", err)
|
||||
tk := new(oauth2.Token)
|
||||
if err := json.Unmarshal(info.([]byte), tk); err != nil {
|
||||
ctx.Handle(500, "Unmarshal token", err)
|
||||
return
|
||||
}
|
||||
next = extractPath(ctx.Query("state"))
|
||||
log.Trace("social.SocialSignIn(Got token)")
|
||||
|
||||
ui, err := connect.UserInfo(tk, ctx.Req.URL)
|
||||
if err != nil {
|
||||
ctx.Handle(500, fmt.Sprintf("social.SocialSignIn(get info from %s)", name), err)
|
||||
ctx.Handle(500, fmt.Sprintf("UserInfo(%s)", name), err)
|
||||
return
|
||||
}
|
||||
if len(ui.Identity) == 0 {
|
||||
ctx.Handle(404, "no identity is presented", errors.New(name))
|
||||
return
|
||||
}
|
||||
log.Info("social.SocialSignIn(social login): %s", ui)
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
rm -rf output
|
||||
mkdir output
|
||||
outPath=./output
|
||||
|
||||
rm -rf $outPath
|
||||
mkdir $outPath
|
||||
|
||||
go build ../gogs.go
|
||||
chmod +x gogs
|
||||
mv gogs ./output/
|
||||
cp -r ../conf/ ./output/conf/
|
||||
cp -r ../custom/ ./output/custom/
|
||||
cp -r ./dockerfiles/ ./output/dockerfiles/
|
||||
cp -r ../public/ ./output/public/
|
||||
cp -r ../templates/ ./output/templates/
|
||||
cp ../cert.pem ./output/
|
||||
cp ../CONTRIBUTING.md ./output/
|
||||
cp gogs_supervisord.sh ./output/
|
||||
cp ../key.pem ./output/
|
||||
cp ../LICENSE ./output/
|
||||
cp ../README.md ./output/
|
||||
cp ../README_ZH.md ./output/
|
||||
cp start.bat ./output/
|
||||
cp start.sh ./output/
|
||||
cp ../wercker.yml ./output/
|
||||
cp mysql.sql ./output/
|
||||
mv gogs $outPath/
|
||||
|
||||
cp -r ../conf/ $outPath/conf/
|
||||
cp -r ../custom/ $outPath/custom/
|
||||
cp -r dockerfiles/ $outPath/dockerfiles/
|
||||
cp -r ../public/ $outPath/public/
|
||||
cp -r ../templates/ $outPath/templates/
|
||||
cp ../cert.pem $outPath/
|
||||
cp ../CONTRIBUTING.md $outPath/
|
||||
cp gogs_supervisord.sh $outPath/
|
||||
cp ../key.pem $outPath/
|
||||
cp ../LICENSE $outPath/
|
||||
cp ../README.md $outPath/
|
||||
cp ../README_ZH.md $outPath/
|
||||
cp start.bat $outPath/
|
||||
cp start.sh $outPath/
|
||||
cp ../wercker.yml $outPath/
|
||||
cp mysql.sql $outPath/
|
||||
27
scripts/build_freebsd.sh
Executable file
27
scripts/build_freebsd.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
outPlattform=freebsd
|
||||
outArch=amd64
|
||||
outPath=./output_$outPlattform_$outArch
|
||||
|
||||
rm -rf $outPath
|
||||
mkdir $outPath
|
||||
|
||||
CGO_ENABLED=0 GOOS=$outPlattform GOARCH=$outArch go build ../gogs.go
|
||||
chmod +x gogs
|
||||
mv gogs $outPath/
|
||||
|
||||
cp -r ../conf/ $outPath/conf/
|
||||
cp -r ../custom/ $outPath/custom/
|
||||
cp -r dockerfiles/ $outPath/dockerfiles/
|
||||
cp -r ../public/ $outPath/public/
|
||||
cp -r ../templates/ $outPath/templates/
|
||||
cp ../cert.pem $outPath/
|
||||
cp ../CONTRIBUTING.md $outPath/
|
||||
cp gogs_supervisord.sh $outPath/
|
||||
cp ../key.pem $outPath/
|
||||
cp ../LICENSE $outPath/
|
||||
cp ../README.md $outPath/
|
||||
cp ../README_ZH.md $outPath/
|
||||
cp start.bat $outPath/
|
||||
cp start.sh $outPath/
|
||||
cp ../wercker.yml $outPath/
|
||||
cp mysql.sql $outPath/
|
||||
@@ -1,21 +1,27 @@
|
||||
rm -rf output_linux_64
|
||||
mkdir output_linux_64
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ../gogs.go
|
||||
outPlattform=linux
|
||||
outArch=amd64
|
||||
outPath=./output_$outPlattform_$outArch
|
||||
|
||||
rm -rf $outPath
|
||||
mkdir $outPath
|
||||
|
||||
CGO_ENABLED=0 GOOS=$outPlattform GOARCH=$outArch go build ../gogs.go
|
||||
chmod +x gogs
|
||||
mv gogs ./output_linux_64/
|
||||
cp -r ../conf/ ./output_linux_64/conf/
|
||||
cp -r ../custom/ ./output_linux_64/custom/
|
||||
cp -r dockerfiles/ ./output_linux_64/dockerfiles/
|
||||
cp -r ../public/ ./output_linux_64/public/
|
||||
cp -r ../templates/ ./output_linux_64/templates/
|
||||
cp ../cert.pem ./output_linux_64/
|
||||
cp ../CONTRIBUTING.md ./output_linux_64/
|
||||
cp gogs_supervisord.sh ./output_linux_64/
|
||||
cp ../key.pem ./output_linux_64/
|
||||
cp ../LICENSE ./output_linux_64/
|
||||
cp ../README.md ./output_linux_64/
|
||||
cp ../README_ZH.md ./output_linux_64/
|
||||
cp start.bat ./output_linux_64/
|
||||
cp start.sh ./output_linux_64/
|
||||
cp ../wercker.yml ./output_linux_64/
|
||||
cp mysql.sql ./output_linux_64/
|
||||
mv gogs $outPath/
|
||||
|
||||
cp -r ../conf/ $outPath/conf/
|
||||
cp -r ../custom/ $outPath/custom/
|
||||
cp -r dockerfiles/ $outPath/dockerfiles/
|
||||
cp -r ../public/ $outPath/public/
|
||||
cp -r ../templates/ $outPath/templates/
|
||||
cp ../cert.pem $outPath/
|
||||
cp ../CONTRIBUTING.md $outPath/
|
||||
cp gogs_supervisord.sh $outPath/
|
||||
cp ../key.pem $outPath/
|
||||
cp ../LICENSE $outPath/
|
||||
cp ../README.md $outPath/
|
||||
cp ../README_ZH.md $outPath/
|
||||
cp start.bat $outPath/
|
||||
cp start.sh $outPath/
|
||||
cp ../wercker.yml $outPath/
|
||||
cp mysql.sql $outPath/
|
||||
@@ -1 +1 @@
|
||||
0.5.8.1118 Beta
|
||||
0.5.11.0103 Beta
|
||||
@@ -34,8 +34,8 @@
|
||||
<td><a href="{{AppSubUrl}}/admin/auths/{{.Id}}">{{.Name}}</a></td>
|
||||
<td>{{.TypeString}}</td>
|
||||
<td><i class="fa fa{{if .IsActived}}-check{{end}}-square-o"></i></td>
|
||||
<td>{{DateFormat .Updated "M d, Y"}}</td>
|
||||
<td>{{DateFormat .Created "M d, Y"}}</td>
|
||||
<td><span title="{{DateFormat .Updated "r"}}">{{DateFormat .Updated "M d, Y"}}</span></td>
|
||||
<td><span title="{{DateFormat .Created "r"}}">{{DateFormat .Created "M d, Y"}}</span></td>
|
||||
<td><a href="{{AppSubUrl}}/admin/auths/{{.Id}}"><i class="fa fa-pencil-square-o"></i></a></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
||||
@@ -169,13 +169,11 @@
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal admin-dl-horizontal">
|
||||
<dt>{{.i18n.Tr "admin.config.session_provider"}}</dt>
|
||||
<dd>{{.SessionProvider}}</dd>
|
||||
<dd>{{.SessionConfig.Provider}}</dd>
|
||||
<dt>{{.i18n.Tr "admin.config.provider_config"}}</dt>
|
||||
<dd><pre>{{.SessionConfig.ProviderConfig}}</pre></dd>
|
||||
<dt>{{.i18n.Tr "admin.config.cookie_name"}}</dt>
|
||||
<dd>{{.SessionConfig.CookieName}}</dd>
|
||||
<dt>{{.i18n.Tr "admin.config.enable_set_cookie"}}</dt>
|
||||
<dd><i class="fa fa{{if .SessionConfig.EnableSetCookie}}-check{{end}}-square-o"></i></dd>
|
||||
<dt>{{.i18n.Tr "admin.config.gc_interval_time"}}</dt>
|
||||
<dd>{{.SessionConfig.Gclifetime}} {{.i18n.Tr "tool.raw_seconds"}}</dd>
|
||||
<dt>{{.i18n.Tr "admin.config.session_life_time"}}</dt>
|
||||
|
||||
@@ -44,6 +44,10 @@
|
||||
<td>{{.i18n.Tr "admin.dashboard.delete_repo_archives"}}</td>
|
||||
<td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubUrl}}/admin?op=3">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{.i18n.Tr "admin.dashboard.git_gc_repos"}}</td>
|
||||
<td><i class="fa fa-caret-square-o-right"></i> <a href="{{AppSubUrl}}/admin?op=4">{{.i18n.Tr "admin.dashboard.operation_run"}}</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
</table>
|
||||
{{if or .LastPageNum .NextPageNum}}
|
||||
<ul class="pagination">
|
||||
{{if .LastPageNum}}<li><a class="btn btn-medium btn-gray btn-radius" href="{{AppSubUrl}}/admin/users?p={{.LastPageNum}}">« {{.i18n.Tr "admin.prev"}}</a></li>{{end}}
|
||||
{{if .NextPageNum}}<li><a class="btn btn-medium btn-gray btn-radius" href="{{AppSubUrl}}/admin/users?p={{.NextPageNum}}">» {{.i18n.Tr "admin.next"}}</a></li>{{end}}
|
||||
{{if .LastPageNum}}<li><a class="btn btn-medium btn-gray btn-radius" href="{{AppSubUrl}}/admin/notices?p={{.LastPageNum}}">« {{.i18n.Tr "admin.prev"}}</a></li>{{end}}
|
||||
{{if .NextPageNum}}<li><a class="btn btn-medium btn-gray btn-radius" href="{{AppSubUrl}}/admin/notices?p={{.NextPageNum}}">» {{.i18n.Tr "admin.next"}}</a></li>{{end}}
|
||||
</ul>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<td>{{.NumTeams}}</td>
|
||||
<td>{{.NumMembers}}</td>
|
||||
<td>{{.NumRepos}}</td>
|
||||
<td>{{DateFormat .Created "M d, Y"}}</td>
|
||||
<td><span title="{{DateFormat .Created "r"}}">{{DateFormat .Created "M d, Y"}}</span></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
|
||||
@@ -31,13 +31,13 @@
|
||||
{{range .Repos}}
|
||||
<tr>
|
||||
<td>{{.Id}}</td>
|
||||
<td><a href="{{AppSubUrl}}/user/{{.Owner.Name}}">{{.Owner.Name}}</a></td>
|
||||
<td><a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a></td>
|
||||
<td><a href="{{AppSubUrl}}/{{.Owner.Name}}/{{.Name}}">{{.Name}}</a></td>
|
||||
<td><i class="fa fa{{if .IsPrivate}}-check{{end}}-square-o"></i></td>
|
||||
<td>{{.NumWatches}}</td>
|
||||
<td>{{.NumIssues}}</td>
|
||||
<td>{{.NumStars}}</td>
|
||||
<td>{{DateFormat .Created "M d, Y"}}</td>
|
||||
<td>{{.NumIssues}}</td>
|
||||
<td><span title="{{DateFormat .Created "r"}}">{{DateFormat .Created "M d, Y"}}</span></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
@@ -57,4 +57,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "ng/base/footer" .}}
|
||||
{{template "ng/base/footer" .}}
|
||||
|
||||
@@ -32,12 +32,12 @@
|
||||
{{range .Users}}
|
||||
<tr>
|
||||
<td>{{.Id}}</td>
|
||||
<td><a href="{{AppSubUrl}}/user/{{.Name}}">{{.Name}}</a></td>
|
||||
<td><a href="{{AppSubUrl}}/{{.Name}}">{{.Name}}</a></td>
|
||||
<td>{{.Email}}</td>
|
||||
<td><i class="fa fa{{if .IsActive}}-check{{end}}-square-o"></i></td>
|
||||
<td><i class="fa fa{{if .IsAdmin}}-check{{end}}-square-o"></i></td>
|
||||
<td>{{.NumRepos}}</td>
|
||||
<td>{{DateFormat .Created "M d, Y"}}</td>
|
||||
<td><span title="{{DateFormat .Created "r"}}">{{DateFormat .Created "M d, Y"}}</span></td>
|
||||
<td><a href="{{AppSubUrl}}/admin/users/{{.Id}}"><i class="fa fa-pencil-square-o"></i></a></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
||||
30
templates/mail/auth/activate_email.tmpl
Normal file
30
templates/mail/auth/activate_email.tmpl
Normal file
@@ -0,0 +1,30 @@
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>{{.User.Name}}, please activate your e-mail address</title>
|
||||
</head>
|
||||
<body style="background:#eee;">
|
||||
<div style="color:#333; font:12px/1.5 Tahoma,Arial,sans-serif;; text-shadow:1px 1px #fff; padding:0; margin:0;">
|
||||
<div style="width:600px;margin:0 auto; padding:40px 0 20px;">
|
||||
<div style="border:1px solid #d9d9d9;border-radius:3px; background:#fff; box-shadow: 0px 2px 5px rgba(0, 0, 0,.05); -webkit-box-shadow: 0px 2px 5px rgba(0, 0, 0,.05);">
|
||||
<div style="padding: 20px 15px;">
|
||||
<h1 style="font-size:20px; padding:10px 0 20px; margin:0; border-bottom:1px solid #ddd;"><img src="{{.AppUrl}}/img/favicon.png" style="height: 32px; margin-bottom: -10px;"> <a style="color:#333;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{.AppName}}</a></h1>
|
||||
<div style="padding:40px 15px;">
|
||||
<div style="font-size:16px; padding-bottom:30px; font-weight:bold;">
|
||||
Hi <span style="color: #00BFFF;">{{.User.Name}}</span>,
|
||||
</div>
|
||||
<div style="font-size:14px; padding:0 15px;">
|
||||
<p style="margin:0;padding:0 0 9px 0;">Please click the following link to verify your e-mail address within <b>{{.ActiveCodeLives}} hours</b>.</p>
|
||||
<p style="margin:0;padding:0 0 9px 0;">
|
||||
<a href="{{.AppUrl}}user/activate_email?code={{.Code}}&email={{.Email}}">{{.AppUrl}}user/activate_email?code={{.Code}}&email={{.Email}}</a>
|
||||
</p>
|
||||
<p style="margin:0;padding:0 0 9px 0;">Not working? Try copying and pasting it to your browser.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="color:#aaa;padding:10px;text-align:center;">
|
||||
© 2014 <a style="color:#888;text-decoration:none;" target="_blank" href="http://gogits.org">Gogs: Go Git Service</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -34,6 +34,7 @@
|
||||
<script src="{{AppSubUrl}}/ng/js/lib/jquery.magnific-popup.min.js"></script>
|
||||
<script src="{{AppSubUrl}}/ng/js/utils/tabs.js"></script>
|
||||
<script src="{{AppSubUrl}}/ng/js/utils/preview.js"></script>
|
||||
<script src="{{AppSubUrl}}/ng/js/gogs/issue_label.js"></script>
|
||||
<script src="{{AppSubUrl}}/ng/js/gogs.js"></script>
|
||||
|
||||
<title>{{if .Title}}{{.Title}} - {{end}}{{AppName}}</title>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user