From ecd59deb27437963ea0589f07a97d397a0e5e1ef Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 1 Oct 2015 15:17:27 +0200 Subject: [PATCH] implemented #1721: see users who forked/starred/watched a repository --- cmd/web.go | 3 + models/repo.go | 30 ++++++++++ public/ng/css/gogs.css | 88 +++++++++++++++++++++++++++++ public/ng/less/gogs/repository.less | 81 ++++++++++++++++++++++++++ routers/repo/forks.go | 37 ++++++++++++ routers/repo/stars.go | 44 +++++++++++++++ routers/repo/watchers.go | 44 +++++++++++++++ templates/repo/forks.tmpl | 27 +++++++++ templates/repo/stars.tmpl | 61 ++++++++++++++++++++ templates/repo/watchers.tmpl | 61 ++++++++++++++++++++ 10 files changed, 476 insertions(+) create mode 100644 routers/repo/forks.go create mode 100644 routers/repo/stars.go create mode 100644 routers/repo/watchers.go create mode 100644 templates/repo/forks.tmpl create mode 100644 templates/repo/stars.tmpl create mode 100644 templates/repo/watchers.tmpl diff --git a/cmd/web.go b/cmd/web.go index e78cb13a37..126a86a35f 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -514,6 +514,9 @@ func runWeb(ctx *cli.Context) { m.Get("/labels/", repo.RetrieveLabels, repo.Labels) m.Get("/milestones", repo.Milestones) m.Get("/branches", repo.Branches) + m.Get("/stars/?:index", middleware.RepoRef(), repo.Stars) + m.Get("/watchers/?:index", middleware.RepoRef(), repo.Watchers) + m.Get("/forks", middleware.RepoRef(), repo.Forks) m.Get("/archive/*", repo.Download) m.Group("/pulls/:index", func() { diff --git a/models/repo.go b/models/repo.go index f3a32d6877..fc155fa5f5 100644 --- a/models/repo.go +++ b/models/repo.go @@ -46,6 +46,9 @@ var ( var ( Gitignores, Licenses, Readmes []string + + // Maximum items per page in forks, watchers and stars of a repo + ItemsPerPage = 3 ) func LoadRepoConfig() { @@ -1612,6 +1615,16 @@ func GetWatchers(rid int64) ([]*Watch, error) { return getWatchers(x, rid) } +// Repository.GetWatchers returns all users watching given repository. +func (repo *Repository) GetWatchers(offset int) ([]*User, error) { + users := make([]*User, 0, 10) + offset = (offset - 1) * ItemsPerPage + + err := x.Limit(ItemsPerPage, offset).Where("repo_id=?", repo.ID).Join("LEFT", "watch", "user.id=watch.user_id").Find(&users) + + return users, err +} + func notifyWatchers(e Engine, act *Action) error { // Add feeds for user self and all watchers. watches, err := getWatchers(e, act.RepoID) @@ -1689,6 +1702,15 @@ func IsStaring(uid, repoId int64) bool { return has } +func (repo *Repository) GetStars(offset int) ([]*User, error) { + users := make([]*User, 0, 10) + offset = (offset - 1) * ItemsPerPage + + err := x.Limit(ItemsPerPage, offset).Where("repo_id=?", repo.ID).Join("LEFT", "star", "user.id=star.uid").Find(&users) + + return users, err +} + // ___________ __ // \_ _____/__________| | __ // | __)/ _ \_ __ \ |/ / @@ -1756,3 +1778,11 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit return repo, sess.Commit() } + +func (repo *Repository) GetForks() ([]*Repository, error) { + forks := make([]*Repository, 0, 10) + + err := x.Find(&forks, &Repository{ForkID: repo.ID}) + + return forks, err +} diff --git a/public/ng/css/gogs.css b/public/ng/css/gogs.css index 73273d4f7e..caecfe2579 100644 --- a/public/ng/css/gogs.css +++ b/public/ng/css/gogs.css @@ -1947,6 +1947,94 @@ The register and sign-in page style #release #release-new-form { padding-top: 15px; } +#stars h4, +#watchers h4, +#forks h4 { + font-size: 18px; + padding-bottom: 20px; + text-transform: capitalize; + border-bottom: 1px solid #DDD; +} +#stars h3, +#watchers h3, +#forks h3 { + margin: -4px 0 0 0; + padding: 0; +} +#stars .avatar, +#watchers .avatar, +#forks .avatar { + width: 75px; + height: 75px; + float: left; + display: block; + margin-right: 10px; +} +#stars .avatar-small, +#watchers .avatar-small, +#forks .avatar-small { + width: 24px; + height: 24px; + float: left; + display: block; + margin-right: 10px; +} +#stars ol, +#watchers ol, +#forks ol { + margin-top: 10px; + list-style: none; + width: 100%; + overflow: hidden; +} +#stars li, +#watchers li, +#forks li { + width: 32.25%; + margin: 10px 10px 10px 0; + border-bottom: 1px solid #DDD; + float: left; + padding-bottom: 10px; +} +#stars .pagination, +#watchers .pagination, +#forks .pagination { + width: 100%; + text-align: center; + text-transform: capitalize; +} +#stars .pagination a, +#watchers .pagination a, +#forks .pagination a { + border-radius: 3px; + border: 1px solid #399ADE; + padding: 8px; + margin: 0; +} +#stars .pagination .active, +#watchers .pagination .active, +#forks .pagination .active { + border-radius: 3px; + border: 1px solid #399ADE; + background: #399ADE; + cursor: default; + padding: 8px; + margin: 0; + color: #FFFFFF; +} +#stars .pagination .disabled, +#watchers .pagination .disabled, +#forks .pagination .disabled { + border-radius: 3px; + border: 1px solid #DDD; + color: #D3D3D3; + cursor: default; + padding: 8px; + margin: 0; +} +#forks p { + padding: 5px 0; +} #admin-wrapper, #setting-wrapper { padding-bottom: 100px; diff --git a/public/ng/less/gogs/repository.less b/public/ng/less/gogs/repository.less index 6b0a927e8f..c403b51fec 100644 --- a/public/ng/less/gogs/repository.less +++ b/public/ng/less/gogs/repository.less @@ -795,3 +795,84 @@ padding-top: 15px; } } + +#stars, #watchers, #forks { + h4 { + font-size: 18px; + padding-bottom: 20px; + text-transform: capitalize; + border-bottom: 1px solid #DDD; + } + + h3 { + margin: -4px 0 0 0; + padding: 0; + } + + .avatar { + width: 75px; + height: 75px; + float: left; + display: block; + margin-right: 10px; + } + + .avatar-small { + width: 24px; + height: 24px; + float: left; + display: block; + margin-right: 10px; + } + + ol { + margin-top: 10px; + list-style: none; + width: 100%; + overflow: hidden; + } + + li { + width: 32.25%; + margin: 10px 10px 10px 0; + border-bottom: 1px solid #DDD; + float: left; + padding-bottom: 10px; + } + + .pagination { + width: 100%; + text-align: center; + text-transform: capitalize; + + a { + border-radius: 3px; + border: 1px solid #399ADE; + padding: 8px; + margin: 0; + } + + .active { + border-radius: 3px; + border: 1px solid #399ADE; + background: #399ADE; + cursor: default; + padding: 8px; + margin: 0; + color: #FFFFFF; + } + + .disabled { + border-radius: 3px; + border: 1px solid #DDD; + color: #D3D3D3; + cursor: default; + padding: 8px; + margin: 0; + } + } +} + +#forks p { + padding: 5px 0; +} diff --git a/routers/repo/forks.go b/routers/repo/forks.go new file mode 100644 index 0000000000..099f0cc4f6 --- /dev/null +++ b/routers/repo/forks.go @@ -0,0 +1,37 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + "fmt" + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/middleware" +) + +const ( + FORKS base.TplName = "repo/forks" +) + +func Forks(ctx *middleware.Context) { + ctx.Data["Title"] = ctx.Tr("repos.forks") + + forks, err := ctx.Repo.Repository.GetForks() + + if err != nil { + ctx.Handle(500, "GetForks", err) + return + } + + for _, fork := range forks { + if err = fork.GetOwner(); err != nil { + ctx.Handle(500, "GetOwner", fmt.Errorf("%d: %v", fork.ID, err)) + return + } + } + + ctx.Data["Forks"] = forks + + ctx.HTML(200, FORKS) +} diff --git a/routers/repo/stars.go b/routers/repo/stars.go new file mode 100644 index 0000000000..ffccd1765b --- /dev/null +++ b/routers/repo/stars.go @@ -0,0 +1,44 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + "github.com/Unknwon/paginater" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/middleware" +) + +const ( + STARS base.TplName = "repo/stars" +) + +func Stars(ctx *middleware.Context) { + ctx.Data["Title"] = ctx.Tr("repos.stars") + + page := ctx.ParamsInt(":index") + if page <= 0 { + page = 1 + } + + ctx.Data["Page"] = paginater.New(ctx.Repo.Repository.NumStars, models.ItemsPerPage, page, 5) + + stars, err := ctx.Repo.Repository.GetStars(ctx.ParamsInt(":index")) + + if err != nil { + ctx.Handle(500, "GetStars", err) + return + } + + if (ctx.ParamsInt(":index")-1)*models.ItemsPerPage > ctx.Repo.Repository.NumStars { + ctx.Handle(404, "ctx.Repo.Repository.NumStars", nil) + return + } + + ctx.Data["Stars"] = stars + + ctx.HTML(200, STARS) +} diff --git a/routers/repo/watchers.go b/routers/repo/watchers.go new file mode 100644 index 0000000000..8765b18376 --- /dev/null +++ b/routers/repo/watchers.go @@ -0,0 +1,44 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + "github.com/Unknwon/paginater" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/middleware" +) + +const ( + WATCHERS base.TplName = "repo/watchers" +) + +func Watchers(ctx *middleware.Context) { + ctx.Data["Title"] = ctx.Tr("repos.watches") + + page := ctx.ParamsInt(":index") + if page <= 0 { + page = 1 + } + + ctx.Data["Page"] = paginater.New(ctx.Repo.Repository.NumWatches, models.ItemsPerPage, page, 5) + + watchers, err := ctx.Repo.Repository.GetWatchers(ctx.ParamsInt(":index")) + + if err != nil { + ctx.Handle(500, "GetWatchers", err) + return + } + + if (ctx.ParamsInt(":index")-1)*models.ItemsPerPage > ctx.Repo.Repository.NumWatches { + ctx.Handle(404, "ctx.Repo.Repository.NumWatches", nil) + return + } + + ctx.Data["Watchers"] = watchers + + ctx.HTML(200, WATCHERS) +} diff --git a/templates/repo/forks.tmpl b/templates/repo/forks.tmpl new file mode 100644 index 0000000000..d1fd0320c0 --- /dev/null +++ b/templates/repo/forks.tmpl @@ -0,0 +1,27 @@ +{{template "ng/base/head" .}} +{{template "ng/base/header" .}} +
+ {{template "repo/header_old" .}} +
+
+
+

