Albirew/nyaa-pantsu
Albirew
/
nyaa-pantsu
Archivé
1
0
Bifurcation 0
* Fix "torrent is being generated" message showing up even when the torrent couldn't be generated

* Add janitor user status

* Remove usage of deprecated HasAdmin() function

* Give Janitors access to mod panel

* Stop using deprecated HasAdmin() function

* Stop using deprecated HasAdmin() function

* Update edit.go

* Update profile.go

* Rollback bad changes, remove redundant email check and stop using deprecated function

* Show every userstatus (member, janitor, banned, mod) in comments

* Return empty status if anonymous

* Show no userstatus for anonymous profile

* Show moderation link to janitors too

* Stop using deprecated HasAdmin() function

* Stop using deprecated HasAdmin() function

* Stop using deprecated HasAdmin() function

* Add Janitor to userstatus select in user edit

* "Janitor" translation string

* didnt mean to commit this change

* rollback wrongfully comitted changes

* rollback commit

* Update CHANGELOG.md

* Fix wrong id for translation

* remove deprecated HasAdmin() function again

* change name of variable used in comment loop for clarity purposes

* visual cue for locked torrents in torrent listing for admins

* add visual cues for hidden torrents in admin torrent listing

* Dont show delete buttons for janitors

* janitor cannot delete torrents

* show block/unblock button in torrent list for janitors instead of delete button

* fix function that didnt get executed

* add ban buttons on userlist & visual cue for banned users

* Fix "user successfully deleted" message showing even if user wasnt deleted

* Add "ban" button, no "delete" button for jantiors

* add "unban" and "ban" translation strings

* add "unban" and "ban" translation strings

* different <form> for ban button

* Update index.jet.html

* add userprofile ban route

* add toggleBan() function, janitors dont need captcha either

* fix panic error when deleting an anonymous comment

* add user_banned_by and user_unbanned_by

* add user_banned_by and user_unbanned_by

* Make ToggleBan() return whether or not the user is now banned

* Add handler for /ban route

* change log filter

* hide locked torrents from regular users

* hide locked torrents from regular users

* hide locked torrent from api search for regular users

* change function to CurrentOrJanitor

* change function to currentorjanitor

* change function to currentorjanitor

* add CurrentOrJanitor function

* fix extra (

* fix extra ) and wrong variable name

* Fix wrong value for janitor user status

* Fix user edit that did not work because of "unique constraint user.emails" error

* only immediately visually update user if user has been updated successfully

* use FindAllForAdminsOrderBy in order to preload users

* create FindAllForAdminsOrderBy that preloads users

* Show username instead of ID in Uploader column in admin panel

* Fix userprofile buttons overflowing at some specific resolutions

* Mods can set users as janitors

* Show ban/unban buttons on userprofile for janitors

* Identical styling for usermenu links and buttons

* dont show ban buttons on other staff

* add ban message through get parameter

* make toggleBan() trigger user update

* Add "user_banned" and "user_unbanned"

* add "user_banned" and "user_unbanned"

* Visual cue for banned user

* banned users can still log in

* visual cue for banned user in  badgemenu

* locked status if banned user on upload

* banned users cannot comment

* Put "banned" text between ()

* add GetCategoryName() to template_test

* add GetCategoryName() that returns full category name from full category string

* Show search content in page title if it exists, or search category if it exists, otherwise shows "Home"

* error message when user uploads an torrent & is banned

* add torrent_uploaded_locked

* add torrent_uploaded_locked

* fix delete definitely button that never appeared

* Show delete definitely button on admin panel index

* admins can undelete a torrent by editing it's status

* Trigger ViewHandler() directly instead of redirecting

* Render the template directly instead of triggering torrent view handler

* bigger usermenu buttons once responsive design kicks in

* make btn-* class non-bold

* Responsive notification page

* visual cue for locked torrents in torrent listing

* Update search.go

* Update search.go

* Update api.go

* Update helpers.go

* Update template.go

* Update torrentParam.go

* remove "hidden" class

* Update search.go

* fix an html error

* Add files via upload

* Update admin.jet.html

* Update index.go

* Update index.go

* Update router.go

* Update torrentParam_test.go

* Update torrentParam_test.go

* fix extra "

* fix bad copypaste

* Update CHANGELOG.md

* Update guidelines.jet.html

* Update CHANGELOG.md

