révision
ac92ddfeba
33 fichiers modifiés avec 167 ajouts et 198 suppressions
|
@ -12,9 +12,8 @@ type User struct {
|
|||
Status int `gorm:"column:status"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||
// Currently unused (auth is stateless now)
|
||||
/*Token string `gorm:"column:api_token"`
|
||||
TokenExpiration time.Time `gorm:"column:api_token_expiry"`*/
|
||||
Token string `gorm:"column:api_token"`
|
||||
TokenExpiration time.Time `gorm:"column:api_token_expiry"`
|
||||
Language string `gorm:"column:language"`
|
||||
|
||||
// TODO: move this to PublicUser
|
||||
|
@ -43,7 +42,7 @@ func (u User) Size() (s int) {
|
|||
4*3 + //time.Time
|
||||
3*2 + // arrays
|
||||
// string arrays
|
||||
len(u.Username) + len(u.Password) + len(u.Email) + len(u.MD5) + len(u.Language)
|
||||
len(u.Username) + len(u.Password) + len(u.Email) + len(u.Token) + len(u.MD5) + len(u.Language)
|
||||
s *= 8
|
||||
|
||||
// Ignoring foreign key users. Fuck them.
|
||||
|
|
|
@ -186,6 +186,10 @@ div.container div.blockBody:nth-of-type(2) table tr:first-of-type th:last-of-typ
|
|||
.navbar {
|
||||
border-radius: 0px;
|
||||
}
|
||||
/* the logo needs some space */
|
||||
.navbar-logo {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
:target {
|
||||
background-color: #e5eecc;
|
||||
|
|
BIN
public/img/logo.png
Fichier normal
BIN
public/img/logo.png
Fichier normal
Fichier binaire non affiché.
Après Largeur: | Hauteur: | Taille: 8 Kio |
|
@ -19,10 +19,10 @@ var FuncMap = template.FuncMap{
|
|||
}
|
||||
return "error"
|
||||
},
|
||||
"genRouteWithQuery": func(name string, currentUrl *url.URL, params ...string) template.HTML {
|
||||
"genRouteWithQuery": func(name string, currentUrl *url.URL, params ...string) template.URL {
|
||||
url, err := Router.Get(name).URL(params...)
|
||||
if err == nil {
|
||||
return template.HTML(template.HTMLEscapeString(url.String() + "?" + currentUrl.RawQuery)) // TODO: Review application of character escaping
|
||||
return template.URL(url.String() + "?" + currentUrl.RawQuery)
|
||||
}
|
||||
return "error"
|
||||
},
|
||||
|
@ -69,6 +69,7 @@ var FuncMap = template.FuncMap{
|
|||
"CurrentOrAdmin": userPermission.CurrentOrAdmin,
|
||||
"CurrentUserIdentical": userPermission.CurrentUserIdentical,
|
||||
"HasAdmin": userPermission.HasAdmin,
|
||||
"NeedsCaptcha": userPermission.NeedsCaptcha,
|
||||
"GetRole": userPermission.GetRole,
|
||||
"IsFollower": userPermission.IsFollower,
|
||||
"NoEncode": func(str string) template.HTML {
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
"github.com/ewhal/nyaa/common"
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/service/captcha"
|
||||
"github.com/ewhal/nyaa/service/user"
|
||||
userForms "github.com/ewhal/nyaa/service/user/form"
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -36,7 +35,7 @@ type NotFoundTemplateVariables struct {
|
|||
|
||||
type ViewTemplateVariables struct {
|
||||
Torrent model.TorrentJSON
|
||||
Captcha captcha.Captcha
|
||||
CaptchaID string
|
||||
Search SearchForm
|
||||
Navigation Navigation
|
||||
User *model.User
|
||||
|
|
|
@ -17,7 +17,6 @@ import (
|
|||
|
||||
"github.com/ewhal/nyaa/cache"
|
||||
"github.com/ewhal/nyaa/config"
|
||||
"github.com/ewhal/nyaa/service/captcha"
|
||||
"github.com/ewhal/nyaa/service/upload"
|
||||
"github.com/ewhal/nyaa/util"
|
||||
"github.com/ewhal/nyaa/util/metainfo"
|
||||
|
@ -33,7 +32,7 @@ type UploadForm struct {
|
|||
Remake bool
|
||||
Description string
|
||||
Status int
|
||||
captcha.Captcha
|
||||
CaptchaID string
|
||||
|
||||
Infohash string
|
||||
CategoryID int
|
||||
|
@ -84,12 +83,6 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
|
|||
f.Status, _ = strconv.Atoi(r.FormValue(UploadFormStatus))
|
||||
f.Magnet = r.FormValue(UploadFormMagnet)
|
||||
f.Remake = r.FormValue(UploadFormRemake) == "on"
|
||||
f.Captcha = captcha.Extract(r)
|
||||
|
||||
if !captcha.Authenticate(f.Captcha) {
|
||||
// TODO: Prettier passing of mistyped Captcha errors
|
||||
return errors.New(captcha.ErrInvalidCaptcha.Error())
|
||||
}
|
||||
|
||||
// trim whitespace
|
||||
f.Name = util.TrimWhitespaces(f.Name)
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/ewhal/nyaa/db"
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/service/captcha"
|
||||
"github.com/ewhal/nyaa/service/user"
|
||||
"github.com/ewhal/nyaa/service/user/permission"
|
||||
"github.com/ewhal/nyaa/util/languages"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
@ -23,26 +23,32 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
var uploadForm UploadForm
|
||||
if r.Method == "POST" {
|
||||
defer r.Body.Close()
|
||||
user := GetUser(r)
|
||||
if userPermission.NeedsCaptcha(user) {
|
||||
userCaptcha := captcha.Extract(r)
|
||||
if !captcha.Authenticate(userCaptcha) {
|
||||
http.Error(w, captcha.ErrInvalidCaptcha.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// validation is done in ExtractInfo()
|
||||
err := uploadForm.ExtractInfo(r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
user, _, err := userService.RetrieveCurrentUser(r)
|
||||
if err != nil {
|
||||
fmt.Printf("error %+v\n", err)
|
||||
}
|
||||
status := 1 // normal
|
||||
if uploadForm.Remake { // overrides trusted
|
||||
status = 2
|
||||
} else if user.Status == 1 {
|
||||
status = 3 // mark as trusted if user is trusted
|
||||
}
|
||||
var sameTorrents int
|
||||
db.ORM.Model(&model.Torrent{}).Where("torrent_hash = ?", uploadForm.Infohash).Count(&sameTorrents)
|
||||
if (sameTorrents == 0) {
|
||||
//add to db and redirect depending on result
|
||||
|
||||
var sameTorrents int
|
||||
db.ORM.Model(&model.Torrent{}).Where("torrent_hash = ?", uploadForm.Infohash).Count(&sameTorrents)
|
||||
if (sameTorrents == 0) {
|
||||
// add to db and redirect
|
||||
torrent := model.Torrent{
|
||||
Name: uploadForm.Name,
|
||||
Category: uploadForm.CategoryID,
|
||||
|
@ -54,7 +60,6 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Description: uploadForm.Description,
|
||||
UploaderID: user.ID}
|
||||
db.ORM.Create(&torrent)
|
||||
fmt.Printf("%+v\n", torrent)
|
||||
url, err := Router.Get("view_torrent").URL("id", strconv.FormatUint(uint64(torrent.ID), 10))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
@ -66,7 +71,14 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
} else if r.Method == "GET" {
|
||||
uploadForm.CaptchaID = captcha.GetID()
|
||||
user := GetUser(r)
|
||||
if userPermission.NeedsCaptcha(user) {
|
||||
uploadForm.CaptchaID = captcha.GetID()
|
||||
} else {
|
||||
uploadForm.CaptchaID = ""
|
||||
}
|
||||
|
||||
|
||||
htv := UploadTemplateVariables{uploadForm, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
|
||||
languages.SetTranslationFromRequest(uploadTemplate, r, "en-us")
|
||||
err := uploadTemplate.ExecuteTemplate(w, "index.html", htv)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/service/captcha"
|
||||
"github.com/ewhal/nyaa/service/torrent"
|
||||
"github.com/ewhal/nyaa/service/user/permission"
|
||||
"github.com/ewhal/nyaa/util"
|
||||
"github.com/ewhal/nyaa/util/languages"
|
||||
"github.com/ewhal/nyaa/util/log"
|
||||
|
@ -26,7 +27,12 @@ func ViewHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
b := torrent.ToJSON()
|
||||
htv := ViewTemplateVariables{b, captcha.Captcha{CaptchaID: captcha.GetID()}, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
|
||||
captchaID := ""
|
||||
user := GetUser(r)
|
||||
if userPermission.NeedsCaptcha(user) {
|
||||
captchaID = captcha.GetID()
|
||||
}
|
||||
htv := ViewTemplateVariables{b, captchaID, NewSearchForm(), Navigation{}, user, r.URL, mux.CurrentRoute(r)}
|
||||
|
||||
languages.SetTranslationFromRequest(viewTemplate, r, "en-us")
|
||||
err = viewTemplate.ExecuteTemplate(w, "index.html", htv)
|
||||
|
@ -39,12 +45,14 @@ func PostCommentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
userCaptcha := captcha.Extract(r)
|
||||
if !captcha.Authenticate(userCaptcha) {
|
||||
http.Error(w, "bad captcha", 403)
|
||||
return
|
||||
}
|
||||
currentUser := GetUser(r)
|
||||
if userPermission.NeedsCaptcha(currentUser) {
|
||||
userCaptcha := captcha.Extract(r)
|
||||
if !captcha.Authenticate(userCaptcha) {
|
||||
http.Error(w, "bad captcha", 403)
|
||||
return
|
||||
}
|
||||
}
|
||||
content := p.Sanitize(r.FormValue("comment"))
|
||||
|
||||
if strings.TrimSpace(content) == "" {
|
||||
|
@ -75,12 +83,14 @@ func ReportTorrentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
userCaptcha := captcha.Extract(r)
|
||||
if !captcha.Authenticate(userCaptcha) {
|
||||
http.Error(w, "bad captcha", 403)
|
||||
return
|
||||
}
|
||||
currentUser := GetUser(r)
|
||||
if userPermission.NeedsCaptcha(currentUser) {
|
||||
userCaptcha := captcha.Extract(r)
|
||||
if !captcha.Authenticate(userCaptcha) {
|
||||
http.Error(w, "bad captcha", 403)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
idNum, err := strconv.Atoi(id)
|
||||
userID := currentUser.ID
|
||||
|
|
|
@ -5,98 +5,96 @@ import (
|
|||
"github.com/ewhal/nyaa/db"
|
||||
"github.com/ewhal/nyaa/model"
|
||||
formStruct "github.com/ewhal/nyaa/service/user/form"
|
||||
"github.com/ewhal/nyaa/util/log"
|
||||
"github.com/ewhal/nyaa/util/modelHelper"
|
||||
"github.com/ewhal/nyaa/util/timeHelper"
|
||||
"github.com/gorilla/securecookie"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const CookieName = "session"
|
||||
|
||||
// If you want to keep login cookies between restarts you need to make these permanent
|
||||
var cookieHandler = securecookie.New(
|
||||
securecookie.GenerateRandomKey(64),
|
||||
securecookie.GenerateRandomKey(32))
|
||||
|
||||
// Encoding & Decoding of the cookie value
|
||||
func DecodeCookie(cookie_value string) (uint, error) {
|
||||
value := make(map[string]string)
|
||||
err := cookieHandler.Decode(CookieName, cookie_value, &value)
|
||||
func Token(r *http.Request) (string, error) {
|
||||
var token string
|
||||
cookie, err := r.Cookie("session")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return token, err
|
||||
}
|
||||
time_int, _ := strconv.ParseInt(value["t"], 10, 0)
|
||||
if timeHelper.IsExpired(time.Unix(time_int, 0)) {
|
||||
return 0, errors.New("Cookie is expired")
|
||||
cookieValue := make(map[string]string)
|
||||
err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
|
||||
if err != nil {
|
||||
return token, err
|
||||
}
|
||||
ret, err := strconv.ParseUint(value["u"], 10, 0)
|
||||
return uint(ret), err
|
||||
token = cookieValue["token"]
|
||||
if len(token) == 0 {
|
||||
return token, errors.New("token is empty")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func EncodeCookie(user_id uint) (string, error) {
|
||||
validUntil := timeHelper.FewDaysLater(7) // 1 week
|
||||
// SetCookie sets a cookie.
|
||||
func SetCookie(w http.ResponseWriter, token string) (int, error) {
|
||||
value := map[string]string{
|
||||
"u": strconv.FormatUint(uint64(user_id), 10),
|
||||
"t": strconv.FormatInt(validUntil.Unix(), 10),
|
||||
"token": token,
|
||||
}
|
||||
return cookieHandler.Encode(CookieName, value)
|
||||
encoded, err := cookieHandler.Encode("session", value)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
cookie := &http.Cookie{
|
||||
Name: "session",
|
||||
Value: encoded,
|
||||
Path: "/",
|
||||
}
|
||||
http.SetCookie(w, cookie)
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
// ClearCookie clears a cookie.
|
||||
func ClearCookie(w http.ResponseWriter) (int, error) {
|
||||
cookie := &http.Cookie{
|
||||
Name: CookieName,
|
||||
Name: "session",
|
||||
Value: "",
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
MaxAge: -1,
|
||||
}
|
||||
http.SetCookie(w, cookie)
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
// SetCookieHandler sets the authentication cookie
|
||||
// SetCookieHandler sets a cookie with email and password.
|
||||
func SetCookieHandler(w http.ResponseWriter, email string, pass string) (int, error) {
|
||||
if email == "" || pass == "" {
|
||||
return http.StatusNotFound, errors.New("No username/password entered")
|
||||
}
|
||||
|
||||
var user model.User
|
||||
// search by email or username
|
||||
isValidEmail, _ := formStruct.EmailValidation(email, formStruct.NewErrors())
|
||||
if isValidEmail {
|
||||
if db.ORM.Where("email = ?", email).First(&user).RecordNotFound() {
|
||||
return http.StatusNotFound, errors.New("User not found")
|
||||
if email != "" && pass != "" {
|
||||
var user model.User
|
||||
isValidEmail, _ := formStruct.EmailValidation(email, formStruct.NewErrors())
|
||||
if isValidEmail {
|
||||
log.Debug("User entered valid email.")
|
||||
if db.ORM.Where("email = ?", email).First(&user).RecordNotFound() {
|
||||
return http.StatusNotFound, errors.New("User not found")
|
||||
}
|
||||
} else {
|
||||
log.Debug("User entered username.")
|
||||
if db.ORM.Where("username = ?", email).First(&user).RecordNotFound() {
|
||||
return http.StatusNotFound, errors.New("User not found")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if db.ORM.Where("username = ?", email).First(&user).RecordNotFound() {
|
||||
return http.StatusNotFound, errors.New("User not found")
|
||||
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass))
|
||||
if err != nil {
|
||||
return http.StatusUnauthorized, errors.New("Password incorrect")
|
||||
}
|
||||
if user.Status == -1 {
|
||||
return http.StatusUnauthorized, errors.New("Account banned")
|
||||
}
|
||||
status, err := SetCookie(w, user.Token)
|
||||
if err != nil {
|
||||
return status, err
|
||||
}
|
||||
w.Header().Set("X-Auth-Token", user.Token)
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass))
|
||||
if err != nil {
|
||||
return http.StatusUnauthorized, errors.New("Password incorrect")
|
||||
}
|
||||
if user.Status == -1 {
|
||||
return http.StatusUnauthorized, errors.New("Account banned")
|
||||
}
|
||||
|
||||
encoded, err := EncodeCookie(user.ID)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
cookie := &http.Cookie{
|
||||
Name: CookieName,
|
||||
Value: encoded,
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
}
|
||||
http.SetCookie(w, cookie)
|
||||
// also set response header for convenience
|
||||
w.Header().Set("X-Auth-Token", encoded)
|
||||
return http.StatusOK, nil
|
||||
return http.StatusNotFound, errors.New("user not found")
|
||||
}
|
||||
|
||||
// RegisterHanderFromForm sets cookie from a RegistrationForm.
|
||||
|
@ -113,31 +111,24 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) (int, error) {
|
|||
return RegisterHanderFromForm(w, registrationForm)
|
||||
}
|
||||
|
||||
// CurrentUser determines the current user from the request
|
||||
// CurrentUser get a current user.
|
||||
func CurrentUser(r *http.Request) (model.User, error) {
|
||||
var user model.User
|
||||
var encoded string
|
||||
|
||||
encoded = r.Header.Get("X-Auth-Token")
|
||||
if len(encoded) == 0 {
|
||||
// check cookie instead
|
||||
cookie, err := r.Cookie(CookieName)
|
||||
var token string
|
||||
var err error
|
||||
token = r.Header.Get("X-Auth-Token")
|
||||
if len(token) > 0 {
|
||||
log.Debug("header token exists")
|
||||
} else {
|
||||
token, err = Token(r)
|
||||
log.Debug("header token does not exist")
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
encoded = cookie.Value
|
||||
}
|
||||
user_id, err := DecodeCookie(encoded)
|
||||
if err != nil {
|
||||
return user, err
|
||||
if db.ORM.Where("api_token = ?", token).First(&user).RecordNotFound() {
|
||||
return user, errors.New("user not found")
|
||||
}
|
||||
if db.ORM.Where("user_id = ?", user_id).First(&user).RecordNotFound() {
|
||||
return user, errors.New("User not found")
|
||||
}
|
||||
|
||||
if user.Status == -1 {
|
||||
// recheck as user might've been banned in the meantime
|
||||
return user, errors.New("Account banned")
|
||||
}
|
||||
return user, nil
|
||||
err = db.ORM.Model(&user).Error
|
||||
return user, err
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/ewhal/nyaa/util/log"
|
||||
)
|
||||
|
||||
|
||||
// HasAdmin checks that user has an admin permission.
|
||||
func HasAdmin(user *model.User) bool {
|
||||
return user.Status == 2
|
||||
|
@ -18,11 +19,16 @@ func CurrentOrAdmin(user *model.User, userID uint) bool {
|
|||
}
|
||||
|
||||
// CurrentUserIdentical check that userID is same as current user's ID.
|
||||
// TODO: Inline this
|
||||
// TODO: Inline this (won't go do this for us?)
|
||||
func CurrentUserIdentical(user *model.User, userID uint) bool {
|
||||
return user.ID == userID
|
||||
}
|
||||
|
||||
func NeedsCaptcha(user *model.User) bool {
|
||||
// Trusted members & Moderators don't
|
||||
return !(user.Status == 1 || user.Status == 2)
|
||||
}
|
||||
|
||||
func GetRole(user *model.User) string {
|
||||
switch user.Status {
|
||||
case -1:
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ewhal/nyaa/config"
|
||||
"github.com/ewhal/nyaa/db"
|
||||
"github.com/ewhal/nyaa/model"
|
||||
formStruct "github.com/ewhal/nyaa/service/user/form"
|
||||
|
@ -14,6 +15,7 @@ import (
|
|||
"github.com/ewhal/nyaa/util/crypto"
|
||||
"github.com/ewhal/nyaa/util/log"
|
||||
"github.com/ewhal/nyaa/util/modelHelper"
|
||||
"github.com/ewhal/nyaa/util/timeHelper"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
|
@ -67,8 +69,15 @@ func CreateUserFromForm(registrationForm formStruct.RegistrationForm) (model.Use
|
|||
return user, err
|
||||
}
|
||||
}
|
||||
token, err := crypto.GenerateRandomToken32()
|
||||
if err != nil {
|
||||
return user, errors.New("token not generated")
|
||||
}
|
||||
user.Email = "" // unset email because it will be verified later
|
||||
|
||||
user.Token = token
|
||||
user.TokenExpiration = timeHelper.FewDaysLater(config.AuthTokenExpirationDay)
|
||||
log.Debugf("user %+v\n", user)
|
||||
if db.ORM.Create(&user).Error != nil {
|
||||
return user, errors.New("user not created")
|
||||
}
|
||||
|
@ -148,13 +157,17 @@ func UpdateUserCore(user *model.User) (int, error) {
|
|||
}
|
||||
}
|
||||
|
||||
user.UpdatedAt = time.Now()
|
||||
err := db.ORM.Save(user).Error
|
||||
token, err := crypto.GenerateRandomToken32()
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
user.Token = token
|
||||
user.TokenExpiration = timeHelper.FewDaysLater(config.AuthTokenExpirationDay)
|
||||
if db.ORM.Save(user).Error != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
|
||||
user.UpdatedAt = time.Now()
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
|
@ -184,13 +197,18 @@ func UpdateUser(w http.ResponseWriter, form *formStruct.UserForm, currentUser *m
|
|||
form.Username = user.Username
|
||||
}
|
||||
if (form.Email != user.Email) {
|
||||
// send verification to new email and keep old
|
||||
SendVerificationToUser(user, form.Email)
|
||||
form.Email = user.Email
|
||||
}
|
||||
log.Debugf("form %+v\n", form)
|
||||
modelHelper.AssignValue(&user, form)
|
||||
status, err := UpdateUserCore(&user)
|
||||
if err != nil {
|
||||
return user, status, err
|
||||
}
|
||||
if userPermission.CurrentUserIdentical(currentUser, user.ID) {
|
||||
status, err = SetCookie(w, user.Token)
|
||||
}
|
||||
return user, status, err
|
||||
}
|
||||
|
||||
|
|
|
@ -63,8 +63,5 @@ http://tracker.baka-sub.cf/announce</pre>
|
|||
<br />
|
||||
|
||||
<img style="max-width: 100%" src="https://my.mixtape.moe/omrskw.png" alt="funny meme">
|
||||
|
||||
<br />
|
||||
<h2>{{T "nyaa_pantsu_dont_host_files"}}</h2>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
{{define "captcha"}}
|
||||
{{/* unset if user doesn't need captcha */}}
|
||||
{{if ne .CaptchaID ""}}
|
||||
<div class="form-group captcha-container">
|
||||
<label for="solution">Captcha</label>
|
||||
<input type="text" name="captchaID" value="{{.CaptchaID}}" hidden>
|
||||
<img src="/captcha/{{.CaptchaID}}.png">
|
||||
<input type="text" name="solution" class="form-control" placeholder="Captcha" autocomplete="off" required>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<th class="col-xs-1">Action</th>
|
||||
</tr>
|
||||
|
||||
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">{{ .Torrent.Name }}</a></td><td>{{.User.Username}}</td><td>{{.Description}}</td>
|
||||
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">{{ .Torrent.Name }}</a></td><td>{{.UserID}}</td><td>{{.Description}}</td>
|
||||
<td><a href="{{ genRoute "mod_trdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
|
||||
{{end}}
|
||||
</table>
|
||||
|
@ -70,7 +70,7 @@
|
|||
|
||||
{{ range .Comments}}
|
||||
|
||||
<tr><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .Content }}</a></td><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .User.Username }}</a></td>
|
||||
<tr><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .Content }}</a></td><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{.UserID}}</a></td>
|
||||
<td><a href="{{ genRoute "mod_cdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
|
||||
{{end}}
|
||||
</table>
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="{{.URL.Parse "/"}}">Nyaa Pantsu</a>
|
||||
<a href="{{.URL.Parse "/"}}"><img src="/img/logo.png" class ="navbar-logo" alt="Nyaa Pantsu"></a>
|
||||
<a class="navbar-brand hidden-md pull-right nightswitch" href="javascript:toggleNightMode();" ></a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
|
|
|
@ -23,11 +23,11 @@
|
|||
<p class="text-error">{{ . }}</p>
|
||||
{{end}}
|
||||
</div>
|
||||
<span class="button-checkbox">
|
||||
<!-- <button type="button" class="btn hidden" data-color="info">{{ T "remember_me"}}</button>
|
||||
<input type="checkbox" name="remember_me" id="remember_me" checked="checked"> -->
|
||||
<!-- <span class="button-checkbox">
|
||||
<button type="button" class="btn hidden" data-color="info">{{ T "remember_me"}}</button>
|
||||
<input type="checkbox" name="remember_me" id="remember_me" checked="checked">
|
||||
<a href="" class="btn btn-link pull-right">{{ T "forgot_password"}}</a>
|
||||
</span>
|
||||
</span> -->
|
||||
<hr class="colorgraph">
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-sm-6 col-md-6">
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
<label for="comment">{{ if gt .User.ID 0}} {{T "submit_a_comment_as_username" .User.Username}} {{else}} {{T "submit_a_comment_as_anonymous"}} {{end}}</label>
|
||||
<textarea name="comment" class="form-control" rows="5"></textarea>
|
||||
</div>
|
||||
{{with .Captcha}} {{block "captcha" .}}{{end}} {{end}}
|
||||
{{block "captcha" .}}{{end}}
|
||||
<button type="submit" class="btn btn-success">{{T " submit "}}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -137,7 +137,7 @@
|
|||
<input type="radio" name="report_type" value="illegal"> Illegal content <br/>
|
||||
<input type="radio" name="report_type" value="spam"> Spam / garbage
|
||||
{{end}}
|
||||
{{with .Captcha}} {{block "captcha" .}}{{end}} {{end}}
|
||||
{{block "captcha" .}}{{end}}
|
||||
<button type="submit" class="btn btn-default">Report!</button>
|
||||
</form> <br />
|
||||
</div>
|
||||
|
|
|
@ -319,10 +319,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "És el llenguatge de programació preferit de l'autor."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat i sukebei.pantsu.cat no allotgen cap fitxer."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Carrega un enllaç magnètic"
|
||||
|
|
|
@ -331,10 +331,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "Weil es die Lieblingsprogrammiersprache des Entwicklers ist."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat und sukebei.pantsu.cat stellen keine Dateien bereit."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Magnet hochladen"
|
||||
|
|
|
@ -331,10 +331,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "It's the author's favorite programming language."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat and sukebei.pantsu.cat do not host any files."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Upload magnet"
|
||||
|
|
|
@ -315,10 +315,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "Es el lenguaje de programación favorito del autor."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat y sukebei.pantsu.cat no hospedan archivos."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Subir Magnet"
|
||||
|
|
|
@ -331,10 +331,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "C'est le langage de programmation favori du développeur."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat and sukebei.pantsu.cat n'hébergent aucun fichier."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Uploader un magnet"
|
||||
|
|
|
@ -319,10 +319,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "Ez a dev kedvenc programozási nyelve."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat és sukebei.pantsu.cat nem hosztol fájlokat."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Magnet Feltöltése"
|
||||
|
|
|
@ -331,10 +331,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "È il linguaggio di programmazione preferito dell'autore."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat e sukebei.pantsu.cat non contengono/hostano nessun file al loro interno."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Carica magnet"
|
||||
|
|
|
@ -319,10 +319,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "作者の一番好きなプログラミング言語だからです。"
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat と sukebei.pantsu.cat はいかなるファイルもホストしていません。"
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "magnet リンクのアップロード"
|
||||
|
|
|
@ -319,10 +319,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "제일 좋아하는 언어입니다. >__- 찡긋"
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat 와 sukebei.pantsu.cat 는 그 어떤 파일도 호스팅하고 있지 않습니다"
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "마그넷 업로드"
|
||||
|
|
|
@ -331,10 +331,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "Favorittspråket til karen som dro sulamitten i gang."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat og sukebei.pantsu.cat har ingen av disse filene på sine servere, og deler ingen filer fra serverene ellers."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Last opp magnet"
|
||||
|
|
|
@ -319,10 +319,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "Het is de favoriete programmeertaal van de bedenker."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat en sukebei.pantsu.cat hosten geen bestanden."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Magnet uploaden"
|
||||
|
|
|
@ -331,10 +331,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "É a linguagem de programação favorita do autor."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat e sukebei.pantsu.cat não hospedam nenhum arquivo."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Enviar link magnético"
|
||||
|
|
|
@ -331,10 +331,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "Это любимый язык программирования автора."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat и sukebei.pantsu.cat не содержат файлы."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Загрузить магнит"
|
||||
|
|
|
@ -331,10 +331,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "Det är skaparens favoritspråk."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat och sukebei.pantsu.cat tillhandahåller inga filer på sina servrar."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Ladda upp magnet"
|
||||
|
|
|
@ -331,10 +331,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "เพราะเป็นภาษาถนัดของผู้พัฒนา"
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat และ sukebei.pantsu.cat จะไม่เก็บไฟล์ใดๆ ไว้ในระบบทั้งสิ้น"
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "อัพโหลด Magnet"
|
||||
|
|
|
@ -331,10 +331,6 @@
|
|||
"id": "authors_favorite_language",
|
||||
"translation": "這是作者的愛啊啊啊啊"
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat 以及 sukebei.pantsu.cat 沒有存放任何檔案喔∼"
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "上傳磁力連結"
|
||||
|
|
Référencer dans un nouveau ticket