Added Follow&Unfollow
Cette révision appartient à :
Parent
8277143dfa
révision
9bbd544a20
10 fichiers modifiés avec 98 ajouts et 9 suppressions
|
@ -19,8 +19,8 @@ type User struct {
|
||||||
// TODO: move this to PublicUser
|
// TODO: move this to PublicUser
|
||||||
LikingCount int `json:"likingCount" gorm:"-"`
|
LikingCount int `json:"likingCount" gorm:"-"`
|
||||||
LikedCount int `json:"likedCount" gorm:"-"`
|
LikedCount int `json:"likedCount" gorm:"-"`
|
||||||
Likings []User `gorm:"foreignkey:userId;associationforeignkey:follower_id;many2many:user_follows"`
|
Likings []User // Don't work `gorm:"foreignkey:user_id;associationforeignkey:follower_id;many2many:user_follows"`
|
||||||
Liked []User `gorm:"foreignkey:follower_id;associationforeignkey:userId;many2many:user_follows"`
|
Liked []User // Don't work `gorm:"foreignkey:follower_id;associationforeignkey:user_id;many2many:user_follows"`
|
||||||
|
|
||||||
Md5 string `json:"md5"` // Used for gravatar
|
Md5 string `json:"md5"` // Used for gravatar
|
||||||
Torrents []Torrents `gorm:"ForeignKey:UploaderId"`
|
Torrents []Torrents `gorm:"ForeignKey:UploaderId"`
|
||||||
|
|
|
@ -36,6 +36,7 @@ func init() {
|
||||||
gzipUserLoginPostHandler := handlers.CompressHandler(http.HandlerFunc(UserLoginPostHandler))
|
gzipUserLoginPostHandler := handlers.CompressHandler(http.HandlerFunc(UserLoginPostHandler))
|
||||||
gzipUserLogoutHandler := handlers.CompressHandler(http.HandlerFunc(UserLogoutHandler))
|
gzipUserLogoutHandler := handlers.CompressHandler(http.HandlerFunc(UserLogoutHandler))
|
||||||
gzipUserProfileHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileHandler))
|
gzipUserProfileHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileHandler))
|
||||||
|
gzipUserFollowHandler := handlers.CompressHandler(http.HandlerFunc(UserFollowHandler))
|
||||||
gzipUserProfileFormHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileFormHandler))
|
gzipUserProfileFormHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileFormHandler))
|
||||||
|
|
||||||
Router = mux.NewRouter()
|
Router = mux.NewRouter()
|
||||||
|
@ -65,6 +66,7 @@ func init() {
|
||||||
Router.Handle("/user/login", gzipUserLoginPostHandler).Name("user_login").Methods("POST")
|
Router.Handle("/user/login", gzipUserLoginPostHandler).Name("user_login").Methods("POST")
|
||||||
Router.Handle("/user/logout", gzipUserLogoutHandler).Name("user_logout")
|
Router.Handle("/user/logout", gzipUserLogoutHandler).Name("user_logout")
|
||||||
Router.Handle("/user/{id}/{username}", gzipUserProfileHandler).Name("user_profile").Methods("GET")
|
Router.Handle("/user/{id}/{username}", gzipUserProfileHandler).Name("user_profile").Methods("GET")
|
||||||
|
Router.Handle("/user/{id}/{username}/follow", gzipUserFollowHandler).Name("user_follow").Methods("GET")
|
||||||
Router.Handle("/user/{id}/{username}", gzipUserProfileFormHandler).Name("user_profile").Methods("POST")
|
Router.Handle("/user/{id}/{username}", gzipUserProfileFormHandler).Name("user_profile").Methods("POST")
|
||||||
Router.PathPrefix("/captcha").Methods("GET").HandlerFunc(captcha.ServeFiles)
|
Router.PathPrefix("/captcha").Methods("GET").HandlerFunc(captcha.ServeFiles)
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,10 @@ var FuncMap = template.FuncMap{
|
||||||
ret = ret + "<li><a id=\"page-prev\" href=\"" + url.String() + "?" + currentUrl.RawQuery + "\" aria-label=\"Previous\"><span aria-hidden=\"true\">«</span></a></li>"
|
ret = ret + "<li><a id=\"page-prev\" href=\"" + url.String() + "?" + currentUrl.RawQuery + "\" aria-label=\"Previous\"><span aria-hidden=\"true\">«</span></a></li>"
|
||||||
}
|
}
|
||||||
startValue := 1
|
startValue := 1
|
||||||
if nav.CurrentPage > pagesSelectable {
|
if nav.CurrentPage > pagesSelectable/2 {
|
||||||
startValue = (int(math.Min((float64(nav.CurrentPage)+math.Floor(float64(pagesSelectable)/2)), maxPages)) - pagesSelectable + 1)
|
startValue = (int(math.Min((float64(nav.CurrentPage)+math.Floor(float64(pagesSelectable)/2)), maxPages)) - pagesSelectable + 1)
|
||||||
}
|
}
|
||||||
endValue := (startValue + pagesSelectable - 1)
|
endValue := (startValue + pagesSelectable -1)
|
||||||
if endValue > int(maxPages) {
|
if endValue > int(maxPages) {
|
||||||
endValue = int(maxPages)
|
endValue = int(maxPages)
|
||||||
}
|
}
|
||||||
|
@ -67,4 +67,5 @@ var FuncMap = template.FuncMap{
|
||||||
"CurrentUserIdentical": userPermission.CurrentUserIdentical,
|
"CurrentUserIdentical": userPermission.CurrentUserIdentical,
|
||||||
"HasAdmin": userPermission.HasAdmin,
|
"HasAdmin": userPermission.HasAdmin,
|
||||||
"GetRole": userPermission.GetRole,
|
"GetRole": userPermission.GetRole,
|
||||||
|
"IsFollower": userPermission.IsFollower,
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,7 @@ type UserLoginFormVariables struct {
|
||||||
|
|
||||||
type UserProfileVariables struct {
|
type UserProfileVariables struct {
|
||||||
UserProfile *model.User
|
UserProfile *model.User
|
||||||
|
FormInfos map[string][]string
|
||||||
Search SearchForm
|
Search SearchForm
|
||||||
Navigation Navigation
|
Navigation Navigation
|
||||||
User *model.User
|
User *model.User
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
|
||||||
"github.com/ewhal/nyaa/model"
|
"github.com/ewhal/nyaa/model"
|
||||||
"github.com/ewhal/nyaa/service/captcha"
|
"github.com/ewhal/nyaa/service/captcha"
|
||||||
|
@ -56,6 +59,10 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if (errorUser == nil) {
|
if (errorUser == nil) {
|
||||||
currentUser := GetUser(r)
|
currentUser := GetUser(r)
|
||||||
view := r.URL.Query()["edit"]
|
view := r.URL.Query()["edit"]
|
||||||
|
follow := r.URL.Query()["followed"]
|
||||||
|
unfollow := r.URL.Query()["unfollowed"]
|
||||||
|
infosForm := form.NewInfos()
|
||||||
|
|
||||||
deleteVar := r.URL.Query()["delete"]
|
deleteVar := r.URL.Query()["delete"]
|
||||||
if ((view != nil)&&(userPermission.CurrentOrAdmin(currentUser, userProfile.Id))) {
|
if ((view != nil)&&(userPermission.CurrentOrAdmin(currentUser, userProfile.Id))) {
|
||||||
b := form.UserForm{}
|
b := form.UserForm{}
|
||||||
|
@ -80,8 +87,14 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
|
http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
languages.SetTranslationFromRequest(viewProfileTemplate, r, "en-us")
|
T := languages.SetTranslationFromRequest(viewProfileTemplate, r, "en-us")
|
||||||
htv := UserProfileVariables{&userProfile, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
|
if (follow != nil) {
|
||||||
|
infosForm["infos"] = append(infosForm["infos"], fmt.Sprintf(T("user_followed_msg"), userProfile.Username))
|
||||||
|
}
|
||||||
|
if (unfollow != nil) {
|
||||||
|
infosForm["infos"] = append(infosForm["infos"], fmt.Sprintf(T("user_unfollowed_msg"), userProfile.Username))
|
||||||
|
}
|
||||||
|
htv := UserProfileVariables{&userProfile, infosForm, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
|
||||||
|
|
||||||
err := viewProfileTemplate.ExecuteTemplate(w, "index.html", htv)
|
err := viewProfileTemplate.ExecuteTemplate(w, "index.html", htv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -251,3 +264,23 @@ func UserLogoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
url, _ := Router.Get("home").URL()
|
url, _ := Router.Get("home").URL()
|
||||||
http.Redirect(w, r, url.String(), http.StatusSeeOther)
|
http.Redirect(w, r, url.String(), http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func UserFollowHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var followAction string
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
id := vars["id"]
|
||||||
|
currentUser := GetUser(r)
|
||||||
|
user, _, errorUser := userService.RetrieveUserForAdmin(id)
|
||||||
|
if (errorUser == nil) {
|
||||||
|
if (!userPermission.IsFollower(&user, currentUser)) {
|
||||||
|
followAction = "followed"
|
||||||
|
userService.SetFollow(&user, currentUser)
|
||||||
|
} else {
|
||||||
|
followAction = "unfollowed"
|
||||||
|
userService.RemoveFollow(&user, currentUser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
url, _ := Router.Get("user_profile").URL("id", strconv.Itoa(int(user.Id)), "username", user.Username)
|
||||||
|
http.Redirect(w, r, url.String()+"?"+followAction, http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package userPermission
|
||||||
import (
|
import (
|
||||||
"github.com/ewhal/nyaa/model"
|
"github.com/ewhal/nyaa/model"
|
||||||
"github.com/ewhal/nyaa/util/log"
|
"github.com/ewhal/nyaa/util/log"
|
||||||
|
"github.com/ewhal/nyaa/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HasAdmin checks that user has an admin permission.
|
// HasAdmin checks that user has an admin permission.
|
||||||
|
@ -38,3 +39,12 @@ func GetRole(user *model.User) string {
|
||||||
}
|
}
|
||||||
return "Member"
|
return "Member"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsFollower(user *model.User, currentUser *model.User) bool {
|
||||||
|
var likingUserCount int
|
||||||
|
db.ORM.Model(&model.UserFollows{}).Where("user_id = ? and following = ?", user.Id, currentUser.Id).Count(&likingUserCount)
|
||||||
|
if likingUserCount != 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -262,6 +262,11 @@ func RetrieveUserForAdmin(id string) (model.User, int, error) {
|
||||||
if db.ORM.Preload("Torrents").First(&user, id).RecordNotFound() {
|
if db.ORM.Preload("Torrents").First(&user, id).RecordNotFound() {
|
||||||
return user, http.StatusNotFound, errors.New("User is not found.")
|
return user, http.StatusNotFound, errors.New("User is not found.")
|
||||||
}
|
}
|
||||||
|
var liked,likings []model.User
|
||||||
|
db.ORM.Joins("JOIN user_follows on user_follows.user_id=?", user.Id).Where("users.user_id = user_follows.following").Group("users.user_id").Find(&likings)
|
||||||
|
db.ORM.Joins("JOIN user_follows on user_follows.following=?", user.Id).Where("users.user_id = user_follows.user_id").Group("users.user_id").Find(&liked)
|
||||||
|
user.Likings = likings
|
||||||
|
user.Liked = liked
|
||||||
return user, http.StatusOK, nil
|
return user, http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +277,7 @@ func RetrieveUsersForAdmin() []model.User {
|
||||||
db.ORM.Find(&users)
|
db.ORM.Find(&users)
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
db.ORM.Model(&user)
|
db.ORM.Model(&user)
|
||||||
db.ORM.Model(&user).Related("Torrents").Find(&model.Torrents{})
|
db.ORM.Model(&user).Related("Torrents").Related("Likings").Related("Liked").Find(&model.Torrents{})
|
||||||
userArr = append(userArr, user)
|
userArr = append(userArr, user)
|
||||||
}
|
}
|
||||||
return userArr
|
return userArr
|
||||||
|
@ -287,3 +292,17 @@ func CreateUserAuthentication(w http.ResponseWriter, r *http.Request) (int, erro
|
||||||
status, err := SetCookieHandler(w, username, pass)
|
status, err := SetCookieHandler(w, username, pass)
|
||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetFollow(user *model.User, follower *model.User) {
|
||||||
|
if (follower.Id > 0 && user.Id > 0) {
|
||||||
|
var userFollows = model.UserFollows{UserID: user.Id, FollowerID: follower.Id}
|
||||||
|
db.ORM.Create(&userFollows)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveFollow(user *model.User, follower *model.User) {
|
||||||
|
if (follower.Id > 0 && user.Id > 0) {
|
||||||
|
var userFollows = model.UserFollows{UserID: user.Id, FollowerID: follower.Id}
|
||||||
|
db.ORM.Delete(&userFollows)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
{{define "title"}}{{ T "profile_page" .UserProfile.Username }}{{end}}
|
{{define "title"}}{{ T "profile_page" .UserProfile.Username }}{{end}}
|
||||||
{{define "contclass"}}cont-view{{end}}
|
{{define "contclass"}}cont-view{{end}}
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
|
{{ range (index $.FormInfos "infos")}}
|
||||||
|
<div class="alert alert-info"><a class="panel-close close" data-dismiss="alert">×</a><i class="glyphicon glyphicon-info-sign"></i> {{ . }}</div>
|
||||||
|
{{end}}
|
||||||
<div class="row profile">
|
<div class="row profile">
|
||||||
{{with .UserProfile}}
|
{{with .UserProfile}}
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
|
@ -24,7 +27,11 @@
|
||||||
<div class="profile-userbuttons">
|
<div class="profile-userbuttons">
|
||||||
{{if gt $.User.Id 0 }}
|
{{if gt $.User.Id 0 }}
|
||||||
{{if not (CurrentUserIdentical $.User .Id) }}
|
{{if not (CurrentUserIdentical $.User .Id) }}
|
||||||
<button type="button" class="btn btn-success btn-sm">{{ T "follow"}}</button>
|
{{if not (IsFollower . $.User)}}
|
||||||
|
<a href="{{ genRoute "user_follow" "id" ( print .Id ) "username" .Username }}" class="btn btn-success btn-sm">{{ T "follow"}}</a>
|
||||||
|
{{else}}
|
||||||
|
<a href="{{ genRoute "user_follow" "id" ( print .Id ) "username" .Username }}" class="btn btn-danger btn-sm">{{ T "unfollow"}}</a>
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<!-- <button type="button" class="btn btn-danger btn-sm">Message</button> -->
|
<!-- <button type="button" class="btn btn-danger btn-sm">Message</button> -->
|
||||||
|
|
|
@ -24,7 +24,11 @@
|
||||||
<div class="profile-userbuttons">
|
<div class="profile-userbuttons">
|
||||||
{{if gt $.User.Id 0 }}
|
{{if gt $.User.Id 0 }}
|
||||||
{{if not (CurrentUserIdentical $.User .Id) }}
|
{{if not (CurrentUserIdentical $.User .Id) }}
|
||||||
<button type="button" class="btn btn-success btn-sm">{{ T "follow"}}</button>
|
{{if not (IsFollower . $.User)}}
|
||||||
|
<a href="{{ genRoute "user_follow" "id" .Id }}" class="btn btn-success btn-sm">{{ T "follow"}}</a>
|
||||||
|
{{else}}
|
||||||
|
<a href="{{ genRoute "user_follow" "id" .Id }}" class="btn btn-danger btn-sm">{{ T "unfollow"}}</a>
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<!-- <button type="button" class="btn btn-danger btn-sm">Message</button> -->
|
<!-- <button type="button" class="btn btn-danger btn-sm">Message</button> -->
|
||||||
|
|
|
@ -119,6 +119,18 @@
|
||||||
"id":"follow",
|
"id":"follow",
|
||||||
"translation": "Follow"
|
"translation": "Follow"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id":"unfollow",
|
||||||
|
"translation": "Unfollow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":"user_followed_msg",
|
||||||
|
"translation": "You have followed %s!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":"user_unfollowed_msg",
|
||||||
|
"translation": "You have unfollowed %s!"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id":"profile_page",
|
"id":"profile_page",
|
||||||
"translation": "%s Profile Page"
|
"translation": "%s Profile Page"
|
||||||
|
|
Référencer dans un nouveau ticket