* add Guidelines and Moderation Guidelines

* fix missing commas

* Update torrentlist.jet.html

* Update find.go

* Update stats.go

* Update view.jet.html

* Update index.jet.html
Cette révision appartient à :
kilo 2017-11-14 09:39:39 +01:00 révisé par GitHub
Parent ca2f5d3c42
révision 5dcd30676f
Signature inconnue de Forgejo
ID de la clé GPG: 4AEE18F83AFDEB23
45 fichiers modifiés avec 464 ajouts et 227 suppressions

Voir le fichier

@ -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)
}

Voir le fichier

@ -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)

Voir le fichier

@ -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
}

Voir le fichier

@ -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()

Voir le fichier

@ -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")

Voir le fichier

@ -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)

Voir le fichier

@ -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")
}

Voir le fichier

@ -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)

Voir le fichier

@ -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

Voir le fichier

@ -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

Voir le fichier

@ -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)
}

Voir le fichier

@ -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)

Voir le fichier

@ -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
}

Voir le fichier

@ -45,16 +45,16 @@ func GetStatsHandler(c *gin.Context) {
if len(torrent.Trackers) > 3 {
for _, line := range strings.Split(torrent.Trackers[3:], "&tr=") {
tracker, error := url.QueryUnescape(line)
if error == nil && strings.Contains(tracker, "udp://") {
if error == nil && strings.HasPrefix(tracker, "udp") {
Trackers = append(Trackers, tracker)
}
//Cannot scrape from http trackers so don't put them in the array
}
}
for _, line := range config.Get().Torrents.Trackers.Default {
if !contains(Trackers, line) {
Trackers = append(Trackers, line)
for _, tracker := range config.Get().Torrents.Trackers.Default {
if !contains(Trackers, tracker) && strings.HasPrefix(tracker, "udp") {
Trackers = append(Trackers, tracker)
}
}

Voir le fichier

@ -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)
}

Voir le fichier

@ -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)

Voir le fichier

@ -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

Voir le fichier

@ -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)

Voir le fichier

@ -312,17 +312,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{})
}

Voir le fichier

@ -156,7 +156,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 != "" {
@ -193,6 +193,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)

Voir le fichier

@ -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"
}

Voir le fichier

@ -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

Voir le fichier

