summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--README_ZH.md2
-rw-r--r--models/action.go29
-rw-r--r--models/issue.go1
-rw-r--r--models/repo.go71
-rw-r--r--modules/base/tool.go21
-rw-r--r--routers/repo/issue.go40
-rw-r--r--templates/issue/list.tmpl46
8 files changed, 118 insertions, 94 deletions
diff --git a/README.md b/README.md
index a39a92a322..d99031c687 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ More importantly, Gogs only needs one binary to setup your own project hosting o
- Create/delete/watch public repository.
- User profile page.
- Repository viewer.
-- Gravatar support.
+- Gravatar and cache support.
- Mail service(register).
- Administration panel.
- Supports MySQL, PostgreSQL and SQLite3(binary release only).
diff --git a/README_ZH.md b/README_ZH.md
index 440f952f86..0a4d3bdc16 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -28,7 +28,7 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依
- 创建/删除/关注公开仓库
- 用户个人信息页面
- 仓库浏览器
-- Gravatar 支持
+- Gravatar 以及缓存支持
- 邮件服务(注册)
- 管理员面板
- 支持 MySQL、PostgreSQL 以及 SQLite3(仅限二进制版本)
diff --git a/models/action.go b/models/action.go
index dffc0e537e..edf1bf58f9 100644
--- a/models/action.go
+++ b/models/action.go
@@ -19,6 +19,7 @@ const (
OP_STAR_REPO
OP_FOLLOW_REPO
OP_COMMIT_REPO
+ OP_CREATE_ISSUE
OP_PULL_REQUEST
)
@@ -67,34 +68,10 @@ func CommitRepoAction(userId int64, userName string,
return err
}
- // Add feeds for user self and all watchers.
- watches, err := GetWatches(repoId)
- if err != nil {
- log.Error("action.CommitRepoAction(get watches): %d/%s", userId, repoName)
+ if err = NotifyWatchers(userId, repoId, OP_COMMIT_REPO, userName, repoName, refName, string(bs)); err != nil {
+ log.Error("action.CommitRepoAction(notify watchers): %d/%s", userId, repoName)
return err
}
- watches = append(watches, Watch{UserId: userId})
-
- for i := range watches {
- if userId == watches[i].UserId && i > 0 {
- continue // Do not add twice in case author watches his/her repository.
- }
-
- _, err = orm.InsertOne(&Action{
- UserId: watches[i].UserId,
- ActUserId: userId,
- ActUserName: userName,
- OpType: OP_COMMIT_REPO,
- Content: string(bs),
- RepoId: repoId,
- RepoName: repoName,
- RefName: refName,
- })
- if err != nil {
- log.Error("action.CommitRepoAction(notify watches): %d/%s", userId, repoName)
- return err
- }
- }
// Update repository last update time.
repo, err := GetRepositoryByName(userId, repoName)
diff --git a/models/issue.go b/models/issue.go
index fe43a94b59..2bdd083d90 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -23,6 +23,7 @@ type Issue struct {
Name string
RepoId int64 `xorm:"index"`
PosterId int64
+ Poster *User `xorm:"-"`
MilestoneId int64
AssigneeId int64
IsPull bool // Indicates whether is a pull request or not.
diff --git a/models/repo.go b/models/repo.go
index d5f9be72ac..824d5ba0ca 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -262,27 +262,27 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep
}
/*
- // hook/post-update
- pu, err := os.OpenFile(filepath.Join(repoPath, "hooks", "post-update"), os.O_CREATE|os.O_WRONLY, 0777)
- if err != nil {
- return err
- }
- defer pu.Close()
- // TODO: Windows .bat
- if _, err = pu.WriteString(fmt.Sprintf("#!/usr/bin/env bash\n%s update\n", appPath)); err != nil {
- return err
- }
+ // hook/post-update
+ pu, err := os.OpenFile(filepath.Join(repoPath, "hooks", "post-update"), os.O_CREATE|os.O_WRONLY, 0777)
+ if err != nil {
+ return err
+ }
+ defer pu.Close()
+ // TODO: Windows .bat
+ if _, err = pu.WriteString(fmt.Sprintf("#!/usr/bin/env bash\n%s update\n", appPath)); err != nil {
+ return err
+ }
- // hook/post-update
- pu2, err := os.OpenFile(filepath.Join(repoPath, "hooks", "post-receive"), os.O_CREATE|os.O_WRONLY, 0777)
- if err != nil {
- return err
- }
- defer pu2.Close()
- // TODO: Windows .bat
- if _, err = pu2.WriteString("#!/usr/bin/env bash\ngit update-server-info\n"); err != nil {
- return err
- }
+ // hook/post-update
+ pu2, err := os.OpenFile(filepath.Join(repoPath, "hooks", "post-receive"), os.O_CREATE|os.O_WRONLY, 0777)
+ if err != nil {
+ return err
+ }
+ defer pu2.Close()
+ // TODO: Windows .bat
+ if _, err = pu2.WriteString("#!/usr/bin/env bash\ngit update-server-info\n"); err != nil {
+ return err
+ }
*/
// Initialize repository according to user's choice.
@@ -506,6 +506,37 @@ func GetWatches(repoId int64) ([]Watch, error) {
return watches, err
}
+// NotifyWatchers creates batch of actions for every watcher.
+func NotifyWatchers(userId, repoId int64, opType int, userName, repoName, refName, content string) error {
+ // Add feeds for user self and all watchers.
+ watches, err := GetWatches(repoId)
+ if err != nil {
+ return errors.New("repo.NotifyWatchers(get watches): " + err.Error())
+ }
+ watches = append(watches, Watch{UserId: userId})
+
+ for i := range watches {
+ if userId == watches[i].UserId && i > 0 {
+ continue // Do not add twice in case author watches his/her repository.
+ }
+
+ _, err = orm.InsertOne(&Action{
+ UserId: watches[i].UserId,
+ ActUserId: userId,
+ ActUserName: userName,
+ OpType: opType,
+ Content: content,
+ RepoId: repoId,
+ RepoName: repoName,
+ RefName: refName,
+ })
+ if err != nil {
+ return errors.New("repo.NotifyWatchers(create action): " + err.Error())
+ }
+ }
+ return nil
+}
+
// IsWatching checks if user has watched given repository.
func IsWatching(userId, repoId int64) bool {
has, _ := orm.Get(&Watch{0, repoId, userId})
diff --git a/modules/base/tool.go b/modules/base/tool.go
index c23f5de6cc..8f38d492a5 100644
--- a/modules/base/tool.go
+++ b/modules/base/tool.go
@@ -486,15 +486,19 @@ func ActionIcon(opType int) string {
return "plus-circle"
case 5: // Commit repository.
return "arrow-circle-o-right"
+ case 6: // Create issue.
+ return "exclamation-circle"
default:
return "invalid type"
}
}
const (
- TPL_CREATE_REPO = `<a href="/user/%s">%s</a> created repository <a href="/%s/%s">%s</a>`
- TPL_COMMIT_REPO = `<a href="/user/%s">%s</a> pushed to <a href="/%s/%s/tree/%s">%s</a> at <a href="/%s/%s">%s/%s</a>%s`
- TPL_COMMIT_REPO_LI = `<div><img id="gogs-user-avatar-commit" src="%s?s=16" alt="user-avatar" title="username"/> <a href="/%s/%s/commit/%s">%s</a> %s</div>`
+ TPL_CREATE_REPO = `<a href="/user/%s">%s</a> created repository <a href="/%s">%s</a>`
+ TPL_COMMIT_REPO = `<a href="/user/%s">%s</a> pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a>%s`
+ TPL_COMMIT_REPO_LI = `<div><img src="%s?s=16" alt="user-avatar"/> <a href="/%s/commit/%s">%s</a> %s</div>`
+ TPL_CREATE_Issue = `<a href="/user/%s">%s</a> opened issue <a href="/%s/issues/%s">%s#%s</a>
+<div><img src="%s?s=16" alt="user-avatar"/> %s</div>`
)
type PushCommits struct {
@@ -507,11 +511,12 @@ type PushCommits struct {
func ActionDesc(act Actioner, avatarLink string) string {
actUserName := act.GetActUserName()
repoName := act.GetRepoName()
+ repoLink := actUserName + "/" + repoName
branch := act.GetBranch()
content := act.GetContent()
switch act.GetOpType() {
case 1: // Create repository.
- return fmt.Sprintf(TPL_CREATE_REPO, actUserName, actUserName, actUserName, repoName, repoName)
+ return fmt.Sprintf(TPL_CREATE_REPO, actUserName, actUserName, repoLink, repoName)
case 5: // Commit repository.
var push *PushCommits
if err := json.Unmarshal([]byte(content), &push); err != nil {
@@ -519,13 +524,17 @@ func ActionDesc(act Actioner, avatarLink string) string {
}
buf := bytes.NewBuffer([]byte("\n"))
for _, commit := range push.Commits {
- buf.WriteString(fmt.Sprintf(TPL_COMMIT_REPO_LI, avatarLink, actUserName, repoName, commit[0], commit[0][:7], commit[1]) + "\n")
+ buf.WriteString(fmt.Sprintf(TPL_COMMIT_REPO_LI, avatarLink, repoLink, commit[0], commit[0][:7], commit[1]) + "\n")
}
if push.Len > 3 {
buf.WriteString(fmt.Sprintf(`<div><a href="/%s/%s/commits/%s">%d other commits >></a></div>`, actUserName, repoName, branch, push.Len))
}
- return fmt.Sprintf(TPL_COMMIT_REPO, actUserName, actUserName, actUserName, repoName, branch, branch, actUserName, repoName, actUserName, repoName,
+ return fmt.Sprintf(TPL_COMMIT_REPO, actUserName, actUserName, repoLink, branch, branch, repoLink, repoLink,
buf.String())
+ case 6: // Create issue.
+ infos := strings.SplitN(content, "|", 2)
+ return fmt.Sprintf(TPL_CREATE_Issue, actUserName, actUserName, repoLink, infos[0], repoLink, infos[0],
+ avatarLink, infos[1])
default:
return "invalid type"
}
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index 4e8324605c..fc5bb98643 100644
--- a/routers/repo/issue.go
+++ b/routers/repo/issue.go
@@ -23,13 +23,33 @@ func Issues(ctx *middleware.Context, params martini.Params) {
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,
+ // Get issues.
+ 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
}
+
+ var closedCount int
+ // Get posters.
+ for i := range issues {
+ u, err := models.GetUserById(issues[i].PosterId)
+ if err != nil {
+ ctx.Handle(200, "issue.Issues(get poster): %v", err)
+ return
+ }
+
+ if issues[i].IsClosed {
+ closedCount++
+ }
+ issues[i].Poster = u
+ }
+
+ ctx.Data["Issues"] = issues
+ ctx.Data["IssueCount"] = len(issues)
+ ctx.Data["OpenCount"] = len(issues) - closedCount
+ ctx.Data["ClosedCount"] = closedCount
ctx.HTML(200, "issue/list")
}
@@ -54,12 +74,20 @@ func CreateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat
issue, err := models.CreateIssue(ctx.User.Id, ctx.Repo.Repository.Id, form.MilestoneId, form.AssigneeId,
form.IssueName, form.Labels, form.Content, false)
- if err == nil {
- log.Trace("%d Issue created: %d", ctx.Repo.Repository.Id, issue.Id)
- ctx.Redirect(fmt.Sprintf("/%s/%s/issues/%d", params["username"], params["reponame"], issue.Index))
+ if err != nil {
+ ctx.Handle(200, "issue.CreateIssue", err)
return
}
- ctx.Handle(200, "issue.CreateIssue", err)
+
+ // Notify watchers.
+ if err = models.NotifyWatchers(ctx.User.Id, ctx.Repo.Repository.Id, models.OP_CREATE_ISSUE,
+ ctx.User.Name, ctx.Repo.Repository.Name, "", fmt.Sprintf("%d|%s", issue.Index, issue.Name)); err != nil {
+ ctx.Handle(200, "issue.CreateIssue", err)
+ return
+ }
+
+ log.Trace("%d Issue created: %d", ctx.Repo.Repository.Id, issue.Id)
+ ctx.Redirect(fmt.Sprintf("/%s/%s/issues/%d", params["username"], params["reponame"], issue.Index))
}
func ViewIssue(ctx *middleware.Context, params martini.Params) {
diff --git a/templates/issue/list.tmpl b/templates/issue/list.tmpl
index 685eaeeb96..0df6883867 100644
--- a/templates/issue/list.tmpl
+++ b/templates/issue/list.tmpl
@@ -6,7 +6,7 @@
<div id="issue">
<div class="col-md-3 filter-list">
<ul class="list-unstyled">
- <li><a href="#" class="active">All Issues <strong class="pull-right">10</strong></a></li>
+ <li><a href="#" class="active">All Issues <strong class="pull-right">{{.IssueCount}}</strong></a></li>
<li><a href="#">My Issues</a></li>
<li><a href="#">Mentioned</a></li>
</ul>
@@ -14,46 +14,24 @@
<div class="col-md-9">
<div class="filter-option">
<div class="btn-group">
- <a class="btn btn-default active issue-open" href="#">27 Open</a>
- <a class="btn btn-default issue-close" href="#">Close 128</a>
+ <a class="btn btn-default active issue-open" href="#">{{.OpenCount}} Open</a>
+ <a class="btn btn-default issue-close" href="#">{{.ClosedCount}} Closed</a>
</div>
</div>
<div class="issues list-group">
{{range .Issues}}
- <div class="list-group-item issue-item" id="{{.Id}}"></div>
- {{end}}
- </div>
- <div class="issues list-group">
- <div class="list-group-item unread issue-item" id="issue-id">
- <span class="number pull-right">#123</span>
- <h5 class="title"><a href="#">Bug: When running tests after generating a beego app, templates do not load.</a></h5>
- <p class="info">
- <span class="author"><img class="avatar" src="http://tp2.sinaimg.cn/5068084885/50/40050297589/1" alt="" width="20"/>
- <a href="#">Obama</a></span>
- <span class="time">3 days ago</span>
- <span class="comment"><i class="fa fa-comments"></i> 3</span>
- </p>
- </div>
- <div class="list-group-item issue-item" id="issue-id2">
- <span class="number pull-right">#123</span>
- <h5 class="title"><a href="#">Bug: When running tests after generating a beego app, templates do not load.</a></h5>
+ <div class="list-group-item issue-item" id="{{.Id}}">
+ <span class="number pull-right">#{{.Index}}</span>
+ <h5 class="title"><a href="/{{$.RepositoryLink}}/issues/{{.Index}}">{{.Name}}</a></h5>
<p class="info">
- <span class="author"><img class="avatar" src="http://tp2.sinaimg.cn/5068084885/50/40050297589/1" alt="" width="20"/>
- <a href="#">Obama</a></span>
- <span class="time">3 days ago</span>
- <span class="comment"><i class="fa fa-comments"></i> 3</span>
- </p>
- </div>
- <div class="list-group-item issue-item" id="issue-id3">
- <span class="number pull-right">#123</span>
- <h5 class="title"><a href="#">Bug: When running tests after generating a beego app, templates do not load.</a></h5>
- <p class="info">
- <span class="author"><img class="avatar" src="http://tp2.sinaimg.cn/5068084885/50/40050297589/1" alt="" width="20"/>
- <a href="#">Obama</a></span>
- <span class="time">3 days ago</span>
- <span class="comment"><i class="fa fa-comments"></i> 3</span>
+ <span class="author"><img class="avatar" src="{{.Poster.AvatarLink}}" alt="" width="20"/>
+ <a href="/user/{{.Poster.Name}}">{{.Poster.Name}}</a></span>
+ <span class="time">{{TimeSince .Created}}</span>
+ <span class="comment"><i class="fa fa-comments"></i> {{.NumComments}}</span>
</p>
</div>
+ {{end}}
+ </div>
</div>
</div>
</div>