+ {{.i18n.Tr "repos.forks"}} +

+ +
    + {{range .Forks}} +

    + + {{.Owner.Name}} + / + {{.Name}} +

    + {{end}} +
+
+ + {{template "repo/sidebar" .}} +
+
+{{template "ng/base/footer" .}} diff --git a/templates/repo/stars.tmpl b/templates/repo/stars.tmpl new file mode 100644 index 0000000000..afdb55d332 --- /dev/null +++ b/templates/repo/stars.tmpl @@ -0,0 +1,61 @@ +{{template "ng/base/head" .}} +{{template "ng/base/header" .}} +
+ {{template "repo/header_old" .}} +
+
+
+

+ {{.i18n.Tr "repos.stars"}} +

+ +
    + {{range .Stars}} +
  1. + + + +

    {{.Name}}

    +
    + +

    + {{if .Website}} + {{.Website}} + {{else if .Location}} + {{.Location}} + {{else}} + {{$.i18n.Tr "user.join_on"}} {{DateFmtShort .Created}} + {{end}} +

    +
  2. + {{end}} +
+ + {{with .Page}} + {{if gt .TotalPages 1}} + + {{end}} + {{end}} +
+
+ + {{template "repo/sidebar" .}} +
+
+{{template "ng/base/footer" .}} diff --git a/templates/repo/watchers.tmpl b/templates/repo/watchers.tmpl new file mode 100644 index 0000000000..3e5db820ca --- /dev/null +++ b/templates/repo/watchers.tmpl @@ -0,0 +1,61 @@ +{{template "ng/base/head" .}} +{{template "ng/base/header" .}} +
+ {{template "repo/header_old" .}} +
+
+
+

+ {{.i18n.Tr "repos.watches"}} +

+ +
    + {{range .Watchers}} +
  1. + + + +

    {{.Name}}

    +
    + +

    + {{if .Website}} + {{.Website}} + {{else if .Location}} + {{.Location}} + {{else}} + {{$.i18n.Tr "user.join_on"}} {{DateFmtShort .Created}} + {{end}} +

    +
  2. + {{end}} +
+ + {{with .Page}} + {{if gt .TotalPages 1}} + + {{end}} + {{end}} +
+
+ + {{template "repo/sidebar" .}} +
+
+{{template "ng/base/footer" .}}