@ -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,7 @@ html, body {
}
.comment-form textarea {
margin-bottom: 0;
margin-bottom: 0;
}
.CodeMirror {
@ -1817,7 +1839,6 @@ input.filelist-checkbox:checked+table.table-filelist {
}
[class^="btn-"] {
font-weight: bold;
color: white;
}
@ -1876,7 +1897,7 @@ summary:after {
width: 12px;
font-size: 1.5em;
font-weight: bold;
outline: none;
outline: none;
}
details[open] summary:after {
@ -2124,32 +2145,32 @@ p.upload-rules a {
}
.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;
@ -2339,7 +2360,6 @@ table.multiple-upload {
}
.profile-panel .user-search {
padding: 0;
max-width: 170px;
}
.user-search [type="text"] {
@ -2423,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;
}

Voir le fichier

@ -0,0 +1,8 @@
{{ extends "layouts/index_admin" }}
{{block title()}}{{ T("moderation_guidelines") }}{{end}}
{{ block content_body()}}
<div class="results box">
<h1>{{ T("moderation_guidelines") }}</h1>
<img src="https://www.themarysue.com/wp-content/uploads/2016/09/harassment-guide.png">
</div>
{{end}}

Voir le fichier

@ -14,19 +14,26 @@
</thead>
<tbody>
{{range Torrents}}
<tr>
<tr {{ if .IsBlocked() }}class="locked"{{end}}>
<td class="tr-name home-td">
<a href="/view/{{.ID }}">{{ .Name }}</a>
<a href="/mod/torrent?id={{.ID}}" class="form-input btn-blue float-right">{{ T("edit") }}</a>
</td>
<td class="tr-size home-td">
<a href="/mod/torrents?userID={{.UploaderID}}">{{ .UploaderID }}</a>
<a href="/mod/torrents?userID={{.UploaderID}}">{{ if .Uploader }}{{.Uploader.Username }}{{else}}れんちょん{{end}}</a>
</td>
<td class="tr-size home-td">
<form method="POST" action="/mod/torrent/delete">
<input type="hidden" name="id" value="{{ .ID }}">
<button type="submit" class="form-input btn-red" onclick="if (!confirm('{{ T("are_you_sure") }}')) return false;"><i class="icon-trash"></i> {{ T("delete") }}</button>
</form>
{{ if User.IsModerator() }}
<form method="POST" action="/mod/torrent/delete" class="delete-form">
<input type="hidden" name="id" value="{{ .ID }}">
{{ if .IsDeleted() }}<input type="hidden" name="definitely" value="true">{{ end }}
<button type="submit" class="form-input btn-red" onclick="if (!confirm('{{ T("are_you_sure") }} {{ if !.IsDeleted() }}{{ T("delete") }}{{else}}{{ T("delete_definitely_torrent_warning ")}}{{end}}')) return false;"><i class="icon-trash"></i>{{ if .IsDeleted() }}{{ T("delete_definitely") }}{{else}}{{ T("delete") }}{{end}}</button>
</form>
{{end}}
<form method="POST" action="/mod/torrent/block" class="delete-form">
<input type="hidden" name="id" value="{{ .ID }}">
<button type="submit" class="form-input btn-orange" onclick="if (!confirm('{{ T("are_you_sure") }}')) return false;"><i class="icon-trash"></i>{{ if .IsBlocked() }}{{ T("torrent_unblock") }}{{else}}{{ T("torrent_block") }}{{end}}</button>
</form>
</td>
</tr>
{{end}}
@ -88,10 +95,16 @@
<a href="/user/{{.ID}}/{{.Username }}">{{ .Username }}</a>
</td>
<td class="tr-size home-td">{{if .ID > 0}}
<form method="POST" action="/user/{{.ID}}/{{.Username }}/delete" >
{{ yield csrf_field()}}
<button type="submit" class="form-input btn-red" onclick="if (!confirm('{{ T("are_you_sure") }}')) return false;"><i class="icon-trash"></i> {{ T("delete") }}</button>
</form>
{{ if User.IsModerator() }}
<form method="POST" action="/user/{{.ID}}/{{.Username }}/delete" class="delete-form">
{{ yield csrf_field()}}
<button type="submit" class="form-input btn-red" onclick="if (!confirm('{{ T("are_you_sure") }}')) return false;"><i class="icon-trash"></i> {{ T("delete") }}</button>
</form>
{{end}}
<form method="POST" action="/user/{{.ID}}/{{.Username }}/ban" class="delete-form">
{{ yield csrf_field()}}
<button type="submit" class="form-input btn-orange" onclick="if (!confirm('{{ T("are_you_sure") }}')) return false;"><i class="icon-trash"></i>{{ if .IsBanned() }}{{ T("unban") }}{{else}}{{ T("ban") }}{{end}}</button>
</form>
{{end}}
</td>
</tr>

Voir le fichier

@ -38,12 +38,12 @@
</thead>
<tbody>
{{ range Models}}
<tr>
<tr {{ if .Status == 5}}class="locked hidden"{{end}}>
<td class="tr-cb">
<input type="checkbox" class="selectable" name="torrent_id" value="{{.ID }}" />
</td>
<td class="tr-name home-td">
<a href="/view/{{ .ID }}">{{ .Name }}</a> {{ if !.IsDeleted }}
<a href="/view/{{ .ID }}">{{ .Name }}</a> {{ if User.IsModerator() }}
<a href="/mod/torrent?id={{.ID}}" class="form-input btn-blue float-right">
{{ T("edit")}}
</a> {{end}}
@ -57,14 +57,15 @@
<td class="tr-actions home-td"> <form></form>
<form method="POST" action="/mod/torrent/block" class="delete-form">
<input type="hidden" name="id" value="{{ .ID }}">
<button type="submit" class="form-input btn-orange" onclick="if (!confirm('{{ T("are_you_sure") }}')) return false;"><i class="icon-trash"></i>{{ if .IsBlocked }}{{ T("torrent_unblock") }}{{else}}{{ T("torrent_block") }}{{end}}</button>
<button type="submit" class="form-input btn-orange" {{ if !User.IsModerator() }}onclick="if (!confirm('{{ T("are_you_sure") }}')) return false;"{{end}}><i class="icon-trash"></i>{{ if .IsBlocked() }}{{ T("torrent_unblock") }}{{else}}{{ T("torrent_block") }}{{end}}</button>
</form>
{{ if User.IsModerator() }}
<form method="POST" action="/mod/torrent/delete" class="delete-form">
<input type="hidden" name="id" value="{{ .ID }}">
{{ if .IsDeleted }}<input type="hidden" name="definitely" value="true">{{ end }}
<button type="submit" class="form-input btn-red" onclick="if (!confirm('{{ T("are_you_sure") }} {{ if !.IsDeleted }}{{ T("delete") }}{{else}}{{ T("delete_definitely_torrent_warning ")}}{{end}}')) return false;"><i class="icon-trash"></i>{{ if .IsDeleted }}{{ T("delete_definitely") }}{{else}}{{ T("delete") }}{{end}}</button>
{{ if .IsDeleted() }}<input type="hidden" name="definitely" value="true">{{ end }}
<button type="submit" class="form-input btn-red" onclick="if (!confirm('{{ T("are_you_sure") }} {{ if !.IsDeleted() }}{{ T("delete") }}{{else}}{{ T("delete_definitely_torrent_warning ")}}{{end}}')) return false;"><i class="icon-trash"></i>{{ if .IsDeleted() }}{{ T("delete_definitely") }}{{else}}{{ T("delete") }}{{end}}</button>
</form>
{{end}}
</td>
</tr>
{{end}}

Voir le fichier

@ -13,7 +13,7 @@
</thead>
<tbody>
{{ range Models}}
<tr>
<tr {{ if .IsBanned() }}class="locked hidden"{{end}}>
<td class="tr-name home-td">
<a href="/user/{{.ID}}/{{.Username }}">{{ .Username }}</a>
</td>
@ -21,7 +21,10 @@
{{if .ID > 0}}
<form method="POST" action="/user/{{.ID}}/{{.Username }}/delete">
{{ yield csrf_field()}}
{{ if User.IsModerator() }}
<button type="submit" class="form-input btn-red" onclick="if (!confirm('{{ T("are_you_sure") }}')) return false;"><i class="icon-trash"></i> {{ T("delete") }}</button>
{{end}}
<button type="submit" class="form-input btn-orange" onclick="if (!confirm('{{ T("are_you_sure") }}')) return false;"><i class="icon-trash"></i> {{ if !.IsBanned() }}{{ T("ban") }}{{else}}{{ T("unban") }}{{end}}</button>
</form>
{{end}}
</td>

Voir le fichier

@ -7,7 +7,7 @@
<a href="{{magnet}}" class="form-input btn-green download" style="height: auto;">
<div class="icon-magnet"></div>{{ T("magnet_link")}}
</a>
{{ if !isset(Errors[name])}}<br/><p>{{ T("pending_torrent") }}</p>{{end}}<br/><br/>
{{ if !isset(Errors["errors"])}}<br/><p>{{ T("pending_torrent") }}</p>{{end}}<br/><br/>
{{ end }}
<img src="/img/no_torrent_file.jpg" alt="No torrent file"/>
</div>

Voir le fichier

@ -1,18 +1,18 @@
{{ import "csrf" }}
{{block badge_user()}}
<div class="h-user">
<div class="h-user {{ if User.IsBanned() }}hidden{{end}}">
{{if User.ID > 0 }}
<button href="#" class="nav-btn">
<div class="user-avatar small">
<img src="{{getAvatar(User.MD5, 50)}}"/>
{{if User.GetUnreadNotifications() > 0}}<span>{{User.GetUnreadNotifications()}}</span>{{end}}
</div>
<span class="user-info" title="{{ User.Username}}">
<span class="user-info" title="{{ User.Username }}">
<span class="hide-md">{{User.Username}}</span>
</span>
</button>
<div class="user-menu">
<a class="nav-btn" href="/user/{{ User.ID }}/{{ User.Username }}">{{ T("profile")}}</a>
<a class="nav-btn" href="/user/{{ User.ID }}/{{ User.Username }}">{{ T("profile")}}{{ if User.IsBanned() }} ({{T("banned")}}){{end}}</a>
<a class="nav-btn notif" href="/notifications">
{{ T("my_notifications")}}
{{if User.GetUnreadNotifications() > 0}}<span class="badge">({{ User.GetUnreadNotifications() }})</span>{{end}}
@ -20,7 +20,7 @@
<a class="nav-btn" href="/user/{{ User.ID }}/{{ User.Username }}/edit">
{{ T("settings")}}
</a>
{{if User.HasAdmin()}}
{{if User.IsJanitor()}}
<a class="nav-btn" href="/mod">{{ T("moderation")}}</a>
{{end}}
<form action="/logout" method="POST">

Voir le fichier

@ -13,7 +13,6 @@
{{ if _% 3 != 2}}{{ if _% 3 == 0}}<td></td>{{end}}<td></td>{{end}}
</tr>{{end}}
{{ end }}
</tr>
</tbody>
</table>
{{ if Search.Category != ""}}

Voir le fichier

@ -7,4 +7,5 @@
<a href="{{URL.Parse("/mod/announcement")}}" class="nav-btn{{if strcmp(URL.String(), "/mod/announcement", 17, 5) }} active{{end}}">{{ T("announcements")}}</a>
<a href="{{URL.Parse("/mod/reports")}}" class="nav-btn{{if URL.String() == "/mod/reports"}} active{{end}}">{{ T("torrent_reports")}}</a>
<a href="{{URL.Parse("/mod/reassign")}}" class="nav-btn{{if URL.String() == "/mod/reassign"}} active{{end}}">{{ T("torrent_reassign")}}</a>
<a href="{{URL.Parse("/mod/guidelines")}}" class="nav-btn{{if URL.String() == "/mod/guidelines"}} active{{end}}">{{ T("guidelines")}}</a>
</div>

Voir le fichier

@ -1,5 +1,6 @@
{{ import "layouts/partials/helpers/csrf" }}
{{ block profile_menu(route="profile") }}
<div class="profile-sidebar">
<div class="profile-sidebar {{ if UserProfile.IsBanned() }} hidden{{end}}">
<!-- SIDEBAR USERPIC -->
<div class="profile-userpic">
<img src="{{ getAvatar(UserProfile.MD5, 130) }}" alt="{{ UserProfile.Username }}"/>
@ -10,9 +11,11 @@
<p class="profile-usertitle-name">
{{ UserProfile.Username}}
</p>
{{ if UserProfile.GetRole() != "" }}
<p class="profile-usertitle-job">
{{T(UserProfile.GetRole())}}
</p>
{{end}}
<p class="profile-usertitle-uploadcount">{{T("followers")}}: <b>{{len(UserProfile.Followers)}}</b><br/>{{ T("torrents_uploaded") }}:<b>{{ NbTorrents[0] }}</b><br>{{T("size")}}: <b>{{fileSize(NbTorrents[1], T, false)}}</b></p>
</div>
<!-- END SIDEBAR USER TITLE -->
@ -43,12 +46,19 @@
{{ if User.CurrentUserIdentical(UserProfile.ID) }}
<a class="form-input" href="/notifications">{{ T("my_notifications")}}</a>
{{end}}
{{if UserProfile.ID > 0 && User.CurrentOrAdmin(UserProfile.ID) }}
<a class="form-input" href="/user/{{UserProfile.ID}}/{{UserProfile.Username}}/edit">
{{ T("settings")}}
</a>
{{end}}
{{if UserProfile.ID > 0}}
{{ if User.CurrentOrAdmin(UserProfile.ID) }}
<a class="form-input" href="/user/{{UserProfile.ID}}/{{UserProfile.Username}}/edit">
{{ T("settings")}}
</a>
{{else if User.IsJanitor() && !UserProfile.IsJanitor() }}
<form method="POST" action="/user/{{UserProfile.ID}}/{{UserProfile.Username}}/ban">
{{ yield csrf_field()}}
<button type="submit" class="form-input btn-blue" onclick="if (!confirm('{{ T("are_you_sure") }}')) return false;"><i class="icon-trash"></i>{{ if UserProfile.IsBanned() }}{{ T("unban") }}{{else}}{{ T("ban") }}{{end}}</button>
</form>
{{end}}
{{end}}
{{end}}
</div>
{{ if User.ID != UserProfile.ID }}
<div class="user-search">

Voir le fichier

@ -17,7 +17,7 @@ Templates.Add("torrents.item", function(torrent) {
} else if (torrent.status == 4) {
tr_class += " aplus"
}
// {{ if User.HasAdmin() }}
// {{ if User.IsModerator() }}
var cb_hide = (!TorrentsMod.enabled) ? " hide" : ""
var cb_show = (TorrentsMod.enabled) ? ' style="display:table-cell;"' : ""
// {{ end }}
@ -43,7 +43,7 @@ Templates.Add("torrents.item", function(torrent) {
}
return `<tr id="torrent_` + torrent.id + `" class="` + tr_class + `">
{{ if User.HasAdmin() }}
{{ if User.IsModerator() }}
<td class="tr-cb` + cb_hide + `"` + cb_show + `>
<input data-name="` + Templates.EncodeEntities(torrent.name) + `" type="checkbox" id="torrent_cb_` + torrent.id + `" name="torrent_id" value="` + torrent.id + `">
</td>

Voir le fichier

@ -1,7 +1,7 @@
{{ extends "layouts/index_site" }}
{{ import "layouts/partials/helpers/search" }}
{{block title()}}{{if Search.UserName == ""}}{{ T("home")}}{{else}}{{Search.UserName}}{{end}}{{end}}
{{block contclass()}}{{if User.HasAdmin() }}content-admin{{end}}{{end}}
{{block title()}}{{if Search.UserName != ""}}{{Search.UserName}}{{else if Search.NameLike != ""}}{{Search.NameLike}}{{else if Search.Category != ""}}{{T(GetCategoryName(Search.Category))}}{{else}}{{ T("home")}}{{end}}{{end}}
{{block contclass()}}{{if User.IsModerator() }}content-admin{{end}}{{end}}
{{block content_body()}}
{{ if OldNav || Theme == "classic"}}
{{ include "layouts/partials/helpers/oldNav" }}
@ -11,7 +11,7 @@
<table>
<thead class="torrent-info">
<tr>
{{ if User.HasAdmin() }}
{{ if User.IsModerator() }}
<th class="tr-cb hide">
<input type="checkbox" name="select_all" onchange="TorrentsMod.selectAll(this.checked)"/>
</th>
@ -55,8 +55,8 @@
</thead>
<tbody id="torrentListResults" {{if AltColors}}class="alt-colors"{{end}}>
{{ range Models}}
<tr id="torrent_{{ .ID }}" class="torrent-info {{if .Status == 2}}remake{{else if .Status == 3}}trusted{{else if .Status == 4}}aplus{{end}}" >
{{ if User.HasAdmin() }}
<tr id="torrent_{{ .ID }}" class="torrent-info {{if .Status == 2}}remake{{else if .Status == 3}}trusted{{else if .Status == 4}}aplus{{else if .Status == 5}}locked hidden{{end}}">
{{ if User.IsModerator() }}
<td class="tr-cb hide">
<input data-name="{{ .Name }}" type="checkbox" id="torrent_cb_{{ .ID }}" name="torrent_id" value="{{ .ID }}"/>
</td>
@ -108,7 +108,7 @@
</tbody>
</table>
</div>
{{ if User.HasAdmin() }}
{{ if User.IsModerator() }}
<div class="modtools">
<button id="show_actions" class="form-input" data-toggle-text="{{ T("hide_mod_tools")}}">{{ T("show_mod_tools")}}</button>
<span class="actions">
@ -175,7 +175,7 @@
<script type="text/javascript" src="{{ URL.Parse("/js/modal.js") }}"></script>
<script type="text/javascript" src="{{ URL.Parse("/js/torrents.js") }}"></script>
<script type="text/javascript" src="{{ URL.Parse("/js/translation.js") }}"></script>
{{ if User.HasAdmin() }}
{{ if User.IsModerator() }}
<script type="text/javascript" src="{{ URL.Parse("/js/torrentsMod.js") }}"></script>
<script type="text/javascript">
// We add translations string

Voir le fichier

@ -163,7 +163,7 @@
</a>
<a id="reportPopup" href="/report/{{Torrent.ID}}" class="form-input">{{ T("report_btn") }}</a>
{{ if User.ID > 0}}
{{ if User.HasAdmin()}}
{{ if User.IsModerator()}}
<form method="POST" action="/mod/torrent/delete" class="delete-form">
{{ yield csrf_field()}}
<input type="hidden" name="id" value="{{ Torrent.ID }}">
@ -212,23 +212,23 @@
{{idx := 1}}
{{previousComment := ""}}
{{previousUser := 0}}
{{range index, element := Torrent.Comments}}
{{if previousComment != element.Content || previousUser != element.UserID || element.UserID == 0}}
{{range _, comment := Torrent.Comments}}
{{if previousComment != comment.Content || previousUser != comment.UserID || comment.UserID == 0}}
<div class="torrent-info-box comment-box">
<span class="comment-index">
<a href="#comment_{{idx}}">{{idx}}</a>
<small style="padding-left: 4px;" class="date-full">{{formatDate(element.Date, false)}}</small>
<small style="padding-left: 4px;" class="date-full">{{formatDate(comment.Date, false)}}</small>
</span>
<span class="comment-userinfo"><img src="{{ getAvatar(element.UserAvatar, 50) }}"/>
{{if element.UserID > 0}}<a href="/user/{{element.UserID}}/{{element.Username}}" class="comment-user">{{element.Username}}</a>{{if element.UserStatus != ""}}<span class="user-status">{{T(element.UserStatus)}}</span>{{end}}{{else}}
<span class="comment-user">れんちょん</span>{{end}}
<span class="comment-userinfo"><img src="{{ getAvatar(comment.UserAvatar, 50) }}"/>
{{if comment.UserID > 0}}<a href="/user/{{comment.UserID}}/{{comment.Username}}" class="comment-user">{{comment.Username}}</a>{{if comment.UserStatus != ""}}<span class="user-status">{{T(comment.UserStatus)}}</span>{{end}}{{else}}
<span class="comment-user">{{ if comment.Username != ""}}{{comment.Username}}{{else}}れんちょん{{end}}</span>{{end}}
</span>
<div class="comment-content">{{element.Content|raw}}</div>
<div class="comment-content">{{comment.Content|raw}}</div>
</div>
{{idx = idx + 1}}
{{end}}
{{previousComment = element.Content}}
{{previousUser = element.UserID}}
{{previousComment = comment.Content}}
{{previousUser = comment.UserID}}
{{end}}
{{ if len(Torrent.Comments) == 0 }}
<p id="no-comment-message">{{ T("torrent_no_comments") }}</p>

Voir le fichier

@ -33,7 +33,7 @@
</select>
</td>
</tr>
{{ if !User.HasAdmin()}}
{{ if !User.IsModerator()}}
<tr>
<td><label for="current_password">{{ T("current_password") }}</label></td>
<td><input class="form-input up-input up-input" name="current_password" id="current_password" type="password"/></td>
@ -51,7 +51,7 @@
</table>
{{ 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()}}
<h2>{{ T("moderation")}}</h2>
<table class="user-edit-table">
<tbody>
@ -243,8 +243,9 @@
<option value="-1" {{ if UserProfile.Status == -1 }}selected{{end}}>{{ T("userstatus_banned")}}</option>
<option value="0" {{ if UserProfile.Status == 0 }}selected{{end}}>{{ T("userstatus_member")}} ({{ T("default") }})</option>
<option value="1" {{ if UserProfile.Status == 1 }}selected{{end}}>{{ T("userstatus_trusted")}}</option>
{{ if UserProfile.Status == 2}}
<option value="2" selected>{{ T("userstatus_moderator")}}</option>
{{ if User.Status == 2}}
<option value="4" {{ if UserProfile.Status == 4 }}selected{{end}}>{{ T("userstatus_janitor")}}</option>
<option value="2" {{ if UserProfile.Status == 2 }}selected{{end}}>{{ T("userstatus_moderator")}}</option>
{{end}}
<option value="3" {{ if UserProfile.Status == 3 }}selected{{end}}>{{ T("userstatus_scraped")}}</option>
</select>

Voir le fichier

@ -168,10 +168,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

Voir le fichier

@ -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, ", ")

Voir le fichier

@ -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

Voir le fichier

@ -91,3 +91,15 @@
## 2017/11/04
* + nsfw_content
* + generating_torrent_failed
## 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

Voir le fichier

@ -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"
@ -883,6 +887,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"
@ -976,9 +984,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"
@ -1099,6 +1119,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!"
@ -2250,5 +2278,13 @@
{
"id": "unread",
"translation": "Unread"
},
{
"id": "moderation_guidelines",
"translation": "Moderation Guidelines"
},
{
"id": "guidelines",
"translation": "Guidelines"
}
]

Voir le fichier

@ -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)
}

Voir le fichier

@ -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)
}

Voir le fichier

@ -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 {

Voir le fichier

@ -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