Skip to content

Commit a49af93

Browse files
committed
#1692 APIs: Users Followers
- User profile un/follow - List user's followers/following
1 parent c62a6b7 commit a49af93

27 files changed

+636
-266
lines changed

.bra.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ watch_dirs = [
1313
watch_exts = [".go"]
1414
build_delay = 1500
1515
cmds = [
16-
["go", "install", "-race"], # sqlite redis memcache cert pam tidb
16+
["go", "install", "-v", "-race"], # sqlite redis memcache cert pam tidb
1717
["go", "build", "-race"],
1818
["./gogs", "web"]
1919
]

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ NOW = $(shell date -u '+%Y%m%d%I%M%S')
1616
.IGNORE: public/css/gogs.css
1717

1818
build: $(GENERATED)
19-
go install -ldflags '$(LDFLAGS)' -tags '$(TAGS)'
19+
go install -v -ldflags '$(LDFLAGS)' -tags '$(TAGS)'
2020
cp '$(GOPATH)/bin/gogs' .
2121

2222
govet:

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra
33

44
![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true)
55

6-
##### Current version: 0.8.12
6+
##### Current version: 0.8.13
77

88
| Web | UI | Preview |
99
|:-------------:|:-------:|:-------:|
@@ -82,6 +82,7 @@ There are 5 ways to install Gogs:
8282
- [阿里云上 Ubuntu 14.04 64 位安装 Gogs](http://my.oschina.net/luyao/blog/375654) (Chinese)
8383
- [Installing Gogs on FreeBSD](https://www.codejam.info/2015/03/installing-gogs-on-freebsd.html)
8484
- [Gogs on Raspberry Pi](http://blog.meinside.pe.kr/Gogs-on-Raspberry-Pi/)
85+
- [Cloudflare Full SSL with GOGS (Go Git Service) using NGINX](http://www.listekconsulting.com/articles/cloudflare-full-ssl-with-gogs-go-git-service-using-nginx/)
8586

8687
### Screencasts
8788

@@ -101,6 +102,7 @@ There are 5 ways to install Gogs:
101102
- [Drone](https://github.com/drone/drone) (CI)
102103
- [Fabric8](http://fabric8.io/) (DevOps)
103104
- [Taiga](https://taiga.io/) (Project Management)
105+
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs) (IT)
104106

105107
### Product Support
106108

README_ZH.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
7373
- [Drone](https://github.com/drone/drone)(CI)
7474
- [Fabric8](http://fabric8.io/)(DevOps)
7575
- [Taiga](https://taiga.io/)(项目管理)
76+
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs)(IT)
7677

7778
### 产品支持
7879

cmd/web.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,13 @@ func runWeb(ctx *cli.Context) {
289289
// ***** END: Admin *****
290290

291291
m.Group("", func() {
292-
m.Get("/:username", user.Profile)
292+
m.Group("/:username", func() {
293+
m.Get("", user.Profile)
294+
m.Get("/followers", user.Followers)
295+
m.Get("/following", user.Following)
296+
m.Get("/stars", user.Stars)
297+
})
298+
293299
m.Get("/attachments/:uuid", func(ctx *middleware.Context) {
294300
attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid"))
295301
if err != nil {
@@ -319,6 +325,10 @@ func runWeb(ctx *cli.Context) {
319325
m.Post("/issues/attachments", repo.UploadIssueAttachment)
320326
}, ignSignIn)
321327

328+
m.Group("/:username", func() {
329+
m.Get("/action/:action", user.Action)
330+
}, reqSignIn)
331+
322332
if macaron.Env == macaron.DEV {
323333
m.Get("/template/*", dev.TemplatePreview)
324334
}

conf/locale/locale_en-US.ini

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,10 @@ join_on = Joined on
230230
repositories = Repositories
231231
activity = Public Activity
232232
followers = Followers
233-
starred = Starred
233+
starred = Starred repositories
234234
following = Following
235+
follow = Follow
236+
unfollow = Unfollow
235237
236238
form.name_reserved = Username '%s' is reserved.
237239
form.name_pattern_not_allowed = Username pattern '%s' is not allowed.

gogs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
"github.com/gogits/gogs/modules/setting"
1818
)
1919

20-
const APP_VER = "0.8.12.1219"
20+
const APP_VER = "0.8.13.1221"
2121

2222
func init() {
2323
runtime.GOMAXPROCS(runtime.NumCPU())

models/issue.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,47 @@ func GetIssueUserPairsByMode(uid, rid int64, isClosed bool, page, filterMode int
665665
return ius, err
666666
}
667667

668+
func UpdateMentions(userNames []string, issueId int64) error {
669+
for i := range userNames {
670+
userNames[i] = strings.ToLower(userNames[i])
671+
}
672+
users := make([]*User, 0, len(userNames))
673+
674+
if err := x.Where("lower_name IN (?)", strings.Join(userNames, "\",\"")).OrderBy("lower_name ASC").Find(&users); err != nil {
675+
return err
676+
}
677+
678+
ids := make([]int64, 0, len(userNames))
679+
for _, user := range users {
680+
ids = append(ids, user.Id)
681+
if !user.IsOrganization() {
682+
continue
683+
}
684+
685+
if user.NumMembers == 0 {
686+
continue
687+
}
688+
689+
tempIds := make([]int64, 0, user.NumMembers)
690+
orgUsers, err := GetOrgUsersByOrgId(user.Id)
691+
if err != nil {
692+
return err
693+
}
694+
695+
for _, orgUser := range orgUsers {
696+
tempIds = append(tempIds, orgUser.ID)
697+
}
698+
699+
ids = append(ids, tempIds...)
700+
}
701+
702+
if err := UpdateIssueUsersByMentions(ids, issueId); err != nil {
703+
return err
704+
}
705+
706+
return nil
707+
}
708+
668709
// IssueStats represents issue statistic information.
669710
type IssueStats struct {
670711
OpenCount, ClosedCount int64

models/user.go

Lines changed: 73 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ type User struct {
5656
LowerName string `xorm:"UNIQUE NOT NULL"`
5757
Name string `xorm:"UNIQUE NOT NULL"`
5858
FullName string
59-
// Email is the primary email address (to be used for communication).
59+
// Email is the primary email address (to be used for communication)
6060
Email string `xorm:"NOT NULL"`
6161
Passwd string `xorm:"NOT NULL"`
6262
LoginType LoginType
@@ -78,24 +78,24 @@ type User struct {
7878
// Maximum repository creation limit, -1 means use gloabl default
7979
MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"`
8080

81-
// Permissions.
81+
// Permissions
8282
IsActive bool
8383
IsAdmin bool
8484
AllowGitHook bool
8585
AllowImportLocal bool // Allow migrate repository by local path
8686

87-
// Avatar.
87+
// Avatar
8888
Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
8989
AvatarEmail string `xorm:"NOT NULL"`
9090
UseCustomAvatar bool
9191

92-
// Counters.
93-
NumFollowers int
94-
NumFollowings int
95-
NumStars int
96-
NumRepos int
92+
// Counters
93+
NumFollowers int
94+
NumFollowing int `xorm:"NOT NULL"`
95+
NumStars int
96+
NumRepos int
9797

98-
// For organization.
98+
// For organization
9999
Description string
100100
NumTeams int
101101
NumMembers int
@@ -263,6 +263,34 @@ func (u *User) AvatarLink() string {
263263
return link
264264
}
265265

266+
// User.GetFollwoers returns range of user's followers.
267+
func (u *User) GetFollowers(page int) ([]*User, error) {
268+
users := make([]*User, 0, ItemsPerPage)
269+
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("follow.follow_id=?", u.Id)
270+
if setting.UsePostgreSQL {
271+
sess = sess.Join("LEFT", "follow", `"user".id=follow.user_id`)
272+
} else {
273+
sess = sess.Join("LEFT", "follow", "user.id=follow.user_id")
274+
}
275+
return users, sess.Find(&users)
276+
}
277+
278+
func (u *User) IsFollowing(followID int64) bool {
279+
return IsFollowing(u.Id, followID)
280+
}
281+
282+
// GetFollowing returns range of user's following.
283+
func (u *User) GetFollowing(page int) ([]*User, error) {
284+
users := make([]*User, 0, ItemsPerPage)
285+
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("follow.user_id=?", u.Id)
286+
if setting.UsePostgreSQL {
287+
sess = sess.Join("LEFT", "follow", `"user".id=follow.follow_id`)
288+
} else {
289+
sess = sess.Join("LEFT", "follow", "user.id=follow.follow_id")
290+
}
291+
return users, sess.Find(&users)
292+
}
293+
266294
// NewGitSig generates and returns the signature of given user.
267295
func (u *User) NewGitSig() *git.Signature {
268296
return &git.Signature{
@@ -1077,100 +1105,73 @@ func SearchUserByName(opt SearchOption) (us []*User, err error) {
10771105
return us, err
10781106
}
10791107

1080-
// Follow is connection request for receiving user notification.
1108+
// ___________ .__ .__
1109+
// \_ _____/___ | | | | ______ _ __
1110+
// | __)/ _ \| | | | / _ \ \/ \/ /
1111+
// | \( <_> ) |_| |_( <_> ) /
1112+
// \___ / \____/|____/____/\____/ \/\_/
1113+
// \/
1114+
1115+
// Follow represents relations of user and his/her followers.
10811116
type Follow struct {
10821117
ID int64 `xorm:"pk autoincr"`
10831118
UserID int64 `xorm:"UNIQUE(follow)"`
10841119
FollowID int64 `xorm:"UNIQUE(follow)"`
10851120
}
10861121

1122+
func IsFollowing(userID, followID int64) bool {
1123+
has, _ := x.Get(&Follow{UserID: userID, FollowID: followID})
1124+
return has
1125+
}
1126+
10871127
// FollowUser marks someone be another's follower.
1088-
func FollowUser(userId int64, followId int64) (err error) {
1128+
func FollowUser(userID, followID int64) (err error) {
1129+
if userID == followID || IsFollowing(userID, followID) {
1130+
return nil
1131+
}
1132+
10891133
sess := x.NewSession()
1090-
defer sess.Close()
1091-
sess.Begin()
1134+
defer sessionRelease(sess)
1135+
if err = sess.Begin(); err != nil {
1136+
return err
1137+
}
10921138

1093-
if _, err = sess.Insert(&Follow{UserID: userId, FollowID: followId}); err != nil {
1094-
sess.Rollback()
1139+
if _, err = sess.Insert(&Follow{UserID: userID, FollowID: followID}); err != nil {
10951140
return err
10961141
}
10971142

1098-
rawSql := "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?"
1099-
if _, err = sess.Exec(rawSql, followId); err != nil {
1100-
sess.Rollback()
1143+
if _, err = sess.Exec("UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil {
11011144
return err
11021145
}
11031146

1104-
rawSql = "UPDATE `user` SET num_followings = num_followings + 1 WHERE id = ?"
1105-
if _, err = sess.Exec(rawSql, userId); err != nil {
1106-
sess.Rollback()
1147+
if _, err = sess.Exec("UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil {
11071148
return err
11081149
}
11091150
return sess.Commit()
11101151
}
11111152

1112-
// UnFollowUser unmarks someone be another's follower.
1113-
func UnFollowUser(userId int64, unFollowId int64) (err error) {
1114-
session := x.NewSession()
1115-
defer session.Close()
1116-
session.Begin()
1117-
1118-
if _, err = session.Delete(&Follow{UserID: userId, FollowID: unFollowId}); err != nil {
1119-
session.Rollback()
1120-
return err
1153+
// UnfollowUser unmarks someone be another's follower.
1154+
func UnfollowUser(userID, followID int64) (err error) {
1155+
if userID == followID || !IsFollowing(userID, followID) {
1156+
return nil
11211157
}
11221158

1123-
rawSql := "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?"
1124-
if _, err = session.Exec(rawSql, unFollowId); err != nil {
1125-
session.Rollback()
1159+
sess := x.NewSession()
1160+
defer sessionRelease(sess)
1161+
if err = sess.Begin(); err != nil {
11261162
return err
11271163
}
11281164

1129-
rawSql = "UPDATE `user` SET num_followings = num_followings - 1 WHERE id = ?"
1130-
if _, err = session.Exec(rawSql, userId); err != nil {
1131-
session.Rollback()
1165+
if _, err = sess.Delete(&Follow{UserID: userID, FollowID: followID}); err != nil {
11321166
return err
11331167
}
1134-
return session.Commit()
1135-
}
11361168

1137-
func UpdateMentions(userNames []string, issueId int64) error {
1138-
for i := range userNames {
1139-
userNames[i] = strings.ToLower(userNames[i])
1140-
}
1141-
users := make([]*User, 0, len(userNames))
1142-
1143-
if err := x.Where("lower_name IN (?)", strings.Join(userNames, "\",\"")).OrderBy("lower_name ASC").Find(&users); err != nil {
1169+
if _, err = sess.Exec("UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil {
11441170
return err
11451171
}
11461172

1147-
ids := make([]int64, 0, len(userNames))
1148-
for _, user := range users {
1149-
ids = append(ids, user.Id)
1150-
if !user.IsOrganization() {
1151-
continue
1152-
}
1153-
1154-
if user.NumMembers == 0 {
1155-
continue
1156-
}
1157-
1158-
tempIds := make([]int64, 0, user.NumMembers)
1159-
orgUsers, err := GetOrgUsersByOrgId(user.Id)
1160-
if err != nil {
1161-
return err
1162-
}
1163-
1164-
for _, orgUser := range orgUsers {
1165-
tempIds = append(tempIds, orgUser.ID)
1166-
}
1167-
1168-
ids = append(ids, tempIds...)
1169-
}
1170-
1171-
if err := UpdateIssueUsersByMentions(ids, issueId); err != nil {
1173+
if _, err = sess.Exec("UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil {
11721174
return err
11731175
}
1174-
1175-
return nil
1176+
return sess.Commit()
11761177
}

modules/bindata/bindata.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/middleware/context.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ func Contexter() macaron.Handler {
242242

243243
ctx.Data["CsrfToken"] = x.GetToken()
244244
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + x.GetToken() + `">`)
245-
log.Debug("CSRF Token: %v | %v", ctx.Data["CsrfToken"], ctx.GetCookie("_csrf"))
245+
log.Debug("Session ID: %s", sess.ID())
246+
log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"])
246247

247248
ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton
248249
ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding

0 commit comments

Comments
 (0)