mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Add comment of issue
This commit is contained in:
		@@ -1,3 +1,6 @@
 | 
				
			|||||||
 | 
					Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest)
 | 
				
			||||||
 | 
					=====================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language.
 | 
					Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language.
 | 
				
			||||||
 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -163,9 +163,29 @@ type Milestone struct {
 | 
				
			|||||||
type Comment struct {
 | 
					type Comment struct {
 | 
				
			||||||
	Id       int64
 | 
						Id       int64
 | 
				
			||||||
	PosterId int64
 | 
						PosterId int64
 | 
				
			||||||
 | 
						Poster   *User `xorm:"-"`
 | 
				
			||||||
	IssueId  int64
 | 
						IssueId  int64
 | 
				
			||||||
	CommitId int64
 | 
						CommitId int64
 | 
				
			||||||
	Line     int
 | 
						Line     int64
 | 
				
			||||||
	Content  string
 | 
						Content  string
 | 
				
			||||||
	Created  time.Time `xorm:"created"`
 | 
						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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -72,7 +72,7 @@ func setEngine() {
 | 
				
			|||||||
func NewEngine() {
 | 
					func NewEngine() {
 | 
				
			||||||
	setEngine()
 | 
						setEngine()
 | 
				
			||||||
	if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Watch),
 | 
						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)
 | 
							fmt.Printf("sync database struct error: %v\n", err)
 | 
				
			||||||
		os.Exit(2)
 | 
							os.Exit(2)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,8 +113,34 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) {
 | 
				
			|||||||
		return
 | 
							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["Title"] = issue.Name
 | 
				
			||||||
	ctx.Data["Issue"] = issue
 | 
						ctx.Data["Issue"] = issue
 | 
				
			||||||
 | 
						ctx.Data["Comments"] = comments
 | 
				
			||||||
	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")
 | 
				
			||||||
@@ -132,7 +158,7 @@ func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat
 | 
				
			|||||||
		if err == models.ErrIssueNotExist {
 | 
							if err == models.ErrIssueNotExist {
 | 
				
			||||||
			ctx.Handle(404, "issue.UpdateIssue", err)
 | 
								ctx.Handle(404, "issue.UpdateIssue", err)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			ctx.Handle(200, "issue.UpdateIssue", err)
 | 
								ctx.Handle(200, "issue.UpdateIssue(get issue)", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -148,10 +174,48 @@ func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat
 | 
				
			|||||||
	issue.Labels = form.Labels
 | 
						issue.Labels = form.Labels
 | 
				
			||||||
	issue.Content = form.Content
 | 
						issue.Content = form.Content
 | 
				
			||||||
	if err = models.UpdateIssue(issue); err != nil {
 | 
						if err = models.UpdateIssue(issue); err != nil {
 | 
				
			||||||
		ctx.Handle(200, "issue.UpdateIssue", err)
 | 
							ctx.Handle(200, "issue.UpdateIssue(update issue)", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["Title"] = issue.Name
 | 
						ctx.Data["Title"] = issue.Name
 | 
				
			||||||
	ctx.Data["Issue"] = issue
 | 
						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))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,52 +6,39 @@
 | 
				
			|||||||
    <div id="issue">
 | 
					    <div id="issue">
 | 
				
			||||||
        <div id="issue-id" class="issue-whole">
 | 
					        <div id="issue-id" class="issue-whole">
 | 
				
			||||||
            <div class="issue-head clearfix">
 | 
					            <div class="issue-head clearfix">
 | 
				
			||||||
                <div class="number pull-right">#448</div>
 | 
					                <div class="number pull-right">#{{.Issue.Index}}</div>
 | 
				
			||||||
                <span class="author pull-left"><img class="avatar" src="#" alt="" width="30"/></span>
 | 
					                <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">[Request]关于context中的Download方法</h1>
 | 
					                <h1 class="title pull-left">{{.Issue.Name}}</h1>
 | 
				
			||||||
                <p class="info pull-left">
 | 
					                <p class="info pull-left">
 | 
				
			||||||
                    <span class="status label label-success">Open</span>
 | 
					                    <span class="status label label-{{if .Issue.IsClosed}}danger{{else}}success{{end}}">{{if .Issue.IsClosed}}Closed{{else}}Open{{end}}</span>
 | 
				
			||||||
                    <a href="#" class="author"><strong>linbaozhong</strong></a> opened this issue
 | 
					                    <a href="/user/{{.Issue.Poster.Name}}" class="author"><strong>{{.Issue.Poster.Name}}</strong></a> opened this issue
 | 
				
			||||||
                    <span class="time">2 months ago</span> · 1 comment
 | 
					                    <span class="time">{{TimeSince .Issue.Created}}</span> · {{.Issue.NumComments}} comments
 | 
				
			||||||
                </p>
 | 
					                </p>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div class="issue-main">
 | 
					            <div class="issue-main">
 | 
				
			||||||
               <div class="panel panel-default issue-content">
 | 
					               <div class="panel panel-default issue-content">
 | 
				
			||||||
                   <div class="panel-body markdown">
 | 
					                   <div class="panel-body markdown">
 | 
				
			||||||
                       <p>context中的Download方法:</p>
 | 
					                       <p>{{.Issue.Content}}</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>
 | 
					 | 
				
			||||||
                   </div>
 | 
					                   </div>
 | 
				
			||||||
               </div>
 | 
					               </div>
 | 
				
			||||||
 | 
					               {{range .Comments}}
 | 
				
			||||||
               <div class="issue-child">
 | 
					               <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="issue-content panel panel-default">
 | 
				
			||||||
                       <div class="panel-heading">
 | 
					                       <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>
 | 
				
			||||||
                       <div class="panel-body markdown">
 | 
					                       <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>
 | 
					                   </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					                {{end}}
 | 
				
			||||||
                <hr class="issue-line"/>
 | 
					                <hr class="issue-line"/>
 | 
				
			||||||
                <div class="issue-child issue-reply">
 | 
					                <div class="issue-child issue-reply">
 | 
				
			||||||
                    <a class="user pull-left" href="#"><img class="avatar" src="#" alt=""/></a>
 | 
					                    <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="">
 | 
					                    <form class="panel panel-default issue-content" action="/{{.RepositoryLink}}/comment/new" method="post">
 | 
				
			||||||
 | 
					                        {{.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 -->
 | 
					                                <div class="md-help pull-right"><!-- todo help link -->
 | 
				
			||||||
@@ -64,6 +51,7 @@
 | 
				
			|||||||
                                <div class="tab-content">
 | 
					                                <div class="tab-content">
 | 
				
			||||||
                                    <div class="tab-pane" id="issue-textarea">
 | 
					                                    <div class="tab-pane" id="issue-textarea">
 | 
				
			||||||
                                        <div class="form-group">
 | 
					                                        <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>
 | 
					                                            <textarea class="form-control" name="content" id="issue-content" rows="10" placeholder="Write some content">{{.content}}</textarea>
 | 
				
			||||||
                                        </div>
 | 
					                                        </div>
 | 
				
			||||||
                                    </div>
 | 
					                                    </div>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								web.go
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								web.go
									
									
									
									
									
								
							@@ -144,6 +144,7 @@ func runWeb(*cli.Context) {
 | 
				
			|||||||
		r.Get("/action/:action", repo.Action)
 | 
							r.Get("/action/:action", repo.Action)
 | 
				
			||||||
		r.Any("/issues/new", binding.BindIgnErr(auth.CreateIssueForm{}), repo.CreateIssue)
 | 
							r.Any("/issues/new", binding.BindIgnErr(auth.CreateIssueForm{}), repo.CreateIssue)
 | 
				
			||||||
		r.Post("/issues/:index", binding.BindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue)
 | 
							r.Post("/issues/:index", binding.BindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue)
 | 
				
			||||||
 | 
							r.Post("/comment/:action", repo.Comment)
 | 
				
			||||||
	}, reqSignIn, middleware.RepoAssignment(true))
 | 
						}, reqSignIn, middleware.RepoAssignment(true))
 | 
				
			||||||
	m.Group("/:username/:reponame", func(r martini.Router) {
 | 
						m.Group("/:username/:reponame", func(r martini.Router) {
 | 
				
			||||||
		r.Get("/commits/:branchname", repo.Commits)
 | 
							r.Get("/commits/:branchname", repo.Commits)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user