From 263d4093260707c6249eecb52ad52a0205e61351 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sat, 4 Oct 2014 17:15:22 -0400 Subject: Basic xss prevention --- modules/base/markdown.go | 48 +++++++++++++++++++++++++----------------------- modules/base/tool.go | 27 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 23 deletions(-) (limited to 'modules') diff --git a/modules/base/markdown.go b/modules/base/markdown.go index a3db15df..cb083200 100644 --- a/modules/base/markdown.go +++ b/modules/base/markdown.go @@ -13,7 +13,8 @@ import ( "regexp" "strings" - "github.com/gogits/gfm" + "github.com/russross/blackfriday" + "github.com/gogits/gogs/modules/setting" ) @@ -74,7 +75,7 @@ func IsReadmeFile(name string) bool { } type CustomRender struct { - gfm.Renderer + blackfriday.Renderer urlPrefix string } @@ -154,39 +155,40 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte { func RenderRawMarkdown(body []byte, urlPrefix string) []byte { htmlFlags := 0 - // htmlFlags |= gfm.HTML_USE_XHTML - // htmlFlags |= gfm.HTML_USE_SMARTYPANTS - // htmlFlags |= gfm.HTML_SMARTYPANTS_FRACTIONS - // htmlFlags |= gfm.HTML_SMARTYPANTS_LATEX_DASHES - // htmlFlags |= gfm.HTML_SKIP_HTML - htmlFlags |= gfm.HTML_SKIP_STYLE - htmlFlags |= gfm.HTML_SKIP_SCRIPT - htmlFlags |= gfm.HTML_GITHUB_BLOCKCODE - htmlFlags |= gfm.HTML_OMIT_CONTENTS - // htmlFlags |= gfm.HTML_COMPLETE_PAGE + // htmlFlags |= blackfriday.HTML_USE_XHTML + // htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS + // htmlFlags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS + // htmlFlags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES + // htmlFlags |= blackfriday.HTML_SKIP_HTML + htmlFlags |= blackfriday.HTML_SKIP_STYLE + // htmlFlags |= blackfriday.HTML_SKIP_SCRIPT + // htmlFlags |= blackfriday.HTML_GITHUB_BLOCKCODE + htmlFlags |= blackfriday.HTML_OMIT_CONTENTS + // htmlFlags |= blackfriday.HTML_COMPLETE_PAGE renderer := &CustomRender{ - Renderer: gfm.HtmlRenderer(htmlFlags, "", ""), + Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""), urlPrefix: urlPrefix, } // set up the parser extensions := 0 - extensions |= gfm.EXTENSION_NO_INTRA_EMPHASIS - extensions |= gfm.EXTENSION_TABLES - extensions |= gfm.EXTENSION_FENCED_CODE - extensions |= gfm.EXTENSION_AUTOLINK - extensions |= gfm.EXTENSION_STRIKETHROUGH - extensions |= gfm.EXTENSION_HARD_LINE_BREAK - extensions |= gfm.EXTENSION_SPACE_HEADERS - extensions |= gfm.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK - - body = gfm.Markdown(body, renderer, extensions) + extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS + extensions |= blackfriday.EXTENSION_TABLES + extensions |= blackfriday.EXTENSION_FENCED_CODE + extensions |= blackfriday.EXTENSION_AUTOLINK + extensions |= blackfriday.EXTENSION_STRIKETHROUGH + extensions |= blackfriday.EXTENSION_HARD_LINE_BREAK + extensions |= blackfriday.EXTENSION_SPACE_HEADERS + extensions |= blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK + + body = blackfriday.Markdown(body, renderer, extensions) return body } func RenderMarkdown(rawBytes []byte, urlPrefix string) []byte { body := RenderSpecialLink(rawBytes, urlPrefix) body = RenderRawMarkdown(body, urlPrefix) + body = XSS(body) return body } diff --git a/modules/base/tool.go b/modules/base/tool.go index b4083d09..38fd1e21 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -14,6 +14,7 @@ import ( "hash" "html/template" "math" + "regexp" "strings" "time" @@ -446,3 +447,29 @@ func DateFormat(t time.Time, format string) string { format = replacer.Replace(format) return t.Format(format) } + +type xssFilter struct { + reg *regexp.Regexp + repl []byte +} + +var ( + whiteSpace = []byte(" ") + xssFilters = []xssFilter{ + {regexp.MustCompile(`\ [ONon]\w*=["]*`), whiteSpace}, + {regexp.MustCompile(`<[SCRIPTscript]{6}`), whiteSpace}, + {regexp.MustCompile(`=[` + "`" + `'"]*[JAVASCRIPTjavascript \t\0 ]*:`), whiteSpace}, + } +) + +// XSS goes through all the XSS filters to make user input content as safe as possible. +func XSS(in []byte) []byte { + for _, filter := range xssFilters { + in = filter.reg.ReplaceAll(in, filter.repl) + } + return in +} + +func XSSString(in string) string { + return string(XSS([]byte(in))) +} -- cgit v1.2.3 From 67c44b7d27a15dd415651ccb9a2ad486a3738183 Mon Sep 17 00:00:00 2001 From: Linquize Date: Sat, 4 Oct 2014 00:25:54 +0800 Subject: If git >= 2.0, sort tags in descending order by version number --- modules/git/repo_tag.go | 12 ++++++++++++ modules/git/version.go | 4 ++++ 2 files changed, 16 insertions(+) (limited to 'modules') diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go index 77ae3db0..ed994d48 100644 --- a/modules/git/repo_tag.go +++ b/modules/git/repo_tag.go @@ -22,6 +22,9 @@ func (repo *Repository) IsTagExist(tagName string) bool { // GetTags returns all tags of given repository. func (repo *Repository) GetTags() ([]string, error) { + if gitVer.AtLeast(MustParseVersion("2.0.0")) { + return repo.getTagsReversed() + } stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l") if err != nil { return nil, errors.New(stderr) @@ -30,6 +33,15 @@ func (repo *Repository) GetTags() ([]string, error) { return tags[:len(tags)-1], nil } +func (repo *Repository) getTagsReversed() ([]string, error) { + stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l", "--sort=-v:refname") + if err != nil { + return nil, errors.New(stderr) + } + tags := strings.Split(stdout, "\n") + return tags[:len(tags)-1], nil +} + func (repo *Repository) CreateTag(tagName, idStr string) error { _, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", tagName, idStr) if err != nil { diff --git a/modules/git/version.go b/modules/git/version.go index 9908d11e..b535521e 100644 --- a/modules/git/version.go +++ b/modules/git/version.go @@ -74,6 +74,10 @@ func (v *Version) LessThan(that *Version) bool { return v.Compare(that) < 0 } +func (v *Version) AtLeast(that *Version) bool { + return v.Compare(that) >= 0 +} + // GetVersion returns current Git version installed. func GetVersion() (*Version, error) { if gitVer != nil { -- cgit v1.2.3 From 91e5c24a314170490139d661a921d94b8ab0555b Mon Sep 17 00:00:00 2001 From: Unknwon Date: Mon, 6 Oct 2014 10:36:16 -0400 Subject: Fix #522 --- gogs.go | 2 +- modules/auth/repo_form.go | 2 +- templates/.VERSION | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'modules') diff --git a/gogs.go b/gogs.go index 12f142ab..c0f1d1e2 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.5.4.1004 Beta" +const APP_VER = "0.5.4.1005 Beta" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 233f7b10..df5b8b69 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -102,7 +102,7 @@ func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs *binding.Errors, // \/ \/ \/ type CreateIssueForm struct { - IssueName string `form:"title" binding:"Required;MaxSize(50)"` + IssueName string `form:"title" binding:"Required;MaxSize(255)"` MilestoneId int64 `form:"milestoneid"` AssigneeId int64 `form:"assigneeid"` Labels string `form:"labels"` diff --git a/templates/.VERSION b/templates/.VERSION index 776ae02a..7760f35b 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.5.4.1004 Beta \ No newline at end of file +0.5.4.1005 Beta \ No newline at end of file -- cgit v1.2.3 From 64c68220d203cb07be001184cde4b35d4b503344 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Mon, 6 Oct 2014 17:50:00 -0400 Subject: Fix #264 --- README.md | 4 +- README_ZH.md | 4 +- cmd/web.go | 6 ++ conf/app.ini | 2 + conf/locale/locale_en-US.ini | 6 ++ conf/locale/locale_zh-CN.ini | 6 ++ gogs.go | 2 +- modules/git/hooks.go | 111 ++++++++++++++++++++++++++++++ modules/git/utils.go | 21 ++++++ modules/middleware/repo.go | 10 +++ modules/setting/setting.go | 2 + public/ng/css/ui.css | 4 ++ public/ng/less/ui/form.less | 23 +++---- routers/repo/setting.go | 54 +++++++++++++++ templates/.VERSION | 2 +- templates/repo/settings/githook_edit.tmpl | 41 +++++++++++ templates/repo/settings/githooks.tmpl | 37 ++++++++++ templates/repo/settings/nav.tmpl | 1 + 18 files changed, 317 insertions(+), 19 deletions(-) create mode 100644 modules/git/hooks.go create mode 100644 templates/repo/settings/githook_edit.tmpl create mode 100644 templates/repo/settings/githooks.tmpl (limited to 'modules') diff --git a/README.md b/README.md index b7ff264e..826d9b9e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Gogs(Go Git Service) is a painless self-hosted Git Service written in Go. ![Demo](https://gowalker.org/public/gogs_demo.gif) -##### Current version: 0.5.4 Beta +##### Current version: 0.5.5 Beta ### NOTICES @@ -44,7 +44,7 @@ The goal of this project is to make the easiest, fastest and most painless way t - Slack webhook integration - Supports MySQL, PostgreSQL and SQLite3 - Social account login(GitHub, Google, QQ, Weibo) -- Multi-language support(English, Chinese, Germany, French etc.) +- Multi-language support(English, Chinese, Germany, French, Dutch etc.) ## System Requirements diff --git a/README_ZH.md b/README_ZH.md index d704053f..3da3b6be 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。 ![Demo](https://gowalker.org/public/gogs_demo.gif) -##### 当前版本:0.5.4 Beta +##### 当前版本:0.5.5 Beta ## 开发目的 @@ -35,7 +35,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自 - Slack Web 钩子集成 - 支持 MySQL、PostgreSQL 以及 SQLite3 数据库 - 社交帐号登录(GitHub、Google、QQ、微博) -- 多语言支持(英文、简体中文、德语、法语等等) +- 多语言支持(英文、简体中文、德语、法语、荷兰语等等) ## 系统要求 diff --git a/cmd/web.go b/cmd/web.go index 72a58bc9..810e36d3 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -313,6 +313,12 @@ func runWeb(*cli.Context) { r.Get("/hooks/:id", repo.WebHooksEdit) r.Post("/hooks/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) r.Post("/hooks/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) + + m.Group("/hooks/git", func(r *macaron.Router) { + r.Get("", repo.GitHooks) + r.Get("/:name", repo.GitHooksEdit) + r.Post("/:name", repo.GitHooksEditPost) + }, middleware.GitHookService()) }) }, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner) diff --git a/conf/app.ini b/conf/app.ini index 224f45dd..1ea92e9f 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -70,6 +70,8 @@ ENABLE_CACHE_AVATAR = false ENABLE_NOTIFY_MAIL = false ; More detail: https://github.com/gogits/gogs/issues/165 ENABLE_REVERSE_PROXY_AUTHENTICATION = false +; Repository Git hooks +ENABLE_GIT_HOOKS = false [webhook] ; Cron task interval in minutes diff --git a/conf/locale/locale_en-US.ini b/conf/locale/locale_en-US.ini index 8e68fb98..4fc8c359 100644 --- a/conf/locale/locale_en-US.ini +++ b/conf/locale/locale_en-US.ini @@ -287,6 +287,7 @@ settings = Settings settings.options = Options settings.collaboration = Collaboration settings.hooks = Webhooks +settings.githooks = Git Hooks settings.deploy_keys = Deploy Keys settings.basic_settings = Basic Settings settings.danger_zone = Danger Zone @@ -310,6 +311,11 @@ settings.add_collaborator_success = New collaborator has been added. settings.remove_collaborator_success = Collaborator has been removed. settings.add_webhook = Add Webhook settings.hooks_desc = Webhooks allow external services to be notified when certain events happen on Gogs. When the specified events happen, we'll send a POST request to each of the URLs you provide. Learn more in our Webhooks Guide. +settings.githooks_desc = Git Hooks are powered by Git itself, you can edit files of supported hooks in the list below to apply custom operations. +settings.githook_edit_desc = If hook is not active, sample content will be presented. Leave content to be blank will disable this hook. +settings.githook_name = Hook Name +settings.githook_content = Hook Content +settings.update_githook = Update Hook settings.remove_hook_success = Webhook has been removed. settings.add_webhook_desc = We’ll send a POST request to the URL below with details of any subscribed events. You can also specify which data format you'd like to receive (JSON, x-www-form-urlencoded, etc). More information can be found in Webhooks Guide. settings.payload_url = Payload URL diff --git a/conf/locale/locale_zh-CN.ini b/conf/locale/locale_zh-CN.ini index 360bf4bc..dc454848 100644 --- a/conf/locale/locale_zh-CN.ini +++ b/conf/locale/locale_zh-CN.ini @@ -287,6 +287,7 @@ settings = 仓库设置 settings.options = 基本设置 settings.collaboration = 管理协作者 settings.hooks = 管理 Web 钩子 +settings.githooks = 管理 Git 钩子 settings.deploy_keys = 管理部署密钥 settings.basic_settings = 基本设置 settings.danger_zone = 危险操作区 @@ -312,6 +313,11 @@ settings.add_webhook = 添加 Web 钩子 settings.hooks_desc = Web 钩子允许您设定在 Gogs 上发生指定事件时对指定 URL 发送 POST 通知。查看 Webhooks 文档 获取更多信息。 settings.remove_hook_success = Web 钩子删除成功! settings.add_webhook_desc = 我们会通过 POST 请求将订阅事件信息发送至向指定 URL 地址。您可以设置不同的数据接收方式(JSON 或 x-www-form-urlencoded)。 请查阅 Webhooks 文档 获取更多信息。 +settings.githooks_desc = Git 钩子是由 Git 本身提供的功能,以下为 Gogs 所支持的钩子列表。 +settings.githook_edit_desc = 如果钩子未启动,则会显示样例文件中的内容。如果想要删除某个钩子,则提交空白文本即可。 +settings.githook_name = 钩子名称 +settings.githook_content = 钩子文本 +settings.update_githook = 更新钩子设置 settings.payload_url = 推送地址 settings.content_type = 数据格式 settings.secret = 密钥文本 diff --git a/gogs.go b/gogs.go index c0f1d1e2..43705061 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.5.4.1005 Beta" +const APP_VER = "0.5.5.1006 Beta" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/modules/git/hooks.go b/modules/git/hooks.go new file mode 100644 index 00000000..b8d15e5e --- /dev/null +++ b/modules/git/hooks.go @@ -0,0 +1,111 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package git + +import ( + "errors" + "io/ioutil" + "os" + "path" + "strings" +) + +// hookNames is a list of Git hooks' name that are supported. +var hookNames = []string{ + "pre-applypatch", + "applypatch-msg", + "prepare-commit-msg", + "commit-msg", + "pre-commit", + "pre-rebase", + "post-commit", + "post-receive", + "post-update", +} + +var ( + ErrNotValidHook = errors.New("not a valid Git hook") +) + +// IsValidHookName returns true if given name is a valid Git hook. +func IsValidHookName(name string) bool { + for _, hn := range hookNames { + if hn == name { + return true + } + } + return false +} + +// Hook represents a Git hook. +type Hook struct { + name string + IsActive bool // Indicates whether repository has this hook. + Content string // Content of hook if it's active. + Sample string // Sample content from Git. + path string // Hook file path. +} + +// GetHook returns a Git hook by given name and repository. +func GetHook(repoPath, name string) (*Hook, error) { + if !IsValidHookName(name) { + return nil, ErrNotValidHook + } + h := &Hook{ + name: name, + path: path.Join(repoPath, "hooks", name), + } + if isFile(h.path) { + data, err := ioutil.ReadFile(h.path) + if err != nil { + return nil, err + } + h.IsActive = true + h.Content = string(data) + } else if isFile(h.path + ".sample") { + data, err := ioutil.ReadFile(h.path + ".sample") + if err != nil { + return nil, err + } + h.Sample = string(data) + } + return h, nil +} + +func (h *Hook) Name() string { + return h.name +} + +// Update updates hook settings. +func (h *Hook) Update() error { + if len(strings.TrimSpace(h.Content)) == 0 { + return os.Remove(h.path) + } + return ioutil.WriteFile(h.path, []byte(h.Content), os.ModePerm) +} + +// ListHooks returns a list of Git hooks of given repository. +func ListHooks(repoPath string) (_ []*Hook, err error) { + if !isDir(path.Join(repoPath, "hooks")) { + return nil, errors.New("hooks path does not exist") + } + + hooks := make([]*Hook, len(hookNames)) + for i, name := range hookNames { + hooks[i], err = GetHook(repoPath, name) + if err != nil { + return nil, err + } + } + return hooks, nil +} + +func (repo *Repository) GetHook(name string) (*Hook, error) { + return GetHook(repo.Path, name) +} + +func (repo *Repository) Hooks() ([]*Hook, error) { + return ListHooks(repo.Path) +} diff --git a/modules/git/utils.go b/modules/git/utils.go index 26eef231..6abbca55 100644 --- a/modules/git/utils.go +++ b/modules/git/utils.go @@ -7,6 +7,7 @@ package git import ( "bytes" "container/list" + "os" "path/filepath" "strings" ) @@ -46,3 +47,23 @@ func RefEndName(refStr string) string { func filepathFromSHA1(rootdir, sha1 string) string { return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:]) } + +// isDir returns true if given path is a directory, +// or returns false when it's a file or does not exist. +func isDir(dir string) bool { + f, e := os.Stat(dir) + if e != nil { + return false + } + return f.IsDir() +} + +// isFile returns true if given path is a file, +// or returns false when it's a directory or does not exist. +func isFile(filePath string) bool { + f, e := os.Stat(filePath) + if e != nil { + return false + } + return !f.IsDir() +} diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index c6250f6d..78af58ea 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -308,3 +308,13 @@ func RequireTrueOwner() macaron.Handler { } } } + +// GitHookService checks if repsitory Git hooks service has been enabled. +func GitHookService() macaron.Handler { + return func(ctx *Context) { + if !setting.Service.EnableGitHooks { + ctx.Handle(404, "GitHookService", nil) + return + } + } +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 67e48108..b8fc4dec 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -275,6 +275,7 @@ var Service struct { LdapAuth bool ActiveCodeLives int ResetPwdCodeLives int + EnableGitHooks bool } func newService() { @@ -284,6 +285,7 @@ func newService() { Service.RequireSignInView = Cfg.MustBool("service", "REQUIRE_SIGNIN_VIEW") Service.EnableCacheAvatar = Cfg.MustBool("service", "ENABLE_CACHE_AVATAR") Service.EnableReverseProxyAuth = Cfg.MustBool("service", "ENABLE_REVERSE_PROXY_AUTHENTICATION") + Service.EnableGitHooks = Cfg.MustBool("service", "ENABLE_GIT_HOOKS") } var logLevels = map[string]string{ diff --git a/public/ng/css/ui.css b/public/ng/css/ui.css index 9c3c8ded..3ea6fcd4 100644 --- a/public/ng/css/ui.css +++ b/public/ng/css/ui.css @@ -480,6 +480,10 @@ dt { .ipt-large { font-size: 14.4px; } +.ipt-textarea { + height: auto !important; + width: auto; +} .ipt-disabled, input[disabled] { background-color: #f2f2f2 !important; diff --git a/public/ng/less/ui/form.less b/public/ng/less/ui/form.less index 4a681994..b3de4273 100644 --- a/public/ng/less/ui/form.less +++ b/public/ng/less/ui/form.less @@ -116,25 +116,24 @@ } // input form elements - .ipt { - &:focus { - border-color: @iptFocusBorderColor; - } + &:focus { + border-color: @iptFocusBorderColor; + } } - .ipt-radius { - border-radius: .25em; + border-radius: .25em; } - .ipt-small { - font-size: .8*@baseFontSize; + font-size: .8*@baseFontSize; } - .ipt-large { - font-size: 1.2*@baseFontSize; + font-size: 1.2*@baseFontSize; +} +.ipt-textarea { + height: auto !important; + width: auto; } - .ipt-disabled, input[disabled] { background-color: @iptDisabledColor !important; @@ -144,14 +143,12 @@ input[disabled] { color: #888; cursor: not-allowed; } - .ipt-readonly, input[readonly] { &:focus { background-color: @iptDisabledColor !important; } } - .ipt-error { border-color: @iptErrorBorderColor !important; background-color: @iptErrorFocusColor !important; diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 48089787..0e58029e 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -16,6 +16,7 @@ import ( "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/git" "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/mailer" "github.com/gogits/gogs/modules/middleware" @@ -26,6 +27,8 @@ const ( SETTINGS_OPTIONS base.TplName = "repo/settings/options" COLLABORATION base.TplName = "repo/settings/collaboration" HOOKS base.TplName = "repo/settings/hooks" + GITHOOKS base.TplName = "repo/settings/githooks" + GITHOOK_EDIT base.TplName = "repo/settings/githook_edit" HOOK_NEW base.TplName = "repo/settings/hook_new" ORG_HOOK_NEW base.TplName = "org/settings/hook_new" ) @@ -591,3 +594,54 @@ func getOrgRepoCtx(ctx *middleware.Context) (*OrgRepoCtx, error) { return &OrgRepoCtx{}, errors.New("Unable to set OrgRepo context") } } + +func GitHooks(ctx *middleware.Context) { + ctx.Data["Title"] = ctx.Tr("repo.settings") + ctx.Data["PageIsSettingsGitHooks"] = true + + hooks, err := ctx.Repo.GitRepo.Hooks() + if err != nil { + ctx.Handle(500, "Hooks", err) + return + } + ctx.Data["Hooks"] = hooks + + ctx.HTML(200, GITHOOKS) +} + +func GitHooksEdit(ctx *middleware.Context) { + ctx.Data["Title"] = ctx.Tr("repo.settings") + ctx.Data["PageIsSettingsGitHooks"] = true + + name := ctx.Params(":name") + hook, err := ctx.Repo.GitRepo.GetHook(name) + if err != nil { + if err == git.ErrNotValidHook { + ctx.Handle(404, "GetHook", err) + } else { + ctx.Handle(500, "GetHook", err) + } + return + } + ctx.Data["Hook"] = hook + ctx.HTML(200, GITHOOK_EDIT) +} + +func GitHooksEditPost(ctx *middleware.Context) { + name := ctx.Params(":name") + hook, err := ctx.Repo.GitRepo.GetHook(name) + if err != nil { + if err == git.ErrNotValidHook { + ctx.Handle(404, "GetHook", err) + } else { + ctx.Handle(500, "GetHook", err) + } + return + } + hook.Content = ctx.Query("content") + if err = hook.Update(); err != nil { + ctx.Handle(500, "hook.Update", err) + return + } + ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks/git") +} diff --git a/templates/.VERSION b/templates/.VERSION index 7760f35b..e551dcfe 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.5.4.1005 Beta \ No newline at end of file +0.5.5.1006 Beta \ No newline at end of file diff --git a/templates/repo/settings/githook_edit.tmpl b/templates/repo/settings/githook_edit.tmpl new file mode 100644 index 00000000..23fc26e3 --- /dev/null +++ b/templates/repo/settings/githook_edit.tmpl @@ -0,0 +1,41 @@ +{{template "ng/base/head" .}} +{{template "ng/base/header" .}} +
+ {{template "repo/header" .}} +
+
+ {{template "repo/settings/nav" .}} +
+
+ {{template "ng/base/alert" .}} +
+
+
+ {{.i18n.Tr "repo.settings.githooks"}} +
+
+ {{.CsrfTokenHtml}} +
{{.i18n.Tr "repo.settings.githook_edit_desc"}}
+ {{with .Hook}} +
+ + +
+
+ + +
+
+ + +
+ {{end}} +
+
+
+
+
+
+
+
+{{template "ng/base/footer" .}} \ No newline at end of file diff --git a/templates/repo/settings/githooks.tmpl b/templates/repo/settings/githooks.tmpl new file mode 100644 index 00000000..a059b0e7 --- /dev/null +++ b/templates/repo/settings/githooks.tmpl @@ -0,0 +1,37 @@ +{{template "ng/base/head" .}} +{{template "ng/base/header" .}} +
+ {{template "repo/header" .}} +
+
+ {{template "repo/settings/nav" .}} +
+
+ {{template "ng/base/alert" .}} +
+
+
+ {{.i18n.Tr "repo.settings.githooks"}} +
+
    +
  • {{.i18n.Tr "repo.settings.githooks_desc" | Str2html}}
  • + {{range .Hooks}} +
  • + {{if .IsActive}} + + {{else}} + + {{end}} + {{.Name}} + +
  • + {{end}} +
+
+
+
+
+
+
+
+{{template "ng/base/footer" .}} \ No newline at end of file diff --git a/templates/repo/settings/nav.tmpl b/templates/repo/settings/nav.tmpl index ef0765fe..16128f01 100644 --- a/templates/repo/settings/nav.tmpl +++ b/templates/repo/settings/nav.tmpl @@ -5,6 +5,7 @@
  • {{.i18n.Tr "repo.settings.options"}}
  • {{.i18n.Tr "repo.settings.collaboration"}}
  • {{.i18n.Tr "repo.settings.hooks"}}
  • +
  • {{.i18n.Tr "repo.settings.githooks"}}
  • -- cgit v1.2.3 From 1e1f9e716660533dc3c570b51006e4e36b2c3a7f Mon Sep 17 00:00:00 2001 From: Unknwon Date: Mon, 6 Oct 2014 19:12:52 -0400 Subject: Update with macaron --- .bra.toml | 4 ++-- cmd/web.go | 10 +++++++--- modules/middleware/context.go | 2 -- routers/org/setting.go | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) (limited to 'modules') diff --git a/.bra.toml b/.bra.toml index a5afa276..a5fcdf6e 100644 --- a/.bra.toml +++ b/.bra.toml @@ -11,7 +11,7 @@ watch_dirs = [ watch_exts = [".go", ".ini"] build_delay = 1500 cmds = [ - ["go", "install"], - ["go", "build"], + ["go", "install", "-tags", "sqlite"], + ["go", "build", "-tags", "sqlite"], ["./gogs", "web"] ] \ No newline at end of file diff --git a/cmd/web.go b/cmd/web.go index 810e36d3..201eb48f 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -61,10 +61,14 @@ func checkVersion() { log.Fatal(4, "Binary and template file version does not match, did you forget to recompile?") } - // Macaron. + // Check dependency version. macaronVer := git.MustParseVersion(strings.Join(strings.Split(macaron.Version(), ".")[:3], ".")) - if macaronVer.LessThan(git.MustParseVersion("0.1.8")) { - log.Fatal(4, "Macaron version does not match, did you forget to update?(github.com/Unknwon/macaron)") + if macaronVer.LessThan(git.MustParseVersion("0.2.0")) { + log.Fatal(4, "Macaron version is too old, did you forget to update?(github.com/Unknwon/macaron)") + } + i18nVer := git.MustParseVersion(i18n.Version()) + if i18nVer.LessThan(git.MustParseVersion("0.0.1")) { + log.Fatal(4, "i18n version is too old, did you forget to update?(github.com/macaron-contrib/i18n)") } } diff --git a/modules/middleware/context.go b/modules/middleware/context.go index 90716d2c..1d9f5738 100644 --- a/modules/middleware/context.go +++ b/modules/middleware/context.go @@ -29,7 +29,6 @@ import ( // Context represents context of a request. type Context struct { *macaron.Context - i18n.Locale Cache cache.Cache csrf csrf.CSRF Flash *session.Flash @@ -162,7 +161,6 @@ func Contexter() macaron.Handler { return func(c *macaron.Context, l i18n.Locale, cache cache.Cache, sess session.Store, f *session.Flash, x csrf.CSRF) { ctx := &Context{ Context: c, - Locale: l, Cache: cache, csrf: x, Flash: f, diff --git a/routers/org/setting.go b/routers/org/setting.go index 0522f998..41ec4a21 100644 --- a/routers/org/setting.go +++ b/routers/org/setting.go @@ -92,7 +92,7 @@ func SettingsDelete(ctx *middleware.Context) { ctx.Handle(500, "DeleteOrganization", err) } } else { - log.Trace("Organization deleted: %s", ctx.User.Name) + log.Trace("Organization deleted: %s", org.Name) ctx.Redirect(setting.AppSubUrl + "/") } return -- cgit v1.2.3 From 6705559ce08326381c12791d269e8179ac44d531 Mon Sep 17 00:00:00 2001 From: Michel Roux Date: Wed, 8 Oct 2014 09:37:54 +0200 Subject: Fix malformed address --- modules/mailer/mailer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules') diff --git a/modules/mailer/mailer.go b/modules/mailer/mailer.go index 92cdfc7d..6397ccc4 100644 --- a/modules/mailer/mailer.go +++ b/modules/mailer/mailer.go @@ -33,7 +33,7 @@ func (m Message) Content() string { } // create mail content - content := "From: " + m.From + "<" + m.User + + content := "From: \"" + m.From + "\" <" + m.User + ">\r\nSubject: " + m.Subject + "\r\nContent-Type: " + contentType + "\r\n\r\n" + m.Body return content } -- cgit v1.2.3 From 39931f8e001af113d3ea0905a79f152fb93f70ec Mon Sep 17 00:00:00 2001 From: Unknwon Date: Thu, 9 Oct 2014 18:08:07 -0400 Subject: Allow mail with self-signed certificates --- gogs.go | 2 +- modules/mailer/mailer.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++-- templates/.VERSION | 2 +- 3 files changed, 53 insertions(+), 4 deletions(-) (limited to 'modules') diff --git a/gogs.go b/gogs.go index 250333f3..f3825622 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.5.5.1008 Beta" +const APP_VER = "0.5.5.1009 Beta" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/modules/mailer/mailer.go b/modules/mailer/mailer.go index 6397ccc4..758792a3 100644 --- a/modules/mailer/mailer.go +++ b/modules/mailer/mailer.go @@ -5,7 +5,9 @@ package mailer import ( + "crypto/tls" "fmt" + "net" "net/smtp" "strings" @@ -64,6 +66,53 @@ func processMailQueue() { } } +// sendMail allows mail with self-signed certificates. +func sendMail(hostAddressWithPort string, auth smtp.Auth, from string, recipients []string, msgContent []byte) error { + client, err := smtp.Dial(hostAddressWithPort) + if err != nil { + return err + } + + host, _, _ := net.SplitHostPort(hostAddressWithPort) + tlsConn := &tls.Config{ + InsecureSkipVerify: true, + ServerName: host, + } + if err = client.StartTLS(tlsConn); err != nil { + return err + } + + if auth != nil { + if err = client.Auth(auth); err != nil { + return err + } + } + + if err = client.Mail(from); err != nil { + return err + } + + for _, rec := range recipients { + if err = client.Rcpt(rec); err != nil { + return err + } + } + + w, err := client.Data() + if err != nil { + return err + } + if _, err = w.Write([]byte(msgContent)); err != nil { + return err + } + + if err = w.Close(); err != nil { + return err + } + + return client.Quit() +} + // Direct Send mail message func Send(msg *Message) (int, error) { log.Trace("Sending mails to: %s", strings.Join(msg.To, "; ")) @@ -85,7 +134,7 @@ func Send(msg *Message) (int, error) { num := 0 for _, to := range msg.To { body := []byte("To: " + to + "\r\n" + content) - err := smtp.SendMail(setting.MailService.Host, auth, msg.From, []string{to}, body) + err := sendMail(setting.MailService.Host, auth, msg.From, []string{to}, body) if err != nil { return num, err } @@ -96,7 +145,7 @@ func Send(msg *Message) (int, error) { body := []byte("To: " + strings.Join(msg.To, ";") + "\r\n" + content) // send to multiple emails in one message - err := smtp.SendMail(setting.MailService.Host, auth, msg.From, msg.To, body) + err := sendMail(setting.MailService.Host, auth, msg.From, msg.To, body) if err != nil { return 0, err } else { diff --git a/templates/.VERSION b/templates/.VERSION index 194ec580..35a9c242 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.5.5.1008 Beta \ No newline at end of file +0.5.5.1009 Beta \ No newline at end of file -- cgit v1.2.3 From b2632dec099cb2727933149d2d59cc5e46baa15f Mon Sep 17 00:00:00 2001 From: Unknwon Date: Fri, 10 Oct 2014 21:40:51 -0400 Subject: Page: Compare 2 commits --- README.md | 1 + README_ZH.md | 1 + cmd/dump.go | 1 + conf/locale/locale_en-US.ini | 10 +++ conf/locale/locale_zh-CN.ini | 10 +++ models/action.go | 18 ++--- models/update.go | 9 +-- modules/base/template.go | 66 +--------------- public/ng/css/gogs.css | 104 ++++++++++++++++++++++++ public/ng/css/ui.css | 8 ++ public/ng/js/gogs.js | 38 +++++++++ public/ng/js/min/gogs-min.js | 10 +-- public/ng/less/gogs/base.less | 4 + public/ng/less/gogs/repository.less | 153 ++++++++++++++++++++++++++++++------ public/ng/less/ui/panel.less | 8 ++ routers/repo/commit.go | 2 + templates/repo/commits_table.tmpl | 4 +- templates/repo/diff.tmpl | 76 +++++++++--------- templates/user/dashboard/feeds.tmpl | 1 + 19 files changed, 379 insertions(+), 145 deletions(-) (limited to 'modules') diff --git a/README.md b/README.md index 6eb4afbf..44c62649 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ The goal of this project is to make the easiest, fastest and most painless way t - Activity timeline - SSH/HTTP(S) protocol support - SMTP/LDAP/reverse proxy authentication support +- Reverse proxy suburl support - Register/delete/rename account - Create/manage/delete organization with team management - Create/migrate/mirror/delete/watch/rename/transfer public/private repository diff --git a/README_ZH.md b/README_ZH.md index cb79a43c..9581faf7 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -23,6 +23,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自 - 活动时间线 - 支持 SSH/HTTP(S) 协议 - 支持 SMTP/LDAP/反向代理 用户认证 +- 支持反向代理子路径 - 注册/删除/重命名 用户 - 创建/管理/删除 组织以及团队管理功能 - 创建/迁移/镜像/删除/关注/重命名/转移 公开/私有 仓库 diff --git a/cmd/dump.go b/cmd/dump.go index 41491224..fe3763f0 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -60,6 +60,7 @@ func runDump(ctx *cli.Context) { z.AddFile("gogs-db.sql", path.Join(workDir, "gogs-db.sql")) z.AddFile("custom/conf/app.ini", path.Join(workDir, "custom/conf/app.ini")) z.AddDir("log", path.Join(workDir, "log")) + // FIXME: SSH key file. if err = z.Close(); err != nil { os.Remove(fileName) log.Fatalf("Fail to save %s: %v", fileName, err) diff --git a/conf/locale/locale_en-US.ini b/conf/locale/locale_en-US.ini index 2182167c..92cbf324 100644 --- a/conf/locale/locale_en-US.ini +++ b/conf/locale/locale_en-US.ini @@ -337,6 +337,15 @@ settings.slack_token = Token settings.slack_domain = Domain settings.slack_channel = Channel +diff.browse_source = Browse Source +diff.parent = parent +diff.commit = commit +diff.data_not_available = Diff Data Not Available. +diff.show_diff_stats = Show Diff Stats +diff.stats_desc = %d changed files with %d additions and %d deletions +diff.bin = BIN +diff.view_file = View File + [org] org_name_holder = Organization Name org_name_helper = Great organization names are short and memorable. @@ -609,6 +618,7 @@ commit_repo = pushed to %s at %s create_issue = opened issue %s#%s comment_issue = commented on issue %s#%s transfer_repo = transfered repository %s to %s +compare_2_commits = View comparison for these 2 commits [tool] ago = ago diff --git a/conf/locale/locale_zh-CN.ini b/conf/locale/locale_zh-CN.ini index a2242b45..ed3e1bef 100644 --- a/conf/locale/locale_zh-CN.ini +++ b/conf/locale/locale_zh-CN.ini @@ -337,6 +337,15 @@ settings.slack_token = 令牌 settings.slack_domain = 域名 settings.slack_channel = 频道 +diff.browse_source = 浏览代码 +diff.parent = 父节点 +diff.commit = 当前提交 +diff.data_not_available = 暂无可用数据 +diff.show_diff_stats = 显示文件统计 +diff.stats_desc = 共有 %d 个文件被更改,包括 %d 次插入%d 次删除 +diff.bin = 二进制 +diff.view_file = 查看文件 + [org] org_name_holder = 组织名称 org_name_helper = 伟大的组织都有一个简短而寓意深刻的名字。 @@ -609,6 +618,7 @@ commit_repo = 推送了 %s 分支的代码到 %s#%s comment_issue = 评论了工单 %s#%s transfer_repo = 将仓库 %s 转移至 %s +compare_2_commits = 查看 2 次提交的内容对比 [tool] ago = 之前 diff --git a/models/action.go b/models/action.go index 4203ead3..ef111e67 100644 --- a/models/action.go +++ b/models/action.go @@ -181,13 +181,19 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string, commit = &base.PushCommits{} } - refName := git.RefEndName(refFullName) + repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName) + // if not the first commit, set the compareUrl + if !strings.HasPrefix(oldCommitId, "0000000") { + commit.CompareUrl = fmt.Sprintf("%s/compare/%s...%s", repoLink, oldCommitId, newCommitId) + } bs, err := json.Marshal(commit) if err != nil { return errors.New("action.CommitRepoAction(json): " + err.Error()) } + refName := git.RefEndName(refFullName) + // Change repository bare status and update last updated time. repo, err := GetRepositoryByName(repoUserId, repoName) if err != nil { @@ -211,7 +217,6 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string, return errors.New("action.CommitRepoAction(NotifyWatchers): " + err.Error()) } - //qlog.Info("action.CommitRepoAction(end): %d/%s", repoUserId, repoName) // New push event hook. if err := repo.GetOwner(); err != nil { @@ -237,13 +242,6 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string, return nil } - repoLink := fmt.Sprintf("%s%s/%s", setting.AppUrl, repoUserName, repoName) - compareUrl := "" - // if not the first commit, set the compareUrl - if !strings.HasPrefix(oldCommitId, "0000000") { - compareUrl = fmt.Sprintf("%s/compare/%s...%s", repoLink, oldCommitId, newCommitId) - } - pusher_email, pusher_name := "", "" pusher, err := GetUserByName(userName) if err == nil { @@ -293,7 +291,7 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string, }, Before: oldCommitId, After: newCommitId, - CompareUrl: compareUrl, + CompareUrl: commit.CompareUrl, } for _, w := range ws { diff --git a/models/update.go b/models/update.go index d939a908..33b7733e 100644 --- a/models/update.go +++ b/models/update.go @@ -106,7 +106,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName if err = CommitRepoAction(userId, ru.Id, userName, actEmail, repos.Id, repoUserName, repoName, refName, commit, oldCommitId, newCommitId); err != nil { - log.GitLogger.Fatal(4, "runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err) + log.GitLogger.Fatal(4, "CommitRepoAction: %s/%s:%v", repoUserName, repoName, err) } return err } @@ -116,8 +116,8 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName return fmt.Errorf("runUpdate GetCommit of newCommitId: %v", err) } + // Push new branch. var l *list.List - // if a new branch if isNew { l, err = newCommit.CommitsBefore() if err != nil { @@ -134,7 +134,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName return fmt.Errorf("runUpdate.Commit repoId: %v", err) } - // if commits push + // Push commits. commits := make([]*base.PushCommit, 0) var actEmail string for e := l.Front(); e != nil; e = e.Next() { @@ -153,9 +153,8 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName } } - //commits = append(commits, []string{lastCommit.Id().String(), lastCommit.Message()}) if err = CommitRepoAction(userId, ru.Id, userName, actEmail, - repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits}, oldCommitId, newCommitId); err != nil { + repos.Id, repoUserName, repoName, refName, &base.PushCommits{l.Len(), commits, ""}, oldCommitId, newCommitId); err != nil { return fmt.Errorf("runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err) } return nil diff --git a/modules/base/template.go b/modules/base/template.go index b1c8c161..6d25cd45 100644 --- a/modules/base/template.go +++ b/modules/base/template.go @@ -5,7 +5,6 @@ package base import ( - "bytes" "container/list" "encoding/json" "errors" @@ -107,7 +106,6 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ return a + b }, "ActionIcon": ActionIcon, - "ActionDesc": ActionDesc, "DateFormat": DateFormat, "List": List, "Mail2Domain": func(mail string) string { @@ -162,19 +160,6 @@ func ActionIcon(opType int) string { } } -// FIXME: Legacy -const ( - TPL_CREATE_REPO = `%s created repository %s` - TPL_COMMIT_REPO = `%s pushed to %s at %s%s` - TPL_COMMIT_REPO_LI = `
    user-avatar %s %s
    ` - TPL_CREATE_ISSUE = `%s opened issue %s#%s -
    user-avatar %s
    ` - TPL_TRANSFER_REPO = `%s transfered repository %s to %s` - TPL_PUSH_TAG = `%s pushed tag %s at %s` - TPL_COMMENT_ISSUE = `%s commented on issue %s#%s -
    user-avatar %s
    ` -) - type PushCommit struct { Sha1 string Message string @@ -183,8 +168,9 @@ type PushCommit struct { } type PushCommits struct { - Len int - Commits []*PushCommit + Len int + Commits []*PushCommit + CompareUrl string } func ActionContent2Commits(act Actioner) *PushCommits { @@ -195,52 +181,6 @@ func ActionContent2Commits(act Actioner) *PushCommits { return push } -// FIXME: Legacy -// ActionDesc accepts int that represents action operation type -// and returns the description. -func ActionDesc(act Actioner) string { - actUserName := act.GetActUserName() - email := act.GetActEmail() - repoUserName := act.GetRepoUserName() - repoName := act.GetRepoName() - repoLink := repoUserName + "/" + repoName - branch := act.GetBranch() - content := act.GetContent() - switch act.GetOpType() { - case 1: // Create repository. - return fmt.Sprintf(TPL_CREATE_REPO, setting.AppSubUrl, actUserName, actUserName, repoLink, repoName) - case 5: // Commit repository. - var push *PushCommits - if err := json.Unmarshal([]byte(content), &push); err != nil { - return err.Error() - } - buf := bytes.NewBuffer([]byte("\n")) - for _, commit := range push.Commits { - buf.WriteString(fmt.Sprintf(TPL_COMMIT_REPO_LI, AvatarLink(commit.AuthorEmail), repoLink, commit.Sha1, commit.Sha1[:7], commit.Message) + "\n") - } - if push.Len > 3 { - buf.WriteString(fmt.Sprintf(``, actUserName, repoName, branch, push.Len)) - } - return fmt.Sprintf(TPL_COMMIT_REPO, setting.AppSubUrl, actUserName, actUserName, repoLink, branch, branch, repoLink, repoLink, - buf.String()) - case 6: // Create issue. - infos := strings.SplitN(content, "|", 2) - return fmt.Sprintf(TPL_CREATE_ISSUE, setting.AppSubUrl, actUserName, actUserName, repoLink, infos[0], repoLink, infos[0], - AvatarLink(email), infos[1]) - case 8: // Transfer repository. - newRepoLink := content + "/" + repoName - return fmt.Sprintf(TPL_TRANSFER_REPO, setting.AppSubUrl, actUserName, actUserName, repoLink, newRepoLink, newRepoLink) - case 9: // Push tag. - return fmt.Sprintf(TPL_PUSH_TAG, setting.AppSubUrl, actUserName, actUserName, repoLink, branch, branch, repoLink, repoLink) - case 10: // Comment issue. - infos := strings.SplitN(content, "|", 2) - return fmt.Sprintf(TPL_COMMENT_ISSUE, setting.AppSubUrl, actUserName, actUserName, repoLink, infos[0], repoLink, infos[0], - AvatarLink(email), infos[1]) - default: - return "invalid type" - } -} - func DiffTypeToStr(diffType int) string { diffTypes := map[int]string{ 1: "add", 2: "modify", 3: "del", diff --git a/public/ng/css/gogs.css b/public/ng/css/gogs.css index af95092f..6c8211c7 100644 --- a/public/ng/css/gogs.css +++ b/public/ng/css/gogs.css @@ -271,6 +271,10 @@ img.avatar-100 { .pagination li { display: inline; } +.list-unstyled { + padding-left: 0; + list-style: none; +} .markdown { background-color: white; font-size: 16px; @@ -1487,6 +1491,106 @@ The register and sign-in page style font-family: Consolas, Menlo, Monaco, "Lucida Console", monospace; font-size: 14px; } +.diff-head-box { + margin-top: 10px; +} +.diff-head-box .panel-body { + padding: 10px 15px 5px 10px; +} +.diff-head-box .author img { + margin-top: -7px; +} +.diff-detail-box { + margin: 15px 0; + line-height: 30px; +} +.diff-detail-box ol { + clear: both; + padding-left: 0; + margin-bottom: 28px; +} +.diff-detail-box ol li { + list-style: none; + padding-bottom: 4px; + margin-bottom: 4px; + border-bottom: 1px dashed #DDD; + padding-left: 6px; +} +.diff-detail-box span.status { + display: inline-block; + width: 12px; + height: 12px; + margin-right: 8px; + vertical-align: middle; +} +.diff-detail-box span.status.modify { + background-color: #f0db88; +} +.diff-detail-box span.status.add { + background-color: #b4e2b4; +} +.diff-detail-box span.status.del { + background-color: #e9aeae; +} +.diff-detail-box span.status.rename { + background-color: #dad8ff; +} +.diff-box .count { + margin-right: 12px; +} +.diff-box .count .bar { + background-color: #e75316; + height: 12px; + width: 40px; + display: inline-block; + margin: 2px 4px 0 4px; + vertical-align: text-top; +} +.diff-box .count .bar .add { + background-color: #77c64a; + height: 12px; +} +.diff-box .file { + color: #888; +} +.diff-box .panel-header { + font-size: 14px; +} +.diff-file-box .code-diff tbody tr:hover td, +.diff-file-box .code-diff tbody tr:hover pre { + background-color: #FFF8D2 !important; + border-color: #F0DB88 !important; +} +.diff-file-box .file-body.file-code .lines-num-old { + border-right: 1px solid #DDD; +} +.file-content .file-body.file-code .lines-num { + text-align: right; + color: #999; + background: #fafafa; + width: 1%; +} +.diff-file-box .code-diff tbody tr.tag-code td, +.diff-file-box .code-diff tbody tr.tag-code pre { + background-color: #E0E0E0 !important; + border-color: #ADADAD !important; +} +.diff-file-box .code-diff tbody tr.del-code td, +.diff-file-box .code-diff tbody tr.del-code pre { + background-color: #ffe2dd !important; + border-color: #e9aeae !important; +} +.diff-file-box .code-diff tbody tr.add-code td, +.diff-file-box .code-diff tbody tr.add-code pre { + background-color: #d1ffd6 !important; + border-color: #b4e2b4 !important; +} +.compare-head-box { + margin-top: 10px; +} +.compare-head-box .compare { + padding: 0 15px 15px 15px; +} #admin-wrapper, #setting-wrapper { padding-bottom: 100px; diff --git a/public/ng/css/ui.css b/public/ng/css/ui.css index 3ea6fcd4..cc1a277f 100644 --- a/public/ng/css/ui.css +++ b/public/ng/css/ui.css @@ -713,6 +713,14 @@ ul.menu-radius > li:last-child > a { border-bottom-left-radius: .3em; border-bottom-right-radius: .3em; } +.panel.panel-info { + border-color: #85c5e5; +} +.panel.panel-info > .panel-header { + color: #31708f; + background-color: #d9edf7; + border-color: #85c5e5; +} .panel.panel-warning { border-color: #F0C36D; } diff --git a/public/ng/js/gogs.js b/public/ng/js/gogs.js index 4bcdc5c8..538e6c67 100644 --- a/public/ng/js/gogs.js +++ b/public/ng/js/gogs.js @@ -299,6 +299,9 @@ function initCore() { e.preventDefault(); $.magnificPopup.close(); }); + + // Collapse. + $('.collapse').hide(); } function initUserSetting() { @@ -698,6 +701,37 @@ function initProfile() { }); } +function initTimeSwitch() { + // Time switch. + $(".time-since[title]").on("click", function () { + var $this = $(this); + + var title = $this.attr("title"); + var text = $this.text(); + + $this.text(title); + $this.attr("title", text); + }); +} + +function initDiff() { + $('.diff-detail-box>a').click(function () { + $($(this).data('target')).slideToggle(100); + }) + + var $counter = $('.diff-counter'); + if ($counter.length < 1) { + return; + } + $counter.each(function (i, item) { + var $item = $(item); + var addLine = $item.find('span[data-line].add').data("line"); + var delLine = $item.find('span[data-line].del').data("line"); + var addPercent = parseFloat(addLine) / (parseFloat(addLine) + parseFloat(delLine)) * 100; + $item.find(".bar .add").css("width", addPercent + "%"); + }); +} + $(document).ready(function () { Gogs.AppSubUrl = $('head').data('suburl') || ''; initCore(); @@ -737,6 +771,10 @@ $(document).ready(function () { if ($('#user-profile-page').length) { initProfile(); } + if ($('#diff-page').length) { + initTimeSwitch(); + initDiff(); + } $('#dashboard-sidebar-menu').tabs(); $('#pull-issue-preview').markdown_preview(".issue-add-comment"); diff --git a/public/ng/js/min/gogs-min.js b/public/ng/js/min/gogs-min.js index 1538c363..6b7f408d 100644 --- a/public/ng/js/min/gogs-min.js +++ b/public/ng/js/min/gogs-min.js @@ -1,5 +1,5 @@ -function Tabs(e){function t(e){console.log("hide",e),e.removeClass("js-tab-nav-show"),$(e.data("tab-target")).removeClass("js-tab-show").hide()}function n(e){console.log("show",e),e.addClass("js-tab-nav-show"),$(e.data("tab-target")).addClass("js-tab-show").show()}var r=$(e);if(r.length){var i=r.find(".js-tab-nav-show");i.length&&$(i.data("tab-target")).addClass("js-tab-show"),r.on("click",".js-tab-nav",function(e){e.preventDefault();var o=$(this);o.hasClass("js-tab-nav-show")||(i=r.find(".js-tab-nav-show").eq(0),t(i),n(o))}),console.log("init tabs @",e)}}function Preview(e,t){function n(e){return e.find(".js-preview-input").eq(0)}function r(e){return e.hasClass("js-preview-container")?e:e.find(".js-preview-container").eq(0)}var i=$(e),o=$(t),a=n(o);if(!a.length)return void console.log("[preview]: no preview input");var s=r(o);return s.length?(i.on("click",function(){$.post("/api/v1/markdown",{text:a.val()},function(e){s.html(e)})}),void console.log("[preview]: init preview @",e,"&",t)):void console.log("[preview]: no preview container")}function initCore(){Gogs.renderMarkdown(),Gogs.renderCodeView(),$(".js-tab-nav").click(function(e){$(this).hasClass("js-tab-nav-show")||($(this).parent().find(".js-tab-nav-show").each(function(){$(this).removeClass("js-tab-nav-show"),$($(this).data("tab-target")).hide()}),$(this).addClass("js-tab-nav-show"),$($(this).data("tab-target")).show()),e.preventDefault()}),$(document).on("click",".popup-modal-dismiss",function(e){e.preventDefault(),$.magnificPopup.close()})}function initUserSetting(){var t=$("#username"),n=$("#user-profile-form");$("#change-username-btn").magnificPopup({modal:!0,callbacks:{open:function(){t.data("uname")==t.val()&&($.magnificPopup.close(),n.submit())}}}).click(function(){return t.data("uname")!=t.val()?(e.preventDefault(),!0):void 0}),$("#change-username-submit").click(function(){$.magnificPopup.close(),n.submit()}),$("#ssh-add").click(function(){$("#user-ssh-add-form").removeClass("hide")}),$("#delete-account-btn").magnificPopup({modal:!0}).click(function(e){return e.preventDefault(),!0}),$("#delete-account-submit").click(function(){$.magnificPopup.close(),$("#delete-account-form").submit()})}function initRepoCreate(){$("#repo-create-owner-list").on("click","li",function(){if(!$(this).hasClass("checked")){var e=$(this).data("uid");$("#repo-owner-id").val(e),$("#repo-owner-avatar").attr("src",$(this).find("img").attr("src")),$("#repo-owner-name").text($(this).text().trim()),$(this).parent().find(".checked").removeClass("checked"),$(this).addClass("checked"),console.log("set repo owner to uid :",e,$(this).text().trim())}}),$("#auth-button").click(function(e){$("#repo-migrate-auth").slideToggle("fast"),e.preventDefault()}),console.log("initRepoCreate")}function initRepo(){$("#repo-clone-ssh").click(function(){$(this).removeClass("btn-gray").addClass("btn-blue"),$("#repo-clone-https").removeClass("btn-blue").addClass("btn-gray"),$("#repo-clone-url").val($(this).data("link")),$(".clone-url").text($(this).data("link"))}),$("#repo-clone-https").click(function(){$(this).removeClass("btn-gray").addClass("btn-blue"),$("#repo-clone-ssh").removeClass("btn-blue").addClass("btn-gray"),$("#repo-clone-url").val($(this).data("link")),$(".clone-url").text($(this).data("link"))});var e=$("#repo-clone-copy");e.hover(function(){Gogs.bindCopy($(this))}),e.tipsy({fade:!0})}function initHookTypeChange(){$("select#hook-type").on("change",function(){hookTypes=["Gogs","Slack"];var e=$(this).val();hookTypes.forEach(function(t){e===t?$("div#"+t.toLowerCase()).toggleShow():$("div#"+t.toLowerCase()).toggleHide()})})}function initRepoSetting(){var t=$("#repo_name"),n=$("#repo-setting-form");$("#change-reponame-btn").magnificPopup({modal:!0,callbacks:{open:function(){t.data("repo-name")==t.val()&&($.magnificPopup.close(),n.submit())}}}).click(function(){return t.data("repo-name")!=t.val()?(e.preventDefault(),!0):void 0}),$("#change-reponame-submit").click(function(){$.magnificPopup.close(),n.submit()}),initHookTypeChange(),$("#transfer-repo-btn").magnificPopup({modal:!0}),$("#transfer-repo-submit").click(function(){$.magnificPopup.close(),$("#transfer-repo-form").submit()}),$("#delete-repo-btn").magnificPopup({modal:!0}),$("#delete-repo-submit").click(function(){$.magnificPopup.close(),$("#delete-repo-form").submit()}),$("#repo-collab-list hr:last-child").remove();var r=$("#repo-collaborator").next().next().find("ul");$("#repo-collaborator").on("keyup",function(){var e=$(this);return e.val()?void Gogs.searchUsers(e.val(),r):void r.toggleHide()}).on("focus",function(){$(this).val()?r.toggleShow():r.toggleHide()}).next().next().find("ul").on("click","li",function(){$("#repo-collaborator").val($(this).text()),r.toggleHide()})}function initOrgSetting(){var t=$("#orgname"),n=$("#org-setting-form");$("#change-orgname-btn").magnificPopup({modal:!0,callbacks:{open:function(){t.data("orgname")==t.val()&&($.magnificPopup.close(),n.submit())}}}).click(function(){return t.data("orgname")!=t.val()?(e.preventDefault(),!0):void 0}),$("#change-orgname-submit").click(function(){$.magnificPopup.close(),n.submit()}),$("#delete-org-btn").magnificPopup({modal:!0}).click(function(e){return e.preventDefault(),!0}),$("#delete-org-submit").click(function(){$.magnificPopup.close(),$("#delete-org-form").submit()}),initHookTypeChange()}function initInvite(){var e=$("#org-member-invite-list");$("#org-member-invite").on("keyup",function(){var t=$(this);return t.val()?void Gogs.searchUsers(t.val(),e):void e.toggleHide()}).on("focus",function(){$(this).val()?e.toggleShow():e.toggleHide()}).next().next().find("ul").on("click","li",function(){$("#org-member-invite").val($(this).text()),e.toggleHide()})}function initOrgTeamCreate(){$("#org-team-delete").magnificPopup({modal:!0}).click(function(e){return e.preventDefault(),!0}),$("#delete-team-submit").click(function(){$.magnificPopup.close();var e=$("#team-create-form");e.attr("action",e.data("delete-url"))})}function initTeamMembersList(){var e=$("#org-team-members-list");$("#org-team-members-add").on("keyup",function(){var t=$(this);return t.val()?void Gogs.searchUsers(t.val(),e):void e.toggleHide()}).on("focus",function(){$(this).val()?e.toggleShow():e.toggleHide()}).next().next().find("ul").on("click","li",function(){$("#org-team-members-add").val($(this).text()),e.toggleHide()})}function initTeamRepositoriesList(){var e=$("#org-team-repositories-list");$("#org-team-repositories-add").on("keyup",function(){var t=$(this);return t.val()?void Gogs.searchRepos(t.val(),e,"uid="+t.data("uid")):void e.toggleHide()}).on("focus",function(){$(this).val()?e.toggleShow():e.toggleHide()}).next().next().find("ul").on("click","li",function(){$("#org-team-repositories-add").val($(this).text()),e.toggleHide()})}function initAdmin(){$("#login-type").on("change",function(){var e=$(this).val();e.indexOf("0-")+1?($(".auth-name").toggleHide(),$(".pwd").find("input").attr("required","required").end().toggleShow()):($(".pwd").find("input").removeAttr("required").end().toggleHide(),$(".auth-name").toggleShow())}),$("#delete-account-btn").magnificPopup({modal:!0}).click(function(e){return e.preventDefault(),!0}),$("#delete-account-submit").click(function(){$.magnificPopup.close();var e=$("#user-profile-form");e.attr("action",e.data("delete-url"))}),$("#auth-type").on("change",function(){var e=$(this).val();2==e&&($(".ldap").toggleShow(),$(".smtp").toggleHide()),3==e&&($(".smtp").toggleShow(),$(".ldap").toggleHide())}),$("#delete-auth-btn").magnificPopup({modal:!0}).click(function(e){return e.preventDefault(),!0}),$("#delete-auth-submit").click(function(){$.magnificPopup.close();var e=$("#auth-setting-form");e.attr("action",e.data("delete-url"))})}function initInstall(){!function(){var e="127.0.0.1:3306",t="127.0.0.1:5432";$("#install-database").on("change",function(){var n=$(this).val();"SQLite3"!=n?($(".server-sql").show(),$(".sqlite-setting").addClass("hide"),"PostgreSQL"==n?($(".pgsql-setting").removeClass("hide"),$("#database-host").val()==e&&$("#database-host").val(t)):"MySQL"==n?($(".pgsql-setting").addClass("hide"),$("#database-host").val()==t&&$("#database-host").val(e)):$(".pgsql-setting").addClass("hide")):($(".server-sql").hide(),$(".pgsql-setting").hide(),$(".sqlite-setting").removeClass("hide"))})}()}function initProfile(){$("#profile-avatar").tipsy({fade:!0})}function homepage(){$("#promo-form").submit(function(e){return""===$("#username").val()?(e.preventDefault(),window.location.href=Gogs.AppSubUrl+"/user/login",!0):void 0}),$("#register-button").click(function(e){return""===$("#username").val()?(e.preventDefault(),window.location.href=Gogs.AppSubUrl+"/user/sign_up",!0):void $("#promo-form").attr("action",Gogs.AppSubUrl+"/user/sign_up")})}!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=e.length,n=ot.type(e);return"function"===n||ot.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e}function r(e,t,n){if(ot.isFunction(t))return ot.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return ot.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(pt.test(t))return ot.filter(t,e,n);t=ot.filter(t,e)}return ot.grep(e,function(e){return ot.inArray(e,t)>=0!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t=wt[e]={};return ot.each(e.match(xt)||[],function(e,n){t[n]=!0}),t}function a(){mt.addEventListener?(mt.removeEventListener("DOMContentLoaded",s,!1),e.removeEventListener("load",s,!1)):(mt.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(mt.addEventListener||"load"===event.type||"complete"===mt.readyState)&&(a(),ot.ready())}function l(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace($t,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:Tt.test(n)?ot.parseJSON(n):n}catch(i){}ot.data(e,t,n)}else n=void 0}return n}function c(e){var t;for(t in e)if(("data"!==t||!ot.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function u(e,t,n,r){if(ot.acceptData(e)){var i,o,a=ot.expando,s=e.nodeType,l=s?ot.cache:e,c=s?e[a]:e[a]&&a;if(c&&l[c]&&(r||l[c].data)||void 0!==n||"string"!=typeof t)return c||(c=s?e[a]=V.pop()||ot.guid++:a),l[c]||(l[c]=s?{}:{toJSON:ot.noop}),("object"==typeof t||"function"==typeof t)&&(r?l[c]=ot.extend(l[c],t):l[c].data=ot.extend(l[c].data,t)),o=l[c],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[ot.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[ot.camelCase(t)])):i=o,i}}function d(e,t,n){if(ot.acceptData(e)){var r,i,o=e.nodeType,a=o?ot.cache:e,s=o?e[ot.expando]:ot.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){ot.isArray(t)?t=t.concat(ot.map(t,ot.camelCase)):t in r?t=[t]:(t=ot.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!c(r):!ot.isEmptyObject(r))return}(n||(delete a[s].data,c(a[s])))&&(o?ot.cleanData([e],!0):rt.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}function f(){return!0}function p(){return!1}function h(){try{return mt.activeElement}catch(e){}}function m(e){var t=Ht.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function g(e,t){var n,r,i=0,o=typeof e.getElementsByTagName!==St?e.getElementsByTagName(t||"*"):typeof e.querySelectorAll!==St?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||ot.nodeName(r,t)?o.push(r):ot.merge(o,g(r,t));return void 0===t||t&&ot.nodeName(e,t)?ot.merge([e],o):o}function v(e){jt.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t){return ot.nodeName(e,"table")&&ot.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function b(e){return e.type=(null!==ot.find.attr(e,"type"))+"/"+e.type,e}function x(e){var t=Zt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function w(e,t){for(var n,r=0;null!=(n=e[r]);r++)ot._data(n,"globalEval",!t||ot._data(t[r],"globalEval"))}function C(e,t){if(1===t.nodeType&&ot.hasData(e)){var n,r,i,o=ot._data(e),a=ot._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)ot.event.add(t,n,s[n][r])}a.data&&(a.data=ot.extend({},a.data))}}function S(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!rt.noCloneEvent&&t[ot.expando]){i=ot._data(t);for(r in i.events)ot.removeEvent(t,r,i.handle);t.removeAttribute(ot.expando)}"script"===n&&t.text!==e.text?(b(t).text=e.text,x(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),rt.html5Clone&&e.innerHTML&&!ot.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&jt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}function k(t,n){var r,i=ot(n.createElement(t)).appendTo(n.body),o=e.getDefaultComputedStyle&&(r=e.getDefaultComputedStyle(i[0]))?r.display:ot.css(i[0],"display");return i.detach(),o}function T(e){var t=mt,n=Jt[e];return n||(n=k(e,t),"none"!==n&&n||(Kt=(Kt||ot("