* Tab on user profile to show starred repos * Make golint happy and use transactions on StarRepo function * x -> sess * Use sess.Close() instead of sess.Rollback() * Add copyright * Fix lintmaster
@@ -273,7 +273,6 @@ func runWeb(ctx *cli.Context) error { | |||||
m.Get("", user.Profile) | m.Get("", user.Profile) | ||||
m.Get("/followers", user.Followers) | m.Get("/followers", user.Followers) | ||||
m.Get("/following", user.Following) | m.Get("/following", user.Following) | ||||
m.Get("/stars", user.Stars) | |||||
}) | }) | ||||
m.Get("/attachments/:uuid", func(ctx *context.Context) { | m.Get("/attachments/:uuid", func(ctx *context.Context) { | ||||
@@ -2146,66 +2146,6 @@ func NotifyWatchers(act *Action) error { | |||||
return notifyWatchers(x, act) | return notifyWatchers(x, act) | ||||
} | } | ||||
// _________ __ | |||||
// / _____// |______ _______ | |||||
// \_____ \\ __\__ \\_ __ \ | |||||
// / \| | / __ \| | \/ | |||||
// /_______ /|__| (____ /__| | |||||
// \/ \/ | |||||
// Star contains the star information | |||||
type Star struct { | |||||
ID int64 `xorm:"pk autoincr"` | |||||
UID int64 `xorm:"UNIQUE(s)"` | |||||
RepoID int64 `xorm:"UNIQUE(s)"` | |||||
} | |||||
// StarRepo star or unstar repository. | |||||
func StarRepo(userID, repoID int64, star bool) (err error) { | |||||
if star { | |||||
if IsStaring(userID, repoID) { | |||||
return nil | |||||
} | |||||
if _, err = x.Insert(&Star{UID: userID, RepoID: repoID}); err != nil { | |||||
return err | |||||
} else if _, err = x.Exec("UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repoID); err != nil { | |||||
return err | |||||
} | |||||
_, err = x.Exec("UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?", userID) | |||||
} else { | |||||
if !IsStaring(userID, repoID) { | |||||
return nil | |||||
} | |||||
if _, err = x.Delete(&Star{0, userID, repoID}); err != nil { | |||||
return err | |||||
} else if _, err = x.Exec("UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repoID); err != nil { | |||||
return err | |||||
} | |||||
_, err = x.Exec("UPDATE `user` SET num_stars = num_stars - 1 WHERE id = ?", userID) | |||||
} | |||||
return err | |||||
} | |||||
// IsStaring checks if user has starred given repository. | |||||
func IsStaring(userID, repoID int64) bool { | |||||
has, _ := x.Get(&Star{0, userID, repoID}) | |||||
return has | |||||
} | |||||
// GetStargazers returns the users who gave stars to this repository | |||||
func (repo *Repository) GetStargazers(page int) ([]*User, error) { | |||||
users := make([]*User, 0, ItemsPerPage) | |||||
sess := x. | |||||
Limit(ItemsPerPage, (page-1)*ItemsPerPage). | |||||
Where("star.repo_id=?", repo.ID) | |||||
if setting.UsePostgreSQL { | |||||
sess = sess.Join("LEFT", "star", `"user".id=star.uid`) | |||||
} else { | |||||
sess = sess.Join("LEFT", "star", "user.id=star.uid") | |||||
} | |||||
return users, sess.Find(&users) | |||||
} | |||||
// ___________ __ | // ___________ __ | ||||
// \_ _____/__________| | __ | // \_ _____/__________| | __ | ||||
// | __)/ _ \_ __ \ |/ / | // | __)/ _ \_ __ \ |/ / | ||||
@@ -0,0 +1,87 @@ | |||||
// Copyright 2016 The Gitea 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 models | |||||
// Star represents a starred repo by an user. | |||||
type Star struct { | |||||
ID int64 `xorm:"pk autoincr"` | |||||
UID int64 `xorm:"UNIQUE(s)"` | |||||
RepoID int64 `xorm:"UNIQUE(s)"` | |||||
} | |||||
// StarRepo or unstar repository. | |||||
func StarRepo(userID, repoID int64, star bool) error { | |||||
sess := x.NewSession() | |||||
defer sess.Close() | |||||
if err := sess.Begin(); err != nil { | |||||
return err | |||||
} | |||||
if star { | |||||
if IsStaring(userID, repoID) { | |||||
return nil | |||||
} | |||||
if _, err := sess.Insert(&Star{UID: userID, RepoID: repoID}); err != nil { | |||||
return err | |||||
} | |||||
if _, err := sess.Exec("UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repoID); err != nil { | |||||
return err | |||||
} | |||||
if _, err := sess.Exec("UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?", userID); err != nil { | |||||
return err | |||||
} | |||||
} else { | |||||
if !IsStaring(userID, repoID) { | |||||
return nil | |||||
} | |||||
if _, err := sess.Delete(&Star{0, userID, repoID}); err != nil { | |||||
return err | |||||
} | |||||
if _, err := sess.Exec("UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repoID); err != nil { | |||||
return err | |||||
} | |||||
if _, err := sess.Exec("UPDATE `user` SET num_stars = num_stars - 1 WHERE id = ?", userID); err != nil { | |||||
return err | |||||
} | |||||
} | |||||
return sess.Commit() | |||||
} | |||||
// IsStaring checks if user has starred given repository. | |||||
func IsStaring(userID, repoID int64) bool { | |||||
has, _ := x.Get(&Star{0, userID, repoID}) | |||||
return has | |||||
} | |||||
// GetStargazers returns the users that starred the repo. | |||||
func (repo *Repository) GetStargazers(page int) ([]*User, error) { | |||||
users := make([]*User, 0, ItemsPerPage) | |||||
err := x. | |||||
Limit(ItemsPerPage, (page-1)*ItemsPerPage). | |||||
Where("star.repo_id = ?", repo.ID). | |||||
Join("LEFT", "star", "`user`.id = star.uid"). | |||||
Find(&users) | |||||
return users, err | |||||
} | |||||
// GetStarredRepos returns the repos the user starred. | |||||
func (u *User) GetStarredRepos(private bool) (repos []*Repository, err error) { | |||||
sess := x. | |||||
Join("INNER", "star", "star.repo_id = repository.id"). | |||||
Where("star.uid = ?", u.ID) | |||||
if !private { | |||||
sess = sess.And("is_private = ?", false) | |||||
} | |||||
err = sess. | |||||
Find(&repos) | |||||
return | |||||
} |
@@ -36,6 +36,7 @@ admin_panel = Admin Panel | |||||
account_settings = Account Settings | account_settings = Account Settings | ||||
settings = Settings | settings = Settings | ||||
your_profile = Your Profile | your_profile = Your Profile | ||||
your_starred = Your starred | |||||
your_settings = Your Settings | your_settings = Your Settings | ||||
activities = Activities | activities = Activities | ||||
@@ -95,6 +95,14 @@ func Profile(ctx *context.Context) { | |||||
if ctx.Written() { | if ctx.Written() { | ||||
return | return | ||||
} | } | ||||
case "stars": | |||||
showPrivateRepos := ctx.IsSigned && ctx.User.ID == ctxUser.ID | |||||
starredRepos, err := ctxUser.GetStarredRepos(showPrivateRepos) | |||||
if err != nil { | |||||
ctx.Handle(500, "GetStarredRepos", err) | |||||
return | |||||
} | |||||
ctx.Data["Repos"] = starredRepos | |||||
default: | default: | ||||
page := ctx.QueryInt("page") | page := ctx.QueryInt("page") | ||||
if page <= 0 { | if page <= 0 { | ||||
@@ -138,11 +146,6 @@ func Following(ctx *context.Context) { | |||||
repo.RenderUserCards(ctx, u.NumFollowing, u.GetFollowing, tplFollowers) | repo.RenderUserCards(ctx, u.NumFollowing, u.GetFollowing, tplFollowers) | ||||
} | } | ||||
// Stars show repositories user starred | |||||
func Stars(ctx *context.Context) { | |||||
} | |||||
// Action response for follow/unfollow user request | // Action response for follow/unfollow user request | ||||
func Action(ctx *context.Context) { | func Action(ctx *context.Context) { | ||||
u := GetUserByParams(ctx) | u := GetUserByParams(ctx) | ||||
@@ -116,6 +116,10 @@ | |||||
<i class="octicon octicon-person"></i> | <i class="octicon octicon-person"></i> | ||||
{{.i18n.Tr "your_profile"}}<!-- Your profile --> | {{.i18n.Tr "your_profile"}}<!-- Your profile --> | ||||
</a> | </a> | ||||
<a class="item" href="{{AppSubUrl}}/{{.SignedUser.Name}}?tab=stars"> | |||||
<i class="octicon octicon-star"></i> | |||||
{{.i18n.Tr "your_starred"}} | |||||
</a> | |||||
<a class="{{if .PageIsUserSettings}}active{{end}} item" href="{{AppSubUrl}}/user/settings"> | <a class="{{if .PageIsUserSettings}}active{{end}} item" href="{{AppSubUrl}}/user/settings"> | ||||
<i class="octicon octicon-settings"></i> | <i class="octicon octicon-settings"></i> | ||||
{{.i18n.Tr "your_settings"}}<!-- Your settings --> | {{.i18n.Tr "your_settings"}}<!-- Your settings --> | ||||
@@ -75,23 +75,28 @@ | |||||
</div> | </div> | ||||
<div class="ui eleven wide column"> | <div class="ui eleven wide column"> | ||||
<div class="ui secondary pointing menu"> | <div class="ui secondary pointing menu"> | ||||
<a class="{{if ne .TabName "activity"}}active{{end}} item" href="{{.Owner.HomeLink}}"> | |||||
<a class='{{if and (ne .TabName "activity") (ne .TabName "stars")}}active{{end}} item' href="{{.Owner.HomeLink}}"> | |||||
<i class="octicon octicon-repo"></i> {{.i18n.Tr "user.repositories"}} | <i class="octicon octicon-repo"></i> {{.i18n.Tr "user.repositories"}} | ||||
</a> | </a> | ||||
<a class="item"> | |||||
<a class="{{if eq .TabName "activity"}}active{{end}} item" href="{{.Owner.HomeLink}}?tab=activity"> | |||||
<i class="octicon octicon-rss"></i> {{.i18n.Tr "user.activity"}} | |||||
</a> | |||||
<a class='{{if eq .TabName "activity"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=activity"> | |||||
<i class="octicon octicon-rss"></i> {{.i18n.Tr "user.activity"}} | |||||
</a> | |||||
<a class='{{if eq .TabName "stars"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=stars"> | |||||
<i class="octicon octicon-star"></i> {{.i18n.Tr "user.starred"}} | |||||
</a> | </a> | ||||
</div> | </div> | ||||
{{if ne .TabName "activity"}} | |||||
{{template "explore/repo_list" .}} | |||||
{{template "base/paginate" .}} | |||||
{{else}} | |||||
<br> | |||||
{{if eq .TabName "activity"}} | |||||
<div class="feeds"> | <div class="feeds"> | ||||
{{template "user/dashboard/feeds" .}} | {{template "user/dashboard/feeds" .}} | ||||
</div> | </div> | ||||
{{else if eq .TabName "stars"}} | |||||
<div class="stars"> | |||||
{{template "explore/repo_list" .}} | |||||
</div> | |||||
{{else}} | |||||
{{template "explore/repo_list" .}} | |||||
{{template "base/paginate" .}} | |||||
{{end}} | {{end}} | ||||
</div> | </div> | ||||
</div> | </div> | ||||