Browse Source

Merge branch 'develop' into api/github-compliance

pull/3742/head
Kim "BKC" Carlbäcker 8 years ago committed by GitHub
parent
commit
4e4ed0cb20
  1. 2
      .github/ISSUE_TEMPLATE.md
  2. 1
      .gitignore
  3. 6
      .gopmfile
  4. 24
      .travis.yml
  5. 4
      Makefile
  6. 8
      README.md
  7. 8
      README_ZH.md
  8. 2
      appveyor.yml
  9. 14
      cmd/dump.go
  10. 140
      cmd/hook.go
  11. 202
      cmd/serv.go
  12. 58
      cmd/update.go
  13. 35
      cmd/web.go
  14. 127
      conf/app.ini
  15. 4
      conf/locale/locale_bg-BG.ini
  16. 4
      conf/locale/locale_cs-CZ.ini
  17. 12
      conf/locale/locale_de-DE.ini
  18. 7
      conf/locale/locale_en-US.ini
  19. 22
      conf/locale/locale_es-ES.ini
  20. 4
      conf/locale/locale_fi-FI.ini
  21. 4
      conf/locale/locale_fr-FR.ini
  22. 4
      conf/locale/locale_gl-ES.ini
  23. 4
      conf/locale/locale_it-IT.ini
  24. 4
      conf/locale/locale_ja-JP.ini
  25. 4
      conf/locale/locale_ko-KR.ini
  26. 4
      conf/locale/locale_lv-LV.ini
  27. 4
      conf/locale/locale_nl-NL.ini
  28. 4
      conf/locale/locale_pl-PL.ini
  29. 4
      conf/locale/locale_pt-BR.ini
  30. 26
      conf/locale/locale_ru-RU.ini
  31. 4
      conf/locale/locale_sr-SP.ini
  32. 4
      conf/locale/locale_sv-SE.ini
  33. 4
      conf/locale/locale_tr-TR.ini
  34. 4
      conf/locale/locale_zh-CN.ini
  35. 4
      conf/locale/locale_zh-HK.ini
  36. 28
      conf/locale/locale_zh-TW.ini
  37. 14
      docker/build.sh
  38. 154
      glide.lock
  39. 59
      glide.yaml
  40. 6
      gogs.go
  41. 2
      models/access.go
  42. 4
      models/action.go
  43. 2
      models/admin.go
  44. 175
      models/attachment.go
  45. 2
      models/comment.go
  46. 2
      models/git_diff.go
  47. 530
      models/issue.go
  48. 2
      models/issue_mail.go
  49. 2
      models/login_source.go
  50. 5
      models/migrations/migrations.go
  51. 91
      models/migrations/v15.go
  52. 349
      models/milestone.go
  53. 2
      models/mirror.go
  54. 38
      models/models.go
  55. 18
      models/models_tidb.go
  56. 2
      models/org.go
  57. 25
      models/org_team.go
  58. 18
      models/pull.go
  59. 80
      models/repo.go
  60. 23
      models/repo_collaboration.go
  61. 2
      models/repo_editor.go
  62. 16
      models/ssh_key.go
  63. 6
      models/update.go
  64. 10
      models/user.go
  65. 6
      models/webhook.go
  66. 32
      models/webhook_slack.go
  67. 4
      models/wiki.go
  68. 2
      modules/auth/auth.go
  69. 43
      modules/auth/ldap/ldap.go
  70. 2
      modules/auth/user_form.go
  71. 13
      modules/base/tool.go
  72. 92
      modules/bindata/bindata.go
  73. 2
      modules/context/api.go
  74. 27
      modules/context/context.go
  75. 20
      modules/context/repo.go
  76. 3
      modules/cron/cron.go
  77. 104
      modules/log/conn.go
  78. 73
      modules/log/console.go
  79. 243
      modules/log/file.go
  80. 312
      modules/log/log.go
  81. 87
      modules/log/smtp.go
  82. 2
      modules/mailer/mail.go
  83. 2
      modules/mailer/mailer.go
  84. 2
      modules/markdown/markdown.go
  85. 2
      modules/process/manager.go
  86. 192
      modules/setting/setting.go
  87. 9
      modules/ssh/ssh.go
  88. 2
      modules/template/template.go
  89. 76
      public/assets/librejs/librejs.html
  90. 88
      public/css/gogs.css
  91. BIN
      public/img/avatar_default.png
  92. BIN
      public/img/favicon.png
  93. BIN
      public/img/gogs-large-resize.png
  94. BIN
      public/img/gogs-lg.png
  95. 192
      public/img/gogs.svg
  96. 23
      public/js/gogs.js
  97. 1
      public/less/_home.less
  98. 105
      public/less/_repository.less
  99. 6
      public/plugins/marked-0.3.6/marked.min.js
  100. 1
      public/plugins/notebookjs-0.2.6/notebook.min.js
  101. Some files were not shown because too many files have changed in this diff Show More

2
.github/ISSUE_TEMPLATE.md

@ -18,7 +18,7 @@ The issue will be closed without any reasons if it does not satisfy any of follo
- [ ] Yes (provide example URL)
- [ ] No
- [ ] Not relevant
- Log gist:
- Log gist (usually found in `log/gogs.log`):
## Description

1
.gitignore vendored

@ -17,4 +17,3 @@ output*
gogs.sublime-project
gogs.sublime-workspace
/release
vendor

6
.gopmfile

@ -3,7 +3,7 @@ path = github.com/gogits/gogs
[deps]
github.com/bradfitz/gomemcache = commit:2fafb84
github.com/urfave/cli = commit:47a988
github.com/urfave/cli = commit:347a988
github.com/go-macaron/binding = commit:4892016
github.com/go-macaron/cache = commit:5617353
github.com/go-macaron/captcha = commit:8aa5919
@ -29,6 +29,8 @@ github.com/klauspost/compress = commit:461e8fd
github.com/klauspost/cpuid = commit:09cded8
github.com/klauspost/crc32 = commit:cb6bfca
github.com/lib/pq = commit:67c3f2a
github.com/mattn/go-colorable = commit:d228849
github.com/mattn/go-isatty = commit:30a891c
github.com/mattn/go-sqlite3 = commit:ce9149a
github.com/mcuadros/go-version = commit:257f7b9
github.com/microcosm-cc/bluemonday = commit:e797637
@ -42,6 +44,7 @@ github.com/Unknwon/cae = commit:c6aac99
github.com/Unknwon/com = commit:28b053d
github.com/Unknwon/i18n = commit:39d6f27
github.com/Unknwon/paginater = commit:701c23f
github.com/fatih/color = commit:42c364b
golang.org/x/crypto = commit:dc137be
golang.org/x/net = commit:f249948
golang.org/x/sys = commit:d75a526
@ -49,6 +52,7 @@ golang.org/x/text = commit:ece019d
gopkg.in/alexcesaro/quotedprintable.v3 = commit:2caba25
gopkg.in/asn1-ber.v1 = commit:4e86f43
gopkg.in/bufio.v1 = commit:567b2bf
gopkg.in/clog.v1 = commit:d359c28
gopkg.in/editorconfig/editorconfig-core-go.v1 = commit:a872f05
gopkg.in/gomail.v2 = commit:81ebce5
gopkg.in/ini.v1 = commit:e3c2d47

24
.travis.yml

@ -1,29 +1,17 @@
language: go
go:
- 1.5
- 1.6
- 1.7
- 1.5.x
- 1.6.x
- 1.7.x
- master
before_install:
- sudo apt-get update -qq
- sudo apt-get install -y libpam-dev
- go get github.com/msteinert/pam
install:
- go get -t -v ./...
env:
- GO15VENDOREXPERIMENT=1
script:
- go build -v -tags "pam"
- go test -v -cover -race ./...
notifications:
email:
- u@gogs.io
slack: gophercn:o5pSanyTeNhnfYc3QnG0X7Wx
webhooks:
urls:
- https://webhooks.gitter.im/e/b590f8e03882f7aedc3e
on_success: change
on_failure: always
on_start: never

4
Makefile

@ -74,3 +74,7 @@ fixme:
todo:
grep -rnw "TODO" routers models modules
# Legacy code should be remove by the time of release
legacy:
grep -rnw "LEGACY" routers models modules

8
README.md

