diff --git a/db/gorm.go b/db/gorm.go index 01d10832..b2c57d93 100644 --- a/db/gorm.go +++ b/db/gorm.go @@ -4,7 +4,7 @@ import ( "github.com/ewhal/nyaa/config" "github.com/ewhal/nyaa/model" "github.com/ewhal/nyaa/util/log" - "github.com/jinzhu/gorm" + "github.com/azhao12345/gorm" _ "github.com/jinzhu/gorm/dialects/postgres" _ "github.com/jinzhu/gorm/dialects/sqlite" ) @@ -30,7 +30,7 @@ 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{}) + db.AutoMigrate(&model.Torrent{}, &model.UserFollows{}, &model.User{}, &model.Comment{}, &model.OldComment{}, &model.TorrentReport{}) } return db, nil diff --git a/model/torrent.go b/model/torrent.go index 59e1a95d..7a72ce1d 100644 --- a/model/torrent.go +++ b/model/torrent.go @@ -61,6 +61,20 @@ func (t Torrent) Size() (s int) { } return + +} + +// 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 @@ -97,6 +111,20 @@ type TorrentJSON struct { 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...) @@ -149,3 +177,11 @@ 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 +} diff --git a/router/router.go b/router/router.go index 96656b29..7aa7fe7e 100644 --- a/router/router.go +++ b/router/router.go @@ -39,7 +39,6 @@ func init() { gzipUserFollowHandler := handlers.CompressHandler(http.HandlerFunc(UserFollowHandler)) gzipUserProfileFormHandler := handlers.CompressHandler(http.HandlerFunc(UserProfileFormHandler)) - gzipIndexModPanel := handlers.CompressHandler(http.HandlerFunc(IndexModPanel)) gzipTorrentsListPanel := handlers.CompressHandler(http.HandlerFunc(TorrentsListPanel)) gzipUsersListPanel := handlers.CompressHandler(http.HandlerFunc(UsersListPanel)) @@ -49,7 +48,10 @@ 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)) Router = mux.NewRouter() @@ -91,8 +93,16 @@ func init() { Router.Handle("/mod/torrent/delete", gzipTorrentDeleteModPanel).Name("mod_tdelete") Router.Handle("/mod/comment/delete", gzipCommentDeleteModPanel).Name("mod_cdelete") + //reporting a torrent + Router.HandleFunc("/report/{id}", ReportTorrentHandler).Methods("POST").Name("post_comment") Router.PathPrefix("/captcha").Methods("GET").HandlerFunc(captcha.ServeFiles) + //Router.Handle("/report/create", gzipTorrentReportCreateHandler).Name("torrent_report_create").Methods("POST") + // 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.NotFoundHandler = http.HandlerFunc(NotFoundHandler) } diff --git a/router/template.go b/router/template.go index e206ffac..60940c48 100644 --- a/router/template.go +++ b/router/template.go @@ -7,7 +7,7 @@ import ( var TemplateDir = "templates" -var homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate, viewVerifySuccessTemplate, viewProfileTemplate, viewProfileEditTemplate, viewUserDeleteTemplate, notFoundTemplate *template.Template +var torrentReportTemplate, homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate, viewVerifySuccessTemplate, viewProfileTemplate, viewProfileEditTemplate, viewUserDeleteTemplate, notFoundTemplate *template.Template type templateLoader struct { templ **template.Template @@ -18,6 +18,11 @@ 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", diff --git a/router/templateVariables.go b/router/templateVariables.go index aa92dc9e..126b012d 100644 --- a/router/templateVariables.go +++ b/router/templateVariables.go @@ -132,6 +132,10 @@ type PanelTorrentEdVbs struct { Torrent model.Torrent } +type ViewTorrentReportsVariables struct { + Torrents []model.TorrentReportJson +} + /* * Variables used by the upper ones */ diff --git a/router/torrentReportHandler.go b/router/torrentReportHandler.go new file mode 100644 index 00000000..74907c0d --- /dev/null +++ b/router/torrentReportHandler.go @@ -0,0 +1,78 @@ +package router + +import ( + "net/http" + + "github.com/ewhal/nyaa/model" + "github.com/ewhal/nyaa/service/moderation" + "github.com/ewhal/nyaa/service/user/permission" +) +/* +func SanitizeTorrentReport(torrentReport *model.TorrentReport) { + // TODO unescape html ? + return +} + +func IsValidTorrentReport() bool { + // TODO Validate, see if user_id already reported, see if torrent exists + return true +} + +// TODO Only allow moderators for each action in this file +func CreateTorrentReportHandler(w http.ResponseWriter, r *http.Request) { + var torrentReport model.TorrentReport + var err error + + modelHelper.BindValueForm(&torrentReport, r) + if IsValidTorrentReport() { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + SanitizeTorrentReport(&torrentReport) + _, err = moderationService.CreateTorrentReport(torrentReport) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func DeleteTorrentReportHandler(w http.ResponseWriter, r *http.Request) { + // TODO Figure out how to get torrent report id from form + var id int + var err error + _, err = moderationService.DeleteTorrentReport(id) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} +*/ +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) { + // TODO Figure out how to get torrent report id from form + var err error + var id string + _, err = torrentService.DeleteTorrent(id) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +}*/ diff --git a/router/viewTorrentHandler.go b/router/viewTorrentHandler.go index b9f12c1c..ccc51edb 100644 --- a/router/viewTorrentHandler.go +++ b/router/viewTorrentHandler.go @@ -63,3 +63,32 @@ err = db.ORM.Create(&comment).Error http.Error(w, err.Error(), http.StatusInternalServerError) } } + +func ReportTorrentHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + id := vars["id"] + + userCaptcha := captcha.Extract(r) + if !captcha.Authenticate(userCaptcha) { + http.Error(w, "bad captcha", 403) + } + currentUser := GetUser(r) + + idNum, err := strconv.Atoi(id) + + userID := currentUser.ID + report := model.TorrentReport{Description: r.FormValue("report_type"), TorrentID: uint(idNum), UserID: userID} + + err = db.ORM.Create(&report).Error + if err != nil { + util.SendError(w, err, 500) + return + } + + url, err := Router.Get("view_torrent").URL("id", id) + if err == nil { + http.Redirect(w, r, url.String(), 302) + } else { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} diff --git a/service/moderation/torrent_report.go b/service/moderation/torrent_report.go new file mode 100644 index 00000000..ebce2555 --- /dev/null +++ b/service/moderation/torrent_report.go @@ -0,0 +1,38 @@ +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 +} diff --git a/service/torrent/torrent.go b/service/torrent/torrent.go index a099bea4..8667ff6f 100644 --- a/service/torrent/torrent.go +++ b/service/torrent/torrent.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" "net/http" + "github.com/ewhal/nyaa/config" "github.com/ewhal/nyaa/db" "github.com/ewhal/nyaa/model" diff --git a/templates/torrent_report.html b/templates/torrent_report.html new file mode 100755 index 00000000..ff93de86 --- /dev/null +++ b/templates/torrent_report.html @@ -0,0 +1,8 @@ + + + + {{ range .Torrents}} + + {{end}} +
{{ .Torrent.Name }}{{.User}}{{.Description}}Delete
+ diff --git a/templates/view.html b/templates/view.html index 3fcdf2cb..30bbe67a 100644 --- a/templates/view.html +++ b/templates/view.html @@ -92,17 +92,17 @@ - {{end}}