diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index fd8169c1..9b1847a5 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.sqlite +*.db-journal *.db *.sql main @@ -14,3 +15,7 @@ templates/*.html.go *.backup tags *.retry + +# emacs temp files +*\#* +*~ \ No newline at end of file diff --git a/db/gorm.go b/db/gorm.go index b2c57d93..8872bd17 100644 --- a/db/gorm.go +++ b/db/gorm.go @@ -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 diff --git a/main.go b/main.go index a0c8a3a4..3c89c7a8 100644 --- a/main.go +++ b/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) + } + } } diff --git a/model/report.go b/model/report.go new file mode 100644 index 00000000..ae158448 --- /dev/null +++ b/model/report.go @@ -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 +} diff --git a/model/torrent.go b/model/torrent.go index 55eb2105..ef8024d6 100644 --- a/model/torrent.go +++ b/model/torrent.go @@ -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 -} diff --git a/model/user.go b/model/user.go index 4a01c168..1e1e3ca7 100644 --- a/model/user.go +++ b/model/user.go @@ -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 +} diff --git a/network/closer.go b/network/closer.go new file mode 100644 index 00000000..267db4b7 --- /dev/null +++ b/network/closer.go @@ -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) +} diff --git a/network/graceful.go b/network/graceful.go new file mode 100644 index 00000000..bcf4555a --- /dev/null +++ b/network/graceful.go @@ -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), + } +} diff --git a/network/network.go b/network/network.go index 89b249b6..d851da0a 100644 --- a/network/network.go +++ b/network/network.go @@ -20,5 +20,8 @@ func CreateHTTPListener(conf *config.Config) (l net.Listener, err error) { l = s } } + if l != nil { + l = WrapListener(l) + } return } diff --git a/public/css/style.css b/public/css/style.css index c39c0238..02cec37b 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -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; -} \ No newline at end of file + color: #616161; + text-shadow: -1px -1px #999999; +} diff --git a/router/apiHandler.go b/router/apiHandler.go index a01fb58e..2939ea50 100644 --- a/router/apiHandler.go +++ b/router/apiHandler.go @@ -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") diff --git a/router/modpanel.go b/router/modpanel.go index 6b2ad5af..d48c5b5c 100644 --- a/router/modpanel.go +++ b/router/modpanel.go @@ -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) diff --git a/router/router.go b/router/router.go old mode 100644 new mode 100755 index 998c2a0b..da317cec --- a/router/router.go +++ b/router/router.go @@ -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) } diff --git a/router/template.go b/router/template.go index 60940c48..e206ffac 100644 --- a/router/template.go +++ b/router/template.go @@ -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", diff --git a/router/templateFunctions.go b/router/templateFunctions.go index 9f974f71..51e497ec 100644 --- a/router/templateFunctions.go +++ b/router/templateFunctions.go @@ -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 + "