]> source.dussan.org Git - gitea.git/commitdiff
Add comment of issue
authorUnknown <joe2010xtmf@163.com>
Wed, 26 Mar 2014 16:31:01 +0000 (12:31 -0400)
committerUnknown <joe2010xtmf@163.com>
Wed, 26 Mar 2014 16:31:01 +0000 (12:31 -0400)
README.md
models/issue.go
models/models.go
routers/repo/issue.go
templates/issue/view.tmpl
web.go

index b7396e5f47e99d82e265afd0fe2d7170bd5ba3f8..47f4bd515bb60a52cc6ccd7c49a97f6f86ac6799 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
+Gogs - Go Git Service [![wercker status](https://app.wercker.com/status/ad0bdb0bc450ac6f09bc56b9640a50aa/s/ "wercker status")](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [![Build Status](https://drone.io/github.com/gogits/gogs/status.png)](https://drone.io/github.com/gogits/gogs/latest)
+=====================
+
 Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language.
 
 ![Demo](http://gowalker.org/public/gogs_demo.gif)
index 2de6568589b9bbf27b2c51b70e21567dff584127..47ec4edd0bd6d2171660fec83096c4fe5f2e40c6 100644 (file)
@@ -163,9 +163,29 @@ type Milestone struct {
 type Comment struct {
        Id       int64
        PosterId int64
+       Poster   *User `xorm:"-"`
        IssueId  int64
        CommitId int64
-       Line     int
+       Line     int64
        Content  string
        Created  time.Time `xorm:"created"`
 }
+
+// CreateComment creates comment of issue or commit.
+func CreateComment(userId, issueId, commitId, line int64, content string) error {
+       _, err := orm.Insert(&Comment{
+               PosterId: userId,
+               IssueId:  issueId,
+               CommitId: commitId,
+               Line:     line,
+               Content:  content,
+       })
+       return err
+}
+
+// GetIssueComments returns list of comment by given issue id.
+func GetIssueComments(issueId int64) ([]Comment, error) {
+       comments := make([]Comment, 0, 10)
+       err := orm.Asc("created").Find(&comments, &Comment{IssueId: issueId})
+       return comments, err
+}
index ad19a929fbd4a82a07e19bce95e68a8c5e7d66cb..813725be4646f269c23c860831f763176c5c3213 100644 (file)
@@ -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), new(Issue)); err != nil {
+               new(Action), new(Access), new(Issue), new(Comment)); err != nil {
                fmt.Printf("sync database struct error: %v\n", err)
                os.Exit(2)
        }
index 72d07d4acf674f71e3aad80495598dece8985598..e53aebf636fcf1d7f636e21d27cdfe7535e1ab9c 100644 (file)
@@ -113,8 +113,34 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) {
                return
        }
 
+       // Get posters.
+       u, err := models.GetUserById(issue.PosterId)
+       if err != nil {
+               ctx.Handle(200, "issue.ViewIssue(get poster): %v", err)
+               return
+       }
+       issue.Poster = u
+
+       // Get comments.
+       comments, err := models.GetIssueComments(issue.Id)
+       if err != nil {
+               ctx.Handle(200, "issue.ViewIssue(get comments): %v", err)
+               return
+       }
+
+       // Get posters.
+       for i := range comments {
+               u, err := models.GetUserById(comments[i].PosterId)
+               if err != nil {
+                       ctx.Handle(200, "issue.ViewIssue(get poster): %v", err)
+                       return
+               }
+               comments[i].Poster = u
+       }
+
        ctx.Data["Title"] = issue.Name
        ctx.Data["Issue"] = issue
+       ctx.Data["Comments"] = comments
        ctx.Data["IsRepoToolbarIssues"] = true
        ctx.Data["IsRepoToolbarIssuesList"] = false
        ctx.HTML(200, "issue/view")
@@ -132,7 +158,7 @@ func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat
                if err == models.ErrIssueNotExist {
                        ctx.Handle(404, "issue.UpdateIssue", err)
                } else {
-                       ctx.Handle(200, "issue.UpdateIssue", err)
+                       ctx.Handle(200, "issue.UpdateIssue(get issue)", err)
                }
                return
        }
@@ -148,10 +174,48 @@ func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat
        issue.Labels = form.Labels
        issue.Content = form.Content
        if err = models.UpdateIssue(issue); err != nil {
-               ctx.Handle(200, "issue.UpdateIssue", err)
+               ctx.Handle(200, "issue.UpdateIssue(update issue)", err)
                return
        }
 
        ctx.Data["Title"] = issue.Name
        ctx.Data["Issue"] = issue
 }
+
+func Comment(ctx *middleware.Context, params martini.Params) {
+       index, err := base.StrTo(ctx.Query("issueIndex")).Int()
+       if err != nil {
+               ctx.Handle(404, "issue.Comment", err)
+               return
+       }
+
+       issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, int64(index))
+       if err != nil {
+               if err == models.ErrIssueNotExist {
+                       ctx.Handle(404, "issue.Comment", err)
+               } else {
+                       ctx.Handle(200, "issue.Comment(get issue)", err)
+               }
+               return
+       }
+
+       content := ctx.Query("content")
+       if len(content) == 0 {
+               ctx.Handle(404, "issue.Comment", err)
+               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)
+                       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))
+}
index 381c79990c643f8b48cfb064e8a5c970343fc6ac..6b282513aab2379194ccf2091d30c71c2ddf8ec5 100644 (file)
@@ -6,52 +6,39 @@
     <div id="issue">
         <div id="issue-id" class="issue-whole">
             <div class="issue-head clearfix">
