Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0

Added multi action on torrents

Cette révision appartient à :
akuma06 2017-05-20 13:45:15 +02:00
Parent a511b901e9
révision 0d5e2abf7f
12 fichiers modifiés avec 284 ajouts et 33 suppressions

8
config/torrents.go Fichier normal
Voir le fichier

@ -0,0 +1,8 @@
package config
var TorrentStatus = map[int]bool{
0:true,
1:true,
2:true,
3:true,
}

Voir le fichier

@ -37,7 +37,7 @@ type Torrent struct {
WebsiteLink string `gorm:"column:website_link"`
DeletedAt *time.Time
Uploader *User `gorm:"ForeignKey:uploader"`
Uploader *User `gorm:"AssociationForeignKey:UploaderID;ForeignKey:user_id"`
OldUploader string `gorm:"-"` // ???????
OldComments []OldComment `gorm:"ForeignKey:torrent_id"`
Comments []Comment `gorm:"ForeignKey:torrent_id"`

4
public/js/selectAll.js Fichier normal
Voir le fichier

@ -0,0 +1,4 @@
$("[data-selectall='checkbox']").on("change", function(e) {
var form = $(this).parents("form");
$(form).find("input[type='checkbox']").prop('checked', $(this).prop('checked'));
});

Voir le fichier

@ -7,6 +7,7 @@ import (
"strconv"
"strings"
"github.com/NyaaPantsu/nyaa/config"
"github.com/NyaaPantsu/nyaa/db"
"github.com/NyaaPantsu/nyaa/model"
"github.com/NyaaPantsu/nyaa/service"
@ -18,6 +19,7 @@ import (
"github.com/NyaaPantsu/nyaa/service/user/permission"
"github.com/NyaaPantsu/nyaa/util/languages"
"github.com/NyaaPantsu/nyaa/util/log"
msg "github.com/NyaaPantsu/nyaa/util/messages"
"github.com/NyaaPantsu/nyaa/util/search"
"github.com/gorilla/mux"
)
@ -140,17 +142,18 @@ func TorrentsListPanel(w http.ResponseWriter, r *http.Request) {
return
}
}
offset := 100
searchParam, torrents, _, err := search.SearchByQuery(r, pagenum)
searchParam, torrents, count, err := search.SearchByQueryWithUser(r, pagenum)
searchForm := SearchForm{
SearchParam: searchParam,
Category: searchParam.Category.String(),
ShowItemsPerPage: true,
}
messages := msg.GetMessages(r)
languages.SetTranslationFromRequest(panelTorrentList, r)
htv := PanelTorrentListVbs{torrents, searchForm, Navigation{int(searchParam.Max), offset, pagenum, "mod_tlist_page"}, currentUser, r.URL}
htv := PanelTorrentListVbs{torrents, searchForm, Navigation{ count, int(searchParam.Max), pagenum, "mod_tlist_page"}, currentUser, messages.GetAllErrors(), messages.GetAllInfos(), r.URL}
err = panelTorrentList.ExecuteTemplate(w, "admin_index.html", htv)
log.CheckError(err)
} else {
@ -396,3 +399,66 @@ func TorrentPostReassignModPanel(w http.ResponseWriter, r *http.Request) {
err_ := panelTorrentReassign.ExecuteTemplate(w, "admin_index.html", htv)
log.CheckError(err_)
}
func TorrentsPostListPanel(w http.ResponseWriter, r *http.Request) {
currentUser := GetUser(r)
if !userPermission.HasAdmin(currentUser) {
http.Error(w, "admins only", http.StatusForbidden)
return
}
torrentManyAction(r)
TorrentsListPanel(w, r)
}
/*
* Controller to modify multiple torrents and can be used by the owner of the torrent or admin
*/
func torrentManyAction(r *http.Request) {
currentUser := GetUser(r)
r.ParseForm()
torrentsSelected := r.Form["torrent_id"] // should be []string
action := r.FormValue("action")
moveTo, _ := strconv.Atoi(r.FormValue("moveto"))
messages := msg.GetMessages(r) // new util for errors and infos
if action == "" {
messages.AddError(r, "errors", "You have to tell what you want to do with your selection!")
}
if action == "move" && r.FormValue("moveto") == "" { // We need to check the form value, not the int one because hidden is 0
messages.AddError(r, "errors", "Thou has't to telleth whither thee wanteth to moveth thy selection!")
}
if len(torrentsSelected) == 0 {
messages.AddError(r, "errors", "You need to select at least 1 element!")
}
if !messages.HasErrors() {
for _, torrent_id := range torrentsSelected {
torrent, _ := torrentService.GetTorrentById(torrent_id)
if torrent.ID > 0 && userPermission.CurrentOrAdmin(currentUser, torrent.UploaderID) {
switch action {
case "move":
if config.TorrentStatus[moveTo] {
torrent.Status = moveTo
db.ORM.Save(&torrent)
messages.AddInfof(r, "infos", "Torrent %s moved!", torrent.Name)
} else {
messages.AddErrorf(r, "errors", "No such status %d exist!", moveTo)
}
case "delete":
_, err := torrentService.DeleteTorrent(torrent_id)
if err != nil {
messages.ImportFromError(r, "errors", err)
} else {
messages.AddInfof(r, "infos", "Torrent %s deleted!", torrent.Name)
}
default:
messages.AddErrorf(r, "errors", "No such action %s exist!", action)
}
} else {
messages.AddErrorf(r, "errors", "Torrent with ID %s doesn't exist!", torrent_id)
}
}
}
}

Voir le fichier

@ -99,8 +99,10 @@ func init() {
Router.Handle("/user/{id}/{username}/edit", wrapHandler(gzipUserProfileFormHandler)).Name("user_profile_edit").Methods("POST")
Router.HandleFunc("/mod", IndexModPanel).Name("mod_index")
Router.HandleFunc("/mod/torrents", TorrentsListPanel).Name("mod_tlist")
Router.HandleFunc("/mod/torrents/{page}", TorrentsListPanel).Name("mod_tlist_page")
Router.HandleFunc("/mod/torrents", TorrentsListPanel).Name("mod_tlist").Methods("GET")
Router.HandleFunc("/mod/torrents", TorrentsPostListPanel).Methods("POST")
Router.HandleFunc("/mod/torrents/{page}", TorrentsListPanel).Name("mod_tlist_page").Methods("GET")
Router.HandleFunc("/mod/torrents/{page}", TorrentsPostListPanel).Methods("POST")
Router.HandleFunc("/mod/reports", TorrentReportListPanel).Name("mod_trlist")
Router.HandleFunc("/mod/reports/{page}", TorrentReportListPanel).Name("mod_trlist_page")
Router.HandleFunc("/mod/users", UsersListPanel).Name("mod_ulist")

Voir le fichier

@ -150,6 +150,8 @@ type PanelTorrentListVbs struct {
Search SearchForm
Navigation Navigation
User *model.User
Errors map[string][]string
Infos map[string][]string
URL *url.URL // For parsing Url in templates
}
type PanelUserListVbs struct {

Voir le fichier

@ -11,6 +11,7 @@ import (
"github.com/NyaaPantsu/nyaa/model"
"github.com/NyaaPantsu/nyaa/service"
"github.com/NyaaPantsu/nyaa/util"
// "github.com/NyaaPantsu/nyaa/util/log"
)
/* Function to interact with Models
@ -91,23 +92,28 @@ func GetTorrentById(id string) (torrent model.Torrent, err error) {
// won't fetch user or comments
func GetRawTorrentById(id uint) (torrent model.Torrent, err error) {
err = nil
if db.ORM.Table(config.TableName).Table(config.TableName).Where("torrent_id = ?", id).Find(&torrent).RecordNotFound() {
if db.ORM.Table(config.TableName).Where("torrent_id = ?", id).Find(&torrent).RecordNotFound() {
err = errors.New("Article is not found.")
}
return
}
func GetTorrentsOrderByNoCount(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, err error) {
torrents, _, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, false)
torrents, _, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, false, false)
return
}
func GetTorrentsOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, count int, err error) {
torrents, count, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, true)
torrents, count, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, true, false)
return
}
func getTorrentsOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int, countAll bool) (
func GetTorrentsWithUserOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, count int, err error) {
torrents, count, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, true, true)
return
}
func getTorrentsOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int, countAll bool, withUser bool) (
torrents []model.Torrent, count int, err error,
) {
var conditionArray []string
@ -146,7 +152,14 @@ func getTorrentsOrderBy(parameters *serviceBase.WhereParams, orderBy string, lim
if limit != 0 || offset != 0 { // if limits provided
dbQuery = dbQuery + " LIMIT " + strconv.Itoa(limit) + " OFFSET " + strconv.Itoa(offset)
}
err = db.ORM.Preload("Comments").Raw(dbQuery, params...).Find(&torrents).Error
dbQ := db.ORM
if withUser {
dbQ = dbQ.Preload("Uploader")
}
if countAll {
dbQ = dbQ.Preload("Comments")
}
err = dbQ.Raw(dbQuery, params...).Find(&torrents).Error
return
}

Voir le fichier

@ -7,6 +7,7 @@ import (
formStruct "github.com/NyaaPantsu/nyaa/service/user/form"
"github.com/NyaaPantsu/nyaa/util/modelHelper"
"github.com/NyaaPantsu/nyaa/util/timeHelper"
"github.com/gorilla/context"
"github.com/gorilla/securecookie"
"golang.org/x/crypto/bcrypt"
"net/http"
@ -14,7 +15,10 @@ import (
"time"
)
const CookieName = "session"
const (
CookieName = "session"
UserContextKey = "user"
)
// If you want to keep login cookies between restarts you need to make these permanent
var cookieHandler = securecookie.New(
@ -113,7 +117,7 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) (int, error) {
return RegisterHanderFromForm(w, registrationForm)
}
// CurrentUser determines the current user from the request
// CurrentUser determines the current user from the request or context
func CurrentUser(r *http.Request) (model.User, error) {
var user model.User
var encoded string
@ -131,8 +135,17 @@ func CurrentUser(r *http.Request) (model.User, error) {
if err != nil {
return user, err
}
if db.ORM.Where("user_id = ?", user_id).First(&user).RecordNotFound() {
return user, errors.New("User not found")
userFromContext := getUserFromContext(r)
if userFromContext.ID > 0 && user_id == userFromContext.ID {
user = userFromContext
} else {
if db.ORM.Where("user_id = ?", user_id).First(&user).RecordNotFound() {
return user, errors.New("User not found")
} else {
setUserToContext(r, user)
}
}
if user.Status == -1 {
@ -141,3 +154,13 @@ func CurrentUser(r *http.Request) (model.User, error) {
}
return user, nil
}
func getUserFromContext(r *http.Request) model.User {
if rv := context.Get(r, UserContextKey); rv != nil {
return rv.(model.User)
}
return model.User{}
}
func setUserToContext(r *http.Request, val model.User) {
context.Set(r, UserContextKey, val)
}

Voir le fichier

@ -2,21 +2,54 @@
{{define "contclass"}}cont-view{{end}}
{{define "content"}}
<div class="blockBody">
<table class="table">
<tr>
<th class="col-xs-10">Torrent Name</th>
<th class="col-xs-1">Uploader</th>
<th class="col-xs-1">Action</th>
</tr>
<form method="post" action="">
{{ range (index $.Infos "infos")}}
<div class="alert alert-info"><a class="panel-close close" data-dismiss="alert">×</a><i class="glyphicon glyphicon-info-sign"></i> {{ . }}</div>
{{end}}
{{ range (index $.Errors "errors")}}
<div class="alert alert-danger"><a class="panel-close close" data-dismiss="alert">×</a><i class="glyphicon glyphicon-exclamation-sign"></i> {{ . }}</div>
{{end}}
<nav class="navbar navbar-default">
<div class="navbar-form">
<div class="form-group">
<select class="form-control" name="action">
<option value="">Action...</option>
<option value="move">Move</option>
<option value="delete">Delete</option>
</select>
<select class="form-control" style="display: none;" name="moveto">
<option value="">To...</option>
<option value="0">{{T "torrent_status_hidden"}}</option>
<option value="1">{{T "torrent_status_normal"}}</option>
<option value="2">{{T "torrent_status_remake"}}</option>
<option value="3">{{T "trusted"}}</option>
<option value="4">A+</option>
</select>
</div>
<input type="submit" class="btn btn-success" value="Apply">
<input type="reset" class="btn btn-danger" value="Reset">
</div>
</nav>
<div class="table-responsive">
<table class="table table-hover">
<tr>
<th style="width:12px;"><input type="checkbox" name="checkall" data-selectall="checkbox"></th>
<th>Torrent Name</th>
<th>Uploader</th>
<th>Action</th>
</tr>
{{ range .Torrents}}
<tr>
<td><a href="{{ genViewTorrentRoute .ID }}">{{ .Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">Edit</a>)</td>
<td><a href="{{ genRoute "mod_tlist" }}?userID={{.UploaderID}}">{{ .UploaderID }}</a></td>
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i> {{ T "delete" }}</a></td>
</tr>
{{end}}
</table>
{{ range .Torrents}}
<tr>
<td><input type="checkbox" name="torrent_id" value="{{.ID }}"></td>
<td><a href="{{ genViewTorrentRoute .ID }}">{{ .Name }}</a> (<a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">Edit</a>)</td>
<td>{{ if gt .UploaderID 0 }}<a href="{{ genRoute "mod_tlist" }}?userID={{.UploaderID}}">{{ .Uploader.Username }}</a>{{ else }}れんちょん{{end}}</td>
<td><a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}" class="btn btn-danger" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i> {{ T "delete" }}</a></td>
</tr>
{{end}}
</table>
</div>
</form>
<nav class="torrentNav" aria-label="Page navigation">
<ul class="pagination">
{{ genNav .Navigation .URL 5 }}
@ -24,3 +57,15 @@
</nav>
</div>
{{end}}
{{ define "js_footer"}}
<!-- JS Function for selecting multiple checkboxes -->
<script type="text/javascript" src="{{ .URL.Parse "/js/selectAll.js" }}"></script>
<script type="text/javascript">
$("select[name='action']").on("change", function(e) {
if ($(this).val() == "move")
$(this).siblings("select[name='moveto']").show()
else
$(this).siblings("select[name='moveto']").hide()
});
</script>
{{end}}

Voir le fichier

@ -71,6 +71,7 @@
{{block "search_button" .}}{{end}}
</div>
</form>
<div style="clear: both;"></div>
{{block "content" .}}{{end}}
</div>
<footer style="text-align: center; padding-bottom: 2rem;font-size: 2rem;font-family: cursive; color: #616161;text-shadow: -1px -1px #999999;">

80
util/messages/messages.go Fichier normal
Voir le fichier

@ -0,0 +1,80 @@
package Messages
import (
"github.com/gorilla/context"
"fmt"
"net/http"
)
const MessagesKey = "messages"
type Messages struct {
Errors map[string][]string
Infos map[string][]string
}
func GetMessages(r *http.Request) Messages {
if rv := context.Get(r, MessagesKey); rv != nil {
return rv.(Messages)
} else {
context.Set(r, MessagesKey, Messages{})
return Messages{make(map[string][]string),make(map[string][]string)}
}
}
func (mes Messages) AddError(r *http.Request, name string, msg string) {
mes.Errors[name] = append(mes.Errors[name], msg)
mes.setMessagesInContext(r)
}
func (mes Messages) ImportFromError(r *http.Request, name string, err error) {
mes.AddError(r, name, err.Error())
}
func (mes Messages) AddInfo(r *http.Request, name string, msg string) {
mes.Infos[name] = append(mes.Infos[name], msg)
mes.setMessagesInContext(r)
}
func (mes Messages) AddErrorf(r *http.Request, name string, msg string, args ...interface{}) {
mes.Errors[name] = append(mes.Errors[name], fmt.Sprintf(msg, args...))
mes.setMessagesInContext(r)
}
func (mes Messages) AddInfof(r *http.Request, name string, msg string, args ...interface{}) {
mes.Infos[name] = append(mes.Infos[name], fmt.Sprintf(msg, args...))
mes.setMessagesInContext(r)
}
func (mes Messages) ClearInfos(r *http.Request) {
mes.Errors = nil
mes.setMessagesInContext(r)
}
func (mes Messages) ClearErrors(r *http.Request) {
mes.Infos = nil
mes.setMessagesInContext(r)
}
func (mes Messages) GetInfos(name string) []string {
return mes.Infos[name]
}
func (mes Messages) GetErrors(name string) []string {
return mes.Errors[name]
}
func (mes Messages) GetAllInfos() map[string][]string {
return mes.Infos
}
func (mes Messages) GetAllErrors() map[string][]string {
return mes.Errors
}
func (mes Messages) setMessagesInContext(r *http.Request) {
context.Set(r, MessagesKey, mes)
}
func (mes Messages) HasErrors() bool {
return len(mes.Errors) > 0
}
func (mes Messages) HasInfos() bool {
return len(mes.Infos) > 0
}

Voir le fichier

@ -44,16 +44,21 @@ func stringIsAscii(input string) bool {
}
func SearchByQuery(r *http.Request, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
search, tor, count, err = searchByQuery(r, pagenum, true)
search, tor, count, err = searchByQuery(r, pagenum, true, false)
return
}
func SearchByQueryWithUser(r *http.Request, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
search, tor, count, err = searchByQuery(r, pagenum, true, true)
return
}
func SearchByQueryNoCount(r *http.Request, pagenum int) (search common.SearchParam, tor []model.Torrent, err error) {
search, tor, _, err = searchByQuery(r, pagenum, false)
search, tor, _, err = searchByQuery(r, pagenum, false, false)
return
}
func searchByQuery(r *http.Request, pagenum int, countAll bool) (
func searchByQuery(r *http.Request, pagenum int, countAll bool, withUser bool) (
search common.SearchParam, tor []model.Torrent, count int, err error,
) {
max, err := strconv.ParseUint(r.URL.Query().Get("max"), 10, 32)
@ -211,8 +216,10 @@ func searchByQuery(r *http.Request, pagenum int, countAll bool) (
tor, count, err = cache.Impl.Get(search, func() (tor []model.Torrent, count int, err error) {
if countAll {
if countAll && !withUser {
tor, count, err = torrentService.GetTorrentsOrderBy(&parameters, orderBy, int(search.Max), int(search.Max)*(search.Page-1))
} else if withUser {
tor, count, err = torrentService.GetTorrentsWithUserOrderBy(&parameters, orderBy, int(search.Max), int(search.Max)*(search.Page-1))
} else {
tor, err = torrentService.GetTorrentsOrderByNoCount(&parameters, orderBy, int(search.Max), int(search.Max)*(search.Page-1))
}