| @@ -5,7 +5,7 @@ Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language | |||||
|  |  | ||||
| ##### Current version: 0.1.9 Alpha | |||||
| ##### Current version: 0.2.0 Alpha | |||||
| #### Due to testing purpose, data of [try.gogits.org](http://try.gogits.org) has been reset in March 29, 2014 and will reset multiple times after. Please do NOT put your important data on the site. | #### Due to testing purpose, data of [try.gogits.org](http://try.gogits.org) has been reset in March 29, 2014 and will reset multiple times after. Please do NOT put your important data on the site. | ||||
| @@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。 | |||||
|  |  | ||||
| ##### 当前版本:0.1.9 Alpha | |||||
| ##### 当前版本:0.2.0 Alpha | |||||
| ## 开发目的 | ## 开发目的 | ||||
| @@ -19,7 +19,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.9.0329 Alpha" | |||||
| const APP_VER = "0.2.0.0329 Alpha" | |||||
| func init() { | func init() { | ||||
| base.AppVer = APP_VER | base.AppVer = APP_VER | ||||
| @@ -170,9 +170,17 @@ type Milestone struct { | |||||
| Created time.Time `xorm:"created"` | Created time.Time `xorm:"created"` | ||||
| } | } | ||||
| // Issue types. | |||||
| const ( | |||||
| IT_PLAIN = iota // Pure comment. | |||||
| IT_REOPEN // Issue reopen status change prompt. | |||||
| IT_CLOSE // Issue close status change prompt. | |||||
| ) | |||||
| // Comment represents a comment in commit and issue page. | // Comment represents a comment in commit and issue page. | ||||
| type Comment struct { | type Comment struct { | ||||
| Id int64 | Id int64 | ||||
| Type int | |||||
| PosterId int64 | PosterId int64 | ||||
| Poster *User `xorm:"-"` | Poster *User `xorm:"-"` | ||||
| IssueId int64 | IssueId int64 | ||||
| @@ -183,21 +191,37 @@ type Comment struct { | |||||
| } | } | ||||
| // CreateComment creates comment of issue or commit. | // CreateComment creates comment of issue or commit. | ||||
| func CreateComment(userId, issueId, commitId, line int64, content string) error { | |||||
| func CreateComment(userId, repoId, issueId, commitId, line int64, cmtType int, content string) error { | |||||
| sess := orm.NewSession() | sess := orm.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| sess.Begin() | sess.Begin() | ||||
| if _, err := orm.Insert(&Comment{PosterId: userId, IssueId: issueId, | |||||
| if _, err := orm.Insert(&Comment{PosterId: userId, Type: cmtType, IssueId: issueId, | |||||
| CommitId: commitId, Line: line, Content: content}); err != nil { | CommitId: commitId, Line: line, Content: content}); err != nil { | ||||
| sess.Rollback() | sess.Rollback() | ||||
| return err | return err | ||||
| } | } | ||||
| rawSql := "UPDATE `issue` SET num_comments = num_comments + 1 WHERE id = ?" | |||||
| if _, err := sess.Exec(rawSql, issueId); err != nil { | |||||
| sess.Rollback() | |||||
| return err | |||||
| // Check comment type. | |||||
| switch cmtType { | |||||
| case IT_PLAIN: | |||||
| rawSql := "UPDATE `issue` SET num_comments = num_comments + 1 WHERE id = ?" | |||||
| if _, err := sess.Exec(rawSql, issueId); err != nil { | |||||
| sess.Rollback() | |||||
| return err | |||||
| } | |||||
| case IT_REOPEN: | |||||
| rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues - 1 WHERE id = ?" | |||||
| if _, err := sess.Exec(rawSql, repoId); err != nil { | |||||
| sess.Rollback() | |||||
| return err | |||||
| } | |||||
| case IT_CLOSE: | |||||
| rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues + 1 WHERE id = ?" | |||||
| if _, err := sess.Exec(rawSql, repoId); err != nil { | |||||
| sess.Rollback() | |||||
| return err | |||||
| } | |||||
| } | } | ||||
| return sess.Commit() | return sess.Commit() | ||||
| } | } | ||||
| @@ -34,8 +34,7 @@ func LoadModelsConfig() { | |||||
| DbCfg.Path = base.Cfg.MustValue("database", "PATH", "data/gogs.db") | DbCfg.Path = base.Cfg.MustValue("database", "PATH", "data/gogs.db") | ||||
| } | } | ||||
| func SetEngine() { | |||||
| var err error | |||||
| func SetEngine() (err error) { | |||||
| switch DbCfg.Type { | switch DbCfg.Type { | ||||
| case "mysql": | case "mysql": | ||||
| orm, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", | orm, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", | ||||
| @@ -47,12 +46,10 @@ func SetEngine() { | |||||
| os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm) | os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm) | ||||
| orm, err = xorm.NewEngine("sqlite3", DbCfg.Path) | orm, err = xorm.NewEngine("sqlite3", DbCfg.Path) | ||||
| default: | default: | ||||
| fmt.Printf("Unknown database type: %s\n", DbCfg.Type) | |||||
| os.Exit(2) | |||||
| return fmt.Errorf("Unknown database type: %s\n", DbCfg.Type) | |||||
| } | } | ||||
| if err != nil { | if err != nil { | ||||
| fmt.Printf("models.init(fail to conntect database): %v\n", err) | |||||
| os.Exit(2) | |||||
| return fmt.Errorf("models.init(fail to conntect database): %v\n", err) | |||||
| } | } | ||||
| // WARNNING: for serv command, MUST remove the output to os.stdout, | // WARNNING: for serv command, MUST remove the output to os.stdout, | ||||
| @@ -62,20 +59,21 @@ func SetEngine() { | |||||
| //orm.ShowErr = true | //orm.ShowErr = true | ||||
| f, err := os.Create("xorm.log") | f, err := os.Create("xorm.log") | ||||
| if err != nil { | if err != nil { | ||||
| fmt.Printf("models.init(fail to create xorm.log): %v\n", err) | |||||
| os.Exit(2) | |||||
| return fmt.Errorf("models.init(fail to create xorm.log): %v\n", err) | |||||
| } | } | ||||
| orm.Logger = f | orm.Logger = f | ||||
| orm.ShowSQL = true | orm.ShowSQL = true | ||||
| return nil | |||||
| } | } | ||||
| func NewEngine() { | |||||
| SetEngine() | |||||
| if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Watch), | |||||
| func NewEngine() (err error) { | |||||
| if err = SetEngine(); err != nil { | |||||
| return err | |||||
| } else if err = orm.Sync(new(User), new(PublicKey), new(Repository), new(Watch), | |||||
| new(Action), new(Access), new(Issue), new(Comment)); err != nil { | new(Action), new(Access), new(Issue), new(Comment)); err != nil { | ||||
| fmt.Printf("sync database struct error: %v\n", err) | |||||
| os.Exit(2) | |||||
| return fmt.Errorf("sync database struct error: %v\n", err) | |||||
| } | } | ||||
| return nil | |||||
| } | } | ||||
| type Statistic struct { | type Statistic struct { | ||||
| @@ -11,7 +11,6 @@ import ( | |||||
| "os" | "os" | ||||
| "os/exec" | "os/exec" | ||||
| "path/filepath" | "path/filepath" | ||||
| "regexp" | |||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| "unicode/utf8" | "unicode/utf8" | ||||
| @@ -59,15 +58,6 @@ func NewRepoContext() { | |||||
| os.Exit(2) | 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 | |||||
| } | |||||
| } | } | ||||
| // Repository represents a git repository. | // Repository represents a git repository. | ||||
| @@ -105,15 +95,20 @@ func IsRepositoryExist(user *User, repoName string) (bool, error) { | |||||
| } | } | ||||
| var ( | var ( | ||||
| // Define as all lower case!! | |||||
| illegalPatterns = []string{"[.][Gg][Ii][Tt]", "raw", "user", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin"} | |||||
| illegalEquals = []string{"raw", "install", "api", "avatar", "user", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin"} | |||||
| illegalSuffixs = []string{".git"} | |||||
| ) | ) | ||||
| // IsLegalName returns false if name contains illegal characters. | // IsLegalName returns false if name contains illegal characters. | ||||
| func IsLegalName(repoName string) bool { | func IsLegalName(repoName string) bool { | ||||
| for _, pattern := range illegalPatterns { | |||||
| has, _ := regexp.MatchString(pattern, repoName) | |||||
| if has { | |||||
| repoName = strings.ToLower(repoName) | |||||
| for _, char := range illegalEquals { | |||||
| if repoName == char { | |||||
| return false | |||||
| } | |||||
| } | |||||
| for _, char := range illegalSuffixs { | |||||
| if strings.HasSuffix(repoName, char) { | |||||
| return false | return false | ||||
| } | } | ||||
| } | } | ||||
| @@ -172,6 +172,7 @@ type InstallForm struct { | |||||
| DatabasePath string `form:"database_path"` | DatabasePath string `form:"database_path"` | ||||
| RepoRootPath string `form:"repo_path"` | RepoRootPath string `form:"repo_path"` | ||||
| RunUser string `form:"run_user"` | RunUser string `form:"run_user"` | ||||
| Domain string `form:"domain"` | |||||
| AppUrl string `form:"app_url"` | AppUrl string `form:"app_url"` | ||||
| AdminName string `form:"admin_name" binding:"Required"` | AdminName string `form:"admin_name" binding:"Required"` | ||||
| AdminPasswd string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(30)"` | AdminPasswd string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(30)"` | ||||
| @@ -272,18 +272,19 @@ func NewConfigContext() { | |||||
| Domain = Cfg.MustValue("server", "DOMAIN") | Domain = Cfg.MustValue("server", "DOMAIN") | ||||
| SecretKey = Cfg.MustValue("security", "SECRET_KEY") | SecretKey = Cfg.MustValue("security", "SECRET_KEY") | ||||
| InstallLock = Cfg.MustBool("security", "INSTALL_LOCK", false) | |||||
| RunUser = Cfg.MustValue("", "RUN_USER") | RunUser = Cfg.MustValue("", "RUN_USER") | ||||
| curUser := os.Getenv("USERNAME") | curUser := os.Getenv("USERNAME") | ||||
| if len(curUser) == 0 { | if len(curUser) == 0 { | ||||
| curUser = os.Getenv("USER") | curUser = os.Getenv("USER") | ||||
| } | } | ||||
| if RunUser != curUser { | |||||
| // Does not check run user when the install lock is off. | |||||
| if InstallLock && RunUser != curUser { | |||||
| fmt.Printf("Expect user(%s) but current user is: %s\n", RunUser, curUser) | fmt.Printf("Expect user(%s) but current user is: %s\n", RunUser, curUser) | ||||
| os.Exit(2) | os.Exit(2) | ||||
| } | } | ||||
| InstallLock = Cfg.MustBool("security", "INSTALL_LOCK", false) | |||||
| LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS") | LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS") | ||||
| CookieUserName = Cfg.MustValue("security", "COOKIE_USERNAME") | CookieUserName = Cfg.MustValue("security", "COOKIE_USERNAME") | ||||
| CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME") | CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME") | ||||
| @@ -1197,13 +1197,13 @@ html, body { | |||||
| line-height: 42px; | line-height: 42px; | ||||
| } | } | ||||
| #issue .issue-closed{ | |||||
| border-bottom: 3px solid #CCC; | |||||
| #issue .issue-closed, #issue .issue-opened { | |||||
| border-bottom: 2px solid #CCC; | |||||
| margin-bottom: 24px; | margin-bottom: 24px; | ||||
| padding-bottom: 24px; | padding-bottom: 24px; | ||||
| } | } | ||||
| #issue .issue-closed .btn-danger,#issue .issue-opened .btn-success{ | |||||
| #issue .issue-closed .label-danger,#issue .issue-opened .label-success{ | |||||
| margin: 0 .8em; | margin: 0 .8em; | ||||
| } | } | ||||
| @@ -6,13 +6,46 @@ package routers | |||||
| import ( | import ( | ||||
| "errors" | "errors" | ||||
| "os" | |||||
| "strings" | |||||
| "github.com/Unknwon/goconfig" | |||||
| "github.com/codegangsta/martini" | |||||
| "github.com/gogits/gogs/models" | "github.com/gogits/gogs/models" | ||||
| "github.com/gogits/gogs/modules/auth" | "github.com/gogits/gogs/modules/auth" | ||||
| "github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
| "github.com/gogits/gogs/modules/log" | |||||
| "github.com/gogits/gogs/modules/mailer" | |||||
| "github.com/gogits/gogs/modules/middleware" | "github.com/gogits/gogs/modules/middleware" | ||||
| ) | ) | ||||
| // Check run mode(Default of martini is Dev). | |||||
| func checkRunMode() { | |||||
| switch base.Cfg.MustValue("", "RUN_MODE") { | |||||
| case "prod": | |||||
| martini.Env = martini.Prod | |||||
| case "test": | |||||
| martini.Env = martini.Test | |||||
| } | |||||
| log.Info("Run Mode: %s", strings.Title(martini.Env)) | |||||
| } | |||||
| // GlobalInit is for global configuration reload-able. | |||||
| func GlobalInit() { | |||||
| base.NewConfigContext() | |||||
| mailer.NewMailerContext() | |||||
| models.LoadModelsConfig() | |||||
| models.LoadRepoConfig() | |||||
| models.NewRepoContext() | |||||
| if err := models.NewEngine(); err != nil && base.InstallLock { | |||||
| log.Error("%v", err) | |||||
| os.Exit(2) | |||||
| } | |||||
| base.NewServices() | |||||
| checkRunMode() | |||||
| } | |||||
| func Install(ctx *middleware.Context, form auth.InstallForm) { | func Install(ctx *middleware.Context, form auth.InstallForm) { | ||||
| if base.InstallLock { | if base.InstallLock { | ||||
| ctx.Handle(404, "install.Install", errors.New("Installation is prohibited")) | ctx.Handle(404, "install.Install", errors.New("Installation is prohibited")) | ||||
| @@ -20,14 +53,114 @@ func Install(ctx *middleware.Context, form auth.InstallForm) { | |||||
| } | } | ||||
| ctx.Data["Title"] = "Install" | ctx.Data["Title"] = "Install" | ||||
| ctx.Data["DbCfg"] = models.DbCfg | |||||
| ctx.Data["RepoRootPath"] = base.RepoRootPath | |||||
| ctx.Data["RunUser"] = base.RunUser | |||||
| ctx.Data["AppUrl"] = base.AppUrl | |||||
| ctx.Data["PageIsInstall"] = true | ctx.Data["PageIsInstall"] = true | ||||
| // Get and assign value to install form. | |||||
| if len(form.Host) == 0 { | |||||
| form.Host = models.DbCfg.Host | |||||
| } | |||||
| if len(form.User) == 0 { | |||||
| form.User = models.DbCfg.User | |||||
| } | |||||
| if len(form.Passwd) == 0 { | |||||
| form.Passwd = models.DbCfg.Pwd | |||||
| } | |||||
| if len(form.DatabaseName) == 0 { | |||||
| form.DatabaseName = models.DbCfg.Name | |||||
| } | |||||
| if len(form.DatabasePath) == 0 { | |||||
| form.DatabasePath = models.DbCfg.Path | |||||
| } | |||||
| if len(form.RepoRootPath) == 0 { | |||||
| form.RepoRootPath = base.RepoRootPath | |||||
| } | |||||
| if len(form.RunUser) == 0 { | |||||
| form.RunUser = base.RunUser | |||||
| } | |||||
| if len(form.Domain) == 0 { | |||||
| form.Domain = base.Domain | |||||
| } | |||||
| if len(form.AppUrl) == 0 { | |||||
| form.AppUrl = base.AppUrl | |||||
| } | |||||
| auth.AssignForm(form, ctx.Data) | |||||
| if ctx.Req.Method == "GET" { | if ctx.Req.Method == "GET" { | ||||
| ctx.HTML(200, "install") | ctx.HTML(200, "install") | ||||
| return | return | ||||
| } | } | ||||
| if ctx.HasError() { | |||||
| ctx.HTML(200, "install") | |||||
| return | |||||
| } | |||||
| // Pass basic check, now test configuration. | |||||
| // Test database setting. | |||||
| dbTypes := map[string]string{"mysql": "mysql", "pgsql": "postgres", "sqlite": "sqlite3"} | |||||
| models.DbCfg.Type = dbTypes[form.Database] | |||||
| models.DbCfg.Host = form.Host | |||||
| models.DbCfg.User = form.User | |||||
| models.DbCfg.Pwd = form.Passwd | |||||
| models.DbCfg.Name = form.DatabaseName | |||||
| models.DbCfg.SslMode = form.SslMode | |||||
| models.DbCfg.Path = form.DatabasePath | |||||
| if err := models.NewEngine(); err != nil { | |||||
| ctx.RenderWithErr("Database setting is not correct: "+err.Error(), "install", &form) | |||||
| return | |||||
| } | |||||
| // Test repository root path. | |||||
| if err := os.MkdirAll(form.RepoRootPath, os.ModePerm); err != nil { | |||||
| ctx.RenderWithErr("Repository root path is invalid: "+err.Error(), "install", &form) | |||||
| return | |||||
| } | |||||
| // Create admin account. | |||||
| if _, err := models.RegisterUser(&models.User{Name: form.AdminName, Email: form.AdminEmail, Passwd: form.AdminPasswd, | |||||
| IsAdmin: true, IsActive: true}); err != nil { | |||||
| if err != models.ErrUserAlreadyExist { | |||||
| ctx.RenderWithErr("Admin account setting is invalid: "+err.Error(), "install", &form) | |||||
| return | |||||
| } | |||||
| } | |||||
| // Save settings. | |||||
| base.Cfg.SetValue("database", "DB_TYPE", models.DbCfg.Type) | |||||
| base.Cfg.SetValue("database", "HOST", models.DbCfg.Host) | |||||
| base.Cfg.SetValue("database", "NAME", models.DbCfg.Name) | |||||
| base.Cfg.SetValue("database", "USER", models.DbCfg.User) | |||||
| base.Cfg.SetValue("database", "PASSWD", models.DbCfg.Pwd) | |||||
| base.Cfg.SetValue("database", "SSL_MODE", models.DbCfg.SslMode) | |||||
| base.Cfg.SetValue("database", "PATH", models.DbCfg.Path) | |||||
| base.Cfg.SetValue("repository", "ROOT", form.RepoRootPath) | |||||
| base.Cfg.SetValue("", "RUN_USER", form.RunUser) | |||||
| base.Cfg.SetValue("server", "DOMAIN", form.Domain) | |||||
| base.Cfg.SetValue("server", "ROOT_URL", form.AppUrl) | |||||
| if len(form.Host) > 0 { | |||||
| base.Cfg.SetValue("mailer", "ENABLED", "true") | |||||
| base.Cfg.SetValue("mailer", "HOST", form.SmtpHost) | |||||
| base.Cfg.SetValue("mailer", "USER", form.SmtpEmail) | |||||
| base.Cfg.SetValue("mailer", "PASSWD", form.SmtpPasswd) | |||||
| base.Cfg.SetValue("service", "REGISTER_EMAIL_CONFIRM", base.ToStr(form.RegisterConfirm == "on")) | |||||
| base.Cfg.SetValue("service", "ENABLE_NOTIFY_MAIL", base.ToStr(form.MailNotify == "on")) | |||||
| } | |||||
| base.Cfg.SetValue("security", "INSTALL_LOCK", "true") | |||||
| if err := goconfig.SaveConfigFile(base.Cfg, "custom/conf/app.ini"); err != nil { | |||||
| ctx.RenderWithErr("Fail to save configuration: "+err.Error(), "install", &form) | |||||
| return | |||||
| } | |||||
| GlobalInit() | |||||
| log.Info("First-time run install finished!") | |||||
| ctx.Redirect("/user/login") | |||||
| } | } | ||||
| @@ -7,6 +7,7 @@ package repo | |||||
| import ( | import ( | ||||
| "fmt" | "fmt" | ||||
| "net/url" | "net/url" | ||||
| "strings" | |||||
| "github.com/codegangsta/martini" | "github.com/codegangsta/martini" | ||||
| @@ -175,7 +176,7 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) { | |||||
| ctx.Data["Title"] = issue.Name | ctx.Data["Title"] = issue.Name | ||||
| ctx.Data["Issue"] = issue | ctx.Data["Issue"] = issue | ||||
| ctx.Data["Comments"] = comments | ctx.Data["Comments"] = comments | ||||
| ctx.Data["IsIssueOwner"] = issue.PosterId == ctx.User.Id | |||||
| ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner || issue.PosterId == ctx.User.Id | |||||
| ctx.Data["IsRepoToolbarIssues"] = true | ctx.Data["IsRepoToolbarIssues"] = true | ||||
| ctx.Data["IsRepoToolbarIssuesList"] = false | ctx.Data["IsRepoToolbarIssuesList"] = false | ||||
| ctx.HTML(200, "issue/view") | ctx.HTML(200, "issue/view") | ||||
| @@ -225,24 +226,17 @@ func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat | |||||
| } | } | ||||
| func Comment(ctx *middleware.Context, params martini.Params) { | func Comment(ctx *middleware.Context, params martini.Params) { | ||||
| fmt.Println(ctx.Query("change_status")) | |||||
| if !ctx.Repo.IsValid { | if !ctx.Repo.IsValid { | ||||
| ctx.Handle(404, "issue.Comment(invalid repo):", nil) | ctx.Handle(404, "issue.Comment(invalid repo):", nil) | ||||
| } | } | ||||
| index, err := base.StrTo(ctx.Query("issueIndex")).Int() | |||||
| index, err := base.StrTo(ctx.Query("issueIndex")).Int64() | |||||
| if err != nil { | if err != nil { | ||||
| ctx.Handle(404, "issue.Comment", err) | |||||
| ctx.Handle(404, "issue.Comment(get index)", err) | |||||
| return | return | ||||
| } | } | ||||
| content := ctx.Query("content") | |||||
| if len(content) == 0 { | |||||
| ctx.Redirect(fmt.Sprintf("/%s/%s/issues/%d", ctx.User.Name, ctx.Repo.Repository.Name, index)) | |||||
| return | |||||
| } | |||||
| issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, int64(index)) | |||||
| issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, index) | |||||
| if err != nil { | if err != nil { | ||||
| if err == models.ErrIssueNotExist { | if err == models.ErrIssueNotExist { | ||||
| ctx.Handle(404, "issue.Comment", err) | ctx.Handle(404, "issue.Comment", err) | ||||
| @@ -252,16 +246,46 @@ func Comment(ctx *middleware.Context, params martini.Params) { | |||||
| return | return | ||||
| } | } | ||||
| switch params["action"] { | |||||
| case "new": | |||||
| if err = models.CreateComment(ctx.User.Id, issue.Id, 0, 0, content); err != nil { | |||||
| ctx.Handle(500, "issue.Comment(create comment)", err) | |||||
| // Check if issue owner changes the status of issue. | |||||
| var newStatus string | |||||
| if ctx.Repo.IsOwner || issue.PosterId == ctx.User.Id { | |||||
| newStatus = ctx.Query("change_status") | |||||
| } | |||||
| if len(newStatus) > 0 { | |||||
| if (strings.Contains(newStatus, "Reopen") && issue.IsClosed) || | |||||
| (strings.Contains(newStatus, "Close") && !issue.IsClosed) { | |||||
| issue.IsClosed = !issue.IsClosed | |||||
| if err = models.UpdateIssue(issue); err != nil { | |||||
| ctx.Handle(200, "issue.Comment(update issue status)", err) | |||||
| return | |||||
| } | |||||
| cmtType := models.IT_CLOSE | |||||
| if !issue.IsClosed { | |||||
| cmtType = models.IT_REOPEN | |||||
| } | |||||
| if err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, cmtType, ""); err != nil { | |||||
| ctx.Handle(200, "issue.Comment(create status change comment)", err) | |||||
| return | |||||
| } | |||||
| log.Trace("%s Issue(%d) status changed: %v", ctx.Req.RequestURI, issue.Id, !issue.IsClosed) | |||||
| } | |||||
| } | |||||
| content := ctx.Query("content") | |||||
| if len(content) > 0 { | |||||
| switch params["action"] { | |||||
| case "new": | |||||
| if err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, models.IT_PLAIN, content); err != nil { | |||||
| ctx.Handle(500, "issue.Comment(create comment)", err) | |||||
| return | |||||
| } | |||||
| log.Trace("%s Comment created: %d", ctx.Req.RequestURI, issue.Id) | |||||
| default: | |||||
| ctx.Handle(404, "issue.Comment", err) | |||||
| return | return | ||||
| } | } | ||||
| log.Trace("%s Comment created: %d", ctx.Req.RequestURI, issue.Id) | |||||
| default: | |||||
| ctx.Handle(404, "issue.Comment", err) | |||||
| return | |||||
| } | } | ||||
| ctx.Redirect(fmt.Sprintf("/%s/%s/issues/%d", ctx.User.Name, ctx.Repo.Repository.Name, index)) | ctx.Redirect(fmt.Sprintf("/%s/%s/issues/%d", ctx.User.Name, ctx.Repo.Repository.Name, index)) | ||||
| @@ -120,7 +120,7 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) { | |||||
| return | return | ||||
| } | } | ||||
| if hasErr, ok := ctx.Data["HasError"]; ok && hasErr.(bool) { | |||||
| if ctx.HasError() { | |||||
| ctx.HTML(200, "user/signin") | ctx.HTML(200, "user/signin") | ||||
| return | return | ||||
| } | } | ||||
| @@ -308,17 +308,19 @@ func Issues(ctx *middleware.Context) { | |||||
| showRepos := make([]models.Repository, 0, len(repos)) | showRepos := make([]models.Repository, 0, len(repos)) | ||||
| var closedIssueCount, createdByCount int | |||||
| isShowClosed := ctx.Query("state") == "closed" | |||||
| var closedIssueCount, createdByCount, allIssueCount int | |||||
| // Get all issues. | // Get all issues. | ||||
| allIssues := make([]models.Issue, 0, 5*len(repos)) | allIssues := make([]models.Issue, 0, 5*len(repos)) | ||||
| for i, repo := range repos { | for i, repo := range repos { | ||||
| issues, err := models.GetIssues(0, repo.Id, posterId, 0, page, false, false, "", "") | |||||
| issues, err := models.GetIssues(0, repo.Id, posterId, 0, page, isShowClosed, false, "", "") | |||||
| if err != nil { | if err != nil { | ||||
| ctx.Handle(200, "user.Issues(get issues)", err) | ctx.Handle(200, "user.Issues(get issues)", err) | ||||
| return | return | ||||
| } | } | ||||
| allIssueCount += repo.NumIssues | |||||
| closedIssueCount += repo.NumClosedIssues | closedIssueCount += repo.NumClosedIssues | ||||
| // Set repository information to issues. | // Set repository information to issues. | ||||
| @@ -330,12 +332,10 @@ func Issues(ctx *middleware.Context) { | |||||
| repos[i].NumOpenIssues = repo.NumIssues - repo.NumClosedIssues | repos[i].NumOpenIssues = repo.NumIssues - repo.NumClosedIssues | ||||
| if repos[i].NumOpenIssues > 0 { | if repos[i].NumOpenIssues > 0 { | ||||
| showRepos = append(showRepos, repos[i]) | showRepos = append(showRepos, repos[i]) | ||||
| } | } | ||||
| } | } | ||||
| showIssues := make([]models.Issue, 0, len(allIssues)) | showIssues := make([]models.Issue, 0, len(allIssues)) | ||||
| isShowClosed := ctx.Query("state") == "closed" | |||||
| ctx.Data["IsShowClosed"] = isShowClosed | ctx.Data["IsShowClosed"] = isShowClosed | ||||
| // Get posters and filter issues. | // Get posters and filter issues. | ||||
| @@ -361,9 +361,9 @@ func Issues(ctx *middleware.Context) { | |||||
| ctx.Data["Repos"] = showRepos | ctx.Data["Repos"] = showRepos | ||||
| ctx.Data["Issues"] = showIssues | ctx.Data["Issues"] = showIssues | ||||
| ctx.Data["AllIssueCount"] = len(allIssues) | |||||
| ctx.Data["AllIssueCount"] = allIssueCount | |||||
| ctx.Data["ClosedIssueCount"] = closedIssueCount | ctx.Data["ClosedIssueCount"] = closedIssueCount | ||||
| ctx.Data["OpenIssueCount"] = len(allIssues) - closedIssueCount | |||||
| ctx.Data["OpenIssueCount"] = allIssueCount - closedIssueCount | |||||
| ctx.Data["CreatedByCount"] = createdByCount | ctx.Data["CreatedByCount"] = createdByCount | ||||
| ctx.HTML(200, "issue/user") | ctx.HTML(200, "issue/user") | ||||
| } | } | ||||
| @@ -3,9 +3,8 @@ | |||||
| <form action="/install" method="post" class="form-horizontal card" id="install-card"> | <form action="/install" method="post" class="form-horizontal card" id="install-card"> | ||||
| {{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} | ||||
| <h3>Install Steps For First-time Run</h3> | <h3>Install Steps For First-time Run</h3> | ||||
| <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> | <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> | ||||
| <p class="help-block text-center">Gogs requires MySQL or PostgreSQL based on your choice</p> | |||||
| <p class="help-block text-center">Gogs requires MySQL or PostgreSQL, SQLite3 only available for official binary version</p> | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-3 control-label">Database Type: </label> | <label class="col-md-3 control-label">Database Type: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| @@ -16,26 +15,28 @@ | |||||
| </select> | </select> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="server-sql"> | <div class="server-sql"> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-3 control-label">Host: </label> | <label class="col-md-3 control-label">Host: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="host" class="form-control" placeholder="Type database server host, leave blank to keep default" value="{{.DbCfg.Host}}" required="required"> | |||||
| <input name="host" class="form-control" placeholder="Type database server host" value="{{.host}}" required="required"> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-3 control-label">User: </label> | <label class="col-md-3 control-label">User: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="user" class="form-control" placeholder="Type database username" required="required" value="{{.DbCfg.User}}"> | |||||
| <input name="user" class="form-control" placeholder="Type database username" required="required" value="{{.user}}"> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-3 control-label">Password: </label> | <label class="col-md-3 control-label">Password: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="passwd" type="password" class="form-control" placeholder="Type database password" required="required" value="{{.DbCfg.Pwd}}"> | |||||
| <input name="passwd" type="password" class="form-control" placeholder="Type database password" required="required" value="{{.passwd}}"> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -43,7 +44,7 @@ | |||||
| <label class="col-md-3 control-label">Database Name: </label> | <label class="col-md-3 control-label">Database Name: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="database_name" type="text" class="form-control" placeholder="Type mysql database name" value="{{.DbCfg.Name}}" required="required"> | |||||
| <input name="database_name" type="text" class="form-control" placeholder="Type mysql database name" value="{{.database_name}}" required="required"> | |||||
| <p class="help-block">Recommend use INNODB engine with utf8_general_ci charset.</p> | <p class="help-block">Recommend use INNODB engine with utf8_general_ci charset.</p> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -59,12 +60,13 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="sqlite-setting hide"> | <div class="sqlite-setting hide"> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-3 control-label">Path: </label> | <label class="col-md-3 control-label">Path: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="database_path" class="form-control" placeholder="Type sqlite3 file path" value="{{.DbCfg.Path}}"> | |||||
| <input name="database_path" class="form-control" placeholder="Type sqlite3 file path" value="{{.database_path}}"> | |||||
| <p class="help-block">The file path of SQLite3 database.</p> | <p class="help-block">The file path of SQLite3 database.</p> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -73,12 +75,11 @@ | |||||
| <hr/> | <hr/> | ||||
| <p class="help-block text-center">General Settings of Gogs</p> | <p class="help-block text-center">General Settings of Gogs</p> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-3 control-label">Repository Path: </label> | <label class="col-md-3 control-label">Repository Path: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="repo_path" type="text" class="form-control" placeholder="Type your repository directory" value="{{.RepoRootPath}}" required="required"> | |||||
| <input name="repo_path" type="text" class="form-control" placeholder="Type your repository directory" value="{{.repo_path}}" required="required"> | |||||
| <p class="help-block">The git copy of each repository is saved in this directory.</p> | <p class="help-block">The git copy of each repository is saved in this directory.</p> | ||||
| </div> | </div> | ||||
| @@ -88,16 +89,25 @@ | |||||
| <label class="col-md-3 control-label">Run User: </label> | <label class="col-md-3 control-label">Run User: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="run_user" type="text" class="form-control" placeholder="Type system user name" value="{{.RunUser}}" required="required"> | |||||
| <input name="run_user" type="text" class="form-control" placeholder="Type system user name" value="{{.run_user}}" required="required"> | |||||
| <p class="help-block">The user has access to visit and run Gogs.</p> | <p class="help-block">The user has access to visit and run Gogs.</p> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | |||||
| <label class="col-md-3 control-label">Domain: </label> | |||||
| <div class="col-md-8"> | |||||
| <input name="domain" type="text" class="form-control" placeholder="Type your domain name" value="{{.domain}}" required="required"> | |||||
| <p class="help-block">This affects SSH clone URL.</p> | |||||
| </div> | |||||
| </div> | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-3 control-label">App URL: </label> | <label class="col-md-3 control-label">App URL: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="app_url" type="text" class="form-control" placeholder="Type app root URL " value="{{.AppUrl}}" required="required"> | |||||
| <input name="app_url" type="text" class="form-control" placeholder="Type app root URL" value="{{.app_url}}" required="required"> | |||||
| <p class="help-block">This affects HTTP/HTTPS clone URL and somewhere in e-mail.</p> | <p class="help-block">This affects HTTP/HTTPS clone URL and somewhere in e-mail.</p> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -105,35 +115,30 @@ | |||||
| <hr/> | <hr/> | ||||
| <p class="help-block text-center">Admin Account Settings</p> | <p class="help-block text-center">Admin Account Settings</p> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-3 control-label">Username: </label> | <label class="col-md-3 control-label">Username: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="admin_name" type="text" class="form-control" placeholder="Type admin user name" value="admin" required="required"> | |||||
| <input name="admin_name" type="text" class="form-control" placeholder="Type admin user name" value="{{.admin_name}}" required="required"> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | |||||
| <div class="form-group {{if .Err_AdminPasswd}}has-error has-feedback{{end}}"> | |||||
| <label class="col-md-3 control-label">Password: </label> | <label class="col-md-3 control-label">Password: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="admin_pwd" type="password" class="form-control" placeholder="Type admin user password" required="required"> | |||||
| <input name="admin_pwd" type="password" class="form-control" placeholder="Type admin user password" value="{{.admin_pwd}}" required="required"> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | |||||
| <div class="form-group {{if .Err_AdminEmail}}has-error has-feedback{{end}}"> | |||||
| <label class="col-md-3 control-label">E-mail: </label> | <label class="col-md-3 control-label">E-mail: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="admin_email" type="text" class="form-control" placeholder="Type admin user e-mail" required="required"> | |||||
| <input name="admin_email" type="text" class="form-control" placeholder="Type admin user e-mail" value="{{.admin_email}}" required="required"> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <hr/> | <hr/> | ||||
| <div class="form-group text-center"> | <div class="form-group text-center"> | ||||
| <!-- <button class="btn btn-primary btn-lg">Test Configuration</button> --> | |||||
| <button class="btn btn-danger btn-lg">Install Gogs</button> | <button class="btn btn-danger btn-lg">Install Gogs</button> | ||||
| <button class="btn btn-default btn-sm" type="button" data-toggle="modal" data-target="#advance-options-modal"> | <button class="btn btn-default btn-sm" type="button" data-toggle="modal" data-target="#advance-options-modal"> | ||||
| Advanced Options | Advanced Options | ||||
| @@ -151,21 +156,21 @@ | |||||
| <label class="col-md-3 control-label">SMTP Host: </label> | <label class="col-md-3 control-label">SMTP Host: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="smtp_host" type="text" class="form-control" placeholder="Type SMTP host address"> | |||||
| <input name="smtp_host" type="text" class="form-control" placeholder="Type SMTP host address" value="{{.smtp_host}}"> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-3 control-label">Email: </label> | <label class="col-md-3 control-label">Email: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="mailer_user" type="text" class="form-control" placeholder="Type SMTP user e-mail address"> | |||||
| <input name="mailer_user" type="text" class="form-control" placeholder="Type SMTP user e-mail address" value="{{.mailer_user}}"> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-3 control-label">Password: </label> | <label class="col-md-3 control-label">Password: </label> | ||||
| <div class="col-md-8"> | <div class="col-md-8"> | ||||
| <input name="mailer_pwd" type="password" class="form-control" placeholder="Type SMTP user password"> | |||||
| <input name="mailer_pwd" type="password" class="form-control" placeholder="Type SMTP user password" value="{{.mailer_pwd}}"> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <hr/> | <hr/> | ||||
| @@ -175,7 +180,7 @@ | |||||
| <div class="col-md-offset-3 col-md-7"> | <div class="col-md-offset-3 col-md-7"> | ||||
| <div class="checkbox"> | <div class="checkbox"> | ||||
| <label> | <label> | ||||
| <input name="register_confirm" type="checkbox"> | |||||
| <input name="register_confirm" type="checkbox" {{if .register_confirm}}checked{{end}}> | |||||
| <strong>Enable Register Confirmation</strong> | <strong>Enable Register Confirmation</strong> | ||||
| </label> | </label> | ||||
| </div> | </div> | ||||
| @@ -186,7 +191,7 @@ | |||||
| <div class="col-md-offset-3 col-md-7"> | <div class="col-md-offset-3 col-md-7"> | ||||
| <div class="checkbox"> | <div class="checkbox"> | ||||
| <label> | <label> | ||||
| <input name="mail_notify" type="checkbox"> | |||||
| <input name="mail_notify" type="checkbox" {{if .mail_notify}}checked{{end}}> | |||||
| <strong>Enable Mail Notification</strong> | <strong>Enable Mail Notification</strong> | ||||
| </label> | </label> | ||||
| </div> | </div> | ||||
| @@ -30,37 +30,37 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{range .Comments}} | {{range .Comments}} | ||||
| <div class="issue-child" id="issue-comment-{{.Id}}"> | |||||
| <a class="user pull-left" href="/user/{{.Poster.Name}}"><img class="avatar" src="{{.Poster.AvatarLink}}" alt=""/></a> | |||||
| <div class="issue-content panel panel-default"> | |||||
| <div class="panel-heading"> | |||||
| <a href="/user/{{.Poster.Name}}" class="user">{{.Poster.Name}}</a> commented <span class="time">{{TimeSince .Created}}</span> | |||||
| <!-- <a class="issue-comment-del pull-right issue-action" href="#" title="Edit Comment"><i class="fa fa-times-circle"></i></a> | |||||
| <a class="issue-comment-edit pull-right issue-action" href="#" title="Remove Comment" data-url="{remove-link}"><i class="fa fa-edit"></i></a> --> | |||||
| <span class="role label label-default pull-right">Owner</span> | |||||
| </div> | |||||
| <div class="panel-body markdown"> | |||||
| {{str2html .Content}} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{end}} | |||||
| <!-- <div class="issue-child issue-closed"> | |||||
| <a class="user pull-left" href="{user.link}"><img class="avatar" src="{user.avatar}" alt=""/></a> | |||||
| {{if eq .Type 0}} | |||||
| <div class="issue-child" id="issue-comment-{{.Id}}"> | |||||
| <a class="user pull-left" href="/user/{{.Poster.Name}}"><img class="avatar" src="{{.Poster.AvatarLink}}" alt=""/></a> | |||||
| <div class="issue-content panel panel-default"> | |||||
| <div class="panel-heading"> | |||||
| <a href="/user/{{.Poster.Name}}" class="user">{{.Poster.Name}}</a> commented <span class="time">{{TimeSince .Created}}</span> | |||||
| <!-- <a class="issue-comment-del pull-right issue-action" href="#" title="Edit Comment"><i class="fa fa-times-circle"></i></a> | |||||
| <a class="issue-comment-edit pull-right issue-action" href="#" title="Remove Comment" data-url="{remove-link}"><i class="fa fa-edit"></i></a> --> | |||||
| <span class="role label label-default pull-right">Owner</span> | |||||
| </div> | |||||
| <div class="panel-body markdown"> | |||||
| {{str2html .Content}} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{else if eq .Type 1}} | |||||
| <div class="issue-child issue-opened"> | |||||
| <a class="user pull-left" href="/user/{{.Poster.Name}}"><img class="avatar" src="{{.Poster.AvatarLink}}" alt="" /></a> | |||||
| <div class="issue-content"> | |||||
| <a class="user pull-left" href="/user/{{.Poster.Name}}">{{.Poster.Name}}</a> <span class="label label-success">Reopened</span> this issue <span class="time">{{TimeSince .Created}}</span> | |||||
| </div> | |||||
| </div> | |||||
| {{else if eq .Type 2}} | |||||
| <div class="issue-child issue-closed"> | |||||
| <a class="user pull-left" href="/user/{{.Poster.Name}}"><img class="avatar" src="{{.Poster.AvatarLink}}" alt=""/></a> | |||||
| <div class="issue-content"> | <div class="issue-content"> | ||||
| <a class="user pull-left" href="{user.link}">{user.name}</a> | |||||
| <span class="btn btn-danger">Closed</span> this | |||||
| <span class="time">{close.time}</span> | |||||
| <a class="user pull-left" href="/user/{{.Poster.Name}}">{{.Poster.Name}}</a> <span class="label label-danger">Closed</span> this issue <span class="time">{{TimeSince .Created}}</span> | |||||
| </div> | </div> | ||||
| </div> | |||||
| <div class="issue-child issue-opened"> | |||||
| <a class="user pull-left" href="{user.link}"><img class="avatar" src="{user.avatar}" alt=""/></a> | |||||
| <div class="issue-content"> | |||||
| <a class="user pull-left" href="{user.link}">{user.name}</a> | |||||
| <span class="btn btn-success">Reopened</span> this | |||||
| <span class="time">{close.time}</span> | |||||
| </div> | |||||
| </div> --> | |||||
| </div> | |||||
| {{end}} | |||||
| {{end}} | |||||
| <hr class="issue-line"/> | <hr class="issue-line"/> | ||||
| {{if .SignedUser}}<div class="issue-child issue-reply"> | {{if .SignedUser}}<div class="issue-child issue-reply"> | ||||
| <a class="user pull-left" href="/user/{{.SignedUser.Name}}"><img class="avatar" src="{{.SignedUser.AvatarLink}}" alt=""/></a> | <a class="user pull-left" href="/user/{{.SignedUser.Name}}"><img class="avatar" src="{{.SignedUser.AvatarLink}}" alt=""/></a> | ||||
| @@ -68,8 +68,7 @@ | |||||
| {{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} | ||||
| <div class="panel-body"> | <div class="panel-body"> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <div class="md-help pull-right"><!-- todo help link --> | |||||
| Content with <a href="https://help.github.com/articles/markdown-basics">Markdown</a> | |||||
| <div class="md-help pull-right">Content with <a href="https://help.github.com/articles/markdown-basics">Markdown</a> | |||||
| </div> | </div> | ||||
| <ul class="nav nav-tabs" data-init="tabs"> | <ul class="nav nav-tabs" data-init="tabs"> | ||||
| <li class="active issue-write"><a href="#issue-textarea" data-toggle="tab">Write</a></li> | <li class="active issue-write"><a href="#issue-textarea" data-toggle="tab">Write</a></li> | ||||
| @@ -82,13 +81,13 @@ | |||||
| <textarea class="form-control" name="content" id="issue-reply-content" rows="10" placeholder="Write some content" data-ajax-rel="issue-preview" data-ajax-val="val" data-ajax-field="content">{{.content}}</textarea> | <textarea class="form-control" name="content" id="issue-reply-content" rows="10" placeholder="Write some content" data-ajax-rel="issue-preview" data-ajax-val="val" data-ajax-field="content">{{.content}}</textarea> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="tab-pane issue-preview-content" id="issue-preview">loading...</div> | |||||
| <div class="tab-pane issue-preview-content" id="issue-preview">Loading...</div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="text-right"> | <div class="text-right"> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| {{if .Issue.IsClosed}} | {{if .Issue.IsClosed}} | ||||
| <input type="submit" class="btn-default btn issue-open" id="issue-open-btn" data-origin="Re-Open" data-text="Re-Open & Comment" name="change_status" value="Reopen"/>{{else}} | |||||
| <input type="submit" class="btn-default btn issue-open" id="issue-open-btn" data-origin="Reopen" data-text="Reopen & Comment" name="change_status" value="Reopen"/>{{else}} | |||||
| <input type="submit" class="btn-default btn issue-close" id="issue-close-btn" data-origin="Close" data-text="Close & Comment" name="change_status" value="Close"/>{{end}} | <input type="submit" class="btn-default btn issue-close" id="issue-close-btn" data-origin="Close" data-text="Close & Comment" name="change_status" value="Close"/>{{end}} | ||||
| <button class="btn-success btn" id="issue-reply-btn">Comment</button> | <button class="btn-success btn" id="issue-reply-btn">Comment</button> | ||||
| </div> | </div> | ||||
| @@ -8,19 +8,16 @@ import ( | |||||
| "fmt" | "fmt" | ||||
| "html/template" | "html/template" | ||||
| "net/http" | "net/http" | ||||
| "strings" | |||||
| "github.com/codegangsta/cli" | "github.com/codegangsta/cli" | ||||
| "github.com/codegangsta/martini" | "github.com/codegangsta/martini" | ||||
| "github.com/gogits/binding" | "github.com/gogits/binding" | ||||
| "github.com/gogits/gogs/models" | |||||
| "github.com/gogits/gogs/modules/auth" | "github.com/gogits/gogs/modules/auth" | ||||
| "github.com/gogits/gogs/modules/avatar" | "github.com/gogits/gogs/modules/avatar" | ||||
| "github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
| "github.com/gogits/gogs/modules/log" | "github.com/gogits/gogs/modules/log" | ||||
| "github.com/gogits/gogs/modules/mailer" | |||||
| "github.com/gogits/gogs/modules/middleware" | "github.com/gogits/gogs/modules/middleware" | ||||
| "github.com/gogits/gogs/routers" | "github.com/gogits/gogs/routers" | ||||
| "github.com/gogits/gogs/routers/admin" | "github.com/gogits/gogs/routers/admin" | ||||
| @@ -40,27 +37,6 @@ and it takes care of all the other things for you`, | |||||
| Flags: []cli.Flag{}, | 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") { | |||||
| case "prod": | |||||
| martini.Env = martini.Prod | |||||
| case "test": | |||||
| martini.Env = martini.Test | |||||
| } | |||||
| log.Info("Run Mode: %s", strings.Title(martini.Env)) | |||||
| } | |||||
| func newMartini() *martini.ClassicMartini { | func newMartini() *martini.ClassicMartini { | ||||
| r := martini.NewRouter() | r := martini.NewRouter() | ||||
| m := martini.New() | m := martini.New() | ||||
| @@ -74,9 +50,7 @@ func newMartini() *martini.ClassicMartini { | |||||
| func runWeb(*cli.Context) { | func runWeb(*cli.Context) { | ||||
| fmt.Println("Server is running...") | fmt.Println("Server is running...") | ||||
| globalInit() | |||||
| base.NewServices() | |||||
| checkRunMode() | |||||
| routers.GlobalInit() | |||||
| log.Info("%s %s", base.AppName, base.AppVer) | log.Info("%s %s", base.AppName, base.AppVer) | ||||
| m := newMartini() | m := newMartini() | ||||