-                <div class="number pull-right">#448</div>
-                <span class="author pull-left"><img class="avatar" src="#" alt="" width="30"/></span>
-                <h1 class="title pull-left">[Request]关于context中的Download方法</h1>
+                <div class="number pull-right">#{{.Issue.Index}}</div>
+                <a class="author pull-left" href="/user/{{.Issue.Poster.Name}}"><img class="avatar" src="{{.Issue.Poster.AvatarLink}}" alt="" width="30"/></a>
+                <h1 class="title pull-left">{{.Issue.Name}}</h1>
                 <p class="info pull-left">
-                    <span class="status label label-success">Open</span>
-                    <a href="#" class="author"><strong>linbaozhong</strong></a> opened this issue
-                    <span class="time">2 months ago</span> · 1 comment
+                    <span class="status label label-{{if .Issue.IsClosed}}danger{{else}}success{{end}}">{{if .Issue.IsClosed}}Closed{{else}}Open{{end}}</span>
+                    <a href="/user/{{.Issue.Poster.Name}}" class="author"><strong>{{.Issue.Poster.Name}}</strong></a> opened this issue
+                    <span class="time">{{TimeSince .Issue.Created}}</span> · {{.Issue.NumComments}} comments
                 </p>
             </div>
             <div class="issue-main">
                <div class="panel panel-default issue-content">
                    <div class="panel-body markdown">
-                       <p>context中的Download方法:</p>
-                       <p>func (output *BeegoOutput) Download(file string)</p>
-                       <p>建议在file参数后面增加一个可选参数filename.</p>
-                       <p>如果filename不存在或为空,output.Header("Content-Disposition", "attachment; filename="+filepath.Base(file))</p>
-                       <p>如果filename不为空,output.Header("Content-Disposition", "attachment; filename="+filename)</p>
-                       <p>因为有时候,多数情况下,要下载的真实的文件名与显示和保存的本地的文件名是不一样的,希望显示的文件名更友好些</p>
+                       <p>{{.Issue.Content}}</p>
                    </div>
                </div>
+               {{range .Comments}}
                <div class="issue-child">
-                   <a class="user pull-left" href="#"><img class="avatar" src="#" alt=""/></a>
+                   <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="#" class="user">phpqinsir</a> commented <span class="time">3 days ago</span>
+                           <a href="/user/{{.Poster.Name}}" class="user">{{.Poster.Name}}</a> commented <span class="time">{{TimeSince .Created}}</span>
                        </div>
                        <div class="panel-body markdown">
-                           <p>@slene 看来也只能这样了。最主要是数组与切片的用法,我感觉不科学。因为要知道个数,然后个数与问号个数要对应。不能像PHP YII框架那样,直接传入一个数组,自己在里面把参数组装成1,2,3,4这种格式。希望,Beego框架能加上。那就太完美了。谢谢。</p>
+                           <p>{{.Content}}</p>
                        </div>
                    </div>
-               </div>
-                <div class="issue-child">
-                    <a class="user pull-left" href="#"><img class="avatar" src="#" alt=""/></a>
-                    <div class="issue-content panel panel-default">
-                        <div class="panel-heading">
-                            <a href="#" class="user">phpqinsir</a> commented <span class="time">3 days ago</span>
-                        </div>
-                        <div class="panel-body markdown">
-                            <p>@slene 看来也只能这样了。最主要是数组与切片的用法,我感觉不科学。因为要知道个数,然后个数与问号个数要对应。不能像PHP YII框架那样,直接传入一个数组,自己在里面把参数组装成1,2,3,4这种格式。希望,Beego框架能加上。那就太完美了。谢谢。</p>
-                        </div>
-                    </div>
                 </div>
+                {{end}}
                 <hr class="issue-line"/>
                 <div class="issue-child issue-reply">
-                    <a class="user pull-left" href="#"><img class="avatar" src="#" alt=""/></a>
-                    <form class="panel panel-default issue-content" action="">
+                    <a class="user pull-left" href="/user/{{.SignedUser.Name}}"><img class="avatar" src="{{.SignedUser.AvatarLink}}" alt=""/></a>
+                    <form class="panel panel-default issue-content" action="/{{.RepositoryLink}}/comment/new" method="post">
+                        {{.CsrfTokenHtml}}
                         <div class="panel-body">
                             <div class="form-group">
                                 <div class="md-help pull-right"><!-- todo help link -->
@@ -64,6 +51,7 @@
                                 <div class="tab-content">
                                     <div class="tab-pane" id="issue-textarea">
                                         <div class="form-group">
+                                            <input type="hidden" value="{{.Issue.Index}}" name="issueIndex"/>
                                             <textarea class="form-control" name="content" id="issue-content" rows="10" placeholder="Write some content">{{.content}}</textarea>
                                         </div>
                                     </div>
diff --git a/web.go b/web.go
index 9423c3eecb63fb298794a9f5bca97d6b07435ed7..6bc55908b891dfa51c0c69bbd62d454204dc2a29 100644 (file)
--- a/web.go
+++ b/web.go
@@ -144,6 +144,7 @@ func runWeb(*cli.Context) {
                r.Get("/action/:action", repo.Action)
                r.Any("/issues/new", binding.BindIgnErr(auth.CreateIssueForm{}), repo.CreateIssue)
                r.Post("/issues/:index", binding.BindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue)
+               r.Post("/comment/:action", repo.Comment)
        }, reqSignIn, middleware.RepoAssignment(true))
        m.Group("/:username/:reponame", func(r martini.Router) {
                r.Get("/commits/:branchname", repo.Commits)