| @@ -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. | 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 | ## Purpose | ||||
| @@ -61,8 +61,14 @@ USER = | |||||
| PASSWD = | PASSWD = | ||||
| [cache] | [cache] | ||||
| ; Either "memory", "redis", or "memcache", default is "memory" | |||||
| ADAPTER = 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] | [log] | ||||
| ; Either "console", "file", "conn" or "smtp", default is "console" | ; Either "console", "file", "conn" or "smtp", default is "console" | ||||
| @@ -20,7 +20,7 @@ import ( | |||||
| // Test that go1.2 tag above is included in builds. main.go refers to this definition. | // Test that go1.2 tag above is included in builds. main.go refers to this definition. | ||||
| const go12tag = true | const go12tag = true | ||||
| const APP_VER = "0.1.4.0321" | |||||
| const APP_VER = "0.1.5.0321" | |||||
| func init() { | func init() { | ||||
| base.AppVer = APP_VER | base.AppVer = APP_VER | ||||
| @@ -35,7 +35,6 @@ func LoadModelsConfig() { | |||||
| } | } | ||||
| func setEngine() { | func setEngine() { | ||||
| var err error | var err error | ||||
| switch DbCfg.Type { | switch DbCfg.Type { | ||||
| case "mysql": | case "mysql": | ||||
| @@ -133,6 +133,30 @@ func newLogService() { | |||||
| log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName) | 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() { | func newMailService() { | ||||
| // Check mailer setting. | // Check mailer setting. | ||||
| if Cfg.MustBool("mailer", "ENABLED") { | if Cfg.MustBool("mailer", "ENABLED") { | ||||
| @@ -188,16 +212,6 @@ func NewConfigContext() { | |||||
| SecretKey = Cfg.MustValue("security", "SECRET_KEY") | SecretKey = Cfg.MustValue("security", "SECRET_KEY") | ||||
| RunUser = Cfg.MustValue("", "RUN_USER") | 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. | // Determine and create root git reposiroty path. | ||||
| RepoRootPath = Cfg.MustValue("repository", "ROOT") | RepoRootPath = Cfg.MustValue("repository", "ROOT") | ||||
| if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { | if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { | ||||
| @@ -209,6 +223,7 @@ func NewConfigContext() { | |||||
| func NewServices() { | func NewServices() { | ||||
| newService() | newService() | ||||
| newLogService() | newLogService() | ||||
| newCacheService() | |||||
| newMailService() | newMailService() | ||||
| newRegisterMailService() | newRegisterMailService() | ||||
| } | } | ||||
| @@ -575,6 +575,15 @@ html, body { | |||||
| min-width: 200px; | min-width: 200px; | ||||
| } | } | ||||
| #gogs-repo-clone .dropdown-menu{ | |||||
| width: 400px; | |||||
| padding: 20px; | |||||
| } | |||||
| #gogs-repo-clone .input-group{ | |||||
| margin-bottom: 15px; | |||||
| } | |||||
| /* #gogs-source */ | /* #gogs-source */ | ||||
| #gogs-source { | #gogs-source { | ||||
| margin-top: -20px; | margin-top: -20px; | ||||
| @@ -63,6 +63,12 @@ var Gogits = { | |||||
| var $tabs = $('[data-init=tabs]'); | var $tabs = $('[data-init=tabs]'); | ||||
| $tabs.find("li:eq(0) a").tab("show"); | $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 | // render markdown | ||||
| Gogits.renderMarkdown = function () { | Gogits.renderMarkdown = function () { | ||||
| @@ -136,6 +142,7 @@ function initCore() { | |||||
| Gogits.initPopovers(); | Gogits.initPopovers(); | ||||
| Gogits.initTabs(); | Gogits.initTabs(); | ||||
| Gogits.initModals(); | Gogits.initModals(); | ||||
| Gogits.initDropDown(); | |||||
| Gogits.renderMarkdown(); | Gogits.renderMarkdown(); | ||||
| } | } | ||||
| @@ -181,18 +188,18 @@ function initUserSetting() { | |||||
| } | } | ||||
| function initRepository() { | function initRepository() { | ||||
| // guide box script | |||||
| // clone group button script | |||||
| (function () { | (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); | var $this = $(this); | ||||
| if (!$this.hasClass('btn-primary')) { | 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'); | $(this).addClass('btn-primary').removeClass('btn-default'); | ||||
| $url.val($this.data("link")); | $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"); | }).eq(0).trigger("click"); | ||||
| // todo copy to clipboard | // todo copy to clipboard | ||||
| @@ -67,5 +67,8 @@ func Config(ctx *middleware.Context) { | |||||
| ctx.Data["Mailer"] = base.MailService | ctx.Data["Mailer"] = base.MailService | ||||
| } | } | ||||
| ctx.Data["CacheAdapter"] = base.CacheAdapter | |||||
| ctx.Data["CacheConfig"] = base.CacheConfig | |||||
| ctx.HTML(200, "admin/config") | ctx.HTML(200, "admin/config") | ||||
| } | } | ||||
| @@ -167,6 +167,10 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) { | |||||
| ctx.Data["Email"] = u.Email | ctx.Data["Email"] = u.Email | ||||
| ctx.Data["Hours"] = base.Service.ActiveCodeLives / 60 | ctx.Data["Hours"] = base.Service.ActiveCodeLives / 60 | ||||
| ctx.HTML(200, "user/active") | 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 | return | ||||
| } | } | ||||
| ctx.Redirect("/user/login") | ctx.Redirect("/user/login") | ||||
| @@ -247,8 +251,12 @@ func Activate(ctx *middleware.Context) { | |||||
| } | } | ||||
| // Resend confirmation e-mail. | // Resend confirmation e-mail. | ||||
| if base.Service.RegisterEmailConfirm { | 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 { | } else { | ||||
| ctx.Data["ServiceNotEnabled"] = true | ctx.Data["ServiceNotEnabled"] = true | ||||
| } | } | ||||
| @@ -63,6 +63,17 @@ | |||||
| <div><b>User:</b> {{.Mailer.User}}</div> | <div><b>User:</b> {{.Mailer.User}}</div> | ||||
| </div> | </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> | ||||
| </div> | </div> | ||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| @@ -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> | <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> | <p class="desc">{{.Repository.Description}}{{if .Repository.Website}}<a href="{{.Repository.Website}}">{{.Repository.Website}}</a>{{end}}</p> | ||||
| </div> | </div> | ||||
| <div class="col-md-6 actions text-right"> | |||||
| <div class="col-md-6 actions text-right clone-group-btn"> | |||||
| {{if not .IsBareRepo}} | {{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"><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"> | <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> | <span class="caret"></span> | ||||
| </button> | </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> | ||||
| <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"> | <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}} | {{if .IsRepositoryWatching}} | ||||
| @@ -21,9 +40,8 @@ | |||||
| {{end}} | {{end}} | ||||
| <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> | <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> | ||||
| <span class="caret"></span> | <span class="caret"></span> | ||||
| <span class="sr-only">Toggle Dropdown</span> | |||||
| </button> | </button> | ||||
| <div class="dropdown-menu" role="menu"> | |||||
| <div class="dropdown-menu"> | |||||
| <div class="dropdown-item text-left to-unwatch"> | <div class="dropdown-item text-left to-unwatch"> | ||||
| <h4 role="presentation" class="dropdown-header {{if not .IsRepositoryWatching}}text-primary{{end}}">Not Watching</h4> | <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="description">You only receive notifications for conversations in which you participate or are @mentioned.</p> | ||||
| @@ -36,11 +54,11 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="btn-group"> | <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> | </div> | ||||
| {{end}} | {{end}} | ||||
| <div class="btn-group"> | <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> | </div> | ||||
| </div> | </div> | ||||
| @@ -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"> | <div class="panel-heading guide-head"> | ||||
| <h4>Quick Guide</h4> | <h4>Quick Guide</h4> | ||||
| </div> | </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.SSH}}" type="button">SSH</button> | ||||
| <button class="btn btn-default" data-link="{{.CloneLink.HTTPS}}" type="button">HTTPS</button> | <button class="btn btn-default" data-link="{{.CloneLink.HTTPS}}" type="button">HTTPS</button> | ||||
| </span> | </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"> | <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> | <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> | </span> | ||||
| @@ -6,6 +6,8 @@ | |||||
| {{if .IsActivatePage}} | {{if .IsActivatePage}} | ||||
| {{if .ServiceNotEnabled}} | {{if .ServiceNotEnabled}} | ||||
| <p>Sorry, Register Mail Confirmation has been disabled.</p> | <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}} | {{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> | <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/> | <hr/> | ||||