@ -1,4 +1,4 @@
Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?branch=master)](https://travis-ci.org/gogits/gogs) [![Build status](https://ci.appveyor.com/api/projects/status/b9uu5ejl933e2wlt/branch/master?svg=true)](https://ci.appveyor.com/project/Unknwon/gogs/branch/master) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/gogs/localized.svg)](https://crowdin.com/project/gogs) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gogits/gogs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Gogs [![Build Status](https://travis-ci.org/gogits/gogs.svg?branch=master)](https://travis-ci.org/gogits/gogs) [![Build status](https://ci.appveyor.com/api/projects/status/b9uu5ejl933e2wlt/branch/master?svg=true)](https://ci.appveyor.com/project/Unknwon/gogs/branch/master) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/gogs/localized.svg)](https://crowdin.com/project/gogs) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gogits/gogs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
=====================
![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true)
@ -46,10 +46,11 @@ The goal of this project is to make the easiest, fastest, and most painless way
- Repository issues, pull requests and wiki
- Migrate and mirror repository and its wiki
- Web editor for repository files and wiki
- Jupyter Notebook
- Gravatar and Federated avatar with custom source
- Mail service
- Administration panel
- Supports MySQL, PostgreSQL, SQLite3 and [TiDB](https://github.com/pingcap/tidb) (experimental)
- Supports MySQL, PostgreSQL, SQLite3, MSSQL and [TiDB](https://github.com/pingcap/tidb) (experimental)
- Multi-language support ([22 languages](https://crowdin.com/project/gogs))
## System Requirements
@ -105,6 +106,7 @@ There are 5 ways to install Gogs:
## Software and Service Support
- [Drone](https://github.com/drone/drone) (CI)
- [Jenkins](https://wiki.jenkins-ci.org/display/JENKINS/Gogs+Webhook+Plugin) (CI)
- [Fabric8](http://fabric8.io/) (DevOps)
- [Taiga](https://taiga.io/) (Project Management)
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs) (IT)
@ -121,7 +123,7 @@ There are 5 ways to install Gogs:
- Router and middleware mechanism of [Macaron](https://github.com/go-macaron/macaron).
- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
- Thanks [Rocker](http://weibo.com/rocker1989) for designing Logo.
- Thanks [Egon Elbre](https://twitter.com/egonelbre) for designing logo.
- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
- Thanks [DigitalOcean](https://www.digitalocean.com) for hosting home and demo sites.
- Thanks [KeyCDN](https://www.keycdn.com/) and [QiNiu](http://www.qiniu.com/) for providing CDN service.

8
README_ZH.md

@ -1,4 +1,4 @@
Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?branch=master)](https://travis-ci.org/gogits/gogs)
Gogs [![Build Status](https://travis-ci.org/gogits/gogs.svg?branch=master)](https://travis-ci.org/gogits/gogs) [![Build status](https://ci.appveyor.com/api/projects/status/b9uu5ejl933e2wlt/branch/master?svg=true)](https://ci.appveyor.com/project/Unknwon/gogs/branch/master)
=====================
Gogs (Go Git Service) 是一款极易搭建的自助 Git 服务。
@ -28,9 +28,10 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
- 支持迁移和镜像仓库以及它的 Wiki
- 支持在线编辑仓库文件和 Wiki
- 支持自定义源的 Gravatar 和 Federated Avatar
- 支持 Jupyter Notebook
- 支持邮件服务
- 支持后台管理面板
- 支持 MySQL、PostgreSQL、SQLite3 和 [TiDB](https://github.com/pingcap/tidb)(实验性支持) 数据库
- 支持 MySQL、PostgreSQL、SQLite3、MSSQL 和 [TiDB](https://github.com/pingcap/tidb)(实验性支持) 数据库
- 支持多语言本地化([22 种语言]([more](https://crowdin.com/project/gogs)))
## 系统要求
@ -74,6 +75,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
## 软件及服务支持
- [Drone](https://github.com/drone/drone)(CI)
- [Jenkins](https://wiki.jenkins-ci.org/display/JENKINS/Gogs+Webhook+Plugin)(CI)
- [Fabric8](http://fabric8.io/)(DevOps)
- [Taiga](https://taiga.io/)(项目管理)
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs)(IT)
@ -90,7 +92,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
- 基于 [Macaron](https://github.com/go-macaron/macaron) 的路由与中间件机制。
- 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。
- 感谢 [Rocker](http://weibo.com/rocker1989) 设计的 Logo。
- 感谢 [Egon Elbre](https://twitter.com/egonelbre) 设计的 Logo。
- 感谢 [Crowdin](https://crowdin.com/project/gogs) 提供免费的开源项目本地化支持。
- 感谢 [DigitalOcean](https://www.digitalocean.com) 提供主站和体验站点的服务器赞助。
- 感谢 [KeyCDN](https://www.keycdn.com/) 和 [七牛云存储](http://www.qiniu.com/) 提供 CDN 服务赞助。

2
appveyor.yml

@ -11,7 +11,7 @@ build: false
deploy: false
install:
- go get -t -v ./...
- go build -v
notifications:
- provider: Email

14
cmd/dump.go

@ -14,6 +14,7 @@ import (
"io/ioutil"
"github.com/Unknwon/cae/zip"
"github.com/Unknwon/com"
"github.com/urfave/cli"
"github.com/gogits/gogs/models"
@ -87,9 +88,22 @@ func runDump(ctx *cli.Context) error {
} else {
log.Printf("Custom dir %s doesn't exist, skipped", setting.CustomPath)
}
if err := z.AddDir("log", setting.LogRootPath); err != nil {
log.Fatalf("Fail to include log: %v", err)
}
for _, dir := range []string{"attachments", "avatars"} {
dirPath := path.Join(setting.AppDataPath, dir)
if !com.IsDir(dirPath) {
continue
}
if err := z.AddDir(path.Join("data", dir), dirPath); err != nil {
log.Fatalf("Fail to include '%s': %v", dirPath, err)
}
}
// FIXME: SSH key file.
if err = z.Close(); err != nil {
os.Remove(fileName)

140
cmd/hook.go

@ -0,0 +1,140 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"bufio"
"bytes"
"os"
"os/exec"
"path/filepath"
"github.com/Unknwon/com"
"github.com/urfave/cli"
"github.com/gogits/gogs/models"
)
var (
CmdHook = cli.Command{
Name: "hook",
Usage: "Delegate commands to corresponding Git hooks",
Description: "All sub-commands should only be called by Git",
Flags: []cli.Flag{
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
},
Subcommands: []cli.Command{
subcmdHookPreReceive,
subcmdHookUpadte,
subcmdHookPostReceive,
},
}
subcmdHookPreReceive = cli.Command{
Name: "pre-receive",
Usage: "Delegate pre-receive Git hook",
Description: "This command should only be called by Git",
Action: runHookPreReceive,
}
subcmdHookUpadte = cli.Command{
Name: "update",
Usage: "Delegate update Git hook",
Description: "This command should only be called by Git",
Action: runHookUpdate,
}
subcmdHookPostReceive = cli.Command{
Name: "post-receive",
Usage: "Delegate post-receive Git hook",
Description: "This command should only be called by Git",
Action: runHookPostReceive,
}
)
func runHookPreReceive(c *cli.Context) error {
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
return nil
}
setup(c, "hooks/pre-receive.log")
buf := bytes.NewBuffer(nil)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
buf.Write(scanner.Bytes())
buf.WriteByte('\n')
}
customHooksPath := os.Getenv(_ENV_REPO_CUSTOM_HOOKS_PATH)
if !com.IsFile(customHooksPath) {
return nil
}
hookCmd := exec.Command(filepath.Join(customHooksPath, "pre-receive"))
hookCmd.Stdout = os.Stdout
hookCmd.Stdin = buf
hookCmd.Stderr = os.Stderr
if err := hookCmd.Run(); err != nil {
fail("Internal error", "Fail to execute custom pre-receive hook: %v", err)
}
return nil
}
func runHookUpdate(c *cli.Context) error {
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
return nil
}
setup(c, "hooks/update.log")
args := c.Args()
if len(args) != 3 {
fail("Arguments received are not equal to three", "Arguments received are not equal to three")
} else if len(args[0]) == 0 {
fail("First argument 'refName' is empty", "First argument 'refName' is empty")
}
uuid := os.Getenv(_ENV_UPDATE_TASK_UUID)
if err := models.AddUpdateTask(&models.UpdateTask{
UUID: uuid,
RefName: args[0],
OldCommitID: args[1],
NewCommitID: args[2],
}); err != nil {
fail("Internal error", "Fail to add update task '%s': %v", uuid, err)
}
customHooksPath := os.Getenv(_ENV_REPO_CUSTOM_HOOKS_PATH)
if !com.IsFile(customHooksPath) {
return nil
}
hookCmd := exec.Command(filepath.Join(customHooksPath, "update"), args...)
hookCmd.Stdout = os.Stdout
hookCmd.Stdin = os.Stdin
hookCmd.Stderr = os.Stderr
if err := hookCmd.Run(); err != nil {
fail("Internal error", "Fail to execute custom pre-receive hook: %v", err)
}
return nil
}
func runHookPostReceive(c *cli.Context) error {
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
return nil
}
setup(c, "hooks/post-receive.log")
customHooksPath := os.Getenv(_ENV_REPO_CUSTOM_HOOKS_PATH)
if !com.IsFile(customHooksPath) {
return nil
}
hookCmd := exec.Command(filepath.Join(customHooksPath, "post-receive"))
hookCmd.Stdout = os.Stdout
hookCmd.Stdin = os.Stdin
hookCmd.Stderr = os.Stderr
if err := hookCmd.Run(); err != nil {
fail("Internal error", "Fail to execute custom post-receive hook: %v", err)
}
return nil
}

202
cmd/serve.go → cmd/serv.go

@ -14,22 +14,24 @@ import (
"time"
"github.com/Unknwon/com"
git "github.com/gogits/git-module"
"github.com/gogits/git-module"
gouuid "github.com/satori/go.uuid"
"github.com/urfave/cli"
log "gopkg.in/clog.v1"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/httplib"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
const (
_ACCESS_DENIED_MESSAGE = "Repository does not exist or you do not have access"
_ACCESS_DENIED_MESSAGE = "Repository does not exist or you do not have access"
_ENV_UPDATE_TASK_UUID = "UPDATE_TASK_UUID"
_ENV_REPO_CUSTOM_HOOKS_PATH = "REPO_CUSTOM_HOOKS_PATH"
)
var CmdServ = cli.Command{
var Serv = cli.Command{
Name: "serv",
Usage: "This command should only be called by SSH shell",
Description: `Serv provide access auth for repositories`,
@ -39,13 +41,28 @@ var CmdServ = cli.Command{
},
}
func setup(logPath string) {
func setup(c *cli.Context, logPath string) {
if c.IsSet("config") {
setting.CustomConf = c.String("config")
} else if c.GlobalIsSet("config") {
setting.CustomConf = c.GlobalString("config")
}
setting.NewContext()
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
setting.NewService()
log.New(log.FILE, log.FileConfig{
Filename: filepath.Join(setting.LogRootPath, logPath),
FileRotationConfig: log.FileRotationConfig{
Rotate: true,
Daily: true,
MaxDays: 3,
},
})
log.Delete(log.CONSOLE) // Remove primary logger
models.LoadConfigs()
if setting.UseSQLite3 || setting.UseTiDB {
if setting.UseSQLite3 {
workDir, _ := setting.WorkDir()
os.Chdir(workDir)
}
@ -53,7 +70,7 @@ func setup(logPath string) {
models.SetEngine()
}
func parseCmd(cmd string) (string, string) {
func parseSSHCmd(cmd string) (string, string) {
ss := strings.SplitN(cmd, " ", 2)
if len(ss) != 2 {
return "", ""
@ -61,6 +78,24 @@ func parseCmd(cmd string) (string, string) {
return ss[0], strings.Replace(ss[1], "'/", "'", 1)
}
func checkDeployKey(key *models.PublicKey, repo *models.Repository) {
// Check if this deploy key belongs to current repository.
if !models.HasDeployKey(key.ID, repo.ID) {
fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
}
// Update deploy key activity.
deployKey, err := models.GetDeployKeyByRepo(key.ID, repo.ID)
if err != nil {
fail("Internal error", "GetDeployKey: %v", err)
}
deployKey.Updated = time.Now()
if err = models.UpdateDeployKey(deployKey); err != nil {
fail("Internal error", "UpdateDeployKey: %v", err)
}
}
var (
allowedCommands = map[string]models.AccessMode{
"git-upload-pack": models.ACCESS_MODE_READ,
@ -76,11 +111,10 @@ func fail(userMessage, logMessage string, args ...interface{}) {
if !setting.ProdMode {
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
}
log.GitLogger.Fatal(3, logMessage, args...)
return
log.Fatal(3, logMessage, args...)
}
log.GitLogger.Close()
log.Shutdown()
os.Exit(1)
}
@ -88,12 +122,12 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
task, err := models.GetUpdateTaskByUUID(uuid)
if err != nil {
if models.IsErrUpdateTaskNotExist(err) {
log.GitLogger.Trace("No update task is presented: %s", uuid)
log.Trace("No update task is presented: %s", uuid)
return
}
log.GitLogger.Fatal(2, "GetUpdateTaskByUUID: %v", err)
log.Fatal(2, "GetUpdateTaskByUUID: %v", err)
} else if err = models.DeleteUpdateTaskByUUID(uuid); err != nil {
log.GitLogger.Fatal(2, "DeleteUpdateTaskByUUID: %v", err)
log.Fatal(2, "DeleteUpdateTaskByUUID: %v", err)
}
if isWiki {
@ -109,13 +143,13 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
RepoUserName: repoUser.Name,
RepoName: reponame,
}); err != nil {
log.GitLogger.Error(2, "Update: %v", err)
log.Error(2, "Update: %v", err)
}
// Ask for running deliver hook and test pull request tasks.
reqURL := setting.LocalURL + repoUser.Name + "/" + reponame + "/tasks/trigger?branch=" +
strings.TrimPrefix(task.RefName, git.BRANCH_PREFIX) + "&secret=" + base.EncodeMD5(repoUser.Salt) + "&pusher=" + com.ToStr(user.ID)
log.GitLogger.Trace("Trigger task: %s", reqURL)
log.Trace("Trigger task: %s", reqURL)
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
InsecureSkipVerify: true,
@ -123,19 +157,15 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
if err == nil {
resp.Body.Close()
if resp.StatusCode/100 != 2 {
log.GitLogger.Error(2, "Fail to trigger task: not 2xx response code")
log.Error(2, "Fail to trigger task: not 2xx response code")
}
} else {
log.GitLogger.Error(2, "Fail to trigger task: %v", err)
log.Error(2, "Fail to trigger task: %v", err)
}
}
func runServ(c *cli.Context) error {
if c.IsSet("config") {
setting.CustomConf = c.String("config")
}
setup("serv.log")
setup(c, "serv.log")
if setting.SSH.Disabled {
println("Gogs: SSH has been disabled")
@ -146,21 +176,21 @@ func runServ(c *cli.Context) error {
fail("Not enough arguments", "Not enough arguments")
}
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
if len(cmd) == 0 {
sshCmd := os.Getenv("SSH_ORIGINAL_COMMAND")
if len(sshCmd) == 0 {
println("Hi there, You've successfully authenticated, but Gogs does not provide shell access.")
println("If this is unexpected, please log in with password and setup Gogs under another user.")
return nil
}
verb, args := parseCmd(cmd)
repoPath := strings.ToLower(strings.Trim(args, "'"))
rr := strings.SplitN(repoPath, "/", 2)
if len(rr) != 2 {
verb, args := parseSSHCmd(sshCmd)
repoFullName := strings.ToLower(strings.Trim(args, "'"))
repoFields := strings.SplitN(repoFullName, "/", 2)
if len(repoFields) != 2 {
fail("Invalid repository path", "Invalid repository path: %v", args)
}
username := strings.ToLower(rr[0])
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
username := strings.ToLower(repoFields[0])
reponame := strings.ToLower(strings.TrimSuffix(repoFields[1], ".git"))
isWiki := false
if strings.HasSuffix(reponame, ".wiki") {
@ -168,122 +198,112 @@ func runServ(c *cli.Context) error {
reponame = reponame[:len(reponame)-5]
}
repoUser, err := models.GetUserByName(username)
repoOwner, err := models.GetUserByName(username)
if err != nil {
if models.IsErrUserNotExist(err) {
fail("Repository owner does not exist", "Unregistered owner: %s", username)
}
fail("Internal error", "Failed to get repository owner (%s): %v", username, err)
fail("Internal error", "Fail to get repository owner '%s': %v", username, err)
}
repo, err := models.GetRepositoryByName(repoUser.ID, reponame)
repo, err := models.GetRepositoryByName(repoOwner.ID, reponame)
if err != nil {
if models.IsErrRepoNotExist(err) {
fail(_ACCESS_DENIED_MESSAGE, "Repository does not exist: %s/%s", repoUser.Name, reponame)
fail(_ACCESS_DENIED_MESSAGE, "Repository does not exist: %s/%s", repoOwner.Name, reponame)
}
fail("Internal error", "Failed to get repository: %v", err)
fail("Internal error", "Fail to get repository: %v", err)
}
repo.Owner = repoOwner
requestedMode, has := allowedCommands[verb]
if !has {
fail("Unknown git command", "Unknown git command %s", verb)
requestMode, ok := allowedCommands[verb]
if !ok {
fail("Unknown git command", "Unknown git command '%s'", verb)
}
// Prohibit push to mirror repositories.
if requestedMode > models.ACCESS_MODE_READ && repo.IsMirror {
if requestMode > models.ACCESS_MODE_READ && repo.IsMirror {
fail("mirror repository is read-only", "")
}
// Allow anonymous clone for public repositories.
var (
keyID int64
user *models.User
)
if requestedMode == models.ACCESS_MODE_WRITE || repo.IsPrivate {
keys := strings.Split(c.Args()[0], "-")
if len(keys) != 2 {
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
}
// Allow anonymous (user is nil) clone for public repositories.
var user *models.User
key, err := models.GetPublicKeyByID(com.StrTo(keys[1]).MustInt64())
if err != nil {
fail("Invalid key ID", "Invalid key ID[%s]: %v", c.Args()[0], err)
}
keyID = key.ID
key, err := models.GetPublicKeyByID(com.StrTo(strings.TrimPrefix(c.Args()[0], "key-")).MustInt64())
if err != nil {
fail("Invalid key ID", "Invalid key ID '%s': %v", c.Args()[0], err)
}
if requestMode == models.ACCESS_MODE_WRITE || repo.IsPrivate {
// Check deploy key or user key.
if key.Type == models.KEY_TYPE_DEPLOY {
if key.Mode < requestedMode {
if key.IsDeployKey() {
if key.Mode < requestMode {
fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
}
// Check if this deploy key belongs to current repository.
if !models.HasDeployKey(key.ID, repo.ID) {
fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
}
// Update deploy key activity.
deployKey, err := models.GetDeployKeyByRepo(key.ID, repo.ID)
if err != nil {
fail("Internal error", "GetDeployKey: %v", err)
}
deployKey.Updated = time.Now()
if err = models.UpdateDeployKey(deployKey); err != nil {
fail("Internal error", "UpdateDeployKey: %v", err)
}
checkDeployKey(key, repo)
} else {
user, err = models.GetUserByKeyID(key.ID)
if err != nil {
fail("internal error", "Failed to get user by key ID(%d): %v", keyID, err)
fail("Internal error", "Fail to get user by key ID '%d': %v", key.ID, err)
}
mode, err := models.AccessLevel(user, repo)
if err != nil {
fail("Internal error", "Fail to check access: %v", err)
} else if mode < requestedMode {
}
if mode < requestMode {
clientMessage := _ACCESS_DENIED_MESSAGE
if mode >= models.ACCESS_MODE_READ {
clientMessage = "You do not have sufficient authorization for this action"
}
fail(clientMessage,
"User %s does not have level %v access to repository %s",
user.Name, requestedMode, repoPath)
"User '%s' does not have level '%v' access to repository '%s'",
user.Name, requestMode, repoFullName)
}
}
} else {
// Check if the key can access to the repository in case of it is a deploy key (a deploy keys != user key).
// A deploy key doesn't represent a signed in user, so in a site with Service.RequireSignInView activated
// we should give read access only in repositories where this deploy key is in use. In other case, a server
// or system using an active deploy key can get read access to all the repositories in a Gogs service.
if key.IsDeployKey() && setting.Service.RequireSignInView {
checkDeployKey(key, repo)
}
}
uuid := gouuid.NewV4().String()
os.Setenv("uuid", uuid)
os.Setenv(_ENV_UPDATE_TASK_UUID, uuid)
os.Setenv(_ENV_REPO_CUSTOM_HOOKS_PATH, filepath.Join(repo.RepoPath(), "custom_hooks"))
// Special handle for Windows.
if setting.IsWindows {
verb = strings.Replace(verb, "-", " ", 1)
}
var gitcmd *exec.Cmd
var gitCmd *exec.Cmd
verbs := strings.Split(verb, " ")
if len(verbs) == 2 {
gitcmd = exec.Command(verbs[0], verbs[1], repoPath)
gitCmd = exec.Command(verbs[0], verbs[1], repoFullName)
} else {
gitcmd = exec.Command(verb, repoPath)
gitCmd = exec.Command(verb, repoFullName)
}
gitcmd.Dir = setting.RepoRootPath
gitcmd.Stdout = os.Stdout
gitcmd.Stdin = os.Stdin
gitcmd.Stderr = os.Stderr
if err = gitcmd.Run(); err != nil {
fail("Internal error", "Failed to execute git command: %v", err)
gitCmd.Dir = setting.RepoRootPath
gitCmd.Stdout = os.Stdout
gitCmd.Stdin = os.Stdin
gitCmd.Stderr = os.Stderr
if err = gitCmd.Run(); err != nil {
fail("Internal error", "Fail to execute git command: %v", err)
}
if requestedMode == models.ACCESS_MODE_WRITE {
handleUpdateTask(uuid, user, repoUser, reponame, isWiki)
if requestMode == models.ACCESS_MODE_WRITE {
handleUpdateTask(uuid, user, repoOwner, reponame, isWiki)
}
// Update user key activity.
if keyID > 0 {
key, err := models.GetPublicKeyByID(keyID)
if key.ID > 0 {
key, err := models.GetPublicKeyByID(key.ID)
if err != nil {
fail("Internal error", "GetPublicKeyById: %v", err)
fail("Internal error", "GetPublicKeyByID: %v", err)
}
key.Updated = time.Now()

58
cmd/update.go

@ -1,58 +0,0 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"os"
"github.com/urfave/cli"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
var CmdUpdate = cli.Command{
Name: "update",
Usage: "This command should only be called by Git hook",
Description: `Update get pushed info and insert into database`,
Action: runUpdate,
Flags: []cli.Flag{
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
},
}
func runUpdate(c *cli.Context) error {
if c.IsSet("config") {
setting.CustomConf = c.String("config")
}
setup("update.log")
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
log.GitLogger.Trace("SSH_ORIGINAL_COMMAND is empty")
return nil
}
args := c.Args()
if len(args) != 3 {
log.GitLogger.Fatal(2, "Arguments received are not equal to three")
} else if len(args[0]) == 0 {
log.GitLogger.Fatal(2, "First argument 'refName' is empty, shouldn't use")
}
task := models.UpdateTask{
UUID: os.Getenv("uuid"),
RefName: args[0],
OldCommitID: args[1],
NewCommitID: args[2],
}
if err := models.AddUpdateTask(&task); err != nil {
log.GitLogger.Fatal(2, "AddUpdateTask: %v", err)
}
return nil
}

35
cmd/web.go

@ -26,6 +26,7 @@ import (
"github.com/go-xorm/xorm"
"github.com/mcuadros/go-version"
"github.com/urfave/cli"
log "gopkg.in/clog.v1"
"gopkg.in/ini.v1"
"gopkg.in/macaron.v1"
@ -36,7 +37,6 @@ import (
"github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/bindata"
"github.com/gogits/gogs/modules/context"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/mailer"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/template"
@ -84,6 +84,7 @@ func checkVersion() {
}
// Check dependency version.
// LEGACY [0.11]: no need to check version as we check in vendor into version control
checkers := []VerChecker{
{"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.6.0"},
{"github.com/go-macaron/binding", binding.Version, "0.3.2"},
@ -94,7 +95,7 @@ func checkVersion() {
{"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"},
{"gopkg.in/ini.v1", ini.Version, "1.8.4"},
{"gopkg.in/macaron.v1", macaron.Version, "1.1.7"},
{"github.com/gogits/git-module", git.Version, "0.4.6"},
{"github.com/gogits/git-module", git.Version, "0.4.7"},
{"github.com/gogits/go-gogs-client", gogs.Version, "0.12.1"},
}
for _, c := range checkers {
@ -362,8 +363,14 @@ func runWeb(ctx *cli.Context) error {
// ***** START: Organization *****
m.Group("/org", func() {
m.Get("/create", org.Create)
m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost)
m.Group("", func() {
m.Get("/create", org.Create)
m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost)
}, func(ctx *context.Context) {
if !ctx.User.CanCreateOrganization() {
ctx.NotFound()
}
})
m.Group("/:org", func() {
m.Get("/dashboard", user.Dashboard)
@ -525,6 +532,10 @@ func runWeb(ctx *cli.Context) error {
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
})
// FIXME: Should use ctx.Repo.PullRequest to unify template, currently we have inconsistent URL
// for PR in same repository. After select branch on the page, the URL contains redundant head user name.
// e.g. /org1/test-repo/compare/master...org1:develop
// which should be /org1/test-repo/compare/master...develop
m.Combo("/compare/*", repo.MustAllowPulls).Get(repo.CompareAndPullRequest).
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
@ -566,7 +577,7 @@ func runWeb(ctx *cli.Context) error {
}, context.RepoRef())
// m.Get("/branches", repo.Branches)
m.Post("/branches/:name/delete", reqSignIn, reqRepoWriter, repo.DeleteBranchPost)
m.Post("/branches/delete/*", reqSignIn, reqRepoWriter, repo.DeleteBranchPost)
m.Group("/wiki", func() {
m.Get("/?:page", repo.Wiki)
@ -598,7 +609,7 @@ func runWeb(ctx *cli.Context) error {
}, context.RepoRef())
m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.RawDiff)
m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.CompareDiff)
m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", context.RepoRef(), repo.CompareDiff)
}, ignSignIn, context.RepoAssignment(), repo.MustBeNotBare)
m.Group("/:username/:reponame", func() {
m.Get("/stars", repo.Stars)
@ -653,7 +664,17 @@ func runWeb(ctx *cli.Context) error {
case setting.SCHEME_HTTP:
err = http.ListenAndServe(listenAddr, m)
case setting.SCHEME_HTTPS:
server := &http.Server{Addr: listenAddr, TLSConfig: &tls.Config{MinVersion: tls.VersionTLS10}, Handler: m}
server := &http.Server{Addr: listenAddr, TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS10,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // Required for HTTP/2 support.
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
},
}, Handler: m}
err = server.ListenAndServeTLS(setting.CertFile, setting.KeyFile)
case setting.SCHEME_FCGI:
err = fcgi.Serve(nil, m)

127
conf/app.ini

@ -52,34 +52,6 @@ FILE_MAX_SIZE = 3
; Maximum number of files per upload
MAX_FILES = 5
[ui]
; Number of repositories that are showed in one explore page
EXPLORE_PAGING_NUM = 20
; Number of issues that are showed in one page
ISSUE_PAGING_NUM = 10
; Number of maximum commits showed in one activity feed
FEED_MAX_COMMIT_NUM = 5
; Value of `theme-color` meta tag, used by Android >= 5.0
; An invalid color like "none" or "disable" will have the default style
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
THEME_COLOR_META_TAG = `#ff5343`
; Max size of files to be displayed (defaults is 8MiB)
MAX_DISPLAY_FILE_SIZE = 8388608
[ui.admin]
; Number of users that are showed in one page
USER_PAGING_NUM = 50
; Number of repos that are showed in one page
REPO_PAGING_NUM = 50
; Number of notices that are showed in one page
NOTICE_PAGING_NUM = 25
; Number of organization that are showed in one page
ORG_PAGING_NUM = 50
[ui.user]
; Number of repos that are showed in one page
REPO_PAGING_NUM = 15
[markdown]
; Enable hard line break extension
ENABLE_HARD_LINE_BREAK = false
@ -106,16 +78,18 @@ LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
DISABLE_SSH = false
; Whether use builtin SSH server or not.
START_SSH_SERVER = false
; Domain name to be exposed in clone URL
; Domain name to be exposed in SSH clone URL
SSH_DOMAIN = %(DOMAIN)s
; Port number to be exposed in SSH clone URL
SSH_PORT = 22
; Network interface builtin SSH server listens on
SSH_LISTEN_HOST = 0.0.0.0
; Port number to be exposed in clone URL
SSH_PORT = 22
; Port number builtin SSH server listens on
SSH_LISTEN_PORT = %(SSH_PORT)s
; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
SSH_ROOT_PATH =
; Choose the ciphers to support for SSH connections
SSH_SERVER_CIPHERS = aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, arcfour256, arcfour128
; Directory to create temporary files when test publick key using ssh-keygen,
; default is system temporary directory.
SSH_KEY_TEST_PATH =
@ -157,7 +131,7 @@ RSA = 2048
DSA = 1024
[database]
; Either "mysql", "postgres" or "sqlite3", it's your choice
; Either "mysql", "postgres" or "sqlite3", you can connect to TiDB with MySQL protocol
DB_TYPE = mysql
HOST = 127.0.0.1:3306
NAME = gogs
@ -169,6 +143,8 @@ SSL_MODE = disable
PATH = data/gogs.db
[admin]
; Disable regular (non-admin) users to create organizations
DISABLE_REGULAR_ORG_CREATION = false
[security]
INSTALL_LOCK = false
@ -178,6 +154,7 @@ SECRET_KEY = !#@FDEWREWR&*(
LOGIN_REMEMBER_DAYS = 7
COOKIE_USERNAME = gogs_awesome
COOKIE_REMEMBER_NAME = gogs_incredible
COOKIE_SECURE = false
; Reverse proxy authentication header name of user name
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
@ -268,6 +245,7 @@ GC_INTERVAL_TIME = 86400
SESSION_LIFE_TIME = 86400
[picture]
; Path to store user uploaded avatars
AVATAR_UPLOAD_PATH = data/avatars
; Chinese users can choose "duoshuo"
; or a custom avatar source, like: http://cn.gravatar.com/avatar/
@ -297,66 +275,43 @@ MAX_FILES = 5
; For more information about the format see http://golang.org/pkg/time/#pkg-constants
FORMAT =
; General settings of loggers
[log]
ROOT_PATH =
; Either "console", "file", "conn", "smtp" or "database", default is "console"
; Can be "console" and "file", default is "console"
; Use comma to separate multiple modes, e.g. "console, file"
MODE = console
; Buffer length of channel, keep it as it is if you don't know what it is.
BUFFER_LEN = 10000
; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
BUFFER_LEN = 100
; Either "Trace", "Info", "Warn", "Error", "Fatal", default is "Trace"
LEVEL = Trace
; For "console" mode only
[log.console]
; leave empty to inherit
LEVEL =
; For "file" mode only
[log.file]
; leave empty to inherit
LEVEL =
; This enables automated log rotate(switch of following options), default is true
; This enables automated log rotate (switch of following options)
LOG_ROTATE = true
; Max line number of single file, default is 1000000
MAX_LINES = 1000000
; Segment log daily
DAILY_ROTATE = true
; Max size shift of single file, default is 28 means 1 << 28, 256MB
MAX_SIZE_SHIFT = 28
; Segment log daily, default is true
DAILY_ROTATE = true
; Expired days of log file(delete after max days), default is 7
; Max line number of single file
MAX_LINES = 1000000
; Expired days of log file (delete after max days)
MAX_DAYS = 7
; For "conn" mode only
[log.conn]
LEVEL =
; Reconnect host for every single message, default is false
RECONNECT_ON_MSG = false
; Try to reconnect when connection is lost, default is false
RECONNECT = false
; Either "tcp", "unix" or "udp", default is "tcp"
PROTOCOL = tcp
; Host address
ADDR =
; For "smtp" mode only
[log.smtp]
; For "slack" mode only
[log.slack]
; leave empty to inherit
LEVEL =
; Name displayed in mail title, default is "Diagnostic message from server"
SUBJECT = Diagnostic message from server
; Mail server
HOST =
; Mailer user name and password
USER =
PASSWD =
; Receivers, can be one or more, e.g. 1@example.com,2@example.com
RECEIVERS =
; For "database" mode only
[log.database]
LEVEL =
; Either "mysql" or "postgres"
DRIVER =
; Based on xorm, e.g.: root:root@localhost/gogs?charset=utf8
CONN =
; Webhook URL
URL =
[cron]
; Enable running cron tasks periodically.
@ -410,6 +365,34 @@ DEFAULT_INTERVAL = 8
; Max number of items will response in a page
MAX_RESPONSE_ITEMS = 50
[ui]
; Number of repositories that are showed in one explore page
EXPLORE_PAGING_NUM = 20
; Number of issues that are showed in one page
ISSUE_PAGING_NUM = 10
; Number of maximum commits showed in one activity feed
FEED_MAX_COMMIT_NUM = 5
; Value of "theme-color" meta tag, used by Android >= 5.0
; An invalid color like "none" or "disable" will have the default style
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
THEME_COLOR_META_TAG = `#ff5343`
; Max size in bytes of files to be displayed (default is 8MB)
MAX_DISPLAY_FILE_SIZE = 8388608
[ui.admin]
; Number of users that are showed in one page
USER_PAGING_NUM = 50
; Number of repos that are showed in one page
REPO_PAGING_NUM = 50
; Number of notices that are showed in one page
NOTICE_PAGING_NUM = 25
; Number of organization that are showed in one page
ORG_PAGING_NUM = 50
[ui.user]
; Number of repos that are showed in one page
REPO_PAGING_NUM = 15
[i18n]
LANGS = en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE,ko-KR,gl-ES
NAMES = English,简体中文,繁體中文(香港),繁體中文(台湾),Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,Polski,български,Italiano,Suomalainen,Türkçe,čeština,Српски,Svenska,한국어,Galego

4
conf/locale/locale_bg-BG.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Вашата версия не поддържа SQLite3,
invalid_db_setting=Настройките на базата данни са некоректни: %v
invalid_repo_path=Основният път към хранилищата е невалиден: %v
run_user_not_match=Потребителският контекст на приложението не е на текущия потребител: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Неуспешно запазване на конфигурация: %v
invalid_admin_setting=Настройките на профил на администратора са невалидни: %v
install_success=Добре дошли! Радваме се, че избрахте Gogs, и Ви пожелаваме приятна работа и сърдечни поздрави!
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Посетителите ще бъдат пре
settings.issues_desc=Включи система за проследяване на задачи
settings.use_internal_issue_tracker=Изполвай вградена система за проследяване на задачи
settings.use_external_issue_tracker=Използвай външна система за проследяване на задачи
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Формат на URL адрес на външна система за проследяване на задачи
settings.tracker_issue_style=Стил на именуване на външна система за проследяване на задачи:
settings.tracker_issue_style.numeric=Цифров
@ -1048,6 +1051,7 @@ config.offline_mode=Офлайн режим
config.disable_router_log=Изключи журнал на маршрутизатора
config.run_user=Потребителски контекст
config.run_mode=Режим на изпълнение
config.git_version=Git Version
config.repo_root_path=Основен път към хранилища
config.static_file_root_path=Път към статични файлове
config.log_file_root_path=Път към журнал

4
conf/locale/locale_cs-CZ.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Vaše verze vydání Gogs nepodporuje SQLite3, prosíme st
invalid_db_setting=Nastavení databáze není správné: %v
invalid_repo_path=Kořenový adresář repositáře není správný: %v
run_user_not_match=Uživatel pro spuštění není aktuální uživatel: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Uložení konfigurace se nezdařilo: %v
invalid_admin_setting=Nastavení účtu správce není správné: %v
install_success=Vítejte! Jsme rádi, že jste si vybrali Gogs. Bavte se a opatrujte se.
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Návštěvníci budou při kliknutí na záložk
settings.issues_desc=Povolit systém úkolů
settings.use_internal_issue_tracker=Povolit věstavěný odlehčený systém úkolů
settings.use_external_issue_tracker=Použít externí systém úkolů
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Formát URL externího systému úkolů
settings.tracker_issue_style=Styl pojmenování externího systému úkolů:
settings.tracker_issue_style.numeric=Číselný
@ -1048,6 +1051,7 @@ config.offline_mode=Režim off-line
config.disable_router_log=Vypnout log směrovače
config.run_user=Účet pro spouštění
config.run_mode=Režim spouštění
config.git_version=Git Version
config.repo_root_path=Kořenový adresář repositářů
config.static_file_root_path=Kořenový adresář statického souboru
config.log_file_root_path=Kořenový adresář souboru logu

12
conf/locale/locale_de-DE.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Ihre Gogs-Version unterstützt SQLite3 nicht. Bitte laden
invalid_db_setting=Datenbankeinstellungen sind nicht korrekt: %v
invalid_repo_path=Repository Verzeichnis ist ungültig: %v
run_user_not_match=Der ausführende Benutzer ist nicht der aktuelle Benutzer: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Fehler beim Speichern der Konfiguration: %v
invalid_admin_setting=Admin-Konto Einstellungen sind ungültig: %v
install_success=Herzlich Willkommen! Wir sind froh, dass Sie sich für Gogs entschieden haben. Wir wünschen viel Vergnügen damit.
@ -338,8 +339,8 @@ access_token_deletion_desc=Das Löschen dieses persönlichen Zugangs-Tokens wird
delete_token_success=Persönlicher Zugriffs-Token wurde erfolgreich entfernt! Vergessen Sie nicht Ihre Anwendung zu aktualisieren.
orgs.none=Sie sind kein Mitglied einer Organisation.
orgs.leave_title=Leave an organization
orgs.leave_desc=You will lose access to all repositories and teams after you left the organization. Do you want to continue?
orgs.leave_title=Organisation verlassen
orgs.leave_desc=Sie verlieren den Zugriff auf alle Repositories und Teams nach dem Verlassen der Organisation. Möchten Sie fortfahren?
delete_account=Konto löschen
delete_prompt=Diese Aktion wird Ihr Konto dauerhaft löschen und kann <strong>NICHT</strong> rückgängig gemacht werden!
@ -388,8 +389,8 @@ migrate_type=Migrationstyp
migrate_type_helper=Dieses Repository wird ein <span class="text blue">Mirror</span> sein
migrate_repo=Repository migrieren
migrate.clone_address=Adresse kopieren
migrate.clone_address_desc=This can be a HTTP/HTTPS/GIT URL.
migrate.clone_address_desc_import_local=You're also allowed to migrate a repository by local server path.
migrate.clone_address_desc=Dies kann eine HTTP/HTTPS/GIT-URL sein.
migrate.clone_address_desc_import_local=Du darfst ein Repository vom lokalen Serverpfad migrieren.
migrate.permission_denied=Ihnen fehlen die Rechte zum Importieren lokaler Repositories.
migrate.invalid_local_path=Der lokale Pfad ist ungültig, existiert nicht oder ist kein Ordner.
migrate.failed=Fehler bei Migration: %v
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Besucher werden auf diese URL umgeleitet, wenn s
settings.issues_desc=Issue-Tracker einschalten
settings.use_internal_issue_tracker=Eingebauten Issue-Tracker verwenden
settings.use_external_issue_tracker=Externes Issue-System verwenden
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=URL-Format des externen Issue-Systems
settings.tracker_issue_style=Namenskonvention des externen Issue-Trackers:
settings.tracker_issue_style.numeric=Numerisch
@ -1048,6 +1051,7 @@ config.offline_mode=Offline-Modus
config.disable_router_log=Router-Log deaktivieren
config.run_user=Ausführender Benutzer
config.run_mode=Laufzeit-Modus
config.git_version=Git Version
config.repo_root_path=Repository-Verzeichnis
config.static_file_root_path=Verzeichnis für statische Dateien
config.log_file_root_path=Log-Verzeichnis

7
conf/locale/locale_en-US.ini

@ -116,6 +116,7 @@ sqlite3_not_available = Your release version does not support SQLite3, please do
invalid_db_setting = Database setting is not correct: %v
invalid_repo_path = Repository root path is invalid: %v
run_user_not_match = Run user isn't the current user: %s -> %s
invalid_smtp_from = SMTP From field is not valid: %v
save_config_failed = Fail to save configuration: %v
invalid_admin_setting = Admin account setting is invalid: %v
install_success = Welcome! We're glad that you chose Gogs, have fun and take care.
@ -826,6 +827,7 @@ team_permission_desc = What permission level should this team have?
form.name_reserved = Organization name '%s' is reserved.
form.name_pattern_not_allowed = Organization name pattern '%s' is not allowed.
form.team_name_reserved = Team name '%s' is reserved.
settings = Settings
settings.options = Options
@ -916,8 +918,8 @@ dashboard.git_gc_repos = Do garbage collection on repositories
dashboard.git_gc_repos_success = All repositories have done garbage collection successfully.
dashboard.resync_all_sshkeys = Rewrite '.ssh/authorized_keys' file (caution: non-Gogs keys will be lost)
dashboard.resync_all_sshkeys_success = All public keys have been rewritten successfully.
dashboard.resync_all_update_hooks = Rewrite all update hook of repositories (needed when custom config path is changed)
dashboard.resync_all_update_hooks_success = All repositories' update hook have been rewritten successfully.
dashboard.resync_all_hooks = Resync pre-receive, update and post-receive hooks of all repositories.
dashboard.resync_all_hooks_success = All repositories' pre-receive, update and post-receive hooks have been resynced successfully.
dashboard.reinit_missing_repos = Reinitialize all repository records that lost Git files
dashboard.reinit_missing_repos_success = All repository records that lost Git files have been reinitialized successfully.
@ -1050,6 +1052,7 @@ config.offline_mode = Offline Mode
config.disable_router_log = Disable Router Log
config.run_user = Run User
config.run_mode = Run Mode
config.git_version = Git Version
config.repo_root_path = Repository Root Path
config.static_file_root_path = Static File Root Path
config.log_file_root_path = Log File Root Path

22
conf/locale/locale_es-ES.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Tu versión no soporta SQLite3, por favor descarga el bina
invalid_db_setting=La configuración de la base de datos no es correcta: %v
invalid_repo_path=La ruta de la raíz del repositorio es inválida: %v
run_user_not_match=El usuario que está ejecutando la aplicación no es el usuario actual: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Error al guardar la configuración: %v
invalid_admin_setting=La configuración de la cuenta de administración es inválida: %v
install_success=Bienvenido! Estamos encantados de que hayas escogido Gogs, diviértete y cuídate.
@ -337,9 +338,9 @@ access_token_deletion=Borrado de Token de Acceso Personal
access_token_deletion_desc=Si elimina este token de acceso personal la aplicación asociada perderá el permiso de acceso. ¿Desea continuar?
delete_token_success=¡El token de acceso personal ha sido eliminado con éxito! No se olvide de actualizar también las aplicaciones asociadas.
orgs.none=You are not a member of any organizations.
orgs.leave_title=Leave an organization
orgs.leave_desc=You will lose access to all repositories and teams after you left the organization. Do you want to continue?
orgs.none=No eres un miembro de ninguna organización.
orgs.leave_title=Salir de una organización
orgs.leave_desc=Perderá el acceso a todos los repositorios y equipos después dejar la organización. ¿Desea continuar?
delete_account=Elimina tu cuenta
delete_prompt=La operación eliminará tu cuenta de forma permanente y ¡<strong>NO</strong> se puede deshacer!
@ -388,8 +389,8 @@ migrate_type=Tipo de migración
migrate_type_helper=Este repositorio será una <span class="text blue">réplica</span>
migrate_repo=Migrar Repositorio
migrate.clone_address=Clonar dirección
migrate.clone_address_desc=This can be a HTTP/HTTPS/GIT URL.
migrate.clone_address_desc_import_local=You're also allowed to migrate a repository by local server path.
migrate.clone_address_desc=Esto puede ser una dirección URL HTTP/HTTPS/GIT.
migrate.clone_address_desc_import_local=También se le permite migrar un repositorio por la ruta del servidor local.
migrate.permission_denied=No te está permitido importar repositorios locales.
migrate.invalid_local_path=Rutal local inválida, no existe o no es un directorio.
migrate.failed=Migración fallida: %v
@ -414,7 +415,7 @@ create_new_repo_command=Crear un nuevo repositorio desde línea de comandos
push_exist_repo=Hacer Push de un repositorio existente desde línea de comandos
repo_is_empty=Este repositorio está vacío, por favor, ¡vuelva más tarde!
files=Files
files=Archivos
branch=Rama
tree=Árbol
filter_branch_and_tag=Filtrar por rama o etiqueta
@ -529,7 +530,7 @@ issues.next=Página Siguiente
issues.open_title=Abierta
issues.closed_title=Cerrada
issues.num_comments=%d comentarios
issues.commented_at='comentado <a href="#%s"> %s'</a>
issues.commented_at=`comentado <a href="#%s"> %s</a>`
issues.delete_comment_confirm=¿Seguro que deseas eliminar este comentario?
issues.no_content=Aún no existe contenido.
issues.close_issue=Cerrar
@ -587,7 +588,7 @@ pulls.cannot_auto_merge_helper=Por favor, fusiona manualmente para resolver los
pulls.merge_pull_request=Fusionar Pull Request
pulls.open_unmerged_pull_exists=`Usted no puede realizar la operación de reapertura porque en estos momentos existe una solicitud de pull request (#%d) para el mismo repositorio con la misma información que se encuentra a la espera de aprobación`
pulls.delete_branch=Eliminar la rama
pulls.delete_branch_has_new_commits=Branch cannot be deleted because it has new commits after mergence.
pulls.delete_branch_has_new_commits=La rama no se puede eliminar porque tiene nuevos commits después de la fusión.
milestones.new=Nuevo Milestone
milestones.open_tab=%d abiertas
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Los visitantes serán redireccionados a la URL c
settings.issues_desc=Habilitar rastreo de incidencias
settings.use_internal_issue_tracker=Usar rastreo de incidencias ligero incluido
settings.use_external_issue_tracker=Usar tracker externo de incidencias
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Formato URL del tracker de incidencias externo
settings.tracker_issue_style=Estilo de etiquetado del tracker externo de incidencias:
settings.tracker_issue_style.numeric=Numérico
@ -1037,7 +1040,7 @@ auths.delete_auth_title=Borrado de autenticación
auths.delete_auth_desc=Esta autenticación será eliminada. ¿Deseas continuar?
auths.still_in_used=Este método de autentificación aún es utilizado por algunos usuarios, por favor elimine o convierta estos usuarios a otro tipo de autentificación.
auths.deletion_success=¡La autenticación ha sido eliminada con éxito!
auths.login_source_exist=Login source '%s' already exists.
auths.login_source_exist=El origen de autenticación '%s' ya existe.
config.server_config=Configuración del servidor
config.app_name=Nombre de la Aplicación
@ -1048,6 +1051,7 @@ config.offline_mode=Modo Sin Conexión
config.disable_router_log=Deshabilitar Log del Router
config.run_user=Ejecutada como Usuario
config.run_mode=Modo de ejecución
config.git_version=Git Version
config.repo_root_path=Ruta del Repositorio
config.static_file_root_path=Ruta de los Ficheros Estáticos
config.log_file_root_path=Ruta de los Ficheros de Log

4
conf/locale/locale_fi-FI.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Julkaisu versiosi ei tue SQLite3, ole hyvä ja lataa viral
invalid_db_setting=Tietokanta asetus ei ole oikea: %v
invalid_repo_path=Repo juuri polku on virheellinen: %v
run_user_not_match=Suoritus käyttäjä ei ole nykyinen käyttäjä: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Asetuksien tallennus epäonnistui: %v
invalid_admin_setting=Ylläpito tili asetus virheellinen: %v
install_success=Tervetuloa! Olemme iloisia että valitsit Gogs, pidä hauskaa ja pidä huolta itsestäsi.
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Vierailijat uudelleenohjataan URL-osoitteeseen k
settings.issues_desc=Enable issue tracker
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
settings.use_external_issue_tracker=Käytä ulkoista vikaseurantaa
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Ulkoisen vikaseurannan URL muoto
settings.tracker_issue_style=External Issue Tracker Naming Style:
settings.tracker_issue_style.numeric=Numeric
@ -1048,6 +1051,7 @@ config.offline_mode=Offline-tila
config.disable_router_log=Poista käytöstä reitittimen loki
config.run_user=Suorita käyttäjänä
config.run_mode=Suoritustila
config.git_version=Git Version
config.repo_root_path=Repon juuren polku
config.static_file_root_path=Staattisen tiedoston juuren polku
config.log_file_root_path=Lokitiedoston juuren polku

4
conf/locale/locale_fr-FR.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Votre version publiée ne prend pas en charge SQLite3. Veu
invalid_db_setting=Paramètres de base de données incorrects : %v
invalid_repo_path=Chemin vers la racine du dépôt invalide : %v
run_user_not_match=L'utilisateur d'exécution saisi n'est pas l'utilisateur d'exécution actuel : %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=La sauvegarde de la configuration a échoué : %v
invalid_admin_setting=Paramètres du compte administrateur invalides : %v
install_success=Bienvenue ! Nous sommes heureux que vous ayez choisi Gogs, amusez-vous et prenez soin de vous.
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Les visiteurs seront redirigés vers cette URL l
settings.issues_desc=Activer le système de tickets
settings.use_internal_issue_tracker=Utiliser le système simplifié de tickets interne
settings.use_external_issue_tracker=Utiliser un bug-tracker externe
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Format d'URL du bug tracker
settings.tracker_issue_style=Style de nommage des bugs du tracker externe :
settings.tracker_issue_style.numeric=Numérique
@ -1048,6 +1051,7 @@ config.offline_mode=Mode hors-ligne
config.disable_router_log=Désactiver la Journalisation du Routeur
config.run_user=Utilisateur système
config.run_mode=Mode d'Éxécution
config.git_version=Git Version
config.repo_root_path=Emplacement des Dépôts
config.static_file_root_path=Chemin statique des fichiers racines
config.log_file_root_path=Emplacement Racine du Fichier Journal

4
conf/locale/locale_gl-ES.ini

@ -116,6 +116,7 @@ sqlite3_not_available=A túa versión non soporta SQLite3, por favor, descarga o
invalid_db_setting=A configuración da base de datos non é correcta: %v
invalid_repo_path=A ruta da raíz do repositorio é inválida: %v
run_user_not_match=A persoa usuaria que está executando a aplicación non é a persoa usuaria actual: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Erro ao gardar a configuración: %v
invalid_admin_setting=A configuración da conta de administración é inválida: %v
install_success=Benvido/a! Estamos encantados de que escolleses Gogs, divírtete e cóidate.
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Os visitantes serán redireccionados á URL cand
settings.issues_desc=Habilitar rastrexo de incidencias
settings.use_internal_issue_tracker=Usar rastrexo de incidencias lixeiro incluído
settings.use_external_issue_tracker=Usar tracker externo de incidencias
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Formato URL do tracker de incidencias externo
settings.tracker_issue_style=Estilo de etiquetaxe do tracker externo de incidencias:
settings.tracker_issue_style.numeric=Numérico
@ -1048,6 +1051,7 @@ config.offline_mode=Modo sen conexión
config.disable_router_log=Deshabilitar log do router
config.run_user=Executada como usuario
config.run_mode=Modo de execución
config.git_version=Git Version
config.repo_root_path=Ruta do repositorio
config.static_file_root_path=Ruta dos ficheiros estáticos
config.log_file_root_path=Ruta dos ficheiros de log

4
conf/locale/locale_it-IT.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Questa versione non supporta SQLite3, si prega di scaricar
invalid_db_setting=La configurazione del database non è corretta: %v
invalid_repo_path=Percorso root del repository invalido: %v
run_user_not_match=Run user non è l'utente corrente: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Fallito il salvataggio della configurazione: %v
invalid_admin_setting=Impostazioni account Admin non valide: %v
install_success=Benvenuto! Siamo felici che tu abbia scelto Gogs, buon divertimento.
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=I visitatori verranno reindirizzati all'URL quan
settings.issues_desc=Enable issue tracker
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
settings.use_external_issue_tracker=Utilizza gestore di problemi esterno
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Formato URL Gestore Problemi Esterno
settings.tracker_issue_style=External Issue Tracker Naming Style:
settings.tracker_issue_style.numeric=Numeric
@ -1048,6 +1051,7 @@ config.offline_mode=Modalità Offline
config.disable_router_log=Disattivare Log del Router
config.run_user=Utente Esecutore
config.run_mode=Modalità Esecuzione
config.git_version=Git Version
config.repo_root_path=Percorso Root del Repository
config.static_file_root_path=Percorso Root del File Statico
config.log_file_root_path=Percorso Root del File di Log

4
conf/locale/locale_ja-JP.ini

@ -116,6 +116,7 @@ sqlite3_not_available=このリリース バージョンは SQLite3 をサポー
invalid_db_setting=データベースの設定が正しくありません: %v
invalid_repo_path=リポジトリのルート パスが無効です: %v
run_user_not_match=実行ユーザーは、現在のユーザーではない: %s-> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=構成の保存に失敗した: %v
invalid_admin_setting=管理者アカウントの設定が無効です: %v
install_success=ようこそ!我々はあなたが Gogs を選んでくれて嬉しいです!楽しみましょう!
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Visitors will be redirected to URL when they cli
settings.issues_desc=Enable issue tracker
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
settings.use_external_issue_tracker=外部課題トラッキングシステムを使用
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=外部課題トラッキングツール URLのフォーマット
settings.tracker_issue_style=External Issue Tracker Naming Style:
settings.tracker_issue_style.numeric=数値
@ -1048,6 +1051,7 @@ config.offline_mode=オフラインモード
config.disable_router_log=ルーターのログを無効にする
config.run_user=実行ユーザ
config.run_mode=実行モード
config.git_version=Git Version
config.repo_root_path=リポジトリのルートパス
config.static_file_root_path=静的ファイルのルートパス
config.log_file_root_path=ログ ファイルのルート パス

4
conf/locale/locale_ko-KR.ini

@ -116,6 +116,7 @@ sqlite3_not_available=설치하신 버전은 SQLite3을 지원하지 않으므
invalid_db_setting=데이터베이스 설정이 올바르지 않습니다: %v
invalid_repo_path=저장소 루트 경로가 올바르지 않습니다: %v
run_user_not_match=실행 유저가 현재 유저가 아닙니다: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=설정을 저장할 수 없습니다: %v
invalid_admin_setting=관리자 계정 설정이 잘못되었습니다: %v
install_success=환영합니다! Gogs를 선택해 주셔서 감사합니다.
@ -657,6 +658,8 @@ settings.external_wiki_url_desc=탭을 클릭하면 URL로 리다이렉트됩니
settings.issues_desc=이슈 추적기를 사용하도록 설정
settings.use_internal_issue_tracker=내장된 경량 이슈 트레커를 사용
settings.use_external_issue_tracker=외부 이슈 트래커 사용하기
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=외부 이슈 트래커 URL 형식
settings.tracker_issue_style=외부 이슈 트래커 명명 스타일:
settings.tracker_issue_style.numeric=숫자
@ -1049,6 +1052,7 @@ config.offline_mode=오프라인 모드
config.disable_router_log=라우터 로그 비활성화
config.run_user=데몬 실행 계정
config.run_mode=실행 모드
config.git_version=Git Version
config.repo_root_path=저장소 최상위 경로
config.static_file_root_path=정적 파일 최상위 경로
config.log_file_root_path=로그 파일 최상위 경로

4
conf/locale/locale_lv-LV.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Jūsu versija neatbalsta SQLite3, lūdzu lejupielādējiet
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
invalid_smtp_from=SMTP From field is not valid: %v
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!
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Apmeklētāji tiks novirzīti uz adresi, kad vi
settings.issues_desc=Iespējot problēmu sekotāju
settings.use_internal_issue_tracker=Izmantot iebūvētu vieglu problēmu sekotāju
settings.use_external_issue_tracker=Izmantot ārējo problēmu sekotāju
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Ārējā problēmu sekotāja adreses formāts
settings.tracker_issue_style=Ārējā problēmu reģistra nosaukumu stils:
settings.tracker_issue_style.numeric=Cipari
@ -1048,6 +1051,7 @@ 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.git_version=Git Version
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

4
conf/locale/locale_nl-NL.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Uw versie biedt geen ondersteuning voor SQLite3, download
invalid_db_setting=Uw database instellingen zijn niet correct: %v
invalid_repo_path=Repositorie basis map is niet correct: %v
run_user_not_match=De uitvoerende gebruiker is niet de huidig gebruiker: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Kan de configuratie niet opslaan: %v
invalid_admin_setting=Uw admin-instellingen zijn niet geldig: %v
install_success=Welkom! Wij zijn veheugd dat u voor Gogs heeft gekozen, veel plezier en tot ziens
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Bezoekers worden doorgestuurd naar de URL als ze
settings.issues_desc=Enable issue tracker
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
settings.use_external_issue_tracker=Externe issuetracker gebruiken
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=URL-formaat externe issuetracker
settings.tracker_issue_style=External Issue Tracker Naming Style:
settings.tracker_issue_style.numeric=Numeric
@ -1048,6 +1051,7 @@ config.offline_mode=Offline-modus
config.disable_router_log=Router-log uitschakelen
config.run_user=Uitvoerende gebruiker
config.run_mode=Uitvoer modus
config.git_version=Git Version
config.repo_root_path=Repositorie basis pad
config.static_file_root_path=Statische bestanden basis pad
config.log_file_root_path=Log bestand basis pad

4
conf/locale/locale_pl-PL.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Twoje wydanie nie obsługuje SQLite3, proszę pobrać ofic
invalid_db_setting=Ustawienia bazy danych nie są poprawne: %v
invalid_repo_path=Ścieżka repozytoriów nie jest poprawna: %v
run_user_not_match=Użytkownik aplikacji nie jest aktualnym użytkownikiem: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Nie udało się zapisać konfiguracji: %v
invalid_admin_setting=Nieprawidłowe ustawienia konta admina: %v
install_success=Cześć! Cieszymy się, że wybierałeś Gogs, baw się dobrze.
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Odwiedzający zostaną przekierowani do adresu U
settings.issues_desc=Włącz system zgłaszania problemów
settings.use_internal_issue_tracker=Użyj wbudowany lekki system zgłaszania problemów
settings.use_external_issue_tracker=Użyj zewnętrznego systemu zgłaszania problemów
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Format dla adresu URL zewnętrznego systemu
settings.tracker_issue_style=Styl nazw zewnętrznego systemu zgłaszania problemów:
settings.tracker_issue_style.numeric=Numeryczny
@ -1048,6 +1051,7 @@ config.offline_mode=Tryb offline
config.disable_router_log=Wyłącz dziennik routera
config.run_user=Użytkownik uruchomieniowy
config.run_mode=Tryb uruchamienia
config.git_version=Git Version
config.repo_root_path=Ścieżka repozytoriów
config.static_file_root_path=Ścieżka plików statycznych
config.log_file_root_path=Ścieżka plików dziennika

4
conf/locale/locale_pt-BR.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Sua versão não suporta SQLite3, por favor faça o downlo
invalid_db_setting=Configuração do banco de dados não está correta: %v
invalid_repo_path=A raiz do repositório é inválida: %v
run_user_not_match=O usuário da execução não é o usuário atual: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Falha ao salvar a configuração: %v
invalid_admin_setting=Configuração da conta de administrador está inválida: %v
install_success=Bem-vindo! Estamos contentes que você escolheu o Gogs, divirta-se e tenha cuidado.
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Os visitantes serão redirecionados para a URL a
settings.issues_desc=Habilitar issue tracker
settings.use_internal_issue_tracker=Usar o issue tracker nativo
settings.use_external_issue_tracker=Usar issue tracker externo
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Formato de URL do issue tracker externo
settings.tracker_issue_style=Estilo de nome de issue tracker externo:
settings.tracker_issue_style.numeric=Numérico
@ -1048,6 +1051,7 @@ config.offline_mode=Modo Offline
config.disable_router_log=Desabilitar o Log do router
config.run_user=Usuário de execução
config.run_mode=Modo de execução
config.git_version=Git Version
config.repo_root_path=Caminho raiz do repositório
config.static_file_root_path=Caminho raiz para arquivo estático
config.log_file_root_path=Caminho raiz para arquivo de log

26
conf/locale/locale_ru-RU.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Ваша версия не поддерживает SQLite
invalid_db_setting=Настройки базы данных не правильные: %v
invalid_repo_path=Недопустимый путь к корню репозитория: %v
run_user_not_match=Текущий пользователь не является пользователем для запуска: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Не удалось сохранить конфигурацию: %v
invalid_admin_setting=Указан недопустимый параметр учетной записи администратора: %v
install_success=Добро пожаловать! Мы рады, что вы выбрали Gogs. Веселитесь и берегите себя.
@ -137,7 +138,7 @@ issues.in_your_repos=В ваших репозиториях
[explore]
repos=Репозитории
users=Пользователи
organizations=Organizations
organizations=Организации
search=Поиск
[auth]
@ -337,9 +338,9 @@ access_token_deletion=Удаление личного токена доступ
access_token_deletion_desc=Удаление этого персонального токена доступа приведет к удалению всех связанных прав доступа к приложению. Вы хотите продолжить?
delete_token_success=Персональный токен доступа успешно удален! Не забудьте изменить настройки вашего приложения.
orgs.none=You are not a member of any organizations.
orgs.leave_title=Leave an organization
orgs.leave_desc=You will lose access to all repositories and teams after you left the organization. Do you want to continue?
orgs.none=Вы не состоите ни в одной организации.
orgs.leave_title=Покинуть организацию
orgs.leave_desc=Вы потеряете доступ ко всем репозиториям и командам, как только покинете организацию. Вы хотите продолжить?
delete_account=Удалить свой аккаунт
delete_prompt=Этим действием вы удалите свою учетную запись навсегда и <strong>НЕ СМОЖЕТЕ</strong> ее вернуть!
@ -388,7 +389,7 @@ migrate_type=Тип миграции
migrate_type_helper=Этот репозиторий будет <span class="text blue">зеркалом</span>
migrate_repo=Перенос репозитория
migrate.clone_address=Скопировать адрес
migrate.clone_address_desc=This can be a HTTP/HTTPS/GIT URL.
migrate.clone_address_desc=Это может быть HTTP/HTTPS/GIT URL-адрес.
migrate.clone_address_desc_import_local=You're also allowed to migrate a repository by local server path.
migrate.permission_denied=У вас нет прав на импорт локальных репозиториев.
migrate.invalid_local_path=Недопустимый локальный путь. Возможно он не существует или является не папкой.
@ -414,7 +415,7 @@ create_new_repo_command=Создать новый репозиторий из к
push_exist_repo=Отправить существующий репозиторий из командной строки
repo_is_empty=Этот репозиторий пуст, пожалуйста, возвращайтесь позже!
files=Files
files=Файлы
branch=Ветка
tree=Дерево
filter_branch_and_tag=Фильтр по ветке или тегу
@ -431,7 +432,7 @@ file_history=История
file_view_raw=Посмотреть исходник
file_permalink=Постоянная ссылка
file_too_large=Этот файл слишком большой, поэтому он не может быть отображен
video_not_supported_in_browser=Your browser doesn't support HTML5 video tag.
video_not_supported_in_browser=Ваш браузер не поддерживает HTML5 видео тэг.
editor.new_file=Новый файл
editor.upload_file=Загрузить файл
@ -461,7 +462,7 @@ editor.cancel=Отмена
editor.filename_cannot_be_empty=Имя файла не может быть пустым.
editor.branch_already_exists=Ветка «%s» уже существует в этом репозитории.
editor.directory_is_a_file=Запись «%s» в пути на верх является файлом, а не каталогом этого репозитория.
editor.file_is_a_symlink=The file '%s' is a symlink that cannot be modified from the web editor.
editor.file_is_a_symlink=Файл '%s' является символической ссылкой, которая не может быть изменена через веб-редактор.
editor.filename_is_a_directory=Файл «%s» является каталогом в этом репозитории.
editor.file_editing_no_longer_exists=Редактируемый вами файл «%s» больше не существует в репозитории.
editor.file_changed_while_editing=Содержимое файла изменилось со времени начала редактирования. <a target="_blank" href="%s"> нажмите здесь,</a> чтобы увидеть, что было изменено, или <strong>нажмите кнопку commit снова</strong>, чтобы перезаписать эти изменения.
@ -586,8 +587,8 @@ pulls.cannot_auto_merge_desc=Этот запрос на слияние не мо
pulls.cannot_auto_merge_helper=Пожалуйста, совершите слияние вручную для урегулирования конфликтов.
pulls.merge_pull_request=Слить пулл-реквест
pulls.open_unmerged_pull_exists=`Вы не можете снова открыть, поскольку уже существует запрос на слияние (#%d) из того же репозитория с той же информацией о слиянии и ожидающий слияния. `
pulls.delete_branch=Delete Branch
pulls.delete_branch_has_new_commits=Branch cannot be deleted because it has new commits after mergence.
pulls.delete_branch=Удалить ветку
pulls.delete_branch_has_new_commits=Нельзя удалить ветку, так как она имеет новые коммиты после слияния.
milestones.new=Новая контрольная точка
milestones.open_tab=%d открыты
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Посетители будут перенапр
settings.issues_desc=Включить систему отслеживания ошибок
settings.use_internal_issue_tracker=Использовать встроенную легковесную систему отслеживания ошибок
settings.use_external_issue_tracker=Использовать внешнюю систему отслеживания ошибок
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Внешний формат ссылки системы отслеживания ошибок.
settings.tracker_issue_style=Стиль Именования Внешней Системы Учета Задач:
settings.tracker_issue_style.numeric=Цифровой
@ -1037,7 +1040,7 @@ auths.delete_auth_title=Удаление канала аутентификаци
auths.delete_auth_desc=Этот канал аутентификации будет удален. Вы уверены что хотите продолжить?
auths.still_in_used=Эта проверка подлинности до сих пор используется некоторыми пользователями, удалите или преобразуйте этих пользователей в другой тип входа в систему.
auths.deletion_success=Канал аутентификации успешно удален!
auths.login_source_exist=Login source '%s' already exists.
auths.login_source_exist=Источник входа '%s' уже существует.
config.server_config=Конфигурация сервера
config.app_name=Имя приложения
@ -1048,6 +1051,7 @@ config.offline_mode=Автономный режим
config.disable_router_log=Отключение журнала маршрутизатора
config.run_user=Запуск пользователем
config.run_mode=Режим выполнения
config.git_version=Git Version
config.repo_root_path=Путь до корня репозитория
config.static_file_root_path=Статичный путь до файла
config.log_file_root_path=Путь до папки с логами

4
conf/locale/locale_sr-SP.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Ваша верзија не подржава SQLite3, м
invalid_db_setting=Подешавања базе података су неправилна: %v
invalid_repo_path=Пут до корена спремишта не неправилно: %v
run_user_not_match=Тренутни корисник није корисник за покретање: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Није могуће сачувати конфигурацију: %v
invalid_admin_setting=Подешавањна администратора су неправилна: %v
install_success=Добродошли! Ми смо срећни што сте изабрали Gogs.
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Посетиоци ће бити преусме
settings.issues_desc=Омогући систем праћења грешака
settings.use_internal_issue_tracker=Користи уграђен систем праћења грешака
settings.use_external_issue_tracker=Користи спољни систем праћења грешака
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Спољни формат везе система за праћење грешака
settings.tracker_issue_style=Стил именовања на спољном систему:
settings.tracker_issue_style.numeric=Нумерично
@ -1048,6 +1051,7 @@ config.offline_mode=Режим ван мреже
config.disable_router_log=Онемогући журнал рутера
config.run_user=Покретни корисник
config.run_mode=Режим извршавања
config.git_version=Git Version
config.repo_root_path=Пут до корена спремишта
config.static_file_root_path=Пут до статичке датотеке
config.log_file_root_path=Пут до журнала

4
conf/locale/locale_sv-SE.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Din release stödjer ej SQLite3, ladda vänligen ner den o
invalid_db_setting=Databas inställningen är inkorrekt: %v
invalid_repo_path=Utvecklingskatalogens rotsökväg är ogiltig: %v
run_user_not_match=Köranvändaren är inte aktuell användare: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Konfigurationssparningen misslyckades: %v
invalid_admin_setting=Inställningarna för administratörskontot är felaktiga: %v
install_success=Välkommen! Vi är glada att du väljer Gogs, ha kul och ta hand om dig.
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Besökare kommer att bli omdirigerade till denna
settings.issues_desc=Aktivera ärendehantering
settings.use_internal_issue_tracker=Använd enkel inbyggd ärendehantering
settings.use_external_issue_tracker=Använd extern ärendehanterare
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=URL-Format För Extern Ärendehanterare
settings.tracker_issue_style=Namngivningsstil hos det externa ärendehanteringsystemet:
settings.tracker_issue_style.numeric=Numerisk
@ -1048,6 +1051,7 @@ config.offline_mode=Offline Mode
config.disable_router_log=Disable Router Log
config.run_user=Run User
config.run_mode=Run Mode
config.git_version=Git Version
config.repo_root_path=Rotsökväg för utvecklingskatalog
config.static_file_root_path=Static File Root Path
config.log_file_root_path=Log File Root Path

4
conf/locale/locale_tr-TR.ini

@ -116,6 +116,7 @@ sqlite3_not_available=Yayın sürümünüz SQLite3'ü desteklemiyor, lütfen %s'
invalid_db_setting=Veritabanı ayarları geçersiz: %v
invalid_repo_path=Depo kök dizini geçersiz: %v
run_user_not_match=Çalıştırma kullanıcısı geçerli kullanıcı değil: %s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=Yapılandırma kaydedilemedi: %v
invalid_admin_setting=Yönetici hesap ayarları geçersiz: %v
install_success=Merhaba! Gogs'u tercih ettiğiniz için çok mutluyuz, keyfini çıkarın!
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=Ziyaretçiler, sekmeye tıkladıklarında bağla
settings.issues_desc=Enable issue tracker
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
settings.use_external_issue_tracker=Harici sorun takipçisi kullan
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=Harici Sorun Takipçisi Bağlantı Formatı
settings.tracker_issue_style=Harici Hata İzleyicisi Adlandırma Stili:
settings.tracker_issue_style.numeric=Sayısal
@ -1048,6 +1051,7 @@ config.offline_mode=Çevrim Dışı Modu
config.disable_router_log=Yönlendirici Log'larını Devre Dışı Bırak
config.run_user=Çalıştırma Kullanıcısı
config.run_mode=Çalıştırma Modu
config.git_version=Git Version
config.repo_root_path=Depo Kök Yolu
config.static_file_root_path=Sabit Dosya Kök Yolu
config.log_file_root_path=Log Dosyası Kök Yolu

4
conf/locale/locale_zh-CN.ini

@ -116,6 +116,7 @@ sqlite3_not_available=您所使用的发行版不支持 SQLite3,请从 %s 下
invalid_db_setting=数据库设置不正确:%v
invalid_repo_path=仓库根目录设置不正确:%v
run_user_not_match=运行系统用户非当前用户:%s -> %s
invalid_smtp_from=SMTP From 字段不合法:%v
save_config_failed=应用配置保存失败:%v
invalid_admin_setting=管理员帐户设置不正确:%v
install_success=您好!我们很高兴您选择使用 Gogs,祝您使用愉快,代码从此无 BUG!
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=当访问者单击分页标签时,将会被重
settings.issues_desc=启用工单管理系统
settings.use_internal_issue_tracker=使用内置的轻量级工单管理系统
settings.use_external_issue_tracker=使用外部的工单管理系统
settings.external_tracker_url=外部工单管理系统 URL
settings.external_tracker_url_desc=当访问者单击分页标签时,将会被重定向到该链接。
settings.tracker_url_format=外部工单管理系统的 URL 格式
settings.tracker_issue_style=外部工单管理系统命名风格:
settings.tracker_issue_style.numeric=纯数字形式
@ -1048,6 +1051,7 @@ config.offline_mode=离线模式
config.disable_router_log=关闭路由日志
config.run_user=运行用户
config.run_mode=运行模式
config.git_version=Git 版本
config.repo_root_path=仓库根目录
config.static_file_root_path=静态文件根目录
config.log_file_root_path=日志文件根目录

4
conf/locale/locale_zh-HK.ini

@ -116,6 +116,7 @@ sqlite3_not_available=您所使用的發行版本不支持 SQLite3,請從 %s
invalid_db_setting=數據庫設置不正確:%v
invalid_repo_path=倉庫根目錄設置不正確:%v
run_user_not_match=執行系統用戶非當前用戶:%s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=應用配置保存失敗:%v
invalid_admin_setting=管理員帳戶設置不正確:%v
install_success=您好!我們很高興您選擇使用 Gogs,祝您使用愉快,代碼從此無 BUG!
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=當分頁上按一下,訪客將會重新導到
settings.issues_desc=Enable issue tracker
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
settings.use_external_issue_tracker=使用外部的問題管理系統
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=外部問題管理系統的 URL 格式
settings.tracker_issue_style=External Issue Tracker Naming Style:
settings.tracker_issue_style.numeric=Numeric
@ -1048,6 +1051,7 @@ config.offline_mode=離線模式
config.disable_router_log=關閉路由日志
config.run_user=執行用戶
config.run_mode=執行模式
config.git_version=Git Version
config.repo_root_path=倉庫根目錄
config.static_file_root_path=靜態文件根目錄
config.log_file_root_path=日志文件根目錄

28
conf/locale/locale_zh-TW.ini

@ -116,6 +116,7 @@ sqlite3_not_available=您所使用的發行版本不支持 SQLite3,請從 %s
invalid_db_setting=數據庫設置不正確:%v
invalid_repo_path=倉庫根目錄設置不正確:%v
run_user_not_match=執行系統用戶非當前用戶:%s -> %s
invalid_smtp_from=SMTP From field is not valid: %v
save_config_failed=應用配置保存失敗:%v
invalid_admin_setting=管理員帳戶設置不正確:%v
install_success=您好!我們很高興您選擇使用 Gogs,祝您使用愉快,代碼從此無 BUG!
@ -137,7 +138,7 @@ issues.in_your_repos=屬於該用戶倉庫的
[explore]
repos=探索倉庫
users=用戶
organizations=Organizations
organizations=組織
search=搜索
[auth]
@ -337,9 +338,9 @@ access_token_deletion=刪除個人的連接token
access_token_deletion_desc=刪除此連接token將會刪除與相關應用程式的連結。您想要繼續嗎?
delete_token_success=您的連接token已成功刪除。請記得更新您的應用程式。
orgs.none=You are not a member of any organizations.
orgs.leave_title=Leave an organization
orgs.leave_desc=You will lose access to all repositories and teams after you left the organization. Do you want to continue?
orgs.none=您現在還不是任何組織的成員。
orgs.leave_title=離開組織
orgs.leave_desc=離開組織後,所有與組織相關的倉庫和團隊權限將被取消。是否繼續?
delete_account=刪除當前帳戶
delete_prompt=刪除操作會永久清除您的帳戶信息,並且 <strong>不可恢復</strong>!
@ -388,8 +389,8 @@ migrate_type=遷移類型
migrate_type_helper=該倉庫將是一個 <span class="text blue">鏡像</span>
migrate_repo=遷移倉庫
migrate.clone_address=複製地址
migrate.clone_address_desc=This can be a HTTP/HTTPS/GIT URL.
migrate.clone_address_desc_import_local=You're also allowed to migrate a repository by local server path.
migrate.clone_address_desc=該地址可以是 HTTP/HTTPS/GIT 類型的 URL。
migrate.clone_address_desc_import_local=您還可以用本地服務器路徑來遷移倉庫。
migrate.permission_denied=您並沒有導入本地倉庫的權限。
migrate.invalid_local_path=無效的本地路徑,該路徑不存在或不是一個目錄!
migrate.failed=遷移失敗:%v
@ -414,7 +415,7 @@ create_new_repo_command=從命令行創建一個新的倉庫
push_exist_repo=從命令行推送已經創建的倉庫
repo_is_empty=這倉庫不包含任何內容,請稍後再訪問!
files=Files
files=檔案
branch=分支
tree=目錄樹
filter_branch_and_tag=過濾分支或標籤
@ -431,7 +432,7 @@ file_history=文件歷史
file_view_raw=查看原始文件
file_permalink=永久連結
file_too_large=檔案太大,無法顯示
video_not_supported_in_browser=Your browser doesn't support HTML5 video tag.
video_not_supported_in_browser=您的瀏覽器不支援 HTML5 影片播放標籤。
editor.new_file=開新檔案
editor.upload_file=上傳檔案
@ -461,7 +462,7 @@ editor.cancel=取消
editor.filename_cannot_be_empty=檔案名不能為空。
editor.branch_already_exists='%s' 已存在於此存儲庫。
editor.directory_is_a_file='%s' 在此倉庫中的路徑是檔案而不是目錄。
editor.file_is_a_symlink=The file '%s' is a symlink that cannot be modified from the web editor.
editor.file_is_a_symlink=檔案 '%s' 是一個符號連結,不能從 web 編輯器修改。
editor.filename_is_a_directory=檔案名 '%s' 是此資料庫中的現有目錄。
editor.file_editing_no_longer_exists=檔 '%s' 您正在編輯不再存在於資料庫。
editor.file_changed_while_editing=從您開始編輯已更改檔的內容。<a target="_blank"href="%s"> 按一下此處</a> 以查看什麼發生了更改或 <strong>按提交再</strong> 覆蓋這些更改。
@ -586,8 +587,8 @@ pulls.cannot_auto_merge_desc=由於存在衝突,不能自動合併這推送請
pulls.cannot_auto_merge_helper=請手動合併來解決衝突。
pulls.merge_pull_request=合併請求
pulls.open_unmerged_pull_exists=`由於已經存在來自相同倉庫和合併信息的未合併請求(#%d),您無法執行重新開啟操作。`
pulls.delete_branch=Delete Branch
pulls.delete_branch_has_new_commits=Branch cannot be deleted because it has new commits after mergence.
pulls.delete_branch=刪除分支
pulls.delete_branch_has_new_commits=該分支在合併完成後又推送了新的提交,無法執行刪除。
milestones.new=新的里程碑
milestones.open_tab=%d 開啟中
@ -656,6 +657,8 @@ settings.external_wiki_url_desc=當分頁上按一下,訪客將會重新導到
settings.issues_desc=啟用問題追蹤
settings.use_internal_issue_tracker=使用內建輕量級問題追蹤
settings.use_external_issue_tracker=使用外部的問題管理系統
settings.external_tracker_url=External Issue Tracker URL
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.tracker_url_format=外部問題管理系統的 URL 格式
settings.tracker_issue_style=外部公單管理系統命名風格:
settings.tracker_issue_style.numeric=數字
@ -1037,7 +1040,7 @@ auths.delete_auth_title=刪除認證操作
auths.delete_auth_desc=該認證將被刪除。是否繼續?
auths.still_in_used=一些使用者仍然使用此驗證方式,請先刪除或轉換這些使用者到其它登入類型。
auths.deletion_success=認證源刪除成功!
auths.login_source_exist=Login source '%s' already exists.
auths.login_source_exist=登錄源 '%s' 已存在。
config.server_config=服務器配置
config.app_name=應用名稱
@ -1048,6 +1051,7 @@ config.offline_mode=離線模式
config.disable_router_log=關閉路由日志
config.run_user=執行用戶
config.run_mode=執行模式
config.git_version=Git Version
config.repo_root_path=倉庫根目錄
config.static_file_root_path=靜態文件根目錄
config.log_file_root_path=日志文件根目錄

14
docker/build.sh

@ -10,23 +10,15 @@ export GO15VENDOREXPERIMENT=1
# Install build deps
apk --no-cache --no-progress add --virtual build-deps build-base linux-pam-dev go
# Install glide
git clone -b 0.10.2 https://github.com/Masterminds/glide ${GOPATH}/src/github.com/Masterminds/glide
cd ${GOPATH}/src/github.com/Masterminds/glide
make build
go install
# Build Gogs
mkdir -p ${GOPATH}/src/github.com/gogits/
ln -s /app/gogs/ ${GOPATH}/src/github.com/gogits/gogs
cd ${GOPATH}/src/github.com/gogits/gogs
glide install
go get -v -tags "sqlite cert pam" ./...
make build TAGS="sqlite cert pam"
# Cleanup GOPATH & vendoring dir
rm -r $GOPATH /app/gogs/vendor
# Cleanup GOPATH
rm -r $GOPATH
# Remove build deps
apk --no-progress del build-deps

154
glide.lock generated

@ -1,154 +0,0 @@
hash: 1d5fcf2a90f7621ecbc0b1abed548e11d13bda3fea49b4326c829a523268e5cf
updated: 2016-06-12T17:35:14.27036884+08:00
imports:
- name: github.com/bradfitz/gomemcache
version: 2fafb84a66c4911e11a8f50955b01e74fe3ab9c5
subpackages:
- memcache
- name: github.com/urfave/cli
version: 347a9884a87374d000eec7e6445a34487c1f4a2b
- name: github.com/go-macaron/binding
version: 48920167fa152d02f228cfbece7e0f1e452d200a
- name: github.com/go-macaron/cache
version: 56173531277692bc2925924d51fda1cd0a6b8178
subpackages:
- memcache
- redis
- name: github.com/go-macaron/captcha
version: 8aa5919789ab301e865595eb4b1114d6b9847deb
- name: github.com/go-macaron/csrf
version: 6a9a7df172cc1fcd81e4585f44b09200b6087cc0
- name: github.com/go-macaron/gzip
version: cad1c6580a07c56f5f6bc52d66002a05985c5854
- name: github.com/go-macaron/i18n
version: ef57533c3b0fc2d8581deda14937e52f11a203ab
- name: github.com/go-macaron/inject
version: d8a0b8677191f4380287cfebd08e462217bac7ad
- name: github.com/go-macaron/session
version: 66031fcb37a0fff002a1f028eb0b3a815c78306b
subpackages:
- redis
- name: github.com/go-macaron/toolbox
version: 82b511550b0aefc36b3a28062ad3a52e812bee38
- name: github.com/go-sql-driver/mysql
version: 2e00b5cd70399450106cec6431c2e2ce3cae5034
- name: github.com/go-xorm/builder
version: 867edcc549127f15667ece8876c3a60093c9634b
- name: github.com/go-xorm/core
version: 2fbe2c76c6781d9e1c0398fc25386426e611f975
- name: github.com/go-xorm/xorm
version: 445a934d32ed0934aedf0ef99b4b7c69b22f8e58
- name: github.com/gogits/chardet
version: 2404f777256163ea3eadb273dada5dcb037993c0
- name: github.com/gogits/cron
version: 2fc07a4c4f1e3c4d2301c5ed578d5e2c31c70421
- name: github.com/gogits/git-module
version: 172cbc21accbf0085a58fd0832f46a9f694130e8
- name: github.com/gogits/go-gogs-client
version: 98046bb98061fc6baa5bb86359af0b7c300d384a
- name: github.com/gogits/go-libravatar
version: cd1abbd55d09b793672732a7a1dfdaa12a40dfd0
- name: github.com/issue9/identicon
version: d36b54562f4cf70c83653e13dc95c220c79ef521
- name: github.com/jaytaylor/html2text
version: d16d4129aab4e6be4497f4db898965f09b93b565
- name: github.com/kardianos/minwinsvc
version: cad6b2b879b0970e4245a20ebf1a81a756e2bb70
- name: github.com/klauspost/compress
version: 461e8fd8397ae84a23f56e385801e4feda2048ce
subpackages:
- gzip
- flate
- name: github.com/klauspost/cpuid
version: 09cded8978dc9e80714c4d85b0322337b0a1e5e0
- name: github.com/klauspost/crc32
version: cb6bfca970f6908083f26f39a79009d608efd5cd
- name: github.com/lib/pq
version: 67c3f2a8884c9b1aac5503c8d42ae4f73a93511c
subpackages:
- oid
- name: github.com/mattn/go-sqlite3
version: ce9149a3c941c30de51a01dbc5bc414ddaa52927
- name: github.com/mcuadros/go-version
version: 257f7b9a7d87427c8d7f89469a5958d57f8abd7c
- name: github.com/microcosm-cc/bluemonday
version: e79763773ab6222ca1d5a7cbd9d62d83c1f77081
- name: github.com/msteinert/pam
version: 02ccfbfaf0cc627aa3aec8ef7ed5cfeec5b43f63
- name: github.com/nfnt/resize
version: 891127d8d1b52734debe1b3c3d7e747502b6c366
- name: github.com/russross/blackfriday
version: 5f33e7b7878355cd2b7e6b8eefc48a5472c69f70
- name: github.com/satori/go.uuid
version: b061729afc07e77a8aa4fad0a2fd840958f1942a
- name: github.com/sergi/go-diff
version: 24e2351369ec4949b2ed0dc5c477afdd4c4034e8
subpackages:
- diffmatchpatch
- name: github.com/shurcooL/sanitized_anchor_name
version: 1dba4b3954bc059efc3991ec364f9f9a35f597d2
- name: github.com/Unknwon/cae
version: c6aac99ea2cae2ebaf23f26f76b04fe3fcfc9f8c
subpackages:
- zip
- name: github.com/Unknwon/com
version: 28b053d5a2923b87ce8c5a08f3af779894a72758
- name: github.com/Unknwon/i18n
version: 39d6f2727e0698b1021ceb6a77c1801aa92e7d5d
- name: github.com/Unknwon/paginater
version: 701c23f468663c34d1b1768c3ae1bcc57e11c5b3
- name: golang.org/x/crypto
version: dc137beb6cce2043eb6b5f223ab8bf51c32459f4
subpackages:
- ssh
- curve25519
- ed25519
- ed25519/internal/edwards25519
- name: golang.org/x/net
version: f2499483f923065a842d38eb4c7f1927e6fc6e6d
subpackages:
- html
- html/charset
- html/atom
- name: golang.org/x/sys
version: d75a52659825e75fff6158388dddc6a5b04f9ba5
subpackages:
- windows/svc
- windows
- name: golang.org/x/text
version: ece019dcfd29abcf65d0d1dfe145e8faad097640
subpackages:
- transform
- language
- encoding
- encoding/charmap
- encoding/htmlindex
- internal/tag
- encoding/internal/identifier
- encoding/internal
- encoding/japanese
- encoding/korean
- encoding/simplifiedchinese
- encoding/traditionalchinese
- encoding/unicode
- internal/utf8internal
- runes
- name: gopkg.in/alexcesaro/quotedprintable.v3
version: 2caba252f4dc53eaf6b553000885530023f54623
- name: gopkg.in/asn1-ber.v1
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
- name: gopkg.in/bufio.v1
version: 567b2bfa514e796916c4747494d6ff5132a1dfce
- name: gopkg.in/editorconfig/editorconfig-core-go.v1
version: a872f05c2e34b37b567401384d202aff11ba06d4
- name: gopkg.in/gomail.v2
version: 81ebce5c23dfd25c6c67194b37d3dd3f338c98b1
- name: gopkg.in/ini.v1
version: e3c2d47c61e5333f9aa2974695dd94396eb69c75
- name: gopkg.in/ldap.v2
version: 8168ee085ee43257585e50c6441aadf54ecb2c9f
- name: gopkg.in/macaron.v1
version: 78521e4647ad5dcbabd5734d94582ca2373cbad8
- name: gopkg.in/redis.v2
version: e6179049628164864e6e84e973cfb56335748dea
devImports: []

59
glide.yaml

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

6
gogs.go

@ -16,7 +16,7 @@ import (
"github.com/gogits/gogs/modules/setting"
)
const APP_VER = "0.9.131.0201"
const APP_VER = "0.9.147.0214"
func init() {
setting.AppVer = APP_VER
@ -29,8 +29,8 @@ func main() {
app.Version = APP_VER
app.Commands = []cli.Command{
cmd.CmdWeb,
cmd.CmdServ,
cmd.CmdUpdate,
cmd.Serv,
cmd.CmdHook,
cmd.CmdDump,
cmd.CmdCert,
cmd.CmdAdmin,

2
models/access.go

@ -7,7 +7,7 @@ package models
import (
"fmt"
"github.com/gogits/gogs/modules/log"
log "gopkg.in/clog.v1"
)
type AccessMode int

4
models/action.go

@ -15,12 +15,12 @@ import (
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
log "gopkg.in/clog.v1"
"github.com/gogits/git-module"
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
@ -468,7 +468,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
}
if err = UpdateIssuesCommit(pusher, repo, opts.Commits.Commits); err != nil {
log.Error(4, "updateIssuesCommit: %v", err)
log.Error(4, "UpdateIssuesCommit: %v", err)
}
}

2
models/admin.go

@ -13,9 +13,9 @@ import (
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
log "gopkg.in/clog.v1"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)

175
models/attachment.go

@ -0,0 +1,175 @@
// Copyright 2017 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"fmt"
"io"
"mime/multipart"
"os"
"path"
"time"
"github.com/go-xorm/xorm"
gouuid "github.com/satori/go.uuid"
"github.com/gogits/gogs/modules/setting"
)
// Attachment represent a attachment of issue/comment/release.
type Attachment struct {
ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"uuid UNIQUE"`
IssueID int64 `xorm:"INDEX"`
CommentID int64
ReleaseID int64 `xorm:"INDEX"`
Name string
Created time.Time `xorm:"-"`
CreatedUnix int64
}
func (a *Attachment) BeforeInsert() {
a.CreatedUnix = time.Now().Unix()
}
func (a *Attachment) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
a.Created = time.Unix(a.CreatedUnix, 0).Local()
}
}
// AttachmentLocalPath returns where attachment is stored in local file system based on given UUID.
func AttachmentLocalPath(uuid string) string {
return path.Join(setting.AttachmentPath, uuid[0:1], uuid[1:2], uuid)
}
// LocalPath returns where attachment is stored in local file system.
func (attach *Attachment) LocalPath() string {
return AttachmentLocalPath(attach.UUID)
}
// NewAttachment creates a new attachment object.
func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment, err error) {
attach := &Attachment{
UUID: gouuid.NewV4().String(),
Name: name,
}
localPath := attach.LocalPath()
if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil {
return nil, fmt.Errorf("MkdirAll: %v", err)
}
fw, err := os.Create(localPath)
if err != nil {
return nil, fmt.Errorf("Create: %v", err)
}
defer fw.Close()
if _, err = fw.Write(buf); err != nil {
return nil, fmt.Errorf("Write: %v", err)
} else if _, err = io.Copy(fw, file); err != nil {
return nil, fmt.Errorf("Copy: %v", err)
}
if _, err := x.Insert(attach); err != nil {
return nil, err
}
return attach, nil
}
func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) {
attach := &Attachment{UUID: uuid}
has, err := x.Get(attach)
if err != nil {
return nil, err
} else if !has {
return nil, ErrAttachmentNotExist{0, uuid}
}
return attach, nil
}
func getAttachmentsByUUIDs(e Engine, uuids []string) ([]*Attachment, error) {
if len(uuids) == 0 {
return []*Attachment{}, nil
}
// Silently drop invalid uuids.
attachments := make([]*Attachment, 0, len(uuids))
return attachments, e.In("uuid", uuids).Find(&attachments)
}
// GetAttachmentByUUID returns attachment by given UUID.
func GetAttachmentByUUID(uuid string) (*Attachment, error) {
return getAttachmentByUUID(x, uuid)
}
func getAttachmentsByIssueID(e Engine, issueID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10)
return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments)
}
// GetAttachmentsByIssueID returns all attachments of an issue.
func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) {
return getAttachmentsByIssueID(x, issueID)
}
func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10)
return attachments, e.Where("comment_id=?", commentID).Find(&attachments)
}
// GetAttachmentsByCommentID returns all attachments if comment by given ID.
func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) {
return getAttachmentsByCommentID(x, commentID)
}
// DeleteAttachment deletes the given attachment and optionally the associated file.
func DeleteAttachment(a *Attachment, remove bool) error {
_, err := DeleteAttachments([]*Attachment{a}, remove)
return err
}
// DeleteAttachments deletes the given attachments and optionally the associated files.
func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) {
for i, a := range attachments {
if remove {
if err := os.Remove(a.LocalPath()); err != nil {
return i, err
}
}
if _, err := x.Delete(a); err != nil {
return i, err
}
}
return len(attachments), nil
}
// DeleteAttachmentsByIssue deletes all attachments associated with the given issue.
func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) {
attachments, err := GetAttachmentsByIssueID(issueId)
if err != nil {
return 0, err
}
return DeleteAttachments(attachments, remove)
}
// DeleteAttachmentsByComment deletes all attachments associated with the given comment.
func DeleteAttachmentsByComment(commentId int64, remove bool) (int, error) {
attachments, err := GetAttachmentsByCommentID(commentId)
if err != nil {
return 0, err
}
return DeleteAttachments(attachments, remove)
}

2
models/issue_comment.go → models/comment.go

@ -11,10 +11,10 @@ import (
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
log "gopkg.in/clog.v1"
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/markdown"
)

2
models/git_diff.go

@ -20,11 +20,11 @@ import (
"github.com/sergi/go-diff/diffmatchpatch"
"golang.org/x/net/html/charset"
"golang.org/x/text/transform"
log "gopkg.in/clog.v1"
"github.com/gogits/git-module"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/template/highlight"

530
models/issue.go

@ -7,20 +7,15 @@ package models
import (
"errors"
"fmt"
"io"
"mime/multipart"
"os"
"path"
"strings"
"time"
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
api "github.com/gogits/go-gogs-client"
gouuid "github.com/satori/go.uuid"
log "gopkg.in/clog.v1"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
@ -818,20 +813,20 @@ type IssuesOptions struct {
SortType string
}
// Issues returns a list of issues by given conditions.
func Issues(opts *IssuesOptions) ([]*Issue, error) {
// buildIssuesQuery returns nil if it foresees there won't be any value returned.
func buildIssuesQuery(opts *IssuesOptions) *xorm.Session {
sess := x.NewSession()
if opts.Page <= 0 {
opts.Page = 1
}
sess := x.Limit(setting.UI.IssuePagingNum, (opts.Page-1)*setting.UI.IssuePagingNum)
if opts.RepoID > 0 {
sess.Where("issue.repo_id=?", opts.RepoID).And("issue.is_closed=?", opts.IsClosed)
} else if opts.RepoIDs != nil {
// In case repository IDs are provided but actually no repository has issue.
if len(opts.RepoIDs) == 0 {
return make([]*Issue, 0), nil
return nil
}
sess.In("issue.repo_id", base.Int64sToStrings(opts.RepoIDs)).And("issue.is_closed=?", opts.IsClosed)
} else {
@ -882,6 +877,28 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
}
}
return sess
}
// IssuesCount returns the number of issues by given conditions.
func IssuesCount(opts *IssuesOptions) (int64, error) {
sess := buildIssuesQuery(opts)
if sess == nil {
return 0, nil
}
return sess.Count(&Issue{})
}
// Issues returns a list of issues by given conditions.
func Issues(opts *IssuesOptions) ([]*Issue, error) {
sess := buildIssuesQuery(opts)
if sess == nil {
return make([]*Issue, 0), nil
}
sess.Limit(setting.UI.IssuePagingNum, (opts.Page-1)*setting.UI.IssuePagingNum)
issues := make([]*Issue, 0, setting.UI.IssuePagingNum)
if err := sess.Find(&issues); err != nil {
return nil, fmt.Errorf("Find: %v", err)
@ -1332,494 +1349,3 @@ func updateIssueUsersByMentions(e Engine, issueID int64, uids []int64) error {
}
return nil
}
// _____ .__.__ __
// / \ |__| | ____ _______/ |_ ____ ____ ____
// / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \
// / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/
// \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ >
// \/ \/ \/ \/ \/
// Milestone represents a milestone of repository.
type Milestone struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX"`
Name string
Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-"`
IsClosed bool
NumIssues int
NumClosedIssues int
NumOpenIssues int `xorm:"-"`
Completeness int // Percentage(1-100).
IsOverDue bool `xorm:"-"`
DeadlineString string `xorm:"-"`
Deadline time.Time `xorm:"-"`
DeadlineUnix int64
ClosedDate time.Time `xorm:"-"`
ClosedDateUnix int64
}
func (m *Milestone) BeforeInsert() {
m.DeadlineUnix = m.Deadline.Unix()
}
func (m *Milestone) BeforeUpdate() {
if m.NumIssues > 0 {
m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
} else {
m.Completeness = 0
}
m.DeadlineUnix = m.Deadline.Unix()
m.ClosedDateUnix = m.ClosedDate.Unix()
}
func (m *Milestone) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "num_closed_issues":
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
case "deadline_unix":
m.Deadline = time.Unix(m.DeadlineUnix, 0).Local()
if m.Deadline.Year() == 9999 {
return
}
m.DeadlineString = m.Deadline.Format("2006-01-02")
if time.Now().Local().After(m.Deadline) {
m.IsOverDue = true
}
case "closed_date_unix":
m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local()
}
}
// State returns string representation of milestone status.
func (m *Milestone) State() api.StateType {
if m.IsClosed {
return api.STATE_CLOSED
}
return api.STATE_OPEN
}
func (m *Milestone) APIFormat() *api.Milestone {
apiMilestone := &api.Milestone{
ID: m.ID,
State: m.State(),
Title: m.Name,
Description: m.Content,
OpenIssues: m.NumOpenIssues,
ClosedIssues: m.NumClosedIssues,
}
if m.IsClosed {
apiMilestone.Closed = &m.ClosedDate
}
if m.Deadline.Year() < 9999 {
apiMilestone.Deadline = &m.Deadline
}
return apiMilestone
}
// NewMilestone creates new milestone of repository.
func NewMilestone(m *Milestone) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Insert(m); err != nil {
return err
}
if _, err = sess.Exec("UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil {
return err
}
return sess.Commit()
}
func getMilestoneByRepoID(e Engine, repoID, id int64) (*Milestone, error) {
m := &Milestone{
ID: id,
RepoID: repoID,
}
has, err := e.Get(m)
if err != nil {
return nil, err
} else if !has {
return nil, ErrMilestoneNotExist{id, repoID}
}
return m, nil
}
// GetWebhookByRepoID returns the milestone in a repository.
func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) {
return getMilestoneByRepoID(x, repoID, id)
}
// GetMilestonesByRepoID returns all milestones of a repository.
func GetMilestonesByRepoID(repoID int64) ([]*Milestone, error) {
miles := make([]*Milestone, 0, 10)
return miles, x.Where("repo_id = ?", repoID).Find(&miles)
}
// GetMilestones returns a list of milestones of given repository and status.
func GetMilestones(repoID int64, page int, isClosed bool) ([]*Milestone, error) {
miles := make([]*Milestone, 0, setting.UI.IssuePagingNum)
sess := x.Where("repo_id = ? AND is_closed = ?", repoID, isClosed)
if page > 0 {
sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum)
}
return miles, sess.Find(&miles)
}
func updateMilestone(e Engine, m *Milestone) error {
_, err := e.Id(m.ID).AllCols().Update(m)
return err
}
// UpdateMilestone updates information of given milestone.
func UpdateMilestone(m *Milestone) error {
return updateMilestone(x, m)
}
func countRepoMilestones(e Engine, repoID int64) int64 {
count, _ := e.Where("repo_id=?", repoID).Count(new(Milestone))
return count
}
// CountRepoMilestones returns number of milestones in given repository.
func CountRepoMilestones(repoID int64) int64 {
return countRepoMilestones(x, repoID)
}
func countRepoClosedMilestones(e Engine, repoID int64) int64 {
closed, _ := e.Where("repo_id=? AND is_closed=?", repoID, true).Count(new(Milestone))
return closed
}
// CountRepoClosedMilestones returns number of closed milestones in given repository.
func CountRepoClosedMilestones(repoID int64) int64 {
return countRepoClosedMilestones(x, repoID)
}
// MilestoneStats returns number of open and closed milestones of given repository.
func MilestoneStats(repoID int64) (open int64, closed int64) {
open, _ = x.Where("repo_id=? AND is_closed=?", repoID, false).Count(new(Milestone))
return open, CountRepoClosedMilestones(repoID)
}
// ChangeMilestoneStatus changes the milestone open/closed status.
func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
repo, err := GetRepositoryByID(m.RepoID)
if err != nil {
return err
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
m.IsClosed = isClosed
if err = updateMilestone(sess, m); err != nil {
return err
}
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID))
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID))
if _, err = sess.Id(repo.ID).AllCols().Update(repo); err != nil {
return err
}
return sess.Commit()
}
func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error {
if issue.MilestoneID == 0 {
return nil
}
m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
if err != nil {
return err
}
if issue.IsClosed {
m.NumOpenIssues--
m.NumClosedIssues++
} else {
m.NumOpenIssues++
m.NumClosedIssues--
}
return updateMilestone(e, m)
}
// ChangeMilestoneIssueStats updates the open/closed issues counter and progress
// for the milestone associated with the given issue.
func ChangeMilestoneIssueStats(issue *Issue) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if err = changeMilestoneIssueStats(sess, issue); err != nil {
return err
}
return sess.Commit()
}
func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64) error {
if oldMilestoneID > 0 {
m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID)
if err != nil {
return err
}
m.NumIssues--
if issue.IsClosed {
m.NumClosedIssues--
}
if err = updateMilestone(e, m); err != nil {
return err
} else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?", issue.ID); err != nil {
return err
}
}
if issue.MilestoneID > 0 {
m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
if err != nil {
return err
}
m.NumIssues++
if issue.IsClosed {
m.NumClosedIssues++
}
if err = updateMilestone(e, m); err != nil {
return err
} else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?", m.ID, issue.ID); err != nil {
return err
}
}
return updateIssue(e, issue)
}
// ChangeMilestoneAssign changes assignment of milestone for issue.
func ChangeMilestoneAssign(issue *Issue, oldMilestoneID int64) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = changeMilestoneAssign(sess, issue, oldMilestoneID); err != nil {
return err
}
return sess.Commit()
}
// DeleteMilestoneOfRepoByID deletes a milestone from a repository.
func DeleteMilestoneOfRepoByID(repoID, id int64) error {
m, err := GetMilestoneByRepoID(repoID, id)
if err != nil {
if IsErrMilestoneNotExist(err) {
return nil
}
return err
}
repo, err := GetRepositoryByID(m.RepoID)
if err != nil {
return err
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Id(m.ID).Delete(new(Milestone)); err != nil {
return err
}
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID))
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID))
if _, err = sess.Id(repo.ID).AllCols().Update(repo); err != nil {
return err
}
if _, err = sess.Exec("UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
return err
} else if _, err = sess.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
return err
}
return sess.Commit()
}
// Attachment represent a attachment of issue/comment/release.
type Attachment struct {
ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"uuid UNIQUE"`
IssueID int64 `xorm:"INDEX"`
CommentID int64
ReleaseID int64 `xorm:"INDEX"`
Name string
Created time.Time `xorm:"-"`
CreatedUnix int64
}
func (a *Attachment) BeforeInsert() {
a.CreatedUnix = time.Now().Unix()
}
func (a *Attachment) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
a.Created = time.Unix(a.CreatedUnix, 0).Local()
}
}
// AttachmentLocalPath returns where attachment is stored in local file system based on given UUID.
func AttachmentLocalPath(uuid string) string {
return path.Join(setting.AttachmentPath, uuid[0:1], uuid[1:2], uuid)
}
// LocalPath returns where attachment is stored in local file system.
func (attach *Attachment) LocalPath() string {
return AttachmentLocalPath(attach.UUID)
}
// NewAttachment creates a new attachment object.
func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment, err error) {
attach := &Attachment{
UUID: gouuid.NewV4().String(),
Name: name,
}
localPath := attach.LocalPath()
if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil {
return nil, fmt.Errorf("MkdirAll: %v", err)
}
fw, err := os.Create(localPath)
if err != nil {
return nil, fmt.Errorf("Create: %v", err)
}
defer fw.Close()
if _, err = fw.Write(buf); err != nil {
return nil, fmt.Errorf("Write: %v", err)
} else if _, err = io.Copy(fw, file); err != nil {
return nil, fmt.Errorf("Copy: %v", err)
}
if _, err := x.Insert(attach); err != nil {
return nil, err
}
return attach, nil
}
func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) {
attach := &Attachment{UUID: uuid}
has, err := x.Get(attach)
if err != nil {
return nil, err
} else if !has {
return nil, ErrAttachmentNotExist{0, uuid}
}
return attach, nil
}
func getAttachmentsByUUIDs(e Engine, uuids []string) ([]*Attachment, error) {
if len(uuids) == 0 {
return []*Attachment{}, nil
}
// Silently drop invalid uuids.
attachments := make([]*Attachment, 0, len(uuids))
return attachments, e.In("uuid", uuids).Find(&attachments)
}
// GetAttachmentByUUID returns attachment by given UUID.
func GetAttachmentByUUID(uuid string) (*Attachment, error) {
return getAttachmentByUUID(x, uuid)
}
func getAttachmentsByIssueID(e Engine, issueID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10)
return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments)
}
// GetAttachmentsByIssueID returns all attachments of an issue.
func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) {
return getAttachmentsByIssueID(x, issueID)
}
func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10)
return attachments, e.Where("comment_id=?", commentID).Find(&attachments)
}
// GetAttachmentsByCommentID returns all attachments if comment by given ID.
func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) {
return getAttachmentsByCommentID(x, commentID)
}
// DeleteAttachment deletes the given attachment and optionally the associated file.
func DeleteAttachment(a *Attachment, remove bool) error {
_, err := DeleteAttachments([]*Attachment{a}, remove)
return err
}
// DeleteAttachments deletes the given attachments and optionally the associated files.
func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) {
for i, a := range attachments {
if remove {
if err := os.Remove(a.LocalPath()); err != nil {
return i, err
}
}
if _, err := x.Delete(a); err != nil {
return i, err
}
}
return len(attachments), nil
}
// DeleteAttachmentsByIssue deletes all attachments associated with the given issue.
func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) {
attachments, err := GetAttachmentsByIssueID(issueId)
if err != nil {
return 0, err
}
return DeleteAttachments(attachments, remove)
}
// DeleteAttachmentsByComment deletes all attachments associated with the given comment.
func DeleteAttachmentsByComment(commentId int64, remove bool) (int, error) {
attachments, err := GetAttachmentsByCommentID(commentId)
if err != nil {
return 0, err
}
return DeleteAttachments(attachments, remove)
}

2
models/issue_mail.go

@ -8,8 +8,8 @@ import (
"fmt"
"github.com/Unknwon/com"
log "gopkg.in/clog.v1"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/mailer"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/setting"

2
models/login_source.go

@ -18,10 +18,10 @@ import (
"github.com/go-macaron/binding"
"github.com/go-xorm/core"
"github.com/go-xorm/xorm"
log "gopkg.in/clog.v1"
"github.com/gogits/gogs/modules/auth/ldap"
"github.com/gogits/gogs/modules/auth/pam"
"github.com/gogits/gogs/modules/log"
)
type LoginType int

5
models/migrations/migrations.go

@ -18,10 +18,10 @@ import (
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
gouuid "github.com/satori/go.uuid"
log "gopkg.in/clog.v1"
"gopkg.in/ini.v1"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
@ -72,6 +72,8 @@ var migrations = []Migration{
// v13 -> v14:v0.9.87
NewMigration("set comment updated with created", setCommentUpdatedWithCreated),
// v14 -> v15:v0.9.147
NewMigration("generate and migrate Git hooks", generateAndMigrateGitHooks),
}
// Migrate database to current version
@ -87,6 +89,7 @@ func Migrate(x *xorm.Engine) error {
} else if !has {
// If the version record does not exist we think
// it is a fresh installation and we can skip all migrations.
currentVersion.ID = 0
currentVersion.Version = int64(_MIN_DB_VER + len(migrations))
if _, err = x.InsertOne(currentVersion); err != nil {

91
models/migrations/v15.go

@ -0,0 +1,91 @@
// Copyright 2017 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
log "gopkg.in/clog.v1"
"github.com/gogits/gogs/modules/setting"
)
func generateAndMigrateGitHooks(x *xorm.Engine) (err error) {
type Repository struct {
ID int64
OwnerID int64
Name string
}
type User struct {
ID int64
Name string
}
var (
hookNames = []string{"pre-receive", "update", "post-receive"}
hookTpls = []string{
fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' update $1 $2 $3\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
fmt.Sprintf("#!/usr/bin/env %s\n\"%s\" hook --config='%s' post-receive\n", setting.ScriptType, setting.AppPath, setting.CustomConf),
}
)
// Cleanup old update.log and http.log files.
filepath.Walk(setting.LogRootPath, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() &&
(strings.HasPrefix(filepath.Base(path), "update.log") ||
strings.HasPrefix(filepath.Base(path), "http.log")) {
os.Remove(path)
}
return nil
})
return x.Where("id > 0").Iterate(new(Repository),
func(idx int, bean interface{}) error {
repo := bean.(*Repository)
if repo.Name == "." || repo.Name == ".." {
return nil
}
user := new(User)
has, err := x.Where("id = ?", repo.OwnerID).Get(user)
if err != nil {
return fmt.Errorf("query owner of repository [repo_id: %d, owner_id: %d]: %v", repo.ID, repo.OwnerID, err)
} else if !has {
return nil
}
repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(user.Name), strings.ToLower(repo.Name)) + ".git"
log.Trace("[%04d]: %s", idx, repoPath)
hookDir := filepath.Join(repoPath, "hooks")
customHookDir := filepath.Join(repoPath, "custom_hooks")
for i, hookName := range hookNames {
oldHookPath := filepath.Join(hookDir, hookName)
newHookPath := filepath.Join(customHookDir, hookName)
// Gogs didn't allow user to set custom update hook thus no migration for it.
// In case user runs this migration multiple times, and custom hook exists,
// we assume it's been migrated already.
if hookName != "update" && com.IsFile(oldHookPath) && !com.IsExist(customHookDir) {
os.MkdirAll(customHookDir, os.ModePerm)
if err = os.Rename(oldHookPath, newHookPath); err != nil {
return fmt.Errorf("move hook file to custom directory '%s' -> '%s': %v", oldHookPath, newHookPath, err)
}
}
if err = ioutil.WriteFile(oldHookPath, []byte(hookTpls[i]), 0777); err != nil {
return fmt.Errorf("write hook file '%s': %v", oldHookPath, err)
}
}
return nil
})
}

349
models/milestone.go

@ -0,0 +1,349 @@
// Copyright 2017 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"time"
"github.com/go-xorm/xorm"
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/modules/setting"
)
// Milestone represents a milestone of repository.
type Milestone struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX"`
Name string
Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-"`
IsClosed bool
NumIssues int
NumClosedIssues int
NumOpenIssues int `xorm:"-"`
Completeness int // Percentage(1-100).
IsOverDue bool `xorm:"-"`
DeadlineString string `xorm:"-"`
Deadline time.Time `xorm:"-"`
DeadlineUnix int64
ClosedDate time.Time `xorm:"-"`
ClosedDateUnix int64
}
func (m *Milestone) BeforeInsert() {
m.DeadlineUnix = m.Deadline.Unix()
}
func (m *Milestone) BeforeUpdate() {
if m.NumIssues > 0 {
m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
} else {
m.Completeness = 0
}
m.DeadlineUnix = m.Deadline.Unix()
m.ClosedDateUnix = m.ClosedDate.Unix()
}
func (m *Milestone) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "num_closed_issues":
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
case "deadline_unix":
m.Deadline = time.Unix(m.DeadlineUnix, 0).Local()
if m.Deadline.Year() == 9999 {
return
}
m.DeadlineString = m.Deadline.Format("2006-01-02")
if time.Now().Local().After(m.Deadline) {
m.IsOverDue = true
}
case "closed_date_unix":
m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local()
}
}
// State returns string representation of milestone status.
func (m *Milestone) State() api.StateType {
if m.IsClosed {
return api.STATE_CLOSED
}
return api.STATE_OPEN
}
func (m *Milestone) ChangeStatus(isClosed bool) error {
return ChangeMilestoneStatus(m, isClosed)
}
func (m *Milestone) APIFormat() *api.Milestone {
apiMilestone := &api.Milestone{
ID: m.ID,
State: m.State(),
Title: m.Name,
Description: m.Content,
OpenIssues: m.NumOpenIssues,
ClosedIssues: m.NumClosedIssues,
}
if m.IsClosed {
apiMilestone.Closed = &m.ClosedDate
}
if m.Deadline.Year() < 9999 {
apiMilestone.Deadline = &m.Deadline
}
return apiMilestone
}
// NewMilestone creates new milestone of repository.
func NewMilestone(m *Milestone) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Insert(m); err != nil {
return err
}
if _, err = sess.Exec("UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil {
return err
}
return sess.Commit()
}
func getMilestoneByRepoID(e Engine, repoID, id int64) (*Milestone, error) {
m := &Milestone{
ID: id,
RepoID: repoID,
}
has, err := e.Get(m)
if err != nil {
return nil, err
} else if !has {
return nil, ErrMilestoneNotExist{id, repoID}
}
return m, nil
}
// GetWebhookByRepoID returns the milestone in a repository.
func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) {
return getMilestoneByRepoID(x, repoID, id)
}
// GetMilestonesByRepoID returns all milestones of a repository.
func GetMilestonesByRepoID(repoID int64) ([]*Milestone, error) {
miles := make([]*Milestone, 0, 10)
return miles, x.Where("repo_id = ?", repoID).Find(&miles)
}
// GetMilestones returns a list of milestones of given repository and status.
func GetMilestones(repoID int64, page int, isClosed bool) ([]*Milestone, error) {
miles := make([]*Milestone, 0, setting.UI.IssuePagingNum)
sess := x.Where("repo_id = ? AND is_closed = ?", repoID, isClosed)
if page > 0 {
sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum)
}
return miles, sess.Find(&miles)
}
func updateMilestone(e Engine, m *Milestone) error {
_, err := e.Id(m.ID).AllCols().Update(m)
return err
}
// UpdateMilestone updates information of given milestone.
func UpdateMilestone(m *Milestone) error {
return updateMilestone(x, m)
}
func countRepoMilestones(e Engine, repoID int64) int64 {
count, _ := e.Where("repo_id=?", repoID).Count(new(Milestone))
return count
}
// CountRepoMilestones returns number of milestones in given repository.
func CountRepoMilestones(repoID int64) int64 {
return countRepoMilestones(x, repoID)
}
func countRepoClosedMilestones(e Engine, repoID int64) int64 {
closed, _ := e.Where("repo_id=? AND is_closed=?", repoID, true).Count(new(Milestone))
return closed
}
// CountRepoClosedMilestones returns number of closed milestones in given repository.
func CountRepoClosedMilestones(repoID int64) int64 {
return countRepoClosedMilestones(x, repoID)
}
// MilestoneStats returns number of open and closed milestones of given repository.
func MilestoneStats(repoID int64) (open int64, closed int64) {
open, _ = x.Where("repo_id=? AND is_closed=?", repoID, false).Count(new(Milestone))
return open, CountRepoClosedMilestones(repoID)
}
// ChangeMilestoneStatus changes the milestone open/closed status.
// If milestone passes with changed values, those values will be
// updated to database as well.
func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
repo, err := GetRepositoryByID(m.RepoID)
if err != nil {
return err
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
m.IsClosed = isClosed
if err = updateMilestone(sess, m); err != nil {
return err
}
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID))
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID))
if _, err = sess.Id(repo.ID).AllCols().Update(repo); err != nil {
return err
}
return sess.Commit()
}
func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error {
if issue.MilestoneID == 0 {
return nil
}
m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
if err != nil {
return err
}
if issue.IsClosed {
m.NumOpenIssues--
m.NumClosedIssues++
} else {
m.NumOpenIssues++
m.NumClosedIssues--
}
return updateMilestone(e, m)
}
// ChangeMilestoneIssueStats updates the open/closed issues counter and progress
// for the milestone associated with the given issue.
func ChangeMilestoneIssueStats(issue *Issue) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if err = changeMilestoneIssueStats(sess, issue); err != nil {
return err
}
return sess.Commit()
}
func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64) error {
if oldMilestoneID > 0 {
m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID)
if err != nil {
return err
}
m.NumIssues--
if issue.IsClosed {
m.NumClosedIssues--
}
if err = updateMilestone(e, m); err != nil {
return err
} else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?", issue.ID); err != nil {
return err
}
}
if issue.MilestoneID > 0 {
m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
if err != nil {
return err
}
m.NumIssues++
if issue.IsClosed {
m.NumClosedIssues++
}
if err = updateMilestone(e, m); err != nil {
return err
} else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?", m.ID, issue.ID); err != nil {
return err
}
}
return updateIssue(e, issue)
}
// ChangeMilestoneAssign changes assignment of milestone for issue.
func ChangeMilestoneAssign(issue *Issue, oldMilestoneID int64) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = changeMilestoneAssign(sess, issue, oldMilestoneID); err != nil {
return err
}
return sess.Commit()
}
// DeleteMilestoneOfRepoByID deletes a milestone from a repository.
func DeleteMilestoneOfRepoByID(repoID, id int64) error {
m, err := GetMilestoneByRepoID(repoID, id)
if err != nil {
if IsErrMilestoneNotExist(err) {
return nil
}
return err
}
repo, err := GetRepositoryByID(m.RepoID)
if err != nil {
return err
}
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Id(m.ID).Delete(new(Milestone)); err != nil {
return err
}
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID))
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID))
if _, err = sess.Id(repo.ID).AllCols().Update(repo); err != nil {
return err
}
if _, err = sess.Exec("UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
return err
} else if _, err = sess.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
return err
}
return sess.Commit()
}

2
models/repo_mirror.go → models/mirror.go

@ -11,11 +11,11 @@ import (
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
log "gopkg.in/clog.v1"
"gopkg.in/ini.v1"
"github.com/gogits/git-module"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/sync"

38
models/models.go

@ -13,6 +13,7 @@ import (
"path"
"strings"
_ "github.com/denisenkom/go-mssqldb"
_ "github.com/go-sql-driver/mysql"
"github.com/go-xorm/core"
"github.com/go-xorm/xorm"
@ -55,7 +56,6 @@ var (
}
EnableSQLite3 bool
EnableTiDB bool
)
func init() {
@ -86,8 +86,8 @@ func LoadConfigs() {
setting.UseMySQL = true
case "postgres":
setting.UsePostgreSQL = true
case "tidb":
setting.UseTiDB = true
case "mssql":
setting.UseMSSQL = true
}
DbCfg.Host = sec.Key("HOST").String()
DbCfg.Name = sec.Key("NAME").String()
@ -114,6 +114,20 @@ func parsePostgreSQLHostPort(info string) (string, string) {
return host, port
}
func parseMSSQLHostPort(info string) (string, string) {
host, port := "127.0.0.1", "1433"
if strings.Contains(info, ":") {
host = strings.Split(info, ":")[0]
port = strings.Split(info, ":")[1]
} else if strings.Contains(info, ",") {
host = strings.Split(info, ",")[0]
port = strings.TrimSpace(strings.Split(info, ",")[1])
} else if len(info) > 0 {
host = info
}
return host, port
}
func getEngine() (*xorm.Engine, error) {
connStr := ""
var Param string = "?"
@ -138,6 +152,9 @@ func getEngine() (*xorm.Engine, error) {
connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s",
url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), host, port, DbCfg.Name, Param, DbCfg.SSLMode)
}
case "mssql":
host, port := parseMSSQLHostPort(DbCfg.Host)
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, DbCfg.Name, DbCfg.User, DbCfg.Passwd)
case "sqlite3":
if !EnableSQLite3 {
return nil, errors.New("This binary version does not build support for SQLite3.")
@ -146,14 +163,6 @@ func getEngine() (*xorm.Engine, error) {
return nil, fmt.Errorf("Fail to create directories: %v", err)
}
connStr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
case "tidb":
if !EnableTiDB {
return nil, errors.New("This binary version does not build support for TiDB.")
}
if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
return nil, fmt.Errorf("Fail to create directories: %v", err)
}
connStr = "goleveldb://" + DbCfg.Path
default:
return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
}
@ -187,7 +196,12 @@ func SetEngine() (err error) {
if err != nil {
return fmt.Errorf("Fail to create xorm.log: %v", err)
}
x.SetLogger(xorm.NewSimpleLogger(f))
if setting.ProdMode {
x.SetLogger(xorm.NewSimpleLogger3(f, xorm.DEFAULT_LOG_PREFIX, xorm.DEFAULT_LOG_FLAG, core.LOG_WARNING))
} else {
x.SetLogger(xorm.NewSimpleLogger(f))
}
x.ShowSQL(true)
return nil
}

18
models/models_tidb.go

@ -1,18 +0,0 @@
// +build tidb go1.4.2
// Copyright 2015 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
_ "github.com/go-xorm/tidb"
"github.com/ngaut/log"
_ "github.com/pingcap/tidb"
)
func init() {
EnableTiDB = true
log.SetLevelByString("error")
}

2
models/org.go

@ -11,9 +11,9 @@ import (
"strings"
"github.com/go-xorm/xorm"
log "gopkg.in/clog.v1"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
)
var (

25
models/org_team.go

@ -8,6 +8,8 @@ import (
"errors"
"fmt"
"strings"
"github.com/go-xorm/xorm"
)
const OWNER_TEAM = "Owners"
@ -26,6 +28,16 @@ type Team struct {
NumMembers int
}
func (t *Team) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "num_repos":
// LEGACY [0.11]: this is backward compatibility bug fix for https://github.com/gogits/gogs/issues/3671
if t.NumRepos < 0 {
t.NumRepos = 0
}
}
}
// IsOwnerTeam returns true if team is owner team.
func (t *Team) IsOwnerTeam() bool {
return t.Name == OWNER_TEAM
@ -194,11 +206,24 @@ func (t *Team) RemoveRepository(repoID int64) error {
return sess.Commit()
}
var reservedTeamNames = []string{"new"}
// IsUsableTeamName return an error if given name is a reserved name or pattern.
func IsUsableTeamName(name string) error {
return isUsableName(reservedTeamNames, nil, name)
}
// NewTeam creates a record of new team.
// It's caller's responsibility to assign organization ID.
func NewTeam(t *Team) error {
if len(t.Name) == 0 {
return errors.New("empty team name")
} else if t.OrgID == 0 {
return errors.New("OrgID is not assigned")
}
if err := IsUsableTeamName(t.Name); err != nil {
return err
}
has, err := x.Id(t.OrgID).Get(new(User))

18
models/pull.go

@ -13,11 +13,11 @@ import (
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
log "gopkg.in/clog.v1"
"github.com/gogits/git-module"
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/sync"
@ -364,7 +364,7 @@ func (pr *PullRequest) testPatch() (err error) {
log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
if err := pr.BaseRepo.UpdateLocalCopyBranch(pr.BaseBranch); err != nil {
return fmt.Errorf("UpdateLocalCopy: %v", err)
return fmt.Errorf("UpdateLocalCopy [%d]: %v", pr.BaseRepoID, err)
}
pr.Status = PULL_REQUEST_STATUS_CHECKING
@ -621,18 +621,18 @@ func (pr *PullRequest) AddToTaskQueue() {
type PullRequestList []*PullRequest
func (prs PullRequestList) loadAttributes(e Engine) error {
func (prs PullRequestList) loadAttributes(e Engine) (err error) {
if len(prs) == 0 {
return nil
}
// Load issues.
// Load issues
issueIDs := make([]int64, 0, len(prs))
for i := range prs {
issueIDs = append(issueIDs, prs[i].IssueID)
}
issues := make([]*Issue, 0, len(issueIDs))
if err := e.Where("id > 0").In("id", issueIDs).Find(&issues); err != nil {
if err = e.Where("id > 0").In("id", issueIDs).Find(&issues); err != nil {
return fmt.Errorf("find issues: %v", err)
}
@ -643,6 +643,14 @@ func (prs PullRequestList) loadAttributes(e Engine) error {
for i := range prs {
prs[i].Issue = set[prs[i].IssueID]
}
// Load attributes
for i := range prs {
if err = prs[i].loadAttributes(e); err != nil {
return fmt.Errorf("loadAttributes [%d]: %v", prs[i].ID, err)
}
}
return nil
}

80
models/repo.go

@ -23,23 +23,19 @@ import (
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
"github.com/mcuadros/go-version"
log "gopkg.in/clog.v1"
"gopkg.in/ini.v1"
git "github.com/gogits/git-module"
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/modules/bindata"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/sync"
)
const (
_TPL_UPDATE_HOOK = "#!/usr/bin/env %s\n%s update $1 $2 $3 --config='%s'\n"
)
var repoWorkingPool = sync.NewExclusivePool()
var (
@ -115,15 +111,17 @@ func NewRepoContext() {
}
// Check Git version.
gitVer, err := git.BinVersion()
var err error
setting.Git.Version, err = git.BinVersion()
if err != nil {
log.Fatal(4, "Fail to get Git version: %v", err)
}
log.Info("Git Version: %s", gitVer)
if version.Compare("1.7.1", gitVer, ">") {
log.Info("Git Version: %s", setting.Git.Version)
if version.Compare("1.7.1", setting.Git.Version, ">") {
log.Fatal(4, "Gogs requires Git version greater or equal to 1.7.1")
}
git.HookDir = "custom_hooks"
// Git requires setting user.name and user.email in order to commit changes.
for configKey, defaultValue := range map[string]string{"user.name": "Gogs", "user.email": "gogs@fake.local"} {
@ -467,7 +465,7 @@ func (repo *Repository) DescriptionHtml() template.HTML {
}
func (repo *Repository) LocalCopyPath() string {
return path.Join(setting.AppDataPath, "tmp/local-rpeo", com.ToStr(repo.ID))
return path.Join(setting.AppDataPath, "tmp/local-repo", com.ToStr(repo.ID))
}
// UpdateLocalCopy pulls latest changes of given branch from repoPath to localPath.
@ -653,7 +651,8 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
Quiet: true,
Timeout: migrateTimeout,
}); err != nil {
log.Info("Clone wiki: %v", err)
log.Trace("Fail to clone wiki: %v", err)
os.RemoveAll(wikiPath)
}
}
@ -713,20 +712,33 @@ func cleanUpMigrateGitConfig(configPath string) error {
return nil
}
func createUpdateHook(repoPath string) error {
return git.SetUpdateHook(repoPath,
fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf))
var hooksTpls = map[string]string{
"pre-receive": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' pre-receive\n",
"update": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' update $1 $2 $3\n",
"post-receive": "#!/usr/bin/env %s\n\"%s\" hook --config='%s' post-receive\n",
}
func createDelegateHooks(repoPath string) (err error) {
for _, name := range git.HookNames {
hookPath := filepath.Join(repoPath, "hooks", name)
if err = ioutil.WriteFile(hookPath,
[]byte(fmt.Sprintf(hooksTpls[name], setting.ScriptType, setting.AppPath, setting.CustomConf)),
os.ModePerm); err != nil {
return fmt.Errorf("create delegate hook '%s': %v", hookPath, err)
}
}
return nil
}
// Finish migrating repository and/or wiki with things that don't need to be done for mirrors.
func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
repoPath := repo.RepoPath()
if err := createUpdateHook(repoPath); err != nil {
return repo, fmt.Errorf("createUpdateHook: %v", err)
if err := createDelegateHooks(repoPath); err != nil {
return repo, fmt.Errorf("createDelegateHooks: %v", err)
}
if repo.HasWiki() {
if err := createUpdateHook(repo.WikiPath()); err != nil {
return repo, fmt.Errorf("createUpdateHook (wiki): %v", err)
if err := createDelegateHooks(repo.WikiPath()); err != nil {
return repo, fmt.Errorf("createDelegateHooks.(wiki): %v", err)
}
}
@ -735,7 +747,7 @@ func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
}
if repo.HasWiki() {
if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil {
return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err)
return repo, fmt.Errorf("cleanUpMigrateGitConfig.(wiki): %v", err)
}
}
@ -860,8 +872,8 @@ func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts C
// Init bare new repository.
if err = git.InitRepository(repoPath, true); err != nil {
return fmt.Errorf("InitRepository: %v", err)
} else if err = createUpdateHook(repoPath); err != nil {
return fmt.Errorf("createUpdateHook: %v", err)
} else if err = createDelegateHooks(repoPath); err != nil {
return fmt.Errorf("createDelegateHooks: %v", err)
}
tmpDir := filepath.Join(os.TempDir(), "gogs-"+repo.Name+"-"+com.ToStr(time.Now().Nanosecond()))
@ -904,6 +916,7 @@ var (
reservedRepoPatterns = []string{"*.git", "*.wiki"}
)
// IsUsableRepoName return an error if given name is a reserved name or pattern.
func IsUsableRepoName(name string) error {
return isUsableName(reservedRepoNames, reservedRepoPatterns, name)
}
@ -1228,7 +1241,7 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error)
return fmt.Errorf("GetRepositoryByName: %v", err)
}
// Change repository directory name.
// Change repository directory name
if err = os.Rename(repo.RepoPath(), RepoPath(u.Name, newRepoName)); err != nil {
return fmt.Errorf("rename repository directory: %v", err)
}
@ -1241,6 +1254,7 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error)
RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath())
}
RemoveAllWithNotice("Delete repository local copy", repo.LocalCopyPath())
return nil
}
@ -1413,6 +1427,10 @@ func DeleteRepository(uid, repoID int64) error {
return err
}
if err = sess.Commit(); err != nil {
return fmt.Errorf("Commit: %v", err)
}
// Remove repository files.
repoPath := repo.repoPath(sess)
RemoveAllWithNotice("Delete repository files", repoPath)
@ -1424,10 +1442,6 @@ func DeleteRepository(uid, repoID int64) error {
RemoveAllWithNotice("Delete attachment", attachmentPaths[i])
}
if err = sess.Commit(); err != nil {
return fmt.Errorf("Commit: %v", err)
}
if repo.NumForks > 0 {
if _, err = x.Exec("UPDATE `repository` SET fork_id=0,is_fork=? WHERE fork_id=?", false, repo.ID); err != nil {
log.Error(4, "reset 'fork_id' and 'is_fork': %v", err)
@ -1644,12 +1658,12 @@ func ReinitMissingRepositories() error {
return nil
}
// RewriteRepositoryUpdateHook rewrites all repositories' update hook.
func RewriteRepositoryUpdateHook() error {
// SyncRepositoryHooks rewrites all repositories' pre-receive, update and post-receive hooks
// to make sure the binary and custom conf path are up-to-date.
func SyncRepositoryHooks() error {
return x.Where("id > 0").Iterate(new(Repository),
func(idx int, bean interface{}) error {
repo := bean.(*Repository)
return createUpdateHook(repo.RepoPath())
return createDelegateHooks(bean.(*Repository).RepoPath())
})
}
@ -2094,21 +2108,21 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
repoPath := RepoPath(u.Name, repo.Name)
_, stderr, err := process.ExecTimeout(10*time.Minute,
fmt.Sprintf("ForkRepository(git clone): %s/%s", u.Name, repo.Name),
fmt.Sprintf("ForkRepository 'git clone': %s/%s", u.Name, repo.Name),
"git", "clone", "--bare", oldRepo.RepoPath(), repoPath)
if err != nil {
return nil, fmt.Errorf("git clone: %v", stderr)
}
_, stderr, err = process.ExecDir(-1,
repoPath, fmt.Sprintf("ForkRepository(git update-server-info): %s", repoPath),
repoPath, fmt.Sprintf("ForkRepository 'git update-server-info': %s", repoPath),
"git", "update-server-info")
if err != nil {
return nil, fmt.Errorf("git update-server-info: %v", err)
}
if err = createUpdateHook(repoPath); err != nil {
return nil, fmt.Errorf("createUpdateHook: %v", err)
if err = createDelegateHooks(repoPath); err != nil {
return nil, fmt.Errorf("createDelegateHooks: %v", err)
}
return repo, sess.Commit()

23
models/repo_collaboration.go

@ -6,6 +6,8 @@ package models
import (
"fmt"
api "github.com/gogits/go-gogs-client"
)
// Collaboration represent the relation between an individual and a repository.
@ -29,6 +31,16 @@ func (c *Collaboration) ModeI18nKey() string {
}
}
//IsCollaborator returns true if the user is a collaborator
func (repo *Repository) IsCollaborator(uid int64) (bool, error) {
collaboration := &Collaboration{
RepoID: repo.ID,
UserID: uid,
}
return x.Get(collaboration)
}
// AddCollaborator adds new collaboration to a repository with default access mode.
func (repo *Repository) AddCollaborator(u *User) error {
collaboration := &Collaboration{
@ -77,6 +89,17 @@ type Collaborator struct {
Collaboration *Collaboration
}
func (c *Collaborator) APIFormat() *api.Collaborator {
return &api.Collaborator{
User: c.User.APIFormat(),
Permissions: api.Permission{
Admin: c.Collaboration.Mode >= ACCESS_MODE_ADMIN,
Push: c.Collaboration.Mode >= ACCESS_MODE_WRITE,
Pull: c.Collaboration.Mode >= ACCESS_MODE_READ,
},
}
}
func (repo *Repository) getCollaborators(e Engine) ([]*Collaborator, error) {
collaborations, err := repo.getCollaborations(e)
if err != nil {

2
models/repo_editor.go

@ -17,10 +17,10 @@ import (
"github.com/Unknwon/com"
gouuid "github.com/satori/go.uuid"
log "gopkg.in/clog.v1"
git "github.com/gogits/git-module"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting"
)

16
models/ssh_key.go

@ -21,9 +21,9 @@ import (
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
"golang.org/x/crypto/ssh"
log "gopkg.in/clog.v1"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting"
)
@ -84,8 +84,13 @@ func (k *PublicKey) OmitEmail() string {
}
// AuthorizedString returns formatted public key string for authorized_keys file.
func (key *PublicKey) AuthorizedString() string {
return fmt.Sprintf(_TPL_PUBLICK_KEY, setting.AppPath, key.ID, setting.CustomConf, key.Content)
func (k *PublicKey) AuthorizedString() string {
return fmt.Sprintf(_TPL_PUBLICK_KEY, setting.AppPath, k.ID, setting.CustomConf, k.Content)
}
// IsDeployKey returns true if the public key is used as deploy key.
func (k *PublicKey) IsDeployKey() bool {
return k.Type == KEY_TYPE_DEPLOY
}
func extractTypeFromBase64Key(key string) (string, error) {
@ -380,9 +385,10 @@ func addKey(e Engine, key *PublicKey) (err error) {
if err = ioutil.WriteFile(tmpPath, []byte(key.Content), 0644); err != nil {
return err
}
stdout, stderr, err := process.Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath)
stdout, stderr, err := process.Exec("AddPublicKey", setting.SSH.KeygenPath, "-lf", tmpPath)
if err != nil {
return fmt.Errorf("'ssh-keygen -lf %s' failed with error '%s': %s", tmpPath, err, stderr)
return fmt.Errorf("fail to parse public key: %s - %s", err, stderr)
} else if len(stdout) < 2 {
return errors.New("not enough output for calculating fingerprint: " + stdout)
}

6
models/update.go

@ -10,9 +10,9 @@ import (
"os/exec"
"strings"
git "github.com/gogits/git-module"
log "gopkg.in/clog.v1"
"github.com/gogits/gogs/modules/log"
git "github.com/gogits/git-module"
)
type UpdateTask struct {
@ -101,7 +101,7 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
}
if isDelRef {
log.GitLogger.Info("Reference '%s' has been deleted from '%s/%s' by %s",
log.Trace("Reference '%s' has been deleted from '%s/%s' by %s",
opts.RefFullName, opts.RepoUserName, opts.RepoName, opts.PusherName)
return nil
}

10
models/user.go

@ -25,13 +25,13 @@ import (
"github.com/go-xorm/xorm"
"github.com/nfnt/resize"
"golang.org/x/crypto/pbkdf2"
log "gopkg.in/clog.v1"
"github.com/gogits/git-module"
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/modules/avatar"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/setting"
)
@ -170,6 +170,10 @@ func (u *User) CanCreateRepo() bool {
return u.NumRepos < u.MaxRepoCreation
}
func (u *User) CanCreateOrganization() bool {
return !setting.Admin.DisableRegularOrgCreation || u.IsAdmin
}
// CanEditGitHook returns true if user can edit Git hooks.
func (u *User) CanEditGitHook() bool {
return u.IsAdmin || u.AllowGitHook
@ -193,6 +197,10 @@ func (u *User) HomeLink() string {
return setting.AppSubUrl + "/" + u.Name
}
func (u *User) HTMLURL() string {
return setting.AppUrl + u.Name
}
// GenerateEmailActivateCode generates an activate code based on user information and given e-mail.
func (u *User) GenerateEmailActivateCode(email string) string {
code := base.CreateTimeLimitCode(

6
models/webhook.go

@ -14,11 +14,11 @@ import (
"github.com/go-xorm/xorm"
gouuid "github.com/satori/go.uuid"
log "gopkg.in/clog.v1"
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/modules/httplib"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/sync"
)
@ -476,7 +476,9 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err
// Use separate objects so modifcations won't be made on payload on non-Gogs type hooks.
switch w.HookTaskType {
case SLACK:
payloader, err = GetSlackPayload(p, event, w.Meta)
// FIXME: dirty fix for buggy support of Discord for Slack-type webhook.
// Should remove this if we want to support Discord fully as its own.
payloader, err = GetSlackPayload(strings.Contains(w.URL, ".discordapp.com/"), p, event, w.Meta)
if err != nil {
return fmt.Errorf("GetSlackPayload: %v", err)
}

32
models/webhook_slack.go

@ -72,13 +72,21 @@ func SlackLinkFormatter(url string, text string) string {
return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text))
}
func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) {
// created tag/branch
func replaceBadCharsForDiscord(in string) string {
return strings.NewReplacer("[", "", "]", ":", ":", "/").Replace(in)
}
func getSlackCreatePayload(isDiscord bool, p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) {
// Created tag/branch
refName := git.RefEndName(p.Ref)
repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
refLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName)
format := "[%s:%s] %s created by %s"
if isDiscord {
format = replaceBadCharsForDiscord(format)
}
text := fmt.Sprintf(format, repoLink, refLink, p.RefType, p.Sender.UserName)
return &SlackPayload{
Channel: slack.Channel,
@ -88,7 +96,7 @@ func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayloa
}, nil
}
func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) {
func getSlackPushPayload(isDiscord bool, p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) {
// n new commits
var (
branchName = git.RefEndName(p.Ref)
@ -109,7 +117,11 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e
repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
branchLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+branchName, branchName)
text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.UserName)
format := "[%s:%s] %s pushed by %s"
if isDiscord {
format = replaceBadCharsForDiscord(format)
}
text := fmt.Sprintf(format, repoLink, branchLink, commitString, p.Pusher.UserName)
var attachmentText string
// for each commit, generate attachment text
@ -133,7 +145,7 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e
}, nil
}
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
func getSlackPullRequestPayload(isDiscord bool, p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
senderLink := SlackLinkFormatter(setting.AppUrl+p.Sender.UserName, p.Sender.UserName)
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
@ -181,7 +193,7 @@ func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*S
}, nil
}
func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackPayload, error) {
func GetSlackPayload(isDiscord bool, p api.Payloader, event HookEventType, meta string) (*SlackPayload, error) {
s := new(SlackPayload)
slack := &SlackMeta{}
@ -191,11 +203,11 @@ func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackP
switch event {
case HOOK_EVENT_CREATE:
return getSlackCreatePayload(p.(*api.CreatePayload), slack)
return getSlackCreatePayload(isDiscord, p.(*api.CreatePayload), slack)
case HOOK_EVENT_PUSH:
return getSlackPushPayload(p.(*api.PushPayload), slack)
return getSlackPushPayload(isDiscord, p.(*api.PushPayload), slack)
case HOOK_EVENT_PULL_REQUEST:
return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
return getSlackPullRequestPayload(isDiscord, p.(*api.PullRequestPayload), slack)
}
return s, nil

4
models/wiki.go

@ -64,8 +64,8 @@ func (repo *Repository) InitWiki() error {
if err := git.InitRepository(repo.WikiPath(), true); err != nil {
return fmt.Errorf("InitRepository: %v", err)
} else if err = createUpdateHook(repo.WikiPath()); err != nil {
return fmt.Errorf("createUpdateHook: %v", err)
} else if err = createDelegateHooks(repo.WikiPath()); err != nil {
return fmt.Errorf("createDelegateHooks: %v", err)
}
return nil
}

2
modules/auth/auth.go

@ -13,11 +13,11 @@ import (
"github.com/go-macaron/binding"
"github.com/go-macaron/session"
gouuid "github.com/satori/go.uuid"
log "gopkg.in/clog.v1"
"gopkg.in/macaron.v1"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)

43
modules/auth/ldap/ldap.go

@ -11,9 +11,8 @@ import (
"fmt"
"strings"
log "gopkg.in/clog.v1"
"gopkg.in/ldap.v2"
"github.com/gogits/gogs/modules/log"
)
type SecurityProtocol int
@ -50,7 +49,7 @@ func (ls *Source) sanitizedUserQuery(username string) (string, bool) {
// See http://tools.ietf.org/search/rfc4515
badCharacters := "\x00()*\\"
if strings.ContainsAny(username, badCharacters) {
log.Debug("'%s' contains invalid query characters. Aborting.", username)
log.Trace("Username contains invalid query characters: %s", username)
return "", false
}
@ -61,7 +60,7 @@ func (ls *Source) sanitizedUserDN(username string) (string, bool) {
// See http://tools.ietf.org/search/rfc4514: "special characters"
badCharacters := "\x00()*\\,='\"#+;<>"
if strings.ContainsAny(username, badCharacters) || strings.HasPrefix(username, " ") || strings.HasSuffix(username, " ") {
log.Debug("'%s' contains invalid DN characters. Aborting.", username)
log.Trace("Username contains invalid query characters: %s", username)
return "", false
}
@ -73,12 +72,12 @@ func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) {
if ls.BindDN != "" && ls.BindPassword != "" {
err := l.Bind(ls.BindDN, ls.BindPassword)
if err != nil {
log.Debug("Failed to bind as BindDN[%s]: %v", ls.BindDN, err)
log.Trace("Failed to bind as BindDN '%s': %v", ls.BindDN, err)
return "", false
}
log.Trace("Bound as BindDN %s", ls.BindDN)
log.Trace("Bound as BindDN: %s", ls.BindDN)
} else {
log.Trace("Proceeding with anonymous LDAP search.")
log.Trace("Proceeding with anonymous LDAP search")
}
// A search for the user.
@ -87,7 +86,7 @@ func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) {
return "", false
}
log.Trace("Searching for DN using filter %s and base %s", userFilter, ls.UserBase)
log.Trace("Searching for DN using filter '%s' and base '%s'", userFilter, ls.UserBase)
search := ldap.NewSearchRequest(
ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0,
false, userFilter, []string{}, nil)
@ -95,10 +94,10 @@ func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) {
// Ensure we found a user
sr, err := l.Search(search)
if err != nil || len(sr.Entries) < 1 {
log.Debug("Failed search using filter[%s]: %v", userFilter, err)
log.Trace("Failed search using filter '%s': %v", userFilter, err)
return "", false
} else if len(sr.Entries) > 1 {
log.Debug("Filter '%s' returned more than one user.", userFilter)
log.Trace("Filter '%s' returned more than one user", userFilter)
return "", false
}
@ -112,7 +111,7 @@ func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) {
}
func dial(ls *Source) (*ldap.Conn, error) {
log.Trace("Dialing LDAP with security protocol (%v) without verifying: %v", ls.SecurityProtocol, ls.SkipVerify)
log.Trace("Dialing LDAP with security protocol '%v' without verifying: %v", ls.SecurityProtocol, ls.SkipVerify)
tlsCfg := &tls.Config{
ServerName: ls.Host,
@ -141,7 +140,7 @@ func bindUser(l *ldap.Conn, userDN, passwd string) error {
log.Trace("Binding with userDN: %s", userDN)
err := l.Bind(userDN, passwd)
if err != nil {
log.Debug("LDAP auth. failed for %s, reason: %v", userDN, err)
log.Trace("LDAP authentication failed for '%s': %v", userDN, err)
return err
}
log.Trace("Bound successfully with userDN: %s", userDN)
@ -152,12 +151,12 @@ func bindUser(l *ldap.Conn, userDN, passwd string) error {
func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, string, string, string, bool, bool) {
// See https://tools.ietf.org/search/rfc4513#section-5.1.2
if len(passwd) == 0 {
log.Debug("Auth. failed for %s, password cannot be empty")
log.Trace("authentication failed for '%s' with empty password")
return "", "", "", "", false, false
}
l, err := dial(ls)
if err != nil {
log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err)
log.Error(4, "LDAP connect failed for '%s': %v", ls.Host, err)
ls.Enabled = false
return "", "", "", "", false, false
}
@ -173,7 +172,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
return "", "", "", "", false, false
}
} else {
log.Trace("LDAP will use BindDN.")
log.Trace("LDAP will use BindDN")
var found bool
userDN, found = ls.findUserDN(l, name)
@ -195,7 +194,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
return "", "", "", "", false, false
}
log.Trace("Fetching attributes '%v', '%v', '%v', '%v' with filter %s and base %s", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, userFilter, userDN)
log.Trace("Fetching attributes '%v', '%v', '%v', '%v' with filter '%s' and base '%s'", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, userFilter, userDN)
search := ldap.NewSearchRequest(
userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
[]string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail},
@ -203,13 +202,13 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
sr, err := l.Search(search)
if err != nil {
log.Error(4, "LDAP Search failed unexpectedly! (%v)", err)
log.Error(4, "LDAP search failed: %v", err)
return "", "", "", "", false, false
} else if len(sr.Entries) < 1 {
if directBind {
log.Error(4, "User filter inhibited user login.")
log.Error(4, "User filter inhibited user login")
} else {
log.Error(4, "LDAP Search failed unexpectedly! (0 entries)")
log.Error(4, "LDAP search failed: 0 entries")
}
return "", "", "", "", false, false
@ -222,7 +221,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
isAdmin := false
if len(ls.AdminFilter) > 0 {
log.Trace("Checking admin with filter %s and base %s", ls.AdminFilter, userDN)
log.Trace("Checking admin with filter '%s' and base '%s'", ls.AdminFilter, userDN)
search = ldap.NewSearchRequest(
userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ls.AdminFilter,
[]string{ls.AttributeName},
@ -230,9 +229,9 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str
sr, err = l.Search(search)
if err != nil {
log.Error(4, "LDAP Admin Search failed unexpectedly! (%v)", err)
log.Error(4, "LDAP admin search failed: %v", err)
} else if len(sr.Entries) < 1 {
log.Error(4, "LDAP Admin Search failed")
log.Error(4, "LDAP admin search failed: 0 entries")
} else {
isAdmin = true
}

2
modules/auth/user_form.go

@ -31,7 +31,7 @@ type InstallForm struct {
SMTPHost string
SMTPFrom string
SMTPEmail string `binding:"OmitEmpty;Email;MaxSize(254)" locale:"install.mailer_user"`
SMTPUser string `binding:"OmitEmpty;MaxSize(254)" locale:"install.mailer_user"`
SMTPPasswd string
RegisterConfirm bool
MailNotify bool

13
modules/base/tool.go

@ -22,10 +22,10 @@ import (
"github.com/Unknwon/com"
"github.com/Unknwon/i18n"
log "gopkg.in/clog.v1"
"github.com/gogits/chardet"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
@ -52,17 +52,17 @@ func ShortSha(sha1 string) string {
func DetectEncoding(content []byte) (string, error) {
if utf8.Valid(content) {
log.Debug("Detected encoding: utf-8 (fast)")
log.Trace("Detected encoding: utf-8 (fast)")
return "UTF-8", nil
}
result, err := chardet.NewTextDetector().DetectBest(content)
if result.Charset != "UTF-8" && len(setting.Repository.AnsiCharset) > 0 {
log.Debug("Using default AnsiCharset: %s", setting.Repository.AnsiCharset)
log.Trace("Using default AnsiCharset: %s", setting.Repository.AnsiCharset)
return setting.Repository.AnsiCharset, err
}
log.Debug("Detected encoding: %s", result.Charset)
log.Trace("Detected encoding: %s", result.Charset)
return result.Charset, err
}
@ -181,11 +181,12 @@ func HashEmail(email string) string {
// which includes app sub-url as prefix. However, it is possible
// to return full URL if user enables Gravatar-like service.
func AvatarLink(email string) (url string) {
if setting.EnableFederatedAvatar && setting.LibravatarService != nil {
if setting.EnableFederatedAvatar && setting.LibravatarService != nil &&
len(email) > 0 {
var err error
url, err = setting.LibravatarService.FromEmail(email)
if err != nil {
log.Error(4, "LibravatarService.FromEmail [%s]: %v", email, err)
log.Error(3, "LibravatarService.FromEmail [%s]: %v", email, err)
}
}
if len(url) == 0 && !setting.DisableGravatar {

92
modules/bindata/bindata.go

File diff suppressed because one or more lines are too long

2
modules/context/api.go

@ -9,10 +9,10 @@ import (
"strings"
"github.com/Unknwon/paginater"
log "gopkg.in/clog.v1"
"gopkg.in/macaron.v1"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)

27
modules/context/context.go

@ -16,12 +16,12 @@ import (
"github.com/go-macaron/csrf"
"github.com/go-macaron/i18n"
"github.com/go-macaron/session"
log "gopkg.in/clog.v1"
"gopkg.in/macaron.v1"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
@ -73,7 +73,7 @@ func (ctx *Context) HasValue(name string) bool {
// HTML calls Context.HTML and converts template name to string.
func (ctx *Context) HTML(status int, name base.TplName) {
log.Debug("Template: %s", name)
log.Trace("Template: %s", name)
ctx.Context.HTML(status, string(name))
}
@ -89,22 +89,24 @@ func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}
// Handle handles and logs error by given status.
func (ctx *Context) Handle(status int, title string, err error) {
if err != nil {
log.Error(4, "%s: %v", title, err)
if macaron.Env != macaron.PROD {
ctx.Data["ErrorMsg"] = err
}
}
switch status {
case 404:
ctx.Data["Title"] = "Page Not Found"
case 500:
ctx.Data["Title"] = "Internal Server Error"
log.Error(4, "%s: %v", title, err)
if !setting.ProdMode || (ctx.IsSigned && ctx.User.IsAdmin) {
ctx.Data["ErrorMsg"] = err
}
}
ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status)))
}
// NotFound simply renders the 404 page.
func (ctx *Context) NotFound() {
ctx.Handle(404, "", nil)
}
// NotFoundOrServerError use error check function to determine if the error
// is about not found. It responses with 404 status code for not found error,
// or error context description for logging purpose of 500 server error.
@ -118,9 +120,6 @@ func (ctx *Context) NotFoundOrServerError(title string, errck func(error) bool,
}
func (ctx *Context) HandleText(status int, title string) {
if (status/100 == 4) || (status/100 == 5) {
log.Error(4, "%s", title)
}
ctx.PlainText(status, []byte(title))
}
@ -191,8 +190,8 @@ func Contexter() macaron.Handler {
ctx.Data["CsrfToken"] = x.GetToken()
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + x.GetToken() + `">`)
log.Debug("Session ID: %s", sess.ID())
log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"])
log.Trace("Session ID: %s", sess.ID())
log.Trace("CSRF Token: %v", ctx.Data["CsrfToken"])
ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton
ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding

20
modules/context/repo.go

@ -11,13 +11,13 @@ import (
"strings"
"github.com/Unknwon/com"
log "gopkg.in/clog.v1"
"gopkg.in/editorconfig/editorconfig-core-go.v1"
"gopkg.in/macaron.v1"
"github.com/gogits/git-module"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
@ -164,7 +164,7 @@ func RepoAssignment(args ...bool) macaron.Handler {
earlyResponseForGoGetMeta(ctx)
return
}
ctx.Handle(404, "GetUserByName", err)
ctx.NotFound()
} else {
ctx.Handle(500, "GetUserByName", err)
}
@ -182,7 +182,7 @@ func RepoAssignment(args ...bool) macaron.Handler {
earlyResponseForGoGetMeta(ctx)
return
}
ctx.Handle(404, "GetRepositoryByName", err)
ctx.NotFound()
} else {
ctx.Handle(500, "GetRepositoryByName", err)
}
@ -210,7 +210,7 @@ func RepoAssignment(args ...bool) macaron.Handler {
earlyResponseForGoGetMeta(ctx)
return
}
ctx.Handle(404, "no access right", err)
ctx.NotFound()
return
}
ctx.Data["HasAccess"] = true
@ -242,7 +242,7 @@ func RepoAssignment(args ...bool) macaron.Handler {
tags, err := ctx.Repo.GitRepo.GetTags()
if err != nil {
ctx.Handle(500, "GetTags", err)
ctx.Handle(500, fmt.Sprintf("GetTags '%s'", ctx.Repo.Repository.RepoPath()), err)
return
}
ctx.Data["Tags"] = tags
@ -267,7 +267,7 @@ func RepoAssignment(args ...bool) macaron.Handler {
// repo is bare and display enable
if ctx.Repo.Repository.IsBare {
log.Debug("Bare repository: %s", ctx.Repo.RepoLink)
log.Trace("Bare repository: %s", ctx.Repo.RepoLink)
// NOTE: to prevent templating error
ctx.Data["BranchName"] = ""
if displayBare {
@ -395,7 +395,7 @@ func RepoRef() macaron.Handler {
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
if err != nil {
ctx.Handle(404, "GetCommit", nil)
ctx.NotFound()
return
}
} else {
@ -455,7 +455,7 @@ func RepoRef() macaron.Handler {
func RequireRepoAdmin() macaron.Handler {
return func(ctx *Context) {
if !ctx.IsSigned || (!ctx.Repo.IsAdmin() && !ctx.User.IsAdmin) {
ctx.Handle(404, ctx.Req.RequestURI, nil)
ctx.NotFound()
return
}
}
@ -464,7 +464,7 @@ func RequireRepoAdmin() macaron.Handler {
func RequireRepoWriter() macaron.Handler {
return func(ctx *Context) {
if !ctx.IsSigned || (!ctx.Repo.IsWriter() && !ctx.User.IsAdmin) {
ctx.Handle(404, ctx.Req.RequestURI, nil)
ctx.NotFound()
return
}
}
@ -474,7 +474,7 @@ func RequireRepoWriter() macaron.Handler {
func GitHookService() macaron.Handler {
return func(ctx *Context) {
if !ctx.User.CanEditGitHook() {
ctx.Handle(404, "GitHookService", nil)
ctx.NotFound()
return
}
}

3
modules/cron/cron.go

@ -7,10 +7,11 @@ package cron
import (
"time"
log "gopkg.in/clog.v1"
"github.com/gogits/cron"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)

104
modules/log/conn.go

@ -1,104 +0,0 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package log
import (
"encoding/json"
"io"
"log"
"net"
)
// ConnWriter implements LoggerInterface.
// it writes messages in keep-live tcp connection.
type ConnWriter struct {
lg *log.Logger
innerWriter io.WriteCloser
ReconnectOnMsg bool `json:"reconnectOnMsg"`
Reconnect bool `json:"reconnect"`
Net string `json:"net"`
Addr string `json:"addr"`
Level int `json:"level"`
}
// create new ConnWrite returning as LoggerInterface.
func NewConn() LoggerInterface {
conn := new(ConnWriter)
conn.Level = TRACE
return conn
}
// init connection writer with json config.
// json config only need key "level".
func (cw *ConnWriter) Init(jsonconfig string) error {
return json.Unmarshal([]byte(jsonconfig), cw)
}
// write message in connection.
// if connection is down, try to re-connect.
func (cw *ConnWriter) WriteMsg(msg string, skip, level int) error {
if cw.Level > level {
return nil
}
if cw.neddedConnectOnMsg() {
if err := cw.connect(); err != nil {
return err
}
}
if cw.ReconnectOnMsg {
defer cw.innerWriter.Close()
}
cw.lg.Println(msg)
return nil
}
func (_ *ConnWriter) Flush() {
}
// destroy connection writer and close tcp listener.
func (cw *ConnWriter) Destroy() {
if cw.innerWriter == nil {
return
}
cw.innerWriter.Close()
}
func (cw *ConnWriter) connect() error {
if cw.innerWriter != nil {
cw.innerWriter.Close()
cw.innerWriter = nil
}
conn, err := net.Dial(cw.Net, cw.Addr)
if err != nil {
return err
}
if tcpConn, ok := conn.(*net.TCPConn); ok {
tcpConn.SetKeepAlive(true)
}
cw.innerWriter = conn
cw.lg = log.New(conn, "", log.Ldate|log.Ltime)
return nil
}
func (cw *ConnWriter) neddedConnectOnMsg() bool {
if cw.Reconnect {
cw.Reconnect = false
return true
}
if cw.innerWriter == nil {
return true
}
return cw.ReconnectOnMsg
}
func init() {
Register("conn", NewConn)
}

73
modules/log/console.go

@ -1,73 +0,0 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package log
import (
"encoding/json"
"log"
"os"
"runtime"
)
type Brush func(string) string
func NewBrush(color string) Brush {
pre := "\033["
reset := "\033[0m"
return func(text string) string {
return pre + color + "m" + text + reset
}
}
var colors = []Brush{
NewBrush("1;36"), // Trace cyan
NewBrush("1;34"), // Debug blue
NewBrush("1;32"), // Info green
NewBrush("1;33"), // Warn yellow
NewBrush("1;31"), // Error red
NewBrush("1;35"), // Critical purple
NewBrush("1;31"), // Fatal red
}
// ConsoleWriter implements LoggerInterface and writes messages to terminal.
type ConsoleWriter struct {
lg *log.Logger
Level int `json:"level"`
}
// create ConsoleWriter returning as LoggerInterface.
func NewConsole() LoggerInterface {
return &ConsoleWriter{
lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
Level: TRACE,
}
}
func (cw *ConsoleWriter) Init(config string) error {
return json.Unmarshal([]byte(config), cw)
}
func (cw *ConsoleWriter) WriteMsg(msg string, skip, level int) error {
if cw.Level > level {
return nil
}
if runtime.GOOS == "windows" {
cw.lg.Println(msg)
} else {
cw.lg.Println(colors[level](msg))
}
return nil
}
func (_ *ConsoleWriter) Flush() {
}
func (_ *ConsoleWriter) Destroy() {
}
func init() {
Register("console", NewConsole)
}

243
modules/log/file.go

@ -1,243 +0,0 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package log
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"sync"
"time"
)
// FileLogWriter implements LoggerInterface.
// It writes messages by lines limit, file size limit, or time frequency.
type FileLogWriter struct {
*log.Logger
mw *MuxWriter
// The opened file
Filename string `json:"filename"`
Maxlines int `json:"maxlines"`
maxlines_curlines int
// Rotate at size
Maxsize int `json:"maxsize"`
maxsize_cursize int
// Rotate daily
Daily bool `json:"daily"`
Maxdays int64 `json:"maxdays"`
daily_opendate int
Rotate bool `json:"rotate"`
startLock sync.Mutex // Only one log can write to the file
Level int `json:"level"`
}
// an *os.File writer with locker.
type MuxWriter struct {
sync.Mutex
fd *os.File
}
// write to os.File.
func (l *MuxWriter) Write(b []byte) (int, error) {
l.Lock()
defer l.Unlock()
return l.fd.Write(b)
}
// set os.File in writer.
func (l *MuxWriter) SetFd(fd *os.File) {
if l.fd != nil {
l.fd.Close()
}
l.fd = fd
}
// create a FileLogWriter returning as LoggerInterface.
func NewFileWriter() LoggerInterface {
w := &FileLogWriter{
Filename: "",
Maxlines: 1000000,
Maxsize: 1 << 28, //256 MB
Daily: true,
Maxdays: 7,
Rotate: true,
Level: TRACE,
}
// use MuxWriter instead direct use os.File for lock write when rotate
w.mw = new(MuxWriter)
// set MuxWriter as Logger's io.Writer
w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime)
return w
}
// Init file logger with json config.
// config like:
// {
// "filename":"log/gogs.log",
// "maxlines":10000,
// "maxsize":1<<30,
// "daily":true,
// "maxdays":15,
// "rotate":true
// }
func (w *FileLogWriter) Init(config string) error {
if err := json.Unmarshal([]byte(config), w); err != nil {
return err
}
if len(w.Filename) == 0 {
return errors.New("config must have filename")
}
return w.StartLogger()
}
// start file logger. create log file and set to locker-inside file writer.
func (w *FileLogWriter) StartLogger() error {
fd, err := w.createLogFile()
if err != nil {
return err
}
w.mw.SetFd(fd)
if err = w.initFd(); err != nil {
return err
}
return nil
}
func (w *FileLogWriter) docheck(size int) {
w.startLock.Lock()
defer w.startLock.Unlock()
if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) ||
(w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) ||
(w.Daily && time.Now().Day() != w.daily_opendate)) {
if err := w.DoRotate(); err != nil {
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
return
}
}
w.maxlines_curlines++
w.maxsize_cursize += size
}
// write logger message into file.
func (w *FileLogWriter) WriteMsg(msg string, skip, level int) error {
if level < w.Level {
return nil
}
n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] "
w.docheck(n)
w.Logger.Println(msg)
return nil
}
func (w *FileLogWriter) createLogFile() (*os.File, error) {
// Open the log file
return os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
}
func (w *FileLogWriter) initFd() error {
fd := w.mw.fd
finfo, err := fd.Stat()
if err != nil {
return fmt.Errorf("get stat: %s\n", err)
}
w.maxsize_cursize = int(finfo.Size())
w.daily_opendate = time.Now().Day()
if finfo.Size() > 0 {
content, err := ioutil.ReadFile(w.Filename)
if err != nil {
return err
}
w.maxlines_curlines = len(strings.Split(string(content), "\n"))
} else {
w.maxlines_curlines = 0
}
return nil
}
// DoRotate means it need to write file in new file.
// new file name like xx.log.2013-01-01.2
func (w *FileLogWriter) DoRotate() error {
_, err := os.Lstat(w.Filename)
if err == nil { // file exists
// Find the next available number
num := 1
fname := ""
for ; err == nil && num <= 999; num++ {
fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num)
_, err = os.Lstat(fname)
}
// return error if the last file checked still existed
if err == nil {
return fmt.Errorf("rotate: cannot find free log number to rename %s\n", w.Filename)
}
// block Logger's io.Writer
w.mw.Lock()
defer w.mw.Unlock()
fd := w.mw.fd
fd.Close()
// close fd before rename
// Rename the file to its newfound home
if err = os.Rename(w.Filename, fname); err != nil {
return fmt.Errorf("Rotate: %s\n", err)
}
// re-start logger
if err = w.StartLogger(); err != nil {
return fmt.Errorf("Rotate StartLogger: %s\n", err)
}
go w.deleteOldLog()
}
return nil
}
func (w *FileLogWriter) deleteOldLog() {
dir := filepath.Dir(w.Filename)
filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) {
defer func() {
if r := recover(); r != nil {
returnErr = fmt.Errorf("Unable to delete old log '%s', error: %+v", path, r)
}
}()
if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) {
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
os.Remove(path)
}
}
return returnErr
})
}
// destroy file logger, close file writer.
func (w *FileLogWriter) Destroy() {
w.mw.fd.Close()
}
// flush file logger.
// there are no buffering messages in file logger in memory.
// flush file means sync file from disk.
func (w *FileLogWriter) Flush() {
w.mw.fd.Sync()
}
func init() {
Register("file", NewFileWriter)
}

312
modules/log/log.go

@ -1,312 +0,0 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package log
import (
"fmt"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"sync"
)
var (
loggers []*Logger
GitLogger *Logger
)
func NewLogger(bufLen int64, mode, config string) {
logger := newLogger(bufLen)
isExist := false
for i, l := range loggers {
if l.adapter == mode {
isExist = true
loggers[i] = logger
}
}
if !isExist {
loggers = append(loggers, logger)
}
if err := logger.SetLogger(mode, config); err != nil {
Fatal(2, "Fail to set logger (%s): %v", mode, err)
}
}
// FIXME: use same log level as other loggers.
func NewGitLogger(logPath string) {
os.MkdirAll(path.Dir(logPath), os.ModePerm)
GitLogger = newLogger(0)
GitLogger.SetLogger("file", fmt.Sprintf(`{"level":0,"filename":"%s","rotate":false}`, logPath))
}
func Trace(format string, v ...interface{}) {
for _, logger := range loggers {
logger.Trace(format, v...)
}
}
func Debug(format string, v ...interface{}) {
for _, logger := range loggers {
logger.Debug(format, v...)
}
}
func Info(format string, v ...interface{}) {
for _, logger := range loggers {
logger.Info(format, v...)
}
}
func Warn(format string, v ...interface{}) {
for _, logger := range loggers {
logger.Warn(format, v...)
}
}
func Error(skip int, format string, v ...interface{}) {
for _, logger := range loggers {
logger.Error(skip, format, v...)
}
}
func Critical(skip int, format string, v ...interface{}) {
for _, logger := range loggers {
logger.Critical(skip, format, v...)
}
}
func Fatal(skip int, format string, v ...interface{}) {
Error(skip, format, v...)
for _, l := range loggers {
l.Close()
}
os.Exit(1)
}
func Close() {
for _, l := range loggers {
l.Close()
}
}
// .___ __ _____
// | | _____/ |_ ____________/ ____\____ ____ ____
// | |/ \ __\/ __ \_ __ \ __\\__ \ _/ ___\/ __ \
// | | | \ | \ ___/| | \/| | / __ \\ \__\ ___/
// |___|___| /__| \___ >__| |__| (____ /\___ >___ >
// \/ \/ \/ \/ \/
type LogLevel int
const (
TRACE = iota
DEBUG
INFO
WARN
ERROR
CRITICAL
FATAL
)
// LoggerInterface represents behaviors of a logger provider.
type LoggerInterface interface {
Init(config string) error
WriteMsg(msg string, skip, level int) error
Destroy()
Flush()
}
type loggerType func() LoggerInterface
var adapters = make(map[string]loggerType)
// Register registers given logger provider to adapters.
func Register(name string, log loggerType) {
if log == nil {
panic("log: register provider is nil")
}
if _, dup := adapters[name]; dup {
panic("log: register called twice for provider \"" + name + "\"")
}
adapters[name] = log
}
type logMsg struct {
skip, level int
msg string
}
// Logger is default logger in beego application.
// it can contain several providers and log message into all providers.
type Logger struct {
adapter string
lock sync.Mutex
level int
msg chan *logMsg
outputs map[string]LoggerInterface
quit chan bool
}
// newLogger initializes and returns a new logger.
func newLogger(buffer int64) *Logger {
l := &Logger{
msg: make(chan *logMsg, buffer),
outputs: make(map[string]LoggerInterface),
quit: make(chan bool),
}
go l.StartLogger()
return l
}
// 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()
if log, ok := adapters[adapter]; ok {
lg := log()
if err := lg.Init(config); err != nil {
return err
}
l.outputs[adapter] = lg
l.adapter = adapter
} else {
panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
}
return nil
}
// DelLogger removes a logger adapter instance.
func (l *Logger) DelLogger(adapter string) error {
l.lock.Lock()
defer l.lock.Unlock()
if lg, ok := l.outputs[adapter]; ok {
lg.Destroy()
delete(l.outputs, adapter)
} else {
panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
}
return nil
}
func (l *Logger) writerMsg(skip, level int, msg string) error {
if l.level > level {
return nil
}
lm := &logMsg{
skip: skip,
level: level,
}
// Only error information needs locate position for debugging.
if lm.level >= ERROR {
pc, file, line, ok := runtime.Caller(skip)
if ok {
// Get caller function name.
fn := runtime.FuncForPC(pc)
var fnName string
if fn == nil {
fnName = "?()"
} else {
fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()"
}
fileName := file
if len(fileName) > 20 {
fileName = "..." + fileName[len(fileName)-20:]
}
lm.msg = fmt.Sprintf("[%s:%d %s] %s", fileName, line, fnName, msg)
} else {
lm.msg = msg
}
} else {
lm.msg = msg
}
l.msg <- lm
return nil
}
// StartLogger starts logger chan reading.
func (l *Logger) StartLogger() {
for {
select {
case bm := <-l.msg:
for _, l := range l.outputs {
if err := l.WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
fmt.Println("ERROR, unable to WriteMsg:", err)
}
}
case <-l.quit:
return
}
}
}
// Flush flushs all chan data.
func (l *Logger) Flush() {
for _, l := range l.outputs {
l.Flush()
}
}
// Close closes logger, flush all chan data and destroy all adapter instances.
func (l *Logger) Close() {
l.quit <- true
for {
if len(l.msg) > 0 {
bm := <-l.msg
for _, l := range l.outputs {
if err := l.WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
fmt.Println("ERROR, unable to WriteMsg:", err)
}
}
} else {
break
}
}
for _, l := range l.outputs {
l.Flush()
l.Destroy()
}
}
func (l *Logger) Trace(format string, v ...interface{}) {
msg := fmt.Sprintf("[T] "+format, v...)
l.writerMsg(0, TRACE, msg)
}
func (l *Logger) Debug(format string, v ...interface{}) {
msg := fmt.Sprintf("[D] "+format, v...)
l.writerMsg(0, DEBUG, msg)
}
func (l *Logger) Info(format string, v ...interface{}) {
msg := fmt.Sprintf("[I] "+format, v...)
l.writerMsg(0, INFO, msg)
}
func (l *Logger) Warn(format string, v ...interface{}) {
msg := fmt.Sprintf("[W] "+format, v...)
l.writerMsg(0, WARN, msg)
}
func (l *Logger) Error(skip int, format string, v ...interface{}) {
msg := fmt.Sprintf("[E] "+format, v...)
l.writerMsg(skip, ERROR, msg)
}
func (l *Logger) Critical(skip int, format string, v ...interface{}) {
msg := fmt.Sprintf("[C] "+format, v...)
l.writerMsg(skip, CRITICAL, msg)
}
func (l *Logger) Fatal(skip int, format string, v ...interface{}) {
msg := fmt.Sprintf("[F] "+format, v...)
l.writerMsg(skip, FATAL, msg)
l.Close()
os.Exit(1)
}

87
modules/log/smtp.go

@ -1,87 +0,0 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package log
import (
"encoding/json"
"fmt"
"net/smtp"
"strings"
"time"
)
const (
subjectPhrase = "Diagnostic message from server"
)
// smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server.
type SmtpWriter struct {
Username string `json:"Username"`
Password string `json:"password"`
Host string `json:"Host"`
Subject string `json:"subject"`
RecipientAddresses []string `json:"sendTos"`
Level int `json:"level"`
}
// create smtp writer.
func NewSmtpWriter() LoggerInterface {
return &SmtpWriter{Level: TRACE}
}
// init smtp writer with json config.
// config like:
// {
// "Username":"example@gmail.com",
// "password:"password",
// "host":"smtp.gmail.com:465",
// "subject":"email title",
// "sendTos":["email1","email2"],
// "level":LevelError
// }
func (sw *SmtpWriter) Init(jsonconfig string) error {
return json.Unmarshal([]byte(jsonconfig), sw)
}
// write message in smtp writer.
// it will send an email with subject and only this message.
func (s *SmtpWriter) WriteMsg(msg string, skip, level int) error {
if level < s.Level {
return nil
}
hp := strings.Split(s.Host, ":")
// Set up authentication information.
auth := smtp.PlainAuth(
"",
s.Username,
s.Password,
hp[0],
)
// Connect to the server, authenticate, set the sender and recipient,
// and send the email all in one step.
content_type := "Content-Type: text/plain" + "; charset=UTF-8"
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.Username + "<" + s.Username +
">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg)
return smtp.SendMail(
s.Host,
auth,
s.Username,
s.RecipientAddresses,
mailmsg,
)
}
func (_ *SmtpWriter) Flush() {
}
func (_ *SmtpWriter) Destroy() {
}
func init() {
Register("smtp", NewSmtpWriter)
}

2
modules/mailer/mail.go

@ -8,11 +8,11 @@ import (
"fmt"
"html/template"
log "gopkg.in/clog.v1"
"gopkg.in/gomail.v2"
"gopkg.in/macaron.v1"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/setting"
)

2
modules/mailer/mailer.go

@ -15,9 +15,9 @@ import (
"time"
"github.com/jaytaylor/html2text"
log "gopkg.in/clog.v1"
"gopkg.in/gomail.v2"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)

2
modules/markdown/markdown.go

@ -43,7 +43,7 @@ func BuildSanitizer() {
Sanitizer.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
}
var validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`)
var validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://|^mailto:`)
// isLink reports whether link fits valid format.
func isLink(link []byte) bool {

2
modules/process/manager.go

@ -11,7 +11,7 @@ import (
"os/exec"
"time"
"github.com/gogits/gogs/modules/log"
log "gopkg.in/clog.v1"
)
var (

192
modules/setting/setting.go

@ -5,7 +5,6 @@
package setting
import (
"fmt"
"net/mail"
"net/url"
"os"
@ -22,12 +21,12 @@ import (
_ "github.com/go-macaron/cache/redis"
"github.com/go-macaron/session"
_ "github.com/go-macaron/session/redis"
log "gopkg.in/clog.v1"
"gopkg.in/ini.v1"
"github.com/gogits/go-libravatar"
"github.com/gogits/gogs/modules/bindata"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/user"
)
@ -86,6 +85,7 @@ var (
ListenHost string `ini:"SSH_LISTEN_HOST"`
ListenPort int `ini:"SSH_LISTEN_PORT"`
RootPath string `ini:"SSH_ROOT_PATH"`
ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"`
KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
KeygenPath string `ini:"SSH_KEYGEN_PATH"`
MinimumKeySizeCheck bool `ini:"-"`
@ -98,13 +98,14 @@ var (
LogInRememberDays int
CookieUserName string
CookieRememberName string
CookieSecure bool
ReverseProxyAuthUser string
// Database settings
UseSQLite3 bool
UseMySQL bool
UsePostgreSQL bool
UseTiDB bool
UseMSSQL bool
// Webhook settings
Webhook struct {
@ -144,25 +145,6 @@ var (
RepoRootPath string
ScriptType string
// UI settings
UI struct {
ExplorePagingNum int
IssuePagingNum int
FeedMaxCommitNum int
ThemeColorMetaTag string
MaxDisplayFileSize int64
Admin struct {
UserPagingNum int
RepoPagingNum int
NoticePagingNum int
OrgPagingNum int
} `ini:"ui.admin"`
User struct {
RepoPagingNum int
} `ini:"ui.user"`
}
// Markdown sttings
Markdown struct {
EnableHardLineBreak bool
@ -170,6 +152,11 @@ var (
FileExtensions []string
}
// Admin settings
Admin struct {
DisableRegularOrgCreation bool
}
// Picture settings
AvatarUploadPath string
GravatarSource string
@ -180,7 +167,7 @@ var (
// Log settings
LogRootPath string
LogModes []string
LogConfigs []string
LogConfigs []interface{}
// Attachment settings
AttachmentPath string
@ -224,6 +211,7 @@ var (
// Git settings
Git struct {
Version string `ini:"-"`
DisableDiffHighlight bool
MaxGitDiffLines int
MaxGitDiffLineCharacters int
@ -248,6 +236,25 @@ var (
MaxResponseItems int
}
// UI settings
UI struct {
ExplorePagingNum int
IssuePagingNum int
FeedMaxCommitNum int
ThemeColorMetaTag string
MaxDisplayFileSize int64
Admin struct {
UserPagingNum int
RepoPagingNum int
NoticePagingNum int
OrgPagingNum int
} `ini:"ui.admin"`
User struct {
RepoPagingNum int
} `ini:"ui.user"`
}
// I18n settings
Langs, Names []string
dateLangs map[string]string
@ -290,11 +297,11 @@ func execPath() (string, error) {
func init() {
IsWindows = runtime.GOOS == "windows"
log.NewLogger(0, "console", `{"level": 0}`)
log.New(log.CONSOLE, log.ConsoleConfig{})
var err error
if AppPath, err = execPath(); err != nil {
log.Fatal(4, "fail to get app path: %v\n", err)
log.Fatal(4, "Fail to get app path: %v\n", err)
}
// Note: we don't use path.Dir here because it does not handle case
@ -426,6 +433,7 @@ func NewContext() {
}
SSH.RootPath = path.Join(homeDir, ".ssh")
SSH.ServerCiphers = sec.Key("SSH_SERVER_CIPHERS").Strings(",")
SSH.KeyTestPath = os.TempDir()
if err = Cfg.Section("server").MapTo(&SSH); err != nil {
log.Fatal(4, "Fail to map SSH settings: %v", err)
@ -458,6 +466,7 @@ func NewContext() {
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt()
CookieUserName = sec.Key("COOKIE_USERNAME").String()
CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").String()
CookieSecure = sec.Key("COOKIE_SECURE").MustBool(false)
ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
sec = Cfg.Section("attachment")
@ -563,10 +572,10 @@ func NewContext() {
if err = Cfg.Section("http").MapTo(&HTTP); err != nil {
log.Fatal(4, "Fail to map HTTP settings: %v", err)
} else if err = Cfg.Section("ui").MapTo(&UI); err != nil {
log.Fatal(4, "Fail to map UI settings: %v", err)
} else if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil {
log.Fatal(4, "Fail to map Markdown settings: %v", err)
} else if err = Cfg.Section("admin").MapTo(&Admin); err != nil {
log.Fatal(4, "Fail to map Admin settings: %v", err)
} else if err = Cfg.Section("cron").MapTo(&Cron); err != nil {
log.Fatal(4, "Fail to map Cron settings: %v", err)
} else if err = Cfg.Section("git").MapTo(&Git); err != nil {
@ -575,6 +584,8 @@ func NewContext() {
log.Fatal(4, "Fail to map Mirror settings: %v", err)
} else if err = Cfg.Section("api").MapTo(&API); err != nil {
log.Fatal(4, "Fail to map API settings: %v", err)
} else if err = Cfg.Section("ui").MapTo(&UI); err != nil {
log.Fatal(4, "Fail to map UI settings: %v", err)
}
if Mirror.DefaultInterval <= 0 {
@ -617,82 +628,87 @@ func newService() {
Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool()
}
var logLevels = map[string]string{
"Trace": "0",
"Debug": "1",
"Info": "2",
"Warn": "3",
"Error": "4",
"Critical": "5",
}
func newLogService() {
log.Info("%s %s", AppName, AppVer)
if len(BuildTime) > 0 {
log.Info("Build Time: %s", BuildTime)
log.Info("Build Git Hash: %s", BuildGitHash)
log.Trace("Build Time: %s", BuildTime)
log.Trace("Build Git Hash: %s", BuildGitHash)
}
// Get and check log mode.
// Because we always create a console logger as primary logger before all settings are loaded,
// thus if user doesn't set console logger, we should remove it after other loggers are created.
hasConsole := false
// Get and check log modes.
LogModes = strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")
LogConfigs = make([]string, len(LogModes))
LogConfigs = make([]interface{}, len(LogModes))
levelNames := map[string]log.LEVEL{
"trace": log.TRACE,
"info": log.INFO,
"warn": log.WARN,
"error": log.ERROR,
"fatal": log.FATAL,
}
for i, mode := range LogModes {
mode = strings.TrimSpace(mode)
mode = strings.ToLower(strings.TrimSpace(mode))
sec, err := Cfg.GetSection("log." + mode)
if err != nil {
log.Fatal(4, "Unknown log mode: %s", mode)
log.Fatal(4, "Unknown logger mode: %s", mode)
}
validLevels := []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}
// Log level.
levelName := Cfg.Section("log."+mode).Key("LEVEL").In(
Cfg.Section("log").Key("LEVEL").In("Trace", validLevels),
validLevels)
level, ok := logLevels[levelName]
if !ok {
log.Fatal(4, "Unknown log level: %s", levelName)
}
validLevels := []string{"trace", "info", "warn", "error", "fatal"}
name := Cfg.Section("log." + mode).Key("LEVEL").Validate(func(v string) string {
v = strings.ToLower(v)
if com.IsSliceContainsStr(validLevels, v) {
return v
}
return "trace"
})
level := levelNames[name]
// Generate log configuration.
switch mode {
case "console":
LogConfigs[i] = fmt.Sprintf(`{"level":%s}`, level)
case "file":
logPath := sec.Key("FILE_NAME").MustString(path.Join(LogRootPath, "gogs.log"))
switch log.MODE(mode) {
case log.CONSOLE:
hasConsole = true
LogConfigs[i] = log.ConsoleConfig{
Level: level,
BufferSize: Cfg.Section("log").Key("BUFFER_LEN").MustInt64(100),
}
case log.FILE:
logPath := path.Join(LogRootPath, "gogs.log")
if err = os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil {
panic(err.Error())
log.Fatal(4, "Fail to create log directory '%s': %v", path.Dir(logPath), err)
}
LogConfigs[i] = log.FileConfig{
Level: level,
BufferSize: Cfg.Section("log").Key("BUFFER_LEN").MustInt64(100),
Filename: logPath,
FileRotationConfig: log.FileRotationConfig{
Rotate: sec.Key("LOG_ROTATE").MustBool(true),
Daily: sec.Key("DAILY_ROTATE").MustBool(true),
MaxSize: 1 << uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
MaxLines: sec.Key("MAX_LINES").MustInt64(1000000),
MaxDays: sec.Key("MAX_DAYS").MustInt64(7),
},
}
LogConfigs[i] = fmt.Sprintf(
`{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,
logPath,
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,
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,
sec.Key("USER").MustString("example@example.com"),
sec.Key("PASSWD").MustString("******"),
sec.Key("HOST").MustString("127.0.0.1:25"),
strings.Replace(sec.Key("RECEIVERS").MustString(""), ",", `","`, -1),
sec.Key("SUBJECT").MustString("Diagnostic message from serve"))
case "database":
LogConfigs[i] = fmt.Sprintf(`{"level":%s,"driver":"%s","conn":"%s"}`, level,
sec.Key("DRIVER").String(),
sec.Key("CONN").String())
case log.SLACK:
LogConfigs[i] = log.SlackConfig{
Level: level,
BufferSize: Cfg.Section("log").Key("BUFFER_LEN").MustInt64(100),
URL: sec.Key("URL").String(),
}
}
log.NewLogger(Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000), mode, LogConfigs[i])
log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName)
log.New(log.MODE(mode), LogConfigs[i])
log.Trace("Log Mode: %s (%s)", strings.Title(mode), strings.Title(name))
}
// Make sure everyone gets version info printed.
log.Info("%s %s", AppName, AppVer)
if !hasConsole {
log.Delete(log.CONSOLE)
}
}
@ -806,6 +822,10 @@ func newWebhookService() {
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
}
func NewService() {
newService()
}
func NewServices() {
newService()
newLogService()

9
modules/ssh/ssh.go

@ -16,9 +16,9 @@ import (
"github.com/Unknwon/com"
"golang.org/x/crypto/ssh"
log "gopkg.in/clog.v1"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
)
@ -148,8 +148,11 @@ func listen(config *ssh.ServerConfig, host string, port int) {
}
// Listen starts a SSH server listens on given port.
func Listen(host string, port int) {
func Listen(host string, port int, ciphers []string) {
config := &ssh.ServerConfig{
Config: ssh.Config{
Ciphers: ciphers,
},
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
pkey, err := models.SearchPublicKeyByContent(strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key))))
if err != nil {
@ -163,7 +166,7 @@ func Listen(host string, port int) {
keyPath := filepath.Join(setting.AppDataPath, "ssh/gogs.rsa")
if !com.IsExist(keyPath) {
os.MkdirAll(filepath.Dir(keyPath), os.ModePerm)
_, stderr, err := com.ExecCmd("ssh-keygen", "-f", keyPath, "-t", "rsa", "-N", "")
_, stderr, err := com.ExecCmd(setting.SSH.KeygenPath, "-f", keyPath, "-t", "rsa", "-N", "")
if err != nil {
panic(fmt.Sprintf("Fail to generate private key: %v - %s", err, stderr))
}

2
modules/template/template.go

@ -17,11 +17,11 @@ import (
"golang.org/x/net/html/charset"
"golang.org/x/text/transform"
log "gopkg.in/clog.v1"
"gopkg.in/editorconfig/editorconfig-core-go.v1"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/setting"
)

76
public/assets/librejs/librejs.html

@ -0,0 +1,76 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript license information</title>
</head>
<body>
<table id="jslicense-labels1">
<tr>
<td><a href="/js/libs/jquery.are-you-sure.js">jquery.are-you-sure.js</a></td>
<td>
<a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a>
<br>
<a href="http://www.gnu.org/licenses/gpl-2.0.html">GNU-GPL-2.0-or-later</a>
</td>
<td><a href="/js/libs/jquery.are-you-sure.js">jquery.are-you-sure.js</a></td>
</tr>
<tr>
<td><a href="/js/semantic-2.2.7.min.js">semantic-2.2.1.min.js</a></td>
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td>
<td><a href="https://github.com/Semantic-Org/Semantic-UI/archive/2.2.1.tar.gz">semantic-UI-2.2.1.tar.gz</a></td>
</tr>
<tr>
<td><a href="/js/gogs.js">gogs.js</a></td>
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td>
<td><a href="/js/gogs.js">gogs.js</a></td>
</tr>
<tr>
<td><a href="/js/libs/clipboard-1.5.9.min.js">clipboard-1.5.9.min.js</a></td>
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td>
<td><a href="https://github.com/zenorocha/clipboard.js/archive/v1.5.9.tar.gz">clipboard-1.5.9.tar.gz</a></td>
</tr>
<tr>
<td><a href="/js/libs/emojify-1.1.0.min.js">emojify-1.1.0.min.js</a></td>
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td>
<td><a href="https://github.com/Ranks/emojify.js/archive/1.1.0.tar.gz">emojify-1.1.0.tar.gz</a></td>
</tr>
<tr>
<td><a href="/plugins/dropzone-4.2.0/dropzone.js">dropzone.js</a></td>
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td>
<td><a href="/plugins/dropzone-4.2.0/dropzone.js">dropzone.js</a></td>
</tr>
<tr>
<td><a href="/plugins/highlight-9.6.0/highlight.pack.js">highlight.pack.js</a></td>
<td><a href="https://github.com/isagalaev/highlight.js/blob/master/LICENSE">BSD 3 Clause</a></td>
<td><a href="https://github.com/isagalaev/highlight.js/archive/9.6.0.tar.gz">highlight.js-9.6.0.tar.gz</a></td>
</tr>
<tr>
<td><a href="/plugins/jquery.datetimepicker-2.4.5/jquery.datetimepicker.js">jquery.datetimepicker.js</a></td>
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td>
<td><a href="/plugins/jquery.datetimepicker-2.4.5/jquery.datetimepicker.js">jquery.datetimepicker.js</a></td>
</tr>
<tr>
<td><a href="/plugins/jquery.minicolors-2.2.3/jquery.minicolors.min.js">jquery.minicolors.min.js</a></td>
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td>
<td><a href="https://github.com/claviska/jquery-minicolors/archive/2.2.3.tar.gz">jquery.minicolors-2.2.3.tar.gz</a></td>
</tr>
<tr>
<td><a href="/plugins/simplemde-1.10.1/simplemde.min.js">simplemde.min.js</a></td>
<td><a href="http://www.freebsd.org/copyright/freebsd-license.html">Expat</a></td>
<td><a href="https://github.com/NextStepWebs/simplemde-markdown-editor/archive/1.10.1.tar.gz">simplemde-markdown-editor-1.10.1.tar.gz</a></td>
</tr>
<tr>
<td><a href="/plugins/marked-0.3.6/marked.min.js">marked.min.js</a></td>
<td><a href="https://github.com/chjj/marked/blob/master/LICENSE">Expat</a></td>
<td><a href="https://github.com/chjj/marked/archive/v0.3.6.tar.gz">marked-0.3.6.tar.gz</a></td>
</tr>
<tr>
<td><a href="/plugins/notebookjs-0.2.6/notebook.min.js">notebook.min.js</a></td>
<td><a href="https://github.com/jsvine/notebookjs/blob/master/LICENSE.txt">Expat</a></td>
<td><a href="https://github.com/jsvine/notebookjs/archive/v0.2.6.tar.gz">notebookjs-0.2.6.tar.gz</a></td>
</tr>
</table>
</body>
</html>

88
public/css/gogs.css

@ -845,6 +845,7 @@ footer .ui.language .menu {
}
.home .logo {
max-width: 220px;
margin-bottom: 20px;
}
.home .hero h1,
.home .hero h2 {
@ -1306,6 +1307,93 @@ footer .ui.language .menu {
.repository.file.list #file-content .view-raw img {
margin-bottom: -5px;
}
.repository.file.list #file-content #ipython-notebook {
margin-left: 95px;
padding-top: 1px;
}
.repository.file.list #file-content #ipython-notebook .nb-notebook {
line-height: 1.5;
}
.repository.file.list #file-content #ipython-notebook .nb-stdout,
.repository.file.list #file-content #ipython-notebook .nb-stderr {
white-space: pre-wrap;
margin: 1em 0;
padding: 0.1em 0.5em;
}
.repository.file.list #file-content #ipython-notebook .nb-stderr {
background-color: #FAA;
}
.repository.file.list #file-content #ipython-notebook .nb-cell + .nb-cell {
margin-top: 0.5em;
}
.repository.file.list #file-content #ipython-notebook .nb-cell {
position: relative;
}
.repository.file.list #file-content #ipython-notebook .nb-cell.nb-heading-cell {
margin-top: 0.5em;
}
.repository.file.list #file-content #ipython-notebook .nb-raw-cell {
white-space: pre-wrap;
background-color: #f5f2f0;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
padding: 1em;
margin: .5em 0;
}
.repository.file.list #file-content #ipython-notebook .nb-input:before,
.repository.file.list #file-content #ipython-notebook .nb-output:before {
position: absolute;
font-family: monospace;
color: #999;
left: -7.5em;
width: 7em;
text-align: right;
}
.repository.file.list #file-content #ipython-notebook .nb-input:before {
content: "In [" attr(data-prompt-number) "]:";
}
.repository.file.list #file-content #ipython-notebook .nb-input pre {
background-color: #f7f7f7;
margin-right: 10px;
padding: 5px 10px;
}
.repository.file.list #file-content #ipython-notebook .nb-input pre code {
min-height: 18px;
line-height: 18px;
font-size: 14px;
}
.repository.file.list #file-content #ipython-notebook .nb-output:before {
content: "Out [" attr(data-prompt-number) "]:";
}
.repository.file.list #file-content #ipython-notebook .nb-output pre {
padding: 5px 10px;
font-size: 14px;
}
.repository.file.list #file-content #ipython-notebook .nb-output img {
max-width: 100%;
}
.repository.file.list #file-content #ipython-notebook .nb-output table {
border: 1px solid #000;
border-collapse: collapse;
}
.repository.file.list #file-content #ipython-notebook .nb-output th {
font-weight: bold;
}
.repository.file.list #file-content #ipython-notebook .nb-output th,
.repository.file.list #file-content #ipython-notebook .nb-output td {
border: 1px solid #000;
padding: 0.25em;
text-align: left;
vertical-align: middle;
border-collapse: collapse;
}
.repository.file.list #file-content #ipython-notebook .nb-markdown-cell {
margin-top: 10px;
margin-right: 10px;
padding: 10px;
}
.repository.file.list #file-content #ipython-notebook div[style="max-height:1000px;max-width:1500px;overflow:auto;"] {
max-height: none !important;
}
.repository.file.list #file-content .plain-text {
font-size: 14px;
padding: 10px 15px;

BIN
public/img/avatar_default.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 502 KiB

BIN
public/img/favicon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 28 KiB

BIN
public/img/gogs-large-resize.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 81 KiB

BIN
public/img/gogs-lg.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 164 KiB

192
public/img/gogs.svg

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="256"
height="256"
viewBox="0 0 67.733332 67.733335"
version="1.1"
id="svg4576"
inkscape:version="0.92.0 r15299"
sodipodi:docname="icon.svg"
enable-background="new"
inkscape:export-filename="favicon.png"
inkscape:export-xdpi="96.000008"
inkscape:export-ydpi="96.000008">
<defs
id="defs4570">
<linearGradient
inkscape:collect="always"
id="linearGradient5777">
<stop
style="stop-color:#dc493c;stop-opacity:1;"
offset="0"
id="stop5773" />
<stop
style="stop-color:#e8887f;stop-opacity:1"
offset="1"
id="stop5775" />
</linearGradient>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath5477">
<path
sodipodi:nodetypes="cccccccccccccccccccccscc"
inkscape:connector-curvature="0"
id="path5479"
d="m 33.86713,243.43265 c -10.880422,-1.1e-4 -19.700791,8.82024 -19.700666,19.70068 -1.25e-4,10.88042 8.820244,19.70077 19.700666,19.70066 6.169379,-0.0143 11.975716,-2.91813 15.688204,-7.84549 l 0.01791,0.0107 0.102743,-0.17755 c 0.918898,-1.23276 1.69033,-2.56885 2.29852,-3.98101 l -12.387966,-7.1527 c 0.01826,-0.18413 0.02739,-0.36903 0.02774,-0.55405 -1.9e-5,-3.17384 -2.572912,-5.74673 -5.746748,-5.74677 -3.173838,4e-5 -5.746734,2.57293 -5.746768,5.74677 3.4e-5,3.17384 2.57293,5.74673 5.746768,5.74677 1.179669,-0.002 2.32995,-0.36815 3.29469,-1.04706 l 7.43758,4.29458 c -2.657252,3.18291 -6.585981,5.02791 -10.73227,5.0401 -7.750976,-4e-5 -14.034355,-6.28343 -14.034391,-14.03439 3.6e-5,-7.75096 6.283415,-14.03435 14.034391,-14.03437 1.644346,0.009 3.274248,0.30783 4.815319,0.88147 0.432941,0.23108 0.919115,0.35231 1.413341,0.35241 1.619414,1.3e-4 2.932237,-1.27253 2.932112,-2.8424 -0.0013,-1.0165 -0.711451,-1.96286 -1.620721,-2.46915 0,0 -0.459666,-0.22667 -0.620453,-0.28907 -2.205641,-0.85587 -4.553725,-1.2854 -6.919582,-1.29957 z"
style="opacity:1;fill:none;fill-opacity:1;stroke:#be3529;stroke-width:0.66145831;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</clipPath>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5777"
id="linearGradient5779"
x1="52.65686"
y1="291.88074"
x2="9.1281252"
y2="234.42603"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5777"
id="linearGradient5874"
x1="58.605209"
y1="304.01141"
x2="7.1437502"
y2="224.76874"
gradientUnits="userSpaceOnUse" />
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter4520"
x="-0.0024505704"
width="1.0049011"
y="-0.0023514745"
height="1.0047029">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.038604687"
id="feGaussianBlur4522" />
</filter>
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter4528"
x="-0.0024479733"
width="1.0048959"
y="-0.0023538708"
height="1.0047077">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.064072276"
id="feGaussianBlur4530" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2"
inkscape:cx="44.531899"
inkscape:cy="180.4412"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:showpageshadow="false"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:snap-page="true" />
<metadata
id="metadata4573">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="shadowed"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-229.26665)"
style="display:inline">
<g
id="g5745">
<path
inkscape:connector-curvature="0"
style="opacity:1;fill:none;fill-opacity:1;stroke:#5b1a14;stroke-width:0.66145831;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4528)"
d="m 27.626438,230.93706 -1.796274,7.39227 a 26.550438,26.550438 0 0 0 -9.959928,5.78561 l -7.2442762,-2.1237 -6.1676142,10.68233 5.4923951,5.24175 a 26.550438,26.550438 0 0 0 -0.6345185,5.68571 26.550438,26.550438 0 0 0 0.6835439,5.83648 l -5.4683514,5.21769 6.1676178,10.68233 7.3006845,-2.14037 a 26.550438,26.550438 0 0 0 9.996944,5.75973 l 1.775923,7.30808 h 12.334305 l 1.796274,-7.39133 a 26.550438,26.550438 0 0 0 9.959946,-5.78561 l 7.244271,2.12277 6.167608,-10.68233 -5.492404,-5.24081 a 26.550438,26.550438 0 0 0 0.634521,-5.68663 26.550438,26.550438 0 0 0 -0.683546,-5.83557 l 5.468366,-5.2186 -6.167627,-10.68235 -7.300689,2.14037 a 26.550438,26.550438 0 0 0 -9.996012,-5.7588 l -1.775923,-7.30902 z"
id="path5311" />
<path
id="circle4730"
d="m 27.626438,230.46936 -1.796274,7.39227 a 26.550438,26.550438 0 0 0 -9.959928,5.78561 l -7.2442762,-2.1237 -6.1676142,10.68233 5.4923951,5.24175 a 26.550438,26.550438 0 0 0 -0.6345185,5.68571 26.550438,26.550438 0 0 0 0.6835439,5.83648 l -5.4683514,5.21769 6.1676178,10.68233 7.3006845,-2.14037 a 26.550438,26.550438 0 0 0 9.996944,5.75973 l 1.775923,7.30808 h 12.334305 l 1.796274,-7.39133 a 26.550438,26.550438 0 0 0 9.959946,-5.78561 l 7.244271,2.12277 6.167608,-10.68233 -5.492404,-5.24081 a 26.550438,26.550438 0 0 0 0.634521,-5.68663 26.550438,26.550438 0 0 0 -0.683546,-5.83557 l 5.468366,-5.2186 -6.167627,-10.68235 -7.300689,2.14037 a 26.550438,26.550438 0 0 0 -9.996012,-5.7588 l -1.775923,-7.30902 z"
style="opacity:1;fill:url(#linearGradient5874);fill-opacity:1;stroke:#be3529;stroke-width:0.66145831;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="cccccccccccccccccccccscc"
inkscape:connector-curvature="0"
id="circle4744"
d="m 33.867129,243.43265 c -10.880421,-1.1e-4 -19.70079,8.82024 -19.700665,19.70068 -1.25e-4,10.88042 8.820244,19.70077 19.700665,19.70066 6.169381,-0.0143 11.975716,-2.91813 15.688205,-7.84549 l 0.0179,0.0107 0.102741,-0.17755 c 0.9189,-1.23276 1.69033,-2.56885 2.29852,-3.98101 l -12.387966,-7.1527 c 0.01826,-0.18413 0.02739,-0.36903 0.02774,-0.55405 -1.8e-5,-3.17384 -2.572913,-5.74673 -5.746749,-5.74677 -3.173837,4e-5 -5.746732,2.57293 -5.746768,5.74677 3.6e-5,3.17384 2.572931,5.74673 5.746768,5.74677 1.17967,-0.002 2.329951,-0.36815 3.29469,-1.04706 l 7.43758,4.29458 c -2.657252,3.18291 -6.58598,5.02791 -10.73227,5.0401 -7.750975,-4e-5 -14.034354,-6.28343 -14.03439,-14.03439 3.6e-5,-7.75096 6.283415,-14.03435 14.03439,-14.03437 1.644347,0.009 3.27425,0.30783 4.815319,0.88147 0.432942,0.23108 0.919115,0.35231 1.413343,0.35241 1.619414,1.3e-4 2.932235,-1.27253 2.93211,-2.8424 -0.0014,-1.0165 -0.71145,-1.96286 -1.62072,-2.46915 0,0 -0.459665,-0.22667 -0.620452,-0.28907 -2.205643,-0.85587 -4.553726,-1.2854 -6.919583,-1.29957 z"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.66145831;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="opacity:1;fill:none;fill-opacity:1;stroke:#7f231c;stroke-width:0.66145831;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4520)"
d="m 33.94898,243.79513 c -10.88042,-1.4e-4 -19.705814,8.97349 -19.700665,19.70068 0.0049,10.72717 8.820244,19.70077 19.700665,19.70066 6.169381,-0.0143 11.976514,-2.94246 15.688205,-7.84549 l 0.0179,0.0107 0.102741,-0.17755 c 0.9187,-1.22666 1.690687,-2.57973 2.29852,-3.98101 l -12.387966,-7.1527 c 0.01795,-0.17465 0.02771,-0.37872 0.02774,-0.55405 -0.0055,-3.00718 -2.572913,-5.74673 -5.746749,-5.74677 -3.173837,4e-5 -5.752196,2.73959 -5.746768,5.74677 0.0055,3.00719 2.572931,5.74674 5.746768,5.74677 1.179664,-0.002 2.330958,-0.39886 3.29469,-1.04706 l 7.43758,4.29458 c -2.654987,3.11383 -6.586003,5.02861 -10.73227,5.0401 -7.750975,-3e-5 -14.026256,-6.5304 -14.03439,-14.03439 -0.0081,-7.50398 6.283415,-14.03434 14.03439,-14.03437 1.644325,0.01 3.27333,0.33588 4.815319,0.88147 0.43305,0.22778 0.918249,0.37874 1.413343,0.35241 1.622253,-0.0865 2.928924,-1.17153 2.93211,-2.8424 7.41e-4,-1.08181 -0.711334,-1.96639 -1.62072,-2.46915 0,0 -0.459602,-0.22859 -0.620452,-0.28907 -2.206511,-0.8294 -4.553766,-1.28416 -6.919583,-1.29957 z"
id="path5473"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccccccccccscc"
clip-path="url(#clipPath5477)" />
<path
style="opacity:1;fill:none;fill-opacity:1;stroke:#be3529;stroke-width:0.66145831;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 33.867129,243.43265 c -10.880421,-1.1e-4 -19.70079,8.82024 -19.700665,19.70068 -1.25e-4,10.88042 8.820244,19.70077 19.700665,19.70066 6.169381,-0.0143 11.975716,-2.91813 15.688205,-7.84549 l 0.0179,0.0107 0.102741,-0.17755 c 0.9189,-1.23276 1.69033,-2.56885 2.29852,-3.98101 l -12.387966,-7.1527 c 0.01826,-0.18413 0.02739,-0.36903 0.02774,-0.55405 -1.8e-5,-3.17384 -2.572913,-5.74673 -5.746749,-5.74677 -3.173837,4e-5 -5.746732,2.57293 -5.746768,5.74677 3.6e-5,3.17384 2.572931,5.74673 5.746768,5.74677 1.17967,-0.002 2.329951,-0.36815 3.29469,-1.04706 l 7.43758,4.29458 c -2.657252,3.18291 -6.58598,5.02791 -10.73227,5.0401 -7.750975,-4e-5 -14.034354,-6.28343 -14.03439,-14.03439 3.6e-5,-7.75096 6.283415,-14.03435 14.03439,-14.03437 1.644347,0.009 3.27425,0.30783 4.815319,0.88147 0.432942,0.23108 0.919115,0.35231 1.413343,0.35241 1.619414,1.3e-4 2.932235,-1.27253 2.93211,-2.8424 -0.0014,-1.0165 -0.71145,-1.96286 -1.62072,-2.46915 0,0 -0.459665,-0.22667 -0.620452,-0.28907 -2.205643,-0.85587 -4.553726,-1.2854 -6.919583,-1.29957 z"
id="path5733"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccccccccccscc" />
</g>
</g>
<g
transform="translate(0,-229.26665)"
id="g5771"
inkscape:groupmode="layer"
inkscape:label="plain"
style="display:none">
<g
id="g5769">
<path
inkscape:connector-curvature="0"
style="opacity:1;fill:url(#linearGradient5779);fill-opacity:1;stroke:#be3529;stroke-width:0.66145831;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 27.626438,230.46936 -1.796274,7.39227 a 26.550438,26.550438 0 0 0 -9.959928,5.78561 l -7.2442762,-2.1237 -6.1676142,10.68233 5.4923951,5.24175 a 26.550438,26.550438 0 0 0 -0.6345185,5.68571 26.550438,26.550438 0 0 0 0.6835439,5.83648 l -5.4683514,5.21769 6.1676178,10.68233 7.3006845,-2.14037 a 26.550438,26.550438 0 0 0 9.996944,5.75973 l 1.775923,7.30808 h 12.334305 l 1.796274,-7.39133 a 26.550438,26.550438 0 0 0 9.959946,-5.78561 l 7.244271,2.12277 6.167608,-10.68233 -5.492404,-5.24081 a 26.550438,26.550438 0 0 0 0.634521,-5.68663 26.550438,26.550438 0 0 0 -0.683546,-5.83557 l 5.468366,-5.2186 -6.167627,-10.68235 -7.300689,2.14037 a 26.550438,26.550438 0 0 0 -9.996012,-5.7588 l -1.775923,-7.30902 z"
id="path5761" />
<path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.66145831;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 33.867129,243.43265 c -10.880421,-1.1e-4 -19.70079,8.82024 -19.700665,19.70068 -1.25e-4,10.88042 8.820244,19.70077 19.700665,19.70066 6.169381,-0.0143 11.975716,-2.91813 15.688205,-7.84549 l 0.0179,0.0107 0.102741,-0.17755 c 0.9189,-1.23276 1.69033,-2.56885 2.29852,-3.98101 l -12.387966,-7.1527 c 0.01826,-0.18413 0.02739,-0.36903 0.02774,-0.55405 -1.8e-5,-3.17384 -2.572913,-5.74673 -5.746749,-5.74677 -3.173837,4e-5 -5.746732,2.57293 -5.746768,5.74677 3.6e-5,3.17384 2.572931,5.74673 5.746768,5.74677 1.17967,-0.002 2.329951,-0.36815 3.29469,-1.04706 l 7.43758,4.29458 c -2.657252,3.18291 -6.58598,5.02791 -10.73227,5.0401 -7.750975,-4e-5 -14.034354,-6.28343 -14.03439,-14.03439 3.6e-5,-7.75096 6.283415,-14.03435 14.03439,-14.03437 1.644347,0.009 3.27425,0.30783 4.815319,0.88147 0.432942,0.23108 0.919115,0.35231 1.413343,0.35241 1.619414,1.3e-4 2.932235,-1.27253 2.93211,-2.8424 -0.0014,-1.0165 -0.71145,-1.96286 -1.62072,-2.46915 0,0 -0.459665,-0.22667 -0.620452,-0.28907 -2.205643,-0.85587 -4.553726,-1.2854 -6.919583,-1.29957 z"
id="path5763"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccccccccccscc" />
<path
sodipodi:nodetypes="cccccccccccccccccccccscc"
inkscape:connector-curvature="0"
id="path5767"
d="m 33.867129,243.43265 c -10.880421,-1.1e-4 -19.70079,8.82024 -19.700665,19.70068 -1.25e-4,10.88042 8.820244,19.70077 19.700665,19.70066 6.169381,-0.0143 11.975716,-2.91813 15.688205,-7.84549 l 0.0179,0.0107 0.102741,-0.17755 c 0.9189,-1.23276 1.69033,-2.56885 2.29852,-3.98101 l -12.387966,-7.1527 c 0.01826,-0.18413 0.02739,-0.36903 0.02774,-0.55405 -1.8e-5,-3.17384 -2.572913,-5.74673 -5.746749,-5.74677 -3.173837,4e-5 -5.746732,2.57293 -5.746768,5.74677 3.6e-5,3.17384 2.572931,5.74673 5.746768,5.74677 1.17967,-0.002 2.329951,-0.36815 3.29469,-1.04706 l 7.43758,4.29458 c -2.657252,3.18291 -6.58598,5.02791 -10.73227,5.0401 -7.750975,-4e-5 -14.034354,-6.28343 -14.03439,-14.03439 3.6e-5,-7.75096 6.283415,-14.03435 14.03439,-14.03437 1.644347,0.009 3.27425,0.30783 4.815319,0.88147 0.432942,0.23108 0.919115,0.35231 1.413343,0.35241 1.619414,1.3e-4 2.932235,-1.27253 2.93211,-2.8424 -0.0014,-1.0165 -0.71145,-1.96286 -1.62072,-2.46915 0,0 -0.459665,-0.22667 -0.620452,-0.28907 -2.205643,-0.85587 -4.553726,-1.2854 -6.919583,-1.29957 z"
style="opacity:1;fill:none;fill-opacity:1;stroke:#be3529;stroke-width:0.66145831;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

23
public/js/gogs.js

@ -228,22 +228,21 @@ function initInstall() {
return;
}
var mysqlDefault = '127.0.0.1:3306';
var postgresDefault = '127.0.0.1:5432';
var dbDefaults = {
"MySQL": "127.0.0.1:3306",
"PostgreSQL": "127.0.0.1:5432",
"MSSQL": "127.0.0.1, 1433"
};
$('#sqlite_settings').hide();
$('#sql_settings').show();
if (dbType === "PostgreSQL") {
$('#pgsql_settings').show();
if ($('#db_host').val() == mysqlDefault) {
$('#db_host').val(postgresDefault);
}
} else {
$('#pgsql_settings').hide();
if ($('#db_host').val() == postgresDefault) {
$('#db_host').val(mysqlDefault);
$('#pgsql_settings').toggle(dbType === "PostgreSQL");
$.each(dbDefaults, function(type, defaultHost) {
if ($('#db_host').val() == defaultHost) {
$('#db_host').val(dbDefaults[dbType]);
return false;
}
}
});
});
// TODO: better handling of exclusive relations.

1
public/less/_home.less

@ -2,6 +2,7 @@
padding-bottom: @footer-margin * 2;
.logo {
max-width: 220px;
margin-bottom: 20px;
}
.hero {
h1, h2 {

105
public/less/_repository.less

@ -251,6 +251,111 @@
}
}
#ipython-notebook {
margin-left: 95px;
padding-top: 1px;
.nb-notebook {
line-height: 1.5;
}
.nb-stdout, .nb-stderr {
white-space: pre-wrap;
margin: 1em 0;
padding: 0.1em 0.5em;
}
.nb-stderr {
background-color: #FAA;
}
.nb-cell + .nb-cell {
margin-top: 0.5em;
}
.nb-cell {
position: relative;
&.nb-heading-cell {
margin-top: 0.5em;
}
}
.nb-raw-cell {
white-space: pre-wrap;
background-color: #f5f2f0;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
padding: 1em;
margin: .5em 0;
}
.nb-input:before,
.nb-output:before {
position: absolute;
font-family: monospace;
color: #999;
left: -7.5em;
width: 7em;
text-align: right;
}
.nb-input {
&:before {
content: "In [" attr(data-prompt-number) "]:";
}
pre {
background-color: #f7f7f7;
margin-right: 10px;
padding: 5px 10px;
code {
min-height: 18px;
line-height: 18px;
font-size: 14px;
}
}
}
.nb-output{
&:before {
content: "Out [" attr(data-prompt-number) "]:";
}
pre {
padding: 5px 10px;
font-size: 14px;
}
img {
max-width: 100%;
}
table {
border: 1px solid #000;
border-collapse: collapse;
}
th {
font-weight: bold;
}
th,
td {
border: 1px solid #000;
padding: 0.25em;
text-align: left;
vertical-align: middle;
border-collapse: collapse;
}
}
.nb-markdown-cell {
margin-top: 10px;
margin-right: 10px;
padding: 10px;
}
// Fix pandas dataframe formatting
div[style="max-height:1000px;max-width:1500px;overflow:auto;"] {
max-height: none !important;
}
}
.plain-text {
font-size: 14px;
padding: 10px 15px;

6
public/plugins/marked-0.3.6/marked.min.js vendored

File diff suppressed because one or more lines are too long

1
public/plugins/notebookjs-0.2.6/notebook.min.js vendored

File diff suppressed because one or more lines are too long

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

Loading…
Cancel
Save