révision
b067e42b96
5
.gitignore
externe
Fichier normal → Fichier exécutable
5
.gitignore
externe
Fichier normal → Fichier exécutable
|
@ -1,4 +1,5 @@
|
|||
*.sqlite
|
||||
*.db-journal
|
||||
*.db
|
||||
*.sql
|
||||
main
|
||||
|
@ -14,3 +15,7 @@ templates/*.html.go
|
|||
*.backup
|
||||
tags
|
||||
*.retry
|
||||
|
||||
# emacs temp files
|
||||
*\#*
|
||||
*~
|
|
@ -30,7 +30,9 @@ func GormInit(conf *config.Config) (*gorm.DB, error) {
|
|||
// TODO: Enable Gorm initialization for non-development builds
|
||||
if config.Environment == "DEVELOPMENT" {
|
||||
db.LogMode(true)
|
||||
db.AutoMigrate(&model.Torrent{}, &model.UserFollows{}, &model.User{}, &model.Comment{}, &model.OldComment{}, &model.TorrentReport{})
|
||||
db.AutoMigrate(&model.User{}, &model.UserFollows{}, &model.UserUploadsOld{})
|
||||
db.AutoMigrate(&model.Torrent{}, &model.TorrentReport{})
|
||||
db.AutoMigrate(&model.Comment{}, &model.OldComment{})
|
||||
}
|
||||
|
||||
return db, nil
|
||||
|
|
10
main.go
10
main.go
|
@ -40,9 +40,17 @@ func RunServer(conf *config.Config) {
|
|||
l, err := network.CreateHTTPListener(conf)
|
||||
log.CheckError(err)
|
||||
if err == nil {
|
||||
// add http server to be closed gracefully
|
||||
signals.RegisterCloser(&network.GracefulHttpCloser{
|
||||
Server: srv,
|
||||
Listener: l,
|
||||
})
|
||||
log.Infof("listening on %s", l.Addr())
|
||||
err := srv.Serve(l)
|
||||
log.CheckError(err)
|
||||
if err != nil && err != network.ErrListenerStopped {
|
||||
log.CheckError(err)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
37
model/report.go
Fichier normal
37
model/report.go
Fichier normal
|
@ -0,0 +1,37 @@
|
|||
package model
|
||||
|
||||
// TODO Add field to specify kind of reports
|
||||
// TODO Add CreatedAt field
|
||||
// INFO User can be null (anonymous reports)
|
||||
// FIXME can't preload field Torrents for model.TorrentReport
|
||||
type TorrentReport struct {
|
||||
ID uint `gorm:"column:torrent_report_id;primary_key"`
|
||||
Description string `gorm:"column:type"`
|
||||
TorrentID uint `gorm:"column:torrent_id"`
|
||||
UserID uint `gorm:"column:user_id"`
|
||||
|
||||
Torrent Torrent `gorm:"AssociationForeignKey:TorrentID;ForeignKey:torrent_id"`
|
||||
User User `gorm:"AssociationForeignKey:UserID;ForeignKey:ID"`
|
||||
}
|
||||
|
||||
type TorrentReportJson struct {
|
||||
ID uint `json:"id"`
|
||||
Description string `json:"description"`
|
||||
Torrent TorrentJSON `json:"torrent"`
|
||||
User UserJSON `json:"user"`
|
||||
}
|
||||
|
||||
/* Model Conversion to Json */
|
||||
|
||||
func (report *TorrentReport) ToJson() TorrentReportJson {
|
||||
json := TorrentReportJson{report.ID, report.Description, report.Torrent.ToJSON(), report.User.ToJSON()}
|
||||
return json
|
||||
}
|
||||
|
||||
func TorrentReportsToJSON(reports []TorrentReport) []TorrentReportJson {
|
||||
json := make([]TorrentReportJson, len(reports))
|
||||
for i := range reports {
|
||||
json[i] = reports[i].ToJson()
|
||||
}
|
||||
return json
|
||||
}
|
|
@ -5,7 +5,6 @@ import (
|
|||
"github.com/ewhal/nyaa/util"
|
||||
|
||||
"fmt"
|
||||
"html"
|
||||
"html/template"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -36,12 +35,14 @@ type Torrent struct {
|
|||
WebsiteLink string `gorm:"column:website_link"`
|
||||
DeletedAt *time.Time
|
||||
|
||||
Uploader *User `gorm:"ForeignKey:UploaderId"`
|
||||
Uploader *User `gorm:"ForeignKey:uploader"`
|
||||
OldUploader string `gorm:"-"` // ???????
|
||||
OldComments []OldComment `gorm:"ForeignKey:torrent_id"`
|
||||
Comments []Comment `gorm:"ForeignKey:torrent_id"`
|
||||
}
|
||||
|
||||
// Returns the total size of memory recursively allocated for this struct
|
||||
// FIXME: doesn't go have sizeof or something nicer for this?
|
||||
func (t Torrent) Size() (s int) {
|
||||
s += 8 + // ints
|
||||
2*3 + // time.Time
|
||||
|
@ -66,19 +67,6 @@ func (t Torrent) Size() (s int) {
|
|||
|
||||
}
|
||||
|
||||
// TODO Add field to specify kind of reports
|
||||
// TODO Add CreatedAt field
|
||||
// INFO User can be null (anonymous reports)
|
||||
// FIXME can't preload field Torrents for model.TorrentReport
|
||||
type TorrentReport struct {
|
||||
ID uint `gorm:"column:torrent_report_id;primary_key"`
|
||||
Description string `gorm:"column:type"`
|
||||
TorrentID uint
|
||||
UserID uint
|
||||
Torrent Torrent `gorm:"AssociationForeignKey:TorrentID;ForeignKey:ID"`
|
||||
User User `gorm:"AssociationForeignKey:UserID;ForeignKey:ID"`
|
||||
}
|
||||
|
||||
/* We need a JSON object instead of a Gorm structure because magnet URLs are
|
||||
not in the database and have to be generated dynamically */
|
||||
|
||||
|
@ -108,32 +96,18 @@ type TorrentJSON struct {
|
|||
Downloads int `json:"downloads"`
|
||||
UploaderID uint `json:"uploader_id"`
|
||||
UploaderName template.HTML `json:"uploader_name"`
|
||||
OldUploader template.HTML `json:"uploader_old"`
|
||||
WebsiteLink template.URL `json:"website_link"`
|
||||
Magnet template.URL `json:"magnet"`
|
||||
TorrentLink template.URL `json:"torrent"`
|
||||
}
|
||||
|
||||
type TorrentReportJson struct {
|
||||
ID uint `json:"id"`
|
||||
Description string `json:"description"`
|
||||
Torrent TorrentJSON `json:"torrent"`
|
||||
User string
|
||||
}
|
||||
|
||||
/* Model Conversion to Json */
|
||||
|
||||
func (report *TorrentReport) ToJson() TorrentReportJson {
|
||||
json := TorrentReportJson{report.ID, report.Description, report.Torrent.ToJSON(), report.User.Username}
|
||||
return json
|
||||
}
|
||||
|
||||
// ToJSON converts a model.Torrent to its equivalent JSON structure
|
||||
func (t *Torrent) ToJSON() TorrentJSON {
|
||||
magnet := util.InfoHashToMagnet(strings.TrimSpace(t.Hash), t.Name, config.Trackers...)
|
||||
commentsJSON := make([]CommentJSON, 0, len(t.OldComments)+len(t.Comments))
|
||||
for _, c := range t.OldComments {
|
||||
escapedContent := template.HTML(html.EscapeString(c.Content))
|
||||
commentsJSON = append(commentsJSON, CommentJSON{Username: c.Username, Content: escapedContent, Date: c.Date})
|
||||
commentsJSON = append(commentsJSON, CommentJSON{Username: c.Username, Content: template.HTML(c.Content), Date: c.Date})
|
||||
}
|
||||
for _, c := range t.Comments {
|
||||
commentsJSON = append(commentsJSON, CommentJSON{Username: c.User.Username, Content: util.MarkdownToHTML(c.Content), Date: c.CreatedAt})
|
||||
|
@ -162,6 +136,7 @@ func (t *Torrent) ToJSON() TorrentJSON {
|
|||
Downloads: t.Downloads,
|
||||
UploaderID: t.UploaderID,
|
||||
UploaderName: util.SafeText(uploader),
|
||||
OldUploader: util.SafeText(t.OldUploader),
|
||||
WebsiteLink: util.Safe(t.WebsiteLink),
|
||||
Magnet: util.Safe(magnet),
|
||||
TorrentLink: util.Safe(torrentlink)}
|
||||
|
@ -179,11 +154,3 @@ func TorrentsToJSON(t []Torrent) []TorrentJSON { // TODO: Convert to singular ve
|
|||
}
|
||||
return json
|
||||
}
|
||||
|
||||
func TorrentReportsToJSON(reports []TorrentReport) []TorrentReportJson {
|
||||
json := make([]TorrentReportJson, len(reports))
|
||||
for i := range reports {
|
||||
json[i] = reports[i].ToJson()
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
|
|
@ -22,10 +22,19 @@ type User struct {
|
|||
Likings []User // Don't work `gorm:"foreignkey:user_id;associationforeignkey:follower_id;many2many:user_follows"`
|
||||
Liked []User // Don't work `gorm:"foreignkey:follower_id;associationforeignkey:user_id;many2many:user_follows"`
|
||||
|
||||
MD5 string `json:"md5"` // Hash of email address, used for Gravatar
|
||||
MD5 string `json:"md5" gorm:"column:md5"` // Hash of email address, used for Gravatar
|
||||
Torrents []Torrent `gorm:"ForeignKey:UploaderID"`
|
||||
}
|
||||
|
||||
type UserJSON struct {
|
||||
ID uint `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
Status int `json:"status"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
LikingCount int `json:"liking_count"`
|
||||
LikedCount int `json:"liked_count"`
|
||||
}
|
||||
|
||||
// Returns the total size of memory recursively allocated for this struct
|
||||
func (u User) Size() (s int) {
|
||||
s += 4 + // ints
|
||||
|
@ -50,3 +59,25 @@ type UserFollows struct {
|
|||
UserID uint `gorm:"column:user_id"`
|
||||
FollowerID uint `gorm:"column:following"`
|
||||
}
|
||||
|
||||
type UserUploadsOld struct {
|
||||
Username string `gorm:"column:username"`
|
||||
TorrentId uint `gorm:"column:torrent_id"`
|
||||
}
|
||||
|
||||
func (c UserUploadsOld) TableName() string {
|
||||
// TODO: rename this in db
|
||||
return "user_uploads_old"
|
||||
}
|
||||
|
||||
func (u *User) ToJSON() UserJSON {
|
||||
json := UserJSON{
|
||||
ID: u.ID,
|
||||
Username: u.Username,
|
||||
Status: u.Status,
|
||||
CreatedAt: u.CreatedAt.Format(time.RFC3339),
|
||||
LikingCount: u.LikingCount,
|
||||
LikedCount: u.LikedCount,
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
|
17
network/closer.go
Fichier normal
17
network/closer.go
Fichier normal
|
@ -0,0 +1,17 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// implements io.Closer that gracefully closes an http server
|
||||
type GracefulHttpCloser struct {
|
||||
Server *http.Server
|
||||
Listener net.Listener
|
||||
}
|
||||
|
||||
func (c *GracefulHttpCloser) Close() error {
|
||||
c.Listener.Close()
|
||||
return c.Server.Shutdown(nil)
|
||||
}
|
58
network/graceful.go
Fichier normal
58
network/graceful.go
Fichier normal
|
@ -0,0 +1,58 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
var ErrListenerStopped = errors.New("listener was stopped")
|
||||
|
||||
// GracefulListener provides safe and graceful net.Listener wrapper that prevents error on graceful shutdown
|
||||
type GracefulListener struct {
|
||||
listener net.Listener
|
||||
stop chan int
|
||||
}
|
||||
|
||||
func (l *GracefulListener) Accept() (net.Conn, error) {
|
||||
for {
|
||||
c, err := l.listener.Accept()
|
||||
select {
|
||||
case <-l.stop:
|
||||
if c != nil {
|
||||
c.Close()
|
||||
}
|
||||
close(l.stop)
|
||||
l.stop = nil
|
||||
return nil, ErrListenerStopped
|
||||
default:
|
||||
|
||||
}
|
||||
if err != nil {
|
||||
neterr, ok := err.(net.Error)
|
||||
if ok && neterr.Timeout() && neterr.Temporary() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return c, err
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GracefulListener) Close() (err error) {
|
||||
l.listener.Close()
|
||||
if l.stop != nil {
|
||||
l.stop <- 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (l *GracefulListener) Addr() net.Addr {
|
||||
return l.listener.Addr()
|
||||
}
|
||||
|
||||
// WrapListener wraps a net.Listener such that it can be closed gracefully
|
||||
func WrapListener(l net.Listener) net.Listener {
|
||||
return &GracefulListener{
|
||||
listener: l,
|
||||
stop: make(chan int),
|
||||
}
|
||||
}
|
|
@ -20,5 +20,8 @@ func CreateHTTPListener(conf *config.Config) (l net.Listener, err error) {
|
|||
l = s
|
||||
}
|
||||
}
|
||||
if l != nil {
|
||||
l = WrapListener(l)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
/* Torrent status colors */
|
||||
.remake {
|
||||
background-color: rgb(240, 176, 128);
|
||||
|
@ -323,7 +321,8 @@ div.container div.blockBody:nth-of-type(2) table tr:first-of-type th:last-of-typ
|
|||
background: #fff;
|
||||
min-height: 460px;
|
||||
}
|
||||
/* Night mode switcher */
|
||||
|
||||
/* Night mode switcher */
|
||||
#mainmenu a.nightswitch {
|
||||
background: transparent url(/img/sun.png) no-repeat;
|
||||
background-size: 24px;
|
||||
|
@ -331,11 +330,12 @@ div.container div.blockBody:nth-of-type(2) table tr:first-of-type th:last-of-typ
|
|||
margin-left: 3px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
font-size: 2rem;
|
||||
font-family: cursive;
|
||||
color: #CCC;
|
||||
text-shadow: 0px -1px #999999;
|
||||
}
|
||||
color: #616161;
|
||||
text-shadow: -1px -1px #999999;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/ewhal/nyaa/config"
|
||||
"github.com/ewhal/nyaa/db"
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/service"
|
||||
"github.com/ewhal/nyaa/service/api"
|
||||
"github.com/ewhal/nyaa/service/torrent"
|
||||
"github.com/ewhal/nyaa/util"
|
||||
|
@ -21,7 +22,7 @@ import (
|
|||
func ApiHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
page := vars["page"]
|
||||
whereParams := torrentService.WhereParams{}
|
||||
whereParams := serviceBase.WhereParams{}
|
||||
req := apiService.TorrentsRequest{}
|
||||
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
|
|
|
@ -3,30 +3,42 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"html"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"fmt"
|
||||
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/service/comment"
|
||||
"github.com/ewhal/nyaa/service/report"
|
||||
"github.com/ewhal/nyaa/service/torrent"
|
||||
"github.com/ewhal/nyaa/service/torrent/form"
|
||||
"github.com/ewhal/nyaa/service/user"
|
||||
form "github.com/ewhal/nyaa/service/user/form"
|
||||
"github.com/ewhal/nyaa/service/user/permission"
|
||||
"github.com/ewhal/nyaa/util/languages"
|
||||
"github.com/ewhal/nyaa/util/log"
|
||||
"github.com/ewhal/nyaa/util/modelHelper"
|
||||
"github.com/ewhal/nyaa/util/search"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var panelIndex, panelTorrentList, panelUserList, panelCommentList, panelTorrentEd *template.Template
|
||||
var panelIndex, panelTorrentList, panelUserList, panelCommentList, panelTorrentEd, panelTorrentReportList *template.Template
|
||||
|
||||
func init() {
|
||||
panelTorrentList = template.Must(template.New("torrentlist").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/torrentlist.html")))
|
||||
panelTorrentList = template.Must(panelTorrentList.ParseGlob(filepath.Join("templates", "_*.html")))
|
||||
panelUserList = template.Must(template.New("userlist").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/userlist.html")))
|
||||
panelUserList = template.Must(panelUserList.ParseGlob(filepath.Join("templates", "_*.html")))
|
||||
panelCommentList = template.Must(template.New("commentlist").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/commentlist.html")))
|
||||
panelCommentList = template.Must(panelCommentList.ParseGlob(filepath.Join("templates", "_*.html")))
|
||||
panelIndex = template.Must(template.New("indexPanel").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/panelindex.html")))
|
||||
panelTorrentEd = template.Must(template.New("indexPanel").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/paneltorrentedit.html")))
|
||||
panelIndex = template.Must(panelIndex.ParseGlob(filepath.Join("templates", "_*.html")))
|
||||
panelTorrentEd = template.Must(template.New("torrent_ed").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/paneltorrentedit.html")))
|
||||
panelTorrentEd = template.Must(panelTorrentEd.ParseGlob(filepath.Join("templates", "_*.html")))
|
||||
panelTorrentReportList = template.Must(template.New("torrent_report").Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "admin_index.html"), filepath.Join(TemplateDir, "admin/torrent_report.html")))
|
||||
panelTorrentReportList = template.Must(panelTorrentReportList.ParseGlob(filepath.Join("templates", "_*.html")))
|
||||
}
|
||||
|
||||
func IndexModPanel(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -34,58 +46,138 @@ func IndexModPanel(w http.ResponseWriter, r *http.Request) {
|
|||
if userPermission.HasAdmin(currentUser) {
|
||||
offset := 10
|
||||
|
||||
torrents, _, _ := torrentService.GetAllTorrents(0, offset)
|
||||
users := userService.RetrieveUsersForAdmin(0, offset)
|
||||
comments := commentService.GetAllComments(0, offset)
|
||||
torrents, _, _ := torrentService.GetAllTorrents(offset, 0)
|
||||
users, _ := userService.RetrieveUsersForAdmin(offset, 0)
|
||||
comments, _ := commentService.GetAllComments(offset, 0, "", "")
|
||||
torrentReports, _, _ := reportService.GetAllTorrentReports(offset, 0)
|
||||
|
||||
languages.SetTranslationFromRequest(panelIndex, r, "en-us")
|
||||
htv := PanelIndexVbs{torrents, users, comments}
|
||||
htv := PanelIndexVbs{torrents, torrentReports, users, comments, NewSearchForm(), currentUser, r.URL}
|
||||
_ = panelIndex.ExecuteTemplate(w, "admin_index.html", htv)
|
||||
} else {
|
||||
http.Error(w, "admins only", http.StatusForbidden)
|
||||
}
|
||||
}
|
||||
|
||||
func TorrentsListPanel(w http.ResponseWriter, r *http.Request) {
|
||||
currentUser := GetUser(r)
|
||||
if userPermission.HasAdmin(currentUser) {
|
||||
page, _ := strconv.Atoi(r.URL.Query().Get("p"))
|
||||
vars := mux.Vars(r)
|
||||
page := vars["page"]
|
||||
|
||||
var err error
|
||||
pagenum := 1
|
||||
if page != "" {
|
||||
pagenum, err = strconv.Atoi(html.EscapeString(page))
|
||||
if !log.CheckError(err) {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
offset := 100
|
||||
|
||||
torrents, _, _ := torrentService.GetAllTorrents(offset, page * offset)
|
||||
searchParam, torrents, _, err := search.SearchByQuery(r, pagenum)
|
||||
searchForm := SearchForm{
|
||||
SearchParam: searchParam,
|
||||
Category: searchParam.Category.String(),
|
||||
HideAdvancedSearch: false,
|
||||
}
|
||||
|
||||
languages.SetTranslationFromRequest(panelTorrentList, r, "en-us")
|
||||
htv := PanelTorrentListVbs{torrents}
|
||||
err := panelTorrentList.ExecuteTemplate(w, "admin_index.html", htv)
|
||||
fmt.Println(err)
|
||||
htv := PanelTorrentListVbs{torrents, searchForm, Navigation{int(searchParam.Max), offset, pagenum, "mod_tlist_page"}, currentUser, r.URL}
|
||||
err = panelTorrentList.ExecuteTemplate(w, "admin_index.html", htv)
|
||||
log.CheckError(err)
|
||||
} else {
|
||||
|
||||
http.Error(w, "admins only", http.StatusForbidden)
|
||||
}
|
||||
}
|
||||
|
||||
func TorrentReportListPanel(w http.ResponseWriter, r *http.Request) {
|
||||
currentUser := GetUser(r)
|
||||
if userPermission.HasAdmin(currentUser) {
|
||||
vars := mux.Vars(r)
|
||||
page := vars["page"]
|
||||
|
||||
var err error
|
||||
pagenum := 1
|
||||
if page != "" {
|
||||
pagenum, err = strconv.Atoi(html.EscapeString(page))
|
||||
if !log.CheckError(err) {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
offset := 100
|
||||
|
||||
torrentReports, nbReports, _ := reportService.GetAllTorrentReports(offset, (pagenum-1)*offset)
|
||||
|
||||
reportJSON := model.TorrentReportsToJSON(torrentReports)
|
||||
languages.SetTranslationFromRequest(panelTorrentReportList, r, "en-us")
|
||||
htv := PanelTorrentReportListVbs{reportJSON, NewSearchForm(), Navigation{nbReports, offset, pagenum, "mod_trlist_page"}, currentUser, r.URL}
|
||||
err = panelTorrentReportList.ExecuteTemplate(w, "admin_index.html", htv)
|
||||
log.CheckError(err)
|
||||
} else {
|
||||
http.Error(w, "admins only", http.StatusForbidden)
|
||||
}
|
||||
}
|
||||
|
||||
func UsersListPanel(w http.ResponseWriter, r *http.Request) {
|
||||
currentUser := GetUser(r)
|
||||
if userPermission.HasAdmin(currentUser) {
|
||||
page, _ := strconv.Atoi(r.URL.Query().Get("p"))
|
||||
vars := mux.Vars(r)
|
||||
page := vars["page"]
|
||||
|
||||
var err error
|
||||
pagenum := 1
|
||||
if page != "" {
|
||||
pagenum, err = strconv.Atoi(html.EscapeString(page))
|
||||
if !log.CheckError(err) {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
offset := 100
|
||||
|
||||
users := userService.RetrieveUsersForAdmin(offset, page*offset)
|
||||
users, nbUsers := userService.RetrieveUsersForAdmin(offset, (pagenum-1)*offset)
|
||||
languages.SetTranslationFromRequest(panelUserList, r, "en-us")
|
||||
htv := PanelUserListVbs{users}
|
||||
err := panelUserList.ExecuteTemplate(w, "admin_index.html", htv)
|
||||
fmt.Println(err)
|
||||
htv := PanelUserListVbs{users, NewSearchForm(), Navigation{nbUsers, offset, pagenum, "mod_ulist_page"}, currentUser, r.URL}
|
||||
err = panelUserList.ExecuteTemplate(w, "admin_index.html", htv)
|
||||
log.CheckError(err)
|
||||
} else {
|
||||
http.Error(w, "admins only", http.StatusForbidden)
|
||||
}
|
||||
}
|
||||
|
||||
func CommentsListPanel(w http.ResponseWriter, r *http.Request) {
|
||||
currentUser := GetUser(r)
|
||||
if userPermission.HasAdmin(currentUser) {
|
||||
page, _ := strconv.Atoi(r.URL.Query().Get("p"))
|
||||
offset := 100
|
||||
vars := mux.Vars(r)
|
||||
page := vars["page"]
|
||||
|
||||
comments := commentService.GetAllComments(offset, page * offset)
|
||||
var err error
|
||||
pagenum := 1
|
||||
if page != "" {
|
||||
pagenum, err = strconv.Atoi(html.EscapeString(page))
|
||||
if !log.CheckError(err) {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
offset := 100
|
||||
userid := r.URL.Query().Get("userid")
|
||||
var conditions string
|
||||
var values []interface{}
|
||||
if userid != "" {
|
||||
conditions = "user_id = ?"
|
||||
values = append(values, userid)
|
||||
}
|
||||
|
||||
comments, nbComments := commentService.GetAllComments(offset, (pagenum-1)*offset, conditions, values...)
|
||||
languages.SetTranslationFromRequest(panelCommentList, r, "en-us")
|
||||
htv := PanelCommentListVbs{comments}
|
||||
err := panelCommentList.ExecuteTemplate(w, "admin_index.html", htv)
|
||||
fmt.Println(err)
|
||||
htv := PanelCommentListVbs{comments, NewSearchForm(), Navigation{nbComments, offset, pagenum, "mod_clist_page"}, currentUser, r.URL}
|
||||
err = panelCommentList.ExecuteTemplate(w, "admin_index.html", htv)
|
||||
log.CheckError(err)
|
||||
} else {
|
||||
http.Error(w, "admins only", http.StatusForbidden)
|
||||
}
|
||||
|
@ -97,9 +189,9 @@ func TorrentEditModPanel(w http.ResponseWriter, r *http.Request) {
|
|||
id := r.URL.Query().Get("id")
|
||||
torrent, _ := torrentService.GetTorrentById(id)
|
||||
languages.SetTranslationFromRequest(panelTorrentEd, r, "en-us")
|
||||
htv := PanelTorrentEdVbs{torrent}
|
||||
htv := PanelTorrentEdVbs{torrent, NewSearchForm(), currentUser}
|
||||
err := panelTorrentEd.ExecuteTemplate(w, "admin_index.html", htv)
|
||||
fmt.Println(err)
|
||||
log.CheckError(err)
|
||||
} else {
|
||||
http.Error(w, "admins only", http.StatusForbidden)
|
||||
}
|
||||
|
@ -128,7 +220,7 @@ func TorrentPostEditModPanel(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
languages.SetTranslationFromRequest(panelTorrentEd, r, "en-us")
|
||||
htv := PanelTorrentEdVbs{torrent}
|
||||
htv := PanelTorrentEdVbs{torrent, NewSearchForm(), currentUser}
|
||||
_ = panelTorrentEd.ExecuteTemplate(w, "admin_index.html", htv)
|
||||
} else {
|
||||
http.Error(w, "admins only", http.StatusForbidden)
|
||||
|
|
16
router/router.go
Fichier normal → Fichier exécutable
16
router/router.go
Fichier normal → Fichier exécutable
|
@ -37,10 +37,12 @@ func init() {
|
|||
gzipUserLogoutHandler := handlers.CompressHandler(http.HandlerFunc(UserLogoutHandler))
|
||||
gzipUserProfileHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileHandler))
|
||||
gzipUserFollowHandler := handlers.CompressHandler(http.HandlerFunc(UserFollowHandler))
|
||||
gzipUserDetailsHandler := handlers.CompressHandler(http.HandlerFunc(UserDetailsHandler))
|
||||
gzipUserProfileFormHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileFormHandler))
|
||||
|
||||
gzipIndexModPanel := handlers.CompressHandler(http.HandlerFunc(IndexModPanel))
|
||||
gzipTorrentsListPanel := handlers.CompressHandler(http.HandlerFunc(TorrentsListPanel))
|
||||
gzipTorrentReportListPanel := handlers.CompressHandler(http.HandlerFunc(TorrentReportListPanel))
|
||||
gzipUsersListPanel := handlers.CompressHandler(http.HandlerFunc(UsersListPanel))
|
||||
gzipCommentsListPanel := handlers.CompressHandler(http.HandlerFunc(CommentsListPanel))
|
||||
gzipTorrentEditModPanel := handlers.CompressHandler(http.HandlerFunc(TorrentEditModPanel))
|
||||
|
@ -48,7 +50,6 @@ func init() {
|
|||
gzipCommentDeleteModPanel := handlers.CompressHandler(http.HandlerFunc(CommentDeleteModPanel))
|
||||
gzipTorrentDeleteModPanel := handlers.CompressHandler(http.HandlerFunc(TorrentDeleteModPanel))
|
||||
|
||||
gzipGetTorrentReportHandler := handlers.CompressHandler(http.HandlerFunc(GetTorrentReportHandler))
|
||||
//gzipTorrentReportCreateHandler := handlers.CompressHandler(http.HandlerFunc(CreateTorrentReportHandler))
|
||||
//gzipTorrentReportDeleteHandler := handlers.CompressHandler(http.HandlerFunc(DeleteTorrentReportHandler))
|
||||
//gzipTorrentDeleteHandler := handlers.CompressHandler(http.HandlerFunc(DeleteTorrentHandler))
|
||||
|
@ -81,13 +82,17 @@ func init() {
|
|||
Router.Handle("/user/logout", gzipUserLogoutHandler).Name("user_logout")
|
||||
Router.Handle("/user/{id}/{username}", wrapHandler(gzipUserProfileHandler)).Name("user_profile").Methods("GET")
|
||||
Router.Handle("/user/{id}/{username}/follow", gzipUserFollowHandler).Name("user_follow").Methods("GET")
|
||||
Router.Handle("/user/{id}/{username}", wrapHandler(gzipUserProfileFormHandler)).Name("user_profile").Methods("POST")
|
||||
Router.Handle("/user/{id}/{username}/edit", wrapHandler(gzipUserDetailsHandler)).Name("user_profile_details").Methods("GET")
|
||||
Router.Handle("/user/{id}/{username}/edit", wrapHandler(gzipUserProfileFormHandler)).Name("user_profile_edit").Methods("POST")
|
||||
|
||||
Router.Handle("/mod/", gzipIndexModPanel).Name("mod_index")
|
||||
Router.Handle("/mod", gzipIndexModPanel).Name("mod_index")
|
||||
Router.Handle("/mod/torrents", gzipTorrentsListPanel).Name("mod_tlist")
|
||||
Router.Handle("/mod/torrents/{page}", gzipTorrentsListPanel).Name("mod_tlist_page")
|
||||
Router.Handle("/mod/users", gzipUsersListPanel).Name("mod_ulist")
|
||||
Router.Handle("/mod/users/{page}", gzipUsersListPanel).Name("mod_ulist_page")
|
||||
Router.Handle("/mod/comments", gzipCommentsListPanel).Name("mod_clist")
|
||||
Router.Handle("/mod/comments", gzipCommentsListPanel).Name("mod_cedit") // TODO
|
||||
Router.Handle("/mod/comments/{page}", gzipCommentsListPanel).Name("mod_clist_page")
|
||||
Router.Handle("/mod/comment", gzipCommentsListPanel).Name("mod_cedit") // TODO
|
||||
Router.Handle("/mod/torrent/", gzipTorrentEditModPanel).Name("mod_tedit")
|
||||
Router.Handle("/mod/torrent/", gzipTorrentPostEditModPanel).Name("mod_ptedit")
|
||||
Router.Handle("/mod/torrent/delete", gzipTorrentDeleteModPanel).Name("mod_tdelete")
|
||||
|
@ -102,7 +107,8 @@ func init() {
|
|||
// TODO Allow only moderators to access /moderation/*
|
||||
//Router.Handle("/moderation/report/delete", gzipTorrentReportDeleteHandler).Name("torrent_report_delete").Methods("POST")
|
||||
//Router.Handle("/moderation/torrent/delete", gzipTorrentDeleteHandler).Name("torrent_delete").Methods("POST")
|
||||
Router.Handle("/moderation/report", gzipGetTorrentReportHandler ).Name("torrent_report").Methods("GET")
|
||||
Router.Handle("/mod/reports", gzipTorrentReportListPanel).Name("mod_trlist").Methods("GET")
|
||||
Router.Handle("/mod/reports/{page}", gzipTorrentReportListPanel).Name("mod_trlist_page").Methods("GET")
|
||||
|
||||
Router.NotFoundHandler = http.HandlerFunc(NotFoundHandler)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
var TemplateDir = "templates"
|
||||
|
||||
var torrentReportTemplate, homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate, viewVerifySuccessTemplate, viewProfileTemplate, viewProfileEditTemplate, viewUserDeleteTemplate, notFoundTemplate *template.Template
|
||||
var homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate, viewVerifySuccessTemplate, viewProfileTemplate, viewProfileEditTemplate, viewUserDeleteTemplate, notFoundTemplate *template.Template
|
||||
|
||||
type templateLoader struct {
|
||||
templ **template.Template
|
||||
|
@ -18,11 +18,6 @@ type templateLoader struct {
|
|||
// ReloadTemplates reloads templates on runtime
|
||||
func ReloadTemplates() {
|
||||
templs := []templateLoader{
|
||||
templateLoader{
|
||||
templ: &torrentReportTemplate,
|
||||
name: "torrent_report",
|
||||
file: "torrent_report.html",
|
||||
},
|
||||
templateLoader{
|
||||
templ: &homeTemplate,
|
||||
name: "home",
|
||||
|
|
|
@ -27,9 +27,10 @@ var FuncMap = template.FuncMap{
|
|||
return "error"
|
||||
},
|
||||
"genNav": func(nav Navigation, currentUrl *url.URL, pagesSelectable int) template.HTML {
|
||||
var ret = ""
|
||||
if (nav.TotalItem > 0) {
|
||||
maxPages := math.Ceil(float64(nav.TotalItem) / float64(nav.MaxItemPerPage))
|
||||
|
||||
var ret = ""
|
||||
if nav.CurrentPage-1 > 0 {
|
||||
url, _ := Router.Get(nav.Route).URL("page", "1")
|
||||
ret = ret + "<li><a id=\"page-prev\" href=\"" + url.String() + "?" + currentUrl.RawQuery + "\" aria-label=\"Previous\"><span aria-hidden=\"true\">«</span></a></li>"
|
||||
|
@ -57,6 +58,7 @@ var FuncMap = template.FuncMap{
|
|||
url, _ := Router.Get(nav.Route).URL("page", strconv.Itoa(nav.CurrentPage+1))
|
||||
ret = ret + "<li><a id=\"page-next\" href=\"" + url.String() + "?" + currentUrl.RawQuery + "\" aria-label=\"Next\"><span aria-hidden=\"true\">»</span></a></li>"
|
||||
}
|
||||
}
|
||||
return template.HTML(ret)
|
||||
},
|
||||
"T": i18n.IdentityTfunc,
|
||||
|
|
|
@ -113,27 +113,51 @@ type UploadTemplateVariables struct {
|
|||
Route *mux.Route
|
||||
}
|
||||
|
||||
/* MODERATION Variables */
|
||||
|
||||
type PanelIndexVbs struct {
|
||||
Torrents []model.Torrent
|
||||
Users []model.User
|
||||
Comments []model.Comment
|
||||
Torrents []model.Torrent
|
||||
TorrentReports []model.TorrentReport
|
||||
Users []model.User
|
||||
Comments []model.Comment
|
||||
Search SearchForm
|
||||
User *model.User
|
||||
URL *url.URL // For parsing Url in templates
|
||||
}
|
||||
|
||||
type PanelTorrentListVbs struct {
|
||||
Torrents []model.Torrent
|
||||
Torrents []model.Torrent
|
||||
Search SearchForm
|
||||
Navigation Navigation
|
||||
User *model.User
|
||||
URL *url.URL // For parsing Url in templates
|
||||
}
|
||||
type PanelUserListVbs struct {
|
||||
Users []model.User
|
||||
Users []model.User
|
||||
Search SearchForm
|
||||
Navigation Navigation
|
||||
User *model.User
|
||||
URL *url.URL // For parsing Url in templates
|
||||
}
|
||||
type PanelCommentListVbs struct {
|
||||
Comments []model.Comment
|
||||
Comments []model.Comment
|
||||
Search SearchForm
|
||||
Navigation Navigation
|
||||
User *model.User
|
||||
URL *url.URL // For parsing Url in templates
|
||||
}
|
||||
type PanelTorrentEdVbs struct {
|
||||
Torrent model.Torrent
|
||||
Search SearchForm
|
||||
User *model.User
|
||||
}
|
||||
|
||||
type ViewTorrentReportsVariables struct {
|
||||
Torrents []model.TorrentReportJson
|
||||
type PanelTorrentReportListVbs struct {
|
||||
TorrentReports []model.TorrentReportJson
|
||||
Search SearchForm
|
||||
Navigation Navigation
|
||||
User *model.User
|
||||
URL *url.URL // For parsing Url in templates
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
/*import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/service/moderation"
|
||||
"github.com/ewhal/nyaa/service/user/permission"
|
||||
)
|
||||
"github.com/gorilla/mux"
|
||||
)*/
|
||||
|
||||
/*
|
||||
func SanitizeTorrentReport(torrentReport *model.TorrentReport) {
|
||||
// TODO unescape html ?
|
||||
|
@ -47,24 +50,7 @@ func DeleteTorrentReportHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
*/
|
||||
func GetTorrentReportHandler(w http.ResponseWriter, r *http.Request) {
|
||||
currentUser := GetUser(r)
|
||||
if userPermission.HasAdmin(currentUser) {
|
||||
|
||||
torrentReports, err := moderationService.GetTorrentReports()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
err = torrentReportTemplate.ExecuteTemplate(w, "torrent_report.html", ViewTorrentReportsVariables{model.TorrentReportsToJSON(torrentReports)})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
http.Error(w, "admins only", http.StatusForbidden)
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
func DeleteTorrentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -76,3 +62,32 @@ func DeleteTorrentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}*/
|
||||
|
||||
/*func GetTorrentReportHandler(w http.ResponseWriter, r *http.Request) {
|
||||
currentUser := GetUser(r)
|
||||
if userPermission.HasAdmin(currentUser) {
|
||||
vars := mux.Vars(r)
|
||||
page, _ := strconv.Atoi(vars["page"])
|
||||
offset := 100
|
||||
userid := r.URL.Query().Get("userid")
|
||||
var conditions string
|
||||
var values []interface{}
|
||||
if (userid != "") {
|
||||
conditions = "user_id = ?"
|
||||
values = append(values, userid)
|
||||
}
|
||||
|
||||
torrentReports, nbReports, err := moderationService.GetTorrentReports(offset, page * offset, conditions, values...)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
err = torrentReportTemplate.ExecuteTemplate(w, "admin_index.html", ViewTorrentReportsVariables{model.TorrentReportsToJSON(torrentReports), NewSearchForm(), Navigation{nbReports, offset, page, "mod_trlist_page"}, currentUser, r.URL})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
http.Error(w, "admins only", http.StatusForbidden)
|
||||
}
|
||||
}*/
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ewhal/nyaa/cache"
|
||||
"github.com/ewhal/nyaa/config"
|
||||
"github.com/ewhal/nyaa/service/captcha"
|
||||
"github.com/ewhal/nyaa/util"
|
||||
|
@ -94,6 +95,7 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
|
|||
f.Name = util.TrimWhitespaces(f.Name)
|
||||
f.Description = p.Sanitize(util.TrimWhitespaces(f.Description))
|
||||
f.Magnet = util.TrimWhitespaces(f.Magnet)
|
||||
cache.Clear()
|
||||
|
||||
catsSplit := strings.Split(f.Category, "_")
|
||||
// need this to prevent out of index panics
|
||||
|
|
47
router/userHandler.go
Fichier normal → Fichier exécutable
47
router/userHandler.go
Fichier normal → Fichier exécutable
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/ewhal/nyaa/service/user"
|
||||
"github.com/ewhal/nyaa/service/user/form"
|
||||
"github.com/ewhal/nyaa/service/user/permission"
|
||||
"github.com/ewhal/nyaa/util/log"
|
||||
"github.com/ewhal/nyaa/util/languages"
|
||||
"github.com/ewhal/nyaa/util/modelHelper"
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -54,30 +55,21 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
|
|||
userProfile, _, errorUser := userService.RetrieveUserForAdmin(id)
|
||||
if errorUser == nil {
|
||||
currentUser := GetUser(r)
|
||||
view := r.URL.Query()["edit"]
|
||||
follow := r.URL.Query()["followed"]
|
||||
unfollow := r.URL.Query()["unfollowed"]
|
||||
infosForm := form.NewInfos()
|
||||
deleteVar := r.URL.Query()["delete"]
|
||||
|
||||
if (view != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) {
|
||||
b := form.UserForm{}
|
||||
modelHelper.BindValueForm(&b, r)
|
||||
languages.SetTranslationFromRequest(viewProfileEditTemplate, r, "en-us")
|
||||
htv := UserProfileEditVariables{&userProfile, b, form.NewErrors(), form.NewInfos(), NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
|
||||
|
||||
err := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", htv)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
} else if (deleteVar != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) {
|
||||
if (deleteVar != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) {
|
||||
err := form.NewErrors()
|
||||
_, errUser := userService.DeleteUser(w, currentUser, id)
|
||||
if errUser != nil {
|
||||
err["errors"] = append(err["errors"], errUser.Error())
|
||||
}
|
||||
languages.SetTranslationFromRequest(viewUserDeleteTemplate, r, "en-us")
|
||||
htv := UserVerifyTemplateVariables{err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
|
||||
searchForm := NewSearchForm()
|
||||
searchForm.HideAdvancedSearch = true
|
||||
htv := UserVerifyTemplateVariables{err, searchForm, Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)}
|
||||
errorTmpl := viewUserDeleteTemplate.ExecuteTemplate(w, "index.html", htv)
|
||||
if errorTmpl != nil {
|
||||
http.Error(w, errorTmpl.Error(), http.StatusInternalServerError)
|
||||
|
@ -90,7 +82,9 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
|
|||
if unfollow != nil {
|
||||
infosForm["infos"] = append(infosForm["infos"], fmt.Sprintf(T("user_unfollowed_msg"), userProfile.Username))
|
||||
}
|
||||
htv := UserProfileVariables{&userProfile, infosForm, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
|
||||
searchForm := NewSearchForm()
|
||||
searchForm.HideAdvancedSearch = true
|
||||
htv := UserProfileVariables{&userProfile, infosForm, searchForm, Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
|
||||
|
||||
err := viewProfileTemplate.ExecuteTemplate(w, "index.html", htv)
|
||||
if err != nil {
|
||||
|
@ -109,6 +103,25 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
//Getting User Profile Details View
|
||||
func UserDetailsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
b := form.UserForm{}
|
||||
userProfile, _, errorUser := userService.RetrieveUserForAdmin(id)
|
||||
if errorUser == nil {
|
||||
currentUser := GetUser(r)
|
||||
modelHelper.BindValueForm(&b, r)
|
||||
languages.SetTranslationFromRequest(viewProfileEditTemplate, r, "en-us")
|
||||
searchForm := NewSearchForm()
|
||||
searchForm.HideAdvancedSearch = true
|
||||
htv := UserProfileEditVariables{&userProfile, b, form.NewErrors(), form.NewInfos(), searchForm, Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)}
|
||||
err := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", htv)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Getting View User Profile Update
|
||||
func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
@ -129,9 +142,14 @@ func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
if len(err) == 0 {
|
||||
modelHelper.BindValueForm(&b, r)
|
||||
if (!userPermission.HasAdmin(currentUser)) {
|
||||
b.Username = currentUser.Username
|
||||
}
|
||||
err = modelHelper.ValidateForm(&b, err)
|
||||
log.Info("lol")
|
||||
if len(err) == 0 {
|
||||
userProfile, _, errorUser = userService.UpdateUser(w, &b, currentUser, id)
|
||||
log.Infof("xD2")
|
||||
if errorUser != nil {
|
||||
err["errors"] = append(err["errors"], errorUser.Error())
|
||||
}
|
||||
|
@ -169,7 +187,6 @@ func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// Post Registration controller, we do some check on the form here, the rest on user service
|
||||
func UserRegisterPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Check same Password
|
||||
b := form.RegistrationForm{}
|
||||
err := form.NewErrors()
|
||||
if !captcha.Authenticate(captcha.Extract(r)) {
|
||||
|
|
|
@ -13,6 +13,8 @@ import (
|
|||
"github.com/ewhal/nyaa/util/languages"
|
||||
"github.com/ewhal/nyaa/util/log"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func ViewHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -49,8 +51,8 @@ func PostCommentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
userID := currentUser.ID
|
||||
comment := model.Comment{TorrentID: uint(idNum), UserID: userID, Content: content, CreatedAt: time.Now()}
|
||||
|
||||
err = db.ORM.Create(&comment).Error
|
||||
|
||||
err = db.ORM.Create(&comment).Error
|
||||
if err != nil {
|
||||
util.SendError(w, err, 500)
|
||||
return
|
||||
|
@ -75,9 +77,18 @@ func ReportTorrentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
currentUser := GetUser(r)
|
||||
|
||||
idNum, err := strconv.Atoi(id)
|
||||
|
||||
userID := currentUser.ID
|
||||
report := model.TorrentReport{Description: r.FormValue("report_type"), TorrentID: uint(idNum), UserID: userID}
|
||||
|
||||
torrent, _ := torrentService.GetTorrentById(id)
|
||||
|
||||
report := model.TorrentReport{
|
||||
Description: r.FormValue("report_type"),
|
||||
TorrentID: uint(idNum),
|
||||
UserID: userID,
|
||||
Torrent: torrent,
|
||||
User: *currentUser,
|
||||
}
|
||||
fmt.Println(report)
|
||||
|
||||
err = db.ORM.Create(&report).Error
|
||||
if err != nil {
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/service/torrent"
|
||||
"github.com/ewhal/nyaa/service"
|
||||
)
|
||||
|
||||
type torrentsQuery struct {
|
||||
|
@ -40,8 +40,8 @@ type UpdateRequest struct {
|
|||
Update TorrentRequest `json:"update"`
|
||||
}
|
||||
|
||||
func (r *TorrentsRequest) ToParams() torrentService.WhereParams {
|
||||
res := torrentService.WhereParams{}
|
||||
func (r *TorrentsRequest) ToParams() serviceBase.WhereParams {
|
||||
res := serviceBase.WhereParams{}
|
||||
conditions := ""
|
||||
v := reflect.ValueOf(r.Query)
|
||||
|
||||
|
|
|
@ -6,8 +6,10 @@ import (
|
|||
|
||||
)
|
||||
|
||||
func GetAllComments(limit int, offset int) []model.Comment{
|
||||
func GetAllComments(limit int, offset int, conditions string, values ...interface{}) ([]model.Comment, int){
|
||||
var comments []model.Comment
|
||||
db.ORM.Limit(limit).Offset(offset).Preload("User").Find(&comments)
|
||||
return comments
|
||||
var nbComments int
|
||||
db.ORM.Model(&comments).Where(conditions, values...).Count(&nbComments)
|
||||
db.ORM.Preload("User").Limit(limit).Offset(offset).Where(conditions, values...).Find(&comments)
|
||||
return comments, nbComments
|
||||
}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
package moderationService
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/ewhal/nyaa/db"
|
||||
"github.com/ewhal/nyaa/model"
|
||||
)
|
||||
|
||||
// Return torrentReport in case we did modified it (ie: CreatedAt field)
|
||||
func CreateTorrentReport(torrentReport model.TorrentReport) (model.TorrentReport, error) {
|
||||
if db.ORM.Create(&torrentReport).Error != nil {
|
||||
return torrentReport, errors.New("TorrentReport was not created")
|
||||
}
|
||||
return torrentReport, nil
|
||||
}
|
||||
|
||||
func DeleteTorrentReport(id int) (int, error) {
|
||||
var torrentReport model.TorrentReport
|
||||
if db.ORM.First(&torrentReport, id).RecordNotFound() {
|
||||
return http.StatusNotFound, errors.New("Trying to delete a torrent report that does not exists.")
|
||||
}
|
||||
if db.ORM.Delete(&torrentReport).Error != nil {
|
||||
return http.StatusInternalServerError, errors.New("User is not deleted.")
|
||||
}
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
|
||||
// TODO Add WhereParams to filter the torrent reports (ie: searching description)
|
||||
// TODO Use limit, offset
|
||||
func GetTorrentReports() ([]model.TorrentReport, error) {
|
||||
var torrentReports []model.TorrentReport
|
||||
if db.ORM.Preload("User").Preload("Torrent").Find(&torrentReports).Error != nil {
|
||||
return nil, errors.New("Problem finding all torrent reports.")
|
||||
}
|
||||
return torrentReports, nil
|
||||
}
|
76
service/report/report.go
Fichier normal
76
service/report/report.go
Fichier normal
|
@ -0,0 +1,76 @@
|
|||
package reportService
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ewhal/nyaa/db"
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/service"
|
||||
)
|
||||
|
||||
// Return torrentReport in case we did modified it (ie: CreatedAt field)
|
||||
func CreateTorrentReport(torrentReport model.TorrentReport) error {
|
||||
if db.ORM.Create(&torrentReport).Error != nil {
|
||||
return errors.New("TorrentReport was not created")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteTorrentReport(id int) (error, int) {
|
||||
var torrentReport model.TorrentReport
|
||||
if db.ORM.First(&torrentReport, id).RecordNotFound() {
|
||||
return errors.New("Trying to delete a torrent report that does not exists."), http.StatusNotFound
|
||||
}
|
||||
if err := db.ORM.Delete(&torrentReport).Error; err != nil {
|
||||
return err, http.StatusInternalServerError
|
||||
}
|
||||
return nil, http.StatusOK
|
||||
}
|
||||
|
||||
func getTorrentReportsOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int, countAll bool) (
|
||||
torrentReports []model.TorrentReport, count int, err error,
|
||||
) {
|
||||
var conditionArray []string
|
||||
var params []interface{}
|
||||
if parameters != nil { // if there is where parameters
|
||||
if len(parameters.Conditions) > 0 {
|
||||
conditionArray = append(conditionArray, parameters.Conditions)
|
||||
}
|
||||
params = parameters.Params
|
||||
}
|
||||
conditions := strings.Join(conditionArray, " AND ")
|
||||
if countAll {
|
||||
err = db.ORM.Model(&torrentReports).Where(conditions, params...).Count(&count).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// TODO: Vulnerable to injections. Use query builder. (is it?)
|
||||
|
||||
// build custom db query for performance reasons
|
||||
dbQuery := "SELECT * FROM torrent_reports"
|
||||
if conditions != "" {
|
||||
dbQuery = dbQuery + " WHERE " + conditions
|
||||
}
|
||||
|
||||
if orderBy == "" { // default OrderBy
|
||||
orderBy = "torrent_report_id DESC"
|
||||
}
|
||||
dbQuery = dbQuery + " ORDER BY " + orderBy
|
||||
if limit != 0 || offset != 0 { // if limits provided
|
||||
dbQuery = dbQuery + " LIMIT " + strconv.Itoa(limit) + " OFFSET " + strconv.Itoa(offset)
|
||||
}
|
||||
err = db.ORM.Preload("Torrent").Preload("User").Raw(dbQuery, params...).Find(&torrentReports).Error //fixed !!!!
|
||||
return
|
||||
}
|
||||
|
||||
func GetTorrentReportsOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) ([]model.TorrentReport, int, error) {
|
||||
return getTorrentReportsOrderBy(parameters, orderBy, limit, offset, true)
|
||||
}
|
||||
|
||||
func GetAllTorrentReports(limit int, offset int) ([]model.TorrentReport, int, error) {
|
||||
return GetTorrentReportsOrderBy(nil, "", limit, offset)
|
||||
}
|
17
service/service.go
Fichier normal
17
service/service.go
Fichier normal
|
@ -0,0 +1,17 @@
|
|||
package serviceBase
|
||||
|
||||
type WhereParams struct {
|
||||
Conditions string // Ex : name LIKE ? AND category_id LIKE ?
|
||||
Params []interface{}
|
||||
}
|
||||
|
||||
func CreateWhereParams(conditions string, params ...string) WhereParams {
|
||||
whereParams := WhereParams{
|
||||
Conditions: conditions,
|
||||
Params: make([]interface{}, len(params)),
|
||||
}
|
||||
for i := range params {
|
||||
whereParams.Params[i] = params[i]
|
||||
}
|
||||
return whereParams
|
||||
}
|
|
@ -2,21 +2,17 @@ package torrentService
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"net/http"
|
||||
|
||||
"github.com/ewhal/nyaa/config"
|
||||
"github.com/ewhal/nyaa/db"
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/service"
|
||||
"github.com/ewhal/nyaa/util"
|
||||
)
|
||||
|
||||
type WhereParams struct {
|
||||
Conditions string // Ex : name LIKE ? AND category_id LIKE ?
|
||||
Params []interface{}
|
||||
}
|
||||
|
||||
/* Function to interact with Models
|
||||
*
|
||||
* Get the torrents with where clause
|
||||
|
@ -51,7 +47,8 @@ func GetFeeds() (result []model.Feed, err error) {
|
|||
}
|
||||
|
||||
func GetTorrentById(id string) (torrent model.Torrent, err error) {
|
||||
id_int, err := strconv.Atoi(id)
|
||||
// Postgres DB integer size is 32-bit
|
||||
id_int, err := strconv.ParseInt(id, 10, 32)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -73,6 +70,13 @@ func GetTorrentById(id string) (torrent model.Torrent, err error) {
|
|||
// (or maybe I'm just retarded)
|
||||
torrent.Uploader = new(model.User)
|
||||
db.ORM.Where("user_id = ?", torrent.UploaderID).Find(torrent.Uploader)
|
||||
torrent.OldUploader = ""
|
||||
if torrent.ID <= config.LastOldTorrentID {
|
||||
var tmp model.UserUploadsOld
|
||||
if !db.ORM.Where("torrent_id = ?", torrent.ID).Find(&tmp).RecordNotFound() {
|
||||
torrent.OldUploader = tmp.Username
|
||||
}
|
||||
}
|
||||
for i := range torrent.Comments {
|
||||
torrent.Comments[i].User = new(model.User)
|
||||
err = db.ORM.Where("user_id = ?", torrent.Comments[i].UserID).Find(torrent.Comments[i].User).Error
|
||||
|
@ -84,21 +88,21 @@ func GetTorrentById(id string) (torrent model.Torrent, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func GetTorrentsOrderByNoCount(parameters *WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, err error) {
|
||||
func GetTorrentsOrderByNoCount(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, err error) {
|
||||
torrents, _, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, false)
|
||||
return
|
||||
}
|
||||
|
||||
func GetTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, count int, err error) {
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
func getTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offset int, countAll bool) (
|
||||
func getTorrentsOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int, countAll bool) (
|
||||
torrents []model.Torrent, count int, err error,
|
||||
) {
|
||||
var conditionArray []string
|
||||
conditionArray = append(conditionArray, "deleted_at IS NULL")
|
||||
conditionArray = append(conditionArray, "deleted_at IS NULL")
|
||||
if strings.HasPrefix(orderBy, "filesize") {
|
||||
// torrents w/ NULL filesize fuck up the sorting on Postgres
|
||||
conditionArray = append(conditionArray, "filesize IS NOT NULL")
|
||||
|
@ -112,12 +116,13 @@ func getTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offs
|
|||
}
|
||||
conditions := strings.Join(conditionArray, " AND ")
|
||||
if countAll {
|
||||
// FIXME: `deleted_at IS NULL` is duplicate in here because GORM handles this for us
|
||||
err = db.ORM.Model(&torrents).Where(conditions, params...).Count(&count).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// TODO: Vulnerable to injections. Use query builder.
|
||||
// TODO: Vulnerable to injections. Use query builder. (is it?)
|
||||
|
||||
// build custom db query for performance reasons
|
||||
dbQuery := "SELECT * FROM torrents"
|
||||
|
@ -143,12 +148,12 @@ func getTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offs
|
|||
// database. The list will be of length 'limit' and in default order.
|
||||
// GetTorrents returns the first records found. Later records may be retrieved
|
||||
// by providing a positive 'offset'
|
||||
func GetTorrents(parameters WhereParams, limit int, offset int) ([]model.Torrent, int, error) {
|
||||
func GetTorrents(parameters serviceBase.WhereParams, limit int, offset int) ([]model.Torrent, int, error) {
|
||||
return GetTorrentsOrderBy(¶meters, "", limit, offset)
|
||||
}
|
||||
|
||||
// Get Torrents with where parameters but no limit and order by default (get all the torrents corresponding in the db)
|
||||
func GetTorrentsDB(parameters WhereParams) ([]model.Torrent, int, error) {
|
||||
func GetTorrentsDB(parameters serviceBase.WhereParams) ([]model.Torrent, int, error) {
|
||||
return GetTorrentsOrderBy(¶meters, "", 0, 0)
|
||||
}
|
||||
|
||||
|
@ -164,17 +169,6 @@ func GetAllTorrentsDB() ([]model.Torrent, int, error) {
|
|||
return GetTorrentsOrderBy(nil, "", 0, 0)
|
||||
}
|
||||
|
||||
func CreateWhereParams(conditions string, params ...string) WhereParams {
|
||||
whereParams := WhereParams{
|
||||
Conditions: conditions,
|
||||
Params: make([]interface{}, len(params)),
|
||||
}
|
||||
for i := range params {
|
||||
whereParams.Params[i] = params[i]
|
||||
}
|
||||
return whereParams
|
||||
}
|
||||
|
||||
func DeleteTorrent(id string) (int, error) {
|
||||
var torrent model.Torrent
|
||||
if db.ORM.First(&torrent, id).RecordNotFound() {
|
||||
|
|
|
@ -116,6 +116,9 @@ func SetCookieHandler(w http.ResponseWriter, email string, pass string) (int, er
|
|||
// RegisterHanderFromForm sets cookie from a RegistrationForm.
|
||||
func RegisterHanderFromForm(w http.ResponseWriter, registrationForm formStruct.RegistrationForm) (int, error) {
|
||||
email := registrationForm.Email
|
||||
if email == "" {
|
||||
email = registrationForm.Username
|
||||
}
|
||||
pass := registrationForm.Password
|
||||
log.Debugf("RegisterHandler UserEmail : %s", email)
|
||||
log.Debugf("RegisterHandler UserPassword : %s", pass)
|
||||
|
|
|
@ -49,7 +49,7 @@ func IsAgreed(termsAndConditions string) bool { // TODO: Inline function
|
|||
// RegistrationForm is used when creating a user.
|
||||
type RegistrationForm struct {
|
||||
Username string `form:"username" needed:"true" len_min:"3" len_max:"20"`
|
||||
Email string `form:"email" needed:"true"`
|
||||
Email string `form:"email"`
|
||||
Password string `form:"password" needed:"true" len_min:"6" len_max:"25" equalInput:"ConfirmPassword"`
|
||||
ConfirmPassword string `form:"password_confirmation" omit:"true" needed:"true"`
|
||||
CaptchaID string `form:"captchaID" omit:"true" needed:"true"`
|
||||
|
@ -64,13 +64,13 @@ type LoginForm struct {
|
|||
|
||||
// UserForm is used when updating a user.
|
||||
type UserForm struct {
|
||||
Username string `form:"username" needed:"true" len_min:"3" len_max:"20"`
|
||||
Email string `form:"email" needed:"true"`
|
||||
Language string `form:"language" default:"en-us"`
|
||||
CurrentPassword string `form:"password" len_min:"6" len_max:"25" omit:"true"`
|
||||
Password string `form:"password" len_min:"6" len_max:"25" equalInput:"ConfirmPassword"`
|
||||
ConfirmPassword string `form:"password_confirmation" omit:"true"`
|
||||
Status int `form:"language" default:"0"`
|
||||
Username string `form:"username" needed:"true" len_min:"3" len_max:"20"`
|
||||
Email string `form:"email" needed:"true"`
|
||||
Language string `form:"language" default:"en-us"`
|
||||
CurrentPassword string `form:"current_password" len_min:"6" len_max:"25" omit:"true"`
|
||||
Password string `form:"password" len_min:"6" len_max:"25" equalInput:"Confirm_Password"`
|
||||
Confirm_Password string `form:"password_confirmation" omit:"true"`
|
||||
Status int `form:"status" default:"0"`
|
||||
}
|
||||
|
||||
// PasswordForm is used when updating a user password.
|
||||
|
|
|
@ -20,7 +20,7 @@ func CurrentOrAdmin(user *model.User, userID uint) bool {
|
|||
// CurrentUserIdentical check that userID is same as current user's ID.
|
||||
// TODO: Inline this
|
||||
func CurrentUserIdentical(user *model.User, userID uint) bool {
|
||||
return user.ID != userID
|
||||
return user.ID == userID
|
||||
}
|
||||
|
||||
func GetRole(user *model.User) string {
|
||||
|
|
|
@ -173,7 +173,7 @@ func UpdateUser(w http.ResponseWriter, form *formStruct.UserForm, currentUser *m
|
|||
if db.ORM.First(&user, id).RecordNotFound() {
|
||||
return user, http.StatusNotFound, errors.New("user not found")
|
||||
}
|
||||
|
||||
log.Infof("updateUser")
|
||||
if form.Password != "" {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(form.CurrentPassword))
|
||||
if err != nil && !userPermission.HasAdmin(currentUser) {
|
||||
|
@ -273,10 +273,12 @@ func RetrieveUserForAdmin(id string) (model.User, int, error) {
|
|||
}
|
||||
|
||||
// RetrieveUsersForAdmin retrieves users for an administrator.
|
||||
func RetrieveUsersForAdmin(limit int, offset int) []model.User {
|
||||
func RetrieveUsersForAdmin(limit int, offset int) ([]model.User, int) {
|
||||
var users []model.User
|
||||
db.ORM.Preload("Torrents").Find(&users).Limit(limit).Offset(offset)
|
||||
return users
|
||||
var nbUsers int
|
||||
db.ORM.Model(&users).Count(&nbUsers)
|
||||
db.ORM.Preload("Torrents").Limit(limit).Offset(offset).Find(&users)
|
||||
return users, nbUsers
|
||||
}
|
||||
|
||||
// CreateUserAuthentication creates user authentication.
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<img src="https://www.gravatar.com/avatar/{{ .MD5 }}?s=50" class="img-circle special-img"> {{ .Username }} <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}"><i class="fa fa-cog"></i> Profile</a></li>
|
||||
{{if HasAdmin . }}<li><a href="{{ genRoute "mod_index" }}"><i class="fa fa-cog"></i> {{T "moderation"}}</a></li>{{end}}
|
||||
<li class="divider"></li>
|
||||
<li><a href="{{ genRoute "user_logout" }}"><i class="fa fa-sign-out"></i> {{ T "sign_out"}}</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<form class="form-horizontal" role="form" method="POST">
|
||||
<div class="form-group">
|
||||
<label class="col-lg-3 control-label">{{ T "email" }}:</label>
|
||||
<label class="col-lg-3 control-label">{{ T "email_address" }}:</label>
|
||||
<div class="col-lg-8">
|
||||
<input class="form-control" type="text" name="email" id="email" value="{{.Email}}">
|
||||
{{ range (index $.FormErrors "email")}}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<th>{{T "links"}}</th>
|
||||
</tr>
|
||||
{{ range .Torrents }}
|
||||
{{ with .ToJson }}
|
||||
{{ with .ToJSON }}
|
||||
<tr class="torrent-info
|
||||
{{if eq .Status 2}}remake{{end}}
|
||||
{{if eq .Status 3}}trusted{{end}}
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
{{define "title"}}Comments List{{end}}
|
||||
{{define "content"}}
|
||||
<table>
|
||||
<table class="table">
|
||||
{{ range .Comments}}
|
||||
|
||||
<tr><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .Content }}</a></td><td><a href="{{ genRoute "mod_cdelete" }}?id={{ .ID }}">Delete</a></td></tr>
|
||||
<tr><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .Content }}</a></td><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .User.Username }}</a></td>
|
||||
<td><a href="{{ genRoute "mod_cdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
|
||||
{{end}}
|
||||
</table>
|
||||
<nav class="torrentNav" aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
{{ genNav .Navigation .URL 5 }}
|
||||
</ul>
|
||||
</nav>
|
||||
{{end}}
|
||||
|
|
|
@ -1,7 +1,57 @@
|
|||
{{define "title"}}Moderation Overview{{end}}
|
||||
{{define "content"}}
|
||||
nigga this just be some links
|
||||
<a href="/mod/torrents">torrent list</a>
|
||||
<a href="/mod/users">user list </a>
|
||||
<a href="/mod/comments">comments list</a>
|
||||
<h3 id="torrents">Last Torrents</h3>
|
||||
<table class="table">
|
||||
{{ range .Torrents}}
|
||||
|
||||
{{end}}
|
||||
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">{{ .Name }}</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>
|
||||
<nav class="torrentNav" aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<li><a href="{{ genRoute "mod_tlist" }}">More</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<h3 id="torrents">Last Torrents Report</h3>
|
||||
<table class="table">
|
||||
{{ range .TorrentReports}}
|
||||
|
||||
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">{{ .Torrent.Name }}</a></td><td>{{.User.Username}}</td><td>{{.Description}}</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>
|
||||
<nav class="torrentNav" aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<li><a href="{{ genRoute "mod_trlist" }}">More</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<h3 id="users">Last Users</h3>
|
||||
<table class="table">
|
||||
{{ range .Users}}
|
||||
|
||||
<tr>
|
||||
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?edit">{{ .Username }}</a></td>
|
||||
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?delete" 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>
|
||||
<nav class="torrentNav" aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<li><a href="{{ genRoute "mod_ulist" }}">More </a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<h3 id="comments">Last Comments</h3>
|
||||
<table class="table">
|
||||
{{ range .Comments}}
|
||||
|
||||
<tr><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .Content }}</a></td><td><a href="{{ genRoute "mod_cedit" }}?id={{.ID}}">{{ .User.Username }}</a></td>
|
||||
<td><a href="{{ genRoute "mod_cdelete" }}?id={{ .ID }}" class="btn btn-danger btn-lg" onclick="if (!confirm('Are you sure?')) return false;"><i class="glyphicon glyphicon-trash"></i>{{ T "delete" }}</a></td></tr>
|
||||
{{end}}
|
||||
</table>
|
||||
<nav class="torrentNav" aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<li><a href="{{ genRoute "mod_clist" }}">More</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
{{end}}
|
||||
|
|
8
templates/admin/torrent_report.html
Fichier normal
8
templates/admin/torrent_report.html
Fichier normal
|
@ -0,0 +1,8 @@
|
|||
{{define "title"}}Torrents Report{{end}}
|
||||
{{define "content"}}
|
||||
<table class="table">
|
||||
{{ range .TorrentReports}}
|
||||
<tr><td><a href="{{ genRoute "mod_tedit"}}?id={{.Torrent.ID}}">{{.Torrent.Name}}</a></td><td>{{.User.Username}}</td><td>{{.Description}}</td><td><a href="{{ genRoute "mod_tdelete" }}?id={{ .Torrent.ID }}">Delete</a></td></tr>
|
||||
{{end}}
|
||||
</table>
|
||||
{{end}}
|
|
@ -1,8 +1,15 @@
|
|||
{{define "title"}}Torrents List{{end}}
|
||||
{{define "content"}}
|
||||
<table>
|
||||
<table class="table">
|
||||
{{ range .Torrents}}
|
||||
|
||||
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">{{ .Name }}</a></td><td><a href="{{ genRoute "mod_tdelete" }}?id={{ .ID }}">Delete</a></td></tr>
|
||||
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">{{ .Name }}</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>
|
||||
<nav class="torrentNav" aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
{{ genNav .Navigation .URL 5 }}
|
||||
</ul>
|
||||
</nav>
|
||||
{{end}}
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
{{define "title"}}Users List{{end}}
|
||||
{{define "content"}}
|
||||
<table>
|
||||
<table class="table">
|
||||
{{ range .Users}}
|
||||
|
||||
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.ID}}">{{ .Username }}</a></td><td><a href="{{ genRoute "mod_tdelete" }}?id{{ .ID }}">Delete</a></td></tr>
|
||||
<tr>
|
||||
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?edit">{{ .Username }}</a></td>
|
||||
<td><a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}?delete" 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>
|
||||
<nav class="torrentNav" aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
{{ genNav .Navigation .URL 5 }}
|
||||
</ul>
|
||||
</nav>
|
||||
{{end}}
|
||||
|
|
|
@ -27,10 +27,46 @@
|
|||
<!-- Website CSS -->
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default" id="mainmenu">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
|
||||
<span class="sr-only">{{ T "toggle_navigation" }}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="{{.URL.Parse "/"}}">Nyaa Pantsu Moderation</a>
|
||||
<a class="navbar-brand hidden-md pull-right nightswitch" href="javascript:toggleNightMode();" ></a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="{{.URL.Parse "/"}}">{{T "website"}}</a/></li>
|
||||
<li><a href="{{ genRoute "mod_tlist"}}">{{T "torrents"}}</a></li>
|
||||
<li><a href="{{ genRoute "mod_ulist"}}">{{T "users"}}</a></li>
|
||||
<li><a href="{{ genRoute "mod_clist"}}">{{T "comments"}}</a></li>
|
||||
<li><a href="{{ genRoute "mod_trlist"}}">{{T "torrent_reports"}}</a></li>
|
||||
</ul>
|
||||
{{block "badge_user" .}}{{end}}
|
||||
<form class="navbar-form navbar-right" role="search" action="/mod/torrents" method="get">
|
||||
<div class="form-group">
|
||||
{{block "search_common" .}}{{end}}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{block "search_button" .}}{{end}}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container" id="container">
|
||||
{{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;">
|
||||
Powered by NyaaPantsu
|
||||
</footer>
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
{{.Name}}
|
||||
</a>
|
||||
</td>
|
||||
<td class="hidden-xs" class="date date-short">{{.Date}}</td>
|
||||
<td class="hidden-xs" class="filesize">{{.Filesize}}</td>
|
||||
<td class="hidden-xs date date-short">{{.Date}}</td>
|
||||
<td class="hidden-xs filesize">{{.Filesize}}</td>
|
||||
<td class="hidden-xs">
|
||||
<a href="{{.Magnet}}" title="Magnet link">
|
||||
<span class="glyphicon glyphicon-magnet" aria-hidden="true"></span>
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<table>
|
||||
{{ range .Torrents}}
|
||||
<tr><td><a href="{{ genRoute "mod_tedit" }}?id={{.Torrent.ID}}">{{ .Torrent.Name }}</a></td><td>{{.User}}</td><td>{{.Description}}</td><td><a href="{{ genRoute "mod_tdelete" }}?id={{ .Torrent.ID }}">Delete</a></td></tr>
|
||||
{{end}}
|
||||
</table>
|
||||
</html>
|
23
templates/user/profile.html
Fichier normal → Fichier exécutable
23
templates/user/profile.html
Fichier normal → Fichier exécutable
|
@ -26,13 +26,13 @@
|
|||
<!-- SIDEBAR BUTTONS -->
|
||||
<div class="profile-userbuttons">
|
||||
{{if gt $.User.ID 0 }}
|
||||
{{if not (CurrentUserIdentical $.User .ID) }}
|
||||
{{if not (IsFollower . $.User)}}
|
||||
<a href="{{ genRoute "user_follow" "id" ( print .ID ) "username" .Username }}" class="btn btn-success btn-sm">{{ T "follow"}}</a>
|
||||
{{else}}
|
||||
<a href="{{ genRoute "user_follow" "id" ( print .ID ) "username" .Username }}" class="btn btn-danger btn-sm">{{ T "unfollow"}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if not (CurrentUserIdentical $.User .ID) }}
|
||||
{{if not (IsFollower . $.User)}}
|
||||
<a href="{{ genRoute "user_follow" "id" ( print .ID ) "username" .Username }}" class="btn btn-success btn-sm">{{ T "follow"}}</a>
|
||||
{{else}}
|
||||
<a href="{{ genRoute "user_follow" "id" ( print .ID ) "username" .Username }}" class="btn btn-danger btn-sm">{{ T "unfollow"}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
<!-- <button type="button" class="btn btn-danger btn-sm">Message</button> -->
|
||||
</div>
|
||||
|
@ -41,16 +41,13 @@
|
|||
<div class="profile-usermenu">
|
||||
<ul class="nav">
|
||||
<li class="active">
|
||||
<a href="#">
|
||||
<i class="glyphicon glyphicon-home"></i>
|
||||
{{T "torrents"}} </a>
|
||||
<a href="{{ genRoute "user_profile" "id" ( print .ID ) "username" .Username }}"><i class="glyphicon glyphicon-home"></i>{{T "torrents"}}</a>
|
||||
</li>
|
||||
{{if gt $.User.ID 0 }}
|
||||
{{if CurrentOrAdmin $.User .ID }}
|
||||
<li>
|
||||
<a href="?edit">
|
||||
<i class="glyphicon glyphicon-user"></i>
|
||||
{{T "settings"}} </a>
|
||||
<a href="{{ genRoute "user_profile_details" "id" ( print .ID ) "username" .Username }}"><i class="glyphicon glyphicon-user"></i>{{T "settings"}}</a>
|
||||
|
||||
</li>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
30
templates/user/profile_edit.html
Fichier normal → Fichier exécutable
30
templates/user/profile_edit.html
Fichier normal → Fichier exécutable
|
@ -23,13 +23,13 @@
|
|||
<!-- SIDEBAR BUTTONS -->
|
||||
<div class="profile-userbuttons">
|
||||
{{if gt $.User.ID 0 }}
|
||||
{{if not (CurrentUserIdentical $.User .ID) }}
|
||||
{{if not (IsFollower . $.User)}}
|
||||
<a href="{{ genRoute "user_follow" "id" (print .ID)}}" class="btn btn-success btn-sm">{{ T "follow"}}</a>
|
||||
{{else}}
|
||||
<a href="{{ genRoute "user_follow" "id" (print .ID)}}" class="btn btn-danger btn-sm">{{ T "unfollow"}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if not (CurrentUserIdentical $.User .ID) }}
|
||||
{{if not (IsFollower . $.User)}}
|
||||
<a href="{{ genRoute "user_follow" "id" (print .ID) "username" .Username }}" class="btn btn-success btn-sm">{{ T "follow"}}</a>
|
||||
{{else}}
|
||||
<a href="{{ genRoute "user_follow" "id" (print .ID) "username" .Username }}" class="btn btn-danger btn-sm">{{ T "unfollow"}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
<!-- <button type="button" class="btn btn-danger btn-sm">Message</button> -->
|
||||
</div>
|
||||
|
@ -38,18 +38,14 @@
|
|||
<div class="profile-usermenu">
|
||||
<ul class="nav">
|
||||
<li>
|
||||
<a href="#">
|
||||
<i class="glyphicon glyphicon-home"></i>
|
||||
{{T "torrents"}} </a>
|
||||
<a href="{{ genRoute "user_profile" "id" (print .ID) "username" .Username }}"><i class="glyphicon glyphicon-home"></i>{{T "torrents"}}</a>
|
||||
</li>
|
||||
{{if gt $.User.ID 0 }}
|
||||
{{if CurrentOrAdmin $.User .ID }}
|
||||
<li class="active">
|
||||
<a href="?edit">
|
||||
<i class="glyphicon glyphicon-user"></i>
|
||||
{{T "settings"}} </a>
|
||||
</li>
|
||||
{{end}}
|
||||
{{if CurrentOrAdmin $.User .ID }}
|
||||
<li class="active">
|
||||
<a href="{{ genRoute "user_profile_edit" "id" (print .ID) "username" .Username }}"><i class="glyphicon glyphicon-user"></i>{{T "settings"}}</a>
|
||||
</li>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -28,7 +28,12 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>Uploader</td>
|
||||
<td><a href="{{$.URL.Parse (printf "/user/%d/-" .UploaderID) }}">{{.UploaderName}}</a></td>
|
||||
<td>
|
||||
<a href="{{$.URL.Parse (printf "/user/%d/-" .UploaderID) }}">{{.UploaderName}}</a>
|
||||
{{if ne .OldUploader ""}}
|
||||
({{.OldUploader}})
|
||||
{{end}}
|
||||
</td>
|
||||
{{if ne .WebsiteLink ""}}
|
||||
<tr>
|
||||
<td>{{T "Link"}}</td>
|
||||
|
|
566
translations/de-de.all.json
Fichier normal
566
translations/de-de.all.json
Fichier normal
|
@ -0,0 +1,566 @@
|
|||
[
|
||||
{
|
||||
"id": "link",
|
||||
"translation": "Link"
|
||||
},
|
||||
{
|
||||
"id": "verify_email_title",
|
||||
"translation": "Bestätige deine E-Mail Adresse für Nyaapantsu."
|
||||
},
|
||||
{
|
||||
"id": "verify_email_content",
|
||||
"translation": "Klicke unten auf den Link um deine E-Mail Adresse zu verifizieren."
|
||||
},
|
||||
{
|
||||
"id": "reset_password_title",
|
||||
"translation": "Setze dein nyaapantsu Passwort zurück."
|
||||
},
|
||||
{
|
||||
"id": "reset_password_content",
|
||||
"translation": "Klicke unten auf den Link um dein Passwort zurückzusetzen."
|
||||
},
|
||||
{
|
||||
"id":"register_title",
|
||||
"translation": "Neuen Account erstellen"
|
||||
},
|
||||
{
|
||||
"id":"signup_box_title",
|
||||
"translation": "Registriere dich <small>Es ist kostenlos und wird immer so bleiben.</small>"
|
||||
},
|
||||
{
|
||||
"id":"username",
|
||||
"translation": "Nutzername"
|
||||
},
|
||||
{
|
||||
"id":"email_address_or_username",
|
||||
"translation": "E-Mail-Adresse oder Nutzername"
|
||||
},
|
||||
{
|
||||
"id":"email_address",
|
||||
"translation": "E-Mail Adresse"
|
||||
},
|
||||
{
|
||||
"id":"password",
|
||||
"translation": "Passwort"
|
||||
},
|
||||
{
|
||||
"id":"confirm_password",
|
||||
"translation": "Bestätige dein Passwort"
|
||||
},
|
||||
{
|
||||
"id":"i_agree",
|
||||
"translation": "Ich stimme zu"
|
||||
},
|
||||
{
|
||||
"id":"terms_conditions_confirm",
|
||||
"translation": "Mit Klick auf <strong class=\"label label-primary\">Registrieren</strong> stimmst du den <a href=\"#\" data-toggle=\"modal\" data-target=\"#t_and_c_m\">Nutzungsbedingungen</a> sowie der Nutzung von Cookies zu."
|
||||
},
|
||||
{
|
||||
"id":"signin",
|
||||
"translation": "Einloggen"
|
||||
},
|
||||
{
|
||||
"id":"register",
|
||||
"translation": "Registrieren"
|
||||
},
|
||||
{
|
||||
"id":"terms_conditions",
|
||||
"translation": "Nutzungsbedingungen"
|
||||
},
|
||||
{
|
||||
"id":"terms_conditions_full",
|
||||
"translation": "Coming sooner than you might expect..."
|
||||
},
|
||||
{
|
||||
"id":"remember_me",
|
||||
"translation": "Login speichern"
|
||||
},
|
||||
{
|
||||
"id":"forgot_password",
|
||||
"translation": "Passwort vergessen?"
|
||||
},
|
||||
{
|
||||
"id":"sign_in_box_title",
|
||||
"translation": "Bitte logge dich ein"
|
||||
},
|
||||
{
|
||||
"id":"sign_in_title",
|
||||
"translation": "Login"
|
||||
},
|
||||
{
|
||||
"id":"register_success_title",
|
||||
"translation": "Login erfolgreich"
|
||||
},
|
||||
{
|
||||
"id":"sign_up_success",
|
||||
"translation": "Danke für's Registrieren!"
|
||||
},
|
||||
{
|
||||
"id":"verify_success",
|
||||
"translation": "<i style=\"color:limegreen\" class=\"glyphicon glyphicon-ok-circle\"></i>Dein Account ist jetzt aktiviert!"
|
||||
},
|
||||
{
|
||||
"id":"signup_verification_email",
|
||||
"translation": "Geschafft, überprüfe deinen Posteingang (und Spam Ordner!) auf eine Bestätigungsmail."
|
||||
},
|
||||
{
|
||||
"id":"signup_verification_noemail",
|
||||
"translation": "Die Registrierung war erfolgreich, du kannst deinen Account jetzt benutzen."
|
||||
},
|
||||
{
|
||||
"id":"settings",
|
||||
"translation": "Profil-Einstellungen"
|
||||
},
|
||||
{
|
||||
"id":"torrents",
|
||||
"translation": "Torrents"
|
||||
},
|
||||
{
|
||||
"id":"follow",
|
||||
"translation": "Folgen"
|
||||
},
|
||||
{
|
||||
"id":"unfollow",
|
||||
"translation": "Nicht mehr Folgen"
|
||||
},
|
||||
{
|
||||
"id":"user_followed_msg",
|
||||
"translation": "Du folgst jetzt %s!"
|
||||
},
|
||||
{
|
||||
"id":"user_unfollowed_msg",
|
||||
"translation": "Du folgst %s nicht mehr!"
|
||||
},
|
||||
{
|
||||
"id":"profile_page",
|
||||
"translation": "Profil von %s"
|
||||
},
|
||||
{
|
||||
"id":"see_more_torrents_from",
|
||||
"translation": "Mehr Torrents von %s "
|
||||
},
|
||||
{
|
||||
"id":"category",
|
||||
"translation": "Kategorie"
|
||||
},
|
||||
{
|
||||
"id": "name",
|
||||
"translation": "Name"
|
||||
},
|
||||
{
|
||||
"id": "date",
|
||||
"translation": "Datum"
|
||||
},
|
||||
{
|
||||
"id": "size",
|
||||
"translation": "Größe"
|
||||
},
|
||||
{
|
||||
"id": "links",
|
||||
"translation": "Links"
|
||||
},
|
||||
{
|
||||
"id": "home",
|
||||
"translation": "Home"
|
||||
},
|
||||
{
|
||||
"id": "error_404",
|
||||
"translation": "Fehler 404"
|
||||
},
|
||||
{
|
||||
"id": "toggle_navigation",
|
||||
"translation": "Navigation umschalten"
|
||||
},
|
||||
{
|
||||
"id": "upload",
|
||||
"translation": "Hochladen"
|
||||
},
|
||||
{
|
||||
"id": "faq",
|
||||
"translation": "FAQ"
|
||||
},
|
||||
{
|
||||
"id": "fap",
|
||||
"translation": "Fap"
|
||||
},
|
||||
{
|
||||
"id": "advanced_search",
|
||||
"translation": "Erweiterte Suche"
|
||||
},
|
||||
{
|
||||
"id": "nothing_here",
|
||||
"translation": "Nichts zu finden."
|
||||
},
|
||||
{
|
||||
"id": "404_not_found",
|
||||
"translation": "404 nichts gefunden"
|
||||
},
|
||||
{
|
||||
"id": "no_torrents_uploaded",
|
||||
"translation": "Es sind noch keine Torrents hochgeladen worden!"
|
||||
},
|
||||
{
|
||||
"id": "profile",
|
||||
"translation": "Profil"
|
||||
},
|
||||
{
|
||||
"id": "sign_out",
|
||||
"translation": "Logout"
|
||||
},
|
||||
{
|
||||
"id": "member",
|
||||
"translation": "Mitglied"
|
||||
},
|
||||
{
|
||||
"id": "sign_in",
|
||||
"translation": "Einloggen"
|
||||
},
|
||||
{
|
||||
"id": "sign_up",
|
||||
"translation": "Registrieren"
|
||||
},
|
||||
{
|
||||
"id": "no_results_found",
|
||||
"translation": "Es wurden keine Ergebnisse gefunden"
|
||||
},
|
||||
{
|
||||
"id": "notice_keep_seeding",
|
||||
"translation": "WICHTIG: WEITER SEEDEN UND DHT AKTIVIEREN"
|
||||
},
|
||||
{
|
||||
"id": "official_nyaapocalipse_faq",
|
||||
"translation": "Offizielles Nyaapocalypse FAQ"
|
||||
},
|
||||
{
|
||||
"id": "links_replacement_mirror",
|
||||
"translation": "Ersatzlinks"
|
||||
},
|
||||
{
|
||||
"id": "what_happened",
|
||||
"translation": "Was ist passiert?"
|
||||
},
|
||||
{
|
||||
"id": "nyaa_se_went_offline",
|
||||
"translation": "nyaa.se und andere Domains (wie nyaatorrents.info) gingen am 01. Mai 2017 offline."
|
||||
},
|
||||
{
|
||||
"id": "its_not_a_ddos",
|
||||
"translation": "Die Seite wurde deaktiviert, es war kein DDoS Angriff."
|
||||
},
|
||||
{
|
||||
"id": "future_not_looking_good",
|
||||
"translation": "Die Zukunft für nyaa sieht schlecht aus. (Die Seite ist tot)"
|
||||
},
|
||||
{
|
||||
"id": "recovery_effort",
|
||||
"translation": "Zur Zeit wird versucht die Seite zu ersetzen."
|
||||
},
|
||||
{
|
||||
"id": "is_everything_lost",
|
||||
"translation": "Ist alles verloren?"
|
||||
},
|
||||
{
|
||||
"id": "in_short_no",
|
||||
"translation": "Kurzgesagt, Nein."
|
||||
},
|
||||
{
|
||||
"id": "are_some_things_lost",
|
||||
"translation": "Gibt es Verluste?"
|
||||
},
|
||||
{
|
||||
"id": "answer_is_nyaa_db_lost",
|
||||
"translation": "Wir haben die Torrent-Datenbank von nyaa bis zum <s>5. April</s> 1. Mai. Das heißt es fehlt fast gar nichts."
|
||||
},
|
||||
{
|
||||
"id": "answer_is_sukebei_db_lost",
|
||||
"translation": "Um Sukebei steht es hingegen schlechter. Zurzeit haben wir nur eine Sukebei-Datenbank bis 2016, aber eine neuere Datenbank steht möglicherweise zu Verfügung."
|
||||
},
|
||||
{
|
||||
"id": "how_are_we_recovering",
|
||||
"translation": "Wie läuft die Wiederherstellung ab?"
|
||||
},
|
||||
{
|
||||
"id": "answer_how_are_we_recovering",
|
||||
"translation": "Die obengenannten Datenbanken werden im Moment auf nyaa.pantsu.cat und sukebei.pantsu.cat bereitgestellt. Es gibt eine Suchfunktion und (fast) vollständige Funktionalität von nyaa sollte bald wiederhergestellt sein. Seeder/Leecher Statistiken sind via Scraping möglich und werden in Zukunft vielleicht wiederhergestellt werden, da andere Funktionen Vorrang haben."
|
||||
},
|
||||
{
|
||||
"id": "are_the_trackers_working",
|
||||
"translation": "Funktionieren die Torrents noch?"
|
||||
},
|
||||
{
|
||||
"id": "answer_are_the_trackers_working",
|
||||
"translation": "Auch went die Tracker offline sind, sind die Seeder immernoch mit dem dezentralisierten DHT Netzwerk verbunden. Solange das DHT Netzwerk die Torrentdaten hat, sollte es wie gewohnt weitergehen."
|
||||
},
|
||||
{
|
||||
"id": "how_do_i_download_the_torrents",
|
||||
"translation": "Wie lade ich Torrents herunter?"
|
||||
},
|
||||
{
|
||||
"id": "answer_how_do_i_download_the_torrents",
|
||||
"translation": "Benutze einfach <b>Magnet Links</b>. Deine BitTorrent Software liest den Magnet Link und durchsucht das DHT Netzwerk nach den Metadaten und der Download sollte ohne weiteres funktionieren."
|
||||
},
|
||||
{
|
||||
"id": "magnet_link_should_look_like",
|
||||
"translation": "Ein Magnet Link sollte so aussehen:"
|
||||
},
|
||||
{
|
||||
"id": "which_trackers_do_you_recommend",
|
||||
"translation": "Welche Tracker sind empfohlen?"
|
||||
},
|
||||
{
|
||||
"id": "answer_which_trackers_do_you_recommend",
|
||||
"translation": "Wenn deine Torrents wegen Trackern Probleme machen, solltest du einige dieser hinzufügen:"
|
||||
},
|
||||
{
|
||||
"id": "how_can_i_help",
|
||||
"translation": "Wie kann ich helfen?"
|
||||
},
|
||||
{
|
||||
"id": "answer_how_can_i_help",
|
||||
"translation": "Wenn du Erfahrungen mit Webseitenprogrammierung hast, kannst du dem IRC-Kanal #nyaapantsu auf irc.rizon.net(Englisch) beitreten. Wenn du irgendwelche Datenbanken hast, insbesondere für Sukebei, <b>LAD SIE HOCH</b>."
|
||||
},
|
||||
{
|
||||
"id": "your_design_sucks_found_a_bug",
|
||||
"translation": "Euer Design ist mies / Ich habe einen Fehler gefunden"
|
||||
},
|
||||
{
|
||||
"id": "why_written_in_go",
|
||||
"translation": "Warum in Gottesnamen ist eurer Code in Go geschrieben?"
|
||||
},
|
||||
{
|
||||
"id": "authors_favorite_language",
|
||||
"translation": "Weil es die Lieblingsprogrammiersprache des Entwicklers ist."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
"translation": "nyaa.pantsu.cat und sukebei.pantsu.cat stellen keine Dateien bereit."
|
||||
},
|
||||
{
|
||||
"id": "upload_magnet",
|
||||
"translation": "Magnet hochladen"
|
||||
},
|
||||
{
|
||||
"id": "torrent_file",
|
||||
"translation": "Torrent Datei"
|
||||
},
|
||||
{
|
||||
"id": "uploading_file_prefills_fields",
|
||||
"translation": "Beim hochladen einer Torrentdatei werden einige Felder automatisch ausgefüllt (wird bevorzugt)."
|
||||
},
|
||||
{
|
||||
"id": "magnet_link",
|
||||
"translation": "Magnet Link"
|
||||
},
|
||||
{
|
||||
"id": "all_categories",
|
||||
"translation": "Alle Kategorien"
|
||||
},
|
||||
{
|
||||
"id": "anime",
|
||||
"translation": "Anime"
|
||||
},
|
||||
{
|
||||
"id": "anime_amv",
|
||||
"translation": "Anime - Anime Music Video"
|
||||
},
|
||||
{
|
||||
"id": "anime_english_translated",
|
||||
"translation": "Anime - Englisch-übersetzt"
|
||||
},
|
||||
{
|
||||
"id": "anime_non_english_translated",
|
||||
"translation": "Anime - andere Übersetzungen"
|
||||
},
|
||||
{
|
||||
"id": "anime_raw",
|
||||
"translation": "Anime - Raw"
|
||||
},
|
||||
{
|
||||
"id": "audio",
|
||||
"translation": "Audio"
|
||||
},
|
||||
{
|
||||
"id": "audio_lossless",
|
||||
"translation": "Audio - Verlustfrei"
|
||||
},
|
||||
{
|
||||
"id": "audio_lossy",
|
||||
"translation": "Audio - Verlustbehaftet"
|
||||
},
|
||||
{
|
||||
"id": "literature",
|
||||
"translation": "Literatur"
|
||||
},
|
||||
{
|
||||
"id": "literature_english_translated",
|
||||
"translation": "Literatur - Englisch-übersetzt"
|
||||
},
|
||||
{
|
||||
"id": "literature_raw",
|
||||
"translation": "Literatur - Raw"
|
||||
},
|
||||
{
|
||||
"id": "literature_non_english_translated",
|
||||
"translation": "Literature - andere Übersetzungen"
|
||||
},
|
||||
{
|
||||
"id": "live_action",
|
||||
"translation": "Live Action"
|
||||
},
|
||||
{
|
||||
"id": "live_action_english_translated",
|
||||
"translation": "Live Action - Englisch-übersetzt"
|
||||
},
|
||||
{
|
||||
"id": "live_action_idol_pv",
|
||||
"translation": "Live Action - Idol/Promotional Video"
|
||||
},
|
||||
{
|
||||
"id": "live_action_non_english_translated",
|
||||
"translation": "Live Action - andere Übersetzungen"
|
||||
},
|
||||
{
|
||||
"id": "live_action_raw",
|
||||
"translation": "Live Action - Raw"
|
||||
},
|
||||
{
|
||||
"id": "pictures",
|
||||
"translation": "Bilder"
|
||||
},
|
||||
{
|
||||
"id": "pictures_graphics",
|
||||
"translation": "Bilder - Grafiken"
|
||||
},
|
||||
{
|
||||
"id": "pictures_photos",
|
||||
"translation": "Bilder - Fotos"
|
||||
},
|
||||
{
|
||||
"id": "software",
|
||||
"translation": "Software"
|
||||
},
|
||||
{
|
||||
"id": "software_applications",
|
||||
"translation": "Software - Programme"
|
||||
},
|
||||
{
|
||||
"id": "software_games",
|
||||
"translation": "Software - Spiele"
|
||||
},
|
||||
{
|
||||
"id": "torrent_description",
|
||||
"translation": "Torrent Beschreibung"
|
||||
},
|
||||
{
|
||||
"id": "description_markdown_notice",
|
||||
"translation": "Markdown kann in der Beschreibung verwendet werden."
|
||||
},
|
||||
{
|
||||
"id": "show_all",
|
||||
"translation": "Alle anzeigen"
|
||||
},
|
||||
{
|
||||
"id": "filter_remakes",
|
||||
"translation": "Remakes herausfiltern"
|
||||
},
|
||||
{
|
||||
"id": "trusted",
|
||||
"translation": "Trusted"
|
||||
},
|
||||
{
|
||||
"id": "id",
|
||||
"translation": "ID"
|
||||
},
|
||||
{
|
||||
"id": "downloads",
|
||||
"translation": "Downloads"
|
||||
},
|
||||
{
|
||||
"id": "descending",
|
||||
"translation": "Absteigend"
|
||||
},
|
||||
{
|
||||
"id": "ascending",
|
||||
"translation": "Aufsteigend"
|
||||
},
|
||||
{
|
||||
"id": "search",
|
||||
"translation": "Suche"
|
||||
},
|
||||
{
|
||||
"id": "hash",
|
||||
"translation": "Hash"
|
||||
},
|
||||
{
|
||||
"id": "description",
|
||||
"translation": "Beschreibung"
|
||||
},
|
||||
{
|
||||
"id": "comments",
|
||||
"translation": "Kommentare"
|
||||
},
|
||||
{
|
||||
"id": "submit_a_comment_as_username",
|
||||
"translation": "Kommentar als %s verfassen"
|
||||
},
|
||||
{
|
||||
"id": "submit_a_comment_as_anonymous",
|
||||
"translation": "anonymen Kommentar verfassen"
|
||||
},
|
||||
{
|
||||
"id": "submit",
|
||||
"translation": "Absenden"
|
||||
},
|
||||
{
|
||||
"id": "personal_info",
|
||||
"translation": "Persönliche Informationen"
|
||||
},
|
||||
{
|
||||
"id": "language",
|
||||
"translation": "Sprache"
|
||||
},
|
||||
{
|
||||
"id": "current_password",
|
||||
"translation": "Derzeitiges Passwort"
|
||||
},
|
||||
{
|
||||
"id": "role",
|
||||
"translation": "Rolle"
|
||||
},
|
||||
{
|
||||
"id": "banned",
|
||||
"translation": "Gebannt"
|
||||
},
|
||||
{
|
||||
"id": "default",
|
||||
"translation": "Standard"
|
||||
},
|
||||
{
|
||||
"id": "trusted_member",
|
||||
"translation": "Trusted"
|
||||
},
|
||||
{
|
||||
"id": "moderator",
|
||||
"translation": "Moderator"
|
||||
},
|
||||
{
|
||||
"id": "save_changes",
|
||||
"translation": "Änderungen speichern"
|
||||
},
|
||||
{
|
||||
"id": "profile_updated",
|
||||
"translation": "Dein Profil wurde entsprechend geändert!"
|
||||
},
|
||||
{
|
||||
"id": "delete_account",
|
||||
"translation": "Profil löschen"
|
||||
},
|
||||
{
|
||||
"id": "delete_account_confirm",
|
||||
"translation": "Bist du sicher das du dein Profil löschen möchtest?"
|
||||
},
|
||||
{
|
||||
"id": "delete_success",
|
||||
"translation": "Dein Profil wurde erfolgreich gelöscht!"
|
||||
}
|
||||
]
|
|
@ -328,7 +328,7 @@
|
|||
"translation": "Archivo Torrent"
|
||||
},
|
||||
{
|
||||
"id": "uploading_torrent_prefills_fields",
|
||||
"id": "uploading_file_prefills_fields",
|
||||
"translation": "Subir un archivo torrent permite pre-llenar algunos campos, esto es recomendado."
|
||||
},
|
||||
{
|
||||
|
|
|
@ -117,7 +117,19 @@
|
|||
},
|
||||
{
|
||||
"id":"follow",
|
||||
"translation": "Suivre"
|
||||
"translation": "S'abonner"
|
||||
},
|
||||
{
|
||||
"id":"unfollow",
|
||||
"translation": "Se désabonner"
|
||||
},
|
||||
{
|
||||
"id":"user_followed_msg",
|
||||
"translation": "Vous vous êtes abonné à %s!"
|
||||
},
|
||||
{
|
||||
"id":"user_unfollowed_msg",
|
||||
"translation": "Vous vous êtes désabonné de %s!"
|
||||
},
|
||||
{
|
||||
"id":"profile_page",
|
||||
|
@ -213,15 +225,15 @@
|
|||
},
|
||||
{
|
||||
"id": "notice_keep_seeding",
|
||||
"translation": "IMPORTANT : Continuez le partage et activez le DHT."
|
||||
"translation": "IMPORTANT : Continuez le partage et activez DHT sur votre client BitTorrent."
|
||||
},
|
||||
{
|
||||
"id": "official_nyaapocalipse_faq",
|
||||
"translation": "FAQ Officielle du Nyaapocalypse"
|
||||
"translation": "FAQ Officielle de la Nyaapocalypse"
|
||||
},
|
||||
{
|
||||
"id": "links_replacement_mirror",
|
||||
"translation": "Liens pour le remplacement/miroir"
|
||||
"translation": "Liens miroir"
|
||||
},
|
||||
{
|
||||
"id": "what_happened",
|
||||
|
@ -233,15 +245,15 @@
|
|||
},
|
||||
{
|
||||
"id": "its_not_a_ddos",
|
||||
"translation": "Ils ont été désactivés, ce n'est pas une attaque DDoS comme d'habitude."
|
||||
"translation": "Ils ont été désactivés et soumis à suppression, ce n'est pas une attaque DDoS comme d'habitude."
|
||||
},
|
||||
{
|
||||
"id": "future_not_looking_good",
|
||||
"translation": "Les persectives d'avenir pour Nyaa ne sont pas bonnes. (C'est mort)"
|
||||
"translation": "Nyaa est vraisemblablement mort pour de bon."
|
||||
},
|
||||
{
|
||||
"id": "recovery_effort",
|
||||
"translation": "Un travail de récupération se déroule actuellement."
|
||||
"translation": "Un travail de récupération est en cours."
|
||||
},
|
||||
{
|
||||
"id": "is_everything_lost",
|
||||
|
@ -253,23 +265,23 @@
|
|||
},
|
||||
{
|
||||
"id": "are_some_things_lost",
|
||||
"translation": "Certaines choses sont-elles perdues ?"
|
||||
"translation": "Qu'est-ce qui a pu être récupéré ?"
|
||||
},
|
||||
{
|
||||
"id": "answer_is_nyaa_db_lost",
|
||||
"translation": "Nous avons une base de données des torrents datant du <s>5 Avril</s> 1er Mai. Ce qui signifie que presque rien n'est perdu."
|
||||
"translation": "Nous avons une base de données des torrents de Nyaa datant du <s>5 Avril</s> 1er Mai. Ce qui signifie que presque rien n'a été perdu."
|
||||
},
|
||||
{
|
||||
"id": "answer_is_sukebei_db_lost",
|
||||
"translation": "Sukebei, cependant, semble en plus mauvais état. Actuellement nous avons seulement des bases de données datant de 2016, mais une base de donnée plus récente pourrait être disponible à l'usage."
|
||||
"translation": "Sukebei, cependant, semble en plus mauvais état. La base de donnée actuelle remonte à 2016, mais une version plus récente pourrait être disponible à l'usage."
|
||||
},
|
||||
{
|
||||
"id": "how_are_we_recovering",
|
||||
"translation": "Comment récupérons-nous ?"
|
||||
"translation": "Où en est le développement ?"
|
||||
},
|
||||
{
|
||||
"id": "answer_how_are_we_recovering",
|
||||
"translation": "Les bases de données mentionnées sont hébergées sur nyaa.pantsu.cat et sukebei.pantsu.cat. Une fonction de recherche est disponible et (presque) toutes les fonctionnalités de Nyaa devraient arriver. Les statistiques Seeders/Leechers peuvent être obtenues par 'scraping' et pourraient être restaurées à l'avenir, toutefois d'autres fonctionnalités prennent la priorité en ce moment."
|
||||
"translation": "Les bases de données mentionnées sont hébergées sur nyaa.pantsu.cat et sukebei.pantsu.cat. Une fonction de recherche est disponible et (presque) toutes les fonctionnalités de Nyaa devraient arriver. Les statistiques seeders/leechers peuvent être obtenues par 'scraping' et pourraient être restaurées à l'avenir, toutefois d'autres fonctionnalités prennent la priorité en ce moment."
|
||||
},
|
||||
{
|
||||
"id": "are_the_trackers_working",
|
||||
|
@ -317,7 +329,7 @@
|
|||
},
|
||||
{
|
||||
"id": "authors_favorite_language",
|
||||
"translation": "C'est le langage de programmation favori de l'auteur."
|
||||
"translation": "C'est le langage de programmation favori du développeur."
|
||||
},
|
||||
{
|
||||
"id": "nyaa_pantsu_dont_host_files",
|
||||
|
@ -333,7 +345,7 @@
|
|||
},
|
||||
{
|
||||
"id": "uploading_file_prefills_fields",
|
||||
"translation": "Uploader un fichier torrent permet de pré-remplir certains champs, ce qui est recommandé."
|
||||
"translation": "Il est recommandé d'uploader un fichier torrent afin de pré-remplir certains champs."
|
||||
},
|
||||
{
|
||||
"id": "magnet_link",
|
||||
|
@ -503,13 +515,17 @@
|
|||
"id": "personal_info",
|
||||
"translation": "Informations personnelles"
|
||||
},
|
||||
{
|
||||
"id": "email",
|
||||
"translation": "Adresse email"
|
||||
},
|
||||
{
|
||||
"id": "language",
|
||||
"translation": "Langue"
|
||||
},
|
||||
{
|
||||
"id": "current_password",
|
||||
"translation": "Mot de passe actuel"
|
||||
"translation": "Mot de passe actuel "
|
||||
},
|
||||
{
|
||||
"id": "role",
|
||||
|
|
|
@ -29,11 +29,11 @@
|
|||
},
|
||||
{
|
||||
"id":"username",
|
||||
"translation": "별명"
|
||||
"translation": "아이디"
|
||||
},
|
||||
{
|
||||
"id":"email_address_or_username",
|
||||
"translation": "이메일 주소 혹은 별명"
|
||||
"translation": "이메일 주소 또는 아이디"
|
||||
},
|
||||
{
|
||||
"id":"email_address",
|
||||
|
@ -61,7 +61,7 @@
|
|||
},
|
||||
{
|
||||
"id":"register",
|
||||
"translation": "가입"
|
||||
"translation": "회원가입"
|
||||
},
|
||||
{
|
||||
"id":"terms_conditions",
|
||||
|
@ -73,15 +73,15 @@
|
|||
},
|
||||
{
|
||||
"id":"remember_me",
|
||||
"translation": "로그인 유지"
|
||||
"translation": "로그인 상태 유지"
|
||||
},
|
||||
{
|
||||
"id":"forgot_password",
|
||||
"translation": "암호를 잊어버리셨나요?"
|
||||
"translation": "비밀번호 찾기"
|
||||
},
|
||||
{
|
||||
"id":"sign_in_box_title",
|
||||
"translation": "로그인하세요"
|
||||
"translation": "로그인 하세요"
|
||||
},
|
||||
{
|
||||
"id":"sign_in_title",
|
||||
|
@ -328,7 +328,7 @@
|
|||
"translation": "토렌트 파일"
|
||||
},
|
||||
{
|
||||
"id": "uploading_torrent_prefills_fields",
|
||||
"id": "uploading_file_prefills_fields",
|
||||
"translation": "토렌트 파일을 업로드하면 몇몇 필드가 자동으로 채워집니다. 추천드립니다."
|
||||
},
|
||||
{
|
||||
|
|
Le diff du fichier est caché, car celui-ci est trop grand
Voir la diff
|
@ -328,7 +328,7 @@
|
|||
"translation": "Торрент-файл"
|
||||
},
|
||||
{
|
||||
"id": "uploading_torrent_prefills_fields",
|
||||
"id": "uploading_file_prefills_fields",
|
||||
"translation": "Загрузка торрент-файла позволяет предварительно заполнить некоторые поля, это рекомендуется."
|
||||
},
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
},
|
||||
{
|
||||
"id": "verify_email_title",
|
||||
"translation": "ยืนยันอีเมลสำหรับ NyaaPantsu."
|
||||
"translation": "ยืนยันอีเมลสำหรับ NyaaPantsu"
|
||||
},
|
||||
{
|
||||
"id": "verify_email_content",
|
||||
|
@ -249,7 +249,7 @@
|
|||
},
|
||||
{
|
||||
"id": "future_not_looking_good",
|
||||
"translation": "อนาคตข้างหน้าสำหรับ Nyaa ดูไม่ดีเท่าไหร่ (ตายสนิท)"
|
||||
"translation": "และอนาคตข้างหน้าสำหรับ Nyaa ดูจะไม่ดีเท่าไหร่ (ตายสนิท)"
|
||||
},
|
||||
{
|
||||
"id": "recovery_effort",
|
||||
|
@ -273,7 +273,7 @@
|
|||
},
|
||||
{
|
||||
"id": "answer_is_sukebei_db_lost",
|
||||
"translation": "สำหรับ Sukebei นั่นโชคร้ายหน่อย ที่ในตอนนี้เรามีฐานข้อมูลของ Sukebei ถึงแค่ช่วงปี 2016 แต่อาจจะมีฐานข้อมูลที่ใหม่กว่าให้ใช้ก็ได้"
|
||||
"translation": "สำหรับ Sukebei นั่นโชคร้ายหน่อย ที่ในตอนนี้เรามีฐานข้อมูลของ Sukebei ถึงแค่ช่วงปี 2016 แต่อาจจะมีฐานข้อมูลที่ใหม่กว่านี้ให้ใช้ก็ได้"
|
||||
},
|
||||
{
|
||||
"id": "how_are_we_recovering",
|
||||
|
@ -281,15 +281,15 @@
|
|||
},
|
||||
{
|
||||
"id": "answer_how_are_we_recovering",
|
||||
"translation": "ฐานข้อมูลดังกล่าวถูกใช้งานกับ nyaa.pantsu.cat และ sukebei.pantsu.cat ซึ่งมีระบบค้นหา และระบบของ Nyaa (เกือบ) ครบถ้วนควรจะมาในเร็วๆ นี้ สถิติผู้ปล่อย/ผู้โหลดอาจจะกู้กลับมาผ่านการ scraping ในอาคนอันใกล้ตั้งแต่ฟีเจอร์อื่นถูกให้ความสำคัญมากกว่าในตอนนี้"
|
||||
"translation": "ฐานข้อมูลดังกล่าวถูกใช้งานกับ nyaa.pantsu.cat และ sukebei.pantsu.cat ซึ่งมีระบบค้นหา และระบบของ Nyaa (เกือบ) ครบถ้วนควรจะมาในเร็วๆ นี้ สถิติผู้ปล่อย/ผู้โหลดที่อาจจะกู้กลับมาผ่านการ Scraping ในอนาคตอันใกล้ตั้งแต่ฟีเจอร์อื่นๆ ถูกให้ความสำคัญมากกว่าในตอนนี้"
|
||||
},
|
||||
{
|
||||
"id": "are_the_trackers_working",
|
||||
"translation": "ตัวทอร์เรนท์ยังใช้ได้รึเปล่า?"
|
||||
"translation": "ตัวทอร์เรนท์จะยังใช้ได้รึเปล่า?"
|
||||
},
|
||||
{
|
||||
"id": "answer_are_the_trackers_working",
|
||||
"translation": "ถ้าตัวทอร์เรนท์ใช้ไม่ได้ ผู้ปล่อย Seed จะยังเชื่อมต่อกันผ่านเครือข่าย DHT ที่ไร้ศูนย์กลาง ตราบที่ไฟล์ยังอยู่ในเครือข่าย DHT ก็ยังโหลดได้"
|
||||
"translation": "ถ้าแทรคเกอร์ล่ม ผู้ปล่อย Seed จะยังเชื่อมต่อกันผ่านเครือข่าย DHT ที่ไร้ศูนย์กลาง ตราบที่ไฟล์ยังอยู่ในเครือข่าย DHT ก็ยังโหลดได้"
|
||||
},
|
||||
{
|
||||
"id": "how_do_i_download_the_torrents",
|
||||
|
@ -301,7 +301,7 @@
|
|||
},
|
||||
{
|
||||
"id": "magnet_link_should_look_like",
|
||||
"translation": "ตัว magnet link ควรจะเป็นลักษณะนี้:"
|
||||
"translation": "ตัว Magnet link ควรจะเป็นลักษณะนี้:"
|
||||
},
|
||||
{
|
||||
"id": "which_trackers_do_you_recommend",
|
||||
|
|
|
@ -92,7 +92,7 @@ func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]
|
|||
if tag.Get("needed") != "" && formElem.Field(i).String() == "" {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Field needed: %s", inputName))
|
||||
}
|
||||
if tag.Get("default") != "" {
|
||||
if formElem.Field(i).String() == "" && tag.Get("default") != "" {
|
||||
formElem.Field(i).SetString(tag.Get("default"))
|
||||
}
|
||||
case "int":
|
||||
|
@ -105,7 +105,7 @@ func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]
|
|||
if tag.Get("needed") != "" && formElem.Field(i).Int() == 0 {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Field needed: %s", inputName))
|
||||
}
|
||||
if tag.Get("default") != "" {
|
||||
if formElem.Field(i).Interface == nil && tag.Get("default") != "" {
|
||||
defaultValue, _ := strconv.Atoi(tag.Get("default"))
|
||||
formElem.Field(i).SetInt(int64(defaultValue))
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]
|
|||
if tag.Get("needed") != "" && formElem.Field(i).Float() == 0 {
|
||||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Field needed: %s", inputName))
|
||||
}
|
||||
if tag.Get("default") != "" {
|
||||
if formElem.Field(i).Interface == nil && tag.Get("default") != "" {
|
||||
defaultValue, _ := strconv.Atoi(tag.Get("default"))
|
||||
formElem.Field(i).SetFloat(float64(defaultValue))
|
||||
}
|
||||
|
@ -130,10 +130,6 @@ func ValidateForm(form interface{}, errorForm map[string][]string) map[string][]
|
|||
errorForm[tag.Get("form")] = append(errorForm[tag.Get("form")], fmt.Sprintf("Wrong value for the input: %s", inputName))
|
||||
}
|
||||
}
|
||||
if tag.Get("default") != "" {
|
||||
defaultValue, _ := strconv.ParseBool(tag.Get("default"))
|
||||
formElem.Field(i).SetBool(defaultValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errorForm
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/ewhal/nyaa/common"
|
||||
"github.com/ewhal/nyaa/db"
|
||||
"github.com/ewhal/nyaa/model"
|
||||
"github.com/ewhal/nyaa/service"
|
||||
"github.com/ewhal/nyaa/service/torrent"
|
||||
"github.com/ewhal/nyaa/util/log"
|
||||
)
|
||||
|
@ -96,7 +97,7 @@ func searchByQuery(r *http.Request, pagenum int, countAll bool) (
|
|||
}
|
||||
|
||||
tor, count, err = cache.Get(search, func() (tor []model.Torrent, count int, err error) {
|
||||
parameters := torrentService.WhereParams{
|
||||
parameters := serviceBase.WhereParams{
|
||||
Params: make([]interface{}, 0, 64),
|
||||
}
|
||||
conditions := make([]string, 0, 64)
|
||||
|
|
26
util/signals/closers.go
Fichier normal
26
util/signals/closers.go
Fichier normal
|
@ -0,0 +1,26 @@
|
|||
package signals
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
closeAccess sync.Mutex
|
||||
closers []io.Closer
|
||||
)
|
||||
|
||||
// RegisterCloser adds an io.Closer to close on interrupt
|
||||
func RegisterCloser(c io.Closer) {
|
||||
closeAccess.Lock()
|
||||
closers = append(closers, c)
|
||||
closeAccess.Unlock()
|
||||
}
|
||||
|
||||
func closeClosers() {
|
||||
closeAccess.Lock()
|
||||
for idx := range closers {
|
||||
closers[idx].Close()
|
||||
}
|
||||
closeAccess.Unlock()
|
||||
}
|
7
util/signals/interrupt.go
Fichier normal
7
util/signals/interrupt.go
Fichier normal
|
@ -0,0 +1,7 @@
|
|||
package signals
|
||||
|
||||
// handle interrupt signal, platform independent
|
||||
func interrupted() {
|
||||
closeClosers()
|
||||
handleInterrupt()
|
||||
}
|
|
@ -21,7 +21,7 @@ func handleReload() {
|
|||
// returns when done
|
||||
func Handle() {
|
||||
chnl := make(chan os.Signal)
|
||||
signal.Notify(chnl, syscall.SIGHUP)
|
||||
signal.Notify(chnl, syscall.SIGHUP, os.Interrupt)
|
||||
for {
|
||||
sig, ok := <-chnl
|
||||
if !ok {
|
||||
|
@ -31,8 +31,17 @@ func Handle() {
|
|||
case syscall.SIGHUP:
|
||||
handleReload()
|
||||
break
|
||||
case os.Interrupt:
|
||||
interrupted()
|
||||
return
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unix implementation of interrupt
|
||||
// called in interrupted()
|
||||
func handleInterrupt() {
|
||||
// XXX: put unix specific cleanup here as needed
|
||||
}
|
||||
|
|
|
@ -2,7 +2,34 @@
|
|||
|
||||
package signals
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
func Handle() {
|
||||
// windows has no sighup LOOOOL, this does nothing
|
||||
// TODO: Something about SIGHUP for Windows
|
||||
|
||||
chnl := make(chan os.Signal)
|
||||
signal.Notify(chnl, os.Interrupt)
|
||||
for {
|
||||
sig, ok := <-chnl
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
switch sig {
|
||||
case os.Interrupt:
|
||||
// this also closes listeners
|
||||
interrupted()
|
||||
return
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// win32 interrupt handler
|
||||
// called in interrupted()
|
||||
func handleInterrupt() {
|
||||
// XXX: put any win32 specific cleanup here as needed
|
||||
}
|
||||
|
|
Référencer dans un nouveau ticket