Comments Admin pagination

This commit is contained in:
Jan-Lukas Else 2021-02-17 16:07:42 +01:00
parent 4f31d12975
commit 175c78624b
9 changed files with 241 additions and 137 deletions

View File

@ -104,20 +104,24 @@ func checkCommentTarget(w http.ResponseWriter, r *http.Request) string {
return targetURL.Path
}
func commentsAdmin(w http.ResponseWriter, r *http.Request) {
comments, err := getComments()
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
render(w, templateCommentsAdmin, &renderData{
Data: comments,
})
type commentsRequestConfig struct {
offset, limit int
}
func getComments() ([]*comment, error) {
func buildCommentsQuery(config *commentsRequestConfig) (query string, args []interface{}) {
args = []interface{}{}
query = "select id, target, name, website, comment from comments order by id desc"
if config.limit != 0 || config.offset != 0 {
query += " limit @limit offset @offset"
args = append(args, sql.Named("limit", config.limit), sql.Named("offset", config.offset))
}
return
}
func getComments(config *commentsRequestConfig) ([]*comment, error) {
comments := []*comment{}
rows, err := appDbQuery("select id, target, name, website, comment from comments order by id desc")
query, args := buildCommentsQuery(config)
rows, err := appDbQuery(query, args...)
if err != nil {
return nil, err
}
@ -132,19 +136,15 @@ func getComments() ([]*comment, error) {
return comments, nil
}
func commentsAdminDelete(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.FormValue("commentid"))
func countComments(config *commentsRequestConfig) (count int, err error) {
query, params := buildCommentsQuery(config)
query = "select count(*) from (" + query + ")"
row, err := appDbQueryRow(query, params...)
if err != nil {
serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
err = deleteComment(id)
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
purgeCache()
http.Redirect(w, r, ".", http.StatusFound)
err = row.Scan(&count)
return
}
func deleteComment(id int) error {

98
commentsAdmin.go Normal file
View File

@ -0,0 +1,98 @@
package main
import (
"fmt"
"net/http"
"reflect"
"strconv"
"github.com/go-chi/chi"
"github.com/vcraescu/go-paginator"
)
type commentsPaginationAdapter struct {
config *commentsRequestConfig
nums int64
}
func (p *commentsPaginationAdapter) Nums() (int64, error) {
if p.nums == 0 {
nums, _ := countComments(p.config)
p.nums = int64(nums)
}
return p.nums, nil
}
func (p *commentsPaginationAdapter) Slice(offset, length int, data interface{}) error {
modifiedConfig := *p.config
modifiedConfig.offset = offset
modifiedConfig.limit = length
comments, err := getComments(&modifiedConfig)
reflect.ValueOf(data).Elem().Set(reflect.ValueOf(&comments).Elem())
return err
}
func commentsAdmin(blog, commentPath string) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
// Adapter
pageNoString := chi.URLParam(r, "page")
pageNo, _ := strconv.Atoi(pageNoString)
p := paginator.New(&commentsPaginationAdapter{config: &commentsRequestConfig{}}, 5)
p.SetPage(pageNo)
var comments []*comment
err := p.Results(&comments)
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
// Navigation
var hasPrev, hasNext bool
var prevPage, nextPage int
var prevPath, nextPath string
hasPrev, _ = p.HasPrev()
if hasPrev {
prevPage, _ = p.PrevPage()
} else {
prevPage, _ = p.Page()
}
if prevPage < 2 {
prevPath = commentPath
} else {
prevPath = fmt.Sprintf("%s/page/%d", commentPath, prevPage)
}
hasNext, _ = p.HasNext()
if hasNext {
nextPage, _ = p.NextPage()
} else {
nextPage, _ = p.Page()
}
nextPath = fmt.Sprintf("%s/page/%d", commentPath, nextPage)
// Render
render(w, templateCommentsAdmin, &renderData{
BlogString: blog,
Data: map[string]interface{}{
"Comments": comments,
"HasPrev": hasPrev,
"HasNext": hasNext,
"Prev": slashIfEmpty(prevPath),
"Next": slashIfEmpty(nextPath),
},
})
}
}
func commentsAdminDelete(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.FormValue("commentid"))
if err != nil {
serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
err = deleteComment(id)
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
purgeCache()
http.Redirect(w, r, ".", http.StatusFound)
}

2
go.mod
View File

@ -54,7 +54,7 @@ require (
golang.org/x/mod v0.4.1 // indirect
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
golang.org/x/sys v0.0.0-20210216224549-f992740a1bac // indirect
golang.org/x/sys v0.0.0-20210217105451-b926d437f341 // indirect
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
golang.org/x/text v0.3.5 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect

4
go.sum
View File

@ -404,8 +404,8 @@ golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210216224549-f992740a1bac h1:9glrpwtNjBYgRpb67AZJKHfzj1stG/8BL5H7In2oTC4=
golang.org/x/sys v0.0.0-20210216224549-f992740a1bac/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210217105451-b926d437f341 h1:2/QtM1mL37YmcsT8HaDNHDgTqqFVw+zr8UzMiBVLzYU=
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=

View File

@ -366,7 +366,9 @@ func buildHandler() (http.Handler, error) {
// Admin
cr.Group(func(r chi.Router) {
r.Use(authMiddleware)
r.Get("/", commentsAdmin)
handler := commentsAdmin(blog, commentsPath)
r.Get("/", handler)
r.Get(paginationPath, handler)
r.Post("/delete", commentsAdminDelete)
})
})

View File

@ -98,10 +98,6 @@ func (p *postPaginationAdapter) Nums() (int64, error) {
}
func (p *postPaginationAdapter) Slice(offset, length int, data interface{}) error {
if reflect.TypeOf(data).Kind() != reflect.Ptr {
panic("data has to be a pointer")
}
modifiedConfig := *p.config
modifiedConfig.offset = offset
modifiedConfig.limit = length

View File

@ -6,7 +6,7 @@
<main>
<h1>{{ string .Blog.Lang "comments" }}</h1>
{{ $blog := .Blog }}
{{ range $i, $comment := .Data }}
{{ range $i, $comment := .Data.Comments }}
<div class="p">
<p>
Target: <a href="{{ $comment.Target }}" target="_blank">{{ $comment.Target }}</a><br/>
@ -20,6 +20,12 @@
</form>
</div>
{{ end }}
{{ if .Data.HasPrev }}
<p><a href="{{ .Data.Prev }}">{{ string .Blog.Lang "prev" }}</a></p>
{{ end }}
{{ if .Data.HasNext }}
<p><a href="{{ .Data.Next }}">{{ string .Blog.Lang "next" }}</a></p>
{{ end }}
</main>
{{ end }}

View File

@ -6,13 +6,8 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"strconv"
"strings"
"time"
"github.com/go-chi/chi"
"github.com/vcraescu/go-paginator"
)
type webmentionStatus string
@ -87,81 +82,6 @@ func extractMention(r *http.Request) (*mention, error) {
}, nil
}
func webmentionAdmin(w http.ResponseWriter, r *http.Request) {
pageNoString := chi.URLParam(r, "page")
pageNo, _ := strconv.Atoi(pageNoString)
p := paginator.New(&webmentionPaginationAdapter{config: &webmentionsRequestConfig{}}, 10)
p.SetPage(pageNo)
var mentions []*mention
err := p.Results(&mentions)
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
// Navigation
var hasPrev, hasNext bool
var prevPage, nextPage int
var prevPath, nextPath string
hasPrev, _ = p.HasPrev()
if hasPrev {
prevPage, _ = p.PrevPage()
} else {
prevPage, _ = p.Page()
}
if prevPage < 2 {
prevPath = webmentionPath
} else {
prevPath = fmt.Sprintf("%s/page/%d", webmentionPath, prevPage)
}
hasNext, _ = p.HasNext()
if hasNext {
nextPage, _ = p.NextPage()
} else {
nextPage, _ = p.Page()
}
nextPath = fmt.Sprintf("%s/page/%d", webmentionPath, nextPage)
// Render
render(w, "webmentionadmin", &renderData{
Data: map[string]interface{}{
"Mentions": mentions,
"HasPrev": hasPrev,
"HasNext": hasNext,
"Prev": slashIfEmpty(prevPath),
"Next": slashIfEmpty(nextPath),
},
})
}
func webmentionAdminDelete(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.FormValue("mentionid"))
if err != nil {
serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
err = deleteWebmention(id)
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
purgeCache()
http.Redirect(w, r, ".", http.StatusFound)
}
func webmentionAdminApprove(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.FormValue("mentionid"))
if err != nil {
serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
err = approveWebmention(id)
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
purgeCache()
http.Redirect(w, r, ".", http.StatusFound)
}
func webmentionExists(source, target string) bool {
result := 0
row, err := appDbQueryRow("select exists(select 1 from webmentions where source = ? and target = ?)", source, target)
@ -199,33 +119,6 @@ type webmentionsRequestConfig struct {
offset, limit int
}
type webmentionPaginationAdapter struct {
config *webmentionsRequestConfig
nums int64
}
func (p *webmentionPaginationAdapter) Nums() (int64, error) {
if p.nums == 0 {
nums, _ := countWebmentions(p.config)
p.nums = int64(nums)
}
return p.nums, nil
}
func (p *webmentionPaginationAdapter) Slice(offset, length int, data interface{}) error {
if reflect.TypeOf(data).Kind() != reflect.Ptr {
panic("data has to be a pointer")
}
modifiedConfig := *p.config
modifiedConfig.offset = offset
modifiedConfig.limit = length
wms, err := getWebmentions(&modifiedConfig)
reflect.ValueOf(data).Elem().Set(reflect.ValueOf(&wms).Elem())
return err
}
func buildWebmentionsQuery(config *webmentionsRequestConfig) (query string, args []interface{}) {
args = []interface{}{}
filter := ""

109
webmentionAdmin.go Normal file
View File

@ -0,0 +1,109 @@
package main
import (
"fmt"
"net/http"
"reflect"
"strconv"
"github.com/go-chi/chi"
"github.com/vcraescu/go-paginator"
)
type webmentionPaginationAdapter struct {
config *webmentionsRequestConfig
nums int64
}
func (p *webmentionPaginationAdapter) Nums() (int64, error) {
if p.nums == 0 {
nums, _ := countWebmentions(p.config)
p.nums = int64(nums)
}
return p.nums, nil
}
func (p *webmentionPaginationAdapter) Slice(offset, length int, data interface{}) error {
modifiedConfig := *p.config
modifiedConfig.offset = offset
modifiedConfig.limit = length
wms, err := getWebmentions(&modifiedConfig)
reflect.ValueOf(data).Elem().Set(reflect.ValueOf(&wms).Elem())
return err
}
func webmentionAdmin(w http.ResponseWriter, r *http.Request) {
pageNoString := chi.URLParam(r, "page")
pageNo, _ := strconv.Atoi(pageNoString)
p := paginator.New(&webmentionPaginationAdapter{config: &webmentionsRequestConfig{}}, 10)
p.SetPage(pageNo)
var mentions []*mention
err := p.Results(&mentions)
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
// Navigation
var hasPrev, hasNext bool
var prevPage, nextPage int
var prevPath, nextPath string
hasPrev, _ = p.HasPrev()
if hasPrev {
prevPage, _ = p.PrevPage()
} else {
prevPage, _ = p.Page()
}
if prevPage < 2 {
prevPath = webmentionPath
} else {
prevPath = fmt.Sprintf("%s/page/%d", webmentionPath, prevPage)
}
hasNext, _ = p.HasNext()
if hasNext {
nextPage, _ = p.NextPage()
} else {
nextPage, _ = p.Page()
}
nextPath = fmt.Sprintf("%s/page/%d", webmentionPath, nextPage)
// Render
render(w, "webmentionadmin", &renderData{
Data: map[string]interface{}{
"Mentions": mentions,
"HasPrev": hasPrev,
"HasNext": hasNext,
"Prev": slashIfEmpty(prevPath),
"Next": slashIfEmpty(nextPath),
},
})
}
func webmentionAdminDelete(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.FormValue("mentionid"))
if err != nil {
serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
err = deleteWebmention(id)
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
purgeCache()
http.Redirect(w, r, ".", http.StatusFound)
}
func webmentionAdminApprove(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.FormValue("mentionid"))
if err != nil {
serveError(w, r, err.Error(), http.StatusBadRequest)
return
}
err = approveWebmention(id)
if err != nil {
serveError(w, r, err.Error(), http.StatusInternalServerError)
return
}
purgeCache()
http.Redirect(w, r, ".", http.StatusFound)
}