]> source.dussan.org Git - gitea.git/commitdiff
Add notify watcher action
authorUnknown <joe2010xtmf@163.com>
Tue, 25 Mar 2014 18:04:57 +0000 (14:04 -0400)
committerUnknown <joe2010xtmf@163.com>
Tue, 25 Mar 2014 18:04:57 +0000 (14:04 -0400)
README.md
README_ZH.md
models/action.go
models/issue.go
models/repo.go
modules/base/tool.go
routers/repo/issue.go
templates/issue/list.tmpl

index a39a92a32214f6a5c245c90c9014757ff4520f3e..d99031c6870027d06f922d59a3449984de45e76e 100644 (file)
--- 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).
index 440f952f861483e9fa72439cc664128671f53a7e..0a4d3bdc16ef35baac5bed240ba29f540a5df051 100644 (file)
@@ -28,7 +28,7 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依
 - 创建/删除/关注公开仓库
 - 用户个人信息页面
 - 仓库浏览器
-- Gravatar 支持
+- Gravatar 以及缓存支持
 - 邮件服务(注册)
 - 管理员面板
 - 支持 MySQL、PostgreSQL 以及 SQLite3(仅限二进制版本)
index dffc0e537e46f5a5743ef490e0837a8a649a98ce..edf1bf58f972531bb6fc47baa9bafc559e70742b 100644 (file)
@@ -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)
index fe43a94b59a5c7002e71b0899fc7e174ae02cd45..2bdd083d9029aff87006434d47ed134f7a7e0399 100644 (file)
@@ -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.
index d5f9be72acf7faef336648cd1aca987b7a634c1f..824d5ba0ca5f2d59210d06e39e8e763a5cb43d4b 100644 (file)
@@ -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})
index c23f5de6cc7d346585ff9731778b5c6c47828eb6..8f38d492a57d5eb1de504383bbcac00998088684 100644 (file)
@@ -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"
        }
index 4e8324605c531d670a5d9da0b941036c4617602b..fc5bb986438926c34f417e998be8b5cd39cee51f 100644 (file)
@@ -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) {
index 685eaeeb96f6faea8e6c8aeddcecb44114ea0bb6..0df68838670e3382f19af927481d6509464ffe88 100644 (file)
@@ -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>
         <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>