User Settings Notification (WIP)
Cette révision appartient à :
Parent
8081e0b639
révision
32c51a57cb
6 fichiers modifiés avec 105 ajouts et 40 suppressions
13
config/notifications.go
Fichier normal
13
config/notifications.go
Fichier normal
|
@ -0,0 +1,13 @@
|
|||
package config
|
||||
|
||||
|
||||
/*
|
||||
* Here we config the notifications options
|
||||
*/
|
||||
var EnableNotifications = map[string]bool {
|
||||
"new_torrent": true,
|
||||
"new_comment_owner": true,
|
||||
"new_comment_all": true,
|
||||
"new_follower": false,
|
||||
"followed": false,
|
||||
}
|
|
@ -24,6 +24,7 @@ type User struct {
|
|||
ApiToken string `gorm:"column:api_token"`
|
||||
ApiTokenExpiry time.Time `gorm:"column:api_token_expiry"`
|
||||
Language string `gorm:"column:language"`
|
||||
UserSettings string `gorm:"column:settings"`
|
||||
|
||||
// TODO: move this to PublicUser
|
||||
Likings []User // Don't work `gorm:"foreignkey:user_id;associationforeignkey:follower_id;many2many:user_follows"`
|
||||
|
@ -31,9 +32,10 @@ type User struct {
|
|||
|
||||
MD5 string `json:"md5" gorm:"column:md5"` // Hash of email address, used for Gravatar
|
||||
Torrents []Torrent `gorm:"ForeignKey:UploaderID"`
|
||||
Notifications []Notification `gorm:"ForeignKey:UserID"`
|
||||
|
||||
UnreadNotifications int `gorm:"-"` // We don't want to loop every notifications when accessing user unread notif
|
||||
Notifications []Notification `gorm:"ForeignKey:UserID"`
|
||||
Settings *UserSettings `gorm:"-"` // We don't want to loop every notifications when accessing user unread notif
|
||||
}
|
||||
|
||||
type UserJSON struct {
|
||||
|
@ -99,6 +101,10 @@ type UserUploadsOld struct {
|
|||
TorrentId uint `gorm:"column:torrent_id"`
|
||||
}
|
||||
|
||||
type UserSettings struct {
|
||||
settings map[string]interface{} `json:"settings"`
|
||||
}
|
||||
|
||||
func (c UserUploadsOld) TableName() string {
|
||||
// is this needed here?
|
||||
return config.UploadsOldTableName
|
||||
|
@ -115,3 +121,26 @@ func (u *User) ToJSON() UserJSON {
|
|||
}
|
||||
return json
|
||||
}
|
||||
|
||||
/* User Settings */
|
||||
|
||||
func(s *UserSettings) Get(key string) interface{} {
|
||||
return s.settings[key]
|
||||
}
|
||||
|
||||
func (s *UserSettings) Set(key string, val interface{}) {
|
||||
s.settings[key] = val
|
||||
}
|
||||
|
||||
func (s *UserSettings) GetSettings() {
|
||||
return s.settings
|
||||
}
|
||||
|
||||
func (u *User) SaveSettings() {
|
||||
u.UserSettings , _ = json.Marshal(u.Settings.GetSettings())
|
||||
}
|
||||
|
||||
func (u *User) ParseSettings() {
|
||||
if len(u.Settings.GetSettings()) == 0
|
||||
json.Unmarshal([]byte(u.UserSettings), u.Settings)
|
||||
}
|
|
@ -79,14 +79,16 @@ func UploadPostHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
url, err := Router.Get("view_torrent").URL("id", strconv.FormatUint(uint64(torrent.ID), 10))
|
||||
|
||||
if (user.ID > 0) { // If we are a member
|
||||
if (user.ID > 0 && config.EnableNotifications["new_torrent"]) { // If we are a member and notifications for new torrents are enabled
|
||||
userService.GetLikings(user) // We populate the liked field for users
|
||||
if len(user.Likings) > 0 { // If we are followed by at least someone
|
||||
for _, follower := range user.Likings {
|
||||
T, _, _ := languages.TfuncAndLanguageWithFallback(user.Language, user.Language) // We need to send the notification to every user in their language
|
||||
follower.ParseSettings() // We need to call it before checking settings
|
||||
if follower.Settings.Get("notifications.new_torrent"] {
|
||||
T, _, _ := languages.TfuncAndLanguageWithFallback(user.Language, user.Language) // We need to send the notification to every user in their language
|
||||
|
||||
notifierService.NotifyUser(&follower, torrent.Identifier(), fmt.Sprintf(T("new_torrent_uploaded"), torrent.Name, user.Username), url.String())
|
||||
|
||||
notifierService.NotifyUser(&follower, torrent.Identifier(), fmt.Sprintf(T("new_torrent_uploaded"), torrent.Name, user.Username), url.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,40 +137,42 @@ func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
|
|||
NotFoundHandler(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
messages := msg.GetMessages(r)
|
||||
userForm := form.UserForm{}
|
||||
err := form.NewErrors()
|
||||
infos := form.NewInfos()
|
||||
userSettingsForm := form.UserSettingsForm{}
|
||||
|
||||
|
||||
T := languages.GetTfuncFromRequest(r)
|
||||
if len(r.PostFormValue("email")) > 0 {
|
||||
_, err = form.EmailValidation(r.PostFormValue("email"), err)
|
||||
form.EmailValidation(r.PostFormValue("email"), &messages)
|
||||
}
|
||||
if len(r.PostFormValue("username")) > 0 {
|
||||
_, err = form.ValidateUsername(r.PostFormValue("username"), err)
|
||||
form.ValidateUsername(r.PostFormValue("username"), &messages)
|
||||
}
|
||||
|
||||
if len(err) == 0 {
|
||||
if !messages.HasErrors() {
|
||||
modelHelper.BindValueForm(&userForm, r)
|
||||
modelHelper.BindValueForm(&userSettingsForm, r)
|
||||
if !userPermission.HasAdmin(currentUser) {
|
||||
userForm.Username = userProfile.Username
|
||||
userForm.Status = userProfile.Status
|
||||
} else {
|
||||
if userProfile.Status != userForm.Status && userForm.Status == 2 {
|
||||
err["errors"] = append(err["errors"], "Elevating status to moderator is prohibited")
|
||||
messages.AddError("errors", "Elevating status to moderator is prohibited")
|
||||
}
|
||||
}
|
||||
err = modelHelper.ValidateForm(&userForm, err)
|
||||
if len(err) == 0 {
|
||||
modelHelper.ValidateForm(&userForm, &messages)
|
||||
if !messages.HasErrors() {
|
||||
if userForm.Email != userProfile.Email {
|
||||
userService.SendVerificationToUser(*currentUser, userForm.Email)
|
||||
infos["infos"] = append(infos["infos"], fmt.Sprintf(string(T("email_changed")), userForm.Email))
|
||||
messages.AddInfof("infos", string(T("email_changed")), userForm.Email)
|
||||
userForm.Email = userProfile.Email // reset, it will be set when user clicks verification
|
||||
}
|
||||
userProfile, _, errorUser = userService.UpdateUser(w, &userForm, currentUser, id)
|
||||
if errorUser != nil {
|
||||
err["errors"] = append(err["errors"], errorUser.Error())
|
||||
messages.ImportFromError("errors", errorUser)
|
||||
} else {
|
||||
infos["infos"] = append(infos["infos"], string(T("profile_updated")))
|
||||
messages.AddInfo("infos", string(T("profile_updated")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,8 +181,8 @@ func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
|
|||
CommonTemplateVariables: NewCommonVariables(r),
|
||||
UserProfile: &userProfile,
|
||||
UserForm: userForm,
|
||||
FormErrors: err,
|
||||
FormInfos: infos,
|
||||
FormErrors: messages.GetAllErrors(),
|
||||
FormInfos: messages.GetAllInfos(),
|
||||
Languages: availableLanguages,
|
||||
}
|
||||
errorTmpl := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", upev)
|
||||
|
|
|
@ -4,33 +4,34 @@ import (
|
|||
"regexp"
|
||||
|
||||
"github.com/NyaaPantsu/nyaa/util/log"
|
||||
msg "github.com/NyaaPantsu/nyaa/util/messages"
|
||||
)
|
||||
|
||||
const EMAIL_REGEX = `(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})`
|
||||
const USERNAME_REGEX = `(\W)`
|
||||
|
||||
func EmailValidation(email string, err map[string][]string) (bool, map[string][]string) {
|
||||
func EmailValidation(email string, mes *msg.Messages) bool {
|
||||
exp, errorRegex := regexp.Compile(EMAIL_REGEX)
|
||||
if regexpCompiled := log.CheckError(errorRegex); regexpCompiled {
|
||||
if exp.MatchString(email) {
|
||||
return true, err
|
||||
return true
|
||||
}
|
||||
}
|
||||
err["email"] = append(err["email"], "Email Address is not valid")
|
||||
return false, err
|
||||
mes.AddError("email", "Email Address is not valid")
|
||||
return false
|
||||
}
|
||||
|
||||
func ValidateUsername(username string, err map[string][]string) (bool, map[string][]string) {
|
||||
func ValidateUsername(username string, mes *msg.Messages) bool {
|
||||
exp, errorRegex := regexp.Compile(USERNAME_REGEX)
|
||||
if regexpCompiled := log.CheckError(errorRegex); regexpCompiled {
|
||||
if exp.MatchString(username) {
|
||||
err["username"] = append(err["username"], "Username contains illegal characters")
|
||||
return false, err
|
||||
mes.AddError("username", "Username contains illegal characters")
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false, err
|
||||
return false
|
||||
}
|
||||
return true, err
|
||||
return true
|
||||
}
|
||||
|
||||
func NewErrors() map[string][]string {
|
||||
|
@ -73,6 +74,20 @@ type UserForm struct {
|
|||
Status int `form:"status" default:"0"`
|
||||
}
|
||||
|
||||
// UserSettingsForm is used when updating a user.
|
||||
type UserSettingsForm struct {
|
||||
NewTorrent bool `form:"new_torrent" default:"true"`
|
||||
NewTorrentEmail bool `form:"new_torrent_email" default:"true"`
|
||||
NewComment bool `form:"new_comment" default:"true"`
|
||||
NewCommentEmail bool `form:"new_comment_email" default:"false"`
|
||||
NewResponses bool `form:"new_responses" default:"true"`
|
||||
NewResponsesEmail bool `form:"new_responses_email" default:"false"`
|
||||
NewFollower bool `form:"new_follower" default:"true"`
|
||||
NewFollowerEmail bool `form:"new_follower_email" default:"true"`
|
||||
Followed bool `form:"followed" default:"false"`
|
||||
FollowedEmail bool `form:"followed_email" default:"false"`
|
||||
}
|
||||
|
||||
// PasswordForm is used when updating a user password.
|
||||
type PasswordForm struct {
|
||||
CurrentPassword string `form:"currentPassword"`
|
||||
|
|
|
@ -3,6 +3,7 @@ package modelHelper
|
|||
import (
|
||||
"fmt"
|
||||
"github.com/NyaaPantsu/nyaa/util/log"
|
||||
msg "github.com/NyaaPantsu/nyaa/util/messages"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
@ -57,7 +58,7 @@ func BindValueForm(form interface{}, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]string {
|
||||
func ValidateForm(form interface{}, mes *msg.Messages) {
|
||||
formElem := reflect.ValueOf(form).Elem()
|
||||
for i := 0; i < formElem.NumField(); i++ {
|
||||
typeField := formElem.Type().Field(i)
|
||||
|
@ -69,28 +70,28 @@ func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]
|
|||
if tag.Get("len_min") != "" && (tag.Get("needed") != "" || formElem.Field(i).Len() > 0) { // Check minimum length
|
||||
lenMin, _ := strconv.Atoi(tag.Get("len_min"))
|
||||
if formElem.Field(i).Len() < lenMin {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Minimal length of %s required for the input: %s", strconv.Itoa(lenMin), inputName))
|
||||
mes.AddErrorf(tag.Get("form"), "Minimal length of %s required for the input: %s", strconv.Itoa(lenMin), inputName)
|
||||
}
|
||||
}
|
||||
if tag.Get("len_max") != "" && (tag.Get("needed") != "" || formElem.Field(i).Len() > 0) { // Check maximum length
|
||||
lenMax, _ := strconv.Atoi(tag.Get("len_max"))
|
||||
if formElem.Field(i).Len() > lenMax {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Maximal length of %s required for the input: %s", strconv.Itoa(lenMax), inputName))
|
||||
mes.AddErrorf(tag.Get("form"), "Maximal length of %s required for the input: %s", strconv.Itoa(lenMax), inputName)
|
||||
}
|
||||
}
|
||||
if tag.Get("equalInput") != "" && (tag.Get("needed") != "" || formElem.Field(i).Len() > 0) {
|
||||
otherInput := formElem.FieldByName(tag.Get("equalInput"))
|
||||
if formElem.Field(i).Interface() != otherInput.Interface() {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Must be same %s", inputName))
|
||||
mes.AddErrorf(tag.Get("form"), "Must be same %s", inputName)
|
||||
}
|
||||
}
|
||||
switch typeField.Type.Name() {
|
||||
case "string":
|
||||
if tag.Get("equal") != "" && formElem.Field(i).String() != tag.Get("equal") {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Wrong value for the input: %s", inputName))
|
||||
mes.AddErrorf(tag.Get("form"), "Wrong value for the input: %s", inputName)
|
||||
}
|
||||
if tag.Get("needed") != "" && formElem.Field(i).String() == "" {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Field needed: %s", inputName))
|
||||
mes.AddErrorf(tag.Get("form"), "Field needed: %s", inputName)
|
||||
}
|
||||
if formElem.Field(i).String() == "" && tag.Get("default") != "" {
|
||||
formElem.Field(i).SetString(tag.Get("default"))
|
||||
|
@ -99,11 +100,11 @@ func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]
|
|||
if tag.Get("equal") != "" { // Check minimum length
|
||||
equal, _ := strconv.Atoi(tag.Get("equal"))
|
||||
if formElem.Field(i).Int() > int64(equal) {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Wrong value for the input: %s", inputName))
|
||||
mes.AddErrorf(tag.Get("form"), "Wrong value for the input: %s", inputName)
|
||||
}
|
||||
}
|
||||
if tag.Get("needed") != "" && formElem.Field(i).Int() == 0 {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Field needed: %s", inputName))
|
||||
mes.AddErrorf(tag.Get("form"), "Field needed: %s", inputName)
|
||||
}
|
||||
if formElem.Field(i).Interface == nil && tag.Get("default") != "" {
|
||||
defaultValue, _ := strconv.Atoi(tag.Get("default"))
|
||||
|
@ -113,11 +114,11 @@ func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]
|
|||
if tag.Get("equal") != "" { // Check minimum length
|
||||
equal, _ := strconv.Atoi(tag.Get("equal"))
|
||||
if formElem.Field(i).Float() != float64(equal) {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Wrong value for the input: %s", inputName))
|
||||
mes.AddErrorf(tag.Get("form"), "Wrong value for the input: %s", inputName)
|
||||
}
|
||||
}
|
||||
if tag.Get("needed") != "" && formElem.Field(i).Float() == 0 {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Field needed: %s", inputName))
|
||||
mes.AddErrorf(tag.Get("form"), "Field needed: %s", inputName)
|
||||
}
|
||||
if formElem.Field(i).Interface == nil && tag.Get("default") != "" {
|
||||
defaultValue, _ := strconv.Atoi(tag.Get("default"))
|
||||
|
@ -127,10 +128,13 @@ func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]
|
|||
if tag.Get("equal") != "" { // Check minimum length
|
||||
equal, _ := strconv.ParseBool(tag.Get("equal"))
|
||||
if formElem.Field(i).Bool() != equal {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Wrong value for the input: %s", inputName))
|
||||
mes.AddErrorf(tag.Get("form"), "Wrong value for the input: %s", inputName)
|
||||
}
|
||||
}
|
||||
if formElem.Field(i).Interface == nil && tag.Get("default") != "" {
|
||||
defaultValue, _ := strconv.ParseBool(tag.Get("default"))
|
||||
formElem.Field(i).SetBool(defaultValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errorForm
|
||||
}
|
||||
|
|
Référencer dans un nouveau ticket