#lang racket/base ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Wiki and user settings for the browser-side CMap workspace. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (require db racket/string "database.rkt") (provide read-cmap-settings save-start-cmap! save-cmap-page-guides! save-cmap-zoom!) (define start-cmap-setting-key "cmap.start.v1") (define (clean-context-key who context-key) (unless (and (string? context-key) (not (string=? (string-trim context-key) "")) (<= (string-length context-key) 200)) (error who "invalid CMap context")) context-key) (define (clean-zoom who zoom) (unless (and (exact-integer? zoom) (<= 25 zoom 300)) (error who "zoom must be an integer between 25 and 300")) zoom) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Read the wiki start CMap and one user's CMap view preferences. ; pre : user-id identifies an authenticated wiki user. ; post : The database is unchanged; missing preferences receive defaults. ; result : A JSON-compatible settings hash. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (read-cmap-settings config user-id) (call-with-wiki-database config (λ (db) (define start-slug (or (query-maybe-value db "SELECT value FROM wiki_settings WHERE key = $1" start-cmap-setting-key) "")) (define page-guide-row (query-maybe-row db "SELECT page_guides_visible FROM user_cmap_preferences WHERE user_id = $1" user-id)) (define page-guides-visible (if page-guide-row (vector-ref page-guide-row 0) #t)) (define zoom-rows (query-rows db "SELECT cmap_slug, context_key, zoom_percent FROM user_cmap_zoom_levels WHERE user_id = $1 ORDER BY cmap_slug, context_key" user-id)) (define zooms (for/list ((row (in-list zoom-rows))) (hash 'cmapSlug (vector-ref row 0) 'contextKey (vector-ref row 1) 'zoomPercent (vector-ref row 2)))) (hash 'startCmapSlug start-slug 'pageGuidesVisible page-guides-visible 'zooms zooms)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Select the wiki-wide start CMap. ; pre : slug is empty, or identifies a current non-archived CMap. ; post : The start setting is removed or updated atomically. ; result : The stored slug, or the empty string when cleared. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (save-start-cmap! config slug) (define clean-slug (if (string? slug) (string-trim slug) "")) (call-with-wiki-database config (λ (db) (cond ((string=? clean-slug "") (query-exec db "DELETE FROM wiki_settings WHERE key = $1" start-cmap-setting-key)) ((query-maybe-value db "SELECT 1 FROM concept_maps WHERE slug = $1 AND archived = FALSE" clean-slug) (query-exec db "INSERT INTO wiki_settings(key, value, updated_at) VALUES ($1, $2, $3) ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at" start-cmap-setting-key clean-slug (current-seconds))) (else (error 'save-start-cmap! "the selected start CMap does not exist"))))) clean-slug) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Store one user's wiki-wide A4 page-guide preference. ; pre : visible is a boolean and user-id identifies an authenticated user. ; post : The user's preference is inserted or updated atomically. ; result : The stored boolean. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (save-cmap-page-guides! config user-id visible) (unless (boolean? visible) (error 'save-cmap-page-guides! "pageGuidesVisible must be a boolean")) (call-with-wiki-database config (λ (db) (query-exec db "INSERT INTO user_cmap_preferences(user_id, page_guides_visible, updated_at) VALUES ($1, $2, $3) ON CONFLICT(user_id) DO UPDATE SET page_guides_visible = excluded.page_guides_visible, updated_at = excluded.updated_at" user-id visible (current-seconds)))) visible) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Store one user's zoom for one CMap context. ; pre : The CMap exists, context-key is non-empty and zoom is 25 through 300. ; post : This single context preference is inserted or updated atomically. ; result : The normalized zoom percentage. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (save-cmap-zoom! config user-id cmap-slug context-key zoom) (define clean-context (clean-context-key 'save-cmap-zoom! context-key)) (define clean-zoom-percent (clean-zoom 'save-cmap-zoom! zoom)) (call-with-wiki-database config (λ (db) (unless (query-maybe-value db "SELECT 1 FROM concept_maps WHERE slug = $1 AND archived = FALSE" cmap-slug) (error 'save-cmap-zoom! "the selected CMap does not exist")) (query-exec db "INSERT INTO user_cmap_zoom_levels(user_id, cmap_slug, context_key, zoom_percent, updated_at) VALUES ($1, $2, $3, $4, $5) ON CONFLICT(user_id, cmap_slug, context_key) DO UPDATE SET zoom_percent = excluded.zoom_percent, updated_at = excluded.updated_at" user-id cmap-slug clean-context clean-zoom-percent (current-seconds)))) clean-zoom-percent)