mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	#1692 APIs: Users Followers
- User profile un/follow - List user's followers/following
This commit is contained in:
		@@ -13,7 +13,7 @@ watch_dirs = [
 | 
				
			|||||||
watch_exts = [".go"]
 | 
					watch_exts = [".go"]
 | 
				
			||||||
build_delay = 1500
 | 
					build_delay = 1500
 | 
				
			||||||
cmds = [
 | 
					cmds = [
 | 
				
			||||||
	["go", "install", "-race"], # sqlite redis memcache cert pam tidb
 | 
						["go", "install", "-v", "-race"], # sqlite redis memcache cert pam tidb
 | 
				
			||||||
	["go", "build", "-race"],
 | 
						["go", "build", "-race"],
 | 
				
			||||||
	["./gogs", "web"]
 | 
						["./gogs", "web"]
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							@@ -16,7 +16,7 @@ NOW = $(shell date -u '+%Y%m%d%I%M%S')
 | 
				
			|||||||
.IGNORE: public/css/gogs.css
 | 
					.IGNORE: public/css/gogs.css
 | 
				
			||||||
 | 
					
 | 
				
			||||||
build: $(GENERATED)
 | 
					build: $(GENERATED)
 | 
				
			||||||
	go install -ldflags '$(LDFLAGS)' -tags '$(TAGS)'
 | 
						go install -v -ldflags '$(LDFLAGS)' -tags '$(TAGS)'
 | 
				
			||||||
	cp '$(GOPATH)/bin/gogs' .
 | 
						cp '$(GOPATH)/bin/gogs' .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
govet:
 | 
					govet:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ Gogs - Go Git Service [
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
##### Current version: 0.8.12
 | 
					##### Current version: 0.8.13
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| Web | UI  | Preview  |
 | 
					| Web | UI  | Preview  |
 | 
				
			||||||
|:-------------:|:-------:|:-------:|
 | 
					|:-------------:|:-------:|:-------:|
 | 
				
			||||||
@@ -82,6 +82,7 @@ There are 5 ways to install Gogs:
 | 
				
			|||||||
- [阿里云上 Ubuntu 14.04 64 位安装 Gogs](http://my.oschina.net/luyao/blog/375654) (Chinese)
 | 
					- [阿里云上 Ubuntu 14.04 64 位安装 Gogs](http://my.oschina.net/luyao/blog/375654) (Chinese)
 | 
				
			||||||
- [Installing Gogs on FreeBSD](https://www.codejam.info/2015/03/installing-gogs-on-freebsd.html)
 | 
					- [Installing Gogs on FreeBSD](https://www.codejam.info/2015/03/installing-gogs-on-freebsd.html)
 | 
				
			||||||
- [Gogs on Raspberry Pi](http://blog.meinside.pe.kr/Gogs-on-Raspberry-Pi/)
 | 
					- [Gogs on Raspberry Pi](http://blog.meinside.pe.kr/Gogs-on-Raspberry-Pi/)
 | 
				
			||||||
 | 
					- [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/)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Screencasts
 | 
					### Screencasts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -101,6 +102,7 @@ There are 5 ways to install Gogs:
 | 
				
			|||||||
- [Drone](https://github.com/drone/drone) (CI)
 | 
					- [Drone](https://github.com/drone/drone) (CI)
 | 
				
			||||||
- [Fabric8](http://fabric8.io/) (DevOps)
 | 
					- [Fabric8](http://fabric8.io/) (DevOps)
 | 
				
			||||||
- [Taiga](https://taiga.io/) (Project Management)
 | 
					- [Taiga](https://taiga.io/) (Project Management)
 | 
				
			||||||
 | 
					- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs) (IT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Product Support
 | 
					### Product Support
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,6 +73,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
 | 
				
			|||||||
- [Drone](https://github.com/drone/drone)(CI)
 | 
					- [Drone](https://github.com/drone/drone)(CI)
 | 
				
			||||||
- [Fabric8](http://fabric8.io/)(DevOps)
 | 
					- [Fabric8](http://fabric8.io/)(DevOps)
 | 
				
			||||||
- [Taiga](https://taiga.io/)(项目管理)
 | 
					- [Taiga](https://taiga.io/)(项目管理)
 | 
				
			||||||
 | 
					- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs)(IT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 产品支持
 | 
					### 产品支持
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								cmd/web.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								cmd/web.go
									
									
									
									
									
								
							@@ -289,7 +289,13 @@ func runWeb(ctx *cli.Context) {
 | 
				
			|||||||
	// ***** END: Admin *****
 | 
						// ***** END: Admin *****
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m.Group("", func() {
 | 
						m.Group("", func() {
 | 
				
			||||||
		m.Get("/:username", user.Profile)
 | 
							m.Group("/:username", func() {
 | 
				
			||||||
 | 
								m.Get("", user.Profile)
 | 
				
			||||||
 | 
								m.Get("/followers", user.Followers)
 | 
				
			||||||
 | 
								m.Get("/following", user.Following)
 | 
				
			||||||
 | 
								m.Get("/stars", user.Stars)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m.Get("/attachments/:uuid", func(ctx *middleware.Context) {
 | 
							m.Get("/attachments/:uuid", func(ctx *middleware.Context) {
 | 
				
			||||||
			attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid"))
 | 
								attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid"))
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
@@ -319,6 +325,10 @@ func runWeb(ctx *cli.Context) {
 | 
				
			|||||||
		m.Post("/issues/attachments", repo.UploadIssueAttachment)
 | 
							m.Post("/issues/attachments", repo.UploadIssueAttachment)
 | 
				
			||||||
	}, ignSignIn)
 | 
						}, ignSignIn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m.Group("/:username", func() {
 | 
				
			||||||
 | 
							m.Get("/action/:action", user.Action)
 | 
				
			||||||
 | 
						}, reqSignIn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if macaron.Env == macaron.DEV {
 | 
						if macaron.Env == macaron.DEV {
 | 
				
			||||||
		m.Get("/template/*", dev.TemplatePreview)
 | 
							m.Get("/template/*", dev.TemplatePreview)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -230,8 +230,10 @@ join_on = Joined on
 | 
				
			|||||||
repositories = Repositories
 | 
					repositories = Repositories
 | 
				
			||||||
activity = Public Activity
 | 
					activity = Public Activity
 | 
				
			||||||
followers = Followers
 | 
					followers = Followers
 | 
				
			||||||
starred = Starred
 | 
					starred = Starred repositories
 | 
				
			||||||
following = Following
 | 
					following = Following
 | 
				
			||||||
 | 
					follow = Follow
 | 
				
			||||||
 | 
					unfollow = Unfollow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
form.name_reserved = Username '%s' is reserved.
 | 
					form.name_reserved = Username '%s' is reserved.
 | 
				
			||||||
form.name_pattern_not_allowed = Username pattern '%s' is not allowed.
 | 
					form.name_pattern_not_allowed = Username pattern '%s' is not allowed.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							@@ -17,7 +17,7 @@ import (
 | 
				
			|||||||
	"github.com/gogits/gogs/modules/setting"
 | 
						"github.com/gogits/gogs/modules/setting"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const APP_VER = "0.8.12.1219"
 | 
					const APP_VER = "0.8.13.1221"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	runtime.GOMAXPROCS(runtime.NumCPU())
 | 
						runtime.GOMAXPROCS(runtime.NumCPU())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -665,6 +665,47 @@ func GetIssueUserPairsByMode(uid, rid int64, isClosed bool, page, filterMode int
 | 
				
			|||||||
	return ius, err
 | 
						return ius, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func UpdateMentions(userNames []string, issueId int64) error {
 | 
				
			||||||
 | 
						for i := range userNames {
 | 
				
			||||||
 | 
							userNames[i] = strings.ToLower(userNames[i])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						users := make([]*User, 0, len(userNames))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := x.Where("lower_name IN (?)", strings.Join(userNames, "\",\"")).OrderBy("lower_name ASC").Find(&users); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ids := make([]int64, 0, len(userNames))
 | 
				
			||||||
 | 
						for _, user := range users {
 | 
				
			||||||
 | 
							ids = append(ids, user.Id)
 | 
				
			||||||
 | 
							if !user.IsOrganization() {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if user.NumMembers == 0 {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							tempIds := make([]int64, 0, user.NumMembers)
 | 
				
			||||||
 | 
							orgUsers, err := GetOrgUsersByOrgId(user.Id)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, orgUser := range orgUsers {
 | 
				
			||||||
 | 
								tempIds = append(tempIds, orgUser.ID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ids = append(ids, tempIds...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := UpdateIssueUsersByMentions(ids, issueId); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IssueStats represents issue statistic information.
 | 
					// IssueStats represents issue statistic information.
 | 
				
			||||||
type IssueStats struct {
 | 
					type IssueStats struct {
 | 
				
			||||||
	OpenCount, ClosedCount int64
 | 
						OpenCount, ClosedCount int64
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										165
									
								
								models/user.go
									
									
									
									
									
								
							
							
						
						
									
										165
									
								
								models/user.go
									
									
									
									
									
								
							@@ -56,7 +56,7 @@ type User struct {
 | 
				
			|||||||
	LowerName string `xorm:"UNIQUE NOT NULL"`
 | 
						LowerName string `xorm:"UNIQUE NOT NULL"`
 | 
				
			||||||
	Name      string `xorm:"UNIQUE NOT NULL"`
 | 
						Name      string `xorm:"UNIQUE NOT NULL"`
 | 
				
			||||||
	FullName  string
 | 
						FullName  string
 | 
				
			||||||
	// Email is the primary email address (to be used for communication).
 | 
						// Email is the primary email address (to be used for communication)
 | 
				
			||||||
	Email       string `xorm:"NOT NULL"`
 | 
						Email       string `xorm:"NOT NULL"`
 | 
				
			||||||
	Passwd      string `xorm:"NOT NULL"`
 | 
						Passwd      string `xorm:"NOT NULL"`
 | 
				
			||||||
	LoginType   LoginType
 | 
						LoginType   LoginType
 | 
				
			||||||
@@ -78,24 +78,24 @@ type User struct {
 | 
				
			|||||||
	// Maximum repository creation limit, -1 means use gloabl default
 | 
						// Maximum repository creation limit, -1 means use gloabl default
 | 
				
			||||||
	MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"`
 | 
						MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Permissions.
 | 
						// Permissions
 | 
				
			||||||
	IsActive         bool
 | 
						IsActive         bool
 | 
				
			||||||
	IsAdmin          bool
 | 
						IsAdmin          bool
 | 
				
			||||||
	AllowGitHook     bool
 | 
						AllowGitHook     bool
 | 
				
			||||||
	AllowImportLocal bool // Allow migrate repository by local path
 | 
						AllowImportLocal bool // Allow migrate repository by local path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Avatar.
 | 
						// Avatar
 | 
				
			||||||
	Avatar          string `xorm:"VARCHAR(2048) NOT NULL"`
 | 
						Avatar          string `xorm:"VARCHAR(2048) NOT NULL"`
 | 
				
			||||||
	AvatarEmail     string `xorm:"NOT NULL"`
 | 
						AvatarEmail     string `xorm:"NOT NULL"`
 | 
				
			||||||
	UseCustomAvatar bool
 | 
						UseCustomAvatar bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Counters.
 | 
						// Counters
 | 
				
			||||||
	NumFollowers int
 | 
						NumFollowers int
 | 
				
			||||||
	NumFollowings int
 | 
						NumFollowing int `xorm:"NOT NULL"`
 | 
				
			||||||
	NumStars     int
 | 
						NumStars     int
 | 
				
			||||||
	NumRepos     int
 | 
						NumRepos     int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// For organization.
 | 
						// For organization
 | 
				
			||||||
	Description string
 | 
						Description string
 | 
				
			||||||
	NumTeams    int
 | 
						NumTeams    int
 | 
				
			||||||
	NumMembers  int
 | 
						NumMembers  int
 | 
				
			||||||
@@ -263,6 +263,34 @@ func (u *User) AvatarLink() string {
 | 
				
			|||||||
	return link
 | 
						return link
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// User.GetFollwoers returns range of user's followers.
 | 
				
			||||||
 | 
					func (u *User) GetFollowers(page int) ([]*User, error) {
 | 
				
			||||||
 | 
						users := make([]*User, 0, ItemsPerPage)
 | 
				
			||||||
 | 
						sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("follow.follow_id=?", u.Id)
 | 
				
			||||||
 | 
						if setting.UsePostgreSQL {
 | 
				
			||||||
 | 
							sess = sess.Join("LEFT", "follow", `"user".id=follow.user_id`)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							sess = sess.Join("LEFT", "follow", "user.id=follow.user_id")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return users, sess.Find(&users)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *User) IsFollowing(followID int64) bool {
 | 
				
			||||||
 | 
						return IsFollowing(u.Id, followID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetFollowing returns range of user's following.
 | 
				
			||||||
 | 
					func (u *User) GetFollowing(page int) ([]*User, error) {
 | 
				
			||||||
 | 
						users := make([]*User, 0, ItemsPerPage)
 | 
				
			||||||
 | 
						sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("follow.user_id=?", u.Id)
 | 
				
			||||||
 | 
						if setting.UsePostgreSQL {
 | 
				
			||||||
 | 
							sess = sess.Join("LEFT", "follow", `"user".id=follow.follow_id`)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							sess = sess.Join("LEFT", "follow", "user.id=follow.follow_id")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return users, sess.Find(&users)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewGitSig generates and returns the signature of given user.
 | 
					// NewGitSig generates and returns the signature of given user.
 | 
				
			||||||
func (u *User) NewGitSig() *git.Signature {
 | 
					func (u *User) NewGitSig() *git.Signature {
 | 
				
			||||||
	return &git.Signature{
 | 
						return &git.Signature{
 | 
				
			||||||
@@ -1077,100 +1105,73 @@ func SearchUserByName(opt SearchOption) (us []*User, err error) {
 | 
				
			|||||||
	return us, err
 | 
						return us, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Follow is connection request for receiving user notification.
 | 
					// ___________    .__  .__
 | 
				
			||||||
 | 
					// \_   _____/___ |  | |  |   ______  _  __
 | 
				
			||||||
 | 
					//  |    __)/  _ \|  | |  |  /  _ \ \/ \/ /
 | 
				
			||||||
 | 
					//  |     \(  <_> )  |_|  |_(  <_> )     /
 | 
				
			||||||
 | 
					//  \___  / \____/|____/____/\____/ \/\_/
 | 
				
			||||||
 | 
					//      \/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Follow represents relations of user and his/her followers.
 | 
				
			||||||
type Follow struct {
 | 
					type Follow struct {
 | 
				
			||||||
	ID       int64 `xorm:"pk autoincr"`
 | 
						ID       int64 `xorm:"pk autoincr"`
 | 
				
			||||||
	UserID   int64 `xorm:"UNIQUE(follow)"`
 | 
						UserID   int64 `xorm:"UNIQUE(follow)"`
 | 
				
			||||||
	FollowID int64 `xorm:"UNIQUE(follow)"`
 | 
						FollowID int64 `xorm:"UNIQUE(follow)"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func IsFollowing(userID, followID int64) bool {
 | 
				
			||||||
 | 
						has, _ := x.Get(&Follow{UserID: userID, FollowID: followID})
 | 
				
			||||||
 | 
						return has
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FollowUser marks someone be another's follower.
 | 
					// FollowUser marks someone be another's follower.
 | 
				
			||||||
func FollowUser(userId int64, followId int64) (err error) {
 | 
					func FollowUser(userID, followID int64) (err error) {
 | 
				
			||||||
 | 
						if userID == followID || IsFollowing(userID, followID) {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sess := x.NewSession()
 | 
						sess := x.NewSession()
 | 
				
			||||||
	defer sess.Close()
 | 
						defer sessionRelease(sess)
 | 
				
			||||||
	sess.Begin()
 | 
						if err = sess.Begin(); err != nil {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if _, err = sess.Insert(&Follow{UserID: userId, FollowID: followId}); err != nil {
 | 
					 | 
				
			||||||
		sess.Rollback()
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rawSql := "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?"
 | 
						if _, err = sess.Insert(&Follow{UserID: userID, FollowID: followID}); err != nil {
 | 
				
			||||||
	if _, err = sess.Exec(rawSql, followId); err != nil {
 | 
					 | 
				
			||||||
		sess.Rollback()
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rawSql = "UPDATE `user` SET num_followings = num_followings + 1 WHERE id = ?"
 | 
						if _, err = sess.Exec("UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil {
 | 
				
			||||||
	if _, err = sess.Exec(rawSql, userId); err != nil {
 | 
							return err
 | 
				
			||||||
		sess.Rollback()
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err = sess.Exec("UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return sess.Commit()
 | 
						return sess.Commit()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UnFollowUser unmarks someone be another's follower.
 | 
					// UnfollowUser unmarks someone be another's follower.
 | 
				
			||||||
func UnFollowUser(userId int64, unFollowId int64) (err error) {
 | 
					func UnfollowUser(userID, followID int64) (err error) {
 | 
				
			||||||
	session := x.NewSession()
 | 
						if userID == followID || !IsFollowing(userID, followID) {
 | 
				
			||||||
	defer session.Close()
 | 
					 | 
				
			||||||
	session.Begin()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if _, err = session.Delete(&Follow{UserID: userId, FollowID: unFollowId}); err != nil {
 | 
					 | 
				
			||||||
		session.Rollback()
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rawSql := "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?"
 | 
					 | 
				
			||||||
	if _, err = session.Exec(rawSql, unFollowId); err != nil {
 | 
					 | 
				
			||||||
		session.Rollback()
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rawSql = "UPDATE `user` SET num_followings = num_followings - 1 WHERE id = ?"
 | 
					 | 
				
			||||||
	if _, err = session.Exec(rawSql, userId); err != nil {
 | 
					 | 
				
			||||||
		session.Rollback()
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return session.Commit()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func UpdateMentions(userNames []string, issueId int64) error {
 | 
					 | 
				
			||||||
	for i := range userNames {
 | 
					 | 
				
			||||||
		userNames[i] = strings.ToLower(userNames[i])
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	users := make([]*User, 0, len(userNames))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := x.Where("lower_name IN (?)", strings.Join(userNames, "\",\"")).OrderBy("lower_name ASC").Find(&users); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ids := make([]int64, 0, len(userNames))
 | 
					 | 
				
			||||||
	for _, user := range users {
 | 
					 | 
				
			||||||
		ids = append(ids, user.Id)
 | 
					 | 
				
			||||||
		if !user.IsOrganization() {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if user.NumMembers == 0 {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		tempIds := make([]int64, 0, user.NumMembers)
 | 
					 | 
				
			||||||
		orgUsers, err := GetOrgUsersByOrgId(user.Id)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for _, orgUser := range orgUsers {
 | 
					 | 
				
			||||||
			tempIds = append(tempIds, orgUser.ID)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ids = append(ids, tempIds...)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := UpdateIssueUsersByMentions(ids, issueId); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sessionRelease(sess)
 | 
				
			||||||
 | 
						if err = sess.Begin(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err = sess.Delete(&Follow{UserID: userID, FollowID: followID}); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err = sess.Exec("UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err = sess.Exec("UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return sess.Commit()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -242,7 +242,8 @@ func Contexter() macaron.Handler {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		ctx.Data["CsrfToken"] = x.GetToken()
 | 
							ctx.Data["CsrfToken"] = x.GetToken()
 | 
				
			||||||
		ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + x.GetToken() + `">`)
 | 
							ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + x.GetToken() + `">`)
 | 
				
			||||||
		log.Debug("CSRF Token: %v | %v", ctx.Data["CsrfToken"], ctx.GetCookie("_csrf"))
 | 
							log.Debug("Session ID: %s", sess.ID())
 | 
				
			||||||
 | 
							log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton
 | 
							ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton
 | 
				
			||||||
		ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding
 | 
							ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2465,31 +2465,6 @@ footer .container .links > *:first-child {
 | 
				
			|||||||
.repository.new.release .prerelease.field {
 | 
					.repository.new.release .prerelease.field {
 | 
				
			||||||
  margin-bottom: 0;
 | 
					  margin-bottom: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.repository.watchers .list {
 | 
					 | 
				
			||||||
  padding: 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.repository.watchers .list .item {
 | 
					 | 
				
			||||||
  list-style: none;
 | 
					 | 
				
			||||||
  width: 32%;
 | 
					 | 
				
			||||||
  margin: 10px 10px 10px 0;
 | 
					 | 
				
			||||||
  padding-bottom: 14px;
 | 
					 | 
				
			||||||
  float: left;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.repository.watchers .list .item .avatar {
 | 
					 | 
				
			||||||
  width: 48px;
 | 
					 | 
				
			||||||
  height: 48px;
 | 
					 | 
				
			||||||
  float: left;
 | 
					 | 
				
			||||||
  display: block;
 | 
					 | 
				
			||||||
  margin-right: 10px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.repository.watchers .list .item .name {
 | 
					 | 
				
			||||||
  margin-top: 0;
 | 
					 | 
				
			||||||
  margin-bottom: 0;
 | 
					 | 
				
			||||||
  font-weight: normal;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.repository.watchers .list .item .meta {
 | 
					 | 
				
			||||||
  margin-top: 5px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.repository.forks .list {
 | 
					.repository.forks .list {
 | 
				
			||||||
  margin-top: 0;
 | 
					  margin-top: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -2551,6 +2526,31 @@ footer .container .links > *:first-child {
 | 
				
			|||||||
  margin-left: 5px;
 | 
					  margin-left: 5px;
 | 
				
			||||||
  margin-top: -3px;
 | 
					  margin-top: -3px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					.user-cards .list {
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.user-cards .list .item {
 | 
				
			||||||
 | 
					  list-style: none;
 | 
				
			||||||
 | 
					  width: 32%;
 | 
				
			||||||
 | 
					  margin: 10px 10px 10px 0;
 | 
				
			||||||
 | 
					  padding-bottom: 14px;
 | 
				
			||||||
 | 
					  float: left;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.user-cards .list .item .avatar {
 | 
				
			||||||
 | 
					  width: 48px;
 | 
				
			||||||
 | 
					  height: 48px;
 | 
				
			||||||
 | 
					  float: left;
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  margin-right: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.user-cards .list .item .name {
 | 
				
			||||||
 | 
					  margin-top: 0;
 | 
				
			||||||
 | 
					  margin-bottom: 0;
 | 
				
			||||||
 | 
					  font-weight: normal;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.user-cards .list .item .meta {
 | 
				
			||||||
 | 
					  margin-top: 5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#search-repo-box .results,
 | 
					#search-repo-box .results,
 | 
				
			||||||
#search-user-box .results {
 | 
					#search-user-box .results {
 | 
				
			||||||
  padding: 0;
 | 
					  padding: 0;
 | 
				
			||||||
@@ -2862,7 +2862,7 @@ footer .container .links > *:first-child {
 | 
				
			|||||||
  margin-left: 5px;
 | 
					  margin-left: 5px;
 | 
				
			||||||
  margin-top: -3px;
 | 
					  margin-top: -3px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.user {
 | 
					.user:not(.icon) {
 | 
				
			||||||
  padding-top: 15px;
 | 
					  padding-top: 15px;
 | 
				
			||||||
  padding-bottom: 80px;
 | 
					  padding-bottom: 80px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -2893,9 +2893,24 @@ footer .container .links > *:first-child {
 | 
				
			|||||||
.user.profile .ui.card .extra.content ul li:not(:last-child) {
 | 
					.user.profile .ui.card .extra.content ul li:not(:last-child) {
 | 
				
			||||||
  border-bottom: 1px solid #eaeaea;
 | 
					  border-bottom: 1px solid #eaeaea;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					.user.profile .ui.card .extra.content ul li .octicon {
 | 
				
			||||||
 | 
					  margin-left: 1px;
 | 
				
			||||||
 | 
					  margin-right: 5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.user.profile .ui.card .extra.content ul li.follow .ui.button {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
.user.profile .ui.repository.list {
 | 
					.user.profile .ui.repository.list {
 | 
				
			||||||
  margin-top: 25px;
 | 
					  margin-top: 25px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					.user.followers .header.name {
 | 
				
			||||||
 | 
					  font-size: 20px;
 | 
				
			||||||
 | 
					  line-height: 24px;
 | 
				
			||||||
 | 
					  vertical-align: middle;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.user.followers .follow .ui.button {
 | 
				
			||||||
 | 
					  padding: 8px 15px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
.dashboard {
 | 
					.dashboard {
 | 
				
			||||||
  padding-top: 15px;
 | 
					  padding-top: 15px;
 | 
				
			||||||
  padding-bottom: 80px;
 | 
					  padding-bottom: 80px;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -859,35 +859,6 @@
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	&.watchers {
 | 
					 | 
				
			||||||
		.list {
 | 
					 | 
				
			||||||
			padding: 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			.item {
 | 
					 | 
				
			||||||
				list-style: none;
 | 
					 | 
				
			||||||
				width: 32%;
 | 
					 | 
				
			||||||
				margin: 10px 10px 10px 0;
 | 
					 | 
				
			||||||
				padding-bottom: 14px;
 | 
					 | 
				
			||||||
				float: left;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				.avatar {
 | 
					 | 
				
			||||||
					width: 48px;
 | 
					 | 
				
			||||||
					height: 48px;
 | 
					 | 
				
			||||||
					float: left;
 | 
					 | 
				
			||||||
					display: block;
 | 
					 | 
				
			||||||
					margin-right: 10px;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				.name {
 | 
					 | 
				
			||||||
					margin-top: 0;
 | 
					 | 
				
			||||||
					margin-bottom: 0;
 | 
					 | 
				
			||||||
					font-weight: normal;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				.meta {
 | 
					 | 
				
			||||||
					margin-top: 5px;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	&.forks {
 | 
						&.forks {
 | 
				
			||||||
		.list {
 | 
							.list {
 | 
				
			||||||
			margin-top: 0;
 | 
								margin-top: 0;
 | 
				
			||||||
@@ -982,6 +953,36 @@
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
// End of .repository
 | 
					// End of .repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					&.user-cards {
 | 
				
			||||||
 | 
						.list {
 | 
				
			||||||
 | 
							padding: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.item {
 | 
				
			||||||
 | 
								list-style: none;
 | 
				
			||||||
 | 
								width: 32%;
 | 
				
			||||||
 | 
								margin: 10px 10px 10px 0;
 | 
				
			||||||
 | 
								padding-bottom: 14px;
 | 
				
			||||||
 | 
								float: left;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								.avatar {
 | 
				
			||||||
 | 
									width: 48px;
 | 
				
			||||||
 | 
									height: 48px;
 | 
				
			||||||
 | 
									float: left;
 | 
				
			||||||
 | 
									display: block;
 | 
				
			||||||
 | 
									margin-right: 10px;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								.name {
 | 
				
			||||||
 | 
									margin-top: 0;
 | 
				
			||||||
 | 
									margin-bottom: 0;
 | 
				
			||||||
 | 
									font-weight: normal;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								.meta {
 | 
				
			||||||
 | 
									margin-top: 5px;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#search-repo-box,
 | 
					#search-repo-box,
 | 
				
			||||||
#search-user-box {
 | 
					#search-user-box {
 | 
				
			||||||
	.results {
 | 
						.results {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
.user {
 | 
					.user {
 | 
				
			||||||
 | 
						&:not(.icon) {
 | 
				
			||||||
		padding-top: 15px;
 | 
							padding-top: 15px;
 | 
				
			||||||
		padding-bottom: @footer-margin * 2;
 | 
							padding-bottom: @footer-margin * 2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	&.settings {
 | 
						&.settings {
 | 
				
			||||||
		.list {
 | 
							.list {
 | 
				
			||||||
@@ -38,6 +40,17 @@
 | 
				
			|||||||
						&:not(:last-child) {
 | 
											&:not(:last-child) {
 | 
				
			||||||
							border-bottom: 1px solid #eaeaea;
 | 
												border-bottom: 1px solid #eaeaea;
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											.octicon {
 | 
				
			||||||
 | 
												margin-left: 1px;
 | 
				
			||||||
 | 
												margin-right: 5px;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											&.follow {
 | 
				
			||||||
 | 
												.ui.button {
 | 
				
			||||||
 | 
													width: 100%;
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -47,4 +60,18 @@
 | 
				
			|||||||
			margin-top: 25px;
 | 
								margin-top: 25px;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&.followers {
 | 
				
			||||||
 | 
							.header.name {
 | 
				
			||||||
 | 
								font-size: 20px;
 | 
				
			||||||
 | 
								line-height: 24px;
 | 
				
			||||||
 | 
								vertical-align: middle;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.follow {
 | 
				
			||||||
 | 
								.ui.button {
 | 
				
			||||||
 | 
									padding: 8px 15px;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -135,19 +135,32 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
				
			|||||||
		m.Group("/users", func() {
 | 
							m.Group("/users", func() {
 | 
				
			||||||
			m.Group("/:username", func() {
 | 
								m.Group("/:username", func() {
 | 
				
			||||||
				m.Get("/keys", user.ListPublicKeys)
 | 
									m.Get("/keys", user.ListPublicKeys)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									m.Get("/followers", user.ListFollowers)
 | 
				
			||||||
 | 
									m.Group("/following", func() {
 | 
				
			||||||
 | 
										m.Get("", user.ListFollowing)
 | 
				
			||||||
 | 
										m.Get("/:target", user.CheckFollowing)
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		}, ReqToken())
 | 
							}, ReqToken())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m.Group("/user", func() {
 | 
							m.Group("/user", func() {
 | 
				
			||||||
 | 
								m.Combo("/emails").Get(user.ListEmails).
 | 
				
			||||||
 | 
									Post(bind(api.CreateEmailOption{}), user.AddEmail).
 | 
				
			||||||
 | 
									Delete(bind(api.CreateEmailOption{}), user.DeleteEmail)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								m.Get("/followers", user.ListMyFollowers)
 | 
				
			||||||
 | 
								m.Group("/following", func() {
 | 
				
			||||||
 | 
									m.Get("", user.ListMyFollowing)
 | 
				
			||||||
 | 
									m.Combo("/:username").Get(user.CheckMyFollowing).Put(user.Follow).Delete(user.Unfollow)
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			m.Group("/keys", func() {
 | 
								m.Group("/keys", func() {
 | 
				
			||||||
				m.Combo("").Get(user.ListMyPublicKeys).
 | 
									m.Combo("").Get(user.ListMyPublicKeys).
 | 
				
			||||||
					Post(bind(api.CreateKeyOption{}), user.CreatePublicKey)
 | 
										Post(bind(api.CreateKeyOption{}), user.CreatePublicKey)
 | 
				
			||||||
				m.Combo("/:id").Get(user.GetPublicKey).
 | 
									m.Combo("/:id").Get(user.GetPublicKey).
 | 
				
			||||||
					Delete(user.DeletePublicKey)
 | 
										Delete(user.DeletePublicKey)
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
			m.Combo("/emails").Get(user.ListEmails).
 | 
					 | 
				
			||||||
				Post(bind(api.CreateEmailOption{}), user.AddEmail).
 | 
					 | 
				
			||||||
				Delete(bind(api.CreateEmailOption{}), user.DeleteEmail)
 | 
					 | 
				
			||||||
		}, ReqToken())
 | 
							}, ReqToken())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Repositories
 | 
							// Repositories
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										121
									
								
								routers/api/v1/user/followers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								routers/api/v1/user/followers.go
									
									
									
									
									
										Normal 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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -244,11 +244,7 @@ func Action(ctx *middleware.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error(4, "Action(%s): %v", ctx.Params(":action"), err)
 | 
							ctx.Handle(500, fmt.Sprintf("Action (%s)", ctx.Params(":action")), err)
 | 
				
			||||||
		ctx.JSON(200, map[string]interface{}{
 | 
					 | 
				
			||||||
			"ok":  false,
 | 
					 | 
				
			||||||
			"err": err.Error(),
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -217,7 +217,7 @@ func Home(ctx *middleware.Context) {
 | 
				
			|||||||
	ctx.HTML(200, HOME)
 | 
						ctx.HTML(200, HOME)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func renderItems(ctx *middleware.Context, total int, getter func(page int) ([]*models.User, error)) {
 | 
					func RenderUserCards(ctx *middleware.Context, total int, getter func(page int) ([]*models.User, error), tpl base.TplName) {
 | 
				
			||||||
	page := ctx.QueryInt("page")
 | 
						page := ctx.QueryInt("page")
 | 
				
			||||||
	if page <= 0 {
 | 
						if page <= 0 {
 | 
				
			||||||
		page = 1
 | 
							page = 1
 | 
				
			||||||
@@ -230,21 +230,23 @@ func renderItems(ctx *middleware.Context, total int, getter func(page int) ([]*m
 | 
				
			|||||||
		ctx.Handle(500, "getter", err)
 | 
							ctx.Handle(500, "getter", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Data["Watchers"] = items
 | 
						ctx.Data["Cards"] = items
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.HTML(200, WATCHERS)
 | 
						ctx.HTML(200, tpl)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Watchers(ctx *middleware.Context) {
 | 
					func Watchers(ctx *middleware.Context) {
 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("repo.watchers")
 | 
						ctx.Data["Title"] = ctx.Tr("repo.watchers")
 | 
				
			||||||
 | 
						ctx.Data["CardsTitle"] = ctx.Tr("repo.watchers")
 | 
				
			||||||
	ctx.Data["PageIsWatchers"] = true
 | 
						ctx.Data["PageIsWatchers"] = true
 | 
				
			||||||
	renderItems(ctx, ctx.Repo.Repository.NumWatches, ctx.Repo.Repository.GetWatchers)
 | 
						RenderUserCards(ctx, ctx.Repo.Repository.NumWatches, ctx.Repo.Repository.GetWatchers, WATCHERS)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Stars(ctx *middleware.Context) {
 | 
					func Stars(ctx *middleware.Context) {
 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("repo.stargazers")
 | 
						ctx.Data["Title"] = ctx.Tr("repo.stargazers")
 | 
				
			||||||
 | 
						ctx.Data["CardsTitle"] = ctx.Tr("repo.stargazers")
 | 
				
			||||||
	ctx.Data["PageIsStargazers"] = true
 | 
						ctx.Data["PageIsStargazers"] = true
 | 
				
			||||||
	renderItems(ctx, ctx.Repo.Repository.NumStars, ctx.Repo.Repository.GetStargazers)
 | 
						RenderUserCards(ctx, ctx.Repo.Repository.NumStars, ctx.Repo.Repository.GetStargazers, WATCHERS)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Forks(ctx *middleware.Context) {
 | 
					func Forks(ctx *middleware.Context) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,6 @@ package user
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/Unknwon/com"
 | 
						"github.com/Unknwon/com"
 | 
				
			||||||
	"github.com/Unknwon/paginater"
 | 
						"github.com/Unknwon/paginater"
 | 
				
			||||||
@@ -21,7 +20,6 @@ import (
 | 
				
			|||||||
const (
 | 
					const (
 | 
				
			||||||
	DASHBOARD base.TplName = "user/dashboard/dashboard"
 | 
						DASHBOARD base.TplName = "user/dashboard/dashboard"
 | 
				
			||||||
	ISSUES    base.TplName = "user/dashboard/issues"
 | 
						ISSUES    base.TplName = "user/dashboard/issues"
 | 
				
			||||||
	STARS     base.TplName = "user/stars"
 | 
					 | 
				
			||||||
	PROFILE   base.TplName = "user/profile"
 | 
						PROFILE   base.TplName = "user/profile"
 | 
				
			||||||
	ORG_HOME  base.TplName = "org/home"
 | 
						ORG_HOME  base.TplName = "org/home"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -338,67 +336,6 @@ func showOrgProfile(ctx *middleware.Context) {
 | 
				
			|||||||
	ctx.HTML(200, ORG_HOME)
 | 
						ctx.HTML(200, ORG_HOME)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Profile(ctx *middleware.Context) {
 | 
					 | 
				
			||||||
	ctx.Data["Title"] = "Profile"
 | 
					 | 
				
			||||||
	ctx.Data["PageIsUserProfile"] = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	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
 | 
					 | 
				
			||||||
		uname = strings.TrimSuffix(uname, ".keys")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	u, err := models.GetUserByName(uname)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if models.IsErrUserNotExist(err) {
 | 
					 | 
				
			||||||
			ctx.Handle(404, "GetUserByName", err)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			ctx.Handle(500, "GetUserByName", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Show SSH keys.
 | 
					 | 
				
			||||||
	if isShowKeys {
 | 
					 | 
				
			||||||
		ShowSSHKeys(ctx, u.Id)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if u.IsOrganization() {
 | 
					 | 
				
			||||||
		showOrgProfile(ctx)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	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:
 | 
					 | 
				
			||||||
		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 Email2User(ctx *middleware.Context) {
 | 
					func Email2User(ctx *middleware.Context) {
 | 
				
			||||||
	u, err := models.GetUserByEmail(ctx.Query("email"))
 | 
						u, err := models.GetUserByEmail(ctx.Query("email"))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										145
									
								
								routers/user/profile.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								routers/user/profile.go
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
				
			||||||
							
								
								
									
										47
									
								
								templates/repo/user_cards.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								templates/repo/user_cards.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -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" .}}
 | 
					{{template "base/head" .}}
 | 
				
			||||||
<div class="repository watchers">
 | 
					<div class="repository watchers">
 | 
				
			||||||
	{{template "repo/header" .}}
 | 
						{{template "repo/header" .}}
 | 
				
			||||||
	<div class="ui container">
 | 
						{{template "repo/user_cards" .}}
 | 
				
			||||||
		<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>
 | 
					 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
{{template "base/footer" .}}
 | 
					{{template "base/footer" .}}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								templates/user/meta/followers.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								templates/user/meta/followers.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					{{template "base/head" .}}
 | 
				
			||||||
 | 
					<div class="user followers">
 | 
				
			||||||
 | 
						{{template "user/meta/header" .}}
 | 
				
			||||||
 | 
						{{template "repo/user_cards" .}}
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{{template "base/footer" .}}
 | 
				
			||||||
							
								
								
									
										25
									
								
								templates/user/meta/header.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								templates/user/meta/header.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -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>
 | 
				
			||||||
							
								
								
									
										0
									
								
								templates/user/meta/stars.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								templates/user/meta/stars.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -39,6 +39,33 @@
 | 
				
			|||||||
								</li>
 | 
													</li>
 | 
				
			||||||
							{{end}}
 | 
												{{end}}
 | 
				
			||||||
							<li><i class="icon octicon octicon-clock"></i> {{.i18n.Tr "user.join_on"}} {{DateFmtShort .Owner.Created}}</li>
 | 
												<li><i class="icon octicon octicon-clock"></i> {{.i18n.Tr "user.join_on"}} {{DateFmtShort .Owner.Created}}</li>
 | 
				
			||||||
 | 
												<li>
 | 
				
			||||||
 | 
													<i class="user icon"></i>
 | 
				
			||||||
 | 
													<a href="{{.Owner.HomeLink}}/followers">
 | 
				
			||||||
 | 
														{{.Owner.NumFollowers}} {{.i18n.Tr "user.followers"}}
 | 
				
			||||||
 | 
													</a>
 | 
				
			||||||
 | 
													-
 | 
				
			||||||
 | 
													<a href="{{.Owner.HomeLink}}/following">
 | 
				
			||||||
 | 
														{{.Owner.NumFollowing}} {{.i18n.Tr "user.following"}}
 | 
				
			||||||
 | 
													</a>
 | 
				
			||||||
 | 
												</li>
 | 
				
			||||||
 | 
												{{/*
 | 
				
			||||||
 | 
												<li>
 | 
				
			||||||
 | 
													<i class="octicon octicon-star"></i>
 | 
				
			||||||
 | 
													<a href="{{.Owner.HomeLink}}/stars">
 | 
				
			||||||
 | 
														{{.Owner.NumStars}} {{.i18n.Tr "user.starred"}}
 | 
				
			||||||
 | 
													</a>
 | 
				
			||||||
 | 
												</li>
 | 
				
			||||||
 | 
												*/}}
 | 
				
			||||||
 | 
												{{if and .IsSigned (ne .SignedUserName .Owner.Name)}}
 | 
				
			||||||
 | 
												<li class="follow">
 | 
				
			||||||
 | 
													{{if .SignedUser.IsFollowing .Owner.Id}}
 | 
				
			||||||
 | 
													<a class="ui basic red button" href="{{.Link}}/action/unfollow?redirect_to={{$.Link}}"><i class="octicon octicon-person"></i> {{.i18n.Tr "user.unfollow"}}</a>
 | 
				
			||||||
 | 
													{{else}}
 | 
				
			||||||
 | 
													<a class="ui basic green button" href="{{.Link}}/action/follow?redirect_to={{$.Link}}"><i class="octicon octicon-person"></i> {{.i18n.Tr "user.follow"}}</a>
 | 
				
			||||||
 | 
													{{end}}
 | 
				
			||||||
 | 
												</li>
 | 
				
			||||||
 | 
												{{end}}
 | 
				
			||||||
						</ul>
 | 
											</ul>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user