mirror of https://github.com/jlelse/GoBlog
Add pagination to Webmention Admin
This commit is contained in:
parent
a840879d48
commit
78f3d3c07b
10
http.go
10
http.go
|
@ -71,6 +71,10 @@ func reloadRouter() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildHandler() (http.Handler, error) {
|
func buildHandler() (http.Handler, error) {
|
||||||
|
|
||||||
|
paginationPath := "/page/{page:[0-9-]+}"
|
||||||
|
feedPath := ".{feed:rss|json|atom}"
|
||||||
|
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
|
||||||
if appConfig.Server.Logging {
|
if appConfig.Server.Logging {
|
||||||
|
@ -128,10 +132,11 @@ func buildHandler() (http.Handler, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Webmentions
|
// Webmentions
|
||||||
r.Route("/webmention", func(webmentionRouter chi.Router) {
|
r.Route(webmentionPath, func(webmentionRouter chi.Router) {
|
||||||
webmentionRouter.Use(middleware.NoCache)
|
webmentionRouter.Use(middleware.NoCache)
|
||||||
webmentionRouter.Post("/", handleWebmention)
|
webmentionRouter.Post("/", handleWebmention)
|
||||||
webmentionRouter.With(minifier.Middleware, authMiddleware).Get("/", webmentionAdmin)
|
webmentionRouter.With(minifier.Middleware, authMiddleware).Get("/", webmentionAdmin)
|
||||||
|
webmentionRouter.With(minifier.Middleware, authMiddleware).Get(paginationPath, webmentionAdmin)
|
||||||
webmentionRouter.With(authMiddleware).Post("/delete", webmentionAdminDelete)
|
webmentionRouter.With(authMiddleware).Post("/delete", webmentionAdminDelete)
|
||||||
webmentionRouter.With(authMiddleware).Post("/approve", webmentionAdminApprove)
|
webmentionRouter.With(authMiddleware).Post("/approve", webmentionAdminApprove)
|
||||||
})
|
})
|
||||||
|
@ -194,9 +199,6 @@ func buildHandler() (http.Handler, error) {
|
||||||
// Short paths
|
// Short paths
|
||||||
r.With(cacheMiddleware).Get("/s/{id:[0-9a-fA-F]+}", redirectToLongPath)
|
r.With(cacheMiddleware).Get("/s/{id:[0-9a-fA-F]+}", redirectToLongPath)
|
||||||
|
|
||||||
paginationPath := "/page/{page:[0-9-]+}"
|
|
||||||
feedPath := ".{feed:rss|json|atom}"
|
|
||||||
|
|
||||||
for blog, blogConfig := range appConfig.Blogs {
|
for blog, blogConfig := range appConfig.Blogs {
|
||||||
|
|
||||||
fullBlogPath := blogConfig.Path
|
fullBlogPath := blogConfig.Path
|
||||||
|
|
|
@ -5,9 +5,8 @@
|
||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
<main>
|
<main>
|
||||||
<h1>{{ string .Blog.Lang "webmentions" }}</h1>
|
<h1>{{ string .Blog.Lang "webmentions" }}</h1>
|
||||||
<h2>{{ string .Blog.Lang "verified" }}</h2>
|
|
||||||
{{ $blog := .Blog }}
|
{{ $blog := .Blog }}
|
||||||
{{ range $i, $mention := .Data.Verified }}
|
{{ range $i, $mention := .Data.Mentions }}
|
||||||
<div class="p">
|
<div class="p">
|
||||||
<p>
|
<p>
|
||||||
From: <a href="{{ $mention.Source }}" target="_blank" rel="noopener noreferrer">{{ $mention.Source }}</a><br/>
|
From: <a href="{{ $mention.Source }}" target="_blank" rel="noopener noreferrer">{{ $mention.Source }}</a><br/>
|
||||||
|
@ -16,24 +15,18 @@
|
||||||
</p>
|
</p>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<input type="hidden" name="mentionid" value="{{ $mention.ID }}">
|
<input type="hidden" name="mentionid" value="{{ $mention.ID }}">
|
||||||
<input type="submit" formaction="/webmention/approve" value="{{ string $blog.Lang "approve" }}">
|
{{ if eq $mention.Status "verified" }}
|
||||||
|
<input type="submit" formaction="/webmention/approve" value="{{ string $blog.Lang "approve" }}">
|
||||||
|
{{ end }}
|
||||||
<input type="submit" formaction="/webmention/delete" value="{{ string $blog.Lang "delete" }}">
|
<input type="submit" formaction="/webmention/delete" value="{{ string $blog.Lang "delete" }}">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<h2>{{ string .Blog.Lang "approved" }}</h2>
|
{{ if .Data.HasPrev }}
|
||||||
{{ range $i, $mention := .Data.Approved }}
|
<p><a href="{{ .Data.Prev }}">{{ string .Blog.Lang "prev" }}</a></p>
|
||||||
<div class="p">
|
{{ end }}
|
||||||
<p>
|
{{ if .Data.HasNext }}
|
||||||
From: <a href="{{ $mention.Source }}" target="_blank" rel="noopener noreferrer">{{ $mention.Source }}</a><br/>
|
<p><a href="{{ .Data.Next }}">{{ string .Blog.Lang "next" }}</a></p>
|
||||||
To: <a href="{{ $mention.Target }}" target="_blank">{{ $mention.Target }}</a><br/>
|
|
||||||
Created: {{ unixtodate $mention.Created }}
|
|
||||||
</p>
|
|
||||||
<form method="post">
|
|
||||||
<input type="hidden" name="mentionid" value="{{ $mention.ID }}">
|
|
||||||
<input type="submit" formaction="/webmention/delete" value="{{ string $blog.Lang "delete" }}">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</main>
|
</main>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
122
webmention.go
122
webmention.go
|
@ -6,18 +6,22 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"github.com/vcraescu/go-paginator"
|
||||||
)
|
)
|
||||||
|
|
||||||
type webmentionStatus string
|
type webmentionStatus string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
webmentionStatusNew webmentionStatus = "new"
|
|
||||||
webmentionStatusRenew webmentionStatus = "renew"
|
|
||||||
webmentionStatusVerified webmentionStatus = "verified"
|
webmentionStatusVerified webmentionStatus = "verified"
|
||||||
webmentionStatusApproved webmentionStatus = "approved"
|
webmentionStatusApproved webmentionStatus = "approved"
|
||||||
|
|
||||||
|
webmentionPath = "/webmention"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mention struct {
|
type mention struct {
|
||||||
|
@ -28,6 +32,7 @@ type mention struct {
|
||||||
Title string
|
Title string
|
||||||
Content string
|
Content string
|
||||||
Author string
|
Author string
|
||||||
|
Status webmentionStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func initWebmention() error {
|
func initWebmention() error {
|
||||||
|
@ -83,24 +88,46 @@ func extractMention(r *http.Request) (*mention, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func webmentionAdmin(w http.ResponseWriter, r *http.Request) {
|
func webmentionAdmin(w http.ResponseWriter, r *http.Request) {
|
||||||
verified, err := getWebmentions(&webmentionsRequestConfig{
|
pageNoString := chi.URLParam(r, "page")
|
||||||
status: webmentionStatusVerified,
|
pageNo, _ := strconv.Atoi(pageNoString)
|
||||||
})
|
p := paginator.New(&webmentionPaginationAdapter{config: &webmentionsRequestConfig{}}, 10)
|
||||||
|
p.SetPage(pageNo)
|
||||||
|
var mentions []*mention
|
||||||
|
err := p.Results(&mentions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
approved, err := getWebmentions(&webmentionsRequestConfig{
|
// Navigation
|
||||||
status: webmentionStatusApproved,
|
var hasPrev, hasNext bool
|
||||||
})
|
var prevPage, nextPage int
|
||||||
if err != nil {
|
var prevPath, nextPath string
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
hasPrev, _ = p.HasPrev()
|
||||||
return
|
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{
|
render(w, "webmentionadmin", &renderData{
|
||||||
Data: map[string][]*mention{
|
Data: map[string]interface{}{
|
||||||
"Verified": verified,
|
"Mentions": mentions,
|
||||||
"Approved": approved,
|
"HasPrev": hasPrev,
|
||||||
|
"HasNext": hasNext,
|
||||||
|
"Prev": slashIfEmpty(prevPath),
|
||||||
|
"Next": slashIfEmpty(nextPath),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -168,16 +195,41 @@ func approveWebmention(id int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type webmentionsRequestConfig struct {
|
type webmentionsRequestConfig struct {
|
||||||
target string
|
target string
|
||||||
status webmentionStatus
|
status webmentionStatus
|
||||||
asc bool
|
asc bool
|
||||||
|
offset, limit int
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWebmentions(config *webmentionsRequestConfig) ([]*mention, error) {
|
type webmentionPaginationAdapter struct {
|
||||||
mentions := []*mention{}
|
config *webmentionsRequestConfig
|
||||||
var rows *sql.Rows
|
nums int64
|
||||||
var err error
|
}
|
||||||
args := []interface{}{}
|
|
||||||
|
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 := ""
|
filter := ""
|
||||||
if config != nil {
|
if config != nil {
|
||||||
if config.target != "" && config.status != "" {
|
if config.target != "" && config.status != "" {
|
||||||
|
@ -195,13 +247,24 @@ func getWebmentions(config *webmentionsRequestConfig) ([]*mention, error) {
|
||||||
if config.asc {
|
if config.asc {
|
||||||
order = "asc"
|
order = "asc"
|
||||||
}
|
}
|
||||||
rows, err = appDbQuery("select id, source, target, created, title, content, author from webmentions "+filter+" order by created "+order, args...)
|
query = "select id, source, target, created, title, content, author, status from webmentions " + filter + " order by created " + order
|
||||||
|
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 query, args
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWebmentions(config *webmentionsRequestConfig) ([]*mention, error) {
|
||||||
|
mentions := []*mention{}
|
||||||
|
query, args := buildWebmentionsQuery(config)
|
||||||
|
rows, err := appDbQuery(query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
m := &mention{}
|
m := &mention{}
|
||||||
err = rows.Scan(&m.ID, &m.Source, &m.Target, &m.Created, &m.Title, &m.Content, &m.Author)
|
err = rows.Scan(&m.ID, &m.Source, &m.Target, &m.Created, &m.Title, &m.Content, &m.Author, &m.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -209,3 +272,14 @@ func getWebmentions(config *webmentionsRequestConfig) ([]*mention, error) {
|
||||||
}
|
}
|
||||||
return mentions, nil
|
return mentions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func countWebmentions(config *webmentionsRequestConfig) (count int, err error) {
|
||||||
|
query, params := buildWebmentionsQuery(config)
|
||||||
|
query = "select count(*) from (" + query + ")"
|
||||||
|
row, err := appDbQueryRow(query, params...)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = row.Scan(&count)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue