From 84cf511fe36e7161d9c9c65fc621952a32ad5e0b Mon Sep 17 00:00:00 2001 From: akuma06 Date: Tue, 9 May 2017 03:36:48 +0200 Subject: [PATCH] Fixed User Profile Page, badge menu, every thing before previous commit (except email) Added Logout --- db/gorm.go | 3 +- model/user.go | 20 +++-- public/css/style.css | 117 +++++++++++++++++++++++++- router/router.go | 2 + router/template.go | 7 +- router/templateFunctions.go | 7 ++ router/templateVariables.go | 30 ++++--- router/userHandler.go | 26 ++++++ service/user/permission/permission.go | 29 ++++--- service/user/user.go | 5 +- templates/_badgemenu.html | 11 +-- templates/_user_list_torrents.html | 48 +++++++++++ templates/user/profile.html | 62 ++++++++++++++ util/languages/translation.go | 1 + 14 files changed, 330 insertions(+), 38 deletions(-) create mode 100644 templates/_user_list_torrents.html create mode 100644 templates/user/profile.html diff --git a/db/gorm.go b/db/gorm.go index 67c32450..bdbf3d1c 100644 --- a/db/gorm.go +++ b/db/gorm.go @@ -30,8 +30,7 @@ func GormInit(conf *config.Config) (*gorm.DB, error) { // db.SingularTable(true) if config.Environment == "DEVELOPMENT" { db.LogMode(true) - db.AutoMigrate(&model.User{}, &model.UserFollows{}) - db.AutoMigrate(&model.User{}, &model.Torrents{}, &model.Comment{}, &model.OldComment{}) + db.AutoMigrate(&model.Torrents{}, &model.UsersFollowers{}, &model.User{}, &model.Comment{}, &model.OldComment{}) // db.Model(&model.User{}).AddIndex("idx_user_token", "token") } diff --git a/model/user.go b/model/user.go index d2064b2b..e4533b79 100644 --- a/model/user.go +++ b/model/user.go @@ -17,17 +17,25 @@ type User struct { UpdatedAt time.Time `gorm:"column:updated_at"` /*Api*/Token string `gorm:"column:api_token"` //ApiTokenExpiry + + // Liking + LikingCount int `json:"likingCount"` + LikedCount int `json:"likedCount"` + Likings []User `gorm:"foreignkey:userId;associationforeignkey:follower_id;many2many:users_followers;"` + Liked []User `gorm:"foreignkey:follower_id;associationforeignkey:userId;many2many:users_followers;"` + + Md5 string `json:"md5"` TokenExpiration time.Time `gorm:"column:api_token_expiry"` Language string `gorm:"column:language"` + Torrents []Torrents `gorm:"ForeignKey:owner_id"` } type PublicUser struct { User *User } -type UserFollows struct { - User User `gorm:"ForeignKey:user_id"` - Following User `gorm:"ForeignKey:following"` -} - - +// UsersFollowers is a relation table to relate users each other. +type UsersFollowers struct { + UserID uint `gorm:"column:userId"` + FollowerID uint `gorm:"column:follower_id"` +} \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css index 8932a359..da2d9089 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -203,6 +203,119 @@ div.container div.blockBody:nth-of-type(2) table tr:first-of-type th:last-of-typ left: -5px; } -#mainmenu .badgemenu { - padding-top: 0; +#mainmenu .badgemenu .profile-image { + line-height: 35px; } + +#mainmenu .navbar-header, #mainmenu .navbar-nav, #mainmenu .navbar-form { + padding-top: 10px; +} + +#mainmenu .badgemenu { + padding-top: 0; +} + +/* PROFILE PAGE */ +/* Profile container */ +.profile { + margin: 20px 0; +} + +/* Profile sidebar */ +.profile-sidebar { + padding: 20px 0 10px 0; + background: #182430; +} + +.profile-userpic img { + float: none; + margin: 0 auto; + width: 50%; + height: 50%; + -webkit-border-radius: 50% !important; + -moz-border-radius: 50% !important; + border-radius: 50% !important; +} + +.profile-usertitle { + text-align: center; + margin-top: 20px; +} + +.profile-usertitle-name { + color: #5a7391; + font-size: 16px; + font-weight: 600; + margin-bottom: 7px; +} + +.profile-usertitle-job { + text-transform: uppercase; + color: #5b9bd1; + font-size: 12px; + font-weight: 600; + margin-bottom: 15px; +} + +.profile-userbuttons { + text-align: center; + margin-top: 10px; +} + +.profile-userbuttons .btn { + text-transform: uppercase; + font-size: 11px; + font-weight: 600; + padding: 6px 15px; + margin-right: 5px; +} + +.profile-userbuttons .btn:last-child { + margin-right: 0px; +} + +.profile-usermenu { + margin-top: 30px; +} + +.profile-usermenu ul li { + border-bottom: 1px solid #f0f4f7; +} + +.profile-usermenu ul li:last-child { + border-bottom: none; +} + +.profile-usermenu ul li a { + color: #93a3b5; + font-size: 14px; + font-weight: 400; +} + +.profile-usermenu ul li a i { + margin-right: 8px; + font-size: 14px; +} + +.profile-usermenu ul li a:hover { + background-color: #fafcfd; + color: #5b9bd1; +} + +.profile-usermenu ul li.active { + border-bottom: none; +} + +.profile-usermenu ul li.active a { + color: #5b9bd1; + background-color: #f6f9fb; + border-left: 2px solid #5b9bd1; + margin-left: -2px; +} + +/* Profile Content */ +.profile-content { + padding: 20px; + background: #fff; + min-height: 460px; +} \ No newline at end of file diff --git a/router/router.go b/router/router.go index b9e5c5a5..362ad030 100644 --- a/router/router.go +++ b/router/router.go @@ -33,6 +33,7 @@ func init() { gzipUserVerifyEmailHandler := handlers.CompressHandler(http.HandlerFunc(UserVerifyEmailHandler)) gzipUserRegisterPostHandler := handlers.CompressHandler(http.HandlerFunc(UserRegisterPostHandler)) gzipUserLoginPostHandler := handlers.CompressHandler(http.HandlerFunc(UserLoginPostHandler)) + gzipUserLogoutHandler := handlers.CompressHandler(http.HandlerFunc(UserLogoutHandler)) gzipUserProfileHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileHandler)) Router = mux.NewRouter() @@ -58,6 +59,7 @@ func init() { Router.Handle("/verify/email/{token}", gzipUserVerifyEmailHandler).Name("user_verify").Methods("GET") Router.Handle("/user/register", gzipUserRegisterPostHandler).Name("user_register").Methods("POST") Router.Handle("/user/login", gzipUserLoginPostHandler).Name("user_login").Methods("POST") + Router.Handle("/user/logout", gzipUserLogoutHandler).Name("user_logout") Router.Handle("/user/{id}/{username}", gzipUserProfileHandler).Name("user_profile").Methods("GET") Router.PathPrefix("/captcha").Methods("GET").HandlerFunc(captcha.ServeFiles) diff --git a/router/template.go b/router/template.go index 678a4ab2..4a26fdb4 100644 --- a/router/template.go +++ b/router/template.go @@ -7,7 +7,7 @@ import ( var TemplateDir = "templates" -var homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate, viewVerifySuccessTemplate *template.Template +var homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate, viewVerifySuccessTemplate, viewProfileTemplate *template.Template type templateLoader struct { templ **template.Template @@ -63,6 +63,11 @@ func ReloadTemplates() { name: "user_login", file: "user/login.html", }, + templateLoader{ + templ: &viewProfileTemplate, + name: "user_profile", + file: "user/profile.html", + }, } for _, templ := range templs { t := template.Must(template.New(templ.name).Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "index.html"), filepath.Join(TemplateDir, templ.file))) diff --git a/router/templateFunctions.go b/router/templateFunctions.go index 67536f61..974aa626 100644 --- a/router/templateFunctions.go +++ b/router/templateFunctions.go @@ -7,6 +7,7 @@ import ( "net/url" "strconv" "github.com/nicksnyder/go-i18n/i18n" + "github.com/ewhal/nyaa/service/user/permission" ) var FuncMap = template.FuncMap{ @@ -59,4 +60,10 @@ var FuncMap = template.FuncMap{ return template.HTML(ret) }, "T": i18n.IdentityTfunc, + "getAvatar": func (hash string, size int) string { + return "https://www.gravatar.com/avatar/"+hash+"?s="+strconv.Itoa(size) + }, + "CurrentOrAdmin": userPermission.CurrentOrAdmin, + "CurrentUserIdentical": userPermission.CurrentUserIdentical, + "GetRole": userPermission.GetRole, } diff --git a/router/templateVariables.go b/router/templateVariables.go index e73bcf15..9edccc65 100644 --- a/router/templateVariables.go +++ b/router/templateVariables.go @@ -20,7 +20,7 @@ import ( type FaqTemplateVariables struct { Navigation Navigation Search SearchForm - User model.User + User *model.User URL *url.URL // For parsing Url in templates Route *mux.Route // For getting current route in templates } @@ -28,7 +28,7 @@ type FaqTemplateVariables struct { type NotFoundTemplateVariables struct { Navigation Navigation Search SearchForm - User model.User + User *model.User URL *url.URL // For parsing Url in templates Route *mux.Route // For getting current route in templates } @@ -38,7 +38,7 @@ type ViewTemplateVariables struct { Captcha captcha.Captcha Search SearchForm Navigation Navigation - User model.User + User *model.User URL *url.URL // For parsing Url in templates Route *mux.Route // For getting current route in templates } @@ -48,7 +48,7 @@ type UserRegisterTemplateVariables struct { FormErrors map[string][]string Search SearchForm Navigation Navigation - User model.User + User *model.User URL *url.URL // For parsing Url in templates Route *mux.Route // For getting current route in templates } @@ -57,7 +57,7 @@ type UserVerifyTemplateVariables struct { FormErrors map[string][]string Search SearchForm Navigation Navigation - User model.User + User *model.User URL *url.URL // For parsing Url in templates Route *mux.Route // For getting current route in templates } @@ -67,7 +67,17 @@ type UserLoginFormVariables struct { FormErrors map[string][]string Search SearchForm Navigation Navigation - User model.User + User *model.User + URL *url.URL // For parsing Url in templates + Route *mux.Route // For getting current route in templates +} + +type UserProfileVariables struct { + UserProfile *model.User + FormErrors map[string][]string + Search SearchForm + Navigation Navigation + User *model.User URL *url.URL // For parsing Url in templates Route *mux.Route // For getting current route in templates } @@ -76,7 +86,7 @@ type HomeTemplateVariables struct { ListTorrents []model.TorrentsJson Search SearchForm Navigation Navigation - User model.User + User *model.User URL *url.URL // For parsing Url in templates Route *mux.Route // For getting current route in templates } @@ -85,7 +95,7 @@ type UploadTemplateVariables struct { Upload UploadForm Search SearchForm Navigation Navigation - User model.User + User *model.User URL *url.URL Route *mux.Route } @@ -136,7 +146,7 @@ func NewSearchForm(params ...string) (searchForm SearchForm) { return } -func GetUser(r *http.Request) model.User { +func GetUser(r *http.Request) *model.User { user, _ , _ := userService.RetrieveCurrentUser(r) - return user + return &user } diff --git a/router/userHandler.go b/router/userHandler.go index 8fad031d..2bcf105b 100644 --- a/router/userHandler.go +++ b/router/userHandler.go @@ -6,6 +6,7 @@ import ( "github.com/ewhal/nyaa/service/captcha" "github.com/ewhal/nyaa/service/user" "github.com/ewhal/nyaa/service/user/form" + "github.com/ewhal/nyaa/service/user/permission" "github.com/ewhal/nyaa/util/languages" "github.com/ewhal/nyaa/util/modelHelper" "github.com/gorilla/mux" @@ -48,7 +49,25 @@ func UserLoginFormHandler(w http.ResponseWriter, r *http.Request) { // Getting User Profile func UserProfileHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id := vars["id"] + userProfile, _, errorUser := userService.RetrieveUserForAdmin(id) + currentUser := GetUser(r) + view := r.URL.Query().Get("view") + if (errorUser == nil) { + if ((view == "edit")&&(userPermission.CurrentOrAdmin(currentUser, userProfile.Id))) { + } else { + languages.SetTranslationFromRequest(viewProfileTemplate, r, "en-us") + htv := UserProfileVariables{&userProfile, form.NewErrors(), NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)} + err := viewProfileTemplate.ExecuteTemplate(w, "index.html", htv) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + } + } else { + NotFoundHandler(w, r) + } } // Getting View User Profile Update @@ -141,6 +160,13 @@ func UserLoginPostHandler(w http.ResponseWriter, r *http.Request) { } +// Logout +func UserLogoutHandler(w http.ResponseWriter, r *http.Request) { + _, _ = userService.ClearCookie(w) + url, _ := Router.Get("home").URL() + http.Redirect(w, r, url.String(), http.StatusSeeOther) +} + // Post Profule Update controller func UserProfilePostHandler(w http.ResponseWriter, r *http.Request) { diff --git a/service/user/permission/permission.go b/service/user/permission/permission.go index 9805f8ef..64edb7be 100644 --- a/service/user/permission/permission.go +++ b/service/user/permission/permission.go @@ -1,10 +1,7 @@ package userPermission import ( - "errors" - "net/http" "github.com/ewhal/nyaa/model" - "github.com/ewhal/nyaa/service/user" "github.com/ewhal/nyaa/util/log" ) @@ -20,14 +17,24 @@ func CurrentOrAdmin(user *model.User, userId uint) bool { } // CurrentUserIdentical check that userId is same as current user's Id. -func CurrentUserIdentical(r *http.Request, userId uint) (bool, error) { - currentUser, err := userService.CurrentUser(r) - if err != nil { - return false, errors.New("Auth failed.") - } - if currentUser.Id != userId { - return false, errors.New("User is not identical.") +func CurrentUserIdentical(user *model.User, userId uint) (bool) { + if user.Id != userId { + return false } - return true, nil + return true +} + +func GetRole(user *model.User) string { + switch user.Status { + case -1 : + return "Banned" + case 0 : + return "Member" + case 1 : + return "Trusted Member" + case 2 : + return "Moderator" + } + return "Member" } diff --git a/service/user/user.go b/service/user/user.go index a942afa8..072ec894 100644 --- a/service/user/user.go +++ b/service/user/user.go @@ -62,6 +62,7 @@ func CreateUserFromForm(registrationForm formStruct.RegistrationForm) (model.Use var user model.User log.Debugf("registrationForm %+v\n", registrationForm) modelHelper.AssignValue(&user, ®istrationForm) + user.Md5 = crypto.GenerateMD5Hash(user.Email) // Gravatar token, err := crypto.GenerateRandomToken32() if err != nil { return user, errors.New("Token not generated.") @@ -136,6 +137,7 @@ func RetrieveUsers() []*model.PublicUser { // UpdateUserCore updates a user. (Applying the modifed data of user). func UpdateUserCore(user *model.User) (int, error) { + user.Md5 = crypto.GenerateMD5Hash(user.Email) token, err := crypto.GenerateRandomToken32() if err != nil { return http.StatusInternalServerError, errors.New("Token not generated.") @@ -245,7 +247,7 @@ func RetrieveUserForAdmin(id string) (model.User, int, error) { if db.ORM.First(&user, id).RecordNotFound() { return user, http.StatusNotFound, errors.New("User is not found.") } - db.ORM.Model(&user) + db.ORM.Model(&user).Related("Torrents").Find(&model.Torrents{}) return user, http.StatusOK, nil } @@ -256,6 +258,7 @@ func RetrieveUsersForAdmin() []model.User { db.ORM.Find(&users) for _, user := range users { db.ORM.Model(&user) + db.ORM.Model(&user).Related("Torrents").Find(&model.Torrents{}) userArr = append(userArr, user) } return userArr diff --git a/templates/_badgemenu.html b/templates/_badgemenu.html index a0b62a0c..301875ce 100644 --- a/templates/_badgemenu.html +++ b/templates/_badgemenu.html @@ -3,17 +3,18 @@