From 0fc470a8d588cac1a51cc4b3e1a2237dc56041c9 Mon Sep 17 00:00:00 2001 From: grm Date: Fri, 18 Sep 2026 14:32:22 +0300 Subject: Special pages: a gallery page of the blog's images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A page's kind says what it shows between its intro and outro: posts, as before, or something special. The first special kind is a gallery — every image in the files library as a masonry wall (CSS columns, no script), newest first, each linked to its full-size file. The page form lists the library's images with a Hide tick per picture; exclusions are stored (page_hidden_files) rather than inclusions so a new upload shows up without editing the page, and the design's own pictures — logo, favicon, header and background image — are never shown. Special pages keep the page chrome (menu, announcements, home, ordering) but hold no posts: the post forms skip them and a forced page id is refused. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A --- AGENTS.md | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'AGENTS.md') diff --git a/AGENTS.md b/AGENTS.md index 5aec663..e8ab0bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,7 +58,7 @@ internal/config/ env → Config; RootSubdomain = "www" internal/db/ Cluster (control pool + lazy per-blog pools, CREATE/DROP DATABASE), goose providers; migrations/control/*.sql, migrations/blog/*.sql internal/store/ Store = control DB (users, blog registry, create/delete blog); - BlogStore = one blog's DB (settings, pages, posts, files, sections, modules, menu) + BlogStore = one blog's DB (settings, pages + hidden gallery files, posts, files, sections, modules, menu) internal/auth/ bcrypt, JWT issue/parse, cookie, HMAC CSRF internal/markdown/ Render(md) → sanitized HTML internal/i18n/ languages, T/Tf (English keys → catalog), FormatDate/Month, Accept-Language Match; @@ -372,6 +372,28 @@ internal/web/ server.go (host router, middleware, render helpers) blog-wide `above`/`below` HTML modules' per-page counterpart (a home page with a site map or a widget after its posts). `blog/page.html` prints the outro after the `postlist` and treats a page with only an outro as not empty. +- **Special pages** (`pages.kind`, `store/pages.go`): a page's `kind` says + what it shows between its intro and outro — `posts` (the default) or a + special kind; the list is a CHECK constraint, so a new kind needs a + migration. `Page.Special()` is "not posts": special pages keep the whole + page chrome (title, address, intro/outro, menu, announcements, home, order) + but never hold posts — the post form and posts filter skip them and + `handlePostSave` refuses their id. `Kind` is set at creation only (`?kind=` + on the Pages tab's "+ New special page" link, a hidden field on the POST, + read by `pageKind`); `UpdatePage` never touches it. With one special kind + the button links straight to `?kind=gallery`; a second kind should turn it + into a chooser. `renderPage` branches on the kind after `blogView`. + **Gallery** (`gallery`, `blog/gallery.html`): every `kind = image` file in + the library, newest first, each linked to its `/media/` (the no-JS + lightbox), minus the page's `page_hidden_files` rows (the form's "Hide" + checkboxes, an `.imagegrid` of thumbnails; exclusions are stored, so a new + upload shows up without editing the page; the file's FK cascades) and + minus the theme's own images (`themeFiles`: logo, favicon, header and + background image) — `BlogStore.GalleryImages(pageID, except)`. No + pagination: images are `loading=lazy` and the library is bounded by the + upload limit. The wall is CSS multi-column (`.gallery { column-width }` in + blog.css, narrower under 700px), no script; browsers without columns + stack the pictures. - **Summaries** (`web/summary.go`, `theme.list_style`): with `summary` a listing (page or tag) shows `Post.Excerpt` + a "Read more" link when `Post.HasMore` — both filled by `fillExcerpts` in the listing handlers, never @@ -526,7 +548,7 @@ superadmin password to `admin`. Production refuses both. `-- +goose Up/Down` sections; they run automatically at startup. Never edit an applied migration. Blog chain so far: `00001_init`, `00002_language`, `00003_files`, `00004_tags`, `00005_search`, `00006_format`, `00007_post_image`, - `00008_page_outro`, `00009_gizmo_style`, `00010_font_kind`, `00011_post_hide_date`, `00012_page_show_notices`; control: `00001_init`, `00002_upload_limit`. Both chains were re-baselined at 00001 after + `00008_page_outro`, `00009_gizmo_style`, `00010_font_kind`, `00011_post_hide_date`, `00012_page_show_notices`, `00013_page_kind`; control: `00001_init`, `00002_upload_limit`. Both chains were re-baselined at 00001 after the move to per-blog databases; deployments from before it have `goose_db_version` rows 2–7 in the control DB that must be deleted once (README "Upgrading from a single database") or the next control migration -- cgit v1.2.3