diff --git a/router/router.go b/router/router.go index 362ad030..2525005e 100644 --- a/router/router.go +++ b/router/router.go @@ -35,6 +35,7 @@ func init() { gzipUserLoginPostHandler := handlers.CompressHandler(http.HandlerFunc(UserLoginPostHandler)) gzipUserLogoutHandler := handlers.CompressHandler(http.HandlerFunc(UserLogoutHandler)) gzipUserProfileHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileHandler)) + gzipUserProfileFormHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileFormHandler)) Router = mux.NewRouter() @@ -61,6 +62,7 @@ func init() { 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.Handle("/user/{id}/{username}", gzipUserProfileFormHandler).Name("user_profile").Methods("POST") Router.PathPrefix("/captcha").Methods("GET").HandlerFunc(captcha.ServeFiles) Router.NotFoundHandler = http.HandlerFunc(NotFoundHandler) diff --git a/router/template.go b/router/template.go index 6a6148c5..e206ffac 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, viewProfileTemplate, notFoundTemplate *template.Template +var homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate, viewVerifySuccessTemplate, viewProfileTemplate, viewProfileEditTemplate, viewUserDeleteTemplate, notFoundTemplate *template.Template type templateLoader struct { templ **template.Template @@ -68,6 +68,16 @@ func ReloadTemplates() { name: "user_profile", file: "user/profile.html", }, + templateLoader{ + templ: &viewProfileEditTemplate, + name: "user_profile", + file: "user/profile_edit.html", + }, + templateLoader{ + templ: &viewUserDeleteTemplate, + name: "user_delete", + file: "user/delete_success.html", + }, templateLoader{ templ: ¬FoundTemplate, name: "404", diff --git a/router/templateFunctions.go b/router/templateFunctions.go index 974aa626..14193ca7 100644 --- a/router/templateFunctions.go +++ b/router/templateFunctions.go @@ -65,5 +65,6 @@ var FuncMap = template.FuncMap{ }, "CurrentOrAdmin": userPermission.CurrentOrAdmin, "CurrentUserIdentical": userPermission.CurrentUserIdentical, + "HasAdmin": userPermission.HasAdmin, "GetRole": userPermission.GetRole, } diff --git a/router/templateVariables.go b/router/templateVariables.go index 85c4a6b1..4f8ca4d7 100644 --- a/router/templateVariables.go +++ b/router/templateVariables.go @@ -54,6 +54,18 @@ type UserRegisterTemplateVariables struct { Route *mux.Route // For getting current route in templates } +type UserProfileEditVariables struct { + UserProfile *model.User + UserForm userForms.UserForm + FormErrors map[string][]string + FormInfos 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 +} + type UserVerifyTemplateVariables struct { FormErrors map[string][]string Search SearchForm @@ -75,7 +87,6 @@ type UserLoginFormVariables struct { type UserProfileVariables struct { UserProfile *model.User - FormErrors map[string][]string Search SearchForm Navigation Navigation User *model.User diff --git a/router/userHandler.go b/router/userHandler.go index efa73756..a19f0381 100644 --- a/router/userHandler.go +++ b/router/userHandler.go @@ -54,11 +54,33 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) { userProfile, _, errorUser := userService.RetrieveUserForAdmin(id) if (errorUser == nil) { currentUser := GetUser(r) - view := r.URL.Query().Get("view") - if ((view == "edit")&&(userPermission.CurrentOrAdmin(currentUser, userProfile.Id))) { - } else { + view := r.URL.Query()["edit"] + deleteVar := r.URL.Query()["delete"] + if ((view != nil)&&(userPermission.CurrentOrAdmin(currentUser, userProfile.Id))) { + b := form.UserForm{} + modelHelper.BindValueForm(&b, r) + languages.SetTranslationFromRequest(viewProfileEditTemplate, r, "en-us") + htv := UserProfileEditVariables{&userProfile, b, form.NewErrors(), form.NewInfos(), NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)} + + err := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", htv) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + } else if ((deleteVar != nil)&&(userPermission.CurrentOrAdmin(currentUser, userProfile.Id))) { + err := form.NewErrors() + _, errUser := userService.DeleteUser(w, currentUser, id) + if (errUser != nil) { + err["errors"] = append(err["errors"], errUser.Error()) + } + languages.SetTranslationFromRequest(viewUserDeleteTemplate, r, "en-us") + htv := UserVerifyTemplateVariables{err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)} + errorTmpl := viewUserDeleteTemplate.ExecuteTemplate(w, "index.html", htv) + if errorTmpl != nil { + http.Error(w, errorTmpl.Error(), http.StatusInternalServerError) + } + } else { languages.SetTranslationFromRequest(viewProfileTemplate, r, "en-us") - htv := UserProfileVariables{&userProfile, form.NewErrors(), NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)} + htv := UserProfileVariables{&userProfile, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)} err := viewProfileTemplate.ExecuteTemplate(w, "index.html", htv) if err != nil { @@ -79,7 +101,60 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) { // Getting View User Profile Update func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id := vars["id"] + currentUser := GetUser(r) + userProfile, _, errorUser := userService.RetrieveUserForAdmin(id) + if (errorUser == nil) { + if (userPermission.CurrentOrAdmin(currentUser, userProfile.Id)) { + b := form.UserForm{} + err := form.NewErrors() + infos := form.NewInfos() + T := languages.SetTranslationFromRequest(viewProfileEditTemplate, r, "en-us") + if len(r.PostFormValue("email")) > 0 { + _, err = form.EmailValidation(r.PostFormValue("email"), err) + } + if len(r.PostFormValue("username")) > 0 { + _, err = form.ValidateUsername(r.PostFormValue("username"), err) + } + if (len(err) == 0) { + modelHelper.BindValueForm(&b, r) + err = modelHelper.ValidateForm(&b, err) + if (len(err) == 0) { + userProfile, _, errorUser = userService.UpdateUser(w, &b, currentUser, id) + if (errorUser != nil) { + err["errors"] = append(err["errors"], errorUser.Error()) + } + if (len(err) == 0) { + infos["infos"] = append(infos["infos"], T("profile_updated")) + } + } + } + htv := UserProfileEditVariables{&userProfile, b, err, infos, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)} + errorTmpl := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", htv) + if errorTmpl != nil { + http.Error(w, errorTmpl.Error(), http.StatusInternalServerError) + } + } else { + searchForm := NewSearchForm() + searchForm.HideAdvancedSearch = true + languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us") + err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)}) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + } + } else { + searchForm := NewSearchForm() + searchForm.HideAdvancedSearch = true + + languages.SetTranslationFromRequest(notFoundTemplate, r, "en-us") + err := notFoundTemplate.ExecuteTemplate(w, "index.html", NotFoundTemplateVariables{Navigation{}, searchForm, GetUser(r), r.URL, mux.CurrentRoute(r)}) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + } } // Post Registration controller, we do some check on the form here, the rest on user service @@ -104,7 +179,6 @@ func UserRegisterPostHandler(w http.ResponseWriter, r *http.Request) { err["errors"] = append(err["errors"], errorUser.Error()) } if (len(err) == 0) { - b := form.RegistrationForm{} languages.SetTranslationFromRequest(viewRegisterSuccessTemplate, r, "en-us") htv := UserRegisterTemplateVariables{b, err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)} errorTmpl := viewRegisterSuccessTemplate.ExecuteTemplate(w, "index.html", htv) @@ -173,8 +247,3 @@ func UserLogoutHandler(w http.ResponseWriter, r *http.Request) { 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/form/formValidator.go b/service/user/form/formValidator.go index 03c7d605..24ee85f7 100644 --- a/service/user/form/formValidator.go +++ b/service/user/form/formValidator.go @@ -36,6 +36,10 @@ func NewErrors() map[string][]string { err := make(map[string][]string) return err } +func NewInfos() map[string][]string { + infos := make(map[string][]string) + return infos +} func IsAgreed(t_and_c string) bool { if t_and_c == "1" { return true @@ -46,7 +50,7 @@ func IsAgreed(t_and_c string) bool { // RegistrationForm is used when creating a user. type RegistrationForm struct { Username string `form:"username" needed:"true" len_min:"3" len_max:"20"` - Email string `form:"email"` + Email string `form:"email" needed:"true"` Password string `form:"password" needed:"true" len_min:"6" len_max:"25" equalInput:"Confirm_Password"` Confirm_Password string `form:"password_confirmation" omit:"true" needed:"true"` CaptchaID string `form:"captchaID" omit:"true" needed:"true"` @@ -61,7 +65,13 @@ type LoginForm struct { // UserForm is used when updating a user. type UserForm struct { - Email string `form:"email"` + Username string `form:"username" needed:"true" len_min:"3" len_max:"20"` + Email string `form:"email" needed:"true"` + Language string `form:"language" default:"en-us"` + CurrentPassword string `form:"password" len_min:"6" len_max:"25" omit:"true"` + Password string `form:"password" len_min:"6" len_max:"25" equalInput:"Confirm_Password"` + Confirm_Password string `form:"password_confirmation" omit:"true"` + Status int `form:"language" default:"0"` } // PasswordForm is used when updating a user password. diff --git a/service/user/user.go b/service/user/user.go index 6ac7927d..1c6314cf 100644 --- a/service/user/user.go +++ b/service/user/user.go @@ -11,6 +11,7 @@ import ( "github.com/ewhal/nyaa/db" "github.com/ewhal/nyaa/model" formStruct "github.com/ewhal/nyaa/service/user/form" + "github.com/ewhal/nyaa/service/user/permission" "github.com/ewhal/nyaa/util/crypto" "github.com/ewhal/nyaa/util/log" "github.com/ewhal/nyaa/util/modelHelper" @@ -151,46 +152,47 @@ func UpdateUserCore(user *model.User) (int, error) { } // UpdateUser updates a user. -func UpdateUser(w http.ResponseWriter, r *http.Request, id string) (*model.User, int, error) { +func UpdateUser(w http.ResponseWriter, form *formStruct.UserForm, currentUser *model.User, id string) (model.User, int, error) { var user model.User if db.ORM.First(&user, id).RecordNotFound() { - return &user, http.StatusNotFound, errors.New("User is not found.") - } - switch r.FormValue("type") { - case "password": - var passwordForm formStruct.PasswordForm - modelHelper.BindValueForm(&passwordForm, r) - log.Debugf("form %+v\n", passwordForm) - err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(passwordForm.CurrentPassword)) - if err != nil { - log.Error("Password Incorrect.") - return &user, http.StatusInternalServerError, errors.New("User is not updated. Password Incorrect.") - } else { - newPassword, err := bcrypt.GenerateFromPassword([]byte(passwordForm.Password), 10) - if err != nil { - return &user, http.StatusInternalServerError, errors.New("User is not updated. Password not Generated.") - } else { - passwordForm.Password = string(newPassword) - modelHelper.AssignValue(&user, &passwordForm) - } - } - default: - var form formStruct.UserForm - modelHelper.BindValueForm(&form, r) - log.Debugf("form %+v\n", form) - modelHelper.AssignValue(&user, &form) + return user, http.StatusNotFound, errors.New("User is not found.") } + if (form.Password != "") { + err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(form.CurrentPassword)) + if err != nil && !userPermission.HasAdmin(currentUser) { + log.Error("Password Incorrect.") + return user, http.StatusInternalServerError, errors.New("User is not updated. Password Incorrect.") + } else { + newPassword, err := bcrypt.GenerateFromPassword([]byte(form.Password), 10) + if err != nil { + return user, http.StatusInternalServerError, errors.New("User is not updated. Password not Generated.") + } else { + form.Password = string(newPassword) + } + } + } else { // Then no change of password + form.Password = user.Password + } + if !userPermission.HasAdmin(currentUser) { // We don't want users to be able to modify some fields + form.Status = user.Status + form.Username = user.Username + } + log.Debugf("form %+v\n", form) + modelHelper.AssignValue(&user, form) + status, err := UpdateUserCore(&user) if err != nil { - return &user, status, err + return user, status, err } - status, err = SetCookie(w, user.Token) - return &user, status, err + if (userPermission.CurrentUserIdentical(currentUser, user.Id)) { + status, err = SetCookie(w, user.Token) + } + return user, status, err } // DeleteUser deletes a user. -func DeleteUser(w http.ResponseWriter, id string) (int, error) { +func DeleteUser(w http.ResponseWriter, currentUser *model.User, id string) (int, error) { var user model.User if db.ORM.First(&user, id).RecordNotFound() { return http.StatusNotFound, errors.New("User is not found.") @@ -198,8 +200,10 @@ func DeleteUser(w http.ResponseWriter, id string) (int, error) { if db.ORM.Delete(&user).Error != nil { return http.StatusInternalServerError, errors.New("User is not deleted.") } - status, err := ClearCookie(w) - return status, err + if (userPermission.CurrentUserIdentical(currentUser, user.Id)) { + return ClearCookie(w) + } + return http.StatusOK, nil } // RetrieveCurrentUser retrieves a current user. diff --git a/templates/_profile_edit.html b/templates/_profile_edit.html new file mode 100644 index 00000000..e751e081 --- /dev/null +++ b/templates/_profile_edit.html @@ -0,0 +1,105 @@ +{{define "profile_edit_content"}} + {{with .UserProfile}} + {{ range (index $.FormInfos "infos")}} +