diff --git a/controllers/activities/activities.go b/controllers/activities/activities.go index 972dc2df..56d75cff 100644 --- a/controllers/activities/activities.go +++ b/controllers/activities/activities.go @@ -32,7 +32,7 @@ func ActivityListHandler(c *gin.Context) { } var conditions []string var values []interface{} - if userid != "" && currentUser.HasAdmin() { + if userid != "" && currentUser.IsModerator() { conditions = append(conditions, "user_id = ?") values = append(values, userid) } diff --git a/controllers/api/api.go b/controllers/api/api.go index fbd9ea8a..b4466b5b 100644 --- a/controllers/api/api.go +++ b/controllers/api/api.go @@ -433,7 +433,7 @@ func APISearchHandler(c *gin.Context) { userID = 0 } - _, torrentSearch, nbTorrents, err := search.AuthorizedQuery(c, pagenum, currentUser.CurrentOrAdmin(uint(userID))) + _, torrentSearch, nbTorrents, err := search.AuthorizedQuery(c, pagenum, currentUser.CurrentOrJanitor(uint(userID)), currentUser.CurrentOrJanitor(uint(userID))) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) diff --git a/controllers/feed/helpers.go b/controllers/feed/helpers.go index 76a7dffa..7a49e013 100644 --- a/controllers/feed/helpers.go +++ b/controllers/feed/helpers.go @@ -86,7 +86,7 @@ func getTorrentList(c *gin.Context) (torrents []models.Torrent, createdAsTime ti user = 0 } - _, torrents, _, err = search.AuthorizedQuery(c, pagenum, currentUser.CurrentOrAdmin(uint(user))) + _, torrents, _, err = search.AuthorizedQuery(c, pagenum, currentUser.CurrentOrJanitor(uint(user)), currentUser.CurrentOrJanitor(uint(user))) return } diff --git a/controllers/middlewares/middlewares.go b/controllers/middlewares/middlewares.go index a0264408..7fd59722 100644 --- a/controllers/middlewares/middlewares.go +++ b/controllers/middlewares/middlewares.go @@ -39,7 +39,7 @@ func ErrorMiddleware() gin.HandlerFunc { func ModMiddleware() gin.HandlerFunc { return func(c *gin.Context) { currentUser := router.GetUser(c) - if !currentUser.HasAdmin() { + if !currentUser.IsJanitor() { NotFoundHandler(c) } c.Next() diff --git a/controllers/moderator/comments.go b/controllers/moderator/comments.go index 26952c8e..38d02e73 100644 --- a/controllers/moderator/comments.go +++ b/controllers/moderator/comments.go @@ -61,10 +61,19 @@ func CommentsListPanel(c *gin.Context) { // CommentDeleteModPanel : Controller for deleting a comment func CommentDeleteModPanel(c *gin.Context) { - id, _ := strconv.ParseInt(c.PostForm("id"), 10, 32) + id, err := strconv.ParseInt(c.PostForm("id"), 10, 32) + if err != nil { + c.Redirect(http.StatusSeeOther, "/mod/comments") + return + } + comment, _, err := comments.Delete(uint(id)) if err == nil { - activities.Log(&models.User{}, comment.Identifier(), "delete", "comment_deleted_by", strconv.Itoa(int(comment.ID)), comment.User.Username, router.GetUser(c).Username) + username := "れんちょん" + if comment.UserID != 0 { + username = comment.User.Username + } + activities.Log(&models.User{}, comment.Identifier(), "delete", "comment_deleted_by", strconv.Itoa(int(comment.ID)), username, router.GetUser(c).Username) } c.Redirect(http.StatusSeeOther, "/mod/comments?deleted") diff --git a/controllers/moderator/helpers.go b/controllers/moderator/helpers.go index ded87655..97de3f3a 100644 --- a/controllers/moderator/helpers.go +++ b/controllers/moderator/helpers.go @@ -53,7 +53,7 @@ func torrentManyAction(c *gin.Context) { messages.AddErrorTf("errors", "no_status_exist", status) status = -1 } - if !currentUser.HasAdmin() { + if !currentUser.IsModerator() { if c.PostForm("status") != "" { // Condition to check if a user try to change torrent status without having the right permission if (status == models.TorrentStatusTrusted && !currentUser.IsTrusted()) || status == models.TorrentStatusAPlus || status == 0 { status = models.TorrentStatusNormal @@ -64,7 +64,7 @@ func torrentManyAction(c *gin.Context) { } withReport = false // Users should not be able to remove reports } - if c.PostForm("owner") != "" && currentUser.HasAdmin() { // We check that the user given exist and if not we return an error + if c.PostForm("owner") != "" && currentUser.IsModerator() { // We check that the user given exist and if not we return an error _, _, errorUser := users.FindForAdmin(uint(owner)) if errorUser != nil { messages.AddErrorTf("errors", "no_user_found_id", owner) diff --git a/controllers/moderator/index.go b/controllers/moderator/index.go index 8783910c..8542f313 100644 --- a/controllers/moderator/index.go +++ b/controllers/moderator/index.go @@ -13,10 +13,14 @@ import ( // IndexModPanel : Controller for showing index page of Mod Panel func IndexModPanel(c *gin.Context) { offset := 10 - torrents, _, _ := torrents.FindAllOrderBy("torrent_id DESC", offset, 0) + torrents, _, _ := torrents.FindAllForAdminsOrderBy("torrent_id DESC", offset, 0) users, _ := users.FindUsersForAdmin(offset, 0) comments, _ := comments.FindAll(offset, 0, "", "") torrentReports, _, _ := reports.GetAll(offset, 0) templates.PanelAdmin(c, torrents, models.TorrentReportsToJSON(torrentReports), users, comments) } + +func GuidelinesModPanel(c *gin.Context) { + templates.Static(c, "admin/guidelines.jet.html") +} diff --git a/controllers/moderator/router.go b/controllers/moderator/router.go index 6fd0e94e..612d30b4 100644 --- a/controllers/moderator/router.go +++ b/controllers/moderator/router.go @@ -46,8 +46,11 @@ func init() { modRoutes.GET("/torrent", TorrentEditModPanel) modRoutes.POST("/torrent", TorrentPostEditModPanel) - /* Torrent delete routes */ + /* Torrent delete routs */ modRoutes.POST("/torrent/delete", TorrentDeleteModPanel) + + /* Guidelines route */ + modRoutes.Any("/guidelines", GuidelinesModPanel) /* Announcement edit view */ modRoutes.GET("/announcement/form", addAnnouncement) diff --git a/controllers/moderator/torrents.go b/controllers/moderator/torrents.go index b594f5e9..e3d5cb10 100644 --- a/controllers/moderator/torrents.go +++ b/controllers/moderator/torrents.go @@ -120,38 +120,43 @@ func TorrentPostEditModPanel(c *gin.Context) { // TorrentDeleteModPanel : Controller for deleting a torrent func TorrentDeleteModPanel(c *gin.Context) { - id, _ := strconv.ParseInt(c.PostForm("id"), 10, 32) - definitely := c.Request.URL.Query()["definitely"] - var returnRoute = "/mod/torrents" - torrent, errFind := torrents.FindByID(uint(id)) - if errFind == nil { - var err error - if definitely != nil { - _, _, err = torrent.DefinitelyDelete() - returnRoute = "/mod/torrents/deleted" - } else { - _, _, err = torrent.Delete(false) - } - - //delete reports of torrent - query := &search.Query{} - query.Append("torrent_id", id) - reports, _, _ := reports.FindOrderBy(query, "", 0, 0) - for _, report := range reports { - report.Delete() - } - - if err == nil { - if torrent.Uploader == nil { - torrent.Uploader = &models.User{} + currentUser := router.GetUser(c) + + if currentUser.IsModerator() { + id, _ := strconv.ParseInt(c.PostForm("id"), 10, 32) + definitely := c.Request.URL.Query()["definitely"] + + torrent, errFind := torrents.FindByID(uint(id)) + if errFind == nil { + var err error + if definitely != nil { + _, _, err = torrent.DefinitelyDelete() + returnRoute = "/mod/torrents/deleted" + } else { + _, _, err = torrent.Delete(false) + } + + //delete reports of torrent + query := &search.Query{} + query.Append("torrent_id", id) + reports, _, _ := reports.FindOrderBy(query, "", 0, 0) + for _, report := range reports { + report.Delete() + } + + if err == nil { + if torrent.Uploader == nil { + torrent.Uploader = &models.User{} + } + _, username := torrents.HideUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) + activities.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, currentUser.Username) } - _, username := torrents.HideUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) - activities.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, router.GetUser(c).Username) } + c.Redirect(http.StatusSeeOther, returnRoute+"?deleted") } - c.Redirect(http.StatusSeeOther, returnRoute+"?deleted") + c.Redirect(http.StatusSeeOther, returnRoute) } // DeleteTagsModPanel : Controller for deleting all torrent tags diff --git a/controllers/search/search.go b/controllers/search/search.go index e2ef86a5..8fa9c506 100644 --- a/controllers/search/search.go +++ b/controllers/search/search.go @@ -76,7 +76,7 @@ func SearchHandler(c *gin.Context) { return } - searchParam, torrents, nbTorrents, err := search.AuthorizedQuery(c, pagenum, currentUser.CurrentOrAdmin(uint(userID))) + searchParam, torrents, nbTorrents, err := search.AuthorizedQuery(c, pagenum, currentUser.CurrentOrJanitor(uint(userID)), currentUser.IsJanitor()) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return diff --git a/controllers/torrent/comment.go b/controllers/torrent/comment.go index 350f2c6d..4924edd1 100644 --- a/controllers/torrent/comment.go +++ b/controllers/torrent/comment.go @@ -10,8 +10,10 @@ import ( "github.com/NyaaPantsu/nyaa/models/comments" "github.com/NyaaPantsu/nyaa/models/torrents" "github.com/NyaaPantsu/nyaa/utils/captcha" + "github.com/NyaaPantsu/nyaa/utils/filelist" msg "github.com/NyaaPantsu/nyaa/utils/messages" "github.com/NyaaPantsu/nyaa/utils/sanitize" + "github.com/NyaaPantsu/nyaa/templates" "github.com/gin-gonic/gin" ) @@ -34,6 +36,9 @@ func PostCommentHandler(c *gin.Context) { messages.AddErrorT("errors", "bad_captcha") } } + if currentUser.IsBanned() { + messages.AddErrorT("errors", "account_banned") + } content := sanitize.Sanitize(c.PostForm("comment"), "comment") userID := currentUser.ID @@ -54,6 +59,13 @@ func PostCommentHandler(c *gin.Context) { messages.Error(err) } } - url := "/view/" + strconv.FormatUint(uint64(torrent.ID), 10) - c.Redirect(302, url) + + captchaID := "" + //Generate a captcha + if currentUser.NeedsCaptcha() { + captchaID = captcha.GetID() + } + + folder := filelist.FileListToFolder(torrent.FileList, "root") + templates.Torrent(c, torrent.ToJSON(), folder, captchaID) } diff --git a/controllers/torrent/delete.go b/controllers/torrent/delete.go index 5f853fa1..a45b5269 100644 --- a/controllers/torrent/delete.go +++ b/controllers/torrent/delete.go @@ -25,7 +25,7 @@ func TorrentDeleteUserPanel(c *gin.Context) { torrent.Uploader = &models.User{} } _, username := torrents.HideUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) - if currentUser.HasAdmin() { // We hide username on log activity if user is not admin and torrent is hidden + if currentUser.IsModerator() { // We hide username on log activity if user is not admin and torrent is hidden activities.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, currentUser.Username) } else { activities.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, username) diff --git a/controllers/torrent/edit.go b/controllers/torrent/edit.go index 5c387995..2f282ec4 100644 --- a/controllers/torrent/edit.go +++ b/controllers/torrent/edit.go @@ -52,7 +52,7 @@ func TorrentPostEditUserPanel(c *gin.Context) { messages.AddErrorT("errors", "fail_torrent_update") } if !messages.HasErrors() { - upload.UpdateTorrent(&uploadForm, torrent, currentUser).Update(currentUser.HasAdmin()) + upload.UpdateTorrent(&uploadForm, torrent, currentUser).Update(currentUser.IsModerator()) c.Redirect(http.StatusSeeOther, fmt.Sprintf("/view/%d?success_edit", id)) return } diff --git a/controllers/torrent/view.go b/controllers/torrent/view.go index e32cbed2..123a0b73 100644 --- a/controllers/torrent/view.go +++ b/controllers/torrent/view.go @@ -20,23 +20,6 @@ func ViewHandler(c *gin.Context) { messages := msg.GetMessages(c) user := router.GetUser(c) - // Display success message on upload - if c.Request.URL.Query()["success"] != nil { - messages.AddInfoT("infos", "torrent_uploaded") - } - // Display success message on edit - if c.Request.URL.Query()["success_edit"] != nil { - messages.AddInfoT("infos", "torrent_updated") - } - // Display wrong captcha error message - if c.Request.URL.Query()["badcaptcha"] != nil { - messages.AddErrorT("errors", "bad_captcha") - } - // Display reported successful message - if c.Request.URL.Query()["reported"] != nil { - messages.AddInfoTf("infos", "report_msg", id) - } - // Retrieve the torrent torrent, err := torrents.FindByID(uint(id)) @@ -66,6 +49,27 @@ func ViewHandler(c *gin.Context) { captchaID = captcha.GetID() } + // Display success message on upload + if c.Request.URL.Query()["success"] != nil { + if torrent.IsBlocked() { + messages.AddInfoT("infos", "torrent_uploaded_locked") + } else { + messages.AddInfoT("infos", "torrent_uploaded") + } + } + // Display success message on edit + if c.Request.URL.Query()["success_edit"] != nil { + messages.AddInfoT("infos", "torrent_updated") + } + // Display wrong captcha error message + if c.Request.URL.Query()["badcaptcha"] != nil { + messages.AddErrorT("errors", "bad_captcha") + } + // Display reported successful message + if c.Request.URL.Query()["reported"] != nil { + messages.AddInfoTf("infos", "report_msg", id) + } + if c.Request.URL.Query()["followed"] != nil { messages.AddInfoTf("infos", "user_followed_msg", b.UploaderName) } diff --git a/controllers/upload/upload.go b/controllers/upload/upload.go index 67171037..ece7310b 100644 --- a/controllers/upload/upload.go +++ b/controllers/upload/upload.go @@ -58,6 +58,8 @@ func UploadPostHandler(c *gin.Context) { uploadForm.Status = models.TorrentStatusRemake } else if user.IsTrusted() { uploadForm.Status = models.TorrentStatusTrusted + } else if user.IsBanned() { + uploadForm.Status = models.TorrentStatusBlocked } err = torrents.ExistOrDelete(uploadForm.Infohash, user) diff --git a/controllers/user/profile.go b/controllers/user/profile.go index 634ac395..9e9f157f 100644 --- a/controllers/user/profile.go +++ b/controllers/user/profile.go @@ -7,9 +7,12 @@ import ( "net/http" + "github.com/NyaaPantsu/nyaa/controllers/router" "github.com/NyaaPantsu/nyaa/models/notifications" + "github.com/NyaaPantsu/nyaa/models/activities" "github.com/NyaaPantsu/nyaa/models/users" + "github.com/NyaaPantsu/nyaa/models" "github.com/NyaaPantsu/nyaa/templates" "github.com/NyaaPantsu/nyaa/utils/cookies" "github.com/NyaaPantsu/nyaa/utils/crypto" @@ -33,8 +36,36 @@ func UserProfileDelete(c *gin.Context) { if err == nil && currentUser.CurrentUserIdentical(userProfile.ID) { cookies.Clear(c) } - } templates.Static(c, "site/static/delete_success.jet.html") + } + } else { + c.AbortWithStatus(http.StatusNotFound) + } +} + +// UserProfileBan : Ban an User +func UserProfileBan(c *gin.Context) { + currentUser := router.GetUser(c) + + if currentUser.IsJanitor() { + id, _ := strconv.ParseUint(c.Param("id"), 10, 32) + + userProfile, _, errorUser := users.FindForAdmin(uint(id)) + if errorUser == nil && !userProfile.IsModerator() { + action := "user_unbanned_by" + message := "?unbanned" + if userProfile.ToggleBan() { + action = "user_banned_by" + message = "?banned" + } + + activities.Log(&models.User{}, fmt.Sprintf("user_%d", id), "edit", action, userProfile.Username, strconv.Itoa(int(id)), currentUser.Username) + c.Redirect(http.StatusSeeOther, fmt.Sprintf("/user/%d/%s", id, c.Param("username") + message)) + } else { + c.AbortWithStatus(http.StatusNotFound) + } + } else { + c.AbortWithStatus(http.StatusNotFound) } } @@ -150,7 +181,7 @@ func UserDetailsHandler(c *gin.Context) { } } -// UserProfileFormHandler : Getting View User Profile Update +// UserProfileFormHandler : Updating User Profile func UserProfileFormHandler(c *gin.Context) { id, _ := strconv.ParseUint(c.Param("id"), 10, 32) currentUser := router.GetUser(c) @@ -178,21 +209,21 @@ func UserProfileFormHandler(c *gin.Context) { if !messages.HasErrors() { c.Bind(&userForm) c.Bind(&userSettingsForm) - if !currentUser.HasAdmin() { + if !currentUser.IsModerator() { userForm.Username = userProfile.Username userForm.Status = userProfile.Status } else { - if userProfile.Status != userForm.Status && userForm.Status == 2 { + if userProfile.Status != userForm.Status && (userForm.Status == 2){ messages.AddErrorT("errors", "elevating_user_error") } } validator.ValidateForm(&userForm, messages) if !messages.HasErrors() { if userForm.Email != userProfile.Email { - if currentUser.HasAdmin() { - userProfile.Email = userForm.Email + if currentUser.IsModerator() { + userProfile.Email = userForm.Email // reset, it will be set when user clicks verification } else { - email.SendVerificationToUser(currentUser, userForm.Email) + email.SendVerificationToUser(userProfile, userForm.Email) messages.AddInfoTf("infos", "email_changed", userForm.Email) userForm.Email = userProfile.Email // reset, it will be set when user clicks verification } @@ -201,10 +232,7 @@ func UserProfileFormHandler(c *gin.Context) { if err != nil { messages.Error(err) } - if userForm.Email != user.Email { - // send verification to new email and keep old - email.SendVerificationToUser(user, userForm.Email) - } + if !messages.HasErrors() { messages.AddInfoT("infos", "profile_updated") userProfile = user diff --git a/controllers/user/router.go b/controllers/user/router.go index 33e62ef7..7bc9f4c4 100644 --- a/controllers/user/router.go +++ b/controllers/user/router.go @@ -38,6 +38,7 @@ func init() { userRoutes.GET("/:id/:username/feed", feedController.RSSHandler) userRoutes.GET("/:id/:username/feed/:page", feedController.RSSHandler) userRoutes.POST("/:id/:username/delete", UserProfileDelete) + userRoutes.POST("/:id/:username/ban", UserProfileBan) } router.Get().Any("/username", RedirectToUserSearch) diff --git a/models/torrent.go b/models/torrent.go index d4696dc5..bfb0d2b7 100644 --- a/models/torrent.go +++ b/models/torrent.go @@ -313,17 +313,7 @@ func (t *Torrent) ToJSON() TorrentJSON { } for _, c := range t.Comments { if c.User != nil { - userStatus := "" - if c.User.IsBanned() { - userStatus = "userstatus_banned" - } - if c.User.HasAdmin() { - userStatus = "userstatus_moderator" - } - if c.User.ID == t.ID { - userStatus = "userstatus_uploader" - } - commentsJSON = append(commentsJSON, CommentJSON{Username: c.User.Username, UserID: int(c.User.ID), UserStatus: userStatus, Content: sanitize.MarkdownToHTML(c.Content), Date: c.CreatedAt.UTC(), UserAvatar: c.User.MD5}) + commentsJSON = append(commentsJSON, CommentJSON{Username: c.User.Username, UserID: int(c.User.ID), UserStatus: c.User.GetRole(), Content: sanitize.MarkdownToHTML(c.Content), Date: c.CreatedAt.UTC(), UserAvatar: c.User.MD5}) } else { commentsJSON = append(commentsJSON, CommentJSON{}) } diff --git a/models/torrents/find.go b/models/torrents/find.go index 5c9d114c..d56af241 100644 --- a/models/torrents/find.go +++ b/models/torrents/find.go @@ -154,7 +154,7 @@ func findOrderBy(parameters Query, orderBy string, limit int, offset int, countA dbQuery = dbQuery.Preload("Uploader") } if countAll { - dbQuery = dbQuery.Preload("Comments") + dbQuery = dbQuery.Preload("Comments").Preload("OldComments") } if conditions != "" { @@ -191,6 +191,11 @@ func FindAllOrderBy(orderBy string, limit int, offset int) ([]models.Torrent, in return FindOrderBy(nil, orderBy, limit, offset) } +// FindAllForAdminsOrderBy : Get all torrents ordered by parameters +func FindAllForAdminsOrderBy(orderBy string, limit int, offset int) ([]models.Torrent, int, error) { + return findOrderBy(nil, orderBy, limit, offset, true, true, false) +} + // FindAll : Get all torrents without order func FindAll(limit int, offset int) ([]models.Torrent, int, error) { return FindOrderBy(nil, "", limit, offset) diff --git a/models/user.go b/models/user.go index cb9f49cc..5016d931 100644 --- a/models/user.go +++ b/models/user.go @@ -29,6 +29,8 @@ const ( UserStatusModerator = 2 // UserStatusScraped : Int for User status scrapped UserStatusScraped = 3 + // UserStatusModerator : Int for User status moderator + UserStatusJanitor = 4 ) // User model @@ -135,6 +137,11 @@ func (u *User) IsModerator() bool { return u.Status == UserStatusModerator } +// IsJanitor : Return true if user is janitor OR moderator +func (u *User) IsJanitor() bool { + return u.Status == UserStatusJanitor || u.Status == UserStatusModerator +} + // IsScraped : Return true if user is a scrapped user func (u *User) IsScraped() bool { return u.Status == UserStatusScraped @@ -152,11 +159,18 @@ func (u *User) GetUnreadNotifications() int { return u.UnreadNotifications } -// HasAdmin checks that user has an admin permission. Deprecated -func (u *User) HasAdmin() bool { - return u.IsModerator() +// ToggleBan : Ban/Unban an user an user, return true if the user is now banned +func (u *User) ToggleBan() bool { + if u.IsBanned() { + u.Status = UserStatusMember + } else { + u.Status = UserStatusBanned + } + u.Update() + return u.IsBanned() } + // CurrentOrAdmin check that user has admin permission or user is the current user. func (u *User) CurrentOrAdmin(userID uint) bool { if userID == 0 && !u.IsModerator() { @@ -165,6 +179,14 @@ func (u *User) CurrentOrAdmin(userID uint) bool { log.Debugf("user.ID == userID %d %d %s", u.ID, userID, u.ID == userID) return (u.IsModerator() || u.ID == userID) } +// CurrentOrJanitor check that user has janitor permission or user is the current user. +func (u *User) CurrentOrJanitor(userID uint) bool { + if userID == 0 && !u.IsJanitor() { + return false + } + log.Debugf("user.ID == userID %d %d %s", u.ID, userID, u.ID == userID) + return (u.IsJanitor() || u.ID == userID) +} // CurrentUserIdentical check that userID is same as current user's ID. // TODO: Inline this (won't go do this for us?) @@ -175,7 +197,7 @@ func (u *User) CurrentUserIdentical(userID uint) bool { // NeedsCaptcha : Check if a user needs captcha func (u *User) NeedsCaptcha() bool { // Trusted members & Moderators don't - return !(u.IsTrusted() || u.IsModerator()) + return !(u.IsTrusted() || u.IsJanitor()) } // CanUpload : Check if a user can upload or if upload is enabled in config @@ -194,6 +216,9 @@ func (u *User) CanUpload() bool { // GetRole : Get the status/role of a user func (u *User) GetRole() string { + if u.ID == 0 { + return "" + } switch u.Status { case UserStatusBanned: return "userstatus_banned" @@ -203,6 +228,8 @@ func (u *User) GetRole() string { return "userstatus_scraped" case UserStatusTrusted: return "userstatus_trusted" + case UserStatusJanitor: + return "userstatus_janitor" case UserStatusModerator: return "userstatus_moderator" } diff --git a/models/users/helpers.go b/models/users/helpers.go index fdb4a0e0..321467ea 100644 --- a/models/users/helpers.go +++ b/models/users/helpers.go @@ -44,10 +44,7 @@ func Exists(email string, pass string) (user *models.User, status int, err error status, err = http.StatusUnauthorized, errors.New("incorrect_password") return } - if userExist.IsBanned() { - status, err = http.StatusUnauthorized, errors.New("account_banned") - return - } + if userExist.IsScraped() { status, err = http.StatusUnauthorized, errors.New("account_need_activation") return diff --git a/public/css/main.css b/public/css/main.css index be0eaac3..da2eebd8 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -664,7 +664,7 @@ th { width: auto; } .website-nav table tr { - background: none!important; + background: none!important; } .website-nav #nav-category-list { width: 70%; @@ -675,17 +675,17 @@ th { } .sub-category-list { padding-left: 16px; - font-size: 12px; - margin-bottom: 9px; + font-size: 12px; + margin-bottom: 9px; } .sub-category-list span{ - display: block; + display: block; } .sub-category-list span:before { - content: '-» '; + content: '-» '; } .sub-category-list span:first-child:before { - content: ''; + content: ''; } textarea { max-width: 100%; @@ -899,8 +899,8 @@ html, body { width: 100% !important; margin-bottom: 15px; } - .profile-panel .user-search { - max-width: none; + .profile-usermenu, .profile-panel .user-search { + width: 200px!important; } .header .h-user { width: 46px; @@ -939,6 +939,9 @@ html, body { .upload-form-table .table-input-label { width: 25%!important; } + .notification-status { + width: 100px; + } } @media (max-height: 750px),(max-width: 500px) { @@ -1030,12 +1033,28 @@ html, body { .upload-form-table .table-input-label { display: none; } - .torrent-view-data { - display: table!important; - } - .torrent-view-data td, .torrent-view-data table { - width: 100%!important; - } + .torrent-view-data { + display: table!important; + } + .torrent-view-data td, .torrent-view-data table { + width: 100%!important; + } + .notification-status { + width: 41px!important; + font-size: 0; + } + #clear-notification a { + display: block; + margin-left: 0px!important; + float: none!important; + margin-top: 2px; + } + .notification-table { + margin-bottom: 108px!important; + } + .notification-date { + width: 100px!important; + } } @media (max-width: 440px) { @@ -1062,7 +1081,7 @@ html, body { } .profile-sidebar { - display: inline-block; + display: block; } .profile-usertitle { @@ -1087,11 +1106,14 @@ html, body { border-radius: 6px; } -.profile-usermenu { - min-width: 170px; +.profile-usermenu, .profile-panel .user-search { + width: 170px; + margin: 0 auto; + max-width: calc(100% - 16px); } -.profile-usermenu a { +.profile-usermenu a, .profile-usermenu button { display: block; + width: 100%; margin-bottom: 11px; } .profile-usermenu .icon-rss-squared { @@ -1237,7 +1259,7 @@ html, body { } .comment-content :first-child { - margin-top: 8px; + margin-top: 8px; } .comment-content :last-child { margin-bottom: 2px; @@ -1251,7 +1273,32 @@ html, body { } .comment-form textarea { - margin-bottom: 0; + margin-bottom: 0; +} + +.CodeMirror { + cursor: text; +} +.CodeMirror-fullscreen, .editor-toolbar.fullscreen { + max-width: none!important; +} +.editor-toolbar.fullscreen { + padding: 8px 6px!important; +} +.comment-form .CodeMirror, .comment-form .CodeMirror-scroll { + min-height: 122px; +} +.comment-text, .markdown-container { + position: relative; +} +.comment-form .editor-toolbar { + padding: 0 6px; +} +.comment-form .editor-toolbar:before { + margin-bottom: 4px; +} +.comment-form .editor-toolbar:after { + margin-top: 4px; } .comment-form h3 { @@ -1792,7 +1839,6 @@ input.filelist-checkbox:checked+table.table-filelist { } [class^="btn-"] { - font-weight: bold; color: white; } @@ -1851,7 +1897,7 @@ summary:after { width: 12px; font-size: 1.5em; font-weight: bold; - outline: none; + outline: none; } details[open] summary:after { @@ -2083,40 +2129,48 @@ p.upload-rules a { .upload-form-table .table-torrent-link input[type="text"] { width: 62%; } -.upload-form-table .editor-statusbar { - position: relative; - margin-top: -27px; - margin-bottom: 2px; +.upload-form-table #desc { + margin-bottom: 20px; +} +.comment-text .editor-statusbar { + right: 9px; + bottom: 20px; +} +.editor-statusbar { + position: absolute; + padding: 0; + right: 8px; z-index: 1; + bottom: 6px; } .upload-form-table details { - margin-bottom: 4px; + margin-bottom: 4px; } #anidex-upload-info > div { - height: auto; - padding: 10px 6px; - border-top: none; + height: auto; + padding: 10px 6px; + border-top: none; } #anidex-upload-info p { - margin-bottom: 4px; + margin-bottom: 4px; } #anidex-upload-info p:first-child { - margin-top: 0; + margin-top: 0; } #anidex-upload-info > div > div { - margin-left: 9px; + margin-left: 9px; } #anidex-upload-info > div input[type="text"], #anidex-upload-info > div select { - margin-bottom: 4px; - + margin-bottom: 4px; + } #anidex-upload-info > div select { - width: calc(100% - 8px); + width: calc(100% - 8px); } #anidex-upload-info > div select[name="anidex_form_category"] option{ - color: #000; + color: #000; text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); font-size: 14px; font-weight: bold; @@ -2306,7 +2360,6 @@ table.multiple-upload { } .profile-panel .user-search { padding: 0; - max-width: 170px; } .user-search [type="text"] { @@ -2390,49 +2443,49 @@ form.delete-form button.form-input.btn-red { bottom: 8px; right: 8px; position: absolute; - width: calc(100% - 16px); + width: calc(100% - 16px); } #clear-notification a { - float: right; - margin-left: 3px; + float: right; + margin-left: 3px; } .notification-table { margin-bottom: 44px; } .notification-table td { - text-align: center; - padding: 6px 0; - border-bottom: 1px solid; + text-align: center; + padding: 6px 0; + border-bottom: 1px solid; } .notification-table tr:hover td { - filter: brightness(1.2); + filter: brightness(1.2); } .notification-status { - width: 140px; + width: 140px; } .notification-event { - text-align: left!important; - padding: 6px 10px!important; + text-align: left!important; + padding: 6px 10px!important; } .notification-date { - width: 195px; + width: 195px; } td.notification-status { - border: 1px solid black; + border: 1px solid black; } td.notification-status { - background-color: #e4e4e4; + background-color: #e4e4e4; } td.notification-status.notification-unread { - background-color: rgb(161, 211, 253); - color: white; + background-color: rgb(161, 211, 253); + color: white; } .torrent-report-table td, .torrent-report-table th { - width: 195px; + width: 195px; } .td-report-message { - width: auto!important; + width: auto!important; white-space: normal; word-break: break-word; } diff --git a/public/css/themes/classic.css b/public/css/themes/classic.css index fb98ef0a..4e693089 100644 --- a/public/css/themes/classic.css +++ b/public/css/themes/classic.css @@ -279,6 +279,9 @@ select.form-input { .CodeMirror { padding: 0; } +.comment-form .CodeMirror, .comment-form .CodeMirror-scroll { + min-height: 70px; +} .upload-form-table .checkbox-container { height: 22px; @@ -689,33 +692,58 @@ span.tag { } .upload-form-table .table-torrent-link input { width: 100%!important; -} -.upload-form-table .editor-toolbar { +} +.editor-toolbar, .CodeMirror { + max-width: 459px; +} +.editor-toolbar.fullscreen { + top: 40px; +} +.CodeMirror-fullscreen { + top: 90px; +} +.markdown-container .editor-statusbar { + bottom: 6px; + left: 141px; +} +.editor-statusbar { + bottom: 19px; + left: 209px; + right: unset!important; +} +.editor-toolbar { padding: 0 2px; font-size: 10px; } -.upload-form-table .editor-toolbar, .upload-form-table .CodeMirror { +.editor-toolbar, .CodeMirror { border-radius: 0; border-color: #c4c4c4; opacity: 1; } -.upload-form-table .editor-toolbar::before { +.editor-toolbar::before { margin-bottom: 2px; } -.upload-form-table .editor-toolbar::after { +.editor-toolbar::after { margin-top: 4px; } -.upload-form-table .editor-toolbar a { +.editor-toolbar a { width: 26px; height: 24px; } -.upload-form-table .editor-toolbar a:before { +.comment-form .editor-toolbar a { + width: 24px; + height: 20px; +} +.editor-toolbar a:before { line-height: 25px; } +.comment-form .editor-toolbar a:before { + line-height: 18px; +} .upload-form-table .CodeMirror-scroll, .upload-form-table .CodeMirror { min-height: 120px; } -.upload-form-table .CodeMirror-scroll { +.CodeMirror-scroll { max-width: unset!important; } diff --git a/templates/admin/guidelines.jet.html b/templates/admin/guidelines.jet.html new file mode 100644 index 00000000..68804848 --- /dev/null +++ b/templates/admin/guidelines.jet.html @@ -0,0 +1,8 @@ +{{ extends "layouts/index_admin" }} +{{block title()}}{{ T("moderation_guidelines") }}{{end}} +{{ block content_body()}} +
+

{{ T("moderation_guidelines") }}

+ +
+{{end}} diff --git a/templates/admin/index.jet.html b/templates/admin/index.jet.html index 00cde9b2..61390077 100644 --- a/templates/admin/index.jet.html +++ b/templates/admin/index.jet.html @@ -14,19 +14,26 @@ {{range Torrents}} - + {{ .Name }} {{ T("edit") }} - {{ .UploaderID }} + {{ if .Uploader }}{{.Uploader.Username }}{{else}}れんちょん{{end}} -
- - -
+ {{ if User.IsModerator() }} +
+ + {{ if .IsDeleted() }}{{ end }} + +
+ {{end}} +
+ + +
{{end}} @@ -88,10 +95,16 @@ {{ .Username }} {{if .ID > 0}} -
- {{ yield csrf_field()}} - -
+ {{ if User.IsModerator() }} +
+ {{ yield csrf_field()}} + +
+ {{end}} +
+ {{ yield csrf_field()}} + +
{{end}} diff --git a/templates/admin/torrentlist.jet.html b/templates/admin/torrentlist.jet.html index 5d12f5d8..82b23e83 100644 --- a/templates/admin/torrentlist.jet.html +++ b/templates/admin/torrentlist.jet.html @@ -38,12 +38,12 @@ {{ range Models}} - + - {{ .Name }} {{ if !.IsDeleted }} + {{ .Name }} {{ if User.IsModerator() }} {{ T("edit")}} {{end}} @@ -57,14 +57,15 @@
- +
+ {{ if User.IsModerator() }}
- {{ if .IsDeleted }}{{ end }} - + {{ if .IsDeleted() }}{{ end }} +
- + {{end}} {{end}} diff --git a/templates/admin/userlist.jet.html b/templates/admin/userlist.jet.html index a7600796..511a7152 100644 --- a/templates/admin/userlist.jet.html +++ b/templates/admin/userlist.jet.html @@ -13,7 +13,7 @@ {{ range Models}} - + {{ .Username }} @@ -21,7 +21,10 @@ {{if .ID > 0}}
{{ yield csrf_field()}} + {{ if User.IsModerator() }} + {{end}} +
{{end}} diff --git a/templates/errors/torrent_file_missing.jet.html b/templates/errors/torrent_file_missing.jet.html index 1eb8727a..c0d5f0e4 100644 --- a/templates/errors/torrent_file_missing.jet.html +++ b/templates/errors/torrent_file_missing.jet.html @@ -7,7 +7,7 @@
{{ T("magnet_link")}}
- {{ if !isset(Errors[name])}}

{{ T("pending_torrent") }}

{{end}}

+ {{ if !isset(Errors["errors"])}}

{{ T("pending_torrent") }}

{{end}}

{{ end }} No torrent file diff --git a/templates/layouts/partials/helpers/badgemenu.jet.html b/templates/layouts/partials/helpers/badgemenu.jet.html index 17a77187..ec5ec00e 100644 --- a/templates/layouts/partials/helpers/badgemenu.jet.html +++ b/templates/layouts/partials/helpers/badgemenu.jet.html @@ -1,18 +1,18 @@ {{ import "csrf" }} {{block badge_user()}} -
+
{{if User.ID > 0 }}
- {{ T("profile")}} + {{ T("profile")}}{{ if User.IsBanned() }} ({{T("banned")}}){{end}} {{ T("my_notifications")}} {{if User.GetUnreadNotifications() > 0}}({{ User.GetUnreadNotifications() }}){{end}} @@ -20,7 +20,7 @@ {{ T("settings")}} - {{if User.HasAdmin()}} + {{if User.IsJanitor()}} {{ T("moderation")}} {{end}}
diff --git a/templates/layouts/partials/helpers/oldNav.jet.html b/templates/layouts/partials/helpers/oldNav.jet.html index 270e5748..3790c884 100644 --- a/templates/layouts/partials/helpers/oldNav.jet.html +++ b/templates/layouts/partials/helpers/oldNav.jet.html @@ -13,7 +13,6 @@ {{ if _% 3 != 2}}{{ if _% 3 == 0}}{{end}}{{end}} {{end}} {{ end }} - {{ if Search.Category != ""}} diff --git a/templates/layouts/partials/menu/admin.jet.html b/templates/layouts/partials/menu/admin.jet.html index 2b1f7a78..1e993ce2 100644 --- a/templates/layouts/partials/menu/admin.jet.html +++ b/templates/layouts/partials/menu/admin.jet.html @@ -7,4 +7,5 @@ {{ T("announcements")}} {{ T("torrent_reports")}} {{ T("torrent_reassign")}} + {{ T("guidelines")}}
diff --git a/templates/layouts/partials/menu/profile.jet.html b/templates/layouts/partials/menu/profile.jet.html index ee9fa8c9..cfd6d7a5 100644 --- a/templates/layouts/partials/menu/profile.jet.html +++ b/templates/layouts/partials/menu/profile.jet.html @@ -1,5 +1,6 @@ +{{ import "layouts/partials/helpers/csrf" }} {{ block profile_menu(route="profile") }} -
+
{{ UserProfile.Username }} @@ -10,9 +11,11 @@

{{ UserProfile.Username}}

+ {{ if UserProfile.GetRole() != "" }}

{{T(UserProfile.GetRole())}}

+ {{end}}

{{T("followers")}}: {{len(UserProfile.Followers)}}
{{ T("torrents_uploaded") }}:{{ NbTorrents[0] }}
{{T("size")}}: {{fileSize(NbTorrents[1], T, false)}}

@@ -43,12 +46,19 @@ {{ if User.CurrentUserIdentical(UserProfile.ID) }} {{ T("my_notifications")}} {{end}} - {{if UserProfile.ID > 0 && User.CurrentOrAdmin(UserProfile.ID) }} - - {{ T("settings")}} - - {{end}} + {{if UserProfile.ID > 0}} + {{ if User.CurrentOrAdmin(UserProfile.ID) }} + + {{ T("settings")}} + + {{else if User.IsJanitor() && !UserProfile.IsJanitor() }} + + {{ yield csrf_field()}} + + + {{end}} {{end}} + {{end}}
{{ if User.ID != UserProfile.ID }} -{{ if User.HasAdmin() }} +{{ if User.IsModerator() }}
@@ -175,7 +175,7 @@ -{{ if User.HasAdmin() }} +{{ if User.IsModerator() }} + + {{ if !torrentFileExists(Torrent.Hash, Torrent.TorrentLink)}} var torrentLink = document.getElementById("torrent-download-link"), oldDownloadHtml = torrentLink.innerHTML, diff --git a/templates/site/user/edit.jet.html b/templates/site/user/edit.jet.html index a8f18e00..df59334c 100644 --- a/templates/site/user/edit.jet.html +++ b/templates/site/user/edit.jet.html @@ -33,7 +33,7 @@ - {{ if !User.HasAdmin()}} + {{ if !User.IsModerator()}} @@ -51,7 +51,7 @@ {{ yield errors(name="Email")}} {{ yield errors(name="Language")}} - {{ if !User.HasAdmin()}} + {{ if !User.IsModerator()}} {{ yield errors(name="CurrentPassword")}} {{end}} {{ yield errors(name="Password")}} @@ -228,7 +228,7 @@ {{ yield errors(name="FollowedEmail")}} {{end}} -{{ if User.HasAdmin()}} +{{ if User.IsModerator()}}

{{ T("moderation")}}

@@ -243,8 +243,9 @@ - {{ if UserProfile.Status == 2}} - + {{ if User.Status == 2}} + + {{end}} diff --git a/templates/template.go b/templates/template.go index 3c5b73d3..6813ef81 100644 --- a/templates/template.go +++ b/templates/template.go @@ -176,10 +176,10 @@ func userProfileBase(c *gin.Context, templateName string, userProfile *models.Us query.Set("limit", "15") c.Request.URL.RawQuery = query.Encode() nbTorrents := 0 - if userProfile.ID > 0 && currentUser.CurrentOrAdmin(userProfile.ID) { - _, userProfile.Torrents, nbTorrents, _ = search.ByQuery(c, 1, true, false, false) + if userProfile.ID > 0 && currentUser.CurrentOrJanitor(userProfile.ID) { + _, userProfile.Torrents, nbTorrents, _ = search.ByQuery(c, 1, true, false, false, true) } else { - _, userProfile.Torrents, nbTorrents, _ = search.ByQuery(c, 1, true, false, true) + _, userProfile.Torrents, nbTorrents, _ = search.ByQuery(c, 1, true, false, true, false) } var uploadedSize int64 diff --git a/templates/template_functions.go b/templates/template_functions.go index a7bdc0dc..7816e86a 100644 --- a/templates/template_functions.go +++ b/templates/template_functions.go @@ -40,6 +40,7 @@ func templateFunctions(vars jet.VarMap) jet.VarMap { vars.Set("GetCategories", categories.GetSelect) vars.Set("GetCategory", getCategory) vars.Set("CategoryName", categoryName) + vars.Set("GetCategoryName", GetCategoryName) vars.Set("GetTorrentLanguages", torrentLanguages.GetTorrentLanguages) vars.Set("LanguageName", languageName) vars.Set("LanguageNameFromCode", languageNameFromCode) @@ -224,13 +225,16 @@ func getCategory(category string, keepParent bool) categories.Categories { return categoryRet } func categoryName(category string, subCategory string) string { - s := category + "_" + subCategory + return GetCategoryName( category + "_" + subCategory) +} - if category, ok := categories.GetByID(s); ok { - return category.Name +func GetCategoryName(category string) string { + if cat, ok := categories.GetByID(category); ok { + return cat.Name } return "" } + func languageName(lang publicSettings.Language, T publicSettings.TemplateTfunc) string { if strings.Contains(lang.Name, ",") { langs := strings.Split(lang.Name, ", ") diff --git a/templates/template_functions_test.go b/templates/template_functions_test.go index 26c32dc9..551987f6 100644 --- a/templates/template_functions_test.go +++ b/templates/template_functions_test.go @@ -289,6 +289,37 @@ func TestCategoryName(t *testing.T) { } } +func TestCategoryName2(t *testing.T) { + var tests = []struct { + TestCat string + Expected string + }{ + { + TestCat: "_", + Expected: "", + }, + { + TestCat: "d", + Expected: "", + }, + { + TestCat: "3_", + Expected: "anime", + }, + { + TestCat: "3_6", + Expected: "anime_raw", + }, + } + + for _, test := range tests { + value := GetCategoryName(test.TestCat) + if value != test.Expected { + t.Errorf("Unexpected value from the function categoryName, got '%s', wanted '%s' for '%s'", value, test.Expected, test.TestCat) + } + } +} + func TestLanguageName(t *testing.T) { var tests = []struct { TestLang publicSettings.Language diff --git a/translations/CHANGELOG.md b/translations/CHANGELOG.md index 26bb6aae..72b2cbc4 100644 --- a/translations/CHANGELOG.md +++ b/translations/CHANGELOG.md @@ -95,3 +95,15 @@ * + loading_file_list * + torrent_filelist * + back_to_torrent +## 2017/11/09 +* + userstatus_janitor +* + ban +* + unban +* + user_banned_by +* + user_unbanned_by +## 2017/11/10 +* + user_banned +* + user_unbanned +* + torrent_uploaded_locked +* + moderation_guidelines +* + guidelines diff --git a/translations/ca-es.json b/translations/ca-es.json index 2b1f48dc..74ea1c95 100644 --- a/translations/ca-es.json +++ b/translations/ca-es.json @@ -1,4 +1,8 @@ [ + { + "id": "read_rules", + "translation": "Llegiu les normes abans de carregar res." + }, { "id": "rules", "translation": "Normes" @@ -19,6 +23,10 @@ "id": "rules_sukebei", "translation": "El contingut per a adults correspon a sukebei.pantsu.cat" }, + { + "id": "rule_tracker_url", + "translation": "https://uw0.xyz/ és la URL del rastrejador oficial" + }, { "id": "verify_email_title", "translation": "Verifiqueu la vostra adreça electrònica de Nyaapantsu." @@ -81,7 +89,7 @@ }, { "id": "terms_conditions_full", - "translation": "Una mica de merda." + "translation": "

Termes i condicions de NyaaPantsu

1. Termes

Si accediu al lloc web ubicat a https://pantsu.cat, accepteu aquests termes del servei, totes les lleis i regulacions aplicables, i accepteu que sou responsable del compliment de totes les lleis locals aplicables. Si no esteu d'acord amb algun d'aquests termes, us és prohibit d'utilitzar o d'accedir a aquest lloc web. El material contingut en aquest lloc web està protegit per les lleis de marques i copyright aplicables.

2. Exempció de responsabilitat

  1. El material del lloc web de NyaaPantsu Lda es proporciona 'tal com és'. NyaaPantsu Lda no proporciona cap garantia, explícita o implícita, i per tant nega i s'eximeix de totes les altres garanties, incloent-hi, però sense limitació, les garanties implícites o les condicions de mercantibilitat, adequació per a un fi particular, o el no infringiment de la propietat intel·lectual o la violació d'altres drets.
  2. De manera addicional, NyaaPantsu Lda no garanteix ni efectua cap representació pel que fa a la precisió, similitud de resultats, o confiabilitat en l'ús del material del seu lloc web o cap altra relació amb aquest material o cap dels llocs enllaçats en aquest lloc.

3. Limitacions

En cap cas NyaaPantsu Lda o els seus proveïdors seran responsables dels danys (incloent-hi, sense limitació, danys per la pèrdua de dades o de beneficis, o a causa de la interrupció del negoci) que sorgeixin de l'ús o incapacitat d'ús del material del lloc web de NyaaPantsu Lda, fins i tot quan NyaaPantsu Lda o un representant autoritzat de NyaaPantsu Lda hagi estat notificat oralment o per escrit de la possibilitat d'aquests danys. Com que algunes jurisdiccions no permeten limitacions a les garanties implícites, o limitacions en la responsabilitat en els danys incidentals o conseqüencials, és possible que aquestes limitacions no us siguin d'aplicació.

4. Precisió del material

El material que apareix al lloc web de NyaaPantsu Lda pot incloure errors tècnics, tipogràfics o fotogràfics. NyaaPantsu Lda no garanteix que cap dels materials del seu lloc web siguin precisos, complets o actuals. NyaaPantsu Lda pot fer canvis al material contingut al seu lloc web en qualsevol moment i sense avís. No obstant això, NyaaPantsu Lda no estableix cap compromís d'actualitzar el material.

5. Enllaços

NyaaPantsu Lda no ha revisat tots els llocs web que s'enllacen al seu lloc web i no és responsable del contingut de cap d'aquests llocs enllaçats. La inclusió d'un enllaç no implica suport del lloc per part de NyaaPantsu Lda. L'ús dels llocs web enllaçats resta sota el propi risc de l'usuari.

6. Modificacions

NyaaPantsu Lda pot revisar aquests termes del servei del seu lloc web en qualsevol moment sense avís. Si utilitzeu aquest lloc web, accepteu la versió llavors actual d'aquests termes del servei.

7. Llei aplicable

Aquests termes i condicions són governats i s'interpreten sota les lleis de Portugal i accepteu irrevocablement sotmetre-us a la jurisdicció exclusiva dels tribunals d'aquest estat o ubicació.

" }, { "id": "remember_me", @@ -131,6 +139,10 @@ "id": "torrents", "translation": "Torrents" }, + { + "id": "followers", + "translation": "Seguidors" + }, { "id": "follow", "translation": "Segueix" @@ -199,6 +211,10 @@ "id": "err_no_results", "translation": "No s'han trobat resultats" }, + { + "id": "err_no_torrent_file", + "translation": "No s'ha trobat el fitxer torrent" + }, { "id": "upload", "translation": "Carrega" @@ -245,7 +261,7 @@ }, { "id": "userstatus_member", - "translation": "Membres" + "translation": "Membre" }, { "id": "no_results_found", @@ -385,7 +401,7 @@ }, { "id": "select_a_torrent_category", - "translation": "Escolliu una categoria de torrents" + "translation": "Escolliu una categoria de torrent" }, { "id": "anime", @@ -523,6 +539,10 @@ "id": "description_markdown_notice", "translation": "Podeu utilitzar Markdown a les descripcions." }, + { + "id": "comment_markdown_notice", + "translation": "Podeu utilitzar Markdown als comentaris." + }, { "id": "show_all", "translation": "Mostra-ho tot" @@ -567,6 +587,10 @@ "id": "submit_a_comment_as_anonymous", "translation": "Envieu un comentari com a Anònim" }, + { + "id": "torrent_no_comments", + "translation": "No hi ha cap comentari dels usuaris." + }, { "id": "submit", "translation": "Envia" @@ -607,6 +631,10 @@ "id": "userstatus_moderator", "translation": "Moderador" }, + { + "id": "userstatus_uploader", + "translation": "Publicador" + }, { "id": "api_token", "translation": "Token de l'API" @@ -755,6 +783,10 @@ "id": "report_type", "translation": "Tipus d'informe" }, + { + "id": "report_message", + "translation": "Missatge de l'informe" + }, { "id": "illegal_content", "translation": "Contingut il·legal" @@ -771,6 +803,14 @@ "id": "duplicate_deprecated", "translation": "Duplicat / Caducat" }, + { + "id": "nsfw_content", + "translation": "Contingut per a adults" + }, + { + "id": "other", + "translation": "Altres" + }, { "id": "captcha", "translation": "Captcha" @@ -809,12 +849,28 @@ }, { "id": "clear_notifications", - "translation": "Neteja les notificacions" + "translation": "Neteja les notificacions llegides" + }, + { + "id": "clear_all_notifications", + "translation": "Neteja totes les notificacions" + }, + { + "id": "mark_notifications_as_read", + "translation": "Marca les notificacions com a llegides" }, { "id": "notifications_cleared", "translation": "S'han netejat les notificacions!" }, + { + "id": "read_notifications_cleared", + "translation": "S'han netejat les notificacions llegides!" + }, + { + "id": "notifications_read", + "translation": "Totes les notificacions s'han marcat com a llegides!" + }, { "id": "my_notifications", "translation": "Les meves notificacions" @@ -935,6 +991,10 @@ "id": "delete_report", "translation": "Suprimeix l'informe" }, + { + "id": "delete_all_reports", + "translation": "Suprimeix tots els informes" + }, { "id": "comment_deleted", "translation": "S'ha suprimit el comentari!" @@ -1065,7 +1125,7 @@ }, { "id": "change_settings", - "translation": "Canvia l'aparença o l'idioma" + "translation": "Canvia l'aparença / Configuració" }, { "id": "mascot", @@ -1083,6 +1143,10 @@ "id": "theme_none", "translation": "Cap" }, + { + "id": "themes_classic", + "translation": "nyaa.se (Beta)" + }, { "id": "upload_as_anon", "translation": "Carrega'l anònimament" @@ -1307,6 +1371,18 @@ "id": "mascot_url", "translation": "URL de la mascota" }, + { + "id": "alternating_color", + "translation": "Color alternat (i tipus de lletra sense negreta)" + }, + { + "id": "old_nav", + "translation": "Navegació antiga" + }, + { + "id": "old_nav_explanation", + "translation": "Mostra les categories principals i els números de pàgina a la part superior de la pàgina durant una cerca, com nyaa.se." + }, { "id": "no_notifications", "translation": "No hi ha notificacions" @@ -1327,10 +1403,6 @@ "id": "torrent_language", "translation": "Idioma del torrent" }, - { - "id": "language_not_mandatory", - "translation": "L'idioma ja no és obligatori" - }, { "id": "language_en-us_name", "translation": "Anglès" @@ -1545,7 +1617,7 @@ }, { "id": "error_numeric_valid", - "translation": "%s ha se ser un valor numèric vàlid" + "translation": "%s ha de ser un valor numèric vàlid" }, { "id": "error_number_valid", @@ -1623,6 +1695,10 @@ "id": "refine_search", "translation": "Refina la cerca" }, + { + "id": "refine", + "translation": "Refina" + }, { "id": "between", "translation": "Entre" @@ -1643,18 +1719,10 @@ "id": "years", "translation": "Anys" }, - { - "id": "refine", - "translation": "Refina" - }, { "id": "large", "translation": "gran." }, - { - "id": "old", - "translation": "antic" - }, { "id": "optional", "translation": "Opcional" @@ -1667,6 +1735,34 @@ "id": "show", "translation": "Mostra" }, + { + "id": "sort_by", + "translation": "Ordena per" + }, + { + "id": "ascending", + "translation": "Ascendent" + }, + { + "id": "descending", + "translation": "Descendent" + }, + { + "id": "quality", + "translation": "Qualitat" + }, + { + "id": "exclude_user", + "translation": "Exclou" + }, + { + "id": "from_user", + "translation": "De" + }, + { + "id": "limit_torrent_count", + "translation": "Límit" + }, { "id": "username_taken", "translation": "El nom d'usuari ja està ocupat, podeu escollir: %s" @@ -1759,6 +1855,10 @@ "id": "error_content_type_post", "translation": "Proporcioneu una capçalera Content-Type: application/json o multipart/form-data" }, + { + "id": "torrent_name", + "translation": "Nom del torrent" + }, { "id": "torrent_name_invalid", "translation": "El nom del torrent és invàlid" @@ -1847,10 +1947,26 @@ "id": "torrent_colors", "translation": "Colors dels torrents" }, + { + "id": "faq_ads", + "translation": "Per què Pantsu té anuncis?" + }, + { + "id": "faq_ads_explanation", + "translation": "No és una decisió que hàgim pres a la lleugera, i hem considerat profundament les objeccions que hi heu posat tots, però pensem que és l'única solució sostenible per finançar Pantsu i assegurar el seu funcionament continuar.
Hem pensat molt en les possibles fonts de finançament, i cap altra opció que hàgim considerat s'acostava a ser capaç de finançar Pantsu.
Després de pensar-hi molt, hem decidit que fer servir anuncis d'a-ads és la millor opció per a nosaltres i per als nostres usuaris. Aquests anuncis són simples i poc intrusius, no interfereixen de cap manera amb l'experiència del lloc, i no inclouen cap tipus de rastreig." + }, { "id": "torrent_preview", "translation": "Previsualitza el torrent" }, + { + "id": "announcement", + "translation": "Anunci" + }, + { + "id": "create_anouncement_success", + "translation": "Anunci creat satisfactòriament" + }, { "id": "update_client_failed", "translation": "L'actualització del client ha fallat!" @@ -1861,7 +1977,7 @@ }, { "id": "update_client_panel", - "translation": "Actualitza el client" + "translation": "Actualitza un client" }, { "id": "create_client_success", @@ -1923,6 +2039,10 @@ "id": "remove", "translation": "Suprimeix" }, + { + "id": "close", + "translation": "Tanca" + }, { "id": "secret", "translation": "Secret del client" @@ -1931,6 +2051,14 @@ "id": "torrent_age", "translation": "fa {1} dies i {2} hores" }, + { + "id": "tag", + "translation": "Etiqueta" + }, + { + "id": "torrent_tags", + "translation": "Etiquetes" + }, { "id": "wrong_tag_type", "translation": "El tipus d'etiqueta escollit no existeix" @@ -1943,6 +2071,14 @@ "id": "tagtype", "translation": "Tipus d'etiqueta" }, + { + "id": "accepted_tags", + "translation": "Etiquetes acceptades" + }, + { + "id": "recommended_tags", + "translation": "Etiquetes recomanades" + }, { "id": "tagtype_anidbid", "translation": "Identificador d'AniDB" @@ -1953,10 +2089,166 @@ }, { "id": "tagtype_videoquality", - "translation": "Etiqueta de qualitat" + "translation": "Qualitat del vídeo" }, { - "id": "torrent_tags", - "translation": "Etiquetes del torrent" + "id": "tagtype_dlsite", + "translation": "Lloc de descàrrega" + }, + { + "id": "tagtype_vgmdbid", + "translation": "Identificador de VGMDB" + }, + { + "id": "tagtype_tags", + "translation": "Etiquetes descriptives" + }, + { + "id": "tagvalue_select", + "translation": "Selecciona..." + }, + { + "id": "tagvalue_full_hd", + "translation": "1080p" + }, + { + "id": "tagvalue_hd", + "translation": "720p" + }, + { + "id": "tagvalue_sd", + "translation": "480p" + }, + { + "id": "tagvalue_bluray", + "translation": "Bluray" + }, + { + "id": "announcements", + "translation": "Anuncis" + }, + { + "id": "message", + "translation": "Missatge" + }, + { + "id": "duration", + "translation": "Durada (hores)" + }, + { + "id": "update_annoucement_panel", + "translation": "Actualitza l'anunci" + }, + { + "id": "create_annoucement_panel", + "translation": "Crea un anunci" + }, + { + "id": "related_to", + "translation": "Relacionat amb" + }, + { + "id": "expire", + "translation": "Caduca" + }, + { + "id": "complement", + "translation": "Complement" + }, + { + "id": "from", + "translation": "Des de" + }, + { + "id": "to", + "translation": "fins a" + }, + { + "id": "feed_types", + "translation": "Tipus de fonts: RSS (feed), RSS amb enllaços magnètics (magnet), Especificació Torznab (torznab), Especificació EZTV (eztv)" + }, + { + "id": "append_query", + "translation": "Podeu afegir la vostra cerca al final de la URL de la font." + }, + { + "id": "cookie_warning", + "translation": "Si feu servir aquest lloc web, n'accepteu l'ús de les galetes per personalitzar-ne el contingut.
Aquest missatge s'amagarà quan torneu a carregar la pàgina." + }, + { + "id": "pending_torrent", + "translation": "Tingueu paciència, s'està generant el fitxer torrent. Torneu-ho a provar d'aquí uns segons." + }, + { + "id": "generating_torrent", + "translation": "S'està generant el fitxer torrent..." + }, + { + "id": "generating_torrent_failed", + "translation": "No s'ha pogut generar el fitxer torrent" + }, + { + "id": "enabled", + "translation": "Activat" + }, + { + "id": "disabled", + "translation": "Desactivat" + }, + { + "id": "reset_api_key", + "translation": "Reinicialitza la clau de l'API" + }, + { + "id": "tag_error", + "translation": "No s'ha pogut afegir l'etiqueta, comproveu la connexió a internet i que hàgiu omplert tots els camps." + }, + { + "id": "search_from_user", + "translation": "Cerca d'aquest usuari..." + }, + { + "id": "usersearch_user_not_found", + "translation": "No s'ha trobat l'usuari \"%s\"" + }, + { + "id": "user_search_explanation", + "translation": "Cerqueu un usuari escrivint-ne el nom d'usuari a la casella de sota" + }, + { + "id": "no_api_token", + "translation": "No hi ha token de l'API" + }, + { + "id": "search_from_specific_user", + "translation": "Cerca de %s" + }, + { + "id": "browsing_user_torrents", + "translation": "Cerca dels torrents de %s" + }, + { + "id": "hours_minutes_ago", + "translation": "fa {0} hores i {1} minuts" + }, + { + "id": "days_hours_ago", + "translation": "fa {0} dies i {1} hores" + }, + { + "id": "status", + "translation": "Estat" + }, + { + "id": "event", + "translation": "Esdeveniment" + }, + { + "id": "read", + "translation": "Llegit" + }, + { + "id": "unread", + "translation": "No llegit" } ] diff --git a/translations/en-us.all.json b/translations/en-us.all.json index 22aacf12..1ff5f663 100644 --- a/translations/en-us.all.json +++ b/translations/en-us.all.json @@ -607,6 +607,10 @@ "id": "current_password", "translation": "Current password" }, + { + "id": "default", + "translation": "Default" + }, { "id": "role", "translation": "Role" @@ -615,10 +619,6 @@ "id": "userstatus_banned", "translation": "Banned" }, - { - "id": "default", - "translation": "Default" - }, { "id": "userstatus_trusted", "translation": "Trusted member" @@ -631,6 +631,10 @@ "id": "userstatus_moderator", "translation": "Moderator" }, + { + "id": "userstatus_janitor", + "translation": "Janitor" + }, { "id": "userstatus_uploader", "translation": "Uploader" @@ -895,6 +899,10 @@ "id": "torrent_uploaded", "translation": "torrent uploaded successfully!" }, + { + "id": "torrent_uploaded_locked", + "translation": "Your torrent has been uploaded but is locked because you are banned." + }, { "id": "preferences", "translation": "Preferences" @@ -988,9 +996,21 @@ "translation": "Torrent #%d from %s has been locked by %s." }, { - "id": "torrent_blocked_by", + "id": "torrent_unblocked_by", "translation": "Torrent #%d from %s has been unlocked by %s." }, + { + "id": "user_banned", + "translation": "User has been banned!" + }, + { + "id": "user_banned", + "translation": "User has been unbanned!" + }, + { + "id": "user_unbanned_by", + "translation": "User %s(%d) #%d has been unbanned by %s" + }, { "id": "torrents_deleted", "translation": "Torrents Deleted" @@ -1111,6 +1131,14 @@ "id": "torrent_block", "translation": "Lock" }, + { + "id": "unban", + "translation": "Unban" + }, + { + "id": "ban", + "translation": "Ban" + }, { "id": "torrent_deleted_definitely", "translation": "Torrent has been erased from the database!" @@ -2262,5 +2290,13 @@ { "id": "unread", "translation": "Unread" + }, + { + "id": "moderation_guidelines", + "translation": "Moderation Guidelines" + }, + { + "id": "guidelines", + "translation": "Guidelines" } ] diff --git a/translations/fr-fr.all.json b/translations/fr-fr.all.json index 26d8e39c..c60aff30 100644 --- a/translations/fr-fr.all.json +++ b/translations/fr-fr.all.json @@ -606,6 +606,10 @@ { "id": "current_password", "translation": "Mot de passe actuel" + }, + { + "id": "default", + "translation": "Par défaut" }, { "id": "role", @@ -615,10 +619,6 @@ "id": "userstatus_banned", "translation": "Banni" }, - { - "id": "default", - "translation": "Par défaut" - }, { "id": "userstatus_trusted", "translation": "Membre de confiance" @@ -631,9 +631,13 @@ "id": "userstatus_moderator", "translation": "Modérateur" }, + { + "id": "userstatus_janitor", + "translation": "Janiteur" + }, { "id": "userstatus_uploader", - "translation": "Uploader" + "translation": "Uploadeur" }, { "id": "api_token", @@ -763,6 +767,18 @@ "id": "no_files", "translation": "Aucun fichier trouvé." }, + { + "id": "loading_file_list", + "translation": "Chargement de la liste des fichiers, la récupération des longues listes peut prendre un certain temps..." + }, + { + "id": "torrent_filelist", + "translation": "Liste des fichiers du torrent" + }, + { + "id": "back_to_torrent", + "translation": "Revenir à \"%s\"" + }, { "id": "uploaded_by", "translation": "Uploadé par" @@ -883,6 +899,10 @@ "id": "torrent_uploaded", "translation": "Le torrent a été uploadé avec succès !" }, + { + "id": "torrent_uploaded_locked", + "translation": "Le torrent a été uploadé mais est actuellement bloqué en raison de votre bannissement." + }, { "id": "preferences", "translation": "Préférences" @@ -976,9 +996,25 @@ "translation": "Le torrent n° %s de %s a été bloqué par %s." }, { - "id": "torrent_blocked_by", + "id": "torrent_unblocked_by", "translation": "Le torrent n° %s de %s a été débloqué par %s." }, + { + "id": "user_banned", + "translation": "L'utilisateur a été banni !" + }, + { + "id": "user_banned_by", + "translation": "L'utilisateur %s(%d) #%d a été banni par %s." + }, + { + "id": "user_unbanned", + "translation": "L'utilisateur a été débanni !" + }, + { + "id": "user_unbanned_by", + "translation": "L'utilisateur %s(%d) #%d a été débanni par %s." + }, { "id": "torrents_deleted", "translation": "Torrents supprimés" @@ -1099,6 +1135,14 @@ "id": "torrent_block", "translation": "Bloquer" }, + { + "id": "unban", + "translation": "Débannir" + }, + { + "id": "ban", + "translation": "Bannir" + }, { "id": "torrent_deleted_definitely", "translation": "Le torrent a été effacé de la base de données." @@ -1929,7 +1973,7 @@ }, { "id": "remux", - "translation": "Remux de la version originale d'un autre uploader" + "translation": "Remux de la version originale d'un autre uploadeur" }, { "id": "reupload", @@ -2250,5 +2294,13 @@ { "id": "unread", "translation": "Non lu" + }, + { + "id": "moderation_guidelines", + "translation": "Directives de modération" + }, + { + "id": "guidelines", + "translation": "Directives" } ] diff --git a/translations/ja-jp.all.json b/translations/ja-jp.all.json index 587cc769..79cf0e62 100644 --- a/translations/ja-jp.all.json +++ b/translations/ja-jp.all.json @@ -537,11 +537,11 @@ }, { "id": "description_markdown_notice", - "translation": "説明文には MarkDown 記法を使うことができます。" + "translation": "説明文には Markdown 記法を使うことができます。" }, { "id": "comment_markdown_notice", - "translation": "コメントには MarkDown 記法を使うことができます。" + "translation": "コメントには Markdown 記法を使うことができます。" }, { "id": "show_all", @@ -607,6 +607,10 @@ "id": "current_password", "translation": "現在のパスワード" }, + { + "id": "default", + "translation": "デフォルト" + }, { "id": "role", "translation": "役割" @@ -615,10 +619,6 @@ "id": "userstatus_banned", "translation": "BAN" }, - { - "id": "default", - "translation": "デフォルト" - }, { "id": "userstatus_trusted", "translation": "高信頼アカウント" @@ -631,6 +631,10 @@ "id": "userstatus_moderator", "translation": "モデレーター" }, + { + "id": "userstatus_janitor", + "translation": "管理人" + }, { "id": "userstatus_uploader", "translation": "アップロード者" @@ -883,6 +887,10 @@ "id": "torrent_uploaded", "translation": "Torrent は正常にアップロードされました。" }, + { + "id": "torrent_uploaded_locked", + "translation": "Torrent はアップロードされましたが、あなたが BAN されているためロック状態になっています。" + }, { "id": "preferences", "translation": "カスタム設定" @@ -976,9 +984,21 @@ "translation": "Torrent #%s (%s アップロード) は %s によってロックされました。" }, { - "id": "torrent_blocked_by", + "id": "torrent_unblocked_by", "translation": "Torrent #%s (%s アップロード) は %s によってロック解除されました。" }, + { + "id": "user_banned", + "translation": "ユーザーは BAN されました。" + }, + { + "id": "user_banned", + "translation": "ユーザーは BAN 解除されました。" + }, + { + "id": "user_unbanned_by", + "translation": "ユーザー %s (%d) #%d は %s によって BAN 解除されました。" + }, { "id": "torrents_deleted", "translation": "Torrent が削除されました" @@ -1099,6 +1119,14 @@ "id": "torrent_block", "translation": "ロック" }, + { + "id": "unban", + "translation": "BAN 解除" + }, + { + "id": "ban", + "translation": "BAN" + }, { "id": "torrent_deleted_definitely", "translation": "Torrent がデータベースから削除されました。" @@ -1723,10 +1751,6 @@ "id": "large", "translation": " " }, - { - "id": "old", - "translation": "前" - }, { "id": "optional", "translation": "任意" @@ -1813,7 +1837,7 @@ }, { "id": "elevating_user_error", - "translation": "モデレーターへのステータス降格は禁止されています" + "translation": "モデレーターへのステータス昇格は禁止されています" }, { "id": "parse_error_line", @@ -1967,6 +1991,10 @@ "id": "announcement", "translation": "お知らせ" }, + { + "id": "create_anouncement_success", + "translation": "正常にお知らせが作成されました。" + }, { "id": "update_client_failed", "translation": "クライアントの更新に失敗しました。" @@ -2250,5 +2278,13 @@ { "id": "unread", "translation": "未読" + }, + { + "id": "moderation_guidelines", + "translation": "モデレーション ガイドライン" + }, + { + "id": "guidelines", + "translation": "ガイドライン" } ] diff --git a/translations/ru-ru.all.json b/translations/ru-ru.all.json index a2555225..9cf2ebe3 100644 --- a/translations/ru-ru.all.json +++ b/translations/ru-ru.all.json @@ -281,15 +281,15 @@ }, { "id": "future_not_looking_good", - "translation": "Будущие перспективы для nyaa выглядят не очень хорошо. (Он мертв)" + "translation": "Будущие перспективы для nyaa выглядят не очень хорошо. (Он мёртв)" }, { "id": "recovery_effort", - "translation": "Идет процесс восстановления." + "translation": "Идёт процесс восстановления." }, { "id": "is_everything_lost", - "translation": "Все пропало?" + "translation": "Всё пропало?" }, { "id": "in_short_no", @@ -297,7 +297,7 @@ }, { "id": "are_some_things_lost", - "translation": "Что-нибудь все же потерялось?" + "translation": "Что-нибудь всё же потерялось?" }, { "id": "answer_is_nyaa_db_lost", @@ -309,7 +309,7 @@ }, { "id": "how_are_we_recovering", - "translation": "Как идет процесс восстановления?" + "translation": "Как идёт процесс восстановления?" }, { "id": "answer_how_are_we_recovering", @@ -329,7 +329,7 @@ }, { "id": "answer_are_the_trackers_working", - "translation": "Даже если трекеры не работают, сиды все еще подключены к децентрализованной сети DHT. Пока файл находится в сети DHT, все должено работать как обычно." + "translation": "Даже если трекеры не работают, сиды всё ещё подключены к децентрализованной сети DHT. Пока файл находится в сети DHT, всё должно работать как обычно." }, { "id": "how_do_i_download_the_torrents", @@ -337,11 +337,11 @@ }, { "id": "answer_how_do_i_download_the_torrents", - "translation": "Просто используйте Magnet-ссылку. Она будет использоваться вашим клиентом BitTorrent для поиска файла в сети DHT, и он должен загрузиться просто отлично." + "translation": "Просто используйте Магнет-ссылку. Она будет использоваться вашим клиентом BitTorrent для поиска файла в сети DHT, и он должен загрузиться просто отлично." }, { "id": "magnet_link_should_look_like", - "translation": "Magnet-ссылка должна выглядеть так:" + "translation": "Магнет-ссылка должна выглядеть так:" }, { "id": "which_trackers_do_you_recommend", @@ -349,7 +349,7 @@ }, { "id": "answer_which_trackers_do_you_recommend", - "translation": "У нас теперь есть собственный трекер uw0.xyz, добавьте его в начало списка перед загрузкой. Вам стоит еще добавить эти, просто на случай, если что-то пойдет не так." + "translation": "У нас теперь есть собственный трекер uw0.xyz, добавьте его в начало списка перед загрузкой. Вам стоит еще добавить эти, просто на случай, если что-то пойдёт не так." }, { "id": "how_can_i_help", @@ -381,7 +381,7 @@ }, { "id": "magnet_link", - "translation": "Magnet-ссылка" + "translation": "Магнет-ссылка" }, { "id": "all_categories", @@ -549,7 +549,7 @@ }, { "id": "hash", - "translation": "Хэш" + "translation": "Хеш" }, { "id": "description", @@ -621,7 +621,7 @@ }, { "id": "profile_updated", - "translation": "Ваш профиль был успешно обновлен!" + "translation": "Ваш профиль был успешно обновлён!" }, { "id": "delete_account", @@ -633,7 +633,7 @@ }, { "id": "delete_success", - "translation": "Аккаунт успешно удален!" + "translation": "Аккаунт успешно удалён!" }, { "id": "moderation", @@ -761,7 +761,7 @@ }, { "id": "illegal_content", - "translation": "Запрещенный контент" + "translation": "Запрещённый контент" }, { "id": "spam_garbage", @@ -893,7 +893,7 @@ }, { "id": "no_move_location_selected", - "translation": "Thou has't to telleth whither thee wanteth to moveth thy selection!" + "translation": "Вы должны указать, куда вы хотите переместить свой выбор!" }, { "id": "select_one_element", @@ -1173,7 +1173,7 @@ }, { "id": "delete_reports_with_torrents", - "translation": "Вы хотите удалить отчеты по выбранным торрентам?" + "translation": "Вы хотите удалить отчёты по выбранным торрентам?" }, { "id": "with_st", @@ -1181,27 +1181,27 @@ }, { "id": "and_reports", - "translation": " and reports" + "translation": " и отчёты" }, { "id": "reports", - "translation": "reports" + "translation": "отчёты" }, { "id": "lock", - "translation": "lock" + "translation": "блок" }, { "id": "status_js", - "translation": "status: {0}" + "translation": "статус: {0}" }, { "id": "owner_id_js", - "translation": "owner_id: {0}" + "translation": "id_владельца: {0}" }, { "id": "category_js", - "translation": "category: {0}" + "translation": "категория: {0}" }, { "id": "no_changes", @@ -1321,7 +1321,7 @@ }, { "id": "alternating_color", - "translation": "Переменный цвет (и не-жирный шрифт)" + "translation": "Переменный цвет (и нежирный шрифт)" }, { "id": "no_notifications", @@ -1565,7 +1565,7 @@ }, { "id": "error_number_valid", - "translation": "%s должно быть дейстивительным числом" + "translation": "%s должно быть действительным числом" }, { "id": "error_hexadecimal_valid", @@ -1729,7 +1729,7 @@ }, { "id": "password_error_generating", - "translation": "Ошибка генерации хэша для вашего пароля" + "translation": "Ошибка генерации хеша для вашего пароля" }, { "id": "permission_delete_error", @@ -1781,7 +1781,7 @@ }, { "id": "error_api_token", - "translation": "Error API токен не существует" + "translation": "Ошибка API, токен не существует" }, { "id": "uploads_disabled", @@ -1837,11 +1837,11 @@ }, { "id": "torrent_magnet_invalid", - "translation": "Magnet-ссылка не может быть спарсена, пожалуйста, проверьте её" + "translation": "Магнет-ссылка не может быть спарсена, пожалуйста, проверьте её" }, { "id": "torrent_hash_invalid", - "translation": "Неверный хэш торрента" + "translation": "Неверный хеш торрента" }, { "id": "torrent_plus_magnet", @@ -1849,7 +1849,7 @@ }, { "id": "torrent_file_invalid", - "translation": "Неверный torrent-файл" + "translation": "Неверный торрент-файл" }, { "id": "torrent_uri_invalid", @@ -1937,15 +1937,15 @@ }, { "id": "grant_types", - "translation": "Grant Types" + "translation": "Типы разрешений" }, { "id": "response_types", - "translation": "Response Types" + "translation": "Типы ответов" }, { "id": "scope", - "translation": "Scopes" + "translation": "Области" }, { "id": "owner", @@ -2105,7 +2105,7 @@ }, { "id": "feed_types", - "translation": "Типы фидов: RSS(feed), RSS с Magnet-ссылками(magnet), Torznab Spec(torznab), EZTV Spec(eztv)" + "translation": "Типы фидов: RSS(feed), RSS с магнет-ссылками(magnet), Torznab Spec(torznab), EZTV Spec(eztv)" }, { "id": "append_query", diff --git a/utils/search/search.go b/utils/search/search.go index f79331de..ed17bf7c 100644 --- a/utils/search/search.go +++ b/utils/search/search.go @@ -39,25 +39,25 @@ func stringIsASCII(input string) bool { // ByQueryNoUser : search torrents according to request without user func ByQueryNoUser(c *gin.Context, pagenum int) (search TorrentParam, tor []models.Torrent, count int, err error) { - search, tor, count, err = ByQuery(c, pagenum, false, false, false) + search, tor, count, err = ByQuery(c, pagenum, false, false, false, false) return } // ByQueryWithUser : search torrents according to request with user func ByQueryWithUser(c *gin.Context, pagenum int) (search TorrentParam, tor []models.Torrent, count int, err error) { - search, tor, count, err = ByQuery(c, pagenum, true, false, false) + search, tor, count, err = ByQuery(c, pagenum, true, false, false, true) return } // ByQueryDeleted : search deleted torrents according to request with user and count func ByQueryDeleted(c *gin.Context, pagenum int) (search TorrentParam, tor []models.Torrent, count int, err error) { - search, tor, count, err = ByQuery(c, pagenum, true, true, false) + search, tor, count, err = ByQuery(c, pagenum, true, true, false, true) return } // ByQueryNoHidden : search torrents and filter those hidden func ByQueryNoHidden(c *gin.Context, pagenum int) (search TorrentParam, tor []models.Torrent, count int, err error) { - search, tor, count, err = ByQuery(c, pagenum, false, false, true) + search, tor, count, err = ByQuery(c, pagenum, false, false, true, false) return } @@ -66,11 +66,12 @@ func ByQueryNoHidden(c *gin.Context, pagenum int) (search TorrentParam, tor []mo // elasticsearch always provide a count to how many hits // ES doesn't store users // deleted is unused because es doesn't index deleted torrents -func ByQuery(c *gin.Context, pagenum int, withUser bool, deleted bool, hidden bool) (TorrentParam, []models.Torrent, int, error) { +func ByQuery(c *gin.Context, pagenum int, withUser bool, deleted bool, hidden bool, locked bool) (TorrentParam, []models.Torrent, int, error) { var torrentParam TorrentParam torrentParam.FromRequest(c) torrentParam.Offset = uint32(pagenum) torrentParam.Hidden = hidden + torrentParam.Locked = locked torrentParam.Full = withUser torrentParam.Deleted = deleted @@ -106,6 +107,6 @@ func ByQuery(c *gin.Context, pagenum int, withUser bool, deleted bool, hidden bo } // AuthorizedQuery return a seach byquery according to the bool. If false, it doesn't look for hidden torrents, else it looks for every torrents -func AuthorizedQuery(c *gin.Context, pagenum int, authorized bool) (TorrentParam, []models.Torrent, int, error) { - return ByQuery(c, pagenum, true, false, !authorized) +func AuthorizedQuery(c *gin.Context, pagenum int, authorized bool, locked bool) (TorrentParam, []models.Torrent, int, error) { + return ByQuery(c, pagenum, true, false, !authorized, locked) } diff --git a/utils/search/torrentParam.go b/utils/search/torrentParam.go index 394ae4cc..11be217b 100644 --- a/utils/search/torrentParam.go +++ b/utils/search/torrentParam.go @@ -26,6 +26,7 @@ type TorrentParam struct { Full bool // True means load all members Order bool // True means ascending Hidden bool // True means filter hidden torrents + Locked bool // False means filter locked torrents Deleted bool // False means filter deleted torrents Status Status Sort SortMode @@ -71,7 +72,7 @@ func (p *TorrentParam) Identifier() string { tags += p.VideoQuality dbids := fmt.Sprintf("%d%d%d%s", p.AnidbID, p.VndbID, p.VgmdbID, p.Dlsite) - identifier := fmt.Sprintf("%s%s%s%d%d%d%d%d%d%d%s%s%s%d%s%s%s%t%t%t%t", p.NameLike, p.NotNull, languages, p.Max, p.Offset, p.FromID, p.MinSize, p.MaxSize, p.Status, p.Sort, dbids, p.FromDate, p.ToDate, p.UserID, ids, cats, tags, p.Full, p.Order, p.Hidden, p.Deleted) + identifier := fmt.Sprintf("%s%s%s%d%d%d%d%d%d%d%s%s%s%d%s%s%s%t%t%t%t%t", p.NameLike, p.NotNull, languages, p.Max, p.Offset, p.FromID, p.MinSize, p.MaxSize, p.Status, p.Sort, dbids, p.FromDate, p.ToDate, p.UserID, ids, cats, tags, p.Full, p.Order, p.Hidden, p.Locked, p.Deleted) return base64.URLEncoding.EncodeToString([]byte(identifier)) } @@ -251,8 +252,11 @@ func (p *TorrentParam) toESQuery(c *gin.Context) *Query { if p.Status != ShowAll { query.Append(p.Status.ToESQuery()) + } else if !p.Locked { + query.Append(fmt.Sprintf("!(status:%d)", 5)) } + if p.FromID != 0 { query.Append("id:>" + strconv.FormatInt(int64(p.FromID), 10)) } @@ -406,7 +410,10 @@ func (p *TorrentParam) toDBQuery(c *gin.Context) *Query { } if p.Status != 0 { query.Append(p.Status.ToDBQuery()) + } else if !p.Locked { + query.Append("status IS NOT ?", 5) } + if len(p.NotNull) > 0 { query.Append(p.NotNull) } diff --git a/utils/search/torrentParam_test.go b/utils/search/torrentParam_test.go index 1af3346f..b84009a6 100644 --- a/utils/search/torrentParam_test.go +++ b/utils/search/torrentParam_test.go @@ -12,13 +12,13 @@ import ( func TestTorrentParam_Identifier(t *testing.T) { torrentParam := &TorrentParam{} assert := assert.New(t) - assert.Equal("MDAwMDAwMDAwMDBmYWxzZWZhbHNlZmFsc2VmYWxzZQ==", torrentParam.Identifier(), "It should be empty") + assert.Equal("MDAwMDAwMDAwMDBmYWxzZWZhbHNlZmFsc2VmYWxzZWZhbHNl", torrentParam.Identifier(), "It should be empty") torrentParam = &TorrentParam{ NameLike: "test", NotNull: "IS NULL", Hidden: false, } - assert.Equal("dGVzdElTIE5VTEwwMDAwMDAwMDAwMGZhbHNlZmFsc2VmYWxzZWZhbHNl", torrentParam.Identifier(), "It should be empty") + assert.Equal("dGVzdElTIE5VTEwwMDAwMDAwMDAwMGZhbHNlZmFsc2VmYWxzZWZhbHNlZmFsc2U=", torrentParam.Identifier(), "It should be empty") } func TestTorrentParam_FromRequest(t *testing.T) { @@ -57,14 +57,14 @@ func TestTorrentParam_ToESQuery(t *testing.T) { Test TorrentParam Expected string }{ - {TorrentParam{}, ""}, - {TorrentParam{NameLike: "lol"}, ""}, - {TorrentParam{NameLike: "lol", FromID: 12}, "id:>12"}, - {TorrentParam{NameLike: "lol", FromID: 12, FromDate: DateFilter("2017-08-01"), ToDate: DateFilter("2017-08-05")}, "id:>12 date: [2017-08-01 2017-08-05]"}, - {TorrentParam{NameLike: "lol", FromID: 12, ToDate: DateFilter("2017-08-05")}, "id:>12 date: [* 2017-08-05]"}, - {TorrentParam{NameLike: "lol", FromID: 12, FromDate: DateFilter("2017-08-01")}, "id:>12 date: [2017-08-01 *]"}, - {TorrentParam{NameLike: "lol", FromID: 12, Category: Categories{&Category{3, 12}}}, "(category: 3 AND sub_category: 12) id:>12"}, - {TorrentParam{NameLike: "lol", FromID: 12, Category: Categories{&Category{3, 12}, &Category{3, 12}}}, "((category: 3 AND sub_category: 12) OR (category: 3 AND sub_category: 12)) id:>12"}, + {TorrentParam{}, "!(status:5)"}, + {TorrentParam{NameLike: "lol"}, "!(status:5)"}, + {TorrentParam{NameLike: "lol", FromID: 12}, "!(status:5) id:>12"}, + {TorrentParam{NameLike: "lol", FromID: 12, FromDate: DateFilter("2017-08-01"), ToDate: DateFilter("2017-08-05")}, "!(status:5) id:>12 date: [2017-08-01 2017-08-05]"}, + {TorrentParam{NameLike: "lol", FromID: 12, ToDate: DateFilter("2017-08-05")}, "!(status:5) id:>12 date: [* 2017-08-05]"}, + {TorrentParam{NameLike: "lol", FromID: 12, FromDate: DateFilter("2017-08-01")}, "!(status:5) id:>12 date: [2017-08-01 *]"}, + {TorrentParam{NameLike: "lol", FromID: 12, Category: Categories{&Category{3, 12}}}, "(category: 3 AND sub_category: 12) !(status:5) id:>12"}, + {TorrentParam{NameLike: "lol", FromID: 12, Category: Categories{&Category{3, 12}, &Category{3, 12}}}, "((category: 3 AND sub_category: 12) OR (category: 3 AND sub_category: 12)) !(status:5) id:>12"}, } for _, test := range tests { diff --git a/utils/upload/upload.go b/utils/upload/upload.go index d48282cc..68ea9324 100644 --- a/utils/upload/upload.go +++ b/utils/upload/upload.go @@ -174,6 +174,9 @@ func UpdateTorrent(r *torrentValidator.UpdateRequest, t *models.Torrent, current } else if currentUser.IsTrusted() { status = models.TorrentStatusTrusted } + if status != t.Status && status != models.TorrentStatusBlocked { + t.DeletedAt = nil + } t.Status = status t.Hidden = r.Update.Hidden