aboutsummaryrefslogtreecommitdiffstats
path: root/internal/store/pages.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/store/pages.go')
-rw-r--r--internal/store/pages.go70
1 files changed, 64 insertions, 6 deletions
diff --git a/internal/store/pages.go b/internal/store/pages.go
index 3fdc492..2a69d99 100644
--- a/internal/store/pages.go
+++ b/internal/store/pages.go
@@ -3,10 +3,20 @@ package store
import (
"context"
"time"
+
+ "github.com/google/uuid"
+)
+
+// Page kinds: what a page shows under its intro. Everything but posts is a
+// "special page"; the list is also a CHECK constraint on pages.kind.
+const (
+ PageKindPosts = "posts"
+ PageKindGallery = "gallery" // every library image, except the hidden ones
)
type Page struct {
ID int64
+ Kind string // PageKindPosts | PageKindGallery; set at creation, never changed
Slug string
Title string
IntroMD string
@@ -22,11 +32,21 @@ type Page struct {
PostCount int // filled by ListPages only
}
-const pageCols = `id, slug, title, intro_md, intro_html, outro_md, outro_html, format, nav_order, EXISTS (SELECT 1 FROM menu_items m WHERE m.page_id=pages.id), show_notices, is_home, created_at`
+// Special is a page that shows something other than posts.
+func (p *Page) Special() bool { return p.Kind != PageKindPosts }
+
+func kindOrPosts(k string) string {
+ if k == PageKindGallery {
+ return k
+ }
+ return PageKindPosts
+}
+
+const pageCols = `id, kind, slug, title, intro_md, intro_html, outro_md, outro_html, format, nav_order, EXISTS (SELECT 1 FROM menu_items m WHERE m.page_id=pages.id), show_notices, is_home, created_at`
func scanPage(row interface{ Scan(...any) error }) (*Page, error) {
var p Page
- err := row.Scan(&p.ID, &p.Slug, &p.Title, &p.IntroMD, &p.IntroHTML, &p.OutroMD, &p.OutroHTML, &p.Format, &p.NavOrder, &p.ShowInNav, &p.ShowNotices, &p.IsHome, &p.CreatedAt)
+ err := row.Scan(&p.ID, &p.Kind, &p.Slug, &p.Title, &p.IntroMD, &p.IntroHTML, &p.OutroMD, &p.OutroHTML, &p.Format, &p.NavOrder, &p.ShowInNav, &p.ShowNotices, &p.IsHome, &p.CreatedAt)
if err != nil {
return nil, wrap(err)
}
@@ -43,7 +63,7 @@ func (bs *BlogStore) ListPages(ctx context.Context) ([]Page, error) {
var out []Page
for rows.Next() {
var p Page
- if err := rows.Scan(&p.ID, &p.Slug, &p.Title, &p.IntroMD, &p.IntroHTML, &p.OutroMD, &p.OutroHTML, &p.Format, &p.NavOrder, &p.ShowInNav, &p.ShowNotices, &p.IsHome, &p.CreatedAt, &p.PostCount); err != nil {
+ if err := rows.Scan(&p.ID, &p.Kind, &p.Slug, &p.Title, &p.IntroMD, &p.IntroHTML, &p.OutroMD, &p.OutroHTML, &p.Format, &p.NavOrder, &p.ShowInNav, &p.ShowNotices, &p.IsHome, &p.CreatedAt, &p.PostCount); err != nil {
return nil, err
}
out = append(out, p)
@@ -71,9 +91,9 @@ func (bs *BlogStore) CreatePage(ctx context.Context, p *Page) (*Page, error) {
return nil, err
}
defer tx.Rollback(ctx)
- created, err := scanPage(tx.QueryRow(ctx, `INSERT INTO pages (slug, title, intro_md, intro_html, outro_md, outro_html, format, show_notices, nav_order)
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,(SELECT coalesce(max(nav_order),-1)+1 FROM pages)) RETURNING `+pageCols,
- p.Slug, p.Title, p.IntroMD, p.IntroHTML, p.OutroMD, p.OutroHTML, formatOrMD(p.Format), p.ShowNotices))
+ created, err := scanPage(tx.QueryRow(ctx, `INSERT INTO pages (kind, slug, title, intro_md, intro_html, outro_md, outro_html, format, show_notices, nav_order)
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,(SELECT coalesce(max(nav_order),-1)+1 FROM pages)) RETURNING `+pageCols,
+ kindOrPosts(p.Kind), p.Slug, p.Title, p.IntroMD, p.IntroHTML, p.OutroMD, p.OutroHTML, formatOrMD(p.Format), p.ShowNotices))
if err != nil {
return nil, err
}
@@ -87,6 +107,7 @@ func (bs *BlogStore) CreatePage(ctx context.Context, p *Page) (*Page, error) {
}
// UpdatePage saves the page and keeps its menu entry in step with ShowInNav.
+// The kind is not touched: a page is what it was created as.
func (bs *BlogStore) UpdatePage(ctx context.Context, p *Page) error {
tx, err := bs.db.Begin(ctx)
if err != nil {
@@ -160,3 +181,40 @@ func (bs *BlogStore) MovePage(ctx context.Context, id int64, dir int) error {
}
return tx.Commit(ctx)
}
+
+// HiddenFiles lists the library files a gallery page leaves out.
+func (bs *BlogStore) HiddenFiles(ctx context.Context, pageID int64) (map[string]bool, error) {
+ rows, err := bs.db.Query(ctx, `SELECT file_id FROM page_hidden_files WHERE page_id=$1`, pageID)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ out := map[string]bool{}
+ for rows.Next() {
+ var id uuid.UUID
+ if err := rows.Scan(&id); err != nil {
+ return nil, err
+ }
+ out[id.String()] = true
+ }
+ return out, rows.Err()
+}
+
+// SetHiddenFiles makes ids the files a gallery page leaves out. Ids of files
+// that are gone are skipped rather than refused: the form was rendered before
+// the deletion.
+func (bs *BlogStore) SetHiddenFiles(ctx context.Context, pageID int64, ids []uuid.UUID) error {
+ tx, err := bs.db.Begin(ctx)
+ if err != nil {
+ return err
+ }
+ defer tx.Rollback(ctx)
+ if _, err := tx.Exec(ctx, `DELETE FROM page_hidden_files WHERE page_id=$1`, pageID); err != nil {
+ return err
+ }
+ if _, err := tx.Exec(ctx, `INSERT INTO page_hidden_files (page_id, file_id)
+ SELECT $1, id FROM files WHERE id = ANY($2)`, pageID, ids); err != nil {
+ return err
+ }
+ return tx.Commit(ctx)
+}