From 78f3d3c07b33abc2b5e4bdf281087f8549c87f16 Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Sat, 30 Jan 2021 19:37:26 +0100 Subject: [PATCH] Add pagination to Webmention Admin --- http.go | 10 ++- templates/webmentionadmin.gohtml | 25 +++---- webmention.go | 122 +++++++++++++++++++++++++------ 3 files changed, 113 insertions(+), 44 deletions(-) diff --git a/http.go b/http.go index 09febf3..b934c0c 100644 --- a/http.go +++ b/http.go @@ -71,6 +71,10 @@ func reloadRouter() error { } func buildHandler() (http.Handler, error) { + + paginationPath := "/page/{page:[0-9-]+}" + feedPath := ".{feed:rss|json|atom}" + r := chi.NewRouter() if appConfig.Server.Logging { @@ -128,10 +132,11 @@ func buildHandler() (http.Handler, error) { } // Webmentions - r.Route("/webmention", func(webmentionRouter chi.Router) { + r.Route(webmentionPath, func(webmentionRouter chi.Router) { webmentionRouter.Use(middleware.NoCache) webmentionRouter.Post("/", handleWebmention) 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("/approve", webmentionAdminApprove) }) @@ -194,9 +199,6 @@ func buildHandler() (http.Handler, error) { // Short paths 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 { fullBlogPath := blogConfig.Path diff --git a/templates/webmentionadmin.gohtml b/templates/webmentionadmin.gohtml index fbe4f15..41ee900 100644 --- a/templates/webmentionadmin.gohtml +++ b/templates/webmentionadmin.gohtml @@ -5,9 +5,8 @@ {{ define "main" }}

{{ string .Blog.Lang "webmentions" }}

-

{{ string .Blog.Lang "verified" }}

{{ $blog := .Blog }} - {{ range $i, $mention := .Data.Verified }} + {{ range $i, $mention := .Data.Mentions }}

From: {{ $mention.Source }}
@@ -16,24 +15,18 @@

- + {{ if eq $mention.Status "verified" }} + + {{ end }}
{{ end }} -

{{ string .Blog.Lang "approved" }}

- {{ range $i, $mention := .Data.Approved }} -
-

- From: {{ $mention.Source }}
- To: {{ $mention.Target }}
- Created: {{ unixtodate $mention.Created }} -

-
- - -
-
+ {{ if .Data.HasPrev }} +

{{ string .Blog.Lang "prev" }}

+ {{ end }} + {{ if .Data.HasNext }} +

{{ string .Blog.Lang "next" }}

{{ end }}
{{ end }} diff --git a/webmention.go b/webmention.go index 4fd2d4b..9a4d277 100644 --- a/webmention.go +++ b/webmention.go @@ -6,18 +6,22 @@ import ( "fmt" "net/http" "net/http/httptest" + "reflect" "strconv" "strings" "time" + + "github.com/go-chi/chi" + "github.com/vcraescu/go-paginator" ) type webmentionStatus string const ( - webmentionStatusNew webmentionStatus = "new" - webmentionStatusRenew webmentionStatus = "renew" webmentionStatusVerified webmentionStatus = "verified" webmentionStatusApproved webmentionStatus = "approved" + + webmentionPath = "/webmention" ) type mention struct { @@ -28,6 +32,7 @@ type mention struct { Title string Content string Author string + Status webmentionStatus } func initWebmention() error { @@ -83,24 +88,46 @@ func extractMention(r *http.Request) (*mention, error) { } func webmentionAdmin(w http.ResponseWriter, r *http.Request) { - verified, err := getWebmentions(&webmentionsRequestConfig{ - status: webmentionStatusVerified, - }) + 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 } - approved, err := getWebmentions(&webmentionsRequestConfig{ - status: webmentionStatusApproved, - }) - 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][]*mention{ - "Verified": verified, - "Approved": approved, + Data: map[string]interface{}{ + "Mentions": mentions, + "HasPrev": hasPrev, + "HasNext": hasNext, + "Prev": slashIfEmpty(prevPath), + "Next": slashIfEmpty(nextPath), }, }) } @@ -168,16 +195,41 @@ func approveWebmention(id int) error { } type webmentionsRequestConfig struct { - target string - status webmentionStatus - asc bool + target string + status webmentionStatus + asc bool + offset, limit int } -func getWebmentions(config *webmentionsRequestConfig) ([]*mention, error) { - mentions := []*mention{} - var rows *sql.Rows - var err error - args := []interface{}{} +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 := "" if config != nil { if config.target != "" && config.status != "" { @@ -195,13 +247,24 @@ func getWebmentions(config *webmentionsRequestConfig) ([]*mention, error) { if config.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 { return nil, err } for rows.Next() { 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 { return nil, err } @@ -209,3 +272,14 @@ func getWebmentions(config *webmentionsRequestConfig) ([]*mention, error) { } 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 +}