breadcrumps, todos, etc.

This commit is contained in:
2026-08-15 01:46:15 +02:00
parent b3afd64769
commit ea7cd45eed
11 changed files with 635 additions and 27 deletions
+31 -1
View File
@@ -11,7 +11,7 @@
database-schema-version
migrate-database!)
(define current-schema-version 3)
(define current-schema-version 5)
(define schema-1-statements
(list
@@ -215,6 +215,30 @@ SQL
(replace-page-todos! db (vector-ref row 0) (vector-ref row 1)))
(record-schema-version! db 3))
(define (migrate-3->4! db)
(query-exec db
#<<SQL
CREATE TABLE IF NOT EXISTS bookmarks (
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
page_id BIGINT NOT NULL REFERENCES pages(id) ON DELETE CASCADE,
section TEXT NOT NULL DEFAULT '',
position INTEGER NOT NULL DEFAULT 0,
created_at BIGINT NOT NULL,
PRIMARY KEY(user_id, page_id)
)
SQL
)
(query-exec db
"CREATE INDEX IF NOT EXISTS bookmarks_user_idx ON bookmarks(user_id, section, position, created_at)")
(record-schema-version! db 4))
(define (migrate-4->5! db)
(for ((row (in-list (query-rows db "SELECT id, markdown FROM pages WHERE archived = FALSE"))))
(replace-page-todos! db (vector-ref row 0) (vector-ref row 1)))
(record-schema-version! db 5))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Bring a racket-wiki PostgreSQL database to the current schema.
; pre : db is a writable PostgreSQL connection and config identifies the
@@ -235,6 +259,12 @@ SQL
(define after-attachments (database-schema-version db))
(when (= after-attachments 2)
(migrate-2->3! db))
(define after-todos (database-schema-version db))
(when (= after-todos 3)
(migrate-3->4! db))
(define after-bookmarks (database-schema-version db))
(when (= after-bookmarks 4)
(migrate-4->5! db))
(define resulting-version (database-schema-version db))
(when (> resulting-version current-schema-version)
(error 'migrate-database!
+110
View File
@@ -22,6 +22,10 @@
read-version
search-pages
list-todos
list-recent-pages
list-bookmarks
set-bookmark!
delete-bookmark!
save-upload!
uploaded-file)
@@ -390,6 +394,112 @@ SQL
'line (vector-ref row 3)
'text (vector-ref row 4))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : List recently edited current wiki pages.
; pre : PostgreSQL schema 1 or newer is initialized.
; post : Page rows have only been read.
; result : At most limit page metadata hashes, newest first.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (list-recent-pages config [limit 50])
(call-with-wiki-database
config
(λ (db)
(for/list ((row (in-list
(query-rows db
(string-append "SELECT " page-columns
" FROM pages WHERE archived = FALSE"
" ORDER BY updated_at DESC, lower(title), title"
" LIMIT $1")
limit))))
(row->page row #f)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : List bookmarks for one wiki user.
; pre : PostgreSQL schema 4 or newer is initialized and user-id identifies a user.
; post : Bookmark and page rows have only been read.
; result : Bookmarks ordered by section and position.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (list-bookmarks config user-id)
(call-with-wiki-database
config
(λ (db)
(for/list ((row (in-list
(query-rows db
#<<SQL
SELECT p.slug, p.title, b.section, b.position, b.created_at, p.updated_at
FROM bookmarks b
JOIN pages p ON p.id = b.page_id
WHERE b.user_id = $1
AND p.archived = FALSE
ORDER BY lower(b.section), b.section, b.position, b.created_at, lower(p.title), p.title
SQL
user-id))))
(hash 'slug (vector-ref row 0)
'title (vector-ref row 1)
'section (vector-ref row 2)
'position (vector-ref row 3)
'createdAt (vector-ref row 4)
'updatedAt (vector-ref row 5))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Add a page to a user's bookmarks or move it to another section.
; pre : PostgreSQL schema 4 or newer is initialized and slug identifies a current page.
; post : Exactly one bookmark exists for user-id and the page.
; result : void.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (set-bookmark! config user-id slug section)
(call-with-wiki-database
config
(λ (db)
(call-with-transaction
db
(λ ()
(define page-id
(query-maybe-value db
"SELECT id FROM pages WHERE slug = $1 AND archived = FALSE"
slug))
(unless page-id
(error 'set-bookmark! "unknown page: ~a" slug))
(define clean-section (string-trim section))
(define position
(query-value db
#<<SQL
SELECT COALESCE(MAX(position), -1) + 1
FROM bookmarks
WHERE user_id = $1 AND section = $2
SQL
user-id
clean-section))
(query-exec db
#<<SQL
INSERT INTO bookmarks(user_id, page_id, section, position, created_at)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (user_id, page_id)
DO UPDATE SET section = EXCLUDED.section, position = EXCLUDED.position
SQL
user-id page-id clean-section position (current-seconds))))))
(void))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Remove one page from a user's bookmarks.
; pre : PostgreSQL schema 4 or newer is initialized.
; post : The bookmark no longer exists; other bookmarks are unchanged.
; result : void.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (delete-bookmark! config user-id slug)
(call-with-wiki-database
config
(λ (db)
(query-exec db
#<<SQL
DELETE FROM bookmarks
WHERE user_id = $1
AND page_id = (SELECT id FROM pages WHERE slug = $2)
SQL
user-id slug)))
(void))
(define (safe-file-name name)
(define clean
(regexp-replace* #px"[^A-Za-z0-9._ -]" name "_"))
+1 -1
View File
@@ -23,7 +23,7 @@
((regexp-match? #px"^(```|~~~)" trimmed)
(set! in-fence? (not in-fence?)))
((not in-fence?)
(for ((match (in-list (regexp-match* #px"todo\\([^()]+\\)" line))))
(for ((match (in-list (regexp-match* #px"[Tt][Oo][Dd][Oo]\\([^()]+\\)" line))))
(define text (string-trim (substring match 5 (- (string-length match) 1))))
(when (not (string=? text ""))
(set! item-number (+ item-number 1))