diff --git a/gogs.go b/gogs.go index a60903209..2bb80a664 100644 --- a/gogs.go +++ b/gogs.go @@ -20,7 +20,7 @@ import ( // Test that go1.2 tag above is included in builds. main.go refers to this definition. const go12tag = true -const APP_VER = "0.1.5.0322.2" +const APP_VER = "0.1.6.0323.1" func init() { base.AppVer = APP_VER diff --git a/models/action.go b/models/action.go index 4e1107f89..a996e16aa 100644 --- a/models/action.go +++ b/models/action.go @@ -88,7 +88,6 @@ func CommitRepoAction(userId int64, userName string, return err } repo.IsBare = false - repo.Updated = time.Now() if err = UpdateRepository(repo); err != nil { return err } diff --git a/models/issue.go b/models/issue.go index c669d201f..0b6ca4c32 100644 --- a/models/issue.go +++ b/models/issue.go @@ -4,16 +4,127 @@ package models +import ( + "strings" + "time" + + "github.com/gogits/gogs/modules/base" +) + +// Issue represents an issue or pull request of repository. type Issue struct { - Id int64 - RepoId int64 `xorm:"index"` - PosterId int64 + Id int64 + Index int64 // Index in one repository. + Name string + RepoId int64 `xorm:"index"` + PosterId int64 + MilestoneId int64 + AssigneeId int64 + IsPull bool // Indicates whether is a pull request or not. + IsClosed bool + Labels string + Mentions string + Content string + NumComments int + Created time.Time `xorm:"created"` + Updated time.Time `xorm:"updated"` +} + +// CreateIssue creates new issue for repository. +func CreateIssue(userId, repoId, milestoneId, assigneeId int64, name, labels, mentions, content string, isPull bool) error { + count, err := GetIssueCount(repoId) + if err != nil { + return err + } + + _, err = orm.Insert(&Issue{ + Index: count + 1, + Name: name, + RepoId: repoId, + PosterId: userId, + MilestoneId: milestoneId, + AssigneeId: assigneeId, + IsPull: isPull, + Labels: labels, + Mentions: mentions, + Content: content, + }) + return err } -type PullRequest struct { - Id int64 +// GetIssueCount returns count of issues in the repository. +func GetIssueCount(repoId int64) (int64, error) { + return orm.Count(&Issue{RepoId: repoId}) } +// GetIssues returns a list of issues by given conditions. +func GetIssues(userId, repoId, posterId, milestoneId int64, page int, isClosed, isMention bool, labels, sortType string) ([]Issue, error) { + sess := orm.Limit(20, (page-1)*20).Where("repo_id=?", repoId).And("is_closed=?", isClosed) + if userId > 0 { + sess = sess.And("assignee_id=?", userId) + } else if posterId > 0 { + sess = sess.And("poster_id=?", posterId) + } else if isMention { + sess = sess.And("mentions like '%$" + base.ToStr(userId) + "|%'") + } + + if milestoneId > 0 { + sess = sess.And("milestone_id=?", milestoneId) + } + + if len(labels) > 0 { + for _, label := range strings.Split(labels, ",") { + sess = sess.And("mentions like '%$" + label + "|%'") + } + } + + switch sortType { + case "oldest": + sess = sess.Asc("created") + case "recentupdate": + sess = sess.Desc("updated") + case "leastupdate": + sess = sess.Asc("updated") + case "mostcomment": + sess = sess.Desc("num_comments") + case "leastcomment": + sess = sess.Asc("num_comments") + default: + sess = sess.Desc("created") + } + + var issues []Issue + err := sess.Find(&issues) + return issues, err +} + +// Label represents a list of labels of repository for issues. +type Label struct { + Id int64 + RepoId int64 `xorm:"index"` + Names string + Colors string +} + +// Milestone represents a milestone of repository. +type Milestone struct { + Id int64 + Name string + RepoId int64 `xorm:"index"` + IsClosed bool + Content string + NumIssues int + DueDate time.Time + Created time.Time `xorm:"created"` +} + +// Comment represents a comment in commit and issue page. type Comment struct { - Id int64 + Id int64 + PosterId int64 + IssueId int64 + CommitId int64 + Line int + Content string + Created time.Time `xorm:"created"` } diff --git a/models/models.go b/models/models.go index 8713ff289..fb749c5d8 100644 --- a/models/models.go +++ b/models/models.go @@ -72,7 +72,7 @@ func setEngine() { func NewEngine() { setEngine() if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Watch), - new(Action), new(Access)); err != nil { + new(Action), new(Access), new(Issue)); err != nil { fmt.Printf("sync database struct error: %v\n", err) os.Exit(2) } diff --git a/routers/repo/issue.go b/routers/repo/issue.go new file mode 100644 index 000000000..c6af8ca0b --- /dev/null +++ b/routers/repo/issue.go @@ -0,0 +1,30 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + "github.com/codegangsta/martini" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/middleware" +) + +func Issues(ctx *middleware.Context, params martini.Params) { + ctx.Data["IsRepoToolbarIssues"] = true + + milestoneId, _ := base.StrTo(params["milestone"]).Int() + page, _ := base.StrTo(params["page"]).Int() + + var err error + ctx.Data["Issues"], err = models.GetIssues(0, ctx.Repo.Repository.Id, 0, + int64(milestoneId), page, params["state"] == "closed", false, params["labels"], params["sortType"]) + if err != nil { + ctx.Handle(200, "issue.Issues: %v", err) + return + } + + ctx.HTML(200, "repo/issues") +} diff --git a/routers/repo/repo.go b/routers/repo/repo.go index c83a6df52..ff0fa85dd 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -5,8 +5,17 @@ package repo import ( + "path" + "strings" + + "github.com/codegangsta/martini" + + "github.com/gogits/git" + "github.com/gogits/webdav" + "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/auth" + "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/middleware" ) @@ -61,3 +70,288 @@ func SettingPost(ctx *middleware.Context) { log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName) ctx.Redirect("/", 302) } + +func Branches(ctx *middleware.Context, params martini.Params) { + if !ctx.Repo.IsValid { + return + } + + brs, err := models.GetBranches(params["username"], params["reponame"]) + if err != nil { + ctx.Handle(200, "repo.Branches", err) + return + } else if len(brs) == 0 { + ctx.Error(404) + return + } + + ctx.Data["Username"] = params["username"] + ctx.Data["Reponame"] = params["reponame"] + + ctx.Data["Branchname"] = brs[0] + ctx.Data["Branches"] = brs + ctx.Data["IsRepoToolbarBranches"] = true + + ctx.HTML(200, "repo/branches") +} + +func Single(ctx *middleware.Context, params martini.Params) { + if !ctx.Repo.IsValid { + return + } + + if len(params["branchname"]) == 0 { + params["branchname"] = "master" + } + + // Get tree path + treename := params["_1"] + + if len(treename) > 0 && treename[len(treename)-1] == '/' { + ctx.Redirect("/"+ctx.Repo.Owner.LowerName+"/"+ + ctx.Repo.Repository.Name+"/src/"+params["branchname"]+"/"+treename[:len(treename)-1], 302) + return + } + + ctx.Data["IsRepoToolbarSource"] = true + + // Branches. + brs, err := models.GetBranches(params["username"], params["reponame"]) + if err != nil { + log.Error("repo.Single(GetBranches): %v", err) + ctx.Error(404) + return + } else if ctx.Repo.Repository.IsBare { + ctx.Data["IsBareRepo"] = true + ctx.HTML(200, "repo/single") + return + } + + ctx.Data["Branches"] = brs + + repoFile, err := models.GetTargetFile(params["username"], params["reponame"], + params["branchname"], params["commitid"], treename) + + if err != nil && err != models.ErrRepoFileNotExist { + log.Error("repo.Single(GetTargetFile): %v", err) + ctx.Error(404) + return + } + + branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + params["branchname"] + + if len(treename) != 0 && repoFile == nil { + ctx.Error(404) + return + } + + if repoFile != nil && repoFile.IsFile() { + if repoFile.Size > 1024*1024 || repoFile.Filemode != git.FileModeBlob { + ctx.Data["FileIsLarge"] = true + } else if blob, err := repoFile.LookupBlob(); err != nil { + log.Error("repo.Single(repoFile.LookupBlob): %v", err) + ctx.Error(404) + } else { + ctx.Data["IsFile"] = true + ctx.Data["FileName"] = repoFile.Name + ext := path.Ext(repoFile.Name) + if len(ext) > 0 { + ext = ext[1:] + } + ctx.Data["FileExt"] = ext + + readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name) + ctx.Data["ReadmeExist"] = readmeExist + if readmeExist { + ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), "")) + } else { + ctx.Data["FileContent"] = string(blob.Contents()) + } + } + + } else { + // Directory and file list. + files, err := models.GetReposFiles(params["username"], params["reponame"], + params["branchname"], params["commitid"], treename) + if err != nil { + log.Error("repo.Single(GetReposFiles): %v", err) + ctx.Error(404) + return + } + + ctx.Data["Files"] = files + + var readmeFile *models.RepoFile + + for _, f := range files { + if !f.IsFile() || !base.IsReadmeFile(f.Name) { + continue + } else { + readmeFile = f + break + } + } + + if readmeFile != nil { + ctx.Data["ReadmeExist"] = true + // if file large than 1M not show it + if readmeFile.Size > 1024*1024 || readmeFile.Filemode != git.FileModeBlob { + ctx.Data["FileIsLarge"] = true + } else if blob, err := readmeFile.LookupBlob(); err != nil { + log.Error("repo.Single(readmeFile.LookupBlob): %v", err) + ctx.Error(404) + return + } else { + // current repo branch link + + ctx.Data["FileName"] = readmeFile.Name + ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), branchLink)) + } + } + } + + ctx.Data["Username"] = params["username"] + ctx.Data["Reponame"] = params["reponame"] + ctx.Data["Branchname"] = params["branchname"] + + var treenames []string + Paths := make([]string, 0) + + if len(treename) > 0 { + treenames = strings.Split(treename, "/") + for i, _ := range treenames { + Paths = append(Paths, strings.Join(treenames[0:i+1], "/")) + } + + ctx.Data["HasParentPath"] = true + if len(Paths)-2 >= 0 { + ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2] + } + } + + // Get latest commit according username and repo name + commit, err := models.GetCommit(params["username"], params["reponame"], + params["branchname"], params["commitid"]) + if err != nil { + log.Error("repo.Single(GetCommit): %v", err) + ctx.Error(404) + return + } + ctx.Data["LastCommit"] = commit + + ctx.Data["Paths"] = Paths + ctx.Data["Treenames"] = treenames + ctx.Data["BranchLink"] = branchLink + ctx.HTML(200, "repo/single") +} + +func Http(ctx *middleware.Context, params martini.Params) { + /*if !ctx.Repo.IsValid { + return + }*/ + + // TODO: access check + + username := params["username"] + reponame := params["reponame"] + if strings.HasSuffix(reponame, ".git") { + reponame = reponame[:len(reponame)-4] + } + + prefix := path.Join("/", username, params["reponame"]) + server := &webdav.Server{ + Fs: webdav.Dir(models.RepoPath(username, reponame)), + TrimPrefix: prefix, + Listings: true, + } + + server.ServeHTTP(ctx.ResponseWriter, ctx.Req) +} + +func Setting(ctx *middleware.Context, params martini.Params) { + if !ctx.Repo.IsOwner { + ctx.Error(404) + return + } + + ctx.Data["IsRepoToolbarSetting"] = true + + if ctx.Repo.Repository.IsBare { + ctx.Data["IsBareRepo"] = true + ctx.HTML(200, "repo/setting") + return + } + + var title string + if t, ok := ctx.Data["Title"].(string); ok { + title = t + } + + if len(params["branchname"]) == 0 { + params["branchname"] = "master" + } + + ctx.Data["Branchname"] = params["branchname"] + ctx.Data["Title"] = title + " - settings" + ctx.HTML(200, "repo/setting") +} + +func Commits(ctx *middleware.Context, params martini.Params) { + brs, err := models.GetBranches(params["username"], params["reponame"]) + if err != nil { + ctx.Handle(200, "repo.Commits", err) + return + } else if len(brs) == 0 { + ctx.Error(404) + return + } + + ctx.Data["IsRepoToolbarCommits"] = true + commits, err := models.GetCommits(params["username"], + params["reponame"], params["branchname"]) + if err != nil { + ctx.Error(404) + return + } + ctx.Data["Username"] = params["username"] + ctx.Data["Reponame"] = params["reponame"] + ctx.Data["CommitCount"] = commits.Len() + ctx.Data["Commits"] = commits + ctx.HTML(200, "repo/commits") +} + +func Pulls(ctx *middleware.Context) { + ctx.Data["IsRepoToolbarPulls"] = true + ctx.HTML(200, "repo/pulls") +} + +func Action(ctx *middleware.Context, params martini.Params) { + var err error + switch params["action"] { + case "watch": + err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true) + case "unwatch": + err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false) + case "desc": + if !ctx.Repo.IsOwner { + ctx.Error(404) + return + } + + ctx.Repo.Repository.Description = ctx.Query("desc") + ctx.Repo.Repository.Website = ctx.Query("site") + err = models.UpdateRepository(ctx.Repo.Repository) + } + + if err != nil { + log.Error("repo.Action(%s): %v", params["action"], err) + ctx.JSON(200, map[string]interface{}{ + "ok": false, + "err": err.Error(), + }) + return + } + ctx.JSON(200, map[string]interface{}{ + "ok": true, + }) +} diff --git a/routers/repo/single.go b/routers/repo/single.go deleted file mode 100644 index 5906e64fb..000000000 --- a/routers/repo/single.go +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "path" - "strings" - - "github.com/codegangsta/martini" - - "github.com/gogits/git" - "github.com/gogits/webdav" - - "github.com/gogits/gogs/models" - "github.com/gogits/gogs/modules/base" - "github.com/gogits/gogs/modules/log" - "github.com/gogits/gogs/modules/middleware" -) - -func Branches(ctx *middleware.Context, params martini.Params) { - if !ctx.Repo.IsValid { - return - } - - brs, err := models.GetBranches(params["username"], params["reponame"]) - if err != nil { - ctx.Handle(200, "repo.Branches", err) - return - } else if len(brs) == 0 { - ctx.Error(404) - return - } - - ctx.Data["Username"] = params["username"] - ctx.Data["Reponame"] = params["reponame"] - - ctx.Data["Branchname"] = brs[0] - ctx.Data["Branches"] = brs - ctx.Data["IsRepoToolbarBranches"] = true - - ctx.HTML(200, "repo/branches") -} - -func Single(ctx *middleware.Context, params martini.Params) { - if !ctx.Repo.IsValid { - return - } - - if len(params["branchname"]) == 0 { - params["branchname"] = "master" - } - - // Get tree path - treename := params["_1"] - - if len(treename) > 0 && treename[len(treename)-1] == '/' { - ctx.Redirect("/"+ctx.Repo.Owner.LowerName+"/"+ - ctx.Repo.Repository.Name+"/src/"+params["branchname"]+"/"+treename[:len(treename)-1], 302) - return - } - - ctx.Data["IsRepoToolbarSource"] = true - - // Branches. - brs, err := models.GetBranches(params["username"], params["reponame"]) - if err != nil { - log.Error("repo.Single(GetBranches): %v", err) - ctx.Error(404) - return - } else if ctx.Repo.Repository.IsBare { - ctx.Data["IsBareRepo"] = true - ctx.HTML(200, "repo/single") - return - } - - ctx.Data["Branches"] = brs - - repoFile, err := models.GetTargetFile(params["username"], params["reponame"], - params["branchname"], params["commitid"], treename) - - if err != nil && err != models.ErrRepoFileNotExist { - log.Error("repo.Single(GetTargetFile): %v", err) - ctx.Error(404) - return - } - - branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + params["branchname"] - - if len(treename) != 0 && repoFile == nil { - ctx.Error(404) - return - } - - if repoFile != nil && repoFile.IsFile() { - if repoFile.Size > 1024*1024 || repoFile.Filemode != git.FileModeBlob { - ctx.Data["FileIsLarge"] = true - } else if blob, err := repoFile.LookupBlob(); err != nil { - log.Error("repo.Single(repoFile.LookupBlob): %v", err) - ctx.Error(404) - } else { - ctx.Data["IsFile"] = true - ctx.Data["FileName"] = repoFile.Name - ext := path.Ext(repoFile.Name) - if len(ext) > 0 { - ext = ext[1:] - } - ctx.Data["FileExt"] = ext - - readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name) - ctx.Data["ReadmeExist"] = readmeExist - if readmeExist { - ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), "")) - } else { - ctx.Data["FileContent"] = string(blob.Contents()) - } - } - - } else { - // Directory and file list. - files, err := models.GetReposFiles(params["username"], params["reponame"], - params["branchname"], params["commitid"], treename) - if err != nil { - log.Error("repo.Single(GetReposFiles): %v", err) - ctx.Error(404) - return - } - - ctx.Data["Files"] = files - - var readmeFile *models.RepoFile - - for _, f := range files { - if !f.IsFile() || !base.IsReadmeFile(f.Name) { - continue - } else { - readmeFile = f - break - } - } - - if readmeFile != nil { - ctx.Data["ReadmeExist"] = true - // if file large than 1M not show it - if readmeFile.Size > 1024*1024 || readmeFile.Filemode != git.FileModeBlob { - ctx.Data["FileIsLarge"] = true - } else if blob, err := readmeFile.LookupBlob(); err != nil { - log.Error("repo.Single(readmeFile.LookupBlob): %v", err) - ctx.Error(404) - return - } else { - // current repo branch link - - ctx.Data["FileName"] = readmeFile.Name - ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), branchLink)) - } - } - } - - ctx.Data["Username"] = params["username"] - ctx.Data["Reponame"] = params["reponame"] - ctx.Data["Branchname"] = params["branchname"] - - var treenames []string - Paths := make([]string, 0) - - if len(treename) > 0 { - treenames = strings.Split(treename, "/") - for i, _ := range treenames { - Paths = append(Paths, strings.Join(treenames[0:i+1], "/")) - } - - ctx.Data["HasParentPath"] = true - if len(Paths)-2 >= 0 { - ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2] - } - } - - // Get latest commit according username and repo name - commit, err := models.GetCommit(params["username"], params["reponame"], - params["branchname"], params["commitid"]) - if err != nil { - log.Error("repo.Single(GetCommit): %v", err) - ctx.Error(404) - return - } - ctx.Data["LastCommit"] = commit - - ctx.Data["Paths"] = Paths - ctx.Data["Treenames"] = treenames - ctx.Data["BranchLink"] = branchLink - ctx.HTML(200, "repo/single") -} - -func Http(ctx *middleware.Context, params martini.Params) { - /*if !ctx.Repo.IsValid { - return - }*/ - - // TODO: access check - - username := params["username"] - reponame := params["reponame"] - if strings.HasSuffix(reponame, ".git") { - reponame = reponame[:len(reponame)-4] - } - - prefix := path.Join("/", username, params["reponame"]) - server := &webdav.Server{ - Fs: webdav.Dir(models.RepoPath(username, reponame)), - TrimPrefix: prefix, - Listings: true, - } - - server.ServeHTTP(ctx.ResponseWriter, ctx.Req) -} - -func Setting(ctx *middleware.Context, params martini.Params) { - if !ctx.Repo.IsOwner { - ctx.Error(404) - return - } - - ctx.Data["IsRepoToolbarSetting"] = true - - if ctx.Repo.Repository.IsBare { - ctx.Data["IsBareRepo"] = true - ctx.HTML(200, "repo/setting") - return - } - - var title string - if t, ok := ctx.Data["Title"].(string); ok { - title = t - } - - if len(params["branchname"]) == 0 { - params["branchname"] = "master" - } - - ctx.Data["Branchname"] = params["branchname"] - ctx.Data["Title"] = title + " - settings" - ctx.HTML(200, "repo/setting") -} - -func Commits(ctx *middleware.Context, params martini.Params) { - brs, err := models.GetBranches(params["username"], params["reponame"]) - if err != nil { - ctx.Handle(200, "repo.Commits", err) - return - } else if len(brs) == 0 { - ctx.Error(404) - return - } - - ctx.Data["IsRepoToolbarCommits"] = true - commits, err := models.GetCommits(params["username"], - params["reponame"], params["branchname"]) - if err != nil { - ctx.Error(404) - return - } - ctx.Data["Username"] = params["username"] - ctx.Data["Reponame"] = params["reponame"] - ctx.Data["CommitCount"] = commits.Len() - ctx.Data["Commits"] = commits - ctx.HTML(200, "repo/commits") -} - -func Issues(ctx *middleware.Context) { - ctx.Data["IsRepoToolbarIssues"] = true - ctx.HTML(200, "repo/issues") -} - -func Pulls(ctx *middleware.Context) { - ctx.Data["IsRepoToolbarPulls"] = true - ctx.HTML(200, "repo/pulls") -} - -func Action(ctx *middleware.Context, params martini.Params) { - var err error - switch params["action"] { - case "watch": - err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true) - case "unwatch": - err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false) - } - - if err != nil { - log.Error("repo.Action(%s): %v", params["action"], err) - ctx.JSON(200, map[string]interface{}{ - "ok": false, - "err": err.Error(), - }) - return - } - ctx.JSON(200, map[string]interface{}{ - "ok": true, - }) -}