From 8ef71343dfef8914e9383e159915baa6a341eeef Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Thu, 20 Mar 2014 17:25:04 +0800 Subject: [PATCH 01/41] fix link --- routers/repo/single.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/routers/repo/single.go b/routers/repo/single.go index c10d30a7d6..2bc77c1c01 100644 --- a/routers/repo/single.go +++ b/routers/repo/single.go @@ -137,10 +137,9 @@ func Single(ctx *middleware.Context, params martini.Params) { return } else { // current repo branch link - urlPrefix := "http://" + base.Domain + branchLink ctx.Data["FileName"] = readmeFile.Name - ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), urlPrefix)) + ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), branchLink)) } } } From c3532718a79f102f4e19c11868a2c618b482eb6a Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Thu, 20 Mar 2014 17:25:48 +0800 Subject: [PATCH 02/41] add anchor --- public/css/markdown.css | 59 ++++++++++++++++++++++++++++++++++++++--- public/js/app.js | 21 ++++++++++++++- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/public/css/markdown.css b/public/css/markdown.css index 9283fb847c..1edc3b626c 100644 --- a/public/css/markdown.css +++ b/public/css/markdown.css @@ -15,7 +15,8 @@ line-height: 1.7; padding: 15px 0 0; margin: 0 0 15px; - color: #666; + color: #444; + font-weight: bold; } .markdown h1, @@ -86,6 +87,10 @@ margin-top: 6px; } +.markdown li:first-child { + margin-top: 0; +} + .markdown dl dt { font-style: italic; margin-top: 9px; @@ -130,11 +135,11 @@ } .markdown > pre > ol.linenums > li:first-child { - padding-top: 6px; + padding-top: 12px; } .markdown > pre > ol.linenums > li:last-child { - padding-bottom: 6px; + padding-bottom: 12px; } .markdown > pre > ol.linenums > li { @@ -163,6 +168,54 @@ color: #fff; } +.markdown .anchor-wrap { + /*margin-top: -50px;*/ + /*padding-top: 50px;*/ +} + +.markdown h1 a, .markdown h2 a, .markdown h3 a { + text-decoration: none; +} + +.markdown h1 a.anchor, +.markdown h2 a.anchor, +.markdown h3 a.anchor, +.markdown h4 a.anchor, +.markdown h5 a.anchor, +.markdown h6 a.anchor { + text-decoration:none; + line-height:1; + padding-left:0; + margin-left:5px; + top:15%; +} +.markdown a span.octicon { + font-size: 16px; + font-family: "FontAwesome"; + line-height: 1; + display: inline-block; + text-decoration: none; + -webkit-font-smoothing: antialiased; +} + +.markdown a span.octicon-link { + display: none; + color: #000; +} + +.markdown a span.octicon-link:before { + content: "\f0c1"; +} + +.markdown h1:hover .octicon-link, +.markdown h2:hover .octicon-link, +.markdown h3:hover .octicon-link, +.markdown h4:hover .octicon-link, +.markdown h5:hover .octicon-link, +.markdown h6:hover .octicon-link { + display:inline-block +} + /* Author: jmblog */ /* Project: https://github.com/jmblog/color-themes-for-google-code-prettify */ /* GitHub Theme */ diff --git a/public/js/app.js b/public/js/app.js index 30296bc337..f3e8d6d1d2 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -66,9 +66,28 @@ var Gogits = { // render markdown Gogits.renderMarkdown = function () { - var $pre = $('.markdown').find('pre > code').parent(); + var $md = $('.markdown'); + var $pre = $md.find('pre > code').parent(); $pre.addClass("prettyprint"); prettyPrint(); + + // Set anchor. + var headers = {}; + $md.find('h1, h2, h3, h4, h5, h6').each(function () { + var node = $(this); + var val = encodeURIComponent(node.text().toLowerCase().replace(/[^\w\- ]/g, '').replace(/[ ]/g, '-')); + var name = val; + if(headers[val] > 0){ + name = val + '-' + headers[val]; + } + if(headers[val] == undefined){ + headers[val] = 1; + }else{ + headers[val] += 1; + } + node = node.wrap('<div id="' + name + '" class="anchor-wrap" ></div>'); + node.append('<a class="anchor" href="#' + name + '"><span class="octicon octicon-link"></span></a>'); + }); } })(jQuery); From 21379e30a18fed473ae2bbeb41332919ff80497d Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Thu, 20 Mar 2014 17:31:18 +0800 Subject: [PATCH 03/41] fix link --- modules/base/markdown.go | 4 ++-- public/css/gogs.css | 8 ++++++++ templates/repo/single_file.tmpl | 10 +++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/modules/base/markdown.go b/modules/base/markdown.go index a9f4cbf0d7..e49e111c0e 100644 --- a/modules/base/markdown.go +++ b/modules/base/markdown.go @@ -36,7 +36,7 @@ func isLink(link []byte) bool { func IsMarkdownFile(name string) bool { name = strings.ToLower(name) switch filepath.Ext(name) { - case "md", "markdown": + case "md", "markdown", "mdown": return true } return false @@ -61,7 +61,7 @@ type CustomRender struct { func (options *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) { if len(link) > 0 && !isLink(link) { if link[0] == '#' { - link = append([]byte(options.urlPrefix), link...) + // link = append([]byte(options.urlPrefix), link...) } else { link = []byte(path.Join(options.urlPrefix, string(link))) } diff --git a/public/css/gogs.css b/public/css/gogs.css index a4767c1c8b..83d1b96fe2 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -647,6 +647,14 @@ html, body { padding: 30px 30px 50px; } +.file-content .file-body.file-code { + padding: 0; +} + +.file-content .file-body.file-code > pre { + border: none; +} + .branch-list th, .commit-list th { background-color: #FFF; line-height: 28px !important; diff --git a/templates/repo/single_file.tmpl b/templates/repo/single_file.tmpl index b4626053fd..0c1c3713f6 100644 --- a/templates/repo/single_file.tmpl +++ b/templates/repo/single_file.tmpl @@ -1,6 +1,10 @@ <div class="panel panel-default file-content"> <div class="panel-heading file-head"> - <i class="icon fa fa-book"></i> {{.FileName}} + {{if .ReadmeExist}} + <i class="icon fa fa-book"></i> + {{else}} + <i class="icon fa fa-file-text-o"></i> + {{end}}{{.FileName}} </div> {{if .FileIsLarge}} <div class="panel-footer"> @@ -12,8 +16,8 @@ {{.FileContent|str2html}} </div> {{else}} - <div class="panel-body file-body markdown"> - <pre><code>{{.FileContent}}</code></pre> + <div class="panel-body file-body file-code markdown"> + <pre class="linenums"><code>{{.FileContent}}</code></pre> </div> {{end}} {{end}} From 9f9cd6bfc61d82ee0a3d31cee112be7975b8ca86 Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Thu, 20 Mar 2014 07:50:26 -0400 Subject: [PATCH 04/41] Work on admin --- .gitignore | 4 +++- conf/app.ini | 4 ++++ models/user.go | 1 + modules/base/conf.go | 2 ++ modules/middleware/auth.go | 14 +++++++++++++- modules/middleware/context.go | 16 +++++++++++++--- routers/admin/admin.go | 24 ++++++++++++++++++++++++ routers/dashboard.go | 4 ++-- routers/dev/template.go | 2 +- routers/repo/repo.go | 4 ++-- routers/repo/single.go | 16 ++++++++-------- routers/user/setting.go | 18 +++++++++--------- routers/user/user.go | 28 ++++++++++++++-------------- templates/admin/dashboard.tmpl | 24 ++++++++++++++++++++++++ templates/admin/repos.tmpl | 23 +++++++++++++++++++++++ templates/admin/users.tmpl | 23 +++++++++++++++++++++++ templates/base/navbar.tmpl | 1 + templates/repo/setting.tmpl | 4 ++++ web.go | 6 ++++++ 19 files changed, 177 insertions(+), 41 deletions(-) create mode 100644 routers/admin/admin.go create mode 100644 templates/admin/dashboard.tmpl create mode 100644 templates/admin/repos.tmpl create mode 100644 templates/admin/users.tmpl diff --git a/.gitignore b/.gitignore index 3e550c3fc7..ad27cc8be8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ gogs *.db *.log custom/ -.vendor/ \ No newline at end of file +.vendor/ +.idea/ +*.iml \ No newline at end of file diff --git a/conf/app.ini b/conf/app.ini index 658f7c0151..21090ceba5 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -27,6 +27,10 @@ PASSWD = ; For "postgres" only, either "disable", "require" or "verify-full" SSL_MODE = disable +[admin] +; Administor's name, which should be same as the user name you want to authorize +NAME = admin + [security] ; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! SECRET_KEY = !#@FDEWREWR&*( diff --git a/models/user.go b/models/user.go index 76cf2d20ce..8f74fd53fb 100644 --- a/models/user.go +++ b/models/user.go @@ -51,6 +51,7 @@ type User struct { Location string Website string IsActive bool + IsAdmin bool Rands string `xorm:"VARCHAR(10)"` Created time.Time `xorm:"created"` Updated time.Time `xorm:"updated"` diff --git a/modules/base/conf.go b/modules/base/conf.go index fdbf3ad385..c904c5b39c 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -32,6 +32,7 @@ var ( AppUrl string Domain string SecretKey string + AdminName string Cfg *goconfig.ConfigFile MailService *Mailer ) @@ -173,6 +174,7 @@ func init() { AppUrl = Cfg.MustValue("server", "ROOT_URL") Domain = Cfg.MustValue("server", "DOMAIN") SecretKey = Cfg.MustValue("security", "SECRET_KEY") + AdminName = strings.ToLower(Cfg.MustValue("admin", "NAME")) } func NewServices() { diff --git a/modules/middleware/auth.go b/modules/middleware/auth.go index d45a21e988..b67f766bd7 100644 --- a/modules/middleware/auth.go +++ b/modules/middleware/auth.go @@ -20,7 +20,7 @@ func SignInRequire(redirect bool) martini.Handler { return } else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm { ctx.Data["Title"] = "Activate Your Account" - ctx.Render.HTML(200, "user/active", ctx.Data) + ctx.HTML(200, "user/active") return } } @@ -31,6 +31,18 @@ func SignOutRequire() martini.Handler { return func(ctx *Context) { if ctx.IsSigned { ctx.Redirect("/") + return } } } + +// AdminRequire requires user signed in as administor. +func AdminRequire() martini.Handler { + return func(ctx *Context) { + if ctx.User.LowerName != base.AdminName && !ctx.User.IsAdmin { + ctx.Error(403) + return + } + ctx.Data["PageIsAdmin"] = true + } +} diff --git a/modules/middleware/context.go b/modules/middleware/context.go index 6ac87de3be..744cdfc10c 100644 --- a/modules/middleware/context.go +++ b/modules/middleware/context.go @@ -14,6 +14,7 @@ import ( "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/auth" + "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" ) @@ -61,24 +62,29 @@ func (ctx *Context) HasError() bool { return hasErr.(bool) } +// HTML calls render.HTML underlying but reduce one argument. +func (ctx *Context) HTML(status int, name string, htmlOpt ...HTMLOptions) { + ctx.Render.HTML(status, name, ctx.Data, htmlOpt...) +} + // RenderWithErr used for page has form validation but need to prompt error to users. func (ctx *Context) RenderWithErr(msg, tpl string, form auth.Form) { ctx.Data["HasError"] = true ctx.Data["ErrorMsg"] = msg auth.AssignForm(form, ctx.Data) - ctx.HTML(200, tpl, ctx.Data) + ctx.HTML(200, tpl) } // Handle handles and logs error by given status. func (ctx *Context) Handle(status int, title string, err error) { log.Error("%s: %v", title, err) if martini.Dev == martini.Prod { - ctx.HTML(500, "status/500", ctx.Data) + ctx.HTML(500, "status/500") return } ctx.Data["ErrorMsg"] = err - ctx.HTML(status, fmt.Sprintf("status/%d", status), ctx.Data) + ctx.HTML(status, fmt.Sprintf("status/%d", status)) } // InitContext initializes a classic context for a request. @@ -106,6 +112,10 @@ func InitContext() martini.Handler { ctx.Data["SignedUser"] = user ctx.Data["SignedUserId"] = user.Id ctx.Data["SignedUserName"] = user.LowerName + + if ctx.User.IsAdmin || ctx.User.LowerName == base.AdminName { + ctx.Data["IsAdmin"] = true + } } ctx.Data["PageStartTime"] = time.Now() diff --git a/routers/admin/admin.go b/routers/admin/admin.go new file mode 100644 index 0000000000..c7523b7f59 --- /dev/null +++ b/routers/admin/admin.go @@ -0,0 +1,24 @@ +// 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 admin + +import ( + "github.com/gogits/gogs/modules/middleware" +) + +func Dashboard(ctx *middleware.Context) { + ctx.Data["Title"] = "Admin Dashboard" + ctx.HTML(200, "admin/dashboard") +} + +func Users(ctx *middleware.Context) { + ctx.Data["Title"] = "User Management" + ctx.HTML(200, "admin/users") +} + +func Repositories(ctx *middleware.Context) { + ctx.Data["Title"] = "Repository Management" + ctx.HTML(200, "admin/repos") +} diff --git a/routers/dashboard.go b/routers/dashboard.go index 6c194ad9e5..f61d67b7de 100644 --- a/routers/dashboard.go +++ b/routers/dashboard.go @@ -15,10 +15,10 @@ func Home(ctx *middleware.Context) { return } ctx.Data["PageIsHome"] = true - ctx.HTML(200, "home", ctx.Data) + ctx.HTML(200, "home") } func Help(ctx *middleware.Context) { ctx.Data["PageIsHelp"] = true - ctx.HTML(200, "help", ctx.Data) + ctx.HTML(200, "help") } diff --git a/routers/dev/template.go b/routers/dev/template.go index 7d5225ece7..d2f77ac4d5 100644 --- a/routers/dev/template.go +++ b/routers/dev/template.go @@ -21,5 +21,5 @@ func TemplatePreview(ctx *middleware.Context, params martini.Params) { ctx.Data["Code"] = "2014031910370000009fff6782aadb2162b4a997acb69d4400888e0b9274657374" ctx.Data["ActiveCodeLives"] = base.Service.ActiveCodeLives / 60 ctx.Data["ResetPwdCodeLives"] = base.Service.ResetPwdCodeLives / 60 - ctx.HTML(200, params["_1"], ctx.Data) + ctx.HTML(200, params["_1"]) } diff --git a/routers/repo/repo.go b/routers/repo/repo.go index b38473b18a..556cc4343c 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -18,7 +18,7 @@ func Create(ctx *middleware.Context, form auth.CreateRepoForm) { ctx.Data["Licenses"] = models.Licenses if ctx.Req.Method == "GET" { - ctx.HTML(200, "repo/create", ctx.Data) + ctx.HTML(200, "repo/create") return } @@ -45,7 +45,7 @@ func SettingPost(ctx *middleware.Context) { case "delete": if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") { ctx.Data["ErrorMsg"] = "Please make sure you entered repository name is correct." - ctx.HTML(200, "repo/setting", ctx.Data) + ctx.HTML(200, "repo/setting") return } diff --git a/routers/repo/single.go b/routers/repo/single.go index c10d30a7d6..ebf64dc665 100644 --- a/routers/repo/single.go +++ b/routers/repo/single.go @@ -38,7 +38,7 @@ func Branches(ctx *middleware.Context, params martini.Params) { ctx.Data["Branches"] = brs ctx.Data["IsRepoToolbarBranches"] = true - ctx.HTML(200, "repo/branches", ctx.Data) + ctx.HTML(200, "repo/branches") } func Single(ctx *middleware.Context, params martini.Params) { @@ -67,7 +67,7 @@ func Single(ctx *middleware.Context, params martini.Params) { return } else if len(brs) == 0 { ctx.Data["IsBareRepo"] = true - ctx.HTML(200, "repo/single", ctx.Data) + ctx.HTML(200, "repo/single") return } @@ -178,7 +178,7 @@ func Single(ctx *middleware.Context, params martini.Params) { ctx.Data["Treenames"] = treenames ctx.Data["IsRepoToolbarSource"] = true ctx.Data["BranchLink"] = branchLink - ctx.HTML(200, "repo/single", ctx.Data) + ctx.HTML(200, "repo/single") } func Setting(ctx *middleware.Context, params martini.Params) { @@ -195,7 +195,7 @@ func Setting(ctx *middleware.Context, params martini.Params) { return } else if len(brs) == 0 { ctx.Data["IsBareRepo"] = true - ctx.HTML(200, "repo/setting", ctx.Data) + ctx.HTML(200, "repo/setting") return } @@ -206,7 +206,7 @@ func Setting(ctx *middleware.Context, params martini.Params) { ctx.Data["Title"] = title + " - settings" ctx.Data["IsRepoToolbarSetting"] = true - ctx.HTML(200, "repo/setting", ctx.Data) + ctx.HTML(200, "repo/setting") } func Commits(ctx *middleware.Context, params martini.Params) { @@ -230,17 +230,17 @@ func Commits(ctx *middleware.Context, params martini.Params) { ctx.Data["Reponame"] = params["reponame"] ctx.Data["CommitCount"] = commits.Len() ctx.Data["Commits"] = commits - ctx.HTML(200, "repo/commits", ctx.Data) + ctx.HTML(200, "repo/commits") } func Issues(ctx *middleware.Context) { ctx.Data["IsRepoToolbarIssues"] = true - ctx.HTML(200, "repo/issues", ctx.Data) + ctx.HTML(200, "repo/issues") } func Pulls(ctx *middleware.Context) { ctx.Data["IsRepoToolbarPulls"] = true - ctx.HTML(200, "repo/pulls", ctx.Data) + ctx.HTML(200, "repo/pulls") } func Action(ctx *middleware.Context, params martini.Params) { diff --git a/routers/user/setting.go b/routers/user/setting.go index 053f327f0f..f0c7a8a5b1 100644 --- a/routers/user/setting.go +++ b/routers/user/setting.go @@ -24,13 +24,13 @@ func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) { ctx.Data["Owner"] = user if ctx.Req.Method == "GET" { - ctx.HTML(200, "user/setting", ctx.Data) + ctx.HTML(200, "user/setting") return } // below is for POST requests if hasErr, ok := ctx.Data["HasError"]; ok && hasErr.(bool) { - ctx.HTML(200, "user/setting", ctx.Data) + ctx.HTML(200, "user/setting") return } @@ -45,7 +45,7 @@ func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) { } ctx.Data["IsSuccess"] = true - ctx.HTML(200, "user/setting", ctx.Data) + ctx.HTML(200, "user/setting") log.Trace("%s User setting updated: %s", ctx.Req.RequestURI, ctx.User.LowerName) } @@ -55,7 +55,7 @@ func SettingPassword(ctx *middleware.Context, form auth.UpdatePasswdForm) { ctx.Data["IsUserPageSettingPasswd"] = true if ctx.Req.Method == "GET" { - ctx.HTML(200, "user/password", ctx.Data) + ctx.HTML(200, "user/password") return } @@ -82,7 +82,7 @@ func SettingPassword(ctx *middleware.Context, form auth.UpdatePasswdForm) { } ctx.Data["Owner"] = user - ctx.HTML(200, "user/password", ctx.Data) + ctx.HTML(200, "user/password") log.Trace("%s User password updated: %s", ctx.Req.RequestURI, ctx.User.LowerName) } @@ -123,7 +123,7 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) { // Add new SSH key. if ctx.Req.Method == "POST" { if hasErr, ok := ctx.Data["HasError"]; ok && hasErr.(bool) { - ctx.HTML(200, "user/publickey", ctx.Data) + ctx.HTML(200, "user/publickey") return } @@ -155,7 +155,7 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) { ctx.Data["PageIsUserSetting"] = true ctx.Data["IsUserPageSettingSSH"] = true ctx.Data["Keys"] = keys - ctx.HTML(200, "user/publickey", ctx.Data) + ctx.HTML(200, "user/publickey") } func SettingNotification(ctx *middleware.Context) { @@ -163,7 +163,7 @@ func SettingNotification(ctx *middleware.Context) { ctx.Data["Title"] = "Notification" ctx.Data["PageIsUserSetting"] = true ctx.Data["IsUserPageSettingNotify"] = true - ctx.HTML(200, "user/notification", ctx.Data) + ctx.HTML(200, "user/notification") } func SettingSecurity(ctx *middleware.Context) { @@ -171,5 +171,5 @@ func SettingSecurity(ctx *middleware.Context) { ctx.Data["Title"] = "Security" ctx.Data["PageIsUserSetting"] = true ctx.Data["IsUserPageSettingSecurity"] = true - ctx.HTML(200, "user/security", ctx.Data) + ctx.HTML(200, "user/security") } diff --git a/routers/user/user.go b/routers/user/user.go index f495cb13ab..2b759e4192 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -34,7 +34,7 @@ func Dashboard(ctx *middleware.Context) { return } ctx.Data["Feeds"] = feeds - ctx.HTML(200, "user/dashboard", ctx.Data) + ctx.HTML(200, "user/dashboard") } func Profile(ctx *middleware.Context, params martini.Params) { @@ -70,19 +70,19 @@ func Profile(ctx *middleware.Context, params martini.Params) { } ctx.Data["PageIsUserProfile"] = true - ctx.HTML(200, "user/profile", ctx.Data) + ctx.HTML(200, "user/profile") } func SignIn(ctx *middleware.Context, form auth.LogInForm) { ctx.Data["Title"] = "Log In" if ctx.Req.Method == "GET" { - ctx.HTML(200, "user/signin", ctx.Data) + ctx.HTML(200, "user/signin") return } if hasErr, ok := ctx.Data["HasError"]; ok && hasErr.(bool) { - ctx.HTML(200, "user/signin", ctx.Data) + ctx.HTML(200, "user/signin") return } @@ -113,7 +113,7 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) { ctx.Data["PageIsSignUp"] = true if ctx.Req.Method == "GET" { - ctx.HTML(200, "user/signup", ctx.Data) + ctx.HTML(200, "user/signup") return } @@ -126,7 +126,7 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) { } if ctx.HasError() { - ctx.HTML(200, "user/signup", ctx.Data) + ctx.HTML(200, "user/signup") return } @@ -158,7 +158,7 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) { ctx.Data["IsSendRegisterMail"] = true ctx.Data["Email"] = u.Email ctx.Data["Hours"] = base.Service.ActiveCodeLives / 60 - ctx.Render.HTML(200, "user/active", ctx.Data) + ctx.HTML(200, "user/active") return } ctx.Redirect("/user/login") @@ -170,7 +170,7 @@ func Delete(ctx *middleware.Context) { ctx.Data["IsUserPageSettingDelete"] = true if ctx.Req.Method == "GET" { - ctx.HTML(200, "user/delete", ctx.Data) + ctx.HTML(200, "user/delete") return } @@ -195,7 +195,7 @@ func Delete(ctx *middleware.Context) { } } - ctx.HTML(200, "user/delete", ctx.Data) + ctx.HTML(200, "user/delete") } const ( @@ -218,15 +218,15 @@ func Feeds(ctx *middleware.Context, form auth.FeedsForm) { } func Issues(ctx *middleware.Context) { - ctx.HTML(200, "user/issues", ctx.Data) + ctx.HTML(200, "user/issues") } func Pulls(ctx *middleware.Context) { - ctx.HTML(200, "user/pulls", ctx.Data) + ctx.HTML(200, "user/pulls") } func Stars(ctx *middleware.Context) { - ctx.HTML(200, "user/stars", ctx.Data) + ctx.HTML(200, "user/stars") } func Activate(ctx *middleware.Context) { @@ -244,7 +244,7 @@ func Activate(ctx *middleware.Context) { } else { ctx.Data["ServiceNotEnabled"] = true } - ctx.Render.HTML(200, "user/active", ctx.Data) + ctx.HTML(200, "user/active") return } @@ -263,5 +263,5 @@ func Activate(ctx *middleware.Context) { } ctx.Data["IsActivateFailed"] = true - ctx.Render.HTML(200, "user/active", ctx.Data) + ctx.HTML(200, "user/active") } diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl new file mode 100644 index 0000000000..84456c85b8 --- /dev/null +++ b/templates/admin/dashboard.tmpl @@ -0,0 +1,24 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +<div id="gogs-body" class="container" data-page="admin"> + <div id="gogs-user-setting-nav" class="col-md-3"> + <ul class="list-group" data-init="tabs"> + <li class="list-group-item active"><a href="/admin"><i class="fa fa-tachometer fa-lg"></i> Dashboard</a></li> + <li class="list-group-item"><a href="/admin/users"><i class="fa fa-users fa-lg"></i> Users</a></li> + <li class="list-group-item"><a href="/admin/repos"><i class="fa fa-book fa-lg"></i> Repositories</a></li> + </ul> + </div> + + <div id="gogs-admin-container" class="col-md-9"> + <div class="panel panel-default"> + <div class="panel-heading"> + Statistic + </div> + + <div class="panel-body"> + Gogs database has 4 users, 3 repositories, 4 SSH keys. + </div> + </div> + </div> +</div> +{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/admin/repos.tmpl b/templates/admin/repos.tmpl new file mode 100644 index 0000000000..ec7f47e090 --- /dev/null +++ b/templates/admin/repos.tmpl @@ -0,0 +1,23 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +<div id="gogs-body" class="container" data-page="admin"> + <div id="gogs-user-setting-nav" class="col-md-3"> + <ul class="list-group" data-init="tabs"> + <li class="list-group-item"><a href="/admin"><i class="fa fa-tachometer fa-lg"></i> Dashboard</a></li> + <li class="list-group-item"><a href="/admin/users"><i class="fa fa-users fa-lg"></i> Users</a></li> + <li class="list-group-item active"><a href="/admin/repos"><i class="fa fa-book fa-lg"></i> Repositories</a></li> + </ul> + </div> + + <div id="gogs-admin-container" class="col-md-9"> + <div class="panel panel-default"> + <div class="panel-heading"> + Repository Management + </div> + + <div class="panel-body"> + </div> + </div> + </div> +</div> +{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/admin/users.tmpl b/templates/admin/users.tmpl new file mode 100644 index 0000000000..8acf256d05 --- /dev/null +++ b/templates/admin/users.tmpl @@ -0,0 +1,23 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +<div id="gogs-body" class="container" data-page="admin"> + <div id="gogs-user-setting-nav" class="col-md-3"> + <ul class="list-group" data-init="tabs"> + <li class="list-group-item"><a href="/admin"><i class="fa fa-tachometer fa-lg"></i> Dashboard</a></li> + <li class="list-group-item active"><a href="/admin/users"><i class="fa fa-users fa-lg"></i> Users</a></li> + <li class="list-group-item"><a href="/admin/repos"><i class="fa fa-book fa-lg"></i> Repositories</a></li> + </ul> + </div> + + <div id="gogs-admin-container" class="col-md-9"> + <div class="panel panel-default"> + <div class="panel-heading"> + User Management + </div> + + <div class="panel-body"> + </div> + </div> + </div> +</div> +{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/base/navbar.tmpl b/templates/base/navbar.tmpl index e0d796a87b..9c064d07e7 100644 --- a/templates/base/navbar.tmpl +++ b/templates/base/navbar.tmpl @@ -10,6 +10,7 @@ </a> <a class="navbar-right gogs-nav-item{{if .PageIsNewRepo}} active{{end}}" href="/repo/create" data-toggle="tooltip" data-placement="bottom" title="New Repository"><i class="fa fa-plus fa-lg"></i></a> <a class="navbar-right gogs-nav-item{{if .PageIsUserSetting}} active{{end}}" href="/user/setting" data-toggle="tooltip" data-placement="bottom" title="Setting"><i class="fa fa-cogs fa-lg"></i></a> + {{if .IsAdmin}}<a class="navbar-right gogs-nav-item{{if .PageIsAdmin}} active{{end}}" href="/admin" data-toggle="tooltip" data-placement="bottom" title="Admin"><i class="fa fa-gear fa-lg"></i></a>{{end}} {{else}}<a id="gogs-nav-signin" class="gogs-nav-item navbar-right navbar-btn btn btn-danger" href="/user/login/">Sign in</a>{{end}} </nav> </div> diff --git a/templates/repo/setting.tmpl b/templates/repo/setting.tmpl index 06f0ed4d50..a2fb1771d4 100644 --- a/templates/repo/setting.tmpl +++ b/templates/repo/setting.tmpl @@ -10,20 +10,24 @@ <li class="list-group-item"><a href="#">Notifications</a></li>--> </ul> </div> + <div id="gogs-repo-setting-container" class="col-md-9"> {{if .ErrorMsg}}<p class="alert alert-danger">{{.ErrorMsg}}</p>{{end}} <div class="panel panel-default"> <div class="panel-heading"> Repository Options </div> + <div class="panel-body"> </div> </div> + <div class="panel panel-warning"> <div class="panel-heading"> Danger Zone </div> + <div class="panel-body"> <button type="button" class="btn btn-default pull-right" href="#delete-repository-modal" data-toggle="modal"> Delete this repository diff --git a/web.go b/web.go index ceb193e6fd..a5a8305aee 100644 --- a/web.go +++ b/web.go @@ -21,6 +21,7 @@ import ( "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/routers" + "github.com/gogits/gogs/routers/admin" "github.com/gogits/gogs/routers/dev" "github.com/gogits/gogs/routers/repo" "github.com/gogits/gogs/routers/user" @@ -99,6 +100,11 @@ func runWeb(*cli.Context) { m.Get("/help", routers.Help) + adminReq := middleware.AdminRequire() + m.Any("/admin", reqSignIn, adminReq, admin.Dashboard) + m.Any("/admin/users", reqSignIn, adminReq, admin.Users) + m.Any("/admin/repos", reqSignIn, adminReq, admin.Repositories) + m.Post("/:username/:reponame/settings", reqSignIn, middleware.RepoAssignment(true), repo.SettingPost) m.Get("/:username/:reponame/settings", reqSignIn, middleware.RepoAssignment(true), repo.Setting) From 4cf6cc63b0679aaf5fe8b74a2aaf0bd92b1f12d3 Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Thu, 20 Mar 2014 08:02:14 -0400 Subject: [PATCH 05/41] Work on admin --- conf/app.ini | 4 ---- models/user.go | 8 +++++++- modules/base/conf.go | 2 -- modules/middleware/auth.go | 2 +- modules/middleware/context.go | 6 +----- routers/user/user.go | 2 +- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/conf/app.ini b/conf/app.ini index 21090ceba5..658f7c0151 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -27,10 +27,6 @@ PASSWD = ; For "postgres" only, either "disable", "require" or "verify-full" SSL_MODE = disable -[admin] -; Administor's name, which should be same as the user name you want to authorize -NAME = admin - [security] ; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! SECRET_KEY = !#@FDEWREWR&*( diff --git a/models/user.go b/models/user.go index 8f74fd53fb..fd89af6b3f 100644 --- a/models/user.go +++ b/models/user.go @@ -137,7 +137,13 @@ func RegisterUser(user *User) (*User, error) { } return nil, err } - return user, nil + + if user.Id == 1 { + user.IsAdmin = true + user.IsActive = true + _, err = orm.Id(user.Id).UseBool().Update(user) + } + return user, err } // get user by erify code diff --git a/modules/base/conf.go b/modules/base/conf.go index c904c5b39c..fdbf3ad385 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -32,7 +32,6 @@ var ( AppUrl string Domain string SecretKey string - AdminName string Cfg *goconfig.ConfigFile MailService *Mailer ) @@ -174,7 +173,6 @@ func init() { AppUrl = Cfg.MustValue("server", "ROOT_URL") Domain = Cfg.MustValue("server", "DOMAIN") SecretKey = Cfg.MustValue("security", "SECRET_KEY") - AdminName = strings.ToLower(Cfg.MustValue("admin", "NAME")) } func NewServices() { diff --git a/modules/middleware/auth.go b/modules/middleware/auth.go index b67f766bd7..44033abb8f 100644 --- a/modules/middleware/auth.go +++ b/modules/middleware/auth.go @@ -39,7 +39,7 @@ func SignOutRequire() martini.Handler { // AdminRequire requires user signed in as administor. func AdminRequire() martini.Handler { return func(ctx *Context) { - if ctx.User.LowerName != base.AdminName && !ctx.User.IsAdmin { + if !ctx.User.IsAdmin { ctx.Error(403) return } diff --git a/modules/middleware/context.go b/modules/middleware/context.go index 744cdfc10c..cb3cbabca6 100644 --- a/modules/middleware/context.go +++ b/modules/middleware/context.go @@ -14,7 +14,6 @@ import ( "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/auth" - "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" ) @@ -112,10 +111,7 @@ func InitContext() martini.Handler { ctx.Data["SignedUser"] = user ctx.Data["SignedUserId"] = user.Id ctx.Data["SignedUserName"] = user.LowerName - - if ctx.User.IsAdmin || ctx.User.LowerName == base.AdminName { - ctx.Data["IsAdmin"] = true - } + ctx.Data["IsAdmin"] = ctx.User.IsAdmin } ctx.Data["PageStartTime"] = time.Now() diff --git a/routers/user/user.go b/routers/user/user.go index 2b759e4192..be2c4d3839 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -153,7 +153,7 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) { log.Trace("%s User created: %s", ctx.Req.RequestURI, strings.ToLower(form.UserName)) // Send confirmation e-mail. - if base.Service.RegisterEmailConfirm { + if base.Service.RegisterEmailConfirm && u.Id > 1 { mailer.SendRegisterMail(ctx.Render, u) ctx.Data["IsSendRegisterMail"] = true ctx.Data["Email"] = u.Email From cb80111e8923ff9317199dca61509e32408279ca Mon Sep 17 00:00:00 2001 From: FuXiaoHei <fuxiaohei@hexiaz.com> Date: Thu, 20 Mar 2014 20:12:31 +0800 Subject: [PATCH 06/41] guide page for bare repo some ui fix --- public/css/gogs.css | 14 ++++++++++++++ public/js/app.js | 30 +++++++++++++++++++++++++----- routers/repo/single.go | 6 ++++-- templates/repo/single.tmpl | 2 +- templates/repo/single_bare.tmpl | 31 +++++++++++++++++++++++++++++++ templates/repo/toolbar.tmpl | 3 +-- 6 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 templates/repo/single_bare.tmpl diff --git a/public/css/gogs.css b/public/css/gogs.css index a4767c1c8b..95f44d0fd4 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -724,6 +724,20 @@ html, body { background-color: #FFF; } +.commit-list .date { + width: 120px; +} + +.guide-box pre, .guide-box .input-group { + margin-top: 20px; + margin-bottom: 30px; + line-height: 24px; +} + +.guide-box input[readonly] { + background-color: #FFF; +} + /* wrapper and footer */ #wrapper { diff --git a/public/js/app.js b/public/js/app.js index 30296bc337..e1aeb8a51a 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -41,15 +41,15 @@ var Gogits = { }); }; Gogits.initPopovers = function () { - var hideAllPopovers = function() { - $('[data-toggle=popover]').each(function() { + var hideAllPopovers = function () { + $('[data-toggle=popover]').each(function () { $(this).popover('hide'); - }); + }); }; - $(document).on('click', function(e) { + $(document).on('click', function (e) { var $e = $(e.target); - if($e.data('toggle') == 'popover'||$e.parents("[data-toggle=popover], .popover").length > 0){ + if ($e.data('toggle') == 'popover' || $e.parents("[data-toggle=popover], .popover").length > 0) { return; } hideAllPopovers(); @@ -142,6 +142,23 @@ function initUserSetting() { }); } +function initRepository() { + var $guide = $('.guide-box'); + if ($guide.length) { + var $url = $('#guide-clone-url'); + $guide.find('button[data-url]').on("click",function () { + var $this = $(this); + if (!$this.hasClass('btn-primary')) { + $guide.find('.btn-primary').removeClass('btn-primary').addClass("btn-default"); + $(this).addClass('btn-primary').removeClass('btn-default'); + $url.val($this.data("url")); + $guide.find('span.clone-url').text($this.data('url')); + } + }).eq(0).trigger("click"); + // todo copy to clipboard + } +} + (function ($) { $(function () { initCore(); @@ -152,5 +169,8 @@ function initUserSetting() { if (body.data("page") == "user") { initUserSetting(); } + if ($('.gogs-repo-nav').length) { + initRepository(); + } }); })(jQuery); diff --git a/routers/repo/single.go b/routers/repo/single.go index ebf64dc665..4a6af9ff50 100644 --- a/routers/repo/single.go +++ b/routers/repo/single.go @@ -59,6 +59,8 @@ func Single(ctx *middleware.Context, params martini.Params) { return } + ctx.Data["IsRepoToolbarSource"] = true + // Branches. brs, err := models.GetBranches(params["username"], params["reponame"]) if err != nil { @@ -176,7 +178,6 @@ func Single(ctx *middleware.Context, params martini.Params) { ctx.Data["Paths"] = Paths ctx.Data["Treenames"] = treenames - ctx.Data["IsRepoToolbarSource"] = true ctx.Data["BranchLink"] = branchLink ctx.HTML(200, "repo/single") } @@ -187,6 +188,8 @@ func Setting(ctx *middleware.Context, params martini.Params) { return } + ctx.Data["IsRepoToolbarSetting"] = true + // Branches. brs, err := models.GetBranches(params["username"], params["reponame"]) if err != nil { @@ -205,7 +208,6 @@ func Setting(ctx *middleware.Context, params martini.Params) { } ctx.Data["Title"] = title + " - settings" - ctx.Data["IsRepoToolbarSetting"] = true ctx.HTML(200, "repo/setting") } diff --git a/templates/repo/single.tmpl b/templates/repo/single.tmpl index 60247898e2..8a7b5e479b 100644 --- a/templates/repo/single.tmpl +++ b/templates/repo/single.tmpl @@ -5,7 +5,7 @@ <div id="gogs-body" class="container"> <div id="gogs-source"> {{if .IsBareRepo}} - Need to fill in some guide. + {{template "repo/single_bare" .}} {{else}} <div class="source-toolbar"> {{ $n := len .Treenames}} diff --git a/templates/repo/single_bare.tmpl b/templates/repo/single_bare.tmpl new file mode 100644 index 0000000000..caf2ef7498 --- /dev/null +++ b/templates/repo/single_bare.tmpl @@ -0,0 +1,31 @@ +<div class="panel panel-default guide-box"> + <div class="panel-heading guide-head"> + <h4>Quick Guide</h4> + </div> + <div class="panel-body guide-content text-center"> + <h3>Clone this repository</h3> + <div class="input-group col-md-8 col-md-offset-2 guide-buttons"> + <span class="input-group-btn"> + <button class="btn btn-default" data-url="https-link" type="button">HTTPS</button> + <button class="btn btn-default" data-url="git-link" type="button">SSH</button> + </span> + <input type="text" class="form-control" id="guide-clone-url" value="" readonly/> + <span class="input-group-btn"> + <button class="btn btn-default" type="button"><i class="fa fa-copy" data-toggle="tooltip" title="copy to clipboard" data-placement="top"></i></button> + </span> + </div> + <p>We recommend every repository include a <strong>README</strong>, <strong>LICENSE</strong>, and <strong>.gitignore</strong>.</p> + <hr/> + <h3>Create a new repository on the command line</h3> + <pre class="text-left"><code>touch README.md +git init +git add README.md +git commit -m "first commit" +git remote add origin <span class="clone-url">https://github.com/fuxiaohei/air.git</span> +git push -u origin master</code></pre> + <hr/> + <h3>Push an existing repository from the command line</h3> + <pre class="text-left"><code>git remote add origin <span class="clone-url">https://github.com/fuxiaohei/air.git</span> +git push -u origin master</code></pre> + </div> +</div> \ No newline at end of file diff --git a/templates/repo/toolbar.tmpl b/templates/repo/toolbar.tmpl index 5cd9f526b4..b51768a3c3 100644 --- a/templates/repo/toolbar.tmpl +++ b/templates/repo/toolbar.tmpl @@ -15,9 +15,8 @@ <li><a href="/{{.RepositoryLink}}/release">Release</a></li> <li><a href="//{{.RepositoryLink}}/wiki">Wiki</a></li> </ul> - </li> + </li>{{end}} </ul> - {{end}} <ul class="nav navbar-nav navbar-right"> {{if not .IsBareRepo}} <li class="dropdown"> From e8a49c7746f3d93688d83401c69b0f4936c25783 Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Thu, 20 Mar 2014 08:25:41 -0400 Subject: [PATCH 07/41] Add data to ui --- modules/middleware/repo.go | 1 + templates/repo/single_bare.tmpl | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index b25c942316..62c67bcee1 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -70,6 +70,7 @@ func RepoAssignment(redirect bool) martini.Handler { } ctx.Repo.Repository = repo ctx.Repo.CloneLink.SSH = fmt.Sprintf("git@%s:%s/%s.git", base.Domain, user.LowerName, repo.LowerName) + ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("https://%s/%s/%s.git", base.Domain, user.LowerName, repo.LowerName) ctx.Data["IsRepositoryValid"] = true ctx.Data["Repository"] = repo diff --git a/templates/repo/single_bare.tmpl b/templates/repo/single_bare.tmpl index caf2ef7498..999ff557b0 100644 --- a/templates/repo/single_bare.tmpl +++ b/templates/repo/single_bare.tmpl @@ -6,8 +6,8 @@ <h3>Clone this repository</h3> <div class="input-group col-md-8 col-md-offset-2 guide-buttons"> <span class="input-group-btn"> - <button class="btn btn-default" data-url="https-link" type="button">HTTPS</button> - <button class="btn btn-default" data-url="git-link" type="button">SSH</button> + <button class="btn btn-default" data-url="{{.CloneLink.HTTPS}}" type="button">HTTPS</button> + <button class="btn btn-default" data-url="{{.CloneLink.SSH}}" type="button">SSH</button> </span> <input type="text" class="form-control" id="guide-clone-url" value="" readonly/> <span class="input-group-btn"> @@ -20,12 +20,12 @@ <pre class="text-left"><code>touch README.md git init git add README.md -git commit -m "first commit" -git remote add origin <span class="clone-url">https://github.com/fuxiaohei/air.git</span> +git commit -m "first commit" {{.CloneLink.SSH}} +git remote add origin <span class="clone-url">{{.CloneLink.HTTPS}}</span> git push -u origin master</code></pre> <hr/> <h3>Push an existing repository from the command line</h3> - <pre class="text-left"><code>git remote add origin <span class="clone-url">https://github.com/fuxiaohei/air.git</span> + <pre class="text-left"><code>git remote add origin <span class="clone-url">{{.CloneLink.HTTPS}}</span> git push -u origin master</code></pre> </div> </div> \ No newline at end of file From c7b6fbfd46fe465b5103ecc0b743ea009eb1e586 Mon Sep 17 00:00:00 2001 From: FuXiaoHei <fuxiaohei@hexiaz.com> Date: Thu, 20 Mar 2014 20:42:06 +0800 Subject: [PATCH 08/41] clean padding in single file page --- public/css/gogs.css | 18 +++++++++++++++++- public/js/app.js | 2 +- templates/repo/single_bare.tmpl | 8 ++++---- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/public/css/gogs.css b/public/css/gogs.css index 95f44d0fd4..d2ba5b9a43 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -644,7 +644,19 @@ html, body { } .file-content .file-body { - padding: 30px 30px 50px; + padding: 0; + border: none; + background-color: #FFF; +} + +.file-content .file-body pre { + background-color: #FFF; + border: none; +} + +.file-content .markdown > pre > ol.linenums > li:first-child { + padding-top: 0; + margin-top: 0; } .branch-list th, .commit-list th { @@ -738,6 +750,10 @@ html, body { background-color: #FFF; } +.guide-box { + margin-top: 20px; +} + /* wrapper and footer */ #wrapper { diff --git a/public/js/app.js b/public/js/app.js index e1aeb8a51a..1f64b529d5 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -67,7 +67,7 @@ var Gogits = { // render markdown Gogits.renderMarkdown = function () { var $pre = $('.markdown').find('pre > code').parent(); - $pre.addClass("prettyprint"); + $pre.addClass("prettyprint linenums"); prettyPrint(); } diff --git a/templates/repo/single_bare.tmpl b/templates/repo/single_bare.tmpl index 999ff557b0..06711157d4 100644 --- a/templates/repo/single_bare.tmpl +++ b/templates/repo/single_bare.tmpl @@ -6,8 +6,8 @@ <h3>Clone this repository</h3> <div class="input-group col-md-8 col-md-offset-2 guide-buttons"> <span class="input-group-btn"> - <button class="btn btn-default" data-url="{{.CloneLink.HTTPS}}" type="button">HTTPS</button> <button class="btn btn-default" data-url="{{.CloneLink.SSH}}" type="button">SSH</button> + <button class="btn btn-default" data-url="{{.CloneLink.HTTPS}}" type="button">HTTPS</button> </span> <input type="text" class="form-control" id="guide-clone-url" value="" readonly/> <span class="input-group-btn"> @@ -20,12 +20,12 @@ <pre class="text-left"><code>touch README.md git init git add README.md -git commit -m "first commit" {{.CloneLink.SSH}} -git remote add origin <span class="clone-url">{{.CloneLink.HTTPS}}</span> +git commit -m "first commit" +git remote add origin <span class="clone-url"></span> git push -u origin master</code></pre> <hr/> <h3>Push an existing repository from the command line</h3> - <pre class="text-left"><code>git remote add origin <span class="clone-url">{{.CloneLink.HTTPS}}</span> + <pre class="text-left"><code>git remote add origin <span class="clone-url"></span> git push -u origin master</code></pre> </div> </div> \ No newline at end of file From de956c477684f66a9e27ad895efbebae1837b2b1 Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Thu, 20 Mar 2014 21:10:37 +0800 Subject: [PATCH 09/41] linenums --- public/css/gogs.css | 47 ++++++++++++++++++++++++++++----- public/css/markdown.css | 10 +++++-- public/js/app.js | 23 +++++++++++++++- public/js/lib.js | 2 +- routers/repo/single.go | 2 ++ templates/repo/single_file.tmpl | 11 ++++++-- 6 files changed, 82 insertions(+), 13 deletions(-) diff --git a/public/css/gogs.css b/public/css/gogs.css index eb5f7ad2a5..41e4be9a4c 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -644,7 +644,7 @@ html, body { } .file-content .file-body { - padding: 0; + padding: 30px 30px 50px; border: none; background-color: #FFF; } @@ -654,17 +654,50 @@ html, body { border: none; } -.file-content .markdown > pre > ol.linenums > li:first-child { - padding-top: 0; - margin-top: 0; -} - .file-content .file-body.file-code { padding: 0; } -.file-content .file-body.file-code > pre { +.file-content .file-body.file-code .lines-code > pre { border: none; + background: none; + border-left: 1px solid #ddd; +} + +.file-content .file-body.file-code .lines-code ol.linenums > .active { + background: #ffffdd; +} + +.file-content .file-body.file-code .lines-num { + text-align: right; + color: #999; + background: #fafafa; + width: 1%; +} + +.file-content .file-body.file-code .lines-num span { + font-family: Menlo,Monaco,Consolas,"Courier New",monospace; + line-height: 1.6; + padding: 0 8px 0 10px; + cursor: pointer; + display: block; + margin-top: 6px; + font-size: 90%; +} + +.file-content .file-body.file-code .lines-num span:first-child { + margin-top: 0; +} + +.file-content .file-body.file-code > table { + width: 100%; +} + +.file-content .file-body.file-code > table > tbody > tr, +.file-content .file-body.file-code > table > tbody > tr > td, +.file-content .file-body.file-code > table { + border: none; + background: none; } .branch-list th, .commit-list th { diff --git a/public/css/markdown.css b/public/css/markdown.css index 1edc3b626c..68094867b1 100644 --- a/public/css/markdown.css +++ b/public/css/markdown.css @@ -111,7 +111,6 @@ line-height: 1.6; overflow: auto; background: #f8f8f8; - padding: 6px 10px; border: 1px solid #ddd; } @@ -120,6 +119,13 @@ } .markdown > pre > ol.linenums { + list-style: none; + padding: 0; +} + +.markdown > pre.linenums-style > ol.linenums { + list-style-type: decimal; + padding: 0 0 0 40px; -webkit-box-shadow: inset 40px 0 0 #f5f5f5, inset 41px 0 0 #ccc; box-shadow: inset 40px 0 0 #f5f5f5, inset 41px 0 0 #ccc; } @@ -142,7 +148,7 @@ padding-bottom: 12px; } -.markdown > pre > ol.linenums > li { +.markdown > pre.linenums-style > ol.linenums > li { border-left: 1px solid #ddd; } diff --git a/public/js/app.js b/public/js/app.js index 12f9e7f359..93cfbc1faa 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -68,9 +68,30 @@ var Gogits = { Gogits.renderMarkdown = function () { var $md = $('.markdown'); var $pre = $md.find('pre > code').parent(); - $pre.addClass("prettyprint"); + $pre.addClass('prettyprint'); prettyPrint(); + var $lineNums = $pre.parent().siblings('.lines-num'); + if($lineNums.length > 0){ + var nums = $pre.find('ol.linenums > li').length; + for(var i=0;i < nums;i++){ + $lineNums.append('<span id="L'+i+'" rel=".L'+i+'">'+(i+1)+'</span>'); + } + + var last; + $(document).on('click', '.lines-num span', function(){ + var $e = $(this); + console.log($e.parent().siblings('.lines-code').find('ol.linenums > ' + $e.attr('rel'))); + console.log('ol.linenums > ' + $e.attr('rel')); + if(last){ + last.removeClass('active'); + } + last = $e.parent().siblings('.lines-code').find('ol.linenums > ' + $e.attr('rel')); + last.addClass('active'); + window.location.href = '#' + $e.attr('id'); + }); + } + // Set anchor. var headers = {}; $md.find('h1, h2, h3, h4, h5, h6').each(function () { diff --git a/public/js/lib.js b/public/js/lib.js index 2a98f27775..b9e07c0caf 100644 --- a/public/js/lib.js +++ b/public/js/lib.js @@ -340,7 +340,7 @@ q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(? s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/, q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d= c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i<c.length;++i)b(c[i]);d===(d|0)&&c[0].setAttribute("value",d);var r=j.createElement("ol"); -r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d)%10,k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a; +r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d),k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a; a.a=b;a.d=g.d;a.e=0;I(d,b)(a);var s=/\bMSIE\s(\d+)/.exec(navigator.userAgent),s=s&&+s[1]<=8,d=/\n/g,x=a.a,m=x.length,g=0,j=a.d,k=j.length,b=0,c=a.g,i=c.length,r=0;c[i]=m;var n,e;for(e=n=0;e<i;)c[e]!==c[e+2]?(c[n++]=c[e++],c[n++]=c[e++]):e+=2;i=n;for(e=n=0;e<i;){for(var p=c[e],w=c[e+1],t=e+2;t+2<=i&&c[t+1]===w;)t+=2;c[n++]=p;c[n++]=w;e=t}c.length=n;var f=a.c,h;if(f)h=f.style.display,f.style.display="none";try{for(;b<k;){var l=j[b+2]||m,B=c[r+2]||m,t=Math.min(l,B),A=j[b+1],G;if(A.nodeType!==1&&(G=x.substring(g, t))){s&&(G=G.replace(d,"\r"));A.nodeValue=G;var L=A.ownerDocument,o=L.createElement("span");o.className=c[r+1];var v=A.parentNode;v.replaceChild(o,A);o.appendChild(A);g<l&&(j[b+1]=A=L.createTextNode(x.substring(t,l)),v.insertBefore(A,o.nextSibling))}g=t;g>=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"], diff --git a/routers/repo/single.go b/routers/repo/single.go index 6eb839c10d..49eb55d3e0 100644 --- a/routers/repo/single.go +++ b/routers/repo/single.go @@ -5,6 +5,7 @@ package repo import ( + "path" "strings" "github.com/codegangsta/martini" @@ -95,6 +96,7 @@ func Single(ctx *middleware.Context, params martini.Params) { } else { ctx.Data["IsFile"] = true ctx.Data["FileName"] = repoFile.Name + ctx.Data["FileExt"] = path.Ext(repoFile.Name) readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name) ctx.Data["ReadmeExist"] = readmeExist diff --git a/templates/repo/single_file.tmpl b/templates/repo/single_file.tmpl index 0c1c3713f6..7bca626aaa 100644 --- a/templates/repo/single_file.tmpl +++ b/templates/repo/single_file.tmpl @@ -16,8 +16,15 @@ {{.FileContent|str2html}} </div> {{else}} - <div class="panel-body file-body file-code markdown"> - <pre class="linenums"><code>{{.FileContent}}</code></pre> + <div class="panel-body file-body file-code"> + <table> + <tbody> + <tr> + <td class="lines-num"></td> + <td class="lines-code markdown"><pre class="linenums lang-{{.FileExt}}"><code>{{.FileContent}}</code></pre></td> + </tr> + </tbody> + </table> </div> {{end}} {{end}} From 47234f1031b35dfc6b3a20223d5dd61db2decda1 Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Thu, 20 Mar 2014 21:11:48 +0800 Subject: [PATCH 10/41] fix ext --- modules/base/markdown.go | 2 +- routers/repo/single.go | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/base/markdown.go b/modules/base/markdown.go index e49e111c0e..2273cd772f 100644 --- a/modules/base/markdown.go +++ b/modules/base/markdown.go @@ -36,7 +36,7 @@ func isLink(link []byte) bool { func IsMarkdownFile(name string) bool { name = strings.ToLower(name) switch filepath.Ext(name) { - case "md", "markdown", "mdown": + case ".md", ".markdown", ".mdown": return true } return false diff --git a/routers/repo/single.go b/routers/repo/single.go index 49eb55d3e0..e218bc9158 100644 --- a/routers/repo/single.go +++ b/routers/repo/single.go @@ -96,7 +96,11 @@ func Single(ctx *middleware.Context, params martini.Params) { } else { ctx.Data["IsFile"] = true ctx.Data["FileName"] = repoFile.Name - ctx.Data["FileExt"] = path.Ext(repoFile.Name) + ext := path.Ext(repoFile.Name) + if len(ext) > 0 { + ext = ext[1:] + } + ctx.Data["FileExt"] = ext readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name) ctx.Data["ReadmeExist"] = readmeExist From c8790eb7d367cec25f1201f923ca017268fe157d Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Thu, 20 Mar 2014 21:24:15 +0800 Subject: [PATCH 11/41] overflow --- public/css/gogs.css | 3 +++ public/js/app.js | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/public/css/gogs.css b/public/css/gogs.css index 41e4be9a4c..3a8760c355 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -647,6 +647,9 @@ html, body { padding: 30px 30px 50px; border: none; background-color: #FFF; + overflow: auto; + overflow-x: auto; + overflow-y: hidden; } .file-content .file-body pre { diff --git a/public/js/app.js b/public/js/app.js index 93cfbc1faa..a39976006f 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -81,8 +81,6 @@ var Gogits = { var last; $(document).on('click', '.lines-num span', function(){ var $e = $(this); - console.log($e.parent().siblings('.lines-code').find('ol.linenums > ' + $e.attr('rel'))); - console.log('ol.linenums > ' + $e.attr('rel')); if(last){ last.removeClass('active'); } From 42b08ff26158b9c815d13a427c596bd76468beb8 Mon Sep 17 00:00:00 2001 From: FuXiaoHei <fuxiaohei@hexiaz.com> Date: Thu, 20 Mar 2014 21:28:12 +0800 Subject: [PATCH 12/41] fix single bare page link --- modules/middleware/repo.go | 1 + public/js/app.js | 46 +++++++++++++++++---------------- templates/repo/nav.tmpl | 2 +- templates/repo/single_bare.tmpl | 4 +-- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index 62c67bcee1..a9a90e3ff5 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -79,5 +79,6 @@ func RepoAssignment(redirect bool) martini.Handler { ctx.Data["CloneLink"] = ctx.Repo.CloneLink ctx.Data["RepositoryLink"] = ctx.Data["Title"] ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner + ctx.Data["IsRepositoryWatching"] = ctx.Repo.IsWatching } } diff --git a/public/js/app.js b/public/js/app.js index 93cfbc1faa..64cc980fd8 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -72,18 +72,18 @@ var Gogits = { prettyPrint(); var $lineNums = $pre.parent().siblings('.lines-num'); - if($lineNums.length > 0){ + if ($lineNums.length > 0) { var nums = $pre.find('ol.linenums > li').length; - for(var i=0;i < nums;i++){ - $lineNums.append('<span id="L'+i+'" rel=".L'+i+'">'+(i+1)+'</span>'); + for (var i = 1; i <= nums; i++) { + $lineNums.append('<span id="L' + i + '" rel=".L' + i + '">' + i + '</span>'); } var last; - $(document).on('click', '.lines-num span', function(){ + $(document).on('click', '.lines-num span', function () { var $e = $(this); console.log($e.parent().siblings('.lines-code').find('ol.linenums > ' + $e.attr('rel'))); console.log('ol.linenums > ' + $e.attr('rel')); - if(last){ + if (last) { last.removeClass('active'); } last = $e.parent().siblings('.lines-code').find('ol.linenums > ' + $e.attr('rel')); @@ -98,12 +98,12 @@ var Gogits = { var node = $(this); var val = encodeURIComponent(node.text().toLowerCase().replace(/[^\w\- ]/g, '').replace(/[ ]/g, '-')); var name = val; - if(headers[val] > 0){ + if (headers[val] > 0) { name = val + '-' + headers[val]; } - if(headers[val] == undefined){ + if (headers[val] == undefined) { headers[val] = 1; - }else{ + } else { headers[val] += 1; } node = node.wrap('<div id="' + name + '" class="anchor-wrap" ></div>'); @@ -183,20 +183,22 @@ function initUserSetting() { } function initRepository() { - var $guide = $('.guide-box'); - if ($guide.length) { - var $url = $('#guide-clone-url'); - $guide.find('button[data-url]').on("click",function () { - var $this = $(this); - if (!$this.hasClass('btn-primary')) { - $guide.find('.btn-primary').removeClass('btn-primary').addClass("btn-default"); - $(this).addClass('btn-primary').removeClass('btn-default'); - $url.val($this.data("url")); - $guide.find('span.clone-url').text($this.data('url')); - } - }).eq(0).trigger("click"); - // todo copy to clipboard - } + (function () { + var $guide = $('.guide-box'); + if ($guide.length) { + var $url = $('#guide-clone-url'); + $guide.find('button[data-url]').on("click",function () { + var $this = $(this); + if (!$this.hasClass('btn-primary')) { + $guide.find('.btn-primary').removeClass('btn-primary').addClass("btn-default"); + $(this).addClass('btn-primary').removeClass('btn-default'); + $url.val($this.data("url")); + $guide.find('span.clone-url').text($this.data('url')); + } + }).eq(0).trigger("click"); + // todo copy to clipboard + } + })(); } (function ($) { diff --git a/templates/repo/nav.tmpl b/templates/repo/nav.tmpl index 92e529dbc7..e8685b08bc 100644 --- a/templates/repo/nav.tmpl +++ b/templates/repo/nav.tmpl @@ -13,7 +13,7 @@ <span class="caret"></span> </button> </div> - <div class="btn-group" id="gogs-repo-watching"> + <div class="btn-group {{if .IsRepositoryWatching}}watching{{end}}" id="gogs-repo-watching"> <button type="button" class="btn btn-default"><i class="fa fa-eye fa-lg fa-m"></i></button> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> <span class="caret"></span> diff --git a/templates/repo/single_bare.tmpl b/templates/repo/single_bare.tmpl index 06711157d4..bb05c2f9a6 100644 --- a/templates/repo/single_bare.tmpl +++ b/templates/repo/single_bare.tmpl @@ -6,8 +6,8 @@ <h3>Clone this repository</h3> <div class="input-group col-md-8 col-md-offset-2 guide-buttons"> <span class="input-group-btn"> - <button class="btn btn-default" data-url="{{.CloneLink.SSH}}" type="button">SSH</button> - <button class="btn btn-default" data-url="{{.CloneLink.HTTPS}}" type="button">HTTPS</button> + <button class="btn btn-default" data-link="{{.CloneLink.SSH}}" type="button">SSH</button> + <button class="btn btn-default" data-link="{{.CloneLink.HTTPS}}" type="button">HTTPS</button> </span> <input type="text" class="form-control" id="guide-clone-url" value="" readonly/> <span class="input-group-btn"> From 4efd73d37a7e76401ef93982db63b4538013ea23 Mon Sep 17 00:00:00 2001 From: FuXiaoHei <fuxiaohei@hexiaz.com> Date: Thu, 20 Mar 2014 21:32:08 +0800 Subject: [PATCH 13/41] fix single bare page link --- public/js/app.js | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/public/js/app.js b/public/js/app.js index a39976006f..2952083ed8 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -72,16 +72,16 @@ var Gogits = { prettyPrint(); var $lineNums = $pre.parent().siblings('.lines-num'); - if($lineNums.length > 0){ + if ($lineNums.length > 0) { var nums = $pre.find('ol.linenums > li').length; - for(var i=0;i < nums;i++){ - $lineNums.append('<span id="L'+i+'" rel=".L'+i+'">'+(i+1)+'</span>'); + for (var i = 0; i < nums; i++) { + $lineNums.append('<span id="L' + i + '" rel=".L' + i + '">' + (i + 1) + '</span>'); } var last; - $(document).on('click', '.lines-num span', function(){ + $(document).on('click', '.lines-num span', function () { var $e = $(this); - if(last){ + if (last) { last.removeClass('active'); } last = $e.parent().siblings('.lines-code').find('ol.linenums > ' + $e.attr('rel')); @@ -96,12 +96,12 @@ var Gogits = { var node = $(this); var val = encodeURIComponent(node.text().toLowerCase().replace(/[^\w\- ]/g, '').replace(/[ ]/g, '-')); var name = val; - if(headers[val] > 0){ + if (headers[val] > 0) { name = val + '-' + headers[val]; } - if(headers[val] == undefined){ + if (headers[val] == undefined) { headers[val] = 1; - }else{ + } else { headers[val] += 1; } node = node.wrap('<div id="' + name + '" class="anchor-wrap" ></div>'); @@ -181,20 +181,22 @@ function initUserSetting() { } function initRepository() { - var $guide = $('.guide-box'); - if ($guide.length) { - var $url = $('#guide-clone-url'); - $guide.find('button[data-url]').on("click",function () { - var $this = $(this); - if (!$this.hasClass('btn-primary')) { - $guide.find('.btn-primary').removeClass('btn-primary').addClass("btn-default"); - $(this).addClass('btn-primary').removeClass('btn-default'); - $url.val($this.data("url")); - $guide.find('span.clone-url').text($this.data('url')); - } - }).eq(0).trigger("click"); - // todo copy to clipboard - } + (function () { + var $guide = $('.guide-box'); + if ($guide.length) { + var $url = $('#guide-clone-url'); + $guide.find('button[data-link]').on("click",function () { + var $this = $(this); + if (!$this.hasClass('btn-primary')) { + $guide.find('.btn-primary').removeClass('btn-primary').addClass("btn-default"); + $(this).addClass('btn-primary').removeClass('btn-default'); + $url.val($this.data("link")); + $guide.find('span.clone-url').text($this.data('link')); + } + }).eq(0).trigger("click"); + // todo copy to clipboard + } + })(); } (function ($) { From fa455ab757886b56a2adb3de77d9db06bfae0eb6 Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Thu, 20 Mar 2014 21:32:44 +0800 Subject: [PATCH 14/41] fix offset --- public/css/gogs.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/css/gogs.css b/public/css/gogs.css index 3a8760c355..d1b1e155a8 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -501,8 +501,7 @@ html, body { } .activity-list .info { - float: left; - padding: 0 0 0 10px; + margin: 0 0 0 40px; line-height: 1.7em; } From 7c6c6125f3846acc633c00bb92c8698c30e761fa Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Thu, 20 Mar 2014 21:35:46 +0800 Subject: [PATCH 15/41] fix line num --- public/js/app.js | 4 ++-- public/js/lib.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/js/app.js b/public/js/app.js index 2952083ed8..555410329b 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -74,8 +74,8 @@ var Gogits = { var $lineNums = $pre.parent().siblings('.lines-num'); if ($lineNums.length > 0) { var nums = $pre.find('ol.linenums > li').length; - for (var i = 0; i < nums; i++) { - $lineNums.append('<span id="L' + i + '" rel=".L' + i + '">' + (i + 1) + '</span>'); + for (var i = 1; i <= nums; i++) { + $lineNums.append('<span id="L' + i + '" rel=".L' + i + '">' + i + '</span>'); } var last; diff --git a/public/js/lib.js b/public/js/lib.js index b9e07c0caf..8deb6f7226 100644 --- a/public/js/lib.js +++ b/public/js/lib.js @@ -340,7 +340,7 @@ q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(? s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/, q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d= c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i<c.length;++i)b(c[i]);d===(d|0)&&c[0].setAttribute("value",d);var r=j.createElement("ol"); -r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d),k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a; +r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d+1),k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a; a.a=b;a.d=g.d;a.e=0;I(d,b)(a);var s=/\bMSIE\s(\d+)/.exec(navigator.userAgent),s=s&&+s[1]<=8,d=/\n/g,x=a.a,m=x.length,g=0,j=a.d,k=j.length,b=0,c=a.g,i=c.length,r=0;c[i]=m;var n,e;for(e=n=0;e<i;)c[e]!==c[e+2]?(c[n++]=c[e++],c[n++]=c[e++]):e+=2;i=n;for(e=n=0;e<i;){for(var p=c[e],w=c[e+1],t=e+2;t+2<=i&&c[t+1]===w;)t+=2;c[n++]=p;c[n++]=w;e=t}c.length=n;var f=a.c,h;if(f)h=f.style.display,f.style.display="none";try{for(;b<k;){var l=j[b+2]||m,B=c[r+2]||m,t=Math.min(l,B),A=j[b+1],G;if(A.nodeType!==1&&(G=x.substring(g, t))){s&&(G=G.replace(d,"\r"));A.nodeValue=G;var L=A.ownerDocument,o=L.createElement("span");o.className=c[r+1];var v=A.parentNode;v.replaceChild(o,A);o.appendChild(A);g<l&&(j[b+1]=A=L.createTextNode(x.substring(t,l)),v.insertBefore(A,o.nextSibling))}g=t;g>=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"], From b4a55434a46ffaf5c18df10d6bb39eb1e775822b Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Thu, 20 Mar 2014 21:43:54 +0800 Subject: [PATCH 16/41] wrong link should 404 --- routers/repo/single.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/routers/repo/single.go b/routers/repo/single.go index e218bc9158..f1b15cceed 100644 --- a/routers/repo/single.go +++ b/routers/repo/single.go @@ -87,6 +87,11 @@ func Single(ctx *middleware.Context, params martini.Params) { branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + params["branchname"] + if len(treename) != 0 && repoFile == nil { + ctx.Error(404) + return + } + if repoFile != nil && repoFile.IsFile() { if repoFile.Size > 1024*1024 || repoFile.Filemode != git.FileModeBlob { ctx.Data["FileIsLarge"] = true From 4a6c56d2fdca7f3f7cbc6d03a6809796c0b5a56e Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Thu, 20 Mar 2014 09:49:06 -0400 Subject: [PATCH 17/41] Bug fix --- gogs.go | 2 +- models/repo.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/gogs.go b/gogs.go index 385b74217b..d32e3908c9 100644 --- a/gogs.go +++ b/gogs.go @@ -20,7 +20,7 @@ import ( // Test that go1.1 tag above is included in builds. main.go refers to this definition. const go11tag = true -const APP_VER = "0.1.1.0320.1" +const APP_VER = "0.1.2.0320.1" func init() { base.AppVer = APP_VER diff --git a/models/repo.go b/models/repo.go index 4b6dedaf90..052341ff6e 100644 --- a/models/repo.go +++ b/models/repo.go @@ -411,6 +411,10 @@ func DeleteRepository(userId, repoId int64, userName string) (err error) { session.Rollback() return err } + if _, err = session.Delete(&Watch{RepoId: repoId}); err != nil { + session.Rollback() + return err + } if err = session.Commit(); err != nil { session.Rollback() return err From 1a0d7c54a2abecbe5335ca374fd8a345c2e7d3fd Mon Sep 17 00:00:00 2001 From: FuXiaoHei <fuxiaohei@hexiaz.com> Date: Thu, 20 Mar 2014 22:39:10 +0800 Subject: [PATCH 18/41] repo watching ajax --- public/css/gogs.css | 8 ++++++-- public/js/app.js | 35 +++++++++++++++++++++++++++++++++++ templates/repo/nav.tmpl | 14 +++++++++----- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/public/css/gogs.css b/public/css/gogs.css index d1b1e155a8..f88b396397 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -445,7 +445,7 @@ html, body { padding: 0; } -#gogs-repo-watching .dropdown-menu .dropdown-item:hover .dropdown-header { +#gogs-repo-watching .dropdown-menu .dropdown-item:hover .dropdown-header, #gogs-repo-watching .dropdown-item .dropdown-header.text-primary { color: rgb(65, 131, 196); cursor: pointer; } @@ -678,7 +678,7 @@ html, body { } .file-content .file-body.file-code .lines-num span { - font-family: Menlo,Monaco,Consolas,"Courier New",monospace; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; line-height: 1.6; padding: 0 8px 0 10px; cursor: pointer; @@ -783,6 +783,10 @@ html, body { width: 120px; } +.commit-list .author { + min-width: 180px; +} + .guide-box pre, .guide-box .input-group { margin-top: 20px; margin-bottom: 30px; diff --git a/public/js/app.js b/public/js/app.js index 555410329b..9a5a0d68ad 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -181,6 +181,7 @@ function initUserSetting() { } function initRepository() { + // guide box script (function () { var $guide = $('.guide-box'); if ($guide.length) { @@ -197,6 +198,40 @@ function initRepository() { // todo copy to clipboard } })(); + + // watching script + (function () { + var $watch = $('#gogs-repo-watching'), + watchLink = $watch.data("watch"), + unwatchLink = $watch.data("unwatch"); + $watch.on('click', '.to-watch',function () { + if ($watch.hasClass("watching")) { + return false; + } + $.get(watchLink, function (json) { + if (json.ok) { + $watch.find('.text-primary').removeClass('text-primary'); + $watch.find('.to-watch h4').addClass('text-primary'); + $watch.find('.fa-eye-slash').removeClass('fa-eye-slash').addClass('fa-eye'); + $watch.removeClass("no-watching").addClass("watching"); + } + }); + return false; + }).on('click', '.to-unwatch', function () { + if ($watch.hasClass("no-watching")) { + return false; + } + $.get(unwatchLink, function (json) { + if (json.ok) { + $watch.find('.text-primary').removeClass('text-primary'); + $watch.find('.to-unwatch h4').addClass('text-primary'); + $watch.find('.fa-eye').removeClass('fa-eye').addClass('fa-eye-slash'); + $watch.removeClass("watching").addClass("no-watching"); + } + }); + return false; + }); + })(); } (function ($) { diff --git a/templates/repo/nav.tmpl b/templates/repo/nav.tmpl index e8685b08bc..718c429a2b 100644 --- a/templates/repo/nav.tmpl +++ b/templates/repo/nav.tmpl @@ -13,20 +13,24 @@ <span class="caret"></span> </button> </div> - <div class="btn-group {{if .IsRepositoryWatching}}watching{{end}}" id="gogs-repo-watching"> + <div class="btn-group {{if .IsRepositoryWatching}}watching{{else}}no-watching{{end}}" id="gogs-repo-watching" data-watch="/{{.SignedUser.Name}}/{{.Repository.Name}}/action/watch" data-unwatch="/{{.SignedUser.Name}}/{{.Repository.Name}}/action/unwatch"> + {{if .IsRepositoryWatching}} <button type="button" class="btn btn-default"><i class="fa fa-eye fa-lg fa-m"></i></button> + {{else}} + <button type="button" class="btn btn-default"><i class="fa fa-eye-slash fa-lg fa-m"></i></button> + {{end}} <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> <span class="caret"></span> <span class="sr-only">Toggle Dropdown</span> </button> <div class="dropdown-menu" role="menu"> - <div class="dropdown-item text-left" data-val="not-watching"> - <h4 role="presentation" class="dropdown-header">Not Watching</h4> + <div class="dropdown-item text-left to-unwatch"> + <h4 role="presentation" class="dropdown-header {{if not .IsRepositoryWatching}}text-primary{{end}}">Not Watching</h4> <p class="description">You only receive notifications for conversations in which you participate or are @mentioned.</p> <p class="divider"></p> </div> - <div class="dropdown-item text-left" data-val="watching"> - <h4 role="presentation" class="dropdown-header">Watching</h4> + <div class="dropdown-item text-left to-watch"> + <h4 role="presentation" class="dropdown-header {{if .IsRepositoryWatching}}text-primary{{end}}">Watching</h4> <p class="description">You receive notifications for all conversations in this repository.</p> </div> </div> From ef3c0b215df8429af9bc03a559259c77be32907c Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Thu, 20 Mar 2014 23:37:17 +0800 Subject: [PATCH 19/41] fix --- public/js/lib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/lib.js b/public/js/lib.js index 8deb6f7226..b5cc41c042 100644 --- a/public/js/lib.js +++ b/public/js/lib.js @@ -400,7 +400,7 @@ PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\u000 ["kwd",/^-[_a-z]+/],["typ",/^[A-Z_]\w*/],["pun",/^[,.;]/]]),["erlang","erl"]); // lang-go.js -PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["pln",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])+(?:'|$)|`[^`]*(?:`|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\/\*[\S\s]*?\*\/)/],["pln",/^(?:[^"'/`]|\/(?![*/]))+/]]),["go"]); +// PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["pln",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])+(?:'|$)|`[^`]*(?:`|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\/\*[\S\s]*?\*\/)/],["pln",/^(?:[^"'/`]|\/(?![*/]))+/]]),["go"]); // lang-hs.js PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\u000b\u000c\r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^\n\f\r'\\]|\\[^&])'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:--+[^\n\f\r]*|{-(?:[^-]|-+[^}-])*-})/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^\d'A-Za-z]|$)/, From 3b387336bfc097090d5b03f5b01e136bca56f8fd Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Thu, 20 Mar 2014 11:41:24 -0400 Subject: [PATCH 20/41] Add Repository/user name filter --- README.md | 2 +- gogs.go | 2 +- models/repo.go | 31 +++++++++++++++++++++++++++++++ models/user.go | 5 +++++ routers/repo/repo.go | 3 +++ routers/repo/single.go | 5 +++++ routers/user/user.go | 8 +++++--- 7 files changed, 51 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3a1023c6d9..4219e4ed03 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Gogs(Go Git Service) is a GitHub-like clone in the Go Programming Language. Since we choose to use pure Go implementation of Git manipulation, Gogs certainly supports **ALL platforms** that Go supports, including Linux, Max OS X, and Windows with **ZERO** dependency. -##### Current version: 0.1.1 Alpha +##### Current version: 0.1.4 Alpha ## Purpose diff --git a/gogs.go b/gogs.go index d32e3908c9..a600c53d12 100644 --- a/gogs.go +++ b/gogs.go @@ -20,7 +20,7 @@ import ( // Test that go1.1 tag above is included in builds. main.go refers to this definition. const go11tag = true -const APP_VER = "0.1.2.0320.1" +const APP_VER = "0.1.3.0320.1" func init() { base.AppVer = APP_VER diff --git a/models/repo.go b/models/repo.go index 052341ff6e..bcbc586785 100644 --- a/models/repo.go +++ b/models/repo.go @@ -12,6 +12,7 @@ import ( "os" "path" "path/filepath" + "regexp" "strings" "sync" "time" @@ -82,6 +83,7 @@ var ( ErrRepoAlreadyExist = errors.New("Repository already exist") ErrRepoNotExist = errors.New("Repository does not exist") ErrRepoFileNotExist = errors.New("Target Repo file does not exist") + ErrRepoNameIllegal = errors.New("Repository name contains illegal characters") ) func init() { @@ -104,6 +106,15 @@ func init() { os.Exit(2) } } + + // Initialize illegal patterns. + for i := range illegalPatterns[1:] { + pattern := "" + for j := range illegalPatterns[i+1] { + pattern += "[" + string(illegalPatterns[i+1][j]-32) + string(illegalPatterns[i+1][j]) + "]" + } + illegalPatterns[i+1] = pattern + } } // IsRepositoryExist returns true if the repository with given name under user has already existed. @@ -120,8 +131,28 @@ func IsRepositoryExist(user *User, repoName string) (bool, error) { return s.IsDir(), nil } +var ( + // Define as all lower case!! + illegalPatterns = []string{"[.][Gg][Ii][Tt]", "user", "help", "stars", "issues", "pulls", "commits", "admin", "repo", "template"} +) + +// IsLegalName returns false if name contains illegal characters. +func IsLegalName(repoName string) bool { + for _, pattern := range illegalPatterns { + has, _ := regexp.MatchString(pattern, repoName) + if has { + return false + } + } + return true +} + // CreateRepository creates a repository for given user or orgnaziation. func CreateRepository(user *User, repoName, desc, repoLang, license string, private bool, initReadme bool) (*Repository, error) { + if !IsLegalName(repoName) { + return nil, ErrRepoNameIllegal + } + isExist, err := IsRepositoryExist(user, repoName) if err != nil { return nil, err diff --git a/models/user.go b/models/user.go index fd89af6b3f..990e1954a5 100644 --- a/models/user.go +++ b/models/user.go @@ -79,6 +79,7 @@ var ( ErrUserAlreadyExist = errors.New("User already exist") ErrUserNotExist = errors.New("User does not exist") ErrEmailAlreadyUsed = errors.New("E-mail already used") + ErrUserNameIllegal = errors.New("User name contains illegal characters") ) // IsUserExist checks if given user name exist, @@ -108,6 +109,10 @@ func GetUserSalt() string { // RegisterUser creates record of a new user. func RegisterUser(user *User) (*User, error) { + if !IsLegalName(user.Name) { + return nil, ErrUserNameIllegal + } + isExist, err := IsUserExist(user.Name) if err != nil { return nil, err diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 556cc4343c..c83a6df522 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -31,6 +31,9 @@ func Create(ctx *middleware.Context, form auth.CreateRepoForm) { } else if err == models.ErrRepoAlreadyExist { ctx.RenderWithErr("Repository name has already been used", "repo/create", &form) return + } else if err == models.ErrRepoNameIllegal { + ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "repo/create", &form) + return } ctx.Handle(200, "repo.Create", err) } diff --git a/routers/repo/single.go b/routers/repo/single.go index f1b15cceed..eab49be919 100644 --- a/routers/repo/single.go +++ b/routers/repo/single.go @@ -217,6 +217,11 @@ func Setting(ctx *middleware.Context, params martini.Params) { title = t } + if len(params["branchname"]) == 0 { + params["branchname"] = "master" + } + + ctx.Data["Branchname"] = params["branchname"] ctx.Data["Title"] = title + " - settings" ctx.HTML(200, "repo/setting") } diff --git a/routers/user/user.go b/routers/user/user.go index be2c4d3839..ea6922591e 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -139,11 +139,13 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) { var err error if u, err = models.RegisterUser(u); err != nil { - switch err.Error() { - case models.ErrUserAlreadyExist.Error(): + switch err { + case models.ErrUserAlreadyExist: ctx.RenderWithErr("Username has been already taken", "user/signup", &form) - case models.ErrEmailAlreadyUsed.Error(): + case models.ErrEmailAlreadyUsed: ctx.RenderWithErr("E-mail address has been already used", "user/signup", &form) + case models.ErrUserNameIllegal: + ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "user/signup", &form) default: ctx.Handle(200, "user.SignUp", err) } From c44aa757d05c2711da1c6e90c0d9d73bcd387433 Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Thu, 20 Mar 2014 23:44:13 +0800 Subject: [PATCH 21/41] fix style --- public/css/gogs.css | 2 +- public/css/markdown.css | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/public/css/gogs.css b/public/css/gogs.css index f88b396397..89ae347004 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -683,7 +683,7 @@ html, body { padding: 0 8px 0 10px; cursor: pointer; display: block; - margin-top: 6px; + margin-top: 2px; font-size: 90%; } diff --git a/public/css/markdown.css b/public/css/markdown.css index 68094867b1..fb9d667fb0 100644 --- a/public/css/markdown.css +++ b/public/css/markdown.css @@ -123,6 +123,10 @@ padding: 0; } +.markdown > pre > ol.linenums > li { + margin-top: 2px; +} + .markdown > pre.linenums-style > ol.linenums { list-style-type: decimal; padding: 0 0 0 40px; @@ -246,6 +250,7 @@ /* a comment */ .com { color: #999988; + font-style: italic; } /* a type name */ From 686348974b1a1f6f11cab5244df2362f99e0a124 Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Thu, 20 Mar 2014 23:55:27 +0800 Subject: [PATCH 22/41] fix code style --- public/css/gogs.css | 2 +- public/css/markdown.css | 5 +++-- public/js/app.js | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/public/css/gogs.css b/public/css/gogs.css index 89ae347004..b799882352 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -651,7 +651,7 @@ html, body { overflow-y: hidden; } -.file-content .file-body pre { +.file-content .file-body.file-code pre { background-color: #FFF; border: none; } diff --git a/public/css/markdown.css b/public/css/markdown.css index fb9d667fb0..a810fa3ce7 100644 --- a/public/css/markdown.css +++ b/public/css/markdown.css @@ -112,6 +112,7 @@ overflow: auto; background: #f8f8f8; border: 1px solid #ddd; + padding: 0; } .markdown > pre.linenums { @@ -127,7 +128,7 @@ margin-top: 2px; } -.markdown > pre.linenums-style > ol.linenums { +.markdown > pre.nums-style > ol.linenums { list-style-type: decimal; padding: 0 0 0 40px; -webkit-box-shadow: inset 40px 0 0 #f5f5f5, inset 41px 0 0 #ccc; @@ -152,7 +153,7 @@ padding-bottom: 12px; } -.markdown > pre.linenums-style > ol.linenums > li { +.markdown > pre.nums-style > ol.linenums > li { border-left: 1px solid #ddd; } diff --git a/public/js/app.js b/public/js/app.js index 9a5a0d68ad..a264fc94eb 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -68,7 +68,7 @@ var Gogits = { Gogits.renderMarkdown = function () { var $md = $('.markdown'); var $pre = $md.find('pre > code').parent(); - $pre.addClass('prettyprint'); + $pre.addClass('prettyprint linenums'); prettyPrint(); var $lineNums = $pre.parent().siblings('.lines-num'); From 06631ab91f5d84b48d6f71ac8eaf4df740ba0282 Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Thu, 20 Mar 2014 16:04:56 -0400 Subject: [PATCH 23/41] Basic admin data table, models changes --- README.md | 2 +- gogs.go | 8 +- models/issue.go | 19 +++ models/models.go | 56 ++++----- models/publickey.go | 10 +- models/repo.go | 216 +++++++++++++++++---------------- models/user.go | 97 ++++++++------- modules/base/conf.go | 21 +++- routers/admin/admin.go | 15 +++ serve.go | 4 +- templates/admin/dashboard.tmpl | 2 +- templates/admin/repos.tmpl | 24 ++++ templates/admin/users.tmpl | 26 ++++ 13 files changed, 291 insertions(+), 209 deletions(-) create mode 100644 models/issue.go diff --git a/README.md b/README.md index 4219e4ed03..8c971fdb85 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ There are some very good products in this category such as [gitlab](http://gitla - Please see [Wiki](https://github.com/gogits/gogs/wiki) for project design, develop specification, change log and road map. - See [Trello Broad](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team. -- Try it before anything? Go down to **Installation -> Install from binary** section!. +- Try it before anything? Do it [online](http://try.gogits.org/Unknown/gogs) or go down to **Installation -> Install from binary** section! - Having troubles? Get help from [Troubleshooting](https://github.com/gogits/gogs/wiki/Troubleshooting). ## Features diff --git a/gogs.go b/gogs.go index a600c53d12..a9f7e6b5c4 100644 --- a/gogs.go +++ b/gogs.go @@ -15,12 +15,12 @@ import ( "github.com/gogits/gogs/modules/base" ) -// +build go1.1 +// +build go1.2 -// Test that go1.1 tag above is included in builds. main.go refers to this definition. -const go11tag = true +// Test that go1.2 tag above is included in builds. main.go refers to this definition. +const go12tag = true -const APP_VER = "0.1.3.0320.1" +const APP_VER = "0.1.4.0321" func init() { base.AppVer = APP_VER diff --git a/models/issue.go b/models/issue.go new file mode 100644 index 0000000000..c669d201f6 --- /dev/null +++ b/models/issue.go @@ -0,0 +1,19 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +type Issue struct { + Id int64 + RepoId int64 `xorm:"index"` + PosterId int64 +} + +type PullRequest struct { + Id int64 +} + +type Comment struct { + Id int64 +} diff --git a/models/models.go b/models/models.go index 2e0bb759d4..214d1c767a 100644 --- a/models/models.go +++ b/models/models.go @@ -15,30 +15,7 @@ import ( "github.com/gogits/gogs/modules/base" ) -var ( - orm *xorm.Engine - RepoRootPath string -) - -type Members struct { - Id int64 - OrgId int64 `xorm:"unique(s) index"` - UserId int64 `xorm:"unique(s)"` -} - -type Issue struct { - Id int64 - RepoId int64 `xorm:"index"` - PosterId int64 -} - -type PullRequest struct { - Id int64 -} - -type Comment struct { - Id int64 -} +var orm *xorm.Engine func setEngine() { dbType := base.Cfg.MustValue("database", "DB_TYPE") @@ -65,8 +42,8 @@ func setEngine() { os.Exit(2) } - // TODO: for serv command, MUST remove the output to os.stdout, so - // use log file to instead print to stdout + // WARNNING: for serv command, MUST remove the output to os.stdout, + // so use log file to instead print to stdout. //x.ShowDebug = true //orm.ShowErr = true @@ -77,20 +54,29 @@ func setEngine() { } orm.Logger = f orm.ShowSQL = true - - // Determine and create root git reposiroty path. - RepoRootPath = base.Cfg.MustValue("repository", "ROOT") - if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { - fmt.Printf("models.init(fail to create RepoRootPath(%s)): %v\n", RepoRootPath, err) - os.Exit(2) - } } func init() { setEngine() - if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Access), - new(Action), new(Watch)); err != nil { + if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Watch), + new(Action), new(Access)); err != nil { fmt.Printf("sync database struct error: %v\n", err) os.Exit(2) } } + +type Statistic struct { + Counter struct { + User, PublicKey, Repo, Watch, Action, Access int64 + } +} + +func GetStatistic() (stats Statistic) { + stats.Counter.User, _ = orm.Count(new(User)) + stats.Counter.PublicKey, _ = orm.Count(new(PublicKey)) + stats.Counter.Repo, _ = orm.Count(new(Repository)) + stats.Counter.Watch, _ = orm.Count(new(Watch)) + stats.Counter.Action, _ = orm.Count(new(Action)) + stats.Counter.Access, _ = orm.Count(new(Access)) + return stats +} diff --git a/models/publickey.go b/models/publickey.go index 092436d55f..c69bca681d 100644 --- a/models/publickey.go +++ b/models/publickey.go @@ -27,8 +27,12 @@ const ( ) var ( - sshOpLocker = sync.Mutex{} + ErrKeyAlreadyExist = errors.New("Public key already exist") +) +var sshOpLocker = sync.Mutex{} + +var ( sshPath string appPath string ) @@ -79,10 +83,6 @@ type PublicKey struct { Updated time.Time `xorm:"updated"` } -var ( - ErrKeyAlreadyExist = errors.New("Public key already exist") -) - // GenAuthorizedKey returns formatted public key string. func GenAuthorizedKey(keyId int64, key string) string { return fmt.Sprintf(TPL_PUBLICK_KEY+"\n", appPath, keyId, key) diff --git a/models/repo.go b/models/repo.go index bcbc586785..f7173d76d0 100644 --- a/models/repo.go +++ b/models/repo.go @@ -27,63 +27,18 @@ import ( "github.com/gogits/gogs/modules/log" ) -// Repository represents a git repository. -type Repository struct { - Id int64 - OwnerId int64 `xorm:"unique(s)"` - ForkId int64 - LowerName string `xorm:"unique(s) index not null"` - Name string `xorm:"index not null"` - Description string - Website string - Private bool - NumWatchs int - NumStars int - NumForks int - Created time.Time `xorm:"created"` - Updated time.Time `xorm:"updated"` -} - -// Watch is connection request for receiving repository notifycation. -type Watch struct { - Id int64 - RepoId int64 `xorm:"UNIQUE(watch)"` - UserId int64 `xorm:"UNIQUE(watch)"` -} - -// Watch or unwatch repository. -func WatchRepo(userId, repoId int64, watch bool) (err error) { - if watch { - _, err = orm.Insert(&Watch{RepoId: repoId, UserId: userId}) - } else { - _, err = orm.Delete(&Watch{0, repoId, userId}) - } - return err -} - -// GetWatches returns all watches of given repository. -func GetWatches(repoId int64) ([]Watch, error) { - watches := make([]Watch, 0, 10) - err := orm.Find(&watches, &Watch{RepoId: repoId}) - return watches, err -} - -// IsWatching checks if user has watched given repository. -func IsWatching(userId, repoId int64) bool { - has, _ := orm.Get(&Watch{0, repoId, userId}) - return has -} - var ( - gitInitLocker = sync.Mutex{} - LanguageIgns, Licenses []string + ErrRepoAlreadyExist = errors.New("Repository already exist") + ErrRepoNotExist = errors.New("Repository does not exist") + ErrRepoFileNotExist = errors.New("Target Repo file does not exist") + ErrRepoNameIllegal = errors.New("Repository name contains illegal characters") + ErrRepoFileNotLoaded = fmt.Errorf("repo file not loaded") ) +var gitInitLocker = sync.Mutex{} + var ( - ErrRepoAlreadyExist = errors.New("Repository already exist") - ErrRepoNotExist = errors.New("Repository does not exist") - ErrRepoFileNotExist = errors.New("Target Repo file does not exist") - ErrRepoNameIllegal = errors.New("Repository name contains illegal characters") + LanguageIgns, Licenses []string ) func init() { @@ -117,6 +72,23 @@ func init() { } } +// Repository represents a git repository. +type Repository struct { + Id int64 + OwnerId int64 `xorm:"unique(s)"` + ForkId int64 + LowerName string `xorm:"unique(s) index not null"` + Name string `xorm:"index not null"` + Description string + Website string + Private bool + NumWatches int + NumStars int + NumForks int + Created time.Time `xorm:"created"` + Updated time.Time `xorm:"updated"` +} + // IsRepositoryExist returns true if the repository with given name under user has already existed. func IsRepositoryExist(user *User, repoName string) (bool, error) { repo := Repository{OwnerId: user.Id} @@ -351,6 +323,60 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep return nil } +// GetRepos returns given number of repository objects with offset. +func GetRepos(num, offset int) ([]Repository, error) { + repos := make([]Repository, 0, num) + err := orm.Limit(num, offset).Asc("id").Find(&repos) + return repos, err +} + +func RepoPath(userName, repoName string) string { + return filepath.Join(UserPath(userName), repoName+".git") +} + +// DeleteRepository deletes a repository for a user or orgnaztion. +func DeleteRepository(userId, repoId int64, userName string) (err error) { + repo := &Repository{Id: repoId, OwnerId: userId} + has, err := orm.Get(repo) + if err != nil { + return err + } else if !has { + return ErrRepoNotExist + } + + session := orm.NewSession() + if err = session.Begin(); err != nil { + return err + } + if _, err = session.Delete(&Repository{Id: repoId}); err != nil { + session.Rollback() + return err + } + if _, err := session.Delete(&Access{UserName: userName, RepoName: repo.Name}); err != nil { + session.Rollback() + return err + } + rawSql := "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?" + if _, err = session.Exec(rawSql, userId); err != nil { + session.Rollback() + return err + } + if _, err = session.Delete(&Watch{RepoId: repoId}); err != nil { + session.Rollback() + return err + } + if err = session.Commit(); err != nil { + session.Rollback() + return err + } + if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil { + // TODO: log and delete manully + log.Error("delete repo %s/%s failed: %v", userName, repo.Name, err) + return err + } + return nil +} + // GetRepositoryByName returns the repository by given name under user if exists. func GetRepositoryByName(user *User, repoName string) (*Repository, error) { repo := &Repository{ @@ -388,6 +414,36 @@ func GetRepositoryCount(user *User) (int64, error) { return orm.Count(&Repository{OwnerId: user.Id}) } +// Watch is connection request for receiving repository notifycation. +type Watch struct { + Id int64 + RepoId int64 `xorm:"UNIQUE(watch)"` + UserId int64 `xorm:"UNIQUE(watch)"` +} + +// Watch or unwatch repository. +func WatchRepo(userId, repoId int64, watch bool) (err error) { + if watch { + _, err = orm.Insert(&Watch{RepoId: repoId, UserId: userId}) + } else { + _, err = orm.Delete(&Watch{0, repoId, userId}) + } + return err +} + +// GetWatches returns all watches of given repository. +func GetWatches(repoId int64) ([]Watch, error) { + watches := make([]Watch, 0, 10) + err := orm.Find(&watches, &Watch{RepoId: repoId}) + return watches, err +} + +// IsWatching checks if user has watched given repository. +func IsWatching(userId, repoId int64) bool { + has, _ := orm.Get(&Watch{0, repoId, userId}) + return has +} + func StarReposiory(user *User, repoName string) error { return nil } @@ -408,60 +464,6 @@ func ForkRepository(reposName string, userId int64) { } -func RepoPath(userName, repoName string) string { - return filepath.Join(UserPath(userName), repoName+".git") -} - -// DeleteRepository deletes a repository for a user or orgnaztion. -func DeleteRepository(userId, repoId int64, userName string) (err error) { - repo := &Repository{Id: repoId, OwnerId: userId} - has, err := orm.Get(repo) - if err != nil { - return err - } else if !has { - return ErrRepoNotExist - } - - session := orm.NewSession() - if err = session.Begin(); err != nil { - return err - } - if _, err = session.Delete(&Repository{Id: repoId}); err != nil { - session.Rollback() - return err - } - if _, err := session.Delete(&Access{UserName: userName, RepoName: repo.Name}); err != nil { - session.Rollback() - return err - } - rawSql := "UPDATE user SET num_repos = num_repos - 1 WHERE id = ?" - if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { - rawSql = "UPDATE \"user\" SET num_repos = num_repos - 1 WHERE id = ?" - } - if _, err = session.Exec(rawSql, userId); err != nil { - session.Rollback() - return err - } - if _, err = session.Delete(&Watch{RepoId: repoId}); err != nil { - session.Rollback() - return err - } - if err = session.Commit(); err != nil { - session.Rollback() - return err - } - if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil { - // TODO: log and delete manully - log.Error("delete repo %s/%s failed: %v", userName, repo.Name, err) - return err - } - return nil -} - -var ( - ErrRepoFileNotLoaded = fmt.Errorf("repo file not loaded") -) - // RepoFile represents a file object in git repository. type RepoFile struct { *git.TreeEntry diff --git a/models/user.go b/models/user.go index 990e1954a5..3c11091285 100644 --- a/models/user.go +++ b/models/user.go @@ -33,6 +33,14 @@ const ( LT_LDAP ) +var ( + ErrUserOwnRepos = errors.New("User still have ownership of repositories") + ErrUserAlreadyExist = errors.New("User already exist") + ErrUserNotExist = errors.New("User does not exist") + ErrEmailAlreadyUsed = errors.New("E-mail already used") + ErrUserNameIllegal = errors.New("User name contains illegal characters") +) + // User represents the object of individual and member of organization. type User struct { Id int64 @@ -67,20 +75,28 @@ func (user *User) AvatarLink() string { return "http://1.gravatar.com/avatar/" + user.Avatar } -type Follow struct { - Id int64 - UserId int64 `xorm:"unique(s)"` - FollowId int64 `xorm:"unique(s)"` - Created time.Time `xorm:"created"` +// NewGitSig generates and returns the signature of given user. +func (user *User) NewGitSig() *git.Signature { + return &git.Signature{ + Name: user.Name, + Email: user.Email, + When: time.Now(), + } } -var ( - ErrUserOwnRepos = errors.New("User still have ownership of repositories") - ErrUserAlreadyExist = errors.New("User already exist") - ErrUserNotExist = errors.New("User does not exist") - ErrEmailAlreadyUsed = errors.New("E-mail already used") - ErrUserNameIllegal = errors.New("User name contains illegal characters") -) +// EncodePasswd encodes password to safe format. +func (user *User) EncodePasswd() error { + newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(base.SecretKey), 16384, 8, 1, 64) + user.Passwd = fmt.Sprintf("%x", newPasswd) + return err +} + +// Member represents user is member of organization. +type Member struct { + Id int64 + OrgId int64 `xorm:"unique(member) index"` + UserId int64 `xorm:"unique(member)"` +} // IsUserExist checks if given user name exist, // the user name should be noncased unique. @@ -93,15 +109,6 @@ func IsEmailUsed(email string) (bool, error) { return orm.Get(&User{Email: email}) } -// NewGitSig generates and returns the signature of given user. -func (user *User) NewGitSig() *git.Signature { - return &git.Signature{ - Name: user.Name, - Email: user.Email, - When: time.Now(), - } -} - // return a user salt token func GetUserSalt() string { return base.GetRandomString(10) @@ -151,6 +158,13 @@ func RegisterUser(user *User) (*User, error) { return user, err } +// GetUsers returns given number of user objects with offset. +func GetUsers(num, offset int) ([]User, error) { + users := make([]User, 0, num) + err := orm.Limit(num, offset).Asc("id").Find(&users) + return users, err +} + // get user by erify code func getVerifyUser(code string) (user *User) { if len(code) <= base.TimeLimitCodeLength { @@ -229,24 +243,14 @@ func DeleteUser(user *User) error { return err } -// EncodePasswd encodes password to safe format. -func (user *User) EncodePasswd() error { - newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(base.SecretKey), 16384, 8, 1, 64) - user.Passwd = fmt.Sprintf("%x", newPasswd) - return err -} - // UserPath returns the path absolute path of user repositories. func UserPath(userName string) string { - return filepath.Join(RepoRootPath, strings.ToLower(userName)) + return filepath.Join(base.RepoRootPath, strings.ToLower(userName)) } func GetUserByKeyId(keyId int64) (*User, error) { user := new(User) - rawSql := "SELECT a.* FROM user AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?" - if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { - rawSql = "SELECT a.* FROM \"user\" AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?" - } + rawSql := "SELECT a.* FROM `user` AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?" has, err := orm.Sql(rawSql, keyId).Get(user) if err != nil { return nil, err @@ -303,6 +307,13 @@ func LoginUserPlain(name, passwd string) (*User, error) { return &user, err } +// Follow is connection request for receiving user notifycation. +type Follow struct { + Id int64 + UserId int64 `xorm:"unique(follow)"` + FollowId int64 `xorm:"unique(follow)"` +} + // FollowUser marks someone be another's follower. func FollowUser(userId int64, followId int64) (err error) { session := orm.NewSession() @@ -314,19 +325,13 @@ func FollowUser(userId int64, followId int64) (err error) { return err } - rawSql := "UPDATE user SET num_followers = num_followers + 1 WHERE id = ?" - if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { - rawSql = "UPDATE \"user\" SET num_followers = num_followers + 1 WHERE id = ?" - } + rawSql := "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?" if _, err = session.Exec(rawSql, followId); err != nil { session.Rollback() return err } - rawSql = "UPDATE user SET num_followings = num_followings + 1 WHERE id = ?" - if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { - rawSql = "UPDATE \"user\" SET num_followings = num_followings + 1 WHERE id = ?" - } + rawSql = "UPDATE `user` SET num_followings = num_followings + 1 WHERE id = ?" if _, err = session.Exec(rawSql, userId); err != nil { session.Rollback() return err @@ -345,19 +350,13 @@ func UnFollowUser(userId int64, unFollowId int64) (err error) { return err } - rawSql := "UPDATE user SET num_followers = num_followers - 1 WHERE id = ?" - if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { - rawSql = "UPDATE \"user\" SET num_followers = num_followers - 1 WHERE id = ?" - } + rawSql := "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?" if _, err = session.Exec(rawSql, unFollowId); err != nil { session.Rollback() return err } - rawSql = "UPDATE user SET num_followings = num_followings - 1 WHERE id = ?" - if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { - rawSql = "UPDATE \"user\" SET num_followings = num_followings - 1 WHERE id = ?" - } + rawSql = "UPDATE `user` SET num_followings = num_followings - 1 WHERE id = ?" if _, err = session.Exec(rawSql, userId); err != nil { session.Rollback() return err diff --git a/modules/base/conf.go b/modules/base/conf.go index fdbf3ad385..81f32bd591 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -26,12 +26,14 @@ type Mailer struct { } var ( - AppVer string - AppName string - AppLogo string - AppUrl string - Domain string - SecretKey string + AppVer string + AppName string + AppLogo string + AppUrl string + Domain string + SecretKey string + RepoRootPath string + Cfg *goconfig.ConfigFile MailService *Mailer ) @@ -173,6 +175,13 @@ func init() { AppUrl = Cfg.MustValue("server", "ROOT_URL") Domain = Cfg.MustValue("server", "DOMAIN") SecretKey = Cfg.MustValue("security", "SECRET_KEY") + + // Determine and create root git reposiroty path. + RepoRootPath = Cfg.MustValue("repository", "ROOT") + if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { + fmt.Printf("models.init(fail to create RepoRootPath(%s)): %v\n", RepoRootPath, err) + os.Exit(2) + } } func NewServices() { diff --git a/routers/admin/admin.go b/routers/admin/admin.go index c7523b7f59..a37f1207c9 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -5,20 +5,35 @@ package admin import ( + "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/middleware" ) func Dashboard(ctx *middleware.Context) { ctx.Data["Title"] = "Admin Dashboard" + ctx.Data["Stats"] = models.GetStatistic() ctx.HTML(200, "admin/dashboard") } func Users(ctx *middleware.Context) { ctx.Data["Title"] = "User Management" + + var err error + ctx.Data["Users"], err = models.GetUsers(100, 0) + if err != nil { + ctx.Handle(200, "admin.Users", err) + return + } ctx.HTML(200, "admin/users") } func Repositories(ctx *middleware.Context) { ctx.Data["Title"] = "Repository Management" + var err error + ctx.Data["Repos"], err = models.GetRepos(100, 0) + if err != nil { + ctx.Handle(200, "admin.Repositories", err) + return + } ctx.HTML(200, "admin/repos") } diff --git a/serve.go b/serve.go index ddc670235e..2a17a33490 100644 --- a/serve.go +++ b/serve.go @@ -12,7 +12,9 @@ import ( "strings" "github.com/codegangsta/cli" + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/base" ) var ( @@ -144,7 +146,7 @@ func runServ(*cli.Context) { } gitcmd := exec.Command(verb, rRepo) - gitcmd.Dir = models.RepoRootPath + gitcmd.Dir = base.RepoRootPath gitcmd.Stdout = os.Stdout gitcmd.Stdin = os.Stdin gitcmd.Stderr = os.Stderr diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index 84456c85b8..6a914b65f7 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -16,7 +16,7 @@ </div> <div class="panel-body"> - Gogs database has 4 users, 3 repositories, 4 SSH keys. + Gogs database has <b>{{.Stats.Counter.User}}</b> users, <b>{{.Stats.Counter.PublicKey}}</b> SSH keys, <b>{{.Stats.Counter.Repo}}</b> repositories, <b>{{.Stats.Counter.Watch}}</b> watches, <b>{{.Stats.Counter.Action}}</b> actions, and <b>{{.Stats.Counter.Access}}</b> accesses. </div> </div> </div> diff --git a/templates/admin/repos.tmpl b/templates/admin/repos.tmpl index ec7f47e090..4522c66792 100644 --- a/templates/admin/repos.tmpl +++ b/templates/admin/repos.tmpl @@ -16,6 +16,30 @@ </div> <div class="panel-body"> + <table class="table table-striped"> + <thead> + <tr> + <th>Id</th> + <th>Name</th> + <th>Private</th> + <th>Watches</th> + <th>Forks</th> + <th>Created</th> + </tr> + </thead> + <tbody> + {{range .Repos}} + <tr> + <td>{{.Id}}</td> + <td>{{.Name}}</td> + <td><i class="fa fa{{if .Private}}-check{{end}}-square-o"></i></td> + <td>{{.NumWatches}}</td> + <td>{{.NumForks}}</td> + <td>{{DateFormat .Created "M d, Y"}}</td> + </tr> + {{end}} + </tbody> + </table> </div> </div> </div> diff --git a/templates/admin/users.tmpl b/templates/admin/users.tmpl index 8acf256d05..c087f268f2 100644 --- a/templates/admin/users.tmpl +++ b/templates/admin/users.tmpl @@ -16,6 +16,32 @@ </div> <div class="panel-body"> + <table class="table table-striped"> + <thead> + <tr> + <th>Id</th> + <th>Name</th> + <th>E-mail</th> + <th>Actived</th> + <th>Admin</th> + <th>Repos</th> + <th>Join</th> + </tr> + </thead> + <tbody> + {{range .Users}} + <tr> + <td>{{.Id}}</td> + <td>{{.Name}}</td> + <td>{{.Email}}</td> + <td><i class="fa fa{{if .IsActive}}-check{{end}}-square-o"></i></td> + <td><i class="fa fa{{if .IsAdmin}}-check{{end}}-square-o"></i></td> + <td>{{.NumRepos}}</td> + <td>{{DateFormat .Created "M d, Y"}}</td> + </tr> + {{end}} + </tbody> + </table> </div> </div> </div> From 53a17bbd240e0dd3755b7a666792d69e358f3e00 Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Thu, 20 Mar 2014 16:14:50 -0400 Subject: [PATCH 24/41] Bug fix --- models/repo.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/models/repo.go b/models/repo.go index f7173d76d0..f5ceaf7631 100644 --- a/models/repo.go +++ b/models/repo.go @@ -424,9 +424,18 @@ type Watch struct { // Watch or unwatch repository. func WatchRepo(userId, repoId int64, watch bool) (err error) { if watch { - _, err = orm.Insert(&Watch{RepoId: repoId, UserId: userId}) + if _, err = orm.Insert(&Watch{RepoId: repoId, UserId: userId}); err != nil { + return err + } + + rawSql := "UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?" + _, err = orm.Exec(rawSql, repoId) } else { - _, err = orm.Delete(&Watch{0, repoId, userId}) + if _, err = orm.Delete(&Watch{0, repoId, userId}); err != nil { + return err + } + rawSql := "UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?" + _, err = orm.Exec(rawSql, repoId) } return err } From 369ddf76a8ae6916ab72f1fa26c81b44c456c6ea Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Fri, 21 Mar 2014 01:09:22 -0400 Subject: [PATCH 25/41] Batch fix --- README.md | 2 +- conf/app.ini | 8 +++++++- models/action.go | 4 ++++ models/models.go | 5 +++++ models/repo.go | 28 +++++++++++++++++++++++++--- modules/base/conf.go | 8 +++++--- modules/base/template.go | 11 ++++++++++- routers/user/user.go | 6 ++++++ templates/admin/repos.tmpl | 4 +++- templates/admin/users.tmpl | 2 +- templates/base/navbar.tmpl | 3 ++- templates/repo/single.tmpl | 2 +- templates/user/signin.tmpl | 2 +- templates/user/signup.tmpl | 4 ++++ 14 files changed, 75 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 8c971fdb85..3668ae8998 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ There are some very good products in this category such as [gitlab](http://gitla - Repository viewer. - Gravatar support. - Mail service(register). -- Supports MySQL and PostgreSQL. +- Supports MySQL, PostgreSQL and SQLite3(binary release only). ## Installation diff --git a/conf/app.ini b/conf/app.ini index 658f7c0151..d38cd1f05e 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -18,7 +18,7 @@ HTTP_ADDR = HTTP_PORT = 3000 [database] -; Either "mysql" or "postgres", it's your choice +; Either "mysql", "postgres" or "sqlite3"(binary release only), it's your choice DB_TYPE = mysql HOST = NAME = gogs @@ -26,6 +26,10 @@ USER = root PASSWD = ; For "postgres" only, either "disable", "require" or "verify-full" SSL_MODE = disable +; For "sqlite3" only +PATH = data/gogs.db + +[admin] [security] ; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! @@ -36,6 +40,8 @@ ACTIVE_CODE_LIVE_MINUTES = 180 RESET_PASSWD_CODE_LIVE_MINUTES = 180 ; User need to confirm e-mail for registration REGISTER_EMAIL_CONFIRM = false +; Does not allow register and admin create account only +DISENABLE_REGISTERATION = false [mailer] ENABLED = false diff --git a/models/action.go b/models/action.go index b3be093533..107d4b1057 100644 --- a/models/action.go +++ b/models/action.go @@ -64,6 +64,10 @@ func CommitRepoAction(userId int64, userName string, watches = append(watches, Watch{UserId: userId}) for i := range watches { + if userId == watches[i].UserId && i > 0 { + continue // Do not add twice in case author watches his/her repository. + } + _, err = orm.InsertOne(&Action{ UserId: watches[i].UserId, ActUserId: userId, diff --git a/models/models.go b/models/models.go index 214d1c767a..8df230975f 100644 --- a/models/models.go +++ b/models/models.go @@ -7,6 +7,7 @@ package models import ( "fmt" "os" + "path" _ "github.com/go-sql-driver/mysql" _ "github.com/lib/pq" @@ -23,6 +24,7 @@ func setEngine() { dbName := base.Cfg.MustValue("database", "NAME") dbUser := base.Cfg.MustValue("database", "USER") dbPwd := base.Cfg.MustValue("database", "PASSWD") + dbPath := base.Cfg.MustValue("database", "PATH", "data/gogs.db") sslMode := base.Cfg.MustValue("database", "SSL_MODE") var err error @@ -33,6 +35,9 @@ func setEngine() { case "postgres": orm, err = xorm.NewEngine("postgres", fmt.Sprintf("user=%s password=%s dbname=%s sslmode=%s", dbUser, dbPwd, dbName, sslMode)) + case "sqlite3": + os.MkdirAll(path.Dir(dbPath), os.ModePerm) + orm, err = xorm.NewEngine("sqlite3", dbPath) default: fmt.Printf("Unknown database type: %s\n", dbType) os.Exit(2) diff --git a/models/repo.go b/models/repo.go index f5ceaf7631..93f68ceddf 100644 --- a/models/repo.go +++ b/models/repo.go @@ -323,11 +323,33 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep return nil } +// UserRepo reporesents a repository with user name. +type UserRepo struct { + *Repository + UserName string +} + // GetRepos returns given number of repository objects with offset. -func GetRepos(num, offset int) ([]Repository, error) { +func GetRepos(num, offset int) ([]UserRepo, error) { repos := make([]Repository, 0, num) - err := orm.Limit(num, offset).Asc("id").Find(&repos) - return repos, err + if err := orm.Limit(num, offset).Asc("id").Find(&repos); err != nil { + return nil, err + } + + urepos := make([]UserRepo, len(repos)) + for i := range repos { + urepos[i].Repository = &repos[i] + u := new(User) + has, err := orm.Id(urepos[i].Repository.OwnerId).Get(u) + if err != nil { + return nil, err + } else if !has { + return nil, ErrUserNotExist + } + urepos[i].UserName = u.Name + } + + return urepos, nil } func RepoPath(userName, repoName string) string { diff --git a/modules/base/conf.go b/modules/base/conf.go index 81f32bd591..41b66b69f8 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -39,9 +39,10 @@ var ( ) var Service struct { - RegisterEmailConfirm bool - ActiveCodeLives int - ResetPwdCodeLives int + RegisterEmailConfirm bool + DisenableRegisteration bool + ActiveCodeLives int + ResetPwdCodeLives int } func exeDir() (string, error) { @@ -68,6 +69,7 @@ var logLevels = map[string]string{ func newService() { Service.ActiveCodeLives = Cfg.MustInt("service", "ACTIVE_CODE_LIVE_MINUTES", 180) Service.ResetPwdCodeLives = Cfg.MustInt("service", "RESET_PASSWD_CODE_LIVE_MINUTES", 180) + Service.DisenableRegisteration = Cfg.MustBool("service", "DISENABLE_REGISTERATION", false) } func newLogService() { diff --git a/modules/base/template.go b/modules/base/template.go index e596d1dada..8d95dbeab7 100644 --- a/modules/base/template.go +++ b/modules/base/template.go @@ -33,6 +33,10 @@ func List(l *list.List) chan interface{} { return c } +var mailDomains = map[string]string{ + "gmail.com": "gmail.com", +} + var TemplateFuncs template.FuncMap = map[string]interface{}{ "AppName": func() string { return AppName @@ -56,7 +60,12 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ "DateFormat": DateFormat, "List": List, "Mail2Domain": func(mail string) string { - return "mail." + strings.Split(mail, "@")[1] + suffix := strings.SplitN(mail, "@", 2)[1] + domain, ok := mailDomains[suffix] + if !ok { + return "mail." + suffix + } + return domain }, "SubStr": func(str string, start, length int) string { return str[start : start+length] diff --git a/routers/user/user.go b/routers/user/user.go index ea6922591e..40b594ab80 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -112,6 +112,12 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) { ctx.Data["Title"] = "Sign Up" ctx.Data["PageIsSignUp"] = true + if base.Service.DisenableRegisteration { + ctx.Data["DisenableRegisteration"] = true + ctx.HTML(200, "user/signup") + return + } + if ctx.Req.Method == "GET" { ctx.HTML(200, "user/signup") return diff --git a/templates/admin/repos.tmpl b/templates/admin/repos.tmpl index 4522c66792..f4834c9060 100644 --- a/templates/admin/repos.tmpl +++ b/templates/admin/repos.tmpl @@ -20,6 +20,7 @@ <thead> <tr> <th>Id</th> + <th>Owner</th> <th>Name</th> <th>Private</th> <th>Watches</th> @@ -31,7 +32,8 @@ {{range .Repos}} <tr> <td>{{.Id}}</td> - <td>{{.Name}}</td> + <th>{{.UserName}}</th> + <td><a href="/{{.UserName}}/{{.Name}}">{{.Name}}</a></td> <td><i class="fa fa{{if .Private}}-check{{end}}-square-o"></i></td> <td>{{.NumWatches}}</td> <td>{{.NumForks}}</td> diff --git a/templates/admin/users.tmpl b/templates/admin/users.tmpl index c087f268f2..b690e1771e 100644 --- a/templates/admin/users.tmpl +++ b/templates/admin/users.tmpl @@ -32,7 +32,7 @@ {{range .Users}} <tr> <td>{{.Id}}</td> - <td>{{.Name}}</td> + <td><a href="/user/{{.Name}}">{{.Name}}</a></td> <td>{{.Email}}</td> <td><i class="fa fa{{if .IsActive}}-check{{end}}-square-o"></i></td> <td><i class="fa fa{{if .IsAdmin}}-check{{end}}-square-o"></i></td> diff --git a/templates/base/navbar.tmpl b/templates/base/navbar.tmpl index 9c064d07e7..d775191a5a 100644 --- a/templates/base/navbar.tmpl +++ b/templates/base/navbar.tmpl @@ -11,7 +11,8 @@ <a class="navbar-right gogs-nav-item{{if .PageIsNewRepo}} active{{end}}" href="/repo/create" data-toggle="tooltip" data-placement="bottom" title="New Repository"><i class="fa fa-plus fa-lg"></i></a> <a class="navbar-right gogs-nav-item{{if .PageIsUserSetting}} active{{end}}" href="/user/setting" data-toggle="tooltip" data-placement="bottom" title="Setting"><i class="fa fa-cogs fa-lg"></i></a> {{if .IsAdmin}}<a class="navbar-right gogs-nav-item{{if .PageIsAdmin}} active{{end}}" href="/admin" data-toggle="tooltip" data-placement="bottom" title="Admin"><i class="fa fa-gear fa-lg"></i></a>{{end}} - {{else}}<a id="gogs-nav-signin" class="gogs-nav-item navbar-right navbar-btn btn btn-danger" href="/user/login/">Sign in</a>{{end}} + {{else}}<a id="gogs-nav-signin" class="gogs-nav-item navbar-right navbar-btn btn btn-success" href="/user/login/">Sign In</a> + <a id="gogs-nav-signup" class="gogs-nav-item navbar-right navbar-btn btn btn-info" href="/user/sign_up/">Sign Up</a>{{end}} </nav> </div> </div> diff --git a/templates/repo/single.tmpl b/templates/repo/single.tmpl index 8a7b5e479b..a1c3cfbb26 100644 --- a/templates/repo/single.tmpl +++ b/templates/repo/single.tmpl @@ -15,7 +15,7 @@ <b class="caret"></b></a> <ul class="dropdown-menu"> {{range .Branches}} - <li><a {{if eq . $.Branchname}}class="current" {{end}}href="{{$.BranchLink}}">{{.}}</a></li> + <li><a {{if eq . $.Branchname}}class="current" {{end}}href="/{{$.Username}}/{{$.Reponame}}/src/{{.}}">{{.}}</a></li> {{end}} </ul> </div> diff --git a/templates/user/signin.tmpl b/templates/user/signin.tmpl index e60cedec88..a49bf11405 100644 --- a/templates/user/signin.tmpl +++ b/templates/user/signin.tmpl @@ -32,7 +32,7 @@ </div> <div class="form-group text-center" id="gogs-social-login"> - <a class="btn btn-default btn-lg">Social Login</a> + <a class="btn btn-danger btn-lg">Register new account</a> </div> </form> </div> diff --git a/templates/user/signup.tmpl b/templates/user/signup.tmpl index 187364de8f..069d34a5b2 100644 --- a/templates/user/signup.tmpl +++ b/templates/user/signup.tmpl @@ -2,6 +2,9 @@ {{template "base/navbar" .}} <div class="container" id="gogs-body" data-page="user-signup"> <form action="/user/sign_up" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> + {{if .DisenableRegisteration}} + Sorry, registeration has been disenabled, you can only get account from administrator. + {{else}} <h3>Sign Up</h3> <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> @@ -43,6 +46,7 @@ <a href="/user/login">Already have an account? Sign in now!</a> </div> </div> + {{end}} </form> </div> {{template "base/footer" .}} \ No newline at end of file From f6596f11c4aacd3c7c30098f7ffe79e936d21583 Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Fri, 21 Mar 2014 01:48:10 -0400 Subject: [PATCH 26/41] All configuration reload-able --- README.md | 1 + models/models.go | 39 +++++++++++++++++++++------------- models/models_test.go | 3 +++ models/repo.go | 4 +++- modules/base/conf.go | 2 +- modules/mailer/mailer.go | 2 +- routers/admin/admin.go | 10 +++++++++ templates/admin/config.tmpl | 17 +++++++++++++++ templates/admin/dashboard.tmpl | 9 +------- templates/admin/nav.tmpl | 8 +++++++ templates/admin/repos.tmpl | 9 +------- templates/admin/users.tmpl | 9 +------- web.go | 20 ++++++++++++++--- 13 files changed, 88 insertions(+), 45 deletions(-) create mode 100644 templates/admin/config.tmpl create mode 100644 templates/admin/nav.tmpl diff --git a/README.md b/README.md index 3668ae8998..e78ce8fe99 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ There are some very good products in this category such as [gitlab](http://gitla - Repository viewer. - Gravatar support. - Mail service(register). +- Administration panel. - Supports MySQL, PostgreSQL and SQLite3(binary release only). ## Installation diff --git a/models/models.go b/models/models.go index 8df230975f..bb0015d3de 100644 --- a/models/models.go +++ b/models/models.go @@ -16,30 +16,39 @@ import ( "github.com/gogits/gogs/modules/base" ) -var orm *xorm.Engine +var ( + orm *xorm.Engine + + dbCfg struct { + Type, Host, Name, User, Pwd, Path, SslMode string + } +) + +func LoadModelsConfig() { + dbCfg.Type = base.Cfg.MustValue("database", "DB_TYPE") + dbCfg.Host = base.Cfg.MustValue("database", "HOST") + dbCfg.Name = base.Cfg.MustValue("database", "NAME") + dbCfg.User = base.Cfg.MustValue("database", "USER") + dbCfg.Pwd = base.Cfg.MustValue("database", "PASSWD") + dbCfg.Path = base.Cfg.MustValue("database", "PATH", "data/gogs.db") + dbCfg.SslMode = base.Cfg.MustValue("database", "SSL_MODE") +} func setEngine() { - dbType := base.Cfg.MustValue("database", "DB_TYPE") - dbHost := base.Cfg.MustValue("database", "HOST") - dbName := base.Cfg.MustValue("database", "NAME") - dbUser := base.Cfg.MustValue("database", "USER") - dbPwd := base.Cfg.MustValue("database", "PASSWD") - dbPath := base.Cfg.MustValue("database", "PATH", "data/gogs.db") - sslMode := base.Cfg.MustValue("database", "SSL_MODE") var err error - switch dbType { + switch dbCfg.Type { case "mysql": orm, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@%s/%s?charset=utf8", - dbUser, dbPwd, dbHost, dbName)) + dbCfg.User, dbCfg.Pwd, dbCfg.Host, dbCfg.Name)) case "postgres": orm, err = xorm.NewEngine("postgres", fmt.Sprintf("user=%s password=%s dbname=%s sslmode=%s", - dbUser, dbPwd, dbName, sslMode)) + dbCfg.User, dbCfg.Pwd, dbCfg.Name, dbCfg.SslMode)) case "sqlite3": - os.MkdirAll(path.Dir(dbPath), os.ModePerm) - orm, err = xorm.NewEngine("sqlite3", dbPath) + os.MkdirAll(path.Dir(dbCfg.Path), os.ModePerm) + orm, err = xorm.NewEngine("sqlite3", dbCfg.Path) default: - fmt.Printf("Unknown database type: %s\n", dbType) + fmt.Printf("Unknown database type: %s\n", dbCfg.Type) os.Exit(2) } if err != nil { @@ -61,7 +70,7 @@ func setEngine() { orm.ShowSQL = true } -func init() { +func NewEngine() { setEngine() if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Watch), new(Action), new(Access)); err != nil { diff --git a/models/models_test.go b/models/models_test.go index c44ef476c6..d0f734d678 100644 --- a/models/models_test.go +++ b/models/models_test.go @@ -13,6 +13,9 @@ import ( ) func init() { + LoadModelsConfig() + NewEngine() + var err error orm, err = xorm.NewEngine("sqlite3", "./test.db") if err != nil { diff --git a/models/repo.go b/models/repo.go index 93f68ceddf..f252004785 100644 --- a/models/repo.go +++ b/models/repo.go @@ -41,10 +41,12 @@ var ( LanguageIgns, Licenses []string ) -func init() { +func LoadRepoConfig() { LanguageIgns = strings.Split(base.Cfg.MustValue("repository", "LANG_IGNS"), "|") Licenses = strings.Split(base.Cfg.MustValue("repository", "LICENSES"), "|") +} +func NewRepoContext() { zip.Verbose = false // Check if server has basic git setting. diff --git a/modules/base/conf.go b/modules/base/conf.go index 41b66b69f8..42d50da4f3 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -148,7 +148,7 @@ func newRegisterMailService() { log.Info("Register Mail Service Enabled") } -func init() { +func NewConfigContext() { var err error workDir, err := exeDir() if err != nil { diff --git a/modules/mailer/mailer.go b/modules/mailer/mailer.go index 150607f8c4..da63e01d2a 100644 --- a/modules/mailer/mailer.go +++ b/modules/mailer/mailer.go @@ -40,7 +40,7 @@ func (m Message) Content() string { var mailQueue chan *Message -func init() { +func NewMailerContext() { mailQueue = make(chan *Message, base.Cfg.MustInt("mailer", "SEND_BUFFER_LEN", 10)) go processMailQueue() } diff --git a/routers/admin/admin.go b/routers/admin/admin.go index a37f1207c9..1095a599b9 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -11,12 +11,14 @@ import ( func Dashboard(ctx *middleware.Context) { ctx.Data["Title"] = "Admin Dashboard" + ctx.Data["PageIsDashboard"] = true ctx.Data["Stats"] = models.GetStatistic() ctx.HTML(200, "admin/dashboard") } func Users(ctx *middleware.Context) { ctx.Data["Title"] = "User Management" + ctx.Data["PageIsUsers"] = true var err error ctx.Data["Users"], err = models.GetUsers(100, 0) @@ -29,6 +31,8 @@ func Users(ctx *middleware.Context) { func Repositories(ctx *middleware.Context) { ctx.Data["Title"] = "Repository Management" + ctx.Data["PageIsRepos"] = true + var err error ctx.Data["Repos"], err = models.GetRepos(100, 0) if err != nil { @@ -37,3 +41,9 @@ func Repositories(ctx *middleware.Context) { } ctx.HTML(200, "admin/repos") } + +func Config(ctx *middleware.Context) { + ctx.Data["Title"] = "Server Configuration" + ctx.Data["PageIsConfig"] = true + ctx.HTML(200, "admin/config") +} diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl new file mode 100644 index 0000000000..d209bcdfd9 --- /dev/null +++ b/templates/admin/config.tmpl @@ -0,0 +1,17 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +<div id="gogs-body" class="container" data-page="admin"> + {{template "admin/nav" .}} + <div id="gogs-admin-container" class="col-md-9"> + <div class="panel panel-default"> + <div class="panel-heading"> + Server Configuration + </div> + + <div class="panel-body"> + + </div> + </div> + </div> +</div> +{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index 6a914b65f7..8950f50cac 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -1,14 +1,7 @@ {{template "base/head" .}} {{template "base/navbar" .}} <div id="gogs-body" class="container" data-page="admin"> - <div id="gogs-user-setting-nav" class="col-md-3"> - <ul class="list-group" data-init="tabs"> - <li class="list-group-item active"><a href="/admin"><i class="fa fa-tachometer fa-lg"></i> Dashboard</a></li> - <li class="list-group-item"><a href="/admin/users"><i class="fa fa-users fa-lg"></i> Users</a></li> - <li class="list-group-item"><a href="/admin/repos"><i class="fa fa-book fa-lg"></i> Repositories</a></li> - </ul> - </div> - + {{template "admin/nav" .}} <div id="gogs-admin-container" class="col-md-9"> <div class="panel panel-default"> <div class="panel-heading"> diff --git a/templates/admin/nav.tmpl b/templates/admin/nav.tmpl new file mode 100644 index 0000000000..bb704ee3fb --- /dev/null +++ b/templates/admin/nav.tmpl @@ -0,0 +1,8 @@ +<div id="gogs-user-setting-nav" class="col-md-3"> + <ul class="list-group" data-init="tabs"> + <li class="list-group-item{{if .PageIsDashboard}} active{{end}}"><a href="/admin"><i class="fa fa-tachometer fa-lg"></i> Dashboard</a></li> + <li class="list-group-item{{if .PageIsUsers}} active{{end}}"><a href="/admin/users"><i class="fa fa-users fa-lg"></i> Users</a></li> + <li class="list-group-item{{if .PageIsRepos}} active{{end}}"><a href="/admin/repos"><i class="fa fa-book fa-lg"></i> Repositories</a></li> + <li class="list-group-item{{if .PageIsConfig}} active{{end}}"><a href="/admin/config"><i class="fa fa-cogs fa-lg"></i> Configuration</a></li> + </ul> +</div> \ No newline at end of file diff --git a/templates/admin/repos.tmpl b/templates/admin/repos.tmpl index f4834c9060..a1f41d8365 100644 --- a/templates/admin/repos.tmpl +++ b/templates/admin/repos.tmpl @@ -1,14 +1,7 @@ {{template "base/head" .}} {{template "base/navbar" .}} <div id="gogs-body" class="container" data-page="admin"> - <div id="gogs-user-setting-nav" class="col-md-3"> - <ul class="list-group" data-init="tabs"> - <li class="list-group-item"><a href="/admin"><i class="fa fa-tachometer fa-lg"></i> Dashboard</a></li> - <li class="list-group-item"><a href="/admin/users"><i class="fa fa-users fa-lg"></i> Users</a></li> - <li class="list-group-item active"><a href="/admin/repos"><i class="fa fa-book fa-lg"></i> Repositories</a></li> - </ul> - </div> - + {{template "admin/nav" .}} <div id="gogs-admin-container" class="col-md-9"> <div class="panel panel-default"> <div class="panel-heading"> diff --git a/templates/admin/users.tmpl b/templates/admin/users.tmpl index b690e1771e..ae2b5bbb65 100644 --- a/templates/admin/users.tmpl +++ b/templates/admin/users.tmpl @@ -1,14 +1,7 @@ {{template "base/head" .}} {{template "base/navbar" .}} <div id="gogs-body" class="container" data-page="admin"> - <div id="gogs-user-setting-nav" class="col-md-3"> - <ul class="list-group" data-init="tabs"> - <li class="list-group-item"><a href="/admin"><i class="fa fa-tachometer fa-lg"></i> Dashboard</a></li> - <li class="list-group-item active"><a href="/admin/users"><i class="fa fa-users fa-lg"></i> Users</a></li> - <li class="list-group-item"><a href="/admin/repos"><i class="fa fa-book fa-lg"></i> Repositories</a></li> - </ul> - </div> - + {{template "admin/nav" .}} <div id="gogs-admin-container" class="col-md-9"> <div class="panel panel-default"> <div class="panel-heading"> diff --git a/web.go b/web.go index a5a8305aee..648cb9d79c 100644 --- a/web.go +++ b/web.go @@ -16,9 +16,11 @@ import ( "github.com/gogits/binding" + "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/mailer" "github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/routers" "github.com/gogits/gogs/routers/admin" @@ -36,6 +38,16 @@ gogs web`, Flags: []cli.Flag{}, } +// globalInit is for global configuration reload-able. +func globalInit() { + base.NewConfigContext() + mailer.NewMailerContext() + models.LoadModelsConfig() + models.LoadRepoConfig() + models.NewRepoContext() + models.NewEngine() +} + // Check run mode(Default of martini is Dev). func checkRunMode() { switch base.Cfg.MustValue("", "RUN_MODE") { @@ -59,6 +71,7 @@ func newMartini() *martini.ClassicMartini { } func runWeb(*cli.Context) { + globalInit() base.NewServices() checkRunMode() log.Info("%s %s", base.AppName, base.AppVer) @@ -101,9 +114,10 @@ func runWeb(*cli.Context) { m.Get("/help", routers.Help) adminReq := middleware.AdminRequire() - m.Any("/admin", reqSignIn, adminReq, admin.Dashboard) - m.Any("/admin/users", reqSignIn, adminReq, admin.Users) - m.Any("/admin/repos", reqSignIn, adminReq, admin.Repositories) + m.Get("/admin", reqSignIn, adminReq, admin.Dashboard) + m.Get("/admin/users", reqSignIn, adminReq, admin.Users) + m.Get("/admin/repos", reqSignIn, adminReq, admin.Repositories) + m.Get("/admin/config", reqSignIn, adminReq, admin.Config) m.Post("/:username/:reponame/settings", reqSignIn, middleware.RepoAssignment(true), repo.SettingPost) m.Get("/:username/:reponame/settings", reqSignIn, middleware.RepoAssignment(true), repo.Setting) From 5373a3093eaf9bc9ced7a6b3335ccf1b17fd343e Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Fri, 21 Mar 2014 01:59:15 -0400 Subject: [PATCH 27/41] config option: Require sign in to view repository --- conf/app.ini | 2 ++ modules/base/conf.go | 2 ++ modules/middleware/auth.go | 2 +- web.go | 3 ++- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/conf/app.ini b/conf/app.ini index d38cd1f05e..d4fdc0dcf7 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -42,6 +42,8 @@ RESET_PASSWD_CODE_LIVE_MINUTES = 180 REGISTER_EMAIL_CONFIRM = false ; Does not allow register and admin create account only DISENABLE_REGISTERATION = false +; User must sign in to view anything. +REQUIRE_SIGNIN_VIEW = false [mailer] ENABLED = false diff --git a/modules/base/conf.go b/modules/base/conf.go index 42d50da4f3..3050b915fa 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -41,6 +41,7 @@ var ( var Service struct { RegisterEmailConfirm bool DisenableRegisteration bool + RequireSignInView bool ActiveCodeLives int ResetPwdCodeLives int } @@ -70,6 +71,7 @@ func newService() { Service.ActiveCodeLives = Cfg.MustInt("service", "ACTIVE_CODE_LIVE_MINUTES", 180) Service.ResetPwdCodeLives = Cfg.MustInt("service", "RESET_PASSWD_CODE_LIVE_MINUTES", 180) Service.DisenableRegisteration = Cfg.MustBool("service", "DISENABLE_REGISTERATION", false) + Service.RequireSignInView = Cfg.MustBool("service", "REQUIRE_SIGNIN_VIEW", false) } func newLogService() { diff --git a/modules/middleware/auth.go b/modules/middleware/auth.go index 44033abb8f..f211de32b9 100644 --- a/modules/middleware/auth.go +++ b/modules/middleware/auth.go @@ -15,7 +15,7 @@ func SignInRequire(redirect bool) martini.Handler { return func(ctx *Context) { if !ctx.IsSigned { if redirect { - ctx.Redirect("/") + ctx.Redirect("/user/login") } return } else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm { diff --git a/web.go b/web.go index 648cb9d79c..6fe838aa78 100644 --- a/web.go +++ b/web.go @@ -87,7 +87,8 @@ func runWeb(*cli.Context) { m.Use(middleware.InitContext()) - reqSignIn, ignSignIn := middleware.SignInRequire(true), middleware.SignInRequire(false) + reqSignIn := middleware.SignInRequire(true) + ignSignIn := middleware.SignInRequire(base.Service.RequireSignInView) reqSignOut := middleware.SignOutRequire() // Routers. m.Get("/", ignSignIn, routers.Home) From c1576b4c400376f22ec25012a6ca66e9c5539ee4 Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Fri, 21 Mar 2014 03:27:59 -0400 Subject: [PATCH 28/41] Add admin add user --- models/models.go | 28 +++++++-------- models/repo.go | 2 +- modules/base/conf.go | 2 ++ routers/admin/admin.go | 18 ++++++++++ routers/admin/user.go | 63 ++++++++++++++++++++++++++++++++++ templates/admin/config.tmpl | 50 +++++++++++++++++++++++++++ templates/admin/users.tmpl | 1 + templates/admin/users/new.tmpl | 54 +++++++++++++++++++++++++++++ web.go | 1 + 9 files changed, 204 insertions(+), 15 deletions(-) create mode 100644 routers/admin/user.go create mode 100644 templates/admin/users/new.tmpl diff --git a/models/models.go b/models/models.go index bb0015d3de..a4550d7243 100644 --- a/models/models.go +++ b/models/models.go @@ -19,36 +19,36 @@ import ( var ( orm *xorm.Engine - dbCfg struct { + DbCfg struct { Type, Host, Name, User, Pwd, Path, SslMode string } ) func LoadModelsConfig() { - dbCfg.Type = base.Cfg.MustValue("database", "DB_TYPE") - dbCfg.Host = base.Cfg.MustValue("database", "HOST") - dbCfg.Name = base.Cfg.MustValue("database", "NAME") - dbCfg.User = base.Cfg.MustValue("database", "USER") - dbCfg.Pwd = base.Cfg.MustValue("database", "PASSWD") - dbCfg.Path = base.Cfg.MustValue("database", "PATH", "data/gogs.db") - dbCfg.SslMode = base.Cfg.MustValue("database", "SSL_MODE") + DbCfg.Type = base.Cfg.MustValue("database", "DB_TYPE") + DbCfg.Host = base.Cfg.MustValue("database", "HOST") + DbCfg.Name = base.Cfg.MustValue("database", "NAME") + DbCfg.User = base.Cfg.MustValue("database", "USER") + DbCfg.Pwd = base.Cfg.MustValue("database", "PASSWD") + DbCfg.SslMode = base.Cfg.MustValue("database", "SSL_MODE") + DbCfg.Path = base.Cfg.MustValue("database", "PATH", "data/gogs.db") } func setEngine() { var err error - switch dbCfg.Type { + switch DbCfg.Type { case "mysql": orm, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@%s/%s?charset=utf8", - dbCfg.User, dbCfg.Pwd, dbCfg.Host, dbCfg.Name)) + DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name)) case "postgres": orm, err = xorm.NewEngine("postgres", fmt.Sprintf("user=%s password=%s dbname=%s sslmode=%s", - dbCfg.User, dbCfg.Pwd, dbCfg.Name, dbCfg.SslMode)) + DbCfg.User, DbCfg.Pwd, DbCfg.Name, DbCfg.SslMode)) case "sqlite3": - os.MkdirAll(path.Dir(dbCfg.Path), os.ModePerm) - orm, err = xorm.NewEngine("sqlite3", dbCfg.Path) + os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm) + orm, err = xorm.NewEngine("sqlite3", DbCfg.Path) default: - fmt.Printf("Unknown database type: %s\n", dbCfg.Type) + fmt.Printf("Unknown database type: %s\n", DbCfg.Type) os.Exit(2) } if err != nil { diff --git a/models/repo.go b/models/repo.go index f252004785..918e5dc84c 100644 --- a/models/repo.go +++ b/models/repo.go @@ -107,7 +107,7 @@ func IsRepositoryExist(user *User, repoName string) (bool, error) { var ( // Define as all lower case!! - illegalPatterns = []string{"[.][Gg][Ii][Tt]", "user", "help", "stars", "issues", "pulls", "commits", "admin", "repo", "template"} + illegalPatterns = []string{"[.][Gg][Ii][Tt]", "user", "help", "stars", "issues", "pulls", "commits", "admin", "repo", "template", "admin"} ) // IsLegalName returns false if name contains illegal characters. diff --git a/modules/base/conf.go b/modules/base/conf.go index 3050b915fa..bf054ec3c5 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -32,6 +32,7 @@ var ( AppUrl string Domain string SecretKey string + RunUser string RepoRootPath string Cfg *goconfig.ConfigFile @@ -179,6 +180,7 @@ func NewConfigContext() { AppUrl = Cfg.MustValue("server", "ROOT_URL") Domain = Cfg.MustValue("server", "DOMAIN") SecretKey = Cfg.MustValue("security", "SECRET_KEY") + RunUser = Cfg.MustValue("", "RUN_USER") // Determine and create root git reposiroty path. RepoRootPath = Cfg.MustValue("repository", "ROOT") diff --git a/routers/admin/admin.go b/routers/admin/admin.go index 1095a599b9..547883f7bc 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -5,7 +5,12 @@ package admin import ( + "strings" + + "github.com/codegangsta/martini" + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/middleware" ) @@ -45,5 +50,18 @@ func Repositories(ctx *middleware.Context) { func Config(ctx *middleware.Context) { ctx.Data["Title"] = "Server Configuration" ctx.Data["PageIsConfig"] = true + + ctx.Data["AppUrl"] = base.AppUrl + ctx.Data["Domain"] = base.Domain + ctx.Data["RunUser"] = base.RunUser + ctx.Data["RunMode"] = strings.Title(martini.Env) + ctx.Data["RepoRootPath"] = base.RepoRootPath + + ctx.Data["Service"] = base.Service + + ctx.Data["DbCfg"] = models.DbCfg + + ctx.Data["Mailer"] = base.MailService + ctx.HTML(200, "admin/config") } diff --git a/routers/admin/user.go b/routers/admin/user.go new file mode 100644 index 0000000000..9dcc1499e3 --- /dev/null +++ b/routers/admin/user.go @@ -0,0 +1,63 @@ +// 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 admin + +import ( + "strings" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/auth" + "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/middleware" +) + +func NewUser(ctx *middleware.Context, form auth.RegisterForm) { + ctx.Data["Title"] = "New Account" + + if ctx.Req.Method == "GET" { + ctx.HTML(200, "admin/users/new") + return + } + + if form.Password != form.RetypePasswd { + ctx.Data["HasError"] = true + ctx.Data["Err_Password"] = true + ctx.Data["Err_RetypePasswd"] = true + ctx.Data["ErrorMsg"] = "Password and re-type password are not same" + auth.AssignForm(form, ctx.Data) + } + + if ctx.HasError() { + ctx.HTML(200, "admin/users/new") + return + } + + u := &models.User{ + Name: form.UserName, + Email: form.Email, + Passwd: form.Password, + IsActive: true, + } + + var err error + if u, err = models.RegisterUser(u); err != nil { + switch err { + case models.ErrUserAlreadyExist: + ctx.RenderWithErr("Username has been already taken", "admin/users/new", &form) + case models.ErrEmailAlreadyUsed: + ctx.RenderWithErr("E-mail address has been already used", "admin/users/new", &form) + case models.ErrUserNameIllegal: + ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "admin/users/new", &form) + default: + ctx.Handle(200, "admin.user.NewUser", err) + } + return + } + + log.Trace("%s User created by admin(%s): %s", ctx.Req.RequestURI, + ctx.User.LowerName, strings.ToLower(form.UserName)) + + ctx.Redirect("/admin/users") +} diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index d209bcdfd9..9593a545f8 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -9,7 +9,57 @@ </div> <div class="panel-body"> + <div><b>Application Name:</b> {{AppName}}</div> + <div><b>Application Version:</b> {{AppVer}}</div> + <div><b>Application URL:</b> {{.AppUrl}}</div> + <div><b>Domain:</b> {{.Domain}}</div> + <hr/> + <div><b>Run User:</b> {{.RunUser}}</div> + <div><b>Run Mode:</b> {{.RunMode}}</div> + <hr/> + <div><b>Repository Root Path:</b> {{.RepoRootPath}}</div> + </div> + </div> + <div class="panel panel-default"> + <div class="panel-heading"> + Database Configuration + </div> + + <div class="panel-body"> + <div><b>Type:</b> {{.DbCfg.Type}}</div> + <div><b>Host:</b> {{.DbCfg.Host}}</div> + <div><b>Name:</b> {{.DbCfg.Name}}</div> + <div><b>User:</b> {{.DbCfg.User}}</div> + <div><b>SslMode:</b> {{.DbCfg.SslMode}} (for "postgres" only)</div> + <div><b>Path:</b> {{.DbCfg.Path}} (for "sqlite3" only)</div> + </div> + </div> + + <div class="panel panel-default"> + <div class="panel-heading"> + Service Configuration + </div> + + <div class="panel-body"> + <div><b>Register Email Confirmation:</b> <i class="fa fa{{if .Service.RegisterEmailConfirm}}-check{{end}}-square-o"></i></div> + <div><b>Disenable Registeration:</b> <i class="fa fa{{if .Service.DisenableRegisteration}}-check{{end}}-square-o"></i></div> + <div><b>Require Sign In View:</b> <i class="fa fa{{if .Service.RequireSignInView}}-check{{end}}-square-o"></i></div> + <hr/> + <div><b>Active Code Lives:</b> {{.Service.ActiveCodeLives}} minutes</div> + <div><b>Reset Password Code Lives:</b> {{.Service.ResetPwdCodeLives}} minutes</div> + </div> + </div> + + <div class="panel panel-default"> + <div class="panel-heading"> + Mailer Configuration + </div> + + <div class="panel-body"> + <div><b>Name:</b> {{.Mailer.Name}}</div> + <div><b>Host:</b> {{.Mailer.Host}}</div> + <div><b>User:</b> {{.Mailer.User}}</div> </div> </div> </div> diff --git a/templates/admin/users.tmpl b/templates/admin/users.tmpl index ae2b5bbb65..d82f04b812 100644 --- a/templates/admin/users.tmpl +++ b/templates/admin/users.tmpl @@ -9,6 +9,7 @@ </div> <div class="panel-body"> + <a href="/admin/users/new" class="btn btn-primary">New Account</a> <table class="table table-striped"> <thead> <tr> diff --git a/templates/admin/users/new.tmpl b/templates/admin/users/new.tmpl new file mode 100644 index 0000000000..bf59b16a80 --- /dev/null +++ b/templates/admin/users/new.tmpl @@ -0,0 +1,54 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +<div id="gogs-body" class="container" data-page="admin"> + {{template "admin/nav" .}} + <div id="gogs-admin-container" class="col-md-9"> + <div class="panel panel-default"> + <div class="panel-heading"> + New Account + </div> + + <div class="panel-body"> + <br/> + <form action="/admin/users/new" method="post" class="form-horizontal"> + <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> + <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> + <label class="col-md-4 control-label">Username: </label> + <div class="col-md-6"> + <input name="username" class="form-control" placeholder="Type account's username" value="{{.username}}" required="required"> + </div> + </div> + + <div class="form-group {{if .Err_Email}}has-error has-feedback{{end}}"> + <label class="col-md-4 control-label">Email: </label> + <div class="col-md-6"> + <input name="email" class="form-control" placeholder="Type account's e-mail address" value="{{.email}}" required="required" title="Email is not valid"> + </div> + </div> + + <div class="form-group {{if .Err_Password}}has-error has-feedback{{end}}"> + <label class="col-md-4 control-label">Password: </label> + <div class="col-md-6"> + <input name="passwd" type="password" class="form-control" placeholder="Type account's password" required="required" title="Password must contain at least 6 characters"> + </div> + </div> + + <div class="form-group {{if .Err_RetypePasswd}}has-error has-feedback{{end}}"> + <label class="col-md-4 control-label">Re-type: </label> + <div class="col-md-6"> + <input name="retypepasswd" type="password" class="form-control" placeholder="Re-type account's password" required="required" title="Re-type Password must be same to Password"> + </div> + </div> + + <div class="form-group"> + <div class="col-md-offset-4 col-md-6"> + <button type="submit" class="btn btn-lg btn-primary">Create new account</button> + </div> + </div> + </form> + </div> + </div> + + </div> +</div> +{{template "base/footer" .}} \ No newline at end of file diff --git a/web.go b/web.go index 6fe838aa78..d6d78afa5d 100644 --- a/web.go +++ b/web.go @@ -117,6 +117,7 @@ func runWeb(*cli.Context) { adminReq := middleware.AdminRequire() m.Get("/admin", reqSignIn, adminReq, admin.Dashboard) m.Get("/admin/users", reqSignIn, adminReq, admin.Users) + m.Any("/admin/users/new", reqSignIn, adminReq, binding.BindIgnErr(auth.RegisterForm{}), admin.NewUser) m.Get("/admin/repos", reqSignIn, adminReq, admin.Repositories) m.Get("/admin/config", reqSignIn, adminReq, admin.Config) From c2816afe9c60922b3468cece689c84e52e0ba11c Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Fri, 21 Mar 2014 03:36:26 -0400 Subject: [PATCH 29/41] Bug fix --- serve.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/serve.go b/serve.go index 2a17a33490..3ce8f9046c 100644 --- a/serve.go +++ b/serve.go @@ -45,6 +45,10 @@ func In(b string, sl map[string]int) bool { } func runServ(*cli.Context) { + base.NewConfigContext() + models.LoadModelsConfig() + models.NewEngine() + keys := strings.Split(os.Args[2], "-") if len(keys) != 2 { fmt.Println("auth file format error") From e67653cf13857f671de9bf6279453f99cdd60d11 Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Fri, 21 Mar 2014 04:13:32 -0400 Subject: [PATCH 30/41] Bug fix --- CONTRIBUTING.md | 49 ++++++++++++++++++++++++++++++++++ routers/admin/admin.go | 6 ++++- templates/admin/config.tmpl | 1 + templates/admin/dashboard.tmpl | 9 +++++++ templates/repo/nav.tmpl | 6 ++--- 5 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..08013d3705 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,49 @@ +# Contributing to Gogs + +Want to hack on Gogs? Awesome! Here are instructions to get you +started. They are probably not perfect, please let us know if anything +feels wrong or incomplete. + +## Contribution guidelines + +### Pull requests are always welcome + +We are always thrilled to receive pull requests, and do our best to +process them as fast as possible. Not sure if that typo is worth a pull +request? Do it! We will appreciate it. + +If your pull request is not accepted on the first try, don't be +discouraged! If there's a problem with the implementation, hopefully you +received feedback on what to improve. + +We're trying very hard to keep Gogs lean and focused. We don't want it +to do everything for everybody. This means that we might decide against +incorporating a new feature. + +### Discuss your design on the mailing list + +We recommend discussing your plans [on the mailing +list](https://groups.google.com/forum/#!forum/gogits) +before starting to code - especially for more ambitious contributions. +This gives other contributors a chance to point you in the right +direction, give feedback on your design, and maybe point out if someone +else is working on the same thing. + +We may close your pull request if not first discussed on the mailing +list. We aren't doing this to be jerks. We are doing this to prevent +people from spending large amounts of time on changes that may need +to be designed or architected in a specific way, or may not align with +the vision of the project. + +### Create issues... + +Any significant improvement should be documented as [a GitHub +issue](https://github.com/gogits/gogs/issues) before anybody +starts working on it. + +### ...but check for existing issues first! + +Please take a moment to check that an issue doesn't already exist +documenting your bug report or improvement proposal. If it does, it +never hurts to add a quick "+1" or "I have this problem too". This will +help prioritize the most common problems and requests. \ No newline at end of file diff --git a/routers/admin/admin.go b/routers/admin/admin.go index 547883f7bc..6d3831a9f3 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -61,7 +61,11 @@ func Config(ctx *middleware.Context) { ctx.Data["DbCfg"] = models.DbCfg - ctx.Data["Mailer"] = base.MailService + ctx.Data["MailerEnabled"] = false + if base.MailService != nil { + ctx.Data["MailerEnabled"] = true + ctx.Data["Mailer"] = base.MailService + } ctx.HTML(200, "admin/config") } diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 9593a545f8..7013c201ae 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -57,6 +57,7 @@ </div> <div class="panel-body"> + <div><b>Enabled:</b> <i class="fa fa{{if .MailerEnabled}}-check{{end}}-square-o"></i></div> <div><b>Name:</b> {{.Mailer.Name}}</div> <div><b>Host:</b> {{.Mailer.Host}}</div> <div><b>User:</b> {{.Mailer.User}}</div> diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index 8950f50cac..6088487d62 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -12,6 +12,15 @@ Gogs database has <b>{{.Stats.Counter.User}}</b> users, <b>{{.Stats.Counter.PublicKey}}</b> SSH keys, <b>{{.Stats.Counter.Repo}}</b> repositories, <b>{{.Stats.Counter.Watch}}</b> watches, <b>{{.Stats.Counter.Action}}</b> actions, and <b>{{.Stats.Counter.Access}}</b> accesses. </div> </div> + + <div class="panel panel-default"> + <div class="panel-heading"> + System Status + </div> + + <div class="panel-body"> + </div> + </div> </div> </div> {{template "base/footer" .}} \ No newline at end of file diff --git a/templates/repo/nav.tmpl b/templates/repo/nav.tmpl index 718c429a2b..c61051af25 100644 --- a/templates/repo/nav.tmpl +++ b/templates/repo/nav.tmpl @@ -5,8 +5,8 @@ <h3 class="name"><i class="fa fa-book fa-lg"></i><a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> / {{.Repository.Name}}</h3> <p class="desc">{{.Repository.Description}}{{if .Repository.Website}}<a href="{{.Repository.Website}}">{{.Repository.Website}}</a>{{end}}</p> </div> - {{if not .IsBareRepo}} <div class="col-md-6 actions text-right"> + {{if not .IsBareRepo}} <div class="btn-group" id="gogs-repo-clone"> <button type="button" class="btn btn-default"><i class="fa fa-download fa-lg fa-m"></i></button> <button type="button" class="btn btn-default dropdown-toggle" data-container="body" data-toggle="popover" data-placement="bottom" data-content="<label>SSH:</label><div class='input-group'><input type='text' class='form-control' value='{{.CloneLink.SSH}}'></div>" data-html="1"> @@ -38,11 +38,11 @@ <div class="btn-group"> <button type="button" class="btn btn-default"><i class="fa fa-star"></i>Star {{.Repository.NumStars}}</button> </div> + {{end}} <div class="btn-group"> - <button type="button" class="btn btn-default"><i class="fa fa-code-fork"></i>Fork {{.Repository.NumForks}}</button> + <a type="button" {{if not .IsRepositoryOwner}}href="/{{.Username}}/{{.Reponame}}/fork"{{end}} class="btn btn-default"><i class="fa fa-code-fork"></i>Fork {{.Repository.NumForks}}</a> </div> </div> - {{end}} </div> </div> </div> \ No newline at end of file From 30618b271aab40d29a6d34cb4e06c8d28fa74d18 Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Fri, 21 Mar 2014 06:15:58 -0400 Subject: [PATCH 31/41] Add admin edit user --- conf/app.ini | 4 +- conf/gitignore/C++ | 13 ++ conf/license/Artistic License 2.0 | 201 ++++++++++++++++++++++++++++++ modules/auth/admin.go | 55 ++++++++ modules/auth/user.go | 2 +- routers/admin/user.go | 44 +++++++ routers/user/setting.go | 1 + templates/admin/users.tmpl | 2 + templates/admin/users/edit.tmpl | 83 ++++++++++++ templates/admin/users/new.tmpl | 20 +-- templates/user/setting.tmpl | 6 +- web.go | 1 + 12 files changed, 416 insertions(+), 16 deletions(-) create mode 100644 conf/gitignore/C++ create mode 100644 conf/license/Artistic License 2.0 create mode 100644 modules/auth/admin.go create mode 100644 templates/admin/users/edit.tmpl diff --git a/conf/app.ini b/conf/app.ini index d4fdc0dcf7..985903a8e5 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -8,8 +8,8 @@ RUN_MODE = dev [repository] ROOT = /Users/%(RUN_USER)s/git/gogs-repositories -LANG_IGNS = Google Go|C|Python|Ruby|C Sharp -LICENSES = Apache v2 License|GPL v2|MIT License|Affero GPL|BSD (3-Clause) License +LANG_IGNS = Google Go|C|C++|Python|Ruby|C Sharp +LICENSES = Apache v2 License|GPL v2|MIT License|Affero GPL|Artistic License 2.0|BSD (3-Clause) License [server] DOMAIN = localhost diff --git a/conf/gitignore/C++ b/conf/gitignore/C++ new file mode 100644 index 0000000000..5a1b6ec431 --- /dev/null +++ b/conf/gitignore/C++ @@ -0,0 +1,13 @@ +# Compiled Object files +*.slo +*.lo +*.o + +# Compiled Dynamic libraries +*.so +*.dylib + +# Compiled Static libraries +*.lai +*.la +*.a \ No newline at end of file diff --git a/conf/license/Artistic License 2.0 b/conf/license/Artistic License 2.0 new file mode 100644 index 0000000000..b7c38097e6 --- /dev/null +++ b/conf/license/Artistic License 2.0 @@ -0,0 +1,201 @@ +The Artistic License 2.0 + + Copyright (c) 2014 + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble + +This license establishes the terms under which a given free software +Package may be copied, modified, distributed, and/or redistributed. +The intent is that the Copyright Holder maintains some artistic +control over the development of that Package while still keeping the +Package available as open source and free software. + +You are always permitted to make arrangements wholly outside of this +license directly with the Copyright Holder of a given Package. If the +terms of this license do not permit the full use that you propose to +make of the Package, you should contact the Copyright Holder and seek +a different licensing arrangement. + +Definitions + + "Copyright Holder" means the individual(s) or organization(s) + named in the copyright notice for the entire Package. + + "Contributor" means any party that has contributed code or other + material to the Package, in accordance with the Copyright Holder's + procedures. + + "You" and "your" means any person who would like to copy, + distribute, or modify the Package. + + "Package" means the collection of files distributed by the + Copyright Holder, and derivatives of that collection and/or of + those files. A given Package may consist of either the Standard + Version, or a Modified Version. + + "Distribute" means providing a copy of the Package or making it + accessible to anyone else, or in the case of a company or + organization, to others outside of your company or organization. + + "Distributor Fee" means any fee that you charge for Distributing + this Package or providing support for this Package to another + party. It does not mean licensing fees. + + "Standard Version" refers to the Package if it has not been + modified, or has been modified only in ways explicitly requested + by the Copyright Holder. + + "Modified Version" means the Package, if it has been changed, and + such changes were not explicitly requested by the Copyright + Holder. + + "Original License" means this Artistic License as Distributed with + the Standard Version of the Package, in its current version or as + it may be modified by The Perl Foundation in the future. + + "Source" form means the source code, documentation source, and + configuration files for the Package. + + "Compiled" form means the compiled bytecode, object code, binary, + or any other form resulting from mechanical transformation or + translation of the Source form. + + +Permission for Use and Modification Without Distribution + +(1) You are permitted to use the Standard Version and create and use +Modified Versions for any purpose without restriction, provided that +you do not Distribute the Modified Version. + + +Permissions for Redistribution of the Standard Version + +(2) You may Distribute verbatim copies of the Source form of the +Standard Version of this Package in any medium without restriction, +either gratis or for a Distributor Fee, provided that you duplicate +all of the original copyright notices and associated disclaimers. At +your discretion, such verbatim copies may or may not include a +Compiled form of the Package. + +(3) You may apply any bug fixes, portability changes, and other +modifications made available from the Copyright Holder. The resulting +Package will still be considered the Standard Version, and as such +will be subject to the Original License. + + +Distribution of Modified Versions of the Package as Source + +(4) You may Distribute your Modified Version as Source (either gratis +or for a Distributor Fee, and with or without a Compiled form of the +Modified Version) provided that you clearly document how it differs +from the Standard Version, including, but not limited to, documenting +any non-standard features, executables, or modules, and provided that +you do at least ONE of the following: + + (a) make the Modified Version available to the Copyright Holder + of the Standard Version, under the Original License, so that the + Copyright Holder may include your modifications in the Standard + Version. + + (b) ensure that installation of your Modified Version does not + prevent the user installing or running the Standard Version. In + addition, the Modified Version must bear a name that is different + from the name of the Standard Version. + + (c) allow anyone who receives a copy of the Modified Version to + make the Source form of the Modified Version available to others + under + + (i) the Original License or + + (ii) a license that permits the licensee to freely copy, + modify and redistribute the Modified Version using the same + licensing terms that apply to the copy that the licensee + received, and requires that the Source form of the Modified + Version, and of any works derived from it, be made freely + available in that license fees are prohibited but Distributor + Fees are allowed. + + +Distribution of Compiled Forms of the Standard Version +or Modified Versions without the Source + +(5) You may Distribute Compiled forms of the Standard Version without +the Source, provided that you include complete instructions on how to +get the Source of the Standard Version. Such instructions must be +valid at the time of your distribution. If these instructions, at any +time while you are carrying out such distribution, become invalid, you +must provide new instructions on demand or cease further distribution. +If you provide valid instructions or cease distribution within thirty +days after you become aware that the instructions are invalid, then +you do not forfeit any of your rights under this license. + +(6) You may Distribute a Modified Version in Compiled form without +the Source, provided that you comply with Section 4 with respect to +the Source of the Modified Version. + + +Aggregating or Linking the Package + +(7) You may aggregate the Package (either the Standard Version or +Modified Version) with other packages and Distribute the resulting +aggregation provided that you do not charge a licensing fee for the +Package. Distributor Fees are permitted, and licensing fees for other +components in the aggregation are permitted. The terms of this license +apply to the use and Distribution of the Standard or Modified Versions +as included in the aggregation. + +(8) You are permitted to link Modified and Standard Versions with +other works, to embed the Package in a larger work of your own, or to +build stand-alone binary or bytecode versions of applications that +include the Package, and Distribute the result without restriction, +provided the result does not expose a direct interface to the Package. + + +Items That are Not Considered Part of a Modified Version + +(9) Works (including, but not limited to, modules and scripts) that +merely extend or make use of the Package, do not, by themselves, cause +the Package to be a Modified Version. In addition, such works are not +considered parts of the Package itself, and are not subject to the +terms of this license. + + +General Provisions + +(10) Any use, modification, and distribution of the Standard or +Modified Versions is governed by this Artistic License. By using, +modifying or distributing the Package, you accept this license. Do not +use, modify, or distribute the Package, if you do not accept this +license. + +(11) If your Modified Version has been derived from a Modified +Version made by someone other than you, you are nevertheless required +to ensure that your Modified Version complies with the requirements of +this license. + +(12) This license does not grant you the right to use any trademark, +service mark, tradename, or logo of the Copyright Holder. + +(13) This license includes the non-exclusive, worldwide, +free-of-charge patent license to make, have made, use, offer to sell, +sell, import and otherwise transfer the Package with respect to any +patent claims licensable by the Copyright Holder that are necessarily +infringed by the Package. If you institute patent litigation +(including a cross-claim or counterclaim) against any party alleging +that the Package constitutes direct or contributory patent +infringement, then this Artistic License to you shall terminate on the +date that such litigation is filed. + +(14) Disclaimer of Warranty: +THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS +IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL +LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/modules/auth/admin.go b/modules/auth/admin.go new file mode 100644 index 0000000000..eccab00718 --- /dev/null +++ b/modules/auth/admin.go @@ -0,0 +1,55 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package auth + +import ( + "net/http" + "reflect" + + "github.com/codegangsta/martini" + + "github.com/gogits/binding" + + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/log" +) + +type AdminEditUserForm struct { + Email string `form:"email" binding:"Required;Email;MaxSize(50)"` + Website string `form:"website" binding:"MaxSize(50)"` + Location string `form:"location" binding:"MaxSize(50)"` + Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"` + Active string `form:"active"` + Admin string `form:"admin"` +} + +func (f *AdminEditUserForm) Name(field string) string { + names := map[string]string{ + "Email": "E-mail address", + "Website": "Website", + "Location": "Location", + "Avatar": "Gravatar Email", + } + return names[field] +} + +func (f *AdminEditUserForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { + if req.Method == "GET" || errors.Count() == 0 { + return + } + + data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) + data["HasError"] = true + AssignForm(f, data) + + if len(errors.Overall) > 0 { + for _, err := range errors.Overall { + log.Error("AdminEditUserForm.Validate: %v", err) + } + return + } + + validate(errors, data, f) +} diff --git a/modules/auth/user.go b/modules/auth/user.go index 491ec65aca..f8d8f66149 100644 --- a/modules/auth/user.go +++ b/modules/auth/user.go @@ -79,7 +79,7 @@ type UpdateProfileForm struct { func (f *UpdateProfileForm) Name(field string) string { names := map[string]string{ - "Email": "Email address", + "Email": "E-mail address", "Website": "Website", "Location": "Location", "Avatar": "Gravatar Email", diff --git a/routers/admin/user.go b/routers/admin/user.go index 9dcc1499e3..47eec5c914 100644 --- a/routers/admin/user.go +++ b/routers/admin/user.go @@ -7,8 +7,11 @@ package admin import ( "strings" + "github.com/codegangsta/martini" + "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/middleware" ) @@ -61,3 +64,44 @@ func NewUser(ctx *middleware.Context, form auth.RegisterForm) { ctx.Redirect("/admin/users") } + +func EditUser(ctx *middleware.Context, params martini.Params, form auth.AdminEditUserForm) { + ctx.Data["Title"] = "Edit Account" + + uid, err := base.StrTo(params["userid"]).Int() + if err != nil { + ctx.Handle(200, "admin.user.EditUser", err) + return + } + + u, err := models.GetUserById(int64(uid)) + if err != nil { + ctx.Handle(200, "admin.user.EditUser", err) + return + } + + if ctx.Req.Method == "GET" { + ctx.Data["User"] = u + ctx.HTML(200, "admin/users/edit") + return + } + + u.Email = form.Email + u.Website = form.Website + u.Location = form.Location + u.Avatar = base.EncodeMd5(form.Avatar) + u.AvatarEmail = form.Avatar + u.IsActive = form.Active == "on" + u.IsAdmin = form.Admin == "on" + if err := models.UpdateUser(u); err != nil { + ctx.Handle(200, "admin.user.EditUser", err) + return + } + + ctx.Data["IsSuccess"] = true + ctx.Data["User"] = u + ctx.HTML(200, "admin/users/edit") + + log.Trace("%s User profile updated by admin(%s): %s", ctx.Req.RequestURI, + ctx.User.LowerName, ctx.User.LowerName) +} diff --git a/routers/user/setting.go b/routers/user/setting.go index f0c7a8a5b1..75adf2b81d 100644 --- a/routers/user/setting.go +++ b/routers/user/setting.go @@ -46,6 +46,7 @@ func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) { ctx.Data["IsSuccess"] = true ctx.HTML(200, "user/setting") + log.Trace("%s User setting updated: %s", ctx.Req.RequestURI, ctx.User.LowerName) } diff --git a/templates/admin/users.tmpl b/templates/admin/users.tmpl index d82f04b812..dec5c18954 100644 --- a/templates/admin/users.tmpl +++ b/templates/admin/users.tmpl @@ -20,6 +20,7 @@ <th>Admin</th> <th>Repos</th> <th>Join</th> + <th>Edit</th> </tr> </thead> <tbody> @@ -32,6 +33,7 @@ <td><i class="fa fa{{if .IsAdmin}}-check{{end}}-square-o"></i></td> <td>{{.NumRepos}}</td> <td>{{DateFormat .Created "M d, Y"}}</td> + <td><a href="/admin/users/{{.Id}}"><i class="fa fa-pencil-square-o"></i></a></td> </tr> {{end}} </tbody> diff --git a/templates/admin/users/edit.tmpl b/templates/admin/users/edit.tmpl new file mode 100644 index 0000000000..f7782fe07a --- /dev/null +++ b/templates/admin/users/edit.tmpl @@ -0,0 +1,83 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +<div id="gogs-body" class="container" data-page="admin"> + {{template "admin/nav" .}} + <div id="gogs-admin-container" class="col-md-9"> + <div class="panel panel-default"> + <div class="panel-heading"> + Edit Account + </div> + + <div class="panel-body"> + <br/> + <form action="/admin/users/{{.User.Id}}" method="post" class="form-horizontal"> + {{if .IsSuccess}}<p class="alert alert-success">Your profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} + <input type="hidden" value="{{.User.Id}}" name="userId"/> + <div class="form-group"> + <label class="col-md-3 control-label">Username: </label> + <label class="control-label">{{.User.Name}}</label> + </div> + + <div class="form-group {{if .Err_Email}}has-error has-feedback{{end}}"> + <label class="col-md-3 control-label">Email<strong class="text-danger">*</strong></label> + <div class="col-md-7"> + <input name="email" class="form-control" placeholder="Type account's e-mail address" value="{{.User.Email}}" required="required"> + </div> + </div> + + <div class="form-group"> + <label class="col-md-3 control-label">Website</label> + <div class="col-md-7"> + <input name="website" class="form-control" placeholder="Type account's website URL" value="{{.User.Website}}"> + </div> + </div> + + <div class="form-group"> + <label class="col-md-3 control-label">Location</label> + <div class="col-md-7"> + <input name="location" class="form-control" placeholder="Type account's current location" value="{{.User.Location}}"> + </div> + </div> + + <div class="form-group {{if .Err_Avatar}}has-error has-feedback{{end}}"> + <label class="col-md-3 control-label">Gravatar Email<strong class="text-danger">*</strong></label> + <div class="col-md-7"> + <input name="avatar" class="form-control" placeholder="Type account's Gravatar e-mail address" required="required" value="{{.User.AvatarEmail}}"> + </div> + </div> + + <div class="form-group"> + <div class="col-md-7 col-md-offset-3"> + <div class="checkbox"> + <label> + <input type="checkbox" name="active" {{if .User.IsActive}}checked{{end}}> + <strong>This account has activated</strong> + </label> + </div> + </div> + </div> + + <div class="form-group"> + <div class="col-md-7 col-md-offset-3"> + <div class="checkbox"> + <label> + <input type="checkbox" name="admin" {{if .User.IsAdmin}}checked{{end}}> + <strong>This account has administor permisson</strong> + </label> + </div> + </div> + </div> + <hr/> + <div class="form-group"> + <div class="col-md-offset-3 col-md-6"> + <button type="submit" class="btn btn-lg btn-primary btn-block">Update account profile</button> + <!-- <a type="button" href="/admin/users/{{.User.Id}}/delete" class="btn btn-lg btn-danger btn-block">Delete this account</a> --> + </div> + </div> + </form> + </div> + </div> + + </div> +</div> +{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/admin/users/new.tmpl b/templates/admin/users/new.tmpl index bf59b16a80..01d976caa0 100644 --- a/templates/admin/users/new.tmpl +++ b/templates/admin/users/new.tmpl @@ -13,35 +13,35 @@ <form action="/admin/users/new" method="post" class="form-horizontal"> <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> - <label class="col-md-4 control-label">Username: </label> - <div class="col-md-6"> + <label class="col-md-3 control-label">Username: </label> + <div class="col-md-7"> <input name="username" class="form-control" placeholder="Type account's username" value="{{.username}}" required="required"> </div> </div> <div class="form-group {{if .Err_Email}}has-error has-feedback{{end}}"> - <label class="col-md-4 control-label">Email: </label> - <div class="col-md-6"> + <label class="col-md-3 control-label">Email: </label> + <div class="col-md-7"> <input name="email" class="form-control" placeholder="Type account's e-mail address" value="{{.email}}" required="required" title="Email is not valid"> </div> </div> <div class="form-group {{if .Err_Password}}has-error has-feedback{{end}}"> - <label class="col-md-4 control-label">Password: </label> - <div class="col-md-6"> + <label class="col-md-3 control-label">Password: </label> + <div class="col-md-7"> <input name="passwd" type="password" class="form-control" placeholder="Type account's password" required="required" title="Password must contain at least 6 characters"> </div> </div> <div class="form-group {{if .Err_RetypePasswd}}has-error has-feedback{{end}}"> - <label class="col-md-4 control-label">Re-type: </label> - <div class="col-md-6"> + <label class="col-md-3 control-label">Re-type: </label> + <div class="col-md-7"> <input name="retypepasswd" type="password" class="form-control" placeholder="Re-type account's password" required="required" title="Re-type Password must be same to Password"> </div> </div> - + <hr/> <div class="form-group"> - <div class="col-md-offset-4 col-md-6"> + <div class="col-md-offset-3 col-md-7"> <button type="submit" class="btn btn-lg btn-primary">Create new account</button> </div> </div> diff --git a/templates/user/setting.tmpl b/templates/user/setting.tmpl index c38054f39b..222ddd895b 100644 --- a/templates/user/setting.tmpl +++ b/templates/user/setting.tmpl @@ -5,8 +5,8 @@ <div id="gogs-user-setting-container" class="col-md-9"> <div id="gogs-setting-pwd"> <h4>Account Profile</h4> - <form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting">{{if .IsSuccess}} - <p class="alert alert-success">Your profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} + <form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting"> + {{if .IsSuccess}}<p class="alert alert-success">Your profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} <p>Your Email will be public and used for Account related notifications and any web based operations made via the web.</p> <div class="form-group"> <label class="col-md-2 control-label">Email</label> @@ -29,7 +29,7 @@ </div> </div> - <div class="form-group"> + <div class="form-group {{if .Err_Avatar}}has-error has-feedback{{end}}"> <label class="col-md-2 control-label">Gravatar Email<strong class="text-danger">*</strong></label> <div class="col-md-8"> <input type="text" name="avatar" class="form-control" placeholder="Type your Gravatar e-mail address" required="required" value="{{.Owner.AvatarEmail}}"> diff --git a/web.go b/web.go index d6d78afa5d..54aaab573b 100644 --- a/web.go +++ b/web.go @@ -118,6 +118,7 @@ func runWeb(*cli.Context) { m.Get("/admin", reqSignIn, adminReq, admin.Dashboard) m.Get("/admin/users", reqSignIn, adminReq, admin.Users) m.Any("/admin/users/new", reqSignIn, adminReq, binding.BindIgnErr(auth.RegisterForm{}), admin.NewUser) + m.Any("/admin/users/:userid", reqSignIn, adminReq, binding.BindIgnErr(auth.AdminEditUserForm{}), admin.EditUser) m.Get("/admin/repos", reqSignIn, adminReq, admin.Repositories) m.Get("/admin/config", reqSignIn, adminReq, admin.Config) From e4053f5e982f627d983a08bf3fe6bb2b20b904b5 Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Fri, 21 Mar 2014 06:47:24 -0400 Subject: [PATCH 32/41] Mirror update --- templates/admin/users/edit.tmpl | 2 +- templates/base/footer.tmpl | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/templates/admin/users/edit.tmpl b/templates/admin/users/edit.tmpl index f7782fe07a..415bcedc92 100644 --- a/templates/admin/users/edit.tmpl +++ b/templates/admin/users/edit.tmpl @@ -11,7 +11,7 @@ <div class="panel-body"> <br/> <form action="/admin/users/{{.User.Id}}" method="post" class="form-horizontal"> - {{if .IsSuccess}}<p class="alert alert-success">Your profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} + {{if .IsSuccess}}<p class="alert alert-success">Account profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} <input type="hidden" value="{{.User.Id}}" name="userId"/> <div class="form-group"> <label class="col-md-3 control-label">Username: </label> diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl index fe0ea93027..5ae5c96d68 100644 --- a/templates/base/footer.tmpl +++ b/templates/base/footer.tmpl @@ -2,12 +2,19 @@ </div> <footer id="footer"> <div class="container footer-wrap"> - <p>© 2014 Gogs · ver {{AppVer}} · - All: {{LoadTimes .PageStartTime}} · - Tmpl: {{call .TmplLoadTimes}} · - <i class="fa fa-github"></i><a target="_blank" href="https://github.com/gogits/gogs">GitHub</a> · - </p> - <p class="desc"></p> + <div class="row"> + <div class="col-md-6"> + <p>© 2014 Gogs · Version: {{AppVer}} · + Page: <b>{{LoadTimes .PageStartTime}}</b> · + Template: <b>{{call .TmplLoadTimes}}</b> + </p> + </div> + + <div class="col-md-1" style="margin: -5px;"> + <a target="_blank" href="https://github.com/gogits/gogs"><i class="fa fa-github fa-2x"></i></a> + </div> + <p class="desc"></p> + </div> </div> </footer> </body> From ecc61ced413a60fcfdc432b921dc2c1b3cab0366 Mon Sep 17 00:00:00 2001 From: FuXiaoHei <fuxiaohei@hexiaz.com> Date: Fri, 21 Mar 2014 21:06:05 +0800 Subject: [PATCH 33/41] update admin nav ui --- public/css/gogs.css | 21 +++++++++++++++++++++ routers/admin/user.go | 2 ++ templates/admin/nav.tmpl | 2 +- templates/base/footer.tmpl | 2 +- 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/public/css/gogs.css b/public/css/gogs.css index b799882352..aec8f1b328 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -324,6 +324,27 @@ html, body { color: #444; } +.gogs-admin-nav{ + background-color: #FFF; + padding-top: 10px; + padding-left: 0; + padding-right: 0; + border:1px solid #D8D8D8; +} + +.gogs-admin-nav li{ + margin-bottom: 8px; + border-left: 4px solid transparent; +} + +.gogs-admin-nav li:hover{ + border-left-color: #EEE; +} + +.gogs-admin-nav li.active:hover{ + border-left: 4px solid #DD4B39; +} + /* gogits user ssh keys */ #gogs-ssh-keys .list-group-item { diff --git a/routers/admin/user.go b/routers/admin/user.go index 47eec5c914..d6f8523218 100644 --- a/routers/admin/user.go +++ b/routers/admin/user.go @@ -18,6 +18,7 @@ import ( func NewUser(ctx *middleware.Context, form auth.RegisterForm) { ctx.Data["Title"] = "New Account" + ctx.Data["PageIsUsers"] = true if ctx.Req.Method == "GET" { ctx.HTML(200, "admin/users/new") @@ -67,6 +68,7 @@ func NewUser(ctx *middleware.Context, form auth.RegisterForm) { func EditUser(ctx *middleware.Context, params martini.Params, form auth.AdminEditUserForm) { ctx.Data["Title"] = "Edit Account" + ctx.Data["PageIsUsers"] = true uid, err := base.StrTo(params["userid"]).Int() if err != nil { diff --git a/templates/admin/nav.tmpl b/templates/admin/nav.tmpl index bb704ee3fb..72f008acc7 100644 --- a/templates/admin/nav.tmpl +++ b/templates/admin/nav.tmpl @@ -1,4 +1,4 @@ -<div id="gogs-user-setting-nav" class="col-md-3"> +<div id="gogs-user-setting-nav" class="col-md-3 gogs-admin-nav"> <ul class="list-group" data-init="tabs"> <li class="list-group-item{{if .PageIsDashboard}} active{{end}}"><a href="/admin"><i class="fa fa-tachometer fa-lg"></i> Dashboard</a></li> <li class="list-group-item{{if .PageIsUsers}} active{{end}}"><a href="/admin/users"><i class="fa fa-users fa-lg"></i> Users</a></li> diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl index 5ae5c96d68..6c2da63e5d 100644 --- a/templates/base/footer.tmpl +++ b/templates/base/footer.tmpl @@ -4,7 +4,7 @@ <div class="container footer-wrap"> <div class="row"> <div class="col-md-6"> - <p>© 2014 Gogs · Version: {{AppVer}} · + <p>© 2014 GoGits · Version: {{AppVer}} · Page: <b>{{LoadTimes .PageStartTime}}</b> · Template: <b>{{call .TmplLoadTimes}}</b> </p> From c7e0d3a499941a4cbd7814e30308c8f3c6e45543 Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Fri, 21 Mar 2014 21:06:47 +0800 Subject: [PATCH 34/41] add cache --- conf/app.ini | 4 ++++ modules/base/conf.go | 16 ++++++++++++++++ modules/middleware/context.go | 3 +++ 3 files changed, 23 insertions(+) diff --git a/conf/app.ini b/conf/app.ini index 985903a8e5..71fe81e834 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -60,6 +60,10 @@ FROM = USER = PASSWD = +[cache] +ADAPTER = memory +CONFIG = + [log] ; Either "console", "file", "conn" or "smtp", default is "console" MODE = console diff --git a/modules/base/conf.go b/modules/base/conf.go index bf054ec3c5..3962972cda 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -15,6 +15,8 @@ import ( "github.com/Unknwon/com" "github.com/Unknwon/goconfig" + "github.com/gogits/cache" + "github.com/gogits/gogs/modules/log" ) @@ -37,6 +39,10 @@ var ( Cfg *goconfig.ConfigFile MailService *Mailer + + Cache cache.Cache + CacheAdapter string + CacheConfig string ) var Service struct { @@ -182,6 +188,16 @@ func NewConfigContext() { SecretKey = Cfg.MustValue("security", "SECRET_KEY") RunUser = Cfg.MustValue("", "RUN_USER") + CacheAdapter = Cfg.MustValue("cache", "ADAPTER") + CacheConfig = Cfg.MustValue("cache", "CONFIG") + + Cache, err = cache.NewCache(CacheAdapter, CacheConfig) + if err != nil { + fmt.Printf("Init cache system failed, adapter: %s, config: %s, %v\n", + CacheAdapter, CacheConfig, err) + os.Exit(2) + } + // Determine and create root git reposiroty path. RepoRootPath = Cfg.MustValue("repository", "ROOT") if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { diff --git a/modules/middleware/context.go b/modules/middleware/context.go index cb3cbabca6..da051918b1 100644 --- a/modules/middleware/context.go +++ b/modules/middleware/context.go @@ -12,6 +12,8 @@ import ( "github.com/codegangsta/martini" "github.com/martini-contrib/sessions" + "github.com/gogits/cache" + "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/log" @@ -25,6 +27,7 @@ type Context struct { Req *http.Request Res http.ResponseWriter Session sessions.Session + Cache cache.Cache User *models.User IsSigned bool From 14ddb8b1fa9ccdd877f808112e38d2f0146fe3c2 Mon Sep 17 00:00:00 2001 From: FuXiaoHei <fuxiaohei@hexiaz.com> Date: Fri, 21 Mar 2014 21:16:13 +0800 Subject: [PATCH 35/41] fix dashboard repo link --- public/css/gogs.css | 3 +-- templates/user/dashboard.tmpl | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/css/gogs.css b/public/css/gogs.css index aec8f1b328..9f16196616 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -397,7 +397,7 @@ html, body { } #gogs-feed-right .repo-panel .list-group-item a { - display: inline-block; + display: block; margin-left: 0; background-color: transparent; padding-left: 0; @@ -427,7 +427,6 @@ html, body { #gogs-feed-right .repo-panel span.stars { color: #666; - line-height: 44px; margin-right: 1em; } diff --git a/templates/user/dashboard.tmpl b/templates/user/dashboard.tmpl index c528185c01..72c31d3d8f 100644 --- a/templates/user/dashboard.tmpl +++ b/templates/user/dashboard.tmpl @@ -33,8 +33,9 @@ </div> <div class="panel-body"> <ul class="list-group">{{range .MyRepos}} - <li class="list-group-item"><i class="fa fa-book"></i><a href="/{{$.SignedUserName}}/{{.Name}}">{{.Name}}</a> + <li class="list-group-item"><a href="/{{$.SignedUserName}}/{{.Name}}"> <span class="stars pull-right"><i class="fa fa-star"></i>{{.NumStars}}</span> + <i class="fa fa-book"></i>{{.Name}}</a> </li>{{end}} </ul> </div> From 770c78a177c64ba2014f4152da95f0899495772a Mon Sep 17 00:00:00 2001 From: slene <vslene@gmail.com> Date: Fri, 21 Mar 2014 21:31:47 +0800 Subject: [PATCH 36/41] fix --- modules/middleware/context.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/middleware/context.go b/modules/middleware/context.go index da051918b1..a25a3dbbeb 100644 --- a/modules/middleware/context.go +++ b/modules/middleware/context.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/log" ) @@ -100,6 +101,7 @@ func InitContext() martini.Handler { Req: r, Res: res, Session: session, + Cache: base.Cache, Render: rd, } From 85abe5132e1c67a247839bd819b67311f97a7563 Mon Sep 17 00:00:00 2001 From: FuXiaoHei <fuxiaohei@hexiaz.com> Date: Fri, 21 Mar 2014 21:36:15 +0800 Subject: [PATCH 37/41] fix navbar button ui --- templates/base/navbar.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/base/navbar.tmpl b/templates/base/navbar.tmpl index d775191a5a..2c18b038a3 100644 --- a/templates/base/navbar.tmpl +++ b/templates/base/navbar.tmpl @@ -11,7 +11,7 @@ <a class="navbar-right gogs-nav-item{{if .PageIsNewRepo}} active{{end}}" href="/repo/create" data-toggle="tooltip" data-placement="bottom" title="New Repository"><i class="fa fa-plus fa-lg"></i></a> <a class="navbar-right gogs-nav-item{{if .PageIsUserSetting}} active{{end}}" href="/user/setting" data-toggle="tooltip" data-placement="bottom" title="Setting"><i class="fa fa-cogs fa-lg"></i></a> {{if .IsAdmin}}<a class="navbar-right gogs-nav-item{{if .PageIsAdmin}} active{{end}}" href="/admin" data-toggle="tooltip" data-placement="bottom" title="Admin"><i class="fa fa-gear fa-lg"></i></a>{{end}} - {{else}}<a id="gogs-nav-signin" class="gogs-nav-item navbar-right navbar-btn btn btn-success" href="/user/login/">Sign In</a> + {{else}}<a id="gogs-nav-signin" class="gogs-nav-item navbar-right navbar-btn btn btn-danger" href="/user/login/">Sign In</a> <a id="gogs-nav-signup" class="gogs-nav-item navbar-right navbar-btn btn btn-info" href="/user/sign_up/">Sign Up</a>{{end}} </nav> </div> From 67c4e5429818b501b0db5527ea38f2be75e5df55 Mon Sep 17 00:00:00 2001 From: FuXiaoHei <fuxiaohei@hexiaz.com> Date: Fri, 21 Mar 2014 21:48:13 +0800 Subject: [PATCH 38/41] fix navbar button ui --- public/css/gogs.css | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/public/css/gogs.css b/public/css/gogs.css index 9f16196616..36d57536c6 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -145,8 +145,9 @@ html, body { float: right; } -#gogs-nav-signin { +#gogs-nav-signin, #gogs-nav-signup { float: right; + margin-left: 1em; } #gogs-nav-out .fa { @@ -324,24 +325,24 @@ html, body { color: #444; } -.gogs-admin-nav{ +.gogs-admin-nav { background-color: #FFF; padding-top: 10px; padding-left: 0; padding-right: 0; - border:1px solid #D8D8D8; + border: 1px solid #D8D8D8; } -.gogs-admin-nav li{ +.gogs-admin-nav li { margin-bottom: 8px; border-left: 4px solid transparent; } -.gogs-admin-nav li:hover{ +.gogs-admin-nav li:hover { border-left-color: #EEE; } -.gogs-admin-nav li.active:hover{ +.gogs-admin-nav li.active:hover { border-left: 4px solid #DD4B39; } From d40499e7fa3d62655431f160b6909d9751dabe11 Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Fri, 21 Mar 2014 10:09:57 -0400 Subject: [PATCH 39/41] Finsih mail resend limit --- README.md | 2 +- conf/app.ini | 8 +++++++- gogs.go | 2 +- models/models.go | 1 - modules/base/conf.go | 35 +++++++++++++++++++++++++---------- routers/admin/admin.go | 3 +++ routers/user/user.go | 12 ++++++++++-- templates/admin/config.tmpl | 11 +++++++++++ templates/user/active.tmpl | 2 ++ 9 files changed, 60 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index e78ce8fe99..cbd1f588df 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Gogs(Go Git Service) is a GitHub-like clone in the Go Programming Language. Since we choose to use pure Go implementation of Git manipulation, Gogs certainly supports **ALL platforms** that Go supports, including Linux, Max OS X, and Windows with **ZERO** dependency. -##### Current version: 0.1.4 Alpha +##### Current version: 0.1.5 Alpha ## Purpose diff --git a/conf/app.ini b/conf/app.ini index 71fe81e834..ecb0d2511f 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -61,8 +61,14 @@ USER = PASSWD = [cache] +; Either "memory", "redis", or "memcache", default is "memory" ADAPTER = memory -CONFIG = +; For "memory" only, GC interval in seconds, default is 60 +INTERVAL = 60 +; For "redis" and "memcache", connection host address +; redis: ":6039" +; memcache: "127.0.0.1:11211" +HOST = [log] ; Either "console", "file", "conn" or "smtp", default is "console" diff --git a/gogs.go b/gogs.go index a9f7e6b5c4..41df79280a 100644 --- a/gogs.go +++ b/gogs.go @@ -20,7 +20,7 @@ import ( // Test that go1.2 tag above is included in builds. main.go refers to this definition. const go12tag = true -const APP_VER = "0.1.4.0321" +const APP_VER = "0.1.5.0321" func init() { base.AppVer = APP_VER diff --git a/models/models.go b/models/models.go index a4550d7243..8713ff2896 100644 --- a/models/models.go +++ b/models/models.go @@ -35,7 +35,6 @@ func LoadModelsConfig() { } func setEngine() { - var err error switch DbCfg.Type { case "mysql": diff --git a/modules/base/conf.go b/modules/base/conf.go index 3962972cda..2c3ecd72d8 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -133,6 +133,30 @@ func newLogService() { log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName) } +func newCacheService() { + CacheAdapter = Cfg.MustValue("cache", "ADAPTER", "memory") + + switch CacheAdapter { + case "memory": + CacheConfig = fmt.Sprintf(`{"interval":%d}`, Cfg.MustInt("cache", "INTERVAL", 60)) + case "redis", "memcache": + CacheConfig = fmt.Sprintf(`{"conn":"%s"}`, Cfg.MustValue("cache", "HOST")) + default: + fmt.Printf("Unknown cache adapter: %s\n", CacheAdapter) + os.Exit(2) + } + + var err error + Cache, err = cache.NewCache(CacheAdapter, CacheConfig) + if err != nil { + fmt.Printf("Init cache system failed, adapter: %s, config: %s, %v\n", + CacheAdapter, CacheConfig, err) + os.Exit(2) + } + + log.Info("Cache Service Enabled") +} + func newMailService() { // Check mailer setting. if Cfg.MustBool("mailer", "ENABLED") { @@ -188,16 +212,6 @@ func NewConfigContext() { SecretKey = Cfg.MustValue("security", "SECRET_KEY") RunUser = Cfg.MustValue("", "RUN_USER") - CacheAdapter = Cfg.MustValue("cache", "ADAPTER") - CacheConfig = Cfg.MustValue("cache", "CONFIG") - - Cache, err = cache.NewCache(CacheAdapter, CacheConfig) - if err != nil { - fmt.Printf("Init cache system failed, adapter: %s, config: %s, %v\n", - CacheAdapter, CacheConfig, err) - os.Exit(2) - } - // Determine and create root git reposiroty path. RepoRootPath = Cfg.MustValue("repository", "ROOT") if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { @@ -209,6 +223,7 @@ func NewConfigContext() { func NewServices() { newService() newLogService() + newCacheService() newMailService() newRegisterMailService() } diff --git a/routers/admin/admin.go b/routers/admin/admin.go index 6d3831a9f3..d70af3c50c 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -67,5 +67,8 @@ func Config(ctx *middleware.Context) { ctx.Data["Mailer"] = base.MailService } + ctx.Data["CacheAdapter"] = base.CacheAdapter + ctx.Data["CacheConfig"] = base.CacheConfig + ctx.HTML(200, "admin/config") } diff --git a/routers/user/user.go b/routers/user/user.go index 40b594ab80..d38eb1ceb3 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -167,6 +167,10 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) { ctx.Data["Email"] = u.Email ctx.Data["Hours"] = base.Service.ActiveCodeLives / 60 ctx.HTML(200, "user/active") + + if err = ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil { + log.Error("Set cache(MailResendLimit) fail: %v", err) + } return } ctx.Redirect("/user/login") @@ -247,8 +251,12 @@ func Activate(ctx *middleware.Context) { } // Resend confirmation e-mail. if base.Service.RegisterEmailConfirm { - ctx.Data["Hours"] = base.Service.ActiveCodeLives / 60 - mailer.SendActiveMail(ctx.Render, ctx.User) + if ctx.Cache.IsExist("MailResendLimit_" + ctx.User.LowerName) { + ctx.Data["ResendLimited"] = true + } else { + ctx.Data["Hours"] = base.Service.ActiveCodeLives / 60 + mailer.SendActiveMail(ctx.Render, ctx.User) + } } else { ctx.Data["ServiceNotEnabled"] = true } diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 7013c201ae..ad32ec3fb1 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -63,6 +63,17 @@ <div><b>User:</b> {{.Mailer.User}}</div> </div> </div> + + <div class="panel panel-default"> + <div class="panel-heading"> + Cache Configuration + </div> + + <div class="panel-body"> + <div><b>Cache Adapter:</b> {{.CacheAdapter}}</div> + <div><b>Cache Config:</b> <code>{{.CacheConfig}}</code></div> + </div> + </div> </div> </div> {{template "base/footer" .}} \ No newline at end of file diff --git a/templates/user/active.tmpl b/templates/user/active.tmpl index fefd7d3aed..47c87a591c 100644 --- a/templates/user/active.tmpl +++ b/templates/user/active.tmpl @@ -6,6 +6,8 @@ {{if .IsActivatePage}} {{if .ServiceNotEnabled}} <p>Sorry, Register Mail Confirmation has been disabled.</p> + {{else if .ResendLimited}} + <p>Sorry, you are sending activation e-mail too frequently, please wait 3 minutes.</p> {{else}} <p>New confirmation e-mail has been sent to <b>{{.SignedUser.Email}}</b>, please check your inbox within {{.Hours}} hours to complete your registeration.</p> <hr/> From 011134e0aff5d16501b60a6feaaa8c2ccf0d269d Mon Sep 17 00:00:00 2001 From: FuXiaoHei <fuxiaohei@hexiaz.com> Date: Fri, 21 Mar 2014 23:27:33 +0800 Subject: [PATCH 40/41] add clone link in repo index page --- public/css/gogs.css | 9 +++++++++ public/js/app.js | 21 ++++++++++++++------- templates/repo/nav.tmpl | 30 ++++++++++++++++++++++++------ templates/repo/single_bare.tmpl | 4 ++-- 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/public/css/gogs.css b/public/css/gogs.css index 36d57536c6..78040bee51 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -575,6 +575,15 @@ html, body { min-width: 200px; } +#gogs-repo-clone .dropdown-menu{ + width: 400px; + padding: 20px; +} + +#gogs-repo-clone .input-group{ + margin-bottom: 15px; +} + /* #gogs-source */ #gogs-source { margin-top: -20px; diff --git a/public/js/app.js b/public/js/app.js index a264fc94eb..f179342f4b 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -63,6 +63,12 @@ var Gogits = { var $tabs = $('[data-init=tabs]'); $tabs.find("li:eq(0) a").tab("show"); }; + // fix dropdown inside click + Gogits.initDropDown = function(){ + $('.dropdown-menu').on('click','a,button,input,select',function(e){ + e.stopPropagation(); + }); + }; // render markdown Gogits.renderMarkdown = function () { @@ -136,6 +142,7 @@ function initCore() { Gogits.initPopovers(); Gogits.initTabs(); Gogits.initModals(); + Gogits.initDropDown(); Gogits.renderMarkdown(); } @@ -181,18 +188,18 @@ function initUserSetting() { } function initRepository() { - // guide box script + // clone group button script (function () { - var $guide = $('.guide-box'); - if ($guide.length) { - var $url = $('#guide-clone-url'); - $guide.find('button[data-link]').on("click",function () { + var $clone = $('.clone-group-btn'); + if ($clone.length) { + var $url = $('.clone-group-url'); + $clone.find('button[data-link]').on("click",function (e) { var $this = $(this); if (!$this.hasClass('btn-primary')) { - $guide.find('.btn-primary').removeClass('btn-primary').addClass("btn-default"); + $clone.find('.btn-primary').removeClass('btn-primary').addClass("btn-default"); $(this).addClass('btn-primary').removeClass('btn-default'); $url.val($this.data("link")); - $guide.find('span.clone-url').text($this.data('link')); + $clone.find('span.clone-url').text($this.data('link')); } }).eq(0).trigger("click"); // todo copy to clipboard diff --git a/templates/repo/nav.tmpl b/templates/repo/nav.tmpl index c61051af25..d4a692fd03 100644 --- a/templates/repo/nav.tmpl +++ b/templates/repo/nav.tmpl @@ -5,13 +5,32 @@ <h3 class="name"><i class="fa fa-book fa-lg"></i><a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> / {{.Repository.Name}}</h3> <p class="desc">{{.Repository.Description}}{{if .Repository.Website}}<a href="{{.Repository.Website}}">{{.Repository.Website}}</a>{{end}}</p> </div> - <div class="col-md-6 actions text-right"> + <div class="col-md-6 actions text-right clone-group-btn"> {{if not .IsBareRepo}} - <div class="btn-group" id="gogs-repo-clone"> + <!--<div class="btn-group" id="gogs-repo-clone"> <button type="button" class="btn btn-default"><i class="fa fa-download fa-lg fa-m"></i></button> <button type="button" class="btn btn-default dropdown-toggle" data-container="body" data-toggle="popover" data-placement="bottom" data-content="<label>SSH:</label><div class='input-group'><input type='text' class='form-control' value='{{.CloneLink.SSH}}'></div>" data-html="1"> <span class="caret"></span> </button> + </div>--> + <div class="btn-group" id="gogs-repo-clone"> + <button type="button" class="btn btn-default"><i class="fa fa-download fa-lg fa-m"></i></button> + <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> + <span class="caret"></span> + </button> + <div class="dropdown-menu clone-group-btn dropdown-menu-right"> + <div class="input-group"> + <span class="input-group-btn"> + <button class="btn btn-default" data-link="{{.CloneLink.SSH}}" type="button">SSH</button> + <button class="btn btn-default" data-link="{{.CloneLink.HTTPS}}" type="button">HTTPS</button> + </span> + <input type="text" class="form-control clone-group-url" value="" readonly/> + <span class="input-group-btn"> + <button class="btn btn-default" type="button"><i class="fa fa-copy" data-toggle="tooltip" title="copy to clipboard" data-placement="top"></i></button> + </span> + </div> + <p class="help-block text-center">Need help cloning? Visit <a href="#">Help</a>!</p> + </div> </div> <div class="btn-group {{if .IsRepositoryWatching}}watching{{else}}no-watching{{end}}" id="gogs-repo-watching" data-watch="/{{.SignedUser.Name}}/{{.Repository.Name}}/action/watch" data-unwatch="/{{.SignedUser.Name}}/{{.Repository.Name}}/action/unwatch"> {{if .IsRepositoryWatching}} @@ -21,9 +40,8 @@ {{end}} <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> <span class="caret"></span> - <span class="sr-only">Toggle Dropdown</span> </button> - <div class="dropdown-menu" role="menu"> + <div class="dropdown-menu"> <div class="dropdown-item text-left to-unwatch"> <h4 role="presentation" class="dropdown-header {{if not .IsRepositoryWatching}}text-primary{{end}}">Not Watching</h4> <p class="description">You only receive notifications for conversations in which you participate or are @mentioned.</p> @@ -36,11 +54,11 @@ </div> </div> <div class="btn-group"> - <button type="button" class="btn btn-default"><i class="fa fa-star"></i>Star {{.Repository.NumStars}}</button> + <button type="button" class="btn btn-default" data-toggle="tooltip" data-placement="top" title="Star"><i class="fa fa-star"></i> {{.Repository.NumStars}}</button> </div> {{end}} <div class="btn-group"> - <a type="button" {{if not .IsRepositoryOwner}}href="/{{.Username}}/{{.Reponame}}/fork"{{end}} class="btn btn-default"><i class="fa fa-code-fork"></i>Fork {{.Repository.NumForks}}</a> + <a type="button" {{if not .IsRepositoryOwner}}href="/{{.Username}}/{{.Reponame}}/fork"{{end}} class="btn btn-default" data-toggle="tooltip" data-placement="top" title="Fork"><i class="fa fa-code-fork fa-lg"></i> {{.Repository.NumForks}}</a> </div> </div> </div> diff --git a/templates/repo/single_bare.tmpl b/templates/repo/single_bare.tmpl index bb05c2f9a6..ed182ad28e 100644 --- a/templates/repo/single_bare.tmpl +++ b/templates/repo/single_bare.tmpl @@ -1,4 +1,4 @@ -<div class="panel panel-default guide-box"> +<div class="panel panel-default guide-box clone-group-btn"> <div class="panel-heading guide-head"> <h4>Quick Guide</h4> </div> @@ -9,7 +9,7 @@ <button class="btn btn-default" data-link="{{.CloneLink.SSH}}" type="button">SSH</button> <button class="btn btn-default" data-link="{{.CloneLink.HTTPS}}" type="button">HTTPS</button> </span> - <input type="text" class="form-control" id="guide-clone-url" value="" readonly/> + <input type="text" class="form-control clone-group-url" id="guide-clone-url" value="" readonly/> <span class="input-group-btn"> <button class="btn btn-default" type="button"><i class="fa fa-copy" data-toggle="tooltip" title="copy to clipboard" data-placement="top"></i></button> </span> From f219ddcf4ef13b9d5de129da4eb2b56dbc899d61 Mon Sep 17 00:00:00 2001 From: Unknown <joe2010xtmf@163.com> Date: Fri, 21 Mar 2014 12:13:13 -0400 Subject: [PATCH 41/41] Add log config panel in admin --- .gopmfile | 3 +-- modules/base/conf.go | 26 ++++++++++++++------------ routers/admin/admin.go | 3 +++ templates/admin/config.tmpl | 16 +++++++++++++++- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/.gopmfile b/.gopmfile index 9e2440f3ef..5b690a06a7 100644 --- a/.gopmfile +++ b/.gopmfile @@ -9,13 +9,12 @@ github.com/Unknwon/com= github.com/Unknwon/cae= github.com/Unknwon/goconfig= github.com/dchest/scrypt= -github.com/go-sql-driver/mysql= -github.com/lib/pq= github.com/lunny/xorm= github.com/gogits/logs= github.com/gogits/binding= github.com/gogits/git= github.com/gogits/gfm= +github.com/gogits/cache= [res] include=templates|public|conf diff --git a/modules/base/conf.go b/modules/base/conf.go index 2c3ecd72d8..863daca644 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -43,6 +43,9 @@ var ( Cache cache.Cache CacheAdapter string CacheConfig string + + LogMode string + LogConfig string ) var Service struct { @@ -83,15 +86,15 @@ func newService() { func newLogService() { // Get and check log mode. - mode := Cfg.MustValue("log", "MODE", "console") - modeSec := "log." + mode + LogMode = Cfg.MustValue("log", "MODE", "console") + modeSec := "log." + LogMode if _, err := Cfg.GetSection(modeSec); err != nil { - fmt.Printf("Unknown log mode: %s\n", mode) + fmt.Printf("Unknown log mode: %s\n", LogMode) os.Exit(2) } // Log level. - levelName := Cfg.MustValue("log."+mode, "LEVEL", "Trace") + levelName := Cfg.MustValue("log."+LogMode, "LEVEL", "Trace") level, ok := logLevels[levelName] if !ok { fmt.Printf("Unknown log level: %s\n", levelName) @@ -99,14 +102,13 @@ func newLogService() { } // Generate log configuration. - var config string - switch mode { + switch LogMode { case "console": - config = fmt.Sprintf(`{"level":%s}`, level) + LogConfig = fmt.Sprintf(`{"level":%s}`, level) case "file": logPath := Cfg.MustValue(modeSec, "FILE_NAME", "log/gogs.log") os.MkdirAll(path.Dir(logPath), os.ModePerm) - config = fmt.Sprintf( + LogConfig = fmt.Sprintf( `{"level":%s,"filename":%s,"rotate":%v,"maxlines":%d,"maxsize",%d,"daily":%v,"maxdays":%d}`, level, logPath, Cfg.MustBool(modeSec, "LOG_ROTATE", true), @@ -115,13 +117,13 @@ func newLogService() { Cfg.MustBool(modeSec, "DAILY_ROTATE", true), Cfg.MustInt(modeSec, "MAX_DAYS", 7)) case "conn": - config = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":%s,"addr":%s}`, level, + LogConfig = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":%s,"addr":%s}`, level, Cfg.MustBool(modeSec, "RECONNECT_ON_MSG", false), Cfg.MustBool(modeSec, "RECONNECT", false), Cfg.MustValue(modeSec, "PROTOCOL", "tcp"), Cfg.MustValue(modeSec, "ADDR", ":7020")) case "smtp": - config = fmt.Sprintf(`{"level":%s,"username":%s,"password":%s,"host":%s,"sendTos":%s,"subject":%s}`, level, + LogConfig = fmt.Sprintf(`{"level":%s,"username":%s,"password":%s,"host":%s,"sendTos":%s,"subject":%s}`, level, Cfg.MustValue(modeSec, "USER", "example@example.com"), Cfg.MustValue(modeSec, "PASSWD", "******"), Cfg.MustValue(modeSec, "HOST", "127.0.0.1:25"), @@ -129,8 +131,8 @@ func newLogService() { Cfg.MustValue(modeSec, "SUBJECT", "Diagnostic message from serve")) } - log.NewLogger(Cfg.MustInt64("log", "BUFFER_LEN", 10000), mode, config) - log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName) + log.NewLogger(Cfg.MustInt64("log", "BUFFER_LEN", 10000), LogMode, LogConfig) + log.Info("Log Mode: %s(%s)", strings.Title(LogMode), levelName) } func newCacheService() { diff --git a/routers/admin/admin.go b/routers/admin/admin.go index d70af3c50c..2e19b99c10 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -70,5 +70,8 @@ func Config(ctx *middleware.Context) { ctx.Data["CacheAdapter"] = base.CacheAdapter ctx.Data["CacheConfig"] = base.CacheConfig + ctx.Data["LogMode"] = base.LogMode + ctx.Data["LogConfig"] = base.LogConfig + ctx.HTML(200, "admin/config") } diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index ad32ec3fb1..6906f2409d 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -71,7 +71,21 @@ <div class="panel-body"> <div><b>Cache Adapter:</b> {{.CacheAdapter}}</div> - <div><b>Cache Config:</b> <code>{{.CacheConfig}}</code></div> + <div><b>Cache Config:</b></div> + <div style="padding-top: 5px;"><pre>{{.CacheConfig}}</pre></div> + </div> + </div> + + <div class="panel panel-default"> + <div class="panel-heading"> + Log Configuration + </div> + + <div class="panel-body"> + <div><b>Log Mode:</b> {{.LogMode}}</div> + <div><b>Log Config:</b></div> + <div style="padding-top: 5px;"><pre>{{.LogConfig}}</pre></div> + </div> </div> </div>