Show owner/poster tags of comments and fix #1312
This commit is contained in:
parent
1fb53067f4
commit
817b48ed1e
|
@ -97,7 +97,6 @@ There are 5 ways to install Gogs:
|
||||||
- Mail Service, modules design is inspired by [WeTalk](https://github.com/beego/wetalk).
|
- Mail Service, modules design is inspired by [WeTalk](https://github.com/beego/wetalk).
|
||||||
- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
|
- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
|
||||||
- Thanks [lavachen](http://www.lavachen.cn/) and [Rocker](http://weibo.com/rocker1989) for designing Logo.
|
- Thanks [lavachen](http://www.lavachen.cn/) and [Rocker](http://weibo.com/rocker1989) for designing Logo.
|
||||||
- Thanks [gobuild.io](http://gobuild.io) for providing binary compile and download service.
|
|
||||||
- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
|
- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
|
@ -67,7 +67,6 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||||
- 基于 [Macaron](https://github.com/Unknwon/macaron) 的路由与中间件机制。
|
- 基于 [Macaron](https://github.com/Unknwon/macaron) 的路由与中间件机制。
|
||||||
- 基于 [WeTalk](https://github.com/beego/wetalk) 修改的邮件服务和模块设计。
|
- 基于 [WeTalk](https://github.com/beego/wetalk) 修改的邮件服务和模块设计。
|
||||||
- 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。
|
- 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。
|
||||||
- 感谢 [gobuild.io](http://gobuild.io) 提供二进制编译与下载服务。
|
|
||||||
- 感谢 [lavachen](http://www.lavachen.cn/) 和 [Rocker](http://weibo.com/rocker1989) 设计的 Logo。
|
- 感谢 [lavachen](http://www.lavachen.cn/) 和 [Rocker](http://weibo.com/rocker1989) 设计的 Logo。
|
||||||
- 感谢 [Crowdin](https://crowdin.com/project/gogs) 提供免费的开源项目本地化支持。
|
- 感谢 [Crowdin](https://crowdin.com/project/gogs) 提供免费的开源项目本地化支持。
|
||||||
|
|
||||||
|
|
|
@ -413,6 +413,13 @@ issues.reopen_issue = Reopen
|
||||||
issues.reopen_comment_issue = Reopen and comment
|
issues.reopen_comment_issue = Reopen and comment
|
||||||
issues.create_comment = Comment
|
issues.create_comment = Comment
|
||||||
issues.closed_at = `closed at <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
issues.closed_at = `closed at <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||||
|
issues.reopened_at = `reopened at <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||||
|
issues.poster = Poster
|
||||||
|
issues.admin = Admin
|
||||||
|
issues.owner = Owner
|
||||||
|
issues.not_signed_in = You must sign in to comment
|
||||||
|
issues.sign_up_for_free = Sign up for free
|
||||||
|
issues.sign_in_require_desc = to join this conversation. Already have an account? <a href="%s">Sign in to comment</a>
|
||||||
issues.label_title = Label name
|
issues.label_title = Label name
|
||||||
issues.label_color = Label color
|
issues.label_color = Label color
|
||||||
issues.label_count = %d labels
|
issues.label_count = %d labels
|
||||||
|
|
2
gogs.go
2
gogs.go
|
@ -17,7 +17,7 @@ import (
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"github.com/gogits/gogs/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
const APP_VER = "0.6.4.0813 Beta"
|
const APP_VER = "0.6.4.0814 Beta"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
|
@ -1231,6 +1231,15 @@ const (
|
||||||
COMMENT_TYPE_PULL_REF
|
COMMENT_TYPE_PULL_REF
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CommentTag int
|
||||||
|
|
||||||
|
const (
|
||||||
|
COMMENT_TAG_NONE CommentTag = iota
|
||||||
|
COMMENT_TAG_POSTER
|
||||||
|
COMMENT_TAG_ADMIN
|
||||||
|
COMMENT_TAG_OWNER
|
||||||
|
)
|
||||||
|
|
||||||
// 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 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
@ -1245,6 +1254,9 @@ type Comment struct {
|
||||||
Created time.Time `xorm:"CREATED"`
|
Created time.Time `xorm:"CREATED"`
|
||||||
|
|
||||||
Attachments []*Attachment `xorm:"-"`
|
Attachments []*Attachment `xorm:"-"`
|
||||||
|
|
||||||
|
// For view issue page.
|
||||||
|
ShowTag CommentTag `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashTag returns unique hash tag for comment.
|
// HashTag returns unique hash tag for comment.
|
||||||
|
|
|
@ -247,8 +247,8 @@ func (repo *Repository) HasAccess(u *User) bool {
|
||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) IsOwnedBy(u *User) bool {
|
func (repo *Repository) IsOwnedBy(userID int64) bool {
|
||||||
return repo.OwnerID == u.Id
|
return repo.OwnerID == userID
|
||||||
}
|
}
|
||||||
|
|
||||||
// DescriptionHtml does special handles to description and return HTML string.
|
// DescriptionHtml does special handles to description and return HTML string.
|
||||||
|
|
|
@ -222,6 +222,25 @@ func (u *User) UploadAvatar(data []byte) error {
|
||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAdminOfRepo returns true if user has admin or higher access of repository.
|
||||||
|
func (u *User) IsAdminOfRepo(repo *Repository) bool {
|
||||||
|
if err := repo.GetOwner(); err != nil {
|
||||||
|
log.Error(3, "GetOwner: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.Owner.IsOrganization() {
|
||||||
|
has, err := HasAccess(u, repo, ACCESS_MODE_ADMIN)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(3, "HasAccess: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return has
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo.IsOwnedBy(u.Id)
|
||||||
|
}
|
||||||
|
|
||||||
// IsOrganization returns true if user is actually a organization.
|
// IsOrganization returns true if user is actually a organization.
|
||||||
func (u *User) IsOrganization() bool {
|
func (u *User) IsOrganization() bool {
|
||||||
return u.Type == ORGANIZATION
|
return u.Type == ORGANIZATION
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,12 +5,16 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/Unknwon/macaron"
|
"github.com/Unknwon/macaron"
|
||||||
"github.com/macaron-contrib/csrf"
|
"github.com/macaron-contrib/csrf"
|
||||||
|
|
||||||
|
"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/log"
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"github.com/gogits/gogs/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,6 +25,41 @@ type ToggleOptions struct {
|
||||||
DisableCsrf bool
|
DisableCsrf bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AutoSignIn reads cookie and try to auto-login.
|
||||||
|
func AutoSignIn(ctx *Context) (bool, error) {
|
||||||
|
uname := ctx.GetCookie(setting.CookieUserName)
|
||||||
|
if len(uname) == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
isSucceed := false
|
||||||
|
defer func() {
|
||||||
|
if !isSucceed {
|
||||||
|
log.Trace("auto-login cookie cleared: %s", uname)
|
||||||
|
ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubUrl)
|
||||||
|
ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubUrl)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
u, err := models.GetUserByName(uname)
|
||||||
|
if err != nil {
|
||||||
|
if !models.IsErrUserNotExist(err) {
|
||||||
|
return false, fmt.Errorf("GetUserByName: %v", err)
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, _ := ctx.GetSuperSecureCookie(
|
||||||
|
base.EncodeMd5(u.Rands+u.Passwd), setting.CookieRememberName); val != u.Name {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
isSucceed = true
|
||||||
|
ctx.Session.Set("uid", u.Id)
|
||||||
|
ctx.Session.Set("uname", u.Name)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func Toggle(options *ToggleOptions) macaron.Handler {
|
func Toggle(options *ToggleOptions) macaron.Handler {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
// Cannot view any page before installation.
|
// Cannot view any page before installation.
|
||||||
|
|
|
@ -197,6 +197,14 @@ func Contexter() macaron.Handler {
|
||||||
|
|
||||||
ctx.Data["PageStartTime"] = time.Now()
|
ctx.Data["PageStartTime"] = time.Now()
|
||||||
|
|
||||||
|
// Check auto-signin.
|
||||||
|
if sess.Get("uid") == nil {
|
||||||
|
if _, err := AutoSignIn(ctx); err != nil {
|
||||||
|
ctx.Handle(500, "AutoSignIn", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get user from session if logined.
|
// Get user from session if logined.
|
||||||
ctx.User, ctx.IsBasicAuth = auth.SignedInUser(ctx.Req.Request, ctx.Session)
|
ctx.User, ctx.IsBasicAuth = auth.SignedInUser(ctx.Req.Request, ctx.Session)
|
||||||
|
|
||||||
|
|
2
public/css/gogs.min.css
vendored
2
public/css/gogs.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -180,6 +180,14 @@
|
||||||
.avatar {
|
.avatar {
|
||||||
width: @comment-avatar-width;
|
width: @comment-avatar-width;
|
||||||
}
|
}
|
||||||
|
.tag {
|
||||||
|
color: #767676;
|
||||||
|
margin-top: 3px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
border: 1px solid rgba(0,0,0,0.1);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
.content {
|
.content {
|
||||||
margin-left: 4em;
|
margin-left: 4em;
|
||||||
.header {
|
.header {
|
||||||
|
|
|
@ -427,7 +427,7 @@ func ViewIssue(ctx *middleware.Context) {
|
||||||
ctx.Data["Title"] = issue.Name
|
ctx.Data["Title"] = issue.Name
|
||||||
|
|
||||||
if err = issue.GetPoster(); err != nil {
|
if err = issue.GetPoster(); err != nil {
|
||||||
ctx.Handle(500, "GetPoster: %v", err)
|
ctx.Handle(500, "GetPoster", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink))
|
issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink))
|
||||||
|
@ -441,7 +441,7 @@ func ViewIssue(ctx *middleware.Context) {
|
||||||
if ctx.IsSigned {
|
if ctx.IsSigned {
|
||||||
// Update issue-user.
|
// Update issue-user.
|
||||||
if err = issue.ReadBy(ctx.User.Id); err != nil {
|
if err = issue.ReadBy(ctx.User.Id); err != nil {
|
||||||
ctx.Handle(500, "ReadBy: %v", err)
|
ctx.Handle(500, "ReadBy", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,10 +475,35 @@ func ViewIssue(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
repo = ctx.Repo.Repository
|
||||||
|
tag models.CommentTag
|
||||||
|
ok bool
|
||||||
|
marked = make(map[int64]models.CommentTag)
|
||||||
|
comment *models.Comment
|
||||||
|
)
|
||||||
// Render comments.
|
// Render comments.
|
||||||
for i := range issue.Comments {
|
for _, comment = range issue.Comments {
|
||||||
if issue.Comments[i].Type == models.COMMENT_TYPE_COMMENT {
|
if comment.Type == models.COMMENT_TYPE_COMMENT {
|
||||||
issue.Comments[i].RenderedContent = string(base.RenderMarkdown([]byte(issue.Comments[i].Content), ctx.Repo.RepoLink))
|
comment.RenderedContent = string(base.RenderMarkdown([]byte(comment.Content), ctx.Repo.RepoLink))
|
||||||
|
|
||||||
|
// Check tag.
|
||||||
|
tag, ok = marked[comment.PosterID]
|
||||||
|
if ok {
|
||||||
|
comment.ShowTag = tag
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.IsOwnedBy(comment.PosterID) ||
|
||||||
|
(repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(comment.PosterID)) {
|
||||||
|
comment.ShowTag = models.COMMENT_TAG_OWNER
|
||||||
|
} else if comment.Poster.IsAdminOfRepo(repo) {
|
||||||
|
comment.ShowTag = models.COMMENT_TAG_ADMIN
|
||||||
|
} else if comment.PosterID == issue.PosterID {
|
||||||
|
comment.ShowTag = models.COMMENT_TAG_POSTER
|
||||||
|
}
|
||||||
|
|
||||||
|
marked[comment.PosterID] = comment.ShowTag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,49 +42,22 @@ func SignIn(ctx *middleware.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check auto-login.
|
// Check auto-login.
|
||||||
uname := ctx.GetCookie(setting.CookieUserName)
|
isSucceed, err := middleware.AutoSignIn(ctx)
|
||||||
if len(uname) == 0 {
|
|
||||||
ctx.HTML(200, SIGNIN)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
isSucceed := false
|
|
||||||
defer func() {
|
|
||||||
if !isSucceed {
|
|
||||||
log.Trace("auto-login cookie cleared: %s", uname)
|
|
||||||
ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubUrl)
|
|
||||||
ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubUrl)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
u, err := models.GetUserByName(uname)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !models.IsErrUserNotExist(err) {
|
ctx.Handle(500, "AutoSignIn", err)
|
||||||
ctx.Handle(500, "GetUserByName", err)
|
return
|
||||||
} else {
|
}
|
||||||
ctx.HTML(200, SIGNIN)
|
|
||||||
|
if isSucceed {
|
||||||
|
if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 {
|
||||||
|
ctx.SetCookie("redirect_to", "", -1, setting.AppSubUrl)
|
||||||
|
ctx.Redirect(redirectTo)
|
||||||
}
|
}
|
||||||
|
ctx.Redirect(setting.AppSubUrl + "/")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, _ := ctx.GetSuperSecureCookie(
|
ctx.HTML(200, SIGNIN)
|
||||||
base.EncodeMd5(u.Rands+u.Passwd), setting.CookieRememberName); val != u.Name {
|
|
||||||
ctx.HTML(200, SIGNIN)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
isSucceed = true
|
|
||||||
|
|
||||||
ctx.Session.Set("uid", u.Id)
|
|
||||||
ctx.Session.Set("uname", u.Name)
|
|
||||||
if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 {
|
|
||||||
ctx.SetCookie("redirect_to", "", -1, setting.AppSubUrl)
|
|
||||||
ctx.Redirect(redirectTo)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Redirect(setting.AppSubUrl + "/")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SignInPost(ctx *middleware.Context, form auth.SignInForm) {
|
func SignInPost(ctx *middleware.Context, form auth.SignInForm) {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.6.4.0813 Beta
|
0.6.4.0814 Beta
|
|
@ -22,8 +22,8 @@
|
||||||
<div class="twelve wide column comment-list">
|
<div class="twelve wide column comment-list">
|
||||||
<ui class="ui comments">
|
<ui class="ui comments">
|
||||||
<div class="comment">
|
<div class="comment">
|
||||||
<a class="avatar" href="{{.SignedUser.HomeLink}}">
|
<a class="avatar" href="{{.Issue.Poster.HomeLink}}">
|
||||||
<img src="{{.SignedUser.AvatarLink}}">
|
<img src="{{.Issue.Poster.AvatarLink}}">
|
||||||
</a>
|
</a>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="ui top attached header">
|
<div class="ui top attached header">
|
||||||
|
@ -63,6 +63,17 @@
|
||||||
<div class="ui top attached header">
|
<div class="ui top attached header">
|
||||||
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a> {{$.i18n.Tr "repo.issues.commented_at" .HashTag $createdStr | Safe}}</span>
|
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a> {{$.i18n.Tr "repo.issues.commented_at" .HashTag $createdStr | Safe}}</span>
|
||||||
<div class="ui right actions">
|
<div class="ui right actions">
|
||||||
|
{{if gt .ShowTag 0}}
|
||||||
|
<div class="tag">
|
||||||
|
{{if eq .ShowTag 1}}
|
||||||
|
{{$.i18n.Tr "repo.issues.poster"}}
|
||||||
|
{{else if eq .ShowTag 2}}
|
||||||
|
{{$.i18n.Tr "repo.issues.admin"}}
|
||||||
|
{{else if eq .ShowTag 3}}
|
||||||
|
{{$.i18n.Tr "repo.issues.owner"}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui attached segment markdown">
|
<div class="ui attached segment markdown">
|
||||||
|
@ -89,7 +100,7 @@
|
||||||
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
|
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
|
||||||
<img src="{{.Poster.AvatarLink}}">
|
<img src="{{.Poster.AvatarLink}}">
|
||||||
</a>
|
</a>
|
||||||
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a> {{$.i18n.Tr "repo.issues.closed_at" .EventTag $createdStr | Safe}}</span>
|
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a> {{$.i18n.Tr "repo.issues.reopened_at" .EventTag $createdStr | Safe}}</span>
|
||||||
</div>
|
</div>
|
||||||
{{else if eq .Type 2}}
|
{{else if eq .Type 2}}
|
||||||
<div class="event">
|
<div class="event">
|
||||||
|
@ -103,6 +114,7 @@
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{if .IsSigned}}
|
||||||
<div class="comment form">
|
<div class="comment form">
|
||||||
<a class="avatar" href="{{.SignedUser.HomeLink}}">
|
<a class="avatar" href="{{.SignedUser.HomeLink}}">
|
||||||
<img src="{{.SignedUser.AvatarLink}}">
|
<img src="{{.SignedUser.AvatarLink}}">
|
||||||
|
@ -129,6 +141,12 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="ui warning message">
|
||||||
|
<a href="/user/sign_up" class="ui green button">{{.i18n.Tr "repo.issues.sign_up_for_free"}}</a>
|
||||||
|
{{.i18n.Tr "repo.issues.sign_in_require_desc" "/user/login" | Safe}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
</ui>
|
</ui>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue