#1692 APIs: Users Followers

- User profile un/follow
- List user's followers/following
This commit is contained in:
Unknwon committed 2015-12-21 04:24:11 -08:00
1 parent c62a6b7a12
commit a49af93faf
27 files changed
+641 -271

No files matched your search

+1 -1
View File
@@ -13,7 +13,7 @@ watch_dirs = [
watch_exts = [".go"]
build_delay = 1500
cmds = [
["go", "install", "-race"], # sqlite redis memcache cert pam tidb
["go", "install", "-v", "-race"], # sqlite redis memcache cert pam tidb
["go", "build", "-race"],
["./gogs", "web"]
]
+1 -1
View File
@@ -16,7 +16,7 @@ NOW = $(shell date -u '+%Y%m%d%I%M%S')
.IGNORE: public/css/gogs.css
build: $(GENERATED)
go install -ldflags '$(LDFLAGS)' -tags '$(TAGS)'
go install -v -ldflags '$(LDFLAGS)' -tags '$(TAGS)'
cp '$(GOPATH)/bin/gogs' .
govet:
+3 -1
View File
@@ -3,7 +3,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra
![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true)
##### Current version: 0.8.12
##### Current version: 0.8.13
| 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)
- [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/)
- [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
@@ -101,6 +102,7 @@ There are 5 ways to install Gogs:
- [Drone](https://github.com/drone/drone) (CI)
- [Fabric8](http://fabric8.io/) (DevOps)
- [Taiga](https://taiga.io/) (Project Management)
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs) (IT)
### Product Support
+1
View File
@@ -73,6 +73,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
- [Drone](https://github.com/drone/drone)(CI)
- [Fabric8](http://fabric8.io/)(DevOps)
- [Taiga](https://taiga.io/)(项目管理)
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs)(IT)
### 产品支持
+11 -1
View File
@@ -289,7 +289,13 @@ func runWeb(ctx *cli.Context) {
// ***** END: Admin *****
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) {
attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid"))
if err != nil {
@@ -319,6 +325,10 @@ func runWeb(ctx *cli.Context) {
m.Post("/issues/attachments", repo.UploadIssueAttachment)
}, ignSignIn)
m.Group("/:username", func() {
m.Get("/action/:action", user.Action)
}, reqSignIn)
if macaron.Env == macaron.DEV {
m.Get("/template/*", dev.TemplatePreview)
}
+3 -1
View File
@@ -230,8 +230,10 @@ join_on = Joined on
repositories = Repositories
activity = Public Activity
followers = Followers
starred = Starred
starred = Starred repositories
following = Following
follow = Follow
unfollow = Unfollow
form.name_reserved = Username '%s' is reserved.
form.name_pattern_not_allowed = Username pattern '%s' is not allowed.
+1 -1
View File
@@ -17,7 +17,7 @@ import (
"github.com/gogits/gogs/modules/setting"
)
const APP_VER = "0.8.12.1219"
const APP_VER = "0.8.13.1221"
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
+41
View File
@@ -665,6 +665,47 @@ func GetIssueUserPairsByMode(uid, rid int64, isClosed bool, page, filterMode int
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.
type IssueStats struct {
OpenCount, ClosedCount int64
+78 -77
View File
@@ -56,7 +56,7 @@ type User struct {
LowerName string `xorm:"UNIQUE NOT NULL"`
Name string `xorm:"UNIQUE NOT NULL"`
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"`
Passwd string `xorm:"NOT NULL"`
LoginType LoginType
@@ -78,24 +78,24 @@ type User struct {
// Maximum repository creation limit, -1 means use gloabl default
MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"`
// Permissions.
// Permissions
IsActive bool
IsAdmin bool
AllowGitHook bool
AllowImportLocal bool // Allow migrate repository by local path
// Avatar.
// Avatar
Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
AvatarEmail string `xorm:"NOT NULL"`
UseCustomAvatar bool
// Counters.
NumFollowers int
NumFollowings int
NumStars int
NumRepos int
// Counters
NumFollowers int
NumFollowing int `xorm:"NOT NULL"`
NumStars int
NumRepos int
// For organization.
// For organization
Description string
NumTeams int
NumMembers int
@@ -263,6 +263,34 @@ func (u *User) AvatarLink() string {
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.
func (u *User) NewGitSig() *git.Signature {
return &git.Signature{
@@ -1077,100 +1105,73 @@ func SearchUserByName(opt SearchOption) (us []*User, err error) {
return us, err
}
// Follow is connection request for receiving user notification.
// ___________ .__ .__
// \_ _____/___ | | | | ______ _ __
// | __)/ _ \| | | | / _ \ \/ \/ /
// | \( <_> ) |_| |_( <_> ) /
// \___ / \____/|____/____/\____/ \/\_/
// \/
// Follow represents relations of user and his/her followers.
type Follow struct {
ID int64 `xorm:"pk autoincr"`
UserID 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.
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()
defer sess.Close()
sess.Begin()
if _, err = sess.Insert(&Follow{UserID: userId, FollowID: followId}); err != nil {
sess.Rollback()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
rawSql := "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?"
if _, err = sess.Exec(rawSql, followId); err != nil {
sess.Rollback()
if _, err = sess.Insert(&Follow{UserID: userID, FollowID: followID}); err != nil {
return err
}
rawSql = "UPDATE `user` SET num_followings = num_followings + 1 WHERE id = ?"
if _, err = sess.Exec(rawSql, userId); err != nil {
sess.Rollback()
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()
}
// UnFollowUser unmarks someone be another's follower.
func UnFollowUser(userId int64, unFollowId int64) (err error) {
session := x.NewSession()
defer session.Close()
session.Begin()
// UnfollowUser unmarks someone be another's follower.
func UnfollowUser(userID, followID int64) (err error) {
if userID == followID || !IsFollowing(userID, followID) {
return nil
}
if _, err = session.Delete(&Follow{UserID: userId, FollowID: unFollowId}); err != nil {
session.Rollback()
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
rawSql := "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?"
if _, err = session.Exec(rawSql, unFollowId); err != nil {
session.Rollback()
if _, err = sess.Delete(&Follow{UserID: userID, FollowID: followID}); err != nil {
return err
}
rawSql = "UPDATE `user` SET num_followings = num_followings - 1 WHERE id = ?"
if _, err = session.Exec(rawSql, userId); err != nil {
session.Rollback()
if _, err = sess.Exec("UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil {
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
if _, err = sess.Exec("UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil {
return err
}
return sess.Commit()
}
+2 -2
View File
@@ -4364,7 +4364,7 @@ func confLocaleLocale_deDeIni() (*asset, error) {
return a, nil
}
var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x7d\xeb\x72\xdc\x56\xee\xe7\x77\x3e\x05\xe3\x29\x97\xed\x2a\xb9\x5d\x99\xfc\xf7\x52\xa9\x38\x59\x59\x8a\x2f\xf3\xb7\x2c\x8d\xa5\x24\x3b\x9b\x72\x31\xec\x26\xbb\x9b\xe3\x6e\xb2\x87\x64\x4b\xd6\x4c\xcd\x1b\xec\x03\xec\xf3\xed\x93\x2c\xf0\x03\x70\x2e\x24\x5b\xb2\x33\x53\xeb\x0f\x16\xfb\x1c\x9c\x3b\x0e\x0e\x80\x03\xe0\xe4\xbb\x5d\x56\x94\xdd\x22\x7d\x9e\x1e\xa7\xbb\xbc\xaa\x37\x65\xd7\xa5\x5d\xb9\x59\x3e\x5d\x37\x5d\x5f\x16\xe9\xab\xaa\xa7\xdf\xed\x75\xb5\x28\x93\x64\xdd\x6c\x4b\x02\x7d\x4d\x7f\x92\x22\xef\xd6\xf3\x26\x6f\x0b\x4a\x38\xb5\xef\xa4\xfc\xb4\xdb\x34\x2d\x03\xfd\x28\x5f\xc9\xba\xdc\xec\xb8\x0c\xfd\x49\xba\x6a\x55\x67\x55\x4d\x3f\x2f\xe9\x2b\x7d\x53\x4b\x4a\xb3\xef\x2d\xe9\x7c\xdf\x4b\xda\x7e\x67\x49\x3f\xed\x92\xb6\x5c\x55\xd4\x9b\x96\x92\xde\xeb\x67\x72\x53\xce\xbb\xaa\xe7\x96\x7e\x91\xaf\xe4\xba\x6c\xbb\xaa\xe1\xda\x7f\x96\xaf\x64\x97\xaf\x18\xe0\x82\xfe\x24\x7d\xb9\xdd\x6d\x72\x14\xb8\xd2\xcf\x64\x93\xd7\xab\xbd\xc0\xbc\xd5\xcf\x64\xd1\x96\x94\x95\xd5\xe5\x0d\xa5\x9e\xe0\xc7\x6c\x36\x4b\xf6\x34\x09\xd9\xae\x6d\x96\xd5\xa6\xcc\xf2\xba\xc8\xb6\x32\xcc\x9f\x28\x3d\xd5\xf4\x94\xd2\x53\x4e\xc7\x10\xca\x82\x86\x9a\xe5\x9d\x8e\x83\xe6\x92\x46\x9e\x77\x09\xaa\xaa\xf3\xad\x95\xe6\xcf\xa4\xdc\xe6\xd5\x86\x67\x8d\xff\x52\xbf\xbb\xee\xa6\xc1\xd4\x5e\xe8\x27\xcd\x41\xd6\xdf\xee\x4a\x4c\xc1\xd3\x2b\xfa\x4a\x16\xf9\xae\x5f\xac\x73\xee\xa6\x7c\x25\x04\xb4\x6b\x68\x2e\x9a\xf6\x16\x70\xf6\x23\x69\xda\x55\x5e\x57\x7f\xcf\x7b\x99\x9f\xf3\xe0\x67\xb2\xad\xda\xb6\xe1\xa9\x3d\xc3\x47\x42\x23\xcf\xb8\x1e\x4a\x79\x47\x93\x10\xd4\xc2\x39\xdb\x6a\xd5\xca\x2c\x72\xe6\x19\x7e\x71\x2d\x92\xa7\x35\x49\x96\xab\x6d\xd9\xb4\x1f\x35\xf5\x25\x7f\x0e\xaa\xa4\xce\x69\x6e\xdc\xaf\xbc\xa6\xf5\xd0\xdc\x33\xfc\x88\x00\xba\x24\x2f\xb6\x34\xc3\xbb\xbc\x2e\x79\xea\x8e\xf9\x17\xcd\x17\xfd\x4a\xf2\xc5\xa2\xd9\xd7\x7d\xd6\x95\x7d\x5f\xd5\x2b\x5e\x83\x63\x49\x4a\x2f\x35\x29\x09\xf2\x5c\xda\x6d\xb3\x77\xab\x4c\xe9\x7f\xa1\x9f\xe9\x85\xfc\x94\xbc\xa0\x10\x32\x5d\x49\x1e\x49\x97\x2d\xcb\xb2\x90\xb1\x74\xe9\x4b\xfa\x4e\x76\xfb\xcd\x86\x66\xf3\x6f\xfb\xb2\xeb\xb9\xd0\x05\xfd\xa6\xf1\xcb\xef\xa4\xea\x3a\xfa\xa0\xe4\x37\xf8\x48\x68\x49\xeb\x05\x06\x73\x82\x8f\x24\xf9\xb5\x2b\xf3\x76\xb1\xfe\x90\xc8\x5f\xf4\x95\x3f\x18\x25\x0f\x2d\x36\xe3\x97\xe2\x96\xb4\x60\x0d\x24\x8b\xa6\xe0\x1f\x27\xf4\x87\xaa\xae\xea\xae\xcf\x37\x9b\x0f\x89\x7e\x30\x98\x7c\xc9\x02\xf4\x55\x8f\x59\xd0\xc4\xf4\xb2\x2f\x77\x1d\xaf\x60\xfa\xb2\x6a\xbb\xfe\x69\x5f\x11\x0e\xbf\xdf\xd7\x49\xd1\x2c\x3e\xd2\xee\xe0\x9d\x8e\x96\xdf\x2c\x53\x9a\xac\x47\xb4\x3f\xda\x7d\x5d\xd3\xf4\xa4\xaf\x1a\x9a\x32\x6a\xa6\xa2\xf6\x4f\x01\x7d\x94\xee\x36\x65\xde\x11\x48\x99\x17\xe9\x77\x79\xda\xe7\xed\xaa\xec\x9f\x3f\xc8\xe6\xb4\x2b\x3f\x3e\x48\xd7\x6d\xb9\x7c\xfe\xe0\x61\xf7\xe0\xfb\x57\x7b\x2a\xb6\xa9\xea\xb2\xfb\xee\x59\xfe\x7d\xba\xc8\x29\x87\xa6\xf1\x36\x9d\x97\x4b\xde\x84\xd4\x56\x4a\xd8\x5f\xaf\x78\x03\xde\xf6\x6b\x6e\x90\x30\x81\x3e\xba\x94\x29\xc0\x57\x09\x2f\x00\x51\x88\xac\x98\x1b\xb5\x43\x87\x90\xdc\xd2\x02\x9c\xdd\x5e\xfe\xf9\xed\x51\x7a\x41\x24\x6f\xd5\x96\xf8\xa6\xff\xa8\xc4\x37\x29\x8d\xf6\xaa\x3a\x7d\x31\x4b\xa8\xac\x4d\xc8\x69\xde\xe7\x73\xee\xbb\x5b\x7d\xce\x94\xcd\xe9\xf2\xb0\x45\x99\x88\x82\x60\x76\x7d\xb4\x2c\x53\x1b\x9c\xea\x50\xaa\xe0\xea\x78\xc7\xa4\x81\xd2\xdd\xcc\x5e\xc8\x9c\x51\x55\xe9\x9b\x77\xef\xce\x4f\x5f\xa4\x65\xbd\xa2\x99\x49\x6f\xaa\x7e\x9d\xee\xfb\xe5\x7f\xcf\x56\x65\x5d\xb6\xf9\x26\x5b\x54\x3c\x29\x2d\x21\x6c\x4a\xb3\x24\x43\x9c\x25\x5d\xb7\x21\xca\x05\x2c\xb8\xbc\x7c\x9b\x9e\x31\x26\xec\xf2\x7e\x8d\x8e\xf4\xeb\xa4\xfb\xdb\x86\x27\xca\x35\x78\xb5\x2e\x53\x6c\x06\x00\x35\xcb\xe1\xbc\xa4\x85\xf6\x75\x96\x94\x6d\x9b\x11\x61\xed\x6f\x79\x9a\xb5\xce\x43\xd0\x52\x1d\x61\x7b\xdd\xf4\xb4\x8c\x29\xca\x49\x15\x55\x7d\x9d\x6f\xaa\x82\x26\xdb\x4f\x48\x5c\x16\x89\x45\x43\xeb\xc6\xa5\x09\x33\x9b\x1b\x0c\x35\x5f\xd0\xb9\xd0\xa5\x0f\x66\x0f\x40\x88\x1f\x3c\x7d\x30\x4b\xea\x26\x13\x2a\xc1\x24\xbb\xa8\xba\x7c\x4e\xe4\x5b\x8e\x93\xd6\xa8\xe1\x5f\x18\x7f\xa4\x2b\x0a\x91\x46\x10\x3c\xb7\x7c\x44\xe1\x64\x60\xe4\xca\x89\x8e\x83\xd8\x28\x99\x09\xc7\x6e\x34\xc9\xad\xaf\x90\x25\x97\x30\x1a\x73\x62\x0b\x66\xd8\x75\xbc\xdb\x6d\xaa\x85\x34\xfd\x4a\xf2\x3c\xa2\xf1\x81\xad\x93\x12\xc2\x01\x51\x2c\x2f\x40\x17\xea\x35\x93\xad\x34\xa2\xff\x28\xbf\x2e\x69\xe7\xac\xf7\x2b\x39\xb4\x36\xcd\xbe\xf8\x0a\x04\xc5\x56\xce\xd3\x93\xf4\x7d\x43\x1d\x06\x76\x38\x00\xdf\xc4\x31\x11\x06\xe6\x11\xda\x72\xdb\xf4\x3c\x71\x5a\xac\xa2\xe5\xb9\xa9\x28\x93\x46\xda\xe5\xd7\x44\x16\xfb\x46\xb6\x64\x41\x5b\x6e\xc1\x15\x13\x05\xdb\xd3\x41\x2f\xdb\x82\xe8\x88\x6c\x0d\x4b\x8b\x71\x10\x50\xdb\x3d\xed\xa6\x35\x55\xc6\x13\xcf\x8c\x0a\x55\x39\xd5\x4f\x0c\x89\xea\xc1\x2e\xa7\x9d\xdb\xd0\xa1\xca\x0b\x7d\x8a\x0f\xfd\x1d\xd6\x4f\xbd\xca\x97\x4b\xea\x55\x47\xbb\xe2\x75\xba\xd8\x34\xb4\xa5\x7e\x7a\xff\xb6\xe3\x0d\xb3\xce\x76\x4d\x0b\x06\x85\xb2\x2e\xe8\xd3\xa5\x05\x13\xcd\x10\xf5\x7e\x3b\xa7\x5f\x37\xeb\x8a\x08\x35\xa6\x9d\x4b\x30\xf3\x44\xa9\xd4\xc4\xbe\xaLine truncated
var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x7d\xeb\x72\xdc\x48\xae\xe6\x7f\x3e\x05\xdb\x13\x0e\xdb\x11\x72\x39\x7a\xfa\xec\x25\x3a\xda\xdd\x2b\x4b\xed\xcb\x39\x96\xa5\x63\xc9\xdd\x3b\xdb\xe1\x60\xb3\x8a\xac\x2a\x1e\x57\x91\x35\x24\x4b\xb2\x66\x62\xde\x60\x1f\x60\x9f\x6f\x9f\x64\x81\x0f\x40\x5e\x48\x96\x64\xf7\x9c\x58\xff\xb0\x58\x99\xc8\x3b\x12\x09\x20\x01\x64\xbe\xdb\x65\x45\xd9\x2d\xd2\xe7\xe9\x71\xba\xcb\xab\x7a\x53\x76\x5d\xda\x95\x9b\xe5\xd3\x75\xd3\xf5\x65\x91\xbe\xaa\x7a\xfa\xdd\x5e\x57\x8b\x32\x49\xd6\xcd\xb6\x24\xd0\xd7\xf4\x27\x29\xf2\x6e\x3d\x6f\xf2\xb6\xa0\x84\x53\xfb\x4e\xca\xcf\xbb\x4d\xd3\x32\xd0\xcf\xf2\x95\xac\xcb\xcd\x8e\xcb\xd0\x9f\xa4\xab\x56\x75\x56\xd5\xf4\xf3\x92\xbe\xd2\x37\xb5\xa4\x34\xfb\xde\x92\xce\xf7\xbd\xa4\xed\x77\x96\xf4\x61\x97\xb4\xe5\xaa\xa2\xde\xb4\x94\xf4\x5e\x3f\x93\x9b\x72\xde\x55\x3d\xb7\xf4\xab\x7c\x25\xd7\x65\xdb\x55\x0d\xd7\xfe\x8b\x7c\x25\xbb\x7c\xc5\x00\x17\xf4\x27\xe9\xcb\xed\x6e\x93\xa3\xc0\x95\x7e\x26\x9b\xbc\x5e\xed\x05\xe6\xad\x7e\x26\x8b\xb6\xa4\xac\xac\x2e\x6f\x28\xf5\x04\x3f\x66\xb3\x59\xb2\xa7\x49\xc8\x76\x6d\xb3\xac\x36\x65\x96\xd7\x45\xb6\x95\x61\x7e\xa0\xf4\x54\xd3\x53\x4a\x4f\x39\x1d\x43\x28\x0b\x1a\x6a\x96\x77\x3a\x0e\x9a\x4b\x1a\x79\xde\x25\xa8\xaa\xce\xb7\x56\x9a\x3f\x93\x72\x9b\x57\x1b\x9e\x35\xfe\x4b\xfd\xee\xba\x9b\x06\x53\x7b\xa1\x9f\x34\x07\x59\x7f\xbb\x2b\x31\x05\x4f\xaf\xe8\x2b\x59\xe4\xbb\x7e\xb1\xce\xb9\x9b\xf2\x95\x10\xd0\xae\xa1\xb9\x68\xda\x5b\xc0\xd9\x8f\xa4\x69\x57\x79\x5d\xfd\x2d\xef\x65\x7e\xce\x83\x9f\xc9\xb6\x6a\xdb\x86\xa7\xf6\x0c\x1f\x09\x8d\x3c\xe3\x7a\x28\xe5\x1d\x4d\x42\x50\x0b\xe7\x6c\xab\x55\x2b\xb3\xc8\x99\x67\xf8\xc5\xb5\x48\x9e\xd6\x24\x59\xae\xb6\x65\xd3\x7e\xd2\xd4\x97\xfc\x39\xa8\x92\x3a\xa7\xb9\x71\xbf\xf2\x9a\xd6\x43\x73\xcf\xf0\x23\x02\xe8\x92\xbc\xd8\xd2\x0c\xef\xf2\xba\xe4\xa9\x3b\xe6\x5f\x34\x5f\xf4\x2b\xc9\x17\x8b\x66\x5f\xf7\x59\x57\xf6\x7d\x55\xaf\x78\x0d\x8e\x25\x29\xbd\xd4\xa4\x24\xc8\x73\x69\xb7\xcd\xde\xad\x32\xa5\xff\x85\x7e\xa6\x17\xf2\x53\xf2\x82\x42\xc8\x74\x25\x79\x24\x5d\xb6\x2c\xcb\x42\xc6\xd2\xa5\x2f\xe9\x3b\xd9\xed\x37\x1b\x9a\xcd\xbf\xee\xcb\xae\xe7\x42\x17\xf4\x9b\xc6\x2f\xbf\x93\xaa\xeb\xe8\x83\x92\xdf\xe0\x23\xa1\x25\xad\x17\x18\xcc\x09\x3e\x92\xe4\xb7\xae\xcc\xdb\xc5\xfa\x63\x22\x7f\xd1\x57\xfe\x60\x94\x3c\xb4\xd8\x8c\x5f\x8a\x5b\xd2\x82\x35\x90\x2c\x9a\x82\x7f\x9c\xd0\x1f\xaa\xba\xaa\xbb\x3e\xdf\x6c\x3e\x26\xfa\xc1\x60\xf2\x25\x0b\xd0\x57\x3d\x66\x41\x13\xd3\xcb\xbe\xdc\x75\xbc\x82\xe9\xcb\xaa\xed\xfa\xa7\x7d\x45\x38\xfc\x7e\x5f\x27\x45\xb3\xf8\x44\xbb\x83\x77\x3a\x5a\x7e\xb3\x4c\x69\xb2\x1e\xd1\xfe\x68\xf7\x75\x4d\xd3\x93\xbe\x6a\x68\xca\xa8\x99\x8a\xda\x3f\x05\xf4\x51\xba\xdb\x94\x79\x47\x20\x65\x5e\xa4\x3f\xe4\x69\x9f\xb7\xab\xb2\x7f\xfe\x20\x9b\xd3\xae\xfc\xf4\x20\x5d\xb7\xe5\xf2\xf9\x83\x87\xdd\x83\x1f\x5f\xed\xa9\xd8\xa6\xaa\xcb\xee\x87\x67\xf9\x8f\xe9\x22\xa7\x1c\x9a\xc6\xdb\x74\x5e\x2e\x79\x13\x52\x5b\x29\x61\x7f\xbd\xe2\x0d\x78\xdb\xaf\xb9\x41\xc2\x04\xfa\xe8\x52\xa6\x00\xdf\x24\xbc\x00\x44\x21\xb2\x62\x6e\xd4\x0e\x1d\x42\x72\x4b\x0b\x70\x76\x7b\xf9\xef\x6f\x8f\xd2\x0b\x22\x79\xab\xb6\xc4\x37\xfd\x47\x25\xbe\x4b\x69\xb4\x57\xd5\xe9\x8b\x59\x42\x65\x6d\x42\x4e\xf3\x3e\x9f\x73\xdf\xdd\xea\x73\xa6\x6c\x4e\x97\x87\x2d\xca\x44\x14\x04\xb3\xeb\xa3\x65\x99\xda\xe0\x54\x87\x52\x05\x57\xc7\x3b\x26\x0d\x94\xee\x66\xf6\x42\xe6\x8c\xaa\x4a\xdf\xbc\x7b\x77\x7e\xfa\x22\x2d\xeb\x15\xcd\x4c\x7a\x53\xf5\xeb\x74\xdf\x2f\xff\x7b\xb6\x2a\xeb\xb2\xcd\x37\xd9\xa2\xe2\x49\x69\x09\x61\x53\x9a\x25\x19\xe2\x2c\xe9\xba\x0d\x51\x2e\x60\xc1\xe5\xe5\xdb\xf4\x8c\x31\x61\x97\xf7\x6b\x74\xa4\x5f\x27\xdd\x5f\x37\x3c\x51\xae\xc1\xab\x75\x99\x62\x33\x00\xa8\x59\x0e\xe7\x25\x2d\xb4\xaf\xb3\xa4\x6c\xdb\x8c\x08\x6b\x7f\xcb\xd3\xac\x75\x1e\x82\x96\xea\x08\xdb\xeb\xa6\xa7\x65\x4c\x51\x4e\xaa\xa8\xea\xeb\x7c\x53\x15\x34\xd9\x7e\x42\xe2\xb2\x48\x2c\x1a\x5a\x37\x2e\x4d\x98\xd9\xdc\x60\xa8\xf9\x82\xce\x85\x2e\x7d\x30\x7b\x00\x42\xfc\xe0\xe9\x83\x59\x52\x37\x99\x50\x09\x26\xd9\x45\xd5\xe5\x73\x22\xdf\x72\x9c\xb4\x46\x0d\xff\xc2\xf8\x23\x5d\x51\x88\x34\x82\xe0\xb9\xe5\x23\x0a\x27\x03\x23\x57\x4e\x74\x1c\xc4\x46\xc9\x4c\x38\x76\xa3\x49\x6e\x7d\x85\x2c\xb9\x84\xd1\x98\x13\x5b\x30\xc3\xae\xe3\xdd\x6e\x53\x2d\xa4\xe9\x57\x92\xe7\x11\x8d\x0f\x6c\x9d\x94\x10\x0e\x88\x62\x79\x01\xba\x50\xaf\x99\x6c\xa5\x11\xfd\x47\xf9\x75\x49\x3b\x67\xbd\x5f\xc9\xa1\xb5\x69\xf6\xc5\x37\x20\x28\xb6\x72\x9e\x9e\xa4\xef\x1b\xea\x30\xb0\xc3\x01\xf8\x26\x8e\x89\x30\x30\x8f\xd0\x96\xdb\xa6\xe7\x89\xd3\x62\x15\x2d\xcf\x4d\x45\x99\x34\xd2\x2e\xbf\x26\xb2\xd8\x37\xb2\x25\x0b\xda\x72\x0b\xae\x98\x28\xd8\x9e\x0e\x7a\xd9\x16\x44\x47\x64\x6b\x58\x5a\x8c\x83\x80\xda\xee\x69\x37\xad\xa9\x32\x9e\x78\x66\x54\xa8\xca\xa9\x7e\x62\x48\x54\x0f\x76\x39\xed\xdc\x86\x0e\x55\x5e\xe8\x53\x7c\xe8\xef\xb0\x7e\xea\x55\xbe\x5c\x52\xaf\x3a\xda\x15\xaf\xd3\xc5\xa6\xa1\x2d\xf5\xe1\xfd\xdb\x8e\x37\xcc\x3a\xdb\x35\x2d\x18\x14\xca\xba\xa0\x4f\x97\x16\x4c\x34\x43\xd4\xfb\xed\x9c\x7e\xdd\xac\x2b\x22\xd4\x98\x76\x2e\xc1\xcc\x13\xa5\x52\x13\xfb\x8Line truncated
func confLocaleLocale_enUsIniBytes() ([]byte, error) {
return bindataRead(
@@ -4379,7 +4379,7 @@ func confLocaleLocale_enUsIni() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 48295, mode: os.FileMode(420), modTime: time.Unix(1450418132, 0)}
info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 48344, mode: os.FileMode(420), modTime: time.Unix(1450614667, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
+2 -1
View File
@@ -242,7 +242,8 @@ func Contexter() macaron.Handler {
ctx.Data["CsrfToken"] = 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["ShowFooterBranding"] = setting.ShowFooterBranding
+41 -26
View File
@@ -2465,31 +2465,6 @@ footer .container .links > *:first-child {
.repository.new.release .prerelease.field {
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 {
margin-top: 0;
}
@@ -2551,6 +2526,31 @@ footer .container .links > *:first-child {
margin-left: 5px;
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-user-box .results {
padding: 0;
@@ -2862,7 +2862,7 @@ footer .container .links > *:first-child {
margin-left: 5px;
margin-top: -3px;
}
.user {
.user:not(.icon) {
padding-top: 15px;
padding-bottom: 80px;
}
@@ -2893,9 +2893,24 @@ footer .container .links > *:first-child {
.user.profile .ui.card .extra.content ul li:not(:last-child) {
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 {
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 {
padding-top: 15px;
padding-bottom: 80px;
+30 -29
View File
@@ -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 {
.list {
margin-top: 0;
@@ -982,6 +953,36 @@
}
// 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-user-box {
.results {
+29 -2
View File
@@ -1,6 +1,8 @@
.user {
padding-top: 15px;
padding-bottom: @footer-margin * 2;
&:not(.icon) {
padding-top: 15px;
padding-bottom: @footer-margin * 2;
}
&.settings {
.list {
@@ -38,6 +40,17 @@
&:not(:last-child) {
border-bottom: 1px solid #eaeaea;
}
.octicon {
margin-left: 1px;
margin-right: 5px;
}
&.follow {
.ui.button {
width: 100%;
}
}
}
}
}
@@ -47,4 +60,18 @@
margin-top: 25px;
}
}
&.followers {
.header.name {
font-size: 20px;
line-height: 24px;
vertical-align: middle;
}
.follow {
.ui.button {
padding: 8px 15px;
}
}
}
}
+16 -3
View File
@@ -135,19 +135,32 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/users", func() {
m.Group("/:username", func() {
m.Get("/keys", user.ListPublicKeys)
m.Get("/followers", user.ListFollowers)
m.Group("/following", func() {
m.Get("", user.ListFollowing)
m.Get("/:target", user.CheckFollowing)
})
})
}, ReqToken())
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.Combo("").Get(user.ListMyPublicKeys).
Post(bind(api.CreateKeyOption{}), user.CreatePublicKey)
m.Combo("/:id").Get(user.GetPublicKey).
Delete(user.DeletePublicKey)
})
m.Combo("/emails").Get(user.ListEmails).
Post(bind(api.CreateEmailOption{}), user.AddEmail).
Delete(bind(api.CreateEmailOption{}), user.DeleteEmail)
}, ReqToken())
// Repositories
+121
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)
}
+1 -5
View File
@@ -244,11 +244,7 @@ func Action(ctx *middleware.Context) {
}
if err != nil {
log.Error(4, "Action(%s): %v", ctx.Params(":action"), err)
ctx.JSON(200, map[string]interface{}{
"ok": false,
"err": err.Error(),
})
ctx.Handle(500, fmt.Sprintf("Action (%s)", ctx.Params(":action")), err)
return
}
+7 -5
View File
@@ -217,7 +217,7 @@ func Home(ctx *middleware.Context) {
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")
if page <= 0 {
page = 1
@@ -230,21 +230,23 @@ func renderItems(ctx *middleware.Context, total int, getter func(page int) ([]*m
ctx.Handle(500, "getter", err)
return
}
ctx.Data["Watchers"] = items
ctx.Data["Cards"] = items
ctx.HTML(200, WATCHERS)
ctx.HTML(200, tpl)
}
func Watchers(ctx *middleware.Context) {
ctx.Data["Title"] = ctx.Tr("repo.watchers")
ctx.Data["CardsTitle"] = ctx.Tr("repo.watchers")
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) {
ctx.Data["Title"] = ctx.Tr("repo.stargazers")
ctx.Data["CardsTitle"] = ctx.Tr("repo.stargazers")
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) {
-63
View File
@@ -7,7 +7,6 @@ package user
import (
"bytes"
"fmt"
"strings"
"github.com/Unknwon/com"
"github.com/Unknwon/paginater"
@@ -21,7 +20,6 @@ import (
const (
DASHBOARD base.TplName = "user/dashboard/dashboard"
ISSUES base.TplName = "user/dashboard/issues"
STARS base.TplName = "user/stars"
PROFILE base.TplName = "user/profile"
ORG_HOME base.TplName = "org/home"
)
@@ -338,67 +336,6 @@ func showOrgProfile(ctx *middleware.Context) {
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) {
u, err := models.GetUserByEmail(ctx.Query("email"))
if err != nil {
+145
View 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
View File
@@ -1 +1 @@
0.8.12.1219
0.8.13.1221
+47
View 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"}}&nbsp;<i class="icon right arrow"></i>
</a>
</div>
</div>
{{end}}
{{end}}
</div>
+1 -51
View File
@@ -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"}}&nbsp;<i class="icon right arrow"></i>
</a>
</div>
</div>
{{end}}
{{end}}
</div>
{{template "repo/user_cards" .}}
</div>
{{template "base/footer" .}}
+6
View 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
View 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>
View File
Whitespace-only changes.
+27
View File
@@ -39,6 +39,33 @@
</li>
{{end}}
<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>
</div>
</div>