Pārlūkot izejas kodu

Finish close and reopen issue, install page, ready going to test stage of v0.2.0

Unknown 11 gadi atpakaļ
vecāks
revīzija
107a1eadac
15 mainītis faili ar 314 papildinājumiem un 160 dzēšanām
  1. 1 1
      README.md
  2. 1 1
      README_ZH.md
  3. 1 1
      gogs.go
  4. 30 6
      models/issue.go
  5. 11 13
      models/models.go
  6. 10 15
      models/repo.go
  7. 1 0
      modules/auth/auth.go
  8. 4 3
      modules/base/conf.go
  9. 3 3
      public/css/gogs.css
  10. 137 4
      routers/install.go
  11. 43 19
      routers/repo/issue.go
  12. 7 7
      routers/user/user.go
  13. 32 27
      templates/install.tmpl
  14. 32 33
      templates/issue/view.tmpl
  15. 1 27
      web.go

+ 1 - 1
README.md

@@ -5,7 +5,7 @@ Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language
 
 ![Demo](http://gowalker.org/public/gogs_demo.gif)
 
-##### 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.
 

+ 1 - 1
README_ZH.md

@@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。
 
 ![Demo](http://gowalker.org/public/gogs_demo.gif)
 
-##### 当前版本:0.1.9 Alpha
+##### 当前版本:0.2.0 Alpha
 
 ## 开发目的
 

+ 1 - 1
gogs.go

@@ -19,7 +19,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.9.0329 Alpha"
+const APP_VER = "0.2.0.0329 Alpha"
 
 func init() {
 	base.AppVer = APP_VER

+ 30 - 6
models/issue.go

@@ -170,9 +170,17 @@ type Milestone struct {
 	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.
 type Comment struct {
 	Id       int64
+	Type     int
 	PosterId int64
 	Poster   *User `xorm:"-"`
 	IssueId  int64
@@ -183,21 +191,37 @@ type Comment struct {
 }
 
 // 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()
 	defer sess.Close()
 	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 {
 		sess.Rollback()
 		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()
 }

+ 11 - 13
models/models.go

@@ -34,8 +34,7 @@ func LoadModelsConfig() {
 	DbCfg.Path = base.Cfg.MustValue("database", "PATH", "data/gogs.db")
 }
 
-func SetEngine() {
-	var err error
+func SetEngine() (err error) {
 	switch DbCfg.Type {
 	case "mysql":
 		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)
 		orm, err = xorm.NewEngine("sqlite3", DbCfg.Path)
 	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 {
-		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,
@@ -62,20 +59,21 @@ func SetEngine() {
 	//orm.ShowErr = true
 	f, err := os.Create("xorm.log")
 	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.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 {
-		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 {

+ 10 - 15
models/repo.go

@@ -11,7 +11,6 @@ import (
 	"os"
 	"os/exec"
 	"path/filepath"
-	"regexp"
 	"strings"
 	"time"
 	"unicode/utf8"
@@ -59,15 +58,6 @@ func NewRepoContext() {
 			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.
@@ -105,15 +95,20 @@ func IsRepositoryExist(user *User, repoName string) (bool, error) {
 }
 
 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.
 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
 		}
 	}

+ 1 - 0
modules/auth/auth.go

@@ -172,6 +172,7 @@ type InstallForm struct {
 	DatabasePath    string `form:"database_path"`
 	RepoRootPath    string `form:"repo_path"`
 	RunUser         string `form:"run_user"`
+	Domain          string `form:"domain"`
 	AppUrl          string `form:"app_url"`
 	AdminName       string `form:"admin_name" binding:"Required"`
 	AdminPasswd     string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(30)"`

+ 4 - 3
modules/base/conf.go

@@ -272,18 +272,19 @@ func NewConfigContext() {
 	Domain = Cfg.MustValue("server", "DOMAIN")
 	SecretKey = Cfg.MustValue("security", "SECRET_KEY")
 
+	InstallLock = Cfg.MustBool("security", "INSTALL_LOCK", false)
+
 	RunUser = Cfg.MustValue("", "RUN_USER")
 	curUser := os.Getenv("USERNAME")
 	if len(curUser) == 0 {
 		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)
 		os.Exit(2)
 	}
 
-	InstallLock = Cfg.MustBool("security", "INSTALL_LOCK", false)
-
 	LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS")
 	CookieUserName = Cfg.MustValue("security", "COOKIE_USERNAME")
 	CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME")

+ 3 - 3
public/css/gogs.css

@@ -1197,13 +1197,13 @@ html, body {
     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;
     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;
 }
 

+ 137 - 4
routers/install.go

@@ -6,13 +6,46 @@ package routers
 
 import (
 	"errors"
+	"os"
+	"strings"
+
+	"github.com/Unknwon/goconfig"
+	"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/mailer"
 	"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) {
 	if base.InstallLock {
 		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["DbCfg"] = models.DbCfg
-	ctx.Data["RepoRootPath"] = base.RepoRootPath
-	ctx.Data["RunUser"] = base.RunUser
-	ctx.Data["AppUrl"] = base.AppUrl
 	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" {
 		ctx.HTML(200, "install")
 		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")
 }

+ 43 - 19
routers/repo/issue.go

@@ -7,6 +7,7 @@ package repo
 import (
 	"fmt"
 	"net/url"
+	"strings"
 
 	"github.com/codegangsta/martini"
 
@@ -175,7 +176,7 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) {
 	ctx.Data["Title"] = issue.Name
 	ctx.Data["Issue"] = issue
 	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["IsRepoToolbarIssuesList"] = false
 	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) {
-	fmt.Println(ctx.Query("change_status"))
 	if !ctx.Repo.IsValid {
 		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 {
-		ctx.Handle(404, "issue.Comment", err)
+		ctx.Handle(404, "issue.Comment(get index)", err)
 		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 == models.ErrIssueNotExist {
 			ctx.Handle(404, "issue.Comment", err)
@@ -252,16 +246,46 @@ func Comment(ctx *middleware.Context, params martini.Params) {
 		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
 		}
-		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))

+ 7 - 7
routers/user/user.go

@@ -120,7 +120,7 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) {
 		return
 	}
 
-	if hasErr, ok := ctx.Data["HasError"]; ok && hasErr.(bool) {
+	if ctx.HasError() {
 		ctx.HTML(200, "user/signin")
 		return
 	}
@@ -308,17 +308,19 @@ func Issues(ctx *middleware.Context) {
 
 	showRepos := make([]models.Repository, 0, len(repos))
 
-	var closedIssueCount, createdByCount int
+	isShowClosed := ctx.Query("state") == "closed"
+	var closedIssueCount, createdByCount, allIssueCount int
 
 	// Get all issues.
 	allIssues := make([]models.Issue, 0, 5*len(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 {
 			ctx.Handle(200, "user.Issues(get issues)", err)
 			return
 		}
 
+		allIssueCount += repo.NumIssues
 		closedIssueCount += repo.NumClosedIssues
 
 		// Set repository information to issues.
@@ -330,12 +332,10 @@ func Issues(ctx *middleware.Context) {
 		repos[i].NumOpenIssues = repo.NumIssues - repo.NumClosedIssues
 		if repos[i].NumOpenIssues > 0 {
 			showRepos = append(showRepos, repos[i])
-
 		}
 	}
 
 	showIssues := make([]models.Issue, 0, len(allIssues))
-	isShowClosed := ctx.Query("state") == "closed"
 	ctx.Data["IsShowClosed"] = isShowClosed
 
 	// Get posters and filter issues.
@@ -361,9 +361,9 @@ func Issues(ctx *middleware.Context) {
 
 	ctx.Data["Repos"] = showRepos
 	ctx.Data["Issues"] = showIssues
-	ctx.Data["AllIssueCount"] = len(allIssues)
+	ctx.Data["AllIssueCount"] = allIssueCount
 	ctx.Data["ClosedIssueCount"] = closedIssueCount
-	ctx.Data["OpenIssueCount"] = len(allIssues) - closedIssueCount
+	ctx.Data["OpenIssueCount"] = allIssueCount - closedIssueCount
 	ctx.Data["CreatedByCount"] = createdByCount
 	ctx.HTML(200, "issue/user")
 }

+ 32 - 27
templates/install.tmpl

@@ -3,9 +3,8 @@
     <form action="/install" method="post" class="form-horizontal card" id="install-card">
         {{.CsrfTokenHtml}}
         <h3>Install Steps For First-time Run</h3>
-
         <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">
             <label class="col-md-3 control-label">Database Type: </label>
             <div class="col-md-8">
@@ -16,26 +15,28 @@
                 </select>
             </div>
         </div>
+
         <div class="server-sql">
             <div class="form-group">
                 <label class="col-md-3 control-label">Host: </label>
-
                 <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 class="form-group">
                 <label class="col-md-3 control-label">User: </label>
 
                 <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 class="form-group">
                 <label class="col-md-3 control-label">Password: </label>
 
                 <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>
 
@@ -43,7 +44,7 @@
                 <label class="col-md-3 control-label">Database Name: </label>
 
                 <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>
                 </div>
             </div>
@@ -59,12 +60,13 @@
                 </div>
             </div>
         </div>
+
         <div class="sqlite-setting hide">
             <div class="form-group">
                 <label class="col-md-3 control-label">Path: </label>
 
                 <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>
                 </div>
             </div>
@@ -73,12 +75,11 @@
         <hr/>
 
         <p class="help-block text-center">General Settings of Gogs</p>
-
         <div class="form-group">
             <label class="col-md-3 control-label">Repository Path: </label>
 
             <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>
             </div>
@@ -88,16 +89,25 @@
             <label class="col-md-3 control-label">Run User: </label>
 
             <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>
             </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">
             <label class="col-md-3 control-label">App URL: </label>
 
             <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>
             </div>
         </div>
@@ -105,35 +115,30 @@
         <hr/>
 
         <p class="help-block text-center">Admin Account Settings</p>
-
         <div class="form-group">
             <label class="col-md-3 control-label">Username: </label>
-
             <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 class="form-group">
+        <div class="form-group {{if .Err_AdminPasswd}}has-error has-feedback{{end}}">
             <label class="col-md-3 control-label">Password: </label>
-
             <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 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>
-
             <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>
 
         <hr/>
 
         <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-default btn-sm" type="button" data-toggle="modal" data-target="#advance-options-modal">
                 Advanced Options
@@ -151,21 +156,21 @@
                             <label class="col-md-3 control-label">SMTP Host: </label>
 
                             <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 class="form-group">
                             <label class="col-md-3 control-label">Email: </label>
 
                             <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 class="form-group">
                             <label class="col-md-3 control-label">Password: </label>
 
                             <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>
                         <hr/>
@@ -175,7 +180,7 @@
                             <div class="col-md-offset-3 col-md-7">
                                 <div class="checkbox">
                                     <label>
-                                        <input name="register_confirm" type="checkbox">
+                                        <input name="register_confirm" type="checkbox" {{if .register_confirm}}checked{{end}}>
                                         <strong>Enable Register Confirmation</strong>
                                     </label>
                                 </div>
@@ -186,7 +191,7 @@
                             <div class="col-md-offset-3 col-md-7">
                                 <div class="checkbox">
                                     <label>
-                                        <input name="mail_notify" type="checkbox">
+                                        <input name="mail_notify" type="checkbox" {{if .mail_notify}}checked{{end}}>
                                         <strong>Enable Mail Notification</strong>
                                     </label>
                                 </div>

+ 32 - 33
templates/issue/view.tmpl

@@ -30,37 +30,37 @@
                    </div>
                </div>
                {{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">
-                        <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 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"/>
                 {{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>
@@ -68,8 +68,7 @@
                         {{.CsrfTokenHtml}}
                         <div class="panel-body">
                             <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>
                                 <ul class="nav nav-tabs" data-init="tabs">
                                     <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>
                                         </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 class="text-right">
                                 <div class="form-group">
                                     {{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}}&nbsp;&nbsp;
                                     <button class="btn-success btn" id="issue-reply-btn">Comment</button>
                                 </div>

+ 1 - 27
web.go

@@ -8,19 +8,16 @@ import (
 	"fmt"
 	"html/template"
 	"net/http"
-	"strings"
 
 	"github.com/codegangsta/cli"
 	"github.com/codegangsta/martini"
 
 	"github.com/gogits/binding"
 
-	"github.com/gogits/gogs/models"
 	"github.com/gogits/gogs/modules/auth"
 	"github.com/gogits/gogs/modules/avatar"
 	"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"
@@ -40,27 +37,6 @@ and it takes care of all the other things for you`,
 	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 {
 	r := martini.NewRouter()
 	m := martini.New()
@@ -74,9 +50,7 @@ func newMartini() *martini.ClassicMartini {
 
 func runWeb(*cli.Context) {
 	fmt.Println("Server is running...")
-	globalInit()
-	base.NewServices()
-	checkRunMode()
+	routers.GlobalInit()
 	log.Info("%s %s", base.AppName, base.AppVer)
 
 	m := newMartini()