Browse Source
#1692 APIs: Users Followers
#1692 APIs: Users Followers
- User profile un/follow - List user's followers/followingrelease/v0.9
27 changed files with 636 additions and 266 deletions
-
2.bra.toml
-
2Makefile
-
4README.md
-
1README_ZH.md
-
12cmd/web.go
-
4conf/locale/locale_en-US.ini
-
2gogs.go
-
41models/issue.go
-
145models/user.go
-
4modules/bindata/bindata.go
-
3modules/middleware/context.go
-
67public/css/gogs.css
-
59public/less/_repository.less
-
31public/less/_user.less
-
19routers/api/v1/api.go
-
121routers/api/v1/user/followers.go
-
6routers/repo/repo.go
-
12routers/repo/view.go
-
63routers/user/home.go
-
145routers/user/profile.go
-
2templates/.VERSION
-
47templates/repo/user_cards.tmpl
-
52templates/repo/watchers.tmpl
-
6templates/user/meta/followers.tmpl
-
25templates/user/meta/header.tmpl
-
0templates/user/meta/stars.tmpl
-
27templates/user/profile.tmpl
4
modules/bindata/bindata.go
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,121 @@ |
|||
// Copyright 2015 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 user |
|||
|
|||
import ( |
|||
api "github.com/gogits/go-gogs-client" |
|||
|
|||
"github.com/gogits/gogs/models" |
|||
"github.com/gogits/gogs/modules/middleware" |
|||
"github.com/gogits/gogs/routers/api/v1/convert" |
|||
) |
|||
|
|||
func responseApiUsers(ctx *middleware.Context, users []*models.User) { |
|||
apiUsers := make([]*api.User, len(users)) |
|||
for i := range users { |
|||
apiUsers[i] = convert.ToApiUser(users[i]) |
|||
} |
|||
ctx.JSON(200, &apiUsers) |
|||
} |
|||
|
|||
func listUserFollowers(ctx *middleware.Context, u *models.User) { |
|||
users, err := u.GetFollowers(ctx.QueryInt("page")) |
|||
if err != nil { |
|||
ctx.APIError(500, "GetUserFollowers", err) |
|||
return |
|||
} |
|||
responseApiUsers(ctx, users) |
|||
} |
|||
|
|||
func ListMyFollowers(ctx *middleware.Context) { |
|||
listUserFollowers(ctx, ctx.User) |
|||
} |
|||
|
|||
// https://github.com/gogits/go-gogs-client/wiki/Users-Followers#list-followers-of-a-user
|
|||
func ListFollowers(ctx *middleware.Context) { |
|||
u := GetUserByParams(ctx) |
|||
if ctx.Written() { |
|||
return |
|||
} |
|||
listUserFollowers(ctx, u) |
|||
} |
|||
|
|||
func listUserFollowing(ctx *middleware.Context, u *models.User) { |
|||
users, err := u.GetFollowing(ctx.QueryInt("page")) |
|||
if err != nil { |
|||
ctx.APIError(500, "GetFollowing", err) |
|||
return |
|||
} |
|||
responseApiUsers(ctx, users) |
|||
} |
|||
|
|||
func ListMyFollowing(ctx *middleware.Context) { |
|||
listUserFollowing(ctx, ctx.User) |
|||
} |
|||
|
|||
// https://github.com/gogits/go-gogs-client/wiki/Users-Followers#list-users-followed-by-another-user
|
|||
func ListFollowing(ctx *middleware.Context) { |
|||
u := GetUserByParams(ctx) |
|||
if ctx.Written() { |
|||
return |
|||
} |
|||
listUserFollowing(ctx, u) |
|||
} |
|||
|
|||
func checkUserFollowing(ctx *middleware.Context, u *models.User, followID int64) { |
|||
if u.IsFollowing(followID) { |
|||
ctx.Status(204) |
|||
} else { |
|||
ctx.Error(404) |
|||
} |
|||
} |
|||
|
|||
// https://github.com/gogits/go-gogs-client/wiki/Users-Followers#check-if-you-are-following-a-user
|
|||
func CheckMyFollowing(ctx *middleware.Context) { |
|||
target := GetUserByParams(ctx) |
|||
if ctx.Written() { |
|||
return |
|||
} |
|||
checkUserFollowing(ctx, ctx.User, target.Id) |
|||
} |
|||
|
|||
// https://github.com/gogits/go-gogs-client/wiki/Users-Followers#check-if-one-user-follows-another
|
|||
func CheckFollowing(ctx *middleware.Context) { |
|||
u := GetUserByParams(ctx) |
|||
if ctx.Written() { |
|||
return |
|||
} |
|||
target := GetUserByParamsName(ctx, ":target") |
|||
if ctx.Written() { |
|||
return |
|||
} |
|||
checkUserFollowing(ctx, u, target.Id) |
|||
} |
|||
|
|||
// https://github.com/gogits/go-gogs-client/wiki/Users-Followers#follow-a-user
|
|||
func Follow(ctx *middleware.Context) { |
|||
target := GetUserByParams(ctx) |
|||
if ctx.Written() { |
|||
return |
|||
} |
|||
if err := models.FollowUser(ctx.User.Id, target.Id); err != nil { |
|||
ctx.APIError(500, "FollowUser", err) |
|||
return |
|||
} |
|||
ctx.Status(204) |
|||
} |
|||
|
|||
// https://github.com/gogits/go-gogs-client/wiki/Users-Followers#unfollow-a-user
|
|||
func Unfollow(ctx *middleware.Context) { |
|||
target := GetUserByParams(ctx) |
|||
if ctx.Written() { |
|||
return |
|||
} |
|||
if err := models.UnfollowUser(ctx.User.Id, target.Id); err != nil { |
|||
ctx.APIError(500, "UnfollowUser", err) |
|||
return |
|||
} |
|||
ctx.Status(204) |
|||
} |
@ -0,0 +1,145 @@ |
|||
// Copyright 2015 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 user |
|||
|
|||
import ( |
|||
"fmt" |
|||
"strings" |
|||
|
|||
"github.com/gogits/gogs/models" |
|||
"github.com/gogits/gogs/modules/base" |
|||
"github.com/gogits/gogs/modules/middleware" |
|||
"github.com/gogits/gogs/modules/setting" |
|||
"github.com/gogits/gogs/routers/repo" |
|||
) |
|||
|
|||
const ( |
|||
FOLLOWERS base.TplName = "user/meta/followers" |
|||
STARS base.TplName = "user/meta/stars" |
|||
) |
|||
|
|||
// GetUserByParams returns user whose name is presented in URL paramenter.
|
|||
func GetUserByParams(ctx *middleware.Context) *models.User { |
|||
user, err := models.GetUserByName(ctx.Params(":username")) |
|||
if err != nil { |
|||
if models.IsErrUserNotExist(err) { |
|||
ctx.Error(404) |
|||
} else { |
|||
ctx.Handle(500, "GetUserByName", err) |
|||
} |
|||
return nil |
|||
} |
|||
return user |
|||
} |
|||
|
|||
func Profile(ctx *middleware.Context) { |
|||
uname := ctx.Params(":username") |
|||
// Special handle for FireFox requests favicon.ico.
|
|||
if uname == "favicon.ico" { |
|||
ctx.Redirect(setting.AppSubUrl + "/img/favicon.png") |
|||
return |
|||
} else if strings.HasSuffix(uname, ".png") { |
|||
ctx.Error(404) |
|||
return |
|||
} |
|||
|
|||
isShowKeys := false |
|||
if strings.HasSuffix(uname, ".keys") { |
|||
isShowKeys = true |
|||
} |
|||
|
|||
u := GetUserByParams(ctx) |
|||
if ctx.Written() { |
|||
return |
|||
} |
|||
|
|||
// Show SSH keys.
|
|||
if isShowKeys { |
|||
ShowSSHKeys(ctx, u.Id) |
|||
return |
|||
} |
|||
|
|||
if u.IsOrganization() { |
|||
showOrgProfile(ctx) |
|||
return |
|||
} |
|||
|
|||
ctx.Data["Title"] = u.DisplayName() |
|||
ctx.Data["PageIsUserProfile"] = true |
|||
ctx.Data["Owner"] = u |
|||
|
|||
tab := ctx.Query("tab") |
|||
ctx.Data["TabName"] = tab |
|||
switch tab { |
|||
case "activity": |
|||
retrieveFeeds(ctx, u.Id, 0, true) |
|||
if ctx.Written() { |
|||
return |
|||
} |
|||
default: |
|||
var err error |
|||
ctx.Data["Repos"], err = models.GetRepositories(u.Id, ctx.IsSigned && ctx.User.Id == u.Id) |
|||
if err != nil { |
|||
ctx.Handle(500, "GetRepositories", err) |
|||
return |
|||
} |
|||
} |
|||
|
|||
ctx.HTML(200, PROFILE) |
|||
} |
|||
|
|||
func Followers(ctx *middleware.Context) { |
|||
u := GetUserByParams(ctx) |
|||
if ctx.Written() { |
|||
return |
|||
} |
|||
ctx.Data["Title"] = u.DisplayName() |
|||
ctx.Data["CardsTitle"] = ctx.Tr("user.followers") |
|||
ctx.Data["PageIsFollowers"] = true |
|||
ctx.Data["Owner"] = u |
|||
repo.RenderUserCards(ctx, u.NumFollowers, u.GetFollowers, FOLLOWERS) |
|||
} |
|||
|
|||
func Following(ctx *middleware.Context) { |
|||
u := GetUserByParams(ctx) |
|||
if ctx.Written() { |
|||
return |
|||
} |
|||
ctx.Data["Title"] = u.DisplayName() |
|||
ctx.Data["CardsTitle"] = ctx.Tr("user.following") |
|||
ctx.Data["PageIsFollowing"] = true |
|||
ctx.Data["Owner"] = u |
|||
repo.RenderUserCards(ctx, u.NumFollowing, u.GetFollowing, FOLLOWERS) |
|||
} |
|||
|
|||
func Stars(ctx *middleware.Context) { |
|||
|
|||
} |
|||
|
|||
func Action(ctx *middleware.Context) { |
|||
u := GetUserByParams(ctx) |
|||
if ctx.Written() { |
|||
return |
|||
} |
|||
|
|||
var err error |
|||
switch ctx.Params(":action") { |
|||
case "follow": |
|||
err = models.FollowUser(ctx.User.Id, u.Id) |
|||
case "unfollow": |
|||
err = models.UnfollowUser(ctx.User.Id, u.Id) |
|||
} |
|||
|
|||
if err != nil { |
|||
ctx.Handle(500, fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) |
|||
return |
|||
} |
|||
|
|||
redirectTo := ctx.Query("redirect_to") |
|||
if len(redirectTo) == 0 { |
|||
redirectTo = u.HomeLink() |
|||
} |
|||
ctx.Redirect(redirectTo) |
|||
} |
@ -1 +1 @@ |
|||
0.8.12.1219 |
|||
0.8.13.1221 |
@ -0,0 +1,47 @@ |
|||
<div class="ui container user-cards"> |
|||
<h2 class="ui dividing header"> |
|||
{{.CardsTitle}} |
|||
</h2> |
|||
<ul class="list"> |
|||
{{range .Cards}} |
|||
<li class="item ui segment"> |
|||
<a href="{{.HomeLink}}"> |
|||
<img class="avatar" src="{{.AvatarLink}}"/> |
|||
</a> |
|||
<h3 class="name"><a href="{{.HomeLink}}">{{.DisplayName}}</a></h3> |
|||
|
|||
<div class="meta"> |
|||
{{if .Website}} |
|||
<span class="icon octicon octicon-link"></span> <a href="{{.Website}}" target="_blank">{{.Website}}</a> |
|||
{{else if .Location}} |
|||
<span class="icon octicon octicon-location"></span> {{.Location}} |
|||
{{else}} |
|||
<span class="icon octicon octicon-clock"></span> {{$.i18n.Tr "user.join_on"}} {{DateFmtShort .Created}} |
|||
{{end}} |
|||
</div> |
|||
</li> |
|||
{{end}} |
|||
</ul> |
|||
|
|||
{{with .Page}} |
|||
{{if gt .TotalPages 1}} |
|||
<div class="center page buttons"> |
|||
<div class="ui borderless pagination menu"> |
|||
<a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}"{{end}}> |
|||
<i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}} |
|||
</a> |
|||
{{range .Pages}} |
|||
{{if eq .Num -1}} |
|||
<a class="disabled item">...</a> |
|||
{{else}} |
|||
<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?page={{.Num}}"{{end}}>{{.Num}}</a> |
|||
{{end}} |
|||
{{end}} |
|||
<a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?page={{.Next}}"{{end}}> |
|||
{{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
{{end}} |
|||
{{end}} |
|||
</div> |
@ -1,56 +1,6 @@ |
|||
{{template "base/head" .}} |
|||
<div class="repository watchers"> |
|||
{{template "repo/header" .}} |
|||
<div class="ui container"> |
|||
<h2 class="ui dividing header"> |
|||
{{if .PageIsWatchers}} |
|||
{{.i18n.Tr "repo.watchers"}} |
|||
{{else}} |
|||
{{.i18n.Tr "repo.stargazers"}} |
|||
{{end}} |
|||
</h2> |
|||
<ul class="list"> |
|||
{{range .Watchers}} |
|||
<li class="item ui segment"> |
|||
<a href="{{.HomeLink}}"> |
|||
<img class="avatar" src="{{.AvatarLink}}"/> |
|||
</a> |
|||
<h3 class="name"><a href="{{.HomeLink}}">{{.DisplayName}}</a></h3> |
|||
|
|||
<div class="meta"> |
|||
{{if .Website}} |
|||
<span class="icon octicon octicon-link"></span> <a href="{{.Website}}" target="_blank">{{.Website}}</a> |
|||
{{else if .Location}} |
|||
<span class="icon octicon octicon-location"></span> {{.Location}} |
|||
{{else}} |
|||
<span class="icon octicon octicon-clock"></span> {{$.i18n.Tr "user.join_on"}} {{DateFmtShort .Created}} |
|||
{{end}} |
|||
</div> |
|||
</li> |
|||
{{end}} |
|||
</ul> |
|||
|
|||
{{with .Page}} |
|||
{{if gt .TotalPages 1}} |
|||
<div class="center page buttons"> |
|||
<div class="ui borderless pagination menu"> |
|||
<a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}"{{end}}> |
|||
<i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}} |
|||
</a> |
|||
{{range .Pages}} |
|||
{{if eq .Num -1}} |
|||
<a class="disabled item">...</a> |
|||
{{else}} |
|||
<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?page={{.Num}}"{{end}}>{{.Num}}</a> |
|||
{{end}} |
|||
{{end}} |
|||
<a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?page={{.Next}}"{{end}}> |
|||
{{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
{{end}} |
|||
{{end}} |
|||
</div> |
|||
{{template "repo/user_cards" .}} |
|||
</div> |
|||
{{template "base/footer" .}} |
@ -0,0 +1,6 @@ |
|||
{{template "base/head" .}} |
|||
<div class="user followers"> |
|||
{{template "user/meta/header" .}} |
|||
{{template "repo/user_cards" .}} |
|||
</div> |
|||
{{template "base/footer" .}} |
@ -0,0 +1,25 @@ |
|||
{{with .Owner}} |
|||
<div class="ui container"> |
|||
<img class="ui avatar image" src="{{.AvatarLink}}"> |
|||
<span class="header name"> |
|||
<a href="{{.HomeLink}}">{{.Name}}</a> |
|||
{{with .FullName}}({{.}}){{end}} |
|||
</span> |
|||
|
|||
<div class="ui right"> |
|||
|
|||
{{if or $.PageIsFollowers $.PageIsFollowing}} |
|||
{{if and $.IsSigned (ne $.SignedUserName .Name)}} |
|||
<div class="follow"> |
|||
{{if $.SignedUser.IsFollowing .Id}} |
|||
<a class="ui small basic red button" href="{{.HomeLink}}/action/unfollow?redirect_to={{$.Link}}"><i class="octicon octicon-person"></i> {{$.i18n.Tr "user.unfollow"}}</a> |
|||
{{else}} |
|||
<a class="ui small basic green button" href="{{.HomeLink}}/action/follow?redirect_to={{$.Link}}"><i class="octicon octicon-person"></i> {{$.i18n.Tr "user.follow"}}</a> |
|||
{{end}} |
|||
</div> |
|||
{{end}} |
|||
{{end}} |
|||
</div> |
|||
</div> |
|||
{{end}} |
|||
<div class="ui divider"></div> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue